@ixfx/components 0.2.6 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/bundle/index.d.ts +213 -117
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +1438 -686
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-text.js +3 -3
  6. package/dist/ac-text.js.map +1 -1
  7. package/dist/{button-B9ztaKyl.js → button-DMt5XWUK.js} +4 -4
  8. package/dist/{button-B9ztaKyl.js.map → button-DMt5XWUK.js.map} +1 -1
  9. package/dist/button.js +1 -1
  10. package/dist/checkbox.d.ts +2 -3
  11. package/dist/checkbox.d.ts.map +1 -1
  12. package/dist/checkbox.js +1 -1
  13. package/dist/checkbox.js.map +1 -1
  14. package/dist/{colour-C3MQIjFJ-BJV-QC-3.js → colour-C3MQIjFJ-BLXg8W3U.js} +2 -2
  15. package/dist/{colour-C3MQIjFJ-BJV-QC-3.js.map → colour-C3MQIjFJ-BLXg8W3U.js.map} +1 -1
  16. package/dist/{colour-picker-BNqDz4wK.js → colour-picker-K6zu82lm.js} +5 -5
  17. package/dist/{colour-picker-BNqDz4wK.js.map → colour-picker-K6zu82lm.js.map} +1 -1
  18. package/dist/colour-picker.js +1 -1
  19. package/dist/crumbs.d.ts +2 -3
  20. package/dist/crumbs.d.ts.map +1 -1
  21. package/dist/crumbs.js +10 -10
  22. package/dist/crumbs.js.map +1 -1
  23. package/dist/data-display.js +3 -3
  24. package/dist/{decorate-O4m1U4l3.js → decorate-D7rC1gLP.js} +10 -8
  25. package/dist/decorate-D7rC1gLP.js.map +1 -0
  26. package/dist/{defaults-FEZaGiYZ.js → defaults-C7elhuyJ.js} +2 -2
  27. package/dist/{defaults-FEZaGiYZ.js.map → defaults-C7elhuyJ.js.map} +1 -1
  28. package/dist/{dist-By5URG0Y.js → dist-BX0OVIm7.js} +2 -2
  29. package/dist/{dist-By5URG0Y.js.map → dist-BX0OVIm7.js.map} +1 -1
  30. package/dist/{dist-D7kJgnSE.js → dist-TAGYwaju.js} +1 -1
  31. package/dist/{dist-D7kJgnSE.js.map → dist-TAGYwaju.js.map} +1 -1
  32. package/dist/editable-label.d.ts +6 -1
  33. package/dist/editable-label.d.ts.map +1 -1
  34. package/dist/editable-label.js +49 -2
  35. package/dist/editable-label.js.map +1 -1
  36. package/dist/{hex-editor-gH3mteMr.js → hex-editor-CsN_ySkH.js} +85 -85
  37. package/dist/hex-editor-CsN_ySkH.js.map +1 -0
  38. package/dist/{hex-editor-2_s0Ua5t.d.ts → hex-editor-DlfoA65y.d.ts} +5 -5
  39. package/dist/{hex-editor-2_s0Ua5t.d.ts.map → hex-editor-DlfoA65y.d.ts.map} +1 -1
  40. package/dist/hex.d.ts +1 -1
  41. package/dist/hex.js +1 -1
  42. package/dist/{highlight-CAjwQ4GE.js → highlight-TPbSHruI.js} +3 -3
  43. package/dist/highlight-TPbSHruI.js.map +1 -0
  44. package/dist/{icon-CYACf6o8.js → icon-9NWTXtE1.js} +4 -5
  45. package/dist/icon-9NWTXtE1.js.map +1 -0
  46. package/dist/icon-CeTDJhC7.d.ts.map +1 -1
  47. package/dist/icons.js +3 -3
  48. package/dist/{incr-search-BZawvrfl.js → incr-search-DSKKsk6w.js} +12 -10
  49. package/dist/incr-search-DSKKsk6w.js.map +1 -0
  50. package/dist/incr-search.d.ts +1 -1
  51. package/dist/incr-search.js +2 -2
  52. package/dist/{index-CwGkb9s1.d.ts → index--hrxPBkQ.d.ts} +69 -69
  53. package/dist/index--hrxPBkQ.d.ts.map +1 -0
  54. package/dist/{index-kY5gxbkI.d.ts → index-DSlPeWLK.d.ts} +15 -15
  55. package/dist/{index-kY5gxbkI.d.ts.map → index-DSlPeWLK.d.ts.map} +1 -1
  56. package/dist/{index-V2cJorlA.d.ts → index-hXFn14tM.d.ts} +4 -4
  57. package/dist/index-hXFn14tM.d.ts.map +1 -0
  58. package/dist/index.d.ts +109 -13
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +748 -52
  61. package/dist/index.js.map +1 -1
  62. package/dist/keyboard-De_vOHLg.js.map +1 -1
  63. package/dist/{labelled-input-base-BvgH7ldm.js → labelled-input-base-CqPL5rKm.js} +2 -2
  64. package/dist/labelled-input-base-CqPL5rKm.js.map +1 -0
  65. package/dist/labelled-radial-input.js +3 -3
  66. package/dist/labelled-radial-input.js.map +1 -1
  67. package/dist/labelled-range-input.js +2 -2
  68. package/dist/led.js +1 -1
  69. package/dist/{menu-C3eANB5M.js → menu-DJJuCm-5.js} +48 -48
  70. package/dist/menu-DJJuCm-5.js.map +1 -0
  71. package/dist/{menu-item-6IU5weHi.js → menu-item-CKRfP0pN.js} +10 -10
  72. package/dist/menu-item-CKRfP0pN.js.map +1 -0
  73. package/dist/menu.d.ts +1 -1
  74. package/dist/menu.js +2 -2
  75. package/dist/miller.d.ts +5 -7
  76. package/dist/miller.d.ts.map +1 -1
  77. package/dist/miller.js +14 -14
  78. package/dist/miller.js.map +1 -1
  79. package/dist/narrowed-text.d.ts.map +1 -1
  80. package/dist/narrowed-text.js +4 -4
  81. package/dist/narrowed-text.js.map +1 -1
  82. package/dist/panel.js +2 -2
  83. package/dist/panel.js.map +1 -1
  84. package/dist/plots.js +1 -1
  85. package/dist/polar-pad.d.ts.map +1 -1
  86. package/dist/polar-pad.js +1 -1
  87. package/dist/polar-pad.js.map +1 -1
  88. package/dist/{popup-Ddx9LCzY.js → popup-ImtS9KnS.js} +1 -1
  89. package/dist/{popup-Ddx9LCzY.js.map → popup-ImtS9KnS.js.map} +1 -1
  90. package/dist/{prominence-CreDBdCs.js → prominence--ic9uWBl.js} +1 -1
  91. package/dist/{prominence-CreDBdCs.js.map → prominence--ic9uWBl.js.map} +1 -1
  92. package/dist/{radial-input-Dbc6fgCK.js → radial-input-DZA6WaFu.js} +14 -14
  93. package/dist/radial-input-DZA6WaFu.js.map +1 -0
  94. package/dist/radial-input.js +1 -1
  95. package/dist/range-input.js +1 -1
  96. package/dist/range.js +2 -2
  97. package/dist/{registry-CZEB1YI9.js → registry-Q2gYQCHM.js} +1 -1
  98. package/dist/{registry-CZEB1YI9.js.map → registry-Q2gYQCHM.js.map} +1 -1
  99. package/dist/selecthorizontal.js +1 -1
  100. package/dist/snap-container.js +1 -1
  101. package/dist/split-layout.js +4 -4
  102. package/dist/split-layout.js.map +1 -1
  103. package/dist/swipe.d.ts +1 -2
  104. package/dist/swipe.d.ts.map +1 -1
  105. package/dist/swipe.js +2 -2
  106. package/dist/swipe.js.map +1 -1
  107. package/dist/tab-list-BnqISNvO.d.ts.map +1 -1
  108. package/dist/tabs.js +2 -2
  109. package/dist/tabs.js.map +1 -1
  110. package/dist/{timeline-B62Sz6J_.js → timeline-Cv75Th2U.js} +120 -116
  111. package/dist/timeline-Cv75Th2U.js.map +1 -0
  112. package/dist/timeline.d.ts +1 -1
  113. package/dist/timeline.js +1 -1
  114. package/dist/tooltip-CwGfb4lp.d.ts.map +1 -1
  115. package/dist/tree-CvvKbO-f.js +1330 -0
  116. package/dist/tree-CvvKbO-f.js.map +1 -0
  117. package/dist/{tree-component-CQpoPo4s.d.ts → tree-component-D8Bg48tt.d.ts} +12 -12
  118. package/dist/{tree-component-CQpoPo4s.d.ts.map → tree-component-D8Bg48tt.d.ts.map} +1 -1
  119. package/dist/tree.d.ts +1 -1
  120. package/dist/tree.js +1 -1143
  121. package/dist/xy-axis-D9uwpGTT.d.ts.map +1 -1
  122. package/dist/{xy-axis-C7fNxDwY.js → xy-axis-DL0tfJDr.js} +10 -10
  123. package/dist/{xy-axis-C7fNxDwY.js.map → xy-axis-DL0tfJDr.js.map} +1 -1
  124. package/dist/xy-pad.js +2 -2
  125. package/package.json +3 -3
  126. package/dist/decorate-O4m1U4l3.js.map +0 -1
  127. package/dist/hex-editor-gH3mteMr.js.map +0 -1
  128. package/dist/highlight-CAjwQ4GE.js.map +0 -1
  129. package/dist/icon-CYACf6o8.js.map +0 -1
  130. package/dist/incr-search-BZawvrfl.js.map +0 -1
  131. package/dist/index-CwGkb9s1.d.ts.map +0 -1
  132. package/dist/index-V2cJorlA.d.ts.map +0 -1
  133. package/dist/labelled-input-base-BvgH7ldm.js.map +0 -1
  134. package/dist/menu-C3eANB5M.js.map +0 -1
  135. package/dist/menu-item-6IU5weHi.js.map +0 -1
  136. package/dist/radial-input-Dbc6fgCK.js.map +0 -1
  137. package/dist/timeline-B62Sz6J_.js.map +0 -1
  138. package/dist/tree-data-model-DGvRVOFY.js +0 -190
  139. package/dist/tree-data-model-DGvRVOFY.js.map +0 -1
  140. package/dist/tree.js.map +0 -1
  141. /package/dist/{interaction-CQDQJf2Q.js → interaction-DG7X1rhI.js} +0 -0
package/bundle/index.js CHANGED
@@ -737,12 +737,12 @@ function buildHighlightTargets(root, itemSelector, positionsByItem, getItem, get
737
737
  }
738
738
  return targets;
739
739
  }
740
- const findTextNode = (element, expectedText) => {
740
+ function findTextNode(element, expectedText) {
741
741
  for (const node of element.childNodes) if (node.nodeType === Node.TEXT_NODE) {
742
742
  if (expectedText === void 0 || node.textContent === expectedText) return node;
743
743
  }
744
744
  return null;
745
- };
745
+ }
746
746
  //#endregion
747
747
  //#region src/interaction/tickled-controller.ts
748
748
  /**
@@ -1067,6 +1067,22 @@ const tickledItemStyles = i$7`
1067
1067
  }
1068
1068
  `;
1069
1069
  //#endregion
1070
+ //#region src/util/custom-elements.ts
1071
+ function getRegistry() {
1072
+ if (typeof customElements === `undefined`) return void 0;
1073
+ return customElements;
1074
+ }
1075
+ function safeDefine(name, ctor) {
1076
+ const registry = getRegistry();
1077
+ if (!registry) return;
1078
+ if (registry.get(name) === void 0) registry.define(name, ctor);
1079
+ }
1080
+ function safeCustomElement(name) {
1081
+ return (ctor) => {
1082
+ safeDefine(name, ctor);
1083
+ };
1084
+ }
1085
+ //#endregion
1070
1086
  //#region node_modules/.pnpm/lit-html@3.3.3/node_modules/lit-html/directive.js
1071
1087
  /**
1072
1088
  * @license
@@ -1100,37 +1116,6 @@ var i$3 = class {
1100
1116
  }
1101
1117
  };
1102
1118
  //#endregion
1103
- //#region node_modules/.pnpm/lit-html@3.3.3/node_modules/lit-html/directives/style-map.js
1104
- /**
1105
- * @license
1106
- * Copyright 2018 Google LLC
1107
- * SPDX-License-Identifier: BSD-3-Clause
1108
- */ const n$1 = "important", i$2 = " !" + n$1, o$1 = e$3(class extends i$3 {
1109
- constructor(t) {
1110
- if (super(t), t.type !== t$2.ATTRIBUTE || "style" !== t.name || t.strings?.length > 2) throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.");
1111
- }
1112
- render(t) {
1113
- return Object.keys(t).reduce((e, r) => {
1114
- const s = t[r];
1115
- return null == s ? e : e + `${r = r.includes("-") ? r : r.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g, "-$&").toLowerCase()}:${s};`;
1116
- }, "");
1117
- }
1118
- update(e, [r]) {
1119
- const { style: s } = e.element;
1120
- if (void 0 === this.ft) return this.ft = new Set(Object.keys(r)), this.render(r);
1121
- for (const t of this.ft) r[t] ?? (this.ft.delete(t), t.includes("-") ? s.removeProperty(t) : s[t] = null);
1122
- for (const t in r) {
1123
- const e = r[t];
1124
- if (null != e) {
1125
- this.ft.add(t);
1126
- const r = "string" == typeof e && e.endsWith(i$2);
1127
- t.includes("-") || r ? s.setProperty(t, r ? e.slice(0, -11) : e, r ? n$1 : "") : s[t] = e;
1128
- }
1129
- }
1130
- return E;
1131
- }
1132
- });
1133
- //#endregion
1134
1119
  //#region node_modules/.pnpm/lit-html@3.3.3/node_modules/lit-html/directives/unsafe-html.js
1135
1120
  /**
1136
1121
  * @license
@@ -1155,7 +1140,7 @@ var i$3 = class {
1155
1140
  }
1156
1141
  };
1157
1142
  e$2.directiveName = "unsafeHTML", e$2.resultType = 1;
1158
- const o = e$3(e$2);
1143
+ const o$1 = e$3(e$2);
1159
1144
  //#endregion
1160
1145
  //#region src/icons/registry.ts
1161
1146
  const _registry = /* @__PURE__ */ new Map();
@@ -1175,20 +1160,6 @@ function svgToDataUri(svg) {
1175
1160
  return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
1176
1161
  }
1177
1162
  //#endregion
1178
- //#region src/util/custom-elements.ts
1179
- const getRegistry = () => {
1180
- if (typeof customElements === `undefined`) return void 0;
1181
- return customElements;
1182
- };
1183
- const safeDefine = (name, ctor) => {
1184
- const registry = getRegistry();
1185
- if (!registry) return;
1186
- if (registry.get(name) === void 0) registry.define(name, ctor);
1187
- };
1188
- const safeCustomElement = (name) => (ctor) => {
1189
- safeDefine(name, ctor);
1190
- };
1191
- //#endregion
1192
1163
  //#region \0@oxc-project+runtime@0.130.0/helpers/decorate.js
1193
1164
  function __decorate(decorators, target, key, desc) {
1194
1165
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -1227,7 +1198,7 @@ let IxfxIconElement = class IxfxIconElement extends i$4 {
1227
1198
  class="icon-wrapper"
1228
1199
  aria-hidden=${hasLabel ? A : `true`}
1229
1200
  aria-label=${hasLabel ? this.label : A}
1230
- >${o(svg)}</span>
1201
+ >${o$1(svg)}</span>
1231
1202
  `;
1232
1203
  }
1233
1204
  updated(changed) {
@@ -1283,14 +1254,14 @@ __decorate([n$2({
1283
1254
  })], IxfxIconElement.prototype, "prominence", void 0);
1284
1255
  __decorate([n$2({
1285
1256
  type: String,
1286
- attribute: "rotate",
1257
+ attribute: `rotate`,
1287
1258
  reflect: true
1288
1259
  })], IxfxIconElement.prototype, "rotate", void 0);
1289
1260
  IxfxIconElement = __decorate([safeCustomElement(`ixfx-icon`)], IxfxIconElement);
1290
1261
  //#endregion
1291
1262
  //#region src/ac-text/ac-text.ts
1292
1263
  const HIGHLIGHT_KEY$1 = `ac-text-highlight`;
1293
- function makeLruCache(maxSize) {
1264
+ function makeLruCache$1(maxSize) {
1294
1265
  const map = /* @__PURE__ */ new Map();
1295
1266
  return {
1296
1267
  get: (key) => map.get(key),
@@ -1322,7 +1293,7 @@ let AcTextElement = class AcTextElement extends i$4 {
1322
1293
  id: i,
1323
1294
  enabled: true
1324
1295
  })) });
1325
- this.#cache = makeLruCache(20);
1296
+ this.#cache = makeLruCache$1(20);
1326
1297
  this.#onFocus = () => {
1327
1298
  this._isOpen = true;
1328
1299
  if (this._query.trim()) this.#triggerQuery(this._query);
@@ -1397,7 +1368,7 @@ let AcTextElement = class AcTextElement extends i$4 {
1397
1368
  e.preventDefault();
1398
1369
  this.#selectItem(item);
1399
1370
  };
1400
- this.#cache = makeLruCache(this.cacheSize);
1371
+ this.#cache = makeLruCache$1(this.cacheSize);
1401
1372
  }
1402
1373
  disconnectedCallback() {
1403
1374
  super.disconnectedCallback();
@@ -1406,7 +1377,7 @@ let AcTextElement = class AcTextElement extends i$4 {
1406
1377
  }
1407
1378
  updated(changedProps) {
1408
1379
  if (changedProps.has(`completionProvider`)) this.#cache.clear();
1409
- if (changedProps.has(`cacheSize`)) this.#cache = makeLruCache(this.cacheSize);
1380
+ if (changedProps.has(`cacheSize`)) this.#cache = makeLruCache$1(this.cacheSize);
1410
1381
  if (changedProps.has(`value`)) {
1411
1382
  const input = this.shadowRoot?.querySelector(`input`);
1412
1383
  if (!input || document.activeElement !== input) this._query = this.value;
@@ -1763,6 +1734,934 @@ __decorate([r$1()], AcTextElement.prototype, "_isOpen", void 0);
1763
1734
  __decorate([r$1()], AcTextElement.prototype, "_isLoading", void 0);
1764
1735
  AcTextElement = __decorate([safeCustomElement(`ixfx-ac-text`)], AcTextElement);
1765
1736
  //#endregion
1737
+ //#region src/incr-search/fuzzy.ts
1738
+ function defaultSelector(item) {
1739
+ if (typeof item === `string`) return item;
1740
+ const r = item;
1741
+ if (typeof r.label === `string`) return r.label;
1742
+ if (typeof r.key === `string`) return r.key;
1743
+ if (typeof r.value === `string`) return r.value;
1744
+ return String(item);
1745
+ }
1746
+ function createFuzzySearch(items, options = {}) {
1747
+ const fzf = new Fzf(items, {
1748
+ selector: options.selector ?? defaultSelector,
1749
+ ...options.fzf ?? {}
1750
+ });
1751
+ const find = (query) => {
1752
+ if (!query.trim()) return items.map((item) => ({
1753
+ item,
1754
+ positions: /* @__PURE__ */ new Set(),
1755
+ score: 0
1756
+ }));
1757
+ return fzf.find(query).map((r) => ({
1758
+ item: r.item,
1759
+ positions: r.positions,
1760
+ score: r.score
1761
+ }));
1762
+ };
1763
+ return {
1764
+ find,
1765
+ items
1766
+ };
1767
+ }
1768
+ //#endregion
1769
+ //#region src/incr-search/ac-adapter.ts
1770
+ /**
1771
+ * Creates an ixfx-ac-text-compatible `completionProvider` function from a
1772
+ * static item list. Uses synchronous fuzzy search wrapped in a Promise so it
1773
+ * fits the async provider signature.
1774
+ *
1775
+ * Each returned item has `positions` populated with matched character indices,
1776
+ * which `ixfx-ac-text` uses to render CSS Custom Highlights on the label.
1777
+ *
1778
+ * Empty query returns all items in original order (no positions).
1779
+ * A cancelled signal causes the returned promise to reject with an AbortError.
1780
+ */
1781
+ function createAcCompletionProvider(items, options = {}) {
1782
+ const fuzzy = createFuzzySearch(items, { selector: options.selector ?? ((item) => item.label) });
1783
+ return (query, signal) => new Promise((resolve, reject) => {
1784
+ if (signal.aborted) {
1785
+ reject(new DOMException(`Aborted`, `AbortError`));
1786
+ return;
1787
+ }
1788
+ const results = fuzzy.find(query).map((r) => ({
1789
+ ...r.item,
1790
+ positions: r.positions
1791
+ }));
1792
+ if (signal.aborted) {
1793
+ reject(new DOMException(`Aborted`, `AbortError`));
1794
+ return;
1795
+ }
1796
+ resolve(results);
1797
+ });
1798
+ }
1799
+ //#endregion
1800
+ //#region src/incr-search/incr-search.ts
1801
+ function buildState(results, query) {
1802
+ const positionsByItem = /* @__PURE__ */ new Map();
1803
+ for (const r of results) positionsByItem.set(r.item, r.positions);
1804
+ return Object.freeze({
1805
+ query,
1806
+ results: Object.freeze([...results]),
1807
+ positionsByItem
1808
+ });
1809
+ }
1810
+ function createIncrSearch(options = {}) {
1811
+ let items = options.items ?? [];
1812
+ let query = ``;
1813
+ let fuzzy = createFuzzySearch(items, {
1814
+ selector: options.selector,
1815
+ fzf: options.fzf
1816
+ });
1817
+ let state = buildState(fuzzy.find(query), query);
1818
+ let disposed = false;
1819
+ const observers = /* @__PURE__ */ new Set();
1820
+ if (options.onChange) observers.add(options.onChange);
1821
+ const notify = (s) => {
1822
+ for (const cb of observers) cb(s);
1823
+ };
1824
+ const run = async (q, signal) => {
1825
+ await Promise.resolve();
1826
+ if (signal?.aborted || disposed) return state;
1827
+ const results = fuzzy.find(q);
1828
+ if (signal?.aborted || disposed) return state;
1829
+ state = buildState(results, q);
1830
+ notify(state);
1831
+ return state;
1832
+ };
1833
+ return {
1834
+ get state() {
1835
+ return state;
1836
+ },
1837
+ async setItems(newItems, signal) {
1838
+ items = newItems;
1839
+ fuzzy = createFuzzySearch(items, {
1840
+ selector: options.selector,
1841
+ fzf: options.fzf
1842
+ });
1843
+ await run(query, signal);
1844
+ },
1845
+ async setQuery(q, signal) {
1846
+ query = q;
1847
+ return run(q, signal);
1848
+ },
1849
+ async clearQuery() {
1850
+ query = ``;
1851
+ await run(``, void 0);
1852
+ },
1853
+ onChange(cb) {
1854
+ observers.add(cb);
1855
+ cb(state);
1856
+ return () => {
1857
+ observers.delete(cb);
1858
+ };
1859
+ },
1860
+ dispose() {
1861
+ disposed = true;
1862
+ observers.clear();
1863
+ items = [];
1864
+ }
1865
+ };
1866
+ }
1867
+ //#endregion
1868
+ //#region src/incr-search/controller-tree.ts
1869
+ const DEFAULT_HIGHLIGHT_KEY = `incr-search-highlight`;
1870
+ /**
1871
+ * Incremental-search controller for any component implementing `TreeSearchHost`
1872
+ * (i.e. `ixfx-tree-list`, `ixfx-miller-list`, `ixfx-crumb-navigation`).
1873
+ *
1874
+ * Replaces the previous separate `IncrSearchTreeController` / `IncrSearchMillerController`
1875
+ * classes — the unified `TreeFilterPredicate` (with depth) means one implementation works
1876
+ * for all tree-like components.
1877
+ *
1878
+ * @example Using with ixfx-tree-list:
1879
+ * ```typescript
1880
+ * const ctrl = new IncrSearchTreeController(myTreeList, {
1881
+ * selector: item => item.node.item.label,
1882
+ * });
1883
+ * ctrl.connect();
1884
+ * ```
1885
+ *
1886
+ * @example Using with ixfx-miller-list (different label selector):
1887
+ * ```typescript
1888
+ * const ctrl = new IncrSearchTreeController(myMillerList, {
1889
+ * selector: item => item.node.item.label,
1890
+ * labelSelector: '.label',
1891
+ * });
1892
+ * ctrl.connect();
1893
+ * ```
1894
+ */
1895
+ var IncrSearchTreeController = class {
1896
+ #el;
1897
+ #opts;
1898
+ #search;
1899
+ #unsub;
1900
+ #labelMap = /* @__PURE__ */ new Map();
1901
+ constructor(el, opts) {
1902
+ this.#el = el;
1903
+ this.#opts = opts;
1904
+ this.#search = createIncrSearch({
1905
+ selector: opts.selector,
1906
+ fzf: opts.fzf
1907
+ });
1908
+ }
1909
+ connect() {
1910
+ this.disconnect();
1911
+ const items = this.#el.getNodes();
1912
+ for (const item of items) this.#labelMap.set(this.#opts.selector(item), item);
1913
+ this.#search.setItems(items);
1914
+ if (this.#opts.input) {
1915
+ this.#opts.input.addEventListener(`input`, this.#onInput);
1916
+ this.#opts.input.addEventListener(`keydown`, this.#onKeydown);
1917
+ }
1918
+ this.#unsub = this.#search.onChange((state) => this.#onStateChange(state));
1919
+ }
1920
+ disconnect() {
1921
+ if (this.#unsub) {
1922
+ this.#unsub();
1923
+ this.#unsub = void 0;
1924
+ }
1925
+ if (this.#opts.input) {
1926
+ this.#opts.input.removeEventListener(`input`, this.#onInput);
1927
+ this.#opts.input.removeEventListener(`keydown`, this.#onKeydown);
1928
+ }
1929
+ this.#el.filterPredicate = void 0;
1930
+ clearHighlights({ key: this.#opts.highlightKey ?? DEFAULT_HIGHLIGHT_KEY });
1931
+ this.#labelMap.clear();
1932
+ }
1933
+ async setQuery(query) {
1934
+ await this.#search.setQuery(query);
1935
+ }
1936
+ async clearQuery() {
1937
+ await this.#search.clearQuery();
1938
+ }
1939
+ #onInput = () => {
1940
+ const value = this.#opts.input?.value ?? ``;
1941
+ this.setQuery(value);
1942
+ };
1943
+ #onKeydown = (e) => {
1944
+ if (e.key === `Escape` && this.#opts.input) {
1945
+ this.#opts.input.value = ``;
1946
+ this.clearQuery();
1947
+ }
1948
+ };
1949
+ #onStateChange(state) {
1950
+ const matched = new Set(state.results.map((r) => r.item.node.item.key));
1951
+ const hasQuery = state.query.trim().length > 0;
1952
+ this.#el.filterPredicate = hasQuery && matched.size > 0 ? (node, _depth) => matched.has(node.item.key) : void 0;
1953
+ const key = this.#opts.highlightKey ?? DEFAULT_HIGHLIGHT_KEY;
1954
+ if (!this.#el.shadowRoot) return;
1955
+ if (!state.query.trim()) {
1956
+ clearHighlights({ key });
1957
+ return;
1958
+ }
1959
+ const labelSelector = this.#opts.labelSelector ?? `.tree-label`;
1960
+ applyHighlights(buildHighlightTargets(this.#el.shadowRoot, labelSelector, state.positionsByItem, (labelEl) => {
1961
+ const text = labelEl.textContent ?? ``;
1962
+ return this.#labelMap.get(text);
1963
+ }, (labelEl) => labelEl.textContent ?? ``), { key });
1964
+ }
1965
+ };
1966
+ //#endregion
1967
+ //#region src/incr-search/element-search.ts
1968
+ const defaultGetLabel = (el) => el.textContent?.trim() ?? ``;
1969
+ function defaultGetKey(el) {
1970
+ return el.getAttribute(`data-key`) ?? el.getAttribute(`data-value`) ?? el.textContent?.trim() ?? ``;
1971
+ }
1972
+ function createElementIncrSearch(opts) {
1973
+ const { container, itemSelector, getLabel = defaultGetLabel, getKey = defaultGetKey, labelSelector, highlightKey } = opts;
1974
+ let elementByKey = /* @__PURE__ */ new Map();
1975
+ const search = createIncrSearch({
1976
+ selector: (item) => item.label,
1977
+ fzf: opts.fzf
1978
+ });
1979
+ const updateVisibility = (state) => {
1980
+ const matchedKeys = new Set(state.results.map((r) => r.item.key));
1981
+ const hasQuery = state.query.trim().length > 0;
1982
+ for (const [key, el] of elementByKey) el.hidden = hasQuery ? !matchedKeys.has(key) : false;
1983
+ };
1984
+ const updateHighlights = (state) => {
1985
+ if (!state.query.trim()) {
1986
+ clearHighlights({ key: highlightKey });
1987
+ return;
1988
+ }
1989
+ applyHighlights(buildHighlightTargets(container, labelSelector ?? itemSelector, state.positionsByItem, (el) => {
1990
+ const key = getKey(el);
1991
+ return state.results.find((r) => r.item.key === key)?.item;
1992
+ }, (el) => getLabel(el)), { key: highlightKey });
1993
+ };
1994
+ const unsubscribeState = search.onChange((state) => {
1995
+ updateVisibility(state);
1996
+ updateHighlights(state);
1997
+ opts.onChange?.(state);
1998
+ });
1999
+ let refreshPending = false;
2000
+ const refresh = () => {
2001
+ if (refreshPending) return;
2002
+ refreshPending = true;
2003
+ Promise.resolve().then(() => {
2004
+ refreshPending = false;
2005
+ elementByKey = /* @__PURE__ */ new Map();
2006
+ const els = container.querySelectorAll(itemSelector);
2007
+ const items = [];
2008
+ for (const el of els) {
2009
+ const key = getKey(el);
2010
+ elementByKey.set(key, el);
2011
+ items.push({
2012
+ key,
2013
+ label: getLabel(el)
2014
+ });
2015
+ }
2016
+ search.setItems(items);
2017
+ });
2018
+ };
2019
+ const observer = new MutationObserver(() => refresh());
2020
+ observer.observe(container, {
2021
+ childList: true,
2022
+ subtree: false
2023
+ });
2024
+ refresh();
2025
+ return {
2026
+ async setQuery(query, signal) {
2027
+ await search.setQuery(query, signal);
2028
+ },
2029
+ async clearQuery() {
2030
+ await search.clearQuery();
2031
+ },
2032
+ refresh,
2033
+ onChange(cb) {
2034
+ return search.onChange(cb);
2035
+ },
2036
+ dispose() {
2037
+ observer.disconnect();
2038
+ unsubscribeState();
2039
+ search.dispose();
2040
+ clearHighlights({ key: highlightKey });
2041
+ }
2042
+ };
2043
+ }
2044
+ //#endregion
2045
+ //#region src/ac-token/ac-token.ts
2046
+ const DEFAULT_TOKENIZER = (text) => text.split(/[\s,;]+/);
2047
+ function makeLruCache(maxSize) {
2048
+ const map = /* @__PURE__ */ new Map();
2049
+ return {
2050
+ get: (k) => map.get(k),
2051
+ set: (k, v) => {
2052
+ if (map.has(k)) map.delete(k);
2053
+ map.set(k, v);
2054
+ if (map.size > maxSize) map.delete(map.keys().next().value);
2055
+ },
2056
+ has: (k) => map.has(k),
2057
+ clear: () => map.clear()
2058
+ };
2059
+ }
2060
+ let AcTokenElement = class AcTokenElement extends i$4 {
2061
+ #cache;
2062
+ #debounceTimer;
2063
+ #abortController;
2064
+ #staticProvider;
2065
+ constructor() {
2066
+ super();
2067
+ this.placeholder = ``;
2068
+ this.cacheSize = 20;
2069
+ this.debounceMs = 150;
2070
+ this.strict = false;
2071
+ this._tokens = [];
2072
+ this._draft = ``;
2073
+ this._suggestions = [];
2074
+ this._isOpen = false;
2075
+ this._isLoading = false;
2076
+ this._tickled = new TickledController(this, { getItems: () => this._suggestions.map((_, i) => ({
2077
+ id: i,
2078
+ enabled: true
2079
+ })) });
2080
+ this.#cache = makeLruCache(20);
2081
+ this.#onWrapperClick = (e) => {
2082
+ if (!e.target.closest(`.token-chip`)) this.shadowRoot?.querySelector(`input.draft`)?.focus();
2083
+ };
2084
+ this.#onFocus = () => {
2085
+ this._selectedTokenIndex = void 0;
2086
+ this.#scheduleSuggestionFetch(this._draft);
2087
+ };
2088
+ this.#onBlur = () => {
2089
+ setTimeout(() => {
2090
+ this._isOpen = false;
2091
+ this._tickled.clearTickled();
2092
+ this._selectedTokenIndex = void 0;
2093
+ }, 120);
2094
+ };
2095
+ this.#onInput = (e) => {
2096
+ const value = e.target.value;
2097
+ this._selectedTokenIndex = void 0;
2098
+ const parts = (this.tokenizer ?? DEFAULT_TOKENIZER)(value);
2099
+ if (parts.length > 1) {
2100
+ for (let i = 0; i < parts.length - 1; i++) {
2101
+ const label = parts[i].trim();
2102
+ if (label) this.#addToken({ label });
2103
+ }
2104
+ this._draft = parts[parts.length - 1] ?? ``;
2105
+ } else this._draft = value;
2106
+ this.#scheduleSuggestionFetch(this._draft);
2107
+ };
2108
+ this.#onKeyDown = (e) => {
2109
+ const input = e.target;
2110
+ if ((e.metaKey || e.ctrlKey) && e.key === `a`) {
2111
+ e.preventDefault();
2112
+ const parts = [...this._tokens.map((t) => t.label), this._draft.trim()].filter(Boolean);
2113
+ navigator.clipboard.writeText(parts.join(` `)).catch(() => {});
2114
+ return;
2115
+ }
2116
+ if (this._selectedTokenIndex !== void 0 && e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
2117
+ e.preventDefault();
2118
+ this.#startEditingSelectedToken(e.key);
2119
+ return;
2120
+ }
2121
+ this.#handleNavigationKey(e, input);
2122
+ };
2123
+ this.#onPaste = (e) => {
2124
+ e.preventDefault();
2125
+ const text = e.clipboardData?.getData(`text/plain`) ?? ``;
2126
+ if (!text) return;
2127
+ const parts = (this.tokenizer ?? DEFAULT_TOKENIZER)(this._draft + text).filter(Boolean);
2128
+ if (parts.length === 0) return;
2129
+ if (parts.length === 1) this._draft = parts[0];
2130
+ else {
2131
+ for (let i = 0; i < parts.length - 1; i++) {
2132
+ const label = parts[i].trim();
2133
+ if (label) this.#addToken({ label });
2134
+ }
2135
+ this._draft = parts[parts.length - 1] ?? ``;
2136
+ }
2137
+ this.updateComplete.then(() => {
2138
+ this.shadowRoot?.querySelector(`input.draft`)?.setSelectionRange(this._draft.length, this._draft.length);
2139
+ });
2140
+ this.#scheduleSuggestionFetch(this._draft);
2141
+ };
2142
+ this.#onSuggestionPointerDown = (e, item) => {
2143
+ e.preventDefault();
2144
+ this.#selectSuggestion(item);
2145
+ };
2146
+ this.#onChipPointerDown = (e, index) => {
2147
+ if (e.button !== 0) return;
2148
+ e.preventDefault();
2149
+ const el = e.currentTarget;
2150
+ const startY = e.clientY;
2151
+ let dragged = false;
2152
+ el.setPointerCapture(e.pointerId);
2153
+ const cleanup = () => {
2154
+ el.removeEventListener(`pointermove`, onMove);
2155
+ el.removeEventListener(`pointerup`, onUp);
2156
+ };
2157
+ const onMove = (me) => {
2158
+ if (Math.abs(me.clientY - startY) > 28) {
2159
+ dragged = true;
2160
+ el.releasePointerCapture(me.pointerId);
2161
+ cleanup();
2162
+ this.#removeToken(index);
2163
+ this.shadowRoot?.querySelector(`input.draft`)?.focus();
2164
+ }
2165
+ };
2166
+ const onUp = () => {
2167
+ cleanup();
2168
+ if (!dragged) {
2169
+ this._selectedTokenIndex = this._selectedTokenIndex === index ? void 0 : index;
2170
+ this.shadowRoot?.querySelector(`input.draft`)?.focus();
2171
+ }
2172
+ };
2173
+ el.addEventListener(`pointermove`, onMove);
2174
+ el.addEventListener(`pointerup`, onUp);
2175
+ };
2176
+ this.#cache = makeLruCache(this.cacheSize);
2177
+ }
2178
+ get tokens() {
2179
+ return this._tokens;
2180
+ }
2181
+ set tokens(value) {
2182
+ this._tokens = [...value];
2183
+ this.#emitChange();
2184
+ }
2185
+ /** All committed token labels joined with commas. */
2186
+ get value() {
2187
+ return this._tokens.map((t) => t.label).join(`, `);
2188
+ }
2189
+ connectedCallback() {
2190
+ super.connectedCallback();
2191
+ this.#buildStaticProvider();
2192
+ }
2193
+ disconnectedCallback() {
2194
+ super.disconnectedCallback();
2195
+ this.#cancelPending();
2196
+ }
2197
+ updated(changedProps) {
2198
+ if (changedProps.has(`completionProvider`) || changedProps.has(`cacheSize`)) this.#cache = makeLruCache(this.cacheSize);
2199
+ if (changedProps.has(`completionProvider`)) this.#buildStaticProvider();
2200
+ }
2201
+ #buildStaticProvider() {
2202
+ const raw = this.getAttribute(`data-tokens`);
2203
+ if (!raw || this.completionProvider) {
2204
+ this.#staticProvider = void 0;
2205
+ return;
2206
+ }
2207
+ const items = raw.trim().split(/[\s,;]+/).filter(Boolean).map((label) => ({ label }));
2208
+ this.#staticProvider = createAcCompletionProvider(items);
2209
+ }
2210
+ #onWrapperClick;
2211
+ #onFocus;
2212
+ #onBlur;
2213
+ #onInput;
2214
+ #onKeyDown;
2215
+ #handleNavigationKey(e, input) {
2216
+ switch (e.key) {
2217
+ case `ArrowLeft`:
2218
+ if (input.selectionStart === 0 && input.selectionEnd === 0) {
2219
+ e.preventDefault();
2220
+ if (this._selectedTokenIndex === void 0) {
2221
+ if (this._tokens.length > 0) this._selectedTokenIndex = this._tokens.length - 1;
2222
+ } else if (this._selectedTokenIndex > 0) this._selectedTokenIndex--;
2223
+ }
2224
+ break;
2225
+ case `ArrowRight`:
2226
+ if (this._selectedTokenIndex !== void 0) {
2227
+ e.preventDefault();
2228
+ if (this._selectedTokenIndex < this._tokens.length - 1) this._selectedTokenIndex++;
2229
+ else this._selectedTokenIndex = void 0;
2230
+ }
2231
+ break;
2232
+ case `Backspace`:
2233
+ if (input.selectionStart === 0 && input.selectionEnd === 0) {
2234
+ e.preventDefault();
2235
+ if (this._selectedTokenIndex !== void 0) this.#removeToken(this._selectedTokenIndex);
2236
+ else if (this._tokens.length > 0) this.#removeToken(this._tokens.length - 1);
2237
+ }
2238
+ break;
2239
+ case `Enter`: {
2240
+ const tickledIdx = this._tickled.tickledId;
2241
+ if (this._isOpen && tickledIdx !== void 0 && this._suggestions[tickledIdx]) {
2242
+ e.preventDefault();
2243
+ this.#selectSuggestion(this._suggestions[tickledIdx]);
2244
+ } else if (!this.strict && this._draft.trim()) {
2245
+ e.preventDefault();
2246
+ this.#addToken({ label: this._draft.trim() });
2247
+ this._draft = ``;
2248
+ this.#closePopup();
2249
+ }
2250
+ break;
2251
+ }
2252
+ case `ArrowDown`:
2253
+ if (!this._isOpen) return;
2254
+ e.preventDefault();
2255
+ this._tickled.handleKeyboardNavigation(`next`);
2256
+ this.#scrollSuggestionIntoView();
2257
+ break;
2258
+ case `ArrowUp`:
2259
+ if (!this._isOpen) return;
2260
+ e.preventDefault();
2261
+ this._tickled.handleKeyboardNavigation(`previous`);
2262
+ this.#scrollSuggestionIntoView();
2263
+ break;
2264
+ case `Escape`:
2265
+ e.preventDefault();
2266
+ this.#closePopup();
2267
+ this._selectedTokenIndex = void 0;
2268
+ break;
2269
+ }
2270
+ }
2271
+ #onPaste;
2272
+ #onSuggestionPointerDown;
2273
+ #onChipPointerDown;
2274
+ #startEditingSelectedToken(key) {
2275
+ const idx = this._selectedTokenIndex;
2276
+ if (idx === void 0) return;
2277
+ const token = this._tokens[idx];
2278
+ if (!token) return;
2279
+ const newDraft = token.label + key;
2280
+ this.#removeToken(idx);
2281
+ this._selectedTokenIndex = void 0;
2282
+ this._draft = newDraft;
2283
+ this.updateComplete.then(() => {
2284
+ const inp = this.shadowRoot?.querySelector(`input.draft`);
2285
+ if (!inp) return;
2286
+ inp.setSelectionRange(inp.value.length, inp.value.length);
2287
+ });
2288
+ this.#scheduleSuggestionFetch(newDraft);
2289
+ }
2290
+ #addToken(item) {
2291
+ const index = this._tokens.length;
2292
+ this._tokens = [...this._tokens, item];
2293
+ this.dispatchEvent(new CustomEvent(`ac-token-add`, {
2294
+ detail: {
2295
+ item,
2296
+ index
2297
+ },
2298
+ bubbles: true,
2299
+ composed: true
2300
+ }));
2301
+ this.#emitChange();
2302
+ }
2303
+ #removeToken(index) {
2304
+ const item = this._tokens[index];
2305
+ if (!item) return;
2306
+ this._tokens = this._tokens.filter((_, i) => i !== index);
2307
+ if (this._selectedTokenIndex !== void 0) {
2308
+ if (this._selectedTokenIndex === index) this._selectedTokenIndex = void 0;
2309
+ else if (this._selectedTokenIndex > index) this._selectedTokenIndex--;
2310
+ }
2311
+ this.dispatchEvent(new CustomEvent(`ac-token-remove`, {
2312
+ detail: {
2313
+ item,
2314
+ index
2315
+ },
2316
+ bubbles: true,
2317
+ composed: true
2318
+ }));
2319
+ this.#emitChange();
2320
+ }
2321
+ #selectSuggestion(item) {
2322
+ this.#addToken(item);
2323
+ this._draft = ``;
2324
+ this.#closePopup();
2325
+ }
2326
+ #emitChange() {
2327
+ this.dispatchEvent(new CustomEvent(`ac-token-change`, {
2328
+ detail: { tokens: this._tokens },
2329
+ bubbles: true,
2330
+ composed: true
2331
+ }));
2332
+ }
2333
+ #closePopup() {
2334
+ this._isOpen = false;
2335
+ this._tickled.clearTickled();
2336
+ }
2337
+ #cancelPending() {
2338
+ if (this.#debounceTimer !== void 0) {
2339
+ clearTimeout(this.#debounceTimer);
2340
+ this.#debounceTimer = void 0;
2341
+ }
2342
+ this.#abortController?.abort();
2343
+ this.#abortController = void 0;
2344
+ this._isLoading = false;
2345
+ }
2346
+ #scheduleSuggestionFetch(query) {
2347
+ const provider = this.completionProvider ?? this.#staticProvider;
2348
+ if (!provider) {
2349
+ this._isOpen = false;
2350
+ return;
2351
+ }
2352
+ if (this.#debounceTimer !== void 0) clearTimeout(this.#debounceTimer);
2353
+ this._isOpen = true;
2354
+ this.#debounceTimer = setTimeout(() => {
2355
+ this.#fetchSuggestions(query, provider);
2356
+ }, this.debounceMs);
2357
+ }
2358
+ #fetchSuggestions(query, provider) {
2359
+ const cached = this.#cache.get(query);
2360
+ if (cached !== void 0) {
2361
+ this._suggestions = cached;
2362
+ this._tickled.clearTickled();
2363
+ this._isLoading = false;
2364
+ return;
2365
+ }
2366
+ this.#abortController?.abort();
2367
+ this.#abortController = new AbortController();
2368
+ const { signal } = this.#abortController;
2369
+ this._isLoading = true;
2370
+ Promise.resolve(provider(query, signal)).then((results) => {
2371
+ if (signal.aborted) return;
2372
+ this.#cache.set(query, results);
2373
+ this._suggestions = results;
2374
+ this._tickled.clearTickled();
2375
+ this._isLoading = false;
2376
+ }).catch((err) => {
2377
+ if (signal.aborted) return;
2378
+ console.error(`[ixfx-ac-token] completionProvider error:`, err);
2379
+ this._suggestions = [];
2380
+ this._isLoading = false;
2381
+ });
2382
+ }
2383
+ #scrollSuggestionIntoView() {
2384
+ this.updateComplete.then(() => {
2385
+ this.shadowRoot?.querySelector(`.suggestion.tickled`)?.scrollIntoView({ block: `nearest` });
2386
+ });
2387
+ }
2388
+ #renderChip(item, index) {
2389
+ return b`
2390
+ <span
2391
+ class="token-chip ${this._selectedTokenIndex === index ? `selected` : ``}"
2392
+ title=${item.subLabel ?? A}
2393
+ @pointerdown=${(e) => this.#onChipPointerDown(e, index)}
2394
+ >
2395
+ ${item.icon ? b`<ixfx-icon class="chip-icon" name=${item.icon}></ixfx-icon>` : A}
2396
+ <span class="chip-label">${item.label}</span>
2397
+ </span>
2398
+ `;
2399
+ }
2400
+ #renderSuggestion(item, index) {
2401
+ const isTickled = this._tickled.isTickled(index);
2402
+ return b`
2403
+ <div
2404
+ class="suggestion ${isTickled ? `tickled` : ``}"
2405
+ role="option"
2406
+ id="ac-token-sugg-${index}"
2407
+ aria-selected=${isTickled}
2408
+ @pointerenter=${() => this._tickled.handlePointerEnter(index)}
2409
+ @pointerdown=${(e) => this.#onSuggestionPointerDown(e, item)}
2410
+ >
2411
+ ${item.icon ? b`<ixfx-icon class="sugg-icon" name=${item.icon}></ixfx-icon>` : A}
2412
+ <span class="sugg-label">${item.label}</span>
2413
+ ${item.subLabel ? b`<span class="sugg-sublabel">${item.subLabel}</span>` : A}
2414
+ </div>
2415
+ `;
2416
+ }
2417
+ #renderPopup() {
2418
+ const showEmpty = !this._isLoading && this._suggestions.length === 0;
2419
+ return b`
2420
+ <div
2421
+ class="popup"
2422
+ role="listbox"
2423
+ aria-label="Suggestions"
2424
+ data-input-mode=${this._tickled.inputMode}
2425
+ @pointerleave=${(e) => this._tickled.handlePointerLeave(e)}
2426
+ >
2427
+ ${showEmpty ? b`<div class="no-results">No matching results</div>` : this._suggestions.map((item, i) => this.#renderSuggestion(item, i))}
2428
+ </div>
2429
+ `;
2430
+ }
2431
+ render() {
2432
+ const tickledIdx = this._tickled.tickledId ?? -1;
2433
+ const activeDescendant = tickledIdx >= 0 ? `ac-token-sugg-${tickledIdx}` : void 0;
2434
+ const showPlaceholder = this._tokens.length === 0 ? this.placeholder || A : A;
2435
+ return b`
2436
+ <div class="wrapper" @click=${this.#onWrapperClick}>
2437
+ ${this._tokens.map((t, i) => this.#renderChip(t, i))}
2438
+ <input
2439
+ type="text"
2440
+ class="draft"
2441
+ .value=${this._draft}
2442
+ placeholder=${showPlaceholder}
2443
+ autocomplete="off"
2444
+ spellcheck="false"
2445
+ role="combobox"
2446
+ aria-autocomplete="list"
2447
+ aria-expanded=${this._isOpen}
2448
+ aria-activedescendant=${activeDescendant ?? A}
2449
+ @input=${this.#onInput}
2450
+ @keydown=${this.#onKeyDown}
2451
+ @focus=${this.#onFocus}
2452
+ @blur=${this.#onBlur}
2453
+ @paste=${this.#onPaste}
2454
+ >
2455
+ ${this._isLoading ? b`<span class="spinner" aria-hidden="true"></span>` : A}
2456
+ </div>
2457
+ ${this._isOpen ? this.#renderPopup() : A}
2458
+ `;
2459
+ }
2460
+ static {
2461
+ this.styles = [tickledStyles, i$7`
2462
+ :host {
2463
+ display: block;
2464
+ position: relative;
2465
+ font-family: var(--font-family, system-ui, sans-serif);
2466
+ font-size: var(--text-m, 12px);
2467
+ color: var(--surface-2-text);
2468
+ }
2469
+
2470
+ /* ---- Wrapper (looks like a text input) ---- */
2471
+
2472
+ .wrapper {
2473
+ display: flex;
2474
+ flex-wrap: wrap;
2475
+ align-items: center;
2476
+ gap: var(--ac-token-chip-gap, var(--space-xs, 3px));
2477
+ padding: var(--ac-token-input-padding, var(--space-xs, 3px) var(--space-m, 8px));
2478
+ background: var(--ac-token-input-background, var(--surface-3, var(--surface-2)));
2479
+ color: var(--surface-2-text);
2480
+ border: var(--ac-token-input-border, var(--border));
2481
+ border-radius: var(--ac-token-input-border-radius, var(--radius-m, 4px));
2482
+ cursor: text;
2483
+ min-height: 32px;
2484
+ box-sizing: border-box;
2485
+ transition: border-color var(--transition, 0.15s ease-out);
2486
+ }
2487
+
2488
+ .wrapper:focus-within {
2489
+ border-color: var(--accent);
2490
+ }
2491
+
2492
+ /* ---- Token chips ---- */
2493
+
2494
+ .token-chip {
2495
+ display: inline-flex;
2496
+ align-items: center;
2497
+ gap: var(--space-xs, 3px);
2498
+ padding: 1px var(--space-m, 8px);
2499
+ background: var(--ac-token-chip-background, var(--surface-4));
2500
+ border: var(--ac-token-chip-border, var(--border));
2501
+ border-radius: var(--ac-token-chip-border-radius, var(--radius-s, 3px));
2502
+ user-select: none;
2503
+ cursor: default;
2504
+ font-size: var(--text-s, 11px);
2505
+ white-space: nowrap;
2506
+ touch-action: none;
2507
+ transition:
2508
+ background var(--transition, 0.1s ease-out),
2509
+ border-color var(--transition, 0.1s ease-out),
2510
+ color var(--transition, 0.1s ease-out);
2511
+ }
2512
+
2513
+ .token-chip.selected {
2514
+ background: var(--ac-token-chip-selected-background, var(--accent));
2515
+ color: var(--ac-token-chip-selected-color, var(--accent-text, #fff));
2516
+ border-color: var(--accent);
2517
+ }
2518
+
2519
+ .chip-icon {
2520
+ flex-shrink: 0;
2521
+ width: 1em;
2522
+ height: 1em;
2523
+ }
2524
+
2525
+ /* ---- Draft input ---- */
2526
+
2527
+ input.draft {
2528
+ flex: 1;
2529
+ min-width: 60px;
2530
+ border: none;
2531
+ background: transparent;
2532
+ color: inherit;
2533
+ font: inherit;
2534
+ outline: none;
2535
+ caret-color: var(--accent);
2536
+ padding: 1px 0;
2537
+ anchor-name: --ac-token-draft;
2538
+ }
2539
+
2540
+ input.draft::placeholder {
2541
+ color: var(--surface-muted-text);
2542
+ }
2543
+
2544
+ /* ---- Spinner ---- */
2545
+
2546
+ .spinner {
2547
+ width: 12px;
2548
+ height: 12px;
2549
+ border: 2px solid var(--surface-5);
2550
+ border-top-color: var(--accent);
2551
+ border-radius: 50%;
2552
+ animation: ac-token-spin 0.7s linear infinite;
2553
+ flex-shrink: 0;
2554
+ pointer-events: none;
2555
+ margin-left: var(--space-xs, 3px);
2556
+ }
2557
+
2558
+ @keyframes ac-token-spin {
2559
+ to { transform: rotate(360deg); }
2560
+ }
2561
+
2562
+ /* ---- Popup ---- */
2563
+
2564
+ .popup {
2565
+ position: fixed;
2566
+ position-anchor: --ac-token-draft;
2567
+ top: anchor(bottom);
2568
+ left: anchor(left);
2569
+ margin-top: 2px;
2570
+ position-try-fallbacks: --ac-token-popup-above;
2571
+ z-index: 200;
2572
+ background: var(--surface-3, var(--surface-2));
2573
+ border: var(--border);
2574
+ border-radius: var(--radius-m, 4px);
2575
+ box-shadow: var(--shadow-l, 0 4px 24px rgba(0, 0, 0, 0.5));
2576
+ padding: var(--space-xs, 3px) 0;
2577
+ max-height: 260px;
2578
+ min-width: 160px;
2579
+ overflow-y: auto;
2580
+ font-size: var(--ac-token-popup-font-size, var(--text-s, 11px));
2581
+ }
2582
+
2583
+ @position-try --ac-token-popup-above {
2584
+ top: auto;
2585
+ bottom: anchor(top);
2586
+ left: anchor(left);
2587
+ margin-top: 0;
2588
+ margin-bottom: 2px;
2589
+ }
2590
+
2591
+ /* ---- Suggestion items ---- */
2592
+
2593
+ .suggestion {
2594
+ display: flex;
2595
+ align-items: center;
2596
+ gap: var(--space-m, 8px);
2597
+ padding: var(--space-xs, 3px) var(--space-l, 12px);
2598
+ cursor: pointer;
2599
+ border-radius: var(--radius-s, 3px);
2600
+ margin: 0 var(--space-xs, 3px);
2601
+ transition: background var(--transition, 0.1s ease-out);
2602
+ user-select: none;
2603
+ white-space: nowrap;
2604
+ min-height: 28px;
2605
+ }
2606
+
2607
+ .suggestion.tickled {
2608
+ background: var(--_item-focused-bg, var(--accent));
2609
+ color: var(--accent-text, #fff);
2610
+ }
2611
+
2612
+ .suggestion.tickled .sugg-sublabel {
2613
+ color: color-mix(in srgb, var(--accent-text, #fff) 70%, transparent);
2614
+ }
2615
+
2616
+ .sugg-icon {
2617
+ flex-shrink: 0;
2618
+ width: 1em;
2619
+ height: 1em;
2620
+ }
2621
+
2622
+ .sugg-label {
2623
+ flex: 1;
2624
+ overflow: hidden;
2625
+ text-overflow: ellipsis;
2626
+ }
2627
+
2628
+ .sugg-sublabel {
2629
+ flex-shrink: 0;
2630
+ margin-left: auto;
2631
+ color: var(--surface-muted-text);
2632
+ font-size: var(--text-s, 11px);
2633
+ }
2634
+
2635
+ /* ---- Empty state ---- */
2636
+
2637
+ .no-results {
2638
+ padding: var(--space-m, 8px) var(--space-l, 12px);
2639
+ color: var(--surface-muted-text);
2640
+ font-style: italic;
2641
+ }
2642
+ `];
2643
+ }
2644
+ };
2645
+ __decorate([n$2({ type: Object })], AcTokenElement.prototype, "completionProvider", void 0);
2646
+ __decorate([n$2({ type: Object })], AcTokenElement.prototype, "tokenizer", void 0);
2647
+ __decorate([n$2({ type: String })], AcTokenElement.prototype, "placeholder", void 0);
2648
+ __decorate([n$2({
2649
+ type: Number,
2650
+ attribute: `cache-size`
2651
+ })], AcTokenElement.prototype, "cacheSize", void 0);
2652
+ __decorate([n$2({
2653
+ type: Number,
2654
+ attribute: `debounce-ms`
2655
+ })], AcTokenElement.prototype, "debounceMs", void 0);
2656
+ __decorate([n$2({ type: Boolean })], AcTokenElement.prototype, "strict", void 0);
2657
+ __decorate([r$1()], AcTokenElement.prototype, "_tokens", void 0);
2658
+ __decorate([r$1()], AcTokenElement.prototype, "_draft", void 0);
2659
+ __decorate([r$1()], AcTokenElement.prototype, "_selectedTokenIndex", void 0);
2660
+ __decorate([r$1()], AcTokenElement.prototype, "_suggestions", void 0);
2661
+ __decorate([r$1()], AcTokenElement.prototype, "_isOpen", void 0);
2662
+ __decorate([r$1()], AcTokenElement.prototype, "_isLoading", void 0);
2663
+ AcTokenElement = __decorate([safeCustomElement(`ixfx-ac-token`)], AcTokenElement);
2664
+ //#endregion
1766
2665
  //#region src/icons/defaults.ts
1767
2666
  /**
1768
2667
  * Chevron pointing down. Used for split-button dropdown toggle.
@@ -1979,7 +2878,7 @@ let ButtonElement = class ButtonElement extends i$4 {
1979
2878
  }
1980
2879
  _renderIcon() {
1981
2880
  if (this.hideIcon || !this.icon) return A;
1982
- if (this.icon.startsWith(`<svg`)) return b`<span class="button-icon">${o(this.icon)}</span>`;
2881
+ if (this.icon.startsWith(`<svg`)) return b`<span class="button-icon">${o$1(this.icon)}</span>`;
1983
2882
  if (!getIcon(this.icon)) return A;
1984
2883
  return b`<ixfx-icon class="button-icon" name="${this.icon}"></ixfx-icon>`;
1985
2884
  }
@@ -2107,7 +3006,7 @@ let RadioGroup = class RadioGroup extends i$4 {
2107
3006
  if (iconName) return b`<ixfx-icon class="radio-icon" name="${iconName}"></ixfx-icon>`;
2108
3007
  const iconHtml = this._icons.get(item.value);
2109
3008
  if (!iconHtml) return null;
2110
- return b`<span class="radio-icon">${o(iconHtml)}</span>`;
3009
+ return b`<span class="radio-icon">${o$1(iconHtml)}</span>`;
2111
3010
  }
2112
3011
  render() {
2113
3012
  if (this._isSingleToggle) {
@@ -2361,7 +3260,7 @@ let MenuContainer = class MenuContainer extends i$4 {
2361
3260
  getItems: () => {
2362
3261
  return this._getMenuItems().map((item, i) => ({
2363
3262
  id: i,
2364
- enabled: !item.hasAttribute("disabled")
3263
+ enabled: !item.hasAttribute(`disabled`)
2365
3264
  }));
2366
3265
  },
2367
3266
  onTickle: (index) => {
@@ -2495,7 +3394,7 @@ let MenuContainer = class MenuContainer extends i$4 {
2495
3394
  */
2496
3395
  willUpdate(changed) {
2497
3396
  super.willUpdate(changed);
2498
- this.setAttribute("data-input-mode", this.tickled.inputMode);
3397
+ this.setAttribute(`data-input-mode`, this.tickled.inputMode);
2499
3398
  }
2500
3399
  render() {
2501
3400
  return b`
@@ -2553,14 +3452,14 @@ let MenuContainer = class MenuContainer extends i$4 {
2553
3452
  }
2554
3453
  }
2555
3454
  });
2556
- if (this.tickled.inputMode === "keyboard" && this.tickled.tickledId !== void 0) items[this.tickled.tickledId]?.focus();
3455
+ if (this.tickled.inputMode === `keyboard` && this.tickled.tickledId !== void 0) items[this.tickled.tickledId]?.focus();
2557
3456
  }
2558
3457
  focusFirst() {
2559
- this.tickled.handleKeyboardNavigation("first");
3458
+ this.tickled.handleKeyboardNavigation(`first`);
2560
3459
  }
2561
3460
  _focusNext(items, direction) {
2562
3461
  if (items.length === 0) return;
2563
- this.tickled.handleKeyboardNavigation(direction > 0 ? "next" : "previous");
3462
+ this.tickled.handleKeyboardNavigation(direction > 0 ? `next` : `previous`);
2564
3463
  }
2565
3464
  _focusItem(items, index) {
2566
3465
  if (items.length === 0) return;
@@ -18879,7 +19778,7 @@ let ColourPickerPopup = class ColourPickerPopup extends i$4 {
18879
19778
  ></ixfx-colour-picker>
18880
19779
  ${this.mode === `confirm` ? b`
18881
19780
  <button class="confirm-btn" @click=${this._onConfirm} aria-label="Confirm colour" title="Confirm colour">
18882
- ${o(ICON_CHECK)}
19781
+ ${o$1(ICON_CHECK)}
18883
19782
  </button>
18884
19783
  ` : A}
18885
19784
  ` : A}
@@ -19110,7 +20009,7 @@ var TreeDataModel = class {
19110
20009
  this.#root = root;
19111
20010
  this.#cache.clear();
19112
20011
  this.#notify({
19113
- type: "root",
20012
+ type: `root`,
19114
20013
  root
19115
20014
  });
19116
20015
  }
@@ -19138,7 +20037,7 @@ var TreeDataModel = class {
19138
20037
  invalidate(key) {
19139
20038
  this.#cache.delete(key);
19140
20039
  this.#notify({
19141
- type: "invalidate",
20040
+ type: `invalidate`,
19142
20041
  key
19143
20042
  });
19144
20043
  }
@@ -19149,7 +20048,7 @@ var TreeDataModel = class {
19149
20048
  addNode(parentKey, node) {
19150
20049
  this.#root = addChildTo(this.#root, parentKey, node);
19151
20050
  this.#notify({
19152
- type: "root",
20051
+ type: `root`,
19153
20052
  root: this.#root
19154
20053
  });
19155
20054
  return this.#root;
@@ -19162,7 +20061,7 @@ var TreeDataModel = class {
19162
20061
  this.#cache.delete(key);
19163
20062
  this.#root = removeNodeFrom(this.#root, key);
19164
20063
  this.#notify({
19165
- type: "root",
20064
+ type: `root`,
19166
20065
  root: this.#root
19167
20066
  });
19168
20067
  return this.#root;
@@ -19180,7 +20079,7 @@ var TreeDataModel = class {
19180
20079
  children: n.children
19181
20080
  }));
19182
20081
  this.#notify({
19183
- type: "root",
20082
+ type: `root`,
19184
20083
  root: this.#root
19185
20084
  });
19186
20085
  return this.#root;
@@ -19192,7 +20091,7 @@ var TreeDataModel = class {
19192
20091
  moveNode(key, newParentKey) {
19193
20092
  this.#root = moveNodeTo(this.#root, key, newParentKey);
19194
20093
  this.#notify({
19195
- type: "root",
20094
+ type: `root`,
19196
20095
  root: this.#root
19197
20096
  });
19198
20097
  return this.#root;
@@ -19212,312 +20111,6 @@ var TreeDataModel = class {
19212
20111
  }
19213
20112
  };
19214
20113
  //#endregion
19215
- //#region src/incr-search/fuzzy.ts
19216
- const defaultSelector = (item) => {
19217
- if (typeof item === `string`) return item;
19218
- const r = item;
19219
- if (typeof r[`label`] === `string`) return r[`label`];
19220
- if (typeof r[`key`] === `string`) return r[`key`];
19221
- if (typeof r[`value`] === `string`) return r[`value`];
19222
- return String(item);
19223
- };
19224
- function createFuzzySearch(items, options = {}) {
19225
- const fzf = new Fzf(items, {
19226
- selector: options.selector ?? defaultSelector,
19227
- ...options.fzf ?? {}
19228
- });
19229
- const find = (query) => {
19230
- if (!query.trim()) return items.map((item) => ({
19231
- item,
19232
- positions: /* @__PURE__ */ new Set(),
19233
- score: 0
19234
- }));
19235
- return fzf.find(query).map((r) => ({
19236
- item: r.item,
19237
- positions: r.positions,
19238
- score: r.score
19239
- }));
19240
- };
19241
- return {
19242
- find,
19243
- items
19244
- };
19245
- }
19246
- //#endregion
19247
- //#region src/incr-search/ac-adapter.ts
19248
- /**
19249
- * Creates an ixfx-ac-text-compatible `completionProvider` function from a
19250
- * static item list. Uses synchronous fuzzy search wrapped in a Promise so it
19251
- * fits the async provider signature.
19252
- *
19253
- * Each returned item has `positions` populated with matched character indices,
19254
- * which `ixfx-ac-text` uses to render CSS Custom Highlights on the label.
19255
- *
19256
- * Empty query returns all items in original order (no positions).
19257
- * A cancelled signal causes the returned promise to reject with an AbortError.
19258
- */
19259
- function createAcCompletionProvider(items, options = {}) {
19260
- const fuzzy = createFuzzySearch(items, { selector: options.selector ?? ((item) => item.label) });
19261
- return (query, signal) => new Promise((resolve, reject) => {
19262
- if (signal.aborted) {
19263
- reject(new DOMException(`Aborted`, `AbortError`));
19264
- return;
19265
- }
19266
- const results = fuzzy.find(query).map((r) => ({
19267
- ...r.item,
19268
- positions: r.positions
19269
- }));
19270
- if (signal.aborted) {
19271
- reject(new DOMException(`Aborted`, `AbortError`));
19272
- return;
19273
- }
19274
- resolve(results);
19275
- });
19276
- }
19277
- //#endregion
19278
- //#region src/incr-search/incr-search.ts
19279
- const buildState = (results, query) => {
19280
- const positionsByItem = /* @__PURE__ */ new Map();
19281
- for (const r of results) positionsByItem.set(r.item, r.positions);
19282
- return Object.freeze({
19283
- query,
19284
- results: Object.freeze([...results]),
19285
- positionsByItem
19286
- });
19287
- };
19288
- function createIncrSearch(options = {}) {
19289
- let items = options.items ?? [];
19290
- let query = ``;
19291
- let fuzzy = createFuzzySearch(items, {
19292
- selector: options.selector,
19293
- fzf: options.fzf
19294
- });
19295
- let state = buildState(fuzzy.find(query), query);
19296
- let disposed = false;
19297
- const observers = /* @__PURE__ */ new Set();
19298
- if (options.onChange) observers.add(options.onChange);
19299
- const notify = (s) => {
19300
- for (const cb of observers) cb(s);
19301
- };
19302
- const run = async (q, signal) => {
19303
- await Promise.resolve();
19304
- if (signal?.aborted || disposed) return state;
19305
- const results = fuzzy.find(q);
19306
- if (signal?.aborted || disposed) return state;
19307
- state = buildState(results, q);
19308
- notify(state);
19309
- return state;
19310
- };
19311
- return {
19312
- get state() {
19313
- return state;
19314
- },
19315
- async setItems(newItems, signal) {
19316
- items = newItems;
19317
- fuzzy = createFuzzySearch(items, {
19318
- selector: options.selector,
19319
- fzf: options.fzf
19320
- });
19321
- await run(query, signal);
19322
- },
19323
- async setQuery(q, signal) {
19324
- query = q;
19325
- return run(q, signal);
19326
- },
19327
- async clearQuery() {
19328
- query = ``;
19329
- await run(``, void 0);
19330
- },
19331
- onChange(cb) {
19332
- observers.add(cb);
19333
- cb(state);
19334
- return () => {
19335
- observers.delete(cb);
19336
- };
19337
- },
19338
- dispose() {
19339
- disposed = true;
19340
- observers.clear();
19341
- items = [];
19342
- }
19343
- };
19344
- }
19345
- //#endregion
19346
- //#region src/incr-search/controller-tree.ts
19347
- const DEFAULT_HIGHLIGHT_KEY = `incr-search-highlight`;
19348
- /**
19349
- * Incremental-search controller for any component implementing `TreeSearchHost`
19350
- * (i.e. `ixfx-tree-list`, `ixfx-miller-list`, `ixfx-crumb-navigation`).
19351
- *
19352
- * Replaces the previous separate `IncrSearchTreeController` / `IncrSearchMillerController`
19353
- * classes — the unified `TreeFilterPredicate` (with depth) means one implementation works
19354
- * for all tree-like components.
19355
- *
19356
- * @example Using with ixfx-tree-list:
19357
- * ```typescript
19358
- * const ctrl = new IncrSearchTreeController(myTreeList, {
19359
- * selector: item => item.node.item.label,
19360
- * });
19361
- * ctrl.connect();
19362
- * ```
19363
- *
19364
- * @example Using with ixfx-miller-list (different label selector):
19365
- * ```typescript
19366
- * const ctrl = new IncrSearchTreeController(myMillerList, {
19367
- * selector: item => item.node.item.label,
19368
- * labelSelector: '.label',
19369
- * });
19370
- * ctrl.connect();
19371
- * ```
19372
- */
19373
- var IncrSearchTreeController = class {
19374
- #el;
19375
- #opts;
19376
- #search;
19377
- #unsub;
19378
- #labelMap = /* @__PURE__ */ new Map();
19379
- constructor(el, opts) {
19380
- this.#el = el;
19381
- this.#opts = opts;
19382
- this.#search = createIncrSearch({
19383
- selector: opts.selector,
19384
- fzf: opts.fzf
19385
- });
19386
- }
19387
- connect() {
19388
- this.disconnect();
19389
- const items = this.#el.getNodes();
19390
- for (const item of items) this.#labelMap.set(this.#opts.selector(item), item);
19391
- this.#search.setItems(items);
19392
- if (this.#opts.input) {
19393
- this.#opts.input.addEventListener(`input`, this.#onInput);
19394
- this.#opts.input.addEventListener(`keydown`, this.#onKeydown);
19395
- }
19396
- this.#unsub = this.#search.onChange((state) => this.#onStateChange(state));
19397
- }
19398
- disconnect() {
19399
- if (this.#unsub) {
19400
- this.#unsub();
19401
- this.#unsub = void 0;
19402
- }
19403
- if (this.#opts.input) {
19404
- this.#opts.input.removeEventListener(`input`, this.#onInput);
19405
- this.#opts.input.removeEventListener(`keydown`, this.#onKeydown);
19406
- }
19407
- this.#el.filterPredicate = void 0;
19408
- clearHighlights({ key: this.#opts.highlightKey ?? DEFAULT_HIGHLIGHT_KEY });
19409
- this.#labelMap.clear();
19410
- }
19411
- async setQuery(query) {
19412
- await this.#search.setQuery(query);
19413
- }
19414
- async clearQuery() {
19415
- await this.#search.clearQuery();
19416
- }
19417
- #onInput = () => {
19418
- const value = this.#opts.input?.value ?? ``;
19419
- this.setQuery(value);
19420
- };
19421
- #onKeydown = (e) => {
19422
- if (e.key === `Escape` && this.#opts.input) {
19423
- this.#opts.input.value = ``;
19424
- this.clearQuery();
19425
- }
19426
- };
19427
- #onStateChange(state) {
19428
- const matched = new Set(state.results.map((r) => r.item.node.item.key));
19429
- const hasQuery = state.query.trim().length > 0;
19430
- this.#el.filterPredicate = hasQuery && matched.size > 0 ? (node, _depth) => matched.has(node.item.key) : void 0;
19431
- const key = this.#opts.highlightKey ?? DEFAULT_HIGHLIGHT_KEY;
19432
- if (!this.#el.shadowRoot) return;
19433
- if (!state.query.trim()) {
19434
- clearHighlights({ key });
19435
- return;
19436
- }
19437
- const labelSelector = this.#opts.labelSelector ?? `.tree-label`;
19438
- applyHighlights(buildHighlightTargets(this.#el.shadowRoot, labelSelector, state.positionsByItem, (labelEl) => {
19439
- const text = labelEl.textContent ?? ``;
19440
- return this.#labelMap.get(text);
19441
- }, (labelEl) => labelEl.textContent ?? ``), { key });
19442
- }
19443
- };
19444
- //#endregion
19445
- //#region src/incr-search/element-search.ts
19446
- const defaultGetLabel = (el) => el.textContent?.trim() ?? ``;
19447
- const defaultGetKey = (el) => el.getAttribute(`data-key`) ?? el.getAttribute(`data-value`) ?? el.textContent?.trim() ?? ``;
19448
- function createElementIncrSearch(opts) {
19449
- const { container, itemSelector, getLabel = defaultGetLabel, getKey = defaultGetKey, labelSelector, highlightKey } = opts;
19450
- let elementByKey = /* @__PURE__ */ new Map();
19451
- const search = createIncrSearch({
19452
- selector: (item) => item.label,
19453
- fzf: opts.fzf
19454
- });
19455
- const updateVisibility = (state) => {
19456
- const matchedKeys = new Set(state.results.map((r) => r.item.key));
19457
- const hasQuery = state.query.trim().length > 0;
19458
- for (const [key, el] of elementByKey) el.hidden = hasQuery ? !matchedKeys.has(key) : false;
19459
- };
19460
- const updateHighlights = (state) => {
19461
- if (!state.query.trim()) {
19462
- clearHighlights({ key: highlightKey });
19463
- return;
19464
- }
19465
- applyHighlights(buildHighlightTargets(container, labelSelector ?? itemSelector, state.positionsByItem, (el) => {
19466
- const key = getKey(el);
19467
- return state.results.find((r) => r.item.key === key)?.item;
19468
- }, (el) => getLabel(el)), { key: highlightKey });
19469
- };
19470
- const unsubscribeState = search.onChange((state) => {
19471
- updateVisibility(state);
19472
- updateHighlights(state);
19473
- opts.onChange?.(state);
19474
- });
19475
- let refreshPending = false;
19476
- const refresh = () => {
19477
- if (refreshPending) return;
19478
- refreshPending = true;
19479
- Promise.resolve().then(() => {
19480
- refreshPending = false;
19481
- elementByKey = /* @__PURE__ */ new Map();
19482
- const els = container.querySelectorAll(itemSelector);
19483
- const items = [];
19484
- for (const el of els) {
19485
- const key = getKey(el);
19486
- elementByKey.set(key, el);
19487
- items.push({
19488
- key,
19489
- label: getLabel(el)
19490
- });
19491
- }
19492
- search.setItems(items);
19493
- });
19494
- };
19495
- const observer = new MutationObserver(() => refresh());
19496
- observer.observe(container, {
19497
- childList: true,
19498
- subtree: false
19499
- });
19500
- refresh();
19501
- return {
19502
- async setQuery(query, signal) {
19503
- await search.setQuery(query, signal);
19504
- },
19505
- async clearQuery() {
19506
- await search.clearQuery();
19507
- },
19508
- refresh,
19509
- onChange(cb) {
19510
- return search.onChange(cb);
19511
- },
19512
- dispose() {
19513
- observer.disconnect();
19514
- unsubscribeState();
19515
- search.dispose();
19516
- clearHighlights({ key: highlightKey });
19517
- }
19518
- };
19519
- }
19520
- //#endregion
19521
20114
  //#region src/narrowed-text/narrowed-text.ts
19522
20115
  const HIGHLIGHT_KEY = `narrowed-text-highlight`;
19523
20116
  let NarrowedTextElement = class NarrowedTextElement extends i$4 {
@@ -20084,13 +20677,13 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i$4 {
20084
20677
  constructor(..._args) {
20085
20678
  super(..._args);
20086
20679
  this._selectedNodes = /* @__PURE__ */ new Set();
20087
- this.exclusivity = "none";
20088
- this.selectionFilter = "leaf";
20680
+ this.exclusivity = `none`;
20681
+ this.selectionFilter = `leaf`;
20089
20682
  this._openIdx = -1;
20090
20683
  this._isFocused = false;
20091
20684
  this._menuOpen = false;
20092
20685
  this._focusOnNarrowedText = false;
20093
- this.inputMode = "";
20686
+ this.inputMode = ``;
20094
20687
  this.#previousSelectionKey = ``;
20095
20688
  this.#menuOpenedByKeyboard = false;
20096
20689
  this.#currentOpenTrigger = null;
@@ -20355,7 +20948,7 @@ let CrumbNavigationElement = class CrumbNavigationElement extends i$4 {
20355
20948
  if (model) {
20356
20949
  this.root = model.root;
20357
20950
  this.#modelUnsubscribe = model._subscribe((event) => {
20358
- if (event.type === "root") {
20951
+ if (event.type === `root`) {
20359
20952
  this.root = event.root;
20360
20953
  this.requestUpdate();
20361
20954
  }
@@ -20842,11 +21435,11 @@ __decorate([n$2({ attribute: false })], CrumbNavigationElement.prototype, "loadC
20842
21435
  __decorate([n$2({ attribute: false })], CrumbNavigationElement.prototype, "filterPredicate", void 0);
20843
21436
  __decorate([n$2({
20844
21437
  reflect: true,
20845
- attribute: "exclusivity"
21438
+ attribute: `exclusivity`
20846
21439
  })], CrumbNavigationElement.prototype, "exclusivity", void 0);
20847
21440
  __decorate([n$2({
20848
21441
  reflect: true,
20849
- attribute: "selection-filter"
21442
+ attribute: `selection-filter`
20850
21443
  })], CrumbNavigationElement.prototype, "selectionFilter", void 0);
20851
21444
  __decorate([r$1()], CrumbNavigationElement.prototype, "_openIdx", void 0);
20852
21445
  __decorate([r$1()], CrumbNavigationElement.prototype, "_isFocused", void 0);
@@ -20854,7 +21447,7 @@ __decorate([r$1()], CrumbNavigationElement.prototype, "_menuOpen", void 0);
20854
21447
  __decorate([r$1()], CrumbNavigationElement.prototype, "_focusOnNarrowedText", void 0);
20855
21448
  __decorate([n$2({
20856
21449
  reflect: true,
20857
- attribute: "input-mode"
21450
+ attribute: `input-mode`
20858
21451
  })], CrumbNavigationElement.prototype, "inputMode", void 0);
20859
21452
  CrumbNavigationElement = __decorate([safeCustomElement(`ixfx-crumb-navigation`)], CrumbNavigationElement);
20860
21453
  //#endregion
@@ -21668,6 +22261,9 @@ let EditableLabel = class EditableLabel extends i$4 {
21668
22261
  this._hovering = false;
21669
22262
  this._startTransientRevertTimer();
21670
22263
  };
22264
+ this._handleClick = () => {
22265
+ if (this.mode === `label` && !this.disabled && !this._editing) this._beginEdit();
22266
+ };
21671
22267
  this._handleFocus = () => {
21672
22268
  if (this.mode === `editor` && !this.disabled && !this._editing) this._beginEdit();
21673
22269
  };
@@ -21703,6 +22299,17 @@ let EditableLabel = class EditableLabel extends i$4 {
21703
22299
  get _contentEl() {
21704
22300
  return this.renderRoot?.querySelector(`.content`) ?? void 0;
21705
22301
  }
22302
+ connectedCallback() {
22303
+ super.connectedCallback();
22304
+ this.addEventListener(`mouseenter`, this._onMouseEnter);
22305
+ this.addEventListener(`mouseleave`, this._onMouseLeave);
22306
+ }
22307
+ disconnectedCallback() {
22308
+ super.disconnectedCallback();
22309
+ this.removeEventListener(`mouseenter`, this._onMouseEnter);
22310
+ this.removeEventListener(`mouseleave`, this._onMouseLeave);
22311
+ this._clearTransientTimer();
22312
+ }
21706
22313
  _clearTransientTimer() {
21707
22314
  if (this._transientTimer !== void 0) {
21708
22315
  clearTimeout(this._transientTimer);
@@ -21796,6 +22403,32 @@ let EditableLabel = class EditableLabel extends i$4 {
21796
22403
  default: return true;
21797
22404
  }
21798
22405
  }
22406
+ render() {
22407
+ const isEditor = this.mode === `editor`;
22408
+ return b`<div
22409
+ class="content"
22410
+ contenteditable=${this._editing ? `plaintext-only` : `false`}
22411
+ data-placeholder=${this.placeholder}
22412
+ tabindex=${isEditor && !this._editing ? `0` : A}
22413
+ @click=${this._handleClick}
22414
+ @focus=${this._handleFocus}
22415
+ @blur=${this._handleBlur}
22416
+ @keydown=${this._handleKeydown}
22417
+ @beforeinput=${this._handleBeforeInput}
22418
+ @input=${this._handleInput}
22419
+ >${this._editing ? A : this._getDisplayText()}</div>`;
22420
+ }
22421
+ updated(changed) {
22422
+ super.updated(changed);
22423
+ if (changed.has(`_editing`) && this._editing) {
22424
+ const el = this._contentEl;
22425
+ if (el) {
22426
+ el.textContent = this.value;
22427
+ el.focus();
22428
+ _selectAll(el);
22429
+ }
22430
+ }
22431
+ }
21799
22432
  static {
21800
22433
  this.styles = [i$7`
21801
22434
  :host {
@@ -21908,6 +22541,13 @@ __decorate([n$2({ attribute: false })], EditableLabel.prototype, "validator", vo
21908
22541
  __decorate([n$2({ attribute: false })], EditableLabel.prototype, "formatter", void 0);
21909
22542
  __decorate([r$1()], EditableLabel.prototype, "_editing", void 0);
21910
22543
  EditableLabel = __decorate([safeCustomElement(`ixfx-editable-label`)], EditableLabel);
22544
+ function _selectAll(el) {
22545
+ const range = document.createRange();
22546
+ range.selectNodeContents(el);
22547
+ const sel = window.getSelection();
22548
+ sel?.removeAllRanges();
22549
+ sel?.addRange(range);
22550
+ }
21911
22551
  //#endregion
21912
22552
  //#region src/keyboard.ts
21913
22553
  /**
@@ -22509,7 +23149,7 @@ EditableNumber = __decorate([safeCustomElement(`ixfx-editable-number`)], Editabl
22509
23149
  * @license
22510
23150
  * Copyright 2020 Google LLC
22511
23151
  * SPDX-License-Identifier: BSD-3-Clause
22512
- */ const { I: t$1 } = j$1, i$1 = (o) => o, s = () => document.createComment(""), v = (o, n, e) => {
23152
+ */ const { I: t$1 } = j$1, i$2 = (o) => o, s = () => document.createComment(""), v = (o, n, e) => {
22513
23153
  const l = o._$AA.parentNode, d = void 0 === n ? o._$AB : n._$AA;
22514
23154
  if (void 0 === e) e = new t$1(l.insertBefore(s(), d), l.insertBefore(s(), d), o, o.options);
22515
23155
  else {
@@ -22521,8 +23161,8 @@ EditableNumber = __decorate([safeCustomElement(`ixfx-editable-number`)], Editabl
22521
23161
  if (t !== d || c) {
22522
23162
  let o = e._$AA;
22523
23163
  for (; o !== t;) {
22524
- const t = i$1(o).nextSibling;
22525
- i$1(l).insertBefore(o, d), o = t;
23164
+ const t = i$2(o).nextSibling;
23165
+ i$2(l).insertBefore(o, d), o = t;
22526
23166
  }
22527
23167
  }
22528
23168
  }
@@ -22591,6 +23231,37 @@ const u = (e, s, t) => {
22591
23231
  }
22592
23232
  });
22593
23233
  //#endregion
23234
+ //#region node_modules/.pnpm/lit-html@3.3.3/node_modules/lit-html/directives/style-map.js
23235
+ /**
23236
+ * @license
23237
+ * Copyright 2018 Google LLC
23238
+ * SPDX-License-Identifier: BSD-3-Clause
23239
+ */ const n = "important", i$1 = " !" + n, o = e$3(class extends i$3 {
23240
+ constructor(t) {
23241
+ if (super(t), t.type !== t$2.ATTRIBUTE || "style" !== t.name || t.strings?.length > 2) throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.");
23242
+ }
23243
+ render(t) {
23244
+ return Object.keys(t).reduce((e, r) => {
23245
+ const s = t[r];
23246
+ return null == s ? e : e + `${r = r.includes("-") ? r : r.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g, "-$&").toLowerCase()}:${s};`;
23247
+ }, "");
23248
+ }
23249
+ update(e, [r]) {
23250
+ const { style: s } = e.element;
23251
+ if (void 0 === this.ft) return this.ft = new Set(Object.keys(r)), this.render(r);
23252
+ for (const t of this.ft) r[t] ?? (this.ft.delete(t), t.includes("-") ? s.removeProperty(t) : s[t] = null);
23253
+ for (const t in r) {
23254
+ const e = r[t];
23255
+ if (null != e) {
23256
+ this.ft.add(t);
23257
+ const r = "string" == typeof e && e.endsWith(i$1);
23258
+ t.includes("-") || r ? s.setProperty(t, r ? e.slice(0, -11) : e, r ? n : "") : s[t] = e;
23259
+ }
23260
+ }
23261
+ return E;
23262
+ }
23263
+ });
23264
+ //#endregion
22594
23265
  //#region src/hex/colour.ts
22595
23266
  const WHITESPACE_BYTES = new Set([
22596
23267
  9,
@@ -22600,45 +23271,45 @@ const WHITESPACE_BYTES = new Set([
22600
23271
  13,
22601
23272
  32
22602
23273
  ]);
22603
- const isControlByte = (byte) => {
23274
+ function isControlByte(byte) {
22604
23275
  return byte < 32 && byte !== 9 && byte !== 10 && byte !== 11 && byte !== 12 && byte !== 13;
22605
- };
22606
- const isPrintableAscii = (byte) => {
23276
+ }
23277
+ function isPrintableAscii(byte) {
22607
23278
  return byte >= 32 && byte <= 126;
22608
- };
22609
- const getAsciiClass = (byte) => {
22610
- if (byte === 0) return "byte-null";
22611
- if (WHITESPACE_BYTES.has(byte)) return "byte-whitespace";
22612
- if (isControlByte(byte)) return "byte-control";
22613
- if (isPrintableAscii(byte)) return "byte-printable";
22614
- return "byte-other";
22615
- };
22616
- const createShadeMap = (nullColour, maxColour, stepStart, stepEnd) => {
23279
+ }
23280
+ function getAsciiClass(byte) {
23281
+ if (byte === 0) return `byte-null`;
23282
+ if (WHITESPACE_BYTES.has(byte)) return `byte-whitespace`;
23283
+ if (isControlByte(byte)) return `byte-control`;
23284
+ if (isPrintableAscii(byte)) return `byte-printable`;
23285
+ return `byte-other`;
23286
+ }
23287
+ function createShadeMap(nullColour, maxColour, stepStart, stepEnd) {
22617
23288
  const cssSteps = colour_exports.createSteps(stepStart, stepEnd, { steps: 16 }).map(colour_exports.toCssColour);
22618
23289
  return { forByte: (byte) => {
22619
23290
  if (byte === 0) return nullColour;
22620
23291
  if (byte === 255) return maxColour;
22621
23292
  return cssSteps[byte >> 4];
22622
23293
  } };
22623
- };
23294
+ }
22624
23295
  //#endregion
22625
23296
  //#region src/hex/data.ts
22626
- const createHexStore = (data) => {
23297
+ function createHexStore(data) {
22627
23298
  return {
22628
23299
  data: new Uint8Array(data),
22629
23300
  changeset: /* @__PURE__ */ new Set()
22630
23301
  };
22631
- };
22632
- const readByte = (store, pos) => {
23302
+ }
23303
+ function readByte(store, pos) {
22633
23304
  return store.data[pos] ?? 0;
22634
- };
22635
- const length = (store) => {
23305
+ }
23306
+ function length(store) {
22636
23307
  return store.data.length;
22637
- };
22638
- const clamp$1 = (value) => {
23308
+ }
23309
+ function clamp$1(value) {
22639
23310
  return Math.max(0, Math.min(255, value));
22640
- };
22641
- const writeByte = (store, pos, value) => {
23311
+ }
23312
+ function writeByte(store, pos, value) {
22642
23313
  const clampedValue = clamp$1(value);
22643
23314
  const before = store.data[pos];
22644
23315
  const diff = new Map([[pos, {
@@ -22648,8 +23319,8 @@ const writeByte = (store, pos, value) => {
22648
23319
  store.data[pos] = clampedValue;
22649
23320
  store.changeset.add(pos);
22650
23321
  return diff;
22651
- };
22652
- const writeBytes = (store, entries) => {
23322
+ }
23323
+ function writeBytes(store, entries) {
22653
23324
  const diff = /* @__PURE__ */ new Map();
22654
23325
  entries.forEach((value, pos) => {
22655
23326
  const clampedValue = clamp$1(value);
@@ -22662,8 +23333,8 @@ const writeBytes = (store, entries) => {
22662
23333
  store.changeset.add(pos);
22663
23334
  });
22664
23335
  return diff;
22665
- };
22666
- const applyDiff = (store, diff) => {
23336
+ }
23337
+ function applyDiff(store, diff) {
22667
23338
  const reverse = /* @__PURE__ */ new Map();
22668
23339
  diff.forEach((entry, pos) => {
22669
23340
  const before = store.data[pos];
@@ -22675,8 +23346,8 @@ const applyDiff = (store, diff) => {
22675
23346
  store.changeset.add(pos);
22676
23347
  });
22677
23348
  return reverse;
22678
- };
22679
- const reverseDiff = (diff) => {
23349
+ }
23350
+ function reverseDiff(diff) {
22680
23351
  const reversed = /* @__PURE__ */ new Map();
22681
23352
  diff.forEach((entry, pos) => {
22682
23353
  reversed.set(pos, {
@@ -22685,52 +23356,52 @@ const reverseDiff = (diff) => {
22685
23356
  });
22686
23357
  });
22687
23358
  return reversed;
22688
- };
22689
- const getChangeset = (store) => {
23359
+ }
23360
+ function getChangeset(store) {
22690
23361
  return store.changeset;
22691
- };
23362
+ }
22692
23363
  //#endregion
22693
23364
  //#region src/hex/diff.ts
22694
- const createUndoRedoStack = () => {
23365
+ function createUndoRedoStack() {
22695
23366
  return {
22696
23367
  undos: [],
22697
23368
  redos: []
22698
23369
  };
22699
- };
22700
- const push = (stack, diff) => {
23370
+ }
23371
+ function push(stack, diff) {
22701
23372
  stack.undos.push(diff);
22702
23373
  stack.redos = [];
22703
- };
22704
- const undo = (stack) => {
23374
+ }
23375
+ function undo(stack) {
22705
23376
  const diff = stack.undos.pop();
22706
23377
  if (diff) stack.redos.push(diff);
22707
23378
  return diff;
22708
- };
22709
- const redo = (stack) => {
23379
+ }
23380
+ function redo(stack) {
22710
23381
  const diff = stack.redos.pop();
22711
23382
  if (diff) stack.undos.push(diff);
22712
23383
  return diff;
22713
- };
23384
+ }
22714
23385
  //#endregion
22715
23386
  //#region src/hex/selection.ts
22716
23387
  const bytesPerRow = (columns) => columns * 4;
22717
23388
  const min = (a, b) => a < b ? a : b;
22718
23389
  const max = (a, b) => a > b ? a : b;
22719
- const clampPos = (pos, dataLength) => {
23390
+ function clampPos(pos, dataLength) {
22720
23391
  return Math.max(0, Math.min(pos, dataLength - 1));
22721
- };
22722
- const getSelectedPositions = (sel, columns) => {
23392
+ }
23393
+ function getSelectedPositions(sel, columns) {
22723
23394
  if (!sel) return [];
22724
23395
  const bpr = bytesPerRow(columns);
22725
- if (sel.kind === "single") return [sel.pos];
22726
- if (sel.kind === "contiguous") {
23396
+ if (sel.kind === `single`) return [sel.pos];
23397
+ if (sel.kind === `contiguous`) {
22727
23398
  const start = min(sel.start, sel.end);
22728
23399
  const end = max(sel.start, sel.end);
22729
23400
  const positions = [];
22730
23401
  for (let i = start; i <= end; i++) positions.push(i);
22731
23402
  return positions;
22732
23403
  }
22733
- if (sel.kind === "rectangular") {
23404
+ if (sel.kind === `rectangular`) {
22734
23405
  const positions = [];
22735
23406
  for (let row = 0; row < sel.height; row++) for (let col = 0; col < sel.width; col++) {
22736
23407
  const y = sel.y + row;
@@ -22741,17 +23412,17 @@ const getSelectedPositions = (sel, columns) => {
22741
23412
  return positions;
22742
23413
  }
22743
23414
  return [];
22744
- };
22745
- const selectionContains = (sel, pos, columns) => {
23415
+ }
23416
+ function selectionContains(sel, pos, columns) {
22746
23417
  if (!sel) return false;
22747
23418
  const bpr = bytesPerRow(columns);
22748
- if (sel.kind === "single") return pos === sel.pos;
22749
- if (sel.kind === "contiguous") {
23419
+ if (sel.kind === `single`) return pos === sel.pos;
23420
+ if (sel.kind === `contiguous`) {
22750
23421
  const start = min(sel.start, sel.end);
22751
23422
  const end = max(sel.start, sel.end);
22752
23423
  return pos >= start && pos <= end;
22753
23424
  }
22754
- if (sel.kind === "rectangular") {
23425
+ if (sel.kind === `rectangular`) {
22755
23426
  const row = Math.floor(pos / bpr);
22756
23427
  const col = pos % bpr;
22757
23428
  const x = sel.x;
@@ -22761,59 +23432,59 @@ const selectionContains = (sel, pos, columns) => {
22761
23432
  return row >= y && row < y + height && col >= x && col < x + width;
22762
23433
  }
22763
23434
  return false;
22764
- };
22765
- const getCursorPos = (sel, dir, columns, dataLength) => {
23435
+ }
23436
+ function getCursorPos(sel, dir, columns, dataLength) {
22766
23437
  let currentPos;
22767
23438
  if (!sel) currentPos = 0;
22768
- else if (sel.kind === "single") currentPos = sel.pos;
22769
- else if (sel.kind === "contiguous") currentPos = max(sel.start, sel.end);
22770
- else if (sel.kind === "rectangular") currentPos = sel.y * bytesPerRow(columns) + sel.x + sel.width - 1;
23439
+ else if (sel.kind === `single`) currentPos = sel.pos;
23440
+ else if (sel.kind === `contiguous`) currentPos = max(sel.start, sel.end);
23441
+ else if (sel.kind === `rectangular`) currentPos = sel.y * bytesPerRow(columns) + sel.x + sel.width - 1;
22771
23442
  else currentPos = 0;
22772
23443
  const bpr = bytesPerRow(columns);
22773
23444
  let newPos;
22774
23445
  switch (dir) {
22775
- case "left":
23446
+ case `left`:
22776
23447
  newPos = currentPos - 1;
22777
23448
  break;
22778
- case "right":
23449
+ case `right`:
22779
23450
  newPos = currentPos + 1;
22780
23451
  break;
22781
- case "up":
23452
+ case `up`:
22782
23453
  newPos = currentPos - bpr;
22783
23454
  break;
22784
- case "down":
23455
+ case `down`:
22785
23456
  newPos = currentPos + bpr;
22786
23457
  break;
22787
23458
  default: newPos = currentPos;
22788
23459
  }
22789
23460
  return clampPos(newPos, dataLength);
22790
- };
22791
- const moveSelection = (sel, dir, columns, dataLength) => {
23461
+ }
23462
+ function moveSelection(sel, dir, columns, dataLength) {
22792
23463
  return {
22793
- kind: "single",
23464
+ kind: `single`,
22794
23465
  pos: getCursorPos(sel, dir, columns, dataLength)
22795
23466
  };
22796
- };
22797
- const extendRectangular = (sel, dir, columns, _dataLength, anchorX = 0, anchorY = 0) => {
23467
+ }
23468
+ function extendRectangular(sel, dir, columns, _dataLength, anchorX = 0, anchorY = 0) {
22798
23469
  const bpr = bytesPerRow(columns);
22799
23470
  let x;
22800
23471
  let y;
22801
23472
  let width;
22802
23473
  let height;
22803
- if (!sel || sel.kind === "single") {
22804
- const pos = sel?.kind === "single" ? sel.pos : 0;
23474
+ if (!sel || sel.kind === `single`) {
23475
+ const pos = sel?.kind === `single` ? sel.pos : 0;
22805
23476
  x = pos % bpr;
22806
23477
  y = Math.floor(pos / bpr);
22807
23478
  width = 1;
22808
23479
  height = 1;
22809
- } else if (sel.kind === "contiguous") {
23480
+ } else if (sel.kind === `contiguous`) {
22810
23481
  const start = min(sel.start, sel.end);
22811
23482
  const end = max(sel.start, sel.end);
22812
23483
  x = start % bpr;
22813
23484
  y = Math.floor(start / bpr);
22814
23485
  width = end - start + 1;
22815
23486
  height = 1;
22816
- } else if (sel.kind === "rectangular") {
23487
+ } else if (sel.kind === `rectangular`) {
22817
23488
  x = sel.x;
22818
23489
  y = sel.y;
22819
23490
  width = sel.width;
@@ -22824,10 +23495,10 @@ const extendRectangular = (sel, dir, columns, _dataLength, anchorX = 0, anchorY
22824
23495
  width = 1;
22825
23496
  height = 1;
22826
23497
  }
22827
- const effectiveAnchorX = anchorX > 0 || sel?.kind === "rectangular" ? anchorX : x;
22828
- const effectiveAnchorY = anchorY > 0 || sel?.kind === "rectangular" ? anchorY : y;
23498
+ const effectiveAnchorX = anchorX > 0 || sel?.kind === `rectangular` ? anchorX : x;
23499
+ const effectiveAnchorY = anchorY > 0 || sel?.kind === `rectangular` ? anchorY : y;
22829
23500
  switch (dir) {
22830
- case "left":
23501
+ case `left`:
22831
23502
  if (x > effectiveAnchorX) width -= 1;
22832
23503
  else if (x === effectiveAnchorX && width > 1) {
22833
23504
  x -= 1;
@@ -22837,10 +23508,10 @@ const extendRectangular = (sel, dir, columns, _dataLength, anchorX = 0, anchorY
22837
23508
  width += 1;
22838
23509
  }
22839
23510
  break;
22840
- case "right":
23511
+ case `right`:
22841
23512
  if (x + width < bpr) width += 1;
22842
23513
  break;
22843
- case "up":
23514
+ case `up`:
22844
23515
  if (y > effectiveAnchorY) height -= 1;
22845
23516
  else if (y === effectiveAnchorY && height > 1) {
22846
23517
  y -= 1;
@@ -22850,22 +23521,22 @@ const extendRectangular = (sel, dir, columns, _dataLength, anchorX = 0, anchorY
22850
23521
  height += 1;
22851
23522
  }
22852
23523
  break;
22853
- case "down":
23524
+ case `down`:
22854
23525
  height += 1;
22855
23526
  break;
22856
23527
  }
22857
23528
  return {
22858
- kind: "rectangular",
23529
+ kind: `rectangular`,
22859
23530
  x,
22860
23531
  y,
22861
23532
  width,
22862
23533
  height
22863
23534
  };
22864
- };
22865
- const selectionFromPointerRange = (start, end, mode, columns) => {
23535
+ }
23536
+ function selectionFromPointerRange(start, end, mode, columns) {
22866
23537
  const bpr = bytesPerRow(columns);
22867
- if (mode === "contiguous") return {
22868
- kind: "contiguous",
23538
+ if (mode === `contiguous`) return {
23539
+ kind: `contiguous`,
22869
23540
  start: min(start, end),
22870
23541
  end: max(start, end)
22871
23542
  };
@@ -22874,13 +23545,13 @@ const selectionFromPointerRange = (start, end, mode, columns) => {
22874
23545
  const endX = end % bpr;
22875
23546
  const endY = Math.floor(end / bpr);
22876
23547
  return {
22877
- kind: "rectangular",
23548
+ kind: `rectangular`,
22878
23549
  x: min(startX, endX),
22879
23550
  y: min(startY, endY),
22880
23551
  width: Math.abs(endX - startX) + 1,
22881
23552
  height: Math.abs(endY - startY) + 1
22882
23553
  };
22883
- };
23554
+ }
22884
23555
  //#endregion
22885
23556
  //#region src/hex/hex-editor.ts
22886
23557
  let IxfxHexEditorElement = class IxfxHexEditorElement extends i$4 {
@@ -23675,7 +24346,7 @@ let IxfxHexEditorElement = class IxfxHexEditorElement extends i$4 {
23675
24346
  return b`
23676
24347
  <span
23677
24348
  class=${classes.join(` `)}
23678
- style=${o$1(colourStyle || {})}
24349
+ style=${o(colourStyle || {})}
23679
24350
  @pointerdown=${(e) => this._onPointerDown(pos, `hex`, e)}
23680
24351
  @pointerup=${(e) => this._onPointerUp(pos, `hex`, e)}
23681
24352
  @pointermove=${(e) => this._onPointerMove(pos, `hex`, e)}
@@ -23698,7 +24369,7 @@ let IxfxHexEditorElement = class IxfxHexEditorElement extends i$4 {
23698
24369
  return b`
23699
24370
  <span
23700
24371
  class=${classes.join(` `)}
23701
- style=${o$1(colourStyle || {})}
24372
+ style=${o(colourStyle || {})}
23702
24373
  @pointerdown=${(e) => this._onPointerDown(pos, `text`, e)}
23703
24374
  @pointerup=${(e) => this._onPointerUp(pos, `text`, e)}
23704
24375
  @pointermove=${(e) => this._onPointerMove(pos, `text`, e)}
@@ -23774,10 +24445,10 @@ function getFlattenedCircleAngles(flatten) {
23774
24445
  function valueToAngle(relativeValue, valueMode, flatten) {
23775
24446
  const { startAngle, endAngle, totalArc } = getFlattenedCircleAngles(flatten);
23776
24447
  switch (valueMode) {
23777
- case "range": return startAngle + relativeValue * totalArc;
23778
- case "bipolar": if (relativeValue <= .5) return startAngle + relativeValue / .5 * (MIDPOINT_ANGLE - startAngle);
24448
+ case `range`: return startAngle + relativeValue * totalArc;
24449
+ case `bipolar`: if (relativeValue <= .5) return startAngle + relativeValue / .5 * (MIDPOINT_ANGLE - startAngle);
23779
24450
  else return MIDPOINT_ANGLE + (relativeValue - .5) / .5 * (endAngle - MIDPOINT_ANGLE);
23780
- case "unipolar": return MIDPOINT_ANGLE;
24451
+ case `unipolar`: return MIDPOINT_ANGLE;
23781
24452
  default: return startAngle;
23782
24453
  }
23783
24454
  }
@@ -23791,11 +24462,11 @@ function valueToAngle(relativeValue, valueMode, flatten) {
23791
24462
  function getValueArcAngles(relativeValue, valueMode, flatten) {
23792
24463
  const { startAngle, endAngle, totalArc } = getFlattenedCircleAngles(flatten);
23793
24464
  switch (valueMode) {
23794
- case "range": return {
24465
+ case `range`: return {
23795
24466
  startAngle,
23796
24467
  endAngle: startAngle + relativeValue * totalArc
23797
24468
  };
23798
- case "bipolar": if (relativeValue < .5) return {
24469
+ case `bipolar`: if (relativeValue < .5) return {
23799
24470
  startAngle: startAngle + relativeValue / .5 * (MIDPOINT_ANGLE - startAngle),
23800
24471
  endAngle: MIDPOINT_ANGLE
23801
24472
  };
@@ -23807,7 +24478,7 @@ function getValueArcAngles(relativeValue, valueMode, flatten) {
23807
24478
  startAngle: MIDPOINT_ANGLE,
23808
24479
  endAngle: MIDPOINT_ANGLE
23809
24480
  };
23810
- case "unipolar": {
24481
+ case `unipolar`: {
23811
24482
  const spreadAmount = relativeValue * totalArc / 2;
23812
24483
  return {
23813
24484
  startAngle: MIDPOINT_ANGLE - spreadAmount,
@@ -23858,7 +24529,7 @@ function generateArcPath(startAngle, endAngle, radius, center = {
23858
24529
  x: 50,
23859
24530
  y: 50
23860
24531
  }) {
23861
- if (startAngle === endAngle) return "";
24532
+ if (startAngle === endAngle) return ``;
23862
24533
  let arcLength = endAngle - startAngle;
23863
24534
  if (arcLength < 0) arcLength += PI2;
23864
24535
  if (arcLength >= PI2 - .001) {
@@ -23899,7 +24570,7 @@ function generateWedgePath(startAngle, endAngle, radius, center = {
23899
24570
  x: 50,
23900
24571
  y: 50
23901
24572
  }, clockwise = true) {
23902
- if (startAngle === endAngle) return "";
24573
+ if (startAngle === endAngle) return ``;
23903
24574
  let arcLength = endAngle - startAngle;
23904
24575
  if (arcLength < 0) arcLength += PI2;
23905
24576
  if (arcLength >= PI2 - .001) {
@@ -23935,12 +24606,12 @@ function getWedgeAngles(relativeValue, valueMode, flatten) {
23935
24606
  const { startAngle, endAngle, totalArc } = getFlattenedCircleAngles(flatten);
23936
24607
  const valueAngle = valueToAngle(relativeValue, valueMode, flatten);
23937
24608
  switch (valueMode) {
23938
- case "range": return {
24609
+ case `range`: return {
23939
24610
  startAngle,
23940
24611
  endAngle: valueAngle,
23941
24612
  clockwise: true
23942
24613
  };
23943
- case "bipolar": if (relativeValue < .5) return {
24614
+ case `bipolar`: if (relativeValue < .5) return {
23944
24615
  startAngle: MIDPOINT_ANGLE,
23945
24616
  endAngle: valueAngle,
23946
24617
  clockwise: false
@@ -23950,7 +24621,7 @@ function getWedgeAngles(relativeValue, valueMode, flatten) {
23950
24621
  endAngle: valueAngle,
23951
24622
  clockwise: true
23952
24623
  };
23953
- case "unipolar": {
24624
+ case `unipolar`: {
23954
24625
  const halfSpread = relativeValue * totalArc / 2;
23955
24626
  return {
23956
24627
  startAngle: MIDPOINT_ANGLE - halfSpread,
@@ -25091,32 +25762,6 @@ var MenuCommandController = class {
25091
25762
  }
25092
25763
  };
25093
25764
  //#endregion
25094
- //#region src/menu/menu-header.ts
25095
- let MenuHeader = class MenuHeader extends i$4 {
25096
- constructor(..._args) {
25097
- super(..._args);
25098
- this.label = ``;
25099
- }
25100
- static {
25101
- this.styles = i$7`
25102
- :host {
25103
- display: block;
25104
- padding: 6px var(--space-m) var(--space-xs);
25105
- font-size: var(--text-s);
25106
- font-weight: var(--font-weight-bold);
25107
- text-transform: uppercase;
25108
- color: var(--surface-muted-text);
25109
- letter-spacing: 0.5px;
25110
- }
25111
- `;
25112
- }
25113
- render() {
25114
- return b`<div>${this.label}</div>`;
25115
- }
25116
- };
25117
- __decorate([n$2({ type: String })], MenuHeader.prototype, "label", void 0);
25118
- MenuHeader = __decorate([safeCustomElement(`ixfx-menu-header`)], MenuHeader);
25119
- //#endregion
25120
25765
  //#region src/menu/menu-sub-trigger.ts
25121
25766
  var _MenuSubTrigger;
25122
25767
  let MenuSubTrigger = class MenuSubTrigger extends i$4 {
@@ -25395,6 +26040,32 @@ let MenuSeparator = class MenuSeparator extends i$4 {
25395
26040
  };
25396
26041
  MenuSeparator = __decorate([safeCustomElement(`ixfx-menu-separator`)], MenuSeparator);
25397
26042
  //#endregion
26043
+ //#region src/menu/menu-header.ts
26044
+ let MenuHeader = class MenuHeader extends i$4 {
26045
+ constructor(..._args) {
26046
+ super(..._args);
26047
+ this.label = ``;
26048
+ }
26049
+ static {
26050
+ this.styles = i$7`
26051
+ :host {
26052
+ display: block;
26053
+ padding: 6px var(--space-m) var(--space-xs);
26054
+ font-size: var(--text-s);
26055
+ font-weight: var(--font-weight-bold);
26056
+ text-transform: uppercase;
26057
+ color: var(--surface-muted-text);
26058
+ letter-spacing: 0.5px;
26059
+ }
26060
+ `;
26061
+ }
26062
+ render() {
26063
+ return b`<div>${this.label}</div>`;
26064
+ }
26065
+ };
26066
+ __decorate([n$2({ type: String })], MenuHeader.prototype, "label", void 0);
26067
+ MenuHeader = __decorate([safeCustomElement(`ixfx-menu-header`)], MenuHeader);
26068
+ //#endregion
25398
26069
  //#region src/menu/menu-from-tree.ts
25399
26070
  function isLeaf(node) {
25400
26071
  return node.children === void 0 || node.children.length === 0;
@@ -25403,8 +26074,8 @@ function menuFromTree(nodes, options = {}) {
25403
26074
  const { onCommand, registry, keyboardManager } = options;
25404
26075
  return b`${nodes.map((node) => {
25405
26076
  const label = node.item.label;
25406
- if (label === "-") return b`<ixfx-menu-separator></ixfx-menu-separator>`;
25407
- if (label.startsWith("# ")) return b`<ixfx-menu-header label="${label.slice(2)}"></ixfx-menu-header>`;
26077
+ if (label === `-`) return b`<ixfx-menu-separator></ixfx-menu-separator>`;
26078
+ if (label.startsWith(`# `)) return b`<ixfx-menu-header label="${label.slice(2)}"></ixfx-menu-header>`;
25408
26079
  if (!isLeaf(node)) {
25409
26080
  const submenu = menuFromTree(node.children, options);
25410
26081
  return b`
@@ -25430,30 +26101,30 @@ function menuFromTree(nodes, options = {}) {
25430
26101
  function buildMenuItemEl(node, options) {
25431
26102
  const { onCommand, registry } = options;
25432
26103
  const label = node.item.label;
25433
- const el = document.createElement("ixfx-menu-item");
25434
- el.setAttribute("command", node.item.key);
25435
- el.setAttribute("label", label);
25436
- if (node.item.icon) el.setAttribute("icon-name", node.item.icon);
25437
- if (onCommand) el.addEventListener("command", (e) => onCommand(e.detail));
26104
+ const el = document.createElement(`ixfx-menu-item`);
26105
+ el.setAttribute(`command`, node.item.key);
26106
+ el.setAttribute(`label`, label);
26107
+ if (node.item.icon) el.setAttribute(`icon-name`, node.item.icon);
26108
+ if (onCommand) el.addEventListener(`command`, (e) => onCommand(e.detail));
25438
26109
  if (registry) el.registry = registry;
25439
26110
  return el;
25440
26111
  }
25441
26112
  function buildMenuSeparatorEl() {
25442
- return document.createElement("ixfx-menu-separator");
26113
+ return document.createElement(`ixfx-menu-separator`);
25443
26114
  }
25444
26115
  function buildMenuHeaderEl(label) {
25445
- const el = document.createElement("ixfx-menu-header");
25446
- el.setAttribute("label", label);
26116
+ const el = document.createElement(`ixfx-menu-header`);
26117
+ el.setAttribute(`label`, label);
25447
26118
  return el;
25448
26119
  }
25449
26120
  function buildMenuSubTriggerEl(node, options) {
25450
26121
  const { onCommand, registry, keyboardManager } = options;
25451
- const el = document.createElement("ixfx-menu-sub-trigger");
25452
- el.setAttribute("label", node.item.label);
26122
+ const el = document.createElement(`ixfx-menu-sub-trigger`);
26123
+ el.setAttribute(`label`, node.item.label);
25453
26124
  if (registry) el.registry = registry;
25454
26125
  if (keyboardManager) el.keyboardManager = keyboardManager;
25455
- const container = document.createElement("ixfx-menu-container");
25456
- container.setAttribute("slot", "submenu");
26126
+ const container = document.createElement(`ixfx-menu-container`);
26127
+ container.setAttribute(`slot`, `submenu`);
25457
26128
  if (registry) container.registry = registry;
25458
26129
  if (keyboardManager) container.keyboardManager = keyboardManager;
25459
26130
  const childEls = menuFromTreeToElements(node.children, options);
@@ -25464,8 +26135,8 @@ function buildMenuSubTriggerEl(node, options) {
25464
26135
  function menuFromTreeToElements(nodes, options = {}) {
25465
26136
  return nodes.map((node) => {
25466
26137
  const label = node.item.label;
25467
- if (label === "-") return buildMenuSeparatorEl();
25468
- if (label.startsWith("# ")) return buildMenuHeaderEl(label.slice(2));
26138
+ if (label === `-`) return buildMenuSeparatorEl();
26139
+ if (label.startsWith(`# `)) return buildMenuHeaderEl(label.slice(2));
25469
26140
  if (!isLeaf(node)) return buildMenuSubTriggerEl(node, options);
25470
26141
  return buildMenuItemEl(node, options);
25471
26142
  });
@@ -25645,8 +26316,8 @@ let MillerBaseElement = class MillerBaseElement extends i$4 {
25645
26316
  super(..._args);
25646
26317
  this.headers = false;
25647
26318
  this.interactionMode = `standard`;
25648
- this.selectionFilter = "leaf";
25649
- this.exclusivity = "none";
26319
+ this.selectionFilter = `leaf`;
26320
+ this.exclusivity = `none`;
25650
26321
  this._columns = [];
25651
26322
  this._selectedByDepth = /* @__PURE__ */ new Map();
25652
26323
  this._selectedKeys = /* @__PURE__ */ new Set();
@@ -25876,7 +26547,7 @@ let MillerBaseElement = class MillerBaseElement extends i$4 {
25876
26547
  #getTickledDepth() {
25877
26548
  const id = this.tickled.tickledId;
25878
26549
  if (!id) return void 0;
25879
- const colonIndex = id.indexOf(":");
26550
+ const colonIndex = id.indexOf(`:`);
25880
26551
  if (colonIndex < 0) return void 0;
25881
26552
  return Number(id.substring(0, colonIndex));
25882
26553
  }
@@ -25898,10 +26569,10 @@ let MillerBaseElement = class MillerBaseElement extends i$4 {
25898
26569
  if (model) {
25899
26570
  this.root = model.root;
25900
26571
  this.#modelUnsubscribe = model._subscribe((event) => {
25901
- if (event.type === "root") {
26572
+ if (event.type === `root`) {
25902
26573
  this.root = event.root;
25903
26574
  this.requestUpdate();
25904
- } else if (event.type === "invalidate") {
26575
+ } else if (event.type === `invalidate`) {
25905
26576
  for (let depth = 0; depth < this._columns.length - 1; depth++) if (this._columns[depth]?.some((n) => n.item.key === event.key)) {
25906
26577
  this._columns = this._columns.slice(0, depth + 1);
25907
26578
  this.requestUpdate();
@@ -26181,9 +26852,9 @@ let MillerBaseElement = class MillerBaseElement extends i$4 {
26181
26852
  };
26182
26853
  }
26183
26854
  #canSelect(node) {
26184
- if (this.selectionFilter === "none") return false;
26185
- if (this.selectionFilter === "leaf") return node.item.isLeaf !== false;
26186
- if (this.selectionFilter === "branch") return node.item.isLeaf === false || node.children !== void 0 && node.children.length > 0;
26855
+ if (this.selectionFilter === `none`) return false;
26856
+ if (this.selectionFilter === `leaf`) return node.item.isLeaf !== false;
26857
+ if (this.selectionFilter === `branch`) return node.item.isLeaf === false || node.children !== void 0 && node.children.length > 0;
26187
26858
  return true;
26188
26859
  }
26189
26860
  #dispatchSelectEvent(previous) {
@@ -26420,7 +27091,7 @@ let MillerBaseElement = class MillerBaseElement extends i$4 {
26420
27091
  #getTickledNodeAndDepth() {
26421
27092
  const id = this.tickled.tickledId;
26422
27093
  if (!id) return void 0;
26423
- const colonIndex = id.indexOf(":");
27094
+ const colonIndex = id.indexOf(`:`);
26424
27095
  if (colonIndex < 0) return void 0;
26425
27096
  const depth = Number(id.substring(0, colonIndex));
26426
27097
  const key = id.substring(colonIndex + 1);
@@ -26553,7 +27224,7 @@ let MillerBaseElement = class MillerBaseElement extends i$4 {
26553
27224
  * Called by subclasses when pointer leaves the column container.
26554
27225
  */
26555
27226
  _handleColumnPointerLeave() {
26556
- this.tickled.handlePointerLeave(new PointerEvent("pointerleave"));
27227
+ this.tickled.handlePointerLeave(new PointerEvent(`pointerleave`));
26557
27228
  }
26558
27229
  static {
26559
27230
  this.styles = [tickledStyles, i$7`
@@ -26675,11 +27346,11 @@ __decorate([n$2({ type: Boolean })], MillerBaseElement.prototype, "headers", voi
26675
27346
  __decorate([n$2({ type: String })], MillerBaseElement.prototype, "interactionMode", void 0);
26676
27347
  __decorate([n$2({
26677
27348
  reflect: true,
26678
- attribute: "selection-filter"
27349
+ attribute: `selection-filter`
26679
27350
  })], MillerBaseElement.prototype, "selectionFilter", void 0);
26680
27351
  __decorate([n$2({
26681
27352
  reflect: true,
26682
- attribute: "exclusivity"
27353
+ attribute: `exclusivity`
26683
27354
  })], MillerBaseElement.prototype, "exclusivity", void 0);
26684
27355
  __decorate([n$2({ type: Object })], MillerBaseElement.prototype, "loadChildren", void 0);
26685
27356
  __decorate([n$2({ attribute: false })], MillerBaseElement.prototype, "filterPredicate", void 0);
@@ -28247,7 +28918,7 @@ let PlotSingleAxis = class PlotSingleAxis extends i$4 {
28247
28918
  this.paused = false;
28248
28919
  this.clickPauses = false;
28249
28920
  this.shaded = false;
28250
- this.areaColor = "";
28921
+ this.areaColor = ``;
28251
28922
  this.colorMode = `fixed`;
28252
28923
  this.colorEasing = ``;
28253
28924
  this.colorScale = ``;
@@ -28290,7 +28961,7 @@ let PlotSingleAxis = class PlotSingleAxis extends i$4 {
28290
28961
  #hasProgrammaticData;
28291
28962
  #parseDeclarativeData(text) {
28292
28963
  if (!text) return [];
28293
- return text.trim().split(/[,\s;]+/).map((t) => parseFloat(t)).filter((n) => !isNaN(n));
28964
+ return text.trim().split(/[,\s;]+/).map((t) => Number.parseFloat(t)).filter((n) => !isNaN(n));
28294
28965
  }
28295
28966
  #lastClientX;
28296
28967
  #lastClientY;
@@ -28321,7 +28992,7 @@ let PlotSingleAxis = class PlotSingleAxis extends i$4 {
28321
28992
  this._height = e.contentRect.height;
28322
28993
  }).observe(this);
28323
28994
  if (!this.#hasProgrammaticData) {
28324
- const data = this.#parseDeclarativeData(this.textContent ?? "");
28995
+ const data = this.#parseDeclarativeData(this.textContent ?? ``);
28325
28996
  if (data.length > 0) this.#series.set(data);
28326
28997
  }
28327
28998
  }
@@ -28503,9 +29174,9 @@ __decorate([n$2({
28503
29174
  })], PlotSingleAxis.prototype, "clickPauses", void 0);
28504
29175
  __decorate([n$2({
28505
29176
  type: Boolean,
28506
- attribute: "shaded"
29177
+ attribute: `shaded`
28507
29178
  })], PlotSingleAxis.prototype, "shaded", void 0);
28508
- __decorate([n$2({ attribute: "area-color" })], PlotSingleAxis.prototype, "areaColor", void 0);
29179
+ __decorate([n$2({ attribute: `area-color` })], PlotSingleAxis.prototype, "areaColor", void 0);
28509
29180
  __decorate([n$2({ attribute: `color-mode` })], PlotSingleAxis.prototype, "colorMode", void 0);
28510
29181
  __decorate([n$2({ attribute: `color-easing` })], PlotSingleAxis.prototype, "colorEasing", void 0);
28511
29182
  __decorate([n$2({ attribute: `color-scale` })], PlotSingleAxis.prototype, "colorScale", void 0);
@@ -31005,7 +31676,7 @@ let SplitLayoutElement = class SplitLayoutElement extends i$4 {
31005
31676
  const cursorZoneLocalY = event.clientY - zoneBounds.top;
31006
31677
  if (this.layout === `vertical` && nestedSplit.layout === `horizontal`) {
31007
31678
  let newOuterFraction = cursorZoneLocalX / outerBounds.width;
31008
- let newNestedFraction = cursorZoneLocalY / outerBounds.height;
31679
+ const newNestedFraction = cursorZoneLocalY / outerBounds.height;
31009
31680
  const { sizeA: constrainedA, sizeB: constrainedB } = this.#applyConstraints(`${newOuterFraction * 100}%`, `${(1 - newOuterFraction) * 100}%`, outerBounds.width);
31010
31681
  newOuterFraction = this.#parseSizeAsFraction(constrainedA, outerBounds.width);
31011
31682
  this.sizeA = constrainedA;
@@ -31021,7 +31692,7 @@ let SplitLayoutElement = class SplitLayoutElement extends i$4 {
31021
31692
  cornerZone.style.top = `${cursorZoneLocalY - 6}px`;
31022
31693
  }
31023
31694
  } else if (this.layout === `horizontal` && nestedSplit.layout === `vertical`) {
31024
- let newNestedFraction = cursorZoneLocalX / outerBounds.width;
31695
+ const newNestedFraction = cursorZoneLocalX / outerBounds.width;
31025
31696
  let newOuterFraction = cursorZoneLocalY / outerBounds.height;
31026
31697
  const { sizeA: constrainedA, sizeB: constrainedB } = this.#applyConstraints(`${newOuterFraction * 100}%`, `${(1 - newOuterFraction) * 100}%`, outerBounds.height);
31027
31698
  newOuterFraction = this.#parseSizeAsFraction(constrainedA, outerBounds.height);
@@ -31621,16 +32292,19 @@ let TabListItemElement = class TabListItemElement extends i$4 {
31621
32292
  <div>
31622
32293
  ${this.iconName ? b`<ixfx-icon name="${this.iconName}"></ixfx-icon>` : A}
31623
32294
  ${showLabel ? b`<span class="label"><slot></slot></span>` : A}
31624
- ${this.closeable ? b`
31625
- <button
31626
- class="close-btn"
31627
- type="button"
31628
- aria-label="Close tab"
31629
- @click=${this._onCloseClick}
31630
- >
31631
- <ixfx-icon name="close"></ixfx-icon>
31632
- </button>
31633
- ` : A}
32295
+ <div class="actions">
32296
+ <div class="toolbar"><slot name="toolbar"></slot></div>
32297
+ ${this.closeable ? b`
32298
+ <button
32299
+ class="close-btn"
32300
+ type="button"
32301
+ aria-label="Close tab"
32302
+ @click=${this._onCloseClick}
32303
+ >
32304
+ <ixfx-icon name="close"></ixfx-icon>
32305
+ </button>
32306
+ ` : A}
32307
+ </div>
31634
32308
  </div>
31635
32309
  `;
31636
32310
  }
@@ -31654,7 +32328,7 @@ let TabListItemElement = class TabListItemElement extends i$4 {
31654
32328
  box-sizing: border-box;
31655
32329
  }
31656
32330
 
31657
- div {
32331
+ :host > div {
31658
32332
  display: flex;
31659
32333
  align-items: center;
31660
32334
  gap: var(--space-s, 4px);
@@ -31685,6 +32359,28 @@ let TabListItemElement = class TabListItemElement extends i$4 {
31685
32359
  flex-shrink: 0;
31686
32360
  }
31687
32361
 
32362
+ .actions {
32363
+ display: flex;
32364
+ align-items: center;
32365
+ gap: var(--space-xs);
32366
+ margin-left: auto;
32367
+ flex-shrink: 0;
32368
+ }
32369
+
32370
+ .toolbar {
32371
+ display: none;
32372
+ align-items: center;
32373
+ gap: var(--space-xs);
32374
+ }
32375
+
32376
+ :host([selected]) .toolbar {
32377
+ display: flex;
32378
+ }
32379
+
32380
+ slot[name="toolbar"] {
32381
+ display: contents;
32382
+ }
32383
+
31688
32384
  .close-btn {
31689
32385
  display: inline-flex;
31690
32386
  align-items: center;
@@ -31692,7 +32388,6 @@ let TabListItemElement = class TabListItemElement extends i$4 {
31692
32388
  background: none;
31693
32389
  border: none;
31694
32390
  padding: var(--space-xs);
31695
- margin-left: auto;
31696
32391
  cursor: pointer;
31697
32392
  color: inherit;
31698
32393
  border-radius: var(--radius-s);
@@ -31798,7 +32493,7 @@ let TabListElement = class TabListElement extends i$4 {
31798
32493
  }
31799
32494
  updated(changedProperties) {
31800
32495
  super.updated(changedProperties);
31801
- this.classList.toggle("scrollable", this.scrollable);
32496
+ this.classList.toggle(`scrollable`, this.scrollable);
31802
32497
  }
31803
32498
  #syncIndicatorColors() {
31804
32499
  for (const item of this.#items) {
@@ -31971,14 +32666,17 @@ let TabPanelsElement = class TabPanelsElement extends i$4 {
31971
32666
  if (this.onSelectedPanel) this.onSelectedPanel(el.id, el);
31972
32667
  }
31973
32668
  syncWithList(el) {
32669
+ const apply = (t) => {
32670
+ if (!t) return;
32671
+ const forEl = t.getAttribute(`for`);
32672
+ if (forEl) this.selectPanel(forEl);
32673
+ };
31974
32674
  const t = el.getSelectedElement();
31975
32675
  if (!t) {
31976
- el.updateComplete.then(() => this.syncWithList(el));
32676
+ el.updateComplete.then(() => apply(el.getSelectedElement()));
31977
32677
  return;
31978
32678
  }
31979
- const forEl = t.getAttribute(`for`);
31980
- if (!forEl) return;
31981
- this.selectPanel(forEl);
32679
+ apply(t);
31982
32680
  }
31983
32681
  selectPanel(id) {
31984
32682
  const current = this.getSelectedElement();
@@ -32005,6 +32703,70 @@ let TabPanelsElement = class TabPanelsElement extends i$4 {
32005
32703
  __decorate([o$2()], TabPanelsElement.prototype, "_listItems", void 0);
32006
32704
  TabPanelsElement = __decorate([safeCustomElement(`ixfx-tab-panels`)], TabPanelsElement);
32007
32705
  //#endregion
32706
+ //#region src/timeline/timeline-controller.ts
32707
+ /**
32708
+ * External adapter class that can configure a timeline element
32709
+ * with musical time, custom label formatting, etc.
32710
+ *
32711
+ * The controller has no import of any timeline element type.
32712
+ * Users create a controller, pass it the component instance,
32713
+ * and the controller calls the component's public API.
32714
+ */
32715
+ var TimelineController = class {
32716
+ #host;
32717
+ #config;
32718
+ #bpm = 120;
32719
+ #timeSignatureNumerator = 4;
32720
+ #timeSignatureDenominator = 4;
32721
+ constructor(timeline, config) {
32722
+ this.#host = timeline;
32723
+ this.#config = config;
32724
+ timeline.min = config.logicalMin;
32725
+ timeline.max = config.logicalMax;
32726
+ if (config.labelFormatter) timeline.formatLabel = config.labelFormatter;
32727
+ timeline.addEventListener(`track-select`, this.#onTrackSelect);
32728
+ }
32729
+ /** Update the tempo (BPM) for musical time calculations. */
32730
+ setTempo(bpm) {
32731
+ this.#bpm = bpm;
32732
+ this.#updateFormatLabel();
32733
+ }
32734
+ /** Update the time signature for musical time calculations. */
32735
+ setTimeSignature(numerator, denominator) {
32736
+ this.#timeSignatureNumerator = numerator;
32737
+ this.#timeSignatureDenominator = denominator;
32738
+ this.#updateFormatLabel();
32739
+ }
32740
+ /** Set the visible view range on the timeline. */
32741
+ setViewRange(range) {
32742
+ this.#host.setViewRange(range);
32743
+ }
32744
+ /** Clean up any resources held by the controller. */
32745
+ dispose() {
32746
+ this.#host.removeEventListener(`track-select`, this.#onTrackSelect);
32747
+ }
32748
+ #onTrackSelect = (e) => {
32749
+ const detail = e.detail;
32750
+ const trackEventIds = this.#host.getTrackEvents(detail.trackKey).map((e) => e.id);
32751
+ if (detail.shiftKey) {
32752
+ const current = new Set(this.#host.selectedIds);
32753
+ for (const id of trackEventIds) if (current.has(id)) current.delete(id);
32754
+ else current.add(id);
32755
+ this.#host.selectedIds = [...current];
32756
+ } else this.#host.selectedIds = trackEventIds;
32757
+ };
32758
+ #updateFormatLabel() {
32759
+ if (this.#config.labelFormatter) return;
32760
+ this.#bpm;
32761
+ const num = this.#timeSignatureNumerator;
32762
+ this.#host.formatLabel = (v) => {
32763
+ const beatsPerBar = num;
32764
+ const totalBeats = v;
32765
+ return `${Math.floor(totalBeats / beatsPerBar) + 1}.${Math.floor(totalBeats % beatsPerBar) + 1}`;
32766
+ };
32767
+ }
32768
+ };
32769
+ //#endregion
32008
32770
  //#region src/timeline/timeline-draw.ts
32009
32771
  const NICE_STEPS = [
32010
32772
  1,
@@ -32015,15 +32777,15 @@ const NICE_STEPS = [
32015
32777
  /**
32016
32778
  * Dispatch to adaptive or fixed grid mode.
32017
32779
  */
32018
- const resolveGrid = (viewRange, userScale, viewportWidth, mode = `adaptive`, fixedMinorStep, ppu) => {
32780
+ function resolveGrid(viewRange, userScale, viewportWidth, mode = `adaptive`, fixedMinorStep, ppu) {
32019
32781
  if (mode === `fixed` && fixedMinorStep !== void 0 && ppu !== void 0) return resolveFixedGrid(fixedMinorStep, ppu);
32020
32782
  return resolveAdaptiveGrid(viewRange, userScale, viewportWidth);
32021
- };
32783
+ }
32022
32784
  /**
32023
32785
  * Compute a nice major/minor grid resolution for the current view.
32024
32786
  * Uses a 1-2-5-10 "nice number" algorithm.
32025
32787
  */
32026
- const resolveAdaptiveGrid = (viewRange, userScale, viewportWidth) => {
32788
+ function resolveAdaptiveGrid(viewRange, userScale, viewportWidth) {
32027
32789
  const span = viewRange.max - viewRange.min;
32028
32790
  if (span <= 0 || viewportWidth <= 0) return {
32029
32791
  majorStep: 100,
@@ -32032,7 +32794,7 @@ const resolveAdaptiveGrid = (viewRange, userScale, viewportWidth) => {
32032
32794
  };
32033
32795
  const targetPx = 80 * scaleMultiplier(userScale);
32034
32796
  const rawStep = span / Math.max(2, Math.floor(viewportWidth / targetPx));
32035
- const magnitude = Math.pow(10, Math.floor(Math.log10(rawStep)));
32797
+ const magnitude = 10 ** Math.floor(Math.log10(rawStep));
32036
32798
  let bestStep = magnitude;
32037
32799
  for (const n of NICE_STEPS) {
32038
32800
  const candidate = n * magnitude;
@@ -32049,14 +32811,14 @@ const resolveAdaptiveGrid = (viewRange, userScale, viewportWidth) => {
32049
32811
  minorDivisions,
32050
32812
  minorStep
32051
32813
  };
32052
- };
32814
+ }
32053
32815
  /**
32054
32816
  * Fixed grid: user specifies a base minor step in logical units.
32055
32817
  * If the minor step at current zoom would produce lines < 5px apart,
32056
32818
  * double the effective step until they're >= 5px.
32057
32819
  * Major = 4× effective minor.
32058
32820
  */
32059
- const resolveFixedGrid = (baseMinorStep, ppu) => {
32821
+ function resolveFixedGrid(baseMinorStep, ppu) {
32060
32822
  const MIN_PX_BETWEEN = 5;
32061
32823
  let effectiveMinor = baseMinorStep;
32062
32824
  while (effectiveMinor * ppu < MIN_PX_BETWEEN && effectiveMinor < 0xe8d4a51000) effectiveMinor *= 2;
@@ -32066,8 +32828,8 @@ const resolveFixedGrid = (baseMinorStep, ppu) => {
32066
32828
  minorDivisions,
32067
32829
  minorStep: effectiveMinor
32068
32830
  };
32069
- };
32070
- const scaleMultiplier = (scale) => {
32831
+ }
32832
+ function scaleMultiplier(scale) {
32071
32833
  switch (scale) {
32072
32834
  case `x-wide`: return 2;
32073
32835
  case `wide`: return 1.5;
@@ -32076,8 +32838,8 @@ const scaleMultiplier = (scale) => {
32076
32838
  case `x-slim`: return .5;
32077
32839
  default: return 1;
32078
32840
  }
32079
- };
32080
- const drawMajorMinorGrid = (ctx, region, resolution, altMajorBg, vars, ppu) => {
32841
+ }
32842
+ function drawMajorMinorGrid(ctx, region, resolution, altMajorBg, vars, ppu) {
32081
32843
  const { majorStep, minorStep } = resolution;
32082
32844
  if (majorStep <= 0) return;
32083
32845
  const regionEnd = region.x + region.width;
@@ -32091,7 +32853,7 @@ const drawMajorMinorGrid = (ctx, region, resolution, altMajorBg, vars, ppu) => {
32091
32853
  }
32092
32854
  if (minorStep > 0) {
32093
32855
  ctx.strokeStyle = vars[`--timeline-grid-minor-color`] ?? `rgba(255,255,255,0.05)`;
32094
- ctx.lineWidth = parseFloat(vars[`--timeline-grid-minor-width`] ?? `0.5`);
32856
+ ctx.lineWidth = Number.parseFloat(vars[`--timeline-grid-minor-width`] ?? `0.5`);
32095
32857
  ctx.beginPath();
32096
32858
  const firstMinor = Math.floor(region.x / minorStep) * minorStep;
32097
32859
  for (let val = firstMinor; val < regionEnd; val += minorStep) {
@@ -32103,7 +32865,7 @@ const drawMajorMinorGrid = (ctx, region, resolution, altMajorBg, vars, ppu) => {
32103
32865
  ctx.stroke();
32104
32866
  }
32105
32867
  ctx.strokeStyle = vars[`--timeline-grid-major-color`] ?? `rgba(255,255,255,0.12)`;
32106
- ctx.lineWidth = parseFloat(vars[`--timeline-grid-major-width`] ?? `1`);
32868
+ ctx.lineWidth = Number.parseFloat(vars[`--timeline-grid-major-width`] ?? `1`);
32107
32869
  ctx.beginPath();
32108
32870
  const firstMajor = Math.floor(region.x / majorStep) * majorStep;
32109
32871
  for (let val = firstMajor; val < regionEnd; val += majorStep) {
@@ -32112,13 +32874,13 @@ const drawMajorMinorGrid = (ctx, region, resolution, altMajorBg, vars, ppu) => {
32112
32874
  ctx.lineTo(px, region.y + region.height);
32113
32875
  }
32114
32876
  ctx.stroke();
32115
- };
32877
+ }
32116
32878
  /**
32117
32879
  * Draw the axis header band. With 2D virtualization the axis is "sticky" —
32118
32880
  * it is always drawn at the current vertical scroll position (`scrollTop`)
32119
32881
  * so it appears fixed at the viewport top.
32120
32882
  */
32121
- const drawAxis = (ctx, region, resolution, axisHeight, formatLabel, vars, ppu, scrollTop = 0) => {
32883
+ function drawAxis(ctx, region, resolution, axisHeight, formatLabel, vars, ppu, scrollTop = 0) {
32122
32884
  const { majorStep } = resolution;
32123
32885
  if (majorStep <= 0 || axisHeight <= 0) return;
32124
32886
  const axisTop = scrollTop;
@@ -32130,7 +32892,7 @@ const drawAxis = (ctx, region, resolution, axisHeight, formatLabel, vars, ppu, s
32130
32892
  const bgLeft = region.x * ppu;
32131
32893
  ctx.fillRect(bgLeft, axisTop, region.width * ppu, bgHeight);
32132
32894
  ctx.strokeStyle = vars[`--timeline-grid-major-color`] ?? `rgba(255,255,255,0.12)`;
32133
- ctx.lineWidth = parseFloat(vars[`--timeline-grid-major-width`] ?? `1`);
32895
+ ctx.lineWidth = Number.parseFloat(vars[`--timeline-grid-major-width`] ?? `1`);
32134
32896
  ctx.beginPath();
32135
32897
  const firstMajor = Math.ceil(region.x / majorStep) * majorStep;
32136
32898
  for (let val = firstMajor; val < regionEnd; val += majorStep) {
@@ -32155,8 +32917,8 @@ const drawAxis = (ctx, region, resolution, axisHeight, formatLabel, vars, ppu, s
32155
32917
  ctx.fillText(formatLabel(val), px + labelPadding, axisTop + bgHeight / 2);
32156
32918
  }
32157
32919
  ctx.textAlign = `start`;
32158
- };
32159
- const drawTrackBackgrounds = (ctx, region, trackLayout, tracks, vars, ppu) => {
32920
+ }
32921
+ function drawTrackBackgrounds(ctx, region, trackLayout, tracks, vars, ppu) {
32160
32922
  const bgDefault = vars[`--timeline-track-bg`] ?? `#1a1a1a`;
32161
32923
  const bgAlt = vars[`--timeline-track-bg-alt`] ?? `#1e1e1e`;
32162
32924
  for (let i = 0; i < trackLayout.length; i++) {
@@ -32164,7 +32926,7 @@ const drawTrackBackgrounds = (ctx, region, trackLayout, tracks, vars, ppu) => {
32164
32926
  if (layout.y + layout.height < region.y) continue;
32165
32927
  if (layout.y > region.y + region.height) break;
32166
32928
  const track = tracks.find((t) => t.key === layout.trackKey);
32167
- ctx.fillStyle = track?.color ? track.color + `22` : i % 2 === 0 ? bgDefault : bgAlt;
32929
+ ctx.fillStyle = track?.color ? `${track.color}22` : i % 2 === 0 ? bgDefault : bgAlt;
32168
32930
  ctx.fillRect(region.x * ppu, layout.y, region.width * ppu, layout.height);
32169
32931
  ctx.strokeStyle = vars[`--timeline-grid-major-color`] ?? `rgba(255,255,255,0.12)`;
32170
32932
  ctx.lineWidth = .5;
@@ -32173,13 +32935,13 @@ const drawTrackBackgrounds = (ctx, region, trackLayout, tracks, vars, ppu) => {
32173
32935
  ctx.lineTo((region.x + region.width) * ppu, layout.y + layout.height);
32174
32936
  ctx.stroke();
32175
32937
  }
32176
- };
32177
- const drawEventRects = (ctx, region, events, trackLayout, selectedIds, tickledId, drag, vars, ppu) => {
32938
+ }
32939
+ function drawEventRects(ctx, region, events, trackLayout, selectedIds, tickledId, drag, vars, ppu) {
32178
32940
  const eventBg = vars[`--timeline-event-bg`] ?? `#3498db`;
32179
32941
  const eventSelectedBg = vars[`--timeline-event-selected-bg`] ?? `#5dade2`;
32180
32942
  const eventTickledBg = vars[`--timeline-event-tickled-bg`] ?? `#444`;
32181
32943
  const eventLabelColor = vars[`--timeline-event-label-color`] ?? `#fff`;
32182
- const radius = parseFloat(vars[`--timeline-event-radius`] ?? `4`);
32944
+ const radius = Number.parseFloat(vars[`--timeline-event-radius`] ?? `4`);
32183
32945
  const regionEnd = region.x + region.width;
32184
32946
  const padding = 3;
32185
32947
  for (const evt of events) {
@@ -32233,8 +32995,8 @@ const drawEventRects = (ctx, region, events, trackLayout, selectedIds, tickledId
32233
32995
  ctx.restore();
32234
32996
  }
32235
32997
  }
32236
- };
32237
- const drawMarquee = (ctx, drag, ppu) => {
32998
+ }
32999
+ function drawMarquee(ctx, drag, ppu) {
32238
33000
  if (drag.kind !== `marquee`) return;
32239
33001
  const x1 = Math.min(drag.startLogicalX, drag.currentLogicalX) * ppu;
32240
33002
  const x2 = Math.max(drag.startLogicalX, drag.currentLogicalX) * ppu;
@@ -32245,8 +33007,8 @@ const drawMarquee = (ctx, drag, ppu) => {
32245
33007
  ctx.strokeStyle = `rgba(100, 150, 255, 0.6)`;
32246
33008
  ctx.lineWidth = 1;
32247
33009
  ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
32248
- };
32249
- const drawRoundRect = (ctx, x, y, w, h, r) => {
33010
+ }
33011
+ function drawRoundRect(ctx, x, y, w, h, r) {
32250
33012
  r = Math.max(0, r);
32251
33013
  ctx.moveTo(x + r, y);
32252
33014
  ctx.arcTo(x + w, y, x + w, y + h, r);
@@ -32254,7 +33016,7 @@ const drawRoundRect = (ctx, x, y, w, h, r) => {
32254
33016
  ctx.arcTo(x, y + h, x, y, r);
32255
33017
  ctx.arcTo(x, y, x + w, y, r);
32256
33018
  ctx.closePath();
32257
- };
33019
+ }
32258
33020
  const TIMELINE_CSS_VARS = [
32259
33021
  `--timeline-bg`,
32260
33022
  `--timeline-track-bg`,
@@ -32271,12 +33033,12 @@ const TIMELINE_CSS_VARS = [
32271
33033
  `--timeline-event-radius`,
32272
33034
  `--timeline-axis-label-color`
32273
33035
  ];
32274
- const resolveCssVars = (host) => {
33036
+ function resolveCssVars(host) {
32275
33037
  const computed = getComputedStyle(host);
32276
33038
  const vars = {};
32277
33039
  for (const v of TIMELINE_CSS_VARS) vars[v] = computed.getPropertyValue(v).trim();
32278
33040
  return vars;
32279
- };
33041
+ }
32280
33042
  //#endregion
32281
33043
  //#region src/timeline/timeline-interactions.ts
32282
33044
  /** Hit zone near the edge of an event (px in screen space) */
@@ -32285,13 +33047,17 @@ const EDGE_HIT_PX = 8;
32285
33047
  * Convert a vcanvas logical (pixel) coordinate to timeline logical units.
32286
33048
  * vcanvas pixel coordinates are in the 1:1 pixel space; divide by ppu then add min.
32287
33049
  */
32288
- const canvasToTimeline = (canvasX, ppu, min) => canvasX / ppu + min;
33050
+ function canvasToTimeline(canvasX, ppu, min) {
33051
+ return canvasX / ppu + min;
33052
+ }
32289
33053
  /**
32290
33054
  * Convert screen-space pixel distance to timeline logical units.
32291
33055
  * Used for edge hit threshold: how many logical units does EDGE_HIT_PX correspond to.
32292
33056
  */
32293
- const pxToLogicalUnits = (ppu) => ppu > 0 ? 1 / ppu : 1;
32294
- const hitTest = (logicalX, y, events, trackLayout, ppu) => {
33057
+ function pxToLogicalUnits(ppu) {
33058
+ return ppu > 0 ? 1 / ppu : 1;
33059
+ }
33060
+ function hitTest(logicalX, y, events, trackLayout, ppu) {
32295
33061
  const track = trackLayout.find((tl) => y >= tl.y && y < tl.y + tl.height);
32296
33062
  if (!track) return {
32297
33063
  kind: `empty`,
@@ -32324,17 +33090,19 @@ const hitTest = (logicalX, y, events, trackLayout, ppu) => {
32324
33090
  y,
32325
33091
  trackKey: track.trackKey
32326
33092
  };
32327
- };
32328
- const trackAtY = (y, trackLayout) => {
33093
+ }
33094
+ function trackAtY(y, trackLayout) {
32329
33095
  return trackLayout.find((tl) => y >= tl.y && y < tl.y + tl.height)?.trackKey;
32330
- };
33096
+ }
32331
33097
  /** Sensitivity: how many pixels of vertical drag = 1 e-fold of zoom */
32332
33098
  const ZOOM_SENSITIVITY_PX = 100;
32333
33099
  /**
32334
33100
  * Subtract a deadzone from a value.
32335
33101
  * Returns 0 if |value| <= deadzone, otherwise value shifted toward zero by deadzone.
32336
33102
  */
32337
- const applyDeadzone = (value, deadzone) => Math.abs(value) > deadzone ? value - Math.sign(value) * deadzone : 0;
33103
+ function applyDeadzone(value, deadzone) {
33104
+ return Math.abs(value) > deadzone ? value - Math.sign(value) * deadzone : 0;
33105
+ }
32338
33106
  /**
32339
33107
  * Compute new ppu + viewMin from an axis-zoom drag.
32340
33108
  * Vertical delta (dy) controls zoom (up = zoom in, down = zoom out), anchored at `anchorUnit`.
@@ -32342,7 +33110,7 @@ const applyDeadzone = (value, deadzone) => Math.abs(value) > deadzone ? value -
32342
33110
  *
32343
33111
  * Returns { ppu, viewMin } — the caller uses host.setZoomPan() to apply.
32344
33112
  */
32345
- const computeAxisZoom = (startViewMin, startPpu, anchorUnit, dx, dy) => {
33113
+ function computeAxisZoom(startViewMin, startPpu, anchorUnit, dx, dy) {
32346
33114
  const newPpu = startPpu * Math.exp(-dy / ZOOM_SENSITIVITY_PX);
32347
33115
  let newMin = anchorUnit - (anchorUnit - startViewMin) * startPpu / newPpu;
32348
33116
  if (newPpu > 0) newMin += -dx / newPpu;
@@ -32350,7 +33118,7 @@ const computeAxisZoom = (startViewMin, startPpu, anchorUnit, dx, dy) => {
32350
33118
  ppu: newPpu,
32351
33119
  viewMin: newMin
32352
33120
  };
32353
- };
33121
+ }
32354
33122
  /**
32355
33123
  * Compute new vertical view range from a v-axis-zoom drag.
32356
33124
  * Horizontal delta (dx) controls zoom (right = zoom in, left = zoom out),
@@ -32359,7 +33127,7 @@ const computeAxisZoom = (startViewMin, startPpu, anchorUnit, dx, dy) => {
32359
33127
  *
32360
33128
  * Returns { vMin, vMax } — the caller uses host.setVerticalZoomPan() to apply.
32361
33129
  */
32362
- const computeVerticalAxisZoom = (startVMin, startVMax, startVScale, anchorBaseY, dx, dy) => {
33130
+ function computeVerticalAxisZoom(startVMin, startVMax, startVScale, anchorBaseY, dx, dy) {
32363
33131
  const startSpan = startVMax - startVMin;
32364
33132
  const newSpan = startSpan * Math.exp(-dx / ZOOM_SENSITIVITY_PX);
32365
33133
  let newMin = anchorBaseY - (startSpan > 0 ? (anchorBaseY - startVMin) / startSpan : .5) * newSpan;
@@ -32368,12 +33136,12 @@ const computeVerticalAxisZoom = (startVMin, startVMax, startVScale, anchorBaseY,
32368
33136
  vMin: newMin,
32369
33137
  vMax: newMin + newSpan
32370
33138
  };
32371
- };
33139
+ }
32372
33140
  /**
32373
33141
  * Attaches pointer and keyboard interaction handlers to the timeline.
32374
33142
  * Returns a cleanup function.
32375
33143
  */
32376
- const attachTimelineInteractions = (host, vcanvasEl) => {
33144
+ function attachTimelineInteractions(host, vcanvasEl) {
32377
33145
  const ac = new AbortController();
32378
33146
  const signal = ac.signal;
32379
33147
  vcanvasEl.addEventListener(`pointerdown`, (e) => {
@@ -32434,7 +33202,6 @@ const attachTimelineInteractions = (host, vcanvasEl) => {
32434
33202
  currentLogicalX: lx,
32435
33203
  currentY: ly
32436
33204
  };
32437
- return;
32438
33205
  }
32439
33206
  }, { signal });
32440
33207
  vcanvasEl.addEventListener(`pointermove`, (e) => {
@@ -32507,7 +33274,6 @@ const attachTimelineInteractions = (host, vcanvasEl) => {
32507
33274
  end: newEnd
32508
33275
  } : ev);
32509
33276
  vcanvasEl.invalidateAll();
32510
- return;
32511
33277
  }
32512
33278
  }, { signal });
32513
33279
  vcanvasEl.addEventListener(`pointerup`, (e) => {
@@ -32580,8 +33346,8 @@ const attachTimelineInteractions = (host, vcanvasEl) => {
32580
33346
  }
32581
33347
  }, { signal });
32582
33348
  return () => ac.abort();
32583
- };
32584
- const handleHover = (host, vcanvasEl, e) => {
33349
+ }
33350
+ function handleHover(host, vcanvasEl, e) {
32585
33351
  const ppu = host.pxPerUnit;
32586
33352
  const canvasCoord = vcanvasEl.convertFromViewportToCanvas(e.offsetX, e.offsetY);
32587
33353
  const lx = canvasToTimeline(canvasCoord.x, ppu, host.min);
@@ -32605,22 +33371,22 @@ const handleHover = (host, vcanvasEl, e) => {
32605
33371
  host._tickledId = newTickledId;
32606
33372
  vcanvasEl.invalidateAll();
32607
33373
  }
32608
- };
32609
- const toggleSelection = (host, eventId) => {
33374
+ }
33375
+ function toggleSelection(host, eventId) {
32610
33376
  const next = new Set(host._selectedIds);
32611
33377
  if (next.has(eventId)) next.delete(eventId);
32612
33378
  else next.add(eventId);
32613
33379
  host._selectedIds = next;
32614
33380
  fireSelectionChange(host);
32615
33381
  host.getVCanvas()?.invalidateAll();
32616
- };
32617
- const fireSelectionChange = (host) => {
33382
+ }
33383
+ function fireSelectionChange(host) {
32618
33384
  host.dispatchEvent(new CustomEvent(`selection-change`, {
32619
33385
  detail: { selectedIds: [...host._selectedIds] },
32620
33386
  bubbles: true,
32621
33387
  composed: true
32622
33388
  }));
32623
- };
33389
+ }
32624
33390
  //#endregion
32625
33391
  //#region src/timeline/timeline-styles.ts
32626
33392
  const timelineStyles = i$7`
@@ -33682,70 +34448,6 @@ __decorate([r$1()], IxfxTimelineElement.prototype, "_internalEvents", void 0);
33682
34448
  __decorate([r$1()], IxfxTimelineElement.prototype, "_internalTracks", void 0);
33683
34449
  IxfxTimelineElement = __decorate([safeCustomElement(`ixfx-timeline`)], IxfxTimelineElement);
33684
34450
  //#endregion
33685
- //#region src/timeline/timeline-controller.ts
33686
- /**
33687
- * External adapter class that can configure a timeline element
33688
- * with musical time, custom label formatting, etc.
33689
- *
33690
- * The controller has no import of any timeline element type.
33691
- * Users create a controller, pass it the component instance,
33692
- * and the controller calls the component's public API.
33693
- */
33694
- var TimelineController = class {
33695
- #host;
33696
- #config;
33697
- #bpm = 120;
33698
- #timeSignatureNumerator = 4;
33699
- #timeSignatureDenominator = 4;
33700
- constructor(timeline, config) {
33701
- this.#host = timeline;
33702
- this.#config = config;
33703
- timeline.min = config.logicalMin;
33704
- timeline.max = config.logicalMax;
33705
- if (config.labelFormatter) timeline.formatLabel = config.labelFormatter;
33706
- timeline.addEventListener(`track-select`, this.#onTrackSelect);
33707
- }
33708
- /** Update the tempo (BPM) for musical time calculations. */
33709
- setTempo(bpm) {
33710
- this.#bpm = bpm;
33711
- this.#updateFormatLabel();
33712
- }
33713
- /** Update the time signature for musical time calculations. */
33714
- setTimeSignature(numerator, denominator) {
33715
- this.#timeSignatureNumerator = numerator;
33716
- this.#timeSignatureDenominator = denominator;
33717
- this.#updateFormatLabel();
33718
- }
33719
- /** Set the visible view range on the timeline. */
33720
- setViewRange(range) {
33721
- this.#host.setViewRange(range);
33722
- }
33723
- /** Clean up any resources held by the controller. */
33724
- dispose() {
33725
- this.#host.removeEventListener(`track-select`, this.#onTrackSelect);
33726
- }
33727
- #onTrackSelect = (e) => {
33728
- const detail = e.detail;
33729
- const trackEventIds = this.#host.getTrackEvents(detail.trackKey).map((e) => e.id);
33730
- if (detail.shiftKey) {
33731
- const current = new Set(this.#host.selectedIds);
33732
- for (const id of trackEventIds) if (current.has(id)) current.delete(id);
33733
- else current.add(id);
33734
- this.#host.selectedIds = [...current];
33735
- } else this.#host.selectedIds = trackEventIds;
33736
- };
33737
- #updateFormatLabel() {
33738
- if (this.#config.labelFormatter) return;
33739
- this.#bpm;
33740
- const num = this.#timeSignatureNumerator;
33741
- this.#host.formatLabel = (v) => {
33742
- const beatsPerBar = num;
33743
- const totalBeats = v;
33744
- return `${Math.floor(totalBeats / beatsPerBar) + 1}.${Math.floor(totalBeats % beatsPerBar) + 1}`;
33745
- };
33746
- }
33747
- };
33748
- //#endregion
33749
34451
  //#region node_modules/.pnpm/lit-html@3.3.3/node_modules/lit-html/directives/keyed.js
33750
34452
  /**
33751
34453
  * @license
@@ -34115,7 +34817,7 @@ let TreeBaseElement = class TreeBaseElement extends i$4 {
34115
34817
  event.preventDefault();
34116
34818
  const current = nodes[currentIdx];
34117
34819
  if (!current) break;
34118
- if (this.interactionMode === `checked`) this._handleCheckboxClick(new MouseEvent("click"), current.node, current.depth);
34820
+ if (this.interactionMode === `checked`) this._handleCheckboxClick(new MouseEvent(`click`), current.node, current.depth);
34119
34821
  else if (this.interactionMode === `standard` || this.interactionMode === `vscode` || this.interactionMode === `manual`) this.#toggleSelection(current.node, current.depth);
34120
34822
  break;
34121
34823
  }
@@ -35254,7 +35956,7 @@ let TooltipElement = class TooltipElement extends i$4 {
35254
35956
  }
35255
35957
  #renderIcon() {
35256
35958
  if (!this.icon) return A;
35257
- if (this.icon.startsWith(`<svg`)) return b`<span class="tooltip-icon" style="display:inline-flex;align-items:center;margin-right:0.25em">${o(this.icon)}</span>`;
35959
+ if (this.icon.startsWith(`<svg`)) return b`<span class="tooltip-icon" style="display:inline-flex;align-items:center;margin-right:0.25em">${o$1(this.icon)}</span>`;
35258
35960
  if (!getIcon(this.icon)) return A;
35259
35961
  return b`<ixfx-icon class="tooltip-icon" style="margin-right:0.25em" name="${this.icon}"></ixfx-icon>`;
35260
35962
  }
@@ -35297,7 +35999,9 @@ TooltipElement = _TooltipElement = __decorate([safeCustomElement(`ixfx-tooltip`)
35297
35999
  //#endregion
35298
36000
  //#region src/vertical-list/vertical-list.ts
35299
36001
  const ITEM_SELECTOR = `li, [data-list-item]`;
35300
- const isMultiMode = (mode) => mode === `checked` || mode === `vscode` || mode === `standard`;
36002
+ function isMultiMode(mode) {
36003
+ return mode === `checked` || mode === `vscode` || mode === `standard`;
36004
+ }
35301
36005
  let VerticalListElement = class VerticalListElement extends i$4 {
35302
36006
  constructor(..._args) {
35303
36007
  super(..._args);
@@ -35306,6 +36010,7 @@ let VerticalListElement = class VerticalListElement extends i$4 {
35306
36010
  this._selectedItems = /* @__PURE__ */ new Set();
35307
36011
  this._searchVisible = false;
35308
36012
  this._searchQuery = ``;
36013
+ this.#customWrappers = /* @__PURE__ */ new Map();
35309
36014
  this.tickled = new TickledController(this, {
35310
36015
  getItems: () => this._getVisibleItems().map((el) => ({
35311
36016
  id: el,
@@ -35415,6 +36120,7 @@ let VerticalListElement = class VerticalListElement extends i$4 {
35415
36120
  }
35416
36121
  #search;
35417
36122
  #selectionAnchor;
36123
+ #customWrappers;
35418
36124
  connectedCallback() {
35419
36125
  super.connectedCallback();
35420
36126
  if (!this.hasAttribute(`tabindex`)) this.tabIndex = 0;
@@ -35447,9 +36153,29 @@ let VerticalListElement = class VerticalListElement extends i$4 {
35447
36153
  super.updated(changed);
35448
36154
  if (changed.has(`interactionMode`)) {
35449
36155
  this.#selectionAnchor = void 0;
35450
- this._clearSelection();
36156
+ const previousSelected = this._selectedItems;
36157
+ const savedWrappers = changed.get(`interactionMode`) === `checked` ? new Map(this.#customWrappers) : null;
36158
+ this._syncCheckboxes();
36159
+ if (previousSelected.size > 0) {
36160
+ let remapped;
36161
+ if (savedWrappers && savedWrappers.size > 0) remapped = new Set([...previousSelected].map((el) => savedWrappers.get(el) ?? el));
36162
+ else if (this.#customWrappers.size > 0) {
36163
+ const innerToWrapper = new Map([...this.#customWrappers].map(([w, i]) => [i, w]));
36164
+ remapped = new Set([...previousSelected].map((el) => innerToWrapper.get(el) ?? el));
36165
+ } else remapped = new Set(previousSelected);
36166
+ this._selectedItems = remapped;
36167
+ }
36168
+ this._syncItemStates();
35451
36169
  }
35452
36170
  }
36171
+ /** The most recently selected item, or `undefined` if nothing is selected. */
36172
+ get selectedItem() {
36173
+ return [...this._selectedItems].at(-1);
36174
+ }
36175
+ /** The full current selection set (read-only). */
36176
+ get selectedItems() {
36177
+ return this._selectedItems;
36178
+ }
35453
36179
  addItem(item) {
35454
36180
  if (typeof item === `string`) {
35455
36181
  const li = document.createElement(`li`);
@@ -35462,6 +36188,7 @@ let VerticalListElement = class VerticalListElement extends i$4 {
35462
36188
  item.remove();
35463
36189
  }
35464
36190
  clearItems() {
36191
+ this.#customWrappers.clear();
35465
36192
  this.replaceChildren();
35466
36193
  this.#selectionAnchor = void 0;
35467
36194
  this._selectedItems = /* @__PURE__ */ new Set();
@@ -35470,6 +36197,19 @@ let VerticalListElement = class VerticalListElement extends i$4 {
35470
36197
  select(item) {
35471
36198
  this._replaceSelection(item);
35472
36199
  }
36200
+ selectMany(items) {
36201
+ if (this.selectionMode !== `multiple`) throw new Error(`selectMany requires selectionMode="multiple" (current: "${this.selectionMode}")`);
36202
+ const previous = this._selectedItems;
36203
+ const next = new Set(items);
36204
+ if (next.size === 0) return;
36205
+ this._selectedItems = next;
36206
+ this.#selectionAnchor = [...next].at(-1);
36207
+ this._syncItemStates();
36208
+ this._dispatch(`list-select`, {
36209
+ selected: next,
36210
+ previous
36211
+ });
36212
+ }
35473
36213
  deselect(item) {
35474
36214
  if (!this._selectedItems.has(item)) return;
35475
36215
  const previous = this._selectedItems;
@@ -35601,11 +36341,34 @@ let VerticalListElement = class VerticalListElement extends i$4 {
35601
36341
  if (this.interactionMode === `checked`) el.toggleAttribute(`data-checked`, this._selectedItems.has(el));
35602
36342
  else el.removeAttribute(`data-checked`);
35603
36343
  }
35604
- this.dataset[`inputMode`] = this.tickled.inputMode;
36344
+ this.dataset.inputMode = this.tickled.inputMode;
35605
36345
  this._syncCheckboxes();
35606
36346
  }
35607
36347
  _syncCheckboxes() {
35608
36348
  const inCheckedMode = this.interactionMode === `checked`;
36349
+ if (inCheckedMode) for (const inner of Array.from(this.children)) {
36350
+ if (!inner.hasAttribute(`data-list-item`)) continue;
36351
+ if (inner.parentElement !== this) continue;
36352
+ const wrapper = document.createElement(`li`);
36353
+ wrapper.className = `ixfx-custom-row`;
36354
+ const key = inner.getAttribute(`data-key`);
36355
+ const label = inner.getAttribute(`data-search-label`);
36356
+ if (key !== null) wrapper.setAttribute(`data-key`, key);
36357
+ if (label !== null) wrapper.setAttribute(`data-search-label`, label);
36358
+ inner.removeAttribute(`data-list-item`);
36359
+ this.insertBefore(wrapper, inner);
36360
+ wrapper.appendChild(inner);
36361
+ this.#customWrappers.set(wrapper, inner);
36362
+ }
36363
+ else {
36364
+ for (const [wrapper, inner] of this.#customWrappers) {
36365
+ if (wrapper.parentElement !== this) continue;
36366
+ inner.setAttribute(`data-list-item`, ``);
36367
+ this.insertBefore(inner, wrapper);
36368
+ wrapper.remove();
36369
+ }
36370
+ this.#customWrappers.clear();
36371
+ }
35609
36372
  for (const el of this.querySelectorAll(`li`)) {
35610
36373
  let cb = el.querySelector(`input.ixfx-list-checkbox`);
35611
36374
  if (inCheckedMode) {
@@ -35747,17 +36510,6 @@ let VerticalListElement = class VerticalListElement extends i$4 {
35747
36510
  outline: 1px solid var(--accent, #60a5fa);
35748
36511
  }
35749
36512
 
35750
- /* checked mode: left-border indicator for custom shadow-DOM items (no checkbox injection possible) */
35751
- :host([interaction-mode="checked"]) ::slotted([data-list-item]) {
35752
- border-left: 3px solid transparent;
35753
- transition: background var(--transition, 0.1s ease-out),
35754
- border-color var(--transition, 0.1s ease-out);
35755
- }
35756
-
35757
- :host([interaction-mode="checked"]) ::slotted([data-list-item][data-checked]) {
35758
- border-left: 3px solid var(--accent, #60a5fa);
35759
- }
35760
-
35761
36513
  .search-overlay {
35762
36514
  position: absolute;
35763
36515
  top: 8px;
@@ -36273,6 +37025,6 @@ function init() {
36273
37025
  console.log(`Init`);
36274
37026
  }
36275
37027
  //#endregion
36276
- export { AcTextElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataDisplayComponent, EditableLabel, EditableNumber, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LedElement, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, SelectHorizontalElement, SnapContainer, SplitButton, SplitLayoutElement, SwipeElement, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TimelineController, TimelineTrackTabElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeListElement, VerticalListElement, XyPad, applyAgeModification, applyHighlights, attachTimelineInteractions, buildHighlightTargets, clearHighlights, computeColour, createAcCompletionProvider, createElementIncrSearch, createFuzzySearch, createIncrSearch, getIcon, hasIcon, iconBus, init, monitorIndeterminate, parseColourScaleAttr, readBaseColour, registerIcon, resolveCssVars, resolveGrid, svgToDataUri, timelineStyles };
37028
+ export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataDisplayComponent, EditableLabel, EditableNumber, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LedElement, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, SelectHorizontalElement, SnapContainer, SplitButton, SplitLayoutElement, SwipeElement, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TimelineController, TimelineTrackTabElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeListElement, VerticalListElement, XyPad, applyAgeModification, applyHighlights, attachTimelineInteractions, buildHighlightTargets, clearHighlights, computeColour, createAcCompletionProvider, createElementIncrSearch, createFuzzySearch, createIncrSearch, getIcon, hasIcon, iconBus, init, monitorIndeterminate, parseColourScaleAttr, readBaseColour, registerIcon, resolveCssVars, resolveGrid, svgToDataUri, timelineStyles };
36277
37029
 
36278
37030
  //# sourceMappingURL=index.js.map