From 8a30bb26f9490feb76f5c887fdb99993f0ae2e37 Mon Sep 17 00:00:00 2001 From: SuLea-IT <105108570+SuLea-IT@users.noreply.github.com> Date: Fri, 10 Jul 2026 10:40:10 +0800 Subject: [PATCH] fix(settings): preserve editor font after UI font changes --- .../editor/EditorSettingsDialog.vue | 273 ++++++++---------- .../src/lib/settings/editorSettingsDraft.ts | 84 ++++++ .../app-tests/editorSettingsDraft.test.ts | 27 ++ 3 files changed, 238 insertions(+), 146 deletions(-) create mode 100644 apps/desktop/src/lib/settings/editorSettingsDraft.ts create mode 100644 packages/app-tests/editorSettingsDraft.test.ts diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index 16ff2989f..d164b409b 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -91,6 +91,7 @@ import AiProviderLogo from "@/components/icons/AiProviderLogo.vue"; import AppLogo from "@/components/icons/AppLogo.vue"; import SqlFormatterSettingsPanel from "./SqlFormatterSettingsPanel.vue"; import { APP_THEME_PALETTES, type AppThemeAppearance, type AppThemeMode, type AppThemePalette } from "@/lib/app/appTheme"; +import { editorSettingsDraftChanged, editorSettingsDraftFromSettings, editorSettingsPatchFromDraft, type EditorSettingsDraft } from "@/lib/settings/editorSettingsDraft"; import { useConnectionStore } from "@/stores/connectionStore"; import { useSavedSqlStore } from "@/stores/savedSqlStore"; import { currentLocale, setLocale, type Locale } from "@/i18n"; @@ -336,6 +337,60 @@ function editableSnippet(snippet: SqlSnippet): SqlSnippet { const editSnippets = ref(settingsStore.editorSettings.snippets.map(editableSnippet)); +function currentEditorSettingsDraft(): EditorSettingsDraft { + return { + fontFamily: editFontFamily.value, + fontSize: editFontSize.value, + uiFontFamily: editUiFontFamily.value, + uiScale: editUiScale.value, + theme: editTheme.value, + customThemes: editCustomThemes.value, + activeCustomThemeId: editActiveCustomThemeId.value, + executeMode: editExecuteMode.value, + showExecutionTargetPicker: editShowExecutionTargetPicker.value, + showStatementRunButtons: editShowStatementRunButtons.value, + showCurrentStatementFrame: editShowCurrentStatementFrame.value, + autoAliasTables: editAutoAliasTables.value, + wordWrap: editWordWrap.value, + vimModeEnabled: editVimModeEnabled.value, + autoCloseBrackets: editAutoCloseBrackets.value, + sqlSemanticDiagnosticsMode: editSqlSemanticDiagnosticsMode.value, + confirmDangerousSqlExecution: editConfirmDangerousSqlExecution.value, + confirmUnsavedSqlClose: editConfirmUnsavedSqlClose.value, + appLayout: editAppLayout.value, + showColumnCommentsInHeader: editShowColumnCommentsInHeader.value, + showColumnTypesInHeader: editShowColumnTypesInHeader.value, + compactColumnHeaderActions: editCompactColumnHeaderActions.value, + dataGridQuickEntry: editDataGridQuickEntry.value, + infiniteScroll: editInfiniteScroll.value, + infiniteScrollMaxRows: editInfiniteScrollMaxRows.value, + tableColumnTemplateFields: normalizedEditTableColumnTemplateFields.value, + shortcuts: editShortcuts.value, + sqlFormatter: normalizeSqlFormatterSettings(editSqlFormatter.value), + sidebarActivation: editSidebarActivation.value, + sidebarObjectDisplay: editSidebarObjectDisplay.value, + sidebarTableSearchEnabled: editSidebarTableSearchEnabled.value, + autoSelectActiveSidebarNode: editAutoSelectActiveSidebarNode.value, + openTabsRestoreMode: editOpenTabsRestoreMode.value, + disconnectTabHandlingMode: editDisconnectTabHandlingMode.value, + reuseDataTab: editReuseDataTab.value, + updateNotificationsEnabled: editUpdateNotificationsEnabled.value, + sidebarHideTableComments: editSidebarHideTableComments.value, + sidebarAllowHorizontalScroll: editSidebarAllowHorizontalScroll.value, + sidebarHiddenTablePrefixes: normalizeSidebarHiddenTablePrefixes(editSidebarHiddenTablePrefixes.value), + exportBatchSize: editExportBatchSize.value, + exportRowLimitEnabled: editExportRowLimitEnabled.value, + exportRowLimit: editExportRowLimit.value, + queryExportKeysetOptimizationEnabled: editQueryExportKeysetOptimizationEnabled.value, + updateDownloadSource: editUpdateDownloadSource.value, + toolbarItems: { ...editToolbarItems.value }, + snippets: editSnippets.value, + }; +} + +const editEditorSettingsBase = ref(editorSettingsDraftFromSettings(settingsStore.editorSettings)); +const hasEditorDraftChanges = computed(() => editorSettingsDraftChanged(currentEditorSettingsDraft(), editEditorSettingsBase.value)); + const snippetDialogOpen = ref(false); const snippetEditingId = ref(null); const snippetForm = ref({ label: "", prefix: "", body: "" }); @@ -552,70 +607,85 @@ async function loadSystemFontOptions() { } } +function syncEditorSettingsDraftFromStore() { + editFontFamily.value = settingsStore.editorSettings.fontFamily; + editFontSize.value = settingsStore.editorSettings.fontSize; + editUiFontFamily.value = settingsStore.editorSettings.uiFontFamily; + editUiScale.value = settingsStore.editorSettings.uiScale; + editTheme.value = settingsStore.editorSettings.theme; + editCustomThemes.value = [...settingsStore.editorSettings.customThemes]; + editActiveCustomThemeId.value = settingsStore.editorSettings.activeCustomThemeId; + editExecuteMode.value = settingsStore.editorSettings.executeMode; + editShowExecutionTargetPicker.value = settingsStore.editorSettings.showExecutionTargetPicker; + editShowStatementRunButtons.value = settingsStore.editorSettings.showStatementRunButtons; + editShowCurrentStatementFrame.value = settingsStore.editorSettings.showCurrentStatementFrame; + editAutoAliasTables.value = settingsStore.editorSettings.autoAliasTables; + editWordWrap.value = settingsStore.editorSettings.wordWrap; + editVimModeEnabled.value = settingsStore.editorSettings.vimModeEnabled; + editAutoCloseBrackets.value = settingsStore.editorSettings.autoCloseBrackets; + editSqlSemanticDiagnosticsMode.value = settingsStore.editorSettings.sqlSemanticDiagnosticsMode; + editSqlSemanticDiagnosticsEnabled.value = settingsStore.editorSettings.sqlSemanticDiagnosticsEnabled; + editConfirmDangerousSqlExecution.value = settingsStore.editorSettings.confirmDangerousSqlExecution; + editConfirmUnsavedSqlClose.value = settingsStore.editorSettings.confirmUnsavedSqlClose; + editAppLayout.value = settingsStore.editorSettings.appLayout; + editShowColumnCommentsInHeader.value = settingsStore.editorSettings.showColumnCommentsInHeader; + editShowColumnTypesInHeader.value = settingsStore.editorSettings.showColumnTypesInHeader; + editCompactColumnHeaderActions.value = settingsStore.editorSettings.compactColumnHeaderActions; + editDataGridQuickEntry.value = settingsStore.editorSettings.dataGridQuickEntry; + editInfiniteScroll.value = settingsStore.editorSettings.infiniteScroll; + editInfiniteScrollMaxRows.value = settingsStore.editorSettings.infiniteScrollMaxRows; + editTableColumnTemplateRows.value = tableColumnTemplateRowsFromSettings(settingsStore.editorSettings.tableColumnTemplateFields); + editShortcuts.value = normalizeShortcutSettings(settingsStore.editorSettings.shortcuts); + editSqlFormatter.value = normalizeSqlFormatterSettings(settingsStore.editorSettings.sqlFormatter); + sqlFormatterConfigValid.value = true; + editSidebarActivation.value = settingsStore.editorSettings.sidebarActivation; + editSidebarObjectDisplay.value = settingsStore.editorSettings.sidebarObjectDisplay; + editSidebarTableSearchEnabled.value = settingsStore.editorSettings.sidebarTableSearchEnabled; + editAutoSelectActiveSidebarNode.value = settingsStore.editorSettings.autoSelectActiveSidebarNode; + editOpenTabsRestoreMode.value = settingsStore.editorSettings.openTabsRestoreMode; + editDisconnectTabHandlingMode.value = settingsStore.editorSettings.disconnectTabHandlingMode; + editReuseDataTab.value = settingsStore.editorSettings.reuseDataTab; + editUpdateNotificationsEnabled.value = settingsStore.editorSettings.updateNotificationsEnabled; + editSidebarHiddenTablePrefixes.value = settingsStore.editorSettings.sidebarHiddenTablePrefixes.join("\n"); + editSidebarHideTableComments.value = settingsStore.editorSettings.sidebarHideTableComments; + editSidebarAllowHorizontalScroll.value = settingsStore.editorSettings.sidebarAllowHorizontalScroll; + editExportBatchSize.value = settingsStore.editorSettings.exportBatchSize; + editExportRowLimitEnabled.value = settingsStore.editorSettings.exportRowLimitEnabled; + editExportRowLimit.value = settingsStore.editorSettings.exportRowLimit; + editQueryExportKeysetOptimizationEnabled.value = settingsStore.editorSettings.queryExportKeysetOptimizationEnabled; + editUpdateDownloadSource.value = settingsStore.editorSettings.updateDownloadSource; + editToolbarItems.value = { ...settingsStore.editorSettings.toolbarItems }; + editSnippets.value = settingsStore.editorSettings.snippets.map(editableSnippet); + editEditorSettingsBase.value = editorSettingsDraftFromSettings(settingsStore.editorSettings); +} + // Sync from store when dialog opens watch( () => settingsVisible.value, (open) => { if (open) { - editFontFamily.value = settingsStore.editorSettings.fontFamily; - editFontSize.value = settingsStore.editorSettings.fontSize; - editUiFontFamily.value = settingsStore.editorSettings.uiFontFamily; - editUiScale.value = settingsStore.editorSettings.uiScale; - editTheme.value = settingsStore.editorSettings.theme; - editCustomThemes.value = [...settingsStore.editorSettings.customThemes]; - editActiveCustomThemeId.value = settingsStore.editorSettings.activeCustomThemeId; - editExecuteMode.value = settingsStore.editorSettings.executeMode; - editShowExecutionTargetPicker.value = settingsStore.editorSettings.showExecutionTargetPicker; - editShowStatementRunButtons.value = settingsStore.editorSettings.showStatementRunButtons; - editShowCurrentStatementFrame.value = settingsStore.editorSettings.showCurrentStatementFrame; - editAutoAliasTables.value = settingsStore.editorSettings.autoAliasTables; - editWordWrap.value = settingsStore.editorSettings.wordWrap; - editVimModeEnabled.value = settingsStore.editorSettings.vimModeEnabled; - editAutoCloseBrackets.value = settingsStore.editorSettings.autoCloseBrackets; - editSqlSemanticDiagnosticsMode.value = settingsStore.editorSettings.sqlSemanticDiagnosticsMode; - editSqlSemanticDiagnosticsEnabled.value = settingsStore.editorSettings.sqlSemanticDiagnosticsEnabled; - editConfirmDangerousSqlExecution.value = settingsStore.editorSettings.confirmDangerousSqlExecution; - editConfirmUnsavedSqlClose.value = settingsStore.editorSettings.confirmUnsavedSqlClose; - editAppLayout.value = settingsStore.editorSettings.appLayout; + syncEditorSettingsDraftFromStore(); editShowTrayIcon.value = settingsStore.desktopSettings.show_tray_icon; editQuitOnClose.value = settingsStore.desktopSettings.quit_on_close; editIconTheme.value = settingsStore.desktopSettings.icon_theme; editDebugLoggingEnabled.value = settingsStore.desktopSettings.debug_logging_enabled; editDuckDbWorkerProcessIsolation.value = settingsStore.desktopSettings.duckdb_worker_process_isolation; editSidebarTablePageSize.value = settingsStore.desktopSettings.sidebar_table_page_size ?? DEFAULT_SIDEBAR_TABLE_PAGE_SIZE; - editShowColumnCommentsInHeader.value = settingsStore.editorSettings.showColumnCommentsInHeader; - editShowColumnTypesInHeader.value = settingsStore.editorSettings.showColumnTypesInHeader; - editCompactColumnHeaderActions.value = settingsStore.editorSettings.compactColumnHeaderActions; - editDataGridQuickEntry.value = settingsStore.editorSettings.dataGridQuickEntry; - editInfiniteScroll.value = settingsStore.editorSettings.infiniteScroll; - editInfiniteScrollMaxRows.value = settingsStore.editorSettings.infiniteScrollMaxRows; - editTableColumnTemplateRows.value = tableColumnTemplateRowsFromSettings(settingsStore.editorSettings.tableColumnTemplateFields); - editShortcuts.value = normalizeShortcutSettings(settingsStore.editorSettings.shortcuts); - editSqlFormatter.value = normalizeSqlFormatterSettings(settingsStore.editorSettings.sqlFormatter); - sqlFormatterConfigValid.value = true; - editSidebarActivation.value = settingsStore.editorSettings.sidebarActivation; - editSidebarObjectDisplay.value = settingsStore.editorSettings.sidebarObjectDisplay; - editSidebarTableSearchEnabled.value = settingsStore.editorSettings.sidebarTableSearchEnabled; - editAutoSelectActiveSidebarNode.value = settingsStore.editorSettings.autoSelectActiveSidebarNode; - editOpenTabsRestoreMode.value = settingsStore.editorSettings.openTabsRestoreMode; - editDisconnectTabHandlingMode.value = settingsStore.editorSettings.disconnectTabHandlingMode; - editReuseDataTab.value = settingsStore.editorSettings.reuseDataTab; - editUpdateNotificationsEnabled.value = settingsStore.editorSettings.updateNotificationsEnabled; - editSidebarHiddenTablePrefixes.value = settingsStore.editorSettings.sidebarHiddenTablePrefixes.join("\n"); - editSidebarHideTableComments.value = settingsStore.editorSettings.sidebarHideTableComments; - editSidebarAllowHorizontalScroll.value = settingsStore.editorSettings.sidebarAllowHorizontalScroll; - editExportBatchSize.value = settingsStore.editorSettings.exportBatchSize; - editExportRowLimitEnabled.value = settingsStore.editorSettings.exportRowLimitEnabled; - editExportRowLimit.value = settingsStore.editorSettings.exportRowLimit; - editQueryExportKeysetOptimizationEnabled.value = settingsStore.editorSettings.queryExportKeysetOptimizationEnabled; - editUpdateDownloadSource.value = settingsStore.editorSettings.updateDownloadSource; - editToolbarItems.value = { ...settingsStore.editorSettings.toolbarItems }; - editSnippets.value = settingsStore.editorSettings.snippets.map(editableSnippet); } }, { immediate: true }, ); +watch( + () => settingsStore.editorSettings, + () => { + if (settingsVisible.value && !hasEditorDraftChanges.value) { + syncEditorSettingsDraftFromStore(); + } + }, + { deep: true }, +); + const shortcutConflicts = computed(() => SHORTCUT_DEFINITIONS.flatMap((definition) => { const conflict = findShortcutConflict(definition.id, editShortcuts.value[definition.id], editShortcuts.value); @@ -655,7 +725,7 @@ const filteredShortcutDefinitions = computed(() => { }); }); const hasShortcutConflicts = computed(() => shortcutConflicts.value.length > 0); -const shortcutsChanged = computed(() => JSON.stringify(editShortcuts.value) !== JSON.stringify(settingsStore.editorSettings.shortcuts)); +const shortcutsChanged = computed(() => JSON.stringify(editShortcuts.value) !== JSON.stringify(editEditorSettingsBase.value.shortcuts)); const duckDbWorkerSettingsRequireRestart = computed(() => editDuckDbWorkerProcessIsolation.value !== startupDuckDbWorkerProcessIsolation.value || normalizeDuckDbWorkerMaxProcesses(editDuckDbWorkerMaxProcesses.value) !== startupDuckDbWorkerMaxProcesses.value); const hasBlockingShortcutConflicts = computed(() => shortcutsChanged.value && hasShortcutConflicts.value); const hasBlockingFormatterConfig = computed(() => activeSettingsTab.value === "formatter" && !sqlFormatterConfigValid.value); @@ -663,115 +733,26 @@ const hasApplyBlocker = computed(() => hasBlockingShortcutConflicts.value || has function hasChanges(): boolean { return ( - editFontFamily.value !== settingsStore.editorSettings.fontFamily || - editFontSize.value !== settingsStore.editorSettings.fontSize || - editUiFontFamily.value !== settingsStore.editorSettings.uiFontFamily || - editUiScale.value !== settingsStore.editorSettings.uiScale || - editTheme.value !== settingsStore.editorSettings.theme || - JSON.stringify(editCustomThemes.value) !== JSON.stringify(settingsStore.editorSettings.customThemes) || - editActiveCustomThemeId.value !== settingsStore.editorSettings.activeCustomThemeId || - editExecuteMode.value !== settingsStore.editorSettings.executeMode || - editShowExecutionTargetPicker.value !== settingsStore.editorSettings.showExecutionTargetPicker || - editShowStatementRunButtons.value !== settingsStore.editorSettings.showStatementRunButtons || - editShowCurrentStatementFrame.value !== settingsStore.editorSettings.showCurrentStatementFrame || - editAutoAliasTables.value !== settingsStore.editorSettings.autoAliasTables || - editWordWrap.value !== settingsStore.editorSettings.wordWrap || - editVimModeEnabled.value !== settingsStore.editorSettings.vimModeEnabled || - editAutoCloseBrackets.value !== settingsStore.editorSettings.autoCloseBrackets || - editSqlSemanticDiagnosticsMode.value !== settingsStore.editorSettings.sqlSemanticDiagnosticsMode || - editSqlSemanticDiagnosticsEnabled.value !== settingsStore.editorSettings.sqlSemanticDiagnosticsEnabled || - editConfirmDangerousSqlExecution.value !== settingsStore.editorSettings.confirmDangerousSqlExecution || - editConfirmUnsavedSqlClose.value !== settingsStore.editorSettings.confirmUnsavedSqlClose || - editAppLayout.value !== settingsStore.editorSettings.appLayout || + hasEditorDraftChanges.value || editShowTrayIcon.value !== settingsStore.desktopSettings.show_tray_icon || editQuitOnClose.value !== settingsStore.desktopSettings.quit_on_close || editIconTheme.value !== settingsStore.desktopSettings.icon_theme || editDebugLoggingEnabled.value !== settingsStore.desktopSettings.debug_logging_enabled || editDuckDbWorkerProcessIsolation.value !== settingsStore.desktopSettings.duckdb_worker_process_isolation || normalizeDuckDbWorkerMaxProcesses(editDuckDbWorkerMaxProcesses.value) !== settingsStore.desktopSettings.duckdb_worker_max_processes || - editSidebarTablePageSize.value !== (settingsStore.desktopSettings.sidebar_table_page_size ?? DEFAULT_SIDEBAR_TABLE_PAGE_SIZE) || - editShowColumnCommentsInHeader.value !== settingsStore.editorSettings.showColumnCommentsInHeader || - editShowColumnTypesInHeader.value !== settingsStore.editorSettings.showColumnTypesInHeader || - editCompactColumnHeaderActions.value !== settingsStore.editorSettings.compactColumnHeaderActions || - editDataGridQuickEntry.value !== settingsStore.editorSettings.dataGridQuickEntry || - editInfiniteScroll.value !== settingsStore.editorSettings.infiniteScroll || - editInfiniteScrollMaxRows.value !== settingsStore.editorSettings.infiniteScrollMaxRows || - JSON.stringify(normalizedEditTableColumnTemplateFields.value) !== JSON.stringify(settingsStore.editorSettings.tableColumnTemplateFields) || - JSON.stringify(editShortcuts.value) !== JSON.stringify(settingsStore.editorSettings.shortcuts) || - JSON.stringify(editSqlFormatter.value) !== JSON.stringify(normalizeSqlFormatterSettings(settingsStore.editorSettings.sqlFormatter)) || - editSidebarActivation.value !== settingsStore.editorSettings.sidebarActivation || - editSidebarObjectDisplay.value !== settingsStore.editorSettings.sidebarObjectDisplay || - editSidebarTableSearchEnabled.value !== settingsStore.editorSettings.sidebarTableSearchEnabled || - editAutoSelectActiveSidebarNode.value !== settingsStore.editorSettings.autoSelectActiveSidebarNode || - editOpenTabsRestoreMode.value !== settingsStore.editorSettings.openTabsRestoreMode || - editDisconnectTabHandlingMode.value !== settingsStore.editorSettings.disconnectTabHandlingMode || - editReuseDataTab.value !== settingsStore.editorSettings.reuseDataTab || - editUpdateNotificationsEnabled.value !== settingsStore.editorSettings.updateNotificationsEnabled || - editSidebarHideTableComments.value !== settingsStore.editorSettings.sidebarHideTableComments || - editSidebarAllowHorizontalScroll.value !== settingsStore.editorSettings.sidebarAllowHorizontalScroll || - editExportBatchSize.value !== settingsStore.editorSettings.exportBatchSize || - editExportRowLimitEnabled.value !== settingsStore.editorSettings.exportRowLimitEnabled || - editExportRowLimit.value !== settingsStore.editorSettings.exportRowLimit || - editQueryExportKeysetOptimizationEnabled.value !== settingsStore.editorSettings.queryExportKeysetOptimizationEnabled || - editUpdateDownloadSource.value !== settingsStore.editorSettings.updateDownloadSource || - JSON.stringify(editToolbarItems.value) !== JSON.stringify(settingsStore.editorSettings.toolbarItems) || - JSON.stringify(normalizeSidebarHiddenTablePrefixes(editSidebarHiddenTablePrefixes.value)) !== JSON.stringify(settingsStore.editorSettings.sidebarHiddenTablePrefixes) || - JSON.stringify(editSnippets.value) !== JSON.stringify(settingsStore.editorSettings.snippets) + editSidebarTablePageSize.value !== (settingsStore.desktopSettings.sidebar_table_page_size ?? DEFAULT_SIDEBAR_TABLE_PAGE_SIZE) ); } async function persistSettings() { if (hasApplyBlocker.value) return; - const sidebarObjectDisplayChanged = editSidebarObjectDisplay.value !== settingsStore.editorSettings.sidebarObjectDisplay; + const editorSettingsPatch = editorSettingsPatchFromDraft(currentEditorSettingsDraft(), editEditorSettingsBase.value); + const sidebarObjectDisplayChanged = editorSettingsPatch.sidebarObjectDisplay !== undefined && editorSettingsPatch.sidebarObjectDisplay !== settingsStore.editorSettings.sidebarObjectDisplay; const sidebarTablePageSizeChanged = editSidebarTablePageSize.value !== (settingsStore.desktopSettings.sidebar_table_page_size ?? DEFAULT_SIDEBAR_TABLE_PAGE_SIZE); - settingsStore.updateEditorSettings({ - fontFamily: editFontFamily.value, - fontSize: editFontSize.value, - uiFontFamily: editUiFontFamily.value, - uiScale: editUiScale.value, - theme: editTheme.value, - customThemes: editCustomThemes.value, - activeCustomThemeId: editActiveCustomThemeId.value, - executeMode: editExecuteMode.value, - showExecutionTargetPicker: editShowExecutionTargetPicker.value, - showStatementRunButtons: editShowStatementRunButtons.value, - showCurrentStatementFrame: editShowCurrentStatementFrame.value, - autoAliasTables: editAutoAliasTables.value, - wordWrap: editWordWrap.value, - vimModeEnabled: editVimModeEnabled.value, - autoCloseBrackets: editAutoCloseBrackets.value, - sqlSemanticDiagnosticsMode: editSqlSemanticDiagnosticsMode.value, - confirmDangerousSqlExecution: editConfirmDangerousSqlExecution.value, - confirmUnsavedSqlClose: editConfirmUnsavedSqlClose.value, - appLayout: editAppLayout.value, - showColumnCommentsInHeader: editShowColumnCommentsInHeader.value, - showColumnTypesInHeader: editShowColumnTypesInHeader.value, - compactColumnHeaderActions: editCompactColumnHeaderActions.value, - dataGridQuickEntry: editDataGridQuickEntry.value, - infiniteScroll: editInfiniteScroll.value, - infiniteScrollMaxRows: editInfiniteScrollMaxRows.value, - tableColumnTemplateFields: normalizedEditTableColumnTemplateFields.value, - shortcuts: editShortcuts.value, - sqlFormatter: normalizeSqlFormatterSettings(editSqlFormatter.value), - sidebarActivation: editSidebarActivation.value, - sidebarObjectDisplay: editSidebarObjectDisplay.value, - sidebarTableSearchEnabled: editSidebarTableSearchEnabled.value, - autoSelectActiveSidebarNode: editAutoSelectActiveSidebarNode.value, - openTabsRestoreMode: editOpenTabsRestoreMode.value, - disconnectTabHandlingMode: editDisconnectTabHandlingMode.value, - reuseDataTab: editReuseDataTab.value, - updateNotificationsEnabled: editUpdateNotificationsEnabled.value, - sidebarHideTableComments: editSidebarHideTableComments.value, - sidebarAllowHorizontalScroll: editSidebarAllowHorizontalScroll.value, - sidebarHiddenTablePrefixes: normalizeSidebarHiddenTablePrefixes(editSidebarHiddenTablePrefixes.value), - exportBatchSize: editExportBatchSize.value, - exportRowLimitEnabled: editExportRowLimitEnabled.value, - exportRowLimit: editExportRowLimit.value, - queryExportKeysetOptimizationEnabled: editQueryExportKeysetOptimizationEnabled.value, - updateDownloadSource: editUpdateDownloadSource.value, - toolbarItems: { ...editToolbarItems.value }, - snippets: editSnippets.value, - }); + if (Object.keys(editorSettingsPatch).length > 0) { + settingsStore.updateEditorSettings(editorSettingsPatch); + editEditorSettingsBase.value = editorSettingsDraftFromSettings(settingsStore.editorSettings); + } await settingsStore.updateDesktopSettings({ show_tray_icon: editShowTrayIcon.value, quit_on_close: editQuitOnClose.value, diff --git a/apps/desktop/src/lib/settings/editorSettingsDraft.ts b/apps/desktop/src/lib/settings/editorSettingsDraft.ts new file mode 100644 index 000000000..ac145e1b1 --- /dev/null +++ b/apps/desktop/src/lib/settings/editorSettingsDraft.ts @@ -0,0 +1,84 @@ +import type { EditorSettings } from "@/stores/settingsStore"; + +export const EDITOR_SETTINGS_DRAFT_KEYS = [ + "fontFamily", + "fontSize", + "uiFontFamily", + "uiScale", + "theme", + "customThemes", + "activeCustomThemeId", + "executeMode", + "showExecutionTargetPicker", + "showStatementRunButtons", + "showCurrentStatementFrame", + "autoAliasTables", + "wordWrap", + "vimModeEnabled", + "autoCloseBrackets", + "sqlSemanticDiagnosticsMode", + "confirmDangerousSqlExecution", + "confirmUnsavedSqlClose", + "appLayout", + "showColumnCommentsInHeader", + "showColumnTypesInHeader", + "compactColumnHeaderActions", + "dataGridQuickEntry", + "infiniteScroll", + "infiniteScrollMaxRows", + "tableColumnTemplateFields", + "shortcuts", + "sqlFormatter", + "sidebarActivation", + "sidebarObjectDisplay", + "sidebarTableSearchEnabled", + "autoSelectActiveSidebarNode", + "openTabsRestoreMode", + "disconnectTabHandlingMode", + "reuseDataTab", + "updateNotificationsEnabled", + "sidebarHideTableComments", + "sidebarAllowHorizontalScroll", + "sidebarHiddenTablePrefixes", + "exportBatchSize", + "exportRowLimitEnabled", + "exportRowLimit", + "queryExportKeysetOptimizationEnabled", + "updateDownloadSource", + "toolbarItems", + "snippets", +] as const satisfies readonly (keyof EditorSettings)[]; + +export type EditorSettingsDraftKey = (typeof EDITOR_SETTINGS_DRAFT_KEYS)[number]; +export type EditorSettingsDraft = Pick; + +function cloneDraftValue(value: T): T { + if (value === null || typeof value !== "object") return value; + return JSON.parse(JSON.stringify(value)) as T; +} + +function draftValueChanged(a: unknown, b: unknown): boolean { + return JSON.stringify(a) !== JSON.stringify(b); +} + +export function editorSettingsDraftFromSettings(settings: EditorSettings): EditorSettingsDraft { + const draft = {} as EditorSettingsDraft; + for (const key of EDITOR_SETTINGS_DRAFT_KEYS) { + draft[key] = cloneDraftValue(settings[key]) as never; + } + return draft; +} + +export function editorSettingsPatchFromDraft(draft: EditorSettingsDraft, base: EditorSettingsDraft): Partial { + const patch: Partial = {}; + for (const key of EDITOR_SETTINGS_DRAFT_KEYS) { + if (draftValueChanged(draft[key], base[key])) { + patch[key] = cloneDraftValue(draft[key]) as never; + } + } + return patch; +} + +export function editorSettingsDraftChanged(draft: EditorSettingsDraft, base: EditorSettingsDraft): boolean { + return EDITOR_SETTINGS_DRAFT_KEYS.some((key) => draftValueChanged(draft[key], base[key])); +} diff --git a/packages/app-tests/editorSettingsDraft.test.ts b/packages/app-tests/editorSettingsDraft.test.ts new file mode 100644 index 000000000..0edff9d51 --- /dev/null +++ b/packages/app-tests/editorSettingsDraft.test.ts @@ -0,0 +1,27 @@ +import assert from "node:assert/strict"; +import { test } from "vitest"; +import { createPinia, setActivePinia } from "pinia"; +import { DEFAULT_UI_FONT_FAMILY } from "../../apps/desktop/src/lib/app/appFonts.ts"; +import { editorSettingsDraftFromSettings, editorSettingsPatchFromDraft } from "../../apps/desktop/src/lib/settings/editorSettingsDraft.ts"; +import { DEFAULT_EDITOR_SETTINGS, useSettingsStore } from "../../apps/desktop/src/stores/settingsStore.ts"; + +test("keeps a loaded editor font when an old appearance draft only changes the UI font", () => { + setActivePinia(createPinia()); + const store = useSettingsStore(); + const savedEditorFont = "'Cascadia Code', 'Cascadia Mono', monospace"; + + store.updateEditorSettings({ fontFamily: savedEditorFont }); + + const staleDraftBase = editorSettingsDraftFromSettings(DEFAULT_EDITOR_SETTINGS); + const staleDraft = { + ...staleDraftBase, + uiFontFamily: `"Aptos", ${DEFAULT_UI_FONT_FAMILY}`, + }; + + const patch = editorSettingsPatchFromDraft(staleDraft, staleDraftBase); + store.updateEditorSettings(patch); + + assert.equal(patch.fontFamily, undefined); + assert.equal(store.editorSettings.fontFamily, savedEditorFont); + assert.equal(store.editorSettings.uiFontFamily, staleDraft.uiFontFamily); +});