From 0ea921032ec50f2dda283d9e5f20980a8dc9390b Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Wed, 3 Jun 2026 18:03:00 +0300 Subject: [PATCH] fix: move file preview setting to chat settings Moved file preview mode toggle next to Show Dotfiles Saved checkbox changes through settings config Applied preview mode changes to open editor tabs immediately --- .../sections/openchamber/DefaultsSettings.tsx | 25 ----------- .../sections/openchamber/OpenChamberPage.tsx | 2 +- .../openchamber/OpenChamberVisualSettings.tsx | 38 ++++++++++++++++- .../ui/src/components/views/FilesView.tsx | 42 +++++++++++++++++++ 4 files changed, 79 insertions(+), 28 deletions(-) diff --git a/packages/ui/src/components/sections/openchamber/DefaultsSettings.tsx b/packages/ui/src/components/sections/openchamber/DefaultsSettings.tsx index 1d60f005..92334d62 100644 --- a/packages/ui/src/components/sections/openchamber/DefaultsSettings.tsx +++ b/packages/ui/src/components/sections/openchamber/DefaultsSettings.tsx @@ -32,8 +32,6 @@ export const DefaultsSettings: React.FC = () => { const setSettingsDefaultModel = useConfigStore((state) => state.setSettingsDefaultModel); const setSettingsDefaultVariant = useConfigStore((state) => state.setSettingsDefaultVariant); const setSettingsDefaultAgent = useConfigStore((state) => state.setSettingsDefaultAgent); - const setSettingsDefaultFileViewerPreview = useConfigStore((state) => state.setSettingsDefaultFileViewerPreview); - const settingsDefaultFileViewerPreview = useConfigStore((state) => state.settingsDefaultFileViewerPreview); const showDeletionDialog = useUIStore((state) => state.showDeletionDialog); const setShowDeletionDialog = useUIStore((state) => state.setShowDeletionDialog); const providers = useConfigStore((state) => state.providers); @@ -191,12 +189,6 @@ export const DefaultsSettings: React.FC = () => { [setAgent, setSettingsDefaultAgent] ); - const handleToggleFileViewerPreview = React.useCallback(() => { - const next = !settingsDefaultFileViewerPreview; - setSettingsDefaultFileViewerPreview(next); - updateDesktopSettings({ defaultFileViewerPreview: next }).catch(console.warn); - }, [settingsDefaultFileViewerPreview, setSettingsDefaultFileViewerPreview]); - const availableVariants = React.useMemo(() => { if (!parsedModel.providerId || !parsedModel.modelId) return []; const provider = providers.find((p) => p.id === parsedModel.providerId); @@ -311,23 +303,6 @@ export const DefaultsSettings: React.FC = () => { {t('settings.openchamber.defaults.field.showDeletionDialog')} -
{ - if (event.key === ' ' || event.key === 'Enter') { - event.preventDefault(); - handleToggleFileViewerPreview(); - } - }} - > - - {t('settings.openchamber.defaults.field.openFilesPreview')} -
- ); diff --git a/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx b/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx index f1a64b0f..7e998f5f 100644 --- a/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx +++ b/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx @@ -142,7 +142,7 @@ const VisualSectionContent: React.FC = () => { // Chat section: User message rendering, Diff layout, Mobile status bar, Show reasoning traces, Queue mode, Persist draft const ChatSectionContent: React.FC = () => { - return ; + return ; }; // Sessions section: Default model & agent, Session retention diff --git a/packages/ui/src/components/sections/openchamber/OpenChamberVisualSettings.tsx b/packages/ui/src/components/sections/openchamber/OpenChamberVisualSettings.tsx index f9df4f25..07777071 100644 --- a/packages/ui/src/components/sections/openchamber/OpenChamberVisualSettings.tsx +++ b/packages/ui/src/components/sections/openchamber/OpenChamberVisualSettings.tsx @@ -247,7 +247,7 @@ const normalizeUserMessageRenderingMode = (mode: unknown): 'markdown' | 'plain' return mode === 'markdown' ? 'markdown' : 'plain'; }; -export type VisibleSetting = 'theme' | 'pwaInstallName' | 'pwaOrientation' | 'mobileKeyboardMode' | 'timeFormat' | 'weekStart' | 'fontSize' | 'terminalFontSize' | 'spacing' | 'inputBarOffset' | 'mermaidRendering' | 'userMessageRendering' | 'chatRenderMode' | 'messageTransport' | 'activityRenderMode' | 'stickyUserHeader' | 'wideChatLayout' | 'splitAssistantMessageActions' | 'diffLayout' | 'mobileStatusBar' | 'dotfiles' | 'reasoning' | 'showToolFileIcons' | 'expandedTools' | 'queueMode' | 'terminalQuickKeys' | 'persistDraft' | 'inputSpellcheck' | 'reportUsage'; +export type VisibleSetting = 'theme' | 'pwaInstallName' | 'pwaOrientation' | 'mobileKeyboardMode' | 'timeFormat' | 'weekStart' | 'fontSize' | 'terminalFontSize' | 'spacing' | 'inputBarOffset' | 'mermaidRendering' | 'userMessageRendering' | 'chatRenderMode' | 'messageTransport' | 'activityRenderMode' | 'stickyUserHeader' | 'wideChatLayout' | 'splitAssistantMessageActions' | 'diffLayout' | 'mobileStatusBar' | 'dotfiles' | 'fileViewerPreview' | 'reasoning' | 'showToolFileIcons' | 'expandedTools' | 'queueMode' | 'terminalQuickKeys' | 'persistDraft' | 'inputSpellcheck' | 'reportUsage'; interface OpenChamberVisualSettingsProps { /** Which settings to show. If undefined, shows all. */ @@ -319,6 +319,8 @@ export const OpenChamberVisualSettings: React.FC const setShowMobileSessionStatusBar = useUIStore(state => state.setShowMobileSessionStatusBar); const messageStreamTransport = useConfigStore((state) => state.settingsMessageStreamTransport); const setMessageStreamTransport = useConfigStore((state) => state.setSettingsMessageStreamTransport); + const settingsDefaultFileViewerPreview = useConfigStore((state) => state.settingsDefaultFileViewerPreview); + const setSettingsDefaultFileViewerPreview = useConfigStore((state) => state.setSettingsDefaultFileViewerPreview); const isSettingsDialogOpen = useUIStore(state => state.isSettingsDialogOpen); const { themeMode, @@ -439,6 +441,12 @@ export const OpenChamberVisualSettings: React.FC void updateDesktopSettings({ showToolFileIcons: enabled }); }, [setShowToolFileIcons]); + const handleFileViewerPreviewChange = React.useCallback((enabled: boolean) => { + setSettingsDefaultFileViewerPreview(enabled); + void updateDesktopSettings({ defaultFileViewerPreview: enabled }); + window.dispatchEvent(new CustomEvent('openchamber:file-viewer-preview-mode-changed', { detail: { enabled } })); + }, [setSettingsDefaultFileViewerPreview]); + const handleShowExpandedBashToolsChange = React.useCallback((enabled: boolean) => { setShowExpandedBashTools(enabled); void updateDesktopSettings({ showExpandedBashTools: enabled }); @@ -513,6 +521,7 @@ export const OpenChamberVisualSettings: React.FC || shouldShow('diffLayout') || (shouldShow('mobileStatusBar') && isMobile) || shouldShow('dotfiles') + || shouldShow('fileViewerPreview') || shouldShow('reasoning') || shouldShow('queueMode') || shouldShow('persistDraft') @@ -1578,7 +1587,7 @@ export const OpenChamberVisualSettings: React.FC )} - {(shouldShow('stickyUserHeader') || shouldShow('wideChatLayout') || shouldShow('splitAssistantMessageActions') || (shouldShow('mobileStatusBar') && isMobile) || shouldShow('dotfiles') || shouldShow('queueMode') || shouldShow('persistDraft') || shouldShow('showToolFileIcons') || (!isMobile && shouldShow('inputSpellcheck')) || shouldShow('reasoning')) && ( + {(shouldShow('stickyUserHeader') || shouldShow('wideChatLayout') || shouldShow('splitAssistantMessageActions') || (shouldShow('mobileStatusBar') && isMobile) || shouldShow('dotfiles') || shouldShow('fileViewerPreview') || shouldShow('queueMode') || shouldShow('persistDraft') || shouldShow('showToolFileIcons') || (!isMobile && shouldShow('inputSpellcheck')) || shouldShow('reasoning')) && (
{shouldShow('reasoning') && (
)} + {shouldShow('fileViewerPreview') && ( +
handleFileViewerPreviewChange(!settingsDefaultFileViewerPreview)} + onKeyDown={(event) => { + if (event.key === ' ' || event.key === 'Enter') { + event.preventDefault(); + handleFileViewerPreviewChange(!settingsDefaultFileViewerPreview); + } + }} + > + event.stopPropagation()}> + + + {t('settings.openchamber.defaults.field.openFilesPreview')} +
+ )} + {shouldShow('queueMode') && (
= ({ mode = 'full' }) => { const getHtmlViewMode = React.useCallback((): PreviewViewMode => { return htmlViewMode; }, [htmlViewMode]); + + React.useEffect(() => { + const applyDefaultFileViewerMode = (enabled: boolean) => { + const textMode: TextViewMode = enabled ? 'view' : 'edit'; + const previewMode: PreviewViewMode = enabled ? 'preview' : 'edit'; + const nextJsonMode: 'tree' | 'text' = enabled ? 'tree' : 'text'; + + for (const path of openPaths) { + textViewModeByPathRef.current[path] = textMode; + if (isMarkdownFile(path)) { + mdViewModeByPathRef.current[path] = previewMode; + } + if (isHtmlFile(path)) { + htmlViewModeByPathRef.current[path] = previewMode; + } + } + + setTextViewMode(textMode); + setMdViewMode(previewMode); + setHtmlViewMode(previewMode); + setJsonViewMode(nextJsonMode); + + try { + localStorage.setItem(MD_VIEWER_MODE_KEY, previewMode); + localStorage.setItem(HTML_VIEWER_MODE_KEY, previewMode); + localStorage.setItem(JSON_VIEWER_MODE_KEY, nextJsonMode); + } catch { + // Ignore localStorage errors + } + }; + + const handleFileViewerModeChanged = (event: Event) => { + const enabled = Boolean((event as CustomEvent<{ enabled?: boolean }>).detail?.enabled); + applyDefaultFileViewerMode(enabled); + }; + + window.addEventListener('openchamber:file-viewer-preview-mode-changed', handleFileViewerModeChanged); + return () => { + window.removeEventListener('openchamber:file-viewer-preview-mode-changed', handleFileViewerModeChanged); + }; + }, [openPaths]); + React.useEffect(() => { if (!pendingFileNavigation || !root) { return;