refactor(chat): complete turn-based pipeline and stabilize streaming, scroll, and tool UX (#629)
* refactor: add canonical turn projection pipeline for chat Centralizes turn/activity/summary derivation, migrates render consumers to shared projection sources, and adds projector regression coverage for retries, malformed parent fallbacks, and activity segmentation. * refactor: stream assistant text live in turn-first render path * refactor: complete phase-3 turn timeline history/navigation * refactor: complete phase-4 turn pipeline cutover Remove legacy grouping adapters and lock key edge cases with executable regression tests so turn-centric rendering stays stable across retry/history/navigation flows. * fix: align chat history loading path with opencode parity * fix: stabilize chat streaming and reduce render churn * feat: add blurIn streaming text animation and smooth lerp-based auto-scroll Streaming markdown now uses Streamdown blurIn animation (150ms, ease-out) for per-word reveal Auto-scroll during streaming uses continuous exponential smoothing (lerp) instead of restarting spring animations One-shot scroll-to-bottom (button click) uses motion spring for natural deceleration * fix: respect user scroll-up during streaming and re-pin on scroll back to bottom * fix: stabilize activity stream and tool progress rendering Activity now stays chronologically consistent and avoids duplicate reasoning/justification. Tool rows show running state and duration earlier with smoother pending behavior. Removed legacy trim/timestamp paths to prevent message drops and simplify Activity UI. * fix: reverted regression of revert functionality * fix: stabilize chat activity layout and tool progress behavior Stopped user messages from re-animating when new user messages appear. Made Activity and aggregated tool rows wrap inline consistently without misaligned headers/icons. Fixed tool part matching so duration and shine no longer reset across pending/in-progress status updates. * fix: restore activity mode behavior and stabilize tool rendering Re-enabled Collapsed/Summary/Detailed/Changes defaults for Activity and tool expansion. Fixed tool row wrapping/alignment, removed user fade re-animations, and disabled FadeInOnReveal animations. Improved tool status handling to reduce timer/shine resets and made reasoning bold render as normal text. * refactor: simplify chat tool and message rendering Replace activity-group rendering with flat inline part rendering Remove obsolete tool output and justification activity settings Keep user message animation and improve edit/apply-patch file path headers * style: narrow chat and composer columns equally * fix: Tool status checks and abort flow regression * fix: improve multi-file apply patch header rendering * fix: make timeline message jump scroll to selected chat entry Conversation Timeline clicks now reliably scroll to the selected message in chat Timeline dialog waits for navigation result before closing to avoid silent failures Chat message scrolling now resolves the active chat scroller consistently * chore: remove branch-added chat tests and clean lint leftovers Removes newly added chat and message test files from this branch Fixes lint blockers in ProgressiveGroup, ToolPart, and messageStore Keeps type-check, lint, and build passing after cleanup * fix: keep composer status row closest to the input Status/todo row now renders below queued messages and attachments. Linked GitHub issue/PR chips stay above status so input context order is consistent. Improves chat composer readability while assistant is working. * fix: disable assistant header animations and polish chat status behavior Assistant message header now renders without transition effects Removed a lint issue from the working placeholder props Chat status and message flow updates improve consistency during active turns * feat: increase bottom spacer in mobile chat for better scrolling * feat: add sorted chat mode with progressive activity rendering Adds global chat render mode settings for sorted and live behavior Renders non-stop assistant output in Activity as messages complete Disables streaming text animation in sorted mode and restores classic reasoning/justification blocks * fix: stabilize chat timeline navigation and older history loading Prevents auto-loading older history from scroll and keeps loading behind the button Fixes timeline jump-to-message behavior that snapped back to the bottom Reduces chat list flicker and empty-state glitches on large session pagination * fix: stabilize sorted activity text and hide reasoning timers Prevented sorted justification from appearing before message completion by requiring explicit non-stop finish. Removed duration values for sorted Thinking and Justification blocks. Kept smooth sorted auto-follow behavior tuned to reduce bottom-jump jitter. * refactor: simplify chat tool rendering paths and add parts docs Centralized tool icon mapping into a shared presentation helper used by grouped and expandable rows. Removed static-tool-specific branches from ToolPart to keep it focused on expandable tools. Added a new DOCUMENTATION.md explaining where to change tool descriptions and rendering behavior. * feat: enhance event stream handling for message parts - Refactor event key building functions to improve clarity and reduce redundancy. - Introduce `applyPartDelta` method in message store for handling delta updates to message parts. - Update `useEventStream` to utilize the new `applyPartDelta` method for processing incoming deltas. - Enhance text extraction logic to accommodate new part types and improve merging behavior. - Implement logic to skip delta-driven updates for ongoing text/reasoning parts to prevent overwriting. - Adjust server-side text merging logic to handle trimmed text comparisons and improve consistency. * feat: enhance OpencodeService with delta handling and stale delta management - Introduced globalSseStaleDeltas to track stale deltas. - Added methods to generate keys for delta and updated part events. - Updated global SSE loop to skip processing stale deltas. - Refactored global SSE event handling to improve performance and reliability. refactor: remove unused streaming part handling in messageStore - Eliminated streaming part queue and associated logic. - Simplified message merging logic by removing redundant checks. - Streamlined message handling to improve clarity and maintainability. chore: clean up unused streaming text normalization functions - Removed deprecated functions related to streaming text identity and merging. - Simplified normalizeStreamingTextPayload to focus on current functionality. * refactor: simplify streaming event passthrough Use SDK-native global event streaming in the UI client Remove dead server-side SSE normalization helpers Keep streaming payloads closer to upstream behavior * revert: remove message memory settings UI Drop the restored message memory settings screen Keep session settings focused on current defaults Preserve chat history limits as internal behavior * feat: add compact collapsed Activity preview in sorted mode Collapsed Activity now shows a live preview of the latest 7 entries Older activity rows roll up behind a +N more indicator The +N more indicator is clickable to expand the full Activity * fix: adjusted activity changed files text prominence * fix: make sorted mode snap to bottom when not streaming * perf: show chat scrollbar only on user-driven scrolling Chat scrollbar now stays hidden during automatic follow-to-bottom Scrollbar visibility is gated by real user scroll intent Reduced scroll overhead by removing timer-based intent tracking * refactor: align web and vscode session activity streaming Route VS Code SSE through SDK-based proxy flow Restore controlled web session activity tracking and cooldowns Keep chat activity behavior consistent across runtimes * fix: adjusted muted foreground themes color * fix: stabilize session activity and attention updates Keep streamed chat state in sync as sessions complete Restore reliable attention indicators for inactive sessions Tighten server and UI event handling for chat updates * feat: add chat render previews and file icon visibility setting Added animated chat render mode previews in Chat settings. Added a new toggle to show or hide file icons across tool rows. Persisted chat render, activity, and mermaid modes to shared settings file. * fix: update default GitHub OAuth client ID Use the new OpenChamber org GitHub OAuth app ID by default. Keep web server and VS Code auth defaults in sync. * fix: sync chat settings across VS Code chat views Broadcast settings changes to both sidebar and editor chat webviews. Apply synced settings in each webview via shared settings refresh. Ensures chat render mode updates appear immediately in editor tabs. * fix: stop writing session folders file in VS Code workspaces Disable disk persistence for session folders in VS Code webview runtime Keep session folder state in VS Code/webview storage instead of project files Preserve existing behavior for non-VS Code runtimes * style: polish expanded tool panels and bash output readability Removed extra borders/labels in expanded tool cards for a cleaner UI Added softer scroll shadows and full-height decorative activity line Switched bash command/output to code-style typography with tighter spacing * feat: add default-open tool controls for chat activity Added Chat settings toggles to open Bash and Edit tools by default Default-open behavior now applies across both sorted and live activity rendering Users can still manually collapse default-open tools, plus bash output height was reduced * fix: align chat loading skeleton with message layout Match skeleton width and horizontal alignment to chat message column Add one-line tool-style skeleton rows with subtle circular placeholders Increase top spacing so loading state no longer touches chat header * fix: prevent blank screen when adding inline diff comments Sanitize persisted inline comment drafts to ignore invalid data Harden diff annotations against malformed line numbers Fail safely when annotation updates error instead of crashing the view * fix: streamline VS Code archived sessions sidebar Show archived sessions in VS Code only for the active workspace directory. Render archived sessions as a flat list in VS Code without project-root subfolders. Hide the "Open in Side Panel" session action in VS Code.
This commit is contained in:
committed by
GitHub
parent
d969ff4af1
commit
fab32ca992
@@ -1,128 +0,0 @@
|
||||
import React from 'react';
|
||||
import { RiInformationLine, RiRestartLine } from '@remixicon/react';
|
||||
import { NumberInput } from '@/components/ui/number-input';
|
||||
import { ButtonSmall } from '@/components/ui/button-small';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { updateDesktopSettings } from '@/lib/persistence';
|
||||
import { getRegisteredRuntimeAPIs } from '@/contexts/runtimeAPIRegistry';
|
||||
import { DEFAULT_MESSAGE_LIMIT } from '@/stores/types/sessionTypes';
|
||||
|
||||
const MIN_LIMIT = 10;
|
||||
const MAX_LIMIT = 500;
|
||||
|
||||
export const MemoryLimitsSettings: React.FC = () => {
|
||||
const messageLimit = useUIStore((state) => state.messageLimit);
|
||||
const setMessageLimit = useUIStore((state) => state.setMessageLimit);
|
||||
|
||||
const [isLoading, setIsLoading] = React.useState(true);
|
||||
|
||||
// Load settings from server on mount
|
||||
React.useEffect(() => {
|
||||
const loadSettings = async () => {
|
||||
try {
|
||||
let data: { messageLimit?: number } | null = null;
|
||||
|
||||
// 1. Runtime settings API (VSCode)
|
||||
if (!data) {
|
||||
const runtimeSettings = getRegisteredRuntimeAPIs()?.settings;
|
||||
if (runtimeSettings) {
|
||||
try {
|
||||
const result = await runtimeSettings.load();
|
||||
const settings = result?.settings as Record<string, unknown> | undefined;
|
||||
if (settings) {
|
||||
data = {
|
||||
messageLimit: typeof settings.messageLimit === 'number' ? settings.messageLimit : undefined,
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fetch API (Web/server)
|
||||
if (!data) {
|
||||
const response = await fetch('/api/config/settings', {
|
||||
method: 'GET',
|
||||
headers: { Accept: 'application/json' },
|
||||
});
|
||||
if (response.ok) {
|
||||
data = await response.json();
|
||||
}
|
||||
}
|
||||
|
||||
if (data && typeof data.messageLimit === 'number') {
|
||||
setMessageLimit(data.messageLimit);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to load memory limits settings:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
loadSettings();
|
||||
}, [setMessageLimit]);
|
||||
|
||||
const handleChange = React.useCallback((value: number) => {
|
||||
setMessageLimit(value);
|
||||
void updateDesktopSettings({ messageLimit: value }).catch((error: unknown) => {
|
||||
console.warn('Failed to save messageLimit:', error);
|
||||
});
|
||||
}, [setMessageLimit]);
|
||||
|
||||
if (isLoading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isDefault = messageLimit === DEFAULT_MESSAGE_LIMIT;
|
||||
|
||||
return (
|
||||
<div className="mb-8">
|
||||
<div className="mb-1 px-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="typography-ui-header font-medium text-foreground">Message Memory</h3>
|
||||
<Tooltip delayDuration={1000}>
|
||||
<TooltipTrigger asChild>
|
||||
<RiInformationLine className="h-3.5 w-3.5 text-muted-foreground/60 cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent sideOffset={8} className="max-w-xs">
|
||||
Limit how many messages are loaded per session in memory.<br />
|
||||
Older messages are available via "Load more". Background sessions are trimmed automatically.
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section className="px-2 pb-2 pt-0">
|
||||
<div className="flex flex-col gap-2 py-1.5 sm:flex-row sm:items-center sm:gap-8">
|
||||
<div className="flex min-w-0 flex-col sm:w-56 shrink-0">
|
||||
<span className="typography-ui-label text-foreground">Message Limit</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 sm:w-fit">
|
||||
<NumberInput
|
||||
value={messageLimit}
|
||||
onValueChange={handleChange}
|
||||
min={MIN_LIMIT}
|
||||
max={MAX_LIMIT}
|
||||
step={10}
|
||||
aria-label="Message limit"
|
||||
className="w-20 tabular-nums"
|
||||
/>
|
||||
<ButtonSmall
|
||||
type="button"
|
||||
variant="ghost"
|
||||
onClick={() => handleChange(DEFAULT_MESSAGE_LIMIT)}
|
||||
disabled={isDefault}
|
||||
className="h-7 w-7 px-0 text-muted-foreground hover:text-foreground"
|
||||
aria-label="Reset message limit"
|
||||
title="Reset"
|
||||
>
|
||||
<RiRestartLine className="h-3.5 w-3.5" />
|
||||
</ButtonSmall>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { OpenChamberVisualSettings } from './OpenChamberVisualSettings';
|
||||
import { AboutSettings } from './AboutSettings';
|
||||
import { SessionRetentionSettings } from './SessionRetentionSettings';
|
||||
import { MemoryLimitsSettings } from './MemoryLimitsSettings';
|
||||
import { DefaultsSettings } from './DefaultsSettings';
|
||||
import { GitSettings } from './GitSettings';
|
||||
import { NotificationSettings } from './NotificationSettings';
|
||||
@@ -115,12 +114,12 @@ const VisualSectionContent: React.FC = () => {
|
||||
]} />;
|
||||
};
|
||||
|
||||
// Chat section: Default Tool Output, User message rendering, Diff layout, Mobile status bar, Show reasoning traces, Justification activity, Activity header timestamps, Queue mode, Persist draft
|
||||
// Chat section: User message rendering, Diff layout, Mobile status bar, Show reasoning traces, Queue mode, Persist draft
|
||||
const ChatSectionContent: React.FC = () => {
|
||||
return <OpenChamberVisualSettings visibleSettings={['toolOutput', 'mermaidRendering', 'userMessageRendering', 'stickyUserHeader', 'diffLayout', 'mobileStatusBar', 'dotfiles', 'reasoning', 'textJustificationActivity', 'activityHeaderTimestamps', 'queueMode', 'persistDraft', 'inputSpellcheck']} />;
|
||||
return <OpenChamberVisualSettings visibleSettings={['chatRenderMode', 'activityRenderMode', 'userMessageRendering', 'mermaidRendering', 'reasoning', 'showToolFileIcons', 'expandedTools', 'stickyUserHeader', 'diffLayout', 'mobileStatusBar', 'dotfiles', 'queueMode', 'persistDraft', 'inputSpellcheck']} />;
|
||||
};
|
||||
|
||||
// Sessions section: Default model & agent, Session retention, Memory limits
|
||||
// Sessions section: Default model & agent, Session retention
|
||||
const SessionsSectionContent: React.FC = () => {
|
||||
const isVSCode = isVSCodeRuntime();
|
||||
return (
|
||||
@@ -134,9 +133,6 @@ const SessionsSectionContent: React.FC = () => {
|
||||
<div className="border-t border-border/40 pt-6">
|
||||
<SessionRetentionSettings />
|
||||
</div>
|
||||
<div className="border-t border-border/40 pt-6">
|
||||
<MemoryLimitsSettings />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { isVSCodeRuntime, isWebRuntime } from '@/lib/desktop';
|
||||
import { isDesktopShell, isVSCodeRuntime, isWebRuntime } from '@/lib/desktop';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { usePwaDetection } from '@/hooks/usePwaDetection';
|
||||
import { updateDesktopSettings } from '@/lib/persistence';
|
||||
@@ -49,13 +49,6 @@ const THEME_MODE_OPTIONS: Array<{ value: ThemeMode; label: string }> = [
|
||||
},
|
||||
];
|
||||
|
||||
const TOOL_EXPANSION_OPTIONS: Array<{ value: 'collapsed' | 'activity' | 'detailed' | 'changes'; label: string; description: string }> = [
|
||||
{ value: 'collapsed', label: 'Collapsed', description: 'Activity and tool calls stay collapsed by default.' },
|
||||
{ value: 'activity', label: 'Summary', description: 'Activity opens by default; tool calls stay collapsed.' },
|
||||
{ value: 'detailed', label: 'Detailed', description: 'Activity opens; key tools auto-expand for richer detail.' },
|
||||
{ value: 'changes', label: 'Changes', description: 'Activity opens; only edit/write/patch tools auto-expand.' },
|
||||
];
|
||||
|
||||
const DIFF_LAYOUT_OPTIONS: Option<'dynamic' | 'inline' | 'side-by-side'>[] = [
|
||||
{
|
||||
id: 'dynamic',
|
||||
@@ -120,11 +113,37 @@ const USER_MESSAGE_RENDERING_OPTIONS: Option<'markdown' | 'plain'>[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const CHAT_RENDER_MODE_OPTIONS: Option<'sorted' | 'live'>[] = [
|
||||
{
|
||||
id: 'sorted',
|
||||
label: 'Sorted',
|
||||
description: 'Render completed assistant messages without live streaming.',
|
||||
},
|
||||
{
|
||||
id: 'live',
|
||||
label: 'Live',
|
||||
description: 'Stream assistant text and tools as they arrive.',
|
||||
},
|
||||
];
|
||||
|
||||
const ACTIVITY_RENDER_MODE_OPTIONS: Option<'collapsed' | 'summary'>[] = [
|
||||
{
|
||||
id: 'collapsed',
|
||||
label: 'Collapsed',
|
||||
description: 'Keep Activity collapsed by default.',
|
||||
},
|
||||
{
|
||||
id: 'summary',
|
||||
label: 'Summary',
|
||||
description: 'Expand Activity by default.',
|
||||
},
|
||||
];
|
||||
|
||||
const normalizeUserMessageRenderingMode = (mode: unknown): 'markdown' | 'plain' => {
|
||||
return mode === 'markdown' ? 'markdown' : 'plain';
|
||||
};
|
||||
|
||||
export type VisibleSetting = 'theme' | 'pwaInstallName' | 'fontSize' | 'terminalFontSize' | 'spacing' | 'cornerRadius' | 'inputBarOffset' | 'navRail' | 'toolOutput' | 'mermaidRendering' | 'userMessageRendering' | 'stickyUserHeader' | 'diffLayout' | 'mobileStatusBar' | 'dotfiles' | 'reasoning' | 'queueMode' | 'textJustificationActivity' | 'activityHeaderTimestamps' | 'terminalQuickKeys' | 'persistDraft' | 'inputSpellcheck';
|
||||
export type VisibleSetting = 'theme' | 'pwaInstallName' | 'fontSize' | 'terminalFontSize' | 'spacing' | 'cornerRadius' | 'inputBarOffset' | 'navRail' | 'mermaidRendering' | 'userMessageRendering' | 'chatRenderMode' | 'activityRenderMode' | 'stickyUserHeader' | 'diffLayout' | 'mobileStatusBar' | 'dotfiles' | 'reasoning' | 'showToolFileIcons' | 'expandedTools' | 'queueMode' | 'terminalQuickKeys' | 'persistDraft' | 'inputSpellcheck';
|
||||
|
||||
interface OpenChamberVisualSettingsProps {
|
||||
/** Which settings to show. If undefined, shows all. */
|
||||
@@ -137,18 +156,17 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
const directoryShowHidden = useDirectoryShowHidden();
|
||||
const showReasoningTraces = useUIStore(state => state.showReasoningTraces);
|
||||
const setShowReasoningTraces = useUIStore(state => state.setShowReasoningTraces);
|
||||
const showTextJustificationActivity = useUIStore(state => state.showTextJustificationActivity);
|
||||
const setShowTextJustificationActivity = useUIStore(state => state.setShowTextJustificationActivity);
|
||||
const showActivityHeaderTimestamps = useUIStore(state => state.showActivityHeaderTimestamps);
|
||||
const setShowActivityHeaderTimestamps = useUIStore(state => state.setShowActivityHeaderTimestamps);
|
||||
const toolCallExpansion = useUIStore(state => state.toolCallExpansion);
|
||||
const setToolCallExpansion = useUIStore(state => state.setToolCallExpansion);
|
||||
|
||||
const mermaidRenderingMode = useUIStore(state => state.mermaidRenderingMode);
|
||||
const setMermaidRenderingMode = useUIStore(state => state.setMermaidRenderingMode);
|
||||
const userMessageRenderingMode = useUIStore(state => state.userMessageRenderingMode);
|
||||
const setUserMessageRenderingMode = useUIStore(state => state.setUserMessageRenderingMode);
|
||||
const stickyUserHeader = useUIStore(state => state.stickyUserHeader);
|
||||
const setStickyUserHeader = useUIStore(state => state.setStickyUserHeader);
|
||||
const chatRenderMode = useUIStore(state => state.chatRenderMode);
|
||||
const setChatRenderMode = useUIStore(state => state.setChatRenderMode);
|
||||
const activityRenderMode = useUIStore(state => state.activityRenderMode);
|
||||
const setActivityRenderMode = useUIStore(state => state.setActivityRenderMode);
|
||||
const fontSize = useUIStore(state => state.fontSize);
|
||||
const setFontSize = useUIStore(state => state.setFontSize);
|
||||
const terminalFontSize = useUIStore(state => state.terminalFontSize);
|
||||
@@ -171,6 +189,12 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
const setPersistChatDraft = useUIStore(state => state.setPersistChatDraft);
|
||||
const inputSpellcheckEnabled = useUIStore(state => state.inputSpellcheckEnabled);
|
||||
const setInputSpellcheckEnabled = useUIStore(state => state.setInputSpellcheckEnabled);
|
||||
const showToolFileIcons = useUIStore(state => state.showToolFileIcons);
|
||||
const setShowToolFileIcons = useUIStore(state => state.setShowToolFileIcons);
|
||||
const showExpandedBashTools = useUIStore(state => state.showExpandedBashTools);
|
||||
const setShowExpandedBashTools = useUIStore(state => state.setShowExpandedBashTools);
|
||||
const showExpandedEditTools = useUIStore(state => state.showExpandedEditTools);
|
||||
const setShowExpandedEditTools = useUIStore(state => state.setShowExpandedEditTools);
|
||||
const isNavRailExpanded = useUIStore(state => state.isNavRailExpanded);
|
||||
const setNavRailExpanded = useUIStore(state => state.setNavRailExpanded);
|
||||
const showMobileSessionStatusBar = useUIStore(state => state.showMobileSessionStatusBar);
|
||||
@@ -188,6 +212,18 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
} = useThemeSystem();
|
||||
|
||||
const [themesReloading, setThemesReloading] = React.useState(false);
|
||||
const [chatRenderPreviewTick, setChatRenderPreviewTick] = React.useState(0);
|
||||
|
||||
React.useEffect(() => {
|
||||
const intervalId = setInterval(() => {
|
||||
setChatRenderPreviewTick((prev) => (prev + 1) % 24);
|
||||
}, 420);
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleUserMessageRenderingModeChange = React.useCallback((mode: 'markdown' | 'plain') => {
|
||||
setUserMessageRenderingMode(mode);
|
||||
void updateDesktopSettings({ userMessageRenderingMode: mode });
|
||||
@@ -203,6 +239,36 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
void updateDesktopSettings({ inputSpellcheckEnabled: enabled });
|
||||
}, [setInputSpellcheckEnabled]);
|
||||
|
||||
const handleChatRenderModeChange = React.useCallback((mode: 'sorted' | 'live') => {
|
||||
setChatRenderMode(mode);
|
||||
void updateDesktopSettings({ chatRenderMode: mode });
|
||||
}, [setChatRenderMode]);
|
||||
|
||||
const handleActivityRenderModeChange = React.useCallback((mode: 'collapsed' | 'summary') => {
|
||||
setActivityRenderMode(mode);
|
||||
void updateDesktopSettings({ activityRenderMode: mode });
|
||||
}, [setActivityRenderMode]);
|
||||
|
||||
const handleMermaidRenderingModeChange = React.useCallback((mode: 'svg' | 'ascii') => {
|
||||
setMermaidRenderingMode(mode);
|
||||
void updateDesktopSettings({ mermaidRenderingMode: mode });
|
||||
}, [setMermaidRenderingMode]);
|
||||
|
||||
const handleShowToolFileIconsChange = React.useCallback((enabled: boolean) => {
|
||||
setShowToolFileIcons(enabled);
|
||||
void updateDesktopSettings({ showToolFileIcons: enabled });
|
||||
}, [setShowToolFileIcons]);
|
||||
|
||||
const handleShowExpandedBashToolsChange = React.useCallback((enabled: boolean) => {
|
||||
setShowExpandedBashTools(enabled);
|
||||
void updateDesktopSettings({ showExpandedBashTools: enabled });
|
||||
}, [setShowExpandedBashTools]);
|
||||
|
||||
const handleShowExpandedEditToolsChange = React.useCallback((enabled: boolean) => {
|
||||
setShowExpandedEditTools(enabled);
|
||||
void updateDesktopSettings({ showExpandedEditTools: enabled });
|
||||
}, [setShowExpandedEditTools]);
|
||||
|
||||
const lightThemes = React.useMemo(
|
||||
() => availableThemes
|
||||
.filter((theme) => theme.metadata.variant === 'light')
|
||||
@@ -241,22 +307,22 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
const hasAppearanceSettings = (shouldShow('theme') || shouldShow('pwaInstallName')) && !isVSCode;
|
||||
const hasLayoutSettings = shouldShow('fontSize') || shouldShow('terminalFontSize') || shouldShow('spacing') || shouldShow('cornerRadius') || shouldShow('inputBarOffset');
|
||||
const hasNavigationSettings = (!isMobile && shouldShow('navRail')) || (shouldShow('terminalQuickKeys') && !isMobile);
|
||||
const hasBehaviorSettings = shouldShow('toolOutput')
|
||||
|| shouldShow('mermaidRendering')
|
||||
const hasBehaviorSettings = shouldShow('mermaidRendering')
|
||||
|| shouldShow('userMessageRendering')
|
||||
|| shouldShow('chatRenderMode')
|
||||
|| (shouldShow('activityRenderMode') && chatRenderMode === 'sorted')
|
||||
|| shouldShow('stickyUserHeader')
|
||||
|| shouldShow('diffLayout')
|
||||
|| (shouldShow('mobileStatusBar') && isMobile)
|
||||
|| shouldShow('dotfiles')
|
||||
|| shouldShow('reasoning')
|
||||
|| shouldShow('queueMode')
|
||||
|| shouldShow('textJustificationActivity')
|
||||
|| shouldShow('activityHeaderTimestamps')
|
||||
|| shouldShow('persistDraft')
|
||||
|| shouldShow('showToolFileIcons')
|
||||
|| shouldShow('expandedTools')
|
||||
|| (!isMobile && shouldShow('inputSpellcheck'));
|
||||
const selectedToolExpansionOption = TOOL_EXPANSION_OPTIONS.find((option) => option.value === toolCallExpansion);
|
||||
|
||||
const showPwaInstallNameSetting = shouldShow('pwaInstallName') && isWebRuntime() && browserTab;
|
||||
const showPwaInstallNameSetting = shouldShow('pwaInstallName') && isWebRuntime() && browserTab && !isDesktopShell() && !isVSCode;
|
||||
const [pwaInstallName, setPwaInstallName] = React.useState('');
|
||||
|
||||
const applyPwaInstallName = React.useCallback(async (value: string) => {
|
||||
@@ -426,12 +492,12 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
</div>
|
||||
|
||||
{showPwaInstallNameSetting && (
|
||||
<div className={cn('py-1.5', isMobile ? 'space-y-2' : 'flex items-center gap-8')}>
|
||||
<div className={cn('flex min-w-0 flex-col', isMobile ? 'w-full' : 'w-56 shrink-0')}>
|
||||
<div className="py-1.5 space-y-1.5">
|
||||
<div className="flex min-w-0 flex-col">
|
||||
<span className="typography-ui-label text-foreground">Install App Name</span>
|
||||
<span className="typography-meta text-muted-foreground">Used by PWA installation process.</span>
|
||||
</div>
|
||||
<div className={cn('flex items-center gap-2', isMobile ? 'w-full' : 'w-fit min-w-[22rem]')}>
|
||||
<div className="flex w-full max-w-[28rem] items-center gap-2">
|
||||
<Input
|
||||
value={pwaInstallName}
|
||||
onChange={(event) => {
|
||||
@@ -715,44 +781,182 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
{hasBehaviorSettings && (
|
||||
<div className="space-y-3">
|
||||
|
||||
{shouldShow('toolOutput') && (
|
||||
<section className="px-2 pb-2 pt-0">
|
||||
<h4 className="typography-ui-header font-medium text-foreground">Default Tool Output</h4>
|
||||
<div className="mt-1.5 flex flex-wrap items-center gap-1">
|
||||
{TOOL_EXPANSION_OPTIONS.map((option) => {
|
||||
return (
|
||||
<ButtonSmall
|
||||
key={option.value}
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="xs"
|
||||
className={cn(
|
||||
'!font-normal',
|
||||
toolCallExpansion === option.value
|
||||
? 'border-[var(--primary-base)] text-[var(--primary-base)] bg-[var(--primary-base)]/10 hover:text-[var(--primary-base)]'
|
||||
: 'text-foreground'
|
||||
)}
|
||||
onClick={() => setToolCallExpansion(option.value)}
|
||||
>
|
||||
{option.label}
|
||||
</ButtonSmall>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{selectedToolExpansionOption && (
|
||||
<p className="mt-2 typography-ui-label font-normal text-muted-foreground">
|
||||
{selectedToolExpansionOption.description}
|
||||
</p>
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
|
||||
{(shouldShow('userMessageRendering') || shouldShow('mermaidRendering') || (shouldShow('diffLayout') && !isVSCode)) && (
|
||||
|
||||
{(shouldShow('userMessageRendering') || shouldShow('mermaidRendering') || shouldShow('chatRenderMode') || (shouldShow('activityRenderMode') && chatRenderMode === 'sorted') || (shouldShow('diffLayout') && !isVSCode)) && (
|
||||
<div className="grid grid-cols-1 gap-y-2 md:grid-cols-[minmax(0,16rem)_minmax(0,16rem)] md:justify-start md:gap-x-2">
|
||||
{shouldShow('chatRenderMode') && (
|
||||
<section className="p-2 md:col-span-2">
|
||||
<h4 className="typography-ui-header font-medium text-foreground">Chat Render Mode</h4>
|
||||
<div role="radiogroup" aria-label="Chat render mode" className="mt-1 grid w-full max-w-[26rem] grid-cols-1 gap-3 sm:grid-cols-2">
|
||||
{CHAT_RENDER_MODE_OPTIONS.map((option) => {
|
||||
const selected = chatRenderMode === option.id;
|
||||
const previewPhase = chatRenderPreviewTick % 12;
|
||||
return (
|
||||
<button
|
||||
key={option.id}
|
||||
type="button"
|
||||
onClick={() => handleChatRenderModeChange(option.id)}
|
||||
aria-pressed={selected}
|
||||
className={cn(
|
||||
'flex flex-col items-start gap-1 rounded-lg border p-3 text-left transition-colors',
|
||||
selected
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-border hover:border-border/80 hover:bg-muted/50'
|
||||
)}
|
||||
>
|
||||
<span className={cn('typography-ui-label', selected ? 'text-foreground' : 'text-muted-foreground')}>
|
||||
{option.label}
|
||||
</span>
|
||||
<div className="mt-2 w-full rounded-md border border-border/60 bg-muted/30 p-2">
|
||||
{option.id === 'live' ? (
|
||||
<div className="space-y-1.5">
|
||||
{[0, 1, 2].map((index) => {
|
||||
const rowStart = index * 3 + 1;
|
||||
const rowProgressPhase = previewPhase - rowStart + 1;
|
||||
const rowProgress = rowProgressPhase <= 0
|
||||
? 0
|
||||
: rowProgressPhase === 1
|
||||
? 42
|
||||
: rowProgressPhase === 2
|
||||
? 68
|
||||
: 92;
|
||||
const visible = rowProgress > 0;
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 transition-all duration-300 motion-reduce:transition-none',
|
||||
visible ? 'translate-y-0 opacity-100' : 'translate-y-1 opacity-0'
|
||||
)}
|
||||
>
|
||||
<span className="h-2 w-2 shrink-0 rounded-full bg-muted-foreground/55" />
|
||||
<span
|
||||
className="h-1.5 rounded bg-muted-foreground/30 transition-all duration-300 motion-reduce:transition-none"
|
||||
style={{ width: `${rowProgress}%` }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-1.5">
|
||||
{[0, 1, 2].map((index) => {
|
||||
const visible = previewPhase >= (index + 1) * 3;
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 transition-all duration-300 motion-reduce:transition-none',
|
||||
visible ? 'translate-y-0 opacity-100' : 'translate-y-1 opacity-0'
|
||||
)}
|
||||
>
|
||||
<span className="h-2 w-2 shrink-0 rounded-full bg-muted-foreground/55" />
|
||||
<span
|
||||
className="h-1.5 rounded bg-muted-foreground/30"
|
||||
style={{ width: '92%' }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{shouldShow('activityRenderMode') && chatRenderMode === 'sorted' && (
|
||||
<section className="p-2 md:col-span-2">
|
||||
<h4 className="typography-ui-header font-medium text-foreground">Activity Default</h4>
|
||||
<div role="radiogroup" aria-label="Activity default mode" className="mt-0.5 space-y-0">
|
||||
{ACTIVITY_RENDER_MODE_OPTIONS.map((option) => {
|
||||
const selected = activityRenderMode === option.id;
|
||||
return (
|
||||
<div
|
||||
key={option.id}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={selected}
|
||||
onClick={() => handleActivityRenderModeChange(option.id)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === ' ' || event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
handleActivityRenderModeChange(option.id);
|
||||
}
|
||||
}}
|
||||
className="flex w-full items-center gap-2 py-0 text-left"
|
||||
>
|
||||
<Radio
|
||||
checked={selected}
|
||||
onChange={() => handleActivityRenderModeChange(option.id)}
|
||||
ariaLabel={`Activity default mode: ${option.label}`}
|
||||
/>
|
||||
<span className={cn('typography-ui-label font-normal', selected ? 'text-foreground' : 'text-foreground/50')}>
|
||||
{option.label}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{shouldShow('expandedTools') && (
|
||||
<section className="p-2 md:col-span-2 space-y-0.5">
|
||||
<div className="typography-ui-header font-medium text-foreground py-1.5">Show tools opened by default:</div>
|
||||
|
||||
<div
|
||||
className="group flex cursor-pointer items-center gap-2 py-0.5"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={showExpandedBashTools}
|
||||
onClick={() => handleShowExpandedBashToolsChange(!showExpandedBashTools)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === ' ' || event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
handleShowExpandedBashToolsChange(!showExpandedBashTools);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={showExpandedBashTools}
|
||||
onChange={handleShowExpandedBashToolsChange}
|
||||
ariaLabel="Show expanded bash tools"
|
||||
/>
|
||||
<span className="typography-ui-label text-foreground">Bash</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="group flex cursor-pointer items-center gap-2 py-0.5"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={showExpandedEditTools}
|
||||
onClick={() => handleShowExpandedEditToolsChange(!showExpandedEditTools)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === ' ' || event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
handleShowExpandedEditToolsChange(!showExpandedEditTools);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={showExpandedEditTools}
|
||||
onChange={handleShowExpandedEditToolsChange}
|
||||
ariaLabel="Show expanded edit tools"
|
||||
/>
|
||||
<span className="typography-ui-label text-foreground">Edit tools</span>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{shouldShow('userMessageRendering') && (
|
||||
<section className="p-2">
|
||||
<h4 className="typography-ui-header font-medium text-foreground">User Message Rendering</h4>
|
||||
<div role="radiogroup" aria-label="User message rendering mode" className="mt-1 space-y-0">
|
||||
<div role="radiogroup" aria-label="User message rendering mode" className="mt-0.5 space-y-0">
|
||||
{USER_MESSAGE_RENDERING_OPTIONS.map((option) => {
|
||||
const selected = normalizeUserMessageRenderingMode(userMessageRenderingMode) === option.id;
|
||||
return (
|
||||
@@ -768,7 +972,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
handleUserMessageRenderingModeChange(option.id);
|
||||
}
|
||||
}}
|
||||
className="flex w-full items-center gap-2 py-0.5 text-left"
|
||||
className="flex w-full items-center gap-2 py-0 text-left"
|
||||
>
|
||||
<Radio
|
||||
checked={selected}
|
||||
@@ -788,7 +992,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
{shouldShow('mermaidRendering') && (
|
||||
<section className="p-2">
|
||||
<h4 className="typography-ui-header font-medium text-foreground">Mermaid Rendering</h4>
|
||||
<div role="radiogroup" aria-label="Mermaid rendering mode" className="mt-1 space-y-0">
|
||||
<div role="radiogroup" aria-label="Mermaid rendering mode" className="mt-0.5 space-y-0">
|
||||
{MERMAID_RENDERING_OPTIONS.map((option) => {
|
||||
const selected = mermaidRenderingMode === option.id;
|
||||
return (
|
||||
@@ -797,18 +1001,18 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={selected}
|
||||
onClick={() => setMermaidRenderingMode(option.id)}
|
||||
onClick={() => handleMermaidRenderingModeChange(option.id)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === ' ' || event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
setMermaidRenderingMode(option.id);
|
||||
handleMermaidRenderingModeChange(option.id);
|
||||
}
|
||||
}}
|
||||
className="flex w-full items-center gap-2 py-0.5 text-left"
|
||||
className="flex w-full items-center gap-2 py-0 text-left"
|
||||
>
|
||||
<Radio
|
||||
checked={selected}
|
||||
onChange={() => setMermaidRenderingMode(option.id)}
|
||||
onChange={() => handleMermaidRenderingModeChange(option.id)}
|
||||
ariaLabel={`Mermaid rendering: ${option.label}`}
|
||||
/>
|
||||
<span className={cn('typography-ui-label font-normal', selected ? 'text-foreground' : 'text-foreground/50')}>
|
||||
@@ -824,7 +1028,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
{shouldShow('diffLayout') && !isVSCode && (
|
||||
<section className="p-2">
|
||||
<h4 className="typography-ui-header font-medium text-foreground">Diff Layout</h4>
|
||||
<div role="radiogroup" aria-label="Diff layout" className="mt-1 space-y-0">
|
||||
<div role="radiogroup" aria-label="Diff layout" className="mt-0.5 space-y-0">
|
||||
{DIFF_LAYOUT_OPTIONS.map((option) => {
|
||||
const selected = diffLayoutPreference === option.id;
|
||||
return (
|
||||
@@ -840,7 +1044,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
setDiffLayoutPreference(option.id);
|
||||
}
|
||||
}}
|
||||
className="flex w-full items-center gap-2 py-0.5 text-left"
|
||||
className="flex w-full items-center gap-2 py-0 text-left"
|
||||
>
|
||||
<Radio
|
||||
checked={selected}
|
||||
@@ -860,7 +1064,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
{shouldShow('diffLayout') && !isVSCode && (
|
||||
<section className="p-2">
|
||||
<h4 className="typography-ui-header font-medium text-foreground">Diff View Mode</h4>
|
||||
<div role="radiogroup" aria-label="Diff view mode" className="mt-1 space-y-0">
|
||||
<div role="radiogroup" aria-label="Diff view mode" className="mt-0.5 space-y-0">
|
||||
{DIFF_VIEW_MODE_OPTIONS.map((option) => {
|
||||
const selected = diffViewMode === option.id;
|
||||
return (
|
||||
@@ -876,7 +1080,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
setDiffViewMode(option.id);
|
||||
}
|
||||
}}
|
||||
className="flex w-full items-center gap-2 py-0.5 text-left"
|
||||
className="flex w-full items-center gap-2 py-0 text-left"
|
||||
>
|
||||
<Radio
|
||||
checked={selected}
|
||||
@@ -895,11 +1099,34 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(shouldShow('stickyUserHeader') || (shouldShow('mobileStatusBar') && isMobile) || shouldShow('dotfiles') || shouldShow('queueMode') || shouldShow('persistDraft') || (!isMobile && shouldShow('inputSpellcheck')) || shouldShow('reasoning') || shouldShow('textJustificationActivity')) && (
|
||||
{(shouldShow('stickyUserHeader') || (shouldShow('mobileStatusBar') && isMobile) || shouldShow('dotfiles') || shouldShow('queueMode') || shouldShow('persistDraft') || shouldShow('showToolFileIcons') || (!isMobile && shouldShow('inputSpellcheck')) || shouldShow('reasoning')) && (
|
||||
<section className="p-2 space-y-0.5">
|
||||
{shouldShow('reasoning') && (
|
||||
<div
|
||||
className="group flex cursor-pointer items-center gap-2 py-0.5"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={showReasoningTraces}
|
||||
onClick={() => setShowReasoningTraces(!showReasoningTraces)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === ' ' || event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
setShowReasoningTraces(!showReasoningTraces);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={showReasoningTraces}
|
||||
onChange={setShowReasoningTraces}
|
||||
ariaLabel="Show reasoning traces"
|
||||
/>
|
||||
<span className="typography-ui-label text-foreground">Show Reasoning Traces</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{shouldShow('stickyUserHeader') && (
|
||||
<div
|
||||
className="group flex cursor-pointer items-center gap-2 py-1.5"
|
||||
className="group flex cursor-pointer items-center gap-2 py-0.5"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={stickyUserHeader}
|
||||
@@ -920,9 +1147,32 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{shouldShow('showToolFileIcons') && (
|
||||
<div
|
||||
className="group flex cursor-pointer items-center gap-2 py-0.5"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={showToolFileIcons}
|
||||
onClick={() => handleShowToolFileIconsChange(!showToolFileIcons)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === ' ' || event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
handleShowToolFileIconsChange(!showToolFileIcons);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={showToolFileIcons}
|
||||
onChange={handleShowToolFileIconsChange}
|
||||
ariaLabel="Show tool file icons"
|
||||
/>
|
||||
<span className="typography-ui-label text-foreground">Show Tool File Icons</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{shouldShow('mobileStatusBar') && isMobile && (
|
||||
<div
|
||||
className="group flex cursor-pointer items-center gap-2 py-1.5"
|
||||
className="group flex cursor-pointer items-center gap-2 py-0.5"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={showMobileSessionStatusBar}
|
||||
@@ -945,7 +1195,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
|
||||
{shouldShow('dotfiles') && !isVSCodeRuntime() && (
|
||||
<div
|
||||
className="group flex cursor-pointer items-center gap-2 py-1.5"
|
||||
className="group flex cursor-pointer items-center gap-2 py-0.5"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={directoryShowHidden}
|
||||
@@ -968,7 +1218,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
|
||||
{shouldShow('queueMode') && (
|
||||
<div
|
||||
className="group flex cursor-pointer items-center gap-2 py-1.5"
|
||||
className="group flex cursor-pointer items-center gap-2 py-0.5"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={queueModeEnabled}
|
||||
@@ -1001,7 +1251,7 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
|
||||
{shouldShow('persistDraft') && (
|
||||
<div
|
||||
className="group flex cursor-pointer items-center gap-2 py-1.5"
|
||||
className="group flex cursor-pointer items-center gap-2 py-0.5"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={persistChatDraft}
|
||||
@@ -1045,74 +1295,6 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{shouldShow('reasoning') && (
|
||||
<div
|
||||
className="group flex cursor-pointer items-center gap-2 py-1.5"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={showReasoningTraces}
|
||||
onClick={() => setShowReasoningTraces(!showReasoningTraces)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === ' ' || event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
setShowReasoningTraces(!showReasoningTraces);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={showReasoningTraces}
|
||||
onChange={setShowReasoningTraces}
|
||||
ariaLabel="Show reasoning traces"
|
||||
/>
|
||||
<span className="typography-ui-label text-foreground">Show Reasoning Traces</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{shouldShow('textJustificationActivity') && (
|
||||
<div
|
||||
className="group flex cursor-pointer items-center gap-2 py-1.5"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={showTextJustificationActivity}
|
||||
onClick={() => setShowTextJustificationActivity(!showTextJustificationActivity)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === ' ' || event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
setShowTextJustificationActivity(!showTextJustificationActivity);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={showTextJustificationActivity}
|
||||
onChange={setShowTextJustificationActivity}
|
||||
ariaLabel="Show justification activity"
|
||||
/>
|
||||
<span className="typography-ui-label text-foreground">Show Justification Activity</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{shouldShow('activityHeaderTimestamps') && (
|
||||
<div
|
||||
className="group flex cursor-pointer items-center gap-2 py-1.5"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={showActivityHeaderTimestamps}
|
||||
onClick={() => setShowActivityHeaderTimestamps(!showActivityHeaderTimestamps)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === ' ' || event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
setShowActivityHeaderTimestamps(!showActivityHeaderTimestamps);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
checked={showActivityHeaderTimestamps}
|
||||
onChange={setShowActivityHeaderTimestamps}
|
||||
ariaLabel="Show tool and reasoning header timestamps"
|
||||
/>
|
||||
<span className="typography-ui-label text-foreground">Show Activity Header Timestamps</span>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user