@workerdeck/ui 0.13.0 → 0.16.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.
Files changed (69) hide show
  1. package/README.md +72 -0
  2. package/build/{SessionPanel-CZMA44NM.d.mts → SessionPanel-B9CHoq8x.d.mts} +213 -27
  3. package/build/{SessionPanel-CKQa4i0Y.mjs → SessionPanel-DII9MmQ8.mjs} +5192 -2542
  4. package/build/SessionPanel-DII9MmQ8.mjs.map +1 -0
  5. package/build/{format-ljc3lKpA.d.mts → format-DfI_je9S.d.mts} +1 -1
  6. package/build/format.d.mts +39 -4
  7. package/build/format.mjs +2 -118
  8. package/build/index.d.mts +494 -45
  9. package/build/index.mjs +182 -24
  10. package/build/index.mjs.map +1 -1
  11. package/build/status-Ydzi7n6j.mjs +143 -0
  12. package/build/status-Ydzi7n6j.mjs.map +1 -0
  13. package/build/workspace.d.mts +13 -1
  14. package/build/workspace.mjs +111 -5
  15. package/build/workspace.mjs.map +1 -1
  16. package/package.json +14 -7
  17. package/src/components/agent/Composer.tsx +251 -84
  18. package/src/components/agent/Conversation.tsx +12 -12
  19. package/src/components/agent/FileCard.tsx +0 -26
  20. package/src/components/agent/FileTree.tsx +9 -8
  21. package/src/components/agent/Loader.tsx +22 -72
  22. package/src/components/agent/Message.tsx +11 -43
  23. package/src/components/agent/PermissionPrompt.tsx +0 -92
  24. package/src/components/agent/QuestionPrompt.tsx +0 -122
  25. package/src/components/agent/Reasoning.tsx +5 -19
  26. package/src/components/agent/Response.tsx +1 -132
  27. package/src/components/agent/SessionBrowser.tsx +35 -25
  28. package/src/components/agent/SessionPanel.tsx +312 -63
  29. package/src/components/agent/SessionWorkspace.tsx +36 -0
  30. package/src/components/agent/StatusBar.tsx +70 -15
  31. package/src/components/agent/ToolCallCard.tsx +17 -111
  32. package/src/components/agent/Transcript.tsx +710 -203
  33. package/src/components/agent/UsageDialog.tsx +20 -106
  34. package/src/components/agent/UsageMeters.tsx +133 -0
  35. package/src/components/agent/pulse.tsx +3 -2
  36. package/src/components/agent/transcript-rows.ts +82 -0
  37. package/src/components/agent/transcript-variant.tsx +43 -51
  38. package/src/components/agent/use-height-epoch.ts +60 -0
  39. package/src/components/agent/use-path-links.ts +147 -0
  40. package/src/components/agent/use-transcript-jumps.ts +190 -0
  41. package/src/components/prompt-area/cursor-helpers.ts +65 -0
  42. package/src/components/prompt-area/use-prompt-area.ts +16 -10
  43. package/src/components/terminal/PermissionPrompt.tsx +119 -0
  44. package/src/components/terminal/QuestionPrompt.tsx +322 -0
  45. package/src/components/terminal/StatusLine.tsx +159 -0
  46. package/src/components/terminal/TerminalTranscript.tsx +147 -0
  47. package/src/components/terminal/affordances.tsx +118 -0
  48. package/src/components/terminal/diff.tsx +130 -0
  49. package/src/components/terminal/height.ts +727 -0
  50. package/src/components/terminal/items.tsx +449 -0
  51. package/src/components/terminal/markdown.tsx +191 -0
  52. package/src/components/terminal/press.tsx +120 -0
  53. package/src/components/terminal/prompt.tsx +343 -0
  54. package/src/components/terminal/result-preview.ts +72 -0
  55. package/src/components/terminal/row.tsx +132 -0
  56. package/src/components/terminal/scrubber.tsx +663 -0
  57. package/src/components/terminal/surface.tsx +80 -0
  58. package/src/components/terminal/tool-run.ts +91 -0
  59. package/src/components/ui/Badge.tsx +6 -1
  60. package/src/components/ui/Empty.tsx +56 -0
  61. package/src/components/ui/Splitter.tsx +14 -0
  62. package/src/index.ts +36 -0
  63. package/src/lib/status.ts +59 -3
  64. package/src/lib/tool-icon.ts +14 -0
  65. package/src/styles/terminal.css +1011 -0
  66. package/src/styles/theme.css +73 -0
  67. package/build/SessionPanel-CKQa4i0Y.mjs.map +0 -1
  68. package/build/format.mjs.map +0 -1
  69. package/src/components/agent/line-prompt.tsx +0 -249
package/build/index.mjs CHANGED
@@ -1,8 +1,9 @@
1
- import { $ as Tip, A as SkillsDialog, B as commandTrigger, C as isMutatingTool, Ct as Button, D as permissionModeChoices, E as PermissionModeSelect, F as skillPrompt, G as plainTextToSegments, H as mentionTrigger, I as TranscriptDensityProvider, J as Splitter, K as segmentsToPlainText, L as TranscriptVariantProvider, M as HostFilesDialog, N as ContextDialog, O as permissionModeMeta, P as Composer, Q as copyText, R as useTranscriptDensity, S as Response, St as badgeVariants, T as PERMISSION_MODES, Tt as cn, U as usePromptAreaState, V as hashtagTrigger, W as PromptArea, X as Spinner, Y as CodeBlock, Z as CopyButton, _ as SessionInfoDialog, _t as SelectItemText, a as SessionEmptyState, at as DialogContent, b as parseUserQuestions, bt as Input, c as Message, ct as DialogTrigger, d as FileCard, dt as MenuItem, et as TooltipContent, f as Conversation, ft as MenuSeparator, g as STATUS_META, gt as SelectItem, h as StatusBar, ht as SelectContent, i as ToolCallCard, it as DialogClose, j as McpDialog, k as ModelSelect, l as MessageContent, lt as Menu, m as ConversationScrollButton, mt as Select, n as UsageDialog, nt as Dialog, o as Reasoning, ot as DialogHeader, p as ConversationContent, pt as MenuTrigger, q as ProgressRing, r as Transcript, rt as DialogBody, s as PromptTokenText, st as DialogRow, t as SessionPanel, tt as TooltipProvider, u as Loader, ut as MenuContent, v as QUESTION_BEHAVIORS, vt as SelectTrigger, w as toolIcon, wt as buttonVariants, x as PermissionPrompt, xt as Badge, y as QuestionPrompt, yt as SelectValue, z as useTranscriptVariant } from "./SessionPanel-CKQa4i0Y.mjs";
1
+ import { $ as TranscriptDensityProvider, A as PermissionModeSelect, At as MenuTrigger, B as TerminalSurface, Bt as Button, C as TerminalQuestionPrompt, Ct as DialogHeader, D as parseUserQuestions, Dt as MenuContent, E as QuestionPrompt, Et as Menu, F as McpDialog, Ft as SelectTrigger, G as Blank, H as TerminalDiff, Ht as cn, I as HostFilesDialog, It as SelectValue, J as CopyAction, K as Ink, L as ContextDialog, Lt as Input, M as permissionModeMeta, Mt as SelectContent, N as ModelSelect, Nt as SelectItem, O as PermissionPrompt, Ot as MenuItem, P as SkillsDialog, Pt as SelectItemText, Q as toolIcon, R as Composer, Rt as Badge, S as SessionInfoDialog, St as DialogContent, T as QUESTION_BEHAVIORS, Tt as DialogTrigger, U as previewPatch, V as TerminalMarkdown, Vt as buttonVariants, W as Band, X as useAffordances, Y as WithActions, Z as isMutatingTool, _ as Conversation, _t as TooltipContent, a as Transcript, at as mentionTrigger, b as StatusBar, bt as DialogBody, c as ToolCallCard, ct as plainTextToSegments, d as Response, dt as Splitter, et as TranscriptVariantProvider, f as PromptTokenText, ft as CodeBlock, g as FileCard, gt as Tip, h as Loader, ht as copyText, i as useMinuteClock, it as hashtagTrigger, j as permissionModeChoices, jt as Select, k as PERMISSION_MODES, kt as MenuSeparator, l as SessionEmptyState, lt as segmentsToPlainText, m as MessageContent, mt as CopyButton, n as UsageDialog, nt as useTranscriptVariant, o as TerminalItemView, ot as usePromptAreaState, p as Message, pt as Spinner, q as Row, r as UsageMeters, rt as commandTrigger, s as TerminalTranscript, st as PromptArea, t as SessionPanel, tt as useTranscriptDensity, u as Reasoning, ut as ProgressRing, v as ConversationContent, vt as TooltipProvider, w as TerminalPermissionPrompt, wt as DialogRow, x as STATUS_META, xt as DialogClose, y as ConversationScrollButton, yt as Dialog, z as skillPrompt, zt as badgeVariants } from "./SessionPanel-DII9MmQ8.mjs";
2
2
  import { a as formatDuration, c as formatRelativeTime, d as rateLimitWindowSeconds, f as toolInputPreview, i as formatCountdown, l as formatTokens, n as formatBytes, o as formatRateLimitWindow, r as formatCost, s as formatRateLimitWindowLong, t as formatAgoPrecise, u as friendlyModel } from "./format-DqR56Y8l.mjs";
3
+ import { a as statusPresentation, c as windowLabel, i as modelLabel, o as tightestWindow, r as meterSeverity, t as contextSeverity } from "./status-Ydzi7n6j.mjs";
3
4
  import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
4
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
5
- import { BellRing, CircleAlert, CircleSlash, Moon, PauseCircle, Pencil, Search, Trash2, X } from "lucide-react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { BellRing, CircleAlert, CircleSlash, Layers, Moon, PauseCircle, Pencil, Search, SearchX, Trash2, X } from "lucide-react";
6
7
  import { AlertDialog as AlertDialog$1 } from "@base-ui/react/alert-dialog";
7
8
  import { Toaster as Toaster$1, toast } from "sonner";
8
9
  import { STATE_LABELS, STATE_ORDER, adaptersOf, clearFilters, filterRows, groupRows, hasFacetFilter, sessionLabel, subsetSummary } from "@workerdeck/protocol";
@@ -77,6 +78,57 @@ function Toaster() {
77
78
  });
78
79
  }
79
80
  //#endregion
81
+ //#region src/components/ui/Empty.tsx
82
+ /**
83
+ * A view with nothing in it: icon, title, description, and at most one action.
84
+ *
85
+ * Centered and given room, because an empty view is the first thing a new
86
+ * install shows and a flush-left sentence in the top corner reads like a bug
87
+ * report. It lives here rather than in one client because every panel that can
88
+ * be empty should be empty the same way — the extension's views and the
89
+ * dashboard's four sidebars are the same shape of thing.
90
+ *
91
+ * **At most one action, and never one the view header already offers.** Creating
92
+ * a session and adding a gateway are the `+` in the native title bar, exclusively
93
+ * — so those empty states point at it in words rather than growing a second
94
+ * button that does the same thing two inches lower. A button here is for the way
95
+ * out of a state the header has no answer to: clearing a filter, widening a
96
+ * scope.
97
+ */
98
+ function Empty({ icon, title, description, action, onAction }) {
99
+ return /* @__PURE__ */ jsxs("div", {
100
+ className: "flex flex-col items-center gap-2 px-5 py-8 text-center",
101
+ children: [
102
+ /* @__PURE__ */ jsx("div", {
103
+ className: "text-fg-4 opacity-60 [&_svg]:size-7",
104
+ children: icon
105
+ }),
106
+ /* @__PURE__ */ jsx("p", {
107
+ className: "text-body-sm font-medium text-fg-2",
108
+ children: title
109
+ }),
110
+ description ? /* @__PURE__ */ jsx("p", {
111
+ className: "max-w-[26ch] text-balance text-label leading-relaxed text-fg-4",
112
+ children: description
113
+ }) : null,
114
+ action && onAction ? /* @__PURE__ */ jsx(Button, {
115
+ variant: "outline",
116
+ size: "sm",
117
+ className: "mt-1",
118
+ onClick: onAction,
119
+ children: action
120
+ }) : null
121
+ ]
122
+ });
123
+ }
124
+ /** A key or icon named inline in an empty state's description — "use the + above". */
125
+ function EmptyKey({ children }) {
126
+ return /* @__PURE__ */ jsx("span", {
127
+ className: "rounded border border-border px-1 font-mono text-fg-3",
128
+ children
129
+ });
130
+ }
131
+ //#endregion
80
132
  //#region src/components/prompt-area/segment-helpers.ts
81
133
  /** Returns `true` when the segment array is empty or contains only whitespace text. */
82
134
  function isSegmentsEmpty(segments) {
@@ -88,6 +140,108 @@ function getChipsByTrigger(segments, trigger) {
88
140
  return segments.filter((seg) => seg.type === "chip" && seg.trigger === trigger);
89
141
  }
90
142
  //#endregion
143
+ //#region src/components/terminal/StatusLine.tsx
144
+ /**
145
+ * The session's readings, as one line.
146
+ *
147
+ * The styled bar puts each of these in its own affordance — a progress ring, a
148
+ * badge, a tooltip — which is right for a dashboard and wrong here for the usual
149
+ * reason: a terminal has one line height, and five widgets of five different
150
+ * heights is five different rhythms in a row that is supposed to be quiet. So
151
+ * they are words, `·`-separated, exactly as the CLI's own status line writes
152
+ * them.
153
+ *
154
+ * The readings themselves come from the same helpers the styled bar uses
155
+ * (`lib/status.ts`), and that matters more than it looks: `statusPresentation`
156
+ * is where the rule lives that **a dropped socket outranks the session status**,
157
+ * because a status held over a dead socket is a stale reading being presented as
158
+ * a live one. Re-deriving that here would be a second answer to a question that
159
+ * already has one.
160
+ */
161
+ /** Severity → tone. The 80/95 thresholds are `lib/status.ts`'s, not new ones. */
162
+ const SEVERITY_TONE = {
163
+ none: "dim",
164
+ warning: "yellow",
165
+ error: "red"
166
+ };
167
+ /** One reading. A button only when it leads somewhere — a segment that looks
168
+ * pressable and does nothing is worse than plain text. */
169
+ function Reading({ tone, onPress, label, children }) {
170
+ if (!onPress) return /* @__PURE__ */ jsx(Ink, {
171
+ tone,
172
+ children
173
+ });
174
+ return /* @__PURE__ */ jsx("button", {
175
+ type: "button",
176
+ className: "term-reading",
177
+ onClick: onPress,
178
+ title: label,
179
+ children: /* @__PURE__ */ jsx(Ink, {
180
+ tone,
181
+ children
182
+ })
183
+ });
184
+ }
185
+ function TerminalStatusLine({ state, rateLimits, connection, onOpenStatus, onOpenContext, onOpenUsage }) {
186
+ const presentation = statusPresentation({
187
+ status: state.status,
188
+ connection
189
+ });
190
+ const usage = state.contextUsage;
191
+ const window = tightestWindow(rateLimits ?? state.rateLimits);
192
+ const model = modelLabel({
193
+ model: state.model,
194
+ models: state.models ?? []
195
+ });
196
+ const parts = [/* @__PURE__ */ jsx(Reading, {
197
+ tone: SEVERITY_TONE[presentation.severity],
198
+ onPress: onOpenStatus,
199
+ label: "Session status",
200
+ children: presentation.label
201
+ }, "status")];
202
+ if (usage) parts.push(/* @__PURE__ */ jsxs(Reading, {
203
+ tone: SEVERITY_TONE[contextSeverity(usage)],
204
+ onPress: onOpenContext,
205
+ label: "Context window",
206
+ children: [
207
+ Math.round(usage.percentage),
208
+ "% ctx (",
209
+ formatTokens(usage.totalTokens),
210
+ ")"
211
+ ]
212
+ }, "context"));
213
+ if (window) parts.push(/* @__PURE__ */ jsxs(Reading, {
214
+ tone: SEVERITY_TONE[meterSeverity(window.info.utilization)],
215
+ onPress: onOpenUsage,
216
+ label: "Plan usage",
217
+ children: [
218
+ Math.round(window.info.utilization ?? 0),
219
+ "% ",
220
+ windowLabel(window.key)
221
+ ]
222
+ }, "usage"));
223
+ if (state.totalCostUsd > 0) parts.push(/* @__PURE__ */ jsx(Ink, {
224
+ tone: "faint",
225
+ children: formatCost(state.totalCostUsd)
226
+ }, "cost"));
227
+ if (model) parts.push(/* @__PURE__ */ jsx(Ink, {
228
+ tone: "faint",
229
+ children: model
230
+ }, "model"));
231
+ if (state.permissionMode && state.permissionMode !== "default") parts.push(/* @__PURE__ */ jsx(Ink, {
232
+ tone: "yellow",
233
+ children: state.permissionMode
234
+ }, "mode"));
235
+ return /* @__PURE__ */ jsx(Row, {
236
+ "data-slot": "status-line",
237
+ tone: "dim",
238
+ children: parts.map((part, index) => /* @__PURE__ */ jsxs("span", { children: [index > 0 ? /* @__PURE__ */ jsx(Ink, {
239
+ tone: "faint",
240
+ children: " · "
241
+ }) : null, part] }, index))
242
+ });
243
+ }
244
+ //#endregion
91
245
  //#region src/components/agent/SessionList.tsx
92
246
  function SessionListItem({ session, active, onSelect, onDelete }) {
93
247
  const meta = STATUS_META[session.status];
@@ -319,16 +473,20 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
319
473
  children: "Show all"
320
474
  })]
321
475
  }) : null,
322
- rows.length === 0 ? emptyState ?? /* @__PURE__ */ jsx(Empty, { children: "No sessions yet." }) : visible.length === 0 ? /* @__PURE__ */ jsx(Empty, { children: hasFacetFilter(config) ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
323
- "No sessions match.",
324
- " ",
325
- /* @__PURE__ */ jsx("button", {
326
- type: "button",
327
- className: "underline underline-offset-2 hover:text-fg-1",
328
- onClick: () => onConfigChange(clearFilters(config)),
329
- children: "Clear filters"
330
- })
331
- ] }) : "No sessions here." }) : /* @__PURE__ */ jsx("div", {
476
+ rows.length === 0 ? emptyState ?? /* @__PURE__ */ jsx(Empty, {
477
+ icon: /* @__PURE__ */ jsx(Layers, {}),
478
+ title: "No sessions yet"
479
+ }) : visible.length === 0 ? hasFacetFilter(config) ? /* @__PURE__ */ jsx(Empty, {
480
+ icon: /* @__PURE__ */ jsx(SearchX, {}),
481
+ title: "No matches",
482
+ description: "No session matches the current search and filters.",
483
+ action: "Clear filters",
484
+ onAction: () => onConfigChange(clearFilters(config))
485
+ }) : /* @__PURE__ */ jsx(Empty, {
486
+ icon: /* @__PURE__ */ jsx(Layers, {}),
487
+ title: "Nothing here",
488
+ description: "No session to show."
489
+ }) : /* @__PURE__ */ jsx("div", {
332
490
  className: "flex flex-col gap-4",
333
491
  children: groups.map((group) => /* @__PURE__ */ jsxs("div", {
334
492
  className: "flex flex-col gap-1",
@@ -354,12 +512,6 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
354
512
  ]
355
513
  });
356
514
  }
357
- function Empty({ children }) {
358
- return /* @__PURE__ */ jsx("div", {
359
- className: "px-3 py-6 text-center text-body-sm text-fg-4",
360
- children
361
- });
362
- }
363
515
  function SessionRowItem({ row, active, showGateway, onSelect, onDelete, onRename }) {
364
516
  const { info } = row;
365
517
  const [editing, setEditing] = useState(false);
@@ -374,6 +526,7 @@ function SessionRowItem({ row, active, showGateway, onSelect, onDelete, onRename
374
526
  return /* @__PURE__ */ jsxs("div", {
375
527
  "data-slot": "session-row",
376
528
  "data-active": active || void 0,
529
+ onClick: () => !editing && onSelect?.(row),
377
530
  className: cn("group flex cursor-pointer flex-col gap-0.5 text-left transition-colors", rowShapeClass(active === true)),
378
531
  children: [/* @__PURE__ */ jsxs("div", {
379
532
  className: "flex items-center gap-1.5",
@@ -387,7 +540,6 @@ function SessionRowItem({ row, active, showGateway, onSelect, onDelete, onRename
387
540
  onCancel: () => setEditing(false)
388
541
  }) : /* @__PURE__ */ jsx("button", {
389
542
  type: "button",
390
- onClick: () => onSelect?.(row),
391
543
  className: cn("min-w-0 flex-1 truncate text-left text-body-sm outline-none", active ? "font-medium text-fg-1" : "text-fg-2"),
392
544
  children: sessionLabel(info)
393
545
  }),
@@ -407,7 +559,7 @@ function SessionRowItem({ row, active, showGateway, onSelect, onDelete, onRename
407
559
  children: [
408
560
  /* @__PURE__ */ jsx("button", {
409
561
  type: "button",
410
- onClick: () => !editing && onSelect?.(row),
562
+ tabIndex: -1,
411
563
  className: "min-w-0 flex-1 truncate text-left font-mono outline-none",
412
564
  children: details.join(" · ")
413
565
  }),
@@ -416,7 +568,10 @@ function SessionRowItem({ row, active, showGateway, onSelect, onDelete, onRename
416
568
  size: "icon-sm",
417
569
  "aria-label": "Rename session",
418
570
  className: "size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100",
419
- onClick: () => setEditing(true),
571
+ onClick: (e) => {
572
+ e.stopPropagation();
573
+ setEditing(true);
574
+ },
420
575
  children: /* @__PURE__ */ jsx(Pencil, { className: "size-3 text-fg-3" })
421
576
  }) : null,
422
577
  onDelete ? /* @__PURE__ */ jsx(Button, {
@@ -424,7 +579,10 @@ function SessionRowItem({ row, active, showGateway, onSelect, onDelete, onRename
424
579
  size: "icon-sm",
425
580
  "aria-label": "Close session",
426
581
  className: "size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100",
427
- onClick: () => onDelete(row),
582
+ onClick: (e) => {
583
+ e.stopPropagation();
584
+ onDelete(row);
585
+ },
428
586
  children: /* @__PURE__ */ jsx(Trash2, { className: "size-3 text-fg-3" })
429
587
  }) : null
430
588
  ]
@@ -612,6 +770,6 @@ function EngineIcon({ engine, model, className }) {
612
770
  });
613
771
  }
614
772
  //#endregion
615
- export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, Button, Card, CardContent, CardHeader, CardTitle, CodeBlock, Composer, ContextDialog, Conversation, ConversationContent, ConversationScrollButton, CopyButton, Dialog, DialogBody, DialogClose, DialogContent, DialogHeader, DialogRow, DialogTrigger, EngineIcon, FileCard, HostFilesDialog, Input, Loader, McpDialog, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Message, MessageContent, ModelSelect, PERMISSION_MODES, PermissionModeSelect, PermissionPrompt, ProgressRing, PromptArea, PromptTokenText, QUESTION_BEHAVIORS, QuestionPrompt, Reasoning, Response, STATUS_META, Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue, SessionBrowser, SessionEmptyState, SessionInfoDialog, SessionList, SessionListItem, SessionPanel, SessionStatusIcon, SkillsDialog, Spinner, Splitter, StatusBar, Textarea, Tip, Toaster, ToolCallCard, TooltipContent, TooltipProvider, Transcript, TranscriptDensityProvider, TranscriptVariantProvider, UsageDialog, badgeVariants, buttonVariants, cn, commandTrigger, copyText, formatAgoPrecise, formatBytes, formatCost, formatCountdown, formatDuration, formatRateLimitWindow, formatRateLimitWindowLong, formatRelativeTime, formatTokens, friendlyModel, getChipsByTrigger, hashtagTrigger, isMutatingTool, isSegmentsEmpty, mentionTrigger, parseUserQuestions, permissionModeChoices, permissionModeMeta, plainTextToSegments, rateLimitWindowSeconds, rowShapeClass, segmentsToPlainText, skillPrompt, toast, toolIcon, toolInputPreview, usePromptAreaState, useTranscriptDensity, useTranscriptVariant };
773
+ export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, Band, Blank, Button, Card, CardContent, CardHeader, CardTitle, CodeBlock, Composer, ContextDialog, Conversation, ConversationContent, ConversationScrollButton, CopyAction, CopyButton, Dialog, DialogBody, DialogClose, DialogContent, DialogHeader, DialogRow, DialogTrigger, Empty, EmptyKey, EngineIcon, FileCard, HostFilesDialog, Ink, Input, Loader, McpDialog, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Message, MessageContent, ModelSelect, PERMISSION_MODES, PermissionModeSelect, PermissionPrompt, ProgressRing, PromptArea, PromptTokenText, QUESTION_BEHAVIORS, QuestionPrompt, Reasoning, Response, Row, STATUS_META, Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue, SessionBrowser, SessionEmptyState, SessionInfoDialog, SessionList, SessionListItem, SessionPanel, SessionStatusIcon, SkillsDialog, Spinner, Splitter, StatusBar, TerminalDiff, TerminalItemView, TerminalMarkdown, TerminalPermissionPrompt, TerminalQuestionPrompt, TerminalStatusLine, TerminalSurface, TerminalTranscript, Textarea, Tip, Toaster, ToolCallCard, TooltipContent, TooltipProvider, Transcript, TranscriptDensityProvider, TranscriptVariantProvider, UsageDialog, UsageMeters, WithActions, badgeVariants, buttonVariants, cn, commandTrigger, copyText, formatAgoPrecise, formatBytes, formatCost, formatCountdown, formatDuration, formatRateLimitWindow, formatRateLimitWindowLong, formatRelativeTime, formatTokens, friendlyModel, getChipsByTrigger, hashtagTrigger, isMutatingTool, isSegmentsEmpty, mentionTrigger, parseUserQuestions, permissionModeChoices, permissionModeMeta, plainTextToSegments, previewPatch, rateLimitWindowSeconds, rowShapeClass, segmentsToPlainText, skillPrompt, toast, toolIcon, toolInputPreview, useAffordances, useMinuteClock, usePromptAreaState, useTranscriptDensity, useTranscriptVariant };
616
774
 
617
775
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["AlertDialogPrimitive","SonnerToaster"],"sources":["../src/components/ui/Card.tsx","../src/components/ui/Textarea.tsx","../src/components/ui/AlertDialog.tsx","../src/components/ui/Sonner.tsx","../src/components/prompt-area/segment-helpers.ts","../src/components/agent/SessionList.tsx","../src/components/agent/SessionBrowser.tsx","../src/components/agent/EngineIcon.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'\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 <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 </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","/**\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 { 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}\n\nexport function SessionListItem({ session, active, onSelect, onDelete }: 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 )}>\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 { useEffect, useMemo, useRef, useState } from 'react'\nimport {\n BellRing,\n CircleAlert,\n CircleSlash,\n Moon,\n PauseCircle,\n Pencil,\n Search,\n Trash2,\n X,\n} from 'lucide-react'\nimport {\n STATE_LABELS,\n STATE_ORDER,\n adaptersOf,\n clearFilters,\n filterRows,\n groupRows,\n hasFacetFilter,\n sessionLabel,\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 { Input } from '../ui/Input.tsx'\nimport { Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue } from '../ui/Select.tsx'\nimport { Spinner } from '../ui/Spinner.tsx'\nimport { cn } from '../../lib/utils.ts'\nimport { formatCost, formatRelativeTime, friendlyModel } from '../../lib/format.ts'\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 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 /** 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 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 onSelect,\n onDelete,\n onRename,\n emptyState,\n showControls = true,\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 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 <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 ...(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 ...(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>No sessions yet.</Empty>)\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.\n <Empty>\n {hasFacetFilter(config) ? (\n <>\n No sessions match.{' '}\n <button\n type='button'\n className='underline underline-offset-2 hover:text-fg-1'\n onClick={() => onConfigChange(clearFilters(config))}>\n Clear filters\n </button>\n </>\n ) : (\n 'No sessions here.'\n )}\n </Empty>\n ) : (\n <div className='flex flex-col gap-4'>\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-baseline gap-2 px-3 text-label font-medium text-fg-4'>\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 showGateway={gateways.length > 1}\n onSelect={onSelect}\n onDelete={onDelete}\n onRename={onRename}\n />\n ))}\n </div>\n ))}\n </div>\n )}\n </div>\n )\n}\n\nfunction Empty({ children }: { children: React.ReactNode }) {\n return <div className='px-3 py-6 text-center text-body-sm text-fg-4'>{children}</div>\n}\n\ninterface SessionRowItemProps {\n row: SessionRow\n active?: boolean\n showGateway?: boolean\n onSelect?: (row: SessionRow) => void\n onDelete?: (row: SessionRow) => void\n onRename?: (row: SessionRow, title: string) => void\n}\n\nfunction SessionRowItem({\n row,\n active,\n showGateway,\n onSelect,\n onDelete,\n onRename,\n}: SessionRowItemProps) {\n const { info } = row\n const [editing, setEditing] = useState(false)\n\n // What it is and what it has spent, in one line — the same set the extension\n // shows, joined the same way, so the two lists read as one product.\n const folder = info.cwd.split('/').filter(Boolean).pop() ?? info.cwd\n const details = [\n showGateway ? row.hostName : undefined,\n friendlyModel(info.model),\n folder,\n info.profile ? `@${info.profile}` : undefined,\n formatCost(info.totalCostUsd),\n ].filter(Boolean)\n\n return (\n <div\n data-slot='session-row'\n data-active={active || undefined}\n className={cn(\n 'group flex cursor-pointer flex-col gap-0.5 text-left transition-colors',\n rowShapeClass(active === true),\n )}>\n {/* Line one: what you scan the list by on the left, how it is doing on the\n right, state last. */}\n <div className='flex items-center gap-1.5'>\n {/* The editor replaces the link rather than sitting inside it — an\n input nested in a button is invalid, and disabling the button to\n protect the edit disables the field with it. */}\n {editing && onRename ? (\n <NameEditor\n initial={info.title ?? ''}\n onCommit={(title) => {\n setEditing(false)\n onRename(row, title)\n }}\n onCancel={() => setEditing(false)}\n />\n ) : (\n <button\n type='button'\n onClick={() => onSelect?.(row)}\n className={cn(\n 'min-w-0 flex-1 truncate text-left text-body-sm outline-none',\n active ? 'font-medium text-fg-1' : 'text-fg-2',\n )}>\n {sessionLabel(info)}\n </button>\n )}\n {row.unseen > 0 ? (\n // Transcript rows since this session was last on screen — the same\n // unit the VS Code badge counts, because turns undercount badly.\n <span\n title={`${row.unseen} new`}\n className='shrink-0 rounded-full bg-accent px-1.5 text-label text-accent-fg'>\n {row.unseen}\n </span>\n ) : null}\n <span className='shrink-0 text-label text-fg-4'>\n {formatRelativeTime(info.lastActivityAt ?? info.createdAt)}\n </span>\n <SessionStatusIcon row={row} />\n </div>\n\n {/* Line two: what it is, with the actions at the far right — away from the\n state icon, and away from the title you are actually reading. */}\n <div className='flex items-center gap-1 text-label text-fg-4'>\n <button\n type='button'\n onClick={() => !editing && onSelect?.(row)}\n className='min-w-0 flex-1 truncate text-left font-mono outline-none'>\n {details.join(' · ')}\n </button>\n {onRename && !editing ? (\n <Button\n variant='ghost'\n size='icon-sm'\n aria-label='Rename session'\n className='size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100'\n onClick={() => setEditing(true)}>\n <Pencil className='size-3 text-fg-3' />\n </Button>\n ) : null}\n {onDelete ? (\n <Button\n variant='ghost'\n size='icon-sm'\n aria-label='Close session'\n className='size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100'\n onClick={() => onDelete(row)}>\n <Trash2 className='size-3 text-fg-3' />\n </Button>\n ) : null}\n </div>\n </div>\n )\n}\n\n/**\n * State as one glyph on the right edge — a ringing bell when it wants a human, a\n * spinner while it works, a moon when it is only sleeping. Replaces the text\n * badge: in a sidebar the word costs more room than it earns, and the states\n * that matter are the two you can recognise without reading.\n */\nexport function SessionStatusIcon({ row }: { row: SessionRow }) {\n const { info } = row\n if (info.pendingPermissionCount > 0 || info.status === 'awaiting_approval') {\n return <BellRing className='size-3.5 shrink-0 animate-pulse text-warning' />\n }\n if (info.status === 'running' || info.status === 'starting') {\n return <Spinner className='size-3.5 shrink-0 text-info' />\n }\n switch (info.status) {\n case 'failed':\n return <CircleAlert className='size-3.5 shrink-0 text-danger' />\n case 'closed':\n return <CircleSlash className='size-3.5 shrink-0 text-fg-4' />\n case 'parked':\n return <PauseCircle className='size-3.5 shrink-0 text-fg-3' />\n default:\n return <Moon className='size-3.5 shrink-0 text-fg-4' />\n }\n}\n\n/**\n * Inline rename. Enter commits, Escape cancels, blur commits — but only a blur\n * that is still inside this document: switching windows must not silently commit,\n * nor kill the editor in the frame it opened.\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?.select()\n }, [])\n return (\n <input\n ref={ref}\n value={value}\n autoFocus\n aria-label='Session name'\n // The row is a button; a click in here must not select the session.\n onClick={(e) => e.stopPropagation()}\n onChange={(e) => setValue(e.target.value)}\n onBlur={() => (document.hasFocus() ? onCommit(value.trim()) : undefined)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') onCommit(value.trim())\n else if (e.key === 'Escape') onCancel()\n e.stopPropagation()\n }}\n className='-my-0.5 min-w-0 flex-1 rounded-sm border border-ring bg-bg px-1 py-0.5 text-body-sm font-medium text-fg-1 outline-none'\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","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\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"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["AlertDialogPrimitive","SonnerToaster"],"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/SessionBrowser.tsx","../src/components/agent/EngineIcon.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'\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 <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 </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 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 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'>\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}\n\nexport function SessionListItem({ session, active, onSelect, onDelete }: 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 )}>\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 { useEffect, useMemo, useRef, useState } from 'react'\nimport {\n BellRing,\n CircleAlert,\n CircleSlash,\n Layers,\n Moon,\n PauseCircle,\n Pencil,\n Search,\n SearchX,\n Trash2,\n X,\n} from 'lucide-react'\nimport {\n STATE_LABELS,\n STATE_ORDER,\n adaptersOf,\n clearFilters,\n filterRows,\n groupRows,\n hasFacetFilter,\n sessionLabel,\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 { Spinner } from '../ui/Spinner.tsx'\nimport { cn } from '../../lib/utils.ts'\nimport { formatCost, formatRelativeTime, friendlyModel } from '../../lib/format.ts'\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 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 /** 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 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 onSelect,\n onDelete,\n onRename,\n emptyState,\n showControls = true,\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 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 <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 ...(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 ...(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 <div className='flex flex-col gap-4'>\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-baseline gap-2 px-3 text-label font-medium text-fg-4'>\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 showGateway={gateways.length > 1}\n onSelect={onSelect}\n onDelete={onDelete}\n onRename={onRename}\n />\n ))}\n </div>\n ))}\n </div>\n )}\n </div>\n )\n}\n\ninterface SessionRowItemProps {\n row: SessionRow\n active?: boolean\n showGateway?: boolean\n onSelect?: (row: SessionRow) => void\n onDelete?: (row: SessionRow) => void\n onRename?: (row: SessionRow, title: string) => void\n}\n\nfunction SessionRowItem({\n row,\n active,\n showGateway,\n onSelect,\n onDelete,\n onRename,\n}: SessionRowItemProps) {\n const { info } = row\n const [editing, setEditing] = useState(false)\n\n // What it is and what it has spent, in one line — the same set the extension\n // shows, joined the same way, so the two lists read as one product.\n const folder = info.cwd.split('/').filter(Boolean).pop() ?? info.cwd\n const details = [\n showGateway ? row.hostName : undefined,\n friendlyModel(info.model),\n folder,\n info.profile ? `@${info.profile}` : undefined,\n formatCost(info.totalCostUsd),\n ].filter(Boolean)\n\n return (\n <div\n data-slot='session-row'\n data-active={active || undefined}\n // Selection lives on the whole row, not on the two text buttons inside\n // it: the age, the unread badge and the state glyph sit outside them, and\n // a row where a third of the surface silently does nothing is a row that\n // feels broken. The buttons stay — they are what makes the row reachable\n // by keyboard — but their activation now reaches this handler by\n // bubbling, so there is one code path and no double-fire. Anything that\n // is its own action (rename, close, the name editor) stops the event.\n onClick={() => !editing && onSelect?.(row)}\n className={cn(\n 'group flex cursor-pointer flex-col gap-0.5 text-left transition-colors',\n rowShapeClass(active === true),\n )}>\n {/* Line one: what you scan the list by on the left, how it is doing on the\n right, state last. */}\n <div className='flex items-center gap-1.5'>\n {/* The editor replaces the link rather than sitting inside it — an\n input nested in a button is invalid, and disabling the button to\n protect the edit disables the field with it. */}\n {editing && onRename ? (\n <NameEditor\n initial={info.title ?? ''}\n onCommit={(title) => {\n setEditing(false)\n onRename(row, title)\n }}\n onCancel={() => setEditing(false)}\n />\n ) : (\n <button\n type='button'\n className={cn(\n 'min-w-0 flex-1 truncate text-left text-body-sm outline-none',\n active ? 'font-medium text-fg-1' : 'text-fg-2',\n )}>\n {sessionLabel(info)}\n </button>\n )}\n {row.unseen > 0 ? (\n // Transcript rows since this session was last on screen — the same\n // unit the VS Code badge counts, because turns undercount badly.\n <span\n title={`${row.unseen} new`}\n className='shrink-0 rounded-full bg-accent px-1.5 text-label text-accent-fg'>\n {row.unseen}\n </span>\n ) : null}\n <span className='shrink-0 text-label text-fg-4'>\n {formatRelativeTime(info.lastActivityAt ?? info.createdAt)}\n </span>\n <SessionStatusIcon row={row} />\n </div>\n\n {/* Line two: what it is, with the actions at the far right — away from the\n state icon, and away from the title you are actually reading. */}\n <div className='flex items-center gap-1 text-label text-fg-4'>\n <button\n type='button'\n tabIndex={-1}\n className='min-w-0 flex-1 truncate text-left font-mono outline-none'>\n {details.join(' · ')}\n </button>\n {onRename && !editing ? (\n <Button\n variant='ghost'\n size='icon-sm'\n aria-label='Rename session'\n className='size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100'\n onClick={(e) => {\n e.stopPropagation()\n setEditing(true)\n }}>\n <Pencil className='size-3 text-fg-3' />\n </Button>\n ) : null}\n {onDelete ? (\n <Button\n variant='ghost'\n size='icon-sm'\n aria-label='Close session'\n className='size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100'\n onClick={(e) => {\n e.stopPropagation()\n onDelete(row)\n }}>\n <Trash2 className='size-3 text-fg-3' />\n </Button>\n ) : null}\n </div>\n </div>\n )\n}\n\n/**\n * State as one glyph on the right edge — a ringing bell when it wants a human, a\n * spinner while it works, a moon when it is only sleeping. Replaces the text\n * badge: in a sidebar the word costs more room than it earns, and the states\n * that matter are the two you can recognise without reading.\n */\nexport function SessionStatusIcon({ row }: { row: SessionRow }) {\n const { info } = row\n if (info.pendingPermissionCount > 0 || info.status === 'awaiting_approval') {\n return <BellRing className='size-3.5 shrink-0 animate-pulse text-warning' />\n }\n if (info.status === 'running' || info.status === 'starting') {\n return <Spinner className='size-3.5 shrink-0 text-info' />\n }\n switch (info.status) {\n case 'failed':\n return <CircleAlert className='size-3.5 shrink-0 text-danger' />\n case 'closed':\n return <CircleSlash className='size-3.5 shrink-0 text-fg-4' />\n case 'parked':\n return <PauseCircle className='size-3.5 shrink-0 text-fg-3' />\n default:\n return <Moon className='size-3.5 shrink-0 text-fg-4' />\n }\n}\n\n/**\n * Inline rename. Enter commits, Escape cancels, blur commits — but only a blur\n * that is still inside this document: switching windows must not silently commit,\n * nor kill the editor in the frame it opened.\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?.select()\n }, [])\n return (\n <input\n ref={ref}\n value={value}\n autoFocus\n aria-label='Session name'\n // The row is a button; a click in here must not select the session.\n onClick={(e) => e.stopPropagation()}\n onChange={(e) => setValue(e.target.value)}\n onBlur={() => (document.hasFocus() ? onCommit(value.trim()) : undefined)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') onCommit(value.trim())\n else if (e.key === 'Escape') onCancel()\n e.stopPropagation()\n }}\n className='-my-0.5 min-w-0 flex-1 rounded-sm border border-ring bg-bg px-1 py-0.5 text-body-sm font-medium text-fg-1 outline-none'\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","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\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"],"mappings":"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"}