sentienceapi 0.90.16__py3-none-any.whl → 0.92.2__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 +14 -5
- sentience/action_executor.py +215 -0
- sentience/actions.py +408 -25
- sentience/agent.py +802 -293
- sentience/agent_config.py +3 -0
- sentience/async_api.py +83 -1142
- sentience/base_agent.py +95 -0
- sentience/browser.py +484 -1
- sentience/browser_evaluator.py +299 -0
- sentience/cloud_tracing.py +457 -33
- sentience/conversational_agent.py +77 -43
- sentience/element_filter.py +136 -0
- sentience/expect.py +98 -2
- sentience/extension/background.js +56 -185
- sentience/extension/content.js +117 -289
- sentience/extension/injected_api.js +799 -1374
- sentience/extension/manifest.json +1 -1
- sentience/extension/pkg/sentience_core.js +190 -396
- sentience/extension/pkg/sentience_core_bg.wasm +0 -0
- sentience/extension/release.json +47 -47
- sentience/formatting.py +9 -53
- sentience/inspector.py +183 -1
- sentience/llm_interaction_handler.py +191 -0
- sentience/llm_provider.py +74 -52
- sentience/llm_provider_utils.py +120 -0
- sentience/llm_response_builder.py +153 -0
- sentience/models.py +60 -1
- sentience/overlay.py +109 -2
- sentience/protocols.py +228 -0
- sentience/query.py +1 -1
- sentience/read.py +95 -3
- sentience/recorder.py +223 -3
- sentience/schemas/trace_v1.json +102 -9
- sentience/screenshot.py +48 -2
- sentience/sentience_methods.py +86 -0
- sentience/snapshot.py +291 -38
- sentience/snapshot_diff.py +141 -0
- sentience/text_search.py +119 -5
- sentience/trace_event_builder.py +129 -0
- sentience/trace_file_manager.py +197 -0
- sentience/trace_indexing/index_schema.py +95 -7
- sentience/trace_indexing/indexer.py +117 -14
- sentience/tracer_factory.py +119 -6
- sentience/tracing.py +172 -8
- 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 +1 -1
- sentience/visual_agent.py +2056 -0
- sentience/wait.py +68 -2
- {sentienceapi-0.90.16.dist-info → sentienceapi-0.92.2.dist-info}/METADATA +2 -1
- sentienceapi-0.92.2.dist-info/RECORD +65 -0
- sentience/extension/test-content.js +0 -4
- sentienceapi-0.90.16.dist-info/RECORD +0 -50
- {sentienceapi-0.90.16.dist-info → sentienceapi-0.92.2.dist-info}/WHEEL +0 -0
- {sentienceapi-0.90.16.dist-info → sentienceapi-0.92.2.dist-info}/entry_points.txt +0 -0
- {sentienceapi-0.90.16.dist-info → sentienceapi-0.92.2.dist-info}/licenses/LICENSE +0 -0
- {sentienceapi-0.90.16.dist-info → sentienceapi-0.92.2.dist-info}/licenses/LICENSE-APACHE +0 -0
- {sentienceapi-0.90.16.dist-info → sentienceapi-0.92.2.dist-info}/licenses/LICENSE-MIT +0 -0
- {sentienceapi-0.90.16.dist-info → sentienceapi-0.92.2.dist-info}/top_level.txt +0 -0
|
@@ -1,1473 +1,898 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const check = setInterval(() => {
|
|
13
|
-
extId = getExtensionId();
|
|
14
|
-
if (extId) { clearInterval(check); resolve(); }
|
|
15
|
-
}, 50);
|
|
16
|
-
setTimeout(() => resolve(), 5000); // Max 5s wait
|
|
17
|
-
});
|
|
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;
|
|
18
12
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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;
|
|
23
35
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return NodeFilter.FILTER_REJECT;
|
|
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;
|
|
38
49
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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 || {}
|
|
42
87
|
}
|
|
43
|
-
|
|
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
|
+
};
|
|
44
98
|
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
+
};
|
|
53
130
|
}
|
|
54
131
|
}
|
|
55
|
-
|
|
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;
|
|
56
159
|
}
|
|
57
|
-
|
|
58
|
-
// --- HELPER: Smart Text Extractor ---
|
|
59
160
|
function getText(el) {
|
|
60
|
-
|
|
61
|
-
if (el.tagName === 'INPUT') return el.value || el.placeholder || '';
|
|
62
|
-
if (el.tagName === 'IMG') return el.alt || '';
|
|
63
|
-
return (el.innerText || '').replace(/\s+/g, ' ').trim().substring(0, 100);
|
|
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);
|
|
64
162
|
}
|
|
65
|
-
|
|
66
|
-
// --- HELPER: Safe Class Name Extractor (Handles SVGAnimatedString) ---
|
|
67
163
|
function getClassName(el) {
|
|
68
|
-
if (!el || !el.className) return
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// Handle SVGAnimatedString (SVG elements)
|
|
74
|
-
if (typeof el.className === 'object') {
|
|
75
|
-
if ('baseVal' in el.className && typeof el.className.baseVal === 'string') {
|
|
76
|
-
return el.className.baseVal;
|
|
77
|
-
}
|
|
78
|
-
if ('animVal' in el.className && typeof el.className.animVal === 'string') {
|
|
79
|
-
return el.className.animVal;
|
|
80
|
-
}
|
|
81
|
-
// Fallback: convert to string
|
|
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;
|
|
82
169
|
try {
|
|
83
170
|
return String(el.className);
|
|
84
171
|
} catch (e) {
|
|
85
|
-
return
|
|
172
|
+
return "";
|
|
86
173
|
}
|
|
87
174
|
}
|
|
88
|
-
|
|
89
|
-
return '';
|
|
175
|
+
return "";
|
|
90
176
|
}
|
|
91
|
-
|
|
92
|
-
// --- HELPER: Paranoid String Converter (Handles SVGAnimatedString) ---
|
|
93
177
|
function toSafeString(value) {
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// 2. Handle SVG objects (SVGAnimatedString, SVGAnimatedNumber, etc.)
|
|
100
|
-
if (typeof value === 'object') {
|
|
101
|
-
// Try extracting baseVal (standard SVG property)
|
|
102
|
-
if ('baseVal' in value && typeof value.baseVal === 'string') {
|
|
103
|
-
return value.baseVal;
|
|
104
|
-
}
|
|
105
|
-
// Try animVal as fallback
|
|
106
|
-
if ('animVal' in value && typeof value.animVal === 'string') {
|
|
107
|
-
return value.animVal;
|
|
108
|
-
}
|
|
109
|
-
// Fallback: Force to string (prevents WASM crash even if data is less useful)
|
|
110
|
-
// This prevents the "Invalid Type" crash, even if the data is "[object SVGAnimatedString]"
|
|
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;
|
|
111
183
|
try {
|
|
112
184
|
return String(value);
|
|
113
185
|
} catch (e) {
|
|
114
186
|
return null;
|
|
115
187
|
}
|
|
116
188
|
}
|
|
117
|
-
|
|
118
|
-
// 3. Last resort cast for primitives
|
|
119
189
|
try {
|
|
120
190
|
return String(value);
|
|
121
191
|
} catch (e) {
|
|
122
192
|
return null;
|
|
123
193
|
}
|
|
124
194
|
}
|
|
125
|
-
|
|
126
|
-
// --- HELPER: Get SVG Fill/Stroke Color ---
|
|
127
|
-
// For SVG elements, get the fill or stroke color (SVGs use fill/stroke, not backgroundColor)
|
|
128
195
|
function getSVGColor(el) {
|
|
129
|
-
if (!el || el.tagName
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// Try fill first (most common for SVG icons)
|
|
134
|
-
const fill = style.fill;
|
|
135
|
-
if (fill && fill !== 'none' && fill !== 'transparent' && fill !== 'rgba(0, 0, 0, 0)') {
|
|
136
|
-
// Convert fill to rgb() format if needed
|
|
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) {
|
|
137
199
|
const rgbaMatch = fill.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
138
200
|
if (rgbaMatch) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return `rgb(${rgbaMatch[1]}, ${rgbaMatch[2]}, ${rgbaMatch[3]})`;
|
|
142
|
-
}
|
|
143
|
-
} else if (fill.startsWith('rgb(')) {
|
|
144
|
-
return fill;
|
|
145
|
-
}
|
|
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;
|
|
146
203
|
}
|
|
147
|
-
|
|
148
|
-
// Fallback to stroke if fill is not available
|
|
149
204
|
const stroke = style.stroke;
|
|
150
|
-
if (stroke &&
|
|
205
|
+
if (stroke && "none" !== stroke && "transparent" !== stroke && "rgba(0, 0, 0, 0)" !== stroke) {
|
|
151
206
|
const rgbaMatch = stroke.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
152
207
|
if (rgbaMatch) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return `rgb(${rgbaMatch[1]}, ${rgbaMatch[2]}, ${rgbaMatch[3]})`;
|
|
156
|
-
}
|
|
157
|
-
} else if (stroke.startsWith('rgb(')) {
|
|
158
|
-
return stroke;
|
|
159
|
-
}
|
|
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;
|
|
160
210
|
}
|
|
161
|
-
|
|
162
211
|
return null;
|
|
163
212
|
}
|
|
164
|
-
|
|
165
|
-
// --- HELPER: Get Effective Background Color ---
|
|
166
|
-
// Traverses up the DOM tree to find the nearest non-transparent background color
|
|
167
|
-
// For SVGs, also checks fill/stroke properties
|
|
168
|
-
// This handles rgba(0,0,0,0) and transparent values that browsers commonly return
|
|
169
|
-
function getEffectiveBackgroundColor(el) {
|
|
170
|
-
if (!el) return null;
|
|
171
|
-
|
|
172
|
-
// For SVG elements, use fill/stroke instead of backgroundColor
|
|
173
|
-
if (el.tagName === 'SVG') {
|
|
174
|
-
const svgColor = getSVGColor(el);
|
|
175
|
-
if (svgColor) return svgColor;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
let current = el;
|
|
179
|
-
const maxDepth = 10; // Prevent infinite loops
|
|
180
|
-
let depth = 0;
|
|
181
|
-
|
|
182
|
-
while (current && depth < maxDepth) {
|
|
183
|
-
const style = window.getComputedStyle(current);
|
|
184
|
-
|
|
185
|
-
// For SVG elements in the tree, also check fill/stroke
|
|
186
|
-
if (current.tagName === 'SVG') {
|
|
187
|
-
const svgColor = getSVGColor(current);
|
|
188
|
-
if (svgColor) return svgColor;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const bgColor = style.backgroundColor;
|
|
192
|
-
|
|
193
|
-
if (bgColor && bgColor !== 'transparent' && bgColor !== 'rgba(0, 0, 0, 0)') {
|
|
194
|
-
// Check if it's rgba with alpha < 1 (semi-transparent)
|
|
195
|
-
const rgbaMatch = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
196
|
-
if (rgbaMatch) {
|
|
197
|
-
const alpha = rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1.0;
|
|
198
|
-
// If alpha is high enough (>= 0.9), consider it opaque enough
|
|
199
|
-
if (alpha >= 0.9) {
|
|
200
|
-
// Convert to rgb() format for Gateway compatibility
|
|
201
|
-
return `rgb(${rgbaMatch[1]}, ${rgbaMatch[2]}, ${rgbaMatch[3]})`;
|
|
202
|
-
}
|
|
203
|
-
// If semi-transparent, continue up the tree
|
|
204
|
-
} else if (bgColor.startsWith('rgb(')) {
|
|
205
|
-
// Already in rgb() format, use it
|
|
206
|
-
return bgColor;
|
|
207
|
-
} else {
|
|
208
|
-
// Named color or other format, return as-is
|
|
209
|
-
return bgColor;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Move up the DOM tree
|
|
214
|
-
current = current.parentElement;
|
|
215
|
-
depth++;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Fallback: return null if nothing found
|
|
219
|
-
return null;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// --- HELPER: Viewport Check ---
|
|
223
|
-
function isInViewport(rect) {
|
|
224
|
-
return (
|
|
225
|
-
rect.top < window.innerHeight && rect.bottom > 0 &&
|
|
226
|
-
rect.left < window.innerWidth && rect.right > 0
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// --- HELPER: Occlusion Check (Optimized to avoid layout thrashing) ---
|
|
231
|
-
// Only checks occlusion for elements likely to be occluded (high z-index, positioned)
|
|
232
|
-
// This avoids forced reflow for most elements, dramatically improving performance
|
|
233
|
-
function isOccluded(el, rect, style) {
|
|
234
|
-
// Fast path: Skip occlusion check for most elements
|
|
235
|
-
// Only check for elements that are likely to be occluded (overlays, modals, tooltips)
|
|
236
|
-
const zIndex = parseInt(style.zIndex, 10);
|
|
237
|
-
const position = style.position;
|
|
238
|
-
|
|
239
|
-
// Skip occlusion check for normal flow elements (vast majority)
|
|
240
|
-
// Only check for positioned elements or high z-index (likely overlays)
|
|
241
|
-
if (position === 'static' && (isNaN(zIndex) || zIndex <= 10)) {
|
|
242
|
-
return false; // Assume not occluded for performance
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// For positioned/high z-index elements, do the expensive check
|
|
246
|
-
const cx = rect.x + rect.width / 2;
|
|
247
|
-
const cy = rect.y + rect.height / 2;
|
|
248
|
-
|
|
249
|
-
if (cx < 0 || cx > window.innerWidth || cy < 0 || cy > window.innerHeight) return false;
|
|
250
|
-
|
|
251
|
-
const topEl = document.elementFromPoint(cx, cy);
|
|
252
|
-
if (!topEl) return false;
|
|
253
|
-
|
|
254
|
-
return !(el === topEl || el.contains(topEl) || topEl.contains(el));
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// --- HELPER: Screenshot Bridge ---
|
|
258
|
-
function captureScreenshot(options) {
|
|
259
|
-
return new Promise(resolve => {
|
|
260
|
-
const requestId = Math.random().toString(36).substring(7);
|
|
261
|
-
const listener = (e) => {
|
|
262
|
-
if (e.data.type === 'SENTIENCE_SCREENSHOT_RESULT' && e.data.requestId === requestId) {
|
|
263
|
-
window.removeEventListener('message', listener);
|
|
264
|
-
resolve(e.data.screenshot);
|
|
265
|
-
}
|
|
266
|
-
};
|
|
267
|
-
window.addEventListener('message', listener);
|
|
268
|
-
window.postMessage({ type: 'SENTIENCE_SCREENSHOT_REQUEST', requestId, options }, '*');
|
|
269
|
-
setTimeout(() => {
|
|
270
|
-
window.removeEventListener('message', listener);
|
|
271
|
-
resolve(null);
|
|
272
|
-
}, 10000); // 10s timeout
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// --- HELPER: Snapshot Processing Bridge (NEW!) ---
|
|
277
|
-
function processSnapshotInBackground(rawData, options) {
|
|
278
|
-
return new Promise((resolve, reject) => {
|
|
279
|
-
const requestId = Math.random().toString(36).substring(7);
|
|
280
|
-
const TIMEOUT_MS = 25000; // 25 seconds (longer than content.js timeout)
|
|
281
|
-
let resolved = false;
|
|
282
|
-
|
|
283
|
-
const timeout = setTimeout(() => {
|
|
284
|
-
if (!resolved) {
|
|
285
|
-
resolved = true;
|
|
286
|
-
window.removeEventListener('message', listener);
|
|
287
|
-
reject(new Error('WASM processing timeout - extension may be unresponsive. Try reloading the extension.'));
|
|
288
|
-
}
|
|
289
|
-
}, TIMEOUT_MS);
|
|
290
|
-
|
|
291
|
-
const listener = (e) => {
|
|
292
|
-
if (e.data.type === 'SENTIENCE_SNAPSHOT_RESULT' && e.data.requestId === requestId) {
|
|
293
|
-
if (resolved) return; // Already handled
|
|
294
|
-
resolved = true;
|
|
295
|
-
clearTimeout(timeout);
|
|
296
|
-
window.removeEventListener('message', listener);
|
|
297
|
-
|
|
298
|
-
if (e.data.error) {
|
|
299
|
-
reject(new Error(e.data.error));
|
|
300
|
-
} else {
|
|
301
|
-
resolve({
|
|
302
|
-
elements: e.data.elements,
|
|
303
|
-
raw_elements: e.data.raw_elements,
|
|
304
|
-
duration: e.data.duration
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
window.addEventListener('message', listener);
|
|
311
|
-
|
|
312
|
-
try {
|
|
313
|
-
window.postMessage({
|
|
314
|
-
type: 'SENTIENCE_SNAPSHOT_REQUEST',
|
|
315
|
-
requestId,
|
|
316
|
-
rawData,
|
|
317
|
-
options
|
|
318
|
-
}, '*');
|
|
319
|
-
} catch (error) {
|
|
320
|
-
if (!resolved) {
|
|
321
|
-
resolved = true;
|
|
322
|
-
clearTimeout(timeout);
|
|
323
|
-
window.removeEventListener('message', listener);
|
|
324
|
-
reject(new Error(`Failed to send snapshot request: ${error.message}`));
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// --- HELPER: Raw HTML Extractor (unchanged) ---
|
|
331
213
|
function getRawHTML(root) {
|
|
332
|
-
const sourceRoot = root || document.body;
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
unwantedTags.forEach(tag => {
|
|
337
|
-
const elements = clone.querySelectorAll(tag);
|
|
338
|
-
elements.forEach(el => {
|
|
339
|
-
if (el.parentNode) el.parentNode.removeChild(el);
|
|
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);
|
|
340
218
|
});
|
|
341
219
|
});
|
|
342
|
-
|
|
343
|
-
// Remove invisible elements
|
|
344
|
-
const invisibleSelectors = [];
|
|
345
|
-
const walker = document.createTreeWalker(sourceRoot, NodeFilter.SHOW_ELEMENT, null, false);
|
|
220
|
+
const invisibleSelectors = [], walker = document.createTreeWalker(sourceRoot, NodeFilter.SHOW_ELEMENT, null, !1);
|
|
346
221
|
let node;
|
|
347
|
-
|
|
222
|
+
for (;node = walker.nextNode(); ) {
|
|
348
223
|
const tag = node.tagName.toLowerCase();
|
|
349
|
-
if (
|
|
350
|
-
|
|
224
|
+
if ("head" === tag || "title" === tag) continue;
|
|
351
225
|
const style = window.getComputedStyle(node);
|
|
352
|
-
if (style.display
|
|
353
|
-
(node.offsetWidth === 0 && node.offsetHeight === 0)) {
|
|
226
|
+
if ("none" === style.display || "hidden" === style.visibility || 0 === node.offsetWidth && 0 === node.offsetHeight) {
|
|
354
227
|
let selector = tag;
|
|
355
|
-
if (node.id) {
|
|
356
|
-
selector = `#${node.id}`;
|
|
357
|
-
} else if (node.className && typeof node.className === 'string') {
|
|
228
|
+
if (node.id) selector = `#${node.id}`; else if (node.className && "string" == typeof node.className) {
|
|
358
229
|
const classes = node.className.trim().split(/\s+/).filter(c => c);
|
|
359
|
-
|
|
360
|
-
selector = `${tag}.${classes.join('.')}`;
|
|
361
|
-
}
|
|
230
|
+
classes.length > 0 && (selector = `${tag}.${classes.join(".")}`);
|
|
362
231
|
}
|
|
363
232
|
invisibleSelectors.push(selector);
|
|
364
233
|
}
|
|
365
234
|
}
|
|
366
|
-
|
|
367
235
|
invisibleSelectors.forEach(selector => {
|
|
368
236
|
try {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (el.parentNode) el.parentNode.removeChild(el);
|
|
237
|
+
clone.querySelectorAll(selector).forEach(el => {
|
|
238
|
+
el.parentNode && el.parentNode.removeChild(el);
|
|
372
239
|
});
|
|
373
|
-
} catch (e) {
|
|
374
|
-
// Invalid selector, skip
|
|
375
|
-
}
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
// Resolve relative URLs
|
|
379
|
-
const links = clone.querySelectorAll('a[href]');
|
|
380
|
-
links.forEach(link => {
|
|
381
|
-
const href = link.getAttribute('href');
|
|
382
|
-
if (href && !href.startsWith('http://') && !href.startsWith('https://') && !href.startsWith('#')) {
|
|
383
|
-
try {
|
|
384
|
-
link.setAttribute('href', new URL(href, document.baseURI).href);
|
|
385
|
-
} catch (e) {}
|
|
386
|
-
}
|
|
240
|
+
} catch (e) {}
|
|
387
241
|
});
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
try {
|
|
394
|
-
img.setAttribute('src', new URL(src, document.baseURI).href);
|
|
395
|
-
} catch (e) {}
|
|
396
|
-
}
|
|
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) {}
|
|
397
247
|
});
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
const rawHTML = getRawHTML(root);
|
|
405
|
-
const tempDiv = document.createElement('div');
|
|
406
|
-
tempDiv.innerHTML = rawHTML;
|
|
407
|
-
|
|
408
|
-
let markdown = '';
|
|
409
|
-
let insideLink = false;
|
|
410
|
-
|
|
411
|
-
function walk(node) {
|
|
412
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
413
|
-
const text = node.textContent.replace(/[\r\n]+/g, ' ').replace(/\s+/g, ' ');
|
|
414
|
-
if (text.trim()) markdown += text;
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
419
|
-
|
|
420
|
-
const tag = node.tagName.toLowerCase();
|
|
421
|
-
|
|
422
|
-
// Prefix
|
|
423
|
-
if (tag === 'h1') markdown += '\n# ';
|
|
424
|
-
if (tag === 'h2') markdown += '\n## ';
|
|
425
|
-
if (tag === 'h3') markdown += '\n### ';
|
|
426
|
-
if (tag === 'li') markdown += '\n- ';
|
|
427
|
-
if (!insideLink && (tag === 'p' || tag === 'div' || tag === 'br')) markdown += '\n';
|
|
428
|
-
if (tag === 'strong' || tag === 'b') markdown += '**';
|
|
429
|
-
if (tag === 'em' || tag === 'i') markdown += '_';
|
|
430
|
-
if (tag === 'a') {
|
|
431
|
-
markdown += '[';
|
|
432
|
-
insideLink = true;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Children
|
|
436
|
-
if (node.shadowRoot) {
|
|
437
|
-
Array.from(node.shadowRoot.childNodes).forEach(walk);
|
|
438
|
-
} else {
|
|
439
|
-
node.childNodes.forEach(walk);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// Suffix
|
|
443
|
-
if (tag === 'a') {
|
|
444
|
-
const href = node.getAttribute('href');
|
|
445
|
-
if (href) markdown += `](${href})`;
|
|
446
|
-
else markdown += ']';
|
|
447
|
-
insideLink = false;
|
|
448
|
-
}
|
|
449
|
-
if (tag === 'strong' || tag === 'b') markdown += '**';
|
|
450
|
-
if (tag === 'em' || tag === 'i') markdown += '_';
|
|
451
|
-
if (!insideLink && (tag === 'h1' || tag === 'h2' || tag === 'h3' || tag === 'p' || tag === 'div')) markdown += '\n';
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
walk(tempDiv);
|
|
455
|
-
return markdown.replace(/\n{3,}/g, '\n\n').trim();
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// --- HELPER: Text Extractor (unchanged) ---
|
|
459
|
-
function convertToText(root) {
|
|
460
|
-
let text = '';
|
|
461
|
-
function walk(node) {
|
|
462
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
463
|
-
text += node.textContent;
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
467
|
-
const tag = node.tagName.toLowerCase();
|
|
468
|
-
if (['nav', 'footer', 'header', 'script', 'style', 'noscript', 'iframe', 'svg'].includes(tag)) return;
|
|
469
|
-
|
|
470
|
-
const style = window.getComputedStyle(node);
|
|
471
|
-
if (style.display === 'none' || style.visibility === 'hidden') return;
|
|
472
|
-
|
|
473
|
-
const isBlock = style.display === 'block' || style.display === 'flex' || node.tagName === 'P' || node.tagName === 'DIV';
|
|
474
|
-
if (isBlock) text += ' ';
|
|
475
|
-
|
|
476
|
-
if (node.shadowRoot) {
|
|
477
|
-
Array.from(node.shadowRoot.childNodes).forEach(walk);
|
|
478
|
-
} else {
|
|
479
|
-
node.childNodes.forEach(walk);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
if (isBlock) text += '\n';
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
walk(root || document.body);
|
|
486
|
-
return text.replace(/\n{3,}/g, '\n\n').trim();
|
|
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;
|
|
487
254
|
}
|
|
488
|
-
|
|
489
|
-
// --- HELPER: Clean null/undefined fields ---
|
|
490
255
|
function cleanElement(obj) {
|
|
491
|
-
if (Array.isArray(obj))
|
|
492
|
-
|
|
493
|
-
}
|
|
494
|
-
if (obj !== null && typeof obj === 'object') {
|
|
256
|
+
if (Array.isArray(obj)) return obj.map(cleanElement);
|
|
257
|
+
if (null !== obj && "object" == typeof obj) {
|
|
495
258
|
const cleaned = {};
|
|
496
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
if (Object.keys(deepClean).length > 0) {
|
|
501
|
-
cleaned[key] = deepClean;
|
|
502
|
-
}
|
|
503
|
-
} else {
|
|
504
|
-
cleaned[key] = value;
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
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;
|
|
508
263
|
return cleaned;
|
|
509
264
|
}
|
|
510
265
|
return obj;
|
|
511
266
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
return {
|
|
519
|
-
tag: el.tagName,
|
|
520
|
-
rect: {
|
|
521
|
-
x: Math.round(rect.x),
|
|
522
|
-
y: Math.round(rect.y),
|
|
523
|
-
width: Math.round(rect.width),
|
|
524
|
-
height: Math.round(rect.height)
|
|
525
|
-
},
|
|
526
|
-
styles: {
|
|
527
|
-
cursor: style.cursor || null,
|
|
528
|
-
backgroundColor: style.backgroundColor || null,
|
|
529
|
-
color: style.color || null,
|
|
530
|
-
fontWeight: style.fontWeight || null,
|
|
531
|
-
fontSize: style.fontSize || null,
|
|
532
|
-
display: style.display || null,
|
|
533
|
-
position: style.position || null,
|
|
534
|
-
zIndex: style.zIndex || null,
|
|
535
|
-
opacity: style.opacity || null,
|
|
536
|
-
visibility: style.visibility || null
|
|
537
|
-
},
|
|
538
|
-
attributes: {
|
|
539
|
-
role: el.getAttribute('role') || null,
|
|
540
|
-
type: el.getAttribute('type') || null,
|
|
541
|
-
ariaLabel: el.getAttribute('aria-label') || null,
|
|
542
|
-
id: el.id || null,
|
|
543
|
-
className: el.className || null
|
|
544
|
-
}
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// --- HELPER: Generate Unique CSS Selector (for Golden Set) ---
|
|
549
|
-
function getUniqueSelector(el) {
|
|
550
|
-
if (!el || !el.tagName) return '';
|
|
551
|
-
|
|
552
|
-
// If element has a unique ID, use it
|
|
553
|
-
if (el.id) {
|
|
554
|
-
return `#${el.id}`;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// Try data attributes or aria-label for uniqueness
|
|
558
|
-
for (const attr of el.attributes) {
|
|
559
|
-
if (attr.name.startsWith('data-') || attr.name === 'aria-label') {
|
|
560
|
-
const value = attr.value ? attr.value.replace(/"/g, '\\"') : '';
|
|
561
|
-
return `${el.tagName.toLowerCase()}[${attr.name}="${value}"]`;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
// Build path with classes and nth-child for uniqueness
|
|
566
|
-
const path = [];
|
|
567
|
-
let current = el;
|
|
568
|
-
|
|
569
|
-
while (current && current !== document.body && current !== document.documentElement) {
|
|
570
|
-
let selector = current.tagName.toLowerCase();
|
|
571
|
-
|
|
572
|
-
// If current element has ID, use it and stop
|
|
573
|
-
if (current.id) {
|
|
574
|
-
selector = `#${current.id}`;
|
|
575
|
-
path.unshift(selector);
|
|
576
|
-
break;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// Add class if available
|
|
580
|
-
if (current.className && typeof current.className === 'string') {
|
|
581
|
-
const classes = current.className.trim().split(/\s+/).filter(c => c);
|
|
582
|
-
if (classes.length > 0) {
|
|
583
|
-
// Use first class for simplicity
|
|
584
|
-
selector += `.${classes[0]}`;
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
// Add nth-of-type if needed for uniqueness
|
|
589
|
-
if (current.parentElement) {
|
|
590
|
-
const siblings = Array.from(current.parentElement.children);
|
|
591
|
-
const sameTagSiblings = siblings.filter(s => s.tagName === current.tagName);
|
|
592
|
-
const index = sameTagSiblings.indexOf(current);
|
|
593
|
-
if (index > 0 || sameTagSiblings.length > 1) {
|
|
594
|
-
selector += `:nth-of-type(${index + 1})`;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
path.unshift(selector);
|
|
599
|
-
current = current.parentElement;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
return path.join(' > ') || el.tagName.toLowerCase();
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// --- HELPER: Wait for DOM Stability (SPA Hydration) ---
|
|
606
|
-
// Waits for the DOM to stabilize before taking a snapshot
|
|
607
|
-
// Useful for React/Vue apps that render empty skeletons before hydration
|
|
608
|
-
async function waitForStability(options = {}) {
|
|
609
|
-
const {
|
|
610
|
-
minNodeCount = 500,
|
|
611
|
-
quietPeriod = 200, // milliseconds
|
|
612
|
-
maxWait = 5000 // maximum wait time
|
|
613
|
-
} = options;
|
|
614
|
-
|
|
615
|
-
const startTime = Date.now();
|
|
616
|
-
|
|
617
|
-
return new Promise((resolve) => {
|
|
618
|
-
// Check if DOM already has enough nodes
|
|
619
|
-
const nodeCount = document.querySelectorAll('*').length;
|
|
620
|
-
if (nodeCount >= minNodeCount) {
|
|
621
|
-
// DOM seems ready, but wait for quiet period to ensure stability
|
|
622
|
-
let lastChange = Date.now();
|
|
623
|
-
const observer = new MutationObserver(() => {
|
|
624
|
-
lastChange = Date.now();
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
observer.observe(document.body, {
|
|
628
|
-
childList: true,
|
|
629
|
-
subtree: true,
|
|
630
|
-
attributes: false
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
const checkStable = () => {
|
|
634
|
-
const timeSinceLastChange = Date.now() - lastChange;
|
|
635
|
-
const totalWait = Date.now() - startTime;
|
|
636
|
-
|
|
637
|
-
if (timeSinceLastChange >= quietPeriod) {
|
|
638
|
-
observer.disconnect();
|
|
639
|
-
resolve();
|
|
640
|
-
} else if (totalWait >= maxWait) {
|
|
641
|
-
observer.disconnect();
|
|
642
|
-
console.warn('[SentienceAPI] DOM stability timeout - proceeding anyway');
|
|
643
|
-
resolve();
|
|
644
|
-
} else {
|
|
645
|
-
setTimeout(checkStable, 50);
|
|
646
|
-
}
|
|
647
|
-
};
|
|
648
|
-
|
|
649
|
-
checkStable();
|
|
650
|
-
} else {
|
|
651
|
-
// DOM doesn't have enough nodes yet, wait for them
|
|
652
|
-
const observer = new MutationObserver(() => {
|
|
653
|
-
const currentCount = document.querySelectorAll('*').length;
|
|
654
|
-
const totalWait = Date.now() - startTime;
|
|
655
|
-
|
|
656
|
-
if (currentCount >= minNodeCount) {
|
|
657
|
-
observer.disconnect();
|
|
658
|
-
// Now wait for quiet period
|
|
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) {
|
|
659
273
|
let lastChange = Date.now();
|
|
660
|
-
const
|
|
274
|
+
const observer = new MutationObserver(() => {
|
|
661
275
|
lastChange = Date.now();
|
|
662
276
|
});
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
attributes: false
|
|
277
|
+
observer.observe(document.body, {
|
|
278
|
+
childList: !0,
|
|
279
|
+
subtree: !0,
|
|
280
|
+
attributes: !1
|
|
668
281
|
});
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
if (timeSinceLastChange >= quietPeriod) {
|
|
675
|
-
quietObserver.disconnect();
|
|
676
|
-
resolve();
|
|
677
|
-
} else if (totalWait >= maxWait) {
|
|
678
|
-
quietObserver.disconnect();
|
|
679
|
-
console.warn('[SentienceAPI] DOM stability timeout - proceeding anyway');
|
|
680
|
-
resolve();
|
|
681
|
-
} else {
|
|
682
|
-
setTimeout(checkQuiet, 50);
|
|
683
|
-
}
|
|
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);
|
|
684
286
|
};
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
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);
|
|
691
317
|
}
|
|
692
318
|
});
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
// Playwright's page.frame() API.
|
|
718
|
-
async function collectIframeSnapshots(options = {}) {
|
|
719
|
-
const iframeData = new Map(); // Map of iframe element -> snapshot data
|
|
720
|
-
|
|
721
|
-
// Find all iframe elements in current document
|
|
722
|
-
const iframes = Array.from(document.querySelectorAll('iframe'));
|
|
723
|
-
|
|
724
|
-
if (iframes.length === 0) {
|
|
725
|
-
return iframeData;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
console.log(`[SentienceAPI] Found ${iframes.length} iframe(s), requesting snapshots...`);
|
|
729
|
-
// Request snapshot from each iframe
|
|
730
|
-
const iframePromises = iframes.map((iframe, idx) => {
|
|
731
|
-
// OPTIMIZATION: Skip common ad domains to save time
|
|
732
|
-
const src = iframe.src || '';
|
|
733
|
-
if (src.includes('doubleclick') || src.includes('googleadservices') || src.includes('ads system')) {
|
|
734
|
-
console.log(`[SentienceAPI] Skipping ad iframe: ${src.substring(0, 30)}...`);
|
|
735
|
-
return Promise.resolve(null);
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
return new Promise((resolve) => {
|
|
739
|
-
const requestId = `iframe-${idx}-${Date.now()}`;
|
|
740
|
-
|
|
741
|
-
// 1. EXTENDED TIMEOUT (Handle slow children)
|
|
742
|
-
const timeout = setTimeout(() => {
|
|
743
|
-
console.warn(`[SentienceAPI] ⚠️ Iframe ${idx} snapshot TIMEOUT (id: ${requestId})`);
|
|
744
|
-
resolve(null);
|
|
745
|
-
}, 5000); // Increased to 5s to handle slow processing
|
|
746
|
-
|
|
747
|
-
// 2. ROBUST LISTENER with debugging
|
|
748
|
-
const listener = (event) => {
|
|
749
|
-
// Debug: Log all SENTIENCE_IFRAME_SNAPSHOT_RESPONSE messages to see what's happening
|
|
750
|
-
if (event.data?.type === 'SENTIENCE_IFRAME_SNAPSHOT_RESPONSE') {
|
|
751
|
-
// Only log if it's not our request (for debugging)
|
|
752
|
-
if (event.data?.requestId !== requestId) {
|
|
753
|
-
// console.log(`[SentienceAPI] Received response for different request: ${event.data.requestId} (expected: ${requestId})`);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
// Check if this is the response we're waiting for
|
|
758
|
-
if (event.data?.type === 'SENTIENCE_IFRAME_SNAPSHOT_RESPONSE' &&
|
|
759
|
-
event.data?.requestId === requestId) {
|
|
760
|
-
|
|
761
|
-
clearTimeout(timeout);
|
|
762
|
-
window.removeEventListener('message', listener);
|
|
763
|
-
|
|
764
|
-
if (event.data.error) {
|
|
765
|
-
console.warn(`[SentienceAPI] Iframe ${idx} returned error:`, event.data.error);
|
|
766
|
-
resolve(null);
|
|
767
|
-
} else {
|
|
768
|
-
const elementCount = event.data.snapshot?.raw_elements?.length || 0;
|
|
769
|
-
console.log(`[SentienceAPI] ✓ Received ${elementCount} elements from Iframe ${idx} (id: ${requestId})`);
|
|
770
|
-
resolve({
|
|
771
|
-
iframe: iframe,
|
|
772
|
-
data: event.data.snapshot,
|
|
773
|
-
error: null
|
|
774
|
-
});
|
|
775
|
-
}
|
|
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
|
+
window.sentience_registry[idx] = el;
|
|
327
|
+
const semanticText = function(el, options = {}) {
|
|
328
|
+
if (!el) return {
|
|
329
|
+
text: "",
|
|
330
|
+
source: null
|
|
331
|
+
};
|
|
332
|
+
const explicitAriaLabel = el.getAttribute ? el.getAttribute("aria-label") : null;
|
|
333
|
+
if (explicitAriaLabel && explicitAriaLabel.trim()) return {
|
|
334
|
+
text: explicitAriaLabel.trim(),
|
|
335
|
+
source: "explicit_aria_label"
|
|
336
|
+
};
|
|
337
|
+
if ("INPUT" === el.tagName) {
|
|
338
|
+
const value = (el.value || el.placeholder || "").trim();
|
|
339
|
+
if (value) return {
|
|
340
|
+
text: value,
|
|
341
|
+
source: "input_value"
|
|
342
|
+
};
|
|
776
343
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
if (iframe.contentWindow) {
|
|
784
|
-
// console.log(`[SentienceAPI] Sending request to Iframe ${idx} (id: ${requestId})`);
|
|
785
|
-
iframe.contentWindow.postMessage({
|
|
786
|
-
type: 'SENTIENCE_IFRAME_SNAPSHOT_REQUEST',
|
|
787
|
-
requestId: requestId,
|
|
788
|
-
options: {
|
|
789
|
-
...options,
|
|
790
|
-
collectIframes: true // Enable recursion for nested iframes
|
|
791
|
-
}
|
|
792
|
-
}, '*'); // Use '*' for cross-origin, but browser will enforce same-origin policy
|
|
793
|
-
} else {
|
|
794
|
-
console.warn(`[SentienceAPI] Iframe ${idx} contentWindow is inaccessible (Cross-Origin?)`);
|
|
795
|
-
clearTimeout(timeout);
|
|
796
|
-
window.removeEventListener('message', listener);
|
|
797
|
-
resolve(null);
|
|
344
|
+
if ("IMG" === el.tagName) {
|
|
345
|
+
const alt = (el.alt || "").trim();
|
|
346
|
+
if (alt) return {
|
|
347
|
+
text: alt,
|
|
348
|
+
source: "img_alt"
|
|
349
|
+
};
|
|
798
350
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
// Send response back to parent
|
|
845
|
-
if (event.source && event.source.postMessage) {
|
|
846
|
-
event.source.postMessage({
|
|
847
|
-
type: 'SENTIENCE_IFRAME_SNAPSHOT_RESPONSE',
|
|
848
|
-
requestId: requestId,
|
|
849
|
-
snapshot: snapshot,
|
|
850
|
-
error: null
|
|
851
|
-
}, '*');
|
|
351
|
+
const innerText = (el.innerText || "").trim();
|
|
352
|
+
if (innerText) return {
|
|
353
|
+
text: innerText.substring(0, 100),
|
|
354
|
+
source: "inner_text"
|
|
355
|
+
};
|
|
356
|
+
const inferred = getInferredLabel(el, {
|
|
357
|
+
enableInference: !1 !== options.enableInference,
|
|
358
|
+
inferenceConfig: options.inferenceConfig
|
|
359
|
+
});
|
|
360
|
+
return inferred || {
|
|
361
|
+
text: "",
|
|
362
|
+
source: null
|
|
363
|
+
};
|
|
364
|
+
}(el, {
|
|
365
|
+
enableInference: !1 !== options.enableInference,
|
|
366
|
+
inferenceConfig: options.inferenceConfig
|
|
367
|
+
}), textVal = semanticText.text || getText(el), inferredRole = function(el, options = {}) {
|
|
368
|
+
const {enableInference: enableInference = !0} = options;
|
|
369
|
+
if (!enableInference) return null;
|
|
370
|
+
if (!function(el) {
|
|
371
|
+
if (!el || !el.tagName) return !1;
|
|
372
|
+
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");
|
|
373
|
+
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")))));
|
|
374
|
+
}(el)) return null;
|
|
375
|
+
const hasAriaLabel = el.getAttribute ? el.getAttribute("aria-label") : null, hasExplicitRole = el.getAttribute ? el.getAttribute("role") : null;
|
|
376
|
+
if (hasAriaLabel || hasExplicitRole) return null;
|
|
377
|
+
const tag = el.tagName.toLowerCase();
|
|
378
|
+
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;
|
|
379
|
+
}(el, {
|
|
380
|
+
enableInference: !1 !== options.enableInference,
|
|
381
|
+
inferenceConfig: options.inferenceConfig
|
|
382
|
+
}), inView = function(rect) {
|
|
383
|
+
return rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0;
|
|
384
|
+
}(rect), style = window.getComputedStyle(el), occluded = !!inView && function(el, rect, style) {
|
|
385
|
+
const zIndex = parseInt(style.zIndex, 10);
|
|
386
|
+
if ("static" === style.position && (isNaN(zIndex) || zIndex <= 10)) return !1;
|
|
387
|
+
const cx = rect.x + rect.width / 2, cy = rect.y + rect.height / 2;
|
|
388
|
+
if (cx < 0 || cx > window.innerWidth || cy < 0 || cy > window.innerHeight) return !1;
|
|
389
|
+
const topEl = document.elementFromPoint(cx, cy);
|
|
390
|
+
return !!topEl && !(el === topEl || el.contains(topEl) || topEl.contains(el));
|
|
391
|
+
}(el, rect, style), effectiveBgColor = function(el) {
|
|
392
|
+
if (!el) return null;
|
|
393
|
+
if ("SVG" === el.tagName) {
|
|
394
|
+
const svgColor = getSVGColor(el);
|
|
395
|
+
if (svgColor) return svgColor;
|
|
852
396
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
397
|
+
let current = el, depth = 0;
|
|
398
|
+
for (;current && depth < 10; ) {
|
|
399
|
+
const style = window.getComputedStyle(current);
|
|
400
|
+
if ("SVG" === current.tagName) {
|
|
401
|
+
const svgColor = getSVGColor(current);
|
|
402
|
+
if (svgColor) return svgColor;
|
|
403
|
+
}
|
|
404
|
+
const bgColor = style.backgroundColor;
|
|
405
|
+
if (bgColor && "transparent" !== bgColor && "rgba(0, 0, 0, 0)" !== bgColor) {
|
|
406
|
+
const rgbaMatch = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
407
|
+
if (!rgbaMatch) return bgColor.startsWith("rgb("), bgColor;
|
|
408
|
+
if ((rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1) >= .9) return `rgb(${rgbaMatch[1]}, ${rgbaMatch[2]}, ${rgbaMatch[3]})`;
|
|
409
|
+
}
|
|
410
|
+
current = current.parentElement, depth++;
|
|
862
411
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
const style = window.getComputedStyle(el);
|
|
902
|
-
|
|
903
|
-
// Only check occlusion for elements likely to be occluded (optimized)
|
|
904
|
-
// This avoids layout thrashing for the vast majority of elements
|
|
905
|
-
const occluded = inView ? isOccluded(el, rect, style) : false;
|
|
906
|
-
|
|
907
|
-
// Get effective background color (traverses DOM to find non-transparent color)
|
|
908
|
-
const effectiveBgColor = getEffectiveBackgroundColor(el);
|
|
909
|
-
|
|
910
|
-
rawData.push({
|
|
911
|
-
id: idx,
|
|
912
|
-
tag: el.tagName.toLowerCase(),
|
|
913
|
-
rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
914
|
-
styles: {
|
|
915
|
-
display: toSafeString(style.display),
|
|
916
|
-
visibility: toSafeString(style.visibility),
|
|
917
|
-
opacity: toSafeString(style.opacity),
|
|
918
|
-
z_index: toSafeString(style.zIndex || "auto"),
|
|
919
|
-
position: toSafeString(style.position),
|
|
920
|
-
bg_color: toSafeString(effectiveBgColor || style.backgroundColor),
|
|
921
|
-
color: toSafeString(style.color),
|
|
922
|
-
cursor: toSafeString(style.cursor),
|
|
923
|
-
font_weight: toSafeString(style.fontWeight),
|
|
924
|
-
font_size: toSafeString(style.fontSize)
|
|
925
|
-
},
|
|
926
|
-
attributes: {
|
|
927
|
-
role: toSafeString(el.getAttribute('role')),
|
|
928
|
-
type_: toSafeString(el.getAttribute('type')),
|
|
929
|
-
aria_label: toSafeString(el.getAttribute('aria-label')),
|
|
930
|
-
href: toSafeString(el.href || el.getAttribute('href') || null),
|
|
931
|
-
class: toSafeString(getClassName(el)),
|
|
932
|
-
// Capture dynamic input state (not just initial attributes)
|
|
933
|
-
value: el.value !== undefined ? toSafeString(el.value) : toSafeString(el.getAttribute('value')),
|
|
934
|
-
checked: el.checked !== undefined ? String(el.checked) : null
|
|
935
|
-
},
|
|
936
|
-
text: toSafeString(textVal),
|
|
937
|
-
in_viewport: inView,
|
|
938
|
-
is_occluded: occluded
|
|
939
|
-
});
|
|
412
|
+
return null;
|
|
413
|
+
}(el);
|
|
414
|
+
rawData.push({
|
|
415
|
+
id: idx,
|
|
416
|
+
tag: el.tagName.toLowerCase(),
|
|
417
|
+
rect: {
|
|
418
|
+
x: rect.x,
|
|
419
|
+
y: rect.y,
|
|
420
|
+
width: rect.width,
|
|
421
|
+
height: rect.height
|
|
422
|
+
},
|
|
423
|
+
styles: {
|
|
424
|
+
display: toSafeString(style.display),
|
|
425
|
+
visibility: toSafeString(style.visibility),
|
|
426
|
+
opacity: toSafeString(style.opacity),
|
|
427
|
+
z_index: toSafeString(style.zIndex || "auto"),
|
|
428
|
+
position: toSafeString(style.position),
|
|
429
|
+
bg_color: toSafeString(effectiveBgColor || style.backgroundColor),
|
|
430
|
+
color: toSafeString(style.color),
|
|
431
|
+
cursor: toSafeString(style.cursor),
|
|
432
|
+
font_weight: toSafeString(style.fontWeight),
|
|
433
|
+
font_size: toSafeString(style.fontSize)
|
|
434
|
+
},
|
|
435
|
+
attributes: {
|
|
436
|
+
role: toSafeString(el.getAttribute("role")),
|
|
437
|
+
type_: toSafeString(el.getAttribute("type")),
|
|
438
|
+
aria_label: "explicit_aria_label" === semanticText?.source ? semanticText.text : toSafeString(el.getAttribute("aria-label")),
|
|
439
|
+
inferred_label: semanticText?.source && ![ "explicit_aria_label", "input_value", "img_alt", "inner_text" ].includes(semanticText.source) ? toSafeString(semanticText.text) : null,
|
|
440
|
+
label_source: semanticText?.source || null,
|
|
441
|
+
inferred_role: inferredRole ? toSafeString(inferredRole) : null,
|
|
442
|
+
href: toSafeString(el.href || el.getAttribute("href") || null),
|
|
443
|
+
class: toSafeString(getClassName(el)),
|
|
444
|
+
value: void 0 !== el.value ? toSafeString(el.value) : toSafeString(el.getAttribute("value")),
|
|
445
|
+
checked: void 0 !== el.checked ? String(el.checked) : null
|
|
446
|
+
},
|
|
447
|
+
text: toSafeString(textVal),
|
|
448
|
+
in_viewport: inView,
|
|
449
|
+
is_occluded: occluded
|
|
940
450
|
});
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
const iframeSrc = iframeEl.src || iframeEl.getAttribute('src') || '';
|
|
971
|
-
let isSameOrigin = false;
|
|
972
|
-
try {
|
|
973
|
-
// Try to access contentWindow to check if same-origin
|
|
974
|
-
isSameOrigin = iframeEl.contentWindow !== null;
|
|
975
|
-
} catch (e) {
|
|
976
|
-
isSameOrigin = false;
|
|
451
|
+
});
|
|
452
|
+
const allRawElements = [ ...rawData ];
|
|
453
|
+
let totalIframeElements = 0;
|
|
454
|
+
if (!1 !== options.collectIframes) try {
|
|
455
|
+
const iframeSnapshots = await async function(options = {}) {
|
|
456
|
+
const iframeData = new Map, iframes = Array.from(document.querySelectorAll("iframe"));
|
|
457
|
+
if (0 === iframes.length) return iframeData;
|
|
458
|
+
const iframePromises = iframes.map((iframe, idx) => {
|
|
459
|
+
const src = iframe.src || "";
|
|
460
|
+
return src.includes("doubleclick") || src.includes("googleadservices") || src.includes("ads system") ? Promise.resolve(null) : new Promise(resolve => {
|
|
461
|
+
const requestId = `iframe-${idx}-${Date.now()}`, timeout = setTimeout(() => {
|
|
462
|
+
resolve(null);
|
|
463
|
+
}, 5e3), listener = event => {
|
|
464
|
+
"SENTIENCE_IFRAME_SNAPSHOT_RESPONSE" === event.data?.type && event.data, "SENTIENCE_IFRAME_SNAPSHOT_RESPONSE" === event.data?.type && event.data?.requestId === requestId && (clearTimeout(timeout),
|
|
465
|
+
window.removeEventListener("message", listener), event.data.error ? resolve(null) : (event.data.snapshot,
|
|
466
|
+
resolve({
|
|
467
|
+
iframe: iframe,
|
|
468
|
+
data: event.data.snapshot,
|
|
469
|
+
error: null
|
|
470
|
+
})));
|
|
471
|
+
};
|
|
472
|
+
window.addEventListener("message", listener);
|
|
473
|
+
try {
|
|
474
|
+
iframe.contentWindow ? iframe.contentWindow.postMessage({
|
|
475
|
+
type: "SENTIENCE_IFRAME_SNAPSHOT_REQUEST",
|
|
476
|
+
requestId: requestId,
|
|
477
|
+
options: {
|
|
478
|
+
...options,
|
|
479
|
+
collectIframes: !0
|
|
977
480
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
481
|
+
}, "*") : (clearTimeout(timeout), window.removeEventListener("message", listener),
|
|
482
|
+
resolve(null));
|
|
483
|
+
} catch (error) {
|
|
484
|
+
clearTimeout(timeout), window.removeEventListener("message", listener), resolve(null);
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
return (await Promise.all(iframePromises)).forEach((result, idx) => {
|
|
489
|
+
result && result.data && !result.error ? iframeData.set(iframes[idx], result.data) : result && result.error;
|
|
490
|
+
}), iframeData;
|
|
491
|
+
}(options);
|
|
492
|
+
iframeSnapshots.size > 0 && iframeSnapshots.forEach((iframeSnapshot, iframeEl) => {
|
|
493
|
+
if (iframeSnapshot && iframeSnapshot.raw_elements) {
|
|
494
|
+
iframeSnapshot.raw_elements.length;
|
|
495
|
+
const iframeRect = iframeEl.getBoundingClientRect(), offset = {
|
|
496
|
+
x: iframeRect.x,
|
|
497
|
+
y: iframeRect.y
|
|
498
|
+
}, iframeSrc = iframeEl.src || iframeEl.getAttribute("src") || "";
|
|
499
|
+
let isSameOrigin = !1;
|
|
500
|
+
try {
|
|
501
|
+
isSameOrigin = null !== iframeEl.contentWindow;
|
|
502
|
+
} catch (e) {
|
|
503
|
+
isSameOrigin = !1;
|
|
504
|
+
}
|
|
505
|
+
const adjustedElements = iframeSnapshot.raw_elements.map(el => {
|
|
506
|
+
const adjusted = {
|
|
507
|
+
...el
|
|
508
|
+
};
|
|
509
|
+
return adjusted.rect && (adjusted.rect = {
|
|
510
|
+
...adjusted.rect,
|
|
511
|
+
x: adjusted.rect.x + offset.x,
|
|
512
|
+
y: adjusted.rect.y + offset.y
|
|
513
|
+
}), adjusted.iframe_context = {
|
|
514
|
+
src: iframeSrc,
|
|
515
|
+
is_same_origin: isSameOrigin
|
|
516
|
+
}, adjusted;
|
|
517
|
+
});
|
|
518
|
+
allRawElements.push(...adjustedElements), totalIframeElements += adjustedElements.length;
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
} catch (error) {}
|
|
522
|
+
const processed = await function(rawData, options) {
|
|
523
|
+
return new Promise((resolve, reject) => {
|
|
524
|
+
const requestId = Math.random().toString(36).substring(7);
|
|
525
|
+
let resolved = !1;
|
|
526
|
+
const timeout = setTimeout(() => {
|
|
527
|
+
resolved || (resolved = !0, window.removeEventListener("message", listener), reject(new Error("WASM processing timeout - extension may be unresponsive. Try reloading the extension.")));
|
|
528
|
+
}, 25e3), listener = e => {
|
|
529
|
+
if ("SENTIENCE_SNAPSHOT_RESULT" === e.data.type && e.data.requestId === requestId) {
|
|
530
|
+
if (resolved) return;
|
|
531
|
+
resolved = !0, clearTimeout(timeout), window.removeEventListener("message", listener),
|
|
532
|
+
e.data.error ? reject(new Error(e.data.error)) : resolve({
|
|
533
|
+
elements: e.data.elements,
|
|
534
|
+
raw_elements: e.data.raw_elements,
|
|
535
|
+
duration: e.data.duration
|
|
1005
536
|
});
|
|
1006
|
-
|
|
1007
|
-
// console.log(`[SentienceAPI] Merged ${iframeSnapshots.size} iframe(s). Total elements: ${allRawElements.length} (${rawData.length} main + ${totalIframeElements} iframe)`);
|
|
1008
537
|
}
|
|
538
|
+
};
|
|
539
|
+
window.addEventListener("message", listener);
|
|
540
|
+
try {
|
|
541
|
+
window.postMessage({
|
|
542
|
+
type: "SENTIENCE_SNAPSHOT_REQUEST",
|
|
543
|
+
requestId: requestId,
|
|
544
|
+
rawData: rawData,
|
|
545
|
+
options: options
|
|
546
|
+
}, "*");
|
|
1009
547
|
} catch (error) {
|
|
1010
|
-
|
|
548
|
+
resolved || (resolved = !0, clearTimeout(timeout), window.removeEventListener("message", listener),
|
|
549
|
+
reject(new Error(`Failed to send snapshot request: ${error.message}`)));
|
|
1011
550
|
}
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
// FIXED: Removed undefined 'totalIframeRawElements'
|
|
1035
|
-
// FIXED: Logic updated for "Flatten Early" architecture.
|
|
1036
|
-
// processed.elements ALREADY contains the merged iframe elements,
|
|
1037
|
-
// so we simply use .length. No addition needed.
|
|
1038
|
-
|
|
1039
|
-
const totalCount = cleanedElements.length;
|
|
1040
|
-
const totalRaw = cleanedRawElements.length;
|
|
1041
|
-
const iframeCount = totalIframeElements || 0;
|
|
1042
|
-
|
|
1043
|
-
console.log(`[SentienceAPI] ✓ Complete: ${totalCount} Smart Elements, ${totalRaw} Raw Elements (includes ${iframeCount} from iframes) (WASM took ${processed.duration?.toFixed(1)}ms)`);
|
|
1044
|
-
|
|
1045
|
-
return {
|
|
1046
|
-
status: "success",
|
|
1047
|
-
url: window.location.href,
|
|
1048
|
-
viewport: {
|
|
1049
|
-
width: window.innerWidth,
|
|
1050
|
-
height: window.innerHeight
|
|
1051
|
-
},
|
|
1052
|
-
elements: cleanedElements,
|
|
1053
|
-
raw_elements: cleanedRawElements,
|
|
1054
|
-
screenshot: screenshot
|
|
1055
|
-
};
|
|
1056
|
-
} catch (error) {
|
|
1057
|
-
console.error('[SentienceAPI] snapshot() failed:', error);
|
|
1058
|
-
console.error('[SentienceAPI] Error stack:', error.stack);
|
|
1059
|
-
return {
|
|
1060
|
-
status: "error",
|
|
1061
|
-
error: error.message || 'Unknown error',
|
|
1062
|
-
stack: error.stack
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
},
|
|
1066
|
-
|
|
1067
|
-
// 2. Read Content (unchanged)
|
|
1068
|
-
read: (options = {}) => {
|
|
1069
|
-
const format = options.format || 'raw';
|
|
1070
|
-
let content;
|
|
1071
|
-
|
|
1072
|
-
if (format === 'raw') {
|
|
1073
|
-
content = getRawHTML(document.body);
|
|
1074
|
-
} else if (format === 'markdown') {
|
|
1075
|
-
content = convertToMarkdown(document.body);
|
|
1076
|
-
} else {
|
|
1077
|
-
content = convertToText(document.body);
|
|
1078
|
-
}
|
|
1079
|
-
|
|
551
|
+
});
|
|
552
|
+
}(allRawElements, options);
|
|
553
|
+
if (!processed || !processed.elements) throw new Error("WASM processing returned invalid result");
|
|
554
|
+
let screenshot = null;
|
|
555
|
+
options.screenshot && (screenshot = await function(options) {
|
|
556
|
+
return new Promise(resolve => {
|
|
557
|
+
const requestId = Math.random().toString(36).substring(7), listener = e => {
|
|
558
|
+
"SENTIENCE_SCREENSHOT_RESULT" === e.data.type && e.data.requestId === requestId && (window.removeEventListener("message", listener),
|
|
559
|
+
resolve(e.data.screenshot));
|
|
560
|
+
};
|
|
561
|
+
window.addEventListener("message", listener), window.postMessage({
|
|
562
|
+
type: "SENTIENCE_SCREENSHOT_REQUEST",
|
|
563
|
+
requestId: requestId,
|
|
564
|
+
options: options
|
|
565
|
+
}, "*"), setTimeout(() => {
|
|
566
|
+
window.removeEventListener("message", listener), resolve(null);
|
|
567
|
+
}, 1e4);
|
|
568
|
+
});
|
|
569
|
+
}(options.screenshot));
|
|
570
|
+
const cleanedElements = cleanElement(processed.elements), cleanedRawElements = cleanElement(processed.raw_elements);
|
|
571
|
+
cleanedElements.length, cleanedRawElements.length;
|
|
1080
572
|
return {
|
|
1081
573
|
status: "success",
|
|
1082
574
|
url: window.location.href,
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
575
|
+
viewport: {
|
|
576
|
+
width: window.innerWidth,
|
|
577
|
+
height: window.innerHeight
|
|
578
|
+
},
|
|
579
|
+
elements: cleanedElements,
|
|
580
|
+
raw_elements: cleanedRawElements,
|
|
581
|
+
screenshot: screenshot
|
|
1086
582
|
};
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
const results = [];
|
|
1107
|
-
const searchText = caseSensitive ? text : text.toLowerCase();
|
|
1108
|
-
|
|
1109
|
-
// Helper function to find text in a single text node
|
|
1110
|
-
function findInTextNode(textNode) {
|
|
1111
|
-
const nodeText = textNode.nodeValue;
|
|
1112
|
-
const searchableText = caseSensitive ? nodeText : nodeText.toLowerCase();
|
|
1113
|
-
|
|
1114
|
-
let startIndex = 0;
|
|
1115
|
-
while (startIndex < nodeText.length && results.length < maxResults) {
|
|
1116
|
-
const foundIndex = searchableText.indexOf(searchText, startIndex);
|
|
1117
|
-
|
|
1118
|
-
if (foundIndex === -1) break;
|
|
1119
|
-
|
|
1120
|
-
// Check whole word matching if required
|
|
1121
|
-
if (wholeWord) {
|
|
1122
|
-
const before = foundIndex > 0 ? nodeText[foundIndex - 1] : ' ';
|
|
1123
|
-
const after = foundIndex + text.length < nodeText.length
|
|
1124
|
-
? nodeText[foundIndex + text.length]
|
|
1125
|
-
: ' ';
|
|
1126
|
-
|
|
1127
|
-
// Check if surrounded by word boundaries
|
|
1128
|
-
if (!/\s/.test(before) || !/\s/.test(after)) {
|
|
1129
|
-
startIndex = foundIndex + 1;
|
|
1130
|
-
continue;
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
try {
|
|
1135
|
-
// Create range for this occurrence
|
|
1136
|
-
const range = document.createRange();
|
|
1137
|
-
range.setStart(textNode, foundIndex);
|
|
1138
|
-
range.setEnd(textNode, foundIndex + text.length);
|
|
1139
|
-
|
|
1140
|
-
const rect = range.getBoundingClientRect();
|
|
1141
|
-
|
|
1142
|
-
// Only include visible rectangles
|
|
1143
|
-
if (rect.width > 0 && rect.height > 0) {
|
|
1144
|
-
results.push({
|
|
1145
|
-
text: nodeText.substring(foundIndex, foundIndex + text.length),
|
|
1146
|
-
rect: {
|
|
1147
|
-
x: rect.left + window.scrollX,
|
|
1148
|
-
y: rect.top + window.scrollY,
|
|
1149
|
-
width: rect.width,
|
|
1150
|
-
height: rect.height,
|
|
1151
|
-
left: rect.left + window.scrollX,
|
|
1152
|
-
top: rect.top + window.scrollY,
|
|
1153
|
-
right: rect.right + window.scrollX,
|
|
1154
|
-
bottom: rect.bottom + window.scrollY
|
|
1155
|
-
},
|
|
1156
|
-
viewport_rect: {
|
|
1157
|
-
x: rect.left,
|
|
1158
|
-
y: rect.top,
|
|
1159
|
-
width: rect.width,
|
|
1160
|
-
height: rect.height
|
|
1161
|
-
},
|
|
1162
|
-
context: {
|
|
1163
|
-
before: nodeText.substring(Math.max(0, foundIndex - 20), foundIndex),
|
|
1164
|
-
after: nodeText.substring(foundIndex + text.length, Math.min(nodeText.length, foundIndex + text.length + 20))
|
|
1165
|
-
},
|
|
1166
|
-
in_viewport: (
|
|
1167
|
-
rect.top >= 0 &&
|
|
1168
|
-
rect.left >= 0 &&
|
|
1169
|
-
rect.bottom <= window.innerHeight &&
|
|
1170
|
-
rect.right <= window.innerWidth
|
|
1171
|
-
)
|
|
1172
|
-
});
|
|
1173
|
-
}
|
|
1174
|
-
} catch (e) {
|
|
1175
|
-
console.warn('[SentienceAPI] Failed to get rect for text:', e);
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
startIndex = foundIndex + 1;
|
|
583
|
+
} catch (error) {
|
|
584
|
+
return {
|
|
585
|
+
status: "error",
|
|
586
|
+
error: error.message || "Unknown error",
|
|
587
|
+
stack: error.stack
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
function read(options = {}) {
|
|
592
|
+
const format = options.format || "raw";
|
|
593
|
+
let content;
|
|
594
|
+
return content = "raw" === format ? getRawHTML(document.body) : "markdown" === format ? function(root) {
|
|
595
|
+
const rawHTML = getRawHTML(root), tempDiv = document.createElement("div");
|
|
596
|
+
tempDiv.innerHTML = rawHTML;
|
|
597
|
+
let markdown = "", insideLink = !1;
|
|
598
|
+
return function walk(node) {
|
|
599
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
600
|
+
const text = node.textContent.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ");
|
|
601
|
+
return void (text.trim() && (markdown += text));
|
|
1179
602
|
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
{
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
const parent = node.parentElement;
|
|
1190
|
-
if (!parent) return NodeFilter.FILTER_REJECT;
|
|
1191
|
-
|
|
1192
|
-
const tagName = parent.tagName.toLowerCase();
|
|
1193
|
-
if (tagName === 'script' || tagName === 'style' || tagName === 'noscript') {
|
|
1194
|
-
return NodeFilter.FILTER_REJECT;
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
// Skip whitespace-only nodes
|
|
1198
|
-
if (!node.nodeValue || node.nodeValue.trim().length === 0) {
|
|
1199
|
-
return NodeFilter.FILTER_REJECT;
|
|
1200
|
-
}
|
|
1201
|
-
|
|
1202
|
-
// Check if element is visible
|
|
1203
|
-
const computedStyle = window.getComputedStyle(parent);
|
|
1204
|
-
if (computedStyle.display === 'none' ||
|
|
1205
|
-
computedStyle.visibility === 'hidden' ||
|
|
1206
|
-
computedStyle.opacity === '0') {
|
|
1207
|
-
return NodeFilter.FILTER_REJECT;
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
return NodeFilter.FILTER_ACCEPT;
|
|
1211
|
-
}
|
|
603
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
604
|
+
const tag = node.tagName.toLowerCase();
|
|
605
|
+
if ("h1" === tag && (markdown += "\n# "), "h2" === tag && (markdown += "\n## "),
|
|
606
|
+
"h3" === tag && (markdown += "\n### "), "li" === tag && (markdown += "\n- "), insideLink || "p" !== tag && "div" !== tag && "br" !== tag || (markdown += "\n"),
|
|
607
|
+
"strong" !== tag && "b" !== tag || (markdown += "**"), "em" !== tag && "i" !== tag || (markdown += "_"),
|
|
608
|
+
"a" === tag && (markdown += "[", insideLink = !0), node.shadowRoot ? Array.from(node.shadowRoot.childNodes).forEach(walk) : node.childNodes.forEach(walk),
|
|
609
|
+
"a" === tag) {
|
|
610
|
+
const href = node.getAttribute("href");
|
|
611
|
+
markdown += href ? `](${href})` : "]", insideLink = !1;
|
|
1212
612
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
613
|
+
"strong" !== tag && "b" !== tag || (markdown += "**"), "em" !== tag && "i" !== tag || (markdown += "_"),
|
|
614
|
+
insideLink || "h1" !== tag && "h2" !== tag && "h3" !== tag && "p" !== tag && "div" !== tag || (markdown += "\n");
|
|
615
|
+
}(tempDiv), markdown.replace(/\n{3,}/g, "\n\n").trim();
|
|
616
|
+
}(document.body) : function(root) {
|
|
617
|
+
let text = "";
|
|
618
|
+
return function walk(node) {
|
|
619
|
+
if (node.nodeType !== Node.TEXT_NODE) {
|
|
620
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
621
|
+
const tag = node.tagName.toLowerCase();
|
|
622
|
+
if ([ "nav", "footer", "header", "script", "style", "noscript", "iframe", "svg" ].includes(tag)) return;
|
|
623
|
+
const style = window.getComputedStyle(node);
|
|
624
|
+
if ("none" === style.display || "hidden" === style.visibility) return;
|
|
625
|
+
const isBlock = "block" === style.display || "flex" === style.display || "P" === node.tagName || "DIV" === node.tagName;
|
|
626
|
+
isBlock && (text += " "), node.shadowRoot ? Array.from(node.shadowRoot.childNodes).forEach(walk) : node.childNodes.forEach(walk),
|
|
627
|
+
isBlock && (text += "\n");
|
|
628
|
+
}
|
|
629
|
+
} else text += node.textContent;
|
|
630
|
+
}(root || document.body), text.replace(/\n{3,}/g, "\n\n").trim();
|
|
631
|
+
}(document.body), {
|
|
632
|
+
status: "success",
|
|
633
|
+
url: window.location.href,
|
|
634
|
+
format: format,
|
|
635
|
+
content: content,
|
|
636
|
+
length: content.length
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
function findTextRect(options = {}) {
|
|
640
|
+
const {text: text, containerElement: containerElement = document.body, caseSensitive: caseSensitive = !1, wholeWord: wholeWord = !1, maxResults: maxResults = 10} = options;
|
|
641
|
+
if (!text || 0 === text.trim().length) return {
|
|
642
|
+
status: "error",
|
|
643
|
+
error: "Text parameter is required"
|
|
644
|
+
};
|
|
645
|
+
const results = [], searchText = caseSensitive ? text : text.toLowerCase();
|
|
646
|
+
function findInTextNode(textNode) {
|
|
647
|
+
const nodeText = textNode.nodeValue, searchableText = caseSensitive ? nodeText : nodeText.toLowerCase();
|
|
648
|
+
let startIndex = 0;
|
|
649
|
+
for (;startIndex < nodeText.length && results.length < maxResults; ) {
|
|
650
|
+
const foundIndex = searchableText.indexOf(searchText, startIndex);
|
|
651
|
+
if (-1 === foundIndex) break;
|
|
652
|
+
if (wholeWord) {
|
|
653
|
+
const before = foundIndex > 0 ? nodeText[foundIndex - 1] : " ", after = foundIndex + text.length < nodeText.length ? nodeText[foundIndex + text.length] : " ";
|
|
654
|
+
if (!/\s/.test(before) || !/\s/.test(after)) {
|
|
655
|
+
startIndex = foundIndex + 1;
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
1233
658
|
}
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
return () => {}; // Return no-op cleanup function
|
|
659
|
+
try {
|
|
660
|
+
const range = document.createRange();
|
|
661
|
+
range.setStart(textNode, foundIndex), range.setEnd(textNode, foundIndex + text.length);
|
|
662
|
+
const rect = range.getBoundingClientRect();
|
|
663
|
+
rect.width > 0 && rect.height > 0 && results.push({
|
|
664
|
+
text: nodeText.substring(foundIndex, foundIndex + text.length),
|
|
665
|
+
rect: {
|
|
666
|
+
x: rect.left + window.scrollX,
|
|
667
|
+
y: rect.top + window.scrollY,
|
|
668
|
+
width: rect.width,
|
|
669
|
+
height: rect.height,
|
|
670
|
+
left: rect.left + window.scrollX,
|
|
671
|
+
top: rect.top + window.scrollY,
|
|
672
|
+
right: rect.right + window.scrollX,
|
|
673
|
+
bottom: rect.bottom + window.scrollY
|
|
674
|
+
},
|
|
675
|
+
viewport_rect: {
|
|
676
|
+
x: rect.left,
|
|
677
|
+
y: rect.top,
|
|
678
|
+
width: rect.width,
|
|
679
|
+
height: rect.height
|
|
680
|
+
},
|
|
681
|
+
context: {
|
|
682
|
+
before: nodeText.substring(Math.max(0, foundIndex - 20), foundIndex),
|
|
683
|
+
after: nodeText.substring(foundIndex + text.length, Math.min(nodeText.length, foundIndex + text.length + 20))
|
|
684
|
+
},
|
|
685
|
+
in_viewport: rect.top >= 0 && rect.left >= 0 && rect.bottom <= window.innerHeight && rect.right <= window.innerWidth
|
|
686
|
+
});
|
|
687
|
+
} catch (e) {}
|
|
688
|
+
startIndex = foundIndex + 1;
|
|
1265
689
|
}
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
if (
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
highlightBox = document.createElement('div');
|
|
1277
|
-
highlightBox.id = 'sentience-highlight-box';
|
|
1278
|
-
highlightBox.style.cssText = `
|
|
1279
|
-
position: fixed;
|
|
1280
|
-
pointer-events: none;
|
|
1281
|
-
z-index: 2147483647;
|
|
1282
|
-
border: 2px solid ${highlightColor};
|
|
1283
|
-
background: rgba(255, 0, 0, 0.1);
|
|
1284
|
-
display: none;
|
|
1285
|
-
transition: all 0.1s ease;
|
|
1286
|
-
box-sizing: border-box;
|
|
1287
|
-
`;
|
|
1288
|
-
document.body.appendChild(highlightBox);
|
|
690
|
+
}
|
|
691
|
+
const walker = document.createTreeWalker(containerElement, NodeFilter.SHOW_TEXT, {
|
|
692
|
+
acceptNode(node) {
|
|
693
|
+
const parent = node.parentElement;
|
|
694
|
+
if (!parent) return NodeFilter.FILTER_REJECT;
|
|
695
|
+
const tagName = parent.tagName.toLowerCase();
|
|
696
|
+
if ("script" === tagName || "style" === tagName || "noscript" === tagName) return NodeFilter.FILTER_REJECT;
|
|
697
|
+
if (!node.nodeValue || 0 === node.nodeValue.trim().length) return NodeFilter.FILTER_REJECT;
|
|
698
|
+
const computedStyle = window.getComputedStyle(parent);
|
|
699
|
+
return "none" === computedStyle.display || "hidden" === computedStyle.visibility || "0" === computedStyle.opacity ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
|
|
1289
700
|
}
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
`;
|
|
1306
|
-
document.body.appendChild(recordingIndicator);
|
|
701
|
+
});
|
|
702
|
+
let currentNode;
|
|
703
|
+
for (;(currentNode = walker.nextNode()) && results.length < maxResults; ) findInTextNode(currentNode);
|
|
704
|
+
return {
|
|
705
|
+
status: "success",
|
|
706
|
+
query: text,
|
|
707
|
+
case_sensitive: caseSensitive,
|
|
708
|
+
whole_word: wholeWord,
|
|
709
|
+
matches: results.length,
|
|
710
|
+
results: results,
|
|
711
|
+
viewport: {
|
|
712
|
+
width: window.innerWidth,
|
|
713
|
+
height: window.innerHeight,
|
|
714
|
+
scroll_x: window.scrollX,
|
|
715
|
+
scroll_y: window.scrollY
|
|
1307
716
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
const
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
function click(id) {
|
|
720
|
+
const el = window.sentience_registry[id];
|
|
721
|
+
return !!el && (el.click(), el.focus(), !0);
|
|
722
|
+
}
|
|
723
|
+
function startRecording(options = {}) {
|
|
724
|
+
const {highlightColor: highlightColor = "#ff0000", successColor: successColor = "#00ff00", autoDisableTimeout: autoDisableTimeout = 18e5, keyboardShortcut: keyboardShortcut = "Ctrl+Shift+I"} = options;
|
|
725
|
+
if (!window.sentience_registry || 0 === window.sentience_registry.length) return alert("Registry empty. Run `await window.sentience.snapshot()` first!"),
|
|
726
|
+
() => {};
|
|
727
|
+
window.sentience_registry_map = new Map, window.sentience_registry.forEach((el, idx) => {
|
|
728
|
+
el && window.sentience_registry_map.set(el, idx);
|
|
729
|
+
});
|
|
730
|
+
let highlightBox = document.getElementById("sentience-highlight-box");
|
|
731
|
+
highlightBox || (highlightBox = document.createElement("div"), highlightBox.id = "sentience-highlight-box",
|
|
732
|
+
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 `,
|
|
733
|
+
document.body.appendChild(highlightBox));
|
|
734
|
+
let recordingIndicator = document.getElementById("sentience-recording-indicator");
|
|
735
|
+
recordingIndicator || (recordingIndicator = document.createElement("div"), recordingIndicator.id = "sentience-recording-indicator",
|
|
736
|
+
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 `,
|
|
737
|
+
document.body.appendChild(recordingIndicator)), recordingIndicator.style.display = "block";
|
|
738
|
+
const mouseOverHandler = e => {
|
|
739
|
+
const el = e.target;
|
|
740
|
+
if (!el || el === highlightBox || el === recordingIndicator) return;
|
|
741
|
+
const rect = el.getBoundingClientRect();
|
|
742
|
+
highlightBox.style.display = "block", highlightBox.style.top = rect.top + window.scrollY + "px",
|
|
743
|
+
highlightBox.style.left = rect.left + window.scrollX + "px", highlightBox.style.width = rect.width + "px",
|
|
744
|
+
highlightBox.style.height = rect.height + "px";
|
|
745
|
+
}, clickHandler = e => {
|
|
746
|
+
e.preventDefault(), e.stopPropagation();
|
|
747
|
+
const el = e.target;
|
|
748
|
+
if (!el || el === highlightBox || el === recordingIndicator) return;
|
|
749
|
+
const sentienceId = window.sentience_registry_map.get(el);
|
|
750
|
+
if (void 0 === sentienceId) return void alert("Element not in registry. Run `await window.sentience.snapshot()` first!");
|
|
751
|
+
const rawData = function(el) {
|
|
752
|
+
const style = window.getComputedStyle(el), rect = el.getBoundingClientRect();
|
|
753
|
+
return {
|
|
754
|
+
tag: el.tagName,
|
|
755
|
+
rect: {
|
|
756
|
+
x: Math.round(rect.x),
|
|
757
|
+
y: Math.round(rect.y),
|
|
758
|
+
width: Math.round(rect.width),
|
|
759
|
+
height: Math.round(rect.height)
|
|
760
|
+
},
|
|
761
|
+
styles: {
|
|
762
|
+
cursor: style.cursor || null,
|
|
763
|
+
backgroundColor: style.backgroundColor || null,
|
|
764
|
+
color: style.color || null,
|
|
765
|
+
fontWeight: style.fontWeight || null,
|
|
766
|
+
fontSize: style.fontSize || null,
|
|
767
|
+
display: style.display || null,
|
|
768
|
+
position: style.position || null,
|
|
769
|
+
zIndex: style.zIndex || null,
|
|
770
|
+
opacity: style.opacity || null,
|
|
771
|
+
visibility: style.visibility || null
|
|
1355
772
|
},
|
|
1356
|
-
|
|
773
|
+
attributes: {
|
|
774
|
+
role: el.getAttribute("role") || null,
|
|
775
|
+
type: el.getAttribute("type") || null,
|
|
776
|
+
ariaLabel: el.getAttribute("aria-label") || null,
|
|
777
|
+
id: el.id || null,
|
|
778
|
+
className: el.className || null
|
|
779
|
+
}
|
|
1357
780
|
};
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
// Flash green to indicate success
|
|
1365
|
-
highlightBox.style.border = `2px solid ${successColor}`;
|
|
1366
|
-
highlightBox.style.background = 'rgba(0, 255, 0, 0.2)';
|
|
1367
|
-
setTimeout(() => {
|
|
1368
|
-
highlightBox.style.border = `2px solid ${highlightColor}`;
|
|
1369
|
-
highlightBox.style.background = 'rgba(255, 0, 0, 0.1)';
|
|
1370
|
-
}, 500);
|
|
1371
|
-
}).catch(err => {
|
|
1372
|
-
console.error("❌ Failed to copy to clipboard:", err);
|
|
1373
|
-
alert("Failed to copy to clipboard. Check console for JSON.");
|
|
1374
|
-
});
|
|
1375
|
-
};
|
|
1376
|
-
|
|
1377
|
-
// Auto-disable timeout
|
|
1378
|
-
let timeoutId = null;
|
|
1379
|
-
|
|
1380
|
-
// Cleanup function to stop recording (defined before use)
|
|
1381
|
-
const stopRecording = () => {
|
|
1382
|
-
document.removeEventListener('mouseover', mouseOverHandler, true);
|
|
1383
|
-
document.removeEventListener('click', clickHandler, true);
|
|
1384
|
-
document.removeEventListener('keydown', keyboardHandler, true);
|
|
1385
|
-
|
|
1386
|
-
if (timeoutId) {
|
|
1387
|
-
clearTimeout(timeoutId);
|
|
1388
|
-
timeoutId = null;
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
if (highlightBox) {
|
|
1392
|
-
highlightBox.style.display = 'none';
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
if (recordingIndicator) {
|
|
1396
|
-
recordingIndicator.style.display = 'none';
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
// Clean up registry map (optional, but good practice)
|
|
1400
|
-
if (window.sentience_registry_map) {
|
|
1401
|
-
window.sentience_registry_map.clear();
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
// Remove global reference
|
|
1405
|
-
if (window.sentience_stopRecording === stopRecording) {
|
|
1406
|
-
delete window.sentience_stopRecording;
|
|
781
|
+
}(el), selector = function(el) {
|
|
782
|
+
if (!el || !el.tagName) return "";
|
|
783
|
+
if (el.id) return `#${el.id}`;
|
|
784
|
+
for (const attr of el.attributes) if (attr.name.startsWith("data-") || "aria-label" === attr.name) {
|
|
785
|
+
const value = attr.value ? attr.value.replace(/"/g, '\\"') : "";
|
|
786
|
+
return `${el.tagName.toLowerCase()}[${attr.name}="${value}"]`;
|
|
1407
787
|
}
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
788
|
+
const path = [];
|
|
789
|
+
let current = el;
|
|
790
|
+
for (;current && current !== document.body && current !== document.documentElement; ) {
|
|
791
|
+
let selector = current.tagName.toLowerCase();
|
|
792
|
+
if (current.id) {
|
|
793
|
+
selector = `#${current.id}`, path.unshift(selector);
|
|
794
|
+
break;
|
|
795
|
+
}
|
|
796
|
+
if (current.className && "string" == typeof current.className) {
|
|
797
|
+
const classes = current.className.trim().split(/\s+/).filter(c => c);
|
|
798
|
+
classes.length > 0 && (selector += `.${classes[0]}`);
|
|
799
|
+
}
|
|
800
|
+
if (current.parentElement) {
|
|
801
|
+
const sameTagSiblings = Array.from(current.parentElement.children).filter(s => s.tagName === current.tagName), index = sameTagSiblings.indexOf(current);
|
|
802
|
+
(index > 0 || sameTagSiblings.length > 1) && (selector += `:nth-of-type(${index + 1})`);
|
|
803
|
+
}
|
|
804
|
+
path.unshift(selector), current = current.parentElement;
|
|
1418
805
|
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
806
|
+
return path.join(" > ") || el.tagName.toLowerCase();
|
|
807
|
+
}(el), role = el.getAttribute("role") || el.tagName.toLowerCase(), text = getText(el), snippet = {
|
|
808
|
+
task: `Interact with ${text.substring(0, 20)}${text.length > 20 ? "..." : ""}`,
|
|
809
|
+
url: window.location.href,
|
|
810
|
+
timestamp: (new Date).toISOString(),
|
|
811
|
+
target_criteria: {
|
|
812
|
+
id: sentienceId,
|
|
813
|
+
selector: selector,
|
|
814
|
+
role: role,
|
|
815
|
+
text: text.substring(0, 50)
|
|
816
|
+
},
|
|
817
|
+
debug_snapshot: rawData
|
|
818
|
+
}, jsonString = JSON.stringify(snippet, null, 2);
|
|
819
|
+
navigator.clipboard.writeText(jsonString).then(() => {
|
|
820
|
+
highlightBox.style.border = `2px solid ${successColor}`, highlightBox.style.background = "rgba(0, 255, 0, 0.2)",
|
|
821
|
+
setTimeout(() => {
|
|
822
|
+
highlightBox.style.border = `2px solid ${highlightColor}`, highlightBox.style.background = "rgba(255, 0, 0, 0.1)";
|
|
823
|
+
}, 500);
|
|
824
|
+
}).catch(err => {
|
|
825
|
+
alert("Failed to copy to clipboard. Check console for JSON.");
|
|
826
|
+
});
|
|
827
|
+
};
|
|
828
|
+
let timeoutId = null;
|
|
829
|
+
const stopRecording = () => {
|
|
830
|
+
document.removeEventListener("mouseover", mouseOverHandler, !0), document.removeEventListener("click", clickHandler, !0),
|
|
831
|
+
document.removeEventListener("keydown", keyboardHandler, !0), timeoutId && (clearTimeout(timeoutId),
|
|
832
|
+
timeoutId = null), highlightBox && (highlightBox.style.display = "none"), recordingIndicator && (recordingIndicator.style.display = "none"),
|
|
833
|
+
window.sentience_registry_map && window.sentience_registry_map.clear(), window.sentience_stopRecording === stopRecording && delete window.sentience_stopRecording;
|
|
834
|
+
}, keyboardHandler = e => {
|
|
835
|
+
(e.ctrlKey || e.metaKey) && e.shiftKey && "I" === e.key && (e.preventDefault(),
|
|
836
|
+
stopRecording());
|
|
837
|
+
};
|
|
838
|
+
return document.addEventListener("mouseover", mouseOverHandler, !0), document.addEventListener("click", clickHandler, !0),
|
|
839
|
+
document.addEventListener("keydown", keyboardHandler, !0), autoDisableTimeout > 0 && (timeoutId = setTimeout(() => {
|
|
840
|
+
stopRecording();
|
|
841
|
+
}, autoDisableTimeout)), window.sentience_stopRecording = stopRecording, stopRecording;
|
|
842
|
+
}
|
|
843
|
+
function showOverlay(elements, targetElementId = null) {
|
|
844
|
+
elements && Array.isArray(elements) && window.postMessage({
|
|
845
|
+
type: "SENTIENCE_SHOW_OVERLAY",
|
|
1454
846
|
elements: elements,
|
|
1455
847
|
targetElementId: targetElementId,
|
|
1456
848
|
timestamp: Date.now()
|
|
1457
|
-
},
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
};
|
|
1461
|
-
|
|
1462
|
-
/**
|
|
1463
|
-
* Clear overlay manually
|
|
1464
|
-
*/
|
|
1465
|
-
window.sentience.clearOverlay = function() {
|
|
849
|
+
}, "*");
|
|
850
|
+
}
|
|
851
|
+
function clearOverlay() {
|
|
1466
852
|
window.postMessage({
|
|
1467
|
-
type:
|
|
1468
|
-
},
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
853
|
+
type: "SENTIENCE_CLEAR_OVERLAY"
|
|
854
|
+
}, "*");
|
|
855
|
+
}
|
|
856
|
+
(async () => {
|
|
857
|
+
const getExtensionId = () => document.documentElement.dataset.sentienceExtensionId;
|
|
858
|
+
let extId = getExtensionId();
|
|
859
|
+
extId || await new Promise(resolve => {
|
|
860
|
+
const check = setInterval(() => {
|
|
861
|
+
extId = getExtensionId(), extId && (clearInterval(check), resolve());
|
|
862
|
+
}, 50);
|
|
863
|
+
setTimeout(() => resolve(), 5e3);
|
|
864
|
+
}), extId && (window.sentience_registry = [], window.sentience = {
|
|
865
|
+
snapshot: snapshot,
|
|
866
|
+
read: read,
|
|
867
|
+
findTextRect: findTextRect,
|
|
868
|
+
click: click,
|
|
869
|
+
startRecording: startRecording,
|
|
870
|
+
showOverlay: showOverlay,
|
|
871
|
+
clearOverlay: clearOverlay
|
|
872
|
+
}, window.sentience_iframe_handler_setup || (window.addEventListener("message", async event => {
|
|
873
|
+
if ("SENTIENCE_IFRAME_SNAPSHOT_REQUEST" === event.data?.type) {
|
|
874
|
+
const {requestId: requestId, options: options} = event.data;
|
|
875
|
+
try {
|
|
876
|
+
const snapshotOptions = {
|
|
877
|
+
...options,
|
|
878
|
+
collectIframes: !0,
|
|
879
|
+
waitForStability: (options.waitForStability, !1)
|
|
880
|
+
}, snapshot = await window.sentience.snapshot(snapshotOptions);
|
|
881
|
+
event.source && event.source.postMessage && event.source.postMessage({
|
|
882
|
+
type: "SENTIENCE_IFRAME_SNAPSHOT_RESPONSE",
|
|
883
|
+
requestId: requestId,
|
|
884
|
+
snapshot: snapshot,
|
|
885
|
+
error: null
|
|
886
|
+
}, "*");
|
|
887
|
+
} catch (error) {
|
|
888
|
+
event.source && event.source.postMessage && event.source.postMessage({
|
|
889
|
+
type: "SENTIENCE_IFRAME_SNAPSHOT_RESPONSE",
|
|
890
|
+
requestId: requestId,
|
|
891
|
+
snapshot: null,
|
|
892
|
+
error: error.message
|
|
893
|
+
}, "*");
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}), window.sentience_iframe_handler_setup = !0));
|
|
897
|
+
})();
|
|
898
|
+
}();
|