@workerdeck/ui 0.23.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/build/{SessionPanel-DUt2VzXG.mjs → SessionPanel-DDgBkHsi.mjs} +2151 -3357
  2. package/build/SessionPanel-DDgBkHsi.mjs.map +1 -0
  3. package/build/SessionPanel-E_L_ldQ5.d.mts +207 -0
  4. package/build/format-B8ZM9LPy.d.mts +16 -0
  5. package/build/format.d.mts +8 -75
  6. package/build/format.mjs +2 -2
  7. package/build/index.d.mts +63 -1138
  8. package/build/index.mjs +4 -300
  9. package/build/index.mjs.map +1 -1
  10. package/build/scoped.css +15 -23
  11. package/build/{status-BE-zg88x.mjs → status-C0HSLZas.mjs} +14 -85
  12. package/build/status-C0HSLZas.mjs.map +1 -0
  13. package/build/workspace.d.mts +5 -126
  14. package/build/workspace.mjs +11 -143
  15. package/build/workspace.mjs.map +1 -1
  16. package/package.json +4 -4
  17. package/src/components/agent/CodeEditor.tsx +31 -103
  18. package/src/components/agent/Composer.tsx +114 -333
  19. package/src/components/agent/ContextDialog.tsx +20 -41
  20. package/src/components/agent/ContextRing.tsx +6 -28
  21. package/src/components/agent/Conversation.tsx +21 -33
  22. package/src/components/agent/EditorTabs.tsx +39 -67
  23. package/src/components/agent/EngineIcon.tsx +29 -72
  24. package/src/components/agent/EntryIcon.tsx +16 -0
  25. package/src/components/agent/FileCard.tsx +10 -16
  26. package/src/components/agent/FileTree.tsx +52 -125
  27. package/src/components/agent/FileViewer.tsx +30 -60
  28. package/src/components/agent/HostFilesDialog.tsx +40 -69
  29. package/src/components/agent/Loader.tsx +3 -24
  30. package/src/components/agent/McpDialog.tsx +64 -137
  31. package/src/components/agent/Message.tsx +2 -21
  32. package/src/components/agent/ModelSelect.tsx +33 -62
  33. package/src/components/agent/PermissionModeSelect.tsx +21 -79
  34. package/src/components/agent/PermissionPrompt.tsx +59 -63
  35. package/src/components/agent/ProjectIcon.tsx +228 -93
  36. package/src/components/agent/PromptTokenText.tsx +10 -16
  37. package/src/components/agent/QuestionPrompt.tsx +43 -64
  38. package/src/components/agent/Reasoning.tsx +13 -16
  39. package/src/components/agent/Response.tsx +3 -9
  40. package/src/components/agent/Scrubber.tsx +31 -80
  41. package/src/components/agent/SessionBrowser.tsx +64 -243
  42. package/src/components/agent/SessionEmptyState.tsx +10 -40
  43. package/src/components/agent/SessionInfoDialog.tsx +46 -78
  44. package/src/components/agent/SessionItem.tsx +53 -320
  45. package/src/components/agent/SessionList.tsx +23 -39
  46. package/src/components/agent/SessionPanel.tsx +315 -996
  47. package/src/components/agent/SessionStatusIcon.tsx +14 -28
  48. package/src/components/agent/SessionSteps.tsx +34 -187
  49. package/src/components/agent/SessionWorkspace.tsx +104 -219
  50. package/src/components/agent/SkillsDialog.tsx +35 -80
  51. package/src/components/agent/StatusBar.tsx +51 -136
  52. package/src/components/agent/SubagentStrip.tsx +12 -77
  53. package/src/components/agent/ToolCallCard.tsx +94 -135
  54. package/src/components/agent/Transcript.tsx +103 -1288
  55. package/src/components/agent/TranscriptItemView.tsx +155 -0
  56. package/src/components/agent/TranscriptRows.tsx +418 -0
  57. package/src/components/agent/UsageDialog.tsx +7 -29
  58. package/src/components/agent/UsageMeters.tsx +18 -73
  59. package/src/components/agent/pulse.tsx +9 -32
  60. package/src/components/agent/scrubber-marks.ts +33 -103
  61. package/src/components/agent/status.ts +1 -5
  62. package/src/components/agent/tool-result-fetch.tsx +3 -25
  63. package/src/components/agent/tool-result-image.tsx +32 -111
  64. package/src/components/agent/transcript-rows.ts +41 -118
  65. package/src/components/agent/transcript-variant.tsx +3 -80
  66. package/src/components/agent/use-height-epoch.ts +10 -24
  67. package/src/components/agent/use-path-links.ts +45 -63
  68. package/src/components/agent/use-subagent-frame.ts +106 -0
  69. package/src/components/agent/use-transcript-jumps.ts +49 -107
  70. package/src/components/prompt-area/animated-placeholder.tsx +6 -5
  71. package/src/components/prompt-area/clipboard-helpers.ts +26 -21
  72. package/src/components/prompt-area/cursor-helpers.ts +79 -37
  73. package/src/components/prompt-area/dom-helpers.ts +97 -69
  74. package/src/components/prompt-area/file-strip.tsx +90 -80
  75. package/src/components/prompt-area/html-to-markdown.ts +67 -46
  76. package/src/components/prompt-area/image-strip.tsx +8 -18
  77. package/src/components/prompt-area/index.ts +2 -15
  78. package/src/components/prompt-area/prompt-area-engine.ts +100 -115
  79. package/src/components/prompt-area/prompt-area-list-ops.ts +66 -59
  80. package/src/components/prompt-area/prompt-area.tsx +31 -38
  81. package/src/components/prompt-area/remove-button.tsx +3 -9
  82. package/src/components/prompt-area/segment-helpers.ts +4 -12
  83. package/src/components/prompt-area/trigger-popover.tsx +15 -27
  84. package/src/components/prompt-area/trigger-presets.ts +0 -10
  85. package/src/components/prompt-area/types.ts +3 -8
  86. package/src/components/prompt-area/use-chip-editing.ts +305 -0
  87. package/src/components/prompt-area/use-markdown-mode.ts +7 -17
  88. package/src/components/prompt-area/use-prompt-area-events.ts +80 -86
  89. package/src/components/prompt-area/use-prompt-area-keydown.ts +598 -0
  90. package/src/components/prompt-area/use-prompt-area-state.ts +5 -23
  91. package/src/components/prompt-area/use-prompt-area.ts +170 -896
  92. package/src/components/prompt-area/use-trigger-search.ts +21 -19
  93. package/src/components/terminal/PermissionPrompt.tsx +56 -59
  94. package/src/components/terminal/QuestionPrompt.tsx +28 -79
  95. package/src/components/terminal/StatusLine.tsx +13 -63
  96. package/src/components/terminal/TerminalTranscript.tsx +44 -160
  97. package/src/components/terminal/affordances.tsx +68 -98
  98. package/src/components/terminal/blocks.ts +20 -156
  99. package/src/components/terminal/diff.tsx +19 -67
  100. package/src/components/terminal/height.ts +163 -313
  101. package/src/components/terminal/image-box.ts +0 -44
  102. package/src/components/terminal/items.tsx +150 -305
  103. package/src/components/terminal/markdown.tsx +36 -95
  104. package/src/components/terminal/press.tsx +26 -53
  105. package/src/components/terminal/prompt.tsx +40 -170
  106. package/src/components/terminal/result-preview.ts +8 -56
  107. package/src/components/terminal/row.tsx +11 -98
  108. package/src/components/terminal/scrubber.tsx +108 -401
  109. package/src/components/terminal/surface.tsx +6 -54
  110. package/src/components/terminal/todos.ts +66 -0
  111. package/src/components/terminal/tool-run.ts +21 -192
  112. package/src/components/ui/AlertDialog.tsx +8 -23
  113. package/src/components/ui/Badge.tsx +18 -29
  114. package/src/components/ui/Button.tsx +7 -15
  115. package/src/components/ui/Card.tsx +2 -13
  116. package/src/components/ui/CodeBlock.tsx +10 -37
  117. package/src/components/ui/CopyButton.tsx +10 -23
  118. package/src/components/ui/Dialog.tsx +23 -36
  119. package/src/components/ui/Empty.tsx +6 -29
  120. package/src/components/ui/Input.tsx +14 -16
  121. package/src/components/ui/Menu.tsx +6 -18
  122. package/src/components/ui/PortalScope.tsx +1 -21
  123. package/src/components/ui/ProgressRing.tsx +8 -19
  124. package/src/components/ui/Select.tsx +18 -31
  125. package/src/components/ui/Sonner.tsx +1 -2
  126. package/src/components/ui/Spinner.tsx +1 -1
  127. package/src/components/ui/Splitter.tsx +30 -64
  128. package/src/components/ui/Textarea.tsx +2 -5
  129. package/src/components/ui/Tooltip.tsx +3 -13
  130. package/src/format.ts +0 -9
  131. package/src/index.ts +16 -113
  132. package/src/lib/clipboard.ts +8 -28
  133. package/src/lib/css.ts +8 -0
  134. package/src/lib/format.ts +101 -86
  135. package/src/lib/plan-request.ts +16 -0
  136. package/src/lib/status.ts +45 -95
  137. package/src/lib/tool-icon.ts +39 -47
  138. package/src/lib/utils.ts +1 -3
  139. package/src/styles/terminal.css +81 -49
  140. package/src/styles/theme.css +96 -32
  141. package/src/workspace.ts +1 -24
  142. package/build/SessionPanel-BobynUo4.d.mts +0 -669
  143. package/build/SessionPanel-DUt2VzXG.mjs.map +0 -1
  144. package/build/format-ljc3lKpA.d.mts +0 -59
  145. package/build/status-BE-zg88x.mjs.map +0 -1
package/build/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { $ as toolIcon, A as PERMISSION_MODES, At as MenuSeparator, B as skillPrompt, Bt as Badge, C as TerminalQuestionPrompt, Ct as DialogContent, D as parseUserQuestions, Dt as Menu, E as QuestionPrompt, Et as DialogTrigger, F as SkillsDialog, Ft as SelectItemText, G as Band, H as TerminalMarkdown, Ht as Button, I as McpDialog, It as SelectTrigger, J as Row, K as Blank, L as HostFilesDialog, Lt as SelectValue, M as permissionModeChoices, Mt as Select, N as permissionModeMeta, Nt as SelectContent, O as SubagentStrip, Ot as MenuContent, P as ModelSelect, Pt as SelectItem, Q as isMutatingTool, R as ContextDialog, Rt as PortalScope, S as SessionInfoDialog, St as DialogClose, T as QUESTION_BEHAVIORS, Tt as DialogRow, U as TerminalDiff, Ut as buttonVariants, V as TerminalSurface, Vt as badgeVariants, W as previewPatch, Wt as cn, X as WithActions, Y as CopyAction, Z as useAffordances, _ as Conversation, _t as Tip, a as Transcript, at as hashtagTrigger, b as StatusBar, bt as Dialog, c as ToolCallCard, ct as PromptArea, d as Response, dt as ProgressRing, et as TranscriptDensityProvider, f as PromptTokenText, ft as Splitter, g as FileCard, gt as copyText, h as Loader, ht as CopyButton, i as useMinuteClock, it as commandTrigger, j as PermissionModeSelect, jt as MenuTrigger, k as PermissionPrompt, kt as MenuItem, l as SessionEmptyState, lt as plainTextToSegments, m as MessageContent, mt as Spinner, n as UsageDialog, nt as useTranscriptDensity, o as TerminalItemView, ot as mentionTrigger, p as Message, pt as CodeBlock, q as Ink, r as UsageMeters, rt as useTranscriptVariant, s as TerminalTranscript, st as usePromptAreaState, t as SessionPanel, tt as TranscriptVariantProvider, u as Reasoning, ut as segmentsToPlainText, v as ConversationContent, vt as TooltipContent, w as TerminalPermissionPrompt, wt as DialogHeader, x as STATUS_META, xt as DialogBody, y as ConversationScrollButton, yt as TooltipProvider, z as Composer, zt as Input } from "./SessionPanel-DUt2VzXG.mjs";
2
- import { _ as formatRelativeTime, a as modelLabel, b as rateLimitWindowSeconds, d as formatBytes, f as formatCost, g as formatRateLimitWindowLong, h as formatRateLimitWindow, i as meterSeverity, l as windowLabel, m as formatDuration, o as statusPresentation, p as formatCountdown, r as meterColorClass, s as tightestWindow, t as contextSeverity, u as formatAgoPrecise, v as formatTokens, x as toolInputPreview, y as friendlyModel } from "./status-BE-zg88x.mjs";
1
+ import { $ as isMutatingTool, A as PERMISSION_MODES, At as MenuItem, B as Composer, Bt as Input, C as TerminalPermissionPrompt, Ct as DialogClose, D as SubagentStrip, Dt as DialogTrigger, E as parseUserQuestions, Et as DialogRow, F as SkillsDialog, Ft as SelectItem, G as previewPatch, Gt as cn, H as TerminalSurface, Ht as badgeVariants, I as McpDialog, It as SelectItemText, J as Ink, K as Band, L as HostFilesDialog, Lt as SelectTrigger, M as permissionModeChoices, Mt as MenuTrigger, N as permissionModeMeta, Nt as Select, O as PermissionPrompt, Ot as Menu, P as ModelSelect, Pt as SelectContent, Q as useAffordances, Rt as SelectValue, S as TerminalQuestionPrompt, St as DialogBody, T as QuestionPrompt, Tt as DialogHeader, U as TerminalMarkdown, Ut as Button, V as skillPrompt, Vt as Badge, W as TerminalDiff, Wt as buttonVariants, X as CopyAction, Y as Row, Z as WithActions, _ as ConversationContent, _t as copyText, a as Transcript, at as commandTrigger, b as STATUS_META, bt as TooltipProvider, c as PromptTokenText, ct as usePromptAreaState, d as FileCard, dt as segmentsToPlainText, et as toolIcon, f as TerminalItemView, ft as ProgressRing, g as Conversation, gt as CopyButton, h as Loader, ht as Spinner, i as useMinuteClock, it as useTranscriptVariant, j as PermissionModeSelect, jt as MenuSeparator, k as Response, kt as MenuContent, l as Message, lt as PromptArea, m as SessionEmptyState, mt as CodeBlock, n as UsageDialog, nt as TranscriptVariantProvider, o as ToolCallCard, ot as hashtagTrigger, p as TerminalTranscript, pt as Splitter, q as Blank, r as UsageMeters, rt as useTranscriptDensity, s as Reasoning, st as mentionTrigger, t as SessionPanel, tt as TranscriptDensityProvider, u as MessageContent, ut as plainTextToSegments, v as ConversationScrollButton, vt as Tip, w as QUESTION_BEHAVIORS, wt as DialogContent, x as SessionInfoDialog, xt as Dialog, y as StatusBar, yt as TooltipContent, z as ContextDialog, zt as PortalScope } from "./SessionPanel-DDgBkHsi.mjs";
2
+ import { S as toolInputPreview, _ as formatRateLimitWindowLong, b as friendlyModel, c as tightestWindow, d as formatAgoPrecise, f as formatBytes, g as formatRateLimitWindow, h as formatDuration, i as meterSeverity, m as formatCountdown, o as modelLabel, p as formatCost, r as meterColorClass, s as statusPresentation, t as contextSeverity, u as windowLabel, v as formatRelativeTime, x as rateLimitWindowSeconds, y as formatTokens } from "./status-C0HSLZas.mjs";
3
3
  import { Fragment, forwardRef, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
5
5
  import { Anchor, ArrowRight, Atom, Beaker, BellRing, Bike, Binary, Blocks, Bot, Box, Brain, Briefcase, Brush, Bug, Building2, Cable, Camera, Car, Cat, Check, ChevronDown, ChevronRight, CircleAlert, CircleSlash, CircuitBoard, Cloud, Code, Coffee, Cog, Compass, Container, Cpu, CreditCard, Database, Diamond, Dog, Dot, Eraser, Feather, Film, Flame, FlaskConical, Folder, Gamepad2, Gauge, Gem, Ghost, Gift, GitBranch, Globe, GraduationCap, Hammer, Hexagon, House, Image, Key, Landmark, Laptop, Layers, Leaf, Library, Lightbulb, Lock, Mail, Map as Map$1, MessageCircle, Mic, Monitor, Moon, Mountain, Music, Network, Newspaper, Notebook, Orbit, Package, Palette, PauseCircle, PenTool, Pencil, Phone, PiggyBank, Plane, Plug, Puzzle, Radar, Radio, Receipt, Rocket, Ruler, Satellite, Scale, Scissors, Search, SearchX, Server, Shield, Ship, ShoppingCart, Signal, Smartphone, Sparkles, Sprout, Square, Star, Sun, Table, Tag, Target, Telescope, Terminal, TestTube, Trash2, TreePine, Trophy, Truck, Umbrella, Users, Wallet, Wand, Waves, Webhook, Wifi, Wrench, X, Zap } from "lucide-react";
@@ -62,7 +62,6 @@ const AlertDialogDescription = ({ className, ...props }) => /* @__PURE__ */ jsx(
62
62
  });
63
63
  //#endregion
64
64
  //#region src/components/ui/Sonner.tsx
65
- /** Token-themed toaster; relies on the [data-theme] swap, so no `theme` prop needed. */
66
65
  function Toaster() {
67
66
  return /* @__PURE__ */ jsx(Toaster$1, {
68
67
  position: "bottom-right",
@@ -78,22 +77,6 @@ function Toaster() {
78
77
  }
79
78
  //#endregion
80
79
  //#region src/components/ui/Empty.tsx
81
- /**
82
- * A view with nothing in it: icon, title, description, and at most one action.
83
- *
84
- * Centered and given room, because an empty view is the first thing a new
85
- * install shows and a flush-left sentence in the top corner reads like a bug
86
- * report. It lives here rather than in one client because every panel that can
87
- * be empty should be empty the same way — the extension's views and the
88
- * dashboard's four sidebars are the same shape of thing.
89
- *
90
- * **At most one action, and never one the view header already offers.** Creating
91
- * a session and adding a gateway are the `+` in the native title bar, exclusively
92
- * — so those empty states point at it in words rather than growing a second
93
- * button that does the same thing two inches lower. A button here is for the way
94
- * out of a state the header has no answer to: clearing a filter, widening a
95
- * scope.
96
- */
97
80
  function Empty({ icon, title, description, action, onAction }) {
98
81
  return /* @__PURE__ */ jsxs("div", {
99
82
  className: "flex flex-col items-center gap-2 px-5 py-8 text-center",
@@ -120,7 +103,6 @@ function Empty({ icon, title, description, action, onAction }) {
120
103
  ]
121
104
  });
122
105
  }
123
- /** A key or icon named inline in an empty state's description — "use the + above". */
124
106
  function EmptyKey({ children }) {
125
107
  return /* @__PURE__ */ jsx("span", {
126
108
  className: "rounded border border-border px-1 font-mono text-fg-3",
@@ -140,31 +122,11 @@ function getChipsByTrigger(segments, trigger) {
140
122
  }
141
123
  //#endregion
142
124
  //#region src/components/terminal/StatusLine.tsx
143
- /**
144
- * The session's readings, as one line.
145
- *
146
- * The styled bar puts each of these in its own affordance — a progress ring, a
147
- * badge, a tooltip — which is right for a dashboard and wrong here for the usual
148
- * reason: a terminal has one line height, and five widgets of five different
149
- * heights is five different rhythms in a row that is supposed to be quiet. So
150
- * they are words, `·`-separated, exactly as the CLI's own status line writes
151
- * them.
152
- *
153
- * The readings themselves come from the same helpers the styled bar uses
154
- * (`lib/status.ts`), and that matters more than it looks: `statusPresentation`
155
- * is where the rule lives that **a dropped socket outranks the session status**,
156
- * because a status held over a dead socket is a stale reading being presented as
157
- * a live one. Re-deriving that here would be a second answer to a question that
158
- * already has one.
159
- */
160
- /** Severity → tone. The 80/95 thresholds are `lib/status.ts`'s, not new ones. */
161
125
  const SEVERITY_TONE = {
162
126
  none: "dim",
163
127
  warning: "yellow",
164
128
  error: "red"
165
129
  };
166
- /** One reading. A button only when it leads somewhere — a segment that looks
167
- * pressable and does nothing is worse than plain text. */
168
130
  function Reading({ tone, onPress, label, children }) {
169
131
  if (!onPress) return /* @__PURE__ */ jsx(Ink, {
170
132
  tone,
@@ -312,50 +274,6 @@ function SessionList({ sessions, activeId, onSelect, onDelete, emptyText = "No s
312
274
  }
313
275
  //#endregion
314
276
  //#region src/components/agent/ProjectIcon.tsx
315
- /**
316
- * A project's icon — the render side of protocol's `ProjectIcon`, for a list
317
- * row or a group header.
318
- *
319
- * **The glyph arm is a curated set, and that is the design rather than a
320
- * shortcut.** The gateway validates a glyph name by *shape* only (lucide's
321
- * lowercase-kebab convention) and explicitly declines to grow an icon catalog,
322
- * so the contract already says a client must fall back on a name it does not
323
- * know.
324
- *
325
- * The alternative was measured rather than argued, against the VS Code
326
- * sidebar's own bundle: `sidebar.js` is **31 KB** with one glyph, **77 KB**
327
- * with the 110 below (~418 bytes each), and **927 KB** with a
328
- * `import * as` over lucide's ~1,600 — which is what it costs, because a
329
- * namespace import defeats tree-shaking by construction. (`DynamicIcon` is the
330
- * third option and worse in a different currency: ~1,600 chunk files shipped
331
- * inside the `.vsix`.) Twelve times the bundle to render a name nobody has
332
- * declared yet is not a trade worth making; 46 KB to cover the names people
333
- * actually use is.
334
- *
335
- * The set is chosen to cover what people call a project; an unlisted-but-valid
336
- * name draws {@link Folder}, which is what the row drew before this feature
337
- * existed. **Grow it freely** — each addition is ~400 bytes and the fallback
338
- * means a missing one is a shrug, not a hole.
339
- *
340
- * **The image arm draws nothing until its bytes arrive**, and takes them as a
341
- * resolved `src` rather than fetching: the wire carries only an address
342
- * (media type, size, content hash), and *who* fetches it differs per client —
343
- * a VS Code webview cannot reach a gateway at all and must be handed a data
344
- * URL by its extension host, while a browser can fetch and `createObjectURL`.
345
- * A component that fetched would be wrong on one of them. Absent `src` renders
346
- * nothing rather than a placeholder box: an icon is decoration beside a name
347
- * that is already there, and a box that becomes a picture a beat later is more
348
- * movement than the picture is worth.
349
- *
350
- * One thing an image icon cannot do, and a glyph can: **take the row's
351
- * colour.** An `<img>`-embedded SVG is its own document, so `currentColor` does
352
- * not reach it and its own `prefers-color-scheme` resolves against the *OS*
353
- * rather than the host's theme (measured: it does resolve, which is the
354
- * surprise — so a monochrome mark tuned for both schemes is right on a dark OS
355
- * in a dark editor and wrong on a light OS in a dark one). A repo that wants a
356
- * mark that always matches its row should declare a glyph; one that wants its
357
- * brand should declare the image and accept that it is a picture.
358
- */
359
277
  const GLYPHS = {
360
278
  anchor: Anchor,
361
279
  atom: Atom,
@@ -485,23 +403,6 @@ function ProjectIcon({ icon, src, name, className }) {
485
403
  }
486
404
  //#endregion
487
405
  //#region src/components/agent/ContextRing.tsx
488
- /**
489
- * How full a session's context window is, sized for a **list row**.
490
- *
491
- * The reading the session screen has always drawn, moved somewhere it answers a
492
- * question the session screen cannot: *which* of these thirty sessions is
493
- * bloating. That is also why it is a ring and not a number — across a list, a
494
- * ring is read at a glance and `71%` has to be read one row at a time.
495
- *
496
- * **Absent draws nothing, and absent is not zero.** A promptless session, a
497
- * parked one from before the field existed, or an engine that reports no window
498
- * has no reading; an empty ring would claim its context is empty rather than
499
- * unknown. The title carries the exact numbers for anyone who wants them.
500
- *
501
- * Shared rather than spelled per client: the dashboard's row and the VS Code
502
- * sidebar's card both draw it, and two copies would be two sets of thresholds
503
- * the day one of them is nudged.
504
- */
505
406
  function ContextRing({ usage, size = 11, className }) {
506
407
  if (usage === void 0) return null;
507
408
  return /* @__PURE__ */ jsx("span", {
@@ -516,23 +417,6 @@ function ContextRing({ usage, size = 11, className }) {
516
417
  }
517
418
  //#endregion
518
419
  //#region src/components/agent/EngineIcon.tsx
519
- /**
520
- * The engine's own mark, monochrome.
521
- *
522
- * Inlined rather than depended on: the React package these come from
523
- * (`@lobehub/icons`) pulls antd, `@lobehub/ui` and a second copy of
524
- * lucide-react — an absurd amount of bundle for a few glyphs in a sidebar.
525
- * These are paths from `@lobehub/icons-static-svg` (MIT, © LobeHub), already
526
- * `fill="currentColor"` single-path marks, so they take the row's colour like
527
- * any other glyph and need no light/dark variants.
528
- *
529
- * The marks are trademarks of their owners (Anthropic, OpenAI, Google,
530
- * DeepSeek, Moonshot), used here only to identify which engine a session runs
531
- * on.
532
- *
533
- * Codex is drawn as OpenAI: the session's engine *is* OpenAI's, and OpenAI's
534
- * mark is the one a person recognises at 12px.
535
- */
536
420
  const PATHS = {
537
421
  claude: {
538
422
  title: "Claude",
@@ -555,57 +439,25 @@ const PATHS = {
555
439
  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"
556
440
  }
557
441
  };
558
- /**
559
- * Which mark a session wears.
560
- *
561
- * The two first-party engines are named directly. A `provider` session is
562
- * whatever the host wired up, so its *model* is the only thing that says whose
563
- * it is — sniffed loosely on purpose (`gemini-2.5-pro`, `deepseek-chat`,
564
- * `kimi-k2`), and falling through to no mark rather than guessing wrong.
565
- */
566
442
  function engineMark(engine, model) {
567
443
  if (engine === "claude") return "claude";
568
444
  if (engine === "codex") return "codex";
569
445
  const id = model?.toLowerCase() ?? "";
570
- if (!id) return void 0;
446
+ if (!id) return;
571
447
  if (id.includes("gemini")) return "gemini";
572
448
  if (id.includes("deepseek")) return "deepseek";
573
449
  if (id.includes("moonshot") || id.includes("kimi")) return "moonshot";
574
450
  if (id.includes("claude")) return "claude";
575
451
  if (id.startsWith("gpt") || id.startsWith("o1") || id.startsWith("o3") || id.includes("openai")) return "codex";
576
452
  }
577
- /**
578
- * Which engines wear their vendor's colour, and **how far it reaches**.
579
- *
580
- * `MARK` is the glyph, `TEXT` is the model name beside it. They are the same for
581
- * Anthropic and deliberately not for OpenAI, because the two brands differ in
582
- * kind rather than in hue: coral is an accent, and OpenAI's guidelines forbid
583
- * adding colour to the mark at all, so theirs is pure white/black. At full
584
- * contrast that is right on a 12px glyph and wrong on an 11px label — a
585
- * pure-white model name is *brighter than the session title above it*, which
586
- * inverts the row's whole hierarchy to say something the mark has already said.
587
- * So OpenAI's name keeps the muted line it always had, which is also the most
588
- * literal reading of "don't add any colors".
589
- *
590
- * Anything absent falls through to muted on both counts.
591
- */
592
453
  const VENDOR_MARK = {
593
454
  claude: "text-vendor-claude",
594
455
  codex: "text-vendor-openai"
595
456
  };
596
457
  const VENDOR_TEXT = { claude: "text-vendor-claude" };
597
- /** The colour a session's engine mark wears. Pass it to `EngineIcon`'s
598
- * `className` rather than to a parent: the svg carries its own `text-fg-3`,
599
- * and only a class on the element itself is merged over it. */
600
458
  function vendorMarkClass(engine, model) {
601
459
  return VENDOR_MARK[engineMark(engine, model) ?? ""] ?? "text-fg-3";
602
460
  }
603
- /** The colour the model *name* wears beside that mark — muted unless the vendor
604
- * is one whose accent survives at label size. See `VENDOR_TEXT`.
605
- *
606
- * The fallback is `text-fg-3` rather than the metadata line's own `text-fg-4`,
607
- * so an unbranded model still sits one step above the project and gateway
608
- * beside it — the order the spec lists them in, held without a colour. */
609
461
  function vendorTextClass(engine, model) {
610
462
  return VENDOR_TEXT[engineMark(engine, model) ?? ""] ?? "text-fg-3";
611
463
  }
@@ -629,28 +481,6 @@ function EngineIcon({ engine, model, className }) {
629
481
  }
630
482
  //#endregion
631
483
  //#region src/components/agent/SessionStatusIcon.tsx
632
- /**
633
- * State as one glyph — a ringing bell when it wants a human, a spinner while it
634
- * works, a moon when it is only sleeping. Replaces the text badge: in a sidebar
635
- * the word costs more room than it earns, and the states that matter are the two
636
- * you can recognise without reading.
637
- *
638
- * **It reads `row.state`, not `info.status`, and that distinction is the whole
639
- * point of the row model.** `sessionState` already folds in the arm this glyph
640
- * cannot see for itself: a *background* sub-agent outlives its turn by design,
641
- * so the turn ends, `status` comes to rest at `idle`, and the agent keeps
642
- * working. Reading the raw status drew a **moon on a row filed under the
643
- * "Working" header** — the list contradicting itself on one line, which is
644
- * exactly what a derived view model exists to prevent. The value was in scope
645
- * and unread.
646
- *
647
- * The terminal statuses still come off `info.status`, because `ended` collapses
648
- * `failed` and `closed` into one bucket and those are worth telling apart here.
649
- *
650
- * It lives in its own file rather than inside the dashboard's browser because
651
- * the extension's cards draw it too, and a second copy there is how the two
652
- * lists last disagreed about what a parked session looks like.
653
- */
654
484
  function SessionStatusIcon({ row, className }) {
655
485
  const { info } = row;
656
486
  const size = cn("size-4 shrink-0", className);
@@ -665,25 +495,6 @@ function SessionStatusIcon({ row, className }) {
665
495
  }
666
496
  //#endregion
667
497
  //#region src/components/agent/SessionSteps.tsx
668
- /**
669
- * The steps under one session, **agents first**.
670
- *
671
- * The two kinds do different things when pressed and are worth different
672
- * amounts of attention: an agent has work of its own to go and read, a task is a
673
- * marker in a transcript. Interleaved in dispatch order they read as one
674
- * undifferentiated list, and the rows you can actually open are scattered
675
- * through it. Grouped, the openable ones are a block at the top and the markers
676
- * are a tail you can skip.
677
- *
678
- * Stable **within** each group, deliberately: dispatch order is the only order
679
- * these records have that means anything (it is the order the work was started
680
- * in), so the sort partitions and never reorders inside a partition.
681
- *
682
- * `onSelect` is handed the **kind** as well as the id, because the two kinds go
683
- * to different places — see `SessionItem`, which is what routes them. Passing
684
- * only the id is what let a task be opened as if it were an agent, and a framed
685
- * task id matches no items, so the panel drew an **empty agent view**.
686
- */
687
498
  function sessionSteps(info, onSelect) {
688
499
  const steps = (info.subagents ?? []).map((sub) => {
689
500
  const kind = isAgentRecord(sub) ? "agent" : "task";
@@ -707,20 +518,9 @@ function stepState(status) {
707
518
  default: return "done";
708
519
  }
709
520
  }
710
- /** How many of these are still going — the live half of the disclosure's count. */
711
521
  function runningSteps(steps) {
712
522
  return steps.filter((s) => s.state === "running").length;
713
523
  }
714
- /**
715
- * The disclosure, which is also the reading: `3 agents` — or `2 of 3 agents`
716
- * while some have settled, because "how many are still going" is the live
717
- * question and a bare total answers it wrong the moment one finishes.
718
- *
719
- * Sub-agents are an annotation on a working row rather than a state of their own
720
- * (see `runningSubagents` in protocol's `session-list.ts`), so this never
721
- * competes with the row's status glyph: that still says what the *session* is
722
- * doing.
723
- */
724
524
  function StepToggle({ expanded, running, total, noun, onToggle }) {
725
525
  const label = running > 0 && running < total ? `${running}/${total}` : String(total);
726
526
  const words = running > 0 && running < total ? `${running} of ${total} ${noun}s running` : `${total} ${noun}${total === 1 ? "" : "s"}`;
@@ -741,46 +541,6 @@ function StepToggle({ expanded, running, total, noun, onToggle }) {
741
541
  })]
742
542
  });
743
543
  }
744
- /**
745
- * One step under its session — **pressable, all of them**, and what a press
746
- * means is what tells the two kinds apart.
747
- *
748
- * Pressing an **agent** hands the panel over to that agent's own work
749
- * (`SessionPanel.openSubagent`): it is not a session and never becomes one, but
750
- * it has a surface, so it can be the selected thing. Pressing a **task** selects
751
- * the *session* and travels to that task's marker inside it — a task is a
752
- * reference to a place in a transcript, not a thing with a screen, so it can be
753
- * followed but never held.
754
- *
755
- * That is a reversal, and a deliberate one. A task used to be inert markup on
756
- * the argument that "a disabled-looking button still announces itself as one" —
757
- * correct about the markup, wrong about the premise, because there *was* always
758
- * somewhere to go and the row simply swallowed the click on its way there. A row
759
- * that looks like a list item, sits in a list, and does nothing when pressed is
760
- * the worse lie. So every step answers the pointer and every step answers a
761
- * press; only what the press does differs.
762
- *
763
- * Divided from the card's header by **indentation and its own hit shape**, not
764
- * by a rule. The rules came first, on the argument that at 11px an indent is not
765
- * enough to say "list inside a row" — and they were right about the reading and
766
- * wrong about the cost: a stack of hairlines across every open card turned the
767
- * list into a ledger, and a rule cannot answer a pointer. A step that lights up
768
- * under the cursor and fills when it is the one on screen says *list* far more
769
- * plainly than a line between two of them, and it says it while doing the job
770
- * the rule could not.
771
- *
772
- * **Only an agent can wear the selection**, and `active` is guarded on that here
773
- * rather than trusted from the caller: a host that hands back a task's key is
774
- * describing where it navigated, not what it selected, and the row must not
775
- * paint itself blue for it.
776
- *
777
- * The hover is `--row-active` — **alpha, not a flat fill** — because this row
778
- * has to answer the pointer on three different grounds: a transparent card, a
779
- * blue one (its session is selected), and a grey one (a sibling agent is). A
780
- * flat value tuned for any of those is wrong on the other two; a tint darkens or
781
- * lifts whatever it lands on. It is the one place in the list where the alpha
782
- * token earns its keep.
783
- */
784
544
  function StepRow({ step, active = false, onSelect }) {
785
545
  const agent = step.kind === "agent";
786
546
  const selected = active && agent;
@@ -903,7 +663,7 @@ function SessionItem({ row, active = false, activeStepKey, showGateway, showProj
903
663
  children: sessionLabel(info)
904
664
  }),
905
665
  row.unseen > 0 ? /* @__PURE__ */ jsx("span", {
906
- title: `${row.unseen} new`,
666
+ title: `${row.unseen} new message${row.unseen === 1 ? "" : "s"}`,
907
667
  className: cn("flex h-4 min-w-6 shrink-0 items-center justify-center rounded-full px-2", "text-[0.75rem] leading-none tracking-[-0.005em] tabular-nums", row.state === "working" || row.state === "attention" ? "bg-accent text-accent-fg" : "bg-badge text-badge-fg"),
908
668
  children: row.unseen
909
669
  }) : null,
@@ -950,32 +710,12 @@ function SessionItem({ row, active = false, activeStepKey, showGateway, showProj
950
710
  }) : null]
951
711
  });
952
712
  }
953
- /**
954
- * The one 16px cell both lines hang their glyph in.
955
- *
956
- * A shared column rather than a leading character: the state and the engine mark
957
- * are different widths and a bare glyph would let the two text columns start at
958
- * different x. Centring inside a fixed cell is what makes them agree.
959
- */
960
713
  function Gutter({ children }) {
961
714
  return /* @__PURE__ */ jsx("span", {
962
715
  className: "flex size-4 shrink-0 items-center justify-center",
963
716
  children
964
717
  });
965
718
  }
966
- /**
967
- * Enter commits, Escape cancels, blur commits — the editor's inline-rename feel.
968
- * An empty value is a deliberate "clear the name", which the gateway answers by
969
- * handing back the derived title.
970
- *
971
- * The blur rule needs one guard. Selecting a session focuses a different surface
972
- * — in the extension, a different VS Code view — so this document can lose focus
973
- * a tick after a double-click opened the editor, and an unguarded blur reads
974
- * that as "the user clicked away" and closes the editor in the frame it
975
- * appeared. `document.hasFocus()` tells the two apart: focus moving WITHIN this
976
- * document is the user leaving the field; the whole view losing focus is not.
977
- * When the window comes back, so does the caret.
978
- */
979
719
  function NameEditor({ initial, onCommit, onCancel }) {
980
720
  const [value, setValue] = useState(initial);
981
721
  const ref = useRef(null);
@@ -1013,22 +753,6 @@ function NameEditor({ initial, onCommit, onCancel }) {
1013
753
  }
1014
754
  //#endregion
1015
755
  //#region src/components/agent/SessionBrowser.tsx
1016
- /**
1017
- * How a list row is drawn, in one place, so `SidebarRow` in `web` matches this
1018
- * exactly rather than approximating it — the dashboard's other three sidebars
1019
- * are that component, and a sessions list that hovered differently from the
1020
- * gateways list beside it would read as a different product.
1021
- *
1022
- * Two rules are load-bearing:
1023
- *
1024
- * - **Fill means hover, and only hover.** It stays on the row whether or not
1025
- * the row is selected, because a selected row still has to answer the
1026
- * pointer. Selection gets the gutter instead.
1027
- * - **`ml-0` on the selected row is not cosmetic.** It hands the accent border
1028
- * the 4px the margin was holding, so the text does not shift sideways as a
1029
- * row becomes the selected one. The squared left corners are what let the bar
1030
- * sit flush against the sidebar edge.
1031
- */
1032
756
  function rowShapeClass(active) {
1033
757
  return cn("px-2 py-1.5 hover:bg-row-hover", active ? "mr-1 ml-0 rounded-r-md border-l-4 border-l-accent" : "mx-1 rounded-md");
1034
758
  }
@@ -1248,9 +972,6 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, activeS
1248
972
  ]
1249
973
  });
1250
974
  }
1251
- /** The bytes for a row's project icon, if it has an image one and the caller has
1252
- * fetched it yet. Shared by the row and its group header so the two cannot draw
1253
- * different pictures for one project. */
1254
975
  function iconSrcOf(row, icons) {
1255
976
  const icon = row?.info.project?.icon;
1256
977
  return icon?.type === "image" ? icons?.[icon.hash] : void 0;
@@ -1292,16 +1013,6 @@ function SessionRowItem({ row, active, activeSubagentId, showGateway, showProjec
1292
1013
  ] })
1293
1014
  });
1294
1015
  }
1295
- /**
1296
- * One of the dashboard's hover actions.
1297
- *
1298
- * Hover-revealed rather than always on, which is the opposite of the
1299
- * extension's single overflow glyph and the right call for each: there are
1300
- * three of them here and a sidebar card has room for one, so the extension
1301
- * spends that room on a menu and this spends the row's hover on the actions
1302
- * themselves. Both stop the click — the whole card is pressable underneath, and
1303
- * an action that also selected the session would do two things per press.
1304
- */
1305
1016
  function RowAction({ label, title, onClick, children }) {
1306
1017
  return /* @__PURE__ */ jsx(Button, {
1307
1018
  variant: "ghost",
@@ -1316,13 +1027,6 @@ function RowAction({ label, title, onClick, children }) {
1316
1027
  children
1317
1028
  });
1318
1029
  }
1319
- /** A multi-select facet. Empty = no filter, which is why the trigger reads the
1320
- * facet's name rather than "All": nothing is being excluded. */
1321
- /**
1322
- * One labelled control row. The label column is fixed so every control starts
1323
- * on the same x — the thing that makes a stack of them read as a form rather
1324
- * than as five unrelated widgets.
1325
- */
1326
1030
  function FilterRow({ label, children }) {
1327
1031
  return /* @__PURE__ */ jsxs("div", {
1328
1032
  className: "flex items-center gap-2",