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