@opentui/keymap 0.2.1 → 0.2.3

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.
Files changed (86) hide show
  1. package/README.md +64 -30
  2. package/chunks/index-frk6sdcd.js +409 -0
  3. package/chunks/index-frk6sdcd.js.map +14 -0
  4. package/package.json +43 -23
  5. package/src/addons/index.js +1130 -0
  6. package/src/addons/index.js.map +25 -0
  7. package/src/addons/opentui/edit-buffer-bindings.d.ts +6 -2
  8. package/src/addons/opentui/index.d.ts +2 -2
  9. package/src/addons/opentui/index.js +467 -0
  10. package/src/addons/opentui/index.js.map +12 -0
  11. package/src/addons/universal/binding-overrides.d.ts +6 -0
  12. package/src/addons/universal/dead-bindings.d.ts +1 -1
  13. package/src/addons/universal/default-parser.d.ts +2 -2
  14. package/src/addons/universal/ex-commands.d.ts +11 -8
  15. package/src/addons/universal/index.d.ts +3 -1
  16. package/src/addons/universal/leader.d.ts +1 -1
  17. package/src/addons/universal/metadata.d.ts +2 -2
  18. package/src/addons/universal/mod-bindings.d.ts +6 -0
  19. package/src/addons/universal/unresolved-commands.d.ts +1 -1
  20. package/src/extras/binding-sections.d.ts +18 -0
  21. package/src/extras/command-bindings.d.ts +19 -0
  22. package/src/extras/formatting.d.ts +27 -0
  23. package/src/extras/graph.d.ts +9 -0
  24. package/src/extras/graph.js +373 -0
  25. package/src/extras/graph.js.map +11 -0
  26. package/src/extras/index.d.ts +6 -0
  27. package/src/extras/index.js +239 -0
  28. package/src/extras/index.js.map +12 -0
  29. package/src/extras/lib/graph-snapshot.d.ts +14 -0
  30. package/src/extras/lib/graph-types.d.ts +83 -0
  31. package/src/html.d.ts +3 -3
  32. package/src/html.js +297 -0
  33. package/src/html.js.map +10 -0
  34. package/src/index.d.ts +3 -1
  35. package/src/index.js +4492 -0
  36. package/src/index.js.map +34 -0
  37. package/src/keymap.d.ts +23 -35
  38. package/src/lib/emitter.d.ts +1 -2
  39. package/src/lib/registry.d.ts +2 -2
  40. package/src/lib/runtime-utils.d.ts +34 -0
  41. package/src/opentui.d.ts +1 -3
  42. package/src/opentui.js +133 -0
  43. package/src/opentui.js.map +10 -0
  44. package/src/react/index.d.ts +5 -19
  45. package/{react → src/react}/index.js +3 -0
  46. package/src/react/index.js.map +10 -0
  47. package/src/runtime-modules.d.ts +20 -0
  48. package/src/runtime-modules.js +28 -0
  49. package/src/runtime-modules.js.map +10 -0
  50. package/src/services/activation.d.ts +7 -33
  51. package/src/services/active-key-cache.d.ts +29 -0
  52. package/src/services/command-catalog.d.ts +28 -45
  53. package/src/services/command-executor.d.ts +7 -13
  54. package/src/services/compiler.d.ts +6 -12
  55. package/src/services/conditions.d.ts +4 -16
  56. package/src/services/dispatch-decisions.d.ts +21 -0
  57. package/src/services/dispatch-patterns.d.ts +5 -0
  58. package/src/services/dispatch.d.ts +6 -42
  59. package/src/services/environment.d.ts +6 -21
  60. package/src/services/extension-context.d.ts +16 -0
  61. package/src/services/layer-diagnostics.d.ts +10 -0
  62. package/src/services/layers.d.ts +15 -23
  63. package/src/services/notify.d.ts +6 -8
  64. package/src/services/pending-sequence.d.ts +4 -0
  65. package/src/services/primitives/active-layers.d.ts +2 -3
  66. package/src/services/primitives/bindings.d.ts +4 -0
  67. package/src/services/primitives/command-normalization.d.ts +3 -0
  68. package/src/services/primitives/field-invariants.d.ts +16 -1
  69. package/src/services/primitives/pending-captures.d.ts +5 -0
  70. package/src/services/runtime-view.d.ts +5 -0
  71. package/src/services/runtime.d.ts +2 -7
  72. package/src/services/sequence-index.d.ts +24 -0
  73. package/src/services/state.d.ts +46 -91
  74. package/src/solid/index.d.ts +5 -19
  75. package/{solid → src/solid}/index.js +3 -0
  76. package/src/solid/index.js.map +10 -0
  77. package/src/testing/index.d.ts +90 -0
  78. package/src/testing/index.js +276 -0
  79. package/src/testing/index.js.map +10 -0
  80. package/src/types.d.ts +194 -126
  81. package/addons/index.js +0 -5240
  82. package/addons/opentui/index.js +0 -5632
  83. package/html.js +0 -5042
  84. package/index.js +0 -4411
  85. package/opentui.js +0 -4887
  86. package/src/services/primitives/binding-inputs.d.ts +0 -4
package/src/index.js ADDED
@@ -0,0 +1,4492 @@
1
+ // @bun
2
+ import {
3
+ KEYMAP_EXTENSION_CONTEXT,
4
+ activeOptionsForCaptures,
5
+ advanceSequenceCapture,
6
+ buildSequenceTree,
7
+ captureHasContinuations,
8
+ captureIsExact,
9
+ capturePriority,
10
+ cloneKeySequence,
11
+ cloneKeyStroke,
12
+ collectRootSequenceCaptures,
13
+ createKeySequencePart,
14
+ createTextKeyMatch,
15
+ getActiveLayersForFocused,
16
+ getFocusedTargetIfAvailable,
17
+ isLayerActiveForFocused,
18
+ normalizeBindingTokenName,
19
+ resolveKeyMatch,
20
+ stringifyKeySequence,
21
+ stringifyKeyStroke
22
+ } from "../chunks/index-frk6sdcd.js";
23
+
24
+ // src/lib/runtime-utils.ts
25
+ function createRuntimeEmitter(onError) {
26
+ let listeners = Object.create(null);
27
+ const off = (name, listener) => {
28
+ const current = listeners[name];
29
+ if (!current)
30
+ return;
31
+ const next = current.filter((candidate) => candidate !== listener);
32
+ if (next.length === 0)
33
+ delete listeners[name];
34
+ else if (next.length !== current.length)
35
+ listeners[name] = next;
36
+ };
37
+ return {
38
+ hook(name, listener) {
39
+ listeners[name] = [...listeners[name] ?? [], listener];
40
+ return () => off(name, listener);
41
+ },
42
+ has(name) {
43
+ return (listeners[name]?.length ?? 0) > 0;
44
+ },
45
+ emit(name, ...args) {
46
+ const current = listeners[name];
47
+ if (!current)
48
+ return;
49
+ for (const listener of current) {
50
+ try {
51
+ if (args.length === 0)
52
+ listener();
53
+ else
54
+ listener(args[0]);
55
+ } catch (error) {
56
+ onError(name, error);
57
+ }
58
+ }
59
+ }
60
+ };
61
+ }
62
+ function createItems() {
63
+ let items = [];
64
+ return {
65
+ get: () => items,
66
+ set: (next) => {
67
+ items = next;
68
+ },
69
+ remove(value) {
70
+ items = items.filter((candidate) => candidate !== value);
71
+ },
72
+ has: () => items.length > 0,
73
+ clear: () => {
74
+ items = [];
75
+ }
76
+ };
77
+ }
78
+ function createRuntimeOrderedRegistry() {
79
+ const items = createItems();
80
+ return {
81
+ append(value) {
82
+ items.set([...items.get(), value]);
83
+ return () => items.remove(value);
84
+ },
85
+ prepend(value) {
86
+ items.set([value, ...items.get()]);
87
+ return () => items.remove(value);
88
+ },
89
+ values: items.get,
90
+ has: items.has,
91
+ clear: items.clear
92
+ };
93
+ }
94
+ function createRuntimePriorityRegistry() {
95
+ const items = createItems();
96
+ let order = 0;
97
+ return {
98
+ register(listener, options) {
99
+ const registered = { ...options, listener, order: order++ };
100
+ items.set([...items.get(), registered].sort((left, right) => {
101
+ const priorityDiff = right.priority - left.priority;
102
+ return priorityDiff || left.order - right.order;
103
+ }));
104
+ return () => items.remove(registered);
105
+ },
106
+ entries: items.get,
107
+ has: items.has,
108
+ clear: items.clear
109
+ };
110
+ }
111
+
112
+ // src/services/primitives/bindings.ts
113
+ function isKeyLike(value) {
114
+ return typeof value === "string" || !!value && typeof value === "object" && !Array.isArray(value);
115
+ }
116
+ function validateBindings(bindings) {
117
+ if (!Array.isArray(bindings)) {
118
+ return { ok: false, reason: "Invalid keymap bindings: expected an array of binding objects" };
119
+ }
120
+ for (const [index, binding] of bindings.entries()) {
121
+ if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
122
+ return { ok: false, reason: `Invalid keymap binding at index ${index}: expected a binding object` };
123
+ }
124
+ if (!isKeyLike(binding.key)) {
125
+ return {
126
+ ok: false,
127
+ reason: `Invalid keymap binding at index ${index}: expected "key" to be a string or keystroke object`
128
+ };
129
+ }
130
+ }
131
+ return { ok: true };
132
+ }
133
+ function snapshotBindings(bindings) {
134
+ const validation = validateBindings(bindings);
135
+ if (!validation.ok) {
136
+ throw new Error(validation.reason);
137
+ }
138
+ return bindings.map((binding) => ({
139
+ ...binding,
140
+ key: typeof binding.key === "string" ? binding.key : { ...binding.key }
141
+ }));
142
+ }
143
+ function snapshotParsedBinding(binding) {
144
+ return {
145
+ ...binding,
146
+ sequence: cloneKeySequence(binding.sequence)
147
+ };
148
+ }
149
+
150
+ // src/services/layer-diagnostics.ts
151
+ function createLayerDiagnostics(notify, commands) {
152
+ const analyzers = createRuntimeOrderedRegistry();
153
+ return {
154
+ prependLayerAnalyzer(analyzer) {
155
+ return analyzers.prepend(analyzer);
156
+ },
157
+ appendLayerAnalyzer(analyzer) {
158
+ return analyzers.append(analyzer);
159
+ },
160
+ clearLayerAnalyzers() {
161
+ analyzers.clear();
162
+ },
163
+ analyzeLayer(options) {
164
+ const registeredAnalyzers = analyzers.values();
165
+ if (registeredAnalyzers.length === 0)
166
+ return;
167
+ const bindings = buildLayerBindingAnalyses(options.bindings);
168
+ const ctx = {
169
+ target: options.target,
170
+ order: options.order,
171
+ sourceBindings: options.sourceBindings,
172
+ bindings,
173
+ hasTokenBindings: options.hasTokenBindings,
174
+ checkCommandResolution(command) {
175
+ return commands.getCommandResolutionStatus(command, options.commands);
176
+ },
177
+ warn(code, warning, message) {
178
+ notify.emitWarning(code, warning, message);
179
+ },
180
+ warnOnce(key, code, warning, message) {
181
+ notify.warnOnce(key, code, warning, message);
182
+ },
183
+ error(code, error, message) {
184
+ notify.emitError(code, error, message);
185
+ }
186
+ };
187
+ for (const analyzer of registeredAnalyzers) {
188
+ try {
189
+ analyzer(ctx);
190
+ } catch (error) {
191
+ notify.emitError("layer-analyzer-error", error, "[Keymap] Error in layer analyzer:");
192
+ }
193
+ }
194
+ }
195
+ };
196
+ }
197
+ function buildLayerBindingAnalyses(bindings) {
198
+ return bindings.map((binding) => ({
199
+ sequence: cloneKeySequence(binding.sequence),
200
+ command: binding.command,
201
+ attrs: binding.attrs,
202
+ event: binding.event,
203
+ preventDefault: binding.preventDefault,
204
+ fallthrough: binding.fallthrough,
205
+ parsedBinding: snapshotParsedBinding(binding.parsedBinding),
206
+ sourceTarget: binding.sourceTarget,
207
+ sourceLayerOrder: binding.sourceLayerOrder,
208
+ bindingIndex: binding.bindingIndex,
209
+ hasCommandAtSequence: bindings.some((candidate) => {
210
+ return candidate.event === "press" && candidate.command !== undefined && sameSequence(candidate, binding);
211
+ }),
212
+ hasContinuations: bindings.some((candidate) => {
213
+ return candidate.event === "press" && isPrefix(binding, candidate);
214
+ })
215
+ }));
216
+ }
217
+ function sameSequence(left, right) {
218
+ return left.sequence.length === right.sequence.length && left.sequence.every((part, index) => part.match === right.sequence[index]?.match);
219
+ }
220
+ function isPrefix(left, right) {
221
+ return left.sequence.length < right.sequence.length && left.sequence.every((part, index) => part.match === right.sequence[index]?.match);
222
+ }
223
+
224
+ // src/services/active-key-cache.ts
225
+ function createActiveKeysCache() {
226
+ return {
227
+ version: -1,
228
+ notifyVersion: -1,
229
+ focused: undefined,
230
+ value: [],
231
+ targets: new WeakMap
232
+ };
233
+ }
234
+ function createActiveKeysCaches() {
235
+ return {
236
+ plain: createActiveKeysCache(),
237
+ bindings: createActiveKeysCache(),
238
+ metadata: createActiveKeysCache(),
239
+ bindingsAndMetadata: createActiveKeysCache()
240
+ };
241
+ }
242
+ function getActiveKeysCache(caches, options) {
243
+ if (options === undefined) {
244
+ return caches.plain;
245
+ }
246
+ const includeBindings = options.includeBindings === true;
247
+ const includeMetadata = options.includeMetadata === true;
248
+ return includeBindings ? includeMetadata ? caches.bindingsAndMetadata : caches.bindings : includeMetadata ? caches.metadata : caches.plain;
249
+ }
250
+ function getFocusedActiveKeysCache(cache, cacheVersion, focused) {
251
+ const cached = focused ? cache.targets.get(focused) : cache.nullTarget;
252
+ return cached?.version === cacheVersion ? cached : undefined;
253
+ }
254
+ function setFocusedActiveKeysCache(cache, cacheVersion, focused, value) {
255
+ const cached = { version: cacheVersion, value };
256
+ if (focused) {
257
+ cache.targets.set(focused, cached);
258
+ } else {
259
+ cache.nullTarget = cached;
260
+ }
261
+ }
262
+
263
+ // src/services/pending-sequence.ts
264
+ function isSamePendingSequence(current, next) {
265
+ if (current === next) {
266
+ return true;
267
+ }
268
+ if (!current || !next) {
269
+ return false;
270
+ }
271
+ if (current.captures.length !== next.captures.length) {
272
+ return false;
273
+ }
274
+ for (let index = 0;index < current.captures.length; index += 1) {
275
+ const left = current.captures[index];
276
+ const right = next.captures[index];
277
+ if (!left || !right || left.layer !== right.layer || left.binding !== right.binding || left.index !== right.index || left.parts.length !== right.parts.length) {
278
+ return false;
279
+ }
280
+ for (let partIndex = 0;partIndex < left.parts.length; partIndex += 1) {
281
+ if (left.parts[partIndex]?.match !== right.parts[partIndex]?.match) {
282
+ return false;
283
+ }
284
+ }
285
+ const leftPatterns = left.patterns ?? [];
286
+ const rightPatterns = right.patterns ?? [];
287
+ if (leftPatterns.length !== rightPatterns.length) {
288
+ return false;
289
+ }
290
+ for (let patternIndex = 0;patternIndex < leftPatterns.length; patternIndex += 1) {
291
+ const leftPattern = leftPatterns[patternIndex];
292
+ const rightPattern = rightPatterns[patternIndex];
293
+ if (!leftPattern || !rightPattern || leftPattern.name !== rightPattern.name) {
294
+ return false;
295
+ }
296
+ if (leftPattern.values.length !== rightPattern.values.length) {
297
+ return false;
298
+ }
299
+ for (let valueIndex = 0;valueIndex < leftPattern.values.length; valueIndex += 1) {
300
+ if (!Object.is(leftPattern.values[valueIndex], rightPattern.values[valueIndex])) {
301
+ return false;
302
+ }
303
+ }
304
+ }
305
+ }
306
+ return true;
307
+ }
308
+ function popCapture(capture) {
309
+ const lastPart = capture.parts.at(-1);
310
+ if (!lastPart || capture.parts.length <= 1) {
311
+ return;
312
+ }
313
+ let index = capture.index - 1;
314
+ let patterns = capture.patterns;
315
+ if (lastPart.patternName) {
316
+ const lastPattern = patterns?.at(-1);
317
+ if (lastPattern?.name === lastPart.patternName) {
318
+ if (lastPattern.values.length > 1) {
319
+ index = capture.index;
320
+ patterns = [
321
+ ...(patterns ?? []).slice(0, -1),
322
+ {
323
+ ...lastPattern,
324
+ values: lastPattern.values.slice(0, -1),
325
+ parts: lastPattern.parts.slice(0, -1)
326
+ }
327
+ ];
328
+ } else {
329
+ patterns = (patterns ?? []).slice(0, -1);
330
+ }
331
+ }
332
+ }
333
+ return {
334
+ layer: capture.layer,
335
+ binding: capture.binding,
336
+ index,
337
+ parts: capture.parts.slice(0, -1),
338
+ patterns
339
+ };
340
+ }
341
+ function collectSequencePartsFromPending(pending) {
342
+ const firstCapture = pending.captures[0];
343
+ if (!firstCapture || firstCapture.parts.length === 0) {
344
+ return [];
345
+ }
346
+ const parts = [];
347
+ for (let index = 0;index < firstCapture.parts.length; index += 1) {
348
+ const firstPart = firstCapture.parts[index];
349
+ if (!firstPart)
350
+ continue;
351
+ let display;
352
+ let tokenName;
353
+ let hasDisplayConflict = false;
354
+ let hasTokenConflict = false;
355
+ for (const capture of pending.captures) {
356
+ const part = capture.parts[index];
357
+ if (!part) {
358
+ continue;
359
+ }
360
+ if (display === undefined) {
361
+ display = part.display;
362
+ tokenName = part.tokenName;
363
+ continue;
364
+ }
365
+ if (!hasDisplayConflict && display !== part.display) {
366
+ hasDisplayConflict = true;
367
+ }
368
+ if (!hasTokenConflict && tokenName !== part.tokenName) {
369
+ hasTokenConflict = true;
370
+ }
371
+ }
372
+ if (display === undefined || hasDisplayConflict) {
373
+ display = stringifyKeyStroke(firstPart.stroke);
374
+ }
375
+ if (hasTokenConflict) {
376
+ tokenName = undefined;
377
+ }
378
+ parts.push(createKeySequencePart(firstPart.stroke, {
379
+ display,
380
+ match: firstPart.match,
381
+ tokenName
382
+ }));
383
+ }
384
+ return parts;
385
+ }
386
+
387
+ // src/services/activation.ts
388
+ function getLiveHost(host) {
389
+ if (host.isDestroyed) {
390
+ throw new Error("Cannot use a keymap after its host was destroyed");
391
+ }
392
+ return host;
393
+ }
394
+ function createActivationService(state, host, hooks, notify, conditions, catalog, options = {}) {
395
+ const activeKeysCaches = createActiveKeysCaches();
396
+ let pendingSequenceCacheVersion = -1;
397
+ let pendingSequenceCache = [];
398
+ const getFocusedTarget = () => {
399
+ return getLiveHost(host).getFocusedTarget();
400
+ };
401
+ const getFocusedTargetIfAvailable2 = () => {
402
+ return getFocusedTargetIfAvailable(host);
403
+ };
404
+ const setPendingSequence = (next) => {
405
+ const previous = state.pending;
406
+ if (isSamePendingSequence(previous, next)) {
407
+ return;
408
+ }
409
+ state.pending = next;
410
+ options.onPendingSequenceChanged?.(previous, next);
411
+ notifyPendingSequenceChange();
412
+ notify.queueStateChange();
413
+ };
414
+ const ensureValidPendingSequence = () => {
415
+ const pending = state.pending;
416
+ if (!pending) {
417
+ return;
418
+ }
419
+ const focused = getFocusedTarget();
420
+ const activeView = catalog.getActiveCommandView(focused);
421
+ const captures = pending.captures.filter((capture) => {
422
+ return state.layers.has(capture.layer) && isActiveLayerForFocused(capture.layer, focused) && conditions.matchesConditions(capture.layer) && bindingMatchesRuntimeState(capture.binding, focused, activeView) && captureHasContinuations(capture, state.patterns);
423
+ });
424
+ if (captures.length === 0) {
425
+ setPendingSequence(null);
426
+ return;
427
+ }
428
+ if (captures.length !== pending.captures.length) {
429
+ setPendingSequence({ captures });
430
+ }
431
+ return state.pending ?? undefined;
432
+ };
433
+ const revalidatePendingSequenceIfNeeded = () => {
434
+ if (host.isDestroyed || !state.pending) {
435
+ return;
436
+ }
437
+ ensureValidPendingSequence();
438
+ };
439
+ const hasPendingSequenceState = () => {
440
+ return !host.isDestroyed && state.pending !== null;
441
+ };
442
+ const getPendingSequence = () => {
443
+ if (pendingSequenceCacheVersion === state.cacheVersion) {
444
+ return pendingSequenceCache;
445
+ }
446
+ const pending = ensureValidPendingSequence();
447
+ const sequence = pending ? collectSequencePartsFromPending(pending) : [];
448
+ if (!pending || allRegisteredLayersCanCacheActiveKeys()) {
449
+ pendingSequenceCacheVersion = state.cacheVersion;
450
+ pendingSequenceCache = sequence;
451
+ }
452
+ return sequence;
453
+ };
454
+ const popPendingSequence = () => {
455
+ const pending = ensureValidPendingSequence();
456
+ if (!pending) {
457
+ return false;
458
+ }
459
+ const firstCapture = pending.captures[0];
460
+ if (!firstCapture || firstCapture.parts.length <= 1) {
461
+ setPendingSequence(null);
462
+ return true;
463
+ }
464
+ const nextCaptures = [];
465
+ for (const capture of pending.captures) {
466
+ const nextCapture = popCapture(capture);
467
+ if (!nextCapture) {
468
+ continue;
469
+ }
470
+ nextCaptures.push(nextCapture);
471
+ }
472
+ if (nextCaptures.length === 0) {
473
+ setPendingSequence(null);
474
+ return true;
475
+ }
476
+ setPendingSequence({ captures: nextCaptures });
477
+ return true;
478
+ };
479
+ const getActiveKeys = (options2) => {
480
+ const includeBindings = options2?.includeBindings === true;
481
+ const includeMetadata = options2?.includeMetadata === true;
482
+ const cache = getActiveKeysCache(activeKeysCaches, options2);
483
+ if (cache.notifyVersion === state.derivedVersion) {
484
+ return cache.value;
485
+ }
486
+ return collectActiveKeysForCache(cache, includeBindings, includeMetadata);
487
+ };
488
+ const collectActiveKeysForCache = (cache, includeBindings, includeMetadata) => {
489
+ if (host.isDestroyed) {
490
+ getLiveHost(host);
491
+ }
492
+ const focused = getFocusedTarget();
493
+ const cached = getFocusedActiveKeysCache(cache, state.cacheVersion, focused);
494
+ if (cached) {
495
+ cache.notifyVersion = state.derivedVersion;
496
+ cache.version = state.cacheVersion;
497
+ cache.focused = focused;
498
+ cache.value = cached.value;
499
+ return cached.value;
500
+ }
501
+ const activeView = catalog.getActiveCommandView(focused);
502
+ const pending = ensureValidPendingSequence();
503
+ const activeLayers = pending ? [] : getActiveLayers(focused);
504
+ const activeKeys = pending ? collectActiveKeysFromPending(pending.captures, includeBindings, includeMetadata, focused, activeView) : collectActiveKeysAtRoot(activeLayers, includeBindings, includeMetadata, focused, activeView);
505
+ const canUseCache = pending ? allRegisteredLayersCanCacheActiveKeys() : activeLayersCanCacheActiveKeys(activeLayers);
506
+ if (canUseCache) {
507
+ cache.version = state.cacheVersion;
508
+ cache.notifyVersion = state.derivedVersion;
509
+ cache.focused = focused;
510
+ cache.value = activeKeys;
511
+ setFocusedActiveKeysCache(cache, state.cacheVersion, focused, activeKeys);
512
+ }
513
+ return activeKeys;
514
+ };
515
+ const getActiveKeysForCaptures = (captures, options2) => {
516
+ const includeBindings = options2?.includeBindings === true;
517
+ const includeMetadata = options2?.includeMetadata === true;
518
+ const focused = getFocusedTarget();
519
+ const activeView = catalog.getActiveCommandView(focused);
520
+ return collectActiveKeysFromPending(captures, includeBindings, includeMetadata, focused, activeView);
521
+ };
522
+ const getActiveKeysForFocused = (focused, options2) => {
523
+ const includeBindings = options2?.includeBindings === true;
524
+ const includeMetadata = options2?.includeMetadata === true;
525
+ const currentFocused = getFocusedTargetIfAvailable2();
526
+ const pending = focused === currentFocused ? ensureValidPendingSequence() : undefined;
527
+ const activeView = catalog.getActiveCommandView(focused);
528
+ if (pending) {
529
+ return collectActiveKeysFromPending(pending.captures, includeBindings, includeMetadata, focused, activeView);
530
+ }
531
+ return collectActiveKeysAtRoot(getActiveLayers(focused), includeBindings, includeMetadata, focused, activeView);
532
+ };
533
+ const getActiveLayers = (focused) => {
534
+ if (state.activeLayersCacheVersion === state.cacheVersion && state.activeLayersCacheFocused === focused) {
535
+ return state.activeLayersCache;
536
+ }
537
+ state.activeLayersCacheVersion = state.cacheVersion;
538
+ state.activeLayersCacheFocused = focused;
539
+ state.activeLayersCache = getActiveLayersForFocused(state.sortedLayers, host, focused);
540
+ return state.activeLayersCache;
541
+ };
542
+ const isActiveLayerForFocused = (layer, focused) => {
543
+ return isLayerActiveForFocused(host, layer, focused);
544
+ };
545
+ const activeLayersCanCacheActiveKeys = (activeLayers) => {
546
+ return !state.commandResolvers.has() && state.activeKeyCacheBlockers === 0;
547
+ };
548
+ const allRegisteredLayersCanCacheActiveKeys = () => {
549
+ return !state.commandResolvers.has() && state.activeKeyCacheBlockers === 0;
550
+ };
551
+ const collectMatchingBindings = (bindings, focused, activeView) => {
552
+ const matches = [];
553
+ for (const binding of bindings) {
554
+ if (conditions.matchesConditions(binding) && catalog.isBindingVisible(binding, focused, activeView)) {
555
+ matches.push(binding);
556
+ }
557
+ }
558
+ return matches;
559
+ };
560
+ const bindingMatchesRuntimeState = (binding, focused, activeView) => {
561
+ return conditions.matchesConditions(binding) && catalog.isBindingVisible(binding, focused, activeView);
562
+ };
563
+ const getPartPresentation = (bindings, partIndex) => {
564
+ let display;
565
+ let tokenName;
566
+ let hasDisplayConflict = false;
567
+ let hasTokenConflict = false;
568
+ for (const binding of bindings) {
569
+ const part = binding.sequence[partIndex];
570
+ if (!part) {
571
+ continue;
572
+ }
573
+ if (display === undefined) {
574
+ display = part.display;
575
+ tokenName = part.tokenName;
576
+ continue;
577
+ }
578
+ if (!hasDisplayConflict && display !== part.display) {
579
+ hasDisplayConflict = true;
580
+ }
581
+ if (!hasTokenConflict && tokenName !== part.tokenName) {
582
+ hasTokenConflict = true;
583
+ }
584
+ }
585
+ if (display === undefined || hasDisplayConflict) {
586
+ const stroke = bindings[0]?.sequence[partIndex]?.stroke;
587
+ display = stroke ? stringifyKeyStroke(stroke) : "";
588
+ }
589
+ if (hasTokenConflict) {
590
+ tokenName = undefined;
591
+ }
592
+ return {
593
+ display,
594
+ tokenName
595
+ };
596
+ };
597
+ const toActiveBinding = (binding, focused, activeView) => {
598
+ return {
599
+ sequence: binding.sequence,
600
+ command: binding.command,
601
+ commandAttrs: catalog.getBindingCommandAttrs(binding, focused, activeView),
602
+ attrs: binding.attrs,
603
+ event: binding.event,
604
+ preventDefault: binding.preventDefault,
605
+ fallthrough: binding.fallthrough
606
+ };
607
+ };
608
+ const collectActiveBindings = (bindings, focused, activeView) => {
609
+ return bindings.map((binding) => toActiveBinding(binding, focused, activeView));
610
+ };
611
+ const collectActiveKeysAtRoot = (activeLayers, includeBindings, includeMetadata, focused, activeView) => {
612
+ const activeKeys = new Map;
613
+ const stopped = new Set;
614
+ for (const layer of activeLayers) {
615
+ if (layer.root.children.size === 0 && layer.root.patternChildren.length === 0 || !conditions.matchesConditions(layer)) {
616
+ continue;
617
+ }
618
+ collectActiveKeyNodes(layer.root.children.values(), activeKeys, stopped, includeBindings, focused, activeView);
619
+ collectActiveKeyNodes(layer.root.patternChildren, activeKeys, stopped, includeBindings, focused, activeView);
620
+ }
621
+ return materializeActiveKeys(activeKeys, includeBindings, includeMetadata, focused, activeView);
622
+ };
623
+ const collectActiveKeyNodes = (nodes, activeKeys, stopped, includeBindings, focused, activeView) => {
624
+ for (const node of nodes) {
625
+ const bindingKey = node.match;
626
+ if (!bindingKey || stopped.has(bindingKey) || !node.stroke) {
627
+ continue;
628
+ }
629
+ const selection = selectActiveKeyNode(node, includeBindings, focused, activeView);
630
+ if (!selection) {
631
+ continue;
632
+ }
633
+ const existing = activeKeys.get(bindingKey);
634
+ if (!existing) {
635
+ activeKeys.set(bindingKey, createActiveKeyState(node.stroke, selection, includeBindings));
636
+ } else {
637
+ updateActiveKeyState(existing, selection, includeBindings);
638
+ }
639
+ if (selection.stop) {
640
+ stopped.add(bindingKey);
641
+ }
642
+ }
643
+ };
644
+ const selectActiveKeyNode = (node, includeBindings, focused, activeView) => {
645
+ const continues = node.children.size > 0 || node.patternChildren.length > 0;
646
+ if (!continues) {
647
+ const selected2 = selectActiveBindings(nodeExactBindingsNeedPrefilter(node) ? collectMatchingBindings(node.bindings, focused, activeView) : node.bindings, focused, activeView);
648
+ if (!selected2) {
649
+ return;
650
+ }
651
+ const presentation2 = getPartPresentation(selected2.bindings, node.depth - 1);
652
+ return {
653
+ display: presentation2.display,
654
+ tokenName: presentation2.tokenName,
655
+ continues: false,
656
+ firstBinding: selected2.bindings[0],
657
+ commandBinding: selected2.commandBinding,
658
+ bindings: includeBindings ? [...selected2.bindings] : undefined,
659
+ stop: selected2.stop
660
+ };
661
+ }
662
+ const reachableBindings = collectMatchingBindings(node.reachableBindings, focused, activeView);
663
+ if (reachableBindings.length === 0) {
664
+ return;
665
+ }
666
+ const selected = selectActiveBindings(node.bindings, focused, activeView);
667
+ const presentation = getPartPresentation(reachableBindings, node.depth - 1);
668
+ return {
669
+ display: presentation.display,
670
+ tokenName: presentation.tokenName,
671
+ continues: true,
672
+ firstBinding: selected?.bindings[0],
673
+ commandBinding: selected?.commandBinding,
674
+ bindings: includeBindings && selected ? [...selected.bindings] : undefined,
675
+ stop: true
676
+ };
677
+ };
678
+ const nodeExactBindingsNeedPrefilter = (node) => {
679
+ if (state.commandResolvers.has()) {
680
+ return true;
681
+ }
682
+ for (const binding of node.bindings) {
683
+ if (binding.matchers.length > 0) {
684
+ return true;
685
+ }
686
+ }
687
+ return false;
688
+ };
689
+ const collectActiveKeysFromPending = (captures, includeBindings, includeMetadata, focused, activeView) => {
690
+ const activeKeys = new Map;
691
+ const stopped = new Set;
692
+ collectActiveKeyOptions(activeOptionsForCaptures(captures, state.patterns), activeKeys, stopped, includeBindings, focused, activeView);
693
+ return materializeActiveKeys(activeKeys, includeBindings, includeMetadata, focused, activeView);
694
+ };
695
+ const collectActiveKeyOptions = (options2, activeKeys, stopped, includeBindings, focused, activeView) => {
696
+ const seen = new Set;
697
+ for (const option of options2) {
698
+ if (seen.has(option.part.match)) {
699
+ continue;
700
+ }
701
+ seen.add(option.part.match);
702
+ collectActiveKeyOption(option, options2, activeKeys, stopped, includeBindings, focused, activeView);
703
+ }
704
+ };
705
+ const collectActiveKeyOption = (option, siblingOptions, activeKeys, stopped, includeBindings, focused, activeView) => {
706
+ const bindingKey = option.part.match;
707
+ if (stopped.has(bindingKey)) {
708
+ return;
709
+ }
710
+ const selection = selectActiveKeyOption(option, siblingOptions, includeBindings, focused, activeView);
711
+ if (!selection) {
712
+ return;
713
+ }
714
+ const existing = activeKeys.get(bindingKey);
715
+ if (!existing) {
716
+ activeKeys.set(bindingKey, createActiveKeyState(option.part.stroke, selection, includeBindings));
717
+ } else {
718
+ updateActiveKeyState(existing, selection, includeBindings);
719
+ }
720
+ if (selection.stop) {
721
+ stopped.add(bindingKey);
722
+ }
723
+ };
724
+ const materializeActiveKeys = (activeKeys, includeBindings, includeMetadata, focused, activeView) => {
725
+ const materialized = [];
726
+ for (const state2 of activeKeys.values()) {
727
+ const activeKey = materializeActiveKey(state2, includeBindings, includeMetadata, focused, activeView);
728
+ if (activeKey) {
729
+ materialized.push(activeKey);
730
+ }
731
+ }
732
+ return materialized;
733
+ };
734
+ const selectActiveKeyOption = (option, siblingOptions, includeBindings, focused, activeView) => {
735
+ const matchingOptions = siblingOptions.filter((candidate) => candidate.part.match === option.part.match);
736
+ const exactBindings = matchingOptions.filter((candidate) => candidate.exact).map((candidate) => candidate.binding);
737
+ const selected = selectActiveBindings(exactBindings, focused, activeView);
738
+ const continues = matchingOptions.some((candidate) => candidate.continues);
739
+ if (!continues && !selected) {
740
+ return;
741
+ }
742
+ const presentation = getOptionPresentation(matchingOptions);
743
+ return {
744
+ display: presentation.display,
745
+ tokenName: presentation.tokenName,
746
+ continues,
747
+ firstBinding: selected?.bindings[0],
748
+ commandBinding: selected?.commandBinding,
749
+ bindings: includeBindings && selected ? [...selected.bindings] : undefined,
750
+ stop: continues || selected?.stop === true
751
+ };
752
+ };
753
+ const getOptionPresentation = (options2) => {
754
+ let display;
755
+ let tokenName;
756
+ let hasDisplayConflict = false;
757
+ let hasTokenConflict = false;
758
+ for (const option of options2) {
759
+ const part = option.part;
760
+ if (display === undefined) {
761
+ display = part.display;
762
+ tokenName = part.tokenName;
763
+ continue;
764
+ }
765
+ if (!hasDisplayConflict && display !== part.display) {
766
+ hasDisplayConflict = true;
767
+ }
768
+ if (!hasTokenConflict && tokenName !== part.tokenName) {
769
+ hasTokenConflict = true;
770
+ }
771
+ }
772
+ const firstPart = options2[0]?.part;
773
+ if (display === undefined || hasDisplayConflict) {
774
+ display = firstPart ? stringifyKeyStroke(firstPart.stroke) : "";
775
+ }
776
+ if (hasTokenConflict) {
777
+ tokenName = undefined;
778
+ }
779
+ return { display, tokenName };
780
+ };
781
+ const selectActiveBindings = (bindings, focused, activeView) => {
782
+ const selected = [];
783
+ let commandBinding;
784
+ for (const binding of bindings) {
785
+ if (!conditions.matchesConditions(binding) || !catalog.isBindingVisible(binding, focused, activeView)) {
786
+ continue;
787
+ }
788
+ selected.push(binding);
789
+ if (binding.command === undefined) {
790
+ continue;
791
+ }
792
+ commandBinding ??= binding;
793
+ if (!binding.fallthrough) {
794
+ return { bindings: selected, commandBinding, stop: true };
795
+ }
796
+ }
797
+ if (selected.length === 0) {
798
+ return;
799
+ }
800
+ return { bindings: selected, commandBinding, stop: false };
801
+ };
802
+ const createActiveKeyState = (stroke, selection, includeBindings) => {
803
+ return {
804
+ stroke,
805
+ display: selection.display,
806
+ tokenName: selection.tokenName,
807
+ continues: selection.continues,
808
+ firstBinding: selection.firstBinding,
809
+ commandBinding: selection.commandBinding,
810
+ bindings: includeBindings && selection.bindings ? [...selection.bindings] : undefined
811
+ };
812
+ };
813
+ const updateActiveKeyState = (state2, selection, includeBindings) => {
814
+ if (!state2.firstBinding && selection.firstBinding) {
815
+ state2.firstBinding = selection.firstBinding;
816
+ }
817
+ if (!state2.commandBinding && selection.commandBinding) {
818
+ state2.commandBinding = selection.commandBinding;
819
+ }
820
+ if (selection.continues) {
821
+ state2.continues = true;
822
+ }
823
+ if (!includeBindings || !selection.bindings || selection.bindings.length === 0) {
824
+ return;
825
+ }
826
+ if (!state2.bindings) {
827
+ state2.bindings = [...selection.bindings];
828
+ return;
829
+ }
830
+ state2.bindings.push(...selection.bindings);
831
+ };
832
+ const materializeActiveKey = (state2, includeBindings, includeMetadata, focused, activeView) => {
833
+ if (!state2.commandBinding && !state2.continues) {
834
+ return;
835
+ }
836
+ const activeKey = {
837
+ stroke: cloneKeyStroke(state2.stroke),
838
+ display: state2.display,
839
+ continues: state2.continues
840
+ };
841
+ if (state2.tokenName) {
842
+ activeKey.tokenName = state2.tokenName;
843
+ }
844
+ if (state2.commandBinding) {
845
+ activeKey.command = state2.commandBinding.command;
846
+ }
847
+ if (includeBindings && state2.bindings && state2.bindings.length > 0) {
848
+ activeKey.bindings = state2.bindings.length === 1 ? [toActiveBinding(state2.bindings[0], focused, activeView)] : collectActiveBindings(state2.bindings, focused, activeView);
849
+ }
850
+ if (includeMetadata) {
851
+ if (state2.firstBinding?.attrs) {
852
+ activeKey.bindingAttrs = state2.firstBinding.attrs;
853
+ }
854
+ const commandAttrs = state2.commandBinding ? catalog.getBindingCommandAttrs(state2.commandBinding, focused, activeView) : undefined;
855
+ if (commandAttrs) {
856
+ activeKey.commandAttrs = commandAttrs;
857
+ }
858
+ }
859
+ return activeKey;
860
+ };
861
+ const notifyPendingSequenceChange = () => {
862
+ if (!hooks.has("pendingSequence")) {
863
+ return;
864
+ }
865
+ hooks.emit("pendingSequence", state.pending ? collectSequencePartsFromPending(state.pending) : []);
866
+ };
867
+ return {
868
+ getFocusedTarget,
869
+ getFocusedTargetIfAvailable: getFocusedTargetIfAvailable2,
870
+ setPendingSequence,
871
+ ensureValidPendingSequence,
872
+ revalidatePendingSequenceIfNeeded,
873
+ hasPendingSequenceState,
874
+ getPendingSequence,
875
+ popPendingSequence,
876
+ getActiveKeys,
877
+ getActiveKeysForCaptures,
878
+ getActiveKeysForFocused,
879
+ getActiveLayers,
880
+ isLayerActiveForFocused: isActiveLayerForFocused,
881
+ collectSequencePartsFromPending,
882
+ collectMatchingBindings,
883
+ collectActiveBindings
884
+ };
885
+ }
886
+
887
+ // src/schema.ts
888
+ var RESERVED_COMMAND_FIELDS = new Set(["name", "run", "fields", "attrs", "rejectedResult"]);
889
+ var RESERVED_BINDING_FIELDS = new Set(["key", "cmd", "event", "preventDefault", "fallthrough"]);
890
+ var RESERVED_LAYER_FIELDS = new Set(["target", "targetMode", "priority", "bindings", "commands"]);
891
+
892
+ // src/services/primitives/command-normalization.ts
893
+ function normalizeBindingCommand(command) {
894
+ if (command === undefined || typeof command === "function") {
895
+ return command;
896
+ }
897
+ const trimmed = command.trim();
898
+ if (!trimmed) {
899
+ throw new Error("Invalid keymap command: command cannot be empty");
900
+ }
901
+ return trimmed;
902
+ }
903
+ function normalizeCommandName(name) {
904
+ const trimmed = name.trim();
905
+ if (!trimmed) {
906
+ throw new Error("Invalid keymap command name: name cannot be empty");
907
+ }
908
+ if (/\s/.test(trimmed)) {
909
+ throw new Error(`Invalid keymap command name "${name}": command names cannot contain whitespace`);
910
+ }
911
+ return trimmed;
912
+ }
913
+
914
+ // src/services/runtime-view.ts
915
+ function pushCommandEntry(target, name, entry) {
916
+ const existing = target.get(name);
917
+ if (existing)
918
+ existing.push(entry);
919
+ else
920
+ target.set(name, [entry]);
921
+ }
922
+ function getRegisteredCommandView(state) {
923
+ const entries = [];
924
+ const chainsByName = new Map;
925
+ for (const layer of state.layers) {
926
+ for (const commandState of layer.commands) {
927
+ const entry = { layer, commandState };
928
+ entries.push(entry);
929
+ pushCommandEntry(chainsByName, commandState.command.name, entry);
930
+ }
931
+ }
932
+ return { entries, chainsByName };
933
+ }
934
+ function collectActiveCommands(layers, conditions, checkLayerConditions) {
935
+ const entries = [];
936
+ const reachable = [];
937
+ const reachableByName = new Map;
938
+ const chainsByName = new Map;
939
+ for (const layer of layers) {
940
+ if (layer.commands.length === 0)
941
+ continue;
942
+ if (checkLayerConditions && !conditions.matchesConditions(layer))
943
+ continue;
944
+ for (const commandState of layer.commands) {
945
+ if (!conditions.matchesConditions(commandState))
946
+ continue;
947
+ const entry = { layer, commandState };
948
+ entries.push(entry);
949
+ pushCommandEntry(chainsByName, commandState.command.name, entry);
950
+ if (!reachableByName.has(commandState.command.name)) {
951
+ reachableByName.set(commandState.command.name, entry);
952
+ reachable.push(entry);
953
+ }
954
+ }
955
+ }
956
+ return { layers, entries, reachable, reachableByName, chainsByName };
957
+ }
958
+ function getActiveCommandView(state, host, conditions, focused) {
959
+ if (state.activeLayersCacheVersion !== state.cacheVersion || state.activeLayersCacheFocused !== focused) {
960
+ state.activeLayersCacheVersion = state.cacheVersion;
961
+ state.activeLayersCacheFocused = focused;
962
+ state.activeLayersCache = getActiveLayersForFocused(state.sortedLayers, host, focused);
963
+ }
964
+ return collectActiveCommands(state.activeLayersCache, conditions, true);
965
+ }
966
+
967
+ // src/services/primitives/field-invariants.ts
968
+ function mergeRequirement(target, name, value, source) {
969
+ if (Object.prototype.hasOwnProperty.call(target, name) && !Object.is(target[name], value)) {
970
+ throw new Error(`Conflicting keymap requirement for "${name}" from ${source}`);
971
+ }
972
+ target[name] = value;
973
+ }
974
+ function mergeAttribute(target, name, value, source) {
975
+ if (Object.prototype.hasOwnProperty.call(target, name) && !Object.is(target[name], value)) {
976
+ throw new Error(`Conflicting keymap attribute for "${name}" from ${source}`);
977
+ }
978
+ target[name] = value;
979
+ }
980
+ function createFieldCompilerContext(options) {
981
+ const source = `field ${options.fieldName}`;
982
+ return {
983
+ require(name, value) {
984
+ mergeRequirement(options.requirements, name, value, source);
985
+ },
986
+ attr(name, value) {
987
+ if (!options.attrs) {
988
+ throw new Error(`Keymap ${source} cannot publish attrs`);
989
+ }
990
+ mergeAttribute(options.attrs, name, value, source);
991
+ },
992
+ activeWhen(matcher) {
993
+ options.matchers.push(options.conditions.buildRuntimeMatcher(matcher, source));
994
+ }
995
+ };
996
+ }
997
+
998
+ // src/services/values.ts
999
+ function isPlainObject(value) {
1000
+ const prototype = Object.getPrototypeOf(value);
1001
+ return prototype === Object.prototype || prototype === null;
1002
+ }
1003
+ function getErrorMessage(error, fallback) {
1004
+ if (error instanceof Error && error.message) {
1005
+ return error.message;
1006
+ }
1007
+ return fallback;
1008
+ }
1009
+ function isPromiseLike(value) {
1010
+ if (!value) {
1011
+ return false;
1012
+ }
1013
+ if (typeof value !== "object" && typeof value !== "function") {
1014
+ return false;
1015
+ }
1016
+ return typeof value.then === "function";
1017
+ }
1018
+ function snapshotDataValue(value, options) {
1019
+ const deep = options?.deep === true;
1020
+ const freeze = options?.freeze === true;
1021
+ const preserveNonPlainObjects = options?.preserveNonPlainObjects === true;
1022
+ if (Array.isArray(value)) {
1023
+ const cloned = deep ? value.map((entry) => snapshotDataValue(entry, options)) : [...value];
1024
+ return freeze ? Object.freeze(cloned) : cloned;
1025
+ }
1026
+ if (value && typeof value === "object") {
1027
+ if (preserveNonPlainObjects && !isPlainObject(value)) {
1028
+ return value;
1029
+ }
1030
+ const cloned = {};
1031
+ for (const [key, entry] of Object.entries(value)) {
1032
+ cloned[key] = deep ? snapshotDataValue(entry, options) : entry;
1033
+ }
1034
+ return freeze ? Object.freeze(cloned) : cloned;
1035
+ }
1036
+ return value;
1037
+ }
1038
+
1039
+ // src/services/command-catalog.ts
1040
+ var DEFAULT_COMMAND_SEARCH_FIELDS = ["name"];
1041
+ var EMPTY_COMMAND_FIELDS = Object.freeze({});
1042
+ var commandSearchCache = new WeakMap;
1043
+ function createCommandCatalogService(state, host, notify, conditions, options) {
1044
+ let registeredViewVersion = -1;
1045
+ let registeredView;
1046
+ let activeViewVersion = -1;
1047
+ let activeViewFocused;
1048
+ let activeView;
1049
+ let registeredBindingsCacheVersion = -1;
1050
+ let registeredBindingsCacheCommands;
1051
+ let registeredBindingsCache;
1052
+ let registeredBindingsByCommandVersion = -1;
1053
+ let registeredBindingsByCommand;
1054
+ let registeredResolvedCacheVersion = -1;
1055
+ let registeredResolvedCache = new Map;
1056
+ const normalizeLayerCommands = (commands) => {
1057
+ return normalizeCommands({
1058
+ commands,
1059
+ commandFields: state.commandFields,
1060
+ conditions,
1061
+ onError: (code, error, message) => {
1062
+ notify.emitError(code, error, message);
1063
+ }
1064
+ });
1065
+ };
1066
+ const prependCommandResolver = (resolver) => {
1067
+ return mutateCommandResolvers(() => state.commandResolvers.prepend(resolver), resolver);
1068
+ };
1069
+ const appendCommandResolver = (resolver) => {
1070
+ return mutateCommandResolvers(() => state.commandResolvers.append(resolver), resolver);
1071
+ };
1072
+ const clearCommandResolvers = () => {
1073
+ if (!state.commandResolvers.has()) {
1074
+ return;
1075
+ }
1076
+ notify.runWithStateChangeBatch(() => {
1077
+ state.commandResolvers.clear();
1078
+ options.onCommandResolversChanged();
1079
+ notify.queueStateChange();
1080
+ });
1081
+ };
1082
+ const getCommands = (query) => {
1083
+ return getFilteredCommandEntries(query).map((entry) => getCommand(entry.commandState));
1084
+ };
1085
+ const getCommandEntries = (query) => {
1086
+ const context = getCommandQueryContext(query);
1087
+ const filteredEntries = getFilteredCommandEntries(query, context);
1088
+ if (filteredEntries.length === 0) {
1089
+ return [];
1090
+ }
1091
+ const grouped = filteredEntries.map((entry) => ({
1092
+ entry,
1093
+ command: getCommand(entry.commandState),
1094
+ commandAttrs: entry.commandState.attrs,
1095
+ bindings: []
1096
+ }));
1097
+ const indexesByName = new Map;
1098
+ for (const [index, item] of grouped.entries()) {
1099
+ const existing = indexesByName.get(item.command.name);
1100
+ if (existing) {
1101
+ existing.push(index);
1102
+ } else {
1103
+ indexesByName.set(item.command.name, [index]);
1104
+ }
1105
+ }
1106
+ if (indexesByName.size > 0) {
1107
+ collectCommandEntryBindings(grouped, indexesByName, context);
1108
+ }
1109
+ return grouped.map((item) => ({
1110
+ command: item.command,
1111
+ bindings: item.bindings
1112
+ }));
1113
+ };
1114
+ const getCommandBindings = (query) => {
1115
+ if (query.visibility === "registered" && registeredBindingsCacheVersion === state.derivedVersion && registeredBindingsCacheCommands === query.commands && registeredBindingsCache) {
1116
+ return registeredBindingsCache;
1117
+ }
1118
+ const bindingsByCommand = new Map;
1119
+ for (const command of query.commands) {
1120
+ if (!bindingsByCommand.has(command)) {
1121
+ bindingsByCommand.set(command, []);
1122
+ }
1123
+ }
1124
+ if (bindingsByCommand.size === 0) {
1125
+ return bindingsByCommand;
1126
+ }
1127
+ collectCommandBindings(bindingsByCommand, getCommandQueryContext(query));
1128
+ if (query.visibility === "registered") {
1129
+ registeredBindingsCacheVersion = state.derivedVersion;
1130
+ registeredBindingsCacheCommands = query.commands;
1131
+ registeredBindingsCache = bindingsByCommand;
1132
+ }
1133
+ return bindingsByCommand;
1134
+ };
1135
+ const getResolvedCommandChain = (command, focused, execution) => {
1136
+ const view = getActiveCommandView2(focused);
1137
+ if (execution) {
1138
+ const resolved2 = [];
1139
+ const chain = view.chainsByName.get(command);
1140
+ if (chain) {
1141
+ for (const entry of chain) {
1142
+ resolved2.push({
1143
+ target: entry.layer.target,
1144
+ command: entry.commandState.command,
1145
+ attrs: entry.commandState.attrs,
1146
+ payload: execution.payload
1147
+ });
1148
+ }
1149
+ }
1150
+ const fallback2 = resolveCommandWithResolversForMode(command, focused, { mode: "active", execution });
1151
+ if (fallback2.resolved) {
1152
+ resolved2.push(fallback2.resolved);
1153
+ }
1154
+ return { entries: resolved2.length > 0 ? resolved2 : undefined, hadError: fallback2.hadError };
1155
+ }
1156
+ const resolved = [];
1157
+ for (const entry of view.chainsByName.get(command) ?? []) {
1158
+ resolved.push({
1159
+ target: entry.layer.target,
1160
+ command: entry.commandState.command,
1161
+ attrs: entry.commandState.attrs
1162
+ });
1163
+ }
1164
+ const fallback = resolveCommandWithResolversForMode(command, focused, { mode: "active" });
1165
+ if (fallback.resolved) {
1166
+ resolved.push(fallback.resolved);
1167
+ }
1168
+ return { entries: resolved.length > 0 ? resolved : undefined, hadError: fallback.hadError };
1169
+ };
1170
+ const getRegisteredResolvedEntries = (command) => {
1171
+ if (registeredResolvedCacheVersion !== state.derivedVersion) {
1172
+ registeredResolvedCacheVersion = state.derivedVersion;
1173
+ registeredResolvedCache = new Map;
1174
+ }
1175
+ if (registeredResolvedCache.has(command)) {
1176
+ return registeredResolvedCache.get(command) ?? undefined;
1177
+ }
1178
+ const resolved = resolveRegisteredEntries(getRegisteredCommandChain(command));
1179
+ registeredResolvedCache.set(command, resolved ?? null);
1180
+ return resolved;
1181
+ };
1182
+ const getActiveRegisteredResolvedEntries = (command, focused) => {
1183
+ return resolveRegisteredEntries(getActiveCommandView2(focused).chainsByName.get(command));
1184
+ };
1185
+ const resolveRegisteredResolverFallback = (command, execution) => {
1186
+ return resolveCommandWithResolversForMode(command, null, { mode: "registered", execution });
1187
+ };
1188
+ const resolveActiveResolverFallback = (command, focused, execution) => {
1189
+ return resolveCommandWithResolversForMode(command, focused, { mode: "active", execution });
1190
+ };
1191
+ const getTopCommand = (command, focused) => {
1192
+ const top = getTopResolvedCommand(command, focused);
1193
+ return top?.command;
1194
+ };
1195
+ const getCommandByName = (command) => {
1196
+ const top = getCommandEntry(command);
1197
+ return top?.commandState.command;
1198
+ };
1199
+ const getDispatchUnavailableCommandState = (command, focused, includeCommand) => {
1200
+ const view = getCommandView();
1201
+ const chain = view.chainsByName.get(command);
1202
+ if (!chain || chain.length === 0) {
1203
+ return;
1204
+ }
1205
+ let inactiveEntry;
1206
+ let disabledEntry;
1207
+ for (const entry of chain) {
1208
+ if (!isLayerActiveForFocused(host, entry.layer, focused)) {
1209
+ inactiveEntry ??= entry;
1210
+ continue;
1211
+ }
1212
+ if (!conditions.matchesConditions(entry.layer) || !conditions.matchesConditions(entry.commandState)) {
1213
+ disabledEntry ??= entry;
1214
+ }
1215
+ }
1216
+ const unavailableEntry = disabledEntry ?? inactiveEntry;
1217
+ if (!unavailableEntry) {
1218
+ return;
1219
+ }
1220
+ return {
1221
+ reason: disabledEntry ? "disabled" : "inactive",
1222
+ command: includeCommand ? unavailableEntry.commandState.command : undefined
1223
+ };
1224
+ };
1225
+ const getActiveCommandView2 = (focused) => {
1226
+ if (activeViewVersion === state.derivedVersion && activeViewFocused === focused && activeView) {
1227
+ return activeView;
1228
+ }
1229
+ const view = getActiveCommandView(state, host, conditions, focused);
1230
+ if (activeCommandViewCanCache()) {
1231
+ activeViewVersion = state.derivedVersion;
1232
+ activeViewFocused = focused;
1233
+ activeView = view;
1234
+ }
1235
+ return view;
1236
+ };
1237
+ const getCommandView = () => {
1238
+ if (registeredViewVersion === state.derivedVersion && registeredView) {
1239
+ return registeredView;
1240
+ }
1241
+ registeredViewVersion = state.derivedVersion;
1242
+ registeredView = getRegisteredCommandView(state);
1243
+ return registeredView;
1244
+ };
1245
+ const activeCommandViewCanCache = () => {
1246
+ return state.activeCommandViewCacheBlockers === 0;
1247
+ };
1248
+ const isBindingVisible = (binding, focused, activeView2) => {
1249
+ if (binding.command === undefined || binding.run) {
1250
+ return true;
1251
+ }
1252
+ if (typeof binding.command !== "string") {
1253
+ return false;
1254
+ }
1255
+ if (activeView2.reachableByName.has(binding.command)) {
1256
+ return true;
1257
+ }
1258
+ return getFallbackResolvedCommand(binding.command, focused, "active") !== undefined;
1259
+ };
1260
+ const getBindingCommandAttrs = (binding, focused, activeView2) => {
1261
+ if (typeof binding.command !== "string") {
1262
+ return;
1263
+ }
1264
+ const active = activeView2.reachableByName.get(binding.command);
1265
+ if (active) {
1266
+ return active.commandState.attrs;
1267
+ }
1268
+ const fallback = getFallbackResolvedCommand(binding.command, focused, "active");
1269
+ return fallback?.attrs;
1270
+ };
1271
+ const getCommandResolutionStatus = (command, layerCommands) => {
1272
+ if (layerCommands?.some((state2) => state2.command.name === command) || getCommandView().chainsByName.has(command)) {
1273
+ return "resolved";
1274
+ }
1275
+ const lookup = resolveCommandWithResolversForMode(command, getFocusedTargetIfAvailable(host));
1276
+ if (lookup.resolved || lookup.hadError) {
1277
+ return lookup.resolved ? "resolved" : "error";
1278
+ }
1279
+ return "unresolved";
1280
+ };
1281
+ const mutateCommandResolvers = (register, resolver) => {
1282
+ return notify.runWithStateChangeBatch(() => {
1283
+ const off = register();
1284
+ options.onCommandResolversChanged();
1285
+ notify.queueStateChange();
1286
+ return () => {
1287
+ notify.runWithStateChangeBatch(() => {
1288
+ off();
1289
+ if (state.commandResolvers.values().includes(resolver)) {
1290
+ return;
1291
+ }
1292
+ options.onCommandResolversChanged();
1293
+ notify.queueStateChange();
1294
+ });
1295
+ };
1296
+ });
1297
+ };
1298
+ const getTopResolvedCommand = (command, focused) => {
1299
+ const activeView2 = getActiveCommandView2(focused);
1300
+ const active = activeView2.reachableByName.get(command);
1301
+ if (active) {
1302
+ return {
1303
+ target: active.layer.target,
1304
+ command: active.commandState.command,
1305
+ attrs: active.commandState.attrs
1306
+ };
1307
+ }
1308
+ return getFallbackResolvedCommand(command, focused, "active");
1309
+ };
1310
+ const getCommandEntry = (command) => {
1311
+ return getRegisteredCommandChain(command)?.[0];
1312
+ };
1313
+ const getRegisteredCommandChain = (command) => {
1314
+ const entries = [];
1315
+ for (const layer of state.sortedLayers) {
1316
+ for (const commandState of layer.commands) {
1317
+ if (commandState.command.name === command) {
1318
+ entries.push({ layer, commandState });
1319
+ }
1320
+ }
1321
+ }
1322
+ return entries.length > 0 ? entries : undefined;
1323
+ };
1324
+ const resolveRegisteredEntries = (chain) => {
1325
+ if (!chain?.length) {
1326
+ return;
1327
+ }
1328
+ return chain.map((entry) => ({
1329
+ target: entry.layer.target,
1330
+ command: entry.commandState.command,
1331
+ attrs: entry.commandState.attrs
1332
+ }));
1333
+ };
1334
+ const getFallbackResolvedCommand = (command, focused, mode) => {
1335
+ const lookup = resolveCommandWithResolversForMode(command, focused, { mode });
1336
+ return lookup.resolved;
1337
+ };
1338
+ const getRegisteredLayerCommandEntries = () => {
1339
+ return getCommandView().entries;
1340
+ };
1341
+ const getRegisteredBindingsByCommand = () => {
1342
+ if (registeredBindingsByCommandVersion === state.derivedVersion && registeredBindingsByCommand) {
1343
+ return registeredBindingsByCommand;
1344
+ }
1345
+ const bindingsByCommand = new Map;
1346
+ for (const layer of state.layers) {
1347
+ for (const binding of layer.bindings) {
1348
+ if (typeof binding.command !== "string") {
1349
+ continue;
1350
+ }
1351
+ const bindings = bindingsByCommand.get(binding.command);
1352
+ if (bindings) {
1353
+ bindings.push(binding);
1354
+ } else {
1355
+ bindingsByCommand.set(binding.command, [binding]);
1356
+ }
1357
+ }
1358
+ }
1359
+ registeredBindingsByCommandVersion = state.derivedVersion;
1360
+ registeredBindingsByCommand = bindingsByCommand;
1361
+ return bindingsByCommand;
1362
+ };
1363
+ const getCommandQueryContext = (query) => {
1364
+ const visibility = query?.visibility ?? "reachable";
1365
+ const focused = query && Object.prototype.hasOwnProperty.call(query, "focused") ? query.focused ?? null : getFocusedTargetIfAvailable(host);
1366
+ if (visibility === "registered") {
1367
+ return { visibility, focused };
1368
+ }
1369
+ return {
1370
+ visibility,
1371
+ focused,
1372
+ activeView: getActiveCommandView2(focused)
1373
+ };
1374
+ };
1375
+ const getFilteredCommandEntries = (query, context = getCommandQueryContext(query)) => {
1376
+ let entries;
1377
+ if (context.visibility === "registered") {
1378
+ entries = getRegisteredLayerCommandEntries();
1379
+ } else if (context.visibility === "active") {
1380
+ entries = context.activeView?.entries ?? [];
1381
+ } else {
1382
+ entries = context.activeView?.reachable ?? [];
1383
+ }
1384
+ return queryLayerCommandEntries({
1385
+ entries,
1386
+ query,
1387
+ getCommand: (command) => getCommand(command),
1388
+ onFilterError: (error) => {
1389
+ notify.emitError("command-query-filter-error", error, "[Keymap] Error in command query filter:");
1390
+ }
1391
+ });
1392
+ };
1393
+ const collectCommandEntryBindings = (grouped, indexesByName, context) => {
1394
+ visitCommandQueryBindings(context, (binding) => {
1395
+ collectBindingForCommandEntries(grouped, indexesByName, binding);
1396
+ });
1397
+ };
1398
+ const collectCommandBindings = (bindingsByCommand, context) => {
1399
+ if (context.visibility === "registered") {
1400
+ const registeredBindings = getRegisteredBindingsByCommand();
1401
+ for (const [command, bindings] of bindingsByCommand) {
1402
+ const commandAttrs = getCommandView().chainsByName.get(command)?.[0]?.commandState.attrs;
1403
+ for (const binding of registeredBindings.get(command) ?? []) {
1404
+ bindings.push(createActiveBinding(binding, commandAttrs));
1405
+ }
1406
+ }
1407
+ return;
1408
+ }
1409
+ visitCommandQueryBindings(context, (binding) => {
1410
+ collectBindingForCommandBindings(bindingsByCommand, binding, context);
1411
+ });
1412
+ };
1413
+ const visitCommandQueryBindings = (context, visit) => {
1414
+ if (context.visibility === "registered") {
1415
+ for (const layer of state.layers) {
1416
+ for (const binding of layer.bindings)
1417
+ visit(binding);
1418
+ }
1419
+ return;
1420
+ }
1421
+ const activeView2 = context.activeView;
1422
+ if (!activeView2) {
1423
+ return;
1424
+ }
1425
+ for (const layer of activeView2.layers) {
1426
+ if (layer.bindings.length === 0 || !conditions.matchesConditions(layer)) {
1427
+ continue;
1428
+ }
1429
+ for (const binding of layer.bindings) {
1430
+ if (conditions.matchesConditions(binding) && isBindingVisible(binding, context.focused, activeView2)) {
1431
+ visit(binding);
1432
+ }
1433
+ }
1434
+ }
1435
+ };
1436
+ const collectBindingForCommandEntries = (grouped, indexesByName, binding) => {
1437
+ if (typeof binding.command !== "string") {
1438
+ return;
1439
+ }
1440
+ const indexes = indexesByName.get(binding.command);
1441
+ if (!indexes || indexes.length === 0) {
1442
+ return;
1443
+ }
1444
+ for (const index of indexes) {
1445
+ const item = grouped[index];
1446
+ if (!item) {
1447
+ continue;
1448
+ }
1449
+ item.bindings.push(createActiveBinding(binding, item.commandAttrs));
1450
+ }
1451
+ };
1452
+ const collectBindingForCommandBindings = (bindingsByCommand, binding, context) => {
1453
+ if (typeof binding.command !== "string") {
1454
+ return;
1455
+ }
1456
+ const bindings = bindingsByCommand.get(binding.command);
1457
+ if (!bindings) {
1458
+ return;
1459
+ }
1460
+ bindings.push(createActiveBinding(binding, getCommandBindingAttrsForQuery(binding, context)));
1461
+ };
1462
+ const createActiveBinding = (binding, commandAttrs) => {
1463
+ return {
1464
+ sequence: binding.sequence,
1465
+ command: binding.command,
1466
+ commandAttrs,
1467
+ attrs: binding.attrs,
1468
+ event: binding.event,
1469
+ preventDefault: binding.preventDefault,
1470
+ fallthrough: binding.fallthrough
1471
+ };
1472
+ };
1473
+ const getCommandBindingAttrsForQuery = (binding, context) => {
1474
+ if (typeof binding.command !== "string") {
1475
+ return;
1476
+ }
1477
+ if (context.visibility === "registered") {
1478
+ return getCommandView().chainsByName.get(binding.command)?.[0]?.commandState.attrs;
1479
+ }
1480
+ const activeView2 = context.activeView;
1481
+ if (!activeView2) {
1482
+ return;
1483
+ }
1484
+ return getBindingCommandAttrs(binding, context.focused, activeView2);
1485
+ };
1486
+ const resolveCommandWithResolversForMode = (command, focused, options2) => {
1487
+ const mode = options2?.mode ?? "active";
1488
+ const execution = options2?.execution ?? { input: command };
1489
+ const lookup = resolveCommandWithResolvers(command, state.commandResolvers.values(), () => createCommandResolverContext(focused, mode, execution), (error) => {
1490
+ notify.emitError("command-resolver-error", error, `[Keymap] Error in command resolver for "${command}":`);
1491
+ });
1492
+ let resolved = lookup.resolved;
1493
+ if (resolved) {
1494
+ const entry = getCommandEntryForMode(resolved.command.name, focused, mode);
1495
+ if (entry?.commandState.command === resolved.command && resolved.target === undefined) {
1496
+ resolved = { ...resolved, target: entry.layer.target };
1497
+ lookup.resolved = resolved;
1498
+ }
1499
+ }
1500
+ if (resolved && !resolved.attrs) {
1501
+ const attrs = getCommandStateAttrs(resolved.command.name, focused, mode) ?? getResolverCommandAttrs(resolved.command);
1502
+ if (attrs) {
1503
+ lookup.resolved = { ...resolved, attrs };
1504
+ }
1505
+ }
1506
+ return lookup;
1507
+ };
1508
+ const getCommandStateAttrs = (command, focused, mode) => {
1509
+ if (mode === "registered") {
1510
+ return getCommandEntry(command)?.commandState.attrs;
1511
+ }
1512
+ return getActiveCommandView2(focused).reachableByName.get(command)?.commandState.attrs;
1513
+ };
1514
+ const getCommandEntryForMode = (command, focused, mode) => {
1515
+ if (mode === "registered") {
1516
+ return getCommandEntry(command);
1517
+ }
1518
+ return getActiveCommandView2(focused).reachableByName.get(command);
1519
+ };
1520
+ const createCommandResolverContext = (focused, mode, execution) => {
1521
+ let input = execution.input;
1522
+ let payload = execution.payload;
1523
+ return {
1524
+ context: {
1525
+ get input() {
1526
+ return input;
1527
+ },
1528
+ get payload() {
1529
+ return payload;
1530
+ },
1531
+ setInput(nextInput) {
1532
+ input = nextInput;
1533
+ },
1534
+ setPayload(nextPayload) {
1535
+ payload = nextPayload;
1536
+ },
1537
+ getCommand: (name) => {
1538
+ if (mode === "registered") {
1539
+ return getCommandByName(name);
1540
+ }
1541
+ return getTopCommand(name, focused);
1542
+ }
1543
+ },
1544
+ getExecutionFields() {
1545
+ return { input, payload };
1546
+ }
1547
+ };
1548
+ };
1549
+ return {
1550
+ normalizeCommands: normalizeLayerCommands,
1551
+ prependCommandResolver,
1552
+ appendCommandResolver,
1553
+ clearCommandResolvers,
1554
+ getCommands,
1555
+ getCommandEntries,
1556
+ getCommandBindings,
1557
+ getResolvedCommandChain,
1558
+ getRegisteredResolvedEntries,
1559
+ getActiveRegisteredResolvedEntries,
1560
+ resolveRegisteredResolverFallback,
1561
+ resolveActiveResolverFallback,
1562
+ getTopCommand,
1563
+ getDispatchUnavailableCommandState,
1564
+ getActiveCommandView: getActiveCommandView2,
1565
+ isBindingVisible,
1566
+ getBindingCommandAttrs,
1567
+ getCommandResolutionStatus
1568
+ };
1569
+ }
1570
+ function getCommand(state) {
1571
+ return state.command;
1572
+ }
1573
+ function normalizeCommands(options) {
1574
+ const normalizedCommands = [];
1575
+ const seen = new Set;
1576
+ for (const command of options.commands) {
1577
+ try {
1578
+ const mergedRequires = {};
1579
+ const matchers = [];
1580
+ const normalizedName = normalizeCommandName(command.name);
1581
+ const fields = getCommandFields(command);
1582
+ const attrs = {};
1583
+ if (seen.has(normalizedName)) {
1584
+ options.onError("duplicate-command", { command: normalizedName }, `Duplicate keymap command "${normalizedName}" in the same layer`);
1585
+ continue;
1586
+ }
1587
+ command.name = normalizedName;
1588
+ for (const [fieldName, value] of Object.entries(fields)) {
1589
+ if (value === undefined) {
1590
+ continue;
1591
+ }
1592
+ const compiler = options.commandFields.get(fieldName);
1593
+ if (!compiler) {
1594
+ continue;
1595
+ }
1596
+ compiler(value, createFieldCompilerContext({
1597
+ fieldName,
1598
+ conditions: options.conditions,
1599
+ requirements: mergedRequires,
1600
+ matchers,
1601
+ attrs
1602
+ }));
1603
+ }
1604
+ const commandState = {
1605
+ command,
1606
+ fields,
1607
+ attrs: Object.keys(attrs).length === 0 ? undefined : attrs,
1608
+ requires: Object.entries(mergedRequires),
1609
+ matchers
1610
+ };
1611
+ seen.add(commandState.command.name);
1612
+ normalizedCommands.push(commandState);
1613
+ } catch (error) {
1614
+ options.onError("register-command-failed", error, getErrorMessage(error, `Failed to register keymap command "${String(command.name)}"`));
1615
+ continue;
1616
+ }
1617
+ }
1618
+ return normalizedCommands;
1619
+ }
1620
+ function resolveCommandWithResolvers(command, resolvers, createContext, onResolverError) {
1621
+ if (resolvers.length === 0) {
1622
+ return { hadError: false };
1623
+ }
1624
+ let hadError = false;
1625
+ for (const resolver of resolvers) {
1626
+ let resolvedCommand;
1627
+ const attempt = createContext();
1628
+ try {
1629
+ resolvedCommand = resolver(command, attempt.context);
1630
+ } catch (error) {
1631
+ hadError = true;
1632
+ onResolverError(error);
1633
+ continue;
1634
+ }
1635
+ if (resolvedCommand) {
1636
+ return { hadError, resolved: getResolverCommandEntry(resolvedCommand, attempt.getExecutionFields()) };
1637
+ }
1638
+ }
1639
+ return { hadError };
1640
+ }
1641
+ function getCommandFields(command) {
1642
+ const fields = {};
1643
+ for (const [name, value] of Object.entries(command)) {
1644
+ if (!RESERVED_COMMAND_FIELDS.has(name) && value !== undefined) {
1645
+ fields[name] = value;
1646
+ }
1647
+ }
1648
+ return Object.keys(fields).length === 0 ? EMPTY_COMMAND_FIELDS : fields;
1649
+ }
1650
+ function getResolverCommandEntry(command, execution) {
1651
+ return {
1652
+ command,
1653
+ input: execution.input,
1654
+ payload: execution.payload
1655
+ };
1656
+ }
1657
+ function getResolverCommandAttrs(command) {
1658
+ const fields = getCommandFields(command);
1659
+ return fields === EMPTY_COMMAND_FIELDS ? undefined : fields;
1660
+ }
1661
+ function queryLayerCommandEntries(options) {
1662
+ const namespace = options.query?.namespace;
1663
+ const limit = normalizeQueryLimit(options.query?.limit);
1664
+ if (limit === 0) {
1665
+ return [];
1666
+ }
1667
+ const normalizedSearch = options.query?.search?.trim().toLowerCase() ?? "";
1668
+ let searchKeys = DEFAULT_COMMAND_SEARCH_FIELDS;
1669
+ if (options.query?.searchIn && options.query.searchIn.length > 0) {
1670
+ searchKeys = options.query.searchIn;
1671
+ }
1672
+ const filter = options.query?.filter;
1673
+ let filterEntries;
1674
+ let filterPredicate;
1675
+ let exactNameFilter;
1676
+ if (typeof filter === "function") {
1677
+ filterPredicate = filter;
1678
+ } else if (filter) {
1679
+ const entries = Object.entries(filter);
1680
+ const remainingEntries = [];
1681
+ for (const [key, matcher] of entries) {
1682
+ if (key === "name") {
1683
+ if (typeof matcher === "string") {
1684
+ exactNameFilter = new Set([matcher]);
1685
+ continue;
1686
+ }
1687
+ if (Array.isArray(matcher)) {
1688
+ const names = new Set;
1689
+ for (const value of matcher) {
1690
+ if (typeof value === "string") {
1691
+ names.add(value);
1692
+ }
1693
+ }
1694
+ exactNameFilter = names;
1695
+ continue;
1696
+ }
1697
+ }
1698
+ remainingEntries.push([key, matcher]);
1699
+ }
1700
+ filterEntries = remainingEntries.length > 0 ? remainingEntries : undefined;
1701
+ }
1702
+ const results = [];
1703
+ for (const entry of options.entries) {
1704
+ const commandState = entry.commandState;
1705
+ if (!commandMatchesNamespace(commandState, namespace)) {
1706
+ continue;
1707
+ }
1708
+ if (!commandMatchesSearch(commandState, normalizedSearch, searchKeys)) {
1709
+ continue;
1710
+ }
1711
+ if (exactNameFilter && !exactNameFilter.has(commandState.command.name)) {
1712
+ continue;
1713
+ }
1714
+ if (!commandMatchesFilters(commandState, filterEntries, options)) {
1715
+ continue;
1716
+ }
1717
+ if (filterPredicate) {
1718
+ let matches = false;
1719
+ try {
1720
+ matches = filterPredicate(options.getCommand(commandState));
1721
+ } catch (error) {
1722
+ options.onFilterError(error);
1723
+ continue;
1724
+ }
1725
+ if (!matches) {
1726
+ continue;
1727
+ }
1728
+ }
1729
+ results.push(entry);
1730
+ if (limit !== undefined && results.length >= limit) {
1731
+ break;
1732
+ }
1733
+ }
1734
+ return results;
1735
+ }
1736
+ function normalizeQueryLimit(value) {
1737
+ if (value === undefined) {
1738
+ return;
1739
+ }
1740
+ const limit = Math.floor(Number(value));
1741
+ if (!Number.isFinite(limit) || limit <= 0) {
1742
+ return 0;
1743
+ }
1744
+ return limit;
1745
+ }
1746
+ function commandMatchesSearch(commandState, search, searchKeys) {
1747
+ if (!search) {
1748
+ return true;
1749
+ }
1750
+ for (const key of searchKeys) {
1751
+ if (commandKeyMatchesSearch(commandState, key, search)) {
1752
+ return true;
1753
+ }
1754
+ }
1755
+ return false;
1756
+ }
1757
+ function commandMatchesNamespace(commandState, namespace) {
1758
+ if (namespace === undefined) {
1759
+ return true;
1760
+ }
1761
+ const fields = commandState.fields;
1762
+ if (!Object.prototype.hasOwnProperty.call(fields, "namespace")) {
1763
+ return false;
1764
+ }
1765
+ return valueMatchesFilter(fields.namespace, namespace);
1766
+ }
1767
+ function commandMatchesFilters(commandState, filters, options) {
1768
+ if (!filters) {
1769
+ return true;
1770
+ }
1771
+ for (const [key, matcher] of filters) {
1772
+ if (!commandKeyMatchesQuery(commandState, key, matcher, options)) {
1773
+ return false;
1774
+ }
1775
+ }
1776
+ return true;
1777
+ }
1778
+ function commandKeyMatchesSearch(commandState, key, search) {
1779
+ return getCommandSearchText(commandState, key)?.includes(search) === true;
1780
+ }
1781
+ function getCommandSearchText(commandState, key) {
1782
+ let cache = commandSearchCache.get(commandState);
1783
+ if (!cache) {
1784
+ cache = new Map;
1785
+ commandSearchCache.set(commandState, cache);
1786
+ }
1787
+ if (cache.has(key)) {
1788
+ return cache.get(key);
1789
+ }
1790
+ const fields = commandState.fields;
1791
+ const attrs = commandState.attrs;
1792
+ let value;
1793
+ if (key === "name") {
1794
+ value = commandState.command.name;
1795
+ } else if (Object.prototype.hasOwnProperty.call(fields, key)) {
1796
+ value = fields[key];
1797
+ } else if (attrs && Object.prototype.hasOwnProperty.call(attrs, key)) {
1798
+ value = attrs[key];
1799
+ }
1800
+ const text = toSearchText(value);
1801
+ cache.set(key, text);
1802
+ return text;
1803
+ }
1804
+ function toSearchText(value) {
1805
+ if (Array.isArray(value)) {
1806
+ const parts = [];
1807
+ for (const entry of value) {
1808
+ const text = toSearchText(entry);
1809
+ if (text !== undefined) {
1810
+ parts.push(text);
1811
+ }
1812
+ }
1813
+ return parts.length > 0 ? parts.join("\x00") : undefined;
1814
+ }
1815
+ if (typeof value === "string") {
1816
+ return value.toLowerCase();
1817
+ }
1818
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
1819
+ return String(value).toLowerCase();
1820
+ }
1821
+ return;
1822
+ }
1823
+ function runCommandQueryPredicate(matcher, value, command, onFilterError) {
1824
+ try {
1825
+ return matcher(value, command);
1826
+ } catch (error) {
1827
+ onFilterError(error);
1828
+ return false;
1829
+ }
1830
+ }
1831
+ function commandKeyMatchesQuery(commandState, key, matcher, options) {
1832
+ if (typeof matcher === "function") {
1833
+ return commandKeyMatchesPredicate(commandState, key, matcher, options);
1834
+ }
1835
+ return commandKeyMatchesExact(commandState, key, matcher);
1836
+ }
1837
+ function commandKeyMatchesPredicate(commandState, key, matcher, options) {
1838
+ const command = commandState.command;
1839
+ const fields = commandState.fields;
1840
+ const attrs = commandState.attrs;
1841
+ let commandView;
1842
+ let foundValue = false;
1843
+ const getCommandView = () => commandView ??= options.getCommand(commandState);
1844
+ if (key === "name") {
1845
+ foundValue = true;
1846
+ if (runCommandQueryPredicate(matcher, command.name, getCommandView(), options.onFilterError)) {
1847
+ return true;
1848
+ }
1849
+ }
1850
+ if (Object.prototype.hasOwnProperty.call(fields, key)) {
1851
+ foundValue = true;
1852
+ if (runCommandQueryPredicate(matcher, fields[key], getCommandView(), options.onFilterError)) {
1853
+ return true;
1854
+ }
1855
+ }
1856
+ if (attrs && Object.prototype.hasOwnProperty.call(attrs, key)) {
1857
+ foundValue = true;
1858
+ if (runCommandQueryPredicate(matcher, attrs[key], getCommandView(), options.onFilterError)) {
1859
+ return true;
1860
+ }
1861
+ }
1862
+ return !foundValue && runCommandQueryPredicate(matcher, undefined, getCommandView(), options.onFilterError);
1863
+ }
1864
+ function commandKeyMatchesExact(commandState, key, matcher) {
1865
+ const command = commandState.command;
1866
+ const fields = commandState.fields;
1867
+ const attrs = commandState.attrs;
1868
+ if (key === "name" && valueMatchesFilter(command.name, matcher)) {
1869
+ return true;
1870
+ }
1871
+ if (Object.prototype.hasOwnProperty.call(fields, key) && valueMatchesFilter(fields[key], matcher)) {
1872
+ return true;
1873
+ }
1874
+ return !!attrs && Object.prototype.hasOwnProperty.call(attrs, key) && valueMatchesFilter(attrs[key], matcher);
1875
+ }
1876
+ function valueMatchesFilter(value, matcher) {
1877
+ if (Array.isArray(matcher)) {
1878
+ return matcher.some((expected) => valueMatchesExact(value, expected));
1879
+ }
1880
+ return valueMatchesExact(value, matcher);
1881
+ }
1882
+ function valueMatchesExact(value, expected) {
1883
+ if (Array.isArray(value)) {
1884
+ return value.some((entry) => valueMatchesExact(entry, expected));
1885
+ }
1886
+ return Object.is(value, expected);
1887
+ }
1888
+
1889
+ // src/services/command-executor.ts
1890
+ function createCommandExecutorService(notify, runtime, activation, catalog, options) {
1891
+ const createCommandContext = (event, focused, target, data, input, payload) => {
1892
+ return {
1893
+ keymap: options.keymap,
1894
+ event,
1895
+ focused,
1896
+ target,
1897
+ data,
1898
+ input,
1899
+ payload
1900
+ };
1901
+ };
1902
+ const executeResolvedCommand = (commandName, command, context, includeCommand) => {
1903
+ const commandView = typeof command === "function" ? undefined : command;
1904
+ const run = typeof command === "function" ? command : command.run;
1905
+ const resultCommand = includeCommand ? commandView : undefined;
1906
+ let result;
1907
+ try {
1908
+ result = run(commandView ? { ...context, command: commandView } : context);
1909
+ } catch (error) {
1910
+ notify.emitError("command-execution-error", error, `[Keymap] Error running command "${commandName}":`);
1911
+ return {
1912
+ status: "error",
1913
+ result: resultCommand ? { ok: false, reason: "error", command: resultCommand } : { ok: false, reason: "error" }
1914
+ };
1915
+ }
1916
+ if (isPromiseLike(result)) {
1917
+ result.catch((error) => {
1918
+ notify.emitError("async-command-error", error, `[Keymap] Async error in command "${commandName}":`);
1919
+ });
1920
+ return {
1921
+ status: "handled",
1922
+ result: resultCommand ? { ok: true, command: resultCommand } : { ok: true }
1923
+ };
1924
+ }
1925
+ if (isRunCommandResult(result)) {
1926
+ let commandResult = result;
1927
+ if (!result.ok && result.reason !== "not-found" && includeCommand && commandView && !result.command) {
1928
+ commandResult = { ...result, command: commandView };
1929
+ } else if (result.ok && includeCommand && commandView && !result.command) {
1930
+ commandResult = { ...result, command: commandView };
1931
+ }
1932
+ return {
1933
+ status: result.ok ? "handled" : "rejected",
1934
+ result: commandResult
1935
+ };
1936
+ }
1937
+ if (result === false) {
1938
+ return {
1939
+ status: "rejected",
1940
+ result: resultCommand ? { ok: false, reason: "rejected", command: resultCommand } : { ok: false, reason: "rejected" }
1941
+ };
1942
+ }
1943
+ return {
1944
+ status: "handled",
1945
+ result: resultCommand ? { ok: true, command: resultCommand } : { ok: true }
1946
+ };
1947
+ };
1948
+ const executeCommandChain = (commandName, chain, event, focused, target, data, payload, includeCommand) => {
1949
+ let rejected;
1950
+ for (const entry of chain ?? []) {
1951
+ const executed = executeResolvedCommand(commandName, entry.command, createCommandContext(event, focused, target ?? entry.target ?? null, data, commandName, payload), includeCommand);
1952
+ if (executed.status === "handled" || executed.status === "error") {
1953
+ return [executed.result, rejected];
1954
+ }
1955
+ rejected = executed.result;
1956
+ }
1957
+ return [undefined, rejected];
1958
+ };
1959
+ const executeProgrammaticCommand = (cmd, commandOptions, mode) => {
1960
+ let normalized;
1961
+ try {
1962
+ normalized = normalizeBindingCommand(cmd);
1963
+ } catch {
1964
+ return { ok: false, reason: "invalid-args" };
1965
+ }
1966
+ if (typeof normalized !== "string") {
1967
+ return { ok: false, reason: "not-found" };
1968
+ }
1969
+ const includeCommand = commandOptions?.includeCommand === true;
1970
+ const focused = commandOptions?.focused ?? activation.getFocusedTargetIfAvailable();
1971
+ const event = commandOptions?.event ?? options.createCommandEvent();
1972
+ const data = runtime.getReadonlyData();
1973
+ const payload = commandOptions?.payload;
1974
+ const chain = mode === "registered" ? catalog.getRegisteredResolvedEntries(normalized) : catalog.getActiveRegisteredResolvedEntries(normalized, focused);
1975
+ const [done, rejected] = executeCommandChain(normalized, chain, event, focused, commandOptions?.target, data, payload, includeCommand);
1976
+ if (done) {
1977
+ return done;
1978
+ }
1979
+ let rejectedResult = rejected;
1980
+ const fallback = mode === "registered" ? catalog.resolveRegisteredResolverFallback(normalized, { input: normalized, payload }) : catalog.resolveActiveResolverFallback(normalized, focused, { input: normalized, payload });
1981
+ if (fallback.resolved) {
1982
+ const result = executeResolvedCommand(normalized, fallback.resolved.command, createCommandContext(event, focused, commandOptions?.target ?? fallback.resolved.target ?? null, data, fallback.resolved.input ?? normalized, fallback.resolved.payload), includeCommand);
1983
+ if (result.status === "handled" || result.status === "error") {
1984
+ return result.result;
1985
+ }
1986
+ rejectedResult = result.result;
1987
+ }
1988
+ if (fallback.hadError) {
1989
+ return { ok: false, reason: "error" };
1990
+ }
1991
+ if (mode === "active") {
1992
+ const unavailable = catalog.getDispatchUnavailableCommandState(normalized, focused, includeCommand);
1993
+ if (unavailable) {
1994
+ return unavailable.command ? { ok: false, reason: unavailable.reason, command: unavailable.command } : { ok: false, reason: unavailable.reason };
1995
+ }
1996
+ }
1997
+ return rejectedResult ?? { ok: false, reason: "not-found" };
1998
+ };
1999
+ return {
2000
+ runCommand(cmd, commandOptions) {
2001
+ return executeProgrammaticCommand(cmd, commandOptions, "registered");
2002
+ },
2003
+ dispatchCommand(cmd, commandOptions) {
2004
+ return executeProgrammaticCommand(cmd, commandOptions, "active");
2005
+ },
2006
+ runBinding(bindingLayer, binding, event, focused, payload) {
2007
+ const data = runtime.getReadonlyData();
2008
+ if (binding.run) {
2009
+ const result = executeResolvedCommand(typeof binding.command === "string" ? binding.command : "<function>", binding.run, createCommandContext(event, focused, bindingLayer.target ?? null, data, typeof binding.command === "string" ? binding.command : "<function>", payload), false);
2010
+ if (result.status === "rejected") {
2011
+ return false;
2012
+ }
2013
+ applyBindingEventEffects(binding, event);
2014
+ return true;
2015
+ }
2016
+ if (typeof binding.command !== "string") {
2017
+ return false;
2018
+ }
2019
+ const chain = catalog.getResolvedCommandChain(binding.command, focused, payload === undefined ? undefined : { input: binding.command, payload }).entries;
2020
+ for (const entry of chain ?? []) {
2021
+ const result = executeResolvedCommand(binding.command, entry.command, createCommandContext(event, focused, entry.target ?? bindingLayer.target ?? null, data, entry.input ?? binding.command, entry.payload), false);
2022
+ if (result.status === "rejected") {
2023
+ continue;
2024
+ }
2025
+ applyBindingEventEffects(binding, event);
2026
+ return true;
2027
+ }
2028
+ return false;
2029
+ }
2030
+ };
2031
+ }
2032
+ function isRunCommandResult(value) {
2033
+ return typeof value === "object" && value !== null && "ok" in value;
2034
+ }
2035
+ function applyBindingEventEffects(binding, event) {
2036
+ if (!binding.preventDefault) {
2037
+ return;
2038
+ }
2039
+ event.preventDefault();
2040
+ event.stopPropagation();
2041
+ }
2042
+
2043
+ // src/services/compiler.ts
2044
+ var EMPTY_COMPILE_FIELDS = Object.freeze({});
2045
+ var EMPTY_REQUIRES = [];
2046
+ var EMPTY_MATCHERS = [];
2047
+ function snapshotAttributes(attrs) {
2048
+ if (Object.keys(attrs).length === 0) {
2049
+ return;
2050
+ }
2051
+ return snapshotDataValue(attrs, { freeze: true });
2052
+ }
2053
+ function createCompilerService(state, notify, conditions, options) {
2054
+ const parseTokenKey = (key) => {
2055
+ return parseSingleKeyPartWithParsers(key, state.bindingParsers.values(), {
2056
+ tokens: state.tokens,
2057
+ patterns: state.patterns,
2058
+ layer: EMPTY_COMPILE_FIELDS,
2059
+ parseObjectKey: (value, options2) => parseObjectKeyPart(value, options2)
2060
+ });
2061
+ };
2062
+ const parseKeySequence = (key) => {
2063
+ if (typeof key !== "string") {
2064
+ return [parseObjectKeyPart(key)];
2065
+ }
2066
+ const parsed = parseBindingSequenceWithParsers(key, state.bindingParsers.values(), {
2067
+ tokens: state.tokens,
2068
+ patterns: state.patterns,
2069
+ layer: EMPTY_COMPILE_FIELDS,
2070
+ parseObjectKey: (value, options2) => parseObjectKeyPart(value, options2)
2071
+ });
2072
+ for (const tokenName of parsed.unknownTokens) {
2073
+ options.warnUnknownToken(tokenName, key);
2074
+ }
2075
+ return parsed.parts;
2076
+ };
2077
+ const formatKey = (key, options2) => {
2078
+ return stringifyKeySequence(parseKeySequence(key), options2);
2079
+ };
2080
+ const compileBindings = (bindings, tokens, sourceTarget, sourceLayerOrder, compileFields) => {
2081
+ const bindingStates = [];
2082
+ let hasTokenBindings = false;
2083
+ const bindingExpanders = state.bindingExpanders.values();
2084
+ const bindingParsers = state.bindingParsers.values();
2085
+ const bindingFieldCompilers = state.bindingFields;
2086
+ const allowExactPrefixAmbiguity = state.disambiguationResolvers.has();
2087
+ const warnUnknownField = options.warnUnknownField;
2088
+ const warnUnknownToken = options.warnUnknownToken;
2089
+ for (const [bindingIndex, binding] of bindings.entries()) {
2090
+ let expandedBindingKeys;
2091
+ try {
2092
+ expandedBindingKeys = expandBindingKeyWithExpanders(binding.key, bindingExpanders, {
2093
+ layer: compileFields
2094
+ });
2095
+ } catch (error) {
2096
+ notify.emitError("binding-expand-error", error, getErrorMessage(error, "Failed to expand keymap binding"));
2097
+ continue;
2098
+ }
2099
+ for (const expandedBindingKey of expandedBindingKeys) {
2100
+ const expandedKey = expandedBindingKey.key;
2101
+ let parsed;
2102
+ try {
2103
+ parsed = typeof expandedKey === "string" ? parseBindingSequenceWithParsers(expandedKey, bindingParsers, {
2104
+ tokens,
2105
+ patterns: state.patterns,
2106
+ layer: compileFields,
2107
+ parseObjectKey: (value, options2) => parseObjectKeyPart(value, options2)
2108
+ }) : {
2109
+ parts: [parseObjectKeyPart(expandedKey)],
2110
+ usedTokens: [],
2111
+ unknownTokens: [],
2112
+ hasTokenBindings: false
2113
+ };
2114
+ parsed = applyExpansionDisplays(parsed, expandedBindingKey);
2115
+ } catch (error) {
2116
+ notify.emitError("binding-parse-error", error, getErrorMessage(error, "Failed to parse keymap binding"));
2117
+ continue;
2118
+ }
2119
+ const sequence = parsed.parts;
2120
+ hasTokenBindings ||= parsed.hasTokenBindings;
2121
+ for (const tokenName of parsed.unknownTokens) {
2122
+ warnUnknownToken(tokenName, typeof expandedKey === "string" ? expandedKey : String(expandedKey.name));
2123
+ }
2124
+ for (const compiledInput of applyBindingTransformers(binding, sequence, tokens, bindingParsers, compileFields)) {
2125
+ try {
2126
+ const event = normalizeBindingEvent(compiledInput.event);
2127
+ const compiledSequence = compiledInput.sequence;
2128
+ const mergedRequires = {};
2129
+ const mergedAttrs = {};
2130
+ const matchers = [];
2131
+ for (const fieldName in compiledInput) {
2132
+ if (fieldName === "sequence") {
2133
+ continue;
2134
+ }
2135
+ if (RESERVED_BINDING_FIELDS.has(fieldName)) {
2136
+ continue;
2137
+ }
2138
+ const value = compiledInput[fieldName];
2139
+ if (value === undefined) {
2140
+ continue;
2141
+ }
2142
+ const compiler = bindingFieldCompilers.get(fieldName);
2143
+ if (!compiler) {
2144
+ warnUnknownField("binding", fieldName);
2145
+ continue;
2146
+ }
2147
+ compiler(value, createFieldCompilerContext({
2148
+ fieldName,
2149
+ conditions,
2150
+ requirements: mergedRequires,
2151
+ matchers,
2152
+ attrs: mergedAttrs
2153
+ }));
2154
+ }
2155
+ const attrs = Object.keys(mergedAttrs).length > 0 ? snapshotAttributes(mergedAttrs) : undefined;
2156
+ const command = normalizeBindingCommand(compiledInput.cmd);
2157
+ const compiledBinding = {
2158
+ binding,
2159
+ sequence: compiledSequence,
2160
+ command,
2161
+ event,
2162
+ parsedBinding: snapshotParsedBinding(compiledInput),
2163
+ sourceTarget,
2164
+ sourceLayerOrder,
2165
+ bindingIndex,
2166
+ requires: Object.keys(mergedRequires).length > 0 ? Object.entries(mergedRequires) : EMPTY_REQUIRES,
2167
+ matchers: matchers.length > 0 ? matchers : EMPTY_MATCHERS,
2168
+ preventDefault: compiledInput.preventDefault !== false,
2169
+ fallthrough: compiledInput.fallthrough ?? false
2170
+ };
2171
+ if (attrs) {
2172
+ compiledBinding.attrs = attrs;
2173
+ }
2174
+ if (typeof command === "function") {
2175
+ compiledBinding.run = command;
2176
+ }
2177
+ if (compiledSequence.length === 0) {
2178
+ continue;
2179
+ }
2180
+ if (event === "release" && compiledSequence.length > 1) {
2181
+ throw new Error("Keymap release bindings only support a single key stroke");
2182
+ }
2183
+ const terminalPattern = compiledSequence.at(-1);
2184
+ if (terminalPattern?.patternName) {
2185
+ const pattern = state.patterns.get(terminalPattern.patternName);
2186
+ if (pattern && pattern.max !== pattern.min) {
2187
+ throw new Error("Keymap unbounded sequence patterns must be followed by a concrete continuation");
2188
+ }
2189
+ }
2190
+ if (event === "press" && !allowExactPrefixAmbiguity) {
2191
+ validateExactPrefixAmbiguity(bindingStates, compiledBinding);
2192
+ }
2193
+ bindingStates.push(compiledBinding);
2194
+ } catch (error) {
2195
+ notify.emitError("binding-compile-error", error, getErrorMessage(error, "Failed to compile keymap binding"));
2196
+ }
2197
+ }
2198
+ }
2199
+ }
2200
+ return {
2201
+ bindings: bindingStates,
2202
+ hasTokenBindings
2203
+ };
2204
+ };
2205
+ const parseObjectKeyPart = (key, options2) => {
2206
+ return createKeySequencePart(key, options2);
2207
+ };
2208
+ const normalizeBindingEvent = (event) => {
2209
+ if (event === undefined || event === "press") {
2210
+ return "press";
2211
+ }
2212
+ if (event === "release") {
2213
+ return "release";
2214
+ }
2215
+ throw new Error(`Invalid keymap binding event "${String(event)}": expected "press" or "release"`);
2216
+ };
2217
+ const applyBindingTransformers = (binding, sequence, tokens, bindingParsers, compileFields) => {
2218
+ const bindingTransformers = state.bindingTransformers.values();
2219
+ if (bindingTransformers.length === 0) {
2220
+ return [{ ...binding, sequence: cloneKeySequence(sequence) }];
2221
+ }
2222
+ const parsedBinding = {
2223
+ ...binding,
2224
+ sequence: cloneKeySequence(sequence)
2225
+ };
2226
+ const extraBindings = [];
2227
+ let keepOriginal = true;
2228
+ const layer = compileFields ?? EMPTY_COMPILE_FIELDS;
2229
+ for (const transformer of bindingTransformers) {
2230
+ try {
2231
+ transformer(parsedBinding, {
2232
+ layer,
2233
+ parseKey: (key) => {
2234
+ return parseSingleKeyPartWithParsers(key, bindingParsers, {
2235
+ tokens,
2236
+ patterns: state.patterns,
2237
+ layer,
2238
+ parseObjectKey: (value, options2) => parseObjectKeyPart(value, options2)
2239
+ });
2240
+ },
2241
+ add: (nextBinding) => {
2242
+ extraBindings.push(snapshotParsedBinding(nextBinding));
2243
+ },
2244
+ skipOriginal: () => {
2245
+ keepOriginal = false;
2246
+ }
2247
+ });
2248
+ } catch (error) {
2249
+ notify.emitError("binding-transformer-error", error, "[Keymap] Error in binding transformer:");
2250
+ }
2251
+ }
2252
+ if (!keepOriginal) {
2253
+ return extraBindings;
2254
+ }
2255
+ if (extraBindings.length === 0) {
2256
+ return [parsedBinding];
2257
+ }
2258
+ return [parsedBinding, ...extraBindings];
2259
+ };
2260
+ return { parseTokenKey, parseKeySequence, formatKey, compileBindings };
2261
+ }
2262
+ function sequenceMatchesPrefix(left, right) {
2263
+ if (left.length >= right.length) {
2264
+ return false;
2265
+ }
2266
+ for (let index = 0;index < left.length; index += 1) {
2267
+ if (left[index]?.match !== right[index]?.match) {
2268
+ return false;
2269
+ }
2270
+ }
2271
+ return true;
2272
+ }
2273
+ function validateExactPrefixAmbiguity(bindings, next) {
2274
+ for (const existing of bindings) {
2275
+ if (existing.event !== "press") {
2276
+ continue;
2277
+ }
2278
+ if (existing.command !== undefined && sequenceMatchesPrefix(existing.sequence, next.sequence) || next.command !== undefined && sequenceMatchesPrefix(next.sequence, existing.sequence)) {
2279
+ throw new Error("Keymap bindings cannot use the same sequence as both an exact match and a prefix in the same layer");
2280
+ }
2281
+ }
2282
+ }
2283
+ function expandBindingKeyWithExpanders(key, expanders, options) {
2284
+ if (typeof key !== "string" || expanders.length === 0) {
2285
+ return [{ key }];
2286
+ }
2287
+ const layer = options?.layer ?? EMPTY_COMPILE_FIELDS;
2288
+ let candidates = [{ key }];
2289
+ for (const expander of expanders) {
2290
+ const nextCandidates = [];
2291
+ for (const candidate of candidates) {
2292
+ const result = expander({
2293
+ input: candidate.key,
2294
+ displays: candidate.displays,
2295
+ layer
2296
+ });
2297
+ if (!result) {
2298
+ nextCandidates.push(candidate);
2299
+ continue;
2300
+ }
2301
+ if (result.length === 0) {
2302
+ throw new Error(`Keymap binding expander must return at least one key sequence for "${candidate.key}"`);
2303
+ }
2304
+ for (const expanded of result) {
2305
+ if (!expanded || typeof expanded !== "object" || Array.isArray(expanded) || typeof expanded.key !== "string") {
2306
+ throw new Error(`Keymap binding expander must return expansion objects with string keys for "${candidate.key}"`);
2307
+ }
2308
+ if (expanded.displays !== undefined) {
2309
+ if (!Array.isArray(expanded.displays)) {
2310
+ throw new Error(`Keymap binding expander displays must be an array of strings for "${candidate.key}"`);
2311
+ }
2312
+ for (const display of expanded.displays) {
2313
+ if (typeof display !== "string") {
2314
+ throw new Error(`Keymap binding expander displays must be an array of strings for "${candidate.key}"`);
2315
+ }
2316
+ }
2317
+ }
2318
+ nextCandidates.push(expanded);
2319
+ }
2320
+ }
2321
+ candidates = nextCandidates;
2322
+ }
2323
+ return candidates;
2324
+ }
2325
+ function applyExpansionDisplays(parsed, expansion) {
2326
+ if (!expansion.displays) {
2327
+ return parsed;
2328
+ }
2329
+ if (expansion.displays.length !== parsed.parts.length) {
2330
+ throw new Error(`Keymap binding expansion displays length must match parsed sequence length for "${String(expansion.key)}"`);
2331
+ }
2332
+ return {
2333
+ ...parsed,
2334
+ parts: parsed.parts.map((part, index) => ({
2335
+ ...part,
2336
+ display: expansion.displays[index]
2337
+ }))
2338
+ };
2339
+ }
2340
+ function parseBindingSequenceWithParsers(key, parsers, options) {
2341
+ if (key.length === 0) {
2342
+ throw new Error("Invalid key sequence: sequence cannot be empty");
2343
+ }
2344
+ if (parsers.length === 0) {
2345
+ throw new Error("No keymap binding parsers are registered");
2346
+ }
2347
+ const tokens = options.tokens ?? new Map;
2348
+ const patterns = options.patterns ?? new Map;
2349
+ const layer = options.layer ?? EMPTY_COMPILE_FIELDS;
2350
+ const parseObjectKey = options.parseObjectKey;
2351
+ const parts = [];
2352
+ const usedTokens = new Set;
2353
+ const unknownTokens = new Set;
2354
+ let index = 0;
2355
+ while (index < key.length) {
2356
+ let matched = false;
2357
+ for (const parser of parsers) {
2358
+ const result = parser({
2359
+ input: key,
2360
+ index,
2361
+ layer,
2362
+ tokens,
2363
+ patterns,
2364
+ normalizeTokenName: normalizeBindingTokenName,
2365
+ createMatch: createTextKeyMatch,
2366
+ parseObjectKey
2367
+ });
2368
+ if (!result) {
2369
+ continue;
2370
+ }
2371
+ if (result.nextIndex <= index || result.nextIndex > key.length) {
2372
+ throw new Error(`Keymap binding parser must advance the input for "${key}" at index ${index}`);
2373
+ }
2374
+ parts.push(...result.parts);
2375
+ for (const tokenName of result.usedTokens ?? []) {
2376
+ usedTokens.add(tokenName);
2377
+ }
2378
+ for (const tokenName of result.unknownTokens ?? []) {
2379
+ unknownTokens.add(tokenName);
2380
+ }
2381
+ index = result.nextIndex;
2382
+ matched = true;
2383
+ break;
2384
+ }
2385
+ if (!matched) {
2386
+ throw new Error(`No keymap binding parser handled input at index ${index} in "${key}"`);
2387
+ }
2388
+ }
2389
+ return {
2390
+ parts,
2391
+ usedTokens: [...usedTokens],
2392
+ unknownTokens: [...unknownTokens],
2393
+ hasTokenBindings: usedTokens.size > 0 || unknownTokens.size > 0
2394
+ };
2395
+ }
2396
+ function parseSingleKeyPartWithParsers(key, parsers, options) {
2397
+ if (typeof key !== "string") {
2398
+ return options.parseObjectKey(key);
2399
+ }
2400
+ const { parts } = parseBindingSequenceWithParsers(key, parsers, options);
2401
+ const [part] = parts;
2402
+ if (!part || parts.length !== 1) {
2403
+ throw new Error(`Invalid key "${String(key)}": expected a single key stroke`);
2404
+ }
2405
+ return part;
2406
+ }
2407
+
2408
+ // src/services/conditions.ts
2409
+ function isReactiveMatcher(value) {
2410
+ if (!value || typeof value !== "object") {
2411
+ return false;
2412
+ }
2413
+ const candidate = value;
2414
+ return typeof candidate.get === "function" && typeof candidate.subscribe === "function";
2415
+ }
2416
+ function createConditionService(state, notify) {
2417
+ const hasNoConditions = (target) => {
2418
+ return target.requires.length === 0 && target.matchers.length === 0;
2419
+ };
2420
+ const matchesRuntimeMatcher = (matcher) => {
2421
+ try {
2422
+ return matcher.match();
2423
+ } catch (error) {
2424
+ notify.emitError("runtime-matcher-error", error, `[Keymap] Error evaluating runtime matcher from ${matcher.source}:`);
2425
+ return false;
2426
+ }
2427
+ };
2428
+ const matchesRuntimeMatchers = (target) => {
2429
+ if (target.matchers.length === 0) {
2430
+ return true;
2431
+ }
2432
+ if (target.matchers.length === 1) {
2433
+ const [matcher] = target.matchers;
2434
+ return matcher ? matchesRuntimeMatcher(matcher) : true;
2435
+ }
2436
+ for (const matcher of target.matchers) {
2437
+ if (!matchesRuntimeMatcher(matcher)) {
2438
+ return false;
2439
+ }
2440
+ }
2441
+ return true;
2442
+ };
2443
+ const matchRequirements = (requires) => {
2444
+ if (requires.length === 0) {
2445
+ return true;
2446
+ }
2447
+ for (const [name, value] of requires) {
2448
+ if (!Object.is(state.data[name], value)) {
2449
+ return false;
2450
+ }
2451
+ }
2452
+ return true;
2453
+ };
2454
+ const matchesConditions = (target) => {
2455
+ return hasNoConditions(target) || matchRequirements(target.requires) && matchesRuntimeMatchers(target);
2456
+ };
2457
+ return {
2458
+ buildRuntimeMatcher(matcher, source) {
2459
+ if (typeof matcher === "function") {
2460
+ return { source, match: matcher };
2461
+ }
2462
+ if (isReactiveMatcher(matcher)) {
2463
+ return {
2464
+ source,
2465
+ match: () => matcher.get(),
2466
+ subscribe: (onChange) => matcher.subscribe(onChange)
2467
+ };
2468
+ }
2469
+ throw new Error(`Keymap ${source} expected a function or a reactive matcher`);
2470
+ },
2471
+ matchesConditions
2472
+ };
2473
+ }
2474
+
2475
+ // src/types.ts
2476
+ var KEY_DISAMBIGUATION_DECISION = Symbol("keymap-disambiguation-decision");
2477
+ var KEY_DEFERRED_DISAMBIGUATION_DECISION = Symbol("keymap-deferred-disambiguation-decision");
2478
+
2479
+ // src/services/dispatch-decisions.ts
2480
+ function createSyncDecision(action, handler) {
2481
+ return {
2482
+ [KEY_DISAMBIGUATION_DECISION]: true,
2483
+ action,
2484
+ handler
2485
+ };
2486
+ }
2487
+ function createDeferredDecision(action) {
2488
+ return {
2489
+ [KEY_DEFERRED_DISAMBIGUATION_DECISION]: true,
2490
+ action
2491
+ };
2492
+ }
2493
+ function isSyncDecision(value) {
2494
+ return !!value && typeof value === "object" && value[KEY_DISAMBIGUATION_DECISION] === true;
2495
+ }
2496
+ function isDeferredDecision(value) {
2497
+ return !!value && typeof value === "object" && value[KEY_DEFERRED_DISAMBIGUATION_DECISION] === true;
2498
+ }
2499
+ function sleepWithSignal(ms, signal) {
2500
+ if (signal.aborted) {
2501
+ return Promise.resolve(false);
2502
+ }
2503
+ return new Promise((resolve) => {
2504
+ const timeout = setTimeout(() => {
2505
+ signal.removeEventListener("abort", onAbort);
2506
+ resolve(true);
2507
+ }, Math.max(0, ms));
2508
+ const onAbort = () => {
2509
+ clearTimeout(timeout);
2510
+ signal.removeEventListener("abort", onAbort);
2511
+ resolve(false);
2512
+ };
2513
+ signal.addEventListener("abort", onAbort, { once: true });
2514
+ });
2515
+ }
2516
+
2517
+ // src/services/dispatch-patterns.ts
2518
+ function matchSequencePattern(patterns, notify, patternName, event) {
2519
+ const pattern = patterns.get(patternName);
2520
+ if (!pattern) {
2521
+ return;
2522
+ }
2523
+ try {
2524
+ return pattern.matcher(event);
2525
+ } catch (error) {
2526
+ notify.emitError("sequence-pattern-match-error", error, `[Keymap] Error matching sequence pattern "${pattern.name}":`);
2527
+ return;
2528
+ }
2529
+ }
2530
+ function createPatternEventPart(patterns, event, patternName, match) {
2531
+ const pattern = patterns.get(patternName);
2532
+ const payloadKey = pattern?.payloadKey ?? patternName;
2533
+ const part = createKeySequencePart({
2534
+ name: event.name,
2535
+ ctrl: event.ctrl,
2536
+ shift: event.shift,
2537
+ meta: event.meta,
2538
+ super: event.super ?? false,
2539
+ hyper: event.hyper || undefined
2540
+ }, { display: match.display ?? String(match.value ?? event.name) });
2541
+ return { ...part, patternName, payloadKey };
2542
+ }
2543
+ function createSequencePayload(patterns, notify, capture) {
2544
+ if (!capture?.patterns || capture.patterns.length === 0) {
2545
+ return;
2546
+ }
2547
+ const payload = {};
2548
+ let hasPayload = false;
2549
+ for (const captured of capture.patterns) {
2550
+ const pattern = patterns.get(captured.name);
2551
+ let value;
2552
+ try {
2553
+ value = pattern?.finalize ? pattern.finalize(captured.values) : captured.values.length === 1 ? captured.values[0] : [...captured.values];
2554
+ } catch (error) {
2555
+ notify.emitError("sequence-pattern-finalize-error", error, `[Keymap] Error finalizing sequence pattern "${captured.name}":`);
2556
+ continue;
2557
+ }
2558
+ const existing = payload[captured.payloadKey];
2559
+ if (existing === undefined) {
2560
+ payload[captured.payloadKey] = value;
2561
+ hasPayload = true;
2562
+ } else if (Array.isArray(existing)) {
2563
+ existing.push(value);
2564
+ } else {
2565
+ payload[captured.payloadKey] = [existing, value];
2566
+ }
2567
+ }
2568
+ return hasPayload ? payload : undefined;
2569
+ }
2570
+
2571
+ // src/services/dispatch.ts
2572
+ function createDispatchService(state, notify, runtime, activation, conditions, executor, compiler, catalog, layers, hooks) {
2573
+ const eventMatchResolverContext = {
2574
+ resolveKey: (key) => compiler.parseTokenKey(key).match
2575
+ };
2576
+ let pendingDisambiguation = null;
2577
+ let nextPendingDisambiguationId = 0;
2578
+ function intercept(name, fn, options) {
2579
+ if (name === "key") {
2580
+ const keyOptions = options;
2581
+ return state.keyHooks.register(fn, {
2582
+ priority: keyOptions?.priority ?? 0,
2583
+ release: keyOptions?.release ?? false
2584
+ });
2585
+ }
2586
+ if (name === "key:after") {
2587
+ const keyOptions = options;
2588
+ return state.keyAfterHooks.register(fn, {
2589
+ priority: keyOptions?.priority ?? 0,
2590
+ release: keyOptions?.release ?? false
2591
+ });
2592
+ }
2593
+ const rawOptions = options;
2594
+ return state.rawHooks.register(fn, {
2595
+ priority: rawOptions?.priority ?? 0
2596
+ });
2597
+ }
2598
+ function prependEventMatchResolver(resolver) {
2599
+ return state.eventMatchResolvers.prepend(resolver);
2600
+ }
2601
+ function appendEventMatchResolver(resolver) {
2602
+ return state.eventMatchResolvers.append(resolver);
2603
+ }
2604
+ function clearEventMatchResolvers() {
2605
+ state.eventMatchResolvers.clear();
2606
+ }
2607
+ function prependDisambiguationResolver(resolver) {
2608
+ return mutateDisambiguationResolvers(() => state.disambiguationResolvers.prepend(resolver), resolver);
2609
+ }
2610
+ function appendDisambiguationResolver(resolver) {
2611
+ return mutateDisambiguationResolvers(() => state.disambiguationResolvers.append(resolver), resolver);
2612
+ }
2613
+ function clearDisambiguationResolvers() {
2614
+ if (!state.disambiguationResolvers.has()) {
2615
+ return;
2616
+ }
2617
+ notify.runWithStateChangeBatch(() => {
2618
+ state.disambiguationResolvers.clear();
2619
+ layers.recompileBindings();
2620
+ });
2621
+ }
2622
+ function handlePendingSequenceChange(_previous, _next) {
2623
+ if (!pendingDisambiguation) {
2624
+ return;
2625
+ }
2626
+ cancelPendingDisambiguation();
2627
+ }
2628
+ function handleRawSequence(sequence) {
2629
+ const hooks2 = state.rawHooks.entries();
2630
+ if (hooks2.length === 0) {
2631
+ return false;
2632
+ }
2633
+ let stopped = false;
2634
+ const context = {
2635
+ sequence,
2636
+ stop() {
2637
+ stopped = true;
2638
+ }
2639
+ };
2640
+ for (const hook of hooks2) {
2641
+ try {
2642
+ hook.listener(context);
2643
+ } catch (error) {
2644
+ notify.emitError("raw-intercept-error", error, "[Keymap] Error in raw intercept listener:");
2645
+ }
2646
+ if (stopped) {
2647
+ return true;
2648
+ }
2649
+ }
2650
+ return false;
2651
+ }
2652
+ function createDispatchBinding(binding, focused) {
2653
+ return {
2654
+ sequence: cloneKeySequence(binding.sequence),
2655
+ command: binding.command,
2656
+ commandAttrs: catalog.getBindingCommandAttrs(binding, focused, catalog.getActiveCommandView(focused)),
2657
+ attrs: binding.attrs,
2658
+ event: binding.event,
2659
+ preventDefault: binding.preventDefault,
2660
+ fallthrough: binding.fallthrough,
2661
+ sourceLayerOrder: binding.sourceLayerOrder,
2662
+ bindingIndex: binding.bindingIndex
2663
+ };
2664
+ }
2665
+ function emitDispatchEvent(event) {
2666
+ if (!hooks.has("dispatch")) {
2667
+ return;
2668
+ }
2669
+ hooks.emit("dispatch", event);
2670
+ }
2671
+ function emitBindingDispatch(phase, layer, binding, focused) {
2672
+ if (!hooks.has("dispatch")) {
2673
+ return;
2674
+ }
2675
+ emitDispatchEvent({
2676
+ phase,
2677
+ event: binding.event,
2678
+ focused,
2679
+ layer: {
2680
+ order: layer.order,
2681
+ priority: layer.priority,
2682
+ target: layer.target,
2683
+ targetMode: layer.targetMode
2684
+ },
2685
+ binding: createDispatchBinding(binding, focused),
2686
+ sequence: cloneKeySequence(binding.sequence),
2687
+ command: binding.command
2688
+ });
2689
+ }
2690
+ function emitSequenceDispatch(phase, captures, focused) {
2691
+ if (!hooks.has("dispatch")) {
2692
+ return;
2693
+ }
2694
+ const first = captures[0];
2695
+ const sequence = captures.length > 0 ? activation.collectSequencePartsFromPending({ captures }) : [];
2696
+ emitDispatchEvent({
2697
+ phase,
2698
+ event: "press",
2699
+ focused,
2700
+ layer: first ? {
2701
+ order: first.layer.order,
2702
+ priority: first.layer.priority,
2703
+ target: first.layer.target,
2704
+ targetMode: first.layer.targetMode
2705
+ } : undefined,
2706
+ sequence
2707
+ });
2708
+ }
2709
+ function getKeyAfterHooks(release) {
2710
+ const hooks2 = state.keyAfterHooks.entries();
2711
+ for (const hook of hooks2) {
2712
+ if (hook.release === release) {
2713
+ return hooks2;
2714
+ }
2715
+ }
2716
+ return;
2717
+ }
2718
+ function getOutcomeSequence(outcome) {
2719
+ if (outcome.sequence) {
2720
+ return cloneKeySequence(outcome.sequence);
2721
+ }
2722
+ if (outcome.captures) {
2723
+ return activation.collectSequencePartsFromPending({ captures: outcome.captures });
2724
+ }
2725
+ return [];
2726
+ }
2727
+ function createSequenceOutcome(reason, captures) {
2728
+ return {
2729
+ handled: reason !== "sequence-miss",
2730
+ reason,
2731
+ captures
2732
+ };
2733
+ }
2734
+ function createBindingOutcome(binding, handled) {
2735
+ return {
2736
+ handled,
2737
+ reason: handled ? "binding-handled" : "binding-rejected",
2738
+ sequence: binding.sequence
2739
+ };
2740
+ }
2741
+ function preferDispatchOutcome(current, next) {
2742
+ if (next.handled || current.reason === "no-match") {
2743
+ return next;
2744
+ }
2745
+ return current;
2746
+ }
2747
+ function emitKeyAfter(hooks2, event, release, focused, outcome) {
2748
+ const context = {
2749
+ event,
2750
+ eventType: release ? "release" : "press",
2751
+ focused,
2752
+ handled: outcome.handled,
2753
+ reason: outcome.reason,
2754
+ sequence: getOutcomeSequence(outcome),
2755
+ pendingSequence: activation.getPendingSequence(),
2756
+ setData: (name, value) => {
2757
+ runtime.setData(name, value);
2758
+ },
2759
+ getData: (name) => {
2760
+ return runtime.getData(name);
2761
+ },
2762
+ consume: (options) => {
2763
+ const shouldPreventDefault = options?.preventDefault ?? true;
2764
+ const shouldStopPropagation = options?.stopPropagation ?? true;
2765
+ if (shouldPreventDefault) {
2766
+ event.preventDefault();
2767
+ }
2768
+ if (shouldStopPropagation) {
2769
+ event.stopPropagation();
2770
+ }
2771
+ }
2772
+ };
2773
+ for (const hook of hooks2) {
2774
+ if (hook.release !== release) {
2775
+ continue;
2776
+ }
2777
+ try {
2778
+ hook.listener(context);
2779
+ } catch (error) {
2780
+ notify.emitError("key-after-intercept-error", error, "[Keymap] Error in key:after intercept listener:");
2781
+ }
2782
+ }
2783
+ }
2784
+ function noMatchOutcome() {
2785
+ return { handled: false, reason: "no-match" };
2786
+ }
2787
+ function consumeSequenceEvent(event) {
2788
+ event.preventDefault();
2789
+ event.stopPropagation();
2790
+ }
2791
+ function holdSequence(phase, captures, focused, event) {
2792
+ activation.setPendingSequence({ captures });
2793
+ const outcome = createSequenceOutcome("sequence-pending", captures);
2794
+ emitSequenceDispatch(phase, captures, focused);
2795
+ consumeSequenceEvent(event);
2796
+ return outcome;
2797
+ }
2798
+ function clearSequence(reason, captures, focused, event) {
2799
+ const outcome = createSequenceOutcome(reason, captures);
2800
+ emitSequenceDispatch("sequence-clear", captures, focused);
2801
+ activation.setPendingSequence(null);
2802
+ consumeSequenceEvent(event);
2803
+ return outcome;
2804
+ }
2805
+ function handleKeyEvent(event, release) {
2806
+ if (!release) {
2807
+ cancelPendingDisambiguation();
2808
+ }
2809
+ const afterHooks = getKeyAfterHooks(release);
2810
+ const afterFocused = afterHooks ? activation.getFocusedTarget() : null;
2811
+ const hooks2 = state.keyHooks.entries();
2812
+ const context = {
2813
+ event,
2814
+ setData: (name, value) => {
2815
+ runtime.setData(name, value);
2816
+ },
2817
+ getData: (name) => {
2818
+ return runtime.getData(name);
2819
+ },
2820
+ consume: (options) => {
2821
+ const shouldPreventDefault = options?.preventDefault ?? true;
2822
+ const shouldStopPropagation = options?.stopPropagation ?? true;
2823
+ if (shouldPreventDefault) {
2824
+ event.preventDefault();
2825
+ }
2826
+ if (shouldStopPropagation) {
2827
+ event.stopPropagation();
2828
+ }
2829
+ }
2830
+ };
2831
+ for (const hook of hooks2) {
2832
+ if (hook.release !== release) {
2833
+ continue;
2834
+ }
2835
+ try {
2836
+ hook.listener(context);
2837
+ } catch (error) {
2838
+ notify.emitError("key-intercept-error", error, "[Keymap] Error in key intercept listener:");
2839
+ }
2840
+ if (event.propagationStopped) {
2841
+ if (afterHooks) {
2842
+ emitKeyAfter(afterHooks, event, release, afterFocused, {
2843
+ handled: true,
2844
+ reason: "intercept-consumed",
2845
+ sequence: []
2846
+ });
2847
+ }
2848
+ return;
2849
+ }
2850
+ }
2851
+ if (release) {
2852
+ const outcome2 = dispatchReleaseLayers(event);
2853
+ if (afterHooks) {
2854
+ emitKeyAfter(afterHooks, event, release, afterFocused, outcome2);
2855
+ }
2856
+ return;
2857
+ }
2858
+ const outcome = dispatchLayers(event);
2859
+ if (afterHooks) {
2860
+ emitKeyAfter(afterHooks, event, release, afterFocused, outcome);
2861
+ }
2862
+ }
2863
+ function mutateDisambiguationResolvers(register, resolver) {
2864
+ return notify.runWithStateChangeBatch(() => {
2865
+ const hadResolvers = state.disambiguationResolvers.has();
2866
+ const off = register();
2867
+ if (!hadResolvers && state.disambiguationResolvers.has()) {
2868
+ layers.recompileBindings();
2869
+ }
2870
+ return () => {
2871
+ notify.runWithStateChangeBatch(() => {
2872
+ const hadBeforeRemoval = state.disambiguationResolvers.has();
2873
+ off();
2874
+ if (state.disambiguationResolvers.values().includes(resolver)) {
2875
+ return;
2876
+ }
2877
+ if (hadBeforeRemoval && !state.disambiguationResolvers.has()) {
2878
+ layers.recompileBindings();
2879
+ }
2880
+ });
2881
+ };
2882
+ });
2883
+ }
2884
+ function dispatchReleaseLayers(event) {
2885
+ const focused = activation.getFocusedTarget();
2886
+ const activeLayers = activation.getActiveLayers(focused);
2887
+ const matchKeys = resolveEventMatchKeys(event);
2888
+ let outcome = noMatchOutcome();
2889
+ layerLoop:
2890
+ for (const layer of activeLayers) {
2891
+ if (layer.bindings.length === 0) {
2892
+ continue;
2893
+ }
2894
+ if (!conditions.matchesConditions(layer)) {
2895
+ continue;
2896
+ }
2897
+ for (const strokeKey of matchKeys) {
2898
+ const result = runReleaseBindings(layer, strokeKey, event, focused);
2899
+ outcome = preferDispatchOutcome(outcome, result.outcome);
2900
+ if (!result.handled) {
2901
+ continue;
2902
+ }
2903
+ if (result.stop) {
2904
+ return outcome;
2905
+ }
2906
+ continue layerLoop;
2907
+ }
2908
+ }
2909
+ return outcome;
2910
+ }
2911
+ function dispatchLayers(event) {
2912
+ const focused = activation.getFocusedTarget();
2913
+ const pending = activation.ensureValidPendingSequence();
2914
+ const matchKeys = resolveEventMatchKeys(event);
2915
+ if (pending) {
2916
+ return dispatchPendingSequence(pending, matchKeys, event, focused);
2917
+ }
2918
+ const activeLayers = activation.getActiveLayers(focused);
2919
+ return dispatchFromRoot(activeLayers, matchKeys, event, focused);
2920
+ }
2921
+ function dispatchPendingSequence(pending, matchKeys, event, focused) {
2922
+ const activeView = catalog.getActiveCommandView(focused);
2923
+ const advancedCaptures = [];
2924
+ for (const capture of pending.captures) {
2925
+ const advanced = advanceSequenceCapture(capture, matchKeys, event, state.patterns, matchPattern, createPatternEventPart2);
2926
+ if (!advanced) {
2927
+ continue;
2928
+ }
2929
+ advancedCaptures.push(advanced);
2930
+ }
2931
+ const bestPriority = advancedCaptures.reduce((best, capture) => Math.min(best, capturePriority(capture, matchKeys)), Number.POSITIVE_INFINITY);
2932
+ const prioritizedCaptures = advancedCaptures.filter((capture) => capturePriority(capture, matchKeys) === bestPriority);
2933
+ if (prioritizedCaptures.length === 0 || !prioritizedCaptures.some((capture) => captureIsReachable(capture, focused, activeView))) {
2934
+ const outcome = createSequenceOutcome("sequence-miss", pending.captures);
2935
+ emitSequenceDispatch("sequence-clear", pending.captures, focused);
2936
+ activation.setPendingSequence(null);
2937
+ return outcome;
2938
+ }
2939
+ return dispatchPendingCapturesFromIndex(prioritizedCaptures, 0, false, event, focused);
2940
+ }
2941
+ function dispatchPendingCapturesFromIndex(advancedCaptures, startIndex, handledExact, event, focused) {
2942
+ let hasHandledExact = handledExact;
2943
+ let outcome = noMatchOutcome();
2944
+ const processedExact = new Set;
2945
+ for (let index = startIndex;index < advancedCaptures.length; index += 1) {
2946
+ const capture = advancedCaptures[index];
2947
+ if (!capture || processedExact.has(capture)) {
2948
+ continue;
2949
+ }
2950
+ const continuationCapturesForPrefix = collectContinuationCapturesForPrefix(advancedCaptures, index, capture);
2951
+ if (continuationCapturesForPrefix.length > 0) {
2952
+ if (hasHandledExact) {
2953
+ continue;
2954
+ }
2955
+ const exactCaptures2 = collectExactCapturesForPrefix(advancedCaptures, capture);
2956
+ const resolvedOutcome = tryResolvePendingAmbiguity(advancedCaptures, index, continuationCapturesForPrefix, exactCaptures2, event, focused, hasHandledExact);
2957
+ if (resolvedOutcome) {
2958
+ return resolvedOutcome;
2959
+ }
2960
+ return holdSequence("sequence-advance", continuationCapturesForPrefix, focused, event);
2961
+ }
2962
+ if (!captureIsExact(capture, state.patterns)) {
2963
+ continue;
2964
+ }
2965
+ const exactCaptures = collectExactCapturesForPrefix(advancedCaptures, capture);
2966
+ for (const exact of exactCaptures)
2967
+ processedExact.add(exact);
2968
+ const result = runCaptureBindings(capture.layer, exactCaptures, event, focused);
2969
+ outcome = preferDispatchOutcome(outcome, result.outcome);
2970
+ if (!result.handled) {
2971
+ continue;
2972
+ }
2973
+ hasHandledExact = true;
2974
+ if (result.stop) {
2975
+ emitSequenceDispatch("sequence-clear", advancedCaptures, focused);
2976
+ activation.setPendingSequence(null);
2977
+ return outcome;
2978
+ }
2979
+ }
2980
+ emitSequenceDispatch("sequence-clear", advancedCaptures, focused);
2981
+ activation.setPendingSequence(null);
2982
+ return outcome;
2983
+ }
2984
+ function dispatchFromRoot(activeLayers, matchKeys, event, focused) {
2985
+ return dispatchFromRootAtIndex(activeLayers, 0, matchKeys, event, focused);
2986
+ }
2987
+ function dispatchFromRootAtIndex(activeLayers, startIndex, matchKeys, event, focused) {
2988
+ const activeView = catalog.getActiveCommandView(focused);
2989
+ let outcome = noMatchOutcome();
2990
+ for (let index = startIndex;index < activeLayers.length; index += 1) {
2991
+ const layer = activeLayers[index];
2992
+ if (!layer) {
2993
+ continue;
2994
+ }
2995
+ if (!conditions.matchesConditions(layer)) {
2996
+ continue;
2997
+ }
2998
+ const captures = collectRootCaptures(layer, matchKeys, event, focused, activeView);
2999
+ if (captures.length === 0) {
3000
+ continue;
3001
+ }
3002
+ const layerContinuationCaptures = captures.filter((capture) => captureHasContinuations(capture, state.patterns, false));
3003
+ if (layerContinuationCaptures.length > 0) {
3004
+ const exactCaptures2 = captures.filter((capture) => captureIsExact(capture, state.patterns));
3005
+ const continuationCaptures = collectPendingCapturesFromRoot(activeLayers, index, matchKeys, event, focused);
3006
+ const resolvedOutcome = tryResolveRootAmbiguity(activeLayers, index, matchKeys, continuationCaptures, exactCaptures2, event, focused);
3007
+ if (resolvedOutcome) {
3008
+ return resolvedOutcome;
3009
+ }
3010
+ return holdSequence("sequence-start", continuationCaptures, focused, event);
3011
+ }
3012
+ const exactCaptures = captures.filter((capture) => captureIsExact(capture, state.patterns));
3013
+ const result = runCaptureBindings(layer, exactCaptures, event, focused);
3014
+ outcome = preferDispatchOutcome(outcome, result.outcome);
3015
+ if (!result.handled) {
3016
+ continue;
3017
+ }
3018
+ if (result.stop) {
3019
+ return outcome;
3020
+ }
3021
+ }
3022
+ return outcome;
3023
+ }
3024
+ function tryResolveRootAmbiguity(activeLayers, layerIndex, matchKeys, continuationCaptures, exactCaptures, event, focused) {
3025
+ const applyExact = () => {
3026
+ activation.setPendingSequence(null);
3027
+ const layer = exactCaptures[0]?.layer;
3028
+ if (!layer)
3029
+ return noMatchOutcome();
3030
+ const result = runCaptureBindings(layer, exactCaptures, event, focused);
3031
+ if (!result.stop) {
3032
+ return preferDispatchOutcome(result.outcome, dispatchFromRootAtIndex(activeLayers, layerIndex + 1, matchKeys, event, focused));
3033
+ }
3034
+ return result.outcome;
3035
+ };
3036
+ return tryResolveAmbiguity({
3037
+ event,
3038
+ focused,
3039
+ continuationCaptures,
3040
+ exactBindingsSource: exactCaptures.map((capture) => capture.binding),
3041
+ sequencePhase: "sequence-start",
3042
+ runExact: applyExact
3043
+ });
3044
+ }
3045
+ function tryResolvePendingAmbiguity(advancedCaptures, captureIndex, continuationCaptures, exactCaptures, event, focused, handledExact) {
3046
+ const applyExact = () => {
3047
+ activation.setPendingSequence(null);
3048
+ const layer = exactCaptures[0]?.layer;
3049
+ if (!layer)
3050
+ return noMatchOutcome();
3051
+ const result = runCaptureBindings(layer, exactCaptures, event, focused);
3052
+ if (result.stop) {
3053
+ return result.outcome;
3054
+ }
3055
+ return preferDispatchOutcome(result.outcome, dispatchPendingCapturesFromIndex(advancedCaptures, captureIndex + 1, handledExact || result.handled, event, focused));
3056
+ };
3057
+ return tryResolveAmbiguity({
3058
+ event,
3059
+ focused,
3060
+ continuationCaptures,
3061
+ exactBindingsSource: exactCaptures.map((capture) => capture.binding),
3062
+ sequencePhase: "sequence-advance",
3063
+ runExact: applyExact
3064
+ });
3065
+ }
3066
+ function tryResolveAmbiguity(options) {
3067
+ const { event, focused, continuationCaptures, exactBindingsSource, sequencePhase, runExact } = options;
3068
+ if (!state.disambiguationResolvers.has() || continuationCaptures.length === 0) {
3069
+ return;
3070
+ }
3071
+ const activeView = catalog.getActiveCommandView(focused);
3072
+ const exactBindings = activation.collectMatchingBindings(exactBindingsSource, focused, activeView);
3073
+ if (!exactBindings.some((binding) => binding.command !== undefined)) {
3074
+ return;
3075
+ }
3076
+ const continueSequence = () => {
3077
+ return holdSequence(sequencePhase, continuationCaptures, focused, event);
3078
+ };
3079
+ const clear = () => {
3080
+ return clearSequence("sequence-cleared", continuationCaptures, focused, event);
3081
+ };
3082
+ let sequence;
3083
+ const getSequence = () => {
3084
+ sequence ??= activation.collectSequencePartsFromPending({ captures: continuationCaptures });
3085
+ return sequence;
3086
+ };
3087
+ const decision = resolveDisambiguation({
3088
+ event,
3089
+ focused,
3090
+ getSequence,
3091
+ exactBindings,
3092
+ continuationCaptures,
3093
+ activeView
3094
+ });
3095
+ if (!decision) {
3096
+ warnUnresolvedAmbiguity(getSequence());
3097
+ return continueSequence();
3098
+ }
3099
+ return applySyncDecision(decision, continuationCaptures, runExact, continueSequence, clear, focused, getSequence);
3100
+ }
3101
+ function applySyncDecision(decision, continuationCaptures, runExact, continueSequence, clear, focused, getSequence) {
3102
+ if (decision.action === "run-exact") {
3103
+ return runExact();
3104
+ }
3105
+ if (decision.action === "continue-sequence") {
3106
+ return continueSequence();
3107
+ }
3108
+ if (decision.action === "clear") {
3109
+ return clear();
3110
+ }
3111
+ const outcome = continueSequence();
3112
+ scheduleDeferredDisambiguation(continuationCaptures, decision.handler, focused, getSequence(), (nextDecision) => {
3113
+ if (!nextDecision) {
3114
+ return;
3115
+ }
3116
+ if (nextDecision.action === "run-exact") {
3117
+ runExact();
3118
+ return;
3119
+ }
3120
+ if (nextDecision.action === "continue-sequence") {
3121
+ continueSequence();
3122
+ return;
3123
+ }
3124
+ clear();
3125
+ });
3126
+ return outcome;
3127
+ }
3128
+ function resolveDisambiguation(options) {
3129
+ let sequence;
3130
+ let exact;
3131
+ let continuations;
3132
+ let strokePart;
3133
+ const ctx = {
3134
+ event: options.event,
3135
+ focused: options.focused,
3136
+ get sequence() {
3137
+ sequence ??= cloneKeySequence(options.getSequence());
3138
+ return sequence;
3139
+ },
3140
+ get stroke() {
3141
+ const stroke = options.getSequence().at(-1);
3142
+ if (!stroke) {
3143
+ throw new Error("Disambiguation context expected a non-empty sequence");
3144
+ }
3145
+ strokePart ??= {
3146
+ ...stroke,
3147
+ stroke: cloneKeyStroke(stroke.stroke)
3148
+ };
3149
+ return strokePart;
3150
+ },
3151
+ get exact() {
3152
+ exact ??= activation.collectActiveBindings(options.exactBindings, options.focused, options.activeView).map((binding) => ({
3153
+ ...binding,
3154
+ sequence: cloneKeySequence(binding.sequence)
3155
+ }));
3156
+ return exact;
3157
+ },
3158
+ get continuations() {
3159
+ continuations ??= activation.getActiveKeysForCaptures(options.continuationCaptures, {
3160
+ includeBindings: true,
3161
+ includeMetadata: true
3162
+ });
3163
+ return continuations;
3164
+ },
3165
+ getData: (name) => {
3166
+ return runtime.getData(name);
3167
+ },
3168
+ setData: (name, value) => {
3169
+ runtime.setData(name, value);
3170
+ },
3171
+ runExact: () => createSyncDecision("run-exact"),
3172
+ continueSequence: () => createSyncDecision("continue-sequence"),
3173
+ clear: () => createSyncDecision("clear"),
3174
+ defer: (run) => createSyncDecision("defer", run)
3175
+ };
3176
+ for (const resolver of state.disambiguationResolvers.values()) {
3177
+ let result;
3178
+ try {
3179
+ result = resolver(ctx);
3180
+ } catch (error) {
3181
+ notify.emitError("disambiguation-resolver-error", error, "[Keymap] Error in disambiguation resolver:");
3182
+ continue;
3183
+ }
3184
+ if (result === undefined) {
3185
+ continue;
3186
+ }
3187
+ if (isPromiseLike(result)) {
3188
+ notify.emitError("invalid-disambiguation-resolver-return", result, "[Keymap] Disambiguation resolvers must return synchronously; use ctx.defer(...) for async handling");
3189
+ continue;
3190
+ }
3191
+ if (!isSyncDecision(result)) {
3192
+ notify.emitError("invalid-disambiguation-decision", result, "[Keymap] Invalid disambiguation decision returned by resolver:");
3193
+ continue;
3194
+ }
3195
+ return result;
3196
+ }
3197
+ return;
3198
+ }
3199
+ function scheduleDeferredDisambiguation(captures, handler, focused, sequence, apply) {
3200
+ cancelPendingDisambiguation();
3201
+ const controller = new AbortController;
3202
+ const pending = {
3203
+ id: nextPendingDisambiguationId++,
3204
+ controller,
3205
+ captures,
3206
+ apply
3207
+ };
3208
+ pendingDisambiguation = pending;
3209
+ queueMicrotask(() => {
3210
+ executeDeferredDisambiguation(pending, handler, focused, sequence);
3211
+ });
3212
+ }
3213
+ function executeDeferredDisambiguation(pending, handler, focused, sequence) {
3214
+ if (!isPendingDisambiguationCurrent(pending)) {
3215
+ return;
3216
+ }
3217
+ const ctx = {
3218
+ signal: pending.controller.signal,
3219
+ sequence: cloneKeySequence(sequence),
3220
+ focused,
3221
+ sleep: (ms) => {
3222
+ return sleepWithSignal(ms, pending.controller.signal);
3223
+ },
3224
+ runExact: () => createDeferredDecision("run-exact"),
3225
+ continueSequence: () => createDeferredDecision("continue-sequence"),
3226
+ clear: () => createDeferredDecision("clear")
3227
+ };
3228
+ let result;
3229
+ try {
3230
+ result = handler(ctx);
3231
+ } catch (error) {
3232
+ if (isPendingDisambiguationCurrent(pending)) {
3233
+ notify.emitError("deferred-disambiguation-error", error, "[Keymap] Error in deferred disambiguation handler:");
3234
+ finishPendingDisambiguation(pending);
3235
+ }
3236
+ return;
3237
+ }
3238
+ if (isPromiseLike(result)) {
3239
+ result.then((resolved) => {
3240
+ applyDeferredDisambiguationResult(pending, resolved);
3241
+ }).catch((error) => {
3242
+ if (!isPendingDisambiguationCurrent(pending)) {
3243
+ return;
3244
+ }
3245
+ notify.emitError("deferred-disambiguation-error", error, "[Keymap] Error in deferred disambiguation handler:");
3246
+ finishPendingDisambiguation(pending);
3247
+ });
3248
+ return;
3249
+ }
3250
+ applyDeferredDisambiguationResult(pending, result);
3251
+ }
3252
+ function applyDeferredDisambiguationResult(pending, result) {
3253
+ if (!isPendingDisambiguationCurrent(pending)) {
3254
+ return;
3255
+ }
3256
+ if (result !== undefined && !isDeferredDecision(result)) {
3257
+ notify.emitError("invalid-deferred-disambiguation-decision", result, "[Keymap] Invalid deferred disambiguation decision returned by handler:");
3258
+ finishPendingDisambiguation(pending);
3259
+ return;
3260
+ }
3261
+ finishPendingDisambiguation(pending);
3262
+ pending.apply(result);
3263
+ }
3264
+ function finishPendingDisambiguation(pending) {
3265
+ if (!isPendingDisambiguationCurrent(pending)) {
3266
+ return;
3267
+ }
3268
+ pendingDisambiguation = null;
3269
+ }
3270
+ function cancelPendingDisambiguation() {
3271
+ const pending = pendingDisambiguation;
3272
+ if (!pending) {
3273
+ return;
3274
+ }
3275
+ pendingDisambiguation = null;
3276
+ pending.controller.abort();
3277
+ }
3278
+ function isPendingDisambiguationCurrent(pending) {
3279
+ return pendingDisambiguation === pending;
3280
+ }
3281
+ function warnUnresolvedAmbiguity(sequence) {
3282
+ const display = stringifyKeySequence(sequence, { preferDisplay: true });
3283
+ notify.warnOnce(`unresolved-disambiguation:${display}`, "unresolved-disambiguation", { sequence: display }, `[Keymap] Ambiguous exact/prefix sequence "${display}" fell back to prefix handling because no disambiguation resolver resolved it`);
3284
+ }
3285
+ function collectPendingCapturesFromRoot(activeLayers, startIndex, matchKeys, event, focused) {
3286
+ const captures = [];
3287
+ const activeView = catalog.getActiveCommandView(focused);
3288
+ for (let index = startIndex;index < activeLayers.length; index += 1) {
3289
+ const layer = activeLayers[index];
3290
+ if (!layer) {
3291
+ continue;
3292
+ }
3293
+ if (!conditions.matchesConditions(layer)) {
3294
+ continue;
3295
+ }
3296
+ for (const capture of collectRootCaptures(layer, matchKeys, event, focused, activeView)) {
3297
+ if (captureHasContinuations(capture, state.patterns, false)) {
3298
+ captures.push(capture);
3299
+ }
3300
+ }
3301
+ }
3302
+ return captures;
3303
+ }
3304
+ function collectContinuationCapturesForPrefix(captures, startIndex, prefix) {
3305
+ return captures.filter((candidate, candidateIndex) => {
3306
+ return candidateIndex >= startIndex && captureHasContinuations(candidate, state.patterns, false) && sameParts(candidate.parts, prefix.parts);
3307
+ });
3308
+ }
3309
+ function collectExactCapturesForPrefix(captures, prefix) {
3310
+ return captures.filter((capture) => {
3311
+ return capture.layer === prefix.layer && captureIsExact(capture, state.patterns) && sameParts(capture.parts, prefix.parts);
3312
+ });
3313
+ }
3314
+ function resolveEventMatchKeys(event) {
3315
+ const resolvers = state.eventMatchResolvers.values();
3316
+ if (resolvers.length === 0) {
3317
+ return [];
3318
+ }
3319
+ if (resolvers.length === 1) {
3320
+ return resolveSingleEventMatchKeys(resolvers[0], event, eventMatchResolverContext, notify);
3321
+ }
3322
+ const keys = [];
3323
+ const seen = new Set;
3324
+ for (const resolver of resolvers) {
3325
+ let resolved;
3326
+ try {
3327
+ resolved = resolver(event, eventMatchResolverContext);
3328
+ } catch (error) {
3329
+ notify.emitError("event-match-resolver-error", error, "[Keymap] Error in event match resolver:");
3330
+ continue;
3331
+ }
3332
+ if (!resolved || resolved.length === 0) {
3333
+ continue;
3334
+ }
3335
+ for (const candidate of resolved) {
3336
+ if (typeof candidate !== "string") {
3337
+ notify.emitError("invalid-event-match-resolver-candidate", candidate, "[Keymap] Invalid event match resolver candidate:");
3338
+ continue;
3339
+ }
3340
+ if (seen.has(candidate)) {
3341
+ continue;
3342
+ }
3343
+ seen.add(candidate);
3344
+ keys.push(candidate);
3345
+ }
3346
+ }
3347
+ return keys;
3348
+ }
3349
+ function runReleaseBindings(layer, strokeKey, event, focused) {
3350
+ let handled = false;
3351
+ let outcome = noMatchOutcome();
3352
+ for (const binding of layer.bindings) {
3353
+ if (binding.event !== "release") {
3354
+ continue;
3355
+ }
3356
+ const firstPart = binding.sequence[0];
3357
+ if (!firstPart || firstPart.match !== strokeKey) {
3358
+ continue;
3359
+ }
3360
+ if (!conditions.matchesConditions(binding)) {
3361
+ continue;
3362
+ }
3363
+ const bindingHandled = executor.runBinding(layer, binding, event, focused);
3364
+ outcome = preferDispatchOutcome(outcome, createBindingOutcome(binding, bindingHandled));
3365
+ if (!bindingHandled) {
3366
+ emitBindingDispatch("binding-reject", layer, binding, focused);
3367
+ continue;
3368
+ }
3369
+ emitBindingDispatch("binding-execute", layer, binding, focused);
3370
+ handled = true;
3371
+ if (!binding.fallthrough) {
3372
+ return { handled: true, stop: true, outcome };
3373
+ }
3374
+ }
3375
+ return { handled, stop: false, outcome };
3376
+ }
3377
+ function matchPattern(patternName, event) {
3378
+ return matchSequencePattern(state.patterns, notify, patternName, event);
3379
+ }
3380
+ function createPatternEventPart2(event, patternName, match) {
3381
+ return createPatternEventPart(state.patterns, event, patternName, match);
3382
+ }
3383
+ function collectRootCaptures(layer, matchKeys, event, focused, activeView) {
3384
+ const captures = collectRootSequenceCaptures(layer, matchKeys, event, matchPattern, createPatternEventPart2);
3385
+ return captures.some((capture) => captureIsReachable(capture, focused, activeView)) ? captures : [];
3386
+ }
3387
+ function createSequencePayload2(capture) {
3388
+ return createSequencePayload(state.patterns, notify, capture);
3389
+ }
3390
+ function bindingMatchesRuntimeState(binding, focused, activeView) {
3391
+ return conditions.matchesConditions(binding) && catalog.isBindingVisible(binding, focused, activeView);
3392
+ }
3393
+ function captureIsReachable(capture, focused, activeView) {
3394
+ return bindingMatchesRuntimeState(capture.binding, focused, activeView);
3395
+ }
3396
+ function runCaptureBindings(layer, captures, event, focused) {
3397
+ let handled = false;
3398
+ let outcome = noMatchOutcome();
3399
+ for (const capture of captures) {
3400
+ const binding = capture.binding;
3401
+ if (!conditions.matchesConditions(binding)) {
3402
+ continue;
3403
+ }
3404
+ const bindingHandled = executor.runBinding(layer, binding, event, focused, createSequencePayload2(capture));
3405
+ outcome = preferDispatchOutcome(outcome, createBindingOutcome(binding, bindingHandled));
3406
+ if (!bindingHandled) {
3407
+ emitBindingDispatch("binding-reject", layer, binding, focused);
3408
+ continue;
3409
+ }
3410
+ emitBindingDispatch("binding-execute", layer, binding, focused);
3411
+ handled = true;
3412
+ if (!binding.fallthrough) {
3413
+ return { handled: true, stop: true, outcome };
3414
+ }
3415
+ }
3416
+ return { handled, stop: false, outcome };
3417
+ }
3418
+ return {
3419
+ intercept,
3420
+ prependEventMatchResolver,
3421
+ appendEventMatchResolver,
3422
+ clearEventMatchResolvers,
3423
+ prependDisambiguationResolver,
3424
+ appendDisambiguationResolver,
3425
+ clearDisambiguationResolvers,
3426
+ handlePendingSequenceChange,
3427
+ handleRawSequence,
3428
+ handleKeyEvent
3429
+ };
3430
+ }
3431
+ function resolveSingleEventMatchKeys(resolver, event, ctx, notify) {
3432
+ let resolved;
3433
+ try {
3434
+ resolved = resolver(event, ctx);
3435
+ } catch (error) {
3436
+ notify.emitError("event-match-resolver-error", error, "[Keymap] Error in event match resolver:");
3437
+ return [];
3438
+ }
3439
+ if (!resolved || resolved.length === 0) {
3440
+ return [];
3441
+ }
3442
+ if (resolved.length === 1) {
3443
+ const [candidate] = resolved;
3444
+ if (typeof candidate !== "string") {
3445
+ notify.emitError("invalid-event-match-resolver-candidate", candidate, "[Keymap] Invalid event match resolver candidate:");
3446
+ return [];
3447
+ }
3448
+ return [candidate];
3449
+ }
3450
+ const keys = [];
3451
+ const seen = new Set;
3452
+ for (const candidate of resolved) {
3453
+ if (typeof candidate !== "string") {
3454
+ notify.emitError("invalid-event-match-resolver-candidate", candidate, "[Keymap] Invalid event match resolver candidate:");
3455
+ continue;
3456
+ }
3457
+ if (seen.has(candidate)) {
3458
+ continue;
3459
+ }
3460
+ seen.add(candidate);
3461
+ keys.push(candidate);
3462
+ }
3463
+ return keys;
3464
+ }
3465
+ function sameParts(left, right) {
3466
+ if (left.length !== right.length) {
3467
+ return false;
3468
+ }
3469
+ for (let index = 0;index < left.length; index += 1) {
3470
+ if (left[index]?.match !== right[index]?.match) {
3471
+ return false;
3472
+ }
3473
+ }
3474
+ return true;
3475
+ }
3476
+
3477
+ // src/services/environment.ts
3478
+ var NOOP = () => {};
3479
+ function normalizePatternLimit(name, field, value, fallback) {
3480
+ if (value === undefined) {
3481
+ return fallback;
3482
+ }
3483
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
3484
+ throw new Error(`Keymap sequence pattern "${name}" ${field} must be a non-negative integer`);
3485
+ }
3486
+ return value;
3487
+ }
3488
+ function registerFieldCompilers(fields, options) {
3489
+ const { kind, reservedFields, registeredFields, emitError } = options;
3490
+ const entries = Object.entries(fields);
3491
+ const registered = [];
3492
+ for (const [name, compiler] of entries) {
3493
+ if (reservedFields.has(name)) {
3494
+ emitError(`reserved-${kind}-field`, { field: name, kind }, `Keymap ${kind} field "${name}" is reserved`);
3495
+ continue;
3496
+ }
3497
+ if (registeredFields.has(name)) {
3498
+ emitError(`duplicate-${kind}-field`, { field: name, kind }, `Keymap ${kind} field "${name}" is already registered`);
3499
+ continue;
3500
+ }
3501
+ registeredFields.set(name, compiler);
3502
+ registered.push([name, compiler]);
3503
+ }
3504
+ return () => {
3505
+ for (const [name, compiler] of registered) {
3506
+ const current = registeredFields.get(name);
3507
+ if (current === compiler) {
3508
+ registeredFields.delete(name);
3509
+ }
3510
+ }
3511
+ };
3512
+ }
3513
+ function registerToken(state, notify, compiler, layers, token) {
3514
+ let normalizedToken;
3515
+ try {
3516
+ normalizedToken = normalizeBindingTokenName(token.name);
3517
+ } catch (error) {
3518
+ notify.emitError("token-name-normalize-error", error, getErrorMessage(error, "Failed to register keymap token"));
3519
+ return NOOP;
3520
+ }
3521
+ if (state.tokens.has(normalizedToken) || state.patterns.has(normalizedToken)) {
3522
+ notify.emitError("duplicate-token", { token: normalizedToken }, `Keymap token "${normalizedToken}" is already registered`);
3523
+ return NOOP;
3524
+ }
3525
+ let parsedToken;
3526
+ try {
3527
+ parsedToken = compiler.parseTokenKey(token.key);
3528
+ if (parsedToken.patternName) {
3529
+ throw new Error(`Invalid key "${String(token.key)}": expected a concrete key stroke`);
3530
+ }
3531
+ } catch (error) {
3532
+ notify.emitError("token-parse-error", error, getErrorMessage(error, `Failed to register keymap token "${normalizedToken}"`));
3533
+ return NOOP;
3534
+ }
3535
+ const registeredToken = {
3536
+ stroke: parsedToken.stroke,
3537
+ match: parsedToken.match
3538
+ };
3539
+ const nextTokens = new Map(state.tokens);
3540
+ nextTokens.set(normalizedToken, registeredToken);
3541
+ try {
3542
+ layers.applyTokenState(nextTokens);
3543
+ } catch (error) {
3544
+ notify.emitError("token-register-error", error, getErrorMessage(error, `Failed to register keymap token "${normalizedToken}"`));
3545
+ return NOOP;
3546
+ }
3547
+ return () => {
3548
+ const current = state.tokens.get(normalizedToken);
3549
+ if (current !== registeredToken) {
3550
+ return;
3551
+ }
3552
+ const nextTokens2 = new Map(state.tokens);
3553
+ nextTokens2.delete(normalizedToken);
3554
+ try {
3555
+ layers.applyTokenState(nextTokens2);
3556
+ } catch (error) {
3557
+ notify.emitError("token-unregister-error", error, getErrorMessage(error, `Failed to unregister keymap token "${normalizedToken}"`));
3558
+ }
3559
+ };
3560
+ }
3561
+ function registerSequencePattern(state, notify, layers, pattern) {
3562
+ let normalizedName;
3563
+ let resolvedPattern;
3564
+ try {
3565
+ normalizedName = normalizeBindingTokenName(pattern.name);
3566
+ const min = normalizePatternLimit(normalizedName, "min", pattern.min, 1);
3567
+ const max = normalizePatternLimit(normalizedName, "max", pattern.max, Number.MAX_SAFE_INTEGER);
3568
+ if (max < min) {
3569
+ throw new Error(`Keymap sequence pattern "${normalizedName}" max must be greater than or equal to min`);
3570
+ }
3571
+ resolvedPattern = {
3572
+ name: normalizedName,
3573
+ display: pattern.display,
3574
+ payloadKey: pattern.payloadKey ?? normalizedName,
3575
+ match: createTextKeyMatch(`pattern:${normalizedName}`),
3576
+ min,
3577
+ max,
3578
+ matcher: (event) => pattern.match(event),
3579
+ finalize: pattern.finalize
3580
+ };
3581
+ } catch (error) {
3582
+ notify.emitError("sequence-pattern-register-error", error, getErrorMessage(error, "Failed to register keymap sequence pattern"));
3583
+ return NOOP;
3584
+ }
3585
+ if (state.tokens.has(normalizedName) || state.patterns.has(normalizedName)) {
3586
+ notify.emitError("duplicate-sequence-pattern", { pattern: normalizedName }, `Keymap sequence pattern "${normalizedName}" is already registered`);
3587
+ return NOOP;
3588
+ }
3589
+ state.patterns.set(normalizedName, resolvedPattern);
3590
+ layers.recompileBindings();
3591
+ return () => {
3592
+ const current = state.patterns.get(normalizedName);
3593
+ if (current !== resolvedPattern) {
3594
+ return;
3595
+ }
3596
+ state.patterns.delete(normalizedName);
3597
+ layers.recompileBindings();
3598
+ };
3599
+ }
3600
+ function registerFields(state, notify, kind, fields) {
3601
+ const reservedFields = kind === "layer" ? RESERVED_LAYER_FIELDS : kind === "binding" ? RESERVED_BINDING_FIELDS : RESERVED_COMMAND_FIELDS;
3602
+ const registeredFields = kind === "layer" ? state.layerFields : kind === "binding" ? state.bindingFields : state.commandFields;
3603
+ return registerFieldCompilers(fields, {
3604
+ kind,
3605
+ reservedFields,
3606
+ registeredFields,
3607
+ emitError: (code, error, message) => notify.emitError(code, error, message)
3608
+ });
3609
+ }
3610
+
3611
+ // src/services/layers.ts
3612
+ var NOOP2 = () => {};
3613
+ function compareLayers(left, right) {
3614
+ const priorityDiff = right.priority - left.priority;
3615
+ return priorityDiff || right.order - left.order;
3616
+ }
3617
+ function layerBlocksActiveKeyCache(layer) {
3618
+ if (layer.matchers.length > 0)
3619
+ return true;
3620
+ for (const command of layer.commands)
3621
+ if (command.matchers.length > 0)
3622
+ return true;
3623
+ for (const binding of layer.bindings)
3624
+ if (binding.matchers.length > 0)
3625
+ return true;
3626
+ return false;
3627
+ }
3628
+ function layerBlocksActiveCommandViewCache(layer) {
3629
+ if (layer.commands.length === 0)
3630
+ return false;
3631
+ if (layer.matchers.length > 0)
3632
+ return true;
3633
+ for (const command of layer.commands)
3634
+ if (command.matchers.length > 0)
3635
+ return true;
3636
+ return false;
3637
+ }
3638
+ function createLayerService(state, notify, conditions, activation, options) {
3639
+ const registerLayer = (layer) => {
3640
+ return notify.runWithStateChangeBatch(() => {
3641
+ const target = layer.target;
3642
+ if (target && options.host.isTargetDestroyed(target)) {
3643
+ notify.emitError("destroyed-layer-target", { target }, "Cannot register a keymap layer for a destroyed keymap target");
3644
+ return NOOP2;
3645
+ }
3646
+ let sourceBindings;
3647
+ let requires;
3648
+ let matchers;
3649
+ let fields;
3650
+ let attrs;
3651
+ let commands;
3652
+ let targetMode;
3653
+ try {
3654
+ targetMode = normalizeTargetMode(layer);
3655
+ sourceBindings = applyLayerBindingsTransformers(snapshotBindings(layer.bindings ?? []), layer);
3656
+ const sourceCommands = applyCommandTransformers(layer.commands ?? [], layer);
3657
+ commands = sourceCommands.length === 0 ? [] : options.commands.normalizeCommands(sourceCommands);
3658
+ ({ requires, matchers, fields, attrs } = compileLayerRuntimeState(layer));
3659
+ } catch (error) {
3660
+ notify.emitError("register-layer-failed", error, getErrorMessage(error, "Failed to register keymap layer"));
3661
+ return NOOP2;
3662
+ }
3663
+ const order = state.order++;
3664
+ const bindingStates = options.compiler.compileBindings(sourceBindings, state.tokens, target, order, fields);
3665
+ if (bindingStates.bindings.length === 0 && !bindingStates.hasTokenBindings && commands.length === 0) {
3666
+ return NOOP2;
3667
+ }
3668
+ options.diagnostics?.analyzeLayer({
3669
+ target,
3670
+ order,
3671
+ commands,
3672
+ sourceBindings,
3673
+ bindings: bindingStates.bindings,
3674
+ hasTokenBindings: bindingStates.hasTokenBindings
3675
+ });
3676
+ const registeredLayer = {
3677
+ order,
3678
+ target,
3679
+ targetMode,
3680
+ priority: layer.priority ?? 0,
3681
+ requires,
3682
+ matchers,
3683
+ fields,
3684
+ attrs,
3685
+ commands,
3686
+ sourceBindings,
3687
+ bindings: bindingStates.bindings,
3688
+ root: buildSequenceTree(bindingStates.bindings, state.patterns),
3689
+ hasTokenBindings: bindingStates.hasTokenBindings,
3690
+ activeKeyCacheBlocked: false,
3691
+ activeCommandViewCacheBlocked: false
3692
+ };
3693
+ updateCacheBlockers(registeredLayer);
3694
+ state.layers.add(registeredLayer);
3695
+ state.sortedLayers = [...state.sortedLayers, registeredLayer].sort(compareLayers);
3696
+ attachReactiveMatchers(registeredLayer);
3697
+ for (const command of registeredLayer.commands) {
3698
+ attachReactiveMatchers(command);
3699
+ }
3700
+ for (const binding of registeredLayer.bindings) {
3701
+ attachReactiveMatchers(binding);
3702
+ }
3703
+ if (target) {
3704
+ const onTargetDestroy = () => {
3705
+ unregisterLayer(registeredLayer);
3706
+ };
3707
+ registeredLayer.offTargetDestroy = options.host.onTargetDestroy(target, onTargetDestroy);
3708
+ }
3709
+ if (registeredLayer.commands.length > 0) {
3710
+ activation.ensureValidPendingSequence();
3711
+ }
3712
+ notify.queueStateChange();
3713
+ return () => {
3714
+ unregisterLayer(registeredLayer);
3715
+ };
3716
+ });
3717
+ };
3718
+ const applyTokenState = (nextTokens) => {
3719
+ notify.runWithStateChangeBatch(() => {
3720
+ const nextCompilations = new Map;
3721
+ for (const layer of state.layers) {
3722
+ if (!layer.hasTokenBindings) {
3723
+ continue;
3724
+ }
3725
+ nextCompilations.set(layer, compileLayerBindings(layer, nextTokens));
3726
+ }
3727
+ state.tokens = nextTokens;
3728
+ let shouldClearPending = false;
3729
+ for (const [layer, compilation] of nextCompilations) {
3730
+ if (applyBindingStates(layer, compilation)) {
3731
+ shouldClearPending = true;
3732
+ }
3733
+ }
3734
+ if (shouldClearPending) {
3735
+ activation.setPendingSequence(null);
3736
+ }
3737
+ if (nextCompilations.size > 0) {
3738
+ notify.queueStateChange();
3739
+ }
3740
+ });
3741
+ };
3742
+ const recompileBindings = () => {
3743
+ notify.runWithStateChangeBatch(() => {
3744
+ let recompiledLayers = 0;
3745
+ let shouldClearPending = false;
3746
+ for (const layer of state.layers) {
3747
+ if (layer.sourceBindings.length === 0) {
3748
+ continue;
3749
+ }
3750
+ const compilation = compileLayerBindings(layer, state.tokens);
3751
+ if (applyBindingStates(layer, compilation)) {
3752
+ shouldClearPending = true;
3753
+ }
3754
+ recompiledLayers += 1;
3755
+ }
3756
+ if (shouldClearPending) {
3757
+ activation.setPendingSequence(null);
3758
+ }
3759
+ if (recompiledLayers > 0) {
3760
+ notify.queueStateChange();
3761
+ }
3762
+ });
3763
+ };
3764
+ const cleanup = () => {
3765
+ for (const layer of state.layers) {
3766
+ detachReactiveMatchers(layer);
3767
+ for (const command of layer.commands) {
3768
+ detachReactiveMatchers(command);
3769
+ }
3770
+ for (const binding of layer.bindings) {
3771
+ detachReactiveMatchers(binding);
3772
+ }
3773
+ layer.offTargetDestroy?.();
3774
+ layer.offTargetDestroy = undefined;
3775
+ }
3776
+ };
3777
+ const normalizeTargetMode = (layer) => {
3778
+ if (layer.targetMode) {
3779
+ if (!layer.target) {
3780
+ throw new Error(`Keymap targetMode "${layer.targetMode}" requires a target`);
3781
+ }
3782
+ return layer.targetMode;
3783
+ }
3784
+ return layer.target ? "focus-within" : undefined;
3785
+ };
3786
+ const applyLayerBindingsTransformers = (bindings, layer) => {
3787
+ const transformers = state.layerBindingsTransformers.values();
3788
+ if (transformers.length === 0) {
3789
+ return bindings;
3790
+ }
3791
+ let current = bindings;
3792
+ for (const transformer of transformers) {
3793
+ const next = transformer(current, {
3794
+ layer,
3795
+ validateBindings: (bindings2) => validateBindings(bindings2)
3796
+ });
3797
+ if (!next) {
3798
+ continue;
3799
+ }
3800
+ current = snapshotBindings(next);
3801
+ }
3802
+ return current;
3803
+ };
3804
+ const applyCommandTransformers = (commands, layer) => {
3805
+ const transformers = state.commandTransformers.values();
3806
+ if (commands.length === 0 || transformers.length === 0) {
3807
+ return commands;
3808
+ }
3809
+ const transformedCommands = [];
3810
+ for (const command of commands) {
3811
+ const transformedCommand = { ...command };
3812
+ const extraCommands = [];
3813
+ let keepOriginal = true;
3814
+ for (const transformer of transformers) {
3815
+ try {
3816
+ transformer(transformedCommand, {
3817
+ layer,
3818
+ add(nextCommand) {
3819
+ extraCommands.push({ ...nextCommand });
3820
+ },
3821
+ skipOriginal() {
3822
+ keepOriginal = false;
3823
+ }
3824
+ });
3825
+ } catch (error) {
3826
+ notify.emitError("command-transformer-error", error, "[Keymap] Error in command transformer:");
3827
+ }
3828
+ }
3829
+ if (keepOriginal) {
3830
+ transformedCommands.push(transformedCommand);
3831
+ }
3832
+ transformedCommands.push(...extraCommands);
3833
+ }
3834
+ return transformedCommands;
3835
+ };
3836
+ const compileLayerRuntimeState = (layer) => {
3837
+ const mergedRequires = {};
3838
+ const matchers = [];
3839
+ const fields = Object.create(null);
3840
+ const attrs = {};
3841
+ for (const [fieldName, value] of Object.entries(layer)) {
3842
+ if (RESERVED_LAYER_FIELDS.has(fieldName)) {
3843
+ continue;
3844
+ }
3845
+ if (value === undefined) {
3846
+ continue;
3847
+ }
3848
+ fields[fieldName] = snapshotDataValue(value);
3849
+ const compiler = state.layerFields.get(fieldName);
3850
+ if (!compiler) {
3851
+ options.warnUnknownField("layer", fieldName);
3852
+ continue;
3853
+ }
3854
+ compiler(value, createFieldCompilerContext({
3855
+ fieldName,
3856
+ conditions,
3857
+ requirements: mergedRequires,
3858
+ matchers,
3859
+ attrs
3860
+ }));
3861
+ }
3862
+ return {
3863
+ requires: Object.entries(mergedRequires),
3864
+ matchers,
3865
+ fields: Object.keys(fields).length > 0 ? Object.freeze(fields) : undefined,
3866
+ attrs: Object.keys(attrs).length > 0 ? snapshotDataValue(attrs, { freeze: true }) : undefined
3867
+ };
3868
+ };
3869
+ const compileLayerBindings = (layer, tokens) => {
3870
+ return options.compiler.compileBindings(layer.sourceBindings, tokens, layer.target, layer.order, layer.fields);
3871
+ };
3872
+ const applyBindingStates = (layer, compilation) => {
3873
+ options.diagnostics?.analyzeLayer({
3874
+ target: layer.target,
3875
+ order: layer.order,
3876
+ commands: layer.commands,
3877
+ sourceBindings: layer.sourceBindings,
3878
+ bindings: compilation.bindings,
3879
+ hasTokenBindings: compilation.hasTokenBindings
3880
+ });
3881
+ untrackCacheBlockers(layer);
3882
+ for (const binding of layer.bindings) {
3883
+ detachReactiveMatchers(binding);
3884
+ }
3885
+ layer.bindings = compilation.bindings;
3886
+ layer.root = buildSequenceTree(compilation.bindings, state.patterns);
3887
+ layer.hasTokenBindings = compilation.hasTokenBindings;
3888
+ updateCacheBlockers(layer);
3889
+ for (const binding of layer.bindings) {
3890
+ attachReactiveMatchers(binding);
3891
+ }
3892
+ return state.pending?.captures.some((capture) => capture.layer === layer) ?? false;
3893
+ };
3894
+ const unregisterLayer = (layer) => {
3895
+ notify.runWithStateChangeBatch(() => {
3896
+ if (!state.layers.delete(layer)) {
3897
+ return;
3898
+ }
3899
+ state.sortedLayers = state.sortedLayers.filter((candidate) => candidate !== layer);
3900
+ untrackCacheBlockers(layer);
3901
+ detachReactiveMatchers(layer);
3902
+ for (const command of layer.commands) {
3903
+ detachReactiveMatchers(command);
3904
+ }
3905
+ for (const binding of layer.bindings) {
3906
+ detachReactiveMatchers(binding);
3907
+ }
3908
+ layer.offTargetDestroy?.();
3909
+ layer.offTargetDestroy = undefined;
3910
+ if (state.pending?.captures.some((capture) => capture.layer === layer)) {
3911
+ activation.setPendingSequence(null);
3912
+ } else if (layer.commands.length > 0 && !options.host.isDestroyed) {
3913
+ activation.ensureValidPendingSequence();
3914
+ }
3915
+ notify.queueStateChange();
3916
+ });
3917
+ };
3918
+ const attachReactiveMatchers = (target) => {
3919
+ for (const matcher of target.matchers) {
3920
+ if (!matcher.subscribe) {
3921
+ continue;
3922
+ }
3923
+ try {
3924
+ matcher.dispose = matcher.subscribe(() => {
3925
+ if (!activation.hasPendingSequenceState()) {
3926
+ notify.queueStateChange();
3927
+ return;
3928
+ }
3929
+ notify.runWithStateChangeBatch(() => {
3930
+ activation.revalidatePendingSequenceIfNeeded();
3931
+ notify.queueStateChange();
3932
+ });
3933
+ });
3934
+ } catch (error) {
3935
+ notify.emitError("reactive-matcher-subscribe-error", error, getErrorMessage(error, `Failed to subscribe to reactive matcher from ${matcher.source}`));
3936
+ }
3937
+ }
3938
+ };
3939
+ const updateCacheBlockers = (layer) => {
3940
+ const activeKeyBlocked = layerBlocksActiveKeyCache(layer);
3941
+ const activeCommandViewBlocked = layerBlocksActiveCommandViewCache(layer);
3942
+ layer.activeKeyCacheBlocked = activeKeyBlocked;
3943
+ layer.activeCommandViewCacheBlocked = activeCommandViewBlocked;
3944
+ if (activeKeyBlocked)
3945
+ state.activeKeyCacheBlockers += 1;
3946
+ if (activeCommandViewBlocked)
3947
+ state.activeCommandViewCacheBlockers += 1;
3948
+ };
3949
+ const untrackCacheBlockers = (layer) => {
3950
+ if (layer.activeKeyCacheBlocked) {
3951
+ state.activeKeyCacheBlockers -= 1;
3952
+ layer.activeKeyCacheBlocked = false;
3953
+ }
3954
+ if (layer.activeCommandViewCacheBlocked) {
3955
+ state.activeCommandViewCacheBlockers -= 1;
3956
+ layer.activeCommandViewCacheBlocked = false;
3957
+ }
3958
+ };
3959
+ const detachReactiveMatchers = (target) => {
3960
+ for (const matcher of target.matchers) {
3961
+ if (!matcher.dispose) {
3962
+ continue;
3963
+ }
3964
+ try {
3965
+ matcher.dispose();
3966
+ } catch (error) {
3967
+ notify.emitError("reactive-matcher-dispose-error", error, getErrorMessage(error, `Failed to dispose reactive matcher from ${matcher.source}`));
3968
+ }
3969
+ matcher.dispose = undefined;
3970
+ }
3971
+ };
3972
+ return { registerLayer, applyTokenState, recompileBindings, cleanup };
3973
+ }
3974
+
3975
+ // src/services/notify.ts
3976
+ var MAX_STATE_CHANGE_FLUSH_ITERATIONS = 1000;
3977
+ function createNotificationService(state, events, hooks) {
3978
+ const emitWarning = (code, warning, message) => {
3979
+ if (!events.has("warning")) {
3980
+ const consoleMessage = `[${code}] ${message}`;
3981
+ if (warning instanceof Error) {
3982
+ console.warn(consoleMessage, warning);
3983
+ } else {
3984
+ console.warn(consoleMessage);
3985
+ }
3986
+ return;
3987
+ }
3988
+ events.emit("warning", { code, message, warning });
3989
+ };
3990
+ const emitError = (code, error, message) => {
3991
+ if (!events.has("error")) {
3992
+ const consoleMessage = `[${code}] ${message}`;
3993
+ if (error instanceof Error) {
3994
+ console.error(consoleMessage, error);
3995
+ } else {
3996
+ console.error(consoleMessage);
3997
+ }
3998
+ return;
3999
+ }
4000
+ events.emit("error", { code, message, error });
4001
+ };
4002
+ const flushStateChange = () => {
4003
+ if (!state.stateChangePending || state.stateChangeDepth > 0 || state.flushingStateChange) {
4004
+ return;
4005
+ }
4006
+ state.flushingStateChange = true;
4007
+ try {
4008
+ let iterations = 0;
4009
+ while (state.stateChangePending && state.stateChangeDepth === 0) {
4010
+ if (iterations >= MAX_STATE_CHANGE_FLUSH_ITERATIONS) {
4011
+ state.stateChangePending = false;
4012
+ emitError("state-change-feedback-loop", { iterations: MAX_STATE_CHANGE_FLUSH_ITERATIONS }, `[Keymap] Possible infinite state listener feedback loop detected after ${MAX_STATE_CHANGE_FLUSH_ITERATIONS} iterations; pending state notifications were dropped`);
4013
+ break;
4014
+ }
4015
+ iterations += 1;
4016
+ state.stateChangePending = false;
4017
+ hooks.emit("state");
4018
+ }
4019
+ } finally {
4020
+ state.flushingStateChange = false;
4021
+ }
4022
+ };
4023
+ return {
4024
+ runWithStateChangeBatch(fn) {
4025
+ state.stateChangeDepth += 1;
4026
+ try {
4027
+ return fn();
4028
+ } finally {
4029
+ state.stateChangeDepth -= 1;
4030
+ if (state.stateChangeDepth === 0) {
4031
+ flushStateChange();
4032
+ }
4033
+ }
4034
+ },
4035
+ queueStateChange(options) {
4036
+ state.derivedVersion += 1;
4037
+ if (options?.invalidateCaches !== false) {
4038
+ state.cacheVersion += 1;
4039
+ }
4040
+ if (!hooks.has("state")) {
4041
+ return;
4042
+ }
4043
+ state.stateChangePending = true;
4044
+ if (state.stateChangeDepth === 0 && !state.flushingStateChange) {
4045
+ flushStateChange();
4046
+ }
4047
+ },
4048
+ emitWarning,
4049
+ emitError,
4050
+ reportListenerError(name, error) {
4051
+ if (name === "state") {
4052
+ emitError("state-listener-error", error, "[Keymap] Error in state listener:");
4053
+ return;
4054
+ }
4055
+ if (name === "pendingSequence") {
4056
+ emitError("pending-sequence-listener-error", error, "[Keymap] Error in pending sequence listener:");
4057
+ return;
4058
+ }
4059
+ if (name === "dispatch") {
4060
+ emitError("dispatch-listener-error", error, "[Keymap] Error in dispatch listener:");
4061
+ }
4062
+ },
4063
+ warnOnce(key, code, warning, message) {
4064
+ if (state.usedWarningKeys.has(key)) {
4065
+ return;
4066
+ }
4067
+ state.usedWarningKeys.add(key);
4068
+ emitWarning(code, warning, message);
4069
+ }
4070
+ };
4071
+ }
4072
+
4073
+ // src/services/runtime.ts
4074
+ function createRuntimeService(state, notify, activation) {
4075
+ return {
4076
+ getData(name) {
4077
+ return state.data[name];
4078
+ },
4079
+ setData(name, value) {
4080
+ notify.runWithStateChangeBatch(() => {
4081
+ if (value === undefined) {
4082
+ if (!(name in state.data)) {
4083
+ return;
4084
+ }
4085
+ delete state.data[name];
4086
+ state.dataVersion += 1;
4087
+ activation.ensureValidPendingSequence();
4088
+ notify.queueStateChange();
4089
+ return;
4090
+ }
4091
+ if (Object.is(state.data[name], value)) {
4092
+ return;
4093
+ }
4094
+ state.data[name] = value;
4095
+ state.dataVersion += 1;
4096
+ activation.ensureValidPendingSequence();
4097
+ notify.queueStateChange();
4098
+ });
4099
+ },
4100
+ getReadonlyData() {
4101
+ if (state.readonlyDataVersion === state.dataVersion) {
4102
+ return state.readonlyData;
4103
+ }
4104
+ state.readonlyData = Object.freeze({ ...state.data });
4105
+ state.readonlyDataVersion = state.dataVersion;
4106
+ return state.readonlyData;
4107
+ }
4108
+ };
4109
+ }
4110
+
4111
+ // src/services/state.ts
4112
+ var EMPTY_DATA = Object.freeze({});
4113
+ function createKeymapState() {
4114
+ return {
4115
+ order: 0,
4116
+ tokens: new Map,
4117
+ patterns: new Map,
4118
+ layerFields: new Map,
4119
+ layerBindingsTransformers: createRuntimeOrderedRegistry(),
4120
+ bindingExpanders: createRuntimeOrderedRegistry(),
4121
+ bindingParsers: createRuntimeOrderedRegistry(),
4122
+ bindingTransformers: createRuntimeOrderedRegistry(),
4123
+ bindingFields: new Map,
4124
+ commandTransformers: createRuntimeOrderedRegistry(),
4125
+ commandFields: new Map,
4126
+ eventMatchResolvers: createRuntimeOrderedRegistry(),
4127
+ disambiguationResolvers: createRuntimeOrderedRegistry(),
4128
+ keyHooks: createRuntimePriorityRegistry(),
4129
+ keyAfterHooks: createRuntimePriorityRegistry(),
4130
+ rawHooks: createRuntimePriorityRegistry(),
4131
+ layers: new Set,
4132
+ sortedLayers: [],
4133
+ activeLayersCacheVersion: -1,
4134
+ activeLayersCacheFocused: undefined,
4135
+ activeLayersCache: [],
4136
+ activeKeyCacheBlockers: 0,
4137
+ activeCommandViewCacheBlockers: 0,
4138
+ commandResolvers: createRuntimeOrderedRegistry(),
4139
+ pending: null,
4140
+ data: {},
4141
+ dataVersion: 0,
4142
+ readonlyDataVersion: -1,
4143
+ readonlyData: EMPTY_DATA,
4144
+ cacheVersion: 0,
4145
+ derivedVersion: 0,
4146
+ stateChangeDepth: 0,
4147
+ stateChangePending: false,
4148
+ flushingStateChange: false,
4149
+ usedWarningKeys: new Set
4150
+ };
4151
+ }
4152
+
4153
+ // src/keymap.ts
4154
+ function getKeyMatchKey(input) {
4155
+ return resolveKeyMatch(input);
4156
+ }
4157
+
4158
+ class Keymap {
4159
+ #state = createKeymapState();
4160
+ #cleanedUp = false;
4161
+ #resources = new Map;
4162
+ #cleanupListeners = [];
4163
+ #events = createRuntimeEmitter(() => {});
4164
+ #hooks;
4165
+ #notify;
4166
+ #activation;
4167
+ #runtime;
4168
+ #conditions;
4169
+ #catalog;
4170
+ #executor;
4171
+ #compiler;
4172
+ #dispatch;
4173
+ #layers;
4174
+ #layerDiagnostics;
4175
+ #keypressListener;
4176
+ #keyreleaseListener;
4177
+ #rawListener;
4178
+ #focusedTargetListener;
4179
+ #host;
4180
+ getPendingSequence;
4181
+ getActiveKeys;
4182
+ constructor(host) {
4183
+ this.#host = host;
4184
+ if (host.isDestroyed) {
4185
+ throw new Error("Cannot create a keymap for a destroyed host");
4186
+ }
4187
+ this.#hooks = createRuntimeEmitter((name, error) => {
4188
+ this.#notify.reportListenerError(name, error);
4189
+ });
4190
+ this.#notify = createNotificationService(this.#state, this.#events, this.#hooks);
4191
+ this.#conditions = createConditionService(this.#state, this.#notify);
4192
+ this.#catalog = createCommandCatalogService(this.#state, this.#host, this.#notify, this.#conditions, {
4193
+ onCommandResolversChanged: () => {
4194
+ this.#activation.ensureValidPendingSequence();
4195
+ }
4196
+ });
4197
+ this.#activation = createActivationService(this.#state, this.#host, this.#hooks, this.#notify, this.#conditions, this.#catalog, {
4198
+ onPendingSequenceChanged: (previous, next) => {
4199
+ this.#dispatch?.handlePendingSequenceChange(previous, next);
4200
+ }
4201
+ });
4202
+ this.#runtime = createRuntimeService(this.#state, this.#notify, this.#activation);
4203
+ this.#executor = createCommandExecutorService(this.#notify, this.#runtime, this.#activation, this.#catalog, {
4204
+ keymap: this,
4205
+ createCommandEvent: () => this.#host.createCommandEvent()
4206
+ });
4207
+ this.#compiler = createCompilerService(this.#state, this.#notify, this.#conditions, {
4208
+ warnUnknownField: (kind, fieldName) => {
4209
+ this.#warnUnknownField(kind, fieldName);
4210
+ },
4211
+ warnUnknownToken: (token, sequence) => {
4212
+ this.#warnUnknownToken(token, sequence);
4213
+ }
4214
+ });
4215
+ this.#layerDiagnostics = createLayerDiagnostics(this.#notify, this.#catalog);
4216
+ this.#layers = createLayerService(this.#state, this.#notify, this.#conditions, this.#activation, {
4217
+ compiler: this.#compiler,
4218
+ commands: this.#catalog,
4219
+ host: this.#host,
4220
+ diagnostics: this.#layerDiagnostics,
4221
+ warnUnknownField: (kind, fieldName) => {
4222
+ this.#warnUnknownField(kind, fieldName);
4223
+ }
4224
+ });
4225
+ this.#dispatch = createDispatchService(this.#state, this.#notify, this.#runtime, this.#activation, this.#conditions, this.#executor, this.#compiler, this.#catalog, this.#layers, this.#hooks);
4226
+ this.getPendingSequence = this.#activation.getPendingSequence;
4227
+ this.getActiveKeys = this.#activation.getActiveKeys;
4228
+ this.#keypressListener = (event) => {
4229
+ this.#dispatch.handleKeyEvent(event, false);
4230
+ };
4231
+ this.#keyreleaseListener = (event) => {
4232
+ this.#dispatch.handleKeyEvent(event, true);
4233
+ };
4234
+ this.#rawListener = (sequence) => {
4235
+ return this.#dispatch.handleRawSequence(sequence);
4236
+ };
4237
+ this.#focusedTargetListener = (focused) => {
4238
+ this.#handleFocusedTargetChange(focused);
4239
+ };
4240
+ this.#cleanupListeners.push(this.#host.onKeyPress(this.#keypressListener));
4241
+ this.#cleanupListeners.push(this.#host.onKeyRelease(this.#keyreleaseListener));
4242
+ if (this.#host.onRawInput) {
4243
+ this.#cleanupListeners.push(this.#host.onRawInput(this.#rawListener));
4244
+ }
4245
+ this.#cleanupListeners.push(this.#host.onFocusChange(this.#focusedTargetListener));
4246
+ if (this.#host.onDestroy) {
4247
+ this.#cleanupListeners.push(this.#host.onDestroy(() => {
4248
+ this.#cleanup();
4249
+ }));
4250
+ }
4251
+ }
4252
+ [KEYMAP_EXTENSION_CONTEXT]() {
4253
+ return {
4254
+ state: this.#state,
4255
+ host: this.#host,
4256
+ conditions: this.#conditions,
4257
+ catalog: this.#catalog,
4258
+ activation: this.#activation
4259
+ };
4260
+ }
4261
+ #cleanup() {
4262
+ if (this.#cleanedUp) {
4263
+ return;
4264
+ }
4265
+ this.#cleanedUp = true;
4266
+ this.#activation.setPendingSequence(null);
4267
+ for (const resource of this.#resources.values()) {
4268
+ resource.dispose();
4269
+ }
4270
+ this.#resources.clear();
4271
+ this.#layers.cleanup();
4272
+ for (const cleanupListener of this.#cleanupListeners.splice(0)) {
4273
+ cleanupListener();
4274
+ }
4275
+ }
4276
+ setData(name, value) {
4277
+ this.#runtime.setData(name, value);
4278
+ }
4279
+ getData(name) {
4280
+ return this.#runtime.getData(name);
4281
+ }
4282
+ getHostMetadata() {
4283
+ return this.#host.metadata;
4284
+ }
4285
+ hasPendingSequence() {
4286
+ return this.#activation.ensureValidPendingSequence() !== undefined;
4287
+ }
4288
+ createKeyMatcher(key) {
4289
+ const match = this.#compiler.parseTokenKey(key).match;
4290
+ return (input) => {
4291
+ if (!input) {
4292
+ return false;
4293
+ }
4294
+ return getKeyMatchKey(input) === match;
4295
+ };
4296
+ }
4297
+ parseKeySequence(key) {
4298
+ return this.#compiler.parseKeySequence(key);
4299
+ }
4300
+ formatKey(key, options) {
4301
+ return this.#compiler.formatKey(key, options);
4302
+ }
4303
+ clearPendingSequence() {
4304
+ this.#activation.setPendingSequence(null);
4305
+ }
4306
+ popPendingSequence() {
4307
+ return this.#activation.popPendingSequence();
4308
+ }
4309
+ getCommands(query) {
4310
+ return this.#catalog.getCommands(query);
4311
+ }
4312
+ getCommandEntries(query) {
4313
+ return this.#catalog.getCommandEntries(query);
4314
+ }
4315
+ getCommandBindings(query) {
4316
+ return this.#catalog.getCommandBindings(query);
4317
+ }
4318
+ acquireResource(key, setup) {
4319
+ if (this.#cleanedUp || this.#host.isDestroyed) {
4320
+ throw new Error("Cannot use a keymap after its host was destroyed");
4321
+ }
4322
+ const existing = this.#resources.get(key);
4323
+ if (existing) {
4324
+ existing.count += 1;
4325
+ return () => {
4326
+ this.#releaseResource(key, existing);
4327
+ };
4328
+ }
4329
+ const dispose = setup();
4330
+ const resource = { count: 1, dispose };
4331
+ this.#resources.set(key, resource);
4332
+ return () => {
4333
+ this.#releaseResource(key, resource);
4334
+ };
4335
+ }
4336
+ runCommand(cmd, options) {
4337
+ return this.#executor.runCommand(cmd, options);
4338
+ }
4339
+ dispatchCommand(cmd, options) {
4340
+ return this.#executor.dispatchCommand(cmd, options);
4341
+ }
4342
+ on(name, fn) {
4343
+ if (name === "warning") {
4344
+ return this.#events.hook(name, fn);
4345
+ }
4346
+ if (name === "error") {
4347
+ return this.#events.hook(name, fn);
4348
+ }
4349
+ return this.#hooks.hook(name, fn);
4350
+ }
4351
+ intercept(name, fn, options) {
4352
+ if (name === "key") {
4353
+ return this.#dispatch.intercept(name, fn, options);
4354
+ }
4355
+ if (name === "key:after") {
4356
+ return this.#dispatch.intercept(name, fn, options);
4357
+ }
4358
+ return this.#dispatch.intercept(name, fn, options);
4359
+ }
4360
+ registerLayer(layer) {
4361
+ return this.#layers.registerLayer(layer);
4362
+ }
4363
+ registerLayerFields(fields) {
4364
+ return registerFields(this.#state, this.#notify, "layer", fields);
4365
+ }
4366
+ prependLayerBindingsTransformer(transformer) {
4367
+ return this.#state.layerBindingsTransformers.prepend(transformer);
4368
+ }
4369
+ appendLayerBindingsTransformer(transformer) {
4370
+ return this.#state.layerBindingsTransformers.append(transformer);
4371
+ }
4372
+ clearLayerBindingsTransformers() {
4373
+ this.#state.layerBindingsTransformers.clear();
4374
+ }
4375
+ prependBindingTransformer(transformer) {
4376
+ return this.#state.bindingTransformers.prepend(transformer);
4377
+ }
4378
+ appendBindingTransformer(transformer) {
4379
+ return this.#state.bindingTransformers.append(transformer);
4380
+ }
4381
+ clearBindingTransformers() {
4382
+ this.#state.bindingTransformers.clear();
4383
+ }
4384
+ prependCommandTransformer(transformer) {
4385
+ return this.#state.commandTransformers.prepend(transformer);
4386
+ }
4387
+ appendCommandTransformer(transformer) {
4388
+ return this.#state.commandTransformers.append(transformer);
4389
+ }
4390
+ clearCommandTransformers() {
4391
+ this.#state.commandTransformers.clear();
4392
+ }
4393
+ prependBindingParser(parser) {
4394
+ return this.#state.bindingParsers.prepend(parser);
4395
+ }
4396
+ appendBindingParser(parser) {
4397
+ return this.#state.bindingParsers.append(parser);
4398
+ }
4399
+ clearBindingParsers() {
4400
+ this.#state.bindingParsers.clear();
4401
+ }
4402
+ registerToken(token) {
4403
+ return registerToken(this.#state, this.#notify, this.#compiler, this.#layers, token);
4404
+ }
4405
+ registerSequencePattern(pattern) {
4406
+ return registerSequencePattern(this.#state, this.#notify, this.#layers, pattern);
4407
+ }
4408
+ prependBindingExpander(expander) {
4409
+ return this.#state.bindingExpanders.prepend(expander);
4410
+ }
4411
+ appendBindingExpander(expander) {
4412
+ return this.#state.bindingExpanders.append(expander);
4413
+ }
4414
+ clearBindingExpanders() {
4415
+ this.#state.bindingExpanders.clear();
4416
+ }
4417
+ registerBindingFields(fields) {
4418
+ return registerFields(this.#state, this.#notify, "binding", fields);
4419
+ }
4420
+ registerCommandFields(fields) {
4421
+ return registerFields(this.#state, this.#notify, "command", fields);
4422
+ }
4423
+ prependCommandResolver(resolver) {
4424
+ return this.#catalog.prependCommandResolver(resolver);
4425
+ }
4426
+ appendCommandResolver(resolver) {
4427
+ return this.#catalog.appendCommandResolver(resolver);
4428
+ }
4429
+ clearCommandResolvers() {
4430
+ this.#catalog.clearCommandResolvers();
4431
+ }
4432
+ prependLayerAnalyzer(analyzer) {
4433
+ return this.#layerDiagnostics.prependLayerAnalyzer(analyzer);
4434
+ }
4435
+ appendLayerAnalyzer(analyzer) {
4436
+ return this.#layerDiagnostics.appendLayerAnalyzer(analyzer);
4437
+ }
4438
+ clearLayerAnalyzers() {
4439
+ this.#layerDiagnostics.clearLayerAnalyzers();
4440
+ }
4441
+ prependEventMatchResolver(resolver) {
4442
+ return this.#dispatch.prependEventMatchResolver(resolver);
4443
+ }
4444
+ appendEventMatchResolver(resolver) {
4445
+ return this.#dispatch.appendEventMatchResolver(resolver);
4446
+ }
4447
+ clearEventMatchResolvers() {
4448
+ this.#dispatch.clearEventMatchResolvers();
4449
+ }
4450
+ prependDisambiguationResolver(resolver) {
4451
+ return this.#dispatch.prependDisambiguationResolver(resolver);
4452
+ }
4453
+ appendDisambiguationResolver(resolver) {
4454
+ return this.#dispatch.appendDisambiguationResolver(resolver);
4455
+ }
4456
+ clearDisambiguationResolvers() {
4457
+ this.#dispatch.clearDisambiguationResolvers();
4458
+ }
4459
+ #handleFocusedTargetChange(_focused) {
4460
+ this.#notify.runWithStateChangeBatch(() => {
4461
+ this.#activation.setPendingSequence(null);
4462
+ this.#notify.queueStateChange({ invalidateCaches: false });
4463
+ });
4464
+ }
4465
+ #warnUnknownField(kind, fieldName) {
4466
+ this.#notify.warnOnce(`${kind}:${fieldName}`, `unknown-${kind}-field`, { field: fieldName, kind }, `[Keymap] Unknown ${kind} field "${fieldName}" was ignored`);
4467
+ }
4468
+ #warnUnknownToken(token, sequence) {
4469
+ this.#notify.warnOnce(`token:${token}`, "unknown-token", { token, sequence }, `[Keymap] Unknown token "${token}" in key sequence "${sequence}" was ignored`);
4470
+ }
4471
+ #releaseResource(key, resource) {
4472
+ const current = this.#resources.get(key);
4473
+ if (current !== resource) {
4474
+ return;
4475
+ }
4476
+ resource.count -= 1;
4477
+ if (resource.count > 0) {
4478
+ return;
4479
+ }
4480
+ resource.dispose();
4481
+ this.#resources.delete(key);
4482
+ }
4483
+ }
4484
+ export {
4485
+ stringifyKeyStroke,
4486
+ stringifyKeySequence,
4487
+ Keymap,
4488
+ KEYMAP_EXTENSION_CONTEXT
4489
+ };
4490
+
4491
+ //# debugId=DF4DB67A4951EA4E64756E2164756E21
4492
+ //# sourceMappingURL=index.js.map