feat(app): configure background tray behavior

This commit is contained in:
t8y2 committed 2026-05-22 02:24:16 +08:00
1 parent cf520a7ee1
commit bc41936e48
12 files changed
+282 -35

No files matched your search

@@ -18,6 +18,7 @@ import {
EDITOR_THEMES,
FONT_FAMILIES,
DEFAULT_EDITOR_SETTINGS,
DEFAULT_DESKTOP_SETTINGS,
type AiProvider,
type AiApiStyle,
type EditorTheme,
@@ -59,6 +60,7 @@ const editTheme = ref(settingsStore.editorSettings.theme);
const editExecuteMode = ref(settingsStore.editorSettings.executeMode);
const editWordWrap = ref(settingsStore.editorSettings.wordWrap);
const editAppLayout = ref(settingsStore.editorSettings.appLayout);
const editRunInBackground = ref(settingsStore.desktopSettings.run_in_background);
const editRedisScanPageSize = ref(settingsStore.editorSettings.redisScanPageSize);
const editShortcuts = ref(normalizeShortcutSettings(settingsStore.editorSettings.shortcuts));
const editSidebarActivation = ref(settingsStore.editorSettings.sidebarActivation);
@@ -122,6 +124,7 @@ watch(
editExecuteMode.value = settingsStore.editorSettings.executeMode;
editWordWrap.value = settingsStore.editorSettings.wordWrap;
editAppLayout.value = settingsStore.editorSettings.appLayout;
editRunInBackground.value = settingsStore.desktopSettings.run_in_background;
editRedisScanPageSize.value = settingsStore.editorSettings.redisScanPageSize;
editShortcuts.value = normalizeShortcutSettings(settingsStore.editorSettings.shortcuts);
editSidebarActivation.value = settingsStore.editorSettings.sidebarActivation;
@@ -152,6 +155,7 @@ function hasChanges(): boolean {
editExecuteMode.value !== settingsStore.editorSettings.executeMode ||
editWordWrap.value !== settingsStore.editorSettings.wordWrap ||
editAppLayout.value !== settingsStore.editorSettings.appLayout ||
editRunInBackground.value !== settingsStore.desktopSettings.run_in_background ||
editRedisScanPageSize.value !== settingsStore.editorSettings.redisScanPageSize ||
JSON.stringify(editShortcuts.value) !== JSON.stringify(settingsStore.editorSettings.shortcuts) ||
editSidebarActivation.value !== settingsStore.editorSettings.sidebarActivation ||
@@ -161,7 +165,7 @@ function hasChanges(): boolean {
);
}
function applySettings() {
async function applySettings() {
if (hasBlockingShortcutConflicts.value) return;
settingsStore.updateEditorSettings({
fontFamily: editFontFamily.value,
@@ -176,6 +180,9 @@ function applySettings() {
autoSelectActiveSidebarNode: editAutoSelectActiveSidebarNode.value,
sidebarHiddenTablePrefixes: normalizeSidebarHiddenTablePrefixes(editSidebarHiddenTablePrefixes.value),
});
await settingsStore.updateDesktopSettings({
run_in_background: editRunInBackground.value,
});
emit("update:open", false);
}
@@ -186,6 +193,7 @@ function resetDefaults() {
editExecuteMode.value = DEFAULT_EDITOR_SETTINGS.executeMode;
editWordWrap.value = DEFAULT_EDITOR_SETTINGS.wordWrap;
editAppLayout.value = DEFAULT_EDITOR_SETTINGS.appLayout;
editRunInBackground.value = DEFAULT_DESKTOP_SETTINGS.run_in_background;
editRedisScanPageSize.value = DEFAULT_EDITOR_SETTINGS.redisScanPageSize;
editShortcuts.value = normalizeShortcutSettings(DEFAULT_EDITOR_SETTINGS.shortcuts);
editSidebarActivation.value = DEFAULT_EDITOR_SETTINGS.sidebarActivation;
@@ -292,6 +300,8 @@ watch(
newPassword.value = "";
confirmNewPassword.value = "";
await settingsStore.initAiConfig();
await settingsStore.initDesktopSettings();
editRunInBackground.value = settingsStore.desktopSettings.run_in_background;
syncAiEditState();
}
},
@@ -825,6 +835,16 @@ watch(
</Button>
</div>
</div>
<div
v-if="!isWeb"
class="flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2"
>
<div class="space-y-1">
<Label for="run-in-background">{{ t("settings.runInBackground") }}</Label>
<p class="text-xs text-muted-foreground">{{ t("settings.runInBackgroundDescription") }}</p>
</div>
<Switch id="run-in-background" v-model="editRunInBackground" />
</div>
</section>
<section v-else-if="activeSettingsTab === 'navigation'" class="flex flex-col gap-5 py-2">
+3
View File
@@ -1194,6 +1194,9 @@ export default {
appLayoutSeparatedDescription: "Clear panel separation with block-style tabs.",
appLayoutClassic: "Classic compact",
appLayoutClassicDescription: "Continuous tab bar with compact panel headers.",
runInBackground: "Keep running after closing window",
runInBackgroundDescription:
"When enabled, DBX shows a tray/menu bar icon and closing the window keeps it in the background. When disabled, no icon is shown and closing the window quits the app.",
sidebarActivation: "Sidebar activation",
sidebarActivationSingle: "Single click",
sidebarActivationSingleDescription: "Open actionable sidebar items with one click.",
+3
View File
@@ -1092,6 +1092,9 @@ export default {
appLayoutSeparatedDescription: "Separación clara de paneles con pestañas en bloque.",
appLayoutClassic: "Clásico compacto",
appLayoutClassicDescription: "Barra de pestañas continua con encabezados de panel compactos.",
runInBackground: "Seguir ejecutando al cerrar la ventana",
runInBackgroundDescription:
"Si está activado, DBX muestra un icono en la bandeja/barra de menú y cerrar la ventana lo deja en segundo plano. Si está desactivado, no se muestra el icono y cerrar la ventana sale de la app.",
sidebarActivation: "Activación de la barra lateral",
sidebarActivationSingle: "Un clic",
sidebarActivationSingleDescription: "Abrir elementos accionables de la barra lateral con un clic.",
+3
View File
@@ -1172,6 +1172,9 @@ export default {
appLayoutSeparatedDescription: "面板分隔更清晰,标签页为块状样式。",
appLayoutClassic: "经典紧凑",
appLayoutClassicDescription: "使用连续标签栏和更紧凑的面板标题。",
runInBackground: "关闭窗口后后台驻留",
runInBackgroundDescription:
"开启时显示系统托盘/菜单栏图标,关闭窗口会隐藏到后台;关闭后不显示图标,关闭窗口将退出应用。",
sidebarActivation: "侧边栏打开方式",
sidebarActivationSingle: "单击打开",
sidebarActivationSingleDescription: "单击即可打开侧边栏中的可操作项目。",
+2
View File
@@ -95,6 +95,8 @@ export const aiTestConnection = forward("aiTestConnection");
export const aiListModels = forward("aiListModels");
export const saveAiConfig = forward("saveAiConfig");
export const loadAiConfig = forward("loadAiConfig");
export const loadDesktopSettings = forward("loadDesktopSettings");
export const saveDesktopSettings = forward("saveDesktopSettings");
export const saveAiConversation = forward("saveAiConversation");
export const loadAiConversations = forward("loadAiConversations");
export const deleteAiConversation = forward("deleteAiConversation");
+9
View File
@@ -25,6 +25,7 @@ import type {
AiStreamChunk,
AiConversation,
AiModelInfo,
DesktopSettings,
DriverInstallProgress,
JavaRuntimeConfig,
UpdateInfo,
@@ -466,6 +467,14 @@ export async function loadAiConfig(): Promise<AiConfig | null> {
return get("/api/ai/config");
}
export async function loadDesktopSettings(): Promise<DesktopSettings> {
return { run_in_background: true };
}
export async function saveDesktopSettings(_settings: DesktopSettings): Promise<void> {
return;
}
// --- AI Conversations ---
export async function saveAiConversation(conversation: AiConversation): Promise<void> {
+12
View File
@@ -40,6 +40,10 @@ export interface JavaRuntimeConfig {
custom_java_path: string | null;
}
export interface DesktopSettings {
run_in_background: boolean;
}
export interface DriverInstallProgress {
step: string;
downloaded?: number;
@@ -117,6 +121,14 @@ export async function loadAiConfig(): Promise<AiConfig | null> {
return invoke("load_ai_config");
}
export async function loadDesktopSettings(): Promise<DesktopSettings> {
return invoke("load_desktop_settings");
}
export async function saveDesktopSettings(settings: DesktopSettings): Promise<void> {
return invoke("save_desktop_settings", { settings });
}
export async function listSystemFonts(): Promise<string[]> {
return invoke("list_system_fonts");
}
+34
View File
@@ -34,6 +34,14 @@ export interface AiConfig {
enableThinking?: boolean;
}
export interface DesktopSettings {
run_in_background: boolean;
}
export const DEFAULT_DESKTOP_SETTINGS: DesktopSettings = {
run_in_background: true,
};
export interface AiProviderPreset extends Omit<AiConfig, "apiKey"> {
label: string;
iconSlug?: string;
@@ -311,9 +319,32 @@ function saveEditorSettings(settings: EditorSettings) {
export const useSettingsStore = defineStore("settings", () => {
const aiConfig = ref<AiConfig>(normalizeAiConfig({ provider: "claude" }));
const isAiConfigLoaded = ref(false);
const desktopSettings = ref<DesktopSettings>({ ...DEFAULT_DESKTOP_SETTINGS });
const isDesktopSettingsLoaded = ref(false);
const editorSettings = ref<EditorSettings>(loadEditorSettings());
async function initDesktopSettings() {
if (isDesktopSettingsLoaded.value) return;
desktopSettings.value = await api.loadDesktopSettings().catch(() => ({ ...DEFAULT_DESKTOP_SETTINGS }));
isDesktopSettingsLoaded.value = true;
}
async function updateDesktopSettings(partial: Partial<DesktopSettings>) {
const previous = desktopSettings.value;
const next = {
...desktopSettings.value,
...partial,
};
desktopSettings.value = next;
try {
await api.saveDesktopSettings(next);
} catch (error) {
desktopSettings.value = previous;
throw error;
}
}
async function initAiConfig() {
if (isAiConfigLoaded.value) return;
const legacy = localStorage.getItem("dbx-ai-config");
@@ -397,7 +428,10 @@ export const useSettingsStore = defineStore("settings", () => {
updateAiConfig,
isConfigured,
editorSettings,
desktopSettings,
updateEditorSettings,
initDesktopSettings,
updateDesktopSettings,
updateColumnFormatter,
upsertCustomColumnFormatter,
deleteCustomColumnFormatter,