@nice-code/action 0.2.12 → 0.2.13

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 (25) hide show
  1. package/build/devtools/browser/index.js +1761 -969
  2. package/build/devtools/server/index.js +11 -8
  3. package/build/index.js +42 -12
  4. package/build/types/ActionDefinition/Action/Payload/ActionPayload_Request.d.ts +1 -0
  5. package/build/types/ActionDefinition/Action/RunningAction.d.ts +2 -0
  6. package/build/types/ActionDefinition/Action/RunningAction.types.d.ts +2 -0
  7. package/build/types/ActionRuntime/HandlerCallStack.d.ts +3 -0
  8. package/build/types/devtools/browser/components/ActionDetailPanel.d.ts +7 -0
  9. package/build/types/devtools/browser/components/ActionEntryRow.d.ts +11 -0
  10. package/build/types/devtools/browser/components/CallStackSection.d.ts +8 -0
  11. package/build/types/devtools/browser/components/ChildDispatchChips.d.ts +4 -0
  12. package/build/types/devtools/browser/components/Chip.d.ts +11 -0
  13. package/build/types/devtools/browser/components/DetailSection.d.ts +5 -0
  14. package/build/types/devtools/browser/components/DomainChip.d.ts +14 -0
  15. package/build/types/devtools/browser/components/HandlerChips.d.ts +10 -0
  16. package/build/types/devtools/browser/components/MetaSection.d.ts +4 -0
  17. package/build/types/devtools/browser/components/PanelChrome.d.ts +14 -0
  18. package/build/types/devtools/browser/components/RoutingSection.d.ts +5 -0
  19. package/build/types/devtools/browser/components/RunningTimer.d.ts +7 -0
  20. package/build/types/devtools/browser/components/SectionLabel.d.ts +4 -0
  21. package/build/types/devtools/browser/components/StackTraceSection.d.ts +5 -0
  22. package/build/types/devtools/browser/components/utils.d.ts +7 -0
  23. package/build/types/devtools/core/ActionDevtools.types.d.ts +3 -0
  24. package/build/types/devtools/core/ActionDevtoolsCore.d.ts +1 -3
  25. package/package.json +3 -3
@@ -54,10 +54,7 @@ function extractMeta(context) {
54
54
  class ActionDevtoolsCore {
55
55
  _entries = [];
56
56
  _listeners = new Set;
57
- _maxEntries;
58
- constructor(options = {}) {
59
- this._maxEntries = options.maxEntries ?? 100;
60
- }
57
+ constructor(_options = {}) {}
61
58
  attachToDomain(domain) {
62
59
  return domain.addActionListener((update) => {
63
60
  const { runningAction, type, time } = update;
@@ -71,9 +68,11 @@ class ActionDevtoolsCore {
71
68
  startTime: time,
72
69
  input: runningAction.state?.request?.input,
73
70
  progressUpdates: [],
74
- meta: extractMeta(runningAction.context)
71
+ meta: extractMeta(runningAction.context),
72
+ parentCuid: runningAction.parentCuid,
73
+ callSite: runningAction.callSite
75
74
  };
76
- this._entries = [entry, ...this._entries].slice(0, this._maxEntries);
75
+ this._entries = [entry, ...this._entries];
77
76
  this._notify();
78
77
  } else if (type === "progress" /* progress */) {
79
78
  this._updateEntry(runningAction.cuid, (e) => ({
@@ -93,9 +92,13 @@ class ActionDevtoolsCore {
93
92
  return { ...base, status: "success", output: update.response?.result?.output };
94
93
  }
95
94
  if (finishType === "failed" /* failed */) {
96
- return { ...base, status: "failed", error: update.error };
95
+ const rawError = update.error;
96
+ const errorStack2 = rawError instanceof Error ? rawError.stack : undefined;
97
+ return { ...base, status: "failed", error: rawError, errorStack: errorStack2 };
97
98
  }
98
- return { ...base, status: "aborted", abortReason: update.reason };
99
+ const abortReason = update.reason;
100
+ const errorStack = abortReason instanceof Error ? abortReason.stack : undefined;
101
+ return { ...base, status: "aborted", abortReason, errorStack };
99
102
  });
100
103
  }
101
104
  });
@@ -126,19 +129,125 @@ class ActionDevtoolsCore {
126
129
  }
127
130
  // src/devtools/browser/NiceActionDevtools.tsx
128
131
  import { useVirtualizer } from "@tanstack/react-virtual";
129
- import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
130
- import { jsxDEV, Fragment as Fragment2 } from "react/jsx-dev-runtime";
131
- if (typeof document !== "undefined" && !document.getElementById("__nice-action-devtools-styles")) {
132
- const style = document.createElement("style");
133
- style.id = "__nice-action-devtools-styles";
134
- style.textContent = `
135
- @keyframes __nice-action-pulse {
136
- 0%, 100% { opacity: 1; }
137
- 50% { opacity: 0.35; }
138
- }
139
- `;
140
- document.head?.appendChild(style);
132
+ import { useCallback, useEffect as useEffect2, useLayoutEffect, useMemo as useMemo2, useRef as useRef2, useState as useState6 } from "react";
133
+
134
+ // src/devtools/browser/components/ActionDetailPanel.tsx
135
+ import { useMemo, useState as useState5 } from "react";
136
+
137
+ // src/devtools/browser/components/DomainChip.tsx
138
+ import { createContext, useContext, useRef } from "react";
139
+ import { jsxDEV } from "react/jsx-dev-runtime";
140
+ var DomainTooltipCtx = createContext(null);
141
+ function DomainHierarchyTooltip({
142
+ allDomains,
143
+ anchor
144
+ }) {
145
+ return /* @__PURE__ */ jsxDEV("div", {
146
+ style: {
147
+ position: "fixed",
148
+ left: anchor.left,
149
+ top: anchor.top - 6,
150
+ transform: "translateY(-100%)",
151
+ zIndex: 2147483647,
152
+ background: "#0c1526",
153
+ border: "1px solid #312e81",
154
+ borderRadius: "5px",
155
+ padding: "6px 8px",
156
+ fontFamily: "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace",
157
+ boxShadow: "0 4px 20px rgba(0,0,0,0.6)",
158
+ pointerEvents: "none",
159
+ minWidth: "80px"
160
+ },
161
+ children: allDomains.map((d, i) => {
162
+ const isCurrent = i === allDomains.length - 1;
163
+ return /* @__PURE__ */ jsxDEV("div", {
164
+ style: {
165
+ display: "flex",
166
+ alignItems: "center",
167
+ gap: "0.3rem",
168
+ paddingLeft: `${i * 10}px`,
169
+ paddingTop: i > 0 ? "0.1rem" : undefined
170
+ },
171
+ children: [
172
+ /* @__PURE__ */ jsxDEV("span", {
173
+ style: {
174
+ fontSize: "0.8rem",
175
+ height: "0.6em",
176
+ width: "0.6em",
177
+ overflow: "hidden",
178
+ color: isCurrent ? "#818cf8" : "#3730a3",
179
+ display: "flex",
180
+ alignItems: "center",
181
+ justifyContent: "center"
182
+ },
183
+ children: "⬢"
184
+ }, undefined, false, undefined, this),
185
+ /* @__PURE__ */ jsxDEV("span", {
186
+ style: {
187
+ color: isCurrent ? "#a5b4fc" : "#4b5563",
188
+ fontSize: "0.7rem",
189
+ fontWeight: isCurrent ? 500 : undefined
190
+ },
191
+ children: d
192
+ }, undefined, false, undefined, this)
193
+ ]
194
+ }, d, true, undefined, this);
195
+ })
196
+ }, undefined, false, undefined, this);
197
+ }
198
+ function DomainChip({
199
+ domain,
200
+ allDomains,
201
+ size
202
+ }) {
203
+ const ctx = useContext(DomainTooltipCtx);
204
+ const chipRef = useRef(null);
205
+ const hasHierarchy = allDomains != null && allDomains.length > 1;
206
+ const fontSize = size === "md" ? "0.7rem" : "0.6rem";
207
+ const padding = size === "md" ? "1px 5px" : "1px 4px";
208
+ return /* @__PURE__ */ jsxDEV("span", {
209
+ ref: chipRef,
210
+ onMouseEnter: hasHierarchy && ctx != null ? () => {
211
+ if (chipRef.current != null)
212
+ ctx.show(chipRef.current.getBoundingClientRect(), allDomains);
213
+ } : undefined,
214
+ onMouseLeave: hasHierarchy && ctx != null ? () => ctx.hide() : undefined,
215
+ style: {
216
+ display: "flex",
217
+ alignItems: "center",
218
+ gap: "0.4em",
219
+ color: "#818cf8",
220
+ fontSize,
221
+ background: "#0f172a",
222
+ border: "1px solid #312e81",
223
+ padding,
224
+ borderRadius: "0.6rem",
225
+ flexShrink: 0,
226
+ whiteSpace: "nowrap"
227
+ },
228
+ children: [
229
+ /* @__PURE__ */ jsxDEV("span", {
230
+ style: {
231
+ fontSize: size === "md" ? "0.9rem" : "0.8rem",
232
+ height: "0.6em",
233
+ width: "0.6em",
234
+ display: "flex",
235
+ alignItems: "center",
236
+ justifyContent: "center"
237
+ },
238
+ children: "⬢"
239
+ }, undefined, false, undefined, this),
240
+ /* @__PURE__ */ jsxDEV("span", {
241
+ children: domain
242
+ }, undefined, false, undefined, this)
243
+ ]
244
+ }, undefined, true, undefined, this);
141
245
  }
246
+
247
+ // src/devtools/browser/components/RunningTimer.tsx
248
+ import { useEffect, useState } from "react";
249
+
250
+ // src/devtools/browser/components/utils.ts
142
251
  var STATUS_COLOR = {
143
252
  running: "#60a5fa",
144
253
  success: "#4ade80",
@@ -151,826 +260,380 @@ var STATUS_SYMBOL = {
151
260
  failed: "✗",
152
261
  aborted: "○"
153
262
  };
154
- var PREFS_KEY = "__nice-action-devtools-prefs";
155
- var DOCKED_HEIGHT_DEFAULT = 320;
156
- var DOCKED_WIDTH_DEFAULT = 420;
157
- var DOCKED_SIZE_MIN = 140;
158
- var ROW_HEIGHT = 50;
159
- var SUB_ROW_HEIGHT = 30;
160
- function getDockSide(pos) {
161
- if (pos === "dock-top")
162
- return "top";
163
- if (pos === "dock-bottom")
164
- return "bottom";
165
- if (pos === "dock-left")
166
- return "left";
167
- if (pos === "dock-right")
168
- return "right";
169
- return null;
170
- }
171
- function readPrefs(defaultPosition, initialOpen) {
172
- const fallback = {
173
- position: defaultPosition,
174
- isOpen: initialOpen,
175
- dockedHeight: DOCKED_HEIGHT_DEFAULT,
176
- dockedWidth: DOCKED_WIDTH_DEFAULT
177
- };
263
+ function safeStringify(value, indent = 2) {
264
+ if (value === undefined)
265
+ return "undefined";
266
+ if (value === null)
267
+ return "null";
178
268
  try {
179
- if (typeof localStorage === "undefined")
180
- return fallback;
181
- const stored = localStorage.getItem(PREFS_KEY);
182
- return stored != null ? { ...fallback, ...JSON.parse(stored) } : fallback;
183
- } catch (_e) {
184
- return fallback;
269
+ return JSON.stringify(value, null, indent);
270
+ } catch {
271
+ return String(value);
185
272
  }
186
273
  }
187
- function writePrefs(prefs) {
188
- try {
189
- localStorage.setItem(PREFS_KEY, JSON.stringify(prefs));
190
- } catch (_e) {
191
- return;
192
- }
274
+ function formatRelativeAge(ms) {
275
+ if (ms < 1000)
276
+ return `${ms}ms`;
277
+ if (ms < 60000)
278
+ return `${(ms / 1000).toFixed(1)}s`;
279
+ return `${Math.floor(ms / 60000)}m`;
193
280
  }
194
- function getHandlerKey(entry) {
195
- const hop = entry.meta.routing[0];
196
- if (hop == null)
197
- return "none";
198
- if (hop.handlerType === "local")
199
- return "local";
200
- return `ext:${hop.transport ?? "ext"}`;
281
+ function formatDuration(entry) {
282
+ return entry.endTime != null ? `${entry.endTime - entry.startTime}ms` : null;
201
283
  }
202
- function canGroupWith(a, b) {
203
- if (a.status === "running" || b.status === "running")
204
- return false;
205
- const handlerA = getHandlerKey(a);
206
- const handlerB = getHandlerKey(b);
207
- const handlerConflict = handlerA !== "none" && handlerB !== "none" && handlerA !== handlerB;
208
- return a.actionId === b.actionId && a.domain === b.domain && a.status === b.status && !handlerConflict && safeStringify(a.input, 0) === safeStringify(b.input, 0);
284
+ function formatTimestamp(startTime) {
285
+ return new Date(startTime).toLocaleTimeString([], {
286
+ hour: "2-digit",
287
+ minute: "2-digit",
288
+ second: "2-digit",
289
+ hour12: false
290
+ });
209
291
  }
210
- function groupEntries(entries) {
211
- const groups = [];
212
- for (const entry of entries) {
213
- const last = groups[groups.length - 1];
214
- if (last != null && canGroupWith(entry, last.representative)) {
215
- last.rest.push(entry);
216
- } else {
217
- groups.push({ representative: entry, rest: [] });
218
- }
219
- }
220
- return groups;
292
+
293
+ // src/devtools/browser/components/RunningTimer.tsx
294
+ import { jsxDEV as jsxDEV2, Fragment } from "react/jsx-dev-runtime";
295
+ function RunningTimer({ startTime }) {
296
+ const [elapsed, setElapsed] = useState(() => Date.now() - startTime);
297
+ useEffect(() => {
298
+ const interval = setInterval(() => setElapsed(Date.now() - startTime), 100);
299
+ return () => clearInterval(interval);
300
+ }, [startTime]);
301
+ return /* @__PURE__ */ jsxDEV2(Fragment, {
302
+ children: [
303
+ elapsed,
304
+ "ms"
305
+ ]
306
+ }, undefined, true, undefined, this);
221
307
  }
222
- function NiceActionDevtools(props) {
223
- if (false) {}
224
- return /* @__PURE__ */ jsxDEV(NiceActionDevtools_Panel, {
225
- ...props
308
+ function DurationDisplay({ entry }) {
309
+ const d = formatDuration(entry);
310
+ return /* @__PURE__ */ jsxDEV2(Fragment, {
311
+ children: d ?? /* @__PURE__ */ jsxDEV2(RunningTimer, {
312
+ startTime: entry.startTime
313
+ }, undefined, false, undefined, this)
226
314
  }, undefined, false, undefined, this);
227
315
  }
228
- function NiceActionDevtools_Panel({
229
- core,
230
- position: defaultPosition = "bottom-right",
231
- initialOpen = false
316
+
317
+ // src/devtools/browser/components/SectionLabel.tsx
318
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
319
+ function SectionLabel({ label, color = "#60a5fa" }) {
320
+ return /* @__PURE__ */ jsxDEV3("div", {
321
+ style: {
322
+ color,
323
+ fontSize: "10px",
324
+ marginBottom: "3px",
325
+ textTransform: "uppercase",
326
+ letterSpacing: "0.05em"
327
+ },
328
+ children: label
329
+ }, undefined, false, undefined, this);
330
+ }
331
+
332
+ // src/devtools/browser/components/CallStackSection.tsx
333
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
334
+ function CallStackLink({
335
+ entry,
336
+ entryRole,
337
+ isFocused,
338
+ onClick
232
339
  }) {
233
- const [prefs, setPrefsRaw] = useState(() => readPrefs(defaultPosition, initialOpen));
234
- const [entries, setEntries] = useState([]);
235
- const [selectedCuid, setSelectedCuid] = useState(null);
236
- const [expandedGroupCuids, setExpandedGroupCuids] = useState(new Set);
237
- useEffect(() => core.subscribe(setEntries), [core]);
238
- const groups = useMemo(() => groupEntries(entries), [entries]);
239
- const handleGroupRowClick = (group) => {
240
- const repCuid = group.representative.cuid;
241
- const isExpanded = expandedGroupCuids.has(repCuid);
242
- const isSelected = selectedCuid === repCuid;
243
- if (group.rest.length === 0) {
244
- setSelectedCuid(isSelected ? null : repCuid);
245
- setExpandedGroupCuids(new Set);
246
- } else if (isSelected && isExpanded) {
247
- setSelectedCuid(null);
248
- setExpandedGroupCuids(new Set);
249
- } else {
250
- setSelectedCuid(repCuid);
251
- setExpandedGroupCuids(new Set([repCuid]));
252
- }
253
- };
254
- const setPrefs = (update) => {
255
- setPrefsRaw((prev) => {
256
- const next = { ...prev, ...update };
257
- writePrefs(next);
258
- return next;
259
- });
260
- };
261
- const { position, isOpen, dockedHeight, dockedWidth } = prefs;
262
- const dockSide = getDockSide(position);
263
- const isHorizDock = dockSide === "top" || dockSide === "bottom";
264
- const dockedSize = isHorizDock ? dockedHeight : dockedWidth;
265
- const selectedEntry = selectedCuid != null ? entries.find((e) => e.cuid === selectedCuid) : null;
266
- const runningCount = entries.filter((e) => e.status === "running").length;
267
- useEffect(() => {
268
- const sides = ["top", "bottom", "left", "right"];
269
- const clearAll = () => {
270
- sides.forEach((s) => {
271
- document.body.style.removeProperty(`margin-${s}`);
272
- });
273
- };
274
- if (!isOpen || dockSide == null) {
275
- clearAll();
276
- return clearAll;
277
- }
278
- clearAll();
279
- document.body.style.setProperty(`margin-${dockSide}`, `${dockedSize}px`);
280
- return clearAll;
281
- }, [dockSide, isOpen, dockedSize]);
282
- const closedAnchor = (() => {
283
- switch (position) {
284
- case "dock-bottom":
285
- return { bottom: "16px", right: "16px" };
286
- case "dock-top":
287
- return { top: "16px", right: "16px" };
288
- case "dock-left":
289
- return { top: "16px", left: "16px" };
290
- case "dock-right":
291
- return { top: "16px", right: "16px" };
292
- default:
293
- return {
294
- ...position.includes("right") ? { right: "16px" } : { left: "16px" },
295
- ...position.includes("bottom") ? { bottom: "16px" } : { top: "16px" }
296
- };
297
- }
298
- })();
299
- const baseStyle = {
300
- position: "fixed",
301
- zIndex: 2147483647,
302
- fontFamily: "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace",
303
- fontSize: "12px"
304
- };
305
- if (!isOpen) {
306
- return /* @__PURE__ */ jsxDEV("button", {
307
- onClick: () => setPrefs({ isOpen: true }),
308
- style: {
309
- ...baseStyle,
310
- ...closedAnchor,
311
- background: "#1e293b",
312
- color: "#94a3b8",
313
- border: "1px solid #334155",
314
- borderRadius: "6px",
315
- padding: "5px 10px",
316
- cursor: "pointer",
317
- lineHeight: "1.5"
318
- },
319
- children: [
320
- "⚡ actions",
321
- runningCount > 0 && /* @__PURE__ */ jsxDEV("span", {
322
- style: {
323
- marginLeft: "6px",
324
- color: "#60a5fa",
325
- animation: "__nice-action-pulse 1.2s ease-in-out infinite"
326
- },
327
- children: [
328
- runningCount,
329
- " running"
330
- ]
331
- }, undefined, true, undefined, this)
332
- ]
333
- }, undefined, true, undefined, this);
334
- }
335
- const panelStyle = dockSide != null ? {
336
- ...baseStyle,
337
- background: "#0f172a",
338
- border: "1px solid #1e293b",
339
- color: "#e2e8f0",
340
- display: "flex",
341
- flexDirection: "column",
342
- boxShadow: "0 -4px 24px rgba(0,0,0,0.4)",
343
- overflow: "hidden",
344
- ...dockSide === "bottom" ? { bottom: 0, left: 0, right: 0, height: `${dockedSize}px`, borderRadius: "8px 8px 0 0" } : dockSide === "top" ? { top: 0, left: 0, right: 0, height: `${dockedSize}px`, borderRadius: "0 0 8px 8px" } : dockSide === "left" ? { top: 0, left: 0, bottom: 0, width: `${dockedSize}px`, borderRadius: "0 8px 8px 0" } : { top: 0, right: 0, bottom: 0, width: `${dockedSize}px`, borderRadius: "8px 0 0 8px" }
345
- } : {
346
- ...baseStyle,
347
- ...closedAnchor,
348
- width: "460px",
349
- maxHeight: "560px",
350
- background: "#0f172a",
351
- border: "1px solid #1e293b",
352
- borderRadius: "10px",
353
- color: "#e2e8f0",
354
- display: "flex",
355
- flexDirection: "column",
356
- boxShadow: "0 25px 50px rgba(0,0,0,0.5)",
357
- overflow: "hidden"
358
- };
359
- const virtualListProps = {
360
- groups,
361
- selectedCuid,
362
- expandedGroupCuids,
363
- onGroupClick: handleGroupRowClick,
364
- onSubClick: (cuid, isSelected) => setSelectedCuid(isSelected ? null : cuid)
365
- };
366
- return /* @__PURE__ */ jsxDEV("div", {
367
- style: panelStyle,
340
+ const color = STATUS_COLOR[entry.status];
341
+ const symbol = STATUS_SYMBOL[entry.status];
342
+ return /* @__PURE__ */ jsxDEV4("div", {
343
+ onClick,
344
+ style: {
345
+ background: isFocused ? "#1e2e45" : "#1e293b",
346
+ borderRadius: "4px",
347
+ borderLeft: isFocused ? "2px solid #a78bfa" : "2px solid transparent",
348
+ padding: isFocused ? "5px 8px 5px 6px" : "5px 8px",
349
+ display: "flex",
350
+ alignItems: "center",
351
+ gap: "8px",
352
+ cursor: "pointer"
353
+ },
368
354
  children: [
369
- dockSide != null && /* @__PURE__ */ jsxDEV(ResizeHandle, {
370
- dockSide,
371
- dockedSize,
372
- onChange: (size) => setPrefs(isHorizDock ? { dockedHeight: size } : { dockedWidth: size })
355
+ /* @__PURE__ */ jsxDEV4("span", {
356
+ style: {
357
+ color: entryRole === "caller" ? "#94a3b8" : "#a78bfa",
358
+ fontSize: "9px",
359
+ flexShrink: 0,
360
+ minWidth: "34px",
361
+ fontFamily: "inherit"
362
+ },
363
+ children: entryRole === "caller" ? "↑ from" : "↓ call"
373
364
  }, undefined, false, undefined, this),
374
- /* @__PURE__ */ jsxDEV(PanelHeader, {
375
- position,
376
- onPositionChange: (p) => setPrefs({ position: p }),
377
- onClose: () => setPrefs({ isOpen: false }),
378
- onClear: entries.length > 0 ? () => {
379
- core.clear();
380
- setSelectedCuid(null);
381
- setExpandedGroupCuids(new Set);
382
- } : undefined
365
+ /* @__PURE__ */ jsxDEV4("span", {
366
+ style: { color, fontSize: "10px", flexShrink: 0 },
367
+ children: symbol
383
368
  }, undefined, false, undefined, this),
384
- isHorizDock ? /* @__PURE__ */ jsxDEV("div", {
385
- style: { flex: 1, display: "flex", overflow: "hidden" },
369
+ /* @__PURE__ */ jsxDEV4("span", {
370
+ style: {
371
+ color: "#cbd5e1",
372
+ fontSize: "11px",
373
+ flex: 1,
374
+ overflow: "hidden",
375
+ textOverflow: "ellipsis",
376
+ whiteSpace: "nowrap",
377
+ gap: "0.5em",
378
+ display: "flex",
379
+ alignItems: "center",
380
+ justifyContent: "center"
381
+ },
386
382
  children: [
387
- /* @__PURE__ */ jsxDEV(VirtualActionList, {
388
- ...virtualListProps,
389
- style: { width: "340px", flexShrink: 0, overflowY: "auto", borderRight: "1px solid #1e293b" }
383
+ /* @__PURE__ */ jsxDEV4("span", {
384
+ children: entry.actionId
390
385
  }, undefined, false, undefined, this),
391
- /* @__PURE__ */ jsxDEV("div", {
392
- style: { flex: 1, overflowY: "auto" },
393
- children: selectedEntry != null ? /* @__PURE__ */ jsxDEV(ActionDetailPanel, {
394
- entry: selectedEntry
395
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV("div", {
396
- style: { padding: "24px", textAlign: "center", color: "#475569", fontSize: "11px" },
397
- children: "Select an action to inspect"
398
- }, undefined, false, undefined, this)
386
+ /* @__PURE__ */ jsxDEV4(DomainChip, {
387
+ domain: entry.domain,
388
+ allDomains: entry.allDomains,
389
+ size: "sm"
399
390
  }, undefined, false, undefined, this)
400
391
  ]
401
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV(Fragment2, {
392
+ }, undefined, true, undefined, this),
393
+ /* @__PURE__ */ jsxDEV4("span", {
394
+ style: { color: "#475569", fontSize: "10px", flexShrink: 0 },
395
+ children: /* @__PURE__ */ jsxDEV4(DurationDisplay, {
396
+ entry
397
+ }, undefined, false, undefined, this)
398
+ }, undefined, false, undefined, this)
399
+ ]
400
+ }, undefined, true, undefined, this);
401
+ }
402
+ function CallStackSection({
403
+ parent,
404
+ childEntries,
405
+ focusedChildCuid,
406
+ onFocusChild,
407
+ onSelectParent
408
+ }) {
409
+ if (parent == null && childEntries.length === 0)
410
+ return null;
411
+ return /* @__PURE__ */ jsxDEV4("div", {
412
+ children: [
413
+ /* @__PURE__ */ jsxDEV4(SectionLabel, {
414
+ label: "Call Stack",
415
+ color: "#a78bfa"
416
+ }, undefined, false, undefined, this),
417
+ /* @__PURE__ */ jsxDEV4("div", {
418
+ style: { display: "flex", flexDirection: "column", gap: "2px" },
402
419
  children: [
403
- /* @__PURE__ */ jsxDEV(VirtualActionList, {
404
- ...virtualListProps,
405
- style: {
406
- overflowY: "auto",
407
- flex: 1,
408
- minHeight: "80px",
409
- borderBottom: selectedEntry != null ? "1px solid #1e293b" : "none"
410
- }
420
+ parent != null && /* @__PURE__ */ jsxDEV4(CallStackLink, {
421
+ entry: parent,
422
+ entryRole: "caller",
423
+ isFocused: false,
424
+ onClick: () => onSelectParent(parent.cuid)
411
425
  }, undefined, false, undefined, this),
412
- selectedEntry != null && /* @__PURE__ */ jsxDEV("div", {
413
- style: { flexShrink: 0, maxHeight: "45%", overflow: "hidden" },
414
- children: /* @__PURE__ */ jsxDEV(ActionDetailPanel, {
415
- entry: selectedEntry
416
- }, undefined, false, undefined, this)
417
- }, undefined, false, undefined, this)
426
+ childEntries.map((child) => /* @__PURE__ */ jsxDEV4(CallStackLink, {
427
+ entry: child,
428
+ entryRole: "called",
429
+ isFocused: focusedChildCuid === child.cuid,
430
+ onClick: () => onFocusChild(child.cuid)
431
+ }, child.cuid, false, undefined, this))
418
432
  ]
419
433
  }, undefined, true, undefined, this)
420
434
  ]
421
435
  }, undefined, true, undefined, this);
422
436
  }
423
- function VirtualActionList({
424
- groups,
425
- selectedCuid,
426
- expandedGroupCuids,
427
- onGroupClick,
428
- onSubClick,
437
+
438
+ // src/devtools/browser/components/Chip.tsx
439
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
440
+ function Chip({
441
+ color,
442
+ borderColor,
443
+ fontSize = "8px",
444
+ padding = "1px 4px",
445
+ children,
429
446
  style
430
447
  }) {
431
- const outerRef = useRef(null);
432
- const latestTime = groups[0]?.representative.startTime;
433
- const flatItems = useMemo(() => {
434
- const result = [];
435
- for (let gi = 0;gi < groups.length; gi++) {
436
- const group = groups[gi];
437
- const repCuid = group.representative.cuid;
438
- const isExpanded = expandedGroupCuids.has(repCuid) || group.rest.some((e) => e.cuid === selectedCuid);
439
- result.push({ type: "group", group, groupIndex: gi });
440
- if (isExpanded) {
441
- for (const entry of group.rest) {
442
- result.push({ type: "sub", entry, group });
443
- }
444
- }
445
- }
446
- return result;
447
- }, [groups, expandedGroupCuids, selectedCuid]);
448
- const flatItemsRef = useRef(flatItems);
449
- flatItemsRef.current = flatItems;
450
- const virtualizer = useVirtualizer({
451
- count: flatItems.length,
452
- getScrollElement: () => outerRef.current,
453
- estimateSize: (i) => flatItemsRef.current[i]?.type === "sub" ? SUB_ROW_HEIGHT : ROW_HEIGHT,
454
- overscan: 5,
455
- measureElement: (el) => el.getBoundingClientRect().height
456
- });
457
- const prevGroupLenRef = useRef(groups.length);
458
- useLayoutEffect(() => {
459
- const el = outerRef.current;
460
- if (el == null)
461
- return;
462
- const prev = prevGroupLenRef.current;
463
- prevGroupLenRef.current = groups.length;
464
- const added = groups.length - prev;
465
- if (added > 0 && el.scrollTop > 10) {
466
- el.scrollTop += added * ROW_HEIGHT;
467
- }
468
- }, [groups]);
469
- const prevSelectedRef = useRef(selectedCuid);
470
- useEffect(() => {
471
- if (selectedCuid === prevSelectedRef.current)
472
- return;
473
- prevSelectedRef.current = selectedCuid;
474
- if (selectedCuid == null)
475
- return;
476
- const idx = flatItemsRef.current.findIndex((item) => item.type === "group" && (item.group.representative.cuid === selectedCuid || item.group.rest.some((e) => e.cuid === selectedCuid)) || item.type === "sub" && item.entry.cuid === selectedCuid);
477
- if (idx >= 0)
478
- virtualizer.scrollToIndex(idx, { align: "auto" });
479
- }, [selectedCuid, virtualizer]);
480
- if (groups.length === 0) {
481
- return /* @__PURE__ */ jsxDEV("div", {
482
- style,
483
- children: /* @__PURE__ */ jsxDEV("div", {
484
- style: { padding: "24px", textAlign: "center", color: "#475569" },
485
- children: "No actions recorded yet"
486
- }, undefined, false, undefined, this)
487
- }, undefined, false, undefined, this);
488
- }
489
- const virtualItems = virtualizer.getVirtualItems();
490
- return /* @__PURE__ */ jsxDEV("div", {
491
- ref: outerRef,
492
- style,
493
- children: /* @__PURE__ */ jsxDEV("div", {
494
- style: { height: `${virtualizer.getTotalSize()}px`, position: "relative" },
495
- children: virtualItems.map((vi) => {
496
- const item = flatItems[vi.index];
497
- if (item == null)
498
- return null;
499
- return /* @__PURE__ */ jsxDEV("div", {
500
- "data-index": vi.index,
501
- ref: virtualizer.measureElement,
502
- style: {
503
- position: "absolute",
504
- top: 0,
505
- left: 0,
506
- width: "100%",
507
- transform: `translateY(${vi.start}px)`
508
- },
509
- children: item.type === "group" ? /* @__PURE__ */ jsxDEV(ActionEntryRow, {
510
- entry: item.group.representative,
511
- isSelected: selectedCuid === item.group.representative.cuid,
512
- groupCount: item.group.rest.length > 0 ? item.group.rest.length + 1 : undefined,
513
- isLatest: item.groupIndex === 0,
514
- latestTime,
515
- onClick: () => onGroupClick(item.group)
516
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV(ActionEntryRow, {
517
- entry: item.entry,
518
- isSelected: selectedCuid === item.entry.cuid,
519
- isSubEntry: true,
520
- isLatest: false,
521
- latestTime,
522
- onClick: () => onSubClick(item.entry.cuid, selectedCuid === item.entry.cuid)
523
- }, undefined, false, undefined, this)
524
- }, vi.key, false, undefined, this);
525
- })
526
- }, undefined, false, undefined, this)
448
+ return /* @__PURE__ */ jsxDEV5("span", {
449
+ style: {
450
+ color,
451
+ fontSize,
452
+ background: "#0f172a",
453
+ border: `1px solid ${borderColor}`,
454
+ padding,
455
+ borderRadius: "3px",
456
+ flexShrink: 0,
457
+ whiteSpace: "nowrap",
458
+ ...style
459
+ },
460
+ children
527
461
  }, undefined, false, undefined, this);
528
462
  }
529
- function PanelHeader({
530
- position,
531
- onPositionChange,
532
- onClose,
533
- onClear
463
+
464
+ // src/devtools/browser/components/ChildDispatchChips.tsx
465
+ import { jsxDEV as jsxDEV6, Fragment as Fragment2 } from "react/jsx-dev-runtime";
466
+ function ChildDispatchChips({
467
+ labels,
468
+ size = "sm"
534
469
  }) {
535
- return /* @__PURE__ */ jsxDEV("div", {
536
- style: {
537
- display: "flex",
538
- alignItems: "center",
539
- justifyContent: "space-between",
540
- padding: "8px 12px",
541
- background: "#1e293b",
542
- borderBottom: "1px solid #0f172a",
543
- flexShrink: 0
544
- },
470
+ if (labels == null || labels.length === 0)
471
+ return null;
472
+ const fontSize = size === "md" ? "9px" : "8px";
473
+ const padding = size === "md" ? "1px 4px" : "1px 3px";
474
+ return /* @__PURE__ */ jsxDEV6(Fragment2, {
475
+ children: labels.map((label) => /* @__PURE__ */ jsxDEV6(Chip, {
476
+ color: "#c084fc",
477
+ borderColor: "#581c87",
478
+ fontSize,
479
+ padding,
480
+ children: [
481
+ "↳ ",
482
+ label
483
+ ]
484
+ }, label, true, undefined, this))
485
+ }, undefined, false, undefined, this);
486
+ }
487
+
488
+ // src/devtools/browser/components/DetailSection.tsx
489
+ import { useState as useState2 } from "react";
490
+ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
491
+ var COMPACT_CHAR_LIMIT = 120;
492
+ function DetailSection({
493
+ label,
494
+ value,
495
+ color = "#60a5fa"
496
+ }) {
497
+ const [expanded, setExpanded] = useState2(false);
498
+ const fullJson = safeStringify(value, 2);
499
+ const compactJson = safeStringify(value, 0);
500
+ const canExpand = fullJson !== compactJson || compactJson.length > COMPACT_CHAR_LIMIT;
501
+ const compactDisplay = compactJson.length > COMPACT_CHAR_LIMIT ? `${compactJson.slice(0, COMPACT_CHAR_LIMIT)}…` : compactJson;
502
+ return /* @__PURE__ */ jsxDEV7("div", {
545
503
  children: [
546
- /* @__PURE__ */ jsxDEV("span", {
547
- style: { color: "#60a5fa", fontWeight: "bold", fontSize: "11px" },
548
- children: "⚡ nice-action devtools"
549
- }, undefined, false, undefined, this),
550
- /* @__PURE__ */ jsxDEV("div", {
551
- style: { display: "flex", gap: "10px", alignItems: "center" },
504
+ /* @__PURE__ */ jsxDEV7("div", {
505
+ style: {
506
+ display: "flex",
507
+ alignItems: "center",
508
+ justifyContent: "space-between",
509
+ marginBottom: "3px"
510
+ },
552
511
  children: [
553
- /* @__PURE__ */ jsxDEV(PositionPicker, {
554
- position,
555
- onChange: onPositionChange
556
- }, undefined, false, undefined, this),
557
- onClear != null && /* @__PURE__ */ jsxDEV("button", {
558
- onClick: onClear,
512
+ /* @__PURE__ */ jsxDEV7("div", {
559
513
  style: {
560
- background: "none",
561
- border: "none",
562
- color: "#475569",
563
- cursor: "pointer",
564
- fontSize: "11px",
565
- padding: "0"
514
+ color,
515
+ fontSize: "10px",
516
+ textTransform: "uppercase",
517
+ letterSpacing: "0.05em"
566
518
  },
567
- children: "clear"
519
+ children: label
568
520
  }, undefined, false, undefined, this),
569
- /* @__PURE__ */ jsxDEV("button", {
570
- onClick: onClose,
571
- style: {
572
- background: "none",
573
- border: "none",
574
- color: "#475569",
575
- cursor: "pointer",
576
- fontSize: "16px",
577
- padding: "0",
578
- lineHeight: "1"
579
- },
580
- children: "×"
521
+ canExpand && /* @__PURE__ */ jsxDEV7("span", {
522
+ style: { color: "#334155", fontSize: "11px" },
523
+ children: expanded ? "▾" : "▸"
581
524
  }, undefined, false, undefined, this)
582
525
  ]
583
- }, undefined, true, undefined, this)
584
- ]
585
- }, undefined, true, undefined, this);
586
- }
587
- var POSITION_GRID = [
588
- ["top-left", "dock-top", "top-right"],
589
- ["dock-left", null, "dock-right"],
590
- ["bottom-left", "dock-bottom", "bottom-right"]
591
- ];
592
- function PositionPicker({
593
- position,
594
- onChange
595
- }) {
596
- return /* @__PURE__ */ jsxDEV("div", {
597
- title: "Move / dock panel",
598
- style: { display: "grid", gridTemplateColumns: "repeat(3, 9px)", gap: "2px", padding: "2px" },
599
- children: POSITION_GRID.flat().map((pos) => {
600
- if (pos == null)
601
- return /* @__PURE__ */ jsxDEV("div", {
602
- style: { width: "9px", height: "9px" }
603
- }, "center-empty", false, undefined, this);
604
- const isDock = pos.startsWith("dock-");
605
- const isTopBottom = pos === "dock-top" || pos === "dock-bottom";
606
- const isActive = pos === position;
607
- return /* @__PURE__ */ jsxDEV("div", {
608
- title: pos,
609
- onClick: () => onChange(pos),
610
- style: { width: "9px", height: "9px", display: "flex", alignItems: "center", justifyContent: "center", cursor: "pointer" },
611
- children: /* @__PURE__ */ jsxDEV("div", {
612
- style: {
613
- width: isDock ? isTopBottom ? "9px" : "3px" : "7px",
614
- height: isDock ? isTopBottom ? "3px" : "9px" : "7px",
615
- borderRadius: isDock ? "1px" : "50%",
616
- background: isActive ? "#60a5fa" : "#334155"
617
- }
618
- }, undefined, false, undefined, this)
619
- }, pos, false, undefined, this);
620
- })
621
- }, undefined, false, undefined, this);
622
- }
623
- function ResizeHandle({
624
- dockSide,
625
- dockedSize,
626
- onChange
627
- }) {
628
- const isHoriz = dockSide === "left" || dockSide === "right";
629
- const onMouseDown = (e) => {
630
- e.preventDefault();
631
- const startCoord = isHoriz ? e.clientX : e.clientY;
632
- const startSize = dockedSize;
633
- const maxSize = isHoriz ? window.innerWidth * 0.85 : window.innerHeight * 0.85;
634
- const sign = dockSide === "bottom" || dockSide === "right" ? -1 : 1;
635
- const onMove = (me) => {
636
- const delta = (isHoriz ? me.clientX : me.clientY) - startCoord;
637
- onChange(Math.max(DOCKED_SIZE_MIN, Math.min(maxSize, startSize + sign * delta)));
638
- };
639
- const onUp = () => {
640
- window.removeEventListener("mousemove", onMove);
641
- window.removeEventListener("mouseup", onUp);
642
- };
643
- window.addEventListener("mousemove", onMove);
644
- window.addEventListener("mouseup", onUp);
645
- };
646
- const edgeStyle = dockSide === "bottom" ? { top: 0, left: 0, right: 0, height: "5px", cursor: "ns-resize" } : dockSide === "top" ? { bottom: 0, left: 0, right: 0, height: "5px", cursor: "ns-resize" } : dockSide === "right" ? { top: 0, bottom: 0, left: 0, width: "5px", cursor: "ew-resize" } : { top: 0, bottom: 0, right: 0, width: "5px", cursor: "ew-resize" };
647
- return /* @__PURE__ */ jsxDEV("div", {
648
- onMouseDown,
649
- style: {
650
- position: "absolute",
651
- zIndex: 10,
652
- background: "transparent",
653
- ...edgeStyle
654
- }
655
- }, undefined, false, undefined, this);
656
- }
657
- function formatRelativeAge(ms) {
658
- if (ms < 1000)
659
- return `${ms}ms`;
660
- if (ms < 60000)
661
- return `${(ms / 1000).toFixed(1)}s`;
662
- return `${Math.floor(ms / 60000)}m`;
663
- }
664
- function ActionEntryRow({
665
- entry,
666
- isSelected,
667
- onClick,
668
- groupCount,
669
- isSubEntry = false,
670
- isLatest = false,
671
- latestTime
672
- }) {
673
- const color = STATUS_COLOR[entry.status];
674
- const symbol = STATUS_SYMBOL[entry.status];
675
- const parentDomains = entry.allDomains.slice(0, -1);
676
- const breadcrumb = parentDomains.length > 0 ? `[${parentDomains.join(".")}]` : "";
677
- const duration = entry.endTime != null ? `${entry.endTime - entry.startTime}ms` : null;
678
- const timestamp = new Date(entry.startTime).toLocaleTimeString([], {
679
- hour: "2-digit",
680
- minute: "2-digit",
681
- second: "2-digit",
682
- hour12: false
683
- });
684
- const firstHop = entry.meta.routing[0];
685
- const handlerLabel = firstHop == null ? null : firstHop.handlerType === "local" ? "local" : `→ ${firstHop.transport ?? "ext"}`;
686
- const handlerColor = handlerLabel === "local" ? "#34d399" : "#fbbf24";
687
- const handlerBorder = handlerLabel === "local" ? "#14532d" : "#78350f";
688
- if (isSubEntry) {
689
- return /* @__PURE__ */ jsxDEV("div", {
690
- "data-cuid": entry.cuid,
691
- onClick,
692
- style: {
693
- display: "flex",
694
- alignItems: "center",
695
- gap: "8px",
696
- padding: "4px 10px 4px 26px",
697
- cursor: "pointer",
698
- background: isSelected ? "#131f35" : "#070d18",
699
- borderBottom: "1px solid #0f172a",
700
- borderLeft: isSelected ? "2px solid #3b82f6" : "2px solid transparent",
701
- userSelect: "none"
702
- },
703
- children: [
704
- /* @__PURE__ */ jsxDEV("span", {
705
- style: { color: "#334155", fontSize: "10px", flexShrink: 0 },
706
- children: symbol
707
- }, undefined, false, undefined, this),
708
- /* @__PURE__ */ jsxDEV("span", {
709
- style: {
710
- flex: 1,
711
- color: "#475569",
712
- fontSize: "11px",
713
- overflow: "hidden",
714
- textOverflow: "ellipsis",
715
- whiteSpace: "nowrap",
716
- minWidth: 0
717
- },
718
- children: [
719
- entry.actionId,
720
- /* @__PURE__ */ jsxDEV("span", {
721
- style: { color: "#334155", marginLeft: "4px" },
722
- children: [
723
- "(",
724
- entry.domain,
725
- ")"
726
- ]
727
- }, undefined, true, undefined, this)
728
- ]
729
- }, undefined, true, undefined, this),
730
- /* @__PURE__ */ jsxDEV("span", {
731
- style: { color: "#334155", fontSize: "11px", flexShrink: 0 },
732
- children: duration ?? /* @__PURE__ */ jsxDEV(RunningTimer, {
733
- startTime: entry.startTime
734
- }, undefined, false, undefined, this)
735
- }, undefined, false, undefined, this)
736
- ]
737
- }, undefined, true, undefined, this);
738
- }
739
- return /* @__PURE__ */ jsxDEV("div", {
740
- "data-cuid": entry.cuid,
741
- onClick,
742
- style: {
743
- display: "flex",
744
- alignItems: "flex-start",
745
- gap: "8px",
746
- padding: isSelected ? "6px 12px 6px 10px" : "6px 12px",
747
- cursor: "pointer",
748
- background: isSelected ? "#131f35" : "transparent",
749
- borderBottom: "1px solid #0f172a",
750
- borderLeft: isSelected ? "2px solid #60a5fa" : "2px solid transparent",
751
- userSelect: "none"
752
- },
753
- children: [
754
- /* @__PURE__ */ jsxDEV("span", {
755
- style: { flexShrink: 0, paddingTop: "2px" },
756
- children: isLatest ? /* @__PURE__ */ jsxDEV("span", {
757
- style: {
758
- color: "#60a5fa",
759
- fontSize: "8px",
760
- background: "#0f172a",
761
- border: "1px solid #1e3a5f",
762
- padding: "1px 4px",
763
- borderRadius: "3px"
764
- },
765
- children: "latest"
766
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV("span", {
767
- style: {
768
- color: "#475569",
769
- fontSize: "8px",
770
- background: "#0f172a",
771
- border: "1px solid #1e293b",
772
- padding: "1px 4px",
773
- borderRadius: "3px",
774
- whiteSpace: "nowrap"
775
- },
776
- children: [
777
- "+",
778
- latestTime != null ? formatRelativeAge(latestTime - entry.startTime) : "?"
779
- ]
780
- }, undefined, true, undefined, this)
781
- }, undefined, false, undefined, this),
782
- /* @__PURE__ */ jsxDEV("span", {
526
+ }, undefined, true, undefined, this),
527
+ expanded ? /* @__PURE__ */ jsxDEV7("div", {
528
+ onClick: canExpand ? () => setExpanded(false) : undefined,
783
529
  style: {
784
- color,
785
- fontSize: "10px",
786
- flexShrink: 0,
787
- paddingTop: "2px",
788
- animation: entry.status === "running" ? "__nice-action-pulse 1.2s ease-in-out infinite" : undefined
530
+ margin: 0,
531
+ padding: "6px 8px",
532
+ background: "#1e293b",
533
+ borderRadius: "4px",
534
+ fontSize: "11px",
535
+ color: "#cbd5e1",
536
+ overflowX: "auto",
537
+ textAlign: "left",
538
+ whiteSpace: "pre-wrap",
539
+ wordBreak: "break-all",
540
+ cursor: canExpand ? "pointer" : "default"
789
541
  },
790
- children: symbol
791
- }, undefined, false, undefined, this),
792
- /* @__PURE__ */ jsxDEV("div", {
793
- style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: "3px" },
794
- children: [
795
- /* @__PURE__ */ jsxDEV("div", {
796
- style: { display: "flex", alignItems: "baseline", minWidth: 0, overflow: "hidden" },
797
- children: [
798
- /* @__PURE__ */ jsxDEV("span", {
799
- style: {
800
- color: "#cbd5e1",
801
- fontSize: "11px",
802
- overflow: "hidden",
803
- textOverflow: "ellipsis",
804
- whiteSpace: "nowrap",
805
- flexShrink: 1,
806
- minWidth: "24px"
807
- },
808
- children: entry.actionId
809
- }, undefined, false, undefined, this),
810
- /* @__PURE__ */ jsxDEV("span", {
811
- style: {
812
- color: "#7592b6",
813
- fontSize: "11px",
814
- marginLeft: "4px",
815
- whiteSpace: "nowrap",
816
- overflow: "hidden",
817
- textOverflow: "ellipsis",
818
- flexShrink: 2,
819
- minWidth: "24px"
820
- },
821
- children: [
822
- "(",
823
- entry.domain,
824
- ")"
825
- ]
826
- }, undefined, true, undefined, this)
827
- ]
828
- }, undefined, true, undefined, this),
829
- /* @__PURE__ */ jsxDEV("div", {
830
- style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: "6px" },
831
- children: [
832
- /* @__PURE__ */ jsxDEV("div", {
833
- style: { display: "flex", alignItems: "center", gap: "5px", minWidth: 0, overflow: "hidden" },
834
- children: [
835
- handlerLabel != null && /* @__PURE__ */ jsxDEV("span", {
836
- style: {
837
- color: handlerColor,
838
- fontSize: "8px",
839
- background: "#0f172a",
840
- border: `1px solid ${handlerBorder}`,
841
- padding: "1px 4px",
842
- borderRadius: "3px",
843
- flexShrink: 0,
844
- whiteSpace: "nowrap"
845
- },
846
- children: handlerLabel
847
- }, undefined, false, undefined, this),
848
- breadcrumb !== "" && /* @__PURE__ */ jsxDEV("span", {
849
- style: {
850
- color: "#475569",
851
- fontSize: "9px",
852
- overflow: "hidden",
853
- textOverflow: "ellipsis",
854
- whiteSpace: "nowrap",
855
- minWidth: 0
856
- },
857
- children: breadcrumb
858
- }, undefined, false, undefined, this)
859
- ]
860
- }, undefined, true, undefined, this),
861
- /* @__PURE__ */ jsxDEV("div", {
862
- style: { display: "flex", alignItems: "center", gap: "6px", flexShrink: 0 },
863
- children: [
864
- groupCount != null && /* @__PURE__ */ jsxDEV("span", {
865
- style: {
866
- color: "#3b82f6",
867
- fontSize: "9px",
868
- background: "#0f172a",
869
- border: "1px solid #1e3a5f",
870
- padding: "1px 5px",
871
- borderRadius: "3px"
872
- },
873
- children: [
874
- "×",
875
- groupCount
876
- ]
877
- }, undefined, true, undefined, this),
878
- /* @__PURE__ */ jsxDEV("span", {
879
- style: { color: "#1e3a5f", fontSize: "10px", letterSpacing: "0.02em" },
880
- children: timestamp
881
- }, undefined, false, undefined, this),
882
- /* @__PURE__ */ jsxDEV("span", {
883
- style: { color: "#475569", fontSize: "11px" },
884
- children: duration ?? /* @__PURE__ */ jsxDEV(RunningTimer, {
885
- startTime: entry.startTime
886
- }, undefined, false, undefined, this)
887
- }, undefined, false, undefined, this)
888
- ]
889
- }, undefined, true, undefined, this)
890
- ]
891
- }, undefined, true, undefined, this)
892
- ]
893
- }, undefined, true, undefined, this)
542
+ children: fullJson
543
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV7("div", {
544
+ onClick: canExpand ? () => setExpanded(true) : undefined,
545
+ style: {
546
+ padding: "5px 8px",
547
+ background: "#1e293b",
548
+ borderRadius: "4px",
549
+ fontSize: "11px",
550
+ color: "#94a3b8",
551
+ fontFamily: "inherit",
552
+ whiteSpace: "nowrap",
553
+ overflow: "hidden",
554
+ textOverflow: "ellipsis",
555
+ cursor: canExpand ? "pointer" : "default"
556
+ },
557
+ children: compactDisplay
558
+ }, undefined, false, undefined, this)
894
559
  ]
895
560
  }, undefined, true, undefined, this);
896
561
  }
897
- function RunningTimer({ startTime }) {
898
- const [elapsed, setElapsed] = useState(() => Date.now() - startTime);
899
- useEffect(() => {
900
- const interval = setInterval(() => setElapsed(Date.now() - startTime), 100);
901
- return () => clearInterval(interval);
902
- }, [startTime]);
903
- return /* @__PURE__ */ jsxDEV(Fragment2, {
904
- children: [
905
- elapsed,
906
- "ms"
907
- ]
908
- }, undefined, true, undefined, this);
562
+
563
+ // src/devtools/browser/components/HandlerChips.tsx
564
+ import { jsxDEV as jsxDEV8, Fragment as Fragment3 } from "react/jsx-dev-runtime";
565
+ function getExternalLabel(hop) {
566
+ if (hop.handlerType !== "external")
567
+ return null;
568
+ return hop.handlerClient != null ? `${hop.transport ?? "ext"} → ${hop.handlerClient.envId}` : `→ ${hop.transport ?? "ext"}`;
909
569
  }
910
- function ActionDetailPanel({ entry }) {
911
- return /* @__PURE__ */ jsxDEV("div", {
912
- style: {
913
- flex: 1,
914
- overflowY: "auto",
915
- padding: "10px 12px",
916
- display: "flex",
917
- flexDirection: "column",
918
- gap: "8px"
919
- },
570
+ function HandlerChips({ entry, size, alwaysShowLocal = false }) {
571
+ const firstHop = entry.meta.routing[0];
572
+ const localEnvId = firstHop != null ? firstHop.runtime.envId : null;
573
+ const externalLabel = firstHop != null ? getExternalLabel(firstHop) : null;
574
+ const fontSize = size === "md" ? "9px" : "8px";
575
+ const padding = size === "md" ? "1px 5px" : "1px 4px";
576
+ return /* @__PURE__ */ jsxDEV8(Fragment3, {
920
577
  children: [
921
- /* @__PURE__ */ jsxDEV(MetaSection, {
922
- entry
578
+ localEnvId != null && (alwaysShowLocal || externalLabel == null) && /* @__PURE__ */ jsxDEV8(Chip, {
579
+ color: "#34d399",
580
+ borderColor: "#14532d",
581
+ fontSize,
582
+ padding,
583
+ children: localEnvId
923
584
  }, undefined, false, undefined, this),
924
- /* @__PURE__ */ jsxDEV(RoutingSection, {
925
- entry
926
- }, undefined, false, undefined, this),
927
- /* @__PURE__ */ jsxDEV(DetailSection, {
928
- label: "Input",
929
- value: entry.input
930
- }, undefined, false, undefined, this),
931
- entry.status === "success" && /* @__PURE__ */ jsxDEV(DetailSection, {
932
- label: "Output",
933
- value: entry.output,
934
- color: "#4ade80"
935
- }, undefined, false, undefined, this),
936
- entry.status === "failed" && /* @__PURE__ */ jsxDEV(DetailSection, {
937
- label: "Error",
938
- value: entry.error,
939
- color: "#f87171"
940
- }, undefined, false, undefined, this),
941
- entry.status === "aborted" && entry.abortReason != null && /* @__PURE__ */ jsxDEV(DetailSection, {
942
- label: "Abort Reason",
943
- value: entry.abortReason,
944
- color: "#9ca3af"
945
- }, undefined, false, undefined, this),
946
- entry.progressUpdates.length > 0 && /* @__PURE__ */ jsxDEV(DetailSection, {
947
- label: `Progress (${entry.progressUpdates.length})`,
948
- value: entry.progressUpdates
585
+ externalLabel != null && /* @__PURE__ */ jsxDEV8(Chip, {
586
+ color: "#fbbf24",
587
+ borderColor: "#78350f",
588
+ fontSize,
589
+ padding,
590
+ children: externalLabel
949
591
  }, undefined, false, undefined, this)
950
592
  ]
951
593
  }, undefined, true, undefined, this);
952
594
  }
953
- function MetaSection({ entry }) {
954
- const [expanded, setExpanded] = useState(false);
955
- const { meta, cuid, startTime: _startTime } = entry;
956
- const expandedRows = [
957
- { label: "cuid", value: cuid },
958
- { label: "origin", value: meta.originClient.envId },
959
- ...meta.originClient.perId != null ? [{ label: "perId", value: meta.originClient.perId }] : [],
960
- ...meta.originClient.insId != null ? [{ label: "insId", value: meta.originClient.insId }] : []
961
- ];
962
- return /* @__PURE__ */ jsxDEV("div", {
595
+
596
+ // src/devtools/browser/components/MetaSection.tsx
597
+ import { useState as useState3 } from "react";
598
+ import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
599
+ function MetaChip({ label, value }) {
600
+ return /* @__PURE__ */ jsxDEV9("span", {
601
+ style: { whiteSpace: "nowrap" },
963
602
  children: [
964
- /* @__PURE__ */ jsxDEV("div", {
603
+ /* @__PURE__ */ jsxDEV9("span", {
604
+ style: { color: "#475569" },
605
+ children: [
606
+ label,
607
+ " "
608
+ ]
609
+ }, undefined, true, undefined, this),
610
+ /* @__PURE__ */ jsxDEV9("span", {
611
+ style: { color: "#cbd5e1" },
612
+ children: value
613
+ }, undefined, false, undefined, this)
614
+ ]
615
+ }, undefined, true, undefined, this);
616
+ }
617
+ function MetaSection({ entry }) {
618
+ const [expanded, setExpanded] = useState3(false);
619
+ const { meta, cuid } = entry;
620
+ const expandedRows = [
621
+ { label: "cuid", value: cuid },
622
+ { label: "origin", value: meta.originClient.envId },
623
+ ...meta.originClient.perId != null ? [{ label: "perId", value: meta.originClient.perId }] : [],
624
+ ...meta.originClient.insId != null ? [{ label: "insId", value: meta.originClient.insId }] : []
625
+ ];
626
+ return /* @__PURE__ */ jsxDEV9("div", {
627
+ children: [
628
+ /* @__PURE__ */ jsxDEV9("div", {
965
629
  style: {
966
630
  display: "flex",
967
631
  alignItems: "center",
968
632
  justifyContent: "space-between",
969
- marginBottom: "3px",
970
- userSelect: "none"
633
+ marginBottom: "3px"
971
634
  },
972
635
  children: [
973
- /* @__PURE__ */ jsxDEV("div", {
636
+ /* @__PURE__ */ jsxDEV9("div", {
974
637
  style: {
975
638
  color: "#60a5fa",
976
639
  fontSize: "10px",
@@ -979,21 +642,16 @@ function MetaSection({ entry }) {
979
642
  },
980
643
  children: "Meta"
981
644
  }, undefined, false, undefined, this),
982
- /* @__PURE__ */ jsxDEV("span", {
645
+ /* @__PURE__ */ jsxDEV9("span", {
983
646
  style: { color: "#334155", fontSize: "11px" },
984
647
  children: expanded ? "▾" : "▸"
985
648
  }, undefined, false, undefined, this)
986
649
  ]
987
650
  }, undefined, true, undefined, this),
988
- expanded ? /* @__PURE__ */ jsxDEV("div", {
651
+ expanded ? /* @__PURE__ */ jsxDEV9("div", {
989
652
  onClick: () => setExpanded(false),
990
- style: {
991
- background: "#1e293b",
992
- borderRadius: "4px",
993
- overflow: "hidden",
994
- cursor: "pointer"
995
- },
996
- children: expandedRows.map(({ label, value }, i) => /* @__PURE__ */ jsxDEV("div", {
653
+ style: { background: "#1e293b", borderRadius: "4px", overflow: "hidden", cursor: "pointer" },
654
+ children: expandedRows.map(({ label, value }, i) => /* @__PURE__ */ jsxDEV9("div", {
997
655
  style: {
998
656
  display: "grid",
999
657
  gridTemplateColumns: "52px 1fr",
@@ -1003,22 +661,17 @@ function MetaSection({ entry }) {
1003
661
  alignItems: "start"
1004
662
  },
1005
663
  children: [
1006
- /* @__PURE__ */ jsxDEV("span", {
664
+ /* @__PURE__ */ jsxDEV9("span", {
1007
665
  style: { textAlign: "left", color: "#475569", fontSize: "10px", paddingTop: "1px" },
1008
666
  children: label
1009
667
  }, undefined, false, undefined, this),
1010
- /* @__PURE__ */ jsxDEV("span", {
1011
- style: {
1012
- textAlign: "left",
1013
- color: "#cbd5e1",
1014
- fontSize: "11px",
1015
- wordBreak: "break-all"
1016
- },
668
+ /* @__PURE__ */ jsxDEV9("span", {
669
+ style: { textAlign: "left", color: "#cbd5e1", fontSize: "11px", wordBreak: "break-all" },
1017
670
  children: value
1018
671
  }, undefined, false, undefined, this)
1019
672
  ]
1020
673
  }, label, true, undefined, this))
1021
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV("div", {
674
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV9("div", {
1022
675
  onClick: () => setExpanded(true),
1023
676
  style: {
1024
677
  background: "#1e293b",
@@ -1032,19 +685,19 @@ function MetaSection({ entry }) {
1032
685
  cursor: "pointer"
1033
686
  },
1034
687
  children: [
1035
- /* @__PURE__ */ jsxDEV(MetaChip, {
688
+ /* @__PURE__ */ jsxDEV9(MetaChip, {
1036
689
  label: "cuid",
1037
690
  value: `…${cuid.slice(-8)}`
1038
691
  }, undefined, false, undefined, this),
1039
- /* @__PURE__ */ jsxDEV(MetaChip, {
692
+ /* @__PURE__ */ jsxDEV9(MetaChip, {
1040
693
  label: "origin",
1041
694
  value: meta.originClient.envId
1042
695
  }, undefined, false, undefined, this),
1043
- meta.originClient.perId != null && /* @__PURE__ */ jsxDEV(MetaChip, {
696
+ meta.originClient.perId != null && /* @__PURE__ */ jsxDEV9(MetaChip, {
1044
697
  label: "perId",
1045
698
  value: meta.originClient.perId.length > 10 ? `${meta.originClient.perId.slice(0, 10)}…` : meta.originClient.perId
1046
699
  }, undefined, false, undefined, this),
1047
- meta.originClient.insId != null && /* @__PURE__ */ jsxDEV(MetaChip, {
700
+ meta.originClient.insId != null && /* @__PURE__ */ jsxDEV9(MetaChip, {
1048
701
  label: "insId",
1049
702
  value: meta.originClient.insId.length > 10 ? `${meta.originClient.insId.slice(0, 10)}…` : meta.originClient.insId
1050
703
  }, undefined, false, undefined, this)
@@ -1053,221 +706,1360 @@ function MetaSection({ entry }) {
1053
706
  ]
1054
707
  }, undefined, true, undefined, this);
1055
708
  }
1056
- function MetaChip({ label, value }) {
1057
- return /* @__PURE__ */ jsxDEV("span", {
1058
- style: { whiteSpace: "nowrap" },
1059
- children: [
1060
- /* @__PURE__ */ jsxDEV("span", {
1061
- style: { color: "#475569" },
1062
- children: [
1063
- label,
1064
- " "
1065
- ]
1066
- }, undefined, true, undefined, this),
1067
- /* @__PURE__ */ jsxDEV("span", {
1068
- style: { color: "#cbd5e1" },
1069
- children: value
1070
- }, undefined, false, undefined, this)
1071
- ]
1072
- }, undefined, true, undefined, this);
1073
- }
1074
- function RoutingSection({ entry }) {
709
+
710
+ // src/devtools/browser/components/RoutingSection.tsx
711
+ import { jsxDEV as jsxDEV10 } from "react/jsx-dev-runtime";
712
+ function RoutingSection({
713
+ entry,
714
+ minHopCount = 0
715
+ }) {
1075
716
  const { meta, startTime } = entry;
1076
- if (meta.routing.length === 0)
1077
- return null;
1078
717
  const hopCount = meta.routing.length;
1079
- return /* @__PURE__ */ jsxDEV("div", {
718
+ const phantomCount = Math.max(0, minHopCount - hopCount);
719
+ if (hopCount === 0 && phantomCount === 0)
720
+ return null;
721
+ return /* @__PURE__ */ jsxDEV10("div", {
1080
722
  children: [
1081
- /* @__PURE__ */ jsxDEV(SectionLabel, {
1082
- label: `Routing · ${hopCount} hop${hopCount !== 1 ? "s" : ""}`
723
+ /* @__PURE__ */ jsxDEV10(SectionLabel, {
724
+ label: hopCount > 0 ? `Routing · ${hopCount} hop${hopCount !== 1 ? "s" : ""}` : "Routing"
1083
725
  }, undefined, false, undefined, this),
1084
- /* @__PURE__ */ jsxDEV("div", {
726
+ /* @__PURE__ */ jsxDEV10("div", {
1085
727
  style: { display: "flex", flexDirection: "column", gap: "2px" },
1086
- children: meta.routing.map((hop, i) => {
1087
- const isLocal = hop.handlerType === "local";
1088
- const isLast = i === hopCount - 1;
1089
- const badgeColor = isLocal ? "#34d399" : "#fbbf24";
1090
- const badgeText = isLocal ? "● exec" : `→ ${hop.transport ?? "ext"}`;
1091
- const runtimeTitle = [hop.runtime.perId, hop.runtime.insId].filter(Boolean).join(" · ") || undefined;
1092
- const rowBorder = isLast ? undefined : "1px solid #0f172a";
1093
- return /* @__PURE__ */ jsxDEV("div", {
728
+ children: [
729
+ meta.routing.map((hop, i) => {
730
+ const isLocal = hop.handlerType === "local";
731
+ const isLast = i === hopCount - 1 && phantomCount === 0;
732
+ const badgeColor = isLocal ? "#34d399" : "#fbbf24";
733
+ const badgeText = isLocal ? "● exec" : `→ ${hop.transport ?? "ext"}`;
734
+ const runtimeTitle = [hop.runtime.perId, hop.runtime.insId].filter(Boolean).join(" · ") || undefined;
735
+ return /* @__PURE__ */ jsxDEV10("div", {
736
+ style: {
737
+ background: "#1e293b",
738
+ borderRadius: "4px",
739
+ overflow: "hidden",
740
+ display: "grid",
741
+ gridTemplateColumns: "30% 1fr 30%",
742
+ alignItems: "center",
743
+ columnGap: "8px",
744
+ borderBottom: isLast ? undefined : "1px solid #0f172a",
745
+ padding: "4px 8px"
746
+ },
747
+ children: [
748
+ /* @__PURE__ */ jsxDEV10("span", {
749
+ style: { display: "flex", flexDirection: "row", alignItems: "center", gap: "10px" },
750
+ children: [
751
+ /* @__PURE__ */ jsxDEV10("span", {
752
+ style: { color: "#334155", fontSize: "10px", width: "16px", textAlign: "right" },
753
+ children: i + 1
754
+ }, undefined, false, undefined, this),
755
+ /* @__PURE__ */ jsxDEV10("span", {
756
+ title: runtimeTitle,
757
+ style: {
758
+ color: "#94a3b8",
759
+ fontSize: "11px",
760
+ overflow: "hidden",
761
+ textOverflow: "ellipsis",
762
+ whiteSpace: "nowrap",
763
+ textAlign: "center"
764
+ },
765
+ children: hop.runtime.envId
766
+ }, undefined, false, undefined, this)
767
+ ]
768
+ }, undefined, true, undefined, this),
769
+ /* @__PURE__ */ jsxDEV10("span", {
770
+ style: { color: badgeColor, fontSize: "10px", whiteSpace: "nowrap" },
771
+ children: badgeText
772
+ }, undefined, false, undefined, this),
773
+ /* @__PURE__ */ jsxDEV10("span", {
774
+ style: {
775
+ display: "flex",
776
+ alignItems: "center",
777
+ justifyContent: "space-between",
778
+ paddingRight: "8px",
779
+ overflow: "hidden"
780
+ },
781
+ children: [
782
+ /* @__PURE__ */ jsxDEV10("span", {
783
+ style: {
784
+ color: "#475569",
785
+ fontSize: "10px",
786
+ whiteSpace: "nowrap",
787
+ overflow: "hidden",
788
+ textOverflow: "ellipsis"
789
+ },
790
+ children: hop.handlerClient != null ? `↳ ${hop.handlerClient.envId}` : ""
791
+ }, undefined, false, undefined, this),
792
+ /* @__PURE__ */ jsxDEV10("span", {
793
+ style: { color: "#334155", fontSize: "10px", flexShrink: 0, marginLeft: "auto" },
794
+ children: [
795
+ "+",
796
+ hop.time - startTime,
797
+ "ms"
798
+ ]
799
+ }, undefined, true, undefined, this)
800
+ ]
801
+ }, undefined, true, undefined, this)
802
+ ]
803
+ }, `${hop.time}-${hop.runtime.envId}`, true, undefined, this);
804
+ }),
805
+ Array.from({ length: phantomCount }, (_, i) => `routing-phantom-${hopCount + i}`).map((key) => /* @__PURE__ */ jsxDEV10("div", {
806
+ "aria-hidden": "true",
1094
807
  style: {
808
+ visibility: "hidden",
1095
809
  background: "#1e293b",
1096
810
  borderRadius: "4px",
1097
- overflow: "hidden",
1098
811
  display: "grid",
1099
812
  gridTemplateColumns: "30% 1fr 30%",
1100
813
  alignItems: "center",
1101
814
  columnGap: "8px",
1102
- borderBottom: rowBorder,
1103
815
  padding: "4px 8px"
1104
816
  },
1105
817
  children: [
1106
- /* @__PURE__ */ jsxDEV("span", {
818
+ /* @__PURE__ */ jsxDEV10("span", {
1107
819
  style: { display: "flex", flexDirection: "row", alignItems: "center", gap: "10px" },
1108
820
  children: [
1109
- /* @__PURE__ */ jsxDEV("span", {
1110
- style: {
1111
- color: "#334155",
1112
- fontSize: "10px",
1113
- width: "16px",
1114
- textAlign: "right"
1115
- },
1116
- children: i + 1
821
+ /* @__PURE__ */ jsxDEV10("span", {
822
+ style: { fontSize: "10px", width: "16px" },
823
+ children: "0"
1117
824
  }, undefined, false, undefined, this),
1118
- /* @__PURE__ */ jsxDEV("span", {
1119
- title: runtimeTitle,
1120
- style: {
1121
- color: "#94a3b8",
1122
- fontSize: "11px",
1123
- overflow: "hidden",
1124
- textOverflow: "ellipsis",
1125
- whiteSpace: "nowrap",
1126
- textAlign: "center"
1127
- },
1128
- children: hop.runtime.envId
825
+ /* @__PURE__ */ jsxDEV10("span", {
826
+ style: { fontSize: "11px" },
827
+ children: "placeholder"
1129
828
  }, undefined, false, undefined, this)
1130
829
  ]
1131
830
  }, undefined, true, undefined, this),
1132
- /* @__PURE__ */ jsxDEV("span", {
1133
- style: { color: badgeColor, fontSize: "10px", whiteSpace: "nowrap" },
1134
- children: badgeText
831
+ /* @__PURE__ */ jsxDEV10("span", {
832
+ style: { fontSize: "10px" },
833
+ children: "placeholder"
1135
834
  }, undefined, false, undefined, this),
1136
- /* @__PURE__ */ jsxDEV("span", {
835
+ /* @__PURE__ */ jsxDEV10("span", {}, undefined, false, undefined, this)
836
+ ]
837
+ }, key, true, undefined, this))
838
+ ]
839
+ }, undefined, true, undefined, this)
840
+ ]
841
+ }, undefined, true, undefined, this);
842
+ }
843
+
844
+ // src/devtools/browser/components/StackTraceSection.tsx
845
+ import { useState as useState4 } from "react";
846
+ import { jsxDEV as jsxDEV11 } from "react/jsx-dev-runtime";
847
+ var INTERNAL_PATTERNS = [
848
+ "/nice-action/",
849
+ "@nice-code/action",
850
+ "node_modules/",
851
+ "native code",
852
+ "<anonymous>"
853
+ ];
854
+ function isInternalFrame(file) {
855
+ return INTERNAL_PATTERNS.some((p) => file.includes(p));
856
+ }
857
+ function parseStackFrames(stack) {
858
+ return stack.split(`
859
+ `).slice(1).map((line) => {
860
+ const raw = line.trim();
861
+ if (raw === "")
862
+ return null;
863
+ const matchFn = raw.match(/^at (.+?) \((.+):(\d+):(\d+)\)$/);
864
+ if (matchFn != null) {
865
+ const file = matchFn[2];
866
+ return { fn: matchFn[1], file, raw, isInternal: isInternalFrame(file) };
867
+ }
868
+ const matchUrl = raw.match(/^at (.+):(\d+):(\d+)$/);
869
+ if (matchUrl != null) {
870
+ const file = matchUrl[1];
871
+ return { file, raw, isInternal: isInternalFrame(file) };
872
+ }
873
+ return { raw, isInternal: true };
874
+ }).filter((f) => f != null);
875
+ }
876
+ function formatFrameFile(file) {
877
+ if (file == null)
878
+ return "?";
879
+ let path = file;
880
+ try {
881
+ const url = new URL(file);
882
+ path = url.pathname.split("?")[0] ?? url.pathname;
883
+ if (path.startsWith("/@fs/"))
884
+ path = path.slice(4);
885
+ } catch {}
886
+ const parts = path.replace(/\\/g, "/").split("/").filter(Boolean);
887
+ const filtered = parts[0]?.match(/^[a-zA-Z]:$/) != null ? parts.slice(1) : parts;
888
+ const tail = filtered.slice(-4);
889
+ return tail.join("/") || file;
890
+ }
891
+ function StackTraceSection({
892
+ label,
893
+ stack,
894
+ color = "#60a5fa"
895
+ }) {
896
+ const [showAll, setShowAll] = useState4(false);
897
+ if (stack == null)
898
+ return null;
899
+ const allFrames = parseStackFrames(stack);
900
+ const userFrames = allFrames.filter((f) => !f.isInternal);
901
+ const hasInternalFrames = allFrames.length > userFrames.length;
902
+ const displayFrames = showAll ? allFrames : userFrames;
903
+ if (allFrames.length === 0)
904
+ return null;
905
+ return /* @__PURE__ */ jsxDEV11("div", {
906
+ children: [
907
+ /* @__PURE__ */ jsxDEV11("div", {
908
+ style: {
909
+ display: "flex",
910
+ alignItems: "center",
911
+ justifyContent: "space-between",
912
+ marginBottom: "3px"
913
+ },
914
+ children: [
915
+ /* @__PURE__ */ jsxDEV11(SectionLabel, {
916
+ label,
917
+ color
918
+ }, undefined, false, undefined, this),
919
+ hasInternalFrames && /* @__PURE__ */ jsxDEV11("span", {
920
+ style: { color: "#475569", fontSize: "9px" },
921
+ children: !showAll ? "user only" : `all (${allFrames.length})`
922
+ }, undefined, false, undefined, this)
923
+ ]
924
+ }, undefined, true, undefined, this),
925
+ /* @__PURE__ */ jsxDEV11("div", {
926
+ onClick: () => setShowAll((s) => !s),
927
+ style: {
928
+ background: "#040a13",
929
+ borderRadius: "4px",
930
+ padding: "2px",
931
+ overflow: "hidden",
932
+ cursor: "pointer"
933
+ },
934
+ children: displayFrames.length === 0 ? /* @__PURE__ */ jsxDEV11("div", {
935
+ style: { padding: "6px 10px", color: "#64748b", fontSize: "10px", fontStyle: "italic" },
936
+ children: "no user frames captured"
937
+ }, undefined, false, undefined, this) : displayFrames.map((frame, idx) => {
938
+ const displayFile = formatFrameFile(frame.file);
939
+ const slashIdx = displayFile.lastIndexOf("/");
940
+ const folderPrefix = slashIdx >= 0 ? displayFile.slice(0, slashIdx + 1) : "";
941
+ const bareFilename = slashIdx >= 0 ? displayFile.slice(slashIdx + 1) : displayFile;
942
+ const titleStr = frame.file != null ? frame.file : frame.raw;
943
+ const isUser = !frame.isInternal;
944
+ return /* @__PURE__ */ jsxDEV11("div", {
945
+ title: titleStr,
946
+ style: {
947
+ display: "flex",
948
+ flexDirection: "row",
949
+ gap: "0.5em",
950
+ padding: "4px",
951
+ borderBottom: idx < displayFrames.length - 1 ? "1px solid #0f172a" : undefined
952
+ },
953
+ children: [
954
+ /* @__PURE__ */ jsxDEV11("span", {
1137
955
  style: {
1138
- display: "flex",
1139
- alignItems: "center",
1140
- justifyContent: "space-between",
1141
- paddingRight: "8px",
1142
- overflow: "hidden"
956
+ color: isUser ? "#64748b" : "#2d3f53",
957
+ fontSize: "10px",
958
+ minWidth: "10px",
959
+ textAlign: "left",
960
+ flexShrink: 0,
961
+ fontVariantNumeric: "tabular-nums"
1143
962
  },
963
+ children: idx + 1
964
+ }, undefined, false, undefined, this),
965
+ /* @__PURE__ */ jsxDEV11("div", {
966
+ style: { display: "flex", flexDirection: "column", gap: "0.15em", lineHeight: 1.2 },
1144
967
  children: [
1145
- /* @__PURE__ */ jsxDEV("span", {
1146
- style: {
1147
- color: "#475569",
1148
- fontSize: "10px",
1149
- whiteSpace: "nowrap",
1150
- overflow: "hidden",
1151
- textOverflow: "ellipsis"
1152
- },
1153
- children: hop.handlerClient != null ? `↳ ${hop.handlerClient.envId}` : ""
968
+ /* @__PURE__ */ jsxDEV11("div", {
969
+ style: { display: "flex", alignItems: "center" },
970
+ children: /* @__PURE__ */ jsxDEV11("span", {
971
+ style: {
972
+ color: isUser ? "#e2e8f0" : "#50698b",
973
+ fontSize: "12px",
974
+ fontWeight: 500,
975
+ overflow: "hidden",
976
+ textOverflow: "ellipsis",
977
+ whiteSpace: "nowrap"
978
+ },
979
+ children: frame.fn ?? "(anonymous)"
980
+ }, undefined, false, undefined, this)
1154
981
  }, undefined, false, undefined, this),
1155
- /* @__PURE__ */ jsxDEV("span", {
1156
- style: {
1157
- color: "#334155",
1158
- fontSize: "10px",
1159
- flexShrink: 0,
1160
- marginLeft: "auto"
1161
- },
982
+ /* @__PURE__ */ jsxDEV11("div", {
983
+ style: { display: "flex", alignItems: "baseline", overflow: "hidden" },
1162
984
  children: [
1163
- "+",
1164
- hop.time - startTime,
1165
- "ms"
985
+ folderPrefix !== "" && /* @__PURE__ */ jsxDEV11("span", {
986
+ style: {
987
+ color: isUser ? "#596b83" : "#2d3f53",
988
+ fontSize: "10px",
989
+ overflow: "hidden",
990
+ textOverflow: "ellipsis",
991
+ whiteSpace: "nowrap",
992
+ flexShrink: 1,
993
+ minWidth: 0
994
+ },
995
+ children: folderPrefix
996
+ }, undefined, false, undefined, this),
997
+ /* @__PURE__ */ jsxDEV11("span", {
998
+ style: {
999
+ color: isUser ? "#8a9ebb" : "#425979",
1000
+ fontSize: "10px",
1001
+ whiteSpace: "nowrap",
1002
+ flexShrink: 0
1003
+ },
1004
+ children: bareFilename
1005
+ }, undefined, false, undefined, this)
1166
1006
  ]
1167
1007
  }, undefined, true, undefined, this)
1168
1008
  ]
1169
1009
  }, undefined, true, undefined, this)
1170
1010
  ]
1171
- }, `${hop.time}-${hop.runtime.envId}`, true, undefined, this);
1011
+ }, `${frame.fn ?? "anon"}@${frame.file ?? "unknown"}`, true, undefined, this);
1172
1012
  })
1173
1013
  }, undefined, false, undefined, this)
1174
1014
  ]
1175
1015
  }, undefined, true, undefined, this);
1176
1016
  }
1177
- function SectionLabel({ label, color = "#60a5fa" }) {
1178
- return /* @__PURE__ */ jsxDEV("div", {
1017
+
1018
+ // src/devtools/browser/components/ActionDetailPanel.tsx
1019
+ import { jsxDEV as jsxDEV12, Fragment as Fragment4 } from "react/jsx-dev-runtime";
1020
+ function DetailHeader({
1021
+ entry,
1022
+ isActive,
1023
+ onClick,
1024
+ childExternalLabels
1025
+ }) {
1026
+ const color = STATUS_COLOR[entry.status];
1027
+ const symbol = STATUS_SYMBOL[entry.status];
1028
+ const timestamp = formatTimestamp(entry.startTime);
1029
+ return /* @__PURE__ */ jsxDEV12("div", {
1030
+ onClick: !isActive ? onClick : undefined,
1179
1031
  style: {
1180
- color,
1181
- fontSize: "10px",
1182
- marginBottom: "3px",
1183
- textTransform: "uppercase",
1184
- letterSpacing: "0.05em"
1032
+ padding: "10px 12px",
1033
+ background: isActive ? "#1e293b" : "#131f35",
1034
+ borderBottom: "1px solid #0f172a",
1035
+ borderLeft: isActive ? "2px solid #60a5fa" : "2px solid transparent",
1036
+ flexShrink: 0,
1037
+ display: "flex",
1038
+ alignItems: "flex-start",
1039
+ gap: "10px",
1040
+ cursor: isActive ? "default" : "pointer"
1185
1041
  },
1186
- children: label
1187
- }, undefined, false, undefined, this);
1042
+ children: [
1043
+ /* @__PURE__ */ jsxDEV12("span", {
1044
+ style: {
1045
+ color,
1046
+ fontSize: "15px",
1047
+ flexShrink: 0,
1048
+ paddingTop: "1px",
1049
+ animation: entry.status === "running" ? "__nice-action-pulse 1.2s ease-in-out infinite" : undefined
1050
+ },
1051
+ children: symbol
1052
+ }, undefined, false, undefined, this),
1053
+ /* @__PURE__ */ jsxDEV12("div", {
1054
+ style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: "5px" },
1055
+ children: [
1056
+ /* @__PURE__ */ jsxDEV12("div", {
1057
+ style: { display: "flex", alignItems: "center", minWidth: 0, gap: "0.5em" },
1058
+ children: [
1059
+ /* @__PURE__ */ jsxDEV12("span", {
1060
+ style: {
1061
+ color: "#f1f5f9",
1062
+ fontSize: "13px",
1063
+ fontWeight: "600",
1064
+ overflow: "hidden",
1065
+ textOverflow: "ellipsis",
1066
+ whiteSpace: "nowrap",
1067
+ flexShrink: 1,
1068
+ minWidth: "24px"
1069
+ },
1070
+ children: entry.actionId
1071
+ }, undefined, false, undefined, this),
1072
+ /* @__PURE__ */ jsxDEV12(DomainChip, {
1073
+ domain: entry.domain,
1074
+ allDomains: entry.allDomains,
1075
+ size: "md"
1076
+ }, undefined, false, undefined, this)
1077
+ ]
1078
+ }, undefined, true, undefined, this),
1079
+ /* @__PURE__ */ jsxDEV12("div", {
1080
+ style: {
1081
+ display: "flex",
1082
+ alignItems: "center",
1083
+ justifyContent: "space-between",
1084
+ gap: "8px"
1085
+ },
1086
+ children: [
1087
+ /* @__PURE__ */ jsxDEV12("div", {
1088
+ style: {
1089
+ display: "flex",
1090
+ alignItems: "center",
1091
+ gap: "6px",
1092
+ minWidth: 0,
1093
+ overflow: "hidden"
1094
+ },
1095
+ children: [
1096
+ /* @__PURE__ */ jsxDEV12(HandlerChips, {
1097
+ entry,
1098
+ size: "md",
1099
+ alwaysShowLocal: true
1100
+ }, undefined, false, undefined, this),
1101
+ /* @__PURE__ */ jsxDEV12(ChildDispatchChips, {
1102
+ labels: childExternalLabels,
1103
+ size: "md"
1104
+ }, undefined, false, undefined, this)
1105
+ ]
1106
+ }, undefined, true, undefined, this),
1107
+ /* @__PURE__ */ jsxDEV12("div", {
1108
+ style: { display: "flex", alignItems: "center", gap: "8px", flexShrink: 0 },
1109
+ children: [
1110
+ /* @__PURE__ */ jsxDEV12("span", {
1111
+ style: { color: "#334155", fontSize: "10px", letterSpacing: "0.02em" },
1112
+ children: timestamp
1113
+ }, undefined, false, undefined, this),
1114
+ /* @__PURE__ */ jsxDEV12("span", {
1115
+ style: { color, fontSize: "12px", fontWeight: "500" },
1116
+ children: /* @__PURE__ */ jsxDEV12(DurationDisplay, {
1117
+ entry
1118
+ }, undefined, false, undefined, this)
1119
+ }, undefined, false, undefined, this)
1120
+ ]
1121
+ }, undefined, true, undefined, this)
1122
+ ]
1123
+ }, undefined, true, undefined, this)
1124
+ ]
1125
+ }, undefined, true, undefined, this)
1126
+ ]
1127
+ }, undefined, true, undefined, this);
1188
1128
  }
1189
- var COMPACT_CHAR_LIMIT = 120;
1190
- function DetailSection({
1191
- label,
1192
- value,
1193
- color = "#60a5fa"
1129
+ function ActionDetailPanel({
1130
+ entry,
1131
+ parent,
1132
+ childEntries,
1133
+ onSelectEntry
1194
1134
  }) {
1195
- const [expanded, setExpanded] = useState(false);
1196
- const fullJson = safeStringify(value, 2);
1197
- const compactJson = safeStringify(value, 0);
1198
- const canExpand = fullJson !== compactJson || compactJson.length > COMPACT_CHAR_LIMIT;
1199
- const compactDisplay = compactJson.length > COMPACT_CHAR_LIMIT ? `${compactJson.slice(0, COMPACT_CHAR_LIMIT)}…` : compactJson;
1200
- return /* @__PURE__ */ jsxDEV("div", {
1135
+ const [focusedChildCuid, setFocusedChildCuid] = useState5(null);
1136
+ const focusedEntry = focusedChildCuid != null ? childEntries.find((e) => e.cuid === focusedChildCuid) ?? entry : entry;
1137
+ const maxRoutingHops = Math.max(entry.meta.routing.length, ...childEntries.map((e) => e.meta.routing.length), 0);
1138
+ const childExternalLabels = useMemo(() => {
1139
+ const seen = new Set;
1140
+ const result = [];
1141
+ for (const child of childEntries) {
1142
+ const firstHop = child.meta.routing[0];
1143
+ if (firstHop == null)
1144
+ continue;
1145
+ const label = getExternalLabel(firstHop);
1146
+ if (label == null || seen.has(label))
1147
+ continue;
1148
+ seen.add(label);
1149
+ result.push(label);
1150
+ }
1151
+ return result;
1152
+ }, [childEntries]);
1153
+ const handleFocusChild = (cuid) => {
1154
+ setFocusedChildCuid((prev) => prev === cuid ? null : cuid);
1155
+ };
1156
+ return /* @__PURE__ */ jsxDEV12("div", {
1157
+ style: {
1158
+ flex: 1,
1159
+ display: "flex",
1160
+ flexDirection: "column",
1161
+ overflow: "hidden",
1162
+ minHeight: 0,
1163
+ background: "#0f172a"
1164
+ },
1201
1165
  children: [
1202
- /* @__PURE__ */ jsxDEV("div", {
1166
+ /* @__PURE__ */ jsxDEV12(DetailHeader, {
1167
+ entry,
1168
+ isActive: focusedChildCuid === null,
1169
+ onClick: () => setFocusedChildCuid(null),
1170
+ childExternalLabels
1171
+ }, undefined, false, undefined, this),
1172
+ /* @__PURE__ */ jsxDEV12("div", {
1203
1173
  style: {
1174
+ flex: 1,
1175
+ overflowY: "auto",
1176
+ minHeight: 0,
1177
+ padding: "10px 12px",
1204
1178
  display: "flex",
1205
- alignItems: "center",
1206
- justifyContent: "space-between",
1207
- marginBottom: "3px",
1208
- userSelect: "none"
1179
+ flexDirection: "column",
1180
+ gap: "8px"
1209
1181
  },
1210
1182
  children: [
1211
- /* @__PURE__ */ jsxDEV("div", {
1212
- style: {
1213
- color,
1214
- fontSize: "10px",
1215
- textTransform: "uppercase",
1216
- letterSpacing: "0.05em"
1217
- },
1218
- children: label
1183
+ /* @__PURE__ */ jsxDEV12(CallStackSection, {
1184
+ parent,
1185
+ childEntries,
1186
+ focusedChildCuid,
1187
+ onFocusChild: handleFocusChild,
1188
+ onSelectParent: onSelectEntry
1219
1189
  }, undefined, false, undefined, this),
1220
- canExpand && /* @__PURE__ */ jsxDEV("span", {
1221
- style: { color: "#334155", fontSize: "11px" },
1222
- children: expanded ? "▾" : "▸"
1190
+ /* @__PURE__ */ jsxDEV12(MetaSection, {
1191
+ entry: focusedEntry
1192
+ }, undefined, false, undefined, this),
1193
+ /* @__PURE__ */ jsxDEV12(RoutingSection, {
1194
+ entry: focusedEntry,
1195
+ minHopCount: maxRoutingHops
1196
+ }, undefined, false, undefined, this),
1197
+ /* @__PURE__ */ jsxDEV12(StackTraceSection, {
1198
+ label: "Dispatch Site",
1199
+ stack: focusedEntry.callSite,
1200
+ color: "#94a3b8"
1201
+ }, undefined, false, undefined, this),
1202
+ /* @__PURE__ */ jsxDEV12(DetailSection, {
1203
+ label: "Input",
1204
+ value: focusedEntry.input
1205
+ }, undefined, false, undefined, this),
1206
+ focusedEntry.status === "success" && /* @__PURE__ */ jsxDEV12(DetailSection, {
1207
+ label: "Output",
1208
+ value: focusedEntry.output,
1209
+ color: "#4ade80"
1210
+ }, undefined, false, undefined, this),
1211
+ focusedEntry.status === "failed" && /* @__PURE__ */ jsxDEV12(Fragment4, {
1212
+ children: [
1213
+ /* @__PURE__ */ jsxDEV12(DetailSection, {
1214
+ label: "Error",
1215
+ value: focusedEntry.error,
1216
+ color: "#f87171"
1217
+ }, undefined, false, undefined, this),
1218
+ /* @__PURE__ */ jsxDEV12(StackTraceSection, {
1219
+ label: "Error Stack",
1220
+ stack: focusedEntry.errorStack,
1221
+ color: "#f87171"
1222
+ }, undefined, false, undefined, this)
1223
+ ]
1224
+ }, undefined, true, undefined, this),
1225
+ focusedEntry.status === "aborted" && /* @__PURE__ */ jsxDEV12(Fragment4, {
1226
+ children: [
1227
+ focusedEntry.abortReason != null && /* @__PURE__ */ jsxDEV12(DetailSection, {
1228
+ label: "Abort Reason",
1229
+ value: focusedEntry.abortReason,
1230
+ color: "#9ca3af"
1231
+ }, undefined, false, undefined, this),
1232
+ /* @__PURE__ */ jsxDEV12(StackTraceSection, {
1233
+ label: "Abort Stack",
1234
+ stack: focusedEntry.errorStack,
1235
+ color: "#9ca3af"
1236
+ }, undefined, false, undefined, this)
1237
+ ]
1238
+ }, undefined, true, undefined, this),
1239
+ focusedEntry.progressUpdates.length > 0 && /* @__PURE__ */ jsxDEV12(DetailSection, {
1240
+ label: `Progress (${focusedEntry.progressUpdates.length})`,
1241
+ value: focusedEntry.progressUpdates
1223
1242
  }, undefined, false, undefined, this)
1224
1243
  ]
1225
- }, undefined, true, undefined, this),
1226
- expanded ? /* @__PURE__ */ jsxDEV("div", {
1227
- onClick: canExpand ? () => setExpanded(false) : undefined,
1228
- style: {
1229
- margin: 0,
1230
- padding: "6px 8px",
1231
- background: "#1e293b",
1232
- borderRadius: "4px",
1233
- fontSize: "11px",
1234
- color: "#cbd5e1",
1235
- overflowX: "auto",
1236
- textAlign: "left",
1237
- whiteSpace: "pre-wrap",
1238
- wordBreak: "break-all",
1239
- cursor: canExpand ? "pointer" : "default"
1240
- },
1241
- children: fullJson
1242
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV("div", {
1243
- onClick: canExpand ? () => setExpanded(true) : undefined,
1244
- style: {
1245
- padding: "5px 8px",
1246
- background: "#1e293b",
1247
- borderRadius: "4px",
1248
- fontSize: "11px",
1249
- color: "#94a3b8",
1250
- fontFamily: "inherit",
1251
- whiteSpace: "nowrap",
1252
- overflow: "hidden",
1253
- textOverflow: "ellipsis",
1254
- cursor: canExpand ? "pointer" : "default"
1255
- },
1256
- children: compactDisplay
1257
- }, undefined, false, undefined, this)
1244
+ }, undefined, true, undefined, this)
1258
1245
  ]
1259
1246
  }, undefined, true, undefined, this);
1260
1247
  }
1261
- function safeStringify(value, indent = 2) {
1262
- if (value === undefined)
1263
- return "undefined";
1264
- if (value === null)
1265
- return "null";
1266
- try {
1267
- return JSON.stringify(value, null, indent);
1268
- } catch {
1269
- return String(value);
1248
+
1249
+ // src/devtools/browser/components/ActionEntryRow.tsx
1250
+ import { jsxDEV as jsxDEV13 } from "react/jsx-dev-runtime";
1251
+ function ActionEntryRow({
1252
+ entry,
1253
+ isSelected,
1254
+ onClick,
1255
+ groupCount,
1256
+ isSubEntry = false,
1257
+ isLatest = false,
1258
+ latestTime,
1259
+ childExternalLabels
1260
+ }) {
1261
+ const color = STATUS_COLOR[entry.status];
1262
+ const symbol = STATUS_SYMBOL[entry.status];
1263
+ const timestamp = formatTimestamp(entry.startTime);
1264
+ if (isSubEntry) {
1265
+ return /* @__PURE__ */ jsxDEV13("div", {
1266
+ "data-cuid": entry.cuid,
1267
+ onClick,
1268
+ style: {
1269
+ display: "flex",
1270
+ alignItems: "center",
1271
+ gap: "1em",
1272
+ padding: "4px 10px 4px 26px",
1273
+ cursor: "pointer",
1274
+ background: isSelected ? "#131f35" : "#070d18",
1275
+ borderBottom: "1px solid #0f172a",
1276
+ borderLeft: isSelected ? "2px solid #3b82f6" : "2px solid transparent"
1277
+ },
1278
+ children: [
1279
+ /* @__PURE__ */ jsxDEV13("span", {
1280
+ style: { color: "#334155", fontSize: "10px", flexShrink: 0 },
1281
+ children: symbol
1282
+ }, undefined, false, undefined, this),
1283
+ /* @__PURE__ */ jsxDEV13("span", {
1284
+ children: [
1285
+ /* @__PURE__ */ jsxDEV13("span", {
1286
+ style: { color: isSelected ? "#818cf8" : "#3730a3", opacity: 0.5, fontSize: "10px" },
1287
+ children: "⚡"
1288
+ }, undefined, false, undefined, this),
1289
+ /* @__PURE__ */ jsxDEV13("span", {
1290
+ style: {
1291
+ flex: 1,
1292
+ color: "#475569",
1293
+ fontSize: "11px",
1294
+ overflow: "hidden",
1295
+ textOverflow: "ellipsis",
1296
+ whiteSpace: "nowrap",
1297
+ minWidth: 0
1298
+ },
1299
+ children: entry.actionId
1300
+ }, undefined, false, undefined, this)
1301
+ ]
1302
+ }, undefined, true, undefined, this),
1303
+ /* @__PURE__ */ jsxDEV13(DomainChip, {
1304
+ domain: entry.domain,
1305
+ allDomains: entry.allDomains,
1306
+ size: "sm"
1307
+ }, undefined, false, undefined, this),
1308
+ /* @__PURE__ */ jsxDEV13("span", {
1309
+ style: { color: "#334155", fontSize: "11px", flexShrink: 0 },
1310
+ children: /* @__PURE__ */ jsxDEV13(DurationDisplay, {
1311
+ entry
1312
+ }, undefined, false, undefined, this)
1313
+ }, undefined, false, undefined, this)
1314
+ ]
1315
+ }, undefined, true, undefined, this);
1316
+ }
1317
+ return /* @__PURE__ */ jsxDEV13("div", {
1318
+ "data-cuid": entry.cuid,
1319
+ onClick,
1320
+ style: {
1321
+ display: "flex",
1322
+ alignItems: "flex-start",
1323
+ gap: "8px",
1324
+ padding: isSelected ? "6px 12px 6px 10px" : "6px 12px",
1325
+ cursor: "pointer",
1326
+ background: isSelected ? "#131f35" : "transparent",
1327
+ borderBottom: "1px solid #0f172a",
1328
+ borderLeft: isSelected ? "2px solid #60a5fa" : "2px solid transparent"
1329
+ },
1330
+ children: [
1331
+ /* @__PURE__ */ jsxDEV13("span", {
1332
+ style: {
1333
+ flexShrink: 0,
1334
+ minWidth: "3.5em",
1335
+ display: "flex",
1336
+ alignItems: "center",
1337
+ justifyContent: "start"
1338
+ },
1339
+ children: isLatest ? /* @__PURE__ */ jsxDEV13(Chip, {
1340
+ color: "#60a5fa",
1341
+ borderColor: "#1e3a5f",
1342
+ children: "latest"
1343
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV13(Chip, {
1344
+ color: "#475569",
1345
+ borderColor: "#1e293b",
1346
+ children: [
1347
+ "+",
1348
+ latestTime != null ? formatRelativeAge(latestTime - entry.startTime) : "?"
1349
+ ]
1350
+ }, undefined, true, undefined, this)
1351
+ }, undefined, false, undefined, this),
1352
+ /* @__PURE__ */ jsxDEV13("span", {
1353
+ style: {
1354
+ color,
1355
+ fontSize: "10px",
1356
+ flexShrink: 0,
1357
+ paddingTop: "2px",
1358
+ animation: entry.status === "running" ? "__nice-action-pulse 1.2s ease-in-out infinite" : undefined
1359
+ },
1360
+ children: symbol
1361
+ }, undefined, false, undefined, this),
1362
+ /* @__PURE__ */ jsxDEV13("div", {
1363
+ style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: "3px" },
1364
+ children: [
1365
+ /* @__PURE__ */ jsxDEV13("div", {
1366
+ style: {
1367
+ display: "flex",
1368
+ alignItems: "center",
1369
+ minWidth: 0,
1370
+ overflow: "hidden",
1371
+ gap: "0.5em"
1372
+ },
1373
+ children: [
1374
+ /* @__PURE__ */ jsxDEV13("span", {
1375
+ style: {
1376
+ color: "#cbd5e1",
1377
+ fontSize: "11px",
1378
+ overflow: "hidden",
1379
+ textOverflow: "ellipsis",
1380
+ whiteSpace: "nowrap",
1381
+ flexShrink: 1,
1382
+ minWidth: "24px"
1383
+ },
1384
+ children: entry.actionId
1385
+ }, undefined, false, undefined, this),
1386
+ /* @__PURE__ */ jsxDEV13(DomainChip, {
1387
+ domain: entry.domain,
1388
+ allDomains: entry.allDomains,
1389
+ size: "md"
1390
+ }, undefined, false, undefined, this)
1391
+ ]
1392
+ }, undefined, true, undefined, this),
1393
+ /* @__PURE__ */ jsxDEV13("div", {
1394
+ style: {
1395
+ display: "flex",
1396
+ alignItems: "center",
1397
+ justifyContent: "space-between",
1398
+ gap: "6px"
1399
+ },
1400
+ children: [
1401
+ /* @__PURE__ */ jsxDEV13("div", {
1402
+ style: {
1403
+ display: "flex",
1404
+ flexDirection: "row",
1405
+ alignItems: "center",
1406
+ gap: "5px",
1407
+ minWidth: 0,
1408
+ overflow: "hidden"
1409
+ },
1410
+ children: [
1411
+ /* @__PURE__ */ jsxDEV13(HandlerChips, {
1412
+ entry,
1413
+ size: "sm"
1414
+ }, undefined, false, undefined, this),
1415
+ /* @__PURE__ */ jsxDEV13(ChildDispatchChips, {
1416
+ labels: childExternalLabels,
1417
+ size: "sm"
1418
+ }, undefined, false, undefined, this)
1419
+ ]
1420
+ }, undefined, true, undefined, this),
1421
+ /* @__PURE__ */ jsxDEV13("div", {
1422
+ style: { display: "flex", alignItems: "center", gap: "6px", flexShrink: 0 },
1423
+ children: [
1424
+ groupCount != null && /* @__PURE__ */ jsxDEV13(Chip, {
1425
+ color: "#3b82f6",
1426
+ borderColor: "#1e3a5f",
1427
+ fontSize: "9px",
1428
+ padding: "1px 5px",
1429
+ children: [
1430
+ "×",
1431
+ groupCount
1432
+ ]
1433
+ }, undefined, true, undefined, this),
1434
+ /* @__PURE__ */ jsxDEV13("span", {
1435
+ style: { color: "#1e3a5f", fontSize: "10px", letterSpacing: "0.02em" },
1436
+ children: timestamp
1437
+ }, undefined, false, undefined, this),
1438
+ /* @__PURE__ */ jsxDEV13("span", {
1439
+ style: { color: "#475569", fontSize: "11px" },
1440
+ children: /* @__PURE__ */ jsxDEV13(DurationDisplay, {
1441
+ entry
1442
+ }, undefined, false, undefined, this)
1443
+ }, undefined, false, undefined, this)
1444
+ ]
1445
+ }, undefined, true, undefined, this)
1446
+ ]
1447
+ }, undefined, true, undefined, this)
1448
+ ]
1449
+ }, undefined, true, undefined, this)
1450
+ ]
1451
+ }, undefined, true, undefined, this);
1452
+ }
1453
+
1454
+ // src/devtools/browser/components/PanelChrome.tsx
1455
+ import { jsxDEV as jsxDEV14 } from "react/jsx-dev-runtime";
1456
+ var DOCKED_SIZE_MIN = 140;
1457
+ var POSITION_GRID = [
1458
+ ["top-left", "dock-top", "top-right"],
1459
+ ["dock-left", null, "dock-right"],
1460
+ ["bottom-left", "dock-bottom", "bottom-right"]
1461
+ ];
1462
+ function getDockSide(pos) {
1463
+ if (pos === "dock-top")
1464
+ return "top";
1465
+ if (pos === "dock-bottom")
1466
+ return "bottom";
1467
+ if (pos === "dock-left")
1468
+ return "left";
1469
+ if (pos === "dock-right")
1470
+ return "right";
1471
+ return null;
1472
+ }
1473
+ function PanelHeader({
1474
+ position,
1475
+ onPositionChange,
1476
+ onClose,
1477
+ onClear
1478
+ }) {
1479
+ return /* @__PURE__ */ jsxDEV14("div", {
1480
+ style: {
1481
+ display: "flex",
1482
+ alignItems: "center",
1483
+ justifyContent: "space-between",
1484
+ padding: "8px 12px",
1485
+ background: "#1e293b",
1486
+ borderBottom: "1px solid #0f172a",
1487
+ flexShrink: 0
1488
+ },
1489
+ children: [
1490
+ /* @__PURE__ */ jsxDEV14("span", {
1491
+ style: { color: "#60a5fa", fontWeight: "bold", fontSize: "11px" },
1492
+ children: "⚡ nice-action devtools"
1493
+ }, undefined, false, undefined, this),
1494
+ /* @__PURE__ */ jsxDEV14("div", {
1495
+ style: { display: "flex", gap: "10px", alignItems: "center" },
1496
+ children: [
1497
+ /* @__PURE__ */ jsxDEV14(PositionPicker, {
1498
+ position,
1499
+ onChange: onPositionChange
1500
+ }, undefined, false, undefined, this),
1501
+ onClear != null && /* @__PURE__ */ jsxDEV14("button", {
1502
+ onClick: onClear,
1503
+ style: {
1504
+ background: "none",
1505
+ border: "none",
1506
+ color: "#475569",
1507
+ cursor: "pointer",
1508
+ fontSize: "11px",
1509
+ padding: "0"
1510
+ },
1511
+ children: "clear"
1512
+ }, undefined, false, undefined, this),
1513
+ /* @__PURE__ */ jsxDEV14("button", {
1514
+ onClick: onClose,
1515
+ style: {
1516
+ background: "none",
1517
+ border: "none",
1518
+ color: "#475569",
1519
+ cursor: "pointer",
1520
+ fontSize: "16px",
1521
+ padding: "0",
1522
+ lineHeight: "1"
1523
+ },
1524
+ children: "×"
1525
+ }, undefined, false, undefined, this)
1526
+ ]
1527
+ }, undefined, true, undefined, this)
1528
+ ]
1529
+ }, undefined, true, undefined, this);
1530
+ }
1531
+ function PositionPicker({
1532
+ position,
1533
+ onChange
1534
+ }) {
1535
+ return /* @__PURE__ */ jsxDEV14("div", {
1536
+ title: "Move / dock panel",
1537
+ style: { display: "grid", gridTemplateColumns: "repeat(3, 9px)", gap: "2px", padding: "2px" },
1538
+ children: POSITION_GRID.flat().map((pos) => {
1539
+ if (pos == null)
1540
+ return /* @__PURE__ */ jsxDEV14("div", {
1541
+ style: { width: "9px", height: "9px" }
1542
+ }, "center-empty", false, undefined, this);
1543
+ const isDock = pos.startsWith("dock-");
1544
+ const isTopBottom = pos === "dock-top" || pos === "dock-bottom";
1545
+ const isActive = pos === position;
1546
+ return /* @__PURE__ */ jsxDEV14("div", {
1547
+ title: pos,
1548
+ onClick: () => onChange(pos),
1549
+ style: {
1550
+ width: "9px",
1551
+ height: "9px",
1552
+ display: "flex",
1553
+ alignItems: "center",
1554
+ justifyContent: "center",
1555
+ cursor: "pointer"
1556
+ },
1557
+ children: /* @__PURE__ */ jsxDEV14("div", {
1558
+ style: {
1559
+ width: isDock ? isTopBottom ? "9px" : "3px" : "7px",
1560
+ height: isDock ? isTopBottom ? "3px" : "9px" : "7px",
1561
+ borderRadius: isDock ? "1px" : "50%",
1562
+ background: isActive ? "#60a5fa" : "#334155"
1563
+ }
1564
+ }, undefined, false, undefined, this)
1565
+ }, pos, false, undefined, this);
1566
+ })
1567
+ }, undefined, false, undefined, this);
1568
+ }
1569
+ function ResizeHandle({
1570
+ dockSide,
1571
+ dockedSize,
1572
+ onChange
1573
+ }) {
1574
+ const isHoriz = dockSide === "left" || dockSide === "right";
1575
+ const onMouseDown = (e) => {
1576
+ e.preventDefault();
1577
+ const startCoord = isHoriz ? e.clientX : e.clientY;
1578
+ const startSize = dockedSize;
1579
+ const maxSize = isHoriz ? window.innerWidth * 0.85 : window.innerHeight * 0.85;
1580
+ const sign = dockSide === "bottom" || dockSide === "right" ? -1 : 1;
1581
+ const onMove = (me) => {
1582
+ const delta = (isHoriz ? me.clientX : me.clientY) - startCoord;
1583
+ onChange(Math.max(DOCKED_SIZE_MIN, Math.min(maxSize, startSize + sign * delta)));
1584
+ };
1585
+ const onUp = () => {
1586
+ window.removeEventListener("mousemove", onMove);
1587
+ window.removeEventListener("mouseup", onUp);
1588
+ };
1589
+ window.addEventListener("mousemove", onMove);
1590
+ window.addEventListener("mouseup", onUp);
1591
+ };
1592
+ const edgeStyle = dockSide === "bottom" ? { top: 0, left: 0, right: 0, height: "5px", cursor: "ns-resize" } : dockSide === "top" ? { bottom: 0, left: 0, right: 0, height: "5px", cursor: "ns-resize" } : dockSide === "right" ? { top: 0, bottom: 0, left: 0, width: "5px", cursor: "ew-resize" } : { top: 0, bottom: 0, right: 0, width: "5px", cursor: "ew-resize" };
1593
+ return /* @__PURE__ */ jsxDEV14("div", {
1594
+ onMouseDown,
1595
+ style: {
1596
+ position: "absolute",
1597
+ zIndex: 10,
1598
+ background: "transparent",
1599
+ ...edgeStyle
1600
+ }
1601
+ }, undefined, false, undefined, this);
1602
+ }
1603
+
1604
+ // src/devtools/browser/NiceActionDevtools.tsx
1605
+ import { jsxDEV as jsxDEV15, Fragment as Fragment5 } from "react/jsx-dev-runtime";
1606
+ if (typeof document !== "undefined" && !document.getElementById("__nice-action-devtools-styles")) {
1607
+ const style = document.createElement("style");
1608
+ style.id = "__nice-action-devtools-styles";
1609
+ style.textContent = `
1610
+ @keyframes __nice-action-pulse {
1611
+ 0%, 100% { opacity: 1; }
1612
+ 50% { opacity: 0.35; }
1613
+ }
1614
+ `;
1615
+ document.head?.appendChild(style);
1616
+ }
1617
+ var PREFS_KEY = "__nice-action-devtools-prefs";
1618
+ var DOCKED_HEIGHT_DEFAULT = 320;
1619
+ var DOCKED_WIDTH_DEFAULT = 420;
1620
+ var ROW_HEIGHT = 50;
1621
+ var SUB_ROW_HEIGHT = 30;
1622
+ function readPrefs(defaultPosition, initialOpen) {
1623
+ const fallback = {
1624
+ position: defaultPosition,
1625
+ isOpen: initialOpen,
1626
+ dockedHeight: DOCKED_HEIGHT_DEFAULT,
1627
+ dockedWidth: DOCKED_WIDTH_DEFAULT
1628
+ };
1629
+ try {
1630
+ if (typeof localStorage === "undefined")
1631
+ return fallback;
1632
+ const stored = localStorage.getItem(PREFS_KEY);
1633
+ return stored != null ? { ...fallback, ...JSON.parse(stored) } : fallback;
1634
+ } catch (_e) {
1635
+ return fallback;
1636
+ }
1637
+ }
1638
+ function writePrefs(prefs) {
1639
+ try {
1640
+ localStorage.setItem(PREFS_KEY, JSON.stringify(prefs));
1641
+ } catch (_e) {
1642
+ return;
1643
+ }
1644
+ }
1645
+ function getHandlerKey(entry) {
1646
+ const hop = entry.meta.routing[0];
1647
+ if (hop == null)
1648
+ return "none";
1649
+ if (hop.handlerType === "local")
1650
+ return "local";
1651
+ return `ext:${hop.transport ?? "ext"}`;
1652
+ }
1653
+ function canGroupWith(a, b) {
1654
+ if (a.status === "running" || b.status === "running")
1655
+ return false;
1656
+ const handlerA = getHandlerKey(a);
1657
+ const handlerB = getHandlerKey(b);
1658
+ const handlerConflict = handlerA !== "none" && handlerB !== "none" && handlerA !== handlerB;
1659
+ return a.actionId === b.actionId && a.domain === b.domain && a.status === b.status && !handlerConflict && safeStringify(a.input, 0) === safeStringify(b.input, 0);
1660
+ }
1661
+ function groupEntries(entries) {
1662
+ const groups = [];
1663
+ for (const entry of entries) {
1664
+ const last = groups[groups.length - 1];
1665
+ if (last != null && canGroupWith(entry, last.representative)) {
1666
+ last.rest.push(entry);
1667
+ } else {
1668
+ groups.push({ representative: entry, rest: [] });
1669
+ }
1670
+ }
1671
+ return groups;
1672
+ }
1673
+ function NiceActionDevtools(props) {
1674
+ if (false) {}
1675
+ return /* @__PURE__ */ jsxDEV15(NiceActionDevtools_Panel, {
1676
+ ...props
1677
+ }, undefined, false, undefined, this);
1678
+ }
1679
+ function NiceActionDevtools_Panel({
1680
+ core,
1681
+ position: defaultPosition = "bottom-right",
1682
+ initialOpen = false
1683
+ }) {
1684
+ const [prefs, setPrefsRaw] = useState6(() => readPrefs(defaultPosition, initialOpen));
1685
+ const [entries, setEntries] = useState6([]);
1686
+ const [selectedCuid, setSelectedCuid] = useState6(null);
1687
+ const [expandedGroupCuids, setExpandedGroupCuids] = useState6(new Set);
1688
+ const [domainTooltip, setDomainTooltip] = useState6(null);
1689
+ const showDomainTooltip = useCallback((rect, allDomains) => setDomainTooltip({ rect, allDomains }), []);
1690
+ const hideDomainTooltip = useCallback(() => setDomainTooltip(null), []);
1691
+ const domainTooltipCtx = useMemo2(() => ({ show: showDomainTooltip, hide: hideDomainTooltip }), [showDomainTooltip, hideDomainTooltip]);
1692
+ useEffect2(() => core.subscribe(setEntries), [core]);
1693
+ const groups = useMemo2(() => {
1694
+ const byCuid = new Map(entries.map((e) => [e.cuid, e]));
1695
+ const roots = entries.filter((e) => e.parentCuid == null || !byCuid.has(e.parentCuid));
1696
+ return groupEntries(roots);
1697
+ }, [entries]);
1698
+ const childExternalLabelsMap = useMemo2(() => {
1699
+ const map = new Map;
1700
+ for (const entry of entries) {
1701
+ if (entry.parentCuid == null)
1702
+ continue;
1703
+ const firstHop = entry.meta.routing[0];
1704
+ if (firstHop == null)
1705
+ continue;
1706
+ const label = getExternalLabel(firstHop);
1707
+ if (label == null)
1708
+ continue;
1709
+ const existing = map.get(entry.parentCuid) ?? [];
1710
+ if (!existing.includes(label))
1711
+ map.set(entry.parentCuid, [...existing, label]);
1712
+ }
1713
+ return map;
1714
+ }, [entries]);
1715
+ const handleGroupRowClick = (group) => {
1716
+ const repCuid = group.representative.cuid;
1717
+ const isExpanded = expandedGroupCuids.has(repCuid);
1718
+ const isSelected = selectedCuid === repCuid;
1719
+ if (group.rest.length === 0) {
1720
+ setSelectedCuid(isSelected ? null : repCuid);
1721
+ setExpandedGroupCuids(new Set);
1722
+ } else if (isSelected && isExpanded) {
1723
+ setSelectedCuid(null);
1724
+ setExpandedGroupCuids(new Set);
1725
+ } else {
1726
+ setSelectedCuid(repCuid);
1727
+ setExpandedGroupCuids(new Set([repCuid]));
1728
+ }
1729
+ };
1730
+ const setPrefs = (update) => {
1731
+ setPrefsRaw((prev) => {
1732
+ const next = { ...prev, ...update };
1733
+ writePrefs(next);
1734
+ return next;
1735
+ });
1736
+ };
1737
+ const { position, isOpen, dockedHeight, dockedWidth } = prefs;
1738
+ const dockSide = getDockSide(position);
1739
+ const isHorizDock = dockSide === "top" || dockSide === "bottom";
1740
+ const dockedSize = isHorizDock ? dockedHeight : dockedWidth;
1741
+ const selectedEntry = selectedCuid != null ? entries.find((e) => e.cuid === selectedCuid) : null;
1742
+ const runningCount = entries.filter((e) => e.status === "running").length;
1743
+ useEffect2(() => {
1744
+ const sides = ["top", "bottom", "left", "right"];
1745
+ const clearAll = () => {
1746
+ sides.forEach((s) => {
1747
+ document.body.style.removeProperty(`margin-${s}`);
1748
+ });
1749
+ };
1750
+ if (!isOpen || dockSide == null) {
1751
+ clearAll();
1752
+ return clearAll;
1753
+ }
1754
+ clearAll();
1755
+ document.body.style.setProperty(`margin-${dockSide}`, `${dockedSize}px`);
1756
+ return clearAll;
1757
+ }, [dockSide, isOpen, dockedSize]);
1758
+ const closedAnchor = (() => {
1759
+ switch (position) {
1760
+ case "dock-bottom":
1761
+ return { bottom: "16px", right: "16px" };
1762
+ case "dock-top":
1763
+ return { top: "16px", right: "16px" };
1764
+ case "dock-left":
1765
+ return { top: "16px", left: "16px" };
1766
+ case "dock-right":
1767
+ return { top: "16px", right: "16px" };
1768
+ default:
1769
+ return {
1770
+ ...position.includes("right") ? { right: "16px" } : { left: "16px" },
1771
+ ...position.includes("bottom") ? { bottom: "16px" } : { top: "16px" }
1772
+ };
1773
+ }
1774
+ })();
1775
+ const baseStyle = {
1776
+ position: "fixed",
1777
+ zIndex: 2147483647,
1778
+ fontFamily: "ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace",
1779
+ fontSize: "12px"
1780
+ };
1781
+ if (!isOpen) {
1782
+ return /* @__PURE__ */ jsxDEV15("button", {
1783
+ onClick: () => setPrefs({ isOpen: true }),
1784
+ style: {
1785
+ ...baseStyle,
1786
+ ...closedAnchor,
1787
+ background: "#1e293b",
1788
+ color: "#94a3b8",
1789
+ border: "1px solid #334155",
1790
+ borderRadius: "6px",
1791
+ padding: "5px 10px",
1792
+ cursor: "pointer",
1793
+ lineHeight: "1.5"
1794
+ },
1795
+ children: [
1796
+ "⚡ actions",
1797
+ runningCount > 0 && /* @__PURE__ */ jsxDEV15("span", {
1798
+ style: {
1799
+ marginLeft: "6px",
1800
+ color: "#60a5fa",
1801
+ animation: "__nice-action-pulse 1.2s ease-in-out infinite"
1802
+ },
1803
+ children: [
1804
+ runningCount,
1805
+ " running"
1806
+ ]
1807
+ }, undefined, true, undefined, this)
1808
+ ]
1809
+ }, undefined, true, undefined, this);
1810
+ }
1811
+ const panelStyle = dockSide != null ? {
1812
+ ...baseStyle,
1813
+ background: "#0f172a",
1814
+ border: "1px solid #1e293b",
1815
+ color: "#e2e8f0",
1816
+ display: "flex",
1817
+ flexDirection: "column",
1818
+ boxShadow: "0 -4px 24px rgba(0,0,0,0.4)",
1819
+ overflow: "hidden",
1820
+ ...dockSide === "bottom" ? {
1821
+ bottom: 0,
1822
+ left: 0,
1823
+ right: 0,
1824
+ height: `${dockedSize}px`,
1825
+ borderRadius: "8px 8px 0 0"
1826
+ } : dockSide === "top" ? {
1827
+ top: 0,
1828
+ left: 0,
1829
+ right: 0,
1830
+ height: `${dockedSize}px`,
1831
+ borderRadius: "0 0 8px 8px"
1832
+ } : dockSide === "left" ? {
1833
+ top: 0,
1834
+ left: 0,
1835
+ bottom: 0,
1836
+ width: `${dockedSize}px`,
1837
+ borderRadius: "0 8px 8px 0"
1838
+ } : {
1839
+ top: 0,
1840
+ right: 0,
1841
+ bottom: 0,
1842
+ width: `${dockedSize}px`,
1843
+ borderRadius: "8px 0 0 8px"
1844
+ }
1845
+ } : {
1846
+ ...baseStyle,
1847
+ ...closedAnchor,
1848
+ width: "460px",
1849
+ maxHeight: "560px",
1850
+ background: "#0f172a",
1851
+ border: "1px solid #1e293b",
1852
+ borderRadius: "10px",
1853
+ color: "#e2e8f0",
1854
+ display: "flex",
1855
+ flexDirection: "column",
1856
+ boxShadow: "0 25px 50px rgba(0,0,0,0.5)",
1857
+ overflow: "hidden"
1858
+ };
1859
+ const selectedEntryParent = selectedEntry?.parentCuid != null ? entries.find((e) => e.cuid === selectedEntry.parentCuid) ?? null : null;
1860
+ const selectedEntryChildren = selectedEntry != null ? [...entries].filter((e) => e.parentCuid === selectedEntry.cuid).sort((a, b) => a.startTime - b.startTime) : [];
1861
+ const virtualListProps = {
1862
+ groups,
1863
+ selectedCuid,
1864
+ expandedGroupCuids,
1865
+ onGroupClick: handleGroupRowClick,
1866
+ onSubClick: (cuid, isSelected) => setSelectedCuid(isSelected ? null : cuid),
1867
+ childExternalLabelsMap
1868
+ };
1869
+ return /* @__PURE__ */ jsxDEV15(DomainTooltipCtx.Provider, {
1870
+ value: domainTooltipCtx,
1871
+ children: /* @__PURE__ */ jsxDEV15("div", {
1872
+ style: panelStyle,
1873
+ children: [
1874
+ domainTooltip != null && /* @__PURE__ */ jsxDEV15(DomainHierarchyTooltip, {
1875
+ anchor: domainTooltip.rect,
1876
+ allDomains: domainTooltip.allDomains
1877
+ }, undefined, false, undefined, this),
1878
+ dockSide != null && /* @__PURE__ */ jsxDEV15(ResizeHandle, {
1879
+ dockSide,
1880
+ dockedSize,
1881
+ onChange: (size) => setPrefs(isHorizDock ? { dockedHeight: size } : { dockedWidth: size })
1882
+ }, undefined, false, undefined, this),
1883
+ /* @__PURE__ */ jsxDEV15(PanelHeader, {
1884
+ position,
1885
+ onPositionChange: (p) => setPrefs({ position: p }),
1886
+ onClose: () => setPrefs({ isOpen: false }),
1887
+ onClear: entries.length > 0 ? () => {
1888
+ core.clear();
1889
+ setSelectedCuid(null);
1890
+ setExpandedGroupCuids(new Set);
1891
+ } : undefined
1892
+ }, undefined, false, undefined, this),
1893
+ isHorizDock ? /* @__PURE__ */ jsxDEV15("div", {
1894
+ style: { flex: 1, display: "flex", overflow: "hidden" },
1895
+ children: [
1896
+ /* @__PURE__ */ jsxDEV15(VirtualActionList, {
1897
+ ...virtualListProps,
1898
+ style: {
1899
+ width: "340px",
1900
+ flexShrink: 0,
1901
+ overflowY: "auto",
1902
+ borderRight: "1px solid #1e293b"
1903
+ }
1904
+ }, undefined, false, undefined, this),
1905
+ /* @__PURE__ */ jsxDEV15("div", {
1906
+ style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" },
1907
+ children: selectedEntry != null ? /* @__PURE__ */ jsxDEV15(ActionDetailPanel, {
1908
+ entry: selectedEntry,
1909
+ parent: selectedEntryParent,
1910
+ childEntries: selectedEntryChildren,
1911
+ onSelectEntry: (cuid) => setSelectedCuid(cuid)
1912
+ }, selectedEntry.cuid, false, undefined, this) : /* @__PURE__ */ jsxDEV15("div", {
1913
+ style: {
1914
+ padding: "24px",
1915
+ textAlign: "center",
1916
+ color: "#475569",
1917
+ fontSize: "11px"
1918
+ },
1919
+ children: "Select an action to inspect"
1920
+ }, undefined, false, undefined, this)
1921
+ }, undefined, false, undefined, this)
1922
+ ]
1923
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV15(Fragment5, {
1924
+ children: [
1925
+ /* @__PURE__ */ jsxDEV15(VirtualActionList, {
1926
+ ...virtualListProps,
1927
+ style: {
1928
+ overflowY: "auto",
1929
+ flex: 1,
1930
+ minHeight: "80px",
1931
+ borderBottom: selectedEntry != null ? "1px solid #1e293b" : "none"
1932
+ }
1933
+ }, undefined, false, undefined, this),
1934
+ selectedEntry != null && /* @__PURE__ */ jsxDEV15("div", {
1935
+ style: {
1936
+ flexShrink: 0,
1937
+ maxHeight: "45%",
1938
+ display: "flex",
1939
+ flexDirection: "column",
1940
+ overflow: "hidden"
1941
+ },
1942
+ children: /* @__PURE__ */ jsxDEV15(ActionDetailPanel, {
1943
+ entry: selectedEntry,
1944
+ parent: selectedEntryParent,
1945
+ childEntries: selectedEntryChildren,
1946
+ onSelectEntry: (cuid) => setSelectedCuid(cuid)
1947
+ }, selectedEntry.cuid, false, undefined, this)
1948
+ }, undefined, false, undefined, this)
1949
+ ]
1950
+ }, undefined, true, undefined, this)
1951
+ ]
1952
+ }, undefined, true, undefined, this)
1953
+ }, undefined, false, undefined, this);
1954
+ }
1955
+ function VirtualActionList({
1956
+ groups,
1957
+ selectedCuid,
1958
+ expandedGroupCuids,
1959
+ onGroupClick,
1960
+ onSubClick,
1961
+ childExternalLabelsMap,
1962
+ style
1963
+ }) {
1964
+ const outerRef = useRef2(null);
1965
+ const latestTime = groups[0]?.representative.startTime;
1966
+ const flatItems = useMemo2(() => {
1967
+ const result = [];
1968
+ for (let gi = 0;gi < groups.length; gi++) {
1969
+ const group = groups[gi];
1970
+ const repCuid = group.representative.cuid;
1971
+ const isExpanded = expandedGroupCuids.has(repCuid) || group.rest.some((e) => e.cuid === selectedCuid);
1972
+ result.push({ type: "group", group, groupIndex: gi });
1973
+ if (isExpanded) {
1974
+ for (const entry of group.rest) {
1975
+ result.push({ type: "sub", entry, group });
1976
+ }
1977
+ }
1978
+ }
1979
+ return result;
1980
+ }, [groups, expandedGroupCuids, selectedCuid]);
1981
+ const flatItemsRef = useRef2(flatItems);
1982
+ flatItemsRef.current = flatItems;
1983
+ const virtualizer = useVirtualizer({
1984
+ count: flatItems.length,
1985
+ getScrollElement: () => outerRef.current,
1986
+ estimateSize: (i) => flatItemsRef.current[i]?.type === "sub" ? SUB_ROW_HEIGHT : ROW_HEIGHT,
1987
+ overscan: 5,
1988
+ measureElement: (el) => el.getBoundingClientRect().height
1989
+ });
1990
+ const prevGroupLenRef = useRef2(groups.length);
1991
+ useLayoutEffect(() => {
1992
+ const el = outerRef.current;
1993
+ if (el == null)
1994
+ return;
1995
+ const prev = prevGroupLenRef.current;
1996
+ prevGroupLenRef.current = groups.length;
1997
+ const added = groups.length - prev;
1998
+ if (added > 0 && el.scrollTop > 10) {
1999
+ el.scrollTop += added * ROW_HEIGHT;
2000
+ }
2001
+ }, [groups]);
2002
+ const prevSelectedRef = useRef2(selectedCuid);
2003
+ useEffect2(() => {
2004
+ if (selectedCuid === prevSelectedRef.current)
2005
+ return;
2006
+ prevSelectedRef.current = selectedCuid;
2007
+ if (selectedCuid == null)
2008
+ return;
2009
+ const idx = flatItemsRef.current.findIndex((item) => item.type === "group" && (item.group.representative.cuid === selectedCuid || item.group.rest.some((e) => e.cuid === selectedCuid)) || item.type === "sub" && item.entry.cuid === selectedCuid);
2010
+ if (idx >= 0)
2011
+ virtualizer.scrollToIndex(idx, { align: "auto" });
2012
+ }, [selectedCuid, virtualizer]);
2013
+ if (groups.length === 0) {
2014
+ return /* @__PURE__ */ jsxDEV15("div", {
2015
+ style,
2016
+ children: /* @__PURE__ */ jsxDEV15("div", {
2017
+ style: { padding: "24px", textAlign: "center", color: "#475569" },
2018
+ children: "No actions recorded yet"
2019
+ }, undefined, false, undefined, this)
2020
+ }, undefined, false, undefined, this);
1270
2021
  }
2022
+ const virtualItems = virtualizer.getVirtualItems();
2023
+ return /* @__PURE__ */ jsxDEV15("div", {
2024
+ ref: outerRef,
2025
+ style,
2026
+ children: /* @__PURE__ */ jsxDEV15("div", {
2027
+ style: { height: `${virtualizer.getTotalSize()}px`, position: "relative" },
2028
+ children: virtualItems.map((vi) => {
2029
+ const item = flatItems[vi.index];
2030
+ if (item == null)
2031
+ return null;
2032
+ return /* @__PURE__ */ jsxDEV15("div", {
2033
+ "data-index": vi.index,
2034
+ ref: virtualizer.measureElement,
2035
+ style: {
2036
+ position: "absolute",
2037
+ top: 0,
2038
+ left: 0,
2039
+ width: "100%",
2040
+ transform: `translateY(${vi.start}px)`,
2041
+ borderBottom: "1px solid #101109"
2042
+ },
2043
+ children: item.type === "group" ? /* @__PURE__ */ jsxDEV15(ActionEntryRow, {
2044
+ entry: item.group.representative,
2045
+ isSelected: selectedCuid === item.group.representative.cuid,
2046
+ groupCount: item.group.rest.length > 0 ? item.group.rest.length + 1 : undefined,
2047
+ isLatest: item.groupIndex === 0,
2048
+ latestTime,
2049
+ childExternalLabels: childExternalLabelsMap.get(item.group.representative.cuid),
2050
+ onClick: () => onGroupClick(item.group)
2051
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV15(ActionEntryRow, {
2052
+ entry: item.entry,
2053
+ isSelected: selectedCuid === item.entry.cuid,
2054
+ isSubEntry: true,
2055
+ isLatest: false,
2056
+ latestTime,
2057
+ onClick: () => onSubClick(item.entry.cuid, selectedCuid === item.entry.cuid)
2058
+ }, undefined, false, undefined, this)
2059
+ }, vi.key, false, undefined, this);
2060
+ })
2061
+ }, undefined, false, undefined, this)
2062
+ }, undefined, false, undefined, this);
1271
2063
  }
1272
2064
  export {
1273
2065
  NiceActionDevtools,