After writing with the pen, fingers could no longer navigate: - every touch was ignored for 5 s after any pen activity (and for as long as a hover-capable pen stayed near the screen), - after that, a one-finger drag on a page drew ink instead of panning, - a one-finger drag on the background did nothing while an ink tool was on. Once a pen has been used, fingers now only pan and zoom. The 5 s session and hover lockouts only block finger ink; touches are still rejected while the pen is down, for palm-sized contacts and within 150 ms of pen activity. A finger drag off the page always pans. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VsorPV3JeJRoZ1mD1pdvtL
56 lines
2.3 KiB
TypeScript
56 lines
2.3 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { PalmRejector } from './palm';
|
|
|
|
const ev = (type: string, pointerType: string, extra: Partial<PointerEvent> = {}) =>
|
|
({ type, pointerType, pointerId: pointerType === 'pen' ? 1 : 2, width: 10, height: 10, ...extra }) as PointerEvent;
|
|
const touch = (extra: Partial<PointerEvent> = {}) => ev('pointerdown', 'touch', extra);
|
|
|
|
describe('palm rejection', () => {
|
|
beforeEach(() => vi.useFakeTimers());
|
|
afterEach(() => vi.useRealTimers());
|
|
|
|
/** Pen writes a stroke, hovers, then leaves range; `ms` later a finger lands. */
|
|
function afterPen(ms: number, { leave = true } = {}) {
|
|
const palm = new PalmRejector();
|
|
for (const t of ['pointermove', 'pointerdown', 'pointermove', 'pointerup', 'pointermove']) palm.track(ev(t, 'pen'));
|
|
if (leave) palm.track(ev('pointerleave', 'pen'));
|
|
vi.advanceTimersByTime(ms);
|
|
return palm;
|
|
}
|
|
|
|
it('accepts fingers before any pen is used', () => {
|
|
const palm = new PalmRejector();
|
|
expect(palm.penSeen).toBe(false);
|
|
expect(palm.evaluate(touch(), 'draw').accept).toBe(true);
|
|
});
|
|
|
|
it('rejects every touch while the pen is down', () => {
|
|
const palm = new PalmRejector();
|
|
palm.track(ev('pointerdown', 'pen'));
|
|
vi.advanceTimersByTime(1000);
|
|
expect(palm.evaluate(touch(), 'navigate').accept).toBe(false);
|
|
expect(palm.evaluate(touch(), 'draw').accept).toBe(false);
|
|
});
|
|
|
|
it('lets fingers navigate between pen strokes but not draw', () => {
|
|
const palm = afterPen(300);
|
|
expect(palm.penSeen).toBe(true);
|
|
expect(palm.evaluate(touch(), 'navigate').accept).toBe(true);
|
|
expect(palm.evaluate(touch(), 'draw')).toEqual({ accept: false, reason: 'pen session active' });
|
|
});
|
|
|
|
it('lets fingers navigate while the pen hovers, but not draw', () => {
|
|
const palm = afterPen(6000, { leave: false });
|
|
expect(palm.evaluate(touch(), 'navigate').accept).toBe(true);
|
|
expect(palm.evaluate(touch(), 'draw')).toEqual({ accept: false, reason: 'pen hovering' });
|
|
});
|
|
|
|
it('still rejects palms and touches right after the pen', () => {
|
|
expect(afterPen(50).evaluate(touch(), 'navigate').accept).toBe(false);
|
|
expect(afterPen(300).evaluate(touch({ width: 60, height: 60 }), 'navigate').accept).toBe(false);
|
|
});
|
|
|
|
it('allows finger ink again once the pen session ends', () => {
|
|
expect(afterPen(5100).evaluate(touch(), 'draw').accept).toBe(true);
|
|
});
|
|
});
|