+
No semantic transcript yet
@@ -374,7 +374,7 @@ function RunGroup({ group, isLatest }: { group: Extract
-
+
{new Date(group.header.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
@@ -477,7 +477,7 @@ function RunStatusBadge({ status, detail }: { status: RunStatus; detail?: string
}[status]
return (
-
+
{label}{detail ? ` Β· ${detail}` : ''}
)
@@ -600,7 +600,7 @@ function CollapsibleBlock({ label, content, storageKey }: { label: string; conte
{ setOpen(!open) }}
- className="flex w-full items-center gap-2 px-2.5 py-1.5 text-left text-[11px] text-text-secondary hover:text-text-primary"
+ className="flex w-full items-center gap-2 px-2.5 py-1.5 text-left text-xs text-text-secondary hover:text-text-primary"
style={{ cursor: 'pointer' }}
>
βΊ
@@ -697,16 +697,16 @@ function ActionDisclosure({
βΆ
{title}
{status && (
-
+
{status}
)}
{timestamp && (
-
+
{new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
)}
- {hasDetail && details }
+ {hasDetail && details }
{open && hasDetail && (
@@ -809,7 +809,7 @@ function PendingSemanticRunEntry({
Cancel
diff --git a/src/components/panel/chat-flat.tsx b/src/components/panel/chat-flat.tsx
index 9857e676..25c13bb5 100644
--- a/src/components/panel/chat-flat.tsx
+++ b/src/components/panel/chat-flat.tsx
@@ -72,7 +72,7 @@ export function ChatEntry({
className={`border-l pl-3 ${TONE_BORDER[tone]}`}
>
{hasHeader && (
-
+
{label &&
{label} }
{detail &&
{detail} }
{time &&
{time} }
@@ -93,7 +93,7 @@ export function ChatDivider({ children, isLatest = false }: { children: ReactNod
className="flex items-center gap-3 py-0.5"
>
-
{children}
+
{children}
)
diff --git a/src/components/panel/chat-history.tsx b/src/components/panel/chat-history.tsx
index e47271e3..d8c8a732 100644
--- a/src/components/panel/chat-history.tsx
+++ b/src/components/panel/chat-history.tsx
@@ -303,7 +303,7 @@ function TypingDots() {
function QueuedEntry({ content }: { content: string }) {
return (
-
+
diff --git a/src/components/panel/file-browser.tsx b/src/components/panel/file-browser.tsx
index 7acd723f..5169da84 100644
--- a/src/components/panel/file-browser.tsx
+++ b/src/components/panel/file-browser.tsx
@@ -32,13 +32,13 @@ export function FileBrowser({ workspaceId, selectedPath, onSelectFile }: FileBro
}, [workspaceId])
if (error) {
- return {error}
+ return {error}
}
if (root === null) {
- return Loadingβ¦
+ return Loadingβ¦
}
if (root.length === 0) {
- return Empty repository
+ return Empty repository
}
return (
@@ -83,7 +83,7 @@ function Row({
type="button"
onClick={onClick}
style={{ paddingLeft: 8 + depth * 12, cursor: 'pointer' }}
- className={`flex w-full items-center gap-1.5 py-0.5 pr-2 text-left text-[11px] transition-colors ${
+ className={`flex w-full items-center gap-1.5 py-0.5 pr-2 text-left text-xs transition-colors ${
active
? 'bg-surface-hover text-text-primary'
: 'text-text-secondary hover:bg-surface-hover hover:text-text-primary'
@@ -146,7 +146,7 @@ function DirNode({
{open && (
{loading && children === null && (
-
+
β¦
)}
diff --git a/src/components/panel/file-preview.tsx b/src/components/panel/file-preview.tsx
index 13923e0b..fa4df8aa 100644
--- a/src/components/panel/file-preview.tsx
+++ b/src/components/panel/file-preview.tsx
@@ -63,7 +63,7 @@ export function FilePreview({ workspaceId, file, onClose }: FilePreviewProps) {
{ void openInEditor(workspaceId, file.path).catch(() => {}) }}
- className="flex items-center gap-1 rounded px-1.5 py-1 text-[11px] text-text-secondary transition-colors hover:bg-surface-hover hover:text-text-primary"
+ className="flex items-center gap-1 rounded px-1.5 py-1 text-xs text-text-secondary transition-colors hover:bg-surface-hover hover:text-text-primary"
title="Open in editor (VS Code)"
aria-label="Open in editor"
style={{ cursor: 'pointer' }}
diff --git a/src/components/panel/files-tree.tsx b/src/components/panel/files-tree.tsx
index a222ca04..4c6cf5ee 100644
--- a/src/components/panel/files-tree.tsx
+++ b/src/components/panel/files-tree.tsx
@@ -141,7 +141,7 @@ export function FilesTree({ groupedFiles, selectedFile, onSelectFile, loading }:
{file.name}
- {formatDate(file.modifiedAt)}
+ {formatDate(file.modifiedAt)}
)
diff --git a/src/components/panel/interactive-agent-view.tsx b/src/components/panel/interactive-agent-view.tsx
index 43d77e60..e63e96ed 100644
--- a/src/components/panel/interactive-agent-view.tsx
+++ b/src/components/panel/interactive-agent-view.tsx
@@ -250,7 +250,7 @@ export function InteractiveAgentView({
/>
{error && (
{error}
@@ -261,7 +261,7 @@ export function InteractiveAgentView({
data-testid="interactive-agent-done-banner"
className="flex items-center justify-between gap-3 border-b border-running/30 bg-running/10 px-3 py-1.5"
>
-
+
β Agent signaled it's done. The session is still live β keep chatting, or advance the column when you're satisfied.
@@ -269,7 +269,7 @@ export function InteractiveAgentView({
type="button"
onClick={() => { setDoneSignaled(false) }}
data-testid="interactive-agent-done-dismiss"
- className="rounded px-2 py-0.5 text-[11px] text-text-secondary hover:text-text-primary"
+ className="rounded px-2 py-0.5 text-xs text-text-secondary hover:text-text-primary"
style={{ cursor: 'pointer' }}
>
Dismiss
@@ -280,7 +280,7 @@ export function InteractiveAgentView({
disabled={advancing}
data-testid="interactive-agent-advance"
title="Mark complete and run the column's exit/advance"
- className="rounded border border-running/40 bg-running/15 px-2 py-0.5 text-[11px] font-medium text-running hover:bg-running/25 disabled:opacity-50"
+ className="rounded border border-running/40 bg-running/15 px-2 py-0.5 text-xs font-medium text-running hover:bg-running/25 disabled:opacity-50"
style={{ cursor: advancing ? 'wait' : 'pointer' }}
>
{advancing ? 'Advancingβ¦' : 'Advance column β'}
@@ -345,7 +345,7 @@ function ControlBar({
- Interactive
+ Interactive
{isPaused ? 'Resume' : 'Pause'}
@@ -396,7 +396,7 @@ function ControlBar({
disabled={interrupting || !sessionAvailable}
data-testid="interactive-agent-interrupt"
title={sessionAvailable ? 'Send Ctrl+C β aborts current generation, agent stays at prompt' : 'Agent session is stopped'}
- className="rounded border border-border-default bg-surface px-2 py-0.5 text-[11px] font-medium text-text-secondary hover:bg-surface-hover hover:text-text-primary disabled:opacity-50"
+ className="rounded border border-border-default bg-surface px-2 py-0.5 text-xs font-medium text-text-secondary hover:bg-surface-hover hover:text-text-primary disabled:opacity-50"
style={{ cursor: interrupting ? 'wait' : sessionAvailable ? 'pointer' : 'not-allowed' }}
>
Interrupt
@@ -407,7 +407,7 @@ function ControlBar({
disabled={restarting}
data-testid="interactive-agent-restart"
title="Kill the agent and respawn with the same trigger config"
- className="rounded border border-warning/40 bg-surface px-2 py-0.5 text-[11px] font-medium text-warning hover:bg-warning/10 disabled:opacity-50"
+ className="rounded border border-warning/40 bg-surface px-2 py-0.5 text-xs font-medium text-warning hover:bg-warning/10 disabled:opacity-50"
style={{ cursor: restarting ? 'wait' : 'pointer' }}
>
Restart
@@ -442,7 +442,7 @@ function ModelDropdown({
? 'Wait for the agent to finish responding before switching models'
: 'Send /model to the live agent'
}
- className="rounded border border-border-default bg-bg px-1.5 py-0.5 text-[11px] text-text-primary disabled:opacity-50"
+ className="rounded border border-border-default bg-bg px-1.5 py-0.5 text-xs text-text-primary disabled:opacity-50"
>
Model: pickβ¦
{MODEL_OPTIONS.map((m) => (
diff --git a/src/components/panel/orchestrator-terminal-view.tsx b/src/components/panel/orchestrator-terminal-view.tsx
index 79b5d158..71c84f1c 100644
--- a/src/components/panel/orchestrator-terminal-view.tsx
+++ b/src/components/panel/orchestrator-terminal-view.tsx
@@ -46,7 +46,7 @@ export function OrchestratorTerminalView({
return (
{task.title}
- {elapsed}
+ {elapsed}
- {col?.name ?? 'Unknown'}
- {label && {label} }
+ {col?.name ?? 'Unknown'}
+ {label && {label} }
{/* Progress bar */}
@@ -166,11 +166,11 @@ export function PipelineDashboard({ workspaceId }: PipelineDashboardProps) {
>
{task.title}
- {formatRelativeTime(task.updatedAt)}
+ {formatRelativeTime(task.updatedAt)}
-
{col?.name ?? 'Unknown'}
+
{col?.name ?? 'Unknown'}
{task.pipelineError && (
-
{task.pipelineError}
+
{task.pipelineError}
)}
)
@@ -191,13 +191,13 @@ export function PipelineDashboard({ workspaceId }: PipelineDashboardProps) {
>
{task.title}
- {formatRelativeTime(task.updatedAt)}
+ {formatRelativeTime(task.updatedAt)}
{prUrl && (
{ window.open(prUrl, '_blank') }}
- className="mt-0.5 text-[10px] text-accent hover:underline"
+ className="mt-0.5 text-xs text-accent hover:underline"
style={{ cursor: 'pointer' }}
>
{task.prNumber ? `PR #${String(task.prNumber)}` : 'View PR'}
@@ -222,7 +222,7 @@ export function PipelineDashboard({ workspaceId }: PipelineDashboardProps) {
function Section({ title, children }: { title: string; children: React.ReactNode }) {
return (
-
{title}
+
{title}
{children}
)
diff --git a/src/components/panel/pipeline-v2-dashboard.tsx b/src/components/panel/pipeline-v2-dashboard.tsx
index f766898f..e817c55d 100644
--- a/src/components/panel/pipeline-v2-dashboard.tsx
+++ b/src/components/panel/pipeline-v2-dashboard.tsx
@@ -103,7 +103,7 @@ export function PipelineV2Dashboard({ workspaceId }: PipelineV2DashboardProps) {
{/* Summary stats */}
-
Pipeline Status
+
Pipeline Status
{colCounts.size > 0 && (
@@ -135,15 +135,15 @@ export function PipelineV2Dashboard({ workspaceId }: PipelineV2DashboardProps) {
const eta = computeTaskEta(task, sortedColumns, avgCompletionMs)
return (
- {task.title}
-
+ {task.title}
+
{eta > 0 ? `~${formatMs(eta)}` : 'soon'}
)
})}
{activeTasks.length > 6 && (
-
+{activeTasks.length - 6} more
+
+{activeTasks.length - 6} more
)}
)}
@@ -153,10 +153,10 @@ export function PipelineV2Dashboard({ workspaceId }: PipelineV2DashboardProps) {
{Array.from(batchGroups.entries()).map(([batchId, batchTasks]) => (
-
+
{batchId === 'ungrouped' ? 'No batch' : batchId.slice(0, 12)}
-
+
{batchTasks.length} {batchTasks.length === 1 ? 'task' : 'tasks'}
@@ -168,16 +168,16 @@ export function PipelineV2Dashboard({ workspaceId }: PipelineV2DashboardProps) {
{usageSummary && usageSummary.recordCount > 0 && (
- Input tokens
- {formatTokens(usageSummary.totalInputTokens)}
+ Input tokens
+ {formatTokens(usageSummary.totalInputTokens)}
- Output tokens
- {formatTokens(usageSummary.totalOutputTokens)}
+ Output tokens
+ {formatTokens(usageSummary.totalOutputTokens)}
- Sessions
- {usageSummary.recordCount}
+ Sessions
+ {usageSummary.recordCount}
)}
@@ -233,7 +233,7 @@ function Stat({ label, value }: { label: string; value: string }) {
return (
{value}
- {label}
+ {label}
)
}
@@ -241,14 +241,14 @@ function Stat({ label, value }: { label: string; value: string }) {
function ColumnBar({ column, count, pct }: { column: Column; count: number; pct: number }) {
return (
-
{column.name}
+
{column.name}
-
{count}
+
{count}
)
}
@@ -256,7 +256,7 @@ function ColumnBar({ column, count, pct }: { column: Column; count: number; pct:
function Section({ title, children }: { title: string; children: React.ReactNode }) {
return (
-
{title}
+
{title}
{children}
)
diff --git a/src/components/panel/shared/attachment-button.tsx b/src/components/panel/shared/attachment-button.tsx
index 5572959d..9a13dc9e 100644
--- a/src/components/panel/shared/attachment-button.tsx
+++ b/src/components/panel/shared/attachment-button.tsx
@@ -46,7 +46,7 @@ export function AttachmentButton({
)}
{count > 0 && (
-
+
{count}
)}
diff --git a/src/components/panel/shared/attachment-preview.tsx b/src/components/panel/shared/attachment-preview.tsx
index 25280df0..0340ad11 100644
--- a/src/components/panel/shared/attachment-preview.tsx
+++ b/src/components/panel/shared/attachment-preview.tsx
@@ -64,7 +64,7 @@ function AttachmentItem({ attachment, onRemove, disabled }: AttachmentItemProps)
{attachment.name}
-
+
{formatFileSize(attachment.size)}
diff --git a/src/components/panel/shared/chat-input.tsx b/src/components/panel/shared/chat-input.tsx
index 42f0712c..8377cda9 100644
--- a/src/components/panel/shared/chat-input.tsx
+++ b/src/components/panel/shared/chat-input.tsx
@@ -72,7 +72,7 @@ export function ChatInput({
/>
)}
{config.showModelSelector && (
-
+
{state.currentContextWindow}
)}
@@ -92,7 +92,7 @@ export function ChatInput({
)}
{deliveryHint && (
-
+
{deliveryHint}
)}
{queueCount > 0 && (
-
+
{queueCount} queued
)}
diff --git a/src/components/panel/workspace-files-view.tsx b/src/components/panel/workspace-files-view.tsx
index 52936884..d467b5c3 100644
--- a/src/components/panel/workspace-files-view.tsx
+++ b/src/components/panel/workspace-files-view.tsx
@@ -43,7 +43,7 @@ export function WorkspaceFilesView({ workspaceId }: { workspaceId: string }) {
onClick={() => { setMode(m) }}
aria-pressed={mode === m}
data-testid={`files-mode-${m}`}
- className={`rounded px-1.5 py-0.5 text-[10px] font-medium transition-colors ${
+ className={`rounded px-1.5 py-0.5 text-xs font-medium transition-colors ${
mode === m
? 'bg-surface-hover text-text-primary'
: 'text-text-secondary hover:text-text-primary'
diff --git a/src/components/review/diff-viewer.tsx b/src/components/review/diff-viewer.tsx
index d480211d..225775fe 100644
--- a/src/components/review/diff-viewer.tsx
+++ b/src/components/review/diff-viewer.tsx
@@ -246,7 +246,7 @@ function ActionButton({
title={title}
data-diff-action="true"
style={{ cursor: disabled ? 'not-allowed' : 'pointer' }}
- className="rounded border border-border-default/80 bg-surface px-1.5 py-0.5 text-[10px] font-medium text-text-secondary hover:bg-surface-hover hover:text-text-primary disabled:opacity-50"
+ className="rounded border border-border-default/80 bg-surface px-1.5 py-0.5 text-xs font-medium text-text-secondary hover:bg-surface-hover hover:text-text-primary disabled:opacity-50"
>
{children}
@@ -712,7 +712,7 @@ export function DiffViewer({
{selectable && (
-
+
{selectedLineKeys.size > 0
? `${String(selectedLineKeys.size)} line${selectedLineKeys.size === 1 ? '' : 's'} selected`
: 'Select diff lines to copy or send context'}
diff --git a/src/components/roster/agent-dossier.tsx b/src/components/roster/agent-dossier.tsx
index 6507e984..64c0c6df 100644
--- a/src/components/roster/agent-dossier.tsx
+++ b/src/components/roster/agent-dossier.tsx
@@ -40,12 +40,12 @@ function Section({
{title}
{count !== undefined && count > 0 && (
-
+
{count}
)}
{v2 && (
-
+
v2
)}
@@ -64,7 +64,7 @@ function CodeValue({ value, copyable = false }: { value: string; copyable?: bool
if (!copyable) {
return (
-
+
{value}
)
@@ -88,7 +88,7 @@ function CodeValue({ value, copyable = false }: { value: string; copyable?: bool
}}
title="Copy"
style={{ cursor: 'pointer' }}
- className="group inline-flex max-w-full items-center gap-1.5 rounded border border-border-default bg-bg px-1.5 py-0.5 font-mono text-[11px] text-text-primary transition-colors hover:border-text-secondary"
+ className="group inline-flex max-w-full items-center gap-1.5 rounded border border-border-default bg-bg px-1.5 py-0.5 font-mono text-xs text-text-primary transition-colors hover:border-text-secondary"
>
{value}
@@ -128,7 +128,7 @@ function Field({
return (
{label}
diff --git a/src/components/roster/agent-editor.tsx b/src/components/roster/agent-editor.tsx
index 9523c2c3..38fcb76e 100644
--- a/src/components/roster/agent-editor.tsx
+++ b/src/components/roster/agent-editor.tsx
@@ -67,7 +67,7 @@ function Field({
function FormSection({ title }: { title: string }) {
return (
-
+
{title}
diff --git a/src/components/roster/agent-tile.tsx b/src/components/roster/agent-tile.tsx
index 4a751337..7ed61e01 100644
--- a/src/components/roster/agent-tile.tsx
+++ b/src/components/roster/agent-tile.tsx
@@ -47,14 +47,14 @@ export function AgentTile({
{avatar.initials}
{selected && (
-
+
Selected
)}
{agent.name}
-
diff --git a/src/components/settings/settings-panel.tsx b/src/components/settings/settings-panel.tsx
index 8f0822a5..80817a10 100644
--- a/src/components/settings/settings-panel.tsx
+++ b/src/components/settings/settings-panel.tsx
@@ -343,7 +343,7 @@ export function SettingsPanel() {
{TAB_GROUPS.map((group) => (
-
+
{group.label}
{group.tabs.map((tab) => (
diff --git a/src/components/settings/tabs/agent-tab.tsx b/src/components/settings/tabs/agent-tab.tsx
index 758dc94e..2ca09bc3 100644
--- a/src/components/settings/tabs/agent-tab.tsx
+++ b/src/components/settings/tabs/agent-tab.tsx
@@ -345,7 +345,7 @@ export function AgentTab() {
{info.name}
{providerModelCount > 0 && (
-
+
{providerModelCount}
)}
@@ -416,7 +416,7 @@ export function AgentTab() {
{supportsApi ? 'API' : 'API unavailable'}
-
+
{connectionMode === 'cli'
? 'Uses the installed CLI (auth and billing handled by the CLI).'
: supportsApi
@@ -460,7 +460,7 @@ export function AgentTab() {
const isChecking = checkingUpdate[provider.id]
const updateCommand = update?.updateCommand
return (
-
+
{isChecking ? (
@@ -483,7 +483,7 @@ export function AgentTab() {
setCopiedCmd(provider.id)
setTimeout(() => { setCopiedCmd(null) }, 2000)
}}
- className="ml-0.5 rounded border border-yellow-500/30 px-1 py-0.5 text-[10px] text-yellow-400 transition-colors hover:bg-yellow-500/10"
+ className="ml-0.5 rounded border border-yellow-500/30 px-1 py-0.5 text-xs text-yellow-400 transition-colors hover:bg-yellow-500/10"
title={updateCommand}
>
{copiedCmd === provider.id ? 'β copied' : 'copy upgrade cmd'}
@@ -547,7 +547,7 @@ export function AgentTab() {
{ toggleAll(!allEnabled) }}
- className="text-[10px] text-text-secondary transition-colors hover:text-text-primary"
+ className="text-xs text-text-secondary transition-colors hover:text-text-primary"
>
{allEnabled ? 'Deselect all' : 'Select all'}
@@ -582,17 +582,17 @@ export function AgentTab() {
{m.displayName}
{m.alias && (
-
+
{m.alias}
)}
{m.isNew && (
-
+
New
)}
-
+
{String(Math.round(m.contextWindow / 1000))}k
@@ -631,7 +631,7 @@ export function AgentTab() {
{item.name}
-
+
Coming soon
@@ -818,7 +818,7 @@ export function AgentTab() {
className="w-24 rounded-lg border border-border-default bg-surface px-2 py-1.5 text-right text-sm tabular-nums text-text-primary placeholder:text-text-secondary/50 focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/20"
aria-label={`${entry.displayName} daily budget`}
/>
-
/day
+
/day
)
@@ -843,7 +843,7 @@ function StatusTile({ label, value, tone }: { label: string; value: string; tone
: 'border-border-default bg-surface/50 text-text-primary'
return (
-
{label}
+
{label}
{value}
)
@@ -881,7 +881,7 @@ function PermissionCard({
{label}
{tag && (
-
+
{tag}
)}
diff --git a/src/components/settings/tabs/debug-tab.tsx b/src/components/settings/tabs/debug-tab.tsx
index 735d1e47..4bb0b819 100644
--- a/src/components/settings/tabs/debug-tab.tsx
+++ b/src/components/settings/tabs/debug-tab.tsx
@@ -82,7 +82,7 @@ function flagValue(args: string[], flag: string): string | null {
function Field({ label, children }: { label: string; children: React.ReactNode }) {
return (
- {label}
+ {label}
{children}
)
@@ -90,7 +90,7 @@ function Field({ label, children }: { label: string; children: React.ReactNode }
function Mono({ children }: { children: React.ReactNode }) {
return (
-
+
{children}
)
@@ -99,38 +99,38 @@ function Mono({ children }: { children: React.ReactNode }) {
function EventRow({ ev }: { ev: ParsedEvent }) {
switch (ev.kind) {
case 'system':
- return β {ev.text}
+ return β {ev.text}
case 'text':
return (
-
+
assistant
{ev.text}
)
case 'tool_use':
return (
-
+
π§ {ev.name}
{JSON.stringify(ev.input)}
)
case 'tool_result':
return (
-
+
β result
{ev.text.length > 600 ? `${ev.text.slice(0, 600)}β¦` : ev.text}
)
case 'result':
return (
-
+
β result
{ev.meta &&
{ev.meta} }
{ev.text &&
{ev.text}
}
)
case 'raw':
- return
{ev.text}
+ return
{ev.text}
}
}
@@ -158,30 +158,30 @@ function TurnCard({ turn }: { turn: ChatDebugTurn }) {
>
βΆ
{cli}
- {model &&
{model} }
- {mcpConfig &&
mcp }
- {resume &&
resume }
-
{events.length} events Β· {time}
+ {model &&
{model} }
+ {mcpConfig &&
mcp }
+ {resume &&
resume }
+
{events.length} events Β· {time}
{open && (
{/* SENT */}
-
β Sent
+
β Sent
-
+
{turn.command} {turn.args.filter((a) => a !== systemPrompt).join(' ')}
{allowedTools && (
- {allowedTools}
+ {allowedTools}
)}
{systemPrompt && (
- { setShowSys((v) => !v) }} className="self-start text-[11px] text-accent" style={{ cursor: 'pointer' }}>
+ { setShowSys((v) => !v) }} className="self-start text-xs text-accent" style={{ cursor: 'pointer' }}>
{showSys ? 'hide' : 'show'}
{showSys && {systemPrompt} }
@@ -195,14 +195,14 @@ function TurnCard({ turn }: { turn: ChatDebugTurn }) {
{/* RECEIVED */}
-
β Received
-
{ setShowRaw((v) => !v) }} className="text-[10px] text-text-secondary/70 hover:text-text-primary" style={{ cursor: 'pointer' }}>
+ β Received
+ { setShowRaw((v) => !v) }} className="text-xs text-text-secondary/70 hover:text-text-primary" style={{ cursor: 'pointer' }}>
{showRaw ? 'parsed' : 'raw'}
- {turn.outputTruncated && (truncated) }
+ {turn.outputTruncated && (truncated) }
{turn.output.length === 0 ? (
-
(no output captured)
+
(no output captured)
) : showRaw ? (
{turn.output.join('\n')}
) : (
diff --git a/src/components/settings/tabs/git-tab.tsx b/src/components/settings/tabs/git-tab.tsx
index 2162a8dd..49777ca0 100644
--- a/src/components/settings/tabs/git-tab.tsx
+++ b/src/components/settings/tabs/git-tab.tsx
@@ -55,7 +55,7 @@ export function GitTab() {
-
+
Coming Soon
@@ -75,7 +75,7 @@ export function GitTab() {
-
+
Coming Soon
diff --git a/src/components/settings/tabs/model-comparison-section.tsx b/src/components/settings/tabs/model-comparison-section.tsx
index c668bfdc..dd0d0228 100644
--- a/src/components/settings/tabs/model-comparison-section.tsx
+++ b/src/components/settings/tabs/model-comparison-section.tsx
@@ -168,7 +168,7 @@ export function ModelComparisonSection({ models }: Props) {
{metadata.displayName}
{metadata.id}
@@ -261,7 +261,7 @@ function CapabilityBadges({ capabilities }: { capabilities: string[] }) {
{capabilities.map((capability) => (
{capability}
diff --git a/src/components/settings/tabs/script-editor.tsx b/src/components/settings/tabs/script-editor.tsx
index f992c0bd..4d57722e 100644
--- a/src/components/settings/tabs/script-editor.tsx
+++ b/src/components/settings/tabs/script-editor.tsx
@@ -29,7 +29,7 @@ function TemplateVarChip({ varKey, label, hint }: { varKey: string; label: strin
type="button"
onClick={handleCopy}
title={`${hint} β click to copy`}
- className="group flex items-center gap-1 rounded bg-surface px-1.5 py-0.5 font-mono text-[10px] text-text-secondary transition-colors hover:bg-accent/10 hover:text-accent"
+ className="group flex items-center gap-1 rounded bg-surface px-1.5 py-0.5 font-mono text-xs text-text-secondary transition-colors hover:bg-accent/10 hover:text-accent"
>
{copied ? (
β copied
@@ -200,7 +200,7 @@ export function ScriptEditor({ script, onSave, onCancel }: ScriptEditorProps) {
{steps.length === 0 ? (
No steps yet
-
+
Add a Bash command, Agent prompt, or Check assertion below.
@@ -249,7 +249,7 @@ export function ScriptEditor({ script, onSave, onCancel }: ScriptEditorProps) {
{/* Template variables */}
-
+
Template Variables β click to copy
@@ -311,10 +311,10 @@ function StepEditor({
{/* Step header */}
-
+
{step.type}
- Step {index + 1}
+ Step {index + 1}
{script.name}
{script.isBuiltIn && (
-
+
built-in
)}
@@ -328,7 +328,7 @@ function ScriptCard({
{showAttach && (
-
+
Attach as on_entry trigger
@@ -353,15 +353,15 @@ function ScriptCard({
className="flex w-full items-center justify-between px-3 py-1.5 text-left text-xs text-text-primary hover:bg-surface-hover"
>
- {col.icon && {col.icon} }
+ {col.icon && {col.icon} }
{col.name}
{isAttachedHere ? (
-
+
attached
) : hasOtherEntry ? (
-
+
has trigger
) : null}
@@ -408,7 +408,7 @@ function ScriptCard({
{/* Attach status feedback */}
{attachStatus && (
-
+
{attachStatus}
)}
@@ -420,19 +420,19 @@ function ScriptCard({
steps.map((step, i) => (
{step.name || step.type}
))
) : (
- no steps
+ no steps
)}
{/* Show which columns have this script attached */}
{attachedColumns.length > 0 && (
-
+
diff --git a/src/components/settings/tabs/shortcuts-tab.tsx b/src/components/settings/tabs/shortcuts-tab.tsx
index c5c32130..ac8bf21c 100644
--- a/src/components/settings/tabs/shortcuts-tab.tsx
+++ b/src/components/settings/tabs/shortcuts-tab.tsx
@@ -50,7 +50,7 @@ export function ShortcutsTab() {
- Coming Soon
+ Coming Soon
Custom key bindings and conflict detection are not yet active. Shortcuts shown are the current defaults.
diff --git a/src/components/settings/tabs/templates-tab.tsx b/src/components/settings/tabs/templates-tab.tsx
index a0f6cfee..41ca0616 100644
--- a/src/components/settings/tabs/templates-tab.tsx
+++ b/src/components/settings/tabs/templates-tab.tsx
@@ -104,12 +104,12 @@ export function TemplatesTab() {
{template.name}
{template.isBuiltIn && (
-
+
Built-in
)}
{defaultTemplateId === template.id && (
-
+
Default
)}
@@ -173,7 +173,7 @@ export function TemplatesTab() {
{col.icon}
{col.name}
{col.autoAdvance && (
- β
+ β
)}
))}
diff --git a/src/components/settings/tabs/voice-tab.tsx b/src/components/settings/tabs/voice-tab.tsx
index 865c5239..67886e5d 100644
--- a/src/components/settings/tabs/voice-tab.tsx
+++ b/src/components/settings/tabs/voice-tab.tsx
@@ -64,12 +64,12 @@ function ModelCard({
{model.model}
{model.sizeDisplay}
{isDownloaded && (
-
+
Downloaded
)}
{isSelected && isDownloaded && (
-
+
Active
)}
diff --git a/src/components/settings/tabs/workspace-tab.tsx b/src/components/settings/tabs/workspace-tab.tsx
index 7ad67a06..4931d109 100644
--- a/src/components/settings/tabs/workspace-tab.tsx
+++ b/src/components/settings/tabs/workspace-tab.tsx
@@ -147,7 +147,7 @@ export function WorkspaceTab() {
-
Name
+
Name
{workspace.name}
@@ -155,7 +155,7 @@ export function WorkspaceTab() {
-
Repository path
+
Repository path
-
+
Created {new Date(workspace.createdAt).toLocaleDateString()}
Updated {new Date(workspace.updatedAt).toLocaleDateString()}
diff --git a/src/components/shared/empty-state.tsx b/src/components/shared/empty-state.tsx
index 76bc07ab..05ccc35c 100644
--- a/src/components/shared/empty-state.tsx
+++ b/src/components/shared/empty-state.tsx
@@ -18,7 +18,7 @@ const sizeStyles: Record
({
title={showIconsOnly ? tab.label : undefined}
data-testid={tab.testId}
onClick={() => { onChange(tab.value) }}
- className={`relative inline-flex min-w-0 items-center gap-1.5 py-1.5 text-[11px] font-medium transition-colors ${
+ className={`relative inline-flex min-w-0 items-center gap-1.5 py-1.5 text-xs font-medium transition-colors ${
showIconsOnly ? 'px-1.5' : 'px-2'
} ${active ? 'text-text-primary' : 'text-text-secondary hover:text-text-primary'}`}
style={{ cursor: 'pointer' }}
diff --git a/src/components/shared/selector-dropdown.tsx b/src/components/shared/selector-dropdown.tsx
index e1f715a1..0a597acf 100644
--- a/src/components/shared/selector-dropdown.tsx
+++ b/src/components/shared/selector-dropdown.tsx
@@ -48,7 +48,7 @@ export function SelectorDropdown({
className={`absolute bottom-full left-0 z-50 mb-1 ${width} overflow-hidden rounded-md border border-border-default bg-surface shadow-lg`}
>
{header && (
-
+
{header}
)}
@@ -91,7 +91,7 @@ export function SelectorOption({
{label}
{description && (
-
{description}
+
{description}
)}
{selected && (
@@ -124,7 +124,7 @@ export function SelectorButton({
{children}
diff --git a/src/components/shared/status-badge.tsx b/src/components/shared/status-badge.tsx
index 061e91d3..26fcb635 100644
--- a/src/components/shared/status-badge.tsx
+++ b/src/components/shared/status-badge.tsx
@@ -14,7 +14,7 @@ type StatusBadgeProps = {
const sizeStyles = {
sm: {
- container: 'px-1.5 py-0.5 text-[10px]',
+ container: 'px-1.5 py-0.5 text-xs',
dot: 'h-1.5 w-1.5',
},
md: {
diff --git a/src/components/shared/thinking-selector.tsx b/src/components/shared/thinking-selector.tsx
index 17086461..d9d2db75 100644
--- a/src/components/shared/thinking-selector.tsx
+++ b/src/components/shared/thinking-selector.tsx
@@ -66,7 +66,7 @@ export function ThinkingSelector({ value, maxLevel, onChange }: ThinkingSelector
>
{level.label}
-
{level.description}
+
{level.description}
{level.id === value && !disabled && (
diff --git a/src/components/shortcuts-modal.tsx b/src/components/shortcuts-modal.tsx
index b6cc1d46..0fb9e0aa 100644
--- a/src/components/shortcuts-modal.tsx
+++ b/src/components/shortcuts-modal.tsx
@@ -80,11 +80,11 @@ function KbdSequence({ keys }: { keys: readonly string[] }) {
{keys.map((key, index) => (
-
+
{key}
{index < keys.length - 1 && (
- +
+ +
)}
))}
@@ -153,7 +153,7 @@ export function ShortcutsModal({ onClose }: Props) {
{item.altKeys && (
<>
-
or
+
or
>
)}
diff --git a/src/components/task-detail/commits-section.tsx b/src/components/task-detail/commits-section.tsx
index 2afcb88c..0e6fda08 100644
--- a/src/components/task-detail/commits-section.tsx
+++ b/src/components/task-detail/commits-section.tsx
@@ -35,7 +35,7 @@ export function CommitsSection({ commits }: CommitsSectionProps) {
key={commit.hash}
className="flex items-start gap-2 rounded px-2 py-1 text-xs hover:bg-surface-hover"
>
-
+
{commit.shortHash}
@@ -47,7 +47,7 @@ export function CommitsSection({ commits }: CommitsSectionProps) {
{ setExpanded((current) => !current) }}
- className="flex w-full items-center justify-center rounded px-2 py-1 text-[11px] text-text-secondary transition-colors hover:bg-surface-hover hover:text-text-primary"
+ className="flex w-full items-center justify-center rounded px-2 py-1 text-xs text-text-secondary transition-colors hover:bg-surface-hover hover:text-text-primary"
style={{ cursor: 'pointer' }}
>
{expanded ? 'Show less' : `Show ${String(hiddenCommitCount)} more`}
diff --git a/src/components/task-detail/diff-section.tsx b/src/components/task-detail/diff-section.tsx
index cbef89d3..109d9d1c 100644
--- a/src/components/task-detail/diff-section.tsx
+++ b/src/components/task-detail/diff-section.tsx
@@ -117,7 +117,7 @@ export function DiffSection({
+{changes.totalAdditions}
-{changes.totalDeletions}
{referenceLabel && (
-
+
{referenceLabel}
)}
@@ -125,7 +125,7 @@ export function DiffSection({
{ void toggleAll() }}
- className="text-[11px] text-text-secondary hover:text-text-primary underline-offset-2 hover:underline"
+ className="text-xs text-text-secondary hover:text-text-primary underline-offset-2 hover:underline"
style={{ cursor: 'pointer' }}
>
{showAll ? 'Hide combined diff' : 'View all'}
@@ -148,7 +148,7 @@ export function DiffSection({
>
{ setFilesExpanded((current) => !current) }}
- className="mt-1 flex w-full items-center justify-center rounded px-2 py-1 text-[11px] text-text-secondary transition-colors hover:bg-surface-hover hover:text-text-primary"
+ className="mt-1 flex w-full items-center justify-center rounded px-2 py-1 text-xs text-text-secondary transition-colors hover:bg-surface-hover hover:text-text-primary"
style={{ cursor: 'pointer' }}
>
{filesExpanded ? 'Show less' : `Show ${String(hiddenFileCount)} more`}
@@ -216,7 +216,7 @@ function StatusIcon({ status }: { status: string }) {
const letter = status[0]?.toUpperCase() ?? '?'
return (
-
+
{letter}
)
diff --git a/src/components/task-detail/notification-section.tsx b/src/components/task-detail/notification-section.tsx
index e2da925b..26ad23e3 100644
--- a/src/components/task-detail/notification-section.tsx
+++ b/src/components/task-detail/notification-section.tsx
@@ -100,13 +100,13 @@ export function NotificationSection({
return (
-
+
Notify
{ setIsEditing(!isEditing); }}
- className="text-[10px] text-accent hover:text-accent-hover"
+ className="text-xs text-accent hover:text-accent-hover"
>
{isEditing ? 'Done' : 'Edit'}
@@ -167,7 +167,7 @@ export function NotificationSection({
type="button"
onClick={() => void handleAddStakeholder()}
disabled={isLoading || !input.trim()}
- className="rounded bg-accent px-2 py-1 text-[10px] font-medium text-white hover:bg-accent-hover disabled:opacity-50"
+ className="rounded bg-accent px-2 py-1 text-xs font-medium text-white hover:bg-accent-hover disabled:opacity-50"
>
Add
@@ -179,8 +179,8 @@ export function NotificationSection({
{notificationSentAt ? (
- Notified
-
+ Notified
+
{formatDateWithTime(notificationSentAt)}
@@ -188,7 +188,7 @@ export function NotificationSection({
type="button"
onClick={() => void handleClearNotified()}
disabled={isLoading}
- className="text-[10px] text-text-secondary hover:text-text-primary disabled:opacity-50"
+ className="text-xs text-text-secondary hover:text-text-primary disabled:opacity-50"
>
Clear
diff --git a/src/components/task-detail/siege-status.tsx b/src/components/task-detail/siege-status.tsx
index 413be39d..e6cc8fbb 100644
--- a/src/components/task-detail/siege-status.tsx
+++ b/src/components/task-detail/siege-status.tsx
@@ -57,14 +57,14 @@ export function SiegeStatus({ task, onUpdate }: SiegeStatusProps) {
style={{ width: `${String(progress)}%` }}
/>
-
+
{task.siegeIteration}/{task.siegeMaxIterations}
{/* Status info */}
-
+
{task.siegeActive ? (
<>
@@ -91,7 +91,7 @@ export function SiegeStatus({ task, onUpdate }: SiegeStatusProps) {
type="button"
onClick={() => { void (task.siegeActive ? handleStop() : handleStart()) }}
disabled={isLoading || !task.prNumber}
- className={`rounded px-2 py-0.5 text-[11px] font-medium transition-colors disabled:opacity-40 ${
+ className={`rounded px-2 py-0.5 text-xs font-medium transition-colors disabled:opacity-40 ${
task.siegeActive
? 'bg-error/10 text-error hover:bg-error/20'
: 'bg-accent/10 text-accent hover:bg-accent/20'
@@ -104,14 +104,14 @@ export function SiegeStatus({ task, onUpdate }: SiegeStatusProps) {
{/* Last checked */}
{task.siegeLastChecked && (
-
+
Last checked: {new Date(task.siegeLastChecked).toLocaleTimeString()}
)}
{/* No PR warning */}
{!task.prNumber && (
-
+
Create a PR first to use the siege loop.
)}
diff --git a/src/components/task-detail/task-checklist.tsx b/src/components/task-detail/task-checklist.tsx
index e5eac9e1..e64932e6 100644
--- a/src/components/task-detail/task-checklist.tsx
+++ b/src/components/task-detail/task-checklist.tsx
@@ -169,7 +169,7 @@ export function TaskChecklist({ task, onUpdate, repoPath }: TaskChecklistProps)
style={{ width: `${String(progressPercent)}%` }}
/>
-
+
{checkedCount}/{totalCount}
@@ -255,7 +255,7 @@ export function TaskChecklist({ task, onUpdate, repoPath }: TaskChecklistProps)
Add
@@ -269,7 +269,7 @@ export function TaskChecklist({ task, onUpdate, repoPath }: TaskChecklistProps)
type="button"
onClick={() => { void handleGenerateFromPr() }}
disabled={isGenerating}
- className="flex w-full items-center justify-center gap-1.5 rounded px-2 py-1.5 text-[11px] text-accent hover:bg-accent/10 transition-colors disabled:opacity-50"
+ className="flex w-full items-center justify-center gap-1.5 rounded px-2 py-1.5 text-xs text-accent hover:bg-accent/10 transition-colors disabled:opacity-50"
>
{isGenerating ? (
<>
@@ -289,14 +289,14 @@ export function TaskChecklist({ task, onUpdate, repoPath }: TaskChecklistProps)
)}
{generateError && (
- {generateError}
+ {generateError}
)}
)}
{/* Empty state */}
{items.length === 0 && !newItemText && !canGenerateFromPr && (
-
+
Add test items to verify before advancing this task.
)}
diff --git a/src/components/task-detail/usage-section.tsx b/src/components/task-detail/usage-section.tsx
index 5e0972b3..7bcdaa73 100644
--- a/src/components/task-detail/usage-section.tsx
+++ b/src/components/task-detail/usage-section.tsx
@@ -46,7 +46,7 @@ export function UsageSection({ agentType, agentStatus, startedAt }: UsageSection
return (
-
+
Usage
diff --git a/src/components/usage/metrics-dashboard.tsx b/src/components/usage/metrics-dashboard.tsx
index 2338cddc..df89f462 100644
--- a/src/components/usage/metrics-dashboard.tsx
+++ b/src/components/usage/metrics-dashboard.tsx
@@ -226,13 +226,13 @@ export function MetricsDashboard({ workspaceId, onClose }: Props) {
style={{ height: `${String((day.costUsd / maxDailyCost) * 100)}%`, minHeight: day.costUsd > 0 ? '4px' : '0' }}
/>
{dailyCosts.length <= 14 && (
-
{formatUsageDate(day.date)}
+
{formatUsageDate(day.date)}
)}
))}
{dailyCosts.length > 14 && (
-
+
{formatUsageDate(dailyCosts[0]?.date ?? '')}
{formatUsageDate(dailyCosts[dailyCosts.length - 1]?.date ?? '')}
diff --git a/src/index.css b/src/index.css
index 1058ea1a..b0a9ebc9 100644
--- a/src/index.css
+++ b/src/index.css
@@ -20,6 +20,18 @@
--font-sans: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'JetBrains Mono', ui-monospace, monospace;
+ /* Type scale. Tailwind stops at `xs`; this app is dense enough to need one
+ step below it for badges and count bubbles. Defining it here β in rem β
+ is what lets Settings β Appearance β Font Size move it, which an
+ arbitrary pixel size never could. */
+ --text-2xs: 0.625rem;
+ --text-2xs--line-height: calc(0.875 / 0.625);
+
+ /* Animation Speed reaches every `transition-*` utility through Tailwind's
+ own default, rather than through a `.transition-appearance` class each
+ component would have to remember to opt into. */
+ --default-transition-duration: var(--transition-duration);
+
/* Theme colors mapped to Tailwind utilities */
--color-bg: var(--bg);
--color-surface: var(--surface);
@@ -221,20 +233,6 @@ optgroup {
background: var(--scrollbar-track);
}
-/* Card styles using appearance variables */
-.card-padding {
- padding: var(--card-padding);
-}
-
-.card-gap {
- gap: var(--card-gap);
-}
-
-/* Transitions using appearance variables */
-.transition-appearance {
- transition-duration: var(--transition-duration);
-}
-
/* xterm needs explicit box ownership inside the animated Tauri panel. */
.agent-terminal-host,
.agent-terminal-host .xterm {
diff --git a/src/lib/appearance.test.ts b/src/lib/appearance.test.ts
new file mode 100644
index 00000000..0b23a546
--- /dev/null
+++ b/src/lib/appearance.test.ts
@@ -0,0 +1,84 @@
+import { describe, it, expect, beforeEach, afterEach } from 'vitest'
+import {
+ DEFAULT_APPEARANCE,
+ applyAppearance,
+ getAppearanceConfig,
+ initializeAppearance,
+} from './appearance'
+
+/**
+ * These settings work by putting attributes and a custom property on the
+ * document *element*, which `src/index.css` then selects on. jsdom can't
+ * evaluate the Tailwind side of that, but it can pin the half that broke
+ * before: every value has to land on , not , or the `[data-*]`
+ * selectors and `--base-font-size` never match anything.
+ */
+describe('appearance', () => {
+ const root = document.documentElement
+
+ beforeEach(() => {
+ localStorage.clear()
+ root.removeAttribute('style')
+ for (const attr of ['data-font-size', 'data-card-density', 'data-animation-speed']) {
+ root.removeAttribute(attr)
+ }
+ })
+
+ afterEach(() => {
+ localStorage.clear()
+ })
+
+ it('applies every setting to the document element', () => {
+ applyAppearance({
+ accentColor: '#123456',
+ fontSize: 'large',
+ cardDensity: 'compact',
+ animationSpeed: 'none',
+ })
+
+ expect(root.style.getPropertyValue('--accent')).toBe('#123456')
+ expect(root.getAttribute('data-font-size')).toBe('large')
+ expect(root.getAttribute('data-card-density')).toBe('compact')
+ expect(root.getAttribute('data-animation-speed')).toBe('none')
+ expect(document.body.getAttribute('data-font-size')).toBeNull()
+ })
+
+ it('leaves untouched settings alone on a partial update', () => {
+ applyAppearance({ fontSize: 'small', cardDensity: 'spacious' })
+ applyAppearance({ fontSize: 'large' })
+
+ expect(root.getAttribute('data-font-size')).toBe('large')
+ expect(root.getAttribute('data-card-density')).toBe('spacious')
+ })
+
+ it('reads the persisted settings blob', () => {
+ localStorage.setItem(
+ 'bento-settings',
+ JSON.stringify({ state: { global: { appearance: { fontSize: 'large' } } } }),
+ )
+
+ const config = getAppearanceConfig()
+ expect(config.fontSize).toBe('large')
+ // Absent keys fall back rather than coming back undefined.
+ expect(config.cardDensity).toBe(DEFAULT_APPEARANCE.cardDensity)
+ })
+
+ it('falls back to defaults when storage is missing or corrupt', () => {
+ expect(getAppearanceConfig()).toEqual(DEFAULT_APPEARANCE)
+
+ localStorage.setItem('bento-settings', 'not json')
+ expect(getAppearanceConfig()).toEqual(DEFAULT_APPEARANCE)
+ })
+
+ it('initializes from storage on startup', () => {
+ localStorage.setItem(
+ 'bento-settings',
+ JSON.stringify({ state: { global: { appearance: { animationSpeed: 'reduced' } } } }),
+ )
+
+ initializeAppearance()
+ expect(root.getAttribute('data-animation-speed')).toBe('reduced')
+ // Defaults still get applied for the keys storage didn't carry.
+ expect(root.getAttribute('data-font-size')).toBe(DEFAULT_APPEARANCE.fontSize)
+ })
+})