feat: initial release of human_mouse v0.1.0
A small Python library for replaying real human mouse trajectories from the SapiMouse dataset onto a Playwright page. Designed for ML-based bot-detection research, behavioral biometrics prototyping, and replay-based test fixtures. Public API: load_all_segments, pick_segments, affine_warp, upsample, replay, replay_random, download_sapimouse. - src/ layout with hatchling build backend - 23 pytest tests (10 transform unit + 13 integration) - MIT license, PEP 561 py.typed marker - python -m human_mouse download for one-shot dataset fetch - examples/cloakbrowser_demo.py demonstrates end-to-end use with CloakBrowser Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
81
examples/demo.html
Normal file
81
examples/demo.html
Normal file
@@ -0,0 +1,81 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>human_mouse — Trajectory Demo</title>
|
||||
<style>
|
||||
html, body { margin: 0; padding: 0; background: #fafafa; font-family: ui-sans-serif, system-ui, sans-serif; overflow: hidden; }
|
||||
#label { position: fixed; top: 8px; left: 8px; padding: 6px 12px; background: #111; color: #fff; border-radius: 6px; font-size: 14px; z-index: 10; }
|
||||
#count { position: fixed; top: 8px; right: 8px; padding: 6px 12px; background: #fff; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; z-index: 10; }
|
||||
#start { position: fixed; left: 130px; top: 130px; width: 40px; height: 40px; border-radius: 50%; background: #16a34a; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 12px; z-index: 10; }
|
||||
#target { position: fixed; left: 1030px; top: 530px; width: 60px; height: 40px; background: #2563eb; color: #fff; border: none; border-radius: 6px; font-size: 14px; cursor: pointer; z-index: 10; }
|
||||
canvas { position: fixed; inset: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="label">mode: …</div>
|
||||
<div id="count">points: 0</div>
|
||||
<div id="start">start</div>
|
||||
<button id="target">target</button>
|
||||
<canvas id="trail"></canvas>
|
||||
|
||||
<script>
|
||||
const params = new URLSearchParams(location.search);
|
||||
const mode = params.get("mode") || "unknown";
|
||||
document.getElementById("label").textContent = "mode: " + mode;
|
||||
|
||||
const canvas = document.getElementById("trail");
|
||||
const ctx = canvas.getContext("2d");
|
||||
function resize() {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
}
|
||||
resize();
|
||||
window.addEventListener("resize", resize);
|
||||
|
||||
window.__points = [];
|
||||
let last = null;
|
||||
let hue = 0; // degrees on the HSL wheel; 0 = red (default)
|
||||
|
||||
window.__setHue = (h) => { hue = h; last = null; };
|
||||
window.__resetTrace = () => { window.__points = []; last = null; };
|
||||
|
||||
function recordPoint(x, y, t) {
|
||||
const p = { x, y, t };
|
||||
window.__points.push(p);
|
||||
|
||||
ctx.fillStyle = `hsla(${hue}, 75%, 45%, 0.85)`;
|
||||
ctx.beginPath();
|
||||
ctx.arc(p.x, p.y, 2, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
if (last) {
|
||||
ctx.strokeStyle = `hsla(${hue}, 75%, 45%, 0.45)`;
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(last.x, last.y);
|
||||
ctx.lineTo(p.x, p.y);
|
||||
ctx.stroke();
|
||||
}
|
||||
last = p;
|
||||
}
|
||||
|
||||
// pointermove fires once per render frame, but getCoalescedEvents() returns
|
||||
// every sub-event the browser folded into this frame — so we recover the
|
||||
// full CDP event stream the driver sent us.
|
||||
window.addEventListener("pointermove", (e) => {
|
||||
const subs = e.getCoalescedEvents ? e.getCoalescedEvents() : [e];
|
||||
const events = subs.length ? subs : [e];
|
||||
for (const s of events) {
|
||||
recordPoint(s.clientX, s.clientY, s.timeStamp);
|
||||
}
|
||||
document.getElementById("count").textContent = "points: " + window.__points.length;
|
||||
}, { passive: true });
|
||||
|
||||
window.__clickedTarget = false;
|
||||
document.getElementById("target").addEventListener("click", () => {
|
||||
window.__clickedTarget = true;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user