@workerdeck/ui 0.23.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/{SessionPanel-DUt2VzXG.mjs → SessionPanel-DDgBkHsi.mjs} +2151 -3357
- package/build/SessionPanel-DDgBkHsi.mjs.map +1 -0
- package/build/SessionPanel-E_L_ldQ5.d.mts +207 -0
- package/build/format-B8ZM9LPy.d.mts +16 -0
- package/build/format.d.mts +8 -75
- package/build/format.mjs +2 -2
- package/build/index.d.mts +63 -1138
- package/build/index.mjs +4 -300
- package/build/index.mjs.map +1 -1
- package/build/scoped.css +15 -23
- package/build/{status-BE-zg88x.mjs → status-C0HSLZas.mjs} +14 -85
- package/build/status-C0HSLZas.mjs.map +1 -0
- package/build/workspace.d.mts +5 -126
- package/build/workspace.mjs +11 -143
- package/build/workspace.mjs.map +1 -1
- package/package.json +4 -4
- package/src/components/agent/CodeEditor.tsx +31 -103
- package/src/components/agent/Composer.tsx +114 -333
- package/src/components/agent/ContextDialog.tsx +20 -41
- package/src/components/agent/ContextRing.tsx +6 -28
- package/src/components/agent/Conversation.tsx +21 -33
- package/src/components/agent/EditorTabs.tsx +39 -67
- package/src/components/agent/EngineIcon.tsx +29 -72
- package/src/components/agent/EntryIcon.tsx +16 -0
- package/src/components/agent/FileCard.tsx +10 -16
- package/src/components/agent/FileTree.tsx +52 -125
- package/src/components/agent/FileViewer.tsx +30 -60
- package/src/components/agent/HostFilesDialog.tsx +40 -69
- package/src/components/agent/Loader.tsx +3 -24
- package/src/components/agent/McpDialog.tsx +64 -137
- package/src/components/agent/Message.tsx +2 -21
- package/src/components/agent/ModelSelect.tsx +33 -62
- package/src/components/agent/PermissionModeSelect.tsx +21 -79
- package/src/components/agent/PermissionPrompt.tsx +59 -63
- package/src/components/agent/ProjectIcon.tsx +228 -93
- package/src/components/agent/PromptTokenText.tsx +10 -16
- package/src/components/agent/QuestionPrompt.tsx +43 -64
- package/src/components/agent/Reasoning.tsx +13 -16
- package/src/components/agent/Response.tsx +3 -9
- package/src/components/agent/Scrubber.tsx +31 -80
- package/src/components/agent/SessionBrowser.tsx +64 -243
- package/src/components/agent/SessionEmptyState.tsx +10 -40
- package/src/components/agent/SessionInfoDialog.tsx +46 -78
- package/src/components/agent/SessionItem.tsx +53 -320
- package/src/components/agent/SessionList.tsx +23 -39
- package/src/components/agent/SessionPanel.tsx +315 -996
- package/src/components/agent/SessionStatusIcon.tsx +14 -28
- package/src/components/agent/SessionSteps.tsx +34 -187
- package/src/components/agent/SessionWorkspace.tsx +104 -219
- package/src/components/agent/SkillsDialog.tsx +35 -80
- package/src/components/agent/StatusBar.tsx +51 -136
- package/src/components/agent/SubagentStrip.tsx +12 -77
- package/src/components/agent/ToolCallCard.tsx +94 -135
- package/src/components/agent/Transcript.tsx +103 -1288
- package/src/components/agent/TranscriptItemView.tsx +155 -0
- package/src/components/agent/TranscriptRows.tsx +418 -0
- package/src/components/agent/UsageDialog.tsx +7 -29
- package/src/components/agent/UsageMeters.tsx +18 -73
- package/src/components/agent/pulse.tsx +9 -32
- package/src/components/agent/scrubber-marks.ts +33 -103
- package/src/components/agent/status.ts +1 -5
- package/src/components/agent/tool-result-fetch.tsx +3 -25
- package/src/components/agent/tool-result-image.tsx +32 -111
- package/src/components/agent/transcript-rows.ts +41 -118
- package/src/components/agent/transcript-variant.tsx +3 -80
- package/src/components/agent/use-height-epoch.ts +10 -24
- package/src/components/agent/use-path-links.ts +45 -63
- package/src/components/agent/use-subagent-frame.ts +106 -0
- package/src/components/agent/use-transcript-jumps.ts +49 -107
- package/src/components/prompt-area/animated-placeholder.tsx +6 -5
- package/src/components/prompt-area/clipboard-helpers.ts +26 -21
- package/src/components/prompt-area/cursor-helpers.ts +79 -37
- package/src/components/prompt-area/dom-helpers.ts +97 -69
- package/src/components/prompt-area/file-strip.tsx +90 -80
- package/src/components/prompt-area/html-to-markdown.ts +67 -46
- package/src/components/prompt-area/image-strip.tsx +8 -18
- package/src/components/prompt-area/index.ts +2 -15
- package/src/components/prompt-area/prompt-area-engine.ts +100 -115
- package/src/components/prompt-area/prompt-area-list-ops.ts +66 -59
- package/src/components/prompt-area/prompt-area.tsx +31 -38
- package/src/components/prompt-area/remove-button.tsx +3 -9
- package/src/components/prompt-area/segment-helpers.ts +4 -12
- package/src/components/prompt-area/trigger-popover.tsx +15 -27
- package/src/components/prompt-area/trigger-presets.ts +0 -10
- package/src/components/prompt-area/types.ts +3 -8
- package/src/components/prompt-area/use-chip-editing.ts +305 -0
- package/src/components/prompt-area/use-markdown-mode.ts +7 -17
- package/src/components/prompt-area/use-prompt-area-events.ts +80 -86
- package/src/components/prompt-area/use-prompt-area-keydown.ts +598 -0
- package/src/components/prompt-area/use-prompt-area-state.ts +5 -23
- package/src/components/prompt-area/use-prompt-area.ts +170 -896
- package/src/components/prompt-area/use-trigger-search.ts +21 -19
- package/src/components/terminal/PermissionPrompt.tsx +56 -59
- package/src/components/terminal/QuestionPrompt.tsx +28 -79
- package/src/components/terminal/StatusLine.tsx +13 -63
- package/src/components/terminal/TerminalTranscript.tsx +44 -160
- package/src/components/terminal/affordances.tsx +68 -98
- package/src/components/terminal/blocks.ts +20 -156
- package/src/components/terminal/diff.tsx +19 -67
- package/src/components/terminal/height.ts +163 -313
- package/src/components/terminal/image-box.ts +0 -44
- package/src/components/terminal/items.tsx +150 -305
- package/src/components/terminal/markdown.tsx +36 -95
- package/src/components/terminal/press.tsx +26 -53
- package/src/components/terminal/prompt.tsx +40 -170
- package/src/components/terminal/result-preview.ts +8 -56
- package/src/components/terminal/row.tsx +11 -98
- package/src/components/terminal/scrubber.tsx +108 -401
- package/src/components/terminal/surface.tsx +6 -54
- package/src/components/terminal/todos.ts +66 -0
- package/src/components/terminal/tool-run.ts +21 -192
- package/src/components/ui/AlertDialog.tsx +8 -23
- package/src/components/ui/Badge.tsx +18 -29
- package/src/components/ui/Button.tsx +7 -15
- package/src/components/ui/Card.tsx +2 -13
- package/src/components/ui/CodeBlock.tsx +10 -37
- package/src/components/ui/CopyButton.tsx +10 -23
- package/src/components/ui/Dialog.tsx +23 -36
- package/src/components/ui/Empty.tsx +6 -29
- package/src/components/ui/Input.tsx +14 -16
- package/src/components/ui/Menu.tsx +6 -18
- package/src/components/ui/PortalScope.tsx +1 -21
- package/src/components/ui/ProgressRing.tsx +8 -19
- package/src/components/ui/Select.tsx +18 -31
- package/src/components/ui/Sonner.tsx +1 -2
- package/src/components/ui/Spinner.tsx +1 -1
- package/src/components/ui/Splitter.tsx +30 -64
- package/src/components/ui/Textarea.tsx +2 -5
- package/src/components/ui/Tooltip.tsx +3 -13
- package/src/format.ts +0 -9
- package/src/index.ts +16 -113
- package/src/lib/clipboard.ts +8 -28
- package/src/lib/css.ts +8 -0
- package/src/lib/format.ts +101 -86
- package/src/lib/plan-request.ts +16 -0
- package/src/lib/status.ts +45 -95
- package/src/lib/tool-icon.ts +39 -47
- package/src/lib/utils.ts +1 -3
- package/src/styles/terminal.css +81 -49
- package/src/styles/theme.css +96 -32
- package/src/workspace.ts +1 -24
- package/build/SessionPanel-BobynUo4.d.mts +0 -669
- package/build/SessionPanel-DUt2VzXG.mjs.map +0 -1
- package/build/format-ljc3lKpA.d.mts +0 -59
- package/build/status-BE-zg88x.mjs.map +0 -1
package/build/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import {
|
|
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
|
|
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",
|