Skip to content

Commit 08ec993

Browse files
authored
Merge pull request #123 from SentienceAPI/sync-extension-v2.5.0
Sync Extension: v2.5.0
2 parents c8c14b9 + f725e2e commit 08ec993

File tree

5 files changed

+334
-205
lines changed

5 files changed

+334
-205
lines changed

src/extension/injected_api.js

Lines changed: 90 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,12 @@
158158
return null;
159159
}
160160
function getText(el) {
161-
return el.getAttribute("aria-label") ? el.getAttribute("aria-label") : "INPUT" === el.tagName ? el.value || el.placeholder || "" : "IMG" === el.tagName ? el.alt || "" : (el.innerText || "").replace(/\s+/g, " ").trim().substring(0, 100);
161+
if (el.getAttribute("aria-label")) return el.getAttribute("aria-label");
162+
if ("INPUT" === el.tagName) {
163+
const t = el.getAttribute && el.getAttribute("type") || el.type || "";
164+
return "password" === String(t).toLowerCase() ? el.placeholder || "" : el.value || el.placeholder || "";
165+
}
166+
return "IMG" === el.tagName ? el.alt || "" : (el.innerText || "").replace(/\s+/g, " ").trim().substring(0, 100);
162167
}
163168
function getClassName(el) {
164169
if (!el || !el.className) return "";
@@ -268,11 +273,17 @@
268273
try {
269274
!1 !== options.waitForStability && await async function(options = {}) {
270275
const {minNodeCount: minNodeCount = 500, quietPeriod: quietPeriod = 200, maxWait: maxWait = 5e3} = options, startTime = Date.now();
276+
try {
277+
window.__sentience_lastMutationTs = performance.now();
278+
} catch (e) {}
271279
return new Promise(resolve => {
272280
if (document.querySelectorAll("*").length >= minNodeCount) {
273281
let lastChange = Date.now();
274282
const observer = new MutationObserver(() => {
275283
lastChange = Date.now();
284+
try {
285+
window.__sentience_lastMutationTs = performance.now();
286+
} catch (e) {}
276287
});
277288
observer.observe(document.body, {
278289
childList: !0,
@@ -288,11 +299,17 @@
288299
} else {
289300
const observer = new MutationObserver(() => {
290301
const currentCount = document.querySelectorAll("*").length, totalWait = Date.now() - startTime;
302+
try {
303+
window.__sentience_lastMutationTs = performance.now();
304+
} catch (e) {}
291305
if (currentCount >= minNodeCount) {
292306
observer.disconnect();
293307
let lastChange = Date.now();
294308
const quietObserver = new MutationObserver(() => {
295309
lastChange = Date.now();
310+
try {
311+
window.__sentience_lastMutationTs = performance.now();
312+
} catch (e) {}
296313
});
297314
quietObserver.observe(document.body, {
298315
childList: !0,
@@ -323,14 +340,15 @@
323340
if (!el.getBoundingClientRect) return;
324341
const rect = el.getBoundingClientRect();
325342
if (rect.width < 5 || rect.height < 5) return;
326-
if ("span" === el.tagName.toLowerCase()) {
343+
const tagName = el.tagName.toLowerCase();
344+
if ("span" === tagName) {
327345
if (el.closest("a")) return;
328346
const childLink = el.querySelector("a[href]");
329347
if (childLink && childLink.href) return;
330348
options.debug && el.className && el.className.includes("titleline");
331349
}
332350
window.sentience_registry[idx] = el;
333-
const semanticText = function(el, options = {}) {
351+
const inputType = "input" === tagName ? toSafeString(el.getAttribute && el.getAttribute("type") || el.type || null) : null, isPasswordInput = inputType && "password" === inputType.toLowerCase(), semanticText = function(el, options = {}) {
334352
if (!el) return {
335353
text: "",
336354
source: null
@@ -341,10 +359,10 @@
341359
source: "explicit_aria_label"
342360
};
343361
if ("INPUT" === el.tagName) {
344-
const value = (el.value || el.placeholder || "").trim();
362+
const t = el.getAttribute && el.getAttribute("type") || el.type || "", isPassword = "password" === String(t).toLowerCase(), value = (isPassword ? el.placeholder || "" : el.value || el.placeholder || "").trim();
345363
if (value) return {
346364
text: value,
347-
source: "input_value"
365+
source: isPassword ? "input_placeholder" : "input_value"
348366
};
349367
}
350368
if ("IMG" === el.tagName) {
@@ -417,9 +435,53 @@
417435
}
418436
return null;
419437
}(el);
438+
let safeValue = null, valueRedacted = null;
439+
try {
440+
if (void 0 !== el.value || el.getAttribute && null !== el.getAttribute("value")) if (isPasswordInput) safeValue = null,
441+
valueRedacted = "true"; else {
442+
const rawValue = void 0 !== el.value ? String(el.value) : String(el.getAttribute("value"));
443+
safeValue = rawValue.length > 200 ? rawValue.substring(0, 200) : rawValue, valueRedacted = "false";
444+
}
445+
} catch (e) {}
446+
const accessibleName = toSafeString(function(el) {
447+
if (!el || !el.getAttribute) return "";
448+
const ariaLabel = el.getAttribute("aria-label");
449+
if (ariaLabel && ariaLabel.trim()) return ariaLabel.trim().substring(0, 200);
450+
const labelledBy = el.getAttribute("aria-labelledby");
451+
if (labelledBy && labelledBy.trim()) {
452+
const ids = labelledBy.split(/\s+/).filter(id => id.trim()), texts = [];
453+
for (const id of ids) try {
454+
const ref = document.getElementById(id);
455+
if (!ref) continue;
456+
const txt = (ref.innerText || ref.textContent || ref.getAttribute?.("aria-label") || "").toString().trim();
457+
txt && texts.push(txt);
458+
} catch (e) {}
459+
if (texts.length > 0) return texts.join(" ").substring(0, 200);
460+
}
461+
try {
462+
if (el.labels && el.labels.length > 0) {
463+
const t = (el.labels[0].innerText || el.labels[0].textContent || "").toString().trim();
464+
if (t) return t.substring(0, 200);
465+
}
466+
} catch (e) {}
467+
try {
468+
const parentLabel = el.closest && el.closest("label");
469+
if (parentLabel) {
470+
const t = (parentLabel.innerText || parentLabel.textContent || "").toString().trim();
471+
if (t) return t.substring(0, 200);
472+
}
473+
} catch (e) {}
474+
const tag = (el.tagName || "").toUpperCase();
475+
if ("INPUT" === tag || "TEXTAREA" === tag) {
476+
const ph = (el.getAttribute("placeholder") || "").toString().trim();
477+
if (ph) return ph.substring(0, 200);
478+
}
479+
const title = el.getAttribute("title");
480+
return title && title.trim() ? title.trim().substring(0, 200) : "";
481+
}(el) || null);
420482
rawData.push({
421483
id: idx,
422-
tag: el.tagName.toLowerCase(),
484+
tag: tagName,
423485
rect: {
424486
x: rect.x,
425487
y: rect.y,
@@ -441,14 +503,21 @@
441503
attributes: {
442504
role: toSafeString(el.getAttribute("role")),
443505
type_: toSafeString(el.getAttribute("type")),
506+
input_type: inputType,
444507
aria_label: "explicit_aria_label" === semanticText?.source ? semanticText.text : toSafeString(el.getAttribute("aria-label")),
508+
name: accessibleName,
445509
inferred_label: semanticText?.source && ![ "explicit_aria_label", "input_value", "img_alt", "inner_text" ].includes(semanticText.source) ? toSafeString(semanticText.text) : null,
446510
label_source: semanticText?.source || null,
447511
inferred_role: inferredRole ? toSafeString(inferredRole) : null,
448512
href: toSafeString(el.href || el.getAttribute("href") || el.closest && el.closest("a")?.href || null),
449513
class: toSafeString(getClassName(el)),
450-
value: void 0 !== el.value ? toSafeString(el.value) : toSafeString(el.getAttribute("value")),
451-
checked: void 0 !== el.checked ? String(el.checked) : null
514+
value: null !== safeValue ? toSafeString(safeValue) : null,
515+
value_redacted: valueRedacted,
516+
checked: void 0 !== el.checked ? String(el.checked) : null,
517+
disabled: void 0 !== el.disabled ? String(el.disabled) : null,
518+
aria_checked: toSafeString(el.getAttribute("aria-checked")),
519+
aria_disabled: toSafeString(el.getAttribute("aria-disabled")),
520+
aria_expanded: toSafeString(el.getAttribute("aria-expanded"))
452521
},
453522
text: toSafeString(textVal),
454523
in_viewport: inView,
@@ -576,6 +645,17 @@
576645
}(options.screenshot));
577646
const cleanedElements = cleanElement(processed.elements), cleanedRawElements = cleanElement(processed.raw_elements);
578647
cleanedElements.length, cleanedRawElements.length;
648+
let diagnostics;
649+
try {
650+
const lastMutationTs = window.__sentience_lastMutationTs, now = performance.now(), quietMs = "number" == typeof lastMutationTs && Number.isFinite(lastMutationTs) ? Math.max(0, now - lastMutationTs) : null, nodeCount = document.querySelectorAll("*").length;
651+
diagnostics = {
652+
metrics: {
653+
ready_state: document.readyState || null,
654+
quiet_ms: quietMs,
655+
node_count: nodeCount
656+
}
657+
};
658+
} catch (e) {}
579659
return {
580660
status: "success",
581661
url: window.location.href,
@@ -585,7 +665,8 @@
585665
},
586666
elements: cleanedElements,
587667
raw_elements: cleanedRawElements,
588-
screenshot: screenshot
668+
screenshot: screenshot,
669+
diagnostics: diagnostics
589670
};
590671
} catch (error) {
591672
return {

src/extension/pkg/sentience_core.d.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,34 +18,34 @@ export function prune_for_api(val: any): any;
1818
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
1919

2020
export interface InitOutput {
21-
readonly memory: WebAssembly.Memory;
22-
readonly analyze_page: (a: number) => number;
23-
readonly analyze_page_with_options: (a: number, b: number) => number;
24-
readonly decide_and_act: (a: number) => void;
25-
readonly prune_for_api: (a: number) => number;
26-
readonly __wbindgen_export: (a: number, b: number) => number;
27-
readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
28-
readonly __wbindgen_export3: (a: number) => void;
21+
readonly memory: WebAssembly.Memory;
22+
readonly analyze_page: (a: number) => number;
23+
readonly analyze_page_with_options: (a: number, b: number) => number;
24+
readonly decide_and_act: (a: number) => void;
25+
readonly prune_for_api: (a: number) => number;
26+
readonly __wbindgen_export: (a: number, b: number) => number;
27+
readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
28+
readonly __wbindgen_export3: (a: number) => void;
2929
}
3030

3131
export type SyncInitInput = BufferSource | WebAssembly.Module;
3232

3333
/**
34-
* Instantiates the given `module`, which can either be bytes or
35-
* a precompiled `WebAssembly.Module`.
36-
*
37-
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
38-
*
39-
* @returns {InitOutput}
40-
*/
34+
* Instantiates the given `module`, which can either be bytes or
35+
* a precompiled `WebAssembly.Module`.
36+
*
37+
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
38+
*
39+
* @returns {InitOutput}
40+
*/
4141
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
4242

4343
/**
44-
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
45-
* for everything else, calls `WebAssembly.instantiate` directly.
46-
*
47-
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
48-
*
49-
* @returns {Promise<InitOutput>}
50-
*/
44+
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
45+
* for everything else, calls `WebAssembly.instantiate` directly.
46+
*
47+
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
48+
*
49+
* @returns {Promise<InitOutput>}
50+
*/
5151
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;

0 commit comments

Comments
 (0)