From 7dd4bff875ffda91891e74216ac3ab8f00d2a91d Mon Sep 17 00:00:00 2001 From: Apostol Apostolov Date: Sun, 19 Jul 2026 04:13:37 -0700 Subject: [PATCH] feat: control active line highlight (setting + theme slot) Add markdownEditorOptimized.activeLineHighlight.visible (default true) to disable the cursor/active line background in Live and Source modes. Add optional theme field activeLineBackground so themes can restyle the highlight or set it to transparent. Empty keeps the built-in base03 mix. Closes #70 --- CHANGELOG.md | 4 ++++ docs/theming.md | 9 +++++++++ package.json | 16 ++++++++++++++-- src/extension.ts | 6 ++++++ src/extension/panelSession.ts | 3 +++ src/shared/extensionConfig.ts | 5 +++++ src/shared/themeDefaults.ts | 33 +++++++++++++++++++++++++++++++++ webview/src/editor.ts | 25 +++++++++++++++++++++++-- webview/src/helpers/theme.ts | 6 +++++- webview/src/index.ts | 20 ++++++++++++++++++++ webview/src/styles.css | 10 ++++++++++ webview/src/types.d.ts | 4 +++- 12 files changed, 135 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91ce234..d675eb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Markdown Editor Optimized (MEO) --- +## Unreleased +- Added setting to toggle active line highlight (`markdownEditorOptimized.activeLineHighlight.visible`) +- Added theme slot `activeLineBackground` to restyle or clear the active line highlight + ## 0.1.26 - Improved dark Mermaid diagram line contrast - Fixed live find matches and preserve active highlight diff --git a/docs/theming.md b/docs/theming.md index 075397e..129dc65 100644 --- a/docs/theming.md +++ b/docs/theming.md @@ -57,6 +57,15 @@ Guide on how to customise the editor background, colors, syntax highlighting, fo - Exported HTML/PDF documents use this background color. - Color values must be valid `#hex`, `rgb()/rgba()`, `hsl()/hsla()`, or `var(--...)`. +## activeLineBackground + +`activeLineBackground` controls the background highlight on the line that currently has the cursor (Live and Source modes). + +- Optional. Leave empty (or omit) to use the built-in mix derived from `base03`. +- Set to a color (`#hex`, `rgb()`, `hsl()`, `var(--...)`) to restyle the highlight for your theme. +- Set to `transparent` to remove the active-line wash via the theme (without turning off CodeMirror's active-line machinery globally). +- There is also a workspace setting `markdownEditorOptimized.activeLineHighlight.visible` (default `true`) that fully disables the active-line highlight in both modes when set to `false`. + ## colors `colors` is the 9-color base palette (`base01` to `base09`) that drives the theme. diff --git a/package.json b/package.json index c504a16..d354bdc 100644 --- a/package.json +++ b/package.json @@ -191,6 +191,12 @@ "order": 4, "description": "Show line numbers." }, + "markdownEditorOptimized.activeLineHighlight.visible": { + "type": "boolean", + "default": true, + "order": 4, + "description": "Show the cursor/active line background highlight in Live and Source modes." + }, "markdownEditorOptimized.gitChanges.visible": { "type": "boolean", "default": true, @@ -319,13 +325,14 @@ "h6FontWeight": "600", "liveLineHeight": 1.5, "sourceLineHeight": 1.5 - } + }, + "activeLineBackground": "" }, "defaultSnippets": [ { "label": "One Monokai theme payload", "description": "Insert the full theme object with sensible defaults.", - "body": "{\n\t\"id\": \"one-monokai\",\n\t\"name\": \"One Monokai\",\n\t\"backgroundColor\": \"var(--vscode-editor-background)\",\n\t\"colors\": {\n\t\t\"base01\": \"var(--vscode-editor-foreground)\",\n\t\t\"base02\": \"#676f7d\",\n\t\t\"base03\": \"#3e444d\",\n\t\t\"base04\": \"#e06c75\",\n\t\t\"base05\": \"#61afef\",\n\t\t\"base06\": \"#66d9ef\",\n\t\t\"base07\": \"#e5c07b\",\n\t\t\"base08\": \"#c678dd\",\n\t\t\"base09\": \"#98c379\"\n\t},\n\t\"syntaxTokens\": {},\n\t\"fonts\": {\n\t\t\"liveFont\": \"\",\n\t\t\"sourceFont\": \"\",\n\t\t\"liveFontWeight\": \"\",\n\t\t\"sourceFontWeight\": \"\",\n\t\t\"liveFontSize\": null,\n\t\t\"sourceFontSize\": null,\n\t\t\"h1FontSize\": 1.6,\n\t\t\"h2FontSize\": 1.5,\n\t\t\"h3FontSize\": 1.3,\n\t\t\"h4FontSize\": 1.2,\n\t\t\"h5FontSize\": 1.1,\n\t\t\"h6FontSize\": 1,\n\t\t\"h1FontWeight\": \"600\",\n\t\t\"h2FontWeight\": \"600\",\n\t\t\"h3FontWeight\": \"600\",\n\t\t\"h4FontWeight\": \"600\",\n\t\t\"h5FontWeight\": \"600\",\n\t\t\"h6FontWeight\": \"600\",\n\t\t\"liveLineHeight\": 1.5,\n\t\t\"sourceLineHeight\": 1.5\n\t}\n}" + "body": "{\n\t\"id\": \"one-monokai\",\n\t\"name\": \"One Monokai\",\n\t\"backgroundColor\": \"var(--vscode-editor-background)\",\n\t\"activeLineBackground\": \"\",\n\t\"colors\": {\n\t\t\"base01\": \"var(--vscode-editor-foreground)\",\n\t\t\"base02\": \"#676f7d\",\n\t\t\"base03\": \"#3e444d\",\n\t\t\"base04\": \"#e06c75\",\n\t\t\"base05\": \"#61afef\",\n\t\t\"base06\": \"#66d9ef\",\n\t\t\"base07\": \"#e5c07b\",\n\t\t\"base08\": \"#c678dd\",\n\t\t\"base09\": \"#98c379\"\n\t},\n\t\"syntaxTokens\": {},\n\t\"fonts\": {\n\t\t\"liveFont\": \"\",\n\t\t\"sourceFont\": \"\",\n\t\t\"liveFontWeight\": \"\",\n\t\t\"sourceFontWeight\": \"\",\n\t\t\"liveFontSize\": null,\n\t\t\"sourceFontSize\": null,\n\t\t\"h1FontSize\": 1.6,\n\t\t\"h2FontSize\": 1.5,\n\t\t\"h3FontSize\": 1.3,\n\t\t\"h4FontSize\": 1.2,\n\t\t\"h5FontSize\": 1.1,\n\t\t\"h6FontSize\": 1,\n\t\t\"h1FontWeight\": \"600\",\n\t\t\"h2FontWeight\": \"600\",\n\t\t\"h3FontWeight\": \"600\",\n\t\t\"h4FontWeight\": \"600\",\n\t\t\"h5FontWeight\": \"600\",\n\t\t\"h6FontWeight\": \"600\",\n\t\t\"liveLineHeight\": 1.5,\n\t\t\"sourceLineHeight\": 1.5\n\t}\n}" } ], "description": "Theme payload containing the editor background, base palette colors, optional token overrides, and font settings.", @@ -347,6 +354,11 @@ "default": "var(--vscode-editor-background)", "description": "Editor document background color (hex/rgb/hsl/var)." }, + "activeLineBackground": { + "type": "string", + "default": "", + "description": "Optional cursor/active line background color (hex/rgb/hsl/var/transparent). Leave empty to use the built-in mix from base03. Set to \"transparent\" to remove the highlight via theme." + }, "colors": { "type": "object", "default": {}, diff --git a/src/extension.ts b/src/extension.ts index 327ae98..4d86a2c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -34,6 +34,7 @@ import { LINE_NUMBERS_LEGACY_SETTING_KEY, LINE_NUMBERS_LEGACY_VISIBLE_SETTING_KEY, LINE_NUMBERS_SETTING_KEY, + ACTIVE_LINE_HIGHLIGHT_SETTING_KEY, OUTLINE_VISIBLE_KEY, VIM_MODE_BEHAVIOR_SETTING_KEY, VIM_MODE_SETTING_KEY, @@ -50,6 +51,7 @@ import { getGitChangesGutterEnabled, getGitDiffLineHighlightsEnabled, getLineNumbersEnabled, + getActiveLineHighlightEnabled, getOutlinePosition, getOutlineVisible, getContentMaxWidthEnabled, @@ -536,6 +538,10 @@ class MarkdownWebviewProvider implements vscode.CustomTextEditorProvider { this.broadcast({ type: 'lineNumbersChanged', enabled: getLineNumbersEnabled(this.context) }); } + if (event.affectsConfiguration(`${EXTENSION_CONFIG_SECTION}.${ACTIVE_LINE_HIGHLIGHT_SETTING_KEY}`)) { + this.broadcast({ type: 'activeLineHighlightChanged', enabled: getActiveLineHighlightEnabled() }); + } + if ( event.affectsConfiguration(`${EXTENSION_CONFIG_SECTION}.${GIT_CHANGES_GUTTER_SETTING_KEY}`) || event.affectsConfiguration(`${EXTENSION_CONFIG_SECTION}.${GIT_CHANGES_GUTTER_LEGACY_VISIBLE_SETTING_KEY}`) || diff --git a/src/extension/panelSession.ts b/src/extension/panelSession.ts index 08f2f6e..38248ce 100644 --- a/src/extension/panelSession.ts +++ b/src/extension/panelSession.ts @@ -9,6 +9,7 @@ import { SPELL_CHECK_SETTING_KEY, getContentMaxWidthEnabled, getLineNumbersEnabled, + getActiveLineHighlightEnabled, getGitChangesGutterEnabled, getGitDiffLineHighlightsEnabled, getSpellCheckEnabled, @@ -53,6 +54,7 @@ type InitMessage = { diagnostics: SerializedDiagnostic[]; mode: EditorMode; lineNumbers: boolean; + activeLineHighlight: boolean; gitChangesGutter: boolean; gitDiffLineHighlights: boolean; spellCheckEnabled: boolean; @@ -546,6 +548,7 @@ export function createPanelSessionController(params: PanelSessionControllerParam diagnostics: serializeDiagnostics(document), mode, lineNumbers: getLineNumbersEnabled(context), + activeLineHighlight: getActiveLineHighlightEnabled(), gitChangesGutter: getGitChangesGutterEnabled(context), gitDiffLineHighlights: getGitDiffLineHighlightsEnabled(), spellCheckEnabled: getSpellCheckEnabled(), diff --git a/src/shared/extensionConfig.ts b/src/shared/extensionConfig.ts index f0c1f95..06851bb 100644 --- a/src/shared/extensionConfig.ts +++ b/src/shared/extensionConfig.ts @@ -18,6 +18,7 @@ export const VIM_MODE_SETTING_KEY = 'vimMode.enabled'; export const CODE_BLOCKS_VSCODE_THEME_SETTING_KEY = 'codeBlocks.useVscodeTheme'; export const REMEMBER_POSITION_LINES_SETTING_KEY = 'rememberPosition.lines'; export const CONTENT_MAX_WIDTH_SETTING_KEY = 'contentMaxWidth.visible'; +export const ACTIVE_LINE_HIGHLIGHT_SETTING_KEY = 'activeLineHighlight.visible'; export const LINE_NUMBERS_LEGACY_SETTING_KEY = 'lineNumbers.enabled'; export const LINE_NUMBERS_LEGACY_VISIBLE_SETTING_KEY = 'lineNumbers.visibility'; export const GIT_CHANGES_GUTTER_LEGACY_VISIBLE_SETTING_KEY = 'gitChanges.visibility'; @@ -75,6 +76,10 @@ export function getSpellCheckEnabled(): boolean { return vscode.workspace.getConfiguration(EXTENSION_CONFIG_SECTION).get(SPELL_CHECK_SETTING_KEY, true); } +export function getActiveLineHighlightEnabled(): boolean { + return vscode.workspace.getConfiguration(EXTENSION_CONFIG_SECTION).get(ACTIVE_LINE_HIGHLIGHT_SETTING_KEY, true); +} + export function getVimModeEnabled(context: vscode.ExtensionContext): boolean { const config = vscode.workspace.getConfiguration(EXTENSION_CONFIG_SECTION); const behavior = getVimModeBehavior(config); diff --git a/src/shared/themeDefaults.ts b/src/shared/themeDefaults.ts index 06d0f97..450fe2c 100644 --- a/src/shared/themeDefaults.ts +++ b/src/shared/themeDefaults.ts @@ -335,6 +335,12 @@ export type ThemeSettings = { id: string; name: string; backgroundColor: string; + /** + * Cursor/active line background in Live and Source modes. + * Empty string uses the built-in mix from base03. + * Set to "transparent" (or any valid color) to override. + */ + activeLineBackground: string; colors: ThemeColors; syntaxTokens: ThemeSyntaxTokens; fonts: ThemeFonts; @@ -405,6 +411,7 @@ const createThemeFromColors = (params: { id: string; name: string; backgroundColor?: string; + activeLineBackground?: string; colors?: Partial; syntaxTokenPaletteOverrides?: Partial; syntaxTokenOverrides?: Partial; @@ -416,6 +423,7 @@ const createThemeFromColors = (params: { id: params.id, name: params.name, backgroundColor: params.backgroundColor ?? defaultThemeBackgroundColor, + activeLineBackground: params.activeLineBackground ?? '', colors, syntaxTokens: { ...buildSyntaxTokenColors(colors, params.syntaxTokenPaletteOverrides), @@ -596,9 +604,24 @@ const isValidThemeColor = (value: string): boolean => { return false; } const candidate = value.trim(); + if (candidate.toLowerCase() === 'transparent') { + return true; + } return hexColorRegex.test(candidate) || rgbColorRegex.test(candidate) || hslColorRegex.test(candidate) || cssVarColorRegex.test(candidate); }; +/** Empty string means "use built-in mix from base03". */ +const resolveOptionalThemeColor = (value: unknown): string => { + if (typeof value !== 'string') { + return ''; + } + const trimmed = value.trim(); + if (!trimmed) { + return ''; + } + return isValidThemeColor(trimmed) ? trimmed : ''; +}; + const sanitizeThemeColor = (value: unknown, fallback: string): string => { if (typeof value !== 'string') { return fallback; @@ -746,6 +769,7 @@ export const serializeThemeSettings = (theme: ThemeSettings): ThemeSettingsPaylo id: theme.id, name: theme.name, backgroundColor: theme.backgroundColor, + activeLineBackground: theme.activeLineBackground, colors: { ...theme.colors }, syntaxTokens, fonts: { ...theme.fonts } @@ -758,6 +782,7 @@ export const resolveTheme = (themeOverride?: Partial): ThemeSetti id: normalizeString(themeOverride?.id, defaultThemeSettings.id), name: normalizeString(themeOverride?.name, defaultThemeSettings.name), backgroundColor: sanitizeThemeColor(themeOverride?.backgroundColor, defaultThemeBackgroundColor), + activeLineBackground: resolveOptionalThemeColor(themeOverride?.activeLineBackground), colors, syntaxTokens: resolveThemeSyntaxTokens(themeOverride?.syntaxTokens, colors), fonts: resolveThemeFonts(themeOverride?.fonts) @@ -793,6 +818,14 @@ export const validateThemePayload = (value: unknown): ThemeValidationResult => { } } + if (value.activeLineBackground !== undefined) { + if (typeof value.activeLineBackground !== 'string') { + errors.push('Theme "activeLineBackground" must be a string.'); + } else if (value.activeLineBackground.trim() && !isValidThemeColor(value.activeLineBackground)) { + errors.push('Theme "activeLineBackground" must be empty (default), transparent, or a valid hex, rgb, hsl, or var(--...) color string.'); + } + } + const rawColors = value.colors; if (!isRecord(rawColors)) { errors.push('Theme "colors" must be an object.'); diff --git a/webview/src/editor.ts b/webview/src/editor.ts index 94fd0bd..a0e3c5f 100644 --- a/webview/src/editor.ts +++ b/webview/src/editor.ts @@ -166,6 +166,7 @@ export function createEditor({ initialTopLine = null, initialTopLineOffset = 0, initialLineNumbers = true, + initialActiveLineHighlight = true, initialGitGutter = true, initialVimMode = false, initialVimKeybindings = [], @@ -179,8 +180,10 @@ export function createEditor({ const modeCompartment = new Compartment(); const gitGutterCompartment = new Compartment(); const vimCompartment = new Compartment(); + const activeLineHighlightCompartment = new Compartment(); const startMode = initialMode === 'live' ? 'live' : 'source'; let lineNumbersVisible = initialLineNumbers !== false; + let activeLineHighlightVisible = initialActiveLineHighlight !== false; let gitGutterVisible = initialGitGutter !== false; let vimModeEnabled = initialVimMode === true; let vimKeybindings = initialVimKeybindings; @@ -1479,8 +1482,10 @@ export function createEditor({ lineNumbers(), ...gitDiffGutterBaselineExtensions(), gitGutterCompartment.of(startMode === 'live' ? gitDiffGutterLiveRenderExtensions() : gitDiffGutterRenderExtensions()), - highlightActiveLineGutter(), - highlightActiveLine(), + activeLineHighlightCompartment.of(activeLineHighlightVisible ? [ + highlightActiveLineGutter(), + highlightActiveLine() + ] : []), shikiCodeHighlight, EditorView.lineWrapping, scrollPastEnd(), @@ -1747,6 +1752,7 @@ export function createEditor({ syncLineNumbersVisibility(); syncGitGutterVisibility(); syncSelectionClass(); + view.dom.classList.toggle('meo-active-line-highlight-hidden', !activeLineHighlightVisible); view.dispatch({ effects: setDiagnosticsEffect.of(currentDiagnostics) }); emitSelectionChange(); @@ -1954,6 +1960,21 @@ export function createEditor({ lineNumbersVisible = nextVisible; syncLineNumbersVisibility(); }, + setActiveLineHighlight(visible) { + const nextVisible = visible !== false; + if (nextVisible === activeLineHighlightVisible) { + return; + } + activeLineHighlightVisible = nextVisible; + view.dispatch({ + effects: activeLineHighlightCompartment.reconfigure( + activeLineHighlightVisible + ? [highlightActiveLineGutter(), highlightActiveLine()] + : [] + ) + }); + view.dom.classList.toggle('meo-active-line-highlight-hidden', !activeLineHighlightVisible); + }, setGitGutterVisible(visible) { const nextVisible = visible !== false; if (nextVisible === gitGutterVisible) { diff --git a/webview/src/helpers/theme.ts b/webview/src/helpers/theme.ts index a8fe40e..185cea6 100644 --- a/webview/src/helpers/theme.ts +++ b/webview/src/helpers/theme.ts @@ -123,9 +123,13 @@ export const applyThemeSettings = (theme?: ThemeSettings): void => { `color-mix(in srgb, ${resolvedTheme.colors.base05} 28%, transparent 72%)` ); rootStyle.setProperty('--meo-caret-color', resolvedTheme.colors.base01); + const activeLineBackground = typeof resolvedTheme.activeLineBackground === 'string' + ? resolvedTheme.activeLineBackground.trim() + : ''; rootStyle.setProperty( '--meo-active-line-bg', - `color-mix(in srgb, ${resolvedTheme.colors.base03} 35%, transparent 65%)` + activeLineBackground + || `color-mix(in srgb, ${resolvedTheme.colors.base03} 35%, transparent 65%)` ); for (const key of themeColorKeys) { diff --git a/webview/src/index.ts b/webview/src/index.ts index a488612..bc9e0a3 100644 --- a/webview/src/index.ts +++ b/webview/src/index.ts @@ -150,6 +150,7 @@ let vimKeybindingsState: VimKeybinding[] = []; let vimLeaderState = '\\'; let lineNumbersVisible = true; +let activeLineHighlightVisible = true; let gitChangesGutterVisible = true; let gitDiffLineHighlightsEnabled = true; let spellCheckEnabled = true; @@ -242,6 +243,16 @@ const setLineNumbersVisible = (visible, { post = true } = {}) => { } }; +const setActiveLineHighlightVisible = (visible: boolean) => { + const nextVisible = visible !== false; + const changed = nextVisible !== activeLineHighlightVisible; + activeLineHighlightVisible = nextVisible; + if (changed) { + editor?.setActiveLineHighlight?.(activeLineHighlightVisible); + } + root.classList.toggle('meo-active-line-highlight-hidden', !activeLineHighlightVisible); +}; + const setGitChangesGutterVisible = (visible, { post = true } = {}) => { const nextVisible = visible !== false; const changed = nextVisible !== gitChangesGutterVisible; @@ -1246,6 +1257,7 @@ const mountInitialEditor = async () => { initialTopLine, initialTopLineOffset, initialLineNumbers: lineNumbersVisible, + initialActiveLineHighlight: activeLineHighlightVisible, initialGitGutter: gitChangesGutterVisible, initialVimMode: vimModeEnabled, initialVimKeybindings: vimKeybindingsState, @@ -1368,6 +1380,9 @@ const handleInit = (message: any) => { if (typeof message.lineNumbers === 'boolean') { setLineNumbersVisible(message.lineNumbers, { post: false }); } + if (typeof message.activeLineHighlight === 'boolean') { + setActiveLineHighlightVisible(message.activeLineHighlight); + } if (typeof message.gitChangesGutter === 'boolean') { setGitChangesGutterVisible(message.gitChangesGutter, { post: false }); } @@ -1622,6 +1637,11 @@ window.addEventListener('message', (event) => { return; } + if (message.type === 'activeLineHighlightChanged') { + setActiveLineHighlightVisible(message.enabled === true); + return; + } + if (message.type === 'gitChangesGutterChanged') { setGitChangesGutterVisible(message.enabled, { post: false }); return; diff --git a/webview/src/styles.css b/webview/src/styles.css index e517ecf..4124ae6 100644 --- a/webview/src/styles.css +++ b/webview/src/styles.css @@ -1002,6 +1002,16 @@ body { background-color: var(--meo-active-line-bg); } +.cm-editor.meo-active-line-highlight-hidden .cm-activeLine, +.cm-editor.meo-active-line-highlight-hidden .cm-activeLine.meo-md-code-block, +.cm-editor.meo-mode-live.meo-active-line-highlight-hidden .cm-activeLine.meo-md-code-block { + background-color: transparent; +} + +.cm-editor.meo-active-line-highlight-hidden .cm-gutterElement.cm-activeLineGutter { + background: transparent !important; +} + .cm-editor.meo-table-interaction-active .cm-activeLine { background-color: transparent; } diff --git a/webview/src/types.d.ts b/webview/src/types.d.ts index bb3bcbc..1726e99 100644 --- a/webview/src/types.d.ts +++ b/webview/src/types.d.ts @@ -37,7 +37,7 @@ type VimKeybinding = { }; type ExtensionMessage = - | { type: 'init'; text: string; version: number; diagnostics: EditorDiagnostic[]; theme: ThemeSettings; mode: 'live' | 'source'; outlinePosition: 'left' | 'right'; outlineVisible: boolean; lineNumbers: boolean; gitChangesGutter: boolean; gitDiffLineHighlights: boolean; spellCheckEnabled: boolean; contentMaxWidthEnabled: boolean; vimMode: boolean; vimKeybindings: VimKeybinding[]; vimLeader: string; findOptions: { wholeWord: boolean; caseSensitive: boolean }; restoreTopLine?: number; restoreTopLineOffset?: number } + | { type: 'init'; text: string; version: number; diagnostics: EditorDiagnostic[]; theme: ThemeSettings; mode: 'live' | 'source'; outlinePosition: 'left' | 'right'; outlineVisible: boolean; lineNumbers: boolean; activeLineHighlight: boolean; gitChangesGutter: boolean; gitDiffLineHighlights: boolean; spellCheckEnabled: boolean; contentMaxWidthEnabled: boolean; vimMode: boolean; vimKeybindings: VimKeybinding[]; vimLeader: string; findOptions: { wholeWord: boolean; caseSensitive: boolean }; restoreTopLine?: number; restoreTopLineOffset?: number } | { type: 'docChanged'; text: string; version: number } | { type: 'applied'; version: number } | { type: 'focusEditor' } @@ -47,6 +47,7 @@ type ExtensionMessage = | { type: 'outlinePositionChanged'; position: 'left' | 'right' } | { type: 'outlineVisibilityChanged'; visible: boolean } | { type: 'lineNumbersChanged'; enabled: boolean } + | { type: 'activeLineHighlightChanged'; enabled: boolean } | { type: 'gitChangesGutterChanged'; enabled: boolean } | { type: 'gitDiffLineHighlightsChanged'; enabled: boolean } | { type: 'spellCheckChanged'; enabled: boolean } @@ -64,6 +65,7 @@ interface ThemeSettings { id: string; name: string; backgroundColor?: string; + activeLineBackground?: string; colors: Record; syntaxTokens: Record; fonts: {