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;