sentienceapi 0.95.0__py3-none-any.whl
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.
Potentially problematic release.
This version of sentienceapi might be problematic. Click here for more details.
- sentience/__init__.py +253 -0
- sentience/_extension_loader.py +195 -0
- sentience/action_executor.py +215 -0
- sentience/actions.py +1020 -0
- sentience/agent.py +1181 -0
- sentience/agent_config.py +46 -0
- sentience/agent_runtime.py +424 -0
- sentience/asserts/__init__.py +70 -0
- sentience/asserts/expect.py +621 -0
- sentience/asserts/query.py +383 -0
- sentience/async_api.py +108 -0
- sentience/backends/__init__.py +137 -0
- sentience/backends/actions.py +343 -0
- sentience/backends/browser_use_adapter.py +241 -0
- sentience/backends/cdp_backend.py +393 -0
- sentience/backends/exceptions.py +211 -0
- sentience/backends/playwright_backend.py +194 -0
- sentience/backends/protocol.py +216 -0
- sentience/backends/sentience_context.py +469 -0
- sentience/backends/snapshot.py +427 -0
- sentience/base_agent.py +196 -0
- sentience/browser.py +1215 -0
- sentience/browser_evaluator.py +299 -0
- sentience/canonicalization.py +207 -0
- sentience/cli.py +130 -0
- sentience/cloud_tracing.py +807 -0
- sentience/constants.py +6 -0
- sentience/conversational_agent.py +543 -0
- sentience/element_filter.py +136 -0
- sentience/expect.py +188 -0
- sentience/extension/background.js +104 -0
- sentience/extension/content.js +161 -0
- sentience/extension/injected_api.js +914 -0
- sentience/extension/manifest.json +36 -0
- sentience/extension/pkg/sentience_core.d.ts +51 -0
- sentience/extension/pkg/sentience_core.js +323 -0
- sentience/extension/pkg/sentience_core_bg.wasm +0 -0
- sentience/extension/pkg/sentience_core_bg.wasm.d.ts +10 -0
- sentience/extension/release.json +115 -0
- sentience/formatting.py +15 -0
- sentience/generator.py +202 -0
- sentience/inspector.py +367 -0
- sentience/llm_interaction_handler.py +191 -0
- sentience/llm_provider.py +875 -0
- sentience/llm_provider_utils.py +120 -0
- sentience/llm_response_builder.py +153 -0
- sentience/models.py +846 -0
- sentience/ordinal.py +280 -0
- sentience/overlay.py +222 -0
- sentience/protocols.py +228 -0
- sentience/query.py +303 -0
- sentience/read.py +188 -0
- sentience/recorder.py +589 -0
- sentience/schemas/trace_v1.json +335 -0
- sentience/screenshot.py +100 -0
- sentience/sentience_methods.py +86 -0
- sentience/snapshot.py +706 -0
- sentience/snapshot_diff.py +126 -0
- sentience/text_search.py +262 -0
- sentience/trace_event_builder.py +148 -0
- sentience/trace_file_manager.py +197 -0
- sentience/trace_indexing/__init__.py +27 -0
- sentience/trace_indexing/index_schema.py +199 -0
- sentience/trace_indexing/indexer.py +414 -0
- sentience/tracer_factory.py +322 -0
- sentience/tracing.py +449 -0
- sentience/utils/__init__.py +40 -0
- sentience/utils/browser.py +46 -0
- sentience/utils/element.py +257 -0
- sentience/utils/formatting.py +59 -0
- sentience/utils.py +296 -0
- sentience/verification.py +380 -0
- sentience/visual_agent.py +2058 -0
- sentience/wait.py +139 -0
- sentienceapi-0.95.0.dist-info/METADATA +984 -0
- sentienceapi-0.95.0.dist-info/RECORD +82 -0
- sentienceapi-0.95.0.dist-info/WHEEL +5 -0
- sentienceapi-0.95.0.dist-info/entry_points.txt +2 -0
- sentienceapi-0.95.0.dist-info/licenses/LICENSE +24 -0
- sentienceapi-0.95.0.dist-info/licenses/LICENSE-APACHE +201 -0
- sentienceapi-0.95.0.dist-info/licenses/LICENSE-MIT +21 -0
- sentienceapi-0.95.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,914 @@
|
|
|
1
|
+
!function() {
|
|
2
|
+
"use strict";
|
|
3
|
+
function getAllElements(root = document) {
|
|
4
|
+
const elements = [], filter = {
|
|
5
|
+
acceptNode: node => [ "SCRIPT", "STYLE", "NOSCRIPT", "META", "LINK", "HEAD" ].includes(node.tagName) || node.parentNode && "SVG" === node.parentNode.tagName && "SVG" !== node.tagName ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT
|
|
6
|
+
}, walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filter);
|
|
7
|
+
for (;walker.nextNode(); ) {
|
|
8
|
+
const node = walker.currentNode;
|
|
9
|
+
node.isConnected && (elements.push(node), node.shadowRoot && elements.push(...getAllElements(node.shadowRoot)));
|
|
10
|
+
}
|
|
11
|
+
return elements;
|
|
12
|
+
}
|
|
13
|
+
const DEFAULT_INFERENCE_CONFIG = {
|
|
14
|
+
allowedTags: [ "label", "span", "div" ],
|
|
15
|
+
allowedRoles: [],
|
|
16
|
+
allowedClassPatterns: [],
|
|
17
|
+
maxParentDepth: 2,
|
|
18
|
+
maxSiblingDistance: 1,
|
|
19
|
+
requireSameContainer: !0,
|
|
20
|
+
containerTags: [ "form", "fieldset", "div" ],
|
|
21
|
+
methods: {
|
|
22
|
+
explicitLabel: !0,
|
|
23
|
+
ariaLabelledby: !0,
|
|
24
|
+
parentTraversal: !0,
|
|
25
|
+
siblingProximity: !0
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
function isInferenceSource(el, config) {
|
|
29
|
+
if (!el || !el.tagName) return !1;
|
|
30
|
+
const tag = el.tagName.toLowerCase(), role = el.getAttribute ? el.getAttribute("role") : "", className = ((el.className || "") + "").toLowerCase();
|
|
31
|
+
if (config.allowedTags.includes(tag)) return !0;
|
|
32
|
+
if (config.allowedRoles.length > 0 && role && config.allowedRoles.includes(role)) return !0;
|
|
33
|
+
if (config.allowedClassPatterns.length > 0) for (const pattern of config.allowedClassPatterns) if (className.includes(pattern.toLowerCase())) return !0;
|
|
34
|
+
return !1;
|
|
35
|
+
}
|
|
36
|
+
function isInSameValidContainer(element, candidate, limits) {
|
|
37
|
+
if (!element || !candidate) return !1;
|
|
38
|
+
if (limits.requireSameContainer) {
|
|
39
|
+
const commonParent = function(el1, el2) {
|
|
40
|
+
if (!el1 || !el2) return null;
|
|
41
|
+
const doc = "undefined" != typeof global && global.document || "undefined" != typeof window && window.document || "undefined" != typeof document && document || null, parents1 = [];
|
|
42
|
+
let current = el1;
|
|
43
|
+
for (;current && (parents1.push(current), current.parentElement) && (!doc || current !== doc.body && current !== doc.documentElement); ) current = current.parentElement;
|
|
44
|
+
for (current = el2; current; ) {
|
|
45
|
+
if (-1 !== parents1.indexOf(current)) return current;
|
|
46
|
+
if (!current.parentElement) break;
|
|
47
|
+
if (doc && (current === doc.body || current === doc.documentElement)) break;
|
|
48
|
+
current = current.parentElement;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}(element, candidate);
|
|
52
|
+
if (!commonParent) return !1;
|
|
53
|
+
if (!function(el, validTags) {
|
|
54
|
+
if (!el || !el.tagName) return !1;
|
|
55
|
+
const tag = el.tagName.toLowerCase();
|
|
56
|
+
let className = "";
|
|
57
|
+
try {
|
|
58
|
+
className = (el.className || "") + "";
|
|
59
|
+
} catch (e) {
|
|
60
|
+
className = "";
|
|
61
|
+
}
|
|
62
|
+
return validTags.includes(tag) || className.toLowerCase().includes("form") || className.toLowerCase().includes("field");
|
|
63
|
+
}(commonParent, limits.containerTags)) return !1;
|
|
64
|
+
}
|
|
65
|
+
return !0;
|
|
66
|
+
}
|
|
67
|
+
function getInferredLabel(el, options = {}) {
|
|
68
|
+
if (!el) return null;
|
|
69
|
+
const {enableInference: enableInference = !0, inferenceConfig: inferenceConfig = {}} = options;
|
|
70
|
+
if (!enableInference) return null;
|
|
71
|
+
const ariaLabel = el.getAttribute ? el.getAttribute("aria-label") : null, hasAriaLabel = ariaLabel && ariaLabel.trim(), hasInputValue = "INPUT" === el.tagName && (el.value || el.placeholder), hasImgAlt = "IMG" === el.tagName && el.alt;
|
|
72
|
+
let innerTextValue = "";
|
|
73
|
+
try {
|
|
74
|
+
innerTextValue = el.innerText || "";
|
|
75
|
+
} catch (e) {
|
|
76
|
+
innerTextValue = "";
|
|
77
|
+
}
|
|
78
|
+
const hasInnerText = "INPUT" !== el.tagName && "IMG" !== el.tagName && innerTextValue && innerTextValue.trim();
|
|
79
|
+
if (hasAriaLabel || hasInputValue || hasImgAlt || hasInnerText) return null;
|
|
80
|
+
const config = function(userConfig = {}) {
|
|
81
|
+
return {
|
|
82
|
+
...DEFAULT_INFERENCE_CONFIG,
|
|
83
|
+
...userConfig,
|
|
84
|
+
methods: {
|
|
85
|
+
...DEFAULT_INFERENCE_CONFIG.methods,
|
|
86
|
+
...userConfig.methods || {}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}(inferenceConfig);
|
|
90
|
+
if (config.methods.explicitLabel && el.labels && el.labels.length > 0) {
|
|
91
|
+
const label = el.labels[0];
|
|
92
|
+
if (isInferenceSource(label, config)) {
|
|
93
|
+
const text = (label.innerText || "").trim();
|
|
94
|
+
if (text) return {
|
|
95
|
+
text: text,
|
|
96
|
+
source: "explicit_label"
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (config.methods.ariaLabelledby && el.hasAttribute && el.hasAttribute("aria-labelledby")) {
|
|
101
|
+
const labelIdsAttr = el.getAttribute("aria-labelledby");
|
|
102
|
+
if (labelIdsAttr) {
|
|
103
|
+
const labelIds = labelIdsAttr.split(/\s+/).filter(id => id.trim()), labelTexts = [], doc = (() => "undefined" != typeof global && global.document ? global.document : "undefined" != typeof window && window.document ? window.document : "undefined" != typeof document ? document : null)();
|
|
104
|
+
if (doc && doc.getElementById) for (const labelId of labelIds) {
|
|
105
|
+
if (!labelId.trim()) continue;
|
|
106
|
+
let labelEl = null;
|
|
107
|
+
try {
|
|
108
|
+
labelEl = doc.getElementById(labelId);
|
|
109
|
+
} catch (e) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (labelEl) {
|
|
113
|
+
let text = "";
|
|
114
|
+
try {
|
|
115
|
+
if (text = (labelEl.innerText || "").trim(), !text && labelEl.textContent && (text = labelEl.textContent.trim()),
|
|
116
|
+
!text && labelEl.getAttribute) {
|
|
117
|
+
const ariaLabel = labelEl.getAttribute("aria-label");
|
|
118
|
+
ariaLabel && (text = ariaLabel.trim());
|
|
119
|
+
}
|
|
120
|
+
} catch (e) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
text && labelTexts.push(text);
|
|
124
|
+
}
|
|
125
|
+
} else ;
|
|
126
|
+
if (labelTexts.length > 0) return {
|
|
127
|
+
text: labelTexts.join(" "),
|
|
128
|
+
source: "aria_labelledby"
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (config.methods.parentTraversal) {
|
|
133
|
+
let parent = el.parentElement, depth = 0;
|
|
134
|
+
for (;parent && depth < config.maxParentDepth; ) {
|
|
135
|
+
if (isInferenceSource(parent, config)) {
|
|
136
|
+
const text = (parent.innerText || "").trim();
|
|
137
|
+
if (text) return {
|
|
138
|
+
text: text,
|
|
139
|
+
source: "parent_label"
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
parent = parent.parentElement, depth++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (config.methods.siblingProximity) {
|
|
146
|
+
const prev = el.previousElementSibling;
|
|
147
|
+
if (prev && isInferenceSource(prev, config) && isInSameValidContainer(el, prev, {
|
|
148
|
+
requireSameContainer: config.requireSameContainer,
|
|
149
|
+
containerTags: config.containerTags
|
|
150
|
+
})) {
|
|
151
|
+
const text = (prev.innerText || "").trim();
|
|
152
|
+
if (text) return {
|
|
153
|
+
text: text,
|
|
154
|
+
source: "sibling_label"
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
function getText(el) {
|
|
161
|
+
return el.getAttribute("aria-label") ? el.getAttribute("aria-label") : "INPUT" === el.tagName ? el.value || el.placeholder || "" : "IMG" === el.tagName ? el.alt || "" : (el.innerText || "").replace(/\s+/g, " ").trim().substring(0, 100);
|
|
162
|
+
}
|
|
163
|
+
function getClassName(el) {
|
|
164
|
+
if (!el || !el.className) return "";
|
|
165
|
+
if ("string" == typeof el.className) return el.className;
|
|
166
|
+
if ("object" == typeof el.className) {
|
|
167
|
+
if ("baseVal" in el.className && "string" == typeof el.className.baseVal) return el.className.baseVal;
|
|
168
|
+
if ("animVal" in el.className && "string" == typeof el.className.animVal) return el.className.animVal;
|
|
169
|
+
try {
|
|
170
|
+
return String(el.className);
|
|
171
|
+
} catch (e) {
|
|
172
|
+
return "";
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return "";
|
|
176
|
+
}
|
|
177
|
+
function toSafeString(value) {
|
|
178
|
+
if (null == value) return null;
|
|
179
|
+
if ("string" == typeof value) return value;
|
|
180
|
+
if ("object" == typeof value) {
|
|
181
|
+
if ("baseVal" in value && "string" == typeof value.baseVal) return value.baseVal;
|
|
182
|
+
if ("animVal" in value && "string" == typeof value.animVal) return value.animVal;
|
|
183
|
+
try {
|
|
184
|
+
return String(value);
|
|
185
|
+
} catch (e) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
return String(value);
|
|
191
|
+
} catch (e) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function getSVGColor(el) {
|
|
196
|
+
if (!el || "SVG" !== el.tagName) return null;
|
|
197
|
+
const style = window.getComputedStyle(el), fill = style.fill;
|
|
198
|
+
if (fill && "none" !== fill && "transparent" !== fill && "rgba(0, 0, 0, 0)" !== fill) {
|
|
199
|
+
const rgbaMatch = fill.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
200
|
+
if (rgbaMatch) {
|
|
201
|
+
if ((rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1) >= .9) return `rgb(${rgbaMatch[1]}, ${rgbaMatch[2]}, ${rgbaMatch[3]})`;
|
|
202
|
+
} else if (fill.startsWith("rgb(")) return fill;
|
|
203
|
+
}
|
|
204
|
+
const stroke = style.stroke;
|
|
205
|
+
if (stroke && "none" !== stroke && "transparent" !== stroke && "rgba(0, 0, 0, 0)" !== stroke) {
|
|
206
|
+
const rgbaMatch = stroke.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
207
|
+
if (rgbaMatch) {
|
|
208
|
+
if ((rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1) >= .9) return `rgb(${rgbaMatch[1]}, ${rgbaMatch[2]}, ${rgbaMatch[3]})`;
|
|
209
|
+
} else if (stroke.startsWith("rgb(")) return stroke;
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
function getRawHTML(root) {
|
|
214
|
+
const sourceRoot = root || document.body, clone = sourceRoot.cloneNode(!0);
|
|
215
|
+
[ "nav", "footer", "header", "script", "style", "noscript", "iframe", "svg" ].forEach(tag => {
|
|
216
|
+
clone.querySelectorAll(tag).forEach(el => {
|
|
217
|
+
el.parentNode && el.parentNode.removeChild(el);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
const invisibleSelectors = [], walker = document.createTreeWalker(sourceRoot, NodeFilter.SHOW_ELEMENT, null, !1);
|
|
221
|
+
let node;
|
|
222
|
+
for (;node = walker.nextNode(); ) {
|
|
223
|
+
const tag = node.tagName.toLowerCase();
|
|
224
|
+
if ("head" === tag || "title" === tag) continue;
|
|
225
|
+
const style = window.getComputedStyle(node);
|
|
226
|
+
if ("none" === style.display || "hidden" === style.visibility || 0 === node.offsetWidth && 0 === node.offsetHeight) {
|
|
227
|
+
let selector = tag;
|
|
228
|
+
if (node.id) selector = `#${node.id}`; else if (node.className && "string" == typeof node.className) {
|
|
229
|
+
const classes = node.className.trim().split(/\s+/).filter(c => c);
|
|
230
|
+
classes.length > 0 && (selector = `${tag}.${classes.join(".")}`);
|
|
231
|
+
}
|
|
232
|
+
invisibleSelectors.push(selector);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
invisibleSelectors.forEach(selector => {
|
|
236
|
+
try {
|
|
237
|
+
clone.querySelectorAll(selector).forEach(el => {
|
|
238
|
+
el.parentNode && el.parentNode.removeChild(el);
|
|
239
|
+
});
|
|
240
|
+
} catch (e) {}
|
|
241
|
+
});
|
|
242
|
+
clone.querySelectorAll("a[href]").forEach(link => {
|
|
243
|
+
const href = link.getAttribute("href");
|
|
244
|
+
if (href && !href.startsWith("http://") && !href.startsWith("https://") && !href.startsWith("#")) try {
|
|
245
|
+
link.setAttribute("href", new URL(href, document.baseURI).href);
|
|
246
|
+
} catch (e) {}
|
|
247
|
+
});
|
|
248
|
+
return clone.querySelectorAll("img[src]").forEach(img => {
|
|
249
|
+
const src = img.getAttribute("src");
|
|
250
|
+
if (src && !src.startsWith("http://") && !src.startsWith("https://") && !src.startsWith("data:")) try {
|
|
251
|
+
img.setAttribute("src", new URL(src, document.baseURI).href);
|
|
252
|
+
} catch (e) {}
|
|
253
|
+
}), clone.innerHTML;
|
|
254
|
+
}
|
|
255
|
+
function cleanElement(obj) {
|
|
256
|
+
if (Array.isArray(obj)) return obj.map(cleanElement);
|
|
257
|
+
if (null !== obj && "object" == typeof obj) {
|
|
258
|
+
const cleaned = {};
|
|
259
|
+
for (const [key, value] of Object.entries(obj)) if (null != value) if ("object" == typeof value) {
|
|
260
|
+
const deepClean = cleanElement(value);
|
|
261
|
+
Object.keys(deepClean).length > 0 && (cleaned[key] = deepClean);
|
|
262
|
+
} else cleaned[key] = value;
|
|
263
|
+
return cleaned;
|
|
264
|
+
}
|
|
265
|
+
return obj;
|
|
266
|
+
}
|
|
267
|
+
async function snapshot(options = {}) {
|
|
268
|
+
try {
|
|
269
|
+
!1 !== options.waitForStability && await async function(options = {}) {
|
|
270
|
+
const {minNodeCount: minNodeCount = 500, quietPeriod: quietPeriod = 200, maxWait: maxWait = 5e3} = options, startTime = Date.now();
|
|
271
|
+
return new Promise(resolve => {
|
|
272
|
+
if (document.querySelectorAll("*").length >= minNodeCount) {
|
|
273
|
+
let lastChange = Date.now();
|
|
274
|
+
const observer = new MutationObserver(() => {
|
|
275
|
+
lastChange = Date.now();
|
|
276
|
+
});
|
|
277
|
+
observer.observe(document.body, {
|
|
278
|
+
childList: !0,
|
|
279
|
+
subtree: !0,
|
|
280
|
+
attributes: !1
|
|
281
|
+
});
|
|
282
|
+
const checkStable = () => {
|
|
283
|
+
const timeSinceLastChange = Date.now() - lastChange, totalWait = Date.now() - startTime;
|
|
284
|
+
timeSinceLastChange >= quietPeriod || totalWait >= maxWait ? (observer.disconnect(),
|
|
285
|
+
resolve()) : setTimeout(checkStable, 50);
|
|
286
|
+
};
|
|
287
|
+
checkStable();
|
|
288
|
+
} else {
|
|
289
|
+
const observer = new MutationObserver(() => {
|
|
290
|
+
const currentCount = document.querySelectorAll("*").length, totalWait = Date.now() - startTime;
|
|
291
|
+
if (currentCount >= minNodeCount) {
|
|
292
|
+
observer.disconnect();
|
|
293
|
+
let lastChange = Date.now();
|
|
294
|
+
const quietObserver = new MutationObserver(() => {
|
|
295
|
+
lastChange = Date.now();
|
|
296
|
+
});
|
|
297
|
+
quietObserver.observe(document.body, {
|
|
298
|
+
childList: !0,
|
|
299
|
+
subtree: !0,
|
|
300
|
+
attributes: !1
|
|
301
|
+
});
|
|
302
|
+
const checkQuiet = () => {
|
|
303
|
+
const timeSinceLastChange = Date.now() - lastChange, totalWait = Date.now() - startTime;
|
|
304
|
+
timeSinceLastChange >= quietPeriod || totalWait >= maxWait ? (quietObserver.disconnect(),
|
|
305
|
+
resolve()) : setTimeout(checkQuiet, 50);
|
|
306
|
+
};
|
|
307
|
+
checkQuiet();
|
|
308
|
+
} else totalWait >= maxWait && (observer.disconnect(), resolve());
|
|
309
|
+
});
|
|
310
|
+
observer.observe(document.body, {
|
|
311
|
+
childList: !0,
|
|
312
|
+
subtree: !0,
|
|
313
|
+
attributes: !1
|
|
314
|
+
}), setTimeout(() => {
|
|
315
|
+
observer.disconnect(), resolve();
|
|
316
|
+
}, maxWait);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
}(options.waitForStability || {});
|
|
320
|
+
const rawData = [];
|
|
321
|
+
window.sentience_registry = [];
|
|
322
|
+
getAllElements().forEach((el, idx) => {
|
|
323
|
+
if (!el.getBoundingClientRect) return;
|
|
324
|
+
const rect = el.getBoundingClientRect();
|
|
325
|
+
if (rect.width < 5 || rect.height < 5) return;
|
|
326
|
+
if ("span" === el.tagName.toLowerCase()) {
|
|
327
|
+
if (el.closest("a")) return;
|
|
328
|
+
const childLink = el.querySelector("a[href]");
|
|
329
|
+
if (childLink && childLink.href) return;
|
|
330
|
+
options.debug && el.className && el.className.includes("titleline");
|
|
331
|
+
}
|
|
332
|
+
window.sentience_registry[idx] = el;
|
|
333
|
+
const semanticText = function(el, options = {}) {
|
|
334
|
+
if (!el) return {
|
|
335
|
+
text: "",
|
|
336
|
+
source: null
|
|
337
|
+
};
|
|
338
|
+
const explicitAriaLabel = el.getAttribute ? el.getAttribute("aria-label") : null;
|
|
339
|
+
if (explicitAriaLabel && explicitAriaLabel.trim()) return {
|
|
340
|
+
text: explicitAriaLabel.trim(),
|
|
341
|
+
source: "explicit_aria_label"
|
|
342
|
+
};
|
|
343
|
+
if ("INPUT" === el.tagName) {
|
|
344
|
+
const value = (el.value || el.placeholder || "").trim();
|
|
345
|
+
if (value) return {
|
|
346
|
+
text: value,
|
|
347
|
+
source: "input_value"
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
if ("IMG" === el.tagName) {
|
|
351
|
+
const alt = (el.alt || "").trim();
|
|
352
|
+
if (alt) return {
|
|
353
|
+
text: alt,
|
|
354
|
+
source: "img_alt"
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
const innerText = (el.innerText || "").trim();
|
|
358
|
+
if (innerText) return {
|
|
359
|
+
text: innerText.substring(0, 100),
|
|
360
|
+
source: "inner_text"
|
|
361
|
+
};
|
|
362
|
+
const inferred = getInferredLabel(el, {
|
|
363
|
+
enableInference: !1 !== options.enableInference,
|
|
364
|
+
inferenceConfig: options.inferenceConfig
|
|
365
|
+
});
|
|
366
|
+
return inferred || {
|
|
367
|
+
text: "",
|
|
368
|
+
source: null
|
|
369
|
+
};
|
|
370
|
+
}(el, {
|
|
371
|
+
enableInference: !1 !== options.enableInference,
|
|
372
|
+
inferenceConfig: options.inferenceConfig
|
|
373
|
+
}), textVal = semanticText.text || getText(el), inferredRole = function(el, options = {}) {
|
|
374
|
+
const {enableInference: enableInference = !0} = options;
|
|
375
|
+
if (!enableInference) return null;
|
|
376
|
+
if (!function(el) {
|
|
377
|
+
if (!el || !el.tagName) return !1;
|
|
378
|
+
const tag = el.tagName.toLowerCase(), role = el.getAttribute ? el.getAttribute("role") : null, hasTabIndex = !!el.hasAttribute && el.hasAttribute("tabindex"), hasHref = "A" === el.tagName && !!el.hasAttribute && el.hasAttribute("href");
|
|
379
|
+
return [ "button", "input", "textarea", "select", "option", "details", "summary", "a" ].includes(tag) ? !("a" === tag && !hasHref) : !(!role || ![ "button", "link", "tab", "menuitem", "checkbox", "radio", "switch", "slider", "combobox", "textbox", "searchbox", "spinbutton" ].includes(role.toLowerCase())) || (!!hasTabIndex || (!!(el.onclick || el.onkeydown || el.onkeypress || el.onkeyup) || !(!el.getAttribute || !(el.getAttribute("onclick") || el.getAttribute("onkeydown") || el.getAttribute("onkeypress") || el.getAttribute("onkeyup")))));
|
|
380
|
+
}(el)) return null;
|
|
381
|
+
const hasAriaLabel = el.getAttribute ? el.getAttribute("aria-label") : null, hasExplicitRole = el.getAttribute ? el.getAttribute("role") : null;
|
|
382
|
+
if (hasAriaLabel || hasExplicitRole) return null;
|
|
383
|
+
const tag = el.tagName.toLowerCase();
|
|
384
|
+
return [ "button", "a", "input", "textarea", "select", "option" ].includes(tag) ? null : el.onclick || el.getAttribute && el.getAttribute("onclick") || el.onkeydown || el.onkeypress || el.onkeyup || el.getAttribute && (el.getAttribute("onkeydown") || el.getAttribute("onkeypress") || el.getAttribute("onkeyup")) || el.hasAttribute && el.hasAttribute("tabindex") && ("div" === tag || "span" === tag) ? "button" : null;
|
|
385
|
+
}(el, {
|
|
386
|
+
enableInference: !1 !== options.enableInference,
|
|
387
|
+
inferenceConfig: options.inferenceConfig
|
|
388
|
+
}), inView = function(rect) {
|
|
389
|
+
return rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0;
|
|
390
|
+
}(rect), style = window.getComputedStyle(el), occluded = !!inView && function(el, rect, style) {
|
|
391
|
+
const zIndex = parseInt(style.zIndex, 10);
|
|
392
|
+
if ("static" === style.position && (isNaN(zIndex) || zIndex <= 10)) return !1;
|
|
393
|
+
const cx = rect.x + rect.width / 2, cy = rect.y + rect.height / 2;
|
|
394
|
+
if (cx < 0 || cx > window.innerWidth || cy < 0 || cy > window.innerHeight) return !1;
|
|
395
|
+
const topEl = document.elementFromPoint(cx, cy);
|
|
396
|
+
return !!topEl && !(el === topEl || el.contains(topEl) || topEl.contains(el));
|
|
397
|
+
}(el, rect, style), effectiveBgColor = function(el) {
|
|
398
|
+
if (!el) return null;
|
|
399
|
+
if ("SVG" === el.tagName) {
|
|
400
|
+
const svgColor = getSVGColor(el);
|
|
401
|
+
if (svgColor) return svgColor;
|
|
402
|
+
}
|
|
403
|
+
let current = el, depth = 0;
|
|
404
|
+
for (;current && depth < 10; ) {
|
|
405
|
+
const style = window.getComputedStyle(current);
|
|
406
|
+
if ("SVG" === current.tagName) {
|
|
407
|
+
const svgColor = getSVGColor(current);
|
|
408
|
+
if (svgColor) return svgColor;
|
|
409
|
+
}
|
|
410
|
+
const bgColor = style.backgroundColor;
|
|
411
|
+
if (bgColor && "transparent" !== bgColor && "rgba(0, 0, 0, 0)" !== bgColor) {
|
|
412
|
+
const rgbaMatch = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
413
|
+
if (!rgbaMatch) return bgColor.startsWith("rgb("), bgColor;
|
|
414
|
+
if ((rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1) >= .9) return `rgb(${rgbaMatch[1]}, ${rgbaMatch[2]}, ${rgbaMatch[3]})`;
|
|
415
|
+
}
|
|
416
|
+
current = current.parentElement, depth++;
|
|
417
|
+
}
|
|
418
|
+
return null;
|
|
419
|
+
}(el);
|
|
420
|
+
rawData.push({
|
|
421
|
+
id: idx,
|
|
422
|
+
tag: el.tagName.toLowerCase(),
|
|
423
|
+
rect: {
|
|
424
|
+
x: rect.x,
|
|
425
|
+
y: rect.y,
|
|
426
|
+
width: rect.width,
|
|
427
|
+
height: rect.height
|
|
428
|
+
},
|
|
429
|
+
styles: {
|
|
430
|
+
display: toSafeString(style.display),
|
|
431
|
+
visibility: toSafeString(style.visibility),
|
|
432
|
+
opacity: toSafeString(style.opacity),
|
|
433
|
+
z_index: toSafeString(style.zIndex || "auto"),
|
|
434
|
+
position: toSafeString(style.position),
|
|
435
|
+
bg_color: toSafeString(effectiveBgColor || style.backgroundColor),
|
|
436
|
+
color: toSafeString(style.color),
|
|
437
|
+
cursor: toSafeString(style.cursor),
|
|
438
|
+
font_weight: toSafeString(style.fontWeight),
|
|
439
|
+
font_size: toSafeString(style.fontSize)
|
|
440
|
+
},
|
|
441
|
+
attributes: {
|
|
442
|
+
role: toSafeString(el.getAttribute("role")),
|
|
443
|
+
type_: toSafeString(el.getAttribute("type")),
|
|
444
|
+
aria_label: "explicit_aria_label" === semanticText?.source ? semanticText.text : toSafeString(el.getAttribute("aria-label")),
|
|
445
|
+
inferred_label: semanticText?.source && ![ "explicit_aria_label", "input_value", "img_alt", "inner_text" ].includes(semanticText.source) ? toSafeString(semanticText.text) : null,
|
|
446
|
+
label_source: semanticText?.source || null,
|
|
447
|
+
inferred_role: inferredRole ? toSafeString(inferredRole) : null,
|
|
448
|
+
href: toSafeString(el.href || el.getAttribute("href") || el.closest && el.closest("a")?.href || null),
|
|
449
|
+
class: toSafeString(getClassName(el)),
|
|
450
|
+
value: void 0 !== el.value ? toSafeString(el.value) : toSafeString(el.getAttribute("value")),
|
|
451
|
+
checked: void 0 !== el.checked ? String(el.checked) : null
|
|
452
|
+
},
|
|
453
|
+
text: toSafeString(textVal),
|
|
454
|
+
in_viewport: inView,
|
|
455
|
+
is_occluded: occluded,
|
|
456
|
+
scroll_y: window.scrollY
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
const allRawElements = [ ...rawData ];
|
|
460
|
+
let totalIframeElements = 0;
|
|
461
|
+
if (!1 !== options.collectIframes) try {
|
|
462
|
+
const iframeSnapshots = await async function(options = {}) {
|
|
463
|
+
const iframeData = new Map, iframes = Array.from(document.querySelectorAll("iframe"));
|
|
464
|
+
if (0 === iframes.length) return iframeData;
|
|
465
|
+
const iframePromises = iframes.map((iframe, idx) => {
|
|
466
|
+
const src = iframe.src || "";
|
|
467
|
+
return src.includes("doubleclick") || src.includes("googleadservices") || src.includes("ads system") ? Promise.resolve(null) : new Promise(resolve => {
|
|
468
|
+
const requestId = `iframe-${idx}-${Date.now()}`, timeout = setTimeout(() => {
|
|
469
|
+
resolve(null);
|
|
470
|
+
}, 5e3), listener = event => {
|
|
471
|
+
"SENTIENCE_IFRAME_SNAPSHOT_RESPONSE" === event.data?.type && event.data, "SENTIENCE_IFRAME_SNAPSHOT_RESPONSE" === event.data?.type && event.data?.requestId === requestId && (clearTimeout(timeout),
|
|
472
|
+
window.removeEventListener("message", listener), event.data.error ? resolve(null) : (event.data.snapshot,
|
|
473
|
+
resolve({
|
|
474
|
+
iframe: iframe,
|
|
475
|
+
data: event.data.snapshot,
|
|
476
|
+
error: null
|
|
477
|
+
})));
|
|
478
|
+
};
|
|
479
|
+
window.addEventListener("message", listener);
|
|
480
|
+
try {
|
|
481
|
+
iframe.contentWindow ? iframe.contentWindow.postMessage({
|
|
482
|
+
type: "SENTIENCE_IFRAME_SNAPSHOT_REQUEST",
|
|
483
|
+
requestId: requestId,
|
|
484
|
+
options: {
|
|
485
|
+
...options,
|
|
486
|
+
collectIframes: !0
|
|
487
|
+
}
|
|
488
|
+
}, "*") : (clearTimeout(timeout), window.removeEventListener("message", listener),
|
|
489
|
+
resolve(null));
|
|
490
|
+
} catch (error) {
|
|
491
|
+
clearTimeout(timeout), window.removeEventListener("message", listener), resolve(null);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
return (await Promise.all(iframePromises)).forEach((result, idx) => {
|
|
496
|
+
result && result.data && !result.error ? iframeData.set(iframes[idx], result.data) : result && result.error;
|
|
497
|
+
}), iframeData;
|
|
498
|
+
}(options);
|
|
499
|
+
iframeSnapshots.size > 0 && iframeSnapshots.forEach((iframeSnapshot, iframeEl) => {
|
|
500
|
+
if (iframeSnapshot && iframeSnapshot.raw_elements) {
|
|
501
|
+
iframeSnapshot.raw_elements.length;
|
|
502
|
+
const iframeRect = iframeEl.getBoundingClientRect(), offset = {
|
|
503
|
+
x: iframeRect.x,
|
|
504
|
+
y: iframeRect.y
|
|
505
|
+
}, iframeSrc = iframeEl.src || iframeEl.getAttribute("src") || "";
|
|
506
|
+
let isSameOrigin = !1;
|
|
507
|
+
try {
|
|
508
|
+
isSameOrigin = null !== iframeEl.contentWindow;
|
|
509
|
+
} catch (e) {
|
|
510
|
+
isSameOrigin = !1;
|
|
511
|
+
}
|
|
512
|
+
const adjustedElements = iframeSnapshot.raw_elements.map(el => {
|
|
513
|
+
const adjusted = {
|
|
514
|
+
...el
|
|
515
|
+
};
|
|
516
|
+
return adjusted.rect && (adjusted.rect = {
|
|
517
|
+
...adjusted.rect,
|
|
518
|
+
x: adjusted.rect.x + offset.x,
|
|
519
|
+
y: adjusted.rect.y + offset.y
|
|
520
|
+
}), adjusted.iframe_context = {
|
|
521
|
+
src: iframeSrc,
|
|
522
|
+
is_same_origin: isSameOrigin
|
|
523
|
+
}, adjusted;
|
|
524
|
+
});
|
|
525
|
+
allRawElements.push(...adjustedElements), totalIframeElements += adjustedElements.length;
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
} catch (error) {}
|
|
529
|
+
const processed = await function(rawData, options) {
|
|
530
|
+
return new Promise((resolve, reject) => {
|
|
531
|
+
const requestId = Math.random().toString(36).substring(7);
|
|
532
|
+
let resolved = !1;
|
|
533
|
+
const timeout = setTimeout(() => {
|
|
534
|
+
resolved || (resolved = !0, window.removeEventListener("message", listener), reject(new Error("WASM processing timeout - extension may be unresponsive. Try reloading the extension.")));
|
|
535
|
+
}, 25e3), listener = e => {
|
|
536
|
+
if ("SENTIENCE_SNAPSHOT_RESULT" === e.data.type && e.data.requestId === requestId) {
|
|
537
|
+
if (resolved) return;
|
|
538
|
+
resolved = !0, clearTimeout(timeout), window.removeEventListener("message", listener),
|
|
539
|
+
e.data.error ? reject(new Error(e.data.error)) : resolve({
|
|
540
|
+
elements: e.data.elements,
|
|
541
|
+
raw_elements: e.data.raw_elements,
|
|
542
|
+
duration: e.data.duration
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
window.addEventListener("message", listener);
|
|
547
|
+
try {
|
|
548
|
+
window.postMessage({
|
|
549
|
+
type: "SENTIENCE_SNAPSHOT_REQUEST",
|
|
550
|
+
requestId: requestId,
|
|
551
|
+
rawData: rawData,
|
|
552
|
+
options: options
|
|
553
|
+
}, "*");
|
|
554
|
+
} catch (error) {
|
|
555
|
+
resolved || (resolved = !0, clearTimeout(timeout), window.removeEventListener("message", listener),
|
|
556
|
+
reject(new Error(`Failed to send snapshot request: ${error.message}`)));
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
}(allRawElements, options);
|
|
560
|
+
if (!processed || !processed.elements) throw new Error("WASM processing returned invalid result");
|
|
561
|
+
let screenshot = null;
|
|
562
|
+
options.screenshot && (screenshot = await function(options) {
|
|
563
|
+
return new Promise(resolve => {
|
|
564
|
+
const requestId = Math.random().toString(36).substring(7), listener = e => {
|
|
565
|
+
"SENTIENCE_SCREENSHOT_RESULT" === e.data.type && e.data.requestId === requestId && (window.removeEventListener("message", listener),
|
|
566
|
+
resolve(e.data.screenshot));
|
|
567
|
+
};
|
|
568
|
+
window.addEventListener("message", listener), window.postMessage({
|
|
569
|
+
type: "SENTIENCE_SCREENSHOT_REQUEST",
|
|
570
|
+
requestId: requestId,
|
|
571
|
+
options: options
|
|
572
|
+
}, "*"), setTimeout(() => {
|
|
573
|
+
window.removeEventListener("message", listener), resolve(null);
|
|
574
|
+
}, 1e4);
|
|
575
|
+
});
|
|
576
|
+
}(options.screenshot));
|
|
577
|
+
const cleanedElements = cleanElement(processed.elements), cleanedRawElements = cleanElement(processed.raw_elements);
|
|
578
|
+
cleanedElements.length, cleanedRawElements.length;
|
|
579
|
+
return {
|
|
580
|
+
status: "success",
|
|
581
|
+
url: window.location.href,
|
|
582
|
+
viewport: {
|
|
583
|
+
width: window.innerWidth,
|
|
584
|
+
height: window.innerHeight
|
|
585
|
+
},
|
|
586
|
+
elements: cleanedElements,
|
|
587
|
+
raw_elements: cleanedRawElements,
|
|
588
|
+
screenshot: screenshot
|
|
589
|
+
};
|
|
590
|
+
} catch (error) {
|
|
591
|
+
return {
|
|
592
|
+
status: "error",
|
|
593
|
+
error: error.message || "Unknown error",
|
|
594
|
+
stack: error.stack
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
function read(options = {}) {
|
|
599
|
+
const format = options.format || "raw";
|
|
600
|
+
let content;
|
|
601
|
+
return content = "raw" === format ? getRawHTML(document.body) : "markdown" === format ? function(root) {
|
|
602
|
+
const rawHTML = getRawHTML(root), tempDiv = document.createElement("div");
|
|
603
|
+
tempDiv.innerHTML = rawHTML;
|
|
604
|
+
let markdown = "", insideLink = !1;
|
|
605
|
+
return function walk(node) {
|
|
606
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
607
|
+
const text = node.textContent.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ");
|
|
608
|
+
return void (text.trim() && (markdown += text));
|
|
609
|
+
}
|
|
610
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
611
|
+
const tag = node.tagName.toLowerCase();
|
|
612
|
+
if ("h1" === tag && (markdown += "\n# "), "h2" === tag && (markdown += "\n## "),
|
|
613
|
+
"h3" === tag && (markdown += "\n### "), "li" === tag && (markdown += "\n- "), insideLink || "p" !== tag && "div" !== tag && "br" !== tag || (markdown += "\n"),
|
|
614
|
+
"strong" !== tag && "b" !== tag || (markdown += "**"), "em" !== tag && "i" !== tag || (markdown += "_"),
|
|
615
|
+
"a" === tag && (markdown += "[", insideLink = !0), node.shadowRoot ? Array.from(node.shadowRoot.childNodes).forEach(walk) : node.childNodes.forEach(walk),
|
|
616
|
+
"a" === tag) {
|
|
617
|
+
const href = node.getAttribute("href");
|
|
618
|
+
markdown += href ? `](${href})` : "]", insideLink = !1;
|
|
619
|
+
}
|
|
620
|
+
"strong" !== tag && "b" !== tag || (markdown += "**"), "em" !== tag && "i" !== tag || (markdown += "_"),
|
|
621
|
+
insideLink || "h1" !== tag && "h2" !== tag && "h3" !== tag && "p" !== tag && "div" !== tag || (markdown += "\n");
|
|
622
|
+
}(tempDiv), markdown.replace(/\n{3,}/g, "\n\n").trim();
|
|
623
|
+
}(document.body) : function(root) {
|
|
624
|
+
let text = "";
|
|
625
|
+
return function walk(node) {
|
|
626
|
+
if (node.nodeType !== Node.TEXT_NODE) {
|
|
627
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
628
|
+
const tag = node.tagName.toLowerCase();
|
|
629
|
+
if ([ "nav", "footer", "header", "script", "style", "noscript", "iframe", "svg" ].includes(tag)) return;
|
|
630
|
+
const style = window.getComputedStyle(node);
|
|
631
|
+
if ("none" === style.display || "hidden" === style.visibility) return;
|
|
632
|
+
const isBlock = "block" === style.display || "flex" === style.display || "P" === node.tagName || "DIV" === node.tagName;
|
|
633
|
+
isBlock && (text += " "), node.shadowRoot ? Array.from(node.shadowRoot.childNodes).forEach(walk) : node.childNodes.forEach(walk),
|
|
634
|
+
isBlock && (text += "\n");
|
|
635
|
+
}
|
|
636
|
+
} else text += node.textContent;
|
|
637
|
+
}(root || document.body), text.replace(/\n{3,}/g, "\n\n").trim();
|
|
638
|
+
}(document.body), {
|
|
639
|
+
status: "success",
|
|
640
|
+
url: window.location.href,
|
|
641
|
+
format: format,
|
|
642
|
+
content: content,
|
|
643
|
+
length: content.length
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function findTextRect(options = {}) {
|
|
647
|
+
const {text: text, containerElement: containerElement = document.body, caseSensitive: caseSensitive = !1, wholeWord: wholeWord = !1, maxResults: maxResults = 10} = options;
|
|
648
|
+
if (!text || 0 === text.trim().length) return {
|
|
649
|
+
status: "error",
|
|
650
|
+
error: "Text parameter is required"
|
|
651
|
+
};
|
|
652
|
+
const results = [], searchText = caseSensitive ? text : text.toLowerCase();
|
|
653
|
+
function findInTextNode(textNode) {
|
|
654
|
+
const nodeText = textNode.nodeValue, searchableText = caseSensitive ? nodeText : nodeText.toLowerCase();
|
|
655
|
+
let startIndex = 0;
|
|
656
|
+
for (;startIndex < nodeText.length && results.length < maxResults; ) {
|
|
657
|
+
const foundIndex = searchableText.indexOf(searchText, startIndex);
|
|
658
|
+
if (-1 === foundIndex) break;
|
|
659
|
+
if (wholeWord) {
|
|
660
|
+
const before = foundIndex > 0 ? nodeText[foundIndex - 1] : " ", after = foundIndex + text.length < nodeText.length ? nodeText[foundIndex + text.length] : " ";
|
|
661
|
+
if (!/\s/.test(before) || !/\s/.test(after)) {
|
|
662
|
+
startIndex = foundIndex + 1;
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
try {
|
|
667
|
+
const range = document.createRange();
|
|
668
|
+
range.setStart(textNode, foundIndex), range.setEnd(textNode, foundIndex + text.length);
|
|
669
|
+
const rect = range.getBoundingClientRect();
|
|
670
|
+
rect.width > 0 && rect.height > 0 && results.push({
|
|
671
|
+
text: nodeText.substring(foundIndex, foundIndex + text.length),
|
|
672
|
+
rect: {
|
|
673
|
+
x: rect.left + window.scrollX,
|
|
674
|
+
y: rect.top + window.scrollY,
|
|
675
|
+
width: rect.width,
|
|
676
|
+
height: rect.height,
|
|
677
|
+
left: rect.left + window.scrollX,
|
|
678
|
+
top: rect.top + window.scrollY,
|
|
679
|
+
right: rect.right + window.scrollX,
|
|
680
|
+
bottom: rect.bottom + window.scrollY
|
|
681
|
+
},
|
|
682
|
+
viewport_rect: {
|
|
683
|
+
x: rect.left,
|
|
684
|
+
y: rect.top,
|
|
685
|
+
width: rect.width,
|
|
686
|
+
height: rect.height
|
|
687
|
+
},
|
|
688
|
+
context: {
|
|
689
|
+
before: nodeText.substring(Math.max(0, foundIndex - 20), foundIndex),
|
|
690
|
+
after: nodeText.substring(foundIndex + text.length, Math.min(nodeText.length, foundIndex + text.length + 20))
|
|
691
|
+
},
|
|
692
|
+
in_viewport: rect.top >= 0 && rect.left >= 0 && rect.bottom <= window.innerHeight && rect.right <= window.innerWidth
|
|
693
|
+
});
|
|
694
|
+
} catch (e) {}
|
|
695
|
+
startIndex = foundIndex + 1;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
const walker = document.createTreeWalker(containerElement, NodeFilter.SHOW_TEXT, {
|
|
699
|
+
acceptNode(node) {
|
|
700
|
+
const parent = node.parentElement;
|
|
701
|
+
if (!parent) return NodeFilter.FILTER_REJECT;
|
|
702
|
+
const tagName = parent.tagName.toLowerCase();
|
|
703
|
+
if ("script" === tagName || "style" === tagName || "noscript" === tagName) return NodeFilter.FILTER_REJECT;
|
|
704
|
+
if (!node.nodeValue || 0 === node.nodeValue.trim().length) return NodeFilter.FILTER_REJECT;
|
|
705
|
+
const computedStyle = window.getComputedStyle(parent);
|
|
706
|
+
return "none" === computedStyle.display || "hidden" === computedStyle.visibility || "0" === computedStyle.opacity ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
let currentNode;
|
|
710
|
+
for (;(currentNode = walker.nextNode()) && results.length < maxResults; ) findInTextNode(currentNode);
|
|
711
|
+
return {
|
|
712
|
+
status: "success",
|
|
713
|
+
query: text,
|
|
714
|
+
case_sensitive: caseSensitive,
|
|
715
|
+
whole_word: wholeWord,
|
|
716
|
+
matches: results.length,
|
|
717
|
+
results: results,
|
|
718
|
+
viewport: {
|
|
719
|
+
width: window.innerWidth,
|
|
720
|
+
height: window.innerHeight,
|
|
721
|
+
scroll_x: window.scrollX,
|
|
722
|
+
scroll_y: window.scrollY
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
function click(id) {
|
|
727
|
+
const el = window.sentience_registry[id];
|
|
728
|
+
return !!el && (el.click(), el.focus(), !0);
|
|
729
|
+
}
|
|
730
|
+
function startRecording(options = {}) {
|
|
731
|
+
const {highlightColor: highlightColor = "#ff0000", successColor: successColor = "#00ff00", autoDisableTimeout: autoDisableTimeout = 18e5, keyboardShortcut: keyboardShortcut = "Ctrl+Shift+I"} = options;
|
|
732
|
+
if (!window.sentience_registry || 0 === window.sentience_registry.length) return alert("Registry empty. Run `await window.sentience.snapshot()` first!"),
|
|
733
|
+
() => {};
|
|
734
|
+
window.sentience_registry_map = new Map, window.sentience_registry.forEach((el, idx) => {
|
|
735
|
+
el && window.sentience_registry_map.set(el, idx);
|
|
736
|
+
});
|
|
737
|
+
let highlightBox = document.getElementById("sentience-highlight-box");
|
|
738
|
+
highlightBox || (highlightBox = document.createElement("div"), highlightBox.id = "sentience-highlight-box",
|
|
739
|
+
highlightBox.style.cssText = `\n position: fixed;\n pointer-events: none;\n z-index: 2147483647;\n border: 2px solid ${highlightColor};\n background: rgba(255, 0, 0, 0.1);\n display: none;\n transition: all 0.1s ease;\n box-sizing: border-box;\n `,
|
|
740
|
+
document.body.appendChild(highlightBox));
|
|
741
|
+
let recordingIndicator = document.getElementById("sentience-recording-indicator");
|
|
742
|
+
recordingIndicator || (recordingIndicator = document.createElement("div"), recordingIndicator.id = "sentience-recording-indicator",
|
|
743
|
+
recordingIndicator.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 3px;\n background: ${highlightColor};\n z-index: 2147483646;\n pointer-events: none;\n `,
|
|
744
|
+
document.body.appendChild(recordingIndicator)), recordingIndicator.style.display = "block";
|
|
745
|
+
const mouseOverHandler = e => {
|
|
746
|
+
const el = e.target;
|
|
747
|
+
if (!el || el === highlightBox || el === recordingIndicator) return;
|
|
748
|
+
const rect = el.getBoundingClientRect();
|
|
749
|
+
highlightBox.style.display = "block", highlightBox.style.top = rect.top + window.scrollY + "px",
|
|
750
|
+
highlightBox.style.left = rect.left + window.scrollX + "px", highlightBox.style.width = rect.width + "px",
|
|
751
|
+
highlightBox.style.height = rect.height + "px";
|
|
752
|
+
}, clickHandler = e => {
|
|
753
|
+
e.preventDefault(), e.stopPropagation();
|
|
754
|
+
const el = e.target;
|
|
755
|
+
if (!el || el === highlightBox || el === recordingIndicator) return;
|
|
756
|
+
const sentienceId = window.sentience_registry_map.get(el);
|
|
757
|
+
if (void 0 === sentienceId) return void alert("Element not in registry. Run `await window.sentience.snapshot()` first!");
|
|
758
|
+
const rawData = function(el) {
|
|
759
|
+
const style = window.getComputedStyle(el), rect = el.getBoundingClientRect();
|
|
760
|
+
return {
|
|
761
|
+
tag: el.tagName,
|
|
762
|
+
rect: {
|
|
763
|
+
x: Math.round(rect.x),
|
|
764
|
+
y: Math.round(rect.y),
|
|
765
|
+
width: Math.round(rect.width),
|
|
766
|
+
height: Math.round(rect.height)
|
|
767
|
+
},
|
|
768
|
+
styles: {
|
|
769
|
+
cursor: style.cursor || null,
|
|
770
|
+
backgroundColor: style.backgroundColor || null,
|
|
771
|
+
color: style.color || null,
|
|
772
|
+
fontWeight: style.fontWeight || null,
|
|
773
|
+
fontSize: style.fontSize || null,
|
|
774
|
+
display: style.display || null,
|
|
775
|
+
position: style.position || null,
|
|
776
|
+
zIndex: style.zIndex || null,
|
|
777
|
+
opacity: style.opacity || null,
|
|
778
|
+
visibility: style.visibility || null
|
|
779
|
+
},
|
|
780
|
+
attributes: {
|
|
781
|
+
role: el.getAttribute("role") || null,
|
|
782
|
+
type: el.getAttribute("type") || null,
|
|
783
|
+
ariaLabel: el.getAttribute("aria-label") || null,
|
|
784
|
+
id: el.id || null,
|
|
785
|
+
className: el.className || null
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
}(el), selector = function(el) {
|
|
789
|
+
if (!el || !el.tagName) return "";
|
|
790
|
+
if (el.id) return `#${el.id}`;
|
|
791
|
+
for (const attr of el.attributes) if (attr.name.startsWith("data-") || "aria-label" === attr.name) {
|
|
792
|
+
const value = attr.value ? attr.value.replace(/"/g, '\\"') : "";
|
|
793
|
+
return `${el.tagName.toLowerCase()}[${attr.name}="${value}"]`;
|
|
794
|
+
}
|
|
795
|
+
const path = [];
|
|
796
|
+
let current = el;
|
|
797
|
+
for (;current && current !== document.body && current !== document.documentElement; ) {
|
|
798
|
+
let selector = current.tagName.toLowerCase();
|
|
799
|
+
if (current.id) {
|
|
800
|
+
selector = `#${current.id}`, path.unshift(selector);
|
|
801
|
+
break;
|
|
802
|
+
}
|
|
803
|
+
if (current.className && "string" == typeof current.className) {
|
|
804
|
+
const classes = current.className.trim().split(/\s+/).filter(c => c);
|
|
805
|
+
classes.length > 0 && (selector += `.${classes[0]}`);
|
|
806
|
+
}
|
|
807
|
+
if (current.parentElement) {
|
|
808
|
+
const sameTagSiblings = Array.from(current.parentElement.children).filter(s => s.tagName === current.tagName), index = sameTagSiblings.indexOf(current);
|
|
809
|
+
(index > 0 || sameTagSiblings.length > 1) && (selector += `:nth-of-type(${index + 1})`);
|
|
810
|
+
}
|
|
811
|
+
path.unshift(selector), current = current.parentElement;
|
|
812
|
+
}
|
|
813
|
+
return path.join(" > ") || el.tagName.toLowerCase();
|
|
814
|
+
}(el), role = el.getAttribute("role") || el.tagName.toLowerCase(), text = getText(el), snippet = {
|
|
815
|
+
task: `Interact with ${text.substring(0, 20)}${text.length > 20 ? "..." : ""}`,
|
|
816
|
+
url: window.location.href,
|
|
817
|
+
timestamp: (new Date).toISOString(),
|
|
818
|
+
target_criteria: {
|
|
819
|
+
id: sentienceId,
|
|
820
|
+
selector: selector,
|
|
821
|
+
role: role,
|
|
822
|
+
text: text.substring(0, 50)
|
|
823
|
+
},
|
|
824
|
+
debug_snapshot: rawData
|
|
825
|
+
}, jsonString = JSON.stringify(snippet, null, 2);
|
|
826
|
+
navigator.clipboard.writeText(jsonString).then(() => {
|
|
827
|
+
highlightBox.style.border = `2px solid ${successColor}`, highlightBox.style.background = "rgba(0, 255, 0, 0.2)",
|
|
828
|
+
setTimeout(() => {
|
|
829
|
+
highlightBox.style.border = `2px solid ${highlightColor}`, highlightBox.style.background = "rgba(255, 0, 0, 0.1)";
|
|
830
|
+
}, 500);
|
|
831
|
+
}).catch(err => {
|
|
832
|
+
alert("Failed to copy to clipboard. Check console for JSON.");
|
|
833
|
+
});
|
|
834
|
+
};
|
|
835
|
+
let timeoutId = null;
|
|
836
|
+
const stopRecording = () => {
|
|
837
|
+
document.removeEventListener("mouseover", mouseOverHandler, !0), document.removeEventListener("click", clickHandler, !0),
|
|
838
|
+
document.removeEventListener("keydown", keyboardHandler, !0), timeoutId && (clearTimeout(timeoutId),
|
|
839
|
+
timeoutId = null), highlightBox && (highlightBox.style.display = "none"), recordingIndicator && (recordingIndicator.style.display = "none"),
|
|
840
|
+
window.sentience_registry_map && window.sentience_registry_map.clear(), window.sentience_stopRecording === stopRecording && delete window.sentience_stopRecording;
|
|
841
|
+
}, keyboardHandler = e => {
|
|
842
|
+
(e.ctrlKey || e.metaKey) && e.shiftKey && "I" === e.key && (e.preventDefault(),
|
|
843
|
+
stopRecording());
|
|
844
|
+
};
|
|
845
|
+
return document.addEventListener("mouseover", mouseOverHandler, !0), document.addEventListener("click", clickHandler, !0),
|
|
846
|
+
document.addEventListener("keydown", keyboardHandler, !0), autoDisableTimeout > 0 && (timeoutId = setTimeout(() => {
|
|
847
|
+
stopRecording();
|
|
848
|
+
}, autoDisableTimeout)), window.sentience_stopRecording = stopRecording, stopRecording;
|
|
849
|
+
}
|
|
850
|
+
function showOverlay(elements, targetElementId = null) {
|
|
851
|
+
elements && Array.isArray(elements) && window.postMessage({
|
|
852
|
+
type: "SENTIENCE_SHOW_OVERLAY",
|
|
853
|
+
elements: elements,
|
|
854
|
+
targetElementId: targetElementId,
|
|
855
|
+
timestamp: Date.now()
|
|
856
|
+
}, "*");
|
|
857
|
+
}
|
|
858
|
+
function showGrid(grids, targetGridId = null) {
|
|
859
|
+
grids && Array.isArray(grids) && window.postMessage({
|
|
860
|
+
type: "SENTIENCE_SHOW_GRID_OVERLAY",
|
|
861
|
+
grids: grids,
|
|
862
|
+
targetGridId: targetGridId,
|
|
863
|
+
timestamp: Date.now()
|
|
864
|
+
}, "*");
|
|
865
|
+
}
|
|
866
|
+
function clearOverlay() {
|
|
867
|
+
window.postMessage({
|
|
868
|
+
type: "SENTIENCE_CLEAR_OVERLAY"
|
|
869
|
+
}, "*");
|
|
870
|
+
}
|
|
871
|
+
(async () => {
|
|
872
|
+
const getExtensionId = () => document.documentElement.dataset.sentienceExtensionId;
|
|
873
|
+
let extId = getExtensionId();
|
|
874
|
+
extId || await new Promise(resolve => {
|
|
875
|
+
const check = setInterval(() => {
|
|
876
|
+
extId = getExtensionId(), extId && (clearInterval(check), resolve());
|
|
877
|
+
}, 50);
|
|
878
|
+
setTimeout(() => resolve(), 5e3);
|
|
879
|
+
}), extId && (window.sentience_registry = [], window.sentience = {
|
|
880
|
+
snapshot: snapshot,
|
|
881
|
+
read: read,
|
|
882
|
+
findTextRect: findTextRect,
|
|
883
|
+
click: click,
|
|
884
|
+
startRecording: startRecording,
|
|
885
|
+
showOverlay: showOverlay,
|
|
886
|
+
showGrid: showGrid,
|
|
887
|
+
clearOverlay: clearOverlay
|
|
888
|
+
}, window.sentience_iframe_handler_setup || (window.addEventListener("message", async event => {
|
|
889
|
+
if ("SENTIENCE_IFRAME_SNAPSHOT_REQUEST" === event.data?.type) {
|
|
890
|
+
const {requestId: requestId, options: options} = event.data;
|
|
891
|
+
try {
|
|
892
|
+
const snapshotOptions = {
|
|
893
|
+
...options,
|
|
894
|
+
collectIframes: !0,
|
|
895
|
+
waitForStability: (options.waitForStability, !1)
|
|
896
|
+
}, snapshot = await window.sentience.snapshot(snapshotOptions);
|
|
897
|
+
event.source && event.source.postMessage && event.source.postMessage({
|
|
898
|
+
type: "SENTIENCE_IFRAME_SNAPSHOT_RESPONSE",
|
|
899
|
+
requestId: requestId,
|
|
900
|
+
snapshot: snapshot,
|
|
901
|
+
error: null
|
|
902
|
+
}, "*");
|
|
903
|
+
} catch (error) {
|
|
904
|
+
event.source && event.source.postMessage && event.source.postMessage({
|
|
905
|
+
type: "SENTIENCE_IFRAME_SNAPSHOT_RESPONSE",
|
|
906
|
+
requestId: requestId,
|
|
907
|
+
snapshot: null,
|
|
908
|
+
error: error.message
|
|
909
|
+
}, "*");
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}), window.sentience_iframe_handler_setup = !0));
|
|
913
|
+
})();
|
|
914
|
+
}();
|