stimeo-ui 0.1.0-beta.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/README.md +6 -6
  3. package/dist/controllers/auto_submit_controller.js +56 -21
  4. package/dist/controllers/auto_submit_controller.js.map +1 -1
  5. package/dist/controllers/character_counter_controller.js +52 -15
  6. package/dist/controllers/character_counter_controller.js.map +1 -1
  7. package/dist/controllers/combobox_controller.d.ts +6 -2
  8. package/dist/controllers/combobox_controller.js +64 -3
  9. package/dist/controllers/combobox_controller.js.map +1 -1
  10. package/dist/controllers/command_palette_controller.d.ts +13 -6
  11. package/dist/controllers/command_palette_controller.js +256 -39
  12. package/dist/controllers/command_palette_controller.js.map +1 -1
  13. package/dist/controllers/confirm_controller.js +107 -8
  14. package/dist/controllers/confirm_controller.js.map +1 -1
  15. package/dist/controllers/context_menu_controller.d.ts +11 -6
  16. package/dist/controllers/context_menu_controller.js +191 -13
  17. package/dist/controllers/context_menu_controller.js.map +1 -1
  18. package/dist/controllers/countdown_controller.d.ts +2 -2
  19. package/dist/controllers/countdown_controller.js +2 -2
  20. package/dist/controllers/countdown_controller.js.map +1 -1
  21. package/dist/controllers/dialog_controller.js +107 -8
  22. package/dist/controllers/dialog_controller.js.map +1 -1
  23. package/dist/controllers/dropdown_controller.d.ts +9 -6
  24. package/dist/controllers/dropdown_controller.js +139 -16
  25. package/dist/controllers/dropdown_controller.js.map +1 -1
  26. package/dist/controllers/focus_controller.js +107 -8
  27. package/dist/controllers/focus_controller.js.map +1 -1
  28. package/dist/controllers/form_validation_controller.js +2 -2
  29. package/dist/controllers/form_validation_controller.js.map +1 -1
  30. package/dist/controllers/hover_card_controller.d.ts +15 -12
  31. package/dist/controllers/hover_card_controller.js +103 -27
  32. package/dist/controllers/hover_card_controller.js.map +1 -1
  33. package/dist/controllers/intersection_controller.js.map +1 -1
  34. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  35. package/dist/controllers/listbox_controller.js +1 -0
  36. package/dist/controllers/listbox_controller.js.map +1 -1
  37. package/dist/controllers/menu_controller.d.ts +18 -11
  38. package/dist/controllers/menu_controller.js +181 -8
  39. package/dist/controllers/menu_controller.js.map +1 -1
  40. package/dist/controllers/otp_controller.js +62 -22
  41. package/dist/controllers/otp_controller.js.map +1 -1
  42. package/dist/controllers/overflow_menu_controller.d.ts +3 -2
  43. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  44. package/dist/controllers/pointer_drag_controller.js +3 -3
  45. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  46. package/dist/controllers/popover_controller.d.ts +18 -10
  47. package/dist/controllers/popover_controller.js +118 -27
  48. package/dist/controllers/popover_controller.js.map +1 -1
  49. package/dist/controllers/scroll_area_controller.js.map +1 -1
  50. package/dist/controllers/scrollspy_controller.js.map +1 -1
  51. package/dist/controllers/sortable_controller.d.ts +2 -2
  52. package/dist/controllers/sortable_controller.js +1 -1
  53. package/dist/controllers/sortable_controller.js.map +1 -1
  54. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  55. package/dist/controllers/tabs_controller.d.ts +1 -2
  56. package/dist/controllers/tabs_controller.js.map +1 -1
  57. package/dist/controllers/toast_controller.d.ts +7 -9
  58. package/dist/controllers/toast_controller.js +108 -29
  59. package/dist/controllers/toast_controller.js.map +1 -1
  60. package/dist/controllers/tooltip_controller.d.ts +15 -18
  61. package/dist/controllers/tooltip_controller.js +148 -42
  62. package/dist/controllers/tooltip_controller.js.map +1 -1
  63. package/dist/index.d.ts +44 -14
  64. package/dist/index.js +847 -349
  65. package/dist/index.js.map +1 -1
  66. package/dist/inspector/cli.js.map +1 -1
  67. package/dist/inspector/cli_bin.js.map +1 -1
  68. package/dist/inspector/examples.json +6 -6
  69. package/dist/inspector/manifest.json +16 -3
  70. package/package.json +7 -9
@@ -2,6 +2,137 @@ import { Controller } from '@hotwired/stimulus';
2
2
 
3
3
  // src/controllers/command_palette_controller.ts
4
4
 
5
+ // src/utils/composition_tracker.ts
6
+ var CompositionTracker = class {
7
+ #observedTargets = /* @__PURE__ */ new Set();
8
+ #activeTargets = /* @__PURE__ */ new Set();
9
+ #onStart;
10
+ #onEnd;
11
+ constructor(options = {}) {
12
+ this.#onStart = options.onStart;
13
+ this.#onEnd = options.onEnd;
14
+ }
15
+ /** Starts lifecycle tracking for `target`; repeated calls are idempotent. */
16
+ observe(target) {
17
+ if (this.#observedTargets.has(target)) return;
18
+ target.addEventListener("compositionstart", this.#handleStart);
19
+ target.addEventListener("compositionend", this.#handleEnd);
20
+ this.#observedTargets.add(target);
21
+ }
22
+ /** Stops tracking one target and clears any active composition it owned. */
23
+ unobserve(target) {
24
+ if (!this.#observedTargets.delete(target)) return;
25
+ target.removeEventListener("compositionstart", this.#handleStart);
26
+ target.removeEventListener("compositionend", this.#handleEnd);
27
+ this.#activeTargets.delete(target);
28
+ }
29
+ /** Releases every listener and clears state so reconnect starts cleanly. */
30
+ disconnect() {
31
+ for (const target of this.#observedTargets) {
32
+ target.removeEventListener("compositionstart", this.#handleStart);
33
+ target.removeEventListener("compositionend", this.#handleEnd);
34
+ }
35
+ this.#observedTargets.clear();
36
+ this.#activeTargets.clear();
37
+ }
38
+ /** True when lifecycle tracking or the current event reports composition. */
39
+ isComposing(event) {
40
+ return this.#activeTargets.size > 0 || event?.isComposing === true;
41
+ }
42
+ #handleStart = (event) => {
43
+ if (event.currentTarget) this.#activeTargets.add(event.currentTarget);
44
+ this.#onStart?.(event);
45
+ };
46
+ #handleEnd = (event) => {
47
+ if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);
48
+ this.#onEnd?.(event);
49
+ };
50
+ };
51
+
52
+ // src/utils/escape_layer.ts
53
+ var EscapeLayer = class _EscapeLayer {
54
+ static #registries = /* @__PURE__ */ new WeakMap();
55
+ #ownerDocument = null;
56
+ /** Dismissal callback while active; `null` when inactive. */
57
+ #onDismiss = null;
58
+ /** Live predicate deciding whether the layer claims a press; `null` = always. */
59
+ #claims = null;
60
+ /**
61
+ * Activates this layer at the top of its document's Escape stack, installing
62
+ * the document's shared resolver listener if this is its first layer.
63
+ * Re-activating an already-active layer moves it to the top.
64
+ */
65
+ activate(ownerDocument = document, options) {
66
+ this.deactivate();
67
+ let registry = _EscapeLayer.#registries.get(ownerDocument);
68
+ if (!registry) {
69
+ registry = _EscapeLayer.#createRegistry();
70
+ _EscapeLayer.#registries.set(ownerDocument, registry);
71
+ ownerDocument.addEventListener("keydown", registry.onKeydown);
72
+ }
73
+ registry.stack.push(this);
74
+ this.#ownerDocument = ownerDocument;
75
+ this.#onDismiss = options.onDismiss;
76
+ this.#claims = options.claims ?? null;
77
+ }
78
+ /**
79
+ * Removes this layer from its document's Escape stack, uninstalling the
80
+ * shared listener when the stack empties. Safe to call when inactive.
81
+ */
82
+ deactivate() {
83
+ const ownerDocument = this.#ownerDocument;
84
+ if (!ownerDocument) return;
85
+ const registry = _EscapeLayer.#registries.get(ownerDocument);
86
+ if (registry) {
87
+ const index = registry.stack.lastIndexOf(this);
88
+ if (index >= 0) registry.stack.splice(index, 1);
89
+ if (registry.stack.length === 0) {
90
+ ownerDocument.removeEventListener("keydown", registry.onKeydown);
91
+ _EscapeLayer.#registries.delete(ownerDocument);
92
+ }
93
+ }
94
+ this.#ownerDocument = null;
95
+ this.#onDismiss = null;
96
+ this.#claims = null;
97
+ }
98
+ /**
99
+ * Whether this active layer would own a press right now: it is the topmost
100
+ * layer whose {@link EscapeLayerOptions.claims} passes. Exposed for tests
101
+ * and diagnostics — production dismissal goes through the shared listener.
102
+ */
103
+ get ownsEscape() {
104
+ const ownerDocument = this.#ownerDocument;
105
+ if (!ownerDocument) return false;
106
+ const registry = _EscapeLayer.#registries.get(ownerDocument);
107
+ if (!registry) return false;
108
+ return _EscapeLayer.#resolveOwner(registry.stack) === this;
109
+ }
110
+ /** Builds a document's registry with its shared resolver listener. */
111
+ static #createRegistry() {
112
+ const registry = {
113
+ stack: [],
114
+ onKeydown: (event) => {
115
+ if (event.key !== "Escape" || event.defaultPrevented || event.isComposing) return;
116
+ const owner = _EscapeLayer.#resolveOwner(registry.stack);
117
+ if (!owner) return;
118
+ event.preventDefault();
119
+ owner.#onDismiss?.();
120
+ }
121
+ };
122
+ return registry;
123
+ }
124
+ /** The topmost stack layer whose claims predicate passes, or `null`. */
125
+ static #resolveOwner(stack) {
126
+ for (let index = stack.length - 1; index >= 0; index--) {
127
+ const layer = stack[index];
128
+ if (!layer) continue;
129
+ if (layer.#claims && !layer.#claims()) continue;
130
+ return layer;
131
+ }
132
+ return null;
133
+ }
134
+ };
135
+
5
136
  // src/utils/focus_trap.ts
6
137
  var FOCUSABLE = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
7
138
  var FocusTrap = class {
@@ -15,6 +146,8 @@ var FocusTrap = class {
15
146
  #inertedSiblings = [];
16
147
  /** Whether the modal side effects are currently applied. */
17
148
  #activeState = false;
149
+ /** Registers the trap on the shared Escape stack while active (see {@link EscapeLayer}). */
150
+ #escapeLayer = new EscapeLayer();
18
151
  /** Returns the trapped element; called on every operation for the live target. */
19
152
  #getContainer;
20
153
  /** Closing/focus hooks; see {@link FocusTrapOptions}. */
@@ -49,6 +182,9 @@ var FocusTrap = class {
49
182
  }
50
183
  if (this.#flag(this.#options.isolate, true)) this.#isolateBackground();
51
184
  document.addEventListener("keydown", this.#onKeydown);
185
+ document.addEventListener("turbo:before-cache", this.#onBeforeCache);
186
+ const onEscape = this.#options.onEscape;
187
+ if (onEscape) this.#escapeLayer.activate(document, { onDismiss: () => onEscape() });
52
188
  if (this.#flag(this.#options.autoFocus, true)) this.#focusInitial();
53
189
  }
54
190
  /**
@@ -61,7 +197,9 @@ var FocusTrap = class {
61
197
  deactivate({ restoreFocus = true } = {}) {
62
198
  if (!this.#activeState) return;
63
199
  this.#activeState = false;
200
+ this.#escapeLayer.deactivate();
64
201
  document.removeEventListener("keydown", this.#onKeydown);
202
+ document.removeEventListener("turbo:before-cache", this.#onBeforeCache);
65
203
  if (this.#scrollLocked) {
66
204
  document.body.style.overflow = this.#previousBodyOverflow;
67
205
  this.#scrollLocked = false;
@@ -77,15 +215,23 @@ var FocusTrap = class {
77
215
  if (option === void 0) return fallback;
78
216
  return typeof option === "function" ? option() : option;
79
217
  }
80
- /** Handles `Escape` (delegated) and `Tab` (focus trap) while active. */
218
+ /**
219
+ * Reverts the side effects just before Turbo caches the page snapshot, so an
220
+ * overlay left open does not bake the scroll lock into `body[style]` — a
221
+ * restored page would feed that locked value back into {@link activate} as the
222
+ * baseline, and closing would then never unlock the page. Markup state stays
223
+ * untouched (restore-open designs reopen against a clean baseline), and focus
224
+ * is left alone mid-navigation. The listener lives only while active.
225
+ */
226
+ #onBeforeCache = () => {
227
+ this.deactivate({ restoreFocus: false });
228
+ };
229
+ /**
230
+ * Handles `Tab` (focus trap) while active. `Escape` dismissal is owned by the
231
+ * shared {@link EscapeLayer} resolver, so Tab trapping stays independent of
232
+ * which layer currently owns Escape.
233
+ */
81
234
  #onKeydown = (event) => {
82
- if (event.key === "Escape") {
83
- if (this.#options.onEscape) {
84
- event.preventDefault();
85
- this.#options.onEscape();
86
- }
87
- return;
88
- }
89
235
  if (event.key === "Tab") this.#trapTab(event);
90
236
  };
91
237
  /** Keeps `Tab` focus cycling within the container's focusable elements. */
@@ -159,7 +305,9 @@ var FocusTrap = class {
159
305
  };
160
306
 
161
307
  // src/controllers/command_palette_controller.ts
162
- var CommandPaletteController = class extends Controller {
308
+ var CommandPaletteController = class _CommandPaletteController extends Controller {
309
+ static #ORIGINAL_ARIA_DISABLED = "data-command-palette-original-aria-disabled";
310
+ static #ABSENT_ARIA_DISABLED = "absent";
163
311
  static targets = ["dialog", "input", "list", "option", "empty"];
164
312
  static values = {
165
313
  hotkey: { type: String, default: "mod+k" },
@@ -177,6 +325,12 @@ var CommandPaletteController = class extends Controller {
177
325
  static events = ["select"];
178
326
  /** The index of the currently active option within the visible subset. */
179
327
  #activeIndex = -1;
328
+ /** Owns IME lifecycle state; confirmed text re-filters an open palette once. */
329
+ #composition = new CompositionTracker({
330
+ onEnd: () => {
331
+ if (this.#isOpen) this.filter();
332
+ }
333
+ });
180
334
  /**
181
335
  * Owns the modal side effects (focus trap, scroll lock, background `inert`, focus
182
336
  * restore). Escape closes; focus on open goes to the input, and is restored to
@@ -199,6 +353,8 @@ var CommandPaletteController = class extends Controller {
199
353
  */
200
354
  connect() {
201
355
  document.addEventListener("keydown", this.#onGlobalKeydown);
356
+ if (this.hasInputTarget) this.#composition.observe(this.inputTarget);
357
+ this.#syncOptionSemantics();
202
358
  const shouldOpen = this.#isOpen || this.openValue;
203
359
  this.#resetToClosedState();
204
360
  if (shouldOpen) this.open();
@@ -206,9 +362,22 @@ var CommandPaletteController = class extends Controller {
206
362
  /** Tears down the global hotkey listener and reverts the modal side effects. */
207
363
  disconnect() {
208
364
  document.removeEventListener("keydown", this.#onGlobalKeydown);
365
+ this.#composition.disconnect();
209
366
  this.#trap.deactivate({ restoreFocus: false });
210
367
  this.#resetToClosedState();
211
368
  }
369
+ /** Initializes semantics for an option added before or after the controller connects. */
370
+ optionTargetConnected(option) {
371
+ this.#syncOptionSemanticsFor(option);
372
+ }
373
+ /** Tracks an input added initially or after connect without extra consumer actions. */
374
+ inputTargetConnected(input) {
375
+ this.#composition.observe(input);
376
+ }
377
+ /** Removes controller-owned listeners when the input target is replaced or removed. */
378
+ inputTargetDisconnected(input) {
379
+ this.#composition.unobserve(input);
380
+ }
212
381
  /** Toggles the open state of the command palette. */
213
382
  toggle() {
214
383
  if (this.#isOpen) {
@@ -233,8 +402,9 @@ var CommandPaletteController = class extends Controller {
233
402
  this.#trap.deactivate();
234
403
  }
235
404
  /** Filters option elements in-memory matching the input value. Bound to input target. */
236
- filter() {
237
- if (!this.hasInputTarget) return;
405
+ filter(event) {
406
+ if (!this.hasInputTarget || this.#composition.isComposing(event)) return;
407
+ this.#syncOptionSemantics();
238
408
  const query = this.inputTarget.value.trim().toLowerCase();
239
409
  let hasSelectableMatch = false;
240
410
  for (const option of this.optionTargets) {
@@ -257,6 +427,7 @@ var CommandPaletteController = class extends Controller {
257
427
  const target = event.currentTarget;
258
428
  if (!target) return;
259
429
  const option = target.closest("[role='option']");
430
+ if (option) this.#syncOptionSemanticsFor(option);
260
431
  if (option && !this.#isDisabled(option)) {
261
432
  this.#confirmSelection(option);
262
433
  }
@@ -272,7 +443,7 @@ var CommandPaletteController = class extends Controller {
272
443
  * focus — not only the input.
273
444
  */
274
445
  onKeydown(event) {
275
- if (!this.#isOpen) return;
446
+ if (this.#composition.isComposing(event) || !this.#isOpen) return;
276
447
  switch (event.key) {
277
448
  case "ArrowDown":
278
449
  event.preventDefault();
@@ -307,23 +478,25 @@ var CommandPaletteController = class extends Controller {
307
478
  this.#setActiveIndex(newIndex);
308
479
  }
309
480
  #setActiveIndex(index) {
481
+ this.#syncOptionSemantics();
310
482
  const visible = this.#visibleOptions;
311
- this.#activeIndex = index;
312
- visible.forEach((option, i) => {
313
- if (i === index) {
314
- option.setAttribute("aria-selected", "true");
315
- option.setAttribute("data-active", "true");
316
- if (this.hasInputTarget) {
317
- this.inputTarget.setAttribute("aria-activedescendant", option.id || "");
318
- }
319
- option.scrollIntoView({ block: "nearest" });
483
+ const activeOption = visible[index] ?? null;
484
+ this.#activeIndex = activeOption ? index : -1;
485
+ for (const option of this.optionTargets) {
486
+ option.setAttribute("aria-selected", "false");
487
+ option.removeAttribute("data-active");
488
+ }
489
+ if (activeOption) {
490
+ activeOption.setAttribute("aria-selected", "true");
491
+ activeOption.setAttribute("data-active", "true");
492
+ activeOption.scrollIntoView({ block: "nearest" });
493
+ }
494
+ if (this.hasInputTarget) {
495
+ if (activeOption?.id) {
496
+ this.inputTarget.setAttribute("aria-activedescendant", activeOption.id);
320
497
  } else {
321
- option.setAttribute("aria-selected", "false");
322
- option.removeAttribute("data-active");
498
+ this.inputTarget.removeAttribute("aria-activedescendant");
323
499
  }
324
- });
325
- if (index === -1 && this.hasInputTarget) {
326
- this.inputTarget.removeAttribute("aria-activedescendant");
327
500
  }
328
501
  }
329
502
  #confirmSelection(option) {
@@ -336,8 +509,9 @@ var CommandPaletteController = class extends Controller {
336
509
  for (const option of this.optionTargets) {
337
510
  option.removeAttribute("hidden");
338
511
  }
339
- if (this.hasEmptyTarget) this.emptyTarget.hidden = true;
340
- this.#setActiveIndex(0);
512
+ const hasSelectableOption = this.#visibleOptions.length > 0;
513
+ if (this.hasEmptyTarget) this.emptyTarget.hidden = hasSelectableOption;
514
+ this.#setActiveIndex(hasSelectableOption ? 0 : -1);
341
515
  }
342
516
  get #visibleOptions() {
343
517
  return this.optionTargets.filter(
@@ -345,23 +519,66 @@ var CommandPaletteController = class extends Controller {
345
519
  );
346
520
  }
347
521
  #isDisabled(option) {
348
- return option.dataset.disabled === "true";
522
+ return option.dataset.disabled === "true" || option.getAttribute("aria-disabled") === "true";
523
+ }
524
+ /** Synchronizes every option's controller-owned selection and disabled semantics. */
525
+ #syncOptionSemantics() {
526
+ for (const option of this.optionTargets) this.#syncOptionSemanticsFor(option);
527
+ }
528
+ /** Reflects `data-disabled` to ARIA without losing a pre-existing authored value. */
529
+ #syncOptionSemanticsFor(option) {
530
+ if (!option.hasAttribute("aria-selected")) option.setAttribute("aria-selected", "false");
531
+ const originalAttribute = _CommandPaletteController.#ORIGINAL_ARIA_DISABLED;
532
+ const originalValue = option.getAttribute(originalAttribute);
533
+ if (option.dataset.disabled === "true") {
534
+ if (originalValue === null) {
535
+ option.setAttribute(
536
+ originalAttribute,
537
+ option.getAttribute("aria-disabled") ?? _CommandPaletteController.#ABSENT_ARIA_DISABLED
538
+ );
539
+ }
540
+ option.setAttribute("aria-disabled", "true");
541
+ return;
542
+ }
543
+ if (originalValue === null) return;
544
+ if (originalValue === _CommandPaletteController.#ABSENT_ARIA_DISABLED) {
545
+ option.removeAttribute("aria-disabled");
546
+ } else {
547
+ option.setAttribute("aria-disabled", originalValue);
548
+ }
549
+ option.removeAttribute(originalAttribute);
349
550
  }
350
551
  get #isOpen() {
351
552
  return this.hasDialogTarget && !this.dialogTarget.hidden;
352
553
  }
353
554
  #onGlobalKeydown = (event) => {
354
- const hotkey = this.hotkeyValue.toLowerCase();
355
- const isMod = hotkey.includes("mod+");
356
- const key = hotkey.split("+").pop();
357
- if (!key) return;
358
- const modPressed = isMod ? event.metaKey || event.ctrlKey : !event.metaKey && !event.ctrlKey;
359
- const keyMatch = event.key.toLowerCase() === key;
360
- if (modPressed && keyMatch) {
361
- event.preventDefault();
362
- this.toggle();
363
- }
555
+ if (this.#composition.isComposing(event) || !this.#matchesHotkey(event)) return;
556
+ event.preventDefault();
557
+ this.toggle();
364
558
  };
559
+ /** Matches the configured `mod+key` or bare-key hotkey without extra modifiers. */
560
+ #matchesHotkey(event) {
561
+ const hotkey = this.#parseHotkey();
562
+ if (!hotkey || event.altKey || event.shiftKey) return false;
563
+ if (hotkey.requiresMod) {
564
+ const hasExactlyOneModKey = event.metaKey !== event.ctrlKey;
565
+ if (!hasExactlyOneModKey) return false;
566
+ } else if (event.metaKey || event.ctrlKey) {
567
+ return false;
568
+ }
569
+ return event.key.toLowerCase() === hotkey.key;
570
+ }
571
+ /** Parses the intentionally small public hotkey grammar. */
572
+ #parseHotkey() {
573
+ const parts = this.hotkeyValue.toLowerCase().split("+");
574
+ if (parts.length === 1 && parts[0] && parts[0] !== "mod") {
575
+ return { key: parts[0], requiresMod: false };
576
+ }
577
+ if (parts.length === 2 && parts[0] === "mod" && parts[1]) {
578
+ return { key: parts[1], requiresMod: true };
579
+ }
580
+ return null;
581
+ }
365
582
  /** Resets transient open state so reconnect starts from a predictable closed snapshot. */
366
583
  #resetToClosedState() {
367
584
  this.#activeIndex = -1;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/focus_trap.ts","../../src/controllers/command_palette_controller.ts"],"names":[],"mappings":"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","file":"command_palette_controller.js","sourcesContent":["/**\n * Modal focus-trap primitive shared by the modal-overlay controllers\n * (dialog / alert-dialog / drawer).\n *\n * The WAI-ARIA APG modal pattern is more than \"cycle Tab inside a box\": a modal\n * also locks background scroll, makes the rest of the page `inert` (so assistive\n * technology and pointer/Tab cannot reach it, honoring `aria-modal=\"true\"`),\n * sends focus inside on open, and restores it to the opener on close — and every\n * one of those side effects must be reverted if the element is torn down while\n * open (a Turbo navigation mid-dialog). {@link FocusTrap} owns that whole modal\n * lifecycle so each controller only decides *when* to open/close and *what*\n * \"close\" means.\n *\n * It is intentionally **policy-free about closing**. Escape semantics differ per\n * widget (a plain dialog just closes; an alert-dialog closes *as a cancel* with a\n * reason; a drawer runs an exit transition), so the trap merely forwards Escape\n * to an {@link FocusTrapOptions.onEscape | onEscape} callback and never decides on\n * its own what closing entails.\n *\n * @remarks\n * The container is read through a getter so a controller can hand over a Stimulus\n * target without worrying about when the trap instance is constructed relative to\n * `connect()`.\n */\n\n/**\n * Selector matching the elements considered focusable. Shared by the trap's Tab\n * cycling and by form-validation's invalid-focus delegation.\n */\nexport const FOCUSABLE =\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** Behavior hooks a controller supplies when constructing a {@link FocusTrap}. */\nexport interface FocusTrapOptions {\n /**\n * Called when `Escape` is pressed while the trap is active. When omitted,\n * `Escape` is left alone (the trap never closes itself). The trap calls\n * `preventDefault()` before invoking it.\n */\n onEscape?: () => void;\n /**\n * Returns the element to focus when the trap activates. When it returns `null`\n * (or is omitted), the first focusable descendant is used, falling back to the\n * container itself (made programmatically focusable with `tabindex=-1`).\n */\n initialFocus?: () => HTMLElement | null;\n /**\n * Returns the element to focus on deactivation when nothing was focused before\n * the trap opened (e.g. the trigger). The element focused *before* opening\n * always takes precedence.\n */\n fallbackFocus?: () => HTMLElement | null;\n /**\n * Lock background scroll (`body` overflow) while active. Defaults to `true` for\n * the modal overlays; a lighter focus scope passes `false`. Read on `activate`.\n */\n lockScroll?: boolean | (() => boolean);\n /**\n * Make background siblings `inert` while active (the `aria-modal` isolation).\n * Defaults to `true` for the modal overlays; a soft focus scope can opt out so\n * the background stays reachable while `Tab` still cycles inside. Read on `activate`.\n */\n isolate?: boolean | (() => boolean);\n /**\n * Move focus inside on `activate`. Defaults to `true`; a focus scope that only\n * wants the `Tab` boundary (no focus move) passes `false`. Read on `activate`.\n */\n autoFocus?: boolean | (() => boolean);\n}\n\n/**\n * Owns the modal side effects (scroll lock, background `inert`, focus trap, focus\n * restore) for a single container, applied on {@link activate} and reverted on\n * {@link deactivate}.\n */\nexport class FocusTrap {\n /** The element focused before activation, restored on deactivation. */\n #previouslyFocused: HTMLElement | null = null;\n /** The body's inline `overflow` before locking, restored on deactivation. */\n #previousBodyOverflow = \"\";\n /** Whether scroll was locked this activation (so it is only restored if applied). */\n #scrollLocked = false;\n /** Background siblings made `inert` while active, restored on deactivation. */\n #inertedSiblings: HTMLElement[] = [];\n /** Whether the modal side effects are currently applied. */\n #activeState = false;\n\n /** Returns the trapped element; called on every operation for the live target. */\n readonly #getContainer: () => HTMLElement;\n /** Closing/focus hooks; see {@link FocusTrapOptions}. */\n readonly #options: FocusTrapOptions;\n\n /**\n * @param getContainer - Returns the trapped element. Called on every operation\n * so the live target is always used.\n * @param options - Closing/focus hooks; see {@link FocusTrapOptions}.\n */\n constructor(getContainer: () => HTMLElement, options: FocusTrapOptions = {}) {\n this.#getContainer = getContainer;\n this.#options = options;\n }\n\n /** Whether the trap is currently active. */\n get active(): boolean {\n return this.#activeState;\n }\n\n /**\n * Applies the trap: records the current focus, optionally locks background scroll\n * and makes background siblings `inert`, listens for `Tab`/`Escape`, and (unless\n * `autoFocus` is off) moves focus inside. No-ops if already active.\n */\n activate(): void {\n if (this.#activeState) return;\n this.#activeState = true;\n // Record the opener so it can be refocused on close. `<body>` (the default\n // active element when nothing is focused) is treated as \"nothing\", so the\n // fallback target — typically the trigger — wins in that case.\n const active = document.activeElement;\n this.#previouslyFocused =\n active instanceof HTMLElement && active !== document.body ? active : null;\n if (this.#flag(this.#options.lockScroll, true)) {\n this.#previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n this.#scrollLocked = true;\n }\n if (this.#flag(this.#options.isolate, true)) this.#isolateBackground();\n document.addEventListener(\"keydown\", this.#onKeydown);\n if (this.#flag(this.#options.autoFocus, true)) this.#focusInitial();\n }\n\n /**\n * Reverts every side effect applied by {@link activate}. No-ops if inactive, so\n * a controller can call it defensively from both `close()` and `disconnect()`.\n *\n * @param restoreFocus - Move focus back to the opener (default `true`). Pass\n * `false` on teardown (`disconnect`), where yanking focus is undesirable.\n */\n deactivate({ restoreFocus = true }: { restoreFocus?: boolean } = {}): void {\n if (!this.#activeState) return;\n this.#activeState = false;\n document.removeEventListener(\"keydown\", this.#onKeydown);\n if (this.#scrollLocked) {\n document.body.style.overflow = this.#previousBodyOverflow;\n this.#scrollLocked = false;\n }\n this.#releaseBackground();\n if (restoreFocus) {\n const target = this.#previouslyFocused ?? this.#options.fallbackFocus?.() ?? null;\n target?.focus();\n }\n }\n\n /** Resolves a boolean-or-getter option, defaulting when it was not provided. */\n #flag(option: boolean | (() => boolean) | undefined, fallback: boolean): boolean {\n if (option === undefined) return fallback;\n return typeof option === \"function\" ? option() : option;\n }\n\n /** Handles `Escape` (delegated) and `Tab` (focus trap) while active. */\n readonly #onKeydown = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n if (this.#options.onEscape) {\n event.preventDefault();\n this.#options.onEscape();\n }\n return;\n }\n if (event.key === \"Tab\") this.#trapTab(event);\n };\n\n /** Keeps `Tab` focus cycling within the container's focusable elements. */\n #trapTab(event: KeyboardEvent): void {\n const focusable = this.#focusableElements();\n if (focusable.length === 0) {\n event.preventDefault();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement;\n\n // If focus has somehow escaped the container, pull it back to the first item.\n if (!(active instanceof Node) || !this.#getContainer().contains(active)) {\n event.preventDefault();\n first?.focus();\n return;\n }\n\n if (event.shiftKey && active === first) {\n event.preventDefault();\n last?.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first?.focus();\n }\n }\n\n /**\n * Marks every element outside the container's subtree as `inert` so background\n * content cannot be focused or reached by assistive technology, honoring the\n * `aria-modal=\"true\"` contract. An element that was *already* `inert` is left\n * untracked so `#releaseBackground` does not wrongly clear it.\n */\n #isolateBackground(): void {\n const container = this.#getContainer();\n this.#inertedSiblings = [];\n for (const sibling of Array.from(document.body.children)) {\n if (!(sibling instanceof HTMLElement)) continue;\n if (sibling.contains(container) || sibling.inert) continue;\n sibling.inert = true;\n this.#inertedSiblings.push(sibling);\n }\n }\n\n /** Reverts the `inert` flags applied by `#isolateBackground`. */\n #releaseBackground(): void {\n for (const sibling of this.#inertedSiblings) {\n sibling.inert = false;\n }\n this.#inertedSiblings = [];\n }\n\n /** Moves focus to the initial target, the first focusable, or the container. */\n #focusInitial(): void {\n const preferred = this.#options.initialFocus?.();\n if (preferred) {\n preferred.focus();\n return;\n }\n const focusable = this.#focusableElements();\n if (focusable[0]) {\n focusable[0].focus();\n return;\n }\n const container = this.#getContainer();\n container.tabIndex = -1;\n container.focus();\n }\n\n /** Collects the container's currently focusable descendants in DOM order. */\n #focusableElements(): HTMLElement[] {\n return Array.from(this.#getContainer().querySelectorAll<HTMLElement>(FOCUSABLE)).filter(\n (el) => !el.hidden,\n );\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { FocusTrap } from \"../utils/focus_trap\";\n\n/**\n * Headless, highly accessible Command Palette behavior.\n *\n * Markup contract (identifier: `stimeo--command-palette`):\n * <div data-controller=\"stimeo--command-palette\"\n * data-stimeo--command-palette-hotkey-value=\"mod+k\">\n * <div data-stimeo--command-palette-target=\"dialog\" role=\"dialog\"\n * aria-modal=\"true\" aria-label=\"Command palette\"\n * data-action=\"click->stimeo--command-palette#closeOnBackdrop\" hidden>\n * <input data-stimeo--command-palette-target=\"input\" role=\"combobox\"\n * aria-expanded=\"false\" aria-controls=\"cmdk-list\"\n * aria-autocomplete=\"list\" aria-label=\"Search commands\"\n * data-action=\"input->stimeo--command-palette#filter\n * keydown->stimeo--command-palette#onKeydown\" />\n * <ul id=\"cmdk-list\" data-stimeo--command-palette-target=\"list\" role=\"listbox\">\n * <li id=\"cmd-new\" role=\"option\"\n * data-stimeo--command-palette-target=\"option\"\n * data-action=\"click->stimeo--command-palette#selectByClick\">New…</li>\n * <li role=\"option\" data-stimeo--command-palette-target=\"option\"\n * data-disabled=\"true\">Section heading (shown but not selectable)</li>\n * </ul>\n * <p data-stimeo--command-palette-target=\"empty\" hidden>No commands</p>\n * </div>\n * </div>\n *\n * Implements a composite **Dialog (Modal)** + **Combobox** (Listbox options) pattern.\n * A customizable global hotkey (Command/Ctrl + K) toggles it, focus is trapped\n * within the dialog, virtual focus roves via `aria-activedescendant`, and filtering\n * is performed in memory.\n *\n * @remarks\n * Behavior only. The combobox concerns (filtering, virtual focus, selection) live\n * here; the modal lifecycle — focus trap, scroll lock, background `inert`, focus\n * restore, and teardown reversal — is delegated to the shared {@link FocusTrap}\n * primitive (also used by dialog / alert-dialog / drawer). `Escape` closes and\n * `Tab`/`Shift+Tab` cycle focus regardless of which element inside the dialog holds\n * focus (input, close button, …), because the trap listens at the document level.\n * Styling, transitions, and the actual command handlers remain the consumer's.\n */\nexport class CommandPaletteController extends Controller<HTMLElement> {\n static override targets = [\"dialog\", \"input\", \"list\", \"option\", \"empty\"];\n static override values = {\n hotkey: { type: String, default: \"mod+k\" },\n open: { type: Boolean, default: false },\n };\n static actions = [\n \"close\",\n \"closeOnBackdrop\",\n \"filter\",\n \"onKeydown\",\n \"open\",\n \"selectByClick\",\n \"toggle\",\n ] as const;\n static events = [\"select\"] as const;\n\n declare readonly dialogTarget: HTMLElement;\n declare readonly inputTarget: HTMLInputElement;\n declare readonly listTarget: HTMLElement;\n declare readonly optionTargets: HTMLElement[];\n declare readonly emptyTarget: HTMLElement;\n\n declare readonly hasDialogTarget: boolean;\n declare readonly hasInputTarget: boolean;\n declare readonly hasListTarget: boolean;\n declare readonly hasEmptyTarget: boolean;\n\n declare hotkeyValue: string;\n declare openValue: boolean;\n\n /** The index of the currently active option within the visible subset. */\n #activeIndex = -1;\n\n /**\n * Owns the modal side effects (focus trap, scroll lock, background `inert`, focus\n * restore). Escape closes; focus on open goes to the input, and is restored to\n * whatever opened the palette on close.\n */\n readonly #trap = new FocusTrap(() => this.dialogTarget, {\n onEscape: () => this.close(),\n initialFocus: () => (this.hasInputTarget ? this.inputTarget : null),\n });\n\n /**\n * Initializes the global hotkey handler and establishes the initial open state.\n *\n * The DOM is the source of truth on reconnect (Turbo cache restore / morph): if\n * the restored snapshot already shows the dialog open, honor that rather than\n * re-deriving from the declarative `open` Value (which would slam a user-opened\n * palette shut). The `open` Value only seeds the initial state of a genuinely\n * fresh render. We normalize to a clean closed baseline first so {@link open}\n * runs its full setup — the {@link FocusTrap} is a fresh instance after a\n * reconnect and must be re-activated.\n */\n override connect(): void {\n document.addEventListener(\"keydown\", this.#onGlobalKeydown);\n const shouldOpen = this.#isOpen || this.openValue;\n this.#resetToClosedState();\n if (shouldOpen) this.open();\n }\n\n /** Tears down the global hotkey listener and reverts the modal side effects. */\n override disconnect(): void {\n document.removeEventListener(\"keydown\", this.#onGlobalKeydown);\n this.#trap.deactivate({ restoreFocus: false });\n this.#resetToClosedState();\n }\n\n /** Toggles the open state of the command palette. */\n toggle(): void {\n if (this.#isOpen) {\n this.close();\n } else {\n this.open();\n }\n }\n\n /** Opens the palette, traps focus, and shifts focus to the input. */\n open(): void {\n if (!this.hasDialogTarget || this.#isOpen) return;\n this.dialogTarget.hidden = false;\n this.openValue = true;\n if (this.hasInputTarget) this.inputTarget.setAttribute(\"aria-expanded\", \"true\");\n this.#resetFilter();\n this.#trap.activate();\n }\n\n /** Closes the palette and restores focus back to the opener. */\n close(): void {\n if (!this.hasDialogTarget || !this.#isOpen) return;\n this.#resetToClosedState();\n this.#trap.deactivate();\n }\n\n /** Filters option elements in-memory matching the input value. Bound to input target. */\n filter(): void {\n if (!this.hasInputTarget) return;\n const query = this.inputTarget.value.trim().toLowerCase();\n // Disabled options (e.g. group headings) may still be shown, but they do not\n // count toward the empty state and are never navigable/selectable.\n let hasSelectableMatch = false;\n\n for (const option of this.optionTargets) {\n const searchText = (option.dataset.searchValue || option.textContent || \"\")\n .trim()\n .toLowerCase();\n const matches = searchText.includes(query);\n\n if (matches) {\n option.removeAttribute(\"hidden\");\n if (!this.#isDisabled(option)) hasSelectableMatch = true;\n } else {\n option.setAttribute(\"hidden\", \"true\");\n }\n }\n\n if (this.hasEmptyTarget) {\n this.emptyTarget.hidden = hasSelectableMatch;\n }\n\n this.#setActiveIndex(hasSelectableMatch ? 0 : -1);\n }\n\n /** Selects the clicked option. Bound to option targets. */\n selectByClick(event: MouseEvent): void {\n const target = event.currentTarget as HTMLElement | null;\n if (!target) return;\n const option = target.closest(\"[role='option']\") as HTMLElement | null;\n // Ignore clicks on disabled options (e.g. group headings) so they never fire select.\n if (option && !this.#isDisabled(option)) {\n this.#confirmSelection(option);\n }\n }\n\n /** Closes when the backdrop (the dialog target itself) is clicked, ignoring inner clicks. */\n closeOnBackdrop(event: MouseEvent): void {\n if (event.target === this.dialogTarget) this.close();\n }\n\n /**\n * Combobox navigation keys (arrows / Home / End / Enter), bound to the input.\n * `Tab` (focus trap) and `Escape` (close) are owned by the {@link FocusTrap} at\n * the document level, so they work no matter which element inside the dialog has\n * focus — not only the input.\n */\n onKeydown(event: KeyboardEvent): void {\n if (!this.#isOpen) return;\n\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n this.#navigate(1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n this.#navigate(-1);\n break;\n case \"Home\":\n event.preventDefault();\n this.#setActiveIndex(0);\n break;\n case \"End\":\n event.preventDefault();\n this.#setActiveIndex(this.#visibleOptions.length - 1);\n break;\n case \"Enter\": {\n event.preventDefault();\n const activeOption = this.#visibleOptions[this.#activeIndex];\n if (activeOption) this.#confirmSelection(activeOption);\n break;\n }\n }\n }\n\n #navigate(direction: number): void {\n const visible = this.#visibleOptions;\n if (visible.length === 0) return;\n\n let newIndex = this.#activeIndex + direction;\n if (newIndex >= visible.length) newIndex = 0;\n if (newIndex < 0) newIndex = visible.length - 1;\n\n this.#setActiveIndex(newIndex);\n }\n\n #setActiveIndex(index: number): void {\n const visible = this.#visibleOptions;\n this.#activeIndex = index;\n\n visible.forEach((option, i) => {\n if (i === index) {\n option.setAttribute(\"aria-selected\", \"true\");\n option.setAttribute(\"data-active\", \"true\");\n if (this.hasInputTarget) {\n this.inputTarget.setAttribute(\"aria-activedescendant\", option.id || \"\");\n }\n option.scrollIntoView({ block: \"nearest\" });\n } else {\n option.setAttribute(\"aria-selected\", \"false\");\n option.removeAttribute(\"data-active\");\n }\n });\n\n if (index === -1 && this.hasInputTarget) {\n this.inputTarget.removeAttribute(\"aria-activedescendant\");\n }\n }\n\n #confirmSelection(option: HTMLElement): void {\n const value = option.dataset.value || option.textContent || \"\";\n this.dispatch(\"select\", { detail: { value, option } });\n this.close();\n }\n\n #resetFilter(): void {\n if (this.hasInputTarget) this.inputTarget.value = \"\";\n for (const option of this.optionTargets) {\n option.removeAttribute(\"hidden\");\n }\n if (this.hasEmptyTarget) this.emptyTarget.hidden = true;\n this.#setActiveIndex(0);\n }\n\n get #visibleOptions(): HTMLElement[] {\n // Only options that are both shown and not disabled are navigable/selectable.\n return this.optionTargets.filter(\n (option) => !option.hasAttribute(\"hidden\") && !this.#isDisabled(option),\n );\n }\n\n #isDisabled(option: HTMLElement): boolean {\n return option.dataset.disabled === \"true\";\n }\n\n get #isOpen(): boolean {\n return this.hasDialogTarget && !this.dialogTarget.hidden;\n }\n\n readonly #onGlobalKeydown = (event: KeyboardEvent): void => {\n const hotkey = this.hotkeyValue.toLowerCase();\n const isMod = hotkey.includes(\"mod+\");\n const key = hotkey.split(\"+\").pop();\n\n if (!key) return;\n\n // \"mod\" accepts *either* Cmd or Ctrl so the documented \"Cmd+K / Ctrl+K\" works\n // on every platform — including Ctrl+K on macOS, not just Cmd+K. Without \"mod\",\n // the hotkey is a bare key that must be pressed with no Cmd/Ctrl held.\n const modPressed = isMod ? event.metaKey || event.ctrlKey : !event.metaKey && !event.ctrlKey;\n const keyMatch = event.key.toLowerCase() === key;\n\n if (modPressed && keyMatch) {\n event.preventDefault();\n this.toggle();\n }\n };\n\n /** Resets transient open state so reconnect starts from a predictable closed snapshot. */\n #resetToClosedState(): void {\n this.#activeIndex = -1;\n this.openValue = false;\n\n if (this.hasDialogTarget) {\n this.dialogTarget.hidden = true;\n }\n\n if (this.hasInputTarget) {\n this.inputTarget.setAttribute(\"aria-expanded\", \"false\");\n this.inputTarget.removeAttribute(\"aria-activedescendant\");\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/utils/composition_tracker.ts","../../src/utils/escape_layer.ts","../../src/utils/focus_trap.ts","../../src/controllers/command_palette_controller.ts"],"names":[],"mappings":";;;;;AAuCO,IAAM,qBAAN,MAAyB;AAAA,EACrB,gBAAA,uBAAuB,GAAA,EAAiB;AAAA,EACxC,cAAA,uBAAqB,GAAA,EAAiB;AAAA,EACtC,QAAA;AAAA,EACA,MAAA;AAAA,EAET,WAAA,CAAY,OAAA,GAAqC,EAAC,EAAG;AACnD,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,OAAA;AACxB,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,KAAA;AAAA,EACxB;AAAA;AAAA,EAGA,QAAQ,MAAA,EAA2B;AACjC,IAAA,IAAI,IAAA,CAAK,gBAAA,CAAiB,GAAA,CAAI,MAAM,CAAA,EAAG;AACvC,IAAA,MAAA,CAAO,gBAAA,CAAiB,kBAAA,EAAoB,IAAA,CAAK,YAAY,CAAA;AAC7D,IAAA,MAAA,CAAO,gBAAA,CAAiB,gBAAA,EAAkB,IAAA,CAAK,UAAU,CAAA;AACzD,IAAA,IAAA,CAAK,gBAAA,CAAiB,IAAI,MAAM,CAAA;AAAA,EAClC;AAAA;AAAA,EAGA,UAAU,MAAA,EAA2B;AACnC,IAAA,IAAI,CAAC,IAAA,CAAK,gBAAA,CAAiB,MAAA,CAAO,MAAM,CAAA,EAAG;AAC3C,IAAA,MAAA,CAAO,mBAAA,CAAoB,kBAAA,EAAoB,IAAA,CAAK,YAAY,CAAA;AAChE,IAAA,MAAA,CAAO,mBAAA,CAAoB,gBAAA,EAAkB,IAAA,CAAK,UAAU,CAAA;AAC5D,IAAA,IAAA,CAAK,cAAA,CAAe,OAAO,MAAM,CAAA;AAAA,EACnC;AAAA;AAAA,EAGA,UAAA,GAAmB;AACjB,IAAA,KAAA,MAAW,MAAA,IAAU,KAAK,gBAAA,EAAkB;AAC1C,MAAA,MAAA,CAAO,mBAAA,CAAoB,kBAAA,EAAoB,IAAA,CAAK,YAAY,CAAA;AAChE,MAAA,MAAA,CAAO,mBAAA,CAAoB,gBAAA,EAAkB,IAAA,CAAK,UAAU,CAAA;AAAA,IAC9D;AACA,IAAA,IAAA,CAAK,iBAAiB,KAAA,EAAM;AAC5B,IAAA,IAAA,CAAK,eAAe,KAAA,EAAM;AAAA,EAC5B;AAAA;AAAA,EAGA,YAAY,KAAA,EAAoC;AAC9C,IAAA,OAAO,IAAA,CAAK,cAAA,CAAe,IAAA,GAAO,CAAA,IAAK,OAAO,WAAA,KAAgB,IAAA;AAAA,EAChE;AAAA,EAES,YAAA,GAAe,CAAC,KAAA,KAAuB;AAC9C,IAAA,IAAI,MAAM,aAAA,EAAe,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,MAAM,aAAa,CAAA;AACpE,IAAA,IAAA,CAAK,WAAW,KAAK,CAAA;AAAA,EACvB,CAAA;AAAA,EAES,UAAA,GAAa,CAAC,KAAA,KAAuB;AAC5C,IAAA,IAAI,MAAM,aAAA,EAAe,IAAA,CAAK,cAAA,CAAe,MAAA,CAAO,MAAM,aAAa,CAAA;AACvE,IAAA,IAAA,CAAK,SAAS,KAAK,CAAA;AAAA,EACrB,CAAA;AACF,CAAA;;;ACnBO,IAAM,WAAA,GAAN,MAAM,YAAA,CAAY;AAAA,EACvB,OAAgB,WAAA,mBAAc,IAAI,OAAA,EAAuC;AAAA,EAEzE,cAAA,GAAkC,IAAA;AAAA;AAAA,EAElC,UAAA,GAAkC,IAAA;AAAA;AAAA,EAElC,OAAA,GAAkC,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOlC,QAAA,CAAS,aAAA,GAA0B,QAAA,EAAU,OAAA,EAAmC;AAC9E,IAAA,IAAA,CAAK,UAAA,EAAW;AAChB,IAAA,IAAI,QAAA,GAAW,YAAA,CAAY,WAAA,CAAY,GAAA,CAAI,aAAa,CAAA;AACxD,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,QAAA,GAAW,aAAY,eAAA,EAAgB;AACvC,MAAA,YAAA,CAAY,WAAA,CAAY,GAAA,CAAI,aAAA,EAAe,QAAQ,CAAA;AACnD,MAAA,aAAA,CAAc,gBAAA,CAAiB,SAAA,EAAW,QAAA,CAAS,SAAS,CAAA;AAAA,IAC9D;AACA,IAAA,QAAA,CAAS,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB,IAAA,IAAA,CAAK,cAAA,GAAiB,aAAA;AACtB,IAAA,IAAA,CAAK,aAAa,OAAA,CAAQ,SAAA;AAC1B,IAAA,IAAA,CAAK,OAAA,GAAU,QAAQ,MAAA,IAAU,IAAA;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAA,GAAmB;AACjB,IAAA,MAAM,gBAAgB,IAAA,CAAK,cAAA;AAC3B,IAAA,IAAI,CAAC,aAAA,EAAe;AAEpB,IAAA,MAAM,QAAA,GAAW,YAAA,CAAY,WAAA,CAAY,GAAA,CAAI,aAAa,CAAA;AAC1D,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,CAAM,WAAA,CAAY,IAAI,CAAA;AAC7C,MAAA,IAAI,SAAS,CAAA,EAAG,QAAA,CAAS,KAAA,CAAM,MAAA,CAAO,OAAO,CAAC,CAAA;AAC9C,MAAA,IAAI,QAAA,CAAS,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG;AAC/B,QAAA,aAAA,CAAc,mBAAA,CAAoB,SAAA,EAAW,QAAA,CAAS,SAAS,CAAA;AAC/D,QAAA,YAAA,CAAY,WAAA,CAAY,OAAO,aAAa,CAAA;AAAA,MAC9C;AAAA,IACF;AACA,IAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AACtB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,UAAA,GAAsB;AACxB,IAAA,MAAM,gBAAgB,IAAA,CAAK,cAAA;AAC3B,IAAA,IAAI,CAAC,eAAe,OAAO,KAAA;AAC3B,IAAA,MAAM,QAAA,GAAW,YAAA,CAAY,WAAA,CAAY,GAAA,CAAI,aAAa,CAAA;AAC1D,IAAA,IAAI,CAAC,UAAU,OAAO,KAAA;AACtB,IAAA,OAAO,YAAA,CAAY,aAAA,CAAc,QAAA,CAAS,KAAK,CAAA,KAAM,IAAA;AAAA,EACvD;AAAA;AAAA,EAGA,OAAO,eAAA,GAAuC;AAC5C,IAAA,MAAM,QAAA,GAAgC;AAAA,MACpC,OAAO,EAAC;AAAA,MACR,SAAA,EAAW,CAAC,KAAA,KAA+B;AACzC,QAAA,IAAI,MAAM,GAAA,KAAQ,QAAA,IAAY,KAAA,CAAM,gBAAA,IAAoB,MAAM,WAAA,EAAa;AAC3E,QAAA,MAAM,KAAA,GAAQ,YAAA,CAAY,aAAA,CAAc,QAAA,CAAS,KAAK,CAAA;AACtD,QAAA,IAAI,CAAC,KAAA,EAAO;AACZ,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,KAAA,CAAM,UAAA,IAAa;AAAA,MACrB;AAAA,KACF;AACA,IAAA,OAAO,QAAA;AAAA,EACT;AAAA;AAAA,EAGA,OAAO,cAAc,KAAA,EAA0C;AAC7D,IAAA,KAAA,IAAS,QAAQ,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG,KAAA,IAAS,GAAG,KAAA,EAAA,EAAS;AACtD,MAAA,MAAM,KAAA,GAAQ,MAAM,KAAK,CAAA;AACzB,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,IAAI,KAAA,CAAM,OAAA,IAAW,CAAC,KAAA,CAAM,SAAQ,EAAG;AACvC,MAAA,OAAO,KAAA;AAAA,IACT;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AACF,CAAA;;;AC9HO,IAAM,SAAA,GACX,2IAAA;AAgDK,IAAM,YAAN,MAAgB;AAAA;AAAA,EAErB,kBAAA,GAAyC,IAAA;AAAA;AAAA,EAEzC,qBAAA,GAAwB,EAAA;AAAA;AAAA,EAExB,aAAA,GAAgB,KAAA;AAAA;AAAA,EAEhB,mBAAkC,EAAC;AAAA;AAAA,EAEnC,YAAA,GAAe,KAAA;AAAA;AAAA,EAEN,YAAA,GAAe,IAAI,WAAA,EAAY;AAAA;AAAA,EAG/B,aAAA;AAAA;AAAA,EAEA,QAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOT,WAAA,CAAY,YAAA,EAAiC,OAAA,GAA4B,EAAC,EAAG;AAC3E,IAAA,IAAA,CAAK,aAAA,GAAgB,YAAA;AACrB,IAAA,IAAA,CAAK,QAAA,GAAW,OAAA;AAAA,EAClB;AAAA;AAAA,EAGA,IAAI,MAAA,GAAkB;AACpB,IAAA,OAAO,IAAA,CAAK,YAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAA,GAAiB;AACf,IAAA,IAAI,KAAK,YAAA,EAAc;AACvB,IAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AAIpB,IAAA,MAAM,SAAS,QAAA,CAAS,aAAA;AACxB,IAAA,IAAA,CAAK,qBACH,MAAA,YAAkB,WAAA,IAAe,MAAA,KAAW,QAAA,CAAS,OAAO,MAAA,GAAS,IAAA;AACvE,IAAA,IAAI,KAAK,KAAA,CAAM,IAAA,CAAK,QAAA,CAAS,UAAA,EAAY,IAAI,CAAA,EAAG;AAC9C,MAAA,IAAA,CAAK,qBAAA,GAAwB,QAAA,CAAS,IAAA,CAAK,KAAA,CAAM,QAAA;AACjD,MAAA,QAAA,CAAS,IAAA,CAAK,MAAM,QAAA,GAAW,QAAA;AAC/B,MAAA,IAAA,CAAK,aAAA,GAAgB,IAAA;AAAA,IACvB;AACA,IAAA,IAAI,IAAA,CAAK,MAAM,IAAA,CAAK,QAAA,CAAS,SAAS,IAAI,CAAA,OAAQ,kBAAA,EAAmB;AACrE,IAAA,QAAA,CAAS,gBAAA,CAAiB,SAAA,EAAW,IAAA,CAAK,UAAU,CAAA;AACpD,IAAA,QAAA,CAAS,gBAAA,CAAiB,oBAAA,EAAsB,IAAA,CAAK,cAAc,CAAA;AACnE,IAAA,MAAM,QAAA,GAAW,KAAK,QAAA,CAAS,QAAA;AAC/B,IAAA,IAAI,QAAA,EAAU,IAAA,CAAK,YAAA,CAAa,QAAA,CAAS,QAAA,EAAU,EAAE,SAAA,EAAW,MAAM,QAAA,EAAS,EAAG,CAAA;AAClF,IAAA,IAAI,IAAA,CAAK,MAAM,IAAA,CAAK,QAAA,CAAS,WAAW,IAAI,CAAA,OAAQ,aAAA,EAAc;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,WAAW,EAAE,YAAA,GAAe,IAAA,EAAK,GAAgC,EAAC,EAAS;AACzE,IAAA,IAAI,CAAC,KAAK,YAAA,EAAc;AACxB,IAAA,IAAA,CAAK,YAAA,GAAe,KAAA;AACpB,IAAA,IAAA,CAAK,aAAa,UAAA,EAAW;AAC7B,IAAA,QAAA,CAAS,mBAAA,CAAoB,SAAA,EAAW,IAAA,CAAK,UAAU,CAAA;AACvD,IAAA,QAAA,CAAS,mBAAA,CAAoB,oBAAA,EAAsB,IAAA,CAAK,cAAc,CAAA;AACtE,IAAA,IAAI,KAAK,aAAA,EAAe;AACtB,MAAA,QAAA,CAAS,IAAA,CAAK,KAAA,CAAM,QAAA,GAAW,IAAA,CAAK,qBAAA;AACpC,MAAA,IAAA,CAAK,aAAA,GAAgB,KAAA;AAAA,IACvB;AACA,IAAA,IAAA,CAAK,kBAAA,EAAmB;AACxB,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA,MAAM,SAAS,IAAA,CAAK,kBAAA,IAAsB,IAAA,CAAK,QAAA,CAAS,iBAAgB,IAAK,IAAA;AAC7E,MAAA,MAAA,EAAQ,KAAA,EAAM;AAAA,IAChB;AAAA,EACF;AAAA;AAAA,EAGA,KAAA,CAAM,QAA+C,QAAA,EAA4B;AAC/E,IAAA,IAAI,MAAA,KAAW,QAAW,OAAO,QAAA;AACjC,IAAA,OAAO,OAAO,MAAA,KAAW,UAAA,GAAa,MAAA,EAAO,GAAI,MAAA;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUS,iBAAiB,MAAY;AACpC,IAAA,IAAA,CAAK,UAAA,CAAW,EAAE,YAAA,EAAc,KAAA,EAAO,CAAA;AAAA,EACzC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOS,UAAA,GAAa,CAAC,KAAA,KAA+B;AACpD,IAAA,IAAI,KAAA,CAAM,GAAA,KAAQ,KAAA,EAAO,IAAA,CAAK,SAAS,KAAK,CAAA;AAAA,EAC9C,CAAA;AAAA;AAAA,EAGA,SAAS,KAAA,EAA4B;AACnC,IAAA,MAAM,SAAA,GAAY,KAAK,kBAAA,EAAmB;AAC1C,IAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC1B,MAAA,KAAA,CAAM,cAAA,EAAe;AACrB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,KAAA,GAAQ,UAAU,CAAC,CAAA;AACzB,IAAA,MAAM,IAAA,GAAO,SAAA,CAAU,SAAA,CAAU,MAAA,GAAS,CAAC,CAAA;AAC3C,IAAA,MAAM,SAAS,QAAA,CAAS,aAAA;AAGxB,IAAA,IAAI,EAAE,kBAAkB,IAAA,CAAA,IAAS,CAAC,KAAK,aAAA,EAAc,CAAE,QAAA,CAAS,MAAM,CAAA,EAAG;AACvE,MAAA,KAAA,CAAM,cAAA,EAAe;AACrB,MAAA,KAAA,EAAO,KAAA,EAAM;AACb,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,KAAA,CAAM,QAAA,IAAY,MAAA,KAAW,KAAA,EAAO;AACtC,MAAA,KAAA,CAAM,cAAA,EAAe;AACrB,MAAA,IAAA,EAAM,KAAA,EAAM;AAAA,IACd,CAAA,MAAA,IAAW,CAAC,KAAA,CAAM,QAAA,IAAY,WAAW,IAAA,EAAM;AAC7C,MAAA,KAAA,CAAM,cAAA,EAAe;AACrB,MAAA,KAAA,EAAO,KAAA,EAAM;AAAA,IACf;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,kBAAA,GAA2B;AACzB,IAAA,MAAM,SAAA,GAAY,KAAK,aAAA,EAAc;AACrC,IAAA,IAAA,CAAK,mBAAmB,EAAC;AACzB,IAAA,KAAA,MAAW,WAAW,KAAA,CAAM,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,QAAQ,CAAA,EAAG;AACxD,MAAA,IAAI,EAAE,mBAAmB,WAAA,CAAA,EAAc;AACvC,MAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,SAAS,CAAA,IAAK,QAAQ,KAAA,EAAO;AAClD,MAAA,OAAA,CAAQ,KAAA,GAAQ,IAAA;AAChB,MAAA,IAAA,CAAK,gBAAA,CAAiB,KAAK,OAAO,CAAA;AAAA,IACpC;AAAA,EACF;AAAA;AAAA,EAGA,kBAAA,GAA2B;AACzB,IAAA,KAAA,MAAW,OAAA,IAAW,KAAK,gBAAA,EAAkB;AAC3C,MAAA,OAAA,CAAQ,KAAA,GAAQ,KAAA;AAAA,IAClB;AACA,IAAA,IAAA,CAAK,mBAAmB,EAAC;AAAA,EAC3B;AAAA;AAAA,EAGA,aAAA,GAAsB;AACpB,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,QAAA,CAAS,YAAA,IAAe;AAC/C,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,SAAA,CAAU,KAAA,EAAM;AAChB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,SAAA,GAAY,KAAK,kBAAA,EAAmB;AAC1C,IAAA,IAAI,SAAA,CAAU,CAAC,CAAA,EAAG;AAChB,MAAA,SAAA,CAAU,CAAC,EAAE,KAAA,EAAM;AACnB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,SAAA,GAAY,KAAK,aAAA,EAAc;AACrC,IAAA,SAAA,CAAU,QAAA,GAAW,EAAA;AACrB,IAAA,SAAA,CAAU,KAAA,EAAM;AAAA,EAClB;AAAA;AAAA,EAGA,kBAAA,GAAoC;AAClC,IAAA,OAAO,KAAA,CAAM,KAAK,IAAA,CAAK,aAAA,GAAgB,gBAAA,CAA8B,SAAS,CAAC,CAAA,CAAE,MAAA;AAAA,MAC/E,CAAC,EAAA,KAAO,CAAC,EAAA,CAAG;AAAA,KACd;AAAA,EACF;AACF,CAAA;;;ACjOO,IAAM,wBAAA,GAAN,MAAM,yBAAA,SAAiC,UAAA,CAAwB;AAAA,EACpE,OAAgB,uBAAA,GAA0B,6CAAA;AAAA,EAC1C,OAAgB,qBAAA,GAAwB,QAAA;AAAA,EAExC,OAAgB,OAAA,GAAU,CAAC,UAAU,OAAA,EAAS,MAAA,EAAQ,UAAU,OAAO,CAAA;AAAA,EACvE,OAAgB,MAAA,GAAS;AAAA,IACvB,MAAA,EAAQ,EAAE,IAAA,EAAM,MAAA,EAAQ,SAAS,OAAA,EAAQ;AAAA,IACzC,IAAA,EAAM,EAAE,IAAA,EAAM,OAAA,EAAS,SAAS,KAAA;AAAM,GACxC;AAAA,EACA,OAAO,OAAA,GAAU;AAAA,IACf,OAAA;AAAA,IACA,iBAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA,MAAA;AAAA,IACA,eAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA,OAAO,MAAA,GAAS,CAAC,QAAQ,CAAA;AAAA;AAAA,EAiBzB,YAAA,GAAe,EAAA;AAAA;AAAA,EAGN,YAAA,GAAe,IAAI,kBAAA,CAAmB;AAAA,IAC7C,OAAO,MAAM;AACX,MAAA,IAAI,IAAA,CAAK,OAAA,EAAS,IAAA,CAAK,MAAA,EAAO;AAAA,IAChC;AAAA,GACD,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,KAAA,GAAQ,IAAI,SAAA,CAAU,MAAM,KAAK,YAAA,EAAc;AAAA,IACtD,QAAA,EAAU,MAAM,IAAA,CAAK,KAAA,EAAM;AAAA,IAC3B,YAAA,EAAc,MAAO,IAAA,CAAK,cAAA,GAAiB,KAAK,WAAA,GAAc;AAAA,GAC/D,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaQ,OAAA,GAAgB;AACvB,IAAA,QAAA,CAAS,gBAAA,CAAiB,SAAA,EAAW,IAAA,CAAK,gBAAgB,CAAA;AAC1D,IAAA,IAAI,KAAK,cAAA,EAAgB,IAAA,CAAK,YAAA,CAAa,OAAA,CAAQ,KAAK,WAAW,CAAA;AACnE,IAAA,IAAA,CAAK,oBAAA,EAAqB;AAC1B,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,OAAA,IAAW,IAAA,CAAK,SAAA;AACxC,IAAA,IAAA,CAAK,mBAAA,EAAoB;AACzB,IAAA,IAAI,UAAA,OAAiB,IAAA,EAAK;AAAA,EAC5B;AAAA;AAAA,EAGS,UAAA,GAAmB;AAC1B,IAAA,QAAA,CAAS,mBAAA,CAAoB,SAAA,EAAW,IAAA,CAAK,gBAAgB,CAAA;AAC7D,IAAA,IAAA,CAAK,aAAa,UAAA,EAAW;AAC7B,IAAA,IAAA,CAAK,KAAA,CAAM,UAAA,CAAW,EAAE,YAAA,EAAc,OAAO,CAAA;AAC7C,IAAA,IAAA,CAAK,mBAAA,EAAoB;AAAA,EAC3B;AAAA;AAAA,EAGA,sBAAsB,MAAA,EAA2B;AAC/C,IAAA,IAAA,CAAK,wBAAwB,MAAM,CAAA;AAAA,EACrC;AAAA;AAAA,EAGA,qBAAqB,KAAA,EAA+B;AAClD,IAAA,IAAA,CAAK,YAAA,CAAa,QAAQ,KAAK,CAAA;AAAA,EACjC;AAAA;AAAA,EAGA,wBAAwB,KAAA,EAA+B;AACrD,IAAA,IAAA,CAAK,YAAA,CAAa,UAAU,KAAK,CAAA;AAAA,EACnC;AAAA;AAAA,EAGA,MAAA,GAAe;AACb,IAAA,IAAI,KAAK,OAAA,EAAS;AAChB,MAAA,IAAA,CAAK,KAAA,EAAM;AAAA,IACb,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,IAAA,EAAK;AAAA,IACZ;AAAA,EACF;AAAA;AAAA,EAGA,IAAA,GAAa;AACX,IAAA,IAAI,CAAC,IAAA,CAAK,eAAA,IAAmB,IAAA,CAAK,OAAA,EAAS;AAC3C,IAAA,IAAA,CAAK,aAAa,MAAA,GAAS,KAAA;AAC3B,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AACjB,IAAA,IAAI,KAAK,cAAA,EAAgB,IAAA,CAAK,WAAA,CAAY,YAAA,CAAa,iBAAiB,MAAM,CAAA;AAC9E,IAAA,IAAA,CAAK,YAAA,EAAa;AAClB,IAAA,IAAA,CAAK,MAAM,QAAA,EAAS;AAAA,EACtB;AAAA;AAAA,EAGA,KAAA,GAAc;AACZ,IAAA,IAAI,CAAC,IAAA,CAAK,eAAA,IAAmB,CAAC,KAAK,OAAA,EAAS;AAC5C,IAAA,IAAA,CAAK,mBAAA,EAAoB;AACzB,IAAA,IAAA,CAAK,MAAM,UAAA,EAAW;AAAA,EACxB;AAAA;AAAA,EAGA,OAAO,KAAA,EAA0B;AAG/B,IAAA,IAAI,CAAC,IAAA,CAAK,cAAA,IAAkB,KAAK,YAAA,CAAa,WAAA,CAAY,KAAK,CAAA,EAAG;AAClE,IAAA,IAAA,CAAK,oBAAA,EAAqB;AAC1B,IAAA,MAAM,QAAQ,IAAA,CAAK,WAAA,CAAY,KAAA,CAAM,IAAA,GAAO,WAAA,EAAY;AAGxD,IAAA,IAAI,kBAAA,GAAqB,KAAA;AAEzB,IAAA,KAAA,MAAW,MAAA,IAAU,KAAK,aAAA,EAAe;AACvC,MAAA,MAAM,UAAA,GAAA,CAAc,OAAO,OAAA,CAAQ,WAAA,IAAe,OAAO,WAAA,IAAe,EAAA,EACrE,IAAA,EAAK,CACL,WAAA,EAAY;AACf,MAAA,MAAM,OAAA,GAAU,UAAA,CAAW,QAAA,CAAS,KAAK,CAAA;AAEzC,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,MAAA,CAAO,gBAAgB,QAAQ,CAAA;AAC/B,QAAA,IAAI,CAAC,IAAA,CAAK,WAAA,CAAY,MAAM,GAAG,kBAAA,GAAqB,IAAA;AAAA,MACtD,CAAA,MAAO;AACL,QAAA,MAAA,CAAO,YAAA,CAAa,UAAU,MAAM,CAAA;AAAA,MACtC;AAAA,IACF;AAEA,IAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,MAAA,IAAA,CAAK,YAAY,MAAA,GAAS,kBAAA;AAAA,IAC5B;AAEA,IAAA,IAAA,CAAK,eAAA,CAAgB,kBAAA,GAAqB,CAAA,GAAI,EAAE,CAAA;AAAA,EAClD;AAAA;AAAA,EAGA,cAAc,KAAA,EAAyB;AACrC,IAAA,MAAM,SAAS,KAAA,CAAM,aAAA;AACrB,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,OAAA,CAAQ,iBAAiB,CAAA;AAC/C,IAAA,IAAI,MAAA,EAAQ,IAAA,CAAK,uBAAA,CAAwB,MAAM,CAAA;AAE/C,IAAA,IAAI,MAAA,IAAU,CAAC,IAAA,CAAK,WAAA,CAAY,MAAM,CAAA,EAAG;AACvC,MAAA,IAAA,CAAK,kBAAkB,MAAM,CAAA;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA,EAGA,gBAAgB,KAAA,EAAyB;AACvC,IAAA,IAAI,KAAA,CAAM,MAAA,KAAW,IAAA,CAAK,YAAA,OAAmB,KAAA,EAAM;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,UAAU,KAAA,EAA4B;AAGpC,IAAA,IAAI,KAAK,YAAA,CAAa,WAAA,CAAY,KAAK,CAAA,IAAK,CAAC,KAAK,OAAA,EAAS;AAE3D,IAAA,QAAQ,MAAM,GAAA;AAAK,MACjB,KAAK,WAAA;AACH,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,IAAA,CAAK,UAAU,CAAC,CAAA;AAChB,QAAA;AAAA,MACF,KAAK,SAAA;AACH,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,IAAA,CAAK,UAAU,EAAE,CAAA;AACjB,QAAA;AAAA,MACF,KAAK,MAAA;AACH,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,IAAA,CAAK,gBAAgB,CAAC,CAAA;AACtB,QAAA;AAAA,MACF,KAAK,KAAA;AACH,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,IAAA,CAAK,eAAA,CAAgB,IAAA,CAAK,eAAA,CAAgB,MAAA,GAAS,CAAC,CAAA;AACpD,QAAA;AAAA,MACF,KAAK,OAAA,EAAS;AACZ,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,MAAM,YAAA,GAAe,IAAA,CAAK,eAAA,CAAgB,IAAA,CAAK,YAAY,CAAA;AAC3D,QAAA,IAAI,YAAA,EAAc,IAAA,CAAK,iBAAA,CAAkB,YAAY,CAAA;AACrD,QAAA;AAAA,MACF;AAAA;AACF,EACF;AAAA,EAEA,UAAU,SAAA,EAAyB;AACjC,IAAA,MAAM,UAAU,IAAA,CAAK,eAAA;AACrB,IAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AAE1B,IAAA,IAAI,QAAA,GAAW,KAAK,YAAA,GAAe,SAAA;AACnC,IAAA,IAAI,QAAA,IAAY,OAAA,CAAQ,MAAA,EAAQ,QAAA,GAAW,CAAA;AAC3C,IAAA,IAAI,QAAA,GAAW,CAAA,EAAG,QAAA,GAAW,OAAA,CAAQ,MAAA,GAAS,CAAA;AAE9C,IAAA,IAAA,CAAK,gBAAgB,QAAQ,CAAA;AAAA,EAC/B;AAAA,EAEA,gBAAgB,KAAA,EAAqB;AACnC,IAAA,IAAA,CAAK,oBAAA,EAAqB;AAC1B,IAAA,MAAM,UAAU,IAAA,CAAK,eAAA;AACrB,IAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,KAAK,CAAA,IAAK,IAAA;AACvC,IAAA,IAAA,CAAK,YAAA,GAAe,eAAe,KAAA,GAAQ,EAAA;AAE3C,IAAA,KAAA,MAAW,MAAA,IAAU,KAAK,aAAA,EAAe;AACvC,MAAA,MAAA,CAAO,YAAA,CAAa,iBAAiB,OAAO,CAAA;AAC5C,MAAA,MAAA,CAAO,gBAAgB,aAAa,CAAA;AAAA,IACtC;AAEA,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA,YAAA,CAAa,YAAA,CAAa,iBAAiB,MAAM,CAAA;AACjD,MAAA,YAAA,CAAa,YAAA,CAAa,eAAe,MAAM,CAAA;AAC/C,MAAA,YAAA,CAAa,cAAA,CAAe,EAAE,KAAA,EAAO,SAAA,EAAW,CAAA;AAAA,IAClD;AAEA,IAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,MAAA,IAAI,cAAc,EAAA,EAAI;AACpB,QAAA,IAAA,CAAK,WAAA,CAAY,YAAA,CAAa,uBAAA,EAAyB,YAAA,CAAa,EAAE,CAAA;AAAA,MACxE,CAAA,MAAO;AACL,QAAA,IAAA,CAAK,WAAA,CAAY,gBAAgB,uBAAuB,CAAA;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,kBAAkB,MAAA,EAA2B;AAC3C,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,OAAA,CAAQ,KAAA,IAAS,OAAO,WAAA,IAAe,EAAA;AAC5D,IAAA,IAAA,CAAK,QAAA,CAAS,UAAU,EAAE,MAAA,EAAQ,EAAE,KAAA,EAAO,MAAA,IAAU,CAAA;AACrD,IAAA,IAAA,CAAK,KAAA,EAAM;AAAA,EACb;AAAA,EAEA,YAAA,GAAqB;AACnB,IAAA,IAAI,IAAA,CAAK,cAAA,EAAgB,IAAA,CAAK,WAAA,CAAY,KAAA,GAAQ,EAAA;AAClD,IAAA,KAAA,MAAW,MAAA,IAAU,KAAK,aAAA,EAAe;AACvC,MAAA,MAAA,CAAO,gBAAgB,QAAQ,CAAA;AAAA,IACjC;AACA,IAAA,MAAM,mBAAA,GAAsB,IAAA,CAAK,eAAA,CAAgB,MAAA,GAAS,CAAA;AAC1D,IAAA,IAAI,IAAA,CAAK,cAAA,EAAgB,IAAA,CAAK,WAAA,CAAY,MAAA,GAAS,mBAAA;AACnD,IAAA,IAAA,CAAK,eAAA,CAAgB,mBAAA,GAAsB,CAAA,GAAI,EAAE,CAAA;AAAA,EACnD;AAAA,EAEA,IAAI,eAAA,GAAiC;AAEnC,IAAA,OAAO,KAAK,aAAA,CAAc,MAAA;AAAA,MACxB,CAAC,MAAA,KAAW,CAAC,MAAA,CAAO,YAAA,CAAa,QAAQ,CAAA,IAAK,CAAC,IAAA,CAAK,WAAA,CAAY,MAAM;AAAA,KACxE;AAAA,EACF;AAAA,EAEA,YAAY,MAAA,EAA8B;AACxC,IAAA,OAAO,OAAO,OAAA,CAAQ,QAAA,KAAa,UAAU,MAAA,CAAO,YAAA,CAAa,eAAe,CAAA,KAAM,MAAA;AAAA,EACxF;AAAA;AAAA,EAGA,oBAAA,GAA6B;AAC3B,IAAA,KAAA,MAAW,MAAA,IAAU,IAAA,CAAK,aAAA,EAAe,IAAA,CAAK,wBAAwB,MAAM,CAAA;AAAA,EAC9E;AAAA;AAAA,EAGA,wBAAwB,MAAA,EAA2B;AACjD,IAAA,IAAI,CAAC,OAAO,YAAA,CAAa,eAAe,GAAG,MAAA,CAAO,YAAA,CAAa,iBAAiB,OAAO,CAAA;AAEvF,IAAA,MAAM,oBAAoB,yBAAA,CAAyB,uBAAA;AACnD,IAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,YAAA,CAAa,iBAAiB,CAAA;AAC3D,IAAA,IAAI,MAAA,CAAO,OAAA,CAAQ,QAAA,KAAa,MAAA,EAAQ;AACtC,MAAA,IAAI,kBAAkB,IAAA,EAAM;AAC1B,QAAA,MAAA,CAAO,YAAA;AAAA,UACL,iBAAA;AAAA,UACA,MAAA,CAAO,YAAA,CAAa,eAAe,CAAA,IAAK,yBAAA,CAAyB;AAAA,SACnE;AAAA,MACF;AACA,MAAA,MAAA,CAAO,YAAA,CAAa,iBAAiB,MAAM,CAAA;AAC3C,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,kBAAkB,IAAA,EAAM;AAC5B,IAAA,IAAI,aAAA,KAAkB,0BAAyB,qBAAA,EAAuB;AACpE,MAAA,MAAA,CAAO,gBAAgB,eAAe,CAAA;AAAA,IACxC,CAAA,MAAO;AACL,MAAA,MAAA,CAAO,YAAA,CAAa,iBAAiB,aAAa,CAAA;AAAA,IACpD;AACA,IAAA,MAAA,CAAO,gBAAgB,iBAAiB,CAAA;AAAA,EAC1C;AAAA,EAEA,IAAI,OAAA,GAAmB;AACrB,IAAA,OAAO,IAAA,CAAK,eAAA,IAAmB,CAAC,IAAA,CAAK,YAAA,CAAa,MAAA;AAAA,EACpD;AAAA,EAES,gBAAA,GAAmB,CAAC,KAAA,KAA+B;AAC1D,IAAA,IAAI,IAAA,CAAK,aAAa,WAAA,CAAY,KAAK,KAAK,CAAC,IAAA,CAAK,cAAA,CAAe,KAAK,CAAA,EAAG;AAEzE,IAAA,KAAA,CAAM,cAAA,EAAe;AACrB,IAAA,IAAA,CAAK,MAAA,EAAO;AAAA,EACd,CAAA;AAAA;AAAA,EAGA,eAAe,KAAA,EAA+B;AAC5C,IAAA,MAAM,MAAA,GAAS,KAAK,YAAA,EAAa;AACjC,IAAA,IAAI,CAAC,MAAA,IAAU,KAAA,CAAM,MAAA,IAAU,KAAA,CAAM,UAAU,OAAO,KAAA;AAEtD,IAAA,IAAI,OAAO,WAAA,EAAa;AAEtB,MAAA,MAAM,mBAAA,GAAsB,KAAA,CAAM,OAAA,KAAY,KAAA,CAAM,OAAA;AACpD,MAAA,IAAI,CAAC,qBAAqB,OAAO,KAAA;AAAA,IACnC,CAAA,MAAA,IAAW,KAAA,CAAM,OAAA,IAAW,KAAA,CAAM,OAAA,EAAS;AACzC,MAAA,OAAO,KAAA;AAAA,IACT;AAEA,IAAA,OAAO,KAAA,CAAM,GAAA,CAAI,WAAA,EAAY,KAAM,MAAA,CAAO,GAAA;AAAA,EAC5C;AAAA;AAAA,EAGA,YAAA,GAA6D;AAC3D,IAAA,MAAM,QAAQ,IAAA,CAAK,WAAA,CAAY,WAAA,EAAY,CAAE,MAAM,GAAG,CAAA;AACtD,IAAA,IAAI,KAAA,CAAM,WAAW,CAAA,IAAK,KAAA,CAAM,CAAC,CAAA,IAAK,KAAA,CAAM,CAAC,CAAA,KAAM,KAAA,EAAO;AACxD,MAAA,OAAO,EAAE,GAAA,EAAK,KAAA,CAAM,CAAC,CAAA,EAAG,aAAa,KAAA,EAAM;AAAA,IAC7C;AACA,IAAA,IAAI,KAAA,CAAM,WAAW,CAAA,IAAK,KAAA,CAAM,CAAC,CAAA,KAAM,KAAA,IAAS,KAAA,CAAM,CAAC,CAAA,EAAG;AACxD,MAAA,OAAO,EAAE,GAAA,EAAK,KAAA,CAAM,CAAC,CAAA,EAAG,aAAa,IAAA,EAAK;AAAA,IAC5C;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA,EAGA,mBAAA,GAA4B;AAC1B,IAAA,IAAA,CAAK,YAAA,GAAe,EAAA;AACpB,IAAA,IAAA,CAAK,SAAA,GAAY,KAAA;AAEjB,IAAA,IAAI,KAAK,eAAA,EAAiB;AACxB,MAAA,IAAA,CAAK,aAAa,MAAA,GAAS,IAAA;AAAA,IAC7B;AAEA,IAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,MAAA,IAAA,CAAK,WAAA,CAAY,YAAA,CAAa,eAAA,EAAiB,OAAO,CAAA;AACtD,MAAA,IAAA,CAAK,WAAA,CAAY,gBAAgB,uBAAuB,CAAA;AAAA,IAC1D;AAAA,EACF;AACF","file":"command_palette_controller.js","sourcesContent":["/** Minimal event shape carrying the platform's per-event composition signal. */\nexport interface CompositionSignal {\n readonly isComposing?: boolean;\n}\n\n/** Hooks that keep component-specific work outside {@link CompositionTracker}. */\nexport interface CompositionTrackerOptions {\n /** Runs after lifecycle state is set for a `compositionstart` event. */\n readonly onStart?: (event: Event) => void;\n /** Runs after lifecycle state is cleared for a `compositionend` event. */\n readonly onEnd?: (event: Event) => void;\n}\n\n/**\n * Owns IME composition listeners and transient state for one or more event targets.\n *\n * Some browsers omit `KeyboardEvent.isComposing` on the keydown that confirms a\n * conversion. Tracking `compositionstart` through `compositionend` supplies the\n * missing lifecycle signal without relying on deprecated numeric key codes.\n * Component policy stays with the consumer: filtering, validation, and submission\n * belong in the optional {@link CompositionTrackerOptions.onEnd} callback.\n *\n * @example\n * ```ts\n * #composition = new CompositionTracker({ onEnd: () => this.filter() });\n *\n * connect(): void {\n * this.#composition.observe(this.inputTarget);\n * }\n *\n * disconnect(): void {\n * this.#composition.disconnect();\n * }\n *\n * onKeydown(event: KeyboardEvent): void {\n * if (this.#composition.isComposing(event)) return;\n * }\n * ```\n */\nexport class CompositionTracker {\n readonly #observedTargets = new Set<EventTarget>();\n readonly #activeTargets = new Set<EventTarget>();\n readonly #onStart: ((event: Event) => void) | undefined;\n readonly #onEnd: ((event: Event) => void) | undefined;\n\n constructor(options: CompositionTrackerOptions = {}) {\n this.#onStart = options.onStart;\n this.#onEnd = options.onEnd;\n }\n\n /** Starts lifecycle tracking for `target`; repeated calls are idempotent. */\n observe(target: EventTarget): void {\n if (this.#observedTargets.has(target)) return;\n target.addEventListener(\"compositionstart\", this.#handleStart);\n target.addEventListener(\"compositionend\", this.#handleEnd);\n this.#observedTargets.add(target);\n }\n\n /** Stops tracking one target and clears any active composition it owned. */\n unobserve(target: EventTarget): void {\n if (!this.#observedTargets.delete(target)) return;\n target.removeEventListener(\"compositionstart\", this.#handleStart);\n target.removeEventListener(\"compositionend\", this.#handleEnd);\n this.#activeTargets.delete(target);\n }\n\n /** Releases every listener and clears state so reconnect starts cleanly. */\n disconnect(): void {\n for (const target of this.#observedTargets) {\n target.removeEventListener(\"compositionstart\", this.#handleStart);\n target.removeEventListener(\"compositionend\", this.#handleEnd);\n }\n this.#observedTargets.clear();\n this.#activeTargets.clear();\n }\n\n /** True when lifecycle tracking or the current event reports composition. */\n isComposing(event?: CompositionSignal): boolean {\n return this.#activeTargets.size > 0 || event?.isComposing === true;\n }\n\n readonly #handleStart = (event: Event): void => {\n if (event.currentTarget) this.#activeTargets.add(event.currentTarget);\n this.#onStart?.(event);\n };\n\n readonly #handleEnd = (event: Event): void => {\n if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);\n this.#onEnd?.(event);\n };\n}\n","/**\n * Single resolver for layered Escape dismissal.\n *\n * Every Escape-dismissable overlay layer (modal focus traps, disclosure\n * overlays like dropdown/popover/menu, and hover-triggered transient layers)\n * registers here while it is open. One document-level listener per document\n * resolves each press to exactly one owner and invokes that layer's\n * {@link EscapeLayerOptions.onDismiss} — controllers never listen for a\n * dismissing Escape themselves.\n *\n * @remarks\n * Each document owns an activation-ordered stack. The owner of a press is the\n * **topmost layer whose {@link EscapeLayerOptions.claims} passes**; a layer\n * that declines is transparent, so a background overlay opened behind a modal\n * never blocks it. Because a layer opened from within another is necessarily\n * activated later, LIFO order is also inner-first for nested layers — no DOM\n * inspection is needed.\n *\n * The shared listener runs in the document bubble phase and honors\n * `event.defaultPrevented`, so an element-level widget handler that consumes\n * Escape first (an editor cancelling its edit, a combobox closing its list)\n * always wins over every registered layer — the deepest-first half of the\n * shared layered-Escape contract. A keydown that is part of an IME composition\n * (`event.isComposing`) cancels the composition, never a layer, and is ignored\n * here for every layer at once.\n *\n * A WeakMap keeps documents collectible; the listener is installed only while\n * a document's stack is non-empty, and controller lifecycle hooks guarantee\n * that disconnected layers never remain registered.\n */\n\n/** Behavior a layer registers when it activates. */\nexport interface EscapeLayerOptions {\n /**\n * Dismisses the layer. Called by the shared resolver when this layer owns a\n * press; the resolver has already consumed the event (`preventDefault()`),\n * so the callback only needs to close and place focus per the widget's\n * contract.\n */\n onDismiss: () => void;\n /**\n * Whether the layer claims the current press. Evaluated per press, so it can\n * depend on live state (e.g. \"focus is inside me or fell to the body\"). A\n * declining layer is skipped and the next layer down is consulted; omitting\n * it means the layer always claims while active.\n */\n claims?: () => boolean;\n}\n\n/** A document's stack plus the one shared listener bound to it. */\ninterface EscapeLayerRegistry {\n stack: EscapeLayer[];\n onKeydown: (event: KeyboardEvent) => void;\n}\n\n/**\n * Claims predicate shared by the click-opened disclosure overlays (dropdown /\n * popover / navigation-menu / menu / context-menu / menubar): the layer claims\n * a press while focus is inside `element`, or after focus fell to the body —\n * a click on non-focusable overlay content blurs to `<body>`, and Escape must\n * still close the overlay (the \"body-focus rescue\"). A press made after focus\n * moved to another interactive element is declined, so closing never yanks\n * focus away from where the user deliberately went.\n */\nexport function claimsWhileFocusWithin(element: Element): () => boolean {\n return () => {\n const active = element.ownerDocument.activeElement;\n return active === null || active === element.ownerDocument.body || element.contains(active);\n };\n}\n\nexport class EscapeLayer {\n static readonly #registries = new WeakMap<Document, EscapeLayerRegistry>();\n\n #ownerDocument: Document | null = null;\n /** Dismissal callback while active; `null` when inactive. */\n #onDismiss: (() => void) | null = null;\n /** Live predicate deciding whether the layer claims a press; `null` = always. */\n #claims: (() => boolean) | null = null;\n\n /**\n * Activates this layer at the top of its document's Escape stack, installing\n * the document's shared resolver listener if this is its first layer.\n * Re-activating an already-active layer moves it to the top.\n */\n activate(ownerDocument: Document = document, options: EscapeLayerOptions): void {\n this.deactivate();\n let registry = EscapeLayer.#registries.get(ownerDocument);\n if (!registry) {\n registry = EscapeLayer.#createRegistry();\n EscapeLayer.#registries.set(ownerDocument, registry);\n ownerDocument.addEventListener(\"keydown\", registry.onKeydown);\n }\n registry.stack.push(this);\n this.#ownerDocument = ownerDocument;\n this.#onDismiss = options.onDismiss;\n this.#claims = options.claims ?? null;\n }\n\n /**\n * Removes this layer from its document's Escape stack, uninstalling the\n * shared listener when the stack empties. Safe to call when inactive.\n */\n deactivate(): void {\n const ownerDocument = this.#ownerDocument;\n if (!ownerDocument) return;\n\n const registry = EscapeLayer.#registries.get(ownerDocument);\n if (registry) {\n const index = registry.stack.lastIndexOf(this);\n if (index >= 0) registry.stack.splice(index, 1);\n if (registry.stack.length === 0) {\n ownerDocument.removeEventListener(\"keydown\", registry.onKeydown);\n EscapeLayer.#registries.delete(ownerDocument);\n }\n }\n this.#ownerDocument = null;\n this.#onDismiss = null;\n this.#claims = null;\n }\n\n /**\n * Whether this active layer would own a press right now: it is the topmost\n * layer whose {@link EscapeLayerOptions.claims} passes. Exposed for tests\n * and diagnostics — production dismissal goes through the shared listener.\n */\n get ownsEscape(): boolean {\n const ownerDocument = this.#ownerDocument;\n if (!ownerDocument) return false;\n const registry = EscapeLayer.#registries.get(ownerDocument);\n if (!registry) return false;\n return EscapeLayer.#resolveOwner(registry.stack) === this;\n }\n\n /** Builds a document's registry with its shared resolver listener. */\n static #createRegistry(): EscapeLayerRegistry {\n const registry: EscapeLayerRegistry = {\n stack: [],\n onKeydown: (event: KeyboardEvent): void => {\n if (event.key !== \"Escape\" || event.defaultPrevented || event.isComposing) return;\n const owner = EscapeLayer.#resolveOwner(registry.stack);\n if (!owner) return;\n event.preventDefault();\n owner.#onDismiss?.();\n },\n };\n return registry;\n }\n\n /** The topmost stack layer whose claims predicate passes, or `null`. */\n static #resolveOwner(stack: EscapeLayer[]): EscapeLayer | null {\n for (let index = stack.length - 1; index >= 0; index--) {\n const layer = stack[index];\n if (!layer) continue;\n if (layer.#claims && !layer.#claims()) continue;\n return layer;\n }\n return null;\n }\n}\n","import { EscapeLayer } from \"./escape_layer\";\n\n/**\n * Modal focus-trap primitive shared by the modal-overlay controllers\n * (dialog / alert-dialog / confirm / drawer / command-palette / sidebar); the\n * non-modal focus scope (focus) reuses it with the modal side effects opted out.\n *\n * The WAI-ARIA APG modal pattern is more than \"cycle Tab inside a box\": a modal\n * also locks background scroll, makes the rest of the page `inert` (so assistive\n * technology and pointer/Tab cannot reach it, honoring `aria-modal=\"true\"`),\n * sends focus inside on open, and restores it to the opener on close — and every\n * one of those side effects must be reverted if the element is torn down while\n * open (a Turbo navigation mid-dialog) and kept out of the snapshot Turbo\n * caches (`turbo:before-cache`). {@link FocusTrap} owns that whole modal\n * lifecycle so each controller only decides *when* to open/close and *what*\n * \"close\" means.\n *\n * It is intentionally **policy-free about closing**. Escape semantics differ per\n * widget (a plain dialog just closes; an alert-dialog closes *as a cancel* with a\n * reason; a drawer runs an exit transition), so the trap merely forwards Escape\n * to an {@link FocusTrapOptions.onEscape | onEscape} callback and never decides on\n * its own what closing entails.\n *\n * @remarks\n * The container is read through a getter so a controller can hand over a Stimulus\n * target without worrying about when the trap instance is constructed relative to\n * `connect()`.\n */\n\n/**\n * Selector matching the elements considered focusable. Shared by the trap's Tab\n * cycling and by form-validation's invalid-focus delegation.\n */\nexport const FOCUSABLE =\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** Behavior hooks a controller supplies when constructing a {@link FocusTrap}. */\nexport interface FocusTrapOptions {\n /**\n * Called when `Escape` is pressed while the trap is active. When omitted,\n * `Escape` is left alone (the trap never joins the Escape stack). Dismissal\n * is resolved by the shared {@link EscapeLayer}: an Escape already consumed\n * by an inner handler is ignored, and among active layers the most recently\n * activated claiming one owns the press. The resolver consumes the event\n * before invoking the callback (the shared layered-Escape contract).\n */\n onEscape?: () => void;\n /**\n * Returns the element to focus when the trap activates. When it returns `null`\n * (or is omitted), the first focusable descendant is used, falling back to the\n * container itself (made programmatically focusable with `tabindex=-1`).\n */\n initialFocus?: () => HTMLElement | null;\n /**\n * Returns the element to focus on deactivation when nothing was focused before\n * the trap opened (e.g. the trigger). The element focused *before* opening\n * always takes precedence.\n */\n fallbackFocus?: () => HTMLElement | null;\n /**\n * Lock background scroll (`body` overflow) while active. Defaults to `true` for\n * the modal overlays; a lighter focus scope passes `false`. Read on `activate`.\n */\n lockScroll?: boolean | (() => boolean);\n /**\n * Make background siblings `inert` while active (the `aria-modal` isolation).\n * Defaults to `true` for the modal overlays; a soft focus scope can opt out so\n * the background stays reachable while `Tab` still cycles inside. Read on `activate`.\n */\n isolate?: boolean | (() => boolean);\n /**\n * Move focus inside on `activate`. Defaults to `true`; a focus scope that only\n * wants the `Tab` boundary (no focus move) passes `false`. Read on `activate`.\n */\n autoFocus?: boolean | (() => boolean);\n}\n\n/**\n * Owns the modal side effects (scroll lock, background `inert`, focus trap, focus\n * restore) for a single container, applied on {@link activate} and reverted on\n * {@link deactivate}.\n */\nexport class FocusTrap {\n /** The element focused before activation, restored on deactivation. */\n #previouslyFocused: HTMLElement | null = null;\n /** The body's inline `overflow` before locking, restored on deactivation. */\n #previousBodyOverflow = \"\";\n /** Whether scroll was locked this activation (so it is only restored if applied). */\n #scrollLocked = false;\n /** Background siblings made `inert` while active, restored on deactivation. */\n #inertedSiblings: HTMLElement[] = [];\n /** Whether the modal side effects are currently applied. */\n #activeState = false;\n /** Registers the trap on the shared Escape stack while active (see {@link EscapeLayer}). */\n readonly #escapeLayer = new EscapeLayer();\n\n /** Returns the trapped element; called on every operation for the live target. */\n readonly #getContainer: () => HTMLElement;\n /** Closing/focus hooks; see {@link FocusTrapOptions}. */\n readonly #options: FocusTrapOptions;\n\n /**\n * @param getContainer - Returns the trapped element. Called on every operation\n * so the live target is always used.\n * @param options - Closing/focus hooks; see {@link FocusTrapOptions}.\n */\n constructor(getContainer: () => HTMLElement, options: FocusTrapOptions = {}) {\n this.#getContainer = getContainer;\n this.#options = options;\n }\n\n /** Whether the trap is currently active. */\n get active(): boolean {\n return this.#activeState;\n }\n\n /**\n * Applies the trap: records the current focus, optionally locks background scroll\n * and makes background siblings `inert`, listens for `Tab`/`Escape`, and (unless\n * `autoFocus` is off) moves focus inside. No-ops if already active.\n */\n activate(): void {\n if (this.#activeState) return;\n this.#activeState = true;\n // Record the opener so it can be refocused on close. `<body>` (the default\n // active element when nothing is focused) is treated as \"nothing\", so the\n // fallback target — typically the trigger — wins in that case.\n const active = document.activeElement;\n this.#previouslyFocused =\n active instanceof HTMLElement && active !== document.body ? active : null;\n if (this.#flag(this.#options.lockScroll, true)) {\n this.#previousBodyOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n this.#scrollLocked = true;\n }\n if (this.#flag(this.#options.isolate, true)) this.#isolateBackground();\n document.addEventListener(\"keydown\", this.#onKeydown);\n document.addEventListener(\"turbo:before-cache\", this.#onBeforeCache);\n const onEscape = this.#options.onEscape;\n if (onEscape) this.#escapeLayer.activate(document, { onDismiss: () => onEscape() });\n if (this.#flag(this.#options.autoFocus, true)) this.#focusInitial();\n }\n\n /**\n * Reverts every side effect applied by {@link activate}. No-ops if inactive, so\n * a controller can call it defensively from both `close()` and `disconnect()`.\n *\n * @param restoreFocus - Move focus back to the opener (default `true`). Pass\n * `false` on teardown (`disconnect`), where yanking focus is undesirable.\n */\n deactivate({ restoreFocus = true }: { restoreFocus?: boolean } = {}): void {\n if (!this.#activeState) return;\n this.#activeState = false;\n this.#escapeLayer.deactivate();\n document.removeEventListener(\"keydown\", this.#onKeydown);\n document.removeEventListener(\"turbo:before-cache\", this.#onBeforeCache);\n if (this.#scrollLocked) {\n document.body.style.overflow = this.#previousBodyOverflow;\n this.#scrollLocked = false;\n }\n this.#releaseBackground();\n if (restoreFocus) {\n const target = this.#previouslyFocused ?? this.#options.fallbackFocus?.() ?? null;\n target?.focus();\n }\n }\n\n /** Resolves a boolean-or-getter option, defaulting when it was not provided. */\n #flag(option: boolean | (() => boolean) | undefined, fallback: boolean): boolean {\n if (option === undefined) return fallback;\n return typeof option === \"function\" ? option() : option;\n }\n\n /**\n * Reverts the side effects just before Turbo caches the page snapshot, so an\n * overlay left open does not bake the scroll lock into `body[style]` — a\n * restored page would feed that locked value back into {@link activate} as the\n * baseline, and closing would then never unlock the page. Markup state stays\n * untouched (restore-open designs reopen against a clean baseline), and focus\n * is left alone mid-navigation. The listener lives only while active.\n */\n readonly #onBeforeCache = (): void => {\n this.deactivate({ restoreFocus: false });\n };\n\n /**\n * Handles `Tab` (focus trap) while active. `Escape` dismissal is owned by the\n * shared {@link EscapeLayer} resolver, so Tab trapping stays independent of\n * which layer currently owns Escape.\n */\n readonly #onKeydown = (event: KeyboardEvent): void => {\n if (event.key === \"Tab\") this.#trapTab(event);\n };\n\n /** Keeps `Tab` focus cycling within the container's focusable elements. */\n #trapTab(event: KeyboardEvent): void {\n const focusable = this.#focusableElements();\n if (focusable.length === 0) {\n event.preventDefault();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = document.activeElement;\n\n // If focus has somehow escaped the container, pull it back to the first item.\n if (!(active instanceof Node) || !this.#getContainer().contains(active)) {\n event.preventDefault();\n first?.focus();\n return;\n }\n\n if (event.shiftKey && active === first) {\n event.preventDefault();\n last?.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first?.focus();\n }\n }\n\n /**\n * Marks every element outside the container's subtree as `inert` so background\n * content cannot be focused or reached by assistive technology, honoring the\n * `aria-modal=\"true\"` contract. An element that was *already* `inert` is left\n * untracked so `#releaseBackground` does not wrongly clear it.\n */\n #isolateBackground(): void {\n const container = this.#getContainer();\n this.#inertedSiblings = [];\n for (const sibling of Array.from(document.body.children)) {\n if (!(sibling instanceof HTMLElement)) continue;\n if (sibling.contains(container) || sibling.inert) continue;\n sibling.inert = true;\n this.#inertedSiblings.push(sibling);\n }\n }\n\n /** Reverts the `inert` flags applied by `#isolateBackground`. */\n #releaseBackground(): void {\n for (const sibling of this.#inertedSiblings) {\n sibling.inert = false;\n }\n this.#inertedSiblings = [];\n }\n\n /** Moves focus to the initial target, the first focusable, or the container. */\n #focusInitial(): void {\n const preferred = this.#options.initialFocus?.();\n if (preferred) {\n preferred.focus();\n return;\n }\n const focusable = this.#focusableElements();\n if (focusable[0]) {\n focusable[0].focus();\n return;\n }\n const container = this.#getContainer();\n container.tabIndex = -1;\n container.focus();\n }\n\n /** Collects the container's currently focusable descendants in DOM order. */\n #focusableElements(): HTMLElement[] {\n return Array.from(this.#getContainer().querySelectorAll<HTMLElement>(FOCUSABLE)).filter(\n (el) => !el.hidden,\n );\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { CompositionTracker } from \"../utils/composition_tracker\";\nimport { FocusTrap } from \"../utils/focus_trap\";\n\n/**\n * Headless, highly accessible Command Palette behavior.\n *\n * Markup contract (identifier: `stimeo--command-palette`):\n * <div data-controller=\"stimeo--command-palette\"\n * data-stimeo--command-palette-hotkey-value=\"mod+k\">\n * <div data-stimeo--command-palette-target=\"dialog\" role=\"dialog\"\n * aria-modal=\"true\" aria-label=\"Command palette\"\n * data-action=\"click->stimeo--command-palette#closeOnBackdrop\" hidden>\n * <input data-stimeo--command-palette-target=\"input\" role=\"combobox\"\n * aria-expanded=\"false\" aria-controls=\"cmdk-list\"\n * aria-autocomplete=\"list\" aria-label=\"Search commands\"\n * data-action=\"input->stimeo--command-palette#filter\n * keydown->stimeo--command-palette#onKeydown\" />\n * <ul id=\"cmdk-list\" data-stimeo--command-palette-target=\"list\" role=\"listbox\">\n * <li id=\"cmd-new\" role=\"option\" data-stimeo--command-palette-target=\"option\"\n * data-action=\"click->stimeo--command-palette#selectByClick\">New…</li>\n * <li id=\"cmd-heading\" role=\"option\" data-stimeo--command-palette-target=\"option\"\n * data-disabled=\"true\">Disabled command (shown but not selectable)</li>\n * </ul>\n * <p data-stimeo--command-palette-target=\"empty\" hidden>No commands</p>\n * </div>\n * </div>\n *\n * Implements a composite **Dialog (Modal)** + **Combobox** (Listbox options) pattern.\n * A customizable global hotkey (Command/Ctrl + K) toggles it, focus is trapped\n * within the dialog, virtual focus roves via `aria-activedescendant`, and filtering\n * is performed in memory.\n *\n * @remarks\n * Behavior only. The combobox concerns (filtering, virtual focus, selection) live\n * here; the modal lifecycle — focus trap, scroll lock, background `inert`, focus\n * restore, and teardown reversal — is delegated to the shared {@link FocusTrap}\n * primitive (also used by dialog / alert-dialog / drawer). `Escape` closes and\n * `Tab`/`Shift+Tab` cycle focus regardless of which element inside the dialog holds\n * focus (input, close button, …), because the trap listens at the document level.\n * IME composition is tracked from start to end: intermediate input does not filter,\n * and conversion-confirming keys never select a command. Styling, transitions, and\n * the actual command handlers remain the consumer's.\n */\nexport class CommandPaletteController extends Controller<HTMLElement> {\n static readonly #ORIGINAL_ARIA_DISABLED = \"data-command-palette-original-aria-disabled\";\n static readonly #ABSENT_ARIA_DISABLED = \"absent\";\n\n static override targets = [\"dialog\", \"input\", \"list\", \"option\", \"empty\"];\n static override values = {\n hotkey: { type: String, default: \"mod+k\" },\n open: { type: Boolean, default: false },\n };\n static actions = [\n \"close\",\n \"closeOnBackdrop\",\n \"filter\",\n \"onKeydown\",\n \"open\",\n \"selectByClick\",\n \"toggle\",\n ] as const;\n static events = [\"select\"] as const;\n\n declare readonly dialogTarget: HTMLElement;\n declare readonly inputTarget: HTMLInputElement;\n declare readonly listTarget: HTMLElement;\n declare readonly optionTargets: HTMLElement[];\n declare readonly emptyTarget: HTMLElement;\n\n declare readonly hasDialogTarget: boolean;\n declare readonly hasInputTarget: boolean;\n declare readonly hasListTarget: boolean;\n declare readonly hasEmptyTarget: boolean;\n\n declare hotkeyValue: string;\n declare openValue: boolean;\n\n /** The index of the currently active option within the visible subset. */\n #activeIndex = -1;\n\n /** Owns IME lifecycle state; confirmed text re-filters an open palette once. */\n readonly #composition = new CompositionTracker({\n onEnd: () => {\n if (this.#isOpen) this.filter();\n },\n });\n\n /**\n * Owns the modal side effects (focus trap, scroll lock, background `inert`, focus\n * restore). Escape closes; focus on open goes to the input, and is restored to\n * whatever opened the palette on close.\n */\n readonly #trap = new FocusTrap(() => this.dialogTarget, {\n onEscape: () => this.close(),\n initialFocus: () => (this.hasInputTarget ? this.inputTarget : null),\n });\n\n /**\n * Initializes the global hotkey handler and establishes the initial open state.\n *\n * The DOM is the source of truth on reconnect (Turbo cache restore / morph): if\n * the restored snapshot already shows the dialog open, honor that rather than\n * re-deriving from the declarative `open` Value (which would slam a user-opened\n * palette shut). The `open` Value only seeds the initial state of a genuinely\n * fresh render. We normalize to a clean closed baseline first so {@link open}\n * runs its full setup — the {@link FocusTrap} is a fresh instance after a\n * reconnect and must be re-activated.\n */\n override connect(): void {\n document.addEventListener(\"keydown\", this.#onGlobalKeydown);\n if (this.hasInputTarget) this.#composition.observe(this.inputTarget);\n this.#syncOptionSemantics();\n const shouldOpen = this.#isOpen || this.openValue;\n this.#resetToClosedState();\n if (shouldOpen) this.open();\n }\n\n /** Tears down the global hotkey listener and reverts the modal side effects. */\n override disconnect(): void {\n document.removeEventListener(\"keydown\", this.#onGlobalKeydown);\n this.#composition.disconnect();\n this.#trap.deactivate({ restoreFocus: false });\n this.#resetToClosedState();\n }\n\n /** Initializes semantics for an option added before or after the controller connects. */\n optionTargetConnected(option: HTMLElement): void {\n this.#syncOptionSemanticsFor(option);\n }\n\n /** Tracks an input added initially or after connect without extra consumer actions. */\n inputTargetConnected(input: HTMLInputElement): void {\n this.#composition.observe(input);\n }\n\n /** Removes controller-owned listeners when the input target is replaced or removed. */\n inputTargetDisconnected(input: HTMLInputElement): void {\n this.#composition.unobserve(input);\n }\n\n /** Toggles the open state of the command palette. */\n toggle(): void {\n if (this.#isOpen) {\n this.close();\n } else {\n this.open();\n }\n }\n\n /** Opens the palette, traps focus, and shifts focus to the input. */\n open(): void {\n if (!this.hasDialogTarget || this.#isOpen) return;\n this.dialogTarget.hidden = false;\n this.openValue = true;\n if (this.hasInputTarget) this.inputTarget.setAttribute(\"aria-expanded\", \"true\");\n this.#resetFilter();\n this.#trap.activate();\n }\n\n /** Closes the palette and restores focus back to the opener. */\n close(): void {\n if (!this.hasDialogTarget || !this.#isOpen) return;\n this.#resetToClosedState();\n this.#trap.deactivate();\n }\n\n /** Filters option elements in-memory matching the input value. Bound to input target. */\n filter(event?: InputEvent): void {\n // Match the library's Character Counter policy: do not react to intermediate\n // pre-conversion text. `compositionend` applies the confirmed query once.\n if (!this.hasInputTarget || this.#composition.isComposing(event)) return;\n this.#syncOptionSemantics();\n const query = this.inputTarget.value.trim().toLowerCase();\n // Disabled options (e.g. group headings) may still be shown, but they do not\n // count toward the empty state and are never navigable/selectable.\n let hasSelectableMatch = false;\n\n for (const option of this.optionTargets) {\n const searchText = (option.dataset.searchValue || option.textContent || \"\")\n .trim()\n .toLowerCase();\n const matches = searchText.includes(query);\n\n if (matches) {\n option.removeAttribute(\"hidden\");\n if (!this.#isDisabled(option)) hasSelectableMatch = true;\n } else {\n option.setAttribute(\"hidden\", \"true\");\n }\n }\n\n if (this.hasEmptyTarget) {\n this.emptyTarget.hidden = hasSelectableMatch;\n }\n\n this.#setActiveIndex(hasSelectableMatch ? 0 : -1);\n }\n\n /** Selects the clicked option. Bound to option targets. */\n selectByClick(event: MouseEvent): void {\n const target = event.currentTarget as HTMLElement | null;\n if (!target) return;\n const option = target.closest(\"[role='option']\") as HTMLElement | null;\n if (option) this.#syncOptionSemanticsFor(option);\n // Ignore clicks on disabled options (e.g. group headings) so they never fire select.\n if (option && !this.#isDisabled(option)) {\n this.#confirmSelection(option);\n }\n }\n\n /** Closes when the backdrop (the dialog target itself) is clicked, ignoring inner clicks. */\n closeOnBackdrop(event: MouseEvent): void {\n if (event.target === this.dialogTarget) this.close();\n }\n\n /**\n * Combobox navigation keys (arrows / Home / End / Enter), bound to the input.\n * `Tab` (focus trap) and `Escape` (close) are owned by the {@link FocusTrap} at\n * the document level, so they work no matter which element inside the dialog has\n * focus — not only the input.\n */\n onKeydown(event: KeyboardEvent): void {\n // Composition-confirming Enter must stay with the IME instead of selecting a\n // command. Controller-owned lifecycle state covers events that omit isComposing.\n if (this.#composition.isComposing(event) || !this.#isOpen) return;\n\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n this.#navigate(1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n this.#navigate(-1);\n break;\n case \"Home\":\n event.preventDefault();\n this.#setActiveIndex(0);\n break;\n case \"End\":\n event.preventDefault();\n this.#setActiveIndex(this.#visibleOptions.length - 1);\n break;\n case \"Enter\": {\n event.preventDefault();\n const activeOption = this.#visibleOptions[this.#activeIndex];\n if (activeOption) this.#confirmSelection(activeOption);\n break;\n }\n }\n }\n\n #navigate(direction: number): void {\n const visible = this.#visibleOptions;\n if (visible.length === 0) return;\n\n let newIndex = this.#activeIndex + direction;\n if (newIndex >= visible.length) newIndex = 0;\n if (newIndex < 0) newIndex = visible.length - 1;\n\n this.#setActiveIndex(newIndex);\n }\n\n #setActiveIndex(index: number): void {\n this.#syncOptionSemantics();\n const visible = this.#visibleOptions;\n const activeOption = visible[index] ?? null;\n this.#activeIndex = activeOption ? index : -1;\n\n for (const option of this.optionTargets) {\n option.setAttribute(\"aria-selected\", \"false\");\n option.removeAttribute(\"data-active\");\n }\n\n if (activeOption) {\n activeOption.setAttribute(\"aria-selected\", \"true\");\n activeOption.setAttribute(\"data-active\", \"true\");\n activeOption.scrollIntoView({ block: \"nearest\" });\n }\n\n if (this.hasInputTarget) {\n if (activeOption?.id) {\n this.inputTarget.setAttribute(\"aria-activedescendant\", activeOption.id);\n } else {\n this.inputTarget.removeAttribute(\"aria-activedescendant\");\n }\n }\n }\n\n #confirmSelection(option: HTMLElement): void {\n const value = option.dataset.value || option.textContent || \"\";\n this.dispatch(\"select\", { detail: { value, option } });\n this.close();\n }\n\n #resetFilter(): void {\n if (this.hasInputTarget) this.inputTarget.value = \"\";\n for (const option of this.optionTargets) {\n option.removeAttribute(\"hidden\");\n }\n const hasSelectableOption = this.#visibleOptions.length > 0;\n if (this.hasEmptyTarget) this.emptyTarget.hidden = hasSelectableOption;\n this.#setActiveIndex(hasSelectableOption ? 0 : -1);\n }\n\n get #visibleOptions(): HTMLElement[] {\n // Only options that are both shown and not disabled are navigable/selectable.\n return this.optionTargets.filter(\n (option) => !option.hasAttribute(\"hidden\") && !this.#isDisabled(option),\n );\n }\n\n #isDisabled(option: HTMLElement): boolean {\n return option.dataset.disabled === \"true\" || option.getAttribute(\"aria-disabled\") === \"true\";\n }\n\n /** Synchronizes every option's controller-owned selection and disabled semantics. */\n #syncOptionSemantics(): void {\n for (const option of this.optionTargets) this.#syncOptionSemanticsFor(option);\n }\n\n /** Reflects `data-disabled` to ARIA without losing a pre-existing authored value. */\n #syncOptionSemanticsFor(option: HTMLElement): void {\n if (!option.hasAttribute(\"aria-selected\")) option.setAttribute(\"aria-selected\", \"false\");\n\n const originalAttribute = CommandPaletteController.#ORIGINAL_ARIA_DISABLED;\n const originalValue = option.getAttribute(originalAttribute);\n if (option.dataset.disabled === \"true\") {\n if (originalValue === null) {\n option.setAttribute(\n originalAttribute,\n option.getAttribute(\"aria-disabled\") ?? CommandPaletteController.#ABSENT_ARIA_DISABLED,\n );\n }\n option.setAttribute(\"aria-disabled\", \"true\");\n return;\n }\n\n if (originalValue === null) return;\n if (originalValue === CommandPaletteController.#ABSENT_ARIA_DISABLED) {\n option.removeAttribute(\"aria-disabled\");\n } else {\n option.setAttribute(\"aria-disabled\", originalValue);\n }\n option.removeAttribute(originalAttribute);\n }\n\n get #isOpen(): boolean {\n return this.hasDialogTarget && !this.dialogTarget.hidden;\n }\n\n readonly #onGlobalKeydown = (event: KeyboardEvent): void => {\n if (this.#composition.isComposing(event) || !this.#matchesHotkey(event)) return;\n\n event.preventDefault();\n this.toggle();\n };\n\n /** Matches the configured `mod+key` or bare-key hotkey without extra modifiers. */\n #matchesHotkey(event: KeyboardEvent): boolean {\n const hotkey = this.#parseHotkey();\n if (!hotkey || event.altKey || event.shiftKey) return false;\n\n if (hotkey.requiresMod) {\n // \"mod\" accepts exactly one of Cmd or Ctrl on every platform.\n const hasExactlyOneModKey = event.metaKey !== event.ctrlKey;\n if (!hasExactlyOneModKey) return false;\n } else if (event.metaKey || event.ctrlKey) {\n return false;\n }\n\n return event.key.toLowerCase() === hotkey.key;\n }\n\n /** Parses the intentionally small public hotkey grammar. */\n #parseHotkey(): { key: string; requiresMod: boolean } | null {\n const parts = this.hotkeyValue.toLowerCase().split(\"+\");\n if (parts.length === 1 && parts[0] && parts[0] !== \"mod\") {\n return { key: parts[0], requiresMod: false };\n }\n if (parts.length === 2 && parts[0] === \"mod\" && parts[1]) {\n return { key: parts[1], requiresMod: true };\n }\n return null;\n }\n\n /** Resets transient open state so reconnect starts from a predictable closed snapshot. */\n #resetToClosedState(): void {\n this.#activeIndex = -1;\n this.openValue = false;\n\n if (this.hasDialogTarget) {\n this.dialogTarget.hidden = true;\n }\n\n if (this.hasInputTarget) {\n this.inputTarget.setAttribute(\"aria-expanded\", \"false\");\n this.inputTarget.removeAttribute(\"aria-activedescendant\");\n }\n }\n}\n"]}