Next.js app with a browser-side buttplug/buttplug-wasm control layer (server never touches real-time device commands), SQLite storage via Drizzle, single-secret auth, recordings/replay with device remapping, a usage stats dashboard, Docker deployment, and Gitea CI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W8WkFF5ppURBAB918593Eb
54 lines
1.5 KiB
TypeScript
54 lines
1.5 KiB
TypeScript
/**
|
|
* Per-actuator leading+trailing debounce so a dragged slider doesn't fire a
|
|
* device command on every pointermove - devices don't need mouse-move-rate
|
|
* updates, and most BLE stacks choke on a command flood.
|
|
*/
|
|
const THROTTLE_MS = 75;
|
|
|
|
interface ThrottleEntry {
|
|
timer: ReturnType<typeof setTimeout> | null;
|
|
pendingValue: number | null;
|
|
pendingDuration: number | undefined;
|
|
lastSentAt: number;
|
|
}
|
|
|
|
const entries = new Map<string, ThrottleEntry>();
|
|
|
|
export function throttledSend(
|
|
key: string,
|
|
send: (value: number, durationMs?: number) => void | Promise<void>,
|
|
value: number,
|
|
durationMs?: number,
|
|
): void {
|
|
let entry = entries.get(key);
|
|
if (!entry) {
|
|
entry = { timer: null, pendingValue: null, pendingDuration: undefined, lastSentAt: 0 };
|
|
entries.set(key, entry);
|
|
}
|
|
|
|
entry.pendingValue = value;
|
|
entry.pendingDuration = durationMs;
|
|
|
|
const flush = (): void => {
|
|
const v = entry!.pendingValue;
|
|
entry!.pendingValue = null;
|
|
entry!.timer = null;
|
|
entry!.lastSentAt = Date.now();
|
|
if (v !== null) void send(v, entry!.pendingDuration);
|
|
};
|
|
|
|
const elapsed = Date.now() - entry.lastSentAt;
|
|
if (elapsed >= THROTTLE_MS) {
|
|
if (entry.timer) clearTimeout(entry.timer);
|
|
flush();
|
|
} else if (!entry.timer) {
|
|
entry.timer = setTimeout(flush, THROTTLE_MS - elapsed);
|
|
}
|
|
}
|
|
|
|
export function clearThrottle(key: string): void {
|
|
const entry = entries.get(key);
|
|
if (entry?.timer) clearTimeout(entry.timer);
|
|
entries.delete(key);
|
|
}
|