@invana/boards 0.0.32

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/dist/index.cjs ADDED
@@ -0,0 +1,1121 @@
1
+ 'use strict';
2
+
3
+ var ui = require('@invana/ui');
4
+ var editor = require('@invana/editor');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var React = require('react');
7
+ var tables = require('@invana/tables');
8
+ var blocks = require('@invana/blocks');
9
+ var forms = require('@invana/forms');
10
+ var lucideReact = require('lucide-react');
11
+
12
+ function _interopNamespace(e) {
13
+ if (e && e.__esModule) return e;
14
+ var n = Object.create(null);
15
+ if (e) {
16
+ Object.keys(e).forEach(function (k) {
17
+ if (k !== 'default') {
18
+ var d = Object.getOwnPropertyDescriptor(e, k);
19
+ Object.defineProperty(n, k, d.get ? d : {
20
+ enumerable: true,
21
+ get: function () { return e[k]; }
22
+ });
23
+ }
24
+ });
25
+ }
26
+ n.default = e;
27
+ return Object.freeze(n);
28
+ }
29
+
30
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
31
+
32
+ // src/panels/data.tsx
33
+ function JsonPanel({ options }) {
34
+ const value = typeof options.value === "string" ? options.value : JSON.stringify(options.value, null, 2);
35
+ return /* @__PURE__ */ jsxRuntime.jsx(editor.CodeBlock, { language: "json", value, maxHeight: options.maxHeight, className: "border-0" });
36
+ }
37
+ function CodePanel({ options }) {
38
+ return /* @__PURE__ */ jsxRuntime.jsx(
39
+ editor.CodeBlock,
40
+ {
41
+ language: options.language ?? "plain",
42
+ value: options.value,
43
+ maxHeight: options.maxHeight,
44
+ showLineNumbers: options.showLineNumbers,
45
+ className: "border-0"
46
+ }
47
+ );
48
+ }
49
+ function ExchangePanel({ options }) {
50
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: options.blocks.map((block, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
51
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Eyebrow, { children: block.label }),
52
+ /* @__PURE__ */ jsxRuntime.jsx(editor.CodeBlock, { language: block.language ?? "plain", value: block.value })
53
+ ] }, i)) });
54
+ }
55
+ var LEVELS = ["error", "warn", "info", "debug"];
56
+ var offset = (ms) => `${(ms / 1e3).toFixed(3)}s`;
57
+ var took = (ms) => ms == null ? "\u2014" : ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(1)}s`;
58
+ function KindBadge({ kind, level }) {
59
+ if (level === "error")
60
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { variant: "destructive", size: "xs", children: kind });
61
+ if (level === "warn")
62
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { variant: "soft", tone: "warning", size: "xs", children: kind });
63
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { variant: "outline", tone: "muted", size: "xs", children: kind });
64
+ }
65
+ function StatusBadge({ status }) {
66
+ if (status === "succeeded")
67
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { variant: "soft", tone: "success", size: "xs", children: status });
68
+ if (status === "failed")
69
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { variant: "destructive", size: "xs", children: status });
70
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { variant: "outline", tone: "muted", size: "xs", children: status.replace("_", " ") });
71
+ }
72
+ function EventsPanel(props) {
73
+ return props.options.layout === "tree" ? /* @__PURE__ */ jsxRuntime.jsx(EventTree, { ...props }) : /* @__PURE__ */ jsxRuntime.jsx(EventLog, { ...props });
74
+ }
75
+ function EventLog({ panel, options, onAction }) {
76
+ const { selectAction, hideTask } = options;
77
+ const columns = React__namespace.useMemo(
78
+ () => [
79
+ {
80
+ id: "at",
81
+ accessorKey: "atMs",
82
+ header: "t",
83
+ size: 84,
84
+ meta: { mono: true, align: "right" },
85
+ cell: ({ getValue }) => offset(getValue())
86
+ },
87
+ {
88
+ id: "kind",
89
+ accessorKey: "kind",
90
+ header: "Kind",
91
+ size: 132,
92
+ cell: ({ row }) => /* @__PURE__ */ jsxRuntime.jsx(KindBadge, { kind: row.original.kind, level: row.original.level })
93
+ },
94
+ ...hideTask ? [] : [
95
+ {
96
+ id: "task",
97
+ accessorKey: "task",
98
+ header: "Task",
99
+ size: 150,
100
+ meta: { mono: true },
101
+ cell: ({ getValue }) => getValue() ?? "\u2014"
102
+ }
103
+ ],
104
+ {
105
+ id: "took",
106
+ accessorKey: "tookMs",
107
+ header: "Took",
108
+ size: 72,
109
+ meta: { mono: true, align: "right" },
110
+ cell: ({ getValue }) => took(getValue())
111
+ },
112
+ { id: "message", accessorKey: "message", header: "Detail", size: 360, enableSorting: false }
113
+ ],
114
+ [hideTask]
115
+ );
116
+ const levels = React__namespace.useMemo(() => LEVELS.filter((l) => options.events.some((e) => e.level === l)), [options.events]);
117
+ const filters = React__namespace.useMemo(
118
+ () => [
119
+ { id: "kind", label: "kind" },
120
+ ...hideTask ? [] : [{ id: "task", label: "task" }],
121
+ { id: "level", label: "level", options: levels, match: (e, picked) => picked.includes(e.level) }
122
+ ],
123
+ [hideTask, levels]
124
+ );
125
+ return /* @__PURE__ */ jsxRuntime.jsx(
126
+ tables.PaginatedTable,
127
+ {
128
+ columns,
129
+ data: options.events,
130
+ density: "compact",
131
+ seamless: true,
132
+ filters,
133
+ searchColumns: hideTask ? ["message"] : ["task", "message"],
134
+ searchPlaceholder: hideTask ? "Search detail\u2026" : "Search task or detail\u2026",
135
+ noun: "events",
136
+ pageSize: options.pageSize ?? 25,
137
+ pageSizeOptions: [25, 50, 100],
138
+ onRowClick: selectAction ? (e) => e.task && onAction(selectAction, { panelId: panel.id, value: e.task }) : void 0
139
+ }
140
+ );
141
+ }
142
+ function treeOf(options) {
143
+ const tasks = options.tasks ?? [];
144
+ const row = (t) => ({
145
+ id: t.key,
146
+ name: t.key,
147
+ atMs: t.startMs,
148
+ tookMs: t.durationMs,
149
+ summary: t.summary ?? "",
150
+ status: t.status,
151
+ task: t.key,
152
+ children: [
153
+ ...options.events.filter((e) => e.task === t.key).sort((a, b) => a.atMs - b.atMs).map((e) => ({
154
+ id: e.id,
155
+ name: e.kind,
156
+ atMs: e.atMs,
157
+ tookMs: e.tookMs,
158
+ summary: e.message,
159
+ event: e,
160
+ task: t.key
161
+ })),
162
+ ...tasks.filter((c) => c.parent === t.key).map(row)
163
+ ]
164
+ });
165
+ return tasks.filter((t) => !t.parent).map(row);
166
+ }
167
+ function troublePaths(options) {
168
+ const parent = new Map((options.tasks ?? []).map((t) => [t.key, t.parent]));
169
+ const open = {};
170
+ for (const e of options.events.filter((ev) => ev.level === "error" || ev.level === "warn")) {
171
+ open[e.id] = true;
172
+ for (let k = e.task; k; k = parent.get(k)) open[k] = true;
173
+ }
174
+ return open;
175
+ }
176
+ var resolve = (next, old) => typeof next === "function" ? next(old) : next;
177
+ var TREE_COLUMNS = [
178
+ {
179
+ id: "name",
180
+ accessorKey: "name",
181
+ header: "Task / event",
182
+ size: 260,
183
+ cell: ({ row }) => row.original.event ? /* @__PURE__ */ jsxRuntime.jsx(KindBadge, { kind: row.original.event.kind, level: row.original.event.level }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", children: row.original.name })
184
+ },
185
+ {
186
+ id: "status",
187
+ header: "Status",
188
+ size: 100,
189
+ cell: ({ row }) => row.original.status ? /* @__PURE__ */ jsxRuntime.jsx(StatusBadge, { status: row.original.status }) : "\u2014"
190
+ },
191
+ {
192
+ id: "at",
193
+ accessorKey: "atMs",
194
+ header: "t",
195
+ size: 84,
196
+ meta: { mono: true, align: "right" },
197
+ cell: ({ getValue }) => {
198
+ const v = getValue();
199
+ return v == null ? "\u2014" : offset(v);
200
+ }
201
+ },
202
+ {
203
+ id: "took",
204
+ accessorKey: "tookMs",
205
+ header: "Took",
206
+ size: 72,
207
+ meta: { mono: true, align: "right" },
208
+ cell: ({ getValue }) => took(getValue())
209
+ },
210
+ { id: "summary", accessorKey: "summary", header: "Detail", size: 360, enableSorting: false }
211
+ ];
212
+ function EventTree({ panel, options, onAction }) {
213
+ const tree = React__namespace.useMemo(() => treeOf(options), [options]);
214
+ const roots = React__namespace.useMemo(() => Object.fromEntries(tree.map((r) => [r.id, true])), [tree]);
215
+ const [expanded, setExpanded] = React__namespace.useState(roots);
216
+ const { selectAction } = options;
217
+ return /* @__PURE__ */ jsxRuntime.jsx(
218
+ tables.DataTable,
219
+ {
220
+ columns: TREE_COLUMNS,
221
+ data: tree,
222
+ density: "compact",
223
+ seamless: true,
224
+ getSubRows: (r) => r.children,
225
+ getRowId: (r) => r.id,
226
+ expanded,
227
+ onExpandedChange: (next) => setExpanded(resolve(next, expanded)),
228
+ canExpand: (r) => r.event?.fields != null,
229
+ enableSorting: false,
230
+ enableColumnVisibility: false,
231
+ onRowClick: selectAction ? (r) => r.task && onAction(selectAction, { panelId: panel.id, value: r.task }) : void 0,
232
+ toolbar: /* @__PURE__ */ jsxRuntime.jsxs(ui.ButtonGroup, { children: [
233
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "xs", variant: "outline", onClick: () => setExpanded(troublePaths(options)), children: "Open failures" }),
234
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "xs", variant: "outline", onClick: () => setExpanded(true), children: "Expand all" }),
235
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "xs", variant: "outline", onClick: () => setExpanded({}), children: "Collapse all" })
236
+ ] }),
237
+ renderExpanded: (r) => r.event?.fields ? /* @__PURE__ */ jsxRuntime.jsx(ui.PropertyList, { labelWidth: 80, children: Object.entries(r.event.fields).map(([label, value]) => /* @__PURE__ */ jsxRuntime.jsx(ui.PropertyRow, { label, mono: true, children: value }, label)) }) : null
238
+ }
239
+ );
240
+ }
241
+ function BlockPanel({ panel, options, onAction }) {
242
+ return /* @__PURE__ */ jsxRuntime.jsx(
243
+ blocks.Block,
244
+ {
245
+ spec: { kind: panel.kind, ...options },
246
+ id: panel.id,
247
+ state: panel.state,
248
+ value: panel.value,
249
+ onAction: (action, value) => (
250
+ // An action the spec declares arrives by its own id, as every panel's does.
251
+ action === "action" ? onAction(String(value), { panelId: panel.id }) : onAction(action, { panelId: panel.id, value })
252
+ )
253
+ }
254
+ );
255
+ }
256
+ var BLOCK_PANELS = Object.fromEntries(blocks.BLOCKS.map((b) => [b.id, BlockPanel]));
257
+ function SpecChip({ chip }) {
258
+ if (chip.bound)
259
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.BoundChip, { bound: chip.bound, swatch: chip.swatch });
260
+ return /* @__PURE__ */ jsxRuntime.jsx(
261
+ ui.Badge,
262
+ {
263
+ variant: chip.variant ?? "outline",
264
+ tone: chip.tone === "running" || chip.tone === "error" ? void 0 : chip.tone,
265
+ size: "sm",
266
+ children: chip.label
267
+ }
268
+ );
269
+ }
270
+ function SpecChips({ chips }) {
271
+ if (!chips?.length) return null;
272
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: chips.map((chip, i) => /* @__PURE__ */ jsxRuntime.jsx(SpecChip, { chip }, i)) });
273
+ }
274
+ function SpecAction({
275
+ action,
276
+ onAction,
277
+ icons,
278
+ ctx
279
+ }) {
280
+ const switchId = React__namespace.useId();
281
+ const optionLabel = (option) => action.optionLabels?.[option] ?? option;
282
+ if (action.options?.length && action.menu) {
283
+ const Trigger = action.icon ? icons[action.icon] : void 0;
284
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
285
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
286
+ ui.Button,
287
+ {
288
+ variant: action.variant ?? "outline",
289
+ size: Trigger ? "icon-sm" : "sm",
290
+ disabled: action.disabled,
291
+ "aria-label": action.label ?? action.id,
292
+ children: Trigger ? /* @__PURE__ */ jsxRuntime.jsx(Trigger, {}) : action.label
293
+ }
294
+ ) }),
295
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuContent, { align: "end", children: action.options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenuItem, { onSelect: () => onAction(action.id, { ...ctx, option }), children: optionLabel(option) }, option)) })
296
+ ] });
297
+ }
298
+ if (action.options?.length && action.picker) {
299
+ return /* @__PURE__ */ jsxRuntime.jsxs(
300
+ forms.Select,
301
+ {
302
+ value: action.value,
303
+ disabled: action.disabled,
304
+ onValueChange: (option) => onAction(action.id, { ...ctx, option }),
305
+ children: [
306
+ /* @__PURE__ */ jsxRuntime.jsx(forms.SelectTrigger, { triggerSize: "sm", className: "w-auto gap-2", "aria-label": action.label ?? action.id, children: /* @__PURE__ */ jsxRuntime.jsx(forms.SelectValue, {}) }),
307
+ /* @__PURE__ */ jsxRuntime.jsx(forms.SelectContent, { children: action.options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(forms.SelectItem, { value: option, children: optionLabel(option) }, option)) })
308
+ ]
309
+ }
310
+ );
311
+ }
312
+ if (action.options?.length) {
313
+ return /* @__PURE__ */ jsxRuntime.jsx(
314
+ ui.ToggleGroup,
315
+ {
316
+ type: "single",
317
+ size: "sm",
318
+ value: action.value,
319
+ disabled: action.disabled,
320
+ onValueChange: (option) => option && onAction(action.id, { ...ctx, option }),
321
+ children: action.options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(ui.ToggleGroupItem, { value: option, children: optionLabel(option) }, option))
322
+ }
323
+ );
324
+ }
325
+ const Icon = action.icon ? icons[action.icon] : void 0;
326
+ const iconOnly = !action.label && Icon;
327
+ if (action.pressed != null) {
328
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1.5", children: [
329
+ /* @__PURE__ */ jsxRuntime.jsx(forms.Label, { htmlFor: switchId, className: "font-normal", children: action.label ?? action.id }),
330
+ /* @__PURE__ */ jsxRuntime.jsx(
331
+ forms.Switch,
332
+ {
333
+ id: switchId,
334
+ checked: action.pressed,
335
+ disabled: action.disabled,
336
+ onCheckedChange: (pressed) => onAction(action.id, { ...ctx, pressed })
337
+ }
338
+ )
339
+ ] });
340
+ }
341
+ return /* @__PURE__ */ jsxRuntime.jsxs(
342
+ ui.Button,
343
+ {
344
+ variant: action.variant ?? "outline",
345
+ size: iconOnly ? "icon-sm" : "sm",
346
+ disabled: action.disabled,
347
+ "aria-label": iconOnly ? action.id : void 0,
348
+ onClick: () => onAction(action.id, ctx),
349
+ children: [
350
+ Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) : null,
351
+ action.label
352
+ ]
353
+ }
354
+ );
355
+ }
356
+ function SpecActions({
357
+ actions,
358
+ onAction,
359
+ icons,
360
+ ctx
361
+ }) {
362
+ if (!actions?.length) return null;
363
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: actions.map((action) => /* @__PURE__ */ jsxRuntime.jsx(
364
+ SpecAction,
365
+ {
366
+ action,
367
+ onAction,
368
+ icons,
369
+ ctx
370
+ },
371
+ action.id
372
+ )) });
373
+ }
374
+ function LogPanel({ options }) {
375
+ const columns = options.columnTemplate ?? "48px 44px 132px minmax(0,1fr)";
376
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Terminal, { columnTemplate: columns, children: options.lines.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx(
377
+ ui.TerminalLine,
378
+ {
379
+ level: line.level,
380
+ columns: [
381
+ line.time ?? "",
382
+ line.level ? line.level.toUpperCase() : "",
383
+ line.source ?? "",
384
+ line.message
385
+ ]
386
+ },
387
+ i
388
+ )) });
389
+ }
390
+ function ListPanel({ panel, options, onAction, icons }) {
391
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col", children: options.items.map((item, i) => {
392
+ const Icon = item.icon ? icons[item.icon] : void 0;
393
+ const clickable = Boolean(item.action);
394
+ return /* @__PURE__ */ jsxRuntime.jsxs(
395
+ ui.Item,
396
+ {
397
+ size: "xs",
398
+ className: ui.cn(
399
+ "flex-nowrap gap-2 border-b border-border/55 last:border-b-0",
400
+ clickable && "cursor-pointer"
401
+ ),
402
+ onClick: clickable ? () => onAction(item.action, { panelId: panel.id, itemId: item.id }) : void 0,
403
+ children: [
404
+ item.tone || Icon ? /* @__PURE__ */ jsxRuntime.jsxs(ui.ItemMedia, { children: [
405
+ item.tone ? /* @__PURE__ */ jsxRuntime.jsx(ui.StatusDot, { tone: item.tone, size: "md" }) : null,
406
+ Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-3.5 text-muted-foreground" }) : null
407
+ ] }) : null,
408
+ /* @__PURE__ */ jsxRuntime.jsx(ui.ItemContent, { className: "min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(ui.ItemTitle, { className: ui.cn("w-auto min-w-0", item.mono && "font-mono"), children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: item.title }) }) }),
409
+ item.meta || item.chip ? /* @__PURE__ */ jsxRuntime.jsxs(ui.ItemActions, { className: "min-w-0 shrink justify-end", children: [
410
+ item.meta ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-[16ch] truncate font-mono text-sm text-muted-foreground", children: item.meta }) : null,
411
+ item.chip ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(SpecChip, { chip: item.chip }) }) : null
412
+ ] }) : null
413
+ ]
414
+ },
415
+ item.id ?? i
416
+ );
417
+ }) });
418
+ }
419
+ function ParamsPanel({ panel, options, onAction }) {
420
+ const emit = options.changeAction;
421
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col", children: options.params.map((param) => /* @__PURE__ */ jsxRuntime.jsx(
422
+ forms.ParamRow,
423
+ {
424
+ name: param.name,
425
+ type: param.type,
426
+ source: param.source,
427
+ value: param.value,
428
+ note: param.note,
429
+ invalid: param.invalid,
430
+ disabled: param.disabled,
431
+ onSourceChange: emit ? (source) => onAction(emit, {
432
+ panelId: panel.id,
433
+ param: { name: param.name, source, value: param.value }
434
+ }) : void 0,
435
+ onValueChange: emit ? (value) => onAction(emit, {
436
+ panelId: panel.id,
437
+ param: { name: param.name, source: param.source, value }
438
+ }) : void 0
439
+ },
440
+ param.name
441
+ )) });
442
+ }
443
+ var TEXT_TONE = {
444
+ default: "text-foreground",
445
+ muted: "text-muted-foreground",
446
+ success: "text-success",
447
+ warning: "text-warning",
448
+ error: "text-destructive",
449
+ info: "text-info"
450
+ };
451
+ function TextPanel({ panel, options, onAction, icons }) {
452
+ const body = /* @__PURE__ */ jsxRuntime.jsx("p", { className: ui.cn("text-sm", TEXT_TONE[options.tone ?? "muted"]), children: options.text });
453
+ const content = options.callout ? /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: options.tone === "error" ? "destructive" : "default", children: /* @__PURE__ */ jsxRuntime.jsx(ui.AlertDescription, { children: body }) }) : body;
454
+ if (!options.actions?.length) return content;
455
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
456
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: content }),
457
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex shrink-0 items-center gap-1.5", children: /* @__PURE__ */ jsxRuntime.jsx(
458
+ SpecActions,
459
+ {
460
+ actions: options.actions,
461
+ onAction,
462
+ icons,
463
+ ctx: { panelId: panel.id }
464
+ }
465
+ ) })
466
+ ] });
467
+ }
468
+
469
+ // src/registry.ts
470
+ var BUILT_IN_PANELS = {
471
+ json: JsonPanel,
472
+ code: CodePanel,
473
+ exchange: ExchangePanel,
474
+ log: LogPanel,
475
+ events: EventsPanel,
476
+ list: ListPanel,
477
+ params: ParamsPanel,
478
+ text: TextPanel
479
+ };
480
+ function resolveRegistry(extra) {
481
+ return { ...BLOCK_PANELS, ...BUILT_IN_PANELS, ...extra };
482
+ }
483
+ var RecordDescription = React__namespace.forwardRef(
484
+ ({ description, details, moreLabel = "More", lessLabel = "Less", className, ...props }, ref) => {
485
+ const [expanded, setExpanded] = React__namespace.useState(false);
486
+ const [clipped, setClipped] = React__namespace.useState(false);
487
+ const lineRef = React__namespace.useRef(null);
488
+ React__namespace.useLayoutEffect(() => {
489
+ const el = lineRef.current;
490
+ if (!el || expanded) return;
491
+ const measure = () => setClipped(el.scrollWidth - el.clientWidth > 1);
492
+ measure();
493
+ const observer = new ResizeObserver(measure);
494
+ observer.observe(el);
495
+ return () => observer.disconnect();
496
+ }, [expanded, description]);
497
+ const hasMore = clipped || expanded || (details?.length ?? 0) > 0;
498
+ return /* @__PURE__ */ jsxRuntime.jsxs(
499
+ "div",
500
+ {
501
+ ref,
502
+ className: ui.cn("flex flex-col gap-1 border-b border-border bg-card px-3 pb-2", className),
503
+ ...props,
504
+ children: [
505
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 items-baseline gap-2", children: [
506
+ /* @__PURE__ */ jsxRuntime.jsx(
507
+ "p",
508
+ {
509
+ ref: lineRef,
510
+ className: ui.cn(
511
+ "min-w-0 flex-1 text-sm text-muted-foreground",
512
+ expanded ? "whitespace-pre-line" : "truncate"
513
+ ),
514
+ children: description
515
+ }
516
+ ),
517
+ hasMore ? /* @__PURE__ */ jsxRuntime.jsx(
518
+ ui.Button,
519
+ {
520
+ type: "button",
521
+ variant: "link",
522
+ size: "sm",
523
+ className: "h-auto shrink-0 p-0",
524
+ "aria-expanded": expanded,
525
+ onClick: () => setExpanded((open) => !open),
526
+ children: expanded ? lessLabel : moreLabel
527
+ }
528
+ ) : null
529
+ ] }),
530
+ expanded && details?.length ? /* @__PURE__ */ jsxRuntime.jsx(ui.PropertyList, { labelWidth: 110, children: details.map((d, i) => /* @__PURE__ */ jsxRuntime.jsx(ui.PropertyRow, { label: d.label, mono: d.mono, children: d.value }, i)) }) : null
531
+ ]
532
+ }
533
+ );
534
+ }
535
+ );
536
+ RecordDescription.displayName = "RecordDescription";
537
+ var OP_SIGN = { add: "+", remove: "\u2212", change: "~" };
538
+ var OP_CLASS = {
539
+ add: "text-success",
540
+ remove: "text-destructive",
541
+ change: "text-warning"
542
+ };
543
+ var StagedBar = React__namespace.forwardRef(
544
+ ({ items, onDiscard, onDiscardAll, hint, discardAllLabel = "Discard all", className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
545
+ "div",
546
+ {
547
+ ref,
548
+ role: "region",
549
+ "aria-label": "Staged changes",
550
+ className: ui.cn(
551
+ "flex min-h-control-md shrink-0 flex-wrap items-center gap-2 border-b border-border bg-primary/5 px-3 py-1",
552
+ className
553
+ ),
554
+ ...props,
555
+ children: [
556
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 text-sm font-semibold", children: [
557
+ items.length,
558
+ " staged"
559
+ ] }),
560
+ items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
561
+ "span",
562
+ {
563
+ className: "inline-flex max-w-full items-center gap-1.5 border border-border bg-card px-1.5 py-0.5 text-xs",
564
+ children: [
565
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: ui.cn("font-mono", OP_CLASS[item.op] ?? "text-muted-foreground"), children: OP_SIGN[item.op] ?? item.op }),
566
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-mono", children: item.name }),
567
+ item.note != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-muted-foreground", children: item.note }) : null,
568
+ onDiscard ? /* @__PURE__ */ jsxRuntime.jsx(
569
+ "button",
570
+ {
571
+ type: "button",
572
+ "aria-label": "Discard this change",
573
+ className: "text-muted-foreground hover:text-foreground focus-visible:outline-none",
574
+ onClick: () => onDiscard(item.id),
575
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-3" })
576
+ }
577
+ ) : null
578
+ ]
579
+ },
580
+ item.id
581
+ )),
582
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1" }),
583
+ onDiscardAll ? /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "ghost", size: "sm", onClick: onDiscardAll, children: discardAllLabel }) : null,
584
+ hint != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-sm text-muted-foreground", children: hint }) : null
585
+ ]
586
+ }
587
+ )
588
+ );
589
+ StagedBar.displayName = "StagedBar";
590
+ var BoardPatchError = class extends Error {
591
+ };
592
+ function onRows(rows, id, fn, hit) {
593
+ return rows.map((row) => {
594
+ if (!row.panels.some((p) => p.id === id)) return row;
595
+ hit.found = true;
596
+ return { ...row, panels: row.panels.map((p) => p.id === id ? fn(p) : p) };
597
+ });
598
+ }
599
+ function patchPanel(panel, patch) {
600
+ if (patch.op === "update-panel") {
601
+ const { id, kind, options } = panel;
602
+ return { ...blocks.applyBlockPatch(panel, { op: "set", fields: patch.fields }), id, kind, options };
603
+ }
604
+ try {
605
+ return { ...panel, options: blocks.applyBlockPatch(panel.options ?? {}, patch.patch) };
606
+ } catch (error) {
607
+ if (error instanceof blocks.BlockPatchError) throw new BoardPatchError(`"patch-panel" on "${patch.panel}": ${error.message}`);
608
+ throw error;
609
+ }
610
+ }
611
+ function applyBoardPatch(spec, patch) {
612
+ const loose = spec;
613
+ const hit = { found: false };
614
+ const fn = (panel) => patchPanel(panel, patch);
615
+ const next = {
616
+ ...loose,
617
+ rows: onRows(loose.rows, patch.panel, fn, hit),
618
+ ...loose.tabs ? { tabs: loose.tabs.map((tab) => ({ ...tab, rows: onRows(tab.rows, patch.panel, fn, hit) })) } : {}
619
+ };
620
+ if (!hit.found) throw new BoardPatchError(`"${patch.op}": no panel "${patch.panel}" on the board.`);
621
+ return next;
622
+ }
623
+ function applyBoardPatches(spec, patches) {
624
+ return patches.reduce(applyBoardPatch, spec);
625
+ }
626
+ function isDropped(panel) {
627
+ return panel.absent?.reason === "unrecorded";
628
+ }
629
+ function UnknownPanel({ kind }) {
630
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
631
+ "No renderer for panel kind ",
632
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", children: kind }),
633
+ ". Register one, or remove it from the spec."
634
+ ] });
635
+ }
636
+ function Panel({
637
+ panel,
638
+ registry,
639
+ onAction,
640
+ icons,
641
+ gap
642
+ }) {
643
+ const Renderer = registry[panel.kind];
644
+ const body = panel.absent ? /* @__PURE__ */ jsxRuntime.jsx(ui.AbsenceNote, { reason: panel.absent.reason, label: panel.absent.label, children: panel.absent.note }) : panel.render ?? (Renderer ? /* @__PURE__ */ jsxRuntime.jsx(
645
+ Renderer,
646
+ {
647
+ panel,
648
+ options: panel.options ?? {},
649
+ onAction,
650
+ icons,
651
+ gap
652
+ }
653
+ ) : /* @__PURE__ */ jsxRuntime.jsx(UnknownPanel, { kind: panel.kind }));
654
+ const style = panel.width ? { width: panel.width, flexShrink: 0 } : { flexGrow: panel.grow ?? 1, flexBasis: 0, minWidth: 0 };
655
+ return /* @__PURE__ */ jsxRuntime.jsx(
656
+ ui.PanelBox,
657
+ {
658
+ title: panel.title,
659
+ aside: panel.actions?.length ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [
660
+ panel.asideChip ? /* @__PURE__ */ jsxRuntime.jsx(SpecChip, { chip: panel.asideChip }) : panel.aside,
661
+ /* @__PURE__ */ jsxRuntime.jsx(
662
+ SpecActions,
663
+ {
664
+ actions: panel.actions,
665
+ onAction,
666
+ icons,
667
+ ctx: { panelId: panel.id }
668
+ }
669
+ )
670
+ ] }) : panel.asideChip ? /* @__PURE__ */ jsxRuntime.jsx(SpecChip, { chip: panel.asideChip }) : panel.aside,
671
+ flush: panel.flush || panel.absent != null,
672
+ style,
673
+ children: body
674
+ }
675
+ );
676
+ }
677
+ function Row({
678
+ row,
679
+ gap,
680
+ registry,
681
+ onAction,
682
+ icons
683
+ }) {
684
+ return /* @__PURE__ */ jsxRuntime.jsx(
685
+ "div",
686
+ {
687
+ className: ui.cn("flex min-w-0 items-stretch", row.fill && row.height == null && "min-h-0 flex-1"),
688
+ style: { gap: row.gap ?? gap, height: row.height },
689
+ children: row.panels.filter((panel) => !isDropped(panel)).map((panel, i) => /* @__PURE__ */ jsxRuntime.jsx(
690
+ Panel,
691
+ {
692
+ panel,
693
+ registry,
694
+ onAction,
695
+ icons,
696
+ gap
697
+ },
698
+ panel.id ?? i
699
+ ))
700
+ }
701
+ );
702
+ }
703
+ function Board({
704
+ spec: given,
705
+ onAction,
706
+ registry: extraRegistry,
707
+ icons = {},
708
+ stream,
709
+ onStreamEnd,
710
+ onStreamError,
711
+ className,
712
+ ...props
713
+ }) {
714
+ const { live: spec } = blocks.useStreamedSpec(given, stream, applyBoardPatches, {
715
+ onEnd: onStreamEnd,
716
+ onError: onStreamError
717
+ });
718
+ const registry = React__namespace.useMemo(() => resolveRegistry(extraRegistry), [extraRegistry]);
719
+ const gap = spec.gap ?? 12;
720
+ const emit = React__namespace.useCallback(
721
+ (id, ctx) => onAction?.(id, ctx),
722
+ [onAction]
723
+ );
724
+ const tabs = spec.tabs?.length ? spec.tabs : null;
725
+ const inspector = spec.inspector;
726
+ const [ownTab, setOwnTab] = React__namespace.useState(spec.tab ?? tabs?.[0]?.id);
727
+ const activeTab = spec.tabAction ? spec.tab ?? tabs?.[0]?.id : ownTab;
728
+ const body = (rows, flush) => /* @__PURE__ */ jsxRuntime.jsx(
729
+ "div",
730
+ {
731
+ className: ui.cn("flex min-h-0 flex-1 flex-col overflow-y-auto", !flush && "p-3"),
732
+ style: { gap },
733
+ children: rows.filter((row) => row.panels.some((panel) => !isDropped(panel))).map((row, i) => /* @__PURE__ */ jsxRuntime.jsx(
734
+ Row,
735
+ {
736
+ row,
737
+ gap,
738
+ registry,
739
+ onAction: emit,
740
+ icons
741
+ },
742
+ row.id ?? i
743
+ ))
744
+ }
745
+ );
746
+ const main = tabs ? /* @__PURE__ */ jsxRuntime.jsx(
747
+ ui.TabbedPanel,
748
+ {
749
+ className: "min-h-0 flex-1 border-0 bg-transparent shadow-none",
750
+ bodyClassName: "flex min-h-0 flex-col",
751
+ activeTab,
752
+ onTabChange: (value) => spec.tabAction ? emit(spec.tabAction, { option: value }) : setOwnTab(value),
753
+ headerContent: spec.tabActions?.length ? /* @__PURE__ */ jsxRuntime.jsx(SpecActions, { actions: spec.tabActions, onAction: emit, icons }) : void 0,
754
+ tabs: tabs.map((tab) => ({
755
+ value: tab.id,
756
+ label: tab.label,
757
+ icon: tab.locked ? lucideReact.Lock : void 0,
758
+ disabled: tab.locked,
759
+ content: body(tab.rows, tab.flush)
760
+ }))
761
+ }
762
+ ) : body(spec.rows);
763
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ui.cn("flex min-h-0 flex-col bg-background", className), ...props, children: [
764
+ spec.header ? /* @__PURE__ */ jsxRuntime.jsx(SpecHeader, { header: spec.header, onAction: emit, icons }) : null,
765
+ spec.header?.description != null || spec.header?.details?.length ? /* @__PURE__ */ jsxRuntime.jsx(RecordDescription, { description: spec.header.description, details: spec.header.details }) : null,
766
+ spec.staged ? /* @__PURE__ */ jsxRuntime.jsx(SpecStaged, { staged: spec.staged, onAction: emit }) : null,
767
+ inspector ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1", children: [
768
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: main }),
769
+ /* @__PURE__ */ jsxRuntime.jsx(
770
+ Board,
771
+ {
772
+ className: "border-l border-border",
773
+ style: { width: inspector.width ?? 440, flexShrink: 0 },
774
+ spec: {
775
+ ...inspector.spec,
776
+ inspector: void 0,
777
+ header: inspector.spec.header && { size: "md", ...inspector.spec.header }
778
+ },
779
+ onAction: emit,
780
+ registry: extraRegistry,
781
+ icons
782
+ }
783
+ )
784
+ ] }) : main
785
+ ] });
786
+ }
787
+ function SpecStaged({
788
+ staged,
789
+ onAction
790
+ }) {
791
+ const { discardAction, discardAllAction } = staged;
792
+ return /* @__PURE__ */ jsxRuntime.jsx(
793
+ StagedBar,
794
+ {
795
+ items: staged.items,
796
+ hint: staged.hint,
797
+ onDiscard: discardAction ? (id) => onAction(discardAction, { itemId: id }) : void 0,
798
+ onDiscardAll: discardAllAction ? () => onAction(discardAllAction) : void 0
799
+ }
800
+ );
801
+ }
802
+ function SpecHeader({
803
+ header,
804
+ onAction,
805
+ icons
806
+ }) {
807
+ const last = header.crumbs.length - 1;
808
+ const crumbs = header.crumbs.map((crumb, i) => {
809
+ const action = header.crumbActions?.[i];
810
+ if (i === last && header.crumbMenu) {
811
+ const menu = header.crumbMenu;
812
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
813
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenuTrigger, { className: "inline-flex items-center gap-1 hover:text-primary focus-visible:outline-none", children: [
814
+ crumb,
815
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "size-3.5 text-muted-foreground" })
816
+ ] }),
817
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenuContent, { align: "start", className: "max-h-96 min-w-64 overflow-y-auto", children: [
818
+ menu.placeholder ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 py-1 text-sm text-muted-foreground", children: menu.placeholder }) : null,
819
+ menu.items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
820
+ ui.DropdownMenuItem,
821
+ {
822
+ onSelect: () => onAction(menu.action, { itemId: item.id }),
823
+ className: ui.cn(item.id === menu.selected && "bg-accent"),
824
+ children: [
825
+ item.tone ? /* @__PURE__ */ jsxRuntime.jsx(ui.StatusDot, { tone: item.tone }) : null,
826
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate font-mono", children: item.label }),
827
+ item.aside ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-sm text-muted-foreground tabular-nums", children: item.aside }) : null,
828
+ item.id === menu.selected ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "size-3.5 text-primary" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "size-3.5" })
829
+ ]
830
+ },
831
+ item.id
832
+ ))
833
+ ] })
834
+ ] }, i);
835
+ }
836
+ return action ? /* @__PURE__ */ jsxRuntime.jsx(
837
+ "button",
838
+ {
839
+ type: "button",
840
+ className: "hover:text-primary hover:underline focus-visible:outline-none",
841
+ onClick: () => onAction(action),
842
+ children: crumb
843
+ },
844
+ i
845
+ ) : crumb;
846
+ });
847
+ return /* @__PURE__ */ jsxRuntime.jsx(
848
+ ui.RecordHeader,
849
+ {
850
+ size: header.size,
851
+ tone: header.tone,
852
+ crumbs,
853
+ chips: /* @__PURE__ */ jsxRuntime.jsx(SpecChips, { chips: header.chips }),
854
+ actions: /* @__PURE__ */ jsxRuntime.jsx(SpecActions, { actions: header.actions, onAction, icons })
855
+ }
856
+ );
857
+ }
858
+ function BoardPages({
859
+ spec,
860
+ onAction,
861
+ registry,
862
+ icons = {},
863
+ className
864
+ }) {
865
+ const first = spec.pages[0]?.id ?? "";
866
+ const [ownActive, setOwnActive] = React__namespace.useState(spec.active ?? first);
867
+ const active = spec.selectAction ? spec.active ?? first : ownActive;
868
+ const emit = React__namespace.useCallback(
869
+ (id, ctx) => onAction?.(id, ctx),
870
+ [onAction]
871
+ );
872
+ const pages = React__namespace.useMemo(
873
+ () => spec.pages.map((page) => ({
874
+ id: page.id,
875
+ title: page.title,
876
+ icon: page.icon ? icons[page.icon] : void 0,
877
+ disabled: page.disabled,
878
+ closable: page.closable,
879
+ content: /* @__PURE__ */ jsxRuntime.jsx(
880
+ Board,
881
+ {
882
+ spec: page.board,
883
+ registry,
884
+ icons,
885
+ className: "h-full",
886
+ onAction: (id, ctx) => emit(id, { ...ctx, pageId: page.id })
887
+ }
888
+ )
889
+ })),
890
+ [spec.pages, icons, registry, emit]
891
+ );
892
+ return /* @__PURE__ */ jsxRuntime.jsx(
893
+ ui.Workbook,
894
+ {
895
+ pages,
896
+ activeId: active,
897
+ tabPosition: spec.tabPosition,
898
+ pagerPosition: spec.pagerPosition,
899
+ onSelect: (id) => {
900
+ setOwnActive(id);
901
+ if (spec.selectAction) emit(spec.selectAction, { pageId: id });
902
+ },
903
+ onAdd: spec.addAction ? () => emit(spec.addAction) : void 0,
904
+ addLabel: spec.addLabel,
905
+ onClose: spec.closeAction ? (id) => emit(spec.closeAction, { pageId: id }) : void 0,
906
+ className
907
+ }
908
+ );
909
+ }
910
+ var TONE = {
911
+ muted: "text-muted-foreground",
912
+ info: "text-info",
913
+ success: "text-success",
914
+ warning: "text-warning",
915
+ destructive: "text-destructive"
916
+ };
917
+ var AttemptClock = React__namespace.forwardRef(
918
+ ({ rows, summary, className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: ui.cn("flex flex-col", className), ...props, children: [
919
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { density: "compact", children: [
920
+ /* @__PURE__ */ jsxRuntime.jsx(ui.TableHeader, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.TableRow, { children: [
921
+ /* @__PURE__ */ jsxRuntime.jsx(ui.TableHead, { className: "w-[7rem]", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "what" }) }),
922
+ /* @__PURE__ */ jsxRuntime.jsx(ui.TableHead, { className: "w-[6rem]", children: "started" }),
923
+ /* @__PURE__ */ jsxRuntime.jsx(ui.TableHead, { className: "w-[5.5rem]", children: "took" }),
924
+ /* @__PURE__ */ jsxRuntime.jsx(ui.TableHead, { children: "what happened" })
925
+ ] }) }),
926
+ /* @__PURE__ */ jsxRuntime.jsx(ui.TableBody, { children: rows.map((row, index) => {
927
+ const tone = row.tone ? TONE[row.tone] : void 0;
928
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.TableRow, { children: [
929
+ /* @__PURE__ */ jsxRuntime.jsx(ui.TableCell, { className: ui.cn(tone, row.struck && "line-through"), children: row.label }),
930
+ /* @__PURE__ */ jsxRuntime.jsx(ui.TableCell, { className: "font-mono tabular-nums text-muted-foreground", children: row.started }),
931
+ /* @__PURE__ */ jsxRuntime.jsx(ui.TableCell, { className: ui.cn("font-mono tabular-nums", tone), children: row.took }),
932
+ /* @__PURE__ */ jsxRuntime.jsx(
933
+ ui.TableCell,
934
+ {
935
+ className: ui.cn(row.struck && ["line-through", tone]),
936
+ children: row.what
937
+ }
938
+ )
939
+ ] }, index);
940
+ }) })
941
+ ] }),
942
+ summary != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-border border-t px-2.5 py-1.5 text-sm text-muted-foreground", children: summary }) : null
943
+ ] })
944
+ );
945
+ AttemptClock.displayName = "AttemptClock";
946
+ function TracePanel({
947
+ panel,
948
+ options,
949
+ onAction
950
+ }) {
951
+ const step = (entry, key) => /* @__PURE__ */ jsxRuntime.jsx(
952
+ ui.TraceStep,
953
+ {
954
+ seq: entry.seq,
955
+ name: entry.name,
956
+ description: entry.description,
957
+ layer: entry.layer,
958
+ role: entry.role,
959
+ duration: entry.duration,
960
+ note: entry.note,
961
+ mark: entry.mark ? /* @__PURE__ */ jsxRuntime.jsx(ui.MarkChip, { tone: entry.markTone, children: entry.mark }) : void 0,
962
+ palette: options.palette,
963
+ depth: entry.depth,
964
+ dim: entry.dim,
965
+ struck: entry.struck,
966
+ selected: entry.id != null && entry.id === options.selectedId,
967
+ onSelect: options.selectAction && entry.id ? () => onAction(options.selectAction, {
968
+ panelId: panel.id,
969
+ stepId: entry.id
970
+ }) : void 0
971
+ },
972
+ key
973
+ );
974
+ const draw = (entry, key) => {
975
+ if (entry.kind === "gate")
976
+ return /* @__PURE__ */ jsxRuntime.jsx(
977
+ ui.TraceGate,
978
+ {
979
+ label: entry.label,
980
+ note: entry.note,
981
+ tone: entry.tone,
982
+ edge: entry.edge
983
+ },
984
+ key
985
+ );
986
+ if (entry.kind === "loop")
987
+ return /* @__PURE__ */ jsxRuntime.jsx(
988
+ ui.TraceLoop,
989
+ {
990
+ label: entry.label,
991
+ summary: entry.summary,
992
+ tone: entry.tone,
993
+ children: entry.rounds.map((round, i) => draw(round, `${key}-${i}`))
994
+ },
995
+ key
996
+ );
997
+ return step(entry, key);
998
+ };
999
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.TraceList, { children: options.entries.map((entry, i) => draw(entry, `${i}`)) });
1000
+ }
1001
+ function TouchedPanel({ options }) {
1002
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.TouchStrip, { items: options.items, palette: options.palette });
1003
+ }
1004
+ function AttemptsPanel({ options }) {
1005
+ return /* @__PURE__ */ jsxRuntime.jsx(AttemptClock, { rows: options.rows, summary: options.summary });
1006
+ }
1007
+ var RUN_PANELS = {
1008
+ trace: TracePanel,
1009
+ touched: TouchedPanel,
1010
+ attempts: AttemptsPanel,
1011
+ artifacts: ArtifactsPanel,
1012
+ lens: LensPanel,
1013
+ clarification: ClarificationPanel
1014
+ };
1015
+ function ArtifactsPanel({
1016
+ panel,
1017
+ options,
1018
+ onAction
1019
+ }) {
1020
+ return /* @__PURE__ */ jsxRuntime.jsx(
1021
+ ui.ArtifactTable,
1022
+ {
1023
+ files: options.files,
1024
+ onOpen: options.openAction ? (file) => onAction(options.openAction, {
1025
+ panelId: panel.id,
1026
+ itemId: String(file.digest ?? file.name)
1027
+ }) : void 0,
1028
+ onDownload: options.downloadAction ? (file) => onAction(options.downloadAction, {
1029
+ panelId: panel.id,
1030
+ itemId: String(file.digest ?? file.name)
1031
+ }) : void 0
1032
+ }
1033
+ );
1034
+ }
1035
+ function LensPanel({ panel, options, onAction }) {
1036
+ const pick = options.selectAction ? (lens) => onAction(options.selectAction, { panelId: panel.id, lens }) : void 0;
1037
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: options.sections.map((section) => /* @__PURE__ */ jsxRuntime.jsxs(
1038
+ ui.LayerSection,
1039
+ {
1040
+ layer: section.layer,
1041
+ summary: section.summary,
1042
+ palette: options.palette,
1043
+ count: section.count ?? section.participants?.length,
1044
+ dim: section.dim,
1045
+ children: [
1046
+ (section.participants ?? []).map((participant) => /* @__PURE__ */ jsxRuntime.jsx(
1047
+ ui.ParticipantRow,
1048
+ {
1049
+ address: participant.address,
1050
+ verdict: participant.verdict,
1051
+ note: participant.note
1052
+ },
1053
+ participant.address
1054
+ )),
1055
+ (section.rows ?? []).map((row) => /* @__PURE__ */ jsxRuntime.jsx(
1056
+ ui.LensRow,
1057
+ {
1058
+ name: row.name,
1059
+ narrows: row.narrows,
1060
+ usage: row.usage,
1061
+ palette: options.palette,
1062
+ selected: options.selected === row.name,
1063
+ onSelect: pick
1064
+ },
1065
+ row.name
1066
+ ))
1067
+ ]
1068
+ },
1069
+ section.layer
1070
+ )) });
1071
+ }
1072
+ function ClarificationPanel({
1073
+ options
1074
+ }) {
1075
+ return /* @__PURE__ */ jsxRuntime.jsx(
1076
+ ui.ExchangeRecord,
1077
+ {
1078
+ asker: options.asker,
1079
+ question: options.question,
1080
+ why: options.why,
1081
+ options: options.options,
1082
+ answerer: options.answerer,
1083
+ answer: options.answer,
1084
+ answerNote: options.answerNote
1085
+ }
1086
+ );
1087
+ }
1088
+
1089
+ exports.ArtifactsPanel = ArtifactsPanel;
1090
+ exports.AttemptClock = AttemptClock;
1091
+ exports.AttemptsPanel = AttemptsPanel;
1092
+ exports.BLOCK_PANELS = BLOCK_PANELS;
1093
+ exports.BUILT_IN_PANELS = BUILT_IN_PANELS;
1094
+ exports.BlockPanel = BlockPanel;
1095
+ exports.Board = Board;
1096
+ exports.BoardPages = BoardPages;
1097
+ exports.BoardPatchError = BoardPatchError;
1098
+ exports.ClarificationPanel = ClarificationPanel;
1099
+ exports.CodePanel = CodePanel;
1100
+ exports.EventsPanel = EventsPanel;
1101
+ exports.ExchangePanel = ExchangePanel;
1102
+ exports.JsonPanel = JsonPanel;
1103
+ exports.LensPanel = LensPanel;
1104
+ exports.ListPanel = ListPanel;
1105
+ exports.LogPanel = LogPanel;
1106
+ exports.ParamsPanel = ParamsPanel;
1107
+ exports.RUN_PANELS = RUN_PANELS;
1108
+ exports.RecordDescription = RecordDescription;
1109
+ exports.SpecAction = SpecAction;
1110
+ exports.SpecActions = SpecActions;
1111
+ exports.SpecChip = SpecChip;
1112
+ exports.SpecChips = SpecChips;
1113
+ exports.StagedBar = StagedBar;
1114
+ exports.TextPanel = TextPanel;
1115
+ exports.TouchedPanel = TouchedPanel;
1116
+ exports.TracePanel = TracePanel;
1117
+ exports.applyBoardPatch = applyBoardPatch;
1118
+ exports.applyBoardPatches = applyBoardPatches;
1119
+ exports.resolveRegistry = resolveRegistry;
1120
+ //# sourceMappingURL=index.cjs.map
1121
+ //# sourceMappingURL=index.cjs.map