@weasel-js/routing 1.10.0 → 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-4DR6AJMN.js → chunk-QBQQIFPI.js} +625 -49
- package/dist/chunk-QBQQIFPI.js.map +1 -0
- package/dist/{index-Bm2QEV09.d.ts → index-CmfVvwWD.d.ts} +722 -276
- package/dist/index.d.ts +6 -3
- package/dist/index.js +55 -16
- package/dist/index.js.map +1 -1
- package/dist/{internal-D0rDmi7t.d.ts → internal-21M1rTVo.d.ts} +23 -1
- package/dist/internal.d.ts +8 -5
- package/dist/react.d.ts +63 -27
- package/dist/react.js +535 -268
- package/dist/react.js.map +1 -1
- package/package.json +13 -9
- package/dist/chunk-4DR6AJMN.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,6 +332,112 @@ 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
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/interactions/actions/tuning.ts
|
|
369
|
+
var WRAPS = /* @__PURE__ */ new WeakMap();
|
|
370
|
+
function layered(under, over) {
|
|
371
|
+
if (typeof under !== "function" && typeof over !== "function") return tunedParams([{ params: under }], over);
|
|
372
|
+
return () => tunedParams([{ params: under }], resolveParams(over));
|
|
373
|
+
}
|
|
374
|
+
function wrapInvoker(invoker, params) {
|
|
375
|
+
for (let w = invoker; w; w = WRAPS.get(w)?.inner) {
|
|
376
|
+
if (WRAPS.get(w)?.params === params) return invoker;
|
|
377
|
+
}
|
|
378
|
+
const base = (given) => tunedParams([{ params }], given);
|
|
379
|
+
const wrapped = invoker.timing === "immediate" ? { timing: "immediate", run: (deps, given) => invoker.run(deps, base(given)) } : {
|
|
380
|
+
timing: "ongoing",
|
|
381
|
+
start: (ctx, opts) => invoker.start(
|
|
382
|
+
ctx.params ? { ...ctx, params: base(ctx.params) } : ctx,
|
|
383
|
+
{ ...opts, params: layered(params, opts?.params) }
|
|
384
|
+
)
|
|
385
|
+
};
|
|
386
|
+
WRAPS.set(wrapped, { inner: invoker, params });
|
|
387
|
+
return wrapped;
|
|
388
|
+
}
|
|
389
|
+
function withBindings(own, added) {
|
|
390
|
+
const base = own === void 0 ? [] : Array.isArray(own) ? own : [own];
|
|
391
|
+
return [...base, ...added.filter((b) => !base.includes(b))];
|
|
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
|
+
}
|
|
413
|
+
function tuneOnce(action, tuning) {
|
|
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;
|
|
417
|
+
const bindings = tuning.bindings?.length ? withBindings(action.defaultBinding, tuning.bindings) : null;
|
|
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
|
+
}
|
|
422
|
+
return {
|
|
423
|
+
...action,
|
|
424
|
+
...modifierRoles ? { modifierRoles } : {},
|
|
425
|
+
...invoker ? { invoker } : {},
|
|
426
|
+
...cursor ? { cursor } : {},
|
|
427
|
+
...preview ? { preview } : {},
|
|
428
|
+
...bindings ? { defaultBinding: bindings } : {}
|
|
429
|
+
};
|
|
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
|
+
}
|
|
436
|
+
function tuneAction(action, tunings) {
|
|
437
|
+
let out = action;
|
|
438
|
+
for (const t of tunings) out = tuneOnce(out, t);
|
|
439
|
+
return out;
|
|
440
|
+
}
|
|
284
441
|
|
|
285
442
|
// src/interactions/actions/toolOffhand.ts
|
|
286
443
|
var TOOL_OFFHAND_ID = "tool.offhand";
|
|
@@ -301,8 +458,12 @@ function offhandKeyFor(trigger) {
|
|
|
301
458
|
}
|
|
302
459
|
}
|
|
303
460
|
function buildToolOffhandBindings(specs) {
|
|
304
|
-
return specs.map(({ toolId, key }) => ({
|
|
461
|
+
return specs.map(({ toolId, key }) => ({
|
|
462
|
+
spec: { ...heldKeySpec(key), phase: BETWEEN_GESTURES },
|
|
463
|
+
opts: { params: { toolId } }
|
|
464
|
+
}));
|
|
305
465
|
}
|
|
466
|
+
var BETWEEN_GESTURES = [{ channel: "*", phase: "initial" }];
|
|
306
467
|
function heldKeySpec(key) {
|
|
307
468
|
const isModifier = [key].flat().some((k) => MODIFIER_KEYS.has(k));
|
|
308
469
|
return { kind: "key-held", key, mods: { ...isModifier ? ANY_MODS : SHIFT_OR_ALT } };
|
|
@@ -409,6 +570,7 @@ function routeArgsForSpec(spec, fallback) {
|
|
|
409
570
|
if ("key" in spec) return Array.isArray(spec.key) ? spec.key : [spec.key];
|
|
410
571
|
if ("fingers" in spec) return [String(spec.fingers)];
|
|
411
572
|
if ("direction" in spec) return [spec.direction ?? fallback];
|
|
573
|
+
if (spec.kind === "hover") return [spec.motion ?? fallback];
|
|
412
574
|
if ("types" in spec) return [spec.types?.length ? spec.types.join("|") : fallback];
|
|
413
575
|
return [fallback];
|
|
414
576
|
}
|
|
@@ -771,15 +933,7 @@ function buildRuleCtx(args) {
|
|
|
771
933
|
device: args.device
|
|
772
934
|
};
|
|
773
935
|
}
|
|
774
|
-
|
|
775
|
-
// src/interactions/dispatcher/dispatchTrace.ts
|
|
776
|
-
var DEV2 = (() => {
|
|
777
|
-
try {
|
|
778
|
-
return Boolean(import.meta.env?.DEV);
|
|
779
|
-
} catch {
|
|
780
|
-
return false;
|
|
781
|
-
}
|
|
782
|
-
})();
|
|
936
|
+
var DEV2 = isDev();
|
|
783
937
|
var TRACE_LIMIT = 200;
|
|
784
938
|
var traceLog = DEV2 && typeof window !== "undefined" ? window.__weaselDispatchLog__ ??= [] : [];
|
|
785
939
|
function recordTrace(entry) {
|
|
@@ -822,6 +976,9 @@ function gestureIdFor(event) {
|
|
|
822
976
|
if (event.kind === "multitouch") {
|
|
823
977
|
return multiTouchGestureId(event.fingers);
|
|
824
978
|
}
|
|
979
|
+
if (event.kind === "carry") {
|
|
980
|
+
return `carry-${event.pointerId ?? "mouse"}`;
|
|
981
|
+
}
|
|
825
982
|
return `ongoing-${event.kind}`;
|
|
826
983
|
}
|
|
827
984
|
|
|
@@ -879,6 +1036,8 @@ function worldPointOf(event) {
|
|
|
879
1036
|
case "doubleclick":
|
|
880
1037
|
case "contextmenu":
|
|
881
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 };
|
|
882
1041
|
default:
|
|
883
1042
|
return void 0;
|
|
884
1043
|
}
|
|
@@ -919,8 +1078,19 @@ function immediateParams(event, resolved) {
|
|
|
919
1078
|
};
|
|
920
1079
|
// Only a `stage: 'press'` pointerdown reaches an immediate invoker: the
|
|
921
1080
|
// buffered copy matches `drag` specs, which are ongoing.
|
|
922
|
-
case "pointerdown":
|
|
923
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":
|
|
924
1094
|
case "longpress":
|
|
925
1095
|
return {
|
|
926
1096
|
worldX: event.x,
|
|
@@ -941,6 +1111,16 @@ function immediateParams(event, resolved) {
|
|
|
941
1111
|
mods: modifiersOf(event),
|
|
942
1112
|
...resolved
|
|
943
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
|
+
};
|
|
944
1124
|
case "drop":
|
|
945
1125
|
case "paste":
|
|
946
1126
|
return {
|
|
@@ -962,13 +1142,13 @@ function liveInView(binding, view) {
|
|
|
962
1142
|
function namesView(binding, view) {
|
|
963
1143
|
return binding.opts?.views?.includes(view) ?? false;
|
|
964
1144
|
}
|
|
965
|
-
function preferViewScoped(
|
|
1145
|
+
function preferViewScoped(matches2, view) {
|
|
966
1146
|
const named = [];
|
|
967
1147
|
const rest = [];
|
|
968
|
-
for (const m of
|
|
1148
|
+
for (const m of matches2) {
|
|
969
1149
|
(namesView(m.binding, view) ? named : rest).push(m);
|
|
970
1150
|
}
|
|
971
|
-
return named.length === 0 ? [...
|
|
1151
|
+
return named.length === 0 ? [...matches2] : [...named, ...rest];
|
|
972
1152
|
}
|
|
973
1153
|
function ruleCtxOf(ctx) {
|
|
974
1154
|
const r = ctx.getRuleCtx?.();
|
|
@@ -984,7 +1164,7 @@ function isEligible(action, ruleCtx) {
|
|
|
984
1164
|
function describeEligible(eligible) {
|
|
985
1165
|
return describeRule(eligibleToRule(eligible));
|
|
986
1166
|
}
|
|
987
|
-
function preferContextual(
|
|
1167
|
+
function preferContextual(matches2, actionLookup, holds, view) {
|
|
988
1168
|
const out = [];
|
|
989
1169
|
let run = [];
|
|
990
1170
|
const flush = () => {
|
|
@@ -995,7 +1175,7 @@ function preferContextual(matches, actionLookup, holds, view) {
|
|
|
995
1175
|
out.push(...gated, ...run.filter((m) => !gated.includes(m)));
|
|
996
1176
|
run = [];
|
|
997
1177
|
};
|
|
998
|
-
for (const m of
|
|
1178
|
+
for (const m of matches2) {
|
|
999
1179
|
const head = run[0];
|
|
1000
1180
|
if (head && (head.scope !== m.scope || namesView(head.binding, view) !== namesView(m.binding, view) || !sameSpecificity(head.binding.spec, m.binding.spec))) flush();
|
|
1001
1181
|
run.push(m);
|
|
@@ -1087,8 +1267,10 @@ function createGestureFacts() {
|
|
|
1087
1267
|
const points = /* @__PURE__ */ new Map();
|
|
1088
1268
|
const startSpreads = /* @__PURE__ */ new Map();
|
|
1089
1269
|
const turns = /* @__PURE__ */ new Map();
|
|
1270
|
+
const roles = /* @__PURE__ */ new Map();
|
|
1090
1271
|
return {
|
|
1091
|
-
begin(gestureId, event) {
|
|
1272
|
+
begin(gestureId, event, roleKeys) {
|
|
1273
|
+
if (roleKeys) roles.set(gestureId, roleKeys);
|
|
1092
1274
|
if (event.kind === "pointerdown") {
|
|
1093
1275
|
origins.set(gestureId, {
|
|
1094
1276
|
x: event.x ?? 0,
|
|
@@ -1121,7 +1303,9 @@ function createGestureFacts() {
|
|
|
1121
1303
|
const trail = points.get(gestureId);
|
|
1122
1304
|
const startSpread = startSpreads.get(gestureId);
|
|
1123
1305
|
const turn = turns.get(gestureId);
|
|
1306
|
+
const roleKeys = roles.get(gestureId);
|
|
1124
1307
|
return {
|
|
1308
|
+
...roleKeys !== void 0 ? { roleKeys } : {},
|
|
1125
1309
|
...origin !== void 0 ? { origin } : {},
|
|
1126
1310
|
...trail !== void 0 ? { points: trail } : {},
|
|
1127
1311
|
...startSpread !== void 0 ? { startSpread } : {},
|
|
@@ -1133,12 +1317,14 @@ function createGestureFacts() {
|
|
|
1133
1317
|
points.delete(gestureId);
|
|
1134
1318
|
startSpreads.delete(gestureId);
|
|
1135
1319
|
turns.delete(gestureId);
|
|
1320
|
+
roles.delete(gestureId);
|
|
1136
1321
|
},
|
|
1137
1322
|
clear() {
|
|
1138
1323
|
origins.clear();
|
|
1139
1324
|
points.clear();
|
|
1140
1325
|
startSpreads.clear();
|
|
1141
1326
|
turns.clear();
|
|
1327
|
+
roles.clear();
|
|
1142
1328
|
}
|
|
1143
1329
|
};
|
|
1144
1330
|
}
|
|
@@ -1181,8 +1367,9 @@ function createInFlightGestures() {
|
|
|
1181
1367
|
},
|
|
1182
1368
|
cursor() {
|
|
1183
1369
|
let cursor = null;
|
|
1184
|
-
for (const
|
|
1185
|
-
const
|
|
1370
|
+
for (const [id, handle] of handles) {
|
|
1371
|
+
const action = actions.get(id);
|
|
1372
|
+
const spec = handle.cursor?.() ?? action?.activeCursor ?? actionCursor(action);
|
|
1186
1373
|
if (spec !== void 0 && resolveCursor(spec)) cursor = spec;
|
|
1187
1374
|
}
|
|
1188
1375
|
return cursor;
|
|
@@ -1274,6 +1461,7 @@ function buildInvocationCtx(event, deps, viewId, facts = {}) {
|
|
|
1274
1461
|
};
|
|
1275
1462
|
const screen = clientPoint(event);
|
|
1276
1463
|
if (screen) base.screen = screen;
|
|
1464
|
+
if (facts.roleKeys) base.roles = resolveRoles(facts.roleKeys, base.modifiers, false);
|
|
1277
1465
|
if (event.kind === "key") {
|
|
1278
1466
|
base.key = { key: event.key, repeat: event.repeat ?? false };
|
|
1279
1467
|
} else if (event.kind === "key-held") {
|
|
@@ -1288,6 +1476,9 @@ function buildInvocationCtx(event, deps, viewId, facts = {}) {
|
|
|
1288
1476
|
Object.assign(base, pumpDrag(event, facts));
|
|
1289
1477
|
} else if (event.kind === "multitouch") {
|
|
1290
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 };
|
|
1291
1482
|
}
|
|
1292
1483
|
return base;
|
|
1293
1484
|
}
|
|
@@ -1338,6 +1529,13 @@ function pumpGesture(run, event) {
|
|
|
1338
1529
|
handle.onMove(ctxFor(run, event, {}, gestureId));
|
|
1339
1530
|
return "handled";
|
|
1340
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);
|
|
1341
1539
|
if (event.kind === "pointerup") return ended("commit");
|
|
1342
1540
|
if (event.kind === "pointercancel") return ended("cancel");
|
|
1343
1541
|
if (event.kind === "multitouch" && event.end !== void 0) {
|
|
@@ -1355,7 +1553,23 @@ function pumpGesture(run, event) {
|
|
|
1355
1553
|
}
|
|
1356
1554
|
return void 0;
|
|
1357
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
|
+
}
|
|
1358
1571
|
function invokeAction(run, event, match, action, deps) {
|
|
1572
|
+
if (action.invoker && !carryStageFits(event, action.invoker.timing)) return "empty-handle";
|
|
1359
1573
|
if (action.invoker?.timing === "immediate") {
|
|
1360
1574
|
try {
|
|
1361
1575
|
const params = immediateParams(event, resolveParams(match.binding.opts?.params));
|
|
@@ -1374,7 +1588,8 @@ function invokeAction(run, event, match, action, deps) {
|
|
|
1374
1588
|
return "misbound";
|
|
1375
1589
|
}
|
|
1376
1590
|
const gestureId = gestureIdFor(event);
|
|
1377
|
-
|
|
1591
|
+
const roleKeys = roleKeysOf(action.modifierRoles, match.binding.opts?.roles);
|
|
1592
|
+
run.facts.begin(gestureId, event, roleKeys && platformRoleKeys(roleKeys, run.isMac()));
|
|
1378
1593
|
const handle = action.invoker.start(ctxFor(run, event, deps, gestureId), match.binding.opts);
|
|
1379
1594
|
if (isEmptyOngoingHandle(handle)) {
|
|
1380
1595
|
run.facts.drop(gestureId);
|
|
@@ -1643,9 +1858,14 @@ function resolveOnly(event, ctx, engaged) {
|
|
|
1643
1858
|
actionId: winner.action.id,
|
|
1644
1859
|
action: winner.action,
|
|
1645
1860
|
scope: winner.match.scope,
|
|
1646
|
-
ownerId: winner.match.ownerId
|
|
1861
|
+
ownerId: winner.match.ownerId,
|
|
1862
|
+
...paramsOf(winner.match.binding)
|
|
1647
1863
|
};
|
|
1648
1864
|
}
|
|
1865
|
+
function paramsOf(binding) {
|
|
1866
|
+
const params = resolveParams(binding.opts?.params);
|
|
1867
|
+
return params ? { params } : {};
|
|
1868
|
+
}
|
|
1649
1869
|
function explain(event, ctx, engaged, opts) {
|
|
1650
1870
|
const r = rank(event, ctx, engaged, true);
|
|
1651
1871
|
const walked = walk(r, event, ctx, null, opts?.evaluateShadowed ?? false);
|
|
@@ -1664,10 +1884,12 @@ function createDispatcher(opts) {
|
|
|
1664
1884
|
}
|
|
1665
1885
|
const uiOngoing = createUiOngoing(inFlight, notify, opts?.getAction);
|
|
1666
1886
|
let routedView;
|
|
1667
|
-
|
|
1887
|
+
let routedIsMac = false;
|
|
1888
|
+
const run = { inFlight, facts, viewId: () => routedView, isMac: () => routedIsMac };
|
|
1668
1889
|
const invoking = (event) => (match, action, deps) => invokeAction(run, event, match, action, deps);
|
|
1669
1890
|
function handleInput(event, ctx) {
|
|
1670
1891
|
routedView = ctx.viewId;
|
|
1892
|
+
routedIsMac = ctx.isMac;
|
|
1671
1893
|
const pumped = pumpGesture(run, event);
|
|
1672
1894
|
if (pumped !== void 0) return pumped;
|
|
1673
1895
|
if (event.kind === "hover") return hover(event, ctx);
|
|
@@ -1822,6 +2044,75 @@ function openPointerSession(origin, down, cb, opts = {}) {
|
|
|
1822
2044
|
};
|
|
1823
2045
|
}
|
|
1824
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
|
+
|
|
1825
2116
|
// src/tools/types.ts
|
|
1826
2117
|
function isTool(entry) {
|
|
1827
2118
|
return !!(entry.eligibility?.focus || entry.eligibility?.offhand);
|
|
@@ -2144,6 +2435,291 @@ function dwarn(namespace, ...args) {
|
|
|
2144
2435
|
console.warn(`[${namespace}]`, ...args);
|
|
2145
2436
|
}
|
|
2146
2437
|
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
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
|