@weasel-js/gestures 1.11.1 → 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.d.ts +43 -9
- package/dist/index.js +124 -58
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1164,15 +1164,31 @@ type ShortcutInput = MenuShortcut;
|
|
|
1164
1164
|
interface FormatShortcutOptions {
|
|
1165
1165
|
/** Defaults to the detected platform. */
|
|
1166
1166
|
platform?: Platform;
|
|
1167
|
+
/** {@link formatShortcutParts} only; {@link formatShortcut} picks its own. Defaults to `'auto'`. */
|
|
1168
|
+
legend?: LegendStyle;
|
|
1167
1169
|
}
|
|
1168
|
-
/**
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1170
|
+
/** One keymap on the way to a key: its label, and the chips of the leader that opens it. */
|
|
1171
|
+
interface ShortcutLeaderStep {
|
|
1172
|
+
label: string;
|
|
1173
|
+
keys: readonly KeyLegend[];
|
|
1174
|
+
}
|
|
1175
|
+
/** A shortcut as display chips: {@link formatShortcutParts}'s result. */
|
|
1176
|
+
interface ShortcutParts {
|
|
1177
|
+
/** The keymaps leading to the key, outermost first; empty for a key outside any keymap. */
|
|
1178
|
+
leader: readonly ShortcutLeaderStep[];
|
|
1179
|
+
/** The key's own chips: the modifiers, then the key. */
|
|
1180
|
+
keys: readonly KeyLegend[];
|
|
1181
|
+
}
|
|
1182
|
+
/** What {@link formatShortcut} writes after each leader step's label: `Loupe ▸ 2`. */
|
|
1183
|
+
declare const SHORTCUT_LEADER_SEPARATOR = " \u25B8 ";
|
|
1184
|
+
/** Format a shortcut as display chips, one per key: the modifiers, then the
|
|
1185
|
+
* key, each as the platform's keyboard prints it, so `mod` is `⌘` on macOS
|
|
1186
|
+
* and `Ctrl` elsewhere, after the keymaps that lead to it (`Loupe ▸ 2`).
|
|
1187
|
+
* UIs that want a boxed per-key look render each chip in its own element.
|
|
1188
|
+
* `shift: 'optional'` gives an optional chip. */
|
|
1189
|
+
declare function formatShortcutParts(b: ShortcutInput | undefined, opts?: FormatShortcutOptions): ShortcutParts | undefined;
|
|
1174
1190
|
/** Format a shortcut as a single display string. Order: mod, shift, alt,
|
|
1175
|
-
* key. On macOS the glyphs run together (`⌘⇧Z`); elsewhere the keys are
|
|
1191
|
+
* key, after each keymap that leads to it (`Loupe ▸ ⌘2`). On macOS the glyphs run together (`⌘⇧Z`); elsewhere the keys are
|
|
1176
1192
|
* spelled out and joined with `+` (`Ctrl+Shift+Z`). Returns `undefined` for
|
|
1177
1193
|
* `undefined` input so callers can `??` a fallback. When `key` is an array,
|
|
1178
1194
|
* the first element is used. `shift: 'optional'` is treated as falsy. */
|
|
@@ -1181,7 +1197,7 @@ declare function formatShortcut(b: ShortcutInput | undefined, opts?: FormatShort
|
|
|
1181
1197
|
/**
|
|
1182
1198
|
* A shortcut as key legends, one per key, as `@weasel-js/ui`'s `KeySequence` draws them: mod, shift, alt, then the
|
|
1183
1199
|
* key, each in the platform's own legend (`⌘` on macOS, `Ctrl` elsewhere).
|
|
1184
|
-
* `formatShortcutParts`
|
|
1200
|
+
* Only the key itself: `formatShortcutParts` adds the keymaps that lead to it. `shift: 'optional'`
|
|
1185
1201
|
* becomes an optional key; a key list
|
|
1186
1202
|
* renders its first key. Returns `undefined` for no shortcut.
|
|
1187
1203
|
*
|
|
@@ -1199,6 +1215,24 @@ declare function keySpecsFromShortcut(shortcut: ShortcutInput | undefined, opts?
|
|
|
1199
1215
|
*/
|
|
1200
1216
|
declare function keySpecFromCode(code: string, opts?: KeySpecsFromModsOptions): KeyLegend;
|
|
1201
1217
|
|
|
1218
|
+
/** A keystroke in the `Mod+Shift+K` form. Modifiers are case-insensitive:
|
|
1219
|
+
* `Meta`/`Cmd`/`Command`, `Ctrl`/`Control`, `Alt`/`Option`, `Shift`, and
|
|
1220
|
+
* `Mod` for Cmd on Apple platforms and Ctrl elsewhere. */
|
|
1221
|
+
declare function key(shortcut: string): KeySpec;
|
|
1222
|
+
/** The same keystroke held down, as a `key-held` spec. */
|
|
1223
|
+
declare function held(shortcut: string): KeyHeldSpec;
|
|
1224
|
+
/** A binding override that removes the binding. */
|
|
1225
|
+
declare function unbound(): null;
|
|
1226
|
+
/** What a binding override is written as: a route string, a spec, or `null` to unbind. */
|
|
1227
|
+
type BindingOverrideInput = string | GestureSpec | null;
|
|
1228
|
+
/** Read a binding override into the spec it names, or `null`. */
|
|
1229
|
+
declare function bindingSpec(input: BindingOverrideInput): GestureSpec | null;
|
|
1230
|
+
/** A key spec as its route string — what `bindingSpec` reads back to an
|
|
1231
|
+
* equal spec — or `null` for a key the route grammar cannot spell (`?`,
|
|
1232
|
+
* `)`, `*`). A spec is plain JSON, so store that one as it is. A key list is
|
|
1233
|
+
* written as its first key. */
|
|
1234
|
+
declare function keySpecRoute(spec: KeySpec | KeyHeldSpec): string | null;
|
|
1235
|
+
|
|
1202
1236
|
/**
|
|
1203
1237
|
* The nearest element `from` sits inside that can host a portalled overlay,
|
|
1204
1238
|
* or `null` when there is none and the body is the only answer.
|
|
@@ -1523,4 +1557,4 @@ declare function activatesFocusedControl(e: KeyboardEvent): boolean;
|
|
|
1523
1557
|
*/
|
|
1524
1558
|
declare function focusedControlOwnsKey(e: KeyboardEvent): boolean;
|
|
1525
1559
|
|
|
1526
|
-
export { ACTIVE_SIGILS, type BodyClassification, type BodyKind, type BodyTarget, CONTROL_SELECTOR, type CarryEvent, type CarrySpec, type ChannelRef, type ClaimableGesture, type ClickEvent, type ClickMemory, type ClickObserver, type ClickSpec, type ContextMenuEvent, type ContextMenuSpec, DOUBLE_CLICK_MAX_MS, DOUBLE_CLICK_MAX_PX, DRAG_THRESHOLD_PX, type DescribeRouteOptions, type DoubleClickBody, type DoubleClickEvent, type DoubleClickSpec, type DragSpec, type DropEvent, type DropSpec, type EventModifiers, type FixedSeat, type FormatShortcutOptions, GESTURE_DESCRIPTORS, type GestureArgSpec, type GestureDescriptor, type GestureName, type GestureSpec, type HeldPointers, type HeldPress, type HoverEvent, type HoverSpec, type HoverWhere, type IngestItem, type InputEvent, type KeyEvent, type KeyHeldEvent, type KeyHeldSpec, type KeyLabels, type KeyLegend, type KeySpec, type KeySpecsFromModsOptions, LONG_PRESS_MS, type LegendStyle, type LogicalMod, type LogicalModSpec, type LongPressEvent, type LongPressSpec, MODIFIER_LABELS, MOD_NAME_SET, type ModRequirement, type ModSpec, type ModifierCombo, type ModifierKey, type ModifiersEvent, type MultiTouchEvent, type MultiTouchGeometry, type MultiTouchSpec, type MultiTouchTapEvent, type MultiTouchTapSpec, type OptionalMod, type ParsedKeyRoute, type ParsedModifiers, type ParsedRoute, type PasteEvent, type PasteSpec, type PhaseAtom, type PhaseContext, type PhaseSpec, type PinchEvent, type PinchSpec, type Platform, type Point, type PointerCancelEvent, type PointerDownEvent, type PointerDownSpec, type PointerIdentity, type PointerMoveEvent, type PointerRecognizer, type PointerRecognizerOptions, type PointerSample, type PointerSampleStylus, type PointerUpEvent, type PressFacts, type PressSample, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, RESERVED_SIGILS, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, type RecognizerClock, type RecognizerObserver, type RecognizerSink, type ReleaseFacts, type ReleaseOutcome, type RouteDescriptionPart, type RouteFieldName, type RoutePhase, type RouteTermLabel, type ShortcutInput, TAP_THRESHOLD_PX, type TargetPredicate, type TargetSpec, type TargetSpecForm, UNKNOWN_MIME, VALID_MOD_NAMES, type WheelEvent, type WheelSpec, 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 };
|
|
1560
|
+
export { ACTIVE_SIGILS, type BindingOverrideInput, type BodyClassification, type BodyKind, type BodyTarget, CONTROL_SELECTOR, type CarryEvent, type CarrySpec, type ChannelRef, type ClaimableGesture, type ClickEvent, type ClickMemory, type ClickObserver, type ClickSpec, type ContextMenuEvent, type ContextMenuSpec, DOUBLE_CLICK_MAX_MS, DOUBLE_CLICK_MAX_PX, DRAG_THRESHOLD_PX, type DescribeRouteOptions, type DoubleClickBody, type DoubleClickEvent, type DoubleClickSpec, type DragSpec, type DropEvent, type DropSpec, type EventModifiers, type FixedSeat, type FormatShortcutOptions, GESTURE_DESCRIPTORS, type GestureArgSpec, type GestureDescriptor, type GestureName, type GestureSpec, type HeldPointers, type HeldPress, type HoverEvent, type HoverSpec, type HoverWhere, type IngestItem, type InputEvent, type KeyEvent, type KeyHeldEvent, type KeyHeldSpec, type KeyLabels, type KeyLegend, type KeySpec, type KeySpecsFromModsOptions, LONG_PRESS_MS, type LegendStyle, type LogicalMod, type LogicalModSpec, type LongPressEvent, type LongPressSpec, MODIFIER_LABELS, MOD_NAME_SET, type ModRequirement, type ModSpec, type ModifierCombo, type ModifierKey, type ModifiersEvent, type MultiTouchEvent, type MultiTouchGeometry, type MultiTouchSpec, type MultiTouchTapEvent, type MultiTouchTapSpec, type OptionalMod, type ParsedKeyRoute, type ParsedModifiers, type ParsedRoute, type PasteEvent, type PasteSpec, type PhaseAtom, type PhaseContext, type PhaseSpec, type PinchEvent, type PinchSpec, type Platform, type Point, type PointerCancelEvent, type PointerDownEvent, type PointerDownSpec, type PointerIdentity, type PointerMoveEvent, type PointerRecognizer, type PointerRecognizerOptions, type PointerSample, type PointerSampleStylus, type PointerUpEvent, type PressFacts, type PressSample, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, RESERVED_SIGILS, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, type RecognizerClock, type RecognizerObserver, type RecognizerSink, type ReleaseFacts, type ReleaseOutcome, type RouteDescriptionPart, type RouteFieldName, type RoutePhase, type RouteTermLabel, SHORTCUT_LEADER_SEPARATOR, type ShortcutInput, type ShortcutLeaderStep, type ShortcutParts, TAP_THRESHOLD_PX, type TargetPredicate, type TargetSpec, type TargetSpecForm, UNKNOWN_MIME, VALID_MOD_NAMES, type WheelEvent, type WheelSpec, 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 };
|
package/dist/index.js
CHANGED
|
@@ -107,7 +107,7 @@ function parseRoute(input) {
|
|
|
107
107
|
target = "*";
|
|
108
108
|
}
|
|
109
109
|
const modifiers = {};
|
|
110
|
-
let
|
|
110
|
+
let held2;
|
|
111
111
|
while (rest.length > 0) {
|
|
112
112
|
const ch = rest[0];
|
|
113
113
|
if (RESERVED_SIGILS.has(ch)) {
|
|
@@ -123,8 +123,8 @@ function parseRoute(input) {
|
|
|
123
123
|
const name = nameMatch[1];
|
|
124
124
|
if (name === HELD) {
|
|
125
125
|
if (gestureName !== "hover") throw new Error(`invalid route (${gestureName} takes no "${HELD}"): ${input}`);
|
|
126
|
-
if (
|
|
127
|
-
|
|
126
|
+
if (held2 !== void 0) throw new Error(`invalid route (duplicate "${HELD}"): ${input}`);
|
|
127
|
+
held2 = sigil === "+" ? "required" : "optional";
|
|
128
128
|
rest = rest.slice(nameMatch[0].length).trimStart();
|
|
129
129
|
continue;
|
|
130
130
|
}
|
|
@@ -137,7 +137,7 @@ function parseRoute(input) {
|
|
|
137
137
|
modifiers[name] = sigil === "+" ? "required" : "optional";
|
|
138
138
|
rest = rest.slice(nameMatch[0].length).trimStart();
|
|
139
139
|
}
|
|
140
|
-
return { phases, gesture: gestureName, arg, target, modifiers, ...
|
|
140
|
+
return { phases, gesture: gestureName, arg, target, modifiers, ...held2 ? { held: held2 } : {} };
|
|
141
141
|
}
|
|
142
142
|
var HELD = "held";
|
|
143
143
|
function parsePhaseList(raw, input) {
|
|
@@ -264,15 +264,15 @@ function formatRoute(r) {
|
|
|
264
264
|
// src/grammar/keyRouteGrammar.ts
|
|
265
265
|
var OPTIONAL_SET = new Set(VALID_MOD_NAMES);
|
|
266
266
|
function parseKeyRoute(input) {
|
|
267
|
-
const [
|
|
268
|
-
if (!
|
|
267
|
+
const [key2, ...mods2] = input.split("?");
|
|
268
|
+
if (!key2) throw new Error(`invalid key route (empty key): ${input}`);
|
|
269
269
|
const seen = /* @__PURE__ */ new Set();
|
|
270
270
|
for (const m of mods2) {
|
|
271
271
|
if (!OPTIONAL_SET.has(m)) throw new Error(`unknown optional modifier "${m}" in ${input}`);
|
|
272
272
|
if (seen.has(m)) throw new Error(`duplicate optional modifier "${m}" in ${input}`);
|
|
273
273
|
seen.add(m);
|
|
274
274
|
}
|
|
275
|
-
return { key, optionalMods: mods2 };
|
|
275
|
+
return { key: key2, optionalMods: mods2 };
|
|
276
276
|
}
|
|
277
277
|
function formatKeyRoute(r) {
|
|
278
278
|
return r.optionalMods.length === 0 ? r.key : `${r.key}?${r.optionalMods.join("?")}`;
|
|
@@ -378,9 +378,9 @@ function matchModifiers(e, mods2, isMac) {
|
|
|
378
378
|
}
|
|
379
379
|
return checkKey(alt, e.altKey) && checkKey(ctrl, e.ctrlKey) && checkKey(meta, e.metaKey) && checkKey(shift, e.shiftKey);
|
|
380
380
|
}
|
|
381
|
-
function checkKey(req,
|
|
382
|
-
if (req === "required") return
|
|
383
|
-
if (req === "forbidden") return !
|
|
381
|
+
function checkKey(req, held2) {
|
|
382
|
+
if (req === "required") return held2;
|
|
383
|
+
if (req === "forbidden") return !held2;
|
|
384
384
|
return true;
|
|
385
385
|
}
|
|
386
386
|
function matchKey(eventKey, specKey) {
|
|
@@ -609,10 +609,10 @@ function routeToSpec(parsed) {
|
|
|
609
609
|
case "key":
|
|
610
610
|
case "key-held": {
|
|
611
611
|
if (arg === void 0) return fail("a key spec needs a concrete key");
|
|
612
|
-
const { key, optionalMods } = parseKeyRoute(arg);
|
|
612
|
+
const { key: key2, optionalMods } = parseKeyRoute(arg);
|
|
613
613
|
const optional = {};
|
|
614
614
|
for (const m of optionalMods) optional[m] = "optional";
|
|
615
|
-
return withMods({ kind, key, ...optionalMods.length > 0 ? { mods: optional } : {} });
|
|
615
|
+
return withMods({ kind, key: key2, ...optionalMods.length > 0 ? { mods: optional } : {} });
|
|
616
616
|
}
|
|
617
617
|
case "multiTouch":
|
|
618
618
|
case "multiTouchTap":
|
|
@@ -712,8 +712,8 @@ function actionClause(parsed, required) {
|
|
|
712
712
|
case "keyUp":
|
|
713
713
|
case "keyHeld": {
|
|
714
714
|
const verb = parsed.gesture === "keyDown" ? "presses" : parsed.gesture === "keyUp" ? "releases" : "holds";
|
|
715
|
-
const
|
|
716
|
-
return required.length > 0 ? `the user holds ${joinAnd(required)} and ${verb} ${
|
|
715
|
+
const key2 = arg ?? "any key";
|
|
716
|
+
return required.length > 0 ? `the user holds ${joinAnd(required)} and ${verb} ${key2}` : `the user ${verb} ${key2}`;
|
|
717
717
|
}
|
|
718
718
|
case "wheel": {
|
|
719
719
|
const direction = arg === "up" ? " up" : arg === "down" ? " down" : "";
|
|
@@ -736,9 +736,9 @@ function actionClause(parsed, required) {
|
|
|
736
736
|
case "pointerDown":
|
|
737
737
|
return userDoes(required, `presses${target}`);
|
|
738
738
|
case "hover": {
|
|
739
|
-
const
|
|
739
|
+
const held2 = parsed.held === "required" ? " with a button held" : parsed.held === "optional" ? " whether or not a button is held" : "";
|
|
740
740
|
const where = arg === "leave" ? ` ${leftClause(parsed.target)}` : target;
|
|
741
|
-
return userDoes(required, `moves the pointer${where}${
|
|
741
|
+
return userDoes(required, `moves the pointer${where}${held2}`);
|
|
742
742
|
}
|
|
743
743
|
case "dblTap":
|
|
744
744
|
return userDoes(required, `double-taps${target}`);
|
|
@@ -779,10 +779,10 @@ function mergeAdjacentStrings(parts) {
|
|
|
779
779
|
}
|
|
780
780
|
|
|
781
781
|
// src/grammar/modifierComboToParsed.ts
|
|
782
|
-
function modifierComboToParsed(
|
|
783
|
-
if (
|
|
782
|
+
function modifierComboToParsed(key2) {
|
|
783
|
+
if (key2 === "default") return {};
|
|
784
784
|
const out = {};
|
|
785
|
-
for (const part of
|
|
785
|
+
for (const part of key2.split("+")) {
|
|
786
786
|
out[part] = "required";
|
|
787
787
|
}
|
|
788
788
|
return out;
|
|
@@ -916,9 +916,9 @@ function keySpecsFromShortcut(shortcut, opts = {}) {
|
|
|
916
916
|
if (shortcut.mod) mods2.push({ name: "mod" });
|
|
917
917
|
if (shortcut.shift) mods2.push(shortcut.shift === "optional" ? { name: "shift", optional: true } : { name: "shift" });
|
|
918
918
|
if (shortcut.alt) mods2.push({ name: "alt" });
|
|
919
|
-
const
|
|
919
|
+
const key2 = typeof shortcut.key === "string" ? shortcut.key : shortcut.key[0];
|
|
920
920
|
const keys = keySpecsFromMods(mods2, opts);
|
|
921
|
-
return
|
|
921
|
+
return key2 === void 0 ? keys : [...keys, keySpecFromKey(key2, opts)];
|
|
922
922
|
}
|
|
923
923
|
|
|
924
924
|
// src/ui/keySpecFromCode.ts
|
|
@@ -961,13 +961,79 @@ function keySpecFromCode(code, opts = {}) {
|
|
|
961
961
|
}
|
|
962
962
|
|
|
963
963
|
// src/ui/formatShortcut.ts
|
|
964
|
-
var
|
|
964
|
+
var SHORTCUT_LEADER_SEPARATOR = " \u25B8 ";
|
|
965
965
|
function formatShortcutParts(b, opts = {}) {
|
|
966
|
-
|
|
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 };
|
|
967
970
|
}
|
|
968
971
|
function formatShortcut(b, opts = {}) {
|
|
969
972
|
const platform = opts.platform ?? detectPlatform();
|
|
970
|
-
|
|
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
|
+
}
|
|
971
1037
|
}
|
|
972
1038
|
|
|
973
1039
|
// src/ui/fixedSeat.ts
|
|
@@ -1234,9 +1300,9 @@ function pointerUpEvent(s, world) {
|
|
|
1234
1300
|
function multiTouchEvent(fingers, mods2, more) {
|
|
1235
1301
|
return { kind: "multitouch", fingers, ...modifiersOf(mods2), ...more };
|
|
1236
1302
|
}
|
|
1237
|
-
var positionsOf = (
|
|
1238
|
-
function heldGeometry(
|
|
1239
|
-
const g = multiTouchGeometry([...positionsOf(
|
|
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]);
|
|
1240
1306
|
return { ...g, centroid: toSurface(g.centroid) };
|
|
1241
1307
|
}
|
|
1242
1308
|
function fireLongPress(rec, sink, observer) {
|
|
@@ -1270,40 +1336,40 @@ function createPointerRecognizer(opts) {
|
|
|
1270
1336
|
const { sink, clock, observer = {}, toSurface = (p) => p } = opts;
|
|
1271
1337
|
const world = (s) => opts.toWorld?.(s.clientX, s.clientY) ?? { x: s.clientX, y: s.clientY };
|
|
1272
1338
|
const clicks = opts.clicks ?? { last: null };
|
|
1273
|
-
const
|
|
1339
|
+
const held2 = /* @__PURE__ */ new Map();
|
|
1274
1340
|
const { longPressMs, tapPx, dragPx, doubleClick } = thresholdsOf(opts);
|
|
1275
1341
|
const touches = createMultiTouchEpisode(tapPx);
|
|
1276
1342
|
const hold = createLongPressHold(clock, (pending) => {
|
|
1277
1343
|
observer.holdChanged?.(pending);
|
|
1278
1344
|
});
|
|
1279
|
-
const geometry = (extra = []) => heldGeometry(
|
|
1345
|
+
const geometry = (extra = []) => heldGeometry(held2, toSurface, extra);
|
|
1280
1346
|
const happenedAt = sampleTime(clock);
|
|
1281
1347
|
const claimForMultiTouch = (s) => {
|
|
1282
1348
|
hold.cancel();
|
|
1283
|
-
for (const p of
|
|
1349
|
+
for (const p of held2.values()) p.buffered = null;
|
|
1284
1350
|
const g = geometry();
|
|
1285
|
-
touches.join(positionsOf(
|
|
1286
|
-
sink.dispatch(multiTouchEvent(
|
|
1351
|
+
touches.join(positionsOf(held2), g.centroid, happenedAt(s));
|
|
1352
|
+
sink.dispatch(multiTouchEvent(held2.size, s, g));
|
|
1287
1353
|
};
|
|
1288
1354
|
return {
|
|
1289
1355
|
press(s) {
|
|
1290
1356
|
const ev = pointerDownEvent(s);
|
|
1291
1357
|
const rec = { pos: { x: s.clientX, y: s.clientY }, buffered: ev, down: pressFactsOf(s) };
|
|
1292
|
-
|
|
1293
|
-
if (
|
|
1294
|
-
if ((s.pointerType === "touch" || s.pointerType === "pen") &&
|
|
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) {
|
|
1295
1361
|
rec.holdType = s.pointerType;
|
|
1296
1362
|
hold.arm(
|
|
1297
1363
|
{ pointerId: s.pointerId, pointerType: s.pointerType, press: rec.down, startedAt: clock.now(), duration: longPressMs() },
|
|
1298
1364
|
() => {
|
|
1299
|
-
fireLongPress(
|
|
1365
|
+
fireLongPress(held2.get(s.pointerId), sink, observer);
|
|
1300
1366
|
}
|
|
1301
1367
|
);
|
|
1302
1368
|
}
|
|
1303
|
-
if (
|
|
1369
|
+
if (held2.size >= 2) claimForMultiTouch(s);
|
|
1304
1370
|
},
|
|
1305
1371
|
move(s) {
|
|
1306
|
-
const rec =
|
|
1372
|
+
const rec = held2.get(s.pointerId);
|
|
1307
1373
|
const to = { x: s.clientX, y: s.clientY };
|
|
1308
1374
|
if (rec) rec.pos = to;
|
|
1309
1375
|
touches.moved(s.pointerId, to);
|
|
@@ -1313,18 +1379,18 @@ function createPointerRecognizer(opts) {
|
|
|
1313
1379
|
hold.cancel(s.pointerId);
|
|
1314
1380
|
sink.dispatch(press);
|
|
1315
1381
|
}
|
|
1316
|
-
if (
|
|
1317
|
-
sink.dispatch(multiTouchEvent(
|
|
1382
|
+
if (held2.size >= 2 && sink.multiTouchInFlight(held2.size)) {
|
|
1383
|
+
sink.dispatch(multiTouchEvent(held2.size, s, geometry()));
|
|
1318
1384
|
}
|
|
1319
1385
|
sink.dispatch(pointerMoveEvent(s, world(s)));
|
|
1320
1386
|
},
|
|
1321
1387
|
release(s) {
|
|
1322
1388
|
hold.cancel(s.pointerId);
|
|
1323
|
-
const before =
|
|
1324
|
-
const rec =
|
|
1325
|
-
|
|
1389
|
+
const before = held2.size;
|
|
1390
|
+
const rec = held2.get(s.pointerId);
|
|
1391
|
+
held2.delete(s.pointerId);
|
|
1326
1392
|
if (before >= 2) sink.dispatch(multiTouchEvent(before, s, { end: "commit" }));
|
|
1327
|
-
if (before >= 2 &&
|
|
1393
|
+
if (before >= 2 && held2.size < 2) {
|
|
1328
1394
|
const fingers = touches.finish(
|
|
1329
1395
|
geometry([{ x: s.clientX, y: s.clientY }]).centroid,
|
|
1330
1396
|
happenedAt(s),
|
|
@@ -1346,10 +1412,10 @@ function createPointerRecognizer(opts) {
|
|
|
1346
1412
|
return composeClick(rec.down, up, clicks, () => happenedAt(s), (ev) => sink.dispatch(ev), observer, doubleClick());
|
|
1347
1413
|
},
|
|
1348
1414
|
cancel(pointerId) {
|
|
1349
|
-
const rec =
|
|
1415
|
+
const rec = held2.get(pointerId);
|
|
1350
1416
|
if (!rec) return;
|
|
1351
|
-
const before =
|
|
1352
|
-
|
|
1417
|
+
const before = held2.size;
|
|
1418
|
+
held2.delete(pointerId);
|
|
1353
1419
|
hold.cancel(pointerId);
|
|
1354
1420
|
if (before >= 2) sink.dispatch(multiTouchEvent(before, rec.down, { end: "cancel" }));
|
|
1355
1421
|
touches.abandon();
|
|
@@ -1357,7 +1423,7 @@ function createPointerRecognizer(opts) {
|
|
|
1357
1423
|
},
|
|
1358
1424
|
takeHold() {
|
|
1359
1425
|
let holding = false;
|
|
1360
|
-
for (const [id, rec] of
|
|
1426
|
+
for (const [id, rec] of held2) {
|
|
1361
1427
|
if (!rec.holdType) continue;
|
|
1362
1428
|
holding = true;
|
|
1363
1429
|
if (!hold.pendingFor(id)) continue;
|
|
@@ -1366,11 +1432,11 @@ function createPointerRecognizer(opts) {
|
|
|
1366
1432
|
}
|
|
1367
1433
|
return holding;
|
|
1368
1434
|
},
|
|
1369
|
-
heldCount: () =>
|
|
1370
|
-
positionOf: (pointerId) =>
|
|
1435
|
+
heldCount: () => held2.size,
|
|
1436
|
+
positionOf: (pointerId) => held2.get(pointerId)?.pos,
|
|
1371
1437
|
reset() {
|
|
1372
1438
|
hold.cancel();
|
|
1373
|
-
|
|
1439
|
+
held2.clear();
|
|
1374
1440
|
touches.abandon();
|
|
1375
1441
|
}
|
|
1376
1442
|
};
|
|
@@ -1402,20 +1468,20 @@ var trackers = /* @__PURE__ */ new WeakMap();
|
|
|
1402
1468
|
function attach(element) {
|
|
1403
1469
|
const doc = element.ownerDocument;
|
|
1404
1470
|
const win = doc.defaultView;
|
|
1405
|
-
const
|
|
1471
|
+
const held2 = /* @__PURE__ */ new Map();
|
|
1406
1472
|
const down = (e) => {
|
|
1407
1473
|
const p = e;
|
|
1408
|
-
|
|
1474
|
+
held2.set(p.pointerId, !!p.buttons);
|
|
1409
1475
|
};
|
|
1410
1476
|
const up = (e) => {
|
|
1411
|
-
|
|
1477
|
+
held2.delete(e.pointerId);
|
|
1412
1478
|
};
|
|
1413
1479
|
const move = (e) => {
|
|
1414
1480
|
const p = e;
|
|
1415
|
-
if (
|
|
1481
|
+
if (held2.get(p.pointerId) && p.buttons === 0) held2.delete(p.pointerId);
|
|
1416
1482
|
};
|
|
1417
1483
|
const blur = () => {
|
|
1418
|
-
|
|
1484
|
+
held2.clear();
|
|
1419
1485
|
};
|
|
1420
1486
|
element.addEventListener("pointerdown", down, true);
|
|
1421
1487
|
doc.addEventListener("pointerup", up, true);
|
|
@@ -1424,7 +1490,7 @@ function attach(element) {
|
|
|
1424
1490
|
win?.addEventListener("blur", blur);
|
|
1425
1491
|
return {
|
|
1426
1492
|
readers: 0,
|
|
1427
|
-
held,
|
|
1493
|
+
held: held2,
|
|
1428
1494
|
detach() {
|
|
1429
1495
|
element.removeEventListener("pointerdown", down, true);
|
|
1430
1496
|
doc.removeEventListener("pointerup", up, true);
|
|
@@ -1497,6 +1563,6 @@ function focusedControlOwnsKey(e) {
|
|
|
1497
1563
|
return activatesFocusedControl(e) || selectOwnsKey(e);
|
|
1498
1564
|
}
|
|
1499
1565
|
|
|
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 };
|
|
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 };
|
|
1501
1567
|
//# sourceMappingURL=index.js.map
|
|
1502
1568
|
//# sourceMappingURL=index.js.map
|