@aylith/inspekt-core 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/actions/built-in.d.ts +17 -0
  2. package/dist/actions/built-in.d.ts.map +1 -0
  3. package/dist/adapters/generic.d.ts +3 -0
  4. package/dist/adapters/generic.d.ts.map +1 -0
  5. package/dist/adapters/index.d.ts +5 -0
  6. package/dist/adapters/index.d.ts.map +1 -0
  7. package/dist/adapters/react.d.ts +3 -0
  8. package/dist/adapters/react.d.ts.map +1 -0
  9. package/dist/adapters/solid.d.ts +3 -0
  10. package/dist/adapters/solid.d.ts.map +1 -0
  11. package/dist/adapters/svelte.d.ts +3 -0
  12. package/dist/adapters/svelte.d.ts.map +1 -0
  13. package/dist/adapters/types.d.ts +19 -0
  14. package/dist/adapters/types.d.ts.map +1 -0
  15. package/dist/adapters/vue.d.ts +3 -0
  16. package/dist/adapters/vue.d.ts.map +1 -0
  17. package/dist/capability-probe-AKVKYSDE.js +107 -0
  18. package/dist/capability-probe-AKVKYSDE.js.map +1 -0
  19. package/dist/components/rich-select.d.ts +62 -0
  20. package/dist/components/rich-select.d.ts.map +1 -0
  21. package/dist/components/tooltip.d.ts +34 -0
  22. package/dist/components/tooltip.d.ts.map +1 -0
  23. package/dist/detection/capability-probe.d.ts +31 -0
  24. package/dist/detection/capability-probe.d.ts.map +1 -0
  25. package/dist/detection/fiber-detector.d.ts +12 -0
  26. package/dist/detection/fiber-detector.d.ts.map +1 -0
  27. package/dist/detection/source-detector.d.ts +30 -0
  28. package/dist/detection/source-detector.d.ts.map +1 -0
  29. package/dist/detection/vue-detector.d.ts +14 -0
  30. package/dist/detection/vue-detector.d.ts.map +1 -0
  31. package/dist/fiber-detector-CFB7M7OY.js +50 -0
  32. package/dist/fiber-detector-CFB7M7OY.js.map +1 -0
  33. package/dist/highlight/bounding-boxes.d.ts +20 -0
  34. package/dist/highlight/bounding-boxes.d.ts.map +1 -0
  35. package/dist/highlight/highlighter.d.ts +11 -0
  36. package/dist/highlight/highlighter.d.ts.map +1 -0
  37. package/dist/highlight/prism.d.ts +23 -0
  38. package/dist/highlight/prism.d.ts.map +1 -0
  39. package/dist/index.cjs +3560 -0
  40. package/dist/index.cjs.map +1 -0
  41. package/dist/index.d.ts +12 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +3222 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/overlay/overlay.d.ts +30 -0
  46. package/dist/overlay/overlay.d.ts.map +1 -0
  47. package/dist/popover/popover.d.ts +54 -0
  48. package/dist/popover/popover.d.ts.map +1 -0
  49. package/dist/runtime/context.d.ts +3 -0
  50. package/dist/runtime/context.d.ts.map +1 -0
  51. package/dist/runtime/freeze.d.ts +2 -0
  52. package/dist/runtime/freeze.d.ts.map +1 -0
  53. package/dist/runtime/index.d.ts +4 -0
  54. package/dist/runtime/index.d.ts.map +1 -0
  55. package/dist/runtime/open-file.d.ts +2 -0
  56. package/dist/runtime/open-file.d.ts.map +1 -0
  57. package/dist/snippet/cache.d.ts +13 -0
  58. package/dist/snippet/cache.d.ts.map +1 -0
  59. package/dist/snippet/snippet-resolver.d.ts +21 -0
  60. package/dist/snippet/snippet-resolver.d.ts.map +1 -0
  61. package/dist/snippet/sourcemap-resolver.d.ts +9 -0
  62. package/dist/snippet/sourcemap-resolver.d.ts.map +1 -0
  63. package/dist/sourcemap-resolver-3PQ6R6PU.js +182 -0
  64. package/dist/sourcemap-resolver-3PQ6R6PU.js.map +1 -0
  65. package/dist/styles.d.ts +2 -0
  66. package/dist/styles.d.ts.map +1 -0
  67. package/dist/tree-panel/tree-panel.d.ts +33 -0
  68. package/dist/tree-panel/tree-panel.d.ts.map +1 -0
  69. package/dist/types.d.ts +148 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/package.json +51 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,3560 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/snippet/cache.ts
34
+ function getParsedMap(key) {
35
+ const v = parsed.get(key);
36
+ if (v !== void 0) {
37
+ parsed.delete(key);
38
+ parsed.set(key, v);
39
+ }
40
+ return v;
41
+ }
42
+ function setParsedMap(key, value) {
43
+ if (parsed.has(key)) parsed.delete(key);
44
+ parsed.set(key, value);
45
+ if (parsed.size > PARSED_LIMIT) {
46
+ const oldest = parsed.keys().next().value;
47
+ if (oldest !== void 0) parsed.delete(oldest);
48
+ }
49
+ }
50
+ function hasChromeSession() {
51
+ try {
52
+ return typeof chrome !== "undefined" && typeof chrome.storage !== "undefined" && typeof chrome.storage.session !== "undefined";
53
+ } catch {
54
+ return false;
55
+ }
56
+ }
57
+ function getRawMapBackend() {
58
+ if (!backend) {
59
+ backend = hasChromeSession() ? new ChromeSessionBackend() : new MemoryBackend();
60
+ }
61
+ return backend;
62
+ }
63
+ var PARSED_LIMIT, RAW_QUOTA_BYTES, parsed, MemoryBackend, PREFIX_HIT, PREFIX_MISS, ChromeSessionBackend, backend;
64
+ var init_cache = __esm({
65
+ "src/snippet/cache.ts"() {
66
+ "use strict";
67
+ PARSED_LIMIT = 5;
68
+ RAW_QUOTA_BYTES = 9 * 1024 * 1024;
69
+ parsed = /* @__PURE__ */ new Map();
70
+ MemoryBackend = class {
71
+ store = /* @__PURE__ */ new Map();
72
+ async get(key) {
73
+ return this.store.get(key);
74
+ }
75
+ async set(key, value) {
76
+ this.store.set(key, value);
77
+ }
78
+ async setNotFound(key) {
79
+ this.store.set(key, "not-found");
80
+ }
81
+ async clear() {
82
+ this.store.clear();
83
+ }
84
+ };
85
+ PREFIX_HIT = "inspekt-mapcache:";
86
+ PREFIX_MISS = "inspekt-notfound:";
87
+ ChromeSessionBackend = class {
88
+ async get(key) {
89
+ const hitKey = PREFIX_HIT + key;
90
+ const missKey = PREFIX_MISS + key;
91
+ const res = await chrome.storage.session.get([hitKey, missKey]);
92
+ if (res[missKey]) return "not-found";
93
+ const entry = res[hitKey];
94
+ if (typeof entry === "string") return entry;
95
+ return void 0;
96
+ }
97
+ async set(key, value) {
98
+ await this.evictIfNeeded(value.length);
99
+ await chrome.storage.session.set({ [PREFIX_HIT + key]: value });
100
+ }
101
+ async setNotFound(key) {
102
+ await chrome.storage.session.set({ [PREFIX_MISS + key]: true });
103
+ }
104
+ async clear() {
105
+ const all = await chrome.storage.session.get(null);
106
+ const inspektKeys = Object.keys(all).filter(
107
+ (k) => k.startsWith(PREFIX_HIT) || k.startsWith(PREFIX_MISS)
108
+ );
109
+ if (inspektKeys.length > 0) await chrome.storage.session.remove(inspektKeys);
110
+ }
111
+ async evictIfNeeded(incomingBytes) {
112
+ const used = await chrome.storage.session.getBytesInUse(null);
113
+ if (used + incomingBytes < RAW_QUOTA_BYTES) return;
114
+ const all = await chrome.storage.session.get(null);
115
+ const hits = Object.keys(all).filter((k) => k.startsWith(PREFIX_HIT));
116
+ if (hits.length > 0) await chrome.storage.session.remove(hits);
117
+ }
118
+ };
119
+ backend = null;
120
+ }
121
+ });
122
+
123
+ // src/snippet/sourcemap-resolver.ts
124
+ var sourcemap_resolver_exports = {};
125
+ __export(sourcemap_resolver_exports, {
126
+ resolveFromSourceMap: () => resolveFromSourceMap
127
+ });
128
+ function candidateMapUrls() {
129
+ if (typeof document === "undefined") return [];
130
+ const urls = [];
131
+ for (const script of Array.from(document.querySelectorAll("script[src]"))) {
132
+ const src = script.src;
133
+ if (!src) continue;
134
+ if (src.endsWith(".map")) {
135
+ urls.push(src);
136
+ continue;
137
+ }
138
+ if (src.includes(".js")) {
139
+ urls.push(`${src}.map`);
140
+ }
141
+ }
142
+ return urls;
143
+ }
144
+ async function fetchRawMap(mapUrl, fetcher) {
145
+ const cache2 = getRawMapBackend();
146
+ const cached = await cache2.get(mapUrl);
147
+ if (cached === "not-found") return null;
148
+ if (typeof cached === "string") return cached;
149
+ try {
150
+ const res = await fetcher(mapUrl);
151
+ if (!res.ok) {
152
+ await cache2.setNotFound(mapUrl);
153
+ return null;
154
+ }
155
+ const text = await res.text();
156
+ await cache2.set(mapUrl, text);
157
+ return text;
158
+ } catch {
159
+ await cache2.setNotFound(mapUrl);
160
+ return null;
161
+ }
162
+ }
163
+ function getOrParse(mapUrl, raw) {
164
+ const cached = getParsedMap(mapUrl);
165
+ if (cached) return cached;
166
+ const parsed2 = new import_trace_mapping.TraceMap(raw);
167
+ setParsedMap(mapUrl, parsed2);
168
+ return parsed2;
169
+ }
170
+ function matchSourceIndex(sources, target) {
171
+ const normalize = (p) => p.replace(/\\/g, "/").replace(/^\.\//, "");
172
+ const normalizedTarget = normalize(target);
173
+ const tail = normalizedTarget.split("/").slice(-3).join("/");
174
+ for (let i = 0; i < sources.length; i++) {
175
+ const s = sources[i];
176
+ if (!s) continue;
177
+ const ns = normalize(s);
178
+ if (ns === normalizedTarget) return i;
179
+ if (ns.endsWith(tail)) return i;
180
+ }
181
+ return -1;
182
+ }
183
+ async function resolveFromSourceMap(opts) {
184
+ const fetcher = opts.fetcher ?? fetch;
185
+ const context = opts.context ?? 5;
186
+ for (const mapUrl of candidateMapUrls()) {
187
+ const raw = await fetchRawMap(mapUrl, fetcher);
188
+ if (!raw) continue;
189
+ let map;
190
+ try {
191
+ map = getOrParse(mapUrl, raw);
192
+ } catch {
193
+ continue;
194
+ }
195
+ const sources = map.sources ?? [];
196
+ const idx = matchSourceIndex(sources, opts.filePath);
197
+ if (idx === -1) continue;
198
+ const sourcesContent = map.sourcesContent ?? [];
199
+ const content = sourcesContent[idx];
200
+ if (!content) continue;
201
+ const allLines = content.split("\n");
202
+ if (opts.line < 1 || opts.line > allLines.length) continue;
203
+ const startLine = Math.max(1, opts.line - context);
204
+ const endLine = Math.min(allLines.length, opts.line + context);
205
+ const lines = allLines.slice(startLine - 1, endLine);
206
+ const ext = (opts.filePath.split(".").pop() ?? "").toLowerCase();
207
+ return {
208
+ startLine,
209
+ endLine,
210
+ targetLine: opts.line,
211
+ lines,
212
+ language: ext || "text",
213
+ source: "sourcemap"
214
+ };
215
+ }
216
+ return null;
217
+ }
218
+ var import_trace_mapping;
219
+ var init_sourcemap_resolver = __esm({
220
+ "src/snippet/sourcemap-resolver.ts"() {
221
+ "use strict";
222
+ import_trace_mapping = require("@jridgewell/trace-mapping");
223
+ init_cache();
224
+ }
225
+ });
226
+
227
+ // src/detection/capability-probe.ts
228
+ var capability_probe_exports = {};
229
+ __export(capability_probe_exports, {
230
+ INSPEKT_CAPABILITIES_MESSAGE: () => INSPEKT_CAPABILITIES_MESSAGE,
231
+ probeCapabilities: () => probeCapabilities,
232
+ publishCapabilities: () => publishCapabilities,
233
+ watchCapabilities: () => watchCapabilities
234
+ });
235
+ async function pingHead(url, fetcher) {
236
+ const controller = new AbortController();
237
+ const timer = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
238
+ try {
239
+ const res = await fetcher(url, { method: "HEAD", signal: controller.signal });
240
+ return res.ok;
241
+ } catch {
242
+ return false;
243
+ } finally {
244
+ clearTimeout(timer);
245
+ }
246
+ }
247
+ function hasInstrumentation() {
248
+ if (document.querySelector("[data-insp-path]")) return true;
249
+ return false;
250
+ }
251
+ function detectSourceMapHint() {
252
+ const scripts = document.querySelectorAll("script[src]");
253
+ for (const s of Array.from(scripts).slice(0, 5)) {
254
+ if (s.src.endsWith(".map")) return true;
255
+ }
256
+ return false;
257
+ }
258
+ async function probeCapabilities(ctx = {}) {
259
+ const fetcher = ctx.fetcher ?? fetch;
260
+ const instrumented = hasInstrumentation();
261
+ const sourceMapAvailable = detectSourceMapHint();
262
+ const serverReachable = ctx.serverUrl ? await pingHead(new URL("/__inspekt/capabilities", ctx.serverUrl).toString(), fetcher) : false;
263
+ const agentConnected = ctx.daemonUrl ? await pingHead(new URL("/__inspekt/daemon", ctx.daemonUrl).toString(), fetcher) : false;
264
+ const snippetSource = serverReachable ? "devserver" : ctx.sourceMapEnabled && sourceMapAvailable ? "sourcemap" : null;
265
+ return {
266
+ instrumented,
267
+ snippetSource,
268
+ serverReachable,
269
+ sourceMapAvailable,
270
+ agentConnected
271
+ };
272
+ }
273
+ function publishCapabilities(caps, ctx = {}) {
274
+ if (ctx.onResult) {
275
+ ctx.onResult(caps);
276
+ return;
277
+ }
278
+ window.postMessage(
279
+ { source: "inspekt", type: INSPEKT_CAPABILITIES_MESSAGE, payload: caps },
280
+ "*"
281
+ );
282
+ }
283
+ function watchCapabilities(ctx = {}) {
284
+ let pending = false;
285
+ let lastPub = 0;
286
+ const fire = async () => {
287
+ if (pending) return;
288
+ pending = true;
289
+ try {
290
+ const caps = await probeCapabilities(ctx);
291
+ publishCapabilities(caps, ctx);
292
+ lastPub = Date.now();
293
+ } finally {
294
+ pending = false;
295
+ }
296
+ };
297
+ const schedule = () => {
298
+ const elapsed = Date.now() - lastPub;
299
+ if (elapsed >= PROBE_THROTTLE_MS) {
300
+ void fire();
301
+ } else {
302
+ setTimeout(fire, PROBE_THROTTLE_MS - elapsed);
303
+ }
304
+ };
305
+ void fire();
306
+ const observer = new MutationObserver((muts) => {
307
+ for (const m of muts) {
308
+ if (m.type === "attributes" && m.attributeName === "data-insp-path") {
309
+ schedule();
310
+ return;
311
+ }
312
+ if (m.type === "childList" && (m.addedNodes.length || m.removedNodes.length)) {
313
+ const added = Array.from(m.addedNodes).some(
314
+ (n) => n instanceof Element && n.querySelector?.("[data-insp-path]")
315
+ );
316
+ if (added) {
317
+ schedule();
318
+ return;
319
+ }
320
+ }
321
+ }
322
+ });
323
+ observer.observe(document.documentElement, {
324
+ subtree: true,
325
+ childList: true,
326
+ attributes: true,
327
+ attributeFilter: ["data-insp-path"]
328
+ });
329
+ return () => observer.disconnect();
330
+ }
331
+ var PROBE_THROTTLE_MS, PROBE_TIMEOUT_MS, INSPEKT_CAPABILITIES_MESSAGE;
332
+ var init_capability_probe = __esm({
333
+ "src/detection/capability-probe.ts"() {
334
+ "use strict";
335
+ PROBE_THROTTLE_MS = 500;
336
+ PROBE_TIMEOUT_MS = 1e3;
337
+ INSPEKT_CAPABILITIES_MESSAGE = "inspekt:capabilities";
338
+ }
339
+ });
340
+
341
+ // src/index.ts
342
+ var index_exports = {};
343
+ __export(index_exports, {
344
+ attachTooltip: () => attachTooltip,
345
+ createInspekt: () => createInspekt,
346
+ createRichSelect: () => createRichSelect,
347
+ tokenizeToLines: () => tokenizeToLines
348
+ });
349
+ module.exports = __toCommonJS(index_exports);
350
+
351
+ // src/detection/vue-detector.ts
352
+ function fromVue3(el) {
353
+ let cmp = el.__vueParentComponent;
354
+ while (cmp) {
355
+ const file = cmp.type?.__file;
356
+ if (file) {
357
+ const name = cmp.type?.__name ?? cmp.type?.name ?? fileNameOf(file);
358
+ return {
359
+ filePath: file,
360
+ line: 1,
361
+ column: 1,
362
+ componentName: name,
363
+ rawPath: `${file}:1:1:${name}`
364
+ };
365
+ }
366
+ cmp = cmp.parent ?? null;
367
+ }
368
+ return null;
369
+ }
370
+ function fromVue2(el) {
371
+ let vm = el.__vue__;
372
+ while (vm) {
373
+ const file = vm.$options?.__file;
374
+ if (file) {
375
+ const name = vm.$options?.name ?? vm.$options?._componentTag ?? fileNameOf(file);
376
+ return {
377
+ filePath: file,
378
+ line: 1,
379
+ column: 1,
380
+ componentName: name,
381
+ rawPath: `${file}:1:1:${name}`
382
+ };
383
+ }
384
+ vm = vm.$parent ?? null;
385
+ }
386
+ return null;
387
+ }
388
+ function fileNameOf(path) {
389
+ return path.split("/").pop()?.replace(/\.\w+$/, "") ?? "Unknown";
390
+ }
391
+ function vueSourceFor(element) {
392
+ const el = element;
393
+ return fromVue3(el) ?? fromVue2(el);
394
+ }
395
+
396
+ // src/detection/source-detector.ts
397
+ var SOURCE_ATTRIBUTE = "data-insp-path";
398
+ function parseSourceAttribute(value) {
399
+ const parts = value.split(":");
400
+ if (parts.length < 2) return null;
401
+ let filePath;
402
+ let rest;
403
+ if (/^[A-Z]$/i.test(parts[0] ?? "") && parts[1]?.startsWith("\\")) {
404
+ filePath = `${parts[0]}:${parts[1]}`;
405
+ rest = parts.slice(2);
406
+ } else {
407
+ filePath = parts[0];
408
+ rest = parts.slice(1);
409
+ }
410
+ const line = parseInt(rest[0] ?? "", 10);
411
+ const column = parseInt(rest[1] ?? "", 10);
412
+ const componentName = rest[2] ?? filePath.split("/").pop()?.replace(/\.\w+$/, "") ?? "Unknown";
413
+ if (isNaN(line)) return null;
414
+ return {
415
+ filePath,
416
+ line,
417
+ column: isNaN(column) ? 1 : column,
418
+ componentName,
419
+ rawPath: value
420
+ };
421
+ }
422
+ function findSourceAttribute(element) {
423
+ return element.getAttribute(SOURCE_ATTRIBUTE);
424
+ }
425
+ function findClosestSource(element) {
426
+ let current = element;
427
+ while (current) {
428
+ const attrValue = findSourceAttribute(current);
429
+ if (attrValue) {
430
+ const source = parseSourceAttribute(attrValue);
431
+ if (source) return { element: current, source };
432
+ }
433
+ current = current.parentElement;
434
+ }
435
+ const vueSource = vueSourceFor(element);
436
+ if (vueSource) return { element, source: vueSource };
437
+ return null;
438
+ }
439
+ function elementToInspected(domElement, source, pathMapping = {}, detectionSource = "attribute") {
440
+ let resolvedPath = source.filePath;
441
+ for (const [containerPath, hostPath] of Object.entries(pathMapping)) {
442
+ if (resolvedPath.startsWith(containerPath)) {
443
+ resolvedPath = resolvedPath.replace(containerPath, hostPath);
444
+ break;
445
+ }
446
+ }
447
+ return {
448
+ filePath: resolvedPath,
449
+ line: source.line,
450
+ column: source.column,
451
+ componentName: source.componentName,
452
+ rawPath: source.rawPath,
453
+ domElement,
454
+ tagName: domElement.tagName.toLowerCase(),
455
+ classList: Array.from(domElement.classList),
456
+ id: domElement.id || null,
457
+ boundingRect: domElement.getBoundingClientRect(),
458
+ ancestors: [],
459
+ children: [],
460
+ props: null,
461
+ framework: detectionSource === "fiber" ? "react" : "unknown",
462
+ detectionSource
463
+ };
464
+ }
465
+
466
+ // src/highlight/highlighter.ts
467
+ var HIGHLIGHT_ATTR = "data-inspekt-highlight";
468
+ var Highlighter = class {
469
+ highlights = /* @__PURE__ */ new Map();
470
+ config;
471
+ constructor(config) {
472
+ this.config = config;
473
+ }
474
+ highlight(element, type = "selected") {
475
+ if (this.highlights.has(element)) {
476
+ this.unhighlight(element);
477
+ }
478
+ const state = {
479
+ element,
480
+ originalOutline: element.style.outline,
481
+ originalOutlineOffset: element.style.outlineOffset,
482
+ originalBoxShadow: element.style.boxShadow,
483
+ type
484
+ };
485
+ this.highlights.set(element, state);
486
+ element.setAttribute(HIGHLIGHT_ATTR, type);
487
+ switch (type) {
488
+ case "selected":
489
+ element.style.outline = `2px solid ${this.config.color}`;
490
+ element.style.outlineOffset = "2px";
491
+ if (this.config.style === "glow") {
492
+ element.style.boxShadow = `0 0 8px 2px ${this.config.color}40`;
493
+ }
494
+ break;
495
+ case "hovered":
496
+ element.style.outline = "2px dashed #a855f7";
497
+ element.style.outlineOffset = "2px";
498
+ break;
499
+ case "border":
500
+ element.style.outline = `1px solid ${this.config.color}60`;
501
+ element.style.outlineOffset = "0px";
502
+ break;
503
+ }
504
+ }
505
+ unhighlight(element) {
506
+ const state = this.highlights.get(element);
507
+ if (!state) return;
508
+ element.style.outline = state.originalOutline;
509
+ element.style.outlineOffset = state.originalOutlineOffset;
510
+ element.style.boxShadow = state.originalBoxShadow;
511
+ element.removeAttribute(HIGHLIGHT_ATTR);
512
+ this.highlights.delete(element);
513
+ }
514
+ unhighlightAll() {
515
+ for (const [element] of this.highlights) {
516
+ this.unhighlight(element);
517
+ }
518
+ }
519
+ updateConfig(config) {
520
+ this.config = config;
521
+ }
522
+ };
523
+
524
+ // src/highlight/bounding-boxes.ts
525
+ var BoundingBoxOverlay = class {
526
+ constructor(shadowRoot) {
527
+ this.shadowRoot = shadowRoot;
528
+ this.container = document.createElement("div");
529
+ this.container.className = "inspekt-bbox-layer";
530
+ this.shadowRoot.appendChild(this.container);
531
+ }
532
+ shadowRoot;
533
+ container;
534
+ boxes = [];
535
+ mounted = false;
536
+ rafHandle = null;
537
+ resizeObserver = null;
538
+ mutationObserver = null;
539
+ enable() {
540
+ if (this.mounted) return;
541
+ this.mounted = true;
542
+ this.scan();
543
+ window.addEventListener("scroll", this.scheduleRepaint, true);
544
+ window.addEventListener("resize", this.scheduleRepaint);
545
+ this.resizeObserver = new ResizeObserver(this.scheduleRepaint);
546
+ for (const { el } of this.boxes) this.resizeObserver.observe(el);
547
+ this.mutationObserver = new MutationObserver(this.scheduleRescan);
548
+ this.mutationObserver.observe(document.body, {
549
+ childList: true,
550
+ subtree: true,
551
+ attributes: true,
552
+ attributeFilter: ["data-insp-path"]
553
+ });
554
+ }
555
+ disable() {
556
+ if (!this.mounted) return;
557
+ this.mounted = false;
558
+ window.removeEventListener("scroll", this.scheduleRepaint, true);
559
+ window.removeEventListener("resize", this.scheduleRepaint);
560
+ this.resizeObserver?.disconnect();
561
+ this.mutationObserver?.disconnect();
562
+ this.resizeObserver = null;
563
+ this.mutationObserver = null;
564
+ if (this.rafHandle !== null) cancelAnimationFrame(this.rafHandle);
565
+ this.rafHandle = null;
566
+ this.clear();
567
+ }
568
+ destroy() {
569
+ this.disable();
570
+ this.container.remove();
571
+ }
572
+ clear() {
573
+ this.container.innerHTML = "";
574
+ this.boxes = [];
575
+ }
576
+ scan() {
577
+ this.clear();
578
+ const seen = /* @__PURE__ */ new Set();
579
+ const nodes = document.querySelectorAll("[data-insp-path]");
580
+ for (const node of nodes) {
581
+ const hit = findClosestSource(node);
582
+ if (!hit || seen.has(hit.element)) continue;
583
+ seen.add(hit.element);
584
+ this.boxes.push(this.makeBox(hit.element, hit.source));
585
+ }
586
+ this.repaint();
587
+ }
588
+ makeBox(el, source) {
589
+ const box = document.createElement("div");
590
+ box.className = "inspekt-bbox";
591
+ const label = document.createElement("span");
592
+ label.className = "inspekt-bbox-label";
593
+ label.textContent = `${source.componentName} \xB7 ${fileTail(source.filePath)}:${source.line}`;
594
+ box.appendChild(label);
595
+ this.container.appendChild(box);
596
+ return { el, box, label, source };
597
+ }
598
+ scheduleRepaint = () => {
599
+ if (this.rafHandle !== null) return;
600
+ this.rafHandle = requestAnimationFrame(() => {
601
+ this.rafHandle = null;
602
+ this.repaint();
603
+ });
604
+ };
605
+ scheduleRescan = () => {
606
+ if (this.rafHandle !== null) cancelAnimationFrame(this.rafHandle);
607
+ this.rafHandle = requestAnimationFrame(() => {
608
+ this.rafHandle = null;
609
+ this.scan();
610
+ if (this.resizeObserver) {
611
+ this.resizeObserver.disconnect();
612
+ for (const { el } of this.boxes) this.resizeObserver.observe(el);
613
+ }
614
+ });
615
+ };
616
+ repaint() {
617
+ for (const binding of this.boxes) {
618
+ const rect = binding.el.getBoundingClientRect();
619
+ if (rect.width === 0 || rect.height === 0) {
620
+ binding.box.style.display = "none";
621
+ continue;
622
+ }
623
+ binding.box.style.display = "";
624
+ binding.box.style.transform = `translate(${rect.left}px, ${rect.top}px)`;
625
+ binding.box.style.width = `${rect.width}px`;
626
+ binding.box.style.height = `${rect.height}px`;
627
+ }
628
+ }
629
+ };
630
+ function fileTail(path) {
631
+ return path.split("/").pop() ?? path;
632
+ }
633
+
634
+ // src/snippet/snippet-resolver.ts
635
+ var CACHE_LIMIT = 100;
636
+ var cache = /* @__PURE__ */ new Map();
637
+ function cacheKey(opts) {
638
+ return `${opts.filePath}:${opts.line}:${opts.context ?? 5}`;
639
+ }
640
+ function lruTouch(key, value) {
641
+ cache.delete(key);
642
+ cache.set(key, value);
643
+ if (cache.size > CACHE_LIMIT) {
644
+ const oldest = cache.keys().next().value;
645
+ if (oldest !== void 0) cache.delete(oldest);
646
+ }
647
+ }
648
+ async function fetchFromDevServer(opts) {
649
+ if (!opts.serverUrl) return null;
650
+ const fetcher = opts.fetcher ?? fetch;
651
+ const url = new URL("/__inspekt/snippet", opts.serverUrl);
652
+ url.searchParams.set("file", opts.filePath);
653
+ url.searchParams.set("line", String(opts.line));
654
+ url.searchParams.set("context", String(opts.context ?? 5));
655
+ try {
656
+ const res = await fetcher(url.toString(), { method: "GET" });
657
+ if (!res.ok) return null;
658
+ const data = await res.json();
659
+ return { ...data, source: "devserver" };
660
+ } catch {
661
+ return null;
662
+ }
663
+ }
664
+ async function resolveSnippet(opts) {
665
+ const key = cacheKey(opts);
666
+ if (cache.has(key)) {
667
+ return cache.get(key) ?? null;
668
+ }
669
+ const stat = opts.staticSnippets?.[opts.filePath];
670
+ if (stat) {
671
+ const startLine = stat.startLine ?? 1;
672
+ const snippet = {
673
+ startLine,
674
+ endLine: startLine + stat.lines.length - 1,
675
+ targetLine: opts.line,
676
+ lines: stat.lines,
677
+ language: stat.language,
678
+ source: "devserver"
679
+ };
680
+ lruTouch(key, snippet);
681
+ return snippet;
682
+ }
683
+ const fromServer = await fetchFromDevServer(opts);
684
+ if (fromServer) {
685
+ lruTouch(key, fromServer);
686
+ return fromServer;
687
+ }
688
+ if (opts.sourceMapEnabled) {
689
+ try {
690
+ const mod = await Promise.resolve().then(() => (init_sourcemap_resolver(), sourcemap_resolver_exports));
691
+ const fromMap = await mod.resolveFromSourceMap({
692
+ filePath: opts.filePath,
693
+ line: opts.line,
694
+ context: opts.context,
695
+ fetcher: opts.fetcher
696
+ });
697
+ if (fromMap) {
698
+ lruTouch(key, fromMap);
699
+ return fromMap;
700
+ }
701
+ } catch {
702
+ }
703
+ }
704
+ lruTouch(key, null);
705
+ return null;
706
+ }
707
+
708
+ // src/highlight/prism.ts
709
+ var import_prismjs = __toESM(require("prismjs"), 1);
710
+ var import_prism_markup = require("prismjs/components/prism-markup.js");
711
+ var import_prism_clike = require("prismjs/components/prism-clike.js");
712
+ var import_prism_javascript = require("prismjs/components/prism-javascript.js");
713
+ var import_prism_jsx = require("prismjs/components/prism-jsx.js");
714
+ var import_prism_typescript = require("prismjs/components/prism-typescript.js");
715
+ var import_prism_tsx = require("prismjs/components/prism-tsx.js");
716
+ var import_prism_css = require("prismjs/components/prism-css.js");
717
+ var import_prism_json = require("prismjs/components/prism-json.js");
718
+ function resolveGrammarKey(lang) {
719
+ switch ((lang || "").toLowerCase()) {
720
+ case "js":
721
+ case "jsx":
722
+ case "mjs":
723
+ case "cjs":
724
+ return "jsx";
725
+ case "ts":
726
+ case "tsx":
727
+ return "tsx";
728
+ case "javascript":
729
+ return "javascript";
730
+ case "typescript":
731
+ return "typescript";
732
+ case "json":
733
+ return "json";
734
+ case "css":
735
+ case "scss":
736
+ case "less":
737
+ return "css";
738
+ case "html":
739
+ case "htm":
740
+ case "xml":
741
+ case "svg":
742
+ case "markup":
743
+ return "markup";
744
+ default:
745
+ return "plain";
746
+ }
747
+ }
748
+ function tokenizeToLines(code, lang) {
749
+ const key = resolveGrammarKey(lang);
750
+ const grammar = key === "plain" ? void 0 : import_prismjs.default.languages[key];
751
+ if (!grammar) {
752
+ return code.split("\n").map((text) => ({ tokens: [{ text, type: "" }] }));
753
+ }
754
+ const tokens = import_prismjs.default.tokenize(code, grammar);
755
+ return splitTokensIntoLines(tokens);
756
+ }
757
+ function splitTokensIntoLines(nodes) {
758
+ const lines = [{ tokens: [] }];
759
+ const current = () => lines[lines.length - 1].tokens;
760
+ function visit(node, parentType) {
761
+ if (typeof node === "string") {
762
+ pushText(node, parentType);
763
+ return;
764
+ }
765
+ if (Array.isArray(node)) {
766
+ for (const n of node) visit(n, parentType);
767
+ return;
768
+ }
769
+ const type = parentType ? `${parentType} ${node.type}` : node.type;
770
+ visit(node.content, type);
771
+ }
772
+ function pushText(text, type) {
773
+ let buf = text;
774
+ while (buf.length > 0) {
775
+ const nl = buf.indexOf("\n");
776
+ if (nl === -1) {
777
+ if (buf.length > 0) current().push({ text: buf, type });
778
+ return;
779
+ }
780
+ const before = buf.slice(0, nl);
781
+ if (before.length > 0) current().push({ text: before, type });
782
+ lines.push({ tokens: [] });
783
+ buf = buf.slice(nl + 1);
784
+ }
785
+ }
786
+ for (const n of nodes) visit(n, "");
787
+ return lines;
788
+ }
789
+
790
+ // src/components/tooltip.ts
791
+ var import_dom = require("@floating-ui/dom");
792
+ var counter = 0;
793
+ function attachTooltip(trigger, content, opts = {}) {
794
+ const enterDelay = opts.enterDelay ?? 200;
795
+ const leaveDelay = opts.leaveDelay ?? 80;
796
+ const placement = opts.placement ?? "top";
797
+ const offsetPx = opts.offsetPx ?? 8;
798
+ const root = opts.root ?? document.body;
799
+ const isRich = typeof content !== "string";
800
+ let bubble = null;
801
+ let enterTimer = null;
802
+ let leaveTimer = null;
803
+ let cleanupAutoUpdate = null;
804
+ const id = `inspekt-tooltip-${++counter}`;
805
+ function show() {
806
+ if (bubble) return;
807
+ bubble = document.createElement("div");
808
+ bubble.className = "inspekt-tooltip";
809
+ bubble.id = id;
810
+ bubble.setAttribute("role", "tooltip");
811
+ bubble.style.position = "fixed";
812
+ bubble.style.top = "0";
813
+ bubble.style.left = "0";
814
+ bubble.style.zIndex = "2147483647";
815
+ if (typeof content === "string") {
816
+ bubble.textContent = content;
817
+ } else {
818
+ bubble.classList.add("inspekt-tooltip--rich");
819
+ bubble.appendChild(content);
820
+ }
821
+ root.appendChild(bubble);
822
+ cleanupAutoUpdate = (0, import_dom.autoUpdate)(trigger, bubble, update);
823
+ if (isRich) {
824
+ bubble.addEventListener("mouseenter", cancelLeave);
825
+ bubble.addEventListener("mouseleave", scheduleHide);
826
+ }
827
+ }
828
+ function hide() {
829
+ if (!bubble) return;
830
+ cleanupAutoUpdate?.();
831
+ cleanupAutoUpdate = null;
832
+ bubble.remove();
833
+ bubble = null;
834
+ }
835
+ function update() {
836
+ if (!bubble) return;
837
+ void (0, import_dom.computePosition)(trigger, bubble, {
838
+ placement,
839
+ strategy: "fixed",
840
+ middleware: [(0, import_dom.offset)(offsetPx), (0, import_dom.flip)(), (0, import_dom.shift)({ padding: 6 })]
841
+ }).then(({ x, y }) => {
842
+ if (!bubble || !bubble.isConnected) return;
843
+ bubble.style.left = `${Math.round(x)}px`;
844
+ bubble.style.top = `${Math.round(y)}px`;
845
+ });
846
+ }
847
+ function scheduleShow() {
848
+ cancelLeave();
849
+ if (enterTimer) clearTimeout(enterTimer);
850
+ enterTimer = setTimeout(() => {
851
+ enterTimer = null;
852
+ show();
853
+ }, enterDelay);
854
+ }
855
+ function scheduleHide() {
856
+ if (enterTimer) {
857
+ clearTimeout(enterTimer);
858
+ enterTimer = null;
859
+ }
860
+ if (leaveTimer) clearTimeout(leaveTimer);
861
+ leaveTimer = setTimeout(() => {
862
+ leaveTimer = null;
863
+ hide();
864
+ }, leaveDelay);
865
+ }
866
+ function cancelLeave() {
867
+ if (leaveTimer) {
868
+ clearTimeout(leaveTimer);
869
+ leaveTimer = null;
870
+ }
871
+ }
872
+ function onKey(e) {
873
+ if (e.key === "Escape") hide();
874
+ }
875
+ trigger.setAttribute("aria-describedby", id);
876
+ trigger.addEventListener("mouseenter", scheduleShow);
877
+ trigger.addEventListener("mouseleave", scheduleHide);
878
+ trigger.addEventListener("focus", scheduleShow);
879
+ trigger.addEventListener("blur", scheduleHide);
880
+ trigger.addEventListener("keydown", onKey);
881
+ return function detach() {
882
+ if (enterTimer) clearTimeout(enterTimer);
883
+ if (leaveTimer) clearTimeout(leaveTimer);
884
+ trigger.removeEventListener("mouseenter", scheduleShow);
885
+ trigger.removeEventListener("mouseleave", scheduleHide);
886
+ trigger.removeEventListener("focus", scheduleShow);
887
+ trigger.removeEventListener("blur", scheduleHide);
888
+ trigger.removeEventListener("keydown", onKey);
889
+ trigger.removeAttribute("aria-describedby");
890
+ hide();
891
+ };
892
+ }
893
+
894
+ // src/popover/popover.ts
895
+ var ICON_COPY = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg>`;
896
+ var ICON_HTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>`;
897
+ var ICON_CONSOLE = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>`;
898
+ var ICON_INFO = `<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9.5"/><line x1="12" y1="11" x2="12" y2="16.5"/><circle cx="12" cy="7.8" r="0.6" fill="currentColor" stroke="none"/></svg>`;
899
+ var DEFAULT_SNIPPET_CONFIG = {
900
+ context: 5,
901
+ defaultExpanded: false,
902
+ sourceMapEnabled: false
903
+ };
904
+ var Popover = class {
905
+ constructor(shadowRoot) {
906
+ this.shadowRoot = shadowRoot;
907
+ this.container = document.createElement("div");
908
+ this.container.className = "inspekt-popover";
909
+ this.container.style.display = "none";
910
+ this.shadowRoot.appendChild(this.container);
911
+ }
912
+ shadowRoot;
913
+ container;
914
+ visible = false;
915
+ pinned = false;
916
+ snippetConfig = { ...DEFAULT_SNIPPET_CONFIG };
917
+ /** Lock the popover in place — hover/move handlers will not reposition or
918
+ * hide it until {@link unpin} is called. Clicks set this; Escape clears it. */
919
+ pin() {
920
+ this.pinned = true;
921
+ }
922
+ unpin() {
923
+ this.pinned = false;
924
+ }
925
+ isPinned() {
926
+ return this.pinned;
927
+ }
928
+ configureSnippet(config) {
929
+ this.snippetConfig = { ...this.snippetConfig, ...config };
930
+ }
931
+ /**
932
+ * Fallback popover for Standalone-on-an-unbuilt-page: no source attrs,
933
+ * no React fibers — so we show DOM info (selector, classes, id) plus a
934
+ * minimal action set. Keeps Inspekt useful as a pure inspection tool.
935
+ */
936
+ showDom(domElement, position, onCopySelector, onCopyHtml, onConsoleLog) {
937
+ this.container.innerHTML = "";
938
+ const selector = computeSelector(domElement);
939
+ const header = document.createElement("div");
940
+ header.className = "inspekt-popover-header";
941
+ const eyebrow = document.createElement("div");
942
+ eyebrow.className = "inspekt-popover-path-dir inspekt-popover-path-dir-row";
943
+ const eyebrowText = document.createElement("span");
944
+ eyebrowText.textContent = "DOM element";
945
+ eyebrow.appendChild(eyebrowText);
946
+ const infoBadge = document.createElement("span");
947
+ infoBadge.className = "inspekt-info-badge";
948
+ infoBadge.setAttribute("tabindex", "0");
949
+ infoBadge.setAttribute("aria-label", "Why no source data?");
950
+ infoBadge.innerHTML = `${ICON_INFO}<span class="inspekt-info-badge-label">No source</span>`;
951
+ attachTooltip(
952
+ infoBadge,
953
+ "No Inspekt source data on this page. Build with the Inspekt plugin (Vite, Webpack, Rspack, esbuild) to get editor-jump and file paths.",
954
+ { root: this.shadowRoot, enterDelay: 100 }
955
+ );
956
+ eyebrow.appendChild(infoBadge);
957
+ const sel = document.createElement("div");
958
+ sel.className = "inspekt-popover-path-file";
959
+ sel.textContent = selector;
960
+ header.appendChild(eyebrow);
961
+ header.appendChild(sel);
962
+ this.container.appendChild(header);
963
+ const actionsBar = document.createElement("div");
964
+ actionsBar.className = "inspekt-popover-actions";
965
+ const copyBtn = document.createElement("button");
966
+ copyBtn.className = "inspekt-popover-action";
967
+ copyBtn.setAttribute("aria-label", "Copy CSS selector");
968
+ copyBtn.innerHTML = ICON_COPY;
969
+ attachTooltip(copyBtn, "Copy CSS selector", {
970
+ root: this.shadowRoot
971
+ });
972
+ copyBtn.addEventListener("click", async (e) => {
973
+ e.preventDefault();
974
+ e.stopPropagation();
975
+ const ok = await onCopySelector(selector);
976
+ this.showToast(ok ? "Copied" : "Copy failed");
977
+ setTimeout(() => this.hide(), 700);
978
+ });
979
+ actionsBar.appendChild(copyBtn);
980
+ const htmlBtn = document.createElement("button");
981
+ htmlBtn.className = "inspekt-popover-action";
982
+ htmlBtn.setAttribute("aria-label", "Copy HTML");
983
+ htmlBtn.innerHTML = ICON_HTML;
984
+ attachTooltip(htmlBtn, "Copy HTML (outerHTML)", {
985
+ root: this.shadowRoot
986
+ });
987
+ htmlBtn.addEventListener("click", async (e) => {
988
+ e.preventDefault();
989
+ e.stopPropagation();
990
+ const ok = await onCopyHtml(domElement.outerHTML);
991
+ this.showToast(ok ? "HTML copied" : "Copy failed");
992
+ setTimeout(() => this.hide(), 700);
993
+ });
994
+ actionsBar.appendChild(htmlBtn);
995
+ const logBtn = document.createElement("button");
996
+ logBtn.className = "inspekt-popover-action";
997
+ logBtn.setAttribute("aria-label", "console.log this element");
998
+ logBtn.innerHTML = ICON_CONSOLE;
999
+ attachTooltip(logBtn, "console.log this element", {
1000
+ root: this.shadowRoot
1001
+ });
1002
+ logBtn.addEventListener("click", (e) => {
1003
+ e.preventDefault();
1004
+ e.stopPropagation();
1005
+ onConsoleLog();
1006
+ this.showToast("Logged to console");
1007
+ setTimeout(() => this.hide(), 700);
1008
+ });
1009
+ actionsBar.appendChild(logBtn);
1010
+ this.container.appendChild(actionsBar);
1011
+ this.positionAt(position.x, position.y);
1012
+ this.container.style.display = "block";
1013
+ this.visible = true;
1014
+ }
1015
+ /** Brief feedback toast attached to the shadow root. */
1016
+ showToast(message) {
1017
+ const existing = this.shadowRoot.querySelector(".inspekt-popover-toast");
1018
+ if (existing) existing.remove();
1019
+ const toast = document.createElement("div");
1020
+ toast.className = "inspekt-popover-toast";
1021
+ toast.textContent = message;
1022
+ this.shadowRoot.appendChild(toast);
1023
+ setTimeout(() => toast.remove(), 1200);
1024
+ }
1025
+ show(element, actions, position) {
1026
+ this.container.innerHTML = "";
1027
+ const header = document.createElement("div");
1028
+ header.className = "inspekt-popover-header";
1029
+ const pathDir = document.createElement("div");
1030
+ pathDir.className = "inspekt-popover-path-dir";
1031
+ const parts = element.filePath.split("/");
1032
+ pathDir.textContent = parts.slice(0, -1).join("/") + "/";
1033
+ const pathFile = document.createElement("div");
1034
+ pathFile.className = "inspekt-popover-path-file";
1035
+ pathFile.textContent = `${parts[parts.length - 1]}:${element.line}`;
1036
+ header.appendChild(pathDir);
1037
+ header.appendChild(pathFile);
1038
+ this.container.appendChild(header);
1039
+ const snippetSection = this.buildSnippetSection(element);
1040
+ this.container.appendChild(snippetSection);
1041
+ const actionsBar = document.createElement("div");
1042
+ actionsBar.className = "inspekt-popover-actions";
1043
+ for (const action of actions) {
1044
+ const btn = document.createElement("button");
1045
+ btn.className = "inspekt-popover-action";
1046
+ btn.setAttribute("aria-label", action.label);
1047
+ btn.innerHTML = action.icon;
1048
+ attachTooltip(btn, action.label, {
1049
+ root: this.shadowRoot
1050
+ });
1051
+ btn.addEventListener("click", (e) => {
1052
+ e.stopPropagation();
1053
+ action.handler(element);
1054
+ this.hide();
1055
+ });
1056
+ actionsBar.appendChild(btn);
1057
+ }
1058
+ this.container.appendChild(actionsBar);
1059
+ this.positionAt(position.x, position.y);
1060
+ this.container.style.display = "block";
1061
+ this.visible = true;
1062
+ }
1063
+ buildSnippetSection(element) {
1064
+ const section = document.createElement("div");
1065
+ section.className = "inspekt-snippet";
1066
+ section.hidden = true;
1067
+ const initialState = this.snippetConfig.defaultExpanded ? "expanded" : "collapsed";
1068
+ section.dataset["state"] = initialState;
1069
+ const toggle = document.createElement("button");
1070
+ toggle.type = "button";
1071
+ toggle.className = "inspekt-snippet-toggle";
1072
+ toggle.textContent = initialState === "collapsed" ? "Show source \u25BE" : "Hide source \u25B4";
1073
+ const body = document.createElement("div");
1074
+ body.className = "inspekt-snippet-body";
1075
+ if (initialState === "collapsed") body.hidden = true;
1076
+ section.appendChild(toggle);
1077
+ section.appendChild(body);
1078
+ void this.resolveAndMountSnippet(section, toggle, body, element, initialState);
1079
+ return section;
1080
+ }
1081
+ async resolveAndMountSnippet(section, toggle, body, element, initialState) {
1082
+ const snippet = await resolveSnippet({
1083
+ filePath: element.filePath,
1084
+ line: element.line,
1085
+ serverUrl: this.snippetConfig.serverUrl,
1086
+ context: this.snippetConfig.context,
1087
+ sourceMapEnabled: this.snippetConfig.sourceMapEnabled,
1088
+ staticSnippets: this.snippetConfig.staticSnippets
1089
+ });
1090
+ if (!snippet) {
1091
+ section.remove();
1092
+ return;
1093
+ }
1094
+ element.snippet = snippet;
1095
+ section.hidden = false;
1096
+ let rendered = false;
1097
+ const expand = () => {
1098
+ section.dataset["state"] = "expanded";
1099
+ body.dataset["state"] = "expanded";
1100
+ body.hidden = false;
1101
+ toggle.textContent = "Hide source \u25B4";
1102
+ if (!rendered) {
1103
+ rendered = true;
1104
+ body.innerHTML = "";
1105
+ body.appendChild(this.renderSnippet(snippet));
1106
+ }
1107
+ requestAnimationFrame(() => {
1108
+ const target = body.querySelector(".inspekt-line-target");
1109
+ target?.scrollIntoView({ block: "center", inline: "nearest" });
1110
+ });
1111
+ };
1112
+ const collapse = () => {
1113
+ section.dataset["state"] = "collapsed";
1114
+ body.dataset["state"] = "collapsed";
1115
+ body.hidden = true;
1116
+ toggle.textContent = "Show source \u25BE";
1117
+ };
1118
+ toggle.addEventListener("click", (e) => {
1119
+ e.stopPropagation();
1120
+ if (section.dataset["state"] === "collapsed") expand();
1121
+ else collapse();
1122
+ });
1123
+ if (initialState === "expanded") expand();
1124
+ }
1125
+ renderSnippet(snippet) {
1126
+ const pre = document.createElement("pre");
1127
+ pre.className = "inspekt-snippet-pre";
1128
+ pre.dataset["language"] = snippet.language;
1129
+ const highlighted = tokenizeToLines(snippet.lines.join("\n"), snippet.language);
1130
+ snippet.lines.forEach((rawText, index) => {
1131
+ const lineNumber = snippet.startLine + index;
1132
+ const isTarget = lineNumber === snippet.targetLine;
1133
+ const lineEl = document.createElement("span");
1134
+ lineEl.className = isTarget ? "inspekt-line inspekt-line-target" : "inspekt-line";
1135
+ const num = document.createElement("span");
1136
+ num.className = "inspekt-line-no";
1137
+ num.textContent = String(lineNumber);
1138
+ lineEl.appendChild(num);
1139
+ const code = document.createElement("span");
1140
+ code.className = "inspekt-line-code";
1141
+ const tokens = highlighted[index]?.tokens;
1142
+ if (!tokens || tokens.length === 0) {
1143
+ code.textContent = rawText;
1144
+ } else {
1145
+ for (const tok of tokens) {
1146
+ if (!tok.type) {
1147
+ code.appendChild(document.createTextNode(tok.text));
1148
+ } else {
1149
+ const span = document.createElement("span");
1150
+ span.className = `token ${tok.type}`;
1151
+ span.textContent = tok.text;
1152
+ code.appendChild(span);
1153
+ }
1154
+ }
1155
+ }
1156
+ lineEl.appendChild(code);
1157
+ pre.appendChild(lineEl);
1158
+ pre.appendChild(document.createTextNode("\n"));
1159
+ });
1160
+ return pre;
1161
+ }
1162
+ hide() {
1163
+ this.container.style.display = "none";
1164
+ this.visible = false;
1165
+ this.pinned = false;
1166
+ }
1167
+ isVisible() {
1168
+ return this.visible;
1169
+ }
1170
+ positionAt(x, y) {
1171
+ const padding = 8;
1172
+ const vpWidth = window.innerWidth;
1173
+ const vpHeight = window.innerHeight;
1174
+ this.container.style.display = "block";
1175
+ this.container.style.left = "0px";
1176
+ this.container.style.top = "0px";
1177
+ const rect = this.container.getBoundingClientRect();
1178
+ let left = x;
1179
+ let top = y + 10;
1180
+ if (left + rect.width + padding > vpWidth) {
1181
+ left = vpWidth - rect.width - padding;
1182
+ }
1183
+ if (left < padding) left = padding;
1184
+ if (top + rect.height + padding > vpHeight) {
1185
+ top = y - rect.height - 10;
1186
+ }
1187
+ if (top < padding) top = padding;
1188
+ this.container.style.left = `${left}px`;
1189
+ this.container.style.top = `${top}px`;
1190
+ }
1191
+ destroy() {
1192
+ this.container.remove();
1193
+ }
1194
+ };
1195
+ function computeSelector(el) {
1196
+ const tag = el.tagName.toLowerCase();
1197
+ if (el.id) return `${tag}#${el.id}`;
1198
+ const classes = Array.from(el.classList).slice(0, 2);
1199
+ if (classes.length) return `${tag}.${classes.join(".")}`;
1200
+ return tag;
1201
+ }
1202
+
1203
+ // src/overlay/overlay.ts
1204
+ var MAX_VISIBLE_BADGES = 200;
1205
+ var Overlay = class {
1206
+ constructor(shadowRoot) {
1207
+ this.shadowRoot = shadowRoot;
1208
+ this.container = document.createElement("div");
1209
+ this.container.className = "inspekt-overlay";
1210
+ this.container.style.display = "none";
1211
+ this.shadowRoot.appendChild(this.container);
1212
+ }
1213
+ shadowRoot;
1214
+ badges = [];
1215
+ mutationObserver = null;
1216
+ intersectionObserver = null;
1217
+ debounceTimer = null;
1218
+ container;
1219
+ visible = false;
1220
+ show() {
1221
+ this.visible = true;
1222
+ this.container.style.display = "block";
1223
+ this.scan();
1224
+ this.startObserving();
1225
+ }
1226
+ hide() {
1227
+ this.visible = false;
1228
+ this.container.style.display = "none";
1229
+ this.clear();
1230
+ this.stopObserving();
1231
+ }
1232
+ toggle() {
1233
+ if (this.visible) this.hide();
1234
+ else this.show();
1235
+ }
1236
+ isVisible() {
1237
+ return this.visible;
1238
+ }
1239
+ scan() {
1240
+ this.clear();
1241
+ const elements = document.querySelectorAll(
1242
+ "[data-insp-path], [data-insp-path]"
1243
+ );
1244
+ const detected = new Set(elements);
1245
+ const outermost = [];
1246
+ for (const el of elements) {
1247
+ let parent = el.parentElement;
1248
+ let isNested = false;
1249
+ while (parent) {
1250
+ if (detected.has(parent)) {
1251
+ isNested = true;
1252
+ break;
1253
+ }
1254
+ parent = parent.parentElement;
1255
+ }
1256
+ if (!isNested) outermost.push(el);
1257
+ }
1258
+ const toRender = outermost.slice(0, MAX_VISIBLE_BADGES);
1259
+ this.intersectionObserver = new IntersectionObserver(
1260
+ (entries) => {
1261
+ for (const entry of entries) {
1262
+ const badge = this.badges.find((b) => b.element === entry.target);
1263
+ if (!badge) continue;
1264
+ if (entry.isIntersecting) {
1265
+ badge.visible = true;
1266
+ badge.badgeElement.style.display = "block";
1267
+ this.positionBadge(badge.badgeElement, badge.element);
1268
+ } else {
1269
+ badge.visible = false;
1270
+ badge.badgeElement.style.display = "none";
1271
+ }
1272
+ }
1273
+ },
1274
+ { rootMargin: "50px" }
1275
+ );
1276
+ for (const el of toRender) {
1277
+ const attrValue = findSourceAttribute(el);
1278
+ if (!attrValue) continue;
1279
+ const source = parseSourceAttribute(attrValue);
1280
+ if (!source) continue;
1281
+ this.createBadge(el, source.componentName);
1282
+ }
1283
+ }
1284
+ createBadge(element, componentName) {
1285
+ const badge = document.createElement("div");
1286
+ badge.className = "inspekt-badge";
1287
+ badge.textContent = componentName;
1288
+ badge.addEventListener("click", (e) => {
1289
+ e.stopPropagation();
1290
+ element.dispatchEvent(
1291
+ new CustomEvent("inspekt:badge-click", {
1292
+ bubbles: true,
1293
+ detail: { element }
1294
+ })
1295
+ );
1296
+ });
1297
+ badge.addEventListener("mouseenter", () => {
1298
+ element.dispatchEvent(
1299
+ new CustomEvent("inspekt:badge-hover", {
1300
+ bubbles: true,
1301
+ detail: { element }
1302
+ })
1303
+ );
1304
+ });
1305
+ badge.addEventListener("mouseleave", () => {
1306
+ element.dispatchEvent(
1307
+ new CustomEvent("inspekt:badge-leave", {
1308
+ bubbles: true,
1309
+ detail: { element }
1310
+ })
1311
+ );
1312
+ });
1313
+ this.container.appendChild(badge);
1314
+ this.positionBadge(badge, element);
1315
+ const badgeEntry = { element, componentName, badgeElement: badge, visible: true };
1316
+ this.badges.push(badgeEntry);
1317
+ this.intersectionObserver?.observe(element);
1318
+ }
1319
+ positionBadge(badge, target) {
1320
+ const rect = target.getBoundingClientRect();
1321
+ const badgeHeight = 18;
1322
+ const badgeWidth = badge.offsetWidth || 60;
1323
+ const scrollX = window.scrollX;
1324
+ const scrollY = window.scrollY;
1325
+ let left = rect.left + scrollX;
1326
+ let top = rect.top + scrollY - badgeHeight - 2;
1327
+ const vpWidth = window.innerWidth;
1328
+ if (left + badgeWidth > vpWidth + scrollX) {
1329
+ left = vpWidth + scrollX - badgeWidth - 4;
1330
+ }
1331
+ if (left < scrollX + 4) {
1332
+ left = scrollX + 4;
1333
+ }
1334
+ if (top < scrollY) {
1335
+ top = rect.bottom + scrollY + 2;
1336
+ }
1337
+ badge.style.left = `${left}px`;
1338
+ badge.style.top = `${top}px`;
1339
+ }
1340
+ updatePositions() {
1341
+ for (const { badgeElement, element, visible } of this.badges) {
1342
+ if (visible) {
1343
+ this.positionBadge(badgeElement, element);
1344
+ }
1345
+ }
1346
+ }
1347
+ startObserving() {
1348
+ this.mutationObserver = new MutationObserver(() => {
1349
+ if (this.debounceTimer) clearTimeout(this.debounceTimer);
1350
+ this.debounceTimer = setTimeout(() => this.scan(), 100);
1351
+ });
1352
+ this.mutationObserver.observe(document.body, {
1353
+ childList: true,
1354
+ subtree: true
1355
+ });
1356
+ window.addEventListener("scroll", this.handleScrollResize, true);
1357
+ window.addEventListener("resize", this.handleScrollResize);
1358
+ }
1359
+ stopObserving() {
1360
+ this.mutationObserver?.disconnect();
1361
+ this.mutationObserver = null;
1362
+ this.intersectionObserver?.disconnect();
1363
+ this.intersectionObserver = null;
1364
+ if (this.debounceTimer) {
1365
+ clearTimeout(this.debounceTimer);
1366
+ this.debounceTimer = null;
1367
+ }
1368
+ window.removeEventListener("scroll", this.handleScrollResize, true);
1369
+ window.removeEventListener("resize", this.handleScrollResize);
1370
+ }
1371
+ handleScrollResize = () => {
1372
+ if (this.debounceTimer) clearTimeout(this.debounceTimer);
1373
+ this.debounceTimer = setTimeout(() => this.updatePositions(), 16);
1374
+ };
1375
+ clear() {
1376
+ this.intersectionObserver?.disconnect();
1377
+ for (const { badgeElement } of this.badges) {
1378
+ badgeElement.remove();
1379
+ }
1380
+ this.badges = [];
1381
+ }
1382
+ destroy() {
1383
+ this.hide();
1384
+ this.container.remove();
1385
+ }
1386
+ };
1387
+
1388
+ // src/actions/built-in.ts
1389
+ var EDITOR_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>`;
1390
+ var COPY_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>`;
1391
+ var GITHUB_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0 1 12 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"/></svg>`;
1392
+ var CONSOLE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" x2="20" y1="19" y2="19"/></svg>`;
1393
+ function createOpenEditorAction(serverUrl, editor, customEditors = []) {
1394
+ return {
1395
+ id: "open-editor",
1396
+ label: "Open in Editor",
1397
+ icon: EDITOR_ICON,
1398
+ handler(element) {
1399
+ const url = `${serverUrl}/__inspekt/open`;
1400
+ const body = JSON.stringify({
1401
+ file: element.filePath,
1402
+ line: element.line,
1403
+ column: element.column,
1404
+ editor
1405
+ });
1406
+ fetch(url, {
1407
+ method: "POST",
1408
+ headers: { "Content-Type": "application/json" },
1409
+ body
1410
+ }).catch(() => {
1411
+ const protocol = editorProtocol(editor, element, customEditors);
1412
+ if (protocol) window.open(protocol, "_blank");
1413
+ });
1414
+ }
1415
+ };
1416
+ }
1417
+ function createCopyPathAction() {
1418
+ return {
1419
+ id: "copy-path",
1420
+ label: "Copy Path",
1421
+ icon: COPY_ICON,
1422
+ handler(element) {
1423
+ const text = `${element.filePath}:${element.line}`;
1424
+ navigator.clipboard.writeText(text).catch(() => {
1425
+ const textarea = document.createElement("textarea");
1426
+ textarea.value = text;
1427
+ textarea.style.position = "fixed";
1428
+ textarea.style.opacity = "0";
1429
+ document.body.appendChild(textarea);
1430
+ textarea.select();
1431
+ document.execCommand("copy");
1432
+ document.body.removeChild(textarea);
1433
+ });
1434
+ }
1435
+ };
1436
+ }
1437
+ function createOpenGithubAction(repo, branch) {
1438
+ return {
1439
+ id: "open-github",
1440
+ label: "Open on GitHub",
1441
+ icon: GITHUB_ICON,
1442
+ handler(element) {
1443
+ if (!repo) return;
1444
+ const url = `https://github.com/${repo}/blob/${branch}/${element.filePath}#L${element.line}`;
1445
+ window.open(url, "_blank");
1446
+ }
1447
+ };
1448
+ }
1449
+ function createConsoleLogAction() {
1450
+ return {
1451
+ id: "console-log",
1452
+ label: "Log to Console",
1453
+ icon: CONSOLE_ICON,
1454
+ handler(element) {
1455
+ console.group(`[Inspekt] ${element.componentName}`);
1456
+ console.log("File:", `${element.filePath}:${element.line}:${element.column}`);
1457
+ console.log("Element:", element.domElement);
1458
+ console.log("Tag:", element.tagName);
1459
+ if (element.props) console.log("Props:", element.props);
1460
+ console.groupEnd();
1461
+ }
1462
+ };
1463
+ }
1464
+ function editorProtocol(editor, element, customEditors = []) {
1465
+ const { filePath, line, column } = element;
1466
+ for (const ce of customEditors) {
1467
+ if (ce.value === editor) {
1468
+ return ce.urlTemplate.replace(/\{file\}/g, encodeURIComponent(filePath)).replace(/\{line\}/g, String(line)).replace(/\{column\}/g, String(column));
1469
+ }
1470
+ }
1471
+ switch (editor) {
1472
+ case "vscode":
1473
+ return `vscode://file/${filePath}:${line}:${column}`;
1474
+ case "vscode-insiders":
1475
+ return `vscode-insiders://file/${filePath}:${line}:${column}`;
1476
+ case "cursor":
1477
+ return `cursor://file/${filePath}:${line}:${column}`;
1478
+ case "windsurf":
1479
+ return `windsurf://file/${filePath}:${line}:${column}`;
1480
+ case "webstorm":
1481
+ return `webstorm://open?file=${filePath}&line=${line}&column=${column}`;
1482
+ case "phpstorm":
1483
+ return `phpstorm://open?file=${filePath}&line=${line}&column=${column}`;
1484
+ case "idea":
1485
+ return `idea://open?file=${filePath}&line=${line}&column=${column}`;
1486
+ case "zed":
1487
+ return `zed://file/${filePath}:${line}:${column}`;
1488
+ case "sublime":
1489
+ return `subl://open?url=file://${filePath}&line=${line}&column=${column}`;
1490
+ // JetBrains family — query-string shape, same as webstorm/phpstorm/idea.
1491
+ case "pycharm":
1492
+ return `pycharm://open?file=${filePath}&line=${line}&column=${column}`;
1493
+ case "rubymine":
1494
+ return `rubymine://open?file=${filePath}&line=${line}&column=${column}`;
1495
+ case "goland":
1496
+ return `goland://open?file=${filePath}&line=${line}&column=${column}`;
1497
+ case "clion":
1498
+ return `clion://open?file=${filePath}&line=${line}&column=${column}`;
1499
+ case "rider":
1500
+ return `rider://open?file=${filePath}&line=${line}&column=${column}`;
1501
+ // VS Code family
1502
+ case "vscodium":
1503
+ return `vscodium://file/${filePath}:${line}:${column}`;
1504
+ // AI editors — VS Code fork URL shape. Trae/Antigravity/Qoder/CodeBuddy
1505
+ // are inferred (vendor docs don't enumerate the file-open scheme); we
1506
+ // surface this in the UI as "(unverified)" so users can test before
1507
+ // committing.
1508
+ case "trae":
1509
+ return `trae://file/${filePath}:${line}:${column}`;
1510
+ case "kiro":
1511
+ return `kiro://file/${filePath}:${line}:${column}`;
1512
+ case "antigravity":
1513
+ return `antigravity://file/${filePath}:${line}:${column}`;
1514
+ case "pearai":
1515
+ return `pearai://file/${filePath}:${line}:${column}`;
1516
+ case "qoder":
1517
+ return `qoder://file/${filePath}:${line}:${column}`;
1518
+ case "codebuddy":
1519
+ return `codebuddy://file/${filePath}:${line}:${column}`;
1520
+ default:
1521
+ return `vscode://file/${filePath}:${line}:${column}`;
1522
+ }
1523
+ }
1524
+
1525
+ // src/tree-panel/tree-panel.ts
1526
+ var TreePanel = class {
1527
+ constructor(shadowRoot, options) {
1528
+ this.shadowRoot = shadowRoot;
1529
+ this.options = options;
1530
+ this.container = document.createElement("div");
1531
+ this.container.className = `inspekt-tree-panel inspekt-tree-${options.position}`;
1532
+ this.container.style.display = "none";
1533
+ this.shadowRoot.appendChild(this.container);
1534
+ this.buildUI();
1535
+ }
1536
+ shadowRoot;
1537
+ options;
1538
+ container;
1539
+ visible = false;
1540
+ searchInput = null;
1541
+ treeContainer = null;
1542
+ filter = "";
1543
+ buildUI() {
1544
+ const header = document.createElement("div");
1545
+ header.className = "inspekt-tree-header";
1546
+ const title = document.createElement("span");
1547
+ title.className = "inspekt-tree-title";
1548
+ title.textContent = "Component Tree";
1549
+ const closeBtn = document.createElement("button");
1550
+ closeBtn.className = "inspekt-tree-close";
1551
+ closeBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>`;
1552
+ closeBtn.addEventListener("click", () => this.hide());
1553
+ header.appendChild(title);
1554
+ header.appendChild(closeBtn);
1555
+ this.container.appendChild(header);
1556
+ const searchWrap = document.createElement("div");
1557
+ searchWrap.className = "inspekt-tree-search";
1558
+ this.searchInput = document.createElement("input");
1559
+ this.searchInput.type = "text";
1560
+ this.searchInput.placeholder = "Filter components...";
1561
+ this.searchInput.className = "inspekt-tree-search-input";
1562
+ this.searchInput.addEventListener("input", () => {
1563
+ this.filter = this.searchInput.value.toLowerCase();
1564
+ this.renderCurrentTree();
1565
+ });
1566
+ searchWrap.appendChild(this.searchInput);
1567
+ this.container.appendChild(searchWrap);
1568
+ this.treeContainer = document.createElement("div");
1569
+ this.treeContainer.className = "inspekt-tree-content";
1570
+ this.container.appendChild(this.treeContainer);
1571
+ const resizeHandle = document.createElement("div");
1572
+ resizeHandle.className = `inspekt-tree-resize inspekt-tree-resize-${this.options.position === "right" ? "left" : "right"}`;
1573
+ this.container.appendChild(resizeHandle);
1574
+ this.setupResize(resizeHandle);
1575
+ }
1576
+ currentTree = null;
1577
+ show() {
1578
+ this.visible = true;
1579
+ this.container.style.display = "flex";
1580
+ }
1581
+ hide() {
1582
+ this.visible = false;
1583
+ this.container.style.display = "none";
1584
+ }
1585
+ toggle() {
1586
+ if (this.visible) this.hide();
1587
+ else this.show();
1588
+ }
1589
+ isVisible() {
1590
+ return this.visible;
1591
+ }
1592
+ updateTree(tree) {
1593
+ this.currentTree = tree;
1594
+ this.renderCurrentTree();
1595
+ }
1596
+ selectElement(element) {
1597
+ if (!this.treeContainer) return;
1598
+ const nodes = this.treeContainer.querySelectorAll(".inspekt-tree-node");
1599
+ for (const node of nodes) {
1600
+ node.classList.remove("inspekt-tree-node-selected");
1601
+ const path = node.getAttribute("data-path");
1602
+ if (path === `${element.filePath}:${element.line}`) {
1603
+ node.classList.add("inspekt-tree-node-selected");
1604
+ node.scrollIntoView({ behavior: "smooth", block: "nearest" });
1605
+ }
1606
+ }
1607
+ }
1608
+ renderCurrentTree() {
1609
+ if (!this.treeContainer) return;
1610
+ this.treeContainer.innerHTML = "";
1611
+ if (!this.currentTree) {
1612
+ const empty = document.createElement("div");
1613
+ empty.className = "inspekt-tree-empty";
1614
+ empty.textContent = "No component tree detected";
1615
+ this.treeContainer.appendChild(empty);
1616
+ return;
1617
+ }
1618
+ const fragment = document.createDocumentFragment();
1619
+ this.renderNode(this.currentTree, fragment, 0);
1620
+ this.treeContainer.appendChild(fragment);
1621
+ }
1622
+ renderNode(node, parent, depth) {
1623
+ if (this.filter && !this.matchesFilter(node)) return;
1624
+ const row = document.createElement("div");
1625
+ row.className = "inspekt-tree-node";
1626
+ row.setAttribute("data-path", `${node.filePath ?? ""}:${node.line ?? ""}`);
1627
+ row.style.paddingLeft = `${depth * 16 + 8}px`;
1628
+ const hasChildren = node.children.length > 0;
1629
+ const arrow = document.createElement("span");
1630
+ arrow.className = "inspekt-tree-arrow";
1631
+ if (hasChildren) {
1632
+ arrow.innerHTML = `<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>`;
1633
+ arrow.classList.add("inspekt-tree-arrow-expanded");
1634
+ }
1635
+ row.appendChild(arrow);
1636
+ const name = document.createElement("span");
1637
+ name.className = "inspekt-tree-name";
1638
+ name.textContent = node.name;
1639
+ row.appendChild(name);
1640
+ if (this.options.showLineNumbers && node.line) {
1641
+ const line = document.createElement("span");
1642
+ line.className = "inspekt-tree-line";
1643
+ line.textContent = `:${node.line}`;
1644
+ row.appendChild(line);
1645
+ }
1646
+ if (this.options.showProps && node.props) {
1647
+ const propsEl = document.createElement("span");
1648
+ propsEl.className = "inspekt-tree-props";
1649
+ const entries = Object.entries(node.props).slice(0, 3);
1650
+ propsEl.textContent = entries.map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(" ");
1651
+ row.appendChild(propsEl);
1652
+ }
1653
+ row.addEventListener("click", (e) => {
1654
+ e.stopPropagation();
1655
+ this.options.onSelect(node);
1656
+ if (hasChildren) {
1657
+ const childContainer = row.nextElementSibling;
1658
+ if (childContainer?.classList.contains("inspekt-tree-children")) {
1659
+ const isCollapsed = childContainer.classList.toggle("inspekt-tree-collapsed");
1660
+ arrow.classList.toggle("inspekt-tree-arrow-collapsed", isCollapsed);
1661
+ arrow.classList.toggle("inspekt-tree-arrow-expanded", !isCollapsed);
1662
+ }
1663
+ }
1664
+ this.treeContainer.querySelectorAll(".inspekt-tree-node-selected").forEach((n) => n.classList.remove("inspekt-tree-node-selected"));
1665
+ row.classList.add("inspekt-tree-node-selected");
1666
+ });
1667
+ row.addEventListener("mouseenter", () => this.options.onHover(node));
1668
+ row.addEventListener("mouseleave", () => this.options.onHover(null));
1669
+ parent.appendChild(row);
1670
+ if (hasChildren) {
1671
+ const childContainer = document.createElement("div");
1672
+ childContainer.className = "inspekt-tree-children";
1673
+ for (const child of node.children) {
1674
+ this.renderNode(child, childContainer, depth + 1);
1675
+ }
1676
+ parent.appendChild(childContainer);
1677
+ }
1678
+ }
1679
+ matchesFilter(node) {
1680
+ if (node.name.toLowerCase().includes(this.filter)) return true;
1681
+ return node.children.some((child) => this.matchesFilter(child));
1682
+ }
1683
+ setupResize(handle) {
1684
+ let startX;
1685
+ let startWidth;
1686
+ const isRight = this.options.position === "right";
1687
+ const onMouseMove = (e) => {
1688
+ const dx = e.clientX - startX;
1689
+ const newWidth = isRight ? startWidth - dx : startWidth + dx;
1690
+ this.container.style.width = `${Math.max(200, Math.min(600, newWidth))}px`;
1691
+ };
1692
+ const onMouseUp = () => {
1693
+ document.removeEventListener("mousemove", onMouseMove);
1694
+ document.removeEventListener("mouseup", onMouseUp);
1695
+ this.container.style.userSelect = "";
1696
+ };
1697
+ handle.addEventListener("mousedown", (e) => {
1698
+ e.preventDefault();
1699
+ startX = e.clientX;
1700
+ startWidth = this.container.offsetWidth;
1701
+ this.container.style.userSelect = "none";
1702
+ document.addEventListener("mousemove", onMouseMove);
1703
+ document.addEventListener("mouseup", onMouseUp);
1704
+ });
1705
+ }
1706
+ destroy() {
1707
+ this.container.remove();
1708
+ }
1709
+ };
1710
+
1711
+ // src/adapters/react.ts
1712
+ var FIBER_KEY_PREFIX = "__reactFiber$";
1713
+ var INTERNAL_KEY_PREFIX = "__reactInternalInstance$";
1714
+ function getFiberFromElement(element) {
1715
+ for (const key of Object.keys(element)) {
1716
+ if (key.startsWith(FIBER_KEY_PREFIX) || key.startsWith(INTERNAL_KEY_PREFIX)) {
1717
+ return element[key];
1718
+ }
1719
+ }
1720
+ return null;
1721
+ }
1722
+ function isUserComponent(fiber) {
1723
+ const tag = fiber["tag"];
1724
+ return tag === 0 || tag === 1 || tag === 11 || tag === 14 || tag === 15;
1725
+ }
1726
+ function getComponentName(fiber) {
1727
+ const type = fiber["type"];
1728
+ if (!type) return "Unknown";
1729
+ if (typeof type === "function") {
1730
+ return type.displayName ?? type.name ?? "Anonymous";
1731
+ }
1732
+ if (typeof type === "object") {
1733
+ const render = type["render"];
1734
+ if (render) return render.displayName ?? render.name ?? "ForwardRef";
1735
+ const innerType = type["type"];
1736
+ if (innerType) return innerType.displayName ?? innerType.name ?? "Memo";
1737
+ }
1738
+ if (typeof type === "string") return type;
1739
+ return "Unknown";
1740
+ }
1741
+ function getProps(fiber) {
1742
+ const memoizedProps = fiber["memoizedProps"];
1743
+ if (!memoizedProps) return null;
1744
+ const cleaned = {};
1745
+ for (const [key, value] of Object.entries(memoizedProps)) {
1746
+ if (key === "children") continue;
1747
+ if (typeof value === "function") {
1748
+ cleaned[key] = `[Function: ${value.name || "anonymous"}]`;
1749
+ } else if (typeof value === "object" && value !== null) {
1750
+ cleaned[key] = "[Object]";
1751
+ } else {
1752
+ cleaned[key] = value;
1753
+ }
1754
+ }
1755
+ return Object.keys(cleaned).length > 0 ? cleaned : null;
1756
+ }
1757
+ function getSourceFromFiber(fiber) {
1758
+ const debugSource = fiber["_debugSource"];
1759
+ if (debugSource) {
1760
+ return {
1761
+ filePath: debugSource["fileName"] ?? "",
1762
+ line: debugSource["lineNumber"] ?? 0,
1763
+ column: debugSource["columnNumber"] ?? 0
1764
+ };
1765
+ }
1766
+ const stateNode = fiber["stateNode"];
1767
+ if (stateNode && stateNode instanceof HTMLElement) {
1768
+ const attr = findSourceAttribute(stateNode);
1769
+ if (attr) {
1770
+ const parsed2 = parseSourceAttribute(attr);
1771
+ if (parsed2) return { filePath: parsed2.filePath, line: parsed2.line, column: parsed2.column };
1772
+ }
1773
+ }
1774
+ return null;
1775
+ }
1776
+ function fiberToNode(fiber, depth) {
1777
+ if (!isUserComponent(fiber)) return null;
1778
+ const name = getComponentName(fiber);
1779
+ const source = getSourceFromFiber(fiber);
1780
+ const stateNode = fiber["stateNode"];
1781
+ const domElement = stateNode instanceof HTMLElement ? stateNode : findDomElement(fiber);
1782
+ const node = {
1783
+ name,
1784
+ filePath: source?.filePath ?? null,
1785
+ line: source?.line ?? null,
1786
+ column: source?.column ?? null,
1787
+ domElement,
1788
+ props: getProps(fiber),
1789
+ children: [],
1790
+ framework: "react",
1791
+ depth
1792
+ };
1793
+ let child = fiber["child"];
1794
+ while (child) {
1795
+ const childNode = fiberToNode(child, depth + 1);
1796
+ if (childNode) {
1797
+ node.children.push(childNode);
1798
+ } else {
1799
+ const grandchildren = collectUserChildren(child, depth + 1);
1800
+ node.children.push(...grandchildren);
1801
+ }
1802
+ child = child["sibling"];
1803
+ }
1804
+ return node;
1805
+ }
1806
+ function collectUserChildren(fiber, depth) {
1807
+ const result = [];
1808
+ let child = fiber["child"];
1809
+ while (child) {
1810
+ const node = fiberToNode(child, depth);
1811
+ if (node) {
1812
+ result.push(node);
1813
+ } else {
1814
+ result.push(...collectUserChildren(child, depth));
1815
+ }
1816
+ child = child["sibling"];
1817
+ }
1818
+ return result;
1819
+ }
1820
+ function findDomElement(fiber) {
1821
+ let current = fiber;
1822
+ while (current) {
1823
+ const stateNode = current["stateNode"];
1824
+ if (stateNode instanceof HTMLElement) return stateNode;
1825
+ current = current["child"];
1826
+ }
1827
+ return null;
1828
+ }
1829
+ function findRootFiber(container) {
1830
+ for (const key of Object.keys(container)) {
1831
+ if (key.startsWith("__reactContainer$")) {
1832
+ return container[key];
1833
+ }
1834
+ }
1835
+ const root = container["_reactRootContainer"];
1836
+ if (root) {
1837
+ const internalRoot = root["_internalRoot"];
1838
+ if (internalRoot) {
1839
+ return internalRoot["current"];
1840
+ }
1841
+ }
1842
+ return null;
1843
+ }
1844
+ var reactAdapter = {
1845
+ name: "react",
1846
+ detect() {
1847
+ const root = document.getElementById("root") ?? document.getElementById("app") ?? document.querySelector("[data-reactroot]");
1848
+ if (!root) return false;
1849
+ for (const key of Object.keys(root)) {
1850
+ if (key.startsWith("__reactContainer$") || key.startsWith(FIBER_KEY_PREFIX) || key.startsWith(INTERNAL_KEY_PREFIX)) {
1851
+ return true;
1852
+ }
1853
+ }
1854
+ return !!root["_reactRootContainer"];
1855
+ },
1856
+ getComponentTree(root) {
1857
+ const rootFiber = findRootFiber(root);
1858
+ if (!rootFiber) return null;
1859
+ const appFiber = rootFiber["child"];
1860
+ if (!appFiber) return null;
1861
+ return fiberToNode(appFiber, 0);
1862
+ },
1863
+ getComponentAtElement(element) {
1864
+ const fiber = getFiberFromElement(element);
1865
+ if (!fiber) return null;
1866
+ let current = fiber;
1867
+ while (current) {
1868
+ if (isUserComponent(current)) {
1869
+ return fiberToNode(current, 0);
1870
+ }
1871
+ current = current["return"];
1872
+ }
1873
+ return null;
1874
+ },
1875
+ getAncestors(element) {
1876
+ const fiber = getFiberFromElement(element);
1877
+ if (!fiber) return [];
1878
+ const ancestors = [];
1879
+ let current = fiber["return"];
1880
+ while (current) {
1881
+ if (isUserComponent(current)) {
1882
+ const node = fiberToNode(current, 0);
1883
+ if (node) {
1884
+ node.children = [];
1885
+ ancestors.push(node);
1886
+ }
1887
+ }
1888
+ current = current["return"];
1889
+ }
1890
+ return ancestors;
1891
+ }
1892
+ };
1893
+
1894
+ // src/adapters/vue.ts
1895
+ var VUE_KEY = "__vue__";
1896
+ var VUE3_KEY = "__vue_app__";
1897
+ var VUE_PARENT_KEY = "__vueParentComponent";
1898
+ function getVueInstance(element) {
1899
+ const vue3 = element[VUE_PARENT_KEY];
1900
+ if (vue3) return vue3;
1901
+ const vue2 = element[VUE_KEY];
1902
+ if (vue2) return vue2;
1903
+ return null;
1904
+ }
1905
+ function getComponentName2(instance) {
1906
+ if (instance.type) {
1907
+ return instance.type.name ?? instance.type.__name ?? "Anonymous";
1908
+ }
1909
+ if (instance.$options) {
1910
+ return instance.$options.name ?? instance.$options._componentTag ?? "Anonymous";
1911
+ }
1912
+ return "Unknown";
1913
+ }
1914
+ function getFilePath(instance) {
1915
+ if (instance.type?.__file) return instance.type.__file;
1916
+ if (instance.$options?.__file) return instance.$options.__file;
1917
+ return null;
1918
+ }
1919
+ function getElement(instance) {
1920
+ if (instance.subTree?.el instanceof HTMLElement) return instance.subTree.el;
1921
+ if (instance.$el instanceof HTMLElement) return instance.$el;
1922
+ return null;
1923
+ }
1924
+ function getProps2(instance) {
1925
+ const raw = instance.props ?? instance.$props;
1926
+ if (!raw || Object.keys(raw).length === 0) return null;
1927
+ const cleaned = {};
1928
+ for (const [key, value] of Object.entries(raw)) {
1929
+ if (typeof value === "function") {
1930
+ cleaned[key] = `[Function]`;
1931
+ } else if (typeof value === "object" && value !== null) {
1932
+ cleaned[key] = "[Object]";
1933
+ } else {
1934
+ cleaned[key] = value;
1935
+ }
1936
+ }
1937
+ return cleaned;
1938
+ }
1939
+ function instanceToNode(instance, depth) {
1940
+ const name = getComponentName2(instance);
1941
+ const file = getFilePath(instance);
1942
+ const el = getElement(instance);
1943
+ let filePath = file;
1944
+ let line = null;
1945
+ if (el) {
1946
+ const attr = findSourceAttribute(el);
1947
+ if (attr) {
1948
+ const source = parseSourceAttribute(attr);
1949
+ if (source) {
1950
+ filePath = source.filePath;
1951
+ line = source.line;
1952
+ }
1953
+ }
1954
+ }
1955
+ return {
1956
+ name,
1957
+ filePath,
1958
+ line,
1959
+ column: null,
1960
+ domElement: el,
1961
+ props: getProps2(instance),
1962
+ children: [],
1963
+ framework: "vue",
1964
+ depth
1965
+ };
1966
+ }
1967
+ var vueAdapter = {
1968
+ name: "vue",
1969
+ detect() {
1970
+ const root = document.getElementById("app") ?? document.getElementById("root");
1971
+ if (!root) return false;
1972
+ return !!(root[VUE3_KEY] || root[VUE_KEY]);
1973
+ },
1974
+ getComponentTree(root) {
1975
+ const rootNode = {
1976
+ name: "App",
1977
+ filePath: null,
1978
+ line: null,
1979
+ column: null,
1980
+ domElement: root,
1981
+ props: null,
1982
+ children: [],
1983
+ framework: "vue",
1984
+ depth: 0
1985
+ };
1986
+ function walkDOM(el, parent, depth) {
1987
+ for (const child of Array.from(el.children)) {
1988
+ if (!(child instanceof HTMLElement)) continue;
1989
+ const instance = getVueInstance(child);
1990
+ if (instance) {
1991
+ const node = instanceToNode(instance, depth);
1992
+ parent.children.push(node);
1993
+ walkDOM(child, node, depth + 1);
1994
+ } else {
1995
+ walkDOM(child, parent, depth);
1996
+ }
1997
+ }
1998
+ }
1999
+ walkDOM(root, rootNode, 1);
2000
+ return rootNode.children.length > 0 ? rootNode : null;
2001
+ },
2002
+ getComponentAtElement(element) {
2003
+ const instance = getVueInstance(element);
2004
+ if (!instance) return null;
2005
+ return instanceToNode(instance, 0);
2006
+ },
2007
+ getAncestors(element) {
2008
+ const ancestors = [];
2009
+ let instance = getVueInstance(element);
2010
+ while (instance) {
2011
+ const parent = instance.parent ?? instance.$parent;
2012
+ if (parent) {
2013
+ ancestors.push(instanceToNode(parent, 0));
2014
+ }
2015
+ instance = parent ?? null;
2016
+ }
2017
+ return ancestors;
2018
+ }
2019
+ };
2020
+
2021
+ // src/adapters/svelte.ts
2022
+ var SVELTE_CONTEXT_KEY = "__svelte_meta";
2023
+ function getSvelteContext(element) {
2024
+ return element[SVELTE_CONTEXT_KEY];
2025
+ }
2026
+ function getComponentNameFromFile(file) {
2027
+ const parts = file.split("/");
2028
+ const filename = parts[parts.length - 1] ?? "";
2029
+ return filename.replace(/\.svelte$/, "");
2030
+ }
2031
+ var svelteAdapter = {
2032
+ name: "svelte",
2033
+ detect() {
2034
+ return document.querySelector(`[class*="svelte-"]`) !== null || document.querySelector("[data-svelte-h]") !== null;
2035
+ },
2036
+ getComponentTree(root) {
2037
+ const rootNode = {
2038
+ name: "App",
2039
+ filePath: null,
2040
+ line: null,
2041
+ column: null,
2042
+ domElement: root,
2043
+ props: null,
2044
+ children: [],
2045
+ framework: "svelte",
2046
+ depth: 0
2047
+ };
2048
+ function walkDOM(el, parent, depth) {
2049
+ for (const child of Array.from(el.children)) {
2050
+ if (!(child instanceof HTMLElement)) continue;
2051
+ const attr = findSourceAttribute(child);
2052
+ if (attr) {
2053
+ const source = parseSourceAttribute(attr);
2054
+ if (source) {
2055
+ const node = {
2056
+ name: source.componentName,
2057
+ filePath: source.filePath,
2058
+ line: source.line,
2059
+ column: source.column,
2060
+ domElement: child,
2061
+ props: null,
2062
+ children: [],
2063
+ framework: "svelte",
2064
+ depth
2065
+ };
2066
+ parent.children.push(node);
2067
+ walkDOM(child, node, depth + 1);
2068
+ continue;
2069
+ }
2070
+ }
2071
+ const ctx = getSvelteContext(child);
2072
+ if (ctx?.loc?.file) {
2073
+ const node = {
2074
+ name: getComponentNameFromFile(ctx.loc.file),
2075
+ filePath: ctx.loc.file,
2076
+ line: ctx.loc.line ?? null,
2077
+ column: ctx.loc.column ?? null,
2078
+ domElement: child,
2079
+ props: null,
2080
+ children: [],
2081
+ framework: "svelte",
2082
+ depth
2083
+ };
2084
+ parent.children.push(node);
2085
+ walkDOM(child, node, depth + 1);
2086
+ continue;
2087
+ }
2088
+ walkDOM(child, parent, depth);
2089
+ }
2090
+ }
2091
+ walkDOM(root, rootNode, 1);
2092
+ return rootNode.children.length > 0 ? rootNode : null;
2093
+ },
2094
+ getComponentAtElement(element) {
2095
+ const attr = findSourceAttribute(element);
2096
+ if (attr) {
2097
+ const source = parseSourceAttribute(attr);
2098
+ if (source) {
2099
+ return {
2100
+ name: source.componentName,
2101
+ filePath: source.filePath,
2102
+ line: source.line,
2103
+ column: source.column,
2104
+ domElement: element,
2105
+ props: null,
2106
+ children: [],
2107
+ framework: "svelte",
2108
+ depth: 0
2109
+ };
2110
+ }
2111
+ }
2112
+ const ctx = getSvelteContext(element);
2113
+ if (ctx?.loc?.file) {
2114
+ return {
2115
+ name: getComponentNameFromFile(ctx.loc.file),
2116
+ filePath: ctx.loc.file,
2117
+ line: ctx.loc.line ?? null,
2118
+ column: ctx.loc.column ?? null,
2119
+ domElement: element,
2120
+ props: null,
2121
+ children: [],
2122
+ framework: "svelte",
2123
+ depth: 0
2124
+ };
2125
+ }
2126
+ return null;
2127
+ },
2128
+ getAncestors(element) {
2129
+ const ancestors = [];
2130
+ let current = element.parentElement;
2131
+ while (current) {
2132
+ const node = this.getComponentAtElement(current);
2133
+ if (node) ancestors.push(node);
2134
+ current = current.parentElement;
2135
+ }
2136
+ return ancestors;
2137
+ }
2138
+ };
2139
+
2140
+ // src/adapters/solid.ts
2141
+ var SOLID_DEV_KEY = "_$owner";
2142
+ function getSolidOwner(element) {
2143
+ return element[SOLID_DEV_KEY];
2144
+ }
2145
+ var solidAdapter = {
2146
+ name: "solid",
2147
+ detect() {
2148
+ return document.querySelector("[data-hk]") !== null || !!document["_$HY"];
2149
+ },
2150
+ getComponentTree(root) {
2151
+ const rootNode = {
2152
+ name: "App",
2153
+ filePath: null,
2154
+ line: null,
2155
+ column: null,
2156
+ domElement: root,
2157
+ props: null,
2158
+ children: [],
2159
+ framework: "solid",
2160
+ depth: 0
2161
+ };
2162
+ function walkDOM(el, parent, depth) {
2163
+ for (const child of Array.from(el.children)) {
2164
+ if (!(child instanceof HTMLElement)) continue;
2165
+ const attr = findSourceAttribute(child);
2166
+ if (attr) {
2167
+ const source = parseSourceAttribute(attr);
2168
+ if (source) {
2169
+ const node = {
2170
+ name: source.componentName,
2171
+ filePath: source.filePath,
2172
+ line: source.line,
2173
+ column: source.column,
2174
+ domElement: child,
2175
+ props: null,
2176
+ children: [],
2177
+ framework: "solid",
2178
+ depth
2179
+ };
2180
+ parent.children.push(node);
2181
+ walkDOM(child, node, depth + 1);
2182
+ continue;
2183
+ }
2184
+ }
2185
+ const owner = getSolidOwner(child);
2186
+ if (owner?.componentName || owner?.name) {
2187
+ const node = {
2188
+ name: owner.componentName ?? owner.name ?? "Component",
2189
+ filePath: owner.sourceMap?.file ?? null,
2190
+ line: owner.sourceMap?.line ?? null,
2191
+ column: owner.sourceMap?.column ?? null,
2192
+ domElement: child,
2193
+ props: null,
2194
+ children: [],
2195
+ framework: "solid",
2196
+ depth
2197
+ };
2198
+ parent.children.push(node);
2199
+ walkDOM(child, node, depth + 1);
2200
+ continue;
2201
+ }
2202
+ walkDOM(child, parent, depth);
2203
+ }
2204
+ }
2205
+ walkDOM(root, rootNode, 1);
2206
+ return rootNode.children.length > 0 ? rootNode : null;
2207
+ },
2208
+ getComponentAtElement(element) {
2209
+ const attr = findSourceAttribute(element);
2210
+ if (attr) {
2211
+ const source = parseSourceAttribute(attr);
2212
+ if (source) {
2213
+ return {
2214
+ name: source.componentName,
2215
+ filePath: source.filePath,
2216
+ line: source.line,
2217
+ column: source.column,
2218
+ domElement: element,
2219
+ props: null,
2220
+ children: [],
2221
+ framework: "solid",
2222
+ depth: 0
2223
+ };
2224
+ }
2225
+ }
2226
+ const owner = getSolidOwner(element);
2227
+ if (owner?.componentName || owner?.name) {
2228
+ return {
2229
+ name: owner.componentName ?? owner.name ?? "Component",
2230
+ filePath: owner.sourceMap?.file ?? null,
2231
+ line: owner.sourceMap?.line ?? null,
2232
+ column: owner.sourceMap?.column ?? null,
2233
+ domElement: element,
2234
+ props: null,
2235
+ children: [],
2236
+ framework: "solid",
2237
+ depth: 0
2238
+ };
2239
+ }
2240
+ return null;
2241
+ },
2242
+ getAncestors(element) {
2243
+ const ancestors = [];
2244
+ let current = element.parentElement;
2245
+ while (current) {
2246
+ const node = this.getComponentAtElement(current);
2247
+ if (node) ancestors.push(node);
2248
+ current = current.parentElement;
2249
+ }
2250
+ return ancestors;
2251
+ }
2252
+ };
2253
+
2254
+ // src/adapters/generic.ts
2255
+ function buildTreeFromDOM(element, depth) {
2256
+ const nodes = [];
2257
+ for (const child of Array.from(element.children)) {
2258
+ if (!(child instanceof HTMLElement)) continue;
2259
+ if (child.tagName === "INSPEKT-ROOT") continue;
2260
+ if (child.tagName === "SCRIPT" || child.tagName === "STYLE") continue;
2261
+ const attr = findSourceAttribute(child);
2262
+ if (attr) {
2263
+ const source = parseSourceAttribute(attr);
2264
+ if (source) {
2265
+ const node = {
2266
+ name: source.componentName,
2267
+ filePath: source.filePath,
2268
+ line: source.line,
2269
+ column: source.column,
2270
+ domElement: child,
2271
+ props: null,
2272
+ children: buildTreeFromDOM(child, depth + 1),
2273
+ framework: "unknown",
2274
+ depth
2275
+ };
2276
+ nodes.push(node);
2277
+ continue;
2278
+ }
2279
+ }
2280
+ nodes.push(...buildTreeFromDOM(child, depth));
2281
+ }
2282
+ return nodes;
2283
+ }
2284
+ var genericAdapter = {
2285
+ name: "generic",
2286
+ detect() {
2287
+ return document.querySelector("[data-insp-path], [data-insp-path]") !== null;
2288
+ },
2289
+ getComponentTree(root) {
2290
+ const children = buildTreeFromDOM(root, 1);
2291
+ if (children.length === 0) return null;
2292
+ return {
2293
+ name: "Root",
2294
+ filePath: null,
2295
+ line: null,
2296
+ column: null,
2297
+ domElement: root,
2298
+ props: null,
2299
+ children,
2300
+ framework: "unknown",
2301
+ depth: 0
2302
+ };
2303
+ },
2304
+ getComponentAtElement(element) {
2305
+ const attr = findSourceAttribute(element);
2306
+ if (!attr) return null;
2307
+ const source = parseSourceAttribute(attr);
2308
+ if (!source) return null;
2309
+ return {
2310
+ name: source.componentName,
2311
+ filePath: source.filePath,
2312
+ line: source.line,
2313
+ column: source.column,
2314
+ domElement: element,
2315
+ props: null,
2316
+ children: buildTreeFromDOM(element, 1),
2317
+ framework: "unknown",
2318
+ depth: 0
2319
+ };
2320
+ },
2321
+ getAncestors(element) {
2322
+ const ancestors = [];
2323
+ let current = element.parentElement;
2324
+ while (current) {
2325
+ const attr = findSourceAttribute(current);
2326
+ if (attr) {
2327
+ const source = parseSourceAttribute(attr);
2328
+ if (source) {
2329
+ ancestors.push({
2330
+ name: source.componentName,
2331
+ filePath: source.filePath,
2332
+ line: source.line,
2333
+ column: source.column,
2334
+ domElement: current,
2335
+ props: null,
2336
+ children: [],
2337
+ framework: "unknown",
2338
+ depth: 0
2339
+ });
2340
+ }
2341
+ }
2342
+ current = current.parentElement;
2343
+ }
2344
+ return ancestors;
2345
+ }
2346
+ };
2347
+
2348
+ // src/adapters/index.ts
2349
+ var adapters = [
2350
+ reactAdapter,
2351
+ vueAdapter,
2352
+ svelteAdapter,
2353
+ solidAdapter
2354
+ ];
2355
+ function detectAdapter() {
2356
+ for (const adapter of adapters) {
2357
+ if (adapter.detect()) return adapter;
2358
+ }
2359
+ return genericAdapter;
2360
+ }
2361
+
2362
+ // src/styles.ts
2363
+ var STYLES = (
2364
+ /* css */
2365
+ `
2366
+ :host {
2367
+ all: initial;
2368
+ position: fixed;
2369
+ top: 0;
2370
+ left: 0;
2371
+ width: 0;
2372
+ height: 0;
2373
+ z-index: 2147483647;
2374
+ pointer-events: none;
2375
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2376
+ font-size: 13px;
2377
+ line-height: 1.4;
2378
+ }
2379
+
2380
+ *, *::before, *::after {
2381
+ box-sizing: border-box;
2382
+ margin: 0;
2383
+ padding: 0;
2384
+ }
2385
+
2386
+ /* Theme variables */
2387
+ :host {
2388
+ --dl-bg: #1e1e2e;
2389
+ --dl-bg-hover: #313244;
2390
+ --dl-text: #cdd6f4;
2391
+ --dl-text-dim: #a6adc8;
2392
+ --dl-border: #45475a;
2393
+ --dl-accent: #3b82f6;
2394
+ --dl-accent-dim: #3b82f640;
2395
+ --dl-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
2396
+ --dl-radius: 8px;
2397
+ }
2398
+
2399
+ :host(.inspekt-light) {
2400
+ --dl-bg: #ffffff;
2401
+ --dl-bg-hover: #f1f5f9;
2402
+ --dl-text: #1e293b;
2403
+ --dl-text-dim: #64748b;
2404
+ --dl-border: #e2e8f0;
2405
+ --dl-accent: #3b82f6;
2406
+ --dl-accent-dim: #3b82f620;
2407
+ --dl-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
2408
+ }
2409
+
2410
+ /* Popover */
2411
+ .inspekt-popover {
2412
+ position: fixed;
2413
+ background: var(--dl-bg);
2414
+ border: 1px solid var(--dl-border);
2415
+ border-radius: var(--dl-radius);
2416
+ box-shadow: var(--dl-shadow);
2417
+ color: var(--dl-text);
2418
+ min-width: 200px;
2419
+ max-width: 400px;
2420
+ overflow: hidden;
2421
+ pointer-events: auto;
2422
+ z-index: 2147483647;
2423
+ animation: inspekt-fade-in 0.15s ease;
2424
+ }
2425
+
2426
+ @keyframes inspekt-fade-in {
2427
+ from { opacity: 0; transform: translateY(4px); }
2428
+ to { opacity: 1; transform: translateY(0); }
2429
+ }
2430
+
2431
+ .inspekt-popover-header {
2432
+ padding: 8px 12px;
2433
+ border-bottom: 1px solid var(--dl-border);
2434
+ }
2435
+
2436
+ .inspekt-popover-path-dir {
2437
+ font-size: 11px;
2438
+ color: var(--dl-text-dim);
2439
+ overflow: hidden;
2440
+ text-overflow: ellipsis;
2441
+ white-space: nowrap;
2442
+ }
2443
+
2444
+ .inspekt-popover-path-file {
2445
+ font-size: 13px;
2446
+ font-weight: 600;
2447
+ color: var(--dl-text);
2448
+ }
2449
+
2450
+ .inspekt-popover-actions {
2451
+ display: flex;
2452
+ gap: 2px;
2453
+ padding: 6px 8px;
2454
+ }
2455
+
2456
+ /* Snippet section */
2457
+ .inspekt-snippet {
2458
+ border-bottom: 1px solid var(--dl-border);
2459
+ }
2460
+
2461
+ .inspekt-snippet-toggle {
2462
+ display: flex;
2463
+ align-items: center;
2464
+ width: 100%;
2465
+ padding: 6px 12px;
2466
+ background: transparent;
2467
+ border: none;
2468
+ color: var(--dl-text-dim);
2469
+ font-size: 11px;
2470
+ font-weight: 500;
2471
+ cursor: pointer;
2472
+ font-family: inherit;
2473
+ text-align: left;
2474
+ pointer-events: auto;
2475
+ }
2476
+
2477
+ .inspekt-snippet-toggle:hover {
2478
+ background: var(--dl-bg-hover);
2479
+ color: var(--dl-text);
2480
+ }
2481
+
2482
+ .inspekt-snippet-body {
2483
+ padding: 0;
2484
+ max-width: 600px;
2485
+ max-height: 240px;
2486
+ overflow: auto;
2487
+ border-top: 1px solid var(--dl-border);
2488
+ }
2489
+
2490
+ .inspekt-snippet-pre {
2491
+ margin: 0;
2492
+ padding: 8px 0;
2493
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Consolas', monospace;
2494
+ font-size: 12px;
2495
+ line-height: 1.5;
2496
+ color: var(--dl-text);
2497
+ background: transparent;
2498
+ white-space: pre;
2499
+ tab-size: 2;
2500
+ }
2501
+
2502
+ .inspekt-line {
2503
+ display: inline-flex;
2504
+ align-items: baseline;
2505
+ padding: 0 12px 0 0;
2506
+ }
2507
+
2508
+ .inspekt-line-target {
2509
+ background: var(--dl-accent-dim);
2510
+ border-left: 2px solid var(--dl-accent);
2511
+ padding-left: 10px;
2512
+ }
2513
+
2514
+ .inspekt-line:not(.inspekt-line-target) {
2515
+ padding-left: 12px;
2516
+ }
2517
+
2518
+ .inspekt-line-no {
2519
+ display: inline-block;
2520
+ min-width: 30px;
2521
+ text-align: right;
2522
+ color: var(--dl-text-dim);
2523
+ margin-right: 10px;
2524
+ user-select: none;
2525
+ flex-shrink: 0;
2526
+ }
2527
+
2528
+ .inspekt-line-code {
2529
+ white-space: pre;
2530
+ }
2531
+
2532
+ .inspekt-snippet-skeleton,
2533
+ .inspekt-snippet-empty {
2534
+ padding: 12px;
2535
+ font-size: 11px;
2536
+ color: var(--dl-text-dim);
2537
+ text-align: center;
2538
+ font-style: italic;
2539
+ }
2540
+
2541
+ .inspekt-popover-action {
2542
+ display: flex;
2543
+ align-items: center;
2544
+ justify-content: center;
2545
+ width: 32px;
2546
+ height: 32px;
2547
+ border: none;
2548
+ border-radius: 6px;
2549
+ background: transparent;
2550
+ color: var(--dl-text-dim);
2551
+ cursor: pointer;
2552
+ transition: all 0.15s ease;
2553
+ pointer-events: auto;
2554
+ }
2555
+
2556
+ .inspekt-popover-action:hover {
2557
+ background: var(--dl-bg-hover);
2558
+ color: var(--dl-text);
2559
+ }
2560
+
2561
+ .inspekt-popover-action:active {
2562
+ transform: scale(0.92);
2563
+ }
2564
+
2565
+ .inspekt-popover-action svg {
2566
+ width: 16px;
2567
+ height: 16px;
2568
+ }
2569
+
2570
+ /* Popover toast \u2014 brief feedback after Copy / console.log actions */
2571
+ .inspekt-popover-toast {
2572
+ position: fixed;
2573
+ bottom: 24px;
2574
+ left: 50%;
2575
+ transform: translateX(-50%);
2576
+ padding: 6px 14px;
2577
+ background: var(--dl-text);
2578
+ color: var(--dl-bg);
2579
+ font: 500 12px/1.4 inherit;
2580
+ border-radius: 999px;
2581
+ box-shadow: var(--dl-shadow);
2582
+ pointer-events: none;
2583
+ z-index: 2147483647;
2584
+ animation: inspekt-toast 1.2s ease;
2585
+ }
2586
+ @keyframes inspekt-toast {
2587
+ 0% { opacity: 0; transform: translate(-50%, 6px); }
2588
+ 15%, 80% { opacity: 1; transform: translate(-50%, 0); }
2589
+ 100% { opacity: 0; transform: translate(-50%, 0); }
2590
+ }
2591
+
2592
+ /* Info badge \u2014 small pill rendered in the DOM-fallback popover header.
2593
+ Shows an info icon + "No source" label so users see it without hover. */
2594
+ .inspekt-info-badge {
2595
+ display: inline-flex;
2596
+ align-items: center;
2597
+ gap: 4px;
2598
+ padding: 2px 8px 2px 6px;
2599
+ border-radius: 999px;
2600
+ background: var(--dl-bg-hover);
2601
+ color: var(--dl-text-dim);
2602
+ font-size: 10.5px;
2603
+ font-weight: 500;
2604
+ letter-spacing: 0.01em;
2605
+ line-height: 1.4;
2606
+ cursor: help;
2607
+ flex-shrink: 0;
2608
+ margin-left: 8px;
2609
+ border: 1px solid var(--dl-border);
2610
+ text-transform: none;
2611
+ }
2612
+ .inspekt-info-badge:hover,
2613
+ .inspekt-info-badge:focus-visible {
2614
+ color: var(--dl-text);
2615
+ border-color: var(--dl-accent);
2616
+ outline: none;
2617
+ }
2618
+ .inspekt-info-badge svg { display: block; }
2619
+ .inspekt-info-badge-label {
2620
+ white-space: nowrap;
2621
+ }
2622
+ .inspekt-popover-path-dir-row {
2623
+ display: flex;
2624
+ align-items: center;
2625
+ gap: 4px;
2626
+ }
2627
+
2628
+ /* Tooltip (replaces native title="") */
2629
+ .inspekt-tooltip {
2630
+ position: fixed;
2631
+ max-width: 280px;
2632
+ padding: 6px 10px;
2633
+ background: var(--dl-bg);
2634
+ color: var(--dl-text);
2635
+ border: 1px solid var(--dl-border);
2636
+ border-radius: 6px;
2637
+ box-shadow: var(--dl-shadow);
2638
+ font-size: 12px;
2639
+ line-height: 1.4;
2640
+ pointer-events: none;
2641
+ z-index: 2147483647;
2642
+ opacity: 0;
2643
+ animation: inspekt-tt-in 120ms ease-out forwards;
2644
+ }
2645
+ .inspekt-tooltip--rich {
2646
+ pointer-events: auto;
2647
+ max-width: 320px;
2648
+ padding: 10px 12px;
2649
+ }
2650
+ @keyframes inspekt-tt-in {
2651
+ from { opacity: 0; transform: translateY(2px); }
2652
+ to { opacity: 1; transform: translateY(0); }
2653
+ }
2654
+
2655
+ /* Wider source snippet body when accordion is expanded (1.5\xD7 collapsed) */
2656
+ .inspekt-snippet-body[data-state='expanded'] {
2657
+ max-width: 900px;
2658
+ }
2659
+
2660
+ /* Prism syntax tokens \u2014 neutral palette mapped to theme variables. */
2661
+ .inspekt-snippet-pre .token.comment,
2662
+ .inspekt-snippet-pre .token.prolog,
2663
+ .inspekt-snippet-pre .token.doctype,
2664
+ .inspekt-snippet-pre .token.cdata { color: var(--dl-text-dim); font-style: italic; }
2665
+ .inspekt-snippet-pre .token.punctuation { color: var(--dl-text-dim); }
2666
+ .inspekt-snippet-pre .token.namespace { opacity: 0.7; }
2667
+ .inspekt-snippet-pre .token.property,
2668
+ .inspekt-snippet-pre .token.tag,
2669
+ .inspekt-snippet-pre .token.constant,
2670
+ .inspekt-snippet-pre .token.symbol,
2671
+ .inspekt-snippet-pre .token.deleted { color: #e06c75; }
2672
+ .inspekt-snippet-pre .token.boolean,
2673
+ .inspekt-snippet-pre .token.number { color: #d19a66; }
2674
+ .inspekt-snippet-pre .token.selector,
2675
+ .inspekt-snippet-pre .token.attr-name,
2676
+ .inspekt-snippet-pre .token.string,
2677
+ .inspekt-snippet-pre .token.char,
2678
+ .inspekt-snippet-pre .token.builtin,
2679
+ .inspekt-snippet-pre .token.inserted { color: #98c379; }
2680
+ .inspekt-snippet-pre .token.operator,
2681
+ .inspekt-snippet-pre .token.entity,
2682
+ .inspekt-snippet-pre .token.url,
2683
+ .inspekt-snippet-pre .language-css .token.string,
2684
+ .inspekt-snippet-pre .style .token.string { color: #56b6c2; }
2685
+ .inspekt-snippet-pre .token.atrule,
2686
+ .inspekt-snippet-pre .token.attr-value,
2687
+ .inspekt-snippet-pre .token.keyword { color: #c678dd; }
2688
+ .inspekt-snippet-pre .token.function,
2689
+ .inspekt-snippet-pre .token.class-name { color: #61afef; }
2690
+ .inspekt-snippet-pre .token.regex,
2691
+ .inspekt-snippet-pre .token.important,
2692
+ .inspekt-snippet-pre .token.variable { color: #d19a66; }
2693
+ .inspekt-snippet-pre .token.important,
2694
+ .inspekt-snippet-pre .token.bold { font-weight: bold; }
2695
+ .inspekt-snippet-pre .token.italic { font-style: italic; }
2696
+
2697
+ /* Overlay badges */
2698
+ .inspekt-overlay {
2699
+ position: absolute;
2700
+ top: 0;
2701
+ left: 0;
2702
+ pointer-events: none;
2703
+ }
2704
+
2705
+ .inspekt-badge {
2706
+ position: absolute;
2707
+ background: var(--dl-accent);
2708
+ color: #fff;
2709
+ font-size: 10px;
2710
+ font-weight: 600;
2711
+ padding: 1px 6px;
2712
+ border-radius: 4px;
2713
+ white-space: nowrap;
2714
+ pointer-events: auto;
2715
+ cursor: pointer;
2716
+ opacity: 0.9;
2717
+ transition: opacity 0.15s ease;
2718
+ z-index: 2147483646;
2719
+ }
2720
+
2721
+ .inspekt-badge:hover {
2722
+ opacity: 1;
2723
+ }
2724
+
2725
+ /* Pulse animation for selected highlight */
2726
+ @keyframes inspekt-pulse {
2727
+ 0%, 100% { box-shadow: 0 0 0 0 var(--dl-accent-dim); }
2728
+ 50% { box-shadow: 0 0 8px 4px var(--dl-accent-dim); }
2729
+ }
2730
+
2731
+ /* Tree Panel */
2732
+ .inspekt-tree-panel {
2733
+ position: fixed;
2734
+ top: 0;
2735
+ height: 100vh;
2736
+ width: 320px;
2737
+ background: var(--dl-bg);
2738
+ border-left: 1px solid var(--dl-border);
2739
+ color: var(--dl-text);
2740
+ display: flex;
2741
+ flex-direction: column;
2742
+ pointer-events: auto;
2743
+ z-index: 2147483647;
2744
+ box-shadow: var(--dl-shadow);
2745
+ animation: inspekt-slide-in 0.2s ease;
2746
+ }
2747
+
2748
+ .inspekt-tree-right { right: 0; border-left: 1px solid var(--dl-border); border-right: none; }
2749
+ .inspekt-tree-left { left: 0; border-right: 1px solid var(--dl-border); border-left: none; }
2750
+
2751
+ @keyframes inspekt-slide-in {
2752
+ from { opacity: 0; transform: translateX(20px); }
2753
+ to { opacity: 1; transform: translateX(0); }
2754
+ }
2755
+
2756
+ .inspekt-tree-header {
2757
+ display: flex;
2758
+ align-items: center;
2759
+ justify-content: space-between;
2760
+ padding: 10px 12px;
2761
+ border-bottom: 1px solid var(--dl-border);
2762
+ flex-shrink: 0;
2763
+ }
2764
+
2765
+ .inspekt-tree-title {
2766
+ font-weight: 600;
2767
+ font-size: 13px;
2768
+ }
2769
+
2770
+ .inspekt-tree-close {
2771
+ display: flex;
2772
+ align-items: center;
2773
+ justify-content: center;
2774
+ width: 24px;
2775
+ height: 24px;
2776
+ border: none;
2777
+ border-radius: 4px;
2778
+ background: transparent;
2779
+ color: var(--dl-text-dim);
2780
+ cursor: pointer;
2781
+ }
2782
+
2783
+ .inspekt-tree-close:hover {
2784
+ background: var(--dl-bg-hover);
2785
+ color: var(--dl-text);
2786
+ }
2787
+
2788
+ .inspekt-tree-search {
2789
+ padding: 6px 12px;
2790
+ border-bottom: 1px solid var(--dl-border);
2791
+ flex-shrink: 0;
2792
+ }
2793
+
2794
+ .inspekt-tree-search-input {
2795
+ width: 100%;
2796
+ padding: 5px 8px;
2797
+ border: 1px solid var(--dl-border);
2798
+ border-radius: 6px;
2799
+ background: var(--dl-bg-hover);
2800
+ color: var(--dl-text);
2801
+ font-size: 12px;
2802
+ outline: none;
2803
+ font-family: inherit;
2804
+ }
2805
+
2806
+ .inspekt-tree-search-input:focus {
2807
+ border-color: var(--dl-accent);
2808
+ }
2809
+
2810
+ .inspekt-tree-search-input::placeholder {
2811
+ color: var(--dl-text-dim);
2812
+ }
2813
+
2814
+ .inspekt-tree-content {
2815
+ flex: 1;
2816
+ overflow-y: auto;
2817
+ padding: 4px 0;
2818
+ scrollbar-width: thin;
2819
+ scrollbar-color: var(--dl-border) transparent;
2820
+ }
2821
+
2822
+ .inspekt-tree-content::-webkit-scrollbar { width: 6px; }
2823
+ .inspekt-tree-content::-webkit-scrollbar-thumb { background: var(--dl-border); border-radius: 3px; }
2824
+
2825
+ .inspekt-tree-node {
2826
+ display: flex;
2827
+ align-items: center;
2828
+ gap: 4px;
2829
+ padding: 3px 8px;
2830
+ cursor: pointer;
2831
+ min-height: 26px;
2832
+ transition: background 0.1s ease;
2833
+ }
2834
+
2835
+ .inspekt-tree-node:hover {
2836
+ background: var(--dl-bg-hover);
2837
+ }
2838
+
2839
+ .inspekt-tree-node-selected {
2840
+ background: var(--dl-accent-dim) !important;
2841
+ }
2842
+
2843
+ .inspekt-tree-arrow {
2844
+ display: flex;
2845
+ align-items: center;
2846
+ justify-content: center;
2847
+ width: 14px;
2848
+ height: 14px;
2849
+ flex-shrink: 0;
2850
+ transition: transform 0.15s ease;
2851
+ }
2852
+
2853
+ .inspekt-tree-arrow svg { color: var(--dl-text-dim); }
2854
+
2855
+ .inspekt-tree-arrow-expanded { transform: rotate(0deg); }
2856
+ .inspekt-tree-arrow-collapsed { transform: rotate(-90deg); }
2857
+
2858
+ .inspekt-tree-name {
2859
+ font-size: 12px;
2860
+ font-weight: 500;
2861
+ color: #c792ea;
2862
+ }
2863
+
2864
+ :host(.inspekt-light) .inspekt-tree-name {
2865
+ color: #7c3aed;
2866
+ }
2867
+
2868
+ .inspekt-tree-line {
2869
+ font-size: 11px;
2870
+ color: var(--dl-text-dim);
2871
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Consolas', monospace;
2872
+ }
2873
+
2874
+ .inspekt-tree-props {
2875
+ font-size: 10px;
2876
+ color: var(--dl-text-dim);
2877
+ overflow: hidden;
2878
+ text-overflow: ellipsis;
2879
+ white-space: nowrap;
2880
+ max-width: 120px;
2881
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Consolas', monospace;
2882
+ }
2883
+
2884
+ .inspekt-tree-children.inspekt-tree-collapsed {
2885
+ display: none;
2886
+ }
2887
+
2888
+ .inspekt-tree-empty {
2889
+ padding: 16px;
2890
+ text-align: center;
2891
+ color: var(--dl-text-dim);
2892
+ font-size: 12px;
2893
+ }
2894
+
2895
+ /* Resize handle */
2896
+ .inspekt-tree-resize {
2897
+ position: absolute;
2898
+ top: 0;
2899
+ width: 4px;
2900
+ height: 100%;
2901
+ cursor: col-resize;
2902
+ }
2903
+
2904
+ .inspekt-tree-resize-left { left: 0; }
2905
+ .inspekt-tree-resize-right { right: 0; }
2906
+
2907
+ .inspekt-tree-resize:hover {
2908
+ background: var(--dl-accent);
2909
+ opacity: 0.5;
2910
+ }
2911
+
2912
+ /* Bounding-box overlay \u2014 view mode & "Show bounding boxes" toggle */
2913
+ .inspekt-bbox-layer {
2914
+ position: fixed;
2915
+ inset: 0;
2916
+ pointer-events: none;
2917
+ z-index: 2147483646;
2918
+ }
2919
+ .inspekt-bbox {
2920
+ position: fixed;
2921
+ top: 0;
2922
+ left: 0;
2923
+ box-sizing: border-box;
2924
+ border: 1.5px dashed var(--dl-accent, oklch(0.62 0.18 285));
2925
+ background: oklch(0.62 0.18 285 / 0.05);
2926
+ pointer-events: none;
2927
+ will-change: transform, width, height;
2928
+ border-radius: 2px;
2929
+ }
2930
+ .inspekt-bbox-label {
2931
+ position: absolute;
2932
+ top: -18px;
2933
+ left: 0;
2934
+ font: 500 10px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
2935
+ color: #fff;
2936
+ background: var(--dl-accent, oklch(0.55 0.18 285));
2937
+ padding: 2px 5px;
2938
+ border-radius: 3px 3px 3px 0;
2939
+ white-space: nowrap;
2940
+ pointer-events: none;
2941
+ max-width: 280px;
2942
+ overflow: hidden;
2943
+ text-overflow: ellipsis;
2944
+ }
2945
+ `
2946
+ );
2947
+
2948
+ // src/components/rich-select.ts
2949
+ var import_dom2 = require("@floating-ui/dom");
2950
+ function createRichSelect(opts) {
2951
+ let current = opts.value;
2952
+ let listEl = null;
2953
+ let activeIndex = -1;
2954
+ let cleanupAutoUpdate = null;
2955
+ const tooltipDetachers = [];
2956
+ const trigger = document.createElement("button");
2957
+ trigger.type = "button";
2958
+ trigger.className = "inspekt-rich-select-trigger";
2959
+ trigger.setAttribute("aria-haspopup", "listbox");
2960
+ trigger.setAttribute("aria-expanded", "false");
2961
+ if (opts.ariaLabel) trigger.setAttribute("aria-label", opts.ariaLabel);
2962
+ opts.anchor.replaceChildren(trigger);
2963
+ renderTrigger();
2964
+ trigger.addEventListener("click", (e) => {
2965
+ e.preventDefault();
2966
+ if (listEl) close();
2967
+ else open();
2968
+ });
2969
+ trigger.addEventListener("keydown", (e) => {
2970
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
2971
+ e.preventDefault();
2972
+ open();
2973
+ }
2974
+ });
2975
+ function renderTrigger() {
2976
+ trigger.innerHTML = "";
2977
+ const item = opts.items.find((i) => i.value === current) ?? opts.items[0];
2978
+ if (!item) return;
2979
+ trigger.appendChild(renderIcon(item));
2980
+ const labelEl = document.createElement("span");
2981
+ labelEl.className = "rs-label";
2982
+ labelEl.textContent = item.label;
2983
+ trigger.appendChild(labelEl);
2984
+ }
2985
+ function renderIcon(item) {
2986
+ if (item.icon) {
2987
+ if (/^<svg|^<\?xml/.test(item.icon.trim())) {
2988
+ const wrap = document.createElement("span");
2989
+ wrap.className = "rs-icon";
2990
+ wrap.innerHTML = item.icon;
2991
+ return wrap;
2992
+ }
2993
+ const img = document.createElement("img");
2994
+ img.className = "rs-icon";
2995
+ img.src = item.icon;
2996
+ img.alt = "";
2997
+ return img;
2998
+ }
2999
+ const tile = document.createElement("span");
3000
+ tile.className = "inspekt-rich-select-item-letter";
3001
+ tile.textContent = (item.label[0] ?? "?").toUpperCase();
3002
+ return tile;
3003
+ }
3004
+ function open() {
3005
+ if (listEl) return;
3006
+ listEl = document.createElement("div");
3007
+ listEl.className = "inspekt-rich-select-list";
3008
+ listEl.setAttribute("role", "listbox");
3009
+ let lastGroup;
3010
+ opts.items.forEach((item, i) => {
3011
+ if (item.group && item.group !== lastGroup) {
3012
+ const header = document.createElement("div");
3013
+ header.className = "inspekt-rich-select-group-header";
3014
+ header.setAttribute("role", "presentation");
3015
+ header.textContent = item.group;
3016
+ listEl.appendChild(header);
3017
+ }
3018
+ lastGroup = item.group;
3019
+ const row = document.createElement("div");
3020
+ row.className = "inspekt-rich-select-item";
3021
+ if (item.action) row.classList.add("inspekt-rich-select-item-action");
3022
+ row.setAttribute("role", "option");
3023
+ row.dataset["value"] = item.value;
3024
+ if (!item.action && item.value === current) row.dataset["selected"] = "true";
3025
+ row.appendChild(renderIcon(item));
3026
+ const body = document.createElement("div");
3027
+ body.className = "inspekt-rich-select-item-body";
3028
+ const labelEl = document.createElement("span");
3029
+ labelEl.className = "inspekt-rich-select-item-label";
3030
+ labelEl.textContent = item.label;
3031
+ body.appendChild(labelEl);
3032
+ if (item.hint) {
3033
+ const hintEl = document.createElement("span");
3034
+ hintEl.className = "inspekt-rich-select-item-hint";
3035
+ hintEl.textContent = item.hint;
3036
+ body.appendChild(hintEl);
3037
+ }
3038
+ row.appendChild(body);
3039
+ row.addEventListener("mouseenter", () => setActive(i));
3040
+ row.addEventListener("click", (e) => {
3041
+ e.preventDefault();
3042
+ e.stopPropagation();
3043
+ if (item.action) {
3044
+ close();
3045
+ item.action();
3046
+ return;
3047
+ }
3048
+ select(item.value);
3049
+ });
3050
+ if (item.popover) {
3051
+ tooltipDetachers.push(
3052
+ attachTooltip(row, item.popover(), {
3053
+ enterDelay: opts.popoverDelay ?? 50,
3054
+ leaveDelay: 200,
3055
+ placement: opts.popoverPlacement ?? "top"
3056
+ })
3057
+ );
3058
+ }
3059
+ listEl.appendChild(row);
3060
+ });
3061
+ listEl.style.position = "fixed";
3062
+ listEl.style.top = "0";
3063
+ listEl.style.left = "0";
3064
+ document.body.appendChild(listEl);
3065
+ cleanupAutoUpdate = (0, import_dom2.autoUpdate)(trigger, listEl, updateListPosition);
3066
+ trigger.setAttribute("aria-expanded", "true");
3067
+ setActive(opts.items.findIndex((i) => i.value === current));
3068
+ document.addEventListener("mousedown", onOutside, true);
3069
+ document.addEventListener("keydown", onKey, true);
3070
+ }
3071
+ function close() {
3072
+ if (!listEl) return;
3073
+ for (const d of tooltipDetachers) d();
3074
+ tooltipDetachers.length = 0;
3075
+ cleanupAutoUpdate?.();
3076
+ cleanupAutoUpdate = null;
3077
+ listEl.remove();
3078
+ listEl = null;
3079
+ trigger.setAttribute("aria-expanded", "false");
3080
+ document.removeEventListener("mousedown", onOutside, true);
3081
+ document.removeEventListener("keydown", onKey, true);
3082
+ }
3083
+ function updateListPosition() {
3084
+ if (!listEl) return;
3085
+ const list = listEl;
3086
+ const triggerWidth = trigger.getBoundingClientRect().width;
3087
+ list.style.minWidth = `${triggerWidth}px`;
3088
+ void (0, import_dom2.computePosition)(trigger, list, {
3089
+ placement: "bottom-start",
3090
+ strategy: "fixed",
3091
+ middleware: [
3092
+ (0, import_dom2.offset)(4),
3093
+ (0, import_dom2.flip)(),
3094
+ (0, import_dom2.shift)({ padding: 8 }),
3095
+ (0, import_dom2.size)({
3096
+ apply({ availableHeight }) {
3097
+ list.style.maxHeight = `${Math.max(160, availableHeight - 8)}px`;
3098
+ },
3099
+ padding: 8
3100
+ })
3101
+ ]
3102
+ }).then(({ x, y }) => {
3103
+ list.style.left = `${Math.round(x)}px`;
3104
+ list.style.top = `${Math.round(y)}px`;
3105
+ });
3106
+ }
3107
+ function setActive(i) {
3108
+ if (!listEl) return;
3109
+ activeIndex = i;
3110
+ const rows = listEl.querySelectorAll(".inspekt-rich-select-item");
3111
+ rows.forEach((r, idx) => {
3112
+ if (idx === i) r.dataset["active"] = "true";
3113
+ else delete r.dataset["active"];
3114
+ });
3115
+ }
3116
+ function select(value) {
3117
+ if (value !== current) {
3118
+ current = value;
3119
+ renderTrigger();
3120
+ opts.onChange(value);
3121
+ }
3122
+ close();
3123
+ }
3124
+ function onOutside(e) {
3125
+ const t = e.target;
3126
+ if (listEl?.contains(t) || trigger.contains(t)) return;
3127
+ if (t instanceof Element && t.closest(".inspekt-tooltip")) {
3128
+ setTimeout(close, 0);
3129
+ return;
3130
+ }
3131
+ close();
3132
+ }
3133
+ function onKey(e) {
3134
+ if (!listEl) return;
3135
+ if (e.key === "Escape") {
3136
+ e.preventDefault();
3137
+ close();
3138
+ trigger.focus();
3139
+ return;
3140
+ }
3141
+ if (e.key === "ArrowDown") {
3142
+ e.preventDefault();
3143
+ setActive(Math.min(opts.items.length - 1, activeIndex + 1));
3144
+ return;
3145
+ }
3146
+ if (e.key === "ArrowUp") {
3147
+ e.preventDefault();
3148
+ setActive(Math.max(0, activeIndex - 1));
3149
+ return;
3150
+ }
3151
+ if (e.key === "Enter" || e.key === " ") {
3152
+ e.preventDefault();
3153
+ const item = opts.items[activeIndex];
3154
+ if (!item) return;
3155
+ if (item.action) {
3156
+ close();
3157
+ item.action();
3158
+ } else {
3159
+ select(item.value);
3160
+ }
3161
+ return;
3162
+ }
3163
+ if (e.key.length === 1) {
3164
+ const ch = e.key.toLowerCase();
3165
+ const next = opts.items.findIndex((it) => it.label.toLowerCase().startsWith(ch));
3166
+ if (next >= 0) setActive(next);
3167
+ }
3168
+ }
3169
+ return {
3170
+ setValue(value) {
3171
+ current = value;
3172
+ renderTrigger();
3173
+ },
3174
+ getValue() {
3175
+ return current;
3176
+ },
3177
+ destroy() {
3178
+ close();
3179
+ trigger.remove();
3180
+ }
3181
+ };
3182
+ }
3183
+
3184
+ // src/index.ts
3185
+ var DEFAULT_OPTIONS = {
3186
+ // Default preserves the legacy click-mod behavior: page interactions never
3187
+ // intercepted unless Ctrl+Alt is held. Empty array = always-on hover.
3188
+ requireModifiers: ["ctrl", "alt"],
3189
+ showBoundingBoxes: false,
3190
+ shortcut: { key: "click", modifiers: ["ctrl", "alt"] },
3191
+ toggleShortcut: { key: "i", modifiers: ["ctrl", "alt"] },
3192
+ theme: "auto",
3193
+ highlight: {
3194
+ color: "#3b82f6",
3195
+ style: "glow",
3196
+ animation: "pulse"
3197
+ },
3198
+ badge: {
3199
+ show: true,
3200
+ position: "auto",
3201
+ showPath: false
3202
+ },
3203
+ borders: false,
3204
+ actions: ["open-editor", "copy-path", "open-github", "console-log"],
3205
+ editor: "cursor",
3206
+ editorPathBase: "",
3207
+ githubRepo: "",
3208
+ githubBranch: "main",
3209
+ pathMapping: {},
3210
+ treePanel: {
3211
+ enabled: true,
3212
+ position: "right",
3213
+ showProps: true,
3214
+ showLineNumbers: true
3215
+ },
3216
+ sourceAttribute: "data-insp-path",
3217
+ serverUrl: "",
3218
+ defaultSnippetExpanded: false,
3219
+ snippetContext: 5,
3220
+ sourceMapEnabled: false,
3221
+ customEditors: []
3222
+ };
3223
+ function createInspekt(userOptions = {}) {
3224
+ const options = { ...DEFAULT_OPTIONS, ...userOptions };
3225
+ let capabilityTeardown = null;
3226
+ if (userOptions.highlight) options.highlight = { ...DEFAULT_OPTIONS.highlight, ...userOptions.highlight };
3227
+ if (userOptions.badge) options.badge = { ...DEFAULT_OPTIONS.badge, ...userOptions.badge };
3228
+ if (userOptions.treePanel) options.treePanel = { ...DEFAULT_OPTIONS.treePanel, ...userOptions.treePanel };
3229
+ if (userOptions.shortcut) options.shortcut = { ...DEFAULT_OPTIONS.shortcut, ...userOptions.shortcut };
3230
+ if (userOptions.toggleShortcut) options.toggleShortcut = { ...DEFAULT_OPTIONS.toggleShortcut, ...userOptions.toggleShortcut };
3231
+ let enabled = false;
3232
+ const listeners = /* @__PURE__ */ new Map();
3233
+ const customActions = /* @__PURE__ */ new Map();
3234
+ const host = document.createElement("inspekt-root");
3235
+ const shadow = host.attachShadow({ mode: "open" });
3236
+ const style = document.createElement("style");
3237
+ style.textContent = STYLES;
3238
+ shadow.appendChild(style);
3239
+ applyTheme(host, options.theme);
3240
+ const highlighter = new Highlighter(options.highlight);
3241
+ const popover = new Popover(shadow);
3242
+ popover.configureSnippet({
3243
+ serverUrl: options.serverUrl || void 0,
3244
+ context: options.snippetContext,
3245
+ defaultExpanded: options.defaultSnippetExpanded,
3246
+ sourceMapEnabled: options.sourceMapEnabled,
3247
+ staticSnippets: options.staticSnippets
3248
+ });
3249
+ const overlay = new Overlay(shadow);
3250
+ const bboxOverlay = new BoundingBoxOverlay(shadow);
3251
+ const adapter = detectAdapter();
3252
+ let treePanel = null;
3253
+ if (options.treePanel.enabled) {
3254
+ treePanel = new TreePanel(shadow, {
3255
+ position: options.treePanel.position,
3256
+ showProps: options.treePanel.showProps,
3257
+ showLineNumbers: options.treePanel.showLineNumbers,
3258
+ onSelect(node) {
3259
+ if (node.domElement) {
3260
+ highlighter.unhighlightAll();
3261
+ highlighter.highlight(node.domElement, "selected");
3262
+ node.domElement.scrollIntoView({ behavior: "smooth", block: "center" });
3263
+ }
3264
+ emit("tree-select", node);
3265
+ },
3266
+ onHover(node) {
3267
+ highlighter.unhighlightAll();
3268
+ if (node?.domElement) {
3269
+ highlighter.highlight(node.domElement, "hovered");
3270
+ }
3271
+ }
3272
+ });
3273
+ }
3274
+ function refreshTree() {
3275
+ if (!treePanel) return;
3276
+ const root = document.getElementById("root") ?? document.getElementById("app") ?? document.body;
3277
+ const tree = adapter.getComponentTree(root);
3278
+ treePanel.updateTree(tree);
3279
+ }
3280
+ function getActions() {
3281
+ const builtInMap = {
3282
+ "open-editor": () => createOpenEditorAction(options.serverUrl, options.editor, options.customEditors),
3283
+ "copy-path": () => createCopyPathAction(),
3284
+ "open-github": () => createOpenGithubAction(options.githubRepo, options.githubBranch),
3285
+ "console-log": () => createConsoleLogAction()
3286
+ };
3287
+ const result = [];
3288
+ for (const id of options.actions) {
3289
+ const factory = builtInMap[id];
3290
+ if (factory) {
3291
+ result.push(factory());
3292
+ }
3293
+ }
3294
+ for (const action of customActions.values()) {
3295
+ result.push(action);
3296
+ }
3297
+ return result;
3298
+ }
3299
+ function modifiersOk(e) {
3300
+ return options.requireModifiers.every((mod) => {
3301
+ switch (mod) {
3302
+ case "ctrl":
3303
+ return e.ctrlKey || e.metaKey;
3304
+ case "alt":
3305
+ return e.altKey;
3306
+ case "shift":
3307
+ return e.shiftKey;
3308
+ case "meta":
3309
+ return e.metaKey;
3310
+ }
3311
+ });
3312
+ }
3313
+ function handleClick(e) {
3314
+ if (!enabled) return;
3315
+ const clickTarget = e.target;
3316
+ const isInsideInspekt = clickTarget?.tagName === "INSPEKT-ROOT";
3317
+ if (popover.isPinned() && !isInsideInspekt) {
3318
+ popover.unpin();
3319
+ popover.hide();
3320
+ highlighter.unhighlightAll();
3321
+ }
3322
+ if (e.shiftKey && e.altKey && !e.ctrlKey && !e.metaKey) {
3323
+ e.preventDefault();
3324
+ e.stopPropagation();
3325
+ const target2 = e.target;
3326
+ const result2 = findClosestSource(target2);
3327
+ if (result2) {
3328
+ const inspected = elementToInspected(result2.element, result2.source, options.pathMapping);
3329
+ const editorAction = getActions().find((a) => a.id === "open-editor");
3330
+ if (editorAction) editorAction.handler(inspected);
3331
+ emit("action", "open-editor", inspected);
3332
+ }
3333
+ return;
3334
+ }
3335
+ if (!modifiersOk(e)) return;
3336
+ const target = e.target;
3337
+ const result = findClosestSource(target);
3338
+ const isFallback = !result && isFallbackTarget(target);
3339
+ if (!result && !isFallback) return;
3340
+ e.preventDefault();
3341
+ e.stopPropagation();
3342
+ if (result) {
3343
+ const inspected = elementToInspected(result.element, result.source, options.pathMapping);
3344
+ highlighter.unhighlightAll();
3345
+ highlighter.highlight(result.element, "selected");
3346
+ popover.show(inspected, getActions(), { x: e.clientX, y: e.clientY });
3347
+ popover.pin();
3348
+ treePanel?.selectElement(inspected);
3349
+ emit("inspect", inspected);
3350
+ } else if (isFallback) {
3351
+ showDomFallback(target, e.clientX, e.clientY);
3352
+ popover.pin();
3353
+ }
3354
+ }
3355
+ function showDomFallback(el, x, y) {
3356
+ highlighter.unhighlightAll();
3357
+ highlighter.highlight(el, "selected");
3358
+ popover.showDom(
3359
+ el,
3360
+ { x, y },
3361
+ async (selector) => copyToClipboard(selector),
3362
+ async (html) => copyToClipboard(html),
3363
+ () => {
3364
+ console.log("[Inspekt]", el);
3365
+ }
3366
+ );
3367
+ }
3368
+ async function copyToClipboard(text) {
3369
+ if (navigator.clipboard?.writeText) {
3370
+ try {
3371
+ await navigator.clipboard.writeText(text);
3372
+ return true;
3373
+ } catch {
3374
+ }
3375
+ }
3376
+ try {
3377
+ const ta = document.createElement("textarea");
3378
+ ta.value = text;
3379
+ ta.style.cssText = "position:fixed;top:-1000px;left:-1000px;opacity:0;pointer-events:none;";
3380
+ document.body.appendChild(ta);
3381
+ ta.focus();
3382
+ ta.select();
3383
+ const ok = document.execCommand("copy");
3384
+ ta.remove();
3385
+ return ok;
3386
+ } catch {
3387
+ return false;
3388
+ }
3389
+ }
3390
+ function handleKeydown(e) {
3391
+ if (matchesShortcut(e, options.toggleShortcut)) {
3392
+ e.preventDefault();
3393
+ instance.toggle();
3394
+ return;
3395
+ }
3396
+ if (e.key === "Escape" && popover.isVisible()) {
3397
+ popover.unpin();
3398
+ popover.hide();
3399
+ highlighter.unhighlightAll();
3400
+ }
3401
+ if (e.key === "o" && e.ctrlKey && e.altKey) {
3402
+ e.preventDefault();
3403
+ overlay.toggle();
3404
+ }
3405
+ if (e.key === "t" && e.ctrlKey && e.altKey) {
3406
+ e.preventDefault();
3407
+ if (treePanel) {
3408
+ treePanel.toggle();
3409
+ if (treePanel.isVisible()) refreshTree();
3410
+ }
3411
+ }
3412
+ }
3413
+ function matchesShortcut(e, shortcut) {
3414
+ if (e.key.toLowerCase() !== shortcut.key.toLowerCase()) return false;
3415
+ const needCtrl = shortcut.modifiers.includes("ctrl");
3416
+ const needAlt = shortcut.modifiers.includes("alt");
3417
+ const needShift = shortcut.modifiers.includes("shift");
3418
+ const needMeta = shortcut.modifiers.includes("meta");
3419
+ return (needCtrl ? e.ctrlKey || e.metaKey : true) && (needAlt ? e.altKey : true) && (needShift ? e.shiftKey : true) && (needMeta ? e.metaKey : true);
3420
+ }
3421
+ function handleMouseMove(e) {
3422
+ if (!enabled) return;
3423
+ if (popover.isPinned()) return;
3424
+ if (!modifiersOk(e)) {
3425
+ highlighter.unhighlightAll();
3426
+ popover.hide();
3427
+ return;
3428
+ }
3429
+ const target = e.target;
3430
+ const result = findClosestSource(target);
3431
+ highlighter.unhighlightAll();
3432
+ if (result) {
3433
+ highlighter.highlight(result.element, "selected");
3434
+ const inspected = elementToInspected(result.element, result.source, options.pathMapping);
3435
+ popover.show(inspected, getActions(), { x: e.clientX, y: e.clientY });
3436
+ treePanel?.selectElement(inspected);
3437
+ emit("inspect", inspected);
3438
+ } else if (isFallbackTarget(target)) {
3439
+ showDomFallback(target, e.clientX, e.clientY);
3440
+ } else {
3441
+ popover.hide();
3442
+ }
3443
+ }
3444
+ function isFallbackTarget(el) {
3445
+ if (!el) return false;
3446
+ if (el === document.body || el === document.documentElement) return false;
3447
+ if (el.tagName === "INSPEKT-ROOT") return false;
3448
+ return true;
3449
+ }
3450
+ function emit(event, ...args) {
3451
+ const handlers = listeners.get(event);
3452
+ if (handlers) {
3453
+ for (const handler of handlers) {
3454
+ handler(...args);
3455
+ }
3456
+ }
3457
+ }
3458
+ function applyTheme(el, theme) {
3459
+ el.classList.remove("inspekt-light", "inspekt-dark");
3460
+ if (theme === "auto") {
3461
+ const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
3462
+ if (!isDark) el.classList.add("inspekt-light");
3463
+ } else if (theme === "light") {
3464
+ el.classList.add("inspekt-light");
3465
+ }
3466
+ }
3467
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
3468
+ const themeHandler = () => applyTheme(host, options.theme);
3469
+ mediaQuery.addEventListener("change", themeHandler);
3470
+ document.addEventListener("inspekt:settings-update", ((e) => {
3471
+ const newSettings = e.detail;
3472
+ Object.assign(options, newSettings);
3473
+ if (newSettings.highlight) highlighter.updateConfig(options.highlight);
3474
+ applyTheme(host, options.theme);
3475
+ if (enabled) {
3476
+ if (options.showBoundingBoxes === true) bboxOverlay.enable();
3477
+ else bboxOverlay.disable();
3478
+ }
3479
+ }));
3480
+ const instance = {
3481
+ enable() {
3482
+ if (enabled) return;
3483
+ enabled = true;
3484
+ document.body.appendChild(host);
3485
+ document.addEventListener("click", handleClick, true);
3486
+ document.addEventListener("keydown", handleKeydown, true);
3487
+ document.addEventListener("mousemove", handleMouseMove);
3488
+ window.__INSPEKT__ = { version: "0.1.0", options };
3489
+ document.dispatchEvent(new CustomEvent("inspekt:status", { detail: { enabled: true } }));
3490
+ void Promise.resolve().then(() => (init_capability_probe(), capability_probe_exports)).then(({ watchCapabilities: watchCapabilities2 }) => {
3491
+ if (!enabled) return;
3492
+ capabilityTeardown = watchCapabilities2({
3493
+ serverUrl: options.serverUrl || void 0,
3494
+ sourceMapEnabled: options.sourceMapEnabled
3495
+ });
3496
+ });
3497
+ if (treePanel && options.treePanel.enabled) {
3498
+ setTimeout(() => refreshTree(), 100);
3499
+ }
3500
+ if (options.showBoundingBoxes) {
3501
+ bboxOverlay.enable();
3502
+ }
3503
+ emit("enable");
3504
+ },
3505
+ disable() {
3506
+ if (!enabled) return;
3507
+ enabled = false;
3508
+ popover.hide();
3509
+ overlay.hide();
3510
+ treePanel?.hide();
3511
+ bboxOverlay.disable();
3512
+ highlighter.unhighlightAll();
3513
+ capabilityTeardown?.();
3514
+ capabilityTeardown = null;
3515
+ document.removeEventListener("click", handleClick, true);
3516
+ document.removeEventListener("keydown", handleKeydown, true);
3517
+ document.removeEventListener("mousemove", handleMouseMove);
3518
+ host.remove();
3519
+ document.dispatchEvent(new CustomEvent("inspekt:status", { detail: { enabled: false } }));
3520
+ emit("disable");
3521
+ },
3522
+ toggle() {
3523
+ if (enabled) instance.disable();
3524
+ else instance.enable();
3525
+ },
3526
+ destroy() {
3527
+ instance.disable();
3528
+ popover.destroy();
3529
+ overlay.destroy();
3530
+ treePanel?.destroy();
3531
+ bboxOverlay.destroy();
3532
+ mediaQuery.removeEventListener("change", themeHandler);
3533
+ listeners.clear();
3534
+ customActions.clear();
3535
+ delete window.__INSPEKT__;
3536
+ },
3537
+ registerAction(action) {
3538
+ customActions.set(action.id, action);
3539
+ },
3540
+ unregisterAction(id) {
3541
+ customActions.delete(id);
3542
+ },
3543
+ on(event, handler) {
3544
+ if (!listeners.has(event)) listeners.set(event, /* @__PURE__ */ new Set());
3545
+ listeners.get(event).add(handler);
3546
+ },
3547
+ off(event, handler) {
3548
+ listeners.get(event)?.delete(handler);
3549
+ }
3550
+ };
3551
+ return instance;
3552
+ }
3553
+ // Annotate the CommonJS export names for ESM import in node:
3554
+ 0 && (module.exports = {
3555
+ attachTooltip,
3556
+ createInspekt,
3557
+ createRichSelect,
3558
+ tokenizeToLines
3559
+ });
3560
+ //# sourceMappingURL=index.cjs.map