@usecrow/client 0.1.28 → 0.1.30
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/browser.cjs +1 -1
- package/dist/browser.d.ts +7 -4
- package/dist/browser.js +26 -17
- package/dist/browserUse-B-b3zqW1.cjs +9 -0
- package/dist/browserUse-D5NUCzN4.js +1616 -0
- package/dist/index.cjs +7 -3
- package/dist/index.d.ts +5 -4
- package/dist/index.js +71 -48
- package/package.json +1 -1
- package/dist/PageController-72owMK9b.cjs +0 -9
- package/dist/PageController-BT9YJiz0.js +0 -1379
- package/dist/browserUse-CMYea2D_.cjs +0 -1
- package/dist/browserUse-CMq8wG4u.js +0 -259
|
@@ -0,0 +1,1616 @@
|
|
|
1
|
+
async function waitFor(o) {
|
|
2
|
+
await new Promise((e) => setTimeout(e, o * 1e3));
|
|
3
|
+
}
|
|
4
|
+
async function movePointerToElement(o) {
|
|
5
|
+
const e = o.getBoundingClientRect(), n = e.left + e.width / 2, r = e.top + e.height / 2;
|
|
6
|
+
window.dispatchEvent(new CustomEvent("PageAgent::MovePointerTo", { detail: { x: n, y: r } })), await waitFor(0.3);
|
|
7
|
+
}
|
|
8
|
+
function getElementByIndex(o, e) {
|
|
9
|
+
const n = o.get(e);
|
|
10
|
+
if (!n)
|
|
11
|
+
throw new Error(`No interactive element found at index ${e}`);
|
|
12
|
+
const r = n.ref;
|
|
13
|
+
if (!r)
|
|
14
|
+
throw new Error(`Element at index ${e} does not have a reference`);
|
|
15
|
+
if (!(r instanceof HTMLElement))
|
|
16
|
+
throw new Error(`Element at index ${e} is not an HTMLElement`);
|
|
17
|
+
return r;
|
|
18
|
+
}
|
|
19
|
+
let lastClickedElement = null;
|
|
20
|
+
function blurLastClickedElement() {
|
|
21
|
+
lastClickedElement && (lastClickedElement.blur(), lastClickedElement.dispatchEvent(
|
|
22
|
+
new MouseEvent("mouseout", { bubbles: !0, cancelable: !0 })
|
|
23
|
+
), lastClickedElement = null);
|
|
24
|
+
}
|
|
25
|
+
async function clickElement(o) {
|
|
26
|
+
blurLastClickedElement(), lastClickedElement = o, await scrollIntoViewIfNeeded(o), await movePointerToElement(o), window.dispatchEvent(new CustomEvent("PageAgent::ClickPointer")), await waitFor(0.1), o.dispatchEvent(new MouseEvent("mouseenter", { bubbles: !0, cancelable: !0 })), o.dispatchEvent(new MouseEvent("mouseover", { bubbles: !0, cancelable: !0 })), o.dispatchEvent(new MouseEvent("mousedown", { bubbles: !0, cancelable: !0 })), o.focus(), o.dispatchEvent(new MouseEvent("mouseup", { bubbles: !0, cancelable: !0 })), o.dispatchEvent(new MouseEvent("click", { bubbles: !0, cancelable: !0 })), await waitFor(0.1);
|
|
27
|
+
}
|
|
28
|
+
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
|
|
29
|
+
window.HTMLInputElement.prototype,
|
|
30
|
+
"value"
|
|
31
|
+
).set, nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(
|
|
32
|
+
window.HTMLTextAreaElement.prototype,
|
|
33
|
+
"value"
|
|
34
|
+
).set;
|
|
35
|
+
async function inputTextElement(o, e) {
|
|
36
|
+
if (!(o instanceof HTMLInputElement || o instanceof HTMLTextAreaElement))
|
|
37
|
+
throw new Error("Element is not an input or textarea");
|
|
38
|
+
await clickElement(o), o instanceof HTMLTextAreaElement ? nativeTextAreaValueSetter.call(o, e) : nativeInputValueSetter.call(o, e);
|
|
39
|
+
const n = new Event("input", { bubbles: !0 });
|
|
40
|
+
o.dispatchEvent(n), await waitFor(0.1), blurLastClickedElement();
|
|
41
|
+
}
|
|
42
|
+
async function selectOptionElement(o, e) {
|
|
43
|
+
if (!(o instanceof HTMLSelectElement))
|
|
44
|
+
throw new Error("Element is not a select element");
|
|
45
|
+
const r = Array.from(o.options).find((d) => {
|
|
46
|
+
var h;
|
|
47
|
+
return ((h = d.textContent) == null ? void 0 : h.trim()) === e.trim();
|
|
48
|
+
});
|
|
49
|
+
if (!r)
|
|
50
|
+
throw new Error(`Option with text "${e}" not found in select element`);
|
|
51
|
+
o.value = r.value, o.dispatchEvent(new Event("change", { bubbles: !0 })), await waitFor(0.1);
|
|
52
|
+
}
|
|
53
|
+
async function scrollIntoViewIfNeeded(o) {
|
|
54
|
+
const e = o;
|
|
55
|
+
e.scrollIntoViewIfNeeded ? e.scrollIntoViewIfNeeded() : e.scrollIntoView({ behavior: "auto", block: "center", inline: "nearest" });
|
|
56
|
+
}
|
|
57
|
+
async function scrollVertically(o, e, n) {
|
|
58
|
+
if (n) {
|
|
59
|
+
const l = n;
|
|
60
|
+
console.log(
|
|
61
|
+
"[SCROLL DEBUG] Starting direct container scroll for element:",
|
|
62
|
+
l.tagName
|
|
63
|
+
);
|
|
64
|
+
let f = l, v = !1, m = null, c = 0, p = 0;
|
|
65
|
+
const C = e;
|
|
66
|
+
for (; f && p < 10; ) {
|
|
67
|
+
const B = window.getComputedStyle(f), D = /(auto|scroll|overlay)/.test(B.overflowY), S = f.scrollHeight > f.clientHeight;
|
|
68
|
+
if (console.log(
|
|
69
|
+
"[SCROLL DEBUG] Checking element:",
|
|
70
|
+
f.tagName,
|
|
71
|
+
"hasScrollableY:",
|
|
72
|
+
D,
|
|
73
|
+
"canScrollVertically:",
|
|
74
|
+
S,
|
|
75
|
+
"scrollHeight:",
|
|
76
|
+
f.scrollHeight,
|
|
77
|
+
"clientHeight:",
|
|
78
|
+
f.clientHeight
|
|
79
|
+
), D && S) {
|
|
80
|
+
const R = f.scrollTop, U = f.scrollHeight - f.clientHeight;
|
|
81
|
+
let k = C / 3;
|
|
82
|
+
k > 0 ? k = Math.min(k, U - R) : k = Math.max(k, -R), f.scrollTop = R + k;
|
|
83
|
+
const V = f.scrollTop, $ = V - R;
|
|
84
|
+
if (console.log(
|
|
85
|
+
"[SCROLL DEBUG] Scroll attempt:",
|
|
86
|
+
f.tagName,
|
|
87
|
+
"before:",
|
|
88
|
+
R,
|
|
89
|
+
"after:",
|
|
90
|
+
V,
|
|
91
|
+
"delta:",
|
|
92
|
+
$
|
|
93
|
+
), Math.abs($) > 0.5) {
|
|
94
|
+
v = !0, m = f, c = $, console.log(
|
|
95
|
+
"[SCROLL DEBUG] Successfully scrolled container:",
|
|
96
|
+
f.tagName,
|
|
97
|
+
"delta:",
|
|
98
|
+
$
|
|
99
|
+
);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (f === document.body || f === document.documentElement)
|
|
104
|
+
break;
|
|
105
|
+
f = f.parentElement, p++;
|
|
106
|
+
}
|
|
107
|
+
return v ? `Scrolled container (${m == null ? void 0 : m.tagName}) by ${c}px` : `No scrollable container found for element (${l.tagName})`;
|
|
108
|
+
}
|
|
109
|
+
const r = e, d = (l) => l.clientHeight >= window.innerHeight * 0.5, h = (l) => l && /(auto|scroll|overlay)/.test(getComputedStyle(l).overflowY) && l.scrollHeight > l.clientHeight && d(l);
|
|
110
|
+
let s = document.activeElement;
|
|
111
|
+
for (; s && !h(s) && s !== document.body; ) s = s.parentElement;
|
|
112
|
+
if (s = h(s) ? s : Array.from(document.querySelectorAll("*")).find(h) || document.scrollingElement || document.documentElement, s === document.scrollingElement || s === document.documentElement || s === document.body) {
|
|
113
|
+
const l = window.scrollY, f = document.documentElement.scrollHeight - window.innerHeight;
|
|
114
|
+
window.scrollBy(0, r);
|
|
115
|
+
const v = window.scrollY, m = v - l;
|
|
116
|
+
if (Math.abs(m) < 1)
|
|
117
|
+
return r > 0 ? "⚠️ Already at the bottom of the page, cannot scroll down further." : "⚠️ Already at the top of the page, cannot scroll up further.";
|
|
118
|
+
const c = r > 0 && v >= f - 1, p = r < 0 && v <= 1;
|
|
119
|
+
return c ? `✅ Scrolled page by ${m}px. Reached the bottom of the page.` : p ? `✅ Scrolled page by ${m}px. Reached the top of the page.` : `✅ Scrolled page by ${m}px.`;
|
|
120
|
+
} else {
|
|
121
|
+
const l = s.scrollTop, f = s.scrollHeight - s.clientHeight;
|
|
122
|
+
s.scrollBy({ top: r, behavior: "smooth" }), await waitFor(0.1);
|
|
123
|
+
const v = s.scrollTop, m = v - l;
|
|
124
|
+
if (Math.abs(m) < 1)
|
|
125
|
+
return r > 0 ? `⚠️ Already at the bottom of container (${s.tagName}), cannot scroll down further.` : `⚠️ Already at the top of container (${s.tagName}), cannot scroll up further.`;
|
|
126
|
+
const c = r > 0 && v >= f - 1, p = r < 0 && v <= 1;
|
|
127
|
+
return c ? `✅ Scrolled container (${s.tagName}) by ${m}px. Reached the bottom.` : p ? `✅ Scrolled container (${s.tagName}) by ${m}px. Reached the top.` : `✅ Scrolled container (${s.tagName}) by ${m}px.`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function scrollHorizontally(o, e, n) {
|
|
131
|
+
if (n) {
|
|
132
|
+
const l = n;
|
|
133
|
+
console.log(
|
|
134
|
+
"[SCROLL DEBUG] Starting direct container scroll for element:",
|
|
135
|
+
l.tagName
|
|
136
|
+
);
|
|
137
|
+
let f = l, v = !1, m = null, c = 0, p = 0;
|
|
138
|
+
const C = o ? e : -e;
|
|
139
|
+
for (; f && p < 10; ) {
|
|
140
|
+
const B = window.getComputedStyle(f), D = /(auto|scroll|overlay)/.test(B.overflowX), S = f.scrollWidth > f.clientWidth;
|
|
141
|
+
if (console.log(
|
|
142
|
+
"[SCROLL DEBUG] Checking element:",
|
|
143
|
+
f.tagName,
|
|
144
|
+
"hasScrollableX:",
|
|
145
|
+
D,
|
|
146
|
+
"canScrollHorizontally:",
|
|
147
|
+
S,
|
|
148
|
+
"scrollWidth:",
|
|
149
|
+
f.scrollWidth,
|
|
150
|
+
"clientWidth:",
|
|
151
|
+
f.clientWidth
|
|
152
|
+
), D && S) {
|
|
153
|
+
const R = f.scrollLeft, U = f.scrollWidth - f.clientWidth;
|
|
154
|
+
let k = C / 3;
|
|
155
|
+
k > 0 ? k = Math.min(k, U - R) : k = Math.max(k, -R), f.scrollLeft = R + k;
|
|
156
|
+
const V = f.scrollLeft, $ = V - R;
|
|
157
|
+
if (console.log(
|
|
158
|
+
"[SCROLL DEBUG] Scroll attempt:",
|
|
159
|
+
f.tagName,
|
|
160
|
+
"before:",
|
|
161
|
+
R,
|
|
162
|
+
"after:",
|
|
163
|
+
V,
|
|
164
|
+
"delta:",
|
|
165
|
+
$
|
|
166
|
+
), Math.abs($) > 0.5) {
|
|
167
|
+
v = !0, m = f, c = $, console.log(
|
|
168
|
+
"[SCROLL DEBUG] Successfully scrolled container:",
|
|
169
|
+
f.tagName,
|
|
170
|
+
"delta:",
|
|
171
|
+
$
|
|
172
|
+
);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (f === document.body || f === document.documentElement)
|
|
177
|
+
break;
|
|
178
|
+
f = f.parentElement, p++;
|
|
179
|
+
}
|
|
180
|
+
return v ? `Scrolled container (${m == null ? void 0 : m.tagName}) horizontally by ${c}px` : `No horizontally scrollable container found for element (${l.tagName})`;
|
|
181
|
+
}
|
|
182
|
+
const r = o ? e : -e, d = (l) => l.clientWidth >= window.innerWidth * 0.5, h = (l) => l && /(auto|scroll|overlay)/.test(getComputedStyle(l).overflowX) && l.scrollWidth > l.clientWidth && d(l);
|
|
183
|
+
let s = document.activeElement;
|
|
184
|
+
for (; s && !h(s) && s !== document.body; ) s = s.parentElement;
|
|
185
|
+
if (s = h(s) ? s : Array.from(document.querySelectorAll("*")).find(h) || document.scrollingElement || document.documentElement, s === document.scrollingElement || s === document.documentElement || s === document.body) {
|
|
186
|
+
const l = window.scrollX, f = document.documentElement.scrollWidth - window.innerWidth;
|
|
187
|
+
window.scrollBy(r, 0);
|
|
188
|
+
const v = window.scrollX, m = v - l;
|
|
189
|
+
if (Math.abs(m) < 1)
|
|
190
|
+
return r > 0 ? "⚠️ Already at the right edge of the page, cannot scroll right further." : "⚠️ Already at the left edge of the page, cannot scroll left further.";
|
|
191
|
+
const c = r > 0 && v >= f - 1, p = r < 0 && v <= 1;
|
|
192
|
+
return c ? `✅ Scrolled page by ${m}px. Reached the right edge of the page.` : p ? `✅ Scrolled page by ${m}px. Reached the left edge of the page.` : `✅ Scrolled page horizontally by ${m}px.`;
|
|
193
|
+
} else {
|
|
194
|
+
const l = s.scrollLeft, f = s.scrollWidth - s.clientWidth;
|
|
195
|
+
s.scrollBy({ left: r, behavior: "smooth" }), await waitFor(0.1);
|
|
196
|
+
const v = s.scrollLeft, m = v - l;
|
|
197
|
+
if (Math.abs(m) < 1)
|
|
198
|
+
return r > 0 ? `⚠️ Already at the right edge of container (${s.tagName}), cannot scroll right further.` : `⚠️ Already at the left edge of container (${s.tagName}), cannot scroll left further.`;
|
|
199
|
+
const c = r > 0 && v >= f - 1, p = r < 0 && v <= 1;
|
|
200
|
+
return c ? `✅ Scrolled container (${s.tagName}) by ${m}px. Reached the right edge.` : p ? `✅ Scrolled container (${s.tagName}) by ${m}px. Reached the left edge.` : `✅ Scrolled container (${s.tagName}) horizontally by ${m}px.`;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const VIEWPORT_EXPANSION = -1, domTree = (o = {
|
|
204
|
+
doHighlightElements: !0,
|
|
205
|
+
focusHighlightIndex: -1,
|
|
206
|
+
viewportExpansion: 0,
|
|
207
|
+
debugMode: !1,
|
|
208
|
+
/**
|
|
209
|
+
* @edit
|
|
210
|
+
*/
|
|
211
|
+
/** @type {Element[]} */
|
|
212
|
+
interactiveBlacklist: [],
|
|
213
|
+
/** @type {Element[]} */
|
|
214
|
+
interactiveWhitelist: [],
|
|
215
|
+
highlightOpacity: 0.1,
|
|
216
|
+
highlightLabelOpacity: 0.5
|
|
217
|
+
}) => {
|
|
218
|
+
const { interactiveBlacklist: e, interactiveWhitelist: n, highlightOpacity: r, highlightLabelOpacity: d } = o, { doHighlightElements: h, focusHighlightIndex: s, viewportExpansion: l, debugMode: f } = o;
|
|
219
|
+
let v = 0;
|
|
220
|
+
const m = /* @__PURE__ */ new WeakMap();
|
|
221
|
+
function c(t, i) {
|
|
222
|
+
!t || t.nodeType !== Node.ELEMENT_NODE || m.set(t, { ...m.get(t), ...i });
|
|
223
|
+
}
|
|
224
|
+
const p = {
|
|
225
|
+
boundingRects: /* @__PURE__ */ new WeakMap(),
|
|
226
|
+
clientRects: /* @__PURE__ */ new WeakMap(),
|
|
227
|
+
computedStyles: /* @__PURE__ */ new WeakMap(),
|
|
228
|
+
clearCache: () => {
|
|
229
|
+
p.boundingRects = /* @__PURE__ */ new WeakMap(), p.clientRects = /* @__PURE__ */ new WeakMap(), p.computedStyles = /* @__PURE__ */ new WeakMap();
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
function C(t) {
|
|
233
|
+
if (!t) return null;
|
|
234
|
+
if (p.boundingRects.has(t))
|
|
235
|
+
return p.boundingRects.get(t);
|
|
236
|
+
const i = t.getBoundingClientRect();
|
|
237
|
+
return i && p.boundingRects.set(t, i), i;
|
|
238
|
+
}
|
|
239
|
+
function B(t) {
|
|
240
|
+
if (!t) return null;
|
|
241
|
+
if (p.computedStyles.has(t))
|
|
242
|
+
return p.computedStyles.get(t);
|
|
243
|
+
const i = window.getComputedStyle(t);
|
|
244
|
+
return i && p.computedStyles.set(t, i), i;
|
|
245
|
+
}
|
|
246
|
+
function D(t) {
|
|
247
|
+
if (!t) return null;
|
|
248
|
+
if (p.clientRects.has(t))
|
|
249
|
+
return p.clientRects.get(t);
|
|
250
|
+
const i = t.getClientRects();
|
|
251
|
+
return i && p.clientRects.set(t, i), i;
|
|
252
|
+
}
|
|
253
|
+
const S = {}, R = { current: 0 }, U = "playwright-highlight-container";
|
|
254
|
+
function k(t, i, w = null) {
|
|
255
|
+
if (!t) return i;
|
|
256
|
+
const u = [];
|
|
257
|
+
let a = null, x = 20, E = 16, H = null;
|
|
258
|
+
try {
|
|
259
|
+
let b = document.getElementById(U);
|
|
260
|
+
b || (b = document.createElement("div"), b.id = U, b.style.position = "fixed", b.style.pointerEvents = "none", b.style.top = "0", b.style.left = "0", b.style.width = "100%", b.style.height = "100%", b.style.zIndex = "2147483640", b.style.backgroundColor = "transparent", document.body.appendChild(b));
|
|
261
|
+
const P = t.getClientRects();
|
|
262
|
+
if (!P || P.length === 0) return i;
|
|
263
|
+
const O = [
|
|
264
|
+
"#FF0000",
|
|
265
|
+
"#00FF00",
|
|
266
|
+
"#0000FF",
|
|
267
|
+
"#FFA500",
|
|
268
|
+
"#800080",
|
|
269
|
+
"#008080",
|
|
270
|
+
"#FF69B4",
|
|
271
|
+
"#4B0082",
|
|
272
|
+
"#FF4500",
|
|
273
|
+
"#2E8B57",
|
|
274
|
+
"#DC143C",
|
|
275
|
+
"#4682B4"
|
|
276
|
+
], g = i % O.length;
|
|
277
|
+
let y = O[g];
|
|
278
|
+
const I = y + Math.floor(r * 255).toString(16).padStart(2, "0");
|
|
279
|
+
y = y + Math.floor(d * 255).toString(16).padStart(2, "0");
|
|
280
|
+
let L = { x: 0, y: 0 };
|
|
281
|
+
if (w) {
|
|
282
|
+
const N = w.getBoundingClientRect();
|
|
283
|
+
L.x = N.left, L.y = N.top;
|
|
284
|
+
}
|
|
285
|
+
const j = document.createDocumentFragment();
|
|
286
|
+
for (const N of P) {
|
|
287
|
+
if (N.width === 0 || N.height === 0) continue;
|
|
288
|
+
const M = document.createElement("div");
|
|
289
|
+
M.style.position = "fixed", M.style.border = `2px solid ${y}`, M.style.backgroundColor = I, M.style.pointerEvents = "none", M.style.boxSizing = "border-box";
|
|
290
|
+
const T = N.top + L.y, W = N.left + L.x;
|
|
291
|
+
M.style.top = `${T}px`, M.style.left = `${W}px`, M.style.width = `${N.width}px`, M.style.height = `${N.height}px`, j.appendChild(M), u.push({ element: M, initialRect: N });
|
|
292
|
+
}
|
|
293
|
+
const G = P[0];
|
|
294
|
+
if (d > 0) {
|
|
295
|
+
a = document.createElement("div"), a.className = "playwright-highlight-label", a.style.position = "fixed", a.style.background = y, a.style.color = "white", a.style.padding = "1px 4px", a.style.borderRadius = "4px", a.style.fontSize = `${Math.min(12, Math.max(8, G.height / 2))}px`, a.textContent = i.toString(), x = a.offsetWidth > 0 ? a.offsetWidth : x, E = a.offsetHeight > 0 ? a.offsetHeight : E;
|
|
296
|
+
const N = G.top + L.y, M = G.left + L.x;
|
|
297
|
+
let T = N + 2, W = M + G.width - x - 2;
|
|
298
|
+
(G.width < x + 4 || G.height < E + 4) && (T = N - E - 2, W = M + G.width - x, W < L.x && (W = M)), T = Math.max(0, Math.min(T, window.innerHeight - E)), W = Math.max(0, Math.min(W, window.innerWidth - x)), a.style.top = `${T}px`, a.style.left = `${W}px`, j.appendChild(a);
|
|
299
|
+
}
|
|
300
|
+
const q = ((N, M) => {
|
|
301
|
+
let T = 0;
|
|
302
|
+
return (...W) => {
|
|
303
|
+
const X = performance.now();
|
|
304
|
+
if (!(X - T < M))
|
|
305
|
+
return T = X, N(...W);
|
|
306
|
+
};
|
|
307
|
+
})(() => {
|
|
308
|
+
const N = t.getClientRects();
|
|
309
|
+
let M = { x: 0, y: 0 };
|
|
310
|
+
if (w) {
|
|
311
|
+
const T = w.getBoundingClientRect();
|
|
312
|
+
M.x = T.left, M.y = T.top;
|
|
313
|
+
}
|
|
314
|
+
if (u.forEach((T, W) => {
|
|
315
|
+
if (W < N.length) {
|
|
316
|
+
const X = N[W], tt = X.top + M.y, Q = X.left + M.x;
|
|
317
|
+
T.element.style.top = `${tt}px`, T.element.style.left = `${Q}px`, T.element.style.width = `${X.width}px`, T.element.style.height = `${X.height}px`, T.element.style.display = X.width === 0 || X.height === 0 ? "none" : "block";
|
|
318
|
+
} else
|
|
319
|
+
T.element.style.display = "none";
|
|
320
|
+
}), N.length < u.length)
|
|
321
|
+
for (let T = N.length; T < u.length; T++)
|
|
322
|
+
u[T].element.style.display = "none";
|
|
323
|
+
if (a && N.length > 0) {
|
|
324
|
+
const T = N[0], W = T.top + M.y, X = T.left + M.x;
|
|
325
|
+
let tt = W + 2, Q = X + T.width - x - 2;
|
|
326
|
+
(T.width < x + 4 || T.height < E + 4) && (tt = W - E - 2, Q = X + T.width - x, Q < M.x && (Q = X)), tt = Math.max(0, Math.min(tt, window.innerHeight - E)), Q = Math.max(0, Math.min(Q, window.innerWidth - x)), a.style.top = `${tt}px`, a.style.left = `${Q}px`, a.style.display = "block";
|
|
327
|
+
} else a && (a.style.display = "none");
|
|
328
|
+
}, 16);
|
|
329
|
+
return window.addEventListener("scroll", q, !0), window.addEventListener("resize", q), H = () => {
|
|
330
|
+
window.removeEventListener("scroll", q, !0), window.removeEventListener("resize", q), u.forEach((N) => N.element.remove()), a && a.remove();
|
|
331
|
+
}, b.appendChild(j), i + 1;
|
|
332
|
+
} finally {
|
|
333
|
+
H && (window._highlightCleanupFunctions = window._highlightCleanupFunctions || []).push(
|
|
334
|
+
H
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function V(t) {
|
|
339
|
+
if (!t || t.nodeType !== Node.ELEMENT_NODE)
|
|
340
|
+
return null;
|
|
341
|
+
const i = B(t);
|
|
342
|
+
if (!i) return null;
|
|
343
|
+
const w = i.display;
|
|
344
|
+
if (w === "inline" || w === "inline-block")
|
|
345
|
+
return null;
|
|
346
|
+
const u = i.overflowX, a = i.overflowY, x = u === "auto" || u === "scroll", E = a === "auto" || a === "scroll";
|
|
347
|
+
if (!x && !E)
|
|
348
|
+
return null;
|
|
349
|
+
const H = t.scrollWidth - t.clientWidth, b = t.scrollHeight - t.clientHeight, P = 4;
|
|
350
|
+
if (H < P && b < P || !E && H < P || !x && b < P)
|
|
351
|
+
return null;
|
|
352
|
+
const O = t.scrollTop, g = t.scrollLeft, y = t.scrollWidth - t.clientWidth - t.scrollLeft, I = t.scrollHeight - t.clientHeight - t.scrollTop, L = {
|
|
353
|
+
top: O,
|
|
354
|
+
right: y,
|
|
355
|
+
bottom: I,
|
|
356
|
+
left: g
|
|
357
|
+
};
|
|
358
|
+
return c(t, {
|
|
359
|
+
scrollable: !0,
|
|
360
|
+
scrollData: L
|
|
361
|
+
}), L;
|
|
362
|
+
}
|
|
363
|
+
function $(t) {
|
|
364
|
+
try {
|
|
365
|
+
if (l === -1) {
|
|
366
|
+
const E = t.parentElement;
|
|
367
|
+
if (!E) return !1;
|
|
368
|
+
try {
|
|
369
|
+
return E.checkVisibility({
|
|
370
|
+
checkOpacity: !0,
|
|
371
|
+
checkVisibilityCSS: !0
|
|
372
|
+
});
|
|
373
|
+
} catch {
|
|
374
|
+
const b = window.getComputedStyle(E);
|
|
375
|
+
return b.display !== "none" && b.visibility !== "hidden" && b.opacity !== "0";
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const i = document.createRange();
|
|
379
|
+
i.selectNodeContents(t);
|
|
380
|
+
const w = i.getClientRects();
|
|
381
|
+
if (!w || w.length === 0)
|
|
382
|
+
return !1;
|
|
383
|
+
let u = !1, a = !1;
|
|
384
|
+
for (const E of w)
|
|
385
|
+
if (E.width > 0 && E.height > 0 && (u = !0, !(E.bottom < -l || E.top > window.innerHeight + l || E.right < -l || E.left > window.innerWidth + l))) {
|
|
386
|
+
a = !0;
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
if (!u || !a)
|
|
390
|
+
return !1;
|
|
391
|
+
const x = t.parentElement;
|
|
392
|
+
if (!x) return !1;
|
|
393
|
+
try {
|
|
394
|
+
return x.checkVisibility({
|
|
395
|
+
checkOpacity: !0,
|
|
396
|
+
checkVisibilityCSS: !0
|
|
397
|
+
});
|
|
398
|
+
} catch {
|
|
399
|
+
const H = window.getComputedStyle(x);
|
|
400
|
+
return H.display !== "none" && H.visibility !== "hidden" && H.opacity !== "0";
|
|
401
|
+
}
|
|
402
|
+
} catch (i) {
|
|
403
|
+
return console.warn("Error checking text node visibility:", i), !1;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
function _(t) {
|
|
407
|
+
if (!t || !t.tagName) return !1;
|
|
408
|
+
const i = /* @__PURE__ */ new Set([
|
|
409
|
+
"body",
|
|
410
|
+
"div",
|
|
411
|
+
"main",
|
|
412
|
+
"article",
|
|
413
|
+
"section",
|
|
414
|
+
"nav",
|
|
415
|
+
"header",
|
|
416
|
+
"footer"
|
|
417
|
+
]), w = t.tagName.toLowerCase();
|
|
418
|
+
return i.has(w) ? !0 : !(/* @__PURE__ */ new Set([
|
|
419
|
+
"svg",
|
|
420
|
+
"script",
|
|
421
|
+
"style",
|
|
422
|
+
"link",
|
|
423
|
+
"meta",
|
|
424
|
+
"noscript",
|
|
425
|
+
"template"
|
|
426
|
+
])).has(w);
|
|
427
|
+
}
|
|
428
|
+
function F(t) {
|
|
429
|
+
const i = B(t);
|
|
430
|
+
return t.offsetWidth > 0 && t.offsetHeight > 0 && (i == null ? void 0 : i.visibility) !== "hidden" && (i == null ? void 0 : i.display) !== "none";
|
|
431
|
+
}
|
|
432
|
+
function A(t) {
|
|
433
|
+
var I, L;
|
|
434
|
+
if (!t || t.nodeType !== Node.ELEMENT_NODE || e.includes(t))
|
|
435
|
+
return !1;
|
|
436
|
+
if (n.includes(t))
|
|
437
|
+
return !0;
|
|
438
|
+
const i = t.tagName.toLowerCase(), w = B(t), u = /* @__PURE__ */ new Set([
|
|
439
|
+
"pointer",
|
|
440
|
+
// Link/clickable elements
|
|
441
|
+
"move",
|
|
442
|
+
// Movable elements
|
|
443
|
+
"text",
|
|
444
|
+
// Text selection
|
|
445
|
+
"grab",
|
|
446
|
+
// Grabbable elements
|
|
447
|
+
"grabbing",
|
|
448
|
+
// Currently grabbing
|
|
449
|
+
"cell",
|
|
450
|
+
// Table cell selection
|
|
451
|
+
"copy",
|
|
452
|
+
// Copy operation
|
|
453
|
+
"alias",
|
|
454
|
+
// Alias creation
|
|
455
|
+
"all-scroll",
|
|
456
|
+
// Scrollable content
|
|
457
|
+
"col-resize",
|
|
458
|
+
// Column resize
|
|
459
|
+
"context-menu",
|
|
460
|
+
// Context menu available
|
|
461
|
+
"crosshair",
|
|
462
|
+
// Precise selection
|
|
463
|
+
"e-resize",
|
|
464
|
+
// East resize
|
|
465
|
+
"ew-resize",
|
|
466
|
+
// East-west resize
|
|
467
|
+
"help",
|
|
468
|
+
// Help available
|
|
469
|
+
"n-resize",
|
|
470
|
+
// North resize
|
|
471
|
+
"ne-resize",
|
|
472
|
+
// Northeast resize
|
|
473
|
+
"nesw-resize",
|
|
474
|
+
// Northeast-southwest resize
|
|
475
|
+
"ns-resize",
|
|
476
|
+
// North-south resize
|
|
477
|
+
"nw-resize",
|
|
478
|
+
// Northwest resize
|
|
479
|
+
"nwse-resize",
|
|
480
|
+
// Northwest-southeast resize
|
|
481
|
+
"row-resize",
|
|
482
|
+
// Row resize
|
|
483
|
+
"s-resize",
|
|
484
|
+
// South resize
|
|
485
|
+
"se-resize",
|
|
486
|
+
// Southeast resize
|
|
487
|
+
"sw-resize",
|
|
488
|
+
// Southwest resize
|
|
489
|
+
"vertical-text",
|
|
490
|
+
// Vertical text selection
|
|
491
|
+
"w-resize",
|
|
492
|
+
// West resize
|
|
493
|
+
"zoom-in",
|
|
494
|
+
// Zoom in
|
|
495
|
+
"zoom-out"
|
|
496
|
+
// Zoom out
|
|
497
|
+
]), a = /* @__PURE__ */ new Set([
|
|
498
|
+
"not-allowed",
|
|
499
|
+
// Action not allowed
|
|
500
|
+
"no-drop",
|
|
501
|
+
// Drop not allowed
|
|
502
|
+
"wait",
|
|
503
|
+
// Processing
|
|
504
|
+
"progress",
|
|
505
|
+
// In progress
|
|
506
|
+
"initial",
|
|
507
|
+
// Initial value
|
|
508
|
+
"inherit"
|
|
509
|
+
// Inherited value
|
|
510
|
+
//? Let's just include all potentially clickable elements that are not specifically blocked
|
|
511
|
+
// 'none', // No cursor
|
|
512
|
+
// 'default', // Default cursor
|
|
513
|
+
// 'auto', // Browser default
|
|
514
|
+
]);
|
|
515
|
+
function x(j) {
|
|
516
|
+
return j.tagName.toLowerCase() === "html" ? !1 : !!(w != null && w.cursor && u.has(w.cursor));
|
|
517
|
+
}
|
|
518
|
+
if (x(t))
|
|
519
|
+
return !0;
|
|
520
|
+
const H = /* @__PURE__ */ new Set([
|
|
521
|
+
"a",
|
|
522
|
+
// Links
|
|
523
|
+
"button",
|
|
524
|
+
// Buttons
|
|
525
|
+
"input",
|
|
526
|
+
// All input types (text, checkbox, radio, etc.)
|
|
527
|
+
"select",
|
|
528
|
+
// Dropdown menus
|
|
529
|
+
"textarea",
|
|
530
|
+
// Text areas
|
|
531
|
+
"details",
|
|
532
|
+
// Expandable details
|
|
533
|
+
"summary",
|
|
534
|
+
// Summary element (clickable part of details)
|
|
535
|
+
"label",
|
|
536
|
+
// Form labels (often clickable)
|
|
537
|
+
"option",
|
|
538
|
+
// Select options
|
|
539
|
+
"optgroup",
|
|
540
|
+
// Option groups
|
|
541
|
+
"fieldset",
|
|
542
|
+
// Form fieldsets (can be interactive with legend)
|
|
543
|
+
"legend"
|
|
544
|
+
// Fieldset legends
|
|
545
|
+
]), b = /* @__PURE__ */ new Set([
|
|
546
|
+
"disabled",
|
|
547
|
+
// Standard disabled attribute
|
|
548
|
+
// 'aria-disabled', // ARIA disabled state
|
|
549
|
+
"readonly"
|
|
550
|
+
// Read-only state
|
|
551
|
+
// 'aria-readonly', // ARIA read-only state
|
|
552
|
+
// 'aria-hidden', // Hidden from accessibility
|
|
553
|
+
// 'hidden', // Hidden attribute
|
|
554
|
+
// 'inert', // Inert attribute
|
|
555
|
+
// 'aria-inert', // ARIA inert state
|
|
556
|
+
// 'tabindex="-1"', // Removed from tab order
|
|
557
|
+
// 'aria-hidden="true"' // Hidden from screen readers
|
|
558
|
+
]);
|
|
559
|
+
if (H.has(i)) {
|
|
560
|
+
if (w != null && w.cursor && a.has(w.cursor))
|
|
561
|
+
return !1;
|
|
562
|
+
for (const j of b)
|
|
563
|
+
if (t.hasAttribute(j) || t.getAttribute(j) === "true" || t.getAttribute(j) === "")
|
|
564
|
+
return !1;
|
|
565
|
+
return !(t.disabled || t.readOnly || t.inert);
|
|
566
|
+
}
|
|
567
|
+
const P = t.getAttribute("role"), O = t.getAttribute("aria-role");
|
|
568
|
+
if (t.getAttribute("contenteditable") === "true" || t.isContentEditable || t.classList && (t.classList.contains("button") || t.classList.contains("dropdown-toggle") || t.getAttribute("data-index") || t.getAttribute("data-toggle") === "dropdown" || t.getAttribute("aria-haspopup") === "true"))
|
|
569
|
+
return !0;
|
|
570
|
+
const g = /* @__PURE__ */ new Set([
|
|
571
|
+
"button",
|
|
572
|
+
// Directly clickable element
|
|
573
|
+
// 'link', // Clickable link
|
|
574
|
+
"menu",
|
|
575
|
+
// Menu container (ARIA menus)
|
|
576
|
+
"menubar",
|
|
577
|
+
// Menu bar container
|
|
578
|
+
"menuitem",
|
|
579
|
+
// Clickable menu item
|
|
580
|
+
"menuitemradio",
|
|
581
|
+
// Radio-style menu item (selectable)
|
|
582
|
+
"menuitemcheckbox",
|
|
583
|
+
// Checkbox-style menu item (toggleable)
|
|
584
|
+
"radio",
|
|
585
|
+
// Radio button (selectable)
|
|
586
|
+
"checkbox",
|
|
587
|
+
// Checkbox (toggleable)
|
|
588
|
+
"tab",
|
|
589
|
+
// Tab (clickable to switch content)
|
|
590
|
+
"switch",
|
|
591
|
+
// Toggle switch (clickable to change state)
|
|
592
|
+
"slider",
|
|
593
|
+
// Slider control (draggable)
|
|
594
|
+
"spinbutton",
|
|
595
|
+
// Number input with up/down controls
|
|
596
|
+
"combobox",
|
|
597
|
+
// Dropdown with text input
|
|
598
|
+
"searchbox",
|
|
599
|
+
// Search input field
|
|
600
|
+
"textbox",
|
|
601
|
+
// Text input field
|
|
602
|
+
"listbox",
|
|
603
|
+
// Selectable list
|
|
604
|
+
"option",
|
|
605
|
+
// Selectable option in a list
|
|
606
|
+
"scrollbar"
|
|
607
|
+
// Scrollable control
|
|
608
|
+
]);
|
|
609
|
+
if (H.has(i) || P && g.has(P) || O && g.has(O)) return !0;
|
|
610
|
+
try {
|
|
611
|
+
if (typeof getEventListeners == "function") {
|
|
612
|
+
const J = getEventListeners(t), nt = ["click", "mousedown", "mouseup", "dblclick"];
|
|
613
|
+
for (const q of nt)
|
|
614
|
+
if (J[q] && J[q].length > 0)
|
|
615
|
+
return !0;
|
|
616
|
+
}
|
|
617
|
+
const j = ((L = (I = t == null ? void 0 : t.ownerDocument) == null ? void 0 : I.defaultView) == null ? void 0 : L.getEventListenersForNode) || window.getEventListenersForNode;
|
|
618
|
+
if (typeof j == "function") {
|
|
619
|
+
const J = j(t), nt = [
|
|
620
|
+
"click",
|
|
621
|
+
"mousedown",
|
|
622
|
+
"mouseup",
|
|
623
|
+
"keydown",
|
|
624
|
+
"keyup",
|
|
625
|
+
"submit",
|
|
626
|
+
"change",
|
|
627
|
+
"input",
|
|
628
|
+
"focus",
|
|
629
|
+
"blur"
|
|
630
|
+
];
|
|
631
|
+
for (const q of nt)
|
|
632
|
+
for (const N of J)
|
|
633
|
+
if (N.type === q)
|
|
634
|
+
return !0;
|
|
635
|
+
}
|
|
636
|
+
const G = ["onclick", "onmousedown", "onmouseup", "ondblclick"];
|
|
637
|
+
for (const J of G)
|
|
638
|
+
if (t.hasAttribute(J) || typeof t[J] == "function")
|
|
639
|
+
return !0;
|
|
640
|
+
} catch {
|
|
641
|
+
}
|
|
642
|
+
return !!V(t);
|
|
643
|
+
}
|
|
644
|
+
function ot(t) {
|
|
645
|
+
if (l === -1)
|
|
646
|
+
return !0;
|
|
647
|
+
const i = D(t);
|
|
648
|
+
if (!i || i.length === 0)
|
|
649
|
+
return !1;
|
|
650
|
+
let w = !1;
|
|
651
|
+
for (const b of i)
|
|
652
|
+
if (b.width > 0 && b.height > 0 && !// Only check non-empty rects
|
|
653
|
+
(b.bottom < -l || b.top > window.innerHeight + l || b.right < -l || b.left > window.innerWidth + l)) {
|
|
654
|
+
w = !0;
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
657
|
+
if (!w)
|
|
658
|
+
return !1;
|
|
659
|
+
if (t.ownerDocument !== window.document)
|
|
660
|
+
return !0;
|
|
661
|
+
let a = Array.from(i).find((b) => b.width > 0 && b.height > 0);
|
|
662
|
+
if (!a)
|
|
663
|
+
return !1;
|
|
664
|
+
const x = t.getRootNode();
|
|
665
|
+
if (x instanceof ShadowRoot) {
|
|
666
|
+
const b = a.left + a.width / 2, P = a.top + a.height / 2;
|
|
667
|
+
try {
|
|
668
|
+
const O = x.elementFromPoint(b, P);
|
|
669
|
+
if (!O) return !1;
|
|
670
|
+
let g = O;
|
|
671
|
+
for (; g && g !== x; ) {
|
|
672
|
+
if (g === t) return !0;
|
|
673
|
+
g = g.parentElement;
|
|
674
|
+
}
|
|
675
|
+
return !1;
|
|
676
|
+
} catch {
|
|
677
|
+
return !0;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
const E = 5;
|
|
681
|
+
return [
|
|
682
|
+
// Initially only this was used, but it was not enough
|
|
683
|
+
{ x: a.left + a.width / 2, y: a.top + a.height / 2 },
|
|
684
|
+
{ x: a.left + E, y: a.top + E },
|
|
685
|
+
// top left
|
|
686
|
+
// { x: rect.right - margin, y: rect.top + margin }, // top right
|
|
687
|
+
// { x: rect.left + margin, y: rect.bottom - margin }, // bottom left
|
|
688
|
+
{ x: a.right - E, y: a.bottom - E }
|
|
689
|
+
// bottom right
|
|
690
|
+
].some(({ x: b, y: P }) => {
|
|
691
|
+
try {
|
|
692
|
+
const O = document.elementFromPoint(b, P);
|
|
693
|
+
if (!O) return !1;
|
|
694
|
+
let g = O;
|
|
695
|
+
for (; g && g !== document.documentElement; ) {
|
|
696
|
+
if (g === t) return !0;
|
|
697
|
+
g = g.parentElement;
|
|
698
|
+
}
|
|
699
|
+
return !1;
|
|
700
|
+
} catch {
|
|
701
|
+
return !0;
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
function rt(t, i) {
|
|
706
|
+
if (i === -1)
|
|
707
|
+
return !0;
|
|
708
|
+
const w = t.getClientRects();
|
|
709
|
+
if (!w || w.length === 0) {
|
|
710
|
+
const u = C(t);
|
|
711
|
+
return !u || u.width === 0 || u.height === 0 ? !1 : !(u.bottom < -i || u.top > window.innerHeight + i || u.right < -i || u.left > window.innerWidth + i);
|
|
712
|
+
}
|
|
713
|
+
for (const u of w)
|
|
714
|
+
if (!(u.width === 0 || u.height === 0) && !(u.bottom < -i || u.top > window.innerHeight + i || u.right < -i || u.left > window.innerWidth + i))
|
|
715
|
+
return !0;
|
|
716
|
+
return !1;
|
|
717
|
+
}
|
|
718
|
+
function z(t) {
|
|
719
|
+
if (!t || t.nodeType !== Node.ELEMENT_NODE) return !1;
|
|
720
|
+
const i = t.tagName.toLowerCase();
|
|
721
|
+
return (/* @__PURE__ */ new Set([
|
|
722
|
+
"a",
|
|
723
|
+
"button",
|
|
724
|
+
"input",
|
|
725
|
+
"select",
|
|
726
|
+
"textarea",
|
|
727
|
+
"details",
|
|
728
|
+
"summary",
|
|
729
|
+
"label"
|
|
730
|
+
])).has(i) ? !0 : t.hasAttribute("onclick") || t.hasAttribute("role") || t.hasAttribute("tabindex") || t.hasAttribute("aria-") || t.hasAttribute("data-action") || t.getAttribute("contenteditable") === "true";
|
|
731
|
+
}
|
|
732
|
+
const Y = /* @__PURE__ */ new Set([
|
|
733
|
+
"a",
|
|
734
|
+
"button",
|
|
735
|
+
"input",
|
|
736
|
+
"select",
|
|
737
|
+
"textarea",
|
|
738
|
+
"summary",
|
|
739
|
+
"details",
|
|
740
|
+
"label",
|
|
741
|
+
"option"
|
|
742
|
+
]), K = /* @__PURE__ */ new Set([
|
|
743
|
+
"button",
|
|
744
|
+
"link",
|
|
745
|
+
"menuitem",
|
|
746
|
+
"menuitemradio",
|
|
747
|
+
"menuitemcheckbox",
|
|
748
|
+
"radio",
|
|
749
|
+
"checkbox",
|
|
750
|
+
"tab",
|
|
751
|
+
"switch",
|
|
752
|
+
"slider",
|
|
753
|
+
"spinbutton",
|
|
754
|
+
"combobox",
|
|
755
|
+
"searchbox",
|
|
756
|
+
"textbox",
|
|
757
|
+
"listbox",
|
|
758
|
+
"option",
|
|
759
|
+
"scrollbar"
|
|
760
|
+
]);
|
|
761
|
+
function et(t) {
|
|
762
|
+
if (!t || t.nodeType !== Node.ELEMENT_NODE || !F(t)) return !1;
|
|
763
|
+
const i = t.hasAttribute("role") || t.hasAttribute("tabindex") || t.hasAttribute("onclick") || typeof t.onclick == "function", w = /\b(btn|clickable|menu|item|entry|link)\b/i.test(
|
|
764
|
+
t.className || ""
|
|
765
|
+
), u = !!t.closest('button,a,[role="button"],.menu,.dropdown,.list,.toolbar'), a = [...t.children].some(F), x = t.parentElement && t.parentElement.isSameNode(document.body);
|
|
766
|
+
return (A(t) || i || w) && a && u && !x;
|
|
767
|
+
}
|
|
768
|
+
function it(t) {
|
|
769
|
+
var u, a;
|
|
770
|
+
if (!t || t.nodeType !== Node.ELEMENT_NODE)
|
|
771
|
+
return !1;
|
|
772
|
+
const i = t.tagName.toLowerCase(), w = t.getAttribute("role");
|
|
773
|
+
if (i === "iframe" || Y.has(i) || w && K.has(w) || t.isContentEditable || t.getAttribute("contenteditable") === "true" || t.hasAttribute("data-testid") || t.hasAttribute("data-cy") || t.hasAttribute("data-test") || t.hasAttribute("onclick") || typeof t.onclick == "function")
|
|
774
|
+
return !0;
|
|
775
|
+
try {
|
|
776
|
+
const x = ((a = (u = t == null ? void 0 : t.ownerDocument) == null ? void 0 : u.defaultView) == null ? void 0 : a.getEventListenersForNode) || window.getEventListenersForNode;
|
|
777
|
+
if (typeof x == "function") {
|
|
778
|
+
const H = x(t), b = [
|
|
779
|
+
"click",
|
|
780
|
+
"mousedown",
|
|
781
|
+
"mouseup",
|
|
782
|
+
"keydown",
|
|
783
|
+
"keyup",
|
|
784
|
+
"submit",
|
|
785
|
+
"change",
|
|
786
|
+
"input",
|
|
787
|
+
"focus",
|
|
788
|
+
"blur"
|
|
789
|
+
];
|
|
790
|
+
for (const P of b)
|
|
791
|
+
for (const O of H)
|
|
792
|
+
if (O.type === P)
|
|
793
|
+
return !0;
|
|
794
|
+
}
|
|
795
|
+
if ([
|
|
796
|
+
"onmousedown",
|
|
797
|
+
"onmouseup",
|
|
798
|
+
"onkeydown",
|
|
799
|
+
"onkeyup",
|
|
800
|
+
"onsubmit",
|
|
801
|
+
"onchange",
|
|
802
|
+
"oninput",
|
|
803
|
+
"onfocus",
|
|
804
|
+
"onblur"
|
|
805
|
+
].some((H) => t.hasAttribute(H)))
|
|
806
|
+
return !0;
|
|
807
|
+
} catch {
|
|
808
|
+
}
|
|
809
|
+
return !!et(t);
|
|
810
|
+
}
|
|
811
|
+
function st(t, i, w, u) {
|
|
812
|
+
if (!t.isInteractive) return !1;
|
|
813
|
+
let a = !1;
|
|
814
|
+
return u ? it(i) ? a = !0 : a = !1 : a = !0, a && (t.isInViewport = rt(i, l), (t.isInViewport || l === -1) && (t.highlightIndex = v++, h)) ? (s >= 0 ? s === t.highlightIndex && k(i, t.highlightIndex, w) : k(i, t.highlightIndex, w), !0) : !1;
|
|
815
|
+
}
|
|
816
|
+
function Z(t, i = null, w = !1) {
|
|
817
|
+
var E, H, b, P, O;
|
|
818
|
+
if (!t || t.id === U || t.nodeType !== Node.ELEMENT_NODE && t.nodeType !== Node.TEXT_NODE || !t || t.id === U || ((E = t.dataset) == null ? void 0 : E.browserUseIgnore) === "true" || t.getAttribute && t.getAttribute("aria-hidden") === "true")
|
|
819
|
+
return null;
|
|
820
|
+
if (t === document.body) {
|
|
821
|
+
const g = {
|
|
822
|
+
tagName: "body",
|
|
823
|
+
attributes: {},
|
|
824
|
+
xpath: "/body",
|
|
825
|
+
children: []
|
|
826
|
+
};
|
|
827
|
+
for (const I of t.childNodes) {
|
|
828
|
+
const L = Z(I, i, !1);
|
|
829
|
+
L && g.children.push(L);
|
|
830
|
+
}
|
|
831
|
+
const y = `${R.current++}`;
|
|
832
|
+
return S[y] = g, y;
|
|
833
|
+
}
|
|
834
|
+
if (t.nodeType !== Node.ELEMENT_NODE && t.nodeType !== Node.TEXT_NODE)
|
|
835
|
+
return null;
|
|
836
|
+
if (t.nodeType === Node.TEXT_NODE) {
|
|
837
|
+
const g = (H = t.textContent) == null ? void 0 : H.trim();
|
|
838
|
+
if (!g)
|
|
839
|
+
return null;
|
|
840
|
+
const y = t.parentElement;
|
|
841
|
+
if (!y || y.tagName.toLowerCase() === "script")
|
|
842
|
+
return null;
|
|
843
|
+
const I = `${R.current++}`;
|
|
844
|
+
return S[I] = {
|
|
845
|
+
type: "TEXT_NODE",
|
|
846
|
+
text: g,
|
|
847
|
+
isVisible: $(t)
|
|
848
|
+
}, I;
|
|
849
|
+
}
|
|
850
|
+
if (t.nodeType === Node.ELEMENT_NODE && !_(t))
|
|
851
|
+
return null;
|
|
852
|
+
if (l !== -1 && !t.shadowRoot) {
|
|
853
|
+
const g = C(t), y = B(t), I = y && (y.position === "fixed" || y.position === "sticky"), L = t.offsetWidth > 0 || t.offsetHeight > 0;
|
|
854
|
+
if (!g || !I && !L && (g.bottom < -l || g.top > window.innerHeight + l || g.right < -l || g.left > window.innerWidth + l))
|
|
855
|
+
return null;
|
|
856
|
+
}
|
|
857
|
+
const u = {
|
|
858
|
+
tagName: t.tagName.toLowerCase(),
|
|
859
|
+
attributes: {},
|
|
860
|
+
/**
|
|
861
|
+
* @edit no need for xpath
|
|
862
|
+
*/
|
|
863
|
+
// xpath: getXPathTree(node, true),
|
|
864
|
+
children: []
|
|
865
|
+
};
|
|
866
|
+
if (z(t) || t.tagName.toLowerCase() === "iframe" || t.tagName.toLowerCase() === "body") {
|
|
867
|
+
const g = ((b = t.getAttributeNames) == null ? void 0 : b.call(t)) || [];
|
|
868
|
+
for (const y of g) {
|
|
869
|
+
const I = t.getAttribute(y);
|
|
870
|
+
u.attributes[y] = I;
|
|
871
|
+
}
|
|
872
|
+
t.tagName.toLowerCase() === "input" && (t.type === "checkbox" || t.type === "radio") && (u.attributes.checked = t.checked ? "true" : "false");
|
|
873
|
+
}
|
|
874
|
+
let a = !1;
|
|
875
|
+
if (t.nodeType === Node.ELEMENT_NODE && (u.isVisible = F(t), u.isVisible)) {
|
|
876
|
+
u.isTopElement = ot(t);
|
|
877
|
+
const g = t.getAttribute("role"), y = g === "menu" || g === "menubar" || g === "listbox";
|
|
878
|
+
(u.isTopElement || y) && (u.isInteractive = A(t), a = st(u, t, i, w), u.ref = t);
|
|
879
|
+
}
|
|
880
|
+
if (t.tagName) {
|
|
881
|
+
const g = t.tagName.toLowerCase();
|
|
882
|
+
if (g === "iframe")
|
|
883
|
+
try {
|
|
884
|
+
const y = t.contentDocument || ((P = t.contentWindow) == null ? void 0 : P.document);
|
|
885
|
+
if (y)
|
|
886
|
+
for (const I of y.childNodes) {
|
|
887
|
+
const L = Z(I, t, !1);
|
|
888
|
+
L && u.children.push(L);
|
|
889
|
+
}
|
|
890
|
+
} catch (y) {
|
|
891
|
+
console.warn("Unable to access iframe:", y);
|
|
892
|
+
}
|
|
893
|
+
else if (t.isContentEditable || t.getAttribute("contenteditable") === "true" || t.id === "tinymce" || t.classList.contains("mce-content-body") || g === "body" && ((O = t.getAttribute("data-id")) != null && O.startsWith("mce_")))
|
|
894
|
+
for (const y of t.childNodes) {
|
|
895
|
+
const I = Z(y, i, a);
|
|
896
|
+
I && u.children.push(I);
|
|
897
|
+
}
|
|
898
|
+
else {
|
|
899
|
+
if (t.shadowRoot) {
|
|
900
|
+
u.shadowRoot = !0;
|
|
901
|
+
for (const y of t.shadowRoot.childNodes) {
|
|
902
|
+
const I = Z(y, i, a);
|
|
903
|
+
I && u.children.push(I);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
for (const y of t.childNodes) {
|
|
907
|
+
const L = Z(y, i, a || w);
|
|
908
|
+
L && u.children.push(L);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
if (u.tagName === "a" && u.children.length === 0 && !u.attributes.href) {
|
|
913
|
+
const g = C(t);
|
|
914
|
+
if (!(g && g.width > 0 && g.height > 0 || t.offsetWidth > 0 || t.offsetHeight > 0))
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
u.extra = m.get(t) || null;
|
|
918
|
+
const x = `${R.current++}`;
|
|
919
|
+
return S[x] = u, x;
|
|
920
|
+
}
|
|
921
|
+
const lt = Z(document.body);
|
|
922
|
+
return p.clearCache(), { rootId: lt, map: S };
|
|
923
|
+
}, newElementsCache = /* @__PURE__ */ new WeakMap();
|
|
924
|
+
function getFlatTree(o) {
|
|
925
|
+
const e = [];
|
|
926
|
+
for (const h of o.interactiveBlacklist || [])
|
|
927
|
+
typeof h == "function" ? e.push(h()) : e.push(h);
|
|
928
|
+
const n = [];
|
|
929
|
+
for (const h of o.interactiveWhitelist || [])
|
|
930
|
+
typeof h == "function" ? n.push(h()) : n.push(h);
|
|
931
|
+
const r = domTree({
|
|
932
|
+
doHighlightElements: !0,
|
|
933
|
+
debugMode: !0,
|
|
934
|
+
focusHighlightIndex: -1,
|
|
935
|
+
viewportExpansion: VIEWPORT_EXPANSION,
|
|
936
|
+
interactiveBlacklist: e,
|
|
937
|
+
interactiveWhitelist: n,
|
|
938
|
+
highlightOpacity: o.highlightOpacity ?? 0,
|
|
939
|
+
highlightLabelOpacity: o.highlightLabelOpacity ?? 0.1
|
|
940
|
+
}), d = window.location.href;
|
|
941
|
+
for (const h in r.map) {
|
|
942
|
+
const s = r.map[h];
|
|
943
|
+
if (s.isInteractive && s.ref) {
|
|
944
|
+
const l = s.ref;
|
|
945
|
+
newElementsCache.has(l) || (newElementsCache.set(l, d), s.isNew = !0);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
return r;
|
|
949
|
+
}
|
|
950
|
+
function flatTreeToString(o, e) {
|
|
951
|
+
const n = [
|
|
952
|
+
"title",
|
|
953
|
+
"type",
|
|
954
|
+
"checked",
|
|
955
|
+
"name",
|
|
956
|
+
"role",
|
|
957
|
+
"value",
|
|
958
|
+
"placeholder",
|
|
959
|
+
"data-date-format",
|
|
960
|
+
"alt",
|
|
961
|
+
"aria-label",
|
|
962
|
+
"aria-expanded",
|
|
963
|
+
"data-state",
|
|
964
|
+
"aria-checked",
|
|
965
|
+
// @edit added for better form handling
|
|
966
|
+
"id",
|
|
967
|
+
"for",
|
|
968
|
+
// for jump check
|
|
969
|
+
"target",
|
|
970
|
+
// absolute 定位的下拉菜单
|
|
971
|
+
"aria-haspopup",
|
|
972
|
+
"aria-controls",
|
|
973
|
+
"aria-owns"
|
|
974
|
+
], r = [...e || [], ...n], d = (c, p) => c.length > p ? c.substring(0, p) + "..." : c, h = (c) => {
|
|
975
|
+
const p = o.map[c];
|
|
976
|
+
if (!p) return null;
|
|
977
|
+
if (p.type === "TEXT_NODE") {
|
|
978
|
+
const C = p;
|
|
979
|
+
return {
|
|
980
|
+
type: "text",
|
|
981
|
+
text: C.text,
|
|
982
|
+
isVisible: C.isVisible,
|
|
983
|
+
parent: null,
|
|
984
|
+
children: []
|
|
985
|
+
};
|
|
986
|
+
} else {
|
|
987
|
+
const C = p, B = [];
|
|
988
|
+
if (C.children)
|
|
989
|
+
for (const D of C.children) {
|
|
990
|
+
const S = h(D);
|
|
991
|
+
S && (S.parent = null, B.push(S));
|
|
992
|
+
}
|
|
993
|
+
return {
|
|
994
|
+
type: "element",
|
|
995
|
+
tagName: C.tagName,
|
|
996
|
+
attributes: C.attributes ?? {},
|
|
997
|
+
isVisible: C.isVisible ?? !1,
|
|
998
|
+
isInteractive: C.isInteractive ?? !1,
|
|
999
|
+
isTopElement: C.isTopElement ?? !1,
|
|
1000
|
+
isNew: C.isNew ?? !1,
|
|
1001
|
+
highlightIndex: C.highlightIndex,
|
|
1002
|
+
parent: null,
|
|
1003
|
+
children: B,
|
|
1004
|
+
extra: C.extra ?? {}
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
}, s = (c, p = null) => {
|
|
1008
|
+
c.parent = p;
|
|
1009
|
+
for (const C of c.children)
|
|
1010
|
+
s(C, c);
|
|
1011
|
+
}, l = h(o.rootId);
|
|
1012
|
+
if (!l) return "";
|
|
1013
|
+
s(l);
|
|
1014
|
+
const f = (c) => {
|
|
1015
|
+
let p = c.parent;
|
|
1016
|
+
for (; p; ) {
|
|
1017
|
+
if (p.type === "element" && p.highlightIndex !== void 0)
|
|
1018
|
+
return !0;
|
|
1019
|
+
p = p.parent;
|
|
1020
|
+
}
|
|
1021
|
+
return !1;
|
|
1022
|
+
}, v = (c, p, C) => {
|
|
1023
|
+
var S, R, U, k;
|
|
1024
|
+
let B = p;
|
|
1025
|
+
const D = " ".repeat(p);
|
|
1026
|
+
if (c.type === "element") {
|
|
1027
|
+
if (c.highlightIndex !== void 0) {
|
|
1028
|
+
B += 1;
|
|
1029
|
+
const V = getAllTextTillNextClickableElement(c);
|
|
1030
|
+
let $ = "";
|
|
1031
|
+
if (r.length > 0 && c.attributes) {
|
|
1032
|
+
const A = {};
|
|
1033
|
+
for (const z of r) {
|
|
1034
|
+
const Y = c.attributes[z];
|
|
1035
|
+
Y && Y.trim() !== "" && (A[z] = Y.trim());
|
|
1036
|
+
}
|
|
1037
|
+
const ot = r.filter((z) => z in A);
|
|
1038
|
+
if (ot.length > 1) {
|
|
1039
|
+
const z = /* @__PURE__ */ new Set(), Y = {};
|
|
1040
|
+
for (const K of ot) {
|
|
1041
|
+
const et = A[K];
|
|
1042
|
+
et.length > 5 && (et in Y ? z.add(K) : Y[et] = K);
|
|
1043
|
+
}
|
|
1044
|
+
for (const K of z)
|
|
1045
|
+
delete A[K];
|
|
1046
|
+
}
|
|
1047
|
+
A.role === c.tagName && delete A.role;
|
|
1048
|
+
const rt = ["aria-label", "placeholder", "title"];
|
|
1049
|
+
for (const z of rt)
|
|
1050
|
+
A[z] && A[z].toLowerCase().trim() === V.toLowerCase().trim() && delete A[z];
|
|
1051
|
+
Object.keys(A).length > 0 && ($ = Object.entries(A).map(([z, Y]) => `${z}=${d(Y, 20)}`).join(" "));
|
|
1052
|
+
}
|
|
1053
|
+
const _ = c.isNew ? `*[${c.highlightIndex}]` : `[${c.highlightIndex}]`;
|
|
1054
|
+
let F = `${D}${_}<${c.tagName ?? ""}`;
|
|
1055
|
+
if ($ && (F += ` ${$}`), c.extra && c.extra.scrollable) {
|
|
1056
|
+
let A = "";
|
|
1057
|
+
(S = c.extra.scrollData) != null && S.left && (A += `left=${c.extra.scrollData.left}, `), (R = c.extra.scrollData) != null && R.top && (A += `top=${c.extra.scrollData.top}, `), (U = c.extra.scrollData) != null && U.right && (A += `right=${c.extra.scrollData.right}, `), (k = c.extra.scrollData) != null && k.bottom && (A += `bottom=${c.extra.scrollData.bottom}`), F += ` data-scrollable="${A}"`;
|
|
1058
|
+
}
|
|
1059
|
+
if (V) {
|
|
1060
|
+
const A = V.trim();
|
|
1061
|
+
$ || (F += " "), F += `>${A}`;
|
|
1062
|
+
} else $ || (F += " ");
|
|
1063
|
+
F += " />", C.push(F);
|
|
1064
|
+
}
|
|
1065
|
+
for (const V of c.children)
|
|
1066
|
+
v(V, B, C);
|
|
1067
|
+
} else if (c.type === "text") {
|
|
1068
|
+
if (f(c))
|
|
1069
|
+
return;
|
|
1070
|
+
c.parent && c.parent.type === "element" && c.parent.isVisible && c.parent.isTopElement && C.push(`${D}${c.text ?? ""}`);
|
|
1071
|
+
}
|
|
1072
|
+
}, m = [];
|
|
1073
|
+
return v(l, 0, m), m.join(`
|
|
1074
|
+
`);
|
|
1075
|
+
}
|
|
1076
|
+
const getAllTextTillNextClickableElement = (o, e = -1) => {
|
|
1077
|
+
const n = [], r = (d, h) => {
|
|
1078
|
+
if (!(e !== -1 && h > e) && !(d.type === "element" && d !== o && d.highlightIndex !== void 0)) {
|
|
1079
|
+
if (d.type === "text" && d.text)
|
|
1080
|
+
n.push(d.text);
|
|
1081
|
+
else if (d.type === "element")
|
|
1082
|
+
for (const s of d.children)
|
|
1083
|
+
r(s, h + 1);
|
|
1084
|
+
}
|
|
1085
|
+
};
|
|
1086
|
+
return r(o, 0), n.join(`
|
|
1087
|
+
`).trim();
|
|
1088
|
+
};
|
|
1089
|
+
function getSelectorMap(o) {
|
|
1090
|
+
const e = /* @__PURE__ */ new Map(), n = Object.keys(o.map);
|
|
1091
|
+
for (const r of n) {
|
|
1092
|
+
const d = o.map[r];
|
|
1093
|
+
d.isInteractive && typeof d.highlightIndex == "number" && e.set(d.highlightIndex, d);
|
|
1094
|
+
}
|
|
1095
|
+
return e;
|
|
1096
|
+
}
|
|
1097
|
+
function getElementTextMap(o) {
|
|
1098
|
+
const e = o.split(`
|
|
1099
|
+
`).map((r) => r.trim()).filter((r) => r.length > 0), n = /* @__PURE__ */ new Map();
|
|
1100
|
+
for (const r of e) {
|
|
1101
|
+
const h = /^\[(\d+)\]<[^>]+>([^<]*)/.exec(r);
|
|
1102
|
+
if (h) {
|
|
1103
|
+
const s = parseInt(h[1], 10);
|
|
1104
|
+
n.set(s, r);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
return n;
|
|
1108
|
+
}
|
|
1109
|
+
function cleanUpHighlights() {
|
|
1110
|
+
const o = window._highlightCleanupFunctions || [];
|
|
1111
|
+
for (const e of o)
|
|
1112
|
+
typeof e == "function" && e();
|
|
1113
|
+
window._highlightCleanupFunctions = [];
|
|
1114
|
+
}
|
|
1115
|
+
window.addEventListener("popstate", () => {
|
|
1116
|
+
cleanUpHighlights();
|
|
1117
|
+
});
|
|
1118
|
+
window.addEventListener("hashchange", () => {
|
|
1119
|
+
cleanUpHighlights();
|
|
1120
|
+
});
|
|
1121
|
+
window.addEventListener("beforeunload", () => {
|
|
1122
|
+
cleanUpHighlights();
|
|
1123
|
+
});
|
|
1124
|
+
const navigation = window.navigation;
|
|
1125
|
+
if (navigation && typeof navigation.addEventListener == "function")
|
|
1126
|
+
navigation.addEventListener("navigate", () => {
|
|
1127
|
+
cleanUpHighlights();
|
|
1128
|
+
});
|
|
1129
|
+
else {
|
|
1130
|
+
let o = window.location.href;
|
|
1131
|
+
setInterval(() => {
|
|
1132
|
+
window.location.href !== o && (o = window.location.href, cleanUpHighlights());
|
|
1133
|
+
}, 500);
|
|
1134
|
+
}
|
|
1135
|
+
function getPageInfo() {
|
|
1136
|
+
const o = window.innerWidth, e = window.innerHeight, n = Math.max(document.documentElement.scrollWidth, document.body.scrollWidth || 0), r = Math.max(
|
|
1137
|
+
document.documentElement.scrollHeight,
|
|
1138
|
+
document.body.scrollHeight || 0
|
|
1139
|
+
), d = window.scrollX || window.pageXOffset || document.documentElement.scrollLeft || 0, h = window.scrollY || window.pageYOffset || document.documentElement.scrollTop || 0, s = Math.max(0, r - (window.innerHeight + h)), l = Math.max(0, n - (window.innerWidth + d));
|
|
1140
|
+
return {
|
|
1141
|
+
// Current viewport dimensions
|
|
1142
|
+
viewport_width: o,
|
|
1143
|
+
viewport_height: e,
|
|
1144
|
+
// Total page dimensions
|
|
1145
|
+
page_width: n,
|
|
1146
|
+
page_height: r,
|
|
1147
|
+
// Current scroll position
|
|
1148
|
+
scroll_x: d,
|
|
1149
|
+
scroll_y: h,
|
|
1150
|
+
pixels_above: h,
|
|
1151
|
+
pixels_below: s,
|
|
1152
|
+
pages_above: e > 0 ? h / e : 0,
|
|
1153
|
+
pages_below: e > 0 ? s / e : 0,
|
|
1154
|
+
total_pages: e > 0 ? r / e : 0,
|
|
1155
|
+
current_page_position: h / Math.max(1, r - e),
|
|
1156
|
+
pixels_left: d,
|
|
1157
|
+
pixels_right: l
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
function patchReact(o) {
|
|
1161
|
+
const e = document.querySelectorAll(
|
|
1162
|
+
'[data-reactroot], [data-reactid], [data-react-checksum], #root, #app, [id^="root-"], [id^="app-"], #adex-wrapper, #adex-root'
|
|
1163
|
+
);
|
|
1164
|
+
for (const n of e)
|
|
1165
|
+
n.setAttribute("data-page-agent-not-interactive", "true");
|
|
1166
|
+
}
|
|
1167
|
+
class PageController extends EventTarget {
|
|
1168
|
+
constructor(o = {}) {
|
|
1169
|
+
super(), this.flatTree = null, this.selectorMap = /* @__PURE__ */ new Map(), this.elementTextMap = /* @__PURE__ */ new Map(), this.simplifiedHTML = "<EMPTY>", this.lastTimeUpdate = 0, this.isIndexed = !1, this.mask = null, this.maskReady = null, this.config = o, patchReact(), o.enableMask && this.initMask();
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Initialize mask asynchronously (dynamic import to avoid CSS loading in Node)
|
|
1173
|
+
*/
|
|
1174
|
+
initMask() {
|
|
1175
|
+
this.maskReady === null && (this.maskReady = (async () => {
|
|
1176
|
+
const { SimulatorMask: o } = await import("./SimulatorMask-74sRLkJv.js");
|
|
1177
|
+
this.mask = new o();
|
|
1178
|
+
})());
|
|
1179
|
+
}
|
|
1180
|
+
// ======= State Queries =======
|
|
1181
|
+
/**
|
|
1182
|
+
* Get current page URL
|
|
1183
|
+
*/
|
|
1184
|
+
async getCurrentUrl() {
|
|
1185
|
+
return window.location.href;
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Get last tree update timestamp
|
|
1189
|
+
*/
|
|
1190
|
+
async getLastUpdateTime() {
|
|
1191
|
+
return this.lastTimeUpdate;
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Get structured browser state for LLM consumption.
|
|
1195
|
+
* Automatically calls updateTree() to refresh the DOM state.
|
|
1196
|
+
*/
|
|
1197
|
+
async getBrowserState() {
|
|
1198
|
+
const o = window.location.href, e = document.title, n = getPageInfo(), r = this.config.viewportExpansion ?? VIEWPORT_EXPANSION;
|
|
1199
|
+
await this.updateTree();
|
|
1200
|
+
const d = this.simplifiedHTML, h = `Current Page: [${e}](${o})`, s = `Page info: ${n.viewport_width}x${n.viewport_height}px viewport, ${n.page_width}x${n.page_height}px total page size, ${n.pages_above.toFixed(1)} pages above, ${n.pages_below.toFixed(1)} pages below, ${n.total_pages.toFixed(1)} total pages, at ${(n.current_page_position * 100).toFixed(0)}% of page`, l = r === -1 ? "Interactive elements from top layer of the current page (full page):" : "Interactive elements from top layer of the current page inside the viewport:", v = n.pixels_above > 4 && r !== -1 ? `... ${n.pixels_above} pixels above (${n.pages_above.toFixed(1)} pages) - scroll to see more ...` : "[Start of page]", m = `${h}
|
|
1201
|
+
${s}
|
|
1202
|
+
|
|
1203
|
+
${l}
|
|
1204
|
+
|
|
1205
|
+
${v}`, p = n.pixels_below > 4 && r !== -1 ? `... ${n.pixels_below} pixels below (${n.pages_below.toFixed(1)} pages) - scroll to see more ...` : "[End of page]";
|
|
1206
|
+
return { url: o, title: e, header: m, content: d, footer: p };
|
|
1207
|
+
}
|
|
1208
|
+
// ======= DOM Tree Operations =======
|
|
1209
|
+
/**
|
|
1210
|
+
* Update DOM tree, returns simplified HTML for LLM.
|
|
1211
|
+
* This is the main method to refresh the page state.
|
|
1212
|
+
* Automatically bypasses mask during DOM extraction if enabled.
|
|
1213
|
+
*/
|
|
1214
|
+
async updateTree() {
|
|
1215
|
+
this.dispatchEvent(new Event("beforeUpdate")), this.lastTimeUpdate = Date.now(), this.mask && (this.mask.wrapper.style.pointerEvents = "none"), cleanUpHighlights();
|
|
1216
|
+
const o = [
|
|
1217
|
+
...this.config.interactiveBlacklist || [],
|
|
1218
|
+
...document.querySelectorAll("[data-page-agent-not-interactive]").values()
|
|
1219
|
+
];
|
|
1220
|
+
return this.flatTree = getFlatTree({
|
|
1221
|
+
...this.config,
|
|
1222
|
+
interactiveBlacklist: o
|
|
1223
|
+
}), this.simplifiedHTML = flatTreeToString(this.flatTree, this.config.include_attributes), this.selectorMap.clear(), this.selectorMap = getSelectorMap(this.flatTree), this.elementTextMap.clear(), this.elementTextMap = getElementTextMap(this.simplifiedHTML), this.isIndexed = !0, this.mask && (this.mask.wrapper.style.pointerEvents = "auto"), this.dispatchEvent(new Event("afterUpdate")), this.simplifiedHTML;
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Clean up all element highlights
|
|
1227
|
+
*/
|
|
1228
|
+
async cleanUpHighlights() {
|
|
1229
|
+
cleanUpHighlights();
|
|
1230
|
+
}
|
|
1231
|
+
// ======= Element Actions =======
|
|
1232
|
+
/**
|
|
1233
|
+
* Ensure the tree has been indexed before any index-based operation.
|
|
1234
|
+
* Throws if updateTree() hasn't been called yet.
|
|
1235
|
+
*/
|
|
1236
|
+
assertIndexed() {
|
|
1237
|
+
if (!this.isIndexed)
|
|
1238
|
+
throw new Error("DOM tree not indexed yet. Can not perform actions on elements.");
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Click element by index
|
|
1242
|
+
*/
|
|
1243
|
+
async clickElement(o) {
|
|
1244
|
+
try {
|
|
1245
|
+
this.assertIndexed();
|
|
1246
|
+
const e = getElementByIndex(this.selectorMap, o), n = this.elementTextMap.get(o);
|
|
1247
|
+
return await clickElement(e), e instanceof HTMLAnchorElement && e.target === "_blank" ? {
|
|
1248
|
+
success: !0,
|
|
1249
|
+
message: `✅ Clicked element (${n ?? o}). ⚠️ Link opens in a new tab. You are not capable of reading new tabs.`
|
|
1250
|
+
} : {
|
|
1251
|
+
success: !0,
|
|
1252
|
+
message: `✅ Clicked element (${n ?? o}).`
|
|
1253
|
+
};
|
|
1254
|
+
} catch (e) {
|
|
1255
|
+
return {
|
|
1256
|
+
success: !1,
|
|
1257
|
+
message: `❌ Failed to click element: ${e}`
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Input text into element by index
|
|
1263
|
+
*/
|
|
1264
|
+
async inputText(o, e) {
|
|
1265
|
+
try {
|
|
1266
|
+
this.assertIndexed();
|
|
1267
|
+
const n = getElementByIndex(this.selectorMap, o), r = this.elementTextMap.get(o);
|
|
1268
|
+
return await inputTextElement(n, e), {
|
|
1269
|
+
success: !0,
|
|
1270
|
+
message: `✅ Input text (${e}) into element (${r ?? o}).`
|
|
1271
|
+
};
|
|
1272
|
+
} catch (n) {
|
|
1273
|
+
return {
|
|
1274
|
+
success: !1,
|
|
1275
|
+
message: `❌ Failed to input text: ${n}`
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Select dropdown option by index and option text
|
|
1281
|
+
*/
|
|
1282
|
+
async selectOption(o, e) {
|
|
1283
|
+
try {
|
|
1284
|
+
this.assertIndexed();
|
|
1285
|
+
const n = getElementByIndex(this.selectorMap, o), r = this.elementTextMap.get(o);
|
|
1286
|
+
return await selectOptionElement(n, e), {
|
|
1287
|
+
success: !0,
|
|
1288
|
+
message: `✅ Selected option (${e}) in element (${r ?? o}).`
|
|
1289
|
+
};
|
|
1290
|
+
} catch (n) {
|
|
1291
|
+
return {
|
|
1292
|
+
success: !1,
|
|
1293
|
+
message: `❌ Failed to select option: ${n}`
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Scroll vertically
|
|
1299
|
+
*/
|
|
1300
|
+
async scroll(o) {
|
|
1301
|
+
try {
|
|
1302
|
+
const { down: e, numPages: n, pixels: r, index: d } = o;
|
|
1303
|
+
this.assertIndexed();
|
|
1304
|
+
const h = r ?? n * (e ? 1 : -1) * window.innerHeight, s = d !== void 0 ? getElementByIndex(this.selectorMap, d) : null;
|
|
1305
|
+
return {
|
|
1306
|
+
success: !0,
|
|
1307
|
+
message: await scrollVertically(e, h, s)
|
|
1308
|
+
};
|
|
1309
|
+
} catch (e) {
|
|
1310
|
+
return {
|
|
1311
|
+
success: !1,
|
|
1312
|
+
message: `❌ Failed to scroll: ${e}`
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Scroll horizontally
|
|
1318
|
+
*/
|
|
1319
|
+
async scrollHorizontally(o) {
|
|
1320
|
+
try {
|
|
1321
|
+
const { right: e, pixels: n, index: r } = o;
|
|
1322
|
+
this.assertIndexed();
|
|
1323
|
+
const d = n * (e ? 1 : -1), h = r !== void 0 ? getElementByIndex(this.selectorMap, r) : null;
|
|
1324
|
+
return {
|
|
1325
|
+
success: !0,
|
|
1326
|
+
message: await scrollHorizontally(e, d, h)
|
|
1327
|
+
};
|
|
1328
|
+
} catch (e) {
|
|
1329
|
+
return {
|
|
1330
|
+
success: !1,
|
|
1331
|
+
message: `❌ Failed to scroll horizontally: ${e}`
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Execute arbitrary JavaScript on the page
|
|
1337
|
+
*/
|
|
1338
|
+
async executeJavascript(script) {
|
|
1339
|
+
try {
|
|
1340
|
+
const asyncFunction = eval(`(async () => { ${script} })`), result = await asyncFunction();
|
|
1341
|
+
return {
|
|
1342
|
+
success: !0,
|
|
1343
|
+
message: `✅ Executed JavaScript. Result: ${result}`
|
|
1344
|
+
};
|
|
1345
|
+
} catch (o) {
|
|
1346
|
+
return {
|
|
1347
|
+
success: !1,
|
|
1348
|
+
message: `❌ Error executing JavaScript: ${o}`
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
// ======= Mask Operations =======
|
|
1353
|
+
/**
|
|
1354
|
+
* Show the visual mask overlay.
|
|
1355
|
+
* Only works after mask is setup.
|
|
1356
|
+
*/
|
|
1357
|
+
async showMask() {
|
|
1358
|
+
var o;
|
|
1359
|
+
await this.maskReady, (o = this.mask) == null || o.show();
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Hide the visual mask overlay.
|
|
1363
|
+
* Only works after mask is setup.
|
|
1364
|
+
*/
|
|
1365
|
+
async hideMask() {
|
|
1366
|
+
var o;
|
|
1367
|
+
await this.maskReady, (o = this.mask) == null || o.hide();
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Dispose and clean up resources
|
|
1371
|
+
*/
|
|
1372
|
+
dispose() {
|
|
1373
|
+
var o;
|
|
1374
|
+
cleanUpHighlights(), this.flatTree = null, this.selectorMap.clear(), this.elementTextMap.clear(), this.simplifiedHTML = "<EMPTY>", this.isIndexed = !1, (o = this.mask) == null || o.dispose(), this.mask = null;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
const PageController$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1378
|
+
__proto__: null,
|
|
1379
|
+
PageController
|
|
1380
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
1381
|
+
let injectedPageController = null, PageControllerModule = null;
|
|
1382
|
+
function setPageController(o) {
|
|
1383
|
+
injectedPageController = o;
|
|
1384
|
+
}
|
|
1385
|
+
async function getPageController() {
|
|
1386
|
+
if (injectedPageController)
|
|
1387
|
+
return injectedPageController;
|
|
1388
|
+
if (!PageControllerModule)
|
|
1389
|
+
try {
|
|
1390
|
+
PageControllerModule = await Promise.resolve().then(() => PageController$1);
|
|
1391
|
+
} catch {
|
|
1392
|
+
throw new Error(
|
|
1393
|
+
'PageController not available. Either import from "@usecrow/client/browser" or use the bundled version.'
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
return PageControllerModule.PageController;
|
|
1397
|
+
}
|
|
1398
|
+
class CrowBrowserUse {
|
|
1399
|
+
constructor(e) {
|
|
1400
|
+
this.pageController = null, this.sessionId = null, this.maxSteps = 20, this.aborted = !1, this.config = e;
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* Initialize PageController with non-blocking pointer
|
|
1404
|
+
*/
|
|
1405
|
+
async initPageController() {
|
|
1406
|
+
if (this.pageController)
|
|
1407
|
+
return this.pageController;
|
|
1408
|
+
try {
|
|
1409
|
+
const e = await getPageController();
|
|
1410
|
+
this.pageController = new e({
|
|
1411
|
+
enableMask: !0,
|
|
1412
|
+
viewportExpansion: 500,
|
|
1413
|
+
highlightLabelOpacity: 0,
|
|
1414
|
+
// Hide numbered labels from users
|
|
1415
|
+
highlightOpacity: 0
|
|
1416
|
+
// Hide highlight boxes from users
|
|
1417
|
+
}), await this.pageController.showMask();
|
|
1418
|
+
const n = this.pageController.mask;
|
|
1419
|
+
return n != null && n.wrapper && (n.wrapper.style.pointerEvents = "none"), console.log("[CrowBrowserUse] PageController initialized with non-blocking pointer"), this.pageController;
|
|
1420
|
+
} catch (e) {
|
|
1421
|
+
throw console.error("[CrowBrowserUse] Failed to initialize PageController:", e), new Error(
|
|
1422
|
+
"Failed to initialize browser automation. Please import from @usecrow/client/browser."
|
|
1423
|
+
);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* Execute a browser automation task
|
|
1428
|
+
*/
|
|
1429
|
+
async execute(e) {
|
|
1430
|
+
var n, r, d, h, s, l, f, v, m, c, p, C, B, D;
|
|
1431
|
+
if (console.log("[CrowBrowserUse] Starting task:", e), this.config.onConfirmation && !await this.config.onConfirmation(e))
|
|
1432
|
+
return console.log("[CrowBrowserUse] User declined browser automation"), (r = (n = this.config).onProgress) == null || r.call(n, -1, this.maxSteps), {
|
|
1433
|
+
status: "error",
|
|
1434
|
+
error: "User declined browser automation",
|
|
1435
|
+
data: { declined: !0 }
|
|
1436
|
+
};
|
|
1437
|
+
try {
|
|
1438
|
+
const S = await this.initPageController(), R = await this.startSession(e);
|
|
1439
|
+
this.sessionId = R.session_id, this.maxSteps = R.max_steps, console.log("[CrowBrowserUse] Session started:", this.sessionId);
|
|
1440
|
+
let U = 0, k;
|
|
1441
|
+
for (; U < this.maxSteps; ) {
|
|
1442
|
+
if (this.aborted)
|
|
1443
|
+
return console.log("[CrowBrowserUse] Task cancelled by user"), await this.cleanup(), (h = (d = this.config).onProgress) == null || h.call(d, -1, this.maxSteps), {
|
|
1444
|
+
status: "error",
|
|
1445
|
+
error: "Task cancelled by user"
|
|
1446
|
+
};
|
|
1447
|
+
U++;
|
|
1448
|
+
const V = await S.getBrowserState(), $ = S.mask;
|
|
1449
|
+
$ != null && $.wrapper && ($.wrapper.style.pointerEvents = "none");
|
|
1450
|
+
const _ = await this.processStep(V, k);
|
|
1451
|
+
if (_.needs_user_input && _.question) {
|
|
1452
|
+
if (console.log("[CrowBrowserUse] Asking user:", _.question), !this.config.onQuestion) {
|
|
1453
|
+
k = "User input not available - no callback provided", console.warn("[CrowBrowserUse] No onQuestion callback provided");
|
|
1454
|
+
continue;
|
|
1455
|
+
}
|
|
1456
|
+
try {
|
|
1457
|
+
const F = await this.config.onQuestion(_.question);
|
|
1458
|
+
k = `User answered: ${F}`, console.log("[CrowBrowserUse] User answered:", F);
|
|
1459
|
+
} catch (F) {
|
|
1460
|
+
if (k = "User cancelled or failed to respond", console.log("[CrowBrowserUse] User cancelled or error:", F), this.aborted)
|
|
1461
|
+
return console.log("[CrowBrowserUse] Aborted after user cancelled"), await this.cleanup(), (l = (s = this.config).onProgress) == null || l.call(s, -1, this.maxSteps), {
|
|
1462
|
+
status: "error",
|
|
1463
|
+
error: "Task cancelled by user"
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
1466
|
+
continue;
|
|
1467
|
+
}
|
|
1468
|
+
if (_.done)
|
|
1469
|
+
return console.log("[CrowBrowserUse] Task completed:", _.message), await this.cleanup(), (v = (f = this.config).onProgress) == null || v.call(f, U, this.maxSteps), {
|
|
1470
|
+
status: _.success ? "success" : "error",
|
|
1471
|
+
data: {
|
|
1472
|
+
message: _.message,
|
|
1473
|
+
steps: U
|
|
1474
|
+
},
|
|
1475
|
+
error: _.success ? void 0 : _.message
|
|
1476
|
+
};
|
|
1477
|
+
if (_.error)
|
|
1478
|
+
return console.error("[CrowBrowserUse] Error:", _.error), await this.cleanup(), (c = (m = this.config).onProgress) == null || c.call(m, -1, this.maxSteps), {
|
|
1479
|
+
status: "error",
|
|
1480
|
+
error: _.error
|
|
1481
|
+
};
|
|
1482
|
+
_.action && (k = await this.executeAction(S, _.action), console.log(`[CrowBrowserUse] Step ${U}:`, k)), _.reflection && console.log("[CrowBrowserUse] Reflection:", _.reflection.next_goal);
|
|
1483
|
+
}
|
|
1484
|
+
return await this.cleanup(), (C = (p = this.config).onProgress) == null || C.call(p, -1, this.maxSteps), {
|
|
1485
|
+
status: "error",
|
|
1486
|
+
error: `Task incomplete after ${this.maxSteps} steps`
|
|
1487
|
+
};
|
|
1488
|
+
} catch (S) {
|
|
1489
|
+
return console.error("[CrowBrowserUse] Error:", S), await this.cleanup(), (D = (B = this.config).onProgress) == null || D.call(B, -1, this.maxSteps), {
|
|
1490
|
+
status: "error",
|
|
1491
|
+
error: S instanceof Error ? S.message : String(S)
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* Start a browser-use session on the server
|
|
1497
|
+
*/
|
|
1498
|
+
async startSession(e) {
|
|
1499
|
+
const n = await fetch(`${this.config.apiUrl}/api/browser-use/start`, {
|
|
1500
|
+
method: "POST",
|
|
1501
|
+
headers: { "Content-Type": "application/json" },
|
|
1502
|
+
body: JSON.stringify({
|
|
1503
|
+
product_id: this.config.productId,
|
|
1504
|
+
task: e
|
|
1505
|
+
})
|
|
1506
|
+
});
|
|
1507
|
+
if (!n.ok) {
|
|
1508
|
+
const r = await n.json().catch(() => ({ detail: "Unknown error" }));
|
|
1509
|
+
throw new Error(r.detail || `Failed to start session: ${n.status}`);
|
|
1510
|
+
}
|
|
1511
|
+
return n.json();
|
|
1512
|
+
}
|
|
1513
|
+
/**
|
|
1514
|
+
* Process a step on the server
|
|
1515
|
+
*/
|
|
1516
|
+
async processStep(e, n) {
|
|
1517
|
+
const r = await fetch(`${this.config.apiUrl}/api/browser-use/step`, {
|
|
1518
|
+
method: "POST",
|
|
1519
|
+
headers: { "Content-Type": "application/json" },
|
|
1520
|
+
body: JSON.stringify({
|
|
1521
|
+
session_id: this.sessionId,
|
|
1522
|
+
product_id: this.config.productId,
|
|
1523
|
+
browser_state: e,
|
|
1524
|
+
action_result: n
|
|
1525
|
+
})
|
|
1526
|
+
});
|
|
1527
|
+
if (!r.ok) {
|
|
1528
|
+
const d = await r.json().catch(() => ({ detail: "Unknown error" }));
|
|
1529
|
+
throw new Error(d.detail || `Failed to process step: ${r.status}`);
|
|
1530
|
+
}
|
|
1531
|
+
return r.json();
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Execute an action using PageController
|
|
1535
|
+
*/
|
|
1536
|
+
async executeAction(e, n) {
|
|
1537
|
+
const r = Object.keys(n)[0], d = n[r];
|
|
1538
|
+
try {
|
|
1539
|
+
switch (r) {
|
|
1540
|
+
case "click_element_by_index":
|
|
1541
|
+
return (await e.clickElement(d.index)).message;
|
|
1542
|
+
case "input_text":
|
|
1543
|
+
return (await e.inputText(
|
|
1544
|
+
d.index,
|
|
1545
|
+
d.text
|
|
1546
|
+
)).message;
|
|
1547
|
+
case "select_dropdown_option":
|
|
1548
|
+
return (await e.selectOption(
|
|
1549
|
+
d.index,
|
|
1550
|
+
d.text
|
|
1551
|
+
)).message;
|
|
1552
|
+
case "scroll":
|
|
1553
|
+
return (await e.scroll({
|
|
1554
|
+
down: d.down,
|
|
1555
|
+
numPages: d.num_pages,
|
|
1556
|
+
pixels: d.pixels,
|
|
1557
|
+
index: d.index
|
|
1558
|
+
})).message;
|
|
1559
|
+
case "scroll_horizontally":
|
|
1560
|
+
return (await e.scrollHorizontally({
|
|
1561
|
+
right: d.right,
|
|
1562
|
+
pixels: d.pixels,
|
|
1563
|
+
index: d.index
|
|
1564
|
+
})).message;
|
|
1565
|
+
case "wait": {
|
|
1566
|
+
const h = d.seconds || 1;
|
|
1567
|
+
return await new Promise((s) => setTimeout(s, h * 1e3)), `Waited ${h} seconds`;
|
|
1568
|
+
}
|
|
1569
|
+
case "done":
|
|
1570
|
+
return "Task completed";
|
|
1571
|
+
default:
|
|
1572
|
+
return `Unknown action: ${r}`;
|
|
1573
|
+
}
|
|
1574
|
+
} catch (h) {
|
|
1575
|
+
return `Action failed: ${h instanceof Error ? h.message : String(h)}`;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
1579
|
+
* Cleanup resources
|
|
1580
|
+
*/
|
|
1581
|
+
async cleanup() {
|
|
1582
|
+
if (this.pageController) {
|
|
1583
|
+
try {
|
|
1584
|
+
await this.pageController.hideMask(), await this.pageController.cleanUpHighlights(), this.pageController.dispose();
|
|
1585
|
+
} catch (e) {
|
|
1586
|
+
console.warn("[CrowBrowserUse] Cleanup error:", e);
|
|
1587
|
+
}
|
|
1588
|
+
this.pageController = null;
|
|
1589
|
+
}
|
|
1590
|
+
if (this.sessionId) {
|
|
1591
|
+
try {
|
|
1592
|
+
await fetch(`${this.config.apiUrl}/api/browser-use/end`, {
|
|
1593
|
+
method: "POST",
|
|
1594
|
+
headers: { "Content-Type": "application/json" },
|
|
1595
|
+
body: JSON.stringify({
|
|
1596
|
+
session_id: this.sessionId,
|
|
1597
|
+
product_id: this.config.productId
|
|
1598
|
+
})
|
|
1599
|
+
});
|
|
1600
|
+
} catch {
|
|
1601
|
+
}
|
|
1602
|
+
this.sessionId = null;
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Stop the current task
|
|
1607
|
+
*/
|
|
1608
|
+
async stop() {
|
|
1609
|
+
this.aborted = !0, await this.cleanup();
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
export {
|
|
1613
|
+
CrowBrowserUse as C,
|
|
1614
|
+
PageController as P,
|
|
1615
|
+
setPageController as s
|
|
1616
|
+
};
|