From e0229917f8c69c541c37d8b7a542191454fd9b97 Mon Sep 17 00:00:00 2001 From: Leonid <127580858+bashrusakh@users.noreply.github.com> Date: Sat, 11 Jul 2026 22:45:27 +1100 Subject: [PATCH] feat(settings): editor font size for chat input and code editor (#1325) (#2065) * feat(settings): add editor font size setting for chat input and code editor Adds an 'Editor font size' control in Settings > Appearance that sets an absolute px font size for the chat input textarea and the in-app CodeMirror editor. Mirrors the existing terminalFontSize lifecycle. - New store field editorFontSize (default 13, clamp 9-32, step 1) in useUIStore with narrow selectors at each consumer. - Persistence wired through appearanceAutoSave, desktop + runtime API types, and persistence.ts read/normalize. - Settings UI row (NumberInput) with reset to 13, VisibleSetting union entry, OpenChamberPage registration, and search index entry appearance.editor-font-size. - Applied as a post-zoom absolute override on the chat input textarea and on the CodeMirror theme's content rule, leaving gutter/line-number chrome at its existing hardcoded sizes (matches terminal scope). - All 10 locales translated (en, es, fr, ja, ko, pl, pt-BR, uk, zh-CN, zh-TW); no English placeholders in non-English dictionaries. Refs #1325 * fix(codemirror): use unitless lineHeight so it scales with editor font size The & rule in the CodeMirror theme set lineHeight to 1.5rem (~24px), which does not scale when editorFontSize is increased (e.g., 28-32px). This causes overlapping lines at larger font sizes. Change to unitless 1.5, which scales proportionally with whatever fontSize resolves to (dynamic prop or --text-code fallback). Matches browser best practice for proportional leading. Review comment: https://github.com/openchamber/openchamber/pull/2065 --------- Co-authored-by: bashrusakh Co-authored-by: Bohdan Triapitsyn --- packages/ui/src/components/chat/ChatInput.tsx | 2 ++ .../sections/openchamber/OpenChamberPage.tsx | 1 + .../openchamber/OpenChamberVisualSettings.tsx | 35 +++++++++++++++++-- .../components/sections/skills/SkillsPage.tsx | 11 +++--- .../ui/src/components/views/FilesView.tsx | 10 +++--- packages/ui/src/components/views/PlanView.tsx | 6 ++-- packages/ui/src/lib/api/types.ts | 1 + packages/ui/src/lib/appearanceAutoSave.ts | 6 ++++ .../ui/src/lib/codemirror/flexokiTheme.ts | 8 +++-- packages/ui/src/lib/desktop.ts | 1 + .../ui/src/lib/i18n/messages/en.settings.ts | 2 ++ .../ui/src/lib/i18n/messages/es.settings.ts | 2 ++ .../ui/src/lib/i18n/messages/fr.settings.ts | 2 ++ .../ui/src/lib/i18n/messages/ja.settings.ts | 2 ++ .../ui/src/lib/i18n/messages/ko.settings.ts | 2 ++ .../ui/src/lib/i18n/messages/pl.settings.ts | 2 ++ .../src/lib/i18n/messages/pt-BR.settings.ts | 2 ++ .../ui/src/lib/i18n/messages/uk.settings.ts | 2 ++ .../src/lib/i18n/messages/zh-CN.settings.ts | 2 ++ .../src/lib/i18n/messages/zh-TW.settings.ts | 2 ++ packages/ui/src/lib/persistence.ts | 6 ++++ packages/ui/src/lib/settings/search.ts | 6 ++++ packages/ui/src/stores/useUIStore.ts | 10 ++++++ 23 files changed, 108 insertions(+), 15 deletions(-) diff --git a/packages/ui/src/components/chat/ChatInput.tsx b/packages/ui/src/components/chat/ChatInput.tsx index bae96284..c91780aa 100644 --- a/packages/ui/src/components/chat/ChatInput.tsx +++ b/packages/ui/src/components/chat/ChatInput.tsx @@ -1121,6 +1121,7 @@ const ChatInputComponent: React.FC = ({ onOpenSettings, scrollTo const inputBarOffset = useUIStore((state) => state.inputBarOffset); const persistChatDraft = useUIStore((state) => state.persistChatDraft); const inputSpellcheckEnabled = useUIStore((state) => state.inputSpellcheckEnabled); + const editorFontSize = useUIStore((state) => state.editorFontSize); const isExpandedInput = useUIStore((state) => state.isExpandedInput); const setExpandedInput = useUIStore((state) => state.setExpandedInput); const setTimelineDialogOpen = useUIStore((state) => state.setTimelineDialogOpen); @@ -5256,6 +5257,7 @@ const ChatInputComponent: React.FC = ({ onOpenSettings, scrollTo maxHeight: !isComposerExpanded && textareaSize ? `${textareaSize.maxHeight}px` : undefined, borderTopLeftRadius: chatInputRadius, borderTopRightRadius: chatInputRadius, + fontSize: `${editorFontSize}px`, }} rows={1} /> diff --git a/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx b/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx index 37404f5c..8ae64f84 100644 --- a/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx +++ b/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx @@ -133,6 +133,7 @@ const VisualSectionContent: React.FC = () => { ...(!isVSCode ? ['weekStart' as const] : []), 'fontSize', 'terminalFontSize', + 'editorFontSize', 'fileEditorKeymap', 'spacing', 'inputBarOffset', diff --git a/packages/ui/src/components/sections/openchamber/OpenChamberVisualSettings.tsx b/packages/ui/src/components/sections/openchamber/OpenChamberVisualSettings.tsx index 2b39c81c..025cab13 100644 --- a/packages/ui/src/components/sections/openchamber/OpenChamberVisualSettings.tsx +++ b/packages/ui/src/components/sections/openchamber/OpenChamberVisualSettings.tsx @@ -245,7 +245,7 @@ const normalizeUserMessageRenderingMode = (mode: unknown): 'markdown' | 'plain' return mode === 'markdown' ? 'markdown' : 'plain'; }; -type VisibleSetting = 'sessionAssist' | 'theme' | 'pwaInstallName' | 'pwaOrientation' | 'mobileKeyboardMode' | 'timeFormat' | 'weekStart' | 'fontSize' | 'terminalFontSize' | 'spacing' | 'inputBarOffset' | 'mermaidRendering' | 'userMessageRendering' | 'chatRenderMode' | 'messageTransport' | 'activityRenderMode' | 'collapsibleUserMessages' | 'stickyUserHeader' | 'wideChatLayout' | 'codeBlockLineWrap' | 'splitAssistantMessageActions' | 'diffLayout' | 'mobileStatusBar' | 'dotfiles' | 'fileViewerPreview' | 'reasoning' | 'showToolFileIcons' | 'showTurnChangedFiles' | 'expandedTools' | 'followUpBehavior' | 'terminalQuickKeys' | 'fileEditorKeymap' | 'persistDraft' | 'inputSpellcheck' | 'reportUsage' | 'expandedEditorToolbar'; +type VisibleSetting = 'sessionAssist' | 'theme' | 'pwaInstallName' | 'pwaOrientation' | 'mobileKeyboardMode' | 'timeFormat' | 'weekStart' | 'fontSize' | 'terminalFontSize' | 'editorFontSize' | 'spacing' | 'inputBarOffset' | 'mermaidRendering' | 'userMessageRendering' | 'chatRenderMode' | 'messageTransport' | 'activityRenderMode' | 'collapsibleUserMessages' | 'stickyUserHeader' | 'wideChatLayout' | 'codeBlockLineWrap' | 'splitAssistantMessageActions' | 'diffLayout' | 'mobileStatusBar' | 'dotfiles' | 'fileViewerPreview' | 'reasoning' | 'showToolFileIcons' | 'showTurnChangedFiles' | 'expandedTools' | 'followUpBehavior' | 'terminalQuickKeys' | 'fileEditorKeymap' | 'persistDraft' | 'inputSpellcheck' | 'reportUsage' | 'expandedEditorToolbar'; interface OpenChamberVisualSettingsProps { /** Which settings to show. If undefined, shows all. */ @@ -289,6 +289,8 @@ export const OpenChamberVisualSettings: React.FC const setFontSize = useUIStore(state => state.setFontSize); const terminalFontSize = useUIStore(state => state.terminalFontSize); const setTerminalFontSize = useUIStore(state => state.setTerminalFontSize); + const editorFontSize = useUIStore(state => state.editorFontSize); + const setEditorFontSize = useUIStore(state => state.setEditorFontSize); const uiFont = useUIStore(state => state.uiFont); const setUiFont = useUIStore(state => state.setUiFont); const monoFont = useUIStore(state => state.monoFont); @@ -553,7 +555,7 @@ export const OpenChamberVisualSettings: React.FC const hasAppearanceSettings = isVSCode ? hasLocalizationSettings : (shouldShow('theme') || showMobileLayoutSetting || shouldShow('pwaInstallName') || shouldShow('pwaOrientation') || shouldShow('timeFormat') || shouldShow('weekStart')); - const hasLayoutSettings = shouldShow('fontSize') || shouldShow('terminalFontSize') || shouldShow('spacing') || shouldShow('inputBarOffset'); + const hasLayoutSettings = shouldShow('fontSize') || shouldShow('terminalFontSize') || shouldShow('editorFontSize') || shouldShow('spacing') || shouldShow('inputBarOffset'); const hasNavigationSettings = (shouldShow('terminalQuickKeys') && !isMobile) || shouldShow('fileEditorKeymap') || shouldShow('expandedEditorToolbar'); const hasBehaviorSettings = shouldShow('mermaidRendering') || shouldShow('userMessageRendering') @@ -1253,6 +1255,35 @@ export const OpenChamberVisualSettings: React.FC )} + {shouldShow('editorFontSize') && ( +
+
+ {t('settings.openchamber.visual.field.editorFontSize')} +
+
+ + +
+
+ )} + {shouldShow('spacing') && (
diff --git a/packages/ui/src/components/sections/skills/SkillsPage.tsx b/packages/ui/src/components/sections/skills/SkillsPage.tsx index cb43bdac..681e58a5 100644 --- a/packages/ui/src/components/sections/skills/SkillsPage.tsx +++ b/packages/ui/src/components/sections/skills/SkillsPage.tsx @@ -39,6 +39,7 @@ import { shikiHighlightExtension } from '@/lib/codemirror/shikiHighlight'; import { getResolvedShikiTheme } from '@/lib/shiki/appThemeRegistry'; import { getLanguageFromExtension } from '@/lib/toolHelpers'; import { useThemeSystem } from '@/contexts/useThemeSystem'; +import { useUIStore } from '@/stores/useUIStore'; import { cn } from '@/lib/utils'; import { EditorView } from '@codemirror/view'; import type { Extension } from '@codemirror/state'; @@ -244,22 +245,24 @@ const SkillsInstalledPage: React.FC = () => { loadSkillDetails(); }, [selectedSkill, isNewSkill, selectedSkillName, skills, skillDraft, getSkillDetail]); + const editorFontSize = useUIStore((state) => state.editorFontSize); + const skillEditorExtensions = React.useMemo(() => { - const extensions: Extension[] = [createFlexokiCodeMirrorTheme(currentTheme)]; + const extensions: Extension[] = [createFlexokiCodeMirrorTheme(currentTheme, { fontSize: editorFontSize })]; const markdownExtension = languageByExtension(SKILL_DOCUMENT_PATH); if (markdownExtension) { extensions.push(markdownExtension); } extensions.push(EditorView.lineWrapping); return extensions; - }, [currentTheme]); + }, [currentTheme, editorFontSize]); const supportingFileEditorExtensions = React.useMemo(() => { const filePath = newFileName.trim() || 'supporting-file.md'; // Shiki token colors for code supporting files; markdown stays on lezer. const shikiLanguage = getLanguageFromExtension(filePath); const useShiki = Boolean(shikiLanguage) && shikiLanguage !== 'markdown'; - const extensions: Extension[] = [createFlexokiCodeMirrorTheme(currentTheme, useShiki ? { syntaxColors: false } : undefined)]; + const extensions: Extension[] = [createFlexokiCodeMirrorTheme(currentTheme, useShiki ? { syntaxColors: false, fontSize: editorFontSize } : { fontSize: editorFontSize })]; const languageExtension = languageByExtension(filePath); if (languageExtension) { extensions.push(languageExtension); @@ -273,7 +276,7 @@ const SkillsInstalledPage: React.FC = () => { } extensions.push(EditorView.lineWrapping); return extensions; - }, [currentTheme, newFileName]); + }, [currentTheme, newFileName, editorFontSize]); const handleDescriptionChange = React.useCallback((nextDescription: string) => { setDescription(nextDescription); diff --git a/packages/ui/src/components/views/FilesView.tsx b/packages/ui/src/components/views/FilesView.tsx index d788584d..1b1d0498 100644 --- a/packages/ui/src/components/views/FilesView.tsx +++ b/packages/ui/src/components/views/FilesView.tsx @@ -2885,17 +2885,19 @@ export const FilesView: React.FC = ({ mode = 'full' }) => { return () => window.removeEventListener('keydown', handleKeyDown); }, [canEdit, isMobile, shortcutOverrides, textViewMode]); + const editorFontSize = useUIStore((state) => state.editorFontSize); + const editorExtensions = React.useMemo(() => { if (!selectedFile?.path) { - return [createFlexokiCodeMirrorTheme(currentTheme)]; + return [createFlexokiCodeMirrorTheme(currentTheme, { fontSize: editorFontSize })]; } // Shiki token colors (worker-backed) match the Shiki file view exactly. // Same language resolver as the view, so both agree on the language. When // Shiki is the color source, drop the lezer token colors to avoid a - // competing highlighter (keep the lezer language for indentation/folding). + // competing highlighter (Keep the lezer language for indentation/folding). const shikiLanguage = getLanguageFromExtension(selectedFile.path); - const extensions = [createFlexokiCodeMirrorTheme(currentTheme, shikiLanguage ? { syntaxColors: false } : undefined)]; + const extensions = [createFlexokiCodeMirrorTheme(currentTheme, shikiLanguage ? { syntaxColors: false, fontSize: editorFontSize } : { fontSize: editorFontSize })]; const language = staticLanguageExtension ?? dynamicLanguageExtension; if (language) { extensions.push(language); @@ -2925,7 +2927,7 @@ export const FilesView: React.FC = ({ mode = 'full' }) => { })); } return extensions; - }, [currentTheme, selectedFile?.path, staticLanguageExtension, dynamicLanguageExtension, wrapLines, isMobile, nudgeEditorSelectionAboveKeyboard]); + }, [currentTheme, selectedFile?.path, staticLanguageExtension, dynamicLanguageExtension, wrapLines, isMobile, nudgeEditorSelectionAboveKeyboard, editorFontSize]); const pierreTheme = React.useMemo( () => ({ light: lightTheme.metadata.id, dark: darkTheme.metadata.id }), diff --git a/packages/ui/src/components/views/PlanView.tsx b/packages/ui/src/components/views/PlanView.tsx index 1eb5cb12..045bdaea 100644 --- a/packages/ui/src/components/views/PlanView.tsx +++ b/packages/ui/src/components/views/PlanView.tsx @@ -345,12 +345,14 @@ export const PlanView: React.FC = ({ targetPath = null }) => { }; }, [cancel, commentText, editingDraftId, isMobile, lineSelection]); + const editorFontSize = useUIStore((state) => state.editorFontSize); + const editorExtensions = React.useMemo(() => { // Shiki token colors only for code files; markdown keeps the lezer // highlighter (markdown-aware bold headings etc., and no Shiki view to match). const shikiLanguage = resolvedPath ? getLanguageFromExtension(resolvedPath) : null; const useShiki = Boolean(shikiLanguage) && shikiLanguage !== 'markdown'; - const extensions = [createFlexokiCodeMirrorTheme(currentTheme, useShiki ? { syntaxColors: false } : undefined)]; + const extensions = [createFlexokiCodeMirrorTheme(currentTheme, useShiki ? { syntaxColors: false, fontSize: editorFontSize } : { fontSize: editorFontSize })]; const language = languageByExtension(resolvedPath || 'plan.md'); if (language) { extensions.push(language); @@ -364,7 +366,7 @@ export const PlanView: React.FC = ({ targetPath = null }) => { } extensions.push(EditorView.lineWrapping); return extensions; - }, [currentTheme, resolvedPath]); + }, [currentTheme, resolvedPath, editorFontSize]); React.useEffect(() => { // Saved project plans opened via context panel should work even when session plan mode is off. diff --git a/packages/ui/src/lib/api/types.ts b/packages/ui/src/lib/api/types.ts index 810d849d..4cc10358 100644 --- a/packages/ui/src/lib/api/types.ts +++ b/packages/ui/src/lib/api/types.ts @@ -657,6 +657,7 @@ export interface SettingsPayload { showSplitAssistantMessageActions?: boolean; fontSize?: number; terminalFontSize?: number; + editorFontSize?: number; uiFont?: string; monoFont?: string; padding?: number; diff --git a/packages/ui/src/lib/appearanceAutoSave.ts b/packages/ui/src/lib/appearanceAutoSave.ts index 7ac1b1a2..3a790cb6 100644 --- a/packages/ui/src/lib/appearanceAutoSave.ts +++ b/packages/ui/src/lib/appearanceAutoSave.ts @@ -31,6 +31,7 @@ type AppearanceSlice = { sessionRetentionAction: 'archive' | 'delete'; fontSize: number; terminalFontSize: number; + editorFontSize: number; uiFont: UiFontOption; monoFont: MonoFontOption; padding: number; @@ -72,6 +73,7 @@ export const startAppearanceAutoSave = (): void => { sessionRetentionAction: useUIStore.getState().sessionRetentionAction, fontSize: useUIStore.getState().fontSize, terminalFontSize: useUIStore.getState().terminalFontSize, + editorFontSize: useUIStore.getState().editorFontSize, uiFont: useUIStore.getState().uiFont, monoFont: useUIStore.getState().monoFont, padding: useUIStore.getState().padding, @@ -125,6 +127,7 @@ export const startAppearanceAutoSave = (): void => { sessionRetentionAction: state.sessionRetentionAction, fontSize: state.fontSize, terminalFontSize: state.terminalFontSize, + editorFontSize: state.editorFontSize, uiFont: state.uiFont, monoFont: state.monoFont, padding: state.padding, @@ -200,6 +203,9 @@ export const startAppearanceAutoSave = (): void => { if (current.terminalFontSize !== previous.terminalFontSize) { diff.terminalFontSize = current.terminalFontSize; } + if (current.editorFontSize !== previous.editorFontSize) { + diff.editorFontSize = current.editorFontSize; + } if (current.uiFont !== previous.uiFont) { diff.uiFont = current.uiFont; } diff --git a/packages/ui/src/lib/codemirror/flexokiTheme.ts b/packages/ui/src/lib/codemirror/flexokiTheme.ts index f1acf0c5..092a4f0b 100644 --- a/packages/ui/src/lib/codemirror/flexokiTheme.ts +++ b/packages/ui/src/lib/codemirror/flexokiTheme.ts @@ -11,7 +11,7 @@ export function createFlexokiCodeMirrorTheme( // When syntax colors are provided elsewhere (e.g. the Shiki decoration // extension), pass `{ syntaxColors: false }` to keep only the editor UI theme // (gutters, selection, cursor) and avoid a competing token color source. - options?: { syntaxColors?: boolean }, + options?: { syntaxColors?: boolean; fontSize?: number }, ): Extension { const isDark = theme.metadata.variant === 'dark'; @@ -19,12 +19,14 @@ export function createFlexokiCodeMirrorTheme( const highlights = theme.colors.syntax.highlights || {}; const tokens = theme.colors.syntax.tokens || {}; + const contentFontSize = options?.fontSize ? `${options.fontSize}px` : 'var(--text-code)'; + const ui = EditorView.theme({ '&': { backgroundColor: 'var(--background)', color: theme.colors.syntax.base.foreground, - fontSize: 'var(--text-code)', - lineHeight: '1.5rem', + fontSize: contentFontSize, + lineHeight: '1.5', position: 'relative' as const, }, '.cm-scroller': { diff --git a/packages/ui/src/lib/desktop.ts b/packages/ui/src/lib/desktop.ts index 2d483992..995be684 100644 --- a/packages/ui/src/lib/desktop.ts +++ b/packages/ui/src/lib/desktop.ts @@ -155,6 +155,7 @@ export type DesktopSettings = { showSplitAssistantMessageActions?: boolean; fontSize?: number; terminalFontSize?: number; + editorFontSize?: number; uiFont?: string; monoFont?: string; padding?: number; diff --git a/packages/ui/src/lib/i18n/messages/en.settings.ts b/packages/ui/src/lib/i18n/messages/en.settings.ts index 8733b129..bdda7e09 100644 --- a/packages/ui/src/lib/i18n/messages/en.settings.ts +++ b/packages/ui/src/lib/i18n/messages/en.settings.ts @@ -1731,10 +1731,12 @@ export const settingsDict = { 'settings.openchamber.visual.field.fontSizePercentageAria': 'Font size percentage', 'settings.openchamber.visual.actions.resetFontSizeAria': 'Reset font size', 'settings.openchamber.visual.field.terminalFontSize': 'Terminal Font Size', + 'settings.openchamber.visual.field.editorFontSize': 'Editor Font Size', 'settings.openchamber.visual.field.codeFont': 'Code Font', 'settings.openchamber.visual.field.selectCodeFontAria': 'Select code font', 'settings.openchamber.visual.actions.resetCodeFontAria': 'Reset code font', 'settings.openchamber.visual.actions.resetTerminalFontSizeAria': 'Reset terminal font size', + 'settings.openchamber.visual.actions.resetEditorFontSizeAria': 'Reset editor font size', 'settings.openchamber.visual.field.spacingDensity': 'Spacing Density', 'settings.openchamber.visual.actions.resetSpacingAria': 'Reset spacing', 'settings.openchamber.visual.field.inputBarOffset': 'Input Bar Offset', diff --git a/packages/ui/src/lib/i18n/messages/es.settings.ts b/packages/ui/src/lib/i18n/messages/es.settings.ts index 75a93be9..3e08f054 100644 --- a/packages/ui/src/lib/i18n/messages/es.settings.ts +++ b/packages/ui/src/lib/i18n/messages/es.settings.ts @@ -1698,10 +1698,12 @@ export const settingsDict = { "settings.openchamber.visual.field.fontSizePercentageAria": "Porcentaje de tamaño de fuente", "settings.openchamber.visual.actions.resetFontSizeAria": "Restablecer tamaño de fuente", "settings.openchamber.visual.field.terminalFontSize": "Tamaño de fuente del terminal", + "settings.openchamber.visual.field.editorFontSize": "Tamaño de fuente del editor", "settings.openchamber.visual.field.codeFont": "Fuente de código", "settings.openchamber.visual.field.selectCodeFontAria": "Seleccionar fuente de código", "settings.openchamber.visual.actions.resetCodeFontAria": "Restablecer fuente de código", "settings.openchamber.visual.actions.resetTerminalFontSizeAria": "Restablecer tamaño de fuente del terminal", + "settings.openchamber.visual.actions.resetEditorFontSizeAria": "Restablecer tamaño de fuente del editor", "settings.openchamber.visual.field.spacingDensity": "Densidad de espaciado", "settings.openchamber.visual.actions.resetSpacingAria": "Restablecer espaciado", "settings.openchamber.visual.field.inputBarOffset": "Desplazamiento de la barra de entrada", diff --git a/packages/ui/src/lib/i18n/messages/fr.settings.ts b/packages/ui/src/lib/i18n/messages/fr.settings.ts index b3603c35..41da4d97 100644 --- a/packages/ui/src/lib/i18n/messages/fr.settings.ts +++ b/packages/ui/src/lib/i18n/messages/fr.settings.ts @@ -1613,10 +1613,12 @@ export const settingsDict = { 'settings.openchamber.visual.field.fontSizePercentageAria': 'Pourcentage de taille de police', 'settings.openchamber.visual.actions.resetFontSizeAria': 'Réinitialiser la taille de la police', 'settings.openchamber.visual.field.terminalFontSize': 'Taille de la police du terminal', + 'settings.openchamber.visual.field.editorFontSize': 'Taille de la police de l\'éditeur', 'settings.openchamber.visual.field.codeFont': 'Police de code', 'settings.openchamber.visual.field.selectCodeFontAria': 'Sélectionnez la police du code', 'settings.openchamber.visual.actions.resetCodeFontAria': 'Réinitialiser la police du code', 'settings.openchamber.visual.actions.resetTerminalFontSizeAria': 'Réinitialiser la taille de la police du terminal', + 'settings.openchamber.visual.actions.resetEditorFontSizeAria': 'Réinitialiser la taille de la police de l\'éditeur', 'settings.openchamber.visual.field.spacingDensity': 'Densité d\'espacement', 'settings.openchamber.visual.actions.resetSpacingAria': 'Réinitialiser l\'espacement', 'settings.openchamber.visual.field.inputBarOffset': 'Décalage de la barre d\'entrée', diff --git a/packages/ui/src/lib/i18n/messages/ja.settings.ts b/packages/ui/src/lib/i18n/messages/ja.settings.ts index 40cd2e0e..7ac9f7c6 100644 --- a/packages/ui/src/lib/i18n/messages/ja.settings.ts +++ b/packages/ui/src/lib/i18n/messages/ja.settings.ts @@ -1731,10 +1731,12 @@ export const settingsDict = { 'settings.openchamber.visual.field.fontSizePercentageAria': 'フォントサイズの割合', 'settings.openchamber.visual.actions.resetFontSizeAria': 'フォントサイズをリセット', 'settings.openchamber.visual.field.terminalFontSize': 'ターミナルフォントサイズ', + 'settings.openchamber.visual.field.editorFontSize': 'エディターフォントサイズ', 'settings.openchamber.visual.field.codeFont': 'コードフォント', 'settings.openchamber.visual.field.selectCodeFontAria': 'コードフォントを選択', 'settings.openchamber.visual.actions.resetCodeFontAria': 'コードフォントをリセット', 'settings.openchamber.visual.actions.resetTerminalFontSizeAria': 'ターミナルフォントサイズをリセット', + 'settings.openchamber.visual.actions.resetEditorFontSizeAria': 'エディターフォントサイズをリセット', 'settings.openchamber.visual.field.spacingDensity': '間隔の密度', 'settings.openchamber.visual.actions.resetSpacingAria': '間隔をリセット', 'settings.openchamber.visual.field.inputBarOffset': '入力バーオフセット', diff --git a/packages/ui/src/lib/i18n/messages/ko.settings.ts b/packages/ui/src/lib/i18n/messages/ko.settings.ts index 9aceeaf7..f5b5472c 100644 --- a/packages/ui/src/lib/i18n/messages/ko.settings.ts +++ b/packages/ui/src/lib/i18n/messages/ko.settings.ts @@ -1698,10 +1698,12 @@ export const settingsDict = { 'settings.openchamber.visual.field.fontSizePercentageAria': '폰트 크기 비율', 'settings.openchamber.visual.actions.resetFontSizeAria': '폰트 크기 초기화', 'settings.openchamber.visual.field.terminalFontSize': '터미널 폰트 크기', + 'settings.openchamber.visual.field.editorFontSize': '에디터 폰트 크기', 'settings.openchamber.visual.field.codeFont': '코드 폰트', 'settings.openchamber.visual.field.selectCodeFontAria': '코드 폰트 선택', 'settings.openchamber.visual.actions.resetCodeFontAria': '코드 폰트 초기화', 'settings.openchamber.visual.actions.resetTerminalFontSizeAria': '터미널 폰트 크기 초기화', + 'settings.openchamber.visual.actions.resetEditorFontSizeAria': '에디터 폰트 크기 초기화', 'settings.openchamber.visual.field.spacingDensity': '간격 밀도', 'settings.openchamber.visual.actions.resetSpacingAria': '간격 초기화', 'settings.openchamber.visual.field.inputBarOffset': '입력 바 오프셋', diff --git a/packages/ui/src/lib/i18n/messages/pl.settings.ts b/packages/ui/src/lib/i18n/messages/pl.settings.ts index acdecd69..43ed887e 100644 --- a/packages/ui/src/lib/i18n/messages/pl.settings.ts +++ b/packages/ui/src/lib/i18n/messages/pl.settings.ts @@ -935,6 +935,7 @@ export const settingsDict = { 'settings.openchamber.visual.actions.resetInterfaceFontAria': 'Zresetuj czcionkę interfejsu', 'settings.openchamber.visual.actions.resetSpacingAria': 'Zresetuj odstępy', 'settings.openchamber.visual.actions.resetTerminalFontSizeAria': 'Zresetuj rozmiar czcionki terminala', + 'settings.openchamber.visual.actions.resetEditorFontSizeAria': 'Zresetuj rozmiar czcionki edytora', 'settings.openchamber.visual.field.activityDefaultModeAria': 'Domyślny tryb aktywności: {option}', 'settings.openchamber.visual.field.bash': 'Bash', 'settings.openchamber.visual.field.codeFont': 'Czcionka kodu', @@ -1004,6 +1005,7 @@ export const settingsDict = { 'settings.openchamber.visual.field.expandedEditorToolbarAria': 'Always show editor toolbar', 'settings.openchamber.visual.field.expandedEditorToolbar': 'Always show editor toolbar (docked under the file tabs)', 'settings.openchamber.visual.field.terminalFontSize': 'Rozmiar czcionki terminala', + 'settings.openchamber.visual.field.editorFontSize': 'Rozmiar czcionki edytora', 'settings.openchamber.visual.field.terminalQuickKeys': 'Szybkie klawisze terminala', 'settings.openchamber.visual.field.terminalQuickKeysAria': 'Szybkie klawisze terminala', 'settings.openchamber.visual.field.terminalQuickKeysTooltip': 'Pokaż Esc, Ctrl i strzałki w widoku terminala', diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts b/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts index ea273353..f408211d 100644 --- a/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts +++ b/packages/ui/src/lib/i18n/messages/pt-BR.settings.ts @@ -1698,10 +1698,12 @@ export const settingsDict = { "settings.openchamber.visual.field.fontSizePercentageAria": "Porcentagem do tamanho base", "settings.openchamber.visual.actions.resetFontSizeAria": "Redefinir tamanho base", "settings.openchamber.visual.field.terminalFontSize": "Tamanho base do terminal", + "settings.openchamber.visual.field.editorFontSize": "Tamanho base do editor", "settings.openchamber.visual.field.codeFont": "Fonte do código", "settings.openchamber.visual.field.selectCodeFontAria": "Selecionar fonte do código", "settings.openchamber.visual.actions.resetCodeFontAria": "Redefinir fonte do código", "settings.openchamber.visual.actions.resetTerminalFontSizeAria": "Redefinir tamanho base do terminal", + "settings.openchamber.visual.actions.resetEditorFontSizeAria": "Redefinir tamanho base do editor", "settings.openchamber.visual.field.spacingDensity": "Densidade de espaçamento", "settings.openchamber.visual.actions.resetSpacingAria": "Redefinir espaçamento", "settings.openchamber.visual.field.inputBarOffset": "Deslocamento da barra de entrada", diff --git a/packages/ui/src/lib/i18n/messages/uk.settings.ts b/packages/ui/src/lib/i18n/messages/uk.settings.ts index 8bca1b7d..fc54aa55 100644 --- a/packages/ui/src/lib/i18n/messages/uk.settings.ts +++ b/packages/ui/src/lib/i18n/messages/uk.settings.ts @@ -1698,10 +1698,12 @@ export const settingsDict = { "settings.openchamber.visual.field.fontSizePercentageAria": "Розмір шрифту у відсотках", "settings.openchamber.visual.actions.resetFontSizeAria": "Скинути розмір шрифту", "settings.openchamber.visual.field.terminalFontSize": "Розмір шрифту терміналу", + "settings.openchamber.visual.field.editorFontSize": "Розмір шрифту редактора", "settings.openchamber.visual.field.codeFont": "Шрифт коду", "settings.openchamber.visual.field.selectCodeFontAria": "Вибрати шрифт коду", "settings.openchamber.visual.actions.resetCodeFontAria": "Скинути шрифт коду", "settings.openchamber.visual.actions.resetTerminalFontSizeAria": "Скинути розмір шрифту терміналу", + "settings.openchamber.visual.actions.resetEditorFontSizeAria": "Скинути розмір шрифту редактора", "settings.openchamber.visual.field.spacingDensity": "Щільність інтервалу", "settings.openchamber.visual.actions.resetSpacingAria": "Скинути інтервал", "settings.openchamber.visual.field.inputBarOffset": "Зміщення панелі вводу", diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts b/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts index 28ba6c53..d0e8059e 100644 --- a/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts +++ b/packages/ui/src/lib/i18n/messages/zh-CN.settings.ts @@ -1698,10 +1698,12 @@ export const settingsDict = { 'settings.openchamber.visual.field.fontSizePercentageAria': '字体大小百分比', 'settings.openchamber.visual.actions.resetFontSizeAria': '重置字体大小', 'settings.openchamber.visual.field.terminalFontSize': '终端字体大小', + 'settings.openchamber.visual.field.editorFontSize': '编辑器字体大小', 'settings.openchamber.visual.field.codeFont': '代码字体', 'settings.openchamber.visual.field.selectCodeFontAria': '选择代码字体', 'settings.openchamber.visual.actions.resetCodeFontAria': '重置代码字体', 'settings.openchamber.visual.actions.resetTerminalFontSizeAria': '重置终端字体大小', + 'settings.openchamber.visual.actions.resetEditorFontSizeAria': '重置编辑器字体大小', 'settings.openchamber.visual.field.spacingDensity': '间距密度', 'settings.openchamber.visual.actions.resetSpacingAria': '重置间距', 'settings.openchamber.visual.field.inputBarOffset': '输入栏偏移', diff --git a/packages/ui/src/lib/i18n/messages/zh-TW.settings.ts b/packages/ui/src/lib/i18n/messages/zh-TW.settings.ts index 999e545d..e519f5e2 100644 --- a/packages/ui/src/lib/i18n/messages/zh-TW.settings.ts +++ b/packages/ui/src/lib/i18n/messages/zh-TW.settings.ts @@ -1609,10 +1609,12 @@ 'settings.openchamber.visual.field.fontSizePercentageAria': '字體大小百分比', 'settings.openchamber.visual.actions.resetFontSizeAria': '重設字體大小', 'settings.openchamber.visual.field.terminalFontSize': '終端機字體大小', + 'settings.openchamber.visual.field.editorFontSize': '編輯器字體大小', 'settings.openchamber.visual.field.codeFont': '程式碼字體', 'settings.openchamber.visual.field.selectCodeFontAria': '選擇程式碼字體', 'settings.openchamber.visual.actions.resetCodeFontAria': '重設程式碼字體', 'settings.openchamber.visual.actions.resetTerminalFontSizeAria': '重設終端機字體大小', + 'settings.openchamber.visual.actions.resetEditorFontSizeAria': '重設編輯器字體大小', 'settings.openchamber.visual.field.spacingDensity': '間距密度', 'settings.openchamber.visual.actions.resetSpacingAria': '重設間距', 'settings.openchamber.visual.field.inputBarOffset': '輸入列偏移', diff --git a/packages/ui/src/lib/persistence.ts b/packages/ui/src/lib/persistence.ts index 02afee54..27a399b7 100644 --- a/packages/ui/src/lib/persistence.ts +++ b/packages/ui/src/lib/persistence.ts @@ -594,6 +594,9 @@ const applyDesktopUiPreferences = (settings: DesktopSettings) => { if (typeof settings.terminalFontSize === 'number' && Number.isFinite(settings.terminalFontSize) && settings.terminalFontSize !== store.terminalFontSize) { store.setTerminalFontSize(settings.terminalFontSize); } + if (typeof settings.editorFontSize === 'number' && Number.isFinite(settings.editorFontSize) && settings.editorFontSize !== store.editorFontSize) { + store.setEditorFontSize(settings.editorFontSize); + } if (isUiFontOption(settings.uiFont) && settings.uiFont !== store.uiFont) { store.setUiFont(settings.uiFont); } @@ -1111,6 +1114,9 @@ const sanitizeWebSettings = (payload: unknown): DesktopSettings | null => { if (typeof candidate.terminalFontSize === 'number' && Number.isFinite(candidate.terminalFontSize)) { result.terminalFontSize = candidate.terminalFontSize; } + if (typeof candidate.editorFontSize === 'number' && Number.isFinite(candidate.editorFontSize)) { + result.editorFontSize = candidate.editorFontSize; + } if (isUiFontOption(candidate.uiFont)) { result.uiFont = candidate.uiFont; } diff --git a/packages/ui/src/lib/settings/search.ts b/packages/ui/src/lib/settings/search.ts index f25486ce..3d61b1a4 100644 --- a/packages/ui/src/lib/settings/search.ts +++ b/packages/ui/src/lib/settings/search.ts @@ -116,6 +116,12 @@ const SETTINGS_SEARCH_ITEMS: readonly SettingsSearchItem[] = [ titleKey: 'settings.openchamber.visual.field.terminalFontSize', keywords: ['terminal', 'font', 'text size'], }, + { + id: 'appearance.editor-font-size', + page: 'appearance', + titleKey: 'settings.openchamber.visual.field.editorFontSize', + keywords: ['editor', 'font', 'text size', 'code'], + }, { id: 'appearance.spacing-density', page: 'appearance', diff --git a/packages/ui/src/stores/useUIStore.ts b/packages/ui/src/stores/useUIStore.ts index 8bba2be3..47944e83 100644 --- a/packages/ui/src/stores/useUIStore.ts +++ b/packages/ui/src/stores/useUIStore.ts @@ -587,6 +587,7 @@ interface UIStore { // Global draft welcome starters; null = unset (use the default built-in set). globalDraftStarters: DraftStarterRef[] | null; terminalFontSize: number; + editorFontSize: number; uiFont: UiFontOption; monoFont: MonoFontOption; padding: number; @@ -736,6 +737,7 @@ interface UIStore { setFontSize: (size: number) => void; setGlobalDraftStarters: (refs: DraftStarterRef[]) => void; setTerminalFontSize: (size: number) => void; + setEditorFontSize: (size: number) => void; setUiFont: (font: UiFontOption) => void; setMonoFont: (font: MonoFontOption) => void; setPadding: (size: number) => void; @@ -883,6 +885,7 @@ export const useUIStore = create()( fontSize: 100, globalDraftStarters: null, terminalFontSize: 13, + editorFontSize: 13, uiFont: DEFAULT_UI_FONT, monoFont: DEFAULT_MONO_FONT, padding: 100, @@ -1633,6 +1636,12 @@ export const useUIStore = create()( set({ terminalFontSize: clamped }); }, + setEditorFontSize: (size) => { + const rounded = Math.round(size); + const clamped = Math.max(9, Math.min(32, rounded)); + set({ editorFontSize: clamped }); + }, + setUiFont: (font) => { set({ uiFont: font }); }, @@ -2279,6 +2288,7 @@ export const useUIStore = create()( fontSize: state.fontSize, globalDraftStarters: state.globalDraftStarters, terminalFontSize: state.terminalFontSize, + editorFontSize: state.editorFontSize, uiFont: state.uiFont, monoFont: state.monoFont, padding: state.padding,