@ubio/webvision 2.6.5 → 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 -254
- package/build/global.js.map +3 -3
- package/build/page.mjs +253 -254
- 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/render.d.ts +0 -1
- package/out/page/render.js +0 -6
- package/out/page/render.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,197 +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
|
-
const whitelistRefs = options.whitelistRefs ?? [];
|
|
669
|
-
for (const { vxNode, depth } of traverseVxNode(scope)) {
|
|
670
|
-
if (whitelistRefs.length > 0) {
|
|
671
|
-
if (!whitelistRefs.includes(vxNode.ref)) {
|
|
672
|
-
continue;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
if (options.skipNonInteractive && !vxNode.isInteractive) {
|
|
676
|
-
continue;
|
|
677
|
-
}
|
|
678
|
-
const indent = options.skipNonInteractive ? "" : " ".repeat(depth);
|
|
679
|
-
buffer.push(renderIndentedLine(indent, vxNode, options));
|
|
680
|
-
}
|
|
681
|
-
return buffer.join("\n");
|
|
682
|
-
}
|
|
683
|
-
function renderIndentedLine(indent, vxNode, options = {}) {
|
|
684
|
-
const diffPrefix = options.renderDiff ? vxNode.isNew ? "+ " : " " : "";
|
|
685
|
-
if (!vxNode.tagName) {
|
|
686
|
-
return [diffPrefix, indent, vxNode.textContent].filter(Boolean).join("");
|
|
687
|
-
}
|
|
688
|
-
const tagLine = renderTagLine(vxNode, options);
|
|
689
|
-
const htmlStyle = options.renderStyle === "html";
|
|
690
|
-
return [
|
|
691
|
-
diffPrefix,
|
|
692
|
-
indent,
|
|
693
|
-
tagLine,
|
|
694
|
-
htmlStyle ? "" : " ",
|
|
695
|
-
vxNode.textContent ?? ""
|
|
696
|
-
].join("");
|
|
697
|
-
}
|
|
698
|
-
function renderTagLine(vxNode, options) {
|
|
699
|
-
const htmlStyle = options.renderStyle === "html";
|
|
700
|
-
const components = [];
|
|
701
|
-
if (options.renderTagNames && vxNode.tagName) {
|
|
702
|
-
components.push(vxNode.tagName);
|
|
703
|
-
}
|
|
704
|
-
if (options.renderIds && vxNode.id) {
|
|
705
|
-
if (htmlStyle) {
|
|
706
|
-
components.push(`id="${vxNode.id}"`);
|
|
707
|
-
} else {
|
|
708
|
-
components.push(`#${vxNode.id}`);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
if (options.renderClassNames && vxNode.classList?.length) {
|
|
712
|
-
if (htmlStyle) {
|
|
713
|
-
components.push(`class="${vxNode.classList.join(" ")}"`);
|
|
714
|
-
} else {
|
|
715
|
-
components.push("." + vxNode.classList.join("."));
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
if (options.renderRefs) {
|
|
719
|
-
const isRenderRef = [
|
|
720
|
-
options.renderRefs === "all",
|
|
721
|
-
options.renderRefs === true && !isContainerNode(vxNode)
|
|
722
|
-
].some(Boolean);
|
|
723
|
-
if (isRenderRef) {
|
|
724
|
-
components.push(`[@${vxNode.ref}]`);
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
const attrs = [];
|
|
728
|
-
if (options.renderLabelAttrs) {
|
|
729
|
-
for (const [attr, value] of Object.entries(vxNode.labelAttrs ?? {})) {
|
|
730
|
-
attrs.push(`${attr}="${truncateAttrValue(value)}"`);
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
if (options.renderValueAttrs) {
|
|
734
|
-
for (const [attr, value] of Object.entries(vxNode.valueAttrs ?? {})) {
|
|
735
|
-
attrs.push(`${attr}="${truncateAttrValue(value)}"`);
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
if (options.renderSrcAttrs) {
|
|
739
|
-
for (const [attr, value] of Object.entries(vxNode.srcAttrs ?? {})) {
|
|
740
|
-
attrs.push(`${attr}="${truncateAttrValue(value)}"`);
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
if (htmlStyle) {
|
|
744
|
-
components.push(...attrs);
|
|
745
|
-
} else {
|
|
746
|
-
components.push(...attrs.map((attr) => `[${attr}]`));
|
|
747
|
-
}
|
|
748
|
-
return htmlStyle ? `<${components.join(" ")}>` : components.join("");
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
// src/page/snapshot.ts
|
|
752
|
-
var VX_DOM_SYMBOL = Symbol("vx:dom");
|
|
753
|
-
var VX_TREE_SYMBOL = Symbol("vx:tree");
|
|
754
|
-
var VX_LAST_REFS_SYMBOL = Symbol("vx:lastRefs");
|
|
755
|
-
function captureSnapshot(options = {}) {
|
|
756
|
-
const parser = new VxTreeParser(options);
|
|
757
|
-
const domMap = parser.getDomMap();
|
|
758
|
-
const vxTree = parser.getTree();
|
|
759
|
-
globalThis[VX_DOM_SYMBOL] = domMap;
|
|
760
|
-
globalThis[VX_TREE_SYMBOL] = vxTree;
|
|
761
|
-
return vxTree;
|
|
762
|
-
}
|
|
763
|
-
function getSnapshot() {
|
|
764
|
-
const vxTree = globalThis[VX_TREE_SYMBOL];
|
|
765
|
-
if (!vxTree) {
|
|
766
|
-
throw new Error("[VX] Snapshot not found");
|
|
767
|
-
}
|
|
768
|
-
return vxTree;
|
|
769
|
-
}
|
|
770
|
-
function resolveDomNode(ref) {
|
|
771
|
-
const domMap = globalThis[VX_DOM_SYMBOL];
|
|
772
|
-
if (!domMap) {
|
|
773
|
-
return null;
|
|
774
|
-
}
|
|
775
|
-
return domMap.get(ref) ?? null;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
// src/page/tree.ts
|
|
779
|
-
var VxTreeView = class {
|
|
780
|
-
constructor(nodes, refRange) {
|
|
781
|
-
this.nodes = nodes;
|
|
782
|
-
this.refRange = refRange;
|
|
783
|
-
this.refMap = /* @__PURE__ */ new Map();
|
|
784
|
-
this.buildRefMap(nodes);
|
|
785
|
-
}
|
|
786
|
-
get nodeCount() {
|
|
787
|
-
return this.refMap.size;
|
|
788
|
-
}
|
|
789
|
-
*traverse() {
|
|
790
|
-
for (const vxNode of this.nodes) {
|
|
791
|
-
yield* traverseVxNode(vxNode, 0);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
buildRefMap(nodes) {
|
|
795
|
-
for (const node of nodes) {
|
|
796
|
-
this.refMap.set(node.ref, node);
|
|
797
|
-
this.buildRefMap(node.children ?? []);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
findNode(ref) {
|
|
801
|
-
return this.refMap.get(ref) ?? null;
|
|
802
|
-
}
|
|
803
|
-
render(options = {}) {
|
|
804
|
-
return this.nodes.map((node) => {
|
|
805
|
-
return renderVxNode(node, options);
|
|
806
|
-
}).join("\n");
|
|
807
|
-
}
|
|
808
|
-
highlight(options = {}) {
|
|
809
|
-
if (options.clearOverlay) {
|
|
810
|
-
clearOverlay();
|
|
811
|
-
}
|
|
812
|
-
const leafOnly = !options.includeAll;
|
|
813
|
-
const refs = this.collectRefs(leafOnly, options.filterRefs);
|
|
814
|
-
this.highlightRefs(refs, options);
|
|
815
|
-
}
|
|
816
|
-
highlightRefs(refs, options = {}) {
|
|
817
|
-
for (const ref of refs) {
|
|
818
|
-
const el = resolveDomNode(ref);
|
|
819
|
-
if (el instanceof Element) {
|
|
820
|
-
const vxNode = this.findNode(ref);
|
|
821
|
-
const isNew = vxNode?.isNew ?? false;
|
|
822
|
-
const color = options.useColors ? void 0 : isNew ? "#0a0" : "#060";
|
|
823
|
-
highlightEl(el, ref, color);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
collectRefs(leafOnly = true, filterRefs) {
|
|
828
|
-
const refs = [];
|
|
829
|
-
for (const { vxNode } of this.traverse()) {
|
|
830
|
-
if (leafOnly && isContainerNode(vxNode)) {
|
|
831
|
-
continue;
|
|
832
|
-
}
|
|
833
|
-
if (filterRefs && !filterRefs.includes(vxNode.ref)) {
|
|
834
|
-
continue;
|
|
835
|
-
}
|
|
836
|
-
refs.push(vxNode.ref);
|
|
837
|
-
}
|
|
838
|
-
return refs;
|
|
839
|
-
}
|
|
840
|
-
};
|
|
841
|
-
|
|
842
|
-
// src/page/frame.ts
|
|
860
|
+
// src/page/page.ts
|
|
843
861
|
var VxPageView = class {
|
|
844
|
-
constructor(
|
|
845
|
-
this.vxFrames = vxFrames;
|
|
846
|
-
this.vxFrameMap = /* @__PURE__ */ new Map();
|
|
862
|
+
constructor(vxTrees) {
|
|
847
863
|
this.vxTreeMap = /* @__PURE__ */ new Map();
|
|
848
|
-
for (const
|
|
849
|
-
this.
|
|
850
|
-
this.vxTreeMap.set(frame.frameId, new VxTreeView(frame.nodes, frame.refRange));
|
|
864
|
+
for (const vxTree of vxTrees) {
|
|
865
|
+
this.vxTreeMap.set(vxTree.frameId, vxTree);
|
|
851
866
|
}
|
|
852
867
|
}
|
|
853
868
|
get vxTrees() {
|
|
@@ -857,43 +872,28 @@ var VxPageView = class {
|
|
|
857
872
|
return this.vxTrees.reduce((acc, vxTree) => acc + vxTree.nodeCount, 0);
|
|
858
873
|
}
|
|
859
874
|
findNodeByRef(ref) {
|
|
860
|
-
const
|
|
861
|
-
if (
|
|
875
|
+
const frame = this.findFrameByRef(ref);
|
|
876
|
+
if (frame == null) {
|
|
862
877
|
return null;
|
|
863
878
|
}
|
|
864
|
-
return
|
|
879
|
+
return frame.findNode(ref);
|
|
865
880
|
}
|
|
866
|
-
|
|
881
|
+
findFrameById(frameId) {
|
|
867
882
|
return this.vxTreeMap.get(frameId) ?? null;
|
|
868
883
|
}
|
|
869
|
-
findTreeByRef(ref) {
|
|
870
|
-
const vxFrame = this.findFrameByRef(ref);
|
|
871
|
-
if (vxFrame == null) {
|
|
872
|
-
return null;
|
|
873
|
-
}
|
|
874
|
-
return this.vxTreeMap.get(vxFrame.frameId) ?? null;
|
|
875
|
-
}
|
|
876
|
-
findFrameByFrameId(frameId) {
|
|
877
|
-
return this.vxFrameMap.get(frameId) ?? null;
|
|
878
|
-
}
|
|
879
884
|
findFrameByRef(ref) {
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
);
|
|
883
|
-
if (!vxFrame) {
|
|
884
|
-
return null;
|
|
885
|
-
}
|
|
886
|
-
return this.vxFrameMap.get(vxFrame.frameId) ?? null;
|
|
885
|
+
const frame = this.vxTrees.find((f) => f.hasRef(ref));
|
|
886
|
+
return frame ?? null;
|
|
887
887
|
}
|
|
888
888
|
getFrameByRef(ref) {
|
|
889
|
-
const
|
|
890
|
-
if (
|
|
889
|
+
const frame = this.findFrameByRef(ref);
|
|
890
|
+
if (frame == null) {
|
|
891
891
|
throw new Error(`[VX] Frame not found for [ref=${ref}]`);
|
|
892
892
|
}
|
|
893
|
-
return
|
|
893
|
+
return frame;
|
|
894
894
|
}
|
|
895
895
|
findParentFrame(frameId) {
|
|
896
|
-
const frame = this.
|
|
896
|
+
const frame = this.findFrameById(frameId);
|
|
897
897
|
const iframeRef = frame?.iframeRef;
|
|
898
898
|
if (iframeRef == null) {
|
|
899
899
|
return null;
|
|
@@ -901,7 +901,7 @@ var VxPageView = class {
|
|
|
901
901
|
return this.findFrameByRef(iframeRef);
|
|
902
902
|
}
|
|
903
903
|
isFrameShown(frameId) {
|
|
904
|
-
const frame = this.
|
|
904
|
+
const frame = this.findFrameById(frameId);
|
|
905
905
|
if (!frame) {
|
|
906
906
|
return false;
|
|
907
907
|
}
|
|
@@ -910,14 +910,12 @@ var VxPageView = class {
|
|
|
910
910
|
if (parentFrame == null || iframeRef == null) {
|
|
911
911
|
return true;
|
|
912
912
|
}
|
|
913
|
-
const
|
|
914
|
-
const existsInParent = !!vxTree?.findNode(iframeRef);
|
|
913
|
+
const existsInParent = !!parentFrame.findNode(iframeRef);
|
|
915
914
|
return existsInParent ? this.isFrameShown(parentFrame.frameId) : false;
|
|
916
915
|
}
|
|
917
916
|
renderAll(options) {
|
|
918
|
-
return this.
|
|
919
|
-
const
|
|
920
|
-
const rendered = vxTree?.render(options);
|
|
917
|
+
return this.vxTrees.map((frame) => {
|
|
918
|
+
const rendered = frame?.render(options);
|
|
921
919
|
return [
|
|
922
920
|
`FRAME ${frame.frameId}${frame.iframeRef ? ` [@${frame.iframeRef}]` : ""}`,
|
|
923
921
|
rendered
|
|
@@ -926,7 +924,6 @@ var VxPageView = class {
|
|
|
926
924
|
}
|
|
927
925
|
};
|
|
928
926
|
export {
|
|
929
|
-
Counter,
|
|
930
927
|
INTERACTIVE_ROLES,
|
|
931
928
|
INTERACTIVE_TAGS,
|
|
932
929
|
PointSet,
|
|
@@ -949,6 +946,7 @@ export {
|
|
|
949
946
|
containsSelector,
|
|
950
947
|
escapeAttribute,
|
|
951
948
|
fixZIndex,
|
|
949
|
+
getElementDepth,
|
|
952
950
|
getNormalizedText,
|
|
953
951
|
getOffsetTop,
|
|
954
952
|
getOverlayContainer,
|
|
@@ -968,6 +966,7 @@ export {
|
|
|
968
966
|
probeViewport,
|
|
969
967
|
renderVxNode,
|
|
970
968
|
resolveDomNode,
|
|
969
|
+
serializeElement,
|
|
971
970
|
showPoint,
|
|
972
971
|
traverseElements,
|
|
973
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
|