@n-uf/motion 26.11.0 → 26.11.2

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,33 @@
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.2
7
+
8
+ - A two-finger pan is no longer taken for a pinch. One finger's event arrives
9
+ before the other's, and a pan's leading finger alone changes the distance
10
+ like a pinch (fingers 120 px apart, 7.5 px per event: 6.3 % against the 5 %
11
+ threshold, while the centre moved only 3.75 px). The kind is now read once
12
+ both fingers have moved as often as each other since the last look (so a
13
+ lead of one or two events is caught up first); a finger at rest sends no
14
+ moves, so one three moves ahead is read on its own (a pinch around an
15
+ anchored thumb); a finger lifting from an undecided pair is read once, so a
16
+ quick pinch still counts. A move that changes no position (pressure only)
17
+ doesn't count. The thresholds keep their meaning; the API is unchanged.
18
+ - What a host sees differently: `pinch` `start` can come one to three moves
19
+ later than before (it still carries everything since the second finger
20
+ landed), and a pair cancelled before it decided emits no `pinch` `cancel`
21
+ (nothing had started).
22
+
23
+ ## 26.11.1
24
+
25
+ - The recogniser takes the platform (`platform`, default `"other"`). On
26
+ `"mac"`, a mouse press with Ctrl held is a secondary click (button 2), as
27
+ macOS has it, so Ctrl+click works like a right-click and Cmd+Ctrl+drag
28
+ resizes like Cmd+right-drag. Not when Ctrl is the drag modifier (Ctrl+drag
29
+ moves), and not for a pen, a finger or another platform.
30
+ `platformButton(press, platform, dragModifier)` is the rule on its own.
31
+ Ported from the pdfized surface's input session, with its tests.
32
+
6
33
  ## 26.11.0
7
34
 
8
35
  First version: the motion package of the surface's layout-and-motion concept.
package/README.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @n-uf/motion
2
2
 
3
+ > [!CAUTION]
4
+ > ## Not yet stable
5
+ >
6
+ > **@n-uf/motion is young and still changing. Its API (the frame loop, signals, springs and tweens, and the gesture recogniser) and its timing behaviour are not final.**
7
+ >
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
+
3
10
  One frame loop with an injected clock, fine-grained signals, springs and
4
11
  tweens, and a DOM-free gesture recogniser. No DOM, no framework, no globals:
5
12
  the host hands in a clock, and tests hand in a fake one.
@@ -152,10 +159,10 @@ clock.runUntilIdle(); // settled at { x: 300, y: 40, width: 240, height: 100 }
152
159
 
153
160
  | | |
154
161
  |---|---|
155
- | `createRecognizer({ toCanvas, timers?, dragModifier?, thresholds?, longPress?, multiClick?, pinch?, wheel?, staleMs? }, emit)` | Raw pointer, wheel and Safari gesture events in, `InputGesture`s out: `press`, `dragStart`, `move`, `longPress`, `release` (with `click` and `clickCount`), `cancel` (with a reason), `hover`, `pinch` (touch or trackpad, pinch or two-finger pan), `scroll`, `zoom`. Methods: `feed`, `cancel(reason)`, `reset`, `active`, `pinching`, `setDragModifier`, `dispose`. |
156
- | `RECOGNIZER_DEFAULTS` | Click-or-drag thresholds 4 / 6 / 8 px (mouse / pen / touch), long press after 500 ms for touch and pen, double click within 300 ms and 5 px, pinch at 5 % scale or 10 px pan. |
162
+ | `createRecognizer({ toCanvas, timers?, dragModifier?, platform?, thresholds?, longPress?, multiClick?, pinch?, wheel?, staleMs? }, emit)` | Raw pointer, wheel and Safari gesture events in, `InputGesture`s out: `press`, `dragStart`, `move`, `longPress`, `release` (with `click` and `clickCount`), `cancel` (with a reason), `hover`, `pinch` (touch or trackpad, pinch or two-finger pan), `scroll`, `zoom`. Methods: `feed`, `cancel(reason)`, `reset`, `active`, `pinching`, `setDragModifier`, `dispose`. |
163
+ | `RECOGNIZER_DEFAULTS` | Click-or-drag thresholds 4 / 6 / 8 px (mouse / pen / touch), long press after 500 ms for touch and pen, double click within 300 ms and 5 px, pinch at 5 % scale or 10 px pan (read once both fingers have moved as often as each other, or one is three moves ahead, or one lifts). |
157
164
  | `wheelDeltaPx`, `classifyWheel`, `dampWheelPan`, `WHEEL_DEFAULTS`, `WHEEL_PAN_DEFAULTS` | Wheel normalisation: lines and pages to px, Cmd/Ctrl+wheel (and the ctrl+wheel trackpad pinch) zooms by 2^(−dy / 300), Shift turns a vertical wheel horizontal, and a damped pan per event. |
158
- | `toModifiers`, `resolveDragModifier`, `defaultDragModifier`, `isHeld`, `modHeld` | Modifier keys in one shape; the drag modifier per platform (Cmd on macOS, Alt elsewhere). |
165
+ | `toModifiers`, `resolveDragModifier`, `defaultDragModifier`, `isHeld`, `modHeld`, `platformButton` | Modifier keys in one shape; the drag modifier per platform (Cmd on macOS, Alt elsewhere); on macOS a Ctrl+click is the secondary button. |
159
166
  | `createFrameCoalescer(onFrame, frames, { merge? })` | `schedule(sample)`, `flush()`, `cancel()`, `pending`. One delivery per frame with the latest sample (or `merge`d ones) and how many were folded. Same shape as hypr-tiling's. |
160
167
  | `dwellReducer`, `DWELL_IDLE`, `dwellDeadline`, `createDwellMachine` | `idle → dwelling → fired`, for spring-loaded targets: hold over the same key long enough and it fires once. |
161
168
  | `createMachine({ reducer, initial, clock, deadline? })` | Runs a pure reducer on a clock: arms the timer the state's deadline asks for and sends its input when it fires. `state` is reactive. |
@@ -20,6 +20,9 @@ function resolveDragModifier(config, platform) {
20
20
  function isHeld(modifiers, key) {
21
21
  return key !== null && modifiers[key];
22
22
  }
23
+ function platformButton(press, platform, dragModifier) {
24
+ return platform === "mac" && press.kind === "mouse" && press.button === 0 && press.modifiers.ctrl && dragModifier !== "ctrl" ? 2 : press.button;
25
+ }
23
26
  function modHeld(modifiers, platform) {
24
27
  return platform === "mac" ? modifiers.meta : modifiers.ctrl;
25
28
  }
@@ -59,6 +62,7 @@ var RECOGNIZER_DEFAULTS = Object.freeze({
59
62
  multiClick: Object.freeze({ ms: 300, px: 5 }),
60
63
  pinch: Object.freeze({ scaleThreshold: 0.05, panThreshold: 10 })
61
64
  });
65
+ var SOLO_MOVES = 3;
62
66
  function kindOf(pointerType) {
63
67
  return pointerType === "touch" || pointerType === "pen" ? pointerType : "mouse";
64
68
  }
@@ -81,6 +85,7 @@ function createRecognizer(options, emit) {
81
85
  const timers = options.timers;
82
86
  const staleMs = options.staleMs ?? 1e3;
83
87
  let dragModifier = options.dragModifier ?? null;
88
+ const platform = options.platform ?? "other";
84
89
  let press = null;
85
90
  let pinch = null;
86
91
  const touches = /* @__PURE__ */ new Map();
@@ -157,11 +162,18 @@ function createRecognizer(options, emit) {
157
162
  touchLocked = touches.size > 0;
158
163
  if (p && p.kind) emit(pinchEvent(phase, p.lastCenter, 1, { x: 0, y: 0 }, time, modifiers, "touch", p.kind));
159
164
  };
160
- const updatePinch = (time, modifiers) => {
165
+ const updatePinch = (id, time, modifiers, moved, lifting = false) => {
161
166
  const p = pinch;
162
167
  const now = pinchState();
163
168
  if (!p || !now) return;
164
169
  if (!p.kind) {
170
+ if (!moved && !lifting) return;
171
+ const moves = (p.reported.get(id) ?? 0) + (moved ? 1 : 0);
172
+ p.reported.set(id, moves);
173
+ const other = p.reported.get(p.ids[0] === id ? p.ids[1] : p.ids[0]) ?? 0;
174
+ const even = moves > 0 && moves === other;
175
+ if (!lifting && !even && Math.abs(moves - other) < SOLO_MOVES) return;
176
+ p.reported.clear();
165
177
  const scaleChange = Math.abs(now.d / p.startDist - 1);
166
178
  const panDist = dist(now.center, p.startCenter);
167
179
  if (scaleChange > pinchCfg.scaleThreshold) p.kind = "pinch";
@@ -200,7 +212,8 @@ function createRecognizer(options, emit) {
200
212
  if (touches.size === 0) touchLocked = false;
201
213
  };
202
214
  const onDown = (e) => {
203
- const pointer = pointerOf(e);
215
+ const down = pointerOf(e);
216
+ const pointer = { ...down, button: platformButton(down, platform, dragModifier) };
204
217
  dropMissedUps(e, pointer.kind);
205
218
  if (pointer.kind === "touch") {
206
219
  touches.set(e.pointerId, { screen: pointer.screen, time: pointer.time });
@@ -216,7 +229,7 @@ function createRecognizer(options, emit) {
216
229
  const b = touches.get(ids[1]);
217
230
  const d = Math.max(1, dist(a.screen, b.screen));
218
231
  const c = mid(a.screen, b.screen);
219
- pinch = { ids, startDist: d, startCenter: c, lastDist: d, lastCenter: c, kind: null };
232
+ pinch = { ids, startDist: d, startCenter: c, lastDist: d, lastCenter: c, kind: null, reported: /* @__PURE__ */ new Map() };
220
233
  return;
221
234
  }
222
235
  if (touches.size > 2) return;
@@ -247,12 +260,14 @@ function createRecognizer(options, emit) {
247
260
  const kind = kindOf(e.pointerType);
248
261
  if (kind === "touch") {
249
262
  const t = touches.get(e.pointerId);
263
+ const to = finitePoint(e.screen);
264
+ const moved = !t || t.screen.x !== to.x || t.screen.y !== to.y;
250
265
  if (t) {
251
- t.screen = finitePoint(e.screen);
266
+ t.screen = to;
252
267
  t.time = e.time;
253
268
  }
254
269
  if (pinch && pinch.ids.includes(e.pointerId)) {
255
- updatePinch(e.time, toModifiers(e.modifiers));
270
+ updatePinch(e.pointerId, e.time, toModifiers(e.modifiers), moved);
256
271
  return;
257
272
  }
258
273
  }
@@ -279,6 +294,13 @@ function createRecognizer(options, emit) {
279
294
  };
280
295
  const onUp = (e) => {
281
296
  if (kindOf(e.pointerType) === "touch") {
297
+ const t = touches.get(e.pointerId);
298
+ if (t && pinch && !pinch.kind && pinch.ids.includes(e.pointerId)) {
299
+ const to = finitePoint(e.screen);
300
+ const moved = t.screen.x !== to.x || t.screen.y !== to.y;
301
+ t.screen = to;
302
+ updatePinch(e.pointerId, e.time, toModifiers(e.modifiers), moved, true);
303
+ }
282
304
  touches.delete(e.pointerId);
283
305
  if (pinch && pinch.ids.includes(e.pointerId)) {
284
306
  endPinch("end", e.time, toModifiers(e.modifiers));
@@ -555,6 +577,7 @@ export {
555
577
  defaultDragModifier,
556
578
  resolveDragModifier,
557
579
  isHeld,
580
+ platformButton,
558
581
  modHeld,
559
582
  WHEEL_DEFAULTS,
560
583
  wheelDeltaPx,
@@ -571,4 +594,4 @@ export {
571
594
  dwellDeadline,
572
595
  createDwellMachine
573
596
  };
574
- //# sourceMappingURL=chunk-3XAMAZBS.js.map
597
+ //# sourceMappingURL=chunk-NFQ4LIIR.js.map
@@ -0,0 +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"],"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 the pdfized surface'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"],"mappings":"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","names":["scale","pan"]}
@@ -182,6 +182,17 @@ declare function defaultDragModifier(platform: Platform): ModifierKey;
182
182
  */
183
183
  declare function resolveDragModifier(config: ModifierKey | null | undefined, platform: Platform): ModifierKey | null;
184
184
  declare function isHeld(modifiers: Modifiers, key: ModifierKey | null): boolean;
185
+ /**
186
+ * The button a press stands for on this platform. On macOS a Ctrl+click is the secondary click (the system's own
187
+ * rule; the browser's contextmenu follows it), so a primary mouse press with Ctrl held is button 2 there, as a
188
+ * right-click is. Not when Ctrl is the drag modifier (Ctrl+drag then moves). A pen, a finger and the other platforms
189
+ * keep their button.
190
+ */
191
+ declare function platformButton(press: {
192
+ kind: PointerKind;
193
+ button: number;
194
+ modifiers: Modifiers;
195
+ }, platform: Platform, dragModifier: ModifierKey | null): number;
185
196
  /** Platform "Mod": Cmd on macOS, Ctrl elsewhere (Mod+wheel zooms, Mod+click is content's multi-select) */
186
197
  declare function modHeld(modifiers: Modifiers, platform: Platform): boolean;
187
198
 
@@ -235,7 +246,8 @@ declare function dampWheelPan(delta: Point, opts?: WheelPanOptions): Point;
235
246
  /**
236
247
  * The gesture recogniser: raw pointer, wheel and Safari gesture events (plain
237
248
  * objects) in, one stream of InputGesture out. Mouse, pen and touch share one
238
- * path; two fingers become a pinch (5 % scale or 10 px pan decides which); a
249
+ * path; two fingers become a pinch (5 % scale or 10 px pan decides which, read
250
+ * once both fingers have moved, so a pan's leading finger is no pinch); a
239
251
  * press becomes a drag only past a per-kind threshold, so a modifier+click
240
252
  * (Cmd/Ctrl+click to multi-select) stays a click for the content.
241
253
  *
@@ -259,6 +271,8 @@ interface RecognizerOptions {
259
271
  timers?: RecognizerTimers;
260
272
  /** The drag modifier, resolved (see resolveDragModifier); null = off */
261
273
  dragModifier?: ModifierKey | null;
274
+ /** "mac": a mouse Ctrl+press is the secondary button, as the system has it (platformButton); default "other" */
275
+ platform?: Platform;
262
276
  /** Click vs drag: a press becomes a drag after travelling more than this many screen px */
263
277
  thresholds?: Partial<Record<PointerKind, number>>;
264
278
  longPress?: {
@@ -450,4 +464,4 @@ interface DwellMachineOptions {
450
464
  /** A dwell machine on a clock: the dwell timer is armed for you. */
451
465
  declare function createDwellMachine<K = string, P = unknown>(options: DwellMachineOptions): Machine<DwellState<K, P>, DwellInput<K, P>>;
452
466
 
453
- export { type CancelReason, type CoalescerOptions, DWELL_DEFAULTS, DWELL_IDLE, type Deadline, type DwellConfig, type DwellInput, type DwellMachineOptions, type DwellState, type FrameCoalescer, type InputGesture, type InputGestureType, type Machine, type MachineOptions, type ModifierDrag, type ModifierKey, type Modifiers, NO_MODIFIERS, type Platform, type PointerKind, RECOGNIZER_DEFAULTS, type RawInputEvent, type RawPointerEvent, type RawPointerType, type RawSafariGestureEvent, type RawWheelEvent, type Recognizer, type RecognizerOptions, type RecognizerTimers, type SurfacePointer, WHEEL_DEFAULTS, WHEEL_PAN_DEFAULTS, type WheelIntent, type WheelOptions, type WheelPanOptions, classifyWheel, createDwellMachine, createFrameCoalescer, createMachine, createRecognizer, dampWheelPan, defaultDragModifier, dwellDeadline, dwellReducer, isHeld, modHeld, resolveDragModifier, toModifiers, wheelDeltaPx };
467
+ export { type CancelReason, type CoalescerOptions, DWELL_DEFAULTS, DWELL_IDLE, type Deadline, type DwellConfig, type DwellInput, type DwellMachineOptions, type DwellState, type FrameCoalescer, type InputGesture, type InputGestureType, type Machine, type MachineOptions, type ModifierDrag, type ModifierKey, type Modifiers, NO_MODIFIERS, type Platform, type PointerKind, RECOGNIZER_DEFAULTS, type RawInputEvent, type RawPointerEvent, type RawPointerType, type RawSafariGestureEvent, type RawWheelEvent, type Recognizer, type RecognizerOptions, type RecognizerTimers, type SurfacePointer, WHEEL_DEFAULTS, WHEEL_PAN_DEFAULTS, type WheelIntent, type WheelOptions, type WheelPanOptions, classifyWheel, createDwellMachine, createFrameCoalescer, createMachine, createRecognizer, dampWheelPan, defaultDragModifier, dwellDeadline, dwellReducer, isHeld, modHeld, platformButton, resolveDragModifier, toModifiers, wheelDeltaPx };
package/dist/gestures.js CHANGED
@@ -16,10 +16,11 @@ import {
16
16
  dwellReducer,
17
17
  isHeld,
18
18
  modHeld,
19
+ platformButton,
19
20
  resolveDragModifier,
20
21
  toModifiers,
21
22
  wheelDeltaPx
22
- } from "./chunk-3XAMAZBS.js";
23
+ } from "./chunk-NFQ4LIIR.js";
23
24
  import "./chunk-OOKRBVMB.js";
24
25
  export {
25
26
  DWELL_DEFAULTS,
@@ -39,6 +40,7 @@ export {
39
40
  dwellReducer,
40
41
  isHeld,
41
42
  modHeld,
43
+ platformButton,
42
44
  resolveDragModifier,
43
45
  toModifiers,
44
46
  wheelDeltaPx
package/dist/index.d.ts CHANGED
@@ -3,5 +3,5 @@ export { FakeClock, FakeClockOptions, createFakeClock } from './clock.js';
3
3
  export { F as FrameInfo, a as FrameLoop, b as FrameLoopOptions, c as FrameStats, d as FrameTask, P as PHASES, e as Phase, R as RepeatingTask, S as ScheduleOptions, f as createFrameLoop } from './frame-loop-C88dc9xw.js';
4
4
  export { EffectCleanup, EffectOptions, Equals, ReadonlySignal, Signal, SignalOptions, batch, computed, effect, isSignal, signal, untracked } from './signals.js';
5
5
  export { Animator, AnimatorValue, Easing, MotionOptions, MotionValue, SPRING_DEFAULTS, SetOptions, Spring, SpringConfig, SpringParams, SpringState, Tween, TweenConfig, createSpring, createTween, cubicBezier, ease, easeIn, easeInOut, easeInOutCubic, easeOut, easeOutCubic, linear, motionValue, resolveSpring, springStep, springValue, tweenValue } from './values.js';
6
- export { CancelReason, CoalescerOptions, DWELL_DEFAULTS, DWELL_IDLE, Deadline, DwellConfig, DwellInput, DwellMachineOptions, DwellState, FrameCoalescer, InputGesture, InputGestureType, Machine, MachineOptions, ModifierDrag, ModifierKey, Modifiers, NO_MODIFIERS, Platform, PointerKind, RECOGNIZER_DEFAULTS, RawInputEvent, RawPointerEvent, RawPointerType, RawSafariGestureEvent, RawWheelEvent, Recognizer, RecognizerOptions, RecognizerTimers, SurfacePointer, WHEEL_DEFAULTS, WHEEL_PAN_DEFAULTS, WheelIntent, WheelOptions, WheelPanOptions, classifyWheel, createDwellMachine, createFrameCoalescer, createMachine, createRecognizer, dampWheelPan, defaultDragModifier, dwellDeadline, dwellReducer, isHeld, modHeld, resolveDragModifier, toModifiers, wheelDeltaPx } from './gestures.js';
6
+ export { CancelReason, CoalescerOptions, DWELL_DEFAULTS, DWELL_IDLE, Deadline, DwellConfig, DwellInput, DwellMachineOptions, DwellState, FrameCoalescer, InputGesture, InputGestureType, Machine, MachineOptions, ModifierDrag, ModifierKey, Modifiers, NO_MODIFIERS, Platform, PointerKind, RECOGNIZER_DEFAULTS, RawInputEvent, RawPointerEvent, RawPointerType, RawSafariGestureEvent, RawWheelEvent, Recognizer, RecognizerOptions, RecognizerTimers, SurfacePointer, WHEEL_DEFAULTS, WHEEL_PAN_DEFAULTS, WheelIntent, WheelOptions, WheelPanOptions, classifyWheel, createDwellMachine, createFrameCoalescer, createMachine, createRecognizer, dampWheelPan, defaultDragModifier, dwellDeadline, dwellReducer, isHeld, modHeld, platformButton, resolveDragModifier, toModifiers, wheelDeltaPx } from './gestures.js';
7
7
  export { A as Animatable, P as Point, R as Rect, V as Vector, W as Widen, s as shapeEquals } from './shapes-EbQlwbCu.js';
package/dist/index.js CHANGED
@@ -41,10 +41,11 @@ import {
41
41
  dwellReducer,
42
42
  isHeld,
43
43
  modHeld,
44
+ platformButton,
44
45
  resolveDragModifier,
45
46
  toModifiers,
46
47
  wheelDeltaPx
47
- } from "./chunk-3XAMAZBS.js";
48
+ } from "./chunk-NFQ4LIIR.js";
48
49
  import {
49
50
  batch,
50
51
  computed,
@@ -90,6 +91,7 @@ export {
90
91
  linear,
91
92
  modHeld,
92
93
  motionValue,
94
+ platformButton,
93
95
  resolveDragModifier,
94
96
  resolveSpring,
95
97
  shapeEquals,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@n-uf/motion",
3
- "version": "26.11.0",
3
+ "version": "26.11.2",
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",
@@ -1 +0,0 @@
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"],"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 the pdfized surface'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 } 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/** 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); 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, toModifiers } from \"./modifiers\";\nimport type {\n CancelReason,\n InputGesture,\n ModifierDrag,\n ModifierKey,\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 /** 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}\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\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 const updatePinch = (time: number, modifiers: SurfacePointer[\"modifiers\"]) => {\n const p = pinch;\n const now = pinchState();\n if (!p || !now) return;\n if (!p.kind) {\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 pointer = pointerOf(e);\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 };\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 if (t) {\n t.screen = finitePoint(e.screen);\n t.time = e.time;\n }\n if (pinch && pinch.ids.includes(e.pointerId)) {\n updatePinch(e.time, toModifiers(e.modifiers));\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 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"],"mappings":"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","names":["scale","pan"]}