@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.
- package/dist/client/client.d.mts +7 -2
- package/dist/client/client.d.ts +7 -2
- package/dist/client/client.js +4 -2
- package/dist/client/client.mjs +2 -1
- package/dist/index.d.mts +28 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +4 -2
- package/dist/index.mjs +2 -1
- package/dist/inspector.standalone.js +3567 -0
- package/package.json +4 -3
|
@@ -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
|
+
})()
|