@page-agent/page-controller 0.0.6

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.
@@ -0,0 +1,1901 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ async function waitFor(seconds) {
4
+ await new Promise((resolve) => setTimeout(resolve, seconds * 1e3));
5
+ }
6
+ __name(waitFor, "waitFor");
7
+ async function movePointerToElement(element) {
8
+ const rect = element.getBoundingClientRect();
9
+ const x = rect.left + rect.width / 2;
10
+ const y = rect.top + rect.height / 2;
11
+ window.dispatchEvent(new CustomEvent("PageAgent::MovePointerTo", { detail: { x, y } }));
12
+ await waitFor(0.3);
13
+ }
14
+ __name(movePointerToElement, "movePointerToElement");
15
+ function getElementByIndex(selectorMap, index) {
16
+ const interactiveNode = selectorMap.get(index);
17
+ if (!interactiveNode) {
18
+ throw new Error(`No interactive element found at index ${index}`);
19
+ }
20
+ const element = interactiveNode.ref;
21
+ if (!element) {
22
+ throw new Error(`Element at index ${index} does not have a reference`);
23
+ }
24
+ if (!(element instanceof HTMLElement)) {
25
+ throw new Error(`Element at index ${index} is not an HTMLElement`);
26
+ }
27
+ return element;
28
+ }
29
+ __name(getElementByIndex, "getElementByIndex");
30
+ let lastClickedElement = null;
31
+ function blurLastClickedElement() {
32
+ if (lastClickedElement) {
33
+ lastClickedElement.blur();
34
+ lastClickedElement.dispatchEvent(
35
+ new MouseEvent("mouseout", { bubbles: true, cancelable: true })
36
+ );
37
+ lastClickedElement = null;
38
+ }
39
+ }
40
+ __name(blurLastClickedElement, "blurLastClickedElement");
41
+ async function clickElement(element) {
42
+ blurLastClickedElement();
43
+ lastClickedElement = element;
44
+ await scrollIntoViewIfNeeded(element);
45
+ await movePointerToElement(element);
46
+ window.dispatchEvent(new CustomEvent("PageAgent::ClickPointer"));
47
+ await waitFor(0.1);
48
+ element.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true, cancelable: true }));
49
+ element.dispatchEvent(new MouseEvent("mouseover", { bubbles: true, cancelable: true }));
50
+ element.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true }));
51
+ element.focus();
52
+ element.dispatchEvent(new MouseEvent("mouseup", { bubbles: true, cancelable: true }));
53
+ element.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
54
+ await waitFor(0.1);
55
+ }
56
+ __name(clickElement, "clickElement");
57
+ const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
58
+ window.HTMLInputElement.prototype,
59
+ "value"
60
+ ).set;
61
+ const nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(
62
+ window.HTMLTextAreaElement.prototype,
63
+ "value"
64
+ ).set;
65
+ async function inputTextElement(element, text) {
66
+ if (!(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement)) {
67
+ throw new Error("Element is not an input or textarea");
68
+ }
69
+ await clickElement(element);
70
+ if (element instanceof HTMLTextAreaElement) {
71
+ nativeTextAreaValueSetter.call(element, text);
72
+ } else {
73
+ nativeInputValueSetter.call(element, text);
74
+ }
75
+ const inputEvent = new Event("input", { bubbles: true });
76
+ element.dispatchEvent(inputEvent);
77
+ await waitFor(0.1);
78
+ blurLastClickedElement();
79
+ }
80
+ __name(inputTextElement, "inputTextElement");
81
+ async function selectOptionElement(selectElement, optionText) {
82
+ if (!(selectElement instanceof HTMLSelectElement)) {
83
+ throw new Error("Element is not a select element");
84
+ }
85
+ const options = Array.from(selectElement.options);
86
+ const option = options.find((opt) => opt.textContent?.trim() === optionText.trim());
87
+ if (!option) {
88
+ throw new Error(`Option with text "${optionText}" not found in select element`);
89
+ }
90
+ selectElement.value = option.value;
91
+ selectElement.dispatchEvent(new Event("change", { bubbles: true }));
92
+ await waitFor(0.1);
93
+ }
94
+ __name(selectOptionElement, "selectOptionElement");
95
+ async function scrollIntoViewIfNeeded(element) {
96
+ const el = element;
97
+ if (el.scrollIntoViewIfNeeded) {
98
+ el.scrollIntoViewIfNeeded();
99
+ } else {
100
+ el.scrollIntoView({ behavior: "auto", block: "center", inline: "nearest" });
101
+ }
102
+ }
103
+ __name(scrollIntoViewIfNeeded, "scrollIntoViewIfNeeded");
104
+ async function scrollVertically(down, scroll_amount, element) {
105
+ if (element) {
106
+ const targetElement = element;
107
+ console.log(
108
+ "[SCROLL DEBUG] Starting direct container scroll for element:",
109
+ targetElement.tagName
110
+ );
111
+ let currentElement = targetElement;
112
+ let scrollSuccess = false;
113
+ let scrolledElement = null;
114
+ let scrollDelta = 0;
115
+ let attempts = 0;
116
+ const dy2 = scroll_amount;
117
+ while (currentElement && attempts < 10) {
118
+ const computedStyle = window.getComputedStyle(currentElement);
119
+ const hasScrollableY = /(auto|scroll|overlay)/.test(computedStyle.overflowY);
120
+ const canScrollVertically = currentElement.scrollHeight > currentElement.clientHeight;
121
+ console.log(
122
+ "[SCROLL DEBUG] Checking element:",
123
+ currentElement.tagName,
124
+ "hasScrollableY:",
125
+ hasScrollableY,
126
+ "canScrollVertically:",
127
+ canScrollVertically,
128
+ "scrollHeight:",
129
+ currentElement.scrollHeight,
130
+ "clientHeight:",
131
+ currentElement.clientHeight
132
+ );
133
+ if (hasScrollableY && canScrollVertically) {
134
+ const beforeScroll = currentElement.scrollTop;
135
+ const maxScroll = currentElement.scrollHeight - currentElement.clientHeight;
136
+ let scrollAmount = dy2 / 3;
137
+ if (scrollAmount > 0) {
138
+ scrollAmount = Math.min(scrollAmount, maxScroll - beforeScroll);
139
+ } else {
140
+ scrollAmount = Math.max(scrollAmount, -beforeScroll);
141
+ }
142
+ currentElement.scrollTop = beforeScroll + scrollAmount;
143
+ const afterScroll = currentElement.scrollTop;
144
+ const actualScrollDelta = afterScroll - beforeScroll;
145
+ console.log(
146
+ "[SCROLL DEBUG] Scroll attempt:",
147
+ currentElement.tagName,
148
+ "before:",
149
+ beforeScroll,
150
+ "after:",
151
+ afterScroll,
152
+ "delta:",
153
+ actualScrollDelta
154
+ );
155
+ if (Math.abs(actualScrollDelta) > 0.5) {
156
+ scrollSuccess = true;
157
+ scrolledElement = currentElement;
158
+ scrollDelta = actualScrollDelta;
159
+ console.log(
160
+ "[SCROLL DEBUG] Successfully scrolled container:",
161
+ currentElement.tagName,
162
+ "delta:",
163
+ actualScrollDelta
164
+ );
165
+ break;
166
+ }
167
+ }
168
+ if (currentElement === document.body || currentElement === document.documentElement) {
169
+ break;
170
+ }
171
+ currentElement = currentElement.parentElement;
172
+ attempts++;
173
+ }
174
+ if (scrollSuccess) {
175
+ return `Scrolled container (${scrolledElement?.tagName}) by ${scrollDelta}px`;
176
+ } else {
177
+ return `No scrollable container found for element (${targetElement.tagName})`;
178
+ }
179
+ }
180
+ const dy = scroll_amount;
181
+ const bigEnough = /* @__PURE__ */ __name((el2) => el2.clientHeight >= window.innerHeight * 0.5, "bigEnough");
182
+ const canScroll = /* @__PURE__ */ __name((el2) => el2 && /(auto|scroll|overlay)/.test(getComputedStyle(el2).overflowY) && el2.scrollHeight > el2.clientHeight && bigEnough(el2), "canScroll");
183
+ let el = document.activeElement;
184
+ while (el && !canScroll(el) && el !== document.body) el = el.parentElement;
185
+ el = canScroll(el) ? el : Array.from(document.querySelectorAll("*")).find(canScroll) || document.scrollingElement || document.documentElement;
186
+ if (el === document.scrollingElement || el === document.documentElement || el === document.body) {
187
+ window.scrollBy(0, dy);
188
+ return `✅ Scrolled page by ${dy}px.`;
189
+ } else {
190
+ el.scrollBy({ top: dy, behavior: "smooth" });
191
+ await waitFor(0.1);
192
+ return `✅ Scrolled container (${el.tagName}) by ${dy}px.`;
193
+ }
194
+ }
195
+ __name(scrollVertically, "scrollVertically");
196
+ async function scrollHorizontally(right, scroll_amount, element) {
197
+ if (element) {
198
+ const targetElement = element;
199
+ console.log(
200
+ "[SCROLL DEBUG] Starting direct container scroll for element:",
201
+ targetElement.tagName
202
+ );
203
+ let currentElement = targetElement;
204
+ let scrollSuccess = false;
205
+ let scrolledElement = null;
206
+ let scrollDelta = 0;
207
+ let attempts = 0;
208
+ const dx2 = right ? scroll_amount : -scroll_amount;
209
+ while (currentElement && attempts < 10) {
210
+ const computedStyle = window.getComputedStyle(currentElement);
211
+ const hasScrollableX = /(auto|scroll|overlay)/.test(computedStyle.overflowX);
212
+ const canScrollHorizontally = currentElement.scrollWidth > currentElement.clientWidth;
213
+ console.log(
214
+ "[SCROLL DEBUG] Checking element:",
215
+ currentElement.tagName,
216
+ "hasScrollableX:",
217
+ hasScrollableX,
218
+ "canScrollHorizontally:",
219
+ canScrollHorizontally,
220
+ "scrollWidth:",
221
+ currentElement.scrollWidth,
222
+ "clientWidth:",
223
+ currentElement.clientWidth
224
+ );
225
+ if (hasScrollableX && canScrollHorizontally) {
226
+ const beforeScroll = currentElement.scrollLeft;
227
+ const maxScroll = currentElement.scrollWidth - currentElement.clientWidth;
228
+ let scrollAmount = dx2 / 3;
229
+ if (scrollAmount > 0) {
230
+ scrollAmount = Math.min(scrollAmount, maxScroll - beforeScroll);
231
+ } else {
232
+ scrollAmount = Math.max(scrollAmount, -beforeScroll);
233
+ }
234
+ currentElement.scrollLeft = beforeScroll + scrollAmount;
235
+ const afterScroll = currentElement.scrollLeft;
236
+ const actualScrollDelta = afterScroll - beforeScroll;
237
+ console.log(
238
+ "[SCROLL DEBUG] Scroll attempt:",
239
+ currentElement.tagName,
240
+ "before:",
241
+ beforeScroll,
242
+ "after:",
243
+ afterScroll,
244
+ "delta:",
245
+ actualScrollDelta
246
+ );
247
+ if (Math.abs(actualScrollDelta) > 0.5) {
248
+ scrollSuccess = true;
249
+ scrolledElement = currentElement;
250
+ scrollDelta = actualScrollDelta;
251
+ console.log(
252
+ "[SCROLL DEBUG] Successfully scrolled container:",
253
+ currentElement.tagName,
254
+ "delta:",
255
+ actualScrollDelta
256
+ );
257
+ break;
258
+ }
259
+ }
260
+ if (currentElement === document.body || currentElement === document.documentElement) {
261
+ break;
262
+ }
263
+ currentElement = currentElement.parentElement;
264
+ attempts++;
265
+ }
266
+ if (scrollSuccess) {
267
+ return `Scrolled container (${scrolledElement?.tagName}) horizontally by ${scrollDelta}px`;
268
+ } else {
269
+ return `No horizontally scrollable container found for element (${targetElement.tagName})`;
270
+ }
271
+ }
272
+ const dx = right ? scroll_amount : -scroll_amount;
273
+ const bigEnough = /* @__PURE__ */ __name((el2) => el2.clientWidth >= window.innerWidth * 0.5, "bigEnough");
274
+ const canScroll = /* @__PURE__ */ __name((el2) => el2 && /(auto|scroll|overlay)/.test(getComputedStyle(el2).overflowX) && el2.scrollWidth > el2.clientWidth && bigEnough(el2), "canScroll");
275
+ let el = document.activeElement;
276
+ while (el && !canScroll(el) && el !== document.body) el = el.parentElement;
277
+ el = canScroll(el) ? el : Array.from(document.querySelectorAll("*")).find(canScroll) || document.scrollingElement || document.documentElement;
278
+ if (el === document.scrollingElement || el === document.documentElement || el === document.body) {
279
+ window.scrollBy(dx, 0);
280
+ return `✅ Scrolled page horizontally by ${dx}px`;
281
+ } else {
282
+ el.scrollBy({ left: dx, behavior: "smooth" });
283
+ await waitFor(0.1);
284
+ return `✅ Scrolled container (${el.tagName}) horizontally by ${dx}px`;
285
+ }
286
+ }
287
+ __name(scrollHorizontally, "scrollHorizontally");
288
+ const VIEWPORT_EXPANSION = -1;
289
+ const domTree = /* @__PURE__ */ __name((args = {
290
+ doHighlightElements: true,
291
+ focusHighlightIndex: -1,
292
+ viewportExpansion: 0,
293
+ debugMode: false,
294
+ /**
295
+ * @edit
296
+ */
297
+ /** @type {Element[]} */
298
+ interactiveBlacklist: [],
299
+ /** @type {Element[]} */
300
+ interactiveWhitelist: [],
301
+ highlightOpacity: 0.1,
302
+ highlightLabelOpacity: 0.5
303
+ }) => {
304
+ const { interactiveBlacklist, interactiveWhitelist, highlightOpacity, highlightLabelOpacity } = args;
305
+ const { doHighlightElements, focusHighlightIndex, viewportExpansion, debugMode } = args;
306
+ let highlightIndex = 0;
307
+ const extraData = /* @__PURE__ */ new WeakMap();
308
+ function addExtraData(element, data) {
309
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) return;
310
+ extraData.set(element, { ...extraData.get(element), ...data });
311
+ }
312
+ __name(addExtraData, "addExtraData");
313
+ const DOM_CACHE = {
314
+ boundingRects: /* @__PURE__ */ new WeakMap(),
315
+ clientRects: /* @__PURE__ */ new WeakMap(),
316
+ computedStyles: /* @__PURE__ */ new WeakMap(),
317
+ clearCache: /* @__PURE__ */ __name(() => {
318
+ DOM_CACHE.boundingRects = /* @__PURE__ */ new WeakMap();
319
+ DOM_CACHE.clientRects = /* @__PURE__ */ new WeakMap();
320
+ DOM_CACHE.computedStyles = /* @__PURE__ */ new WeakMap();
321
+ }, "clearCache")
322
+ };
323
+ function getCachedBoundingRect(element) {
324
+ if (!element) return null;
325
+ if (DOM_CACHE.boundingRects.has(element)) {
326
+ return DOM_CACHE.boundingRects.get(element);
327
+ }
328
+ const rect = element.getBoundingClientRect();
329
+ if (rect) {
330
+ DOM_CACHE.boundingRects.set(element, rect);
331
+ }
332
+ return rect;
333
+ }
334
+ __name(getCachedBoundingRect, "getCachedBoundingRect");
335
+ function getCachedComputedStyle(element) {
336
+ if (!element) return null;
337
+ if (DOM_CACHE.computedStyles.has(element)) {
338
+ return DOM_CACHE.computedStyles.get(element);
339
+ }
340
+ const style = window.getComputedStyle(element);
341
+ if (style) {
342
+ DOM_CACHE.computedStyles.set(element, style);
343
+ }
344
+ return style;
345
+ }
346
+ __name(getCachedComputedStyle, "getCachedComputedStyle");
347
+ function getCachedClientRects(element) {
348
+ if (!element) return null;
349
+ if (DOM_CACHE.clientRects.has(element)) {
350
+ return DOM_CACHE.clientRects.get(element);
351
+ }
352
+ const rects = element.getClientRects();
353
+ if (rects) {
354
+ DOM_CACHE.clientRects.set(element, rects);
355
+ }
356
+ return rects;
357
+ }
358
+ __name(getCachedClientRects, "getCachedClientRects");
359
+ const DOM_HASH_MAP = {};
360
+ const ID = { current: 0 };
361
+ const HIGHLIGHT_CONTAINER_ID = "playwright-highlight-container";
362
+ function highlightElement(element, index, parentIframe = null) {
363
+ if (!element) return index;
364
+ const overlays = [];
365
+ let label = null;
366
+ let labelWidth = 20;
367
+ let labelHeight = 16;
368
+ let cleanupFn = null;
369
+ try {
370
+ let container = document.getElementById(HIGHLIGHT_CONTAINER_ID);
371
+ if (!container) {
372
+ container = document.createElement("div");
373
+ container.id = HIGHLIGHT_CONTAINER_ID;
374
+ container.style.position = "fixed";
375
+ container.style.pointerEvents = "none";
376
+ container.style.top = "0";
377
+ container.style.left = "0";
378
+ container.style.width = "100%";
379
+ container.style.height = "100%";
380
+ container.style.zIndex = "2147483640";
381
+ container.style.backgroundColor = "transparent";
382
+ document.body.appendChild(container);
383
+ }
384
+ const rects = element.getClientRects();
385
+ if (!rects || rects.length === 0) return index;
386
+ const colors = [
387
+ "#FF0000",
388
+ "#00FF00",
389
+ "#0000FF",
390
+ "#FFA500",
391
+ "#800080",
392
+ "#008080",
393
+ "#FF69B4",
394
+ "#4B0082",
395
+ "#FF4500",
396
+ "#2E8B57",
397
+ "#DC143C",
398
+ "#4682B4"
399
+ ];
400
+ const colorIndex = index % colors.length;
401
+ let baseColor = colors[colorIndex];
402
+ const backgroundColor = baseColor + Math.floor(highlightOpacity * 255).toString(16).padStart(2, "0");
403
+ baseColor = baseColor + Math.floor(highlightLabelOpacity * 255).toString(16).padStart(2, "0");
404
+ let iframeOffset = { x: 0, y: 0 };
405
+ if (parentIframe) {
406
+ const iframeRect = parentIframe.getBoundingClientRect();
407
+ iframeOffset.x = iframeRect.left;
408
+ iframeOffset.y = iframeRect.top;
409
+ }
410
+ const fragment = document.createDocumentFragment();
411
+ for (const rect of rects) {
412
+ if (rect.width === 0 || rect.height === 0) continue;
413
+ const overlay = document.createElement("div");
414
+ overlay.style.position = "fixed";
415
+ overlay.style.border = `2px solid ${baseColor}`;
416
+ overlay.style.backgroundColor = backgroundColor;
417
+ overlay.style.pointerEvents = "none";
418
+ overlay.style.boxSizing = "border-box";
419
+ const top = rect.top + iframeOffset.y;
420
+ const left = rect.left + iframeOffset.x;
421
+ overlay.style.top = `${top}px`;
422
+ overlay.style.left = `${left}px`;
423
+ overlay.style.width = `${rect.width}px`;
424
+ overlay.style.height = `${rect.height}px`;
425
+ fragment.appendChild(overlay);
426
+ overlays.push({ element: overlay, initialRect: rect });
427
+ }
428
+ const firstRect = rects[0];
429
+ label = document.createElement("div");
430
+ label.className = "playwright-highlight-label";
431
+ label.style.position = "fixed";
432
+ label.style.background = baseColor;
433
+ label.style.color = "white";
434
+ label.style.padding = "1px 4px";
435
+ label.style.borderRadius = "4px";
436
+ label.style.fontSize = `${Math.min(12, Math.max(8, firstRect.height / 2))}px`;
437
+ label.textContent = index.toString();
438
+ labelWidth = label.offsetWidth > 0 ? label.offsetWidth : labelWidth;
439
+ labelHeight = label.offsetHeight > 0 ? label.offsetHeight : labelHeight;
440
+ const firstRectTop = firstRect.top + iframeOffset.y;
441
+ const firstRectLeft = firstRect.left + iframeOffset.x;
442
+ let labelTop = firstRectTop + 2;
443
+ let labelLeft = firstRectLeft + firstRect.width - labelWidth - 2;
444
+ if (firstRect.width < labelWidth + 4 || firstRect.height < labelHeight + 4) {
445
+ labelTop = firstRectTop - labelHeight - 2;
446
+ labelLeft = firstRectLeft + firstRect.width - labelWidth;
447
+ if (labelLeft < iframeOffset.x) labelLeft = firstRectLeft;
448
+ }
449
+ labelTop = Math.max(0, Math.min(labelTop, window.innerHeight - labelHeight));
450
+ labelLeft = Math.max(0, Math.min(labelLeft, window.innerWidth - labelWidth));
451
+ label.style.top = `${labelTop}px`;
452
+ label.style.left = `${labelLeft}px`;
453
+ fragment.appendChild(label);
454
+ const updatePositions = /* @__PURE__ */ __name(() => {
455
+ const newRects = element.getClientRects();
456
+ let newIframeOffset = { x: 0, y: 0 };
457
+ if (parentIframe) {
458
+ const iframeRect = parentIframe.getBoundingClientRect();
459
+ newIframeOffset.x = iframeRect.left;
460
+ newIframeOffset.y = iframeRect.top;
461
+ }
462
+ overlays.forEach((overlayData, i) => {
463
+ if (i < newRects.length) {
464
+ const newRect = newRects[i];
465
+ const newTop = newRect.top + newIframeOffset.y;
466
+ const newLeft = newRect.left + newIframeOffset.x;
467
+ overlayData.element.style.top = `${newTop}px`;
468
+ overlayData.element.style.left = `${newLeft}px`;
469
+ overlayData.element.style.width = `${newRect.width}px`;
470
+ overlayData.element.style.height = `${newRect.height}px`;
471
+ overlayData.element.style.display = newRect.width === 0 || newRect.height === 0 ? "none" : "block";
472
+ } else {
473
+ overlayData.element.style.display = "none";
474
+ }
475
+ });
476
+ if (newRects.length < overlays.length) {
477
+ for (let i = newRects.length; i < overlays.length; i++) {
478
+ overlays[i].element.style.display = "none";
479
+ }
480
+ }
481
+ if (label && newRects.length > 0) {
482
+ const firstNewRect = newRects[0];
483
+ const firstNewRectTop = firstNewRect.top + newIframeOffset.y;
484
+ const firstNewRectLeft = firstNewRect.left + newIframeOffset.x;
485
+ let newLabelTop = firstNewRectTop + 2;
486
+ let newLabelLeft = firstNewRectLeft + firstNewRect.width - labelWidth - 2;
487
+ if (firstNewRect.width < labelWidth + 4 || firstNewRect.height < labelHeight + 4) {
488
+ newLabelTop = firstNewRectTop - labelHeight - 2;
489
+ newLabelLeft = firstNewRectLeft + firstNewRect.width - labelWidth;
490
+ if (newLabelLeft < newIframeOffset.x) newLabelLeft = firstNewRectLeft;
491
+ }
492
+ newLabelTop = Math.max(0, Math.min(newLabelTop, window.innerHeight - labelHeight));
493
+ newLabelLeft = Math.max(0, Math.min(newLabelLeft, window.innerWidth - labelWidth));
494
+ label.style.top = `${newLabelTop}px`;
495
+ label.style.left = `${newLabelLeft}px`;
496
+ label.style.display = "block";
497
+ } else if (label) {
498
+ label.style.display = "none";
499
+ }
500
+ }, "updatePositions");
501
+ const throttleFunction = /* @__PURE__ */ __name((func, delay) => {
502
+ let lastCall = 0;
503
+ return (...args2) => {
504
+ const now = performance.now();
505
+ if (now - lastCall < delay) return;
506
+ lastCall = now;
507
+ return func(...args2);
508
+ };
509
+ }, "throttleFunction");
510
+ const throttledUpdatePositions = throttleFunction(updatePositions, 16);
511
+ window.addEventListener("scroll", throttledUpdatePositions, true);
512
+ window.addEventListener("resize", throttledUpdatePositions);
513
+ cleanupFn = /* @__PURE__ */ __name(() => {
514
+ window.removeEventListener("scroll", throttledUpdatePositions, true);
515
+ window.removeEventListener("resize", throttledUpdatePositions);
516
+ overlays.forEach((overlay) => overlay.element.remove());
517
+ if (label) label.remove();
518
+ }, "cleanupFn");
519
+ container.appendChild(fragment);
520
+ return index + 1;
521
+ } finally {
522
+ if (cleanupFn) {
523
+ (window._highlightCleanupFunctions = window._highlightCleanupFunctions || []).push(
524
+ cleanupFn
525
+ );
526
+ }
527
+ }
528
+ }
529
+ __name(highlightElement, "highlightElement");
530
+ function isScrollableElement(element) {
531
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) {
532
+ return null;
533
+ }
534
+ const style = getCachedComputedStyle(element);
535
+ if (!style) return null;
536
+ const display = style.display;
537
+ if (display === "inline" || display === "inline-block") {
538
+ return null;
539
+ }
540
+ const overflowX = style.overflowX;
541
+ const overflowY = style.overflowY;
542
+ const scrollableX = overflowX === "auto" || overflowX === "scroll";
543
+ const scrollableY = overflowY === "auto" || overflowY === "scroll";
544
+ if (!scrollableX && !scrollableY) {
545
+ return null;
546
+ }
547
+ const scrollWidth = element.scrollWidth - element.clientWidth;
548
+ const scrollHeight = element.scrollHeight - element.clientHeight;
549
+ const threshold = 4;
550
+ if (scrollWidth < threshold && scrollHeight < threshold) {
551
+ return null;
552
+ }
553
+ if (!scrollableY && scrollWidth < threshold) {
554
+ return null;
555
+ }
556
+ if (!scrollableX && scrollHeight < threshold) {
557
+ return null;
558
+ }
559
+ const distanceToTop = element.scrollTop;
560
+ const distanceToLeft = element.scrollLeft;
561
+ const distanceToRight = element.scrollWidth - element.clientWidth - element.scrollLeft;
562
+ const distanceToBottom = element.scrollHeight - element.clientHeight - element.scrollTop;
563
+ const scrollData = {
564
+ top: distanceToTop,
565
+ right: distanceToRight,
566
+ bottom: distanceToBottom,
567
+ left: distanceToLeft
568
+ };
569
+ addExtraData(element, {
570
+ scrollable: true,
571
+ scrollData
572
+ });
573
+ return scrollData;
574
+ }
575
+ __name(isScrollableElement, "isScrollableElement");
576
+ function isTextNodeVisible(textNode) {
577
+ try {
578
+ if (viewportExpansion === -1) {
579
+ const parentElement2 = textNode.parentElement;
580
+ if (!parentElement2) return false;
581
+ try {
582
+ return parentElement2.checkVisibility({
583
+ checkOpacity: true,
584
+ checkVisibilityCSS: true
585
+ });
586
+ } catch (e) {
587
+ const style = window.getComputedStyle(parentElement2);
588
+ return style.display !== "none" && style.visibility !== "hidden" && style.opacity !== "0";
589
+ }
590
+ }
591
+ const range = document.createRange();
592
+ range.selectNodeContents(textNode);
593
+ const rects = range.getClientRects();
594
+ if (!rects || rects.length === 0) {
595
+ return false;
596
+ }
597
+ let isAnyRectVisible = false;
598
+ let isAnyRectInViewport = false;
599
+ for (const rect of rects) {
600
+ if (rect.width > 0 && rect.height > 0) {
601
+ isAnyRectVisible = true;
602
+ if (!(rect.bottom < -viewportExpansion || rect.top > window.innerHeight + viewportExpansion || rect.right < -viewportExpansion || rect.left > window.innerWidth + viewportExpansion)) {
603
+ isAnyRectInViewport = true;
604
+ break;
605
+ }
606
+ }
607
+ }
608
+ if (!isAnyRectVisible || !isAnyRectInViewport) {
609
+ return false;
610
+ }
611
+ const parentElement = textNode.parentElement;
612
+ if (!parentElement) return false;
613
+ try {
614
+ return parentElement.checkVisibility({
615
+ checkOpacity: true,
616
+ checkVisibilityCSS: true
617
+ });
618
+ } catch (e) {
619
+ const style = window.getComputedStyle(parentElement);
620
+ return style.display !== "none" && style.visibility !== "hidden" && style.opacity !== "0";
621
+ }
622
+ } catch (e) {
623
+ console.warn("Error checking text node visibility:", e);
624
+ return false;
625
+ }
626
+ }
627
+ __name(isTextNodeVisible, "isTextNodeVisible");
628
+ function isElementAccepted(element) {
629
+ if (!element || !element.tagName) return false;
630
+ const alwaysAccept = /* @__PURE__ */ new Set([
631
+ "body",
632
+ "div",
633
+ "main",
634
+ "article",
635
+ "section",
636
+ "nav",
637
+ "header",
638
+ "footer"
639
+ ]);
640
+ const tagName = element.tagName.toLowerCase();
641
+ if (alwaysAccept.has(tagName)) return true;
642
+ const leafElementDenyList = /* @__PURE__ */ new Set([
643
+ "svg",
644
+ "script",
645
+ "style",
646
+ "link",
647
+ "meta",
648
+ "noscript",
649
+ "template"
650
+ ]);
651
+ return !leafElementDenyList.has(tagName);
652
+ }
653
+ __name(isElementAccepted, "isElementAccepted");
654
+ function isElementVisible(element) {
655
+ const style = getCachedComputedStyle(element);
656
+ return element.offsetWidth > 0 && element.offsetHeight > 0 && style?.visibility !== "hidden" && style?.display !== "none";
657
+ }
658
+ __name(isElementVisible, "isElementVisible");
659
+ function isInteractiveElement(element) {
660
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) {
661
+ return false;
662
+ }
663
+ if (interactiveBlacklist.includes(element)) {
664
+ return false;
665
+ }
666
+ if (interactiveWhitelist.includes(element)) {
667
+ return true;
668
+ }
669
+ const tagName = element.tagName.toLowerCase();
670
+ const style = getCachedComputedStyle(element);
671
+ const interactiveCursors = /* @__PURE__ */ new Set([
672
+ "pointer",
673
+ // Link/clickable elements
674
+ "move",
675
+ // Movable elements
676
+ "text",
677
+ // Text selection
678
+ "grab",
679
+ // Grabbable elements
680
+ "grabbing",
681
+ // Currently grabbing
682
+ "cell",
683
+ // Table cell selection
684
+ "copy",
685
+ // Copy operation
686
+ "alias",
687
+ // Alias creation
688
+ "all-scroll",
689
+ // Scrollable content
690
+ "col-resize",
691
+ // Column resize
692
+ "context-menu",
693
+ // Context menu available
694
+ "crosshair",
695
+ // Precise selection
696
+ "e-resize",
697
+ // East resize
698
+ "ew-resize",
699
+ // East-west resize
700
+ "help",
701
+ // Help available
702
+ "n-resize",
703
+ // North resize
704
+ "ne-resize",
705
+ // Northeast resize
706
+ "nesw-resize",
707
+ // Northeast-southwest resize
708
+ "ns-resize",
709
+ // North-south resize
710
+ "nw-resize",
711
+ // Northwest resize
712
+ "nwse-resize",
713
+ // Northwest-southeast resize
714
+ "row-resize",
715
+ // Row resize
716
+ "s-resize",
717
+ // South resize
718
+ "se-resize",
719
+ // Southeast resize
720
+ "sw-resize",
721
+ // Southwest resize
722
+ "vertical-text",
723
+ // Vertical text selection
724
+ "w-resize",
725
+ // West resize
726
+ "zoom-in",
727
+ // Zoom in
728
+ "zoom-out"
729
+ // Zoom out
730
+ ]);
731
+ const nonInteractiveCursors = /* @__PURE__ */ new Set([
732
+ "not-allowed",
733
+ // Action not allowed
734
+ "no-drop",
735
+ // Drop not allowed
736
+ "wait",
737
+ // Processing
738
+ "progress",
739
+ // In progress
740
+ "initial",
741
+ // Initial value
742
+ "inherit"
743
+ // Inherited value
744
+ //? Let's just include all potentially clickable elements that are not specifically blocked
745
+ // 'none', // No cursor
746
+ // 'default', // Default cursor
747
+ // 'auto', // Browser default
748
+ ]);
749
+ function doesElementHaveInteractivePointer(element2) {
750
+ if (element2.tagName.toLowerCase() === "html") return false;
751
+ if (style?.cursor && interactiveCursors.has(style.cursor)) return true;
752
+ return false;
753
+ }
754
+ __name(doesElementHaveInteractivePointer, "doesElementHaveInteractivePointer");
755
+ let isInteractiveCursor = doesElementHaveInteractivePointer(element);
756
+ if (isInteractiveCursor) {
757
+ return true;
758
+ }
759
+ const interactiveElements = /* @__PURE__ */ new Set([
760
+ "a",
761
+ // Links
762
+ "button",
763
+ // Buttons
764
+ "input",
765
+ // All input types (text, checkbox, radio, etc.)
766
+ "select",
767
+ // Dropdown menus
768
+ "textarea",
769
+ // Text areas
770
+ "details",
771
+ // Expandable details
772
+ "summary",
773
+ // Summary element (clickable part of details)
774
+ "label",
775
+ // Form labels (often clickable)
776
+ "option",
777
+ // Select options
778
+ "optgroup",
779
+ // Option groups
780
+ "fieldset",
781
+ // Form fieldsets (can be interactive with legend)
782
+ "legend"
783
+ // Fieldset legends
784
+ ]);
785
+ const explicitDisableTags = /* @__PURE__ */ new Set([
786
+ "disabled",
787
+ // Standard disabled attribute
788
+ // 'aria-disabled', // ARIA disabled state
789
+ "readonly"
790
+ // Read-only state
791
+ // 'aria-readonly', // ARIA read-only state
792
+ // 'aria-hidden', // Hidden from accessibility
793
+ // 'hidden', // Hidden attribute
794
+ // 'inert', // Inert attribute
795
+ // 'aria-inert', // ARIA inert state
796
+ // 'tabindex="-1"', // Removed from tab order
797
+ // 'aria-hidden="true"' // Hidden from screen readers
798
+ ]);
799
+ if (interactiveElements.has(tagName)) {
800
+ if (style?.cursor && nonInteractiveCursors.has(style.cursor)) {
801
+ return false;
802
+ }
803
+ for (const disableTag of explicitDisableTags) {
804
+ if (element.hasAttribute(disableTag) || element.getAttribute(disableTag) === "true" || element.getAttribute(disableTag) === "") {
805
+ return false;
806
+ }
807
+ }
808
+ if (element.disabled) {
809
+ return false;
810
+ }
811
+ if (element.readOnly) {
812
+ return false;
813
+ }
814
+ if (element.inert) {
815
+ return false;
816
+ }
817
+ return true;
818
+ }
819
+ const role = element.getAttribute("role");
820
+ const ariaRole = element.getAttribute("aria-role");
821
+ if (element.getAttribute("contenteditable") === "true" || element.isContentEditable) {
822
+ return true;
823
+ }
824
+ if (element.classList && (element.classList.contains("button") || element.classList.contains("dropdown-toggle") || element.getAttribute("data-index") || element.getAttribute("data-toggle") === "dropdown" || element.getAttribute("aria-haspopup") === "true")) {
825
+ return true;
826
+ }
827
+ const interactiveRoles = /* @__PURE__ */ new Set([
828
+ "button",
829
+ // Directly clickable element
830
+ // 'link', // Clickable link
831
+ "menu",
832
+ // Menu container (ARIA menus)
833
+ "menubar",
834
+ // Menu bar container
835
+ "menuitem",
836
+ // Clickable menu item
837
+ "menuitemradio",
838
+ // Radio-style menu item (selectable)
839
+ "menuitemcheckbox",
840
+ // Checkbox-style menu item (toggleable)
841
+ "radio",
842
+ // Radio button (selectable)
843
+ "checkbox",
844
+ // Checkbox (toggleable)
845
+ "tab",
846
+ // Tab (clickable to switch content)
847
+ "switch",
848
+ // Toggle switch (clickable to change state)
849
+ "slider",
850
+ // Slider control (draggable)
851
+ "spinbutton",
852
+ // Number input with up/down controls
853
+ "combobox",
854
+ // Dropdown with text input
855
+ "searchbox",
856
+ // Search input field
857
+ "textbox",
858
+ // Text input field
859
+ "listbox",
860
+ // Selectable list
861
+ "option",
862
+ // Selectable option in a list
863
+ "scrollbar"
864
+ // Scrollable control
865
+ ]);
866
+ const hasInteractiveRole = interactiveElements.has(tagName) || role && interactiveRoles.has(role) || ariaRole && interactiveRoles.has(ariaRole);
867
+ if (hasInteractiveRole) return true;
868
+ try {
869
+ if (typeof getEventListeners === "function") {
870
+ const listeners = getEventListeners(element);
871
+ const mouseEvents = ["click", "mousedown", "mouseup", "dblclick"];
872
+ for (const eventType of mouseEvents) {
873
+ if (listeners[eventType] && listeners[eventType].length > 0) {
874
+ return true;
875
+ }
876
+ }
877
+ }
878
+ const getEventListenersForNode = element?.ownerDocument?.defaultView?.getEventListenersForNode || window.getEventListenersForNode;
879
+ if (typeof getEventListenersForNode === "function") {
880
+ const listeners = getEventListenersForNode(element);
881
+ const interactionEvents = [
882
+ "click",
883
+ "mousedown",
884
+ "mouseup",
885
+ "keydown",
886
+ "keyup",
887
+ "submit",
888
+ "change",
889
+ "input",
890
+ "focus",
891
+ "blur"
892
+ ];
893
+ for (const eventType of interactionEvents) {
894
+ for (const listener of listeners) {
895
+ if (listener.type === eventType) {
896
+ return true;
897
+ }
898
+ }
899
+ }
900
+ }
901
+ const commonMouseAttrs = ["onclick", "onmousedown", "onmouseup", "ondblclick"];
902
+ for (const attr of commonMouseAttrs) {
903
+ if (element.hasAttribute(attr) || typeof element[attr] === "function") {
904
+ return true;
905
+ }
906
+ }
907
+ } catch (e) {
908
+ }
909
+ if (isScrollableElement(element)) {
910
+ return true;
911
+ }
912
+ return false;
913
+ }
914
+ __name(isInteractiveElement, "isInteractiveElement");
915
+ function isTopElement(element) {
916
+ if (viewportExpansion === -1) {
917
+ return true;
918
+ }
919
+ const rects = getCachedClientRects(element);
920
+ if (!rects || rects.length === 0) {
921
+ return false;
922
+ }
923
+ let isAnyRectInViewport = false;
924
+ for (const rect2 of rects) {
925
+ if (rect2.width > 0 && rect2.height > 0 && !// Only check non-empty rects
926
+ (rect2.bottom < -viewportExpansion || rect2.top > window.innerHeight + viewportExpansion || rect2.right < -viewportExpansion || rect2.left > window.innerWidth + viewportExpansion)) {
927
+ isAnyRectInViewport = true;
928
+ break;
929
+ }
930
+ }
931
+ if (!isAnyRectInViewport) {
932
+ return false;
933
+ }
934
+ let doc = element.ownerDocument;
935
+ if (doc !== window.document) {
936
+ return true;
937
+ }
938
+ let rect = Array.from(rects).find((r) => r.width > 0 && r.height > 0);
939
+ if (!rect) {
940
+ return false;
941
+ }
942
+ const shadowRoot = element.getRootNode();
943
+ if (shadowRoot instanceof ShadowRoot) {
944
+ const centerX = rect.left + rect.width / 2;
945
+ const centerY = rect.top + rect.height / 2;
946
+ try {
947
+ const topEl = shadowRoot.elementFromPoint(centerX, centerY);
948
+ if (!topEl) return false;
949
+ let current = topEl;
950
+ while (current && current !== shadowRoot) {
951
+ if (current === element) return true;
952
+ current = current.parentElement;
953
+ }
954
+ return false;
955
+ } catch (e) {
956
+ return true;
957
+ }
958
+ }
959
+ const margin = 5;
960
+ const checkPoints = [
961
+ // Initially only this was used, but it was not enough
962
+ { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 },
963
+ { x: rect.left + margin, y: rect.top + margin },
964
+ // top left
965
+ // { x: rect.right - margin, y: rect.top + margin }, // top right
966
+ // { x: rect.left + margin, y: rect.bottom - margin }, // bottom left
967
+ { x: rect.right - margin, y: rect.bottom - margin }
968
+ // bottom right
969
+ ];
970
+ return checkPoints.some(({ x, y }) => {
971
+ try {
972
+ const topEl = document.elementFromPoint(x, y);
973
+ if (!topEl) return false;
974
+ let current = topEl;
975
+ while (current && current !== document.documentElement) {
976
+ if (current === element) return true;
977
+ current = current.parentElement;
978
+ }
979
+ return false;
980
+ } catch (e) {
981
+ return true;
982
+ }
983
+ });
984
+ }
985
+ __name(isTopElement, "isTopElement");
986
+ function isInExpandedViewport(element, viewportExpansion2) {
987
+ if (viewportExpansion2 === -1) {
988
+ return true;
989
+ }
990
+ const rects = element.getClientRects();
991
+ if (!rects || rects.length === 0) {
992
+ const boundingRect = getCachedBoundingRect(element);
993
+ if (!boundingRect || boundingRect.width === 0 || boundingRect.height === 0) {
994
+ return false;
995
+ }
996
+ return !(boundingRect.bottom < -viewportExpansion2 || boundingRect.top > window.innerHeight + viewportExpansion2 || boundingRect.right < -viewportExpansion2 || boundingRect.left > window.innerWidth + viewportExpansion2);
997
+ }
998
+ for (const rect of rects) {
999
+ if (rect.width === 0 || rect.height === 0) continue;
1000
+ if (!(rect.bottom < -viewportExpansion2 || rect.top > window.innerHeight + viewportExpansion2 || rect.right < -viewportExpansion2 || rect.left > window.innerWidth + viewportExpansion2)) {
1001
+ return true;
1002
+ }
1003
+ }
1004
+ return false;
1005
+ }
1006
+ __name(isInExpandedViewport, "isInExpandedViewport");
1007
+ function isInteractiveCandidate(element) {
1008
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) return false;
1009
+ const tagName = element.tagName.toLowerCase();
1010
+ const interactiveElements = /* @__PURE__ */ new Set([
1011
+ "a",
1012
+ "button",
1013
+ "input",
1014
+ "select",
1015
+ "textarea",
1016
+ "details",
1017
+ "summary",
1018
+ "label"
1019
+ ]);
1020
+ if (interactiveElements.has(tagName)) return true;
1021
+ const hasQuickInteractiveAttr = element.hasAttribute("onclick") || element.hasAttribute("role") || element.hasAttribute("tabindex") || element.hasAttribute("aria-") || element.hasAttribute("data-action") || element.getAttribute("contenteditable") === "true";
1022
+ return hasQuickInteractiveAttr;
1023
+ }
1024
+ __name(isInteractiveCandidate, "isInteractiveCandidate");
1025
+ const DISTINCT_INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
1026
+ "a",
1027
+ "button",
1028
+ "input",
1029
+ "select",
1030
+ "textarea",
1031
+ "summary",
1032
+ "details",
1033
+ "label",
1034
+ "option"
1035
+ ]);
1036
+ const INTERACTIVE_ROLES = /* @__PURE__ */ new Set([
1037
+ "button",
1038
+ "link",
1039
+ "menuitem",
1040
+ "menuitemradio",
1041
+ "menuitemcheckbox",
1042
+ "radio",
1043
+ "checkbox",
1044
+ "tab",
1045
+ "switch",
1046
+ "slider",
1047
+ "spinbutton",
1048
+ "combobox",
1049
+ "searchbox",
1050
+ "textbox",
1051
+ "listbox",
1052
+ "option",
1053
+ "scrollbar"
1054
+ ]);
1055
+ function isHeuristicallyInteractive(element) {
1056
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) return false;
1057
+ if (!isElementVisible(element)) return false;
1058
+ const hasInteractiveAttributes = element.hasAttribute("role") || element.hasAttribute("tabindex") || element.hasAttribute("onclick") || typeof element.onclick === "function";
1059
+ const hasInteractiveClass = /\b(btn|clickable|menu|item|entry|link)\b/i.test(
1060
+ element.className || ""
1061
+ );
1062
+ const isInKnownContainer = Boolean(
1063
+ element.closest('button,a,[role="button"],.menu,.dropdown,.list,.toolbar')
1064
+ );
1065
+ const hasVisibleChildren = [...element.children].some(isElementVisible);
1066
+ const isParentBody = element.parentElement && element.parentElement.isSameNode(document.body);
1067
+ return (isInteractiveElement(element) || hasInteractiveAttributes || hasInteractiveClass) && hasVisibleChildren && isInKnownContainer && !isParentBody;
1068
+ }
1069
+ __name(isHeuristicallyInteractive, "isHeuristicallyInteractive");
1070
+ function isElementDistinctInteraction(element) {
1071
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) {
1072
+ return false;
1073
+ }
1074
+ const tagName = element.tagName.toLowerCase();
1075
+ const role = element.getAttribute("role");
1076
+ if (tagName === "iframe") {
1077
+ return true;
1078
+ }
1079
+ if (DISTINCT_INTERACTIVE_TAGS.has(tagName)) {
1080
+ return true;
1081
+ }
1082
+ if (role && INTERACTIVE_ROLES.has(role)) {
1083
+ return true;
1084
+ }
1085
+ if (element.isContentEditable || element.getAttribute("contenteditable") === "true") {
1086
+ return true;
1087
+ }
1088
+ if (element.hasAttribute("data-testid") || element.hasAttribute("data-cy") || element.hasAttribute("data-test")) {
1089
+ return true;
1090
+ }
1091
+ if (element.hasAttribute("onclick") || typeof element.onclick === "function") {
1092
+ return true;
1093
+ }
1094
+ try {
1095
+ const getEventListenersForNode = element?.ownerDocument?.defaultView?.getEventListenersForNode || window.getEventListenersForNode;
1096
+ if (typeof getEventListenersForNode === "function") {
1097
+ const listeners = getEventListenersForNode(element);
1098
+ const interactionEvents = [
1099
+ "click",
1100
+ "mousedown",
1101
+ "mouseup",
1102
+ "keydown",
1103
+ "keyup",
1104
+ "submit",
1105
+ "change",
1106
+ "input",
1107
+ "focus",
1108
+ "blur"
1109
+ ];
1110
+ for (const eventType of interactionEvents) {
1111
+ for (const listener of listeners) {
1112
+ if (listener.type === eventType) {
1113
+ return true;
1114
+ }
1115
+ }
1116
+ }
1117
+ }
1118
+ const commonEventAttrs = [
1119
+ "onmousedown",
1120
+ "onmouseup",
1121
+ "onkeydown",
1122
+ "onkeyup",
1123
+ "onsubmit",
1124
+ "onchange",
1125
+ "oninput",
1126
+ "onfocus",
1127
+ "onblur"
1128
+ ];
1129
+ if (commonEventAttrs.some((attr) => element.hasAttribute(attr))) {
1130
+ return true;
1131
+ }
1132
+ } catch (e) {
1133
+ }
1134
+ if (isHeuristicallyInteractive(element)) {
1135
+ return true;
1136
+ }
1137
+ return false;
1138
+ }
1139
+ __name(isElementDistinctInteraction, "isElementDistinctInteraction");
1140
+ function handleHighlighting(nodeData, node, parentIframe, isParentHighlighted) {
1141
+ if (!nodeData.isInteractive) return false;
1142
+ let shouldHighlight = false;
1143
+ if (!isParentHighlighted) {
1144
+ shouldHighlight = true;
1145
+ } else {
1146
+ if (isElementDistinctInteraction(node)) {
1147
+ shouldHighlight = true;
1148
+ } else {
1149
+ shouldHighlight = false;
1150
+ }
1151
+ }
1152
+ if (shouldHighlight) {
1153
+ nodeData.isInViewport = isInExpandedViewport(node, viewportExpansion);
1154
+ if (nodeData.isInViewport || viewportExpansion === -1) {
1155
+ nodeData.highlightIndex = highlightIndex++;
1156
+ if (doHighlightElements) {
1157
+ if (focusHighlightIndex >= 0) {
1158
+ if (focusHighlightIndex === nodeData.highlightIndex) {
1159
+ highlightElement(node, nodeData.highlightIndex, parentIframe);
1160
+ }
1161
+ } else {
1162
+ highlightElement(node, nodeData.highlightIndex, parentIframe);
1163
+ }
1164
+ return true;
1165
+ }
1166
+ }
1167
+ }
1168
+ return false;
1169
+ }
1170
+ __name(handleHighlighting, "handleHighlighting");
1171
+ function buildDomTree(node, parentIframe = null, isParentHighlighted = false) {
1172
+ if (!node || node.id === HIGHLIGHT_CONTAINER_ID || node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE) {
1173
+ return null;
1174
+ }
1175
+ if (!node || node.id === HIGHLIGHT_CONTAINER_ID) {
1176
+ return null;
1177
+ }
1178
+ if (node.dataset?.browserUseIgnore === "true") {
1179
+ return true;
1180
+ }
1181
+ if (node === document.body) {
1182
+ const nodeData2 = {
1183
+ tagName: "body",
1184
+ attributes: {},
1185
+ xpath: "/body",
1186
+ children: []
1187
+ };
1188
+ for (const child of node.childNodes) {
1189
+ const domElement = buildDomTree(child, parentIframe, false);
1190
+ if (domElement) nodeData2.children.push(domElement);
1191
+ }
1192
+ const id2 = `${ID.current++}`;
1193
+ DOM_HASH_MAP[id2] = nodeData2;
1194
+ return id2;
1195
+ }
1196
+ if (node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE) {
1197
+ return null;
1198
+ }
1199
+ if (node.nodeType === Node.TEXT_NODE) {
1200
+ const textContent = node.textContent?.trim();
1201
+ if (!textContent) {
1202
+ return null;
1203
+ }
1204
+ const parentElement = node.parentElement;
1205
+ if (!parentElement || parentElement.tagName.toLowerCase() === "script") {
1206
+ return null;
1207
+ }
1208
+ const id2 = `${ID.current++}`;
1209
+ DOM_HASH_MAP[id2] = {
1210
+ type: "TEXT_NODE",
1211
+ text: textContent,
1212
+ isVisible: isTextNodeVisible(node)
1213
+ };
1214
+ return id2;
1215
+ }
1216
+ if (node.nodeType === Node.ELEMENT_NODE && !isElementAccepted(node)) {
1217
+ return null;
1218
+ }
1219
+ if (viewportExpansion !== -1 && !node.shadowRoot) {
1220
+ const rect = getCachedBoundingRect(node);
1221
+ const style = getCachedComputedStyle(node);
1222
+ const isFixedOrSticky = style && (style.position === "fixed" || style.position === "sticky");
1223
+ const hasSize = node.offsetWidth > 0 || node.offsetHeight > 0;
1224
+ if (!rect || !isFixedOrSticky && !hasSize && (rect.bottom < -viewportExpansion || rect.top > window.innerHeight + viewportExpansion || rect.right < -viewportExpansion || rect.left > window.innerWidth + viewportExpansion)) {
1225
+ return null;
1226
+ }
1227
+ }
1228
+ const nodeData = {
1229
+ tagName: node.tagName.toLowerCase(),
1230
+ attributes: {},
1231
+ /**
1232
+ * @edit no need for xpath
1233
+ */
1234
+ // xpath: getXPathTree(node, true),
1235
+ children: []
1236
+ };
1237
+ if (isInteractiveCandidate(node) || node.tagName.toLowerCase() === "iframe" || node.tagName.toLowerCase() === "body") {
1238
+ const attributeNames = node.getAttributeNames?.() || [];
1239
+ for (const name of attributeNames) {
1240
+ const value = node.getAttribute(name);
1241
+ nodeData.attributes[name] = value;
1242
+ }
1243
+ if (node.tagName.toLowerCase() === "input" && (node.type === "checkbox" || node.type === "radio")) {
1244
+ nodeData.attributes.checked = node.checked ? "true" : "false";
1245
+ }
1246
+ }
1247
+ let nodeWasHighlighted = false;
1248
+ if (node.nodeType === Node.ELEMENT_NODE) {
1249
+ nodeData.isVisible = isElementVisible(node);
1250
+ if (nodeData.isVisible) {
1251
+ nodeData.isTopElement = isTopElement(node);
1252
+ const role = node.getAttribute("role");
1253
+ const isMenuContainer = role === "menu" || role === "menubar" || role === "listbox";
1254
+ if (nodeData.isTopElement || isMenuContainer) {
1255
+ nodeData.isInteractive = isInteractiveElement(node);
1256
+ nodeWasHighlighted = handleHighlighting(nodeData, node, parentIframe, isParentHighlighted);
1257
+ nodeData.ref = node;
1258
+ }
1259
+ }
1260
+ }
1261
+ if (node.tagName) {
1262
+ const tagName = node.tagName.toLowerCase();
1263
+ if (tagName === "iframe") {
1264
+ try {
1265
+ const iframeDoc = node.contentDocument || node.contentWindow?.document;
1266
+ if (iframeDoc) {
1267
+ for (const child of iframeDoc.childNodes) {
1268
+ const domElement = buildDomTree(child, node, false);
1269
+ if (domElement) nodeData.children.push(domElement);
1270
+ }
1271
+ }
1272
+ } catch (e) {
1273
+ console.warn("Unable to access iframe:", e);
1274
+ }
1275
+ } else if (node.isContentEditable || node.getAttribute("contenteditable") === "true" || node.id === "tinymce" || node.classList.contains("mce-content-body") || tagName === "body" && node.getAttribute("data-id")?.startsWith("mce_")) {
1276
+ for (const child of node.childNodes) {
1277
+ const domElement = buildDomTree(child, parentIframe, nodeWasHighlighted);
1278
+ if (domElement) nodeData.children.push(domElement);
1279
+ }
1280
+ } else {
1281
+ if (node.shadowRoot) {
1282
+ nodeData.shadowRoot = true;
1283
+ for (const child of node.shadowRoot.childNodes) {
1284
+ const domElement = buildDomTree(child, parentIframe, nodeWasHighlighted);
1285
+ if (domElement) nodeData.children.push(domElement);
1286
+ }
1287
+ }
1288
+ for (const child of node.childNodes) {
1289
+ const passHighlightStatusToChild = nodeWasHighlighted || isParentHighlighted;
1290
+ const domElement = buildDomTree(child, parentIframe, passHighlightStatusToChild);
1291
+ if (domElement) nodeData.children.push(domElement);
1292
+ }
1293
+ }
1294
+ }
1295
+ if (nodeData.tagName === "a" && nodeData.children.length === 0 && !nodeData.attributes.href) {
1296
+ const rect = getCachedBoundingRect(node);
1297
+ const hasSize = rect && rect.width > 0 && rect.height > 0 || node.offsetWidth > 0 || node.offsetHeight > 0;
1298
+ if (!hasSize) {
1299
+ return null;
1300
+ }
1301
+ }
1302
+ nodeData.extra = extraData.get(node) || null;
1303
+ const id = `${ID.current++}`;
1304
+ DOM_HASH_MAP[id] = nodeData;
1305
+ return id;
1306
+ }
1307
+ __name(buildDomTree, "buildDomTree");
1308
+ const rootId = buildDomTree(document.body);
1309
+ DOM_CACHE.clearCache();
1310
+ return { rootId, map: DOM_HASH_MAP };
1311
+ }, "domTree");
1312
+ const newElementsCache = /* @__PURE__ */ new WeakMap();
1313
+ function getFlatTree(config) {
1314
+ const interactiveBlacklist = [];
1315
+ for (const item of config.interactiveBlacklist || []) {
1316
+ if (typeof item === "function") {
1317
+ interactiveBlacklist.push(item());
1318
+ } else {
1319
+ interactiveBlacklist.push(item);
1320
+ }
1321
+ }
1322
+ const interactiveWhitelist = [];
1323
+ for (const item of config.interactiveWhitelist || []) {
1324
+ if (typeof item === "function") {
1325
+ interactiveWhitelist.push(item());
1326
+ } else {
1327
+ interactiveWhitelist.push(item);
1328
+ }
1329
+ }
1330
+ const elements = domTree({
1331
+ doHighlightElements: true,
1332
+ debugMode: true,
1333
+ focusHighlightIndex: -1,
1334
+ viewportExpansion: VIEWPORT_EXPANSION,
1335
+ interactiveBlacklist,
1336
+ interactiveWhitelist,
1337
+ highlightOpacity: config.highlightOpacity ?? 0,
1338
+ highlightLabelOpacity: config.highlightLabelOpacity ?? 0.1
1339
+ });
1340
+ const currentUrl = window.location.href;
1341
+ for (const nodeId in elements.map) {
1342
+ const node = elements.map[nodeId];
1343
+ if (node.isInteractive && node.ref) {
1344
+ const ref = node.ref;
1345
+ if (!newElementsCache.has(ref)) {
1346
+ newElementsCache.set(ref, currentUrl);
1347
+ node.isNew = true;
1348
+ }
1349
+ }
1350
+ }
1351
+ return elements;
1352
+ }
1353
+ __name(getFlatTree, "getFlatTree");
1354
+ function flatTreeToString(flatTree, include_attributes) {
1355
+ const DEFAULT_INCLUDE_ATTRIBUTES = [
1356
+ "title",
1357
+ "type",
1358
+ "checked",
1359
+ "name",
1360
+ "role",
1361
+ "value",
1362
+ "placeholder",
1363
+ "data-date-format",
1364
+ "alt",
1365
+ "aria-label",
1366
+ "aria-expanded",
1367
+ "data-state",
1368
+ "aria-checked",
1369
+ // @edit added for better form handling
1370
+ "id",
1371
+ "for",
1372
+ // for jump check
1373
+ "target",
1374
+ // absolute 定位的下拉菜单
1375
+ "aria-haspopup",
1376
+ "aria-controls",
1377
+ "aria-owns"
1378
+ ];
1379
+ const includeAttrs = [...include_attributes || [], ...DEFAULT_INCLUDE_ATTRIBUTES];
1380
+ const capTextLength = /* @__PURE__ */ __name((text, maxLength) => {
1381
+ if (text.length > maxLength) {
1382
+ return text.substring(0, maxLength) + "...";
1383
+ }
1384
+ return text;
1385
+ }, "capTextLength");
1386
+ const buildTreeNode = /* @__PURE__ */ __name((nodeId) => {
1387
+ const node = flatTree.map[nodeId];
1388
+ if (!node) return null;
1389
+ if (node.type === "TEXT_NODE") {
1390
+ const textNode = node;
1391
+ return {
1392
+ type: "text",
1393
+ text: textNode.text,
1394
+ isVisible: textNode.isVisible,
1395
+ parent: null,
1396
+ children: []
1397
+ };
1398
+ } else {
1399
+ const elementNode = node;
1400
+ const children = [];
1401
+ if (elementNode.children) {
1402
+ for (const childId of elementNode.children) {
1403
+ const child = buildTreeNode(childId);
1404
+ if (child) {
1405
+ child.parent = null;
1406
+ children.push(child);
1407
+ }
1408
+ }
1409
+ }
1410
+ return {
1411
+ type: "element",
1412
+ tagName: elementNode.tagName,
1413
+ attributes: elementNode.attributes ?? {},
1414
+ isVisible: elementNode.isVisible ?? false,
1415
+ isInteractive: elementNode.isInteractive ?? false,
1416
+ isTopElement: elementNode.isTopElement ?? false,
1417
+ isNew: elementNode.isNew ?? false,
1418
+ highlightIndex: elementNode.highlightIndex,
1419
+ parent: null,
1420
+ children,
1421
+ extra: elementNode.extra ?? {}
1422
+ };
1423
+ }
1424
+ }, "buildTreeNode");
1425
+ const setParentReferences = /* @__PURE__ */ __name((node, parent = null) => {
1426
+ node.parent = parent;
1427
+ for (const child of node.children) {
1428
+ setParentReferences(child, node);
1429
+ }
1430
+ }, "setParentReferences");
1431
+ const rootNode = buildTreeNode(flatTree.rootId);
1432
+ if (!rootNode) return "";
1433
+ setParentReferences(rootNode);
1434
+ const hasParentWithHighlightIndex = /* @__PURE__ */ __name((node) => {
1435
+ let current = node.parent;
1436
+ while (current) {
1437
+ if (current.type === "element" && current.highlightIndex !== void 0) {
1438
+ return true;
1439
+ }
1440
+ current = current.parent;
1441
+ }
1442
+ return false;
1443
+ }, "hasParentWithHighlightIndex");
1444
+ const processNode = /* @__PURE__ */ __name((node, depth, result22) => {
1445
+ let nextDepth = depth;
1446
+ const depthStr = " ".repeat(depth);
1447
+ if (node.type === "element") {
1448
+ if (node.highlightIndex !== void 0) {
1449
+ nextDepth += 1;
1450
+ const text = getAllTextTillNextClickableElement(node);
1451
+ let attributesHtmlStr = "";
1452
+ if (includeAttrs.length > 0 && node.attributes) {
1453
+ const attributesToInclude = {};
1454
+ for (const key of includeAttrs) {
1455
+ const value = node.attributes[key];
1456
+ if (value && value.trim() !== "") {
1457
+ attributesToInclude[key] = value.trim();
1458
+ }
1459
+ }
1460
+ const orderedKeys = includeAttrs.filter((key) => key in attributesToInclude);
1461
+ if (orderedKeys.length > 1) {
1462
+ const keysToRemove = /* @__PURE__ */ new Set();
1463
+ const seenValues = {};
1464
+ for (const key of orderedKeys) {
1465
+ const value = attributesToInclude[key];
1466
+ if (value.length > 5) {
1467
+ if (value in seenValues) {
1468
+ keysToRemove.add(key);
1469
+ } else {
1470
+ seenValues[value] = key;
1471
+ }
1472
+ }
1473
+ }
1474
+ for (const key of keysToRemove) {
1475
+ delete attributesToInclude[key];
1476
+ }
1477
+ }
1478
+ if (attributesToInclude.role === node.tagName) {
1479
+ delete attributesToInclude.role;
1480
+ }
1481
+ const attrsToRemoveIfTextMatches = ["aria-label", "placeholder", "title"];
1482
+ for (const attr of attrsToRemoveIfTextMatches) {
1483
+ if (attributesToInclude[attr] && attributesToInclude[attr].toLowerCase().trim() === text.toLowerCase().trim()) {
1484
+ delete attributesToInclude[attr];
1485
+ }
1486
+ }
1487
+ if (Object.keys(attributesToInclude).length > 0) {
1488
+ attributesHtmlStr = Object.entries(attributesToInclude).map(([key, value]) => `${key}=${capTextLength(value, 20)}`).join(" ");
1489
+ }
1490
+ }
1491
+ const highlightIndicator = node.isNew ? `*[${node.highlightIndex}]` : `[${node.highlightIndex}]`;
1492
+ let line = `${depthStr}${highlightIndicator}<${node.tagName ?? ""}`;
1493
+ if (attributesHtmlStr) {
1494
+ line += ` ${attributesHtmlStr}`;
1495
+ }
1496
+ if (node.extra) {
1497
+ if (node.extra.scrollable) {
1498
+ let scrollDataText = "";
1499
+ if (node.extra.scrollData?.left)
1500
+ scrollDataText += `left=${node.extra.scrollData.left}, `;
1501
+ if (node.extra.scrollData?.top) scrollDataText += `top=${node.extra.scrollData.top}, `;
1502
+ if (node.extra.scrollData?.right)
1503
+ scrollDataText += `right=${node.extra.scrollData.right}, `;
1504
+ if (node.extra.scrollData?.bottom)
1505
+ scrollDataText += `bottom=${node.extra.scrollData.bottom}`;
1506
+ line += ` data-scrollable="${scrollDataText}"`;
1507
+ }
1508
+ }
1509
+ if (text) {
1510
+ const trimmedText = text.trim();
1511
+ if (!attributesHtmlStr) {
1512
+ line += " ";
1513
+ }
1514
+ line += `>${trimmedText}`;
1515
+ } else if (!attributesHtmlStr) {
1516
+ line += " ";
1517
+ }
1518
+ line += " />";
1519
+ result22.push(line);
1520
+ }
1521
+ for (const child of node.children) {
1522
+ processNode(child, nextDepth, result22);
1523
+ }
1524
+ } else if (node.type === "text") {
1525
+ if (hasParentWithHighlightIndex(node)) {
1526
+ return;
1527
+ }
1528
+ if (node.parent && node.parent.type === "element" && node.parent.isVisible && node.parent.isTopElement) {
1529
+ result22.push(`${depthStr}${node.text ?? ""}`);
1530
+ }
1531
+ }
1532
+ }, "processNode");
1533
+ const result2 = [];
1534
+ processNode(rootNode, 0, result2);
1535
+ return result2.join("\n");
1536
+ }
1537
+ __name(flatTreeToString, "flatTreeToString");
1538
+ const getAllTextTillNextClickableElement = /* @__PURE__ */ __name((node, maxDepth = -1) => {
1539
+ const textParts = [];
1540
+ const collectText = /* @__PURE__ */ __name((currentNode, currentDepth) => {
1541
+ if (maxDepth !== -1 && currentDepth > maxDepth) {
1542
+ return;
1543
+ }
1544
+ if (currentNode.type === "element" && currentNode !== node && currentNode.highlightIndex !== void 0) {
1545
+ return;
1546
+ }
1547
+ if (currentNode.type === "text" && currentNode.text) {
1548
+ textParts.push(currentNode.text);
1549
+ } else if (currentNode.type === "element") {
1550
+ for (const child of currentNode.children) {
1551
+ collectText(child, currentDepth + 1);
1552
+ }
1553
+ }
1554
+ }, "collectText");
1555
+ collectText(node, 0);
1556
+ return textParts.join("\n").trim();
1557
+ }, "getAllTextTillNextClickableElement");
1558
+ function getSelectorMap(flatTree) {
1559
+ const selectorMap = /* @__PURE__ */ new Map();
1560
+ const keys = Object.keys(flatTree.map);
1561
+ for (const key of keys) {
1562
+ const node = flatTree.map[key];
1563
+ if (node.isInteractive && typeof node.highlightIndex === "number") {
1564
+ selectorMap.set(node.highlightIndex, node);
1565
+ }
1566
+ }
1567
+ return selectorMap;
1568
+ }
1569
+ __name(getSelectorMap, "getSelectorMap");
1570
+ function getElementTextMap(simplifiedHTML) {
1571
+ const lines = simplifiedHTML.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
1572
+ const elementTextMap = /* @__PURE__ */ new Map();
1573
+ for (const line of lines) {
1574
+ const regex = /^\[(\d+)\]<[^>]+>([^<]*)/;
1575
+ const match = regex.exec(line);
1576
+ if (match) {
1577
+ const index = parseInt(match[1], 10);
1578
+ elementTextMap.set(index, line);
1579
+ }
1580
+ }
1581
+ return elementTextMap;
1582
+ }
1583
+ __name(getElementTextMap, "getElementTextMap");
1584
+ function cleanUpHighlights() {
1585
+ const cleanupFunctions = window._highlightCleanupFunctions || [];
1586
+ for (const cleanup of cleanupFunctions) {
1587
+ if (typeof cleanup === "function") {
1588
+ cleanup();
1589
+ }
1590
+ }
1591
+ window._highlightCleanupFunctions = [];
1592
+ }
1593
+ __name(cleanUpHighlights, "cleanUpHighlights");
1594
+ window.addEventListener("popstate", () => {
1595
+ cleanUpHighlights();
1596
+ });
1597
+ window.addEventListener("hashchange", () => {
1598
+ cleanUpHighlights();
1599
+ });
1600
+ window.addEventListener("beforeunload", () => {
1601
+ cleanUpHighlights();
1602
+ });
1603
+ const navigation = window.navigation;
1604
+ if (navigation && typeof navigation.addEventListener === "function") {
1605
+ navigation.addEventListener("navigate", () => {
1606
+ cleanUpHighlights();
1607
+ });
1608
+ } else {
1609
+ let currentUrl = window.location.href;
1610
+ setInterval(() => {
1611
+ if (window.location.href !== currentUrl) {
1612
+ currentUrl = window.location.href;
1613
+ cleanUpHighlights();
1614
+ }
1615
+ }, 500);
1616
+ }
1617
+ function getPageInfo() {
1618
+ const viewport_width = window.innerWidth;
1619
+ const viewport_height = window.innerHeight;
1620
+ const page_width = Math.max(document.documentElement.scrollWidth, document.body.scrollWidth || 0);
1621
+ const page_height = Math.max(
1622
+ document.documentElement.scrollHeight,
1623
+ document.body.scrollHeight || 0
1624
+ );
1625
+ const scroll_x = window.scrollX || window.pageXOffset || document.documentElement.scrollLeft || 0;
1626
+ const scroll_y = window.scrollY || window.pageYOffset || document.documentElement.scrollTop || 0;
1627
+ const pixels_below = Math.max(0, page_height - (window.innerHeight + scroll_y));
1628
+ const pixels_right = Math.max(0, page_width - (window.innerWidth + scroll_x));
1629
+ return {
1630
+ // Current viewport dimensions
1631
+ viewport_width,
1632
+ viewport_height,
1633
+ // Total page dimensions
1634
+ page_width,
1635
+ page_height,
1636
+ // Current scroll position
1637
+ scroll_x,
1638
+ scroll_y,
1639
+ pixels_above: scroll_y,
1640
+ pixels_below,
1641
+ pages_above: viewport_height > 0 ? scroll_y / viewport_height : 0,
1642
+ pages_below: viewport_height > 0 ? pixels_below / viewport_height : 0,
1643
+ total_pages: viewport_height > 0 ? page_height / viewport_height : 0,
1644
+ current_page_position: scroll_y / Math.max(1, page_height - viewport_height),
1645
+ pixels_left: scroll_x,
1646
+ pixels_right
1647
+ };
1648
+ }
1649
+ __name(getPageInfo, "getPageInfo");
1650
+ function patchReact(pageController) {
1651
+ const reactRootElements = document.querySelectorAll(
1652
+ '[data-reactroot], [data-reactid], [data-react-checksum], #root, #app, [id^="root-"], [id^="app-"], #adex-wrapper, #adex-root'
1653
+ );
1654
+ for (const element of reactRootElements) {
1655
+ element.setAttribute("data-page-agent-not-interactive", "true");
1656
+ }
1657
+ }
1658
+ __name(patchReact, "patchReact");
1659
+ const _PageController = class _PageController extends EventTarget {
1660
+ config;
1661
+ /** Corresponds to eval_page in browser-use */
1662
+ flatTree = null;
1663
+ /**
1664
+ * All highlighted index-mapped interactive elements
1665
+ * Corresponds to DOMState.selector_map in browser-use
1666
+ */
1667
+ selectorMap = /* @__PURE__ */ new Map();
1668
+ /** Index -> element text description mapping */
1669
+ elementTextMap = /* @__PURE__ */ new Map();
1670
+ /**
1671
+ * Simplified HTML for LLM consumption.
1672
+ * Corresponds to clickable_elements_to_string in browser-use
1673
+ */
1674
+ simplifiedHTML = "<EMPTY>";
1675
+ /** last time the tree was updated */
1676
+ lastTimeUpdate = 0;
1677
+ constructor(config = {}) {
1678
+ super();
1679
+ this.config = config;
1680
+ patchReact();
1681
+ }
1682
+ // ======= State Queries =======
1683
+ /**
1684
+ * Get current page URL
1685
+ */
1686
+ async getCurrentUrl() {
1687
+ return window.location.href;
1688
+ }
1689
+ /**
1690
+ * Get current page title
1691
+ */
1692
+ async getPageTitle() {
1693
+ return document.title;
1694
+ }
1695
+ /**
1696
+ * Get page scroll and size info
1697
+ */
1698
+ async getPageInfo() {
1699
+ return getPageInfo();
1700
+ }
1701
+ /**
1702
+ * Get the simplified HTML representation of the page.
1703
+ * This is used by LLM to understand the page structure.
1704
+ */
1705
+ async getSimplifiedHTML() {
1706
+ return this.simplifiedHTML;
1707
+ }
1708
+ /**
1709
+ * Get text description for an element by index
1710
+ */
1711
+ async getElementText(index) {
1712
+ return this.elementTextMap.get(index);
1713
+ }
1714
+ /**
1715
+ * Get total number of indexed interactive elements
1716
+ */
1717
+ async getElementCount() {
1718
+ return this.selectorMap.size;
1719
+ }
1720
+ /**
1721
+ * Get last tree update timestamp
1722
+ */
1723
+ async getLastUpdateTime() {
1724
+ return this.lastTimeUpdate;
1725
+ }
1726
+ /**
1727
+ * Get the viewport expansion setting
1728
+ */
1729
+ async getViewportExpansion() {
1730
+ return this.config.viewportExpansion ?? VIEWPORT_EXPANSION;
1731
+ }
1732
+ // ======= DOM Tree Operations =======
1733
+ /**
1734
+ * Update DOM tree, returns simplified HTML for LLM.
1735
+ * This is the main method to refresh the page state.
1736
+ */
1737
+ async updateTree() {
1738
+ this.dispatchEvent(new Event("beforeUpdate"));
1739
+ this.lastTimeUpdate = Date.now();
1740
+ cleanUpHighlights();
1741
+ const blacklist = [
1742
+ ...this.config.interactiveBlacklist || [],
1743
+ ...document.querySelectorAll("[data-page-agent-not-interactive]").values()
1744
+ ];
1745
+ this.flatTree = getFlatTree({
1746
+ ...this.config,
1747
+ interactiveBlacklist: blacklist
1748
+ });
1749
+ this.simplifiedHTML = flatTreeToString(this.flatTree, this.config.include_attributes);
1750
+ this.selectorMap.clear();
1751
+ this.selectorMap = getSelectorMap(this.flatTree);
1752
+ this.elementTextMap.clear();
1753
+ this.elementTextMap = getElementTextMap(this.simplifiedHTML);
1754
+ this.dispatchEvent(new Event("afterUpdate"));
1755
+ return this.simplifiedHTML;
1756
+ }
1757
+ /**
1758
+ * Clean up all element highlights
1759
+ */
1760
+ async cleanUpHighlights() {
1761
+ cleanUpHighlights();
1762
+ }
1763
+ // ======= Element Actions =======
1764
+ /**
1765
+ * Click element by index
1766
+ */
1767
+ async clickElement(index) {
1768
+ try {
1769
+ const element = getElementByIndex(this.selectorMap, index);
1770
+ const elemText = this.elementTextMap.get(index);
1771
+ await clickElement(element);
1772
+ if (element instanceof HTMLAnchorElement && element.target === "_blank") {
1773
+ return {
1774
+ success: true,
1775
+ message: `✅ Clicked element (${elemText ?? index}). ⚠️ Link opens in a new tab. You are not capable of reading new tabs.`
1776
+ };
1777
+ }
1778
+ return {
1779
+ success: true,
1780
+ message: `✅ Clicked element (${elemText ?? index}).`
1781
+ };
1782
+ } catch (error) {
1783
+ return {
1784
+ success: false,
1785
+ message: `❌ Failed to click element: ${error}`
1786
+ };
1787
+ }
1788
+ }
1789
+ /**
1790
+ * Input text into element by index
1791
+ */
1792
+ async inputText(index, text) {
1793
+ try {
1794
+ const element = getElementByIndex(this.selectorMap, index);
1795
+ const elemText = this.elementTextMap.get(index);
1796
+ await inputTextElement(element, text);
1797
+ return {
1798
+ success: true,
1799
+ message: `✅ Input text (${text}) into element (${elemText ?? index}).`
1800
+ };
1801
+ } catch (error) {
1802
+ return {
1803
+ success: false,
1804
+ message: `❌ Failed to input text: ${error}`
1805
+ };
1806
+ }
1807
+ }
1808
+ /**
1809
+ * Select dropdown option by index and option text
1810
+ */
1811
+ async selectOption(index, optionText) {
1812
+ try {
1813
+ const element = getElementByIndex(this.selectorMap, index);
1814
+ const elemText = this.elementTextMap.get(index);
1815
+ await selectOptionElement(element, optionText);
1816
+ return {
1817
+ success: true,
1818
+ message: `✅ Selected option (${optionText}) in element (${elemText ?? index}).`
1819
+ };
1820
+ } catch (error) {
1821
+ return {
1822
+ success: false,
1823
+ message: `❌ Failed to select option: ${error}`
1824
+ };
1825
+ }
1826
+ }
1827
+ /**
1828
+ * Scroll vertically
1829
+ */
1830
+ async scroll(options) {
1831
+ try {
1832
+ const { down, numPages, pixels, index } = options;
1833
+ const scrollAmount = pixels ?? numPages * (down ? 1 : -1) * window.innerHeight;
1834
+ const element = index !== void 0 ? getElementByIndex(this.selectorMap, index) : null;
1835
+ const message = await scrollVertically(down, scrollAmount, element);
1836
+ return {
1837
+ success: true,
1838
+ message
1839
+ };
1840
+ } catch (error) {
1841
+ return {
1842
+ success: false,
1843
+ message: `❌ Failed to scroll: ${error}`
1844
+ };
1845
+ }
1846
+ }
1847
+ /**
1848
+ * Scroll horizontally
1849
+ */
1850
+ async scrollHorizontally(options) {
1851
+ try {
1852
+ const { right, pixels, index } = options;
1853
+ const scrollAmount = pixels * (right ? 1 : -1);
1854
+ const element = index !== void 0 ? getElementByIndex(this.selectorMap, index) : null;
1855
+ const message = await scrollHorizontally(right, scrollAmount, element);
1856
+ return {
1857
+ success: true,
1858
+ message
1859
+ };
1860
+ } catch (error) {
1861
+ return {
1862
+ success: false,
1863
+ message: `❌ Failed to scroll horizontally: ${error}`
1864
+ };
1865
+ }
1866
+ }
1867
+ /**
1868
+ * Execute arbitrary JavaScript on the page
1869
+ */
1870
+ async executeJavascript(script) {
1871
+ try {
1872
+ const asyncFunction = eval(`(async () => { ${script} })`);
1873
+ const result = await asyncFunction();
1874
+ return {
1875
+ success: true,
1876
+ message: `✅ Executed JavaScript. Result: ${result}`
1877
+ };
1878
+ } catch (error) {
1879
+ return {
1880
+ success: false,
1881
+ message: `❌ Error executing JavaScript: ${error}`
1882
+ };
1883
+ }
1884
+ }
1885
+ /**
1886
+ * Dispose and clean up resources
1887
+ */
1888
+ dispose() {
1889
+ cleanUpHighlights();
1890
+ this.flatTree = null;
1891
+ this.selectorMap.clear();
1892
+ this.elementTextMap.clear();
1893
+ this.simplifiedHTML = "<EMPTY>";
1894
+ }
1895
+ };
1896
+ __name(_PageController, "PageController");
1897
+ let PageController = _PageController;
1898
+ export {
1899
+ PageController
1900
+ };
1901
+ //# sourceMappingURL=page-controller.js.map