diff --git a/ui-tui/src/__tests__/syntax.test.ts b/ui-tui/src/__tests__/syntax.test.ts index 505988b2ab..603b7be39e 100644 --- a/ui-tui/src/__tests__/syntax.test.ts +++ b/ui-tui/src/__tests__/syntax.test.ts @@ -28,7 +28,7 @@ describe('syntax highlighter', () => { expect(colors).toContain(t.color.bronze) // const expect(colors).toContain(t.color.amber) // 'hi' - expect(colors).toContain(t.color.cornsilk) // 42 + expect(colors).toContain(t.color.text) // 42 }) it('falls through unchanged for unknown langs', () => { diff --git a/ui-tui/src/components/agentsOverlay.tsx b/ui-tui/src/components/agentsOverlay.tsx index 6d3917bf73..0470f9200f 100644 --- a/ui-tui/src/components/agentsOverlay.tsx +++ b/ui-tui/src/components/agentsOverlay.tsx @@ -383,7 +383,7 @@ function Field({ name, t, value }: { name: string; t: Theme; value: ReactNode }) return ( {name} · - {value} + {value} ) } @@ -411,7 +411,7 @@ function Detail({ id, node, t }: { id?: string; node: SubagentNode; t: Theme }) return ( - + {id ? #{id} : null} {glyph} {item.goal} @@ -472,7 +472,7 @@ function Detail({ id, node, t }: { id?: string; node: SubagentNode; t: Theme }) ))} {filesRead.slice(0, 8).map((p, i) => ( - + · {p} ))} @@ -484,7 +484,7 @@ function Detail({ id, node, t }: { id?: string; node: SubagentNode; t: Theme }) {toolLines.length > 0 ? ( {toolLines.map((line, i) => ( - + · {line} ))} @@ -494,7 +494,7 @@ function Detail({ id, node, t }: { id?: string; node: SubagentNode; t: Theme }) {outputTail.length > 0 ? ( {outputTail.map((entry, i) => ( - + {entry.tool} {' '} @@ -507,7 +507,7 @@ function Detail({ id, node, t }: { id?: string; node: SubagentNode; t: Theme }) {item.notes.length ? ( {item.notes.slice(-6).map((line, i) => ( - + · {line} ))} @@ -516,7 +516,7 @@ function Detail({ id, node, t }: { id?: string; node: SubagentNode; t: Theme }) {item.summary ? ( - + {item.summary} @@ -552,7 +552,7 @@ function ListRow({ const paren = line ? line.indexOf('(') : -1 const toolShort = line ? (paren > 0 ? line.slice(0, paren) : line).trim() : '' const trailing = toolShort ? ` · ${compactPreview(toolShort, 14)}` : '' - const fg = active ? t.color.amber : t.color.cornsilk + const fg = active ? t.color.amber : t.color.text return ( @@ -585,7 +585,7 @@ function DiffPane({ }) { return ( - + {label} @@ -661,20 +661,20 @@ function DiffView({ Δ - + {diffMetricLine('agents', aTotals.descendantCount, bTotals.descendantCount, round)} - {diffMetricLine('tools', aTotals.totalTools, bTotals.totalTools, round)} - + {diffMetricLine('tools', aTotals.totalTools, bTotals.totalTools, round)} + {diffMetricLine('depth', aTotals.maxDepthFromHere, bTotals.maxDepthFromHere, round)} - + {diffMetricLine('duration', aTotals.totalDuration, bTotals.totalDuration, n => `${n.toFixed(1)}s`)} - + {diffMetricLine('tokens', sumTokens(aTotals), sumTokens(bTotals), fmtTokens)} - {diffMetricLine('cost', aTotals.costUsd, bTotals.costUsd, dollars)} + {diffMetricLine('cost', aTotals.costUsd, bTotals.costUsd, dollars)} ) diff --git a/ui-tui/src/components/appChrome.tsx b/ui-tui/src/components/appChrome.tsx index 42015e11f4..06f870d206 100644 --- a/ui-tui/src/components/appChrome.tsx +++ b/ui-tui/src/components/appChrome.tsx @@ -15,8 +15,6 @@ import type { Theme } from '../theme.js' import type { Msg, Usage } from '../types.js' const FACE_TICK_MS = 2500 -const HEART_COLORS = ['#ff5fa2', '#ff4d6d'] - function FaceTicker({ color, startedAt }: { color: string; startedAt?: null | number }) { const [tick, setTick] = useState(() => Math.floor(Math.random() * 1000)) const [now, setNow] = useState(() => Date.now()) @@ -169,7 +167,7 @@ export function GoodVibesHeart({ tick, t }: { tick: number; t: Theme }) { return } - const palette = [...HEART_COLORS, t.color.amber] + const palette = [t.color.error, t.color.warn, t.color.amber] setColor(palette[Math.floor(Math.random() * palette.length)]!) setActive(true) diff --git a/ui-tui/src/components/appLayout.tsx b/ui-tui/src/components/appLayout.tsx index d8a9d0f5f2..8377629718 100644 --- a/ui-tui/src/components/appLayout.tsx +++ b/ui-tui/src/components/appLayout.tsx @@ -162,7 +162,7 @@ const ComposerPane = memo(function ComposerPane({ {i === 0 ? `${ui.theme.brand.prompt} ` : ' '} - {line || ' '} + {line || ' '} ))} @@ -262,6 +262,7 @@ export const AppLayout = memo(function AppLayout({ transcript }: AppLayoutProps) { const overlay = useStore($overlayState) + const ui = useStore($uiState) // Inline mode skips AlternateScreen so the host terminal's native // scrollback captures rows scrolled off the top; composer + progress @@ -302,7 +303,7 @@ export const AppLayout = memo(function AppLayout({ {SHOW_FPS && ( - + )} diff --git a/ui-tui/src/components/branding.tsx b/ui-tui/src/components/branding.tsx index d66cbdb622..97e69cac2d 100644 --- a/ui-tui/src/components/branding.tsx +++ b/ui-tui/src/components/branding.tsx @@ -77,7 +77,7 @@ export function SessionPanel({ info, sid, t }: SessionPanelProps) { {shown.map(([k, vs]) => ( {strip(k)}: - {truncLine(strip(k) + ': ', vs)} + {truncLine(strip(k) + ': ', vs)} ))} @@ -149,7 +149,7 @@ export function SessionPanel({ info, sid, t }: SessionPanelProps) { {`[${s.transport}]`} : {s.connected ? ( - + {s.tools} tool{s.tools === 1 ? '' : 's'} ) : ( @@ -162,7 +162,7 @@ export function SessionPanel({ info, sid, t }: SessionPanelProps) { - + {flat(info.tools).length} tools{' · '} {flat(info.skills).length} skills {info.mcp_servers?.length ? ` · ${info.mcp_servers.length} MCP` : ''} @@ -171,7 +171,7 @@ export function SessionPanel({ info, sid, t }: SessionPanelProps) { {learningLine && ( - + {learningLine} · /learned )} @@ -217,12 +217,12 @@ export function Panel({ sections, t, title }: PanelProps) { {sec.rows?.map(([k, v], ri) => ( {k.padEnd(20)} - {v} + {v} ))} {sec.items?.map((item, ii) => ( - + {item} ))} diff --git a/ui-tui/src/components/fpsOverlay.tsx b/ui-tui/src/components/fpsOverlay.tsx index f6fc748656..4400c9d44a 100644 --- a/ui-tui/src/components/fpsOverlay.tsx +++ b/ui-tui/src/components/fpsOverlay.tsx @@ -5,23 +5,25 @@ import { useStore } from '@nanostores/react' import { SHOW_FPS } from '../config/env.js' import { $fpsState } from '../lib/fpsStore.js' +import type { Theme } from '../theme.js' -const fpsColor = (fps: number) => (fps >= 50 ? 'green' : fps >= 30 ? 'yellow' : 'red') +const fpsColor = (fps: number, t: Theme) => + fps >= 50 ? t.color.statusGood : fps >= 30 ? t.color.statusWarn : t.color.error -export function FpsOverlay() { +export function FpsOverlay({ t }: { t: Theme }) { if (!SHOW_FPS) { return null } - return + return } -function FpsOverlayInner() { +function FpsOverlayInner({ t }: { t: Theme }) { const { fps, lastDurationMs, totalFrames } = useStore($fpsState) // Zero-pad widths so digit churn doesn't jitter the corner. return ( - + {fps.toFixed(1).padStart(5)}fps · {lastDurationMs.toFixed(1).padStart(5)}ms · #{totalFrames} ) diff --git a/ui-tui/src/components/learningLedger.tsx b/ui-tui/src/components/learningLedger.tsx index fb3550a942..6d0877a9cd 100644 --- a/ui-tui/src/components/learningLedger.tsx +++ b/ui-tui/src/components/learningLedger.tsx @@ -213,7 +213,7 @@ function LedgerDetails({ item, t, width }: LedgerDetailsProps) { {memoryLike ? item.name : item.summary} - {memoryLike ? {item.summary} : null} + {memoryLike ? {item.summary} : null} {item.count ? used: {item.count}× : null} {item.learned_from ? from: {item.learned_from} : null} {item.via ? via: {item.via} : null} diff --git a/ui-tui/src/components/prompts.tsx b/ui-tui/src/components/prompts.tsx index 1be68da178..be607389a1 100644 --- a/ui-tui/src/components/prompts.tsx +++ b/ui-tui/src/components/prompts.tsx @@ -48,7 +48,7 @@ export function ApprovalPrompt({ onChoice, req, t }: ApprovalPromptProps) { {shown.map((line, i) => ( - + {line || ' '} ))} @@ -85,7 +85,7 @@ export function ClarifyPrompt({ cols = 80, onAnswer, onCancel, req, t }: Clarify const heading = ( ask - {req.question} + {req.question} ) @@ -185,8 +185,8 @@ export function ConfirmPrompt({ onCancel, onConfirm, req, t }: ConfirmPromptProp const accent = req.danger ? t.color.error : t.color.warn const rows = [ - { color: t.color.cornsilk, label: req.cancelLabel ?? 'No' }, - { color: req.danger ? t.color.error : t.color.cornsilk, label: req.confirmLabel ?? 'Yes' } + { color: t.color.text, label: req.cancelLabel ?? 'No' }, + { color: req.danger ? t.color.error : t.color.text, label: req.confirmLabel ?? 'Yes' } ] return ( @@ -197,7 +197,7 @@ export function ConfirmPrompt({ onCancel, onConfirm, req, t }: ConfirmPromptProp {req.detail ? ( - + {req.detail} diff --git a/ui-tui/src/components/skillsHub.tsx b/ui-tui/src/components/skillsHub.tsx index 3284b145f5..bd9c84091f 100644 --- a/ui-tui/src/components/skillsHub.tsx +++ b/ui-tui/src/components/skillsHub.tsx @@ -283,7 +283,7 @@ export function SkillsHub({ gw, onClose, t }: SkillsHubProps) { {info?.category ?? selectedCat} - {info?.description ? {info.description} : null} + {info?.description ? {info.description} : null} {info?.path ? path: {info.path} : null} {!info && !err ? loading… : null} {err ? error: {err} : null} diff --git a/ui-tui/src/components/thinking.tsx b/ui-tui/src/components/thinking.tsx index 0c2b9549c8..73fa948370 100644 --- a/ui-tui/src/components/thinking.tsx +++ b/ui-tui/src/components/thinking.tsx @@ -460,7 +460,7 @@ function SubagentAccordion({ {item.tools.map((line, index) => ( @@ -792,7 +792,7 @@ export const ToolTrail = memo(function ToolTrail({ if (parsed) { groups.push({ - color: parsed.mark === '✗' ? t.color.error : t.color.cornsilk, + color: parsed.mark === '✗' ? t.color.error : t.color.text, content: parsed.call, details: [], key: `tr-${i}`, @@ -815,7 +815,7 @@ export const ToolTrail = memo(function ToolTrail({ const label = toolTrailLabel(line.slice(9).replace(/…$/, '').trim()) groups.push({ - color: t.color.cornsilk, + color: t.color.text, content: label, details: [{ color: t.color.dim, content: 'drafting...', dimColor: true, key: `tr-${i}-d` }], key: `tr-${i}`, @@ -849,7 +849,7 @@ export const ToolTrail = memo(function ToolTrail({ const label = formatToolCall(tool.name, tool.context || '') groups.push({ - color: t.color.cornsilk, + color: t.color.text, key: tool.id, label, details: [], @@ -1001,7 +1001,7 @@ export const ToolTrail = memo(function ToolTrail({ {openThinking ? '▾ ' : '▸ '} {thinkingLive ? ( - + Thinking ) : ( diff --git a/ui-tui/src/components/todoPanel.tsx b/ui-tui/src/components/todoPanel.tsx index 9480ee0af8..38caeffb93 100644 --- a/ui-tui/src/components/todoPanel.tsx +++ b/ui-tui/src/components/todoPanel.tsx @@ -9,7 +9,7 @@ import type { TodoItem } from '../types.js' const rowColor = (t: Theme, status: TodoItem['status']) => { const tone = todoTone(status) - return tone === 'active' ? t.color.cornsilk : tone === 'body' ? t.color.statusFg : t.color.dim + return tone === 'active' ? t.color.text : tone === 'body' ? t.color.statusFg : t.color.dim } export const TodoPanel = memo(function TodoPanel({ @@ -58,7 +58,7 @@ export const TodoPanel = memo(function TodoPanel({ {effectiveCollapsed ? '▸ ' : '▾ '} - + Todo {' '} diff --git a/ui-tui/src/domain/roles.ts b/ui-tui/src/domain/roles.ts index f92d175e65..6046d6355a 100644 --- a/ui-tui/src/domain/roles.ts +++ b/ui-tui/src/domain/roles.ts @@ -2,7 +2,7 @@ import type { Theme } from '../theme.js' import type { Role } from '../types.js' export const ROLE: Record { body: string; glyph: string; prefix: string }> = { - assistant: t => ({ body: t.color.cornsilk, glyph: t.brand.tool, prefix: t.color.bronze }), + assistant: t => ({ body: t.color.text, glyph: t.brand.tool, prefix: t.color.bronze }), system: t => ({ body: '', glyph: '·', prefix: t.color.dim }), tool: t => ({ body: t.color.dim, glyph: '⚡', prefix: t.color.dim }), user: t => ({ body: t.color.label, glyph: t.brand.prompt, prefix: t.color.label }) diff --git a/ui-tui/src/lib/syntax.ts b/ui-tui/src/lib/syntax.ts index 06173b63e9..dd72c6f6c1 100644 --- a/ui-tui/src/lib/syntax.ts +++ b/ui-tui/src/lib/syntax.ts @@ -99,7 +99,7 @@ export function highlightLine(line: string, lang: string, t: Theme): Token[] { if (ch === '"' || ch === "'" || ch === '`') { tokens.push([t.color.amber, tok]) } else if (ch >= '0' && ch <= '9') { - tokens.push([t.color.cornsilk, tok]) + tokens.push([t.color.text, tok]) } else if (spec.keywords.has(tok)) { tokens.push([t.color.bronze, tok]) } else { diff --git a/ui-tui/src/theme.ts b/ui-tui/src/theme.ts index daeedb3377..931d0fb15e 100644 --- a/ui-tui/src/theme.ts +++ b/ui-tui/src/theme.ts @@ -2,7 +2,7 @@ export interface ThemeColors { gold: string amber: string bronze: string - cornsilk: string + text: string dim: string completionBg: string completionCurrentBg: string @@ -93,7 +93,7 @@ export const DARK_THEME: Theme = { gold: '#FFD700', amber: '#FFBF00', bronze: '#CD7F32', - cornsilk: '#FFF8DC', + text: '#FFF8DC', // Bumped from the old `#B8860B` darkgoldenrod (~53% luminance) which // read as barely-visible on dark terminals for long body text. The // new value sits ~60% luminance — readable without losing the "muted / @@ -144,7 +144,7 @@ export const LIGHT_THEME: Theme = { gold: '#8B6914', amber: '#A0651C', bronze: '#7A4F1F', - cornsilk: '#3D2F13', + text: '#3D2F13', dim: '#7A5A0F', completionBg: '#F5F5F5', completionCurrentBg: mix('#F5F5F5', '#A0651C', 0.25), @@ -222,10 +222,10 @@ export function fromSkin( gold: c('banner_title') ?? d.color.gold, amber, bronze: c('banner_border') ?? d.color.bronze, - cornsilk: c('banner_text') ?? d.color.cornsilk, + text: c('ui_text') ?? c('banner_text') ?? d.color.text, dim, - completionBg: c('completion_menu_bg') ?? '#FFFFFF', - completionCurrentBg: c('completion_menu_current_bg') ?? mix('#FFFFFF', accent, 0.25), + completionBg: c('completion_menu_bg') ?? d.color.completionBg, + completionCurrentBg: c('completion_menu_current_bg') ?? mix(d.color.completionBg, accent, 0.25), label: c('ui_label') ?? d.color.label, ok: c('ui_ok') ?? d.color.ok,