From 76b009f84c0d289f3e7f69ba3ba6290791fa278b Mon Sep 17 00:00:00 2001 From: ANonABento Date: Fri, 21 Aug 2026 17:38:52 -0400 Subject: [PATCH] fix(settings): wire the three Appearance settings that never reached the UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Theme and accent worked. The other three did not, and Font Size was worse than merely unwired. Font Size. `--base-font-size` does land on , so rem utilities scale correctly. But 247 arbitrary `text-[Npx]` values across 63 files are absolute and never move. At "small" that inverts the hierarchy: the root drops to 12px so `text-sm` renders 10.5px, while a caption pinned at 11px stays 11px and comes out larger than the body text it sits under. The scale in use was nine tiers inside 6px (8, 9, 10, 10.5, 11, 12, 12.25, 13, 14) — noise, not a hierarchy. It is now four rem steps. Tailwind stops at `xs`, so `--text-2xs` joins the `@theme` block for badges and count bubbles; 8/9px collapse into it, 10/11px into `text-xs`, 12/13px into `text-sm`. At the default size nothing moves more than 0.75px, and every step now tracks the setting. `scripts/check-type-scale.js` fails CI on the next absolute font size, so this doesn't silently accumulate again. Card Density and Animation Speed were fully inert. The chain ran store → data attribute → CSS var → `.card-padding` / `.card-gap` / `.transition-appearance` and died at the last hop: those three helper classes were used by zero components. Density now drives the task card's padding and the column's inter-card gap directly, at the call site where the dependency is visible. Animation Speed rides Tailwind's own `--default-transition-duration`, so every `transition-*` utility honours it without each component remembering to opt in. The three dead classes are deleted. Measured in a real browser, not inferred — small/medium/large now give 12/14/16px root with the full scale tracking, density 7/10.5/14px padding, and transitions 0/75/150ms. Framer Motion still ignores Animation Speed; noted on the roadmap. Adds appearance.test.ts, which pins the half jsdom can check: every value has to land on , since that is what index.css selects on. --- .github/workflows/ci.yml | 4 +- .github/workflows/release.yml | 2 + .tickets/_docs/ROADMAP.md | 52 +++++------- package.json | 1 + scripts/check-type-scale.js | 59 +++++++++++++ src/components/checklist/checklist-item.tsx | 4 +- .../command-palette/command-palette.tsx | 6 +- .../kanban/column-automation-sentence.tsx | 2 +- .../kanban/column-config-dialog.tsx | 2 +- src/components/kanban/column-header.tsx | 10 +-- .../kanban/column-trigger-action-editors.tsx | 2 +- src/components/kanban/column.tsx | 2 +- src/components/kanban/drag-overlay.tsx | 6 +- .../kanban/spawn-cli-action-editor.tsx | 12 +-- src/components/kanban/task-card-activity.tsx | 6 +- src/components/kanban/task-card-badges.tsx | 6 +- src/components/kanban/task-card-expanded.tsx | 26 +++--- src/components/kanban/task-card-status.tsx | 10 +-- src/components/kanban/task-card.tsx | 26 +++--- src/components/kanban/task-context-menu.tsx | 2 +- src/components/kanban/task-label-picker.tsx | 2 +- src/components/kanban/task-quick-actions.tsx | 2 +- src/components/kanban/task-settings-modal.tsx | 4 +- .../kanban/task-template-picker-dialog.tsx | 2 +- src/components/layout/tab-bar.tsx | 6 +- .../onboarding/onboarding-wizard.tsx | 6 +- src/components/panel/agent-panel.tsx | 18 ++-- src/components/panel/agent-transcript.tsx | 16 ++-- src/components/panel/chat-flat.tsx | 4 +- src/components/panel/chat-history.tsx | 2 +- src/components/panel/file-browser.tsx | 10 +-- src/components/panel/file-preview.tsx | 2 +- src/components/panel/files-tree.tsx | 2 +- .../panel/interactive-agent-view.tsx | 20 ++--- .../panel/orchestrator-terminal-view.tsx | 2 +- src/components/panel/pipeline-dashboard.tsx | 18 ++-- .../panel/pipeline-v2-dashboard.tsx | 32 +++---- .../panel/shared/attachment-button.tsx | 2 +- .../panel/shared/attachment-preview.tsx | 2 +- src/components/panel/shared/chat-input.tsx | 8 +- src/components/panel/workspace-files-view.tsx | 2 +- src/components/review/diff-viewer.tsx | 4 +- src/components/roster/agent-dossier.tsx | 10 +-- src/components/roster/agent-editor.tsx | 2 +- src/components/roster/agent-tile.tsx | 4 +- src/components/settings/settings-panel.tsx | 2 +- src/components/settings/tabs/agent-tab.tsx | 24 +++--- src/components/settings/tabs/debug-tab.tsx | 40 ++++----- src/components/settings/tabs/git-tab.tsx | 4 +- .../tabs/model-comparison-section.tsx | 4 +- .../settings/tabs/script-editor.tsx | 12 +-- src/components/settings/tabs/scripts-tab.tsx | 18 ++-- .../settings/tabs/shortcuts-tab.tsx | 2 +- .../settings/tabs/templates-tab.tsx | 6 +- src/components/settings/tabs/voice-tab.tsx | 4 +- .../settings/tabs/workspace-tab.tsx | 6 +- src/components/shared/empty-state.tsx | 2 +- src/components/shared/panel-tabs.tsx | 2 +- src/components/shared/selector-dropdown.tsx | 6 +- src/components/shared/status-badge.tsx | 2 +- src/components/shared/thinking-selector.tsx | 2 +- src/components/shortcuts-modal.tsx | 6 +- .../task-detail/commits-section.tsx | 4 +- src/components/task-detail/diff-section.tsx | 10 +-- .../task-detail/notification-section.tsx | 12 +-- src/components/task-detail/siege-status.tsx | 10 +-- src/components/task-detail/task-checklist.tsx | 10 +-- src/components/task-detail/usage-section.tsx | 2 +- src/components/usage/metrics-dashboard.tsx | 4 +- src/index.css | 26 +++--- src/lib/appearance.test.ts | 84 +++++++++++++++++++ 71 files changed, 429 insertions(+), 297 deletions(-) create mode 100755 scripts/check-type-scale.js create mode 100644 src/lib/appearance.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86559748..2be8512e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,6 +66,8 @@ jobs: - name: IPC registration check run: pnpm run test:ipc + - name: Type scale check + run: pnpm run test:type-scale - name: Frontend tests run: pnpm exec vitest run @@ -98,7 +100,7 @@ jobs: '', 'Checks run:', '- Rust: `cargo check --workspace`, `cargo clippy --workspace --all-targets -- -D warnings`, `cargo test --workspace`', - '- Frontend: `pnpm run type-check`, `pnpm run lint`, `pnpm run test:ipc`, `pnpm audit --audit-level moderate`, `pnpm exec vitest run`', + '- Frontend: `pnpm run type-check`, `pnpm run lint`, `pnpm run test:ipc`, `pnpm run test:type-scale`, `pnpm audit --audit-level moderate`, `pnpm exec vitest run`', '', 'Please inspect the failed workflow run and fix main.' ].join('\n'); diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b56d2567..c513cf4b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,6 +55,8 @@ jobs: - name: IPC registration check run: pnpm run test:ipc + - name: Type scale check + run: pnpm run test:type-scale - name: Dependency audit run: pnpm audit --audit-level moderate diff --git a/.tickets/_docs/ROADMAP.md b/.tickets/_docs/ROADMAP.md index 6114a49f..20560fc9 100644 --- a/.tickets/_docs/ROADMAP.md +++ b/.tickets/_docs/ROADMAP.md @@ -35,6 +35,18 @@ replaces the terminal. with a 60s budget and **never** hard-kills — exhausting it injects anyway and leaves the pane inspectable. `CLI_HEALTH_SPECS` now probes the interactive codex path so the same drift can't ship silently again. +- Settings wiring pass (Appearance): all five settings now reach the UI. Font Size + was worse than unwired — 247 arbitrary `text-[Npx]` values across 63 files were + absolute, so at "small" the root dropped to 12px while a caption pinned at 11px + stayed put and rendered *larger* than the `text-sm` body above it. The scale is + now four rem steps (`text-2xs` added to `@theme`, since Tailwind stops at `xs`), + all 247 converted, and `scripts/check-type-scale.js` fails CI on the next + absolute size. Card Density and Animation Speed were fully inert: the chain died + at `.card-padding`/`.card-gap`/`.transition-appearance`, three helper classes + zero components used. Density now drives the task card's padding and the + column's inter-card gap directly; Animation Speed rides Tailwind's own + `--default-transition-duration`, so every `transition-*` utility honours it + without opting in. The three dead classes are gone. - Interactive done-advisory persisted (migration 048, `tasks.agent_done_signaled_at`): the signal used to be a Tauri event only, so it existed just while the agent panel was mounted. Now the board shows a "Ready to advance" badge that @@ -50,37 +62,11 @@ replaces the terminal. ## 🟡 Important (rough edges, not blockers) -2. **Settings wiring pass — three of five Appearance settings don't reach the UI.** - Measured 2026-08-21 in a real browser, not inferred. The store→DOM plumbing is - fine (`lib/appearance.ts`, called from `main.tsx` and `settings-store.ts`); - what's missing is anything *consuming* the result. - - | Setting | State | - |---|---| - | Theme | works | - | Accent colour | works (sets `--accent`) | - | **Font size** | **partial, and actively wrong at "small"** | - | **Card density** | **inert** | - | **Animation speed** | **inert** | - - - **Font size.** `--base-font-size` lands on `html`, so rem-based utilities do - scale — `text-sm` measures 10.5 / 12.25 / 14px across small/medium/large. - But **247 arbitrary `text-[Npx]` values across 63 files** (144× `text-[10px]`, - 96× `text-[11px]`) are absolute and never move. At "small" that *inverts the - hierarchy*: `text-sm` drops to 10.5px while a caption pinned at `text-[11px]` - stays larger than the body text it's meant to sit under. - Fix: add rem-based steps to the `@theme` block in `index.css` (the scale - wants a `text-micro`/`text-tiny` below `text-xs`) and convert the 247 uses. - - **Card density / animation speed.** The chain is - store → `data-card-density` / `data-animation-speed` → `--card-padding` / - `--card-gap` / `--transition-duration` → `.card-padding` / `.card-gap` / - `.transition-appearance`. That last hop is where it dies: those three helper - classes are used by **zero** components. Either apply them on the card/panel - primitives or drop the settings. - - While in there, re-check the other known-inert surfaces listed under - "Inert / deferred" below — custom keyboard shortcuts render but do nothing, - and the OpenRouter / Google / Ollama provider cards are "Coming soon". +2. **Other inert settings surfaces.** Flagged while wiring Appearance, not fixed: + custom keyboard shortcuts render but do nothing (`shortcuts-tab.tsx:54`), and + the OpenRouter / Google / Ollama provider cards are "Coming soon". Framer + Motion animations also ignore Animation Speed — they read no CSS variable, so + "none" doesn't fully mean none. 3. **`effort_level` not wired into the trigger path.** It's a real DB field + has a `thinking-selector.tsx` and works in the interactive chat panel, but no @@ -123,5 +109,5 @@ replaces the terminal. ## Suggested order -`#1 rm stale claude` (free) → `#2 settings wiring pass` → `#3 effort wiring` → -`#4 checklist/roadmap MCP` → `#5 MCP gaps`. +`#1 rm stale claude` (free) → `#3 effort wiring` → `#4 checklist/roadmap MCP` → +`#5 MCP gaps`. `#2` is cosmetic cleanup; do it opportunistically. diff --git a/package.json b/package.json index cc6a15ac..b31d245e 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "type-check": "tsc --noEmit", "lint": "eslint src/", "test:ipc": "node scripts/check-ipc-registration.js", + "test:type-scale": "node scripts/check-type-scale.js", "format": "prettier --write 'src/**/*.{ts,tsx,css}'", "test": "vitest", "test:run": "vitest run", diff --git a/scripts/check-type-scale.js b/scripts/check-type-scale.js new file mode 100755 index 00000000..ca3acfde --- /dev/null +++ b/scripts/check-type-scale.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node + +/** + * Guards Settings → Appearance → Font Size. + * + * That setting works by putting `--base-font-size` on , so only + * rem-based type scales with it. An arbitrary pixel size is absolute: it + * ignores the setting entirely. Once enough of them accumulate the hierarchy + * doesn't just stop scaling, it inverts — at "small" the root drops to 12px so + * `text-sm` renders 10.5px, and a caption pinned at a fixed 11px comes out *larger* + * than the body text it sits under. + * + * 247 of these had built up before this check existed. Use the scale instead: + * text-2xs / text-xs / text-sm / text-base / text-lg … + */ + +import fs from 'node:fs' +import path from 'node:path' + +const root = process.cwd() +const sourceExtensions = new Set(['.ts', '.tsx', '.css']) +const ARBITRARY_FONT_SIZE = /text-\[\d+(?:\.\d+)?(px|pt)\]/g + +function walk(dir, files = []) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + if (entry.name === 'node_modules' || entry.name === 'dist') continue + const fullPath = path.join(dir, entry.name) + if (entry.isDirectory()) { + walk(fullPath, files) + } else if (sourceExtensions.has(path.extname(entry.name))) { + files.push(fullPath) + } + } + return files +} + +const offenders = [] +let scanned = 0 +for (const file of walk(path.join(root, 'src'))) { + scanned += 1 + const lines = fs.readFileSync(file, 'utf8').split('\n') + lines.forEach((line, i) => { + for (const match of line.matchAll(ARBITRARY_FONT_SIZE)) { + offenders.push(`${path.relative(root, file)}:${i + 1} ${match[0]}`) + } + }) +} + +if (offenders.length > 0) { + console.error('Absolute font sizes found — these ignore the Font Size setting:') + for (const offender of offenders) { + console.error(` - ${offender}`) + } + console.error('\nUse a rem step from the scale instead: text-2xs, text-xs, text-sm, text-base, text-lg.') + console.error('If a genuinely fixed size is required, add a rem token to the @theme block in src/index.css.') + process.exit(1) +} + +console.log(`Type scale check passed (${scanned} file(s), no absolute font sizes).`) diff --git a/src/components/checklist/checklist-item.tsx b/src/components/checklist/checklist-item.tsx index 0b94712a..b86e6803 100644 --- a/src/components/checklist/checklist-item.tsx +++ b/src/components/checklist/checklist-item.tsx @@ -77,7 +77,7 @@ export function ChecklistItemRow({ item, categoryId, onFixThis }: Props) { {/* Auto-detect badge */} {item.detectType && item.detectType !== 'none' && ( Linked Task diff --git a/src/components/command-palette/command-palette.tsx b/src/components/command-palette/command-palette.tsx index 6f65a4a4..e95e9239 100644 --- a/src/components/command-palette/command-palette.tsx +++ b/src/components/command-palette/command-palette.tsx @@ -294,7 +294,7 @@ export function CommandPalette({ onClose, onShowShortcuts }: Props) { autoFocus className="flex-1 bg-transparent text-sm text-text-primary placeholder:text-text-secondary focus:outline-none" /> - + Esc @@ -345,11 +345,11 @@ export function CommandPalette({ onClose, onShowShortcuts }: Props) { -

+

The prompt, runtime, retries and more live under Advanced. Most columns never need them.

diff --git a/src/components/kanban/column-config-dialog.tsx b/src/components/kanban/column-config-dialog.tsx index e2b2f455..344e0574 100644 --- a/src/components/kanban/column-config-dialog.tsx +++ b/src/components/kanban/column-config-dialog.tsx @@ -406,7 +406,7 @@ function CapacityFields({ aria-pressed={selected} > {option.label} - {option.hint} + {option.hint} ) })} diff --git a/src/components/kanban/column-header.tsx b/src/components/kanban/column-header.tsx index 91bbe13e..d12eaf89 100644 --- a/src/components/kanban/column-header.tsx +++ b/src/components/kanban/column-header.tsx @@ -207,7 +207,7 @@ export const ColumnHeader = memo(function ColumnHeader({ > {getIcon(icon)}
- + {taskCount} {isRenaming ? ( @@ -240,7 +240,7 @@ export const ColumnHeader = memo(function ColumnHeader({ ) })} diff --git a/src/components/kanban/column.tsx b/src/components/kanban/column.tsx index d8d732f8..42d5522c 100644 --- a/src/components/kanban/column.tsx +++ b/src/components/kanban/column.tsx @@ -274,7 +274,7 @@ export const Column = memo(function Column({
diff --git a/src/components/kanban/drag-overlay.tsx b/src/components/kanban/drag-overlay.tsx index b32e9c80..c7c99191 100644 --- a/src/components/kanban/drag-overlay.tsx +++ b/src/components/kanban/drag-overlay.tsx @@ -60,7 +60,7 @@ export function DragOverlayContent({ item }: DragOverlayContentProps) { {/* Metadata row */} {hasMetadata && ( -
+
{cardSettings.showPrBadge && task.prNumber && ( @@ -84,14 +84,14 @@ export function DragOverlayContent({ item }: DragOverlayContentProps) { {taskLabels.slice(0, 3).map((label) => ( {label.name} ))} {taskLabels.length > 3 && ( - +{taskLabels.length - 3} + +{taskLabels.length - 3} )}
)} diff --git a/src/components/kanban/spawn-cli-action-editor.tsx b/src/components/kanban/spawn-cli-action-editor.tsx index e6e85ad6..3391ac73 100644 --- a/src/components/kanban/spawn-cli-action-editor.tsx +++ b/src/components/kanban/spawn-cli-action-editor.tsx @@ -118,7 +118,7 @@ export function SpawnCliActionEditor({ }`} > {opt.label} - {opt.description} + {opt.description} ) })} @@ -147,13 +147,13 @@ export function SpawnCliActionEditor({ }`} > {mode.label} - {mode.caption} + {mode.caption} ) })}
{isManagedMode && ( -

+

Managed mode streams structured events; Terminal is more reliable for long-running agents.

)} @@ -182,8 +182,8 @@ export function SpawnCliActionEditor({
{TEMPLATE_VARIABLES.map((v) => (
- {v.name} - {v.desc} + {v.name} + {v.desc}
))}
@@ -199,7 +199,7 @@ export function SpawnCliActionEditor({ className="h-4 w-4 rounded border-border-default accent-accent" /> Use agent queue - + max 3 concurrent diff --git a/src/components/kanban/task-card-activity.tsx b/src/components/kanban/task-card-activity.tsx index e3afe832..0606e2f3 100644 --- a/src/components/kanban/task-card-activity.tsx +++ b/src/components/kanban/task-card-activity.tsx @@ -22,7 +22,7 @@ export function AgentActivityPreview({ return (
-
+
@@ -35,7 +35,7 @@ export function AgentActivityPreview({ {elapsedStr}
{agentStream.toolCount > 0 && ( -
+
@@ -59,7 +59,7 @@ export function AgentActivityPreview({ } return ( -
+
{activity.type === 'active' && ( diff --git a/src/components/kanban/task-card-badges.tsx b/src/components/kanban/task-card-badges.tsx index 5f51f397..c8fa9e14 100644 --- a/src/components/kanban/task-card-badges.tsx +++ b/src/components/kanban/task-card-badges.tsx @@ -70,7 +70,7 @@ export function PrStatusIndicator({ task, settings }: { task: Task; settings: ty - #{task.prNumber} + #{task.prNumber} {statusIcon} ) @@ -90,7 +90,7 @@ export function AgentDoneBadge({ task }: { task: Task }) { if (task.agentDoneSignaledAt === null) return null return ( @@ -116,7 +116,7 @@ export function SiegeBadge({ task }: { task: Task }) { - + {task.siegeIteration}/{task.siegeMaxIterations} diff --git a/src/components/kanban/task-card-expanded.tsx b/src/components/kanban/task-card-expanded.tsx index a47221aa..375ec13f 100644 --- a/src/components/kanban/task-card-expanded.tsx +++ b/src/components/kanban/task-card-expanded.tsx @@ -42,7 +42,7 @@ function DescriptionMarkdown({ description }: { description: string }) { ) }, pre: ({ children }) => ( -
+            
               {children}
             
), @@ -177,7 +177,7 @@ export function TaskCardExpanded({ task }: { task: Task }) { }} className="max-h-56 w-full resize-y rounded-md border border-accent bg-surface px-2 py-1.5 text-xs leading-relaxed text-text-primary outline-none focus:ring-2 focus:ring-accent/20 disabled:opacity-60" /> -
+
{saveShortcutLabel} to save, Escape to cancel
@@ -224,12 +224,12 @@ export function TaskCardExpanded({ task }: { task: Task }) { - + {task.branch} {task.worktreePath && ( worktree @@ -238,7 +238,7 @@ export function TaskCardExpanded({ task }: { task: Task }) {
)} {task.model && ( - + {task.model} )} @@ -247,7 +247,7 @@ export function TaskCardExpanded({ task }: { task: Task }) { {/* Siege Loop Status (only when active) */} {(task.siegeActive || task.siegeIteration > 0) && (
-

+

Siege Loop

@@ -256,7 +256,7 @@ export function TaskCardExpanded({ task }: { task: Task }) { {/* Touched files only; full diffs live in the agent panel. */}
-

+

Touched Files

-

+

Commits

@@ -338,7 +338,7 @@ function TouchedFilesSummary({ @@ -144,7 +144,7 @@ export function PipelineErrorBanner({ onRetry() }} aria-label="Retry pipeline" - className="shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium bg-error/20 hover:bg-error/30 transition-colors" + className="shrink-0 rounded px-1.5 py-0.5 text-xs font-medium bg-error/20 hover:bg-error/30 transition-colors" > Retry @@ -170,7 +170,7 @@ export function PipelineErrorBanner({ > Raw error -

+

{rawError}

diff --git a/src/components/kanban/task-card.tsx b/src/components/kanban/task-card.tsx index faea6426..ad06ffcd 100644 --- a/src/components/kanban/task-card.tsx +++ b/src/components/kanban/task-card.tsx @@ -520,7 +520,7 @@ export const TaskCard = memo(function TaskCard({
{/* Title — click-to-edit when card is expanded */} @@ -562,7 +562,7 @@ export const TaskCard = memo(function TaskCard({ setEditingTitle(true) } }} - className={`min-w-0 flex-1 pr-7 text-[13px] font-medium leading-snug text-text-primary line-clamp-2 ${isExpanded ? 'hover:text-accent' : ''}`} + className={`min-w-0 flex-1 pr-7 text-sm font-medium leading-snug text-text-primary line-clamp-2 ${isExpanded ? 'hover:text-accent' : ''}`} style={isExpanded ? { cursor: 'text' } : undefined} title={isExpanded ? 'Click to edit title' : undefined} > @@ -574,7 +574,7 @@ export const TaskCard = memo(function TaskCard({ type="button" onClick={(e) => { e.stopPropagation(); actions.handleUnarchiveTask(); }} title="Restore — move back to the active board" - className="shrink-0 inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-[10px] font-medium bg-surface-hover text-text-secondary/80 border border-border-default hover:border-accent hover:text-accent transition-colors" + className="shrink-0 inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-xs font-medium bg-surface-hover text-text-secondary/80 border border-border-default hover:border-accent hover:text-accent transition-colors" > @@ -602,7 +602,7 @@ export const TaskCard = memo(function TaskCard({ {/* Compact metadata row — hidden when expanded (expanded view has its own) */} {!isExpanded && hasMetadata && ( -
+
{isPipelineActive && !hasPipelineError && task.pipelineState !== 'running' && ( @@ -615,13 +615,13 @@ export const TaskCard = memo(function TaskCard({ {task.heldByUser && ( - + Held )} {spawnedByLabel && ( @@ -632,7 +632,7 @@ export const TaskCard = memo(function TaskCard({ )} {titleWorkspaceName && ( - + {titleWorkspaceName} )} @@ -653,12 +653,12 @@ export const TaskCard = memo(function TaskCard({ {task.agentType} )} {task.model && ( - + {task.model} )} {cardSettings.showBranch && task.branch && ( - + {task.worktreePath && ( )} @@ -670,12 +670,12 @@ export const TaskCard = memo(function TaskCard({ - {depCount} + {depCount} )} {cardSettings.showTimestamp && !isPipelineActive && ( - + {formatRelativeTime(task.updatedAt)} )} @@ -688,14 +688,14 @@ export const TaskCard = memo(function TaskCard({ {taskLabels.slice(0, 3).map((label) => ( {label.name} ))} {taskLabels.length > 3 && ( - +{taskLabels.length - 3} + +{taskLabels.length - 3} )}
)} diff --git a/src/components/kanban/task-context-menu.tsx b/src/components/kanban/task-context-menu.tsx index 08417a8f..e69954fa 100644 --- a/src/components/kanban/task-context-menu.tsx +++ b/src/components/kanban/task-context-menu.tsx @@ -136,7 +136,7 @@ function MenuItemComponent({ item, onClose }: { item: MenuItem; onClose: () => v {item.icon && {item.icon}} {item.label} {item.shortcut && ( - {item.shortcut} + {item.shortcut} )} ) diff --git a/src/components/kanban/task-label-picker.tsx b/src/components/kanban/task-label-picker.tsx index f7ef8841..8f2b1c44 100644 --- a/src/components/kanban/task-label-picker.tsx +++ b/src/components/kanban/task-label-picker.tsx @@ -65,7 +65,7 @@ export function TaskLabelPicker({ task }: TaskLabelPickerProps) { {open && (
-
Labels
+
Labels
{labels.length === 0 ? (
Create labels in the board header.
) : ( diff --git a/src/components/kanban/task-quick-actions.tsx b/src/components/kanban/task-quick-actions.tsx index e1f41ccb..c9495f36 100644 --- a/src/components/kanban/task-quick-actions.tsx +++ b/src/components/kanban/task-quick-actions.tsx @@ -19,7 +19,7 @@ export const TaskQuickActions = memo(function TaskQuickActions({ {confirmDeletePending && (
diff --git a/src/components/layout/tab-bar.tsx b/src/components/layout/tab-bar.tsx index 6375c20b..8498f01c 100644 --- a/src/components/layout/tab-bar.tsx +++ b/src/components/layout/tab-bar.tsx @@ -104,7 +104,7 @@ function SortableTab({ {/* Notification badge */} {notificationCount > 0 && ( - + {notificationCount > 9 ? '9+' : notificationCount} )} @@ -264,12 +264,12 @@ function ChecklistButton() { /> {hasItems && !allComplete && ( - + {total - progress} )} {allComplete && ( - + )} diff --git a/src/components/onboarding/onboarding-wizard.tsx b/src/components/onboarding/onboarding-wizard.tsx index 22efb319..0a0c6c20 100644 --- a/src/components/onboarding/onboarding-wizard.tsx +++ b/src/components/onboarding/onboarding-wizard.tsx @@ -201,7 +201,7 @@ export function OnboardingWizard({ onComplete }: OnboardingWizardProps) { onClick={() => { void refreshPrerequisites() }} disabled={checkingPrereqs} style={{ cursor: checkingPrereqs ? 'not-allowed' : 'pointer' }} - className="rounded border border-border-default px-2 py-0.5 text-[11px] text-text-secondary hover:bg-surface-hover disabled:opacity-50" + className="rounded border border-border-default px-2 py-0.5 text-xs text-text-secondary hover:bg-surface-hover disabled:opacity-50" > {checkingPrereqs ? 'Checking...' : 'Recheck'} @@ -307,11 +307,11 @@ export function OnboardingWizard({ onComplete }: OnboardingWizardProps) { style={{ cursor: 'pointer' }} >
{opt.name}
-
{opt.blurb}
+
{opt.blurb}
))}
-

+

Headless is the recommended default. You can change this anytime in Settings → Agent.

diff --git a/src/components/panel/agent-panel.tsx b/src/components/panel/agent-panel.tsx index 51219112..c95552c2 100644 --- a/src/components/panel/agent-panel.tsx +++ b/src/components/panel/agent-panel.tsx @@ -236,7 +236,7 @@ function HeadlessPanel({ task, onClose }: AgentPanelProps) {
Type directly in the terminal — /commands,{' '} @files, Tab-complete and paste all work like a real terminal. @@ -624,7 +624,7 @@ function RuntimeModeToggle({ type="button" disabled={disabled} onClick={() => { void handleSelect(choice.value) }} - className={`flex w-full items-center justify-between rounded px-2 py-1.5 text-left text-[11px] transition-colors disabled:opacity-40 ${ + className={`flex w-full items-center justify-between rounded px-2 py-1.5 text-left text-xs transition-colors disabled:opacity-40 ${ current === choice.value ? 'text-text-primary' : 'text-text-secondary hover:bg-surface-hover hover:text-text-primary' @@ -634,7 +634,7 @@ function RuntimeModeToggle({ > {choice.label} {current === choice.value && } - {disabled && off} + {disabled && off} ) }) @@ -643,7 +643,7 @@ function RuntimeModeToggle({ if (variant === 'menu') { return (
-
+
Runtime
{choiceButtons} @@ -662,7 +662,7 @@ function RuntimeModeToggle({ aria-label="Runtime mode" title={`Runtime: ${label} — click to switch`} data-testid="agent-panel-runtime-toggle" - className={`inline-flex h-7 items-center gap-1 rounded-md border border-border-default bg-surface text-[11px] font-medium text-text-secondary transition-colors hover:bg-surface-hover hover:text-text-primary disabled:opacity-50 ${ + className={`inline-flex h-7 items-center gap-1 rounded-md border border-border-default bg-surface text-xs font-medium text-text-secondary transition-colors hover:bg-surface-hover hover:text-text-primary disabled:opacity-50 ${ iconOnly ? 'w-7 justify-center px-0' : 'px-2' }`} style={{ cursor: busy ? 'wait' : 'pointer' }} @@ -721,7 +721,7 @@ function AgentHeaderControls({ onClick={() => { close(); onKill() }} disabled={killBusy} data-testid="agent-panel-kill-button" - className="flex w-full items-center rounded px-2 py-1.5 text-left text-[11px] font-medium text-error hover:bg-error/10 disabled:opacity-50" + className="flex w-full items-center rounded px-2 py-1.5 text-left text-xs font-medium text-error hover:bg-error/10 disabled:opacity-50" style={{ cursor: killBusy ? 'wait' : 'pointer' }} > Kill session @@ -785,7 +785,7 @@ function HoldButton({ onClick={onToggle} disabled={busy} data-testid="agent-panel-hold-button" - className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-[11px] font-medium text-text-secondary hover:bg-surface-hover hover:text-text-primary disabled:opacity-50" + className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs font-medium text-text-secondary hover:bg-surface-hover hover:text-text-primary disabled:opacity-50" style={{ cursor: busy ? 'wait' : 'pointer' }} > @@ -801,7 +801,7 @@ function HoldButton({ disabled={busy} data-testid="agent-panel-hold-button" title={title} - className={`inline-flex h-7 items-center gap-1.5 rounded-md border text-[11px] font-medium transition-colors disabled:opacity-50 ${ + className={`inline-flex h-7 items-center gap-1.5 rounded-md border text-xs font-medium transition-colors disabled:opacity-50 ${ compact ? 'w-7 justify-center px-0' : 'px-2' } ${ held diff --git a/src/components/panel/agent-transcript.tsx b/src/components/panel/agent-transcript.tsx index 2c96c5c5..1240626b 100644 --- a/src/components/panel/agent-transcript.tsx +++ b/src/components/panel/agent-transcript.tsx @@ -82,7 +82,7 @@ export function AgentTranscript({ {isEmpty ? (
-
+
No semantic transcript yet

@@ -374,7 +374,7 @@ function RunGroup({ group, isLatest }: { group: Extract -

{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_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 && (
{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}
-
+
{agent.runtime}
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() {