@react-spot/core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1756 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region \0rolldown/runtime.js
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __exportAll = (all, no_symbols) => {
8
+ let target = {};
9
+ for (var name in all) {
10
+ __defProp(target, name, {
11
+ get: all[name],
12
+ enumerable: true
13
+ });
14
+ }
15
+ if (!no_symbols) {
16
+ __defProp(target, Symbol.toStringTag, { value: "Module" });
17
+ }
18
+ return target;
19
+ };
20
+ var __copyProps = (to, from, except, desc) => {
21
+ if (from && typeof from === "object" || typeof from === "function") {
22
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
23
+ key = keys[i];
24
+ if (!__hasOwnProp.call(to, key) && key !== except) {
25
+ __defProp(to, key, {
26
+ get: ((k) => from[k]).bind(null, key),
27
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
28
+ });
29
+ }
30
+ }
31
+ }
32
+ return to;
33
+ };
34
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
35
+
36
+ //#endregion
37
+ const require_platform = require('./platform-CgftkgwS.cjs');
38
+ let react = require("react");
39
+ let react_dom = require("react-dom");
40
+ let jotai_utils = require("jotai/utils");
41
+ let react_jsx_runtime = require("react/jsx-runtime");
42
+ let _react_spot_ui_components = require("@react-spot/ui-components");
43
+
44
+ //#region ../../node_modules/.pnpm/jotai@2.18.0_@babel+core@7.29.0_@babel+template@7.28.6_@types+react@19.2.14_react@19.2.4/node_modules/jotai/esm/index.mjs
45
+ var esm_exports = /* @__PURE__ */ __exportAll({});
46
+ __reExport(esm_exports, require("jotai/vanilla"));
47
+ __reExport(esm_exports, require("jotai/react"));
48
+
49
+ //#endregion
50
+ //#region ../../node_modules/.pnpm/jotai-family@1.0.1_jotai@2.18.0_@babel+core@7.29.0_@babel+template@7.28.6_@types+react@19.2.14_react@19.2.4_/node_modules/jotai-family/dist/atomFamily.js
51
+ function atomFamily(initializeAtom, areEqual) {
52
+ let shouldRemove = null;
53
+ const atoms = /* @__PURE__ */ new Map();
54
+ const listeners = /* @__PURE__ */ new Set();
55
+ function createAtom(param) {
56
+ let item;
57
+ if (areEqual === void 0) item = atoms.get(param);
58
+ else for (const [key, value] of atoms) if (areEqual(key, param)) {
59
+ item = value;
60
+ break;
61
+ }
62
+ if (item !== void 0) if (shouldRemove?.(item[1], param)) createAtom.remove(param);
63
+ else return item[0];
64
+ const newAtom = initializeAtom(param);
65
+ atoms.set(param, [newAtom, Date.now()]);
66
+ notifyListeners("CREATE", param, newAtom);
67
+ return newAtom;
68
+ }
69
+ function notifyListeners(type, param, atom) {
70
+ for (const listener of listeners) listener({
71
+ type,
72
+ param,
73
+ atom
74
+ });
75
+ }
76
+ createAtom.unstable_listen = (callback) => {
77
+ listeners.add(callback);
78
+ return () => {
79
+ listeners.delete(callback);
80
+ };
81
+ };
82
+ createAtom.getParams = () => atoms.keys();
83
+ createAtom.remove = (param) => {
84
+ if (areEqual === void 0) {
85
+ if (!atoms.has(param)) return;
86
+ const [atom] = atoms.get(param);
87
+ atoms.delete(param);
88
+ notifyListeners("REMOVE", param, atom);
89
+ } else for (const [key, [atom]] of atoms) if (areEqual(key, param)) {
90
+ atoms.delete(key);
91
+ notifyListeners("REMOVE", key, atom);
92
+ break;
93
+ }
94
+ };
95
+ createAtom.setShouldRemove = (fn) => {
96
+ shouldRemove = fn;
97
+ if (!shouldRemove) return;
98
+ for (const [key, [atom, createdAt]] of atoms) if (shouldRemove(createdAt, key)) {
99
+ atoms.delete(key);
100
+ notifyListeners("REMOVE", key, atom);
101
+ }
102
+ };
103
+ return createAtom;
104
+ }
105
+
106
+ //#endregion
107
+ //#region src/store.ts
108
+ /**
109
+ * The scoped Jotai store used by the Trace widget.
110
+ * Created once per `<Trace />` mount via the Provider in Trace.tsx.
111
+ * Plugins import atoms from this module and read them through the hooks in
112
+ * `./hooks.ts`, which are scoped to the nearest Provider.
113
+ */
114
+ /**
115
+ * Absolute path to the project root.
116
+ */
117
+ const projectRootAtom = (0, esm_exports.atom)("");
118
+ /**
119
+ * Settings atom per project root, persisted in localStorage.
120
+ */
121
+ const settingsAtom = atomFamily((root) => (0, jotai_utils.atomWithStorage)(`react-trace:settings:${root}`, { core: {
122
+ position: "bottom-right",
123
+ minimized: false
124
+ } }, (0, jotai_utils.createJSONStorage)(() => localStorage), { getOnInit: typeof window !== "undefined" }));
125
+ const currentSettingsAtom = (0, esm_exports.atom)((get) => get(settingsAtom(get(projectRootAtom))), (get, set, value) => {
126
+ set(settingsAtom(get(projectRootAtom)), value);
127
+ });
128
+ const settingsPluginFamily = atomFamily((pluginKey) => (0, esm_exports.atom)((get) => get(currentSettingsAtom)[pluginKey], (get, set, value) => {
129
+ set(currentSettingsAtom, {
130
+ ...get(currentSettingsAtom),
131
+ [pluginKey]: value
132
+ });
133
+ }));
134
+ /**
135
+ * Atom family for plugin-specific settings.
136
+ * @param pluginKey
137
+ * @returns
138
+ */
139
+ function settingsPluginAtom(pluginKey) {
140
+ return settingsPluginFamily(pluginKey);
141
+ }
142
+ const coreSettingsAtom = settingsPluginFamily("core");
143
+ /**
144
+ * The portal container element that the widget renders into. Plugins can read
145
+ * this to mount their own portals inside the same container.
146
+ */
147
+ const portalContainerAtom = (0, esm_exports.atom)(() => {
148
+ const existing = document.querySelector("[data-react-trace]");
149
+ if (existing) return existing;
150
+ const container = document.createElement("div");
151
+ container.setAttribute("data-react-trace", "");
152
+ container.style.cssText = "position:fixed;inset:0;pointer-events:none;z-index:999997;";
153
+ document.body.appendChild(container);
154
+ return container;
155
+ });
156
+ /**
157
+ * Inspector active state.
158
+ */
159
+ const inspectorActiveAtom = (0, esm_exports.atom)(false);
160
+ /**
161
+ * Editor to open files in when clicking a component.
162
+ */
163
+ const editorAtom = (0, esm_exports.atom)("vscode");
164
+ /**
165
+ * The component context that is currently selected in the inspector.
166
+ */
167
+ const selectedContextAtom = (0, esm_exports.atom)(null);
168
+ /**
169
+ * The source of the currently selected file.
170
+ */
171
+ const selectedSourceAtom = (0, esm_exports.atom)(null);
172
+ /**
173
+ * Creates a new Jotai store instance for Trace widget.
174
+ */
175
+ const createWidgetStore = () => (0, esm_exports.createStore)();
176
+
177
+ //#endregion
178
+ //#region ../../node_modules/.pnpm/@jridgewell+sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs
179
+ var comma = ",".charCodeAt(0);
180
+ var semicolon = ";".charCodeAt(0);
181
+ var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
182
+ var intToChar = new Uint8Array(64);
183
+ var charToInt = new Uint8Array(128);
184
+ for (let i = 0; i < chars.length; i++) {
185
+ const c = chars.charCodeAt(i);
186
+ intToChar[i] = c;
187
+ charToInt[c] = i;
188
+ }
189
+ function decodeInteger(reader, relative) {
190
+ let value = 0;
191
+ let shift = 0;
192
+ let integer = 0;
193
+ do {
194
+ integer = charToInt[reader.next()];
195
+ value |= (integer & 31) << shift;
196
+ shift += 5;
197
+ } while (integer & 32);
198
+ const shouldNegate = value & 1;
199
+ value >>>= 1;
200
+ if (shouldNegate) value = -2147483648 | -value;
201
+ return relative + value;
202
+ }
203
+ function hasMoreVlq(reader, max) {
204
+ if (reader.pos >= max) return false;
205
+ return reader.peek() !== comma;
206
+ }
207
+ var bufLength = 1024 * 16;
208
+ var StringReader = class {
209
+ constructor(buffer) {
210
+ this.pos = 0;
211
+ this.buffer = buffer;
212
+ }
213
+ next() {
214
+ return this.buffer.charCodeAt(this.pos++);
215
+ }
216
+ peek() {
217
+ return this.buffer.charCodeAt(this.pos);
218
+ }
219
+ indexOf(char) {
220
+ const { buffer, pos } = this;
221
+ const idx = buffer.indexOf(char, pos);
222
+ return idx === -1 ? buffer.length : idx;
223
+ }
224
+ };
225
+ function decode(mappings) {
226
+ const { length } = mappings;
227
+ const reader = new StringReader(mappings);
228
+ const decoded = [];
229
+ let genColumn = 0;
230
+ let sourcesIndex = 0;
231
+ let sourceLine = 0;
232
+ let sourceColumn = 0;
233
+ let namesIndex = 0;
234
+ do {
235
+ const semi = reader.indexOf(";");
236
+ const line = [];
237
+ let sorted = true;
238
+ let lastCol = 0;
239
+ genColumn = 0;
240
+ while (reader.pos < semi) {
241
+ let seg;
242
+ genColumn = decodeInteger(reader, genColumn);
243
+ if (genColumn < lastCol) sorted = false;
244
+ lastCol = genColumn;
245
+ if (hasMoreVlq(reader, semi)) {
246
+ sourcesIndex = decodeInteger(reader, sourcesIndex);
247
+ sourceLine = decodeInteger(reader, sourceLine);
248
+ sourceColumn = decodeInteger(reader, sourceColumn);
249
+ if (hasMoreVlq(reader, semi)) {
250
+ namesIndex = decodeInteger(reader, namesIndex);
251
+ seg = [
252
+ genColumn,
253
+ sourcesIndex,
254
+ sourceLine,
255
+ sourceColumn,
256
+ namesIndex
257
+ ];
258
+ } else seg = [
259
+ genColumn,
260
+ sourcesIndex,
261
+ sourceLine,
262
+ sourceColumn
263
+ ];
264
+ } else seg = [genColumn];
265
+ line.push(seg);
266
+ reader.pos++;
267
+ }
268
+ if (!sorted) sort(line);
269
+ decoded.push(line);
270
+ reader.pos = semi + 1;
271
+ } while (reader.pos <= length);
272
+ return decoded;
273
+ }
274
+ function sort(line) {
275
+ line.sort(sortComparator$1);
276
+ }
277
+ function sortComparator$1(a, b) {
278
+ return a[0] - b[0];
279
+ }
280
+
281
+ //#endregion
282
+ //#region ../../node_modules/.pnpm/@jridgewell+resolve-uri@3.1.2/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.mjs
283
+ const schemeRegex = /^[\w+.-]+:\/\//;
284
+ /**
285
+ * Matches the parts of a URL:
286
+ * 1. Scheme, including ":", guaranteed.
287
+ * 2. User/password, including "@", optional.
288
+ * 3. Host, guaranteed.
289
+ * 4. Port, including ":", optional.
290
+ * 5. Path, including "/", optional.
291
+ * 6. Query, including "?", optional.
292
+ * 7. Hash, including "#", optional.
293
+ */
294
+ const urlRegex = /^([\w+.-]+:)\/\/([^@/#?]*@)?([^:/#?]*)(:\d+)?(\/[^#?]*)?(\?[^#]*)?(#.*)?/;
295
+ /**
296
+ * File URLs are weird. They dont' need the regular `//` in the scheme, they may or may not start
297
+ * with a leading `/`, they can have a domain (but only if they don't start with a Windows drive).
298
+ *
299
+ * 1. Host, optional.
300
+ * 2. Path, which may include "/", guaranteed.
301
+ * 3. Query, including "?", optional.
302
+ * 4. Hash, including "#", optional.
303
+ */
304
+ const fileRegex = /^file:(?:\/\/((?![a-z]:)[^/#?]*)?)?(\/?[^#?]*)(\?[^#]*)?(#.*)?/i;
305
+ function isAbsoluteUrl(input) {
306
+ return schemeRegex.test(input);
307
+ }
308
+ function isSchemeRelativeUrl(input) {
309
+ return input.startsWith("//");
310
+ }
311
+ function isAbsolutePath(input) {
312
+ return input.startsWith("/");
313
+ }
314
+ function isFileUrl(input) {
315
+ return input.startsWith("file:");
316
+ }
317
+ function isRelative(input) {
318
+ return /^[.?#]/.test(input);
319
+ }
320
+ function parseAbsoluteUrl(input) {
321
+ const match = urlRegex.exec(input);
322
+ return makeUrl(match[1], match[2] || "", match[3], match[4] || "", match[5] || "/", match[6] || "", match[7] || "");
323
+ }
324
+ function parseFileUrl(input) {
325
+ const match = fileRegex.exec(input);
326
+ const path = match[2];
327
+ return makeUrl("file:", "", match[1] || "", "", isAbsolutePath(path) ? path : "/" + path, match[3] || "", match[4] || "");
328
+ }
329
+ function makeUrl(scheme, user, host, port, path, query, hash) {
330
+ return {
331
+ scheme,
332
+ user,
333
+ host,
334
+ port,
335
+ path,
336
+ query,
337
+ hash,
338
+ type: 7
339
+ };
340
+ }
341
+ function parseUrl(input) {
342
+ if (isSchemeRelativeUrl(input)) {
343
+ const url = parseAbsoluteUrl("http:" + input);
344
+ url.scheme = "";
345
+ url.type = 6;
346
+ return url;
347
+ }
348
+ if (isAbsolutePath(input)) {
349
+ const url = parseAbsoluteUrl("http://foo.com" + input);
350
+ url.scheme = "";
351
+ url.host = "";
352
+ url.type = 5;
353
+ return url;
354
+ }
355
+ if (isFileUrl(input)) return parseFileUrl(input);
356
+ if (isAbsoluteUrl(input)) return parseAbsoluteUrl(input);
357
+ const url = parseAbsoluteUrl("http://foo.com/" + input);
358
+ url.scheme = "";
359
+ url.host = "";
360
+ url.type = input ? input.startsWith("?") ? 3 : input.startsWith("#") ? 2 : 4 : 1;
361
+ return url;
362
+ }
363
+ function stripPathFilename(path) {
364
+ if (path.endsWith("/..")) return path;
365
+ const index = path.lastIndexOf("/");
366
+ return path.slice(0, index + 1);
367
+ }
368
+ function mergePaths(url, base) {
369
+ normalizePath$1(base, base.type);
370
+ if (url.path === "/") url.path = base.path;
371
+ else url.path = stripPathFilename(base.path) + url.path;
372
+ }
373
+ /**
374
+ * The path can have empty directories "//", unneeded parents "foo/..", or current directory
375
+ * "foo/.". We need to normalize to a standard representation.
376
+ */
377
+ function normalizePath$1(url, type) {
378
+ const rel = type <= 4;
379
+ const pieces = url.path.split("/");
380
+ let pointer = 1;
381
+ let positive = 0;
382
+ let addTrailingSlash = false;
383
+ for (let i = 1; i < pieces.length; i++) {
384
+ const piece = pieces[i];
385
+ if (!piece) {
386
+ addTrailingSlash = true;
387
+ continue;
388
+ }
389
+ addTrailingSlash = false;
390
+ if (piece === ".") continue;
391
+ if (piece === "..") {
392
+ if (positive) {
393
+ addTrailingSlash = true;
394
+ positive--;
395
+ pointer--;
396
+ } else if (rel) pieces[pointer++] = piece;
397
+ continue;
398
+ }
399
+ pieces[pointer++] = piece;
400
+ positive++;
401
+ }
402
+ let path = "";
403
+ for (let i = 1; i < pointer; i++) path += "/" + pieces[i];
404
+ if (!path || addTrailingSlash && !path.endsWith("/..")) path += "/";
405
+ url.path = path;
406
+ }
407
+ /**
408
+ * Attempts to resolve `input` URL/path relative to `base`.
409
+ */
410
+ function resolve(input, base) {
411
+ if (!input && !base) return "";
412
+ const url = parseUrl(input);
413
+ let inputType = url.type;
414
+ if (base && inputType !== 7) {
415
+ const baseUrl = parseUrl(base);
416
+ const baseType = baseUrl.type;
417
+ switch (inputType) {
418
+ case 1: url.hash = baseUrl.hash;
419
+ case 2: url.query = baseUrl.query;
420
+ case 3:
421
+ case 4: mergePaths(url, baseUrl);
422
+ case 5:
423
+ url.user = baseUrl.user;
424
+ url.host = baseUrl.host;
425
+ url.port = baseUrl.port;
426
+ case 6: url.scheme = baseUrl.scheme;
427
+ }
428
+ if (baseType > inputType) inputType = baseType;
429
+ }
430
+ normalizePath$1(url, inputType);
431
+ const queryHash = url.query + url.hash;
432
+ switch (inputType) {
433
+ case 2:
434
+ case 3: return queryHash;
435
+ case 4: {
436
+ const path = url.path.slice(1);
437
+ if (!path) return queryHash || ".";
438
+ if (isRelative(base || input) && !isRelative(path)) return "./" + path + queryHash;
439
+ return path + queryHash;
440
+ }
441
+ case 5: return url.path + queryHash;
442
+ default: return url.scheme + "//" + url.user + url.host + url.port + url.path + queryHash;
443
+ }
444
+ }
445
+
446
+ //#endregion
447
+ //#region ../../node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.mjs
448
+ function stripFilename(path) {
449
+ if (!path) return "";
450
+ const index = path.lastIndexOf("/");
451
+ return path.slice(0, index + 1);
452
+ }
453
+ function resolver(mapUrl, sourceRoot) {
454
+ const from = stripFilename(mapUrl);
455
+ const prefix = sourceRoot ? sourceRoot + "/" : "";
456
+ return (source) => resolve(prefix + (source || ""), from);
457
+ }
458
+ var COLUMN = 0;
459
+ var SOURCES_INDEX = 1;
460
+ var SOURCE_LINE = 2;
461
+ var SOURCE_COLUMN = 3;
462
+ var NAMES_INDEX = 4;
463
+ function maybeSort(mappings, owned) {
464
+ const unsortedIndex = nextUnsortedSegmentLine(mappings, 0);
465
+ if (unsortedIndex === mappings.length) return mappings;
466
+ if (!owned) mappings = mappings.slice();
467
+ for (let i = unsortedIndex; i < mappings.length; i = nextUnsortedSegmentLine(mappings, i + 1)) mappings[i] = sortSegments(mappings[i], owned);
468
+ return mappings;
469
+ }
470
+ function nextUnsortedSegmentLine(mappings, start) {
471
+ for (let i = start; i < mappings.length; i++) if (!isSorted(mappings[i])) return i;
472
+ return mappings.length;
473
+ }
474
+ function isSorted(line) {
475
+ for (let j = 1; j < line.length; j++) if (line[j][COLUMN] < line[j - 1][COLUMN]) return false;
476
+ return true;
477
+ }
478
+ function sortSegments(line, owned) {
479
+ if (!owned) line = line.slice();
480
+ return line.sort(sortComparator);
481
+ }
482
+ function sortComparator(a, b) {
483
+ return a[COLUMN] - b[COLUMN];
484
+ }
485
+ var found = false;
486
+ function binarySearch(haystack, needle, low, high) {
487
+ while (low <= high) {
488
+ const mid = low + (high - low >> 1);
489
+ const cmp = haystack[mid][COLUMN] - needle;
490
+ if (cmp === 0) {
491
+ found = true;
492
+ return mid;
493
+ }
494
+ if (cmp < 0) low = mid + 1;
495
+ else high = mid - 1;
496
+ }
497
+ found = false;
498
+ return low - 1;
499
+ }
500
+ function upperBound(haystack, needle, index) {
501
+ for (let i = index + 1; i < haystack.length; index = i++) if (haystack[i][COLUMN] !== needle) break;
502
+ return index;
503
+ }
504
+ function lowerBound(haystack, needle, index) {
505
+ for (let i = index - 1; i >= 0; index = i--) if (haystack[i][COLUMN] !== needle) break;
506
+ return index;
507
+ }
508
+ function memoizedState() {
509
+ return {
510
+ lastKey: -1,
511
+ lastNeedle: -1,
512
+ lastIndex: -1
513
+ };
514
+ }
515
+ function memoizedBinarySearch(haystack, needle, state, key) {
516
+ const { lastKey, lastNeedle, lastIndex } = state;
517
+ let low = 0;
518
+ let high = haystack.length - 1;
519
+ if (key === lastKey) {
520
+ if (needle === lastNeedle) {
521
+ found = lastIndex !== -1 && haystack[lastIndex][COLUMN] === needle;
522
+ return lastIndex;
523
+ }
524
+ if (needle >= lastNeedle) low = lastIndex === -1 ? 0 : lastIndex;
525
+ else high = lastIndex;
526
+ }
527
+ state.lastKey = key;
528
+ state.lastNeedle = needle;
529
+ return state.lastIndex = binarySearch(haystack, needle, low, high);
530
+ }
531
+ function parse(map) {
532
+ return typeof map === "string" ? JSON.parse(map) : map;
533
+ }
534
+ var LINE_GTR_ZERO = "`line` must be greater than 0 (lines start at line 1)";
535
+ var COL_GTR_EQ_ZERO = "`column` must be greater than or equal to 0 (columns start at column 0)";
536
+ var LEAST_UPPER_BOUND = -1;
537
+ var GREATEST_LOWER_BOUND = 1;
538
+ var TraceMap = class {
539
+ constructor(map, mapUrl) {
540
+ const isString = typeof map === "string";
541
+ if (!isString && map._decodedMemo) return map;
542
+ const parsed = parse(map);
543
+ const { version, file, names, sourceRoot, sources, sourcesContent } = parsed;
544
+ this.version = version;
545
+ this.file = file;
546
+ this.names = names || [];
547
+ this.sourceRoot = sourceRoot;
548
+ this.sources = sources;
549
+ this.sourcesContent = sourcesContent;
550
+ this.ignoreList = parsed.ignoreList || parsed.x_google_ignoreList || void 0;
551
+ const resolve = resolver(mapUrl, sourceRoot);
552
+ this.resolvedSources = sources.map(resolve);
553
+ const { mappings } = parsed;
554
+ if (typeof mappings === "string") {
555
+ this._encoded = mappings;
556
+ this._decoded = void 0;
557
+ } else if (Array.isArray(mappings)) {
558
+ this._encoded = void 0;
559
+ this._decoded = maybeSort(mappings, isString);
560
+ } else if (parsed.sections) throw new Error(`TraceMap passed sectioned source map, please use FlattenMap export instead`);
561
+ else throw new Error(`invalid source map: ${JSON.stringify(parsed)}`);
562
+ this._decodedMemo = memoizedState();
563
+ this._bySources = void 0;
564
+ this._bySourceMemos = void 0;
565
+ }
566
+ };
567
+ function cast(map) {
568
+ return map;
569
+ }
570
+ function decodedMappings(map) {
571
+ var _a;
572
+ return (_a = cast(map))._decoded || (_a._decoded = decode(cast(map)._encoded));
573
+ }
574
+ function originalPositionFor(map, needle) {
575
+ let { line, column, bias } = needle;
576
+ line--;
577
+ if (line < 0) throw new Error(LINE_GTR_ZERO);
578
+ if (column < 0) throw new Error(COL_GTR_EQ_ZERO);
579
+ const decoded = decodedMappings(map);
580
+ if (line >= decoded.length) return OMapping(null, null, null, null);
581
+ const segments = decoded[line];
582
+ const index = traceSegmentInternal(segments, cast(map)._decodedMemo, line, column, bias || GREATEST_LOWER_BOUND);
583
+ if (index === -1) return OMapping(null, null, null, null);
584
+ const segment = segments[index];
585
+ if (segment.length === 1) return OMapping(null, null, null, null);
586
+ const { names, resolvedSources } = map;
587
+ return OMapping(resolvedSources[segment[SOURCES_INDEX]], segment[SOURCE_LINE] + 1, segment[SOURCE_COLUMN], segment.length === 5 ? names[segment[NAMES_INDEX]] : null);
588
+ }
589
+ function OMapping(source, line, column, name) {
590
+ return {
591
+ source,
592
+ line,
593
+ column,
594
+ name
595
+ };
596
+ }
597
+ function traceSegmentInternal(segments, memo, line, column, bias) {
598
+ let index = memoizedBinarySearch(segments, column, memo, line);
599
+ if (found) index = (bias === LEAST_UPPER_BOUND ? upperBound : lowerBound)(segments, column, index);
600
+ else if (bias === LEAST_UPPER_BOUND) index++;
601
+ if (index === -1 || index === segments.length) return -1;
602
+ return index;
603
+ }
604
+
605
+ //#endregion
606
+ //#region src/utils/path.ts
607
+ /**
608
+ * Shared path utilities for converting development source identifiers and
609
+ * absolute filesystem paths into relative or absolute forms needed by plugins.
610
+ *
611
+ * Handles the fileName conventions react-trace encounters:
612
+ * - Vite dev URL http://localhost:5173/src/App.tsx
613
+ * - Vite /@fs/ URL http://localhost:5173/@fs/abs/path/src/App.tsx
614
+ * - Next webpack URL webpack-internal:///(app-pages-browser)/./app/page.tsx
615
+ * - Turbopack URL turbopack:///[project]/app/page.tsx
616
+ * - Absolute path / URL /Users/you/project/src/App.tsx
617
+ */
618
+ const NEXT_SOURCE_GROUP_RE = /^\((?:app|pages)-[^/]+\)\//;
619
+ const TURBOPACK_PROJECT_PREFIX_RE = /^\[project\]\//;
620
+ function stripQueryAndHash(fileName) {
621
+ return fileName.split(/[?#]/)[0] ?? fileName;
622
+ }
623
+ function normalizeRoot(root) {
624
+ if (!root) return null;
625
+ return root.replace(/\\/g, "/").replace(/\/$/, "");
626
+ }
627
+ function normalizePath(path) {
628
+ return decodeURIComponent(path).replace(/\\/g, "/");
629
+ }
630
+ function stripRoot(path, root) {
631
+ const normalizedRoot = normalizeRoot(root);
632
+ if (normalizedRoot && path.startsWith(normalizedRoot + "/")) return path.slice(normalizedRoot.length + 1);
633
+ return path;
634
+ }
635
+ function stripWebpackRelativePrefix(path) {
636
+ return path.replace(/^\/+/, "").replace(/^\.\/+/, "");
637
+ }
638
+ function stripVirtualProjectPrefix(path) {
639
+ const relativePath = stripWebpackRelativePrefix(path);
640
+ if (TURBOPACK_PROJECT_PREFIX_RE.test(relativePath)) return relativePath.replace(TURBOPACK_PROJECT_PREFIX_RE, "");
641
+ return path;
642
+ }
643
+ function stripRootSuffixPrefix(relativePath, root) {
644
+ const normalizedRoot = normalizeRoot(root);
645
+ if (!normalizedRoot) return relativePath;
646
+ const rootParts = normalizedRoot.split("/").filter(Boolean);
647
+ const pathParts = relativePath.split("/").filter(Boolean);
648
+ const maxParts = Math.min(rootParts.length, pathParts.length);
649
+ for (let count = maxParts; count > 0; count--) if (rootParts.slice(-count).join("/") === pathParts.slice(0, count).join("/")) return pathParts.slice(count).join("/");
650
+ return relativePath;
651
+ }
652
+ /**
653
+ * Normalizes virtual module identifiers emitted by webpack/Turbopack source
654
+ * maps and React component stacks back to project-relative paths.
655
+ */
656
+ function toVirtualProjectPath(url) {
657
+ if (url.protocol === "webpack:" || url.protocol === "webpack-internal:") return stripWebpackRelativePrefix(normalizePath(url.pathname)).replace(NEXT_SOURCE_GROUP_RE, "");
658
+ if (url.protocol === "turbopack:") return stripVirtualProjectPrefix(normalizePath(url.pathname));
659
+ return null;
660
+ }
661
+ function joinRoot(root, relativePath) {
662
+ const normalizedRoot = normalizeRoot(root);
663
+ if (!normalizedRoot) return null;
664
+ return `${normalizedRoot}/${relativePath.replace(/^\/+/, "")}`;
665
+ }
666
+ /**
667
+ * Converts a source fileName to a path relative to the project root.
668
+ *
669
+ * Vite URL http://localhost:5173/src/App.tsx → src/App.tsx
670
+ * /@fs/ URL http://localhost:5173/@fs/abs/root/src/… → src/App.tsx (root required)
671
+ * Abs URL http://localhost:5173//abs/root/src/… → src/App.tsx (root required)
672
+ * Abs path /Users/you/project/src/App.tsx → src/App.tsx (root required)
673
+ */
674
+ function toRelativePath(fileName, root) {
675
+ const clean = stripQueryAndHash(fileName).trim();
676
+ try {
677
+ const url = new URL(clean);
678
+ const virtualPath = toVirtualProjectPath(url);
679
+ if (virtualPath) return stripRootSuffixPrefix(virtualPath, root);
680
+ const pathname = normalizePath(url.pathname);
681
+ if (url.protocol === "file:") return stripRoot(pathname, root);
682
+ if (pathname.startsWith("/@fs/") || pathname.startsWith("/Users/")) return stripRoot(pathname.startsWith("/@fs/") ? pathname.slice(4) : pathname, root);
683
+ const rootRelativePath = stripRoot(pathname, root);
684
+ if (rootRelativePath !== pathname) return rootRelativePath;
685
+ return pathname.replace(/^\//, "");
686
+ } catch {
687
+ const normalizedPath = normalizePath(clean);
688
+ const virtualPath = stripVirtualProjectPrefix(normalizedPath);
689
+ if (virtualPath !== normalizedPath) return stripRootSuffixPrefix(virtualPath, root);
690
+ return stripRoot(normalizedPath, root);
691
+ }
692
+ }
693
+ /**
694
+ * Converts a source fileName to an absolute filesystem path.
695
+ * Used by plugins that build editor URL schemes (vscode://, cursor://, etc.)
696
+ *
697
+ * Vite /@fs/ URL → strip /@fs prefix → /abs/path/src/App.tsx
698
+ * Vite URL → prepend root if provided → /project/src/App.tsx
699
+ * Abs URL → strip host → /abs/root/src/App.tsx
700
+ * Abs path → used as-is
701
+ *
702
+ * Returns null if the path is empty or cannot be resolved.
703
+ */
704
+ function toAbsolutePath(fileName, root) {
705
+ const clean = stripQueryAndHash(fileName).trim();
706
+ if (!clean) return null;
707
+ try {
708
+ const url = new URL(clean);
709
+ const virtualPath = toVirtualProjectPath(url);
710
+ if (virtualPath) {
711
+ const rootRelativePath = stripRootSuffixPrefix(virtualPath, root);
712
+ return joinRoot(root, rootRelativePath) ?? rootRelativePath;
713
+ }
714
+ const pathname = normalizePath(url.pathname);
715
+ if (url.protocol === "file:") return pathname;
716
+ if (pathname.startsWith("/Users/")) return pathname;
717
+ if (stripRoot(pathname, root) !== pathname) return pathname;
718
+ if (pathname.startsWith("/@fs/")) return pathname.slice(4);
719
+ return joinRoot(root, pathname) ?? pathname;
720
+ } catch {
721
+ const normalizedPath = normalizePath(clean);
722
+ const virtualPath = stripVirtualProjectPrefix(normalizedPath);
723
+ if (virtualPath !== normalizedPath) {
724
+ const rootRelativePath = stripRootSuffixPrefix(virtualPath, root);
725
+ return joinRoot(root, rootRelativePath) ?? rootRelativePath;
726
+ }
727
+ return normalizedPath;
728
+ }
729
+ }
730
+
731
+ //#endregion
732
+ //#region src/utils/fiber.ts
733
+ /**
734
+ * Per-URL cache of TraceMap promises.
735
+ * Each file is fetched at most once per page load — subsequent calls are instant
736
+ * cache hits. The cache is intentionally not invalidated on HMR because the
737
+ * compiled line numbers in the new _debugStack will also change, so the next
738
+ * hover naturally uses fresh positions against the new source map.
739
+ */
740
+ const traceMapCache = /* @__PURE__ */ new Map();
741
+ function loadTraceMap(fileUrl) {
742
+ const cached = traceMapCache.get(fileUrl);
743
+ if (cached !== void 0) return cached;
744
+ const promise = (async () => {
745
+ try {
746
+ const res = await fetch(fileUrl);
747
+ if (!res.ok) return null;
748
+ const sourceMapHeader = res.headers.get("SourceMap") ?? res.headers.get("X-SourceMap");
749
+ if (sourceMapHeader) {
750
+ const mapUrl = new URL(sourceMapHeader, fileUrl).href;
751
+ const mapRes = await fetch(mapUrl);
752
+ if (mapRes.ok) return new TraceMap(await mapRes.json(), mapUrl);
753
+ }
754
+ const code = await res.text();
755
+ const inlineMatch = code.match(/\/\/# sourceMappingURL=data:application\/json;(?:charset=[^;]+;)?base64,([A-Za-z0-9+/=]+)/);
756
+ if (inlineMatch) return new TraceMap(JSON.parse(atob(inlineMatch[1])), fileUrl);
757
+ const externalMatch = code.match(/\/\/# sourceMappingURL=([^\s]+)/);
758
+ if (externalMatch && !externalMatch[1].startsWith("data:")) {
759
+ const mapUrl = new URL(externalMatch[1], fileUrl).href;
760
+ const mapRes = await fetch(mapUrl);
761
+ if (!mapRes.ok) return null;
762
+ return new TraceMap(await mapRes.json(), mapUrl);
763
+ }
764
+ return null;
765
+ } catch {
766
+ return null;
767
+ }
768
+ })();
769
+ traceMapCache.set(fileUrl, promise);
770
+ return promise;
771
+ }
772
+ /**
773
+ * Detects Turbopack chunk file paths that can't be shown to the user as-is.
774
+ * These are generated files like `[project]/.next/static/chunks/_1rlvw7y._.js`
775
+ * or URL-form equivalents that still point at `/_next/static/chunks/`.
776
+ */
777
+ function isChunkPath(fileName) {
778
+ return fileName.includes(".next/static/chunks/") || fileName.includes("_next/static/chunks/");
779
+ }
780
+ /**
781
+ * For Turbopack virtual paths like `[project]/.next/static/chunks/foo.js`,
782
+ * constructs the full dev-server URL so we can fetch the source map.
783
+ * Returns null if the path doesn't look like a servable chunk.
784
+ */
785
+ function tryConstructChunkUrl(fileName) {
786
+ if (typeof globalThis.location === "undefined") return null;
787
+ const stripped = fileName.replace(/^\[project\]\//, "");
788
+ if (!stripped.includes(".next/static/chunks/")) return null;
789
+ const urlPath = stripped.replace(/^\.next\//, "_next/");
790
+ return `${globalThis.location.origin}/${urlPath}`;
791
+ }
792
+ /**
793
+ * Resolves compiled positions (post-Babel/esbuild) back to original TypeScript
794
+ * source positions by fetching and applying the inline source map.
795
+ *
796
+ * - Only runs for URL-form fileNames (Vite dev mode serves files as URLs)
797
+ * - For Turbopack chunk paths, constructs the dev-server URL to fetch source maps
798
+ * - Results are cached per file URL — each file is fetched at most once
799
+ * - Returns the original source unchanged on any error
800
+ *
801
+ * Designed to be called *after* the synchronous context is already rendered,
802
+ * so the UI updates the line number once the source map resolves (usually <50ms
803
+ * after the first hover on a given file, instant on subsequent hovers).
804
+ */
805
+ async function resolveSource(source) {
806
+ let fileUrl = null;
807
+ try {
808
+ fileUrl = new URL(source.fileName).href;
809
+ } catch {
810
+ const chunkUrl = tryConstructChunkUrl(source.fileName);
811
+ if (chunkUrl) fileUrl = chunkUrl;
812
+ else if (isChunkPath(source.fileName)) return null;
813
+ else return source;
814
+ }
815
+ const traceMap = await loadTraceMap(fileUrl);
816
+ if (!traceMap) {
817
+ if (isChunkPath(source.fileName) || isChunkPath(fileUrl)) return null;
818
+ return source;
819
+ }
820
+ const original = originalPositionFor(traceMap, {
821
+ line: source.lineNumber,
822
+ column: source.columnNumber - 1
823
+ });
824
+ if (original.source == null || original.line == null) return null;
825
+ return {
826
+ fileName: original.source,
827
+ lineNumber: original.line,
828
+ columnNumber: original.column != null ? original.column + 1 : 1
829
+ };
830
+ }
831
+ const FiberTags = {
832
+ FunctionComponent: 0,
833
+ ClassComponent: 1,
834
+ IndeterminateComponent: 2,
835
+ HostRoot: 3,
836
+ HostPortal: 4,
837
+ HostComponent: 5,
838
+ HostText: 6,
839
+ Fragment: 7,
840
+ Mode: 8,
841
+ ContextConsumer: 9,
842
+ ContextProvider: 10,
843
+ ForwardRef: 11,
844
+ Profiler: 12,
845
+ SuspenseComponent: 13,
846
+ MemoComponent: 14,
847
+ SimpleMemoComponent: 15,
848
+ LazyComponent: 16,
849
+ IncompleteClassComponent: 17,
850
+ DehydratedFragment: 18,
851
+ SuspenseListComponent: 19,
852
+ ScopeComponent: 21,
853
+ OffscreenComponent: 22,
854
+ LegacyHiddenComponent: 23,
855
+ CacheComponent: 24,
856
+ TracingMarkerComponent: 25
857
+ };
858
+ const COMPONENT_TAGS = [
859
+ FiberTags.FunctionComponent,
860
+ FiberTags.ClassComponent,
861
+ FiberTags.IndeterminateComponent,
862
+ FiberTags.SuspenseComponent,
863
+ FiberTags.ForwardRef,
864
+ FiberTags.MemoComponent,
865
+ FiberTags.SimpleMemoComponent,
866
+ FiberTags.LazyComponent
867
+ ];
868
+ /**
869
+ * Finds the React fiber attached to a DOM element.
870
+ * React attaches it as __reactFiber$<randomKey> in development mode.
871
+ */
872
+ function findFiber(element) {
873
+ const key = Object.keys(element).find((k) => k.startsWith("__reactFiber$"));
874
+ return key ? element[key] : null;
875
+ }
876
+ function getDisplayName(type) {
877
+ if (!type || typeof type === "string") return "Unknown";
878
+ return type.displayName ?? type.name ?? type.render?.name ?? "Anonymous";
879
+ }
880
+ function parseStackFrameLocation(frame) {
881
+ const parenMatch = frame.match(/\((.+):(\d+):(\d+)\)$/);
882
+ const bareMatch = frame.match(/^at\s+(.+):(\d+):(\d+)$/);
883
+ const match = parenMatch ?? bareMatch;
884
+ if (!match) return null;
885
+ return {
886
+ fileName: match[1],
887
+ lineNumber: parseInt(match[2], 10),
888
+ columnNumber: parseInt(match[3], 10)
889
+ };
890
+ }
891
+ function parseSourceLocation(value) {
892
+ const match = value?.match(/^(.+):(\d+):(\d+)$/);
893
+ if (!match) return null;
894
+ return {
895
+ fileName: match[1],
896
+ lineNumber: parseInt(match[2], 10),
897
+ columnNumber: parseInt(match[3], 10)
898
+ };
899
+ }
900
+ function getElementSource(element) {
901
+ return parseSourceLocation(element.getAttribute("data-locatorjs"));
902
+ }
903
+ /**
904
+ * Parses the component definition source location from a React fiber's _debugStack.
905
+ *
906
+ * In React 19, _debugStack is an Error object captured at JSX creation time.
907
+ * For a DOM fiber (e.g. <button> inside Button.tsx), the stack contains
908
+ * Button.tsx as the first non-React frame — which is the component's definition file.
909
+ *
910
+ * The fileName may be a full URL in Vite dev mode (e.g. "http://localhost:5173/src/Button.tsx").
911
+ * URL → absolute path normalization is deferred to the FileSystemService.
912
+ *
913
+ * When the stack only contains Turbopack chunk frames (common for RSC-rendered
914
+ * parent fibers), returns the first chunk frame so resolveSource can attempt
915
+ * source-map resolution on it.
916
+ */
917
+ function parseComponentSource(debugStack) {
918
+ if (!debugStack?.stack) return null;
919
+ let stack = debugStack.stack;
920
+ if (stack.startsWith("Error: react-stack-top-frame\n")) stack = stack.slice(stack.indexOf("\n") + 1);
921
+ else if (stack.startsWith("Error\n")) stack = stack.slice(6);
922
+ let firstChunkSource = null;
923
+ for (const line of stack.split("\n")) {
924
+ const trimmed = line.trim();
925
+ if (!trimmed || !trimmed.startsWith("at ")) continue;
926
+ if (trimmed.includes("node_modules") || trimmed.includes("react-jsx") || trimmed.includes("react-dom") || trimmed.includes("react-stack-bottom-frame") || trimmed.includes("(@fs")) continue;
927
+ const source = parseStackFrameLocation(trimmed);
928
+ if (!source) continue;
929
+ if (isChunkPath(source.fileName)) {
930
+ firstChunkSource ??= source;
931
+ continue;
932
+ }
933
+ return source;
934
+ }
935
+ return firstChunkSource;
936
+ }
937
+ /**
938
+ * Extracts source location from a fiber, supporting both React versions and
939
+ * bundler source strategies.
940
+ *
941
+ * Prefer _debugSource when available because it is the structured JSX source
942
+ * object. In Turbopack builds, _debugStack often points at generated chunk
943
+ * files, while _debugSource still carries the original [project]/... module
944
+ * path emitted by jsxDEV.
945
+ *
946
+ * React 18 paths are absolute filesystem paths (/abs/path/to/File.tsx) so
947
+ * resolveSource() will no-op on them — they're already at original positions.
948
+ */
949
+ function getSource(fiber) {
950
+ if (!fiber) return null;
951
+ if (fiber._debugSource) return fiber._debugSource;
952
+ if (fiber._debugStack) return parseComponentSource(fiber._debugStack);
953
+ return null;
954
+ }
955
+ /**
956
+ * Given a DOM element, returns the ComponentContext describing the nearest
957
+ * React component that rendered it — including display name, breadcrumb path,
958
+ * source location (definition file), and current props.
959
+ *
960
+ * Pass `point` (mouse coordinates) to enable text-node detection: when the
961
+ * cursor is over a bare text node, `caretPositionFromPoint` gives its direct
962
+ * parent element, which is more specific than the DOM event target that bubbles
963
+ * up to the nearest element ancestor.
964
+ *
965
+ * Note on text source accuracy: React's HostText fibers (tag=6) have
966
+ * _debugStack = null — source location is only stored on element fibers.
967
+ * So the location shown is always where the *containing element* was written
968
+ * (e.g. Card.tsx:11 for `<h3>{title}</h3>`), not where the text value was
969
+ * defined. Resolving the value's origin (prop, const, literal) requires
970
+ * AST-level analysis and is handled by the inline-editing plugin, not here.
971
+ *
972
+ * Returns null if:
973
+ * - React is not present / not in dev mode
974
+ * - The element is not part of a React tree
975
+ * - No component fiber is found in the ancestor chain
976
+ */
977
+ async function getComponentContext(element, root) {
978
+ const domFiber = findFiber(element);
979
+ if (!domFiber) return null;
980
+ let fiber = domFiber;
981
+ const parts = [{
982
+ source: getElementSource(element) ?? getSource(fiber),
983
+ names: []
984
+ }];
985
+ let i = 0;
986
+ while (fiber) {
987
+ if (fiber.tag === 5 && typeof fiber.type === "string") parts[i].names.push(fiber.type);
988
+ else if (COMPONENT_TAGS.includes(fiber.tag)) {
989
+ const name = getDisplayName(fiber.type);
990
+ if (name !== "Unknown" && name !== "Anonymous") parts[i].names.push(name);
991
+ parts[++i] = {
992
+ source: getSource(fiber),
993
+ names: [name]
994
+ };
995
+ }
996
+ fiber = fiber.return;
997
+ }
998
+ const sources = await Promise.all(parts.map((part) => part.source ? resolveSource(part.source) : null));
999
+ /** Enrich a raw source with computed relative and absolute paths. */
1000
+ function enrichSource(raw) {
1001
+ return {
1002
+ ...raw,
1003
+ relativePath: toRelativePath(raw.fileName, root),
1004
+ absolutePath: toAbsolutePath(raw.fileName, root) ?? raw.fileName
1005
+ };
1006
+ }
1007
+ const files = parts.map((part, idx) => {
1008
+ const resolved = sources[idx];
1009
+ return {
1010
+ source: resolved ? enrichSource(resolved) : null,
1011
+ names: part.names.reverse()
1012
+ };
1013
+ }).reduce((acc, part) => {
1014
+ if (!part.source) return acc;
1015
+ const file = part.source.fileName;
1016
+ if (!acc.some((p) => p.source?.fileName === file)) acc.push(part);
1017
+ return acc;
1018
+ }, []);
1019
+ const breadcrumb = parts[0].names;
1020
+ return {
1021
+ element,
1022
+ displayName: breadcrumb[0] ?? "Unknown",
1023
+ breadcrumb,
1024
+ all: files,
1025
+ props: domFiber.memoizedProps ?? {}
1026
+ };
1027
+ }
1028
+
1029
+ //#endregion
1030
+ //#region src/hooks/useEffectEvent.ts
1031
+ const useEffectEvent = react.useEffectEvent ?? function useEffectEventShim(fn) {
1032
+ const ref = (0, react.useRef)(fn);
1033
+ ref.current = fn;
1034
+ return (0, react.useCallback)(((...args) => ref.current(...args)), []);
1035
+ };
1036
+
1037
+ //#endregion
1038
+ //#region src/hooks/useInspectorBehavior.ts
1039
+ function isTraceUiTarget(target, portalContainer) {
1040
+ if (!(target instanceof Node)) return false;
1041
+ return portalContainer.contains(target);
1042
+ }
1043
+ function buildEditorUrl(editor, path, line, col) {
1044
+ if (editor === "webstorm") return `webstorm://open?file=${encodeURIComponent(path)}&line=${line}`;
1045
+ if (editor === "intellij") return `idea://open?file=${encodeURIComponent(path)}&line=${line}`;
1046
+ return `${editor}://file/${path}:${line}:${col}`;
1047
+ }
1048
+ function findFirstProjectSource(all, root) {
1049
+ for (const entry of all) if (entry.source && entry.source.absolutePath.startsWith(root) && !entry.source.relativePath.startsWith("node_modules/")) return entry.source;
1050
+ return null;
1051
+ }
1052
+ /**
1053
+ * LocatorJS-style inspector behavior:
1054
+ *
1055
+ * - Tracks hovered component context via mousemove + fiber tree walking
1056
+ * - Click to open the nearest project source file directly in the editor
1057
+ * - Escape to deactivate inspector
1058
+ */
1059
+ function useInspectorBehavior() {
1060
+ const portalContainer = (0, esm_exports.useAtomValue)(portalContainerAtom);
1061
+ const inspectorActive = (0, esm_exports.useAtomValue)(inspectorActiveAtom);
1062
+ const root = (0, esm_exports.useAtomValue)(projectRootAtom);
1063
+ const editor = (0, esm_exports.useAtomValue)(editorAtom);
1064
+ const setInspectorActive = (0, esm_exports.useSetAtom)(inspectorActiveAtom);
1065
+ const [hoveredContext, setHoveredContext] = (0, react.useState)(null);
1066
+ const hoveredContextRef = (0, react.useRef)(hoveredContext);
1067
+ hoveredContextRef.current = hoveredContext;
1068
+ const openSourceInEditor = useEffectEvent((ctx) => {
1069
+ const source = findFirstProjectSource(ctx.all, root);
1070
+ if (!source) return;
1071
+ const url = buildEditorUrl(editor, source.absolutePath, source.lineNumber, source.columnNumber);
1072
+ window.open(url);
1073
+ setInspectorActive(false);
1074
+ });
1075
+ const onEscapeKeyDown = useEffectEvent((e) => {
1076
+ if (e.key !== "Escape") return;
1077
+ setInspectorActive(false);
1078
+ setHoveredContext(null);
1079
+ });
1080
+ (0, react.useEffect)(() => {
1081
+ if (!inspectorActive) return;
1082
+ let lastHoveredElement = null;
1083
+ async function onMouseMove(e) {
1084
+ const target = e.target;
1085
+ if (!target || target === lastHoveredElement) return;
1086
+ if (isTraceUiTarget(target, portalContainer)) return;
1087
+ lastHoveredElement = target;
1088
+ try {
1089
+ const ctx = await getComponentContext(target, root);
1090
+ if (lastHoveredElement !== target) return;
1091
+ setHoveredContext(ctx);
1092
+ } catch {}
1093
+ }
1094
+ function onClick(e) {
1095
+ const target = e.target;
1096
+ if (isTraceUiTarget(target, portalContainer)) return;
1097
+ e.stopPropagation();
1098
+ e.preventDefault();
1099
+ const ctx = hoveredContextRef.current;
1100
+ if (ctx) openSourceInEditor(ctx);
1101
+ }
1102
+ document.addEventListener("mousemove", onMouseMove, { passive: true });
1103
+ document.addEventListener("click", onClick, true);
1104
+ document.addEventListener("keydown", onEscapeKeyDown);
1105
+ return () => {
1106
+ document.removeEventListener("mousemove", onMouseMove);
1107
+ document.removeEventListener("click", onClick, true);
1108
+ document.removeEventListener("keydown", onEscapeKeyDown);
1109
+ };
1110
+ }, [
1111
+ inspectorActive,
1112
+ portalContainer,
1113
+ root
1114
+ ]);
1115
+ (0, react.useEffect)(() => {
1116
+ if (!inspectorActive) setHoveredContext(null);
1117
+ }, [inspectorActive]);
1118
+ return { hoveredContext };
1119
+ }
1120
+
1121
+ //#endregion
1122
+ //#region src/hooks/useLongPressHotkey.ts
1123
+ const MODIFIER_INSPECT_DELAY_MS = 250;
1124
+ const LEGACY_SHORTCUT_DELAY_MS = 600;
1125
+ /**
1126
+ * Activates inspect mode when the user holds Option/Alt. Releasing before the
1127
+ * timer fires cancels activation; releasing after activation exits inspect mode.
1128
+ *
1129
+ * Also keeps the legacy Cmd+X (Mac) / Ctrl+X shortcut working.
1130
+ */
1131
+ function useLongPressHotkey() {
1132
+ const inspectorActive = (0, esm_exports.useAtomValue)(inspectorActiveAtom);
1133
+ const setInspectorActive = (0, esm_exports.useSetAtom)(inspectorActiveAtom);
1134
+ const [coreSettings, setCoreSettings] = (0, esm_exports.useAtom)(coreSettingsAtom);
1135
+ const settingsRef = (0, react.useRef)(coreSettings);
1136
+ const inspectorActiveRef = (0, react.useRef)(inspectorActive);
1137
+ settingsRef.current = coreSettings;
1138
+ inspectorActiveRef.current = inspectorActive;
1139
+ (0, react.useEffect)(() => {
1140
+ let modifierTimer = null;
1141
+ let legacyTimer = null;
1142
+ let modifierActivated = false;
1143
+ let modifierStartedWithInspector = false;
1144
+ function activateInspector() {
1145
+ setInspectorActive(true);
1146
+ if (settingsRef.current.minimized) setCoreSettings({
1147
+ ...settingsRef.current,
1148
+ minimized: false
1149
+ });
1150
+ }
1151
+ function clearModifierTimer() {
1152
+ if (modifierTimer !== null) {
1153
+ clearTimeout(modifierTimer);
1154
+ modifierTimer = null;
1155
+ }
1156
+ }
1157
+ function clearLegacyTimer() {
1158
+ if (legacyTimer !== null) {
1159
+ clearTimeout(legacyTimer);
1160
+ legacyTimer = null;
1161
+ }
1162
+ }
1163
+ function onKeyDown(e) {
1164
+ if (e.key === "Alt" && !e.repeat && modifierTimer === null && !modifierActivated) {
1165
+ modifierStartedWithInspector = inspectorActiveRef.current;
1166
+ modifierTimer = setTimeout(() => {
1167
+ modifierTimer = null;
1168
+ modifierActivated = !modifierStartedWithInspector;
1169
+ activateInspector();
1170
+ }, MODIFIER_INSPECT_DELAY_MS);
1171
+ }
1172
+ const modifierHeld = require_platform.IS_MAC ? e.metaKey : e.ctrlKey;
1173
+ if (e.key === "x" && modifierHeld && !e.repeat && legacyTimer === null) {
1174
+ e.preventDefault();
1175
+ legacyTimer = setTimeout(() => {
1176
+ legacyTimer = null;
1177
+ activateInspector();
1178
+ }, LEGACY_SHORTCUT_DELAY_MS);
1179
+ }
1180
+ }
1181
+ function onKeyUp(e) {
1182
+ if (e.key === "Alt") {
1183
+ clearModifierTimer();
1184
+ if (modifierActivated) {
1185
+ modifierActivated = false;
1186
+ setInspectorActive(false);
1187
+ }
1188
+ }
1189
+ if (e.key === "x" || e.key === "Meta" || e.key === "Control") clearLegacyTimer();
1190
+ }
1191
+ function onWindowBlur() {
1192
+ clearModifierTimer();
1193
+ clearLegacyTimer();
1194
+ if (modifierActivated) {
1195
+ modifierActivated = false;
1196
+ setInspectorActive(false);
1197
+ }
1198
+ }
1199
+ document.addEventListener("keydown", onKeyDown);
1200
+ document.addEventListener("keyup", onKeyUp);
1201
+ window.addEventListener("blur", onWindowBlur);
1202
+ return () => {
1203
+ document.removeEventListener("keydown", onKeyDown);
1204
+ document.removeEventListener("keyup", onKeyUp);
1205
+ window.removeEventListener("blur", onWindowBlur);
1206
+ clearModifierTimer();
1207
+ clearLegacyTimer();
1208
+ };
1209
+ }, []);
1210
+ }
1211
+
1212
+ //#endregion
1213
+ //#region src/components/Overlay.tsx
1214
+ function toRect(el) {
1215
+ const r = el.getBoundingClientRect();
1216
+ return {
1217
+ top: r.top,
1218
+ left: r.left,
1219
+ width: r.width,
1220
+ height: r.height
1221
+ };
1222
+ }
1223
+ function HighlightRect({ rect, label }) {
1224
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
1225
+ position: "fixed",
1226
+ top: rect.top,
1227
+ left: rect.left,
1228
+ width: rect.width,
1229
+ height: rect.height,
1230
+ background: "rgba(59,130,246,0.07)",
1231
+ border: "2px dashed rgba(59,130,246,0.7)",
1232
+ borderRadius: 2,
1233
+ pointerEvents: "none",
1234
+ boxSizing: "border-box"
1235
+ } }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1236
+ style: {
1237
+ position: "fixed",
1238
+ top: Math.max(0, rect.top - 24),
1239
+ left: rect.left,
1240
+ background: "rgba(59,130,246,0.85)",
1241
+ color: "#fff",
1242
+ fontSize: 11,
1243
+ fontFamily: "ui-monospace, monospace",
1244
+ fontWeight: 600,
1245
+ padding: "2px 6px",
1246
+ borderRadius: 4,
1247
+ whiteSpace: "nowrap",
1248
+ pointerEvents: "none",
1249
+ lineHeight: "18px"
1250
+ },
1251
+ children: label
1252
+ })] });
1253
+ }
1254
+ function Overlay({ hoveredContext }) {
1255
+ const [mouse, setMouse] = (0, react.useState)(null);
1256
+ (0, react.useEffect)(() => {
1257
+ function onMove(e) {
1258
+ setMouse({
1259
+ x: e.clientX,
1260
+ y: e.clientY
1261
+ });
1262
+ }
1263
+ document.addEventListener("mousemove", onMove, { passive: true });
1264
+ return () => {
1265
+ document.removeEventListener("mousemove", onMove);
1266
+ };
1267
+ }, []);
1268
+ const [hoveredRect, setHoveredRect] = (0, react.useState)(null);
1269
+ (0, react.useEffect)(() => {
1270
+ setHoveredRect(hoveredContext ? toRect(hoveredContext.element) : null);
1271
+ }, [hoveredContext]);
1272
+ (0, react.useEffect)(() => {
1273
+ document.body.style.cursor = "crosshair";
1274
+ return () => {
1275
+ document.body.style.cursor = "";
1276
+ };
1277
+ }, []);
1278
+ (0, react.useEffect)(() => {
1279
+ if (!hoveredContext) return;
1280
+ function update() {
1281
+ if (hoveredContext) setHoveredRect(toRect(hoveredContext.element));
1282
+ }
1283
+ window.addEventListener("scroll", update, {
1284
+ passive: true,
1285
+ capture: true
1286
+ });
1287
+ window.addEventListener("resize", update, { passive: true });
1288
+ return () => {
1289
+ window.removeEventListener("scroll", update, { capture: true });
1290
+ window.removeEventListener("resize", update);
1291
+ };
1292
+ }, [hoveredContext]);
1293
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1294
+ style: {
1295
+ position: "fixed",
1296
+ inset: 0,
1297
+ pointerEvents: "none",
1298
+ zIndex: 999998
1299
+ },
1300
+ children: [mouse && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
1301
+ position: "fixed",
1302
+ top: 0,
1303
+ left: mouse.x,
1304
+ width: 1,
1305
+ height: "100dvh",
1306
+ background: "rgba(59,130,246,0.5)",
1307
+ pointerEvents: "none"
1308
+ } }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: {
1309
+ position: "fixed",
1310
+ left: 0,
1311
+ top: mouse.y,
1312
+ height: 1,
1313
+ width: "100dvw",
1314
+ background: "rgba(59,130,246,0.5)",
1315
+ pointerEvents: "none"
1316
+ } })] }), hoveredRect && hoveredContext && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HighlightRect, {
1317
+ rect: hoveredRect,
1318
+ label: hoveredContext.breadcrumb.join(" › ")
1319
+ })]
1320
+ });
1321
+ }
1322
+
1323
+ //#endregion
1324
+ //#region src/components/ErrorBoundary.tsx
1325
+ var ErrorBoundary = class extends react.Component {
1326
+ constructor(props) {
1327
+ super(props);
1328
+ this.state = { hasError: false };
1329
+ }
1330
+ static getDerivedStateFromError(error) {
1331
+ return {
1332
+ hasError: true,
1333
+ error
1334
+ };
1335
+ }
1336
+ componentDidCatch(error, errorInfo) {
1337
+ console.error(error, errorInfo);
1338
+ }
1339
+ render() {
1340
+ if (this.state.hasError) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h1", { children: [
1341
+ "Something went wrong.",
1342
+ " ",
1343
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Button, {
1344
+ variant: "primary",
1345
+ onClick: () => this.setState({ hasError: false }),
1346
+ children: "Try Again"
1347
+ })
1348
+ ] });
1349
+ return this.props.children;
1350
+ }
1351
+ };
1352
+
1353
+ //#endregion
1354
+ //#region src/hooks.ts
1355
+ function useProjectRoot() {
1356
+ return (0, esm_exports.useAtomValue)(projectRootAtom);
1357
+ }
1358
+ function useInspectorActive() {
1359
+ return (0, esm_exports.useAtomValue)(inspectorActiveAtom);
1360
+ }
1361
+ function useDeactivateInspector() {
1362
+ const setInspectorActive = (0, esm_exports.useSetAtom)(inspectorActiveAtom);
1363
+ return (0, react.useCallback)(() => setInspectorActive(false), [setInspectorActive]);
1364
+ }
1365
+ function useSelectedContext() {
1366
+ return (0, esm_exports.useAtomValue)(selectedContextAtom);
1367
+ }
1368
+ function useClearSelectedContext() {
1369
+ const setSelectedContext = (0, esm_exports.useSetAtom)(selectedContextAtom);
1370
+ return (0, react.useCallback)(() => setSelectedContext(null), [setSelectedContext]);
1371
+ }
1372
+ function useSelectedSource() {
1373
+ return (0, esm_exports.useAtomValue)(selectedSourceAtom);
1374
+ }
1375
+ function useWidgetPortalContainer() {
1376
+ return (0, esm_exports.useAtomValue)(portalContainerAtom);
1377
+ }
1378
+
1379
+ //#endregion
1380
+ //#region src/components/SettingsMenu.tsx
1381
+ const SECTION_TITLE_STYLE = {
1382
+ fontSize: 11,
1383
+ fontWeight: 600,
1384
+ color: "#71717a",
1385
+ fontFamily: "system-ui, sans-serif",
1386
+ letterSpacing: "0.04em",
1387
+ textTransform: "uppercase"
1388
+ };
1389
+ const LABEL_STYLE = {
1390
+ fontSize: 12,
1391
+ color: "#d4d4d8",
1392
+ fontFamily: "system-ui, sans-serif"
1393
+ };
1394
+ const INPUT_STYLE = {
1395
+ width: "100%",
1396
+ minWidth: 0,
1397
+ background: "#0f0f11",
1398
+ border: "1px solid #3f3f46",
1399
+ borderRadius: 5,
1400
+ color: "#fafafa",
1401
+ fontSize: 12,
1402
+ fontFamily: "system-ui, sans-serif",
1403
+ padding: "6px 8px",
1404
+ boxSizing: "border-box"
1405
+ };
1406
+ const POSITION_OPTIONS = [
1407
+ {
1408
+ value: "bottom-right",
1409
+ label: "Bottom right"
1410
+ },
1411
+ {
1412
+ value: "bottom-left",
1413
+ label: "Bottom left"
1414
+ },
1415
+ {
1416
+ value: "top-right",
1417
+ label: "Top right"
1418
+ },
1419
+ {
1420
+ value: "top-left",
1421
+ label: "Top left"
1422
+ }
1423
+ ];
1424
+ function CoreSettingsSection() {
1425
+ const portalContainer = useWidgetPortalContainer();
1426
+ const [root, setRoot] = (0, esm_exports.useAtom)(projectRootAtom);
1427
+ const [coreSettings, setCoreSettings] = (0, esm_exports.useAtom)(coreSettingsAtom);
1428
+ const [draftRoot, setDraftRoot] = (0, react.useState)(root);
1429
+ const trimmedRoot = draftRoot.trim();
1430
+ const canApplyRoot = trimmedRoot.length > 0 && trimmedRoot !== root;
1431
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1432
+ style: {
1433
+ display: "flex",
1434
+ flexDirection: "column",
1435
+ gap: 12
1436
+ },
1437
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1438
+ style: {
1439
+ display: "flex",
1440
+ flexDirection: "column",
1441
+ gap: 6
1442
+ },
1443
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
1444
+ style: LABEL_STYLE,
1445
+ children: "Project root"
1446
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1447
+ style: {
1448
+ display: "flex",
1449
+ gap: 8
1450
+ },
1451
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1452
+ value: draftRoot,
1453
+ onChange: (e) => setDraftRoot(e.target.value),
1454
+ onKeyDown: (e) => {
1455
+ e.stopPropagation();
1456
+ if (e.key === "Enter" && canApplyRoot) {
1457
+ e.preventDefault();
1458
+ setRoot(trimmedRoot);
1459
+ }
1460
+ },
1461
+ style: INPUT_STYLE
1462
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Button, {
1463
+ variant: "secondary",
1464
+ disabled: !canApplyRoot,
1465
+ onClick: () => setRoot(trimmedRoot),
1466
+ children: "Apply"
1467
+ })]
1468
+ })]
1469
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1470
+ style: {
1471
+ display: "flex",
1472
+ flexDirection: "column",
1473
+ gap: 6
1474
+ },
1475
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
1476
+ style: LABEL_STYLE,
1477
+ children: "Toolbar position"
1478
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_spot_ui_components.Select.Root, {
1479
+ value: coreSettings.position,
1480
+ items: POSITION_OPTIONS,
1481
+ onValueChange: (value) => {
1482
+ if (value) setCoreSettings({
1483
+ ...coreSettings,
1484
+ position: value
1485
+ });
1486
+ },
1487
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Select.Trigger, {
1488
+ onClick: (e) => e.stopPropagation(),
1489
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Select.Value, {})
1490
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Select.Portal, {
1491
+ container: portalContainer,
1492
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Select.Positioner, {
1493
+ style: {
1494
+ zIndex: 1e8,
1495
+ pointerEvents: "auto"
1496
+ },
1497
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Select.Popup, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Select.List, { children: POSITION_OPTIONS.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Select.Item, {
1498
+ value: option.value,
1499
+ onClick: (e) => e.stopPropagation(),
1500
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Select.ItemText, { children: option.label })
1501
+ }, option.value)) }) })
1502
+ })
1503
+ })]
1504
+ })]
1505
+ })]
1506
+ });
1507
+ }
1508
+ function SettingsMenu({ plugins }) {
1509
+ const portalContainer = useWidgetPortalContainer();
1510
+ const deactivateInspector = useDeactivateInspector();
1511
+ const [isOpen, setIsOpen] = (0, react.useState)(false);
1512
+ const buttonRef = (0, react.useRef)(null);
1513
+ const settingsPlugins = plugins.filter((plugin) => Boolean(plugin.settings));
1514
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Tooltip, {
1515
+ label: "Settings",
1516
+ container: portalContainer,
1517
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.ToolbarButton, { ref: buttonRef }),
1518
+ "aria-label": "Settings",
1519
+ onClick: () => {
1520
+ deactivateInspector();
1521
+ setIsOpen((open) => !open);
1522
+ },
1523
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.SettingsIcon, {})
1524
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Popover.Root, {
1525
+ open: isOpen,
1526
+ onOpenChange: setIsOpen,
1527
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Popover.Portal, {
1528
+ container: portalContainer,
1529
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Popover.Positioner, {
1530
+ anchor: buttonRef.current,
1531
+ side: "top",
1532
+ align: "end",
1533
+ sideOffset: 8,
1534
+ collisionPadding: 8,
1535
+ positionMethod: "fixed",
1536
+ style: {
1537
+ zIndex: 99999999,
1538
+ pointerEvents: "auto"
1539
+ },
1540
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_spot_ui_components.Popover.Popup, {
1541
+ style: { width: 320 },
1542
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.PanelHeader, {
1543
+ title: "Settings",
1544
+ actionsRender: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.IconButton, {
1545
+ onClick: () => setIsOpen(false),
1546
+ title: "Close",
1547
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.XIcon, {})
1548
+ })
1549
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1550
+ style: {
1551
+ display: "flex",
1552
+ flexDirection: "column",
1553
+ gap: 12,
1554
+ padding: 12
1555
+ },
1556
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1557
+ style: {
1558
+ display: "flex",
1559
+ flexDirection: "column",
1560
+ gap: 10
1561
+ },
1562
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1563
+ style: SECTION_TITLE_STYLE,
1564
+ children: "Core"
1565
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CoreSettingsSection, {})]
1566
+ }), settingsPlugins.map((plugin) => {
1567
+ const SettingsContent = plugin.settings;
1568
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Separator, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1569
+ style: {
1570
+ display: "flex",
1571
+ flexDirection: "column",
1572
+ gap: 10
1573
+ },
1574
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1575
+ style: SECTION_TITLE_STYLE,
1576
+ children: plugin.name
1577
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsContent, {})]
1578
+ })] }, `settings:${plugin.name}`);
1579
+ })]
1580
+ })]
1581
+ })
1582
+ })
1583
+ })
1584
+ })] });
1585
+ }
1586
+
1587
+ //#endregion
1588
+ //#region src/components/Toolbar.tsx
1589
+ const DEFAULT_SPACING = 32;
1590
+ const POSITION_STYLES = {
1591
+ "bottom-right": {
1592
+ bottom: DEFAULT_SPACING,
1593
+ right: DEFAULT_SPACING
1594
+ },
1595
+ "bottom-left": {
1596
+ bottom: DEFAULT_SPACING,
1597
+ left: DEFAULT_SPACING
1598
+ },
1599
+ "top-right": {
1600
+ top: DEFAULT_SPACING,
1601
+ right: DEFAULT_SPACING
1602
+ },
1603
+ "top-left": {
1604
+ top: DEFAULT_SPACING,
1605
+ left: DEFAULT_SPACING
1606
+ }
1607
+ };
1608
+ const MINIMIZED_POSITION_STYLES = {
1609
+ "bottom-right": {
1610
+ bottom: DEFAULT_SPACING,
1611
+ right: 0
1612
+ },
1613
+ "bottom-left": {
1614
+ bottom: DEFAULT_SPACING,
1615
+ left: 0
1616
+ },
1617
+ "top-right": {
1618
+ top: DEFAULT_SPACING,
1619
+ right: 0
1620
+ },
1621
+ "top-left": {
1622
+ top: DEFAULT_SPACING,
1623
+ left: 0
1624
+ }
1625
+ };
1626
+ const TOGGLE_SHORTCUT = require_platform.IS_MAC ? "Hold Option" : "Hold Alt";
1627
+ const contentGridStyle = {
1628
+ display: "grid",
1629
+ transition: "grid-template-columns 0.3s ease"
1630
+ };
1631
+ const contentInnerStyle = {
1632
+ overflow: "hidden",
1633
+ minWidth: 0,
1634
+ display: "flex",
1635
+ alignItems: "center"
1636
+ };
1637
+ function Toolbar({ plugins }) {
1638
+ const [isInspectorActive, setInspectorActive] = (0, esm_exports.useAtom)(inspectorActiveAtom);
1639
+ const portalContainer = (0, esm_exports.useAtomValue)(portalContainerAtom);
1640
+ const [coreSettings, setCoreSettings] = (0, esm_exports.useAtom)(coreSettingsAtom);
1641
+ const minimized = coreSettings.minimized ?? false;
1642
+ const isRightAligned = coreSettings.position.includes("right");
1643
+ const toggleMinimized = () => {
1644
+ setCoreSettings({
1645
+ ...coreSettings,
1646
+ minimized: !minimized
1647
+ });
1648
+ };
1649
+ const CollapseArrow = isRightAligned ? _react_spot_ui_components.ChevronRightIcon : _react_spot_ui_components.ChevronLeftIcon;
1650
+ const ExpandArrow = isRightAligned ? _react_spot_ui_components.ChevronLeftIcon : _react_spot_ui_components.ChevronRightIcon;
1651
+ const toggleButton = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Tooltip, {
1652
+ label: minimized ? "Expand toolbar" : "Collapse toolbar",
1653
+ container: portalContainer,
1654
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.ToolbarButton, { style: {
1655
+ width: 16,
1656
+ color: "#71717a"
1657
+ } }),
1658
+ "aria-label": minimized ? "Expand toolbar" : "Collapse toolbar",
1659
+ onClick: toggleMinimized,
1660
+ children: minimized ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExpandArrow, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CollapseArrow, {})
1661
+ });
1662
+ const toolbarContent = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1663
+ style: {
1664
+ ...contentGridStyle,
1665
+ gridTemplateColumns: minimized ? "0fr" : "1fr"
1666
+ },
1667
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1668
+ style: contentInnerStyle,
1669
+ children: [
1670
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Tooltip, {
1671
+ label: "Inspector",
1672
+ shortcut: isInspectorActive ? "Esc to exit" : TOGGLE_SHORTCUT,
1673
+ container: portalContainer,
1674
+ render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.ToolbarButton, {}),
1675
+ "aria-label": "Inspector",
1676
+ onClick: () => setInspectorActive((prev) => !prev),
1677
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Logo, {})
1678
+ }),
1679
+ plugins.filter((plugin) => plugin.toolbar).map((plugin) => {
1680
+ const ToolbarContent = plugin.toolbar;
1681
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBoundary, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarContent, {}) }, plugin.name);
1682
+ }),
1683
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsMenu, { plugins })
1684
+ ]
1685
+ })
1686
+ });
1687
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_spot_ui_components.Tooltip.Provider, {
1688
+ delay: 300,
1689
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1690
+ style: {
1691
+ position: "fixed",
1692
+ ...minimized ? MINIMIZED_POSITION_STYLES[coreSettings.position] : POSITION_STYLES[coreSettings.position],
1693
+ display: "flex",
1694
+ alignItems: "center",
1695
+ overflow: "hidden",
1696
+ background: "#18181b",
1697
+ borderRadius: minimized ? isRightAligned ? "10px 0 0 10px" : "0 10px 10px 0" : 10,
1698
+ boxShadow: "0 4px 16px rgba(0,0,0,0.5)",
1699
+ outline: isInspectorActive ? "2px solid #3b82f6" : "2px solid transparent",
1700
+ transition: "right 0.3s ease, left 0.3s ease, border-radius 0.3s ease",
1701
+ userSelect: "none",
1702
+ height: 32,
1703
+ boxSizing: "border-box",
1704
+ zIndex: 999999
1705
+ },
1706
+ children: [
1707
+ !isRightAligned && toggleButton,
1708
+ toolbarContent,
1709
+ isRightAligned && toggleButton
1710
+ ]
1711
+ })
1712
+ });
1713
+ }
1714
+
1715
+ //#endregion
1716
+ //#region src/components/Trace.tsx
1717
+ function Trace({ root, editor = "vscode", plugins = [], position = "bottom-right", minimized = false }) {
1718
+ const [jotaiStore] = (0, react.useState)(() => {
1719
+ const store = createWidgetStore();
1720
+ store.set(projectRootAtom, root);
1721
+ store.set(editorAtom, editor);
1722
+ if (!store.get(coreSettingsAtom)) store.set(coreSettingsAtom, {
1723
+ position,
1724
+ minimized
1725
+ });
1726
+ return store;
1727
+ });
1728
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(esm_exports.Provider, {
1729
+ store: jotaiStore,
1730
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TraceRoot, { plugins })
1731
+ });
1732
+ }
1733
+ function TraceRoot({ plugins }) {
1734
+ const portalContainer = (0, esm_exports.useAtomValue)(portalContainerAtom);
1735
+ const inspectorActive = (0, esm_exports.useAtomValue)(inspectorActiveAtom);
1736
+ const { hoveredContext } = useInspectorBehavior();
1737
+ useLongPressHotkey();
1738
+ return (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1739
+ style: { pointerEvents: "auto" },
1740
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toolbar, { plugins })
1741
+ }), inspectorActive && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Overlay, { hoveredContext })] }), portalContainer);
1742
+ }
1743
+
1744
+ //#endregion
1745
+ exports.IS_MAC = require_platform.IS_MAC;
1746
+ exports.MOD_KEY = require_platform.MOD_KEY;
1747
+ exports.Trace = Trace;
1748
+ exports.settingsPluginAtom = settingsPluginAtom;
1749
+ exports.useClearSelectedContext = useClearSelectedContext;
1750
+ exports.useDeactivateInspector = useDeactivateInspector;
1751
+ exports.useInspectorActive = useInspectorActive;
1752
+ exports.useProjectRoot = useProjectRoot;
1753
+ exports.useSelectedContext = useSelectedContext;
1754
+ exports.useSelectedSource = useSelectedSource;
1755
+ exports.useWidgetPortalContainer = useWidgetPortalContainer;
1756
+ //# sourceMappingURL=index.cjs.map