@weasel-js/routing 1.10.1 → 1.11.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/{chunk-VCEIQ64H.js → chunk-QBQQIFPI.js} +580 -51
- package/dist/chunk-QBQQIFPI.js.map +1 -0
- package/dist/{index-DkOqUxow.d.ts → index-CmfVvwWD.d.ts} +645 -279
- package/dist/index.d.ts +6 -3
- package/dist/index.js +54 -17
- package/dist/index.js.map +1 -1
- package/dist/{internal-Boc2I3zn.d.ts → internal-21M1rTVo.d.ts} +23 -1
- package/dist/internal.d.ts +8 -5
- package/dist/react.d.ts +59 -27
- package/dist/react.js +413 -220
- package/dist/react.js.map +1 -1
- package/package.json +13 -9
- package/dist/chunk-VCEIQ64H.js.map +0 -1
|
@@ -1,17 +1,38 @@
|
|
|
1
|
+
import { isDev } from '@weasel-js/react';
|
|
1
2
|
import { createEmitter } from '@weasel-js/registry';
|
|
2
|
-
import { RESERVED_ID_PREFIXES, RESERVED_ID_NAMES, routeGestureForSpecKind, getGestureDescriptor, formatRoute, parseTargetSpec, matchSpec, canonicalModifiers, UNKNOWN_MIME, specificity, VALID_MOD_NAMES, stylusOf } from '@weasel-js/gestures';
|
|
3
|
+
import { RESERVED_ID_PREFIXES, RESERVED_ID_NAMES, isApplePlatform, routeGestureForSpecKind, getGestureDescriptor, formatRoute, parseTargetSpec, matchSpec, canonicalModifiers, UNKNOWN_MIME, specificity, VALID_MOD_NAMES, stylusOf } from '@weasel-js/gestures';
|
|
3
4
|
export { GESTURE_DESCRIPTORS, LONG_PRESS_MS, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, activatesFocusedControl, canonicalModifiers, collapseShiftPairs, describeRoute, describeRouteParts, focusedControlOwnsKey, formatPhaseAtom, formatRoute, getGestureDescriptor, isEditableTarget, isKnownGestureName, matchKey, matchModifiers, matchPhase, matchSpec, matchTarget, parseRoute, routeGestureForSpecKind, specKindForRouteGesture, specificity } from '@weasel-js/gestures';
|
|
4
5
|
import { activeModeOf, NORMAL, DEFAULT_MODES, capabilitiesAllowed } from '@weasel-js/modes';
|
|
5
6
|
import { resolveCursor } from '@weasel-js/cursor';
|
|
7
|
+
import { prefIsShown, prefIsEnabled, isPrefLeaf, prefAliasTarget, prefLeafAt, prefAliasedLeaf, prefNumberAt, resolvePrefUnit, prefDisplayBounds, prefUnitAccepts } from '@weasel-js/prefs';
|
|
8
|
+
import { endlessAllows } from '@weasel-js/quantity';
|
|
6
9
|
|
|
7
|
-
// src/interactions/actions/
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
// src/interactions/actions/actionItems.ts
|
|
11
|
+
var NO_TAGS = Object.freeze([]);
|
|
12
|
+
function actionItems(action) {
|
|
13
|
+
if (action.invoker?.timing !== "immediate") return [];
|
|
14
|
+
if (!action.variants?.length) {
|
|
15
|
+
return [{ key: action.id, action, label: action.label, icon: action.icon, tags: action.tags ?? NO_TAGS }];
|
|
16
|
+
}
|
|
17
|
+
return action.variants.map((v) => ({
|
|
18
|
+
key: `${action.id}:${v.key}`,
|
|
19
|
+
action,
|
|
20
|
+
label: v.label,
|
|
21
|
+
icon: v.icon ?? action.icon,
|
|
22
|
+
tags: v.tags ? [...action.tags ?? NO_TAGS, ...v.tags] : action.tags ?? NO_TAGS,
|
|
23
|
+
params: v.params
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
function actionItemTitle(item) {
|
|
27
|
+
if (item.key === item.action.id) return item.label;
|
|
28
|
+
const words = (s) => s.toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
|
|
29
|
+
const head = words(item.action.label);
|
|
30
|
+
const label = words(item.label);
|
|
31
|
+
for (let i = 0; i + head.length <= label.length; i++) {
|
|
32
|
+
if (head.every((w, j) => label[i + j] === w)) return item.label;
|
|
33
|
+
}
|
|
34
|
+
return `${item.action.label}: ${item.label}`;
|
|
35
|
+
}
|
|
15
36
|
|
|
16
37
|
// src/interactions/actions/binding.ts
|
|
17
38
|
function actionBindings(action) {
|
|
@@ -26,22 +47,60 @@ function actionBindings(action) {
|
|
|
26
47
|
});
|
|
27
48
|
}
|
|
28
49
|
|
|
29
|
-
// src/
|
|
30
|
-
function
|
|
31
|
-
|
|
50
|
+
// src/interactions/actions/actionShortcuts.ts
|
|
51
|
+
function keySpecShortcut(spec) {
|
|
52
|
+
if (spec.kind !== "key" && spec.kind !== "key-held") return void 0;
|
|
53
|
+
const key = Array.isArray(spec.key) ? spec.key[0] : spec.key;
|
|
54
|
+
if (typeof key !== "string" || key.length === 0) return void 0;
|
|
55
|
+
const mods = "mods" in spec ? spec.mods : void 0;
|
|
56
|
+
return {
|
|
57
|
+
key,
|
|
58
|
+
mod: mods?.mod === true,
|
|
59
|
+
alt: mods?.alt === true,
|
|
60
|
+
shift: mods?.shift === true
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function actionShortcuts(action, params) {
|
|
64
|
+
const out = [];
|
|
65
|
+
const seen = /* @__PURE__ */ new Set();
|
|
66
|
+
for (const { spec, opts } of actionBindings(action)) {
|
|
67
|
+
if (params && !paramsMatch(opts?.params, params)) continue;
|
|
68
|
+
const shortcut = keySpecShortcut(spec);
|
|
69
|
+
if (!shortcut) continue;
|
|
70
|
+
const token = `${shortcut.key}|${shortcut.mod}|${shortcut.alt}|${shortcut.shift}`;
|
|
71
|
+
if (seen.has(token)) continue;
|
|
72
|
+
seen.add(token);
|
|
73
|
+
out.push(shortcut);
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
function paramsMatch(bound, wanted) {
|
|
78
|
+
if (typeof bound !== "object" || bound === null) return false;
|
|
79
|
+
return Object.entries(wanted).every(([k, v]) => bound[k] === v);
|
|
32
80
|
}
|
|
33
81
|
|
|
34
|
-
// src/interactions/actions/
|
|
82
|
+
// src/interactions/actions/action.ts
|
|
83
|
+
var ActionDisabledReason = {
|
|
84
|
+
SelectionRequired: "selection-required",
|
|
85
|
+
SceneEmpty: "scene-empty",
|
|
86
|
+
NotApplicable: "not-applicable",
|
|
87
|
+
/** Sentinel: the predicate threw. Surfaced by `evaluateEnabled`'s catch. */
|
|
88
|
+
PredicateThrew: "predicate-threw"
|
|
89
|
+
};
|
|
90
|
+
function actionCursor(action, params) {
|
|
91
|
+
const c = action?.cursor;
|
|
92
|
+
return typeof c === "function" ? c(params) : c;
|
|
93
|
+
}
|
|
35
94
|
var enabledSlowWarned = /* @__PURE__ */ new Set();
|
|
36
95
|
var enabledThrewWarned = /* @__PURE__ */ new Set();
|
|
37
96
|
var ENABLED_BUDGET_MS = 4;
|
|
38
|
-
function evaluateEnabled(action, deps) {
|
|
97
|
+
function evaluateEnabled(action, deps, at) {
|
|
39
98
|
if (!action.enabled) return { enabled: true };
|
|
40
99
|
const dev = isDev();
|
|
41
100
|
const now = typeof performance !== "undefined" && typeof performance.now === "function" ? () => performance.now() : () => Date.now();
|
|
42
101
|
const start = dev ? now() : 0;
|
|
43
102
|
try {
|
|
44
|
-
const result = action.enabled(deps);
|
|
103
|
+
const result = action.enabled(deps, at);
|
|
45
104
|
if (dev) {
|
|
46
105
|
const elapsed = now() - start;
|
|
47
106
|
if (elapsed > ENABLED_BUDGET_MS && !enabledSlowWarned.has(action.id)) {
|
|
@@ -72,15 +131,7 @@ function evaluateActive(action, deps) {
|
|
|
72
131
|
return false;
|
|
73
132
|
}
|
|
74
133
|
}
|
|
75
|
-
|
|
76
|
-
// src/interactions/actions/buildDeps.ts
|
|
77
|
-
var DEV = (() => {
|
|
78
|
-
try {
|
|
79
|
-
return Boolean(import.meta.env?.DEV);
|
|
80
|
-
} catch {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
})();
|
|
134
|
+
var DEV = isDev();
|
|
84
135
|
function buildDepsFromRequires(action, depRegistry) {
|
|
85
136
|
const requires = action.requires ?? [];
|
|
86
137
|
const entries = requires.map((name) => [name, depRegistry.get(name)]);
|
|
@@ -281,18 +332,50 @@ function resolveParams(params) {
|
|
|
281
332
|
if (params === void 0) return void 0;
|
|
282
333
|
return typeof params === "function" ? params() : params;
|
|
283
334
|
}
|
|
335
|
+
function keysHeld(keys, modifiers, isMac) {
|
|
336
|
+
return keys.some((k) => {
|
|
337
|
+
if (k === "mod") return isMac ? modifiers.meta : modifiers.ctrl;
|
|
338
|
+
return modifiers[k];
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
function roleKeysOf(roles, rebinds) {
|
|
342
|
+
if (!roles) return void 0;
|
|
343
|
+
const out = {};
|
|
344
|
+
for (const id of Object.keys(roles)) out[id] = rebinds?.[id] ?? roles[id].keys;
|
|
345
|
+
return out;
|
|
346
|
+
}
|
|
347
|
+
function platformRoleKeys(keys, isMac) {
|
|
348
|
+
const out = {};
|
|
349
|
+
for (const id of Object.keys(keys)) out[id] = keys[id].map((k) => k === "mod" ? isMac ? "meta" : "ctrl" : k);
|
|
350
|
+
return out;
|
|
351
|
+
}
|
|
352
|
+
function resolveRoles(keys, modifiers, isMac) {
|
|
353
|
+
const out = {};
|
|
354
|
+
for (const id of Object.keys(keys)) out[id] = keysHeld(keys[id], modifiers, isMac);
|
|
355
|
+
return out;
|
|
356
|
+
}
|
|
357
|
+
function rebindRoles(roles, rebinds) {
|
|
358
|
+
const out = {};
|
|
359
|
+
for (const id of Object.keys(roles)) {
|
|
360
|
+
out[id] = rebinds[id] ? { ...roles[id], keys: rebinds[id] } : roles[id];
|
|
361
|
+
}
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
364
|
+
function roleReader(roles) {
|
|
365
|
+
return (ctx, id) => ctx.roles?.[id] ?? keysHeld(roles[id].keys, ctx.modifiers, isApplePlatform());
|
|
366
|
+
}
|
|
284
367
|
|
|
285
368
|
// src/interactions/actions/tuning.ts
|
|
286
369
|
var WRAPS = /* @__PURE__ */ new WeakMap();
|
|
287
370
|
function layered(under, over) {
|
|
288
|
-
if (typeof under !== "function" && typeof over !== "function") return {
|
|
289
|
-
return () => ({
|
|
371
|
+
if (typeof under !== "function" && typeof over !== "function") return tunedParams([{ params: under }], over);
|
|
372
|
+
return () => tunedParams([{ params: under }], resolveParams(over));
|
|
290
373
|
}
|
|
291
374
|
function wrapInvoker(invoker, params) {
|
|
292
375
|
for (let w = invoker; w; w = WRAPS.get(w)?.inner) {
|
|
293
376
|
if (WRAPS.get(w)?.params === params) return invoker;
|
|
294
377
|
}
|
|
295
|
-
const base = (given) => ({
|
|
378
|
+
const base = (given) => tunedParams([{ params }], given);
|
|
296
379
|
const wrapped = invoker.timing === "immediate" ? { timing: "immediate", run: (deps, given) => invoker.run(deps, base(given)) } : {
|
|
297
380
|
timing: "ongoing",
|
|
298
381
|
start: (ctx, opts) => invoker.start(
|
|
@@ -307,16 +390,49 @@ function withBindings(own, added) {
|
|
|
307
390
|
const base = own === void 0 ? [] : Array.isArray(own) ? own : [own];
|
|
308
391
|
return [...base, ...added.filter((b) => !base.includes(b))];
|
|
309
392
|
}
|
|
393
|
+
var CURSOR_WRAPS = /* @__PURE__ */ new WeakMap();
|
|
394
|
+
function wrapCursor(cursor, params) {
|
|
395
|
+
if (typeof cursor !== "function") return cursor;
|
|
396
|
+
for (let w = cursor; typeof w === "function"; w = CURSOR_WRAPS.get(w)?.inner) {
|
|
397
|
+
if (CURSOR_WRAPS.get(w)?.params === params) return cursor;
|
|
398
|
+
}
|
|
399
|
+
const wrapped = (given) => cursor(tunedParams([{ params }], given));
|
|
400
|
+
CURSOR_WRAPS.set(wrapped, { inner: cursor, params });
|
|
401
|
+
return wrapped;
|
|
402
|
+
}
|
|
403
|
+
var PREVIEW_WRAPS = /* @__PURE__ */ new WeakMap();
|
|
404
|
+
function wrapPreview(preview, params) {
|
|
405
|
+
if (!preview) return preview;
|
|
406
|
+
for (let w = preview; w; w = PREVIEW_WRAPS.get(w)?.inner) {
|
|
407
|
+
if (PREVIEW_WRAPS.get(w)?.params === params) return preview;
|
|
408
|
+
}
|
|
409
|
+
const wrapped = (deps, given) => preview(deps, tunedParams([{ params }], given));
|
|
410
|
+
PREVIEW_WRAPS.set(wrapped, { inner: preview, params });
|
|
411
|
+
return wrapped;
|
|
412
|
+
}
|
|
310
413
|
function tuneOnce(action, tuning) {
|
|
311
414
|
const invoker = tuning.params && action.invoker ? wrapInvoker(action.invoker, tuning.params) : action.invoker;
|
|
415
|
+
const cursor = tuning.params ? wrapCursor(action.cursor, tuning.params) : action.cursor;
|
|
416
|
+
const preview = tuning.params ? wrapPreview(action.preview, tuning.params) : action.preview;
|
|
312
417
|
const bindings = tuning.bindings?.length ? withBindings(action.defaultBinding, tuning.bindings) : null;
|
|
313
|
-
|
|
418
|
+
const modifierRoles = tuning.roles && action.modifierRoles ? rebindRoles(action.modifierRoles, tuning.roles) : null;
|
|
419
|
+
if (invoker === action.invoker && cursor === action.cursor && preview === action.preview && bindings === null && modifierRoles === null) {
|
|
420
|
+
return action;
|
|
421
|
+
}
|
|
314
422
|
return {
|
|
315
423
|
...action,
|
|
424
|
+
...modifierRoles ? { modifierRoles } : {},
|
|
316
425
|
...invoker ? { invoker } : {},
|
|
426
|
+
...cursor ? { cursor } : {},
|
|
427
|
+
...preview ? { preview } : {},
|
|
317
428
|
...bindings ? { defaultBinding: bindings } : {}
|
|
318
429
|
};
|
|
319
430
|
}
|
|
431
|
+
function tunedParams(tunings, given) {
|
|
432
|
+
let out = {};
|
|
433
|
+
for (const t of tunings) out = { ...out, ...resolveParams(t.params) };
|
|
434
|
+
return { ...out, ...given };
|
|
435
|
+
}
|
|
320
436
|
function tuneAction(action, tunings) {
|
|
321
437
|
let out = action;
|
|
322
438
|
for (const t of tunings) out = tuneOnce(out, t);
|
|
@@ -817,15 +933,7 @@ function buildRuleCtx(args) {
|
|
|
817
933
|
device: args.device
|
|
818
934
|
};
|
|
819
935
|
}
|
|
820
|
-
|
|
821
|
-
// src/interactions/dispatcher/dispatchTrace.ts
|
|
822
|
-
var DEV2 = (() => {
|
|
823
|
-
try {
|
|
824
|
-
return Boolean(import.meta.env?.DEV);
|
|
825
|
-
} catch {
|
|
826
|
-
return false;
|
|
827
|
-
}
|
|
828
|
-
})();
|
|
936
|
+
var DEV2 = isDev();
|
|
829
937
|
var TRACE_LIMIT = 200;
|
|
830
938
|
var traceLog = DEV2 && typeof window !== "undefined" ? window.__weaselDispatchLog__ ??= [] : [];
|
|
831
939
|
function recordTrace(entry) {
|
|
@@ -868,6 +976,9 @@ function gestureIdFor(event) {
|
|
|
868
976
|
if (event.kind === "multitouch") {
|
|
869
977
|
return multiTouchGestureId(event.fingers);
|
|
870
978
|
}
|
|
979
|
+
if (event.kind === "carry") {
|
|
980
|
+
return `carry-${event.pointerId ?? "mouse"}`;
|
|
981
|
+
}
|
|
871
982
|
return `ongoing-${event.kind}`;
|
|
872
983
|
}
|
|
873
984
|
|
|
@@ -925,6 +1036,8 @@ function worldPointOf(event) {
|
|
|
925
1036
|
case "doubleclick":
|
|
926
1037
|
case "contextmenu":
|
|
927
1038
|
return event.x !== void 0 && event.y !== void 0 ? { x: event.x, y: event.y } : void 0;
|
|
1039
|
+
case "carry":
|
|
1040
|
+
return { x: event.x, y: event.y };
|
|
928
1041
|
default:
|
|
929
1042
|
return void 0;
|
|
930
1043
|
}
|
|
@@ -965,8 +1078,19 @@ function immediateParams(event, resolved) {
|
|
|
965
1078
|
};
|
|
966
1079
|
// Only a `stage: 'press'` pointerdown reaches an immediate invoker: the
|
|
967
1080
|
// buffered copy matches `drag` specs, which are ongoing.
|
|
968
|
-
case "pointerdown":
|
|
969
1081
|
case "contextmenu":
|
|
1082
|
+
return {
|
|
1083
|
+
worldX: event.x,
|
|
1084
|
+
worldY: event.y,
|
|
1085
|
+
clientX: event.clientX,
|
|
1086
|
+
clientY: event.clientY,
|
|
1087
|
+
affordance: event.affordance,
|
|
1088
|
+
bodyTarget: event.bodyTarget,
|
|
1089
|
+
bodyKind: event.bodyKind,
|
|
1090
|
+
mods: modifiersOf(event),
|
|
1091
|
+
...resolved
|
|
1092
|
+
};
|
|
1093
|
+
case "pointerdown":
|
|
970
1094
|
case "longpress":
|
|
971
1095
|
return {
|
|
972
1096
|
worldX: event.x,
|
|
@@ -987,6 +1111,16 @@ function immediateParams(event, resolved) {
|
|
|
987
1111
|
mods: modifiersOf(event),
|
|
988
1112
|
...resolved
|
|
989
1113
|
};
|
|
1114
|
+
case "carry":
|
|
1115
|
+
return {
|
|
1116
|
+
worldX: event.x,
|
|
1117
|
+
worldY: event.y,
|
|
1118
|
+
clientX: event.clientX,
|
|
1119
|
+
clientY: event.clientY,
|
|
1120
|
+
carry: { type: event.type, value: event.value },
|
|
1121
|
+
mods: modifiersOf(event),
|
|
1122
|
+
...resolved
|
|
1123
|
+
};
|
|
990
1124
|
case "drop":
|
|
991
1125
|
case "paste":
|
|
992
1126
|
return {
|
|
@@ -1008,13 +1142,13 @@ function liveInView(binding, view) {
|
|
|
1008
1142
|
function namesView(binding, view) {
|
|
1009
1143
|
return binding.opts?.views?.includes(view) ?? false;
|
|
1010
1144
|
}
|
|
1011
|
-
function preferViewScoped(
|
|
1145
|
+
function preferViewScoped(matches2, view) {
|
|
1012
1146
|
const named = [];
|
|
1013
1147
|
const rest = [];
|
|
1014
|
-
for (const m of
|
|
1148
|
+
for (const m of matches2) {
|
|
1015
1149
|
(namesView(m.binding, view) ? named : rest).push(m);
|
|
1016
1150
|
}
|
|
1017
|
-
return named.length === 0 ? [...
|
|
1151
|
+
return named.length === 0 ? [...matches2] : [...named, ...rest];
|
|
1018
1152
|
}
|
|
1019
1153
|
function ruleCtxOf(ctx) {
|
|
1020
1154
|
const r = ctx.getRuleCtx?.();
|
|
@@ -1030,7 +1164,7 @@ function isEligible(action, ruleCtx) {
|
|
|
1030
1164
|
function describeEligible(eligible) {
|
|
1031
1165
|
return describeRule(eligibleToRule(eligible));
|
|
1032
1166
|
}
|
|
1033
|
-
function preferContextual(
|
|
1167
|
+
function preferContextual(matches2, actionLookup, holds, view) {
|
|
1034
1168
|
const out = [];
|
|
1035
1169
|
let run = [];
|
|
1036
1170
|
const flush = () => {
|
|
@@ -1041,7 +1175,7 @@ function preferContextual(matches, actionLookup, holds, view) {
|
|
|
1041
1175
|
out.push(...gated, ...run.filter((m) => !gated.includes(m)));
|
|
1042
1176
|
run = [];
|
|
1043
1177
|
};
|
|
1044
|
-
for (const m of
|
|
1178
|
+
for (const m of matches2) {
|
|
1045
1179
|
const head = run[0];
|
|
1046
1180
|
if (head && (head.scope !== m.scope || namesView(head.binding, view) !== namesView(m.binding, view) || !sameSpecificity(head.binding.spec, m.binding.spec))) flush();
|
|
1047
1181
|
run.push(m);
|
|
@@ -1133,8 +1267,10 @@ function createGestureFacts() {
|
|
|
1133
1267
|
const points = /* @__PURE__ */ new Map();
|
|
1134
1268
|
const startSpreads = /* @__PURE__ */ new Map();
|
|
1135
1269
|
const turns = /* @__PURE__ */ new Map();
|
|
1270
|
+
const roles = /* @__PURE__ */ new Map();
|
|
1136
1271
|
return {
|
|
1137
|
-
begin(gestureId, event) {
|
|
1272
|
+
begin(gestureId, event, roleKeys) {
|
|
1273
|
+
if (roleKeys) roles.set(gestureId, roleKeys);
|
|
1138
1274
|
if (event.kind === "pointerdown") {
|
|
1139
1275
|
origins.set(gestureId, {
|
|
1140
1276
|
x: event.x ?? 0,
|
|
@@ -1167,7 +1303,9 @@ function createGestureFacts() {
|
|
|
1167
1303
|
const trail = points.get(gestureId);
|
|
1168
1304
|
const startSpread = startSpreads.get(gestureId);
|
|
1169
1305
|
const turn = turns.get(gestureId);
|
|
1306
|
+
const roleKeys = roles.get(gestureId);
|
|
1170
1307
|
return {
|
|
1308
|
+
...roleKeys !== void 0 ? { roleKeys } : {},
|
|
1171
1309
|
...origin !== void 0 ? { origin } : {},
|
|
1172
1310
|
...trail !== void 0 ? { points: trail } : {},
|
|
1173
1311
|
...startSpread !== void 0 ? { startSpread } : {},
|
|
@@ -1179,12 +1317,14 @@ function createGestureFacts() {
|
|
|
1179
1317
|
points.delete(gestureId);
|
|
1180
1318
|
startSpreads.delete(gestureId);
|
|
1181
1319
|
turns.delete(gestureId);
|
|
1320
|
+
roles.delete(gestureId);
|
|
1182
1321
|
},
|
|
1183
1322
|
clear() {
|
|
1184
1323
|
origins.clear();
|
|
1185
1324
|
points.clear();
|
|
1186
1325
|
startSpreads.clear();
|
|
1187
1326
|
turns.clear();
|
|
1327
|
+
roles.clear();
|
|
1188
1328
|
}
|
|
1189
1329
|
};
|
|
1190
1330
|
}
|
|
@@ -1229,7 +1369,7 @@ function createInFlightGestures() {
|
|
|
1229
1369
|
let cursor = null;
|
|
1230
1370
|
for (const [id, handle] of handles) {
|
|
1231
1371
|
const action = actions.get(id);
|
|
1232
|
-
const spec = handle.cursor?.() ?? action?.activeCursor ?? action
|
|
1372
|
+
const spec = handle.cursor?.() ?? action?.activeCursor ?? actionCursor(action);
|
|
1233
1373
|
if (spec !== void 0 && resolveCursor(spec)) cursor = spec;
|
|
1234
1374
|
}
|
|
1235
1375
|
return cursor;
|
|
@@ -1321,6 +1461,7 @@ function buildInvocationCtx(event, deps, viewId, facts = {}) {
|
|
|
1321
1461
|
};
|
|
1322
1462
|
const screen = clientPoint(event);
|
|
1323
1463
|
if (screen) base.screen = screen;
|
|
1464
|
+
if (facts.roleKeys) base.roles = resolveRoles(facts.roleKeys, base.modifiers, false);
|
|
1324
1465
|
if (event.kind === "key") {
|
|
1325
1466
|
base.key = { key: event.key, repeat: event.repeat ?? false };
|
|
1326
1467
|
} else if (event.kind === "key-held") {
|
|
@@ -1335,6 +1476,9 @@ function buildInvocationCtx(event, deps, viewId, facts = {}) {
|
|
|
1335
1476
|
Object.assign(base, pumpDrag(event, facts));
|
|
1336
1477
|
} else if (event.kind === "multitouch") {
|
|
1337
1478
|
base.multiTouch = multiTouchOf(event, facts);
|
|
1479
|
+
} else if (event.kind === "carry") {
|
|
1480
|
+
base.world = { x: event.x, y: event.y };
|
|
1481
|
+
base.carry = { type: event.type, value: event.value };
|
|
1338
1482
|
}
|
|
1339
1483
|
return base;
|
|
1340
1484
|
}
|
|
@@ -1385,6 +1529,13 @@ function pumpGesture(run, event) {
|
|
|
1385
1529
|
handle.onMove(ctxFor(run, event, {}, gestureId));
|
|
1386
1530
|
return "handled";
|
|
1387
1531
|
}
|
|
1532
|
+
if (event.kind === "wheel") {
|
|
1533
|
+
for (const [gestureId, handle] of [...inFlight.handles].reverse()) {
|
|
1534
|
+
if (handle.onWheel?.(ctxFor(run, event, {}, gestureId)) === true) return "handled";
|
|
1535
|
+
}
|
|
1536
|
+
return void 0;
|
|
1537
|
+
}
|
|
1538
|
+
if (event.kind === "carry") return pumpCarry(run, event);
|
|
1388
1539
|
if (event.kind === "pointerup") return ended("commit");
|
|
1389
1540
|
if (event.kind === "pointercancel") return ended("cancel");
|
|
1390
1541
|
if (event.kind === "multitouch" && event.end !== void 0) {
|
|
@@ -1402,7 +1553,23 @@ function pumpGesture(run, event) {
|
|
|
1402
1553
|
}
|
|
1403
1554
|
return void 0;
|
|
1404
1555
|
}
|
|
1556
|
+
function pumpCarry(run, event) {
|
|
1557
|
+
if (event.stage === "enter") return void 0;
|
|
1558
|
+
const gestureId = gestureIdFor(event);
|
|
1559
|
+
const handle = run.inFlight.handles.get(gestureId);
|
|
1560
|
+
if (!handle) return event.stage === "drop" ? void 0 : "unhandled";
|
|
1561
|
+
if (event.stage === "move") {
|
|
1562
|
+
handle.onMove?.(ctxFor(run, event, {}, gestureId));
|
|
1563
|
+
return "handled";
|
|
1564
|
+
}
|
|
1565
|
+
return endGesture(run, gestureId, event, event.stage === "drop" ? "commit" : "cancel") ? "handled" : "unhandled";
|
|
1566
|
+
}
|
|
1567
|
+
function carryStageFits(event, timing) {
|
|
1568
|
+
if (event.kind !== "carry") return true;
|
|
1569
|
+
return event.stage === (timing === "immediate" ? "drop" : "enter");
|
|
1570
|
+
}
|
|
1405
1571
|
function invokeAction(run, event, match, action, deps) {
|
|
1572
|
+
if (action.invoker && !carryStageFits(event, action.invoker.timing)) return "empty-handle";
|
|
1406
1573
|
if (action.invoker?.timing === "immediate") {
|
|
1407
1574
|
try {
|
|
1408
1575
|
const params = immediateParams(event, resolveParams(match.binding.opts?.params));
|
|
@@ -1421,7 +1588,8 @@ function invokeAction(run, event, match, action, deps) {
|
|
|
1421
1588
|
return "misbound";
|
|
1422
1589
|
}
|
|
1423
1590
|
const gestureId = gestureIdFor(event);
|
|
1424
|
-
|
|
1591
|
+
const roleKeys = roleKeysOf(action.modifierRoles, match.binding.opts?.roles);
|
|
1592
|
+
run.facts.begin(gestureId, event, roleKeys && platformRoleKeys(roleKeys, run.isMac()));
|
|
1425
1593
|
const handle = action.invoker.start(ctxFor(run, event, deps, gestureId), match.binding.opts);
|
|
1426
1594
|
if (isEmptyOngoingHandle(handle)) {
|
|
1427
1595
|
run.facts.drop(gestureId);
|
|
@@ -1690,9 +1858,14 @@ function resolveOnly(event, ctx, engaged) {
|
|
|
1690
1858
|
actionId: winner.action.id,
|
|
1691
1859
|
action: winner.action,
|
|
1692
1860
|
scope: winner.match.scope,
|
|
1693
|
-
ownerId: winner.match.ownerId
|
|
1861
|
+
ownerId: winner.match.ownerId,
|
|
1862
|
+
...paramsOf(winner.match.binding)
|
|
1694
1863
|
};
|
|
1695
1864
|
}
|
|
1865
|
+
function paramsOf(binding) {
|
|
1866
|
+
const params = resolveParams(binding.opts?.params);
|
|
1867
|
+
return params ? { params } : {};
|
|
1868
|
+
}
|
|
1696
1869
|
function explain(event, ctx, engaged, opts) {
|
|
1697
1870
|
const r = rank(event, ctx, engaged, true);
|
|
1698
1871
|
const walked = walk(r, event, ctx, null, opts?.evaluateShadowed ?? false);
|
|
@@ -1711,10 +1884,12 @@ function createDispatcher(opts) {
|
|
|
1711
1884
|
}
|
|
1712
1885
|
const uiOngoing = createUiOngoing(inFlight, notify, opts?.getAction);
|
|
1713
1886
|
let routedView;
|
|
1714
|
-
|
|
1887
|
+
let routedIsMac = false;
|
|
1888
|
+
const run = { inFlight, facts, viewId: () => routedView, isMac: () => routedIsMac };
|
|
1715
1889
|
const invoking = (event) => (match, action, deps) => invokeAction(run, event, match, action, deps);
|
|
1716
1890
|
function handleInput(event, ctx) {
|
|
1717
1891
|
routedView = ctx.viewId;
|
|
1892
|
+
routedIsMac = ctx.isMac;
|
|
1718
1893
|
const pumped = pumpGesture(run, event);
|
|
1719
1894
|
if (pumped !== void 0) return pumped;
|
|
1720
1895
|
if (event.kind === "hover") return hover(event, ctx);
|
|
@@ -1869,6 +2044,75 @@ function openPointerSession(origin, down, cb, opts = {}) {
|
|
|
1869
2044
|
};
|
|
1870
2045
|
}
|
|
1871
2046
|
|
|
2047
|
+
// src/interactions/carry/carry.ts
|
|
2048
|
+
var surfaces = /* @__PURE__ */ new Set();
|
|
2049
|
+
function addCarrySurface(surface) {
|
|
2050
|
+
surfaces.add(surface);
|
|
2051
|
+
return () => {
|
|
2052
|
+
surfaces.delete(surface);
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2055
|
+
function contains(r, x, y) {
|
|
2056
|
+
return x >= r.left && x <= r.right && y >= r.top && y <= r.bottom;
|
|
2057
|
+
}
|
|
2058
|
+
function surfaceAt(doc, x, y) {
|
|
2059
|
+
const hit = typeof doc.elementFromPoint === "function" ? doc.elementFromPoint(x, y) : null;
|
|
2060
|
+
let best = null;
|
|
2061
|
+
for (const s of surfaces) {
|
|
2062
|
+
if (!contains(s.element.getBoundingClientRect(), x, y)) continue;
|
|
2063
|
+
if (hit && !s.element.contains(hit)) continue;
|
|
2064
|
+
if (best && s.element.contains(best.element)) continue;
|
|
2065
|
+
best = s;
|
|
2066
|
+
}
|
|
2067
|
+
return best;
|
|
2068
|
+
}
|
|
2069
|
+
function startCarry(origin, down, payload, cb = {}) {
|
|
2070
|
+
const doc = origin.ownerDocument ?? document;
|
|
2071
|
+
const pointerId = down.pointerId;
|
|
2072
|
+
let over = null;
|
|
2073
|
+
const leave = (at) => {
|
|
2074
|
+
const was = over;
|
|
2075
|
+
over = null;
|
|
2076
|
+
if (was && surfaces.has(was)) was.receive("leave", payload, at, pointerId);
|
|
2077
|
+
};
|
|
2078
|
+
const step = (at) => {
|
|
2079
|
+
const next = surfaceAt(doc, at.clientX, at.clientY);
|
|
2080
|
+
if (next === over) {
|
|
2081
|
+
over?.receive("move", payload, at, pointerId);
|
|
2082
|
+
return;
|
|
2083
|
+
}
|
|
2084
|
+
leave(at);
|
|
2085
|
+
over = next;
|
|
2086
|
+
next?.receive("enter", payload, at, pointerId);
|
|
2087
|
+
};
|
|
2088
|
+
let last = down;
|
|
2089
|
+
const session = openPointerSession(origin, down, {
|
|
2090
|
+
onMove: (e) => {
|
|
2091
|
+
last = e;
|
|
2092
|
+
step(e);
|
|
2093
|
+
cb.onMove?.(e);
|
|
2094
|
+
},
|
|
2095
|
+
onEnd: (e) => {
|
|
2096
|
+
step(e);
|
|
2097
|
+
const on = over;
|
|
2098
|
+
over = null;
|
|
2099
|
+
const taken = on !== null && surfaces.has(on) && on.receive("drop", payload, e, pointerId) === "handled";
|
|
2100
|
+
cb.onEnd?.({ taken, event: e });
|
|
2101
|
+
},
|
|
2102
|
+
onCancel: (reason) => {
|
|
2103
|
+
leave(last);
|
|
2104
|
+
cb.onCancel?.(reason);
|
|
2105
|
+
}
|
|
2106
|
+
});
|
|
2107
|
+
return {
|
|
2108
|
+
pointerId,
|
|
2109
|
+
get active() {
|
|
2110
|
+
return session.active;
|
|
2111
|
+
},
|
|
2112
|
+
cancel: () => session.cancel()
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
|
|
1872
2116
|
// src/tools/types.ts
|
|
1873
2117
|
function isTool(entry) {
|
|
1874
2118
|
return !!(entry.eligibility?.focus || entry.eligibility?.offhand);
|
|
@@ -2191,6 +2435,291 @@ function dwarn(namespace, ...args) {
|
|
|
2191
2435
|
console.warn(`[${namespace}]`, ...args);
|
|
2192
2436
|
}
|
|
2193
2437
|
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2438
|
+
// src/menus/pickMenu.ts
|
|
2439
|
+
var oneOf = (want, got) => want === void 0 || (Array.isArray(want) ? want.includes(got) : want === got);
|
|
2440
|
+
function matches(when, hit) {
|
|
2441
|
+
return oneOf(when.bodyTarget, hit.bodyTarget) && oneOf(when.bodyKind, hit.bodyKind);
|
|
2442
|
+
}
|
|
2443
|
+
function mergeMenuDefs(defs, activeTool) {
|
|
2444
|
+
const live = [
|
|
2445
|
+
...defs.filter((d) => d.owner === null),
|
|
2446
|
+
...defs.filter((d) => d.owner !== null && d.owner === activeTool)
|
|
2447
|
+
];
|
|
2448
|
+
const byId = /* @__PURE__ */ new Map();
|
|
2449
|
+
for (const { def } of live) {
|
|
2450
|
+
const prev = byId.get(def.id);
|
|
2451
|
+
if (!prev) {
|
|
2452
|
+
byId.set(def.id, { def: { ...def, menu: { ...def.menu, children: { ...def.menu.children } } }, parts: 1 });
|
|
2453
|
+
continue;
|
|
2454
|
+
}
|
|
2455
|
+
const n = prev.parts++;
|
|
2456
|
+
const children = prev.def.menu.children;
|
|
2457
|
+
children[`~sep${n}`] = { kind: "separator", name: "", description: "", default: void 0 };
|
|
2458
|
+
for (const [k, node] of Object.entries(def.menu.children)) {
|
|
2459
|
+
children[Object.hasOwn(children, k) ? `${k}~${n}` : k] = node;
|
|
2460
|
+
}
|
|
2461
|
+
if (!prev.def.when && def.when) prev.def.when = def.when;
|
|
2462
|
+
}
|
|
2463
|
+
return [...byId.values()].map((m) => m.def);
|
|
2464
|
+
}
|
|
2465
|
+
function pickMenu(defs, hit) {
|
|
2466
|
+
return defs.find((d) => d.when !== void 0 && matches(d.when, hit));
|
|
2467
|
+
}
|
|
2468
|
+
var SUBMENU_AS = /* @__PURE__ */ new Set(["menu", "page", "tab", "panel"]);
|
|
2469
|
+
var noop = () => {
|
|
2470
|
+
};
|
|
2471
|
+
var withAccess = (access) => access ? { access } : {};
|
|
2472
|
+
var sep = (key) => ({ kind: "separator", key });
|
|
2473
|
+
function itemOf(ctx, it, key, label, off, access) {
|
|
2474
|
+
const deps = ctx.src.depsFor(it.action);
|
|
2475
|
+
const point = ctx.at ? { worldX: ctx.at.x, worldY: ctx.at.y } : {};
|
|
2476
|
+
return {
|
|
2477
|
+
kind: "item",
|
|
2478
|
+
key,
|
|
2479
|
+
label,
|
|
2480
|
+
...withAccess(access),
|
|
2481
|
+
icon: it.icon,
|
|
2482
|
+
shortcut: actionShortcuts(it.action, it.params)[0],
|
|
2483
|
+
disabled: off || !evaluateEnabled(it.action, deps, ctx.at).enabled,
|
|
2484
|
+
checked: evaluateActive(it.action, deps),
|
|
2485
|
+
run: () => {
|
|
2486
|
+
ctx.src.trigger(it.action.id, { ...ctx.params, ...it.params, ...point });
|
|
2487
|
+
}
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
function itemsNamed(ctx, name) {
|
|
2491
|
+
const action = ctx.byId.get(name);
|
|
2492
|
+
if (action) return actionItems(action);
|
|
2493
|
+
const colon = name.indexOf(":");
|
|
2494
|
+
const owner = colon > 0 ? ctx.byId.get(name.slice(0, colon)) : void 0;
|
|
2495
|
+
return owner ? actionItems(owner).filter((it) => it.key === name) : [];
|
|
2496
|
+
}
|
|
2497
|
+
function actionRows(ctx, leaf, key, off) {
|
|
2498
|
+
const own = leaf.label || leaf.name;
|
|
2499
|
+
if (leaf.action === void 0) {
|
|
2500
|
+
if (!own) return [];
|
|
2501
|
+
if (isDev()) console.warn(`weasel: menu row "${key}" names no registered action, so it does nothing`);
|
|
2502
|
+
return [{ kind: "item", key, label: own, disabled: true, run: noop }];
|
|
2503
|
+
}
|
|
2504
|
+
const items = itemsNamed(ctx, leaf.action);
|
|
2505
|
+
if (items.length === 1) return [itemOf(ctx, items[0], key, own || items[0].label, off, leaf.access)];
|
|
2506
|
+
return items.map((it) => itemOf(ctx, it, `${key}/${it.key}`, it.label, off));
|
|
2507
|
+
}
|
|
2508
|
+
function siblingsOf(ctx, path) {
|
|
2509
|
+
const dot = path.lastIndexOf(".");
|
|
2510
|
+
const parent = dot < 0 ? void 0 : ctx.src.values(path.slice(0, dot));
|
|
2511
|
+
return parent && typeof parent === "object" ? parent : void 0;
|
|
2512
|
+
}
|
|
2513
|
+
function booleanRows(ctx, leaf, key, path, off) {
|
|
2514
|
+
const { values, onChange } = ctx.src;
|
|
2515
|
+
if (!values || !onChange) return [];
|
|
2516
|
+
const stored = values(path);
|
|
2517
|
+
const on = leaf.encoding ? leaf.encoding.read(stored, siblingsOf(ctx, path)) : stored === true;
|
|
2518
|
+
const write = (v) => leaf.encoding ? leaf.encoding.write(v, siblingsOf(ctx, path)) : v;
|
|
2519
|
+
return [{
|
|
2520
|
+
kind: "item",
|
|
2521
|
+
key,
|
|
2522
|
+
label: leaf.name,
|
|
2523
|
+
...withAccess(leaf.access),
|
|
2524
|
+
checked: on,
|
|
2525
|
+
disabled: off || leaf.readout === true,
|
|
2526
|
+
run: leaf.readout ? noop : () => onChange(path, write(!on))
|
|
2527
|
+
}];
|
|
2528
|
+
}
|
|
2529
|
+
function enumRows(ctx, leaf, key, path, off) {
|
|
2530
|
+
const { values, onChange } = ctx.src;
|
|
2531
|
+
if (!values || !onChange) return [];
|
|
2532
|
+
const stored = values(path);
|
|
2533
|
+
const current = leaf.encoding ? leaf.encoding.read(stored, siblingsOf(ctx, path)) : stored;
|
|
2534
|
+
if (leaf.readout) {
|
|
2535
|
+
const shown = leaf.options.find((o) => o.value === current)?.label ?? "\u2014";
|
|
2536
|
+
return [{ kind: "item", key, label: `${leaf.name}: ${shown}`, disabled: true, run: noop }];
|
|
2537
|
+
}
|
|
2538
|
+
const write = (v) => leaf.encoding ? leaf.encoding.write(v, siblingsOf(ctx, path)) : v;
|
|
2539
|
+
return [
|
|
2540
|
+
sep(`${key}/<`),
|
|
2541
|
+
...leaf.options.map((o) => ({
|
|
2542
|
+
kind: "item",
|
|
2543
|
+
key: `${key}/${o.value}`,
|
|
2544
|
+
label: o.label,
|
|
2545
|
+
checked: o.value === current,
|
|
2546
|
+
disabled: off || o.disabled === true,
|
|
2547
|
+
run: () => onChange(path, write(o.value))
|
|
2548
|
+
})),
|
|
2549
|
+
sep(`${key}/>`)
|
|
2550
|
+
];
|
|
2551
|
+
}
|
|
2552
|
+
function numberRows(ctx, given, key, path, off) {
|
|
2553
|
+
const { values, onChange } = ctx.src;
|
|
2554
|
+
if (!values || !onChange) return [];
|
|
2555
|
+
const leaf = prefNumberAt(given, values);
|
|
2556
|
+
const unit = leaf.unit && resolvePrefUnit(leaf.unit);
|
|
2557
|
+
const stored = values(path);
|
|
2558
|
+
const { min, max, step, endless } = prefDisplayBounds(leaf);
|
|
2559
|
+
const disabled = off || leaf.readout === true;
|
|
2560
|
+
return [{
|
|
2561
|
+
kind: "number",
|
|
2562
|
+
key,
|
|
2563
|
+
label: leaf.name,
|
|
2564
|
+
...withAccess(leaf.access),
|
|
2565
|
+
...leaf.icon ? { icon: leaf.icon } : {},
|
|
2566
|
+
...disabled ? { disabled } : {},
|
|
2567
|
+
value: typeof stored === "number" && (Number.isFinite(stored) || endlessAllows(leaf.endless, stored)) ? unit ? unit.toDisplay(stored) : stored : void 0,
|
|
2568
|
+
...min === void 0 ? {} : { min },
|
|
2569
|
+
...max === void 0 ? {} : { max },
|
|
2570
|
+
step,
|
|
2571
|
+
...unit?.suffix === void 0 ? {} : { suffix: unit.suffix },
|
|
2572
|
+
...leaf.display ? { display: leaf.display } : {},
|
|
2573
|
+
...endless ? { endless } : {},
|
|
2574
|
+
...leaf.infinity === void 0 ? {} : { infinity: leaf.infinity },
|
|
2575
|
+
...unit ? { accepts: prefUnitAccepts(unit) } : {},
|
|
2576
|
+
set: (shown) => onChange(path, unit ? unit.fromDisplay(shown) : shown)
|
|
2577
|
+
}];
|
|
2578
|
+
}
|
|
2579
|
+
var textAt = (ctx, path) => {
|
|
2580
|
+
const v = ctx.src.values?.(path);
|
|
2581
|
+
return typeof v === "string" && v !== "" ? v : void 0;
|
|
2582
|
+
};
|
|
2583
|
+
function colorRows(ctx, leaf, key, path, off) {
|
|
2584
|
+
if (!ctx.src.values) return [];
|
|
2585
|
+
return [{ kind: "color", key, label: leaf.name, ...withAccess(leaf.access), ...off ? { disabled: true } : {}, value: textAt(ctx, path) }];
|
|
2586
|
+
}
|
|
2587
|
+
function stringRows(ctx, leaf, key, path) {
|
|
2588
|
+
if (!ctx.src.values) return [];
|
|
2589
|
+
return [{ kind: "item", key, label: `${leaf.name}: ${textAt(ctx, path) ?? "\u2014"}`, ...withAccess(leaf.access), disabled: true, run: noop }];
|
|
2590
|
+
}
|
|
2591
|
+
function leafRows(ctx, leaf, key, path, off) {
|
|
2592
|
+
switch (leaf.kind) {
|
|
2593
|
+
case "action":
|
|
2594
|
+
return actionRows(ctx, leaf, key, off);
|
|
2595
|
+
case "actions": {
|
|
2596
|
+
const group = leaf.group;
|
|
2597
|
+
return ctx.src.actions().filter((a) => a.group === group).flatMap((a) => actionItems(a).map((it) => itemOf(ctx, it, `${key}/${it.key}`, it.label, off)));
|
|
2598
|
+
}
|
|
2599
|
+
case "boolean":
|
|
2600
|
+
return booleanRows(ctx, leaf, key, path, off);
|
|
2601
|
+
case "enum":
|
|
2602
|
+
return enumRows(ctx, leaf, key, path, off);
|
|
2603
|
+
case "number":
|
|
2604
|
+
return numberRows(ctx, leaf, key, path, off);
|
|
2605
|
+
case "color":
|
|
2606
|
+
return colorRows(ctx, leaf, key, path, off);
|
|
2607
|
+
case "string":
|
|
2608
|
+
return stringRows(ctx, leaf, key, path);
|
|
2609
|
+
case "separator":
|
|
2610
|
+
return [sep(key)];
|
|
2611
|
+
case "label":
|
|
2612
|
+
return [{ kind: "item", key, label: leaf.name, ...withAccess(leaf.access), disabled: true, run: noop }];
|
|
2613
|
+
case "alias": {
|
|
2614
|
+
const alias = leaf;
|
|
2615
|
+
const target = prefAliasTarget(alias, (p) => prefLeafAt(ctx.src.over ?? ctx.root, p));
|
|
2616
|
+
return target ? leafRows(ctx, prefAliasedLeaf(alias, target.leaf), key, target.path, off) : [];
|
|
2617
|
+
}
|
|
2618
|
+
default:
|
|
2619
|
+
return [];
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
function groupRows(ctx, group, prefix, off) {
|
|
2623
|
+
const out = [];
|
|
2624
|
+
for (const [k, node] of Object.entries(group.children)) {
|
|
2625
|
+
const path = prefix ? `${prefix}.${k}` : k;
|
|
2626
|
+
if (ctx.read && !prefIsShown(node, ctx.read)) continue;
|
|
2627
|
+
const disabled = off || ctx.read !== void 0 && !prefIsEnabled(node, ctx.read);
|
|
2628
|
+
if (isPrefLeaf(node)) {
|
|
2629
|
+
out.push(...leafRows(ctx, node, path, path, disabled));
|
|
2630
|
+
} else if (node.as !== void 0 && SUBMENU_AS.has(node.as)) {
|
|
2631
|
+
const entries = tidy(groupRows(ctx, node, path, disabled));
|
|
2632
|
+
if (entries.length) {
|
|
2633
|
+
out.push({ kind: "submenu", key: path, label: node.name, ...node.icon ? { icon: node.icon } : {}, ...withAccess(node.access), entries });
|
|
2634
|
+
}
|
|
2635
|
+
} else {
|
|
2636
|
+
out.push(sep(`${path}/<`), ...groupRows(ctx, node, path, disabled), sep(`${path}/>`));
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
return out;
|
|
2640
|
+
}
|
|
2641
|
+
function tidy(entries) {
|
|
2642
|
+
const out = [];
|
|
2643
|
+
for (const e of entries) {
|
|
2644
|
+
if (e.kind === "separator" && (!out.length || out[out.length - 1].kind === "separator")) continue;
|
|
2645
|
+
out.push(e);
|
|
2646
|
+
}
|
|
2647
|
+
if (out[out.length - 1]?.kind === "separator") out.pop();
|
|
2648
|
+
return out;
|
|
2649
|
+
}
|
|
2650
|
+
function resolveSchemaMenu(schema, src, at, params) {
|
|
2651
|
+
const values = src.values;
|
|
2652
|
+
const ctx = {
|
|
2653
|
+
src,
|
|
2654
|
+
byId: new Map(src.actions().map((a) => [a.id, a])),
|
|
2655
|
+
root: schema,
|
|
2656
|
+
read: values ? (path, from) => from === "item" ? void 0 : values(path) : void 0,
|
|
2657
|
+
at,
|
|
2658
|
+
params
|
|
2659
|
+
};
|
|
2660
|
+
return tidy(groupRows(ctx, schema, "", false));
|
|
2661
|
+
}
|
|
2662
|
+
function resolveMenu(def, src, at, params) {
|
|
2663
|
+
return resolveSchemaMenu(def.menu, src, at, params);
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2666
|
+
// src/menus/menuHost.ts
|
|
2667
|
+
function createMenuHost(src) {
|
|
2668
|
+
const defs = [];
|
|
2669
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2670
|
+
let open = null;
|
|
2671
|
+
let seq = 0;
|
|
2672
|
+
const emit = () => {
|
|
2673
|
+
for (const fn of listeners) fn();
|
|
2674
|
+
};
|
|
2675
|
+
const close = () => {
|
|
2676
|
+
if (!open) return;
|
|
2677
|
+
open = null;
|
|
2678
|
+
emit();
|
|
2679
|
+
};
|
|
2680
|
+
const resolve = (req) => {
|
|
2681
|
+
const live = mergeMenuDefs(defs, src.activeTool());
|
|
2682
|
+
const def = req.menu !== void 0 ? live.find((d) => d.id === req.menu) : req.hit ? pickMenu(live, req.hit) : void 0;
|
|
2683
|
+
return def ? resolveMenu(def, src, req.world, req.params) : [];
|
|
2684
|
+
};
|
|
2685
|
+
return {
|
|
2686
|
+
define(def, owner = null) {
|
|
2687
|
+
const entry = { def, owner };
|
|
2688
|
+
defs.push(entry);
|
|
2689
|
+
return () => {
|
|
2690
|
+
const i = defs.indexOf(entry);
|
|
2691
|
+
if (i >= 0) defs.splice(i, 1);
|
|
2692
|
+
};
|
|
2693
|
+
},
|
|
2694
|
+
open(req) {
|
|
2695
|
+
const entries = resolve(req);
|
|
2696
|
+
if (!entries.length) {
|
|
2697
|
+
close();
|
|
2698
|
+
return false;
|
|
2699
|
+
}
|
|
2700
|
+
open = {
|
|
2701
|
+
seq: ++seq,
|
|
2702
|
+
entries,
|
|
2703
|
+
client: req.client,
|
|
2704
|
+
world: req.world,
|
|
2705
|
+
...req.under ? { under: req.under } : {},
|
|
2706
|
+
source: req.source ?? "pointer"
|
|
2707
|
+
};
|
|
2708
|
+
emit();
|
|
2709
|
+
return true;
|
|
2710
|
+
},
|
|
2711
|
+
has: (req) => resolve(req).length > 0,
|
|
2712
|
+
close,
|
|
2713
|
+
current: () => open,
|
|
2714
|
+
subscribe(fn) {
|
|
2715
|
+
listeners.add(fn);
|
|
2716
|
+
return () => {
|
|
2717
|
+
listeners.delete(fn);
|
|
2718
|
+
};
|
|
2719
|
+
}
|
|
2720
|
+
};
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
export { ALWAYS, ActionDisabledReason, COARSE_TARGET_SCALE, DEFAULT_ALLOWED_CAPABILITIES, DEFAULT_DEVICE_PROFILE, DEV2 as DEV, DepNode, INGEST_STRING_MIMES, LONG_PRESS_HAPTIC_MS, NEVER, PREDICATE_TARGET, ScopeNode, TOOL_OFFHAND_ID, actionBindings, actionCursor, actionItemTitle, actionItems, actionShortcuts, addCarrySurface, assembleEntries, buildDepsFromRequires, buildRouteRegistry, buildRuleCtx, buildToolOffhandBindings, clientToCanvas, clientToCanvasRect, createDepRegistry, createDispatcher, createLongPressStore, createMenuHost, depNodeOf, describeRule, dlog, dwarn, evaluate, evaluateActive, evaluateEnabled, findConflicts, gestureIdFor, heldKeySpec, historyKey, isDebugEnabled, isTool, itemTypesFromDataTransfer, itemsFromClipboardData, itemsFromDataTransfer, itemsFromFiles, keySpecShortcut, keysHeld, liveScope, makeToolOffhandAction, matchBest, matchSorted, matchSortedWithBarred, matchesKeyBinding, mergeMenuDefs, multiTouchGestureId, offhandKeyFor, openPointerSession, pickMenu, pointerGestureId, publishLiveDispatch, pushOwner, recordModeSwitch, reportRouteConflicts, resolveDeviceProfile, resolveMenu, resolveParams, resolveSchemaMenu, roleReader, routesForSpec, ruleCanHoldIn, rulesExclusive, scopeBindings, startCarry, targetConsultsAffordance, tuneAction, tunedParams, validateActionDefaultBinding, validateActionId };
|
|
2724
|
+
//# sourceMappingURL=chunk-QBQQIFPI.js.map
|
|
2725
|
+
//# sourceMappingURL=chunk-QBQQIFPI.js.map
|