feat(web): Dark mode contrast — raise token floor and flatten opacity tier system (#2734)

* feat: Raised four dark-mode tokens, converted five hardcoded oklch valu…

- "web/app/globals.css"
- "web/components/gsd/code-editor.tsx"

GSD-Task: S01/T01

* feat: Applied border-border 2-tier sweep across 21 component files: /20…

- "web/components/gsd/command-surface.tsx"
- "web/components/gsd/remaining-command-panels.tsx"
- "web/components/gsd/chat-mode.tsx"
- "web/components/gsd/settings-panels.tsx"
- "web/components/gsd/diagnostics-panels.tsx"
- "web/components/gsd/onboarding/step-authenticate.tsx"
- "web/components/gsd/knowledge-captures-panel.tsx"
- "web/components/gsd/projects-view.tsx"

GSD-Task: S02/T01

* feat: Swept text-foreground/muted-foreground/sidebar-foreground opacity…

- "web/components/gsd/command-surface.tsx"
- "web/components/gsd/remaining-command-panels.tsx"
- "web/components/gsd/chat-mode.tsx"
- "web/components/gsd/settings-panels.tsx"
- "web/components/gsd/diagnostics-panels.tsx"
- "web/components/gsd/knowledge-captures-panel.tsx"
- "web/components/gsd/projects-view.tsx"
- "web/components/gsd/visualizer-view.tsx"

GSD-Task: S02/T02

* feat: Applied background opacity mapping tables across all component fi…

- "web/components/gsd/remaining-command-panels.tsx"
- "web/components/gsd/command-surface.tsx"
- "web/components/gsd/visualizer-view.tsx"
- "web/components/gsd/chat-mode.tsx"
- "web/components/gsd/settings-panels.tsx"
- "web/components/gsd/diagnostics-panels.tsx"
- "web/components/gsd/onboarding/step-authenticate.tsx"
- "web/components/gsd/knowledge-captures-panel.tsx"

GSD-Task: S02/T03
This commit is contained in:
Andrew 2026-03-26 18:17:03 -04:00 committed by GitHub
parent 74c1736372
commit 07d804588e
34 changed files with 392 additions and 392 deletions

View file

@ -60,12 +60,12 @@
--secondary: oklch(0.18 0 0);
--secondary-foreground: oklch(0.85 0 0);
--muted: oklch(0.15 0 0);
--muted-foreground: oklch(0.55 0 0);
--muted-foreground: oklch(0.60 0 0);
--accent: oklch(0.2 0 0);
--accent-foreground: oklch(0.9 0 0);
--destructive: oklch(0.5 0.15 25);
--destructive-foreground: oklch(0.95 0 0);
--border: oklch(0.22 0 0);
--border: oklch(0.28 0 0);
--input: oklch(0.15 0 0);
--ring: oklch(0.4 0 0);
--chart-1: oklch(0.7 0 0);
@ -79,7 +79,7 @@
--sidebar-primary-foreground: oklch(0.09 0 0);
--sidebar-accent: oklch(0.15 0 0);
--sidebar-accent-foreground: oklch(0.9 0 0);
--sidebar-border: oklch(0.18 0 0);
--sidebar-border: oklch(0.24 0 0);
--sidebar-ring: oklch(0.35 0 0);
/* Custom tokens */
@ -88,7 +88,7 @@
--info: oklch(0.6 0.1 250);
--terminal: oklch(0.06 0 0);
--terminal-foreground: oklch(0.75 0 0);
--code-line-number: oklch(0.35 0 0);
--code-line-number: oklch(0.42 0 0);
}
@theme inline {
@ -210,7 +210,7 @@
width: 3.5ch;
margin-right: 1.5ch;
text-align: right;
color: oklch(0.35 0 0);
color: var(--code-line-number);
user-select: none;
}
@ -228,7 +228,7 @@
margin-top: 0;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid oklch(0.22 0 0);
border-bottom: 1px solid var(--border);
}
.markdown-body h2 {
@ -237,7 +237,7 @@
margin-top: 1.75rem;
margin-bottom: 0.75rem;
padding-bottom: 0.35rem;
border-bottom: 1px solid oklch(0.22 0 0);
border-bottom: 1px solid var(--border);
}
.markdown-body h3 {
@ -289,14 +289,14 @@
.markdown-body blockquote {
margin: 0.75rem 0;
padding: 0.25rem 1rem;
border-left: 3px solid oklch(0.3 0 0);
border-left: 3px solid oklch(0.38 0 0);
color: oklch(0.6 0 0);
}
.markdown-body hr {
margin: 1.5rem 0;
border: none;
border-top: 1px solid oklch(0.22 0 0);
border-top: 1px solid var(--border);
}
.markdown-body strong {
@ -310,7 +310,7 @@
.markdown-body del {
text-decoration: line-through;
color: oklch(0.5 0 0);
color: oklch(0.55 0 0);
}
/* Task list checkboxes */

View file

@ -267,7 +267,7 @@ function WorkspaceChrome() {
beta
</Badge>
</div>
<span className="hidden sm:inline text-2xl font-thin text-muted-foreground/50 leading-none select-none">/</span>
<span className="hidden sm:inline text-2xl font-thin text-muted-foreground leading-none select-none">/</span>
<span className="hidden sm:inline text-sm text-muted-foreground truncate" data-testid="workspace-project-cwd" title={projectPath ?? undefined}>
{isConnecting ? (
<Skeleton className="inline-block h-4 w-28 align-middle" />
@ -427,7 +427,7 @@ function WorkspaceChrome() {
>
<div className="flex items-center gap-2 text-muted-foreground">
<span className="font-medium text-foreground">Terminal</span>
<span className="text-[10px] text-muted-foreground/50">
<span className="text-[10px] text-muted-foreground">
{isTerminalExpanded ? "▼" : "▲"}
</span>
</div>

View file

@ -337,7 +337,7 @@ function MarkdownContent({ content }: { content: string }) {
})
return (
<div
className="chat-code-block my-3 rounded-xl overflow-x-auto text-sm shadow-sm border border-border/40"
className="chat-code-block my-3 rounded-xl overflow-x-auto text-sm shadow-sm border border-border/50"
dangerouslySetInnerHTML={{ __html: highlighted }}
/>
)
@ -348,7 +348,7 @@ function MarkdownContent({ content }: { content: string }) {
if (isInline) {
return (
<code
className="rounded-md bg-muted/80 px-1.5 py-0.5 text-[0.85em] font-mono text-foreground"
className="rounded-md bg-muted px-1.5 py-0.5 text-[0.85em] font-mono text-foreground"
{...props}
>
{children}
@ -357,7 +357,7 @@ function MarkdownContent({ content }: { content: string }) {
}
return (
<pre className={cn("my-3 overflow-x-auto rounded-xl p-4 text-sm border border-border/40", isDark ? "bg-[#0d1117]" : "bg-[#f6f8fa]")}>
<pre className={cn("my-3 overflow-x-auto rounded-xl p-4 text-sm border border-border/50", isDark ? "bg-[#0d1117]" : "bg-[#f6f8fa]")}>
<code className="font-mono">{children}</code>
</pre>
)
@ -374,7 +374,7 @@ function MarkdownContent({ content }: { content: string }) {
},
th({ children }: { children?: React.ReactNode }) {
return (
<th className="border-b border-border bg-muted/40 px-3 py-2 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wide">
<th className="border-b border-border bg-muted/50 px-3 py-2 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wide">
{children}
</th>
)
@ -424,7 +424,7 @@ function MarkdownContent({ content }: { content: string }) {
},
img({ alt, src }: { alt?: string; src?: string }) {
return (
<span className="my-2 block rounded-lg border border-border bg-muted/20 px-3 py-2 text-xs text-muted-foreground italic">
<span className="my-2 block rounded-lg border border-border bg-muted/50 px-3 py-2 text-xs text-muted-foreground italic">
🖼 {alt || src || "image"}
</span>
)
@ -559,7 +559,7 @@ function TuiSelectPrompt({
data-testid="tui-select-prompt"
tabIndex={0}
onKeyDown={handleKeyDown}
className="mt-2 rounded-xl border border-border/60 bg-background/60 p-1.5 shadow-sm outline-none focus-visible:ring-1 focus-visible:ring-border"
className="mt-2 rounded-xl border border-border bg-background p-1.5 shadow-sm outline-none focus-visible:ring-1 focus-visible:ring-border"
aria-label={`Select: ${prompt.label}`}
role="listbox"
aria-activedescendant={`tui-select-option-${localIndex}`}
@ -584,7 +584,7 @@ function TuiSelectPrompt({
"flex w-full items-start gap-2 rounded-lg px-3 py-1.5 text-left text-sm transition-colors",
isSelected
? "bg-primary/15 text-primary font-medium"
: "text-foreground hover:bg-muted/60",
: "text-foreground hover:bg-muted",
)}
>
<span className="mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center">
@ -671,7 +671,7 @@ function TuiTextPrompt({
return (
<div
data-testid="tui-text-prompt"
className="mt-2 rounded-xl border border-border/60 bg-background/60 p-3 shadow-sm"
className="mt-2 rounded-xl border border-border bg-background p-3 shadow-sm"
>
{prompt.label && (
<p className="mb-2 text-[11px] font-medium text-muted-foreground uppercase tracking-wide">
@ -695,7 +695,7 @@ function TuiTextPrompt({
"flex h-8 items-center justify-center rounded-lg px-3 text-xs font-medium transition-all",
value.trim()
? "bg-primary text-primary-foreground hover:bg-primary/90 active:scale-95 shadow-sm"
: "bg-muted text-muted-foreground/40 cursor-not-allowed",
: "bg-muted text-muted-foreground cursor-not-allowed",
)}
>
Submit
@ -771,7 +771,7 @@ function TuiPasswordPrompt({
return (
<div
data-testid="tui-password-prompt"
className="mt-2 rounded-xl border border-border/60 bg-background/60 p-3 shadow-sm"
className="mt-2 rounded-xl border border-border bg-background p-3 shadow-sm"
>
{prompt.label && (
<p className="mb-2 text-[11px] font-medium text-muted-foreground uppercase tracking-wide">
@ -796,7 +796,7 @@ function TuiPasswordPrompt({
onClick={() => setShowPassword((s) => !s)}
tabIndex={-1}
aria-label={showPassword ? "Hide input" : "Show input"}
className="absolute right-2.5 top-1/2 -translate-y-1/2 text-muted-foreground/50 hover:text-muted-foreground transition-colors"
className="absolute right-2.5 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-muted-foreground transition-colors"
>
{showPassword ? (
<EyeOff className="h-3.5 w-3.5" />
@ -812,13 +812,13 @@ function TuiPasswordPrompt({
"flex h-8 items-center justify-center rounded-lg px-3 text-xs font-medium transition-all",
value
? "bg-primary text-primary-foreground hover:bg-primary/90 active:scale-95 shadow-sm"
: "bg-muted text-muted-foreground/40 cursor-not-allowed",
: "bg-muted text-muted-foreground cursor-not-allowed",
)}
>
Submit
</button>
</div>
<p className="mt-1.5 text-[10px] text-muted-foreground/50">
<p className="mt-1.5 text-[10px] text-muted-foreground">
Value is transmitted securely and not stored in chat history.
</p>
</div>
@ -910,7 +910,7 @@ function InlineThinking({ content, isStreaming }: { content: string; isStreaming
onClick={() => setExpanded((e) => !e)}
className={cn(
"group w-full rounded-xl border px-3.5 py-2.5 text-left transition-all",
"border-border/40 bg-muted/20 hover:bg-muted/30",
"border-border/50 bg-muted/50 hover:bg-muted/50",
)}
>
{/* Header row */}
@ -922,21 +922,21 @@ function InlineThinking({ content, isStreaming }: { content: string; isStreaming
</span>
) : (
<span className="flex h-4 w-4 flex-shrink-0 items-center justify-center rounded bg-muted-foreground/10">
<span className="text-[9px] text-muted-foreground/50">💭</span>
<span className="text-[9px] text-muted-foreground">💭</span>
</span>
)}
<span className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground/50">
<span className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
{isStreaming ? "Thinking…" : "Thought process"}
</span>
{hasMore && !expanded && (
<span className="ml-1 rounded-full bg-muted/60 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground/40">
<span className="ml-1 rounded-full bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
{lines.length} lines
</span>
)}
<span className="ml-auto flex-shrink-0">
{expanded
? <ChevronDown className="h-3 w-3 text-muted-foreground/40 transition-transform" />
: <ChevronRight className="h-3 w-3 text-muted-foreground/40 transition-transform group-hover:text-muted-foreground/60" />
? <ChevronDown className="h-3 w-3 text-muted-foreground transition-transform" />
: <ChevronRight className="h-3 w-3 text-muted-foreground transition-transform group-hover:text-muted-foreground" />
}
</span>
</div>
@ -945,7 +945,7 @@ function InlineThinking({ content, isStreaming }: { content: string; isStreaming
{!expanded && (
<div className="mt-2 space-y-0.5 border-l-2 border-muted-foreground/10 pl-3">
{previewLines.map((line, i) => (
<p key={i} className="text-[12px] leading-relaxed text-muted-foreground/50 line-clamp-1">
<p key={i} className="text-[12px] leading-relaxed text-muted-foreground line-clamp-1">
{line}
</p>
))}
@ -957,7 +957,7 @@ function InlineThinking({ content, isStreaming }: { content: string; isStreaming
{expanded && (
<div
ref={scrollRef}
className="mt-2 max-h-[400px] overflow-y-auto overscroll-contain rounded-lg border border-border/30 bg-background/40 p-3 text-[12px] leading-[1.7] text-muted-foreground/60 whitespace-pre-wrap scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent"
className="mt-2 max-h-[400px] overflow-y-auto overscroll-contain rounded-lg border border-border/50 bg-background/50 p-3 text-[12px] leading-[1.7] text-muted-foreground whitespace-pre-wrap scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent"
>
{content}
{isStreaming && <StreamingCursor />}
@ -991,7 +991,7 @@ function ChatBubble({
if (message.role === "system") {
return (
<div className="flex items-center justify-center py-1">
<span className="text-[11px] text-muted-foreground/60 italic px-3">
<span className="text-[11px] text-muted-foreground italic px-3">
{message.content}
</span>
</div>
@ -1047,7 +1047,7 @@ function ChatBubble({
<div className="mt-1 flex-shrink-0 flex h-7 w-7 items-center justify-center rounded-full bg-card border border-border">
<PlatformLogoIcon className="h-3.5 w-auto" />
</div>
<div className="max-w-[82%] min-w-0 rounded-2xl rounded-tl-md border border-border/60 bg-card px-4 py-3 shadow-sm">
<div className="max-w-[82%] min-w-0 rounded-2xl rounded-tl-md border border-border bg-card px-4 py-3 shadow-sm">
{/* Minimal waiting indicator — shown when streaming starts but no content yet */}
{isThinking && !message.content && (
<div className="flex items-center gap-2 py-1">
@ -1055,7 +1055,7 @@ function ChatBubble({
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-muted-foreground/30" />
<span className="relative inline-flex h-2 w-2 rounded-full bg-muted-foreground/50" />
</span>
<span className="text-[10px] font-medium text-muted-foreground/40 uppercase tracking-wider">
<span className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
Thinking
</span>
</div>
@ -1326,7 +1326,7 @@ function ChatInputBar({
const overflowGroups = useMemo(() => groupByCategory(OVERFLOW_ACTIONS), [])
return (
<div className="flex-shrink-0 border-t border-border bg-card/80 px-4 py-3 backdrop-blur-sm">
<div className="flex-shrink-0 border-t border-border bg-card px-4 py-3 backdrop-blur-sm">
<div
className="flex items-end gap-2"
onDrop={handleDrop}
@ -1339,8 +1339,8 @@ function ChatInputBar({
className={cn(
"flex flex-1 flex-col rounded-xl border bg-background transition-colors",
connected
? "border-border focus-within:border-border/80 focus-within:ring-1 focus-within:ring-border/30"
: "border-border/40 opacity-60",
? "border-border focus-within:ring-1 focus-within:ring-border/30"
: "border-border/50 opacity-80",
isDragging && connected && "border-primary/60 ring-2 ring-primary/20 bg-primary/5",
)}
>
@ -1367,7 +1367,7 @@ function ChatInputBar({
</div>
))}
{imageNotice && (
<span className="text-[10px] text-muted-foreground/70 italic">{imageNotice}</span>
<span className="text-[10px] text-muted-foreground italic">{imageNotice}</span>
)}
</div>
)}
@ -1386,12 +1386,12 @@ function ChatInputBar({
? "Message…"
: "Connecting…"
}
className="min-h-[40px] flex-1 resize-none bg-transparent px-3 py-2.5 text-sm text-foreground placeholder:text-muted-foreground/50 focus:outline-none disabled:cursor-not-allowed disabled:text-muted-foreground"
className="min-h-[40px] flex-1 resize-none bg-transparent px-3 py-2.5 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none disabled:cursor-not-allowed disabled:text-muted-foreground"
style={{ height: "40px", maxHeight: "160px", overflowY: "auto" }}
/>
<div className="flex flex-shrink-0 items-end pb-1.5 pr-1.5 gap-1">
{!connected && (
<span className="px-2 py-1 text-[10px] font-medium text-muted-foreground/60 uppercase tracking-wide">
<span className="px-2 py-1 text-[10px] font-medium text-muted-foreground uppercase tracking-wide">
Disconnected
</span>
)}
@ -1403,7 +1403,7 @@ function ChatInputBar({
"flex h-7 w-7 items-center justify-center rounded-lg transition-all",
hasContent && connected
? "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90 active:scale-95"
: "bg-muted text-muted-foreground/40 cursor-not-allowed",
: "bg-muted text-muted-foreground cursor-not-allowed",
)}
>
<SendHorizonal className="h-3.5 w-3.5" />
@ -1476,7 +1476,7 @@ function ChatInputBar({
{overflowGroups.map((group, gi) => (
<div key={group.category}>
{gi > 0 && <div className="my-1.5 border-t border-border/50" />}
<p className="px-2 py-1 text-[10px] font-semibold text-muted-foreground/60 uppercase tracking-wider">
<p className="px-2 py-1 text-[10px] font-semibold text-muted-foreground uppercase tracking-wider">
{group.label}
</p>
{group.items.map((action) => {
@ -1542,9 +1542,9 @@ function PlaceholderState({
<div className="flex flex-1 flex-col items-center justify-center text-center py-16">
<div className="flex h-12 w-12 items-center justify-center rounded-full border border-border bg-card">
{showSpinner ? (
<Loader2 className="h-5 w-5 animate-spin text-muted-foreground/70" />
<Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
) : (
<MessagesSquare className="h-6 w-6 text-muted-foreground/50" />
<MessagesSquare className="h-6 w-6 text-muted-foreground" />
)}
</div>
<div className="mt-3 space-y-1">
@ -1608,7 +1608,7 @@ function InlineUiRequest({ request }: { request: PendingUiRequest }) {
<div className="mt-1 flex-shrink-0 flex h-7 w-7 items-center justify-center rounded-full bg-card border border-border">
<PlatformLogoIcon className="h-3.5 w-auto" />
</div>
<div className="max-w-[82%] min-w-0 rounded-2xl rounded-tl-md border border-border/60 bg-card px-4 py-3 shadow-sm">
<div className="max-w-[82%] min-w-0 rounded-2xl rounded-tl-md border border-border bg-card px-4 py-3 shadow-sm">
{request.title && (
<p className="mb-2.5 text-sm font-medium text-foreground">{request.title}</p>
)}
@ -1675,7 +1675,7 @@ function InlineSelect({
disabled={disabled}
className={cn(
"flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-left text-sm transition-colors",
checked ? "bg-primary/15 text-primary font-medium" : "text-foreground hover:bg-muted/60",
checked ? "bg-primary/15 text-primary font-medium" : "text-foreground hover:bg-muted",
)}
>
<span className="flex h-4 w-4 flex-shrink-0 items-center justify-center rounded border border-border">
@ -1693,7 +1693,7 @@ function InlineSelect({
disabled={disabled}
className={cn(
"flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-left text-sm transition-colors",
selected ? "bg-primary/15 text-primary font-medium" : "text-foreground hover:bg-muted/60",
selected ? "bg-primary/15 text-primary font-medium" : "text-foreground hover:bg-muted",
)}
>
<span className="flex h-4 w-4 flex-shrink-0 items-center justify-center">
@ -1714,7 +1714,7 @@ function InlineSelect({
"mt-2 flex w-full items-center justify-center rounded-lg px-3 py-2 text-xs font-medium transition-all",
canSubmit && !disabled
? "bg-primary text-primary-foreground hover:bg-primary/90 active:scale-[0.98] shadow-sm"
: "bg-muted text-muted-foreground/40 cursor-not-allowed",
: "bg-muted text-muted-foreground cursor-not-allowed",
)}
>
{isMulti ? `Submit (${multiValues.size})` : "Submit"}
@ -1816,7 +1816,7 @@ function InlineInput({
"flex h-8 items-center justify-center rounded-lg px-3 text-xs font-medium transition-all",
value.trim() && !disabled
? "bg-primary text-primary-foreground hover:bg-primary/90 active:scale-95 shadow-sm"
: "bg-muted text-muted-foreground/40 cursor-not-allowed",
: "bg-muted text-muted-foreground cursor-not-allowed",
)}
>
Submit
@ -1927,12 +1927,12 @@ function ToolExecutionBlock({ tool }: { tool: CompletedToolExecution }) {
"w-full rounded-lg border px-3 py-2 text-left text-xs transition-colors",
isError
? "border-destructive/30 bg-destructive/5 hover:bg-destructive/10"
: "border-border/40 bg-muted/20 hover:bg-muted/30",
: "border-border/50 bg-muted/50 hover:bg-muted/50",
)}
>
{/* Header */}
<div className="flex items-center gap-2">
<span className={cn("flex-shrink-0", isError ? "text-destructive" : "text-muted-foreground/60")}>
<span className={cn("flex-shrink-0", isError ? "text-destructive" : "text-muted-foreground")}>
{icon}
</span>
<span className={cn("font-mono font-medium", isError ? "text-destructive" : "text-muted-foreground")}>
@ -1942,16 +1942,16 @@ function ToolExecutionBlock({ tool }: { tool: CompletedToolExecution }) {
<span className="truncate font-mono text-info/80">{shortPath}</span>
)}
{bashCommand && !shortPath && (
<span className="truncate font-mono text-muted-foreground/70">{bashCommand.length > 60 ? bashCommand.slice(0, 60) + "…" : bashCommand}</span>
<span className="truncate font-mono text-muted-foreground">{bashCommand.length > 60 ? bashCommand.slice(0, 60) + "…" : bashCommand}</span>
)}
<span className="ml-auto flex-shrink-0 text-muted-foreground/40">
<span className="ml-auto flex-shrink-0 text-muted-foreground">
{expanded ? <ChevronDown className="h-3 w-3" /> : <ChevronRight className="h-3 w-3" />}
</span>
</div>
{/* Expanded content */}
{expanded && diff && (
<div className="mt-2 overflow-x-auto rounded-md border border-border/30 bg-background/80 p-2 font-mono text-[11px] leading-relaxed">
<div className="mt-2 overflow-x-auto rounded-md border border-border/50 bg-background p-2 font-mono text-[11px] leading-relaxed">
{diff.split("\n").map((line, i) => {
const isAdd = line.startsWith("+")
const isRemove = line.startsWith("-")
@ -1963,8 +1963,8 @@ function ToolExecutionBlock({ tool }: { tool: CompletedToolExecution }) {
"whitespace-pre",
isAdd && "bg-success/10 text-success",
isRemove && "bg-destructive/10 text-destructive",
isContext && "text-muted-foreground/60",
!isAdd && !isRemove && !isContext && "text-muted-foreground/40",
isContext && "text-muted-foreground",
!isAdd && !isRemove && !isContext && "text-muted-foreground",
)}
>
{line}
@ -1976,7 +1976,7 @@ function ToolExecutionBlock({ tool }: { tool: CompletedToolExecution }) {
{/* Expanded: bash output or other result */}
{expanded && !diff && resultText && (
<div className="mt-2 max-h-[200px] overflow-y-auto rounded-md border border-border/30 bg-background/80 p-2 font-mono text-[11px] leading-relaxed text-muted-foreground/70 whitespace-pre-wrap">
<div className="mt-2 max-h-[200px] overflow-y-auto rounded-md border border-border/50 bg-background p-2 font-mono text-[11px] leading-relaxed text-muted-foreground whitespace-pre-wrap">
{resultText.length > 2000 ? resultText.slice(0, 2000) + "\n…" : resultText}
</div>
)}
@ -2291,8 +2291,8 @@ export function ChatPane({ className, onOpenAction }: ChatPaneProps) {
<div key={`active-${item.tool.id}`} className="flex justify-start gap-3">
<div className="w-7 flex-shrink-0" />
<div className="max-w-[82%] min-w-0">
<div className="flex items-center gap-2 rounded-lg border border-border/40 bg-muted/20 px-3.5 py-2">
<Loader2 className="h-3 w-3 animate-spin text-muted-foreground/60" />
<div className="flex items-center gap-2 rounded-lg border border-border/50 bg-muted/50 px-3.5 py-2">
<Loader2 className="h-3 w-3 animate-spin text-muted-foreground" />
<span className="font-mono text-xs text-muted-foreground">
{item.tool.name}
</span>

View file

@ -78,7 +78,7 @@ const darkTheme = createTheme({
selection: "oklch(0.2 0 0)",
lineHighlight: "oklch(0.12 0 0)",
gutterBackground: "oklch(0.09 0 0)",
gutterForeground: "oklch(0.35 0 0)",
gutterForeground: "oklch(0.42 0 0)",
gutterBorder: "transparent",
},
styles: darkStyles,

View file

@ -224,7 +224,7 @@ function SectionHeader({
return (
<div className="flex items-center justify-between gap-3 pb-4">
<div className="flex items-center gap-2.5">
<h3 className="text-[13px] font-semibold uppercase tracking-[0.08em] text-foreground/70">{title}</h3>
<h3 className="text-[13px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">{title}</h3>
{status}
</div>
{action}
@ -290,7 +290,7 @@ function SegmentedControl<T extends string>({
disabled?: boolean
}) {
return (
<div className="inline-flex rounded-lg border border-border/60 bg-card/30 p-0.5">
<div className="inline-flex rounded-lg border border-border bg-card/50 p-0.5">
{options.map((opt) => (
<button
key={opt.value}
@ -673,7 +673,7 @@ export function CommandSurface() {
<div className="space-y-4">
{Array.from(groupedModels.entries()).map(([provider, models]) => (
<div key={provider}>
<div className="mb-1.5 px-1 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground/60">
<div className="mb-1.5 px-1 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground">
{provider}
</div>
<div className="space-y-0.5">
@ -720,10 +720,10 @@ export function CommandSurface() {
{/* Badges */}
<div className="flex shrink-0 items-center gap-1.5">
{model.isCurrent && (
<span className="rounded bg-foreground/10 px-1.5 py-0.5 text-[10px] font-medium text-foreground/70">Active</span>
<span className="rounded bg-foreground/10 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">Active</span>
)}
{model.reasoning && (
<span className="rounded bg-foreground/10 px-1.5 py-0.5 text-[10px] font-medium text-foreground/70">Thinking</span>
<span className="rounded bg-foreground/10 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">Thinking</span>
)}
</div>
</button>
@ -738,7 +738,7 @@ export function CommandSurface() {
)}
{/* Apply */}
<div className="flex justify-end border-t border-border/40 pt-3">
<div className="flex justify-end border-t border-border/50 pt-3">
<Button
type="button"
size="sm"
@ -806,7 +806,7 @@ export function CommandSurface() {
})}
</div>
<div className="flex justify-end border-t border-border/40 pt-3">
<div className="flex justify-end border-t border-border/50 pt-3">
<Button
type="button"
size="sm"
@ -853,7 +853,7 @@ export function CommandSurface() {
)}
</div>
<div className="border-t border-border/30" />
<div className="border-t border-border/50" />
{/* Follow-up mode */}
<div className="space-y-3">
@ -1029,7 +1029,7 @@ export function CommandSurface() {
<LoaderCircle className="h-3.5 w-3.5 animate-spin" />
Loading diagnostics
</div>
<div className="flex flex-wrap gap-2 border-t border-border/30 pt-3" data-testid="command-surface-recovery-actions">
<div className="flex flex-wrap gap-2 border-t border-border/50 pt-3" data-testid="command-surface-recovery-actions">
<Button
type="button"
variant="default"
@ -1050,7 +1050,7 @@ export function CommandSurface() {
<div className="text-sm font-medium text-foreground">{diag.summary.label}</div>
<p className="text-xs text-muted-foreground">{diag.summary.detail}</p>
</div>
<div className="flex flex-wrap gap-2 border-t border-border/30 pt-3" data-testid="command-surface-recovery-actions">
<div className="flex flex-wrap gap-2 border-t border-border/50 pt-3" data-testid="command-surface-recovery-actions">
<Button
type="button"
variant="default"
@ -1117,7 +1117,7 @@ export function CommandSurface() {
<Badge variant={issue.severity === "error" ? "destructive" : "outline"} className="text-[10px]">{issue.code}</Badge>
</div>
<p className="mt-1 text-xs text-muted-foreground">{issue.message}</p>
{issue.suggestion && <p className="mt-0.5 text-[11px] text-muted-foreground/70"> {issue.suggestion}</p>}
{issue.suggestion && <p className="mt-0.5 text-[11px] text-muted-foreground"> {issue.suggestion}</p>}
</div>
))}
</div>
@ -1156,7 +1156,7 @@ export function CommandSurface() {
)}
{/* Actions */}
<div className="flex flex-wrap gap-2 border-t border-border/30 pt-3" data-testid="command-surface-recovery-actions">
<div className="flex flex-wrap gap-2 border-t border-border/50 pt-3" data-testid="command-surface-recovery-actions">
{diag.actions.browser.length > 0 ? (
diag.actions.browser.map((action) => (
<Button
@ -1179,7 +1179,7 @@ export function CommandSurface() {
</div>
{diag.actions.commands.length > 0 && (
<div className="space-y-2 border-t border-border/30 pt-3" data-testid="command-surface-recovery-commands">
<div className="space-y-2 border-t border-border/50 pt-3" data-testid="command-surface-recovery-commands">
<div className="text-xs font-medium text-muted-foreground">Suggested commands</div>
{diag.actions.commands.map((command) => (
<div key={command.command} className="rounded-lg border border-border/50 bg-card/50 px-3 py-2 text-xs">
@ -1259,7 +1259,7 @@ export function CommandSurface() {
<span className="font-mono">{shortenPath(result.project.repoRoot, 3)}</span>
{result.project.repoRelativePath && (
<>
<ChevronRight className="h-3 w-3 text-foreground/20" />
<ChevronRight className="h-3 w-3 text-muted-foreground" />
<span className="font-mono">{result.project.repoRelativePath}</span>
</>
)}
@ -1275,15 +1275,15 @@ export function CommandSurface() {
].map(({ label, count, active, color }) => (
<div key={label} className={cn(
"rounded-md border px-2 py-2 text-center transition-colors",
active ? "border-border/60 bg-card/80" : "border-border/30 bg-card/30",
active ? "border-border bg-card" : "border-border/50 bg-card/50",
)}>
<div className={cn(
"text-base font-semibold tabular-nums leading-none",
active ? color : "text-foreground/25",
active ? color : "text-muted-foreground",
)}>{count}</div>
<div className={cn(
"mt-1.5 text-[10px] leading-none",
active ? "text-muted-foreground" : "text-muted-foreground/50",
active ? "text-muted-foreground" : "text-muted-foreground",
)}>{label}</div>
</div>
))}
@ -1293,14 +1293,14 @@ export function CommandSurface() {
{result.changedFiles.length > 0 && (
<div data-testid="command-surface-git-files">
<div className="mb-2 flex items-center justify-between">
<span className="text-[11px] font-medium uppercase tracking-[0.06em] text-muted-foreground/70">
<span className="text-[11px] font-medium uppercase tracking-[0.06em] text-muted-foreground">
Changes
</span>
<span className="text-[11px] tabular-nums text-muted-foreground/50">
<span className="text-[11px] tabular-nums text-muted-foreground">
{result.changedFiles.length}{result.truncatedFileCount > 0 ? `+${result.truncatedFileCount}` : ""} files
</span>
</div>
<div className="space-y-px rounded-lg border border-border/40 bg-card/30 overflow-hidden">
<div className="space-y-px rounded-lg border border-border/50 bg-card/50 overflow-hidden">
{result.changedFiles.map((file) => (
<div
key={`${file.status}:${file.repoPath}`}
@ -1324,7 +1324,7 @@ export function CommandSurface() {
))}
</div>
{result.truncatedFileCount > 0 && (
<p className="mt-1.5 text-center text-[11px] text-muted-foreground/50">
<p className="mt-1.5 text-center text-[11px] text-muted-foreground">
+{result.truncatedFileCount} more files not shown
</p>
)}
@ -1392,7 +1392,7 @@ export function CommandSurface() {
<button
type="button"
className={cn(
"rounded-md border border-border/60 px-2.5 py-1.5 text-[11px] font-medium transition-colors",
"rounded-md border border-border px-2.5 py-1.5 text-[11px] font-medium transition-colors",
sessionBrowser.nameFilter === "named" ? "bg-foreground/10 text-foreground" : "text-muted-foreground hover:text-foreground",
)}
onClick={() => {
@ -1452,7 +1452,7 @@ export function CommandSurface() {
{session.name && session.firstMessage && (
<p className="mt-0.5 truncate text-xs text-muted-foreground">{session.firstMessage}</p>
)}
<div className="mt-0.5 flex gap-3 text-[11px] text-muted-foreground/70">
<div className="mt-0.5 flex gap-3 text-[11px] text-muted-foreground">
<span>{session.messageCount} msgs</span>
<span>{formatRelativeTime(session.modifiedAt)}</span>
</div>
@ -1473,7 +1473,7 @@ export function CommandSurface() {
{/* Rename controls */}
{renameMode && (
<div className="space-y-3 border-t border-border/30 pt-3">
<div className="space-y-3 border-t border-border/50 pt-3">
<div className="flex gap-2">
<Input
value={selectedNameTarget?.name ?? ""}
@ -1504,7 +1504,7 @@ export function CommandSurface() {
{/* Resume controls */}
{!renameMode && (
<div className="flex items-center justify-between border-t border-border/30 pt-3">
<div className="flex items-center justify-between border-t border-border/50 pt-3">
<span className="text-xs text-muted-foreground" data-testid="command-surface-resume-state">
{resumeBusy ? "Switching…" : commandSurface.resumeRequest.error ?? commandSurface.resumeRequest.result ?? "Select a session"}
</span>
@ -1563,7 +1563,7 @@ export function CommandSurface() {
{selected && <Check className="h-2.5 w-2.5 text-background" />}
</div>
<div className="min-w-0 flex-1">
<div className="font-mono text-[10px] text-muted-foreground/60">{message.entryId}</div>
<div className="font-mono text-[10px] text-muted-foreground">{message.entryId}</div>
<p className="mt-0.5 text-sm text-foreground">{message.text}</p>
</div>
</button>
@ -1574,7 +1574,7 @@ export function CommandSurface() {
<p className="py-4 text-center text-xs text-muted-foreground">No fork points available yet.</p>
)}
<div className="flex justify-end border-t border-border/40 pt-3">
<div className="flex justify-end border-t border-border/50 pt-3">
<Button
type="button"
size="sm"
@ -1647,7 +1647,7 @@ export function CommandSurface() {
)}
{/* Export */}
<div className="space-y-3 border-t border-border/30 pt-3">
<div className="space-y-3 border-t border-border/50 pt-3">
<div className="text-xs font-medium text-muted-foreground">Export</div>
<div className="flex gap-2">
<Input
@ -1779,7 +1779,7 @@ export function CommandSurface() {
</span>
</div>
{provider.recommended && (
<span className="rounded bg-foreground/10 px-1.5 py-0.5 text-[10px] font-medium text-foreground/70">Recommended</span>
<span className="rounded bg-foreground/10 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">Recommended</span>
)}
</button>
)
@ -1788,7 +1788,7 @@ export function CommandSurface() {
{/* Selected provider details */}
{selectedAuthProvider && (
<div className="space-y-4 border-t border-border/30 pt-3">
<div className="space-y-4 border-t border-border/50 pt-3">
<div className="flex items-center justify-between">
<div>
<div className="text-sm font-medium text-foreground">{selectedAuthProvider.label}</div>
@ -1899,7 +1899,7 @@ export function CommandSurface() {
{activeFlow.progress.length > 0 && (
<div className="space-y-1">
{activeFlow.progress.map((message, index) => (
<div key={`${activeFlow.flowId}-${index}`} className="rounded-md border border-border/40 bg-card/30 px-2.5 py-1.5 text-xs text-muted-foreground">
<div key={`${activeFlow.flowId}-${index}`} className="rounded-md border border-border/50 bg-card/50 px-2.5 py-1.5 text-xs text-muted-foreground">
{message}
</div>
))}
@ -1987,7 +1987,7 @@ export function CommandSurface() {
{/* Individual overrides — only visible when master is on */}
{devOverrides.enabled && (
<div className="space-y-2 rounded-lg border border-border/50 bg-card/30 p-3">
<div className="space-y-2 rounded-lg border border-border/50 bg-card/50 p-3">
<div className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">
Override shortcuts
</div>
@ -1999,7 +1999,7 @@ export function CommandSurface() {
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-foreground">{entry.label}</span>
<Badge variant="outline" className="border-border/60 font-mono text-[10px] text-muted-foreground">
<Badge variant="outline" className="border-border font-mono text-[10px] text-muted-foreground">
{entry.shortcutLabel}
</Badge>
</div>
@ -2016,7 +2016,7 @@ export function CommandSurface() {
)}
{/* Onboarding — one-click launch */}
<div className="rounded-lg border border-border/50 bg-card/30 p-3 space-y-3">
<div className="rounded-lg border border-border/50 bg-card/50 p-3 space-y-3">
<div className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">
Onboarding
</div>
@ -2046,7 +2046,7 @@ export function CommandSurface() {
</div>
</div>
<div className="rounded-lg border border-border/40 bg-card/30 px-3 py-2.5 text-xs text-muted-foreground">
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-2.5 text-xs text-muted-foreground">
This tab is only visible when running via{" "}
<code className="rounded bg-muted px-1 py-0.5 font-mono text-[11px]">npm run gsd:web</code>.
Overrides reset on page refresh.
@ -2061,7 +2061,7 @@ export function CommandSurface() {
case "model": return (
<div className="space-y-8">
{renderModelSection()}
<div className="border-t border-border/30 pt-6">
<div className="border-t border-border/50 pt-6">
{renderThinkingSection()}
</div>
</div>
@ -2069,7 +2069,7 @@ export function CommandSurface() {
case "thinking": return (
<div className="space-y-8">
{renderModelSection()}
<div className="border-t border-border/30 pt-6">
<div className="border-t border-border/50 pt-6">
{renderThinkingSection()}
</div>
</div>
@ -2077,10 +2077,10 @@ export function CommandSurface() {
case "session-behavior": return (
<div className="space-y-6">
{renderQueueSection()}
<div className="border-t border-border/30 pt-4">
<div className="border-t border-border/50 pt-4">
{renderCompactionSection()}
</div>
<div className="border-t border-border/30 pt-4">
<div className="border-t border-border/50 pt-4">
{renderRetrySection()}
</div>
</div>
@ -2089,10 +2089,10 @@ export function CommandSurface() {
case "queue": return (
<div className="space-y-6">
{renderQueueSection()}
<div className="border-t border-border/30 pt-4">
<div className="border-t border-border/50 pt-4">
{renderCompactionSection()}
</div>
<div className="border-t border-border/30 pt-4">
<div className="border-t border-border/50 pt-4">
{renderRetrySection()}
</div>
</div>
@ -2100,10 +2100,10 @@ export function CommandSurface() {
case "compaction": return (
<div className="space-y-6">
{renderQueueSection()}
<div className="border-t border-border/30 pt-4">
<div className="border-t border-border/50 pt-4">
{renderCompactionSection()}
</div>
<div className="border-t border-border/30 pt-4">
<div className="border-t border-border/50 pt-4">
{renderRetrySection()}
</div>
</div>
@ -2111,10 +2111,10 @@ export function CommandSurface() {
case "retry": return (
<div className="space-y-6">
{renderQueueSection()}
<div className="border-t border-border/30 pt-4">
<div className="border-t border-border/50 pt-4">
{renderCompactionSection()}
</div>
<div className="border-t border-border/30 pt-4">
<div className="border-t border-border/50 pt-4">
{renderRetrySection()}
</div>
</div>
@ -2188,7 +2188,7 @@ export function CommandSurface() {
const isClean = gitResult?.kind === "repo" && !hasChanges
return (
<div className="border-b border-border/40 px-5 py-4">
<div className="border-b border-border/50 px-5 py-4">
<div className="flex items-start justify-between gap-3">
<div className="flex items-center gap-3">
<div className={cn(
@ -2206,7 +2206,7 @@ export function CommandSurface() {
{branchName ?? "Git"}
</h2>
{branchName && mainBranch && branchName !== mainBranch && (
<span className="text-[11px] text-muted-foreground/50">from {mainBranch}</span>
<span className="text-[11px] text-muted-foreground">from {mainBranch}</span>
)}
</div>
{gitResult?.kind === "repo" && (
@ -2248,7 +2248,7 @@ export function CommandSurface() {
}
const renderDefaultHeader = () => (
<div className="flex items-center justify-between gap-3 border-b border-border/40 px-5 py-4">
<div className="flex items-center justify-between gap-3 border-b border-border/50 px-5 py-4">
<div>
<div className="text-xs uppercase tracking-wider text-muted-foreground">Command surface</div>
<div className="text-lg font-semibold text-foreground" data-testid="command-surface-title">
@ -2285,7 +2285,7 @@ export function CommandSurface() {
<div className="flex h-full min-h-0">
{/* ─── Left nav rail (hidden for single-section surfaces) ─── */}
{!isSingleSection && (
<nav className="flex w-12 shrink-0 flex-col items-center gap-0.5 border-r border-border/40 bg-card/30 py-3" data-testid="command-surface-sections">
<nav className="flex w-12 shrink-0 flex-col items-center gap-0.5 border-r border-border/50 bg-card/50 py-3" data-testid="command-surface-sections">
{surfaceSections.map((section) => {
const active = commandSurface.section === section
return (
@ -2320,7 +2320,7 @@ export function CommandSurface() {
{(commandSurface.lastResult || commandSurface.lastError) && (
<div
className={cn(
"border-b border-border/30 px-5 py-3 text-xs",
"border-b border-border/50 px-5 py-3 text-xs",
commandSurface.lastError ? "bg-destructive/5 text-destructive" : "bg-success/5 text-success",
)}
data-testid="command-surface-result"

View file

@ -82,11 +82,11 @@ function MetricCard({ label, value, subtext, icon }: MetricCardProps) {
function taskStatusIcon(status: ItemStatus) {
switch (status) {
case "done":
return <CheckCircle2 className="h-4 w-4 text-foreground/70" />
return <CheckCircle2 className="h-4 w-4 text-muted-foreground" />
case "in-progress":
return <Play className="h-4 w-4 text-foreground" />
case "pending":
return <Circle className="h-4 w-4 text-muted-foreground/50" />
return <Circle className="h-4 w-4 text-muted-foreground" />
}
}
@ -193,7 +193,7 @@ export function Dashboard({ onSwitchView, onExpandTerminal }: DashboardProps = {
<h1 className="text-base md:text-lg font-semibold shrink-0">Dashboard</h1>
{!isConnecting && scopeLabel && (
<>
<span className="hidden sm:inline text-lg font-thin text-muted-foreground/40 select-none">/</span>
<span className="hidden sm:inline text-lg font-thin text-muted-foreground select-none">/</span>
<span className="hidden sm:inline"><ScopeBadge label={scopeLabel} size="sm" /></span>
</>
)}
@ -351,7 +351,7 @@ export function Dashboard({ onSwitchView, onExpandTerminal }: DashboardProps = {
{task.title}
</span>
{status === "in-progress" && (
<span className="shrink-0 rounded-sm bg-foreground/10 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-foreground/70">
<span className="shrink-0 rounded-sm bg-foreground/10 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground">
active
</span>
)}

View file

@ -58,7 +58,7 @@ function DiagHeader({
return (
<div className="flex items-center justify-between gap-3 pb-4">
<div className="flex items-center gap-2.5">
<h3 className="text-[13px] font-semibold uppercase tracking-[0.08em] text-foreground/70">{title}</h3>
<h3 className="text-[13px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">{title}</h3>
{status}
{subtitle && <span className="text-[11px] text-muted-foreground">{subtitle}</span>}
</div>
@ -89,7 +89,7 @@ function DiagLoading({ label }: { label: string }) {
function DiagEmpty({ message }: { message: string }) {
return (
<div className="rounded-lg border border-border/30 bg-card/30 px-4 py-5 text-center text-xs text-muted-foreground">
<div className="rounded-lg border border-border/50 bg-card/50 px-4 py-5 text-center text-xs text-muted-foreground">
{message}
</div>
)
@ -102,7 +102,7 @@ function StatPill({ label, value, variant }: { label: string; value: number | st
variant === "error" && "border-destructive/20 bg-destructive/5 text-destructive",
variant === "warning" && "border-warning/20 bg-warning/5 text-warning",
variant === "info" && "border-info/20 bg-info/5 text-info",
(!variant || variant === "default") && "border-border/40 bg-card/50 text-foreground/80",
(!variant || variant === "default") && "border-border/50 bg-card/50 text-foreground/80",
)}>
<span className="text-muted-foreground">{label}</span>
<span className="font-medium tabular-nums">{value}</span>
@ -116,7 +116,7 @@ function StatPill({ label, value, variant }: { label: string; value: number | st
function AnomalyRow({ anomaly }: { anomaly: ForensicAnomaly }) {
return (
<div className="rounded-lg border border-border/30 bg-card/30 px-3 py-2.5 space-y-1">
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-2.5 space-y-1">
<div className="flex items-center gap-2">
<SeverityIcon severity={anomaly.severity} />
<Badge variant={severityBadgeVariant(anomaly.severity)} className="text-[10px] px-1.5 py-0">{anomaly.severity}</Badge>
@ -125,7 +125,7 @@ function AnomalyRow({ anomaly }: { anomaly: ForensicAnomaly }) {
<span className="text-[10px] text-muted-foreground font-mono truncate">{anomaly.unitType}/{anomaly.unitId}</span>
)}
</div>
<p className="text-xs text-foreground/90">{anomaly.summary}</p>
<p className="text-xs text-foreground">{anomaly.summary}</p>
{anomaly.details && anomaly.details !== anomaly.summary && (
<p className="text-[11px] text-muted-foreground leading-relaxed">{anomaly.details}</p>
)}
@ -187,7 +187,7 @@ export function ForensicsPanel() {
</div>
</div>
) : (
<div className="flex items-center gap-2 rounded-lg border border-border/30 bg-card/30 px-3 py-2 text-xs text-muted-foreground">
<div className="flex items-center gap-2 rounded-lg border border-border/50 bg-card/50 px-3 py-2 text-xs text-muted-foreground">
<CheckCircle2 className="h-3.5 w-3.5 text-success" />
No crash lock
</div>
@ -196,7 +196,7 @@ export function ForensicsPanel() {
{/* Anomalies */}
{data.anomalies.length > 0 ? (
<div className="space-y-2">
<h4 className="text-xs font-medium text-foreground/70">Anomalies ({data.anomalies.length})</h4>
<h4 className="text-xs font-medium text-muted-foreground">Anomalies ({data.anomalies.length})</h4>
{data.anomalies.map((a, i) => <AnomalyRow key={i} anomaly={a} />)}
</div>
) : (
@ -206,11 +206,11 @@ export function ForensicsPanel() {
{/* Recent units */}
{data.recentUnits.length > 0 && (
<div className="space-y-2">
<h4 className="text-xs font-medium text-foreground/70">Recent Units ({data.recentUnits.length})</h4>
<div className="overflow-x-auto rounded-lg border border-border/30">
<h4 className="text-xs font-medium text-muted-foreground">Recent Units ({data.recentUnits.length})</h4>
<div className="overflow-x-auto rounded-lg border border-border/50">
<table className="w-full text-[11px]">
<thead>
<tr className="border-b border-border/30 bg-card/40">
<tr className="border-b border-border/50 bg-card/50">
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Type</th>
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">ID</th>
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Model</th>
@ -220,7 +220,7 @@ export function ForensicsPanel() {
</thead>
<tbody>
{data.recentUnits.map((u, i) => (
<tr key={i} className="border-b border-border/20 last:border-0">
<tr key={i} className="border-b border-border/50 last:border-0">
<td className="px-2.5 py-1.5 font-mono text-foreground/80">{u.type}</td>
<td className="px-2.5 py-1.5 font-mono text-foreground/80 truncate max-w-[120px]">{u.id}</td>
<td className="px-2.5 py-1.5 text-muted-foreground">{u.model}</td>
@ -249,7 +249,7 @@ function humanizeCode(code: string): string {
function IssueRow({ issue }: { issue: DoctorIssue }) {
return (
<div className="rounded-lg border border-border/30 bg-card/30 px-3 py-2.5 space-y-1">
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-2.5 space-y-1">
<div className="flex items-center gap-2 flex-wrap">
<SeverityIcon severity={issue.severity} />
<Badge variant={severityBadgeVariant(issue.severity)} className="text-[10px] px-1.5 py-0">{issue.severity}</Badge>
@ -261,7 +261,7 @@ function IssueRow({ issue }: { issue: DoctorIssue }) {
</Badge>
)}
</div>
<p className="text-xs text-foreground/90">{issue.message}</p>
<p className="text-xs text-foreground">{issue.message}</p>
{issue.file && <p className="text-[10px] font-mono text-muted-foreground truncate">{issue.file}</p>}
</div>
)
@ -349,7 +349,7 @@ export function DoctorPanel() {
{/* Issue list */}
{data.issues.length > 0 ? (
<div className="space-y-2">
<h4 className="text-xs font-medium text-foreground/70">Issues ({data.issues.length})</h4>
<h4 className="text-xs font-medium text-muted-foreground">Issues ({data.issues.length})</h4>
{data.issues.map((issue, i) => <IssueRow key={i} issue={issue} />)}
</div>
) : (
@ -379,14 +379,14 @@ function trendColor(trend: "stable" | "rising" | "declining"): string {
function SuggestionRow({ suggestion }: { suggestion: SkillHealSuggestion }) {
return (
<div className="rounded-lg border border-border/30 bg-card/30 px-3 py-2.5 space-y-1">
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-2.5 space-y-1">
<div className="flex items-center gap-2 flex-wrap">
<SeverityIcon severity={suggestion.severity} />
<Badge variant={severityBadgeVariant(suggestion.severity)} className="text-[10px] px-1.5 py-0">{suggestion.severity}</Badge>
<span className="text-[11px] font-medium text-foreground/80">{suggestion.skillName}</span>
<Badge variant="outline" className="text-[10px] px-1.5 py-0 font-mono">{suggestion.trigger.replace(/_/g, " ")}</Badge>
</div>
<p className="text-xs text-foreground/90">{suggestion.message}</p>
<p className="text-xs text-foreground">{suggestion.message}</p>
</div>
)
}
@ -429,11 +429,11 @@ export function SkillHealthPanel() {
{/* Skill table */}
{data.skills.length > 0 && (
<div className="space-y-2">
<h4 className="text-xs font-medium text-foreground/70">Skills ({data.skills.length})</h4>
<div className="overflow-x-auto rounded-lg border border-border/30">
<h4 className="text-xs font-medium text-muted-foreground">Skills ({data.skills.length})</h4>
<div className="overflow-x-auto rounded-lg border border-border/50">
<table className="w-full text-[11px]">
<thead>
<tr className="border-b border-border/30 bg-card/40">
<tr className="border-b border-border/50 bg-card/50">
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Skill</th>
<th className="px-2.5 py-1.5 text-right font-medium text-muted-foreground">Uses</th>
<th className="px-2.5 py-1.5 text-right font-medium text-muted-foreground">Success</th>
@ -446,7 +446,7 @@ export function SkillHealthPanel() {
<tbody>
{data.skills.map((skill) => (
<tr key={skill.name} className={cn(
"border-b border-border/20 last:border-0",
"border-b border-border/50 last:border-0",
skill.flagged && "bg-destructive/3",
)}>
<td className="px-2.5 py-1.5 font-mono text-foreground/80">
@ -484,7 +484,7 @@ export function SkillHealthPanel() {
{/* Stale skills */}
{data.staleSkills.length > 0 && (
<div className="space-y-1.5">
<h4 className="text-xs font-medium text-foreground/70">Stale Skills</h4>
<h4 className="text-xs font-medium text-muted-foreground">Stale Skills</h4>
<div className="flex flex-wrap gap-1.5">
{data.staleSkills.map((name) => (
<Badge key={name} variant="secondary" className="text-[10px] font-mono">{name}</Badge>
@ -496,7 +496,7 @@ export function SkillHealthPanel() {
{/* Declining skills */}
{data.decliningSkills.length > 0 && (
<div className="space-y-1.5">
<h4 className="text-xs font-medium text-foreground/70">Declining Skills</h4>
<h4 className="text-xs font-medium text-muted-foreground">Declining Skills</h4>
<div className="flex flex-wrap gap-1.5">
{data.decliningSkills.map((name) => (
<Badge key={name} variant="destructive" className="text-[10px] font-mono">{name}</Badge>
@ -508,7 +508,7 @@ export function SkillHealthPanel() {
{/* Suggestions */}
{data.suggestions.length > 0 && (
<div className="space-y-2">
<h4 className="text-xs font-medium text-foreground/70">Suggestions ({data.suggestions.length})</h4>
<h4 className="text-xs font-medium text-muted-foreground">Suggestions ({data.suggestions.length})</h4>
{data.suggestions.map((s, i) => <SuggestionRow key={i} suggestion={s} />)}
</div>
)}

View file

@ -204,7 +204,7 @@ function PlainViewer({ content }: { content: string }) {
{lines.map((line, i) => (
<tr key={i} className="hover:bg-accent/20">
<td
className="select-none pr-4 text-right text-muted-foreground/40 align-top"
className="select-none pr-4 text-right text-muted-foreground align-top"
style={{ minWidth: `${gutterWidth + 1}ch` }}
>
{i + 1}
@ -313,7 +313,7 @@ function MarkdownViewer({ content, filepath, shikiTheme = "github-dark-default"
},
img({ src, alt }) {
return (
<span className="my-2 block rounded border border-border bg-muted/20 px-3 py-2 text-xs text-muted-foreground italic">
<span className="my-2 block rounded border border-border bg-muted/50 px-3 py-2 text-xs text-muted-foreground italic">
🖼 {alt || (typeof src === "string" ? src : "") || "image"}
</span>
)
@ -485,7 +485,7 @@ function InlineDiffViewer({ before, after, onDismiss }: { before: string; after:
"select-none pr-3 text-right align-top min-w-[3ch]",
line.type === "add" ? "text-emerald-400/40" :
line.type === "remove" ? "text-red-400/40" :
"text-muted-foreground/30",
"text-muted-foreground/50",
)}
>
{line.lineNum ?? ""}
@ -495,8 +495,8 @@ function InlineDiffViewer({ before, after, onDismiss }: { before: string; after:
"whitespace-pre pr-4",
line.type === "add" && "text-emerald-300",
line.type === "remove" && "text-red-300 line-through decoration-red-400/30",
line.type === "context" && line.text === "···" && "text-muted-foreground/30 text-center italic",
line.type === "context" && line.text !== "···" && "text-muted-foreground/70",
line.type === "context" && line.text === "···" && "text-muted-foreground/50 text-center italic",
line.type === "context" && line.text !== "···" && "text-muted-foreground",
)}
>
{line.text || " "}

View file

@ -82,7 +82,7 @@ function SelectRenderer({
{request.options.map((option) => (
<label
key={option}
className="flex cursor-pointer items-center gap-3 rounded-lg border border-border/70 bg-background/70 px-3 py-2.5 transition-colors hover:bg-accent/40"
className="flex cursor-pointer items-center gap-3 rounded-lg border border-border bg-background px-3 py-2.5 transition-colors hover:bg-accent/40"
>
<Checkbox
checked={multiValues.has(option)}
@ -115,7 +115,7 @@ function SelectRenderer({
{request.options.map((option) => (
<label
key={option}
className="flex cursor-pointer items-center gap-3 rounded-lg border border-border/70 bg-background/70 px-3 py-2.5 transition-colors hover:bg-accent/40"
className="flex cursor-pointer items-center gap-3 rounded-lg border border-border bg-background px-3 py-2.5 transition-colors hover:bg-accent/40"
>
<RadioGroupItem value={option} id={`select-${option}`} />
<Label htmlFor={`select-${option}`} className="cursor-pointer text-sm font-normal">
@ -145,7 +145,7 @@ function ConfirmRenderer({
}) {
return (
<div className="space-y-4">
<div className="rounded-lg border border-border/70 bg-background/70 px-4 py-3 text-sm leading-relaxed">
<div className="rounded-lg border border-border bg-background px-4 py-3 text-sm leading-relaxed">
{request.message}
</div>
<div className="flex gap-3">

View file

@ -53,7 +53,7 @@ function PanelHeader({
return (
<div className="flex items-center justify-between gap-3 pb-4">
<div className="flex items-center gap-2.5">
<h3 className="text-[13px] font-semibold uppercase tracking-[0.08em] text-foreground/70">{title}</h3>
<h3 className="text-[13px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">{title}</h3>
{status}
{subtitle && <span className="text-[11px] text-muted-foreground">{subtitle}</span>}
</div>
@ -84,7 +84,7 @@ function PanelLoading({ label }: { label: string }) {
function PanelEmpty({ message }: { message: string }) {
return (
<div className="rounded-lg border border-border/30 bg-card/30 px-4 py-5 text-center text-xs text-muted-foreground">
<div className="rounded-lg border border-border/50 bg-card/50 px-4 py-5 text-center text-xs text-muted-foreground">
{message}
</div>
)
@ -97,7 +97,7 @@ function StatPill({ label, value, variant }: { label: string; value: number | st
variant === "error" && "border-destructive/20 bg-destructive/5 text-destructive",
variant === "warning" && "border-warning/20 bg-warning/5 text-warning",
variant === "info" && "border-info/20 bg-info/5 text-info",
(!variant || variant === "default") && "border-border/40 bg-card/50 text-foreground/80",
(!variant || variant === "default") && "border-border/50 bg-card/50 text-foreground/80",
)}>
<span className="text-muted-foreground">{label}</span>
<span className="font-medium tabular-nums">{value}</span>
@ -181,12 +181,12 @@ const CLASSIFICATION_OPTIONS: Classification[] = ["quick-task", "inject", "defer
function KnowledgeEntryRow({ entry }: { entry: KnowledgeEntry }) {
const badge = knowledgeTypeBadge(entry.type)
return (
<div className="group rounded-lg border border-border/30 bg-card/20 px-3 py-2.5 transition-colors hover:bg-card/40">
<div className="group rounded-lg border border-border/50 bg-card/50 px-3 py-2.5 transition-colors hover:bg-card/50">
<div className="flex items-start gap-2.5">
<KnowledgeTypeIcon type={entry.type} className="mt-0.5" />
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="text-xs font-medium text-foreground/90 truncate">{entry.title}</span>
<span className="text-xs font-medium text-foreground truncate">{entry.title}</span>
<Badge variant="outline" className={cn("text-[10px] px-1.5 py-0 h-4 shrink-0", badge.className)}>
{badge.label}
</Badge>
@ -231,7 +231,7 @@ function KnowledgeTabContent({
))}
</div>
{data.lastModified && (
<p className="pt-2 text-[10px] text-muted-foreground/60">
<p className="pt-2 text-[10px] text-muted-foreground">
Last modified: {new Date(data.lastModified).toLocaleString()}
</p>
)}
@ -255,7 +255,7 @@ function CaptureEntryRow({
const status = captureStatusStyle(entry.status)
return (
<div className="group rounded-lg border border-border/30 bg-card/20 px-3 py-2.5 transition-colors hover:bg-card/40">
<div className="group rounded-lg border border-border/50 bg-card/50 px-3 py-2.5 transition-colors hover:bg-card/50">
<div className="flex items-start gap-2.5">
<div className={cn(
"mt-1 h-2 w-2 shrink-0 rounded-full",
@ -265,24 +265,24 @@ function CaptureEntryRow({
)} />
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2 flex-wrap">
<span className="text-xs text-foreground/90">{entry.text}</span>
<span className="text-xs text-foreground">{entry.text}</span>
<Badge variant="outline" className={cn("text-[10px] px-1.5 py-0 h-4 shrink-0", status.className)}>
{status.label}
</Badge>
{entry.classification && (
<Badge variant="outline" className="text-[10px] px-1.5 py-0 h-4 shrink-0 border-border/40 text-muted-foreground">
<Badge variant="outline" className="text-[10px] px-1.5 py-0 h-4 shrink-0 border-border/50 text-muted-foreground">
{classificationLabel(entry.classification)}
</Badge>
)}
</div>
{entry.timestamp && (
<div className="mt-1 flex items-center gap-1 text-[10px] text-muted-foreground/60">
<div className="mt-1 flex items-center gap-1 text-[10px] text-muted-foreground">
<Clock className="h-2.5 w-2.5" />
{entry.timestamp}
</div>
)}
{entry.resolution && (
<p className="mt-1 text-[10px] text-muted-foreground/70 italic">{entry.resolution}</p>
<p className="mt-1 text-[10px] text-muted-foreground italic">{entry.resolution}</p>
)}
{entry.status === "pending" && (
<div className="mt-2 flex flex-wrap gap-1">
@ -294,7 +294,7 @@ function CaptureEntryRow({
size="sm"
disabled={resolvePending}
onClick={() => onResolve(entry.id, c)}
className="h-6 gap-1 px-2 text-[10px] font-normal border-border/40 hover:bg-foreground/5"
className="h-6 gap-1 px-2 text-[10px] font-normal border-border/50 hover:bg-foreground/5"
>
<ClassificationIcon classification={c} />
{classificationLabel(c)}
@ -397,7 +397,7 @@ export function KnowledgeCapturesPanel({ initialTab }: KnowledgeCapturesPanelPro
return (
<div className="space-y-0">
{/* Tab bar */}
<div className="flex items-center gap-0.5 border-b border-border/30 px-1">
<div className="flex items-center gap-0.5 border-b border-border/50 px-1">
<button
type="button"
onClick={() => setActiveTab("knowledge")}
@ -405,7 +405,7 @@ export function KnowledgeCapturesPanel({ initialTab }: KnowledgeCapturesPanelPro
"flex items-center gap-1.5 px-3 py-2 text-xs font-medium transition-all border-b-2 -mb-px",
activeTab === "knowledge"
? "border-foreground/60 text-foreground"
: "border-transparent text-muted-foreground hover:text-foreground/70",
: "border-transparent text-muted-foreground hover:text-muted-foreground",
)}
>
<BookOpen className="h-3.5 w-3.5" />
@ -418,7 +418,7 @@ export function KnowledgeCapturesPanel({ initialTab }: KnowledgeCapturesPanelPro
"flex items-center gap-1.5 px-3 py-2 text-xs font-medium transition-all border-b-2 -mb-px",
activeTab === "captures"
? "border-foreground/60 text-foreground"
: "border-transparent text-muted-foreground hover:text-foreground/70",
: "border-transparent text-muted-foreground hover:text-muted-foreground",
)}
>
<InboxIcon className="h-3.5 w-3.5" />

View file

@ -451,7 +451,7 @@ export function MainSessionTerminal({ className, fontSize, projectCwd }: MainSes
)}
{/* Drop overlay */}
{isDragOver && (
<div className="absolute inset-0 z-20 flex flex-col items-center justify-center gap-2 bg-background/80 backdrop-blur-sm border-2 border-dashed border-primary rounded-md pointer-events-none">
<div className="absolute inset-0 z-20 flex flex-col items-center justify-center gap-2 bg-background backdrop-blur-sm border-2 border-dashed border-primary rounded-md pointer-events-none">
<ImagePlus className="h-8 w-8 text-primary" />
<span className="text-sm font-medium text-primary">Drop image here</span>
</div>

View file

@ -180,7 +180,7 @@ export function OnboardingGate() {
{/* Right — step label */}
<div className="flex w-24 justify-end">
<span className="text-xs text-muted-foreground/60">{stepLabel}</span>
<span className="text-xs text-muted-foreground">{stepLabel}</span>
</div>
</header>

View file

@ -228,7 +228,7 @@ export function StepAuthenticate({
{/* ─── API key form ─── */}
{hasApiKey && !canProceed && (
<div className="space-y-3 rounded-xl border border-border/40 bg-card/30 p-4">
<div className="space-y-3 rounded-xl border border-border/50 bg-card/50 p-4">
<div className="text-sm font-medium text-foreground">API key</div>
<form
className="space-y-3"
@ -276,15 +276,15 @@ export function StepAuthenticate({
{/* Divider between API key and OAuth */}
{hasApiKey && (
<div className="flex items-center gap-3 py-1">
<div className="h-px flex-1 bg-border/40" />
<span className="text-xs text-muted-foreground/50">or</span>
<div className="h-px flex-1 bg-border/40" />
<div className="h-px flex-1 bg-border/50" />
<span className="text-xs text-muted-foreground">or</span>
<div className="h-px flex-1 bg-border/50" />
</div>
)}
{/* ─── No active flow: show start button ─── */}
{!flowActive && (
<div className="rounded-xl border border-border/40 bg-card/30 p-4">
<div className="rounded-xl border border-border/50 bg-card/50 p-4">
<div className="flex items-center justify-between gap-3">
<div>
<div className="text-sm font-medium text-foreground">Browser sign-in</div>
@ -316,7 +316,7 @@ export function StepAuthenticate({
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
className="rounded-xl border border-border/40 bg-card/30 p-4 space-y-4"
className="rounded-xl border border-border/50 bg-card/50 p-4 space-y-4"
data-testid="onboarding-active-flow"
>
{/* Device code — big and prominent */}
@ -326,12 +326,12 @@ export function StepAuthenticate({
<button
type="button"
onClick={() => copyCode(deviceCode)}
className="group flex items-center gap-3 rounded-lg border border-border/60 bg-background/50 px-5 py-3 transition-colors hover:border-foreground/20 active:scale-[0.98]"
className="group flex items-center gap-3 rounded-lg border border-border bg-background/50 px-5 py-3 transition-colors hover:border-foreground/20 active:scale-[0.98]"
>
<span className="font-mono text-2xl font-bold tracking-[0.15em] text-foreground">
{deviceCode}
</span>
<span className="text-muted-foreground/40 transition-colors group-hover:text-muted-foreground">
<span className="text-muted-foreground transition-colors group-hover:text-muted-foreground">
{copied ? (
<CheckCircle2 className="h-4 w-4 text-success" />
) : (
@ -339,7 +339,7 @@ export function StepAuthenticate({
)}
</span>
</button>
<div className="text-[11px] text-muted-foreground/50">
<div className="text-[11px] text-muted-foreground">
{copied ? "Copied!" : "Click to copy"}
</div>
</div>
@ -402,7 +402,7 @@ export function StepAuthenticate({
size="sm"
onClick={() => onCancelFlow(activeFlow.flowId)}
disabled={isBusy}
className="h-7 text-xs text-muted-foreground/60"
className="h-7 text-xs text-muted-foreground"
>
Cancel
</Button>
@ -412,7 +412,7 @@ export function StepAuthenticate({
{/* Generic prompt input (non-device-code) */}
{activeFlow.prompt && !deviceCode && (
<form
className="space-y-2 border-t border-border/30 pt-3"
className="space-y-2 border-t border-border/50 pt-3"
onSubmit={(e) => {
e.preventDefault()
if (!activeFlow.prompt?.allowEmpty && !flowInput.trim()) return
@ -446,9 +446,9 @@ export function StepAuthenticate({
{/* Progress messages */}
{activeFlow.progress.length > 0 && (
<div className="space-y-1 border-t border-border/30 pt-3">
<div className="space-y-1 border-t border-border/50 pt-3">
{activeFlow.progress.map((message, i) => (
<div key={`${activeFlow.flowId}-${i}`} className="text-xs text-muted-foreground/60">
<div key={`${activeFlow.flowId}-${i}`} className="text-xs text-muted-foreground">
{message}
</div>
))}
@ -461,7 +461,7 @@ export function StepAuthenticate({
{/* OAuth unavailable */}
{provider.supports.oauth && !provider.supports.oauthAvailable && !hasApiKey && (
<div className="rounded-xl border border-border/40 bg-card/30 px-4 py-3.5 text-sm text-muted-foreground">
<div className="rounded-xl border border-border/50 bg-card/50 px-4 py-3.5 text-sm text-muted-foreground">
Browser sign-in is not available in this runtime. Go back and choose a provider with API-key support.
</div>
)}

View file

@ -72,9 +72,9 @@ function InlineFolderBrowser({
}, [browse])
return (
<div className="rounded-xl border border-border/40 bg-card/20 overflow-hidden">
<div className="rounded-xl border border-border/50 bg-card/50 overflow-hidden">
{/* Current path */}
<div className="flex items-center justify-between gap-2 border-b border-border/30 px-4 py-2.5">
<div className="flex items-center justify-between gap-2 border-b border-border/50 px-4 py-2.5">
<p className="min-w-0 truncate font-mono text-xs text-muted-foreground" title={currentPath}>
{currentPath}
</p>
@ -123,7 +123,7 @@ function InlineFolderBrowser({
>
<Folder className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
<span className="min-w-0 flex-1 truncate text-foreground">{entry.name}</span>
<ChevronRight className="h-3 w-3 shrink-0 text-muted-foreground/30 opacity-0 transition-opacity group-hover:opacity-100" />
<ChevronRight className="h-3 w-3 shrink-0 text-muted-foreground/50 opacity-0 transition-opacity group-hover:opacity-100" />
</button>
))}
@ -138,7 +138,7 @@ function InlineFolderBrowser({
</ScrollArea>
{/* Cancel */}
<div className="border-t border-border/30 px-4 py-2">
<div className="border-t border-border/50 px-4 py-2">
<Button
type="button"
variant="ghost"
@ -306,7 +306,7 @@ export function StepDevRoot({ onNext, onBack }: StepDevRootProps) {
"active:scale-[0.96]",
path === suggestion
? "border-foreground/25 bg-foreground/10 text-foreground"
: "border-border/40 text-muted-foreground hover:border-foreground/15 hover:text-foreground",
: "border-border/50 text-muted-foreground hover:border-foreground/15 hover:text-foreground",
)}
>
{suggestion}
@ -337,7 +337,7 @@ export function StepDevRoot({ onNext, onBack }: StepDevRootProps) {
<Button
variant="ghost"
onClick={onNext}
className="gap-1.5 text-muted-foreground/70 transition-transform active:scale-[0.96]"
className="gap-1.5 text-muted-foreground transition-transform active:scale-[0.96]"
data-testid="onboarding-devroot-skip"
>
Skip

View file

@ -82,7 +82,7 @@ export function StepMode({ selected, onSelect, onNext, onBack }: StepModeProps)
"active:scale-[0.98]",
isSelected
? "border-foreground/30 bg-foreground/[0.06] shadow-[0_0_0_1px_rgba(255,255,255,0.06)]"
: "border-border/50 bg-card/30 hover:border-foreground/15 hover:bg-card/60",
: "border-border/50 bg-card/50 hover:border-foreground/15 hover:bg-card/50",
)}
data-testid={`onboarding-mode-${opt.id}`}
>
@ -141,7 +141,7 @@ export function StepMode({ selected, onSelect, onNext, onBack }: StepModeProps)
<span
className={cn(
"ml-2 text-xs font-medium transition-colors duration-200",
isSelected ? "text-foreground/50" : "text-muted-foreground/50",
isSelected ? "text-muted-foreground" : "text-muted-foreground",
)}
>
{opt.tagline}
@ -149,7 +149,7 @@ export function StepMode({ selected, onSelect, onNext, onBack }: StepModeProps)
</div>
{/* Description */}
<p className="mt-2 text-[13px] leading-relaxed text-muted-foreground/80">
<p className="mt-2 text-[13px] leading-relaxed text-muted-foreground">
{opt.description}
</p>
</button>

View file

@ -64,7 +64,7 @@ export function StepOptional({ sections, onBack, onNext }: StepOptionalProps) {
"flex items-start gap-3.5 rounded-xl border px-4 py-3.5 transition-colors",
section.configured
? "border-success/15 bg-success/[0.03]"
: "border-border/40 bg-card/20",
: "border-border/50 bg-card/50",
)}
data-testid={`onboarding-optional-${section.id}`}
>
@ -74,7 +74,7 @@ export function StepOptional({ sections, onBack, onNext }: StepOptionalProps) {
"mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full",
section.configured
? "bg-success/15 text-success"
: "bg-foreground/[0.05] text-muted-foreground/40",
: "bg-foreground/[0.05] text-muted-foreground",
)}
>
{section.configured ? (
@ -95,7 +95,7 @@ export function StepOptional({ sections, onBack, onNext }: StepOptionalProps) {
"text-[10px]",
section.configured
? "border-success/15 text-success/70"
: "border-border/40 text-muted-foreground/50",
: "border-border/50 text-muted-foreground",
)}
>
{section.configured ? "Ready" : "Skipped"}
@ -115,7 +115,7 @@ export function StepOptional({ sections, onBack, onNext }: StepOptionalProps) {
<Badge
key={item}
variant="outline"
className="border-border/30 text-[10px] text-muted-foreground/60"
className="border-border/50 text-[10px] text-muted-foreground"
>
{item}
</Badge>
@ -124,7 +124,7 @@ export function StepOptional({ sections, onBack, onNext }: StepOptionalProps) {
)}
{section.configuredItems.length === 0 && (
<p className="mt-0.5 text-xs text-muted-foreground/50">
<p className="mt-0.5 text-xs text-muted-foreground">
Not configured add later from settings.
</p>
)}

View file

@ -248,7 +248,7 @@ export function StepProject({ onFinish, onBack, onBeforeSwitch }: StepProjectPro
)}
{noDevRoot && (
<div className="rounded-xl border border-border/40 bg-card/30 px-4 py-6 text-center text-sm text-muted-foreground">
<div className="rounded-xl border border-border/50 bg-card/50 px-4 py-6 text-center text-sm text-muted-foreground">
No dev root configured. Go back and set one, or finish setup to configure later.
</div>
)}
@ -278,7 +278,7 @@ export function StepProject({ onFinish, onBack, onBeforeSwitch }: StepProjectPro
"active:scale-[0.98]",
isSwitching
? "border-foreground/30 bg-foreground/[0.06]"
: "border-border/40 bg-card/20 hover:border-foreground/15 hover:bg-card/50",
: "border-border/50 bg-card/50 hover:border-foreground/15 hover:bg-card/50",
switchingTo && !isSwitching && "opacity-40 pointer-events-none",
)}
>
@ -288,7 +288,7 @@ export function StepProject({ onFinish, onBack, onBeforeSwitch }: StepProjectPro
project.kind === "active-gsd" ? "bg-success/10" : "bg-foreground/[0.04]",
)}>
{isSwitching ? (
<Loader2 className="h-4 w-4 animate-spin text-foreground/60" />
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
) : (
<KindIcon className={cn("h-4 w-4", style.color)} />
)}
@ -310,7 +310,7 @@ export function StepProject({ onFinish, onBack, onBeforeSwitch }: StepProjectPro
{stack.map((tag) => (
<span
key={tag}
className="rounded bg-foreground/[0.04] px-1.5 py-0.5 text-[10px] text-muted-foreground/60"
className="rounded bg-foreground/[0.04] px-1.5 py-0.5 text-[10px] text-muted-foreground"
>
{tag}
</span>
@ -320,7 +320,7 @@ export function StepProject({ onFinish, onBack, onBeforeSwitch }: StepProjectPro
{/* Row 3: progress info (for active-gsd projects) */}
{progress && (
<div className="mt-1.5 text-[11px] text-muted-foreground/50">
<div className="mt-1.5 text-[11px] text-muted-foreground">
{progress}
</div>
)}
@ -336,7 +336,7 @@ export function StepProject({ onFinish, onBack, onBeforeSwitch }: StepProjectPro
}}
/>
</div>
<span className="text-[10px] tabular-nums text-muted-foreground/40">
<span className="text-[10px] tabular-nums text-muted-foreground">
{milestoneCount}
</span>
</div>
@ -344,7 +344,7 @@ export function StepProject({ onFinish, onBack, onBeforeSwitch }: StepProjectPro
</div>
{/* Arrow */}
<ArrowRight className="mt-1 h-4 w-4 shrink-0 text-muted-foreground/20 transition-all group-hover:text-muted-foreground/60 group-hover:translate-x-0.5" />
<ArrowRight className="mt-1 h-4 w-4 shrink-0 text-muted-foreground/50 transition-all group-hover:text-muted-foreground group-hover:translate-x-0.5" />
</button>
)
})}
@ -352,7 +352,7 @@ export function StepProject({ onFinish, onBack, onBeforeSwitch }: StepProjectPro
)}
{!loading && devRoot && projects.length === 0 && !error && (
<div className="rounded-xl border border-border/40 bg-card/30 px-4 py-6 text-center text-sm text-muted-foreground">
<div className="rounded-xl border border-border/50 bg-card/50 px-4 py-6 text-center text-sm text-muted-foreground">
No projects found in {devRoot}
</div>
)}
@ -367,7 +367,7 @@ export function StepProject({ onFinish, onBack, onBeforeSwitch }: StepProjectPro
disabled={!!switchingTo}
className={cn(
"flex w-full items-center gap-3.5 rounded-xl border border-dashed px-4 py-3.5 text-left transition-all duration-200",
"border-border/40 text-muted-foreground hover:border-foreground/15 hover:text-foreground",
"border-border/50 text-muted-foreground hover:border-foreground/15 hover:text-foreground",
"active:scale-[0.98]",
switchingTo && "opacity-40 pointer-events-none",
)}
@ -377,7 +377,7 @@ export function StepProject({ onFinish, onBack, onBeforeSwitch }: StepProjectPro
</div>
<div>
<span className="text-sm font-medium">Create new project</span>
<p className="mt-0.5 text-[11px] text-muted-foreground/50">Initialize a new directory with Git</p>
<p className="mt-0.5 text-[11px] text-muted-foreground">Initialize a new directory with Git</p>
</div>
</button>
) : (
@ -385,7 +385,7 @@ export function StepProject({ onFinish, onBack, onBeforeSwitch }: StepProjectPro
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
transition={{ duration: 0.2 }}
className="rounded-xl border border-border/40 bg-card/30 p-4 space-y-3"
className="rounded-xl border border-border/50 bg-card/50 p-4 space-y-3"
>
<div className="text-sm font-medium text-foreground">New project</div>
<form
@ -411,7 +411,7 @@ export function StepProject({ onFinish, onBack, onBeforeSwitch }: StepProjectPro
<p className="text-xs text-destructive">{createError}</p>
)}
{newName && nameValid && !nameConflict && (
<p className="font-mono text-xs text-muted-foreground/40">{devRoot}/{newName}</p>
<p className="font-mono text-xs text-muted-foreground">{devRoot}/{newName}</p>
)}
<div className="flex items-center gap-2 pt-1">
<Button

View file

@ -79,7 +79,7 @@ export function StepProvider({ providers, selectedId, onSelect, onNext, onBack }
>
{groups.map((group) => (
<div key={group.label}>
<div className="mb-2 px-0.5 text-[11px] font-medium uppercase tracking-widest text-muted-foreground/50">
<div className="mb-2 px-0.5 text-[11px] font-medium uppercase tracking-widest text-muted-foreground">
{group.label}
</div>
<div className="grid gap-2 sm:grid-cols-2">
@ -96,7 +96,7 @@ export function StepProvider({ providers, selectedId, onSelect, onNext, onBack }
"active:scale-[0.98]",
selected
? "border-foreground/30 bg-foreground/[0.06]"
: "border-border/40 bg-card/20 hover:border-foreground/15 hover:bg-card/50",
: "border-border/50 bg-card/50 hover:border-foreground/15 hover:bg-card/50",
)}
data-testid={`onboarding-provider-${provider.id}`}
>
@ -116,7 +116,7 @@ export function StepProvider({ providers, selectedId, onSelect, onNext, onBack }
<div className="flex items-center gap-2">
<span className="text-sm font-semibold text-foreground">{provider.label}</span>
{provider.recommended && (
<Badge variant="outline" className="border-foreground/10 bg-foreground/[0.03] text-[9px] text-foreground/50">
<Badge variant="outline" className="border-foreground/10 bg-foreground/[0.03] text-[9px] text-muted-foreground">
Recommended
</Badge>
)}
@ -129,7 +129,7 @@ export function StepProvider({ providers, selectedId, onSelect, onNext, onBack }
<span>{configuredViaLabel(provider.configuredVia)}</span>
</>
) : (
<span className="text-muted-foreground/50">Not configured</span>
<span className="text-muted-foreground">Not configured</span>
)}
</div>
</div>
@ -138,7 +138,7 @@ export function StepProvider({ providers, selectedId, onSelect, onNext, onBack }
{capabilityBadges(provider).map((cap) => (
<Tooltip key={cap}>
<TooltipTrigger asChild>
<Badge variant="outline" className="border-border/30 text-[10px] text-muted-foreground/60">
<Badge variant="outline" className="border-border/50 text-[10px] text-muted-foreground">
{cap}
</Badge>
</TooltipTrigger>

View file

@ -51,7 +51,7 @@ export function StepReady({ providerLabel, onFinish }: StepReadyProps) {
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.26, duration: 0.4 }}
className="mt-8 flex items-center gap-4 rounded-xl border border-border/40 bg-card/30 px-5 py-3"
className="mt-8 flex items-center gap-4 rounded-xl border border-border/50 bg-card/50 px-5 py-3"
>
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<Image
@ -70,7 +70,7 @@ export function StepReady({ providerLabel, onFinish }: StepReadyProps) {
/>
<span>Shell unlocked</span>
</div>
<div className="h-3 w-px bg-border/60" />
<div className="h-3 w-px bg-border" />
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
<span className="h-1.5 w-1.5 rounded-full bg-success" />
<span>{providerLabel}</span>

View file

@ -207,7 +207,7 @@ export function StepRemote({ onBack, onNext }: StepRemoteProps) {
{/* Channel picker */}
{!loading && (
<div className="space-y-2">
<div className="text-xs font-medium text-muted-foreground/60">Channel</div>
<div className="text-xs font-medium text-muted-foreground">Channel</div>
<div className="grid grid-cols-3 gap-2">
{CHANNEL_OPTIONS.map((opt) => (
<button
@ -225,11 +225,11 @@ export function StepRemote({ onBack, onNext }: StepRemoteProps) {
"active:scale-[0.97]",
channel === opt.value
? "border-foreground/30 bg-foreground/[0.06]"
: "border-border/40 bg-card/20 hover:border-foreground/15 hover:bg-card/50",
: "border-border/50 bg-card/50 hover:border-foreground/15 hover:bg-card/50",
)}
>
<div className="text-sm font-medium text-foreground">{opt.label}</div>
<div className="mt-0.5 text-[11px] text-muted-foreground/60">{opt.description}</div>
<div className="mt-0.5 text-[11px] text-muted-foreground">{opt.description}</div>
</button>
))}
</div>
@ -239,7 +239,7 @@ export function StepRemote({ onBack, onNext }: StepRemoteProps) {
{/* Channel ID input */}
{channel && !loading && (
<div className="space-y-2">
<div className="text-xs font-medium text-muted-foreground/60">Channel ID</div>
<div className="text-xs font-medium text-muted-foreground">Channel ID</div>
<Input
value={channelId}
onChange={(e) => {
@ -266,7 +266,7 @@ export function StepRemote({ onBack, onNext }: StepRemoteProps) {
{/* Bot token input */}
{channel && !loading && (
<div className="space-y-2">
<div className="text-xs font-medium text-muted-foreground/60">
<div className="text-xs font-medium text-muted-foreground">
Bot token
{tokenSet && (
<span className="ml-2 text-success"> configured</span>
@ -296,7 +296,7 @@ export function StepRemote({ onBack, onNext }: StepRemoteProps) {
<button
type="button"
onClick={() => setShowToken((v) => !v)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground/50 hover:text-muted-foreground transition-colors"
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-muted-foreground transition-colors"
>
{showToken ? <EyeOff className="h-3.5 w-3.5" /> : <Eye className="h-3.5 w-3.5" />}
</button>
@ -365,7 +365,7 @@ export function StepRemote({ onBack, onNext }: StepRemoteProps) {
<Button
variant="ghost"
onClick={onNext}
className="gap-1.5 text-muted-foreground/70 transition-transform active:scale-[0.96]"
className="gap-1.5 text-muted-foreground transition-transform active:scale-[0.96]"
>
Skip
<SkipForward className="h-3.5 w-3.5" />

View file

@ -54,7 +54,7 @@ export function StepWelcome({ onNext }: StepWelcomeProps) {
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.24, duration: 0.4 }}
className="mt-10 flex items-center gap-3 text-xs text-muted-foreground/60"
className="mt-10 flex items-center gap-3 text-xs text-muted-foreground"
>
{["Mode", "Provider", "Auth", "Workspace"].map((label, i) => (
<span key={label} className="flex items-center gap-3">

View file

@ -61,8 +61,8 @@ export function WizardStepper({ steps, currentIndex, onStepClick, className }: W
className={cn(
"hidden text-sm font-medium transition-colors duration-200 sm:inline",
isCurrent && "text-foreground",
isComplete && "text-foreground/70",
!isComplete && !isCurrent && "text-muted-foreground/60",
isComplete && "text-muted-foreground",
!isComplete && !isCurrent && "text-muted-foreground",
)}
>
{step.shortLabel ?? step.label}

View file

@ -156,7 +156,7 @@ export function ProjectWelcome({
{/* Detail note */}
{variant.detail && (
<p className="mt-2 text-xs leading-relaxed text-muted-foreground/70">
<p className="mt-2 text-xs leading-relaxed text-muted-foreground">
{variant.detail}
</p>
)}

View file

@ -183,7 +183,7 @@ function ProjectCard({
"active:scale-[0.98]",
isActive
? "border-primary/30 bg-primary/[0.08]"
: "border-border/40 bg-card/20 hover:border-foreground/15 hover:bg-card/50",
: "border-border/50 bg-card/50 hover:border-foreground/15 hover:bg-card/50",
disabled && "opacity-40 pointer-events-none",
)}
>
@ -227,7 +227,7 @@ function ProjectCard({
{/* Row 3: progress info */}
{progress && (
<div className="mt-1.5 text-[11px] text-muted-foreground/70">{progress}</div>
<div className="mt-1.5 text-[11px] text-muted-foreground">{progress}</div>
)}
{/* Row 4: milestone progress bar */}
@ -243,13 +243,13 @@ function ProjectCard({
}}
/>
</div>
<span className="text-[10px] tabular-nums text-muted-foreground/60">{milestoneCount}</span>
<span className="text-[10px] tabular-nums text-muted-foreground">{milestoneCount}</span>
</div>
)}
</div>
{/* Arrow */}
<ArrowRight className="mt-1 h-4 w-4 shrink-0 text-muted-foreground/30 transition-all group-hover:text-muted-foreground/70 group-hover:translate-x-0.5" />
<ArrowRight className="mt-1 h-4 w-4 shrink-0 text-muted-foreground/50 transition-all group-hover:text-muted-foreground group-hover:translate-x-0.5" />
</button>
)
}
@ -443,7 +443,7 @@ export function ProjectsPanel({
onClick={() => setNewProjectOpen(true)}
className={cn(
"flex w-full items-center gap-3.5 rounded-xl border border-dashed px-4 py-3.5 text-left transition-all duration-200",
"border-border/40 text-muted-foreground hover:border-foreground/15 hover:text-foreground",
"border-border/50 text-muted-foreground hover:border-foreground/15 hover:text-foreground",
"active:scale-[0.98]",
)}
>
@ -452,7 +452,7 @@ export function ProjectsPanel({
</div>
<div>
<span className="text-sm font-medium">Create new project</span>
<p className="mt-0.5 text-[11px] text-muted-foreground/70">Initialize a new directory with Git</p>
<p className="mt-0.5 text-[11px] text-muted-foreground">Initialize a new directory with Git</p>
</div>
</button>
@ -477,7 +477,7 @@ export function ProjectsPanel({
</SheetHeader>
{/* Visible header */}
<div className="flex items-center justify-between border-b border-border/40 px-5 py-4">
<div className="flex items-center justify-between border-b border-border/50 px-5 py-4">
<div>
<h2 className="text-base font-semibold text-foreground">Projects</h2>
{devRoot && !loading && (
@ -491,7 +491,7 @@ export function ProjectsPanel({
>
Change
</button>
<span className="text-muted-foreground/50">·</span>
<span className="text-muted-foreground">·</span>
<span>{projects.length} project{projects.length !== 1 ? "s" : ""}</span>
</div>
)}
@ -537,7 +537,7 @@ function ActiveProjectSummary({ workspaceState }: { workspaceState: ReturnType<t
if (parts.length === 0) return null
return <div className="mt-1.5 text-[11px] text-muted-foreground/70">{parts.join(" · ")}</div>
return <div className="mt-1.5 text-[11px] text-muted-foreground">{parts.join(" · ")}</div>
}
// ─── New Project Dialog ────────────────────────────────────────────────
@ -726,7 +726,7 @@ function FolderPickerDialog({
</DialogDescription>
</DialogHeader>
<div className="border-y border-border/40 bg-muted/30 px-5 py-2">
<div className="border-y border-border/50 bg-muted/50 px-5 py-2">
<p className="font-mono text-xs text-muted-foreground truncate" title={currentPath}>
{currentPath}
</p>
@ -762,7 +762,7 @@ function FolderPickerDialog({
>
<Folder className="h-4 w-4 text-muted-foreground shrink-0" />
<span className="text-foreground truncate flex-1">{entry.name}</span>
<ChevronRight className="h-3.5 w-3.5 text-muted-foreground/40 opacity-0 group-hover:opacity-100 transition-opacity shrink-0" />
<ChevronRight className="h-3.5 w-3.5 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity shrink-0" />
</button>
))}
@ -774,7 +774,7 @@ function FolderPickerDialog({
</div>
</ScrollArea>
<DialogFooter className="border-t border-border/40 px-5 py-3">
<DialogFooter className="border-t border-border/50 px-5 py-3">
<Button variant="ghost" size="sm" onClick={() => onOpenChange(false)}>
Cancel
</Button>
@ -845,7 +845,7 @@ function DevRootSetup({
return (
<div className="space-y-3" data-testid="devroot-settings">
<div className="flex items-center gap-2">
<code className="flex-1 truncate rounded border border-border/40 bg-muted/30 px-3 py-2 font-mono text-xs text-foreground">
<code className="flex-1 truncate rounded border border-border/50 bg-muted/50 px-3 py-2 font-mono text-xs text-foreground">
{currentRoot}
</code>
<Button
@ -945,7 +945,7 @@ export function DevRootSettingsSection() {
<div className="space-y-3" data-testid="settings-devroot">
<div className="flex items-center gap-2.5">
<FolderRoot className="h-3.5 w-3.5 text-muted-foreground" />
<h3 className="text-[13px] font-semibold uppercase tracking-[0.08em] text-foreground/70">
<h3 className="text-[13px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">
Development Root
</h3>
</div>
@ -1164,7 +1164,7 @@ export function ProjectSelectionGate() {
{/* Dev root + change button */}
{devRoot && (
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<FolderRoot className="h-3.5 w-3.5 shrink-0 text-muted-foreground/50" />
<FolderRoot className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
<code className="rounded bg-muted px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground truncate">{devRoot}</code>
<button
type="button"
@ -1179,18 +1179,18 @@ export function ProjectSelectionGate() {
{/* Filter + count */}
<div className="flex items-center justify-between gap-4">
<p className="text-xs text-muted-foreground/60 tabular-nums">
<p className="text-xs text-muted-foreground tabular-nums">
{sortedProjects.length} project{sortedProjects.length !== 1 ? "s" : ""}
</p>
{showFilter && (
<div className="relative w-48">
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground/50" />
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" />
<input
type="text"
placeholder="Filter…"
value={filter}
onChange={(e) => setFilter(e.target.value)}
className="h-8 w-full rounded-md border border-border bg-background pl-8 pr-3 text-xs text-foreground placeholder:text-muted-foreground/40 focus:outline-none focus:ring-1 focus:ring-ring"
className="h-8 w-full rounded-md border border-border bg-background pl-8 pr-3 text-xs text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
/>
</div>
)}
@ -1232,7 +1232,7 @@ export function ProjectSelectionGate() {
<span>{stack.join(" · ")}</span>
)}
{stack.length > 0 && progress && (
<span className="text-muted-foreground/30"></span>
<span className="text-muted-foreground/50"></span>
)}
{progress && (
<span className="truncate">{progress}</span>
@ -1249,7 +1249,7 @@ export function ProjectSelectionGate() {
style={{ width: `${pct}%` }}
/>
</div>
<span className="text-[10px] tabular-nums text-muted-foreground/50 w-6 text-right">
<span className="text-[10px] tabular-nums text-muted-foreground w-6 text-right">
{project.progress!.milestonesCompleted}/{project.progress!.milestonesTotal}
</span>
</div>
@ -1257,13 +1257,13 @@ export function ProjectSelectionGate() {
{/* Modified time */}
{project.lastModified > 0 && (
<span className="hidden lg:inline text-[10px] text-muted-foreground/40 shrink-0 w-16 text-right tabular-nums">
<span className="hidden lg:inline text-[10px] text-muted-foreground shrink-0 w-16 text-right tabular-nums">
{relativeTime(project.lastModified)}
</span>
)}
{/* Arrow */}
<ChevronRight className="h-4 w-4 shrink-0 text-muted-foreground/20 transition-colors group-hover:text-muted-foreground/60" />
<ChevronRight className="h-4 w-4 shrink-0 text-muted-foreground/50 transition-colors group-hover:text-muted-foreground" />
</button>
)
})}

View file

@ -76,7 +76,7 @@ function PanelHeader({
<div className="flex items-center justify-between gap-3 pb-4">
<div className="flex items-center gap-2.5">
<span className="text-muted-foreground">{icon}</span>
<h3 className="text-[13px] font-semibold uppercase tracking-[0.08em] text-foreground/70">{title}</h3>
<h3 className="text-[13px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">{title}</h3>
{status}
{subtitle && <span className="text-[11px] text-muted-foreground">{subtitle}</span>}
</div>
@ -109,7 +109,7 @@ function PanelLoading({ label }: { label: string }) {
function PanelEmpty({ message }: { message: string }) {
return (
<div className="rounded-lg border border-border/30 bg-card/30 px-4 py-5 text-center text-xs text-muted-foreground">
<div className="rounded-lg border border-border/50 bg-card/50 px-4 py-5 text-center text-xs text-muted-foreground">
{message}
</div>
)
@ -123,7 +123,7 @@ function InfoPill({ label, value, variant }: { label: string; value: string | nu
variant === "warning" && "border-warning/20 bg-warning/5 text-warning",
variant === "success" && "border-success/20 bg-success/5 text-success",
variant === "error" && "border-destructive/20 bg-destructive/5 text-destructive",
(!variant || variant === "default") && "border-border/40 bg-card/50 text-foreground/80",
(!variant || variant === "default") && "border-border/50 bg-card/50 text-foreground/80",
)}>
<span className="text-muted-foreground">{label}</span>
<span className="font-medium tabular-nums">{value}</span>
@ -155,21 +155,21 @@ export function QuickPanel() {
icon={<Zap className="h-3.5 w-3.5" />}
/>
<div className="rounded-lg border border-border/30 bg-card/30 px-4 py-4 space-y-3">
<p className="text-xs text-foreground/90">
<div className="rounded-lg border border-border/50 bg-card/50 px-4 py-4 space-y-3">
<p className="text-xs text-foreground">
Create a quick one-off task outside the current plan. Useful for small fixes, experiments, or ad-hoc work that
doesn&apos;t fit into the milestone structure.
</p>
<div className="space-y-2">
<h4 className="text-[11px] font-medium text-foreground/70 uppercase tracking-wide">Usage</h4>
<div className="rounded-md border border-border/20 bg-background/50 px-3 py-2 font-mono text-[11px] text-foreground/80">
<h4 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wide">Usage</h4>
<div className="rounded-md border border-border/50 bg-background/50 px-3 py-2 font-mono text-[11px] text-foreground/80">
/gsd quick &lt;description&gt;
</div>
</div>
<div className="space-y-2">
<h4 className="text-[11px] font-medium text-foreground/70 uppercase tracking-wide">Examples</h4>
<h4 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wide">Examples</h4>
<div className="space-y-1.5">
{[
"Fix the typo in README.md header",
@ -178,8 +178,8 @@ export function QuickPanel() {
"Run prettier on the whole project",
].map((example) => (
<div key={example} className="flex items-center gap-2 text-[11px]">
<span className="text-muted-foreground/50">$</span>
<code className="font-mono text-foreground/70">/gsd quick {example}</code>
<span className="text-muted-foreground">$</span>
<code className="font-mono text-muted-foreground">/gsd quick {example}</code>
</div>
))}
</div>
@ -231,7 +231,7 @@ export function HistoryPanel() {
</div>
{/* Tab switcher */}
<div className="flex gap-1 rounded-lg border border-border/30 bg-card/20 p-0.5">
<div className="flex gap-1 rounded-lg border border-border/50 bg-card/50 p-0.5">
{(["phase", "slice", "model", "units"] as const).map((tab) => (
<button
key={tab}
@ -240,8 +240,8 @@ export function HistoryPanel() {
className={cn(
"flex-1 rounded-md px-2.5 py-1 text-[11px] font-medium capitalize transition-colors",
activeTab === tab
? "bg-card/80 text-foreground shadow-sm"
: "text-muted-foreground hover:text-foreground/70",
? "bg-card text-foreground shadow-sm"
: "text-muted-foreground hover:text-muted-foreground",
)}
>
{tab === "units" ? "Recent" : `By ${tab}`}
@ -251,10 +251,10 @@ export function HistoryPanel() {
{/* By Phase */}
{activeTab === "phase" && data.byPhase.length > 0 && (
<div className="overflow-x-auto rounded-lg border border-border/30">
<div className="overflow-x-auto rounded-lg border border-border/50">
<table className="w-full text-[11px]">
<thead>
<tr className="border-b border-border/30 bg-card/40">
<tr className="border-b border-border/50 bg-card/50">
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Phase</th>
<th className="px-2.5 py-1.5 text-right font-medium text-muted-foreground">Units</th>
<th className="px-2.5 py-1.5 text-right font-medium text-muted-foreground">Cost</th>
@ -263,7 +263,7 @@ export function HistoryPanel() {
</thead>
<tbody>
{data.byPhase.map((row: HistoryPhaseAggregate) => (
<tr key={row.phase} className="border-b border-border/20 last:border-0">
<tr key={row.phase} className="border-b border-border/50 last:border-0">
<td className="px-2.5 py-1.5 font-mono text-foreground/80 capitalize">{row.phase}</td>
<td className="px-2.5 py-1.5 text-right tabular-nums text-foreground/80">{row.units}</td>
<td className="px-2.5 py-1.5 text-right tabular-nums text-foreground/80">{formatCost(row.cost)}</td>
@ -277,10 +277,10 @@ export function HistoryPanel() {
{/* By Slice */}
{activeTab === "slice" && data.bySlice.length > 0 && (
<div className="overflow-x-auto rounded-lg border border-border/30">
<div className="overflow-x-auto rounded-lg border border-border/50">
<table className="w-full text-[11px]">
<thead>
<tr className="border-b border-border/30 bg-card/40">
<tr className="border-b border-border/50 bg-card/50">
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Slice</th>
<th className="px-2.5 py-1.5 text-right font-medium text-muted-foreground">Units</th>
<th className="px-2.5 py-1.5 text-right font-medium text-muted-foreground">Cost</th>
@ -289,7 +289,7 @@ export function HistoryPanel() {
</thead>
<tbody>
{data.bySlice.map((row: HistorySliceAggregate) => (
<tr key={row.sliceId} className="border-b border-border/20 last:border-0">
<tr key={row.sliceId} className="border-b border-border/50 last:border-0">
<td className="px-2.5 py-1.5 font-mono text-foreground/80">{row.sliceId}</td>
<td className="px-2.5 py-1.5 text-right tabular-nums text-foreground/80">{row.units}</td>
<td className="px-2.5 py-1.5 text-right tabular-nums text-foreground/80">{formatCost(row.cost)}</td>
@ -303,10 +303,10 @@ export function HistoryPanel() {
{/* By Model */}
{activeTab === "model" && data.byModel.length > 0 && (
<div className="overflow-x-auto rounded-lg border border-border/30">
<div className="overflow-x-auto rounded-lg border border-border/50">
<table className="w-full text-[11px]">
<thead>
<tr className="border-b border-border/30 bg-card/40">
<tr className="border-b border-border/50 bg-card/50">
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Model</th>
<th className="px-2.5 py-1.5 text-right font-medium text-muted-foreground">Units</th>
<th className="px-2.5 py-1.5 text-right font-medium text-muted-foreground">Cost</th>
@ -314,7 +314,7 @@ export function HistoryPanel() {
</thead>
<tbody>
{data.byModel.map((row: HistoryModelAggregate) => (
<tr key={row.model} className="border-b border-border/20 last:border-0">
<tr key={row.model} className="border-b border-border/50 last:border-0">
<td className="px-2.5 py-1.5 font-mono text-foreground/80 truncate max-w-[180px]">{row.model}</td>
<td className="px-2.5 py-1.5 text-right tabular-nums text-foreground/80">{row.units}</td>
<td className="px-2.5 py-1.5 text-right tabular-nums text-foreground/80">{formatCost(row.cost)}</td>
@ -329,10 +329,10 @@ export function HistoryPanel() {
{activeTab === "units" && (
<>
{data.units.length > 0 ? (
<div className="overflow-x-auto rounded-lg border border-border/30">
<div className="overflow-x-auto rounded-lg border border-border/50">
<table className="w-full text-[11px]">
<thead>
<tr className="border-b border-border/30 bg-card/40">
<tr className="border-b border-border/50 bg-card/50">
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Type</th>
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">ID</th>
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Model</th>
@ -342,7 +342,7 @@ export function HistoryPanel() {
</thead>
<tbody>
{data.units.slice(0, 20).map((u, i) => (
<tr key={i} className="border-b border-border/20 last:border-0">
<tr key={i} className="border-b border-border/50 last:border-0">
<td className="px-2.5 py-1.5 font-mono text-foreground/80">{u.type}</td>
<td className="px-2.5 py-1.5 font-mono text-foreground/80 truncate max-w-[120px]">{u.id}</td>
<td className="px-2.5 py-1.5 text-muted-foreground truncate max-w-[120px]">{u.model}</td>
@ -418,7 +418,7 @@ export function UndoPanel() {
{result.success ? <CheckCircle2 className="h-3.5 w-3.5" /> : <XCircle className="h-3.5 w-3.5" />}
<span className="font-medium">{result.success ? "Undo Successful" : "Undo Failed"}</span>
</div>
<p className="mt-1 text-[11px] text-foreground/70">{result.message}</p>
<p className="mt-1 text-[11px] text-muted-foreground">{result.message}</p>
</div>
)}
@ -427,8 +427,8 @@ export function UndoPanel() {
{data.lastUnitType ? (
<>
{/* Last unit info */}
<div className="rounded-lg border border-border/30 bg-card/30 px-3 py-2.5 space-y-1.5">
<h4 className="text-[11px] font-medium text-foreground/70 uppercase tracking-wide">Last Completed Unit</h4>
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-2.5 space-y-1.5">
<h4 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wide">Last Completed Unit</h4>
<div className="grid grid-cols-2 gap-x-4 gap-y-0.5 text-[11px]">
<span className="text-muted-foreground">Type</span>
<span className="font-mono text-foreground/80">{data.lastUnitType}</span>
@ -449,7 +449,7 @@ export function UndoPanel() {
{/* Commit SHAs */}
{data.commits.length > 0 && (
<div className="space-y-1.5">
<h4 className="text-[11px] font-medium text-foreground/70">Associated Commits</h4>
<h4 className="text-[11px] font-medium text-muted-foreground">Associated Commits</h4>
<div className="flex flex-wrap gap-1">
{data.commits.map((sha) => (
<Badge key={sha} variant="outline" className="text-[10px] px-1.5 py-0 font-mono">
@ -565,13 +565,13 @@ export function SteerPanel() {
{/* Current overrides */}
<div className="space-y-2">
<h4 className="text-[11px] font-medium text-foreground/70 uppercase tracking-wide">Current Overrides</h4>
<h4 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wide">Current Overrides</h4>
{data?.overridesContent ? (
<div className="rounded-lg border border-border/30 bg-background/50 px-3 py-2.5 text-[11px] font-mono text-foreground/80 whitespace-pre-wrap max-h-[200px] overflow-y-auto leading-relaxed">
<div className="rounded-lg border border-border/50 bg-background/50 px-3 py-2.5 text-[11px] font-mono text-foreground/80 whitespace-pre-wrap max-h-[200px] overflow-y-auto leading-relaxed">
{data.overridesContent}
</div>
) : (
<div className="rounded-lg border border-border/30 bg-card/30 px-3 py-2.5 text-[11px] text-muted-foreground italic">
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-2.5 text-[11px] text-muted-foreground italic">
No active overrides
</div>
)}
@ -579,7 +579,7 @@ export function SteerPanel() {
{/* Steer message form */}
<div className="space-y-2">
<h4 className="text-[11px] font-medium text-foreground/70 uppercase tracking-wide">Send Steering Message</h4>
<h4 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wide">Send Steering Message</h4>
<Textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
@ -633,10 +633,10 @@ export function HooksPanel() {
{data && (
<>
{data.entries.length > 0 ? (
<div className="overflow-x-auto rounded-lg border border-border/30">
<div className="overflow-x-auto rounded-lg border border-border/50">
<table className="w-full text-[11px]">
<thead>
<tr className="border-b border-border/30 bg-card/40">
<tr className="border-b border-border/50 bg-card/50">
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Name</th>
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Type</th>
<th className="px-2.5 py-1.5 text-center font-medium text-muted-foreground">Status</th>
@ -648,7 +648,7 @@ export function HooksPanel() {
{data.entries.map((entry: HookStatusEntry) => {
const totalCycles = Object.values(entry.activeCycles).reduce((sum, n) => sum + n, 0)
return (
<tr key={entry.name} className="border-b border-border/20 last:border-0">
<tr key={entry.name} className="border-b border-border/50 last:border-0">
<td className="px-2.5 py-1.5 font-mono text-foreground/80">{entry.name}</td>
<td className="px-2.5 py-1.5">
<Badge variant="outline" className="text-[10px] px-1.5 py-0">
@ -684,7 +684,7 @@ export function HooksPanel() {
{/* Formatted status */}
{data.formattedStatus && (
<div className="rounded-lg border border-border/30 bg-background/50 px-3 py-2.5 text-[11px] font-mono text-foreground/70 whitespace-pre-wrap leading-relaxed">
<div className="rounded-lg border border-border/50 bg-background/50 px-3 py-2.5 text-[11px] font-mono text-muted-foreground whitespace-pre-wrap leading-relaxed">
{data.formattedStatus}
</div>
)}
@ -730,11 +730,11 @@ export function InspectPanel() {
{/* Recent decisions */}
{data.recentDecisions.length > 0 && (
<div className="space-y-2">
<h4 className="text-xs font-medium text-foreground/70">Recent Decisions ({data.recentDecisions.length})</h4>
<div className="overflow-x-auto rounded-lg border border-border/30">
<h4 className="text-xs font-medium text-muted-foreground">Recent Decisions ({data.recentDecisions.length})</h4>
<div className="overflow-x-auto rounded-lg border border-border/50">
<table className="w-full text-[11px]">
<thead>
<tr className="border-b border-border/30 bg-card/40">
<tr className="border-b border-border/50 bg-card/50">
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">ID</th>
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Decision</th>
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Choice</th>
@ -742,7 +742,7 @@ export function InspectPanel() {
</thead>
<tbody>
{data.recentDecisions.map((d) => (
<tr key={d.id} className="border-b border-border/20 last:border-0">
<tr key={d.id} className="border-b border-border/50 last:border-0">
<td className="px-2.5 py-1.5 font-mono text-foreground/80">{d.id}</td>
<td className="px-2.5 py-1.5 text-foreground/80 max-w-[200px] truncate">{d.decision}</td>
<td className="px-2.5 py-1.5 text-muted-foreground max-w-[150px] truncate">{d.choice}</td>
@ -757,11 +757,11 @@ export function InspectPanel() {
{/* Recent requirements */}
{data.recentRequirements.length > 0 && (
<div className="space-y-2">
<h4 className="text-xs font-medium text-foreground/70">Recent Requirements ({data.recentRequirements.length})</h4>
<div className="overflow-x-auto rounded-lg border border-border/30">
<h4 className="text-xs font-medium text-muted-foreground">Recent Requirements ({data.recentRequirements.length})</h4>
<div className="overflow-x-auto rounded-lg border border-border/50">
<table className="w-full text-[11px]">
<thead>
<tr className="border-b border-border/30 bg-card/40">
<tr className="border-b border-border/50 bg-card/50">
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">ID</th>
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Status</th>
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Description</th>
@ -769,7 +769,7 @@ export function InspectPanel() {
</thead>
<tbody>
{data.recentRequirements.map((r) => (
<tr key={r.id} className="border-b border-border/20 last:border-0">
<tr key={r.id} className="border-b border-border/50 last:border-0">
<td className="px-2.5 py-1.5 font-mono text-foreground/80">{r.id}</td>
<td className="px-2.5 py-1.5">
<Badge
@ -843,8 +843,8 @@ export function ExportPanel() {
{/* Format selector */}
<div className="space-y-2">
<h4 className="text-[11px] font-medium text-foreground/70 uppercase tracking-wide">Format</h4>
<div className="flex gap-1 rounded-lg border border-border/30 bg-card/20 p-0.5">
<h4 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wide">Format</h4>
<div className="flex gap-1 rounded-lg border border-border/50 bg-card/50 p-0.5">
{(["markdown", "json"] as const).map((f) => (
<button
key={f}
@ -853,8 +853,8 @@ export function ExportPanel() {
className={cn(
"flex-1 rounded-md px-3 py-1.5 text-[11px] font-medium capitalize transition-colors",
format === f
? "bg-card/80 text-foreground shadow-sm"
: "text-muted-foreground hover:text-foreground/70",
? "bg-card text-foreground shadow-sm"
: "text-muted-foreground hover:text-muted-foreground",
)}
>
{f === "markdown" ? "Markdown" : "JSON"}
@ -884,7 +884,7 @@ export function ExportPanel() {
<span className="font-medium">Export Ready</span>
</div>
<div className="flex items-center justify-between gap-2">
<span className="text-[11px] font-mono text-foreground/70">{data.filename}</span>
<span className="text-[11px] font-mono text-muted-foreground">{data.filename}</span>
<Button
type="button"
variant="ghost"
@ -952,7 +952,7 @@ export function CleanupPanel() {
<CheckCircle2 className="h-3.5 w-3.5" />
<span className="font-medium">Cleanup Complete</span>
</div>
<p className="mt-1 text-[11px] text-foreground/70">{result.message}</p>
<p className="mt-1 text-[11px] text-muted-foreground">{result.message}</p>
</div>
)}
@ -961,7 +961,7 @@ export function CleanupPanel() {
{/* Branches table */}
<div className="space-y-2">
<div className="flex items-center justify-between">
<h4 className="text-xs font-medium text-foreground/70">Branches ({data.branches.length})</h4>
<h4 className="text-xs font-medium text-muted-foreground">Branches ({data.branches.length})</h4>
{mergedBranches.length > 0 && (
<Button
type="button"
@ -977,17 +977,17 @@ export function CleanupPanel() {
)}
</div>
{data.branches.length > 0 ? (
<div className="overflow-x-auto rounded-lg border border-border/30">
<div className="overflow-x-auto rounded-lg border border-border/50">
<table className="w-full text-[11px]">
<thead>
<tr className="border-b border-border/30 bg-card/40">
<tr className="border-b border-border/50 bg-card/50">
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Branch</th>
<th className="px-2.5 py-1.5 text-center font-medium text-muted-foreground">Status</th>
</tr>
</thead>
<tbody>
{data.branches.map((b: CleanupBranch) => (
<tr key={b.name} className="border-b border-border/20 last:border-0">
<tr key={b.name} className="border-b border-border/50 last:border-0">
<td className="px-2.5 py-1.5 font-mono text-foreground/80 truncate max-w-[250px]">
<span className="flex items-center gap-1.5">
<GitBranch className="h-3 w-3 text-muted-foreground shrink-0" />
@ -1018,7 +1018,7 @@ export function CleanupPanel() {
{/* Snapshots table */}
<div className="space-y-2">
<div className="flex items-center justify-between">
<h4 className="text-xs font-medium text-foreground/70">Snapshots ({data.snapshots.length})</h4>
<h4 className="text-xs font-medium text-muted-foreground">Snapshots ({data.snapshots.length})</h4>
{oldSnapshots.length > 0 && (
<Button
type="button"
@ -1034,17 +1034,17 @@ export function CleanupPanel() {
)}
</div>
{data.snapshots.length > 0 ? (
<div className="overflow-x-auto rounded-lg border border-border/30">
<div className="overflow-x-auto rounded-lg border border-border/50">
<table className="w-full text-[11px]">
<thead>
<tr className="border-b border-border/30 bg-card/40">
<tr className="border-b border-border/50 bg-card/50">
<th className="px-2.5 py-1.5 text-left font-medium text-muted-foreground">Ref</th>
<th className="px-2.5 py-1.5 text-right font-medium text-muted-foreground">Date</th>
</tr>
</thead>
<tbody>
{data.snapshots.map((s: CleanupSnapshot) => (
<tr key={s.ref} className="border-b border-border/20 last:border-0">
<tr key={s.ref} className="border-b border-border/50 last:border-0">
<td className="px-2.5 py-1.5 font-mono text-foreground/80 truncate max-w-[200px]">{s.ref}</td>
<td className="px-2.5 py-1.5 text-right text-muted-foreground">{s.date}</td>
</tr>
@ -1101,13 +1101,13 @@ export function QueuePanel() {
"rounded-lg border px-3 py-2.5 space-y-1.5",
isActive
? "border-info/25 bg-info/5"
: "border-border/30 bg-card/30",
: "border-border/50 bg-card/50",
)}
>
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2">
<span className="text-xs font-mono font-medium text-foreground/80">{m.id}</span>
<span className="text-xs text-foreground/90 truncate">{m.title}</span>
<span className="text-xs text-foreground truncate">{m.title}</span>
{isActive && (
<Badge variant="secondary" className="text-[10px] px-1.5 py-0 border-info/30 text-info">
active
@ -1121,7 +1121,7 @@ export function QueuePanel() {
{/* Progress bar */}
{progress.total > 0 && (
<div className="h-1 rounded-full bg-border/30 overflow-hidden">
<div className="h-1 rounded-full bg-border/50 overflow-hidden">
<div
className={cn(
"h-full rounded-full transition-all",
@ -1194,8 +1194,8 @@ export function StatusPanel() {
/>
{/* Active context card */}
<div className="rounded-lg border border-border/30 bg-card/30 px-3 py-3 space-y-2">
<h4 className="text-[11px] font-medium text-foreground/70 uppercase tracking-wide">Active Context</h4>
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-3 space-y-2">
<h4 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wide">Active Context</h4>
<div className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 text-[11px]">
<span className="text-muted-foreground">Phase</span>
<span className="font-mono text-foreground/80">
@ -1244,7 +1244,7 @@ export function StatusPanel() {
<span>Overall Progress</span>
<span className="tabular-nums">{Math.round((doneSlices / totalSlices) * 100)}%</span>
</div>
<div className="h-1.5 rounded-full bg-border/30 overflow-hidden">
<div className="h-1.5 rounded-full bg-border/50 overflow-hidden">
<div
className={cn(
"h-full rounded-full transition-all",

View file

@ -19,7 +19,7 @@ const StatusIcon = ({
if (status === "in-progress") {
return <Play className={cn(sizeClass, "text-warning")} />
}
return <Circle className={cn(sizeClass, "text-muted-foreground/40")} />
return <Circle className={cn(sizeClass, "text-muted-foreground")} />
}
const RiskBadge = ({ risk }: { risk: RiskLevel }) => {
@ -113,7 +113,7 @@ export function Roadmap() {
className={cn(
"flex items-center gap-3 px-4 py-2.5",
sliceStatus === "in-progress" && "bg-accent/20",
sliceStatus === "pending" && "opacity-60",
sliceStatus === "pending" && "opacity-70",
)}
>
<div className="w-4" />

View file

@ -58,7 +58,7 @@ function SettingsHeader({
<div className="flex items-center justify-between gap-3 pb-4">
<div className="flex items-center gap-2.5">
<span className="text-muted-foreground">{icon}</span>
<h3 className="text-[13px] font-semibold uppercase tracking-[0.08em] text-foreground/70">{title}</h3>
<h3 className="text-[13px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">{title}</h3>
{subtitle && <span className="text-[11px] text-muted-foreground">{subtitle}</span>}
</div>
<Button type="button" variant="ghost" size="sm" onClick={onRefresh} disabled={refreshing} className="h-7 gap-1.5 text-xs">
@ -88,7 +88,7 @@ function SettingsLoading({ label }: { label: string }) {
function SettingsEmpty({ message }: { message: string }) {
return (
<div className="rounded-lg border border-border/30 bg-card/30 px-4 py-5 text-center text-xs text-muted-foreground">
<div className="rounded-lg border border-border/50 bg-card/50 px-4 py-5 text-center text-xs text-muted-foreground">
{message}
</div>
)
@ -101,7 +101,7 @@ function Pill({ label, value, variant }: { label: string; value: string | number
variant === "info" && "border-info/20 bg-info/5 text-info",
variant === "warning" && "border-warning/20 bg-warning/5 text-warning",
variant === "success" && "border-success/20 bg-success/5 text-success",
(!variant || variant === "default") && "border-border/40 bg-card/50 text-foreground/80",
(!variant || variant === "default") && "border-border/50 bg-card/50 text-foreground/80",
)}>
<span className="text-muted-foreground">{label}</span>
<span className="font-medium tabular-nums">{value}</span>
@ -215,7 +215,7 @@ export function PrefsPanel() {
</div>
{/* Toggles */}
<div className="grid grid-cols-2 gap-x-6 gap-y-1.5 rounded-lg border border-border/30 bg-card/30 px-3 py-2.5">
<div className="grid grid-cols-2 gap-x-6 gap-y-1.5 rounded-lg border border-border/50 bg-card/50 px-3 py-2.5">
<KvRow label="Auto-Supervisor">
{prefs.autoSupervisor?.enabled ? (
<span className="text-success">
@ -343,8 +343,8 @@ export function ModelRoutingPanel() {
{/* Tier assignments */}
{routingConfig?.tier_models && (
<div className="rounded-lg border border-border/30 bg-card/30 px-3 py-2.5 space-y-1.5">
<h4 className="text-[11px] font-medium text-foreground/70 uppercase tracking-wide">Tier Assignments</h4>
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-2.5 space-y-1.5">
<h4 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wide">Tier Assignments</h4>
<TierModelRow tier="light" modelId={routingConfig.tier_models.light} />
<TierModelRow tier="standard" modelId={routingConfig.tier_models.standard} />
<TierModelRow tier="heavy" modelId={routingConfig.tier_models.heavy} />
@ -370,10 +370,10 @@ export function ModelRoutingPanel() {
{/* Top patterns table */}
{Object.keys(routingHistory.patterns).length > 0 && (
<div className="space-y-1.5">
<h4 className="text-[11px] font-medium text-foreground/70">Top Patterns</h4>
<h4 className="text-[11px] font-medium text-muted-foreground">Top Patterns</h4>
<div className="space-y-2">
{topPatterns(routingHistory).map(({ name, total, pattern }) => (
<div key={name} className="rounded-lg border border-border/30 bg-card/30 px-3 py-2 space-y-1">
<div key={name} className="rounded-lg border border-border/50 bg-card/50 px-3 py-2 space-y-1">
<div className="flex items-center justify-between gap-2">
<span className="text-xs font-mono text-foreground/80 truncate">{name}</span>
<span className="text-[10px] text-muted-foreground tabular-nums shrink-0">{total} attempts</span>
@ -455,8 +455,8 @@ export function BudgetPanel() {
{/* Context budget allocations */}
{budget && (
<div className="rounded-lg border border-border/30 bg-card/30 px-3 py-2.5 space-y-1.5">
<h4 className="text-[11px] font-medium text-foreground/70 uppercase tracking-wide">Context Budget Allocations</h4>
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-2.5 space-y-1.5">
<h4 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wide">Context Budget Allocations</h4>
<KvRow label="Summary Budget">{formatChars(budget.summaryBudgetChars)} chars</KvRow>
<KvRow label="Inline Context">{formatChars(budget.inlineContextBudgetChars)} chars</KvRow>
<KvRow label="Verification">{formatChars(budget.verificationBudgetChars)} chars</KvRow>
@ -468,7 +468,7 @@ export function BudgetPanel() {
{/* Project cost totals */}
{totals ? (
<div className="space-y-3">
<h4 className="text-[11px] font-medium text-foreground/70 uppercase tracking-wide">Project Cost Totals</h4>
<h4 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wide">Project Cost Totals</h4>
{/* Summary pills */}
<div className="flex flex-wrap gap-2">
@ -478,8 +478,8 @@ export function BudgetPanel() {
</div>
{/* Token breakdown */}
<div className="rounded-lg border border-border/30 bg-card/30 px-3 py-2.5 space-y-1.5">
<h4 className="text-[11px] font-medium text-foreground/70 uppercase tracking-wide">Token Breakdown</h4>
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-2.5 space-y-1.5">
<h4 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wide">Token Breakdown</h4>
<KvRow label="Input">{formatTokens(totals.tokens.input)}</KvRow>
<KvRow label="Output">{formatTokens(totals.tokens.output)}</KvRow>
<KvRow label="Cache Read">{formatTokens(totals.tokens.cacheRead)}</KvRow>
@ -764,7 +764,7 @@ export function RemoteQuestionsPanel() {
{/* ── Channel picker (card-based) ──────────────────────────── */}
<div className="space-y-2">
<div className="text-xs font-medium text-muted-foreground/60">
<div className="text-xs font-medium text-muted-foreground">
{isConfigured ? "Switch channel" : "Choose a channel"}
</div>
<div className="grid grid-cols-3 gap-2">
@ -783,11 +783,11 @@ export function RemoteQuestionsPanel() {
"active:scale-[0.97]",
channel === opt.value
? "border-foreground/30 bg-foreground/[0.06]"
: "border-border/40 bg-card/20 hover:border-foreground/15 hover:bg-card/50",
: "border-border/50 bg-card/50 hover:border-foreground/15 hover:bg-card/50",
)}
>
<div className="text-sm font-medium text-foreground">{opt.label}</div>
<div className="mt-0.5 text-[11px] text-muted-foreground/60">{opt.description}</div>
<div className="mt-0.5 text-[11px] text-muted-foreground">{opt.description}</div>
</button>
))}
</div>
@ -795,7 +795,7 @@ export function RemoteQuestionsPanel() {
{/* ── Channel ID input ─────────────────────────────────────── */}
<div className="space-y-2">
<div className="text-xs font-medium text-muted-foreground/60">Channel ID</div>
<div className="text-xs font-medium text-muted-foreground">Channel ID</div>
<input
type="text"
value={channelId}
@ -803,13 +803,13 @@ export function RemoteQuestionsPanel() {
placeholder={selectedChannelOption.idPlaceholder}
disabled={saving}
className={cn(
"w-full rounded-xl border bg-card/20 px-4 py-2.5 font-mono text-sm text-foreground",
"placeholder:text-muted-foreground/40",
"w-full rounded-xl border bg-card/50 px-4 py-2.5 font-mono text-sm text-foreground",
"placeholder:text-muted-foreground",
"focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent",
"transition-colors",
channelId.trim().length > 0 && !CHANNEL_ID_PATTERNS[channel].test(channelId.trim())
? "border-destructive/40"
: "border-border/40",
: "border-border/50",
)}
onKeyDown={(e) => { if (e.key === "Enter" && canSave) void handleSave() }}
/>
@ -824,7 +824,7 @@ export function RemoteQuestionsPanel() {
<button
type="button"
onClick={() => setShowAdvanced((v) => !v)}
className="flex items-center gap-1.5 text-[11px] text-muted-foreground/60 hover:text-muted-foreground transition-colors"
className="flex items-center gap-1.5 text-[11px] text-muted-foreground hover:text-muted-foreground transition-colors"
>
<svg
className={cn("h-3 w-3 transition-transform", showAdvanced && "rotate-90")}
@ -839,7 +839,7 @@ export function RemoteQuestionsPanel() {
{showAdvanced && (
<div className="grid grid-cols-2 gap-3 pl-4">
<div className="space-y-1.5">
<label className="text-[11px] text-muted-foreground/60" htmlFor="rq-timeout">
<label className="text-[11px] text-muted-foreground" htmlFor="rq-timeout">
Timeout (min)
</label>
<input
@ -849,11 +849,11 @@ export function RemoteQuestionsPanel() {
max={30}
value={timeoutMinutes}
onChange={(e) => setTimeoutMinutes(Math.max(1, Math.min(30, Number(e.target.value) || 1)))}
className="w-full rounded-lg border border-border/40 bg-card/20 px-3 py-2 text-xs text-foreground tabular-nums focus:outline-none focus:ring-2 focus:ring-ring"
className="w-full rounded-lg border border-border/50 bg-card/50 px-3 py-2 text-xs text-foreground tabular-nums focus:outline-none focus:ring-2 focus:ring-ring"
/>
</div>
<div className="space-y-1.5">
<label className="text-[11px] text-muted-foreground/60" htmlFor="rq-poll">
<label className="text-[11px] text-muted-foreground" htmlFor="rq-poll">
Poll interval (sec)
</label>
<input
@ -863,7 +863,7 @@ export function RemoteQuestionsPanel() {
max={30}
value={pollIntervalSeconds}
onChange={(e) => setPollIntervalSeconds(Math.max(2, Math.min(30, Number(e.target.value) || 2)))}
className="w-full rounded-lg border border-border/40 bg-card/20 px-3 py-2 text-xs text-foreground tabular-nums focus:outline-none focus:ring-2 focus:ring-ring"
className="w-full rounded-lg border border-border/50 bg-card/50 px-3 py-2 text-xs text-foreground tabular-nums focus:outline-none focus:ring-2 focus:ring-ring"
/>
</div>
</div>
@ -888,7 +888,7 @@ export function RemoteQuestionsPanel() {
{/* ── Bot token ─────────────────────────────────────────── */}
<div className="space-y-3">
<div className="text-xs font-medium text-muted-foreground/60">Bot token</div>
<div className="text-xs font-medium text-muted-foreground">Bot token</div>
{tokenSuccess && (
<div className="flex items-center gap-2.5 rounded-xl border border-success/15 bg-success/[0.04] px-4 py-2.5 text-xs text-muted-foreground">
@ -920,8 +920,8 @@ export function RemoteQuestionsPanel() {
placeholder={`Paste your ${selectedChannelOption.label} bot token`}
disabled={savingToken}
className={cn(
"w-full rounded-xl border border-border/40 bg-card/20 pl-4 pr-10 py-2.5 font-mono text-sm text-foreground",
"placeholder:text-muted-foreground/40",
"w-full rounded-xl border border-border/50 bg-card/50 pl-4 pr-10 py-2.5 font-mono text-sm text-foreground",
"placeholder:text-muted-foreground",
"focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent",
"transition-colors",
)}
@ -930,7 +930,7 @@ export function RemoteQuestionsPanel() {
<button
type="button"
onClick={() => setShowToken((v) => !v)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground/50 hover:text-muted-foreground transition-colors"
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-muted-foreground transition-colors"
>
{showToken ? <EyeOff className="h-3.5 w-3.5" /> : <Eye className="h-3.5 w-3.5" />}
</button>
@ -976,7 +976,7 @@ function FontSizeControl({
previewFont: "mono" | "sans"
}) {
return (
<div className="rounded-lg border border-border/30 bg-card/30 px-3 py-3 space-y-3">
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-3 space-y-3">
<div>
<div className="text-xs font-medium text-foreground">{label}</div>
<div className="text-[11px] text-muted-foreground mt-0.5">{description}</div>
@ -992,12 +992,12 @@ function FontSizeControl({
"rounded-md border px-3 py-1.5 text-xs font-medium tabular-nums transition-colors",
currentSize === size
? "border-foreground/30 bg-foreground/10 text-foreground shadow-sm"
: "border-border/40 bg-card/50 text-muted-foreground hover:border-foreground/20 hover:text-foreground",
: "border-border/50 bg-card/50 text-muted-foreground hover:border-foreground/20 hover:text-foreground",
)}
>
{size}px
{size === defaultSize && (
<span className="ml-1 text-[10px] text-muted-foreground/60">(default)</span>
<span className="ml-1 text-[10px] text-muted-foreground">(default)</span>
)}
</button>
))}
@ -1005,7 +1005,7 @@ function FontSizeControl({
<div
className={cn(
"mt-2 rounded-md border border-border/20 bg-terminal px-3 py-2 text-foreground/80",
"mt-2 rounded-md border border-border/50 bg-terminal px-3 py-2 text-foreground/80",
previewFont === "mono" ? "font-mono" : "font-sans",
)}
style={{ fontSize: `${currentSize}px`, lineHeight: 1.35 }}
@ -1141,7 +1141,7 @@ export function ExperimentalPanel() {
return (
<div
key={flag.key}
className="rounded-lg border border-border/40 bg-card/30 px-3 py-3 space-y-2"
className="rounded-lg border border-border/50 bg-card/50 px-3 py-3 space-y-2"
>
<div className="flex items-start justify-between gap-3">
<div className="min-w-0 flex-1 space-y-1">

View file

@ -711,7 +711,7 @@ export function ShellTerminal({
{/* Drop overlay */}
{isDragOver && (
<div className="absolute inset-0 z-20 flex flex-col items-center justify-center gap-2 bg-background/80 backdrop-blur-sm border-2 border-dashed border-primary rounded-md pointer-events-none">
<div className="absolute inset-0 z-20 flex flex-col items-center justify-center gap-2 bg-background backdrop-blur-sm border-2 border-dashed border-primary rounded-md pointer-events-none">
<ImagePlus className="h-8 w-8 text-primary" />
<span className="text-sm font-medium text-primary">Drop image here</span>
</div>
@ -719,7 +719,7 @@ export function ShellTerminal({
</div>
{!hideSidebar && (
<div className="flex w-[34px] flex-shrink-0 flex-col border-l border-border/40 bg-terminal">
<div className="flex w-[34px] flex-shrink-0 flex-col border-l border-border/50 bg-terminal">
{/* New terminal button */}
<button
onClick={createTab}
@ -729,7 +729,7 @@ export function ShellTerminal({
<Plus className="h-3 w-3" />
</button>
<div className="h-px bg-border/40" />
<div className="h-px bg-border/50" />
{/* Tab list */}
<div className="flex-1 overflow-y-auto">

View file

@ -62,7 +62,7 @@ const StatusIcon = ({ status }: { status: ItemStatus }) => {
if (status === "in-progress") {
return <Play className="h-4 w-4 shrink-0 text-warning" />
}
return <Circle className="h-4 w-4 shrink-0 text-muted-foreground/50" />
return <Circle className="h-4 w-4 shrink-0 text-muted-foreground" />
}
/* ─── Nav Rail (left icon bar) ─── */
@ -110,7 +110,7 @@ export function NavRail({ activeView, onViewChange, isConnecting = false }: NavR
className={cn(
"flex h-10 w-10 items-center justify-center rounded-md transition-colors",
isConnecting
? "cursor-not-allowed text-muted-foreground/30"
? "cursor-not-allowed text-muted-foreground/50"
: activeView === item.id
? "bg-accent text-foreground"
: "text-muted-foreground hover:bg-accent/50 hover:text-foreground",
@ -127,7 +127,7 @@ export function NavRail({ activeView, onViewChange, isConnecting = false }: NavR
className={cn(
"flex h-10 w-10 items-center justify-center rounded-md transition-colors",
isConnecting
? "cursor-not-allowed text-muted-foreground/30"
? "cursor-not-allowed text-muted-foreground/50"
: "text-muted-foreground hover:bg-accent/50 hover:text-foreground",
)}
title={isConnecting ? "Connecting…" : "Projects"}
@ -748,7 +748,7 @@ function MobileNavPanel({ activeView, onViewChange, isConnecting = false }: NavR
className={cn(
"flex w-full items-center gap-3 rounded-md px-3 py-3 text-sm font-medium transition-colors min-h-[44px]",
isConnecting
? "cursor-not-allowed text-muted-foreground/30"
? "cursor-not-allowed text-muted-foreground/50"
: activeView === item.id
? "bg-accent text-foreground"
: "text-muted-foreground hover:bg-accent/50 hover:text-foreground",

View file

@ -89,24 +89,24 @@ function TerminalWidgetBand({
return (
<div
className="border-t border-border/50 bg-card/20 px-4 py-2"
className="border-t border-border/50 bg-card/50 px-4 py-2"
data-testid={placement === "aboveEditor" ? "terminal-widgets-above-editor" : "terminal-widgets-below-editor"}
>
<div className="space-y-2">
{widgets.map((widget) => (
<div
key={`${widget.placement}:${widget.key}`}
className="rounded-md border border-border/60 bg-background/40 px-3 py-2"
className="rounded-md border border-border bg-background/50 px-3 py-2"
data-testid="terminal-widget"
data-widget-key={widget.key}
data-widget-placement={widget.placement}
title={widget.fullText}
>
<div className="mb-1 flex items-center justify-between gap-2 text-[10px] uppercase tracking-[0.2em] text-muted-foreground/80">
<div className="mb-1 flex items-center justify-between gap-2 text-[10px] uppercase tracking-[0.2em] text-muted-foreground">
<span className="truncate">{widget.key}</span>
<span>{widget.placement === "aboveEditor" ? "Above editor" : "Below editor"}</span>
</div>
<div className="space-y-1 text-xs text-foreground/90">
<div className="space-y-1 text-xs text-foreground">
{widget.visibleLines.map((line, index) => (
<div key={`${widget.key}:${index}`} className="whitespace-pre-wrap break-words">
{line}
@ -238,7 +238,7 @@ export function Terminal({ className }: TerminalProps) {
<div className="flex-1 overflow-y-auto p-4">
{workspace.terminalLines.map((line) => (
<div key={line.id} className="flex" data-testid="terminal-line">
<span className="mr-2 select-none text-muted-foreground/50">{line.timestamp}</span>
<span className="mr-2 select-none text-muted-foreground">{line.timestamp}</span>
<span
className={cn(
"whitespace-pre-wrap",
@ -260,7 +260,7 @@ export function Terminal({ className }: TerminalProps) {
{workspace.liveTranscript.map((block, i) => (
<div
key={`transcript-${i}`}
className="whitespace-pre-wrap rounded border border-border/30 bg-accent/20 px-3 py-2 text-foreground/90"
className="whitespace-pre-wrap rounded border border-border/50 bg-accent/20 px-3 py-2 text-foreground"
>
{block}
</div>
@ -271,7 +271,7 @@ export function Terminal({ className }: TerminalProps) {
{/* Live streaming assistant text */}
{workspace.streamingAssistantText && (
<div className="mt-2" data-testid="terminal-streaming-text">
<div className="whitespace-pre-wrap rounded border border-foreground/10 bg-foreground/[0.03] px-3 py-2 text-foreground/90">
<div className="whitespace-pre-wrap rounded border border-foreground/10 bg-foreground/[0.03] px-3 py-2 text-foreground">
{workspace.streamingAssistantText}
<span className="ml-0.5 inline-block h-4 w-1.5 animate-pulse bg-foreground/60" />
</div>
@ -328,7 +328,7 @@ export function Terminal({ className }: TerminalProps) {
type="text"
value={input}
onChange={(event) => setInput(event.target.value)}
className="flex-1 bg-transparent text-foreground outline-none placeholder:text-muted-foreground/50 disabled:cursor-not-allowed disabled:text-muted-foreground"
className="flex-1 bg-transparent text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:text-muted-foreground"
placeholder={inputModePlaceholder(inputMode, workspace)}
disabled={isInputDisabled}
data-testid="terminal-command-input"

View file

@ -62,7 +62,7 @@ function statusIcon(status: "complete" | "active" | "pending" | "done") {
case "active":
return <Play className="h-4 w-4 shrink-0 text-info" />
case "pending":
return <Circle className="h-4 w-4 shrink-0 text-muted-foreground/30" />
return <Circle className="h-4 w-4 shrink-0 text-muted-foreground/50" />
}
}
@ -121,9 +121,9 @@ function SectionLabel({ children }: { children: React.ReactNode }) {
/** Large empty state with icon */
function EmptyState({ message, icon: Icon = AlertCircle }: { message: string; icon?: React.ComponentType<{ className?: string }> }) {
return (
<div className="flex flex-col items-center justify-center gap-4 rounded-xl border border-dashed border-border/60 py-16 text-center">
<div className="rounded-full border border-border/60 bg-muted/40 p-4">
<Icon className="h-6 w-6 text-muted-foreground/50" />
<div className="flex flex-col items-center justify-center gap-4 rounded-xl border border-dashed border-border py-16 text-center">
<div className="rounded-full border border-border bg-muted/50 p-4">
<Icon className="h-6 w-6 text-muted-foreground" />
</div>
<p className="text-sm font-medium text-muted-foreground">{message}</p>
</div>
@ -182,7 +182,7 @@ function ProgressBar({
const pct = max > 0 ? Math.max(1, (value / max) * 100) : 0
const barColor = { sky: "bg-info", emerald: "bg-success", amber: "bg-warning" }[color]
return (
<div className="h-2 w-full overflow-hidden rounded-full bg-muted/60">
<div className="h-2 w-full overflow-hidden rounded-full bg-muted">
<div
className={cn("h-full rounded-full transition-all duration-700", barColor, animated && "animate-pulse")}
style={{ width: `${pct}%` }}
@ -261,7 +261,7 @@ function ProgressTab({ data }: { data: VisualizerData }) {
{data.milestones.map((ms) => (
<div key={ms.id} className="overflow-hidden rounded-xl border border-border bg-card">
{/* Milestone header */}
<div className="flex items-center justify-between border-b border-border bg-muted/20 px-5 py-4">
<div className="flex items-center justify-between border-b border-border bg-muted/50 px-5 py-4">
<div className="flex items-center gap-3">
{statusIcon(ms.status)}
<span className="font-mono text-xs font-semibold text-muted-foreground">{ms.id}</span>
@ -324,7 +324,7 @@ function ProgressTab({ data }: { data: VisualizerData }) {
"flex items-center gap-2.5 rounded-lg px-3 py-2 transition-colors",
task.active
? "bg-info/8 border border-info/20"
: "hover:bg-muted/40",
: "hover:bg-muted/50",
)}
>
{taskStatusIcon(task)}
@ -332,7 +332,7 @@ function ProgressTab({ data }: { data: VisualizerData }) {
<span
className={cn(
"text-sm",
task.done && "text-muted-foreground/50 line-through",
task.done && "text-muted-foreground line-through",
task.active && "font-semibold text-info",
!task.done && !task.active && "text-muted-foreground",
)}
@ -383,8 +383,8 @@ function DepsTab({ data }: { data: VisualizerData }) {
<span className="rounded-lg border border-info/25 bg-info/10 px-3 py-1.5 font-mono text-sm font-semibold text-info">
{dep}
</span>
<ArrowRight className="h-4 w-4 text-muted-foreground/50" />
<span className="rounded-lg border border-border bg-muted/40 px-3 py-1.5 font-mono text-sm font-medium">
<ArrowRight className="h-4 w-4 text-muted-foreground" />
<span className="rounded-lg border border-border bg-muted/50 px-3 py-1.5 font-mono text-sm font-medium">
{ms.id}
</span>
<span className="text-sm text-muted-foreground">{ms.title}</span>
@ -415,8 +415,8 @@ function DepsTab({ data }: { data: VisualizerData }) {
<span className="rounded-lg border border-info/25 bg-info/10 px-3 py-1.5 font-mono text-sm font-semibold text-info">
{dep}
</span>
<ArrowRight className="h-4 w-4 text-muted-foreground/50" />
<span className="rounded-lg border border-border bg-muted/40 px-3 py-1.5 font-mono text-sm font-medium">
<ArrowRight className="h-4 w-4 text-muted-foreground" />
<span className="rounded-lg border border-border bg-muted/50 px-3 py-1.5 font-mono text-sm font-medium">
{sl.id}
</span>
<span className="text-sm text-muted-foreground">{sl.title}</span>
@ -450,7 +450,7 @@ function DepsTab({ data }: { data: VisualizerData }) {
{id}
</span>
{i < cp.milestonePath.length - 1 && (
<ChevronRight className="h-4 w-4 text-muted-foreground/50" />
<ChevronRight className="h-4 w-4 text-muted-foreground" />
)}
</span>
))}
@ -467,7 +467,7 @@ function DepsTab({ data }: { data: VisualizerData }) {
{data.milestones
.filter((m) => !cp.milestonePath.includes(m.id))
.map((m) => (
<div key={m.id} className="flex items-center gap-4 rounded-lg bg-muted/30 px-4 py-2.5">
<div key={m.id} className="flex items-center gap-4 rounded-lg bg-muted/50 px-4 py-2.5">
<span className="w-16 font-mono text-sm font-semibold">{m.id}</span>
<span className="text-sm text-muted-foreground">{m.title}</span>
<span className="ml-auto font-mono text-xs text-muted-foreground">
@ -492,7 +492,7 @@ function DepsTab({ data }: { data: VisualizerData }) {
{id}
</span>
{i < cp.slicePath.length - 1 && (
<ChevronRight className="h-4 w-4 text-muted-foreground/50" />
<ChevronRight className="h-4 w-4 text-muted-foreground" />
)}
</span>
))}
@ -530,7 +530,7 @@ function DepsTab({ data }: { data: VisualizerData }) {
{Object.entries(cp.sliceSlack).map(([id, slack]) => (
<span
key={id}
className="rounded-lg border border-border bg-muted/40 px-3 py-1.5 font-mono text-xs text-muted-foreground"
className="rounded-lg border border-border bg-muted/50 px-3 py-1.5 font-mono text-xs text-muted-foreground"
>
{id}: {slack}
</span>
@ -639,7 +639,7 @@ function MetricsTab({ data }: { data: VisualizerData }) {
</thead>
<tbody className="divide-y divide-border/50">
{data.bySlice.map((sl) => (
<tr key={sl.sliceId} className="transition-colors hover:bg-muted/30">
<tr key={sl.sliceId} className="transition-colors hover:bg-muted/50">
<td className="py-3 pr-5 font-mono text-xs font-semibold">{sl.sliceId}</td>
<td className="py-3 pr-5 text-right tabular-nums text-muted-foreground">{sl.units}</td>
<td className="py-3 pr-5 text-right tabular-nums font-medium">{formatCost(sl.cost)}</td>
@ -732,7 +732,7 @@ function TimelineTab({ data }: { data: VisualizerData }) {
<div className="space-y-4">
<div className="overflow-hidden rounded-xl border border-border bg-card">
{/* Header */}
<div className="border-b border-border bg-muted/20 px-6 py-4">
<div className="border-b border-border bg-muted/50 px-6 py-4">
<SectionLabel>Execution Timeline</SectionLabel>
<p className="mt-1.5 text-xs text-muted-foreground">
Showing {recent.length} of {data.units.length} units most recent first
@ -758,7 +758,7 @@ function TimelineTab({ data }: { data: VisualizerData }) {
return (
<div
key={`${unit.id}-${unit.startedAt}-${i}`}
className="grid grid-cols-[3.5rem_1.5rem_5rem_8rem_1fr_4.5rem_5rem] items-center gap-3 px-6 py-3.5 transition-colors hover:bg-muted/30"
className="grid grid-cols-[3.5rem_1.5rem_5rem_8rem_1fr_4.5rem_5rem] items-center gap-3 px-6 py-3.5 transition-colors hover:bg-muted/50"
>
<span className="font-mono text-xs text-muted-foreground">
{formatTime(unit.startedAt)}
@ -816,7 +816,7 @@ function AgentTab({ data }: { data: VisualizerData }) {
"relative flex h-10 w-10 items-center justify-center rounded-full",
activity.active
? "bg-success/15"
: "bg-muted/60",
: "bg-muted",
)}>
{activity.active && (
<div className="absolute inset-0 animate-ping rounded-full bg-success/20" />
@ -886,7 +886,7 @@ function AgentTab({ data }: { data: VisualizerData }) {
{/* Recent units */}
{data.units.filter((u) => u.finishedAt > 0).length > 0 && (
<div className="overflow-hidden rounded-xl border border-border bg-card">
<div className="border-b border-border bg-muted/20 px-6 py-4">
<div className="border-b border-border bg-muted/50 px-6 py-4">
<SectionLabel>Recent Completed Units</SectionLabel>
</div>
<div className="divide-y divide-border/40">
@ -895,7 +895,7 @@ function AgentTab({ data }: { data: VisualizerData }) {
.slice(-5)
.reverse()
.map((u, i) => (
<div key={`${u.id}-${i}`} className="flex items-center gap-4 px-6 py-4 transition-colors hover:bg-muted/30">
<div key={`${u.id}-${i}`} className="flex items-center gap-4 px-6 py-4 transition-colors hover:bg-muted/50">
<span className="w-12 font-mono text-xs text-muted-foreground">{formatTime(u.startedAt)}</span>
<CheckCircle2 className="h-4 w-4 shrink-0 text-success" />
<span className="flex-1 truncate text-sm font-medium">{u.type}</span>
@ -927,7 +927,7 @@ function ChangesTab({ data }: { data: VisualizerData }) {
{sorted.map((entry, i) => (
<div key={`${entry.milestoneId}-${entry.sliceId}-${i}`} className="overflow-hidden rounded-xl border border-border bg-card">
{/* Header */}
<div className="flex items-center justify-between border-b border-border bg-muted/20 px-6 py-4">
<div className="flex items-center justify-between border-b border-border bg-muted/50 px-6 py-4">
<div className="flex items-center gap-3">
<CheckCircle2 className="h-4 w-4 shrink-0 text-success" />
<span className="font-mono text-xs font-bold text-success">
@ -956,11 +956,11 @@ function ChangesTab({ data }: { data: VisualizerData }) {
</p>
<div className="space-y-2">
{entry.filesModified.map((f, fi) => (
<div key={fi} className="flex items-start gap-3 rounded-lg bg-muted/30 px-4 py-2.5">
<div key={fi} className="flex items-start gap-3 rounded-lg bg-muted/50 px-4 py-2.5">
<CheckCircle2 className="mt-0.5 h-3.5 w-3.5 shrink-0 text-success/70" />
<span className="font-mono text-xs font-medium text-muted-foreground">{f.path}</span>
{f.description && (
<span className="ml-1 text-xs text-muted-foreground/60"> {f.description}</span>
<span className="ml-1 text-xs text-muted-foreground"> {f.description}</span>
)}
</div>
))}
@ -1069,7 +1069,7 @@ function ExportTab({ data }: { data: VisualizerData }) {
<div className="mt-7 grid gap-4 sm:grid-cols-2">
<button
onClick={handleMarkdown}
className="group flex items-center gap-5 rounded-xl border border-border bg-muted/20 p-5 text-left transition-all hover:border-info/40 hover:bg-info/5"
className="group flex items-center gap-5 rounded-xl border border-border bg-muted/50 p-5 text-left transition-all hover:border-info/40 hover:bg-info/5"
>
<div className="rounded-xl border border-info/20 bg-info/10 p-4 transition-colors group-hover:bg-info/15">
<FileText className="h-6 w-6 text-info" />
@ -1083,7 +1083,7 @@ function ExportTab({ data }: { data: VisualizerData }) {
<button
onClick={handleJSON}
className="group flex items-center gap-5 rounded-xl border border-border bg-muted/20 p-5 text-left transition-all hover:border-success/40 hover:bg-success/5"
className="group flex items-center gap-5 rounded-xl border border-border bg-muted/50 p-5 text-left transition-all hover:border-success/40 hover:bg-success/5"
>
<div className="rounded-xl border border-success/20 bg-success/10 p-4 transition-colors group-hover:bg-success/15">
<FileJson className="h-6 w-6 text-success" />
@ -1147,10 +1147,10 @@ function VisualizerTabList() {
/>
{/* Hover background */}
<span className="absolute inset-x-0 inset-y-1.5 rounded-lg bg-muted/0 transition-colors duration-150 group-hover:bg-muted/60 group-data-[state=active]:bg-transparent" />
<span className="absolute inset-x-0 inset-y-1.5 rounded-lg bg-muted/0 transition-colors duration-150 group-hover:bg-muted group-data-[state=active]:bg-transparent" />
{/* Icon */}
<Icon className="relative h-4 w-4 shrink-0 transition-colors duration-150 text-muted-foreground/70 group-hover:text-foreground/70 group-data-[state=active]:text-foreground" />
<Icon className="relative h-4 w-4 shrink-0 transition-colors duration-150 text-muted-foreground group-hover:text-muted-foreground group-data-[state=active]:text-foreground" />
{/* Label */}
<span className="relative">{label}</span>

View file

@ -7,7 +7,7 @@ function Kbd({ className, ...props }: React.ComponentProps<'kbd'>) {
className={cn(
'bg-muted w-fit text-muted-foreground pointer-events-none inline-flex h-5 min-w-5 items-center justify-center gap-1 rounded-sm px-1 font-sans text-xs font-medium select-none',
"[&_svg:not([class*='size-'])]:size-3",
'[[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background dark:[[data-slot=tooltip-content]_&]:bg-background/10',
'[[data-slot=tooltip-content]_&]:bg-background/50 [[data-slot=tooltip-content]_&]:text-background dark:[[data-slot=tooltip-content]_&]:bg-background/10',
className,
)}
{...props}

View file

@ -405,7 +405,7 @@ function SidebarGroupLabel({
data-slot="sidebar-group-label"
data-sidebar="group-label"
className={cn(
'text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
'text-sidebar-foreground ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
className,
)}

View file

@ -77,7 +77,7 @@ const ToastClose = React.forwardRef<
<ToastPrimitives.Close
ref={ref}
className={cn(
'absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-destructive group-[.destructive]:hover:text-destructive group-[.destructive]:focus:ring-destructive group-[.destructive]:focus:ring-offset-destructive',
'absolute right-2 top-2 rounded-md p-1 text-muted-foreground opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-destructive group-[.destructive]:hover:text-destructive group-[.destructive]:focus:ring-destructive group-[.destructive]:focus:ring-offset-destructive',
className,
)}
toast-close=""