singularity-forge/web/components/sf/command-surface.tsx
ace-pm 172753c3b2 refactor(forge): complete gsd → forge rebrand across native, logging, and build system
- Rename native Rust crates: gsd-engine → forge-engine, gsd-ast → forge-ast, gsd-grep → forge-grep
- Update all crate dependencies (Cargo.toml, .rs source) and N-API artifacts
- Mass rename log prefix [gsd] → [forge] across 81 files (scripts, src/, extensions, tests)
- Rename log prefix "gsd-db:" → "forge-db:" in template literals
- Update nix flake: add sf-run-native devShell with Rust toolchain for native addon builds
- Update CI workflow artifact names (build-native.yml)
- Verify only packages/native/* touched (no upstream pi-* packages renamed)

Rationale: Complete gsd-2 → singularity-forge rebrand (2026-04-15). Native addon is
sf-run-specific; all gsd-prefixed logging and crate names must align with new identity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:11:45 +02:00

2341 lines
99 KiB
TypeScript

"use client"
import { useEffect, useMemo, useRef, useState } from "react"
import {
Archive,
ArrowRightLeft,
Brain,
Check,
ChevronRight,
Cpu,
Download,
ExternalLink,
FileText,
FlaskConical,
FolderRoot,
GitBranch,
KeyRound,
LifeBuoy,
LoaderCircle,
LogIn,
LogOut,
PencilLine,
Radio,
RefreshCw,
Search,
ShieldCheck,
SlidersHorizontal,
SquareTerminal,
X,
} from "lucide-react"
import { toast } from "sonner"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { ScrollArea } from "@/components/ui/scroll-area"
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
} from "@/components/ui/sheet"
import { Switch } from "@/components/ui/switch"
import { Textarea } from "@/components/ui/textarea"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import {
COMMAND_SURFACE_THINKING_LEVELS,
type CommandSurfaceSection,
type CommandSurfaceTarget,
} from "@/lib/command-surface-contract"
import { cn } from "@/lib/utils"
import {
DEV_OVERRIDE_REGISTRY,
useDevOverrides,
} from "@/lib/dev-overrides"
import { DoctorPanel, ForensicsPanel, SkillHealthPanel } from "./diagnostics-panels"
import { KnowledgeCapturesPanel } from "./knowledge-captures-panel"
import { PrefsPanel, ModelRoutingPanel, BudgetPanel, RemoteQuestionsPanel, GeneralPanel, ExperimentalPanel } from "./settings-panels"
import { DevRootSettingsSection } from "./projects-view"
import {
QuickPanel,
HistoryPanel,
UndoPanel,
SteerPanel,
HooksPanel,
InspectPanel,
ExportPanel,
CleanupPanel,
QueuePanel,
StatusPanel,
} from "./remaining-command-panels"
import {
formatCost,
formatTokens,
getModelLabel,
getSessionLabelFromBridge,
shortenPath,
useGSDWorkspaceActions,
useGSDWorkspaceState,
} from "@/lib/sf-workspace-store"
// ─── Section metadata ────────────────────────────────────────────────
const SETTINGS_SURFACE_SECTIONS = ["general", "model", "session-behavior", "recovery", "auth", "integrations", "workspace", "experimental"] as const
const ADMIN_SECTION: CommandSurfaceSection = "admin"
const GIT_SURFACE_SECTIONS = ["git"] as const
const SESSION_SURFACE_SECTIONS = ["resume", "name", "fork", "session", "compact"] as const
function availableSectionsForSurface(surface: string | null, includeAdmin: boolean = false): CommandSurfaceSection[] {
switch (surface) {
case "git":
return [...GIT_SURFACE_SECTIONS]
case "resume":
case "name":
case "fork":
case "session":
case "export":
case "compact":
return [...SESSION_SURFACE_SECTIONS]
default:
return includeAdmin
? [...SETTINGS_SURFACE_SECTIONS, ADMIN_SECTION]
: [...SETTINGS_SURFACE_SECTIONS]
}
}
function sectionLabel(section: CommandSurfaceSection): string {
const labels: Partial<Record<CommandSurfaceSection, string>> = {
general: "General",
model: "Model",
thinking: "Thinking",
queue: "Queue",
compaction: "Compaction",
retry: "Retry",
"session-behavior": "Session",
recovery: "Recovery",
auth: "Auth",
admin: "Admin",
git: "Git",
resume: "Resume",
name: "Name",
fork: "Fork",
session: "Session",
compact: "Compact",
workspace: "Workspace",
integrations: "Integrations",
experimental: "Experimental",
}
return labels[section] ?? section
}
function sectionIcon(section: CommandSurfaceSection) {
const icons: Partial<Record<CommandSurfaceSection, React.ReactNode>> = {
general: <SlidersHorizontal className="h-4 w-4" />,
model: <Cpu className="h-4 w-4" />,
thinking: <Brain className="h-4 w-4" />,
queue: <ArrowRightLeft className="h-4 w-4" />,
compaction: <Archive className="h-4 w-4" />,
retry: <RefreshCw className="h-4 w-4" />,
"session-behavior": <ArrowRightLeft className="h-4 w-4" />,
recovery: <LifeBuoy className="h-4 w-4" />,
auth: <ShieldCheck className="h-4 w-4" />,
admin: <SquareTerminal className="h-4 w-4" />,
git: <GitBranch className="h-4 w-4" />,
resume: <ArrowRightLeft className="h-4 w-4" />,
name: <PencilLine className="h-4 w-4" />,
fork: <GitBranch className="h-4 w-4" />,
session: <FileText className="h-4 w-4" />,
compact: <Archive className="h-4 w-4" />,
workspace: <FolderRoot className="h-4 w-4" />,
integrations: <Radio className="h-4 w-4" />,
experimental: <FlaskConical className="h-4 w-4" />,
}
return icons[section] ?? null
}
function surfaceTitle(surface: string | null): string {
const titles: Record<string, string> = {
model: "Model",
thinking: "Thinking",
git: "Git",
login: "Login",
logout: "Logout",
settings: "Settings",
resume: "Resume",
name: "Name",
fork: "Fork",
session: "Session",
export: "Export",
compact: "Compact",
}
return titles[surface ?? ""] ?? "Settings"
}
function currentAuthIntent(activeSurface: string | null, selectedTarget: CommandSurfaceTarget | null): "login" | "logout" | "manage" {
if (selectedTarget?.kind === "auth") return selectedTarget.intent
if (activeSurface === "login") return "login"
if (activeSurface === "logout") return "logout"
return "manage"
}
function formatRelativeTime(isoDate: string): string {
const now = Date.now()
const then = new Date(isoDate).getTime()
const diffMs = now - then
if (diffMs < 60_000) return "just now"
const minutes = Math.floor(diffMs / 60_000)
if (minutes < 60) return `${minutes}m ago`
const hours = Math.floor(minutes / 60)
if (hours < 24) return `${hours}h ago`
const days = Math.floor(hours / 24)
return `${days}d ago`
}
// ─── Inline status dot ──────────────────────────────────────────────
function StatusDot({ status }: { status: "ok" | "warning" | "error" | "idle" }) {
return (
<span
className={cn(
"inline-block h-1.5 w-1.5 rounded-full",
status === "ok" && "bg-success",
status === "warning" && "bg-warning",
status === "error" && "bg-destructive",
status === "idle" && "bg-foreground/20",
)}
/>
)
}
// ─── Inline section header ──────────────────────────────────────────
function SectionHeader({
title,
action,
status,
}: {
title: string
action?: React.ReactNode
status?: React.ReactNode
}) {
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-muted-foreground">{title}</h3>
{status}
</div>
{action}
</div>
)
}
// ─── Inline key-value row ───────────────────────────────────────────
function KV({ label, children, mono }: { label: string; children: React.ReactNode; mono?: boolean }) {
return (
<div className="flex items-baseline justify-between gap-4 py-1.5 text-sm">
<span className="shrink-0 text-muted-foreground">{label}</span>
<span className={cn("text-right text-foreground", mono && "font-mono text-xs")}>{children}</span>
</div>
)
}
// ─── Toggle row: label + switch ─────────────────────────────────────
function ToggleRow({
label,
description,
checked,
onCheckedChange,
disabled,
busy,
testId,
}: {
label: string
description?: string
checked: boolean
onCheckedChange: (checked: boolean) => void
disabled?: boolean
busy?: boolean
testId?: string
}) {
return (
<div className="flex items-start justify-between gap-4 rounded-lg border border-border/50 bg-card/50 px-4 py-3">
<div className="min-w-0">
<div className="flex items-center gap-2 text-sm font-medium text-foreground">
{label}
{busy && <LoaderCircle className="h-3 w-3 animate-spin text-muted-foreground" />}
</div>
{description && <p className="mt-0.5 text-xs text-muted-foreground">{description}</p>}
</div>
<Switch checked={checked} onCheckedChange={onCheckedChange} disabled={disabled || busy} data-testid={testId} />
</div>
)
}
// ─── Segmented control ──────────────────────────────────────────────
function SegmentedControl<T extends string>({
options,
value,
onChange,
disabled,
}: {
options: { value: T; label: string }[]
value: T | null
onChange: (value: T) => void
disabled?: boolean
}) {
return (
<div className="inline-flex rounded-lg border border-border bg-card/50 p-0.5">
{options.map((opt) => (
<button
key={opt.value}
type="button"
className={cn(
"rounded-md px-3 py-1.5 text-xs font-medium transition-all",
value === opt.value
? "bg-foreground/10 text-foreground shadow-sm"
: "text-muted-foreground hover:text-foreground",
)}
onClick={() => onChange(opt.value)}
disabled={disabled || value === opt.value}
>
{opt.label}
</button>
))}
</div>
)
}
// ═════════════════════════════════════════════════════════════════════
// MAIN COMPONENT
// ═════════════════════════════════════════════════════════════════════
export function CommandSurface() {
const workspace = useGSDWorkspaceState()
const {
closeCommandSurface,
openCommandSurface,
refreshBoot,
setCommandSurfaceSection,
selectCommandSurfaceTarget,
loadGitSummary,
loadRecoveryDiagnostics,
loadForensicsDiagnostics,
loadDoctorDiagnostics,
loadSkillHealthDiagnostics,
loadKnowledgeData,
loadCapturesData,
loadSettingsData,
updateSessionBrowserState,
loadSessionBrowser,
renameSessionFromSurface,
loadAvailableModels,
applyModelSelection,
applyThinkingLevel,
setSteeringModeFromSurface,
setFollowUpModeFromSurface,
setAutoCompactionFromSurface,
setAutoRetryFromSurface,
abortRetryFromSurface,
switchSessionFromSurface,
loadSessionStats,
exportSessionFromSurface,
loadForkMessages,
forkSessionFromSurface,
compactSessionFromSurface,
saveApiKeyFromSurface,
startProviderFlowFromSurface,
submitProviderFlowInputFromSurface,
cancelProviderFlowFromSurface,
logoutProviderFromSurface,
loadHistoryData,
loadInspectData,
loadHooksData,
loadUndoInfo,
loadCleanupData,
loadSteerData,
} = useGSDWorkspaceActions()
const { commandSurface } = workspace
const onboarding = workspace.boot?.onboarding ?? null
const activeFlow = onboarding?.activeFlow ?? null
const gitSummary = commandSurface.gitSummary
const recovery = commandSurface.recovery
const sessionBrowser = commandSurface.sessionBrowser
const liveSessionState = workspace.boot?.bridge.sessionState ?? null
const settingsRequests = commandSurface.settingsRequests
const currentModelLabel = getModelLabel(workspace.boot?.bridge)
const currentSessionLabel = getSessionLabelFromBridge(workspace.boot?.bridge)
const [apiKeys, setApiKeys] = useState<Record<string, string>>({})
const [flowInput, setFlowInput] = useState("")
const commandSurfaceViewportRef = useRef<HTMLDivElement>(null)
// ─── Auto-loaders ──────────────────────────────────────────────────
useEffect(() => {
if (!commandSurface.open || commandSurface.section !== "model") return
if (commandSurface.availableModels.length > 0) return
if (commandSurface.pendingAction === "loading_models") return
void loadAvailableModels()
}, [commandSurface.open, commandSurface.section, commandSurface.availableModels.length, commandSurface.pendingAction, loadAvailableModels])
useEffect(() => {
if (!commandSurface.open || commandSurface.section !== "git") return
if (commandSurface.pendingAction === "load_git_summary") return
if (commandSurface.gitSummary.loaded || commandSurface.gitSummary.error) return
void loadGitSummary()
}, [commandSurface.open, commandSurface.section, commandSurface.pendingAction, commandSurface.gitSummary.loaded, commandSurface.gitSummary.error, loadGitSummary])
useEffect(() => {
if (!commandSurface.open || commandSurface.section !== "recovery") return
if (commandSurface.pendingAction === "load_recovery_diagnostics") return
if (commandSurface.recovery.pending) return
if (commandSurface.recovery.loaded && !commandSurface.recovery.stale && !commandSurface.recovery.error) return
void loadRecoveryDiagnostics()
}, [
commandSurface.open,
commandSurface.section,
commandSurface.pendingAction,
commandSurface.recovery.pending,
commandSurface.recovery.loaded,
commandSurface.recovery.stale,
commandSurface.recovery.error,
loadRecoveryDiagnostics,
])
// Auto-fetch diagnostics panels when their sections open
const diagnostics = commandSurface.diagnostics
const knowledgeCaptures = commandSurface.knowledgeCaptures
const settingsData = commandSurface.settingsData
const remainingCommands = commandSurface.remainingCommands
useEffect(() => {
if (!commandSurface.open) return
if (commandSurface.section === "gsd-forensics" && diagnostics.forensics.phase === "idle") {
void loadForensicsDiagnostics()
} else if (commandSurface.section === "gsd-doctor" && diagnostics.doctor.phase === "idle") {
void loadDoctorDiagnostics()
} else if (commandSurface.section === "gsd-skill-health" && diagnostics.skillHealth.phase === "idle") {
void loadSkillHealthDiagnostics()
} else if (
commandSurface.section === "gsd-knowledge" &&
knowledgeCaptures.knowledge.phase === "idle"
) {
void loadKnowledgeData()
void loadCapturesData()
} else if (
(commandSurface.section === "gsd-capture" || commandSurface.section === "gsd-triage") &&
knowledgeCaptures.captures.phase === "idle"
) {
void loadCapturesData()
void loadKnowledgeData()
} else if (
(commandSurface.section === "gsd-prefs" ||
commandSurface.section === "gsd-mode" ||
commandSurface.section === "gsd-config" ||
commandSurface.section === "experimental") &&
settingsData.phase === "idle"
) {
void loadSettingsData()
} else if (commandSurface.section === "gsd-history" && remainingCommands.history.phase === "idle") {
void loadHistoryData()
} else if (commandSurface.section === "gsd-inspect" && remainingCommands.inspect.phase === "idle") {
void loadInspectData()
} else if (commandSurface.section === "gsd-hooks" && remainingCommands.hooks.phase === "idle") {
void loadHooksData()
} else if (commandSurface.section === "gsd-undo" && remainingCommands.undo.phase === "idle") {
void loadUndoInfo()
} else if (commandSurface.section === "gsd-cleanup" && remainingCommands.cleanup.phase === "idle") {
void loadCleanupData()
} else if (commandSurface.section === "gsd-steer" && remainingCommands.steer.phase === "idle") {
void loadSteerData()
}
}, [
commandSurface.open,
commandSurface.section,
diagnostics.forensics.phase,
diagnostics.doctor.phase,
diagnostics.skillHealth.phase,
knowledgeCaptures.knowledge.phase,
knowledgeCaptures.captures.phase,
settingsData.phase,
remainingCommands.history.phase,
remainingCommands.inspect.phase,
remainingCommands.hooks.phase,
remainingCommands.undo.phase,
remainingCommands.cleanup.phase,
remainingCommands.steer.phase,
loadForensicsDiagnostics,
loadDoctorDiagnostics,
loadSkillHealthDiagnostics,
loadKnowledgeData,
loadCapturesData,
loadSettingsData,
loadHistoryData,
loadInspectData,
loadHooksData,
loadUndoInfo,
loadCleanupData,
loadSteerData,
])
useEffect(() => {
if (!commandSurface.open || (commandSurface.section !== "resume" && commandSurface.section !== "name")) return
if (commandSurface.pendingAction === "load_session_browser") return
if (commandSurface.sessionBrowser.loaded) return
void loadSessionBrowser()
}, [commandSurface.open, commandSurface.section, commandSurface.pendingAction, commandSurface.sessionBrowser.loaded, loadSessionBrowser])
useEffect(() => {
if (!commandSurface.open) return
const viewport = commandSurfaceViewportRef.current
if (!viewport) return
viewport.scrollTop = 0
}, [commandSurface.open, commandSurface.activeSurface, commandSurface.section])
useEffect(() => {
if (!commandSurface.open || commandSurface.section !== "session") return
if (commandSurface.sessionStats) return
if (commandSurface.pendingAction === "load_session_stats") return
void loadSessionStats()
}, [commandSurface.open, commandSurface.section, commandSurface.sessionStats, commandSurface.pendingAction, loadSessionStats])
useEffect(() => {
if (!commandSurface.open || commandSurface.section !== "fork") return
if (commandSurface.forkMessages.length > 0) return
if (commandSurface.pendingAction === "load_fork_messages") return
void loadForkMessages()
}, [commandSurface.open, commandSurface.section, commandSurface.forkMessages.length, commandSurface.pendingAction, loadForkMessages])
useEffect(() => {
if (!commandSurface.open || commandSurface.section !== "resume") return
const selectedResumeTarget = commandSurface.selectedTarget?.kind === "resume" ? commandSurface.selectedTarget : null
if (selectedResumeTarget?.sessionPath) return
const defaultSession = sessionBrowser.sessions.find((session) => !session.isActive) ?? sessionBrowser.sessions[0]
if (!defaultSession) return
selectCommandSurfaceTarget({ kind: "resume", sessionPath: defaultSession.path })
}, [commandSurface.open, commandSurface.section, commandSurface.selectedTarget, sessionBrowser.sessions, selectCommandSurfaceTarget])
useEffect(() => {
if (!commandSurface.open || commandSurface.section !== "name") return
const selectedNameTarget = commandSurface.selectedTarget?.kind === "name" ? commandSurface.selectedTarget : null
if (selectedNameTarget?.sessionPath) return
const defaultSession = sessionBrowser.sessions.find((session) => session.isActive) ?? sessionBrowser.sessions[0]
if (!defaultSession) return
selectCommandSurfaceTarget({ kind: "name", sessionPath: defaultSession.path, name: defaultSession.name ?? "" })
}, [commandSurface.open, commandSurface.section, commandSurface.selectedTarget, sessionBrowser.sessions, selectCommandSurfaceTarget])
useEffect(() => {
const resetTimer = window.setTimeout(() => {
setFlowInput("")
}, 0)
return () => window.clearTimeout(resetTimer)
}, [activeFlow?.flowId])
// ─── Toast on action results ───────────────────────────────────────
useEffect(() => {
if (commandSurface.lastError) {
toast.error(commandSurface.lastError)
}
}, [commandSurface.lastError])
useEffect(() => {
if (commandSurface.lastResult) {
toast.success(commandSurface.lastResult)
}
}, [commandSurface.lastResult])
// ─── Derived state ─────────────────────────────────────────────────
const selectedModelTarget = commandSurface.selectedTarget?.kind === "model" ? commandSurface.selectedTarget : null
const selectedThinkingTarget = commandSurface.selectedTarget?.kind === "thinking" ? commandSurface.selectedTarget : null
const selectedAuthTarget = commandSurface.selectedTarget?.kind === "auth" ? commandSurface.selectedTarget : null
const selectedResumeTarget = commandSurface.selectedTarget?.kind === "resume" ? commandSurface.selectedTarget : null
const selectedNameTarget = commandSurface.selectedTarget?.kind === "name" ? commandSurface.selectedTarget : null
const selectedForkTarget = commandSurface.selectedTarget?.kind === "fork" ? commandSurface.selectedTarget : null
const selectedSessionTarget = commandSurface.selectedTarget?.kind === "session" ? commandSurface.selectedTarget : null
const selectedCompactTarget = commandSurface.selectedTarget?.kind === "compact" ? commandSurface.selectedTarget : null
const selectedAuthIntent = currentAuthIntent(commandSurface.activeSurface, commandSurface.selectedTarget)
const selectedAuthProvider = onboarding?.required.providers.find((provider) => provider.id === selectedAuthTarget?.providerId) ?? null
const modelQuery = (selectedModelTarget?.query ?? commandSurface.args).trim().toLowerCase()
const filteredModels = useMemo(() => {
if (!modelQuery) return commandSurface.availableModels
return commandSurface.availableModels.filter((model) =>
`${model.provider} ${model.modelId} ${model.name ?? ""}`.toLowerCase().includes(modelQuery),
)
}, [commandSurface.availableModels, modelQuery])
// Group filtered models by provider for display
const groupedModels = useMemo(() => {
const groups = new Map<string, typeof filteredModels>()
for (const model of filteredModels) {
const key = model.provider
const existing = groups.get(key)
if (existing) existing.push(model)
else groups.set(key, [model])
}
return groups
}, [filteredModels])
const authBusy = workspace.onboardingRequestState !== "idle"
const modelBusy = commandSurface.pendingAction === "loading_models" || workspace.commandInFlight === "get_available_models"
const gitSummaryBusy = commandSurface.pendingAction === "load_git_summary"
const recoveryBusy = commandSurface.pendingAction === "load_recovery_diagnostics" || recovery.pending
const recoveryDiagnostics = recovery.diagnostics
const sessionBrowserBusy = commandSurface.pendingAction === "load_session_browser"
const forkBusy = commandSurface.pendingAction === "load_fork_messages" || commandSurface.pendingAction === "fork_session"
const sessionBusy = commandSurface.pendingAction === "load_session_stats" || commandSurface.pendingAction === "export_html"
const resumeBusy = commandSurface.pendingAction === "switch_session"
const renameBusy = commandSurface.pendingAction === "rename_session"
const compactBusy = commandSurface.pendingAction === "compact_session" || liveSessionState?.isCompacting === true
const queueBusy = settingsRequests.steeringMode.pending || settingsRequests.followUpMode.pending
const autoCompactionBusy = settingsRequests.autoCompaction.pending
const autoRetryBusy = settingsRequests.autoRetry.pending
const abortRetryBusy = settingsRequests.abortRetry.pending
const selectedProviderApiKey = selectedAuthProvider ? apiKeys[selectedAuthProvider.id] ?? "" : ""
const devOverrides = useDevOverrides()
const surfaceSections = availableSectionsForSurface(commandSurface.activeSurface, devOverrides.isDevMode)
const surfaceKindLabel = `/${commandSurface.activeSurface ?? "settings"}`
const triggerRecoveryBrowserAction = (actionId: string) => {
switch (actionId) {
case "refresh_diagnostics":
void loadRecoveryDiagnostics()
return
case "refresh_workspace":
void refreshBoot({ soft: true })
return
case "open_retry_controls":
setCommandSurfaceSection("retry")
return
case "open_resume_controls":
openCommandSurface("resume", { source: "surface" })
return
case "open_auth_controls":
setCommandSurfaceSection("auth")
return
default:
return
}
}
// ═══════════════════════════════════════════════════════════════════
// SECTION RENDERERS
// ═══════════════════════════════════════════════════════════════════
const renderModelSection = () => (
<div className="space-y-4" data-testid="command-surface-models">
<SectionHeader
title="Model"
status={
<span className="font-mono text-xs text-muted-foreground">{currentModelLabel}</span>
}
action={
<Button type="button" variant="ghost" size="sm" onClick={() => void loadAvailableModels()} disabled={modelBusy} className="h-7 gap-1.5 text-xs">
<RefreshCw className={cn("h-3 w-3", modelBusy && "animate-spin")} />
Refresh
</Button>
}
/>
{/* Search filter */}
<div className="relative">
<Search className="absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" />
<Input
value={selectedModelTarget?.query ?? commandSurface.args}
onChange={(e) =>
selectCommandSurfaceTarget({
kind: "model",
provider: selectedModelTarget?.provider,
modelId: selectedModelTarget?.modelId,
query: e.target.value,
})
}
placeholder="Filter models…"
className="h-8 pl-9 text-xs"
/>
</div>
{/* Model list */}
{modelBusy && commandSurface.availableModels.length === 0 ? (
<div className="flex items-center gap-2 py-8 text-xs text-muted-foreground">
<LoaderCircle className="h-3.5 w-3.5 animate-spin" />
Loading models
</div>
) : filteredModels.length > 0 ? (
<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">
{provider}
</div>
<div className="space-y-0.5">
{models.map((model) => {
const selected = selectedModelTarget?.provider === model.provider && selectedModelTarget?.modelId === model.modelId
return (
<button
key={`${model.provider}/${model.modelId}`}
type="button"
className={cn(
"group flex w-full items-center gap-3 rounded-lg px-3 py-2 text-left transition-colors",
selected
? "bg-foreground/[0.07]"
: "hover:bg-foreground/[0.03]",
)}
onClick={() =>
selectCommandSurfaceTarget({
kind: "model",
provider: model.provider,
modelId: model.modelId,
query: selectedModelTarget?.query,
})
}
>
{/* Selection indicator */}
<div className={cn(
"flex h-4 w-4 shrink-0 items-center justify-center rounded-full border transition-colors",
selected ? "border-foreground bg-foreground" : "border-foreground/25",
)}>
{selected && <Check className="h-2.5 w-2.5 text-background" />}
</div>
{/* Model info */}
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-foreground">{model.name || model.modelId}</span>
{model.isCurrent && <StatusDot status="ok" />}
</div>
<div className="mt-0.5 font-mono text-[11px] text-muted-foreground">
{model.modelId}
</div>
</div>
{/* 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-muted-foreground">Active</span>
)}
{model.reasoning && (
<span className="rounded bg-foreground/10 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">Thinking</span>
)}
</div>
</button>
)
})}
</div>
</div>
))}
</div>
) : (
<p className="py-6 text-center text-xs text-muted-foreground">No models matched.</p>
)}
{/* Apply */}
<div className="flex justify-end border-t border-border/50 pt-3">
<Button
type="button"
size="sm"
onClick={() =>
selectedModelTarget?.provider &&
selectedModelTarget?.modelId &&
void applyModelSelection(selectedModelTarget.provider, selectedModelTarget.modelId)
}
disabled={!selectedModelTarget?.provider || !selectedModelTarget.modelId || commandSurface.pendingAction === "set_model"}
data-testid="command-surface-apply-model"
className="h-8 gap-1.5"
>
{commandSurface.pendingAction === "set_model" ? (
<LoaderCircle className="h-3.5 w-3.5 animate-spin" />
) : (
<Check className="h-3.5 w-3.5" />
)}
Apply model
</Button>
</div>
</div>
)
const renderThinkingSection = () => (
<div className="space-y-4" data-testid="command-surface-thinking">
<SectionHeader
title="Thinking level"
status={
<span className="font-mono text-xs text-muted-foreground">
{workspace.boot?.bridge.sessionState?.thinkingLevel ?? "off"}
</span>
}
/>
<div className="space-y-1">
{COMMAND_SURFACE_THINKING_LEVELS.map((level) => {
const selected = selectedThinkingTarget?.level === level
const isCurrent = workspace.boot?.bridge.sessionState?.thinkingLevel === level
const description = level === "off" ? "No reasoning overhead" : level === "minimal" ? "Light reasoning" : level === "low" ? "Basic analysis" : level === "medium" ? "Balanced reasoning" : level === "high" ? "Deep analysis" : "Maximum deliberation"
return (
<button
key={level}
type="button"
className={cn(
"flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left transition-colors",
selected ? "bg-foreground/[0.07]" : "hover:bg-foreground/[0.03]",
)}
onClick={() => selectCommandSurfaceTarget({ kind: "thinking", level })}
>
<div className={cn(
"flex h-4 w-4 shrink-0 items-center justify-center rounded-full border transition-colors",
selected ? "border-foreground bg-foreground" : "border-foreground/25",
)}>
{selected && <Check className="h-2.5 w-2.5 text-background" />}
</div>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="text-sm font-medium capitalize text-foreground">{level}</span>
{isCurrent && <StatusDot status="ok" />}
</div>
<span className="text-xs text-muted-foreground">{description}</span>
</div>
</button>
)
})}
</div>
<div className="flex justify-end border-t border-border/50 pt-3">
<Button
type="button"
size="sm"
onClick={() => selectedThinkingTarget && void applyThinkingLevel(selectedThinkingTarget.level)}
disabled={!selectedThinkingTarget || commandSurface.pendingAction === "set_thinking_level"}
data-testid="command-surface-apply-thinking"
className="h-8 gap-1.5"
>
{commandSurface.pendingAction === "set_thinking_level" ? (
<LoaderCircle className="h-3.5 w-3.5 animate-spin" />
) : (
<Check className="h-3.5 w-3.5" />
)}
Apply
</Button>
</div>
</div>
)
const renderQueueSection = () => (
<div className="space-y-5" data-testid="command-surface-queue-settings">
<SectionHeader title="Queue modes" />
{/* Steering mode */}
<div className="space-y-3">
<div className="flex items-center justify-between">
<div>
<div className="text-sm font-medium text-foreground">Steering mode</div>
<p className="text-xs text-muted-foreground">How steering messages queue during streaming</p>
</div>
{settingsRequests.steeringMode.pending && <LoaderCircle className="h-3.5 w-3.5 animate-spin text-muted-foreground" />}
</div>
<SegmentedControl
options={[
{ value: "all" as const, label: "Queue all" },
{ value: "one-at-a-time" as const, label: "One at a time" },
]}
value={liveSessionState?.steeringMode ?? null}
onChange={(v) => void setSteeringModeFromSurface(v)}
disabled={!liveSessionState || queueBusy}
/>
{settingsRequests.steeringMode.error && (
<p className="text-xs text-destructive">{settingsRequests.steeringMode.error}</p>
)}
</div>
<div className="border-t border-border/50" />
{/* Follow-up mode */}
<div className="space-y-3">
<div className="flex items-center justify-between">
<div>
<div className="text-sm font-medium text-foreground">Follow-up mode</div>
<p className="text-xs text-muted-foreground">How follow-up prompts sequence during a live turn</p>
</div>
{settingsRequests.followUpMode.pending && <LoaderCircle className="h-3.5 w-3.5 animate-spin text-muted-foreground" />}
</div>
<SegmentedControl
options={[
{ value: "all" as const, label: "Queue all" },
{ value: "one-at-a-time" as const, label: "One at a time" },
]}
value={liveSessionState?.followUpMode ?? null}
onChange={(v) => void setFollowUpModeFromSurface(v)}
disabled={!liveSessionState || queueBusy}
/>
{settingsRequests.followUpMode.error && (
<p className="text-xs text-destructive">{settingsRequests.followUpMode.error}</p>
)}
</div>
</div>
)
const renderCompactionSection = () => (
<div className="space-y-4" data-testid="command-surface-auto-compaction-settings">
<SectionHeader
title="Auto-compaction"
status={
liveSessionState?.isCompacting ? (
<span className="flex items-center gap-1.5 text-xs text-warning">
<LoaderCircle className="h-3 w-3 animate-spin" /> Compacting
</span>
) : null
}
/>
<ToggleRow
label="Auto-compact"
description="Automatically compact when context thresholds are crossed"
checked={liveSessionState?.autoCompactionEnabled ?? false}
onCheckedChange={(checked) => void setAutoCompactionFromSurface(checked)}
disabled={!liveSessionState || autoCompactionBusy}
busy={autoCompactionBusy}
testId="command-surface-toggle-auto-compaction"
/>
{settingsRequests.autoCompaction.error && (
<p className="text-xs text-destructive">{settingsRequests.autoCompaction.error}</p>
)}
{settingsRequests.autoCompaction.result && (
<p className="text-xs text-success">{settingsRequests.autoCompaction.result}</p>
)}
</div>
)
const renderRetrySection = () => (
<div className="space-y-4" data-testid="command-surface-retry-settings">
<SectionHeader
title="Retry"
status={
liveSessionState?.retryInProgress ? (
<span className="flex items-center gap-1.5 text-xs text-warning">
<Radio className="h-3 w-3" /> Attempt {Math.max(1, liveSessionState.retryAttempt)}
</span>
) : null
}
/>
<ToggleRow
label="Auto-retry"
description="Automatically retry on transient failures"
checked={liveSessionState?.autoRetryEnabled ?? false}
onCheckedChange={(checked) => void setAutoRetryFromSurface(checked)}
disabled={!liveSessionState || autoRetryBusy}
busy={autoRetryBusy}
testId="command-surface-toggle-auto-retry"
/>
<p className="text-xs text-muted-foreground" data-testid="command-surface-auto-retry-state">
{autoRetryBusy
? "Updating auto-retry…"
: settingsRequests.autoRetry.error
? settingsRequests.autoRetry.error
: settingsRequests.autoRetry.result
? settingsRequests.autoRetry.result
: liveSessionState?.autoRetryEnabled
? "Auto-retry enabled"
: "Auto-retry disabled"}
</p>
{liveSessionState?.retryInProgress && (
<div className="flex items-center justify-between rounded-lg border border-warning/20 bg-warning/5 px-4 py-3">
<div>
<div className="text-sm font-medium text-foreground">Retry in progress</div>
<p className="text-xs text-muted-foreground">Attempt {Math.max(1, liveSessionState.retryAttempt)} is active</p>
</div>
<Button
type="button"
variant="destructive"
size="sm"
onClick={() => void abortRetryFromSurface()}
disabled={abortRetryBusy}
data-testid="command-surface-abort-retry"
className="h-7 gap-1.5 text-xs"
>
{abortRetryBusy ? <LoaderCircle className="h-3 w-3 animate-spin" /> : <X className="h-3 w-3" />}
Abort
</Button>
</div>
)}
{settingsRequests.autoRetry.error && <p className="text-xs text-destructive">{settingsRequests.autoRetry.error}</p>}
<p className="text-xs text-muted-foreground" data-testid="command-surface-abort-retry-state">
{abortRetryBusy
? "Aborting retry…"
: settingsRequests.abortRetry.error
? settingsRequests.abortRetry.error
: settingsRequests.abortRetry.result
? settingsRequests.abortRetry.result
: liveSessionState?.retryInProgress
? "Retry can be aborted"
: "No retry in progress"}
</p>
{settingsRequests.abortRetry.error && <p className="text-xs text-destructive">{settingsRequests.abortRetry.error}</p>}
</div>
)
const renderRecoverySection = () => {
const diag = recoveryDiagnostics
return (
<div className="space-y-4" data-testid="command-surface-recovery">
<div className="text-xs text-muted-foreground" data-testid="command-surface-recovery-state">
{recoveryBusy
? "Loading recovery diagnostics…"
: recovery.error
? "Recovery diagnostics failed"
: recovery.stale
? "Recovery diagnostics stale"
: recovery.loaded
? "Recovery diagnostics loaded"
: "Recovery diagnostics idle"}
</div>
<SectionHeader
title="Recovery"
status={
diag ? (
<StatusDot status={diag.summary.tone === "healthy" ? "ok" : diag.summary.tone === "warning" ? "warning" : "error"} />
) : null
}
action={
<Button type="button" variant="ghost" size="sm" onClick={() => void loadRecoveryDiagnostics()} disabled={recoveryBusy} className="h-7 gap-1.5 text-xs">
<RefreshCw className={cn("h-3 w-3", recoveryBusy && "animate-spin")} />
Refresh
</Button>
}
/>
{recovery.error && (
<div
className="rounded-lg border border-destructive/20 bg-destructive/5 px-3 py-2.5 text-xs text-destructive"
data-testid="command-surface-recovery-error"
>
{recovery.error}
</div>
)}
{recoveryBusy && !diag && (
<>
<div className="flex items-center gap-2 py-6 text-xs text-muted-foreground">
<LoaderCircle className="h-3.5 w-3.5 animate-spin" />
Loading diagnostics
</div>
<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"
size="sm"
onClick={() => void loadRecoveryDiagnostics()}
data-testid="command-surface-recovery-action-refresh_diagnostics"
className="h-7 text-xs"
>
Refresh diagnostics
</Button>
</div>
</>
)}
{diag?.status === "unavailable" && !recovery.error && (
<>
<div className="space-y-1 rounded-lg border border-border/50 bg-card/50 px-4 py-3" data-testid="command-surface-recovery-summary">
<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/50 pt-3" data-testid="command-surface-recovery-actions">
<Button
type="button"
variant="default"
size="sm"
onClick={() => void loadRecoveryDiagnostics()}
data-testid="command-surface-recovery-action-refresh_diagnostics"
className="h-7 text-xs"
>
Refresh diagnostics
</Button>
</div>
</>
)}
{diag && diag.status !== "unavailable" && (
<>
<div className="space-y-1" data-testid="command-surface-recovery-summary">
<div className="text-sm font-medium text-foreground">{diag.summary.label}</div>
<p className="text-xs text-muted-foreground">{diag.summary.detail}</p>
</div>
{/* Summary stats */}
<div className="grid grid-cols-2 gap-2">
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-2.5">
<div className="text-[10px] uppercase tracking-wider text-muted-foreground">Validation</div>
<div className="mt-1 text-lg font-semibold tabular-nums text-foreground">{diag.summary.validationCount}</div>
</div>
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-2.5">
<div className="text-[10px] uppercase tracking-wider text-muted-foreground">Doctor</div>
<div className="mt-1 text-lg font-semibold tabular-nums text-foreground">{diag.summary.doctorIssueCount}</div>
</div>
</div>
{/* Status badges */}
<div className="flex flex-wrap gap-1.5">
{diag.summary.retryInProgress && <Badge variant="default" className="text-[10px]">Retry {Math.max(1, diag.summary.retryAttempt)}</Badge>}
{diag.summary.compactionActive && <Badge variant="default" className="text-[10px]">Compacting</Badge>}
{diag.summary.lastFailurePhase && <Badge variant="destructive" className="text-[10px]">Phase {diag.summary.lastFailurePhase}</Badge>}
{recovery.stale && <Badge variant="outline" className="text-[10px]">Stale</Badge>}
</div>
{/* Last failure */}
{diag.bridge.lastFailure && (
<div
className="rounded-lg border border-destructive/20 bg-destructive/5 px-3 py-2.5"
data-testid="command-surface-recovery-last-failure"
>
<div className="text-xs font-medium text-destructive">Last failure</div>
<p className="mt-1 text-xs text-destructive/80">{diag.bridge.lastFailure.message}</p>
<div className="mt-1.5 flex gap-3 text-[10px] text-destructive/60">
<span>Phase: {diag.bridge.lastFailure.phase}</span>
<span>{formatRelativeTime(diag.bridge.lastFailure.at)}</span>
</div>
</div>
)}
{/* Validation issues */}
{diag.validation.topIssues.length > 0 && (
<div className="space-y-2">
<div className="text-xs font-medium text-muted-foreground">Validation issues</div>
{diag.validation.topIssues.map((issue) => (
<div key={`${issue.code}:${issue.file ?? issue.message}`} className="rounded-lg border border-border/50 bg-card/50 px-3 py-2">
<div className="flex items-center gap-2">
<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"> {issue.suggestion}</p>}
</div>
))}
</div>
)}
{/* Doctor issues */}
{diag.doctor.topIssues.length > 0 && (
<div className="space-y-2">
<div className="text-xs font-medium text-muted-foreground">Doctor issues</div>
{diag.doctor.topIssues.map((issue) => (
<div key={`${issue.code}:${issue.unitId ?? issue.message}`} className="rounded-lg border border-border/50 bg-card/50 px-3 py-2">
<Badge variant="outline" className="text-[10px]">{issue.code}</Badge>
<p className="mt-1 text-xs text-muted-foreground">{issue.message}</p>
</div>
))}
</div>
)}
{/* Interrupted run */}
{diag.interruptedRun.detected && (
<div className="rounded-lg border border-warning/20 bg-warning/5 px-3 py-2.5" data-testid="command-surface-recovery-interrupted-run">
<div className="text-xs font-medium text-warning">Interrupted run detected</div>
<div className="mt-1 space-y-1 text-xs text-warning/80">
<p>Available: yes</p>
<p>Detected: yes</p>
<p>{diag.interruptedRun.detail}</p>
</div>
<div className="mt-1.5 grid gap-1 text-[10px] text-warning/60">
<span>Tool calls: {diag.interruptedRun.counts.toolCalls}</span>
<span>Files written: {diag.interruptedRun.counts.filesWritten}</span>
<span>Commands: {diag.interruptedRun.counts.commandsRun}</span>
<span>Errors: {diag.interruptedRun.counts.errors}</span>
<span>Last forensic error: {diag.interruptedRun.lastError ?? "[redacted]"}</span>
</div>
</div>
)}
{/* 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
key={action.id}
type="button"
variant={action.emphasis === "danger" ? "destructive" : action.emphasis === "primary" ? "default" : "outline"}
size="sm"
onClick={() => triggerRecoveryBrowserAction(action.id)}
data-testid={`command-surface-recovery-action-${action.id}`}
className="h-7 text-xs"
>
{action.label}
</Button>
))
) : (
<span className="text-xs text-muted-foreground">
{recoveryBusy ? "Loading recovery actions…" : "No browser recovery actions available."}
</span>
)}
</div>
{diag.actions.commands.length > 0 && (
<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">
<div className="font-mono text-foreground">{command.command}</div>
<p className="mt-1 text-muted-foreground">{command.label}</p>
</div>
))}
</div>
)}
</>
)}
</div>
)
}
const gitFileStatusColor = (status: string) => {
switch (status) {
case "M": return "text-warning bg-warning/10"
case "A": return "text-success bg-success/10"
case "D": return "text-destructive bg-destructive/10"
case "R": return "text-info bg-info/10"
case "C": return "text-info bg-info/10"
case "U": return "text-destructive bg-destructive/10"
case "?": return "text-muted-foreground bg-foreground/5"
default: return "text-muted-foreground bg-foreground/5"
}
}
const renderGitSection = () => {
const result = gitSummary.result
return (
<div className="space-y-5" data-testid="command-surface-git-summary">
<div className="text-xs text-muted-foreground" data-testid="command-surface-git-state">
{gitSummaryBusy
? "Loading git summary…"
: gitSummary.error
? "Git summary failed"
: result?.kind === "not_repo"
? "No git repository"
: result?.kind === "repo"
? `Repo ready${result.hasChanges ? " — changes detected" : " — clean"}`
: "Git summary idle"}
</div>
{gitSummaryBusy && !result && (
<div className="flex flex-col items-center justify-center gap-3 py-16">
<LoaderCircle className="h-5 w-5 animate-spin text-muted-foreground" />
<span className="text-xs text-muted-foreground">Loading repo state</span>
</div>
)}
{gitSummary.error && (
<div
className="rounded-lg border border-destructive/20 bg-destructive/5 px-4 py-3 text-xs text-destructive"
data-testid="command-surface-git-error"
>
{gitSummary.error}
</div>
)}
{!gitSummary.error && result?.kind === "not_repo" && (
<div className="flex flex-col items-center gap-3 py-16 text-center" data-testid="command-surface-git-not-repo">
<div className="flex h-10 w-10 items-center justify-center rounded-full border border-border/50 bg-card/50">
<GitBranch className="h-4.5 w-4.5 text-muted-foreground" />
</div>
<div>
<div className="text-sm font-medium text-foreground">No Git repository</div>
<p className="mt-1 text-xs text-muted-foreground">{result.message}</p>
</div>
</div>
)}
{!gitSummary.error && result?.kind === "repo" && (
<>
{/* Repo info bar */}
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<span className="font-mono">{shortenPath(result.project.repoRoot, 3)}</span>
{result.project.repoRelativePath && (
<>
<ChevronRight className="h-3 w-3 text-muted-foreground" />
<span className="font-mono">{result.project.repoRelativePath}</span>
</>
)}
</div>
{/* Counts row */}
<div className="grid grid-cols-4 gap-1.5" data-testid="command-surface-git-counts">
{[
{ label: "Staged", count: result.counts.staged, active: result.counts.staged > 0, color: "text-success" },
{ label: "Modified", count: result.counts.dirty, active: result.counts.dirty > 0, color: "text-warning" },
{ label: "Untracked", count: result.counts.untracked, active: result.counts.untracked > 0, color: "text-muted-foreground" },
{ label: "Conflicts", count: result.counts.conflicts, active: result.counts.conflicts > 0, color: "text-destructive" },
].map(({ label, count, active, color }) => (
<div key={label} className={cn(
"rounded-md border px-2 py-2 text-center transition-colors",
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-muted-foreground",
)}>{count}</div>
<div className={cn(
"mt-1.5 text-[10px] leading-none",
active ? "text-muted-foreground" : "text-muted-foreground",
)}>{label}</div>
</div>
))}
</div>
{/* Changed files */}
{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">
Changes
</span>
<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/50 bg-card/50 overflow-hidden">
{result.changedFiles.map((file) => (
<div
key={`${file.status}:${file.repoPath}`}
className="group flex items-center gap-2.5 px-3 py-2 transition-colors hover:bg-foreground/[0.03]"
>
<span className={cn(
"flex h-5 w-5 shrink-0 items-center justify-center rounded text-[10px] font-semibold",
gitFileStatusColor(file.status),
)}>
{file.status}
</span>
<span className="min-w-0 flex-1 truncate font-mono text-[11px] text-foreground/80">
{file.path}
</span>
{file.conflict && (
<span className="shrink-0 rounded bg-destructive/15 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wider text-destructive">
conflict
</span>
)}
</div>
))}
</div>
{result.truncatedFileCount > 0 && (
<p className="mt-1.5 text-center text-[11px] text-muted-foreground">
+{result.truncatedFileCount} more files not shown
</p>
)}
</div>
)}
{result.changedFiles.length === 0 && (
<div className="flex flex-col items-center gap-2 py-8 text-center">
<Check className="h-4 w-4 text-success/60" />
<span className="text-xs text-muted-foreground">Working tree clean</span>
</div>
)}
</>
)}
</div>
)
}
const renderSessionBrowserSection = (mode: "resume" | "name") => {
const renameMode = mode === "name"
const selectedSessionPath = renameMode ? selectedNameTarget?.sessionPath : selectedResumeTarget?.sessionPath
return (
<div className="space-y-4" data-testid={renameMode ? "command-surface-name" : "command-surface-resume"}>
<SectionHeader
title={renameMode ? "Rename" : "Resume"}
status={
!renameMode ? (
<span className="text-xs text-muted-foreground">{currentSessionLabel ?? "pending"}</span>
) : null
}
/>
{/* Search bar */}
<div className="flex gap-2">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" />
<Input
value={sessionBrowser.query}
onChange={(e) => updateSessionBrowserState({ query: e.target.value })}
onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); void loadSessionBrowser() } }}
placeholder="Search sessions…"
className="h-8 pl-9 text-xs"
disabled={sessionBrowserBusy}
data-testid="command-surface-session-browser-query"
/>
</div>
<Button type="button" variant="ghost" size="sm" onClick={() => void loadSessionBrowser()} disabled={sessionBrowserBusy} className="h-8 w-8 p-0">
<RefreshCw className={cn("h-3.5 w-3.5", sessionBrowserBusy && "animate-spin")} />
</Button>
</div>
{/* Sort/filter controls */}
<div className="flex items-center gap-2">
<SegmentedControl
options={[
{ value: "threaded" as const, label: "Threaded" },
{ value: "recent" as const, label: "Recent" },
{ value: "relevance" as const, label: "Relevance" },
]}
value={sessionBrowser.sortMode}
onChange={(v) => { updateSessionBrowserState({ sortMode: v }); void loadSessionBrowser({ sortMode: v }) }}
disabled={sessionBrowserBusy}
/>
<button
type="button"
className={cn(
"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={() => {
const next = sessionBrowser.nameFilter === "named" ? "all" : "named"
updateSessionBrowserState({ nameFilter: next })
void loadSessionBrowser({ nameFilter: next })
}}
disabled={sessionBrowserBusy}
>
Named
</button>
</div>
{sessionBrowser.error && (
<div className="rounded-lg border border-destructive/20 bg-destructive/5 px-3 py-2.5 text-xs text-destructive">{sessionBrowser.error}</div>
)}
{/* Session list */}
{sessionBrowserBusy && sessionBrowser.sessions.length === 0 ? (
<div className="flex items-center gap-2 py-6 text-xs text-muted-foreground">
<LoaderCircle className="h-3.5 w-3.5 animate-spin" />
Loading sessions
</div>
) : sessionBrowser.sessions.length > 0 ? (
<div className="space-y-1" data-testid="command-surface-session-browser-results">
{sessionBrowser.sessions.map((session) => {
const selected = session.path === selectedSessionPath
return (
<button
key={session.path}
type="button"
className={cn(
"flex w-full items-start gap-3 rounded-lg px-3 py-2.5 text-left transition-colors",
selected ? "bg-foreground/[0.07]" : "hover:bg-foreground/[0.03]",
)}
style={{ paddingLeft: `${0.75 + session.depth * 0.6}rem` }}
onClick={() =>
renameMode
? selectCommandSurfaceTarget({ kind: "name", sessionPath: session.path, name: selectedNameTarget?.sessionPath === session.path ? (selectedNameTarget?.name ?? session.name ?? "") : (session.name ?? "") })
: selectCommandSurfaceTarget({ kind: "resume", sessionPath: session.path })
}
data-testid={`command-surface-session-browser-item-${session.id}`}
>
<div className={cn(
"mt-1 flex h-4 w-4 shrink-0 items-center justify-center rounded-full border transition-colors",
selected ? "border-foreground bg-foreground" : "border-foreground/25",
)}>
{selected && <Check className="h-2.5 w-2.5 text-background" />}
</div>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="truncate text-sm font-medium text-foreground">
{session.name || session.firstMessage || session.id}
</span>
{session.isActive && <StatusDot status="ok" />}
</div>
{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">
<span>{session.messageCount} msgs</span>
<span>{formatRelativeTime(session.modifiedAt)}</span>
</div>
</div>
</button>
)
})}
</div>
) : (
<p className="py-4 text-center text-xs text-muted-foreground">No sessions matched.</p>
)}
{sessionBrowser.loaded && (
<p className="text-[11px] text-muted-foreground" data-testid="command-surface-session-browser-meta">
Current-project sessions · {sessionBrowser.returnedSessions} of {sessionBrowser.totalSessions} · {sessionBrowser.sortMode} · {sessionBrowser.nameFilter}
</p>
)}
{/* Rename controls */}
{renameMode && (
<div className="space-y-3 border-t border-border/50 pt-3">
<div className="flex gap-2">
<Input
value={selectedNameTarget?.name ?? ""}
onChange={(e) =>
selectCommandSurfaceTarget({ kind: "name", sessionPath: selectedNameTarget?.sessionPath, name: e.target.value })
}
placeholder="Session name"
className="h-8 flex-1 text-xs"
disabled={!selectedNameTarget?.sessionPath || renameBusy}
data-testid="command-surface-rename-input"
/>
<Button
type="button"
size="sm"
onClick={() => selectedNameTarget?.sessionPath && void renameSessionFromSurface(selectedNameTarget.sessionPath, selectedNameTarget.name)}
disabled={!selectedNameTarget?.sessionPath || !selectedNameTarget.name.trim() || renameBusy}
data-testid="command-surface-apply-rename"
className="h-8 gap-1.5"
>
{renameBusy ? <LoaderCircle className="h-3.5 w-3.5 animate-spin" /> : <PencilLine className="h-3.5 w-3.5" />}
Rename
</Button>
</div>
{commandSurface.renameRequest.error && <p className="text-xs text-destructive">{commandSurface.renameRequest.error}</p>}
{commandSurface.renameRequest.result && <p className="text-xs text-success">{commandSurface.renameRequest.result}</p>}
</div>
)}
{/* Resume controls */}
{!renameMode && (
<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>
<Button
type="button"
size="sm"
onClick={() => selectedResumeTarget?.sessionPath && void switchSessionFromSurface(selectedResumeTarget.sessionPath)}
disabled={!selectedResumeTarget?.sessionPath || resumeBusy}
data-testid="command-surface-apply-resume"
className="h-8 gap-1.5"
>
{resumeBusy ? <LoaderCircle className="h-3.5 w-3.5 animate-spin" /> : <ArrowRightLeft className="h-3.5 w-3.5" />}
Switch
</Button>
</div>
)}
</div>
)
}
const renderForkSection = () => (
<div className="space-y-4" data-testid="command-surface-fork">
<SectionHeader
title="Fork"
action={
<Button type="button" variant="ghost" size="sm" onClick={() => void loadForkMessages()} disabled={forkBusy} className="h-7 gap-1.5 text-xs">
<RefreshCw className={cn("h-3 w-3", commandSurface.pendingAction === "load_fork_messages" && "animate-spin")} />
Refresh
</Button>
}
/>
{forkBusy && commandSurface.forkMessages.length === 0 ? (
<div className="flex items-center gap-2 py-6 text-xs text-muted-foreground">
<LoaderCircle className="h-3.5 w-3.5 animate-spin" />
Loading fork points
</div>
) : commandSurface.forkMessages.length > 0 ? (
<div className="space-y-1">
{commandSurface.forkMessages.map((message) => {
const selected = selectedForkTarget?.entryId === message.entryId
return (
<button
key={message.entryId}
type="button"
className={cn(
"flex w-full items-start gap-3 rounded-lg px-3 py-2.5 text-left transition-colors",
selected ? "bg-foreground/[0.07]" : "hover:bg-foreground/[0.03]",
)}
onClick={() => selectCommandSurfaceTarget({ kind: "fork", entryId: message.entryId })}
>
<div className={cn(
"mt-1 flex h-4 w-4 shrink-0 items-center justify-center rounded-full border transition-colors",
selected ? "border-foreground bg-foreground" : "border-foreground/25",
)}>
{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">{message.entryId}</div>
<p className="mt-0.5 text-sm text-foreground">{message.text}</p>
</div>
</button>
)
})}
</div>
) : (
<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/50 pt-3">
<Button
type="button"
size="sm"
onClick={() => selectedForkTarget?.entryId && void forkSessionFromSurface(selectedForkTarget.entryId)}
disabled={!selectedForkTarget?.entryId || commandSurface.pendingAction === "fork_session"}
data-testid="command-surface-apply-fork"
className="h-8 gap-1.5"
>
{commandSurface.pendingAction === "fork_session" ? (
<LoaderCircle className="h-3.5 w-3.5 animate-spin" />
) : (
<GitBranch className="h-3.5 w-3.5" />
)}
Create fork
</Button>
</div>
</div>
)
const renderSessionSection = () => (
<div className="space-y-4" data-testid="command-surface-session">
<SectionHeader
title="Session"
status={
<span className="text-xs text-muted-foreground">{currentSessionLabel ?? "pending"}</span>
}
action={
<Button type="button" variant="ghost" size="sm" onClick={() => void loadSessionStats()} disabled={sessionBusy} className="h-7 gap-1.5 text-xs">
<RefreshCw className={cn("h-3 w-3", commandSurface.pendingAction === "load_session_stats" && "animate-spin")} />
Refresh
</Button>
}
/>
{commandSurface.sessionStats ? (
<>
{/* Token & cost grid */}
<div className="grid grid-cols-3 gap-2">
{[
{ label: "Input", value: formatTokens(commandSurface.sessionStats.tokens.input) },
{ label: "Output", value: formatTokens(commandSurface.sessionStats.tokens.output) },
{ label: "Total", value: formatTokens(commandSurface.sessionStats.tokens.total) },
].map(({ label, value }) => (
<div key={label} className="rounded-lg border border-border/50 bg-card/50 px-3 py-2.5 text-center">
<div className="text-[10px] uppercase tracking-wider text-muted-foreground">{label}</div>
<div className="mt-1 text-sm font-semibold tabular-nums text-foreground">{value}</div>
</div>
))}
</div>
{/* Message breakdown */}
<div className="divide-y divide-border/30 rounded-lg border border-border/50 bg-card/50">
<div className="px-4 py-2">
<KV label="User messages">{commandSurface.sessionStats.userMessages}</KV>
<KV label="Assistant messages">{commandSurface.sessionStats.assistantMessages}</KV>
<KV label="Tool calls">{commandSurface.sessionStats.toolCalls}</KV>
<KV label="Tool results">{commandSurface.sessionStats.toolResults}</KV>
</div>
<div className="px-4 py-2">
<KV label="Total messages">{commandSurface.sessionStats.totalMessages}</KV>
<KV label="Cost">{formatCost(commandSurface.sessionStats.cost)}</KV>
{commandSurface.sessionStats.tokens.cacheRead > 0 && (
<KV label="Cache read">{formatTokens(commandSurface.sessionStats.tokens.cacheRead)}</KV>
)}
</div>
</div>
</>
) : (
<p className="py-4 text-center text-xs text-muted-foreground">Refresh to load session stats.</p>
)}
{/* Export */}
<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
value={selectedSessionTarget?.outputPath ?? ""}
onChange={(e) => selectCommandSurfaceTarget({ kind: "session", outputPath: e.target.value })}
placeholder="Output path (optional)"
className="h-8 flex-1 text-xs"
disabled={commandSurface.pendingAction === "export_html"}
data-testid="command-surface-export-path"
/>
<Button
type="button"
size="sm"
onClick={() => void exportSessionFromSurface(selectedSessionTarget?.outputPath)}
disabled={commandSurface.pendingAction === "export_html"}
data-testid="command-surface-export-session"
className="h-8 gap-1.5"
>
{commandSurface.pendingAction === "export_html" ? (
<LoaderCircle className="h-3.5 w-3.5 animate-spin" />
) : (
<Download className="h-3.5 w-3.5" />
)}
Export HTML
</Button>
</div>
</div>
</div>
)
const renderCompactSection = () => (
<div className="space-y-4" data-testid="command-surface-compact">
<SectionHeader
title="Manual compact"
status={
compactBusy ? (
<span className="flex items-center gap-1.5 text-xs text-warning">
<LoaderCircle className="h-3 w-3 animate-spin" /> Working
</span>
) : null
}
/>
<div className="space-y-2">
<label className="text-xs font-medium text-muted-foreground" htmlFor="command-surface-compact-instructions">
Custom instructions
</label>
<Textarea
id="command-surface-compact-instructions"
data-testid="command-surface-compact-instructions"
value={selectedCompactTarget?.customInstructions ?? ""}
onChange={(e) => selectCommandSurfaceTarget({ kind: "compact", customInstructions: e.target.value })}
placeholder="Tell compaction what to preserve or emphasize…"
rows={4}
disabled={compactBusy}
className="text-xs"
/>
</div>
<div className="flex justify-end">
<Button
type="button"
size="sm"
onClick={() => void compactSessionFromSurface(selectedCompactTarget?.customInstructions)}
disabled={compactBusy}
data-testid="command-surface-apply-compact"
className="h-8 gap-1.5"
>
{compactBusy ? <LoaderCircle className="h-3.5 w-3.5 animate-spin" /> : <Archive className="h-3.5 w-3.5" />}
Compact now
</Button>
</div>
{commandSurface.lastCompaction && (
<div className="space-y-2 rounded-lg border border-border/50 bg-card/50 px-4 py-3">
<div className="flex items-center justify-between">
<span className="text-xs font-medium text-muted-foreground">Last compaction</span>
<span className="text-[11px] tabular-nums text-muted-foreground">{formatTokens(commandSurface.lastCompaction.tokensBefore)} before</span>
</div>
<p className="whitespace-pre-wrap text-xs text-foreground">{commandSurface.lastCompaction.summary}</p>
<p className="text-[11px] text-muted-foreground">First kept: {commandSurface.lastCompaction.firstKeptEntryId}</p>
</div>
)}
</div>
)
const renderAuthSection = () => {
if (!onboarding) return null
return (
<div className="space-y-4" data-testid="command-surface-auth">
<SectionHeader
title="Auth"
status={
<span className="text-xs text-muted-foreground">
{selectedAuthIntent === "login" ? "Login" : selectedAuthIntent === "logout" ? "Logout" : "Manage"}
</span>
}
/>
{/* Provider list */}
<div className="space-y-1">
{onboarding.required.providers.map((provider) => {
const selected = provider.id === selectedAuthProvider?.id
return (
<button
key={provider.id}
type="button"
className={cn(
"flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left transition-colors",
selected ? "bg-foreground/[0.07]" : "hover:bg-foreground/[0.03]",
)}
onClick={() =>
selectCommandSurfaceTarget({ kind: "auth", providerId: provider.id, intent: selectedAuthIntent })
}
>
<div className={cn(
"flex h-4 w-4 shrink-0 items-center justify-center rounded-full border transition-colors",
selected ? "border-foreground bg-foreground" : "border-foreground/25",
)}>
{selected && <Check className="h-2.5 w-2.5 text-background" />}
</div>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-foreground">{provider.label}</span>
{provider.configured && <StatusDot status="ok" />}
</div>
<span className="text-xs text-muted-foreground">
{provider.configured ? `via ${provider.configuredVia}` : "Not configured"}
</span>
</div>
{provider.recommended && (
<span className="rounded bg-foreground/10 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">Recommended</span>
)}
</button>
)
})}
</div>
{/* Selected provider details */}
{selectedAuthProvider && (
<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>
<span className="text-xs text-muted-foreground">{selectedAuthProvider.configuredVia ?? "Not configured"}</span>
</div>
</div>
{/* API key form */}
{selectedAuthProvider.supports.apiKey && (
<form
className="space-y-3"
onSubmit={(e) => {
e.preventDefault()
if (!selectedProviderApiKey.trim()) return
void saveApiKeyFromSurface(selectedAuthProvider.id, selectedProviderApiKey)
}}
>
<div className="flex gap-2">
<Input
type="password"
autoComplete="off"
value={selectedProviderApiKey}
onChange={(e) =>
setApiKeys((prev) => ({ ...prev, [selectedAuthProvider.id]: e.target.value }))
}
placeholder="Paste API key"
className="h-8 flex-1 text-xs"
disabled={authBusy}
data-testid="command-surface-api-key-input"
/>
<Button
type="submit"
size="sm"
disabled={!selectedProviderApiKey.trim() || authBusy}
data-testid="command-surface-save-api-key"
className="h-8 gap-1.5"
>
{commandSurface.pendingAction === "save_api_key" ? (
<LoaderCircle className="h-3.5 w-3.5 animate-spin" />
) : (
<KeyRound className="h-3.5 w-3.5" />
)}
Save
</Button>
</div>
</form>
)}
{/* OAuth / sign-in buttons */}
<div className="flex flex-wrap gap-2">
{selectedAuthProvider.supports.oauth && selectedAuthProvider.supports.oauthAvailable && (
<Button
type="button"
variant="outline"
size="sm"
disabled={authBusy}
onClick={() => void startProviderFlowFromSurface(selectedAuthProvider.id)}
data-testid="command-surface-start-provider-flow"
className="h-8 gap-1.5 text-xs"
>
{commandSurface.pendingAction === "start_provider_flow" ? (
<LoaderCircle className="h-3.5 w-3.5 animate-spin" />
) : (
<LogIn className="h-3.5 w-3.5" />
)}
Browser sign-in
</Button>
)}
<Button
type="button"
variant="ghost"
size="sm"
disabled={authBusy}
onClick={() => void logoutProviderFromSurface(selectedAuthProvider.id)}
data-testid="command-surface-logout-provider"
className="h-8 gap-1.5 text-xs text-destructive hover:text-destructive"
>
{commandSurface.pendingAction === "logout_provider" ? (
<LoaderCircle className="h-3.5 w-3.5 animate-spin" />
) : (
<LogOut className="h-3.5 w-3.5" />
)}
Logout
</Button>
</div>
{/* Active OAuth flow */}
{activeFlow && activeFlow.providerId === selectedAuthProvider.id && (
<div className="space-y-3 rounded-lg border border-foreground/10 bg-foreground/[0.03] px-4 py-3" data-testid="command-surface-active-flow">
<div className="flex items-center gap-2 text-xs">
<Badge variant="outline" className="text-[10px]">{activeFlow.status.replaceAll("_", " ")}</Badge>
<span className="text-muted-foreground">{new Date(activeFlow.updatedAt).toLocaleTimeString()}</span>
</div>
{activeFlow.auth?.instructions && (
<p className="text-xs text-muted-foreground">{activeFlow.auth.instructions}</p>
)}
{activeFlow.auth?.url && (
<Button asChild variant="outline" size="sm" className="h-7 gap-1.5 text-xs" data-testid="command-surface-open-auth-url">
<a href={activeFlow.auth.url} target="_blank" rel="noreferrer">
<ExternalLink className="h-3 w-3" />
Open sign-in page
</a>
</Button>
)}
{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/50 bg-card/50 px-2.5 py-1.5 text-xs text-muted-foreground">
{message}
</div>
))}
</div>
)}
{activeFlow.prompt && (
<form
className="space-y-2"
onSubmit={(e) => {
e.preventDefault()
if (!activeFlow.prompt?.allowEmpty && !flowInput.trim()) return
void submitProviderFlowInputFromSurface(activeFlow.flowId, flowInput)
}}
>
<Input
value={flowInput}
onChange={(e) => setFlowInput(e.target.value)}
placeholder={activeFlow.prompt.placeholder || "Enter value"}
className="h-8 text-xs"
disabled={authBusy}
data-testid="command-surface-flow-input"
/>
<p className="text-[11px] text-muted-foreground">{activeFlow.prompt.message}</p>
<div className="flex gap-2">
<Button type="submit" size="sm" disabled={authBusy || (!activeFlow.prompt.allowEmpty && !flowInput.trim())} className="h-7 gap-1.5 text-xs">
{commandSurface.pendingAction === "submit_provider_flow_input" ? (
<LoaderCircle className="h-3 w-3 animate-spin" />
) : (
<ShieldCheck className="h-3 w-3" />
)}
Continue
</Button>
<Button type="button" variant="ghost" size="sm" disabled={authBusy} onClick={() => void cancelProviderFlowFromSurface(activeFlow.flowId)} className="h-7 text-xs">
Cancel
</Button>
</div>
</form>
)}
</div>
)}
{/* Bridge auth refresh status */}
{onboarding.bridgeAuthRefresh.phase !== "idle" && (
<div className="rounded-lg border border-border/50 bg-card/50 px-3 py-2.5 text-xs">
<span className="font-medium text-foreground">Auth refresh</span>
<span className="ml-2 text-muted-foreground">
{onboarding.bridgeAuthRefresh.phase === "pending"
? "Refreshing…"
: onboarding.bridgeAuthRefresh.phase === "failed"
? onboarding.bridgeAuthRefresh.error || "Failed."
: "Complete."}
</span>
</div>
)}
</div>
)}
</div>
)
}
// ═══════════════════════════════════════════════════════════════════
// SECTION DISPATCH
// ═══════════════════════════════════════════════════════════════════
const renderAdminSection = () => (
<div className="space-y-5" data-testid="command-surface-admin">
<SectionHeader
title="Admin"
status={
<Badge variant="outline" className="border-warning/20 bg-warning/[0.06] text-[10px] text-warning">
Dev only
</Badge>
}
/>
{/* Master toggle */}
<ToggleRow
label="UI overrides"
description="Enable keyboard shortcuts and forced UI states for development"
checked={devOverrides.enabled}
onCheckedChange={devOverrides.setEnabled}
testId="admin-ui-overrides-master"
/>
{/* Individual overrides — only visible when master is on */}
{devOverrides.enabled && (
<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>
{DEV_OVERRIDE_REGISTRY.map((entry) => (
<div
key={entry.key}
className="flex items-start justify-between gap-3 rounded-md px-3 py-2.5 transition-colors hover:bg-foreground/[0.03]"
>
<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 font-mono text-[10px] text-muted-foreground">
{entry.shortcutLabel}
</Badge>
</div>
<p className="mt-0.5 text-xs text-muted-foreground">{entry.description}</p>
</div>
<Switch
checked={devOverrides.overrides[entry.key]}
onCheckedChange={() => devOverrides.toggle(entry.key)}
data-testid={`admin-override-${entry.key}`}
/>
</div>
))}
</div>
)}
{/* Onboarding — one-click launch */}
<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>
<div className="flex items-center justify-between gap-3 px-3 py-2.5">
<div className="min-w-0 flex-1">
<div className="text-sm font-medium text-foreground">Run setup wizard</div>
<p className="mt-0.5 text-xs text-muted-foreground">
Opens the full onboarding flow as a new user would see it.
</p>
</div>
<Button
type="button"
size="sm"
className="h-8 shrink-0 gap-1.5 text-xs"
onClick={() => {
closeCommandSurface()
// Small delay so the sheet closes before the gate renders
window.setTimeout(() => {
if (!devOverrides.enabled) devOverrides.setEnabled(true)
if (!devOverrides.overrides.forceOnboarding) devOverrides.toggle("forceOnboarding")
}, 150)
}}
data-testid="admin-trigger-onboarding"
>
Launch
</Button>
</div>
</div>
<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.
</div>
</div>
)
const renderSection = () => {
switch (commandSurface.section) {
case "general": return <GeneralPanel />
case "experimental": return <ExperimentalPanel />
case "model": return (
<div className="space-y-8">
{renderModelSection()}
<div className="border-t border-border/50 pt-6">
{renderThinkingSection()}
</div>
</div>
)
case "thinking": return (
<div className="space-y-8">
{renderModelSection()}
<div className="border-t border-border/50 pt-6">
{renderThinkingSection()}
</div>
</div>
)
case "session-behavior": return (
<div className="space-y-6">
{renderQueueSection()}
<div className="border-t border-border/50 pt-4">
{renderCompactionSection()}
</div>
<div className="border-t border-border/50 pt-4">
{renderRetrySection()}
</div>
</div>
)
// Legacy section routes — redirect to merged panels
case "queue": return (
<div className="space-y-6">
{renderQueueSection()}
<div className="border-t border-border/50 pt-4">
{renderCompactionSection()}
</div>
<div className="border-t border-border/50 pt-4">
{renderRetrySection()}
</div>
</div>
)
case "compaction": return (
<div className="space-y-6">
{renderQueueSection()}
<div className="border-t border-border/50 pt-4">
{renderCompactionSection()}
</div>
<div className="border-t border-border/50 pt-4">
{renderRetrySection()}
</div>
</div>
)
case "retry": return (
<div className="space-y-6">
{renderQueueSection()}
<div className="border-t border-border/50 pt-4">
{renderCompactionSection()}
</div>
<div className="border-t border-border/50 pt-4">
{renderRetrySection()}
</div>
</div>
)
case "recovery": return renderRecoverySection()
case "auth": return renderAuthSection()
case "admin": return renderAdminSection()
case "git": return renderGitSection()
case "resume": return renderSessionBrowserSection("resume")
case "name": return renderSessionBrowserSection("name")
case "fork": return renderForkSection()
case "session": return renderSessionSection()
case "compact": return renderCompactSection()
case "workspace": return <DevRootSettingsSection />
case "integrations": return <RemoteQuestionsPanel />
case "gsd-forensics": return <ForensicsPanel />
case "gsd-doctor": return <DoctorPanel />
case "gsd-skill-health": return <SkillHealthPanel />
case "gsd-knowledge": return <KnowledgeCapturesPanel initialTab="knowledge" />
case "gsd-capture": return <KnowledgeCapturesPanel initialTab="captures" />
case "gsd-triage": return <KnowledgeCapturesPanel initialTab="captures" />
case "gsd-prefs": return (
<div className="space-y-6">
<DevRootSettingsSection />
<PrefsPanel />
<ModelRoutingPanel />
<BudgetPanel />
<RemoteQuestionsPanel />
<GeneralPanel />
<ExperimentalPanel />
</div>
)
case "gsd-mode": return <ModelRoutingPanel />
case "gsd-config": return <BudgetPanel />
case "gsd-quick": return <QuickPanel />
case "gsd-history": return <HistoryPanel />
case "gsd-undo": return <UndoPanel />
case "gsd-steer": return <SteerPanel />
case "gsd-hooks": return <HooksPanel />
case "gsd-inspect": return <InspectPanel />
case "gsd-export": return <ExportPanel />
case "gsd-cleanup": return <CleanupPanel />
case "gsd-queue": return <QueuePanel />
case "gsd-status": return <StatusPanel />
default:
// Safety net for any unknown GSD surface
if (commandSurface.section?.startsWith("gsd-")) {
return (
<div className="p-4 text-sm text-muted-foreground" data-testid={`gsd-surface-${commandSurface.section}`}>
<p className="font-medium text-foreground">/gsd {commandSurface.section.slice(4)}</p>
<p className="mt-1">Unknown GSD surface.</p>
</div>
)
}
return null
}
}
// ═══════════════════════════════════════════════════════════════════
// RENDER
// ═══════════════════════════════════════════════════════════════════
const isSingleSection = surfaceSections.length <= 1
const isGitSurface = commandSurface.activeSurface === "git"
const gitResult = gitSummary.result
const renderGitHeader = () => {
const branchName = gitResult?.kind === "repo" ? (gitResult.branch ?? "detached") : null
const mainBranch = gitResult?.kind === "repo" ? gitResult.mainBranch : null
const hasChanges = gitResult?.kind === "repo" ? gitResult.hasChanges : false
const isClean = gitResult?.kind === "repo" && !hasChanges
return (
<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(
"flex h-8 w-8 items-center justify-center rounded-lg",
isClean ? "bg-success/10" : hasChanges ? "bg-warning/10" : "bg-card/50",
)}>
<GitBranch className={cn(
"h-4 w-4",
isClean ? "text-success" : hasChanges ? "text-warning" : "text-muted-foreground",
)} />
</div>
<div>
<div className="flex items-center gap-2">
<h2 className="text-sm font-semibold text-foreground" data-testid="command-surface-title">
{branchName ?? "Git"}
</h2>
{branchName && mainBranch && branchName !== mainBranch && (
<span className="text-[11px] text-muted-foreground">from {mainBranch}</span>
)}
</div>
{gitResult?.kind === "repo" && (
<div className="mt-0.5 flex items-center gap-1.5">
<StatusDot status={isClean ? "ok" : hasChanges ? "warning" : "idle"} />
<span className="text-[11px] text-muted-foreground">
{isClean ? "Clean" : hasChanges ? "Changes detected" : "Loading…"}
</span>
</div>
)}
</div>
</div>
<div className="flex items-center gap-1">
<Button
type="button"
variant="ghost"
size="icon"
onClick={() => void loadGitSummary()}
disabled={gitSummaryBusy}
aria-label="Refresh"
className="h-7 w-7"
>
<RefreshCw className={cn("h-3.5 w-3.5", gitSummaryBusy && "animate-spin")} />
</Button>
<Button
type="button"
variant="ghost"
size="icon"
onClick={closeCommandSurface}
aria-label="Close"
className="h-7 w-7"
>
<X className="h-3.5 w-3.5" />
</Button>
</div>
</div>
</div>
)
}
const renderDefaultHeader = () => (
<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">
{surfaceTitle(commandSurface.activeSurface)}
</div>
</div>
<div className="flex items-center gap-2">
<div className="rounded-full border border-border bg-card px-2.5 py-1 text-xs font-medium text-muted-foreground" data-testid="command-surface-kind">
{surfaceKindLabel}
</div>
<Button
type="button"
variant="ghost"
size="icon"
onClick={closeCommandSurface}
aria-label="Close"
className="h-8 w-8"
>
<X className="h-4 w-4" />
</Button>
</div>
</div>
)
return (
<Sheet open={commandSurface.open} onOpenChange={(open) => !open && closeCommandSurface()}>
<SheetContent side="right" className="flex h-full w-full flex-col p-0 sm:max-w-[540px]" data-testid="command-surface">
{/* Visually hidden accessible title */}
<SheetHeader className="sr-only">
<SheetTitle>{surfaceTitle(commandSurface.activeSurface)}</SheetTitle>
<SheetDescription>Settings and controls</SheetDescription>
</SheetHeader>
<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/50 bg-card/50 py-3" data-testid="command-surface-sections">
{surfaceSections.map((section) => {
const active = commandSurface.section === section
return (
<Tooltip key={section}>
<TooltipTrigger asChild>
<button
type="button"
className={cn(
"flex h-9 w-9 items-center justify-center rounded-lg transition-colors",
active
? "bg-foreground/10 text-foreground"
: "text-muted-foreground hover:bg-foreground/[0.04] hover:text-foreground",
)}
onClick={() => setCommandSurfaceSection(section)}
data-testid={`command-surface-section-${section}`}
>
{sectionIcon(section)}
</button>
</TooltipTrigger>
<TooltipContent side="right" sideOffset={6}>
{sectionLabel(section)}
</TooltipContent>
</Tooltip>
)
})}
</nav>
)}
{/* ─── Right content area ────────────────────────────────── */}
<div className="flex min-h-0 min-w-0 flex-1 flex-col">
{isGitSurface ? renderGitHeader() : renderDefaultHeader()}
{(commandSurface.lastResult || commandSurface.lastError) && (
<div
className={cn(
"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"
>
{commandSurface.lastError ?? commandSurface.lastResult}
</div>
)}
<ScrollArea className="min-h-0 flex-1" viewportRef={commandSurfaceViewportRef}>
<div className="px-5 py-5">
{renderSection()}
</div>
</ScrollArea>
</div>
</div>
</SheetContent>
</Sheet>
)
}