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