@ubio/webvision 2.6.6 → 3.0.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/build/global.js +253 -248
- package/build/global.js.map +3 -3
- package/build/page.mjs +253 -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 +12 -3
- package/out/page/tree.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) {
|
|
@@ -280,6 +284,186 @@ function isObjectEmpty(obj) {
|
|
|
280
284
|
return !Object.values(obj).some((value) => !!value);
|
|
281
285
|
}
|
|
282
286
|
|
|
287
|
+
// src/page/render.ts
|
|
288
|
+
function renderVxNode(scope, options = {}) {
|
|
289
|
+
const buffer = [];
|
|
290
|
+
for (const { vxNode, depth } of traverseVxNode(scope)) {
|
|
291
|
+
if (options.skipNonInteractive && !vxNode.isInteractive) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const indent = options.skipNonInteractive ? "" : " ".repeat(depth);
|
|
295
|
+
buffer.push(renderIndentedLine(indent, vxNode, options));
|
|
296
|
+
}
|
|
297
|
+
return buffer.join("\n");
|
|
298
|
+
}
|
|
299
|
+
function renderIndentedLine(indent, vxNode, options = {}) {
|
|
300
|
+
const diffPrefix = options.renderDiff ? vxNode.isNew ? "+ " : " " : "";
|
|
301
|
+
if (!vxNode.tagName) {
|
|
302
|
+
return [diffPrefix, indent, vxNode.textContent].filter(Boolean).join("");
|
|
303
|
+
}
|
|
304
|
+
const tagLine = renderTagLine(vxNode, options);
|
|
305
|
+
const htmlStyle = options.renderStyle === "html";
|
|
306
|
+
return [
|
|
307
|
+
diffPrefix,
|
|
308
|
+
indent,
|
|
309
|
+
tagLine,
|
|
310
|
+
htmlStyle ? "" : " ",
|
|
311
|
+
vxNode.textContent ?? ""
|
|
312
|
+
].join("");
|
|
313
|
+
}
|
|
314
|
+
function renderTagLine(vxNode, options) {
|
|
315
|
+
const htmlStyle = options.renderStyle === "html";
|
|
316
|
+
const components = [];
|
|
317
|
+
if (options.renderTagNames && vxNode.tagName) {
|
|
318
|
+
components.push(vxNode.tagName);
|
|
319
|
+
}
|
|
320
|
+
if (options.renderIds && vxNode.id) {
|
|
321
|
+
if (htmlStyle) {
|
|
322
|
+
components.push(`id="${vxNode.id}"`);
|
|
323
|
+
} else {
|
|
324
|
+
components.push(`#${vxNode.id}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (options.renderClassNames && vxNode.classList?.length) {
|
|
328
|
+
if (htmlStyle) {
|
|
329
|
+
components.push(`class="${vxNode.classList.join(" ")}"`);
|
|
330
|
+
} else {
|
|
331
|
+
components.push("." + vxNode.classList.join("."));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (options.renderRefs) {
|
|
335
|
+
const isRenderRef = [
|
|
336
|
+
options.renderRefs === "all",
|
|
337
|
+
options.renderRefs === true && !isContainerNode(vxNode)
|
|
338
|
+
].some(Boolean);
|
|
339
|
+
if (isRenderRef) {
|
|
340
|
+
components.push(`[@${vxNode.ref}]`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
const attrs = [];
|
|
344
|
+
if (options.renderLabelAttrs) {
|
|
345
|
+
for (const [attr, value] of Object.entries(vxNode.labelAttrs ?? {})) {
|
|
346
|
+
attrs.push(`${attr}="${truncateAttrValue(value)}"`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (options.renderValueAttrs) {
|
|
350
|
+
for (const [attr, value] of Object.entries(vxNode.valueAttrs ?? {})) {
|
|
351
|
+
attrs.push(`${attr}="${truncateAttrValue(value)}"`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (options.renderSrcAttrs) {
|
|
355
|
+
for (const [attr, value] of Object.entries(vxNode.srcAttrs ?? {})) {
|
|
356
|
+
attrs.push(`${attr}="${truncateAttrValue(value)}"`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (htmlStyle) {
|
|
360
|
+
components.push(...attrs);
|
|
361
|
+
} else {
|
|
362
|
+
components.push(...attrs.map((attr) => `[${attr}]`));
|
|
363
|
+
}
|
|
364
|
+
return htmlStyle ? `<${components.join(" ")}>` : components.join("");
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// src/page/snapshot.ts
|
|
368
|
+
var VX_DOM_SYMBOL = Symbol("vx:dom");
|
|
369
|
+
var VX_TREE_SYMBOL = Symbol("vx:tree");
|
|
370
|
+
var VX_LAST_REFS_SYMBOL = Symbol("vx:lastRefs");
|
|
371
|
+
async function captureSnapshot(options = {}) {
|
|
372
|
+
const parser = new VxTreeParser(options);
|
|
373
|
+
const domMap = parser.getDomMap();
|
|
374
|
+
const vxTree = parser.getTree(options.frameId ?? "0", options.iframeRef);
|
|
375
|
+
globalThis[VX_DOM_SYMBOL] = domMap;
|
|
376
|
+
globalThis[VX_TREE_SYMBOL] = vxTree;
|
|
377
|
+
return vxTree;
|
|
378
|
+
}
|
|
379
|
+
function getSnapshot() {
|
|
380
|
+
const vxTree = globalThis[VX_TREE_SYMBOL];
|
|
381
|
+
if (!vxTree) {
|
|
382
|
+
throw new Error("[VX] Snapshot not found");
|
|
383
|
+
}
|
|
384
|
+
return vxTree;
|
|
385
|
+
}
|
|
386
|
+
function resolveDomNode(ref) {
|
|
387
|
+
const domMap = globalThis[VX_DOM_SYMBOL];
|
|
388
|
+
if (!domMap) {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
return domMap.get(ref) ?? null;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// src/page/tree.ts
|
|
395
|
+
var VxTreeView = class {
|
|
396
|
+
constructor(nodes, frameId, iframeRef) {
|
|
397
|
+
this.nodes = nodes;
|
|
398
|
+
this.frameId = frameId;
|
|
399
|
+
this.iframeRef = iframeRef;
|
|
400
|
+
this.refMap = /* @__PURE__ */ new Map();
|
|
401
|
+
this.buildRefMap(nodes);
|
|
402
|
+
}
|
|
403
|
+
get nodeCount() {
|
|
404
|
+
return this.refMap.size;
|
|
405
|
+
}
|
|
406
|
+
*traverse() {
|
|
407
|
+
for (const vxNode of this.nodes) {
|
|
408
|
+
yield* traverseVxNode(vxNode, 0);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
buildRefMap(nodes) {
|
|
412
|
+
for (const node of nodes) {
|
|
413
|
+
if (node.ref) {
|
|
414
|
+
this.refMap.set(node.ref, node);
|
|
415
|
+
}
|
|
416
|
+
this.buildRefMap(node.children ?? []);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
hasRef(ref) {
|
|
420
|
+
return this.refMap.has(ref);
|
|
421
|
+
}
|
|
422
|
+
findNode(ref) {
|
|
423
|
+
return this.refMap.get(ref) ?? null;
|
|
424
|
+
}
|
|
425
|
+
render(options = {}) {
|
|
426
|
+
return this.nodes.map((node) => {
|
|
427
|
+
return renderVxNode(node, options);
|
|
428
|
+
}).join("\n");
|
|
429
|
+
}
|
|
430
|
+
highlight(options = {}) {
|
|
431
|
+
if (options.clearOverlay) {
|
|
432
|
+
clearOverlay();
|
|
433
|
+
}
|
|
434
|
+
const leafOnly = !options.includeAll;
|
|
435
|
+
const refs = this.collectRefs(leafOnly, options.filterRefs);
|
|
436
|
+
this.highlightRefs(refs, options);
|
|
437
|
+
}
|
|
438
|
+
highlightRefs(refs, options = {}) {
|
|
439
|
+
for (const ref of refs) {
|
|
440
|
+
const el = resolveDomNode(ref);
|
|
441
|
+
if (el instanceof Element) {
|
|
442
|
+
const vxNode = this.findNode(ref);
|
|
443
|
+
const isNew = vxNode?.isNew ?? false;
|
|
444
|
+
const color = options.useColors ? void 0 : isNew ? "#0a0" : "#060";
|
|
445
|
+
highlightEl(el, ref, color);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
collectRefs(leafOnly = true, filterRefs) {
|
|
450
|
+
const refs = [];
|
|
451
|
+
for (const { vxNode } of this.traverse()) {
|
|
452
|
+
if (!vxNode.ref) {
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
if (leafOnly && isContainerNode(vxNode)) {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (filterRefs && !filterRefs.includes(vxNode.ref)) {
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
refs.push(vxNode.ref);
|
|
462
|
+
}
|
|
463
|
+
return refs;
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
|
|
283
467
|
// src/page/parser.ts
|
|
284
468
|
var VX_NODE_SYMBOL = Symbol("vx:node");
|
|
285
469
|
var VX_IGNORE_SYMBOL = Symbol("vx:ignore");
|
|
@@ -351,41 +535,36 @@ var VX_KEEP_SELECTOR = "a, button, input, textarea, select, label, iframe, optio
|
|
|
351
535
|
var VxTreeParser = class {
|
|
352
536
|
constructor(options = {}) {
|
|
353
537
|
this.options = options;
|
|
538
|
+
this.viewport = { width: 0, height: 0 };
|
|
539
|
+
this.vxNodes = [];
|
|
354
540
|
this.probeElements = [];
|
|
355
541
|
this.domRefMap = /* @__PURE__ */ new Map();
|
|
356
|
-
|
|
542
|
+
}
|
|
543
|
+
async parse() {
|
|
357
544
|
this.viewport = getViewportSize();
|
|
358
|
-
this.
|
|
359
|
-
if (options.probeViewport) {
|
|
545
|
+
if (this.options.probeViewport) {
|
|
360
546
|
this.probeElements = probeViewport();
|
|
361
547
|
}
|
|
362
|
-
const vxRoot = this.
|
|
363
|
-
this.refRange = [startRef, this.counter.current()];
|
|
548
|
+
const vxRoot = await this.parseNode(document.documentElement, null);
|
|
364
549
|
this.vxNodes = this.pruneRecursive(vxRoot);
|
|
365
550
|
}
|
|
366
|
-
|
|
367
|
-
return this.
|
|
368
|
-
}
|
|
369
|
-
getTree() {
|
|
370
|
-
return new VxTreeView(this.vxNodes, this.refRange);
|
|
551
|
+
getTree(frameId, iframeRef) {
|
|
552
|
+
return new VxTreeView(this.vxNodes, frameId, iframeRef);
|
|
371
553
|
}
|
|
372
554
|
getNodes() {
|
|
373
555
|
return this.vxNodes;
|
|
374
556
|
}
|
|
375
|
-
getRefRange() {
|
|
376
|
-
return this.refRange;
|
|
377
|
-
}
|
|
378
557
|
getDomMap() {
|
|
379
558
|
return this.domRefMap;
|
|
380
559
|
}
|
|
381
|
-
parseNode(node, parent) {
|
|
560
|
+
async parseNode(node, parent) {
|
|
382
561
|
if (!node || node[VX_IGNORE_SYMBOL]) {
|
|
383
562
|
return null;
|
|
384
563
|
}
|
|
385
564
|
if (node instanceof Text) {
|
|
386
565
|
const textContent = normalizeText(node.textContent ?? "");
|
|
387
566
|
if (textContent) {
|
|
388
|
-
return this.makeNode(node, {
|
|
567
|
+
return await this.makeNode(node, {
|
|
389
568
|
textContent,
|
|
390
569
|
hasVisibleArea: parent?.hasVisibleArea,
|
|
391
570
|
isOutsideViewport: parent?.isOutsideViewport,
|
|
@@ -398,11 +577,11 @@ var VxTreeParser = class {
|
|
|
398
577
|
if (this.options.opaqueOverlays) {
|
|
399
578
|
makeOverlaysOpaque(node);
|
|
400
579
|
}
|
|
401
|
-
return this.parseElement(node);
|
|
580
|
+
return await this.parseElement(node);
|
|
402
581
|
}
|
|
403
582
|
return null;
|
|
404
583
|
}
|
|
405
|
-
parseElement(el) {
|
|
584
|
+
async parseElement(el) {
|
|
406
585
|
const skip = VX_IGNORE_TAGS.includes(el.tagName.toLowerCase()) || isHidden(el);
|
|
407
586
|
if (skip) {
|
|
408
587
|
this.clearRecursive(el);
|
|
@@ -411,7 +590,7 @@ var VxTreeParser = class {
|
|
|
411
590
|
const parentEl = el.matches("option, optgroup") ? el.closest("select") ?? el : el;
|
|
412
591
|
const rect = parentEl.getBoundingClientRect();
|
|
413
592
|
const id = el.getAttribute("id") ?? "";
|
|
414
|
-
const vxNode = this.makeNode(el, {
|
|
593
|
+
const vxNode = await this.makeNode(el, {
|
|
415
594
|
tagName: el.tagName.toLowerCase(),
|
|
416
595
|
id: isRandomIdentifier(id) ? void 0 : id,
|
|
417
596
|
classList: Array.from(el.classList).filter((cls) => !isRandomIdentifier(cls)).slice(0, 4),
|
|
@@ -424,7 +603,13 @@ var VxTreeParser = class {
|
|
|
424
603
|
isProbeHit: this.isProbeHit(parentEl),
|
|
425
604
|
isKept: el.matches(VX_KEEP_SELECTOR)
|
|
426
605
|
});
|
|
427
|
-
const children = [
|
|
606
|
+
const children = [];
|
|
607
|
+
for (const child of el.childNodes) {
|
|
608
|
+
const childNode = await this.parseNode(child, vxNode);
|
|
609
|
+
if (childNode != null) {
|
|
610
|
+
children.push(childNode);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
428
613
|
if (children.length === 1 && !children[0]?.tagName) {
|
|
429
614
|
vxNode.textContent = normalizeText(children[0].textContent ?? "");
|
|
430
615
|
} else {
|
|
@@ -432,15 +617,17 @@ var VxTreeParser = class {
|
|
|
432
617
|
}
|
|
433
618
|
return vxNode;
|
|
434
619
|
}
|
|
435
|
-
makeNode(node, spec) {
|
|
436
|
-
const ref = this.counter.next();
|
|
620
|
+
async makeNode(node, spec) {
|
|
437
621
|
const isNew = !node[VX_NODE_SYMBOL];
|
|
622
|
+
const ref = await this.createRef(node);
|
|
438
623
|
const vxNode = {
|
|
439
624
|
...spec,
|
|
440
625
|
ref,
|
|
441
626
|
isNew
|
|
442
627
|
};
|
|
443
|
-
|
|
628
|
+
if (ref) {
|
|
629
|
+
this.domRefMap.set(ref, node);
|
|
630
|
+
}
|
|
444
631
|
node[VX_NODE_SYMBOL] = vxNode;
|
|
445
632
|
return vxNode;
|
|
446
633
|
}
|
|
@@ -578,6 +765,13 @@ var VxTreeParser = class {
|
|
|
578
765
|
}
|
|
579
766
|
}
|
|
580
767
|
}
|
|
768
|
+
async createRef(node) {
|
|
769
|
+
if (node instanceof Element) {
|
|
770
|
+
const sig = serializeElement(node);
|
|
771
|
+
const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(sig).buffer);
|
|
772
|
+
return [...new Uint8Array(hash)].map((b) => b.toString(16).padStart(2, "0")).join("").substring(0, 8);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
581
775
|
};
|
|
582
776
|
|
|
583
777
|
// src/page/overlay.ts
|
|
@@ -621,7 +815,7 @@ function getOverlayContainer() {
|
|
|
621
815
|
}
|
|
622
816
|
|
|
623
817
|
// src/page/highlight.ts
|
|
624
|
-
function highlightEl(el, ref
|
|
818
|
+
function highlightEl(el, ref, color) {
|
|
625
819
|
if (!(el instanceof Element)) {
|
|
626
820
|
return;
|
|
627
821
|
}
|
|
@@ -657,191 +851,18 @@ function highlightEl(el, ref = 0, color) {
|
|
|
657
851
|
label.style.transform = "translateY(50%)";
|
|
658
852
|
label.textContent = String(ref);
|
|
659
853
|
}
|
|
660
|
-
function getRandomColor(
|
|
854
|
+
function getRandomColor(ref) {
|
|
855
|
+
const index = ref.charCodeAt(0);
|
|
661
856
|
const hue = index * 120 * 0.382 % 360;
|
|
662
857
|
return `hsl(${hue}, 85%, 50%)`;
|
|
663
858
|
}
|
|
664
859
|
|
|
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
|
|
860
|
+
// src/page/page.ts
|
|
837
861
|
var VxPageView = class {
|
|
838
|
-
constructor(
|
|
839
|
-
this.vxFrames = vxFrames;
|
|
840
|
-
this.vxFrameMap = /* @__PURE__ */ new Map();
|
|
862
|
+
constructor(vxTrees) {
|
|
841
863
|
this.vxTreeMap = /* @__PURE__ */ new Map();
|
|
842
|
-
for (const
|
|
843
|
-
this.
|
|
844
|
-
this.vxTreeMap.set(frame.frameId, new VxTreeView(frame.nodes, frame.refRange));
|
|
864
|
+
for (const vxTree of vxTrees) {
|
|
865
|
+
this.vxTreeMap.set(vxTree.frameId, vxTree);
|
|
845
866
|
}
|
|
846
867
|
}
|
|
847
868
|
get vxTrees() {
|
|
@@ -851,43 +872,28 @@ var VxPageView = class {
|
|
|
851
872
|
return this.vxTrees.reduce((acc, vxTree) => acc + vxTree.nodeCount, 0);
|
|
852
873
|
}
|
|
853
874
|
findNodeByRef(ref) {
|
|
854
|
-
const
|
|
855
|
-
if (
|
|
875
|
+
const frame = this.findFrameByRef(ref);
|
|
876
|
+
if (frame == null) {
|
|
856
877
|
return null;
|
|
857
878
|
}
|
|
858
|
-
return
|
|
879
|
+
return frame.findNode(ref);
|
|
859
880
|
}
|
|
860
|
-
|
|
881
|
+
findFrameById(frameId) {
|
|
861
882
|
return this.vxTreeMap.get(frameId) ?? null;
|
|
862
883
|
}
|
|
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
884
|
findFrameByRef(ref) {
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
);
|
|
877
|
-
if (!vxFrame) {
|
|
878
|
-
return null;
|
|
879
|
-
}
|
|
880
|
-
return this.vxFrameMap.get(vxFrame.frameId) ?? null;
|
|
885
|
+
const frame = this.vxTrees.find((f) => f.hasRef(ref));
|
|
886
|
+
return frame ?? null;
|
|
881
887
|
}
|
|
882
888
|
getFrameByRef(ref) {
|
|
883
|
-
const
|
|
884
|
-
if (
|
|
889
|
+
const frame = this.findFrameByRef(ref);
|
|
890
|
+
if (frame == null) {
|
|
885
891
|
throw new Error(`[VX] Frame not found for [ref=${ref}]`);
|
|
886
892
|
}
|
|
887
|
-
return
|
|
893
|
+
return frame;
|
|
888
894
|
}
|
|
889
895
|
findParentFrame(frameId) {
|
|
890
|
-
const frame = this.
|
|
896
|
+
const frame = this.findFrameById(frameId);
|
|
891
897
|
const iframeRef = frame?.iframeRef;
|
|
892
898
|
if (iframeRef == null) {
|
|
893
899
|
return null;
|
|
@@ -895,7 +901,7 @@ var VxPageView = class {
|
|
|
895
901
|
return this.findFrameByRef(iframeRef);
|
|
896
902
|
}
|
|
897
903
|
isFrameShown(frameId) {
|
|
898
|
-
const frame = this.
|
|
904
|
+
const frame = this.findFrameById(frameId);
|
|
899
905
|
if (!frame) {
|
|
900
906
|
return false;
|
|
901
907
|
}
|
|
@@ -904,14 +910,12 @@ var VxPageView = class {
|
|
|
904
910
|
if (parentFrame == null || iframeRef == null) {
|
|
905
911
|
return true;
|
|
906
912
|
}
|
|
907
|
-
const
|
|
908
|
-
const existsInParent = !!vxTree?.findNode(iframeRef);
|
|
913
|
+
const existsInParent = !!parentFrame.findNode(iframeRef);
|
|
909
914
|
return existsInParent ? this.isFrameShown(parentFrame.frameId) : false;
|
|
910
915
|
}
|
|
911
916
|
renderAll(options) {
|
|
912
|
-
return this.
|
|
913
|
-
const
|
|
914
|
-
const rendered = vxTree?.render(options);
|
|
917
|
+
return this.vxTrees.map((frame) => {
|
|
918
|
+
const rendered = frame?.render(options);
|
|
915
919
|
return [
|
|
916
920
|
`FRAME ${frame.frameId}${frame.iframeRef ? ` [@${frame.iframeRef}]` : ""}`,
|
|
917
921
|
rendered
|
|
@@ -920,7 +924,6 @@ var VxPageView = class {
|
|
|
920
924
|
}
|
|
921
925
|
};
|
|
922
926
|
export {
|
|
923
|
-
Counter,
|
|
924
927
|
INTERACTIVE_ROLES,
|
|
925
928
|
INTERACTIVE_TAGS,
|
|
926
929
|
PointSet,
|
|
@@ -943,6 +946,7 @@ export {
|
|
|
943
946
|
containsSelector,
|
|
944
947
|
escapeAttribute,
|
|
945
948
|
fixZIndex,
|
|
949
|
+
getElementDepth,
|
|
946
950
|
getNormalizedText,
|
|
947
951
|
getOffsetTop,
|
|
948
952
|
getOverlayContainer,
|
|
@@ -962,6 +966,7 @@ export {
|
|
|
962
966
|
probeViewport,
|
|
963
967
|
renderVxNode,
|
|
964
968
|
resolveDomNode,
|
|
969
|
+
serializeElement,
|
|
965
970
|
showPoint,
|
|
966
971
|
traverseElements,
|
|
967
972
|
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
|