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,