@archie/devtools 0.0.17 → 0.0.18

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.
@@ -0,0 +1,3567 @@
1
+ /**
2
+ * Archie Inspector - Standalone script for CDN injection.
3
+ * Set before loading: window.__ARCHIE_INSPECTOR_TARGET_ORIGIN__, window.__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__
4
+ * Then load: <script src="https://your-cdn/inspector.standalone.js"></script>
5
+ * Auto-runs if configured; or call window.__ARCHIE_INSPECTOR_RUN__().
6
+ * Security: No eval(), no "*" origins; style values sanitized.
7
+ */
8
+ ;(function () {
9
+ "use strict"
10
+ // Capture originals at module load time so the patch below is never recursive
11
+ // even when runInspector() is called multiple times.
12
+ var _rawDocumentQuerySelector = Document.prototype.querySelector
13
+ var _rawDocumentQuerySelectorAll = Document.prototype.querySelectorAll
14
+ var _documentQuerySelectorsPatched = false
15
+
16
+ function runInspector() {
17
+ var INSPECTOR_PROBLEMATIC_SELECTOR_PATTERNS = [
18
+ /\/\d+/,
19
+ /\[backdrop-filter\]/,
20
+ /supports-\[/,
21
+ /:\d+\.\d+/,
22
+ /\.bg-background\/\d+/,
23
+ /\.supports-\[backdrop-filter\]/,
24
+ /bg-background\/\d+/,
25
+ /supports-\[/,
26
+ ]
27
+
28
+ function isProblematicInspectorSelector(selector) {
29
+ if (!selector || typeof selector !== "string") return false
30
+ return INSPECTOR_PROBLEMATIC_SELECTOR_PATTERNS.some(function (pattern) {
31
+ return pattern.test(selector)
32
+ })
33
+ }
34
+
35
+ function isInspectorSelectorSyntaxError(error) {
36
+ return (
37
+ error &&
38
+ (error.name === "SyntaxError" ||
39
+ (error.message &&
40
+ (error.message.includes("not a valid selector") ||
41
+ error.message.includes("Failed to execute"))))
42
+ )
43
+ }
44
+
45
+ function safeQuerySelector(selector) {
46
+ try {
47
+ if (!selector || typeof selector !== "string") {
48
+ return _rawDocumentQuerySelector.call(document, selector)
49
+ }
50
+ if (isProblematicInspectorSelector(selector)) return null
51
+ return _rawDocumentQuerySelector.call(document, selector)
52
+ } catch (error) {
53
+ if (isInspectorSelectorSyntaxError(error)) return null
54
+ throw error
55
+ }
56
+ }
57
+
58
+ function safeQuerySelectorAll(selector) {
59
+ function emptyNodeList() {
60
+ return document.createDocumentFragment().childNodes
61
+ }
62
+
63
+ try {
64
+ if (!selector || typeof selector !== "string") {
65
+ return _rawDocumentQuerySelectorAll.call(document, selector)
66
+ }
67
+ if (isProblematicInspectorSelector(selector)) return emptyNodeList()
68
+ return _rawDocumentQuerySelectorAll.call(document, selector)
69
+ } catch (error) {
70
+ if (isInspectorSelectorSyntaxError(error)) return emptyNodeList()
71
+ throw error
72
+ }
73
+ }
74
+
75
+ // Patch only the current document instance (not Document.prototype) to avoid
76
+ // a global cross-library prototype mutation. The patch remains for this
77
+ // document lifetime after the inspector initializes.
78
+ if (!_documentQuerySelectorsPatched) {
79
+ _documentQuerySelectorsPatched = true
80
+ try {
81
+ document.querySelector = function archieQuerySelectorInstance(selector) {
82
+ return safeQuerySelector(selector)
83
+ }
84
+ document.querySelectorAll = function archieQuerySelectorAllInstance(
85
+ selector,
86
+ ) {
87
+ return safeQuerySelectorAll(selector)
88
+ }
89
+ } catch (_) {}
90
+ }
91
+
92
+ ;(function () {
93
+ "use strict"
94
+
95
+ var FIBER_KEY_PREFIX = "__reactFiber$"
96
+ var PROPS_KEY_PREFIX = "__reactProps$"
97
+
98
+ function hasEditorMetaInFiberChain(fiber) {
99
+ var current = fiber
100
+ while (current) {
101
+ if (current.memoizedProps && current.memoizedProps.__editorMeta) {
102
+ return true
103
+ }
104
+ current = current["return"]
105
+ }
106
+ return false
107
+ }
108
+
109
+ // Scoring logic duplicated from fiber-bridge.ts (TypeScript context).
110
+ // If this algorithm changes, update fiber-bridge.ts to maintain parity.
111
+ function compareFiberCandidates(candidate, bestCandidate) {
112
+ if (!bestCandidate) return 1
113
+
114
+ var candidateScore = [
115
+ candidate.hasMatchingProps ? 1 : 0,
116
+ candidate.hasDirectEditorMeta ? 1 : 0,
117
+ candidate.hasCompanionPropsKey ? 1 : 0,
118
+ candidate.hasEditorMetaInReturnChain ? 1 : 0,
119
+ candidate.order,
120
+ ]
121
+ var bestScore = [
122
+ bestCandidate.hasMatchingProps ? 1 : 0,
123
+ bestCandidate.hasDirectEditorMeta ? 1 : 0,
124
+ bestCandidate.hasCompanionPropsKey ? 1 : 0,
125
+ bestCandidate.hasEditorMetaInReturnChain ? 1 : 0,
126
+ bestCandidate.order,
127
+ ]
128
+
129
+ for (var i = 0; i < candidateScore.length; i++) {
130
+ if (candidateScore[i] === bestScore[i]) continue
131
+ return candidateScore[i] > bestScore[i] ? 1 : -1
132
+ }
133
+
134
+ return 0
135
+ }
136
+
137
+ function getFiberSelectionFromElement(el) {
138
+ if (!el || typeof el !== "object") {
139
+ return { fiber: null, fiberKey: null }
140
+ }
141
+
142
+ var record = el
143
+ var keys = Object.keys(record)
144
+ var bestCandidate = null
145
+
146
+ for (var i = 0; i < keys.length; i++) {
147
+ var fiberKey = keys[i]
148
+ if (fiberKey.indexOf(FIBER_KEY_PREFIX) !== 0) continue
149
+
150
+ var fiber = record[fiberKey]
151
+ if (!fiber) continue
152
+ if (fiber.stateNode && fiber.stateNode !== el) continue
153
+
154
+ var suffix = fiberKey.slice(FIBER_KEY_PREFIX.length)
155
+ var propsKey = PROPS_KEY_PREFIX + suffix
156
+ var hasCompanionPropsKey = Object.prototype.hasOwnProperty.call(
157
+ record,
158
+ propsKey
159
+ )
160
+ var companionProps = hasCompanionPropsKey
161
+ ? record[propsKey]
162
+ : undefined
163
+ var candidate = {
164
+ fiber: fiber,
165
+ fiberKey: fiberKey,
166
+ hasMatchingProps:
167
+ hasCompanionPropsKey && fiber.memoizedProps === companionProps,
168
+ hasDirectEditorMeta:
169
+ !!(fiber.memoizedProps && fiber.memoizedProps.__editorMeta),
170
+ hasCompanionPropsKey: hasCompanionPropsKey,
171
+ hasEditorMetaInReturnChain: hasEditorMetaInFiberChain(fiber),
172
+ order: i,
173
+ }
174
+
175
+ if (compareFiberCandidates(candidate, bestCandidate) > 0) {
176
+ bestCandidate = candidate
177
+ }
178
+ }
179
+
180
+ if (!bestCandidate) {
181
+ return { fiber: null, fiberKey: null }
182
+ }
183
+
184
+ return {
185
+ fiber: bestCandidate.fiber,
186
+ fiberKey: bestCandidate.fiberKey,
187
+ }
188
+ }
189
+
190
+ function getFiberFromElement(el) {
191
+ return getFiberSelectionFromElement(el).fiber
192
+ }
193
+
194
+ function getEditorMetaFromElement(el) {
195
+ var fiber = getFiberFromElement(el)
196
+ while (fiber) {
197
+ if (fiber.memoizedProps && fiber.memoizedProps.__editorMeta) {
198
+ return fiber.memoizedProps.__editorMeta
199
+ }
200
+ fiber = fiber["return"]
201
+ }
202
+ return null
203
+ }
204
+
205
+ function getDirectEditorMetaFromElement(el) {
206
+ var fiber = getFiberFromElement(el)
207
+ if (fiber && fiber.memoizedProps && fiber.memoizedProps.__editorMeta) {
208
+ return fiber.memoizedProps.__editorMeta
209
+ }
210
+ return null
211
+ }
212
+
213
+ // Walks the fiber return chain from el's own fiber, stopping at the boundary
214
+ // of the next host element. Returns __editorMeta from the nearest owner
215
+ // component (e.g. <Button> renders <button> — Button's fiber has __editorMeta
216
+ // but the button host fiber does not).
217
+ // Uses nodeType instead of instanceof Element to avoid cross-iframe issues.
218
+ function getOwnerEditorMetaFromElement(el) {
219
+ var fiber = getFiberFromElement(el)
220
+ if (!fiber) return null
221
+
222
+ var current = fiber["return"]
223
+ while (current) {
224
+ if (
225
+ current.stateNode !== null &&
226
+ current.stateNode !== undefined &&
227
+ typeof current.stateNode.nodeType === "number" &&
228
+ current.stateNode.nodeType === 1 &&
229
+ current.stateNode !== el
230
+ ) {
231
+ break
232
+ }
233
+ if (current.memoizedProps && current.memoizedProps.__editorMeta) {
234
+ return current.memoizedProps.__editorMeta
235
+ }
236
+ current = current["return"]
237
+ }
238
+ return null
239
+ }
240
+
241
+ function findNearestEditorMeta(el) {
242
+ var current = el
243
+ while (
244
+ current &&
245
+ current !== document.body &&
246
+ current !== document.documentElement
247
+ ) {
248
+ var meta = getEditorMetaFromElement(current)
249
+ if (meta) return { meta: meta, element: current }
250
+ current = current.parentElement
251
+ }
252
+ return null
253
+ }
254
+
255
+ if (window._inspectorSharedTargetObserver) {
256
+ try {
257
+ window._inspectorSharedTargetObserver.disconnect()
258
+ } catch (e) {
259
+ // Ignore stale observer cleanup failures.
260
+ }
261
+ delete window._inspectorSharedTargetObserver
262
+ }
263
+
264
+ var sharedTargetIndex = {
265
+ dirty: true,
266
+ observer: null,
267
+ exactBySignature: new Map(),
268
+ sharedBySignature: new Map(),
269
+ }
270
+ var ARCHIE_SOURCE_REF_ATTR = "data-archie-source-ref"
271
+
272
+ function normalizeSharedSignaturePart(value) {
273
+ if (value == null) return ""
274
+ return String(value).trim()
275
+ }
276
+
277
+ function getSharedSignatureFromMeta(meta) {
278
+ if (!meta || typeof meta !== "object") return ""
279
+ var file = normalizeSharedSignaturePart(meta.file)
280
+ var line = normalizeSharedSignaturePart(meta.line)
281
+ var column = normalizeSharedSignaturePart(meta.col)
282
+ if (!column) {
283
+ column = normalizeSharedSignaturePart(meta.column)
284
+ }
285
+ if (!file || !line || !column) return ""
286
+ return file + "::" + line + "::" + column
287
+ }
288
+
289
+ function getLegacySharedSignatureFromElement(element) {
290
+ if (!element || typeof element.closest !== "function") return ""
291
+ var componentRoot = element.closest("[data-inspector-relative-path]")
292
+ if (!componentRoot) return ""
293
+
294
+ var path = normalizeSharedSignaturePart(
295
+ componentRoot.getAttribute("data-inspector-relative-path")
296
+ )
297
+ var line = normalizeSharedSignaturePart(
298
+ componentRoot.getAttribute("data-inspector-line")
299
+ )
300
+ var column = normalizeSharedSignaturePart(
301
+ componentRoot.getAttribute("data-inspector-column")
302
+ )
303
+ if (!path || !line || !column) return ""
304
+ return path + "::" + line + "::" + column
305
+ }
306
+
307
+ function getExactSourceSignatureFromElement(element) {
308
+ if (!element) return ""
309
+
310
+ var directMeta = getDirectEditorMetaFromElement(element)
311
+ var directSignature = directMeta
312
+ ? getSharedSignatureFromMeta(directMeta)
313
+ : ""
314
+ if (directSignature) return directSignature
315
+
316
+ // Check the owner component's meta (e.g. <Button> renders <button>:
317
+ // Button's component fiber has __editorMeta, the button host fiber does not).
318
+ var ownerMeta = getOwnerEditorMetaFromElement(element)
319
+ var ownerSignature = ownerMeta
320
+ ? getSharedSignatureFromMeta(ownerMeta)
321
+ : ""
322
+ if (ownerSignature) return ownerSignature
323
+
324
+ if (
325
+ element &&
326
+ typeof element.getAttribute === "function" &&
327
+ element.hasAttribute(ARCHIE_SOURCE_REF_ATTR)
328
+ ) {
329
+ return normalizeSharedSignaturePart(
330
+ element.getAttribute(ARCHIE_SOURCE_REF_ATTR)
331
+ )
332
+ }
333
+
334
+ return ""
335
+ }
336
+
337
+ function getCoarseSharedSignatureFromElement(element) {
338
+ if (!element) return ""
339
+
340
+ var nearestMeta = findNearestEditorMeta(element)
341
+ var nearestSignature =
342
+ nearestMeta && nearestMeta.meta
343
+ ? getSharedSignatureFromMeta(nearestMeta.meta)
344
+ : ""
345
+ if (nearestSignature) return nearestSignature
346
+
347
+ return getLegacySharedSignatureFromElement(element)
348
+ }
349
+
350
+ function resolveCoarseInspectTarget(element) {
351
+ var normalizedTarget = normalizeInspectableTarget(element)
352
+ if (
353
+ !normalizedTarget ||
354
+ normalizedTarget === document.body ||
355
+ normalizedTarget === document.documentElement ||
356
+ isInspectorElement(normalizedTarget)
357
+ ) {
358
+ return null
359
+ }
360
+
361
+ var baseSignature = getCoarseSharedSignatureFromElement(
362
+ normalizedTarget
363
+ )
364
+ if (!baseSignature) return normalizedTarget
365
+
366
+ var canonicalTarget = normalizedTarget
367
+ var current = normalizedTarget.parentElement
368
+
369
+ while (
370
+ current &&
371
+ current !== document.body &&
372
+ current !== document.documentElement &&
373
+ !isInspectorElement(current)
374
+ ) {
375
+ var currentSignature = getCoarseSharedSignatureFromElement(current)
376
+ if (!currentSignature || currentSignature !== baseSignature) break
377
+ canonicalTarget = current
378
+ current = current.parentElement
379
+ }
380
+
381
+ return canonicalTarget
382
+ }
383
+
384
+ function resolveExactInspectTarget(element) {
385
+ var normalizedTarget = normalizeInspectableTarget(element)
386
+ if (
387
+ !normalizedTarget ||
388
+ normalizedTarget === document.body ||
389
+ normalizedTarget === document.documentElement ||
390
+ isInspectorElement(normalizedTarget)
391
+ ) {
392
+ return null
393
+ }
394
+
395
+ // SVG elements are valid inspect targets even without source signatures.
396
+ // Icon libraries render SVGs without __editorMeta; walking past them
397
+ // would select the parent button/anchor, breaking icon selection.
398
+ var normalizedTag = String(normalizedTarget.tagName || "").toLowerCase()
399
+ if (normalizedTag === "svg") {
400
+ return normalizedTarget
401
+ }
402
+
403
+ var current = normalizedTarget
404
+ while (
405
+ current &&
406
+ current !== document.body &&
407
+ current !== document.documentElement &&
408
+ !isInspectorElement(current)
409
+ ) {
410
+ if (getExactSourceSignatureFromElement(current)) {
411
+ return current
412
+ }
413
+ current = current.parentElement
414
+ }
415
+
416
+ return resolveCoarseInspectTarget(normalizedTarget)
417
+ }
418
+
419
+ function resolveSharedGroupSignature(element) {
420
+ var inspectTarget = resolveExactInspectTarget(element)
421
+ if (!inspectTarget) return ""
422
+
423
+ var exactSignature = getExactSourceSignatureFromElement(inspectTarget)
424
+ if (exactSignature) return exactSignature
425
+
426
+ return getCoarseSharedSignatureFromElement(inspectTarget)
427
+ }
428
+
429
+ function appendSharedTarget(map, signature, element) {
430
+ if (!signature || !element) return
431
+ var existing = map.get(signature)
432
+ if (!existing) {
433
+ map.set(signature, [element])
434
+ return
435
+ }
436
+ if (existing.indexOf(element) === -1) {
437
+ existing.push(element)
438
+ }
439
+ }
440
+
441
+ function markSharedTargetIndexDirty() {
442
+ sharedTargetIndex.dirty = true
443
+ }
444
+
445
+ function ensureSharedTargetObserver() {
446
+ if (
447
+ sharedTargetIndex.observer ||
448
+ typeof MutationObserver === "undefined" ||
449
+ !document.body
450
+ ) {
451
+ return
452
+ }
453
+
454
+ sharedTargetIndex.observer = new MutationObserver(function (mutations) {
455
+ for (var i = 0; i < mutations.length; i++) {
456
+ var mutation = mutations[i]
457
+ if (mutation.type === "childList") {
458
+ if (
459
+ mutation.addedNodes.length > 0 ||
460
+ mutation.removedNodes.length > 0
461
+ ) {
462
+ markSharedTargetIndexDirty()
463
+ return
464
+ }
465
+ }
466
+
467
+ if (mutation.type === "attributes") {
468
+ markSharedTargetIndexDirty()
469
+ return
470
+ }
471
+ }
472
+ })
473
+
474
+ sharedTargetIndex.observer.observe(document.body, {
475
+ childList: true,
476
+ subtree: true,
477
+ attributes: true,
478
+ attributeFilter: [
479
+ ARCHIE_SOURCE_REF_ATTR,
480
+ "data-dnd-node-id",
481
+ "data-inspector-relative-path",
482
+ "data-inspector-line",
483
+ "data-inspector-column",
484
+ ],
485
+ })
486
+ window._inspectorSharedTargetObserver = sharedTargetIndex.observer
487
+ }
488
+
489
+ function rebuildSharedTargetIndex() {
490
+ ensureSharedTargetObserver()
491
+ if (!sharedTargetIndex.dirty) return
492
+
493
+ var nextExactBySignature = new Map()
494
+ var nextSharedBySignature = new Map()
495
+ var candidates = safeQuerySelectorAll("body *")
496
+ var indexedTargets = new WeakSet()
497
+
498
+ candidates.forEach(function (candidate) {
499
+ if (!candidate || candidate.nodeType !== Node.ELEMENT_NODE) return
500
+ if (isInspectorElement(candidate)) return
501
+
502
+ var inspectTarget = resolveExactInspectTarget(candidate)
503
+ if (!inspectTarget) return
504
+ if (indexedTargets.has(inspectTarget)) return
505
+ indexedTargets.add(inspectTarget)
506
+
507
+ var exactSignature = getExactSourceSignatureFromElement(inspectTarget)
508
+ if (exactSignature) {
509
+ appendSharedTarget(
510
+ nextExactBySignature,
511
+ exactSignature,
512
+ inspectTarget
513
+ )
514
+ }
515
+
516
+ var sharedSignature = resolveSharedGroupSignature(inspectTarget)
517
+ if (sharedSignature) {
518
+ appendSharedTarget(
519
+ nextSharedBySignature,
520
+ sharedSignature,
521
+ inspectTarget
522
+ )
523
+ }
524
+ })
525
+
526
+ sharedTargetIndex.exactBySignature = nextExactBySignature
527
+ sharedTargetIndex.sharedBySignature = nextSharedBySignature
528
+ sharedTargetIndex.dirty = false
529
+ }
530
+
531
+ function filterSharedTargets(referenceElement, candidates) {
532
+ if (!Array.isArray(candidates)) return []
533
+
534
+ var connectedCandidates = candidates.filter(function (candidate, index) {
535
+ if (!candidate || !document.contains(candidate)) return false
536
+ return candidates.indexOf(candidate) === index
537
+ })
538
+ if (connectedCandidates.length === 0) return []
539
+ return connectedCandidates
540
+ }
541
+
542
+ function getSharedSignatureFromInspectorSource(inspectorSource) {
543
+ if (!inspectorSource || typeof inspectorSource !== "object") return ""
544
+ var file =
545
+ typeof inspectorSource.file === "string"
546
+ ? inspectorSource.file.trim()
547
+ : ""
548
+ var line =
549
+ typeof inspectorSource.line === "number"
550
+ ? String(inspectorSource.line)
551
+ : typeof inspectorSource.line === "string"
552
+ ? inspectorSource.line.trim()
553
+ : ""
554
+ var column =
555
+ typeof inspectorSource.column === "number"
556
+ ? String(inspectorSource.column)
557
+ : typeof inspectorSource.column === "string"
558
+ ? inspectorSource.column.trim()
559
+ : ""
560
+ if (!file || !line) return ""
561
+ return file + "::" + line + "::" + (column || "")
562
+ }
563
+
564
+ function parseSourceSignature(signature) {
565
+ if (!signature || typeof signature !== "string") return null
566
+ var parts = signature.split("::")
567
+ if (parts.length < 3) return null
568
+ var column = normalizeSharedSignaturePart(parts[parts.length - 1])
569
+ var line = normalizeSharedSignaturePart(parts[parts.length - 2])
570
+ var file = normalizeSharedSignaturePart(parts.slice(0, -2).join("::"))
571
+ if (!file || !line || !column) return null
572
+ return {
573
+ file: file,
574
+ line: line,
575
+ column: column,
576
+ }
577
+ }
578
+
579
+ function buildInspectorInfoFromMeta(meta) {
580
+ if (!meta) return null
581
+ return {
582
+ file: meta.file,
583
+ line: meta.line,
584
+ column: meta.col,
585
+ componentName: meta.componentName,
586
+ nodeId: meta.nodeId,
587
+ }
588
+ }
589
+
590
+ function buildInspectorInfoFromExactSignature(signature, element) {
591
+ var parsed = parseSourceSignature(signature)
592
+ if (!parsed) return null
593
+ var tagName = String(element && element.tagName ? element.tagName : "")
594
+ .toLowerCase()
595
+ return {
596
+ file: parsed.file,
597
+ line: Number(parsed.line),
598
+ column: Number(parsed.column),
599
+ componentName: tagName || null,
600
+ nodeId:
601
+ parsed.file +
602
+ ":" +
603
+ parsed.line +
604
+ ":" +
605
+ parsed.column +
606
+ ":" +
607
+ (tagName || "element"),
608
+ }
609
+ }
610
+
611
+ function getInspectorInfoFromElement(element) {
612
+ if (!element) return null
613
+
614
+ var directMeta = getDirectEditorMetaFromElement(element)
615
+ if (directMeta) {
616
+ return buildInspectorInfoFromMeta(directMeta)
617
+ }
618
+
619
+ var exactSignature = getExactSourceSignatureFromElement(element)
620
+ if (exactSignature) {
621
+ return buildInspectorInfoFromExactSignature(exactSignature, element)
622
+ }
623
+
624
+ var metaResult = findNearestEditorMeta(element)
625
+ if (metaResult && metaResult.meta) {
626
+ return buildInspectorInfoFromMeta(metaResult.meta)
627
+ }
628
+
629
+ var legacySignature = getLegacySharedSignatureFromElement(element)
630
+ if (legacySignature) {
631
+ return buildInspectorInfoFromExactSignature(legacySignature, element)
632
+ }
633
+
634
+ return null
635
+ }
636
+
637
+ function resolveSharedTargetsFromMeta(targetMeta, referenceElement) {
638
+ var signature = getSharedSignatureFromMeta(targetMeta)
639
+ if (!signature) return []
640
+ rebuildSharedTargetIndex()
641
+ return filterSharedTargets(
642
+ referenceElement || null,
643
+ sharedTargetIndex.sharedBySignature.get(signature) || []
644
+ )
645
+ }
646
+
647
+ function resolveLegacySharedTargets(referenceElement) {
648
+ var signature = getLegacySharedSignatureFromElement(referenceElement)
649
+ if (!signature) return []
650
+ rebuildSharedTargetIndex()
651
+ return filterSharedTargets(
652
+ referenceElement,
653
+ sharedTargetIndex.sharedBySignature.get(signature) || []
654
+ )
655
+ }
656
+
657
+ function resolveSharedTargets(referenceElement) {
658
+ var inspectTarget = resolveExactInspectTarget(referenceElement)
659
+ if (!inspectTarget) return []
660
+
661
+ var signature = resolveSharedGroupSignature(inspectTarget)
662
+ if (signature) {
663
+ rebuildSharedTargetIndex()
664
+ var sharedTargets = filterSharedTargets(
665
+ inspectTarget,
666
+ sharedTargetIndex.sharedBySignature.get(signature) || []
667
+ )
668
+ if (sharedTargets.length > 0) return sharedTargets
669
+ }
670
+
671
+ var legacyTargets = resolveLegacySharedTargets(inspectTarget)
672
+ if (legacyTargets.length > 0) {
673
+ return legacyTargets
674
+ }
675
+
676
+ return [inspectTarget]
677
+ }
678
+
679
+ function getConfiguredTargetOrigin() {
680
+ var configured =
681
+ typeof window !== "undefined"
682
+ ? window.__ARCHIE_INSPECTOR_TARGET_ORIGIN__
683
+ : ""
684
+ return typeof configured === "string" && configured !== "*"
685
+ ? configured
686
+ : ""
687
+ }
688
+
689
+ function getConfiguredAllowedOrigins() {
690
+ var configured =
691
+ typeof window !== "undefined"
692
+ ? window.__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__
693
+ : null
694
+ if (typeof configured === "string") {
695
+ return configured && configured !== "*" ? [configured] : []
696
+ }
697
+ if (!Array.isArray(configured)) return []
698
+ return configured.filter(function (origin) {
699
+ return typeof origin === "string" && origin && origin !== "*"
700
+ })
701
+ }
702
+
703
+ function getConfiguredAllowedOriginPatterns() {
704
+ var configured =
705
+ typeof window !== "undefined"
706
+ ? window.__ARCHIE_INSPECTOR_ALLOWED_ORIGIN_PATTERNS__
707
+ : []
708
+ if (!Array.isArray(configured)) return []
709
+ return configured.filter(function (pattern) {
710
+ return typeof pattern === "string" && pattern
711
+ })
712
+ }
713
+
714
+ function getReferrerOrigin() {
715
+ try {
716
+ return document.referrer ? new URL(document.referrer).origin : ""
717
+ } catch (error) {
718
+ return ""
719
+ }
720
+ }
721
+
722
+ var bridgeState = {
723
+ targetOrigin: getConfiguredTargetOrigin() || "",
724
+ pendingParentMessages: [],
725
+ }
726
+
727
+ function syncBridgeStateFromWindow() {
728
+ var configuredTargetOrigin = getConfiguredTargetOrigin()
729
+ if (configuredTargetOrigin) {
730
+ bridgeState.targetOrigin = configuredTargetOrigin
731
+ }
732
+ }
733
+
734
+ function flushPendingParentMessages() {
735
+ if (window.parent === window) {
736
+ bridgeState.pendingParentMessages = []
737
+ return
738
+ }
739
+ syncBridgeStateFromWindow()
740
+ if (
741
+ !bridgeState.targetOrigin ||
742
+ bridgeState.targetOrigin === "*" ||
743
+ bridgeState.pendingParentMessages.length === 0
744
+ ) {
745
+ return
746
+ }
747
+
748
+ var pendingMessages = bridgeState.pendingParentMessages.slice()
749
+ bridgeState.pendingParentMessages = []
750
+ pendingMessages.forEach(function (message) {
751
+ window.parent.postMessage(message, bridgeState.targetOrigin)
752
+ })
753
+ }
754
+
755
+ function adoptHostTargetOrigin(origin) {
756
+ if (
757
+ !origin ||
758
+ origin === "*" ||
759
+ bridgeState.targetOrigin === origin ||
760
+ bridgeState.targetOrigin
761
+ ) {
762
+ return
763
+ }
764
+
765
+ bridgeState.targetOrigin = origin
766
+ if (typeof window !== "undefined") {
767
+ window.__ARCHIE_INSPECTOR_TARGET_ORIGIN__ = origin
768
+ }
769
+ flushPendingParentMessages()
770
+ }
771
+
772
+ function postMessageToParent(msg) {
773
+ if (window.parent === window) {
774
+ return
775
+ }
776
+
777
+ syncBridgeStateFromWindow()
778
+ if (!bridgeState.targetOrigin || bridgeState.targetOrigin === "*") {
779
+ bridgeState.pendingParentMessages.push(msg)
780
+ return
781
+ }
782
+
783
+ window.parent.postMessage(msg, bridgeState.targetOrigin)
784
+ }
785
+
786
+ function isOriginAllowed(origin) {
787
+ if (!origin) return false
788
+ var allowedOrigins = getConfiguredAllowedOrigins()
789
+ var allowedOriginPatterns = getConfiguredAllowedOriginPatterns()
790
+ var configuredTargetOrigin = getConfiguredTargetOrigin()
791
+ if (
792
+ Array.isArray(allowedOrigins) &&
793
+ allowedOrigins.length > 0
794
+ ) {
795
+ if (allowedOrigins.indexOf(origin) !== -1) return true
796
+ } else if (configuredTargetOrigin && origin === configuredTargetOrigin) {
797
+ return true
798
+ }
799
+ if (
800
+ Array.isArray(allowedOriginPatterns) &&
801
+ allowedOriginPatterns.length > 0
802
+ ) {
803
+ try {
804
+ var host = new URL(origin).host
805
+ for (var i = 0; i < allowedOriginPatterns.length; i++) {
806
+ if (host.endsWith(allowedOriginPatterns[i])) return true
807
+ }
808
+ } catch (e) {}
809
+ }
810
+ return false
811
+ }
812
+
813
+ /** Reject CSS values that could run script (e.g. expression(), javascript: URLs, -moz-binding). Max length to avoid DoS. */
814
+ function isStyleValueSafe(value) {
815
+ if (value == null || typeof value !== "string") return false
816
+ if (value.length > 10000) return false
817
+ var lower = value.toLowerCase()
818
+ if (lower.indexOf("javascript:") !== -1) return false
819
+ if (lower.indexOf("expression(") !== -1) return false
820
+ if (lower.indexOf("-moz-binding") !== -1) return false
821
+ if (lower.indexOf("behavior") !== -1) return false
822
+ if (lower.indexOf("vbscript:") !== -1) return false
823
+ return true
824
+ }
825
+
826
+ var SCROLL_LISTENER_OPTS = { capture: true, passive: true }
827
+ var RESIZE_LISTENER_OPTS = { passive: true }
828
+
829
+ // Namespace (host can use __ARCHIE_INSPECTOR__ or legacy _inspector* for backward compat)
830
+ if (typeof window.__ARCHIE_INSPECTOR__ === "undefined") {
831
+ window.__ARCHIE_INSPECTOR__ = {}
832
+ }
833
+
834
+ // Global debug flag - can be set from outside: window._inspectorDebug or __ARCHIE_INSPECTOR__.debug
835
+ if (typeof window._inspectorDebug === "undefined") {
836
+ window._inspectorDebug = false
837
+ }
838
+ window.__ARCHIE_INSPECTOR__.debug = window._inspectorDebug
839
+
840
+ // Helper function for conditional logging
841
+ function inspectorLog() {
842
+ if (window._inspectorDebug) {
843
+ // eslint-disable-next-line no-console
844
+ console.log.apply(console, arguments)
845
+ }
846
+ }
847
+
848
+ function inspectorWarn() {
849
+ if (window._inspectorDebug) {
850
+ // eslint-disable-next-line no-console
851
+ console.warn.apply(console, arguments)
852
+ }
853
+ }
854
+
855
+ postMessageToParent({
856
+ type: "INSPECTOR_SCRIPT_LOADED",
857
+ timestamp: Date.now(),
858
+ version: "2.0.3",
859
+ protocolVersion: 1,
860
+ capabilities: {
861
+ inspect: true,
862
+ dndMove: true,
863
+ dndResize: true,
864
+ fiberInspectorSource: true,
865
+ hostControlledHistory: true,
866
+ arrangeStructuredHistory: true,
867
+ arrangeStrongNodeIdentity: true,
868
+ },
869
+ })
870
+
871
+ if (window._inspectorMessageHandler) {
872
+ try {
873
+ window.removeEventListener("message", window._inspectorMessageHandler)
874
+ } catch (e) {
875
+ // Ignore errors
876
+ }
877
+ delete window._inspectorMessageHandler
878
+ }
879
+
880
+ if (window._inspectorSelectedElement) {
881
+ try {
882
+ if (
883
+ !window._inspectorSelectedElement.parentNode ||
884
+ !document.contains(window._inspectorSelectedElement)
885
+ ) {
886
+ window._inspectorSelectedElement = null
887
+ }
888
+ } catch (e) {
889
+ window._inspectorSelectedElement = null
890
+ }
891
+ }
892
+
893
+ let isInspecting = false
894
+ let isElementSelected = false // Track if an element is currently selected/being edited
895
+ let highlightedElement = null
896
+ let overlay = null
897
+ let selectedElement = null
898
+ let selectedElements = []
899
+ let selectedElementBadge = null
900
+ let eventBlockers = [] // Store event blocker functions for cleanup
901
+ let lastMultiSelectIntent = false // Tracks if user held meta/ctrl during pointer down
902
+ var inspectorCleanupMap = new WeakMap()
903
+ var BADGE_UPDATE_DELAY_MS = 50
904
+
905
+ if (!window._inspectorSelectedElement) {
906
+ window._inspectorSelectedElement = null
907
+ }
908
+ window.__ARCHIE_INSPECTOR__.selectedElement =
909
+ window._inspectorSelectedElement
910
+ if (window._inspectorRuntimeBridgeHandler) {
911
+ try {
912
+ window.removeEventListener(
913
+ "archie:dnd-runtime-event",
914
+ window._inspectorRuntimeBridgeHandler
915
+ )
916
+ } catch (e) {
917
+ // Ignore bridge cleanup errors
918
+ }
919
+ delete window._inspectorRuntimeBridgeHandler
920
+ }
921
+
922
+ function isInspectorElement(element) {
923
+ if (!element) return false
924
+ return (
925
+ element === overlay ||
926
+ element === selectedElementBadge ||
927
+ element.id === "inspector-ping-effect" ||
928
+ (element.closest && element.closest("[data-inspector-badge]"))
929
+ )
930
+ }
931
+
932
+ function normalizeInspectableTarget(element) {
933
+ if (!element || typeof element.closest !== "function") {
934
+ return element || null
935
+ }
936
+
937
+ var tagName = String(element.tagName || "").toLowerCase()
938
+ if (tagName === "svg") {
939
+ return element
940
+ }
941
+
942
+ var parentSvg = element.closest("svg")
943
+ if (parentSvg && !isInspectorElement(parentSvg)) {
944
+ return parentSvg
945
+ }
946
+
947
+ return element
948
+ }
949
+
950
+ // Hit-test: skip body/documentElement and inspector UI
951
+ function getElementFromPoint(x, y, fallbackTarget) {
952
+ try {
953
+ var list = document.elementsFromPoint(x, y)
954
+ for (var i = 0; i < list.length; i++) {
955
+ var el = list[i]
956
+ el = resolveExactInspectTarget(el)
957
+ if (!el || el === document.body || el === document.documentElement)
958
+ continue
959
+ if (isInspectorElement(el)) continue
960
+ return el
961
+ }
962
+ } catch (err) {}
963
+ return resolveExactInspectTarget(fallbackTarget || null)
964
+ }
965
+
966
+ var inspectorTheme = {
967
+ accent: "#8b5cf6",
968
+ accentSoft: "rgba(139,92,246,0.10)",
969
+ hover: "rgba(139,92,246,0.72)",
970
+ shared: "rgba(139,92,246,0.62)",
971
+ }
972
+ var INSPECTOR_OUTLINE = "2px solid " + inspectorTheme.accent
973
+ var INSPECTOR_OUTLINE_OFFSET = "-1px"
974
+
975
+ var inspectorStylesInjected = false
976
+ function getSharedOutlineRule() {
977
+ return (
978
+ '[data-inspector-shared="true"]{' +
979
+ "outline:2px dashed " +
980
+ inspectorTheme.shared +
981
+ "!important;" +
982
+ "outline-offset:-2px!important}"
983
+ )
984
+ }
985
+ function injectInspectorStylesheet() {
986
+ var style = document.getElementById("archie-inspector-styles")
987
+ if (!style) {
988
+ style = document.createElement("style")
989
+ style.id = "archie-inspector-styles"
990
+ ;(document.head || document.documentElement).appendChild(style)
991
+ }
992
+ style.textContent = getSharedOutlineRule()
993
+ inspectorStylesInjected = true
994
+ }
995
+
996
+ function updateInspectorTheme(nextTheme) {
997
+ if (!nextTheme || typeof nextTheme !== "object") return
998
+ inspectorTheme = {
999
+ accent:
1000
+ typeof nextTheme.accent === "string" && nextTheme.accent
1001
+ ? nextTheme.accent
1002
+ : inspectorTheme.accent,
1003
+ accentSoft:
1004
+ typeof nextTheme.accentSoft === "string" && nextTheme.accentSoft
1005
+ ? nextTheme.accentSoft
1006
+ : inspectorTheme.accentSoft,
1007
+ hover:
1008
+ typeof nextTheme.hover === "string" && nextTheme.hover
1009
+ ? nextTheme.hover
1010
+ : inspectorTheme.hover,
1011
+ shared:
1012
+ typeof nextTheme.shared === "string" && nextTheme.shared
1013
+ ? nextTheme.shared
1014
+ : inspectorTheme.shared,
1015
+ }
1016
+ INSPECTOR_OUTLINE = "2px solid " + inspectorTheme.accent
1017
+ injectInspectorStylesheet()
1018
+ selectedElements.forEach(function (el) {
1019
+ if (el) applyInspectorStyles(el)
1020
+ })
1021
+ if (
1022
+ highlightedElement &&
1023
+ !selectedElements.includes(highlightedElement)
1024
+ ) {
1025
+ applyOutlineToElement(highlightedElement, "hover")
1026
+ }
1027
+ }
1028
+
1029
+ function applyOutlineToElement(element, mode) {
1030
+ if (!element || !element.style) return
1031
+ var outlineColor =
1032
+ mode === "hover" ? inspectorTheme.hover : inspectorTheme.accent
1033
+ element.style.setProperty(
1034
+ "outline",
1035
+ "2px solid " + outlineColor,
1036
+ "important"
1037
+ )
1038
+ element.style.setProperty(
1039
+ "outline-offset",
1040
+ INSPECTOR_OUTLINE_OFFSET,
1041
+ "important"
1042
+ )
1043
+ }
1044
+
1045
+ function removeOutlineFromElement(element) {
1046
+ if (!element || !element.style) return
1047
+ element.style.removeProperty("outline")
1048
+ element.style.removeProperty("outline-offset")
1049
+ }
1050
+
1051
+ function applyInspectorStyles(element) {
1052
+ if (!element) return
1053
+ injectInspectorStylesheet()
1054
+ element.setAttribute("data-inspector-editing", "true")
1055
+ applyOutlineToElement(element)
1056
+ }
1057
+
1058
+ // Helper: Remove inspector styles from element
1059
+ function removeInspectorStyles(element) {
1060
+ if (!element) return
1061
+ removeOutlineFromElement(element)
1062
+ element.removeAttribute("data-inspector-editing")
1063
+ }
1064
+
1065
+ function getElementCleanup(element) {
1066
+ return element ? inspectorCleanupMap.get(element) : null
1067
+ }
1068
+ function setElementCleanup(element, cleanup) {
1069
+ if (!element) return
1070
+ var prev = inspectorCleanupMap.get(element)
1071
+ if (prev) {
1072
+ window.removeEventListener("scroll", prev.scroll, true)
1073
+ window.removeEventListener("resize", prev.resize)
1074
+ }
1075
+ inspectorCleanupMap.set(element, cleanup)
1076
+ }
1077
+ function deleteElementCleanup(element) {
1078
+ if (!element) return
1079
+ var prev = inspectorCleanupMap.get(element)
1080
+ if (prev) {
1081
+ window.removeEventListener("scroll", prev.scroll, true)
1082
+ window.removeEventListener("resize", prev.resize)
1083
+ }
1084
+ inspectorCleanupMap.delete(element)
1085
+ }
1086
+
1087
+ // Helper: Clean up element's inspector event listeners
1088
+ function cleanupElementListeners(element) {
1089
+ if (!element) return
1090
+ deleteElementCleanup(element)
1091
+ }
1092
+
1093
+ // Helper: attach scroll/resize listeners to keep badge positioned over the given element (uses selectedElement/selectedElementBadge from closure)
1094
+ function setupBadgePositionListeners(element) {
1095
+ if (!element) return
1096
+ function updateBadgePosition() {
1097
+ if (selectedElement && selectedElementBadge) {
1098
+ var rect = selectedElement.getBoundingClientRect()
1099
+ var scrollX =
1100
+ window.pageXOffset || document.documentElement.scrollLeft
1101
+ var scrollY =
1102
+ window.pageYOffset || document.documentElement.scrollTop
1103
+ selectedElementBadge.style.top = rect.top + scrollY + "px"
1104
+ selectedElementBadge.style.left = rect.left + scrollX + "px"
1105
+ }
1106
+ }
1107
+ window.addEventListener(
1108
+ "scroll",
1109
+ updateBadgePosition,
1110
+ SCROLL_LISTENER_OPTS
1111
+ )
1112
+ window.addEventListener(
1113
+ "resize",
1114
+ updateBadgePosition,
1115
+ RESIZE_LISTENER_OPTS
1116
+ )
1117
+ setElementCleanup(element, {
1118
+ scroll: updateBadgePosition,
1119
+ resize: updateBadgePosition,
1120
+ })
1121
+ }
1122
+
1123
+ // Function to block all native events when inspector is active
1124
+ function setupEventBlockers() {
1125
+ removeEventBlockers()
1126
+
1127
+ // Core interaction events to block (removed less critical ones)
1128
+ // Don't block mousedown: inspectorClickHandler needs it to select inputs; it preventDefault+stopPropagation
1129
+ const eventsToBlock = [
1130
+ "click",
1131
+ "mouseup",
1132
+ "dblclick",
1133
+ "contextmenu",
1134
+ "submit",
1135
+ "change",
1136
+ "input",
1137
+ "focus",
1138
+ "blur",
1139
+ "keydown",
1140
+ "keyup",
1141
+ ]
1142
+
1143
+ const blockEvent = function (e) {
1144
+ if (!isInspecting) return
1145
+
1146
+ // Allow move events and inspector UI events
1147
+ if (
1148
+ e.type === "mousemove" ||
1149
+ e.type === "pointermove" ||
1150
+ isInspectorElement(e.target)
1151
+ ) {
1152
+ return
1153
+ }
1154
+
1155
+ // Block all other events
1156
+ e.preventDefault()
1157
+ e.stopPropagation()
1158
+ e.stopImmediatePropagation()
1159
+ return false
1160
+ }
1161
+
1162
+ eventsToBlock.forEach(function (eventType) {
1163
+ document.addEventListener(eventType, blockEvent, {
1164
+ capture: true,
1165
+ passive: false,
1166
+ })
1167
+ eventBlockers.push({ type: eventType, handler: blockEvent })
1168
+ })
1169
+ }
1170
+
1171
+ // Function to remove all event blockers
1172
+ function removeEventBlockers() {
1173
+ eventBlockers.forEach(function (blocker) {
1174
+ document.removeEventListener(blocker.type, blocker.handler, {
1175
+ capture: true,
1176
+ passive: false,
1177
+ })
1178
+ })
1179
+ eventBlockers = []
1180
+ }
1181
+
1182
+ const updateMultiSelectIntent = function (e) {
1183
+ if (!isInspecting) return
1184
+ lastMultiSelectIntent = e.metaKey || e.ctrlKey
1185
+ }
1186
+ document.addEventListener("mousedown", updateMultiSelectIntent, true)
1187
+ document.addEventListener(
1188
+ "pointerup",
1189
+ (event) => {
1190
+ if (!event.metaKey && !event.ctrlKey) {
1191
+ lastMultiSelectIntent = false
1192
+ }
1193
+ },
1194
+ true
1195
+ )
1196
+ document.addEventListener(
1197
+ "keyup",
1198
+ (event) => {
1199
+ if (!event.metaKey && !event.ctrlKey) {
1200
+ lastMultiSelectIntent = false
1201
+ }
1202
+ },
1203
+ true
1204
+ )
1205
+
1206
+ function getSelectedElement() {
1207
+ if (
1208
+ selectedElement &&
1209
+ selectedElement.parentNode !== null &&
1210
+ document.contains(selectedElement)
1211
+ ) {
1212
+ return selectedElement
1213
+ }
1214
+
1215
+ if (
1216
+ window._inspectorSelectedElement &&
1217
+ window._inspectorSelectedElement.parentNode !== null &&
1218
+ document.contains(window._inspectorSelectedElement)
1219
+ ) {
1220
+ selectedElement = window._inspectorSelectedElement
1221
+ return selectedElement
1222
+ }
1223
+
1224
+ try {
1225
+ const element = safeQuerySelector('[data-inspector-editing="true"]')
1226
+ if (element && document.contains(element)) {
1227
+ selectedElement = element
1228
+ window._inspectorSelectedElement = element
1229
+ if (window.__ARCHIE_INSPECTOR__)
1230
+ window.__ARCHIE_INSPECTOR__.selectedElement = element
1231
+ return element
1232
+ }
1233
+ } catch (e) {
1234
+ // Ignore errors
1235
+ }
1236
+
1237
+ return null
1238
+ }
1239
+
1240
+ // Helper: Update shared component highlights
1241
+ function updateSharedHighlights() {
1242
+ // Clear all shared highlights first
1243
+ const sharedElements = safeQuerySelectorAll(
1244
+ '[data-inspector-shared="true"]'
1245
+ )
1246
+ sharedElements.forEach((el) => {
1247
+ el.removeAttribute("data-inspector-shared")
1248
+ })
1249
+
1250
+ const targets = []
1251
+ // Add all selected elements
1252
+ if (selectedElements && selectedElements.length > 0) {
1253
+ selectedElements.forEach((el) => targets.push(el))
1254
+ }
1255
+ // Add highlighted element if different and not already selected
1256
+ if (
1257
+ highlightedElement &&
1258
+ !selectedElements.includes(highlightedElement)
1259
+ ) {
1260
+ targets.push(highlightedElement)
1261
+ }
1262
+
1263
+ targets.forEach((target) => {
1264
+ var newShared = resolveSharedTargets(target)
1265
+
1266
+ newShared.forEach((el) => {
1267
+ if (selectedElements.includes(el) || el === highlightedElement)
1268
+ return
1269
+ injectInspectorStylesheet()
1270
+ el.setAttribute("data-inspector-shared", "true")
1271
+ })
1272
+ })
1273
+ }
1274
+
1275
+ function highlightElement(element) {
1276
+ element = resolveExactInspectTarget(element)
1277
+ if (!element || selectedElements.includes(element)) {
1278
+ return
1279
+ }
1280
+
1281
+ if (highlightedElement && highlightedElement !== element) {
1282
+ highlightedElement.removeAttribute("data-inspector-hover")
1283
+ if (!selectedElements.includes(highlightedElement)) {
1284
+ removeOutlineFromElement(highlightedElement)
1285
+ }
1286
+ }
1287
+
1288
+ injectInspectorStylesheet()
1289
+ highlightedElement = element
1290
+ element.setAttribute("data-inspector-hover", "true")
1291
+ applyOutlineToElement(element, "hover")
1292
+ updateSharedHighlights()
1293
+ }
1294
+
1295
+ function removeHighlight() {
1296
+ if (highlightedElement) {
1297
+ highlightedElement.removeAttribute("data-inspector-hover")
1298
+ if (!selectedElements.includes(highlightedElement)) {
1299
+ removeOutlineFromElement(highlightedElement)
1300
+ }
1301
+ highlightedElement = null
1302
+ }
1303
+ const pingEffect = document.getElementById("inspector-ping-effect")
1304
+ if (pingEffect) {
1305
+ pingEffect.remove()
1306
+ }
1307
+ if (overlay) {
1308
+ overlay.remove()
1309
+ overlay = null
1310
+ }
1311
+ updateSharedHighlights()
1312
+ }
1313
+
1314
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1315
+ function createSelectedBadge(element) {
1316
+ // Badge is now created in the host application, not in the iframe
1317
+ // This function is kept for compatibility but does nothing
1318
+ // The badge will be created by ElementInspectorBadge component in the host
1319
+ }
1320
+
1321
+ function removeSelectedBadge() {
1322
+ if (selectedElementBadge) {
1323
+ selectedElementBadge.remove()
1324
+ selectedElementBadge = null
1325
+ }
1326
+
1327
+ // Cleanup all selected elements
1328
+ selectedElements.forEach((el) => {
1329
+ if (el && el._inspectorPingCleanup) {
1330
+ window.removeEventListener(
1331
+ "scroll",
1332
+ el._inspectorPingCleanup.scroll,
1333
+ true
1334
+ )
1335
+ window.removeEventListener(
1336
+ "resize",
1337
+ el._inspectorPingCleanup.resize
1338
+ )
1339
+ if (el._inspectorPingCleanup.pingElement) {
1340
+ el._inspectorPingCleanup.pingElement.remove()
1341
+ }
1342
+ delete el._inspectorPingCleanup
1343
+ }
1344
+
1345
+ removeInspectorStyles(el)
1346
+ if (el.style) {
1347
+ el.style.removeProperty("transition")
1348
+ }
1349
+ })
1350
+
1351
+ // Also cleanup the single reference if it exists and wasn't in the array (shouldn't happen but for safety)
1352
+ if (selectedElement && !selectedElements.includes(selectedElement)) {
1353
+ removeInspectorStyles(selectedElement)
1354
+ if (selectedElement.style) {
1355
+ selectedElement.style.removeProperty("transition")
1356
+ }
1357
+ }
1358
+
1359
+ selectedElement = null
1360
+ isElementSelected = false // Mark that no element is selected
1361
+ }
1362
+
1363
+ function activateInspectionMode() {
1364
+ selectedElement = null
1365
+ selectedElements = []
1366
+ window._inspectorSelectedElement = null
1367
+ lastMultiSelectIntent = false
1368
+ isInspecting = true
1369
+
1370
+ const applyCursorStyles = () => {
1371
+ if (document.body) {
1372
+ document.body.style.cursor = "crosshair"
1373
+ document.body.style.userSelect = "none"
1374
+ } else {
1375
+ setTimeout(applyCursorStyles, 100)
1376
+ }
1377
+ }
1378
+
1379
+ applyCursorStyles()
1380
+ setupEventBlockers()
1381
+ }
1382
+
1383
+ function deactivateInspectionMode(options) {
1384
+ var notifyParent = !options || options.notifyParent !== false
1385
+ isInspecting = false
1386
+ isElementSelected = false
1387
+ removeHighlight()
1388
+ removeSelectedBadge()
1389
+ selectedElements.forEach(function (el) {
1390
+ if (el) removeInspectorStyles(el)
1391
+ })
1392
+ selectedElement = null
1393
+ selectedElements = []
1394
+ window._inspectorSelectedElement = null
1395
+ if (window.__ARCHIE_INSPECTOR__) {
1396
+ window.__ARCHIE_INSPECTOR__.selectedElement = null
1397
+ }
1398
+ lastMultiSelectIntent = false
1399
+ try {
1400
+ safeQuerySelectorAll('[data-inspector-editing="true"]').forEach(
1401
+ function (el) {
1402
+ removeInspectorStyles(el)
1403
+ }
1404
+ )
1405
+ } catch (_) {}
1406
+ removeEventBlockers()
1407
+ if (document.body) {
1408
+ document.body.style.cursor = ""
1409
+ document.body.style.userSelect = ""
1410
+ }
1411
+ updateSharedHighlights()
1412
+ if (notifyParent) {
1413
+ postMessageToParent({ type: "STOP_INSPECTION" })
1414
+ }
1415
+ }
1416
+
1417
+ function dispatchHostCommand(detail) {
1418
+ window.dispatchEvent(
1419
+ new CustomEvent("archie:editor-host-command", {
1420
+ detail: detail,
1421
+ })
1422
+ )
1423
+ }
1424
+
1425
+ function setEditorModeFromHost(mode, options) {
1426
+ var historyMode =
1427
+ options && options.hostControlledHistory ? "host" : "local"
1428
+
1429
+ if (options && options.theme) {
1430
+ updateInspectorTheme(options.theme)
1431
+ dispatchHostCommand({
1432
+ type: "set-inspector-theme",
1433
+ theme: options.theme,
1434
+ })
1435
+ }
1436
+
1437
+ dispatchHostCommand({
1438
+ type: "set-history-mode",
1439
+ historyMode: historyMode,
1440
+ })
1441
+
1442
+ if (mode === "inspect") {
1443
+ dispatchHostCommand({ type: "set-design-mode", enabled: false })
1444
+ activateInspectionMode()
1445
+ postMessageToParent({ type: "EDITOR_MODE_CHANGED", mode: "inspect" })
1446
+ return
1447
+ }
1448
+
1449
+ if (mode === "design") {
1450
+ deactivateInspectionMode({ notifyParent: false })
1451
+ dispatchHostCommand({ type: "set-design-mode", enabled: true })
1452
+ postMessageToParent({ type: "EDITOR_MODE_CHANGED", mode: "design" })
1453
+ return
1454
+ }
1455
+
1456
+ deactivateInspectionMode({ notifyParent: false })
1457
+ dispatchHostCommand({ type: "set-design-mode", enabled: false })
1458
+ postMessageToParent({ type: "EDITOR_MODE_CHANGED", mode: "off" })
1459
+ }
1460
+
1461
+ function findElementForReplay(selector, inspectorSource) {
1462
+ var element = null
1463
+ if (selector) {
1464
+ element = findElementBySelector(selector)
1465
+ }
1466
+ if (!element && inspectorSource) {
1467
+ element = findElementByInspectorSource(inspectorSource)
1468
+ }
1469
+ return element || getSelectedElement()
1470
+ }
1471
+
1472
+ function applyMoveReplay(message) {
1473
+ var selector = message.selector
1474
+ var placement = message.placement || {}
1475
+ var element = findElementForReplay(selector, message.inspectorSource)
1476
+ if (!element) return false
1477
+
1478
+ var parent =
1479
+ (placement.containerSelector &&
1480
+ findElementBySelector(placement.containerSelector)) ||
1481
+ (placement.containerInspectorSource &&
1482
+ findElementByInspectorSource(placement.containerInspectorSource)) ||
1483
+ null
1484
+ var beforeSibling =
1485
+ (placement.beforeSelector &&
1486
+ findElementBySelector(placement.beforeSelector)) ||
1487
+ (placement.beforeSiblingInspectorSource &&
1488
+ findElementByInspectorSource(
1489
+ placement.beforeSiblingInspectorSource
1490
+ )) ||
1491
+ null
1492
+ var afterSibling =
1493
+ (placement.afterSelector &&
1494
+ findElementBySelector(placement.afterSelector)) ||
1495
+ (placement.afterSiblingInspectorSource &&
1496
+ findElementByInspectorSource(placement.afterSiblingInspectorSource)) ||
1497
+ null
1498
+
1499
+ if (!parent && beforeSibling && beforeSibling.parentElement) {
1500
+ parent = beforeSibling.parentElement
1501
+ }
1502
+ if (!parent && afterSibling && afterSibling.parentElement) {
1503
+ parent = afterSibling.parentElement
1504
+ }
1505
+ if (!parent) {
1506
+ parent = element.parentElement
1507
+ }
1508
+ if (!parent) return false
1509
+
1510
+ if (beforeSibling && beforeSibling.parentElement === parent) {
1511
+ parent.insertBefore(element, beforeSibling)
1512
+ } else if (afterSibling && afterSibling.parentElement === parent) {
1513
+ parent.insertBefore(
1514
+ element,
1515
+ afterSibling.nextSibling ? afterSibling.nextSibling : null
1516
+ )
1517
+ } else if (
1518
+ typeof placement.insertionIndex === "number" &&
1519
+ placement.insertionIndex >= 0 &&
1520
+ parent.children
1521
+ ) {
1522
+ var refChild = parent.children[placement.insertionIndex] || null
1523
+ parent.insertBefore(element, refChild)
1524
+ } else {
1525
+ parent.appendChild(element)
1526
+ }
1527
+
1528
+ return true
1529
+ }
1530
+
1531
+ function applyResizeReplay(message) {
1532
+ var selector = message.selector
1533
+ var resize = message.resize || {}
1534
+ var element = findElementForReplay(selector, message.inspectorSource)
1535
+ if (!element || !element.style) return false
1536
+
1537
+ var styles =
1538
+ resize.styles ||
1539
+ (resize.styleDiff && resize.styleDiff.after) ||
1540
+ message.styles ||
1541
+ null
1542
+ if (styles && typeof styles === "object") {
1543
+ Object.keys(styles).forEach(function (key) {
1544
+ var value = styles[key]
1545
+ if (value == null || value === "") {
1546
+ element.style.removeProperty(key)
1547
+ } else {
1548
+ element.style.setProperty(key, String(value))
1549
+ }
1550
+ })
1551
+ return true
1552
+ }
1553
+
1554
+ if (typeof resize.width === "number") {
1555
+ element.style.width = resize.width + "px"
1556
+ }
1557
+ if (typeof resize.height === "number") {
1558
+ element.style.height = resize.height + "px"
1559
+ }
1560
+
1561
+ return true
1562
+ }
1563
+
1564
+ // Helper function to get CSS selector for an element
1565
+ // This is used for matching elements by selector in APPLY_STYLE_CHANGE handler
1566
+ function getElementSelector(element) {
1567
+ if (!element) return ""
1568
+
1569
+ // If element has ID, return it
1570
+ if (element.id) {
1571
+ return "#" + element.id
1572
+ }
1573
+
1574
+ // Build a simple selector using tag name and classes
1575
+ let selector = element.tagName.toLowerCase()
1576
+ if (
1577
+ element.className &&
1578
+ typeof element.className === "string" &&
1579
+ element.className.trim()
1580
+ ) {
1581
+ const classes = element.className
1582
+ .trim()
1583
+ .split(/\s+/)
1584
+ .filter((c) => c && c.length > 0)
1585
+ if (classes.length > 0) {
1586
+ selector += "." + classes.slice(0, 2).join(".")
1587
+ }
1588
+ }
1589
+
1590
+ return selector
1591
+ }
1592
+
1593
+ /** Stable selector for restore/undo (path with nth-of-type, id, or data-inspector). */
1594
+ function getStableSelector(el) {
1595
+ if (!el) return ""
1596
+ if (el.nodeType !== Node.ELEMENT_NODE) return ""
1597
+ if (el === document.body) return "body"
1598
+ if (el === document.documentElement) return "html"
1599
+ if (el.id) return "#" + el.id
1600
+
1601
+ if (el.hasAttribute && el.hasAttribute("data-inspector-editing")) {
1602
+ const relativePath = el.getAttribute("data-inspector-relative-path")
1603
+ const line = el.getAttribute("data-inspector-line")
1604
+ const column = el.getAttribute("data-inspector-column")
1605
+ const tagName = el.nodeName.toLowerCase()
1606
+
1607
+ if (relativePath && line && column) {
1608
+ const withColumnSelector =
1609
+ tagName +
1610
+ '[data-inspector-relative-path="' +
1611
+ escapeAttributeSelectorValue(relativePath) +
1612
+ '"][data-inspector-line="' +
1613
+ escapeAttributeSelectorValue(line) +
1614
+ '"][data-inspector-column="' +
1615
+ escapeAttributeSelectorValue(column) +
1616
+ '"]'
1617
+ try {
1618
+ const matches = safeQuerySelectorAll(withColumnSelector)
1619
+ if (matches.length === 1) return withColumnSelector
1620
+ } catch (e) {}
1621
+ }
1622
+
1623
+ if (relativePath && line) {
1624
+ const withoutColumnSelector =
1625
+ tagName +
1626
+ '[data-inspector-relative-path="' +
1627
+ escapeAttributeSelectorValue(relativePath) +
1628
+ '"][data-inspector-line="' +
1629
+ escapeAttributeSelectorValue(line) +
1630
+ '"]'
1631
+ try {
1632
+ const matches = safeQuerySelectorAll(withoutColumnSelector)
1633
+ if (matches.length === 1) return withoutColumnSelector
1634
+ } catch (e) {}
1635
+ }
1636
+ }
1637
+
1638
+ const path = []
1639
+ let current = el
1640
+ while (
1641
+ current &&
1642
+ current.nodeType === Node.ELEMENT_NODE &&
1643
+ current !== document.body &&
1644
+ current !== document.documentElement
1645
+ ) {
1646
+ let sel = current.nodeName.toLowerCase()
1647
+ let sibling = current
1648
+ let nth = 1
1649
+ while (sibling.previousElementSibling) {
1650
+ sibling = sibling.previousElementSibling
1651
+ if (sibling.nodeName === current.nodeName) nth++
1652
+ }
1653
+ sel += ":nth-of-type(" + nth + ")"
1654
+ path.unshift(sel)
1655
+ current = current.parentElement
1656
+ }
1657
+ return path.length > 0 ? path.join(" > ") : el.nodeName.toLowerCase()
1658
+ }
1659
+
1660
+ function escapeAttributeSelectorValue(value) {
1661
+ return String(value || "")
1662
+ .replace(/\\/g, "\\\\")
1663
+ .replace(/"/g, '\\"')
1664
+ }
1665
+
1666
+ function stripSelectorClassQualifiers(selector) {
1667
+ if (typeof selector !== "string" || selector.indexOf(".") === -1) {
1668
+ return selector
1669
+ }
1670
+
1671
+ return selector
1672
+ .split(">")
1673
+ .map(function (segment) {
1674
+ const trimmed = segment.trim()
1675
+ if (!trimmed) return trimmed
1676
+
1677
+ const nthIndex = trimmed.indexOf(":nth-of-type(")
1678
+ const suffix = nthIndex === -1 ? "" : trimmed.slice(nthIndex)
1679
+ const prefix =
1680
+ nthIndex === -1 ? trimmed : trimmed.slice(0, nthIndex)
1681
+ const classIndex = prefix.indexOf(".")
1682
+ if (classIndex === -1) return trimmed
1683
+
1684
+ const withoutClasses = prefix.slice(0, classIndex)
1685
+ if (!withoutClasses) return trimmed
1686
+ return withoutClasses + suffix
1687
+ })
1688
+ .join(" > ")
1689
+ }
1690
+
1691
+ function findElementBySelector(selector) {
1692
+ if (!selector || typeof selector !== "string") return null
1693
+
1694
+ try {
1695
+ const exactMatch = safeQuerySelector(selector)
1696
+ if (exactMatch) return exactMatch
1697
+ } catch (e) {}
1698
+
1699
+ const relaxedSelector = stripSelectorClassQualifiers(selector)
1700
+ if (!relaxedSelector || relaxedSelector === selector) {
1701
+ return null
1702
+ }
1703
+ try {
1704
+ return safeQuerySelector(relaxedSelector)
1705
+ } catch (e) {
1706
+ return null
1707
+ }
1708
+ }
1709
+
1710
+ function findRelatedInspectorCandidate(candidates, preferredElement) {
1711
+ if (
1712
+ !preferredElement ||
1713
+ !Array.isArray(candidates) ||
1714
+ candidates.length === 0
1715
+ ) {
1716
+ return null
1717
+ }
1718
+
1719
+ for (let index = 0; index < candidates.length; index++) {
1720
+ const candidate = candidates[index]
1721
+ if (candidate === preferredElement) {
1722
+ return candidate
1723
+ }
1724
+ }
1725
+
1726
+ if (typeof preferredElement.contains === "function") {
1727
+ for (let index = 0; index < candidates.length; index++) {
1728
+ const candidate = candidates[index]
1729
+ if (candidate && preferredElement.contains(candidate)) {
1730
+ return candidate
1731
+ }
1732
+ }
1733
+ }
1734
+
1735
+ for (let index = 0; index < candidates.length; index++) {
1736
+ const candidate = candidates[index]
1737
+ if (
1738
+ candidate &&
1739
+ typeof candidate.contains === "function" &&
1740
+ candidate.contains(preferredElement)
1741
+ ) {
1742
+ return candidate
1743
+ }
1744
+ }
1745
+
1746
+ return null
1747
+ }
1748
+
1749
+ function selectInspectorSourceCandidate(
1750
+ candidates,
1751
+ preferredElement,
1752
+ allowFirstMatchFallback
1753
+ ) {
1754
+ if (!Array.isArray(candidates) || candidates.length === 0) {
1755
+ return null
1756
+ }
1757
+
1758
+ const relatedCandidate = findRelatedInspectorCandidate(
1759
+ candidates,
1760
+ preferredElement
1761
+ )
1762
+ if (relatedCandidate) return relatedCandidate
1763
+
1764
+ const editingCandidate = candidates.find(function (candidate) {
1765
+ return candidate.getAttribute("data-inspector-editing") === "true"
1766
+ })
1767
+ if (editingCandidate) return editingCandidate
1768
+
1769
+ const selectedCandidate = candidates.find(function (candidate) {
1770
+ return selectedElements.indexOf(candidate) !== -1
1771
+ })
1772
+ if (selectedCandidate) return selectedCandidate
1773
+
1774
+ if (candidates.length === 1 || allowFirstMatchFallback !== false) {
1775
+ return candidates[0]
1776
+ }
1777
+
1778
+ return null
1779
+ }
1780
+
1781
+ function findElementByInspectorSource(
1782
+ inspectorSource,
1783
+ preferredElement,
1784
+ allowFirstMatchFallback
1785
+ ) {
1786
+ if (!inspectorSource || typeof inspectorSource !== "object") return null
1787
+
1788
+ const signature = getSharedSignatureFromInspectorSource(inspectorSource)
1789
+ if (!signature) return null
1790
+
1791
+ rebuildSharedTargetIndex()
1792
+ const exactCandidates = sharedTargetIndex.exactBySignature.get(signature)
1793
+ if (Array.isArray(exactCandidates) && exactCandidates.length > 0) {
1794
+ const exactCandidate = selectInspectorSourceCandidate(
1795
+ exactCandidates,
1796
+ preferredElement,
1797
+ allowFirstMatchFallback
1798
+ )
1799
+ if (exactCandidate) return exactCandidate
1800
+ }
1801
+
1802
+ const sharedCandidates = sharedTargetIndex.sharedBySignature.get(signature)
1803
+ if (Array.isArray(sharedCandidates) && sharedCandidates.length > 0) {
1804
+ const sharedCandidate = selectInspectorSourceCandidate(
1805
+ sharedCandidates,
1806
+ preferredElement,
1807
+ allowFirstMatchFallback
1808
+ )
1809
+ if (sharedCandidate) return sharedCandidate
1810
+ }
1811
+
1812
+ try {
1813
+ const exactMatch = safeQuerySelector(
1814
+ "[" +
1815
+ ARCHIE_SOURCE_REF_ATTR +
1816
+ '="' +
1817
+ escapeAttributeSelectorValue(signature) +
1818
+ '"]'
1819
+ )
1820
+ if (exactMatch) return exactMatch
1821
+ } catch (e) {}
1822
+
1823
+ const file =
1824
+ typeof inspectorSource.file === "string"
1825
+ ? inspectorSource.file.trim()
1826
+ : ""
1827
+ const line =
1828
+ typeof inspectorSource.line === "number"
1829
+ ? String(inspectorSource.line)
1830
+ : typeof inspectorSource.line === "string"
1831
+ ? inspectorSource.line.trim()
1832
+ : ""
1833
+ const column =
1834
+ typeof inspectorSource.column === "number"
1835
+ ? String(inspectorSource.column)
1836
+ : typeof inspectorSource.column === "string"
1837
+ ? inspectorSource.column.trim()
1838
+ : ""
1839
+
1840
+ const selectors = []
1841
+ if (column) {
1842
+ selectors.push(
1843
+ '[data-inspector-relative-path="' +
1844
+ escapeAttributeSelectorValue(file) +
1845
+ '"][data-inspector-line="' +
1846
+ escapeAttributeSelectorValue(line) +
1847
+ '"][data-inspector-column="' +
1848
+ escapeAttributeSelectorValue(column) +
1849
+ '"]'
1850
+ )
1851
+ }
1852
+ selectors.push(
1853
+ '[data-inspector-relative-path="' +
1854
+ escapeAttributeSelectorValue(file) +
1855
+ '"][data-inspector-line="' +
1856
+ escapeAttributeSelectorValue(line) +
1857
+ '"]'
1858
+ )
1859
+
1860
+ let fallbackMatch = null
1861
+ for (let index = 0; index < selectors.length; index++) {
1862
+ const selector = selectors[index]
1863
+ try {
1864
+ const matches = safeQuerySelectorAll(selector)
1865
+ if (matches.length === 1) {
1866
+ return matches[0]
1867
+ }
1868
+ if (!fallbackMatch && matches.length > 0) {
1869
+ fallbackMatch = matches[0]
1870
+ }
1871
+ } catch (e) {}
1872
+ }
1873
+ return fallbackMatch
1874
+ }
1875
+
1876
+ function resolveMutationElement(options) {
1877
+ const selector =
1878
+ options && typeof options.selector === "string"
1879
+ ? options.selector.trim()
1880
+ : ""
1881
+ const hasSelector = selector.length > 0
1882
+ const inspectorSource = options ? options.inspectorSource : null
1883
+ const allowSelectedFallback =
1884
+ !!options && options.allowSelectedFallback === true
1885
+ const allowEditingFallback =
1886
+ !!options && options.allowEditingFallback === true
1887
+
1888
+ let element = null
1889
+ let selectorElement = null
1890
+
1891
+ if (hasSelector) {
1892
+ selectorElement = findElementBySelector(selector)
1893
+ }
1894
+ if (!element && inspectorSource) {
1895
+ element = findElementByInspectorSource(
1896
+ inspectorSource,
1897
+ selectorElement,
1898
+ !selectorElement
1899
+ )
1900
+ }
1901
+ if (!element && selectorElement) {
1902
+ element = selectorElement
1903
+ }
1904
+ if (!element && !hasSelector && allowSelectedFallback) {
1905
+ element = getSelectedElement()
1906
+ }
1907
+ if (!element && !hasSelector && allowEditingFallback) {
1908
+ try {
1909
+ element = safeQuerySelector('[data-inspector-editing="true"]')
1910
+ } catch (e) {}
1911
+ }
1912
+
1913
+ return element
1914
+ }
1915
+
1916
+ function extractMetadata(element) {
1917
+ if (!element) return null
1918
+
1919
+ const computed = window.getComputedStyle(element)
1920
+ const rect = element.getBoundingClientRect()
1921
+ const tagName = String(element.tagName || "").toLowerCase()
1922
+ const maxInnerHtmlLength = tagName === "svg" ? 50000 : 500
1923
+
1924
+ const attributes = {}
1925
+ for (let i = 0; i < element.attributes.length; i++) {
1926
+ const attr = element.attributes[i]
1927
+ attributes[attr.name] = attr.value
1928
+ }
1929
+
1930
+ const inspectorInfo = getInspectorInfoFromElement(element)
1931
+ const editorMeta = findNearestEditorMeta(element)
1932
+
1933
+ return {
1934
+ tagName: tagName,
1935
+ id: element.id || null,
1936
+ className:
1937
+ element.className && typeof element.className === "string"
1938
+ ? element.className.trim() || null
1939
+ : null,
1940
+ textContent: element.textContent
1941
+ ? element.textContent.trim().slice(0, 200)
1942
+ : null,
1943
+ innerHTML: element.innerHTML
1944
+ ? element.innerHTML.slice(0, maxInnerHtmlLength)
1945
+ : null,
1946
+ outerHTML: element.outerHTML
1947
+ ? element.outerHTML.slice(0, maxInnerHtmlLength)
1948
+ : null,
1949
+ attributes: attributes,
1950
+ inspectorSource: inspectorInfo,
1951
+ editorMeta: editorMeta ? editorMeta.meta : null,
1952
+ computedStyles: {
1953
+ display: computed.display,
1954
+ position: computed.position,
1955
+ width: computed.width,
1956
+ height: computed.height,
1957
+ margin: computed.margin,
1958
+ padding: computed.padding,
1959
+ fontSize: computed.fontSize,
1960
+ color: computed.color,
1961
+ backgroundColor: computed.backgroundColor,
1962
+ border: computed.border,
1963
+ borderRadius: computed.borderRadius,
1964
+ textAlign: computed.textAlign,
1965
+ fontFamily: computed.fontFamily,
1966
+ fontWeight: computed.fontWeight,
1967
+ lineHeight: computed.lineHeight,
1968
+ letterSpacing: computed.letterSpacing,
1969
+ },
1970
+ boundingRect: {
1971
+ x: rect.x,
1972
+ y: rect.y,
1973
+ width: rect.width,
1974
+ height: rect.height,
1975
+ top: rect.top,
1976
+ left: rect.left,
1977
+ bottom: rect.bottom,
1978
+ right: rect.right,
1979
+ },
1980
+ cssSelector: getStableSelector(element),
1981
+ }
1982
+ }
1983
+
1984
+ var LUCIDE_SVG_NAMESPACE = "http://www.w3.org/2000/svg"
1985
+ var SAFE_LUCIDE_TAGS = {
1986
+ circle: true,
1987
+ ellipse: true,
1988
+ g: true,
1989
+ line: true,
1990
+ path: true,
1991
+ polygon: true,
1992
+ polyline: true,
1993
+ rect: true,
1994
+ }
1995
+ var SAFE_CUSTOM_SVG_TAGS = {
1996
+ svg: true,
1997
+ circle: true,
1998
+ ellipse: true,
1999
+ g: true,
2000
+ line: true,
2001
+ path: true,
2002
+ polygon: true,
2003
+ polyline: true,
2004
+ rect: true,
2005
+ defs: true,
2006
+ clipPath: true,
2007
+ mask: true,
2008
+ symbol: true,
2009
+ use: true,
2010
+ title: true,
2011
+ desc: true,
2012
+ linearGradient: true,
2013
+ radialGradient: true,
2014
+ stop: true,
2015
+ }
2016
+ var MAX_CUSTOM_SVG_DEPTH = 24
2017
+ var MAX_CUSTOM_SVG_NODES = 600
2018
+
2019
+ function isLucideSvgElement(element) {
2020
+ if (!element || !element.tagName) return false
2021
+ if (String(element.tagName).toLowerCase() !== "svg") return false
2022
+ var classAttr = element.getAttribute
2023
+ ? element.getAttribute("class") || ""
2024
+ : ""
2025
+ var hasLucideClass = false
2026
+ if (
2027
+ element.classList &&
2028
+ typeof element.classList.contains === "function"
2029
+ ) {
2030
+ hasLucideClass = element.classList.contains("lucide")
2031
+ } else if (classAttr) {
2032
+ hasLucideClass = classAttr.split(/\s+/).indexOf("lucide") !== -1
2033
+ }
2034
+ return hasLucideClass
2035
+ }
2036
+
2037
+ function toLucideSlug(iconName, explicitSlug) {
2038
+ if (
2039
+ typeof explicitSlug === "string" &&
2040
+ /^[a-z0-9-]{1,120}$/.test(explicitSlug)
2041
+ ) {
2042
+ return explicitSlug
2043
+ }
2044
+ if (typeof iconName !== "string" || !iconName) return ""
2045
+ return iconName
2046
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
2047
+ .replace(/[_\s]+/g, "-")
2048
+ .toLowerCase()
2049
+ .replace(/-+/g, "-")
2050
+ .replace(/^-|-$/g, "")
2051
+ }
2052
+
2053
+ function updateLucideClassName(element, iconSlug) {
2054
+ if (!element || !iconSlug) return
2055
+ var nextIconClass = "lucide-" + iconSlug
2056
+ var classAttr = element.getAttribute("class") || ""
2057
+ var tokens = classAttr
2058
+ .split(/\s+/)
2059
+ .map(function (token) {
2060
+ return token.trim()
2061
+ })
2062
+ .filter(Boolean)
2063
+ var replaced = false
2064
+ for (var i = 0; i < tokens.length; i++) {
2065
+ if (tokens[i].indexOf("lucide-") === 0) {
2066
+ tokens[i] = nextIconClass
2067
+ replaced = true
2068
+ break
2069
+ }
2070
+ }
2071
+ if (tokens.indexOf("lucide") === -1) {
2072
+ tokens.unshift("lucide")
2073
+ }
2074
+ if (!replaced) {
2075
+ tokens.push(nextIconClass)
2076
+ }
2077
+ var uniqueTokens = []
2078
+ for (var j = 0; j < tokens.length; j++) {
2079
+ if (uniqueTokens.indexOf(tokens[j]) === -1) {
2080
+ uniqueTokens.push(tokens[j])
2081
+ }
2082
+ }
2083
+ element.setAttribute("class", uniqueTokens.join(" "))
2084
+ }
2085
+
2086
+ function isSafeSvgAttributeName(name) {
2087
+ if (typeof name !== "string" || !name) return false
2088
+ if (!/^[a-zA-Z_:][-a-zA-Z0-9_:.]*$/.test(name)) return false
2089
+ var lower = name.toLowerCase()
2090
+ if (lower.indexOf("on") === 0) return false
2091
+ return true
2092
+ }
2093
+
2094
+ function isSafeSvgAttributeValue(name, value) {
2095
+ if (typeof value !== "string") return false
2096
+ if (value.length > 500) return false
2097
+ var lowerName = String(name || "").toLowerCase()
2098
+ var lowerValue = value.toLowerCase()
2099
+ if (lowerName === "style") return false
2100
+ if (lowerName === "href" || lowerName === "xlink:href") {
2101
+ if (lowerValue.indexOf("javascript:") !== -1) return false
2102
+ if (lowerValue.indexOf("vbscript:") !== -1) return false
2103
+ if (lowerValue.indexOf("data:text/html") !== -1) return false
2104
+ }
2105
+ return true
2106
+ }
2107
+
2108
+ function sanitizeSvgNode(sourceNode, state, depth) {
2109
+ if (!sourceNode) return null
2110
+ if (depth > MAX_CUSTOM_SVG_DEPTH) return null
2111
+ if (state.count > MAX_CUSTOM_SVG_NODES) return null
2112
+ state.count += 1
2113
+
2114
+ if (sourceNode.nodeType === 3) {
2115
+ var textValue = sourceNode.nodeValue || ""
2116
+ if (!textValue.trim()) return null
2117
+ return document.createTextNode(textValue.slice(0, 500))
2118
+ }
2119
+
2120
+ if (sourceNode.nodeType !== 1) {
2121
+ return null
2122
+ }
2123
+
2124
+ var sourceElement = sourceNode
2125
+ var tagName = sourceElement.tagName
2126
+ if (!SAFE_CUSTOM_SVG_TAGS[tagName]) {
2127
+ return null
2128
+ }
2129
+
2130
+ var sanitizedElement = document.createElementNS(
2131
+ LUCIDE_SVG_NAMESPACE,
2132
+ tagName
2133
+ )
2134
+ var attrs = sourceElement.attributes || []
2135
+ for (var i = 0; i < attrs.length; i++) {
2136
+ var attrName = attrs[i].name
2137
+ var attrValue = attrs[i].value
2138
+ if (!isSafeSvgAttributeName(attrName)) continue
2139
+ if (!isSafeSvgAttributeValue(attrName, String(attrValue))) continue
2140
+ sanitizedElement.setAttribute(
2141
+ attrName,
2142
+ String(attrValue).slice(0, 2000)
2143
+ )
2144
+ }
2145
+
2146
+ var children = sourceElement.childNodes || []
2147
+ for (var j = 0; j < children.length; j++) {
2148
+ var childNode = sanitizeSvgNode(children[j], state, depth + 1)
2149
+ if (!childNode) continue
2150
+ sanitizedElement.appendChild(childNode)
2151
+ }
2152
+
2153
+ return sanitizedElement
2154
+ }
2155
+
2156
+ function parseAndSanitizeCustomSvgMarkup(svgMarkup) {
2157
+ if (typeof svgMarkup !== "string") return null
2158
+ var trimmed = svgMarkup.trim()
2159
+ if (!trimmed || trimmed.length > 50000) return null
2160
+
2161
+ var parsedDocument = null
2162
+ try {
2163
+ parsedDocument = new DOMParser().parseFromString(
2164
+ trimmed,
2165
+ "image/svg+xml"
2166
+ )
2167
+ } catch (e) {
2168
+ return null
2169
+ }
2170
+ if (!parsedDocument || !parsedDocument.documentElement) return null
2171
+ if (parsedDocument.querySelector("parsererror")) return null
2172
+
2173
+ var root = parsedDocument.documentElement
2174
+ if (!root || root.tagName !== "svg") return null
2175
+
2176
+ var state = { count: 0 }
2177
+ var sanitizedRoot = sanitizeSvgNode(root, state, 0)
2178
+ if (!sanitizedRoot || sanitizedRoot.tagName !== "svg") return null
2179
+
2180
+ return sanitizedRoot
2181
+ }
2182
+
2183
+ function applyCustomSvgElement(target, sanitizedSvgElement) {
2184
+ if (!target || !sanitizedSvgElement) return false
2185
+
2186
+ var preservedInspectorAttrs = {}
2187
+ var targetAttrs = target.attributes || []
2188
+ for (var i = 0; i < targetAttrs.length; i++) {
2189
+ var attrName = targetAttrs[i].name
2190
+ if (attrName.indexOf("data-inspector-") === 0) {
2191
+ preservedInspectorAttrs[attrName] = targetAttrs[i].value
2192
+ }
2193
+ }
2194
+
2195
+ while (target.attributes && target.attributes.length > 0) {
2196
+ target.removeAttribute(target.attributes[0].name)
2197
+ }
2198
+
2199
+ var sourceAttrs = sanitizedSvgElement.attributes || []
2200
+ for (var j = 0; j < sourceAttrs.length; j++) {
2201
+ target.setAttribute(sourceAttrs[j].name, sourceAttrs[j].value)
2202
+ }
2203
+
2204
+ var preservedKeys = Object.keys(preservedInspectorAttrs)
2205
+ for (var p = 0; p < preservedKeys.length; p++) {
2206
+ var preservedAttrName = preservedKeys[p]
2207
+ target.setAttribute(
2208
+ preservedAttrName,
2209
+ String(preservedInspectorAttrs[preservedAttrName])
2210
+ )
2211
+ }
2212
+
2213
+ while (target.firstChild) {
2214
+ target.removeChild(target.firstChild)
2215
+ }
2216
+
2217
+ var sourceChildren = sanitizedSvgElement.childNodes || []
2218
+ for (var c = 0; c < sourceChildren.length; c++) {
2219
+ target.appendChild(sourceChildren[c].cloneNode(true))
2220
+ }
2221
+
2222
+ return true
2223
+ }
2224
+
2225
+ function isValidLucideIconNode(iconNode) {
2226
+ if (!Array.isArray(iconNode) || iconNode.length === 0) return false
2227
+ if (iconNode.length > 256) return false
2228
+
2229
+ for (var i = 0; i < iconNode.length; i++) {
2230
+ var item = iconNode[i]
2231
+ if (!Array.isArray(item) || item.length !== 2) return false
2232
+
2233
+ var tagName = item[0]
2234
+ var attrs = item[1]
2235
+ if (typeof tagName !== "string" || !SAFE_LUCIDE_TAGS[tagName])
2236
+ return false
2237
+ if (!attrs || typeof attrs !== "object" || Array.isArray(attrs))
2238
+ return false
2239
+
2240
+ var attrNames = Object.keys(attrs)
2241
+ if (attrNames.length > 40) return false
2242
+ for (var j = 0; j < attrNames.length; j++) {
2243
+ var attrName = attrNames[j]
2244
+ if (attrName === "key") continue
2245
+ if (!isSafeSvgAttributeName(attrName)) return false
2246
+ var rawValue = attrs[attrName]
2247
+ if (rawValue == null) continue
2248
+ if (
2249
+ typeof rawValue !== "string" &&
2250
+ typeof rawValue !== "number" &&
2251
+ typeof rawValue !== "boolean"
2252
+ ) {
2253
+ return false
2254
+ }
2255
+ if (!isSafeSvgAttributeValue(attrName, String(rawValue)))
2256
+ return false
2257
+ }
2258
+ }
2259
+
2260
+ return true
2261
+ }
2262
+
2263
+ function applyLucideBaseAttributes(element) {
2264
+ if (!element || String(element.tagName || "").toLowerCase() !== "svg") {
2265
+ return
2266
+ }
2267
+
2268
+ element.setAttribute("xmlns", LUCIDE_SVG_NAMESPACE)
2269
+ element.setAttribute("viewBox", "0 0 24 24")
2270
+ element.setAttribute("fill", "none")
2271
+ element.setAttribute("stroke", "currentColor")
2272
+ element.setAttribute("stroke-width", "2")
2273
+ element.setAttribute("stroke-linecap", "round")
2274
+ element.setAttribute("stroke-linejoin", "round")
2275
+ }
2276
+
2277
+ function applyLucideIconNode(element, iconNode) {
2278
+ if (!element || !isValidLucideIconNode(iconNode)) return false
2279
+ applyLucideBaseAttributes(element)
2280
+ while (element.firstChild) {
2281
+ element.removeChild(element.firstChild)
2282
+ }
2283
+
2284
+ for (var i = 0; i < iconNode.length; i++) {
2285
+ var item = iconNode[i]
2286
+ var tagName = item[0]
2287
+ var attrs = item[1]
2288
+ var child = document.createElementNS(LUCIDE_SVG_NAMESPACE, tagName)
2289
+
2290
+ var attrNames = Object.keys(attrs)
2291
+ for (var j = 0; j < attrNames.length; j++) {
2292
+ var attrName = attrNames[j]
2293
+ if (attrName === "key") continue
2294
+ var rawValue = attrs[attrName]
2295
+ if (rawValue == null) continue
2296
+ child.setAttribute(attrName, String(rawValue).slice(0, 500))
2297
+ }
2298
+
2299
+ element.appendChild(child)
2300
+ }
2301
+
2302
+ return true
2303
+ }
2304
+
2305
+ function coerceSvgElement(element) {
2306
+ if (!element) return null
2307
+ if (String(element.tagName || "").toLowerCase() === "svg") {
2308
+ return element
2309
+ }
2310
+
2311
+ if (typeof element.closest === "function") {
2312
+ var parentSvg = element.closest("svg")
2313
+ if (parentSvg) return parentSvg
2314
+ }
2315
+
2316
+ if (typeof element.querySelector === "function") {
2317
+ var nestedSvg = element.querySelector("svg")
2318
+ if (nestedSvg) return nestedSvg
2319
+ }
2320
+
2321
+ return null
2322
+ }
2323
+
2324
+ function resolveLucideTargetElement(selector, inspectorSource) {
2325
+ var element = coerceSvgElement(
2326
+ resolveMutationElement({
2327
+ selector: selector,
2328
+ inspectorSource: inspectorSource,
2329
+ allowSelectedFallback: !selector,
2330
+ allowEditingFallback: !selector,
2331
+ })
2332
+ )
2333
+ if (
2334
+ !element &&
2335
+ !selector &&
2336
+ !inspectorSource &&
2337
+ selectedElements &&
2338
+ selectedElements.length > 0
2339
+ ) {
2340
+ for (var i = selectedElements.length - 1; i >= 0; i--) {
2341
+ var candidate = coerceSvgElement(selectedElements[i])
2342
+ if (candidate) {
2343
+ element = candidate
2344
+ break
2345
+ }
2346
+ }
2347
+ }
2348
+ return element
2349
+ }
2350
+
2351
+ function resolveSvgTargetElement(selector, inspectorSource) {
2352
+ var element = coerceSvgElement(
2353
+ resolveMutationElement({
2354
+ selector: selector,
2355
+ inspectorSource: inspectorSource,
2356
+ allowSelectedFallback: !selector,
2357
+ allowEditingFallback: !selector,
2358
+ })
2359
+ )
2360
+ if (
2361
+ !element &&
2362
+ !selector &&
2363
+ !inspectorSource &&
2364
+ selectedElements &&
2365
+ selectedElements.length > 0
2366
+ ) {
2367
+ for (var i = selectedElements.length - 1; i >= 0; i--) {
2368
+ var candidate = coerceSvgElement(selectedElements[i])
2369
+ if (candidate) {
2370
+ element = candidate
2371
+ break
2372
+ }
2373
+ }
2374
+ }
2375
+ return element
2376
+ }
2377
+
2378
+ function getSvgSharedTargets(element, predicate) {
2379
+ var targets = resolveSharedTargets(element)
2380
+ if (targets.length === 0 && element) {
2381
+ targets = [element]
2382
+ }
2383
+ if (!predicate) return targets
2384
+ return targets.filter(function (candidate) {
2385
+ return predicate(candidate)
2386
+ })
2387
+ }
2388
+
2389
+ function getLucideSharedTargets(element) {
2390
+ return getSvgSharedTargets(element, isLucideSvgElement)
2391
+ }
2392
+
2393
+ var pendingArrangeUndoRequestId = null
2394
+
2395
+ const runtimeBridgeHandler = function (event) {
2396
+ const detail = event && event.detail ? event.detail : null
2397
+ if (!detail || typeof detail !== "object" || typeof detail.type !== "string") {
2398
+ return
2399
+ }
2400
+
2401
+ if (detail.type === "design-mode-changed") {
2402
+ postMessageToParent({
2403
+ type: "EDITOR_MODE_CHANGED",
2404
+ mode: detail.mode || "off",
2405
+ })
2406
+ return
2407
+ }
2408
+
2409
+ if (detail.type === "element-moved") {
2410
+ postMessageToParent({
2411
+ type: "ELEMENT_MOVED",
2412
+ payload: detail,
2413
+ })
2414
+ return
2415
+ }
2416
+
2417
+ if (detail.type === "element-resized") {
2418
+ postMessageToParent({
2419
+ type: "ELEMENT_RESIZED",
2420
+ payload: detail,
2421
+ })
2422
+ return
2423
+ }
2424
+
2425
+ if (detail.type === "element-inserted") {
2426
+ postMessageToParent({
2427
+ type: "ELEMENT_INSERTED",
2428
+ payload: detail,
2429
+ })
2430
+ return
2431
+ }
2432
+
2433
+ if (detail.type === "arrange-session-changed") {
2434
+ postMessageToParent({
2435
+ type: "ARRANGE_SESSION_CHANGED",
2436
+ session: detail.session,
2437
+ })
2438
+ return
2439
+ }
2440
+
2441
+ if (detail.type === "arrange-undo-applied") {
2442
+ postMessageToParent({
2443
+ type: "ARRANGE_UNDO_APPLIED",
2444
+ requestId: pendingArrangeUndoRequestId,
2445
+ success: !!detail.success,
2446
+ updatedAt: detail.updatedAt || Date.now(),
2447
+ })
2448
+ pendingArrangeUndoRequestId = null
2449
+ return
2450
+ }
2451
+
2452
+ if (detail.type === "design-mode-error") {
2453
+ postMessageToParent({
2454
+ type: "DESIGN_MODE_ERROR",
2455
+ error:
2456
+ typeof detail.error === "string" && detail.error
2457
+ ? detail.error
2458
+ : "Unknown design mode error",
2459
+ payload: detail,
2460
+ })
2461
+ }
2462
+ }
2463
+
2464
+ window._inspectorRuntimeBridgeHandler = runtimeBridgeHandler
2465
+ window.addEventListener("archie:dnd-runtime-event", runtimeBridgeHandler)
2466
+
2467
+ const messageHandler = function (event) {
2468
+ if (!isOriginAllowed(event.origin)) return
2469
+ adoptHostTargetOrigin(event.origin)
2470
+ if (
2471
+ !event.data ||
2472
+ typeof event.data !== "object" ||
2473
+ typeof event.data.type !== "string"
2474
+ )
2475
+ return
2476
+
2477
+ // Log all messages for debugging (excluding frequent position updates)
2478
+ if (
2479
+ event.data &&
2480
+ event.data.type &&
2481
+ event.data.type !== "GET_ELEMENT_POSITION"
2482
+ ) {
2483
+ inspectorLog(
2484
+ "[Inspector] Received message:",
2485
+ event.data.type,
2486
+ event.data
2487
+ )
2488
+ }
2489
+
2490
+ if (event.data && event.data.type === "SET_EDITOR_MODE") {
2491
+ setEditorModeFromHost(event.data.mode || "off", {
2492
+ hostControlledHistory: event.data.hostControlledHistory !== false,
2493
+ theme: event.data.theme || null,
2494
+ })
2495
+ } else if (event.data && event.data.type === "SYNC_EDITOR_STATE") {
2496
+ setEditorModeFromHost(event.data.mode || "off", {
2497
+ hostControlledHistory: event.data.hostControlledHistory !== false,
2498
+ theme: event.data.theme || null,
2499
+ })
2500
+ } else if (event.data && event.data.type === "SET_INSPECTOR_THEME") {
2501
+ updateInspectorTheme(event.data.theme || null)
2502
+ } else if (event.data && event.data.type === "APPLY_MOVE") {
2503
+ var moveApplied = applyMoveReplay(event.data)
2504
+ postMessageToParent({
2505
+ type: moveApplied ? "ELEMENT_MOVED_APPLIED" : "ELEMENT_MOVE_FAILED",
2506
+ requestId: event.data.requestId,
2507
+ selector: event.data.selector || null,
2508
+ })
2509
+ } else if (event.data && event.data.type === "APPLY_RESIZE") {
2510
+ var resizeApplied = applyResizeReplay(event.data)
2511
+ postMessageToParent({
2512
+ type: resizeApplied
2513
+ ? "ELEMENT_RESIZED_APPLIED"
2514
+ : "ELEMENT_RESIZE_FAILED",
2515
+ requestId: event.data.requestId,
2516
+ selector: event.data.selector || null,
2517
+ })
2518
+ } else if (event.data && event.data.type === "ARRANGE_UNDO") {
2519
+ pendingArrangeUndoRequestId =
2520
+ typeof event.data.requestId === "string" ? event.data.requestId : null
2521
+ dispatchHostCommand({ type: "request-undo" })
2522
+ } else if (event.data && event.data.type === "START_INSPECTION") {
2523
+ setEditorModeFromHost("inspect", {
2524
+ hostControlledHistory: true,
2525
+ })
2526
+ } else if (event.data && event.data.type === "STOP_INSPECTION") {
2527
+ setEditorModeFromHost("off", {
2528
+ hostControlledHistory: true,
2529
+ })
2530
+ } else if (event.data && event.data.type === "SHOW_SELECTED_ELEMENT") {
2531
+ const { selector } = event.data
2532
+
2533
+ // Try to get element by selector first, then fall back to getSelectedElement
2534
+ let element = null
2535
+ try {
2536
+ element = safeQuerySelector(selector)
2537
+ } catch (e) {
2538
+ // Invalid selector, try getSelectedElement
2539
+ element = getSelectedElement()
2540
+ }
2541
+
2542
+ if (!element) {
2543
+ element = getSelectedElement()
2544
+ }
2545
+
2546
+ if (element) {
2547
+ removeSelectedBadge()
2548
+ updateSharedHighlights()
2549
+ removeHighlight()
2550
+ selectedElement = element
2551
+ window._inspectorSelectedElement = element
2552
+ if (window.__ARCHIE_INSPECTOR__)
2553
+ window.__ARCHIE_INSPECTOR__.selectedElement = element
2554
+ isElementSelected = true // Mark that an element is selected
2555
+
2556
+ applyInspectorStyles(element)
2557
+ createSelectedBadge(element)
2558
+ setupBadgePositionListeners(element)
2559
+ }
2560
+ } else if (event.data && event.data.type === "SELECT_PARENT_ELEMENT") {
2561
+ const element = getSelectedElement()
2562
+
2563
+ if (
2564
+ element &&
2565
+ element.parentElement &&
2566
+ element.parentElement !== document.body &&
2567
+ element.parentElement !== document.documentElement
2568
+ ) {
2569
+ const parentElement = element.parentElement
2570
+ const metadata = extractMetadata(parentElement)
2571
+
2572
+ // Remove selection from current element
2573
+ if (selectedElement) {
2574
+ removeInspectorStyles(selectedElement)
2575
+ cleanupElementListeners(selectedElement)
2576
+ }
2577
+
2578
+ // Select parent element
2579
+ selectedElement = parentElement
2580
+ window._inspectorSelectedElement = parentElement
2581
+ if (window.__ARCHIE_INSPECTOR__)
2582
+ window.__ARCHIE_INSPECTOR__.selectedElement = parentElement
2583
+ isElementSelected = true // Mark that an element is selected
2584
+
2585
+ applyInspectorStyles(parentElement)
2586
+ setupBadgePositionListeners(parentElement)
2587
+
2588
+ postMessageToParent({
2589
+ type: "ELEMENT_SELECTED",
2590
+ metadata: metadata,
2591
+ })
2592
+ }
2593
+ } else if (event.data && event.data.type === "HIDE_SELECTED_ELEMENT") {
2594
+ if (selectedElement) {
2595
+ deleteElementCleanup(selectedElement)
2596
+ }
2597
+ if (selectedElement) {
2598
+ // Only remove inspector-specific styles (border is preserved)
2599
+ removeInspectorStyles(selectedElement)
2600
+
2601
+ // Verify user styles are still applied (use getComputedStyle to check actual values)
2602
+ const computed = window.getComputedStyle(selectedElement)
2603
+ const borderWidth = computed.borderWidth
2604
+ const borderColor = computed.borderColor
2605
+ const borderStyle = computed.borderStyle
2606
+ const inlineBorderWidth =
2607
+ selectedElement.style.getPropertyValue("border-width")
2608
+ const inlineBorderColor =
2609
+ selectedElement.style.getPropertyValue("border-color")
2610
+ const inlineBorderStyle =
2611
+ selectedElement.style.getPropertyValue("border-style")
2612
+ inspectorLog("[Inspector] Hiding element, user styles check:", {
2613
+ computed: { borderWidth, borderColor, borderStyle },
2614
+ inline: {
2615
+ borderWidth: inlineBorderWidth,
2616
+ borderColor: inlineBorderColor,
2617
+ borderStyle: inlineBorderStyle,
2618
+ },
2619
+ })
2620
+ }
2621
+ removeSelectedBadge()
2622
+ updateSharedHighlights()
2623
+ isElementSelected = false // Mark that no element is selected
2624
+ } else if (event.data && event.data.type === "GET_ELEMENT_POSITION") {
2625
+ const { selector } = event.data
2626
+ let element = null
2627
+
2628
+ // Try to find the element by selector
2629
+ if (selector) {
2630
+ try {
2631
+ element = safeQuerySelector(selector)
2632
+ } catch (e) {
2633
+ // Invalid selector or element not found
2634
+ }
2635
+ }
2636
+
2637
+ // Fallback to selectedElement if no selector provided or element not found
2638
+ if (!element) {
2639
+ element = getSelectedElement()
2640
+ }
2641
+
2642
+ if (element) {
2643
+ if (element.getAttribute("data-inspector-editing") === "true") {
2644
+ applyInspectorStyles(element)
2645
+ }
2646
+
2647
+ const rect = element.getBoundingClientRect()
2648
+ postMessageToParent({
2649
+ type: "ELEMENT_POSITION_UPDATE",
2650
+ selector: selector,
2651
+ boundingRect: {
2652
+ x: rect.x,
2653
+ y: rect.y,
2654
+ width: rect.width,
2655
+ height: rect.height,
2656
+ top: rect.top,
2657
+ left: rect.left,
2658
+ bottom: rect.bottom,
2659
+ right: rect.right,
2660
+ },
2661
+ })
2662
+ }
2663
+ } else if (event.data && event.data.type === "APPLY_STYLE_CHANGE") {
2664
+ const { selector, property, value } = event.data
2665
+
2666
+ inspectorLog("[Inspector] Received APPLY_STYLE_CHANGE:", {
2667
+ selector,
2668
+ property,
2669
+ value,
2670
+ })
2671
+
2672
+ if (
2673
+ !property ||
2674
+ typeof property !== "string" ||
2675
+ property.length > 100 ||
2676
+ !isStyleValueSafe(value)
2677
+ ) {
2678
+ inspectorWarn(
2679
+ "[Inspector] Rejected invalid or unsafe style property/value"
2680
+ )
2681
+ postMessageToParent({
2682
+ type: "STYLE_CHANGE_FAILED",
2683
+ requestId: event.data.requestId,
2684
+ selector,
2685
+ property: typeof property === "string" ? property : "",
2686
+ value: value != null ? String(value).slice(0, 100) : "",
2687
+ error: !isStyleValueSafe(value)
2688
+ ? "Invalid or unsafe style value"
2689
+ : "Invalid property",
2690
+ })
2691
+ return
2692
+ }
2693
+
2694
+ let element = resolveMutationElement({
2695
+ selector: selector,
2696
+ inspectorSource: event.data.inspectorSource,
2697
+ allowSelectedFallback: !selector,
2698
+ allowEditingFallback: !selector,
2699
+ })
2700
+
2701
+ if (!element) {
2702
+ inspectorWarn(
2703
+ "[Inspector] Element not found for selector:",
2704
+ selector,
2705
+ "property:",
2706
+ property,
2707
+ "inspectorSource:",
2708
+ event.data.inspectorSource
2709
+ )
2710
+
2711
+ postMessageToParent({
2712
+ type: "STYLE_CHANGE_FAILED",
2713
+ requestId: event.data.requestId,
2714
+ selector,
2715
+ property,
2716
+ value,
2717
+ error: "Element not found",
2718
+ })
2719
+ return
2720
+ }
2721
+
2722
+ const kebabProperty = property
2723
+ .replace(/([A-Z])/g, "-$1")
2724
+ .toLowerCase()
2725
+ element.style.setProperty(kebabProperty, value, "important")
2726
+
2727
+ const camelProperty = property.replace(/-([a-z])/g, (g) =>
2728
+ g[1].toUpperCase()
2729
+ )
2730
+ if (element.style.hasOwnProperty(camelProperty)) {
2731
+ element.style[camelProperty] = value
2732
+ }
2733
+ resolveSharedTargets(element).forEach((sharedEl) => {
2734
+ if (sharedEl === element) return
2735
+
2736
+ sharedEl.style.setProperty(kebabProperty, value, "important")
2737
+ if (sharedEl.style.hasOwnProperty(camelProperty)) {
2738
+ sharedEl.style[camelProperty] = value
2739
+ }
2740
+ })
2741
+
2742
+ // Verify the style was actually applied
2743
+ const appliedValue = element.style.getPropertyValue(kebabProperty)
2744
+ inspectorLog(
2745
+ "[Inspector] Applied style:",
2746
+ property,
2747
+ "=",
2748
+ value,
2749
+ "to element:",
2750
+ selector,
2751
+ "found:",
2752
+ !!element,
2753
+ "verified:",
2754
+ appliedValue
2755
+ )
2756
+
2757
+ postMessageToParent({
2758
+ type: "STYLE_CHANGE_APPLIED",
2759
+ requestId: event.data.requestId,
2760
+ selector,
2761
+ property,
2762
+ value,
2763
+ })
2764
+ } else if (event.data && event.data.type === "SET_ELEMENT_CLASS_NAME") {
2765
+ const { selector, className, inspectorSource } = event.data
2766
+
2767
+ inspectorLog("[Inspector] Received SET_ELEMENT_CLASS_NAME:", {
2768
+ selector,
2769
+ className,
2770
+ })
2771
+
2772
+ let element = resolveMutationElement({
2773
+ selector: selector,
2774
+ inspectorSource: inspectorSource,
2775
+ allowSelectedFallback: !selector,
2776
+ allowEditingFallback: !selector,
2777
+ })
2778
+
2779
+ if (!element) {
2780
+ postMessageToParent({
2781
+ type: "STYLE_CHANGE_FAILED",
2782
+ requestId: event.data.requestId,
2783
+ selector,
2784
+ error: "Element not found for class change",
2785
+ })
2786
+ return
2787
+ }
2788
+
2789
+ var classStr =
2790
+ typeof className === "string" ? className.slice(0, 2000) : ""
2791
+ element.className = classStr.trim().replace(/\s+/g, " ")
2792
+
2793
+ resolveSharedTargets(element).forEach((sharedEl) => {
2794
+ if (sharedEl === element) return
2795
+ sharedEl.className = classStr.trim().replace(/\s+/g, " ")
2796
+ })
2797
+
2798
+ postMessageToParent({
2799
+ type: "STYLE_CHANGE_APPLIED",
2800
+ requestId: event.data.requestId,
2801
+ selector,
2802
+ className: classStr,
2803
+ action: "set",
2804
+ })
2805
+ } else if (
2806
+ event.data &&
2807
+ event.data.type === "APPLY_CLASS_NAME_CHANGE"
2808
+ ) {
2809
+ const { selector, className, action, inspectorSource } = event.data
2810
+
2811
+ inspectorLog("[Inspector] Received APPLY_CLASS_NAME_CHANGE:", {
2812
+ selector,
2813
+ className,
2814
+ action,
2815
+ })
2816
+
2817
+ let element = resolveMutationElement({
2818
+ selector: selector,
2819
+ inspectorSource: inspectorSource,
2820
+ allowSelectedFallback: !selector,
2821
+ allowEditingFallback: !selector,
2822
+ })
2823
+
2824
+ if (!element) {
2825
+ postMessageToParent({
2826
+ type: "STYLE_CHANGE_FAILED",
2827
+ requestId: event.data.requestId,
2828
+ selector,
2829
+ error: "Element not found for class change",
2830
+ })
2831
+ return
2832
+ }
2833
+
2834
+ // Apply Class Change
2835
+ var classStr =
2836
+ typeof className === "string" ? className.slice(0, 2000) : ""
2837
+ const classes = classStr.split(/\s+/).filter(Boolean).slice(0, 100)
2838
+ classes.forEach((cls) => {
2839
+ if (action === "remove") {
2840
+ element.classList.remove(cls)
2841
+ } else if (action === "toggle") {
2842
+ element.classList.toggle(cls)
2843
+ } else {
2844
+ element.classList.add(cls)
2845
+ }
2846
+ })
2847
+
2848
+ resolveSharedTargets(element).forEach((sharedEl) => {
2849
+ if (sharedEl === element) return
2850
+ classes.forEach((cls) => {
2851
+ if (action === "remove") {
2852
+ sharedEl.classList.remove(cls)
2853
+ } else if (action === "toggle") {
2854
+ sharedEl.classList.toggle(cls)
2855
+ } else {
2856
+ sharedEl.classList.add(cls)
2857
+ }
2858
+ })
2859
+ })
2860
+
2861
+ postMessageToParent({
2862
+ type: "STYLE_CHANGE_APPLIED",
2863
+ requestId: event.data.requestId,
2864
+ selector,
2865
+ className,
2866
+ action,
2867
+ })
2868
+ } else if (event.data && event.data.type === "APPLY_TEXT_CHANGE") {
2869
+ const { selector, text, inspectorSource } = event.data
2870
+ if (
2871
+ text != null &&
2872
+ typeof text === "string" &&
2873
+ text.length > 500000
2874
+ ) {
2875
+ postMessageToParent({
2876
+ type: "TEXT_CHANGE_FAILED",
2877
+ requestId: event.data.requestId,
2878
+ selector,
2879
+ text: "",
2880
+ error: "Text value too long",
2881
+ })
2882
+ return
2883
+ }
2884
+ let element = resolveMutationElement({
2885
+ selector: selector,
2886
+ inspectorSource: inspectorSource,
2887
+ allowSelectedFallback: !selector,
2888
+ allowEditingFallback: !selector,
2889
+ })
2890
+
2891
+ if (!element) {
2892
+ postMessageToParent({
2893
+ type: "TEXT_CHANGE_FAILED",
2894
+ requestId: event.data.requestId,
2895
+ selector,
2896
+ text,
2897
+ error: "Element not found",
2898
+ })
2899
+ return
2900
+ }
2901
+
2902
+ element.textContent = text != null ? String(text) : ""
2903
+ postMessageToParent({
2904
+ type: "TEXT_CHANGE_APPLIED",
2905
+ requestId: event.data.requestId,
2906
+ selector,
2907
+ text,
2908
+ })
2909
+ } else if (event.data && event.data.type === "REPLACE_LUCIDE_ICON") {
2910
+ var lucideSelector = event.data.selector
2911
+ var lucideIconName = event.data.iconName
2912
+ var lucideIconSlug = toLucideSlug(lucideIconName, event.data.iconSlug)
2913
+ var lucideIconNode = event.data.iconNode
2914
+
2915
+ if (!lucideIconSlug || !/^[a-z0-9-]{1,120}$/.test(lucideIconSlug)) {
2916
+ postMessageToParent({
2917
+ type: "LUCIDE_ICON_REPLACE_FAILED",
2918
+ requestId: event.data.requestId,
2919
+ selector:
2920
+ typeof lucideSelector === "string" ? lucideSelector : "",
2921
+ iconName:
2922
+ typeof lucideIconName === "string" ? lucideIconName : "",
2923
+ error: "Invalid icon slug",
2924
+ })
2925
+ return
2926
+ }
2927
+
2928
+ var element = resolveLucideTargetElement(
2929
+ lucideSelector,
2930
+ event.data.inspectorSource
2931
+ )
2932
+ if (!element) {
2933
+ postMessageToParent({
2934
+ type: "LUCIDE_ICON_REPLACE_FAILED",
2935
+ requestId: event.data.requestId,
2936
+ selector:
2937
+ typeof lucideSelector === "string" ? lucideSelector : "",
2938
+ iconName:
2939
+ typeof lucideIconName === "string" ? lucideIconName : "",
2940
+ iconSlug: lucideIconSlug,
2941
+ error: "SVG target not found",
2942
+ })
2943
+ return
2944
+ }
2945
+
2946
+ var targets = getLucideSharedTargets(element)
2947
+ if (targets.length === 0) {
2948
+ targets.push(element)
2949
+ }
2950
+
2951
+ var canApplyNode = isValidLucideIconNode(lucideIconNode)
2952
+ var replacedNodeCount = 0
2953
+ for (var idx = 0; idx < targets.length; idx++) {
2954
+ var target = targets[idx]
2955
+ updateLucideClassName(target, lucideIconSlug)
2956
+ if (canApplyNode && applyLucideIconNode(target, lucideIconNode)) {
2957
+ replacedNodeCount++
2958
+ }
2959
+ if (target.getAttribute("data-inspector-editing") === "true") {
2960
+ applyInspectorStyles(target)
2961
+ }
2962
+ }
2963
+
2964
+ postMessageToParent({
2965
+ type: "LUCIDE_ICON_REPLACED",
2966
+ requestId: event.data.requestId,
2967
+ selector: getStableSelector(element),
2968
+ iconName: typeof lucideIconName === "string" ? lucideIconName : "",
2969
+ iconSlug: lucideIconSlug,
2970
+ nodeReplaced: replacedNodeCount > 0,
2971
+ targetsCount: targets.length,
2972
+ })
2973
+ } else if (
2974
+ event.data &&
2975
+ event.data.type === "REPLACE_CUSTOM_SVG_ICON"
2976
+ ) {
2977
+ var customSvgSelector = event.data.selector
2978
+ var customSvgMarkup = event.data.svgMarkup
2979
+
2980
+ var sanitizedSvg = parseAndSanitizeCustomSvgMarkup(customSvgMarkup)
2981
+ if (!sanitizedSvg) {
2982
+ postMessageToParent({
2983
+ type: "CUSTOM_SVG_ICON_REPLACE_FAILED",
2984
+ requestId: event.data.requestId,
2985
+ selector:
2986
+ typeof customSvgSelector === "string" ? customSvgSelector : "",
2987
+ error: "Invalid custom SVG markup",
2988
+ })
2989
+ return
2990
+ }
2991
+
2992
+ var svgTarget = resolveSvgTargetElement(
2993
+ customSvgSelector,
2994
+ event.data.inspectorSource
2995
+ )
2996
+ if (!svgTarget) {
2997
+ postMessageToParent({
2998
+ type: "CUSTOM_SVG_ICON_REPLACE_FAILED",
2999
+ requestId: event.data.requestId,
3000
+ selector:
3001
+ typeof customSvgSelector === "string" ? customSvgSelector : "",
3002
+ error: "SVG target not found",
3003
+ })
3004
+ return
3005
+ }
3006
+
3007
+ var svgTargets = getSvgSharedTargets(svgTarget, function (candidate) {
3008
+ return (
3009
+ !!candidate &&
3010
+ String(candidate.tagName || "").toLowerCase() === "svg"
3011
+ )
3012
+ })
3013
+ if (svgTargets.length === 0) {
3014
+ svgTargets.push(svgTarget)
3015
+ }
3016
+
3017
+ for (var customIdx = 0; customIdx < svgTargets.length; customIdx++) {
3018
+ var customTarget = svgTargets[customIdx]
3019
+ applyCustomSvgElement(customTarget, sanitizedSvg)
3020
+ if (
3021
+ customTarget.getAttribute("data-inspector-editing") === "true"
3022
+ ) {
3023
+ applyInspectorStyles(customTarget)
3024
+ }
3025
+ }
3026
+
3027
+ postMessageToParent({
3028
+ type: "CUSTOM_SVG_ICON_REPLACED",
3029
+ requestId: event.data.requestId,
3030
+ selector: getStableSelector(svgTarget),
3031
+ targetsCount: svgTargets.length,
3032
+ })
3033
+ } else if (event.data && event.data.type === "REMOVE_ELEMENT") {
3034
+ // Contract: one REMOVE_ELEMENT → save parentSelector, nextSiblingSelector, outerHTML → remove → ELEMENT_REMOVED
3035
+ const { selector, inspectorSource } = event.data
3036
+ let element = resolveMutationElement({
3037
+ selector: selector,
3038
+ inspectorSource: inspectorSource,
3039
+ allowSelectedFallback: !selector,
3040
+ allowEditingFallback: !selector,
3041
+ })
3042
+
3043
+ if (!element) {
3044
+ postMessageToParent({
3045
+ type: "ELEMENT_REMOVAL_FAILED",
3046
+ requestId: event.data.requestId,
3047
+ selector,
3048
+ error: "Element not found",
3049
+ })
3050
+ return
3051
+ }
3052
+
3053
+ if (
3054
+ element === document.body ||
3055
+ element === document.documentElement
3056
+ ) {
3057
+ postMessageToParent({
3058
+ type: "ELEMENT_REMOVAL_FAILED",
3059
+ requestId: event.data.requestId,
3060
+ selector,
3061
+ error: "Cannot remove body or html",
3062
+ })
3063
+ return
3064
+ }
3065
+
3066
+ var parentEl = element.parentElement
3067
+ var parentSelector = parentEl ? getStableSelector(parentEl) : ""
3068
+ var nextSibling = element.nextElementSibling
3069
+ var nextSiblingSelector = nextSibling
3070
+ ? getStableSelector(nextSibling)
3071
+ : null
3072
+ var outerHTML = element.outerHTML
3073
+ var removedMetadata = extractMetadata(element)
3074
+
3075
+ element.remove()
3076
+
3077
+ var index = selectedElements.indexOf(element)
3078
+ if (index > -1) selectedElements.splice(index, 1)
3079
+
3080
+ if (selectedElements.length === 0) {
3081
+ selectedElement = null
3082
+ window._inspectorSelectedElement = null
3083
+ isElementSelected = false
3084
+ removeSelectedBadge()
3085
+ } else {
3086
+ selectedElement = selectedElements[selectedElements.length - 1]
3087
+ window._inspectorSelectedElement = selectedElement
3088
+ removeSelectedBadge()
3089
+ selectedElements.forEach((el) => createSelectedBadge(el))
3090
+ }
3091
+
3092
+ updateSharedHighlights()
3093
+ postMessageToParent({
3094
+ type: "ELEMENT_REMOVED",
3095
+ requestId: event.data.requestId,
3096
+ selector: selector,
3097
+ parentSelector: parentSelector,
3098
+ nextSiblingSelector: nextSiblingSelector,
3099
+ outerHTML: outerHTML,
3100
+ inspectorSource: removedMetadata
3101
+ ? removedMetadata.inspectorSource
3102
+ : null,
3103
+ tagName: removedMetadata ? removedMetadata.tagName : null,
3104
+ id: removedMetadata ? removedMetadata.id : null,
3105
+ className: removedMetadata ? removedMetadata.className : null,
3106
+ textContent: removedMetadata ? removedMetadata.textContent : null,
3107
+ })
3108
+ } else if (event.data && event.data.type === "RESTORE_ELEMENT") {
3109
+ // Contract: one RESTORE_ELEMENT → find parent, create node from outerHTML, insert before nextSibling or append
3110
+ var _parentSelector = event.data.parentSelector
3111
+ var _nextSiblingSelector = event.data.nextSiblingSelector
3112
+ var _outerHTML = event.data.outerHTML
3113
+
3114
+ if (!_parentSelector || !_outerHTML) {
3115
+ postMessageToParent({
3116
+ type: "ELEMENT_RESTORE_FAILED",
3117
+ requestId: event.data.requestId,
3118
+ error: "Missing parentSelector or outerHTML",
3119
+ })
3120
+ return
3121
+ }
3122
+
3123
+ var parentNode = null
3124
+ try {
3125
+ parentNode = safeQuerySelector(_parentSelector)
3126
+ } catch (e) {}
3127
+
3128
+ var usedBodyFallback = false
3129
+ if (!parentNode && document.body) {
3130
+ parentNode = document.body
3131
+ usedBodyFallback = true
3132
+ }
3133
+ if (!parentNode) {
3134
+ postMessageToParent({
3135
+ type: "ELEMENT_RESTORE_FAILED",
3136
+ requestId: event.data.requestId,
3137
+ error: "Parent not found: " + _parentSelector,
3138
+ })
3139
+ return
3140
+ }
3141
+
3142
+ var nextSiblingNode = null
3143
+ if (!usedBodyFallback && _nextSiblingSelector) {
3144
+ try {
3145
+ nextSiblingNode = safeQuerySelector(_nextSiblingSelector)
3146
+ } catch (e) {}
3147
+ }
3148
+
3149
+ var wrap = document.createElement("div")
3150
+ wrap.innerHTML = _outerHTML
3151
+ var newNode = wrap.firstElementChild
3152
+ if (!newNode) {
3153
+ postMessageToParent({
3154
+ type: "ELEMENT_RESTORE_FAILED",
3155
+ requestId: event.data.requestId,
3156
+ error: "Could not parse outerHTML",
3157
+ })
3158
+ return
3159
+ }
3160
+
3161
+ try {
3162
+ if (nextSiblingNode) {
3163
+ parentNode.insertBefore(newNode, nextSiblingNode)
3164
+ } else {
3165
+ parentNode.appendChild(newNode)
3166
+ }
3167
+ } catch (err) {
3168
+ postMessageToParent({
3169
+ type: "ELEMENT_RESTORE_FAILED",
3170
+ requestId: event.data.requestId,
3171
+ error: err && err.message ? err.message : "Insert failed",
3172
+ })
3173
+ return
3174
+ }
3175
+
3176
+ postMessageToParent({
3177
+ type: "ELEMENT_RESTORED",
3178
+ requestId: event.data.requestId,
3179
+ parentSelector: _parentSelector,
3180
+ restoredSelector: getStableSelector(newNode),
3181
+ usedBodyFallback: usedBodyFallback,
3182
+ })
3183
+ } else if (event.data && event.data.type === "REPLACE_OUTER_HTML") {
3184
+ var replaceSelector =
3185
+ typeof event.data.selector === "string" ? event.data.selector : ""
3186
+ var replaceOuterHTML =
3187
+ typeof event.data.outerHTML === "string" ? event.data.outerHTML : ""
3188
+ var targetEl = resolveMutationElement({
3189
+ selector: replaceSelector,
3190
+ inspectorSource: event.data.inspectorSource,
3191
+ allowSelectedFallback: !replaceSelector,
3192
+ allowEditingFallback: !replaceSelector,
3193
+ })
3194
+ if (!targetEl && !replaceSelector && selectedElements.length > 0) {
3195
+ targetEl = selectedElements[selectedElements.length - 1]
3196
+ }
3197
+
3198
+ if (!targetEl || !replaceOuterHTML) {
3199
+ postMessageToParent({
3200
+ type: "OUTER_HTML_REPLACE_FAILED",
3201
+ requestId: event.data.requestId,
3202
+ selector: replaceSelector,
3203
+ error: !replaceOuterHTML
3204
+ ? "Missing outerHTML payload"
3205
+ : "Target element not found",
3206
+ })
3207
+ return
3208
+ }
3209
+
3210
+ var wrapRE = document.createElement("div")
3211
+ wrapRE.innerHTML = replaceOuterHTML
3212
+ var newEl = wrapRE.firstElementChild
3213
+
3214
+ if (!newEl || !targetEl.parentNode) {
3215
+ postMessageToParent({
3216
+ type: "OUTER_HTML_REPLACE_FAILED",
3217
+ requestId: event.data.requestId,
3218
+ selector: replaceSelector,
3219
+ error: !newEl ? "Could not parse outerHTML" : "Missing parent",
3220
+ })
3221
+ return
3222
+ }
3223
+
3224
+ targetEl.parentNode.replaceChild(newEl, targetEl)
3225
+
3226
+ if (selectedElement === targetEl) {
3227
+ selectedElement = newEl
3228
+ window._inspectorSelectedElement = newEl
3229
+ if (window.__ARCHIE_INSPECTOR__)
3230
+ window.__ARCHIE_INSPECTOR__.selectedElement = newEl
3231
+ }
3232
+
3233
+ if (selectedElements.length > 0) {
3234
+ selectedElements = selectedElements.map(function (candidate) {
3235
+ return candidate === targetEl ? newEl : candidate
3236
+ })
3237
+ }
3238
+
3239
+ if (newEl.getAttribute("data-inspector-editing") === "true") {
3240
+ applyInspectorStyles(newEl)
3241
+ }
3242
+
3243
+ postMessageToParent({
3244
+ type: "OUTER_HTML_REPLACED",
3245
+ requestId: event.data.requestId,
3246
+ selector: getStableSelector(newEl),
3247
+ })
3248
+ } else if (event.data && event.data.type === "REVERT_CHANGES") {
3249
+ // Clean up inspector styles before reloading
3250
+ removeHighlight()
3251
+ removeSelectedBadge()
3252
+ updateSharedHighlights()
3253
+
3254
+ selectedElements.forEach((el) => {
3255
+ el.removeAttribute("data-inspector-editing")
3256
+ })
3257
+ if (selectedElement && !selectedElements.includes(selectedElement)) {
3258
+ selectedElement.removeAttribute("data-inspector-editing")
3259
+ }
3260
+
3261
+ selectedElement = null
3262
+ selectedElements = []
3263
+ window._inspectorSelectedElement = null
3264
+ isInspecting = false
3265
+ isElementSelected = false // Mark that no element is selected
3266
+ lastMultiSelectIntent = false
3267
+ if (document.body) {
3268
+ document.body.style.cursor = ""
3269
+ document.body.style.userSelect = ""
3270
+ }
3271
+ // Reload to restore original DOM state
3272
+ location.reload()
3273
+ } else if (event.data && event.data.type === "EXTERNAL_DRAG_START") {
3274
+ dispatchHostCommand({
3275
+ type: "external-drag-start",
3276
+ component: event.data.component,
3277
+ })
3278
+ } else if (event.data && event.data.type === "EXTERNAL_DRAG_MOVE") {
3279
+ dispatchHostCommand({
3280
+ type: "external-drag-move",
3281
+ clientX: event.data.clientX,
3282
+ clientY: event.data.clientY,
3283
+ })
3284
+ } else if (event.data && event.data.type === "EXTERNAL_DRAG_END") {
3285
+ dispatchHostCommand({
3286
+ type: "external-drag-end",
3287
+ clientX: event.data.clientX,
3288
+ clientY: event.data.clientY,
3289
+ })
3290
+ } else if (event.data && event.data.type === "EXTERNAL_DRAG_CANCEL") {
3291
+ dispatchHostCommand({
3292
+ type: "external-drag-cancel",
3293
+ })
3294
+ }
3295
+ }
3296
+
3297
+ window._inspectorMessageHandler = messageHandler
3298
+ if (window.__ARCHIE_INSPECTOR__)
3299
+ window.__ARCHIE_INSPECTOR__.messageHandler = messageHandler
3300
+ window.addEventListener("message", messageHandler)
3301
+
3302
+ var mousemoveScheduled = false
3303
+ document.addEventListener(
3304
+ "mousemove",
3305
+ function (e) {
3306
+ if (!isInspecting) return
3307
+ if (mousemoveScheduled) return
3308
+ mousemoveScheduled = true
3309
+ requestAnimationFrame(function () {
3310
+ mousemoveScheduled = false
3311
+ var target = getElementFromPoint(e.clientX, e.clientY, e.target)
3312
+ if (
3313
+ !target ||
3314
+ target === document.body ||
3315
+ target === document.documentElement ||
3316
+ isInspectorElement(target) ||
3317
+ target === highlightedElement ||
3318
+ selectedElements.includes(target)
3319
+ ) {
3320
+ return
3321
+ }
3322
+ if (target !== highlightedElement && target !== selectedElement) {
3323
+ highlightElement(target)
3324
+ }
3325
+ })
3326
+ },
3327
+ true
3328
+ )
3329
+
3330
+ // Valid content element: not body/docElement, not inspector UI, in document
3331
+ function isValidContentElement(el) {
3332
+ if (!el || el === document.body || el === document.documentElement)
3333
+ return false
3334
+ if (isInspectorElement(el)) return false
3335
+ try {
3336
+ return document.contains(el)
3337
+ } catch (_) {
3338
+ return false
3339
+ }
3340
+ }
3341
+
3342
+ // Selection handler: MUST run on mousedown before blockEvent. We register at load time (before
3343
+ // START_INSPECTION), so we run first in capture phase; we stopImmediatePropagation so blockEvent never runs.
3344
+ const inspectorClickHandler = function (e) {
3345
+ if (!isInspecting) return
3346
+
3347
+ // Prefer e.target: browser already set it to the element under the pointer (e.g. input).
3348
+ // getElementFromPoint is fallback when overlay or stacking returns something else.
3349
+ let target = null
3350
+ const normalizedEventTarget = resolveExactInspectTarget(e.target)
3351
+ if (isValidContentElement(normalizedEventTarget)) {
3352
+ target = normalizedEventTarget
3353
+ }
3354
+ if (!target) {
3355
+ target = getElementFromPoint(e.clientX, e.clientY, e.target)
3356
+ }
3357
+
3358
+ // Skip if invalid or inspector UI element
3359
+ if (!target || !isValidContentElement(target)) {
3360
+ return
3361
+ }
3362
+
3363
+ // Block native behavior
3364
+ e.preventDefault()
3365
+ e.stopPropagation()
3366
+ e.stopImmediatePropagation()
3367
+
3368
+ // Select the element
3369
+ if (target && target !== overlay && target !== selectedElementBadge) {
3370
+ const isMultiSelect =
3371
+ e.metaKey || e.ctrlKey || lastMultiSelectIntent === true
3372
+
3373
+ // 1. Clear hover highlight immediately to prevent conflict
3374
+ removeHighlight()
3375
+
3376
+ if (isMultiSelect) {
3377
+ // Multi-selection mode
3378
+ const index = selectedElements.indexOf(target)
3379
+ if (index > -1) {
3380
+ // Deselect if already selected
3381
+ selectedElements.splice(index, 1)
3382
+ removeInspectorStyles(target)
3383
+ cleanupElementListeners(target)
3384
+ } else {
3385
+ // Add to selection - apply styles to new element
3386
+ selectedElements.push(target)
3387
+ applyInspectorStyles(target)
3388
+ }
3389
+
3390
+ // Apply styles to ALL selected elements
3391
+ selectedElements.forEach((el) => {
3392
+ if (el) {
3393
+ applyInspectorStyles(el)
3394
+ }
3395
+ })
3396
+ } else {
3397
+ // Single selection mode
3398
+ // Clear ALL previous selections first
3399
+ const previousSelection = [...selectedElements]
3400
+ previousSelection.forEach((el) => {
3401
+ removeInspectorStyles(el)
3402
+ cleanupElementListeners(el)
3403
+ })
3404
+
3405
+ // Reset selection array
3406
+ selectedElements = []
3407
+
3408
+ // Select only the new target
3409
+ selectedElements = [target]
3410
+ applyInspectorStyles(target)
3411
+ }
3412
+
3413
+ // Update single selectedElement reference for compatibility (use the last selected)
3414
+ selectedElement =
3415
+ selectedElements.length > 0
3416
+ ? selectedElements[selectedElements.length - 1]
3417
+ : null
3418
+ window._inspectorSelectedElement = selectedElement
3419
+ if (window.__ARCHIE_INSPECTOR__)
3420
+ window.__ARCHIE_INSPECTOR__.selectedElement = selectedElement
3421
+
3422
+ // Update global flag
3423
+ isElementSelected = selectedElements.length > 0
3424
+
3425
+ // No need to call removeSelectedBadge() here as we handled it above
3426
+ // No need to call removeHighlight() here as we handled it above
3427
+
3428
+ // Re-create badges for all selected elements (if needed for side effects, though createSelectedBadge is empty)
3429
+ selectedElements.forEach((el) => {
3430
+ createSelectedBadge(el)
3431
+ })
3432
+
3433
+ // Update shared highlights BEFORE reapplying styles
3434
+ updateSharedHighlights()
3435
+
3436
+ // CRITICAL: Reapply styles to ALL selected elements AFTER updateSharedHighlights
3437
+ // This ensures all selected elements keep their outline visible
3438
+ if (isMultiSelect) {
3439
+ selectedElements.forEach((el) => {
3440
+ if (el) {
3441
+ applyInspectorStyles(el)
3442
+ }
3443
+ })
3444
+ }
3445
+
3446
+ setTimeout(function () {
3447
+ updateSharedHighlights()
3448
+ if (isMultiSelect && selectedElements.length > 0) {
3449
+ selectedElements.forEach(function (el) {
3450
+ if (el) applyInspectorStyles(el)
3451
+ })
3452
+ }
3453
+ }, BADGE_UPDATE_DELAY_MS)
3454
+
3455
+ if (selectedElement) setupBadgePositionListeners(selectedElement)
3456
+
3457
+ // Extract metadata for all selected elements
3458
+ const metadataArray = selectedElements.map((el) =>
3459
+ extractMetadata(el)
3460
+ )
3461
+
3462
+ // For backward compatibility, if only one element is selected, send it as a single object
3463
+ // Otherwise send the array. This ensures that the chat and other components that expect
3464
+ // a single object for single selection continue to work.
3465
+ const metadataPayload =
3466
+ metadataArray.length === 1 ? metadataArray[0] : metadataArray
3467
+
3468
+ postMessageToParent({
3469
+ type: "ELEMENT_SELECTED",
3470
+ metadata: metadataPayload,
3471
+ })
3472
+ // Keep inspector active - don't set isInspecting = false
3473
+ // User can continue navigating between elements
3474
+ }
3475
+ }
3476
+
3477
+ // Register inspector click handler BEFORE event blockers
3478
+ // This ensures it runs first and can mark events as handled
3479
+ document.addEventListener("mousedown", inspectorClickHandler, true)
3480
+
3481
+ // Block clicks when element is selected but not inspecting
3482
+ document.addEventListener(
3483
+ "click",
3484
+ function (e) {
3485
+ if (
3486
+ isElementSelected &&
3487
+ !isInspecting &&
3488
+ !isInspectorElement(e.target)
3489
+ ) {
3490
+ e.preventDefault()
3491
+ e.stopPropagation()
3492
+ e.stopImmediatePropagation()
3493
+ }
3494
+ },
3495
+ true
3496
+ )
3497
+
3498
+ document.addEventListener("keydown", function (e) {
3499
+ if (e.key === "Escape" && (isInspecting || isElementSelected)) {
3500
+ isInspecting = false
3501
+ isElementSelected = false
3502
+ removeEventBlockers()
3503
+ if (document.body) {
3504
+ document.body.style.cursor = ""
3505
+ document.body.style.userSelect = ""
3506
+ }
3507
+ removeHighlight()
3508
+ removeSelectedBadge()
3509
+ selectedElements.forEach(function (el) {
3510
+ if (el) removeInspectorStyles(el)
3511
+ })
3512
+ selectedElement = null
3513
+ selectedElements = []
3514
+ window._inspectorSelectedElement = null
3515
+ if (window.__ARCHIE_INSPECTOR__)
3516
+ window.__ARCHIE_INSPECTOR__.selectedElement = null
3517
+ updateSharedHighlights()
3518
+ postMessageToParent({ type: "INSPECTION_CANCELLED" })
3519
+ }
3520
+ })
3521
+ })()
3522
+ }
3523
+ if (typeof window !== "undefined") {
3524
+ window.__ARCHIE_INSPECTOR_RUN__ = runInspector
3525
+ var referrerOrigin = ""
3526
+ try {
3527
+ referrerOrigin = document.referrer ? new URL(document.referrer).origin : ""
3528
+ } catch (error) {
3529
+ referrerOrigin = ""
3530
+ }
3531
+
3532
+ if (window.parent === window) {
3533
+ if (!window.__ARCHIE_INSPECTOR_TARGET_ORIGIN__ && window.location.origin) {
3534
+ window.__ARCHIE_INSPECTOR_TARGET_ORIGIN__ = window.location.origin
3535
+ }
3536
+ if (
3537
+ !window.__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__ &&
3538
+ window.location.origin
3539
+ ) {
3540
+ window.__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__ = [window.location.origin]
3541
+ }
3542
+ } else {
3543
+ if (!window.__ARCHIE_INSPECTOR_TARGET_ORIGIN__ && referrerOrigin) {
3544
+ window.__ARCHIE_INSPECTOR_TARGET_ORIGIN__ = referrerOrigin
3545
+ }
3546
+ if (!window.__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__ && referrerOrigin) {
3547
+ window.__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__ = [referrerOrigin]
3548
+ }
3549
+ }
3550
+
3551
+ var hasAllowedOrigins =
3552
+ Array.isArray(window.__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__) &&
3553
+ window.__ARCHIE_INSPECTOR_ALLOWED_ORIGINS__.length > 0
3554
+ var hasAllowedPatterns =
3555
+ Array.isArray(window.__ARCHIE_INSPECTOR_ALLOWED_ORIGIN_PATTERNS__) &&
3556
+ window.__ARCHIE_INSPECTOR_ALLOWED_ORIGIN_PATTERNS__.length > 0
3557
+
3558
+ if (
3559
+ window.parent === window ||
3560
+ window.__ARCHIE_INSPECTOR_TARGET_ORIGIN__ ||
3561
+ hasAllowedOrigins ||
3562
+ hasAllowedPatterns
3563
+ ) {
3564
+ runInspector()
3565
+ }
3566
+ }
3567
+ })()