@ubio/webvision 2.3.0 → 2.4.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/page.mjs CHANGED
@@ -14,7 +14,7 @@ var Counter = class {
14
14
 
15
15
  // src/page/dom.ts
16
16
  var ORIGINAL_STYLE_SYMBOL = Symbol("vx:originalStyle");
17
- var INTERACTIVE_TAGS = ["a", "button", "input", "textarea", "select", "label"];
17
+ var INTERACTIVE_TAGS = ["a", "button", "input", "textarea", "select", "label", "option", "optgroup"];
18
18
  var INTERACTIVE_ROLES = [
19
19
  "button",
20
20
  "link",
@@ -292,7 +292,7 @@ var VX_TAG_PREFERENCE = [
292
292
  "sub",
293
293
  "sup"
294
294
  ];
295
- var VX_KEEP_SELECTOR = "a, button, input, textarea, select, label, iframe";
295
+ var VX_KEEP_SELECTOR = "a, button, input, textarea, select, label, iframe, option, optgroup";
296
296
  var VxTreeParser = class {
297
297
  constructor(options = {}) {
298
298
  this.options = options;
@@ -324,7 +324,7 @@ var VxTreeParser = class {
324
324
  return this.domRefMap;
325
325
  }
326
326
  parseNode(node, parent) {
327
- if (node[VX_IGNORE_SYMBOL]) {
327
+ if (!node || node[VX_IGNORE_SYMBOL]) {
328
328
  return null;
329
329
  }
330
330
  if (node instanceof Text) {
@@ -595,32 +595,17 @@ function* traverseVxNode(vxNode, depth = 0) {
595
595
  function renderVxNode(scope, options = {}) {
596
596
  const buffer = [];
597
597
  const whitelistRefs = options.whitelistRefs ?? [];
598
- const stack = [];
599
598
  for (const { vxNode, depth } of traverseVxNode(scope)) {
600
599
  if (whitelistRefs.length > 0) {
601
600
  if (!whitelistRefs.includes(vxNode.ref)) {
602
601
  continue;
603
602
  }
604
603
  }
605
- while (depth < stack.length) {
606
- stack.pop();
604
+ if (options.skipNonInteractive && !vxNode.isInteractive) {
605
+ continue;
607
606
  }
608
- const parentState = stack[depth - 1];
609
- const parentIndentLevel = parentState?.indentLevel ?? 0;
610
- const parentContributes = parentState?.contributes ?? false;
611
- let currentIndentLevel;
612
- if (depth === 0) {
613
- currentIndentLevel = 0;
614
- } else {
615
- currentIndentLevel = parentIndentLevel + (parentContributes ? 1 : 0);
616
- }
617
- const indent = " ".repeat(currentIndentLevel);
618
- const line = renderIndentedLine(indent, vxNode, options);
619
- if (line.trim()) {
620
- buffer.push(line);
621
- }
622
- const contributes = !!(options.renderNonInteractive || vxNode.isInteractive);
623
- stack.push({ indentLevel: currentIndentLevel, contributes });
607
+ const indent = options.skipNonInteractive ? "" : " ".repeat(depth);
608
+ buffer.push(renderIndentedLine(indent, vxNode, options));
624
609
  }
625
610
  return buffer.join("\n");
626
611
  }
@@ -628,7 +613,7 @@ function renderIndentedLine(indent, vxNode, options = {}) {
628
613
  if (!vxNode.tagName) {
629
614
  return [indent, vxNode.textContent].filter(Boolean).join("");
630
615
  }
631
- const tagLine = !options.renderNonInteractive && !vxNode.isInteractive ? "" : renderTagLine(vxNode, options);
616
+ const tagLine = renderTagLine(vxNode, options);
632
617
  const htmlStyle = options.renderStyle === "html";
633
618
  return [
634
619
  indent,
@@ -757,6 +742,14 @@ var VxTreeView = class {
757
742
  }
758
743
  return refs;
759
744
  }
745
+ highlightRefs(refs) {
746
+ for (const ref of refs) {
747
+ const el = resolveDomNode(ref);
748
+ if (el instanceof Element) {
749
+ highlightEl(el, ref);
750
+ }
751
+ }
752
+ }
760
753
  collectRefs(leafOnly = true, filterRefs) {
761
754
  const refs = [];
762
755
  for (const { vxNode } of this.traverse()) {
@@ -0,0 +1,6 @@
1
+ export declare class Counter {
2
+ value: number;
3
+ constructor(value?: number);
4
+ next(): number;
5
+ current(): number;
6
+ }
@@ -0,0 +1,13 @@
1
+ export class Counter {
2
+ constructor(value = 0) {
3
+ this.value = value;
4
+ }
5
+ next() {
6
+ this.value += 1;
7
+ return this.value;
8
+ }
9
+ current() {
10
+ return this.value;
11
+ }
12
+ }
13
+ //# sourceMappingURL=counter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"counter.js","sourceRoot":"","sources":["../../src/page/counter.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,OAAO;IAEhB,YAAmB,QAAQ,CAAC;QAAT,UAAK,GAAL,KAAK,CAAI;IAAG,CAAC;IAEhC,IAAI;QACA,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;QAChB,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,OAAO;QACH,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;CAEJ"}
@@ -0,0 +1,21 @@
1
+ export declare const INTERACTIVE_TAGS: string[];
2
+ export declare const INTERACTIVE_ROLES: string[];
3
+ export interface Viewport {
4
+ width: number;
5
+ height: number;
6
+ }
7
+ export declare function isHidden(el: Element): boolean;
8
+ export declare function getNormalizedText(el: Element): string;
9
+ export declare function normalizeText(str: string): string;
10
+ export declare function truncateAttrValue(value: string, limit?: number): string;
11
+ export declare function escapeAttribute(value: string): string;
12
+ export declare function containsSelector(el: Element, selector: string): boolean;
13
+ export declare function getOffsetTop(node: Node): number;
14
+ export declare function hasVisibleArea(element: Element): boolean;
15
+ export declare function isDeepHidden(element: Element): boolean;
16
+ export declare function isInteractive(el: Element): boolean;
17
+ export declare function getViewportSize(): Viewport;
18
+ export declare function isRectInViewport(rect: DOMRect, viewport: Viewport): boolean;
19
+ export declare function makeOverlaysOpaque(el: HTMLElement): void;
20
+ export declare function isRandomIdentifier(str: string): boolean;
21
+ export declare function fixZIndex(el: HTMLElement): void;
@@ -0,0 +1,172 @@
1
+ const ORIGINAL_STYLE_SYMBOL = Symbol('vx:originalStyle');
2
+ export const INTERACTIVE_TAGS = ['a', 'button', 'input', 'textarea', 'select', 'label', 'option', 'optgroup'];
3
+ export const INTERACTIVE_ROLES = [
4
+ 'button', 'link', 'checkbox', 'radio', 'textbox', 'combobox', 'listbox', 'menu', 'menuitem', 'menuitemcheckbox', 'menuitemradio', 'option', 'optgroup', 'progressbar', 'scrollbar', 'slider', 'spinbutton', 'switch', 'tab', 'tablist', 'timer', 'toolbar'
5
+ ];
6
+ export function isHidden(el) {
7
+ const style = getComputedStyle(el);
8
+ if (style.display === 'none') {
9
+ return true;
10
+ }
11
+ if (style.visibility === 'hidden') {
12
+ return true;
13
+ }
14
+ if (style.opacity === '0') {
15
+ return true;
16
+ }
17
+ return false;
18
+ }
19
+ export function getNormalizedText(el) {
20
+ return normalizeText(el.textContent ?? '');
21
+ }
22
+ export function normalizeText(str) {
23
+ return str
24
+ .replace(/\p{Cf}/gu, ' ')
25
+ .replace(/\s+/g, ' ')
26
+ .trim();
27
+ }
28
+ export function truncateAttrValue(value, limit = 100) {
29
+ if (value.match(/^(javascript:)?void/)) {
30
+ return '';
31
+ }
32
+ if (value.startsWith('data:')) {
33
+ return '';
34
+ }
35
+ if (value.length > limit) {
36
+ return value.slice(0, limit) + '…';
37
+ }
38
+ return value;
39
+ }
40
+ export function escapeAttribute(value) {
41
+ return value
42
+ .replace(/&/g, '&amp;') // Escape ampersand first!
43
+ .replace(/"/g, '&quot;') // Escape double quotes
44
+ .replace(/'/g, '&#39;') // Escape single quotes
45
+ .replace(/</g, '&lt;') // Escape less than
46
+ .replace(/>/g, '&gt;') // Escape greater than
47
+ .replace(/\r?\n/g, ' '); // Replace newlines with space
48
+ }
49
+ export function containsSelector(el, selector) {
50
+ return el.matches(selector) || !!el.querySelector(selector);
51
+ }
52
+ export function getOffsetTop(node) {
53
+ let y = 0;
54
+ let current = node;
55
+ while (current) {
56
+ y += current.offsetTop ?? 0;
57
+ current = current.offsetParent;
58
+ }
59
+ return y;
60
+ }
61
+ export function hasVisibleArea(element) {
62
+ const rect = element.getBoundingClientRect();
63
+ const area = rect.width * rect.height;
64
+ return area > 64;
65
+ }
66
+ export function isDeepHidden(element) {
67
+ if (isHidden(element)) {
68
+ return true;
69
+ }
70
+ if (!hasVisibleArea(element)) {
71
+ return [...element.children].every(el => isDeepHidden(el));
72
+ }
73
+ return false;
74
+ }
75
+ export function isInteractive(el) {
76
+ const htmlEl = el;
77
+ if (INTERACTIVE_TAGS.includes(el.tagName.toLowerCase())) {
78
+ return true;
79
+ }
80
+ const tabindex = htmlEl.getAttribute('tabindex');
81
+ if (tabindex && parseInt(tabindex) >= 0) {
82
+ return true;
83
+ }
84
+ const role = htmlEl.getAttribute('role') ?? htmlEl.getAttribute('aria-role') ?? '';
85
+ if (INTERACTIVE_ROLES.includes(role)) {
86
+ return true;
87
+ }
88
+ if (htmlEl.onclick != null) {
89
+ return true;
90
+ }
91
+ return false;
92
+ }
93
+ export function getViewportSize() {
94
+ return {
95
+ width: window.innerWidth,
96
+ height: window.innerHeight,
97
+ };
98
+ }
99
+ export function isRectInViewport(rect, viewport) {
100
+ return rect.left < viewport.width &&
101
+ rect.right > 0 &&
102
+ rect.top < viewport.height &&
103
+ rect.bottom > 0;
104
+ }
105
+ export function makeOverlaysOpaque(el) {
106
+ if (!el[ORIGINAL_STYLE_SYMBOL]) {
107
+ el[ORIGINAL_STYLE_SYMBOL] = el.getAttribute('style');
108
+ }
109
+ // Only apply to big elements
110
+ const rect = el.getBoundingClientRect();
111
+ if (rect.width < 500 || rect.height < 500) {
112
+ return;
113
+ }
114
+ const style = getComputedStyle(el);
115
+ if (style.pointerEvents === 'none') {
116
+ return;
117
+ }
118
+ el.style.setProperty('animation', 'none', 'important');
119
+ el.style.setProperty('transition', 'none', 'important');
120
+ el.style.setProperty('backdrop-filter', 'none', 'important');
121
+ el.style.setProperty('mix-blend-mode', 'normal', 'important');
122
+ el.style.setProperty('opacity', '1', 'important');
123
+ el.style.setProperty('filter', 'none', 'important');
124
+ const bg = style.backgroundColor;
125
+ el.style.setProperty('background-color', removeAlpha(bg));
126
+ }
127
+ function removeAlpha(color) {
128
+ const rgba = color.match(/^rgba\((.*)\)$/);
129
+ if (!rgba) {
130
+ return color;
131
+ }
132
+ const [r, g, b, a] = rgba[1].split(',').map(parseFloat);
133
+ if (a > 0 && a < 1) {
134
+ return `rgba(${r},${g},${b},1)`;
135
+ }
136
+ return color;
137
+ }
138
+ export function isRandomIdentifier(str) {
139
+ const words = str.split(/[_-]/g);
140
+ return !words.some(w => isWordLike(w)) || words.some(w => isRandomString(w));
141
+ }
142
+ function isWordLike(str) {
143
+ if (str.length <= 2) {
144
+ return false;
145
+ }
146
+ // Words don't usually have a lot of vowels or consonants in a row
147
+ if (/[^eyuioa]{4,}/.test(str)) {
148
+ return false;
149
+ }
150
+ if (/[eyuioa]{4,}/.test(str)) {
151
+ return false;
152
+ }
153
+ return true;
154
+ }
155
+ function isRandomString(str) {
156
+ // Hex strings will often consist of at least one digit and A-F letters
157
+ if (/^[0-9a-f]+$/i.test(str) && /[0-9]/.test(str)) {
158
+ return true;
159
+ }
160
+ // Digits separated by non-digits are a sign of random ids
161
+ if (/[0-9][^0-9]+[0-9]/.test(str)) {
162
+ return true;
163
+ }
164
+ return false;
165
+ }
166
+ export function fixZIndex(el) {
167
+ const style = getComputedStyle(el);
168
+ if (Number(style.zIndex) > 2147483600) {
169
+ el.style.setProperty('z-index', '2147483600', 'important');
170
+ }
171
+ }
172
+ //# sourceMappingURL=dom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom.js","sourceRoot":"","sources":["../../src/page/dom.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { VxNode } from './parser.js';
2
+ import { VxRenderOptions } from './render.js';
3
+ export interface VxFrame {
4
+ frameId: number;
5
+ iframeRef?: number;
6
+ refRange: [number, number];
7
+ nodes: VxNode[];
8
+ isOmitted?: boolean;
9
+ }
10
+ export declare class VxPageView<T extends VxFrame> {
11
+ readonly vxFrames: T[];
12
+ private vxFrameMap;
13
+ private vxTreeMap;
14
+ constructor(vxFrames: T[]);
15
+ findFrameByFrameId(frameId: number): T | null;
16
+ getFrameByFrameId(frameId: number): T;
17
+ findFrameByRef(ref: number): T | null;
18
+ getFrameByRef(ref: number): T;
19
+ findParentFrame(frameId: number): T | null;
20
+ isFrameShown(frameId: number): boolean;
21
+ renderAll(options: VxRenderOptions): string;
22
+ }
@@ -0,0 +1,69 @@
1
+ import { VxTreeView } from './tree.js';
2
+ export class VxPageView {
3
+ constructor(vxFrames) {
4
+ this.vxFrames = vxFrames;
5
+ this.vxFrameMap = new Map();
6
+ this.vxTreeMap = new Map();
7
+ for (const frame of vxFrames) {
8
+ this.vxFrameMap.set(frame.frameId, frame);
9
+ this.vxTreeMap.set(frame.frameId, new VxTreeView(frame.nodes, frame.refRange));
10
+ }
11
+ }
12
+ findFrameByFrameId(frameId) {
13
+ return this.vxFrameMap.get(frameId) ?? null;
14
+ }
15
+ getFrameByFrameId(frameId) {
16
+ const vxFrame = this.vxFrameMap.get(frameId);
17
+ if (vxFrame == null) {
18
+ throw new Error(`[VX] Frame not found for [frameId=${frameId}]`);
19
+ }
20
+ return vxFrame;
21
+ }
22
+ findFrameByRef(ref) {
23
+ const vxFrame = this.vxFrames.find(frame => ref >= frame.refRange[0] && ref <= frame.refRange[1]);
24
+ if (!vxFrame) {
25
+ return null;
26
+ }
27
+ return this.vxFrameMap.get(vxFrame.frameId) ?? null;
28
+ }
29
+ getFrameByRef(ref) {
30
+ const vxFrame = this.findFrameByRef(ref);
31
+ if (vxFrame == null) {
32
+ throw new Error(`[VX] Frame not found for [ref=${ref}]`);
33
+ }
34
+ return vxFrame;
35
+ }
36
+ findParentFrame(frameId) {
37
+ const frame = this.getFrameByFrameId(frameId);
38
+ const iframeRef = frame?.iframeRef;
39
+ if (iframeRef == null) {
40
+ return null;
41
+ }
42
+ return this.findFrameByRef(iframeRef);
43
+ }
44
+ isFrameShown(frameId) {
45
+ const frame = this.getFrameByFrameId(frameId);
46
+ if (!frame) {
47
+ return false;
48
+ }
49
+ const parentFrame = this.findParentFrame(frameId);
50
+ const { iframeRef } = frame;
51
+ if (parentFrame == null || iframeRef == null) {
52
+ return true;
53
+ }
54
+ const vxTree = this.vxTreeMap.get(frameId);
55
+ const existsInParent = !!vxTree?.findNode(iframeRef);
56
+ return existsInParent ? this.isFrameShown(parentFrame.frameId) : false;
57
+ }
58
+ renderAll(options) {
59
+ return this.vxFrames.map(frame => {
60
+ const vxTree = this.vxTreeMap.get(frame.frameId);
61
+ const rendered = vxTree?.render(options);
62
+ return [
63
+ `FRAME ${frame.frameId}`,
64
+ rendered,
65
+ ].join('\n\n');
66
+ }).join('\n\n');
67
+ }
68
+ }
69
+ //# sourceMappingURL=frame.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame.js","sourceRoot":"","sources":["../../src/page/frame.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export * from './counter.js';
2
+ export * from './dom.js';
3
+ export * from './frame.js';
4
+ export * from './overlay.js';
5
+ export * from './parser.js';
6
+ export * from './probe.js';
7
+ export * from './render.js';
8
+ export * from './snapshot.js';
9
+ export * from './traverse.js';
10
+ export * from './tree.js';
11
+ export * from './util.js';
@@ -0,0 +1,12 @@
1
+ export * from './counter.js';
2
+ export * from './dom.js';
3
+ export * from './frame.js';
4
+ export * from './overlay.js';
5
+ export * from './parser.js';
6
+ export * from './probe.js';
7
+ export * from './render.js';
8
+ export * from './snapshot.js';
9
+ export * from './traverse.js';
10
+ export * from './tree.js';
11
+ export * from './util.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/page/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function showPoint(x: number, y: number, clear?: boolean): void;
2
+ export declare function clearOverlay(): void;
3
+ export declare function highlightEl(el: Element, ref?: number): void;
@@ -0,0 +1,75 @@
1
+ import { VX_IGNORE_SYMBOL } from './parser.js';
2
+ export function showPoint(x, y, clear = true) {
3
+ if (clear) {
4
+ clearOverlay();
5
+ }
6
+ const point = document.createElement('div');
7
+ point.style.position = 'absolute';
8
+ point.style.left = `${x}px`;
9
+ point.style.top = `${y}px`;
10
+ point.style.width = '32px';
11
+ point.style.height = '32px';
12
+ point.style.transform = 'translate(-50%, -50%)';
13
+ point.style.backgroundColor = 'red';
14
+ point.style.borderRadius = '100%';
15
+ point.style.opacity = '0.5';
16
+ const container = getOverlayContainer();
17
+ container.appendChild(point);
18
+ }
19
+ export function clearOverlay() {
20
+ const container = getOverlayContainer();
21
+ container.remove();
22
+ }
23
+ export function highlightEl(el, ref = 0) {
24
+ if (!(el instanceof Element)) {
25
+ return;
26
+ }
27
+ const container = getOverlayContainer();
28
+ const color = getColor(ref);
29
+ const rect = el.getBoundingClientRect();
30
+ const overlay = document.createElement('div');
31
+ container.appendChild(overlay);
32
+ overlay.style.position = 'absolute';
33
+ overlay.style.top = `${rect.top}px`;
34
+ overlay.style.left = `${rect.left}px`;
35
+ overlay.style.width = `${rect.width}px`;
36
+ overlay.style.height = `${rect.height}px`;
37
+ overlay.style.border = `2px solid ${color}`;
38
+ const label = document.createElement('div');
39
+ overlay.appendChild(label);
40
+ label.style.position = 'absolute';
41
+ label.style.bottom = `100%`;
42
+ label.style.left = `0`;
43
+ label.style.backgroundColor = color;
44
+ label.style.color = 'white';
45
+ label.style.fontSize = '10px';
46
+ label.style.fontFamily = 'monospace';
47
+ label.style.fontWeight = 'normal';
48
+ label.style.fontStyle = 'normal';
49
+ label.style.opacity = '0.8';
50
+ label.style.padding = '0 2px';
51
+ label.style.transform = 'translateY(50%)';
52
+ label.textContent = String(ref);
53
+ }
54
+ function getOverlayContainer() {
55
+ let container = document.querySelector('#webvision-overlay');
56
+ if (!container) {
57
+ container = document.createElement('div');
58
+ container[VX_IGNORE_SYMBOL] = true;
59
+ container.id = 'webvision-overlay';
60
+ container.style.position = 'fixed';
61
+ container.style.top = '0';
62
+ container.style.left = '0';
63
+ container.style.bottom = '0';
64
+ container.style.right = '0';
65
+ container.style.zIndex = '2147483647'; // Maximum z-index value
66
+ container.style.pointerEvents = 'none';
67
+ document.body.appendChild(container);
68
+ }
69
+ return container;
70
+ }
71
+ function getColor(index) {
72
+ const hue = (index * 120 * .382) % 360;
73
+ return `hsl(${hue}, 85%, 50%)`;
74
+ }
75
+ //# sourceMappingURL=overlay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay.js","sourceRoot":"","sources":["../../src/page/overlay.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import { VxTreeView } from './tree.js';
2
+ export declare const VX_NODE_SYMBOL: unique symbol;
3
+ export declare const VX_IGNORE_SYMBOL: unique symbol;
4
+ export declare const VX_IGNORE_TAGS: string[];
5
+ export declare const VX_LABEL_ATTRS: string[];
6
+ export declare const VX_VALUE_ATTRS: string[];
7
+ export declare const VX_SRC_ATTRS: string[];
8
+ export declare const VX_TAG_PREFERENCE: string[];
9
+ export type VxDomNode = Element | Text;
10
+ export interface VxNode {
11
+ ref: number;
12
+ tagName?: string;
13
+ children?: VxNode[];
14
+ id?: string;
15
+ classList?: string[];
16
+ textContent?: string;
17
+ hasVisibleArea?: boolean;
18
+ isInteractive?: boolean;
19
+ isOutsideViewport?: boolean;
20
+ isProbeHit?: boolean;
21
+ isKept?: boolean;
22
+ srcAttrs?: Record<string, string>;
23
+ labelAttrs?: Record<string, string>;
24
+ valueAttrs?: Record<string, string>;
25
+ }
26
+ export interface VxTreeOptions {
27
+ startRef?: number;
28
+ skipImages?: boolean;
29
+ viewportOnly?: boolean;
30
+ probeViewport?: boolean;
31
+ opaqueOverlays?: boolean;
32
+ unnestDivs?: boolean;
33
+ }
34
+ export type VxTransform = (vxNode: VxNode) => VxNode;
35
+ export declare class VxTreeParser {
36
+ options: VxTreeOptions;
37
+ private counter;
38
+ private viewport;
39
+ private refRange;
40
+ private vxNodes;
41
+ private probeElements;
42
+ private domRefMap;
43
+ constructor(options?: VxTreeOptions);
44
+ private parseDocument;
45
+ getTree(): VxTreeView;
46
+ getNodes(): VxNode[];
47
+ getRefRange(): [number, number];
48
+ getDomMap(): Map<number, Node>;
49
+ private parseNode;
50
+ private parseElement;
51
+ private makeNode;
52
+ private pruneRecursive;
53
+ private collapseSingleChild;
54
+ private collapsePreferParent;
55
+ private collapseMerge;
56
+ private shouldOmit;
57
+ private isProbeHit;
58
+ private collectLabelAttrs;
59
+ private collectValueAttrs;
60
+ private collectSrcAttrs;
61
+ }
@@ -0,0 +1,253 @@
1
+ import { Counter } from './counter.js';
2
+ import { fixZIndex, getViewportSize, hasVisibleArea, isInteractive, isRandomIdentifier, isRectInViewport, makeOverlaysOpaque, normalizeText, truncateAttrValue } from './dom.js';
3
+ import { isHidden } from './dom.js';
4
+ import { probeViewport } from './probe.js';
5
+ import { VxTreeView } from './tree.js';
6
+ import { isObjectEmpty } from './util.js';
7
+ export const VX_NODE_SYMBOL = Symbol('vx:node');
8
+ export const VX_IGNORE_SYMBOL = Symbol('vx:ignore');
9
+ export const VX_IGNORE_TAGS = ['svg', 'script', 'noscript', 'style', 'link', 'meta'];
10
+ export const VX_LABEL_ATTRS = ['title', 'alt', 'placeholder', 'aria-label'];
11
+ export const VX_VALUE_ATTRS = ['value', 'checked', 'selected', 'disabled', 'readonly'];
12
+ export const VX_SRC_ATTRS = ['src', 'href'];
13
+ export const VX_TAG_PREFERENCE = [
14
+ 'a', 'iframe', 'input', 'button', 'select', 'textarea', 'img', 'label',
15
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
16
+ 'ul', 'ol', 'li', 'dl', 'dt', 'dd',
17
+ 'section', 'article', 'main', 'header', 'footer', 'nav', 'aside',
18
+ 'form', 'input', 'textarea', 'select', 'option', 'fieldset', 'legend',
19
+ 'p', 'pre', 'code', 'blockquote', 'figure', 'figcaption',
20
+ 'table', 'thead', 'tbody', 'tr', 'td', 'th',
21
+ 'strong', 'em', 'sub', 'sup',
22
+ ];
23
+ const VX_KEEP_SELECTOR = 'a, button, input, textarea, select, label, iframe, option, optgroup';
24
+ export class VxTreeParser {
25
+ constructor(options = {}) {
26
+ this.options = options;
27
+ this.probeElements = [];
28
+ this.domRefMap = new Map();
29
+ const { startRef = 0 } = options;
30
+ this.viewport = getViewportSize();
31
+ this.counter = new Counter(startRef);
32
+ if (options.probeViewport) {
33
+ this.probeElements = probeViewport(8);
34
+ }
35
+ const vxRoot = this.parseDocument();
36
+ this.refRange = [startRef, this.counter.current()];
37
+ this.vxNodes = this.pruneRecursive(vxRoot);
38
+ }
39
+ parseDocument() {
40
+ return this.parseNode(document.documentElement, null);
41
+ }
42
+ getTree() {
43
+ return new VxTreeView(this.vxNodes, this.refRange);
44
+ }
45
+ getNodes() {
46
+ return this.vxNodes;
47
+ }
48
+ getRefRange() {
49
+ return this.refRange;
50
+ }
51
+ getDomMap() {
52
+ return this.domRefMap;
53
+ }
54
+ parseNode(node, parent) {
55
+ if (!node || node[VX_IGNORE_SYMBOL]) {
56
+ return null;
57
+ }
58
+ if (node instanceof Text) {
59
+ // Single instances of Text nodes can only occur in "text"
60
+ // (e.g. when regular text is interspersed with links and other elements)
61
+ const textContent = normalizeText(node.textContent ?? '');
62
+ if (textContent) {
63
+ return this.makeNode(node, {
64
+ textContent,
65
+ hasVisibleArea: parent?.hasVisibleArea,
66
+ isOutsideViewport: parent?.isOutsideViewport,
67
+ isProbeHit: parent?.isProbeHit,
68
+ });
69
+ }
70
+ return null;
71
+ }
72
+ if (node instanceof Element) {
73
+ if (this.options.opaqueOverlays) {
74
+ makeOverlaysOpaque(node);
75
+ }
76
+ fixZIndex(node);
77
+ return this.parseElement(node);
78
+ }
79
+ return null;
80
+ }
81
+ parseElement(el) {
82
+ if (VX_IGNORE_TAGS.includes(el.tagName.toLowerCase())) {
83
+ return null;
84
+ }
85
+ if (isHidden(el)) {
86
+ return null;
87
+ }
88
+ const rect = el.getBoundingClientRect();
89
+ const id = el.getAttribute('id') ?? '';
90
+ const vxNode = this.makeNode(el, {
91
+ tagName: el.tagName.toLowerCase(),
92
+ id: isRandomIdentifier(id) ? undefined : id,
93
+ classList: Array.from(el.classList)
94
+ .filter(cls => !isRandomIdentifier(cls))
95
+ .slice(0, 4),
96
+ labelAttrs: this.collectLabelAttrs(el),
97
+ valueAttrs: this.collectValueAttrs(el),
98
+ srcAttrs: this.collectSrcAttrs(el),
99
+ hasVisibleArea: hasVisibleArea(el),
100
+ isInteractive: isInteractive(el),
101
+ isOutsideViewport: !isRectInViewport(rect, this.viewport),
102
+ isProbeHit: this.isProbeHit(el),
103
+ isKept: el.matches(VX_KEEP_SELECTOR),
104
+ });
105
+ const children = [...el.childNodes]
106
+ .map(child => this.parseNode(child, vxNode))
107
+ .filter(_ => _ != null);
108
+ // Special case: if the only child is a text node, return a single node with the text content
109
+ if (children.length === 1 && !children[0]?.tagName) {
110
+ vxNode.textContent = normalizeText(children[0].textContent ?? '');
111
+ }
112
+ else {
113
+ vxNode.children = children;
114
+ }
115
+ return vxNode;
116
+ }
117
+ makeNode(node, spec) {
118
+ const ref = this.counter.next();
119
+ const vxNode = {
120
+ ...spec,
121
+ ref,
122
+ };
123
+ this.domRefMap.set(ref, node);
124
+ node[VX_NODE_SYMBOL] = vxNode;
125
+ return vxNode;
126
+ }
127
+ pruneRecursive(vxNode) {
128
+ if (!vxNode) {
129
+ return [];
130
+ }
131
+ const children = vxNode.children ?? [];
132
+ // Recursively prune children
133
+ const newChildren = children.flatMap(child => this.pruneRecursive(child));
134
+ // If the node itself is omitted, replace it with children
135
+ if (this.shouldOmit(vxNode)) {
136
+ return newChildren;
137
+ }
138
+ vxNode.children = newChildren;
139
+ // Collapse single-child elements
140
+ if (newChildren.length === 1) {
141
+ const child = newChildren[0];
142
+ return this.collapseSingleChild(vxNode, child);
143
+ }
144
+ return [vxNode];
145
+ }
146
+ collapseSingleChild(parent, child) {
147
+ const preferParent = this.collapsePreferParent(parent, child);
148
+ const merged = preferParent ?
149
+ this.collapseMerge(parent, child) :
150
+ this.collapseMerge(child, parent);
151
+ return this.pruneRecursive({
152
+ ...merged,
153
+ children: child.children,
154
+ textContent: child.textContent,
155
+ });
156
+ }
157
+ collapsePreferParent(parent, child) {
158
+ const parentRank = VX_TAG_PREFERENCE.indexOf(parent.tagName ?? '');
159
+ const childRank = VX_TAG_PREFERENCE.indexOf(child.tagName ?? '');
160
+ if (parentRank === -1 && childRank === -1) {
161
+ const parentAttrCount = Object.keys({
162
+ ...parent.labelAttrs,
163
+ ...parent.valueAttrs,
164
+ ...parent.srcAttrs,
165
+ }).length;
166
+ const childAttrCount = Object.keys({
167
+ ...child.labelAttrs,
168
+ ...child.valueAttrs,
169
+ ...child.srcAttrs,
170
+ }).length;
171
+ return parentAttrCount > childAttrCount;
172
+ }
173
+ return parentRank !== -1 && (childRank === -1 || parentRank < childRank);
174
+ }
175
+ collapseMerge(a, b) {
176
+ return {
177
+ ...b,
178
+ ...a,
179
+ labelAttrs: { ...b.labelAttrs, ...a.labelAttrs },
180
+ valueAttrs: { ...b.valueAttrs, ...a.valueAttrs },
181
+ srcAttrs: { ...b.srcAttrs, ...a.srcAttrs },
182
+ };
183
+ }
184
+ shouldOmit(vxNode) {
185
+ const tagName = vxNode.tagName ?? '';
186
+ if (!vxNode.hasVisibleArea) {
187
+ return true;
188
+ }
189
+ if (this.options.viewportOnly && vxNode.isOutsideViewport) {
190
+ return true;
191
+ }
192
+ if (this.options.probeViewport && !vxNode.isProbeHit) {
193
+ return true;
194
+ }
195
+ if (this.options.skipImages && vxNode.tagName === 'img') {
196
+ return true;
197
+ }
198
+ const hasText = !!vxNode.textContent;
199
+ const hasChildren = (vxNode.children ?? []).length > 0;
200
+ const hasAttrs = [vxNode.labelAttrs, vxNode.valueAttrs, vxNode.srcAttrs]
201
+ .some(attrs => !isObjectEmpty(attrs ?? {}));
202
+ if (this.options.unnestDivs) {
203
+ const isDivOrSpan = ['div', 'span'].includes(tagName);
204
+ if (isDivOrSpan && hasChildren && !hasAttrs) {
205
+ return true;
206
+ }
207
+ }
208
+ if (vxNode.isKept) {
209
+ return false;
210
+ }
211
+ return !hasText && !hasAttrs && !hasChildren;
212
+ }
213
+ isProbeHit(el) {
214
+ // TODO this is a bit brute-forcish, consider improving
215
+ for (const probeEl of this.probeElements) {
216
+ if (el.contains(probeEl) || probeEl.contains(el)) {
217
+ return true;
218
+ }
219
+ }
220
+ return false;
221
+ }
222
+ collectLabelAttrs(el) {
223
+ const attrs = {};
224
+ for (const attr of VX_LABEL_ATTRS) {
225
+ const value = truncateAttrValue(el.getAttribute(attr) ?? '');
226
+ if (value) {
227
+ attrs[attr] = value;
228
+ }
229
+ }
230
+ return attrs;
231
+ }
232
+ collectValueAttrs(el) {
233
+ const attrs = {};
234
+ for (const attr of VX_VALUE_ATTRS) {
235
+ const value = el[attr] ?? '';
236
+ if (value) {
237
+ attrs[attr] = String(value);
238
+ }
239
+ }
240
+ return attrs;
241
+ }
242
+ collectSrcAttrs(el) {
243
+ const attrs = {};
244
+ for (const attr of VX_SRC_ATTRS) {
245
+ const value = truncateAttrValue(el[attr] ?? '');
246
+ if (value) {
247
+ attrs[attr] = value;
248
+ }
249
+ }
250
+ return attrs;
251
+ }
252
+ }
253
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/page/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Scans the viewport by picking elements in regular intervals.
3
+ */
4
+ export declare function probeViewport(gridSize?: number): Element[];
5
+ export declare function checkOccluded(element: Element, gridSize?: number): boolean;
@@ -0,0 +1,36 @@
1
+ import { getViewportSize } from './dom.js';
2
+ /**
3
+ * Scans the viewport by picking elements in regular intervals.
4
+ */
5
+ export function probeViewport(gridSize = 32) {
6
+ const { width, height } = getViewportSize();
7
+ const elements = [];
8
+ for (let y = (gridSize / 2); y < height; y += gridSize) {
9
+ for (let x = (gridSize / 2); x < width; x += gridSize) {
10
+ const element = document.elementFromPoint(x, y);
11
+ if (element && !elements.includes(element)) {
12
+ elements.push(element);
13
+ }
14
+ }
15
+ }
16
+ return elements;
17
+ }
18
+ export function checkOccluded(element, gridSize = 8) {
19
+ let total = 0;
20
+ let hits = 0;
21
+ const { top, left, width, height } = element.getBoundingClientRect();
22
+ for (let x = left + (gridSize / 2); x < left + width; x += gridSize) {
23
+ for (let y = top + (gridSize / 2); y < top + height; y += gridSize) {
24
+ total++;
25
+ const el = document.elementFromPoint(x, y);
26
+ if (!el) {
27
+ continue;
28
+ }
29
+ if (element.contains(el) || el.contains(element)) {
30
+ hits++;
31
+ }
32
+ }
33
+ }
34
+ return (hits / total) < 0.5;
35
+ }
36
+ //# sourceMappingURL=probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe.js","sourceRoot":"","sources":["../../src/page/probe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,QAAQ,GAAG,EAAE;IACvC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,eAAe,EAAE,CAAC;IAC5C,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,QAAQ,EAAE,CAAC;QACrD,KAAK,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,QAAQ,EAAE,CAAC;YACpD,MAAM,OAAO,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAChD,IAAI,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC;QACL,CAAC;IACL,CAAC;IACD,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAgB,EAAE,QAAQ,GAAG,CAAC;IACxD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;IACrE,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC,IAAI,QAAQ,EAAE,CAAC;QAClE,KAAK,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,MAAM,EAAE,CAAC,IAAI,QAAQ,EAAE,CAAC;YACjE,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC3C,IAAI,CAAC,EAAE,EAAE,CAAC;gBACN,SAAS;YACb,CAAC;YACD,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC/C,IAAI,EAAE,CAAC;YACX,CAAC;QACL,CAAC;IACL,CAAC;IACD,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC;AAChC,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { VxNode } from './parser.js';
2
+ export interface VxRenderOptions {
3
+ renderStyle?: 'html' | 'text';
4
+ renderTagNames?: boolean;
5
+ renderIds?: boolean;
6
+ renderClassNames?: boolean;
7
+ renderRefs?: boolean | 'all';
8
+ renderLabelAttrs?: boolean;
9
+ renderValueAttrs?: boolean;
10
+ renderSrcAttrs?: boolean;
11
+ whitelistRefs?: number[];
12
+ skipNonInteractive?: boolean;
13
+ }
14
+ export declare function renderVxNode(scope: VxNode, options?: VxRenderOptions): string;
@@ -0,0 +1,89 @@
1
+ import { truncateAttrValue } from './dom.js';
2
+ import { traverseVxNode } from './traverse.js';
3
+ import { isContainerNode } from './util.js';
4
+ export function renderVxNode(scope, options = {}) {
5
+ const buffer = [];
6
+ const whitelistRefs = options.whitelistRefs ?? [];
7
+ for (const { vxNode, depth } of traverseVxNode(scope)) {
8
+ if (whitelistRefs.length > 0) {
9
+ if (!whitelistRefs.includes(vxNode.ref)) {
10
+ continue;
11
+ }
12
+ }
13
+ if (options.skipNonInteractive && !vxNode.isInteractive) {
14
+ continue;
15
+ }
16
+ const indent = options.skipNonInteractive ? '' : ' '.repeat(depth);
17
+ buffer.push(renderIndentedLine(indent, vxNode, options));
18
+ }
19
+ return buffer.join('\n');
20
+ }
21
+ function renderIndentedLine(indent, vxNode, options = {}) {
22
+ if (!vxNode.tagName) {
23
+ return [indent, vxNode.textContent].filter(Boolean).join('');
24
+ }
25
+ const tagLine = renderTagLine(vxNode, options);
26
+ const htmlStyle = options.renderStyle === 'html';
27
+ return [
28
+ indent,
29
+ tagLine,
30
+ htmlStyle ? '' : ' ',
31
+ vxNode.textContent ?? '',
32
+ ].join('');
33
+ }
34
+ function renderTagLine(vxNode, options) {
35
+ const htmlStyle = options.renderStyle === 'html';
36
+ const components = [];
37
+ if (options.renderTagNames && vxNode.tagName) {
38
+ components.push(vxNode.tagName);
39
+ }
40
+ if (options.renderIds && vxNode.id) {
41
+ if (htmlStyle) {
42
+ components.push(`id="${vxNode.id}"`);
43
+ }
44
+ else {
45
+ components.push(`#${vxNode.id}`);
46
+ }
47
+ }
48
+ if (options.renderClassNames && vxNode.classList?.length) {
49
+ if (htmlStyle) {
50
+ components.push(`class="${vxNode.classList.join(' ')}"`);
51
+ }
52
+ else {
53
+ components.push('.' + vxNode.classList.join('.'));
54
+ }
55
+ }
56
+ if (options.renderRefs) {
57
+ const isRenderRef = [
58
+ options.renderRefs === 'all',
59
+ options.renderRefs === true && !isContainerNode(vxNode),
60
+ ].some(Boolean);
61
+ if (isRenderRef) {
62
+ components.push(`[@${vxNode.ref}]`);
63
+ }
64
+ }
65
+ const attrs = [];
66
+ if (options.renderLabelAttrs) {
67
+ for (const [attr, value] of Object.entries(vxNode.labelAttrs ?? {})) {
68
+ attrs.push(`${attr}="${truncateAttrValue(value)}"`);
69
+ }
70
+ }
71
+ if (options.renderValueAttrs) {
72
+ for (const [attr, value] of Object.entries(vxNode.valueAttrs ?? {})) {
73
+ attrs.push(`${attr}="${truncateAttrValue(value)}"`);
74
+ }
75
+ }
76
+ if (options.renderSrcAttrs) {
77
+ for (const [attr, value] of Object.entries(vxNode.srcAttrs ?? {})) {
78
+ attrs.push(`${attr}="${truncateAttrValue(value)}"`);
79
+ }
80
+ }
81
+ if (htmlStyle) {
82
+ components.push(...attrs);
83
+ }
84
+ else {
85
+ components.push(...attrs.map(attr => `[${attr}]`));
86
+ }
87
+ return htmlStyle ? `<${components.join(' ')}>` : components.join('');
88
+ }
89
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/page/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAe5C,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,UAA2B,EAAE;IACrE,MAAM,MAAM,GAAG,EAAE,CAAC;IAClB,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC;IAClD,KAAK,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtC,SAAS;YACb,CAAC;QACL,CAAC;QACD,IAAI,OAAO,CAAC,kBAAkB,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;YACtD,SAAS;QACb,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtE,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAc,EAAE,MAAc,EAAE,UAA2B,EAAE;IACrF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAClB,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,KAAK,MAAM,CAAC;IACjD,OAAO;QACH,MAAM;QACN,OAAO;QACP,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG;QACpB,MAAM,CAAC,WAAW,IAAI,EAAE;KAC3B,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,MAAc,EAAE,OAAwB;IAC3D,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,KAAK,MAAM,CAAC;IACjD,MAAM,UAAU,GAAyB,EAAE,CAAC;IAC5C,IAAI,OAAO,CAAC,cAAc,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QAC3C,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;QACjC,IAAI,SAAS,EAAE,CAAC;YACZ,UAAU,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACJ,UAAU,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;QACrC,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,gBAAgB,IAAI,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;QACvD,IAAI,SAAS,EAAE,CAAC;YACZ,UAAU,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7D,CAAC;aAAM,CAAC;YACJ,UAAU,CAAC,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACtD,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;QACrB,MAAM,WAAW,GAAG;YAChB,OAAO,CAAC,UAAU,KAAK,KAAK;YAC5B,OAAO,CAAC,UAAU,KAAK,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;SAC1D,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChB,IAAI,WAAW,EAAE,CAAC;YACd,UAAU,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC;QACxC,CAAC;IACL,CAAC;IACD,MAAM,KAAK,GAAG,EAAE,CAAC;IACjB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,CAAC;YAClE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxD,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,CAAC;YAClE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxD,CAAC;IACL,CAAC;IACD,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;YAChE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxD,CAAC;IACL,CAAC;IACD,IAAI,SAAS,EAAE,CAAC;QACZ,UAAU,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;IAC9B,CAAC;SAAM,CAAC;QACJ,UAAU,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,SAAS,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACzE,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { VxTreeOptions } from './parser.js';
2
+ import { VxTreeView } from './tree.js';
3
+ export declare const VX_DOM_SYMBOL: unique symbol;
4
+ export declare const VX_TREE_SYMBOL: unique symbol;
5
+ export declare function captureSnapshot(options?: VxTreeOptions): VxTreeView;
6
+ export declare function getSnapshot(): VxTreeView;
7
+ export declare function resolveDomNode(ref: number): Node | null;
@@ -0,0 +1,26 @@
1
+ import { VxTreeParser } from './parser.js';
2
+ export const VX_DOM_SYMBOL = Symbol('vx:dom');
3
+ export const VX_TREE_SYMBOL = Symbol('vx:tree');
4
+ export function captureSnapshot(options = {}) {
5
+ const parser = new VxTreeParser(options);
6
+ const domMap = parser.getDomMap();
7
+ const vxTree = parser.getTree();
8
+ globalThis[VX_DOM_SYMBOL] = domMap;
9
+ globalThis[VX_TREE_SYMBOL] = vxTree;
10
+ return vxTree;
11
+ }
12
+ export function getSnapshot() {
13
+ const vxTree = globalThis[VX_TREE_SYMBOL];
14
+ if (!vxTree) {
15
+ throw new Error('[VX] Snapshot not found');
16
+ }
17
+ return vxTree;
18
+ }
19
+ export function resolveDomNode(ref) {
20
+ const domMap = globalThis[VX_DOM_SYMBOL];
21
+ if (!domMap) {
22
+ return null;
23
+ }
24
+ return domMap.get(ref) ?? null;
25
+ }
26
+ //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/page/snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,YAAY,EAAE,MAAM,aAAa,CAAC;AAG1D,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC9C,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAEhD,MAAM,UAAU,eAAe,CAAC,UAAyB,EAAE;IACvD,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;IAC/B,UAAkB,CAAC,aAAa,CAAC,GAAG,MAAM,CAAC;IAC3C,UAAkB,CAAC,cAAc,CAAC,GAAG,MAAM,CAAC;IAC7C,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,WAAW;IACvB,MAAM,MAAM,GAAI,UAAkB,CAAC,cAAc,CAAe,CAAC;IACjE,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAW;IACtC,MAAM,MAAM,GAAI,UAAkB,CAAC,aAAa,CAAsB,CAAC;IACvE,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;AACnC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { VxNode } from './parser.js';
2
+ export declare function traverseVxNode(vxNode: VxNode, depth?: number): IterableIterator<{
3
+ vxNode: VxNode;
4
+ depth: number;
5
+ }>;
@@ -0,0 +1,7 @@
1
+ export function* traverseVxNode(vxNode, depth = 0) {
2
+ yield { vxNode, depth };
3
+ for (const child of vxNode.children ?? []) {
4
+ yield* traverseVxNode(child, depth + 1);
5
+ }
6
+ }
7
+ //# sourceMappingURL=traverse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traverse.js","sourceRoot":"","sources":["../../src/page/traverse.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,CAAC,CAAC,cAAc,CAAC,MAAc,EAAE,KAAK,GAAG,CAAC;IACrD,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACxB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACxC,KAAK,CAAC,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;IAC5C,CAAC;AACL,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { VxNode } from './parser.js';
2
+ import { VxRenderOptions } from './render.js';
3
+ export interface VxHighlightOptions {
4
+ highlight?: boolean;
5
+ clearOverlay?: boolean;
6
+ includeAll?: boolean;
7
+ filterRefs?: number[];
8
+ }
9
+ export declare class VxTreeView {
10
+ readonly nodes: VxNode[];
11
+ readonly refRange: [number, number];
12
+ private refMap;
13
+ constructor(nodes: VxNode[], refRange: [number, number]);
14
+ traverse(): IterableIterator<{
15
+ vxNode: VxNode;
16
+ depth: number;
17
+ }>;
18
+ private buildRefMap;
19
+ findNode(ref: number): VxNode | null;
20
+ render(options?: VxRenderOptions): string;
21
+ highlight(options?: VxHighlightOptions): number[];
22
+ highlightRefs(refs: number[]): void;
23
+ collectRefs(leafOnly?: boolean, filterRefs?: number[]): number[];
24
+ }
@@ -0,0 +1,68 @@
1
+ import { clearOverlay, highlightEl } from './overlay.js';
2
+ import { renderVxNode } from './render.js';
3
+ import { resolveDomNode } from './snapshot.js';
4
+ import { traverseVxNode } from './traverse.js';
5
+ import { isContainerNode } from './util.js';
6
+ export class VxTreeView {
7
+ constructor(nodes, refRange) {
8
+ this.nodes = nodes;
9
+ this.refRange = refRange;
10
+ this.refMap = new Map();
11
+ this.buildRefMap(nodes);
12
+ }
13
+ *traverse() {
14
+ for (const vxNode of this.nodes) {
15
+ yield* traverseVxNode(vxNode, 0);
16
+ }
17
+ }
18
+ buildRefMap(nodes) {
19
+ for (const node of nodes) {
20
+ this.refMap.set(node.ref, node);
21
+ this.buildRefMap(node.children ?? []);
22
+ }
23
+ }
24
+ findNode(ref) {
25
+ return this.refMap.get(ref) ?? null;
26
+ }
27
+ render(options = {}) {
28
+ return this.nodes.map(node => {
29
+ return renderVxNode(node, options);
30
+ }).join('\n');
31
+ }
32
+ highlight(options = {}) {
33
+ if (options.clearOverlay) {
34
+ clearOverlay();
35
+ }
36
+ const leafOnly = !options.includeAll;
37
+ const refs = this.collectRefs(leafOnly, options.filterRefs);
38
+ for (const ref of refs) {
39
+ const el = resolveDomNode(ref);
40
+ if (el instanceof Element) {
41
+ highlightEl(el, ref);
42
+ }
43
+ }
44
+ return refs;
45
+ }
46
+ highlightRefs(refs) {
47
+ for (const ref of refs) {
48
+ const el = resolveDomNode(ref);
49
+ if (el instanceof Element) {
50
+ highlightEl(el, ref);
51
+ }
52
+ }
53
+ }
54
+ collectRefs(leafOnly = true, filterRefs) {
55
+ const refs = [];
56
+ for (const { vxNode } of this.traverse()) {
57
+ if (leafOnly && isContainerNode(vxNode)) {
58
+ continue;
59
+ }
60
+ if (filterRefs && !filterRefs.includes(vxNode.ref)) {
61
+ continue;
62
+ }
63
+ refs.push(vxNode.ref);
64
+ }
65
+ return refs;
66
+ }
67
+ }
68
+ //# sourceMappingURL=tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.js","sourceRoot":"","sources":["../../src/page/tree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { VxNode } from './parser.js';
2
+ export declare function isContainerNode(vxNode: VxNode): boolean;
3
+ export declare function isObjectEmpty(obj: Record<string, any>): boolean;
@@ -0,0 +1,9 @@
1
+ export function isContainerNode(vxNode) {
2
+ const children = vxNode.children ?? [];
3
+ const hasTextChildren = children.some(child => !child.tagName);
4
+ return children.length > 0 && !hasTextChildren;
5
+ }
6
+ export function isObjectEmpty(obj) {
7
+ return !Object.values(obj).some(value => !!value);
8
+ }
9
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/page/util.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,eAAe,CAAC,MAAc;IAC1C,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;IACvC,MAAM,eAAe,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/D,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAwB;IAClD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACtD,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ubio/webvision",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "main": "out/main/index.js",
5
5
  "type": "module",
6
6
  "exports": {