@weasel-js/routing 1.9.3 → 1.10.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/README.md +6 -5
- package/dist/{chunk-LOP2JTTA.js → chunk-4DR6AJMN.js} +1093 -922
- package/dist/chunk-4DR6AJMN.js.map +1 -0
- package/dist/{index-CrbDNXT_.d.ts → index-Bm2QEV09.d.ts} +576 -706
- package/dist/index.d.ts +5 -4
- package/dist/index.js +27 -74
- package/dist/index.js.map +1 -1
- package/dist/{internal-B1B6gfOS.d.ts → internal-D0rDmi7t.d.ts} +46 -78
- package/dist/internal.d.ts +5 -4
- package/dist/react.d.ts +140 -98
- package/dist/react.js +870 -896
- package/dist/react.js.map +1 -1
- package/package.json +9 -7
- package/dist/chunk-LOP2JTTA.js.map +0 -1
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
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
|
+
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
|
+
import { activeModeOf, NORMAL, DEFAULT_MODES, capabilitiesAllowed } from '@weasel-js/modes';
|
|
4
5
|
import { resolveCursor } from '@weasel-js/cursor';
|
|
5
6
|
|
|
7
|
+
// src/interactions/actions/action.ts
|
|
8
|
+
var ActionDisabledReason = {
|
|
9
|
+
SelectionRequired: "selection-required",
|
|
10
|
+
SceneEmpty: "scene-empty",
|
|
11
|
+
NotApplicable: "not-applicable",
|
|
12
|
+
/** Sentinel: the predicate threw. Surfaced by `evaluateEnabled`'s catch. */
|
|
13
|
+
PredicateThrew: "predicate-threw"
|
|
14
|
+
};
|
|
15
|
+
|
|
6
16
|
// src/interactions/actions/binding.ts
|
|
7
17
|
function actionBindings(action) {
|
|
8
18
|
const gs = action.defaultBinding;
|
|
@@ -16,6 +26,53 @@ function actionBindings(action) {
|
|
|
16
26
|
});
|
|
17
27
|
}
|
|
18
28
|
|
|
29
|
+
// src/devFlag.ts
|
|
30
|
+
function isDev() {
|
|
31
|
+
return typeof process !== "undefined" ? process.env?.NODE_ENV !== "production" : true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/interactions/actions/actionEnabled.ts
|
|
35
|
+
var enabledSlowWarned = /* @__PURE__ */ new Set();
|
|
36
|
+
var enabledThrewWarned = /* @__PURE__ */ new Set();
|
|
37
|
+
var ENABLED_BUDGET_MS = 4;
|
|
38
|
+
function evaluateEnabled(action, deps) {
|
|
39
|
+
if (!action.enabled) return { enabled: true };
|
|
40
|
+
const dev = isDev();
|
|
41
|
+
const now = typeof performance !== "undefined" && typeof performance.now === "function" ? () => performance.now() : () => Date.now();
|
|
42
|
+
const start = dev ? now() : 0;
|
|
43
|
+
try {
|
|
44
|
+
const result = action.enabled(deps);
|
|
45
|
+
if (dev) {
|
|
46
|
+
const elapsed = now() - start;
|
|
47
|
+
if (elapsed > ENABLED_BUDGET_MS && !enabledSlowWarned.has(action.id)) {
|
|
48
|
+
enabledSlowWarned.add(action.id);
|
|
49
|
+
console.warn(
|
|
50
|
+
`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.`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (result === true) return { enabled: true };
|
|
55
|
+
return { enabled: false, reason: result };
|
|
56
|
+
} catch (e) {
|
|
57
|
+
if (dev && !enabledThrewWarned.has(action.id)) {
|
|
58
|
+
enabledThrewWarned.add(action.id);
|
|
59
|
+
console.warn(
|
|
60
|
+
`weasel actions: enabled() for action "${action.id}" threw; treating as disabled. The predicate must not throw \u2014 see Action.enabled JSDoc.`,
|
|
61
|
+
e
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return { enabled: false, reason: ActionDisabledReason.PredicateThrew };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function evaluateActive(action, deps) {
|
|
68
|
+
if (!action.active) return void 0;
|
|
69
|
+
try {
|
|
70
|
+
return action.active(deps) === true;
|
|
71
|
+
} catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
19
76
|
// src/interactions/actions/buildDeps.ts
|
|
20
77
|
var DEV = (() => {
|
|
21
78
|
try {
|
|
@@ -43,8 +100,6 @@ function buildDepsFromRequires(action, depRegistry) {
|
|
|
43
100
|
}
|
|
44
101
|
return deps;
|
|
45
102
|
}
|
|
46
|
-
|
|
47
|
-
// src/interactions/actions/scopeNode.ts
|
|
48
103
|
var ScopeNode = class {
|
|
49
104
|
/** `descends`: whether reads made here answer from the active child down —
|
|
50
105
|
* true for a root or a yoke, which chrome reads through; false for a scope,
|
|
@@ -52,7 +107,7 @@ var ScopeNode = class {
|
|
|
52
107
|
constructor(parent, descends) {
|
|
53
108
|
this.parent = parent;
|
|
54
109
|
this.descends = descends;
|
|
55
|
-
this.tree = parent ? parent.tree : { version: 0, listeners:
|
|
110
|
+
this.tree = parent ? parent.tree : { version: 0, listeners: createEmitter() };
|
|
56
111
|
}
|
|
57
112
|
parent;
|
|
58
113
|
descends;
|
|
@@ -62,13 +117,7 @@ var ScopeNode = class {
|
|
|
62
117
|
/** Bump the tree's version and tell every listener in it. */
|
|
63
118
|
changed() {
|
|
64
119
|
this.tree.version++;
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
listener();
|
|
68
|
-
} catch (err) {
|
|
69
|
-
console.error("weasel input scope: subscriber threw", err);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
120
|
+
this.tree.listeners.emit();
|
|
72
121
|
}
|
|
73
122
|
/** Join the parent's children, newest last; returns the leave. */
|
|
74
123
|
mount() {
|
|
@@ -155,6 +204,7 @@ var DepNode = class extends ScopeNode {
|
|
|
155
204
|
const stack = this.sources.get(key);
|
|
156
205
|
if (stack) stack.push(entry);
|
|
157
206
|
else this.sources.set(key, [entry]);
|
|
207
|
+
this.changed();
|
|
158
208
|
let released = false;
|
|
159
209
|
return () => {
|
|
160
210
|
if (released) return;
|
|
@@ -165,6 +215,7 @@ var DepNode = class extends ScopeNode {
|
|
|
165
215
|
if (i === -1) return;
|
|
166
216
|
cur.splice(i, 1);
|
|
167
217
|
if (cur.length === 0) this.sources.delete(key);
|
|
218
|
+
this.changed();
|
|
168
219
|
};
|
|
169
220
|
},
|
|
170
221
|
get: (name) => {
|
|
@@ -173,7 +224,9 @@ var DepNode = class extends ScopeNode {
|
|
|
173
224
|
if (source) return source();
|
|
174
225
|
}
|
|
175
226
|
return void 0;
|
|
176
|
-
}
|
|
227
|
+
},
|
|
228
|
+
subscribe: (listener) => this.tree.listeners.subscribe(listener),
|
|
229
|
+
changed: () => this.changed()
|
|
177
230
|
};
|
|
178
231
|
constructor(parent, descends) {
|
|
179
232
|
super(parent, descends);
|
|
@@ -248,11 +301,15 @@ function offhandKeyFor(trigger) {
|
|
|
248
301
|
}
|
|
249
302
|
}
|
|
250
303
|
function buildToolOffhandBindings(specs) {
|
|
251
|
-
return specs.map(({ toolId, key }) => ({
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
304
|
+
return specs.map(({ toolId, key }) => ({ spec: heldKeySpec(key), opts: { params: { toolId } } }));
|
|
305
|
+
}
|
|
306
|
+
function heldKeySpec(key) {
|
|
307
|
+
const isModifier = [key].flat().some((k) => MODIFIER_KEYS.has(k));
|
|
308
|
+
return { kind: "key-held", key, mods: { ...isModifier ? ANY_MODS : SHIFT_OR_ALT } };
|
|
255
309
|
}
|
|
310
|
+
var MODIFIER_KEYS = /* @__PURE__ */ new Set(["Alt", "Control", "Meta", "Shift"]);
|
|
311
|
+
var ANY_MODS = { alt: "optional", ctrl: "optional", meta: "optional", shift: "optional" };
|
|
312
|
+
var SHIFT_OR_ALT = { alt: "optional", shift: "optional" };
|
|
256
313
|
function makeToolOffhandAction(bindings) {
|
|
257
314
|
return {
|
|
258
315
|
id: TOOL_OFFHAND_ID,
|
|
@@ -276,36 +333,94 @@ function makeToolOffhandAction(bindings) {
|
|
|
276
333
|
requires: ["activeTool"]
|
|
277
334
|
};
|
|
278
335
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
function liveScope(id, eligibility, state) {
|
|
282
|
-
const tags = eligibility.capabilities;
|
|
283
|
-
if (tags && tags.length > 0 && state.allows && !state.allows(tags)) return null;
|
|
284
|
-
if (state.engagedIds?.has(id)) return "hotkey";
|
|
285
|
-
if (eligibility.focus && state.focusedId === id) return "active";
|
|
286
|
-
if (eligibility.always || eligibility.claimed) return "ambient";
|
|
287
|
-
return null;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// src/contributions/assemble.ts
|
|
291
|
-
function scopeBindings(entries, state) {
|
|
336
|
+
var PREDICATE_TARGET = "predicate";
|
|
337
|
+
function buildRouteRegistry(entries) {
|
|
292
338
|
const out = [];
|
|
293
|
-
for (const
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
339
|
+
for (const owner of entries) {
|
|
340
|
+
for (const binding of owner.bindings ?? []) {
|
|
341
|
+
const row = entryFor(owner.id, binding.spec, binding.actionId);
|
|
342
|
+
if (!row) continue;
|
|
343
|
+
const { views, params, yields } = binding.opts ?? {};
|
|
344
|
+
out.push({
|
|
345
|
+
...row,
|
|
346
|
+
...views === void 0 ? {} : { views },
|
|
347
|
+
...params === void 0 ? {} : { params },
|
|
348
|
+
...yields ? { yields: true } : {}
|
|
349
|
+
});
|
|
298
350
|
}
|
|
299
351
|
}
|
|
300
352
|
return out;
|
|
301
353
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
354
|
+
function routesForSpec(spec) {
|
|
355
|
+
const gesture = routeGestureForSpecKind(spec.kind);
|
|
356
|
+
if (!gesture) return [];
|
|
357
|
+
const descriptor = getGestureDescriptor(gesture);
|
|
358
|
+
const modifiers = parseModSpec("mods" in spec ? spec.mods : void 0);
|
|
359
|
+
const phases = phaseAtomsForSpec("phase" in spec ? spec.phase : void 0);
|
|
360
|
+
const target = descriptor.hasTarget ? routeTargetForSpec("target" in spec ? spec.target : void 0) ?? "*" : void 0;
|
|
361
|
+
const args = descriptor.arg ? routeArgsForSpec(spec, descriptor.arg.default) : [void 0];
|
|
362
|
+
return args.map((arg) => formatRoute({ phases, gesture, arg, target, modifiers }));
|
|
363
|
+
}
|
|
364
|
+
function entryFor(ownerId, spec, actionId) {
|
|
365
|
+
const gesture = routeGestureForSpecKind(spec.kind);
|
|
366
|
+
if (!gesture) return null;
|
|
367
|
+
const descriptor = getGestureDescriptor(gesture);
|
|
368
|
+
return {
|
|
369
|
+
ownerId,
|
|
370
|
+
actionId,
|
|
371
|
+
gesture,
|
|
372
|
+
phase: phaseOf("phase" in spec ? spec.phase : void 0),
|
|
373
|
+
modifiers: parseModSpec("mods" in spec ? spec.mods : void 0),
|
|
374
|
+
arg: descriptor.arg ? argOf(spec, descriptor.arg.default) : void 0,
|
|
375
|
+
...descriptor.arg ? argAlternativesOf(spec, descriptor.arg.default) : {},
|
|
376
|
+
target: descriptor.hasTarget ? routeTargetForSpec("target" in spec ? spec.target : void 0) : void 0,
|
|
377
|
+
spec
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function phaseOf(phase) {
|
|
381
|
+
if (phase === void 0 || phase === "*") return "any";
|
|
382
|
+
if (phase === "initial" || phase === "engaged") return phase;
|
|
383
|
+
const distinct = new Set(phase.map((atom) => atom.phase));
|
|
384
|
+
if (distinct.size !== 1) return "any";
|
|
385
|
+
const only = [...distinct][0];
|
|
386
|
+
return only === "initial" || only === "engaged" ? only : "any";
|
|
387
|
+
}
|
|
388
|
+
function parseModSpec(mods) {
|
|
389
|
+
if (!mods) return {};
|
|
390
|
+
const out = {};
|
|
391
|
+
for (const [name, req] of Object.entries(mods)) {
|
|
392
|
+
if (req === true) out[name] = "required";
|
|
393
|
+
else if (req === "optional") out[name] = "optional";
|
|
394
|
+
}
|
|
395
|
+
return out;
|
|
396
|
+
}
|
|
397
|
+
function phaseAtomsForSpec(phase) {
|
|
398
|
+
if (phase === void 0) return [{ channel: "*", phase: "*" }];
|
|
399
|
+
if (phase === "initial" || phase === "engaged" || phase === "*") {
|
|
400
|
+
return [{ channel: "&", phase }];
|
|
401
|
+
}
|
|
402
|
+
return phase;
|
|
403
|
+
}
|
|
404
|
+
function routeTargetForSpec(target) {
|
|
405
|
+
if (target === void 0) return void 0;
|
|
406
|
+
return typeof target === "string" ? target : PREDICATE_TARGET;
|
|
407
|
+
}
|
|
408
|
+
function routeArgsForSpec(spec, fallback) {
|
|
409
|
+
if ("key" in spec) return Array.isArray(spec.key) ? spec.key : [spec.key];
|
|
410
|
+
if ("fingers" in spec) return [String(spec.fingers)];
|
|
411
|
+
if ("direction" in spec) return [spec.direction ?? fallback];
|
|
412
|
+
if ("types" in spec) return [spec.types?.length ? spec.types.join("|") : fallback];
|
|
413
|
+
return [fallback];
|
|
414
|
+
}
|
|
415
|
+
function argOf(spec, fallback) {
|
|
416
|
+
const args = routeArgsForSpec(spec, fallback);
|
|
417
|
+
return args.length > 1 ? args.join("|") : args[0];
|
|
418
|
+
}
|
|
419
|
+
function argAlternativesOf(spec, fallback) {
|
|
420
|
+
const types = "types" in spec ? spec.types : void 0;
|
|
421
|
+
const alts = types?.length ? types : routeArgsForSpec(spec, fallback).filter((a) => a !== void 0);
|
|
422
|
+
return alts.length > 1 ? { argAlternatives: alts } : {};
|
|
306
423
|
}
|
|
307
|
-
|
|
308
|
-
// src/interactions/dispatcher/matcher.ts
|
|
309
424
|
var SCOPE_PRIORITY = ["hotkey", "active", "ambient"];
|
|
310
425
|
function targetConsultsAffordance(specTarget) {
|
|
311
426
|
if (specTarget === void 0) return false;
|
|
@@ -313,7 +428,7 @@ function targetConsultsAffordance(specTarget) {
|
|
|
313
428
|
if (form === null) return false;
|
|
314
429
|
switch (form.form) {
|
|
315
430
|
case "predicate":
|
|
316
|
-
return form.kindOf.readsAffordance
|
|
431
|
+
return form.kindOf.readsAffordance === true;
|
|
317
432
|
case "affordance":
|
|
318
433
|
return true;
|
|
319
434
|
case "body":
|
|
@@ -413,89 +528,6 @@ function matchSortedWithBarred(e, bindings, isMac, engagedChannels, opts = {}) {
|
|
|
413
528
|
return { matches: out, barred, ...owner !== void 0 ? { claimOwner: owner } : {} };
|
|
414
529
|
}
|
|
415
530
|
var EMPTY_ENGAGED = /* @__PURE__ */ new Set();
|
|
416
|
-
var PREDICATE_TARGET = "predicate";
|
|
417
|
-
function buildRouteRegistry(entries) {
|
|
418
|
-
const out = [];
|
|
419
|
-
for (const owner of entries) {
|
|
420
|
-
for (const binding of owner.bindings ?? []) {
|
|
421
|
-
const row = entryFor(owner.id, binding.spec, binding.actionId);
|
|
422
|
-
if (!row) continue;
|
|
423
|
-
const views = binding.opts?.views;
|
|
424
|
-
out.push(views === void 0 ? row : { ...row, views });
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
return out;
|
|
428
|
-
}
|
|
429
|
-
function routesForSpec(spec) {
|
|
430
|
-
const gesture = routeGestureForSpecKind(spec.kind);
|
|
431
|
-
if (!gesture) return [];
|
|
432
|
-
const descriptor = getGestureDescriptor(gesture);
|
|
433
|
-
const modifiers = parseModSpec("mods" in spec ? spec.mods : void 0);
|
|
434
|
-
const phases = phaseAtomsForSpec("phase" in spec ? spec.phase : void 0);
|
|
435
|
-
const target = descriptor.hasTarget ? routeTargetForSpec("target" in spec ? spec.target : void 0) ?? "*" : void 0;
|
|
436
|
-
const args = descriptor.arg ? routeArgsForSpec(spec, descriptor.arg.default) : [void 0];
|
|
437
|
-
return args.map((arg) => formatRoute({ phases, gesture, arg, target, modifiers }));
|
|
438
|
-
}
|
|
439
|
-
function entryFor(ownerId, spec, actionId) {
|
|
440
|
-
const gesture = routeGestureForSpecKind(spec.kind);
|
|
441
|
-
if (!gesture) return null;
|
|
442
|
-
const descriptor = getGestureDescriptor(gesture);
|
|
443
|
-
return {
|
|
444
|
-
ownerId,
|
|
445
|
-
actionId,
|
|
446
|
-
gesture,
|
|
447
|
-
phase: phaseOf("phase" in spec ? spec.phase : void 0),
|
|
448
|
-
modifiers: parseModSpec("mods" in spec ? spec.mods : void 0),
|
|
449
|
-
arg: descriptor.arg ? argOf(spec, descriptor.arg.default) : void 0,
|
|
450
|
-
...descriptor.arg ? argAlternativesOf(spec, descriptor.arg.default) : {},
|
|
451
|
-
target: descriptor.hasTarget ? routeTargetForSpec("target" in spec ? spec.target : void 0) : void 0,
|
|
452
|
-
spec
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
function phaseOf(phase) {
|
|
456
|
-
if (phase === void 0 || phase === "*") return "any";
|
|
457
|
-
if (phase === "initial" || phase === "engaged") return phase;
|
|
458
|
-
const distinct = new Set(phase.map((atom) => atom.phase));
|
|
459
|
-
if (distinct.size !== 1) return "any";
|
|
460
|
-
const only = [...distinct][0];
|
|
461
|
-
return only === "initial" || only === "engaged" ? only : "any";
|
|
462
|
-
}
|
|
463
|
-
function parseModSpec(mods) {
|
|
464
|
-
if (!mods) return {};
|
|
465
|
-
const out = {};
|
|
466
|
-
for (const [name, req] of Object.entries(mods)) {
|
|
467
|
-
if (req === true) out[name] = "required";
|
|
468
|
-
else if (req === "optional") out[name] = "optional";
|
|
469
|
-
}
|
|
470
|
-
return out;
|
|
471
|
-
}
|
|
472
|
-
function phaseAtomsForSpec(phase) {
|
|
473
|
-
if (phase === void 0) return [{ channel: "*", phase: "*" }];
|
|
474
|
-
if (phase === "initial" || phase === "engaged" || phase === "*") {
|
|
475
|
-
return [{ channel: "&", phase }];
|
|
476
|
-
}
|
|
477
|
-
return phase;
|
|
478
|
-
}
|
|
479
|
-
function routeTargetForSpec(target) {
|
|
480
|
-
if (target === void 0) return void 0;
|
|
481
|
-
return typeof target === "string" ? target : PREDICATE_TARGET;
|
|
482
|
-
}
|
|
483
|
-
function routeArgsForSpec(spec, fallback) {
|
|
484
|
-
if ("key" in spec) return Array.isArray(spec.key) ? spec.key : [spec.key];
|
|
485
|
-
if ("fingers" in spec) return [String(spec.fingers)];
|
|
486
|
-
if ("direction" in spec) return [spec.direction ?? fallback];
|
|
487
|
-
if ("types" in spec) return [spec.types?.length ? spec.types.join("|") : fallback];
|
|
488
|
-
return [fallback];
|
|
489
|
-
}
|
|
490
|
-
function argOf(spec, fallback) {
|
|
491
|
-
const args = routeArgsForSpec(spec, fallback);
|
|
492
|
-
return args.length > 1 ? args.join("|") : args[0];
|
|
493
|
-
}
|
|
494
|
-
function argAlternativesOf(spec, fallback) {
|
|
495
|
-
const types = "types" in spec ? spec.types : void 0;
|
|
496
|
-
const alts = types?.length ? types : routeArgsForSpec(spec, fallback).filter((a) => a !== void 0);
|
|
497
|
-
return alts.length > 1 ? { argAlternatives: alts } : {};
|
|
498
|
-
}
|
|
499
531
|
|
|
500
532
|
// src/eligibility/deviceProfile.ts
|
|
501
533
|
var COARSE_TARGET_SCALE = 1.75;
|
|
@@ -739,9 +771,8 @@ function buildRuleCtx(args) {
|
|
|
739
771
|
device: args.device
|
|
740
772
|
};
|
|
741
773
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
}
|
|
774
|
+
|
|
775
|
+
// src/interactions/dispatcher/dispatchTrace.ts
|
|
745
776
|
var DEV2 = (() => {
|
|
746
777
|
try {
|
|
747
778
|
return Boolean(import.meta.env?.DEV);
|
|
@@ -756,9 +787,9 @@ function recordTrace(entry) {
|
|
|
756
787
|
traceLog.push(entry);
|
|
757
788
|
if (traceLog.length > TRACE_LIMIT) traceLog.shift();
|
|
758
789
|
}
|
|
759
|
-
function publishLiveDispatch(
|
|
790
|
+
function publishLiveDispatch(explain2) {
|
|
760
791
|
if (!DEV2 || typeof window === "undefined") return;
|
|
761
|
-
window.__weaselDispatchLive__ =
|
|
792
|
+
window.__weaselDispatchLive__ = explain2;
|
|
762
793
|
}
|
|
763
794
|
function recordModeSwitch(mode, from, to, detail) {
|
|
764
795
|
recordTrace({
|
|
@@ -770,42 +801,196 @@ function recordModeSwitch(mode, from, to, detail) {
|
|
|
770
801
|
...detail !== void 0 ? { detail } : {}
|
|
771
802
|
});
|
|
772
803
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
}
|
|
777
|
-
function namesView(binding, view) {
|
|
778
|
-
return binding.opts?.views?.includes(view) ?? false;
|
|
779
|
-
}
|
|
780
|
-
function preferViewScoped(matches, view) {
|
|
781
|
-
const named = [];
|
|
782
|
-
const rest = [];
|
|
783
|
-
for (const m of matches) {
|
|
784
|
-
(namesView(m.binding, view) ? named : rest).push(m);
|
|
785
|
-
}
|
|
786
|
-
return named.length === 0 ? [...matches] : [...named, ...rest];
|
|
804
|
+
|
|
805
|
+
// src/interactions/dispatcher/gestureIds.ts
|
|
806
|
+
function pointerGestureId(pointerId) {
|
|
807
|
+
return `pointer-${pointerId ?? "mouse"}`;
|
|
787
808
|
}
|
|
788
|
-
function
|
|
789
|
-
|
|
790
|
-
return r ? { ...r, viewId: ctx.viewId ?? null } : void 0;
|
|
809
|
+
function multiTouchGestureId(fingers) {
|
|
810
|
+
return `multitouch-${fingers}`;
|
|
791
811
|
}
|
|
792
|
-
function
|
|
793
|
-
return
|
|
812
|
+
function isMultiTouchGestureId(gestureId) {
|
|
813
|
+
return gestureId.startsWith("multitouch-");
|
|
794
814
|
}
|
|
795
|
-
function
|
|
796
|
-
if (
|
|
797
|
-
|
|
815
|
+
function gestureIdFor(event) {
|
|
816
|
+
if (event.kind === "key-held") {
|
|
817
|
+
return `key-held-${event.code ?? event.key}`;
|
|
818
|
+
}
|
|
819
|
+
if (event.kind === "pointerdown" || event.kind === "pointermove" || event.kind === "pointerup" || event.kind === "pointercancel") {
|
|
820
|
+
return pointerGestureId(event.pointerId);
|
|
821
|
+
}
|
|
822
|
+
if (event.kind === "multitouch") {
|
|
823
|
+
return multiTouchGestureId(event.fingers);
|
|
824
|
+
}
|
|
825
|
+
return `ongoing-${event.kind}`;
|
|
798
826
|
}
|
|
799
|
-
|
|
800
|
-
|
|
827
|
+
|
|
828
|
+
// src/contributions/eligibility.ts
|
|
829
|
+
function liveScope(id, eligibility, state) {
|
|
830
|
+
const tags = eligibility.capabilities;
|
|
831
|
+
if (tags && state.allows && !state.allows(tags)) return null;
|
|
832
|
+
if (state.engagedIds?.has(id)) return "hotkey";
|
|
833
|
+
if (eligibility.focus && state.focusedId === id) return "active";
|
|
834
|
+
if (eligibility.always || eligibility.claimed) return "ambient";
|
|
835
|
+
return null;
|
|
801
836
|
}
|
|
802
|
-
function
|
|
837
|
+
function scopeBindings(entries, state) {
|
|
803
838
|
const out = [];
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
839
|
+
for (const entry of entries) {
|
|
840
|
+
const scope = liveScope(entry.id, entry.eligibility ?? {}, state);
|
|
841
|
+
if (scope === null) continue;
|
|
842
|
+
for (const binding of entry.bindings ?? []) {
|
|
843
|
+
out.push({ binding, scope, ownerId: entry.id });
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return out;
|
|
847
|
+
}
|
|
848
|
+
function assembleEntries(entries, inputs) {
|
|
849
|
+
const all = [...entries];
|
|
850
|
+
const ordered = [];
|
|
851
|
+
for (let i = inputs.hotkeyStack.length - 1; i >= 0; i--) {
|
|
852
|
+
const entry = all.find((e) => e.id === inputs.hotkeyStack[i]);
|
|
853
|
+
if (entry && !ordered.includes(entry)) ordered.push(entry);
|
|
854
|
+
}
|
|
855
|
+
for (const entry of all) {
|
|
856
|
+
if (!ordered.includes(entry)) ordered.push(entry);
|
|
857
|
+
}
|
|
858
|
+
const allowed = inputs.allowedCapabilities;
|
|
859
|
+
return {
|
|
860
|
+
ordered,
|
|
861
|
+
state: {
|
|
862
|
+
focusedId: inputs.focusedId,
|
|
863
|
+
engagedIds: new Set(inputs.hotkeyStack),
|
|
864
|
+
...allowed ? { allows: (tags) => capabilitiesAllowed(allowed, tags) } : {}
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// src/interactions/dispatcher/inputFacts.ts
|
|
870
|
+
function modifiersOf(e) {
|
|
871
|
+
return { alt: e.altKey, ctrl: e.ctrlKey, meta: e.metaKey, shift: e.shiftKey };
|
|
872
|
+
}
|
|
873
|
+
function worldPointOf(event) {
|
|
874
|
+
switch (event.kind) {
|
|
875
|
+
case "pointerdown":
|
|
876
|
+
case "hover":
|
|
877
|
+
case "longpress":
|
|
878
|
+
case "click":
|
|
879
|
+
case "doubleclick":
|
|
880
|
+
case "contextmenu":
|
|
881
|
+
return event.x !== void 0 && event.y !== void 0 ? { x: event.x, y: event.y } : void 0;
|
|
882
|
+
default:
|
|
883
|
+
return void 0;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
function carriedBy(event) {
|
|
887
|
+
return event.kind === "drop" || event.kind === "paste" ? { items: event.items } : void 0;
|
|
888
|
+
}
|
|
889
|
+
function immediateParams(event, resolved) {
|
|
890
|
+
switch (event.kind) {
|
|
891
|
+
case "wheel":
|
|
892
|
+
return {
|
|
893
|
+
deltaX: event.deltaX,
|
|
894
|
+
deltaY: event.deltaY,
|
|
895
|
+
clientX: event.clientX,
|
|
896
|
+
clientY: event.clientY,
|
|
897
|
+
affordance: event.affordance,
|
|
898
|
+
...resolved
|
|
899
|
+
};
|
|
900
|
+
case "pinch":
|
|
901
|
+
return {
|
|
902
|
+
scale: event.scale,
|
|
903
|
+
rotation: event.rotation,
|
|
904
|
+
clientX: event.clientX,
|
|
905
|
+
clientY: event.clientY,
|
|
906
|
+
affordance: event.affordance,
|
|
907
|
+
...resolved
|
|
908
|
+
};
|
|
909
|
+
case "click":
|
|
910
|
+
case "doubleclick":
|
|
911
|
+
return {
|
|
912
|
+
worldX: event.x,
|
|
913
|
+
worldY: event.y,
|
|
914
|
+
affordance: event.affordance,
|
|
915
|
+
// The press point too: an action that places geometry at the click wants it.
|
|
916
|
+
...event.kind === "click" ? { pressX: event.pressX, pressY: event.pressY } : {},
|
|
917
|
+
mods: modifiersOf(event),
|
|
918
|
+
...resolved
|
|
919
|
+
};
|
|
920
|
+
// Only a `stage: 'press'` pointerdown reaches an immediate invoker: the
|
|
921
|
+
// buffered copy matches `drag` specs, which are ongoing.
|
|
922
|
+
case "pointerdown":
|
|
923
|
+
case "contextmenu":
|
|
924
|
+
case "longpress":
|
|
925
|
+
return {
|
|
926
|
+
worldX: event.x,
|
|
927
|
+
worldY: event.y,
|
|
928
|
+
affordance: event.affordance,
|
|
929
|
+
bodyTarget: event.bodyTarget,
|
|
930
|
+
mods: modifiersOf(event),
|
|
931
|
+
...resolved
|
|
932
|
+
};
|
|
933
|
+
case "hover":
|
|
934
|
+
return {
|
|
935
|
+
worldX: event.x,
|
|
936
|
+
worldY: event.y,
|
|
937
|
+
clientX: event.clientX,
|
|
938
|
+
clientY: event.clientY,
|
|
939
|
+
affordance: event.affordance,
|
|
940
|
+
bodyTarget: event.bodyTarget,
|
|
941
|
+
mods: modifiersOf(event),
|
|
942
|
+
...resolved
|
|
943
|
+
};
|
|
944
|
+
case "drop":
|
|
945
|
+
case "paste":
|
|
946
|
+
return {
|
|
947
|
+
items: event.items,
|
|
948
|
+
via: event.kind,
|
|
949
|
+
...event.kind === "drop" && event.x !== void 0 ? { worldX: event.x, worldY: event.y } : {},
|
|
950
|
+
...resolved
|
|
951
|
+
};
|
|
952
|
+
default:
|
|
953
|
+
return resolved;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// src/interactions/dispatcher/ranking.ts
|
|
958
|
+
function liveInView(binding, view) {
|
|
959
|
+
const views = binding.opts?.views;
|
|
960
|
+
return views === void 0 || views.includes(view);
|
|
961
|
+
}
|
|
962
|
+
function namesView(binding, view) {
|
|
963
|
+
return binding.opts?.views?.includes(view) ?? false;
|
|
964
|
+
}
|
|
965
|
+
function preferViewScoped(matches, view) {
|
|
966
|
+
const named = [];
|
|
967
|
+
const rest = [];
|
|
968
|
+
for (const m of matches) {
|
|
969
|
+
(namesView(m.binding, view) ? named : rest).push(m);
|
|
970
|
+
}
|
|
971
|
+
return named.length === 0 ? [...matches] : [...named, ...rest];
|
|
972
|
+
}
|
|
973
|
+
function ruleCtxOf(ctx) {
|
|
974
|
+
const r = ctx.getRuleCtx?.();
|
|
975
|
+
return r ? { ...r, viewId: ctx.viewId ?? null } : void 0;
|
|
976
|
+
}
|
|
977
|
+
function eligibleToRule(eligible) {
|
|
978
|
+
return typeof eligible === "function" ? eligible.rule : eligible;
|
|
979
|
+
}
|
|
980
|
+
function isEligible(action, ruleCtx) {
|
|
981
|
+
if (!action.eligible) return true;
|
|
982
|
+
return evaluate(eligibleToRule(action.eligible), ruleCtx);
|
|
983
|
+
}
|
|
984
|
+
function describeEligible(eligible) {
|
|
985
|
+
return describeRule(eligibleToRule(eligible));
|
|
986
|
+
}
|
|
987
|
+
function preferContextual(matches, actionLookup, holds, view) {
|
|
988
|
+
const out = [];
|
|
989
|
+
let run = [];
|
|
990
|
+
const flush = () => {
|
|
991
|
+
const gated = run.filter((m) => {
|
|
992
|
+
const action = actionLookup(m.binding.actionId);
|
|
993
|
+
return action?.eligible !== void 0 && holds(action);
|
|
809
994
|
});
|
|
810
995
|
out.push(...gated, ...run.filter((m) => !gated.includes(m)));
|
|
811
996
|
run = [];
|
|
@@ -833,698 +1018,405 @@ function sameSpecificity(a, b) {
|
|
|
833
1018
|
const sb = specificity(b);
|
|
834
1019
|
return sa.every((v, i) => v === sb[i]);
|
|
835
1020
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
const inFlightHandles = /* @__PURE__ */ new Map();
|
|
854
|
-
const inFlightOwners = /* @__PURE__ */ new Map();
|
|
855
|
-
const inFlightActions = /* @__PURE__ */ new Map();
|
|
856
|
-
const dragOrigins = /* @__PURE__ */ new Map();
|
|
857
|
-
const subscribers = /* @__PURE__ */ new Set();
|
|
858
|
-
let version = 0;
|
|
859
|
-
function notify() {
|
|
860
|
-
version++;
|
|
861
|
-
for (const fn of subscribers) fn();
|
|
862
|
-
}
|
|
863
|
-
const dragPoints = /* @__PURE__ */ new Map();
|
|
864
|
-
const pinchStartSpreads = /* @__PURE__ */ new Map();
|
|
865
|
-
let uiOngoingSeq = 0;
|
|
866
|
-
const uiOngoingByAction = /* @__PURE__ */ new Map();
|
|
867
|
-
function isEmptyOngoingHandle(h) {
|
|
868
|
-
return !h.onMove && !h.onEnd && !h.overlay && !h.previewIds && !h.previewPose;
|
|
869
|
-
}
|
|
870
|
-
let routedView;
|
|
871
|
-
function buildUiInvocationCtx(deps, params) {
|
|
872
|
-
return {
|
|
873
|
-
world: { x: 0, y: 0 },
|
|
874
|
-
modifiers: { alt: false, ctrl: false, meta: false, shift: false },
|
|
875
|
-
deps,
|
|
876
|
-
...params !== void 0 ? { params } : {}
|
|
877
|
-
};
|
|
878
|
-
}
|
|
879
|
-
function buildInvocationCtx(event, deps, gestureId) {
|
|
880
|
-
const modifiers = {
|
|
881
|
-
alt: event.altKey,
|
|
882
|
-
ctrl: event.ctrlKey,
|
|
883
|
-
meta: event.metaKey,
|
|
884
|
-
shift: event.shiftKey
|
|
885
|
-
};
|
|
886
|
-
const base = {
|
|
887
|
-
world: { x: 0, y: 0 },
|
|
888
|
-
modifiers,
|
|
889
|
-
deps,
|
|
890
|
-
...routedView !== void 0 ? { viewId: routedView } : {}
|
|
891
|
-
};
|
|
892
|
-
const clientPoint = (ev) => {
|
|
893
|
-
const cx = ev.clientX;
|
|
894
|
-
const cy = ev.clientY;
|
|
895
|
-
return cx !== void 0 && cy !== void 0 ? { x: cx, y: cy } : void 0;
|
|
896
|
-
};
|
|
897
|
-
const screen = clientPoint(event);
|
|
898
|
-
if (screen) base.screen = screen;
|
|
899
|
-
if (event.kind === "key") {
|
|
900
|
-
base.key = { key: event.key, repeat: event.repeat ?? false };
|
|
901
|
-
} else if (event.kind === "key-held") {
|
|
902
|
-
base.key = { key: event.key, repeat: false };
|
|
903
|
-
} else if (event.kind === "wheel") {
|
|
904
|
-
base.wheel = { deltaX: event.deltaX, deltaY: event.deltaY, deltaZ: 0 };
|
|
905
|
-
} else if (event.kind === "pinch") {
|
|
906
|
-
base.pinch = { scale: event.scale, rotation: event.rotation };
|
|
907
|
-
} else if (event.kind === "pointerdown" || event.kind === "click" || event.kind === "doubleclick" || event.kind === "contextmenu") {
|
|
908
|
-
const sx = event.x ?? (event.kind === "click" ? event.pressX : void 0) ?? 0;
|
|
909
|
-
const sy = event.y ?? (event.kind === "click" ? event.pressY : void 0) ?? 0;
|
|
910
|
-
base.world = { x: sx, y: sy };
|
|
911
|
-
const affordance = event.kind === "pointerdown" ? event.affordance : void 0;
|
|
912
|
-
base.drag = {
|
|
913
|
-
start: { x: sx, y: sy },
|
|
914
|
-
current: { x: sx, y: sy },
|
|
915
|
-
delta: { x: 0, y: 0 },
|
|
916
|
-
...affordance !== void 0 ? { affordance } : {}
|
|
917
|
-
};
|
|
918
|
-
} else if (event.kind === "pointermove" || event.kind === "pointerup") {
|
|
919
|
-
const cx = event.x;
|
|
920
|
-
const cy = event.y;
|
|
921
|
-
base.world = { x: cx, y: cy };
|
|
922
|
-
const origin = gestureId ? dragOrigins.get(gestureId) : void 0;
|
|
923
|
-
const ox = origin?.x ?? cx;
|
|
924
|
-
const oy = origin?.y ?? cy;
|
|
925
|
-
const live = gestureId ? dragPoints.get(gestureId) : void 0;
|
|
926
|
-
const points = live && event.kind === "pointerup" ? [...live] : live;
|
|
927
|
-
const eventClientX = event.clientX;
|
|
928
|
-
const eventClientY = event.clientY;
|
|
929
|
-
const hasClient = eventClientX !== void 0 && eventClientY !== void 0 && origin?.clientX !== void 0 && origin?.clientY !== void 0;
|
|
930
|
-
base.drag = {
|
|
931
|
-
start: { x: ox, y: oy },
|
|
932
|
-
current: { x: cx, y: cy },
|
|
933
|
-
delta: { x: cx - ox, y: cy - oy },
|
|
934
|
-
...hasClient ? { screenDelta: { x: eventClientX - origin.clientX, y: eventClientY - origin.clientY } } : {},
|
|
935
|
-
...points !== void 0 ? { points } : {}
|
|
936
|
-
};
|
|
937
|
-
} else if (event.kind === "multitouch") {
|
|
938
|
-
const centroid = event.centroid ?? { x: 0, y: 0 };
|
|
939
|
-
const spread = event.spread ?? 1;
|
|
940
|
-
const startSpread = gestureId ? pinchStartSpreads.get(gestureId) : void 0;
|
|
941
|
-
base.multiTouch = {
|
|
942
|
-
centroid,
|
|
943
|
-
spread,
|
|
944
|
-
rotation: 0,
|
|
945
|
-
...startSpread !== void 0 && event.spread !== void 0 ? { pinch: { startSpread, currentSpread: spread, centroid } } : {}
|
|
1021
|
+
|
|
1022
|
+
// src/interactions/dispatcher/buildDispatchRecord.ts
|
|
1023
|
+
function buildDispatchRecord(r, walked, event, ctx, predicted, outcome) {
|
|
1024
|
+
const view = ctx.viewId ?? null;
|
|
1025
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
1026
|
+
const candidateOf = (m) => {
|
|
1027
|
+
let c = candidates.get(m);
|
|
1028
|
+
if (!c) {
|
|
1029
|
+
const eligible = r.actionMap.get(m.binding.actionId)?.eligible;
|
|
1030
|
+
c = {
|
|
1031
|
+
actionId: m.binding.actionId,
|
|
1032
|
+
routes: routesForSpec(m.binding.spec),
|
|
1033
|
+
scope: m.scope,
|
|
1034
|
+
ownerId: m.ownerId,
|
|
1035
|
+
namesView: namesView(m.binding, view),
|
|
1036
|
+
specificity: specificity(m.binding.spec),
|
|
1037
|
+
...eligible !== void 0 ? { eligible: describeEligible(eligible) } : {}
|
|
946
1038
|
};
|
|
1039
|
+
candidates.set(m, c);
|
|
947
1040
|
}
|
|
948
|
-
return
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
...
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
for (const action of registry.list()) {
|
|
1000
|
-
map.set(action.id, action);
|
|
1001
|
-
}
|
|
1002
|
-
return map;
|
|
1003
|
-
}
|
|
1004
|
-
function handleInput(event, ctx) {
|
|
1005
|
-
routedView = ctx.viewId;
|
|
1006
|
-
if (event.kind === "key-held" && event.phase === "up") {
|
|
1007
|
-
const gestureId = gestureIdFor(event);
|
|
1008
|
-
const handle = inFlightHandles.get(gestureId);
|
|
1009
|
-
if (handle) {
|
|
1010
|
-
const stubCtx = buildInvocationCtx(event, {}, gestureId);
|
|
1011
|
-
handle.onEnd?.(stubCtx, "commit");
|
|
1012
|
-
inFlightHandles.delete(gestureId);
|
|
1013
|
-
inFlightOwners.delete(gestureId);
|
|
1014
|
-
inFlightActions.delete(gestureId);
|
|
1015
|
-
dragOrigins.delete(gestureId);
|
|
1016
|
-
}
|
|
1017
|
-
return handle ? "handled" : "unhandled";
|
|
1018
|
-
}
|
|
1019
|
-
if (event.kind === "key-held" && event.phase === "down") {
|
|
1020
|
-
const gestureId = gestureIdFor(event);
|
|
1021
|
-
if (inFlightHandles.has(gestureId)) {
|
|
1022
|
-
return "handled";
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
if (event.kind === "pointermove") {
|
|
1026
|
-
const gestureId = gestureIdFor(event);
|
|
1027
|
-
const handle = inFlightHandles.get(gestureId);
|
|
1028
|
-
if (!handle) return "unhandled";
|
|
1029
|
-
const pts = dragPoints.get(gestureId);
|
|
1030
|
-
if (pts) {
|
|
1031
|
-
pts.push({
|
|
1032
|
-
x: event.x,
|
|
1033
|
-
y: event.y,
|
|
1034
|
-
...event.pressure !== void 0 ? { pressure: event.pressure } : {},
|
|
1035
|
-
...event.tiltX !== void 0 ? { tiltX: event.tiltX } : {},
|
|
1036
|
-
...event.tiltY !== void 0 ? { tiltY: event.tiltY } : {}
|
|
1037
|
-
});
|
|
1038
|
-
}
|
|
1039
|
-
if (handle.onMove) {
|
|
1040
|
-
const moveCtx = buildInvocationCtx(event, {}, gestureId);
|
|
1041
|
-
handle.onMove(moveCtx);
|
|
1042
|
-
return "handled";
|
|
1043
|
-
}
|
|
1044
|
-
return "unhandled";
|
|
1045
|
-
}
|
|
1046
|
-
if (event.kind === "pointerup") {
|
|
1047
|
-
const gestureId = gestureIdFor(event);
|
|
1048
|
-
const handle = inFlightHandles.get(gestureId);
|
|
1049
|
-
if (handle) {
|
|
1050
|
-
const endCtx = buildInvocationCtx(event, {}, gestureId);
|
|
1051
|
-
handle.onEnd?.(endCtx, "commit");
|
|
1052
|
-
inFlightHandles.delete(gestureId);
|
|
1053
|
-
inFlightOwners.delete(gestureId);
|
|
1054
|
-
inFlightActions.delete(gestureId);
|
|
1055
|
-
dragOrigins.delete(gestureId);
|
|
1056
|
-
dragPoints.delete(gestureId);
|
|
1057
|
-
}
|
|
1058
|
-
return handle ? "handled" : "unhandled";
|
|
1059
|
-
}
|
|
1060
|
-
if (event.kind === "pointercancel") {
|
|
1061
|
-
const gestureId = gestureIdFor(event);
|
|
1062
|
-
const handle = inFlightHandles.get(gestureId);
|
|
1063
|
-
if (handle) {
|
|
1064
|
-
const endCtx = buildInvocationCtx(event, {}, gestureId);
|
|
1065
|
-
handle.onEnd?.(endCtx, "cancel");
|
|
1066
|
-
inFlightHandles.delete(gestureId);
|
|
1067
|
-
inFlightOwners.delete(gestureId);
|
|
1068
|
-
inFlightActions.delete(gestureId);
|
|
1069
|
-
dragOrigins.delete(gestureId);
|
|
1070
|
-
dragPoints.delete(gestureId);
|
|
1071
|
-
}
|
|
1072
|
-
return handle ? "handled" : "unhandled";
|
|
1073
|
-
}
|
|
1074
|
-
if (event.kind === "multitouch" && event.centroid !== void 0) {
|
|
1075
|
-
const gestureId = gestureIdFor(event);
|
|
1076
|
-
const handle = inFlightHandles.get(gestureId);
|
|
1077
|
-
if (handle?.onMove) {
|
|
1078
|
-
const moveCtx = buildInvocationCtx(event, {}, gestureId);
|
|
1079
|
-
handle.onMove(moveCtx);
|
|
1080
|
-
return "handled";
|
|
1081
|
-
}
|
|
1082
|
-
for (const [id, other] of [...inFlightHandles]) {
|
|
1083
|
-
if (!id.startsWith("multitouch-") || id === gestureId) continue;
|
|
1084
|
-
other.onEnd?.(buildInvocationCtx(event, {}, id), "cancel");
|
|
1085
|
-
inFlightHandles.delete(id);
|
|
1086
|
-
inFlightOwners.delete(id);
|
|
1087
|
-
inFlightActions.delete(id);
|
|
1088
|
-
pinchStartSpreads.delete(id);
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
const r = rank(event, ctx, DEV2);
|
|
1092
|
-
const walked = walk(r, event, ctx, (match, action, deps) => invoke(event, match, action, deps), false);
|
|
1093
|
-
const outcome = walked.stop === "fired" ? "handled" : "unhandled";
|
|
1094
|
-
if (DEV2) recordTrace(buildRecord(r, walked, event, ctx, false, outcome));
|
|
1095
|
-
return outcome;
|
|
1096
|
-
}
|
|
1097
|
-
function invoke(event, match, action, deps) {
|
|
1098
|
-
if (action.invoker?.timing === "immediate") {
|
|
1099
|
-
try {
|
|
1100
|
-
const resolved = resolveParams(match.binding.opts?.params);
|
|
1101
|
-
let params;
|
|
1102
|
-
if (event.kind === "wheel") {
|
|
1103
|
-
params = {
|
|
1104
|
-
deltaX: event.deltaX,
|
|
1105
|
-
deltaY: event.deltaY,
|
|
1106
|
-
clientX: event.clientX,
|
|
1107
|
-
clientY: event.clientY,
|
|
1108
|
-
affordance: event.affordance,
|
|
1109
|
-
...resolved
|
|
1110
|
-
};
|
|
1111
|
-
} else if (event.kind === "pinch") {
|
|
1112
|
-
params = {
|
|
1113
|
-
scale: event.scale,
|
|
1114
|
-
rotation: event.rotation,
|
|
1115
|
-
clientX: event.clientX,
|
|
1116
|
-
clientY: event.clientY,
|
|
1117
|
-
affordance: event.affordance,
|
|
1118
|
-
...resolved
|
|
1119
|
-
};
|
|
1120
|
-
} else if (event.kind === "click" || event.kind === "doubleclick") {
|
|
1121
|
-
params = {
|
|
1122
|
-
worldX: event.x,
|
|
1123
|
-
worldY: event.y,
|
|
1124
|
-
affordance: event.affordance,
|
|
1125
|
-
// Press point as well as release point — an action that places
|
|
1126
|
-
// geometry at the click wants the former. See `ClickEvent`.
|
|
1127
|
-
...event.kind === "click" ? { pressX: event.pressX, pressY: event.pressY } : {},
|
|
1128
|
-
mods: modifiersOf(event),
|
|
1129
|
-
...resolved
|
|
1130
|
-
};
|
|
1131
|
-
} else if (event.kind === "contextmenu" || event.kind === "longpress") {
|
|
1132
|
-
params = {
|
|
1133
|
-
worldX: event.x,
|
|
1134
|
-
worldY: event.y,
|
|
1135
|
-
affordance: event.affordance,
|
|
1136
|
-
bodyTarget: event.bodyTarget,
|
|
1137
|
-
mods: modifiersOf(event),
|
|
1138
|
-
...resolved
|
|
1139
|
-
};
|
|
1140
|
-
} else if (event.kind === "pointerdown") {
|
|
1141
|
-
params = {
|
|
1142
|
-
worldX: event.x,
|
|
1143
|
-
worldY: event.y,
|
|
1144
|
-
affordance: event.affordance,
|
|
1145
|
-
bodyTarget: event.bodyTarget,
|
|
1146
|
-
mods: modifiersOf(event),
|
|
1147
|
-
...resolved
|
|
1148
|
-
};
|
|
1149
|
-
} else if (event.kind === "drop" || event.kind === "paste") {
|
|
1150
|
-
params = {
|
|
1151
|
-
items: event.items,
|
|
1152
|
-
via: event.kind,
|
|
1153
|
-
...event.kind === "drop" && event.x !== void 0 ? { worldX: event.x, worldY: event.y } : {},
|
|
1154
|
-
...resolved
|
|
1155
|
-
};
|
|
1156
|
-
} else {
|
|
1157
|
-
params = resolved;
|
|
1158
|
-
}
|
|
1159
|
-
action.invoker.run(deps, params);
|
|
1160
|
-
} catch (err) {
|
|
1161
|
-
console.error(`weasel dispatcher: action "${action.id}" invoker threw`, err);
|
|
1162
|
-
}
|
|
1163
|
-
return "fired";
|
|
1164
|
-
}
|
|
1165
|
-
if (action.invoker?.timing === "ongoing") {
|
|
1166
|
-
if (event.kind === "pointerdown" && event.stage === "press") {
|
|
1167
|
-
console.error(
|
|
1168
|
-
`weasel dispatcher: action "${action.id}" has an ongoing invoker but is bound to a pointerDown spec, which fires at press time. Bind it to a drag spec, or give the action an immediate invoker.`
|
|
1169
|
-
);
|
|
1170
|
-
return "misbound";
|
|
1171
|
-
}
|
|
1172
|
-
const gestureId = gestureIdFor(event);
|
|
1041
|
+
return c;
|
|
1042
|
+
};
|
|
1043
|
+
const dropped = [
|
|
1044
|
+
...r.barred.map((m) => ({
|
|
1045
|
+
candidate: candidateOf(m),
|
|
1046
|
+
filter: "claim",
|
|
1047
|
+
...r.claimOwner !== void 0 ? { owner: r.claimOwner } : {}
|
|
1048
|
+
}))
|
|
1049
|
+
];
|
|
1050
|
+
for (const m of r.ordered) {
|
|
1051
|
+
if (r.eligible(m)) continue;
|
|
1052
|
+
const c = candidateOf(m);
|
|
1053
|
+
dropped.push({ candidate: c, filter: "ineligible", rule: c.eligible ?? "" });
|
|
1054
|
+
}
|
|
1055
|
+
const gated = (m) => r.actionMap.get(m.binding.actionId)?.eligible !== void 0;
|
|
1056
|
+
const ranked = walked.steps.map(({ match, step }, i) => {
|
|
1057
|
+
const prev = walked.steps[i - 1]?.match;
|
|
1058
|
+
return {
|
|
1059
|
+
candidate: candidateOf(match),
|
|
1060
|
+
placedBy: prev ? placedBy(prev, match, view, r.ruleCtx !== void 0 && gated(prev) && !gated(match)) : { step: "first" },
|
|
1061
|
+
walk: step
|
|
1062
|
+
};
|
|
1063
|
+
});
|
|
1064
|
+
const eventKey = event.kind === "key" || event.kind === "key-held" ? event.key : void 0;
|
|
1065
|
+
const world = worldPointOf(event);
|
|
1066
|
+
return {
|
|
1067
|
+
kind: "dispatch",
|
|
1068
|
+
ts: Date.now(),
|
|
1069
|
+
input: {
|
|
1070
|
+
eventKind: event.kind,
|
|
1071
|
+
...eventKey !== void 0 ? { key: eventKey } : {},
|
|
1072
|
+
modifiers: modifiersOf(event),
|
|
1073
|
+
viewId: view,
|
|
1074
|
+
...world !== void 0 ? { world } : {},
|
|
1075
|
+
...r.ruleCtx !== void 0 ? { mode: r.ruleCtx.mode } : {}
|
|
1076
|
+
},
|
|
1077
|
+
matched: [...r.ordered, ...r.barred].map(candidateOf),
|
|
1078
|
+
dropped,
|
|
1079
|
+
ranked,
|
|
1080
|
+
predicted,
|
|
1081
|
+
fired: walked.winner?.action.id ?? null,
|
|
1082
|
+
outcome
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
function createGestureFacts() {
|
|
1086
|
+
const origins = /* @__PURE__ */ new Map();
|
|
1087
|
+
const points = /* @__PURE__ */ new Map();
|
|
1088
|
+
const startSpreads = /* @__PURE__ */ new Map();
|
|
1089
|
+
const turns = /* @__PURE__ */ new Map();
|
|
1090
|
+
return {
|
|
1091
|
+
begin(gestureId, event) {
|
|
1173
1092
|
if (event.kind === "pointerdown") {
|
|
1174
|
-
|
|
1093
|
+
origins.set(gestureId, {
|
|
1175
1094
|
x: event.x ?? 0,
|
|
1176
1095
|
y: event.y ?? 0,
|
|
1177
1096
|
...event.clientX !== void 0 ? { clientX: event.clientX } : {},
|
|
1178
1097
|
...event.clientY !== void 0 ? { clientY: event.clientY } : {}
|
|
1179
1098
|
});
|
|
1180
|
-
|
|
1181
|
-
x: event.x ?? 0,
|
|
1182
|
-
y: event.y ?? 0,
|
|
1183
|
-
...event.pressure !== void 0 ? { pressure: event.pressure } : {},
|
|
1184
|
-
...event.tiltX !== void 0 ? { tiltX: event.tiltX } : {},
|
|
1185
|
-
...event.tiltY !== void 0 ? { tiltY: event.tiltY } : {}
|
|
1186
|
-
}]);
|
|
1099
|
+
points.set(gestureId, [{ x: event.x ?? 0, y: event.y ?? 0, ...stylusOf(event) }]);
|
|
1187
1100
|
}
|
|
1188
1101
|
if (event.kind === "multitouch" && event.spread !== void 0) {
|
|
1189
|
-
|
|
1102
|
+
startSpreads.set(gestureId, event.spread);
|
|
1190
1103
|
}
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
if (isEmptyOngoingHandle(handle)) {
|
|
1194
|
-
if (event.kind === "pointerdown") {
|
|
1195
|
-
dragOrigins.delete(gestureId);
|
|
1196
|
-
dragPoints.delete(gestureId);
|
|
1197
|
-
}
|
|
1198
|
-
if (event.kind === "multitouch") {
|
|
1199
|
-
pinchStartSpreads.delete(gestureId);
|
|
1200
|
-
}
|
|
1201
|
-
return "empty-handle";
|
|
1104
|
+
if (event.kind === "multitouch" && event.angle !== void 0) {
|
|
1105
|
+
turns.set(gestureId, { last: event.angle, total: 0 });
|
|
1202
1106
|
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
let h = held.get(a);
|
|
1226
|
-
if (h === void 0) held.set(a, h = isEligible(a, ruleCtx));
|
|
1227
|
-
return h;
|
|
1228
|
-
};
|
|
1229
|
-
const ordered = ruleCtx ? preferContextual(sorted, (id) => actionMap.get(id), holds, view) : sorted;
|
|
1230
|
-
return {
|
|
1231
|
-
ordered,
|
|
1232
|
-
barred: outcome.barred,
|
|
1233
|
-
...outcome.claimOwner !== void 0 ? { claimOwner: outcome.claimOwner } : {},
|
|
1234
|
-
actionMap,
|
|
1235
|
-
ruleCtx,
|
|
1236
|
-
eligible: (m) => {
|
|
1237
|
-
const a = actionMap.get(m.binding.actionId);
|
|
1238
|
-
return a === void 0 || holds(a);
|
|
1239
|
-
}
|
|
1240
|
-
};
|
|
1241
|
-
}
|
|
1242
|
-
function walk(r, event, ctx, attempt, evaluateShadowed) {
|
|
1243
|
-
const steps = [];
|
|
1244
|
-
const tried = /* @__PURE__ */ new Set();
|
|
1245
|
-
let stop = "exhausted";
|
|
1246
|
-
let winner = null;
|
|
1247
|
-
for (const match of r.ordered) {
|
|
1248
|
-
if (!r.eligible(match)) continue;
|
|
1249
|
-
const push = (step) => {
|
|
1250
|
-
steps.push({ match, step });
|
|
1107
|
+
},
|
|
1108
|
+
sample(gestureId, event) {
|
|
1109
|
+
points.get(gestureId)?.push({ x: event.x, y: event.y, ...stylusOf(event) });
|
|
1110
|
+
},
|
|
1111
|
+
turn(gestureId, event) {
|
|
1112
|
+
const turn = turns.get(gestureId);
|
|
1113
|
+
if (!turn || event.angle === void 0) return;
|
|
1114
|
+
const step = event.angle - turn.last;
|
|
1115
|
+
turn.total += step - 360 * Math.round(step / 360);
|
|
1116
|
+
turn.last = event.angle;
|
|
1117
|
+
},
|
|
1118
|
+
of(gestureId) {
|
|
1119
|
+
if (gestureId === void 0) return {};
|
|
1120
|
+
const origin = origins.get(gestureId);
|
|
1121
|
+
const trail = points.get(gestureId);
|
|
1122
|
+
const startSpread = startSpreads.get(gestureId);
|
|
1123
|
+
const turn = turns.get(gestureId);
|
|
1124
|
+
return {
|
|
1125
|
+
...origin !== void 0 ? { origin } : {},
|
|
1126
|
+
...trail !== void 0 ? { points: trail } : {},
|
|
1127
|
+
...startSpread !== void 0 ? { startSpread } : {},
|
|
1128
|
+
...turn !== void 0 ? { rotation: turn.total } : {}
|
|
1251
1129
|
};
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1130
|
+
},
|
|
1131
|
+
drop(gestureId) {
|
|
1132
|
+
origins.delete(gestureId);
|
|
1133
|
+
points.delete(gestureId);
|
|
1134
|
+
startSpreads.delete(gestureId);
|
|
1135
|
+
turns.delete(gestureId);
|
|
1136
|
+
},
|
|
1137
|
+
clear() {
|
|
1138
|
+
origins.clear();
|
|
1139
|
+
points.clear();
|
|
1140
|
+
startSpreads.clear();
|
|
1141
|
+
turns.clear();
|
|
1142
|
+
}
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
var EMPTY_ENGAGED2 = /* @__PURE__ */ new Set();
|
|
1146
|
+
function createInFlightGestures() {
|
|
1147
|
+
const handles = /* @__PURE__ */ new Map();
|
|
1148
|
+
const owners = /* @__PURE__ */ new Map();
|
|
1149
|
+
const actions = /* @__PURE__ */ new Map();
|
|
1150
|
+
const forget = (gestureId) => {
|
|
1151
|
+
handles.delete(gestureId);
|
|
1152
|
+
owners.delete(gestureId);
|
|
1153
|
+
actions.delete(gestureId);
|
|
1154
|
+
};
|
|
1155
|
+
return {
|
|
1156
|
+
handles,
|
|
1157
|
+
open(gestureId, handle, ownerId, action) {
|
|
1158
|
+
handles.set(gestureId, handle);
|
|
1159
|
+
owners.set(gestureId, ownerId);
|
|
1160
|
+
if (action) actions.set(gestureId, action);
|
|
1161
|
+
},
|
|
1162
|
+
take(gestureId) {
|
|
1163
|
+
const handle = handles.get(gestureId);
|
|
1164
|
+
forget(gestureId);
|
|
1165
|
+
return handle;
|
|
1166
|
+
},
|
|
1167
|
+
takeAll() {
|
|
1168
|
+
const all = [...handles];
|
|
1169
|
+
handles.clear();
|
|
1170
|
+
owners.clear();
|
|
1171
|
+
actions.clear();
|
|
1172
|
+
return all;
|
|
1173
|
+
},
|
|
1174
|
+
engagedOwners() {
|
|
1175
|
+
if (owners.size === 0) return EMPTY_ENGAGED2;
|
|
1176
|
+
const out = /* @__PURE__ */ new Set();
|
|
1177
|
+
for (const owner of owners.values()) {
|
|
1178
|
+
if (owner != null) out.add(owner);
|
|
1281
1179
|
}
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1180
|
+
return out;
|
|
1181
|
+
},
|
|
1182
|
+
cursor() {
|
|
1183
|
+
let cursor = null;
|
|
1184
|
+
for (const action of actions.values()) {
|
|
1185
|
+
const spec = action.activeCursor ?? action.cursor;
|
|
1186
|
+
if (spec !== void 0 && resolveCursor(spec)) cursor = spec;
|
|
1287
1187
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1188
|
+
return cursor;
|
|
1189
|
+
},
|
|
1190
|
+
latest() {
|
|
1191
|
+
let lastId = null;
|
|
1192
|
+
let lastHandle = null;
|
|
1193
|
+
for (const [id, handle] of handles) {
|
|
1194
|
+
lastId = id;
|
|
1195
|
+
lastHandle = handle;
|
|
1292
1196
|
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
winner = { match, action };
|
|
1197
|
+
if (lastHandle === null) return { kind: null, id: null };
|
|
1198
|
+
return { kind: lastHandle.kind ?? null, id: lastId };
|
|
1296
1199
|
}
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
const c = candidateOf(m);
|
|
1329
|
-
dropped.push({ candidate: c, filter: "ineligible", rule: c.eligible ?? "" });
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
function isEmptyOngoingHandle(h) {
|
|
1203
|
+
return !h.onMove && !h.onEnd && !h.overlay && !h.previewIds && !h.previewPose;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
// src/interactions/dispatcher/invocationCtx.ts
|
|
1207
|
+
function uiInvocationCtx(deps, params) {
|
|
1208
|
+
return {
|
|
1209
|
+
world: { x: 0, y: 0 },
|
|
1210
|
+
modifiers: { alt: false, ctrl: false, meta: false, shift: false },
|
|
1211
|
+
deps,
|
|
1212
|
+
...params !== void 0 ? { params } : {}
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
function clientPoint(ev) {
|
|
1216
|
+
const cx = ev.clientX;
|
|
1217
|
+
const cy = ev.clientY;
|
|
1218
|
+
return cx !== void 0 && cy !== void 0 ? { x: cx, y: cy } : void 0;
|
|
1219
|
+
}
|
|
1220
|
+
function pressDrag(event) {
|
|
1221
|
+
const sx = event.x ?? (event.kind === "click" ? event.pressX : void 0) ?? 0;
|
|
1222
|
+
const sy = event.y ?? (event.kind === "click" ? event.pressY : void 0) ?? 0;
|
|
1223
|
+
const affordance = event.kind === "pointerdown" ? event.affordance : void 0;
|
|
1224
|
+
return {
|
|
1225
|
+
world: { x: sx, y: sy },
|
|
1226
|
+
drag: {
|
|
1227
|
+
start: { x: sx, y: sy },
|
|
1228
|
+
current: { x: sx, y: sy },
|
|
1229
|
+
delta: { x: 0, y: 0 },
|
|
1230
|
+
...affordance !== void 0 ? { affordance } : {}
|
|
1330
1231
|
}
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
},
|
|
1353
|
-
matched: [...r.ordered, ...r.barred].map(candidateOf),
|
|
1354
|
-
dropped,
|
|
1355
|
-
ranked,
|
|
1356
|
-
predicted,
|
|
1357
|
-
fired: walked.winner?.action.id ?? null,
|
|
1358
|
-
outcome
|
|
1359
|
-
};
|
|
1360
|
-
}
|
|
1361
|
-
function resolveAll(event, ctx, resolveOpts) {
|
|
1362
|
-
const evaluateShadowed = resolveOpts?.evaluateShadowed ?? false;
|
|
1363
|
-
const r = rank(event, ctx, false);
|
|
1364
|
-
if (r.ordered.length === 0) return [];
|
|
1365
|
-
const walked = walk(r, event, ctx, null, evaluateShadowed);
|
|
1366
|
-
const stepOf = new Map(walked.steps.map((s) => [s.match, s.step]));
|
|
1367
|
-
const verdictByAction = /* @__PURE__ */ new Map();
|
|
1368
|
-
let fired = false;
|
|
1369
|
-
const out = [];
|
|
1370
|
-
for (const match of r.ordered) {
|
|
1371
|
-
const actionId = match.binding.actionId;
|
|
1372
|
-
const action = r.actionMap.get(actionId);
|
|
1373
|
-
if (!action) continue;
|
|
1374
|
-
let verdict = verdictByAction.get(actionId);
|
|
1375
|
-
if (verdict === void 0) {
|
|
1376
|
-
if (!r.eligible(match)) {
|
|
1377
|
-
verdict = fired && !evaluateShadowed ? { kind: "shadowed" } : { kind: "ineligible", reason: describeEligible(action.eligible) };
|
|
1378
|
-
} else {
|
|
1379
|
-
const step = stepOf.get(match);
|
|
1380
|
-
if (step?.kind === "would-fire") {
|
|
1381
|
-
verdict = { kind: "would-fire" };
|
|
1382
|
-
fired = true;
|
|
1383
|
-
} else if (step?.kind === "declined") {
|
|
1384
|
-
verdict = { kind: "disabled", reason: step.reason };
|
|
1385
|
-
} else {
|
|
1386
|
-
verdict = { kind: "shadowed" };
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
verdictByAction.set(actionId, verdict);
|
|
1390
|
-
} else if (verdict.kind === "would-fire") {
|
|
1391
|
-
verdict = { kind: "shadowed" };
|
|
1392
|
-
}
|
|
1393
|
-
out.push({
|
|
1394
|
-
actionId,
|
|
1395
|
-
action,
|
|
1396
|
-
binding: match.binding,
|
|
1397
|
-
scope: match.scope,
|
|
1398
|
-
ownerId: match.ownerId,
|
|
1399
|
-
specificity: specificity(match.binding.spec),
|
|
1400
|
-
verdict
|
|
1401
|
-
});
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
function pumpDrag(event, facts) {
|
|
1235
|
+
const cx = event.x;
|
|
1236
|
+
const cy = event.y;
|
|
1237
|
+
const origin = facts.origin;
|
|
1238
|
+
const ox = origin?.x ?? cx;
|
|
1239
|
+
const oy = origin?.y ?? cy;
|
|
1240
|
+
const live = facts.points;
|
|
1241
|
+
const points = live && event.kind === "pointerup" ? [...live] : live;
|
|
1242
|
+
const eventClientX = event.clientX;
|
|
1243
|
+
const eventClientY = event.clientY;
|
|
1244
|
+
const hasClient = eventClientX !== void 0 && eventClientY !== void 0 && origin?.clientX !== void 0 && origin?.clientY !== void 0;
|
|
1245
|
+
return {
|
|
1246
|
+
world: { x: cx, y: cy },
|
|
1247
|
+
drag: {
|
|
1248
|
+
start: { x: ox, y: oy },
|
|
1249
|
+
current: { x: cx, y: cy },
|
|
1250
|
+
delta: { x: cx - ox, y: cy - oy },
|
|
1251
|
+
...hasClient ? { screenDelta: { x: eventClientX - origin.clientX, y: eventClientY - origin.clientY } } : {},
|
|
1252
|
+
...points !== void 0 ? { points } : {}
|
|
1402
1253
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
function multiTouchOf(event, facts) {
|
|
1257
|
+
const centroid = event.centroid ?? { x: 0, y: 0 };
|
|
1258
|
+
const spread = event.spread ?? 1;
|
|
1259
|
+
const { startSpread } = facts;
|
|
1260
|
+
return {
|
|
1261
|
+
centroid,
|
|
1262
|
+
spread,
|
|
1263
|
+
rotation: facts.rotation ?? 0,
|
|
1264
|
+
// Pinch geometry, on a move of a running gesture.
|
|
1265
|
+
...startSpread !== void 0 && event.spread !== void 0 ? { pinch: { startSpread, currentSpread: spread, centroid } } : {}
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
function buildInvocationCtx(event, deps, viewId, facts = {}) {
|
|
1269
|
+
const base = {
|
|
1270
|
+
world: { x: 0, y: 0 },
|
|
1271
|
+
modifiers: { alt: event.altKey, ctrl: event.ctrlKey, meta: event.metaKey, shift: event.shiftKey },
|
|
1272
|
+
deps,
|
|
1273
|
+
...viewId !== void 0 ? { viewId } : {}
|
|
1274
|
+
};
|
|
1275
|
+
const screen = clientPoint(event);
|
|
1276
|
+
if (screen) base.screen = screen;
|
|
1277
|
+
if (event.kind === "key") {
|
|
1278
|
+
base.key = { key: event.key, repeat: event.repeat ?? false };
|
|
1279
|
+
} else if (event.kind === "key-held") {
|
|
1280
|
+
base.key = { key: event.key, repeat: false };
|
|
1281
|
+
} else if (event.kind === "wheel") {
|
|
1282
|
+
base.wheel = { deltaX: event.deltaX, deltaY: event.deltaY, deltaZ: 0 };
|
|
1283
|
+
} else if (event.kind === "pinch") {
|
|
1284
|
+
base.pinch = { scale: event.scale, rotation: event.rotation };
|
|
1285
|
+
} else if (event.kind === "pointerdown" || event.kind === "click" || event.kind === "doubleclick" || event.kind === "contextmenu") {
|
|
1286
|
+
Object.assign(base, pressDrag(event));
|
|
1287
|
+
} else if (event.kind === "pointermove" || event.kind === "pointerup") {
|
|
1288
|
+
Object.assign(base, pumpDrag(event, facts));
|
|
1289
|
+
} else if (event.kind === "multitouch") {
|
|
1290
|
+
base.multiTouch = multiTouchOf(event, facts);
|
|
1291
|
+
}
|
|
1292
|
+
return base;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
// src/interactions/dispatcher/gestureRun.ts
|
|
1296
|
+
var ctxFor = (run, event, deps, gestureId) => buildInvocationCtx(event, deps, run.viewId(), run.facts.of(gestureId));
|
|
1297
|
+
function runOnEnd(gestureId, handle, endCtx, reason) {
|
|
1298
|
+
try {
|
|
1299
|
+
handle.onEnd?.(endCtx, reason);
|
|
1300
|
+
} catch (err) {
|
|
1301
|
+
console.error(`weasel dispatcher: gesture "${gestureId}" threw on onEnd`, err);
|
|
1420
1302
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1303
|
+
}
|
|
1304
|
+
function endGesture(run, gestureId, event, reason) {
|
|
1305
|
+
if (!run.inFlight.handles.has(gestureId)) return false;
|
|
1306
|
+
const endCtx = ctxFor(run, event, {}, gestureId);
|
|
1307
|
+
const handle = run.inFlight.take(gestureId);
|
|
1308
|
+
run.facts.drop(gestureId);
|
|
1309
|
+
runOnEnd(gestureId, handle, endCtx, reason);
|
|
1310
|
+
return true;
|
|
1311
|
+
}
|
|
1312
|
+
function endMultiTouch(run, event, reason, except) {
|
|
1313
|
+
let ended = false;
|
|
1314
|
+
for (const id of [...run.inFlight.handles.keys()]) {
|
|
1315
|
+
if (isMultiTouchGestureId(id) && id !== except) ended = endGesture(run, id, event, reason) || ended;
|
|
1316
|
+
}
|
|
1317
|
+
return ended;
|
|
1318
|
+
}
|
|
1319
|
+
function endAllGestures(run, reason) {
|
|
1320
|
+
const stubCtx = uiInvocationCtx({});
|
|
1321
|
+
const ending = run.inFlight.takeAll();
|
|
1322
|
+
run.facts.clear();
|
|
1323
|
+
for (const [gestureId, handle] of ending) runOnEnd(gestureId, handle, stubCtx, reason);
|
|
1324
|
+
}
|
|
1325
|
+
function pumpGesture(run, event) {
|
|
1326
|
+
const { inFlight } = run;
|
|
1327
|
+
const ended = (reason) => endGesture(run, gestureIdFor(event), event, reason) ? "handled" : "unhandled";
|
|
1328
|
+
if (event.kind === "key-held" && event.phase === "up") return ended("commit");
|
|
1329
|
+
if (event.kind === "key-held" && event.phase === "down") {
|
|
1330
|
+
return inFlight.handles.has(gestureIdFor(event)) ? "handled" : void 0;
|
|
1331
|
+
}
|
|
1332
|
+
if (event.kind === "pointermove") {
|
|
1333
|
+
const gestureId = gestureIdFor(event);
|
|
1334
|
+
const handle = inFlight.handles.get(gestureId);
|
|
1335
|
+
if (!handle) return "unhandled";
|
|
1336
|
+
run.facts.sample(gestureId, event);
|
|
1337
|
+
if (!handle.onMove) return "unhandled";
|
|
1338
|
+
handle.onMove(ctxFor(run, event, {}, gestureId));
|
|
1339
|
+
return "handled";
|
|
1340
|
+
}
|
|
1341
|
+
if (event.kind === "pointerup") return ended("commit");
|
|
1342
|
+
if (event.kind === "pointercancel") return ended("cancel");
|
|
1343
|
+
if (event.kind === "multitouch" && event.end !== void 0) {
|
|
1344
|
+
return endMultiTouch(run, event, event.end) ? "handled" : "unhandled";
|
|
1345
|
+
}
|
|
1346
|
+
if (event.kind === "multitouch" && event.centroid !== void 0) {
|
|
1347
|
+
const gestureId = gestureIdFor(event);
|
|
1348
|
+
const handle = inFlight.handles.get(gestureId);
|
|
1349
|
+
if (handle?.onMove) {
|
|
1350
|
+
run.facts.turn(gestureId, event);
|
|
1351
|
+
handle.onMove(ctxFor(run, event, {}, gestureId));
|
|
1352
|
+
return "handled";
|
|
1446
1353
|
}
|
|
1447
|
-
|
|
1354
|
+
endMultiTouch(run, event, "cancel", gestureId);
|
|
1448
1355
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
}
|
|
1457
|
-
|
|
1458
|
-
function getVersion() {
|
|
1459
|
-
return version;
|
|
1460
|
-
}
|
|
1461
|
-
function getActiveAction() {
|
|
1462
|
-
let lastId = null;
|
|
1463
|
-
let lastHandle = null;
|
|
1464
|
-
for (const [id, handle] of inFlightHandles) {
|
|
1465
|
-
lastId = id;
|
|
1466
|
-
lastHandle = handle;
|
|
1356
|
+
return void 0;
|
|
1357
|
+
}
|
|
1358
|
+
function invokeAction(run, event, match, action, deps) {
|
|
1359
|
+
if (action.invoker?.timing === "immediate") {
|
|
1360
|
+
try {
|
|
1361
|
+
const params = immediateParams(event, resolveParams(match.binding.opts?.params));
|
|
1362
|
+
action.invoker.run(deps, params);
|
|
1363
|
+
} catch (err) {
|
|
1364
|
+
console.error(`weasel dispatcher: action "${action.id}" invoker threw`, err);
|
|
1467
1365
|
}
|
|
1468
|
-
|
|
1469
|
-
return { kind: lastHandle.kind ?? null, id: lastId };
|
|
1366
|
+
return "fired";
|
|
1470
1367
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
notify();
|
|
1479
|
-
};
|
|
1480
|
-
function beginUiOngoing(actionId, deps, params) {
|
|
1481
|
-
const getAction = opts?.getAction;
|
|
1482
|
-
if (!getAction) {
|
|
1483
|
-
console.warn("weasel dispatcher: beginUiOngoing called but no getAction was provided to createDispatcher");
|
|
1484
|
-
return null;
|
|
1368
|
+
if (action.invoker?.timing === "ongoing") {
|
|
1369
|
+
if (event.kind === "hover" || event.kind === "pointerdown" && event.stage === "press") {
|
|
1370
|
+
const spec = event.kind === "hover" ? "hover spec, which has no press to own" : "pointerDown spec, which fires at press time";
|
|
1371
|
+
console.error(
|
|
1372
|
+
`weasel dispatcher: action "${action.id}" has an ongoing invoker but is bound to a ${spec}. Bind it to a drag spec, or give the action an immediate invoker.`
|
|
1373
|
+
);
|
|
1374
|
+
return "misbound";
|
|
1485
1375
|
}
|
|
1486
|
-
const
|
|
1487
|
-
|
|
1488
|
-
|
|
1376
|
+
const gestureId = gestureIdFor(event);
|
|
1377
|
+
run.facts.begin(gestureId, event);
|
|
1378
|
+
const handle = action.invoker.start(ctxFor(run, event, deps, gestureId), match.binding.opts);
|
|
1379
|
+
if (isEmptyOngoingHandle(handle)) {
|
|
1380
|
+
run.facts.drop(gestureId);
|
|
1381
|
+
return "empty-handle";
|
|
1489
1382
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1383
|
+
run.inFlight.open(gestureId, handle, match.ownerId, action);
|
|
1384
|
+
return "fired";
|
|
1385
|
+
}
|
|
1386
|
+
return "fired";
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
// src/interactions/dispatcher/uiOngoing.ts
|
|
1390
|
+
function createUiOngoing(inFlight, notify, getAction) {
|
|
1391
|
+
let seq = 0;
|
|
1392
|
+
const byAction = /* @__PURE__ */ new Map();
|
|
1393
|
+
function commitPrior(actionId, deps) {
|
|
1394
|
+
const prevGestureId = byAction.get(actionId);
|
|
1395
|
+
if (prevGestureId === void 0) return;
|
|
1396
|
+
const prevHandle = inFlight.handles.get(prevGestureId);
|
|
1397
|
+
if (prevHandle?.onEnd) {
|
|
1398
|
+
try {
|
|
1399
|
+
prevHandle.onEnd(uiInvocationCtx(deps), "commit");
|
|
1400
|
+
} catch (e) {
|
|
1401
|
+
console.error(`weasel dispatcher: prior UI handle for "${actionId}" threw on auto-commit`, e);
|
|
1499
1402
|
}
|
|
1500
|
-
inFlightHandles.delete(prevGestureId);
|
|
1501
|
-
inFlightOwners.delete(prevGestureId);
|
|
1502
|
-
uiOngoingByAction.delete(actionId);
|
|
1503
1403
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
} catch (e) {
|
|
1509
|
-
console.error(`weasel dispatcher: action "${actionId}" threw on start`, e);
|
|
1510
|
-
return null;
|
|
1511
|
-
}
|
|
1512
|
-
if (isEmptyOngoingHandle(handle)) {
|
|
1513
|
-
return null;
|
|
1514
|
-
}
|
|
1515
|
-
inFlightHandles.set(gestureId, handle);
|
|
1516
|
-
inFlightOwners.set(gestureId, null);
|
|
1517
|
-
uiOngoingByAction.set(actionId, gestureId);
|
|
1518
|
-
notify();
|
|
1404
|
+
inFlight.take(prevGestureId);
|
|
1405
|
+
byAction.delete(actionId);
|
|
1406
|
+
}
|
|
1407
|
+
function control(actionId, gestureId, handle, deps) {
|
|
1519
1408
|
let ended = false;
|
|
1520
|
-
const live = () => !ended &&
|
|
1409
|
+
const live = () => !ended && inFlight.handles.get(gestureId) === handle;
|
|
1521
1410
|
return {
|
|
1522
1411
|
gestureId,
|
|
1412
|
+
get active() {
|
|
1413
|
+
return live();
|
|
1414
|
+
},
|
|
1523
1415
|
update(nextParams) {
|
|
1524
1416
|
if (!live()) return;
|
|
1525
1417
|
if (!handle.onMove) return;
|
|
1526
1418
|
try {
|
|
1527
|
-
handle.onMove(
|
|
1419
|
+
handle.onMove(uiInvocationCtx(deps, nextParams));
|
|
1528
1420
|
} catch (e) {
|
|
1529
1421
|
console.error(`weasel dispatcher: action "${actionId}" threw on onMove`, e);
|
|
1530
1422
|
}
|
|
@@ -1538,43 +1430,302 @@ function createDispatcher(opts) {
|
|
|
1538
1430
|
ended = true;
|
|
1539
1431
|
if (handle.onEnd) {
|
|
1540
1432
|
try {
|
|
1541
|
-
handle.onEnd(
|
|
1433
|
+
handle.onEnd(uiInvocationCtx(deps), reason);
|
|
1542
1434
|
} catch (e) {
|
|
1543
1435
|
console.error(`weasel dispatcher: action "${actionId}" threw on onEnd`, e);
|
|
1544
1436
|
}
|
|
1545
1437
|
}
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
inFlightActions.delete(gestureId);
|
|
1549
|
-
if (uiOngoingByAction.get(actionId) === gestureId) {
|
|
1550
|
-
uiOngoingByAction.delete(actionId);
|
|
1551
|
-
}
|
|
1438
|
+
inFlight.take(gestureId);
|
|
1439
|
+
if (byAction.get(actionId) === gestureId) byAction.delete(actionId);
|
|
1552
1440
|
notify();
|
|
1553
1441
|
}
|
|
1554
1442
|
};
|
|
1555
1443
|
}
|
|
1444
|
+
return {
|
|
1445
|
+
begin(actionId, deps, params) {
|
|
1446
|
+
if (!getAction) {
|
|
1447
|
+
console.warn("weasel dispatcher: beginUiOngoing called but no getAction was provided to createDispatcher");
|
|
1448
|
+
return null;
|
|
1449
|
+
}
|
|
1450
|
+
const action = getAction(actionId);
|
|
1451
|
+
if (!action || !action.invoker || action.invoker.timing !== "ongoing") {
|
|
1452
|
+
return null;
|
|
1453
|
+
}
|
|
1454
|
+
commitPrior(actionId, deps);
|
|
1455
|
+
const gestureId = `ui-${actionId}-${++seq}`;
|
|
1456
|
+
let handle;
|
|
1457
|
+
try {
|
|
1458
|
+
handle = action.invoker.start(uiInvocationCtx(deps, params), { params });
|
|
1459
|
+
} catch (e) {
|
|
1460
|
+
console.error(`weasel dispatcher: action "${actionId}" threw on start`, e);
|
|
1461
|
+
return null;
|
|
1462
|
+
}
|
|
1463
|
+
if (isEmptyOngoingHandle(handle)) return null;
|
|
1464
|
+
inFlight.open(gestureId, handle, null);
|
|
1465
|
+
byAction.set(actionId, gestureId);
|
|
1466
|
+
notify();
|
|
1467
|
+
return control(actionId, gestureId, handle, deps);
|
|
1468
|
+
},
|
|
1469
|
+
clear: () => {
|
|
1470
|
+
byAction.clear();
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// src/interactions/dispatcher/walk.ts
|
|
1476
|
+
function assembleScopedBindings(ctx) {
|
|
1477
|
+
const allowedCapabilities = ctx.getRuleCtx?.()?.allowedCapabilities;
|
|
1478
|
+
const { ordered, state } = assembleEntries(ctx.entriesById.values(), {
|
|
1479
|
+
focusedId: ctx.activeToolId,
|
|
1480
|
+
hotkeyStack: ctx.hotkeyStack,
|
|
1481
|
+
...allowedCapabilities ? { allowedCapabilities } : {}
|
|
1482
|
+
});
|
|
1483
|
+
const view = ctx.viewId ?? null;
|
|
1484
|
+
const result = scopeBindings(ordered, state).filter((sb) => liveInView(sb.binding, view));
|
|
1485
|
+
for (const action of ctx.actions.list()) {
|
|
1486
|
+
const targetScope = action.scope === "hotkey" ? "hotkey" : "ambient";
|
|
1487
|
+
for (const binding of actionBindings(action)) {
|
|
1488
|
+
if (!liveInView(binding, view)) continue;
|
|
1489
|
+
result.push({ binding, scope: targetScope, ownerId: null });
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
return result;
|
|
1493
|
+
}
|
|
1494
|
+
function buildActionMap(registry) {
|
|
1495
|
+
const map = /* @__PURE__ */ new Map();
|
|
1496
|
+
for (const action of registry.list()) {
|
|
1497
|
+
map.set(action.id, action);
|
|
1498
|
+
}
|
|
1499
|
+
return map;
|
|
1500
|
+
}
|
|
1501
|
+
function rank(event, ctx, engaged, collectBarred) {
|
|
1502
|
+
const view = ctx.viewId ?? null;
|
|
1503
|
+
const outcome = matchSortedWithBarred(
|
|
1504
|
+
event,
|
|
1505
|
+
assembleScopedBindings(ctx),
|
|
1506
|
+
ctx.isMac,
|
|
1507
|
+
engaged,
|
|
1508
|
+
{ collectBarred }
|
|
1509
|
+
);
|
|
1510
|
+
const sorted = preferViewScoped(outcome.matches, view);
|
|
1511
|
+
const actionMap = buildActionMap(ctx.actions);
|
|
1512
|
+
const ruleCtx = ruleCtxOf(ctx);
|
|
1513
|
+
const held = /* @__PURE__ */ new Map();
|
|
1514
|
+
const holds = (a) => {
|
|
1515
|
+
if (ruleCtx === void 0) return true;
|
|
1516
|
+
let h = held.get(a);
|
|
1517
|
+
if (h === void 0) held.set(a, h = isEligible(a, ruleCtx));
|
|
1518
|
+
return h;
|
|
1519
|
+
};
|
|
1520
|
+
const ordered = ruleCtx ? preferContextual(sorted, (id) => actionMap.get(id), holds, view) : sorted;
|
|
1521
|
+
return {
|
|
1522
|
+
ordered,
|
|
1523
|
+
barred: outcome.barred,
|
|
1524
|
+
...outcome.claimOwner !== void 0 ? { claimOwner: outcome.claimOwner } : {},
|
|
1525
|
+
actionMap,
|
|
1526
|
+
ruleCtx,
|
|
1527
|
+
eligible: (m) => {
|
|
1528
|
+
const a = actionMap.get(m.binding.actionId);
|
|
1529
|
+
return a === void 0 || holds(a);
|
|
1530
|
+
}
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
function walk(r, event, ctx, attempt, evaluateShadowed) {
|
|
1534
|
+
const steps = [];
|
|
1535
|
+
const tried = /* @__PURE__ */ new Set();
|
|
1536
|
+
let stop = "exhausted";
|
|
1537
|
+
let winner = null;
|
|
1538
|
+
for (const match of r.ordered) {
|
|
1539
|
+
if (!r.eligible(match)) continue;
|
|
1540
|
+
const push = (step) => {
|
|
1541
|
+
steps.push({ match, step });
|
|
1542
|
+
};
|
|
1543
|
+
const actionId = match.binding.actionId;
|
|
1544
|
+
if (tried.has(actionId)) {
|
|
1545
|
+
push({ kind: "duplicate" });
|
|
1546
|
+
continue;
|
|
1547
|
+
}
|
|
1548
|
+
if (stop !== "exhausted" && !evaluateShadowed) {
|
|
1549
|
+
push({ kind: "not-asked" });
|
|
1550
|
+
continue;
|
|
1551
|
+
}
|
|
1552
|
+
tried.add(actionId);
|
|
1553
|
+
const action = r.actionMap.get(actionId);
|
|
1554
|
+
if (!action) {
|
|
1555
|
+
if (attempt) {
|
|
1556
|
+
console.warn(
|
|
1557
|
+
`weasel dispatcher: binding resolved actionId "${actionId}" which has no registered action. Skipping. (misconfiguration)`
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
push({ kind: "no-such-action" });
|
|
1561
|
+
continue;
|
|
1562
|
+
}
|
|
1563
|
+
const deps = buildDepsFromRequires(action, ctx.depRegistry);
|
|
1564
|
+
const enabled = action.enabled ? action.enabled(deps, worldPointOf(event), carriedBy(event)) : true;
|
|
1565
|
+
if (enabled !== true) {
|
|
1566
|
+
push({ kind: "declined", reason: String(enabled) });
|
|
1567
|
+
continue;
|
|
1568
|
+
}
|
|
1569
|
+
if (stop !== "exhausted") {
|
|
1570
|
+
push({ kind: "outranked" });
|
|
1571
|
+
continue;
|
|
1572
|
+
}
|
|
1573
|
+
if (!attempt) {
|
|
1574
|
+
push({ kind: "would-fire" });
|
|
1575
|
+
stop = "fired";
|
|
1576
|
+
winner = { match, action };
|
|
1577
|
+
continue;
|
|
1578
|
+
}
|
|
1579
|
+
const result = attempt(match, action, deps);
|
|
1580
|
+
if (result === "empty-handle") {
|
|
1581
|
+
push({ kind: "empty-handle" });
|
|
1582
|
+
continue;
|
|
1583
|
+
}
|
|
1584
|
+
push({ kind: result === "fired" ? "fired" : "misbound" });
|
|
1585
|
+
stop = result;
|
|
1586
|
+
winner = { match, action };
|
|
1587
|
+
}
|
|
1588
|
+
return { steps, stop, winner };
|
|
1589
|
+
}
|
|
1590
|
+
function verdictsOf(r, walked, evaluateShadowed) {
|
|
1591
|
+
const stepOf = new Map(walked.steps.map((s) => [s.match, s.step]));
|
|
1592
|
+
const verdictByAction = /* @__PURE__ */ new Map();
|
|
1593
|
+
let fired = false;
|
|
1594
|
+
const out = [];
|
|
1595
|
+
for (const match of r.ordered) {
|
|
1596
|
+
const actionId = match.binding.actionId;
|
|
1597
|
+
const action = r.actionMap.get(actionId);
|
|
1598
|
+
if (!action) continue;
|
|
1599
|
+
let verdict = verdictByAction.get(actionId);
|
|
1600
|
+
if (verdict === void 0) {
|
|
1601
|
+
if (!r.eligible(match)) {
|
|
1602
|
+
verdict = fired && !evaluateShadowed ? { kind: "shadowed" } : { kind: "ineligible", reason: describeEligible(action.eligible) };
|
|
1603
|
+
} else {
|
|
1604
|
+
const step = stepOf.get(match);
|
|
1605
|
+
if (step?.kind === "would-fire") {
|
|
1606
|
+
verdict = { kind: "would-fire" };
|
|
1607
|
+
fired = true;
|
|
1608
|
+
} else if (step?.kind === "declined") {
|
|
1609
|
+
verdict = { kind: "disabled", reason: step.reason };
|
|
1610
|
+
} else {
|
|
1611
|
+
verdict = { kind: "shadowed" };
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
verdictByAction.set(actionId, verdict);
|
|
1615
|
+
} else if (verdict.kind === "would-fire") {
|
|
1616
|
+
verdict = { kind: "shadowed" };
|
|
1617
|
+
}
|
|
1618
|
+
out.push({
|
|
1619
|
+
actionId,
|
|
1620
|
+
action,
|
|
1621
|
+
binding: match.binding,
|
|
1622
|
+
scope: match.scope,
|
|
1623
|
+
ownerId: match.ownerId,
|
|
1624
|
+
specificity: specificity(match.binding.spec),
|
|
1625
|
+
verdict
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1628
|
+
return out;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
// src/interactions/dispatcher/predict.ts
|
|
1632
|
+
function resolveAll(event, ctx, engaged, opts) {
|
|
1633
|
+
const evaluateShadowed = opts?.evaluateShadowed ?? false;
|
|
1634
|
+
const r = rank(event, ctx, engaged, false);
|
|
1635
|
+
if (r.ordered.length === 0) return [];
|
|
1636
|
+
return verdictsOf(r, walk(r, event, ctx, null, evaluateShadowed), evaluateShadowed);
|
|
1637
|
+
}
|
|
1638
|
+
function resolveOnly(event, ctx, engaged) {
|
|
1639
|
+
const r = rank(event, ctx, engaged, false);
|
|
1640
|
+
const { winner } = walk(r, event, ctx, null, false);
|
|
1641
|
+
if (!winner) return null;
|
|
1642
|
+
return {
|
|
1643
|
+
actionId: winner.action.id,
|
|
1644
|
+
action: winner.action,
|
|
1645
|
+
scope: winner.match.scope,
|
|
1646
|
+
ownerId: winner.match.ownerId
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
function explain(event, ctx, engaged, opts) {
|
|
1650
|
+
const r = rank(event, ctx, engaged, true);
|
|
1651
|
+
const walked = walk(r, event, ctx, null, opts?.evaluateShadowed ?? false);
|
|
1652
|
+
return buildDispatchRecord(r, walked, event, ctx, true, walked.winner ? "handled" : "unhandled");
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// src/interactions/dispatcher/dispatcher.ts
|
|
1656
|
+
function createDispatcher(opts) {
|
|
1657
|
+
const inFlight = createInFlightGestures();
|
|
1658
|
+
const facts = createGestureFacts();
|
|
1659
|
+
const changed = createEmitter();
|
|
1660
|
+
let version = 0;
|
|
1661
|
+
function notify() {
|
|
1662
|
+
version++;
|
|
1663
|
+
changed.emit();
|
|
1664
|
+
}
|
|
1665
|
+
const uiOngoing = createUiOngoing(inFlight, notify, opts?.getAction);
|
|
1666
|
+
let routedView;
|
|
1667
|
+
const run = { inFlight, facts, viewId: () => routedView };
|
|
1668
|
+
const invoking = (event) => (match, action, deps) => invokeAction(run, event, match, action, deps);
|
|
1669
|
+
function handleInput(event, ctx) {
|
|
1670
|
+
routedView = ctx.viewId;
|
|
1671
|
+
const pumped = pumpGesture(run, event);
|
|
1672
|
+
if (pumped !== void 0) return pumped;
|
|
1673
|
+
if (event.kind === "hover") return hover(event, ctx);
|
|
1674
|
+
const r = rank(event, ctx, inFlight.engagedOwners(), DEV2);
|
|
1675
|
+
const walked = walk(r, event, ctx, invoking(event), false);
|
|
1676
|
+
const outcome = walked.stop === "fired" ? "handled" : "unhandled";
|
|
1677
|
+
if (DEV2) recordTrace(buildDispatchRecord(r, walked, event, ctx, false, outcome));
|
|
1678
|
+
return outcome;
|
|
1679
|
+
}
|
|
1680
|
+
function hover(event, ctx) {
|
|
1681
|
+
let fired = false;
|
|
1682
|
+
const invoke = invoking(event);
|
|
1683
|
+
walk(rank(event, ctx, inFlight.engagedOwners(), false), event, ctx, (match, action, deps) => {
|
|
1684
|
+
if (invoke(match, action, deps) === "fired") fired = true;
|
|
1685
|
+
return "empty-handle";
|
|
1686
|
+
}, false);
|
|
1687
|
+
return fired ? "handled" : "unhandled";
|
|
1688
|
+
}
|
|
1689
|
+
function cancelAll(reason) {
|
|
1690
|
+
uiOngoing.clear();
|
|
1691
|
+
endAllGestures(run, reason);
|
|
1692
|
+
}
|
|
1693
|
+
const handleInputWithNotify = (event, ctx) => {
|
|
1694
|
+
const before = inFlight.handles.size;
|
|
1695
|
+
try {
|
|
1696
|
+
return handleInput(event, ctx);
|
|
1697
|
+
} finally {
|
|
1698
|
+
if (before > 0 || inFlight.handles.size > 0) notify();
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
const cancelAllWithNotify = (reason) => {
|
|
1702
|
+
const before = inFlight.handles.size;
|
|
1703
|
+
try {
|
|
1704
|
+
cancelAll(reason);
|
|
1705
|
+
} finally {
|
|
1706
|
+
if (before > 0) notify();
|
|
1707
|
+
}
|
|
1708
|
+
};
|
|
1556
1709
|
return {
|
|
1557
1710
|
handleInput: handleInputWithNotify,
|
|
1558
|
-
resolveOnly,
|
|
1559
|
-
resolveAll,
|
|
1560
|
-
explain,
|
|
1711
|
+
resolveOnly: (event, ctx) => resolveOnly(event, ctx, inFlight.engagedOwners()),
|
|
1712
|
+
resolveAll: (event, ctx, resolveOpts) => resolveAll(event, ctx, inFlight.engagedOwners(), resolveOpts),
|
|
1713
|
+
explain: (event, ctx, explainOpts) => explain(event, ctx, inFlight.engagedOwners(), explainOpts),
|
|
1561
1714
|
cancelAll: cancelAllWithNotify,
|
|
1562
|
-
inFlight,
|
|
1563
|
-
inFlightCursor,
|
|
1564
|
-
getInFlightHandles,
|
|
1565
|
-
subscribe,
|
|
1566
|
-
getVersion,
|
|
1567
|
-
getActiveAction,
|
|
1568
|
-
beginUiOngoing
|
|
1715
|
+
inFlight: () => inFlight.handles,
|
|
1716
|
+
inFlightCursor: inFlight.cursor,
|
|
1717
|
+
getInFlightHandles: () => inFlight.handles.values(),
|
|
1718
|
+
subscribe: changed.subscribe,
|
|
1719
|
+
getVersion: () => version,
|
|
1720
|
+
getActiveAction: inFlight.latest,
|
|
1721
|
+
beginUiOngoing: uiOngoing.begin
|
|
1569
1722
|
};
|
|
1570
1723
|
}
|
|
1571
|
-
|
|
1572
|
-
// src/interactions/dispatcher/longPressState.ts
|
|
1573
|
-
var LONG_PRESS_MS = 500;
|
|
1724
|
+
var LONG_PRESS_HAPTIC_MS = 15;
|
|
1574
1725
|
var nowMs = () => typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
1575
1726
|
function createLongPressStore() {
|
|
1576
1727
|
let current = null;
|
|
1577
|
-
const
|
|
1728
|
+
const changed = createEmitter();
|
|
1578
1729
|
return {
|
|
1579
1730
|
get: () => current,
|
|
1580
1731
|
progress: (now = nowMs()) => {
|
|
@@ -1583,16 +1734,11 @@ function createLongPressStore() {
|
|
|
1583
1734
|
const t = (now - current.startedAt) / current.duration;
|
|
1584
1735
|
return t <= 0 ? 0 : t >= 1 ? 1 : t;
|
|
1585
1736
|
},
|
|
1586
|
-
subscribe:
|
|
1587
|
-
listeners.add(fn);
|
|
1588
|
-
return () => {
|
|
1589
|
-
listeners.delete(fn);
|
|
1590
|
-
};
|
|
1591
|
-
},
|
|
1737
|
+
subscribe: changed.subscribe,
|
|
1592
1738
|
set: (next) => {
|
|
1593
1739
|
if (next === current) return;
|
|
1594
1740
|
current = next;
|
|
1595
|
-
|
|
1741
|
+
changed.emit();
|
|
1596
1742
|
}
|
|
1597
1743
|
};
|
|
1598
1744
|
}
|
|
@@ -1680,6 +1826,64 @@ function openPointerSession(origin, down, cb, opts = {}) {
|
|
|
1680
1826
|
function isTool(entry) {
|
|
1681
1827
|
return !!(entry.eligibility?.focus || entry.eligibility?.offhand);
|
|
1682
1828
|
}
|
|
1829
|
+
|
|
1830
|
+
// src/tools/routing/reflection/conflictKeys.ts
|
|
1831
|
+
var PLATFORMS = ["mac", "other"];
|
|
1832
|
+
function resolveMod(mods, platform) {
|
|
1833
|
+
if (mods.mod === void 0) return mods;
|
|
1834
|
+
const { mod, meta: _meta, ctrl: _ctrl, ...rest } = mods;
|
|
1835
|
+
return { ...rest, [platform === "mac" ? "meta" : "ctrl"]: mod };
|
|
1836
|
+
}
|
|
1837
|
+
function sameOutcome(entries) {
|
|
1838
|
+
const first = entries[0];
|
|
1839
|
+
return entries.every((e) => e.ownerId === first.ownerId && e.actionId === first.actionId && sameParams(e.params, first.params));
|
|
1840
|
+
}
|
|
1841
|
+
function sameParams(a, b) {
|
|
1842
|
+
if (a === b) return true;
|
|
1843
|
+
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
|
|
1844
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
1845
|
+
const keys = Object.keys(a);
|
|
1846
|
+
return keys.length === Object.keys(b).length && keys.every((k) => sameParams(a[k], b[k]));
|
|
1847
|
+
}
|
|
1848
|
+
function modifierTieKeys(entry, platform) {
|
|
1849
|
+
const mods = resolveMod(entry.modifiers, platform);
|
|
1850
|
+
const optional = VALID_MOD_NAMES.filter((k) => mods[k] === "optional");
|
|
1851
|
+
const required = VALID_MOD_NAMES.filter((k) => mods[k] === "required");
|
|
1852
|
+
const keys = [];
|
|
1853
|
+
for (let bits = 0; bits < 1 << optional.length; bits++) {
|
|
1854
|
+
const held = [...required, ...optional.filter((_, i) => (bits & 1 << i) !== 0)].sort();
|
|
1855
|
+
keys.push(`${held.join("+")}:${required.length}`);
|
|
1856
|
+
}
|
|
1857
|
+
return keys;
|
|
1858
|
+
}
|
|
1859
|
+
function viewsTie(a, b) {
|
|
1860
|
+
if (a === void 0 || b === void 0) return a === b;
|
|
1861
|
+
return a.some((v) => b.includes(v));
|
|
1862
|
+
}
|
|
1863
|
+
function argKeysOf(entry) {
|
|
1864
|
+
const alts = entry.argAlternatives;
|
|
1865
|
+
if (alts && alts.length > 0) return [...new Set(alts.map((a) => a.toLowerCase()))];
|
|
1866
|
+
return [(entry.arg ?? "").toLowerCase()];
|
|
1867
|
+
}
|
|
1868
|
+
function targetKey(entry) {
|
|
1869
|
+
if (entry.target !== PREDICATE_TARGET) return entry.target ?? "";
|
|
1870
|
+
const spec = entry.spec;
|
|
1871
|
+
const pred = spec.target;
|
|
1872
|
+
if (typeof pred !== "object" || pred === null) return PREDICATE_TARGET;
|
|
1873
|
+
return `${PREDICATE_TARGET}#${objectId(pred)}`;
|
|
1874
|
+
}
|
|
1875
|
+
var OBJECT_IDS = /* @__PURE__ */ new WeakMap();
|
|
1876
|
+
var nextObjectId = 0;
|
|
1877
|
+
function objectId(obj) {
|
|
1878
|
+
let id = OBJECT_IDS.get(obj);
|
|
1879
|
+
if (id === void 0) {
|
|
1880
|
+
id = nextObjectId++;
|
|
1881
|
+
OBJECT_IDS.set(obj, id);
|
|
1882
|
+
}
|
|
1883
|
+
return id;
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
// src/tools/routing/reflection/conflicts.ts
|
|
1683
1887
|
function findConflicts(entries) {
|
|
1684
1888
|
return findConflictsKeyed(entries).map((k) => k.conflict);
|
|
1685
1889
|
}
|
|
@@ -1688,22 +1892,33 @@ function findConflictsKeyed(entries, rules, modes = KIT_MODES) {
|
|
|
1688
1892
|
const groups = /* @__PURE__ */ new Map();
|
|
1689
1893
|
for (const entry of rows) {
|
|
1690
1894
|
for (const arg of argKeysOf(entry)) {
|
|
1691
|
-
const
|
|
1692
|
-
const
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1895
|
+
const base = `${entry.phase}|${entry.gesture}|${arg}|${targetKey(entry)}`;
|
|
1896
|
+
for (const platform of PLATFORMS) {
|
|
1897
|
+
for (const state of modifierTieKeys(entry, platform)) {
|
|
1898
|
+
const key = `${platform}|${base}|${state}`;
|
|
1899
|
+
const bucket = groups.get(key);
|
|
1900
|
+
if (bucket) {
|
|
1901
|
+
if (!bucket.members.includes(entry)) bucket.members.push(entry);
|
|
1902
|
+
} else groups.set(key, { base, platform, members: [entry] });
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1696
1905
|
}
|
|
1697
1906
|
}
|
|
1698
1907
|
const ruleOf = (entry) => rules?.get(entry.ownerId) ?? ALWAYS;
|
|
1699
1908
|
const tie = (e, f) => viewsTie(e.views, f.views) && rulesCanHoldTogether(ruleOf(e), ruleOf(f), modes);
|
|
1700
|
-
const conflicts =
|
|
1701
|
-
for (const
|
|
1702
|
-
const
|
|
1909
|
+
const conflicts = /* @__PURE__ */ new Map();
|
|
1910
|
+
for (const { base, platform, members } of groups.values()) {
|
|
1911
|
+
const ties = (e, i, f, j) => i !== j && !(i < j ? e : f).yields && tie(e, f);
|
|
1912
|
+
const bucket = members.filter((e, i) => members.some((f, j) => ties(e, i, f, j)));
|
|
1703
1913
|
if (bucket.length < 2) continue;
|
|
1704
1914
|
const first = bucket[0];
|
|
1705
|
-
|
|
1706
|
-
|
|
1915
|
+
const key = `${base}|${bucket.map((e) => `${objectId(e.spec)}:${canonicalModifiers(e.modifiers)}`).join(";")}`;
|
|
1916
|
+
const found = conflicts.get(key);
|
|
1917
|
+
if (found) {
|
|
1918
|
+
found.platforms.add(platform);
|
|
1919
|
+
continue;
|
|
1920
|
+
}
|
|
1921
|
+
conflicts.set(key, {
|
|
1707
1922
|
conflict: {
|
|
1708
1923
|
phase: first.phase,
|
|
1709
1924
|
gesture: first.gesture,
|
|
@@ -1711,36 +1926,12 @@ function findConflictsKeyed(entries, rules, modes = KIT_MODES) {
|
|
|
1711
1926
|
target: first.target,
|
|
1712
1927
|
modifiers: first.modifiers,
|
|
1713
1928
|
ownerIds: bucket.map((e) => e.ownerId)
|
|
1714
|
-
}
|
|
1929
|
+
},
|
|
1930
|
+
bucket,
|
|
1931
|
+
platforms: /* @__PURE__ */ new Set([platform])
|
|
1715
1932
|
});
|
|
1716
1933
|
}
|
|
1717
|
-
return conflicts;
|
|
1718
|
-
}
|
|
1719
|
-
function viewsTie(a, b) {
|
|
1720
|
-
if (a === void 0 || b === void 0) return a === b;
|
|
1721
|
-
return a.some((v) => b.includes(v));
|
|
1722
|
-
}
|
|
1723
|
-
function argKeysOf(entry) {
|
|
1724
|
-
const alts = entry.argAlternatives;
|
|
1725
|
-
if (alts && alts.length > 0) return [...new Set(alts.map((a) => a.toLowerCase()))];
|
|
1726
|
-
return [(entry.arg ?? "").toLowerCase()];
|
|
1727
|
-
}
|
|
1728
|
-
function targetKey(entry) {
|
|
1729
|
-
if (entry.target !== PREDICATE_TARGET) return entry.target ?? "";
|
|
1730
|
-
const spec = entry.spec;
|
|
1731
|
-
const pred = spec.target;
|
|
1732
|
-
if (typeof pred !== "object" || pred === null) return PREDICATE_TARGET;
|
|
1733
|
-
return `${PREDICATE_TARGET}#${predicateId(pred)}`;
|
|
1734
|
-
}
|
|
1735
|
-
var PREDICATE_IDS = /* @__PURE__ */ new WeakMap();
|
|
1736
|
-
var nextPredicateId = 0;
|
|
1737
|
-
function predicateId(pred) {
|
|
1738
|
-
let id = PREDICATE_IDS.get(pred);
|
|
1739
|
-
if (id === void 0) {
|
|
1740
|
-
id = nextPredicateId++;
|
|
1741
|
-
PREDICATE_IDS.set(pred, id);
|
|
1742
|
-
}
|
|
1743
|
-
return id;
|
|
1934
|
+
return [...conflicts].filter(([, c]) => c.platforms.size === PLATFORMS.length || !sameOutcome(c.bucket)).map(([key, { conflict }]) => ({ key, conflict }));
|
|
1744
1935
|
}
|
|
1745
1936
|
function rulesCanHoldTogether(a, b, modes) {
|
|
1746
1937
|
if (rulesExclusive(a, b)) return false;
|
|
@@ -1788,7 +1979,7 @@ function findScopedConflicts(scopes) {
|
|
|
1788
1979
|
if (ambient.length > 1) add(findConflictsKeyed(ambient, rules, modes));
|
|
1789
1980
|
if (gatedActions.length > 1) add(findConflictsKeyed(gatedActions, rules, modes));
|
|
1790
1981
|
const hotkey = [
|
|
1791
|
-
...registry.filter((t) => t.def?.hotkey !== void 0),
|
|
1982
|
+
...registry.filter((t) => t.eligibility?.offhand !== void 0 || t.def?.hotkey !== void 0),
|
|
1792
1983
|
...hotkeyActions
|
|
1793
1984
|
];
|
|
1794
1985
|
if (hotkey.length > 1) add(findConflictsKeyed(hotkey, rules, modes));
|
|
@@ -1814,38 +2005,6 @@ function reportRouteConflicts(scopes, warn = (m) => console.warn(m)) {
|
|
|
1814
2005
|
}
|
|
1815
2006
|
return conflicts;
|
|
1816
2007
|
}
|
|
1817
|
-
|
|
1818
|
-
// src/interactions/keyHelpers.ts
|
|
1819
|
-
function isEditableTarget(target) {
|
|
1820
|
-
if (!target || !(target instanceof Element)) return false;
|
|
1821
|
-
const tag = target.tagName;
|
|
1822
|
-
if (tag === "INPUT" || tag === "TEXTAREA") return true;
|
|
1823
|
-
if (target.isContentEditable) return true;
|
|
1824
|
-
const ce = target.getAttribute("contenteditable");
|
|
1825
|
-
return ce === "true" || ce === "";
|
|
1826
|
-
}
|
|
1827
|
-
var ACTIVATED_BY_KEY = [
|
|
1828
|
-
"button",
|
|
1829
|
-
"a[href]",
|
|
1830
|
-
"summary",
|
|
1831
|
-
"select",
|
|
1832
|
-
'input[type="button"]',
|
|
1833
|
-
'input[type="submit"]',
|
|
1834
|
-
'input[type="reset"]',
|
|
1835
|
-
'input[type="checkbox"]',
|
|
1836
|
-
'input[type="radio"]',
|
|
1837
|
-
'[role="button"]',
|
|
1838
|
-
'[role="link"]',
|
|
1839
|
-
'[role="checkbox"]',
|
|
1840
|
-
'[role="switch"]',
|
|
1841
|
-
'[role="menuitem"]',
|
|
1842
|
-
'[role="tab"]',
|
|
1843
|
-
'[role="option"]'
|
|
1844
|
-
].join(", ");
|
|
1845
|
-
function activatesFocusedControl(e) {
|
|
1846
|
-
if (e.key !== " " && e.key !== "Enter") return false;
|
|
1847
|
-
return e.target instanceof Element && e.target.matches(ACTIVATED_BY_KEY);
|
|
1848
|
-
}
|
|
1849
2008
|
function keyMatches(eventKey, spec) {
|
|
1850
2009
|
const want = typeof spec === "string" ? [spec] : spec;
|
|
1851
2010
|
const ek = eventKey.toLowerCase();
|
|
@@ -1873,8 +2032,6 @@ function historyKey(e) {
|
|
|
1873
2032
|
return "redo";
|
|
1874
2033
|
return null;
|
|
1875
2034
|
}
|
|
1876
|
-
|
|
1877
|
-
// src/ingestion/ingestItems.ts
|
|
1878
2035
|
var INGEST_STRING_MIMES = /* @__PURE__ */ new Set([
|
|
1879
2036
|
"text/plain",
|
|
1880
2037
|
"text/html",
|
|
@@ -1919,6 +2076,16 @@ function itemsFromDataTransfer(dt) {
|
|
|
1919
2076
|
}
|
|
1920
2077
|
return Promise.all(reads).then(() => [...files, ...strings]);
|
|
1921
2078
|
}
|
|
2079
|
+
function itemTypesFromDataTransfer(dt) {
|
|
2080
|
+
const mimes = [];
|
|
2081
|
+
for (const item of Array.from(dt.items ?? [])) {
|
|
2082
|
+
const mime = item.type ? normalizeMime(item.type) : "";
|
|
2083
|
+
if (item.kind === "file") mimes.push(mime && mime !== "application/x-moz-file" ? mime : UNKNOWN_MIME);
|
|
2084
|
+
else if (INGEST_STRING_MIMES.has(mime)) mimes.push(mime);
|
|
2085
|
+
}
|
|
2086
|
+
if (mimes.length === 0 && Array.from(dt.types ?? []).includes("Files")) mimes.push(UNKNOWN_MIME);
|
|
2087
|
+
return mimes.map((mime) => ({ mime }));
|
|
2088
|
+
}
|
|
1922
2089
|
function itemsFromClipboardData(cd) {
|
|
1923
2090
|
const out = [];
|
|
1924
2091
|
for (const file of Array.from(cd.files ?? [])) {
|
|
@@ -1972,7 +2139,11 @@ function dlog(namespace, ...args) {
|
|
|
1972
2139
|
if (!isDebugEnabled(namespace)) return;
|
|
1973
2140
|
console.debug(`[${namespace}]`, ...args);
|
|
1974
2141
|
}
|
|
2142
|
+
function dwarn(namespace, ...args) {
|
|
2143
|
+
if (!isDebugEnabled(namespace)) return;
|
|
2144
|
+
console.warn(`[${namespace}]`, ...args);
|
|
2145
|
+
}
|
|
1975
2146
|
|
|
1976
|
-
export { ALWAYS, COARSE_TARGET_SCALE, DEFAULT_ALLOWED_CAPABILITIES, DEFAULT_DEVICE_PROFILE, DepNode, INGEST_STRING_MIMES,
|
|
1977
|
-
//# sourceMappingURL=chunk-
|
|
1978
|
-
//# sourceMappingURL=chunk-
|
|
2147
|
+
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, assembleEntries, buildDepsFromRequires, buildRouteRegistry, buildRuleCtx, buildToolOffhandBindings, clientToCanvas, clientToCanvasRect, createDepRegistry, createDispatcher, createLongPressStore, depNodeOf, describeRule, dlog, dwarn, evaluate, evaluateActive, evaluateEnabled, findConflicts, heldKeySpec, historyKey, isDebugEnabled, isDev, isTool, itemTypesFromDataTransfer, itemsFromClipboardData, itemsFromDataTransfer, itemsFromFiles, liveScope, makeToolOffhandAction, matchBest, matchSorted, matchSortedWithBarred, matchesKeyBinding, multiTouchGestureId, offhandKeyFor, openPointerSession, pointerGestureId, publishLiveDispatch, pushOwner, recordModeSwitch, reportRouteConflicts, resolveDeviceProfile, resolveParams, routesForSpec, ruleCanHoldIn, rulesExclusive, scopeBindings, targetConsultsAffordance, validateActionDefaultBinding, validateActionId };
|
|
2148
|
+
//# sourceMappingURL=chunk-4DR6AJMN.js.map
|
|
2149
|
+
//# sourceMappingURL=chunk-4DR6AJMN.js.map
|