feat: show changed files after completed turns

Add changed-file pills with per-file diff stats
Add a chat setting to disable the feature fully
Avoid changed-file projection work when disabled
This commit is contained in:
Bohdan Triapitsyn
2026-06-03 22:11:37 +03:00
parent 9087950f1a
commit 04b1425c2e
22 changed files with 218 additions and 41 deletions
@@ -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 <OpenChamberVisualSettings visibleSettings={['chatRenderMode', 'messageTransport', 'activityRenderMode', 'userMessageRendering', 'mermaidRendering', 'reasoning', 'showToolFileIcons', 'expandedTools', 'stickyUserHeader', 'wideChatLayout', 'splitAssistantMessageActions', 'diffLayout', 'mobileStatusBar', 'dotfiles', 'fileViewerPreview', 'queueMode', 'persistDraft', 'inputSpellcheck']} />;
return <OpenChamberVisualSettings visibleSettings={['chatRenderMode', 'messageTransport', 'activityRenderMode', 'userMessageRendering', 'mermaidRendering', 'reasoning', 'showToolFileIcons', 'showTurnChangedFiles', 'expandedTools', 'stickyUserHeader', 'wideChatLayout', 'splitAssistantMessageActions', 'diffLayout', 'mobileStatusBar', 'dotfiles', 'fileViewerPreview', 'queueMode', 'persistDraft', 'inputSpellcheck']} />;
};
// Sessions section: Default model & agent, Session retention
@@ -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' | 'fileViewerPreview' | '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' | 'showTurnChangedFiles' | 'expandedTools' | 'queueMode' | 'terminalQuickKeys' | 'persistDraft' | 'inputSpellcheck' | 'reportUsage';
interface OpenChamberVisualSettingsProps {
/** Which settings to show. If undefined, shows all. */
@@ -305,6 +305,8 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
const setInputSpellcheckEnabled = useUIStore(state => state.setInputSpellcheckEnabled);
const showToolFileIcons = useUIStore(state => state.showToolFileIcons);
const setShowToolFileIcons = useUIStore(state => state.setShowToolFileIcons);
const showTurnChangedFiles = useUIStore(state => state.showTurnChangedFiles);
const setShowTurnChangedFiles = useUIStore(state => state.setShowTurnChangedFiles);
const showExpandedBashTools = useUIStore(state => state.showExpandedBashTools);
const setShowExpandedBashTools = useUIStore(state => state.setShowExpandedBashTools);
const showExpandedEditTools = useUIStore(state => state.showExpandedEditTools);
@@ -441,6 +443,11 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
void updateDesktopSettings({ showToolFileIcons: enabled });
}, [setShowToolFileIcons]);
const handleShowTurnChangedFilesChange = React.useCallback((enabled: boolean) => {
setShowTurnChangedFiles(enabled);
void updateDesktopSettings({ showTurnChangedFiles: enabled });
}, [setShowTurnChangedFiles]);
const handleFileViewerPreviewChange = React.useCallback((enabled: boolean) => {
setSettingsDefaultFileViewerPreview(enabled);
void updateDesktopSettings({ defaultFileViewerPreview: enabled });
@@ -1587,7 +1594,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
</div>
)}
{(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('stickyUserHeader') || shouldShow('wideChatLayout') || shouldShow('splitAssistantMessageActions') || (shouldShow('mobileStatusBar') && isMobile) || shouldShow('dotfiles') || shouldShow('fileViewerPreview') || shouldShow('queueMode') || shouldShow('persistDraft') || shouldShow('showToolFileIcons') || shouldShow('showTurnChangedFiles') || (!isMobile && shouldShow('inputSpellcheck')) || shouldShow('reasoning')) && (
<section className="p-2 space-y-0.5">
{shouldShow('reasoning') && (
<div
@@ -1737,6 +1744,29 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
</div>
)}
{shouldShow('showTurnChangedFiles') && (
<div
className="group flex cursor-pointer items-center gap-2 py-0.5"
role="button"
tabIndex={0}
aria-pressed={showTurnChangedFiles}
onClick={() => handleShowTurnChangedFilesChange(!showTurnChangedFiles)}
onKeyDown={(event) => {
if (event.key === ' ' || event.key === 'Enter') {
event.preventDefault();
handleShowTurnChangedFilesChange(!showTurnChangedFiles);
}
}}
>
<Checkbox
checked={showTurnChangedFiles}
onChange={handleShowTurnChangedFilesChange}
ariaLabel={t('settings.openchamber.visual.field.showTurnChangedFilesAria')}
/>
<span className="typography-ui-label text-foreground">{t('settings.openchamber.visual.field.showTurnChangedFiles')}</span>
</div>
)}
{shouldShow('mobileStatusBar') && isMobile && (
<div
className="group flex cursor-pointer items-center gap-2 py-0.5"