diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index b1267ddfc3..30a8da7ad2 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -108,6 +108,7 @@ import { formatShortcutDisplay } from "@/lib/editor/shortcutDisplay"; import { normalizeSidebarHiddenTablePrefixes } from "@/lib/sidebar/sidebarTableNameDisplay"; import { currentStatementFrameRangeTo } from "@/lib/sql/currentStatementFrame"; import { currentStatementFrameLayer } from "@/lib/editor/codemirrorCurrentStatementFrameLayer"; +import { buildQueryEditorLineNumbersExtension } from "@/lib/editor/queryEditorLineNumbers"; import { normalizeSqlFormatterSettings, type SqlFormatterSettings } from "@/lib/sql/sqlFormatterConfig"; import { validateConfigName, generateId, type AiConfigItem, type ConfigNameValidationResult } from "@/lib/ai/aiConfigList"; import { currentExecutableStatementRange, type SqlTextRange } from "@/lib/sql/sqlStatementRanges"; @@ -3409,6 +3410,7 @@ const previewSettings = computed<{ appPalette: AppThemePalette; customColors?: CustomThemeColors; showStatementRunButtons: boolean; + showLineNumbers: boolean; showCurrentStatementFrame: boolean; }>(() => ({ fontFamily: editFontFamily.value, @@ -3418,6 +3420,7 @@ const previewSettings = computed<{ appPalette: themePalette.value, customColors: getPreviewCustomThemeColors(), showStatementRunButtons: editShowStatementRunButtons.value, + showLineNumbers: editShowLineNumbers.value, showCurrentStatementFrame: editShowCurrentStatementFrame.value, })); @@ -3440,7 +3443,9 @@ let fontThemeComp: import("@codemirror/state").Compartment | null = null; let themeComp: import("@codemirror/state").Compartment | null = null; let diagnosticComp: import("@codemirror/state").Compartment | null = null; let previewRunGutterComp: import("@codemirror/state").Compartment | null = null; +let previewLineNumbersComp: import("@codemirror/state").Compartment | null = null; let currentStatementFrameComp: import("@codemirror/state").Compartment | null = null; +let previewLineNumbersFactory: typeof import("@codemirror/view").lineNumbers | null = null; let setPreviewDiagnosticsEffect: import("@codemirror/state").StateEffectType | null = null; let setPreviewRunHighlightEffect: | import("@codemirror/state").StateEffectType<{ @@ -3555,16 +3560,23 @@ function previewCurrentStatementFrameTo(view: import("@codemirror/view").EditorV return currentStatementFrameRangeTo(view.state.doc, range); } +function buildPreviewLineNumbersExtension(enabled: boolean) { + return buildQueryEditorLineNumbersExtension(previewLineNumbersFactory, enabled, { + domEventHandlers: {}, + }); +} + watch( [previewSettings, editCustomThemes, editActiveCustomThemeId], async ([ss]) => { - if (!previewView.value || !fontThemeComp || !themeComp || !editorViewModule) return; + if (!previewView.value || !fontThemeComp || !themeComp || !previewLineNumbersComp || !editorViewModule) return; const themeExt = await loadEditorTheme(ss.theme, ss.appAppearance, ss.customColors, ss.appPalette); previewView.value.dispatch({ effects: [ themeComp.reconfigure(themeExt), fontThemeComp.reconfigure(editorFontTheme(editorViewModule.EditorView, ss.fontSize, ss.fontFamily)), + previewLineNumbersComp.reconfigure(buildPreviewLineNumbersExtension(ss.showLineNumbers)), ...(previewRunGutterComp ? [previewRunGutterComp.reconfigure(buildPreviewRunGutterExtension())] : []), ...(currentStatementFrameComp ? [currentStatementFrameComp.reconfigure(buildPreviewCurrentStatementFrameExtension(editorViewModule, ss.showCurrentStatementFrame))] : []), ], @@ -3588,7 +3600,9 @@ function cleanupPreviewEditor() { themeComp = null; diagnosticComp = null; previewRunGutterComp = null; + previewLineNumbersComp = null; currentStatementFrameComp = null; + previewLineNumbersFactory = null; setPreviewDiagnosticsEffect = null; setPreviewRunHighlightEffect = null; editorViewModule = null; @@ -3613,7 +3627,7 @@ watch(previewRef, async (el) => { previewInitialized = true; if (previewView.value) return; - const [{ EditorView, Decoration, ViewPlugin, gutter, GutterMarker, layer, RectangleMarker }, { EditorState, Compartment, StateEffect, StateField }, { sql, MySQL }, { basicSetup }] = await Promise.all([ + const [{ EditorView, Decoration, ViewPlugin, gutter, GutterMarker, layer, RectangleMarker, lineNumbers, highlightActiveLineGutter }, { EditorState, Compartment, StateEffect, StateField }, { sql, MySQL }, { basicSetup }] = await Promise.all([ import("@codemirror/view"), import("@codemirror/state"), import("@codemirror/lang-sql"), @@ -3627,10 +3641,12 @@ watch(previewRef, async (el) => { layer, RectangleMarker, } as typeof import("@codemirror/view"); + previewLineNumbersFactory = lineNumbers; fontThemeComp = new Compartment(); themeComp = new Compartment(); diagnosticComp = new Compartment(); previewRunGutterComp = new Compartment(); + previewLineNumbersComp = new Compartment(); currentStatementFrameComp = new Compartment(); setPreviewDiagnosticsEffect = StateEffect.define(); setPreviewRunHighlightEffect = StateEffect.define<{ @@ -3641,6 +3657,7 @@ watch(previewRef, async (el) => { const ss = previewSettings.value; const themeExt = await loadEditorTheme(ss.theme, ss.appAppearance, ss.customColors, ss.appPalette); + const previewBasicSetup = (basicSetup as readonly import("@codemirror/state").Extension[]).slice(2); const diagnosticTheme = EditorView.baseTheme({ ".cm-settings-preview-sql-error": { textDecoration: "underline wavy var(--destructive)", @@ -3719,7 +3736,9 @@ watch(previewRef, async (el) => { const state = EditorState.create({ doc: currentPreviewSql(), extensions: [ - basicSetup, + previewLineNumbersComp.of(buildPreviewLineNumbersExtension(ss.showLineNumbers)), + highlightActiveLineGutter(), + previewBasicSetup, sql({ dialect: MySQL }), themeComp.of(themeExt), fontThemeComp.of(editorFontTheme(EditorView, ss.fontSize, ss.fontFamily)), diff --git a/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.previewPosition.spec.ts b/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.previewPosition.spec.ts index 26f1471d25..0a5530d986 100644 --- a/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.previewPosition.spec.ts +++ b/apps/desktop/src/components/editor/__tests__/EditorSettingsDialog.previewPosition.spec.ts @@ -28,4 +28,14 @@ describe("EditorSettingsDialog live preview placement", () => { expect(dialogSource).toContain("watch(previewRef, async (el) => {"); expect(dialogSource).toContain("cleanupPreviewEditor();"); }); + + it("updates preview line-number visibility from the unsaved editor draft", () => { + expect(dialogSource).toContain("showLineNumbers: editShowLineNumbers.value"); + expect(dialogSource).toContain('import { buildQueryEditorLineNumbersExtension } from "@/lib/editor/queryEditorLineNumbers";'); + expect(dialogSource).toContain('let previewLineNumbersComp: import("@codemirror/state").Compartment | null = null;'); + expect(dialogSource).toContain('const previewBasicSetup = (basicSetup as readonly import("@codemirror/state").Extension[]).slice(2);'); + expect(dialogSource).toContain("previewLineNumbersComp.of(buildPreviewLineNumbersExtension(ss.showLineNumbers))"); + expect(dialogSource).toContain("previewLineNumbersComp.reconfigure(buildPreviewLineNumbersExtension(ss.showLineNumbers))"); + expect(dialogSource).toContain("return buildQueryEditorLineNumbersExtension(previewLineNumbersFactory, enabled"); + }); });