@nice-code/action 0.10.0 → 0.12.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 (45) hide show
  1. package/build/{ActionDevtoolsCore-yfJ9tkvl.js → ActionDevtoolsCore-9PsnscvK.mjs} +2 -2
  2. package/build/ActionDevtoolsCore-9PsnscvK.mjs.map +1 -0
  3. package/build/{ActionDevtoolsCore-B4s6aGvI.d.ts → ActionDevtoolsCore-CCRLYASa.d.cts} +2 -2
  4. package/build/{ActionDevtoolsCore-Pg7ERO3L.d.ts → ActionDevtoolsCore-CYGD2o6C.d.mts} +2 -2
  5. package/build/{ActionDevtoolsCore-BLeY_N-3.js → ActionDevtoolsCore-DtgXwPBZ.cjs} +2 -2
  6. package/build/ActionDevtoolsCore-DtgXwPBZ.cjs.map +1 -0
  7. package/build/{ActionPayload.types-BN-rXFBK.d.ts → ActionPayload.types-BN-rXFBK.d.cts} +1 -1
  8. package/build/{ActionPayload.types-D28ELKXC.d.ts → ActionPayload.types-D28ELKXC.d.mts} +1 -1
  9. package/build/{RunningAction.types-C176rqHG.js → RunningAction.types-C176rqHG.mjs} +1 -1
  10. package/build/RunningAction.types-C176rqHG.mjs.map +1 -0
  11. package/build/{RunningAction.types-DjCX1xp5.js → RunningAction.types-DjCX1xp5.cjs} +1 -1
  12. package/build/RunningAction.types-DjCX1xp5.cjs.map +1 -0
  13. package/build/devtools/browser/index.cjs +3886 -0
  14. package/build/devtools/browser/index.cjs.map +1 -0
  15. package/build/devtools/browser/{index.d.ts → index.d.cts} +2 -2
  16. package/build/devtools/browser/index.d.mts +17 -0
  17. package/build/devtools/browser/{index.js → index.mjs} +2 -2
  18. package/build/devtools/browser/index.mjs.map +1 -0
  19. package/build/devtools/server/index.cjs +108 -0
  20. package/build/devtools/server/index.cjs.map +1 -0
  21. package/build/devtools/server/{index.d.ts → index.d.cts} +2 -2
  22. package/build/devtools/server/index.d.mts +35 -0
  23. package/build/devtools/server/{index.js → index.mjs} +3 -3
  24. package/build/devtools/server/index.mjs.map +1 -0
  25. package/build/index.cjs +4045 -0
  26. package/build/index.cjs.map +1 -0
  27. package/build/{index.d.ts → index.d.cts} +1 -1
  28. package/build/index.d.mts +2 -0
  29. package/build/{index.js → index.mjs} +2 -2
  30. package/build/index.mjs.map +1 -0
  31. package/build/react-query/index.cjs +70 -0
  32. package/build/react-query/index.cjs.map +1 -0
  33. package/build/react-query/{index.d.ts → index.d.cts} +2 -2
  34. package/build/react-query/index.d.mts +17 -0
  35. package/build/react-query/{index.js → index.mjs} +1 -1
  36. package/build/react-query/index.mjs.map +1 -0
  37. package/package.json +39 -15
  38. package/build/ActionDevtoolsCore-BLeY_N-3.js.map +0 -1
  39. package/build/ActionDevtoolsCore-yfJ9tkvl.js.map +0 -1
  40. package/build/RunningAction.types-C176rqHG.js.map +0 -1
  41. package/build/RunningAction.types-DjCX1xp5.js.map +0 -1
  42. package/build/devtools/browser/index.js.map +0 -1
  43. package/build/devtools/server/index.js.map +0 -1
  44. package/build/index.js.map +0 -1
  45. package/build/react-query/index.js.map +0 -1
@@ -0,0 +1,3886 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_ActionDevtoolsCore = require("../../ActionDevtoolsCore-DtgXwPBZ.cjs");
3
+ let _nice_code_util = require("@nice-code/util");
4
+ let react = require("react");
5
+ let react_jsx_runtime = require("react/jsx-runtime");
6
+ let react_dom = require("react-dom");
7
+ let _tanstack_react_virtual = require("@tanstack/react-virtual");
8
+ let lucide_react = require("lucide-react");
9
+ let source_map_js = require("source-map-js");
10
+ //#region ../nice-devtools-shared/src/colors.ts
11
+ const DEVTOOL_COLOR_SEMANTIC_ERROR = "#FF5C5C";
12
+ const DEVTOOL_COLOR_SEMANTIC_SUCCESS = "#A3E635";
13
+ const DEVTOOL_COLOR_SEMANTIC_SYSTEM = "#38BDF8";
14
+ const DEVTOOL_COLOR_SEMANTIC_WARNING = "#FB923C";
15
+ const DEVTOOL_COLOR_SEMANTIC_METADATA = "#A1A1AA";
16
+ const DEVTOOL_COLOR_TEXT_EMPHASIS = "#f1f5f9";
17
+ const DEVTOOL_COLOR_TEXT_SECONDARY = "#cbd5e1";
18
+ const DEVTOOL_COLOR_TEXT_MUTED = "#64748b";
19
+ const DEVTOOL_COLOR_TEXT_FAINT = "#334155";
20
+ const DEVTOOL_LIST_BASE_BACKGROUND = "#0f172a";
21
+ const DEVTOOL_DETAIL_BASE_BACKGROUND = "#0d1729";
22
+ const DEVTOOL_DETAIL_HEADER_BACKGROUND = "#131f35";
23
+ const DEVTOOL_SECTION_BACKGROUND = "#1e293b";
24
+ const DEVTOOL_SECTION_STRING_BACKGROUND = "#0d131f";
25
+ const DEVTOOL_PANEL_BORDER = "#1e293b";
26
+ const DEVTOOL_ERROR_BACKGROUND = "#1e0a0a";
27
+ const DEVTOOL_TOOLTIP_BACKGROUND = "#0c1526";
28
+ const DEVTOOL_TOOLTIP_TITLE_BACKGROUND = "#101b2e";
29
+ const DEVTOOL_TOOLTIP_BORDER = "#312e81";
30
+ const DEVTOOL_JSON_KEY = "#a5b4fc";
31
+ const DEVTOOL_JSON_STRING = "#fbbf24";
32
+ const DEVTOOL_JSON_NUMBER = "#34d399";
33
+ const DEVTOOL_JSON_KEYWORD = "#a78bfa";
34
+ const DEVTOOL_JSON_PUNCTUATION = "#475569";
35
+ const MONO_FONT = "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace";
36
+ const SANS_FONT = "ui-sans-serif, system-ui, sans-serif";
37
+ //#endregion
38
+ //#region ../nice-devtools-shared/src/format.ts
39
+ /** Pretty-print any value to JSON, degrading gracefully for cyclic / non-JSON values. */
40
+ function safeStringify(value, indent = 2) {
41
+ if (value === void 0) return "undefined";
42
+ if (value === null) return "null";
43
+ try {
44
+ return JSON.stringify(value, null, indent);
45
+ } catch {
46
+ return String(value);
47
+ }
48
+ }
49
+ /** Wall-clock time of day, e.g. `14:03:09`. */
50
+ function formatTimestamp(ms) {
51
+ return new Date(ms).toLocaleTimeString([], {
52
+ hour: "2-digit",
53
+ minute: "2-digit",
54
+ second: "2-digit",
55
+ hour12: false
56
+ });
57
+ }
58
+ /** Compact relative age: `850ms`, `3.2s`, `4m`. */
59
+ function formatRelativeAge(ms) {
60
+ if (ms < 1e3) return `${ms}ms`;
61
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
62
+ return `${Math.floor(ms / 6e4)}m`;
63
+ }
64
+ //#endregion
65
+ //#region ../nice-devtools-shared/src/json.tsx
66
+ const JSON_TOKEN_RE = /("(?:\\.|[^"\\])*")(\s*:)?|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)|(\btrue\b|\bfalse\b|\bnull\b|\bundefined\b)|([{}[\],])/g;
67
+ /** Tokenize a JSON string into coloured <span> nodes for inline rendering. */
68
+ function renderColoredJson(text) {
69
+ const nodes = [];
70
+ let last = 0;
71
+ let i = 0;
72
+ JSON_TOKEN_RE.lastIndex = 0;
73
+ for (let m = JSON_TOKEN_RE.exec(text); m !== null; m = JSON_TOKEN_RE.exec(text)) {
74
+ if (m.index > last) nodes.push(text.slice(last, m.index));
75
+ const [, str, colon, num, kw, punct] = m;
76
+ if (str != null) if (colon != null) {
77
+ nodes.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
78
+ style: { color: DEVTOOL_JSON_KEY },
79
+ children: str
80
+ }, i++));
81
+ nodes.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
82
+ style: { color: DEVTOOL_JSON_PUNCTUATION },
83
+ children: colon
84
+ }, i++));
85
+ } else nodes.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
86
+ style: { color: DEVTOOL_JSON_STRING },
87
+ children: str
88
+ }, i++));
89
+ else if (num != null) nodes.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
90
+ style: { color: DEVTOOL_JSON_NUMBER },
91
+ children: num
92
+ }, i++));
93
+ else if (kw != null) nodes.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
94
+ style: { color: DEVTOOL_JSON_KEYWORD },
95
+ children: kw
96
+ }, i++));
97
+ else if (punct != null) nodes.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
98
+ style: { color: DEVTOOL_JSON_PUNCTUATION },
99
+ children: punct
100
+ }, i++));
101
+ last = JSON_TOKEN_RE.lastIndex;
102
+ }
103
+ if (last < text.length) nodes.push(text.slice(last));
104
+ return nodes;
105
+ }
106
+ //#endregion
107
+ //#region ../nice-devtools-shared/src/dock.ts
108
+ const GLOBAL_KEY = "__NICE_DEVTOOLS_DOCK__";
109
+ const VERSION = 4;
110
+ function createCoordinator() {
111
+ const panels = /* @__PURE__ */ new Map();
112
+ const listeners = /* @__PURE__ */ new Set();
113
+ function toRef(panel) {
114
+ return {
115
+ id: panel.id,
116
+ label: panel.label,
117
+ icon: panel.icon,
118
+ badge: panel.badge,
119
+ onOpen: panel.onOpen
120
+ };
121
+ }
122
+ function applyBodyMargins() {
123
+ if (typeof document === "undefined") return;
124
+ const margins = {
125
+ top: 0,
126
+ bottom: 0,
127
+ left: 0,
128
+ right: 0
129
+ };
130
+ for (const panel of panels.values()) if (panel.open) margins[panel.side] += panel.size;
131
+ for (const side of [
132
+ "top",
133
+ "bottom",
134
+ "left",
135
+ "right"
136
+ ]) if (margins[side] > 0) document.body.style.setProperty(`margin-${side}`, `${margins[side]}px`);
137
+ else document.body.style.removeProperty(`margin-${side}`);
138
+ }
139
+ function notify() {
140
+ applyBodyMargins();
141
+ for (const listener of listeners) listener();
142
+ }
143
+ return {
144
+ version: VERSION,
145
+ register(panel) {
146
+ panels.set(panel.id, { ...panel });
147
+ notify();
148
+ return () => {
149
+ panels.delete(panel.id);
150
+ notify();
151
+ };
152
+ },
153
+ update(id, next) {
154
+ const existing = panels.get(id);
155
+ if (existing == null) return;
156
+ if (existing.side === next.side && existing.size === next.size && existing.open === next.open && existing.badge === next.badge) return;
157
+ panels.set(id, {
158
+ ...existing,
159
+ ...next
160
+ });
161
+ notify();
162
+ },
163
+ getView(id) {
164
+ const list = [...panels.values()];
165
+ const anyOpen = list.some((p) => p.open);
166
+ const firstId = list.length > 0 ? list[0].id : null;
167
+ let dockOffset = 0;
168
+ let stacked = false;
169
+ const self = panels.get(id);
170
+ if (self != null && self.open) {
171
+ let seenSelf = false;
172
+ for (const panel of list) {
173
+ if (panel.id === id) {
174
+ seenSelf = true;
175
+ continue;
176
+ }
177
+ if (panel.open && panel.side === self.side) {
178
+ stacked = true;
179
+ if (!seenSelf) dockOffset += panel.size;
180
+ }
181
+ }
182
+ }
183
+ return {
184
+ dockOffset,
185
+ stacked,
186
+ anyOpen,
187
+ isPrimary: id === firstId,
188
+ devtools: list.map(toRef),
189
+ otherClosed: list.filter((p) => !p.open && p.id !== id).map(toRef)
190
+ };
191
+ },
192
+ subscribe(listener) {
193
+ listeners.add(listener);
194
+ return () => {
195
+ listeners.delete(listener);
196
+ };
197
+ }
198
+ };
199
+ }
200
+ /**
201
+ * Returns the page-wide dock coordinator, installing it on `window` the first
202
+ * time it is requested. On the server (no `window`) a throwaway instance is
203
+ * returned so callers can use it unconditionally.
204
+ */
205
+ function getDevtoolsDockCoordinator() {
206
+ if (typeof window === "undefined") return createCoordinator();
207
+ const host = window;
208
+ const existing = host[GLOBAL_KEY];
209
+ if (existing != null && existing.version === VERSION) return existing;
210
+ const created = createCoordinator();
211
+ host[GLOBAL_KEY] = created;
212
+ return created;
213
+ }
214
+ //#endregion
215
+ //#region ../nice-devtools-shared/src/list.ts
216
+ /**
217
+ * Pin a virtualized list's viewport to a stable anchor row across list
218
+ * mutations. Both devtools timelines are newest-first, so new entries (and
219
+ * merges) prepend at the top; without this they shove whatever the user is
220
+ * looking at downward. The anchor is the selected row when one is visible,
221
+ * otherwise the first visible row, so "the thing I selected / am reading" stays
222
+ * put at the same screen position. Returns an `onScroll` handler that must be
223
+ * wired to the scroll container.
224
+ *
225
+ * `hoveringRef` is optional. When provided, the hook keeps the viewport still
226
+ * even across selection changes while the cursor is over the list — the caller
227
+ * is expected to suppress its own scroll-into-view in that case and let this
228
+ * hold the view rock-still (nice-action's pause-on-hover behaviour). When
229
+ * omitted, a selection change yields to the caller's scroll-into-view effect.
230
+ */
231
+ function useListScrollAnchor({ containerRef, virtualizer, itemKeys, selectedKey, hoveringRef }) {
232
+ const anchorRef = (0, react.useRef)(null);
233
+ const selectedKeyRef = (0, react.useRef)(selectedKey);
234
+ selectedKeyRef.current = selectedKey;
235
+ const captureAnchor = (0, react.useCallback)(() => {
236
+ const el = containerRef.current;
237
+ if (el == null) return;
238
+ const items = virtualizer.getVirtualItems();
239
+ if (items.length === 0) {
240
+ anchorRef.current = null;
241
+ return;
242
+ }
243
+ const scrollTop = el.scrollTop;
244
+ const sel = selectedKeyRef.current;
245
+ const chosen = (sel != null ? items.find((vi) => String(vi.key) === sel) : void 0) ?? items.find((vi) => vi.end > scrollTop) ?? items[0];
246
+ anchorRef.current = {
247
+ key: String(chosen.key),
248
+ delta: chosen.start - scrollTop
249
+ };
250
+ }, [containerRef, virtualizer]);
251
+ const prevSelectedRef = (0, react.useRef)(selectedKey);
252
+ (0, react.useLayoutEffect)(() => {
253
+ const selectionChanged = prevSelectedRef.current !== selectedKey;
254
+ prevSelectedRef.current = selectedKey;
255
+ if (selectionChanged && !(hoveringRef?.current ?? false)) return;
256
+ const anchor = anchorRef.current;
257
+ if (anchor == null) return;
258
+ const index = itemKeys.indexOf(anchor.key);
259
+ if (index < 0) return;
260
+ const offset = virtualizer.getOffsetForIndex(index, "start")?.[0];
261
+ if (offset == null) return;
262
+ const target = Math.max(0, offset - anchor.delta);
263
+ const current = virtualizer.scrollOffset ?? 0;
264
+ if (Math.abs(target - current) > 1) virtualizer.scrollToOffset(target);
265
+ }, [
266
+ itemKeys,
267
+ selectedKey,
268
+ virtualizer,
269
+ hoveringRef
270
+ ]);
271
+ return captureAnchor;
272
+ }
273
+ //#endregion
274
+ //#region ../nice-devtools-shared/src/components/SectionLabel.tsx
275
+ /** Small uppercase heading used above a detail/section block. */
276
+ function SectionLabel({ label, color = DEVTOOL_COLOR_SEMANTIC_SYSTEM }) {
277
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
278
+ style: {
279
+ color,
280
+ fontSize: "0.85em",
281
+ marginBottom: "3px",
282
+ textTransform: "uppercase",
283
+ letterSpacing: "0.05em",
284
+ fontWeight: 500,
285
+ textAlign: "left"
286
+ },
287
+ children: label
288
+ });
289
+ }
290
+ //#endregion
291
+ //#region ../nice-devtools-shared/src/components/Tooltip.tsx
292
+ function Tooltip({ anchor, config, children }) {
293
+ const resolvedTitle = config?.title;
294
+ const resolvedAlign = config?.align;
295
+ const resolvedMaxWidth = config?.maxWidth ?? 400;
296
+ const resolvedContent = config != null ? config.content : children;
297
+ const showAbove = anchor.top >= window.innerHeight - anchor.bottom;
298
+ const gap = 6;
299
+ const screenMargin = 16;
300
+ const top = showAbove ? Math.round(anchor.top - gap) : Math.round(anchor.bottom + gap);
301
+ const maxHeight = showAbove ? Math.round(anchor.top - gap - screenMargin) : Math.round(window.innerHeight - anchor.bottom - gap - screenMargin);
302
+ let left;
303
+ let right;
304
+ let transform;
305
+ if (resolvedAlign === "center") {
306
+ const halfMax = resolvedMaxWidth != null ? resolvedMaxWidth / 2 : 120;
307
+ const center = Math.round(anchor.left + anchor.width / 2);
308
+ left = Math.max(halfMax + screenMargin, Math.min(center, window.innerWidth - halfMax - screenMargin));
309
+ transform = showAbove ? "translateX(-50%) translateY(-100%)" : "translateX(-50%)";
310
+ } else {
311
+ if (anchor.left + anchor.width / 2 <= window.innerWidth / 2) left = Math.max(screenMargin, anchor.left);
312
+ else right = Math.max(screenMargin, window.innerWidth - anchor.right);
313
+ if (showAbove) transform = "translateY(-100%)";
314
+ }
315
+ return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
316
+ style: {
317
+ position: "fixed",
318
+ left,
319
+ right,
320
+ top,
321
+ transform,
322
+ zIndex: 2147483647,
323
+ background: DEVTOOL_TOOLTIP_BACKGROUND,
324
+ border: `1px solid ${DEVTOOL_TOOLTIP_BORDER}`,
325
+ borderRadius: "5px",
326
+ boxShadow: "0 4px 20px rgba(0,0,0,0.6)",
327
+ pointerEvents: "none",
328
+ maxWidth: resolvedMaxWidth != null ? `${resolvedMaxWidth}px` : void 0,
329
+ maxHeight: `${maxHeight}px`,
330
+ overflowY: "auto",
331
+ overflowX: "hidden",
332
+ whiteSpace: "normal",
333
+ wordBreak: "break-word",
334
+ overflowWrap: "anywhere"
335
+ },
336
+ children: [resolvedTitle != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
337
+ style: {
338
+ background: "#101b2e",
339
+ padding: "6px 8px",
340
+ alignSelf: "start",
341
+ textAlign: "left",
342
+ color: "#cbd5e1",
343
+ fontSize: "0.75rem",
344
+ fontWeight: 600,
345
+ paddingBottom: "4px",
346
+ marginBottom: "4px",
347
+ borderBottom: `1px solid #211f5f`
348
+ },
349
+ children: resolvedTitle
350
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
351
+ style: { padding: "6px 8px" },
352
+ children: resolvedContent
353
+ })]
354
+ }), document.body);
355
+ }
356
+ /**
357
+ * Wraps inline content so it shows `config` as a hover tooltip. When `config` is null the children
358
+ * render untouched (no hover handlers, no tooltip). Useful for non-Chip text labels that still need
359
+ * the same tooltip treatment as Chips.
360
+ */
361
+ function HoverTooltip({ config, children, style }) {
362
+ const [anchor, setAnchor] = (0, react.useState)(null);
363
+ const enabled = config != null;
364
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
365
+ onMouseEnter: enabled ? (e) => setAnchor(e.currentTarget.getBoundingClientRect()) : void 0,
366
+ onMouseLeave: enabled ? () => setAnchor(null) : void 0,
367
+ style: {
368
+ ...style,
369
+ cursor: enabled ? "default" : style?.cursor
370
+ },
371
+ children
372
+ }), enabled && anchor != null && config != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tooltip, {
373
+ anchor,
374
+ config
375
+ })] });
376
+ }
377
+ //#endregion
378
+ //#region ../nice-devtools-shared/src/components/PanelChrome.tsx
379
+ const DOCKED_SIZE_MIN = 140;
380
+ const POSITION_GRID = [
381
+ {
382
+ key: "tl",
383
+ pos: null
384
+ },
385
+ {
386
+ key: "tc",
387
+ pos: "dock-top"
388
+ },
389
+ {
390
+ key: "tr",
391
+ pos: null
392
+ },
393
+ {
394
+ key: "ml",
395
+ pos: "dock-left"
396
+ },
397
+ {
398
+ key: "mc",
399
+ pos: null
400
+ },
401
+ {
402
+ key: "mr",
403
+ pos: "dock-right"
404
+ },
405
+ {
406
+ key: "bl",
407
+ pos: null
408
+ },
409
+ {
410
+ key: "bc",
411
+ pos: "dock-bottom"
412
+ },
413
+ {
414
+ key: "br",
415
+ pos: null
416
+ }
417
+ ];
418
+ function getDockSide(pos) {
419
+ switch (pos) {
420
+ case "dock-top": return "top";
421
+ case "dock-left": return "left";
422
+ case "dock-right": return "right";
423
+ default: return "bottom";
424
+ }
425
+ }
426
+ function chromeButtonStyle(color) {
427
+ return {
428
+ background: "none",
429
+ border: "none",
430
+ color,
431
+ cursor: "pointer",
432
+ fontSize: "11px",
433
+ padding: 0,
434
+ fontFamily: SANS_FONT,
435
+ whiteSpace: "nowrap"
436
+ };
437
+ }
438
+ /**
439
+ * The panel's top chrome: a brand/title on the left (plus pills to open any other
440
+ * closed devtools), and the panel controls on the right. The controls column
441
+ * holds any caller-provided `children` (e.g. a mode switch) above a row of the
442
+ * pause/clear actions, then the dock-position picker and the close button.
443
+ *
444
+ * `onTogglePause` is optional — when omitted no pause control is shown (the
445
+ * nice-action timeline has nothing to pause), keeping a single header component
446
+ * consistent across both devtools suites.
447
+ */
448
+ function PanelHeader({ title, position, onPositionChange, onClose, onClear, paused, onTogglePause, openOthers, children }) {
449
+ const hasActionsRow = onTogglePause != null || onClear != null;
450
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
451
+ style: {
452
+ display: "flex",
453
+ alignItems: "center",
454
+ justifyContent: "space-between",
455
+ padding: "6px 11px",
456
+ gap: "10px",
457
+ background: DEVTOOL_SECTION_BACKGROUND,
458
+ borderBottom: `1px solid ${DEVTOOL_LIST_BASE_BACKGROUND}`,
459
+ flexShrink: 0
460
+ },
461
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
462
+ style: {
463
+ display: "flex",
464
+ alignItems: "center",
465
+ gap: "8px",
466
+ minWidth: 0
467
+ },
468
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
469
+ style: {
470
+ color: DEVTOOL_COLOR_SEMANTIC_SYSTEM,
471
+ fontWeight: "bold",
472
+ fontSize: "11px",
473
+ whiteSpace: "nowrap"
474
+ },
475
+ children: title
476
+ }), openOthers?.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
477
+ onClick: item.onOpen,
478
+ title: `Open ${item.label} devtools`,
479
+ style: {
480
+ display: "flex",
481
+ alignItems: "center",
482
+ gap: "4px",
483
+ background: DEVTOOL_LIST_BASE_BACKGROUND,
484
+ border: `1px solid ${DEVTOOL_COLOR_TEXT_FAINT}`,
485
+ borderRadius: "999px",
486
+ color: DEVTOOL_COLOR_TEXT_MUTED,
487
+ cursor: "pointer",
488
+ fontSize: "10px",
489
+ padding: "1px 7px 1px 6px",
490
+ fontFamily: SANS_FONT,
491
+ whiteSpace: "nowrap"
492
+ },
493
+ children: [
494
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.icon }),
495
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.label }),
496
+ item.badge != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
497
+ style: { color: "#38BDF8" },
498
+ children: item.badge
499
+ })
500
+ ]
501
+ }, item.id))]
502
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
503
+ style: {
504
+ display: "flex",
505
+ gap: "10px",
506
+ alignItems: "center"
507
+ },
508
+ children: [
509
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
510
+ style: {
511
+ display: "flex",
512
+ flexDirection: "column",
513
+ alignItems: "stretch"
514
+ },
515
+ children: [children, hasActionsRow && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
516
+ style: {
517
+ display: "flex",
518
+ gap: "10px",
519
+ alignItems: "center",
520
+ justifyContent: "space-between",
521
+ padding: "3px"
522
+ },
523
+ children: [onTogglePause != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
524
+ onClick: onTogglePause,
525
+ title: paused ? "Resume recording" : "Pause recording",
526
+ style: chromeButtonStyle(paused ? "#38BDF8" : "#64748b"),
527
+ children: paused ? "▶ resume" : "⏸ pause"
528
+ }), onClear != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
529
+ onClick: onClear,
530
+ style: chromeButtonStyle("#64748b"),
531
+ children: "clear"
532
+ })]
533
+ })]
534
+ }),
535
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PositionPicker, {
536
+ position,
537
+ onChange: onPositionChange
538
+ }),
539
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
540
+ onClick: onClose,
541
+ style: {
542
+ ...chromeButtonStyle(DEVTOOL_COLOR_TEXT_MUTED),
543
+ fontSize: "16px",
544
+ lineHeight: "1"
545
+ },
546
+ children: "×"
547
+ })
548
+ ]
549
+ })]
550
+ });
551
+ }
552
+ function PositionPicker({ position, onChange }) {
553
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
554
+ title: "Move / dock panel",
555
+ style: {
556
+ display: "grid",
557
+ gridTemplateColumns: "repeat(3, 9px)",
558
+ gap: "2px",
559
+ padding: "2px"
560
+ },
561
+ children: POSITION_GRID.map(({ key, pos }) => {
562
+ if (pos == null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
563
+ width: "9px",
564
+ height: "9px"
565
+ } }, key);
566
+ const isTopBottom = pos === "dock-top" || pos === "dock-bottom";
567
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
568
+ title: pos,
569
+ onClick: () => onChange(pos),
570
+ style: {
571
+ width: "9px",
572
+ height: "9px",
573
+ display: "flex",
574
+ alignItems: "center",
575
+ justifyContent: "center",
576
+ cursor: "pointer"
577
+ },
578
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
579
+ width: isTopBottom ? "9px" : "3px",
580
+ height: isTopBottom ? "3px" : "9px",
581
+ borderRadius: "1px",
582
+ background: pos === position ? DEVTOOL_COLOR_SEMANTIC_SYSTEM : DEVTOOL_COLOR_TEXT_FAINT
583
+ } })
584
+ }, key);
585
+ })
586
+ });
587
+ }
588
+ function ResizeHandle({ dockSide, dockedSize, onChange }) {
589
+ const isHoriz = dockSide === "left" || dockSide === "right";
590
+ const onMouseDown = (e) => {
591
+ e.preventDefault();
592
+ const startCoord = isHoriz ? e.clientX : e.clientY;
593
+ const startSize = dockedSize;
594
+ const maxSize = isHoriz ? window.innerWidth * .85 : window.innerHeight * .85;
595
+ const sign = dockSide === "bottom" || dockSide === "right" ? -1 : 1;
596
+ const onMove = (me) => {
597
+ const delta = (isHoriz ? me.clientX : me.clientY) - startCoord;
598
+ onChange(Math.max(DOCKED_SIZE_MIN, Math.min(maxSize, startSize + sign * delta)));
599
+ };
600
+ const onUp = () => {
601
+ window.removeEventListener("mousemove", onMove);
602
+ window.removeEventListener("mouseup", onUp);
603
+ };
604
+ window.addEventListener("mousemove", onMove);
605
+ window.addEventListener("mouseup", onUp);
606
+ };
607
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
608
+ onMouseDown,
609
+ style: {
610
+ position: "absolute",
611
+ zIndex: 10,
612
+ background: "transparent",
613
+ ...dockSide === "bottom" ? {
614
+ top: 0,
615
+ left: 0,
616
+ right: 0,
617
+ height: "5px",
618
+ cursor: "ns-resize"
619
+ } : dockSide === "top" ? {
620
+ bottom: 0,
621
+ left: 0,
622
+ right: 0,
623
+ height: "5px",
624
+ cursor: "ns-resize"
625
+ } : dockSide === "right" ? {
626
+ top: 0,
627
+ bottom: 0,
628
+ left: 0,
629
+ width: "5px",
630
+ cursor: "ew-resize"
631
+ } : {
632
+ top: 0,
633
+ bottom: 0,
634
+ right: 0,
635
+ width: "5px",
636
+ cursor: "ew-resize"
637
+ }
638
+ }
639
+ });
640
+ }
641
+ const SPLIT_RATIO_MIN = .15;
642
+ const SPLIT_RATIO_MAX = .85;
643
+ /**
644
+ * Draggable divider between the list and the detail pane. `horizontal` refers to
645
+ * the split axis: a row layout (dock top/bottom) splits horizontally and drags
646
+ * left/right; a column layout (dock left/right) splits vertically. The reported
647
+ * ratio is the fraction of the container the *detail* pane should occupy.
648
+ */
649
+ function SplitHandle({ horizontal, onRatioChange }) {
650
+ const [hovered, setHovered] = (0, react.useState)(false);
651
+ const onMouseDown = (e) => {
652
+ e.preventDefault();
653
+ const container = e.currentTarget.parentElement;
654
+ if (container == null) return;
655
+ const onMove = (me) => {
656
+ const rect = container.getBoundingClientRect();
657
+ const ratio = horizontal ? (rect.right - me.clientX) / rect.width : (rect.bottom - me.clientY) / rect.height;
658
+ onRatioChange(Math.max(SPLIT_RATIO_MIN, Math.min(SPLIT_RATIO_MAX, ratio)));
659
+ };
660
+ const onUp = () => {
661
+ window.removeEventListener("mousemove", onMove);
662
+ window.removeEventListener("mouseup", onUp);
663
+ };
664
+ window.addEventListener("mousemove", onMove);
665
+ window.addEventListener("mouseup", onUp);
666
+ };
667
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
668
+ onMouseDown,
669
+ onMouseEnter: () => setHovered(true),
670
+ onMouseLeave: () => setHovered(false),
671
+ style: {
672
+ flex: "0 0 5px",
673
+ alignSelf: "stretch",
674
+ cursor: horizontal ? "ew-resize" : "ns-resize",
675
+ background: hovered ? DEVTOOL_COLOR_SEMANTIC_SYSTEM : "transparent",
676
+ opacity: hovered ? .6 : 1,
677
+ zIndex: 5
678
+ }
679
+ });
680
+ }
681
+ /**
682
+ * The combined, page-wide launcher shown while every devtool is collapsed — one
683
+ * grouped pill with a segment per registered devtool, so the buttons never
684
+ * overlap or hide behind each other. Rendered by the coordinator's "primary"
685
+ * devtool only.
686
+ */
687
+ function DevtoolsLauncher({ items }) {
688
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
689
+ style: {
690
+ position: "fixed",
691
+ bottom: "16px",
692
+ right: "16px",
693
+ zIndex: 2147483647,
694
+ display: "flex",
695
+ fontFamily: MONO_FONT,
696
+ fontSize: "12px"
697
+ },
698
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
699
+ style: {
700
+ display: "flex",
701
+ background: DEVTOOL_SECTION_BACKGROUND,
702
+ border: `1px solid ${DEVTOOL_COLOR_TEXT_FAINT}`,
703
+ borderRadius: "6px",
704
+ overflow: "hidden",
705
+ boxShadow: "0 8px 24px rgba(0,0,0,0.35)"
706
+ },
707
+ children: items.map((item, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
708
+ onClick: item.onOpen,
709
+ style: {
710
+ display: "flex",
711
+ alignItems: "center",
712
+ gap: "5px",
713
+ background: "transparent",
714
+ color: DEVTOOL_COLOR_TEXT_SECONDARY,
715
+ border: "none",
716
+ borderLeft: i > 0 ? `1px solid ${DEVTOOL_COLOR_TEXT_FAINT}` : "none",
717
+ cursor: "pointer",
718
+ padding: "6px 11px",
719
+ fontFamily: MONO_FONT,
720
+ fontSize: "12px",
721
+ lineHeight: "1.5"
722
+ },
723
+ children: [
724
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.icon }),
725
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.label }),
726
+ item.badge != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
727
+ style: { color: "#38BDF8" },
728
+ children: item.badge
729
+ })
730
+ ]
731
+ }, item.id))
732
+ })
733
+ });
734
+ }
735
+ //#endregion
736
+ //#region ../nice-devtools-shared/src/components/FollowLatestToggles.tsx
737
+ /**
738
+ * The "Follow latest" toggle pair shown above a devtools timeline list, with its
739
+ * indented "clicking latest re-follows" sub-option. `noun` is the thing the
740
+ * timeline tracks ("action" in nice-action, "change" in nice-state) and is woven
741
+ * into the explanatory tooltips so both suites read naturally from one component.
742
+ */
743
+ function FollowLatestToggles({ noun, stayOnLatest, onStayOnLatestChange, followLatestOnSelect, onFollowLatestOnSelectChange }) {
744
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
745
+ style: {
746
+ display: "flex",
747
+ flexDirection: "column",
748
+ flexShrink: 0,
749
+ paddingBottom: "3px",
750
+ background: DEVTOOL_SECTION_BACKGROUND,
751
+ borderBottom: `1px solid ${DEVTOOL_LIST_BASE_BACKGROUND}`
752
+ },
753
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleLabel, {
754
+ title: `Auto-select the most recent ${noun} so the detail pane keeps showing the latest as new ${noun}s land`,
755
+ checked: stayOnLatest,
756
+ onChange: onStayOnLatestChange,
757
+ children: "Follow latest"
758
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
759
+ style: {
760
+ display: "flex",
761
+ alignItems: "center",
762
+ paddingLeft: "12px",
763
+ marginTop: "-4px"
764
+ },
765
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
766
+ "aria-hidden": true,
767
+ style: {
768
+ color: DEVTOOL_COLOR_TEXT_MUTED,
769
+ fontFamily: SANS_FONT,
770
+ fontSize: "10px",
771
+ lineHeight: 1
772
+ },
773
+ children: "└"
774
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleLabel, {
775
+ title: `When you click the latest ${noun}, turn 'Follow latest' back on so the view resumes tracking new ${noun}s. Turn this off to pin exactly to the ${noun} you click instead.`,
776
+ checked: followLatestOnSelect,
777
+ onChange: onFollowLatestOnSelectChange,
778
+ children: "clicking latest re-follows"
779
+ })]
780
+ })]
781
+ });
782
+ }
783
+ function ToggleLabel({ checked, onChange, title, children }) {
784
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
785
+ title,
786
+ style: {
787
+ display: "flex",
788
+ alignItems: "center",
789
+ gap: "6px",
790
+ padding: "5px 10px",
791
+ cursor: "pointer",
792
+ userSelect: "none",
793
+ color: checked ? DEVTOOL_COLOR_TEXT_SECONDARY : DEVTOOL_COLOR_TEXT_MUTED,
794
+ fontSize: "10px",
795
+ fontFamily: SANS_FONT
796
+ },
797
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
798
+ type: "checkbox",
799
+ checked,
800
+ onChange: (e) => onChange(e.target.checked),
801
+ style: {
802
+ accentColor: DEVTOOL_COLOR_SEMANTIC_SYSTEM,
803
+ cursor: "pointer",
804
+ margin: 0
805
+ }
806
+ }), children]
807
+ });
808
+ }
809
+ //#endregion
810
+ //#region ../nice-devtools-shared/src/components/VirtualList.tsx
811
+ /**
812
+ * The shared, virtualized timeline list used by both the nice-action and
813
+ * nice-state devtools. It owns everything about *how the list behaves* — windowed
814
+ * rendering, viewport anchoring across prepends, scroll-the-selection-into-view,
815
+ * and pause-on-hover with a "N new" catch-up hint — while each devtool supplies
816
+ * its own `renderItem` for the row content (which stays unique per devtool).
817
+ *
818
+ * Behaviour notes:
819
+ * • The list is assumed newest-first: new rows prepend at the top. While the
820
+ * cursor is over the list the viewport is held perfectly still (so reading
821
+ * isn't disrupted); fresh rows still render above and a sticky "↑ N new"
822
+ * badge hints they're there. Move the cursor away and normal follow resumes.
823
+ * • `selectedKey` is the stable item key of the selected row (NOT necessarily a
824
+ * sub-selection inside it). When it changes the row is scrolled into view —
825
+ * unless the cursor is hovering, in which case the view is left untouched.
826
+ */
827
+ function DevtoolsVirtualList({ items, getItemKey, renderItem, selectedKey, estimateSize, overscan = 8, rowStyle, empty, footer, style }) {
828
+ const containerRef = (0, react.useRef)(null);
829
+ const [isHovering, setIsHovering] = (0, react.useState)(false);
830
+ const hoveringRef = (0, react.useRef)(false);
831
+ hoveringRef.current = isHovering;
832
+ const hoverBaselineCountRef = (0, react.useRef)(0);
833
+ const pendingCount = isHovering ? Math.max(0, items.length - hoverBaselineCountRef.current) : 0;
834
+ const hasPendingActivity = isHovering && pendingCount > 0;
835
+ const getItemKeyRef = (0, react.useRef)(getItemKey);
836
+ getItemKeyRef.current = getItemKey;
837
+ const itemKeys = (0, react.useMemo)(() => items.map((item, i) => getItemKeyRef.current(item, i)), [items]);
838
+ const virtualizer = (0, _tanstack_react_virtual.useVirtualizer)({
839
+ count: items.length,
840
+ getScrollElement: () => containerRef.current,
841
+ estimateSize: () => estimateSize,
842
+ overscan,
843
+ getItemKey: (index) => itemKeys[index]
844
+ });
845
+ const onScroll = useListScrollAnchor({
846
+ containerRef,
847
+ virtualizer,
848
+ itemKeys,
849
+ selectedKey,
850
+ hoveringRef
851
+ });
852
+ const prevSelectedRef = (0, react.useRef)(selectedKey);
853
+ (0, react.useEffect)(() => {
854
+ if (selectedKey === prevSelectedRef.current) return;
855
+ prevSelectedRef.current = selectedKey;
856
+ if (selectedKey == null) return;
857
+ if (hoveringRef.current) return;
858
+ const index = itemKeys.indexOf(selectedKey);
859
+ if (index >= 0) virtualizer.scrollToIndex(index, { align: "auto" });
860
+ }, [
861
+ selectedKey,
862
+ itemKeys,
863
+ virtualizer
864
+ ]);
865
+ if (items.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: empty });
866
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
867
+ ref: containerRef,
868
+ style,
869
+ onScroll,
870
+ onMouseEnter: () => {
871
+ hoverBaselineCountRef.current = items.length;
872
+ setIsHovering(true);
873
+ },
874
+ onMouseLeave: () => setIsHovering(false),
875
+ children: [
876
+ hasPendingActivity && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PendingIndicator, { count: pendingCount }),
877
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
878
+ style: {
879
+ position: "relative",
880
+ width: "100%",
881
+ height: virtualizer.getTotalSize()
882
+ },
883
+ children: virtualizer.getVirtualItems().map((vItem) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
884
+ "data-index": vItem.index,
885
+ ref: virtualizer.measureElement,
886
+ style: {
887
+ position: "absolute",
888
+ top: 0,
889
+ left: 0,
890
+ width: "100%",
891
+ transform: `translateY(${vItem.start}px)`,
892
+ ...rowStyle
893
+ },
894
+ children: renderItem(items[vItem.index], vItem.index)
895
+ }, vItem.key))
896
+ }),
897
+ footer
898
+ ]
899
+ });
900
+ }
901
+ /**
902
+ * A sticky, zero-height overlay floating over the rows without taking layout space. Shown only once
903
+ * new items have arrived while hovering, hinting they're above (newest-first) so the user can scroll
904
+ * up without the view having jumped there.
905
+ */
906
+ function PendingIndicator({ count }) {
907
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
908
+ style: {
909
+ position: "sticky",
910
+ top: 0,
911
+ height: 0,
912
+ zIndex: 5,
913
+ pointerEvents: "none"
914
+ },
915
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
916
+ style: {
917
+ position: "absolute",
918
+ top: "6px",
919
+ right: "10px",
920
+ display: "flex",
921
+ alignItems: "center",
922
+ gap: "5px",
923
+ padding: "2px 7px",
924
+ borderRadius: "10px",
925
+ fontSize: "10px",
926
+ fontFamily: SANS_FONT,
927
+ color: DEVTOOL_COLOR_SEMANTIC_WARNING,
928
+ background: DEVTOOL_TOOLTIP_BACKGROUND,
929
+ border: `1px solid ${DEVTOOL_COLOR_SEMANTIC_WARNING}55`,
930
+ boxShadow: "0 2px 8px rgba(0,0,0,0.5)",
931
+ whiteSpace: "nowrap"
932
+ },
933
+ children: [
934
+ "↑ ",
935
+ count,
936
+ " new"
937
+ ]
938
+ })
939
+ });
940
+ }
941
+ //#endregion
942
+ //#region src/devtools/core/devtools_colors.ts
943
+ const DEVTOOL_COLOR_HANDLER_LOCAL_TEXT = "#34bb89";
944
+ const DEVTOOL_COLOR_HANDLER_LOCAL_BORDER = "#144427";
945
+ const DEVTOOL_COLOR_HANDLER_EXTERNAL_TEXT = "#cfa12a";
946
+ const DEVTOOL_COLOR_HANDLER_EXTERNAL_BORDER = "#723917";
947
+ const DEVTOOL_LIST_HEADER_SELECTED_BACKGROUND = "#1d2942";
948
+ const DEVTOOL_LIST_GROUP_DIVIDER = "#101109";
949
+ const DEVTOOL_STACK_TRACE_BACKGROUND = "#040a13";
950
+ const DEVTOOL_ERROR_BADGE_BACKGROUND = "#2d0f0f";
951
+ const DEVTOOL_COLOR_CALL_STACK_DIVIDER = "#0a1120";
952
+ const DEVTOOL_STACK_FRAME_USER_NUMBER = "#64748b";
953
+ const DEVTOOL_STACK_FRAME_USER_FUNCTION = "#e2e8f0";
954
+ const DEVTOOL_STACK_FRAME_USER_FILE = "#8a9ebb";
955
+ const DEVTOOL_STACK_FRAME_INTERNAL_NUMBER = "#2d3f53";
956
+ const DEVTOOL_STACK_FRAME_INTERNAL_FUNCTION = "#50698b";
957
+ const DEVTOOL_STACK_FRAME_INTERNAL_FILE = "#425979";
958
+ const SEMANTIC_COLORS = {
959
+ running_action: {
960
+ color: DEVTOOL_COLOR_SEMANTIC_SYSTEM,
961
+ borderColor: `${DEVTOOL_COLOR_SEMANTIC_SYSTEM}55`
962
+ },
963
+ success: {
964
+ color: DEVTOOL_COLOR_SEMANTIC_SUCCESS,
965
+ borderColor: `${DEVTOOL_COLOR_SEMANTIC_SUCCESS}55`
966
+ },
967
+ action_error: {
968
+ color: DEVTOOL_COLOR_SEMANTIC_WARNING,
969
+ borderColor: `${DEVTOOL_COLOR_SEMANTIC_WARNING}55`
970
+ },
971
+ failed: {
972
+ color: DEVTOOL_COLOR_SEMANTIC_ERROR,
973
+ borderColor: `${DEVTOOL_COLOR_SEMANTIC_ERROR}55`
974
+ },
975
+ aborted: {
976
+ color: DEVTOOL_COLOR_SEMANTIC_METADATA,
977
+ borderColor: DEVTOOL_COLOR_TEXT_FAINT
978
+ },
979
+ error: {
980
+ color: DEVTOOL_COLOR_SEMANTIC_ERROR,
981
+ borderColor: DEVTOOL_COLOR_SEMANTIC_ERROR,
982
+ subtle: {
983
+ color: "#FF5C5C9f",
984
+ borderColor: "transparent"
985
+ }
986
+ },
987
+ domain: {
988
+ color: DEVTOOL_COLOR_SEMANTIC_SYSTEM,
989
+ borderColor: `${DEVTOOL_COLOR_SEMANTIC_SYSTEM}55`,
990
+ subtle: {
991
+ color: "#4b5563",
992
+ borderColor: "transparent"
993
+ }
994
+ },
995
+ handler_local: {
996
+ color: DEVTOOL_COLOR_HANDLER_LOCAL_TEXT,
997
+ borderColor: DEVTOOL_COLOR_HANDLER_LOCAL_BORDER,
998
+ subtle: {
999
+ color: "#4b5563",
1000
+ borderColor: "transparent"
1001
+ }
1002
+ },
1003
+ handler_external: {
1004
+ color: DEVTOOL_COLOR_HANDLER_EXTERNAL_TEXT,
1005
+ borderColor: DEVTOOL_COLOR_HANDLER_EXTERNAL_BORDER,
1006
+ subtle: {
1007
+ color: "#cfa12a9f",
1008
+ borderColor: "transparent"
1009
+ }
1010
+ },
1011
+ origin: {
1012
+ color: DEVTOOL_COLOR_SEMANTIC_SYSTEM,
1013
+ borderColor: `${DEVTOOL_COLOR_SEMANTIC_SYSTEM}55`,
1014
+ subtle: {
1015
+ color: `${DEVTOOL_COLOR_SEMANTIC_SYSTEM}9f`,
1016
+ borderColor: "transparent"
1017
+ }
1018
+ },
1019
+ age: {
1020
+ color: DEVTOOL_COLOR_SEMANTIC_METADATA,
1021
+ borderColor: DEVTOOL_SECTION_BACKGROUND,
1022
+ subtle: {
1023
+ color: "#4b5563",
1024
+ borderColor: "transparent"
1025
+ }
1026
+ },
1027
+ io_input: {
1028
+ color: "#fbbf24",
1029
+ subtle: {
1030
+ color: "#fbbe249f",
1031
+ borderColor: "transparent"
1032
+ },
1033
+ borderColor: "#78350f"
1034
+ },
1035
+ io_output: {
1036
+ color: "#a78bfa",
1037
+ subtle: {
1038
+ color: "#a78bfa9f",
1039
+ borderColor: "transparent"
1040
+ },
1041
+ borderColor: "#4c1d95"
1042
+ },
1043
+ default: {
1044
+ color: DEVTOOL_COLOR_TEXT_MUTED,
1045
+ borderColor: DEVTOOL_COLOR_TEXT_FAINT
1046
+ }
1047
+ };
1048
+ //#endregion
1049
+ //#region src/devtools/browser/ui_util/size.ts
1050
+ function getSizeValue(size) {
1051
+ switch (size) {
1052
+ case "sm": return .75;
1053
+ case "md": return 1;
1054
+ case "lg": return 1.2;
1055
+ default: return 1;
1056
+ }
1057
+ }
1058
+ //#endregion
1059
+ //#region src/devtools/browser/components/DetailSection.tsx
1060
+ const COMPACT_CHAR_LIMIT = 120;
1061
+ const MONO$2 = "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace";
1062
+ function tryParseJson(s) {
1063
+ try {
1064
+ return JSON.parse(s);
1065
+ } catch {
1066
+ return null;
1067
+ }
1068
+ }
1069
+ function DetailSection({ label, value, color = DEVTOOL_COLOR_SEMANTIC_SYSTEM }) {
1070
+ const parsedFromString = typeof value === "string" ? tryParseJson(value) : null;
1071
+ const isPlainString = typeof value === "string" && parsedFromString === null;
1072
+ const effectiveValue = parsedFromString !== null ? parsedFromString : value;
1073
+ const [expanded, setExpanded] = (0, react.useState)(false);
1074
+ const fullJson = isPlainString ? value : safeStringify(effectiveValue, 2);
1075
+ const compactJson = isPlainString ? value : safeStringify(effectiveValue, 0);
1076
+ const canExpand = fullJson !== compactJson || compactJson.length > COMPACT_CHAR_LIMIT;
1077
+ const compactDisplay = compactJson.length > COMPACT_CHAR_LIMIT ? `${compactJson.slice(0, COMPACT_CHAR_LIMIT)}…` : compactJson;
1078
+ const baseStyle = isPlainString ? {
1079
+ background: DEVTOOL_SECTION_STRING_BACKGROUND,
1080
+ color: DEVTOOL_COLOR_TEXT_MUTED
1081
+ } : {
1082
+ background: DEVTOOL_SECTION_BACKGROUND,
1083
+ color: DEVTOOL_COLOR_TEXT_SECONDARY
1084
+ };
1085
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1086
+ style: {
1087
+ display: "flex",
1088
+ alignItems: "center",
1089
+ justifyContent: "space-between",
1090
+ marginBottom: "3px"
1091
+ },
1092
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SectionLabel, {
1093
+ label,
1094
+ color
1095
+ }), canExpand && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1096
+ style: {
1097
+ color: "#334155",
1098
+ fontSize: "11px"
1099
+ },
1100
+ children: expanded ? "▾" : "▸"
1101
+ })]
1102
+ }), expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1103
+ onClick: canExpand ? () => setExpanded(false) : void 0,
1104
+ style: {
1105
+ margin: 0,
1106
+ padding: "6px 8px",
1107
+ borderRadius: "4px",
1108
+ fontSize: "11px",
1109
+ fontFamily: MONO$2,
1110
+ overflowX: "auto",
1111
+ textAlign: "left",
1112
+ whiteSpace: "pre-wrap",
1113
+ wordBreak: "break-all",
1114
+ cursor: canExpand ? "pointer" : "default",
1115
+ fontStyle: isPlainString ? "italic" : "normal",
1116
+ ...baseStyle
1117
+ },
1118
+ children: isPlainString ? fullJson : renderColoredJson(fullJson)
1119
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1120
+ onClick: canExpand ? () => setExpanded(true) : void 0,
1121
+ style: {
1122
+ padding: "5px 8px",
1123
+ borderRadius: "4px",
1124
+ fontSize: "11px",
1125
+ fontFamily: MONO$2,
1126
+ whiteSpace: "nowrap",
1127
+ overflow: "hidden",
1128
+ textOverflow: "ellipsis",
1129
+ cursor: canExpand ? "pointer" : "default",
1130
+ fontStyle: isPlainString ? "italic" : "normal",
1131
+ ...baseStyle
1132
+ },
1133
+ children: compactDisplay
1134
+ })] });
1135
+ }
1136
+ //#endregion
1137
+ //#region src/devtools/browser/components/NiceErrorDisplay.tsx
1138
+ const MONO$1 = "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace";
1139
+ const SANS$1 = "ui-sans-serif, system-ui, -apple-system, sans-serif";
1140
+ function isNiceErrorJson(value) {
1141
+ if (typeof value !== "object" || value == null) return false;
1142
+ const v = value;
1143
+ return v["name"] === "NiceError" && Array.isArray(v["ids"]) && typeof v["message"] === "string";
1144
+ }
1145
+ function NiceErrorBody({ error }) {
1146
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1147
+ style: {
1148
+ background: DEVTOOL_ERROR_BACKGROUND,
1149
+ border: `1px solid ${DEVTOOL_COLOR_SEMANTIC_ERROR}`,
1150
+ borderRadius: "4px",
1151
+ overflow: "hidden"
1152
+ },
1153
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1154
+ style: {
1155
+ padding: "12px 14px",
1156
+ color: DEVTOOL_COLOR_SEMANTIC_ERROR,
1157
+ fontSize: "1em",
1158
+ fontWeight: "700",
1159
+ lineHeight: "1.45",
1160
+ wordBreak: "break-word",
1161
+ fontFamily: SANS$1,
1162
+ textAlign: "left",
1163
+ borderBottom: `1px solid ${DEVTOOL_ERROR_BADGE_BACKGROUND}`
1164
+ },
1165
+ children: error.message
1166
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1167
+ style: {
1168
+ padding: "6px 14px 8px",
1169
+ display: "flex",
1170
+ flexWrap: "wrap",
1171
+ gap: "10px",
1172
+ alignItems: "center",
1173
+ background: DEVTOOL_ERROR_BADGE_BACKGROUND
1174
+ },
1175
+ children: [
1176
+ error.ids.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1177
+ style: {
1178
+ color: DEVTOOL_COLOR_SEMANTIC_ERROR,
1179
+ fontFamily: MONO$1,
1180
+ fontSize: "11px",
1181
+ fontWeight: "600"
1182
+ },
1183
+ children: id
1184
+ }, id)),
1185
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1186
+ style: {
1187
+ color: DEVTOOL_COLOR_TEXT_MUTED,
1188
+ fontSize: "10px",
1189
+ fontFamily: SANS$1
1190
+ },
1191
+ children: [
1192
+ "domain:",
1193
+ " ",
1194
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1195
+ style: {
1196
+ color: DEVTOOL_COLOR_TEXT_MUTED,
1197
+ fontFamily: MONO$1
1198
+ },
1199
+ children: error.def.domain
1200
+ })
1201
+ ]
1202
+ }),
1203
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1204
+ style: {
1205
+ color: DEVTOOL_COLOR_TEXT_MUTED,
1206
+ fontSize: "10px",
1207
+ fontFamily: SANS$1
1208
+ },
1209
+ children: [
1210
+ "http:",
1211
+ " ",
1212
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1213
+ style: {
1214
+ color: DEVTOOL_COLOR_TEXT_MUTED,
1215
+ fontFamily: MONO$1
1216
+ },
1217
+ children: error.httpStatusCode
1218
+ })
1219
+ ]
1220
+ })
1221
+ ]
1222
+ })]
1223
+ });
1224
+ }
1225
+ function NiceErrorDisplay({ error, label, color }) {
1226
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SectionLabel, {
1227
+ label,
1228
+ color
1229
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NiceErrorBody, { error })] });
1230
+ }
1231
+ //#endregion
1232
+ //#region src/devtools/browser/devtools_storage.ts
1233
+ const devtools_storage = (0, _nice_code_util.createTypedWebLocalStorage)({
1234
+ localStorage: window.localStorage,
1235
+ keyPrefix: "nice-action-devtools::"
1236
+ });
1237
+ devtools_storage.updateJsonWithDef("runtimeToProjectFilePath", {}, (prev) => prev);
1238
+ //#endregion
1239
+ //#region src/devtools/browser/components/sourceMapResolver.ts
1240
+ const consumerCache = /* @__PURE__ */ new Map();
1241
+ async function loadConsumer(fileUrl) {
1242
+ try {
1243
+ if (!fileUrl.startsWith("http://") && !fileUrl.startsWith("https://")) return null;
1244
+ const resp = await fetch(fileUrl);
1245
+ if (!resp.ok) return null;
1246
+ const match = (await resp.text()).match(/\/\/[#@] sourceMappingURL=(\S+)/);
1247
+ if (match == null) return null;
1248
+ const mapRef = match[1];
1249
+ let mapJson;
1250
+ if (mapRef.startsWith("data:")) {
1251
+ const commaIdx = mapRef.indexOf(",");
1252
+ if (commaIdx === -1) return null;
1253
+ const header = mapRef.slice(5, commaIdx);
1254
+ const payload = mapRef.slice(commaIdx + 1);
1255
+ mapJson = header.includes("base64") ? atob(payload) : decodeURIComponent(payload);
1256
+ } else {
1257
+ const mapUrl = new URL(mapRef, fileUrl).href;
1258
+ const mapResp = await fetch(mapUrl);
1259
+ if (!mapResp.ok) return null;
1260
+ mapJson = await mapResp.text();
1261
+ }
1262
+ return new source_map_js.SourceMapConsumer(JSON.parse(mapJson));
1263
+ } catch {
1264
+ return null;
1265
+ }
1266
+ }
1267
+ function getConsumer(fileUrl) {
1268
+ const key = fileUrl.split("?")[0] ?? fileUrl;
1269
+ if (!consumerCache.has(key)) consumerCache.set(key, loadConsumer(fileUrl));
1270
+ return consumerCache.get(key);
1271
+ }
1272
+ async function resolveCompiledPosition(fileUrl, line, col) {
1273
+ try {
1274
+ const consumer = await getConsumer(fileUrl);
1275
+ if (consumer == null) return null;
1276
+ const pos = consumer.originalPositionFor({
1277
+ line,
1278
+ column: col
1279
+ });
1280
+ if (pos.source == null || pos.line == null) return null;
1281
+ return {
1282
+ file: pos.source,
1283
+ line: pos.line,
1284
+ col: pos.column ?? 0
1285
+ };
1286
+ } catch {
1287
+ return null;
1288
+ }
1289
+ }
1290
+ //#endregion
1291
+ //#region src/devtools/browser/components/StackTraceSection.tsx
1292
+ const INTERNAL_PATTERNS = [
1293
+ "/nice-action/",
1294
+ "@nice-code/action",
1295
+ "/nice-error/",
1296
+ "@nice-code/error",
1297
+ "node_modules/",
1298
+ "native code",
1299
+ "<anonymous>"
1300
+ ];
1301
+ function isInternalFrame(file) {
1302
+ return INTERNAL_PATTERNS.some((p) => file.includes(p));
1303
+ }
1304
+ function parseStackFrames(stack) {
1305
+ return stack.split("\n").slice(1).map((line) => {
1306
+ const raw = line.trim();
1307
+ if (raw === "") return null;
1308
+ const matchFn = raw.match(/^at (.+?) \((.+):(\d+):(\d+)\)$/);
1309
+ if (matchFn != null) {
1310
+ const file = matchFn[2];
1311
+ return {
1312
+ fn: matchFn[1],
1313
+ file,
1314
+ line: parseInt(matchFn[3], 10),
1315
+ col: parseInt(matchFn[4], 10),
1316
+ raw,
1317
+ isInternal: isInternalFrame(file)
1318
+ };
1319
+ }
1320
+ const matchUrl = raw.match(/^at (.+):(\d+):(\d+)$/);
1321
+ if (matchUrl != null) {
1322
+ const file = matchUrl[1];
1323
+ return {
1324
+ file,
1325
+ line: parseInt(matchUrl[2], 10),
1326
+ col: parseInt(matchUrl[3], 10),
1327
+ raw,
1328
+ isInternal: isInternalFrame(file)
1329
+ };
1330
+ }
1331
+ return {
1332
+ raw,
1333
+ isInternal: true
1334
+ };
1335
+ }).filter((f) => f != null);
1336
+ }
1337
+ async function resolveSourceMapPositions(frames) {
1338
+ return Promise.all(frames.map(async (frame) => {
1339
+ if (frame.isInternal || frame.file == null || frame.line == null) return frame;
1340
+ const resolved = await resolveCompiledPosition(frame.file, frame.line, frame.col ?? 0);
1341
+ if (resolved == null) return frame;
1342
+ return {
1343
+ ...frame,
1344
+ originalFile: frame.file,
1345
+ file: resolved.file,
1346
+ line: resolved.line,
1347
+ col: resolved.col
1348
+ };
1349
+ }));
1350
+ }
1351
+ function formatFrameFile(file) {
1352
+ if (file == null) return "?";
1353
+ let path = file;
1354
+ try {
1355
+ const url = new URL(file);
1356
+ path = url.pathname.split("?")[0] ?? url.pathname;
1357
+ if (path.startsWith("/@fs/")) path = path.slice(4);
1358
+ } catch {}
1359
+ const parts = path.replace(/\\/g, "/").split("/").filter(Boolean);
1360
+ return (parts[0]?.match(/^[a-zA-Z]:$/) != null ? parts.slice(1) : parts).slice(-4).join("/") || file;
1361
+ }
1362
+ function getOriginUrl(frame) {
1363
+ const candidate = frame.originalFile ?? frame.file;
1364
+ if (candidate == null) return null;
1365
+ try {
1366
+ new URL(candidate);
1367
+ return candidate;
1368
+ } catch {
1369
+ return null;
1370
+ }
1371
+ }
1372
+ function frameNeedsProjectRoot(frame) {
1373
+ if (frame.isInternal) return false;
1374
+ const originUrl = getOriginUrl(frame);
1375
+ if (originUrl == null) return false;
1376
+ return !(new URL(originUrl).pathname.split("?")[0] ?? "").startsWith("/@fs/");
1377
+ }
1378
+ function buildVscodeUrl(frame, projectRoot) {
1379
+ if (frame.line == null) return null;
1380
+ const originUrl = getOriginUrl(frame);
1381
+ if (originUrl != null) {
1382
+ let path = new URL(originUrl).pathname.split("?")[0] ?? "";
1383
+ if (path.startsWith("/@fs/")) {
1384
+ path = path.slice(5);
1385
+ if (/^\/[a-zA-Z]:\//.test(path)) path = path.slice(1);
1386
+ return `vscode://file/${path}:${frame.line}:${frame.col ?? 1}`;
1387
+ }
1388
+ if (projectRoot == null) return null;
1389
+ return `vscode://file/${projectRoot.replace(/[/\\]+$/, "").replace(/\\/g, "/")}${path}:${frame.line}:${frame.col ?? 1}`;
1390
+ }
1391
+ const filePath = frame.file;
1392
+ if (filePath == null) return null;
1393
+ const normalPath = filePath.replace(/\\/g, "/");
1394
+ if (/^[a-zA-Z]:\//.test(normalPath)) return `vscode://file/${normalPath}:${frame.line}:${frame.col ?? 1}`;
1395
+ if (/^\/[a-zA-Z]:\//.test(normalPath)) return `vscode://file/${normalPath.slice(1)}:${frame.line}:${frame.col ?? 1}`;
1396
+ if (normalPath.startsWith("/")) {
1397
+ if (projectRoot == null) return null;
1398
+ return `vscode://file/${projectRoot.replace(/[/\\]+$/, "").replace(/\\/g, "/")}${normalPath}:${frame.line}:${frame.col ?? 1}`;
1399
+ }
1400
+ if (projectRoot == null) return null;
1401
+ return `vscode://file/${projectRoot.replace(/[/\\]+$/, "").replace(/\\/g, "/")}/${normalPath}:${frame.line}:${frame.col ?? 1}`;
1402
+ }
1403
+ function ProjectRootSetupModal({ envId, currentValue, onSave, onCancel }) {
1404
+ const [value, setValue] = (0, react.useState)(currentValue);
1405
+ const inputRef = (0, react.useRef)(null);
1406
+ (0, react.useEffect)(() => {
1407
+ inputRef.current?.focus();
1408
+ inputRef.current?.select();
1409
+ }, []);
1410
+ (0, react.useEffect)(() => {
1411
+ const handler = (e) => {
1412
+ if (e.key === "Escape") onCancel();
1413
+ };
1414
+ window.addEventListener("keydown", handler);
1415
+ return () => window.removeEventListener("keydown", handler);
1416
+ }, [onCancel]);
1417
+ const handleSubmit = (e) => {
1418
+ e.preventDefault();
1419
+ const trimmed = value.trim();
1420
+ if (trimmed !== "") onSave(trimmed);
1421
+ };
1422
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1423
+ style: {
1424
+ position: "fixed",
1425
+ inset: 0,
1426
+ zIndex: 2147483647,
1427
+ display: "flex",
1428
+ alignItems: "center",
1429
+ justifyContent: "center"
1430
+ },
1431
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1432
+ onClick: onCancel,
1433
+ style: {
1434
+ position: "absolute",
1435
+ inset: 0,
1436
+ background: "rgba(0, 0, 0, 0.72)"
1437
+ }
1438
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
1439
+ onSubmit: handleSubmit,
1440
+ style: {
1441
+ position: "relative",
1442
+ background: DEVTOOL_TOOLTIP_BACKGROUND,
1443
+ border: `1px solid ${DEVTOOL_TOOLTIP_BORDER}`,
1444
+ borderRadius: "8px",
1445
+ width: "420px",
1446
+ maxWidth: "90vw",
1447
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.8)",
1448
+ overflow: "hidden",
1449
+ display: "flex",
1450
+ flexDirection: "column"
1451
+ },
1452
+ children: [
1453
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1454
+ style: {
1455
+ background: DEVTOOL_TOOLTIP_TITLE_BACKGROUND,
1456
+ borderBottom: `1px solid ${DEVTOOL_TOOLTIP_BORDER}`,
1457
+ padding: "10px 14px"
1458
+ },
1459
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1460
+ style: {
1461
+ color: DEVTOOL_COLOR_TEXT_EMPHASIS,
1462
+ fontSize: "12px",
1463
+ fontWeight: 600,
1464
+ marginBottom: "2px"
1465
+ },
1466
+ children: "Set project root"
1467
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1468
+ style: {
1469
+ color: DEVTOOL_COLOR_TEXT_MUTED,
1470
+ fontSize: "10px"
1471
+ },
1472
+ children: [
1473
+ "Runtime:",
1474
+ " ",
1475
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1476
+ style: {
1477
+ color: DEVTOOL_COLOR_SEMANTIC_SYSTEM,
1478
+ fontFamily: "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace"
1479
+ },
1480
+ children: envId
1481
+ })
1482
+ ]
1483
+ })]
1484
+ }),
1485
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1486
+ style: {
1487
+ padding: "14px",
1488
+ display: "flex",
1489
+ flexDirection: "column",
1490
+ gap: "8px"
1491
+ },
1492
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1493
+ style: {
1494
+ color: DEVTOOL_COLOR_TEXT_MUTED,
1495
+ fontSize: "10px",
1496
+ lineHeight: 1.4
1497
+ },
1498
+ children: [
1499
+ "Provide the absolute path to this project's root so that root-relative source file paths (e.g.",
1500
+ " ",
1501
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1502
+ style: { fontFamily: "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace" },
1503
+ children: "src/App.tsx"
1504
+ }),
1505
+ ") can be opened in VS Code."
1506
+ ]
1507
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1508
+ ref: inputRef,
1509
+ value,
1510
+ onChange: (e) => setValue(e.currentTarget.value),
1511
+ placeholder: "e.g. C:/d/nice-code/packages/demo-frontend",
1512
+ style: {
1513
+ background: DEVTOOL_STACK_TRACE_BACKGROUND,
1514
+ border: `1px solid ${DEVTOOL_TOOLTIP_BORDER}`,
1515
+ borderRadius: "4px",
1516
+ color: DEVTOOL_COLOR_TEXT_SECONDARY,
1517
+ fontSize: "11px",
1518
+ fontFamily: "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace",
1519
+ padding: "7px 9px",
1520
+ outline: "none",
1521
+ width: "100%",
1522
+ boxSizing: "border-box"
1523
+ }
1524
+ })]
1525
+ }),
1526
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1527
+ style: {
1528
+ display: "flex",
1529
+ justifyContent: "flex-end",
1530
+ gap: "8px",
1531
+ padding: "10px 14px",
1532
+ borderTop: `1px solid ${DEVTOOL_PANEL_BORDER}`
1533
+ },
1534
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1535
+ type: "button",
1536
+ onClick: onCancel,
1537
+ style: {
1538
+ background: "transparent",
1539
+ border: `1px solid ${DEVTOOL_PANEL_BORDER}`,
1540
+ borderRadius: "4px",
1541
+ color: DEVTOOL_COLOR_TEXT_MUTED,
1542
+ fontSize: "11px",
1543
+ padding: "5px 12px",
1544
+ cursor: "pointer"
1545
+ },
1546
+ children: "Cancel"
1547
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1548
+ type: "submit",
1549
+ disabled: value.trim() === "",
1550
+ style: {
1551
+ background: `${DEVTOOL_COLOR_SEMANTIC_SYSTEM}22`,
1552
+ border: `1px solid ${DEVTOOL_COLOR_SEMANTIC_SYSTEM}66`,
1553
+ borderRadius: "4px",
1554
+ color: DEVTOOL_COLOR_SEMANTIC_SYSTEM,
1555
+ fontSize: "11px",
1556
+ padding: "5px 12px",
1557
+ cursor: value.trim() === "" ? "not-allowed" : "pointer",
1558
+ opacity: value.trim() === "" ? .5 : 1
1559
+ },
1560
+ children: "Save"
1561
+ })]
1562
+ })
1563
+ ]
1564
+ })]
1565
+ });
1566
+ }
1567
+ function OpenInVscodeButton({ frame, projectRoot, onNeedSetup }) {
1568
+ const [hovered, setHovered] = (0, react.useState)(false);
1569
+ const needsSetup = frameNeedsProjectRoot(frame) && projectRoot == null;
1570
+ const url = buildVscodeUrl(frame, projectRoot);
1571
+ if (frame.file == null || frame.line == null) return null;
1572
+ if (!needsSetup && url == null) return null;
1573
+ const baseStyle = {
1574
+ position: "absolute",
1575
+ bottom: "3px",
1576
+ right: "4px",
1577
+ display: "flex",
1578
+ alignItems: "center",
1579
+ justifyContent: "center",
1580
+ width: "16px",
1581
+ height: "16px",
1582
+ opacity: hovered ? .85 : .25,
1583
+ transition: "opacity 0.15s ease",
1584
+ cursor: "pointer",
1585
+ flexShrink: 0
1586
+ };
1587
+ if (needsSetup) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1588
+ onClick: (e) => {
1589
+ e.stopPropagation();
1590
+ onNeedSetup();
1591
+ },
1592
+ onMouseEnter: () => setHovered(true),
1593
+ onMouseLeave: () => setHovered(false),
1594
+ title: "Set project root to open in VS Code",
1595
+ style: {
1596
+ ...baseStyle,
1597
+ color: DEVTOOL_COLOR_SEMANTIC_WARNING,
1598
+ background: "none",
1599
+ border: "none",
1600
+ padding: 0
1601
+ },
1602
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ExternalLink, {
1603
+ size: 10,
1604
+ strokeWidth: 1.8
1605
+ })
1606
+ });
1607
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
1608
+ href: url,
1609
+ onClick: (e) => e.stopPropagation(),
1610
+ onMouseEnter: () => setHovered(true),
1611
+ onMouseLeave: () => setHovered(false),
1612
+ title: "Open in VS Code",
1613
+ style: {
1614
+ ...baseStyle,
1615
+ color: DEVTOOL_COLOR_TEXT_SECONDARY,
1616
+ textDecoration: "none"
1617
+ },
1618
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ExternalLink, {
1619
+ size: 10,
1620
+ strokeWidth: 1.8
1621
+ })
1622
+ });
1623
+ }
1624
+ function countUserFrames(_runtime, stack) {
1625
+ if (stack == null) return 0;
1626
+ return parseStackFrames(stack).filter((f) => !f.isInternal).length;
1627
+ }
1628
+ function StackTraceSection({ label, stack, color = DEVTOOL_COLOR_SEMANTIC_SYSTEM, runtime, minFrameCount }) {
1629
+ const [showAll, setShowAll] = (0, react.useState)(false);
1630
+ const [resolvedFrames, setResolvedFrames] = (0, react.useState)(null);
1631
+ const [projectRootMap, setProjectRootMap] = (0, react.useState)({});
1632
+ const [showSetupModal, setShowSetupModal] = (0, react.useState)(false);
1633
+ (0, react.useEffect)(() => {
1634
+ devtools_storage.getJsonOrDef("runtimeToProjectFilePath", {}).then((map) => setProjectRootMap(map));
1635
+ }, []);
1636
+ (0, react.useEffect)(() => {
1637
+ if (stack == null) return;
1638
+ setResolvedFrames(null);
1639
+ const parsed = parseStackFrames(stack);
1640
+ let cancelled = false;
1641
+ resolveSourceMapPositions(parsed).then((frames) => {
1642
+ if (!cancelled) setResolvedFrames(frames);
1643
+ });
1644
+ return () => {
1645
+ cancelled = true;
1646
+ };
1647
+ }, [stack]);
1648
+ if (stack == null) return null;
1649
+ const allFrames = resolvedFrames ?? parseStackFrames(stack);
1650
+ const userFrames = allFrames.filter((f) => !f.isInternal);
1651
+ const hasInternalFrames = allFrames.length > userFrames.length;
1652
+ const displayFrames = showAll ? allFrames : userFrames;
1653
+ const phantomFrameCount = !showAll ? Math.max(0, (minFrameCount ?? 0) - displayFrames.length) : 0;
1654
+ if (allFrames.length === 0) return null;
1655
+ const runtimeKey = `envId[${runtime.envId}]`;
1656
+ const projectRoot = projectRootMap[runtimeKey] ?? null;
1657
+ const handleSaveProjectRoot = (path) => {
1658
+ setProjectRootMap((prev) => ({
1659
+ ...prev,
1660
+ [runtimeKey]: path
1661
+ }));
1662
+ setShowSetupModal(false);
1663
+ devtools_storage.updateJsonWithDef("runtimeToProjectFilePath", {}, (prev) => ({
1664
+ ...prev,
1665
+ [runtimeKey]: path
1666
+ }));
1667
+ };
1668
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
1669
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1670
+ style: {
1671
+ display: "flex",
1672
+ alignItems: "center",
1673
+ justifyContent: "space-between",
1674
+ marginBottom: "3px"
1675
+ },
1676
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SectionLabel, {
1677
+ label,
1678
+ color
1679
+ }), hasInternalFrames && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1680
+ style: {
1681
+ color: "#64748b",
1682
+ fontSize: "9px"
1683
+ },
1684
+ children: !showAll ? "user only" : `all (${allFrames.length})`
1685
+ })]
1686
+ }),
1687
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1688
+ onClick: () => setShowAll((s) => !s),
1689
+ style: {
1690
+ background: DEVTOOL_STACK_TRACE_BACKGROUND,
1691
+ borderRadius: "4px",
1692
+ padding: "2px",
1693
+ overflow: "hidden",
1694
+ cursor: "pointer"
1695
+ },
1696
+ children: [displayFrames.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1697
+ style: {
1698
+ padding: "6px 10px",
1699
+ color: DEVTOOL_COLOR_TEXT_MUTED,
1700
+ fontSize: "10px",
1701
+ fontStyle: "italic"
1702
+ },
1703
+ children: "no user frames captured"
1704
+ }) : displayFrames.map((frame, idx) => {
1705
+ const displayFile = formatFrameFile(frame.file);
1706
+ const slashIdx = displayFile.lastIndexOf("/");
1707
+ const folderPrefix = slashIdx >= 0 ? displayFile.slice(0, slashIdx + 1) : "";
1708
+ const bareFilename = slashIdx >= 0 ? displayFile.slice(slashIdx + 1) : displayFile;
1709
+ const titleStr = frame.file != null ? frame.file : frame.raw;
1710
+ const isUser = !frame.isInternal;
1711
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1712
+ title: titleStr,
1713
+ style: {
1714
+ position: "relative",
1715
+ display: "flex",
1716
+ flexDirection: "row",
1717
+ gap: "0.5em",
1718
+ padding: "4px",
1719
+ paddingRight: "22px",
1720
+ borderBottom: `1px solid ${DEVTOOL_LIST_BASE_BACKGROUND}`
1721
+ },
1722
+ children: [
1723
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1724
+ style: {
1725
+ color: isUser ? DEVTOOL_STACK_FRAME_USER_NUMBER : DEVTOOL_STACK_FRAME_INTERNAL_NUMBER,
1726
+ fontSize: "10px",
1727
+ minWidth: "10px",
1728
+ textAlign: "left",
1729
+ flexShrink: 0,
1730
+ fontVariantNumeric: "tabular-nums"
1731
+ },
1732
+ children: idx + 1
1733
+ }),
1734
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1735
+ style: {
1736
+ display: "flex",
1737
+ flexDirection: "column",
1738
+ gap: "0.15em",
1739
+ lineHeight: 1.2
1740
+ },
1741
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1742
+ style: {
1743
+ display: "flex",
1744
+ alignItems: "center"
1745
+ },
1746
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1747
+ style: {
1748
+ color: isUser ? DEVTOOL_STACK_FRAME_USER_FUNCTION : DEVTOOL_STACK_FRAME_INTERNAL_FUNCTION,
1749
+ fontSize: "12px",
1750
+ fontWeight: 500,
1751
+ overflow: "hidden",
1752
+ textOverflow: "ellipsis",
1753
+ whiteSpace: "nowrap"
1754
+ },
1755
+ children: frame.fn ?? "(anonymous)"
1756
+ })
1757
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1758
+ style: {
1759
+ display: "flex",
1760
+ alignItems: "baseline",
1761
+ overflow: "hidden"
1762
+ },
1763
+ children: [
1764
+ folderPrefix !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1765
+ style: {
1766
+ color: isUser ? "#596b83" : "#2d3f53",
1767
+ fontSize: "10px",
1768
+ overflow: "hidden",
1769
+ textOverflow: "ellipsis",
1770
+ whiteSpace: "nowrap",
1771
+ flexShrink: 1,
1772
+ minWidth: 0
1773
+ },
1774
+ children: folderPrefix
1775
+ }),
1776
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1777
+ style: {
1778
+ color: isUser ? DEVTOOL_STACK_FRAME_USER_FILE : DEVTOOL_STACK_FRAME_INTERNAL_FILE,
1779
+ fontSize: "10px",
1780
+ whiteSpace: "nowrap",
1781
+ flexShrink: 0
1782
+ },
1783
+ children: bareFilename
1784
+ }),
1785
+ frame.line != null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1786
+ style: {
1787
+ color: isUser ? "#4a7fa8" : "#2d4a63",
1788
+ fontSize: "10px",
1789
+ whiteSpace: "nowrap",
1790
+ flexShrink: 0,
1791
+ fontVariantNumeric: "tabular-nums"
1792
+ },
1793
+ children: [":", frame.line]
1794
+ })
1795
+ ]
1796
+ })]
1797
+ }),
1798
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OpenInVscodeButton, {
1799
+ frame,
1800
+ projectRoot,
1801
+ onNeedSetup: () => setShowSetupModal(true)
1802
+ })
1803
+ ]
1804
+ }, frame.raw);
1805
+ }), Array.from({ length: phantomFrameCount }, (_, i) => i).map((i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1806
+ "aria-hidden": "true",
1807
+ style: {
1808
+ visibility: "hidden",
1809
+ display: "flex",
1810
+ flexDirection: "row",
1811
+ gap: "0.5em",
1812
+ padding: "4px",
1813
+ borderBottom: `1px solid ${DEVTOOL_LIST_BASE_BACKGROUND}`
1814
+ },
1815
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1816
+ style: {
1817
+ fontSize: "10px",
1818
+ minWidth: "10px",
1819
+ flexShrink: 0
1820
+ },
1821
+ children: "0"
1822
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1823
+ style: {
1824
+ display: "flex",
1825
+ flexDirection: "column",
1826
+ gap: "0.15em",
1827
+ lineHeight: 1.2
1828
+ },
1829
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1830
+ style: { fontSize: "12px" },
1831
+ children: "placeholder"
1832
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1833
+ style: { fontSize: "10px" },
1834
+ children: "placeholder/file.ts:1"
1835
+ })]
1836
+ })]
1837
+ }, `phantom-${i}`))]
1838
+ }),
1839
+ showSetupModal && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProjectRootSetupModal, {
1840
+ envId: runtime.envId,
1841
+ currentValue: projectRoot ?? "",
1842
+ onSave: handleSaveProjectRoot,
1843
+ onCancel: () => setShowSetupModal(false)
1844
+ })
1845
+ ] });
1846
+ }
1847
+ //#endregion
1848
+ //#region src/devtools/browser/components/ActionErrorDisplay.tsx
1849
+ const MONO = "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace";
1850
+ const SANS = "ui-sans-serif, system-ui, -apple-system, sans-serif";
1851
+ function FullErrorContent({ label, error, errorStack, color, runtime }) {
1852
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [isNiceErrorJson(error) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NiceErrorDisplay, {
1853
+ error,
1854
+ label,
1855
+ color
1856
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailSection, {
1857
+ label,
1858
+ value: error,
1859
+ color
1860
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StackTraceSection, {
1861
+ runtime,
1862
+ label: "Error Stack",
1863
+ stack: errorStack,
1864
+ color
1865
+ })] });
1866
+ }
1867
+ function CompactErrorContent({ value, color }) {
1868
+ if (isNiceErrorJson(value)) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1869
+ style: {
1870
+ display: "flex",
1871
+ flexDirection: "column",
1872
+ gap: "1em",
1873
+ width: "100%",
1874
+ minWidth: 0
1875
+ },
1876
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1877
+ style: {
1878
+ color,
1879
+ fontFamily: SANS,
1880
+ fontSize: "12px",
1881
+ fontWeight: 600,
1882
+ lineHeight: "1.4",
1883
+ wordBreak: "break-word",
1884
+ overflowWrap: "anywhere",
1885
+ textAlign: "left"
1886
+ },
1887
+ children: value.message
1888
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1889
+ style: {
1890
+ display: "flex",
1891
+ flexWrap: "wrap",
1892
+ gap: "6px",
1893
+ alignItems: "center",
1894
+ minWidth: 0
1895
+ },
1896
+ children: [value.ids.map((id) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1897
+ style: {
1898
+ color,
1899
+ fontFamily: MONO,
1900
+ fontSize: "10px",
1901
+ opacity: .8,
1902
+ wordBreak: "break-word",
1903
+ overflowWrap: "anywhere"
1904
+ },
1905
+ children: id
1906
+ }, id)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1907
+ style: {
1908
+ color: DEVTOOL_COLOR_TEXT_MUTED,
1909
+ fontFamily: MONO,
1910
+ fontSize: "10px",
1911
+ wordBreak: "break-word",
1912
+ overflowWrap: "anywhere"
1913
+ },
1914
+ children: value.def.domain
1915
+ })]
1916
+ })]
1917
+ });
1918
+ const text = typeof value === "string" ? value : value != null ? String(value) : null;
1919
+ if (text == null) return null;
1920
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1921
+ style: {
1922
+ color,
1923
+ fontFamily: SANS,
1924
+ fontSize: "12px",
1925
+ lineHeight: "1.4",
1926
+ wordBreak: "break-word",
1927
+ overflowWrap: "anywhere"
1928
+ },
1929
+ children: text
1930
+ });
1931
+ }
1932
+ function ActionErrorDisplay({ entry, compact }) {
1933
+ const { status, error, abortReason, errorStack } = entry;
1934
+ if (status === "action-error") {
1935
+ const color = DEVTOOL_COLOR_SEMANTIC_ERROR;
1936
+ if (compact) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CompactErrorContent, {
1937
+ value: error,
1938
+ color
1939
+ });
1940
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FullErrorContent, {
1941
+ runtime: entry.meta.originClient,
1942
+ label: "Action Error",
1943
+ error,
1944
+ errorStack,
1945
+ color
1946
+ });
1947
+ }
1948
+ if (status === "failed") {
1949
+ const color = DEVTOOL_COLOR_SEMANTIC_ERROR;
1950
+ if (compact) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CompactErrorContent, {
1951
+ value: error,
1952
+ color
1953
+ });
1954
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FullErrorContent, {
1955
+ runtime: entry.meta.originClient,
1956
+ label: "Error",
1957
+ error,
1958
+ errorStack,
1959
+ color
1960
+ });
1961
+ }
1962
+ if (status === "aborted") {
1963
+ const color = DEVTOOL_COLOR_SEMANTIC_METADATA;
1964
+ if (compact) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CompactErrorContent, {
1965
+ value: abortReason,
1966
+ color
1967
+ });
1968
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [abortReason != null && (isNiceErrorJson(abortReason) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NiceErrorDisplay, {
1969
+ error: abortReason,
1970
+ label: "Abort Reason",
1971
+ color
1972
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailSection, {
1973
+ label: "Abort Reason",
1974
+ value: abortReason,
1975
+ color
1976
+ })), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StackTraceSection, {
1977
+ runtime: entry.meta.originClient,
1978
+ label: "Abort Stack",
1979
+ stack: errorStack,
1980
+ color
1981
+ })] });
1982
+ }
1983
+ return null;
1984
+ }
1985
+ //#endregion
1986
+ //#region src/devtools/browser/components/Chip.tsx
1987
+ const CHIP_SIZE_STYLES = {
1988
+ ["sm"]: {
1989
+ fontSize: "0.8em",
1990
+ padding: "1px 3px"
1991
+ },
1992
+ ["md"]: {
1993
+ fontSize: "0.9em",
1994
+ padding: "1px 4px"
1995
+ },
1996
+ ["lg"]: {
1997
+ fontSize: "1em",
1998
+ padding: "2px 6px"
1999
+ }
2000
+ };
2001
+ function Chip({ color, subtle = false, size = "md", rounded = false, tooltip, children }) {
2002
+ const [anchor, setAnchor] = (0, react.useState)(null);
2003
+ const hasTooltip = tooltip != null;
2004
+ const { fontSize, padding } = CHIP_SIZE_STYLES[size];
2005
+ const colorEntry = SEMANTIC_COLORS[color];
2006
+ const resolvedColor = subtle ? colorEntry.subtle?.color ?? colorEntry.color : colorEntry.color;
2007
+ const resolvedBorderColor = subtle ? colorEntry.subtle?.borderColor ?? "transparent" : colorEntry.borderColor;
2008
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2009
+ onMouseEnter: hasTooltip ? (e) => setAnchor(e.currentTarget.getBoundingClientRect()) : void 0,
2010
+ onMouseLeave: hasTooltip ? () => setAnchor(null) : void 0,
2011
+ style: {
2012
+ display: "flex",
2013
+ alignItems: "center",
2014
+ color: resolvedColor,
2015
+ fontSize,
2016
+ background: DEVTOOL_LIST_BASE_BACKGROUND,
2017
+ border: `1px solid ${resolvedBorderColor}`,
2018
+ padding,
2019
+ borderRadius: rounded ? "0.6rem" : "3px",
2020
+ flexShrink: 0,
2021
+ whiteSpace: "nowrap",
2022
+ cursor: hasTooltip ? "default" : void 0
2023
+ },
2024
+ children
2025
+ }), anchor != null && hasTooltip && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tooltip, {
2026
+ anchor,
2027
+ config: tooltip
2028
+ })] });
2029
+ }
2030
+ //#endregion
2031
+ //#region src/devtools/browser/components/Icon.tsx
2032
+ const BASE_ICON_SIDE_LENGTH_EM = 1.6;
2033
+ const ICON_GAP_EM = .3;
2034
+ function Icon({ icon: IconComponent, color, size = "md", subtle = false, tooltip, style, noBackground = false }) {
2035
+ const [anchor, setAnchor] = (0, react.useState)(null);
2036
+ const hasTooltip = tooltip != null;
2037
+ const colorEntry = SEMANTIC_COLORS[color];
2038
+ const resolvedColor = subtle ? colorEntry.subtle?.color ?? colorEntry.color : colorEntry.color;
2039
+ const finalIcons = Array.isArray(IconComponent) ? IconComponent : [IconComponent];
2040
+ const fullIconWidthEm = (finalIcons.length - 1) * ICON_GAP_EM + finalIcons.length * BASE_ICON_SIDE_LENGTH_EM * getSizeValue(size);
2041
+ const iconSideLengthEm = `${BASE_ICON_SIDE_LENGTH_EM * getSizeValue(size)}em`;
2042
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2043
+ onMouseEnter: hasTooltip ? (e) => setAnchor(e.currentTarget.getBoundingClientRect()) : void 0,
2044
+ onMouseLeave: hasTooltip ? () => setAnchor(null) : void 0,
2045
+ style: {
2046
+ borderRadius: "0.4em",
2047
+ width: `${fullIconWidthEm}em`,
2048
+ height: iconSideLengthEm,
2049
+ padding: "0.15em",
2050
+ display: "flex",
2051
+ alignItems: "center",
2052
+ justifyContent: "center",
2053
+ background: noBackground ? "none" : "rgba(0,0,0,0.4)",
2054
+ color: resolvedColor,
2055
+ flexShrink: 0,
2056
+ cursor: hasTooltip ? "default" : void 0,
2057
+ ...style
2058
+ },
2059
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2060
+ style: {
2061
+ display: "flex",
2062
+ alignItems: "center",
2063
+ gap: `${ICON_GAP_EM}em`
2064
+ },
2065
+ children: finalIcons.map((Comp, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Comp, {
2066
+ size: iconSideLengthEm,
2067
+ strokeWidth: 2.6
2068
+ }, `${Comp.name}-${i}`))
2069
+ })
2070
+ }), anchor != null && hasTooltip && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tooltip, {
2071
+ anchor,
2072
+ config: tooltip
2073
+ })] });
2074
+ }
2075
+ //#endregion
2076
+ //#region src/devtools/browser/components/DomainChip.tsx
2077
+ function DomainHierarchyContent({ allDomains }) {
2078
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: allDomains.map((d, i) => {
2079
+ const isCurrent = i === allDomains.length - 1;
2080
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2081
+ style: {
2082
+ display: "flex",
2083
+ alignItems: "center",
2084
+ gap: "0.3rem",
2085
+ paddingLeft: `${i * 10}px`,
2086
+ paddingTop: i > 0 ? "0.1rem" : void 0
2087
+ },
2088
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2089
+ style: {
2090
+ fontSize: "0.8rem",
2091
+ height: "0.6em",
2092
+ width: "0.6em",
2093
+ overflow: "hidden",
2094
+ color: isCurrent ? DEVTOOL_COLOR_SEMANTIC_SYSTEM : "#3730a3",
2095
+ display: "flex",
2096
+ alignItems: "center",
2097
+ justifyContent: "center"
2098
+ },
2099
+ children: "⬢"
2100
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2101
+ style: {
2102
+ color: isCurrent ? DEVTOOL_COLOR_SEMANTIC_SYSTEM : "#4b5563",
2103
+ fontSize: "0.7rem",
2104
+ fontWeight: isCurrent ? 500 : void 0
2105
+ },
2106
+ children: d
2107
+ })]
2108
+ }, d);
2109
+ }) });
2110
+ }
2111
+ function DomainChip({ compact = false, subtle = false, domain, allDomains, size }) {
2112
+ if (compact) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
2113
+ icon: lucide_react.Component,
2114
+ color: "domain",
2115
+ tooltip: {
2116
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DomainHierarchyContent, { allDomains }),
2117
+ title: "Action Domain",
2118
+ align: "edge"
2119
+ }
2120
+ });
2121
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
2122
+ color: "domain",
2123
+ subtle,
2124
+ size,
2125
+ rounded: true,
2126
+ tooltip: {
2127
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DomainHierarchyContent, { allDomains }),
2128
+ title: "Action Domain",
2129
+ align: "edge"
2130
+ },
2131
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2132
+ style: {
2133
+ display: "flex",
2134
+ alignItems: "center",
2135
+ gap: "0.4em"
2136
+ },
2137
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2138
+ style: {
2139
+ height: "1.2em",
2140
+ width: "1.2em",
2141
+ display: "flex",
2142
+ alignItems: "center",
2143
+ justifyContent: "center"
2144
+ },
2145
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Component, {
2146
+ height: "1.2em",
2147
+ width: "1.2em"
2148
+ })
2149
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: domain })]
2150
+ })
2151
+ });
2152
+ }
2153
+ //#endregion
2154
+ //#region src/devtools/browser/components/HandlerChips.tsx
2155
+ function getExternalLabel(hop) {
2156
+ if (hop.handlerType !== "external") return null;
2157
+ return hop.handlerClient != null ? `${hop.transport ?? "ext"} → ${hop.handlerClient.envId}` : `→ ${hop.transport ?? "ext"}`;
2158
+ }
2159
+ /**
2160
+ * Extended transport details (request/WebSocket endpoint) for the external handler — shown on hover
2161
+ * only, so the chip/label itself stays short.
2162
+ */
2163
+ function getTransportTooltip(hop) {
2164
+ return getTransportTooltipForHops([hop]);
2165
+ }
2166
+ /**
2167
+ * Aggregate transport tooltip for a chip/label that represents one or more external hops (e.g. a
2168
+ * grouped child-dispatch chip that accumulates several transports for the same runtime).
2169
+ */
2170
+ function getTransportTooltipForHops(hops) {
2171
+ const lines = hops.filter((hop) => hop.handlerType === "external").map((hop) => ({
2172
+ summary: hop.transportSummary,
2173
+ url: hop.transportUrl
2174
+ })).filter((line) => line.summary != null || line.url != null);
2175
+ if (lines.length === 0) return void 0;
2176
+ return {
2177
+ title: "Transport",
2178
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2179
+ style: {
2180
+ display: "flex",
2181
+ flexDirection: "column",
2182
+ gap: "6px",
2183
+ fontSize: "11px"
2184
+ },
2185
+ children: lines.map((line) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2186
+ style: {
2187
+ display: "flex",
2188
+ flexDirection: "column",
2189
+ gap: "2px"
2190
+ },
2191
+ children: [line.summary != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: line.summary }), line.url != null && line.url !== line.summary && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2192
+ style: {
2193
+ color: "#64748b",
2194
+ wordBreak: "break-all"
2195
+ },
2196
+ children: line.url
2197
+ })]
2198
+ }, `${line.summary ?? ""}:${line.url ?? ""}`))
2199
+ })
2200
+ };
2201
+ }
2202
+ function HandlerChips({ entry, size, subtle }) {
2203
+ const firstHop = entry.meta.routing[0];
2204
+ const localEnvId = firstHop != null ? firstHop.runtime.envId : null;
2205
+ const externalLabel = firstHop != null ? getExternalLabel(firstHop) : null;
2206
+ const firstHopIsLocal = firstHop != null && firstHop.handlerType === "local";
2207
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [localEnvId != null && (firstHopIsLocal || externalLabel == null) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
2208
+ color: "handler_local",
2209
+ size,
2210
+ subtle,
2211
+ children: localEnvId
2212
+ }), externalLabel != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
2213
+ color: "handler_external",
2214
+ size,
2215
+ subtle,
2216
+ tooltip: firstHop != null ? getTransportTooltip(firstHop) : void 0,
2217
+ children: externalLabel
2218
+ })] });
2219
+ }
2220
+ //#endregion
2221
+ //#region src/devtools/browser/components/utils.ts
2222
+ const STATUS_COLOR = {
2223
+ running: DEVTOOL_COLOR_SEMANTIC_SYSTEM,
2224
+ success: DEVTOOL_COLOR_SEMANTIC_SUCCESS,
2225
+ "action-error": DEVTOOL_COLOR_SEMANTIC_WARNING,
2226
+ failed: DEVTOOL_COLOR_SEMANTIC_ERROR,
2227
+ aborted: DEVTOOL_COLOR_SEMANTIC_METADATA
2228
+ };
2229
+ const STATUS_SYMBOL = {
2230
+ running: "●",
2231
+ success: "✓",
2232
+ "action-error": "!",
2233
+ failed: "✗",
2234
+ aborted: "○"
2235
+ };
2236
+ const STATUS_ICON = {
2237
+ running: lucide_react.Loader2,
2238
+ success: lucide_react.CircleCheck,
2239
+ "action-error": lucide_react.CircleAlert,
2240
+ failed: lucide_react.CircleX,
2241
+ aborted: lucide_react.Circle
2242
+ };
2243
+ /**
2244
+ * The runtime an action *originated* on, when that differs from the runtime that handled it — i.e. an
2245
+ * inbound action received over a transport (a backend push, or an action relayed from another client),
2246
+ * rather than one dispatched locally. Returns `null` for locally-originated actions (where the origin
2247
+ * matches the handling runtime) and for actions with no known origin.
2248
+ */
2249
+ function getInboundOrigin(entry) {
2250
+ const origin = entry.meta.originClient;
2251
+ if (origin == null || origin.envId === "unknown" || origin.envId === "_unset_") return null;
2252
+ const local = entry.meta.routing[0]?.runtime;
2253
+ if (local == null) return null;
2254
+ return origin.envId === local.envId && (origin.perId ?? null) === (local.perId ?? null) && (origin.insId ?? null) === (local.insId ?? null) ? null : origin;
2255
+ }
2256
+ function formatDuration(entry) {
2257
+ return entry.endTime != null ? `${entry.endTime - entry.startTime}ms` : null;
2258
+ }
2259
+ //#endregion
2260
+ //#region src/devtools/browser/components/RunningTimer.tsx
2261
+ function RunningTimer({ startTime }) {
2262
+ const [elapsed, setElapsed] = (0, react.useState)(() => Date.now() - startTime);
2263
+ (0, react.useEffect)(() => {
2264
+ const interval = setInterval(() => setElapsed(Date.now() - startTime), 100);
2265
+ return () => clearInterval(interval);
2266
+ }, [startTime]);
2267
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [elapsed, "ms"] });
2268
+ }
2269
+ function DurationDisplay({ entry }) {
2270
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: formatDuration(entry) ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RunningTimer, { startTime: entry.startTime }) });
2271
+ }
2272
+ //#endregion
2273
+ //#region src/devtools/browser/components/CallStackSection.tsx
2274
+ function getCalledLabel(entry) {
2275
+ const firstHop = entry.meta.routing[0];
2276
+ if (firstHop == null) return "↳ call";
2277
+ if (firstHop.handlerType === "local") return "↳ local";
2278
+ const label = getExternalLabel(firstHop);
2279
+ return label != null ? `↳ ${label}` : "↳ call";
2280
+ }
2281
+ function getCalledColor(entry) {
2282
+ const firstHop = entry.meta.routing[0];
2283
+ if (firstHop == null) return DEVTOOL_COLOR_SEMANTIC_SYSTEM;
2284
+ if (firstHop.handlerType === "local") return DEVTOOL_COLOR_HANDLER_LOCAL_TEXT;
2285
+ return DEVTOOL_COLOR_HANDLER_EXTERNAL_TEXT;
2286
+ }
2287
+ function CallStackLink({ entry, entryRole, isFocused, onClick }) {
2288
+ const color = STATUS_COLOR[entry.status];
2289
+ const symbol = STATUS_SYMBOL[entry.status];
2290
+ const labelColor = entryRole === "caller" ? DEVTOOL_COLOR_TEXT_SECONDARY : getCalledColor(entry);
2291
+ const label = entryRole === "caller" ? "↑ from" : getCalledLabel(entry);
2292
+ const firstHop = entry.meta.routing[0];
2293
+ const transportTooltip = entryRole === "called" && firstHop != null ? getTransportTooltip(firstHop) : void 0;
2294
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2295
+ onClick,
2296
+ style: {
2297
+ background: isFocused ? DEVTOOL_SECTION_BACKGROUND : DEVTOOL_DETAIL_HEADER_BACKGROUND,
2298
+ borderBottom: `1px solid ${DEVTOOL_COLOR_CALL_STACK_DIVIDER}`,
2299
+ borderLeft: isFocused ? `2px solid ${DEVTOOL_COLOR_SEMANTIC_SYSTEM}` : "2px solid transparent",
2300
+ padding: "5px 12px 5px 10px",
2301
+ display: "grid",
2302
+ gridTemplateColumns: "1fr 1fr",
2303
+ alignItems: "center",
2304
+ columnGap: "6px",
2305
+ cursor: "pointer"
2306
+ },
2307
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2308
+ style: {
2309
+ display: "flex",
2310
+ alignItems: "center",
2311
+ gap: "1em",
2312
+ flexShrink: 0
2313
+ },
2314
+ children: [
2315
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2316
+ style: {
2317
+ color,
2318
+ fontSize: "10px",
2319
+ flexShrink: 0
2320
+ },
2321
+ children: symbol
2322
+ }),
2323
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HoverTooltip, {
2324
+ config: transportTooltip,
2325
+ style: {
2326
+ flexShrink: 0,
2327
+ display: "flex"
2328
+ },
2329
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2330
+ style: {
2331
+ color: labelColor,
2332
+ fontSize: "9px"
2333
+ },
2334
+ children: label
2335
+ })
2336
+ }),
2337
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2338
+ style: {
2339
+ display: "flex",
2340
+ alignItems: "center",
2341
+ gap: "5px"
2342
+ },
2343
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2344
+ style: {
2345
+ color: DEVTOOL_COLOR_TEXT_SECONDARY,
2346
+ fontSize: "11px",
2347
+ overflow: "hidden",
2348
+ textOverflow: "ellipsis",
2349
+ whiteSpace: "nowrap"
2350
+ },
2351
+ children: entry.actionId
2352
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DomainChip, {
2353
+ domain: entry.domain,
2354
+ allDomains: entry.allDomains,
2355
+ size: "sm"
2356
+ })]
2357
+ })
2358
+ ]
2359
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2360
+ style: {
2361
+ color: DEVTOOL_COLOR_TEXT_FAINT,
2362
+ fontSize: "10px",
2363
+ textAlign: "right",
2364
+ whiteSpace: "nowrap"
2365
+ },
2366
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DurationDisplay, { entry })
2367
+ })]
2368
+ });
2369
+ }
2370
+ function CallStackSection({ parent, childEntries, focusedChildCuid, onFocusChild, onSelectParent }) {
2371
+ if (parent == null && childEntries.length === 0) return null;
2372
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [parent != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CallStackLink, {
2373
+ entry: parent,
2374
+ entryRole: "caller",
2375
+ isFocused: false,
2376
+ onClick: () => onSelectParent(parent.cuid)
2377
+ }), childEntries.map((child) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CallStackLink, {
2378
+ entry: child,
2379
+ entryRole: "called",
2380
+ isFocused: focusedChildCuid === child.cuid,
2381
+ onClick: () => onFocusChild(child.cuid)
2382
+ }, child.cuid))] });
2383
+ }
2384
+ //#endregion
2385
+ //#region src/devtools/browser/components/ChildDispatchChips.tsx
2386
+ function ChildDispatchChips({ childRouteItems, size = "sm" }) {
2387
+ if (childRouteItems == null || childRouteItems.length === 0) return null;
2388
+ const groups = /* @__PURE__ */ new Map();
2389
+ for (const item of childRouteItems) {
2390
+ const runtimeId = item.handlerClient?.envId ?? item.transport ?? "ext";
2391
+ const hasClient = item.handlerClient != null;
2392
+ if (!groups.has(runtimeId)) groups.set(runtimeId, {
2393
+ runtimeId,
2394
+ transports: [],
2395
+ hasClient,
2396
+ hops: []
2397
+ });
2398
+ const group = groups.get(runtimeId);
2399
+ group.hops.push(item);
2400
+ const transport = item.transport;
2401
+ if (transport != null && !group.transports.includes(transport)) group.transports.push(transport);
2402
+ }
2403
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2404
+ style: {
2405
+ display: "flex",
2406
+ alignItems: "center",
2407
+ gap: "4px",
2408
+ flexWrap: "wrap"
2409
+ },
2410
+ children: Array.from(groups.values()).map((group) => {
2411
+ const transportStr = group.transports.length > 0 ? group.transports.join(", ") : "ext";
2412
+ const label = group.hasClient ? `${transportStr} → ${group.runtimeId}` : `→ ${transportStr}`;
2413
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
2414
+ color: "handler_external",
2415
+ size,
2416
+ tooltip: getTransportTooltipForHops(group.hops),
2417
+ children: label
2418
+ }, group.runtimeId);
2419
+ })
2420
+ });
2421
+ }
2422
+ //#endregion
2423
+ //#region src/devtools/browser/components/MetaSection.tsx
2424
+ function MetaChip({ label, value }) {
2425
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2426
+ style: { whiteSpace: "nowrap" },
2427
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2428
+ style: { color: DEVTOOL_COLOR_TEXT_MUTED },
2429
+ children: [label, " "]
2430
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2431
+ style: { color: DEVTOOL_COLOR_TEXT_SECONDARY },
2432
+ children: value
2433
+ })]
2434
+ });
2435
+ }
2436
+ function MetaSection({ entry }) {
2437
+ const [expanded, setExpanded] = (0, react.useState)(false);
2438
+ const { meta, cuid } = entry;
2439
+ const expandedRows = [
2440
+ {
2441
+ label: "cuid",
2442
+ value: cuid
2443
+ },
2444
+ {
2445
+ label: "runtime",
2446
+ value: meta.originClient.envId
2447
+ },
2448
+ ...meta.originClient.perId != null ? [{
2449
+ label: "perId",
2450
+ value: meta.originClient.perId
2451
+ }] : [],
2452
+ ...meta.originClient.insId != null ? [{
2453
+ label: "insId",
2454
+ value: meta.originClient.insId
2455
+ }] : []
2456
+ ];
2457
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2458
+ style: {
2459
+ display: "flex",
2460
+ alignItems: "center",
2461
+ justifyContent: "space-between",
2462
+ marginBottom: "3px"
2463
+ },
2464
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2465
+ style: {
2466
+ color: DEVTOOL_COLOR_SEMANTIC_SYSTEM,
2467
+ fontSize: "10px",
2468
+ textTransform: "uppercase",
2469
+ letterSpacing: "0.05em"
2470
+ },
2471
+ children: "Meta"
2472
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2473
+ style: {
2474
+ color: DEVTOOL_COLOR_TEXT_FAINT,
2475
+ fontSize: "11px"
2476
+ },
2477
+ children: expanded ? "▾" : "▸"
2478
+ })]
2479
+ }), expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2480
+ onClick: () => setExpanded(false),
2481
+ style: {
2482
+ background: DEVTOOL_SECTION_BACKGROUND,
2483
+ borderRadius: "4px",
2484
+ overflow: "hidden",
2485
+ cursor: "pointer"
2486
+ },
2487
+ children: expandedRows.map(({ label, value }, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2488
+ style: {
2489
+ display: "grid",
2490
+ gridTemplateColumns: "52px 1fr",
2491
+ columnGap: "8px",
2492
+ padding: "4px 8px",
2493
+ borderBottom: i < expandedRows.length - 1 ? `1px solid ${DEVTOOL_LIST_BASE_BACKGROUND}` : "none",
2494
+ alignItems: "start"
2495
+ },
2496
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2497
+ style: {
2498
+ textAlign: "left",
2499
+ color: DEVTOOL_COLOR_TEXT_MUTED,
2500
+ fontSize: "10px",
2501
+ paddingTop: "1px"
2502
+ },
2503
+ children: label
2504
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2505
+ style: {
2506
+ textAlign: "left",
2507
+ color: DEVTOOL_COLOR_TEXT_SECONDARY,
2508
+ fontSize: "11px",
2509
+ wordBreak: "break-all"
2510
+ },
2511
+ children: value
2512
+ })]
2513
+ }, label))
2514
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2515
+ onClick: () => setExpanded(true),
2516
+ style: {
2517
+ background: DEVTOOL_SECTION_BACKGROUND,
2518
+ borderRadius: "4px",
2519
+ padding: "5px 8px",
2520
+ fontSize: "11px",
2521
+ display: "flex",
2522
+ gap: "10px",
2523
+ rowGap: "2px",
2524
+ flexWrap: "wrap",
2525
+ cursor: "pointer"
2526
+ },
2527
+ children: [
2528
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MetaChip, {
2529
+ label: "cuid",
2530
+ value: `…${cuid.slice(-8)}`
2531
+ }),
2532
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MetaChip, {
2533
+ label: "runtime",
2534
+ value: meta.originClient.envId
2535
+ }),
2536
+ meta.originClient.perId != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MetaChip, {
2537
+ label: "perId",
2538
+ value: meta.originClient.perId.length > 10 ? `${meta.originClient.perId.slice(0, 10)}…` : meta.originClient.perId
2539
+ }),
2540
+ meta.originClient.insId != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MetaChip, {
2541
+ label: "insId",
2542
+ value: meta.originClient.insId.length > 10 ? `${meta.originClient.insId.slice(0, 10)}…` : meta.originClient.insId
2543
+ })
2544
+ ]
2545
+ })] });
2546
+ }
2547
+ //#endregion
2548
+ //#region src/devtools/browser/components/OriginChip.tsx
2549
+ /**
2550
+ * Marks an action that was received from another runtime (a backend push, or an action relayed from
2551
+ * another client) rather than dispatched locally. The chip shows the origin's `envId`; the tooltip
2552
+ * carries the full coordinate. Render it only when {@link getInboundOrigin} returns a value.
2553
+ */
2554
+ function OriginChip({ origin, size = "md", subtle }) {
2555
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
2556
+ color: "origin",
2557
+ size,
2558
+ subtle,
2559
+ tooltip: {
2560
+ title: "Received from",
2561
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2562
+ style: {
2563
+ display: "flex",
2564
+ flexDirection: "column",
2565
+ gap: "2px",
2566
+ fontSize: "11px"
2567
+ },
2568
+ children: [
2569
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["env ", origin.envId] }),
2570
+ origin.perId != null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["perId ", origin.perId] }),
2571
+ origin.insId != null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["insId ", origin.insId] })
2572
+ ]
2573
+ })
2574
+ },
2575
+ children: `⇠ ${origin.envId}`
2576
+ });
2577
+ }
2578
+ //#endregion
2579
+ //#region src/devtools/browser/components/RoutingSection.tsx
2580
+ function RoutingSection({ entry, minHopCount = 0 }) {
2581
+ const { meta, startTime } = entry;
2582
+ const hopCount = meta.routing.length;
2583
+ const phantomCount = Math.max(0, minHopCount - hopCount);
2584
+ if (hopCount === 0 && phantomCount === 0) return null;
2585
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SectionLabel, { label: hopCount > 0 ? `Routing · ${hopCount} hop${hopCount !== 1 ? "s" : ""}` : "Routing" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2586
+ style: {
2587
+ display: "flex",
2588
+ flexDirection: "column",
2589
+ gap: "2px"
2590
+ },
2591
+ children: [meta.routing.map((hop, i) => {
2592
+ const isLocal = hop.handlerType === "local";
2593
+ const isLast = i === hopCount - 1 && phantomCount === 0;
2594
+ const badgeColor = isLocal ? DEVTOOL_COLOR_SEMANTIC_SUCCESS : DEVTOOL_COLOR_SEMANTIC_WARNING;
2595
+ const badgeText = isLocal ? "● exec" : `→ ${hop.transportSummary ?? hop.transport ?? "ext"}`;
2596
+ const transportTooltip = isLocal ? void 0 : getTransportTooltip(hop);
2597
+ const runtimeTitle = [hop.runtime.perId, hop.runtime.insId].filter(Boolean).join(" · ") || void 0;
2598
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2599
+ style: {
2600
+ background: DEVTOOL_SECTION_BACKGROUND,
2601
+ borderRadius: "4px",
2602
+ overflow: "hidden",
2603
+ display: "grid",
2604
+ gridTemplateColumns: "30% 1fr 30%",
2605
+ alignItems: "center",
2606
+ columnGap: "8px",
2607
+ borderBottom: isLast ? void 0 : `1px solid ${DEVTOOL_LIST_BASE_BACKGROUND}`,
2608
+ padding: "4px 8px"
2609
+ },
2610
+ children: [
2611
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2612
+ style: {
2613
+ display: "flex",
2614
+ flexDirection: "row",
2615
+ alignItems: "center",
2616
+ gap: "10px"
2617
+ },
2618
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2619
+ style: {
2620
+ color: DEVTOOL_COLOR_TEXT_FAINT,
2621
+ fontSize: "10px",
2622
+ width: "16px",
2623
+ textAlign: "right"
2624
+ },
2625
+ children: i + 1
2626
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2627
+ title: runtimeTitle,
2628
+ style: {
2629
+ color: DEVTOOL_COLOR_TEXT_SECONDARY,
2630
+ fontSize: "11px",
2631
+ overflow: "hidden",
2632
+ textOverflow: "ellipsis",
2633
+ whiteSpace: "nowrap",
2634
+ textAlign: "center"
2635
+ },
2636
+ children: hop.runtime.envId
2637
+ })]
2638
+ }),
2639
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HoverTooltip, {
2640
+ config: transportTooltip,
2641
+ style: {
2642
+ display: "block",
2643
+ minWidth: 0,
2644
+ overflow: "hidden"
2645
+ },
2646
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2647
+ style: {
2648
+ color: badgeColor,
2649
+ fontSize: "10px",
2650
+ whiteSpace: "nowrap",
2651
+ overflow: "hidden",
2652
+ textOverflow: "ellipsis",
2653
+ display: "block"
2654
+ },
2655
+ children: badgeText
2656
+ })
2657
+ }),
2658
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2659
+ style: {
2660
+ display: "flex",
2661
+ alignItems: "center",
2662
+ justifyContent: "space-between",
2663
+ paddingRight: "8px",
2664
+ overflow: "hidden"
2665
+ },
2666
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2667
+ style: {
2668
+ color: DEVTOOL_COLOR_TEXT_MUTED,
2669
+ fontSize: "10px",
2670
+ whiteSpace: "nowrap",
2671
+ overflow: "hidden",
2672
+ textOverflow: "ellipsis"
2673
+ },
2674
+ children: hop.handlerClient != null ? `↳ ${hop.handlerClient.envId}` : ""
2675
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2676
+ style: {
2677
+ color: DEVTOOL_COLOR_TEXT_FAINT,
2678
+ fontSize: "10px",
2679
+ flexShrink: 0,
2680
+ marginLeft: "auto"
2681
+ },
2682
+ children: [
2683
+ "+",
2684
+ hop.time - startTime,
2685
+ "ms"
2686
+ ]
2687
+ })]
2688
+ })
2689
+ ]
2690
+ }, `${hop.time}-${hop.runtime.envId}`);
2691
+ }), Array.from({ length: phantomCount }, (_, i) => `routing-phantom-${hopCount + i}`).map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2692
+ "aria-hidden": "true",
2693
+ style: {
2694
+ visibility: "hidden",
2695
+ background: DEVTOOL_SECTION_BACKGROUND,
2696
+ borderRadius: "4px",
2697
+ display: "grid",
2698
+ gridTemplateColumns: "30% 1fr 30%",
2699
+ alignItems: "center",
2700
+ columnGap: "8px",
2701
+ padding: "4px 8px"
2702
+ },
2703
+ children: [
2704
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2705
+ style: {
2706
+ display: "flex",
2707
+ flexDirection: "row",
2708
+ alignItems: "center",
2709
+ gap: "10px"
2710
+ },
2711
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2712
+ style: {
2713
+ fontSize: "10px",
2714
+ width: "16px"
2715
+ },
2716
+ children: "0"
2717
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2718
+ style: { fontSize: "11px" },
2719
+ children: "placeholder"
2720
+ })]
2721
+ }),
2722
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2723
+ style: { fontSize: "10px" },
2724
+ children: "placeholder"
2725
+ }),
2726
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {})
2727
+ ]
2728
+ }, key))]
2729
+ })] });
2730
+ }
2731
+ //#endregion
2732
+ //#region src/devtools/browser/components/action_detail/ActionDetailPanel.tsx
2733
+ const STATUS_BADGE_LABEL = {
2734
+ running: "RUNNING",
2735
+ success: "SUCCESS",
2736
+ "action-error": "ERROR",
2737
+ failed: "FAILED",
2738
+ aborted: "ABORTED"
2739
+ };
2740
+ const STATUS_BADGE_BG = {
2741
+ running: STATUS_COLOR.running,
2742
+ success: STATUS_COLOR.success,
2743
+ "action-error": DEVTOOL_COLOR_SEMANTIC_ERROR,
2744
+ failed: DEVTOOL_COLOR_SEMANTIC_ERROR,
2745
+ aborted: DEVTOOL_COLOR_SEMANTIC_METADATA
2746
+ };
2747
+ const STATUS_BADGE_TEXT_COLOR = {
2748
+ running: "#0f172a",
2749
+ success: "#0f172a",
2750
+ "action-error": "#ffffff",
2751
+ failed: "#ffffff",
2752
+ aborted: "#0f172a"
2753
+ };
2754
+ function DetailHeader({ entry, isActive, onClick, childExternalRouteItems }) {
2755
+ const color = STATUS_COLOR[entry.status];
2756
+ const StatusIconComponent = STATUS_ICON[entry.status];
2757
+ const timestamp = formatTimestamp(entry.startTime);
2758
+ const inboundOrigin = getInboundOrigin(entry);
2759
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2760
+ onClick: !isActive ? onClick : void 0,
2761
+ style: {
2762
+ padding: "0.5em 1em",
2763
+ background: isActive ? DEVTOOL_SECTION_BACKGROUND : DEVTOOL_DETAIL_HEADER_BACKGROUND,
2764
+ borderBottom: `1px solid ${DEVTOOL_LIST_BASE_BACKGROUND}`,
2765
+ borderLeft: isActive ? `2px solid ${color}` : "2px solid transparent",
2766
+ flexShrink: 0,
2767
+ flexDirection: "row",
2768
+ display: "flex",
2769
+ alignItems: "flex-start",
2770
+ gap: "1em",
2771
+ cursor: isActive ? "default" : "pointer"
2772
+ },
2773
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2774
+ style: {
2775
+ flex: 1,
2776
+ minWidth: 0,
2777
+ display: "flex",
2778
+ flexDirection: "column",
2779
+ gap: "0.5em"
2780
+ },
2781
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2782
+ style: {
2783
+ display: "flex",
2784
+ alignItems: "center",
2785
+ minWidth: 0,
2786
+ gap: "0.5em"
2787
+ },
2788
+ children: [
2789
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2790
+ style: {
2791
+ color,
2792
+ flexShrink: 0,
2793
+ display: "flex",
2794
+ alignItems: "center",
2795
+ animation: entry.status === "running" ? "__nice-action-pulse 1.2s ease-in-out infinite" : void 0
2796
+ },
2797
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusIconComponent, {
2798
+ size: 20,
2799
+ strokeWidth: 1.75
2800
+ })
2801
+ }),
2802
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2803
+ style: {
2804
+ color: DEVTOOL_COLOR_TEXT_EMPHASIS,
2805
+ fontSize: "1.2em",
2806
+ fontWeight: "600",
2807
+ fontFamily: "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace",
2808
+ overflow: "hidden",
2809
+ textOverflow: "ellipsis",
2810
+ whiteSpace: "nowrap",
2811
+ flexShrink: 1,
2812
+ minWidth: "2.5em"
2813
+ },
2814
+ children: entry.actionId
2815
+ }),
2816
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2817
+ style: {
2818
+ flexShrink: 0,
2819
+ padding: "2px 9px",
2820
+ borderRadius: "999px",
2821
+ background: STATUS_BADGE_BG[entry.status],
2822
+ color: STATUS_BADGE_TEXT_COLOR[entry.status],
2823
+ fontSize: "9px",
2824
+ fontWeight: "700",
2825
+ letterSpacing: "0.1em",
2826
+ textTransform: "uppercase",
2827
+ fontFamily: "ui-sans-serif, system-ui, sans-serif"
2828
+ },
2829
+ children: STATUS_BADGE_LABEL[entry.status]
2830
+ }),
2831
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DomainChip, {
2832
+ domain: entry.domain,
2833
+ allDomains: entry.allDomains,
2834
+ size: "md"
2835
+ })
2836
+ ]
2837
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2838
+ style: {
2839
+ display: "flex",
2840
+ alignItems: "center",
2841
+ justifyContent: "space-between",
2842
+ gap: "8px"
2843
+ },
2844
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2845
+ style: {
2846
+ display: "flex",
2847
+ alignItems: "center",
2848
+ gap: "6px",
2849
+ minWidth: 0,
2850
+ overflow: "hidden"
2851
+ },
2852
+ children: [
2853
+ inboundOrigin != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OriginChip, {
2854
+ origin: inboundOrigin,
2855
+ size: "md"
2856
+ }),
2857
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HandlerChips, {
2858
+ entry,
2859
+ size: "md"
2860
+ }),
2861
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChildDispatchChips, {
2862
+ childRouteItems: childExternalRouteItems,
2863
+ size: "md"
2864
+ })
2865
+ ]
2866
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2867
+ style: {
2868
+ display: "flex",
2869
+ alignItems: "center",
2870
+ gap: "8px",
2871
+ flexShrink: 0
2872
+ },
2873
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2874
+ style: {
2875
+ color: DEVTOOL_COLOR_SEMANTIC_METADATA,
2876
+ fontSize: "10px",
2877
+ letterSpacing: "0.02em",
2878
+ fontFamily: "ui-sans-serif, system-ui, sans-serif"
2879
+ },
2880
+ children: timestamp
2881
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2882
+ style: {
2883
+ color,
2884
+ fontSize: "12px",
2885
+ fontWeight: "500"
2886
+ },
2887
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DurationDisplay, { entry })
2888
+ })]
2889
+ })]
2890
+ })]
2891
+ })
2892
+ });
2893
+ }
2894
+ function ActionDetailPanel({ entry, parent, childEntries, onSelectEntry }) {
2895
+ const [focusedChildCuid, setFocusedChildCuid] = (0, react.useState)(null);
2896
+ const focusedEntry = focusedChildCuid != null ? childEntries.find((e) => e.cuid === focusedChildCuid) ?? entry : entry;
2897
+ const maxRoutingHops = Math.max(entry.meta.routing.length, ...childEntries.map((e) => e.meta.routing.length), 0);
2898
+ const maxCallSiteFrames = Math.max(countUserFrames(entry.meta.originClient, entry.callSite), ...childEntries.map((e) => countUserFrames(e.meta.originClient, e.callSite)), 0);
2899
+ const childExternalRouteItems = (0, react.useMemo)(() => {
2900
+ const seen = /* @__PURE__ */ new Set();
2901
+ const result = [];
2902
+ for (const child of childEntries) {
2903
+ const firstHop = child.meta.routing[0];
2904
+ if (firstHop == null || firstHop.handlerType !== "external") continue;
2905
+ const key = `${firstHop.handlerClient?.envId ?? ""}:${firstHop.transport ?? ""}`;
2906
+ if (seen.has(key)) continue;
2907
+ seen.add(key);
2908
+ result.push(firstHop);
2909
+ }
2910
+ return result;
2911
+ }, [childEntries]);
2912
+ const handleFocusChild = (cuid) => {
2913
+ setFocusedChildCuid((prev) => prev === cuid ? null : cuid);
2914
+ };
2915
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2916
+ style: {
2917
+ flex: 1,
2918
+ display: "flex",
2919
+ flexDirection: "column",
2920
+ overflow: "hidden",
2921
+ minHeight: 0,
2922
+ background: DEVTOOL_DETAIL_BASE_BACKGROUND
2923
+ },
2924
+ children: [
2925
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailHeader, {
2926
+ entry,
2927
+ isActive: focusedChildCuid === null,
2928
+ onClick: () => setFocusedChildCuid(null),
2929
+ childExternalRouteItems
2930
+ }),
2931
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CallStackSection, {
2932
+ parent,
2933
+ childEntries,
2934
+ focusedChildCuid,
2935
+ onFocusChild: handleFocusChild,
2936
+ onSelectParent: onSelectEntry
2937
+ }),
2938
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2939
+ style: {
2940
+ flex: 1,
2941
+ overflowY: "auto",
2942
+ minHeight: 0,
2943
+ padding: "10px 12px",
2944
+ display: "flex",
2945
+ flexDirection: "column",
2946
+ gap: "8px"
2947
+ },
2948
+ children: [
2949
+ focusedEntry.input !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailSection, {
2950
+ label: "Input",
2951
+ value: focusedEntry.input
2952
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailSection, {
2953
+ label: "Input",
2954
+ value: "No input required or given"
2955
+ }),
2956
+ focusedEntry.status === "success" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailSection, {
2957
+ label: "Output",
2958
+ value: focusedEntry.output,
2959
+ color: "#A3E635"
2960
+ }),
2961
+ (focusedEntry.status === "action-error" || focusedEntry.status === "failed" || focusedEntry.status === "aborted") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionErrorDisplay, { entry: focusedEntry }),
2962
+ focusedEntry.progressUpdates.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DetailSection, {
2963
+ label: `Progress (${focusedEntry.progressUpdates.length})`,
2964
+ value: focusedEntry.progressUpdates
2965
+ }),
2966
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StackTraceSection, {
2967
+ runtime: entry.meta.originClient,
2968
+ label: "Dispatch Site",
2969
+ stack: focusedEntry.callSite,
2970
+ color: DEVTOOL_COLOR_TEXT_SECONDARY,
2971
+ minFrameCount: maxCallSiteFrames
2972
+ }),
2973
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RoutingSection, {
2974
+ entry: focusedEntry,
2975
+ minHopCount: maxRoutingHops
2976
+ }),
2977
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MetaSection, { entry: focusedEntry })
2978
+ ]
2979
+ })
2980
+ ]
2981
+ });
2982
+ }
2983
+ //#endregion
2984
+ //#region src/devtools/browser/components/action_list/IoTooltipContent.tsx
2985
+ function stripOuterBraces(json) {
2986
+ const lines = json.split("\n");
2987
+ if (lines.length > 2 && lines[0] === "{" && lines[lines.length - 1] === "}") return lines.slice(1, -1).map((l) => l.slice(2)).join("\n");
2988
+ return json;
2989
+ }
2990
+ function IoTooltipContent({ value }) {
2991
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2992
+ style: {
2993
+ fontFamily: "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace",
2994
+ fontSize: "10px",
2995
+ whiteSpace: "pre-wrap",
2996
+ wordBreak: "break-all",
2997
+ lineHeight: "1.5",
2998
+ textAlign: "left"
2999
+ },
3000
+ children: renderColoredJson(stripOuterBraces(safeStringify(value, 2)))
3001
+ });
3002
+ }
3003
+ //#endregion
3004
+ //#region src/devtools/browser/components/action_list/ActionInputAndOutputChip.tsx
3005
+ const ActionInputAndOutputChip = ({ entry, breakReasons, subtle, size = "md" }) => {
3006
+ const firstHop = entry.meta.routing[0];
3007
+ const localEnvId = firstHop != null ? firstHop.runtime.envId : null;
3008
+ const externalLabel = firstHop != null ? getExternalLabel(firstHop) : null;
3009
+ const firstHopIsLocal = firstHop != null && firstHop.handlerType === "local";
3010
+ const isLocal = localEnvId != null && (firstHopIsLocal || externalLabel == null);
3011
+ const color = isLocal ? "handler_local" : "handler_external";
3012
+ const transportTooltip = firstHop != null ? getTransportTooltip(firstHop) : void 0;
3013
+ const [labelAnchor, setLabelAnchor] = (0, react.useState)(null);
3014
+ const isNewInput = breakReasons.includes("new_input");
3015
+ const isNewOutput = breakReasons.includes("new_output");
3016
+ const hasError = entry.error != null || entry.abortReason != null;
3017
+ const newIconSizeEm = `${getSizeValue(size) * .9}em`;
3018
+ const label = `${(isLocal ? localEnvId : externalLabel) ?? "unknown"}`;
3019
+ const newSparkleComp = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3020
+ style: {
3021
+ opacity: .9,
3022
+ alignSelf: "start",
3023
+ marginLeft: "-0.6em"
3024
+ },
3025
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Sparkle, {
3026
+ strokeWidth: "0.2em",
3027
+ color: "rgba(243, 250, 140, 1)",
3028
+ width: newIconSizeEm,
3029
+ height: newIconSizeEm
3030
+ })
3031
+ });
3032
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
3033
+ color,
3034
+ size,
3035
+ subtle,
3036
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3037
+ style: {
3038
+ display: "flex",
3039
+ alignItems: "center",
3040
+ gap: "0.4em"
3041
+ },
3042
+ children: [
3043
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
3044
+ noBackground: true,
3045
+ icon: lucide_react.Variable,
3046
+ color,
3047
+ subtle: subtle || entry.input === void 0,
3048
+ tooltip: {
3049
+ content: entry.input !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IoTooltipContent, { value: entry.input }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3050
+ style: {
3051
+ color: DEVTOOL_COLOR_TEXT_MUTED,
3052
+ fontSize: "10px"
3053
+ },
3054
+ children: "No input required or given"
3055
+ }),
3056
+ title: isNewInput ? "New Input" : "Input"
3057
+ }
3058
+ }),
3059
+ isNewInput && newSparkleComp,
3060
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3061
+ style: { opacity: .6 },
3062
+ color,
3063
+ children: "→"
3064
+ }),
3065
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3066
+ title: transportTooltip == null ? label : void 0,
3067
+ style: {
3068
+ opacity: .8,
3069
+ cursor: transportTooltip != null ? "default" : void 0,
3070
+ overflow: "hidden",
3071
+ textOverflow: "ellipsis",
3072
+ whiteSpace: "nowrap",
3073
+ maxWidth: "14ch"
3074
+ },
3075
+ onMouseEnter: transportTooltip != null ? (e) => setLabelAnchor(e.currentTarget.getBoundingClientRect()) : void 0,
3076
+ onMouseLeave: transportTooltip != null ? () => setLabelAnchor(null) : void 0,
3077
+ children: label
3078
+ }),
3079
+ labelAnchor != null && transportTooltip != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tooltip, {
3080
+ anchor: labelAnchor,
3081
+ config: transportTooltip
3082
+ }),
3083
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3084
+ style: { opacity: .6 },
3085
+ color,
3086
+ children: "→"
3087
+ }),
3088
+ entry.status === "success" && entry.output !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
3089
+ noBackground: true,
3090
+ icon: lucide_react.PackageCheck,
3091
+ color: !hasError ? color : "default",
3092
+ tooltip: {
3093
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IoTooltipContent, { value: entry.output }),
3094
+ title: isNewOutput ? "New Output" : "Output"
3095
+ }
3096
+ }),
3097
+ (entry.error != null || entry.abortReason != null) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
3098
+ noBackground: true,
3099
+ icon: lucide_react.CircleX,
3100
+ color: entry.status === "aborted" ? "aborted" : "error",
3101
+ tooltip: {
3102
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionErrorDisplay, {
3103
+ entry,
3104
+ compact: true
3105
+ }),
3106
+ title: entry.status === "aborted" ? "Aborted" : "Error",
3107
+ maxWidth: 340
3108
+ }
3109
+ }),
3110
+ isNewOutput && newSparkleComp
3111
+ ]
3112
+ })
3113
+ });
3114
+ };
3115
+ //#endregion
3116
+ //#region src/devtools/browser/components/action_list/ActionEntryRow.tsx
3117
+ const MAX_GROUP_DOTS = 5;
3118
+ function getLatestChipColor(status) {
3119
+ if (status === "failed") return "failed";
3120
+ if (status === "action-error") return "action_error";
3121
+ if (status === "aborted") return "aborted";
3122
+ return "success";
3123
+ }
3124
+ function GroupDotTooltip({ entry, index, total, refTime, dotColor, anchor }) {
3125
+ const symbol = STATUS_SYMBOL[entry.status];
3126
+ const deltaMs = refTime - entry.startTime;
3127
+ const relStr = index === 0 ? "latest run" : `−${formatRelativeAge(deltaMs)} from latest`;
3128
+ const durationStr = entry.endTime != null ? `${entry.endTime - entry.startTime}ms` : "running…";
3129
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tooltip, {
3130
+ anchor,
3131
+ config: {
3132
+ align: "center",
3133
+ maxWidth: 240,
3134
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3135
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3136
+ style: {
3137
+ color: dotColor,
3138
+ marginBottom: "1px"
3139
+ },
3140
+ children: [
3141
+ symbol,
3142
+ " run ",
3143
+ index + 1,
3144
+ " of ",
3145
+ total
3146
+ ]
3147
+ }),
3148
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3149
+ style: { color: DEVTOOL_COLOR_TEXT_SECONDARY },
3150
+ children: formatTimestamp(entry.startTime)
3151
+ }),
3152
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3153
+ style: { color: DEVTOOL_COLOR_TEXT_MUTED },
3154
+ children: durationStr
3155
+ }),
3156
+ index > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3157
+ style: {
3158
+ color: "#64748b",
3159
+ marginTop: "3px",
3160
+ paddingTop: "3px",
3161
+ borderTop: `1px solid #1e293b`
3162
+ },
3163
+ children: relStr
3164
+ })
3165
+ ] })
3166
+ }
3167
+ });
3168
+ }
3169
+ function GroupDot({ entry, index, total, refTime, isActive, onSelect }) {
3170
+ const [anchor, setAnchor] = (0, react.useState)(null);
3171
+ const dotColor = STATUS_COLOR[entry.status];
3172
+ const hovered = anchor != null;
3173
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3174
+ "data-cuid": entry.cuid,
3175
+ onClick: (e) => {
3176
+ e.stopPropagation();
3177
+ onSelect();
3178
+ },
3179
+ onMouseEnter: (e) => setAnchor(e.currentTarget.getBoundingClientRect()),
3180
+ onMouseLeave: () => setAnchor(null),
3181
+ style: {
3182
+ width: "9px",
3183
+ height: "9px",
3184
+ borderRadius: "50%",
3185
+ border: isActive ? `2px solid ${dotColor}` : hovered ? `1px solid ${dotColor}99` : "1px solid transparent",
3186
+ background: isActive ? "transparent" : dotColor,
3187
+ cursor: "pointer",
3188
+ padding: 0,
3189
+ flexShrink: 0,
3190
+ opacity: isActive ? 1 : hovered ? .8 : .35,
3191
+ transform: hovered ? "scale(1.55)" : "scale(1)",
3192
+ transition: "transform 0.1s ease, opacity 0.1s ease, border-color 0.1s ease"
3193
+ }
3194
+ }), hovered && anchor != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupDotTooltip, {
3195
+ entry,
3196
+ index,
3197
+ total,
3198
+ refTime,
3199
+ dotColor,
3200
+ anchor
3201
+ })] });
3202
+ }
3203
+ function ActionEntryRow({ entry, isSelected, onClick, isLatest = false, latestTime, childEntries, breakReasons, groupEntries, selectedGroupCuid, onSelectGroupEntry }) {
3204
+ const timestamp = formatTimestamp(entry.startTime);
3205
+ const hasGroup = groupEntries != null && groupEntries.length > 1;
3206
+ const inboundOrigin = getInboundOrigin(entry);
3207
+ const externalChildEntries = childEntries?.filter((child) => child.meta.routing[0]?.handlerType === "external") ?? [];
3208
+ const nonIoBreakReasons = breakReasons?.filter((r) => r !== "new_input" && r !== "new_output") ?? [];
3209
+ const hasBottomError = entry.error != null || entry.abortReason != null;
3210
+ const hasBottomContent = externalChildEntries.length > 0 || nonIoBreakReasons.length > 0;
3211
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3212
+ "data-cuid": entry.cuid,
3213
+ onClick,
3214
+ style: {
3215
+ position: "relative",
3216
+ display: "flex",
3217
+ flexDirection: "column",
3218
+ gap: "5px",
3219
+ padding: "0.5em 0.6em",
3220
+ cursor: "pointer",
3221
+ background: isSelected ? DEVTOOL_LIST_HEADER_SELECTED_BACKGROUND : "transparent",
3222
+ border: `1px solid ${DEVTOOL_PANEL_BORDER}`,
3223
+ borderLeft: isSelected ? `2px solid ${STATUS_COLOR[entry.status]}` : entry.status === "failed" || entry.status === "action-error" ? `2px solid ${STATUS_COLOR[entry.status]}55` : `2px solid ${DEVTOOL_PANEL_BORDER}`,
3224
+ borderRadius: "3px",
3225
+ margin: "2px 4px"
3226
+ },
3227
+ children: [
3228
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
3229
+ position: "absolute",
3230
+ left: "2em",
3231
+ top: 0,
3232
+ bottom: 0,
3233
+ width: "1px",
3234
+ background: `${DEVTOOL_COLOR_SEMANTIC_METADATA}28`,
3235
+ pointerEvents: "none",
3236
+ zIndex: 0
3237
+ } }),
3238
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3239
+ style: {
3240
+ display: "flex",
3241
+ alignItems: "center",
3242
+ gap: "8px"
3243
+ },
3244
+ children: [
3245
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3246
+ style: {
3247
+ position: "relative",
3248
+ zIndex: 1,
3249
+ flexShrink: 0,
3250
+ minWidth: "3.4em",
3251
+ display: "flex",
3252
+ alignItems: "center",
3253
+ justifyContent: "flex-start"
3254
+ },
3255
+ children: [isLatest ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
3256
+ size: "sm",
3257
+ color: getLatestChipColor(entry.status),
3258
+ children: "latest"
3259
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Chip, {
3260
+ size: "sm",
3261
+ color: getLatestChipColor(entry.status),
3262
+ children: ["+", latestTime != null ? formatRelativeAge(latestTime - entry.startTime) : "?"]
3263
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3264
+ style: {
3265
+ position: "absolute",
3266
+ top: "100%",
3267
+ left: 0,
3268
+ marginTop: "5px",
3269
+ color: DEVTOOL_COLOR_SEMANTIC_METADATA,
3270
+ fontSize: "10px",
3271
+ lineHeight: "1em",
3272
+ letterSpacing: "0.02em",
3273
+ fontFamily: "ui-sans-serif, system-ui, sans-serif",
3274
+ opacity: .7,
3275
+ paddingLeft: "0.1em",
3276
+ whiteSpace: "nowrap"
3277
+ },
3278
+ children: timestamp
3279
+ })]
3280
+ }),
3281
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3282
+ title: entry.actionId,
3283
+ style: {
3284
+ flex: 1,
3285
+ minWidth: 0,
3286
+ color: DEVTOOL_COLOR_TEXT_SECONDARY,
3287
+ fontSize: "1em",
3288
+ fontWeight: 400,
3289
+ fontFamily: "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace",
3290
+ overflow: "hidden",
3291
+ textOverflow: "ellipsis",
3292
+ whiteSpace: "nowrap"
3293
+ },
3294
+ children: entry.actionId
3295
+ }),
3296
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3297
+ style: {
3298
+ color: DEVTOOL_COLOR_SEMANTIC_WARNING,
3299
+ lineHeight: "1em",
3300
+ fontSize: "11px",
3301
+ opacity: .9,
3302
+ flexShrink: 0,
3303
+ fontFamily: "ui-sans-serif, system-ui, sans-serif"
3304
+ },
3305
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DurationDisplay, { entry })
3306
+ })
3307
+ ]
3308
+ }),
3309
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3310
+ style: {
3311
+ display: "flex",
3312
+ alignItems: "center",
3313
+ gap: "0.4em",
3314
+ paddingLeft: "4.5em",
3315
+ minWidth: 0
3316
+ },
3317
+ children: [inboundOrigin != null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OriginChip, {
3318
+ origin: inboundOrigin,
3319
+ size: "sm",
3320
+ subtle: true
3321
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionInputAndOutputChip, {
3322
+ breakReasons,
3323
+ entry,
3324
+ size: "sm"
3325
+ })]
3326
+ }),
3327
+ hasBottomContent && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3328
+ style: {
3329
+ display: "flex",
3330
+ flexWrap: "wrap",
3331
+ alignItems: "center",
3332
+ gap: "4px",
3333
+ paddingLeft: "4.5em"
3334
+ },
3335
+ children: [
3336
+ externalChildEntries.map((child) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [
3337
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
3338
+ size: "sm",
3339
+ icon: lucide_react.Variable,
3340
+ color: "io_input",
3341
+ subtle: true,
3342
+ tooltip: {
3343
+ content: child.input !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IoTooltipContent, { value: child.input }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3344
+ style: {
3345
+ color: "#64748b",
3346
+ fontSize: "10px"
3347
+ },
3348
+ children: "No input required or given"
3349
+ }),
3350
+ title: `Input · ${child.actionId}`
3351
+ }
3352
+ }),
3353
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HandlerChips, {
3354
+ entry: child,
3355
+ size: "sm",
3356
+ subtle: true
3357
+ }),
3358
+ child.status === "success" && child.output !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
3359
+ size: "sm",
3360
+ icon: lucide_react.PackageCheck,
3361
+ color: "io_output",
3362
+ subtle: true,
3363
+ tooltip: {
3364
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IoTooltipContent, { value: child.output }),
3365
+ title: `Output · ${child.actionId}`
3366
+ }
3367
+ })
3368
+ ] }, child.actionId)),
3369
+ nonIoBreakReasons.map((reason) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chip, {
3370
+ color: "default",
3371
+ subtle: true,
3372
+ children: reason
3373
+ }, reason)),
3374
+ externalChildEntries.length > 0 && hasBottomError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
3375
+ size: "sm",
3376
+ icon: lucide_react.CircleX,
3377
+ color: entry.status === "aborted" ? "aborted" : "error",
3378
+ subtle: true,
3379
+ tooltip: {
3380
+ content: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionErrorDisplay, {
3381
+ entry,
3382
+ compact: true
3383
+ }),
3384
+ title: entry.status === "aborted" ? "Aborted" : "Error",
3385
+ maxWidth: 340
3386
+ }
3387
+ })
3388
+ ]
3389
+ }),
3390
+ hasGroup && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3391
+ style: {
3392
+ display: "flex",
3393
+ flexWrap: "wrap",
3394
+ alignItems: "center",
3395
+ gap: "5px",
3396
+ paddingLeft: "4.6em",
3397
+ paddingBottom: "2px"
3398
+ },
3399
+ children: [groupEntries.slice(0, MAX_GROUP_DOTS).map((e, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupDot, {
3400
+ entry: e,
3401
+ index: i,
3402
+ total: groupEntries.length,
3403
+ refTime: groupEntries[0].startTime,
3404
+ isActive: selectedGroupCuid === e.cuid,
3405
+ onSelect: () => onSelectGroupEntry?.(e.cuid)
3406
+ }, e.cuid)), groupEntries.length > MAX_GROUP_DOTS && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
3407
+ style: {
3408
+ fontSize: "0.7em",
3409
+ opacity: .5,
3410
+ flexShrink: 0,
3411
+ lineHeight: 1
3412
+ },
3413
+ children: ["+ ", groupEntries.length - MAX_GROUP_DOTS]
3414
+ })]
3415
+ })
3416
+ ]
3417
+ });
3418
+ }
3419
+ //#endregion
3420
+ //#region src/devtools/browser/components/action_list/ActionList.tsx
3421
+ function getBreakReasons(current, previous) {
3422
+ const reasons = [];
3423
+ if (current.inputHash != null && previous.inputHash != null ? current.inputHash !== previous.inputHash : safeStringify(current.input, 0) !== safeStringify(previous.input, 0)) reasons.push("new_input");
3424
+ if (current.outputHash != null && previous.outputHash != null && current.outputHash !== previous.outputHash) reasons.push("new_output");
3425
+ return reasons;
3426
+ }
3427
+ function getGroupChildEntries(group, childEntriesMap) {
3428
+ const seen = /* @__PURE__ */ new Set();
3429
+ const result = [];
3430
+ for (const e of [group.representative, ...group.rest]) for (const child of childEntriesMap.get(e.cuid) ?? []) if (!seen.has(child.actionId)) {
3431
+ seen.add(child.actionId);
3432
+ result.push(child);
3433
+ }
3434
+ return result.length > 0 ? result.sort((a, b) => a.startTime - b.startTime) : void 0;
3435
+ }
3436
+ function getFlatItemKey(item) {
3437
+ return `g:${item.group.rest[item.group.rest.length - 1]?.cuid ?? item.group.representative.cuid}`;
3438
+ }
3439
+ function ActionList({ groups, selectedCuid, onGroupClick, onSubClick, childEntriesMap, style }) {
3440
+ const latestTime = groups[0]?.representative.startTime;
3441
+ const flatItems = (0, react.useMemo)(() => {
3442
+ return groups.map((group, gi) => {
3443
+ const prevGroup = groups[gi + 1];
3444
+ let breakReasons = [];
3445
+ if (prevGroup != null && prevGroup.representative.actionId === group.representative.actionId && prevGroup.representative.domain === group.representative.domain) {
3446
+ const reasons = getBreakReasons(group.representative, prevGroup.representative);
3447
+ if (reasons.length > 0) breakReasons = reasons;
3448
+ }
3449
+ return {
3450
+ group,
3451
+ groupIndex: gi,
3452
+ breakReasons
3453
+ };
3454
+ });
3455
+ }, [groups]);
3456
+ const prevRepCuidsRef = (0, react.useRef)(null);
3457
+ const newRepCuids = (0, react.useMemo)(() => {
3458
+ const current = new Set(flatItems.map((i) => i.group.representative.cuid));
3459
+ const prev = prevRepCuidsRef.current;
3460
+ const result = /* @__PURE__ */ new Set();
3461
+ if (prev != null) {
3462
+ for (const c of current) if (!prev.has(c)) result.add(c);
3463
+ }
3464
+ prevRepCuidsRef.current = current;
3465
+ return result;
3466
+ }, [flatItems]);
3467
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DevtoolsVirtualList, {
3468
+ items: flatItems,
3469
+ getItemKey: getFlatItemKey,
3470
+ selectedKey: (0, react.useMemo)(() => {
3471
+ if (selectedCuid == null) return null;
3472
+ const item = flatItems.find((i) => i.group.representative.cuid === selectedCuid || i.group.rest.some((e) => e.cuid === selectedCuid));
3473
+ return item != null ? getFlatItemKey(item) : null;
3474
+ }, [flatItems, selectedCuid]),
3475
+ estimateSize: 64,
3476
+ overscan: 8,
3477
+ style,
3478
+ rowStyle: {
3479
+ borderBottom: `1px solid ${DEVTOOL_LIST_GROUP_DIVIDER}`,
3480
+ overflow: "hidden"
3481
+ },
3482
+ empty: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3483
+ style,
3484
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3485
+ style: {
3486
+ padding: "24px",
3487
+ textAlign: "center",
3488
+ color: DEVTOOL_COLOR_TEXT_MUTED
3489
+ },
3490
+ children: "No actions recorded yet"
3491
+ })
3492
+ }),
3493
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3494
+ style: {
3495
+ padding: "24px",
3496
+ textAlign: "center",
3497
+ color: DEVTOOL_COLOR_TEXT_MUTED
3498
+ },
3499
+ children: "Start of action history"
3500
+ }),
3501
+ renderItem: (item) => {
3502
+ const { group } = item;
3503
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [newRepCuids.has(group.representative.cuid) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
3504
+ position: "absolute",
3505
+ inset: 0,
3506
+ pointerEvents: "none",
3507
+ background: "linear-gradient(90deg, transparent 0%, rgba(148, 210, 255, 0.13) 50%, transparent 100%)",
3508
+ animation: "__nice-action-shine 0.65s ease-out forwards"
3509
+ } }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionEntryRow, {
3510
+ entry: group.representative,
3511
+ isSelected: selectedCuid === group.representative.cuid || group.rest.some((e) => e.cuid === selectedCuid),
3512
+ isLatest: item.groupIndex === 0,
3513
+ latestTime,
3514
+ childEntries: getGroupChildEntries(group, childEntriesMap),
3515
+ breakReasons: item.breakReasons,
3516
+ groupEntries: group.rest.length > 0 ? [group.representative, ...group.rest] : void 0,
3517
+ selectedGroupCuid: selectedCuid,
3518
+ onSelectGroupEntry: (cuid) => onSubClick(cuid, selectedCuid === cuid),
3519
+ onClick: () => onGroupClick(group)
3520
+ })] });
3521
+ }
3522
+ });
3523
+ }
3524
+ //#endregion
3525
+ //#region src/devtools/browser/NiceActionDevtools.tsx
3526
+ if (typeof document !== "undefined" && !document.getElementById("__nice-action-devtools-styles")) {
3527
+ const style = document.createElement("style");
3528
+ style.id = "__nice-action-devtools-styles";
3529
+ style.textContent = `
3530
+ @keyframes __nice-action-pulse {
3531
+ 0%, 100% { opacity: 1; }
3532
+ 50% { opacity: 0.35; }
3533
+ }
3534
+ @keyframes __nice-action-shine {
3535
+ 0% { transform: translateX(-100%); opacity: 0; }
3536
+ 15% { opacity: 1; }
3537
+ 85% { opacity: 1; }
3538
+ 100% { transform: translateX(200%); opacity: 0; }
3539
+ }
3540
+ #__nice-action-devtools-panel ::-webkit-scrollbar {
3541
+ width: 4px;
3542
+ height: 4px;
3543
+ }
3544
+ #__nice-action-devtools-panel ::-webkit-scrollbar-track {
3545
+ background: transparent;
3546
+ }
3547
+ #__nice-action-devtools-panel ::-webkit-scrollbar-thumb {
3548
+ background: #334155;
3549
+ border-radius: 2px;
3550
+ }
3551
+ #__nice-action-devtools-panel ::-webkit-scrollbar-thumb:hover {
3552
+ background: #475569;
3553
+ }
3554
+ #__nice-action-devtools-panel ::-webkit-scrollbar-corner {
3555
+ background: transparent;
3556
+ }
3557
+ /* Shield the panel's native form controls from the host app's global element
3558
+ styles (e.g. a bare \`input {}\`/\`button {}\` rule). \`all: revert\` drops them
3559
+ to the UA baseline the panel is authored against; the panel's own inline
3560
+ styles still win, so its look is unchanged across any host. */
3561
+ #__nice-action-devtools-panel input,
3562
+ #__nice-action-devtools-panel button,
3563
+ #__nice-action-devtools-panel select,
3564
+ #__nice-action-devtools-panel textarea { all: revert; font-family: inherit; }
3565
+ `;
3566
+ document.head?.appendChild(style);
3567
+ }
3568
+ const PREFS_KEY = "__nice-action-devtools-prefs";
3569
+ const DOCKED_HEIGHT_DEFAULT = 320;
3570
+ const DOCKED_WIDTH_DEFAULT = 330;
3571
+ const DETAIL_RATIO_DEFAULT = .5;
3572
+ const DOCK_POSITIONS = [
3573
+ "dock-bottom",
3574
+ "dock-top",
3575
+ "dock-left",
3576
+ "dock-right"
3577
+ ];
3578
+ function isDockPosition(value) {
3579
+ return typeof value === "string" && DOCK_POSITIONS.includes(value);
3580
+ }
3581
+ function readPrefs(defaultPosition, initialOpen) {
3582
+ const fallback = {
3583
+ position: defaultPosition,
3584
+ isOpen: initialOpen,
3585
+ dockedHeight: DOCKED_HEIGHT_DEFAULT,
3586
+ dockedWidth: DOCKED_WIDTH_DEFAULT,
3587
+ detailRatio: DETAIL_RATIO_DEFAULT,
3588
+ stayOnLatest: true,
3589
+ followLatestOnSelect: true
3590
+ };
3591
+ try {
3592
+ if (typeof localStorage === "undefined") return fallback;
3593
+ const stored = localStorage.getItem(PREFS_KEY);
3594
+ const merged = stored != null ? {
3595
+ ...fallback,
3596
+ ...JSON.parse(stored)
3597
+ } : fallback;
3598
+ if (!isDockPosition(merged.position)) merged.position = defaultPosition;
3599
+ return merged;
3600
+ } catch (_e) {
3601
+ return fallback;
3602
+ }
3603
+ }
3604
+ function writePrefs(prefs) {
3605
+ try {
3606
+ localStorage.setItem(PREFS_KEY, JSON.stringify(prefs));
3607
+ } catch (_e) {
3608
+ return;
3609
+ }
3610
+ }
3611
+ function getHandlerKey(entry) {
3612
+ const hop = entry.meta.routing[0];
3613
+ if (hop == null) return "none";
3614
+ if (hop.handlerType === "local") return "local";
3615
+ return `ext:${hop.transport ?? "ext"}`;
3616
+ }
3617
+ /** Same logical call — used both for grouping and for deciding which running rows to show. */
3618
+ function entriesShareActionInput(a, b) {
3619
+ if (a.actionId !== b.actionId || a.domain !== b.domain) return false;
3620
+ return a.inputHash != null && b.inputHash != null ? a.inputHash === b.inputHash : safeStringify(a.input, 0) === safeStringify(b.input, 0);
3621
+ }
3622
+ function canGroupWith(a, b) {
3623
+ if (!entriesShareActionInput(a, b)) return false;
3624
+ const handlerA = getHandlerKey(a);
3625
+ const handlerB = getHandlerKey(b);
3626
+ if (handlerA !== "none" && handlerB !== "none" && handlerA !== handlerB) return false;
3627
+ if (a.status === "running" || b.status === "running") return true;
3628
+ const outputMatch = a.outputHash != null && b.outputHash != null ? a.outputHash === b.outputHash : true;
3629
+ return a.status === b.status && outputMatch;
3630
+ }
3631
+ function groupEntries(entries) {
3632
+ const groups = [];
3633
+ for (const entry of entries) {
3634
+ const last = groups[groups.length - 1];
3635
+ if (last != null && canGroupWith(entry, last.representative)) last.rest.push(entry);
3636
+ else groups.push({
3637
+ representative: entry,
3638
+ rest: []
3639
+ });
3640
+ }
3641
+ return groups;
3642
+ }
3643
+ function NiceActionDevtools({ forceEnable, ...props }) {
3644
+ if (!forceEnable && true) return null;
3645
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NiceActionDevtools_Panel, { ...props });
3646
+ }
3647
+ function NiceActionDevtools_Panel({ core, position: defaultPosition = "dock-right", initialOpen = false }) {
3648
+ const [prefs, setPrefsRaw] = (0, react.useState)(() => readPrefs(defaultPosition, initialOpen));
3649
+ const [entries, setEntries] = (0, react.useState)([]);
3650
+ const [selectedCuid, setSelectedCuid] = (0, react.useState)(null);
3651
+ (0, react.useEffect)(() => core.subscribe(setEntries), [core]);
3652
+ const groups = (0, react.useMemo)(() => {
3653
+ const byCuid = new Map(entries.map((e) => [e.cuid, e]));
3654
+ return groupEntries(entries.filter((e) => e.parentCuid == null || !byCuid.has(e.parentCuid)));
3655
+ }, [entries]);
3656
+ const childEntriesMap = (0, react.useMemo)(() => {
3657
+ const map = /* @__PURE__ */ new Map();
3658
+ for (const entry of entries) {
3659
+ if (entry.parentCuid == null) continue;
3660
+ const existing = map.get(entry.parentCuid) ?? [];
3661
+ map.set(entry.parentCuid, [...existing, entry]);
3662
+ }
3663
+ for (const arr of map.values()) arr.sort((a, b) => a.startTime - b.startTime);
3664
+ return map;
3665
+ }, [entries]);
3666
+ const setPrefs = (update) => {
3667
+ setPrefsRaw((prev) => ({
3668
+ ...prev,
3669
+ ...update
3670
+ }));
3671
+ };
3672
+ (0, react.useEffect)(() => {
3673
+ const timer = setTimeout(() => writePrefs(prefs), 250);
3674
+ return () => clearTimeout(timer);
3675
+ }, [prefs]);
3676
+ const { position, isOpen, dockedHeight, dockedWidth, detailRatio, stayOnLatest, followLatestOnSelect } = prefs;
3677
+ const dockSide = getDockSide(position);
3678
+ const isHorizDock = dockSide === "top" || dockSide === "bottom";
3679
+ const dockedSize = isHorizDock ? dockedHeight : dockedWidth;
3680
+ const latestCuid = groups.length > 0 ? groups[0].representative.cuid : null;
3681
+ (0, react.useEffect)(() => {
3682
+ if (stayOnLatest && latestCuid != null) setSelectedCuid(latestCuid);
3683
+ }, [stayOnLatest, latestCuid]);
3684
+ const applySelection = (next) => {
3685
+ if (next != null && next === latestCuid && followLatestOnSelect) {
3686
+ if (!stayOnLatest) setPrefs({ stayOnLatest: true });
3687
+ } else if (stayOnLatest) setPrefs({ stayOnLatest: false });
3688
+ setSelectedCuid(next);
3689
+ };
3690
+ const handleGroupRowClick = (group) => {
3691
+ const repCuid = group.representative.cuid;
3692
+ if (([group.representative, ...group.rest].find((e) => e.cuid === selectedCuid) ?? null) != null && selectedCuid !== repCuid) applySelection(repCuid);
3693
+ else applySelection(selectedCuid === repCuid ? null : repCuid);
3694
+ };
3695
+ const selectedEntry = selectedCuid != null ? entries.find((e) => e.cuid === selectedCuid) : null;
3696
+ const runningCount = entries.filter((e) => e.status === "running").length;
3697
+ const dock = (0, react.useMemo)(() => getDevtoolsDockCoordinator(), []);
3698
+ const panelId = (0, react.useId)();
3699
+ const [, bumpView] = (0, react.useReducer)((n) => n + 1, 0);
3700
+ const badge = runningCount > 0 ? `${runningCount}●` : void 0;
3701
+ (0, react.useEffect)(() => {
3702
+ const unregister = dock.register({
3703
+ id: panelId,
3704
+ label: "actions",
3705
+ icon: "⚡",
3706
+ side: dockSide,
3707
+ size: dockedSize,
3708
+ open: isOpen,
3709
+ badge,
3710
+ onOpen: () => setPrefs({ isOpen: true })
3711
+ });
3712
+ const unsubscribe = dock.subscribe(bumpView);
3713
+ return () => {
3714
+ unregister();
3715
+ unsubscribe();
3716
+ };
3717
+ }, [dock, panelId]);
3718
+ (0, react.useEffect)(() => {
3719
+ dock.update(panelId, {
3720
+ side: dockSide,
3721
+ size: dockedSize,
3722
+ open: isOpen,
3723
+ badge
3724
+ });
3725
+ }, [
3726
+ dock,
3727
+ panelId,
3728
+ dockSide,
3729
+ dockedSize,
3730
+ isOpen,
3731
+ badge
3732
+ ]);
3733
+ const view = dock.getView(panelId);
3734
+ const baseStyle = {
3735
+ position: "fixed",
3736
+ zIndex: 2147483647,
3737
+ fontFamily: "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace",
3738
+ fontSize: "12px"
3739
+ };
3740
+ if (!isOpen) {
3741
+ if (view.isPrimary && !view.anyOpen) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DevtoolsLauncher, { items: view.devtools });
3742
+ return null;
3743
+ }
3744
+ const panelStyle = {
3745
+ ...baseStyle,
3746
+ background: DEVTOOL_LIST_BASE_BACKGROUND,
3747
+ border: `1px solid ${DEVTOOL_PANEL_BORDER}`,
3748
+ color: DEVTOOL_COLOR_TEXT_SECONDARY,
3749
+ display: "flex",
3750
+ flexDirection: "column",
3751
+ boxShadow: "0 -4px 24px rgba(0,0,0,0.4)",
3752
+ overflow: "hidden",
3753
+ ...dockSide === "bottom" ? {
3754
+ bottom: view.dockOffset,
3755
+ left: 0,
3756
+ right: 0,
3757
+ height: `${dockedSize}px`,
3758
+ borderRadius: view.stacked ? "0" : "8px 8px 0 0"
3759
+ } : dockSide === "top" ? {
3760
+ top: view.dockOffset,
3761
+ left: 0,
3762
+ right: 0,
3763
+ height: `${dockedSize}px`,
3764
+ borderRadius: view.stacked ? "0" : "0 0 8px 8px"
3765
+ } : dockSide === "left" ? {
3766
+ top: 0,
3767
+ left: view.dockOffset,
3768
+ bottom: 0,
3769
+ width: `${dockedSize}px`,
3770
+ borderRadius: view.stacked ? "0" : "0 8px 8px 0"
3771
+ } : {
3772
+ top: 0,
3773
+ right: view.dockOffset,
3774
+ bottom: 0,
3775
+ width: `${dockedSize}px`,
3776
+ borderRadius: view.stacked ? "0" : "8px 0 0 8px"
3777
+ }
3778
+ };
3779
+ const selectedEntryParent = selectedEntry?.parentCuid != null ? entries.find((e) => e.cuid === selectedEntry.parentCuid) ?? null : null;
3780
+ const selectedEntryChildren = selectedEntry != null ? [...entries].filter((e) => e.parentCuid === selectedEntry.cuid).sort((a, b) => a.startTime - b.startTime) : [];
3781
+ const virtualListProps = {
3782
+ groups,
3783
+ selectedCuid,
3784
+ onGroupClick: handleGroupRowClick,
3785
+ onSubClick: (cuid, isSelected) => {
3786
+ applySelection(isSelected ? null : cuid);
3787
+ },
3788
+ childEntriesMap
3789
+ };
3790
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3791
+ id: "__nice-action-devtools-panel",
3792
+ style: panelStyle,
3793
+ children: [
3794
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResizeHandle, {
3795
+ dockSide,
3796
+ dockedSize,
3797
+ onChange: (size) => setPrefs(isHorizDock ? { dockedHeight: size } : { dockedWidth: size })
3798
+ }),
3799
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelHeader, {
3800
+ title: "⚡ action",
3801
+ position,
3802
+ onPositionChange: (p) => setPrefs({ position: p }),
3803
+ onClose: () => setPrefs({ isOpen: false }),
3804
+ onClear: entries.length > 0 ? () => {
3805
+ core.clear();
3806
+ setSelectedCuid(null);
3807
+ } : void 0,
3808
+ openOthers: view.otherClosed
3809
+ }),
3810
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3811
+ style: {
3812
+ flex: 1,
3813
+ display: "flex",
3814
+ flexDirection: isHorizDock ? "row" : "column",
3815
+ overflow: "hidden",
3816
+ minHeight: 0
3817
+ },
3818
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3819
+ style: {
3820
+ flexGrow: selectedEntry != null ? 1 - detailRatio : 1,
3821
+ flexShrink: 1,
3822
+ flexBasis: 0,
3823
+ minWidth: 0,
3824
+ minHeight: 0,
3825
+ display: "flex",
3826
+ flexDirection: "column",
3827
+ overflow: "hidden"
3828
+ },
3829
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FollowLatestToggles, {
3830
+ noun: "action",
3831
+ stayOnLatest,
3832
+ onStayOnLatestChange: (next) => setPrefs({ stayOnLatest: next }),
3833
+ followLatestOnSelect,
3834
+ onFollowLatestOnSelectChange: (next) => {
3835
+ if (next && latestCuid != null && selectedCuid === latestCuid && !stayOnLatest) setPrefs({
3836
+ followLatestOnSelect: next,
3837
+ stayOnLatest: true
3838
+ });
3839
+ else setPrefs({ followLatestOnSelect: next });
3840
+ }
3841
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionList, {
3842
+ ...virtualListProps,
3843
+ style: {
3844
+ width: "100%",
3845
+ flex: 1,
3846
+ minHeight: 0,
3847
+ overflowY: "auto"
3848
+ }
3849
+ })]
3850
+ }), selectedEntry != null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SplitHandle, {
3851
+ horizontal: isHorizDock,
3852
+ onRatioChange: (ratio) => setPrefs({ detailRatio: ratio })
3853
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3854
+ style: {
3855
+ flexGrow: detailRatio,
3856
+ flexShrink: 1,
3857
+ flexBasis: 0,
3858
+ minWidth: 0,
3859
+ minHeight: 0,
3860
+ display: "flex",
3861
+ flexDirection: "column",
3862
+ overflow: "hidden",
3863
+ ...isHorizDock ? {
3864
+ borderLeft: `1px solid #1d3352`,
3865
+ boxShadow: "inset 18px 0 36px -14px rgba(0,0,0,0.8)"
3866
+ } : {
3867
+ borderTop: `1px solid #1d3352`,
3868
+ boxShadow: "inset 0 18px 36px -14px rgba(0,0,0,0.8)"
3869
+ }
3870
+ },
3871
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionDetailPanel, {
3872
+ entry: selectedEntry,
3873
+ parent: selectedEntryParent,
3874
+ childEntries: selectedEntryChildren,
3875
+ onSelectEntry: (cuid) => setSelectedCuid(cuid)
3876
+ }, selectedEntry.cuid)
3877
+ })] })]
3878
+ })
3879
+ ]
3880
+ });
3881
+ }
3882
+ //#endregion
3883
+ exports.ActionDevtoolsCore = require_ActionDevtoolsCore.ActionDevtoolsCore;
3884
+ exports.NiceActionDevtools = NiceActionDevtools;
3885
+
3886
+ //# sourceMappingURL=index.cjs.map