diff --git a/pkg/spec/src/web-runtime.ts b/pkg/spec/src/web-runtime.ts index 6e52b0c..9fafce5 100644 --- a/pkg/spec/src/web-runtime.ts +++ b/pkg/spec/src/web-runtime.ts @@ -434,16 +434,23 @@ function resolveGenerator(handle: ActionGeneratorHandle): unknown { } } +// Per-tick cache so the 16-attempt retry loop in __sanderlingNextAction__ +// doesn't re-walk the DOM and re-flush layout on every iteration. +let randomTapCandidates: HTMLElement[] | null = null; + function randomTap(): unknown { - const candidates = Array.from( - document.querySelectorAll( - 'a, button, input, select, textarea, [role="button"], [onclick]', - ), - ).filter((element) => { - if ((element as HTMLButtonElement).disabled) return false; - const rect = element.getBoundingClientRect(); - return rect.width > 0 && rect.height > 0; - }); + if (!randomTapCandidates) { + randomTapCandidates = Array.from( + document.querySelectorAll( + 'a, button, input, select, textarea, [role="button"], [onclick]', + ), + ).filter((element) => { + if ((element as HTMLButtonElement).disabled) return false; + const rect = element.getBoundingClientRect(); + return rect.width > 0 && rect.height > 0; + }); + } + const candidates = randomTapCandidates; if (candidates.length === 0) return null; const picked = candidates[Math.floor(Math.random() * candidates.length)]; if (!picked) return null; @@ -542,6 +549,8 @@ defineLockedGlobal("__sanderlingExtractors__", function (): Record