Zhen Ye
feat: Continuous object tracking, speed estimation, and overlay syncing
ff50694
// Agent Cursor Animation Module
APP.ui.cursor = {};
APP.ui.cursor.ensureAgentCursorOverlay = function () {
const { $ } = APP.core.utils;
if ($("#agentCursor")) return;
const el = document.createElement("div");
el.id = "agentCursor";
el.style.cssText = `
position: fixed;
width: 12px;
height: 12px;
border-radius: 50%;
background: linear-gradient(135deg, rgba(34, 211, 238, 0.9), rgba(124, 58, 237, 0.9));
box-shadow: 0 0 20px rgba(34, 211, 238, 0.6), 0 0 40px rgba(124, 58, 237, 0.4);
pointer-events: none;
z-index: 10000;
opacity: 0;
display: none;
transition: opacity 0.3s ease;
`;
document.body.appendChild(el);
};
APP.ui.cursor.setCursorVisible = function (visible) {
const { $ } = APP.core.utils;
const { state } = APP.core;
APP.ui.cursor.ensureAgentCursorOverlay();
const el = $("#agentCursor");
if (!el) return;
state.ui.agentCursor.visible = visible;
el.style.opacity = visible ? "1" : "0";
el.style.display = visible ? "block" : "none";
};
APP.ui.cursor.moveCursorToRect = function (rect) {
const { state } = APP.core;
const { $, now } = APP.core.utils;
if (state.ui.cursorMode === "off") return;
APP.ui.cursor.ensureAgentCursorOverlay();
const el = $("#agentCursor");
if (!el) return;
const c = state.ui.agentCursor;
c.visible = true;
c.target = rect;
c.t0 = now();
el.style.opacity = "1";
el.style.display = "block";
};
APP.ui.cursor.tickAgentCursor = function () {
const { state } = APP.core;
const { $, clamp, now } = APP.core.utils;
const el = $("#agentCursor");
if (!el || state.ui.cursorMode !== "on" || !state.ui.agentCursor.visible) return;
const c = state.ui.agentCursor;
if (!c.target) return;
const tx = c.target.left + c.target.width * 0.72;
const ty = c.target.top + c.target.height * 0.50;
// Smooth spring physics
const dx = tx - (c.x * window.innerWidth);
const dy = ty - (c.y * window.innerHeight);
c.vx = (c.vx + dx * 0.0018) * 0.85;
c.vy = (c.vy + dy * 0.0018) * 0.85;
const px = (c.x * window.innerWidth) + c.vx * 18;
const py = (c.y * window.innerHeight) + c.vy * 18;
c.x = clamp(px / window.innerWidth, 0.02, 0.98);
c.y = clamp(py / window.innerHeight, 0.02, 0.98);
el.style.transform = `translate(${c.x * window.innerWidth}px, ${c.y * window.innerHeight}px)`;
// Hide after settling
const settle = Math.hypot(dx, dy);
if (settle < 6 && (now() - c.t0) > 650) {
el.style.opacity = "0.75";
}
};