Elbow-on-the-keyboard guard: a PTT hold longer than 5 min (fixed, not a setting) is released exactly like a keyup — pttActive off, mic muted — with a toast 'Push to talk released after 5 minutes — press the key again to keep talking.' A fresh press re-engages normally. One watchdog shared by the DOM path and the desktop-global hotkey path; keydown auto-repeat no longer restarts anything; non-PTT mode, deafen and the mic button are untouched. Helper unit-tested with fake timers; verified headless with Playwright's clock: held → still open at +4 min → released + toast at +5 min → re-press works. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
75 lines
1.5 KiB
TypeScript
75 lines
1.5 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { createPttHoldWatchdog } from './pttHoldWatchdog';
|
|
|
|
const fake = () => {
|
|
let now = 0;
|
|
const timers = new Map<number, { at: number; fn: () => void }>();
|
|
let seq = 0;
|
|
return {
|
|
timers: {
|
|
setTimeout: (fn: () => void, ms: number) => {
|
|
seq += 1;
|
|
timers.set(seq, { at: now + ms, fn });
|
|
return seq;
|
|
},
|
|
clearTimeout: (h: unknown) => {
|
|
timers.delete(h as number);
|
|
},
|
|
},
|
|
advance(ms: number) {
|
|
now += ms;
|
|
[...timers.entries()]
|
|
.filter(([, t]) => t.at <= now)
|
|
.forEach(([id, t]) => {
|
|
timers.delete(id);
|
|
t.fn();
|
|
});
|
|
},
|
|
};
|
|
};
|
|
|
|
test('a hold past the limit expires once; a release before it never does', () => {
|
|
const clock = fake();
|
|
let expired = 0;
|
|
const w = createPttHoldWatchdog(
|
|
() => {
|
|
expired += 1;
|
|
},
|
|
1000,
|
|
clock.timers,
|
|
);
|
|
w.arm();
|
|
clock.advance(999);
|
|
assert.equal(expired, 0);
|
|
clock.advance(1);
|
|
assert.equal(expired, 1);
|
|
assert.equal(w.armed(), false);
|
|
|
|
w.arm();
|
|
clock.advance(500);
|
|
w.disarm();
|
|
clock.advance(2000);
|
|
assert.equal(expired, 1);
|
|
});
|
|
|
|
test('re-pressing restarts the clock', () => {
|
|
const clock = fake();
|
|
let expired = 0;
|
|
const w = createPttHoldWatchdog(
|
|
() => {
|
|
expired += 1;
|
|
},
|
|
1000,
|
|
clock.timers,
|
|
);
|
|
w.arm();
|
|
clock.advance(800);
|
|
w.disarm();
|
|
w.arm();
|
|
clock.advance(800);
|
|
assert.equal(expired, 0);
|
|
clock.advance(200);
|
|
assert.equal(expired, 1);
|
|
});
|