@ubio/webvision 2.6.6 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/global.js +259 -248
- package/build/global.js.map +3 -3
- package/build/page.mjs +259 -248
- package/out/page/dom.d.ts +2 -0
- package/out/page/dom.js +18 -0
- package/out/page/dom.js.map +1 -1
- package/out/page/highlight.d.ts +8 -1
- package/out/page/highlight.js +3 -2
- package/out/page/highlight.js.map +1 -1
- package/out/page/index.d.ts +1 -2
- package/out/page/index.js +1 -2
- package/out/page/index.js.map +1 -1
- package/out/page/page.d.ts +16 -0
- package/out/page/page.js +67 -0
- package/out/page/page.js.map +1 -0
- package/out/page/parser.d.ts +7 -8
- package/out/page/parser.js +35 -26
- package/out/page/parser.js.map +1 -1
- package/out/page/snapshot.d.ts +2 -2
- package/out/page/snapshot.js +2 -2
- package/out/page/snapshot.js.map +1 -1
- package/out/page/tree.d.ts +9 -13
- package/out/page/tree.js +16 -5
- package/out/page/tree.js.map +1 -1
- package/out/page/util.d.ts +1 -0
- package/out/page/util.js +3 -0
- package/out/page/util.js.map +1 -1
- package/package.json +1 -1
- package/out/page/counter.d.ts +0 -6
- package/out/page/counter.js +0 -13
- package/out/page/counter.js.map +0 -1
- package/out/page/frame.d.ts +0 -27
- package/out/page/frame.js +0 -85
- package/out/page/frame.js.map +0 -1
package/build/page.mjs
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
// src/page/counter.ts
|
|
2
|
-
var Counter = class {
|
|
3
|
-
constructor(value = 0) {
|
|
4
|
-
this.value = value;
|
|
5
|
-
}
|
|
6
|
-
next() {
|
|
7
|
-
this.value += 1;
|
|
8
|
-
return this.value;
|
|
9
|
-
}
|
|
10
|
-
current() {
|
|
11
|
-
return this.value;
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
|
|
15
1
|
// src/page/dom.ts
|
|
16
2
|
var ORIGINAL_STYLE_SYMBOL = Symbol("vx:originalStyle");
|
|
17
3
|
var INTERACTIVE_TAGS = ["a", "button", "input", "textarea", "select", "label", "option", "optgroup"];
|
|
@@ -198,6 +184,24 @@ function fixZIndex(el) {
|
|
|
198
184
|
}
|
|
199
185
|
}
|
|
200
186
|
}
|
|
187
|
+
function getElementDepth(el) {
|
|
188
|
+
let depth = 0;
|
|
189
|
+
while (el.parentElement) {
|
|
190
|
+
el = el.parentElement;
|
|
191
|
+
depth += 1;
|
|
192
|
+
}
|
|
193
|
+
return depth;
|
|
194
|
+
}
|
|
195
|
+
function serializeElement(el) {
|
|
196
|
+
const sig = [];
|
|
197
|
+
const depth = getElementDepth(el);
|
|
198
|
+
sig.push(String(depth));
|
|
199
|
+
sig.push(el.tagName);
|
|
200
|
+
for (const attr of el.attributes) {
|
|
201
|
+
sig.push(`${attr.name}=${attr.value}`);
|
|
202
|
+
}
|
|
203
|
+
return sig.join("|");
|
|
204
|
+
}
|
|
201
205
|
|
|
202
206
|
// src/page/traverse.ts
|
|
203
207
|
function* traverseVxNode(vxNode, depth = 0) {
|
|
@@ -279,6 +283,191 @@ function isContainerNode(vxNode) {
|
|
|
279
283
|
function isObjectEmpty(obj) {
|
|
280
284
|
return !Object.values(obj).some((value) => !!value);
|
|
281
285
|
}
|
|
286
|
+
function normalizeRef(ref) {
|
|
287
|
+
return ref.toLowerCase().trim().replace(/^[@#]/, "");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// src/page/render.ts
|
|
291
|
+
function renderVxNode(scope, options = {}) {
|
|
292
|
+
const buffer = [];
|
|
293
|
+
for (const { vxNode, depth } of traverseVxNode(scope)) {
|
|
294
|
+
if (options.skipNonInteractive && !vxNode.isInteractive) {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
const indent = options.skipNonInteractive ? "" : " ".repeat(depth);
|
|
298
|
+
buffer.push(renderIndentedLine(indent, vxNode, options));
|
|
299
|
+
}
|
|
300
|
+
return buffer.join("\n");
|
|
301
|
+
}
|
|
302
|
+
function renderIndentedLine(indent, vxNode, options = {}) {
|
|
303
|
+
const diffPrefix = options.renderDiff ? vxNode.isNew ? "+ " : " " : "";
|
|
304
|
+
if (!vxNode.tagName) {
|
|
305
|
+
return [diffPrefix, indent, vxNode.textContent].filter(Boolean).join("");
|
|
306
|
+
}
|
|
307
|
+
const tagLine = renderTagLine(vxNode, options);
|
|
308
|
+
const htmlStyle = options.renderStyle === "html";
|
|
309
|
+
return [
|
|
310
|
+
diffPrefix,
|
|
311
|
+
indent,
|
|
312
|
+
tagLine,
|
|
313
|
+
htmlStyle ? "" : " ",
|
|
314
|
+
vxNode.textContent ?? ""
|
|
315
|
+
].join("");
|
|
316
|
+
}
|
|
317
|
+
function renderTagLine(vxNode, options) {
|
|
318
|
+
const htmlStyle = options.renderStyle === "html";
|
|
319
|
+
const components = [];
|
|
320
|
+
if (options.renderTagNames && vxNode.tagName) {
|
|
321
|
+
components.push(vxNode.tagName);
|
|
322
|
+
}
|
|
323
|
+
if (options.renderIds && vxNode.id) {
|
|
324
|
+
if (htmlStyle) {
|
|
325
|
+
components.push(`id="${vxNode.id}"`);
|
|
326
|
+
} else {
|
|
327
|
+
components.push(`#${vxNode.id}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (options.renderClassNames && vxNode.classList?.length) {
|
|
331
|
+
if (htmlStyle) {
|
|
332
|
+
components.push(`class="${vxNode.classList.join(" ")}"`);
|
|
333
|
+
} else {
|
|
334
|
+
components.push("." + vxNode.classList.join("."));
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (options.renderRefs) {
|
|
338
|
+
const isRenderRef = [
|
|
339
|
+
options.renderRefs === "all",
|
|
340
|
+
options.renderRefs === true && !isContainerNode(vxNode)
|
|
341
|
+
].some(Boolean);
|
|
342
|
+
if (isRenderRef) {
|
|
343
|
+
components.push(`[@${vxNode.ref}]`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const attrs = [];
|
|
347
|
+
if (options.renderLabelAttrs) {
|
|
348
|
+
for (const [attr, value] of Object.entries(vxNode.labelAttrs ?? {})) {
|
|
349
|
+
attrs.push(`${attr}="${truncateAttrValue(value)}"`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (options.renderValueAttrs) {
|
|
353
|
+
for (const [attr, value] of Object.entries(vxNode.valueAttrs ?? {})) {
|
|
354
|
+
attrs.push(`${attr}="${truncateAttrValue(value)}"`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (options.renderSrcAttrs) {
|
|
358
|
+
for (const [attr, value] of Object.entries(vxNode.srcAttrs ?? {})) {
|
|
359
|
+
attrs.push(`${attr}="${truncateAttrValue(value)}"`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (htmlStyle) {
|
|
363
|
+
components.push(...attrs);
|
|
364
|
+
} else {
|
|
365
|
+
components.push(...attrs.map((attr) => `[${attr}]`));
|
|
366
|
+
}
|
|
367
|
+
return htmlStyle ? `<${components.join(" ")}>` : components.join("");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/page/snapshot.ts
|
|
371
|
+
var VX_DOM_SYMBOL = Symbol("vx:dom");
|
|
372
|
+
var VX_TREE_SYMBOL = Symbol("vx:tree");
|
|
373
|
+
var VX_LAST_REFS_SYMBOL = Symbol("vx:lastRefs");
|
|
374
|
+
async function captureSnapshot(options = {}) {
|
|
375
|
+
const parser = new VxTreeParser(options);
|
|
376
|
+
const domMap = parser.getDomMap();
|
|
377
|
+
const vxTree = parser.getTree(options.frameId ?? "0", options.iframeRef);
|
|
378
|
+
globalThis[VX_DOM_SYMBOL] = domMap;
|
|
379
|
+
globalThis[VX_TREE_SYMBOL] = vxTree;
|
|
380
|
+
return vxTree;
|
|
381
|
+
}
|
|
382
|
+
function getSnapshot() {
|
|
383
|
+
const vxTree = globalThis[VX_TREE_SYMBOL];
|
|
384
|
+
if (!vxTree) {
|
|
385
|
+
throw new Error("[VX] Snapshot not found");
|
|
386
|
+
}
|
|
387
|
+
return vxTree;
|
|
388
|
+
}
|
|
389
|
+
function resolveDomNode(ref) {
|
|
390
|
+
const domMap = globalThis[VX_DOM_SYMBOL];
|
|
391
|
+
if (!domMap) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
return domMap.get(ref) ?? null;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// src/page/tree.ts
|
|
398
|
+
var VxTreeView = class {
|
|
399
|
+
constructor(nodes, frameId, iframeRef) {
|
|
400
|
+
this.nodes = nodes;
|
|
401
|
+
this.frameId = frameId;
|
|
402
|
+
this.iframeRef = iframeRef;
|
|
403
|
+
this.refMap = /* @__PURE__ */ new Map();
|
|
404
|
+
this.buildRefMap(nodes);
|
|
405
|
+
}
|
|
406
|
+
get nodeCount() {
|
|
407
|
+
return this.refMap.size;
|
|
408
|
+
}
|
|
409
|
+
*traverse() {
|
|
410
|
+
for (const vxNode of this.nodes) {
|
|
411
|
+
yield* traverseVxNode(vxNode, 0);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
buildRefMap(nodes) {
|
|
415
|
+
for (const node of nodes) {
|
|
416
|
+
if (node.ref) {
|
|
417
|
+
this.refMap.set(node.ref, node);
|
|
418
|
+
}
|
|
419
|
+
this.buildRefMap(node.children ?? []);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
hasRef(ref) {
|
|
423
|
+
const nref = normalizeRef(ref);
|
|
424
|
+
return this.refMap.has(nref);
|
|
425
|
+
}
|
|
426
|
+
findNode(ref) {
|
|
427
|
+
const nref = normalizeRef(ref);
|
|
428
|
+
return this.refMap.get(nref) ?? null;
|
|
429
|
+
}
|
|
430
|
+
render(options = {}) {
|
|
431
|
+
return this.nodes.map((node) => {
|
|
432
|
+
return renderVxNode(node, options);
|
|
433
|
+
}).join("\n");
|
|
434
|
+
}
|
|
435
|
+
highlight(options = {}) {
|
|
436
|
+
if (options.clearOverlay) {
|
|
437
|
+
clearOverlay();
|
|
438
|
+
}
|
|
439
|
+
const leafOnly = !options.includeAll;
|
|
440
|
+
const refs = this.collectRefs(leafOnly, options.filterRefs);
|
|
441
|
+
this.highlightRefs(refs, options);
|
|
442
|
+
}
|
|
443
|
+
highlightRefs(refs, options = {}) {
|
|
444
|
+
for (const ref of refs) {
|
|
445
|
+
const el = resolveDomNode(ref);
|
|
446
|
+
if (el instanceof Element) {
|
|
447
|
+
const vxNode = this.findNode(ref);
|
|
448
|
+
const isNew = vxNode?.isNew ?? false;
|
|
449
|
+
const color = options.useColors ? void 0 : isNew ? "#0a0" : "#060";
|
|
450
|
+
highlightEl(el, ref, color);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
collectRefs(leafOnly = true, filterRefs) {
|
|
455
|
+
const refs = [];
|
|
456
|
+
for (const { vxNode } of this.traverse()) {
|
|
457
|
+
if (!vxNode.ref) {
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (leafOnly && isContainerNode(vxNode)) {
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (filterRefs && !filterRefs.includes(vxNode.ref)) {
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
refs.push(vxNode.ref);
|
|
467
|
+
}
|
|
468
|
+
return refs;
|
|
469
|
+
}
|
|
470
|
+
};
|
|
282
471
|
|
|
283
472
|
// src/page/parser.ts
|
|
284
473
|
var VX_NODE_SYMBOL = Symbol("vx:node");
|
|
@@ -351,41 +540,36 @@ var VX_KEEP_SELECTOR = "a, button, input, textarea, select, label, iframe, optio
|
|
|
351
540
|
var VxTreeParser = class {
|
|
352
541
|
constructor(options = {}) {
|
|
353
542
|
this.options = options;
|
|
543
|
+
this.viewport = { width: 0, height: 0 };
|
|
544
|
+
this.vxNodes = [];
|
|
354
545
|
this.probeElements = [];
|
|
355
546
|
this.domRefMap = /* @__PURE__ */ new Map();
|
|
356
|
-
|
|
547
|
+
}
|
|
548
|
+
async parse() {
|
|
357
549
|
this.viewport = getViewportSize();
|
|
358
|
-
this.
|
|
359
|
-
if (options.probeViewport) {
|
|
550
|
+
if (this.options.probeViewport) {
|
|
360
551
|
this.probeElements = probeViewport();
|
|
361
552
|
}
|
|
362
|
-
const vxRoot = this.
|
|
363
|
-
this.refRange = [startRef, this.counter.current()];
|
|
553
|
+
const vxRoot = await this.parseNode(document.documentElement, null);
|
|
364
554
|
this.vxNodes = this.pruneRecursive(vxRoot);
|
|
365
555
|
}
|
|
366
|
-
|
|
367
|
-
return this.
|
|
368
|
-
}
|
|
369
|
-
getTree() {
|
|
370
|
-
return new VxTreeView(this.vxNodes, this.refRange);
|
|
556
|
+
getTree(frameId, iframeRef) {
|
|
557
|
+
return new VxTreeView(this.vxNodes, frameId, iframeRef);
|
|
371
558
|
}
|
|
372
559
|
getNodes() {
|
|
373
560
|
return this.vxNodes;
|
|
374
561
|
}
|
|
375
|
-
getRefRange() {
|
|
376
|
-
return this.refRange;
|
|
377
|
-
}
|
|
378
562
|
getDomMap() {
|
|
379
563
|
return this.domRefMap;
|
|
380
564
|
}
|
|
381
|
-
parseNode(node, parent) {
|
|
565
|
+
async parseNode(node, parent) {
|
|
382
566
|
if (!node || node[VX_IGNORE_SYMBOL]) {
|
|
383
567
|
return null;
|
|
384
568
|
}
|
|
385
569
|
if (node instanceof Text) {
|
|
386
570
|
const textContent = normalizeText(node.textContent ?? "");
|
|
387
571
|
if (textContent) {
|
|
388
|
-
return this.makeNode(node, {
|
|
572
|
+
return await this.makeNode(node, {
|
|
389
573
|
textContent,
|
|
390
574
|
hasVisibleArea: parent?.hasVisibleArea,
|
|
391
575
|
isOutsideViewport: parent?.isOutsideViewport,
|
|
@@ -398,11 +582,11 @@ var VxTreeParser = class {
|
|
|
398
582
|
if (this.options.opaqueOverlays) {
|
|
399
583
|
makeOverlaysOpaque(node);
|
|
400
584
|
}
|
|
401
|
-
return this.parseElement(node);
|
|
585
|
+
return await this.parseElement(node);
|
|
402
586
|
}
|
|
403
587
|
return null;
|
|
404
588
|
}
|
|
405
|
-
parseElement(el) {
|
|
589
|
+
async parseElement(el) {
|
|
406
590
|
const skip = VX_IGNORE_TAGS.includes(el.tagName.toLowerCase()) || isHidden(el);
|
|
407
591
|
if (skip) {
|
|
408
592
|
this.clearRecursive(el);
|
|
@@ -411,7 +595,7 @@ var VxTreeParser = class {
|
|
|
411
595
|
const parentEl = el.matches("option, optgroup") ? el.closest("select") ?? el : el;
|
|
412
596
|
const rect = parentEl.getBoundingClientRect();
|
|
413
597
|
const id = el.getAttribute("id") ?? "";
|
|
414
|
-
const vxNode = this.makeNode(el, {
|
|
598
|
+
const vxNode = await this.makeNode(el, {
|
|
415
599
|
tagName: el.tagName.toLowerCase(),
|
|
416
600
|
id: isRandomIdentifier(id) ? void 0 : id,
|
|
417
601
|
classList: Array.from(el.classList).filter((cls) => !isRandomIdentifier(cls)).slice(0, 4),
|
|
@@ -424,7 +608,13 @@ var VxTreeParser = class {
|
|
|
424
608
|
isProbeHit: this.isProbeHit(parentEl),
|
|
425
609
|
isKept: el.matches(VX_KEEP_SELECTOR)
|
|
426
610
|
});
|
|
427
|
-
const children = [
|
|
611
|
+
const children = [];
|
|
612
|
+
for (const child of el.childNodes) {
|
|
613
|
+
const childNode = await this.parseNode(child, vxNode);
|
|
614
|
+
if (childNode != null) {
|
|
615
|
+
children.push(childNode);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
428
618
|
if (children.length === 1 && !children[0]?.tagName) {
|
|
429
619
|
vxNode.textContent = normalizeText(children[0].textContent ?? "");
|
|
430
620
|
} else {
|
|
@@ -432,15 +622,17 @@ var VxTreeParser = class {
|
|
|
432
622
|
}
|
|
433
623
|
return vxNode;
|
|
434
624
|
}
|
|
435
|
-
makeNode(node, spec) {
|
|
436
|
-
const ref = this.counter.next();
|
|
625
|
+
async makeNode(node, spec) {
|
|
437
626
|
const isNew = !node[VX_NODE_SYMBOL];
|
|
627
|
+
const ref = await this.createRef(node);
|
|
438
628
|
const vxNode = {
|
|
439
629
|
...spec,
|
|
440
630
|
ref,
|
|
441
631
|
isNew
|
|
442
632
|
};
|
|
443
|
-
|
|
633
|
+
if (ref) {
|
|
634
|
+
this.domRefMap.set(ref, node);
|
|
635
|
+
}
|
|
444
636
|
node[VX_NODE_SYMBOL] = vxNode;
|
|
445
637
|
return vxNode;
|
|
446
638
|
}
|
|
@@ -578,6 +770,13 @@ var VxTreeParser = class {
|
|
|
578
770
|
}
|
|
579
771
|
}
|
|
580
772
|
}
|
|
773
|
+
async createRef(node) {
|
|
774
|
+
if (node instanceof Element) {
|
|
775
|
+
const sig = serializeElement(node);
|
|
776
|
+
const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(sig).buffer);
|
|
777
|
+
return [...new Uint8Array(hash)].map((b) => b.toString(16).padStart(2, "0")).join("").substring(0, 8);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
581
780
|
};
|
|
582
781
|
|
|
583
782
|
// src/page/overlay.ts
|
|
@@ -621,7 +820,7 @@ function getOverlayContainer() {
|
|
|
621
820
|
}
|
|
622
821
|
|
|
623
822
|
// src/page/highlight.ts
|
|
624
|
-
function highlightEl(el, ref
|
|
823
|
+
function highlightEl(el, ref, color) {
|
|
625
824
|
if (!(el instanceof Element)) {
|
|
626
825
|
return;
|
|
627
826
|
}
|
|
@@ -657,191 +856,18 @@ function highlightEl(el, ref = 0, color) {
|
|
|
657
856
|
label.style.transform = "translateY(50%)";
|
|
658
857
|
label.textContent = String(ref);
|
|
659
858
|
}
|
|
660
|
-
function getRandomColor(
|
|
859
|
+
function getRandomColor(ref) {
|
|
860
|
+
const index = ref.charCodeAt(0);
|
|
661
861
|
const hue = index * 120 * 0.382 % 360;
|
|
662
862
|
return `hsl(${hue}, 85%, 50%)`;
|
|
663
863
|
}
|
|
664
864
|
|
|
665
|
-
// src/page/
|
|
666
|
-
function renderVxNode(scope, options = {}) {
|
|
667
|
-
const buffer = [];
|
|
668
|
-
for (const { vxNode, depth } of traverseVxNode(scope)) {
|
|
669
|
-
if (options.skipNonInteractive && !vxNode.isInteractive) {
|
|
670
|
-
continue;
|
|
671
|
-
}
|
|
672
|
-
const indent = options.skipNonInteractive ? "" : " ".repeat(depth);
|
|
673
|
-
buffer.push(renderIndentedLine(indent, vxNode, options));
|
|
674
|
-
}
|
|
675
|
-
return buffer.join("\n");
|
|
676
|
-
}
|
|
677
|
-
function renderIndentedLine(indent, vxNode, options = {}) {
|
|
678
|
-
const diffPrefix = options.renderDiff ? vxNode.isNew ? "+ " : " " : "";
|
|
679
|
-
if (!vxNode.tagName) {
|
|
680
|
-
return [diffPrefix, indent, vxNode.textContent].filter(Boolean).join("");
|
|
681
|
-
}
|
|
682
|
-
const tagLine = renderTagLine(vxNode, options);
|
|
683
|
-
const htmlStyle = options.renderStyle === "html";
|
|
684
|
-
return [
|
|
685
|
-
diffPrefix,
|
|
686
|
-
indent,
|
|
687
|
-
tagLine,
|
|
688
|
-
htmlStyle ? "" : " ",
|
|
689
|
-
vxNode.textContent ?? ""
|
|
690
|
-
].join("");
|
|
691
|
-
}
|
|
692
|
-
function renderTagLine(vxNode, options) {
|
|
693
|
-
const htmlStyle = options.renderStyle === "html";
|
|
694
|
-
const components = [];
|
|
695
|
-
if (options.renderTagNames && vxNode.tagName) {
|
|
696
|
-
components.push(vxNode.tagName);
|
|
697
|
-
}
|
|
698
|
-
if (options.renderIds && vxNode.id) {
|
|
699
|
-
if (htmlStyle) {
|
|
700
|
-
components.push(`id="${vxNode.id}"`);
|
|
701
|
-
} else {
|
|
702
|
-
components.push(`#${vxNode.id}`);
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
if (options.renderClassNames && vxNode.classList?.length) {
|
|
706
|
-
if (htmlStyle) {
|
|
707
|
-
components.push(`class="${vxNode.classList.join(" ")}"`);
|
|
708
|
-
} else {
|
|
709
|
-
components.push("." + vxNode.classList.join("."));
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
if (options.renderRefs) {
|
|
713
|
-
const isRenderRef = [
|
|
714
|
-
options.renderRefs === "all",
|
|
715
|
-
options.renderRefs === true && !isContainerNode(vxNode)
|
|
716
|
-
].some(Boolean);
|
|
717
|
-
if (isRenderRef) {
|
|
718
|
-
components.push(`[@${vxNode.ref}]`);
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
const attrs = [];
|
|
722
|
-
if (options.renderLabelAttrs) {
|
|
723
|
-
for (const [attr, value] of Object.entries(vxNode.labelAttrs ?? {})) {
|
|
724
|
-
attrs.push(`${attr}="${truncateAttrValue(value)}"`);
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
if (options.renderValueAttrs) {
|
|
728
|
-
for (const [attr, value] of Object.entries(vxNode.valueAttrs ?? {})) {
|
|
729
|
-
attrs.push(`${attr}="${truncateAttrValue(value)}"`);
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
if (options.renderSrcAttrs) {
|
|
733
|
-
for (const [attr, value] of Object.entries(vxNode.srcAttrs ?? {})) {
|
|
734
|
-
attrs.push(`${attr}="${truncateAttrValue(value)}"`);
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
if (htmlStyle) {
|
|
738
|
-
components.push(...attrs);
|
|
739
|
-
} else {
|
|
740
|
-
components.push(...attrs.map((attr) => `[${attr}]`));
|
|
741
|
-
}
|
|
742
|
-
return htmlStyle ? `<${components.join(" ")}>` : components.join("");
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
// src/page/snapshot.ts
|
|
746
|
-
var VX_DOM_SYMBOL = Symbol("vx:dom");
|
|
747
|
-
var VX_TREE_SYMBOL = Symbol("vx:tree");
|
|
748
|
-
var VX_LAST_REFS_SYMBOL = Symbol("vx:lastRefs");
|
|
749
|
-
function captureSnapshot(options = {}) {
|
|
750
|
-
const parser = new VxTreeParser(options);
|
|
751
|
-
const domMap = parser.getDomMap();
|
|
752
|
-
const vxTree = parser.getTree();
|
|
753
|
-
globalThis[VX_DOM_SYMBOL] = domMap;
|
|
754
|
-
globalThis[VX_TREE_SYMBOL] = vxTree;
|
|
755
|
-
return vxTree;
|
|
756
|
-
}
|
|
757
|
-
function getSnapshot() {
|
|
758
|
-
const vxTree = globalThis[VX_TREE_SYMBOL];
|
|
759
|
-
if (!vxTree) {
|
|
760
|
-
throw new Error("[VX] Snapshot not found");
|
|
761
|
-
}
|
|
762
|
-
return vxTree;
|
|
763
|
-
}
|
|
764
|
-
function resolveDomNode(ref) {
|
|
765
|
-
const domMap = globalThis[VX_DOM_SYMBOL];
|
|
766
|
-
if (!domMap) {
|
|
767
|
-
return null;
|
|
768
|
-
}
|
|
769
|
-
return domMap.get(ref) ?? null;
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
// src/page/tree.ts
|
|
773
|
-
var VxTreeView = class {
|
|
774
|
-
constructor(nodes, refRange) {
|
|
775
|
-
this.nodes = nodes;
|
|
776
|
-
this.refRange = refRange;
|
|
777
|
-
this.refMap = /* @__PURE__ */ new Map();
|
|
778
|
-
this.buildRefMap(nodes);
|
|
779
|
-
}
|
|
780
|
-
get nodeCount() {
|
|
781
|
-
return this.refMap.size;
|
|
782
|
-
}
|
|
783
|
-
*traverse() {
|
|
784
|
-
for (const vxNode of this.nodes) {
|
|
785
|
-
yield* traverseVxNode(vxNode, 0);
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
buildRefMap(nodes) {
|
|
789
|
-
for (const node of nodes) {
|
|
790
|
-
this.refMap.set(node.ref, node);
|
|
791
|
-
this.buildRefMap(node.children ?? []);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
findNode(ref) {
|
|
795
|
-
return this.refMap.get(ref) ?? null;
|
|
796
|
-
}
|
|
797
|
-
render(options = {}) {
|
|
798
|
-
return this.nodes.map((node) => {
|
|
799
|
-
return renderVxNode(node, options);
|
|
800
|
-
}).join("\n");
|
|
801
|
-
}
|
|
802
|
-
highlight(options = {}) {
|
|
803
|
-
if (options.clearOverlay) {
|
|
804
|
-
clearOverlay();
|
|
805
|
-
}
|
|
806
|
-
const leafOnly = !options.includeAll;
|
|
807
|
-
const refs = this.collectRefs(leafOnly, options.filterRefs);
|
|
808
|
-
this.highlightRefs(refs, options);
|
|
809
|
-
}
|
|
810
|
-
highlightRefs(refs, options = {}) {
|
|
811
|
-
for (const ref of refs) {
|
|
812
|
-
const el = resolveDomNode(ref);
|
|
813
|
-
if (el instanceof Element) {
|
|
814
|
-
const vxNode = this.findNode(ref);
|
|
815
|
-
const isNew = vxNode?.isNew ?? false;
|
|
816
|
-
const color = options.useColors ? void 0 : isNew ? "#0a0" : "#060";
|
|
817
|
-
highlightEl(el, ref, color);
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
collectRefs(leafOnly = true, filterRefs) {
|
|
822
|
-
const refs = [];
|
|
823
|
-
for (const { vxNode } of this.traverse()) {
|
|
824
|
-
if (leafOnly && isContainerNode(vxNode)) {
|
|
825
|
-
continue;
|
|
826
|
-
}
|
|
827
|
-
if (filterRefs && !filterRefs.includes(vxNode.ref)) {
|
|
828
|
-
continue;
|
|
829
|
-
}
|
|
830
|
-
refs.push(vxNode.ref);
|
|
831
|
-
}
|
|
832
|
-
return refs;
|
|
833
|
-
}
|
|
834
|
-
};
|
|
835
|
-
|
|
836
|
-
// src/page/frame.ts
|
|
865
|
+
// src/page/page.ts
|
|
837
866
|
var VxPageView = class {
|
|
838
|
-
constructor(
|
|
839
|
-
this.vxFrames = vxFrames;
|
|
840
|
-
this.vxFrameMap = /* @__PURE__ */ new Map();
|
|
867
|
+
constructor(vxTrees) {
|
|
841
868
|
this.vxTreeMap = /* @__PURE__ */ new Map();
|
|
842
|
-
for (const
|
|
843
|
-
this.
|
|
844
|
-
this.vxTreeMap.set(frame.frameId, new VxTreeView(frame.nodes, frame.refRange));
|
|
869
|
+
for (const vxTree of vxTrees) {
|
|
870
|
+
this.vxTreeMap.set(vxTree.frameId, vxTree);
|
|
845
871
|
}
|
|
846
872
|
}
|
|
847
873
|
get vxTrees() {
|
|
@@ -851,43 +877,28 @@ var VxPageView = class {
|
|
|
851
877
|
return this.vxTrees.reduce((acc, vxTree) => acc + vxTree.nodeCount, 0);
|
|
852
878
|
}
|
|
853
879
|
findNodeByRef(ref) {
|
|
854
|
-
const
|
|
855
|
-
if (
|
|
880
|
+
const frame = this.findFrameByRef(ref);
|
|
881
|
+
if (frame == null) {
|
|
856
882
|
return null;
|
|
857
883
|
}
|
|
858
|
-
return
|
|
884
|
+
return frame.findNode(ref);
|
|
859
885
|
}
|
|
860
|
-
|
|
886
|
+
findFrameById(frameId) {
|
|
861
887
|
return this.vxTreeMap.get(frameId) ?? null;
|
|
862
888
|
}
|
|
863
|
-
findTreeByRef(ref) {
|
|
864
|
-
const vxFrame = this.findFrameByRef(ref);
|
|
865
|
-
if (vxFrame == null) {
|
|
866
|
-
return null;
|
|
867
|
-
}
|
|
868
|
-
return this.vxTreeMap.get(vxFrame.frameId) ?? null;
|
|
869
|
-
}
|
|
870
|
-
findFrameByFrameId(frameId) {
|
|
871
|
-
return this.vxFrameMap.get(frameId) ?? null;
|
|
872
|
-
}
|
|
873
889
|
findFrameByRef(ref) {
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
);
|
|
877
|
-
if (!vxFrame) {
|
|
878
|
-
return null;
|
|
879
|
-
}
|
|
880
|
-
return this.vxFrameMap.get(vxFrame.frameId) ?? null;
|
|
890
|
+
const frame = this.vxTrees.find((f) => f.hasRef(ref));
|
|
891
|
+
return frame ?? null;
|
|
881
892
|
}
|
|
882
893
|
getFrameByRef(ref) {
|
|
883
|
-
const
|
|
884
|
-
if (
|
|
894
|
+
const frame = this.findFrameByRef(ref);
|
|
895
|
+
if (frame == null) {
|
|
885
896
|
throw new Error(`[VX] Frame not found for [ref=${ref}]`);
|
|
886
897
|
}
|
|
887
|
-
return
|
|
898
|
+
return frame;
|
|
888
899
|
}
|
|
889
900
|
findParentFrame(frameId) {
|
|
890
|
-
const frame = this.
|
|
901
|
+
const frame = this.findFrameById(frameId);
|
|
891
902
|
const iframeRef = frame?.iframeRef;
|
|
892
903
|
if (iframeRef == null) {
|
|
893
904
|
return null;
|
|
@@ -895,7 +906,7 @@ var VxPageView = class {
|
|
|
895
906
|
return this.findFrameByRef(iframeRef);
|
|
896
907
|
}
|
|
897
908
|
isFrameShown(frameId) {
|
|
898
|
-
const frame = this.
|
|
909
|
+
const frame = this.findFrameById(frameId);
|
|
899
910
|
if (!frame) {
|
|
900
911
|
return false;
|
|
901
912
|
}
|
|
@@ -904,14 +915,12 @@ var VxPageView = class {
|
|
|
904
915
|
if (parentFrame == null || iframeRef == null) {
|
|
905
916
|
return true;
|
|
906
917
|
}
|
|
907
|
-
const
|
|
908
|
-
const existsInParent = !!vxTree?.findNode(iframeRef);
|
|
918
|
+
const existsInParent = !!parentFrame.findNode(iframeRef);
|
|
909
919
|
return existsInParent ? this.isFrameShown(parentFrame.frameId) : false;
|
|
910
920
|
}
|
|
911
921
|
renderAll(options) {
|
|
912
|
-
return this.
|
|
913
|
-
const
|
|
914
|
-
const rendered = vxTree?.render(options);
|
|
922
|
+
return this.vxTrees.map((frame) => {
|
|
923
|
+
const rendered = frame?.render(options);
|
|
915
924
|
return [
|
|
916
925
|
`FRAME ${frame.frameId}${frame.iframeRef ? ` [@${frame.iframeRef}]` : ""}`,
|
|
917
926
|
rendered
|
|
@@ -920,7 +929,6 @@ var VxPageView = class {
|
|
|
920
929
|
}
|
|
921
930
|
};
|
|
922
931
|
export {
|
|
923
|
-
Counter,
|
|
924
932
|
INTERACTIVE_ROLES,
|
|
925
933
|
INTERACTIVE_TAGS,
|
|
926
934
|
PointSet,
|
|
@@ -943,6 +951,7 @@ export {
|
|
|
943
951
|
containsSelector,
|
|
944
952
|
escapeAttribute,
|
|
945
953
|
fixZIndex,
|
|
954
|
+
getElementDepth,
|
|
946
955
|
getNormalizedText,
|
|
947
956
|
getOffsetTop,
|
|
948
957
|
getOverlayContainer,
|
|
@@ -958,10 +967,12 @@ export {
|
|
|
958
967
|
isRandomIdentifier,
|
|
959
968
|
isRectInViewport,
|
|
960
969
|
makeOverlaysOpaque,
|
|
970
|
+
normalizeRef,
|
|
961
971
|
normalizeText,
|
|
962
972
|
probeViewport,
|
|
963
973
|
renderVxNode,
|
|
964
974
|
resolveDomNode,
|
|
975
|
+
serializeElement,
|
|
965
976
|
showPoint,
|
|
966
977
|
traverseElements,
|
|
967
978
|
traverseVxNode,
|
package/out/page/dom.d.ts
CHANGED
|
@@ -19,3 +19,5 @@ export declare function isRectInViewport(rect: DOMRect, viewport: Viewport): boo
|
|
|
19
19
|
export declare function makeOverlaysOpaque(el: HTMLElement): void;
|
|
20
20
|
export declare function isRandomIdentifier(str: string): boolean;
|
|
21
21
|
export declare function fixZIndex(el: HTMLElement): void;
|
|
22
|
+
export declare function getElementDepth(el: Element): number;
|
|
23
|
+
export declare function serializeElement(el: Element): string;
|
package/out/page/dom.js
CHANGED
|
@@ -177,4 +177,22 @@ export function fixZIndex(el) {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
+
export function getElementDepth(el) {
|
|
181
|
+
let depth = 0;
|
|
182
|
+
while (el.parentElement) {
|
|
183
|
+
el = el.parentElement;
|
|
184
|
+
depth += 1;
|
|
185
|
+
}
|
|
186
|
+
return depth;
|
|
187
|
+
}
|
|
188
|
+
export function serializeElement(el) {
|
|
189
|
+
const sig = [];
|
|
190
|
+
const depth = getElementDepth(el);
|
|
191
|
+
sig.push(String(depth));
|
|
192
|
+
sig.push(el.tagName);
|
|
193
|
+
for (const attr of el.attributes) {
|
|
194
|
+
sig.push(`${attr.name}=${attr.value}`);
|
|
195
|
+
}
|
|
196
|
+
return sig.join('|');
|
|
197
|
+
}
|
|
180
198
|
//# sourceMappingURL=dom.js.map
|