Files
openswarm/frontend/src/shared/browserSettle.ts
T

40 lines
2.6 KiB
TypeScript

// Renderer-side mirror of the backend's smart-wait (browser_wait.py): a `wait` that returns the instant the page is READY instead of blind-sleeping. A top-level BrowserWait gets this on the backend, but a `wait` INSIDE a BrowserBatch runs entirely here, so without this it would fall back to a dumb sleep. Two readiness signals plus a target: settle when the agent's `until` target is present (no waiting blind), else when the page goes quiet by EITHER the network OR the DOM settling (beacon-heavy SPAs never idle the network but their DOM does).
export const SETTLE_FLOOR_MS = 250; // never settle before this unless the target is already there
export const SETTLE_QUIET_MS = 400; // network OR DOM must be quiet this long to count as settled
export const SETTLE_POLL_MS = 150;
// Probe built per wait so it can also look for the agent's target. Returns ready + quiet (network-idle ms) + elems (element count; the caller watches it stop changing = DOM settle) + found (the `until` target is present + visible). `until` is JSON- encoded into a string literal, so it is data, never executable.
export function settleProbeJs(until: string): string {
const spec = JSON.stringify(until || '');
return (
'(()=>{const n=performance.now();' +
"const es=performance.getEntriesByType('resource');let last=0;" +
'for(const e of es){const t=Math.max(e.responseEnd||0,e.startTime||0);if(t>last)last=t;}' +
'let found=false;const spec=' + spec + ';' +
'if(spec){try{const low=spec.toLowerCase();' +
"let el=[...document.querySelectorAll('button,a,[role],input,textarea,[contenteditable],[aria-label],h1,h2')]" +
".find(e=>((e.innerText||e.value||e.getAttribute('aria-label')||'')+'').toLowerCase().includes(low));" +
'if(!el){try{el=document.querySelector(spec);}catch(_){}}' +
'if(el){const r=el.getBoundingClientRect();found=r.width>0&&r.height>0;}}catch(_){}}' +
"return JSON.stringify({ready:document.readyState==='complete'," +
'quiet:Math.round(n-last),elems:document.getElementsByTagName(\'*\').length,found});})()'
);
}
// Pure decision. Stop the instant the target is present; otherwise, past the floor and once the document is complete, stop as soon as it's quiet by EITHER network OR DOM.
export function shouldStopWaiting(
ready: boolean,
quietMs: number,
domStableMs: number,
found: boolean,
elapsedMs: number,
floorMs = SETTLE_FLOOR_MS,
windowMs = SETTLE_QUIET_MS,
): boolean {
if (found) return true;
if (elapsedMs < floorMs) return false;
if (!ready) return false;
return (quietMs || 0) >= windowMs || (domStableMs || 0) >= windowMs;
}