@weasel-js/routing 1.13.0 → 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-EUCNVGJV.js +1103 -0
- package/dist/chunk-EUCNVGJV.js.map +1 -0
- package/dist/{chunk-XVWEZTUX.js → chunk-HR5F4LBX.js} +221 -971
- package/dist/chunk-HR5F4LBX.js.map +1 -0
- package/dist/chunk-INZ63QGX.js +325 -0
- package/dist/chunk-INZ63QGX.js.map +1 -0
- package/dist/{index-UFm_a202.d.ts → index-DJ7SxEKS.d.ts} +161 -27
- package/dist/index.d.ts +1 -1
- package/dist/index.js +31 -3
- package/dist/index.js.map +1 -1
- package/dist/{internal-DQJ3fv86.d.ts → internal-CaJ4viHc.d.ts} +7 -22
- package/dist/internal.d.ts +2 -2
- package/dist/react.d.ts +9 -10
- package/dist/react.js +77 -335
- package/dist/react.js.map +1 -1
- package/dist/testing/registerAction.d.ts +22 -0
- package/dist/testing/registerAction.js +15 -0
- package/dist/testing/registerAction.js.map +1 -0
- package/package.json +17 -13
- package/dist/chunk-XVWEZTUX.js.map +0 -1
|
@@ -1,514 +1,10 @@
|
|
|
1
|
+
import { NO_OVERRIDES, resolveParams, formatRoute, overriddenBindings, ActionDisabledReason, keySpecShortcut, actionBindings, canonicalModifiers, paramsMatch, VALID_MOD_NAMES, actionCursor, buildDepsFromRequires, resolveRoles, roleKeysOf, platformRoleKeys } from './chunk-EUCNVGJV.js';
|
|
2
|
+
import { routeGestureForSpecKind, getGestureDescriptor, parseTargetSpec, matchSpec, UNKNOWN_MIME, specificity, formatShortcut, stylusOf } from '@weasel-js/gestures';
|
|
3
|
+
export { GESTURE_DESCRIPTORS, LONG_PRESS_MS, activatesFocusedControl, focusedControlOwnsKey, getGestureDescriptor, isEditableTarget, isKnownGestureName, matchKey, matchModifiers, matchPhase, matchSpec, matchTarget, routeGestureForSpecKind, specKindForRouteGesture, specificity } from '@weasel-js/gestures';
|
|
1
4
|
import { isDev } from '@weasel-js/react';
|
|
2
|
-
import { createEmitter } from '@weasel-js/registry';
|
|
3
|
-
import { RESERVED_ID_PREFIXES, RESERVED_ID_NAMES, isApplePlatform, bindingSpec, routeGestureForSpecKind, getGestureDescriptor, formatRoute, parseTargetSpec, matchSpec, canonicalModifiers, UNKNOWN_MIME, specificity, formatShortcut, VALID_MOD_NAMES, stylusOf } from '@weasel-js/gestures';
|
|
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';
|
|
5
5
|
import { activeModeOf, NORMAL, DEFAULT_MODES, capabilitiesAllowed } from '@weasel-js/modes';
|
|
6
|
+
import { createEmitter } from '@weasel-js/registry';
|
|
6
7
|
import { resolveCursor } from '@weasel-js/cursor';
|
|
7
|
-
import { isPrefLeaf, prefScopeIn, prefScopeUnder, prefConditionHolds, prefAliasTarget, prefLeafAt, prefAliasedLeaf, prefNumberAt, resolvePrefUnit, prefDisplayBounds, prefUnitAccepts, PREF_SCOPE, prefValueAtPath } from '@weasel-js/prefs';
|
|
8
|
-
import { endlessAllows } from '@weasel-js/quantity';
|
|
9
|
-
|
|
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
|
-
}
|
|
36
|
-
|
|
37
|
-
// src/interactions/actions/actionSettings.ts
|
|
38
|
-
var EMPTY = { values: {} };
|
|
39
|
-
function readActionSettings(action, deps) {
|
|
40
|
-
const settings = action.settings;
|
|
41
|
-
if (!settings) return null;
|
|
42
|
-
try {
|
|
43
|
-
return settings.read(deps ?? {});
|
|
44
|
-
} catch {
|
|
45
|
-
return EMPTY;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function actionSettingsParams(settings, path, value) {
|
|
49
|
-
return settings.params ? settings.params(path, value) : { [path]: value };
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// src/interactions/actions/binding.ts
|
|
53
|
-
function actionBindings(action) {
|
|
54
|
-
const gs = action.defaultBinding;
|
|
55
|
-
if (!gs) return [];
|
|
56
|
-
const raw = Array.isArray(gs) ? gs : [gs];
|
|
57
|
-
return raw.map((entry) => {
|
|
58
|
-
const isBoundObj = !("kind" in entry);
|
|
59
|
-
const spec = isBoundObj ? entry.spec : entry;
|
|
60
|
-
const opts = isBoundObj ? entry.opts : void 0;
|
|
61
|
-
return { spec, actionId: action.id, ...opts !== void 0 ? { opts } : {} };
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// src/interactions/actions/actionShortcuts.ts
|
|
66
|
-
function keySpecShortcut(spec) {
|
|
67
|
-
if (spec.kind !== "key" && spec.kind !== "key-held") return void 0;
|
|
68
|
-
const key = Array.isArray(spec.key) ? spec.key[0] : spec.key;
|
|
69
|
-
if (typeof key !== "string" || key.length === 0) return void 0;
|
|
70
|
-
const mods = "mods" in spec ? spec.mods : void 0;
|
|
71
|
-
return {
|
|
72
|
-
key,
|
|
73
|
-
mod: mods?.mod === true,
|
|
74
|
-
alt: mods?.alt === true,
|
|
75
|
-
shift: mods?.shift === true
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
function actionShortcuts(action, params) {
|
|
79
|
-
const out = [];
|
|
80
|
-
const seen = /* @__PURE__ */ new Set();
|
|
81
|
-
for (const { spec, opts } of actionBindings(action)) {
|
|
82
|
-
if (params && !paramsMatch(opts?.params, params)) continue;
|
|
83
|
-
const shortcut = keySpecShortcut(spec);
|
|
84
|
-
if (!shortcut) continue;
|
|
85
|
-
const token = `${shortcut.key}|${shortcut.mod}|${shortcut.alt}|${shortcut.shift}`;
|
|
86
|
-
if (seen.has(token)) continue;
|
|
87
|
-
seen.add(token);
|
|
88
|
-
out.push(shortcut);
|
|
89
|
-
}
|
|
90
|
-
return out;
|
|
91
|
-
}
|
|
92
|
-
function paramsMatch(bound, wanted) {
|
|
93
|
-
if (typeof bound !== "object" || bound === null) return false;
|
|
94
|
-
return Object.entries(wanted).every(([k, v]) => bound[k] === v);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// src/interactions/actions/action.ts
|
|
98
|
-
var ActionDisabledReason = {
|
|
99
|
-
SelectionRequired: "selection-required",
|
|
100
|
-
SceneEmpty: "scene-empty",
|
|
101
|
-
NotApplicable: "not-applicable",
|
|
102
|
-
/** Sentinel: the predicate threw. Surfaced by `evaluateEnabled`'s catch. */
|
|
103
|
-
PredicateThrew: "predicate-threw"
|
|
104
|
-
};
|
|
105
|
-
function actionCursor(action, params) {
|
|
106
|
-
const c = action?.cursor;
|
|
107
|
-
return typeof c === "function" ? c(params) : c;
|
|
108
|
-
}
|
|
109
|
-
var enabledSlowWarned = /* @__PURE__ */ new Set();
|
|
110
|
-
var enabledThrewWarned = /* @__PURE__ */ new Set();
|
|
111
|
-
var ENABLED_BUDGET_MS = 4;
|
|
112
|
-
function evaluateEnabled(action, deps, at) {
|
|
113
|
-
if (!action.enabled) return { enabled: true };
|
|
114
|
-
const dev = isDev();
|
|
115
|
-
const now = typeof performance !== "undefined" && typeof performance.now === "function" ? () => performance.now() : () => Date.now();
|
|
116
|
-
const start = dev ? now() : 0;
|
|
117
|
-
try {
|
|
118
|
-
const result = action.enabled(deps, at);
|
|
119
|
-
if (dev) {
|
|
120
|
-
const elapsed = now() - start;
|
|
121
|
-
if (elapsed > ENABLED_BUDGET_MS && !enabledSlowWarned.has(action.id)) {
|
|
122
|
-
enabledSlowWarned.add(action.id);
|
|
123
|
-
console.warn(
|
|
124
|
-
`weasel actions: enabled() for action "${action.id}" took ${elapsed.toFixed(2)}ms (budget ${ENABLED_BUDGET_MS}ms). The predicate must be pure and fast \u2014 see Action.enabled JSDoc.`
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (result === true) return { enabled: true };
|
|
129
|
-
return { enabled: false, reason: result };
|
|
130
|
-
} catch (e) {
|
|
131
|
-
if (dev && !enabledThrewWarned.has(action.id)) {
|
|
132
|
-
enabledThrewWarned.add(action.id);
|
|
133
|
-
console.warn(
|
|
134
|
-
`weasel actions: enabled() for action "${action.id}" threw; treating as disabled. The predicate must not throw \u2014 see Action.enabled JSDoc.`,
|
|
135
|
-
e
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
return { enabled: false, reason: ActionDisabledReason.PredicateThrew };
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
function evaluateActive(action, deps) {
|
|
142
|
-
if (!action.active) return void 0;
|
|
143
|
-
try {
|
|
144
|
-
return action.active(deps) === true;
|
|
145
|
-
} catch {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
var DEV = isDev();
|
|
150
|
-
function buildDepsFromRequires(action, depRegistry) {
|
|
151
|
-
const requires = action.requires ?? [];
|
|
152
|
-
const entries = requires.map((name) => [name, depRegistry.get(name)]);
|
|
153
|
-
const deps = Object.fromEntries(entries);
|
|
154
|
-
if (DEV) {
|
|
155
|
-
const declared = new Set(requires);
|
|
156
|
-
return new Proxy(deps, {
|
|
157
|
-
get(target, prop, receiver) {
|
|
158
|
-
if (typeof prop === "string" && !declared.has(prop) && prop !== "then") {
|
|
159
|
-
const message = `[weasel:dispatcher] action "${action.id}" read deps.${prop} but did not declare it in \`requires\`. Add \`requires: [...'${prop}']\` to the descriptor or the dep will be undefined at runtime.`;
|
|
160
|
-
console.warn(message);
|
|
161
|
-
throw new Error(message);
|
|
162
|
-
}
|
|
163
|
-
return Reflect.get(target, prop, receiver);
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
return deps;
|
|
168
|
-
}
|
|
169
|
-
var ScopeNode = class {
|
|
170
|
-
/** `descends`: whether reads made here answer from the active child down —
|
|
171
|
-
* true for a root or a yoke, which chrome reads through; false for a scope,
|
|
172
|
-
* which answers for itself even with a scope nested inside it. */
|
|
173
|
-
constructor(parent, descends) {
|
|
174
|
-
this.parent = parent;
|
|
175
|
-
this.descends = descends;
|
|
176
|
-
this.tree = parent ? parent.tree : { version: 0, listeners: createEmitter() };
|
|
177
|
-
}
|
|
178
|
-
parent;
|
|
179
|
-
descends;
|
|
180
|
-
tree;
|
|
181
|
-
children = [];
|
|
182
|
-
activeChild = null;
|
|
183
|
-
/** Bump the tree's version and tell every listener in it. */
|
|
184
|
-
changed() {
|
|
185
|
-
this.tree.version++;
|
|
186
|
-
this.tree.listeners.emit();
|
|
187
|
-
}
|
|
188
|
-
/** Join the parent's children, newest last; returns the leave. */
|
|
189
|
-
mount() {
|
|
190
|
-
const parent = this.parent;
|
|
191
|
-
if (!parent) return () => {
|
|
192
|
-
};
|
|
193
|
-
parent.children.push(this);
|
|
194
|
-
this.changed();
|
|
195
|
-
let left = false;
|
|
196
|
-
return () => {
|
|
197
|
-
if (left) return;
|
|
198
|
-
left = true;
|
|
199
|
-
const i = parent.children.lastIndexOf(this);
|
|
200
|
-
if (i !== -1) parent.children.splice(i, 1);
|
|
201
|
-
if (parent.activeChild === this) parent.activeChild = null;
|
|
202
|
-
this.changed();
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
/** Make this node the one its ancestors resolve through. */
|
|
206
|
-
activate() {
|
|
207
|
-
let moved = false;
|
|
208
|
-
for (let n = this; n.parent; n = n.parent) {
|
|
209
|
-
if (n.parent.activeChild !== n) {
|
|
210
|
-
n.parent.activeChild = n;
|
|
211
|
-
moved = true;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
if (moved) this.changed();
|
|
215
|
-
}
|
|
216
|
-
/** The node a lookup made here starts from: down the active children, or the
|
|
217
|
-
* newest where nothing has been activated yet. A yoke with no members yet
|
|
218
|
-
* is passed over — there is no canvas behind it to answer. */
|
|
219
|
-
leaf() {
|
|
220
|
-
let n = this;
|
|
221
|
-
for (; ; ) {
|
|
222
|
-
const next = n.activeChild ?? n.newestMember();
|
|
223
|
-
if (!next) return n;
|
|
224
|
-
n = next;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
newestMember() {
|
|
228
|
-
for (let i = this.children.length - 1; i >= 0; i--) {
|
|
229
|
-
const c = this.children[i];
|
|
230
|
-
if (!(c.descends && c.children.length === 0)) return c;
|
|
231
|
-
}
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
/** Where a read made through this node starts. */
|
|
235
|
-
start() {
|
|
236
|
-
return this.descends ? this.leaf() : this;
|
|
237
|
-
}
|
|
238
|
-
/** Whether this node is on the path a read from the root takes. */
|
|
239
|
-
isActive() {
|
|
240
|
-
let root = this;
|
|
241
|
-
while (root.parent) root = root.parent;
|
|
242
|
-
for (const n of root.leaf().chain()) if (n === this) return true;
|
|
243
|
-
return false;
|
|
244
|
-
}
|
|
245
|
-
/** This node, then each ancestor up to the root. */
|
|
246
|
-
*chain() {
|
|
247
|
-
for (let n = this; n; n = n.parent) yield n;
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
function pushOwner(stack, value) {
|
|
251
|
-
if (value === null) return () => {
|
|
252
|
-
};
|
|
253
|
-
stack.push(value);
|
|
254
|
-
let released = false;
|
|
255
|
-
return () => {
|
|
256
|
-
if (released) return;
|
|
257
|
-
released = true;
|
|
258
|
-
const i = stack.lastIndexOf(value);
|
|
259
|
-
if (i !== -1) stack.splice(i, 1);
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// src/interactions/actions/depNode.ts
|
|
264
|
-
var DepNode = class extends ScopeNode {
|
|
265
|
-
sources = /* @__PURE__ */ new Map();
|
|
266
|
-
registry = {
|
|
267
|
-
register: (name, source) => {
|
|
268
|
-
const key = name;
|
|
269
|
-
const entry = source;
|
|
270
|
-
const stack = this.sources.get(key);
|
|
271
|
-
if (stack) stack.push(entry);
|
|
272
|
-
else this.sources.set(key, [entry]);
|
|
273
|
-
this.changed();
|
|
274
|
-
let released = false;
|
|
275
|
-
return () => {
|
|
276
|
-
if (released) return;
|
|
277
|
-
released = true;
|
|
278
|
-
const cur = this.sources.get(key);
|
|
279
|
-
if (!cur) return;
|
|
280
|
-
const i = cur.lastIndexOf(entry);
|
|
281
|
-
if (i === -1) return;
|
|
282
|
-
cur.splice(i, 1);
|
|
283
|
-
if (cur.length === 0) this.sources.delete(key);
|
|
284
|
-
this.changed();
|
|
285
|
-
};
|
|
286
|
-
},
|
|
287
|
-
get: (name) => {
|
|
288
|
-
const stacks = [...this.start().chain()].map((n) => n.sources.get(name) ?? []);
|
|
289
|
-
return readFrom(stacks, 0, (stacks[0]?.length ?? 0) - 1);
|
|
290
|
-
},
|
|
291
|
-
subscribe: (listener) => this.tree.listeners.subscribe(listener),
|
|
292
|
-
list: () => {
|
|
293
|
-
const found = /* @__PURE__ */ new Map();
|
|
294
|
-
let depth = 0;
|
|
295
|
-
for (const n of this.start().chain()) {
|
|
296
|
-
for (const [name, stack] of n.sources) {
|
|
297
|
-
if (!found.has(name)) found.set(name, { name, depth, stacked: stack.length });
|
|
298
|
-
}
|
|
299
|
-
depth++;
|
|
300
|
-
}
|
|
301
|
-
return [...found.values()].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
302
|
-
},
|
|
303
|
-
changed: () => this.changed()
|
|
304
|
-
};
|
|
305
|
-
constructor(parent, descends) {
|
|
306
|
-
super(parent, descends);
|
|
307
|
-
NODES.set(this.registry, this);
|
|
308
|
-
}
|
|
309
|
-
};
|
|
310
|
-
function readFrom(stacks, scope, index) {
|
|
311
|
-
if (scope >= stacks.length) return void 0;
|
|
312
|
-
if (index < 0) return readFrom(stacks, scope + 1, (stacks[scope + 1]?.length ?? 0) - 1);
|
|
313
|
-
return stacks[scope][index](() => readFrom(stacks, scope, index - 1));
|
|
314
|
-
}
|
|
315
|
-
var NODES = /* @__PURE__ */ new WeakMap();
|
|
316
|
-
function depNodeOf(registry) {
|
|
317
|
-
return registry ? NODES.get(registry) ?? null : null;
|
|
318
|
-
}
|
|
319
|
-
function createDepRegistry() {
|
|
320
|
-
return new DepNode(null, true).registry;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// src/interactions/actions/registry.ts
|
|
324
|
-
function validateActionId(id) {
|
|
325
|
-
if (id.length === 0) {
|
|
326
|
-
throw new Error(`weasel: action id may not be empty`);
|
|
327
|
-
}
|
|
328
|
-
if (RESERVED_ID_PREFIXES.has(id[0])) {
|
|
329
|
-
throw new Error(
|
|
330
|
-
`weasel: action id "${id}" starts with reserved sigil "${id[0]}" (reserved set: ${[...RESERVED_ID_PREFIXES].join(" ")})`
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
if (RESERVED_ID_NAMES.has(id)) {
|
|
334
|
-
throw new Error(
|
|
335
|
-
`weasel: action id "${id}" collides with a reserved phase keyword (reserved: ${[...RESERVED_ID_NAMES].join(", ")})`
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
function validateActionDefaultBinding(action) {
|
|
340
|
-
const gs = action.defaultBinding;
|
|
341
|
-
if (!gs) return;
|
|
342
|
-
const entries = Array.isArray(gs) ? gs : [gs];
|
|
343
|
-
for (const entry of entries) {
|
|
344
|
-
const spec = "kind" in entry ? entry : entry.spec;
|
|
345
|
-
const phase = spec.phase;
|
|
346
|
-
if (phase === void 0) continue;
|
|
347
|
-
const atoms = Array.isArray(phase) ? phase : [{ channel: "&", phase }];
|
|
348
|
-
for (const atom of atoms) {
|
|
349
|
-
if (atom.channel === "&") {
|
|
350
|
-
throw new Error(
|
|
351
|
-
`weasel: action "${action.id}" defaultBinding uses '&' channel which has no owning tool to resolve to. Use a named tool channel (e.g. '[rect:engaged]') or '*' instead, or move this binding to the tool's own Tool.bindings.`
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// src/interactions/actions/invoker.ts
|
|
359
|
-
function resolveParams(params) {
|
|
360
|
-
if (params === void 0) return void 0;
|
|
361
|
-
return typeof params === "function" ? params() : params;
|
|
362
|
-
}
|
|
363
|
-
function keysHeld(keys, modifiers, isMac) {
|
|
364
|
-
return keys.some((k) => {
|
|
365
|
-
if (k === "mod") return isMac ? modifiers.meta : modifiers.ctrl;
|
|
366
|
-
return modifiers[k];
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
function roleKeysOf(roles, rebinds) {
|
|
370
|
-
if (!roles) return void 0;
|
|
371
|
-
const out = {};
|
|
372
|
-
for (const id of Object.keys(roles)) out[id] = rebinds?.[id] ?? roles[id].keys;
|
|
373
|
-
return out;
|
|
374
|
-
}
|
|
375
|
-
function platformRoleKeys(keys, isMac) {
|
|
376
|
-
const out = {};
|
|
377
|
-
for (const id of Object.keys(keys)) out[id] = keys[id].map((k) => k === "mod" ? isMac ? "meta" : "ctrl" : k);
|
|
378
|
-
return out;
|
|
379
|
-
}
|
|
380
|
-
function resolveRoles(keys, modifiers, isMac) {
|
|
381
|
-
const out = {};
|
|
382
|
-
for (const id of Object.keys(keys)) out[id] = keysHeld(keys[id], modifiers, isMac);
|
|
383
|
-
return out;
|
|
384
|
-
}
|
|
385
|
-
function rebindRoles(roles, rebinds) {
|
|
386
|
-
const out = {};
|
|
387
|
-
for (const id of Object.keys(roles)) {
|
|
388
|
-
out[id] = rebinds[id] ? { ...roles[id], keys: rebinds[id] } : roles[id];
|
|
389
|
-
}
|
|
390
|
-
return out;
|
|
391
|
-
}
|
|
392
|
-
function roleReader(roles) {
|
|
393
|
-
return (ctx, id) => ctx.roles?.[id] ?? keysHeld(roles[id].keys, ctx.modifiers, isApplePlatform());
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// src/interactions/actions/tuning.ts
|
|
397
|
-
var WRAPS = /* @__PURE__ */ new WeakMap();
|
|
398
|
-
function layered(under, over) {
|
|
399
|
-
if (typeof under !== "function" && typeof over !== "function") return tunedParams([{ params: under }], over);
|
|
400
|
-
return () => tunedParams([{ params: under }], resolveParams(over));
|
|
401
|
-
}
|
|
402
|
-
function wrapInvoker(invoker, params) {
|
|
403
|
-
for (let w = invoker; w; w = WRAPS.get(w)?.inner) {
|
|
404
|
-
if (WRAPS.get(w)?.params === params) return invoker;
|
|
405
|
-
}
|
|
406
|
-
const base = (given) => tunedParams([{ params }], given);
|
|
407
|
-
const wrapped = invoker.timing === "immediate" ? { timing: "immediate", run: (deps, given) => invoker.run(deps, base(given)) } : {
|
|
408
|
-
timing: "ongoing",
|
|
409
|
-
start: (ctx, opts) => invoker.start(
|
|
410
|
-
ctx.params ? { ...ctx, params: base(ctx.params) } : ctx,
|
|
411
|
-
{ ...opts, params: layered(params, opts?.params) }
|
|
412
|
-
)
|
|
413
|
-
};
|
|
414
|
-
WRAPS.set(wrapped, { inner: invoker, params });
|
|
415
|
-
return wrapped;
|
|
416
|
-
}
|
|
417
|
-
function withBindings(own, added) {
|
|
418
|
-
const base = own === void 0 ? [] : Array.isArray(own) ? own : [own];
|
|
419
|
-
return [...base, ...added.filter((b) => !base.includes(b))];
|
|
420
|
-
}
|
|
421
|
-
var CURSOR_WRAPS = /* @__PURE__ */ new WeakMap();
|
|
422
|
-
function wrapCursor(cursor, params) {
|
|
423
|
-
if (typeof cursor !== "function") return cursor;
|
|
424
|
-
for (let w = cursor; typeof w === "function"; w = CURSOR_WRAPS.get(w)?.inner) {
|
|
425
|
-
if (CURSOR_WRAPS.get(w)?.params === params) return cursor;
|
|
426
|
-
}
|
|
427
|
-
const wrapped = (given) => cursor(tunedParams([{ params }], given));
|
|
428
|
-
CURSOR_WRAPS.set(wrapped, { inner: cursor, params });
|
|
429
|
-
return wrapped;
|
|
430
|
-
}
|
|
431
|
-
var PREVIEW_WRAPS = /* @__PURE__ */ new WeakMap();
|
|
432
|
-
function wrapPreview(preview, params) {
|
|
433
|
-
if (!preview) return preview;
|
|
434
|
-
for (let w = preview; w; w = PREVIEW_WRAPS.get(w)?.inner) {
|
|
435
|
-
if (PREVIEW_WRAPS.get(w)?.params === params) return preview;
|
|
436
|
-
}
|
|
437
|
-
const wrapped = (deps, given) => preview(deps, tunedParams([{ params }], given));
|
|
438
|
-
PREVIEW_WRAPS.set(wrapped, { inner: preview, params });
|
|
439
|
-
return wrapped;
|
|
440
|
-
}
|
|
441
|
-
function tuneOnce(action, tuning) {
|
|
442
|
-
const invoker = tuning.params && action.invoker ? wrapInvoker(action.invoker, tuning.params) : action.invoker;
|
|
443
|
-
const cursor = tuning.params ? wrapCursor(action.cursor, tuning.params) : action.cursor;
|
|
444
|
-
const preview = tuning.params ? wrapPreview(action.preview, tuning.params) : action.preview;
|
|
445
|
-
const bindings = tuning.bindings?.length ? withBindings(action.defaultBinding, tuning.bindings) : null;
|
|
446
|
-
const modifierRoles = tuning.roles && action.modifierRoles ? rebindRoles(action.modifierRoles, tuning.roles) : null;
|
|
447
|
-
if (invoker === action.invoker && cursor === action.cursor && preview === action.preview && bindings === null && modifierRoles === null) {
|
|
448
|
-
return action;
|
|
449
|
-
}
|
|
450
|
-
return {
|
|
451
|
-
...action,
|
|
452
|
-
...modifierRoles ? { modifierRoles } : {},
|
|
453
|
-
...invoker ? { invoker } : {},
|
|
454
|
-
...cursor ? { cursor } : {},
|
|
455
|
-
...preview ? { preview } : {},
|
|
456
|
-
...bindings ? { defaultBinding: bindings } : {}
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
function tunedParams(tunings, given) {
|
|
460
|
-
let out = {};
|
|
461
|
-
for (const t of tunings) out = { ...out, ...resolveParams(t.params) };
|
|
462
|
-
return { ...out, ...given };
|
|
463
|
-
}
|
|
464
|
-
function tuneAction(action, tunings) {
|
|
465
|
-
let out = action;
|
|
466
|
-
for (const t of tunings) out = tuneOnce(out, t);
|
|
467
|
-
return out;
|
|
468
|
-
}
|
|
469
|
-
var NO_OVERRIDES = /* @__PURE__ */ new Map();
|
|
470
|
-
function bindingId(b, ownerId, index) {
|
|
471
|
-
return b.opts?.id ?? (ownerId === null ? `${b.actionId}@${index}` : `${ownerId}#${index}`);
|
|
472
|
-
}
|
|
473
|
-
function resolveBindingOverrides(maps) {
|
|
474
|
-
if (maps.length === 0) return NO_OVERRIDES;
|
|
475
|
-
const out = /* @__PURE__ */ new Map();
|
|
476
|
-
for (const map of maps) {
|
|
477
|
-
for (const [id, input] of Object.entries(map)) {
|
|
478
|
-
try {
|
|
479
|
-
out.set(id, bindingSpec(input));
|
|
480
|
-
} catch (err) {
|
|
481
|
-
console.warn(`weasel: binding override "${id}" is not a route: ${err.message}`);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
return out;
|
|
486
|
-
}
|
|
487
|
-
function overridden(b, id, overrides) {
|
|
488
|
-
if (!overrides.has(id)) return b;
|
|
489
|
-
const spec = overrides.get(id);
|
|
490
|
-
return spec === null ? null : { ...b, spec };
|
|
491
|
-
}
|
|
492
|
-
function overriddenBindings(bindings, ownerId, overrides = NO_OVERRIDES) {
|
|
493
|
-
const out = [];
|
|
494
|
-
bindings.forEach((b, i) => {
|
|
495
|
-
const next = overridden(b, bindingId(b, ownerId, i), overrides);
|
|
496
|
-
if (next) out.push(next);
|
|
497
|
-
});
|
|
498
|
-
return out;
|
|
499
|
-
}
|
|
500
|
-
function overrideActionBindings(action, overrides) {
|
|
501
|
-
if (overrides.size === 0 || action.defaultBinding === void 0) return action;
|
|
502
|
-
const declared = actionBindings(action);
|
|
503
|
-
if (!declared.some((b, i) => overrides.has(bindingId(b, null, i)))) return action;
|
|
504
|
-
const kept = [];
|
|
505
|
-
declared.forEach((b, i) => {
|
|
506
|
-
const id = bindingId(b, null, i);
|
|
507
|
-
const next = overridden(b, id, overrides);
|
|
508
|
-
if (next) kept.push({ spec: next.spec, opts: { ...next.opts, id } });
|
|
509
|
-
});
|
|
510
|
-
return { ...action, defaultBinding: kept };
|
|
511
|
-
}
|
|
512
8
|
|
|
513
9
|
// src/interactions/dispatcher/inputFacts.ts
|
|
514
10
|
function modifierStateOf(e) {
|
|
@@ -525,6 +21,8 @@ function worldPointOf(event) {
|
|
|
525
21
|
return event.x !== void 0 && event.y !== void 0 ? { x: event.x, y: event.y } : void 0;
|
|
526
22
|
case "carry":
|
|
527
23
|
return { x: event.x, y: event.y };
|
|
24
|
+
case "wheel":
|
|
25
|
+
return event.x !== void 0 && event.y !== void 0 ? { x: event.x, y: event.y } : void 0;
|
|
528
26
|
default:
|
|
529
27
|
return void 0;
|
|
530
28
|
}
|
|
@@ -533,6 +31,7 @@ function carriedBy(event) {
|
|
|
533
31
|
if (event.kind === "carry") return { items: [], carry: { type: event.type, value: event.value } };
|
|
534
32
|
return event.kind === "drop" || event.kind === "paste" ? { items: event.items } : void 0;
|
|
535
33
|
}
|
|
34
|
+
var at = (x, y) => x !== void 0 && y !== void 0 ? { world: { x, y } } : {};
|
|
536
35
|
function immediateParams(event, resolved) {
|
|
537
36
|
switch (event.kind) {
|
|
538
37
|
case "wheel":
|
|
@@ -541,6 +40,7 @@ function immediateParams(event, resolved) {
|
|
|
541
40
|
deltaY: event.deltaY,
|
|
542
41
|
clientX: event.clientX,
|
|
543
42
|
clientY: event.clientY,
|
|
43
|
+
...at(event.x, event.y),
|
|
544
44
|
affordance: event.affordance,
|
|
545
45
|
...resolved
|
|
546
46
|
};
|
|
@@ -556,11 +56,9 @@ function immediateParams(event, resolved) {
|
|
|
556
56
|
case "click":
|
|
557
57
|
case "doubleclick":
|
|
558
58
|
return {
|
|
559
|
-
|
|
560
|
-
worldY: event.y,
|
|
59
|
+
...at(event.x, event.y),
|
|
561
60
|
affordance: event.affordance,
|
|
562
|
-
|
|
563
|
-
...event.kind === "click" ? { pressX: event.pressX, pressY: event.pressY } : {},
|
|
61
|
+
...event.kind === "click" && event.pressX !== void 0 && event.pressY !== void 0 ? { press: { x: event.pressX, y: event.pressY } } : {},
|
|
564
62
|
mods: modifierStateOf(event),
|
|
565
63
|
...resolved
|
|
566
64
|
};
|
|
@@ -568,8 +66,7 @@ function immediateParams(event, resolved) {
|
|
|
568
66
|
// buffered copy matches `drag` specs, which are ongoing.
|
|
569
67
|
case "contextmenu":
|
|
570
68
|
return {
|
|
571
|
-
|
|
572
|
-
worldY: event.y,
|
|
69
|
+
...at(event.x, event.y),
|
|
573
70
|
clientX: event.clientX,
|
|
574
71
|
clientY: event.clientY,
|
|
575
72
|
affordance: event.affordance,
|
|
@@ -581,8 +78,7 @@ function immediateParams(event, resolved) {
|
|
|
581
78
|
case "pointerdown":
|
|
582
79
|
case "longpress":
|
|
583
80
|
return {
|
|
584
|
-
|
|
585
|
-
worldY: event.y,
|
|
81
|
+
...at(event.x, event.y),
|
|
586
82
|
affordance: event.affordance,
|
|
587
83
|
bodyTarget: event.bodyTarget,
|
|
588
84
|
mods: modifierStateOf(event),
|
|
@@ -590,8 +86,7 @@ function immediateParams(event, resolved) {
|
|
|
590
86
|
};
|
|
591
87
|
case "hover":
|
|
592
88
|
return {
|
|
593
|
-
|
|
594
|
-
worldY: event.y,
|
|
89
|
+
...at(event.x, event.y),
|
|
595
90
|
clientX: event.clientX,
|
|
596
91
|
clientY: event.clientY,
|
|
597
92
|
affordance: event.affordance,
|
|
@@ -602,8 +97,7 @@ function immediateParams(event, resolved) {
|
|
|
602
97
|
};
|
|
603
98
|
case "carry":
|
|
604
99
|
return {
|
|
605
|
-
|
|
606
|
-
worldY: event.y,
|
|
100
|
+
...at(event.x, event.y),
|
|
607
101
|
clientX: event.clientX,
|
|
608
102
|
clientY: event.clientY,
|
|
609
103
|
carry: { type: event.type, value: event.value },
|
|
@@ -618,7 +112,7 @@ function immediateParams(event, resolved) {
|
|
|
618
112
|
return {
|
|
619
113
|
items: event.items,
|
|
620
114
|
via: event.kind,
|
|
621
|
-
...event.kind === "drop"
|
|
115
|
+
...event.kind === "drop" ? at(event.x, event.y) : {},
|
|
622
116
|
...resolved
|
|
623
117
|
};
|
|
624
118
|
default:
|
|
@@ -1251,13 +745,13 @@ function liveForKeys(binding, reach) {
|
|
|
1251
745
|
function namesView(binding, view) {
|
|
1252
746
|
return binding.opts?.views?.includes(view) ?? false;
|
|
1253
747
|
}
|
|
1254
|
-
function preferViewScoped(
|
|
748
|
+
function preferViewScoped(matches, view) {
|
|
1255
749
|
const named = [];
|
|
1256
750
|
const rest = [];
|
|
1257
|
-
for (const m of
|
|
751
|
+
for (const m of matches) {
|
|
1258
752
|
(namesView(m.binding, view) ? named : rest).push(m);
|
|
1259
753
|
}
|
|
1260
|
-
return named.length === 0 ? [...
|
|
754
|
+
return named.length === 0 ? [...matches] : [...named, ...rest];
|
|
1261
755
|
}
|
|
1262
756
|
function ruleCtxOf(ctx) {
|
|
1263
757
|
const r = ctx.getRuleCtx?.();
|
|
@@ -1273,7 +767,7 @@ function isEligible(action, ruleCtx) {
|
|
|
1273
767
|
function describeEligible(eligible) {
|
|
1274
768
|
return describeRule(eligibleToRule(eligible));
|
|
1275
769
|
}
|
|
1276
|
-
function preferContextual(
|
|
770
|
+
function preferContextual(matches, actionLookup, holds, view) {
|
|
1277
771
|
const out = [];
|
|
1278
772
|
let run = [];
|
|
1279
773
|
const flush = () => {
|
|
@@ -1284,7 +778,7 @@ function preferContextual(matches2, actionLookup, holds, view) {
|
|
|
1284
778
|
out.push(...gated, ...run.filter((m) => !gated.includes(m)));
|
|
1285
779
|
run = [];
|
|
1286
780
|
};
|
|
1287
|
-
for (const m of
|
|
781
|
+
for (const m of matches) {
|
|
1288
782
|
const head = run[0];
|
|
1289
783
|
if (head && (head.scope !== m.scope || namesView(head.binding, view) !== namesView(m.binding, view) || !sameSpecificity(head.binding.spec, m.binding.spec))) flush();
|
|
1290
784
|
run.push(m);
|
|
@@ -1399,6 +893,9 @@ function createGestureFacts() {
|
|
|
1399
893
|
sample(gestureId, event) {
|
|
1400
894
|
points.get(gestureId)?.push({ x: event.x, y: event.y, ...stylusOf(event) });
|
|
1401
895
|
},
|
|
896
|
+
resumeTrail(gestureId, trail) {
|
|
897
|
+
if (points.has(gestureId)) points.set(gestureId, [...trail]);
|
|
898
|
+
},
|
|
1402
899
|
turn(gestureId, event) {
|
|
1403
900
|
const turn = turns.get(gestureId);
|
|
1404
901
|
if (!turn || event.angle === void 0) return;
|
|
@@ -1499,6 +996,33 @@ function isEmptyOngoingHandle(h) {
|
|
|
1499
996
|
return !h.onMove && !h.onEnd && !h.overlay && !h.previewIds && !h.previewPose;
|
|
1500
997
|
}
|
|
1501
998
|
|
|
999
|
+
// src/interactions/dispatcher/deferredStart.ts
|
|
1000
|
+
function deferredStart(start, thresholdPx) {
|
|
1001
|
+
let inner = null;
|
|
1002
|
+
const onMove = (ctx) => {
|
|
1003
|
+
if (!inner) {
|
|
1004
|
+
const travel = ctx.drag?.screenDelta;
|
|
1005
|
+
if (!travel || Math.hypot(travel.x, travel.y) < thresholdPx) return;
|
|
1006
|
+
inner = start();
|
|
1007
|
+
}
|
|
1008
|
+
return inner.onMove?.(ctx);
|
|
1009
|
+
};
|
|
1010
|
+
const onEnd = (ctx, reason) => {
|
|
1011
|
+
inner?.onEnd?.(ctx, reason);
|
|
1012
|
+
};
|
|
1013
|
+
const read2 = (key) => {
|
|
1014
|
+
if (key === "onMove") return onMove;
|
|
1015
|
+
if (key === "onEnd") return onEnd;
|
|
1016
|
+
if (!inner) return void 0;
|
|
1017
|
+
const value = Reflect.get(inner, key);
|
|
1018
|
+
return typeof value === "function" ? value.bind(inner) : value;
|
|
1019
|
+
};
|
|
1020
|
+
return new Proxy({}, {
|
|
1021
|
+
get: (_target, key) => read2(key),
|
|
1022
|
+
has: (_target, key) => read2(key) !== void 0
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1502
1026
|
// src/interactions/dispatcher/invocationCtx.ts
|
|
1503
1027
|
function uiInvocationCtx(deps, params) {
|
|
1504
1028
|
return {
|
|
@@ -1527,7 +1051,13 @@ function pressDrag(event) {
|
|
|
1527
1051
|
}
|
|
1528
1052
|
};
|
|
1529
1053
|
}
|
|
1530
|
-
function
|
|
1054
|
+
function localDelta(toLocal, fromX, fromY, toX, toY) {
|
|
1055
|
+
if (!toLocal) return { x: toX - fromX, y: toY - fromY };
|
|
1056
|
+
const a = toLocal(fromX, fromY);
|
|
1057
|
+
const b = toLocal(toX, toY);
|
|
1058
|
+
return { x: b.x - a.x, y: b.y - a.y };
|
|
1059
|
+
}
|
|
1060
|
+
function pumpDrag(event, facts, toLocal) {
|
|
1531
1061
|
const cx = event.x;
|
|
1532
1062
|
const cy = event.y;
|
|
1533
1063
|
const origin = facts.origin;
|
|
@@ -1544,7 +1074,7 @@ function pumpDrag(event, facts) {
|
|
|
1544
1074
|
start: { x: ox, y: oy },
|
|
1545
1075
|
current: { x: cx, y: cy },
|
|
1546
1076
|
delta: { x: cx - ox, y: cy - oy },
|
|
1547
|
-
...hasClient ? { screenDelta:
|
|
1077
|
+
...hasClient ? { screenDelta: localDelta(toLocal, origin.clientX, origin.clientY, eventClientX, eventClientY) } : {},
|
|
1548
1078
|
...points !== void 0 ? { points } : {}
|
|
1549
1079
|
}
|
|
1550
1080
|
};
|
|
@@ -1561,7 +1091,7 @@ function multiTouchOf(event, facts) {
|
|
|
1561
1091
|
...startSpread !== void 0 && event.spread !== void 0 ? { pinch: { startSpread, currentSpread: spread, centroid } } : {}
|
|
1562
1092
|
};
|
|
1563
1093
|
}
|
|
1564
|
-
function buildInvocationCtx(event, deps, viewId, facts = {}) {
|
|
1094
|
+
function buildInvocationCtx(event, deps, viewId, facts = {}, toLocal) {
|
|
1565
1095
|
const base = {
|
|
1566
1096
|
world: { x: 0, y: 0 },
|
|
1567
1097
|
modifiers: { alt: event.altKey, ctrl: event.ctrlKey, meta: event.metaKey, shift: event.shiftKey },
|
|
@@ -1577,12 +1107,13 @@ function buildInvocationCtx(event, deps, viewId, facts = {}) {
|
|
|
1577
1107
|
base.key = { key: event.key, repeat: false };
|
|
1578
1108
|
} else if (event.kind === "wheel") {
|
|
1579
1109
|
base.wheel = { deltaX: event.deltaX, deltaY: event.deltaY, deltaZ: 0 };
|
|
1110
|
+
if (event.x !== void 0 && event.y !== void 0) base.world = { x: event.x, y: event.y };
|
|
1580
1111
|
} else if (event.kind === "pinch") {
|
|
1581
1112
|
base.pinch = { scale: event.scale, rotation: event.rotation };
|
|
1582
1113
|
} else if (event.kind === "pointerdown" || event.kind === "click" || event.kind === "doubleclick" || event.kind === "contextmenu") {
|
|
1583
1114
|
Object.assign(base, pressDrag(event));
|
|
1584
1115
|
} else if (event.kind === "pointermove" || event.kind === "pointerup") {
|
|
1585
|
-
Object.assign(base, pumpDrag(event, facts));
|
|
1116
|
+
Object.assign(base, pumpDrag(event, facts, toLocal));
|
|
1586
1117
|
} else if (event.kind === "multitouch") {
|
|
1587
1118
|
base.multiTouch = multiTouchOf(event, facts);
|
|
1588
1119
|
} else if (event.kind === "carry") {
|
|
@@ -1593,7 +1124,7 @@ function buildInvocationCtx(event, deps, viewId, facts = {}) {
|
|
|
1593
1124
|
}
|
|
1594
1125
|
|
|
1595
1126
|
// src/interactions/dispatcher/gestureRun.ts
|
|
1596
|
-
var ctxFor = (run, event, deps, gestureId) => buildInvocationCtx(event, deps, run.viewId(), run.facts.of(gestureId));
|
|
1127
|
+
var ctxFor = (run, event, deps, gestureId) => buildInvocationCtx(event, deps, run.viewId(), run.facts.of(gestureId), run.toLocal());
|
|
1597
1128
|
function runOnEnd(gestureId, handle, endCtx, reason) {
|
|
1598
1129
|
try {
|
|
1599
1130
|
handle.onEnd?.(endCtx, reason);
|
|
@@ -1606,9 +1137,25 @@ function endGesture(run, gestureId, event, reason) {
|
|
|
1606
1137
|
const endCtx = ctxFor(run, event, {}, gestureId);
|
|
1607
1138
|
const handle = run.inFlight.take(gestureId);
|
|
1608
1139
|
run.facts.drop(gestureId);
|
|
1140
|
+
run.opened.delete(gestureId);
|
|
1609
1141
|
runOnEnd(gestureId, handle, endCtx, reason);
|
|
1610
1142
|
return true;
|
|
1611
1143
|
}
|
|
1144
|
+
function endPassed(run, event) {
|
|
1145
|
+
const gestureId = gestureIdFor(event);
|
|
1146
|
+
const opened = run.opened.get(gestureId);
|
|
1147
|
+
if (opened === void 0) return null;
|
|
1148
|
+
const trail = [...run.facts.of(gestureId).points ?? []];
|
|
1149
|
+
endGesture(run, gestureId, event, "cancel");
|
|
1150
|
+
return { event: opened.event, skip: /* @__PURE__ */ new Set([...opened.passed, opened.action]), trail };
|
|
1151
|
+
}
|
|
1152
|
+
function notePassed(run, event, passed) {
|
|
1153
|
+
const gestureId = gestureIdFor(event);
|
|
1154
|
+
const opened = run.opened.get(gestureId);
|
|
1155
|
+
if (opened === void 0) return;
|
|
1156
|
+
opened.passed = /* @__PURE__ */ new Set([...opened.passed, ...passed.skip]);
|
|
1157
|
+
run.facts.resumeTrail(gestureId, passed.trail.slice(0, -1));
|
|
1158
|
+
}
|
|
1612
1159
|
function endMultiTouch(run, event, reason, except) {
|
|
1613
1160
|
let ended = false;
|
|
1614
1161
|
for (const id of [...run.inFlight.handles.keys()]) {
|
|
@@ -1620,6 +1167,7 @@ function endAllGestures(run, reason) {
|
|
|
1620
1167
|
const stubCtx = uiInvocationCtx({});
|
|
1621
1168
|
const ending = run.inFlight.takeAll();
|
|
1622
1169
|
run.facts.clear();
|
|
1170
|
+
run.opened.clear();
|
|
1623
1171
|
for (const [gestureId, handle] of ending) runOnEnd(gestureId, handle, stubCtx, reason);
|
|
1624
1172
|
}
|
|
1625
1173
|
function pumpGesture(run, event) {
|
|
@@ -1635,8 +1183,8 @@ function pumpGesture(run, event) {
|
|
|
1635
1183
|
if (!handle) return "unhandled";
|
|
1636
1184
|
run.facts.sample(gestureId, event);
|
|
1637
1185
|
if (!handle.onMove) return "unhandled";
|
|
1638
|
-
handle.onMove(ctxFor(run, event, {}, gestureId));
|
|
1639
|
-
return "handled";
|
|
1186
|
+
const said = handle.onMove(ctxFor(run, event, {}, gestureId));
|
|
1187
|
+
return said === "pass" && run.opened.has(gestureId) ? "pass" : "handled";
|
|
1640
1188
|
}
|
|
1641
1189
|
if (event.kind === "wheel") {
|
|
1642
1190
|
for (const [gestureId, handle] of [...inFlight.handles].reverse()) {
|
|
@@ -1682,7 +1230,7 @@ function invokeAction(run, event, match, action, deps) {
|
|
|
1682
1230
|
if (action.invoker?.timing === "immediate") {
|
|
1683
1231
|
try {
|
|
1684
1232
|
const params = immediateParams(event, resolveParams(match.binding.opts?.params));
|
|
1685
|
-
action.invoker.run(deps, params);
|
|
1233
|
+
if (action.invoker.run(deps, params) === "pass") return "empty-handle";
|
|
1686
1234
|
} catch (err) {
|
|
1687
1235
|
console.error(`weasel dispatcher: action "${action.id}" invoker threw`, err);
|
|
1688
1236
|
}
|
|
@@ -1699,103 +1247,22 @@ function invokeAction(run, event, match, action, deps) {
|
|
|
1699
1247
|
const gestureId = gestureIdFor(event);
|
|
1700
1248
|
const roleKeys = roleKeysOf(action.modifierRoles, match.binding.opts?.roles);
|
|
1701
1249
|
run.facts.begin(gestureId, event, roleKeys && platformRoleKeys(roleKeys, run.isMac()));
|
|
1702
|
-
const
|
|
1250
|
+
const { invoker } = action;
|
|
1251
|
+
const pressCtx = ctxFor(run, event, deps, gestureId);
|
|
1252
|
+
const begin = () => invoker.start(pressCtx, match.binding.opts);
|
|
1253
|
+
const threshold = match.binding.opts?.dragThresholdPx ?? invoker.dragThresholdPx;
|
|
1254
|
+
const handle = event.kind === "pointerdown" && threshold !== void 0 && threshold > 0 ? deferredStart(begin, threshold) : begin();
|
|
1703
1255
|
if (isEmptyOngoingHandle(handle)) {
|
|
1704
1256
|
run.facts.drop(gestureId);
|
|
1705
1257
|
return "empty-handle";
|
|
1706
1258
|
}
|
|
1707
1259
|
run.inFlight.open(gestureId, handle, match.ownerId, action);
|
|
1260
|
+
if (event.kind === "pointerdown") run.opened.set(gestureId, { event, action: action.id, passed: /* @__PURE__ */ new Set() });
|
|
1708
1261
|
return "fired";
|
|
1709
1262
|
}
|
|
1710
1263
|
return "fired";
|
|
1711
1264
|
}
|
|
1712
1265
|
|
|
1713
|
-
// src/interactions/dispatcher/uiOngoing.ts
|
|
1714
|
-
function createUiOngoing(inFlight, notify, getAction) {
|
|
1715
|
-
let seq = 0;
|
|
1716
|
-
const byAction = /* @__PURE__ */ new Map();
|
|
1717
|
-
function commitPrior(actionId, deps) {
|
|
1718
|
-
const prevGestureId = byAction.get(actionId);
|
|
1719
|
-
if (prevGestureId === void 0) return;
|
|
1720
|
-
const prevHandle = inFlight.handles.get(prevGestureId);
|
|
1721
|
-
if (prevHandle?.onEnd) {
|
|
1722
|
-
try {
|
|
1723
|
-
prevHandle.onEnd(uiInvocationCtx(deps), "commit");
|
|
1724
|
-
} catch (e) {
|
|
1725
|
-
console.error(`weasel dispatcher: prior UI handle for "${actionId}" threw on auto-commit`, e);
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
inFlight.take(prevGestureId);
|
|
1729
|
-
byAction.delete(actionId);
|
|
1730
|
-
}
|
|
1731
|
-
function control(actionId, gestureId, handle, deps) {
|
|
1732
|
-
let ended = false;
|
|
1733
|
-
const live = () => !ended && inFlight.handles.get(gestureId) === handle;
|
|
1734
|
-
return {
|
|
1735
|
-
gestureId,
|
|
1736
|
-
get active() {
|
|
1737
|
-
return live();
|
|
1738
|
-
},
|
|
1739
|
-
update(nextParams) {
|
|
1740
|
-
if (!live()) return;
|
|
1741
|
-
if (!handle.onMove) return;
|
|
1742
|
-
try {
|
|
1743
|
-
handle.onMove(uiInvocationCtx(deps, nextParams));
|
|
1744
|
-
} catch (e) {
|
|
1745
|
-
console.error(`weasel dispatcher: action "${actionId}" threw on onMove`, e);
|
|
1746
|
-
}
|
|
1747
|
-
notify();
|
|
1748
|
-
},
|
|
1749
|
-
end(reason) {
|
|
1750
|
-
if (!live()) {
|
|
1751
|
-
ended = true;
|
|
1752
|
-
return;
|
|
1753
|
-
}
|
|
1754
|
-
ended = true;
|
|
1755
|
-
if (handle.onEnd) {
|
|
1756
|
-
try {
|
|
1757
|
-
handle.onEnd(uiInvocationCtx(deps), reason);
|
|
1758
|
-
} catch (e) {
|
|
1759
|
-
console.error(`weasel dispatcher: action "${actionId}" threw on onEnd`, e);
|
|
1760
|
-
}
|
|
1761
|
-
}
|
|
1762
|
-
inFlight.take(gestureId);
|
|
1763
|
-
if (byAction.get(actionId) === gestureId) byAction.delete(actionId);
|
|
1764
|
-
notify();
|
|
1765
|
-
}
|
|
1766
|
-
};
|
|
1767
|
-
}
|
|
1768
|
-
return {
|
|
1769
|
-
begin(actionId, deps, params) {
|
|
1770
|
-
if (!getAction) {
|
|
1771
|
-
console.warn("weasel dispatcher: beginUiOngoing called but no getAction was provided to createDispatcher");
|
|
1772
|
-
return null;
|
|
1773
|
-
}
|
|
1774
|
-
const action = getAction(actionId);
|
|
1775
|
-
if (!action || !action.invoker || action.invoker.timing !== "ongoing") {
|
|
1776
|
-
return null;
|
|
1777
|
-
}
|
|
1778
|
-
commitPrior(actionId, deps);
|
|
1779
|
-
const gestureId = `ui-${actionId}-${++seq}`;
|
|
1780
|
-
let handle;
|
|
1781
|
-
try {
|
|
1782
|
-
handle = action.invoker.start(uiInvocationCtx(deps, params), { params });
|
|
1783
|
-
} catch (e) {
|
|
1784
|
-
console.error(`weasel dispatcher: action "${actionId}" threw on start`, e);
|
|
1785
|
-
return null;
|
|
1786
|
-
}
|
|
1787
|
-
if (isEmptyOngoingHandle(handle)) return null;
|
|
1788
|
-
inFlight.open(gestureId, handle, null);
|
|
1789
|
-
byAction.set(actionId, gestureId);
|
|
1790
|
-
notify();
|
|
1791
|
-
return control(actionId, gestureId, handle, deps);
|
|
1792
|
-
},
|
|
1793
|
-
clear: () => {
|
|
1794
|
-
byAction.clear();
|
|
1795
|
-
}
|
|
1796
|
-
};
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
1266
|
// src/interactions/dispatcher/walk.ts
|
|
1800
1267
|
function assembleScopedBindings(ctx) {
|
|
1801
1268
|
const allowedCapabilities = ctx.getRuleCtx?.()?.allowedCapabilities;
|
|
@@ -1959,6 +1426,104 @@ function verdictsOf(r, walked, evaluateShadowed) {
|
|
|
1959
1426
|
return out;
|
|
1960
1427
|
}
|
|
1961
1428
|
|
|
1429
|
+
// src/interactions/dispatcher/handOff.ts
|
|
1430
|
+
function handOff(run, event, ctx, invoking, engaged) {
|
|
1431
|
+
for (let passed = endPassed(run, event); passed !== null; passed = endPassed(run, event)) {
|
|
1432
|
+
const { event: press, skip } = passed;
|
|
1433
|
+
const invoke = invoking(press);
|
|
1434
|
+
walk(rank(press, ctx, engaged(), false), press, ctx, (match, action, deps) => skip.has(action.id) ? "empty-handle" : invoke(match, action, deps), false);
|
|
1435
|
+
notePassed(run, event, passed);
|
|
1436
|
+
if (pumpGesture(run, event) !== "pass") break;
|
|
1437
|
+
}
|
|
1438
|
+
return "handled";
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// src/interactions/dispatcher/uiOngoing.ts
|
|
1442
|
+
function createUiOngoing(inFlight, notify, getAction) {
|
|
1443
|
+
let seq = 0;
|
|
1444
|
+
const byAction = /* @__PURE__ */ new Map();
|
|
1445
|
+
function commitPrior(actionId, deps) {
|
|
1446
|
+
const prevGestureId = byAction.get(actionId);
|
|
1447
|
+
if (prevGestureId === void 0) return;
|
|
1448
|
+
const prevHandle = inFlight.handles.get(prevGestureId);
|
|
1449
|
+
if (prevHandle?.onEnd) {
|
|
1450
|
+
try {
|
|
1451
|
+
prevHandle.onEnd(uiInvocationCtx(deps), "commit");
|
|
1452
|
+
} catch (e) {
|
|
1453
|
+
console.error(`weasel dispatcher: prior UI handle for "${actionId}" threw on auto-commit`, e);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
inFlight.take(prevGestureId);
|
|
1457
|
+
byAction.delete(actionId);
|
|
1458
|
+
}
|
|
1459
|
+
function control(actionId, gestureId, handle, deps) {
|
|
1460
|
+
let ended = false;
|
|
1461
|
+
const live = () => !ended && inFlight.handles.get(gestureId) === handle;
|
|
1462
|
+
return {
|
|
1463
|
+
gestureId,
|
|
1464
|
+
get active() {
|
|
1465
|
+
return live();
|
|
1466
|
+
},
|
|
1467
|
+
update(nextParams) {
|
|
1468
|
+
if (!live()) return;
|
|
1469
|
+
if (!handle.onMove) return;
|
|
1470
|
+
try {
|
|
1471
|
+
handle.onMove(uiInvocationCtx(deps, nextParams));
|
|
1472
|
+
} catch (e) {
|
|
1473
|
+
console.error(`weasel dispatcher: action "${actionId}" threw on onMove`, e);
|
|
1474
|
+
}
|
|
1475
|
+
notify();
|
|
1476
|
+
},
|
|
1477
|
+
end(reason) {
|
|
1478
|
+
if (!live()) {
|
|
1479
|
+
ended = true;
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
ended = true;
|
|
1483
|
+
if (handle.onEnd) {
|
|
1484
|
+
try {
|
|
1485
|
+
handle.onEnd(uiInvocationCtx(deps), reason);
|
|
1486
|
+
} catch (e) {
|
|
1487
|
+
console.error(`weasel dispatcher: action "${actionId}" threw on onEnd`, e);
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
inFlight.take(gestureId);
|
|
1491
|
+
if (byAction.get(actionId) === gestureId) byAction.delete(actionId);
|
|
1492
|
+
notify();
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
return {
|
|
1497
|
+
begin(actionId, deps, params) {
|
|
1498
|
+
if (!getAction) {
|
|
1499
|
+
console.warn("weasel dispatcher: beginUiOngoing called but no getAction was provided to createDispatcher");
|
|
1500
|
+
return null;
|
|
1501
|
+
}
|
|
1502
|
+
const action = getAction(actionId);
|
|
1503
|
+
if (!action || !action.invoker || action.invoker.timing !== "ongoing") {
|
|
1504
|
+
return null;
|
|
1505
|
+
}
|
|
1506
|
+
commitPrior(actionId, deps);
|
|
1507
|
+
const gestureId = `ui-${actionId}-${++seq}`;
|
|
1508
|
+
let handle;
|
|
1509
|
+
try {
|
|
1510
|
+
handle = action.invoker.start(uiInvocationCtx(deps, params), { params });
|
|
1511
|
+
} catch (e) {
|
|
1512
|
+
console.error(`weasel dispatcher: action "${actionId}" threw on start`, e);
|
|
1513
|
+
return null;
|
|
1514
|
+
}
|
|
1515
|
+
if (isEmptyOngoingHandle(handle)) return null;
|
|
1516
|
+
inFlight.open(gestureId, handle, null);
|
|
1517
|
+
byAction.set(actionId, gestureId);
|
|
1518
|
+
notify();
|
|
1519
|
+
return control(actionId, gestureId, handle, deps);
|
|
1520
|
+
},
|
|
1521
|
+
clear: () => {
|
|
1522
|
+
byAction.clear();
|
|
1523
|
+
}
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1962
1527
|
// src/interactions/dispatcher/predict.ts
|
|
1963
1528
|
function resolveAll(event, ctx, engaged, opts) {
|
|
1964
1529
|
const evaluateShadowed = opts?.evaluateShadowed ?? false;
|
|
@@ -2019,6 +1584,10 @@ function routeThroughKeymap(event, ctx, passes) {
|
|
|
2019
1584
|
if (verdict === "taken") return "unhandled";
|
|
2020
1585
|
if (verdict === "cascaded") return passes.run("none", event);
|
|
2021
1586
|
if (isPlainEscape(event)) {
|
|
1587
|
+
if (passes.engaged() && passes.run("none", event) === "handled") {
|
|
1588
|
+
stack.inputSeen();
|
|
1589
|
+
return "handled";
|
|
1590
|
+
}
|
|
2022
1591
|
stack.closeInnermost();
|
|
2023
1592
|
return "handled";
|
|
2024
1593
|
}
|
|
@@ -2063,17 +1632,28 @@ function createDispatcher(opts) {
|
|
|
2063
1632
|
const uiOngoing = createUiOngoing(inFlight, notify, opts?.getAction);
|
|
2064
1633
|
let routedView;
|
|
2065
1634
|
let routedIsMac = false;
|
|
2066
|
-
|
|
1635
|
+
let routedToLocal;
|
|
1636
|
+
const run = {
|
|
1637
|
+
inFlight,
|
|
1638
|
+
facts,
|
|
1639
|
+
viewId: () => routedView,
|
|
1640
|
+
isMac: () => routedIsMac,
|
|
1641
|
+
toLocal: () => routedToLocal,
|
|
1642
|
+
opened: /* @__PURE__ */ new Map()
|
|
1643
|
+
};
|
|
2067
1644
|
const invoking = (event) => (match, action, deps) => invokeAction(run, event, match, action, deps);
|
|
2068
1645
|
function handleInput(event, ctx) {
|
|
2069
1646
|
routedView = ctx.viewId;
|
|
2070
1647
|
routedIsMac = ctx.isMac;
|
|
1648
|
+
routedToLocal = ctx.toLocal;
|
|
2071
1649
|
const pumped = pumpGesture(run, event);
|
|
1650
|
+
if (pumped === "pass") return handOff(run, event, ctx, invoking, inFlight.engagedOwners);
|
|
2072
1651
|
if (pumped !== void 0) return pumped;
|
|
2073
1652
|
if (event.kind === "hover") return hover(event, ctx);
|
|
2074
1653
|
return routeThroughKeymap(event, ctx, {
|
|
2075
1654
|
run: (pass, ev) => route(ev, { ...ctx, keymapPass: pass }),
|
|
2076
|
-
binds: (ev) => rank(ev, { ...ctx, keymapPass: "only" }, inFlight.engagedOwners(), false).ordered.length > 0
|
|
1655
|
+
binds: (ev) => rank(ev, { ...ctx, keymapPass: "only" }, inFlight.engagedOwners(), false).ordered.length > 0,
|
|
1656
|
+
engaged: () => inFlight.handles.size > 0
|
|
2077
1657
|
}) ?? route(event, ctx);
|
|
2078
1658
|
}
|
|
2079
1659
|
function route(event, ctx) {
|
|
@@ -2255,20 +1835,20 @@ function startCarry(origin, down, payload, cb = {}) {
|
|
|
2255
1835
|
const doc = origin.ownerDocument ?? document;
|
|
2256
1836
|
const pointerId = down.pointerId;
|
|
2257
1837
|
let over = null;
|
|
2258
|
-
const leave = (
|
|
1838
|
+
const leave = (at2) => {
|
|
2259
1839
|
const was = over;
|
|
2260
1840
|
over = null;
|
|
2261
|
-
if (was && surfaces.has(was)) was.receive("leave", payload,
|
|
1841
|
+
if (was && surfaces.has(was)) was.receive("leave", payload, at2, pointerId);
|
|
2262
1842
|
};
|
|
2263
|
-
const step = (
|
|
2264
|
-
const next = surfaceAt(doc,
|
|
1843
|
+
const step = (at2) => {
|
|
1844
|
+
const next = surfaceAt(doc, at2.clientX, at2.clientY);
|
|
2265
1845
|
if (next === over) {
|
|
2266
|
-
over?.receive("move", payload,
|
|
1846
|
+
over?.receive("move", payload, at2, pointerId);
|
|
2267
1847
|
return;
|
|
2268
1848
|
}
|
|
2269
|
-
leave(
|
|
1849
|
+
leave(at2);
|
|
2270
1850
|
over = next;
|
|
2271
|
-
next?.receive("enter", payload,
|
|
1851
|
+
next?.receive("enter", payload, at2, pointerId);
|
|
2272
1852
|
};
|
|
2273
1853
|
let last = down;
|
|
2274
1854
|
const session = openPointerSession(origin, down, {
|
|
@@ -2795,14 +2375,17 @@ function itemsFromDataTransfer(dt) {
|
|
|
2795
2375
|
return Promise.all(reads).then(() => [...files, ...strings]);
|
|
2796
2376
|
}
|
|
2797
2377
|
function itemTypesFromDataTransfer(dt) {
|
|
2798
|
-
const
|
|
2378
|
+
const pending = [];
|
|
2799
2379
|
for (const item of Array.from(dt.items ?? [])) {
|
|
2800
2380
|
const mime = item.type ? normalizeMime(item.type) : "";
|
|
2801
|
-
if (item.kind === "file")
|
|
2802
|
-
else if (keepsString(mime))
|
|
2381
|
+
if (item.kind === "file") pending.push({ pending: "file", mime: mime && mime !== "application/x-moz-file" ? mime : UNKNOWN_MIME });
|
|
2382
|
+
else if (keepsString(mime)) pending.push({ pending: "string", mime });
|
|
2803
2383
|
}
|
|
2804
|
-
if (
|
|
2805
|
-
return
|
|
2384
|
+
if (pending.length === 0 && Array.from(dt.types ?? []).includes("Files")) pending.push({ pending: "file", mime: UNKNOWN_MIME });
|
|
2385
|
+
return pending;
|
|
2386
|
+
}
|
|
2387
|
+
function pendingKindOf(item) {
|
|
2388
|
+
return item.pending;
|
|
2806
2389
|
}
|
|
2807
2390
|
function itemsFromClipboardData(cd) {
|
|
2808
2391
|
const out = [];
|
|
@@ -2862,339 +2445,6 @@ function dwarn(namespace, ...args) {
|
|
|
2862
2445
|
console.warn(`[${namespace}]`, ...args);
|
|
2863
2446
|
}
|
|
2864
2447
|
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
return oneOf(when.bodyTarget, hit.bodyTarget) && oneOf(when.bodyKind, hit.bodyKind);
|
|
2869
|
-
}
|
|
2870
|
-
function mergeMenuDefs(defs, activeTool) {
|
|
2871
|
-
const live = [
|
|
2872
|
-
...defs.filter((d) => d.owner === null),
|
|
2873
|
-
...defs.filter((d) => d.owner !== null && d.owner === activeTool)
|
|
2874
|
-
];
|
|
2875
|
-
const byId = /* @__PURE__ */ new Map();
|
|
2876
|
-
for (const { def } of live) {
|
|
2877
|
-
const prev = byId.get(def.id);
|
|
2878
|
-
if (!prev) {
|
|
2879
|
-
byId.set(def.id, { def: { ...def, menu: { ...def.menu, children: { ...def.menu.children } } }, parts: 1 });
|
|
2880
|
-
continue;
|
|
2881
|
-
}
|
|
2882
|
-
const n = prev.parts++;
|
|
2883
|
-
const children = prev.def.menu.children;
|
|
2884
|
-
children[`~sep${n}`] = { kind: "separator", name: "", description: "", default: void 0 };
|
|
2885
|
-
for (const [k, node] of Object.entries(def.menu.children)) {
|
|
2886
|
-
children[Object.hasOwn(children, k) ? `${k}~${n}` : k] = node;
|
|
2887
|
-
}
|
|
2888
|
-
if (!prev.def.when && def.when) prev.def.when = def.when;
|
|
2889
|
-
}
|
|
2890
|
-
return [...byId.values()].map((m) => m.def);
|
|
2891
|
-
}
|
|
2892
|
-
function pickMenu(defs, hit) {
|
|
2893
|
-
return defs.find((d) => d.when !== void 0 && matches(d.when, hit));
|
|
2894
|
-
}
|
|
2895
|
-
var SUBMENU_AS = /* @__PURE__ */ new Set(["menu", "page", "tab", "panel"]);
|
|
2896
|
-
var noop = () => {
|
|
2897
|
-
};
|
|
2898
|
-
var withAccess = (access) => access ? { access } : {};
|
|
2899
|
-
var sep = (key) => ({ kind: "separator", key });
|
|
2900
|
-
function itemOf(ctx, it, key, label, off, access) {
|
|
2901
|
-
const deps = ctx.src.depsFor(it.action);
|
|
2902
|
-
const point = ctx.at ? { worldX: ctx.at.x, worldY: ctx.at.y } : {};
|
|
2903
|
-
if (it.action.settings) return settingsOf(ctx, it, key, label, off || !evaluateEnabled(it.action, deps, ctx.at).enabled, access);
|
|
2904
|
-
return {
|
|
2905
|
-
kind: "item",
|
|
2906
|
-
key,
|
|
2907
|
-
label,
|
|
2908
|
-
...withAccess(access),
|
|
2909
|
-
icon: it.icon,
|
|
2910
|
-
shortcut: actionShortcuts(it.action, it.params)[0],
|
|
2911
|
-
disabled: off || !evaluateEnabled(it.action, deps, ctx.at).enabled,
|
|
2912
|
-
checked: evaluateActive(it.action, deps),
|
|
2913
|
-
run: () => {
|
|
2914
|
-
ctx.src.trigger(it.action.id, { ...ctx.params, ...it.params, ...point });
|
|
2915
|
-
}
|
|
2916
|
-
};
|
|
2917
|
-
}
|
|
2918
|
-
function settingsOf(ctx, it, key, label, off, access) {
|
|
2919
|
-
const settings = it.action.settings;
|
|
2920
|
-
const state = off ? null : readActionSettings(it.action, ctx.src.depsFor(it.action));
|
|
2921
|
-
const mixed = new Set(state?.mixed ?? []);
|
|
2922
|
-
const values = (path) => mixed.has(path) ? void 0 : prefValueAtPath(state?.values, path);
|
|
2923
|
-
const onChange = (path, value) => {
|
|
2924
|
-
const params = actionSettingsParams(settings, path, value);
|
|
2925
|
-
if (params) ctx.src.trigger(it.action.id, { ...ctx.params, ...it.params, ...params });
|
|
2926
|
-
};
|
|
2927
|
-
const form = {
|
|
2928
|
-
...ctx,
|
|
2929
|
-
src: { ...ctx.src, values, onChange, over: settings.schema },
|
|
2930
|
-
root: settings.schema,
|
|
2931
|
-
read: (path, from) => from === "item" ? void 0 : values(path)
|
|
2932
|
-
};
|
|
2933
|
-
const entries = state ? tidy(groupRows(form, settings.schema, "", ROOT)) : [];
|
|
2934
|
-
return {
|
|
2935
|
-
kind: "submenu",
|
|
2936
|
-
key,
|
|
2937
|
-
label,
|
|
2938
|
-
...it.icon ? { icon: it.icon } : {},
|
|
2939
|
-
...withAccess(access),
|
|
2940
|
-
...entries.length === 0 ? { disabled: true } : {},
|
|
2941
|
-
entries
|
|
2942
|
-
};
|
|
2943
|
-
}
|
|
2944
|
-
function itemsNamed(ctx, name) {
|
|
2945
|
-
const action = ctx.byId.get(name);
|
|
2946
|
-
if (action) return actionItems(action);
|
|
2947
|
-
const colon = name.indexOf(":");
|
|
2948
|
-
const owner = colon > 0 ? ctx.byId.get(name.slice(0, colon)) : void 0;
|
|
2949
|
-
return owner ? actionItems(owner).filter((it) => it.key === name) : [];
|
|
2950
|
-
}
|
|
2951
|
-
function actionRows(ctx, leaf, key, off) {
|
|
2952
|
-
const own = leaf.label || leaf.name;
|
|
2953
|
-
if (leaf.action === void 0) {
|
|
2954
|
-
if (!own) return [];
|
|
2955
|
-
if (isDev()) console.warn(`weasel: menu row "${key}" names no registered action, so it does nothing`);
|
|
2956
|
-
return [{ kind: "item", key, label: own, disabled: true, run: noop }];
|
|
2957
|
-
}
|
|
2958
|
-
const items = itemsNamed(ctx, leaf.action);
|
|
2959
|
-
if (items.length === 1) return [itemOf(ctx, items[0], key, own || items[0].label, off, leaf.access)];
|
|
2960
|
-
return items.map((it) => itemOf(ctx, it, `${key}/${it.key}`, it.label, off));
|
|
2961
|
-
}
|
|
2962
|
-
function siblingsOf(ctx, path) {
|
|
2963
|
-
const dot = path.lastIndexOf(".");
|
|
2964
|
-
const parent = dot < 0 ? void 0 : ctx.src.values(path.slice(0, dot));
|
|
2965
|
-
return parent && typeof parent === "object" ? parent : void 0;
|
|
2966
|
-
}
|
|
2967
|
-
function booleanRows(ctx, leaf, key, path, off) {
|
|
2968
|
-
const { values, onChange } = ctx.src;
|
|
2969
|
-
if (!values || !onChange) return [];
|
|
2970
|
-
const stored = values(path);
|
|
2971
|
-
const on = leaf.encoding ? leaf.encoding.read(stored, siblingsOf(ctx, path)) : stored === true;
|
|
2972
|
-
const write = (v) => leaf.encoding ? leaf.encoding.write(v, siblingsOf(ctx, path)) : v;
|
|
2973
|
-
return [{
|
|
2974
|
-
kind: "item",
|
|
2975
|
-
key,
|
|
2976
|
-
label: leaf.name,
|
|
2977
|
-
...withAccess(leaf.access),
|
|
2978
|
-
checked: on,
|
|
2979
|
-
disabled: off || leaf.readout === true,
|
|
2980
|
-
run: leaf.readout ? noop : () => onChange(path, write(!on))
|
|
2981
|
-
}];
|
|
2982
|
-
}
|
|
2983
|
-
function enumRows(ctx, leaf, key, path, off) {
|
|
2984
|
-
const { values, onChange } = ctx.src;
|
|
2985
|
-
if (!values || !onChange) return [];
|
|
2986
|
-
const stored = values(path);
|
|
2987
|
-
const current = leaf.encoding ? leaf.encoding.read(stored, siblingsOf(ctx, path)) : stored;
|
|
2988
|
-
if (leaf.readout) {
|
|
2989
|
-
const shown = leaf.options.find((o) => o.value === current)?.label ?? "\u2014";
|
|
2990
|
-
return [{ kind: "item", key, label: `${leaf.name}: ${shown}`, disabled: true, run: noop }];
|
|
2991
|
-
}
|
|
2992
|
-
const write = (v) => leaf.encoding ? leaf.encoding.write(v, siblingsOf(ctx, path)) : v;
|
|
2993
|
-
return [
|
|
2994
|
-
sep(`${key}/<`),
|
|
2995
|
-
...leaf.options.map((o) => ({
|
|
2996
|
-
kind: "item",
|
|
2997
|
-
key: `${key}/${o.value}`,
|
|
2998
|
-
label: o.label,
|
|
2999
|
-
checked: o.value === current,
|
|
3000
|
-
disabled: off || o.disabled === true,
|
|
3001
|
-
run: () => onChange(path, write(o.value))
|
|
3002
|
-
})),
|
|
3003
|
-
sep(`${key}/>`)
|
|
3004
|
-
];
|
|
3005
|
-
}
|
|
3006
|
-
function numberRows(ctx, given, key, path, off) {
|
|
3007
|
-
const { values, onChange } = ctx.src;
|
|
3008
|
-
if (!values || !onChange) return [];
|
|
3009
|
-
const leaf = prefNumberAt(given, values);
|
|
3010
|
-
const unit = leaf.unit && resolvePrefUnit(leaf.unit);
|
|
3011
|
-
const stored = values(path);
|
|
3012
|
-
const { min, max, step, endless } = prefDisplayBounds(leaf);
|
|
3013
|
-
const disabled = off || leaf.readout === true;
|
|
3014
|
-
return [{
|
|
3015
|
-
kind: "number",
|
|
3016
|
-
key,
|
|
3017
|
-
label: leaf.name,
|
|
3018
|
-
...withAccess(leaf.access),
|
|
3019
|
-
...leaf.icon ? { icon: leaf.icon } : {},
|
|
3020
|
-
...disabled ? { disabled } : {},
|
|
3021
|
-
value: typeof stored === "number" && (Number.isFinite(stored) || endlessAllows(leaf.endless, stored)) ? unit ? unit.toDisplay(stored) : stored : void 0,
|
|
3022
|
-
...min === void 0 ? {} : { min },
|
|
3023
|
-
...max === void 0 ? {} : { max },
|
|
3024
|
-
step,
|
|
3025
|
-
...unit?.suffix === void 0 ? {} : { suffix: unit.suffix },
|
|
3026
|
-
...leaf.display ? { display: leaf.display } : {},
|
|
3027
|
-
...endless ? { endless } : {},
|
|
3028
|
-
...leaf.infinity === void 0 ? {} : { infinity: leaf.infinity },
|
|
3029
|
-
...unit ? { accepts: prefUnitAccepts(unit) } : {},
|
|
3030
|
-
set: (shown) => onChange(path, unit ? unit.fromDisplay(shown) : shown)
|
|
3031
|
-
}];
|
|
3032
|
-
}
|
|
3033
|
-
var textAt = (ctx, path) => {
|
|
3034
|
-
const v = ctx.src.values?.(path);
|
|
3035
|
-
return typeof v === "string" && v !== "" ? v : void 0;
|
|
3036
|
-
};
|
|
3037
|
-
function colorRows(ctx, leaf, key, path, off) {
|
|
3038
|
-
if (!ctx.src.values) return [];
|
|
3039
|
-
return [{ kind: "color", key, label: leaf.name, ...withAccess(leaf.access), ...off ? { disabled: true } : {}, value: textAt(ctx, path) }];
|
|
3040
|
-
}
|
|
3041
|
-
function stringRows(ctx, leaf, key, path, off) {
|
|
3042
|
-
const { values, onChange } = ctx.src;
|
|
3043
|
-
if (!values) return [];
|
|
3044
|
-
if (leaf.readout || !onChange) {
|
|
3045
|
-
return [{ kind: "item", key, label: `${leaf.name}: ${textAt(ctx, path) ?? "\u2014"}`, ...withAccess(leaf.access), disabled: true, run: noop }];
|
|
3046
|
-
}
|
|
3047
|
-
const stored = values(path);
|
|
3048
|
-
return [{
|
|
3049
|
-
kind: "text",
|
|
3050
|
-
key,
|
|
3051
|
-
label: leaf.name,
|
|
3052
|
-
...withAccess(leaf.access),
|
|
3053
|
-
...leaf.icon ? { icon: leaf.icon } : {},
|
|
3054
|
-
...off ? { disabled: true } : {},
|
|
3055
|
-
value: typeof stored === "string" ? stored : void 0,
|
|
3056
|
-
set: (text) => onChange(path, text)
|
|
3057
|
-
}];
|
|
3058
|
-
}
|
|
3059
|
-
function leafRows(ctx, leaf, key, path, off) {
|
|
3060
|
-
switch (leaf.kind) {
|
|
3061
|
-
case "action":
|
|
3062
|
-
return actionRows(ctx, leaf, key, off);
|
|
3063
|
-
case "actions": {
|
|
3064
|
-
const group = leaf.group;
|
|
3065
|
-
return ctx.src.actions().filter((a) => a.group === group).flatMap((a) => actionItems(a).map((it) => itemOf(ctx, it, `${key}/${it.key}`, it.label, off)));
|
|
3066
|
-
}
|
|
3067
|
-
case "boolean":
|
|
3068
|
-
return booleanRows(ctx, leaf, key, path, off);
|
|
3069
|
-
case "enum":
|
|
3070
|
-
return enumRows(ctx, leaf, key, path, off);
|
|
3071
|
-
case "number":
|
|
3072
|
-
return numberRows(ctx, leaf, key, path, off);
|
|
3073
|
-
case "color":
|
|
3074
|
-
return colorRows(ctx, leaf, key, path, off);
|
|
3075
|
-
case "string":
|
|
3076
|
-
return stringRows(ctx, leaf, key, path, off);
|
|
3077
|
-
case "separator":
|
|
3078
|
-
return [sep(key)];
|
|
3079
|
-
case "label":
|
|
3080
|
-
return [{ kind: "item", key, label: leaf.name, ...withAccess(leaf.access), disabled: true, run: noop }];
|
|
3081
|
-
case "alias": {
|
|
3082
|
-
const alias = leaf;
|
|
3083
|
-
const target = prefAliasTarget(alias, (p) => prefLeafAt(ctx.src.over ?? ctx.root, p));
|
|
3084
|
-
return target ? leafRows(ctx, prefAliasedLeaf(alias, target.leaf), key, target.path, off) : [];
|
|
3085
|
-
}
|
|
3086
|
-
default:
|
|
3087
|
-
return [];
|
|
3088
|
-
}
|
|
3089
|
-
}
|
|
3090
|
-
var ROOT = { node: {}, scope: PREF_SCOPE, above: PREF_SCOPE };
|
|
3091
|
-
function groupRows(ctx, group, prefix, owner) {
|
|
3092
|
-
const { read: read2 } = ctx;
|
|
3093
|
-
const holds = (condition) => read2 === void 0 || prefConditionHolds(condition, read2);
|
|
3094
|
-
const out = [];
|
|
3095
|
-
for (const [k, node] of Object.entries(group.children)) {
|
|
3096
|
-
const path = prefix ? `${prefix}.${k}` : k;
|
|
3097
|
-
const sits = isPrefLeaf(node) ? prefScopeIn(owner, path, holds) : owner.scope;
|
|
3098
|
-
const scope = prefScopeUnder(sits, node, holds);
|
|
3099
|
-
if (!scope.shown) continue;
|
|
3100
|
-
const disabled = !scope.enabled;
|
|
3101
|
-
const inner = { node, scope, above: sits };
|
|
3102
|
-
if (isPrefLeaf(node)) {
|
|
3103
|
-
out.push(...leafRows(ctx, node, path, path, disabled));
|
|
3104
|
-
} else if (node.as !== void 0 && SUBMENU_AS.has(node.as)) {
|
|
3105
|
-
const entries = tidy(groupRows(ctx, node, path, inner));
|
|
3106
|
-
if (entries.length) {
|
|
3107
|
-
out.push({ kind: "submenu", key: path, label: node.name, ...node.icon ? { icon: node.icon } : {}, ...withAccess(node.access), entries });
|
|
3108
|
-
}
|
|
3109
|
-
} else {
|
|
3110
|
-
out.push(sep(`${path}/<`), ...groupRows(ctx, node, path, inner), sep(`${path}/>`));
|
|
3111
|
-
}
|
|
3112
|
-
}
|
|
3113
|
-
return out;
|
|
3114
|
-
}
|
|
3115
|
-
function tidy(entries) {
|
|
3116
|
-
const out = [];
|
|
3117
|
-
for (const e of entries) {
|
|
3118
|
-
if (e.kind === "separator" && (!out.length || out[out.length - 1].kind === "separator")) continue;
|
|
3119
|
-
out.push(e);
|
|
3120
|
-
}
|
|
3121
|
-
if (out[out.length - 1]?.kind === "separator") out.pop();
|
|
3122
|
-
return out;
|
|
3123
|
-
}
|
|
3124
|
-
function resolveSchemaMenu(schema, src, at, params) {
|
|
3125
|
-
const values = src.values;
|
|
3126
|
-
const ctx = {
|
|
3127
|
-
src,
|
|
3128
|
-
byId: new Map(src.actions().map((a) => [a.id, a])),
|
|
3129
|
-
root: schema,
|
|
3130
|
-
read: values ? (path, from) => from === "item" ? void 0 : values(path) : void 0,
|
|
3131
|
-
at,
|
|
3132
|
-
params
|
|
3133
|
-
};
|
|
3134
|
-
return tidy(groupRows(ctx, schema, "", ROOT));
|
|
3135
|
-
}
|
|
3136
|
-
function resolveMenu(def, src, at, params) {
|
|
3137
|
-
return resolveSchemaMenu(def.menu, src, at, params);
|
|
3138
|
-
}
|
|
3139
|
-
|
|
3140
|
-
// src/menus/menuHost.ts
|
|
3141
|
-
function createMenuHost(src) {
|
|
3142
|
-
const defs = [];
|
|
3143
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
3144
|
-
let open = null;
|
|
3145
|
-
let seq = 0;
|
|
3146
|
-
const emit = () => {
|
|
3147
|
-
for (const fn of listeners) fn();
|
|
3148
|
-
};
|
|
3149
|
-
const close = () => {
|
|
3150
|
-
if (!open) return;
|
|
3151
|
-
open = null;
|
|
3152
|
-
emit();
|
|
3153
|
-
};
|
|
3154
|
-
const resolve = (req) => {
|
|
3155
|
-
const live = mergeMenuDefs(defs, src.activeTool());
|
|
3156
|
-
const def = req.menu !== void 0 ? live.find((d) => d.id === req.menu) : req.hit ? pickMenu(live, req.hit) : void 0;
|
|
3157
|
-
return def ? resolveMenu(def, src, req.world, req.params) : [];
|
|
3158
|
-
};
|
|
3159
|
-
return {
|
|
3160
|
-
define(def, owner = null) {
|
|
3161
|
-
const entry = { def, owner };
|
|
3162
|
-
defs.push(entry);
|
|
3163
|
-
return () => {
|
|
3164
|
-
const i = defs.indexOf(entry);
|
|
3165
|
-
if (i >= 0) defs.splice(i, 1);
|
|
3166
|
-
};
|
|
3167
|
-
},
|
|
3168
|
-
open(req) {
|
|
3169
|
-
const entries = resolve(req);
|
|
3170
|
-
if (!entries.length) {
|
|
3171
|
-
close();
|
|
3172
|
-
return false;
|
|
3173
|
-
}
|
|
3174
|
-
open = {
|
|
3175
|
-
seq: ++seq,
|
|
3176
|
-
entries,
|
|
3177
|
-
client: req.client,
|
|
3178
|
-
world: req.world,
|
|
3179
|
-
...req.under ? { under: req.under } : {},
|
|
3180
|
-
...req.side ? { side: req.side } : {},
|
|
3181
|
-
source: req.source ?? "pointer"
|
|
3182
|
-
};
|
|
3183
|
-
emit();
|
|
3184
|
-
return true;
|
|
3185
|
-
},
|
|
3186
|
-
has: (req) => resolve(req).length > 0,
|
|
3187
|
-
close,
|
|
3188
|
-
current: () => open,
|
|
3189
|
-
subscribe(fn) {
|
|
3190
|
-
listeners.add(fn);
|
|
3191
|
-
return () => {
|
|
3192
|
-
listeners.delete(fn);
|
|
3193
|
-
};
|
|
3194
|
-
}
|
|
3195
|
-
};
|
|
3196
|
-
}
|
|
3197
|
-
|
|
3198
|
-
export { ALWAYS, ActionDisabledReason, COARSE_TARGET_SCALE, DEFAULT_ALLOWED_CAPABILITIES, DEFAULT_DEVICE_PROFILE, DepNode, INGEST_STRING_MIMES, KEYMAP_HOLD, LONG_PRESS_HAPTIC_MS, NEVER, NO_OVERRIDES, PREDICATE_TARGET, ScopeNode, TOOL_OFFHAND_ID, actionBindings, actionCursor, actionItemTitle, actionItems, actionSettingsParams, actionShortcuts, addCarrySurface, admitIngestMime, anyBinding, assembleEntries, bindingId, buildDepsFromRequires, buildRouteRegistry, buildRuleCtx, buildToolOffhandBindings, carryGestureId, clientToCanvas, clientToCanvasRect, createDepRegistry, createDispatchTrace, createDispatcher, createKeymapStack, createLongPressStore, createMenuHost, createTraceRing, depNodeOf, describeKeymap, describeRule, dlog, dwarn, evaluate, evaluateActive, evaluateEnabled, findConflicts, heldKeySpec, historyKey, isDebugEnabled, isTool, itemTypesFromDataTransfer, itemsFromClipboardData, itemsFromDataTransfer, itemsFromFiles, keySpecShortcut, keymapContribution, keymapLeaderBindingId, keymapLeaderId, keymapShortcuts, keysHeld, liveScope, makeToolOffhandAction, matchBest, matchSorted, matchSortedWithBarred, matchesKeyBinding, mergeMenuDefs, modifierStateOf, multiTouchGestureId, offhandKeyFor, openPointerSession, overriddenBindings, overrideActionBindings, pickMenu, pointerGestureId, pushOwner, readActionSettings, reportRouteConflicts, resolveBindingOverrides, resolveDeviceProfile, resolveMenu, resolveParams, resolveSchemaMenu, roleReader, routesForSpec, ruleCanHoldIn, rulesExclusive, scopeBindings, startCarry, targetConsultsAffordance, tuneAction, tunedParams, validateActionDefaultBinding, validateActionId };
|
|
3199
|
-
//# sourceMappingURL=chunk-XVWEZTUX.js.map
|
|
3200
|
-
//# sourceMappingURL=chunk-XVWEZTUX.js.map
|
|
2448
|
+
export { ALWAYS, COARSE_TARGET_SCALE, DEFAULT_ALLOWED_CAPABILITIES, DEFAULT_DEVICE_PROFILE, INGEST_STRING_MIMES, KEYMAP_HOLD, LONG_PRESS_HAPTIC_MS, NEVER, PREDICATE_TARGET, TOOL_OFFHAND_ID, addCarrySurface, admitIngestMime, anyBinding, assembleEntries, buildRouteRegistry, buildRuleCtx, buildToolOffhandBindings, carryGestureId, clientToCanvas, clientToCanvasRect, createDispatchTrace, createDispatcher, createKeymapStack, createLongPressStore, createTraceRing, describeKeymap, describeRule, dlog, dwarn, evaluate, findConflicts, heldKeySpec, historyKey, immediateParams, isDebugEnabled, isTool, itemTypesFromDataTransfer, itemsFromClipboardData, itemsFromDataTransfer, itemsFromFiles, keymapContribution, keymapLeaderBindingId, keymapLeaderId, keymapShortcuts, liveScope, makeToolOffhandAction, matchBest, matchSorted, matchSortedWithBarred, matchesKeyBinding, modifierStateOf, multiTouchGestureId, offhandKeyFor, openPointerSession, pendingKindOf, pointerGestureId, reportRouteConflicts, resolveDeviceProfile, routesForSpec, ruleCanHoldIn, rulesExclusive, scopeBindings, startCarry, targetConsultsAffordance };
|
|
2449
|
+
//# sourceMappingURL=chunk-HR5F4LBX.js.map
|
|
2450
|
+
//# sourceMappingURL=chunk-HR5F4LBX.js.map
|