@n-uf/motion 26.11.5 → 26.11.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  Versions are calendar versions, `YY.M.R` (year, month of the release, counter
4
4
  within the month), the same scheme as `@n-uf/hypr-tiling`.
5
5
 
6
+ ## 26.11.7
7
+
8
+ - No change in `@n-uf/motion`. Released together with `@n-uf/placement`
9
+ 26.11.7 (the new `./panels` entry).
10
+
11
+ ## 26.11.6
12
+
13
+ - `@n-uf/motion/drag`: the README shows a drag on a scaled page (a zoomed
14
+ canvas, a fitted preview): the intent works in the coordinate space of its
15
+ targets and home, so each pointer sample goes in as `(p − origin) / scale`.
16
+ - Docs: `item.retarget` is optional on a `LayoutItem` (`holdItem` falls back
17
+ to `moveTo`); the paged swipe's state diagram shows `cancel` from settling
18
+ back to idle.
19
+ - Released together with `@n-uf/placement` 26.11.6 (keyboard stops, slots,
20
+ `allows`, keep priority, stored arrangements).
21
+
6
22
  ## 26.11.5
7
23
 
8
24
  - `@n-uf/motion/drag`: `placeOf(target, point, previous)` is told the place
package/README.md CHANGED
@@ -7,6 +7,8 @@
7
7
  >
8
8
  > Version numbers are dates (YY.M.R), not semantic versions, so any release may break things, including how frames are scheduled, how springs settle and how gestures are timed. Pin an exact version and read the [CHANGELOG](https://github.com/n-uf/motion/blob/main/packages/motion/CHANGELOG.md) before you upgrade. If you need a stable animation library, use [Motion](https://motion.dev) or [GSAP](https://gsap.com). Both work on the DOM, so neither is a drop-in replacement.
9
9
 
10
+ Site and live demos: [motion.n-uf.com](https://motion.n-uf.com)
11
+
10
12
  One frame loop with an injected clock, fine-grained signals, springs and
11
13
  tweens, and a DOM-free gesture recogniser. No DOM, no framework, no globals:
12
14
  the host hands in a clock, and tests hand in a fake one.
@@ -203,7 +205,7 @@ drop targets) is the caller's layout.
203
205
  | `onDraw(batch)`, `flush()` | Batched drawing: every item that moved is drawn in one pass per frame (its drawable, then `onDraw` once with all their frames); an item at rest is not drawn. A change made between frames waits for the next frame, or `flush()` draws it now, which a host calls right after it commits a layout so the next paint shows no frame at the new box. |
204
206
  | `onActiveChange(active)` | Called once when the frames start and once when they stop. |
205
207
  | `item.moveTo(rect)` | Settle there from wherever it is drawn (a retarget mid-flight goes on from the drawn rect). The content is laid out at the new box once, and the item redrawn at once, so no frame shows the old offset from the new box. A size change is revealed, never scaled. |
206
- | `item.retarget(rect)` | Settle there like `moveTo`, but keep the content laid out where it is while the size is unchanged: for chasing a moving point (the held drag's glide back to the pointer uses it), which would otherwise lay the content out on every frame. |
208
+ | `item.retarget(rect)` | Optional on a `LayoutItem`. Settle there like `moveTo`, but keep the content laid out where it is while the size is unchanged: for chasing a moving point (the held drag's glide back to the pointer uses it), which would otherwise lay the content out on every frame. `holdItem` falls back to `moveTo` for an item without it. |
207
209
  | `item.jumpTo(rect)`, `item.hold(rect)`, `item.glideFrom(from, to)` | At once; held exactly where a pointer puts it (the content moves with it); drawn at `from`, then settle to `to`. |
208
210
  | `item.show(on, { now? })` | Appear or disappear with a uniform-scale fade (`popIn`); folding is a `moveTo` to the folded size, revealed. |
209
211
  | `item.drawn`, `target`, `box`, `settled`, `shown`, `showing`, `shownSettled` | Where it is drawn, where it is heading, where its content is laid out. |
@@ -381,6 +383,21 @@ for (let t = 0; t <= 300; t += 16) zones.update({ x: 50, y: 50 }, t, [pane]); //
381
383
  console.log(zones.state.shown, zones.release({ x: 50, y: 50 }, [pane])); // null null: nothing shows, the item goes home
382
384
  ```
383
385
 
386
+ The intent works in one coordinate space: the one its targets and home are in. On a page drawn scaled (a zoomed
387
+ canvas, a fitted preview), convert each pointer sample into page coordinates first, `(p − origin) / scale`, and the
388
+ band, breakthrough and stillness distances stay page px:
389
+
390
+ ```ts
391
+ // The page is drawn at half size, its top-left corner at (300, 200) on screen; targets are in page px.
392
+ const origin = { x: 300, y: 200 };
393
+ const scale = 0.5;
394
+ const toPage = (p: { x: number; y: number }) => ({ x: (p.x - origin.x) / scale, y: (p.y - origin.y) / scale });
395
+ const onPage = createDragIntent({ home: { x: 600, y: 600, width: 100, height: 50 } });
396
+ const screen = { x: 350, y: 225 }; // the pointer, as the browser reports it
397
+ for (let t = 0; t <= 300; t += 16) onPage.update(toPage(screen), t, [slot]); // page (100, 50): over the slot
398
+ console.log(onPage.state.shown?.id, onPage.release(toPage(screen), [slot])?.id); // "slot" "slot"
399
+ ```
400
+
384
401
  ## @n-uf/motion/browser
385
402
 
386
403
  | | |
@@ -818,4 +818,4 @@ export {
818
818
  pagedSwipeDeadline,
819
819
  createPagedSwipe
820
820
  };
821
- //# sourceMappingURL=chunk-BFKPRYEQ.js.map
821
+ //# sourceMappingURL=chunk-KVFX5V4Y.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/gestures/types.ts","../src/gestures/modifiers.ts","../src/gestures/wheel.ts","../src/gestures/recognizer.ts","../src/gestures/coalescer.ts","../src/gestures/machine.ts","../src/gestures/dwell.ts","../src/gestures/touch-press.ts","../src/gestures/paged-swipe.ts"],"sourcesContent":["/**\n * Input types: plain event objects in, one gesture stream out. Nothing here\n * touches the DOM; the host copies the fields it needs out of PointerEvent /\n * WheelEvent / Safari's GestureEvent.\n *\n * Ported from a host app's input layer, whose recogniser this is.\n * Hit testing and routing (what is under a point, who gets a gesture) stay\n * with the host.\n */\n\nimport type { Point } from \"../shapes\";\n\nexport type PointerKind = \"mouse\" | \"pen\" | \"touch\";\n\n/** Pointer modifiers, ctrl included (the drag modifier may be \"ctrl\") */\nexport interface Modifiers {\n shift: boolean;\n alt: boolean;\n meta: boolean;\n ctrl: boolean;\n}\n\nexport type ModifierKey = keyof Modifiers;\n\n/** \"mac\": the drag modifier defaults to Cmd; \"other\": Alt (the OS keeps the Win/Super key) */\nexport type Platform = \"mac\" | \"other\";\n\nexport const NO_MODIFIERS: Readonly<Modifiers> = Object.freeze({ shift: false, alt: false, meta: false, ctrl: false });\n\n/**\n * A pointer as the gesture stream reports it (screen, canvas, kind, buttons,\n * modifiers) plus the fields a recogniser needs: which pointer, which button\n * changed, when.\n */\nexport interface SurfacePointer {\n screen: Point; // CSS px relative to the host's view\n canvas: Point; // the host's world units, through its camera at the time of the event (RecognizerOptions.toCanvas)\n kind: PointerKind;\n buttons: number; // PointerEvent.buttons bit mask\n modifiers: Modifiers;\n pointerId: number;\n /** PointerEvent.button: 0 primary, 1 middle, 2 secondary (a pen's barrel button), 5 eraser; -1 none changed */\n button: number;\n time: number; // event timeStamp, ms\n pressure?: number;\n}\n\n// ----------------------------------------------------------------------------\n// Raw events (copied out of DOM events by the host)\n// ----------------------------------------------------------------------------\n\nexport type RawPointerType = \"pointerdown\" | \"pointermove\" | \"pointerup\" | \"pointercancel\" | \"lostpointercapture\";\n\nexport interface RawPointerEvent {\n type: RawPointerType;\n pointerId: number;\n pointerType: PointerKind | string; // unknown types are treated as mouse\n isPrimary?: boolean;\n button: number;\n buttons: number;\n screen: Point; // clientX/Y minus the view's origin\n modifiers?: Partial<Modifiers>;\n time: number;\n pressure?: number;\n /**\n * lostpointercapture only: whose capture was lost. \"content\" (an element inside the view's content, e.g.\n * right after the host took the pointer for a modifier-drag) leaves the gesture alone; default \"surface\".\n */\n lostBy?: \"surface\" | \"content\";\n}\n\nexport interface RawWheelEvent {\n type: \"wheel\";\n deltaX: number;\n deltaY: number;\n /** 0 pixels, 1 lines, 2 pages */\n deltaMode: 0 | 1 | 2;\n screen: Point;\n modifiers?: Partial<Modifiers>;\n time: number;\n}\n\n/** Safari's trackpad pinch (GestureEvent): scale is relative to the gesture's start */\nexport interface RawSafariGestureEvent {\n type: \"gesturestart\" | \"gesturechange\" | \"gestureend\";\n scale: number;\n screen: Point;\n modifiers?: Partial<Modifiers>;\n time: number;\n}\n\nexport type RawInputEvent = RawPointerEvent | RawWheelEvent | RawSafariGestureEvent;\n\n// ----------------------------------------------------------------------------\n// The gesture stream\n// ----------------------------------------------------------------------------\n\n/** What a modifier-drag would do if the pointer travels with the drag modifier held */\nexport type ModifierDrag = \"move\" | \"resize\";\n\nexport type CancelReason =\n | \"pointercancel\" // the browser or OS took the pointer (scroll, system gesture, palm rejection)\n | \"lostcapture\" // pointer capture lost before the pointer went up\n | \"multitouch\" // a second finger turned the press into a pinch\n | \"missed-up\" // its pointerup never came (the same pointer pressed again, or another went down long after)\n | \"escape\"\n | \"reset\" // the view lost focus, the tool changed, the host was disposed\n | (string & {});\n\ninterface PointerGestureBase {\n pointer: SurfacePointer;\n /** The press that started this gesture */\n origin: SurfacePointer;\n}\n\nexport type InputGesture =\n /** A pointer went down (one per gesture; a second touch becomes a pinch instead) */\n | { type: \"press\"; pointer: SurfacePointer; modifierDrag: ModifierDrag | null }\n /** The pointer travelled past the click threshold; modifierDrag is set when the drag modifier was held then */\n | (PointerGestureBase & { type: \"dragStart\"; modifierDrag: ModifierDrag | null })\n /** A move after dragStart (moves inside the threshold are not reported) */\n | (PointerGestureBase & { type: \"move\" })\n /** Held still past the long-press delay (touch and pen by default); at most once per press */\n | (PointerGestureBase & { type: \"longPress\" })\n /**\n * The pointer went up. click: it never crossed the threshold and no long press fired;\n * clickCount counts quick clicks at the same spot (2 = double click).\n */\n | (PointerGestureBase & { type: \"release\"; click: boolean; clickCount: number; dragged: boolean })\n | (PointerGestureBase & { type: \"cancel\"; reason: CancelReason; dragged: boolean })\n /** A pointer moving with no press (mouse and pen hover): cursor and chrome hover */\n | { type: \"hover\"; pointer: SurfacePointer }\n /**\n * Two fingers (touch) or a trackpad pinch. scale is the change since the last update (multiply it in);\n * pan is the centre's screen-px change since the last update. kind is what was recognised first.\n */\n | {\n type: \"pinch\";\n phase: \"start\" | \"update\" | \"end\" | \"cancel\";\n source: \"touch\" | \"trackpad\";\n kind: \"pinch\" | \"pan\";\n center: { screen: Point; canvas: Point };\n scale: number;\n pan: Point;\n modifiers: Modifiers;\n time: number;\n }\n /** A wheel or trackpad scroll that is not a pinch: delta in screen px (lines and pages converted) */\n | { type: \"scroll\"; delta: Point; at: { screen: Point; canvas: Point }; modifiers: Modifiers; time: number }\n /**\n * Zoom by a factor around a point: Cmd/Ctrl+wheel, a trackpad pinch reported as ctrl+wheel\n * (Chrome, Firefox, Edge). Safari's trackpad pinch comes as \"pinch\" with source \"trackpad\".\n */\n | { type: \"zoom\"; factor: number; at: { screen: Point; canvas: Point }; modifiers: Modifiers; time: number };\n\nexport type InputGestureType = InputGesture[\"type\"];\n","/**\n * Modifier keys: event modifiers to one shape, and the drag modifier\n * resolved per platform (Cmd on macOS, Alt elsewhere; null turns it off).\n */\n\nimport { NO_MODIFIERS, type ModifierKey, type Modifiers, type Platform, type PointerKind } from \"./types\";\n\nexport function toModifiers(m: Partial<Modifiers> | undefined): Modifiers {\n if (!m) return { ...NO_MODIFIERS };\n return { shift: !!m.shift, alt: !!m.alt, meta: !!m.meta, ctrl: !!m.ctrl };\n}\n\n/** The default drag modifier: \"meta\" (Cmd) on macOS, \"alt\" elsewhere */\nexport function defaultDragModifier(platform: Platform): ModifierKey {\n return platform === \"mac\" ? \"meta\" : \"alt\";\n}\n\n/**\n * A configured drag modifier as used: undefined → the platform default, null → off.\n */\nexport function resolveDragModifier(config: ModifierKey | null | undefined, platform: Platform): ModifierKey | null {\n if (config === null) return null;\n return config ?? defaultDragModifier(platform);\n}\n\nexport function isHeld(modifiers: Modifiers, key: ModifierKey | null): boolean {\n return key !== null && modifiers[key];\n}\n\n/**\n * The button a press stands for on this platform. On macOS a Ctrl+click is the secondary click (the system's own\n * rule; the browser's contextmenu follows it), so a primary mouse press with Ctrl held is button 2 there, as a\n * right-click is. Not when Ctrl is the drag modifier (Ctrl+drag then moves). A pen, a finger and the other platforms\n * keep their button.\n */\nexport function platformButton(press: { kind: PointerKind; button: number; modifiers: Modifiers }, platform: Platform, dragModifier: ModifierKey | null): number {\n return platform === \"mac\" && press.kind === \"mouse\" && press.button === 0 && press.modifiers.ctrl && dragModifier !== \"ctrl\" ? 2 : press.button;\n}\n\n/** Platform \"Mod\": Cmd on macOS, Ctrl elsewhere (Mod+wheel zooms, Mod+click is content's multi-select) */\nexport function modHeld(modifiers: Modifiers, platform: Platform): boolean {\n return platform === \"mac\" ? modifiers.meta : modifiers.ctrl;\n}\n","/**\n * Wheel normalisation (Figma / tldraw style): lines and pages to pixels;\n * Cmd/Ctrl+wheel and the trackpad pinch (ctrl+wheel in Chrome, Firefox, Edge)\n * zoom by 2^(−dy / sensitivity); any other wheel scrolls, Shift turns\n * vertical into horizontal.\n */\n\nimport type { Point } from \"../shapes\";\nimport type { Modifiers } from \"./types\";\n\nexport interface WheelOptions {\n /** px per line (deltaMode 1); default 40 */\n lineHeight: number;\n /** px per page (deltaMode 2); default 800 */\n pageHeight: number;\n /** zoom factor = 2^(−deltaY / sensitivity); default 300 */\n zoomSensitivity: number;\n}\n\nexport const WHEEL_DEFAULTS: Readonly<WheelOptions> = Object.freeze({ lineHeight: 40, pageHeight: 800, zoomSensitivity: 300 });\n\nexport function wheelDeltaPx(deltaX: number, deltaY: number, deltaMode: 0 | 1 | 2, opts: WheelOptions = WHEEL_DEFAULTS): Point {\n const k = deltaMode === 1 ? opts.lineHeight : deltaMode === 2 ? opts.pageHeight : 1;\n return { x: finite(deltaX) * k, y: finite(deltaY) * k };\n}\n\nexport type WheelIntent = { kind: \"zoom\"; factor: number } | { kind: \"scroll\"; delta: Point };\n\n/**\n * What a wheel event means. Ctrl or Meta held: zoom (a trackpad pinch arrives as ctrl+wheel).\n * Otherwise scroll; with Shift a vertical-only delta scrolls horizontally (mice without a\n * horizontal wheel).\n */\nexport function classifyWheel(delta: Point, modifiers: Modifiers, opts: WheelOptions = WHEEL_DEFAULTS): WheelIntent {\n if (modifiers.ctrl || modifiers.meta) {\n const factor = Math.pow(2, -delta.y / (opts.zoomSensitivity > 0 ? opts.zoomSensitivity : WHEEL_DEFAULTS.zoomSensitivity));\n return { kind: \"zoom\", factor: Number.isFinite(factor) && factor > 0 ? factor : 1 };\n }\n if (modifiers.shift && delta.x === 0) return { kind: \"scroll\", delta: { x: delta.y, y: 0 } };\n return { kind: \"scroll\", delta };\n}\n\n/** Wheel pan damping for a camera that pans on scroll */\nexport interface WheelPanOptions {\n /** Multiplier on the wheel's px; default 0.65 */\n speed: number;\n /** Linear up to this many px per event; default 30 */\n threshold: number;\n /** Hard cap per event, px; default 60 */\n max: number;\n}\n\nexport const WHEEL_PAN_DEFAULTS: Readonly<WheelPanOptions> = Object.freeze({ speed: 0.65, threshold: 30, max: 60 });\n\n/**\n * The camera pan for one wheel scroll, per axis: speed × delta, linear up to the threshold, then\n * square-root damped (threshold + √(4 × excess)) and capped, so a fast flick of a free-spinning wheel or an\n * accelerated trackpad doesn't throw the canvas away.\n */\nexport function dampWheelPan(delta: Point, opts: WheelPanOptions = WHEEL_PAN_DEFAULTS): Point {\n const one = (d: number) => {\n const v = finite(d) * opts.speed;\n const abs = Math.abs(v);\n if (abs <= opts.threshold) return v;\n return Math.sign(v) * Math.min(opts.threshold + Math.sqrt((abs - opts.threshold) * 4), opts.max);\n };\n return { x: one(delta.x), y: one(delta.y) };\n}\n\nfunction finite(v: number): number {\n return Number.isFinite(v) ? v : 0;\n}\n","/**\n * The gesture recogniser: raw pointer, wheel and Safari gesture events (plain\n * objects) in, one stream of InputGesture out. Mouse, pen and touch share one\n * path; two fingers become a pinch (5 % scale or 10 px pan decides which, read\n * once both fingers have moved, so a pan's leading finger is no pinch); a\n * press becomes a drag only past a per-kind threshold, so a modifier+click\n * (Cmd/Ctrl+click to multi-select) stays a click for the content.\n *\n * One press at a time: a second mouse or pen while one is down is ignored, and\n * a second finger cancels the one-finger press (\"multitouch\") and starts a\n * pinch. After a pinch, the fingers left on the glass start nothing until all\n * are lifted.\n *\n * No DOM, no clock: timestamps come with the events and the long-press timer\n * is injected as the timer half of a ClockPort (without timers there is no\n * long press). The stale-press drop works from event timestamps, so it needs\n * no timer.\n */\n\nimport type { ClockPort } from \"../clock/port\";\nimport type { Point } from \"../shapes\";\nimport { isHeld, platformButton, toModifiers } from \"./modifiers\";\nimport type {\n CancelReason,\n InputGesture,\n ModifierDrag,\n ModifierKey,\n Platform,\n PointerKind,\n RawInputEvent,\n RawPointerEvent,\n RawSafariGestureEvent,\n RawWheelEvent,\n SurfacePointer,\n} from \"./types\";\nimport { classifyWheel, WHEEL_DEFAULTS, wheelDeltaPx, type WheelOptions } from \"./wheel\";\n\n/** The timer half of a ClockPort: any clock (createBrowserClock, createFakeClock) is one. */\nexport type RecognizerTimers = Pick<ClockPort, \"setTimer\" | \"clearTimer\">;\n\nexport interface RecognizerOptions {\n /** Screen px (relative to the view) to canvas units, through the current camera */\n toCanvas: (screen: Point) => Point;\n /** For the long press: a clock; without timers no long press fires */\n timers?: RecognizerTimers;\n /** The drag modifier, resolved (see resolveDragModifier); null = off */\n dragModifier?: ModifierKey | null;\n /** \"mac\": a mouse Ctrl+press is the secondary button, as the system has it (platformButton); default \"other\" */\n platform?: Platform;\n /** Click vs drag: a press becomes a drag after travelling more than this many screen px */\n thresholds?: Partial<Record<PointerKind, number>>;\n longPress?: { ms?: number; kinds?: readonly PointerKind[] };\n /** Two clicks within ms and px of each other count as a double click */\n multiClick?: { ms?: number; px?: number };\n pinch?: { scaleThreshold?: number; panThreshold?: number };\n wheel?: Partial<WheelOptions>;\n /** A mouse or pen press, or a resting finger outside any gesture, untouched this long when another pointer goes down, lost its pointerup; default 1000 ms */\n staleMs?: number;\n}\n\nexport const RECOGNIZER_DEFAULTS = Object.freeze({\n thresholds: Object.freeze({ mouse: 4, pen: 6, touch: 8 }) as Readonly<Record<PointerKind, number>>,\n longPress: Object.freeze({ ms: 500, kinds: Object.freeze([\"touch\", \"pen\"]) as readonly PointerKind[] }),\n multiClick: Object.freeze({ ms: 300, px: 5 }),\n pinch: Object.freeze({ scaleThreshold: 0.05, panThreshold: 10 }),\n});\n\nexport interface Recognizer {\n feed(event: RawInputEvent): void;\n /** Cancel the press and the pinch in flight (Escape, a tool change, the view losing focus) */\n cancel(reason: CancelReason): void;\n /** cancel(\"reset\") and forget every pointer (e.g. after the window lost focus mid-touch) */\n reset(): void;\n /** The press in flight, if any */\n active(): { pointerId: number; kind: PointerKind; dragging: boolean } | null;\n pinching(): boolean;\n /** Change the drag modifier; applies from the next press */\n setDragModifier(key: ModifierKey | null): void;\n dispose(): void;\n}\n\ninterface Press {\n origin: SurfacePointer;\n last: SurfacePointer;\n dragging: boolean;\n longPressed: boolean;\n /** What a modifier-drag would do; decided at the press, confirmed (modifier still held) at dragStart */\n candidate: ModifierDrag | null;\n modifierDrag: ModifierDrag | null;\n timer: number | undefined;\n}\n\ninterface Touch {\n screen: Point;\n time: number;\n}\n\ninterface Pinch {\n ids: [number, number];\n startDist: number;\n startCenter: Point;\n lastDist: number;\n lastCenter: Point;\n kind: \"pinch\" | \"pan\" | null;\n /** While undecided: each finger's moves (position changes) since the last look at the kind */\n reported: Map<number, number>;\n}\n\n/**\n * While two fingers are undecided, the kind is looked at once both have moved as often as each other since the last\n * look: one finger's events arrive before the other's (one or a burst), and a pan's leading finger alone changes the\n * distance like a pinch. A finger at rest sends no moves, so a finger this many moves ahead of the other is looked at\n * on its own (a pinch around an anchored thumb).\n */\nconst SOLO_MOVES = 3;\n\nfunction kindOf(pointerType: string): PointerKind {\n return pointerType === \"touch\" || pointerType === \"pen\" ? pointerType : \"mouse\";\n}\n\nfunction dist(a: Point, b: Point): number {\n return Math.hypot(a.x - b.x, a.y - b.y);\n}\n\nfunction mid(a: Point, b: Point): Point {\n return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };\n}\n\nfunction finitePoint(p: Point): Point {\n return { x: Number.isFinite(p.x) ? p.x : 0, y: Number.isFinite(p.y) ? p.y : 0 };\n}\n\nexport function createRecognizer(options: RecognizerOptions, emit: (gesture: InputGesture) => void): Recognizer {\n const thresholds = { ...RECOGNIZER_DEFAULTS.thresholds, ...options.thresholds };\n const longPressMs = options.longPress?.ms ?? RECOGNIZER_DEFAULTS.longPress.ms;\n const longPressKinds = options.longPress?.kinds ?? RECOGNIZER_DEFAULTS.longPress.kinds;\n const multiClick = { ...RECOGNIZER_DEFAULTS.multiClick, ...options.multiClick };\n const pinchCfg = { ...RECOGNIZER_DEFAULTS.pinch, ...options.pinch };\n const wheelCfg: WheelOptions = { ...WHEEL_DEFAULTS, ...options.wheel };\n const timers = options.timers;\n const staleMs = options.staleMs ?? 1000;\n let dragModifier: ModifierKey | null = options.dragModifier ?? null;\n const platform: Platform = options.platform ?? \"other\";\n\n let press: Press | null = null;\n let pinch: Pinch | null = null;\n const touches = new Map<number, Touch>();\n /** After a pinch (or a reset mid-touch), fingers still down start nothing until all are up */\n let touchLocked = false;\n let lastClick: { time: number; screen: Point; button: number; count: number } | null = null;\n let safari: { last: number } | null = null;\n let lastTime = 0;\n let disposed = false;\n\n const pointerOf = (e: RawPointerEvent): SurfacePointer => {\n const screen = finitePoint(e.screen);\n return {\n screen,\n canvas: options.toCanvas(screen),\n kind: kindOf(e.pointerType),\n buttons: e.buttons,\n modifiers: toModifiers(e.modifiers),\n pointerId: e.pointerId,\n button: e.button,\n time: e.time,\n ...(e.pressure !== undefined ? { pressure: e.pressure } : {}),\n };\n };\n\n const clearTimer = (p: Press) => {\n if (p.timer !== undefined && timers) timers.clearTimer(p.timer);\n p.timer = undefined;\n };\n\n const endPress = (): Press | null => {\n const p = press;\n if (p) clearTimer(p);\n press = null;\n return p;\n };\n\n const cancelPress = (reason: CancelReason, pointer?: SurfacePointer) => {\n const p = endPress();\n if (!p) return;\n emit({ type: \"cancel\", pointer: pointer ?? p.last, origin: p.origin, reason, dragged: p.dragging });\n };\n\n const releasePress = (pointer: SurfacePointer) => {\n const p = endPress();\n if (!p) return;\n const click = !p.dragging && !p.longPressed;\n let clickCount = 0;\n if (click) {\n const prev = lastClick;\n const repeat =\n !!prev &&\n prev.button === p.origin.button &&\n p.origin.time - prev.time <= multiClick.ms &&\n dist(prev.screen, p.origin.screen) <= multiClick.px;\n clickCount = repeat ? prev!.count + 1 : 1;\n lastClick = { time: pointer.time, screen: p.origin.screen, button: p.origin.button, count: clickCount };\n } else {\n lastClick = null;\n }\n emit({ type: \"release\", pointer, origin: p.origin, click, clickCount, dragged: p.dragging });\n };\n\n const pinchState = (): { center: Point; d: number } | null => {\n if (!pinch) return null;\n const a = touches.get(pinch.ids[0]);\n const b = touches.get(pinch.ids[1]);\n if (!a || !b) return null;\n return { center: mid(a.screen, b.screen), d: Math.max(1, dist(a.screen, b.screen)) };\n };\n\n const pinchEvent = (\n phase: \"start\" | \"update\" | \"end\" | \"cancel\",\n center: Point,\n scale: number,\n pan: Point,\n time: number,\n modifiers: SurfacePointer[\"modifiers\"],\n source: \"touch\" | \"trackpad\",\n kind: \"pinch\" | \"pan\",\n ): InputGesture => ({\n type: \"pinch\",\n phase,\n source,\n kind,\n center: { screen: center, canvas: options.toCanvas(center) },\n scale,\n pan,\n modifiers,\n time,\n });\n\n const endPinch = (phase: \"end\" | \"cancel\", time: number, modifiers: SurfacePointer[\"modifiers\"]) => {\n const p = pinch;\n pinch = null;\n touchLocked = touches.size > 0;\n if (p && p.kind) emit(pinchEvent(phase, p.lastCenter, 1, { x: 0, y: 0 }, time, modifiers, \"touch\", p.kind));\n };\n\n /**\n * A pinch finger moved (`id`), or lifts (`lifting`: the last look, whatever moved). The kind is decided on both\n * fingers' latest positions (see SOLO_MOVES); the thresholds keep their meaning.\n */\n const updatePinch = (id: number, time: number, modifiers: SurfacePointer[\"modifiers\"], moved: boolean, lifting = false) => {\n const p = pinch;\n const now = pinchState();\n if (!p || !now) return;\n if (!p.kind) {\n if (!moved && !lifting) return; // a move that changed nothing (pressure only) is no move\n const moves = (p.reported.get(id) ?? 0) + (moved ? 1 : 0);\n p.reported.set(id, moves);\n const other = p.reported.get(p.ids[0] === id ? p.ids[1] : p.ids[0]) ?? 0;\n const even = moves > 0 && moves === other;\n if (!lifting && !even && Math.abs(moves - other) < SOLO_MOVES) return;\n p.reported.clear();\n const scaleChange = Math.abs(now.d / p.startDist - 1);\n const panDist = dist(now.center, p.startCenter);\n if (scaleChange > pinchCfg.scaleThreshold) p.kind = \"pinch\";\n else if (panDist > pinchCfg.panThreshold) p.kind = \"pan\";\n else return;\n // The first event carries everything since the second finger landed, so nothing is lost.\n const scale = p.kind === \"pinch\" ? now.d / p.startDist : 1;\n const pan = { x: now.center.x - p.startCenter.x, y: now.center.y - p.startCenter.y };\n p.lastDist = now.d;\n p.lastCenter = now.center;\n emit(pinchEvent(\"start\", now.center, scale, pan, time, modifiers, \"touch\", p.kind));\n return;\n }\n const scale = p.kind === \"pinch\" ? now.d / p.lastDist : 1;\n const pan = { x: now.center.x - p.lastCenter.x, y: now.center.y - p.lastCenter.y };\n p.lastDist = now.d;\n p.lastCenter = now.center;\n if (scale === 1 && pan.x === 0 && pan.y === 0) return;\n emit(pinchEvent(\"update\", now.center, scale, pan, time, modifiers, \"touch\", p.kind));\n };\n\n /**\n * A pointerup that never came (released outside the page, a pen lifted out of range, a lost event):\n * a press of the same pointer, or a mouse / pen press untouched for staleMs, is over. A finger that\n * lands again under its old id, or one resting untouched for staleMs outside any press or pinch, is gone.\n */\n const dropMissedUps = (e: RawPointerEvent, kind: PointerKind) => {\n const p = press;\n if (p) {\n const same = p.origin.pointerId === e.pointerId;\n const mixed = kind !== \"touch\" || p.origin.kind !== \"touch\"; // two fingers make a pinch, never a stale press\n if (same || (mixed && e.time - p.last.time > staleMs)) cancelPress(\"missed-up\");\n }\n if (kind !== \"touch\") return;\n if (touches.has(e.pointerId)) {\n if (pinch?.ids.includes(e.pointerId)) endPinch(\"cancel\", e.time, toModifiers(e.modifiers));\n touches.delete(e.pointerId);\n }\n for (const [id, t] of touches) {\n const busy = press?.origin.pointerId === id || !!pinch?.ids.includes(id);\n if (!busy && e.time - t.time > staleMs) touches.delete(id);\n }\n if (touches.size === 0) touchLocked = false;\n };\n\n const onDown = (e: RawPointerEvent) => {\n const down = pointerOf(e);\n // The press's button as the platform means it (macOS: Ctrl+click is a secondary click)\n const pointer: SurfacePointer = { ...down, button: platformButton(down, platform, dragModifier) };\n dropMissedUps(e, pointer.kind);\n if (pointer.kind === \"touch\") {\n touches.set(e.pointerId, { screen: pointer.screen, time: pointer.time });\n if (touchLocked || pinch) return; // a third finger, or fingers after a pinch\n if (touches.size === 2) {\n const ids = [...touches.keys()] as [number, number];\n // The second finger: whatever the first one started is cancelled, a pinch may begin.\n if (press && press.origin.kind === \"touch\") cancelPress(\"multitouch\");\n else if (press) {\n touches.delete(e.pointerId); // a mouse or pen press owns the input; ignore the finger\n return;\n }\n const a = touches.get(ids[0])!;\n const b = touches.get(ids[1])!;\n const d = Math.max(1, dist(a.screen, b.screen));\n const c = mid(a.screen, b.screen);\n pinch = { ids, startDist: d, startCenter: c, lastDist: d, lastCenter: c, kind: null, reported: new Map() };\n return;\n }\n if (touches.size > 2) return;\n }\n if (press || pinch) return; // one press at a time\n const candidate: ModifierDrag | null = isHeld(pointer.modifiers, dragModifier)\n ? pointer.button === 0\n ? \"move\"\n : pointer.button === 2\n ? \"resize\"\n : null\n : null;\n const p: Press = {\n origin: pointer,\n last: pointer,\n dragging: false,\n longPressed: false,\n candidate,\n modifierDrag: null,\n timer: undefined,\n };\n press = p;\n if (timers && longPressKinds.includes(pointer.kind)) {\n p.timer = timers.setTimer(() => {\n p.timer = undefined;\n if (press !== p || p.dragging || disposed) return;\n p.longPressed = true;\n emit({ type: \"longPress\", pointer: p.last, origin: p.origin });\n }, longPressMs);\n }\n emit({ type: \"press\", pointer, modifierDrag: candidate });\n };\n\n const onMove = (e: RawPointerEvent) => {\n const kind = kindOf(e.pointerType);\n if (kind === \"touch\") {\n const t = touches.get(e.pointerId);\n const to = finitePoint(e.screen);\n const moved = !t || t.screen.x !== to.x || t.screen.y !== to.y;\n if (t) {\n t.screen = to;\n t.time = e.time;\n }\n if (pinch && pinch.ids.includes(e.pointerId)) {\n updatePinch(e.pointerId, e.time, toModifiers(e.modifiers), moved);\n return;\n }\n }\n if (press && press.origin.pointerId === e.pointerId) {\n const pointer = pointerOf(e);\n // A mouse or pen moving with no button down and no pointerup before it: the release was missed.\n if (pointer.kind !== \"touch\" && e.buttons === 0) {\n releasePress(pointer);\n return;\n }\n const p = press;\n p.last = pointer;\n if (!p.dragging) {\n if (dist(pointer.screen, p.origin.screen) <= thresholds[p.origin.kind]) return;\n p.dragging = true;\n clearTimer(p);\n // The modifier must still be held when the drag starts: a released Cmd makes it a plain drag.\n p.modifierDrag = p.candidate && isHeld(pointer.modifiers, dragModifier) ? p.candidate : null;\n emit({ type: \"dragStart\", pointer, origin: p.origin, modifierDrag: p.modifierDrag });\n return;\n }\n emit({ type: \"move\", pointer, origin: p.origin });\n return;\n }\n if (!press && kind !== \"touch\" && e.buttons === 0) emit({ type: \"hover\", pointer: pointerOf(e) });\n };\n\n const onUp = (e: RawPointerEvent) => {\n if (kindOf(e.pointerType) === \"touch\") {\n // A finger lifting from an undecided pinch: its last position is looked at once, so a quick pinch still counts\n const t = touches.get(e.pointerId);\n if (t && pinch && !pinch.kind && pinch.ids.includes(e.pointerId)) {\n const to = finitePoint(e.screen);\n const moved = t.screen.x !== to.x || t.screen.y !== to.y;\n t.screen = to;\n updatePinch(e.pointerId, e.time, toModifiers(e.modifiers), moved, true);\n }\n touches.delete(e.pointerId);\n if (pinch && pinch.ids.includes(e.pointerId)) {\n endPinch(\"end\", e.time, toModifiers(e.modifiers));\n return;\n }\n if (touches.size === 0) touchLocked = false;\n }\n if (press && press.origin.pointerId === e.pointerId) releasePress(pointerOf(e));\n };\n\n const onCancel = (e: RawPointerEvent, reason: CancelReason) => {\n if (kindOf(e.pointerType) === \"touch\") {\n const tracked = touches.delete(e.pointerId);\n if (pinch && pinch.ids.includes(e.pointerId)) {\n if (tracked) endPinch(\"cancel\", e.time, toModifiers(e.modifiers));\n return;\n }\n if (touches.size === 0) touchLocked = false;\n }\n if (press && press.origin.pointerId === e.pointerId) cancelPress(reason, pointerOf(e));\n };\n\n const onWheel = (e: RawWheelEvent) => {\n const modifiers = toModifiers(e.modifiers);\n const screen = finitePoint(e.screen);\n const at = { screen, canvas: options.toCanvas(screen) };\n const intent = classifyWheel(wheelDeltaPx(e.deltaX, e.deltaY, e.deltaMode, wheelCfg), modifiers, wheelCfg);\n if (intent.kind === \"zoom\") {\n if (intent.factor !== 1) emit({ type: \"zoom\", factor: intent.factor, at, modifiers, time: e.time });\n return;\n }\n if (intent.delta.x === 0 && intent.delta.y === 0) return;\n emit({ type: \"scroll\", delta: intent.delta, at, modifiers, time: e.time });\n };\n\n const onSafari = (e: RawSafariGestureEvent) => {\n // iOS Safari fires gesture events for touch pinches too; the pointer path handles those.\n if (touches.size > 0 || pinch) return;\n const modifiers = toModifiers(e.modifiers);\n const screen = finitePoint(e.screen);\n const scaleNow = Number.isFinite(e.scale) && e.scale > 0 ? e.scale : 1;\n const none = { x: 0, y: 0 };\n if (e.type === \"gesturestart\") {\n safari = { last: scaleNow };\n emit(pinchEvent(\"start\", screen, 1, none, e.time, modifiers, \"trackpad\", \"pinch\"));\n return;\n }\n if (!safari) return;\n const scale = scaleNow / safari.last;\n safari.last = scaleNow;\n if (e.type === \"gesturechange\") {\n if (scale !== 1) emit(pinchEvent(\"update\", screen, scale, none, e.time, modifiers, \"trackpad\", \"pinch\"));\n return;\n }\n safari = null;\n emit(pinchEvent(\"end\", screen, scale, none, e.time, modifiers, \"trackpad\", \"pinch\"));\n };\n\n const cancelAll = (reason: CancelReason) => {\n cancelPress(reason);\n if (pinch) endPinch(\"cancel\", lastTime, toModifiers(undefined));\n if (safari) {\n safari = null;\n emit(pinchEvent(\"cancel\", { x: 0, y: 0 }, 1, { x: 0, y: 0 }, lastTime, toModifiers(undefined), \"trackpad\", \"pinch\"));\n }\n };\n\n return {\n feed(event) {\n if (disposed) return;\n if (Number.isFinite(event.time)) lastTime = event.time;\n switch (event.type) {\n case \"pointerdown\":\n return onDown(event);\n case \"pointermove\":\n return onMove(event);\n case \"pointerup\":\n return onUp(event);\n case \"pointercancel\":\n return onCancel(event, \"pointercancel\");\n case \"lostpointercapture\":\n // Also fires right after pointerup; by then the press is gone and this is a no-op. The content\n // losing its capture (the surface just took the pointer) is not the gesture's end.\n if (event.lostBy === \"content\") return;\n return onCancel(event, \"lostcapture\");\n case \"wheel\":\n return onWheel(event);\n case \"gesturestart\":\n case \"gesturechange\":\n case \"gestureend\":\n return onSafari(event);\n }\n },\n cancel: cancelAll,\n reset() {\n cancelAll(\"reset\");\n touches.clear();\n touchLocked = false;\n lastClick = null;\n },\n active() {\n return press ? { pointerId: press.origin.pointerId, kind: press.origin.kind, dragging: press.dragging } : null;\n },\n pinching() {\n return !!pinch?.kind || !!safari;\n },\n setDragModifier(key) {\n dragModifier = key;\n },\n dispose() {\n if (press) clearTimer(press);\n press = null;\n pinch = null;\n safari = null;\n touches.clear();\n disposed = true;\n },\n };\n}\n","import type { FrameSource } from \"../clock/port\";\n\n/** Options of {@link createFrameCoalescer}. */\nexport interface CoalescerOptions<T> {\n /**\n * How a new sample folds into the pending one. Default: keep the latest\n * (pointer positions). Wheel deltas want a sum instead.\n */\n readonly merge?: (pending: T, next: T) => T;\n}\n\n/**\n * Collapses a burst of samples into at most one delivery per frame. Same\n * shape as hypr-tiling's `createFrameCoalescer` (`schedule`, `cancel`),\n * plus `flush` and `pending`.\n */\nexport interface FrameCoalescer<T> {\n /** Record a sample and make sure a frame is requested. */\n schedule(sample: T): void;\n /**\n * Deliver the pending sample now, if there is one (before a pointer-up, so\n * the last move is seen first). Returns whether it delivered.\n */\n flush(): boolean;\n /** Drop the pending sample and the requested frame. Nothing is delivered after this. */\n cancel(): void;\n /** Whether a sample is waiting for its frame. */\n readonly pending: boolean;\n}\n\n/**\n * Deliver samples to `onFrame` once per frame of `frames`: the browser clock,\n * the fake clock, or a frame loop phase (`loop.frames(\"input\")`). `onFrame`\n * also gets how many samples were folded into this one.\n */\nexport function createFrameCoalescer<T>(\n onFrame: (sample: T, count: number) => void,\n frames: FrameSource,\n options: CoalescerOptions<T> = {},\n): FrameCoalescer<T> {\n const merge = options.merge;\n let handle: number | null = null;\n let sample: T | undefined;\n let count = 0;\n\n const deliver = (): void => {\n const delivered = sample as T;\n const folded = count;\n sample = undefined;\n count = 0;\n onFrame(delivered, folded);\n };\n\n const onFrameDue = (): void => {\n handle = null;\n if (count > 0) deliver();\n };\n\n return {\n schedule(next) {\n sample = count > 0 && merge !== undefined ? merge(sample as T, next) : next;\n count++;\n if (handle === null) handle = frames.requestFrame(onFrameDue);\n },\n flush() {\n if (handle !== null) {\n frames.cancelFrame(handle);\n handle = null;\n }\n if (count === 0) return false;\n deliver();\n return true;\n },\n cancel() {\n if (handle !== null) {\n frames.cancelFrame(handle);\n handle = null;\n }\n sample = undefined;\n count = 0;\n },\n get pending() {\n return count > 0;\n },\n };\n}\n","import type { TimerSource } from \"../clock/port\";\nimport { signal } from \"../signals/signals\";\n\n/**\n * A timer event a state asks for: send `event(t)` when the clock reaches\n * `at`. Machines stay pure; this is how a long-press or a dwell turns into\n * an event without the reducer touching a timer.\n */\nexport interface Deadline<E> {\n /** Clock time in ms. */\n readonly at: number;\n /** The event to send, given the clock time it fired at. */\n readonly event: (t: number) => E;\n}\n\n/** Options of {@link createMachine}. */\nexport interface MachineOptions<S, E> {\n /** The pure transition function. Returning the same state means \"no change\". */\n readonly reducer: (state: S, event: E) => S;\n readonly initial: S;\n /** Timers come from here (and `now()` for their events). */\n readonly clock: TimerSource;\n /** The timer event the state needs, or `null`. Checked after every transition. */\n readonly deadline?: (state: S) => Deadline<E> | null;\n}\n\n/** A running machine: the state, a way to send events, and change listeners. */\nexport interface Machine<S, E> {\n /** The current state; reactive (read it in a computed or an effect). */\n readonly state: S;\n /** The current state, untracked. */\n peek(): S;\n /**\n * Run `event` through the reducer and return the new state. An event sent\n * from a listener waits until the current one has been handled.\n */\n send(event: E): S;\n /** Called after each change of state (not for events that changed nothing). */\n subscribe(listener: (next: S, previous: S, event: E) => void): () => void;\n /** Clear the timer and drop listeners. Events sent after this are ignored. */\n dispose(): void;\n}\n\n/** Run a pure machine on a clock: it arms and clears the deadline timer for you. */\nexport function createMachine<S, E>(options: MachineOptions<S, E>): Machine<S, E> {\n const { reducer, clock, deadline } = options;\n const state = signal(options.initial);\n const listeners = new Set<(next: S, previous: S, event: E) => void>();\n const inbox: E[] = [];\n let dispatching = false;\n let disposed = false;\n let timer: number | null = null;\n let timerAt: number | null = null;\n let timerEvent: ((t: number) => E) | null = null;\n\n const clearTimer = (): void => {\n if (timer !== null) clock.clearTimer(timer);\n timer = null;\n timerAt = null;\n timerEvent = null;\n };\n\n const arm = (): void => {\n const due = deadline === undefined ? null : deadline(state.peek());\n if (due === null) {\n clearTimer();\n return;\n }\n timerEvent = due.event;\n if (timer !== null && timerAt === due.at) return;\n if (timer !== null) clock.clearTimer(timer);\n timerAt = due.at;\n timer = clock.setTimer(() => {\n const make = timerEvent;\n timer = null;\n timerAt = null;\n timerEvent = null;\n if (make !== null) machine.send(make(clock.now()));\n }, Math.max(0, due.at - clock.now()));\n };\n\n const machine: Machine<S, E> = {\n get state() {\n return state.value;\n },\n peek: () => state.peek(),\n send(event) {\n if (disposed) return state.peek();\n inbox.push(event);\n if (dispatching) return state.peek();\n dispatching = true;\n try {\n let next: E | undefined;\n while ((next = inbox.shift()) !== undefined) {\n const previous = state.peek();\n const after = reducer(previous, next);\n if (after === previous) continue;\n state.value = after;\n arm();\n for (const listener of Array.from(listeners)) listener(after, previous, next);\n if (disposed) break;\n }\n } finally {\n dispatching = false;\n inbox.length = 0;\n }\n return state.peek();\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n clearTimer();\n listeners.clear();\n },\n };\n arm();\n return machine;\n}\n","import type { TimerSource } from \"../clock/port\";\nimport { createMachine, type Deadline, type Machine } from \"./machine\";\n\n/**\n * The dwell machine (spring-load): hold over the same target long enough and\n * it fires, once.\n *\n * ```text\n * idle ──hover(key)──► dwelling ──tick (t − since ≥ dwellMs)──► fired\n * ▲ │ hover(other key) restarts `since` │\n * │ │ hover(null) ─► idle │\n * └────────┴─────────────── reset ◄─────────────────────┘\n * ```\n *\n * `fired` holds until `reset`: neither a late tick nor a hover change can fire\n * it again or clear it while the host acts on it. Shapes come from\n * hypr-tiling's `springLoadReducer`, with its workspace tab generalised to\n * any `key` (the host passes `null` for anything that shouldn't dwell, such as\n * the active tab).\n */\n\n/** Tunables of the dwell machine. */\nexport interface DwellConfig {\n /** How long in ms the pointer must stay over the same key. Default 500. */\n readonly dwellMs: number;\n}\n\n/** Defaults of {@link DwellConfig}. */\nexport const DWELL_DEFAULTS: DwellConfig = { dwellMs: 500 };\n\n/** The dwell machine's state. `K` is the target key, `P` the payload carried into `fired`. */\nexport type DwellState<K = string, P = unknown> =\n | { readonly phase: \"idle\" }\n | { readonly phase: \"dwelling\"; readonly key: K; readonly since: number; readonly payload: P | undefined }\n | {\n readonly phase: \"fired\";\n readonly key: K;\n readonly since: number;\n readonly firedAt: number;\n readonly payload: P | undefined;\n };\n\n/** Events of the dwell machine. `t` is the clock time in ms. */\nexport type DwellInput<K = string, P = unknown> =\n | { readonly type: \"hover\"; readonly t: number; readonly key: K | null; readonly payload?: P }\n | { readonly type: \"tick\"; readonly t: number }\n | { readonly type: \"reset\" };\n\n/** The idle state (the same object every time). */\nexport const DWELL_IDLE: DwellState<never, never> = { phase: \"idle\" };\n\n/** The dwell machine's pure transition function. */\nexport function dwellReducer<K, P>(\n state: DwellState<K, P>,\n event: DwellInput<K, P>,\n config: DwellConfig = DWELL_DEFAULTS,\n): DwellState<K, P> {\n switch (event.type) {\n case \"reset\":\n return state.phase === \"idle\" ? state : DWELL_IDLE;\n case \"hover\":\n if (state.phase === \"fired\") return state;\n if (event.key === null) return state.phase === \"idle\" ? state : DWELL_IDLE;\n if (state.phase === \"dwelling\" && Object.is(state.key, event.key)) return state;\n return { phase: \"dwelling\", key: event.key, since: event.t, payload: event.payload };\n case \"tick\":\n if (state.phase !== \"dwelling\" || event.t - state.since < config.dwellMs) return state;\n return { phase: \"fired\", key: state.key, since: state.since, firedAt: event.t, payload: state.payload };\n default:\n return state;\n }\n}\n\n/** When a dwelling state fires, as a timer event; `null` outside `dwelling`. */\nexport function dwellDeadline<K, P>(\n state: DwellState<K, P>,\n config: DwellConfig = DWELL_DEFAULTS,\n): Deadline<DwellInput<K, P>> | null {\n if (state.phase !== \"dwelling\") return null;\n return { at: state.since + config.dwellMs, event: (t) => ({ type: \"tick\", t }) };\n}\n\n/** Options of {@link createDwellMachine}. */\nexport interface DwellMachineOptions {\n readonly clock: TimerSource;\n readonly config?: Partial<DwellConfig>;\n}\n\n/** A dwell machine on a clock: the dwell timer is armed for you. */\nexport function createDwellMachine<K = string, P = unknown>(\n options: DwellMachineOptions,\n): Machine<DwellState<K, P>, DwellInput<K, P>> {\n const config: DwellConfig = { ...DWELL_DEFAULTS, ...options.config };\n return createMachine<DwellState<K, P>, DwellInput<K, P>>({\n reducer: (state, event) => dwellReducer(state, event, config),\n initial: DWELL_IDLE,\n clock: options.clock,\n deadline: (state) => dwellDeadline(state, config),\n });\n}\n","import type { TimerSource } from \"../clock/port\";\nimport type { Point } from \"../shapes\";\nimport { createMachine, type Deadline, type Machine } from \"./machine\";\n\n/**\n * A touch press on something draggable that sits in a scrolling page: is it a drag, or a scroll?\n *\n * ```text\n * idle ──press──► pressed ──move: mostly along the scroll direction, ≥ scrollEscape──► scrolled (never lifts)\n * │ ──move: mostly across it, ≥ lift px────────────────────────────► lifted\n * │ ──tick: held still holdMs─────────────────────────────────────► lifted (where it was pressed)\n * └ ──move: along it short of the escape, or across it below lift px: stays pressed\n * any ──release──► idle\n * ```\n *\n * Before the hold has elapsed, a finger that travels mostly along the page's scroll direction is a scroll: the touch\n * goes back to the page and stays a scroll until it is released; short of the escape it waits, never lifting.\n * Travel past the lift distance mostly across the scroll direction is a deliberate drag. Once the hold has elapsed\n * the press lifts where it is. `scrolled` and `lifted`\n * hold until `release`.\n */\n\n/** Tunables of the touch press. */\nexport interface TouchPressConfig {\n /** px of travel that lifts a press that is not a scroll. Default 6. */\n readonly lift: number;\n /** px of travel along the scroll direction that makes the press a scroll. Default 10. */\n readonly scrollEscape: number;\n /** The page's scroll direction: `\"y\"` (a vertical page, the default) or `\"x\"`. */\n readonly scroll: \"x\" | \"y\";\n /** ms a still finger holds before the press lifts where it is. Default 220. */\n readonly holdMs: number;\n}\n\n/** Defaults of {@link TouchPressConfig}. */\nexport const TOUCH_PRESS_DEFAULTS: TouchPressConfig = { lift: 6, scrollEscape: 10, scroll: \"y\", holdMs: 220 };\n\n/** The touch press's state. `from` is where the finger went down, `at` where it lifted. */\nexport type TouchPressState =\n | { readonly phase: \"idle\" }\n | { readonly phase: \"pressed\"; readonly from: Point; readonly since: number }\n | { readonly phase: \"lifted\"; readonly from: Point; readonly at: Point }\n | { readonly phase: \"scrolled\"; readonly from: Point };\n\n/** Events of the touch press. `t` is the clock time in ms. */\nexport type TouchPressInput =\n | { readonly type: \"press\"; readonly t: number; readonly point: Point }\n | { readonly type: \"move\"; readonly point: Point }\n | { readonly type: \"tick\"; readonly t: number }\n | { readonly type: \"release\" };\n\n/** The idle state (the same object every time). */\nexport const TOUCH_PRESS_IDLE: TouchPressState = { phase: \"idle\" };\n\n/** The touch press's pure transition function. */\nexport function touchPressReducer(state: TouchPressState, event: TouchPressInput, config: TouchPressConfig = TOUCH_PRESS_DEFAULTS): TouchPressState {\n switch (event.type) {\n case \"release\":\n return state.phase === \"idle\" ? state : TOUCH_PRESS_IDLE;\n case \"press\":\n return state.phase === \"idle\" ? { phase: \"pressed\", from: { x: event.point.x, y: event.point.y }, since: event.t } : state;\n case \"tick\":\n if (state.phase !== \"pressed\" || event.t - state.since < config.holdMs) return state;\n return { phase: \"lifted\", from: state.from, at: state.from };\n case \"move\": {\n if (state.phase !== \"pressed\") return state;\n const dx = event.point.x - state.from.x;\n const dy = event.point.y - state.from.y;\n const along = Math.abs(config.scroll === \"y\" ? dy : dx);\n const across = Math.abs(config.scroll === \"y\" ? dx : dy);\n // Mostly along the scroll direction is never a lift: a scroll once it reaches the escape, until then waiting\n // (a finger scroll is reported a few px at a time, and would pass the lift distance first).\n if (along > across) return along >= config.scrollEscape ? { phase: \"scrolled\", from: state.from } : state;\n if (Math.hypot(dx, dy) >= config.lift) return { phase: \"lifted\", from: state.from, at: { x: event.point.x, y: event.point.y } };\n return state;\n }\n default:\n return state;\n }\n}\n\n/** When a pressed state lifts by holding, as a timer event; `null` outside `pressed`. */\nexport function touchPressDeadline(state: TouchPressState, config: TouchPressConfig = TOUCH_PRESS_DEFAULTS): Deadline<TouchPressInput> | null {\n if (state.phase !== \"pressed\") return null;\n return { at: state.since + config.holdMs, event: (t) => ({ type: \"tick\", t }) };\n}\n\n/** Options of {@link createTouchPress}. */\nexport interface TouchPressOptions {\n readonly clock: TimerSource;\n readonly config?: Partial<TouchPressConfig>;\n}\n\n/** A touch press on a clock: the hold timer is armed for you. */\nexport function createTouchPress(options: TouchPressOptions): Machine<TouchPressState, TouchPressInput> {\n const config: TouchPressConfig = { ...TOUCH_PRESS_DEFAULTS, ...options.config };\n return createMachine<TouchPressState, TouchPressInput>({\n reducer: (state, event) => touchPressReducer(state, event, config),\n initial: TOUCH_PRESS_IDLE,\n clock: options.clock,\n deadline: (state) => touchPressDeadline(state, config),\n });\n}\n","import type { TimerSource } from \"../clock/port\";\nimport { createMachine, type Deadline, type Machine } from \"./machine\";\nimport type { ModifierKey, Modifiers } from \"./types\";\n\n/**\n * A paged swipe: a trackpad's two-finger scroll or a finger swipes from one page to the previous or the next, the\n * pages following it, and commits or snaps back when it ends.\n *\n * ```text\n * idle ──wheel / touch-move (arming rules pass)──► armed ──travel ≥ thresholdPx──► tracking\n * ▲ │ │\n * │ rule breach / idle / end gesture end\n * │ ▼ ▼\n * └──────── lockoutMs elapsed ◄── lockout ◄── settled (commit) ◄───────────── settling\n * └──────────────────────────────────────── settled (cancel) ◄───────────────────┘\n * ```\n *\n * - **Arming:** nothing else owns the pointer (`blocked`), the movement runs mostly along the swipe (more than twice\n * the cross movement), no Ctrl (a pinch), the configured modifier is held, nothing under the pointer can still\n * scroll that way (`canScrollFurther`), and there is a page on that side (or `wrap`). A wheel run that breaks a\n * rule belongs to scrolling until the wheel has been idle `idleMs`.\n * - **Tracking:** `progress` is the travel over the page size, −1 to 1, held at 0 towards a side with no page.\n * - **Ending:** a touch ends when the finger lifts, a wheel run after `idleMs` without samples. It commits when\n * `|progress| ≥ commitFraction`, or the peak speed towards the target reached `commitVelocity` (the peak starts\n * over when the travel crosses zero, so a fast fling out and a pull back does not commit the way back).\n * - **Settling** lasts as long as the host's page transition; the host sends `settled` when it is done (or `cancel`,\n * which goes straight back to idle, when it stops the transition). After a commit, `lockout` swallows the\n * trackpad's momentum for `lockoutMs`, and the run stays claimed after it.\n *\n * Pure and timer-free: {@link pagedSwipeDeadline} says when the next `tick` is due ({@link createPagedSwipe} arms it).\n * Signs: a wheel delta > 0 along the swipe heads to `next`; a finger moving towards smaller x (or y) heads to `next`.\n */\n\n/** Which side of the current page a swipe heads to. */\nexport type SwipeSide = \"prev\" | \"next\";\n\n/** Tunables of the paged swipe. */\nexport interface PagedSwipeConfig {\n /** px of travel before an armed swipe tracks. Default 24. */\n readonly thresholdPx: number;\n /** The share of the page size that commits at the end. Default 0.35. */\n readonly commitFraction: number;\n /** px/ms: a peak speed towards the target that commits at the end. Default 0.6. */\n readonly commitVelocity: number;\n /** ms without a wheel sample that ends a wheel run. Default 120. */\n readonly idleMs: number;\n /** ms after a commit during which wheel samples are swallowed (momentum). Default 350. */\n readonly lockoutMs: number;\n /** Past the last page comes the first, and back. Default false. */\n readonly wrap: boolean;\n /** The page size in px when no sample or context gives one. Default 800. */\n readonly size: number;\n /** A modifier the wheel needs held to swipe (`null`: none). Touch never needs one. Default null. */\n readonly modifier: ModifierKey | null;\n /** The swipe's direction: `\"x\"` (the default) or `\"y\"`. */\n readonly along: \"x\" | \"y\";\n}\n\n/** Defaults of {@link PagedSwipeConfig}. */\nexport const PAGED_SWIPE_DEFAULTS: PagedSwipeConfig = {\n thresholdPx: 24,\n commitFraction: 0.35,\n commitVelocity: 0.6,\n idleMs: 120,\n lockoutMs: 350,\n wrap: false,\n size: 800,\n modifier: null,\n along: \"x\",\n};\n\n/** What the host knows about the pages now. */\nexport interface PagedSwipeContext {\n readonly hasPrev: boolean;\n readonly hasNext: boolean;\n /** The page size in px, or null (the config's). */\n readonly size: number | null;\n /** Something else owns the pointer (a drag): nothing arms, and a swipe in progress gives way. */\n readonly blocked: boolean;\n}\n\n/** The context before the host has said anything. */\nexport const PAGED_SWIPE_EMPTY_CONTEXT: PagedSwipeContext = { hasPrev: false, hasNext: false, size: null, blocked: false };\n\ntype Input = \"wheel\" | \"touch\";\ninterface Base {\n readonly context: PagedSwipeContext;\n /** −1 to 1; negative towards `prev`. */\n readonly progress: number;\n readonly target: SwipeSide | null;\n}\n\n/** The paged swipe's state. Every phase has a way back to `idle`. */\nexport type PagedSwipeState =\n | (Base & {\n readonly phase: \"idle\";\n /** The last wheel sample of the current run (null: none, or the run went idle). */\n readonly lastWheel: number | null;\n /** This wheel run broke the arming rules (or committed): it cannot arm until the wheel is idle `idleMs`. */\n readonly claimed: boolean;\n })\n | (Base & {\n readonly phase: \"armed\";\n readonly input: Input;\n /** Where the finger went down (touch). */\n readonly origin: { readonly x: number; readonly y: number };\n /** Wheel travel along the swipe and across it so far. */\n readonly travel: number;\n readonly cross: number;\n readonly last: number;\n })\n | (Base & {\n readonly phase: \"tracking\";\n readonly input: Input;\n readonly origin: { readonly x: number; readonly y: number };\n readonly travel: number;\n readonly last: number;\n /** The highest speed (px/ms) towards the current target. */\n readonly peak: number;\n })\n | (Base & { readonly phase: \"settling\"; readonly input: Input; readonly outcome: \"commit\" | \"cancel\"; readonly ended: number; readonly lastWheel: number })\n | (Base & { readonly phase: \"lockout\"; readonly until: number; readonly lastWheel: number });\n\n/** Events of the paged swipe. `t` is the clock time in ms. */\nexport type PagedSwipeInput =\n | {\n readonly type: \"wheel\";\n readonly dx: number;\n readonly dy: number;\n readonly t: number;\n readonly modifiers: Modifiers;\n /** Something under the pointer can still scroll that way: no arming. */\n readonly canScrollFurther?: boolean;\n readonly size?: number;\n }\n | { readonly type: \"touch-start\"; readonly x: number; readonly y: number; readonly t: number; readonly size?: number }\n | { readonly type: \"touch-move\"; readonly x: number; readonly y: number; readonly t: number; readonly canScrollFurther?: boolean; readonly size?: number }\n | { readonly type: \"touch-end\"; readonly t: number }\n | { readonly type: \"tick\"; readonly t: number }\n | { readonly type: \"settled\" }\n | { readonly type: \"cancel\" }\n | { readonly type: \"block\"; readonly on: boolean }\n | { readonly type: \"context\"; readonly hasPrev: boolean; readonly hasNext: boolean; readonly size?: number };\n\n/** The idle state with no context. */\nexport const PAGED_SWIPE_IDLE: PagedSwipeState = { phase: \"idle\", context: PAGED_SWIPE_EMPTY_CONTEXT, progress: 0, target: null, lastWheel: null, claimed: false };\n\nconst idle = (context: PagedSwipeContext, lastWheel: number | null = null, claimed = false): PagedSwipeState => ({ phase: \"idle\", context, progress: 0, target: null, lastWheel, claimed });\nconst sideOf = (travel: number): SwipeSide | null => (travel > 0 ? \"next\" : travel < 0 ? \"prev\" : null);\nconst hasPage = (c: PagedSwipeContext, side: SwipeSide, wrap: boolean): boolean => (wrap ? c.hasPrev || c.hasNext : side === \"next\" ? c.hasNext : c.hasPrev);\nconst withSize = (c: PagedSwipeContext, size: number | undefined): PagedSwipeContext => (size === undefined || size === c.size ? c : { ...c, size });\nconst sizeOf = (c: PagedSwipeContext, size: number | undefined, config: PagedSwipeConfig): number => size ?? c.size ?? config.size;\n\n/** Where the swipe may head for a movement (`main` along the swipe, `cross` across it), or null when it must not arm. */\nfunction arming(c: PagedSwipeContext, main: number, cross: number, config: PagedSwipeConfig, gates: { ctrl: boolean; modifierHeld: boolean; canScrollFurther: boolean }): SwipeSide | null {\n if (c.blocked || gates.ctrl || gates.canScrollFurther || !gates.modifierHeld) return null;\n if (Math.abs(main) <= 2 * Math.abs(cross)) return null;\n const side = sideOf(main);\n return side && hasPage(c, side, config.wrap) ? side : null;\n}\n\n/** Travel to progress: over the page size, −1 to 1, 0 towards a side with no page (unless `wrap`). */\nexport function swipeProgress(travel: number, size: number, c: PagedSwipeContext, wrap: boolean): number {\n const p = Math.max(-1, Math.min(1, travel / Math.max(1, size)));\n if (wrap) return p;\n if ((p < 0 && !c.hasPrev) || (p > 0 && !c.hasNext)) return 0;\n return p;\n}\n\n/** Speed towards `side` for a step of `delta` px over `dt` ms; 0 moving away. */\nconst towards = (delta: number, dt: number, side: SwipeSide | null): number => {\n if (dt <= 0 || side === null) return 0;\n const v = (side === \"next\" ? delta : -delta) / dt;\n return v > 0 ? v : 0;\n};\n\nfunction track(c: PagedSwipeContext, input: Input, origin: { x: number; y: number }, travel: number, t: number, peak: number, size: number, config: PagedSwipeConfig): PagedSwipeState {\n const progress = swipeProgress(travel, size, c, config.wrap);\n return { phase: \"tracking\", context: c, input, origin, travel, last: t, peak, progress, target: sideOf(progress) };\n}\n\nfunction end(s: Extract<PagedSwipeState, { phase: \"tracking\" }>, t: number, config: PagedSwipeConfig): PagedSwipeState {\n const commit = s.target !== null && (Math.abs(s.progress) >= config.commitFraction || s.peak >= config.commitVelocity);\n return { phase: \"settling\", context: s.context, input: s.input, outcome: commit ? \"commit\" : \"cancel\", ended: t, lastWheel: t, progress: s.progress, target: commit ? s.target : null };\n}\n\nconst giveUp = (s: Extract<PagedSwipeState, { phase: \"tracking\" }>, c: PagedSwipeContext): PagedSwipeState => ({\n phase: \"settling\",\n context: c,\n input: s.input,\n outcome: \"cancel\",\n ended: s.last,\n lastWheel: s.last,\n progress: s.progress,\n target: null,\n});\n\n/** The paged swipe's pure transition function. Unmatched pairs return the state unchanged. */\nexport function pagedSwipeReducer(state: PagedSwipeState, event: PagedSwipeInput, config: PagedSwipeConfig = PAGED_SWIPE_DEFAULTS): PagedSwipeState {\n const y = config.along === \"y\";\n if (event.type === \"context\") {\n return { ...state, context: { ...state.context, hasPrev: event.hasPrev, hasNext: event.hasNext, size: event.size ?? state.context.size } };\n }\n if (event.type === \"block\") {\n const c = state.context.blocked === event.on ? state.context : { ...state.context, blocked: event.on };\n if (event.on && state.phase === \"armed\") return idle(c);\n if (event.on && state.phase === \"tracking\") return giveUp(state, c);\n return c === state.context ? state : { ...state, context: c };\n }\n if (event.type === \"cancel\") {\n // Settling too: a host that will never send `settled` (it went away mid-transition) must not leave it stuck.\n if (state.phase === \"armed\" || state.phase === \"lockout\" || state.phase === \"settling\") return idle(state.context);\n if (state.phase === \"tracking\") return giveUp(state, state.context);\n return state;\n }\n switch (state.phase) {\n case \"idle\": {\n if (event.type === \"wheel\") {\n if (state.context.blocked) return state;\n const fresh = state.lastWheel === null || event.t - state.lastWheel >= config.idleMs;\n if (!fresh && state.claimed) return { ...state, lastWheel: event.t };\n const main = y ? event.dy : event.dx;\n const cross = y ? event.dx : event.dy;\n const side = arming(state.context, main, cross, config, wheelGates(event, config));\n const c = withSize(state.context, event.size);\n if (side === null) return idle(c, event.t, true);\n if (Math.abs(main) >= config.thresholdPx) return track(c, \"wheel\", { x: 0, y: 0 }, main, event.t, 0, sizeOf(c, event.size, config), config);\n return { phase: \"armed\", context: c, input: \"wheel\", origin: { x: 0, y: 0 }, travel: main, cross, last: event.t, progress: 0, target: null };\n }\n if (event.type === \"touch-start\") {\n if (state.context.blocked) return state;\n return { phase: \"armed\", context: withSize(state.context, event.size), input: \"touch\", origin: { x: event.x, y: event.y }, travel: 0, cross: 0, last: event.t, progress: 0, target: null };\n }\n return state;\n }\n case \"armed\": {\n if (event.type === \"wheel\") {\n if (state.input !== \"wheel\") return state;\n const main = y ? event.dy : event.dx;\n const cross = y ? event.dx : event.dy;\n // Every sample before the threshold must pass the rules too; a breach gives the run to scrolling.\n if (arming(state.context, main, cross, config, wheelGates(event, config)) === null) return idle(state.context, event.t, true);\n const c = withSize(state.context, event.size);\n const travel = state.travel + main;\n const crossTravel = state.cross + cross;\n // The run as a whole must stay along the swipe.\n if (Math.abs(crossTravel) > Math.abs(travel) / 2) return idle(c, event.t, true);\n if (Math.abs(travel) < config.thresholdPx) return { ...state, context: c, travel, cross: crossTravel, last: event.t };\n return track(c, \"wheel\", state.origin, travel, event.t, towards(main, event.t - state.last, sideOf(travel)), sizeOf(c, event.size, config), config);\n }\n if (event.type === \"touch-move\") {\n if (state.input !== \"touch\") return state;\n const main = y ? state.origin.y - event.y : state.origin.x - event.x;\n const cross = y ? event.x - state.origin.x : event.y - state.origin.y;\n // Finger jitter.\n if (Math.hypot(main, cross) < config.thresholdPx) return { ...state, last: event.t };\n const side = arming(state.context, main, cross, config, { ctrl: false, modifierHeld: true, canScrollFurther: event.canScrollFurther ?? false });\n // Across the swipe, or nowhere to go: the page owns this touch until it ends.\n if (side === null) return idle(state.context);\n const c = withSize(state.context, event.size);\n return track(c, \"touch\", state.origin, main, event.t, towards(main, event.t - state.last, side), sizeOf(c, event.size, config), config);\n }\n if (event.type === \"tick\") return state.input === \"wheel\" && event.t - state.last >= config.idleMs ? idle(state.context) : state;\n if (event.type === \"touch-end\") return state.input === \"touch\" ? idle(state.context) : state;\n if (event.type === \"touch-start\") return { ...state, context: withSize(state.context, event.size), input: \"touch\", origin: { x: event.x, y: event.y }, travel: 0, cross: 0, last: event.t };\n return state;\n }\n case \"tracking\": {\n if (event.type === \"wheel\" || event.type === \"touch-move\") {\n if (state.input !== (event.type === \"wheel\" ? \"wheel\" : \"touch\")) return state;\n const travel =\n event.type === \"wheel\" ? state.travel + (y ? event.dy : event.dx) : y ? state.origin.y - event.y : state.origin.x - event.x;\n const side = sideOf(travel);\n const v = towards(travel - state.travel, event.t - state.last, side);\n const peak = side === sideOf(state.travel) ? Math.max(state.peak, v) : v;\n const c = withSize(state.context, event.size);\n return track(c, state.input, state.origin, travel, event.t, peak, sizeOf(c, event.size, config), config);\n }\n if (event.type === \"tick\") return state.input === \"wheel\" && event.t - state.last >= config.idleMs ? end(state, event.t, config) : state;\n if (event.type === \"touch-end\") return state.input === \"touch\" ? end(state, event.t, config) : state;\n return state;\n }\n case \"settling\": {\n if (event.type === \"settled\") {\n if (state.outcome === \"commit\") return { phase: \"lockout\", context: state.context, until: state.ended + config.lockoutMs, lastWheel: state.lastWheel, progress: 0, target: null };\n return idle(state.context, state.lastWheel, false);\n }\n if (event.type === \"wheel\") return { ...state, lastWheel: event.t };\n return state;\n }\n case \"lockout\": {\n if (event.type === \"wheel\") return event.t >= state.until ? idle(state.context, event.t, true) : { ...state, lastWheel: event.t };\n if (event.type === \"tick\") {\n if (event.t < state.until) return state;\n return event.t - state.lastWheel < config.idleMs ? idle(state.context, state.lastWheel, true) : idle(state.context);\n }\n // A finger is not momentum: it may start a swipe at once.\n if (event.type === \"touch-start\") return pagedSwipeReducer(idle(state.context), event, config);\n return state;\n }\n default:\n return state;\n }\n}\n\nfunction wheelGates(event: Extract<PagedSwipeInput, { type: \"wheel\" }>, config: PagedSwipeConfig): { ctrl: boolean; modifierHeld: boolean; canScrollFurther: boolean } {\n return { ctrl: event.modifiers.ctrl, modifierHeld: config.modifier === null || event.modifiers[config.modifier], canScrollFurther: event.canScrollFurther ?? false };\n}\n\n/** When the next `tick` is due: a wheel run's idle end, or the end of the lockout; `null` when none is. */\nexport function pagedSwipeDeadline(state: PagedSwipeState, config: PagedSwipeConfig = PAGED_SWIPE_DEFAULTS): Deadline<PagedSwipeInput> | null {\n const tick = (t: number): PagedSwipeInput => ({ type: \"tick\", t });\n if ((state.phase === \"armed\" || state.phase === \"tracking\") && state.input === \"wheel\") return { at: state.last + config.idleMs, event: tick };\n if (state.phase === \"lockout\") return { at: state.until, event: tick };\n return null;\n}\n\n/** Options of {@link createPagedSwipe}. */\nexport interface PagedSwipeOptions {\n readonly clock: TimerSource;\n readonly config?: Partial<PagedSwipeConfig>;\n}\n\n/** A paged swipe on a clock: the idle and lockout timers are armed for you. */\nexport function createPagedSwipe(options: PagedSwipeOptions): Machine<PagedSwipeState, PagedSwipeInput> {\n const config: PagedSwipeConfig = { ...PAGED_SWIPE_DEFAULTS, ...options.config };\n return createMachine<PagedSwipeState, PagedSwipeInput>({\n reducer: (state, event) => pagedSwipeReducer(state, event, config),\n initial: PAGED_SWIPE_IDLE,\n clock: options.clock,\n deadline: (state) => pagedSwipeDeadline(state, config),\n });\n}\n"],"mappings":"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","names":["scale","pan"]}
1
+ {"version":3,"sources":["../src/gestures/types.ts","../src/gestures/modifiers.ts","../src/gestures/wheel.ts","../src/gestures/recognizer.ts","../src/gestures/coalescer.ts","../src/gestures/machine.ts","../src/gestures/dwell.ts","../src/gestures/touch-press.ts","../src/gestures/paged-swipe.ts"],"sourcesContent":["/**\n * Input types: plain event objects in, one gesture stream out. Nothing here\n * touches the DOM; the host copies the fields it needs out of PointerEvent /\n * WheelEvent / Safari's GestureEvent.\n *\n * Ported from a host app's input layer, whose recogniser this is.\n * Hit testing and routing (what is under a point, who gets a gesture) stay\n * with the host.\n */\n\nimport type { Point } from \"../shapes\";\n\nexport type PointerKind = \"mouse\" | \"pen\" | \"touch\";\n\n/** Pointer modifiers, ctrl included (the drag modifier may be \"ctrl\") */\nexport interface Modifiers {\n shift: boolean;\n alt: boolean;\n meta: boolean;\n ctrl: boolean;\n}\n\nexport type ModifierKey = keyof Modifiers;\n\n/** \"mac\": the drag modifier defaults to Cmd; \"other\": Alt (the OS keeps the Win/Super key) */\nexport type Platform = \"mac\" | \"other\";\n\nexport const NO_MODIFIERS: Readonly<Modifiers> = Object.freeze({ shift: false, alt: false, meta: false, ctrl: false });\n\n/**\n * A pointer as the gesture stream reports it (screen, canvas, kind, buttons,\n * modifiers) plus the fields a recogniser needs: which pointer, which button\n * changed, when.\n */\nexport interface SurfacePointer {\n screen: Point; // CSS px relative to the host's view\n canvas: Point; // the host's world units, through its camera at the time of the event (RecognizerOptions.toCanvas)\n kind: PointerKind;\n buttons: number; // PointerEvent.buttons bit mask\n modifiers: Modifiers;\n pointerId: number;\n /** PointerEvent.button: 0 primary, 1 middle, 2 secondary (a pen's barrel button), 5 eraser; -1 none changed */\n button: number;\n time: number; // event timeStamp, ms\n pressure?: number;\n}\n\n// ----------------------------------------------------------------------------\n// Raw events (copied out of DOM events by the host)\n// ----------------------------------------------------------------------------\n\nexport type RawPointerType = \"pointerdown\" | \"pointermove\" | \"pointerup\" | \"pointercancel\" | \"lostpointercapture\";\n\nexport interface RawPointerEvent {\n type: RawPointerType;\n pointerId: number;\n pointerType: PointerKind | string; // unknown types are treated as mouse\n isPrimary?: boolean;\n button: number;\n buttons: number;\n screen: Point; // clientX/Y minus the view's origin\n modifiers?: Partial<Modifiers>;\n time: number;\n pressure?: number;\n /**\n * lostpointercapture only: whose capture was lost. \"content\" (an element inside the view's content, e.g.\n * right after the host took the pointer for a modifier-drag) leaves the gesture alone; default \"surface\".\n */\n lostBy?: \"surface\" | \"content\";\n}\n\nexport interface RawWheelEvent {\n type: \"wheel\";\n deltaX: number;\n deltaY: number;\n /** 0 pixels, 1 lines, 2 pages */\n deltaMode: 0 | 1 | 2;\n screen: Point;\n modifiers?: Partial<Modifiers>;\n time: number;\n}\n\n/** Safari's trackpad pinch (GestureEvent): scale is relative to the gesture's start */\nexport interface RawSafariGestureEvent {\n type: \"gesturestart\" | \"gesturechange\" | \"gestureend\";\n scale: number;\n screen: Point;\n modifiers?: Partial<Modifiers>;\n time: number;\n}\n\nexport type RawInputEvent = RawPointerEvent | RawWheelEvent | RawSafariGestureEvent;\n\n// ----------------------------------------------------------------------------\n// The gesture stream\n// ----------------------------------------------------------------------------\n\n/** What a modifier-drag would do if the pointer travels with the drag modifier held */\nexport type ModifierDrag = \"move\" | \"resize\";\n\nexport type CancelReason =\n | \"pointercancel\" // the browser or OS took the pointer (scroll, system gesture, palm rejection)\n | \"lostcapture\" // pointer capture lost before the pointer went up\n | \"multitouch\" // a second finger turned the press into a pinch\n | \"missed-up\" // its pointerup never came (the same pointer pressed again, or another went down long after)\n | \"escape\"\n | \"reset\" // the view lost focus, the tool changed, the host was disposed\n | (string & {});\n\ninterface PointerGestureBase {\n pointer: SurfacePointer;\n /** The press that started this gesture */\n origin: SurfacePointer;\n}\n\nexport type InputGesture =\n /** A pointer went down (one per gesture; a second touch becomes a pinch instead) */\n | { type: \"press\"; pointer: SurfacePointer; modifierDrag: ModifierDrag | null }\n /** The pointer travelled past the click threshold; modifierDrag is set when the drag modifier was held then */\n | (PointerGestureBase & { type: \"dragStart\"; modifierDrag: ModifierDrag | null })\n /** A move after dragStart (moves inside the threshold are not reported) */\n | (PointerGestureBase & { type: \"move\" })\n /** Held still past the long-press delay (touch and pen by default); at most once per press */\n | (PointerGestureBase & { type: \"longPress\" })\n /**\n * The pointer went up. click: it never crossed the threshold and no long press fired;\n * clickCount counts quick clicks at the same spot (2 = double click).\n */\n | (PointerGestureBase & { type: \"release\"; click: boolean; clickCount: number; dragged: boolean })\n | (PointerGestureBase & { type: \"cancel\"; reason: CancelReason; dragged: boolean })\n /** A pointer moving with no press (mouse and pen hover): cursor and chrome hover */\n | { type: \"hover\"; pointer: SurfacePointer }\n /**\n * Two fingers (touch) or a trackpad pinch. scale is the change since the last update (multiply it in);\n * pan is the centre's screen-px change since the last update. kind is what was recognised first.\n */\n | {\n type: \"pinch\";\n phase: \"start\" | \"update\" | \"end\" | \"cancel\";\n source: \"touch\" | \"trackpad\";\n kind: \"pinch\" | \"pan\";\n center: { screen: Point; canvas: Point };\n scale: number;\n pan: Point;\n modifiers: Modifiers;\n time: number;\n }\n /** A wheel or trackpad scroll that is not a pinch: delta in screen px (lines and pages converted) */\n | { type: \"scroll\"; delta: Point; at: { screen: Point; canvas: Point }; modifiers: Modifiers; time: number }\n /**\n * Zoom by a factor around a point: Cmd/Ctrl+wheel, a trackpad pinch reported as ctrl+wheel\n * (Chrome, Firefox, Edge). Safari's trackpad pinch comes as \"pinch\" with source \"trackpad\".\n */\n | { type: \"zoom\"; factor: number; at: { screen: Point; canvas: Point }; modifiers: Modifiers; time: number };\n\nexport type InputGestureType = InputGesture[\"type\"];\n","/**\n * Modifier keys: event modifiers to one shape, and the drag modifier\n * resolved per platform (Cmd on macOS, Alt elsewhere; null turns it off).\n */\n\nimport { NO_MODIFIERS, type ModifierKey, type Modifiers, type Platform, type PointerKind } from \"./types\";\n\nexport function toModifiers(m: Partial<Modifiers> | undefined): Modifiers {\n if (!m) return { ...NO_MODIFIERS };\n return { shift: !!m.shift, alt: !!m.alt, meta: !!m.meta, ctrl: !!m.ctrl };\n}\n\n/** The default drag modifier: \"meta\" (Cmd) on macOS, \"alt\" elsewhere */\nexport function defaultDragModifier(platform: Platform): ModifierKey {\n return platform === \"mac\" ? \"meta\" : \"alt\";\n}\n\n/**\n * A configured drag modifier as used: undefined → the platform default, null → off.\n */\nexport function resolveDragModifier(config: ModifierKey | null | undefined, platform: Platform): ModifierKey | null {\n if (config === null) return null;\n return config ?? defaultDragModifier(platform);\n}\n\nexport function isHeld(modifiers: Modifiers, key: ModifierKey | null): boolean {\n return key !== null && modifiers[key];\n}\n\n/**\n * The button a press stands for on this platform. On macOS a Ctrl+click is the secondary click (the system's own\n * rule; the browser's contextmenu follows it), so a primary mouse press with Ctrl held is button 2 there, as a\n * right-click is. Not when Ctrl is the drag modifier (Ctrl+drag then moves). A pen, a finger and the other platforms\n * keep their button.\n */\nexport function platformButton(press: { kind: PointerKind; button: number; modifiers: Modifiers }, platform: Platform, dragModifier: ModifierKey | null): number {\n return platform === \"mac\" && press.kind === \"mouse\" && press.button === 0 && press.modifiers.ctrl && dragModifier !== \"ctrl\" ? 2 : press.button;\n}\n\n/** Platform \"Mod\": Cmd on macOS, Ctrl elsewhere (Mod+wheel zooms, Mod+click is content's multi-select) */\nexport function modHeld(modifiers: Modifiers, platform: Platform): boolean {\n return platform === \"mac\" ? modifiers.meta : modifiers.ctrl;\n}\n","/**\n * Wheel normalisation (Figma / tldraw style): lines and pages to pixels;\n * Cmd/Ctrl+wheel and the trackpad pinch (ctrl+wheel in Chrome, Firefox, Edge)\n * zoom by 2^(−dy / sensitivity); any other wheel scrolls, Shift turns\n * vertical into horizontal.\n */\n\nimport type { Point } from \"../shapes\";\nimport type { Modifiers } from \"./types\";\n\nexport interface WheelOptions {\n /** px per line (deltaMode 1); default 40 */\n lineHeight: number;\n /** px per page (deltaMode 2); default 800 */\n pageHeight: number;\n /** zoom factor = 2^(−deltaY / sensitivity); default 300 */\n zoomSensitivity: number;\n}\n\nexport const WHEEL_DEFAULTS: Readonly<WheelOptions> = Object.freeze({ lineHeight: 40, pageHeight: 800, zoomSensitivity: 300 });\n\nexport function wheelDeltaPx(deltaX: number, deltaY: number, deltaMode: 0 | 1 | 2, opts: WheelOptions = WHEEL_DEFAULTS): Point {\n const k = deltaMode === 1 ? opts.lineHeight : deltaMode === 2 ? opts.pageHeight : 1;\n return { x: finite(deltaX) * k, y: finite(deltaY) * k };\n}\n\nexport type WheelIntent = { kind: \"zoom\"; factor: number } | { kind: \"scroll\"; delta: Point };\n\n/**\n * What a wheel event means. Ctrl or Meta held: zoom (a trackpad pinch arrives as ctrl+wheel).\n * Otherwise scroll; with Shift a vertical-only delta scrolls horizontally (mice without a\n * horizontal wheel).\n */\nexport function classifyWheel(delta: Point, modifiers: Modifiers, opts: WheelOptions = WHEEL_DEFAULTS): WheelIntent {\n if (modifiers.ctrl || modifiers.meta) {\n const factor = Math.pow(2, -delta.y / (opts.zoomSensitivity > 0 ? opts.zoomSensitivity : WHEEL_DEFAULTS.zoomSensitivity));\n return { kind: \"zoom\", factor: Number.isFinite(factor) && factor > 0 ? factor : 1 };\n }\n if (modifiers.shift && delta.x === 0) return { kind: \"scroll\", delta: { x: delta.y, y: 0 } };\n return { kind: \"scroll\", delta };\n}\n\n/** Wheel pan damping for a camera that pans on scroll */\nexport interface WheelPanOptions {\n /** Multiplier on the wheel's px; default 0.65 */\n speed: number;\n /** Linear up to this many px per event; default 30 */\n threshold: number;\n /** Hard cap per event, px; default 60 */\n max: number;\n}\n\nexport const WHEEL_PAN_DEFAULTS: Readonly<WheelPanOptions> = Object.freeze({ speed: 0.65, threshold: 30, max: 60 });\n\n/**\n * The camera pan for one wheel scroll, per axis: speed × delta, linear up to the threshold, then\n * square-root damped (threshold + √(4 × excess)) and capped, so a fast flick of a free-spinning wheel or an\n * accelerated trackpad doesn't throw the canvas away.\n */\nexport function dampWheelPan(delta: Point, opts: WheelPanOptions = WHEEL_PAN_DEFAULTS): Point {\n const one = (d: number) => {\n const v = finite(d) * opts.speed;\n const abs = Math.abs(v);\n if (abs <= opts.threshold) return v;\n return Math.sign(v) * Math.min(opts.threshold + Math.sqrt((abs - opts.threshold) * 4), opts.max);\n };\n return { x: one(delta.x), y: one(delta.y) };\n}\n\nfunction finite(v: number): number {\n return Number.isFinite(v) ? v : 0;\n}\n","/**\n * The gesture recogniser: raw pointer, wheel and Safari gesture events (plain\n * objects) in, one stream of InputGesture out. Mouse, pen and touch share one\n * path; two fingers become a pinch (5 % scale or 10 px pan decides which, read\n * once both fingers have moved, so a pan's leading finger is no pinch); a\n * press becomes a drag only past a per-kind threshold, so a modifier+click\n * (Cmd/Ctrl+click to multi-select) stays a click for the content.\n *\n * One press at a time: a second mouse or pen while one is down is ignored, and\n * a second finger cancels the one-finger press (\"multitouch\") and starts a\n * pinch. After a pinch, the fingers left on the glass start nothing until all\n * are lifted.\n *\n * No DOM, no clock: timestamps come with the events and the long-press timer\n * is injected as the timer half of a ClockPort (without timers there is no\n * long press). The stale-press drop works from event timestamps, so it needs\n * no timer.\n */\n\nimport type { ClockPort } from \"../clock/port\";\nimport type { Point } from \"../shapes\";\nimport { isHeld, platformButton, toModifiers } from \"./modifiers\";\nimport type {\n CancelReason,\n InputGesture,\n ModifierDrag,\n ModifierKey,\n Platform,\n PointerKind,\n RawInputEvent,\n RawPointerEvent,\n RawSafariGestureEvent,\n RawWheelEvent,\n SurfacePointer,\n} from \"./types\";\nimport { classifyWheel, WHEEL_DEFAULTS, wheelDeltaPx, type WheelOptions } from \"./wheel\";\n\n/** The timer half of a ClockPort: any clock (createBrowserClock, createFakeClock) is one. */\nexport type RecognizerTimers = Pick<ClockPort, \"setTimer\" | \"clearTimer\">;\n\nexport interface RecognizerOptions {\n /** Screen px (relative to the view) to canvas units, through the current camera */\n toCanvas: (screen: Point) => Point;\n /** For the long press: a clock; without timers no long press fires */\n timers?: RecognizerTimers;\n /** The drag modifier, resolved (see resolveDragModifier); null = off */\n dragModifier?: ModifierKey | null;\n /** \"mac\": a mouse Ctrl+press is the secondary button, as the system has it (platformButton); default \"other\" */\n platform?: Platform;\n /** Click vs drag: a press becomes a drag after travelling more than this many screen px */\n thresholds?: Partial<Record<PointerKind, number>>;\n longPress?: { ms?: number; kinds?: readonly PointerKind[] };\n /** Two clicks within ms and px of each other count as a double click */\n multiClick?: { ms?: number; px?: number };\n pinch?: { scaleThreshold?: number; panThreshold?: number };\n wheel?: Partial<WheelOptions>;\n /** A mouse or pen press, or a resting finger outside any gesture, untouched this long when another pointer goes down, lost its pointerup; default 1000 ms */\n staleMs?: number;\n}\n\nexport const RECOGNIZER_DEFAULTS = Object.freeze({\n thresholds: Object.freeze({ mouse: 4, pen: 6, touch: 8 }) as Readonly<Record<PointerKind, number>>,\n longPress: Object.freeze({ ms: 500, kinds: Object.freeze([\"touch\", \"pen\"]) as readonly PointerKind[] }),\n multiClick: Object.freeze({ ms: 300, px: 5 }),\n pinch: Object.freeze({ scaleThreshold: 0.05, panThreshold: 10 }),\n});\n\nexport interface Recognizer {\n feed(event: RawInputEvent): void;\n /** Cancel the press and the pinch in flight (Escape, a tool change, the view losing focus) */\n cancel(reason: CancelReason): void;\n /** cancel(\"reset\") and forget every pointer (e.g. after the window lost focus mid-touch) */\n reset(): void;\n /** The press in flight, if any */\n active(): { pointerId: number; kind: PointerKind; dragging: boolean } | null;\n pinching(): boolean;\n /** Change the drag modifier; applies from the next press */\n setDragModifier(key: ModifierKey | null): void;\n dispose(): void;\n}\n\ninterface Press {\n origin: SurfacePointer;\n last: SurfacePointer;\n dragging: boolean;\n longPressed: boolean;\n /** What a modifier-drag would do; decided at the press, confirmed (modifier still held) at dragStart */\n candidate: ModifierDrag | null;\n modifierDrag: ModifierDrag | null;\n timer: number | undefined;\n}\n\ninterface Touch {\n screen: Point;\n time: number;\n}\n\ninterface Pinch {\n ids: [number, number];\n startDist: number;\n startCenter: Point;\n lastDist: number;\n lastCenter: Point;\n kind: \"pinch\" | \"pan\" | null;\n /** While undecided: each finger's moves (position changes) since the last look at the kind */\n reported: Map<number, number>;\n}\n\n/**\n * While two fingers are undecided, the kind is looked at once both have moved as often as each other since the last\n * look: one finger's events arrive before the other's (one or a burst), and a pan's leading finger alone changes the\n * distance like a pinch. A finger at rest sends no moves, so a finger this many moves ahead of the other is looked at\n * on its own (a pinch around an anchored thumb).\n */\nconst SOLO_MOVES = 3;\n\nfunction kindOf(pointerType: string): PointerKind {\n return pointerType === \"touch\" || pointerType === \"pen\" ? pointerType : \"mouse\";\n}\n\nfunction dist(a: Point, b: Point): number {\n return Math.hypot(a.x - b.x, a.y - b.y);\n}\n\nfunction mid(a: Point, b: Point): Point {\n return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };\n}\n\nfunction finitePoint(p: Point): Point {\n return { x: Number.isFinite(p.x) ? p.x : 0, y: Number.isFinite(p.y) ? p.y : 0 };\n}\n\nexport function createRecognizer(options: RecognizerOptions, emit: (gesture: InputGesture) => void): Recognizer {\n const thresholds = { ...RECOGNIZER_DEFAULTS.thresholds, ...options.thresholds };\n const longPressMs = options.longPress?.ms ?? RECOGNIZER_DEFAULTS.longPress.ms;\n const longPressKinds = options.longPress?.kinds ?? RECOGNIZER_DEFAULTS.longPress.kinds;\n const multiClick = { ...RECOGNIZER_DEFAULTS.multiClick, ...options.multiClick };\n const pinchCfg = { ...RECOGNIZER_DEFAULTS.pinch, ...options.pinch };\n const wheelCfg: WheelOptions = { ...WHEEL_DEFAULTS, ...options.wheel };\n const timers = options.timers;\n const staleMs = options.staleMs ?? 1000;\n let dragModifier: ModifierKey | null = options.dragModifier ?? null;\n const platform: Platform = options.platform ?? \"other\";\n\n let press: Press | null = null;\n let pinch: Pinch | null = null;\n const touches = new Map<number, Touch>();\n /** After a pinch (or a reset mid-touch), fingers still down start nothing until all are up */\n let touchLocked = false;\n let lastClick: { time: number; screen: Point; button: number; count: number } | null = null;\n let safari: { last: number } | null = null;\n let lastTime = 0;\n let disposed = false;\n\n const pointerOf = (e: RawPointerEvent): SurfacePointer => {\n const screen = finitePoint(e.screen);\n return {\n screen,\n canvas: options.toCanvas(screen),\n kind: kindOf(e.pointerType),\n buttons: e.buttons,\n modifiers: toModifiers(e.modifiers),\n pointerId: e.pointerId,\n button: e.button,\n time: e.time,\n ...(e.pressure !== undefined ? { pressure: e.pressure } : {}),\n };\n };\n\n const clearTimer = (p: Press) => {\n if (p.timer !== undefined && timers) timers.clearTimer(p.timer);\n p.timer = undefined;\n };\n\n const endPress = (): Press | null => {\n const p = press;\n if (p) clearTimer(p);\n press = null;\n return p;\n };\n\n const cancelPress = (reason: CancelReason, pointer?: SurfacePointer) => {\n const p = endPress();\n if (!p) return;\n emit({ type: \"cancel\", pointer: pointer ?? p.last, origin: p.origin, reason, dragged: p.dragging });\n };\n\n const releasePress = (pointer: SurfacePointer) => {\n const p = endPress();\n if (!p) return;\n const click = !p.dragging && !p.longPressed;\n let clickCount = 0;\n if (click) {\n const prev = lastClick;\n const repeat =\n !!prev &&\n prev.button === p.origin.button &&\n p.origin.time - prev.time <= multiClick.ms &&\n dist(prev.screen, p.origin.screen) <= multiClick.px;\n clickCount = repeat ? prev!.count + 1 : 1;\n lastClick = { time: pointer.time, screen: p.origin.screen, button: p.origin.button, count: clickCount };\n } else {\n lastClick = null;\n }\n emit({ type: \"release\", pointer, origin: p.origin, click, clickCount, dragged: p.dragging });\n };\n\n const pinchState = (): { center: Point; d: number } | null => {\n if (!pinch) return null;\n const a = touches.get(pinch.ids[0]);\n const b = touches.get(pinch.ids[1]);\n if (!a || !b) return null;\n return { center: mid(a.screen, b.screen), d: Math.max(1, dist(a.screen, b.screen)) };\n };\n\n const pinchEvent = (\n phase: \"start\" | \"update\" | \"end\" | \"cancel\",\n center: Point,\n scale: number,\n pan: Point,\n time: number,\n modifiers: SurfacePointer[\"modifiers\"],\n source: \"touch\" | \"trackpad\",\n kind: \"pinch\" | \"pan\",\n ): InputGesture => ({\n type: \"pinch\",\n phase,\n source,\n kind,\n center: { screen: center, canvas: options.toCanvas(center) },\n scale,\n pan,\n modifiers,\n time,\n });\n\n const endPinch = (phase: \"end\" | \"cancel\", time: number, modifiers: SurfacePointer[\"modifiers\"]) => {\n const p = pinch;\n pinch = null;\n touchLocked = touches.size > 0;\n if (p && p.kind) emit(pinchEvent(phase, p.lastCenter, 1, { x: 0, y: 0 }, time, modifiers, \"touch\", p.kind));\n };\n\n /**\n * A pinch finger moved (`id`), or lifts (`lifting`: the last look, whatever moved). The kind is decided on both\n * fingers' latest positions (see SOLO_MOVES); the thresholds keep their meaning.\n */\n const updatePinch = (id: number, time: number, modifiers: SurfacePointer[\"modifiers\"], moved: boolean, lifting = false) => {\n const p = pinch;\n const now = pinchState();\n if (!p || !now) return;\n if (!p.kind) {\n if (!moved && !lifting) return; // a move that changed nothing (pressure only) is no move\n const moves = (p.reported.get(id) ?? 0) + (moved ? 1 : 0);\n p.reported.set(id, moves);\n const other = p.reported.get(p.ids[0] === id ? p.ids[1] : p.ids[0]) ?? 0;\n const even = moves > 0 && moves === other;\n if (!lifting && !even && Math.abs(moves - other) < SOLO_MOVES) return;\n p.reported.clear();\n const scaleChange = Math.abs(now.d / p.startDist - 1);\n const panDist = dist(now.center, p.startCenter);\n if (scaleChange > pinchCfg.scaleThreshold) p.kind = \"pinch\";\n else if (panDist > pinchCfg.panThreshold) p.kind = \"pan\";\n else return;\n // The first event carries everything since the second finger landed, so nothing is lost.\n const scale = p.kind === \"pinch\" ? now.d / p.startDist : 1;\n const pan = { x: now.center.x - p.startCenter.x, y: now.center.y - p.startCenter.y };\n p.lastDist = now.d;\n p.lastCenter = now.center;\n emit(pinchEvent(\"start\", now.center, scale, pan, time, modifiers, \"touch\", p.kind));\n return;\n }\n const scale = p.kind === \"pinch\" ? now.d / p.lastDist : 1;\n const pan = { x: now.center.x - p.lastCenter.x, y: now.center.y - p.lastCenter.y };\n p.lastDist = now.d;\n p.lastCenter = now.center;\n if (scale === 1 && pan.x === 0 && pan.y === 0) return;\n emit(pinchEvent(\"update\", now.center, scale, pan, time, modifiers, \"touch\", p.kind));\n };\n\n /**\n * A pointerup that never came (released outside the page, a pen lifted out of range, a lost event):\n * a press of the same pointer, or a mouse / pen press untouched for staleMs, is over. A finger that\n * lands again under its old id, or one resting untouched for staleMs outside any press or pinch, is gone.\n */\n const dropMissedUps = (e: RawPointerEvent, kind: PointerKind) => {\n const p = press;\n if (p) {\n const same = p.origin.pointerId === e.pointerId;\n const mixed = kind !== \"touch\" || p.origin.kind !== \"touch\"; // two fingers make a pinch, never a stale press\n if (same || (mixed && e.time - p.last.time > staleMs)) cancelPress(\"missed-up\");\n }\n if (kind !== \"touch\") return;\n if (touches.has(e.pointerId)) {\n if (pinch?.ids.includes(e.pointerId)) endPinch(\"cancel\", e.time, toModifiers(e.modifiers));\n touches.delete(e.pointerId);\n }\n for (const [id, t] of touches) {\n const busy = press?.origin.pointerId === id || !!pinch?.ids.includes(id);\n if (!busy && e.time - t.time > staleMs) touches.delete(id);\n }\n if (touches.size === 0) touchLocked = false;\n };\n\n const onDown = (e: RawPointerEvent) => {\n const down = pointerOf(e);\n // The press's button as the platform means it (macOS: Ctrl+click is a secondary click)\n const pointer: SurfacePointer = { ...down, button: platformButton(down, platform, dragModifier) };\n dropMissedUps(e, pointer.kind);\n if (pointer.kind === \"touch\") {\n touches.set(e.pointerId, { screen: pointer.screen, time: pointer.time });\n if (touchLocked || pinch) return; // a third finger, or fingers after a pinch\n if (touches.size === 2) {\n const ids = [...touches.keys()] as [number, number];\n // The second finger: whatever the first one started is cancelled, a pinch may begin.\n if (press && press.origin.kind === \"touch\") cancelPress(\"multitouch\");\n else if (press) {\n touches.delete(e.pointerId); // a mouse or pen press owns the input; ignore the finger\n return;\n }\n const a = touches.get(ids[0])!;\n const b = touches.get(ids[1])!;\n const d = Math.max(1, dist(a.screen, b.screen));\n const c = mid(a.screen, b.screen);\n pinch = { ids, startDist: d, startCenter: c, lastDist: d, lastCenter: c, kind: null, reported: new Map() };\n return;\n }\n if (touches.size > 2) return;\n }\n if (press || pinch) return; // one press at a time\n const candidate: ModifierDrag | null = isHeld(pointer.modifiers, dragModifier)\n ? pointer.button === 0\n ? \"move\"\n : pointer.button === 2\n ? \"resize\"\n : null\n : null;\n const p: Press = {\n origin: pointer,\n last: pointer,\n dragging: false,\n longPressed: false,\n candidate,\n modifierDrag: null,\n timer: undefined,\n };\n press = p;\n if (timers && longPressKinds.includes(pointer.kind)) {\n p.timer = timers.setTimer(() => {\n p.timer = undefined;\n if (press !== p || p.dragging || disposed) return;\n p.longPressed = true;\n emit({ type: \"longPress\", pointer: p.last, origin: p.origin });\n }, longPressMs);\n }\n emit({ type: \"press\", pointer, modifierDrag: candidate });\n };\n\n const onMove = (e: RawPointerEvent) => {\n const kind = kindOf(e.pointerType);\n if (kind === \"touch\") {\n const t = touches.get(e.pointerId);\n const to = finitePoint(e.screen);\n const moved = !t || t.screen.x !== to.x || t.screen.y !== to.y;\n if (t) {\n t.screen = to;\n t.time = e.time;\n }\n if (pinch && pinch.ids.includes(e.pointerId)) {\n updatePinch(e.pointerId, e.time, toModifiers(e.modifiers), moved);\n return;\n }\n }\n if (press && press.origin.pointerId === e.pointerId) {\n const pointer = pointerOf(e);\n // A mouse or pen moving with no button down and no pointerup before it: the release was missed.\n if (pointer.kind !== \"touch\" && e.buttons === 0) {\n releasePress(pointer);\n return;\n }\n const p = press;\n p.last = pointer;\n if (!p.dragging) {\n if (dist(pointer.screen, p.origin.screen) <= thresholds[p.origin.kind]) return;\n p.dragging = true;\n clearTimer(p);\n // The modifier must still be held when the drag starts: a released Cmd makes it a plain drag.\n p.modifierDrag = p.candidate && isHeld(pointer.modifiers, dragModifier) ? p.candidate : null;\n emit({ type: \"dragStart\", pointer, origin: p.origin, modifierDrag: p.modifierDrag });\n return;\n }\n emit({ type: \"move\", pointer, origin: p.origin });\n return;\n }\n if (!press && kind !== \"touch\" && e.buttons === 0) emit({ type: \"hover\", pointer: pointerOf(e) });\n };\n\n const onUp = (e: RawPointerEvent) => {\n if (kindOf(e.pointerType) === \"touch\") {\n // A finger lifting from an undecided pinch: its last position is looked at once, so a quick pinch still counts\n const t = touches.get(e.pointerId);\n if (t && pinch && !pinch.kind && pinch.ids.includes(e.pointerId)) {\n const to = finitePoint(e.screen);\n const moved = t.screen.x !== to.x || t.screen.y !== to.y;\n t.screen = to;\n updatePinch(e.pointerId, e.time, toModifiers(e.modifiers), moved, true);\n }\n touches.delete(e.pointerId);\n if (pinch && pinch.ids.includes(e.pointerId)) {\n endPinch(\"end\", e.time, toModifiers(e.modifiers));\n return;\n }\n if (touches.size === 0) touchLocked = false;\n }\n if (press && press.origin.pointerId === e.pointerId) releasePress(pointerOf(e));\n };\n\n const onCancel = (e: RawPointerEvent, reason: CancelReason) => {\n if (kindOf(e.pointerType) === \"touch\") {\n const tracked = touches.delete(e.pointerId);\n if (pinch && pinch.ids.includes(e.pointerId)) {\n if (tracked) endPinch(\"cancel\", e.time, toModifiers(e.modifiers));\n return;\n }\n if (touches.size === 0) touchLocked = false;\n }\n if (press && press.origin.pointerId === e.pointerId) cancelPress(reason, pointerOf(e));\n };\n\n const onWheel = (e: RawWheelEvent) => {\n const modifiers = toModifiers(e.modifiers);\n const screen = finitePoint(e.screen);\n const at = { screen, canvas: options.toCanvas(screen) };\n const intent = classifyWheel(wheelDeltaPx(e.deltaX, e.deltaY, e.deltaMode, wheelCfg), modifiers, wheelCfg);\n if (intent.kind === \"zoom\") {\n if (intent.factor !== 1) emit({ type: \"zoom\", factor: intent.factor, at, modifiers, time: e.time });\n return;\n }\n if (intent.delta.x === 0 && intent.delta.y === 0) return;\n emit({ type: \"scroll\", delta: intent.delta, at, modifiers, time: e.time });\n };\n\n const onSafari = (e: RawSafariGestureEvent) => {\n // iOS Safari fires gesture events for touch pinches too; the pointer path handles those.\n if (touches.size > 0 || pinch) return;\n const modifiers = toModifiers(e.modifiers);\n const screen = finitePoint(e.screen);\n const scaleNow = Number.isFinite(e.scale) && e.scale > 0 ? e.scale : 1;\n const none = { x: 0, y: 0 };\n if (e.type === \"gesturestart\") {\n safari = { last: scaleNow };\n emit(pinchEvent(\"start\", screen, 1, none, e.time, modifiers, \"trackpad\", \"pinch\"));\n return;\n }\n if (!safari) return;\n const scale = scaleNow / safari.last;\n safari.last = scaleNow;\n if (e.type === \"gesturechange\") {\n if (scale !== 1) emit(pinchEvent(\"update\", screen, scale, none, e.time, modifiers, \"trackpad\", \"pinch\"));\n return;\n }\n safari = null;\n emit(pinchEvent(\"end\", screen, scale, none, e.time, modifiers, \"trackpad\", \"pinch\"));\n };\n\n const cancelAll = (reason: CancelReason) => {\n cancelPress(reason);\n if (pinch) endPinch(\"cancel\", lastTime, toModifiers(undefined));\n if (safari) {\n safari = null;\n emit(pinchEvent(\"cancel\", { x: 0, y: 0 }, 1, { x: 0, y: 0 }, lastTime, toModifiers(undefined), \"trackpad\", \"pinch\"));\n }\n };\n\n return {\n feed(event) {\n if (disposed) return;\n if (Number.isFinite(event.time)) lastTime = event.time;\n switch (event.type) {\n case \"pointerdown\":\n return onDown(event);\n case \"pointermove\":\n return onMove(event);\n case \"pointerup\":\n return onUp(event);\n case \"pointercancel\":\n return onCancel(event, \"pointercancel\");\n case \"lostpointercapture\":\n // Also fires right after pointerup; by then the press is gone and this is a no-op. The content\n // losing its capture (the surface just took the pointer) is not the gesture's end.\n if (event.lostBy === \"content\") return;\n return onCancel(event, \"lostcapture\");\n case \"wheel\":\n return onWheel(event);\n case \"gesturestart\":\n case \"gesturechange\":\n case \"gestureend\":\n return onSafari(event);\n }\n },\n cancel: cancelAll,\n reset() {\n cancelAll(\"reset\");\n touches.clear();\n touchLocked = false;\n lastClick = null;\n },\n active() {\n return press ? { pointerId: press.origin.pointerId, kind: press.origin.kind, dragging: press.dragging } : null;\n },\n pinching() {\n return !!pinch?.kind || !!safari;\n },\n setDragModifier(key) {\n dragModifier = key;\n },\n dispose() {\n if (press) clearTimer(press);\n press = null;\n pinch = null;\n safari = null;\n touches.clear();\n disposed = true;\n },\n };\n}\n","import type { FrameSource } from \"../clock/port\";\n\n/** Options of {@link createFrameCoalescer}. */\nexport interface CoalescerOptions<T> {\n /**\n * How a new sample folds into the pending one. Default: keep the latest\n * (pointer positions). Wheel deltas want a sum instead.\n */\n readonly merge?: (pending: T, next: T) => T;\n}\n\n/**\n * Collapses a burst of samples into at most one delivery per frame. Same\n * shape as hypr-tiling's `createFrameCoalescer` (`schedule`, `cancel`),\n * plus `flush` and `pending`.\n */\nexport interface FrameCoalescer<T> {\n /** Record a sample and make sure a frame is requested. */\n schedule(sample: T): void;\n /**\n * Deliver the pending sample now, if there is one (before a pointer-up, so\n * the last move is seen first). Returns whether it delivered.\n */\n flush(): boolean;\n /** Drop the pending sample and the requested frame. Nothing is delivered after this. */\n cancel(): void;\n /** Whether a sample is waiting for its frame. */\n readonly pending: boolean;\n}\n\n/**\n * Deliver samples to `onFrame` once per frame of `frames`: the browser clock,\n * the fake clock, or a frame loop phase (`loop.frames(\"input\")`). `onFrame`\n * also gets how many samples were folded into this one.\n */\nexport function createFrameCoalescer<T>(\n onFrame: (sample: T, count: number) => void,\n frames: FrameSource,\n options: CoalescerOptions<T> = {},\n): FrameCoalescer<T> {\n const merge = options.merge;\n let handle: number | null = null;\n let sample: T | undefined;\n let count = 0;\n\n const deliver = (): void => {\n const delivered = sample as T;\n const folded = count;\n sample = undefined;\n count = 0;\n onFrame(delivered, folded);\n };\n\n const onFrameDue = (): void => {\n handle = null;\n if (count > 0) deliver();\n };\n\n return {\n schedule(next) {\n sample = count > 0 && merge !== undefined ? merge(sample as T, next) : next;\n count++;\n if (handle === null) handle = frames.requestFrame(onFrameDue);\n },\n flush() {\n if (handle !== null) {\n frames.cancelFrame(handle);\n handle = null;\n }\n if (count === 0) return false;\n deliver();\n return true;\n },\n cancel() {\n if (handle !== null) {\n frames.cancelFrame(handle);\n handle = null;\n }\n sample = undefined;\n count = 0;\n },\n get pending() {\n return count > 0;\n },\n };\n}\n","import type { TimerSource } from \"../clock/port\";\nimport { signal } from \"../signals/signals\";\n\n/**\n * A timer event a state asks for: send `event(t)` when the clock reaches\n * `at`. Machines stay pure; this is how a long-press or a dwell turns into\n * an event without the reducer touching a timer.\n */\nexport interface Deadline<E> {\n /** Clock time in ms. */\n readonly at: number;\n /** The event to send, given the clock time it fired at. */\n readonly event: (t: number) => E;\n}\n\n/** Options of {@link createMachine}. */\nexport interface MachineOptions<S, E> {\n /** The pure transition function. Returning the same state means \"no change\". */\n readonly reducer: (state: S, event: E) => S;\n readonly initial: S;\n /** Timers come from here (and `now()` for their events). */\n readonly clock: TimerSource;\n /** The timer event the state needs, or `null`. Checked after every transition. */\n readonly deadline?: (state: S) => Deadline<E> | null;\n}\n\n/** A running machine: the state, a way to send events, and change listeners. */\nexport interface Machine<S, E> {\n /** The current state; reactive (read it in a computed or an effect). */\n readonly state: S;\n /** The current state, untracked. */\n peek(): S;\n /**\n * Run `event` through the reducer and return the new state. An event sent\n * from a listener waits until the current one has been handled.\n */\n send(event: E): S;\n /** Called after each change of state (not for events that changed nothing). */\n subscribe(listener: (next: S, previous: S, event: E) => void): () => void;\n /** Clear the timer and drop listeners. Events sent after this are ignored. */\n dispose(): void;\n}\n\n/** Run a pure machine on a clock: it arms and clears the deadline timer for you. */\nexport function createMachine<S, E>(options: MachineOptions<S, E>): Machine<S, E> {\n const { reducer, clock, deadline } = options;\n const state = signal(options.initial);\n const listeners = new Set<(next: S, previous: S, event: E) => void>();\n const inbox: E[] = [];\n let dispatching = false;\n let disposed = false;\n let timer: number | null = null;\n let timerAt: number | null = null;\n let timerEvent: ((t: number) => E) | null = null;\n\n const clearTimer = (): void => {\n if (timer !== null) clock.clearTimer(timer);\n timer = null;\n timerAt = null;\n timerEvent = null;\n };\n\n const arm = (): void => {\n const due = deadline === undefined ? null : deadline(state.peek());\n if (due === null) {\n clearTimer();\n return;\n }\n timerEvent = due.event;\n if (timer !== null && timerAt === due.at) return;\n if (timer !== null) clock.clearTimer(timer);\n timerAt = due.at;\n timer = clock.setTimer(() => {\n const make = timerEvent;\n timer = null;\n timerAt = null;\n timerEvent = null;\n if (make !== null) machine.send(make(clock.now()));\n }, Math.max(0, due.at - clock.now()));\n };\n\n const machine: Machine<S, E> = {\n get state() {\n return state.value;\n },\n peek: () => state.peek(),\n send(event) {\n if (disposed) return state.peek();\n inbox.push(event);\n if (dispatching) return state.peek();\n dispatching = true;\n try {\n let next: E | undefined;\n while ((next = inbox.shift()) !== undefined) {\n const previous = state.peek();\n const after = reducer(previous, next);\n if (after === previous) continue;\n state.value = after;\n arm();\n for (const listener of Array.from(listeners)) listener(after, previous, next);\n if (disposed) break;\n }\n } finally {\n dispatching = false;\n inbox.length = 0;\n }\n return state.peek();\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n clearTimer();\n listeners.clear();\n },\n };\n arm();\n return machine;\n}\n","import type { TimerSource } from \"../clock/port\";\nimport { createMachine, type Deadline, type Machine } from \"./machine\";\n\n/**\n * The dwell machine (spring-load): hold over the same target long enough and\n * it fires, once.\n *\n * ```text\n * idle ──hover(key)──► dwelling ──tick (t − since ≥ dwellMs)──► fired\n * ▲ │ hover(other key) restarts `since` │\n * │ │ hover(null) ─► idle │\n * └────────┴─────────────── reset ◄─────────────────────┘\n * ```\n *\n * `fired` holds until `reset`: neither a late tick nor a hover change can fire\n * it again or clear it while the host acts on it. Shapes come from\n * hypr-tiling's `springLoadReducer`, with its workspace tab generalised to\n * any `key` (the host passes `null` for anything that shouldn't dwell, such as\n * the active tab).\n */\n\n/** Tunables of the dwell machine. */\nexport interface DwellConfig {\n /** How long in ms the pointer must stay over the same key. Default 500. */\n readonly dwellMs: number;\n}\n\n/** Defaults of {@link DwellConfig}. */\nexport const DWELL_DEFAULTS: DwellConfig = { dwellMs: 500 };\n\n/** The dwell machine's state. `K` is the target key, `P` the payload carried into `fired`. */\nexport type DwellState<K = string, P = unknown> =\n | { readonly phase: \"idle\" }\n | { readonly phase: \"dwelling\"; readonly key: K; readonly since: number; readonly payload: P | undefined }\n | {\n readonly phase: \"fired\";\n readonly key: K;\n readonly since: number;\n readonly firedAt: number;\n readonly payload: P | undefined;\n };\n\n/** Events of the dwell machine. `t` is the clock time in ms. */\nexport type DwellInput<K = string, P = unknown> =\n | { readonly type: \"hover\"; readonly t: number; readonly key: K | null; readonly payload?: P }\n | { readonly type: \"tick\"; readonly t: number }\n | { readonly type: \"reset\" };\n\n/** The idle state (the same object every time). */\nexport const DWELL_IDLE: DwellState<never, never> = { phase: \"idle\" };\n\n/** The dwell machine's pure transition function. */\nexport function dwellReducer<K, P>(\n state: DwellState<K, P>,\n event: DwellInput<K, P>,\n config: DwellConfig = DWELL_DEFAULTS,\n): DwellState<K, P> {\n switch (event.type) {\n case \"reset\":\n return state.phase === \"idle\" ? state : DWELL_IDLE;\n case \"hover\":\n if (state.phase === \"fired\") return state;\n if (event.key === null) return state.phase === \"idle\" ? state : DWELL_IDLE;\n if (state.phase === \"dwelling\" && Object.is(state.key, event.key)) return state;\n return { phase: \"dwelling\", key: event.key, since: event.t, payload: event.payload };\n case \"tick\":\n if (state.phase !== \"dwelling\" || event.t - state.since < config.dwellMs) return state;\n return { phase: \"fired\", key: state.key, since: state.since, firedAt: event.t, payload: state.payload };\n default:\n return state;\n }\n}\n\n/** When a dwelling state fires, as a timer event; `null` outside `dwelling`. */\nexport function dwellDeadline<K, P>(\n state: DwellState<K, P>,\n config: DwellConfig = DWELL_DEFAULTS,\n): Deadline<DwellInput<K, P>> | null {\n if (state.phase !== \"dwelling\") return null;\n return { at: state.since + config.dwellMs, event: (t) => ({ type: \"tick\", t }) };\n}\n\n/** Options of {@link createDwellMachine}. */\nexport interface DwellMachineOptions {\n readonly clock: TimerSource;\n readonly config?: Partial<DwellConfig>;\n}\n\n/** A dwell machine on a clock: the dwell timer is armed for you. */\nexport function createDwellMachine<K = string, P = unknown>(\n options: DwellMachineOptions,\n): Machine<DwellState<K, P>, DwellInput<K, P>> {\n const config: DwellConfig = { ...DWELL_DEFAULTS, ...options.config };\n return createMachine<DwellState<K, P>, DwellInput<K, P>>({\n reducer: (state, event) => dwellReducer(state, event, config),\n initial: DWELL_IDLE,\n clock: options.clock,\n deadline: (state) => dwellDeadline(state, config),\n });\n}\n","import type { TimerSource } from \"../clock/port\";\nimport type { Point } from \"../shapes\";\nimport { createMachine, type Deadline, type Machine } from \"./machine\";\n\n/**\n * A touch press on something draggable that sits in a scrolling page: is it a drag, or a scroll?\n *\n * ```text\n * idle ──press──► pressed ──move: mostly along the scroll direction, ≥ scrollEscape──► scrolled (never lifts)\n * │ ──move: mostly across it, ≥ lift px────────────────────────────► lifted\n * │ ──tick: held still holdMs─────────────────────────────────────► lifted (where it was pressed)\n * └ ──move: along it short of the escape, or across it below lift px: stays pressed\n * any ──release──► idle\n * ```\n *\n * Before the hold has elapsed, a finger that travels mostly along the page's scroll direction is a scroll: the touch\n * goes back to the page and stays a scroll until it is released; short of the escape it waits, never lifting.\n * Travel past the lift distance mostly across the scroll direction is a deliberate drag. Once the hold has elapsed\n * the press lifts where it is. `scrolled` and `lifted`\n * hold until `release`.\n */\n\n/** Tunables of the touch press. */\nexport interface TouchPressConfig {\n /** px of travel that lifts a press that is not a scroll. Default 6. */\n readonly lift: number;\n /** px of travel along the scroll direction that makes the press a scroll. Default 10. */\n readonly scrollEscape: number;\n /** The page's scroll direction: `\"y\"` (a vertical page, the default) or `\"x\"`. */\n readonly scroll: \"x\" | \"y\";\n /** ms a still finger holds before the press lifts where it is. Default 220. */\n readonly holdMs: number;\n}\n\n/** Defaults of {@link TouchPressConfig}. */\nexport const TOUCH_PRESS_DEFAULTS: TouchPressConfig = { lift: 6, scrollEscape: 10, scroll: \"y\", holdMs: 220 };\n\n/** The touch press's state. `from` is where the finger went down, `at` where it lifted. */\nexport type TouchPressState =\n | { readonly phase: \"idle\" }\n | { readonly phase: \"pressed\"; readonly from: Point; readonly since: number }\n | { readonly phase: \"lifted\"; readonly from: Point; readonly at: Point }\n | { readonly phase: \"scrolled\"; readonly from: Point };\n\n/** Events of the touch press. `t` is the clock time in ms. */\nexport type TouchPressInput =\n | { readonly type: \"press\"; readonly t: number; readonly point: Point }\n | { readonly type: \"move\"; readonly point: Point }\n | { readonly type: \"tick\"; readonly t: number }\n | { readonly type: \"release\" };\n\n/** The idle state (the same object every time). */\nexport const TOUCH_PRESS_IDLE: TouchPressState = { phase: \"idle\" };\n\n/** The touch press's pure transition function. */\nexport function touchPressReducer(state: TouchPressState, event: TouchPressInput, config: TouchPressConfig = TOUCH_PRESS_DEFAULTS): TouchPressState {\n switch (event.type) {\n case \"release\":\n return state.phase === \"idle\" ? state : TOUCH_PRESS_IDLE;\n case \"press\":\n return state.phase === \"idle\" ? { phase: \"pressed\", from: { x: event.point.x, y: event.point.y }, since: event.t } : state;\n case \"tick\":\n if (state.phase !== \"pressed\" || event.t - state.since < config.holdMs) return state;\n return { phase: \"lifted\", from: state.from, at: state.from };\n case \"move\": {\n if (state.phase !== \"pressed\") return state;\n const dx = event.point.x - state.from.x;\n const dy = event.point.y - state.from.y;\n const along = Math.abs(config.scroll === \"y\" ? dy : dx);\n const across = Math.abs(config.scroll === \"y\" ? dx : dy);\n // Mostly along the scroll direction is never a lift: a scroll once it reaches the escape, until then waiting\n // (a finger scroll is reported a few px at a time, and would pass the lift distance first).\n if (along > across) return along >= config.scrollEscape ? { phase: \"scrolled\", from: state.from } : state;\n if (Math.hypot(dx, dy) >= config.lift) return { phase: \"lifted\", from: state.from, at: { x: event.point.x, y: event.point.y } };\n return state;\n }\n default:\n return state;\n }\n}\n\n/** When a pressed state lifts by holding, as a timer event; `null` outside `pressed`. */\nexport function touchPressDeadline(state: TouchPressState, config: TouchPressConfig = TOUCH_PRESS_DEFAULTS): Deadline<TouchPressInput> | null {\n if (state.phase !== \"pressed\") return null;\n return { at: state.since + config.holdMs, event: (t) => ({ type: \"tick\", t }) };\n}\n\n/** Options of {@link createTouchPress}. */\nexport interface TouchPressOptions {\n readonly clock: TimerSource;\n readonly config?: Partial<TouchPressConfig>;\n}\n\n/** A touch press on a clock: the hold timer is armed for you. */\nexport function createTouchPress(options: TouchPressOptions): Machine<TouchPressState, TouchPressInput> {\n const config: TouchPressConfig = { ...TOUCH_PRESS_DEFAULTS, ...options.config };\n return createMachine<TouchPressState, TouchPressInput>({\n reducer: (state, event) => touchPressReducer(state, event, config),\n initial: TOUCH_PRESS_IDLE,\n clock: options.clock,\n deadline: (state) => touchPressDeadline(state, config),\n });\n}\n","import type { TimerSource } from \"../clock/port\";\nimport { createMachine, type Deadline, type Machine } from \"./machine\";\nimport type { ModifierKey, Modifiers } from \"./types\";\n\n/**\n * A paged swipe: a trackpad's two-finger scroll or a finger swipes from one page to the previous or the next, the\n * pages following it, and commits or snaps back when it ends.\n *\n * ```text\n * idle ──wheel / touch-move (arming rules pass)──► armed ──travel ≥ thresholdPx──► tracking\n * ▲ │ │\n * │ rule breach / idle / end gesture end\n * │ ▼ ▼\n * └──────── lockoutMs elapsed ◄── lockout ◄── settled (commit) ◄───────────── settling\n * └──────────────────────────────────────── settled (cancel) ◄───────────────────┤\n * └──────────────────────────────────────── cancel (transition stopped) ◄────────┘\n * ```\n *\n * - **Arming:** nothing else owns the pointer (`blocked`), the movement runs mostly along the swipe (more than twice\n * the cross movement), no Ctrl (a pinch), the configured modifier is held, nothing under the pointer can still\n * scroll that way (`canScrollFurther`), and there is a page on that side (or `wrap`). A wheel run that breaks a\n * rule belongs to scrolling until the wheel has been idle `idleMs`.\n * - **Tracking:** `progress` is the travel over the page size, −1 to 1, held at 0 towards a side with no page.\n * - **Ending:** a touch ends when the finger lifts, a wheel run after `idleMs` without samples. It commits when\n * `|progress| ≥ commitFraction`, or the peak speed towards the target reached `commitVelocity` (the peak starts\n * over when the travel crosses zero, so a fast fling out and a pull back does not commit the way back).\n * - **Settling** lasts as long as the host's page transition; the host sends `settled` when it is done (or `cancel`,\n * which goes straight back to idle, when it stops the transition). After a commit, `lockout` swallows the\n * trackpad's momentum for `lockoutMs`, and the run stays claimed after it.\n *\n * Pure and timer-free: {@link pagedSwipeDeadline} says when the next `tick` is due ({@link createPagedSwipe} arms it).\n * Signs: a wheel delta > 0 along the swipe heads to `next`; a finger moving towards smaller x (or y) heads to `next`.\n */\n\n/** Which side of the current page a swipe heads to. */\nexport type SwipeSide = \"prev\" | \"next\";\n\n/** Tunables of the paged swipe. */\nexport interface PagedSwipeConfig {\n /** px of travel before an armed swipe tracks. Default 24. */\n readonly thresholdPx: number;\n /** The share of the page size that commits at the end. Default 0.35. */\n readonly commitFraction: number;\n /** px/ms: a peak speed towards the target that commits at the end. Default 0.6. */\n readonly commitVelocity: number;\n /** ms without a wheel sample that ends a wheel run. Default 120. */\n readonly idleMs: number;\n /** ms after a commit during which wheel samples are swallowed (momentum). Default 350. */\n readonly lockoutMs: number;\n /** Past the last page comes the first, and back. Default false. */\n readonly wrap: boolean;\n /** The page size in px when no sample or context gives one. Default 800. */\n readonly size: number;\n /** A modifier the wheel needs held to swipe (`null`: none). Touch never needs one. Default null. */\n readonly modifier: ModifierKey | null;\n /** The swipe's direction: `\"x\"` (the default) or `\"y\"`. */\n readonly along: \"x\" | \"y\";\n}\n\n/** Defaults of {@link PagedSwipeConfig}. */\nexport const PAGED_SWIPE_DEFAULTS: PagedSwipeConfig = {\n thresholdPx: 24,\n commitFraction: 0.35,\n commitVelocity: 0.6,\n idleMs: 120,\n lockoutMs: 350,\n wrap: false,\n size: 800,\n modifier: null,\n along: \"x\",\n};\n\n/** What the host knows about the pages now. */\nexport interface PagedSwipeContext {\n readonly hasPrev: boolean;\n readonly hasNext: boolean;\n /** The page size in px, or null (the config's). */\n readonly size: number | null;\n /** Something else owns the pointer (a drag): nothing arms, and a swipe in progress gives way. */\n readonly blocked: boolean;\n}\n\n/** The context before the host has said anything. */\nexport const PAGED_SWIPE_EMPTY_CONTEXT: PagedSwipeContext = { hasPrev: false, hasNext: false, size: null, blocked: false };\n\ntype Input = \"wheel\" | \"touch\";\ninterface Base {\n readonly context: PagedSwipeContext;\n /** −1 to 1; negative towards `prev`. */\n readonly progress: number;\n readonly target: SwipeSide | null;\n}\n\n/** The paged swipe's state. Every phase has a way back to `idle`. */\nexport type PagedSwipeState =\n | (Base & {\n readonly phase: \"idle\";\n /** The last wheel sample of the current run (null: none, or the run went idle). */\n readonly lastWheel: number | null;\n /** This wheel run broke the arming rules (or committed): it cannot arm until the wheel is idle `idleMs`. */\n readonly claimed: boolean;\n })\n | (Base & {\n readonly phase: \"armed\";\n readonly input: Input;\n /** Where the finger went down (touch). */\n readonly origin: { readonly x: number; readonly y: number };\n /** Wheel travel along the swipe and across it so far. */\n readonly travel: number;\n readonly cross: number;\n readonly last: number;\n })\n | (Base & {\n readonly phase: \"tracking\";\n readonly input: Input;\n readonly origin: { readonly x: number; readonly y: number };\n readonly travel: number;\n readonly last: number;\n /** The highest speed (px/ms) towards the current target. */\n readonly peak: number;\n })\n | (Base & { readonly phase: \"settling\"; readonly input: Input; readonly outcome: \"commit\" | \"cancel\"; readonly ended: number; readonly lastWheel: number })\n | (Base & { readonly phase: \"lockout\"; readonly until: number; readonly lastWheel: number });\n\n/** Events of the paged swipe. `t` is the clock time in ms. */\nexport type PagedSwipeInput =\n | {\n readonly type: \"wheel\";\n readonly dx: number;\n readonly dy: number;\n readonly t: number;\n readonly modifiers: Modifiers;\n /** Something under the pointer can still scroll that way: no arming. */\n readonly canScrollFurther?: boolean;\n readonly size?: number;\n }\n | { readonly type: \"touch-start\"; readonly x: number; readonly y: number; readonly t: number; readonly size?: number }\n | { readonly type: \"touch-move\"; readonly x: number; readonly y: number; readonly t: number; readonly canScrollFurther?: boolean; readonly size?: number }\n | { readonly type: \"touch-end\"; readonly t: number }\n | { readonly type: \"tick\"; readonly t: number }\n | { readonly type: \"settled\" }\n | { readonly type: \"cancel\" }\n | { readonly type: \"block\"; readonly on: boolean }\n | { readonly type: \"context\"; readonly hasPrev: boolean; readonly hasNext: boolean; readonly size?: number };\n\n/** The idle state with no context. */\nexport const PAGED_SWIPE_IDLE: PagedSwipeState = { phase: \"idle\", context: PAGED_SWIPE_EMPTY_CONTEXT, progress: 0, target: null, lastWheel: null, claimed: false };\n\nconst idle = (context: PagedSwipeContext, lastWheel: number | null = null, claimed = false): PagedSwipeState => ({ phase: \"idle\", context, progress: 0, target: null, lastWheel, claimed });\nconst sideOf = (travel: number): SwipeSide | null => (travel > 0 ? \"next\" : travel < 0 ? \"prev\" : null);\nconst hasPage = (c: PagedSwipeContext, side: SwipeSide, wrap: boolean): boolean => (wrap ? c.hasPrev || c.hasNext : side === \"next\" ? c.hasNext : c.hasPrev);\nconst withSize = (c: PagedSwipeContext, size: number | undefined): PagedSwipeContext => (size === undefined || size === c.size ? c : { ...c, size });\nconst sizeOf = (c: PagedSwipeContext, size: number | undefined, config: PagedSwipeConfig): number => size ?? c.size ?? config.size;\n\n/** Where the swipe may head for a movement (`main` along the swipe, `cross` across it), or null when it must not arm. */\nfunction arming(c: PagedSwipeContext, main: number, cross: number, config: PagedSwipeConfig, gates: { ctrl: boolean; modifierHeld: boolean; canScrollFurther: boolean }): SwipeSide | null {\n if (c.blocked || gates.ctrl || gates.canScrollFurther || !gates.modifierHeld) return null;\n if (Math.abs(main) <= 2 * Math.abs(cross)) return null;\n const side = sideOf(main);\n return side && hasPage(c, side, config.wrap) ? side : null;\n}\n\n/** Travel to progress: over the page size, −1 to 1, 0 towards a side with no page (unless `wrap`). */\nexport function swipeProgress(travel: number, size: number, c: PagedSwipeContext, wrap: boolean): number {\n const p = Math.max(-1, Math.min(1, travel / Math.max(1, size)));\n if (wrap) return p;\n if ((p < 0 && !c.hasPrev) || (p > 0 && !c.hasNext)) return 0;\n return p;\n}\n\n/** Speed towards `side` for a step of `delta` px over `dt` ms; 0 moving away. */\nconst towards = (delta: number, dt: number, side: SwipeSide | null): number => {\n if (dt <= 0 || side === null) return 0;\n const v = (side === \"next\" ? delta : -delta) / dt;\n return v > 0 ? v : 0;\n};\n\nfunction track(c: PagedSwipeContext, input: Input, origin: { x: number; y: number }, travel: number, t: number, peak: number, size: number, config: PagedSwipeConfig): PagedSwipeState {\n const progress = swipeProgress(travel, size, c, config.wrap);\n return { phase: \"tracking\", context: c, input, origin, travel, last: t, peak, progress, target: sideOf(progress) };\n}\n\nfunction end(s: Extract<PagedSwipeState, { phase: \"tracking\" }>, t: number, config: PagedSwipeConfig): PagedSwipeState {\n const commit = s.target !== null && (Math.abs(s.progress) >= config.commitFraction || s.peak >= config.commitVelocity);\n return { phase: \"settling\", context: s.context, input: s.input, outcome: commit ? \"commit\" : \"cancel\", ended: t, lastWheel: t, progress: s.progress, target: commit ? s.target : null };\n}\n\nconst giveUp = (s: Extract<PagedSwipeState, { phase: \"tracking\" }>, c: PagedSwipeContext): PagedSwipeState => ({\n phase: \"settling\",\n context: c,\n input: s.input,\n outcome: \"cancel\",\n ended: s.last,\n lastWheel: s.last,\n progress: s.progress,\n target: null,\n});\n\n/** The paged swipe's pure transition function. Unmatched pairs return the state unchanged. */\nexport function pagedSwipeReducer(state: PagedSwipeState, event: PagedSwipeInput, config: PagedSwipeConfig = PAGED_SWIPE_DEFAULTS): PagedSwipeState {\n const y = config.along === \"y\";\n if (event.type === \"context\") {\n return { ...state, context: { ...state.context, hasPrev: event.hasPrev, hasNext: event.hasNext, size: event.size ?? state.context.size } };\n }\n if (event.type === \"block\") {\n const c = state.context.blocked === event.on ? state.context : { ...state.context, blocked: event.on };\n if (event.on && state.phase === \"armed\") return idle(c);\n if (event.on && state.phase === \"tracking\") return giveUp(state, c);\n return c === state.context ? state : { ...state, context: c };\n }\n if (event.type === \"cancel\") {\n // Settling too: a host that will never send `settled` (it went away mid-transition) must not leave it stuck.\n if (state.phase === \"armed\" || state.phase === \"lockout\" || state.phase === \"settling\") return idle(state.context);\n if (state.phase === \"tracking\") return giveUp(state, state.context);\n return state;\n }\n switch (state.phase) {\n case \"idle\": {\n if (event.type === \"wheel\") {\n if (state.context.blocked) return state;\n const fresh = state.lastWheel === null || event.t - state.lastWheel >= config.idleMs;\n if (!fresh && state.claimed) return { ...state, lastWheel: event.t };\n const main = y ? event.dy : event.dx;\n const cross = y ? event.dx : event.dy;\n const side = arming(state.context, main, cross, config, wheelGates(event, config));\n const c = withSize(state.context, event.size);\n if (side === null) return idle(c, event.t, true);\n if (Math.abs(main) >= config.thresholdPx) return track(c, \"wheel\", { x: 0, y: 0 }, main, event.t, 0, sizeOf(c, event.size, config), config);\n return { phase: \"armed\", context: c, input: \"wheel\", origin: { x: 0, y: 0 }, travel: main, cross, last: event.t, progress: 0, target: null };\n }\n if (event.type === \"touch-start\") {\n if (state.context.blocked) return state;\n return { phase: \"armed\", context: withSize(state.context, event.size), input: \"touch\", origin: { x: event.x, y: event.y }, travel: 0, cross: 0, last: event.t, progress: 0, target: null };\n }\n return state;\n }\n case \"armed\": {\n if (event.type === \"wheel\") {\n if (state.input !== \"wheel\") return state;\n const main = y ? event.dy : event.dx;\n const cross = y ? event.dx : event.dy;\n // Every sample before the threshold must pass the rules too; a breach gives the run to scrolling.\n if (arming(state.context, main, cross, config, wheelGates(event, config)) === null) return idle(state.context, event.t, true);\n const c = withSize(state.context, event.size);\n const travel = state.travel + main;\n const crossTravel = state.cross + cross;\n // The run as a whole must stay along the swipe.\n if (Math.abs(crossTravel) > Math.abs(travel) / 2) return idle(c, event.t, true);\n if (Math.abs(travel) < config.thresholdPx) return { ...state, context: c, travel, cross: crossTravel, last: event.t };\n return track(c, \"wheel\", state.origin, travel, event.t, towards(main, event.t - state.last, sideOf(travel)), sizeOf(c, event.size, config), config);\n }\n if (event.type === \"touch-move\") {\n if (state.input !== \"touch\") return state;\n const main = y ? state.origin.y - event.y : state.origin.x - event.x;\n const cross = y ? event.x - state.origin.x : event.y - state.origin.y;\n // Finger jitter.\n if (Math.hypot(main, cross) < config.thresholdPx) return { ...state, last: event.t };\n const side = arming(state.context, main, cross, config, { ctrl: false, modifierHeld: true, canScrollFurther: event.canScrollFurther ?? false });\n // Across the swipe, or nowhere to go: the page owns this touch until it ends.\n if (side === null) return idle(state.context);\n const c = withSize(state.context, event.size);\n return track(c, \"touch\", state.origin, main, event.t, towards(main, event.t - state.last, side), sizeOf(c, event.size, config), config);\n }\n if (event.type === \"tick\") return state.input === \"wheel\" && event.t - state.last >= config.idleMs ? idle(state.context) : state;\n if (event.type === \"touch-end\") return state.input === \"touch\" ? idle(state.context) : state;\n if (event.type === \"touch-start\") return { ...state, context: withSize(state.context, event.size), input: \"touch\", origin: { x: event.x, y: event.y }, travel: 0, cross: 0, last: event.t };\n return state;\n }\n case \"tracking\": {\n if (event.type === \"wheel\" || event.type === \"touch-move\") {\n if (state.input !== (event.type === \"wheel\" ? \"wheel\" : \"touch\")) return state;\n const travel =\n event.type === \"wheel\" ? state.travel + (y ? event.dy : event.dx) : y ? state.origin.y - event.y : state.origin.x - event.x;\n const side = sideOf(travel);\n const v = towards(travel - state.travel, event.t - state.last, side);\n const peak = side === sideOf(state.travel) ? Math.max(state.peak, v) : v;\n const c = withSize(state.context, event.size);\n return track(c, state.input, state.origin, travel, event.t, peak, sizeOf(c, event.size, config), config);\n }\n if (event.type === \"tick\") return state.input === \"wheel\" && event.t - state.last >= config.idleMs ? end(state, event.t, config) : state;\n if (event.type === \"touch-end\") return state.input === \"touch\" ? end(state, event.t, config) : state;\n return state;\n }\n case \"settling\": {\n if (event.type === \"settled\") {\n if (state.outcome === \"commit\") return { phase: \"lockout\", context: state.context, until: state.ended + config.lockoutMs, lastWheel: state.lastWheel, progress: 0, target: null };\n return idle(state.context, state.lastWheel, false);\n }\n if (event.type === \"wheel\") return { ...state, lastWheel: event.t };\n return state;\n }\n case \"lockout\": {\n if (event.type === \"wheel\") return event.t >= state.until ? idle(state.context, event.t, true) : { ...state, lastWheel: event.t };\n if (event.type === \"tick\") {\n if (event.t < state.until) return state;\n return event.t - state.lastWheel < config.idleMs ? idle(state.context, state.lastWheel, true) : idle(state.context);\n }\n // A finger is not momentum: it may start a swipe at once.\n if (event.type === \"touch-start\") return pagedSwipeReducer(idle(state.context), event, config);\n return state;\n }\n default:\n return state;\n }\n}\n\nfunction wheelGates(event: Extract<PagedSwipeInput, { type: \"wheel\" }>, config: PagedSwipeConfig): { ctrl: boolean; modifierHeld: boolean; canScrollFurther: boolean } {\n return { ctrl: event.modifiers.ctrl, modifierHeld: config.modifier === null || event.modifiers[config.modifier], canScrollFurther: event.canScrollFurther ?? false };\n}\n\n/** When the next `tick` is due: a wheel run's idle end, or the end of the lockout; `null` when none is. */\nexport function pagedSwipeDeadline(state: PagedSwipeState, config: PagedSwipeConfig = PAGED_SWIPE_DEFAULTS): Deadline<PagedSwipeInput> | null {\n const tick = (t: number): PagedSwipeInput => ({ type: \"tick\", t });\n if ((state.phase === \"armed\" || state.phase === \"tracking\") && state.input === \"wheel\") return { at: state.last + config.idleMs, event: tick };\n if (state.phase === \"lockout\") return { at: state.until, event: tick };\n return null;\n}\n\n/** Options of {@link createPagedSwipe}. */\nexport interface PagedSwipeOptions {\n readonly clock: TimerSource;\n readonly config?: Partial<PagedSwipeConfig>;\n}\n\n/** A paged swipe on a clock: the idle and lockout timers are armed for you. */\nexport function createPagedSwipe(options: PagedSwipeOptions): Machine<PagedSwipeState, PagedSwipeInput> {\n const config: PagedSwipeConfig = { ...PAGED_SWIPE_DEFAULTS, ...options.config };\n return createMachine<PagedSwipeState, PagedSwipeInput>({\n reducer: (state, event) => pagedSwipeReducer(state, event, config),\n initial: PAGED_SWIPE_IDLE,\n clock: options.clock,\n deadline: (state) => pagedSwipeDeadline(state, config),\n });\n}\n"],"mappings":"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","names":["scale","pan"]}
@@ -547,7 +547,8 @@ declare function createTouchPress(options: TouchPressOptions): Machine<TouchPres
547
547
  * │ rule breach / idle / end gesture end
548
548
  * │ ▼ ▼
549
549
  * └──────── lockoutMs elapsed ◄── lockout ◄── settled (commit) ◄───────────── settling
550
- * └──────────────────────────────────────── settled (cancel) ◄───────────────────┘
550
+ * └──────────────────────────────────────── settled (cancel) ◄───────────────────┤
551
+ * └──────────────────────────────────────── cancel (transition stopped) ◄────────┘
551
552
  * ```
552
553
  *
553
554
  * - **Arming:** nothing else owns the pointer (`blocked`), the movement runs mostly along the swipe (more than twice
package/dist/gestures.js CHANGED
@@ -32,7 +32,7 @@ import {
32
32
  touchPressDeadline,
33
33
  touchPressReducer,
34
34
  wheelDeltaPx
35
- } from "./chunk-BFKPRYEQ.js";
35
+ } from "./chunk-KVFX5V4Y.js";
36
36
  import "./chunk-OOKRBVMB.js";
37
37
  export {
38
38
  DWELL_DEFAULTS,
package/dist/index.js CHANGED
@@ -57,7 +57,7 @@ import {
57
57
  touchPressDeadline,
58
58
  touchPressReducer,
59
59
  wheelDeltaPx
60
- } from "./chunk-BFKPRYEQ.js";
60
+ } from "./chunk-KVFX5V4Y.js";
61
61
  import {
62
62
  batch,
63
63
  computed,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@n-uf/motion",
3
- "version": "26.11.5",
3
+ "version": "26.11.7",
4
4
  "description": "One frame loop with an injected clock, fine-grained signals, springs and tweens, and a DOM-free gesture recogniser. No DOM, no framework.",
5
5
  "keywords": [
6
6
  "animation",
@@ -30,7 +30,7 @@
30
30
  "url": "git+https://github.com/n-uf/motion.git",
31
31
  "directory": "packages/motion"
32
32
  },
33
- "homepage": "https://github.com/n-uf/motion#readme",
33
+ "homepage": "https://motion.n-uf.com",
34
34
  "bugs": {
35
35
  "url": "https://github.com/n-uf/motion/issues"
36
36
  },