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() {