@nice-code/action 0.2.11 → 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 (32) hide show
  1. package/build/devtools/browser/index.js +2067 -0
  2. package/build/devtools/server/index.js +235 -0
  3. package/build/index.js +51 -21
  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/ActionRuntime.d.ts +4 -2
  8. package/build/types/ActionRuntime/HandlerCallStack.d.ts +3 -0
  9. package/build/types/ActionRuntime/RuntimeCoordinate.d.ts +9 -7
  10. package/build/types/devtools/browser/NiceActionDevtools.d.ts +8 -0
  11. package/build/types/devtools/browser/components/ActionDetailPanel.d.ts +7 -0
  12. package/build/types/devtools/browser/components/ActionEntryRow.d.ts +11 -0
  13. package/build/types/devtools/browser/components/CallStackSection.d.ts +8 -0
  14. package/build/types/devtools/browser/components/ChildDispatchChips.d.ts +4 -0
  15. package/build/types/devtools/browser/components/Chip.d.ts +11 -0
  16. package/build/types/devtools/browser/components/DetailSection.d.ts +5 -0
  17. package/build/types/devtools/browser/components/DomainChip.d.ts +14 -0
  18. package/build/types/devtools/browser/components/HandlerChips.d.ts +10 -0
  19. package/build/types/devtools/browser/components/MetaSection.d.ts +4 -0
  20. package/build/types/devtools/browser/components/PanelChrome.d.ts +14 -0
  21. package/build/types/devtools/browser/components/RoutingSection.d.ts +5 -0
  22. package/build/types/devtools/browser/components/RunningTimer.d.ts +7 -0
  23. package/build/types/devtools/browser/components/SectionLabel.d.ts +4 -0
  24. package/build/types/devtools/browser/components/StackTraceSection.d.ts +5 -0
  25. package/build/types/devtools/browser/components/utils.d.ts +7 -0
  26. package/build/types/devtools/browser/index.d.ts +3 -0
  27. package/build/types/devtools/core/ActionDevtools.types.d.ts +50 -0
  28. package/build/types/devtools/core/ActionDevtoolsCore.d.ts +14 -0
  29. package/build/types/devtools/server/NiceActionServerDevtools.d.ts +30 -0
  30. package/build/types/devtools/server/index.d.ts +3 -0
  31. package/build/types/index.d.ts +1 -1
  32. package/package.json +17 -3
@@ -0,0 +1,2067 @@
1
+ // src/ActionDefinition/Action/RunningAction.types.ts
2
+ var ERunningActionState;
3
+ ((ERunningActionState2) => {
4
+ ERunningActionState2["running"] = "running";
5
+ ERunningActionState2["completed"] = "completed";
6
+ })(ERunningActionState ||= {});
7
+ var ERunningActionUpdateType;
8
+ ((ERunningActionUpdateType2) => {
9
+ ERunningActionUpdateType2["started"] = "started";
10
+ ERunningActionUpdateType2["progress"] = "progress";
11
+ ERunningActionUpdateType2["finished"] = "finished";
12
+ })(ERunningActionUpdateType ||= {});
13
+ var ERunningActionFinishedType;
14
+ ((ERunningActionFinishedType2) => {
15
+ ERunningActionFinishedType2["aborted"] = "aborted";
16
+ ERunningActionFinishedType2["failed"] = "failed";
17
+ ERunningActionFinishedType2["success"] = "success";
18
+ })(ERunningActionFinishedType ||= {});
19
+
20
+ // src/devtools/core/ActionDevtoolsCore.ts
21
+ function extractRouting(context) {
22
+ return (context?.routing ?? []).map((item) => {
23
+ const handler = item.handler;
24
+ const isExternal = handler?.type === "external";
25
+ return {
26
+ runtime: {
27
+ envId: item.runtime?.envId ?? "unknown",
28
+ perId: item.runtime?.perId,
29
+ insId: item.runtime?.insId
30
+ },
31
+ handlerType: isExternal ? "external" : "local",
32
+ handlerClient: isExternal && handler.client != null ? {
33
+ envId: handler.client.envId ?? "unknown",
34
+ perId: handler.client.perId,
35
+ insId: handler.client.insId
36
+ } : undefined,
37
+ transport: isExternal ? handler.transType : undefined,
38
+ time: item.time
39
+ };
40
+ });
41
+ }
42
+ function extractMeta(context) {
43
+ return {
44
+ timeCreated: context?.timeCreated ?? Date.now(),
45
+ originClient: {
46
+ envId: context?.originClient?.envId ?? "unknown",
47
+ perId: context?.originClient?.perId,
48
+ insId: context?.originClient?.insId
49
+ },
50
+ routing: extractRouting(context)
51
+ };
52
+ }
53
+
54
+ class ActionDevtoolsCore {
55
+ _entries = [];
56
+ _listeners = new Set;
57
+ constructor(_options = {}) {}
58
+ attachToDomain(domain) {
59
+ return domain.addActionListener((update) => {
60
+ const { runningAction, type, time } = update;
61
+ if (type === "started" /* started */) {
62
+ const entry = {
63
+ cuid: runningAction.cuid,
64
+ actionId: runningAction.id,
65
+ domain: runningAction.domain,
66
+ allDomains: [...runningAction.allDomains],
67
+ status: "running",
68
+ startTime: time,
69
+ input: runningAction.state?.request?.input,
70
+ progressUpdates: [],
71
+ meta: extractMeta(runningAction.context),
72
+ parentCuid: runningAction.parentCuid,
73
+ callSite: runningAction.callSite
74
+ };
75
+ this._entries = [entry, ...this._entries];
76
+ this._notify();
77
+ } else if (type === "progress" /* progress */) {
78
+ this._updateEntry(runningAction.cuid, (e) => ({
79
+ ...e,
80
+ progressUpdates: [...e.progressUpdates, update.progress]
81
+ }));
82
+ } else if (type === "finished" /* finished */) {
83
+ this._updateEntry(runningAction.cuid, (e) => {
84
+ const finishedRoutingContext = update.response?.context ?? runningAction.context;
85
+ const base = {
86
+ ...e,
87
+ endTime: time,
88
+ meta: { ...e.meta, routing: extractRouting(finishedRoutingContext) }
89
+ };
90
+ const finishType = update.finishType;
91
+ if (finishType === "success" /* success */) {
92
+ return { ...base, status: "success", output: update.response?.result?.output };
93
+ }
94
+ if (finishType === "failed" /* failed */) {
95
+ const rawError = update.error;
96
+ const errorStack2 = rawError instanceof Error ? rawError.stack : undefined;
97
+ return { ...base, status: "failed", error: rawError, errorStack: errorStack2 };
98
+ }
99
+ const abortReason = update.reason;
100
+ const errorStack = abortReason instanceof Error ? abortReason.stack : undefined;
101
+ return { ...base, status: "aborted", abortReason, errorStack };
102
+ });
103
+ }
104
+ });
105
+ }
106
+ getEntries() {
107
+ return this._entries;
108
+ }
109
+ subscribe(listener) {
110
+ this._listeners.add(listener);
111
+ listener(this._entries);
112
+ return () => {
113
+ this._listeners.delete(listener);
114
+ };
115
+ }
116
+ clear() {
117
+ this._entries = [];
118
+ this._notify();
119
+ }
120
+ _updateEntry(cuid, updater) {
121
+ this._entries = this._entries.map((e) => e.cuid === cuid ? updater(e) : e);
122
+ this._notify();
123
+ }
124
+ _notify() {
125
+ const snapshot = this._entries;
126
+ for (const listener of this._listeners)
127
+ listener(snapshot);
128
+ }
129
+ }
130
+ // src/devtools/browser/NiceActionDevtools.tsx
131
+ import { useVirtualizer } from "@tanstack/react-virtual";
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);
245
+ }
246
+
247
+ // src/devtools/browser/components/RunningTimer.tsx
248
+ import { useEffect, useState } from "react";
249
+
250
+ // src/devtools/browser/components/utils.ts
251
+ var STATUS_COLOR = {
252
+ running: "#60a5fa",
253
+ success: "#4ade80",
254
+ failed: "#f87171",
255
+ aborted: "#9ca3af"
256
+ };
257
+ var STATUS_SYMBOL = {
258
+ running: "●",
259
+ success: "✓",
260
+ failed: "✗",
261
+ aborted: "○"
262
+ };
263
+ function safeStringify(value, indent = 2) {
264
+ if (value === undefined)
265
+ return "undefined";
266
+ if (value === null)
267
+ return "null";
268
+ try {
269
+ return JSON.stringify(value, null, indent);
270
+ } catch {
271
+ return String(value);
272
+ }
273
+ }
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`;
280
+ }
281
+ function formatDuration(entry) {
282
+ return entry.endTime != null ? `${entry.endTime - entry.startTime}ms` : null;
283
+ }
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
+ });
291
+ }
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);
307
+ }
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)
314
+ }, undefined, false, undefined, this);
315
+ }
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
339
+ }) {
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
+ },
354
+ children: [
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"
364
+ }, undefined, false, undefined, this),
365
+ /* @__PURE__ */ jsxDEV4("span", {
366
+ style: { color, fontSize: "10px", flexShrink: 0 },
367
+ children: symbol
368
+ }, undefined, false, undefined, this),
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
+ },
382
+ children: [
383
+ /* @__PURE__ */ jsxDEV4("span", {
384
+ children: entry.actionId
385
+ }, undefined, false, undefined, this),
386
+ /* @__PURE__ */ jsxDEV4(DomainChip, {
387
+ domain: entry.domain,
388
+ allDomains: entry.allDomains,
389
+ size: "sm"
390
+ }, undefined, false, undefined, this)
391
+ ]
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" },
419
+ children: [
420
+ parent != null && /* @__PURE__ */ jsxDEV4(CallStackLink, {
421
+ entry: parent,
422
+ entryRole: "caller",
423
+ isFocused: false,
424
+ onClick: () => onSelectParent(parent.cuid)
425
+ }, 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))
432
+ ]
433
+ }, undefined, true, undefined, this)
434
+ ]
435
+ }, undefined, true, undefined, this);
436
+ }
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,
446
+ style
447
+ }) {
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
461
+ }, undefined, false, undefined, this);
462
+ }
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"
469
+ }) {
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", {
503
+ children: [
504
+ /* @__PURE__ */ jsxDEV7("div", {
505
+ style: {
506
+ display: "flex",
507
+ alignItems: "center",
508
+ justifyContent: "space-between",
509
+ marginBottom: "3px"
510
+ },
511
+ children: [
512
+ /* @__PURE__ */ jsxDEV7("div", {
513
+ style: {
514
+ color,
515
+ fontSize: "10px",
516
+ textTransform: "uppercase",
517
+ letterSpacing: "0.05em"
518
+ },
519
+ children: label
520
+ }, undefined, false, undefined, this),
521
+ canExpand && /* @__PURE__ */ jsxDEV7("span", {
522
+ style: { color: "#334155", fontSize: "11px" },
523
+ children: expanded ? "▾" : "▸"
524
+ }, undefined, false, undefined, this)
525
+ ]
526
+ }, undefined, true, undefined, this),
527
+ expanded ? /* @__PURE__ */ jsxDEV7("div", {
528
+ onClick: canExpand ? () => setExpanded(false) : undefined,
529
+ style: {
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"
541
+ },
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)
559
+ ]
560
+ }, undefined, true, undefined, this);
561
+ }
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"}`;
569
+ }
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, {
577
+ children: [
578
+ localEnvId != null && (alwaysShowLocal || externalLabel == null) && /* @__PURE__ */ jsxDEV8(Chip, {
579
+ color: "#34d399",
580
+ borderColor: "#14532d",
581
+ fontSize,
582
+ padding,
583
+ children: localEnvId
584
+ }, undefined, false, undefined, this),
585
+ externalLabel != null && /* @__PURE__ */ jsxDEV8(Chip, {
586
+ color: "#fbbf24",
587
+ borderColor: "#78350f",
588
+ fontSize,
589
+ padding,
590
+ children: externalLabel
591
+ }, undefined, false, undefined, this)
592
+ ]
593
+ }, undefined, true, undefined, this);
594
+ }
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" },
602
+ children: [
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", {
629
+ style: {
630
+ display: "flex",
631
+ alignItems: "center",
632
+ justifyContent: "space-between",
633
+ marginBottom: "3px"
634
+ },
635
+ children: [
636
+ /* @__PURE__ */ jsxDEV9("div", {
637
+ style: {
638
+ color: "#60a5fa",
639
+ fontSize: "10px",
640
+ textTransform: "uppercase",
641
+ letterSpacing: "0.05em"
642
+ },
643
+ children: "Meta"
644
+ }, undefined, false, undefined, this),
645
+ /* @__PURE__ */ jsxDEV9("span", {
646
+ style: { color: "#334155", fontSize: "11px" },
647
+ children: expanded ? "▾" : "▸"
648
+ }, undefined, false, undefined, this)
649
+ ]
650
+ }, undefined, true, undefined, this),
651
+ expanded ? /* @__PURE__ */ jsxDEV9("div", {
652
+ onClick: () => setExpanded(false),
653
+ style: { background: "#1e293b", borderRadius: "4px", overflow: "hidden", cursor: "pointer" },
654
+ children: expandedRows.map(({ label, value }, i) => /* @__PURE__ */ jsxDEV9("div", {
655
+ style: {
656
+ display: "grid",
657
+ gridTemplateColumns: "52px 1fr",
658
+ columnGap: "8px",
659
+ padding: "4px 8px",
660
+ borderBottom: i < expandedRows.length - 1 ? "1px solid #0f172a" : "none",
661
+ alignItems: "start"
662
+ },
663
+ children: [
664
+ /* @__PURE__ */ jsxDEV9("span", {
665
+ style: { textAlign: "left", color: "#475569", fontSize: "10px", paddingTop: "1px" },
666
+ children: label
667
+ }, undefined, false, undefined, this),
668
+ /* @__PURE__ */ jsxDEV9("span", {
669
+ style: { textAlign: "left", color: "#cbd5e1", fontSize: "11px", wordBreak: "break-all" },
670
+ children: value
671
+ }, undefined, false, undefined, this)
672
+ ]
673
+ }, label, true, undefined, this))
674
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV9("div", {
675
+ onClick: () => setExpanded(true),
676
+ style: {
677
+ background: "#1e293b",
678
+ borderRadius: "4px",
679
+ padding: "5px 8px",
680
+ fontSize: "11px",
681
+ display: "flex",
682
+ gap: "10px",
683
+ rowGap: "2px",
684
+ flexWrap: "wrap",
685
+ cursor: "pointer"
686
+ },
687
+ children: [
688
+ /* @__PURE__ */ jsxDEV9(MetaChip, {
689
+ label: "cuid",
690
+ value: `…${cuid.slice(-8)}`
691
+ }, undefined, false, undefined, this),
692
+ /* @__PURE__ */ jsxDEV9(MetaChip, {
693
+ label: "origin",
694
+ value: meta.originClient.envId
695
+ }, undefined, false, undefined, this),
696
+ meta.originClient.perId != null && /* @__PURE__ */ jsxDEV9(MetaChip, {
697
+ label: "perId",
698
+ value: meta.originClient.perId.length > 10 ? `${meta.originClient.perId.slice(0, 10)}…` : meta.originClient.perId
699
+ }, undefined, false, undefined, this),
700
+ meta.originClient.insId != null && /* @__PURE__ */ jsxDEV9(MetaChip, {
701
+ label: "insId",
702
+ value: meta.originClient.insId.length > 10 ? `${meta.originClient.insId.slice(0, 10)}…` : meta.originClient.insId
703
+ }, undefined, false, undefined, this)
704
+ ]
705
+ }, undefined, true, undefined, this)
706
+ ]
707
+ }, undefined, true, undefined, this);
708
+ }
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
+ }) {
716
+ const { meta, startTime } = entry;
717
+ const hopCount = meta.routing.length;
718
+ const phantomCount = Math.max(0, minHopCount - hopCount);
719
+ if (hopCount === 0 && phantomCount === 0)
720
+ return null;
721
+ return /* @__PURE__ */ jsxDEV10("div", {
722
+ children: [
723
+ /* @__PURE__ */ jsxDEV10(SectionLabel, {
724
+ label: hopCount > 0 ? `Routing · ${hopCount} hop${hopCount !== 1 ? "s" : ""}` : "Routing"
725
+ }, undefined, false, undefined, this),
726
+ /* @__PURE__ */ jsxDEV10("div", {
727
+ style: { display: "flex", flexDirection: "column", gap: "2px" },
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",
807
+ style: {
808
+ visibility: "hidden",
809
+ background: "#1e293b",
810
+ borderRadius: "4px",
811
+ display: "grid",
812
+ gridTemplateColumns: "30% 1fr 30%",
813
+ alignItems: "center",
814
+ columnGap: "8px",
815
+ padding: "4px 8px"
816
+ },
817
+ children: [
818
+ /* @__PURE__ */ jsxDEV10("span", {
819
+ style: { display: "flex", flexDirection: "row", alignItems: "center", gap: "10px" },
820
+ children: [
821
+ /* @__PURE__ */ jsxDEV10("span", {
822
+ style: { fontSize: "10px", width: "16px" },
823
+ children: "0"
824
+ }, undefined, false, undefined, this),
825
+ /* @__PURE__ */ jsxDEV10("span", {
826
+ style: { fontSize: "11px" },
827
+ children: "placeholder"
828
+ }, undefined, false, undefined, this)
829
+ ]
830
+ }, undefined, true, undefined, this),
831
+ /* @__PURE__ */ jsxDEV10("span", {
832
+ style: { fontSize: "10px" },
833
+ children: "placeholder"
834
+ }, undefined, false, undefined, this),
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", {
955
+ style: {
956
+ color: isUser ? "#64748b" : "#2d3f53",
957
+ fontSize: "10px",
958
+ minWidth: "10px",
959
+ textAlign: "left",
960
+ flexShrink: 0,
961
+ fontVariantNumeric: "tabular-nums"
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 },
967
+ children: [
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)
981
+ }, undefined, false, undefined, this),
982
+ /* @__PURE__ */ jsxDEV11("div", {
983
+ style: { display: "flex", alignItems: "baseline", overflow: "hidden" },
984
+ children: [
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)
1006
+ ]
1007
+ }, undefined, true, undefined, this)
1008
+ ]
1009
+ }, undefined, true, undefined, this)
1010
+ ]
1011
+ }, `${frame.fn ?? "anon"}@${frame.file ?? "unknown"}`, true, undefined, this);
1012
+ })
1013
+ }, undefined, false, undefined, this)
1014
+ ]
1015
+ }, undefined, true, undefined, this);
1016
+ }
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,
1031
+ style: {
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"
1041
+ },
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);
1128
+ }
1129
+ function ActionDetailPanel({
1130
+ entry,
1131
+ parent,
1132
+ childEntries,
1133
+ onSelectEntry
1134
+ }) {
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
+ },
1165
+ children: [
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", {
1173
+ style: {
1174
+ flex: 1,
1175
+ overflowY: "auto",
1176
+ minHeight: 0,
1177
+ padding: "10px 12px",
1178
+ display: "flex",
1179
+ flexDirection: "column",
1180
+ gap: "8px"
1181
+ },
1182
+ children: [
1183
+ /* @__PURE__ */ jsxDEV12(CallStackSection, {
1184
+ parent,
1185
+ childEntries,
1186
+ focusedChildCuid,
1187
+ onFocusChild: handleFocusChild,
1188
+ onSelectParent: onSelectEntry
1189
+ }, undefined, false, undefined, this),
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
1242
+ }, undefined, false, undefined, this)
1243
+ ]
1244
+ }, undefined, true, undefined, this)
1245
+ ]
1246
+ }, undefined, true, undefined, this);
1247
+ }
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);
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);
2063
+ }
2064
+ export {
2065
+ NiceActionDevtools,
2066
+ ActionDevtoolsCore
2067
+ };