mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-04 12:07:09 +00:00
fix(web-runtime): retry action resolution + implement taps/swipes
V8-side runtime previously returned null when weighted picked a generator that returned [] (page-gated), causing 80%+ of ticks on narrow routes to emit no action and no post-screenshot. Now retries up to 16x like goja, and the taps/swipes builtins query the live DOM for clickable elements / dispatch random swipes instead of returning null.
This commit is contained in:
1 parent
b1ede54dab
commit
82cf889e16
1 file changed
+54
-3
@@ -386,9 +386,9 @@ function resolveGenerator(handle: ActionGeneratorHandle): unknown {
|
|||||||
return resolveGenerator(inner);
|
return resolveGenerator(inner);
|
||||||
}
|
}
|
||||||
case "taps":
|
case "taps":
|
||||||
return null; // host-side fallback handles random taps using hierarchy
|
return randomTap();
|
||||||
case "swipes":
|
case "swipes":
|
||||||
return null;
|
return randomSwipe();
|
||||||
case "waitOnce":
|
case "waitOnce":
|
||||||
return { kind: "Wait", durationMillis: 500 };
|
return { kind: "Wait", durationMillis: 500 };
|
||||||
case "pressKey":
|
case "pressKey":
|
||||||
@@ -398,6 +398,50 @@ function resolveGenerator(handle: ActionGeneratorHandle): unknown {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function randomTap(): unknown {
|
||||||
|
const candidates = Array.from(
|
||||||
|
document.querySelectorAll<HTMLElement>(
|
||||||
|
'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 (candidates.length === 0) return null;
|
||||||
|
const picked = candidates[Math.floor(Math.random() * candidates.length)];
|
||||||
|
if (!picked) return null;
|
||||||
|
const rect = picked.getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
kind: "Tap",
|
||||||
|
on: {
|
||||||
|
x: Math.round(rect.left + rect.width / 2),
|
||||||
|
y: Math.round(rect.top + rect.height / 2),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomSwipe(): unknown {
|
||||||
|
const width = window.innerWidth || 320;
|
||||||
|
const height = window.innerHeight || 480;
|
||||||
|
const cx = Math.round(width / 2);
|
||||||
|
const cy = Math.round(height / 2);
|
||||||
|
const magnitude = 200 + Math.floor(Math.random() * 401);
|
||||||
|
const direction = Math.floor(Math.random() * 4);
|
||||||
|
let toX = cx;
|
||||||
|
let toY = cy;
|
||||||
|
if (direction === 0) toY = Math.max(0, cy - magnitude);
|
||||||
|
else if (direction === 1) toY = cy + magnitude;
|
||||||
|
else if (direction === 2) toX = Math.max(0, cx - magnitude);
|
||||||
|
else toX = cx + magnitude;
|
||||||
|
return {
|
||||||
|
kind: "Swipe",
|
||||||
|
from: { x: cx, y: cy },
|
||||||
|
to: { x: toX, y: toY },
|
||||||
|
durationMillis: 250,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function pickFromArray(value: unknown): unknown {
|
function pickFromArray(value: unknown): unknown {
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
if (!Array.isArray(value)) return value;
|
if (!Array.isArray(value)) return value;
|
||||||
@@ -458,7 +502,14 @@ function pointOf(value: unknown): { x: number; y: number } | undefined {
|
|||||||
|
|
||||||
(globalThis as Record<string, unknown>).__sanderlingNextAction__ = function (): unknown {
|
(globalThis as Record<string, unknown>).__sanderlingNextAction__ = function (): unknown {
|
||||||
if (!actionsRoot) return null;
|
if (!actionsRoot) return null;
|
||||||
return serializeAction(resolveGenerator(actionsRoot));
|
// Match the goja runtime: retry up to 16 times when a weighted entry's
|
||||||
|
// generator returns []. Otherwise on routes where most generators are
|
||||||
|
// gated to other pages, ~80% of ticks would emit no action.
|
||||||
|
for (let attempt = 0; attempt < 16; attempt++) {
|
||||||
|
const action = serializeAction(resolveGenerator(actionsRoot));
|
||||||
|
if (action !== null) return action;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export {};
|
export {};
|
||||||
Reference in new issue
Block a user