@weasel-js/gestures 1.11.0 → 1.12.0

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/dist/index.js CHANGED
@@ -18,7 +18,7 @@ var GESTURE_DESCRIPTORS = [
18
18
  // matches any payload, which is what an omitted arg slot means for a
19
19
  // `'free'` arg.
20
20
  { name: "drop", hasTarget: false, arg: { name: "types", values: "free" } },
21
- { name: "carry", hasTarget: false, arg: { name: "types", values: "free" } },
21
+ { name: "carry", hasTarget: true, arg: { name: "types", values: "free" } },
22
22
  { name: "paste", hasTarget: false, arg: { name: "types", values: "free" } }
23
23
  ];
24
24
  var BY_NAME = new Map(
@@ -107,6 +107,7 @@ function parseRoute(input) {
107
107
  target = "*";
108
108
  }
109
109
  const modifiers = {};
110
+ let held2;
110
111
  while (rest.length > 0) {
111
112
  const ch = rest[0];
112
113
  if (RESERVED_SIGILS.has(ch)) {
@@ -120,6 +121,13 @@ function parseRoute(input) {
120
121
  const nameMatch = /^([a-z]+)/.exec(rest);
121
122
  if (!nameMatch) throw new Error(`invalid route (sigil "${sigil}" without modifier name): ${input}`);
122
123
  const name = nameMatch[1];
124
+ if (name === HELD) {
125
+ if (gestureName !== "hover") throw new Error(`invalid route (${gestureName} takes no "${HELD}"): ${input}`);
126
+ if (held2 !== void 0) throw new Error(`invalid route (duplicate "${HELD}"): ${input}`);
127
+ held2 = sigil === "+" ? "required" : "optional";
128
+ rest = rest.slice(nameMatch[0].length).trimStart();
129
+ continue;
130
+ }
123
131
  if (!MOD_NAME_SET.has(name)) {
124
132
  throw new Error(`invalid route (unknown modifier "${name}"): ${input}`);
125
133
  }
@@ -129,8 +137,9 @@ function parseRoute(input) {
129
137
  modifiers[name] = sigil === "+" ? "required" : "optional";
130
138
  rest = rest.slice(nameMatch[0].length).trimStart();
131
139
  }
132
- return { phases, gesture: gestureName, arg, target, modifiers };
140
+ return { phases, gesture: gestureName, arg, target, modifiers, ...held2 ? { held: held2 } : {} };
133
141
  }
142
+ var HELD = "held";
134
143
  function parsePhaseList(raw, input) {
135
144
  if (raw === "") throw new Error(`invalid route (empty phase list): ${input}`);
136
145
  const tokens = raw.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
@@ -204,6 +213,7 @@ function collapseShiftPairs(routes) {
204
213
  }
205
214
  function isShiftTwin(a, b) {
206
215
  if (a.gesture !== b.gesture) return false;
216
+ if (a.held !== b.held) return false;
207
217
  if (a.arg !== b.arg) return false;
208
218
  if (a.target !== b.target) return false;
209
219
  if (!samePhases(a.phases, b.phases)) return false;
@@ -246,21 +256,23 @@ function formatRoute(r) {
246
256
  if (req === "required") out += ` +${name}`;
247
257
  else if (req === "optional") out += ` ?${name}`;
248
258
  }
259
+ if (r.held === "required") out += ` +${HELD}`;
260
+ else if (r.held === "optional") out += ` ?${HELD}`;
249
261
  return out;
250
262
  }
251
263
 
252
264
  // src/grammar/keyRouteGrammar.ts
253
265
  var OPTIONAL_SET = new Set(VALID_MOD_NAMES);
254
266
  function parseKeyRoute(input) {
255
- const [key, ...mods2] = input.split("?");
256
- if (!key) throw new Error(`invalid key route (empty key): ${input}`);
267
+ const [key2, ...mods2] = input.split("?");
268
+ if (!key2) throw new Error(`invalid key route (empty key): ${input}`);
257
269
  const seen = /* @__PURE__ */ new Set();
258
270
  for (const m of mods2) {
259
271
  if (!OPTIONAL_SET.has(m)) throw new Error(`unknown optional modifier "${m}" in ${input}`);
260
272
  if (seen.has(m)) throw new Error(`duplicate optional modifier "${m}" in ${input}`);
261
273
  seen.add(m);
262
274
  }
263
- return { key, optionalMods: mods2 };
275
+ return { key: key2, optionalMods: mods2 };
264
276
  }
265
277
  function formatKeyRoute(r) {
266
278
  return r.optionalMods.length === 0 ? r.key : `${r.key}?${r.optionalMods.join("?")}`;
@@ -366,9 +378,9 @@ function matchModifiers(e, mods2, isMac) {
366
378
  }
367
379
  return checkKey(alt, e.altKey) && checkKey(ctrl, e.ctrlKey) && checkKey(meta, e.metaKey) && checkKey(shift, e.shiftKey);
368
380
  }
369
- function checkKey(req, held) {
370
- if (req === "required") return held;
371
- if (req === "forbidden") return !held;
381
+ function checkKey(req, held2) {
382
+ if (req === "required") return held2;
383
+ if (req === "forbidden") return !held2;
372
384
  return true;
373
385
  }
374
386
  function matchKey(eventKey, specKey) {
@@ -474,6 +486,7 @@ function matchSpec(e, spec, isMac, phaseCtx) {
474
486
  case "hover": {
475
487
  if (e.kind !== "hover") return false;
476
488
  if (!matchModifiers(e, spec.mods, isMac)) return false;
489
+ if (!e.off && (e.held === true ? spec.held === void 0 : spec.held === true)) return false;
477
490
  const on = !e.off && matchTarget(e.affordance, spec.target, e.bodyTarget, e.bodyKind);
478
491
  if (spec.motion !== "leave") return on;
479
492
  return !on && e.from !== void 0 && matchTarget(e.from.affordance, spec.target, e.from.bodyTarget, e.from.bodyKind);
@@ -503,8 +516,10 @@ function matchSpec(e, spec, isMac, phaseCtx) {
503
516
  function matchArrival(e, spec, isMac) {
504
517
  if (e.kind !== spec.kind) return false;
505
518
  if (e.kind === "carry") {
506
- if (e.stage !== "enter" && e.stage !== "drop") return false;
519
+ const target = "target" in spec ? spec.target : void 0;
520
+ if (e.stage === "leave" || e.stage === "move" && target === void 0) return false;
507
521
  if (!matchIngestTypes([{ mime: e.type }], spec.types)) return false;
522
+ if (!matchTarget(e.affordance, target, e.bodyTarget, e.bodyKind)) return false;
508
523
  } else if (e.kind === "drop" || e.kind === "paste") {
509
524
  if (!matchIngestTypes(e.items, spec.types)) return false;
510
525
  }
@@ -574,7 +589,12 @@ function routeToSpec(parsed) {
574
589
  case void 0:
575
590
  return fail(`${parsed.gesture} has no GestureSpec kind`);
576
591
  case "hover":
577
- return withMods({ kind, ...arg === "leave" ? { motion: "leave" } : {}, ...t });
592
+ return withMods({
593
+ kind,
594
+ ...arg === "leave" ? { motion: "leave" } : {},
595
+ ...parsed.held ? { held: parsed.held === "required" ? true : "optional" } : {},
596
+ ...t
597
+ });
578
598
  case "click":
579
599
  case "pointerDown":
580
600
  case "doubleClick":
@@ -589,17 +609,18 @@ function routeToSpec(parsed) {
589
609
  case "key":
590
610
  case "key-held": {
591
611
  if (arg === void 0) return fail("a key spec needs a concrete key");
592
- const { key, optionalMods } = parseKeyRoute(arg);
612
+ const { key: key2, optionalMods } = parseKeyRoute(arg);
593
613
  const optional = {};
594
614
  for (const m of optionalMods) optional[m] = "optional";
595
- return withMods({ kind, key, ...optionalMods.length > 0 ? { mods: optional } : {} });
615
+ return withMods({ kind, key: key2, ...optionalMods.length > 0 ? { mods: optional } : {} });
596
616
  }
597
617
  case "multiTouch":
598
618
  case "multiTouchTap":
599
619
  if (arg === void 0) return fail(`a ${kind} spec needs concrete fingers`);
600
620
  return withMods({ kind, fingers: Number(arg) });
601
- case "drop":
602
621
  case "carry":
622
+ return withMods({ kind, ...arg ? { types: arg.split("|") } : {}, ...t });
623
+ case "drop":
603
624
  case "paste":
604
625
  return withMods({ kind, ...arg ? { types: arg.split("|") } : {} });
605
626
  }
@@ -669,6 +690,11 @@ function targetClause(target, hasTarget) {
669
690
  if (target === "empty") return " on empty canvas";
670
691
  return ` on a ${target}`;
671
692
  }
693
+ function carryOnto(target) {
694
+ if (target === void 0 || target === "*") return "onto the canvas";
695
+ if (target === "empty") return "onto empty canvas";
696
+ return `onto a ${target}`;
697
+ }
672
698
  function concreteArg(parsed) {
673
699
  return parsed.arg === "*" ? void 0 : parsed.arg;
674
700
  }
@@ -686,8 +712,8 @@ function actionClause(parsed, required) {
686
712
  case "keyUp":
687
713
  case "keyHeld": {
688
714
  const verb = parsed.gesture === "keyDown" ? "presses" : parsed.gesture === "keyUp" ? "releases" : "holds";
689
- const key = arg ?? "any key";
690
- return required.length > 0 ? `the user holds ${joinAnd(required)} and ${verb} ${key}` : `the user ${verb} ${key}`;
715
+ const key2 = arg ?? "any key";
716
+ return required.length > 0 ? `the user holds ${joinAnd(required)} and ${verb} ${key2}` : `the user ${verb} ${key2}`;
691
717
  }
692
718
  case "wheel": {
693
719
  const direction = arg === "up" ? " up" : arg === "down" ? " down" : "";
@@ -709,8 +735,11 @@ function actionClause(parsed, required) {
709
735
  return userDoes(required, `clicks${target}`);
710
736
  case "pointerDown":
711
737
  return userDoes(required, `presses${target}`);
712
- case "hover":
713
- return userDoes(required, arg === "leave" ? `moves the pointer ${leftClause(parsed.target)}` : `moves the pointer${target}`);
738
+ case "hover": {
739
+ const held2 = parsed.held === "required" ? " with a button held" : parsed.held === "optional" ? " whether or not a button is held" : "";
740
+ const where = arg === "leave" ? ` ${leftClause(parsed.target)}` : target;
741
+ return userDoes(required, `moves the pointer${where}${held2}`);
742
+ }
714
743
  case "dblTap":
715
744
  return userDoes(required, `double-taps${target}`);
716
745
  case "drag":
@@ -718,7 +747,7 @@ function actionClause(parsed, required) {
718
747
  case "drop":
719
748
  return userDoes(required, `drops ${arg ?? "any"} content onto the canvas`);
720
749
  case "carry":
721
- return userDoes(required, `carries ${arg ?? "anything"} onto the canvas`);
750
+ return userDoes(required, `carries ${arg ?? "anything"} ${carryOnto(parsed.target)}`);
722
751
  case "paste":
723
752
  return userDoes(required, `pastes ${arg ?? "any"} content`);
724
753
  }
@@ -750,10 +779,10 @@ function mergeAdjacentStrings(parts) {
750
779
  }
751
780
 
752
781
  // src/grammar/modifierComboToParsed.ts
753
- function modifierComboToParsed(key) {
754
- if (key === "default") return {};
782
+ function modifierComboToParsed(key2) {
783
+ if (key2 === "default") return {};
755
784
  const out = {};
756
- for (const part of key.split("+")) {
785
+ for (const part of key2.split("+")) {
757
786
  out[part] = "required";
758
787
  }
759
788
  return out;
@@ -887,19 +916,124 @@ function keySpecsFromShortcut(shortcut, opts = {}) {
887
916
  if (shortcut.mod) mods2.push({ name: "mod" });
888
917
  if (shortcut.shift) mods2.push(shortcut.shift === "optional" ? { name: "shift", optional: true } : { name: "shift" });
889
918
  if (shortcut.alt) mods2.push({ name: "alt" });
890
- const key = typeof shortcut.key === "string" ? shortcut.key : shortcut.key[0];
919
+ const key2 = typeof shortcut.key === "string" ? shortcut.key : shortcut.key[0];
891
920
  const keys = keySpecsFromMods(mods2, opts);
892
- return key === void 0 ? keys : [...keys, keySpecFromKey(key, opts)];
921
+ return key2 === void 0 ? keys : [...keys, keySpecFromKey(key2, opts)];
922
+ }
923
+
924
+ // src/ui/keySpecFromCode.ts
925
+ var MOD_CODES = {
926
+ ShiftLeft: "shift",
927
+ ShiftRight: "shift",
928
+ ControlLeft: "ctrl",
929
+ ControlRight: "ctrl",
930
+ AltLeft: "alt",
931
+ AltRight: "alt",
932
+ MetaLeft: "meta",
933
+ MetaRight: "meta"
934
+ };
935
+ var PRINTED = {
936
+ Backquote: "`",
937
+ Minus: "-",
938
+ Equal: "=",
939
+ BracketLeft: "[",
940
+ BracketRight: "]",
941
+ Backslash: "\\",
942
+ Semicolon: ";",
943
+ Quote: "'",
944
+ Comma: ",",
945
+ Period: ".",
946
+ Slash: "/",
947
+ IntlBackslash: "\xA7",
948
+ CapsLock: "Caps",
949
+ Fn: "fn"
950
+ };
951
+ function keySpecFromCode(code, opts = {}) {
952
+ const own = opts.labels?.[code];
953
+ if (own !== void 0) return { label: own };
954
+ const mod = MOD_CODES[code];
955
+ if (mod) return keySpecsFromMods([{ name: mod }], opts)[0];
956
+ const char = /^Key([A-Z])$/.exec(code) ?? /^Digit(\d)$/.exec(code);
957
+ if (char) return keySpecFromKey(char[1], opts);
958
+ if (code === "NumpadEnter") return keySpecFromKey("Enter", opts);
959
+ const printed = PRINTED[code];
960
+ return printed !== void 0 ? { label: printed } : keySpecFromKey(code, opts);
893
961
  }
894
962
 
895
963
  // src/ui/formatShortcut.ts
896
- var labels = (b, opts) => b ? keySpecsFromShortcut({ ...b, shift: b.shift === true }, opts)?.map((k) => k.label) : void 0;
964
+ var SHORTCUT_LEADER_SEPARATOR = " \u25B8 ";
897
965
  function formatShortcutParts(b, opts = {}) {
898
- return labels(b, opts);
966
+ const keys = keySpecsFromShortcut(b, opts);
967
+ if (!b || !keys) return void 0;
968
+ const leader = (b.leader ?? []).map((l) => ({ label: l.label, keys: keySpecsFromShortcut(l.shortcut, opts) ?? [] }));
969
+ return { leader, keys };
899
970
  }
900
971
  function formatShortcut(b, opts = {}) {
901
972
  const platform = opts.platform ?? detectPlatform();
902
- return platform === "macos" ? labels(b, { platform })?.join("") : labels(b, { platform, legend: "text" })?.join("+");
973
+ const mac = platform === "macos";
974
+ const parts = formatShortcutParts(b && { ...b, shift: b.shift === true }, mac ? { platform } : { platform, legend: "text" });
975
+ if (!parts) return void 0;
976
+ const keys = parts.keys.map((k) => k.label).join(mac ? "" : "+");
977
+ return [...parts.leader.map((l) => `${l.label}${SHORTCUT_LEADER_SEPARATOR}`), keys].join("");
978
+ }
979
+
980
+ // src/ui/keySugar.ts
981
+ var MODIFIERS = {
982
+ meta: "meta",
983
+ cmd: "meta",
984
+ command: "meta",
985
+ ctrl: "ctrl",
986
+ control: "ctrl",
987
+ alt: "alt",
988
+ option: "alt",
989
+ shift: "shift",
990
+ mod: "mod"
991
+ };
992
+ var KEY_NAMES = { space: " ", esc: "Escape" };
993
+ function parse(shortcut) {
994
+ const parts = shortcut.endsWith("++") ? [...shortcut.slice(0, -2).split("+"), "+"] : shortcut.split("+");
995
+ const raw = parts.pop();
996
+ if (!raw) throw new Error(`shortcut "${shortcut}" names no key`);
997
+ const mods2 = {};
998
+ for (const part of parts) {
999
+ const mod = MODIFIERS[part.toLowerCase()];
1000
+ if (!mod) throw new Error(`shortcut "${shortcut}": unknown modifier "${part}"`);
1001
+ mods2[mod] = true;
1002
+ }
1003
+ return { key: KEY_NAMES[raw.toLowerCase()] ?? raw, mods: mods2 };
1004
+ }
1005
+ function key(shortcut) {
1006
+ const { key: k, mods: mods2 } = parse(shortcut);
1007
+ return Object.keys(mods2).length > 0 ? { kind: "key", key: k, mods: mods2 } : { kind: "key", key: k };
1008
+ }
1009
+ function held(shortcut) {
1010
+ const { key: k, mods: mods2 } = parse(shortcut);
1011
+ return Object.keys(mods2).length > 0 ? { kind: "key-held", key: k, mods: mods2 } : { kind: "key-held", key: k };
1012
+ }
1013
+ function unbound() {
1014
+ return null;
1015
+ }
1016
+ function bindingSpec(input) {
1017
+ if (typeof input === "string") return routeToSpec(parseRoute(input));
1018
+ return input;
1019
+ }
1020
+ var MOD_ORDER3 = ["mod", "shift", "alt", "ctrl", "meta"];
1021
+ function keySpecRoute(spec) {
1022
+ const k = Array.isArray(spec.key) ? spec.key[0] : spec.key;
1023
+ const optional = MOD_ORDER3.filter((m) => spec.mods?.[m] === "optional");
1024
+ const required = MOD_ORDER3.filter((m) => spec.mods?.[m] === true);
1025
+ const arg = [k, ...optional].join("?");
1026
+ const gesture = spec.kind === "key" ? "keyDown" : "keyHeld";
1027
+ const route = [`[*:*] ${gesture}(${arg})`, ...required.map((m) => `+${m}`)].join(" ");
1028
+ return spellsKey(route, k) ? route : null;
1029
+ }
1030
+ function spellsKey(route, k) {
1031
+ try {
1032
+ const back = bindingSpec(route);
1033
+ return back !== null && (back.kind === "key" || back.kind === "key-held") && back.key === k;
1034
+ } catch {
1035
+ return false;
1036
+ }
903
1037
  }
904
1038
 
905
1039
  // src/ui/fixedSeat.ts
@@ -1166,9 +1300,9 @@ function pointerUpEvent(s, world) {
1166
1300
  function multiTouchEvent(fingers, mods2, more) {
1167
1301
  return { kind: "multitouch", fingers, ...modifiersOf(mods2), ...more };
1168
1302
  }
1169
- var positionsOf = (held) => new Map([...held].map(([id, p]) => [id, p.pos]));
1170
- function heldGeometry(held, toSurface, extra) {
1171
- const g = multiTouchGeometry([...positionsOf(held).values(), ...extra]);
1303
+ var positionsOf = (held2) => new Map([...held2].map(([id, p]) => [id, p.pos]));
1304
+ function heldGeometry(held2, toSurface, extra) {
1305
+ const g = multiTouchGeometry([...positionsOf(held2).values(), ...extra]);
1172
1306
  return { ...g, centroid: toSurface(g.centroid) };
1173
1307
  }
1174
1308
  function fireLongPress(rec, sink, observer) {
@@ -1191,43 +1325,51 @@ function thresholdsOf(opts) {
1191
1325
  })
1192
1326
  };
1193
1327
  }
1328
+ function sampleTime(clock) {
1329
+ return (s) => {
1330
+ const now = clock.now();
1331
+ const t = s.timeStamp;
1332
+ return t !== void 0 && t > 0 && t <= now ? t : now;
1333
+ };
1334
+ }
1194
1335
  function createPointerRecognizer(opts) {
1195
1336
  const { sink, clock, observer = {}, toSurface = (p) => p } = opts;
1196
1337
  const world = (s) => opts.toWorld?.(s.clientX, s.clientY) ?? { x: s.clientX, y: s.clientY };
1197
1338
  const clicks = opts.clicks ?? { last: null };
1198
- const held = /* @__PURE__ */ new Map();
1339
+ const held2 = /* @__PURE__ */ new Map();
1199
1340
  const { longPressMs, tapPx, dragPx, doubleClick } = thresholdsOf(opts);
1200
1341
  const touches = createMultiTouchEpisode(tapPx);
1201
1342
  const hold = createLongPressHold(clock, (pending) => {
1202
1343
  observer.holdChanged?.(pending);
1203
1344
  });
1204
- const geometry = (extra = []) => heldGeometry(held, toSurface, extra);
1205
- const claimForMultiTouch = (mods2) => {
1345
+ const geometry = (extra = []) => heldGeometry(held2, toSurface, extra);
1346
+ const happenedAt = sampleTime(clock);
1347
+ const claimForMultiTouch = (s) => {
1206
1348
  hold.cancel();
1207
- for (const p of held.values()) p.buffered = null;
1349
+ for (const p of held2.values()) p.buffered = null;
1208
1350
  const g = geometry();
1209
- touches.join(positionsOf(held), g.centroid, clock.now());
1210
- sink.dispatch(multiTouchEvent(held.size, mods2, g));
1351
+ touches.join(positionsOf(held2), g.centroid, happenedAt(s));
1352
+ sink.dispatch(multiTouchEvent(held2.size, s, g));
1211
1353
  };
1212
1354
  return {
1213
1355
  press(s) {
1214
1356
  const ev = pointerDownEvent(s);
1215
1357
  const rec = { pos: { x: s.clientX, y: s.clientY }, buffered: ev, down: pressFactsOf(s) };
1216
- held.set(s.pointerId, rec);
1217
- if (held.size < 2) sink.dispatch({ ...ev, stage: "press" });
1218
- if ((s.pointerType === "touch" || s.pointerType === "pen") && held.size === 1) {
1358
+ held2.set(s.pointerId, rec);
1359
+ if (held2.size < 2) sink.dispatch({ ...ev, stage: "press" });
1360
+ if ((s.pointerType === "touch" || s.pointerType === "pen") && held2.size === 1) {
1219
1361
  rec.holdType = s.pointerType;
1220
1362
  hold.arm(
1221
1363
  { pointerId: s.pointerId, pointerType: s.pointerType, press: rec.down, startedAt: clock.now(), duration: longPressMs() },
1222
1364
  () => {
1223
- fireLongPress(held.get(s.pointerId), sink, observer);
1365
+ fireLongPress(held2.get(s.pointerId), sink, observer);
1224
1366
  }
1225
1367
  );
1226
1368
  }
1227
- if (held.size >= 2) claimForMultiTouch(s);
1369
+ if (held2.size >= 2) claimForMultiTouch(s);
1228
1370
  },
1229
1371
  move(s) {
1230
- const rec = held.get(s.pointerId);
1372
+ const rec = held2.get(s.pointerId);
1231
1373
  const to = { x: s.clientX, y: s.clientY };
1232
1374
  if (rec) rec.pos = to;
1233
1375
  touches.moved(s.pointerId, to);
@@ -1237,21 +1379,21 @@ function createPointerRecognizer(opts) {
1237
1379
  hold.cancel(s.pointerId);
1238
1380
  sink.dispatch(press);
1239
1381
  }
1240
- if (held.size >= 2 && sink.multiTouchInFlight(held.size)) {
1241
- sink.dispatch(multiTouchEvent(held.size, s, geometry()));
1382
+ if (held2.size >= 2 && sink.multiTouchInFlight(held2.size)) {
1383
+ sink.dispatch(multiTouchEvent(held2.size, s, geometry()));
1242
1384
  }
1243
1385
  sink.dispatch(pointerMoveEvent(s, world(s)));
1244
1386
  },
1245
1387
  release(s) {
1246
1388
  hold.cancel(s.pointerId);
1247
- const before = held.size;
1248
- const rec = held.get(s.pointerId);
1249
- held.delete(s.pointerId);
1389
+ const before = held2.size;
1390
+ const rec = held2.get(s.pointerId);
1391
+ held2.delete(s.pointerId);
1250
1392
  if (before >= 2) sink.dispatch(multiTouchEvent(before, s, { end: "commit" }));
1251
- if (before >= 2 && held.size < 2) {
1393
+ if (before >= 2 && held2.size < 2) {
1252
1394
  const fingers = touches.finish(
1253
1395
  geometry([{ x: s.clientX, y: s.clientY }]).centroid,
1254
- clock.now(),
1396
+ happenedAt(s),
1255
1397
  longPressMs()
1256
1398
  );
1257
1399
  if (fingers !== null) sink.dispatch({ kind: "multitouchtap", fingers, ...modifiersOf(s) });
@@ -1267,13 +1409,13 @@ function createPointerRecognizer(opts) {
1267
1409
  worldY: at2.y,
1268
1410
  ...modifiersOf(s)
1269
1411
  };
1270
- return composeClick(rec.down, up, clicks, () => clock.now(), (ev) => sink.dispatch(ev), observer, doubleClick());
1412
+ return composeClick(rec.down, up, clicks, () => happenedAt(s), (ev) => sink.dispatch(ev), observer, doubleClick());
1271
1413
  },
1272
1414
  cancel(pointerId) {
1273
- const rec = held.get(pointerId);
1415
+ const rec = held2.get(pointerId);
1274
1416
  if (!rec) return;
1275
- const before = held.size;
1276
- held.delete(pointerId);
1417
+ const before = held2.size;
1418
+ held2.delete(pointerId);
1277
1419
  hold.cancel(pointerId);
1278
1420
  if (before >= 2) sink.dispatch(multiTouchEvent(before, rec.down, { end: "cancel" }));
1279
1421
  touches.abandon();
@@ -1281,7 +1423,7 @@ function createPointerRecognizer(opts) {
1281
1423
  },
1282
1424
  takeHold() {
1283
1425
  let holding = false;
1284
- for (const [id, rec] of held) {
1426
+ for (const [id, rec] of held2) {
1285
1427
  if (!rec.holdType) continue;
1286
1428
  holding = true;
1287
1429
  if (!hold.pendingFor(id)) continue;
@@ -1290,11 +1432,11 @@ function createPointerRecognizer(opts) {
1290
1432
  }
1291
1433
  return holding;
1292
1434
  },
1293
- heldCount: () => held.size,
1294
- positionOf: (pointerId) => held.get(pointerId)?.pos,
1435
+ heldCount: () => held2.size,
1436
+ positionOf: (pointerId) => held2.get(pointerId)?.pos,
1295
1437
  reset() {
1296
1438
  hold.cancel();
1297
- held.clear();
1439
+ held2.clear();
1298
1440
  touches.abandon();
1299
1441
  }
1300
1442
  };
@@ -1326,20 +1468,20 @@ var trackers = /* @__PURE__ */ new WeakMap();
1326
1468
  function attach(element) {
1327
1469
  const doc = element.ownerDocument;
1328
1470
  const win = doc.defaultView;
1329
- const held = /* @__PURE__ */ new Map();
1471
+ const held2 = /* @__PURE__ */ new Map();
1330
1472
  const down = (e) => {
1331
1473
  const p = e;
1332
- held.set(p.pointerId, !!p.buttons);
1474
+ held2.set(p.pointerId, !!p.buttons);
1333
1475
  };
1334
1476
  const up = (e) => {
1335
- held.delete(e.pointerId);
1477
+ held2.delete(e.pointerId);
1336
1478
  };
1337
1479
  const move = (e) => {
1338
1480
  const p = e;
1339
- if (held.get(p.pointerId) && p.buttons === 0) held.delete(p.pointerId);
1481
+ if (held2.get(p.pointerId) && p.buttons === 0) held2.delete(p.pointerId);
1340
1482
  };
1341
1483
  const blur = () => {
1342
- held.clear();
1484
+ held2.clear();
1343
1485
  };
1344
1486
  element.addEventListener("pointerdown", down, true);
1345
1487
  doc.addEventListener("pointerup", up, true);
@@ -1348,7 +1490,7 @@ function attach(element) {
1348
1490
  win?.addEventListener("blur", blur);
1349
1491
  return {
1350
1492
  readers: 0,
1351
- held,
1493
+ held: held2,
1352
1494
  detach() {
1353
1495
  element.removeEventListener("pointerdown", down, true);
1354
1496
  doc.removeEventListener("pointerup", up, true);
@@ -1421,6 +1563,6 @@ function focusedControlOwnsKey(e) {
1421
1563
  return activatesFocusedControl(e) || selectOwnsKey(e);
1422
1564
  }
1423
1565
 
1424
- export { ACTIVE_SIGILS, CONTROL_SELECTOR, DOUBLE_CLICK_MAX_MS, DOUBLE_CLICK_MAX_PX, DRAG_THRESHOLD_PX, GESTURE_DESCRIPTORS, LONG_PRESS_MS, MODIFIER_LABELS, MOD_NAME_SET, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, RESERVED_SIGILS, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, TAP_THRESHOLD_PX, UNKNOWN_MIME, VALID_MOD_NAMES, activatesFocusedControl, bodyPredicate, canonicalModifiers, clickEvents, collapseShiftPairs, createPointerRecognizer, describeRoute, describeRouteParts, detectPlatform, fixedSeat, focusedControlOwnsKey, formatKeyRoute, formatPhaseAtom, formatRoute, formatShortcut, formatShortcutParts, getGestureDescriptor, heldPointers, hitPredicate, isApplePlatform, isEditableTarget, isInControlWithin, isKnownGestureName, keyRouteToSpec, keySpecFromKey, keySpecsFromMods, keySpecsFromShortcut, longPressEvents, matchIngestTypes, matchKey, matchModifiers, matchPhase, matchSpec, matchTarget, mimeMatchesGlob, modifierComboToParsed, mods, multiTouchGeometry, nearestPortalHost, parseKeyRoute, parseRoute, parseTargetSpec, pastDragThreshold, routeGestureForSpecKind, routeToSpec, specKindForRouteGesture, specificity, stylusOf };
1566
+ export { ACTIVE_SIGILS, CONTROL_SELECTOR, DOUBLE_CLICK_MAX_MS, DOUBLE_CLICK_MAX_PX, DRAG_THRESHOLD_PX, GESTURE_DESCRIPTORS, LONG_PRESS_MS, MODIFIER_LABELS, MOD_NAME_SET, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, RESERVED_SIGILS, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, SHORTCUT_LEADER_SEPARATOR, TAP_THRESHOLD_PX, UNKNOWN_MIME, VALID_MOD_NAMES, activatesFocusedControl, bindingSpec, bodyPredicate, canonicalModifiers, clickEvents, collapseShiftPairs, createPointerRecognizer, describeRoute, describeRouteParts, detectPlatform, fixedSeat, focusedControlOwnsKey, formatKeyRoute, formatPhaseAtom, formatRoute, formatShortcut, formatShortcutParts, getGestureDescriptor, held, heldPointers, hitPredicate, isApplePlatform, isEditableTarget, isInControlWithin, isKnownGestureName, key, keyRouteToSpec, keySpecFromCode, keySpecFromKey, keySpecRoute, keySpecsFromMods, keySpecsFromShortcut, longPressEvents, matchIngestTypes, matchKey, matchModifiers, matchPhase, matchSpec, matchTarget, mimeMatchesGlob, modifierComboToParsed, mods, multiTouchGeometry, nearestPortalHost, parseKeyRoute, parseRoute, parseTargetSpec, pastDragThreshold, routeGestureForSpecKind, routeToSpec, specKindForRouteGesture, specificity, stylusOf, unbound };
1425
1567
  //# sourceMappingURL=index.js.map
1426
1568
  //# sourceMappingURL=index.js.map