@page-agent/page-controller 1.6.3 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/PageController.d.ts +12 -10
- package/dist/lib/{SimulatorMask-BHnQ6LmL.js → SimulatorMask-CU7szDjy.js} +24 -5
- package/dist/lib/SimulatorMask-CU7szDjy.js.map +1 -0
- package/dist/lib/page-controller.js +146 -47
- package/dist/lib/page-controller.js.map +1 -1
- package/package.json +1 -1
- package/dist/lib/SimulatorMask-BHnQ6LmL.js.map +0 -1
|
@@ -20,7 +20,10 @@ export declare interface BrowserState {
|
|
|
20
20
|
declare function cleanUpHighlights(): void;
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Simulate a click
|
|
23
|
+
* Simulate a full click following W3C Pointer Events + UI Events spec order:
|
|
24
|
+
* pointerover/enter → mouseover/enter → pointerdown → mousedown → [focus] →
|
|
25
|
+
* pointerup → mouseup → click
|
|
26
|
+
*
|
|
24
27
|
* @private Internal method, subject to change at any time.
|
|
25
28
|
*/
|
|
26
29
|
export declare function clickElement(element: HTMLElement): Promise<void>;
|
|
@@ -45,6 +48,11 @@ declare interface DomConfig {
|
|
|
45
48
|
includeAttributes?: string[];
|
|
46
49
|
highlightOpacity?: number;
|
|
47
50
|
highlightLabelOpacity?: number;
|
|
51
|
+
/**
|
|
52
|
+
* Preserve semantic landmark tags in dehydrated output even if not interactive
|
|
53
|
+
* @note maybe confusing for LLM combining with page scrolling, use with caution
|
|
54
|
+
**/
|
|
55
|
+
keepSemanticTags?: boolean;
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
declare type DomNode = TextDomNode | ElementDomNode | InteractiveElementDomNode;
|
|
@@ -92,7 +100,7 @@ declare interface FlatDomTree {
|
|
|
92
100
|
*
|
|
93
101
|
* @todo 数据脱敏过滤器
|
|
94
102
|
*/
|
|
95
|
-
declare function flatTreeToString(flatTree: FlatDomTree, includeAttributes?: string[]): string;
|
|
103
|
+
declare function flatTreeToString(flatTree: FlatDomTree, includeAttributes?: string[], keepSemanticTags?: boolean): string;
|
|
96
104
|
|
|
97
105
|
declare const getAllTextTillNextClickableElement: (node: TreeNode, maxDepth?: number) => string;
|
|
98
106
|
|
|
@@ -253,20 +261,14 @@ export declare interface PageControllerConfig extends dom.DomConfig {
|
|
|
253
261
|
|
|
254
262
|
declare function resolveViewportExpansion(viewportExpansion?: number): number;
|
|
255
263
|
|
|
256
|
-
|
|
257
|
-
* @private Internal method, subject to change at any time.
|
|
258
|
-
*/
|
|
259
|
-
export declare function scrollHorizontally(right: boolean, scroll_amount: number, element?: HTMLElement | null): Promise<string>;
|
|
264
|
+
export declare function scrollHorizontally(scroll_amount: number, element?: HTMLElement | null): Promise<string>;
|
|
260
265
|
|
|
261
266
|
/**
|
|
262
267
|
* @private Internal method, subject to change at any time.
|
|
263
268
|
*/
|
|
264
269
|
export declare function scrollIntoViewIfNeeded(element: Element): Promise<void>;
|
|
265
270
|
|
|
266
|
-
|
|
267
|
-
* @private Internal method, subject to change at any time.
|
|
268
|
-
*/
|
|
269
|
-
export declare function scrollVertically(down: boolean, scroll_amount: number, element?: HTMLElement | null): Promise<string>;
|
|
271
|
+
export declare function scrollVertically(scroll_amount: number, element?: HTMLElement | null): Promise<string>;
|
|
270
272
|
|
|
271
273
|
/**
|
|
272
274
|
* @todo browser-use version is very complex and supports menu tags, need to follow up
|
|
@@ -192,8 +192,9 @@ const cursorStyles = {
|
|
|
192
192
|
cursorRipple,
|
|
193
193
|
clicking
|
|
194
194
|
};
|
|
195
|
-
const _SimulatorMask = class _SimulatorMask {
|
|
195
|
+
const _SimulatorMask = class _SimulatorMask extends EventTarget {
|
|
196
196
|
constructor() {
|
|
197
|
+
super();
|
|
197
198
|
__privateAdd(this, _SimulatorMask_instances);
|
|
198
199
|
__publicField(this, "shown", false);
|
|
199
200
|
__publicField(this, "wrapper", document.createElement("div"));
|
|
@@ -249,12 +250,28 @@ const _SimulatorMask = class _SimulatorMask {
|
|
|
249
250
|
__privateMethod(this, _SimulatorMask_instances, createCursor_fn).call(this);
|
|
250
251
|
document.body.appendChild(this.wrapper);
|
|
251
252
|
__privateMethod(this, _SimulatorMask_instances, moveCursorToTarget_fn).call(this);
|
|
252
|
-
|
|
253
|
+
const movePointerToListener = /* @__PURE__ */ __name((event) => {
|
|
253
254
|
const { x, y } = event.detail;
|
|
254
255
|
this.setCursorPosition(x, y);
|
|
255
|
-
});
|
|
256
|
-
|
|
256
|
+
}, "movePointerToListener");
|
|
257
|
+
const clickPointerListener = /* @__PURE__ */ __name(() => {
|
|
257
258
|
this.triggerClickAnimation();
|
|
259
|
+
}, "clickPointerListener");
|
|
260
|
+
const enablePassThroughListener = /* @__PURE__ */ __name(() => {
|
|
261
|
+
this.wrapper.style.pointerEvents = "none";
|
|
262
|
+
}, "enablePassThroughListener");
|
|
263
|
+
const disablePassThroughListener = /* @__PURE__ */ __name(() => {
|
|
264
|
+
this.wrapper.style.pointerEvents = "auto";
|
|
265
|
+
}, "disablePassThroughListener");
|
|
266
|
+
window.addEventListener("PageAgent::MovePointerTo", movePointerToListener);
|
|
267
|
+
window.addEventListener("PageAgent::ClickPointer", clickPointerListener);
|
|
268
|
+
window.addEventListener("PageAgent::EnablePassThrough", enablePassThroughListener);
|
|
269
|
+
window.addEventListener("PageAgent::DisablePassThrough", disablePassThroughListener);
|
|
270
|
+
this.addEventListener("dispose", () => {
|
|
271
|
+
window.removeEventListener("PageAgent::MovePointerTo", movePointerToListener);
|
|
272
|
+
window.removeEventListener("PageAgent::ClickPointer", clickPointerListener);
|
|
273
|
+
window.removeEventListener("PageAgent::EnablePassThrough", enablePassThroughListener);
|
|
274
|
+
window.removeEventListener("PageAgent::DisablePassThrough", disablePassThroughListener);
|
|
258
275
|
});
|
|
259
276
|
}
|
|
260
277
|
setCursorPosition(x, y) {
|
|
@@ -290,8 +307,10 @@ const _SimulatorMask = class _SimulatorMask {
|
|
|
290
307
|
}, 800);
|
|
291
308
|
}
|
|
292
309
|
dispose() {
|
|
310
|
+
console.log("dispose SimulatorMask");
|
|
293
311
|
this.motion?.dispose();
|
|
294
312
|
this.wrapper.remove();
|
|
313
|
+
this.dispatchEvent(new Event("dispose"));
|
|
295
314
|
}
|
|
296
315
|
};
|
|
297
316
|
_cursor = new WeakMap();
|
|
@@ -341,4 +360,4 @@ let SimulatorMask = _SimulatorMask;
|
|
|
341
360
|
export {
|
|
342
361
|
SimulatorMask
|
|
343
362
|
};
|
|
344
|
-
//# sourceMappingURL=SimulatorMask-
|
|
363
|
+
//# sourceMappingURL=SimulatorMask-CU7szDjy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SimulatorMask-CU7szDjy.js","sources":["../../src/mask/checkDarkMode.ts","../../src/mask/SimulatorMask.ts"],"sourcesContent":["/**\n * Checks for common dark mode CSS classes on the html or body elements.\n * @returns {boolean} - True if a common dark mode class is found.\n */\nfunction hasDarkModeClass() {\n\tconst DEFAULT_DARK_MODE_CLASSES = ['dark', 'dark-mode', 'theme-dark', 'night', 'night-mode']\n\n\tconst htmlElement = document.documentElement\n\tconst bodyElement = document.body || document.documentElement // can be null in some cases\n\n\t// Check class names on <html> and <body>\n\tfor (const className of DEFAULT_DARK_MODE_CLASSES) {\n\t\tif (htmlElement.classList.contains(className) || bodyElement?.classList.contains(className)) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\t// Some sites use data attributes\n\tconst darkThemeAttribute = htmlElement.getAttribute('data-theme')\n\tif (darkThemeAttribute?.toLowerCase().includes('dark')) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n/**\n * Parses an RGB or RGBA color string and returns an object with r, g, b properties.\n * @param {string} colorString - e.g., \"rgb(34, 34, 34)\" or \"rgba(0, 0, 0, 0.5)\"\n * @returns {{r: number, g: number, b: number}|null}\n */\nfunction parseRgbColor(colorString: string) {\n\tconst rgbMatch = /rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/.exec(colorString)\n\tif (!rgbMatch) {\n\t\treturn null // Not a valid rgb/rgba string\n\t}\n\treturn {\n\t\tr: parseInt(rgbMatch[1]),\n\t\tg: parseInt(rgbMatch[2]),\n\t\tb: parseInt(rgbMatch[3]),\n\t}\n}\n\n/**\n * Determines if a color is \"dark\" based on its calculated luminance.\n * @param {string} colorString - The CSS color string (e.g., \"rgb(50, 50, 50)\").\n * @param {number} threshold - A value between 0 and 255. Colors with luminance below this will be considered dark. Default is 128.\n * @returns {boolean} - True if the color is considered dark.\n */\nfunction isColorDark(colorString: string, threshold = 128) {\n\tif (!colorString || colorString === 'transparent' || colorString.startsWith('rgba(0, 0, 0, 0)')) {\n\t\treturn false // Transparent is not dark\n\t}\n\n\tconst rgb = parseRgbColor(colorString)\n\tif (!rgb) {\n\t\treturn false // Could not parse color\n\t}\n\n\t// Calculate perceived luminance using the standard formula\n\tconst luminance = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b\n\n\treturn luminance < threshold\n}\n\n/**\n * Checks the background color of the body element to determine if the page is dark.\n * @returns {boolean}\n */\nfunction isBackgroundDark() {\n\t// We check both <html> and <body> because some pages set the color on <html>\n\tconst htmlStyle = window.getComputedStyle(document.documentElement)\n\tconst bodyStyle = window.getComputedStyle(document.body || document.documentElement)\n\n\t// Get background colors\n\tconst htmlBgColor = htmlStyle.backgroundColor\n\tconst bodyBgColor = bodyStyle.backgroundColor\n\n\t// The body's background might be transparent, in which case we should\n\t// fall back to the html element's background.\n\tif (isColorDark(bodyBgColor)) {\n\t\treturn true\n\t} else if (bodyBgColor === 'transparent' || bodyBgColor.startsWith('rgba(0, 0, 0, 0)')) {\n\t\treturn isColorDark(htmlBgColor)\n\t}\n\n\treturn false\n}\n\n/**\n * A comprehensive function to determine if the page is currently in a dark theme.\n * It combines class checking and background color analysis.\n * @returns {boolean} - True if the page is likely dark.\n */\nexport function isPageDark() {\n\ttry {\n\t\t// Strategy 1: Check for common dark mode classes\n\t\tif (hasDarkModeClass()) {\n\t\t\treturn true\n\t\t}\n\n\t\t// Strategy 2: Analyze the computed background color\n\t\tif (isBackgroundDark()) {\n\t\t\treturn true\n\t\t}\n\n\t\t// @TODO add more checks here, e.g., analyzing text color,\n\t\t// or checking the background of major layout elements like <main> or #app.\n\n\t\treturn false\n\t} catch (error) {\n\t\tconsole.warn('Error determining if page is dark:', error)\n\t\treturn false\n\t}\n}\n","import { Motion } from 'ai-motion'\n\nimport { isPageDark } from './checkDarkMode'\n\nimport styles from './SimulatorMask.module.css'\nimport cursorStyles from './cursor.module.css'\n\nexport class SimulatorMask extends EventTarget {\n\tshown: boolean = false\n\twrapper = document.createElement('div')\n\tmotion: Motion | null = null\n\n\t#cursor = document.createElement('div')\n\n\t#currentCursorX = 0\n\t#currentCursorY = 0\n\n\t#targetCursorX = 0\n\t#targetCursorY = 0\n\n\tconstructor() {\n\t\tsuper()\n\n\t\tthis.wrapper.id = 'page-agent-runtime_simulator-mask'\n\t\tthis.wrapper.className = styles.wrapper\n\t\tthis.wrapper.setAttribute('data-browser-use-ignore', 'true')\n\t\tthis.wrapper.setAttribute('data-page-agent-ignore', 'true')\n\n\t\ttry {\n\t\t\tconst motion = new Motion({\n\t\t\t\tmode: isPageDark() ? 'dark' : 'light',\n\t\t\t\tstyles: { position: 'absolute', inset: '0' },\n\t\t\t})\n\t\t\tthis.motion = motion\n\t\t\tthis.wrapper.appendChild(motion.element)\n\t\t\tmotion.autoResize(this.wrapper)\n\t\t} catch (e) {\n\t\t\tconsole.warn('[SimulatorMask] Motion overlay unavailable:', e)\n\t\t}\n\n\t\t// Capture all mouse, keyboard, and wheel events\n\t\tthis.wrapper.addEventListener('click', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('mousedown', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('mouseup', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('mousemove', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('wheel', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('keydown', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\t\tthis.wrapper.addEventListener('keyup', (e) => {\n\t\t\te.stopPropagation()\n\t\t\te.preventDefault()\n\t\t})\n\n\t\t// Create AI cursor\n\t\tthis.#createCursor()\n\t\t// this.show()\n\n\t\tdocument.body.appendChild(this.wrapper)\n\n\t\tthis.#moveCursorToTarget()\n\n\t\t// global events\n\t\t// @note Mask should be isolated from the rest of the code.\n\t\t// Global events are easier to manage and cleanup.\n\n\t\tconst movePointerToListener = (event: Event) => {\n\t\t\tconst { x, y } = (event as CustomEvent).detail\n\t\t\tthis.setCursorPosition(x, y)\n\t\t}\n\t\tconst clickPointerListener = () => {\n\t\t\tthis.triggerClickAnimation()\n\t\t}\n\t\tconst enablePassThroughListener = () => {\n\t\t\tthis.wrapper.style.pointerEvents = 'none'\n\t\t}\n\t\tconst disablePassThroughListener = () => {\n\t\t\tthis.wrapper.style.pointerEvents = 'auto'\n\t\t}\n\n\t\twindow.addEventListener('PageAgent::MovePointerTo', movePointerToListener)\n\t\twindow.addEventListener('PageAgent::ClickPointer', clickPointerListener)\n\t\twindow.addEventListener('PageAgent::EnablePassThrough', enablePassThroughListener)\n\t\twindow.addEventListener('PageAgent::DisablePassThrough', disablePassThroughListener)\n\n\t\tthis.addEventListener('dispose', () => {\n\t\t\twindow.removeEventListener('PageAgent::MovePointerTo', movePointerToListener)\n\t\t\twindow.removeEventListener('PageAgent::ClickPointer', clickPointerListener)\n\t\t\twindow.removeEventListener('PageAgent::EnablePassThrough', enablePassThroughListener)\n\t\t\twindow.removeEventListener('PageAgent::DisablePassThrough', disablePassThroughListener)\n\t\t})\n\t}\n\n\t#createCursor() {\n\t\tthis.#cursor.className = cursorStyles.cursor\n\n\t\t// Create ripple effect container\n\t\tconst rippleContainer = document.createElement('div')\n\t\trippleContainer.className = cursorStyles.cursorRipple\n\t\tthis.#cursor.appendChild(rippleContainer)\n\n\t\t// Create filling layer\n\t\tconst fillingLayer = document.createElement('div')\n\t\tfillingLayer.className = cursorStyles.cursorFilling\n\t\tthis.#cursor.appendChild(fillingLayer)\n\n\t\t// Create border layer\n\t\tconst borderLayer = document.createElement('div')\n\t\tborderLayer.className = cursorStyles.cursorBorder\n\t\tthis.#cursor.appendChild(borderLayer)\n\n\t\tthis.wrapper.appendChild(this.#cursor)\n\t}\n\n\t#moveCursorToTarget() {\n\t\tconst newX = this.#currentCursorX + (this.#targetCursorX - this.#currentCursorX) * 0.2\n\t\tconst newY = this.#currentCursorY + (this.#targetCursorY - this.#currentCursorY) * 0.2\n\n\t\tconst xDistance = Math.abs(newX - this.#targetCursorX)\n\t\tif (xDistance > 0) {\n\t\t\tif (xDistance < 2) {\n\t\t\t\tthis.#currentCursorX = this.#targetCursorX\n\t\t\t} else {\n\t\t\t\tthis.#currentCursorX = newX\n\t\t\t}\n\t\t\tthis.#cursor.style.left = `${this.#currentCursorX}px`\n\t\t}\n\n\t\tconst yDistance = Math.abs(newY - this.#targetCursorY)\n\t\tif (yDistance > 0) {\n\t\t\tif (yDistance < 2) {\n\t\t\t\tthis.#currentCursorY = this.#targetCursorY\n\t\t\t} else {\n\t\t\t\tthis.#currentCursorY = newY\n\t\t\t}\n\t\t\tthis.#cursor.style.top = `${this.#currentCursorY}px`\n\t\t}\n\n\t\trequestAnimationFrame(() => this.#moveCursorToTarget())\n\t}\n\n\tsetCursorPosition(x: number, y: number) {\n\t\tthis.#targetCursorX = x\n\t\tthis.#targetCursorY = y\n\t}\n\n\ttriggerClickAnimation() {\n\t\tthis.#cursor.classList.remove(cursorStyles.clicking)\n\t\t// Force reflow to restart animation\n\t\tvoid this.#cursor.offsetHeight\n\t\tthis.#cursor.classList.add(cursorStyles.clicking)\n\t}\n\n\tshow() {\n\t\tif (this.shown) return\n\n\t\tthis.shown = true\n\t\tthis.motion?.start()\n\t\tthis.motion?.fadeIn()\n\n\t\tthis.wrapper.classList.add(styles.visible)\n\n\t\t// Initialize cursor position\n\t\tthis.#currentCursorX = window.innerWidth / 2\n\t\tthis.#currentCursorY = window.innerHeight / 2\n\t\tthis.#targetCursorX = this.#currentCursorX\n\t\tthis.#targetCursorY = this.#currentCursorY\n\t\tthis.#cursor.style.left = `${this.#currentCursorX}px`\n\t\tthis.#cursor.style.top = `${this.#currentCursorY}px`\n\t}\n\n\thide() {\n\t\tif (!this.shown) return\n\n\t\tthis.shown = false\n\t\tthis.motion?.fadeOut()\n\t\tthis.motion?.pause()\n\n\t\tthis.#cursor.classList.remove(cursorStyles.clicking)\n\n\t\tsetTimeout(() => {\n\t\t\tthis.wrapper.classList.remove(styles.visible)\n\t\t}, 800) // Match the animation duration\n\t}\n\n\tdispose() {\n\t\tconsole.log('dispose SimulatorMask')\n\t\tthis.motion?.dispose()\n\t\tthis.wrapper.remove()\n\t\tthis.dispatchEvent(new Event('dispose'))\n\t}\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -35,15 +35,28 @@ async function waitFor(seconds) {
|
|
|
35
35
|
await new Promise((resolve) => setTimeout(resolve, seconds * 1e3));
|
|
36
36
|
}
|
|
37
37
|
__name(waitFor, "waitFor");
|
|
38
|
-
async function movePointerToElement(element) {
|
|
39
|
-
const rect = element.getBoundingClientRect();
|
|
38
|
+
async function movePointerToElement(element, x, y) {
|
|
40
39
|
const offset = getIframeOffset(element);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
window.dispatchEvent(
|
|
41
|
+
new CustomEvent("PageAgent::MovePointerTo", {
|
|
42
|
+
detail: { x: x + offset.x, y: y + offset.y }
|
|
43
|
+
})
|
|
44
|
+
);
|
|
44
45
|
await waitFor(0.3);
|
|
45
46
|
}
|
|
46
47
|
__name(movePointerToElement, "movePointerToElement");
|
|
48
|
+
async function clickPointer() {
|
|
49
|
+
window.dispatchEvent(new CustomEvent("PageAgent::ClickPointer"));
|
|
50
|
+
}
|
|
51
|
+
__name(clickPointer, "clickPointer");
|
|
52
|
+
async function enablePassThrough() {
|
|
53
|
+
window.dispatchEvent(new CustomEvent("PageAgent::EnablePassThrough"));
|
|
54
|
+
}
|
|
55
|
+
__name(enablePassThrough, "enablePassThrough");
|
|
56
|
+
async function disablePassThrough() {
|
|
57
|
+
window.dispatchEvent(new CustomEvent("PageAgent::DisablePassThrough"));
|
|
58
|
+
}
|
|
59
|
+
__name(disablePassThrough, "disablePassThrough");
|
|
47
60
|
function getElementByIndex(selectorMap, index) {
|
|
48
61
|
const interactiveNode = selectorMap.get(index);
|
|
49
62
|
if (!interactiveNode) {
|
|
@@ -62,13 +75,11 @@ __name(getElementByIndex, "getElementByIndex");
|
|
|
62
75
|
let lastClickedElement = null;
|
|
63
76
|
function blurLastClickedElement() {
|
|
64
77
|
if (lastClickedElement) {
|
|
78
|
+
lastClickedElement.dispatchEvent(new PointerEvent("pointerout", { bubbles: true }));
|
|
79
|
+
lastClickedElement.dispatchEvent(new PointerEvent("pointerleave", { bubbles: false }));
|
|
80
|
+
lastClickedElement.dispatchEvent(new MouseEvent("mouseout", { bubbles: true }));
|
|
81
|
+
lastClickedElement.dispatchEvent(new MouseEvent("mouseleave", { bubbles: false }));
|
|
65
82
|
lastClickedElement.blur();
|
|
66
|
-
lastClickedElement.dispatchEvent(
|
|
67
|
-
new MouseEvent("mouseout", { bubbles: true, cancelable: true })
|
|
68
|
-
);
|
|
69
|
-
lastClickedElement.dispatchEvent(
|
|
70
|
-
new MouseEvent("mouseleave", { bubbles: false, cancelable: true })
|
|
71
|
-
);
|
|
72
83
|
lastClickedElement = null;
|
|
73
84
|
}
|
|
74
85
|
}
|
|
@@ -79,15 +90,35 @@ async function clickElement(element) {
|
|
|
79
90
|
await scrollIntoViewIfNeeded(element);
|
|
80
91
|
const frame = element.ownerDocument.defaultView?.frameElement;
|
|
81
92
|
if (frame) await scrollIntoViewIfNeeded(frame);
|
|
82
|
-
|
|
83
|
-
|
|
93
|
+
const rect = element.getBoundingClientRect();
|
|
94
|
+
const x = rect.left + rect.width / 2;
|
|
95
|
+
const y = rect.top + rect.height / 2;
|
|
96
|
+
await movePointerToElement(element, x, y);
|
|
97
|
+
await clickPointer();
|
|
84
98
|
await waitFor(0.1);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
element.
|
|
90
|
-
|
|
99
|
+
const doc = element.ownerDocument;
|
|
100
|
+
await enablePassThrough();
|
|
101
|
+
const hitTarget = doc.elementFromPoint(x, y);
|
|
102
|
+
await disablePassThrough();
|
|
103
|
+
const target = hitTarget instanceof HTMLElement && element.contains(hitTarget) ? hitTarget : element;
|
|
104
|
+
const pointerOpts = {
|
|
105
|
+
bubbles: true,
|
|
106
|
+
cancelable: true,
|
|
107
|
+
clientX: x,
|
|
108
|
+
clientY: y,
|
|
109
|
+
pointerType: "mouse"
|
|
110
|
+
};
|
|
111
|
+
const mouseOpts = { bubbles: true, cancelable: true, clientX: x, clientY: y, button: 0 };
|
|
112
|
+
target.dispatchEvent(new PointerEvent("pointerover", pointerOpts));
|
|
113
|
+
target.dispatchEvent(new PointerEvent("pointerenter", { ...pointerOpts, bubbles: false }));
|
|
114
|
+
target.dispatchEvent(new MouseEvent("mouseover", mouseOpts));
|
|
115
|
+
target.dispatchEvent(new MouseEvent("mouseenter", { ...mouseOpts, bubbles: false }));
|
|
116
|
+
target.dispatchEvent(new PointerEvent("pointerdown", pointerOpts));
|
|
117
|
+
target.dispatchEvent(new MouseEvent("mousedown", mouseOpts));
|
|
118
|
+
element.focus({ preventScroll: true });
|
|
119
|
+
target.dispatchEvent(new PointerEvent("pointerup", pointerOpts));
|
|
120
|
+
target.dispatchEvent(new MouseEvent("mouseup", mouseOpts));
|
|
121
|
+
target.click();
|
|
91
122
|
await waitFor(0.2);
|
|
92
123
|
}
|
|
93
124
|
__name(clickElement, "clickElement");
|
|
@@ -177,7 +208,7 @@ async function scrollIntoViewIfNeeded(element) {
|
|
|
177
208
|
}
|
|
178
209
|
}
|
|
179
210
|
__name(scrollIntoViewIfNeeded, "scrollIntoViewIfNeeded");
|
|
180
|
-
async function scrollVertically(
|
|
211
|
+
async function scrollVertically(scroll_amount, element) {
|
|
181
212
|
if (element) {
|
|
182
213
|
const targetElement = element;
|
|
183
214
|
let currentElement = targetElement;
|
|
@@ -188,7 +219,7 @@ async function scrollVertically(down, scroll_amount, element) {
|
|
|
188
219
|
const dy2 = scroll_amount;
|
|
189
220
|
while (currentElement && attempts < 10) {
|
|
190
221
|
const computedStyle = window.getComputedStyle(currentElement);
|
|
191
|
-
const hasScrollableY = /(auto|scroll|overlay)/.test(computedStyle.overflowY);
|
|
222
|
+
const hasScrollableY = /(auto|scroll|overlay)/.test(computedStyle.overflowY) || computedStyle.scrollbarWidth && computedStyle.scrollbarWidth !== "auto" || computedStyle.scrollbarGutter && computedStyle.scrollbarGutter !== "auto";
|
|
192
223
|
const canScrollVertically = currentElement.scrollHeight > currentElement.clientHeight;
|
|
193
224
|
if (hasScrollableY && canScrollVertically) {
|
|
194
225
|
const beforeScroll = currentElement.scrollTop;
|
|
@@ -242,6 +273,8 @@ async function scrollVertically(down, scroll_amount, element) {
|
|
|
242
273
|
if (reachedTop) return `✅ Scrolled page by ${scrolled}px. Reached the top of the page.`;
|
|
243
274
|
return `✅ Scrolled page by ${scrolled}px.`;
|
|
244
275
|
} else {
|
|
276
|
+
const warningMsg = `The document is not scrollable. Falling back to container scroll.`;
|
|
277
|
+
console.log(`[PageController] ${warningMsg}`);
|
|
245
278
|
const scrollBefore = el.scrollTop;
|
|
246
279
|
const scrollMax = el.scrollHeight - el.clientHeight;
|
|
247
280
|
el.scrollBy({ top: dy, behavior: "smooth" });
|
|
@@ -249,19 +282,19 @@ async function scrollVertically(down, scroll_amount, element) {
|
|
|
249
282
|
const scrollAfter = el.scrollTop;
|
|
250
283
|
const scrolled = scrollAfter - scrollBefore;
|
|
251
284
|
if (Math.abs(scrolled) < 1) {
|
|
252
|
-
return dy > 0 ? `⚠️ Already at the bottom of container (${el.tagName}), cannot scroll down further.` : `⚠️ Already at the top of container (${el.tagName}), cannot scroll up further.`;
|
|
285
|
+
return dy > 0 ? `⚠️ ${warningMsg} Already at the bottom of container (${el.tagName}), cannot scroll down further.` : `⚠️ ${warningMsg} Already at the top of container (${el.tagName}), cannot scroll up further.`;
|
|
253
286
|
}
|
|
254
287
|
const reachedBottom = dy > 0 && scrollAfter >= scrollMax - 1;
|
|
255
288
|
const reachedTop = dy < 0 && scrollAfter <= 1;
|
|
256
289
|
if (reachedBottom)
|
|
257
|
-
return `✅ Scrolled container (${el.tagName}) by ${scrolled}px. Reached the bottom.`;
|
|
290
|
+
return `✅ ${warningMsg} Scrolled container (${el.tagName}) by ${scrolled}px. Reached the bottom.`;
|
|
258
291
|
if (reachedTop)
|
|
259
|
-
return `✅ Scrolled container (${el.tagName}) by ${scrolled}px. Reached the top.`;
|
|
260
|
-
return `✅ Scrolled container (${el.tagName}) by ${scrolled}px.`;
|
|
292
|
+
return `✅ ${warningMsg} Scrolled container (${el.tagName}) by ${scrolled}px. Reached the top.`;
|
|
293
|
+
return `✅ ${warningMsg} Scrolled container (${el.tagName}) by ${scrolled}px.`;
|
|
261
294
|
}
|
|
262
295
|
}
|
|
263
296
|
__name(scrollVertically, "scrollVertically");
|
|
264
|
-
async function scrollHorizontally(
|
|
297
|
+
async function scrollHorizontally(scroll_amount, element) {
|
|
265
298
|
if (element) {
|
|
266
299
|
const targetElement = element;
|
|
267
300
|
let currentElement = targetElement;
|
|
@@ -269,10 +302,10 @@ async function scrollHorizontally(right, scroll_amount, element) {
|
|
|
269
302
|
let scrolledElement = null;
|
|
270
303
|
let scrollDelta = 0;
|
|
271
304
|
let attempts = 0;
|
|
272
|
-
const dx2 =
|
|
305
|
+
const dx2 = scroll_amount;
|
|
273
306
|
while (currentElement && attempts < 10) {
|
|
274
307
|
const computedStyle = window.getComputedStyle(currentElement);
|
|
275
|
-
const hasScrollableX = /(auto|scroll|overlay)/.test(computedStyle.overflowX);
|
|
308
|
+
const hasScrollableX = /(auto|scroll|overlay)/.test(computedStyle.overflowX) || computedStyle.scrollbarWidth && computedStyle.scrollbarWidth !== "auto" || computedStyle.scrollbarGutter && computedStyle.scrollbarGutter !== "auto";
|
|
276
309
|
const canScrollHorizontally = currentElement.scrollWidth > currentElement.clientWidth;
|
|
277
310
|
if (hasScrollableX && canScrollHorizontally) {
|
|
278
311
|
const beforeScroll = currentElement.scrollLeft;
|
|
@@ -305,7 +338,7 @@ async function scrollHorizontally(right, scroll_amount, element) {
|
|
|
305
338
|
return `No horizontally scrollable container found for element (${targetElement.tagName})`;
|
|
306
339
|
}
|
|
307
340
|
}
|
|
308
|
-
const dx =
|
|
341
|
+
const dx = scroll_amount;
|
|
309
342
|
const bigEnough = /* @__PURE__ */ __name((el2) => el2.clientWidth >= window.innerWidth * 0.5, "bigEnough");
|
|
310
343
|
const canScroll = /* @__PURE__ */ __name((el2) => el2 && /(auto|scroll|overlay)/.test(getComputedStyle(el2).overflowX) && el2.scrollWidth > el2.clientWidth && bigEnough(el2), "canScroll");
|
|
311
344
|
let el = document.activeElement;
|
|
@@ -327,6 +360,8 @@ async function scrollHorizontally(right, scroll_amount, element) {
|
|
|
327
360
|
if (reachedLeft) return `✅ Scrolled page by ${scrolled}px. Reached the left edge of the page.`;
|
|
328
361
|
return `✅ Scrolled page horizontally by ${scrolled}px.`;
|
|
329
362
|
} else {
|
|
363
|
+
const warningMsg = `The document is not scrollable. Falling back to container scroll.`;
|
|
364
|
+
console.log(`[PageController] ${warningMsg}`);
|
|
330
365
|
const scrollBefore = el.scrollLeft;
|
|
331
366
|
const scrollMax = el.scrollWidth - el.clientWidth;
|
|
332
367
|
el.scrollBy({ left: dx, behavior: "smooth" });
|
|
@@ -334,15 +369,15 @@ async function scrollHorizontally(right, scroll_amount, element) {
|
|
|
334
369
|
const scrollAfter = el.scrollLeft;
|
|
335
370
|
const scrolled = scrollAfter - scrollBefore;
|
|
336
371
|
if (Math.abs(scrolled) < 1) {
|
|
337
|
-
return dx > 0 ? `⚠️ Already at the right edge of container (${el.tagName}), cannot scroll right further.` : `⚠️ Already at the left edge of container (${el.tagName}), cannot scroll left further.`;
|
|
372
|
+
return dx > 0 ? `⚠️ ${warningMsg} Already at the right edge of container (${el.tagName}), cannot scroll right further.` : `⚠️ ${warningMsg} Already at the left edge of container (${el.tagName}), cannot scroll left further.`;
|
|
338
373
|
}
|
|
339
374
|
const reachedRight = dx > 0 && scrollAfter >= scrollMax - 1;
|
|
340
375
|
const reachedLeft = dx < 0 && scrollAfter <= 1;
|
|
341
376
|
if (reachedRight)
|
|
342
|
-
return `✅ Scrolled container (${el.tagName}) by ${scrolled}px. Reached the right edge.`;
|
|
377
|
+
return `✅ ${warningMsg} Scrolled container (${el.tagName}) by ${scrolled}px. Reached the right edge.`;
|
|
343
378
|
if (reachedLeft)
|
|
344
|
-
return `✅ Scrolled container (${el.tagName}) by ${scrolled}px. Reached the left edge.`;
|
|
345
|
-
return `✅ Scrolled container (${el.tagName}) horizontally by ${scrolled}px.`;
|
|
379
|
+
return `✅ ${warningMsg} Scrolled container (${el.tagName}) by ${scrolled}px. Reached the left edge.`;
|
|
380
|
+
return `✅ ${warningMsg} Scrolled container (${el.tagName}) horizontally by ${scrolled}px.`;
|
|
346
381
|
}
|
|
347
382
|
}
|
|
348
383
|
__name(scrollHorizontally, "scrollHorizontally");
|
|
@@ -599,9 +634,10 @@ const domTree = /* @__PURE__ */ __name((args = {
|
|
|
599
634
|
}
|
|
600
635
|
const overflowX = style.overflowX;
|
|
601
636
|
const overflowY = style.overflowY;
|
|
637
|
+
const hasScrollbarSignal = style.scrollbarWidth && style.scrollbarWidth !== "auto" || style.scrollbarGutter && style.scrollbarGutter !== "auto";
|
|
602
638
|
const scrollableX = overflowX === "auto" || overflowX === "scroll";
|
|
603
639
|
const scrollableY = overflowY === "auto" || overflowY === "scroll";
|
|
604
|
-
if (!scrollableX && !scrollableY) {
|
|
640
|
+
if (!scrollableX && !scrollableY && !hasScrollbarSignal) {
|
|
605
641
|
return null;
|
|
606
642
|
}
|
|
607
643
|
const scrollWidth = element.scrollWidth - element.clientWidth;
|
|
@@ -610,10 +646,10 @@ const domTree = /* @__PURE__ */ __name((args = {
|
|
|
610
646
|
if (scrollWidth < threshold && scrollHeight < threshold) {
|
|
611
647
|
return null;
|
|
612
648
|
}
|
|
613
|
-
if (!scrollableY && scrollWidth < threshold) {
|
|
649
|
+
if (!scrollableY && !hasScrollbarSignal && scrollWidth < threshold) {
|
|
614
650
|
return null;
|
|
615
651
|
}
|
|
616
|
-
if (!scrollableX && scrollHeight < threshold) {
|
|
652
|
+
if (!scrollableX && !hasScrollbarSignal && scrollHeight < threshold) {
|
|
617
653
|
return null;
|
|
618
654
|
}
|
|
619
655
|
const distanceToTop = element.scrollTop;
|
|
@@ -630,6 +666,7 @@ const domTree = /* @__PURE__ */ __name((args = {
|
|
|
630
666
|
scrollable: true,
|
|
631
667
|
scrollData
|
|
632
668
|
});
|
|
669
|
+
console.log("scrollData!!!", scrollData);
|
|
633
670
|
return scrollData;
|
|
634
671
|
}
|
|
635
672
|
__name(isScrollableElement, "isScrollableElement");
|
|
@@ -1064,6 +1101,28 @@ const domTree = /* @__PURE__ */ __name((args = {
|
|
|
1064
1101
|
return false;
|
|
1065
1102
|
}
|
|
1066
1103
|
__name(isInExpandedViewport, "isInExpandedViewport");
|
|
1104
|
+
const INTERACTIVE_ARIA_ATTRS = [
|
|
1105
|
+
"aria-expanded",
|
|
1106
|
+
"aria-checked",
|
|
1107
|
+
"aria-selected",
|
|
1108
|
+
"aria-pressed",
|
|
1109
|
+
"aria-haspopup",
|
|
1110
|
+
"aria-controls",
|
|
1111
|
+
"aria-owns",
|
|
1112
|
+
"aria-activedescendant",
|
|
1113
|
+
"aria-valuenow",
|
|
1114
|
+
"aria-valuetext",
|
|
1115
|
+
"aria-valuemax",
|
|
1116
|
+
"aria-valuemin",
|
|
1117
|
+
"aria-autocomplete"
|
|
1118
|
+
];
|
|
1119
|
+
function hasInteractiveAria(el) {
|
|
1120
|
+
for (let i = 0; i < INTERACTIVE_ARIA_ATTRS.length; i++) {
|
|
1121
|
+
if (el.hasAttribute(INTERACTIVE_ARIA_ATTRS[i])) return true;
|
|
1122
|
+
}
|
|
1123
|
+
return false;
|
|
1124
|
+
}
|
|
1125
|
+
__name(hasInteractiveAria, "hasInteractiveAria");
|
|
1067
1126
|
function isInteractiveCandidate(element) {
|
|
1068
1127
|
if (!element || element.nodeType !== Node.ELEMENT_NODE) return false;
|
|
1069
1128
|
const tagName = element.tagName.toLowerCase();
|
|
@@ -1078,7 +1137,7 @@ const domTree = /* @__PURE__ */ __name((args = {
|
|
|
1078
1137
|
"label"
|
|
1079
1138
|
]);
|
|
1080
1139
|
if (interactiveElements.has(tagName)) return true;
|
|
1081
|
-
const hasQuickInteractiveAttr = element.hasAttribute("onclick") || element.hasAttribute("role") || element.hasAttribute("tabindex") || element
|
|
1140
|
+
const hasQuickInteractiveAttr = element.hasAttribute("onclick") || element.hasAttribute("role") || element.hasAttribute("tabindex") || hasInteractiveAria(element) || element.hasAttribute("data-action") || element.getAttribute("contenteditable") === "true";
|
|
1082
1141
|
return hasQuickInteractiveAttr;
|
|
1083
1142
|
}
|
|
1084
1143
|
__name(isInteractiveCandidate, "isInteractiveCandidate");
|
|
@@ -1091,9 +1150,10 @@ const domTree = /* @__PURE__ */ __name((args = {
|
|
|
1091
1150
|
"summary",
|
|
1092
1151
|
"details",
|
|
1093
1152
|
"label",
|
|
1094
|
-
"option"
|
|
1153
|
+
"option",
|
|
1154
|
+
"li"
|
|
1095
1155
|
]);
|
|
1096
|
-
const
|
|
1156
|
+
const DISTINCT_INTERACTIVE_ROLES = /* @__PURE__ */ new Set([
|
|
1097
1157
|
"button",
|
|
1098
1158
|
"link",
|
|
1099
1159
|
"menuitem",
|
|
@@ -1109,6 +1169,9 @@ const domTree = /* @__PURE__ */ __name((args = {
|
|
|
1109
1169
|
"searchbox",
|
|
1110
1170
|
"textbox",
|
|
1111
1171
|
"listbox",
|
|
1172
|
+
"listitem",
|
|
1173
|
+
"treeitem",
|
|
1174
|
+
"row",
|
|
1112
1175
|
"option",
|
|
1113
1176
|
"scrollbar"
|
|
1114
1177
|
]);
|
|
@@ -1139,7 +1202,7 @@ const domTree = /* @__PURE__ */ __name((args = {
|
|
|
1139
1202
|
if (DISTINCT_INTERACTIVE_TAGS.has(tagName)) {
|
|
1140
1203
|
return true;
|
|
1141
1204
|
}
|
|
1142
|
-
if (role &&
|
|
1205
|
+
if (role && DISTINCT_INTERACTIVE_ROLES.has(role)) {
|
|
1143
1206
|
return true;
|
|
1144
1207
|
}
|
|
1145
1208
|
if (element.isContentEditable || element.getAttribute("contenteditable") === "true") {
|
|
@@ -1151,6 +1214,9 @@ const domTree = /* @__PURE__ */ __name((args = {
|
|
|
1151
1214
|
if (element.hasAttribute("onclick") || typeof element.onclick === "function") {
|
|
1152
1215
|
return true;
|
|
1153
1216
|
}
|
|
1217
|
+
if (hasInteractiveAria(element)) {
|
|
1218
|
+
return true;
|
|
1219
|
+
}
|
|
1154
1220
|
try {
|
|
1155
1221
|
const getEventListenersForNode = element?.ownerDocument?.defaultView?.getEventListenersForNode || window.getEventListenersForNode;
|
|
1156
1222
|
if (typeof getEventListenersForNode === "function") {
|
|
@@ -1194,6 +1260,9 @@ const domTree = /* @__PURE__ */ __name((args = {
|
|
|
1194
1260
|
if (isHeuristicallyInteractive(element)) {
|
|
1195
1261
|
return true;
|
|
1196
1262
|
}
|
|
1263
|
+
if (extraData.get(element)?.scrollable) {
|
|
1264
|
+
return true;
|
|
1265
|
+
}
|
|
1197
1266
|
return false;
|
|
1198
1267
|
}
|
|
1199
1268
|
__name(isElementDistinctInteraction, "isElementDistinctInteraction");
|
|
@@ -1384,6 +1453,17 @@ function resolveViewportExpansion(viewportExpansion) {
|
|
|
1384
1453
|
return viewportExpansion ?? DEFAULT_VIEWPORT_EXPANSION;
|
|
1385
1454
|
}
|
|
1386
1455
|
__name(resolveViewportExpansion, "resolveViewportExpansion");
|
|
1456
|
+
const SEMANTIC_TAGS = /* @__PURE__ */ new Set([
|
|
1457
|
+
"nav",
|
|
1458
|
+
"menu",
|
|
1459
|
+
// 'main',
|
|
1460
|
+
"header",
|
|
1461
|
+
"footer",
|
|
1462
|
+
"aside",
|
|
1463
|
+
// 'article',
|
|
1464
|
+
// 'form',
|
|
1465
|
+
"dialog"
|
|
1466
|
+
]);
|
|
1387
1467
|
const newElementsCache = /* @__PURE__ */ new WeakMap();
|
|
1388
1468
|
function getFlatTree(config) {
|
|
1389
1469
|
const viewportExpansion = resolveViewportExpansion(config.viewportExpansion);
|
|
@@ -1458,7 +1538,7 @@ function matchAttributes(attrs, patterns) {
|
|
|
1458
1538
|
return result2;
|
|
1459
1539
|
}
|
|
1460
1540
|
__name(matchAttributes, "matchAttributes");
|
|
1461
|
-
function flatTreeToString(flatTree, includeAttributes) {
|
|
1541
|
+
function flatTreeToString(flatTree, includeAttributes = [], keepSemanticTags = false) {
|
|
1462
1542
|
const DEFAULT_INCLUDE_ATTRIBUTES = [
|
|
1463
1543
|
"title",
|
|
1464
1544
|
"type",
|
|
@@ -1485,7 +1565,7 @@ function flatTreeToString(flatTree, includeAttributes) {
|
|
|
1485
1565
|
// content editable
|
|
1486
1566
|
"contenteditable"
|
|
1487
1567
|
];
|
|
1488
|
-
const includeAttrs = [...includeAttributes
|
|
1568
|
+
const includeAttrs = [...includeAttributes, ...DEFAULT_INCLUDE_ATTRIBUTES];
|
|
1489
1569
|
const capTextLength = /* @__PURE__ */ __name((text, maxLength) => {
|
|
1490
1570
|
if (text.length > maxLength) {
|
|
1491
1571
|
return text.substring(0, maxLength) + "...";
|
|
@@ -1554,6 +1634,7 @@ function flatTreeToString(flatTree, includeAttributes) {
|
|
|
1554
1634
|
let nextDepth = depth;
|
|
1555
1635
|
const depthStr = " ".repeat(depth);
|
|
1556
1636
|
if (node.type === "element") {
|
|
1637
|
+
const isSemantic = keepSemanticTags && node.tagName && SEMANTIC_TAGS.has(node.tagName);
|
|
1557
1638
|
if (node.highlightIndex !== void 0) {
|
|
1558
1639
|
nextDepth += 1;
|
|
1559
1640
|
const text = getAllTextTillNextClickableElement(node);
|
|
@@ -1621,9 +1702,22 @@ function flatTreeToString(flatTree, includeAttributes) {
|
|
|
1621
1702
|
line += " />";
|
|
1622
1703
|
result22.push(line);
|
|
1623
1704
|
}
|
|
1705
|
+
const emitSemantic = isSemantic && node.highlightIndex === void 0;
|
|
1706
|
+
const mark = emitSemantic ? result22.length : -1;
|
|
1707
|
+
if (emitSemantic) {
|
|
1708
|
+
result22.push(`${depthStr}<${node.tagName}>`);
|
|
1709
|
+
nextDepth += 1;
|
|
1710
|
+
}
|
|
1624
1711
|
for (const child of node.children) {
|
|
1625
1712
|
processNode(child, nextDepth, result22);
|
|
1626
1713
|
}
|
|
1714
|
+
if (emitSemantic) {
|
|
1715
|
+
if (result22.length === mark + 1) {
|
|
1716
|
+
result22.pop();
|
|
1717
|
+
} else {
|
|
1718
|
+
result22.push(`${depthStr}</${node.tagName}>`);
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1627
1721
|
} else if (node.type === "text") {
|
|
1628
1722
|
if (hasParentWithHighlightIndex(node)) {
|
|
1629
1723
|
return;
|
|
@@ -1794,7 +1888,7 @@ const _PageController = class _PageController extends EventTarget {
|
|
|
1794
1888
|
initMask() {
|
|
1795
1889
|
if (this.maskReady !== null) return;
|
|
1796
1890
|
this.maskReady = (async () => {
|
|
1797
|
-
const { SimulatorMask } = await import("./SimulatorMask-
|
|
1891
|
+
const { SimulatorMask } = await import("./SimulatorMask-CU7szDjy.js");
|
|
1798
1892
|
this.mask = new SimulatorMask();
|
|
1799
1893
|
})();
|
|
1800
1894
|
}
|
|
@@ -1858,7 +1952,11 @@ ${scrollHintAbove}`;
|
|
|
1858
1952
|
...this.config,
|
|
1859
1953
|
interactiveBlacklist: blacklist
|
|
1860
1954
|
});
|
|
1861
|
-
this.simplifiedHTML = flatTreeToString(
|
|
1955
|
+
this.simplifiedHTML = flatTreeToString(
|
|
1956
|
+
this.flatTree,
|
|
1957
|
+
this.config.includeAttributes,
|
|
1958
|
+
this.config.keepSemanticTags
|
|
1959
|
+
);
|
|
1862
1960
|
this.selectorMap.clear();
|
|
1863
1961
|
this.selectorMap = getSelectorMap(this.flatTree);
|
|
1864
1962
|
this.elementTextMap.clear();
|
|
@@ -1874,6 +1972,7 @@ ${scrollHintAbove}`;
|
|
|
1874
1972
|
* Clean up all element highlights
|
|
1875
1973
|
*/
|
|
1876
1974
|
async cleanUpHighlights() {
|
|
1975
|
+
console.log("[PageController] cleanUpHighlights");
|
|
1877
1976
|
cleanUpHighlights();
|
|
1878
1977
|
}
|
|
1879
1978
|
// ======= Element Actions =======
|
|
@@ -1959,9 +2058,9 @@ ${scrollHintAbove}`;
|
|
|
1959
2058
|
try {
|
|
1960
2059
|
const { down, numPages, pixels, index } = options;
|
|
1961
2060
|
this.assertIndexed();
|
|
1962
|
-
const scrollAmount = pixels ?? numPages * (down ? 1 : -1)
|
|
2061
|
+
const scrollAmount = (pixels ?? numPages * window.innerHeight) * (down ? 1 : -1);
|
|
1963
2062
|
const element = index !== void 0 ? getElementByIndex(this.selectorMap, index) : null;
|
|
1964
|
-
const message = await scrollVertically(
|
|
2063
|
+
const message = await scrollVertically(scrollAmount, element);
|
|
1965
2064
|
return {
|
|
1966
2065
|
success: true,
|
|
1967
2066
|
message
|
|
@@ -1982,7 +2081,7 @@ ${scrollHintAbove}`;
|
|
|
1982
2081
|
this.assertIndexed();
|
|
1983
2082
|
const scrollAmount = pixels * (right ? 1 : -1);
|
|
1984
2083
|
const element = index !== void 0 ? getElementByIndex(this.selectorMap, index) : null;
|
|
1985
|
-
const message = await scrollHorizontally(
|
|
2084
|
+
const message = await scrollHorizontally(scrollAmount, element);
|
|
1986
2085
|
return {
|
|
1987
2086
|
success: true,
|
|
1988
2087
|
message
|