@workerdeck/ui 0.23.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/{SessionPanel-DUt2VzXG.mjs → SessionPanel-DDgBkHsi.mjs} +2151 -3357
- package/build/SessionPanel-DDgBkHsi.mjs.map +1 -0
- package/build/SessionPanel-E_L_ldQ5.d.mts +207 -0
- package/build/format-B8ZM9LPy.d.mts +16 -0
- package/build/format.d.mts +8 -75
- package/build/format.mjs +2 -2
- package/build/index.d.mts +63 -1138
- package/build/index.mjs +4 -300
- package/build/index.mjs.map +1 -1
- package/build/scoped.css +15 -23
- package/build/{status-BE-zg88x.mjs → status-C0HSLZas.mjs} +14 -85
- package/build/status-C0HSLZas.mjs.map +1 -0
- package/build/workspace.d.mts +5 -126
- package/build/workspace.mjs +11 -143
- package/build/workspace.mjs.map +1 -1
- package/package.json +4 -4
- package/src/components/agent/CodeEditor.tsx +31 -103
- package/src/components/agent/Composer.tsx +114 -333
- package/src/components/agent/ContextDialog.tsx +20 -41
- package/src/components/agent/ContextRing.tsx +6 -28
- package/src/components/agent/Conversation.tsx +21 -33
- package/src/components/agent/EditorTabs.tsx +39 -67
- package/src/components/agent/EngineIcon.tsx +29 -72
- package/src/components/agent/EntryIcon.tsx +16 -0
- package/src/components/agent/FileCard.tsx +10 -16
- package/src/components/agent/FileTree.tsx +52 -125
- package/src/components/agent/FileViewer.tsx +30 -60
- package/src/components/agent/HostFilesDialog.tsx +40 -69
- package/src/components/agent/Loader.tsx +3 -24
- package/src/components/agent/McpDialog.tsx +64 -137
- package/src/components/agent/Message.tsx +2 -21
- package/src/components/agent/ModelSelect.tsx +33 -62
- package/src/components/agent/PermissionModeSelect.tsx +21 -79
- package/src/components/agent/PermissionPrompt.tsx +59 -63
- package/src/components/agent/ProjectIcon.tsx +228 -93
- package/src/components/agent/PromptTokenText.tsx +10 -16
- package/src/components/agent/QuestionPrompt.tsx +43 -64
- package/src/components/agent/Reasoning.tsx +13 -16
- package/src/components/agent/Response.tsx +3 -9
- package/src/components/agent/Scrubber.tsx +31 -80
- package/src/components/agent/SessionBrowser.tsx +64 -243
- package/src/components/agent/SessionEmptyState.tsx +10 -40
- package/src/components/agent/SessionInfoDialog.tsx +46 -78
- package/src/components/agent/SessionItem.tsx +53 -320
- package/src/components/agent/SessionList.tsx +23 -39
- package/src/components/agent/SessionPanel.tsx +315 -996
- package/src/components/agent/SessionStatusIcon.tsx +14 -28
- package/src/components/agent/SessionSteps.tsx +34 -187
- package/src/components/agent/SessionWorkspace.tsx +104 -219
- package/src/components/agent/SkillsDialog.tsx +35 -80
- package/src/components/agent/StatusBar.tsx +51 -136
- package/src/components/agent/SubagentStrip.tsx +12 -77
- package/src/components/agent/ToolCallCard.tsx +94 -135
- package/src/components/agent/Transcript.tsx +103 -1288
- package/src/components/agent/TranscriptItemView.tsx +155 -0
- package/src/components/agent/TranscriptRows.tsx +418 -0
- package/src/components/agent/UsageDialog.tsx +7 -29
- package/src/components/agent/UsageMeters.tsx +18 -73
- package/src/components/agent/pulse.tsx +9 -32
- package/src/components/agent/scrubber-marks.ts +33 -103
- package/src/components/agent/status.ts +1 -5
- package/src/components/agent/tool-result-fetch.tsx +3 -25
- package/src/components/agent/tool-result-image.tsx +32 -111
- package/src/components/agent/transcript-rows.ts +41 -118
- package/src/components/agent/transcript-variant.tsx +3 -80
- package/src/components/agent/use-height-epoch.ts +10 -24
- package/src/components/agent/use-path-links.ts +45 -63
- package/src/components/agent/use-subagent-frame.ts +106 -0
- package/src/components/agent/use-transcript-jumps.ts +49 -107
- package/src/components/prompt-area/animated-placeholder.tsx +6 -5
- package/src/components/prompt-area/clipboard-helpers.ts +26 -21
- package/src/components/prompt-area/cursor-helpers.ts +79 -37
- package/src/components/prompt-area/dom-helpers.ts +97 -69
- package/src/components/prompt-area/file-strip.tsx +90 -80
- package/src/components/prompt-area/html-to-markdown.ts +67 -46
- package/src/components/prompt-area/image-strip.tsx +8 -18
- package/src/components/prompt-area/index.ts +2 -15
- package/src/components/prompt-area/prompt-area-engine.ts +100 -115
- package/src/components/prompt-area/prompt-area-list-ops.ts +66 -59
- package/src/components/prompt-area/prompt-area.tsx +31 -38
- package/src/components/prompt-area/remove-button.tsx +3 -9
- package/src/components/prompt-area/segment-helpers.ts +4 -12
- package/src/components/prompt-area/trigger-popover.tsx +15 -27
- package/src/components/prompt-area/trigger-presets.ts +0 -10
- package/src/components/prompt-area/types.ts +3 -8
- package/src/components/prompt-area/use-chip-editing.ts +305 -0
- package/src/components/prompt-area/use-markdown-mode.ts +7 -17
- package/src/components/prompt-area/use-prompt-area-events.ts +80 -86
- package/src/components/prompt-area/use-prompt-area-keydown.ts +598 -0
- package/src/components/prompt-area/use-prompt-area-state.ts +5 -23
- package/src/components/prompt-area/use-prompt-area.ts +170 -896
- package/src/components/prompt-area/use-trigger-search.ts +21 -19
- package/src/components/terminal/PermissionPrompt.tsx +56 -59
- package/src/components/terminal/QuestionPrompt.tsx +28 -79
- package/src/components/terminal/StatusLine.tsx +13 -63
- package/src/components/terminal/TerminalTranscript.tsx +44 -160
- package/src/components/terminal/affordances.tsx +68 -98
- package/src/components/terminal/blocks.ts +20 -156
- package/src/components/terminal/diff.tsx +19 -67
- package/src/components/terminal/height.ts +163 -313
- package/src/components/terminal/image-box.ts +0 -44
- package/src/components/terminal/items.tsx +150 -305
- package/src/components/terminal/markdown.tsx +36 -95
- package/src/components/terminal/press.tsx +26 -53
- package/src/components/terminal/prompt.tsx +40 -170
- package/src/components/terminal/result-preview.ts +8 -56
- package/src/components/terminal/row.tsx +11 -98
- package/src/components/terminal/scrubber.tsx +108 -401
- package/src/components/terminal/surface.tsx +6 -54
- package/src/components/terminal/todos.ts +66 -0
- package/src/components/terminal/tool-run.ts +21 -192
- package/src/components/ui/AlertDialog.tsx +8 -23
- package/src/components/ui/Badge.tsx +18 -29
- package/src/components/ui/Button.tsx +7 -15
- package/src/components/ui/Card.tsx +2 -13
- package/src/components/ui/CodeBlock.tsx +10 -37
- package/src/components/ui/CopyButton.tsx +10 -23
- package/src/components/ui/Dialog.tsx +23 -36
- package/src/components/ui/Empty.tsx +6 -29
- package/src/components/ui/Input.tsx +14 -16
- package/src/components/ui/Menu.tsx +6 -18
- package/src/components/ui/PortalScope.tsx +1 -21
- package/src/components/ui/ProgressRing.tsx +8 -19
- package/src/components/ui/Select.tsx +18 -31
- package/src/components/ui/Sonner.tsx +1 -2
- package/src/components/ui/Spinner.tsx +1 -1
- package/src/components/ui/Splitter.tsx +30 -64
- package/src/components/ui/Textarea.tsx +2 -5
- package/src/components/ui/Tooltip.tsx +3 -13
- package/src/format.ts +0 -9
- package/src/index.ts +16 -113
- package/src/lib/clipboard.ts +8 -28
- package/src/lib/css.ts +8 -0
- package/src/lib/format.ts +101 -86
- package/src/lib/plan-request.ts +16 -0
- package/src/lib/status.ts +45 -95
- package/src/lib/tool-icon.ts +39 -47
- package/src/lib/utils.ts +1 -3
- package/src/styles/terminal.css +81 -49
- package/src/styles/theme.css +96 -32
- package/src/workspace.ts +1 -24
- package/build/SessionPanel-BobynUo4.d.mts +0 -669
- package/build/SessionPanel-DUt2VzXG.mjs.map +0 -1
- package/build/format-ljc3lKpA.d.mts +0 -59
- package/build/status-BE-zg88x.mjs.map +0 -1
package/build/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["AlertDialogPrimitive","SonnerToaster","Map"],"sources":["../src/components/ui/Card.tsx","../src/components/ui/Textarea.tsx","../src/components/ui/AlertDialog.tsx","../src/components/ui/Sonner.tsx","../src/components/ui/Empty.tsx","../src/components/prompt-area/segment-helpers.ts","../src/components/terminal/StatusLine.tsx","../src/components/agent/SessionList.tsx","../src/components/agent/ProjectIcon.tsx","../src/components/agent/ContextRing.tsx","../src/components/agent/EngineIcon.tsx","../src/components/agent/SessionStatusIcon.tsx","../src/components/agent/SessionSteps.tsx","../src/components/agent/SessionItem.tsx","../src/components/agent/SessionBrowser.tsx"],"sourcesContent":["import { type HTMLAttributes } from 'react'\nimport { cn } from '../../lib/utils.ts'\n\nexport function Card({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n data-slot='card'\n className={cn('rounded-lg border border-border bg-surface', className)}\n {...props}\n />\n )\n}\n\nexport function CardHeader({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={cn('flex items-start justify-between gap-3 px-4 pt-3.5 pb-3', className)}\n {...props}\n />\n )\n}\n\nexport function CardTitle({ className, ...props }: HTMLAttributes<HTMLHeadingElement>) {\n return <h3 className={cn('text-heading-3 font-semibold text-text', className)} {...props} />\n}\n\nexport function CardContent({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div className={cn('px-4 pb-4', className)} {...props} />\n}\n","import { type TextareaHTMLAttributes, forwardRef } from 'react'\nimport { cn } from '../../lib/utils.ts'\n\nexport const Textarea = forwardRef<\n HTMLTextAreaElement,\n TextareaHTMLAttributes<HTMLTextAreaElement>\n>(({ className, ...props }, ref) => (\n <textarea\n ref={ref}\n data-slot='textarea'\n className={cn(\n 'w-full resize-none rounded-md border border-border bg-bg px-2.5 py-2 text-body-sm text-text',\n 'placeholder:text-fg-4 transition-colors outline-none',\n 'hover:border-border-strong focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40',\n 'disabled:pointer-events-none disabled:opacity-50',\n className,\n )}\n {...props}\n />\n))\nTextarea.displayName = 'Textarea'\n","import { type FunctionComponent } from 'react'\nimport { AlertDialog as AlertDialogPrimitive } from '@base-ui/react/alert-dialog'\nimport { cn } from '../../lib/utils.ts'\nimport { PortalScope } from './PortalScope.tsx'\n\nexport const AlertDialog = AlertDialogPrimitive.Root\nexport const AlertDialogTrigger = AlertDialogPrimitive.Trigger\nexport const AlertDialogClose = AlertDialogPrimitive.Close\n\nexport const AlertDialogContent: FunctionComponent<AlertDialogPrimitive.Popup.Props> = ({\n className,\n children,\n ...props\n}) => (\n <AlertDialogPrimitive.Portal>\n <PortalScope>\n <AlertDialogPrimitive.Backdrop\n className={cn(\n 'fixed inset-0 z-70 bg-black/40 backdrop-blur-[1px]',\n 'transition-opacity duration-(--motion-base)',\n 'data-starting-style:opacity-0 data-ending-style:opacity-0',\n )}\n />\n <AlertDialogPrimitive.Popup\n data-slot='alert-dialog-content'\n className={cn(\n 'fixed top-1/2 left-1/2 z-70 w-[min(28rem,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2',\n 'rounded-lg border border-border bg-surface p-5 shadow-(--shadow-lg) outline-none',\n 'transition-[opacity,transform] duration-(--motion-base)',\n 'data-starting-style:scale-95 data-starting-style:opacity-0',\n 'data-ending-style:scale-95 data-ending-style:opacity-0',\n className,\n )}\n {...props}>\n {children}\n </AlertDialogPrimitive.Popup>\n </PortalScope>\n </AlertDialogPrimitive.Portal>\n)\n\nexport const AlertDialogTitle: FunctionComponent<AlertDialogPrimitive.Title.Props> = ({\n className,\n ...props\n}) => (\n <AlertDialogPrimitive.Title\n className={cn('text-heading-3 font-semibold text-text', className)}\n {...props}\n />\n)\n\nexport const AlertDialogDescription: FunctionComponent<AlertDialogPrimitive.Description.Props> = ({\n className,\n ...props\n}) => (\n <AlertDialogPrimitive.Description\n className={cn('mt-1.5 text-body-sm text-muted-foreground', className)}\n {...props}\n />\n)\n","import { Toaster as SonnerToaster, toast } from 'sonner'\n\nexport { toast }\n\n/** Token-themed toaster; relies on the [data-theme] swap, so no `theme` prop needed. */\nexport function Toaster() {\n return (\n <SonnerToaster\n position='bottom-right'\n toastOptions={{\n style: {\n background: 'var(--surface)',\n color: 'var(--text)',\n border: '1px solid var(--border)',\n boxShadow: 'var(--shadow-md)',\n fontFamily: 'var(--cw-font-sans)',\n fontSize: 'var(--text-body-sm)',\n },\n }}\n />\n )\n}\n","import type { ReactNode } from 'react'\nimport { Button } from './Button.tsx'\n\n/**\n * A view with nothing in it: icon, title, description, and at most one action.\n *\n * Centered and given room, because an empty view is the first thing a new\n * install shows and a flush-left sentence in the top corner reads like a bug\n * report. It lives here rather than in one client because every panel that can\n * be empty should be empty the same way — the extension's views and the\n * dashboard's four sidebars are the same shape of thing.\n *\n * **At most one action, and never one the view header already offers.** Creating\n * a session and adding a gateway are the `+` in the native title bar, exclusively\n * — so those empty states point at it in words rather than growing a second\n * button that does the same thing two inches lower. A button here is for the way\n * out of a state the header has no answer to: clearing a filter, widening a\n * scope.\n */\nexport function Empty({\n icon,\n title,\n description,\n action,\n onAction,\n}: {\n icon: ReactNode\n title: string\n description?: ReactNode\n action?: string\n onAction?: () => void\n}) {\n return (\n <div className='flex flex-col items-center gap-2 px-5 py-8 text-center'>\n <div className='text-fg-4 opacity-60 [&_svg]:size-7'>{icon}</div>\n <p className='text-body-sm font-medium text-fg-2'>{title}</p>\n {description ? (\n <p className='max-w-[26ch] text-balance text-label leading-relaxed text-fg-4'>\n {description}\n </p>\n ) : null}\n {action && onAction ? (\n <Button variant='outline' size='sm' className='mt-1' onClick={onAction}>\n {action}\n </Button>\n ) : null}\n </div>\n )\n}\n\n/** A key or icon named inline in an empty state's description — \"use the + above\". */\nexport function EmptyKey({ children }: { children: ReactNode }) {\n return (\n <span className='rounded border border-border px-1 font-mono text-fg-3'>{children}</span>\n )\n}\n","/**\n * Convenience helpers for creating and inspecting Segments.\n *\n * These reduce boilerplate when building AI chat UIs that work with the\n * PromptArea document model.\n *\n * @example\n * ```ts\n * import { text, chip, isSegmentsEmpty, segmentsToPlainText } from './segment-helpers.ts'\n *\n * const greeting = [text('Hello '), chip({ trigger: '@', value: 'u1', displayText: 'Alice' })]\n * isSegmentsEmpty(greeting) // false\n * segmentsToPlainText(greeting) // \"Hello @Alice\"\n * ```\n */\n\nimport type { Segment, TextSegment, ChipSegment } from './types.ts'\nimport { segmentsToPlainText, plainTextToSegments } from './prompt-area-engine.ts'\n\n// Re-export serialization utilities so consumers have a single import.\nexport { segmentsToPlainText, plainTextToSegments }\n\n// ---------------------------------------------------------------------------\n// Factories\n// ---------------------------------------------------------------------------\n\n/** Create a text segment. */\nexport function text(value: string): TextSegment {\n return { type: 'text', text: value }\n}\n\n/** Create a chip segment. */\nexport function chip(opts: Omit<ChipSegment, 'type'>): ChipSegment {\n return { type: 'chip', ...opts }\n}\n\n// ---------------------------------------------------------------------------\n// Predicates\n// ---------------------------------------------------------------------------\n\n/** Returns `true` when the segment array is empty or contains only whitespace text. */\nexport function isSegmentsEmpty(segments: Segment[]): boolean {\n if (segments.length === 0) return true\n return segments.every((seg) => seg.type === 'text' && seg.text.trim() === '')\n}\n\n/** Returns `true` when the segment array contains at least one chip. */\nexport function hasChips(segments: Segment[]): boolean {\n return segments.some((seg) => seg.type === 'chip')\n}\n\n/** Extracts all chip segments from a segment array. */\nexport function getChips(segments: Segment[]): ChipSegment[] {\n return segments.filter((seg): seg is ChipSegment => seg.type === 'chip')\n}\n\n/** Extracts chips matching a specific trigger character. */\nexport function getChipsByTrigger(segments: Segment[], trigger: string): ChipSegment[] {\n return segments.filter(\n (seg): seg is ChipSegment => seg.type === 'chip' && seg.trigger === trigger,\n )\n}\n","import type { RateLimitInfo } from '@workerdeck/protocol'\nimport type { ConnectionState, TranscriptState } from '@workerdeck/react'\nimport { formatCost, formatTokens } from '../../lib/format.ts'\nimport {\n contextSeverity,\n meterSeverity,\n modelLabel,\n statusPresentation,\n tightestWindow,\n windowLabel,\n type StatusSeverity,\n} from '../../lib/status.ts'\nimport { Ink, Row, type Tone } from './row.tsx'\n\n/**\n * The session's readings, as one line.\n *\n * The styled bar puts each of these in its own affordance — a progress ring, a\n * badge, a tooltip — which is right for a dashboard and wrong here for the usual\n * reason: a terminal has one line height, and five widgets of five different\n * heights is five different rhythms in a row that is supposed to be quiet. So\n * they are words, `·`-separated, exactly as the CLI's own status line writes\n * them.\n *\n * The readings themselves come from the same helpers the styled bar uses\n * (`lib/status.ts`), and that matters more than it looks: `statusPresentation`\n * is where the rule lives that **a dropped socket outranks the session status**,\n * because a status held over a dead socket is a stale reading being presented as\n * a live one. Re-deriving that here would be a second answer to a question that\n * already has one.\n */\n\n/** Severity → tone. The 80/95 thresholds are `lib/status.ts`'s, not new ones. */\nconst SEVERITY_TONE: Record<StatusSeverity, Tone> = {\n none: 'dim',\n warning: 'yellow',\n error: 'red',\n}\n\nexport interface TerminalStatusLineProps {\n className?: string\n state: TranscriptState\n /** Plan windows to read from, when they should not be the session's own — the\n * gateway's per-profile state merged over this transcript's. See\n * {@link StatusBarProps.rateLimits}. Absent = `state.rateLimits`. */\n rateLimits?: Record<string, RateLimitInfo>\n /** How the client is doing at reaching the gateway. Wins the status slot when\n * the socket is down — see above. */\n connection?: ConnectionState\n /** Opens the panel that answers each reading's own question. Omit and the\n * reading is text rather than something to press. */\n onOpenStatus?: () => void\n onOpenContext?: () => void\n onOpenUsage?: () => void\n}\n\n/** One reading. A button only when it leads somewhere — a segment that looks\n * pressable and does nothing is worse than plain text. */\nfunction Reading({\n tone,\n onPress,\n label,\n children,\n}: {\n tone?: Tone\n onPress?: () => void\n label?: string\n children: React.ReactNode\n}) {\n if (!onPress) return <Ink tone={tone}>{children}</Ink>\n return (\n <button type='button' className='term-reading' onClick={onPress} title={label}>\n <Ink tone={tone}>{children}</Ink>\n </button>\n )\n}\n\nexport function TerminalStatusLine({\n className,\n state,\n rateLimits,\n connection,\n onOpenStatus,\n onOpenContext,\n onOpenUsage,\n}: TerminalStatusLineProps) {\n const presentation = statusPresentation({ status: state.status, connection })\n const usage = state.contextUsage\n const window = tightestWindow(rateLimits ?? state.rateLimits)\n const model = modelLabel({ model: state.model, models: state.models ?? [] })\n\n const parts: React.ReactNode[] = [\n <Reading\n key='status'\n tone={SEVERITY_TONE[presentation.severity]}\n onPress={onOpenStatus}\n label='Session status'>\n {presentation.label}\n </Reading>,\n ]\n\n if (usage) {\n parts.push(\n <Reading\n key='context'\n tone={SEVERITY_TONE[contextSeverity(usage)]}\n onPress={onOpenContext}\n label='Context window'>\n {Math.round(usage.percentage)}% ctx ({formatTokens(usage.totalTokens)})\n </Reading>,\n )\n }\n\n if (window) {\n parts.push(\n <Reading\n key='usage'\n tone={SEVERITY_TONE[meterSeverity(window.info.utilization)]}\n onPress={onOpenUsage}\n label='Plan usage'>\n {Math.round(window.info.utilization ?? 0)}% {windowLabel(window.key)}\n </Reading>,\n )\n }\n\n // Cost last of the numbers: it is the reading you check, never the one you act\n // on, and nothing about a turn changes because of it.\n if (state.totalCostUsd > 0) {\n parts.push(\n <Ink key='cost' tone='faint'>\n {formatCost(state.totalCostUsd)}\n </Ink>,\n )\n }\n\n if (model) {\n parts.push(\n <Ink key='model' tone='faint'>\n {model}\n </Ink>,\n )\n }\n if (state.permissionMode && state.permissionMode !== 'default') {\n parts.push(\n <Ink key='mode' tone='yellow'>\n {state.permissionMode}\n </Ink>,\n )\n }\n\n return (\n <Row data-slot='status-line' tone='dim' className={className}>\n {parts.map((part, index) => (\n <span key={index}>\n {index > 0 ? <Ink tone='faint'> · </Ink> : null}\n {part}\n </span>\n ))}\n </Row>\n )\n}\n","import type { SessionInfo } from '@workerdeck/protocol'\nimport { Trash2 } from 'lucide-react'\nimport { Badge } from '../ui/Badge.tsx'\nimport { Button } from '../ui/Button.tsx'\nimport { cn } from '../../lib/utils.ts'\nimport { formatCost, formatRelativeTime } from '../../lib/format.ts'\nimport { STATUS_META } from './status.ts'\n\nexport interface SessionListItemProps {\n session: SessionInfo\n active?: boolean\n onSelect?: (id: string) => void\n onDelete?: (id: string) => void\n className?: string\n}\n\nexport function SessionListItem({ session, active, onSelect, onDelete, className }: SessionListItemProps) {\n const meta = STATUS_META[session.status]\n return (\n <div\n data-slot='session-list-item'\n data-active={active || undefined}\n className={cn(\n 'group flex w-full items-center gap-2 rounded-md border border-transparent px-2.5 py-2 text-left transition-colors',\n active ? 'border-border bg-surface' : 'hover:bg-surface-hover',\n className,\n )}>\n <button\n type='button'\n onClick={() => onSelect?.(session.id)}\n className='min-w-0 flex-1 text-left outline-none'>\n <div className='flex items-center gap-2'>\n <span className='truncate text-body-sm font-medium text-fg-1'>\n {session.title ?? session.id.slice(0, 8)}\n </span>\n <Badge variant={meta.variant} dot className='shrink-0'>\n {meta.label}\n </Badge>\n </div>\n <div className='mt-0.5 flex items-center gap-2 font-mono text-label text-fg-4'>\n <span className='truncate'>{session.cwd}</span>\n {session.profile ? <span className='shrink-0'>@{session.profile}</span> : null}\n <span className='shrink-0'>{formatCost(session.totalCostUsd)}</span>\n <span className='shrink-0'>{formatRelativeTime(session.lastActivityAt ?? session.createdAt)}</span>\n </div>\n </button>\n {onDelete ? (\n <Button\n variant='ghost'\n size='icon-sm'\n aria-label='Close session'\n className='opacity-0 transition-opacity group-hover:opacity-100'\n onClick={() => onDelete(session.id)}>\n <Trash2 className='size-3.5 text-fg-3' />\n </Button>\n ) : null}\n </div>\n )\n}\n\nexport interface SessionListProps {\n sessions: SessionInfo[]\n activeId?: string\n onSelect?: (id: string) => void\n onDelete?: (id: string) => void\n emptyText?: string\n className?: string\n}\n\nexport function SessionList({\n sessions,\n activeId,\n onSelect,\n onDelete,\n emptyText = 'No sessions yet.',\n className,\n}: SessionListProps) {\n return (\n <div data-slot='session-list' className={cn('flex flex-col gap-1', className)}>\n {sessions.length === 0 ? (\n <div className='px-2.5 py-6 text-center text-body-sm text-fg-4'>{emptyText}</div>\n ) : (\n sessions.map((session) => (\n <SessionListItem\n key={session.id}\n session={session}\n active={session.id === activeId}\n onSelect={onSelect}\n onDelete={onDelete}\n />\n ))\n )}\n </div>\n )\n}\n","import type { ProjectIcon as ProjectIconSpec } from '@workerdeck/protocol'\nimport {\n Anchor, Atom, Beaker, Bike, Binary, Blocks, Bot, Box, Brain, Briefcase, Brush, Bug,\n Building2, Cable, Camera, Car, Cat, CircuitBoard, Cloud, Code, Coffee, Cog, Compass,\n Container, Cpu, CreditCard, Database, Diamond, Dog, Feather, Film, Flame, FlaskConical,\n Folder, Gamepad2, Gauge, Gem, Ghost, Gift, GitBranch, Globe, GraduationCap, Hammer,\n Hexagon, House, Image, Key, Landmark, Laptop, Layers, Leaf, Library, Lightbulb, Lock,\n Mail, Map, MessageCircle, Mic, Monitor, Moon, Mountain, Music, Network, Newspaper,\n Notebook, Orbit, Package, Palette, PenTool, Phone, PiggyBank, Plane, Plug, Puzzle,\n Radar, Radio, Receipt, Rocket, Ruler, Satellite, Scale, Scissors, Server, Shield, Ship,\n ShoppingCart, Signal, Smartphone, Sparkles, Sprout, Square, Star, Sun, Table, Tag,\n Target, Telescope, Terminal, TestTube, TreePine, Trophy, Truck, Umbrella, Users, Wallet,\n Wand, Waves, Webhook, Wifi, Wrench, Zap,\n} from 'lucide-react'\nimport type { ComponentType } from 'react'\nimport { cn } from '../../lib/utils.ts'\n\n/**\n * A project's icon — the render side of protocol's `ProjectIcon`, for a list\n * row or a group header.\n *\n * **The glyph arm is a curated set, and that is the design rather than a\n * shortcut.** The gateway validates a glyph name by *shape* only (lucide's\n * lowercase-kebab convention) and explicitly declines to grow an icon catalog,\n * so the contract already says a client must fall back on a name it does not\n * know.\n *\n * The alternative was measured rather than argued, against the VS Code\n * sidebar's own bundle: `sidebar.js` is **31 KB** with one glyph, **77 KB**\n * with the 110 below (~418 bytes each), and **927 KB** with a\n * `import * as` over lucide's ~1,600 — which is what it costs, because a\n * namespace import defeats tree-shaking by construction. (`DynamicIcon` is the\n * third option and worse in a different currency: ~1,600 chunk files shipped\n * inside the `.vsix`.) Twelve times the bundle to render a name nobody has\n * declared yet is not a trade worth making; 46 KB to cover the names people\n * actually use is.\n *\n * The set is chosen to cover what people call a project; an unlisted-but-valid\n * name draws {@link Folder}, which is what the row drew before this feature\n * existed. **Grow it freely** — each addition is ~400 bytes and the fallback\n * means a missing one is a shrug, not a hole.\n *\n * **The image arm draws nothing until its bytes arrive**, and takes them as a\n * resolved `src` rather than fetching: the wire carries only an address\n * (media type, size, content hash), and *who* fetches it differs per client —\n * a VS Code webview cannot reach a gateway at all and must be handed a data\n * URL by its extension host, while a browser can fetch and `createObjectURL`.\n * A component that fetched would be wrong on one of them. Absent `src` renders\n * nothing rather than a placeholder box: an icon is decoration beside a name\n * that is already there, and a box that becomes a picture a beat later is more\n * movement than the picture is worth.\n *\n * One thing an image icon cannot do, and a glyph can: **take the row's\n * colour.** An `<img>`-embedded SVG is its own document, so `currentColor` does\n * not reach it and its own `prefers-color-scheme` resolves against the *OS*\n * rather than the host's theme (measured: it does resolve, which is the\n * surprise — so a monochrome mark tuned for both schemes is right on a dark OS\n * in a dark editor and wrong on a light OS in a dark one). A repo that wants a\n * mark that always matches its row should declare a glyph; one that wants its\n * brand should declare the image and accept that it is a picture.\n */\nconst GLYPHS: Record<string, ComponentType<{ className?: string }>> = {\n anchor: Anchor, atom: Atom, beaker: Beaker, bike: Bike, binary: Binary, blocks: Blocks,\n bot: Bot, box: Box, brain: Brain, briefcase: Briefcase, brush: Brush, bug: Bug,\n 'building-2': Building2, cable: Cable, camera: Camera, car: Car, cat: Cat,\n 'circuit-board': CircuitBoard, cloud: Cloud, code: Code, coffee: Coffee, cog: Cog,\n compass: Compass, container: Container, cpu: Cpu, 'credit-card': CreditCard,\n database: Database, diamond: Diamond, dog: Dog, feather: Feather, film: Film,\n flame: Flame, 'flask-conical': FlaskConical, folder: Folder, 'gamepad-2': Gamepad2,\n gauge: Gauge, gem: Gem, ghost: Ghost, gift: Gift, 'git-branch': GitBranch, globe: Globe,\n 'graduation-cap': GraduationCap, hammer: Hammer, hexagon: Hexagon, house: House,\n image: Image, key: Key, landmark: Landmark, laptop: Laptop, layers: Layers, leaf: Leaf,\n library: Library, lightbulb: Lightbulb, lock: Lock, mail: Mail, map: Map,\n 'message-circle': MessageCircle, mic: Mic, monitor: Monitor, moon: Moon,\n mountain: Mountain, music: Music, network: Network, newspaper: Newspaper,\n notebook: Notebook, orbit: Orbit, package: Package, palette: Palette, 'pen-tool': PenTool,\n phone: Phone, 'piggy-bank': PiggyBank, plane: Plane, plug: Plug, puzzle: Puzzle,\n radar: Radar, radio: Radio, receipt: Receipt, rocket: Rocket, ruler: Ruler,\n satellite: Satellite, scale: Scale, scissors: Scissors, server: Server, shield: Shield,\n ship: Ship, 'shopping-cart': ShoppingCart, signal: Signal, smartphone: Smartphone,\n sparkles: Sparkles, sprout: Sprout, square: Square, star: Star, sun: Sun, table: Table,\n tag: Tag, target: Target, telescope: Telescope, terminal: Terminal, 'test-tube': TestTube,\n 'tree-pine': TreePine, trophy: Trophy, truck: Truck, umbrella: Umbrella, users: Users,\n wallet: Wallet, wand: Wand, waves: Waves, webhook: Webhook, wifi: Wifi, wrench: Wrench,\n zap: Zap,\n}\n\nexport function ProjectIcon({\n icon,\n src,\n name,\n className,\n}: {\n icon: ProjectIconSpec | undefined\n /** Resolved bytes for the `image` arm — an object URL or a data URL. The\n * caller fetches (see the note above); absent draws nothing. */\n src?: string\n /** The project's name, for the alt text. */\n name?: string\n className?: string\n}) {\n if (!icon) return null\n if (icon.type === 'image') {\n if (!src) return null\n return (\n <img\n src={src}\n alt=''\n aria-hidden\n title={name}\n // `object-contain` because a declared icon is whatever aspect the repo\n // checked in, and a squashed logo reads worse than a letterboxed one.\n className={cn('inline-block size-3 shrink-0 object-contain', className)}\n />\n )\n }\n const Glyph = GLYPHS[icon.name] ?? Folder\n return <Glyph className={cn('inline-block size-3 shrink-0', className)} />\n}\n","import type { ContextReading } from '@workerdeck/protocol'\nimport { ProgressRing } from '../ui/ProgressRing.tsx'\nimport { cn } from '../../lib/utils.ts'\nimport { formatTokens } from '../../lib/format.ts'\nimport { meterColorClass } from '../../lib/status.ts'\n\n/**\n * How full a session's context window is, sized for a **list row**.\n *\n * The reading the session screen has always drawn, moved somewhere it answers a\n * question the session screen cannot: *which* of these thirty sessions is\n * bloating. That is also why it is a ring and not a number — across a list, a\n * ring is read at a glance and `71%` has to be read one row at a time.\n *\n * **Absent draws nothing, and absent is not zero.** A promptless session, a\n * parked one from before the field existed, or an engine that reports no window\n * has no reading; an empty ring would claim its context is empty rather than\n * unknown. The title carries the exact numbers for anyone who wants them.\n *\n * Shared rather than spelled per client: the dashboard's row and the VS Code\n * sidebar's card both draw it, and two copies would be two sets of thresholds\n * the day one of them is nudged.\n */\nexport function ContextRing({\n usage,\n size = 11,\n className,\n}: {\n usage: ContextReading | undefined\n size?: number\n className?: string\n}) {\n if (usage === undefined) return null\n return (\n <span\n title={`Context ${usage.percentage.toFixed(0)}% · ${formatTokens(usage.totalTokens)} / ${formatTokens(usage.maxTokens)}`}\n className={cn('shrink-0 leading-none', meterColorClass(usage.percentage), className)}>\n <ProgressRing value={usage.percentage} size={size} strokeWidth={2} />\n </span>\n )\n}\n","import { cn } from '../../lib/utils.ts'\n\n/**\n * The engine's own mark, monochrome.\n *\n * Inlined rather than depended on: the React package these come from\n * (`@lobehub/icons`) pulls antd, `@lobehub/ui` and a second copy of\n * lucide-react — an absurd amount of bundle for a few glyphs in a sidebar.\n * These are paths from `@lobehub/icons-static-svg` (MIT, © LobeHub), already\n * `fill=\"currentColor\"` single-path marks, so they take the row's colour like\n * any other glyph and need no light/dark variants.\n *\n * The marks are trademarks of their owners (Anthropic, OpenAI, Google,\n * DeepSeek, Moonshot), used here only to identify which engine a session runs\n * on.\n *\n * Codex is drawn as OpenAI: the session's engine *is* OpenAI's, and OpenAI's\n * mark is the one a person recognises at 12px.\n */\nconst PATHS: Record<string, { title: string; d: string }> = {\n claude: {\n title: 'Claude',\n d: 'M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z',\n },\n codex: {\n title: 'OpenAI',\n d: 'M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z',\n },\n gemini: {\n title: 'Gemini',\n d: 'M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z',\n },\n deepseek: {\n title: 'DeepSeek',\n d: 'M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z',\n },\n moonshot: {\n title: 'Moonshot',\n d: 'M1.052 16.916l9.539 2.552a21.007 21.007 0 00.06 2.033l5.956 1.593a11.997 11.997 0 01-5.586.865l-.18-.016-.044-.004-.084-.009-.094-.01a11.605 11.605 0 01-.157-.02l-.107-.014-.11-.016a11.962 11.962 0 01-.32-.051l-.042-.008-.075-.013-.107-.02-.07-.015-.093-.019-.075-.016-.095-.02-.097-.023-.094-.022-.068-.017-.088-.022-.09-.024-.095-.025-.082-.023-.109-.03-.062-.02-.084-.025-.093-.028-.105-.034-.058-.019-.08-.026-.09-.031-.066-.024a6.293 6.293 0 01-.044-.015l-.068-.025-.101-.037-.057-.022-.08-.03-.087-.035-.088-.035-.079-.032-.095-.04-.063-.028-.063-.027a5.655 5.655 0 01-.041-.018l-.066-.03-.103-.047-.052-.024-.096-.046-.062-.03-.084-.04-.086-.044-.093-.047-.052-.027-.103-.055-.057-.03-.058-.032a6.49 6.49 0 01-.046-.026l-.094-.053-.06-.034-.051-.03-.072-.041-.082-.05-.093-.056-.052-.032-.084-.053-.061-.039-.079-.05-.07-.047-.053-.035a7.785 7.785 0 01-.054-.036l-.044-.03-.044-.03a6.066 6.066 0 01-.04-.028l-.057-.04-.076-.054-.069-.05-.074-.054-.056-.042-.076-.057-.076-.059-.086-.067-.045-.035-.064-.052-.074-.06-.089-.073-.046-.039-.046-.039a7.516 7.516 0 01-.043-.037l-.045-.04-.061-.053-.07-.062-.068-.06-.062-.058-.067-.062-.053-.05-.088-.084a13.28 13.28 0 01-.099-.097l-.029-.028-.041-.042-.069-.07-.05-.051-.05-.053a6.457 6.457 0 01-.168-.179l-.08-.088-.062-.07-.071-.08-.042-.049-.053-.062-.058-.068-.046-.056a7.175 7.175 0 01-.027-.033l-.045-.055-.066-.082-.041-.052-.05-.064-.02-.025a11.99 11.99 0 01-1.44-2.402zm-1.02-5.794l11.353 3.037a20.468 20.468 0 00-.469 2.011l10.817 2.894a12.076 12.076 0 01-1.845 2.005L.657 15.923l-.016-.046-.035-.104a11.965 11.965 0 01-.05-.153l-.007-.023a11.896 11.896 0 01-.207-.741l-.03-.126-.018-.08-.021-.097-.018-.081-.018-.09-.017-.084-.018-.094c-.026-.141-.05-.283-.071-.426l-.017-.118-.011-.083-.013-.102a12.01 12.01 0 01-.019-.161l-.005-.047a12.12 12.12 0 01-.034-2.145zm1.593-5.15l11.948 3.196c-.368.605-.705 1.231-1.01 1.875l11.295 3.022c-.142.82-.368 1.612-.668 2.365l-11.55-3.09L.124 10.26l.015-.1.008-.049.01-.067.015-.087.018-.098c.026-.148.056-.295.088-.442l.028-.124.02-.085.024-.097c.022-.09.045-.18.07-.268l.028-.102.023-.083.03-.1.025-.082.03-.096.026-.082.031-.095a11.896 11.896 0 011.01-2.232zm4.442-4.4L17.352 4.59a20.77 20.77 0 00-1.688 1.721l7.823 2.093c.267.852.442 1.744.513 2.665L2.106 5.213l.045-.065.027-.04.04-.055.046-.065.055-.076.054-.072.064-.086.05-.065.057-.073.055-.07.06-.074.055-.069.065-.077.054-.066.066-.077.053-.06.072-.082.053-.06.067-.074.054-.058.073-.078.058-.06.063-.067.168-.17.1-.098.059-.056.076-.071a12.084 12.084 0 012.272-1.677zM12.017 0h.097l.082.001.069.001.054.002.068.002.046.001.076.003.047.002.06.003.054.002.087.005.105.007.144.011.088.007.044.004.077.008.082.008.047.005.102.012.05.006.108.014.081.01.042.006.065.01.207.032.07.012.065.011.14.026.092.018.11.022.046.01.075.016.041.01L14.7.3l.042.01.065.015.049.012.071.017.096.024.112.03.113.03.113.032.05.015.07.02.078.024.073.023.05.016.05.016.076.025.099.033.102.036.048.017.064.023.093.034.11.041.116.045.1.04.047.02.06.024.041.018.063.026.04.018.057.025.11.048.1.046.074.035.075.036.06.028.092.046.091.045.102.052.053.028.049.026.046.024.06.033.041.022.052.029.088.05.106.06.087.051.057.034.053.032.096.059.088.055.098.062.036.024.064.041.084.056.04.027.062.042.062.043.023.017c.054.037.108.075.161.114l.083.06.065.048.056.043.086.065.082.064.04.03.05.041.086.069.079.065.085.071c.712.6 1.353 1.283 1.909 2.031L7.222.994l.062-.027.065-.028.081-.034.086-.035c.113-.045.227-.09.341-.131l.096-.035.093-.033.084-.03.096-.031c.087-.03.176-.058.264-.085l.091-.027.086-.025.102-.03.085-.023.1-.026L9.04.37l.09-.023.091-.022.095-.022.09-.02.098-.021.091-.02.095-.018.092-.018.1-.018.091-.016.098-.017.092-.014.097-.015.092-.013.102-.013.091-.012.105-.012.09-.01.105-.01c.093-.01.186-.018.28-.024l.106-.008.09-.005.11-.006.093-.004.1-.004.097-.002.099-.002.197-.002z',\n },\n}\n\n/**\n * Which mark a session wears.\n *\n * The two first-party engines are named directly. A `provider` session is\n * whatever the host wired up, so its *model* is the only thing that says whose\n * it is — sniffed loosely on purpose (`gemini-2.5-pro`, `deepseek-chat`,\n * `kimi-k2`), and falling through to no mark rather than guessing wrong.\n */\nexport function engineMark(engine: string, model?: string): string | undefined {\n if (engine === 'claude') return 'claude'\n if (engine === 'codex') return 'codex'\n const id = model?.toLowerCase() ?? ''\n if (!id) return undefined\n if (id.includes('gemini')) return 'gemini'\n if (id.includes('deepseek')) return 'deepseek'\n if (id.includes('moonshot') || id.includes('kimi')) return 'moonshot'\n if (id.includes('claude')) return 'claude'\n if (id.startsWith('gpt') || id.startsWith('o1') || id.startsWith('o3') || id.includes('openai')) {\n return 'codex'\n }\n return undefined\n}\n\n/**\n * Which engines wear their vendor's colour, and **how far it reaches**.\n *\n * `MARK` is the glyph, `TEXT` is the model name beside it. They are the same for\n * Anthropic and deliberately not for OpenAI, because the two brands differ in\n * kind rather than in hue: coral is an accent, and OpenAI's guidelines forbid\n * adding colour to the mark at all, so theirs is pure white/black. At full\n * contrast that is right on a 12px glyph and wrong on an 11px label — a\n * pure-white model name is *brighter than the session title above it*, which\n * inverts the row's whole hierarchy to say something the mark has already said.\n * So OpenAI's name keeps the muted line it always had, which is also the most\n * literal reading of \"don't add any colors\".\n *\n * Anything absent falls through to muted on both counts.\n */\nconst VENDOR_MARK: Record<string, string> = {\n claude: 'text-vendor-claude',\n codex: 'text-vendor-openai',\n}\nconst VENDOR_TEXT: Record<string, string> = {\n claude: 'text-vendor-claude',\n}\n\n/** The colour a session's engine mark wears. Pass it to `EngineIcon`'s\n * `className` rather than to a parent: the svg carries its own `text-fg-3`,\n * and only a class on the element itself is merged over it. */\nexport function vendorMarkClass(engine: string, model?: string): string {\n return VENDOR_MARK[engineMark(engine, model) ?? ''] ?? 'text-fg-3'\n}\n\n/** The colour the model *name* wears beside that mark — muted unless the vendor\n * is one whose accent survives at label size. See `VENDOR_TEXT`.\n *\n * The fallback is `text-fg-3` rather than the metadata line's own `text-fg-4`,\n * so an unbranded model still sits one step above the project and gateway\n * beside it — the order the spec lists them in, held without a colour. */\nexport function vendorTextClass(engine: string, model?: string): string {\n return VENDOR_TEXT[engineMark(engine, model) ?? ''] ?? 'text-fg-3'\n}\n\nexport function EngineIcon({\n engine,\n model,\n className,\n}: {\n engine: string\n model?: string\n className?: string\n}) {\n const mark = PATHS[engineMark(engine, model) ?? '']\n // An engine this build has never heard of still gets a slot, so the second\n // line doesn't reflow around a missing glyph.\n if (!mark) {\n return (\n <span aria-hidden title={engine} className={cn('shrink-0 text-fg-3', className)}>\n ·\n </span>\n )\n }\n return (\n <svg\n viewBox='0 0 24 24'\n fill='currentColor'\n fillRule='evenodd'\n role='img'\n aria-label={mark.title}\n className={cn('size-3 shrink-0 text-fg-3', className)}>\n <title>{mark.title}</title>\n <path d={mark.d} />\n </svg>\n )\n}\n","import { BellRing, CircleAlert, CircleSlash, Moon, PauseCircle } from 'lucide-react'\nimport type { SessionRow } from '@workerdeck/protocol'\nimport { Spinner } from '../ui/Spinner.tsx'\nimport { cn } from '../../lib/utils.ts'\n\n/**\n * State as one glyph — a ringing bell when it wants a human, a spinner while it\n * works, a moon when it is only sleeping. Replaces the text badge: in a sidebar\n * the word costs more room than it earns, and the states that matter are the two\n * you can recognise without reading.\n *\n * **It reads `row.state`, not `info.status`, and that distinction is the whole\n * point of the row model.** `sessionState` already folds in the arm this glyph\n * cannot see for itself: a *background* sub-agent outlives its turn by design,\n * so the turn ends, `status` comes to rest at `idle`, and the agent keeps\n * working. Reading the raw status drew a **moon on a row filed under the\n * \"Working\" header** — the list contradicting itself on one line, which is\n * exactly what a derived view model exists to prevent. The value was in scope\n * and unread.\n *\n * The terminal statuses still come off `info.status`, because `ended` collapses\n * `failed` and `closed` into one bucket and those are worth telling apart here.\n *\n * It lives in its own file rather than inside the dashboard's browser because\n * the extension's cards draw it too, and a second copy there is how the two\n * lists last disagreed about what a parked session looks like.\n */\nexport function SessionStatusIcon({ row, className }: { row: SessionRow; className?: string }) {\n const { info } = row\n const size = cn('size-4 shrink-0', className)\n if (row.state === 'attention') return <BellRing className={cn(size, 'animate-pulse text-warning')} />\n if (row.state === 'working') return <Spinner className={cn(size, 'text-info')} />\n switch (info.status) {\n case 'failed':\n return <CircleAlert className={cn(size, 'text-danger')} />\n case 'closed':\n return <CircleSlash className={cn(size, 'text-fg-4')} />\n case 'parked':\n return <PauseCircle className={cn(size, 'text-fg-3')} />\n default:\n return <Moon className={cn(size, 'text-fg-4')} />\n }\n}\n","import {\n ArrowRight,\n Check,\n ChevronDown,\n ChevronRight,\n CircleAlert,\n Dot,\n PauseCircle,\n} from 'lucide-react'\nimport { isAgentRecord, subagentLabel } from '@workerdeck/protocol'\nimport type { SessionInfo, SubagentInfo } from '@workerdeck/protocol'\nimport { Spinner } from '../ui/Spinner.tsx'\nimport { cn } from '../../lib/utils.ts'\n\n/**\n * The work *under* a session row, and the one row shape every client renders it\n * through.\n *\n * This lived inside the VS Code webview first, which is exactly why the\n * dashboard had none of it. Nothing in here is extension-specific: a session's\n * sub-agents are a protocol fact, and a disclosure over them is a list\n * affordance. It sits in `packages/ui` so the sidebar, the dashboard and (in its\n * own idiom) the phone are annotating rows the same way rather than three ways.\n *\n * Today the only source is `SessionInfo.subagents`. The other — the CLI's own\n * **task checklist**, the to-do list it keeps for the current turn — is what the\n * design was drawn from, and it is not built: nothing on the wire carries it yet\n * (see `_docs/features/sub-agent-handling.md`, second thread, which opens with\n * \"check a capture\" rather than \"design a surface\"). When it arrives it is a\n * *source*, not a second row component: checklist when the session has one, its\n * sub-agents otherwise.\n *\n * That is also why `state` has a `pending` arm no sub-agent can produce. A\n * sub-agent record exists only once dispatched, so it is never queued; a to-do\n * is queued for most of its life, and dropping the state would mean widening the\n * union later — the shape is cheaper to state now than to retrofit.\n */\nexport type Step = {\n key: string\n label: string\n /** What one of these is called, for the disclosure's count. */\n noun: string\n /**\n * Both kinds press. The kind decides **where the press goes**, which is the\n * whole of the distinction.\n *\n * An **agent** has an identity and work of its own, so it wears the sub-agent\n * colour and opens that agent's own frame. A **task** is something the model\n * described with no agent behind it (`isAgentRecord`), so there is no frame:\n * framing its id selects no items and draws an empty screen. What it has\n * instead is a *place* — the spawning call's row in the transcript — so it is\n * muted, never selected, and travels there. See `StepRow` for why an inert\n * row was the worse answer.\n *\n * Two values and not a boolean because the checklist source this shape was\n * drawn for (see above) produces the second kind natively — a to-do is a task\n * in exactly this sense, and it will slot in here rather than widening\n * anything.\n */\n kind: 'agent' | 'task'\n state: 'done' | 'running' | 'pending' | 'failed'\n /** A trailing reading — a sub-agent's tool count. Absent draws nothing. */\n detail?: string\n title: string\n onSelect: () => void\n}\n\n/**\n * The steps under one session, **agents first**.\n *\n * The two kinds do different things when pressed and are worth different\n * amounts of attention: an agent has work of its own to go and read, a task is a\n * marker in a transcript. Interleaved in dispatch order they read as one\n * undifferentiated list, and the rows you can actually open are scattered\n * through it. Grouped, the openable ones are a block at the top and the markers\n * are a tail you can skip.\n *\n * Stable **within** each group, deliberately: dispatch order is the only order\n * these records have that means anything (it is the order the work was started\n * in), so the sort partitions and never reorders inside a partition.\n *\n * `onSelect` is handed the **kind** as well as the id, because the two kinds go\n * to different places — see `SessionItem`, which is what routes them. Passing\n * only the id is what let a task be opened as if it were an agent, and a framed\n * task id matches no items, so the panel drew an **empty agent view**.\n */\nexport function sessionSteps(\n info: SessionInfo,\n onSelect: (toolUseId: string, kind: Step['kind']) => void,\n): Step[] {\n // The label is protocol's `subagentLabel`, not a spelling of its own: the\n // dashboard and the phone render the same rows from the same records, and two\n // spellings would be two different answers to \"which agent is this\".\n const steps = (info.subagents ?? []).map((sub) => {\n const kind = isAgentRecord(sub) ? ('agent' as const) : ('task' as const)\n return {\n key: sub.toolUseId,\n label: subagentLabel(sub),\n noun: 'agent',\n kind,\n state: stepState(sub.status),\n detail: sub.toolCount > 0 ? String(sub.toolCount) : undefined,\n title: `${subagentLabel(sub)} · ${sub.toolCount} tool${sub.toolCount === 1 ? '' : 's'}`,\n onSelect: () => onSelect(sub.toolUseId, kind),\n }\n })\n return [...steps.filter((s) => s.kind === 'agent'), ...steps.filter((s) => s.kind === 'task')]\n}\n\nfunction stepState(status: SubagentInfo['status']): Step['state'] {\n switch (status) {\n case 'running':\n return 'running'\n case 'failed':\n return 'failed'\n default:\n return 'done'\n }\n}\n\n/** How many of these are still going — the live half of the disclosure's count. */\nexport function runningSteps(steps: readonly Step[]): number {\n return steps.filter((s) => s.state === 'running').length\n}\n\n/**\n * The disclosure, which is also the reading: `3 agents` — or `2 of 3 agents`\n * while some have settled, because \"how many are still going\" is the live\n * question and a bare total answers it wrong the moment one finishes.\n *\n * Sub-agents are an annotation on a working row rather than a state of their own\n * (see `runningSubagents` in protocol's `session-list.ts`), so this never\n * competes with the row's status glyph: that still says what the *session* is\n * doing.\n */\nexport function StepToggle({\n expanded,\n running,\n total,\n noun,\n onToggle,\n}: {\n expanded: boolean\n running: number\n total: number\n noun: string\n onToggle: () => void\n}) {\n // Two spellings of one count. The **words** are the honest reading and go in\n // the tooltip and to a screen reader; the line itself gets the digits, because\n // this sits on the second line of a 280px row next to the folder and the age,\n // and `1 of 6 agents` truncated the folder name away to say something the row\n // could say in three characters.\n const label = running > 0 && running < total ? `${running}/${total}` : String(total)\n const words =\n running > 0 && running < total\n ? `${running} of ${total} ${noun}s running`\n : `${total} ${noun}${total === 1 ? '' : 's'}`\n const Chevron = expanded ? ChevronDown : ChevronRight\n return (\n <button\n type='button'\n aria-expanded={expanded}\n aria-label={`${expanded ? 'Hide' : 'Show'} ${words}`}\n title={`${expanded ? 'Hide' : 'Show'} ${words}`}\n onClick={(e) => {\n // The whole row is a button and this one does not mean \"select\" — the\n // same guard the row's overflow needs, and the reason a drag-select\n // inside the row does not toggle it.\n e.stopPropagation()\n onToggle()\n }}\n className={cn(\n 'flex shrink-0 items-center gap-0.5 rounded-[4px] py-0.5 pr-1 pl-0.5 outline-none',\n 'text-[0.75rem] leading-3 hover:bg-row-hover hover:text-fg-2',\n running > 0 ? 'text-info' : 'text-fg-4',\n )}>\n <Chevron className='size-3' />\n <span className='tabular-nums'>{label}</span>\n </button>\n )\n}\n\n/**\n * One step under its session — **pressable, all of them**, and what a press\n * means is what tells the two kinds apart.\n *\n * Pressing an **agent** hands the panel over to that agent's own work\n * (`SessionPanel.openSubagent`): it is not a session and never becomes one, but\n * it has a surface, so it can be the selected thing. Pressing a **task** selects\n * the *session* and travels to that task's marker inside it — a task is a\n * reference to a place in a transcript, not a thing with a screen, so it can be\n * followed but never held.\n *\n * That is a reversal, and a deliberate one. A task used to be inert markup on\n * the argument that \"a disabled-looking button still announces itself as one\" —\n * correct about the markup, wrong about the premise, because there *was* always\n * somewhere to go and the row simply swallowed the click on its way there. A row\n * that looks like a list item, sits in a list, and does nothing when pressed is\n * the worse lie. So every step answers the pointer and every step answers a\n * press; only what the press does differs.\n *\n * Divided from the card's header by **indentation and its own hit shape**, not\n * by a rule. The rules came first, on the argument that at 11px an indent is not\n * enough to say \"list inside a row\" — and they were right about the reading and\n * wrong about the cost: a stack of hairlines across every open card turned the\n * list into a ledger, and a rule cannot answer a pointer. A step that lights up\n * under the cursor and fills when it is the one on screen says *list* far more\n * plainly than a line between two of them, and it says it while doing the job\n * the rule could not.\n *\n * **Only an agent can wear the selection**, and `active` is guarded on that here\n * rather than trusted from the caller: a host that hands back a task's key is\n * describing where it navigated, not what it selected, and the row must not\n * paint itself blue for it.\n *\n * The hover is `--row-active` — **alpha, not a flat fill** — because this row\n * has to answer the pointer on three different grounds: a transparent card, a\n * blue one (its session is selected), and a grey one (a sibling agent is). A\n * flat value tuned for any of those is wrong on the other two; a tint darkens or\n * lifts whatever it lands on. It is the one place in the list where the alpha\n * token earns its keep.\n */\nexport function StepRow({\n step,\n active = false,\n onSelect,\n}: {\n step: Step\n /** The panel is showing this step's own work. Ignored for tasks, which cannot\n * be the selected thing — see above. */\n active?: boolean\n onSelect: () => void\n}) {\n const agent = step.kind === 'agent'\n const selected = active && agent\n // Body colour by *kind*, state carried by the icon — the rule the transcript's\n // own `TaskRow` already follows, where the body is green and the marker holds\n // the beat. Green means sub-agent across this product (it is the one hue\n // nothing else on a session surface claims), so a list that spent blue on\n // \"running\" here would be saying something different from the transcript\n // about the same agent. Failure still outranks it: an alarm is not a category.\n const body = step.state === 'failed' ? 'text-danger' : agent ? 'text-success' : 'text-fg-4'\n return (\n <button\n type='button'\n title={step.title}\n aria-current={selected || undefined}\n onClick={(e) => {\n // The whole card is pressable underneath and means \"open the session\".\n // This row has its own answer — for an agent a different destination,\n // for a task the same one plus a place to land — so it must not also\n // fire the card's.\n e.stopPropagation()\n onSelect()\n }}\n className={cn(\n 'flex w-full items-center gap-1.5 rounded-[4px] py-1 pr-2.5 pl-3.5',\n 'text-left text-micro outline-none',\n selected ? 'bg-row-selected' : 'hover:bg-row-active',\n body,\n )}>\n <StepIcon state={step.state} kind={step.kind} />\n <span className='min-w-0 flex-1 truncate'>{step.label}</span>\n {/* The progress reading while it works, and what it cost when it is done.\n Zero draws nothing: `0 tools` beside a thinking agent reads as a stall,\n which is the same call `taskSummary` makes one surface over. */}\n {step.detail ? <span className='shrink-0 tabular-nums text-fg-4'>{step.detail}</span> : null}\n {agent ? <ArrowRight className='size-3.5 shrink-0 text-fg-4' /> : null}\n </button>\n )\n}\n\nfunction StepIcon({ state, kind }: { state: Step['state']; kind: Step['kind'] }) {\n // A task that is neither running nor failed gets a neutral dot rather than a\n // tick: `done` is a claim about work, and nothing here did any.\n if (kind === 'task' && state !== 'running' && state !== 'failed') {\n return <Dot className='size-[11px] shrink-0' />\n }\n switch (state) {\n case 'running':\n return <Spinner className='size-[11px] shrink-0' />\n case 'failed':\n return <CircleAlert className='size-[11px] shrink-0' />\n case 'pending':\n return <PauseCircle className='size-[11px] shrink-0' />\n default:\n return <Check className='size-[11px] shrink-0' />\n }\n}\n","import { Fragment, useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { projectLabel, projectSubpath, sessionLabel } from '@workerdeck/protocol'\nimport type { SessionRow } from '@workerdeck/protocol'\nimport { ContextRing } from './ContextRing.tsx'\nimport { EngineIcon, vendorMarkClass, vendorTextClass } from './EngineIcon.tsx'\nimport { ProjectIcon } from './ProjectIcon.tsx'\nimport { SessionStatusIcon } from './SessionStatusIcon.tsx'\nimport { StepRow, StepToggle, runningSteps, sessionSteps } from './SessionSteps.tsx'\nimport type { Step } from './SessionSteps.tsx'\nimport { cn } from '../../lib/utils.ts'\nimport { formatCost, formatRelativeTime, friendlyModel } from '../../lib/format.ts'\n\n/**\n * One session in a list — the **card**, and the only drawing of it in the\n * product.\n *\n * The dashboard's `SessionBrowser` and the VS Code sidebar both render this;\n * before it existed they rendered two hand-kept copies that agreed on the model\n * and disagreed on every measurement, and the two lists read as two products.\n * iOS mirrors this file's geometry rather than either copy.\n *\n * ## The shape\n *\n * A 4px-padded, 4px-rounded card whose whole surface is the hit target, holding\n * two 20px lines and, when it is open, the work under them.\n *\n * **Line one is state and identity**: the status glyph leads, the title takes\n * the rest, and the line closes with the unread badge and the context ring. The\n * glyph leads because a sessions list is scanned for *state* first — which of\n * these is working, which is waiting on me — and the title is what you read once\n * the glyph has told you which row to read. The two readings that close the line\n * are the two that change while you are looking at them.\n *\n * **Line two is identity and cost**: the engine's mark and its model in the\n * vendor's own colour, then the project, then everything else the host wants\n * said, then the age — a `·`-joined run that ends in the one part of it that\n * keeps moving. The line's right edge belongs to the disclosure (which doubles\n * as the count of the work below, so the card says how much there is without\n * being opened) and to the host's own actions.\n *\n * **The gutter is one 16px cell, shared by both lines.** The two glyphs are\n * different sizes and laying them out as plain flex children starts the two\n * text columns at different x — two pixels, invisible as a measurement and\n * obvious as a misalignment. Centring inside a fixed cell fixes the text edges\n * and the ink centres at once. It is the terminal gutter's argument at card\n * scale.\n *\n * ## Selection\n *\n * **Selection is the card's own fill**, not a gutter bar: the card is an inset\n * shape with air around it, so filling it is unambiguous in a way a fill on a\n * full-bleed row is not, and it leaves the left edge to the state glyph.\n *\n * There are **two selections at two grains**, and the card and its steps split\n * them:\n *\n * | what is on screen | card | the step |\n * | --- | --- | --- |\n * | nothing | transparent, hovers | transparent, hovers |\n * | this session | `--row-selected` (blue) | transparent, hovers |\n * | one of its sub-agents | `--row-selected-weak` (grey) | `--row-selected` (blue) |\n *\n * **The blue always marks the finest thing selected.** Opening a sub-agent\n * selects its session too, so both claims are true at once and the blue can only\n * carry one; it goes to the agent, and the card steps back to grey — present,\n * holding what you are looking at, not itself the thing you are looking at.\n * Reversing that (blue card, blue row inside it) says nothing, which is what a\n * blue-on-blue card looked like.\n *\n * A filled card does **not** also answer hover: the fill is already spent, and a\n * selected row that lightened under the pointer read as a third state nobody\n * could name. Its steps still do, on `--row-active`, which is a tint and so\n * works on all three grounds.\n *\n * A **task** never takes the blue. It is a reference to a place inside a\n * session, so pressing one selects the session and travels to its marker — see\n * `StepRow`.\n */\nexport interface SessionItemProps {\n row: SessionRow\n /** This session is the one the host is showing. */\n active?: boolean\n /**\n * Which sub-agent is open, by `Step.key` — the *finer* of the two selections.\n * When it names one of this card's agents, that step takes the blue and the\n * card drops to the secondary grey. A key belonging to some other card's\n * agent, or to a task, changes nothing here.\n */\n activeStepKey?: string\n /** Shown when the list is not already grouped by gateway. */\n showGateway?: boolean\n /**\n * False when the list is already grouped by project — the header above has\n * said the name, so the card must not spend its metadata line repeating it.\n * What takes the slot is the *sub-path inside the project*\n * (`projectSubpath`): `packages/ui`, `packages/server` — the one thing the\n * header cannot say and the only thing that tells two sessions in one repo\n * apart. A session at the project root has nothing to add and the slot\n * disappears entirely. Exactly the rule `showGateway` follows one facet over.\n */\n showProject?: boolean\n /** Resolved project-icon bytes by content hash. A hash this map has not got\n * yet simply draws no icon. */\n projectIcons?: Record<string, string>\n /** Uncontrolled by default — pass both to drive expansion from the host. */\n expanded?: boolean\n onExpandedChange?: (expanded: boolean) => void\n onSelect?: () => void\n /** Open the session *at* one of its sub-agents — hand the panel body over to\n * that agent's own work. Only ever called for an **agent** step. Absent is not\n * a missing feature: without it a step just opens the session, which is all a\n * host with no sub-agent surface can offer. */\n onSelectSubagent?: (toolUseId: string) => void\n /**\n * Open the session and travel to a **task**'s row in the transcript — where\n * the work was started, and where it finished.\n *\n * A separate seam from `onSelectSubagent` because the destinations are\n * different, and conflating them is not a styling detail: a task has no agent\n * behind it, so framing its tool-use id selects **no items at all** and the\n * panel draws an empty agent view. That was the bug. A task is a reference,\n * and the only honest thing to do with a reference is follow it.\n */\n onRevealStep?: (toolUseId: string) => void\n /** Enables in-place rename. The trigger is the host's — see `renameOn`. */\n onRename?: (title: string) => void\n /**\n * How a rename starts. `doubleClick` is the editor feel (a rename is a thing\n * you do to the word you are looking at); `external` means the host opens it\n * from its own affordance — a menu entry, a pencil in `actions` — and drives\n * it through `editing`/`onEditingChange`.\n */\n renameOn?: 'doubleClick' | 'external'\n editing?: boolean\n onEditingChange?: (editing: boolean) => void\n /** The card's trailing controls, at the end of line two. A single overflow\n * glyph is what the design draws; a host with three always-on actions can put\n * them here instead. */\n actions?: ReactNode\n className?: string\n}\n\nexport function SessionItem({\n row,\n active = false,\n activeStepKey,\n showGateway,\n showProject = true,\n projectIcons,\n expanded,\n onExpandedChange,\n onSelect,\n onSelectSubagent,\n onRevealStep,\n onRename,\n renameOn = 'doubleClick',\n editing,\n onEditingChange,\n actions,\n className,\n}: SessionItemProps) {\n const { info } = row\n // Expansion is the card's own state unless the host takes it, and is\n // deliberately not persisted: a list that reopened yesterday's work on every\n // reload would be showing a settled tail nobody asked for.\n const [ownExpanded, setOwnExpanded] = useState(false)\n const open = expanded ?? ownExpanded\n const setOpen = (next: boolean) => {\n setOwnExpanded(next)\n onExpandedChange?.(next)\n }\n const [ownEditing, setOwnEditing] = useState(false)\n const isEditing = editing ?? ownEditing\n const setEditing = (next: boolean) => {\n setOwnEditing(next)\n onEditingChange?.(next)\n }\n\n const engine = info.engine ?? 'claude'\n // protocol's own spelling, so this card, the group header above it and the\n // project facet cannot disagree about what a project is called.\n const project = showProject ? projectLabel(row) : projectSubpath(row)\n const projectIcon = showProject ? info.project?.icon : undefined\n const iconSrc = projectIcon?.type === 'image' ? projectIcons?.[projectIcon.hash] : undefined\n // Everything between the project and the age. Plain strings, because none of\n // them wears a colour or an icon — the two that do are drawn as their own\n // cells either side of this run.\n //\n // A session that has spent nothing contributes **nothing**, rather than\n // `formatCost`'s em-dash. A dash is the right answer in a details panel, where\n // the row exists to be read and an empty cell would look broken; here it is a\n // segment of a `·`-joined run competing for the width the project name needs,\n // and `· — ·` is punctuation pretending to be a reading.\n const cost = formatCost(info.totalCostUsd)\n const extras = [\n showGateway ? row.hostName : undefined,\n info.profile ? `@${info.profile}` : undefined,\n cost === '—' ? undefined : cost,\n ].filter((part): part is string => Boolean(part))\n\n // The metadata run, assembled as a **list of present parts** rather than as a\n // template with conditionals in it. Every one of these is genuinely optional —\n // a session reports no model until its first turn, a session outside any\n // declared project has no name to show, a session at its project's root has no\n // sub-path — and a template makes each absence a separate place to remember\n // the separator. Built as a list, the separator is drawn *between* parts and\n // there is exactly one rule to get right.\n //\n // This is not hypothetical tidiness: `friendlyModel(undefined)` is `undefined`,\n // so a session with no model yet drew an empty span followed by the project's\n // leading `· `, and the line opened with a separator attached to nothing.\n const model = friendlyModel(info.model)\n const parts: ReactNode[] = []\n if (model) {\n parts.push(\n // The model id as a person says it — `claude-opus-5[1m]` is a wire value,\n // and a card line has no room for a context-window suffix.\n <span key='model' className={vendorTextClass(engine, info.model)}>\n {model}\n </span>,\n )\n }\n if (project !== undefined) {\n parts.push(\n <span key='project'>\n <ProjectIcon\n icon={projectIcon}\n src={iconSrc}\n name={project}\n /* 16px, the same box as the engine mark in the gutter, and nudged onto\n the text baseline: the box is taller than the line, so it sits a\n hair proud without this. */\n className='mr-1.5 size-4 align-[-0.3em]'\n />\n {project}\n </span>,\n )\n }\n for (const extra of extras) parts.push(<span key={extra}>{extra}</span>)\n // Where a step press goes, by kind. An **agent** has work of its own and gets\n // the frame; a **task** is a marker and gets travelled to. Both fall back to\n // plainly opening the session, which is the whole of what a host that can do\n // neither has to offer — and is still better than a destination that renders\n // empty.\n const steps = sessionSteps(info, (toolUseId, kind) => {\n if (kind === 'agent') return onSelectSubagent ? onSelectSubagent(toolUseId) : onSelect?.()\n return onRevealStep ? onRevealStep(toolUseId) : onSelect?.()\n })\n // Whether the thing on screen is one of THIS card's sub-agents. Matched\n // against the card's own steps rather than taken on trust from `activeStepKey`\n // alone: every card in the list is handed the same key, and a bare truthiness\n // check would turn all of them grey the moment any one agent opened. Tasks are\n // excluded because a task is a place to go, not a thing to hold — see\n // `StepRow`.\n const holdsOpenAgent = steps.some((s) => s.kind === 'agent' && s.key === activeStepKey)\n\n return (\n <div\n data-slot='session-item'\n data-active={active || undefined}\n role='button'\n tabIndex={0}\n /* Only the FIRST click of a click-streak selects. Selecting reveals the\n session and focuses its composer, so the second click of a double-click\n would steal focus a beat after the rename editor mounted — the editor\n appearing and vanishing in the same gesture. `detail` counts the\n streak; anything past the first is the double-click the title is\n listening for. */\n onClick={(e) => {\n if (e.detail > 1 || isEditing) return\n onSelect?.()\n }}\n onKeyDown={(e) => {\n // Only when the card ITSELF has focus. A press on something inside it —\n // the disclosure, a step, the overflow — already fires that control's\n // own click, and `stopPropagation` there cannot help: the keydown is a\n // separate event travelling the same path, so an unguarded handler ran\n // the control's action AND selected the session.\n if (e.target !== e.currentTarget) return\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onSelect?.()\n }\n }}\n className={cn(\n 'group flex w-full cursor-pointer flex-col p-1 text-left outline-none',\n 'rounded-[4px] transition-colors focus-visible:ring-2 focus-visible:ring-ring',\n // Three states, one place, in priority order. A sub-agent being open\n // OUTRANKS the session being selected, because both are true at once —\n // opening an agent selects its session too — and the blue can only mean\n // one of them. It goes to the finer claim; the card keeps the coarser\n // one in grey. A filled card does not also answer hover: the fill is\n // already spent, and a selected row that lightened under the pointer\n // read as a third state nobody could name.\n holdsOpenAgent\n ? 'bg-row-selected-weak'\n : active\n ? 'bg-row-selected'\n : 'hover:bg-row-hover',\n className,\n )}>\n <div className='flex flex-col gap-1 py-0.5 pr-0.5 pl-1.5'>\n {/* Line one — state, name, and the two readings that keep moving.\n\n **6px between cells, where the column between the lines is 4px.**\n Not a rounding wobble: a horizontal gap separates a glyph from the\n words it labels, and 4px put the two close enough to read as one\n smudge at 13px; a vertical gap separates two lines that are already\n separated by their own leading, so it needs less. The design was\n redrawn to this once the vendor marks lost the transparent padding\n that had been standing in for the missing space. */}\n <div className='flex h-5 items-center gap-1.5 overflow-hidden'>\n <Gutter>\n <SessionStatusIcon row={row} />\n </Gutter>\n {isEditing && onRename ? (\n /* The editor replaces the title rather than sitting inside it — an\n input nested in a button is invalid markup, and disabling the\n button to protect the edit disables the field with it. */\n <NameEditor\n initial={info.title ?? ''}\n onCommit={(title) => {\n setEditing(false)\n if (title !== (info.title ?? '')) onRename(title)\n }}\n onCancel={() => setEditing(false)}\n />\n ) : (\n <span\n onDoubleClick={\n onRename && renameOn === 'doubleClick'\n ? (e) => {\n e.stopPropagation()\n setEditing(true)\n }\n : undefined\n }\n className='min-w-0 flex-1 truncate text-body-sm font-medium tracking-[-0.005em] text-fg-1'>\n {sessionLabel(info)}\n </span>\n )}\n {row.unseen > 0 ? (\n /* Transcript rows since this session was last on screen — the same\n unit the VS Code activity-bar badge counts, because turns\n undercount badly.\n\n **The colour is the state's, not the count's.** On a live session\n unread is a call to look, and it wears the accent. On a settled\n one the same number is a *record* — the turn is over, nothing is\n going to arrive, and there is nothing to answer — so it drops to\n the neutral badge. A list where every finished session still\n shouted in blue is a list where the blue stopped meaning\n anything, which is the whole reason the design draws two. */\n <span\n title={`${row.unseen} new`}\n className={cn(\n 'flex h-4 min-w-6 shrink-0 items-center justify-center rounded-full px-2',\n 'text-[0.75rem] leading-none tracking-[-0.005em] tabular-nums',\n row.state === 'working' || row.state === 'attention'\n ? 'bg-accent text-accent-fg'\n : 'bg-badge text-badge-fg',\n )}>\n {row.unseen}\n </span>\n ) : null}\n {/* How full the window is, at a glance and across the whole list — the\n question you cannot ask from inside one session. Absent draws\n nothing, and absent is not zero. */}\n <ContextRing usage={info.contextUsage} size={16} className='p-0.5' />\n </div>\n\n {/* Line two — what it is, where it runs, what it cost, how old it is. */}\n <div className='flex h-5 items-center gap-1.5 overflow-hidden text-body-sm tracking-[-0.005em]'>\n <Gutter>\n {/* The colour goes ON the icon, not on a parent: the svg ships its\n own `text-fg-3` and only a class on the element itself merges\n over it. `cn` is tailwind-merge, so passing one replaces the\n default rather than losing to it. */}\n <EngineIcon\n engine={engine}\n model={info.model}\n className={cn('size-4', vendorMarkClass(engine, info.model))}\n />\n </Gutter>\n {/* **One truncating run and one atom, not five cells and not one\n span**, and both halves of that are load-bearing.\n\n The run is one span because its parts have a priority order —\n model, then project, then where it ran and what it cost — and a\n single ellipsis honours it for free. As five flex children they\n cannot: `shrink-0` everywhere clips mid-glyph (`$12.4` cut in\n half, a project name gone entirely behind its own icon), and\n letting them shrink costs each of them a little and leaves four\n half-words with no ellipsis anywhere. The project glyph rides\n inside as an inline element for the same reason — it clips with\n the text it labels instead of holding a slot that text has already\n lost.\n\n **The age is not in the run.** It sat at the end of it and was\n therefore the first thing an ellipsis ate, which is precisely\n backwards: `4m ago` is three characters that answer \"is this\n still moving\", and a truncated `4m …` answers nothing while a\n truncated project name still says which repo. So it is its own\n shrink-0 cell and the run yields to it. That is one ellipsis in\n one place, which is the whole reason not to use five cells. */}\n <span className='min-w-0 truncate text-fg-4'>\n {/* Separators live BETWEEN parts and are never attached to one.\n Attached, a part that turned out to be absent left its own\n separator behind — which is what a session with no model\n recorded yet did: `friendlyModel(undefined)` is `undefined`, the\n model drew nothing, and the line opened with a `· ` in front of\n the project, hanging off nothing. */}\n {parts.map((part, i) => (\n <Fragment key={i}>\n {i > 0 ? ' · ' : ''}\n {part}\n </Fragment>\n ))}\n </span>\n <span className='shrink-0 text-fg-4'>\n {parts.length > 0 ? '· ' : ''}\n {formatRelativeTime(info.lastActivityAt ?? info.createdAt)}\n </span>\n {/* The slack goes HERE, after the age — not into the identity run.\n Giving the run `flex-1` made it absorb the surplus, which pushed\n the age to the far right on a card with no disclosure and left a\n hole between a project name and its own timestamp. The design\n draws the same spacer in the same place for the same reason: the\n run and the age are one reading, and what floats is the gap before\n the controls. Basis 0, so it only ever takes *surplus* — under a\n deficit it is worth nothing and the run truncates instead. */}\n <span className='min-w-0 flex-1' />\n {steps.length > 0 ? (\n <StepToggle\n expanded={open}\n running={runningSteps(steps)}\n total={steps.length}\n noun={steps[0]!.noun}\n onToggle={() => setOpen(!open)}\n />\n ) : null}\n {actions}\n </div>\n </div>\n\n {open && steps.length > 0 ? (\n <div className='flex flex-col'>\n {steps.map((step: Step) => (\n <StepRow\n key={step.key}\n step={step}\n active={step.key === activeStepKey}\n onSelect={step.onSelect}\n />\n ))}\n </div>\n ) : null}\n </div>\n )\n}\n\n/**\n * The one 16px cell both lines hang their glyph in.\n *\n * A shared column rather than a leading character: the state and the engine mark\n * are different widths and a bare glyph would let the two text columns start at\n * different x. Centring inside a fixed cell is what makes them agree.\n */\nfunction Gutter({ children }: { children: ReactNode }) {\n return <span className='flex size-4 shrink-0 items-center justify-center'>{children}</span>\n}\n\n/**\n * Enter commits, Escape cancels, blur commits — the editor's inline-rename feel.\n * An empty value is a deliberate \"clear the name\", which the gateway answers by\n * handing back the derived title.\n *\n * The blur rule needs one guard. Selecting a session focuses a different surface\n * — in the extension, a different VS Code view — so this document can lose focus\n * a tick after a double-click opened the editor, and an unguarded blur reads\n * that as \"the user clicked away\" and closes the editor in the frame it\n * appeared. `document.hasFocus()` tells the two apart: focus moving WITHIN this\n * document is the user leaving the field; the whole view losing focus is not.\n * When the window comes back, so does the caret.\n */\nfunction NameEditor({\n initial,\n onCommit,\n onCancel,\n}: {\n initial: string\n onCommit: (title: string) => void\n onCancel: () => void\n}) {\n const [value, setValue] = useState(initial)\n const ref = useRef<HTMLInputElement>(null)\n useEffect(() => {\n ref.current?.focus()\n ref.current?.select()\n }, [])\n useEffect(() => {\n const onWindowFocus = () => {\n ref.current?.focus()\n ref.current?.select()\n }\n window.addEventListener('focus', onWindowFocus)\n return () => window.removeEventListener('focus', onWindowFocus)\n }, [])\n return (\n <input\n ref={ref}\n value={value}\n spellCheck={false}\n placeholder='Session name'\n aria-label='Session name'\n onClick={(e) => e.stopPropagation()}\n onDoubleClick={(e) => e.stopPropagation()}\n onChange={(e) => setValue(e.target.value)}\n onBlur={() => {\n if (document.hasFocus()) onCommit(value.trim())\n }}\n onKeyDown={(e) => {\n e.stopPropagation()\n if (e.key === 'Enter') onCommit(value.trim())\n else if (e.key === 'Escape') onCancel()\n }}\n /* The negative margin is load-bearing: the border (1px) and padding (1px)\n make the input taller than the label it replaces, and the card would\n grow by that much the moment a rename starts. Cancelling it exactly is\n what keeps the list from shifting under the caret. */\n className={cn(\n '-my-0.5 min-w-0 flex-1 rounded-sm border border-ring bg-bg px-1 py-px',\n 'text-body-sm leading-5 text-fg-1 outline-none',\n )}\n />\n )\n}\n","import { useMemo, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { Eraser, Layers, Pencil, Search, SearchX, Trash2, X } from 'lucide-react'\nimport {\n STATE_LABELS,\n STATE_ORDER,\n adaptersOf,\n clearFilters,\n filterRows,\n groupRows,\n hasFacetFilter,\n projectsOf,\n subsetSummary,\n} from '@workerdeck/protocol'\nimport type {\n GroupBy,\n SessionRow,\n SessionState,\n SortBy,\n ViewConfig,\n WorkspaceScope,\n} from '@workerdeck/protocol'\nimport { Button } from '../ui/Button.tsx'\nimport { Empty } from '../ui/Empty.tsx'\nimport { Input } from '../ui/Input.tsx'\nimport { Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue } from '../ui/Select.tsx'\nimport { ProjectIcon } from './ProjectIcon.tsx'\nimport { SessionItem } from './SessionItem.tsx'\nimport { cn } from '../../lib/utils.ts'\n\n// The state glyph moved to its own module when the card did — the extension's\n// list draws it too, and this file re-exports it only so the package's public\n// surface does not shift under a host that imports it from here.\nexport { SessionStatusIcon } from './SessionStatusIcon.tsx'\n\n/**\n * A sessions list with the affordances a list of thirty needs: search, facets,\n * grouping, sorting, unread counts, and one honest line about what is hidden.\n *\n * The *rules* are `@workerdeck/protocol`'s (`filterRows`/`groupRows`/\n * `subsetSummary`), not this component's — the VS Code sidebar renders the same\n * model with workbench chrome, its activity-bar badge counts the same rows this\n * would show, and iOS mirrors them in Swift. What lives here is the styled\n * rendering of that model, so a host that wants the dashboard's look gets it\n * without reimplementing the model behind it.\n *\n * `SessionList` remains beside this for the plain case (a fixed set of rows, no\n * controls); this is what you reach for when the list is the screen.\n */\n\nexport interface SessionBrowserProps {\n rows: SessionRow[]\n config: ViewConfig\n onConfigChange: (config: ViewConfig) => void\n /** The host's own folders, if it has such a notion. Absent — a dashboard, a\n * phone — makes the scope filter genuinely inert rather than empty. */\n scope?: WorkspaceScope\n activeId?: string\n /**\n * Which sub-agent the host currently has open, by tool-use id — the *finer*\n * of the two selections. The card holding it drops to the secondary grey and\n * that step takes the blue; see `SessionItem`. A host with no sub-agent\n * surface leaves it undefined and nothing changes.\n */\n activeSubagentId?: string\n onSelect?: (row: SessionRow) => void\n onDelete?: (row: SessionRow) => void\n /**\n * Rename, from the row's pencil. Empty string restores the derived title. A\n * gateway edit (`PATCH /sessions/:id`), never a local override — every client\n * should see the same name. Omit to make titles read-only.\n *\n * A hover affordance rather than the extension's double-click-the-title,\n * because here a single click on the row navigates: the *first* click of a\n * double-click would have already left the page.\n */\n onRename?: (row: SessionRow, title: string) => void\n /**\n * Clear the conversation in place, from the row's eraser. Same session, empty\n * context — **not** a delete: the engine keeps the old conversation and it\n * stays resumable, which is why the row's copy never says \"deleted\".\n *\n * Rendered only where the session's own capability record allows it\n * (`capabilities.clearContext`, absent = false), so a row that cannot be\n * cleared has no eraser rather than one that errors. Omit the prop to have\n * none at all — a host with no way to send a session command has nothing to\n * hang it on (this is a WS command, not a REST route).\n */\n onClearContext?: (row: SessionRow) => void\n /** Open a session *at* one of its sub-agents, when the host can scroll a\n * transcript to a `Task` row. Absent, the sub-agent list still expands and a\n * step just opens its session — see `SessionRowItemProps`. */\n onSelectSubagent?: (row: SessionRow, toolUseId: string) => void\n /** Open a session and travel to one of its **tasks** — see\n * `SessionItem.onRevealStep`. A task has no agent behind it, so it is a place\n * to go rather than a thing to open, and framing it as an agent drew an empty\n * view. */\n onRevealStep?: (row: SessionRow, toolUseId: string) => void\n /** Rendered when nothing at all exists (as opposed to nothing matching). */\n emptyState?: React.ReactNode\n /**\n * Whether the search + facet bar is shown. Defaults to `true` — a list that\n * *is* the screen shows its controls.\n *\n * A host with somewhere better to put the toggle (a view title bar) passes\n * `false` and owns the boolean itself, the way the VS Code extension does: the\n * key lives where the commands do. Two rules come with it, and they are why\n * this hides only the bar and nothing else — **closing the bar never clears\n * the filters**, and the subset line below it renders either way, so a list\n * filtered by a control you can't currently see still says so.\n */\n showControls?: boolean\n /**\n * Resolved project-icon bytes by content hash — `useProjectIcons`' output.\n *\n * Passed in rather than fetched here for the reason `ProjectIcon` states: the\n * wire carries an *address*, and who can fetch it differs per client. Absent,\n * or a hash not in it yet, simply draws no picture; the project's name is\n * already there.\n */\n projectIcons?: Record<string, string>\n className?: string\n}\n\n/**\n * How a list row is drawn, in one place, so `SidebarRow` in `web` matches this\n * exactly rather than approximating it — the dashboard's other three sidebars\n * are that component, and a sessions list that hovered differently from the\n * gateways list beside it would read as a different product.\n *\n * Two rules are load-bearing:\n *\n * - **Fill means hover, and only hover.** It stays on the row whether or not\n * the row is selected, because a selected row still has to answer the\n * pointer. Selection gets the gutter instead.\n * - **`ml-0` on the selected row is not cosmetic.** It hands the accent border\n * the 4px the margin was holding, so the text does not shift sideways as a\n * row becomes the selected one. The squared left corners are what let the bar\n * sit flush against the sidebar edge.\n */\nexport function rowShapeClass(active: boolean): string {\n return cn(\n 'px-2 py-1.5 hover:bg-row-hover',\n active ? 'mr-1 ml-0 rounded-r-md border-l-4 border-l-accent' : 'mx-1 rounded-md',\n )\n}\n\nexport function SessionBrowser({\n rows,\n config,\n onConfigChange,\n scope,\n activeId,\n activeSubagentId,\n onSelect,\n onDelete,\n onRename,\n onClearContext,\n onSelectSubagent,\n onRevealStep,\n emptyState,\n showControls = true,\n projectIcons,\n className,\n}: SessionBrowserProps) {\n const visible = useMemo(() => filterRows(rows, config, scope), [rows, config, scope])\n const groups = useMemo(() => groupRows(visible, config), [visible, config])\n const subset = subsetSummary(config, scope, visible.length, rows.length)\n // Derived, not enumerated: a new engine or a new gateway needs no change here,\n // and a facet with one possible value is not a choice worth showing.\n const adapters = useMemo(() => adaptersOf(rows), [rows])\n const projects = useMemo(() => projectsOf(rows), [rows])\n const gateways = useMemo(() => {\n const seen = new Map<string, string>()\n for (const row of rows) seen.set(row.hostId, row.hostName)\n return [...seen].map(([id, name]) => ({ id, name }))\n }, [rows])\n\n const set = (patch: Partial<ViewConfig>) => onConfigChange({ ...config, ...patch })\n\n return (\n <div data-slot='session-browser' className={cn('flex flex-col gap-3', className)}>\n {/* One control per row, label left, input right — the shape VS Code uses\n for its own filter surfaces. A wrapping row of pill-shaped selects\n reflows into an unpredictable number of lines as facets appear and\n disappear; a column of labelled rows is the same height every time and\n reads at sidebar width, which is the only width this has. */}\n <div className={cn('flex flex-col gap-1.5 px-2', !showControls && 'hidden')}>\n <div className='relative'>\n <Search className='pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-fg-4' />\n <Input\n value={config.search}\n onChange={(e) => set({ search: e.target.value })}\n placeholder='Search sessions'\n aria-label='Search sessions'\n className='pl-8'\n />\n </div>\n <FilterRow label='State'>\n <FacetSelect\n label='State'\n value={config.states}\n options={STATE_ORDER.map((s) => ({ value: s, label: STATE_LABELS[s] }))}\n onChange={(states) => set({ states: states as SessionState[] })}\n />\n </FilterRow>\n {adapters.length > 1 ? (\n <FilterRow label='Engine'>\n <FacetSelect\n label='Engine'\n value={config.adapters}\n options={adapters.map((a) => ({ value: a, label: a }))}\n onChange={(adapters) => set({ adapters })}\n />\n </FilterRow>\n ) : null}\n {gateways.length > 1 ? (\n <FilterRow label='Gateway'>\n <FacetSelect\n label='Gateway'\n value={config.gateways}\n options={gateways.map((g) => ({ value: g.id, label: g.name }))}\n onChange={(gateways) => set({ gateways })}\n />\n </FilterRow>\n ) : null}\n {projects.length > 1 ? (\n <FilterRow label='Project'>\n <FacetSelect\n label='Project'\n value={config.projects ?? []}\n options={projects.map((p) => ({ value: p.key, label: p.label }))}\n onChange={(next) => set({ projects: next })}\n />\n </FilterRow>\n ) : null}\n <FilterRow label='Group'>\n <OneOfSelect\n label='Group'\n value={config.groupBy}\n options={[\n { value: 'none', label: 'No grouping' },\n { value: 'state', label: 'By state' },\n { value: 'adapter', label: 'By engine' },\n ...(projects.length > 1 ? [{ value: 'project' as const, label: 'By project' }] : []),\n ...(gateways.length > 1 ? [{ value: 'gateway' as const, label: 'By gateway' }] : []),\n ]}\n onChange={(groupBy) => set({ groupBy: groupBy as GroupBy })}\n />\n </FilterRow>\n <FilterRow label='Sort'>\n <OneOfSelect\n label='Sort'\n value={config.sortBy}\n options={[\n { value: 'recent', label: 'Recent' },\n { value: 'name', label: 'Name' },\n { value: 'state', label: 'State' },\n ...(projects.length > 1 ? [{ value: 'project' as const, label: 'Project' }] : []),\n ...(gateways.length > 1 ? [{ value: 'gateway' as const, label: 'Gateway' }] : []),\n ]}\n onChange={(sortBy) => set({ sortBy: sortBy as SortBy })}\n />\n </FilterRow>\n </div>\n\n {subset ? (\n <div className='flex items-center gap-2 px-3 text-label text-fg-4'>\n <span>\n {subset.shown} of {subset.total}\n {subset.causes.length ? ` · ${subset.causes.join(' · ')}` : null}\n </span>\n <button\n type='button'\n className='text-fg-3 underline underline-offset-2 hover:text-fg-1'\n onClick={() => onConfigChange(clearFilters(config))}>\n Show all\n </button>\n </div>\n ) : null}\n\n {rows.length === 0 ? (\n (emptyState ?? <Empty icon={<Layers />} title='No sessions yet' />)\n ) : visible.length === 0 ? (\n // Two different dead ends, two different ways out. \"Nothing matches\" is\n // a filter someone set; anything else is the state of the world — and\n // only the first has a button, because an action that does nothing is\n // worse than none.\n hasFacetFilter(config) ? (\n <Empty\n icon={<SearchX />}\n title='No matches'\n description='No session matches the current search and filters.'\n action='Clear filters'\n onAction={() => onConfigChange(clearFilters(config))}\n />\n ) : (\n <Empty icon={<Layers />} title='Nothing here' description='No session to show.' />\n )\n ) : (\n /* The list's own 4px inset, as **padding on the list** rather than\n margins on the cards. `SessionItem` is `w-full` — it fills the slot it\n is given, which is what lets a host size it — and `w-full` plus\n `mx-1` is `100% + 8px`: an overflow by construction, which is exactly\n what it did. The cards ran past the sidebar's right edge and the\n column grew a horizontal scrollbar. Padding here cannot do that,\n whatever the card's own width rule turns out to be. */\n <div className='flex flex-col gap-4 px-1'>\n {groups.map((group) => (\n <div key={group.key} className='flex flex-col gap-1'>\n {config.groupBy !== 'none' && group.label ? (\n <div className='flex items-center gap-2 px-2 text-label font-medium text-fg-4'>\n {/* Only the project facet has a mark of its own, and a group\n IS one project root, so the first row is a fair source. */}\n {config.groupBy === 'project' ? (\n <ProjectIcon\n icon={group.rows[0]?.info.project?.icon}\n src={iconSrcOf(group.rows[0], projectIcons)}\n name={group.label}\n />\n ) : null}\n <span className='uppercase tracking-wide'>{group.label}</span>\n <span className='text-fg-4/70'>{group.rows.length}</span>\n </div>\n ) : null}\n {group.rows.map((row) => (\n <SessionRowItem\n key={`${row.hostId}:${row.info.id}`}\n row={row}\n active={row.info.id === activeId}\n activeSubagentId={activeSubagentId}\n showGateway={gateways.length > 1 && config.groupBy !== 'gateway'}\n showProject={config.groupBy !== 'project'}\n projectIcons={projectIcons}\n onSelect={onSelect}\n onDelete={onDelete}\n onRename={onRename}\n onClearContext={onClearContext}\n onSelectSubagent={onSelectSubagent}\n onRevealStep={onRevealStep}\n />\n ))}\n </div>\n ))}\n </div>\n )}\n </div>\n )\n}\n\n/** The bytes for a row's project icon, if it has an image one and the caller has\n * fetched it yet. Shared by the row and its group header so the two cannot draw\n * different pictures for one project. */\nfunction iconSrcOf(\n row: SessionRow | undefined,\n icons: Record<string, string> | undefined,\n): string | undefined {\n const icon = row?.info.project?.icon\n return icon?.type === 'image' ? icons?.[icon.hash] : undefined\n}\n\ninterface SessionRowItemProps {\n row: SessionRow\n active?: boolean\n /** See `SessionBrowserProps.activeSubagentId`. */\n activeSubagentId?: string\n showGateway?: boolean\n /** False when the list is already grouped by project — the header has said the\n * name, so the row must not spend its metadata line repeating it. What takes\n * the slot is the *sub-path inside the project* (`projectSubpath`), which is\n * the one thing the header cannot say and the only thing that tells two\n * sessions in the same repo apart; a session sitting at the project root has\n * nothing to add and the slot disappears entirely. The rule `showGateway`\n * follows one facet over. */\n showProject?: boolean\n projectIcons?: Record<string, string>\n onSelect?: (row: SessionRow) => void\n onDelete?: (row: SessionRow) => void\n onRename?: (row: SessionRow, title: string) => void\n /** See `SessionBrowserProps.onClearContext` — gated on the row's own\n * capability record, not on the engine name. */\n onClearContext?: (row: SessionRow) => void\n /** Open one of a session's sub-agents — which now means handing the session\n * panel over to that agent's own work (`SessionPanel.openSubagent`), and meant\n * \"scroll to its `Task` row\" before that surface existed. Both are honest; a\n * host picks what it can draw. Optional, and its absence is not a missing\n * feature: without it a step just opens the session, which is all a host with\n * neither can offer. Only *agent* steps ever call it — see `Step.kind`. */\n onSelectSubagent?: (row: SessionRow, toolUseId: string) => void\n /** See `SessionBrowserProps.onRevealStep`. */\n onRevealStep?: (row: SessionRow, toolUseId: string) => void\n}\n\nfunction SessionRowItem({\n row,\n active,\n activeSubagentId,\n showGateway,\n showProject = true,\n projectIcons,\n onSelect,\n onDelete,\n onRename,\n onClearContext,\n onSelectSubagent,\n onRevealStep,\n}: SessionRowItemProps) {\n const { info } = row\n // The rename affordance here is a pencil, not the card's own double-click:\n // the dashboard already spends the row's hover on two other actions, and a\n // gesture that only one of the three responds to is a gesture nobody finds.\n // So the trigger is `external` and this owns the flag.\n const [editing, setEditing] = useState(false)\n\n return (\n <SessionItem\n row={row}\n active={active === true}\n activeStepKey={activeSubagentId}\n showGateway={showGateway}\n showProject={showProject}\n projectIcons={projectIcons}\n onSelect={() => onSelect?.(row)}\n onSelectSubagent={onSelectSubagent ? (id) => onSelectSubagent(row, id) : undefined}\n onRevealStep={onRevealStep ? (id) => onRevealStep(row, id) : undefined}\n onRename={onRename ? (title) => onRename(row, title) : undefined}\n renameOn='external'\n editing={editing}\n onEditingChange={setEditing}\n actions={\n <>\n {onRename && !editing ? (\n <RowAction\n label='Rename session'\n onClick={() => setEditing(true)}>\n <Pencil className='size-3 text-fg-3' />\n </RowAction>\n ) : null}\n {/* Gated on the row's own capability record, not on the engine name —\n an engine that grows the ability gets the button with no change\n here, and one that loses it stops offering a control that fails. */}\n {onClearContext && info.capabilities?.clearContext ? (\n <RowAction\n label='Clear context'\n title='Clear the conversation — the session keeps running and the old conversation stays resumable'\n onClick={() => onClearContext(row)}>\n <Eraser className='size-3 text-fg-3' />\n </RowAction>\n ) : null}\n {onDelete ? (\n <RowAction label='Close session' onClick={() => onDelete(row)}>\n <Trash2 className='size-3 text-fg-3' />\n </RowAction>\n ) : null}\n </>\n }\n />\n )\n}\n\n/**\n * One of the dashboard's hover actions.\n *\n * Hover-revealed rather than always on, which is the opposite of the\n * extension's single overflow glyph and the right call for each: there are\n * three of them here and a sidebar card has room for one, so the extension\n * spends that room on a menu and this spends the row's hover on the actions\n * themselves. Both stop the click — the whole card is pressable underneath, and\n * an action that also selected the session would do two things per press.\n */\nfunction RowAction({\n label,\n title,\n onClick,\n children,\n}: {\n label: string\n title?: string\n onClick: () => void\n children: ReactNode\n}) {\n return (\n <Button\n variant='ghost'\n size='icon-sm'\n aria-label={label}\n title={title ?? label}\n className='size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100'\n onClick={(e) => {\n e.stopPropagation()\n onClick()\n }}>\n {children}\n </Button>\n )\n}\n\n\n/** A multi-select facet. Empty = no filter, which is why the trigger reads the\n * facet's name rather than \"All\": nothing is being excluded. */\n/**\n * One labelled control row. The label column is fixed so every control starts\n * on the same x — the thing that makes a stack of them read as a form rather\n * than as five unrelated widgets.\n */\nfunction FilterRow({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className='flex items-center gap-2'>\n <span aria-hidden className='w-14 shrink-0 truncate text-label text-fg-3'>\n {label}\n </span>\n <div className='min-w-0 flex-1'>{children}</div>\n </div>\n )\n}\n\nfunction FacetSelect({\n label,\n value,\n options,\n onChange,\n}: {\n label: string\n value: string[]\n options: { value: string; label: string }[]\n onChange: (value: string[]) => void\n}) {\n return (\n <div className='flex items-center gap-1'>\n <Select multiple value={value} onValueChange={(v) => onChange(v as string[])}>\n <SelectTrigger aria-label={label} className='min-w-0 flex-1'>\n <SelectValue>\n {/* \"All\" rather than the label, which the row already carries. */}\n {value.length === 0\n ? 'All'\n : value.length === 1\n ? (options.find((o) => o.value === value[0])?.label ?? 'All')\n : `${value.length} selected`}\n </SelectValue>\n </SelectTrigger>\n <SelectContent>\n {options.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n <SelectItemText>{option.label}</SelectItemText>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {value.length > 0 ? (\n <Button variant='ghost' size='icon-sm' aria-label={`Clear ${label}`} onClick={() => onChange([])}>\n <X className='size-3 text-fg-4' />\n </Button>\n ) : null}\n </div>\n )\n}\n\nfunction OneOfSelect({\n label,\n value,\n options,\n onChange,\n}: {\n label: string\n value: string\n options: readonly { value: string; label: string }[]\n onChange: (value: string) => void\n}) {\n return (\n <Select value={value} onValueChange={(v) => onChange(v as string)}>\n <SelectTrigger aria-label={label} className='w-full min-w-0'>\n <SelectValue>{options.find((o) => o.value === value)?.label ?? label}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {options.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n <SelectItemText>{option.label}</SelectItemText>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["AlertDialogPrimitive","SonnerToaster","Map"],"sources":["../src/components/ui/Card.tsx","../src/components/ui/Textarea.tsx","../src/components/ui/AlertDialog.tsx","../src/components/ui/Sonner.tsx","../src/components/ui/Empty.tsx","../src/components/prompt-area/segment-helpers.ts","../src/components/terminal/StatusLine.tsx","../src/components/agent/SessionList.tsx","../src/components/agent/ProjectIcon.tsx","../src/components/agent/ContextRing.tsx","../src/components/agent/EngineIcon.tsx","../src/components/agent/SessionStatusIcon.tsx","../src/components/agent/SessionSteps.tsx","../src/components/agent/SessionItem.tsx","../src/components/agent/SessionBrowser.tsx"],"sourcesContent":["import { type HTMLAttributes } from 'react'\nimport { cn } from '../../lib/utils.ts'\n\nexport function Card({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div data-slot=\"card\" className={cn('rounded-lg border border-border bg-surface', className)} {...props} />\n}\n\nexport function CardHeader({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div className={cn('flex items-start justify-between gap-3 px-4 pt-3.5 pb-3', className)} {...props} />\n}\n\nexport function CardTitle({ className, ...props }: HTMLAttributes<HTMLHeadingElement>) {\n return <h3 className={cn('text-heading-3 font-semibold text-text', className)} {...props} />\n}\n\nexport function CardContent({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div className={cn('px-4 pb-4', className)} {...props} />\n}\n","import { type TextareaHTMLAttributes, forwardRef } from 'react'\nimport { cn } from '../../lib/utils.ts'\n\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaHTMLAttributes<HTMLTextAreaElement>>(({ className, ...props }, ref) => (\n <textarea\n ref={ref}\n data-slot=\"textarea\"\n className={cn(\n 'w-full resize-none rounded-md border border-border bg-bg px-2.5 py-2 text-body-sm text-text',\n 'placeholder:text-fg-4 transition-colors outline-none',\n 'hover:border-border-strong focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40',\n 'disabled:pointer-events-none disabled:opacity-50',\n className,\n )}\n {...props}\n />\n))\nTextarea.displayName = 'Textarea'\n","import { type FunctionComponent } from 'react'\nimport { AlertDialog as AlertDialogPrimitive } from '@base-ui/react/alert-dialog'\nimport { cn } from '../../lib/utils.ts'\nimport { PortalScope } from './PortalScope.tsx'\n\nexport const AlertDialog = AlertDialogPrimitive.Root\nexport const AlertDialogTrigger = AlertDialogPrimitive.Trigger\nexport const AlertDialogClose = AlertDialogPrimitive.Close\n\nexport const AlertDialogContent: FunctionComponent<AlertDialogPrimitive.Popup.Props> = ({ className, children, ...props }) => (\n <AlertDialogPrimitive.Portal>\n <PortalScope>\n <AlertDialogPrimitive.Backdrop\n className={cn(\n 'fixed inset-0 z-70 bg-black/40 backdrop-blur-[1px]',\n 'transition-opacity duration-(--motion-base)',\n 'data-starting-style:opacity-0 data-ending-style:opacity-0',\n )}\n />\n <AlertDialogPrimitive.Popup\n data-slot=\"alert-dialog-content\"\n className={cn(\n 'fixed top-1/2 left-1/2 z-70 w-[min(28rem,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2',\n 'rounded-lg border border-border bg-surface p-5 shadow-(--shadow-lg) outline-none',\n 'transition-[opacity,transform] duration-(--motion-base)',\n 'data-starting-style:scale-95 data-starting-style:opacity-0',\n 'data-ending-style:scale-95 data-ending-style:opacity-0',\n className,\n )}\n {...props}\n >\n {children}\n </AlertDialogPrimitive.Popup>\n </PortalScope>\n </AlertDialogPrimitive.Portal>\n)\n\nexport const AlertDialogTitle: FunctionComponent<AlertDialogPrimitive.Title.Props> = ({ className, ...props }) => (\n <AlertDialogPrimitive.Title className={cn('text-heading-3 font-semibold text-text', className)} {...props} />\n)\n\nexport const AlertDialogDescription: FunctionComponent<AlertDialogPrimitive.Description.Props> = ({ className, ...props }) => (\n <AlertDialogPrimitive.Description className={cn('mt-1.5 text-body-sm text-muted-foreground', className)} {...props} />\n)\n","import { Toaster as SonnerToaster, toast } from 'sonner'\n\nexport { toast }\n\nexport function Toaster() {\n return (\n <SonnerToaster\n position=\"bottom-right\"\n toastOptions={{\n style: {\n background: 'var(--surface)',\n color: 'var(--text)',\n border: '1px solid var(--border)',\n boxShadow: 'var(--shadow-md)',\n fontFamily: 'var(--cw-font-sans)',\n fontSize: 'var(--text-body-sm)',\n },\n }}\n />\n )\n}\n","import type { ReactNode } from 'react'\nimport { Button } from './Button.tsx'\n\nexport function Empty({\n icon,\n title,\n description,\n action,\n onAction,\n}: {\n icon: ReactNode\n title: string\n description?: ReactNode\n action?: string\n onAction?: () => void\n}) {\n return (\n <div className=\"flex flex-col items-center gap-2 px-5 py-8 text-center\">\n <div className=\"text-fg-4 opacity-60 [&_svg]:size-7\">{icon}</div>\n <p className=\"text-body-sm font-medium text-fg-2\">{title}</p>\n {description ? <p className=\"max-w-[26ch] text-balance text-label leading-relaxed text-fg-4\">{description}</p> : null}\n {action && onAction ? (\n <Button variant=\"outline\" size=\"sm\" className=\"mt-1\" onClick={onAction}>\n {action}\n </Button>\n ) : null}\n </div>\n )\n}\n\nexport function EmptyKey({ children }: { children: ReactNode }) {\n return <span className=\"rounded border border-border px-1 font-mono text-fg-3\">{children}</span>\n}\n","/**\n * Convenience helpers for creating and inspecting Segments.\n *\n * These reduce boilerplate when building AI chat UIs that work with the\n * PromptArea document model.\n *\n * @example\n * ```ts\n * import { text, chip, isSegmentsEmpty, segmentsToPlainText } from './segment-helpers.ts'\n *\n * const greeting = [text('Hello '), chip({ trigger: '@', value: 'u1', displayText: 'Alice' })]\n * isSegmentsEmpty(greeting) // false\n * segmentsToPlainText(greeting) // \"Hello @Alice\"\n * ```\n */\n\nimport type { Segment, TextSegment, ChipSegment } from './types.ts'\nimport { segmentsToPlainText, plainTextToSegments } from './prompt-area-engine.ts'\n\n// Re-export serialization utilities so consumers have a single import.\nexport { segmentsToPlainText, plainTextToSegments }\n\n/** Create a text segment. */\nexport function text(value: string): TextSegment {\n return { type: 'text', text: value }\n}\n\n/** Create a chip segment. */\nexport function chip(opts: Omit<ChipSegment, 'type'>): ChipSegment {\n return { type: 'chip', ...opts }\n}\n\n/** Returns `true` when the segment array is empty or contains only whitespace text. */\nexport function isSegmentsEmpty(segments: Segment[]): boolean {\n if (segments.length === 0) {\n return true\n }\n return segments.every((seg) => seg.type === 'text' && seg.text.trim() === '')\n}\n\n/** Returns `true` when the segment array contains at least one chip. */\nexport function hasChips(segments: Segment[]): boolean {\n return segments.some((seg) => seg.type === 'chip')\n}\n\n/** Extracts all chip segments from a segment array. */\nexport function getChips(segments: Segment[]): ChipSegment[] {\n return segments.filter((seg): seg is ChipSegment => seg.type === 'chip')\n}\n\n/** Extracts chips matching a specific trigger character. */\nexport function getChipsByTrigger(segments: Segment[], trigger: string): ChipSegment[] {\n return segments.filter((seg): seg is ChipSegment => seg.type === 'chip' && seg.trigger === trigger)\n}\n","import type { RateLimitInfo } from '@workerdeck/protocol'\nimport type { ConnectionState, TranscriptState } from '@workerdeck/react'\nimport { formatCost, formatTokens } from '../../lib/format.ts'\nimport {\n contextSeverity,\n meterSeverity,\n modelLabel,\n statusPresentation,\n tightestWindow,\n windowLabel,\n type StatusSeverity,\n} from '../../lib/status.ts'\nimport { Ink, Row, type Tone } from './row.tsx'\n\nconst SEVERITY_TONE: Record<StatusSeverity, Tone> = {\n none: 'dim',\n warning: 'yellow',\n error: 'red',\n}\n\nexport interface TerminalStatusLineProps {\n className?: string\n state: TranscriptState\n rateLimits?: Record<string, RateLimitInfo>\n connection?: ConnectionState\n onOpenStatus?: () => void\n onOpenContext?: () => void\n onOpenUsage?: () => void\n}\n\nfunction Reading({ tone, onPress, label, children }: { tone?: Tone; onPress?: () => void; label?: string; children: React.ReactNode }) {\n if (!onPress) {\n return <Ink tone={tone}>{children}</Ink>\n }\n return (\n <button type=\"button\" className=\"term-reading\" onClick={onPress} title={label}>\n <Ink tone={tone}>{children}</Ink>\n </button>\n )\n}\n\nexport function TerminalStatusLine({\n className,\n state,\n rateLimits,\n connection,\n onOpenStatus,\n onOpenContext,\n onOpenUsage,\n}: TerminalStatusLineProps) {\n const presentation = statusPresentation({ status: state.status, connection })\n const usage = state.contextUsage\n const window = tightestWindow(rateLimits ?? state.rateLimits)\n const model = modelLabel({ model: state.model, models: state.models ?? [] })\n\n const parts: React.ReactNode[] = [\n <Reading key=\"status\" tone={SEVERITY_TONE[presentation.severity]} onPress={onOpenStatus} label=\"Session status\">\n {presentation.label}\n </Reading>,\n ]\n\n if (usage) {\n parts.push(\n <Reading key=\"context\" tone={SEVERITY_TONE[contextSeverity(usage)]} onPress={onOpenContext} label=\"Context window\">\n {Math.round(usage.percentage)}% ctx ({formatTokens(usage.totalTokens)})\n </Reading>,\n )\n }\n\n if (window) {\n parts.push(\n <Reading key=\"usage\" tone={SEVERITY_TONE[meterSeverity(window.info.utilization)]} onPress={onOpenUsage} label=\"Plan usage\">\n {Math.round(window.info.utilization ?? 0)}% {windowLabel(window.key)}\n </Reading>,\n )\n }\n\n if (state.totalCostUsd > 0) {\n parts.push(\n <Ink key=\"cost\" tone=\"faint\">\n {formatCost(state.totalCostUsd)}\n </Ink>,\n )\n }\n\n if (model) {\n parts.push(\n <Ink key=\"model\" tone=\"faint\">\n {model}\n </Ink>,\n )\n }\n if (state.permissionMode && state.permissionMode !== 'default') {\n parts.push(\n <Ink key=\"mode\" tone=\"yellow\">\n {state.permissionMode}\n </Ink>,\n )\n }\n\n return (\n <Row data-slot=\"status-line\" tone=\"dim\" className={className}>\n {parts.map((part, index) => (\n <span key={index}>\n {index > 0 ? <Ink tone=\"faint\"> · </Ink> : null}\n {part}\n </span>\n ))}\n </Row>\n )\n}\n","import type { SessionInfo } from '@workerdeck/protocol'\nimport { Trash2 } from 'lucide-react'\nimport { Badge } from '../ui/Badge.tsx'\nimport { Button } from '../ui/Button.tsx'\nimport { cn } from '../../lib/utils.ts'\nimport { formatCost, formatRelativeTime } from '../../lib/format.ts'\nimport { STATUS_META } from './status.ts'\n\nexport interface SessionListItemProps {\n session: SessionInfo\n active?: boolean\n onSelect?: (id: string) => void\n onDelete?: (id: string) => void\n className?: string\n}\n\nexport function SessionListItem({ session, active, onSelect, onDelete, className }: SessionListItemProps) {\n const meta = STATUS_META[session.status]\n return (\n <div\n data-slot=\"session-list-item\"\n data-active={active || undefined}\n className={cn(\n 'group flex w-full items-center gap-2 rounded-md border border-transparent px-2.5 py-2 text-left transition-colors',\n active ? 'border-border bg-surface' : 'hover:bg-surface-hover',\n className,\n )}\n >\n <button type=\"button\" onClick={() => onSelect?.(session.id)} className=\"min-w-0 flex-1 text-left outline-none\">\n <div className=\"flex items-center gap-2\">\n <span className=\"truncate text-body-sm font-medium text-fg-1\">{session.title ?? session.id.slice(0, 8)}</span>\n <Badge variant={meta.variant} dot className=\"shrink-0\">\n {meta.label}\n </Badge>\n </div>\n <div className=\"mt-0.5 flex items-center gap-2 font-mono text-label text-fg-4\">\n <span className=\"truncate\">{session.cwd}</span>\n {session.profile ? <span className=\"shrink-0\">@{session.profile}</span> : null}\n <span className=\"shrink-0\">{formatCost(session.totalCostUsd)}</span>\n <span className=\"shrink-0\">{formatRelativeTime(session.lastActivityAt ?? session.createdAt)}</span>\n </div>\n </button>\n {onDelete ? (\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label=\"Close session\"\n className=\"opacity-0 transition-opacity group-hover:opacity-100\"\n onClick={() => onDelete(session.id)}\n >\n <Trash2 className=\"size-3.5 text-fg-3\" />\n </Button>\n ) : null}\n </div>\n )\n}\n\nexport interface SessionListProps {\n sessions: SessionInfo[]\n activeId?: string\n onSelect?: (id: string) => void\n onDelete?: (id: string) => void\n emptyText?: string\n className?: string\n}\n\nexport function SessionList({ sessions, activeId, onSelect, onDelete, emptyText = 'No sessions yet.', className }: SessionListProps) {\n return (\n <div data-slot=\"session-list\" className={cn('flex flex-col gap-1', className)}>\n {sessions.length === 0 ? (\n <div className=\"px-2.5 py-6 text-center text-body-sm text-fg-4\">{emptyText}</div>\n ) : (\n sessions.map((session) => (\n <SessionListItem key={session.id} session={session} active={session.id === activeId} onSelect={onSelect} onDelete={onDelete} />\n ))\n )}\n </div>\n )\n}\n","import type { ProjectIcon as ProjectIconSpec } from '@workerdeck/protocol'\nimport {\n Anchor,\n Atom,\n Beaker,\n Bike,\n Binary,\n Blocks,\n Bot,\n Box,\n Brain,\n Briefcase,\n Brush,\n Bug,\n Building2,\n Cable,\n Camera,\n Car,\n Cat,\n CircuitBoard,\n Cloud,\n Code,\n Coffee,\n Cog,\n Compass,\n Container,\n Cpu,\n CreditCard,\n Database,\n Diamond,\n Dog,\n Feather,\n Film,\n Flame,\n FlaskConical,\n Folder,\n Gamepad2,\n Gauge,\n Gem,\n Ghost,\n Gift,\n GitBranch,\n Globe,\n GraduationCap,\n Hammer,\n Hexagon,\n House,\n Image,\n Key,\n Landmark,\n Laptop,\n Layers,\n Leaf,\n Library,\n Lightbulb,\n Lock,\n Mail,\n Map,\n MessageCircle,\n Mic,\n Monitor,\n Moon,\n Mountain,\n Music,\n Network,\n Newspaper,\n Notebook,\n Orbit,\n Package,\n Palette,\n PenTool,\n Phone,\n PiggyBank,\n Plane,\n Plug,\n Puzzle,\n Radar,\n Radio,\n Receipt,\n Rocket,\n Ruler,\n Satellite,\n Scale,\n Scissors,\n Server,\n Shield,\n Ship,\n ShoppingCart,\n Signal,\n Smartphone,\n Sparkles,\n Sprout,\n Square,\n Star,\n Sun,\n Table,\n Tag,\n Target,\n Telescope,\n Terminal,\n TestTube,\n TreePine,\n Trophy,\n Truck,\n Umbrella,\n Users,\n Wallet,\n Wand,\n Waves,\n Webhook,\n Wifi,\n Wrench,\n Zap,\n} from 'lucide-react'\nimport type { ComponentType } from 'react'\nimport { cn } from '../../lib/utils.ts'\n\nconst GLYPHS: Record<string, ComponentType<{ className?: string }>> = {\n anchor: Anchor,\n atom: Atom,\n beaker: Beaker,\n bike: Bike,\n binary: Binary,\n blocks: Blocks,\n bot: Bot,\n box: Box,\n brain: Brain,\n briefcase: Briefcase,\n brush: Brush,\n bug: Bug,\n 'building-2': Building2,\n cable: Cable,\n camera: Camera,\n car: Car,\n cat: Cat,\n 'circuit-board': CircuitBoard,\n cloud: Cloud,\n code: Code,\n coffee: Coffee,\n cog: Cog,\n compass: Compass,\n container: Container,\n cpu: Cpu,\n 'credit-card': CreditCard,\n database: Database,\n diamond: Diamond,\n dog: Dog,\n feather: Feather,\n film: Film,\n flame: Flame,\n 'flask-conical': FlaskConical,\n folder: Folder,\n 'gamepad-2': Gamepad2,\n gauge: Gauge,\n gem: Gem,\n ghost: Ghost,\n gift: Gift,\n 'git-branch': GitBranch,\n globe: Globe,\n 'graduation-cap': GraduationCap,\n hammer: Hammer,\n hexagon: Hexagon,\n house: House,\n image: Image,\n key: Key,\n landmark: Landmark,\n laptop: Laptop,\n layers: Layers,\n leaf: Leaf,\n library: Library,\n lightbulb: Lightbulb,\n lock: Lock,\n mail: Mail,\n map: Map,\n 'message-circle': MessageCircle,\n mic: Mic,\n monitor: Monitor,\n moon: Moon,\n mountain: Mountain,\n music: Music,\n network: Network,\n newspaper: Newspaper,\n notebook: Notebook,\n orbit: Orbit,\n package: Package,\n palette: Palette,\n 'pen-tool': PenTool,\n phone: Phone,\n 'piggy-bank': PiggyBank,\n plane: Plane,\n plug: Plug,\n puzzle: Puzzle,\n radar: Radar,\n radio: Radio,\n receipt: Receipt,\n rocket: Rocket,\n ruler: Ruler,\n satellite: Satellite,\n scale: Scale,\n scissors: Scissors,\n server: Server,\n shield: Shield,\n ship: Ship,\n 'shopping-cart': ShoppingCart,\n signal: Signal,\n smartphone: Smartphone,\n sparkles: Sparkles,\n sprout: Sprout,\n square: Square,\n star: Star,\n sun: Sun,\n table: Table,\n tag: Tag,\n target: Target,\n telescope: Telescope,\n terminal: Terminal,\n 'test-tube': TestTube,\n 'tree-pine': TreePine,\n trophy: Trophy,\n truck: Truck,\n umbrella: Umbrella,\n users: Users,\n wallet: Wallet,\n wand: Wand,\n waves: Waves,\n webhook: Webhook,\n wifi: Wifi,\n wrench: Wrench,\n zap: Zap,\n}\n\nexport function ProjectIcon({\n icon,\n src,\n name,\n className,\n}: {\n icon: ProjectIconSpec | undefined\n src?: string\n name?: string\n className?: string\n}) {\n if (!icon) {\n return null\n }\n if (icon.type === 'image') {\n if (!src) {\n return null\n }\n return <img src={src} alt=\"\" aria-hidden title={name} className={cn('inline-block size-3 shrink-0 object-contain', className)} />\n }\n const Glyph = GLYPHS[icon.name] ?? Folder\n return <Glyph className={cn('inline-block size-3 shrink-0', className)} />\n}\n","import type { ContextReading } from '@workerdeck/protocol'\nimport { ProgressRing } from '../ui/ProgressRing.tsx'\nimport { cn } from '../../lib/utils.ts'\nimport { formatTokens } from '../../lib/format.ts'\nimport { meterColorClass } from '../../lib/status.ts'\n\nexport function ContextRing({ usage, size = 11, className }: { usage: ContextReading | undefined; size?: number; className?: string }) {\n if (usage === undefined) {\n return null\n }\n return (\n <span\n title={`Context ${usage.percentage.toFixed(0)}% · ${formatTokens(usage.totalTokens)} / ${formatTokens(usage.maxTokens)}`}\n className={cn('shrink-0 leading-none', meterColorClass(usage.percentage), className)}\n >\n <ProgressRing value={usage.percentage} size={size} strokeWidth={2} />\n </span>\n )\n}\n","import { cn } from '../../lib/utils.ts'\n\n// Paths from `@lobehub/icons-static-svg` (MIT, © LobeHub), inlined. The marks are trademarks of their owners, used only to identify a session's engine.\nconst PATHS: Record<string, { title: string; d: string }> = {\n claude: {\n title: 'Claude',\n d: 'M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z',\n },\n codex: {\n title: 'OpenAI',\n d: 'M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z',\n },\n gemini: {\n title: 'Gemini',\n d: 'M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z',\n },\n deepseek: {\n title: 'DeepSeek',\n d: 'M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z',\n },\n moonshot: {\n title: 'Moonshot',\n d: 'M1.052 16.916l9.539 2.552a21.007 21.007 0 00.06 2.033l5.956 1.593a11.997 11.997 0 01-5.586.865l-.18-.016-.044-.004-.084-.009-.094-.01a11.605 11.605 0 01-.157-.02l-.107-.014-.11-.016a11.962 11.962 0 01-.32-.051l-.042-.008-.075-.013-.107-.02-.07-.015-.093-.019-.075-.016-.095-.02-.097-.023-.094-.022-.068-.017-.088-.022-.09-.024-.095-.025-.082-.023-.109-.03-.062-.02-.084-.025-.093-.028-.105-.034-.058-.019-.08-.026-.09-.031-.066-.024a6.293 6.293 0 01-.044-.015l-.068-.025-.101-.037-.057-.022-.08-.03-.087-.035-.088-.035-.079-.032-.095-.04-.063-.028-.063-.027a5.655 5.655 0 01-.041-.018l-.066-.03-.103-.047-.052-.024-.096-.046-.062-.03-.084-.04-.086-.044-.093-.047-.052-.027-.103-.055-.057-.03-.058-.032a6.49 6.49 0 01-.046-.026l-.094-.053-.06-.034-.051-.03-.072-.041-.082-.05-.093-.056-.052-.032-.084-.053-.061-.039-.079-.05-.07-.047-.053-.035a7.785 7.785 0 01-.054-.036l-.044-.03-.044-.03a6.066 6.066 0 01-.04-.028l-.057-.04-.076-.054-.069-.05-.074-.054-.056-.042-.076-.057-.076-.059-.086-.067-.045-.035-.064-.052-.074-.06-.089-.073-.046-.039-.046-.039a7.516 7.516 0 01-.043-.037l-.045-.04-.061-.053-.07-.062-.068-.06-.062-.058-.067-.062-.053-.05-.088-.084a13.28 13.28 0 01-.099-.097l-.029-.028-.041-.042-.069-.07-.05-.051-.05-.053a6.457 6.457 0 01-.168-.179l-.08-.088-.062-.07-.071-.08-.042-.049-.053-.062-.058-.068-.046-.056a7.175 7.175 0 01-.027-.033l-.045-.055-.066-.082-.041-.052-.05-.064-.02-.025a11.99 11.99 0 01-1.44-2.402zm-1.02-5.794l11.353 3.037a20.468 20.468 0 00-.469 2.011l10.817 2.894a12.076 12.076 0 01-1.845 2.005L.657 15.923l-.016-.046-.035-.104a11.965 11.965 0 01-.05-.153l-.007-.023a11.896 11.896 0 01-.207-.741l-.03-.126-.018-.08-.021-.097-.018-.081-.018-.09-.017-.084-.018-.094c-.026-.141-.05-.283-.071-.426l-.017-.118-.011-.083-.013-.102a12.01 12.01 0 01-.019-.161l-.005-.047a12.12 12.12 0 01-.034-2.145zm1.593-5.15l11.948 3.196c-.368.605-.705 1.231-1.01 1.875l11.295 3.022c-.142.82-.368 1.612-.668 2.365l-11.55-3.09L.124 10.26l.015-.1.008-.049.01-.067.015-.087.018-.098c.026-.148.056-.295.088-.442l.028-.124.02-.085.024-.097c.022-.09.045-.18.07-.268l.028-.102.023-.083.03-.1.025-.082.03-.096.026-.082.031-.095a11.896 11.896 0 011.01-2.232zm4.442-4.4L17.352 4.59a20.77 20.77 0 00-1.688 1.721l7.823 2.093c.267.852.442 1.744.513 2.665L2.106 5.213l.045-.065.027-.04.04-.055.046-.065.055-.076.054-.072.064-.086.05-.065.057-.073.055-.07.06-.074.055-.069.065-.077.054-.066.066-.077.053-.06.072-.082.053-.06.067-.074.054-.058.073-.078.058-.06.063-.067.168-.17.1-.098.059-.056.076-.071a12.084 12.084 0 012.272-1.677zM12.017 0h.097l.082.001.069.001.054.002.068.002.046.001.076.003.047.002.06.003.054.002.087.005.105.007.144.011.088.007.044.004.077.008.082.008.047.005.102.012.05.006.108.014.081.01.042.006.065.01.207.032.07.012.065.011.14.026.092.018.11.022.046.01.075.016.041.01L14.7.3l.042.01.065.015.049.012.071.017.096.024.112.03.113.03.113.032.05.015.07.02.078.024.073.023.05.016.05.016.076.025.099.033.102.036.048.017.064.023.093.034.11.041.116.045.1.04.047.02.06.024.041.018.063.026.04.018.057.025.11.048.1.046.074.035.075.036.06.028.092.046.091.045.102.052.053.028.049.026.046.024.06.033.041.022.052.029.088.05.106.06.087.051.057.034.053.032.096.059.088.055.098.062.036.024.064.041.084.056.04.027.062.042.062.043.023.017c.054.037.108.075.161.114l.083.06.065.048.056.043.086.065.082.064.04.03.05.041.086.069.079.065.085.071c.712.6 1.353 1.283 1.909 2.031L7.222.994l.062-.027.065-.028.081-.034.086-.035c.113-.045.227-.09.341-.131l.096-.035.093-.033.084-.03.096-.031c.087-.03.176-.058.264-.085l.091-.027.086-.025.102-.03.085-.023.1-.026L9.04.37l.09-.023.091-.022.095-.022.09-.02.098-.021.091-.02.095-.018.092-.018.1-.018.091-.016.098-.017.092-.014.097-.015.092-.013.102-.013.091-.012.105-.012.09-.01.105-.01c.093-.01.186-.018.28-.024l.106-.008.09-.005.11-.006.093-.004.1-.004.097-.002.099-.002.197-.002z',\n },\n}\n\nexport function engineMark(engine: string, model?: string): string | undefined {\n if (engine === 'claude') {\n return 'claude'\n }\n if (engine === 'codex') {\n return 'codex'\n }\n const id = model?.toLowerCase() ?? ''\n if (!id) {\n return undefined\n }\n if (id.includes('gemini')) {\n return 'gemini'\n }\n if (id.includes('deepseek')) {\n return 'deepseek'\n }\n if (id.includes('moonshot') || id.includes('kimi')) {\n return 'moonshot'\n }\n if (id.includes('claude')) {\n return 'claude'\n }\n if (id.startsWith('gpt') || id.startsWith('o1') || id.startsWith('o3') || id.includes('openai')) {\n return 'codex'\n }\n return undefined\n}\n\nconst VENDOR_MARK: Record<string, string> = {\n claude: 'text-vendor-claude',\n codex: 'text-vendor-openai',\n}\nconst VENDOR_TEXT: Record<string, string> = {\n claude: 'text-vendor-claude',\n}\n\nexport function vendorMarkClass(engine: string, model?: string): string {\n return VENDOR_MARK[engineMark(engine, model) ?? ''] ?? 'text-fg-3'\n}\n\nexport function vendorTextClass(engine: string, model?: string): string {\n return VENDOR_TEXT[engineMark(engine, model) ?? ''] ?? 'text-fg-3'\n}\n\nexport function EngineIcon({ engine, model, className }: { engine: string; model?: string; className?: string }) {\n const mark = PATHS[engineMark(engine, model) ?? '']\n if (!mark) {\n return (\n <span aria-hidden title={engine} className={cn('shrink-0 text-fg-3', className)}>\n ·\n </span>\n )\n }\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n fillRule=\"evenodd\"\n role=\"img\"\n aria-label={mark.title}\n className={cn('size-3 shrink-0 text-fg-3', className)}\n >\n <title>{mark.title}</title>\n <path d={mark.d} />\n </svg>\n )\n}\n","import { BellRing, CircleAlert, CircleSlash, Moon, PauseCircle } from 'lucide-react'\nimport type { SessionRow } from '@workerdeck/protocol'\nimport { Spinner } from '../ui/Spinner.tsx'\nimport { cn } from '../../lib/utils.ts'\n\nexport function SessionStatusIcon({ row, className }: { row: SessionRow; className?: string }) {\n const { info } = row\n const size = cn('size-4 shrink-0', className)\n if (row.state === 'attention') {\n return <BellRing className={cn(size, 'animate-pulse text-warning')} />\n }\n if (row.state === 'working') {\n return <Spinner className={cn(size, 'text-info')} />\n }\n switch (info.status) {\n case 'failed': {\n return <CircleAlert className={cn(size, 'text-danger')} />\n }\n case 'closed': {\n return <CircleSlash className={cn(size, 'text-fg-4')} />\n }\n case 'parked': {\n return <PauseCircle className={cn(size, 'text-fg-3')} />\n }\n default: {\n return <Moon className={cn(size, 'text-fg-4')} />\n }\n }\n}\n","import { ArrowRight, Check, ChevronDown, ChevronRight, CircleAlert, Dot, PauseCircle } from 'lucide-react'\nimport { isAgentRecord, subagentLabel } from '@workerdeck/protocol'\nimport type { SessionInfo, SubagentInfo } from '@workerdeck/protocol'\nimport { Spinner } from '../ui/Spinner.tsx'\nimport { cn } from '../../lib/utils.ts'\n\nexport type Step = {\n key: string\n label: string\n noun: string\n kind: 'agent' | 'task'\n state: 'done' | 'running' | 'pending' | 'failed'\n detail?: string\n title: string\n onSelect: () => void\n}\n\nexport function sessionSteps(info: SessionInfo, onSelect: (toolUseId: string, kind: Step['kind']) => void): Step[] {\n const steps = (info.subagents ?? []).map((sub) => {\n const kind = isAgentRecord(sub) ? ('agent' as const) : ('task' as const)\n return {\n key: sub.toolUseId,\n label: subagentLabel(sub),\n noun: 'agent',\n kind,\n state: stepState(sub.status),\n detail: sub.toolCount > 0 ? String(sub.toolCount) : undefined,\n title: `${subagentLabel(sub)} · ${sub.toolCount} tool${sub.toolCount === 1 ? '' : 's'}`,\n onSelect: () => onSelect(sub.toolUseId, kind),\n }\n })\n return [...steps.filter((s) => s.kind === 'agent'), ...steps.filter((s) => s.kind === 'task')]\n}\n\nfunction stepState(status: SubagentInfo['status']): Step['state'] {\n switch (status) {\n case 'running': {\n return 'running'\n }\n case 'failed': {\n return 'failed'\n }\n default: {\n return 'done'\n }\n }\n}\n\nexport function runningSteps(steps: readonly Step[]): number {\n return steps.filter((s) => s.state === 'running').length\n}\n\nexport function StepToggle({\n expanded,\n running,\n total,\n noun,\n onToggle,\n}: {\n expanded: boolean\n running: number\n total: number\n noun: string\n onToggle: () => void\n}) {\n const label = running > 0 && running < total ? `${running}/${total}` : String(total)\n const words = running > 0 && running < total ? `${running} of ${total} ${noun}s running` : `${total} ${noun}${total === 1 ? '' : 's'}`\n const Chevron = expanded ? ChevronDown : ChevronRight\n return (\n <button\n type=\"button\"\n aria-expanded={expanded}\n aria-label={`${expanded ? 'Hide' : 'Show'} ${words}`}\n title={`${expanded ? 'Hide' : 'Show'} ${words}`}\n onClick={(e) => {\n e.stopPropagation()\n onToggle()\n }}\n className={cn(\n 'flex shrink-0 items-center gap-0.5 rounded-[4px] py-0.5 pr-1 pl-0.5 outline-none',\n 'text-[0.75rem] leading-3 hover:bg-row-hover hover:text-fg-2',\n running > 0 ? 'text-info' : 'text-fg-4',\n )}\n >\n <Chevron className=\"size-3\" />\n <span className=\"tabular-nums\">{label}</span>\n </button>\n )\n}\n\nexport function StepRow({ step, active = false, onSelect }: { step: Step; active?: boolean; onSelect: () => void }) {\n const agent = step.kind === 'agent'\n const selected = active && agent\n const body = step.state === 'failed' ? 'text-danger' : agent ? 'text-success' : 'text-fg-4'\n return (\n <button\n type=\"button\"\n title={step.title}\n aria-current={selected || undefined}\n onClick={(e) => {\n e.stopPropagation()\n onSelect()\n }}\n className={cn(\n 'flex w-full items-center gap-1.5 rounded-[4px] py-1 pr-2.5 pl-3.5',\n 'text-left text-micro outline-none',\n selected ? 'bg-row-selected' : 'hover:bg-row-active',\n body,\n )}\n >\n <StepIcon state={step.state} kind={step.kind} />\n <span className=\"min-w-0 flex-1 truncate\">{step.label}</span>\n {step.detail ? <span className=\"shrink-0 tabular-nums text-fg-4\">{step.detail}</span> : null}\n {agent ? <ArrowRight className=\"size-3.5 shrink-0 text-fg-4\" /> : null}\n </button>\n )\n}\n\nfunction StepIcon({ state, kind }: { state: Step['state']; kind: Step['kind'] }) {\n if (kind === 'task' && state !== 'running' && state !== 'failed') {\n return <Dot className=\"size-[11px] shrink-0\" />\n }\n switch (state) {\n case 'running': {\n return <Spinner className=\"size-[11px] shrink-0\" />\n }\n case 'failed': {\n return <CircleAlert className=\"size-[11px] shrink-0\" />\n }\n case 'pending': {\n return <PauseCircle className=\"size-[11px] shrink-0\" />\n }\n default: {\n return <Check className=\"size-[11px] shrink-0\" />\n }\n }\n}\n","import { Fragment, useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { projectLabel, projectSubpath, sessionLabel } from '@workerdeck/protocol'\nimport type { SessionRow } from '@workerdeck/protocol'\nimport { ContextRing } from './ContextRing.tsx'\nimport { EngineIcon, vendorMarkClass, vendorTextClass } from './EngineIcon.tsx'\nimport { ProjectIcon } from './ProjectIcon.tsx'\nimport { SessionStatusIcon } from './SessionStatusIcon.tsx'\nimport { StepRow, StepToggle, runningSteps, sessionSteps } from './SessionSteps.tsx'\nimport type { Step } from './SessionSteps.tsx'\nimport { cn } from '../../lib/utils.ts'\nimport { formatCost, formatRelativeTime, friendlyModel } from '../../lib/format.ts'\n\nexport interface SessionItemProps {\n row: SessionRow\n active?: boolean\n activeStepKey?: string\n showGateway?: boolean\n showProject?: boolean\n projectIcons?: Record<string, string>\n expanded?: boolean\n onExpandedChange?: (expanded: boolean) => void\n onSelect?: () => void\n onSelectSubagent?: (toolUseId: string) => void\n onRevealStep?: (toolUseId: string) => void\n onRename?: (title: string) => void\n renameOn?: 'doubleClick' | 'external'\n editing?: boolean\n onEditingChange?: (editing: boolean) => void\n actions?: ReactNode\n className?: string\n}\n\nexport function SessionItem({\n row,\n active = false,\n activeStepKey,\n showGateway,\n showProject = true,\n projectIcons,\n expanded,\n onExpandedChange,\n onSelect,\n onSelectSubagent,\n onRevealStep,\n onRename,\n renameOn = 'doubleClick',\n editing,\n onEditingChange,\n actions,\n className,\n}: SessionItemProps) {\n const { info } = row\n const [ownExpanded, setOwnExpanded] = useState(false)\n const open = expanded ?? ownExpanded\n const setOpen = (next: boolean) => {\n setOwnExpanded(next)\n onExpandedChange?.(next)\n }\n const [ownEditing, setOwnEditing] = useState(false)\n const isEditing = editing ?? ownEditing\n const setEditing = (next: boolean) => {\n setOwnEditing(next)\n onEditingChange?.(next)\n }\n\n const engine = info.engine ?? 'claude'\n const project = showProject ? projectLabel(row) : projectSubpath(row)\n const projectIcon = showProject ? info.project?.icon : undefined\n const iconSrc = projectIcon?.type === 'image' ? projectIcons?.[projectIcon.hash] : undefined\n const cost = formatCost(info.totalCostUsd)\n const extras = [\n showGateway ? row.hostName : undefined,\n info.profile ? `@${info.profile}` : undefined,\n cost === '—' ? undefined : cost,\n ].filter((part): part is string => Boolean(part))\n\n const model = friendlyModel(info.model)\n const parts: ReactNode[] = []\n if (model) {\n parts.push(\n <span key=\"model\" className={vendorTextClass(engine, info.model)}>\n {model}\n </span>,\n )\n }\n if (project !== undefined) {\n parts.push(\n <span key=\"project\">\n <ProjectIcon icon={projectIcon} src={iconSrc} name={project} className=\"mr-1.5 size-4 align-[-0.3em]\" />\n {project}\n </span>,\n )\n }\n for (const extra of extras) {\n parts.push(<span key={extra}>{extra}</span>)\n }\n const steps = sessionSteps(info, (toolUseId, kind) => {\n if (kind === 'agent') {\n return onSelectSubagent ? onSelectSubagent(toolUseId) : onSelect?.()\n }\n return onRevealStep ? onRevealStep(toolUseId) : onSelect?.()\n })\n const holdsOpenAgent = steps.some((s) => s.kind === 'agent' && s.key === activeStepKey)\n\n return (\n <div\n data-slot=\"session-item\"\n data-active={active || undefined}\n role=\"button\"\n tabIndex={0}\n onClick={(e) => {\n if (e.detail > 1 || isEditing) {\n return\n }\n onSelect?.()\n }}\n onKeyDown={(e) => {\n if (e.target !== e.currentTarget) {\n return\n }\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onSelect?.()\n }\n }}\n className={cn(\n 'group flex w-full cursor-pointer flex-col p-1 text-left outline-none',\n 'rounded-[4px] transition-colors focus-visible:ring-2 focus-visible:ring-ring',\n holdsOpenAgent ? 'bg-row-selected-weak' : active ? 'bg-row-selected' : 'hover:bg-row-hover',\n className,\n )}\n >\n <div className=\"flex flex-col gap-1 py-0.5 pr-0.5 pl-1.5\">\n <div className=\"flex h-5 items-center gap-1.5 overflow-hidden\">\n <Gutter>\n <SessionStatusIcon row={row} />\n </Gutter>\n {isEditing && onRename ? (\n <NameEditor\n initial={info.title ?? ''}\n onCommit={(title) => {\n setEditing(false)\n if (title !== (info.title ?? '')) {\n onRename(title)\n }\n }}\n onCancel={() => setEditing(false)}\n />\n ) : (\n <span\n onDoubleClick={\n onRename && renameOn === 'doubleClick'\n ? (e) => {\n e.stopPropagation()\n setEditing(true)\n }\n : undefined\n }\n className=\"min-w-0 flex-1 truncate text-body-sm font-medium tracking-[-0.005em] text-fg-1\"\n >\n {sessionLabel(info)}\n </span>\n )}\n {row.unseen > 0 ? (\n <span\n // Prose, not rows: `unseen` counts messages a person has not read (`proseCount`).\n title={`${row.unseen} new message${row.unseen === 1 ? '' : 's'}`}\n className={cn(\n 'flex h-4 min-w-6 shrink-0 items-center justify-center rounded-full px-2',\n 'text-[0.75rem] leading-none tracking-[-0.005em] tabular-nums',\n row.state === 'working' || row.state === 'attention' ? 'bg-accent text-accent-fg' : 'bg-badge text-badge-fg',\n )}\n >\n {row.unseen}\n </span>\n ) : null}\n <ContextRing usage={info.contextUsage} size={16} className=\"p-0.5\" />\n </div>\n\n <div className=\"flex h-5 items-center gap-1.5 overflow-hidden text-body-sm tracking-[-0.005em]\">\n <Gutter>\n <EngineIcon engine={engine} model={info.model} className={cn('size-4', vendorMarkClass(engine, info.model))} />\n </Gutter>\n <span className=\"min-w-0 truncate text-fg-4\">\n {parts.map((part, i) => (\n <Fragment key={i}>\n {i > 0 ? ' · ' : ''}\n {part}\n </Fragment>\n ))}\n </span>\n <span className=\"shrink-0 text-fg-4\">\n {parts.length > 0 ? '· ' : ''}\n {formatRelativeTime(info.lastActivityAt ?? info.createdAt)}\n </span>\n <span className=\"min-w-0 flex-1\" />\n {steps.length > 0 ? (\n <StepToggle\n expanded={open}\n running={runningSteps(steps)}\n total={steps.length}\n noun={steps[0]!.noun}\n onToggle={() => setOpen(!open)}\n />\n ) : null}\n {actions}\n </div>\n </div>\n\n {open && steps.length > 0 ? (\n <div className=\"flex flex-col\">\n {steps.map((step: Step) => (\n <StepRow key={step.key} step={step} active={step.key === activeStepKey} onSelect={step.onSelect} />\n ))}\n </div>\n ) : null}\n </div>\n )\n}\n\nfunction Gutter({ children }: { children: ReactNode }) {\n return <span className=\"flex size-4 shrink-0 items-center justify-center\">{children}</span>\n}\n\nfunction NameEditor({ initial, onCommit, onCancel }: { initial: string; onCommit: (title: string) => void; onCancel: () => void }) {\n const [value, setValue] = useState(initial)\n const ref = useRef<HTMLInputElement>(null)\n useEffect(() => {\n ref.current?.focus()\n ref.current?.select()\n }, [])\n useEffect(() => {\n const onWindowFocus = () => {\n ref.current?.focus()\n ref.current?.select()\n }\n window.addEventListener('focus', onWindowFocus)\n return () => window.removeEventListener('focus', onWindowFocus)\n }, [])\n return (\n <input\n ref={ref}\n value={value}\n spellCheck={false}\n placeholder=\"Session name\"\n aria-label=\"Session name\"\n onClick={(e) => e.stopPropagation()}\n onDoubleClick={(e) => e.stopPropagation()}\n onChange={(e) => setValue(e.target.value)}\n onBlur={() => {\n // Guarded on `document.hasFocus()`: selecting a session focuses another surface (in the extension, another view), and an unguarded blur closes the editor in the frame it appeared.\n if (document.hasFocus()) {\n onCommit(value.trim())\n }\n }}\n onKeyDown={(e) => {\n e.stopPropagation()\n if (e.key === 'Enter') {\n onCommit(value.trim())\n } else if (e.key === 'Escape') {\n onCancel()\n }\n }}\n className={cn(\n '-my-0.5 min-w-0 flex-1 rounded-sm border border-ring bg-bg px-1 py-px',\n 'text-body-sm leading-5 text-fg-1 outline-none',\n )}\n />\n )\n}\n","import { useMemo, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { Eraser, Layers, Pencil, Search, SearchX, Trash2, X } from 'lucide-react'\nimport {\n STATE_LABELS,\n STATE_ORDER,\n adaptersOf,\n clearFilters,\n filterRows,\n groupRows,\n hasFacetFilter,\n projectsOf,\n subsetSummary,\n} from '@workerdeck/protocol'\nimport type { GroupBy, SessionRow, SessionState, SortBy, ViewConfig, WorkspaceScope } from '@workerdeck/protocol'\nimport { Button } from '../ui/Button.tsx'\nimport { Empty } from '../ui/Empty.tsx'\nimport { Input } from '../ui/Input.tsx'\nimport { Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue } from '../ui/Select.tsx'\nimport { ProjectIcon } from './ProjectIcon.tsx'\nimport { SessionItem } from './SessionItem.tsx'\nimport { cn } from '../../lib/utils.ts'\n\nexport { SessionStatusIcon } from './SessionStatusIcon.tsx'\n\nexport interface SessionBrowserProps {\n rows: SessionRow[]\n config: ViewConfig\n onConfigChange: (config: ViewConfig) => void\n scope?: WorkspaceScope\n activeId?: string\n activeSubagentId?: string\n onSelect?: (row: SessionRow) => void\n onDelete?: (row: SessionRow) => void\n onRename?: (row: SessionRow, title: string) => void\n onClearContext?: (row: SessionRow) => void\n onSelectSubagent?: (row: SessionRow, toolUseId: string) => void\n onRevealStep?: (row: SessionRow, toolUseId: string) => void\n emptyState?: React.ReactNode\n showControls?: boolean\n projectIcons?: Record<string, string>\n className?: string\n}\n\nexport function rowShapeClass(active: boolean): string {\n return cn('px-2 py-1.5 hover:bg-row-hover', active ? 'mr-1 ml-0 rounded-r-md border-l-4 border-l-accent' : 'mx-1 rounded-md')\n}\n\nexport function SessionBrowser({\n rows,\n config,\n onConfigChange,\n scope,\n activeId,\n activeSubagentId,\n onSelect,\n onDelete,\n onRename,\n onClearContext,\n onSelectSubagent,\n onRevealStep,\n emptyState,\n showControls = true,\n projectIcons,\n className,\n}: SessionBrowserProps) {\n const visible = useMemo(() => filterRows(rows, config, scope), [rows, config, scope])\n const groups = useMemo(() => groupRows(visible, config), [visible, config])\n const subset = subsetSummary(config, scope, visible.length, rows.length)\n const adapters = useMemo(() => adaptersOf(rows), [rows])\n const projects = useMemo(() => projectsOf(rows), [rows])\n const gateways = useMemo(() => {\n const seen = new Map<string, string>()\n for (const row of rows) {\n seen.set(row.hostId, row.hostName)\n }\n return [...seen].map(([id, name]) => ({ id, name }))\n }, [rows])\n\n const set = (patch: Partial<ViewConfig>) => onConfigChange({ ...config, ...patch })\n\n return (\n <div data-slot=\"session-browser\" className={cn('flex flex-col gap-3', className)}>\n <div className={cn('flex flex-col gap-1.5 px-2', !showControls && 'hidden')}>\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-fg-4\" />\n <Input\n value={config.search}\n onChange={(e) => set({ search: e.target.value })}\n placeholder=\"Search sessions\"\n aria-label=\"Search sessions\"\n className=\"pl-8\"\n />\n </div>\n <FilterRow label=\"State\">\n <FacetSelect\n label=\"State\"\n value={config.states}\n options={STATE_ORDER.map((s) => ({ value: s, label: STATE_LABELS[s] }))}\n onChange={(states) => set({ states: states as SessionState[] })}\n />\n </FilterRow>\n {adapters.length > 1 ? (\n <FilterRow label=\"Engine\">\n <FacetSelect\n label=\"Engine\"\n value={config.adapters}\n options={adapters.map((a) => ({ value: a, label: a }))}\n onChange={(adapters) => set({ adapters })}\n />\n </FilterRow>\n ) : null}\n {gateways.length > 1 ? (\n <FilterRow label=\"Gateway\">\n <FacetSelect\n label=\"Gateway\"\n value={config.gateways}\n options={gateways.map((g) => ({ value: g.id, label: g.name }))}\n onChange={(gateways) => set({ gateways })}\n />\n </FilterRow>\n ) : null}\n {projects.length > 1 ? (\n <FilterRow label=\"Project\">\n <FacetSelect\n label=\"Project\"\n value={config.projects ?? []}\n options={projects.map((p) => ({ value: p.key, label: p.label }))}\n onChange={(next) => set({ projects: next })}\n />\n </FilterRow>\n ) : null}\n <FilterRow label=\"Group\">\n <OneOfSelect\n label=\"Group\"\n value={config.groupBy}\n options={[\n { value: 'none', label: 'No grouping' },\n { value: 'state', label: 'By state' },\n { value: 'adapter', label: 'By engine' },\n ...(projects.length > 1 ? [{ value: 'project' as const, label: 'By project' }] : []),\n ...(gateways.length > 1 ? [{ value: 'gateway' as const, label: 'By gateway' }] : []),\n ]}\n onChange={(groupBy) => set({ groupBy: groupBy as GroupBy })}\n />\n </FilterRow>\n <FilterRow label=\"Sort\">\n <OneOfSelect\n label=\"Sort\"\n value={config.sortBy}\n options={[\n { value: 'recent', label: 'Recent' },\n { value: 'name', label: 'Name' },\n { value: 'state', label: 'State' },\n ...(projects.length > 1 ? [{ value: 'project' as const, label: 'Project' }] : []),\n ...(gateways.length > 1 ? [{ value: 'gateway' as const, label: 'Gateway' }] : []),\n ]}\n onChange={(sortBy) => set({ sortBy: sortBy as SortBy })}\n />\n </FilterRow>\n </div>\n\n {subset ? (\n <div className=\"flex items-center gap-2 px-3 text-label text-fg-4\">\n <span>\n {subset.shown} of {subset.total}\n {subset.causes.length ? ` · ${subset.causes.join(' · ')}` : null}\n </span>\n <button\n type=\"button\"\n className=\"text-fg-3 underline underline-offset-2 hover:text-fg-1\"\n onClick={() => onConfigChange(clearFilters(config))}\n >\n Show all\n </button>\n </div>\n ) : null}\n\n {rows.length === 0 ? (\n (emptyState ?? <Empty icon={<Layers />} title=\"No sessions yet\" />)\n ) : visible.length === 0 ? (\n hasFacetFilter(config) ? (\n <Empty\n icon={<SearchX />}\n title=\"No matches\"\n description=\"No session matches the current search and filters.\"\n action=\"Clear filters\"\n onAction={() => onConfigChange(clearFilters(config))}\n />\n ) : (\n <Empty icon={<Layers />} title=\"Nothing here\" description=\"No session to show.\" />\n )\n ) : (\n <div className=\"flex flex-col gap-4 px-1\">\n {groups.map((group) => (\n <div key={group.key} className=\"flex flex-col gap-1\">\n {config.groupBy !== 'none' && group.label ? (\n <div className=\"flex items-center gap-2 px-2 text-label font-medium text-fg-4\">\n {config.groupBy === 'project' ? (\n <ProjectIcon icon={group.rows[0]?.info.project?.icon} src={iconSrcOf(group.rows[0], projectIcons)} name={group.label} />\n ) : null}\n <span className=\"uppercase tracking-wide\">{group.label}</span>\n <span className=\"text-fg-4/70\">{group.rows.length}</span>\n </div>\n ) : null}\n {group.rows.map((row) => (\n <SessionRowItem\n key={`${row.hostId}:${row.info.id}`}\n row={row}\n active={row.info.id === activeId}\n activeSubagentId={activeSubagentId}\n showGateway={gateways.length > 1 && config.groupBy !== 'gateway'}\n showProject={config.groupBy !== 'project'}\n projectIcons={projectIcons}\n onSelect={onSelect}\n onDelete={onDelete}\n onRename={onRename}\n onClearContext={onClearContext}\n onSelectSubagent={onSelectSubagent}\n onRevealStep={onRevealStep}\n />\n ))}\n </div>\n ))}\n </div>\n )}\n </div>\n )\n}\n\nfunction iconSrcOf(row: SessionRow | undefined, icons: Record<string, string> | undefined): string | undefined {\n const icon = row?.info.project?.icon\n return icon?.type === 'image' ? icons?.[icon.hash] : undefined\n}\n\ninterface SessionRowItemProps {\n row: SessionRow\n active?: boolean\n activeSubagentId?: string\n showGateway?: boolean\n showProject?: boolean\n projectIcons?: Record<string, string>\n onSelect?: (row: SessionRow) => void\n onDelete?: (row: SessionRow) => void\n onRename?: (row: SessionRow, title: string) => void\n onClearContext?: (row: SessionRow) => void\n onSelectSubagent?: (row: SessionRow, toolUseId: string) => void\n onRevealStep?: (row: SessionRow, toolUseId: string) => void\n}\n\nfunction SessionRowItem({\n row,\n active,\n activeSubagentId,\n showGateway,\n showProject = true,\n projectIcons,\n onSelect,\n onDelete,\n onRename,\n onClearContext,\n onSelectSubagent,\n onRevealStep,\n}: SessionRowItemProps) {\n const { info } = row\n const [editing, setEditing] = useState(false)\n\n return (\n <SessionItem\n row={row}\n active={active === true}\n activeStepKey={activeSubagentId}\n showGateway={showGateway}\n showProject={showProject}\n projectIcons={projectIcons}\n onSelect={() => onSelect?.(row)}\n onSelectSubagent={onSelectSubagent ? (id) => onSelectSubagent(row, id) : undefined}\n onRevealStep={onRevealStep ? (id) => onRevealStep(row, id) : undefined}\n onRename={onRename ? (title) => onRename(row, title) : undefined}\n renameOn=\"external\"\n editing={editing}\n onEditingChange={setEditing}\n actions={\n <>\n {onRename && !editing ? (\n <RowAction label=\"Rename session\" onClick={() => setEditing(true)}>\n <Pencil className=\"size-3 text-fg-3\" />\n </RowAction>\n ) : null}\n {onClearContext && info.capabilities?.clearContext ? (\n <RowAction\n label=\"Clear context\"\n title=\"Clear the conversation — the session keeps running and the old conversation stays resumable\"\n onClick={() => onClearContext(row)}\n >\n <Eraser className=\"size-3 text-fg-3\" />\n </RowAction>\n ) : null}\n {onDelete ? (\n <RowAction label=\"Close session\" onClick={() => onDelete(row)}>\n <Trash2 className=\"size-3 text-fg-3\" />\n </RowAction>\n ) : null}\n </>\n }\n />\n )\n}\n\nfunction RowAction({ label, title, onClick, children }: { label: string; title?: string; onClick: () => void; children: ReactNode }) {\n return (\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={label}\n title={title ?? label}\n className=\"size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100\"\n onClick={(e) => {\n e.stopPropagation()\n onClick()\n }}\n >\n {children}\n </Button>\n )\n}\n\nfunction FilterRow({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"flex items-center gap-2\">\n <span aria-hidden className=\"w-14 shrink-0 truncate text-label text-fg-3\">\n {label}\n </span>\n <div className=\"min-w-0 flex-1\">{children}</div>\n </div>\n )\n}\n\nfunction FacetSelect({\n label,\n value,\n options,\n onChange,\n}: {\n label: string\n value: string[]\n options: { value: string; label: string }[]\n onChange: (value: string[]) => void\n}) {\n return (\n <div className=\"flex items-center gap-1\">\n <Select multiple value={value} onValueChange={(v) => onChange(v as string[])}>\n <SelectTrigger aria-label={label} className=\"min-w-0 flex-1\">\n <SelectValue>\n {value.length === 0\n ? 'All'\n : value.length === 1\n ? (options.find((o) => o.value === value[0])?.label ?? 'All')\n : `${value.length} selected`}\n </SelectValue>\n </SelectTrigger>\n <SelectContent>\n {options.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n <SelectItemText>{option.label}</SelectItemText>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {value.length > 0 ? (\n <Button variant=\"ghost\" size=\"icon-sm\" aria-label={`Clear ${label}`} onClick={() => onChange([])}>\n <X className=\"size-3 text-fg-4\" />\n </Button>\n ) : null}\n </div>\n )\n}\n\nfunction OneOfSelect({\n label,\n value,\n options,\n onChange,\n}: {\n label: string\n value: string\n options: readonly { value: string; label: string }[]\n onChange: (value: string) => void\n}) {\n return (\n <Select value={value} onValueChange={(v) => onChange(v as string)}>\n <SelectTrigger aria-label={label} className=\"w-full min-w-0\">\n <SelectValue>{options.find((o) => o.value === value)?.label ?? label}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {options.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n <SelectItemText>{option.label}</SelectItemText>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )\n}\n"],"mappings":"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"}
|