diff --git a/ui-tui/src/__tests__/useConfigSync.test.ts b/ui-tui/src/__tests__/useConfigSync.test.ts new file mode 100644 index 0000000000..c14ecff3aa --- /dev/null +++ b/ui-tui/src/__tests__/useConfigSync.test.ts @@ -0,0 +1,67 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { $uiState, resetUiState } from '../app/uiStore.js' +import { applyDisplay } from '../app/useConfigSync.js' + +describe('applyDisplay', () => { + beforeEach(() => { + resetUiState() + }) + + it('fans every display flag out to $uiState and the bell callback', () => { + const setBell = vi.fn() + + applyDisplay( + { + config: { + display: { + bell_on_complete: true, + details_mode: 'expanded', + inline_diffs: false, + show_cost: true, + show_reasoning: true, + streaming: false, + tui_compact: true, + tui_statusbar: false + } + } + }, + setBell + ) + + const s = $uiState.get() + expect(setBell).toHaveBeenCalledWith(true) + expect(s.compact).toBe(true) + expect(s.detailsMode).toBe('expanded') + expect(s.inlineDiffs).toBe(false) + expect(s.showCost).toBe(true) + expect(s.showReasoning).toBe(true) + expect(s.statusBar).toBe(false) + expect(s.streaming).toBe(false) + }) + + it('applies v1 parity defaults when display fields are missing', () => { + const setBell = vi.fn() + + applyDisplay({ config: { display: {} } }, setBell) + + const s = $uiState.get() + expect(setBell).toHaveBeenCalledWith(false) + expect(s.inlineDiffs).toBe(true) + expect(s.showCost).toBe(false) + expect(s.showReasoning).toBe(false) + expect(s.statusBar).toBe(true) + expect(s.streaming).toBe(true) + }) + + it('treats a null config like an empty display block', () => { + const setBell = vi.fn() + + applyDisplay(null, setBell) + + const s = $uiState.get() + expect(setBell).toHaveBeenCalledWith(false) + expect(s.inlineDiffs).toBe(true) + expect(s.streaming).toBe(true) + }) +}) diff --git a/ui-tui/src/app/interfaces.ts b/ui-tui/src/app/interfaces.ts index ff2b1e5b5a..bf3d54c627 100644 --- a/ui-tui/src/app/interfaces.ts +++ b/ui-tui/src/app/interfaces.ts @@ -78,9 +78,13 @@ export interface UiState { compact: boolean detailsMode: DetailsMode info: null | SessionInfo + inlineDiffs: boolean + showCost: boolean + showReasoning: boolean sid: null | string status: string statusBar: boolean + streaming: boolean theme: Theme usage: Usage } diff --git a/ui-tui/src/app/uiStore.ts b/ui-tui/src/app/uiStore.ts index b7f5c20f4d..81089f1795 100644 --- a/ui-tui/src/app/uiStore.ts +++ b/ui-tui/src/app/uiStore.ts @@ -11,9 +11,13 @@ const buildUiState = (): UiState => ({ compact: false, detailsMode: 'collapsed', info: null, + inlineDiffs: true, + showCost: false, + showReasoning: false, sid: null, status: 'summoning hermes…', statusBar: true, + streaming: true, theme: DEFAULT_THEME, usage: ZERO }) diff --git a/ui-tui/src/app/useConfigSync.ts b/ui-tui/src/app/useConfigSync.ts index fe3cec5737..8a3756342b 100644 --- a/ui-tui/src/app/useConfigSync.ts +++ b/ui-tui/src/app/useConfigSync.ts @@ -27,14 +27,18 @@ const quietRpc = async = Record>( } } -const applyDisplay = (cfg: ConfigFullResponse | null, setBell: (v: boolean) => void) => { +export const applyDisplay = (cfg: ConfigFullResponse | null, setBell: (v: boolean) => void) => { const d = cfg?.config?.display ?? {} setBell(!!d.bell_on_complete) patchUiState({ compact: !!d.tui_compact, detailsMode: resolveDetailsMode(d), - statusBar: d.tui_statusbar !== false + inlineDiffs: d.inline_diffs !== false, + showCost: !!d.show_cost, + showReasoning: !!d.show_reasoning, + statusBar: d.tui_statusbar !== false, + streaming: d.streaming !== false }) } diff --git a/ui-tui/src/gatewayTypes.ts b/ui-tui/src/gatewayTypes.ts index c8d1c68552..fd5b6c1347 100644 --- a/ui-tui/src/gatewayTypes.ts +++ b/ui-tui/src/gatewayTypes.ts @@ -53,6 +53,10 @@ export type CommandDispatchResponse = export interface ConfigDisplayConfig { bell_on_complete?: boolean details_mode?: string + inline_diffs?: boolean + show_cost?: boolean + show_reasoning?: boolean + streaming?: boolean thinking_mode?: string tui_compact?: boolean tui_statusbar?: boolean