@ank1015/llm-extension 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +214 -0
- package/dist/chrome/background.js +244 -0
- package/dist/chrome/manifest.json +23 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/native/host-wrapper.sh +11 -0
- package/dist/native/host.d.ts +2 -0
- package/dist/native/host.d.ts.map +1 -0
- package/dist/native/host.js +21 -0
- package/dist/native/host.js.map +1 -0
- package/dist/native/server.d.ts +28 -0
- package/dist/native/server.d.ts.map +1 -0
- package/dist/native/server.js +112 -0
- package/dist/native/server.js.map +1 -0
- package/dist/native/stdio.d.ts +21 -0
- package/dist/native/stdio.d.ts.map +1 -0
- package/dist/native/stdio.js +94 -0
- package/dist/native/stdio.js.map +1 -0
- package/dist/protocol/constants.d.ts +26 -0
- package/dist/protocol/constants.d.ts.map +1 -0
- package/dist/protocol/constants.js +26 -0
- package/dist/protocol/constants.js.map +1 -0
- package/dist/protocol/types.d.ts +39 -0
- package/dist/protocol/types.d.ts.map +1 -0
- package/dist/protocol/types.js +3 -0
- package/dist/protocol/types.js.map +1 -0
- package/dist/sdk/action/diff.d.ts +7 -0
- package/dist/sdk/action/diff.d.ts.map +1 -0
- package/dist/sdk/action/diff.js +54 -0
- package/dist/sdk/action/diff.js.map +1 -0
- package/dist/sdk/action/helpers.d.ts +23 -0
- package/dist/sdk/action/helpers.d.ts.map +1 -0
- package/dist/sdk/action/helpers.js +93 -0
- package/dist/sdk/action/helpers.js.map +1 -0
- package/dist/sdk/action/index.d.ts +6 -0
- package/dist/sdk/action/index.d.ts.map +1 -0
- package/dist/sdk/action/index.js +4 -0
- package/dist/sdk/action/index.js.map +1 -0
- package/dist/sdk/action/script.d.ts +4 -0
- package/dist/sdk/action/script.d.ts.map +1 -0
- package/dist/sdk/action/script.js +359 -0
- package/dist/sdk/action/script.js.map +1 -0
- package/dist/sdk/action/types.d.ts +47 -0
- package/dist/sdk/action/types.d.ts.map +1 -0
- package/dist/sdk/action/types.js +2 -0
- package/dist/sdk/action/types.js.map +1 -0
- package/dist/sdk/client.d.ts +42 -0
- package/dist/sdk/client.d.ts.map +1 -0
- package/dist/sdk/client.js +118 -0
- package/dist/sdk/client.js.map +1 -0
- package/dist/sdk/connect.d.ts +29 -0
- package/dist/sdk/connect.d.ts.map +1 -0
- package/dist/sdk/connect.js +99 -0
- package/dist/sdk/connect.js.map +1 -0
- package/dist/sdk/index.d.ts +17 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +19 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sdk/observe/helpers.d.ts +16 -0
- package/dist/sdk/observe/helpers.d.ts.map +1 -0
- package/dist/sdk/observe/helpers.js +278 -0
- package/dist/sdk/observe/helpers.js.map +1 -0
- package/dist/sdk/observe/index.d.ts +6 -0
- package/dist/sdk/observe/index.d.ts.map +1 -0
- package/dist/sdk/observe/index.js +4 -0
- package/dist/sdk/observe/index.js.map +1 -0
- package/dist/sdk/observe/script.d.ts +7 -0
- package/dist/sdk/observe/script.d.ts.map +1 -0
- package/dist/sdk/observe/script.js +680 -0
- package/dist/sdk/observe/script.js.map +1 -0
- package/dist/sdk/observe/storage.d.ts +39 -0
- package/dist/sdk/observe/storage.d.ts.map +1 -0
- package/dist/sdk/observe/storage.js +83 -0
- package/dist/sdk/observe/storage.js.map +1 -0
- package/dist/sdk/observe/types.d.ts +140 -0
- package/dist/sdk/observe/types.d.ts.map +1 -0
- package/dist/sdk/observe/types.js +2 -0
- package/dist/sdk/observe/types.js.map +1 -0
- package/dist/sdk/window.d.ts +97 -0
- package/dist/sdk/window.d.ts.map +1 -0
- package/dist/sdk/window.js +795 -0
- package/dist/sdk/window.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,680 @@
|
|
|
1
|
+
export function buildObserveScript(options) {
|
|
2
|
+
const serializedOptions = JSON.stringify(options);
|
|
3
|
+
return `
|
|
4
|
+
(() => {
|
|
5
|
+
const options = ${serializedOptions};
|
|
6
|
+
|
|
7
|
+
const normalizeText = (value, maxLength = 220) => {
|
|
8
|
+
if (typeof value !== 'string') return '';
|
|
9
|
+
const compact = value.replace(/\\s+/g, ' ').trim();
|
|
10
|
+
if (!compact) return '';
|
|
11
|
+
if (compact.length <= maxLength) return compact;
|
|
12
|
+
return compact.slice(0, Math.max(1, maxLength - 1)) + '...';
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const toInt = (value) => {
|
|
16
|
+
if (typeof value !== 'number' || Number.isNaN(value)) return 0;
|
|
17
|
+
return Math.round(value);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const cssEscape = (value) => {
|
|
21
|
+
if (window.CSS && typeof window.CSS.escape === 'function') {
|
|
22
|
+
return window.CSS.escape(value);
|
|
23
|
+
}
|
|
24
|
+
return String(value).replace(/[^a-zA-Z0-9_-]/g, '\\\\$&');
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const buildCssPath = (element) => {
|
|
28
|
+
if (!(element instanceof Element)) return '';
|
|
29
|
+
const parts = [];
|
|
30
|
+
let current = element;
|
|
31
|
+
let depth = 0;
|
|
32
|
+
|
|
33
|
+
while (current && current.nodeType === 1 && depth < 6) {
|
|
34
|
+
let selector = current.tagName.toLowerCase();
|
|
35
|
+
if (current.id) {
|
|
36
|
+
selector += '#' + cssEscape(current.id);
|
|
37
|
+
parts.unshift(selector);
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const parent = current.parentElement;
|
|
42
|
+
if (parent) {
|
|
43
|
+
const siblings = Array.from(parent.children).filter(
|
|
44
|
+
(node) => node.tagName === current.tagName
|
|
45
|
+
);
|
|
46
|
+
if (siblings.length > 1) {
|
|
47
|
+
selector += ':nth-of-type(' + (siblings.indexOf(current) + 1) + ')';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
parts.unshift(selector);
|
|
52
|
+
current = parent;
|
|
53
|
+
depth += 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return parts.join(' > ');
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const getRectInfo = (element) => {
|
|
60
|
+
const rect = element.getBoundingClientRect();
|
|
61
|
+
return {
|
|
62
|
+
x: toInt(rect.left),
|
|
63
|
+
y: toInt(rect.top),
|
|
64
|
+
width: toInt(rect.width),
|
|
65
|
+
height: toInt(rect.height),
|
|
66
|
+
bottom: rect.bottom,
|
|
67
|
+
right: rect.right,
|
|
68
|
+
top: rect.top,
|
|
69
|
+
left: rect.left,
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const getVisibility = (element) => {
|
|
74
|
+
const rect = getRectInfo(element);
|
|
75
|
+
const style = window.getComputedStyle(element);
|
|
76
|
+
const hidden =
|
|
77
|
+
element.hidden ||
|
|
78
|
+
element.getAttribute('aria-hidden') === 'true' ||
|
|
79
|
+
style.display === 'none' ||
|
|
80
|
+
style.visibility === 'hidden' ||
|
|
81
|
+
Number.parseFloat(style.opacity || '1') === 0 ||
|
|
82
|
+
rect.width <= 0 ||
|
|
83
|
+
rect.height <= 0;
|
|
84
|
+
|
|
85
|
+
const viewportWidth = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
86
|
+
const viewportHeight = window.innerHeight || document.documentElement.clientHeight || 0;
|
|
87
|
+
const offscreen =
|
|
88
|
+
rect.bottom < 0 || rect.right < 0 || rect.top > viewportHeight || rect.left > viewportWidth;
|
|
89
|
+
|
|
90
|
+
return { hidden, offscreen, rect };
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const getElementText = (element, maxLength = 180) => {
|
|
94
|
+
const rawText =
|
|
95
|
+
typeof element.innerText === 'string' && element.innerText.trim()
|
|
96
|
+
? element.innerText
|
|
97
|
+
: element.textContent || '';
|
|
98
|
+
return normalizeText(rawText, maxLength);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const getByLabelledBy = (element) => {
|
|
102
|
+
const labelledBy = normalizeText(element.getAttribute('aria-labelledby') || '', 300);
|
|
103
|
+
if (!labelledBy) return '';
|
|
104
|
+
|
|
105
|
+
const parts = labelledBy
|
|
106
|
+
.split(/\\s+/)
|
|
107
|
+
.map((id) => document.getElementById(id))
|
|
108
|
+
.filter(Boolean)
|
|
109
|
+
.map((node) => getElementText(node, 100))
|
|
110
|
+
.filter(Boolean);
|
|
111
|
+
|
|
112
|
+
return normalizeText(parts.join(' '), 160);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const getLabelText = (element) => {
|
|
116
|
+
const labels = [];
|
|
117
|
+
|
|
118
|
+
if (element.id) {
|
|
119
|
+
const forLabel = document.querySelector('label[for="' + cssEscape(element.id) + '"]');
|
|
120
|
+
if (forLabel) labels.push(getElementText(forLabel, 100));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (element.labels && element.labels.length) {
|
|
124
|
+
for (const label of element.labels) labels.push(getElementText(label, 100));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (typeof element.closest === 'function') {
|
|
128
|
+
const parentLabel = element.closest('label');
|
|
129
|
+
if (parentLabel) labels.push(getElementText(parentLabel, 100));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return labels.find(Boolean) || '';
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const getName = (element) => {
|
|
136
|
+
const ariaLabel = normalizeText(element.getAttribute('aria-label') || '', 160);
|
|
137
|
+
if (ariaLabel) return ariaLabel;
|
|
138
|
+
|
|
139
|
+
const labelledBy = getByLabelledBy(element);
|
|
140
|
+
if (labelledBy) return labelledBy;
|
|
141
|
+
|
|
142
|
+
const labelText = getLabelText(element);
|
|
143
|
+
if (labelText) return labelText;
|
|
144
|
+
|
|
145
|
+
const placeholder = normalizeText(element.getAttribute('placeholder') || '', 140);
|
|
146
|
+
if (placeholder) return placeholder;
|
|
147
|
+
|
|
148
|
+
const title = normalizeText(element.getAttribute('title') || '', 140);
|
|
149
|
+
if (title) return title;
|
|
150
|
+
|
|
151
|
+
const alt = normalizeText(element.getAttribute('alt') || '', 140);
|
|
152
|
+
if (alt) return alt;
|
|
153
|
+
|
|
154
|
+
if (typeof element.value === 'string' && element.value && element.type !== 'password') {
|
|
155
|
+
return normalizeText(element.value, 140);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return getElementText(element, 180);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const getRole = (element) => normalizeText(element.getAttribute('role') || '', 60);
|
|
162
|
+
|
|
163
|
+
const getCategory = (element) => {
|
|
164
|
+
const tag = element.tagName.toLowerCase();
|
|
165
|
+
const role = getRole(element);
|
|
166
|
+
const type = normalizeText(element.getAttribute('type') || '', 40).toLowerCase();
|
|
167
|
+
|
|
168
|
+
if (
|
|
169
|
+
tag === 'button' ||
|
|
170
|
+
role === 'button' ||
|
|
171
|
+
(tag === 'input' && ['button', 'submit', 'reset', 'image'].includes(type))
|
|
172
|
+
) {
|
|
173
|
+
return 'button';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if ((tag === 'a' && element.getAttribute('href')) || role === 'link') {
|
|
177
|
+
return 'link';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (
|
|
181
|
+
tag === 'input' ||
|
|
182
|
+
tag === 'select' ||
|
|
183
|
+
tag === 'textarea' ||
|
|
184
|
+
element.isContentEditable
|
|
185
|
+
) {
|
|
186
|
+
return 'input';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return 'interactive';
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const getActions = (element, category) => {
|
|
193
|
+
const actions = [];
|
|
194
|
+
const addAction = (action) => {
|
|
195
|
+
if (!actions.includes(action)) actions.push(action);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const tag = element.tagName.toLowerCase();
|
|
199
|
+
const type = normalizeText(element.getAttribute('type') || '', 40).toLowerCase();
|
|
200
|
+
|
|
201
|
+
if (category === 'button' || category === 'link') addAction('click');
|
|
202
|
+
if (tag === 'select') addAction('select');
|
|
203
|
+
if (tag === 'textarea' || element.isContentEditable) addAction('type');
|
|
204
|
+
if (tag === 'input') {
|
|
205
|
+
if (type === 'checkbox' || type === 'radio') addAction('toggle');
|
|
206
|
+
else if (type === 'file') addAction('upload');
|
|
207
|
+
else if (
|
|
208
|
+
!['button', 'submit', 'reset', 'image', 'hidden'].includes(type) &&
|
|
209
|
+
category === 'input'
|
|
210
|
+
)
|
|
211
|
+
addAction('type');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (actions.length === 0 && category === 'interactive') addAction('click');
|
|
215
|
+
return actions;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const getState = (element) => {
|
|
219
|
+
const state = [];
|
|
220
|
+
|
|
221
|
+
if (
|
|
222
|
+
(typeof element.disabled === 'boolean' && element.disabled) ||
|
|
223
|
+
element.getAttribute('aria-disabled') === 'true'
|
|
224
|
+
) {
|
|
225
|
+
state.push('disabled');
|
|
226
|
+
}
|
|
227
|
+
if (typeof element.readOnly === 'boolean' && element.readOnly) state.push('readonly');
|
|
228
|
+
if (typeof element.required === 'boolean' && element.required) state.push('required');
|
|
229
|
+
if (typeof element.checked === 'boolean' && element.checked) state.push('checked');
|
|
230
|
+
if (element.getAttribute('aria-expanded') === 'true') state.push('expanded');
|
|
231
|
+
if (element.getAttribute('aria-pressed') === 'true') state.push('pressed');
|
|
232
|
+
if (element.getAttribute('aria-current')) state.push('current');
|
|
233
|
+
|
|
234
|
+
return state;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const getLocator = (element) => {
|
|
238
|
+
const locator = {};
|
|
239
|
+
const role = getRole(element);
|
|
240
|
+
const id = normalizeText(element.getAttribute('id') || '', 120);
|
|
241
|
+
const testId = normalizeText(
|
|
242
|
+
element.getAttribute('data-testid') ||
|
|
243
|
+
element.getAttribute('data-test-id') ||
|
|
244
|
+
element.getAttribute('data-test') ||
|
|
245
|
+
element.getAttribute('data-qa') ||
|
|
246
|
+
'',
|
|
247
|
+
120
|
|
248
|
+
);
|
|
249
|
+
const name = normalizeText(element.getAttribute('name') || '', 120);
|
|
250
|
+
|
|
251
|
+
if (id) locator.id = id;
|
|
252
|
+
if (testId) locator.testId = testId;
|
|
253
|
+
if (name) locator.name = name;
|
|
254
|
+
if (role) locator.role = role;
|
|
255
|
+
|
|
256
|
+
const cssPath = buildCssPath(element);
|
|
257
|
+
if (cssPath) locator.cssPath = cssPath;
|
|
258
|
+
|
|
259
|
+
return locator;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const getSelectOptions = (element, maxOptions = 30) => {
|
|
263
|
+
if (!(element instanceof HTMLSelectElement)) return [];
|
|
264
|
+
|
|
265
|
+
const options = [];
|
|
266
|
+
for (const option of Array.from(element.options).slice(0, maxOptions)) {
|
|
267
|
+
const value = normalizeText(option.value || '', 140);
|
|
268
|
+
const label = normalizeText(option.textContent || '', 140);
|
|
269
|
+
if (!value && !label) continue;
|
|
270
|
+
|
|
271
|
+
options.push({
|
|
272
|
+
value,
|
|
273
|
+
label,
|
|
274
|
+
selected: Boolean(option.selected),
|
|
275
|
+
disabled: Boolean(option.disabled),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return options;
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
const groupSelector = [
|
|
283
|
+
'[data-testid]',
|
|
284
|
+
'[id]',
|
|
285
|
+
'article',
|
|
286
|
+
'[role="article"]',
|
|
287
|
+
'li',
|
|
288
|
+
'[role="listitem"]',
|
|
289
|
+
'tr',
|
|
290
|
+
'section',
|
|
291
|
+
'form',
|
|
292
|
+
'main',
|
|
293
|
+
].join(',');
|
|
294
|
+
const groupIdByKey = new Map();
|
|
295
|
+
let groupCounter = 1;
|
|
296
|
+
|
|
297
|
+
const getGroupContainer = (element) => {
|
|
298
|
+
if (!(element instanceof Element)) return null;
|
|
299
|
+
return element.closest(groupSelector) || element.parentElement || document.body;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const getGroupKey = (element) => {
|
|
303
|
+
const testId = normalizeText(element.getAttribute('data-testid') || '', 120);
|
|
304
|
+
if (testId) return 'testid:' + testId;
|
|
305
|
+
|
|
306
|
+
const id = normalizeText(element.id || '', 120);
|
|
307
|
+
if (id) return 'id:' + id;
|
|
308
|
+
|
|
309
|
+
const cssPath = normalizeText(buildCssPath(element), 240);
|
|
310
|
+
if (cssPath) return 'css:' + cssPath;
|
|
311
|
+
|
|
312
|
+
return 'tag:' + element.tagName.toLowerCase();
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const getGroupId = (element) => {
|
|
316
|
+
const container = getGroupContainer(element);
|
|
317
|
+
if (!(container instanceof Element)) return 'G0';
|
|
318
|
+
|
|
319
|
+
const key = getGroupKey(container);
|
|
320
|
+
const existing = groupIdByKey.get(key);
|
|
321
|
+
if (existing) return existing;
|
|
322
|
+
|
|
323
|
+
const next = 'G' + groupCounter++;
|
|
324
|
+
groupIdByKey.set(key, next);
|
|
325
|
+
return next;
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const sortByPosition = (a, b) => {
|
|
329
|
+
if (a.bbox.y !== b.bbox.y) return a.bbox.y - b.bbox.y;
|
|
330
|
+
if (a.bbox.x !== b.bbox.x) return a.bbox.x - b.bbox.x;
|
|
331
|
+
const aLabel = typeof a.tag === 'string' ? a.tag : typeof a.kind === 'string' ? a.kind : '';
|
|
332
|
+
const bLabel = typeof b.tag === 'string' ? b.tag : typeof b.kind === 'string' ? b.kind : '';
|
|
333
|
+
return aLabel.localeCompare(bLabel);
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const interactiveSelectors = [
|
|
337
|
+
'a[href]',
|
|
338
|
+
'button',
|
|
339
|
+
'input',
|
|
340
|
+
'select',
|
|
341
|
+
'textarea',
|
|
342
|
+
'[role="button"]',
|
|
343
|
+
'[role="link"]',
|
|
344
|
+
'[contenteditable="true"]',
|
|
345
|
+
'[contenteditable=""]',
|
|
346
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
347
|
+
].join(',');
|
|
348
|
+
|
|
349
|
+
const interactiveCandidates = Array.from(document.querySelectorAll(interactiveSelectors));
|
|
350
|
+
const uniqueCandidates = Array.from(new Set(interactiveCandidates));
|
|
351
|
+
|
|
352
|
+
let hiddenFilteredCount = 0;
|
|
353
|
+
let offscreenFilteredCount = 0;
|
|
354
|
+
const interactiveRows = [];
|
|
355
|
+
|
|
356
|
+
for (const element of uniqueCandidates) {
|
|
357
|
+
const visibility = getVisibility(element);
|
|
358
|
+
if (visibility.hidden) {
|
|
359
|
+
hiddenFilteredCount += 1;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (visibility.offscreen) {
|
|
363
|
+
offscreenFilteredCount += 1;
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const category = getCategory(element);
|
|
368
|
+
const actions = getActions(element, category);
|
|
369
|
+
if (actions.length === 0) continue;
|
|
370
|
+
|
|
371
|
+
const tag = element.tagName.toLowerCase();
|
|
372
|
+
const role = getRole(element);
|
|
373
|
+
const name = getName(element);
|
|
374
|
+
const state = getState(element);
|
|
375
|
+
const locator = getLocator(element);
|
|
376
|
+
const bbox = {
|
|
377
|
+
x: visibility.rect.x,
|
|
378
|
+
y: visibility.rect.y,
|
|
379
|
+
width: visibility.rect.width,
|
|
380
|
+
height: visibility.rect.height,
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
const row = {
|
|
384
|
+
category,
|
|
385
|
+
tag,
|
|
386
|
+
role,
|
|
387
|
+
name,
|
|
388
|
+
groupId: getGroupId(element),
|
|
389
|
+
actions,
|
|
390
|
+
state,
|
|
391
|
+
locator,
|
|
392
|
+
bbox,
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
if (tag === 'a') {
|
|
396
|
+
const href = normalizeText(element.getAttribute('href') || '', 320);
|
|
397
|
+
if (href) row.href = href;
|
|
398
|
+
}
|
|
399
|
+
if (tag === 'select') {
|
|
400
|
+
const selectOptions = getSelectOptions(element);
|
|
401
|
+
if (selectOptions.length > 0) row.selectOptions = selectOptions;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
interactiveRows.push(row);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
interactiveRows.sort(sortByPosition);
|
|
408
|
+
const totalInteractiveCount = interactiveRows.length;
|
|
409
|
+
const interactive = interactiveRows.slice(0, options.maxInteractive).map((row, index) => ({
|
|
410
|
+
id: 'E' + (index + 1),
|
|
411
|
+
...row,
|
|
412
|
+
}));
|
|
413
|
+
|
|
414
|
+
const textSeen = new Set();
|
|
415
|
+
const textBlocks = [];
|
|
416
|
+
const addTextBlock = (kind, text, source, groupId, level) => {
|
|
417
|
+
const normalized = normalizeText(text, 360);
|
|
418
|
+
if (!normalized) return;
|
|
419
|
+
|
|
420
|
+
const normalizedGroupId =
|
|
421
|
+
typeof groupId === 'string' && groupId.trim() ? normalizeText(groupId, 24) : 'G0';
|
|
422
|
+
const key = normalizedGroupId + ':' + kind + ':' + normalized.toLowerCase();
|
|
423
|
+
if (textSeen.has(key)) return;
|
|
424
|
+
textSeen.add(key);
|
|
425
|
+
|
|
426
|
+
const block = {
|
|
427
|
+
id: 'T' + (textBlocks.length + 1),
|
|
428
|
+
kind,
|
|
429
|
+
text: normalized,
|
|
430
|
+
source: normalizeText(source || '', 200),
|
|
431
|
+
groupId: normalizedGroupId,
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
if (typeof level === 'number') block.level = level;
|
|
435
|
+
textBlocks.push(block);
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
const textRows = [];
|
|
439
|
+
const headingNodes = Array.from(document.querySelectorAll('h1, h2, h3, h4, h5, h6'));
|
|
440
|
+
for (const heading of headingNodes) {
|
|
441
|
+
const visibility = getVisibility(heading);
|
|
442
|
+
if (visibility.hidden || visibility.offscreen) continue;
|
|
443
|
+
const headingText = getElementText(heading, 280);
|
|
444
|
+
if (!headingText) continue;
|
|
445
|
+
|
|
446
|
+
const level = Number.parseInt(heading.tagName.slice(1), 10);
|
|
447
|
+
textRows.push({
|
|
448
|
+
kind: 'heading',
|
|
449
|
+
text: headingText,
|
|
450
|
+
source: buildCssPath(heading),
|
|
451
|
+
level: Number.isFinite(level) ? level : undefined,
|
|
452
|
+
groupId: getGroupId(heading),
|
|
453
|
+
bbox: {
|
|
454
|
+
x: visibility.rect.x,
|
|
455
|
+
y: visibility.rect.y,
|
|
456
|
+
width: visibility.rect.width,
|
|
457
|
+
height: visibility.rect.height,
|
|
458
|
+
},
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const textSelectors = ['main p', 'article p', '[role="main"] p', 'section p', 'p', 'li'];
|
|
463
|
+
const textCandidates = Array.from(document.querySelectorAll(textSelectors.join(',')));
|
|
464
|
+
for (const node of textCandidates) {
|
|
465
|
+
const visibility = getVisibility(node);
|
|
466
|
+
if (visibility.hidden || visibility.offscreen) continue;
|
|
467
|
+
|
|
468
|
+
const text = getElementText(node, 360);
|
|
469
|
+
if (!text || text.length < 28) continue;
|
|
470
|
+
|
|
471
|
+
textRows.push({
|
|
472
|
+
kind: 'text',
|
|
473
|
+
text,
|
|
474
|
+
source: buildCssPath(node),
|
|
475
|
+
groupId: getGroupId(node),
|
|
476
|
+
bbox: {
|
|
477
|
+
x: visibility.rect.x,
|
|
478
|
+
y: visibility.rect.y,
|
|
479
|
+
width: visibility.rect.width,
|
|
480
|
+
height: visibility.rect.height,
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
textRows.sort(sortByPosition);
|
|
486
|
+
for (const row of textRows) {
|
|
487
|
+
if (textBlocks.length >= options.maxTextBlocks * 2) break;
|
|
488
|
+
addTextBlock(row.kind, row.text, row.source, row.groupId, row.level);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const totalTextBlockCount = textBlocks.length;
|
|
492
|
+
const trimmedTextBlocks = textBlocks.slice(0, options.maxTextBlocks);
|
|
493
|
+
|
|
494
|
+
const formRows = [];
|
|
495
|
+
const formNodes = Array.from(document.querySelectorAll('form'));
|
|
496
|
+
for (const formNode of formNodes) {
|
|
497
|
+
const visibility = getVisibility(formNode);
|
|
498
|
+
if (visibility.hidden || visibility.offscreen) continue;
|
|
499
|
+
|
|
500
|
+
const fieldNodes = Array.from(formNode.querySelectorAll('input, select, textarea'));
|
|
501
|
+
const fields = [];
|
|
502
|
+
for (const field of fieldNodes) {
|
|
503
|
+
const type = normalizeText(field.getAttribute('type') || '', 40).toLowerCase();
|
|
504
|
+
if (type === 'hidden') continue;
|
|
505
|
+
const fieldName =
|
|
506
|
+
getName(field) ||
|
|
507
|
+
normalizeText(field.getAttribute('name') || '', 80) ||
|
|
508
|
+
normalizeText(field.getAttribute('id') || '', 80) ||
|
|
509
|
+
normalizeText(field.tagName.toLowerCase(), 40);
|
|
510
|
+
if (fieldName && !fields.includes(fieldName)) fields.push(fieldName);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const submitNodes = Array.from(
|
|
514
|
+
formNode.querySelectorAll(
|
|
515
|
+
'button[type="submit"], button:not([type]), input[type="submit"], input[type="image"]'
|
|
516
|
+
)
|
|
517
|
+
);
|
|
518
|
+
const submitButtons = [];
|
|
519
|
+
for (const submitNode of submitNodes) {
|
|
520
|
+
const submitName = getName(submitNode) || normalizeText(submitNode.tagName.toLowerCase(), 40);
|
|
521
|
+
if (submitName && !submitButtons.includes(submitName)) submitButtons.push(submitName);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const formName =
|
|
525
|
+
normalizeText(formNode.getAttribute('aria-label') || '', 100) ||
|
|
526
|
+
normalizeText(formNode.getAttribute('name') || '', 100) ||
|
|
527
|
+
normalizeText(formNode.getAttribute('id') || '', 100) ||
|
|
528
|
+
'form';
|
|
529
|
+
|
|
530
|
+
formRows.push({
|
|
531
|
+
name: formName,
|
|
532
|
+
fields,
|
|
533
|
+
submitButtons,
|
|
534
|
+
bbox: {
|
|
535
|
+
x: visibility.rect.x,
|
|
536
|
+
y: visibility.rect.y,
|
|
537
|
+
width: visibility.rect.width,
|
|
538
|
+
height: visibility.rect.height,
|
|
539
|
+
},
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
formRows.sort(sortByPosition);
|
|
543
|
+
const forms = formRows.map((row, index) => ({
|
|
544
|
+
id: 'F' + (index + 1),
|
|
545
|
+
name: row.name,
|
|
546
|
+
fields: row.fields,
|
|
547
|
+
submitButtons: row.submitButtons,
|
|
548
|
+
}));
|
|
549
|
+
|
|
550
|
+
const mediaRows = [];
|
|
551
|
+
const mediaNodes = Array.from(document.querySelectorAll('audio, video'));
|
|
552
|
+
for (const mediaNode of mediaNodes) {
|
|
553
|
+
const visibility = getVisibility(mediaNode);
|
|
554
|
+
if (visibility.hidden || visibility.offscreen) continue;
|
|
555
|
+
|
|
556
|
+
const kind = mediaNode.tagName.toLowerCase() === 'video' ? 'video' : 'audio';
|
|
557
|
+
const state = [];
|
|
558
|
+
if (mediaNode.ended) state.push('ended');
|
|
559
|
+
else if (mediaNode.paused) state.push('paused');
|
|
560
|
+
else state.push('playing');
|
|
561
|
+
if (mediaNode.muted || mediaNode.volume === 0) state.push('muted');
|
|
562
|
+
if (mediaNode.controls) state.push('controls');
|
|
563
|
+
|
|
564
|
+
const currentTime = Number.isFinite(mediaNode.currentTime)
|
|
565
|
+
? Number.parseFloat(mediaNode.currentTime.toFixed(3))
|
|
566
|
+
: 0;
|
|
567
|
+
const duration = Number.isFinite(mediaNode.duration)
|
|
568
|
+
? Number.parseFloat(mediaNode.duration.toFixed(3))
|
|
569
|
+
: -1;
|
|
570
|
+
const src = normalizeText(mediaNode.currentSrc || mediaNode.getAttribute('src') || '', 320);
|
|
571
|
+
const name =
|
|
572
|
+
getName(mediaNode) ||
|
|
573
|
+
normalizeText(mediaNode.getAttribute('title') || '', 120) ||
|
|
574
|
+
normalizeText(src, 120) ||
|
|
575
|
+
kind;
|
|
576
|
+
|
|
577
|
+
const row = {
|
|
578
|
+
kind,
|
|
579
|
+
name,
|
|
580
|
+
state,
|
|
581
|
+
locator: getLocator(mediaNode),
|
|
582
|
+
bbox: {
|
|
583
|
+
x: visibility.rect.x,
|
|
584
|
+
y: visibility.rect.y,
|
|
585
|
+
width: visibility.rect.width,
|
|
586
|
+
height: visibility.rect.height,
|
|
587
|
+
},
|
|
588
|
+
currentTime,
|
|
589
|
+
duration,
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
if (src) row.src = src;
|
|
593
|
+
mediaRows.push(row);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
mediaRows.sort(sortByPosition);
|
|
597
|
+
const media = mediaRows.map((row, index) => ({
|
|
598
|
+
id: 'M' + (index + 1),
|
|
599
|
+
...row,
|
|
600
|
+
}));
|
|
601
|
+
|
|
602
|
+
const alerts = [];
|
|
603
|
+
const addAlert = (text) => {
|
|
604
|
+
const normalized = normalizeText(text, 240);
|
|
605
|
+
if (!normalized) return;
|
|
606
|
+
if (!alerts.includes(normalized)) alerts.push(normalized);
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
const alertNodes = Array.from(
|
|
610
|
+
document.querySelectorAll(
|
|
611
|
+
'[role="alert"], [role="status"], [aria-live="assertive"], [aria-live="polite"]'
|
|
612
|
+
)
|
|
613
|
+
);
|
|
614
|
+
for (const alertNode of alertNodes) {
|
|
615
|
+
const visibility = getVisibility(alertNode);
|
|
616
|
+
if (visibility.hidden || visibility.offscreen) continue;
|
|
617
|
+
addAlert(getElementText(alertNode, 240));
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const warnings = [];
|
|
621
|
+
const iframeCount = document.querySelectorAll('iframe').length;
|
|
622
|
+
if (iframeCount > 0) warnings.push('Iframe contents are not expanded in this snapshot.');
|
|
623
|
+
|
|
624
|
+
const page = {
|
|
625
|
+
url: location.href,
|
|
626
|
+
title: document.title || '',
|
|
627
|
+
lang: document.documentElement.lang || '',
|
|
628
|
+
capturedAt: new Date().toISOString(),
|
|
629
|
+
viewport: {
|
|
630
|
+
width: toInt(window.innerWidth || document.documentElement.clientWidth || 0),
|
|
631
|
+
height: toInt(window.innerHeight || document.documentElement.clientHeight || 0),
|
|
632
|
+
},
|
|
633
|
+
scroll: {
|
|
634
|
+
x: toInt(window.scrollX || window.pageXOffset || 0),
|
|
635
|
+
y: toInt(window.scrollY || window.pageYOffset || 0),
|
|
636
|
+
maxY: toInt(
|
|
637
|
+
Math.max(
|
|
638
|
+
0,
|
|
639
|
+
(document.documentElement.scrollHeight || 0) -
|
|
640
|
+
(window.innerHeight || document.documentElement.clientHeight || 0)
|
|
641
|
+
)
|
|
642
|
+
),
|
|
643
|
+
},
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
const summary = {
|
|
647
|
+
interactiveCount: interactive.length,
|
|
648
|
+
totalInteractiveCount,
|
|
649
|
+
textBlockCount: trimmedTextBlocks.length,
|
|
650
|
+
totalTextBlockCount,
|
|
651
|
+
formCount: forms.length,
|
|
652
|
+
mediaCount: media.length,
|
|
653
|
+
alertCount: alerts.length,
|
|
654
|
+
totalLinks: document.querySelectorAll('a[href]').length,
|
|
655
|
+
totalButtons: document.querySelectorAll('button, input[type="button"], input[type="submit"]').length,
|
|
656
|
+
totalInputs: document.querySelectorAll('input, select, textarea, [contenteditable="true"], [contenteditable=""]').length,
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
const truncation = {
|
|
660
|
+
interactive: totalInteractiveCount > interactive.length,
|
|
661
|
+
textBlocks: totalTextBlockCount > trimmedTextBlocks.length,
|
|
662
|
+
hiddenFilteredCount,
|
|
663
|
+
offscreenFilteredCount,
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
return {
|
|
667
|
+
page,
|
|
668
|
+
summary,
|
|
669
|
+
interactive,
|
|
670
|
+
textBlocks: trimmedTextBlocks,
|
|
671
|
+
forms,
|
|
672
|
+
media,
|
|
673
|
+
alerts: alerts.slice(0, 25),
|
|
674
|
+
truncation,
|
|
675
|
+
warnings,
|
|
676
|
+
};
|
|
677
|
+
})()
|
|
678
|
+
`.trim();
|
|
679
|
+
}
|
|
680
|
+
//# sourceMappingURL=script.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script.js","sourceRoot":"","sources":["../../../src/sdk/observe/script.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,kBAAkB,CAAC,OAA6B;IAC9D,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAElD,OAAO;;oBAEW,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiqBpC,CAAC,IAAI,EAAE,CAAC;AACT,CAAC"}
|