@hereorcode/dom-inspector 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,764 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
21
+
22
+ // src/index.ts
23
+ var index_exports = {};
24
+ __export(index_exports, {
25
+ createDOMInspector: () => createDOMInspector,
26
+ getCssSelector: () => getCssSelector,
27
+ getElementPath: () => getElementPath,
28
+ getJSPath: () => getJSPath,
29
+ getXPath: () => getXPath
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/overlay.ts
34
+ var DEFAULT_BORDER_COLOR = "#7c3aed";
35
+ var DEFAULT_BACKGROUND_COLOR = "rgba(124, 58, 237, 0.12)";
36
+ var DEFAULT_Z_INDEX = 2147483647;
37
+ var ElementHighlighter = class {
38
+ constructor(document2, options = {}) {
39
+ __publicField(this, "document", document2);
40
+ __publicField(this, "element");
41
+ this.element = document2.createElement("dom-inspector-overlay");
42
+ this.element.setAttribute("data-dom-inspector-overlay", "true");
43
+ this.element.style.cssText = [
44
+ "position: fixed",
45
+ "top: 0",
46
+ "left: 0",
47
+ "display: none",
48
+ "pointer-events: none",
49
+ "box-sizing: border-box",
50
+ `border: 1px dashed ${options.borderColor ?? DEFAULT_BORDER_COLOR}`,
51
+ `background: ${options.backgroundColor ?? DEFAULT_BACKGROUND_COLOR}`,
52
+ `border-radius: ${options.borderRadius ?? 0}px`,
53
+ `z-index: ${options.zIndex ?? DEFAULT_Z_INDEX}`,
54
+ "transition: transform 80ms ease, width 80ms ease, height 80ms ease"
55
+ ].join(";");
56
+ const parent = document2.body ?? document2.documentElement;
57
+ parent.appendChild(this.element);
58
+ }
59
+ show(target) {
60
+ this.showRect(target.getBoundingClientRect());
61
+ }
62
+ showRect(rect) {
63
+ this.element.style.display = "block";
64
+ this.element.style.transform = `translate(${rect.left}px, ${rect.top}px)`;
65
+ this.element.style.width = `${rect.width}px`;
66
+ this.element.style.height = `${rect.height}px`;
67
+ }
68
+ hide() {
69
+ this.element.style.display = "none";
70
+ }
71
+ destroy() {
72
+ this.element.remove();
73
+ }
74
+ };
75
+
76
+ // src/path.ts
77
+ var ROOT_TAGS = /* @__PURE__ */ new Set(["html", "body"]);
78
+ function getCssSelector(element) {
79
+ const segments = [];
80
+ let current = element;
81
+ while (current && current.nodeType === 1) {
82
+ const tagName = getTagName(current);
83
+ segments.unshift(getCssSegment(current));
84
+ if (ROOT_TAGS.has(tagName)) {
85
+ break;
86
+ }
87
+ current = current.parentElement;
88
+ }
89
+ return segments.join(" > ");
90
+ }
91
+ function getXPath(element, options = {}) {
92
+ const segments = [];
93
+ let current = element;
94
+ while (current && current.nodeType === 1) {
95
+ const tagName = getTagName(current);
96
+ const index = getSameTagIndex(current);
97
+ const shouldIncludeIndex = options.alwaysIncludeIndex || hasSameTagSibling(current);
98
+ segments.unshift(`${tagName}${shouldIncludeIndex ? `[${index}]` : ""}`);
99
+ if (tagName === "html") {
100
+ break;
101
+ }
102
+ current = current.parentElement;
103
+ }
104
+ return `/${segments.join("/")}`;
105
+ }
106
+ function getJSPath(element) {
107
+ return `document.querySelector(${JSON.stringify(getCssSelector(element))})`;
108
+ }
109
+ function getElementPath(element, options = {}) {
110
+ const rootDocument = options.rootDocument ?? element.ownerDocument;
111
+ const selector = getCssSelector(element);
112
+ const xpath = getXPath(element);
113
+ const fullXPath = getXPath(element, { alwaysIncludeIndex: true });
114
+ const bridges = collectAccessBridges(element, rootDocument);
115
+ const orderedBridges = [...bridges].reverse();
116
+ const framePath = [];
117
+ const shadowPath = [];
118
+ let expression = "document";
119
+ for (const bridge of orderedBridges) {
120
+ if (bridge.kind === "iframe") {
121
+ const frameExpression = `${expression}.querySelector(${JSON.stringify(
122
+ bridge.selector
123
+ )})`;
124
+ framePath.push({
125
+ element: bridge.element,
126
+ selector: bridge.selector,
127
+ xpath: bridge.xpath,
128
+ fullXPath: bridge.fullXPath,
129
+ jsPath: frameExpression
130
+ });
131
+ expression = `${frameExpression}.contentDocument`;
132
+ continue;
133
+ }
134
+ const hostExpression = `${expression}.querySelector(${JSON.stringify(
135
+ bridge.selector
136
+ )})`;
137
+ shadowPath.push({
138
+ host: bridge.host,
139
+ hostSelector: bridge.selector,
140
+ hostXPath: bridge.xpath,
141
+ hostFullXPath: bridge.fullXPath,
142
+ jsPath: hostExpression
143
+ });
144
+ expression = `${hostExpression}.shadowRoot`;
145
+ }
146
+ return {
147
+ rootKind: getRootKind(bridges),
148
+ selector,
149
+ xpath,
150
+ fullXPath,
151
+ jsPath: `${expression}.querySelector(${JSON.stringify(selector)})`,
152
+ framePath,
153
+ shadowPath
154
+ };
155
+ }
156
+ function getCssSegment(element) {
157
+ const tagName = getTagName(element);
158
+ if (ROOT_TAGS.has(tagName)) {
159
+ return tagName;
160
+ }
161
+ if (!hasSameTagSibling(element)) {
162
+ return tagName;
163
+ }
164
+ return `${tagName}:nth-child(${getElementIndex(element)})`;
165
+ }
166
+ function getTagName(element) {
167
+ return element.localName.toLowerCase();
168
+ }
169
+ function getElementIndex(element) {
170
+ const siblings = getElementSiblings(element);
171
+ if (!siblings) {
172
+ return 1;
173
+ }
174
+ return siblings.indexOf(element) + 1;
175
+ }
176
+ function getSameTagIndex(element) {
177
+ const siblings = getElementSiblings(element);
178
+ if (!siblings) {
179
+ return 1;
180
+ }
181
+ const tagName = getTagName(element);
182
+ let index = 0;
183
+ for (const child of siblings) {
184
+ if (getTagName(child) === tagName) {
185
+ index += 1;
186
+ }
187
+ if (child === element) {
188
+ return index;
189
+ }
190
+ }
191
+ return 1;
192
+ }
193
+ function hasSameTagSibling(element) {
194
+ const siblings = getElementSiblings(element);
195
+ if (!siblings) {
196
+ return false;
197
+ }
198
+ const tagName = getTagName(element);
199
+ let count = 0;
200
+ for (const child of siblings) {
201
+ if (getTagName(child) === tagName) {
202
+ count += 1;
203
+ }
204
+ if (count > 1) {
205
+ return true;
206
+ }
207
+ }
208
+ return false;
209
+ }
210
+ function collectAccessBridges(element, rootDocument) {
211
+ const bridges = [];
212
+ let current = element;
213
+ let root = current.getRootNode();
214
+ while (true) {
215
+ if (isShadowRoot(root)) {
216
+ const host = root.host;
217
+ bridges.push({
218
+ kind: "shadow",
219
+ host,
220
+ selector: getCssSelector(host),
221
+ xpath: getXPath(host),
222
+ fullXPath: getXPath(host, { alwaysIncludeIndex: true })
223
+ });
224
+ current = host;
225
+ root = current.getRootNode();
226
+ continue;
227
+ }
228
+ if (isDocument(root) && root !== rootDocument) {
229
+ const frameElement = findFrameElementForDocument(root, rootDocument);
230
+ if (!frameElement) {
231
+ break;
232
+ }
233
+ bridges.push({
234
+ kind: "iframe",
235
+ element: frameElement,
236
+ selector: getCssSelector(frameElement),
237
+ xpath: getXPath(frameElement),
238
+ fullXPath: getXPath(frameElement, { alwaysIncludeIndex: true })
239
+ });
240
+ current = frameElement;
241
+ root = current.getRootNode();
242
+ continue;
243
+ }
244
+ break;
245
+ }
246
+ return bridges;
247
+ }
248
+ function getRootKind(bridges) {
249
+ const innermostBridge = bridges[0];
250
+ if (!innermostBridge) {
251
+ return "document";
252
+ }
253
+ return innermostBridge.kind === "shadow" ? "shadow" : "iframe";
254
+ }
255
+ function findFrameElementForDocument(targetDocument, searchDocument) {
256
+ const directFrame = targetDocument.defaultView?.frameElement;
257
+ if (directFrame && getTagName(directFrame) === "iframe") {
258
+ return directFrame;
259
+ }
260
+ for (const frame of Array.from(searchDocument.querySelectorAll("iframe"))) {
261
+ const childDocument = getAccessibleFrameDocument(frame);
262
+ if (!childDocument) {
263
+ continue;
264
+ }
265
+ if (childDocument === targetDocument) {
266
+ return frame;
267
+ }
268
+ const nestedFrame = findFrameElementForDocument(
269
+ targetDocument,
270
+ childDocument
271
+ );
272
+ if (nestedFrame) {
273
+ return nestedFrame;
274
+ }
275
+ }
276
+ return null;
277
+ }
278
+ function getAccessibleFrameDocument(frame) {
279
+ try {
280
+ return frame.contentDocument?.documentElement ? frame.contentDocument : null;
281
+ } catch {
282
+ return null;
283
+ }
284
+ }
285
+ function getElementSiblings(element) {
286
+ if (element.parentElement) {
287
+ return Array.from(element.parentElement.children);
288
+ }
289
+ const parentNode = element.parentNode;
290
+ if (!parentNode || parentNode.nodeType !== 11) {
291
+ return null;
292
+ }
293
+ return Array.from(parentNode.childNodes).filter(isElement);
294
+ }
295
+ function isDocument(root) {
296
+ return root.nodeType === 9;
297
+ }
298
+ function isElement(node) {
299
+ return node.nodeType === 1;
300
+ }
301
+ function isShadowRoot(root) {
302
+ return root.nodeType === 11 && "host" in root;
303
+ }
304
+
305
+ // src/pseudo.ts
306
+ var PSEUDO_ELEMENTS = ["::after", "::before"];
307
+ var HIT_SLOP = 4;
308
+ function getPseudoElementMatch(element, clientX, clientY) {
309
+ const pseudoElements = getPseudoElements(element);
310
+ for (const info of pseudoElements) {
311
+ if (!info.rect) {
312
+ continue;
313
+ }
314
+ if (containsPoint(info.rect, clientX, clientY, HIT_SLOP)) {
315
+ return {
316
+ hit: { pseudoElement: info.pseudoElement, rect: info.rect },
317
+ pseudoElements
318
+ };
319
+ }
320
+ }
321
+ return { hit: null, pseudoElements };
322
+ }
323
+ function getPseudoElements(element) {
324
+ const view = element.ownerDocument.defaultView;
325
+ if (!view) {
326
+ return [];
327
+ }
328
+ const pseudoElements = [];
329
+ for (const pseudoElement of PSEUDO_ELEMENTS) {
330
+ if (!hasVisiblePseudoElement(element, pseudoElement)) {
331
+ continue;
332
+ }
333
+ pseudoElements.push({
334
+ pseudoElement,
335
+ rect: getPseudoElementRect(element, pseudoElement)
336
+ });
337
+ }
338
+ return pseudoElements;
339
+ }
340
+ function getPseudoElementRect(element, pseudoElement) {
341
+ const view = element.ownerDocument.defaultView;
342
+ if (!view) {
343
+ return null;
344
+ }
345
+ const style = view.getComputedStyle(element, pseudoElement);
346
+ if (!isVisiblePseudoStyle(style)) {
347
+ return null;
348
+ }
349
+ const baseRect = element.getBoundingClientRect();
350
+ const leftInset = readLength(style.left, baseRect.width);
351
+ const rightInset = readLength(style.right, baseRect.width);
352
+ const topInset = readLength(style.top, baseRect.height);
353
+ const bottomInset = readLength(style.bottom, baseRect.height);
354
+ let width = readLength(style.width, baseRect.width);
355
+ let height = readLength(style.height, baseRect.height);
356
+ const hasGeometrySignal = style.position === "absolute" || style.position === "fixed" || width !== null || height !== null || leftInset !== null || rightInset !== null || topInset !== null || bottomInset !== null;
357
+ if (!hasGeometrySignal) {
358
+ return null;
359
+ }
360
+ if (width === null && leftInset !== null && rightInset !== null) {
361
+ width = Math.max(baseRect.width - leftInset - rightInset, 0);
362
+ }
363
+ if (height === null && topInset !== null && bottomInset !== null) {
364
+ height = Math.max(baseRect.height - topInset - bottomInset, 0);
365
+ }
366
+ if (width === null || height === null) {
367
+ return null;
368
+ }
369
+ const x = leftInset !== null ? baseRect.left + leftInset : rightInset !== null ? baseRect.right - rightInset - width : pseudoElement === "::before" ? baseRect.left : baseRect.right - width;
370
+ const y = topInset !== null ? baseRect.top + topInset : bottomInset !== null ? baseRect.bottom - bottomInset - height : pseudoElement === "::before" ? baseRect.top : baseRect.bottom - height;
371
+ return {
372
+ x,
373
+ y,
374
+ top: y,
375
+ left: x,
376
+ width,
377
+ height,
378
+ right: x + width,
379
+ bottom: y + height
380
+ };
381
+ }
382
+ function hasVisiblePseudoElement(element, pseudoElement) {
383
+ const view = element.ownerDocument.defaultView;
384
+ if (!view) {
385
+ return false;
386
+ }
387
+ return isVisiblePseudoStyle(view.getComputedStyle(element, pseudoElement));
388
+ }
389
+ function isVisiblePseudoStyle(style) {
390
+ return Boolean(
391
+ style && style.display !== "none" && style.visibility !== "hidden" && style.content !== "none" && style.content !== "normal"
392
+ );
393
+ }
394
+ function containsPoint(rect, clientX, clientY, slop) {
395
+ return clientX >= rect.left - slop && clientX <= rect.right + slop && clientY >= rect.top - slop && clientY <= rect.bottom + slop;
396
+ }
397
+ function readLength(value, basis) {
398
+ if (!value || value === "auto") {
399
+ return null;
400
+ }
401
+ if (value.endsWith("%")) {
402
+ const percentage = Number.parseFloat(value);
403
+ return Number.isFinite(percentage) ? basis * percentage / 100 : null;
404
+ }
405
+ const pixels = Number.parseFloat(value);
406
+ return Number.isFinite(pixels) ? pixels : null;
407
+ }
408
+
409
+ // src/inspector.ts
410
+ function createDOMInspector(options) {
411
+ return new DOMInspectorController(options);
412
+ }
413
+ var DOMInspectorController = class {
414
+ constructor(options) {
415
+ __publicField(this, "options", options);
416
+ __publicField(this, "document");
417
+ __publicField(this, "frameLoadHandlers", /* @__PURE__ */ new Map());
418
+ __publicField(this, "managedDocuments", /* @__PURE__ */ new Map());
419
+ __publicField(this, "selectionScope");
420
+ __publicField(this, "active", false);
421
+ __publicField(this, "hoveredElement", null);
422
+ __publicField(this, "hoveredPseudoElement", null);
423
+ __publicField(this, "hoveredPath", []);
424
+ __publicField(this, "hoveredPathIndex", 0);
425
+ __publicField(this, "handleMouseMove", (event) => {
426
+ const path = this.getInspectablePath(event);
427
+ const target = path[0];
428
+ if (!target) {
429
+ return;
430
+ }
431
+ const nextIndex = this.hoveredPath[0] === target ? Math.min(this.hoveredPathIndex, path.length - 1) : 0;
432
+ this.hoveredPath = path;
433
+ this.hoveredPathIndex = nextIndex;
434
+ this.updateHoveredElement(path[nextIndex], event);
435
+ });
436
+ __publicField(this, "handleMouseDown", (event) => {
437
+ if (event.button !== 0) {
438
+ return;
439
+ }
440
+ const target = this.getInspectablePath(event)[0];
441
+ if (!target) {
442
+ return;
443
+ }
444
+ if (this.options.preventDefault !== false) {
445
+ event.preventDefault();
446
+ event.stopPropagation();
447
+ event.stopImmediatePropagation();
448
+ }
449
+ });
450
+ __publicField(this, "handleClick", (event) => {
451
+ const path = this.getInspectablePath(event);
452
+ const target = this.getClickTarget(path);
453
+ if (!target) {
454
+ return;
455
+ }
456
+ const result = this.createResult(target, event);
457
+ if (this.options.preventDefault !== false) {
458
+ event.preventDefault();
459
+ event.stopPropagation();
460
+ event.stopImmediatePropagation();
461
+ }
462
+ this.showHighlight(result);
463
+ try {
464
+ this.options.onSelect?.(result);
465
+ } finally {
466
+ if (this.options.autoStop !== false) {
467
+ this.stop();
468
+ }
469
+ }
470
+ });
471
+ __publicField(this, "handleWheel", (event) => {
472
+ if (!this.selectionScope || !hasModifierKey(event, this.selectionScope.modifierKey)) {
473
+ return;
474
+ }
475
+ const direction = getWheelDirection(event);
476
+ if (direction === 0) {
477
+ return;
478
+ }
479
+ const path = this.getInspectablePath(event);
480
+ if (path.length > 0 && path[0] !== this.hoveredPath[0]) {
481
+ this.hoveredPath = path;
482
+ this.hoveredPathIndex = 0;
483
+ }
484
+ if (this.hoveredPath.length === 0) {
485
+ return;
486
+ }
487
+ event.preventDefault();
488
+ event.stopPropagation();
489
+ event.stopImmediatePropagation();
490
+ const nextIndex = clamp(
491
+ this.hoveredPathIndex + direction,
492
+ 0,
493
+ this.hoveredPath.length - 1
494
+ );
495
+ if (nextIndex === this.hoveredPathIndex) {
496
+ return;
497
+ }
498
+ this.hoveredPathIndex = nextIndex;
499
+ this.updateHoveredElement(this.hoveredPath[nextIndex], event);
500
+ });
501
+ __publicField(this, "handleKeyDown", (event) => {
502
+ if (event.key !== "Escape") {
503
+ return;
504
+ }
505
+ event.preventDefault();
506
+ this.stop();
507
+ this.options.onCancel?.();
508
+ });
509
+ __publicField(this, "handleViewportChange", () => {
510
+ if (!this.hoveredElement) {
511
+ return;
512
+ }
513
+ const highlighter = this.getHighlighter(this.hoveredElement.ownerDocument);
514
+ if (highlighter) {
515
+ this.hideInactiveHighlighters(this.hoveredElement.ownerDocument);
516
+ highlighter.show(this.hoveredElement);
517
+ }
518
+ });
519
+ this.document = options.document ?? document;
520
+ this.selectionScope = getSelectionScope(options.selectionScope);
521
+ }
522
+ start() {
523
+ if (this.active) {
524
+ return;
525
+ }
526
+ this.active = true;
527
+ this.attachDocument(this.document);
528
+ }
529
+ stop() {
530
+ if (!this.active) {
531
+ return;
532
+ }
533
+ this.active = false;
534
+ this.hoveredElement = null;
535
+ this.hoveredPseudoElement = null;
536
+ this.hoveredPath = [];
537
+ this.hoveredPathIndex = 0;
538
+ for (const [frame, handler] of this.frameLoadHandlers) {
539
+ frame.removeEventListener("load", handler);
540
+ }
541
+ this.frameLoadHandlers.clear();
542
+ for (const state of this.managedDocuments.values()) {
543
+ state.document.documentElement.style.cursor = state.previousCursor;
544
+ state.document.removeEventListener("mousemove", this.handleMouseMove, true);
545
+ state.document.removeEventListener("mousedown", this.handleMouseDown, true);
546
+ state.document.removeEventListener("click", this.handleClick, true);
547
+ state.document.removeEventListener("keydown", this.handleKeyDown, true);
548
+ state.document.removeEventListener("wheel", this.handleWheel, true);
549
+ state.document.defaultView?.removeEventListener(
550
+ "scroll",
551
+ this.handleViewportChange,
552
+ true
553
+ );
554
+ state.document.defaultView?.removeEventListener(
555
+ "resize",
556
+ this.handleViewportChange,
557
+ true
558
+ );
559
+ state.observer?.disconnect();
560
+ state.highlighter?.destroy();
561
+ }
562
+ this.managedDocuments.clear();
563
+ }
564
+ destroy() {
565
+ this.stop();
566
+ }
567
+ isActive() {
568
+ return this.active;
569
+ }
570
+ getInspectablePath(event) {
571
+ const path = event.composedPath().filter(isElementLike);
572
+ if (path.some((entry) => this.options.exclude?.(entry))) {
573
+ return [];
574
+ }
575
+ const inspectablePath = [];
576
+ for (const entry of path) {
577
+ if (this.managedDocuments.has(entry.ownerDocument) && !entry.hasAttribute("data-dom-inspector-overlay")) {
578
+ inspectablePath.push(entry);
579
+ }
580
+ }
581
+ if (inspectablePath.length > 0) {
582
+ return inspectablePath;
583
+ }
584
+ return isElementLike(event.target) && this.managedDocuments.has(event.target.ownerDocument) ? [event.target] : [];
585
+ }
586
+ getClickTarget(path) {
587
+ if (this.hoveredElement && path.includes(this.hoveredElement)) {
588
+ return this.hoveredElement;
589
+ }
590
+ return path[0] ?? null;
591
+ }
592
+ updateHoveredElement(element, event) {
593
+ const result = this.createResult(element, event);
594
+ if (element === this.hoveredElement && result.pseudoElement === this.hoveredPseudoElement) {
595
+ return;
596
+ }
597
+ this.hoveredElement = element;
598
+ this.hoveredPseudoElement = result.pseudoElement;
599
+ this.showHighlight(result);
600
+ this.options.onHover?.(result);
601
+ }
602
+ createResult(element, event) {
603
+ const path = getElementPath(element, { rootDocument: this.document });
604
+ const pseudoMatch = getPseudoElementMatch(
605
+ element,
606
+ event.clientX,
607
+ event.clientY
608
+ );
609
+ const pseudoHit = pseudoMatch.hit;
610
+ return {
611
+ ...path,
612
+ element,
613
+ ownerDocument: element.ownerDocument,
614
+ pseudoElement: pseudoHit?.pseudoElement ?? null,
615
+ pseudoElements: pseudoMatch.pseudoElements,
616
+ pseudoElementRect: pseudoHit?.rect ?? null,
617
+ rect: element.getBoundingClientRect(),
618
+ event,
619
+ selectorWithPseudo: pseudoHit ? `${path.selector}${pseudoHit.pseudoElement}` : path.selector
620
+ };
621
+ }
622
+ attachDocument(targetDocument) {
623
+ if (this.managedDocuments.has(targetDocument)) {
624
+ return;
625
+ }
626
+ const previousCursor = targetDocument.documentElement.style.cursor;
627
+ const highlighter = this.options.highlight === false ? null : new ElementHighlighter(
628
+ targetDocument,
629
+ typeof this.options.highlight === "object" ? this.options.highlight : void 0
630
+ );
631
+ const Observer = targetDocument.defaultView?.MutationObserver;
632
+ const observer = Observer ? new Observer(() => this.syncChildFrames(targetDocument)) : null;
633
+ targetDocument.documentElement.style.cursor = "crosshair";
634
+ targetDocument.addEventListener("mousemove", this.handleMouseMove, true);
635
+ targetDocument.addEventListener("mousedown", this.handleMouseDown, true);
636
+ targetDocument.addEventListener("click", this.handleClick, true);
637
+ targetDocument.addEventListener("keydown", this.handleKeyDown, true);
638
+ targetDocument.addEventListener("wheel", this.handleWheel, {
639
+ capture: true,
640
+ passive: false
641
+ });
642
+ targetDocument.defaultView?.addEventListener(
643
+ "scroll",
644
+ this.handleViewportChange,
645
+ true
646
+ );
647
+ targetDocument.defaultView?.addEventListener(
648
+ "resize",
649
+ this.handleViewportChange,
650
+ true
651
+ );
652
+ observer?.observe(targetDocument.documentElement, {
653
+ childList: true,
654
+ subtree: true
655
+ });
656
+ this.managedDocuments.set(targetDocument, {
657
+ document: targetDocument,
658
+ highlighter,
659
+ observer,
660
+ previousCursor
661
+ });
662
+ this.syncChildFrames(targetDocument);
663
+ }
664
+ syncChildFrames(targetDocument) {
665
+ for (const frame of Array.from(
666
+ targetDocument.querySelectorAll("iframe")
667
+ )) {
668
+ this.observeFrameLoad(frame);
669
+ const frameDocument = getAccessibleFrameDocument2(frame);
670
+ if (frameDocument) {
671
+ this.attachDocument(frameDocument);
672
+ }
673
+ }
674
+ }
675
+ observeFrameLoad(frame) {
676
+ if (this.frameLoadHandlers.has(frame)) {
677
+ return;
678
+ }
679
+ const handler = () => {
680
+ if (!this.active) {
681
+ return;
682
+ }
683
+ const frameDocument = getAccessibleFrameDocument2(frame);
684
+ if (frameDocument) {
685
+ this.attachDocument(frameDocument);
686
+ }
687
+ };
688
+ frame.addEventListener("load", handler);
689
+ this.frameLoadHandlers.set(frame, handler);
690
+ }
691
+ showHighlight(result) {
692
+ const highlighter = this.getHighlighter(result.element.ownerDocument);
693
+ if (!highlighter) {
694
+ return;
695
+ }
696
+ this.hideInactiveHighlighters(result.element.ownerDocument);
697
+ if (result.pseudoElementRect) {
698
+ highlighter.showRect(result.pseudoElementRect);
699
+ return;
700
+ }
701
+ highlighter.show(result.element);
702
+ }
703
+ hideInactiveHighlighters(activeDocument) {
704
+ for (const state of this.managedDocuments.values()) {
705
+ if (state.document !== activeDocument) {
706
+ state.highlighter?.hide();
707
+ }
708
+ }
709
+ }
710
+ getHighlighter(targetDocument) {
711
+ return this.managedDocuments.get(targetDocument)?.highlighter ?? null;
712
+ }
713
+ };
714
+ function getAccessibleFrameDocument2(frame) {
715
+ try {
716
+ return frame.contentDocument?.documentElement ? frame.contentDocument : null;
717
+ } catch {
718
+ return null;
719
+ }
720
+ }
721
+ function getSelectionScope(options) {
722
+ if (options === false) {
723
+ return null;
724
+ }
725
+ return {
726
+ modifierKey: options?.modifierKey ?? "Alt"
727
+ };
728
+ }
729
+ function hasModifierKey(event, modifierKey) {
730
+ switch (modifierKey) {
731
+ case "Alt":
732
+ return event.altKey;
733
+ case "Control":
734
+ return event.ctrlKey;
735
+ case "Meta":
736
+ return event.metaKey;
737
+ case "Shift":
738
+ return event.shiftKey;
739
+ }
740
+ }
741
+ function getWheelDirection(event) {
742
+ if (event.deltaY < 0) {
743
+ return 1;
744
+ }
745
+ if (event.deltaY > 0) {
746
+ return -1;
747
+ }
748
+ return 0;
749
+ }
750
+ function clamp(value, min, max) {
751
+ return Math.min(Math.max(value, min), max);
752
+ }
753
+ function isElementLike(value) {
754
+ return typeof value === "object" && value !== null && value.nodeType === 1 && typeof value.tagName === "string";
755
+ }
756
+ // Annotate the CommonJS export names for ESM import in node:
757
+ 0 && (module.exports = {
758
+ createDOMInspector,
759
+ getCssSelector,
760
+ getElementPath,
761
+ getJSPath,
762
+ getXPath
763
+ });
764
+ //# sourceMappingURL=index.cjs.map