@weasel-js/gestures 1.10.1 → 1.11.1
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.d.ts +236 -16
- package/dist/index.js +244 -34
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -18,6 +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: true, arg: { name: "types", values: "free" } },
|
|
21
22
|
{ name: "paste", hasTarget: false, arg: { name: "types", values: "free" } }
|
|
22
23
|
];
|
|
23
24
|
var BY_NAME = new Map(
|
|
@@ -106,6 +107,7 @@ function parseRoute(input) {
|
|
|
106
107
|
target = "*";
|
|
107
108
|
}
|
|
108
109
|
const modifiers = {};
|
|
110
|
+
let held;
|
|
109
111
|
while (rest.length > 0) {
|
|
110
112
|
const ch = rest[0];
|
|
111
113
|
if (RESERVED_SIGILS.has(ch)) {
|
|
@@ -119,6 +121,13 @@ function parseRoute(input) {
|
|
|
119
121
|
const nameMatch = /^([a-z]+)/.exec(rest);
|
|
120
122
|
if (!nameMatch) throw new Error(`invalid route (sigil "${sigil}" without modifier name): ${input}`);
|
|
121
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 (held !== void 0) throw new Error(`invalid route (duplicate "${HELD}"): ${input}`);
|
|
127
|
+
held = sigil === "+" ? "required" : "optional";
|
|
128
|
+
rest = rest.slice(nameMatch[0].length).trimStart();
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
122
131
|
if (!MOD_NAME_SET.has(name)) {
|
|
123
132
|
throw new Error(`invalid route (unknown modifier "${name}"): ${input}`);
|
|
124
133
|
}
|
|
@@ -128,8 +137,9 @@ function parseRoute(input) {
|
|
|
128
137
|
modifiers[name] = sigil === "+" ? "required" : "optional";
|
|
129
138
|
rest = rest.slice(nameMatch[0].length).trimStart();
|
|
130
139
|
}
|
|
131
|
-
return { phases, gesture: gestureName, arg, target, modifiers };
|
|
140
|
+
return { phases, gesture: gestureName, arg, target, modifiers, ...held ? { held } : {} };
|
|
132
141
|
}
|
|
142
|
+
var HELD = "held";
|
|
133
143
|
function parsePhaseList(raw, input) {
|
|
134
144
|
if (raw === "") throw new Error(`invalid route (empty phase list): ${input}`);
|
|
135
145
|
const tokens = raw.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
|
|
@@ -203,6 +213,7 @@ function collapseShiftPairs(routes) {
|
|
|
203
213
|
}
|
|
204
214
|
function isShiftTwin(a, b) {
|
|
205
215
|
if (a.gesture !== b.gesture) return false;
|
|
216
|
+
if (a.held !== b.held) return false;
|
|
206
217
|
if (a.arg !== b.arg) return false;
|
|
207
218
|
if (a.target !== b.target) return false;
|
|
208
219
|
if (!samePhases(a.phases, b.phases)) return false;
|
|
@@ -245,6 +256,8 @@ function formatRoute(r) {
|
|
|
245
256
|
if (req === "required") out += ` +${name}`;
|
|
246
257
|
else if (req === "optional") out += ` ?${name}`;
|
|
247
258
|
}
|
|
259
|
+
if (r.held === "required") out += ` +${HELD}`;
|
|
260
|
+
else if (r.held === "optional") out += ` ?${HELD}`;
|
|
248
261
|
return out;
|
|
249
262
|
}
|
|
250
263
|
|
|
@@ -473,6 +486,7 @@ function matchSpec(e, spec, isMac, phaseCtx) {
|
|
|
473
486
|
case "hover": {
|
|
474
487
|
if (e.kind !== "hover") return false;
|
|
475
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;
|
|
476
490
|
const on = !e.off && matchTarget(e.affordance, spec.target, e.bodyTarget, e.bodyKind);
|
|
477
491
|
if (spec.motion !== "leave") return on;
|
|
478
492
|
return !on && e.from !== void 0 && matchTarget(e.from.affordance, spec.target, e.from.bodyTarget, e.from.bodyKind);
|
|
@@ -490,21 +504,27 @@ function matchSpec(e, spec, isMac, phaseCtx) {
|
|
|
490
504
|
if (e.fingers !== spec.fingers) return false;
|
|
491
505
|
return matchModifiers(e, spec.mods, isMac);
|
|
492
506
|
}
|
|
493
|
-
case "drop":
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
return
|
|
497
|
-
}
|
|
498
|
-
case "paste": {
|
|
499
|
-
if (e.kind !== "paste") return false;
|
|
500
|
-
if (!matchIngestTypes(e.items, spec.types)) return false;
|
|
501
|
-
return matchModifiers(e, spec.mods, isMac);
|
|
502
|
-
}
|
|
507
|
+
case "drop":
|
|
508
|
+
case "carry":
|
|
509
|
+
case "paste":
|
|
510
|
+
return matchArrival(e, spec, isMac);
|
|
503
511
|
default: {
|
|
504
512
|
return false;
|
|
505
513
|
}
|
|
506
514
|
}
|
|
507
515
|
}
|
|
516
|
+
function matchArrival(e, spec, isMac) {
|
|
517
|
+
if (e.kind !== spec.kind) return false;
|
|
518
|
+
if (e.kind === "carry") {
|
|
519
|
+
const target = "target" in spec ? spec.target : void 0;
|
|
520
|
+
if (e.stage === "leave" || e.stage === "move" && target === void 0) return false;
|
|
521
|
+
if (!matchIngestTypes([{ mime: e.type }], spec.types)) return false;
|
|
522
|
+
if (!matchTarget(e.affordance, target, e.bodyTarget, e.bodyKind)) return false;
|
|
523
|
+
} else if (e.kind === "drop" || e.kind === "paste") {
|
|
524
|
+
if (!matchIngestTypes(e.items, spec.types)) return false;
|
|
525
|
+
}
|
|
526
|
+
return matchModifiers(e, spec.mods, isMac);
|
|
527
|
+
}
|
|
508
528
|
var EMPTY_ENGAGED = /* @__PURE__ */ new Set();
|
|
509
529
|
|
|
510
530
|
// src/grammar/specKinds.ts
|
|
@@ -523,6 +543,7 @@ var SPEC_KIND_TO_GESTURE = {
|
|
|
523
543
|
multiTouch: "multiTouch",
|
|
524
544
|
multiTouchTap: "multiTouchTap",
|
|
525
545
|
drop: "drop",
|
|
546
|
+
carry: "carry",
|
|
526
547
|
paste: "paste"
|
|
527
548
|
};
|
|
528
549
|
var GESTURE_TO_SPEC_KIND = new Map(
|
|
@@ -568,7 +589,12 @@ function routeToSpec(parsed) {
|
|
|
568
589
|
case void 0:
|
|
569
590
|
return fail(`${parsed.gesture} has no GestureSpec kind`);
|
|
570
591
|
case "hover":
|
|
571
|
-
return withMods({
|
|
592
|
+
return withMods({
|
|
593
|
+
kind,
|
|
594
|
+
...arg === "leave" ? { motion: "leave" } : {},
|
|
595
|
+
...parsed.held ? { held: parsed.held === "required" ? true : "optional" } : {},
|
|
596
|
+
...t
|
|
597
|
+
});
|
|
572
598
|
case "click":
|
|
573
599
|
case "pointerDown":
|
|
574
600
|
case "doubleClick":
|
|
@@ -592,6 +618,8 @@ function routeToSpec(parsed) {
|
|
|
592
618
|
case "multiTouchTap":
|
|
593
619
|
if (arg === void 0) return fail(`a ${kind} spec needs concrete fingers`);
|
|
594
620
|
return withMods({ kind, fingers: Number(arg) });
|
|
621
|
+
case "carry":
|
|
622
|
+
return withMods({ kind, ...arg ? { types: arg.split("|") } : {}, ...t });
|
|
595
623
|
case "drop":
|
|
596
624
|
case "paste":
|
|
597
625
|
return withMods({ kind, ...arg ? { types: arg.split("|") } : {} });
|
|
@@ -662,6 +690,11 @@ function targetClause(target, hasTarget) {
|
|
|
662
690
|
if (target === "empty") return " on empty canvas";
|
|
663
691
|
return ` on a ${target}`;
|
|
664
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
|
+
}
|
|
665
698
|
function concreteArg(parsed) {
|
|
666
699
|
return parsed.arg === "*" ? void 0 : parsed.arg;
|
|
667
700
|
}
|
|
@@ -702,14 +735,19 @@ function actionClause(parsed, required) {
|
|
|
702
735
|
return userDoes(required, `clicks${target}`);
|
|
703
736
|
case "pointerDown":
|
|
704
737
|
return userDoes(required, `presses${target}`);
|
|
705
|
-
case "hover":
|
|
706
|
-
|
|
738
|
+
case "hover": {
|
|
739
|
+
const held = 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}${held}`);
|
|
742
|
+
}
|
|
707
743
|
case "dblTap":
|
|
708
744
|
return userDoes(required, `double-taps${target}`);
|
|
709
745
|
case "drag":
|
|
710
746
|
return userDoes(required, `drags${target}`);
|
|
711
747
|
case "drop":
|
|
712
748
|
return userDoes(required, `drops ${arg ?? "any"} content onto the canvas`);
|
|
749
|
+
case "carry":
|
|
750
|
+
return userDoes(required, `carries ${arg ?? "anything"} ${carryOnto(parsed.target)}`);
|
|
713
751
|
case "paste":
|
|
714
752
|
return userDoes(required, `pastes ${arg ?? "any"} content`);
|
|
715
753
|
}
|
|
@@ -784,6 +822,154 @@ function isApplePlatform() {
|
|
|
784
822
|
return /mac|iphone|ipad|ipod/i.test(raw);
|
|
785
823
|
}
|
|
786
824
|
|
|
825
|
+
// src/ui/keySpecsFromMods.ts
|
|
826
|
+
var MOD_BY_PLATFORM = {
|
|
827
|
+
mod: {
|
|
828
|
+
macos: { auto: "\u2318", symbol: "\u2318", text: "Cmd" },
|
|
829
|
+
windows: { auto: "Ctrl", symbol: "\u2388", text: "Ctrl" },
|
|
830
|
+
linux: { auto: "Ctrl", symbol: "\u2388", text: "Ctrl" }
|
|
831
|
+
},
|
|
832
|
+
shift: {
|
|
833
|
+
macos: { auto: "\u21E7", symbol: "\u21E7", text: "Shift" },
|
|
834
|
+
windows: { auto: "\u21E7", symbol: "\u21E7", text: "Shift" },
|
|
835
|
+
linux: { auto: "\u21E7", symbol: "\u21E7", text: "Shift" }
|
|
836
|
+
},
|
|
837
|
+
// Off macOS the symbols are ISO/IEC 9995-7's: ⎈ for Control, ⎇ for Alt.
|
|
838
|
+
alt: {
|
|
839
|
+
macos: { auto: "\u2325", symbol: "\u2325", text: "Option" },
|
|
840
|
+
windows: { auto: "Alt", symbol: "\u2387", text: "Alt" },
|
|
841
|
+
linux: { auto: "Alt", symbol: "\u2387", text: "Alt" }
|
|
842
|
+
},
|
|
843
|
+
ctrl: {
|
|
844
|
+
macos: { auto: "\u2303", symbol: "\u2303", text: "Control" },
|
|
845
|
+
windows: { auto: "Ctrl", symbol: "\u2388", text: "Ctrl" },
|
|
846
|
+
linux: { auto: "Ctrl", symbol: "\u2388", text: "Ctrl" }
|
|
847
|
+
},
|
|
848
|
+
// Windows/Linux Meta has a recognized symbol on the physical key (⊞ Win
|
|
849
|
+
// logo / Super logo), so `auto` shows it. Use `text` for Win / Super.
|
|
850
|
+
meta: {
|
|
851
|
+
macos: { auto: "\u2318", symbol: "\u2318", text: "Cmd" },
|
|
852
|
+
windows: { auto: "\u229E", symbol: "\u229E", text: "Win" },
|
|
853
|
+
linux: { auto: "\u229E", symbol: "\u229E", text: "Super" }
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
var MODIFIER_LABELS = new Set(
|
|
857
|
+
Object.values(MOD_BY_PLATFORM).flatMap(
|
|
858
|
+
(byPlatform) => Object.values(byPlatform).flatMap((byLegend) => Object.values(byLegend))
|
|
859
|
+
)
|
|
860
|
+
);
|
|
861
|
+
function detectPlatform() {
|
|
862
|
+
if (typeof navigator === "undefined" || !navigator) return "macos";
|
|
863
|
+
if (isApplePlatform()) return "macos";
|
|
864
|
+
const nav = navigator;
|
|
865
|
+
const raw = nav.userAgentData?.platform || nav.platform || nav.userAgent || "";
|
|
866
|
+
return /\bwin/i.test(raw) ? "windows" : "linux";
|
|
867
|
+
}
|
|
868
|
+
function keySpecsFromMods(mods2, opts = {}) {
|
|
869
|
+
const platform = opts.platform ?? detectPlatform();
|
|
870
|
+
const legend = opts.legend ?? "auto";
|
|
871
|
+
return mods2.map((m) => {
|
|
872
|
+
const label = opts.labels?.[m.name] ?? MOD_BY_PLATFORM[m.name][platform][legend];
|
|
873
|
+
return m.optional ? { label, optional: true } : { label };
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
var NAMED_KEY = {
|
|
877
|
+
Escape: { macos: { auto: "\u238B", symbol: "\u238B", text: "Esc" }, windows: { auto: "Esc", symbol: "\u238B", text: "Esc" }, linux: { auto: "Esc", symbol: "\u238B", text: "Esc" } },
|
|
878
|
+
Enter: { macos: { auto: "\u21B5", symbol: "\u21B5", text: "Return" }, windows: { auto: "Enter", symbol: "\u21B5", text: "Enter" }, linux: { auto: "Enter", symbol: "\u21B5", text: "Enter" } },
|
|
879
|
+
Return: { macos: { auto: "\u21B5", symbol: "\u21B5", text: "Return" }, windows: { auto: "Enter", symbol: "\u21B5", text: "Enter" }, linux: { auto: "Enter", symbol: "\u21B5", text: "Enter" } },
|
|
880
|
+
Tab: { macos: { auto: "\u21E5", symbol: "\u21E5", text: "Tab" }, windows: { auto: "Tab", symbol: "\u21E5", text: "Tab" }, linux: { auto: "Tab", symbol: "\u21E5", text: "Tab" } },
|
|
881
|
+
Space: { macos: { auto: "\u2423", symbol: "\u2423", text: "Space" }, windows: { auto: "Space", symbol: "\u2423", text: "Space" }, linux: { auto: "Space", symbol: "\u2423", text: "Space" } },
|
|
882
|
+
" ": { macos: { auto: "\u2423", symbol: "\u2423", text: "Space" }, windows: { auto: "Space", symbol: "\u2423", text: "Space" }, linux: { auto: "Space", symbol: "\u2423", text: "Space" } },
|
|
883
|
+
Backspace: { macos: { auto: "\u232B", symbol: "\u232B", text: "Backspace" }, windows: { auto: "Backspace", symbol: "\u27F5", text: "Backspace" }, linux: { auto: "Backspace", symbol: "\u27F5", text: "Backspace" } },
|
|
884
|
+
Delete: { macos: { auto: "\u2326", symbol: "\u2326", text: "Delete" }, windows: { auto: "Delete", symbol: "\u2326", text: "Delete" }, linux: { auto: "Delete", symbol: "\u2326", text: "Delete" } },
|
|
885
|
+
// Context-menu key (the right-Ctrl-area key on Windows / Linux keyboards
|
|
886
|
+
// with a ▤ icon). Mac has no equivalent; falls back to text 'Menu'.
|
|
887
|
+
ContextMenu: { macos: { auto: "Menu", symbol: "Menu", text: "Menu" }, windows: { auto: "\u25A4", symbol: "\u25A4", text: "Menu" }, linux: { auto: "\u25A4", symbol: "\u25A4", text: "Menu" } },
|
|
888
|
+
// Symbol mode only: Apple's arrows for Home, End, and the page keys, and
|
|
889
|
+
// ISO/IEC 9995-7's symbols for the rest.
|
|
890
|
+
Home: { macos: { auto: "Home", symbol: "\u2196", text: "Home" }, windows: { auto: "Home", symbol: "\u2196", text: "Home" }, linux: { auto: "Home", symbol: "\u2196", text: "Home" } },
|
|
891
|
+
End: { macos: { auto: "End", symbol: "\u2198", text: "End" }, windows: { auto: "End", symbol: "\u2198", text: "End" }, linux: { auto: "End", symbol: "\u2198", text: "End" } },
|
|
892
|
+
PageUp: { macos: { auto: "PgUp", symbol: "\u21DE", text: "PgUp" }, windows: { auto: "PgUp", symbol: "\u21DE", text: "PgUp" }, linux: { auto: "PgUp", symbol: "\u21DE", text: "PgUp" } },
|
|
893
|
+
PageDown: { macos: { auto: "PgDn", symbol: "\u21DF", text: "PgDn" }, windows: { auto: "PgDn", symbol: "\u21DF", text: "PgDn" }, linux: { auto: "PgDn", symbol: "\u21DF", text: "PgDn" } },
|
|
894
|
+
PrintScreen: { macos: { auto: "PrtSc", symbol: "\u2399", text: "PrtSc" }, windows: { auto: "PrtSc", symbol: "\u2399", text: "PrtSc" }, linux: { auto: "PrtSc", symbol: "\u2399", text: "PrtSc" } },
|
|
895
|
+
Insert: { macos: { auto: "Ins", symbol: "\u2380", text: "Insert" }, windows: { auto: "Ins", symbol: "\u2380", text: "Insert" }, linux: { auto: "Ins", symbol: "\u2380", text: "Insert" } },
|
|
896
|
+
ScrollLock: { macos: { auto: "ScrLk", symbol: "\u21F3", text: "ScrLk" }, windows: { auto: "ScrLk", symbol: "\u21F3", text: "ScrLk" }, linux: { auto: "ScrLk", symbol: "\u21F3", text: "ScrLk" } },
|
|
897
|
+
Pause: { macos: { auto: "Pause", symbol: "\u2389", text: "Pause" }, windows: { auto: "Pause", symbol: "\u2389", text: "Pause" }, linux: { auto: "Pause", symbol: "\u2389", text: "Pause" } },
|
|
898
|
+
ArrowUp: { macos: { auto: "\u2191", symbol: "\u2191", text: "\u2191" }, windows: { auto: "\u2191", symbol: "\u2191", text: "\u2191" }, linux: { auto: "\u2191", symbol: "\u2191", text: "\u2191" } },
|
|
899
|
+
ArrowDown: { macos: { auto: "\u2193", symbol: "\u2193", text: "\u2193" }, windows: { auto: "\u2193", symbol: "\u2193", text: "\u2193" }, linux: { auto: "\u2193", symbol: "\u2193", text: "\u2193" } },
|
|
900
|
+
ArrowLeft: { macos: { auto: "\u2190", symbol: "\u2190", text: "\u2190" }, windows: { auto: "\u2190", symbol: "\u2190", text: "\u2190" }, linux: { auto: "\u2190", symbol: "\u2190", text: "\u2190" } },
|
|
901
|
+
ArrowRight: { macos: { auto: "\u2192", symbol: "\u2192", text: "\u2192" }, windows: { auto: "\u2192", symbol: "\u2192", text: "\u2192" }, linux: { auto: "\u2192", symbol: "\u2192", text: "\u2192" } }
|
|
902
|
+
};
|
|
903
|
+
function keySpecFromKey(raw, opts = {}) {
|
|
904
|
+
const platform = opts.platform ?? detectPlatform();
|
|
905
|
+
const legend = opts.legend ?? "auto";
|
|
906
|
+
const entry = NAMED_KEY[raw];
|
|
907
|
+
const override = opts.labels?.[raw] ?? (raw.length === 1 ? opts.labels?.[raw.toUpperCase()] : void 0);
|
|
908
|
+
const label = override ?? (entry ? entry[platform][legend] : raw.length === 1 ? raw.toUpperCase() : raw);
|
|
909
|
+
return opts.optional ? { label, optional: true } : { label };
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// src/ui/keySpecsFromShortcut.ts
|
|
913
|
+
function keySpecsFromShortcut(shortcut, opts = {}) {
|
|
914
|
+
if (!shortcut) return void 0;
|
|
915
|
+
const mods2 = [];
|
|
916
|
+
if (shortcut.mod) mods2.push({ name: "mod" });
|
|
917
|
+
if (shortcut.shift) mods2.push(shortcut.shift === "optional" ? { name: "shift", optional: true } : { name: "shift" });
|
|
918
|
+
if (shortcut.alt) mods2.push({ name: "alt" });
|
|
919
|
+
const key = typeof shortcut.key === "string" ? shortcut.key : shortcut.key[0];
|
|
920
|
+
const keys = keySpecsFromMods(mods2, opts);
|
|
921
|
+
return key === void 0 ? keys : [...keys, keySpecFromKey(key, 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);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// src/ui/formatShortcut.ts
|
|
964
|
+
var labels = (b, opts) => b ? keySpecsFromShortcut({ ...b, shift: b.shift === true }, opts)?.map((k) => k.label) : void 0;
|
|
965
|
+
function formatShortcutParts(b, opts = {}) {
|
|
966
|
+
return labels(b, opts);
|
|
967
|
+
}
|
|
968
|
+
function formatShortcut(b, opts = {}) {
|
|
969
|
+
const platform = opts.platform ?? detectPlatform();
|
|
970
|
+
return platform === "macos" ? labels(b, { platform })?.join("") : labels(b, { platform, legend: "text" })?.join("+");
|
|
971
|
+
}
|
|
972
|
+
|
|
787
973
|
// src/ui/fixedSeat.ts
|
|
788
974
|
var HOST_SELECTOR = "[data-wzl-portal-host],[data-wzl-theme],[data-wzl-mode]";
|
|
789
975
|
function nearestPortalHost(from) {
|
|
@@ -849,7 +1035,7 @@ function specificity(spec) {
|
|
|
849
1035
|
const mods2 = "mods" in spec ? spec.mods : void 0;
|
|
850
1036
|
const m = modsCount(mods2);
|
|
851
1037
|
const p = phaseRank("phase" in spec ? spec.phase : void 0);
|
|
852
|
-
const typed = (spec.kind === "drop" || spec.kind === "paste") && spec.types !== void 0 && spec.types.length > 0 ? 2 : 1;
|
|
1038
|
+
const typed = (spec.kind === "drop" || spec.kind === "carry" || spec.kind === "paste") && spec.types !== void 0 && spec.types.length > 0 ? 2 : 1;
|
|
853
1039
|
return [t, m, p, typed];
|
|
854
1040
|
}
|
|
855
1041
|
|
|
@@ -883,12 +1069,16 @@ function clickEvents(down, up) {
|
|
|
883
1069
|
x: up.worldX,
|
|
884
1070
|
y: up.worldY,
|
|
885
1071
|
clientX: up.clientX,
|
|
886
|
-
clientY: up.clientY
|
|
887
|
-
...pressedOn(down)
|
|
1072
|
+
clientY: up.clientY
|
|
888
1073
|
};
|
|
1074
|
+
const named = down.doubleClickBody;
|
|
889
1075
|
return {
|
|
890
|
-
click: { kind: "click", ...shared, pressX: down.worldX, pressY: down.worldY },
|
|
891
|
-
doubleclick: {
|
|
1076
|
+
click: { kind: "click", ...shared, ...pressedOn(down), pressX: down.worldX, pressY: down.worldY },
|
|
1077
|
+
doubleclick: {
|
|
1078
|
+
kind: "doubleclick",
|
|
1079
|
+
...shared,
|
|
1080
|
+
...pressedOn(named ? { affordance: down.affordance, ...named } : down)
|
|
1081
|
+
}
|
|
892
1082
|
};
|
|
893
1083
|
}
|
|
894
1084
|
function longPressEvents(down) {
|
|
@@ -908,14 +1098,14 @@ function longPressEvents(down) {
|
|
|
908
1098
|
}
|
|
909
1099
|
|
|
910
1100
|
// src/recognizer/click.ts
|
|
911
|
-
function composeClick(down, up, clicks, now, dispatch, observer) {
|
|
1101
|
+
function composeClick(down, up, clicks, now, dispatch, observer, limits = { maxMs: DOUBLE_CLICK_MAX_MS, maxPx: DOUBLE_CLICK_MAX_PX }) {
|
|
912
1102
|
const evs = clickEvents(down, up);
|
|
913
1103
|
const world = { x: up.worldX, y: up.worldY };
|
|
914
1104
|
dispatch(evs.click);
|
|
915
1105
|
observer.click?.(world);
|
|
916
1106
|
const t = now();
|
|
917
1107
|
const prev = clicks.last;
|
|
918
|
-
const follows = prev !== null && t - prev.t <
|
|
1108
|
+
const follows = prev !== null && t - prev.t < limits.maxMs && Math.hypot(up.clientX - prev.clientX, up.clientY - prev.clientY) < limits.maxPx;
|
|
919
1109
|
if (!follows) {
|
|
920
1110
|
clicks.last = { t, clientX: up.clientX, clientY: up.clientY };
|
|
921
1111
|
return {};
|
|
@@ -969,7 +1159,7 @@ function multiTouchGeometry(positions) {
|
|
|
969
1159
|
angle: Math.atan2(dy, dx) * 180 / Math.PI
|
|
970
1160
|
};
|
|
971
1161
|
}
|
|
972
|
-
function createMultiTouchEpisode() {
|
|
1162
|
+
function createMultiTouchEpisode(tapPx = () => TAP_THRESHOLD_PX) {
|
|
973
1163
|
let episode = null;
|
|
974
1164
|
return {
|
|
975
1165
|
join(positions, centroid, now) {
|
|
@@ -990,14 +1180,14 @@ function createMultiTouchEpisode() {
|
|
|
990
1180
|
},
|
|
991
1181
|
moved(pointerId, to) {
|
|
992
1182
|
const from = episode?.joinedAt.get(pointerId);
|
|
993
|
-
if (episode && from && Math.hypot(to.x - from.x, to.y - from.y) >
|
|
1183
|
+
if (episode && from && Math.hypot(to.x - from.x, to.y - from.y) > tapPx()) episode.moved = true;
|
|
994
1184
|
},
|
|
995
1185
|
finish(centroid, now, maxHoldMs) {
|
|
996
1186
|
const e = episode;
|
|
997
1187
|
episode = null;
|
|
998
1188
|
if (!e) return null;
|
|
999
1189
|
const brief = now - e.startedAt < maxHoldMs;
|
|
1000
|
-
const still = Math.hypot(centroid.x - e.centroid.x, centroid.y - e.centroid.y) <=
|
|
1190
|
+
const still = Math.hypot(centroid.x - e.centroid.x, centroid.y - e.centroid.y) <= tapPx();
|
|
1001
1191
|
return brief && !e.moved && still ? e.fingers : null;
|
|
1002
1192
|
},
|
|
1003
1193
|
abandon() {
|
|
@@ -1018,7 +1208,8 @@ function pressFactsOf(s) {
|
|
|
1018
1208
|
worldX: s.x,
|
|
1019
1209
|
worldY: s.y,
|
|
1020
1210
|
...modifiersOf(s),
|
|
1021
|
-
...pressedOn(s)
|
|
1211
|
+
...pressedOn(s),
|
|
1212
|
+
...s.doubleClickBody !== void 0 ? { doubleClickBody: s.doubleClickBody } : {}
|
|
1022
1213
|
};
|
|
1023
1214
|
}
|
|
1024
1215
|
var at = (s, world) => ({
|
|
@@ -1057,23 +1248,42 @@ function fireLongPress(rec, sink, observer) {
|
|
|
1057
1248
|
rec.buffered = null;
|
|
1058
1249
|
if (rec.holdType) observer.longPress?.(rec.holdType);
|
|
1059
1250
|
}
|
|
1251
|
+
function thresholdsOf(opts) {
|
|
1252
|
+
return {
|
|
1253
|
+
longPressMs: () => opts.longPressMs?.() ?? LONG_PRESS_MS,
|
|
1254
|
+
tapPx: () => opts.tapPx?.() ?? TAP_THRESHOLD_PX,
|
|
1255
|
+
dragPx: () => opts.dragPx?.() ?? DRAG_THRESHOLD_PX,
|
|
1256
|
+
doubleClick: () => ({
|
|
1257
|
+
maxMs: opts.doubleClickMs?.() ?? DOUBLE_CLICK_MAX_MS,
|
|
1258
|
+
maxPx: opts.doubleClickPx?.() ?? DOUBLE_CLICK_MAX_PX
|
|
1259
|
+
})
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
function sampleTime(clock) {
|
|
1263
|
+
return (s) => {
|
|
1264
|
+
const now = clock.now();
|
|
1265
|
+
const t = s.timeStamp;
|
|
1266
|
+
return t !== void 0 && t > 0 && t <= now ? t : now;
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1060
1269
|
function createPointerRecognizer(opts) {
|
|
1061
1270
|
const { sink, clock, observer = {}, toSurface = (p) => p } = opts;
|
|
1062
1271
|
const world = (s) => opts.toWorld?.(s.clientX, s.clientY) ?? { x: s.clientX, y: s.clientY };
|
|
1063
1272
|
const clicks = opts.clicks ?? { last: null };
|
|
1064
1273
|
const held = /* @__PURE__ */ new Map();
|
|
1065
|
-
const
|
|
1274
|
+
const { longPressMs, tapPx, dragPx, doubleClick } = thresholdsOf(opts);
|
|
1275
|
+
const touches = createMultiTouchEpisode(tapPx);
|
|
1066
1276
|
const hold = createLongPressHold(clock, (pending) => {
|
|
1067
1277
|
observer.holdChanged?.(pending);
|
|
1068
1278
|
});
|
|
1069
|
-
const longPressMs = () => opts.longPressMs?.() ?? LONG_PRESS_MS;
|
|
1070
1279
|
const geometry = (extra = []) => heldGeometry(held, toSurface, extra);
|
|
1071
|
-
const
|
|
1280
|
+
const happenedAt = sampleTime(clock);
|
|
1281
|
+
const claimForMultiTouch = (s) => {
|
|
1072
1282
|
hold.cancel();
|
|
1073
1283
|
for (const p of held.values()) p.buffered = null;
|
|
1074
1284
|
const g = geometry();
|
|
1075
|
-
touches.join(positionsOf(held), g.centroid,
|
|
1076
|
-
sink.dispatch(multiTouchEvent(held.size,
|
|
1285
|
+
touches.join(positionsOf(held), g.centroid, happenedAt(s));
|
|
1286
|
+
sink.dispatch(multiTouchEvent(held.size, s, g));
|
|
1077
1287
|
};
|
|
1078
1288
|
return {
|
|
1079
1289
|
press(s) {
|
|
@@ -1097,7 +1307,7 @@ function createPointerRecognizer(opts) {
|
|
|
1097
1307
|
const to = { x: s.clientX, y: s.clientY };
|
|
1098
1308
|
if (rec) rec.pos = to;
|
|
1099
1309
|
touches.moved(s.pointerId, to);
|
|
1100
|
-
if (rec?.buffered && pastDragThreshold(rec.down, s)) {
|
|
1310
|
+
if (rec?.buffered && pastDragThreshold(rec.down, s, dragPx())) {
|
|
1101
1311
|
const press = rec.buffered;
|
|
1102
1312
|
rec.buffered = null;
|
|
1103
1313
|
hold.cancel(s.pointerId);
|
|
@@ -1117,7 +1327,7 @@ function createPointerRecognizer(opts) {
|
|
|
1117
1327
|
if (before >= 2 && held.size < 2) {
|
|
1118
1328
|
const fingers = touches.finish(
|
|
1119
1329
|
geometry([{ x: s.clientX, y: s.clientY }]).centroid,
|
|
1120
|
-
|
|
1330
|
+
happenedAt(s),
|
|
1121
1331
|
longPressMs()
|
|
1122
1332
|
);
|
|
1123
1333
|
if (fingers !== null) sink.dispatch({ kind: "multitouchtap", fingers, ...modifiersOf(s) });
|
|
@@ -1133,7 +1343,7 @@ function createPointerRecognizer(opts) {
|
|
|
1133
1343
|
worldY: at2.y,
|
|
1134
1344
|
...modifiersOf(s)
|
|
1135
1345
|
};
|
|
1136
|
-
return composeClick(rec.down, up, clicks, () =>
|
|
1346
|
+
return composeClick(rec.down, up, clicks, () => happenedAt(s), (ev) => sink.dispatch(ev), observer, doubleClick());
|
|
1137
1347
|
},
|
|
1138
1348
|
cancel(pointerId) {
|
|
1139
1349
|
const rec = held.get(pointerId);
|
|
@@ -1287,6 +1497,6 @@ function focusedControlOwnsKey(e) {
|
|
|
1287
1497
|
return activatesFocusedControl(e) || selectOwnsKey(e);
|
|
1288
1498
|
}
|
|
1289
1499
|
|
|
1290
|
-
export { ACTIVE_SIGILS, CONTROL_SELECTOR, DOUBLE_CLICK_MAX_MS, DOUBLE_CLICK_MAX_PX, DRAG_THRESHOLD_PX, GESTURE_DESCRIPTORS, LONG_PRESS_MS, 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, fixedSeat, focusedControlOwnsKey, formatKeyRoute, formatPhaseAtom, formatRoute, getGestureDescriptor, heldPointers, hitPredicate, isApplePlatform, isEditableTarget, isInControlWithin, isKnownGestureName, keyRouteToSpec, longPressEvents, matchIngestTypes, matchKey, matchModifiers, matchPhase, matchSpec, matchTarget, mimeMatchesGlob, modifierComboToParsed, mods, multiTouchGeometry, nearestPortalHost, parseKeyRoute, parseRoute, parseTargetSpec, pastDragThreshold, routeGestureForSpecKind, routeToSpec, specKindForRouteGesture, specificity, stylusOf };
|
|
1500
|
+
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, keySpecFromCode, keySpecFromKey, keySpecsFromMods, keySpecsFromShortcut, longPressEvents, matchIngestTypes, matchKey, matchModifiers, matchPhase, matchSpec, matchTarget, mimeMatchesGlob, modifierComboToParsed, mods, multiTouchGeometry, nearestPortalHost, parseKeyRoute, parseRoute, parseTargetSpec, pastDragThreshold, routeGestureForSpecKind, routeToSpec, specKindForRouteGesture, specificity, stylusOf };
|
|
1291
1501
|
//# sourceMappingURL=index.js.map
|
|
1292
1502
|
//# sourceMappingURL=index.js.map
|