@ixfx/components 0.7.0 → 0.7.2

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 (125) hide show
  1. package/bundle/index.d.ts +259 -44
  2. package/bundle/index.d.ts.map +1 -1
  3. package/bundle/index.js +828 -125
  4. package/bundle/index.js.map +1 -1
  5. package/dist/ac-text.d.ts +1 -1
  6. package/dist/ac-text.js +1 -1
  7. package/dist/{ac-token-BAa6ke09.d.ts → ac-token--_vCHXWw.d.ts} +2 -2
  8. package/dist/{ac-token-BAa6ke09.d.ts.map → ac-token--_vCHXWw.d.ts.map} +1 -1
  9. package/dist/ac-token.d.ts +1 -1
  10. package/dist/ac-token.js +1 -1
  11. package/dist/{button-Dat2yHAU.js → button-DsFbV4XD.js} +3 -3
  12. package/dist/{button-Dat2yHAU.js.map → button-DsFbV4XD.js.map} +1 -1
  13. package/dist/button.d.ts.map +1 -1
  14. package/dist/button.js +1 -1
  15. package/dist/{colour-picker-BSDBL-QL.js → colour-picker-Ddsk-7E_.js} +2 -2
  16. package/dist/{colour-picker-BSDBL-QL.js.map → colour-picker-Ddsk-7E_.js.map} +1 -1
  17. package/dist/colour-picker.js +1 -1
  18. package/dist/crumbs.d.ts +1 -1
  19. package/dist/crumbs.js +1 -1
  20. package/dist/{data-grid-C8qIo1-u.d.ts → data-grid-Bh4YrQGK.d.ts} +2 -2
  21. package/dist/{data-grid-C8qIo1-u.d.ts.map → data-grid-Bh4YrQGK.d.ts.map} +1 -1
  22. package/dist/data-grid.d.ts +1 -1
  23. package/dist/data-grid.js +1 -1
  24. package/dist/{defaults-BkWCPJFL.js → defaults-BJwDsMH4.js} +26 -2
  25. package/dist/defaults-BJwDsMH4.js.map +1 -0
  26. package/dist/{hex-editor-Bk6rxhfg.js → hex-editor-BiRfwTz1.js} +2 -2
  27. package/dist/{hex-editor-Bk6rxhfg.js.map → hex-editor-BiRfwTz1.js.map} +1 -1
  28. package/dist/hex.js +1 -1
  29. package/dist/{icon-DpA9x2Ve.d.ts → icon-CDGprSdE.d.ts} +1 -12
  30. package/dist/icon-CDGprSdE.d.ts.map +1 -0
  31. package/dist/{icon-CvnPanhA.js → icon-DcEEGQa_.js} +37 -8
  32. package/dist/icon-DcEEGQa_.js.map +1 -0
  33. package/dist/icons.d.ts +3 -3
  34. package/dist/icons.js +4 -4
  35. package/dist/icons.js.map +1 -1
  36. package/dist/{index-CKtLhgYj.d.ts → index-9AhaHIT2.d.ts} +3 -3
  37. package/dist/{index-CKtLhgYj.d.ts.map → index-9AhaHIT2.d.ts.map} +1 -1
  38. package/dist/{index-BtGFYfaK.d.ts → index-CS9yM8L-.d.ts} +14 -4
  39. package/dist/index-CS9yM8L-.d.ts.map +1 -0
  40. package/dist/{index-CG2vIgPv.d.ts → index-UvnoZUAo.d.ts} +4 -4
  41. package/dist/{index-CG2vIgPv.d.ts.map → index-UvnoZUAo.d.ts.map} +1 -1
  42. package/dist/index.d.ts +231 -26
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +701 -110
  45. package/dist/index.js.map +1 -1
  46. package/dist/labelled-radial-input.d.ts +1 -1
  47. package/dist/labelled-radial-input.js +1 -1
  48. package/dist/{labelled-slider-input-DzigjMwP.js → labelled-slider-input-BInB5GKL.js} +3 -3
  49. package/dist/{labelled-slider-input-DzigjMwP.js.map → labelled-slider-input-BInB5GKL.js.map} +1 -1
  50. package/dist/labelled-slider-input.d.ts +1 -1
  51. package/dist/labelled-slider-input.js +1 -1
  52. package/dist/{menu-BjRvlgPy.js → menu-CXDFJxNm.js} +3 -3
  53. package/dist/{menu-BjRvlgPy.js.map → menu-CXDFJxNm.js.map} +1 -1
  54. package/dist/{menu-item-Bs2MYyHq.d.ts → menu-item-CZi9vy5O.d.ts} +4 -2
  55. package/dist/menu-item-CZi9vy5O.d.ts.map +1 -0
  56. package/dist/{menu-item-B-zYrTTi.js → menu-item-D2S4i8u1.js} +16 -4
  57. package/dist/menu-item-D2S4i8u1.js.map +1 -0
  58. package/dist/menu.d.ts +2 -2
  59. package/dist/menu.js +2 -2
  60. package/dist/notification-pill-DyZydXoZ.d.ts.map +1 -1
  61. package/dist/notifier.js +2 -2
  62. package/dist/notifier.js.map +1 -1
  63. package/dist/panel.d.ts +1 -1
  64. package/dist/plots.js +1 -1
  65. package/dist/polar-pad.d.ts +2 -2
  66. package/dist/polar-pad.js +1 -1
  67. package/dist/{radial-input-Dp5qz0ly.js → radial-input-BfA3GO0H.js} +2 -2
  68. package/dist/{radial-input-Dp5qz0ly.js.map → radial-input-BfA3GO0H.js.map} +1 -1
  69. package/dist/{radial-input-DXMN55Nz.d.ts → radial-input-DU6t2hYO.d.ts} +3 -3
  70. package/dist/{radial-input-DXMN55Nz.d.ts.map → radial-input-DU6t2hYO.d.ts.map} +1 -1
  71. package/dist/radial-input.d.ts +1 -1
  72. package/dist/radial-input.js +1 -1
  73. package/dist/range-input.d.ts +2 -2
  74. package/dist/range-input.js +1 -1
  75. package/dist/range.d.ts +2 -2
  76. package/dist/range.js +1 -1
  77. package/dist/registry-BEtb8_wB.js +46 -0
  78. package/dist/registry-BEtb8_wB.js.map +1 -0
  79. package/dist/select-horiz.d.ts +2 -2
  80. package/dist/select-horiz.js +1 -1
  81. package/dist/{slider-input-De4eoT11.js → slider-input-Bl1hFE89.js} +2 -2
  82. package/dist/{slider-input-De4eoT11.js.map → slider-input-Bl1hFE89.js.map} +1 -1
  83. package/dist/{slider-input-BfRUW2ZB.d.ts → slider-input-BxURAAmD.d.ts} +3 -3
  84. package/dist/{slider-input-BfRUW2ZB.d.ts.map → slider-input-BxURAAmD.d.ts.map} +1 -1
  85. package/dist/slider-input.d.ts +1 -1
  86. package/dist/slider-input.js +1 -1
  87. package/dist/swipe.d.ts +2 -2
  88. package/dist/swipe.js +1 -1
  89. package/dist/{tab-list-MPwq_1Jj.d.ts → tab-list-CGyPt07Z.d.ts} +9 -2
  90. package/dist/tab-list-CGyPt07Z.d.ts.map +1 -0
  91. package/dist/tabs.d.ts +1 -1
  92. package/dist/tabs.js +32 -13
  93. package/dist/tabs.js.map +1 -1
  94. package/dist/{titlebar-DaCfpliv.d.ts → titlebar-Bj5pfdw_.d.ts} +2 -2
  95. package/dist/{titlebar-DaCfpliv.d.ts.map → titlebar-Bj5pfdw_.d.ts.map} +1 -1
  96. package/dist/titlebar.d.ts +1 -1
  97. package/dist/titlebar.js +1 -1
  98. package/dist/tooltip-BlvBUQxx.d.ts.map +1 -1
  99. package/dist/wheel-nudge-By5s_ioH.d.ts +31 -0
  100. package/dist/wheel-nudge-By5s_ioH.d.ts.map +1 -0
  101. package/dist/{wheel-nudge-BlSCe_hD.js → wheel-nudge-D5rmht3v.js} +6 -1
  102. package/dist/wheel-nudge-D5rmht3v.js.map +1 -0
  103. package/dist/{xy-axis-CxHC8z5E.js → xy-axis-DWizs0bt.js} +3 -3
  104. package/dist/xy-axis-DWizs0bt.js.map +1 -0
  105. package/dist/xy-pad.d.ts +2 -2
  106. package/dist/xy-pad.js +1 -1
  107. package/docs-user/dock.md +166 -15
  108. package/docs-user/icons.md +4 -0
  109. package/docs-user/index.json +2 -2
  110. package/docs-user/tabs.md +20 -0
  111. package/docs-user/user-theming.md +109 -0
  112. package/package.json +1 -1
  113. package/dist/defaults-BkWCPJFL.js.map +0 -1
  114. package/dist/icon-CvnPanhA.js.map +0 -1
  115. package/dist/icon-DpA9x2Ve.d.ts.map +0 -1
  116. package/dist/index-BtGFYfaK.d.ts.map +0 -1
  117. package/dist/menu-item-B-zYrTTi.js.map +0 -1
  118. package/dist/menu-item-Bs2MYyHq.d.ts.map +0 -1
  119. package/dist/registry-CkW7q09Q.js +0 -21
  120. package/dist/registry-CkW7q09Q.js.map +0 -1
  121. package/dist/tab-list-MPwq_1Jj.d.ts.map +0 -1
  122. package/dist/wheel-nudge-BlSCe_hD.js.map +0 -1
  123. package/dist/wheel-nudge-DGrNzWuE.d.ts +0 -29
  124. package/dist/wheel-nudge-DGrNzWuE.d.ts.map +0 -1
  125. package/dist/xy-axis-CxHC8z5E.js.map +0 -1
package/bundle/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { customElement, eventOptions, property, query, queryAssignedElements, state } from "lit/decorators.js";
3
+ import { styleMap } from "lit/directives/style-map.js";
3
4
  import { unsafeHTML } from "lit/directives/unsafe-html.js";
4
5
  import { Fzf } from "fzf";
5
6
  import { Colour } from "@ixfx/visual";
@@ -7,7 +8,6 @@ import { createCommandRegistry } from "@clinth/ui-commands";
7
8
  import { Trees } from "@ixfx/collections";
8
9
  import { clamp, clamper, rangeStream, round, scaler } from "@ixfx/numbers";
9
10
  import { repeat } from "lit/directives/repeat.js";
10
- import { styleMap } from "lit/directives/style-map.js";
11
11
  import { Easings } from "@ixfx/modulation";
12
12
  import { classMap } from "lit/directives/class-map.js";
13
13
  import "@clinth/vcanvas";
@@ -1405,18 +1405,43 @@ function safeCustomElement(name) {
1405
1405
  //#endregion
1406
1406
  //#region src/icons/registry.ts
1407
1407
  const _registry = /* @__PURE__ */ new Map();
1408
+ let _iconFont;
1408
1409
  const iconBus = new EventTarget();
1409
1410
  function registerIcon(name, svg) {
1410
- _registry.set(name, svg);
1411
+ _registry.set(name, {
1412
+ kind: `svg`,
1413
+ svg
1414
+ });
1415
+ iconBus.dispatchEvent(new CustomEvent(`ixfx-icon-change`, { detail: { name } }));
1416
+ }
1417
+ function registerFontIcon(name, glyph) {
1418
+ let char;
1419
+ if (typeof glyph === `number`) char = String.fromCodePoint(glyph);
1420
+ else char = glyph;
1421
+ if (char.length === 0) throw new Error(`registerFontIcon: glyph must be non-empty`);
1422
+ _registry.set(name, {
1423
+ kind: `font`,
1424
+ char
1425
+ });
1411
1426
  iconBus.dispatchEvent(new CustomEvent(`ixfx-icon-change`, { detail: { name } }));
1412
1427
  }
1413
1428
  function getIcon(name) {
1429
+ const entry = _registry.get(name);
1430
+ return entry?.kind === `svg` ? entry.svg : void 0;
1431
+ }
1432
+ function getIconEntry(name) {
1414
1433
  return _registry.get(name);
1415
1434
  }
1416
1435
  function hasIcon(name) {
1417
1436
  return _registry.has(name);
1418
1437
  }
1419
- /** Converts an SVG string to a CSS data-URI value, e.g. for use as a mask-image. */
1438
+ function setIconFont(fontFamily) {
1439
+ _iconFont = fontFamily;
1440
+ iconBus.dispatchEvent(new CustomEvent(`ixfx-icon-font-change`, { detail: { name: `` } }));
1441
+ }
1442
+ function getIconFont() {
1443
+ return _iconFont;
1444
+ }
1420
1445
  function svgToDataUri(svg) {
1421
1446
  return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
1422
1447
  }
@@ -1440,26 +1465,43 @@ let IxfxIconElement = class IxfxIconElement extends i {
1440
1465
  this.#onIconChange = (e) => {
1441
1466
  if (e.detail.name === this.name) this.requestUpdate();
1442
1467
  };
1468
+ this.#onFontChange = () => {
1469
+ if (getIconEntry(this.name)?.kind === `font`) this.requestUpdate();
1470
+ };
1443
1471
  }
1444
1472
  #onIconChange;
1473
+ #onFontChange;
1445
1474
  connectedCallback() {
1446
1475
  super.connectedCallback();
1447
1476
  iconBus.addEventListener(`ixfx-icon-change`, this.#onIconChange);
1477
+ iconBus.addEventListener(`ixfx-icon-font-change`, this.#onFontChange);
1448
1478
  }
1449
1479
  disconnectedCallback() {
1450
1480
  super.disconnectedCallback();
1451
1481
  iconBus.removeEventListener(`ixfx-icon-change`, this.#onIconChange);
1482
+ iconBus.removeEventListener(`ixfx-icon-font-change`, this.#onFontChange);
1452
1483
  }
1453
1484
  render() {
1454
- const svg = getIcon(this.name);
1455
- if (!svg) return A;
1485
+ const entry = getIconEntry(this.name);
1486
+ if (!entry) return A;
1456
1487
  const hasLabel = this.label.length > 0;
1488
+ if (entry.kind === `font`) {
1489
+ const fontFamily = getIconFont();
1490
+ return b`
1491
+ <span
1492
+ class="font-icon"
1493
+ style=${styleMap({ fontFamily: fontFamily ?? `` })}
1494
+ aria-hidden=${hasLabel ? A : `true`}
1495
+ aria-label=${hasLabel ? this.label : A}
1496
+ >${entry.char}</span>
1497
+ `;
1498
+ }
1457
1499
  return b`
1458
1500
  <span
1459
1501
  class="icon-wrapper"
1460
1502
  aria-hidden=${hasLabel ? A : `true`}
1461
1503
  aria-label=${hasLabel ? this.label : A}
1462
- >${unsafeHTML(svg)}</span>
1504
+ >${unsafeHTML(entry.svg)}</span>
1463
1505
  `;
1464
1506
  }
1465
1507
  updated(changed) {
@@ -1492,17 +1534,28 @@ let IxfxIconElement = class IxfxIconElement extends i {
1492
1534
  opacity: var(--prominence-disabled-opacity, 0.5);
1493
1535
  }
1494
1536
 
1495
- .icon-wrapper {
1537
+ .icon-wrapper,
1538
+ .font-icon {
1496
1539
  display: contents;
1497
1540
  }
1498
1541
 
1499
- :host svg {
1542
+ :host svg,
1543
+ :host .font-icon {
1500
1544
  width: 100%;
1501
1545
  height: 100%;
1502
1546
  transition: transform var(--transition, 0.15s ease-out);
1503
1547
  }
1504
1548
 
1505
- :host([rotate]) svg {
1549
+ :host .font-icon {
1550
+ font-family: inherit;
1551
+ line-height: 1;
1552
+ display: inline-flex;
1553
+ align-items: center;
1554
+ justify-content: center;
1555
+ }
1556
+
1557
+ :host([rotate]) svg,
1558
+ :host([rotate]) .font-icon {
1506
1559
  transform: rotate(var(--rotate));
1507
1560
  }
1508
1561
  `];
@@ -3164,6 +3217,11 @@ const ICON_CHECK = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="tr
3164
3217
  */
3165
3218
  const ICON_CLOSE = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>`;
3166
3219
  /**
3220
+ * Plus / add icon. Pairs with `close` for add/remove actions.
3221
+ * fill="currentColor" so it inherits CSS color.
3222
+ */
3223
+ const ICON_ADD = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>`;
3224
+ /**
3167
3225
  * Error / warning icon. Useful for error notifications.
3168
3226
  * fill="currentColor" so it inherits CSS color.
3169
3227
  */
@@ -3188,12 +3246,31 @@ const ICON_CHEVRON_RIGHT = `<svg viewBox="0 0 20 20" fill="currentColor" aria-hi
3188
3246
  * fill="currentColor" so it inherits CSS color.
3189
3247
  */
3190
3248
  const ICON_FOLDER_UP = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 4L4 12h5v8h6v-8h5L12 4z"/></svg>`;
3249
+ /**
3250
+ * Folder. Useful for file explorers and sidebars.
3251
+ * fill="currentColor" so it inherits CSS color.
3252
+ */
3253
+ const ICON_FOLDER = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 1.99-.9 1.99-2V8c0-1.1-.9-2-2-2h-8l-2-2z"/></svg>`;
3254
+ /**
3255
+ * Document / file page. Useful for file lists and document tabs.
3256
+ * fill="currentColor" so it inherits CSS color.
3257
+ */
3258
+ const ICON_FILE = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></svg>`;
3259
+ /**
3260
+ * Magnifying glass. Useful for search panels and inputs.
3261
+ * fill="currentColor" so it inherits CSS color.
3262
+ */
3263
+ const ICON_SEARCH = `<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>`;
3191
3264
  function registerDefaults() {
3192
3265
  registerIcon(`chevron-down`, ICON_CHEVRON_DOWN);
3193
3266
  registerIcon(`chevron-right`, ICON_CHEVRON_RIGHT);
3194
3267
  registerIcon(`caret-right`, ICON_CARET_RIGHT);
3195
3268
  registerIcon(`check`, ICON_CHECK);
3196
3269
  registerIcon(`close`, ICON_CLOSE);
3270
+ registerIcon(`file`, ICON_FILE);
3271
+ registerIcon(`folder`, ICON_FOLDER);
3272
+ registerIcon(`search`, ICON_SEARCH);
3273
+ registerIcon(`add`, ICON_ADD);
3197
3274
  registerIcon(`error`, ICON_ERROR);
3198
3275
  registerIcon(`folder-up`, ICON_FOLDER_UP);
3199
3276
  registerIcon(`info`, ICON_INFO);
@@ -3409,7 +3486,7 @@ let ButtonElement = class ButtonElement extends i {
3409
3486
  _renderIcon() {
3410
3487
  if (this.hideIcon || !this.icon) return A;
3411
3488
  if (this.icon.startsWith(`<svg`)) return b`<span class="button-icon">${unsafeHTML(this.icon)}</span>`;
3412
- if (!getIcon(this.icon)) return A;
3489
+ if (!hasIcon(this.icon)) return A;
3413
3490
  return b`<ixfx-icon class="button-icon" name="${this.icon}"></ixfx-icon>`;
3414
3491
  }
3415
3492
  render() {
@@ -4244,6 +4321,7 @@ let MenuTrigger = class MenuTrigger extends i {
4244
4321
  super(..._args);
4245
4322
  this.mode = `trigger`;
4246
4323
  this.label = `⋮`;
4324
+ this.iconName = ``;
4247
4325
  this.placement = `bottom-start`;
4248
4326
  this.tabStop = true;
4249
4327
  this._isOpen = false;
@@ -4453,11 +4531,18 @@ let MenuTrigger = class MenuTrigger extends i {
4453
4531
 
4454
4532
  .trigger-area {
4455
4533
  anchor-name: var(--anchor-name);
4456
- display: inline-flex;
4534
+ /* Block flex (not inline-flex): as an inline-level box this would join
4535
+ the host's inline formatting context and pick up strut/baseline gaps
4536
+ above and below the button, offsetting the glyph from center. */
4537
+ display: flex;
4457
4538
  align-items: stretch;
4458
4539
  }
4459
4540
 
4460
4541
  .trigger-btn {
4542
+ display: inline-flex;
4543
+ align-items: center;
4544
+ justify-content: center;
4545
+ line-height: 1;
4461
4546
  background: none;
4462
4547
  border: none;
4463
4548
  color: var(--surface-3-text);
@@ -4522,7 +4607,7 @@ let MenuTrigger = class MenuTrigger extends i {
4522
4607
  @click=${this._handleTriggerClick}
4523
4608
  @contextmenu=${this._handleContextMenu}
4524
4609
  >
4525
- ${showButton ? b`<button class="trigger-btn" part="base" tabindex=${this.tabStop ? `0` : `-1`}>${this.label}</button>` : null}
4610
+ ${showButton ? b`<button class="trigger-btn" part="base" tabindex=${this.tabStop ? `0` : `-1`}>${this.iconName ? b`<ixfx-icon name=${this.iconName}></ixfx-icon>` : this.label}</button>` : null}
4526
4611
  <slot name="trigger" @slotchange=${this._handleTriggerSlotChange}></slot>
4527
4612
  </div>
4528
4613
  <div
@@ -4537,6 +4622,10 @@ let MenuTrigger = class MenuTrigger extends i {
4537
4622
  };
4538
4623
  __decorate([property({ type: String })], MenuTrigger.prototype, "mode", void 0);
4539
4624
  __decorate([property({ type: String })], MenuTrigger.prototype, "label", void 0);
4625
+ __decorate([property({
4626
+ type: String,
4627
+ attribute: `icon-name`
4628
+ })], MenuTrigger.prototype, "iconName", void 0);
4540
4629
  __decorate([property({ attribute: false })], MenuTrigger.prototype, "registry", void 0);
4541
4630
  __decorate([property({ attribute: false })], MenuTrigger.prototype, "keyboardManager", void 0);
4542
4631
  __decorate([property({ type: String })], MenuTrigger.prototype, "placement", void 0);
@@ -12807,10 +12896,11 @@ function unregisterDockHost(host) {
12807
12896
  * Begin a dock-panel drag. Called by hosts in response to their own
12808
12897
  * `dock-panel-drag-start` events (titlebar or tab-item handles).
12809
12898
  */
12810
- function startDrag(panelId, sourceHost, pointerId, clientX, clientY) {
12899
+ function startDrag(panelId, sourceHost, pointerId, clientX, clientY, kind) {
12811
12900
  if (activeDrag !== null);
12812
12901
  activeDrag = {
12813
12902
  panelId,
12903
+ kind,
12814
12904
  sourceHost,
12815
12905
  pointerId,
12816
12906
  ghost: createGhostForPanel(sourceHost, panelId),
@@ -12882,8 +12972,10 @@ function onKeyDown(event) {
12882
12972
  endDrag();
12883
12973
  }
12884
12974
  function findDropZone(drag, clientX, clientY) {
12975
+ const sourceIsolated = drag.sourceHost.isolated;
12885
12976
  for (const host of dragHosts) {
12886
- const zone = host.computeDropZone(clientX, clientY, drag.panelId);
12977
+ if (sourceIsolated && host !== drag.sourceHost) continue;
12978
+ const zone = host.computeDropZone(clientX, clientY, drag.panelId, drag.kind);
12887
12979
  if (zone !== null) return zone;
12888
12980
  }
12889
12981
  return null;
@@ -12971,12 +13063,15 @@ function createEmptyLayout() {
12971
13063
  root: createEmptyStack()
12972
13064
  };
12973
13065
  }
12974
- function createStack(panels, active) {
12975
- return {
13066
+ function createStack(panels, active, name) {
13067
+ const stack = {
12976
13068
  kind: `stack`,
12977
13069
  panels: [...panels],
12978
13070
  active: active ?? panels[0] ?? ``
12979
13071
  };
13072
+ const trimmed = name?.trim();
13073
+ if (trimmed) stack.name = trimmed;
13074
+ return stack;
12980
13075
  }
12981
13076
  function createSplit(direction, children, sizes) {
12982
13077
  const mean = children.length > 0 ? 1 / children.length : 1;
@@ -12990,7 +13085,8 @@ function createSplit(direction, children, sizes) {
12990
13085
  /**
12991
13086
  * Runs after every mutation:
12992
13087
  * - stack `active` fixed to a member of `panels` (or `''` when empty)
12993
- * - empty stacks removed from their parent split (and their `sizes` entry)
13088
+ * - empty *unnamed* stacks removed from their parent split (and their
13089
+ * `sizes` entry); named empty stacks are kept so the name stays addressable
12994
13090
  * - splits with ≤1 child replaced by that child (spliced into the parent)
12995
13091
  * - a child split whose direction equals its parent's is flattened into it,
12996
13092
  * spreading its sizes across the slot it vacated
@@ -12999,13 +13095,14 @@ function createSplit(direction, children, sizes) {
12999
13095
  */
13000
13096
  function normalize(root) {
13001
13097
  if (isStack(root)) {
13098
+ sanitizeStackName(root);
13002
13099
  if (!root.panels.includes(root.active)) root.active = root.panels[0] ?? ``;
13003
13100
  return root;
13004
13101
  }
13005
13102
  for (let i = root.children.length - 1; i >= 0; i--) {
13006
13103
  normalize(root.children[i]);
13007
13104
  const child = root.children[i];
13008
- if (isStack(child) && child.panels.length === 0) {
13105
+ if (isStack(child) && child.panels.length === 0 && !child.name) {
13009
13106
  root.children.splice(i, 1);
13010
13107
  root.sizes.splice(i, 1);
13011
13108
  continue;
@@ -13039,17 +13136,36 @@ function replaceNodeContents(target, source) {
13039
13136
  const t = target;
13040
13137
  t.panels = source.panels;
13041
13138
  t.active = source.active;
13139
+ if (source.name !== void 0) t.name = source.name;
13140
+ else delete t.name;
13141
+ if (source.hideLabels !== void 0) t.hideLabels = source.hideLabels;
13142
+ else delete t.hideLabels;
13042
13143
  delete t.collapsed;
13043
13144
  delete t.preCollapseSize;
13145
+ if (source.collapsed !== void 0) t.collapsed = source.collapsed;
13146
+ if (source.preCollapseSize !== void 0) t.preCollapseSize = source.preCollapseSize;
13044
13147
  } else {
13045
13148
  target.kind = `split`;
13046
13149
  const t = target;
13047
13150
  t.direction = source.direction;
13048
13151
  t.children = source.children;
13049
13152
  t.sizes = source.sizes;
13153
+ const stale = t;
13154
+ delete stale.panels;
13155
+ delete stale.active;
13156
+ delete stale.name;
13157
+ delete stale.collapsed;
13158
+ delete stale.preCollapseSize;
13050
13159
  }
13051
13160
  return target;
13052
13161
  }
13162
+ /** Trim `name`; drop it when empty / non-string so lookups stay predictable. */
13163
+ function sanitizeStackName(stack) {
13164
+ if (stack.name === void 0) return;
13165
+ const trimmed = typeof stack.name === `string` ? stack.name.trim() : ``;
13166
+ if (trimmed) stack.name = trimmed;
13167
+ else delete stack.name;
13168
+ }
13053
13169
  function findNode(root, path) {
13054
13170
  let current = root;
13055
13171
  for (const index of path) {
@@ -13083,6 +13199,36 @@ function collectPanelIds(root) {
13083
13199
  return root.children.flatMap(collectPanelIds);
13084
13200
  }
13085
13201
  /**
13202
+ * Path of the first stack (tree order) whose `name` matches, or null.
13203
+ * Names are expected to be unique per layout; duplicates resolve first-wins.
13204
+ */
13205
+ function findStackPathByName(root, name) {
13206
+ const wanted = name.trim();
13207
+ if (!wanted) return null;
13208
+ return findStackPathByNameInner(root, wanted, []);
13209
+ }
13210
+ function findStackPathByNameInner(node, name, path) {
13211
+ if (isStack(node)) return node.name === name ? path : null;
13212
+ for (let i = 0; i < node.children.length; i++) {
13213
+ const found = findStackPathByNameInner(node.children[i], name, [...path, i]);
13214
+ if (found !== null) return found;
13215
+ }
13216
+ return null;
13217
+ }
13218
+ /** Names of every named stack leaf, in tree order. */
13219
+ function collectContainerNames(root) {
13220
+ const out = [];
13221
+ const visit = (node) => {
13222
+ if (isStack(node)) {
13223
+ if (node.name) out.push(node.name);
13224
+ return;
13225
+ }
13226
+ node.children.forEach(visit);
13227
+ };
13228
+ visit(root);
13229
+ return out;
13230
+ }
13231
+ /**
13086
13232
  * Paths of every stack leaf, in tree order. `[]` is the root itself when it
13087
13233
  * is a stack.
13088
13234
  */
@@ -13164,11 +13310,27 @@ function insertBeside(root, targetPath, direction, newNode, side) {
13164
13310
  * the caller's reference stays valid. Returns true on success.
13165
13311
  */
13166
13312
  function wrapRoot(root, direction, newNode, side) {
13167
- replaceNodeContents(root, createSplit(direction, side === `before` ? [newNode, root] : [root, newNode]));
13313
+ const inner = cloneNodeShallow(root);
13314
+ replaceNodeContents(root, createSplit(direction, side === `before` ? [newNode, inner] : [inner, newNode]));
13168
13315
  normalize(root);
13169
13316
  return true;
13170
13317
  }
13171
13318
  /**
13319
+ * Detached copy of `node` (own arrays copied) so it can become a child of a
13320
+ * wrapper that then replaces `node` in place.
13321
+ */
13322
+ function cloneNodeShallow(node) {
13323
+ if (isStack(node)) return {
13324
+ ...node,
13325
+ panels: [...node.panels]
13326
+ };
13327
+ return {
13328
+ ...node,
13329
+ children: [...node.children],
13330
+ sizes: [...node.sizes]
13331
+ };
13332
+ }
13333
+ /**
13172
13334
  * Remove the stack at `stackPath` from the tree. Its panels are moved into
13173
13335
  * `fallbackStack` (if given and still attached) so they are not silently
13174
13336
  * lost; otherwise they are dropped from the model (elements return to the
@@ -13197,6 +13359,251 @@ function extractStack(root, stackPath, fallbackStack) {
13197
13359
  normalize(root);
13198
13360
  return stack;
13199
13361
  }
13362
+ /** Tolerance for edge-touch comparisons in unit-square geometry. */
13363
+ const GEOM_EPS = 1e-9;
13364
+ function pathsEqual(a, b) {
13365
+ return a.length === b.length && a.every((v, i) => v === b[i]);
13366
+ }
13367
+ /**
13368
+ * Resolve a {@link DockLeafRef} to a stack path. Strings match container
13369
+ * `name` first-wins; arrays must index an existing stack leaf. Returns null
13370
+ * when unresolvable.
13371
+ */
13372
+ function resolveLeafPath(root, ref) {
13373
+ if (typeof ref === `string`) return findStackPathByName(root, ref);
13374
+ if (!Array.isArray(ref)) return null;
13375
+ let current = root;
13376
+ for (const index of ref) {
13377
+ if (!Number.isInteger(index) || index < 0) return null;
13378
+ if (!isSplit(current)) return null;
13379
+ const next = current.children[index];
13380
+ if (!next) return null;
13381
+ current = next;
13382
+ }
13383
+ return isStack(current) ? [...ref] : null;
13384
+ }
13385
+ /** Absolute (unit-square) rect of every stack leaf, in tree order. */
13386
+ function computeLeafGeoms(root) {
13387
+ const out = [];
13388
+ const visit = (node, path, rect) => {
13389
+ if (isStack(node)) {
13390
+ out.push({
13391
+ path,
13392
+ name: node.name ?? null,
13393
+ rect
13394
+ });
13395
+ return;
13396
+ }
13397
+ const total = node.sizes.reduce((sum, w) => sum + w, 0);
13398
+ const safe = total > 0 ? total : node.children.length || 1;
13399
+ let offset = 0;
13400
+ node.children.forEach((child, i) => {
13401
+ const frac = (node.sizes[i] ?? safe / node.children.length) / safe;
13402
+ if (node.direction === `row`) {
13403
+ const w = rect.w * frac;
13404
+ visit(child, [...path, i], {
13405
+ x: rect.x + offset,
13406
+ y: rect.y,
13407
+ w,
13408
+ h: rect.h
13409
+ });
13410
+ offset += w;
13411
+ } else {
13412
+ const h = rect.h * frac;
13413
+ visit(child, [...path, i], {
13414
+ x: rect.x,
13415
+ y: rect.y + offset,
13416
+ w: rect.w,
13417
+ h
13418
+ });
13419
+ offset += h;
13420
+ }
13421
+ });
13422
+ };
13423
+ visit(root, [], {
13424
+ x: 0,
13425
+ y: 0,
13426
+ w: 1,
13427
+ h: 1
13428
+ });
13429
+ return out;
13430
+ }
13431
+ function intervalOverlap(a0, a1, b0, b1) {
13432
+ return Math.min(a1, b1) - Math.max(a0, b0);
13433
+ }
13434
+ /**
13435
+ * Geometric neighbours of a leaf — what the user sees above / below / left /
13436
+ * right of it. Each list holds `{ name, path }` (name is null for unnamed
13437
+ * leaves), ordered most-overlapping first. Returns null when `ref` is
13438
+ * unresolvable.
13439
+ */
13440
+ function getLeafNeighbours(root, ref) {
13441
+ const path = resolveLeafPath(root, ref);
13442
+ if (path === null) return null;
13443
+ const geoms = computeLeafGeoms(root);
13444
+ const self = geoms.find((g) => pathsEqual(g.path, path));
13445
+ if (!self) return null;
13446
+ const s = self.rect;
13447
+ const above = [];
13448
+ const below = [];
13449
+ const left = [];
13450
+ const right = [];
13451
+ geoms.forEach((g, order) => {
13452
+ if (g === self) return;
13453
+ const r = g.rect;
13454
+ const info = {
13455
+ name: g.name,
13456
+ path: [...g.path]
13457
+ };
13458
+ if (Math.abs(r.y + r.h - s.y) < GEOM_EPS) {
13459
+ const o = intervalOverlap(r.x, r.x + r.w, s.x, s.x + s.w);
13460
+ if (o > GEOM_EPS) above.push({
13461
+ info,
13462
+ overlap: o,
13463
+ order
13464
+ });
13465
+ }
13466
+ if (Math.abs(s.y + s.h - r.y) < GEOM_EPS) {
13467
+ const o = intervalOverlap(r.x, r.x + r.w, s.x, s.x + s.w);
13468
+ if (o > GEOM_EPS) below.push({
13469
+ info,
13470
+ overlap: o,
13471
+ order
13472
+ });
13473
+ }
13474
+ if (Math.abs(r.x + r.w - s.x) < GEOM_EPS) {
13475
+ const o = intervalOverlap(r.y, r.y + r.h, s.y, s.y + s.h);
13476
+ if (o > GEOM_EPS) left.push({
13477
+ info,
13478
+ overlap: o,
13479
+ order
13480
+ });
13481
+ }
13482
+ if (Math.abs(s.x + s.w - r.x) < GEOM_EPS) {
13483
+ const o = intervalOverlap(r.y, r.y + r.h, s.y, s.y + s.h);
13484
+ if (o > GEOM_EPS) right.push({
13485
+ info,
13486
+ overlap: o,
13487
+ order
13488
+ });
13489
+ }
13490
+ });
13491
+ const rank = (hits) => hits.sort((a, b) => b.overlap - a.overlap || a.order - b.order).map((h) => h.info);
13492
+ return {
13493
+ above: rank(above),
13494
+ below: rank(below),
13495
+ left: rank(left),
13496
+ right: rank(right)
13497
+ };
13498
+ }
13499
+ /**
13500
+ * Exchange two leaves' positions in the tree, each carrying its size slot so
13501
+ * the remaining structure and proportions are untouched. Returns false when
13502
+ * either path is not a stack leaf.
13503
+ */
13504
+ function swapLeaves(root, pathA, pathB) {
13505
+ if (pathsEqual(pathA, pathB)) return false;
13506
+ const nodeA = findNode(root, pathA);
13507
+ const nodeB = findNode(root, pathB);
13508
+ if (nodeA === null || nodeB === null || !isStack(nodeA) || !isStack(nodeB)) return false;
13509
+ const parentA = findParentOf(root, pathA);
13510
+ const parentB = findParentOf(root, pathB);
13511
+ if (parentA === null || parentB === null) return false;
13512
+ const indexA = pathA[pathA.length - 1];
13513
+ const indexB = pathB[pathB.length - 1];
13514
+ if (parentA === parentB) {
13515
+ const p = parentA;
13516
+ [p.children[indexA], p.children[indexB]] = [p.children[indexB], p.children[indexA]];
13517
+ [p.sizes[indexA], p.sizes[indexB]] = [p.sizes[indexB] ?? 1, p.sizes[indexA] ?? 1];
13518
+ return true;
13519
+ }
13520
+ const carriedNode = parentA.children[indexA];
13521
+ const carriedSize = parentA.sizes[indexA] ?? 1;
13522
+ parentA.children[indexA] = parentB.children[indexB];
13523
+ parentA.sizes[indexA] = parentB.sizes[indexB] ?? 1;
13524
+ parentB.children[indexB] = carriedNode;
13525
+ parentB.sizes[indexB] = carriedSize;
13526
+ return true;
13527
+ }
13528
+ /**
13529
+ * Move a leaf one position in `direction`, swapping with the neighbour it
13530
+ * shares the longest edge with. Returns false when `ref` is unresolvable or
13531
+ * nothing adjoins in that direction. Run `normalize()` afterwards.
13532
+ */
13533
+ function moveLeafStep(root, ref, direction) {
13534
+ const path = resolveLeafPath(root, ref);
13535
+ if (path === null) return false;
13536
+ const neighbours = getLeafNeighbours(root, ref);
13537
+ if (neighbours === null) return false;
13538
+ const list = direction === `up` ? neighbours.above : direction === `down` ? neighbours.below : direction === `left` ? neighbours.left : neighbours.right;
13539
+ if (list.length === 0) return false;
13540
+ return swapLeaves(root, path, list[0].path);
13541
+ }
13542
+ /**
13543
+ * Move a leaf to an edge of the overall layout, keeping the rest of the
13544
+ * tree intact as a single subtree and preserving the leaf's absolute size
13545
+ * along the target axis. Returns false when `ref` is unresolvable or the
13546
+ * layout holds a single leaf. Run `normalize()` afterwards (also safe to
13547
+ * call it twice — the host's commit does).
13548
+ */
13549
+ function moveLeafNodeToEdge(root, ref, edge) {
13550
+ const path = resolveLeafPath(root, ref);
13551
+ if (path === null || path.length === 0) return false;
13552
+ const self = computeLeafGeoms(root).find((g) => pathsEqual(g.path, path));
13553
+ if (!self) return false;
13554
+ const horizontal = edge === `left` || edge === `right`;
13555
+ const frac = Math.min(Math.max(horizontal ? self.rect.w : self.rect.h, .05), .95);
13556
+ if (!(frac > 0) || frac >= 1) return false;
13557
+ const parent = findParentOf(root, path);
13558
+ if (parent === null) return false;
13559
+ const [node] = parent.children.splice(path[path.length - 1], 1);
13560
+ parent.sizes.splice(path[path.length - 1], 1);
13561
+ if (!node) return false;
13562
+ normalize(root);
13563
+ const atStart = edge === `left` || edge === `top`;
13564
+ const direction = horizontal ? `row` : `column`;
13565
+ if (isStack(root)) {
13566
+ const rest = root;
13567
+ const remaining = {
13568
+ kind: `stack`,
13569
+ panels: [...rest.panels],
13570
+ active: rest.active
13571
+ };
13572
+ if (rest.name !== void 0) remaining.name = rest.name;
13573
+ if (rest.collapsed !== void 0) remaining.collapsed = rest.collapsed;
13574
+ if (rest.preCollapseSize !== void 0) remaining.preCollapseSize = rest.preCollapseSize;
13575
+ replaceNodeContents(root, {
13576
+ kind: `split`,
13577
+ direction,
13578
+ children: atStart ? [node, remaining] : [remaining, node],
13579
+ sizes: atStart ? [frac, 1 - frac] : [1 - frac, frac]
13580
+ });
13581
+ return true;
13582
+ }
13583
+ if (isSplit(root) && root.direction === direction) {
13584
+ const size = frac * (root.sizes.reduce((total, w) => total + w, 0) || root.children.length || 1) / (1 - frac);
13585
+ const at = atStart ? 0 : root.children.length;
13586
+ root.children.splice(at, 0, node);
13587
+ root.sizes.splice(at, 0, size);
13588
+ return true;
13589
+ }
13590
+ if (isSplit(root)) {
13591
+ const inner = {
13592
+ kind: `split`,
13593
+ direction: root.direction,
13594
+ children: root.children,
13595
+ sizes: root.sizes
13596
+ };
13597
+ replaceNodeContents(root, {
13598
+ kind: `split`,
13599
+ direction,
13600
+ children: atStart ? [node, inner] : [inner, node],
13601
+ sizes: atStart ? [frac, 1 - frac] : [1 - frac, frac]
13602
+ });
13603
+ return true;
13604
+ }
13605
+ return false;
13606
+ }
13200
13607
  function serializeLayout(root) {
13201
13608
  return {
13202
13609
  version: 1,
@@ -13210,6 +13617,8 @@ function serializeNode(node) {
13210
13617
  panels: [...node.panels],
13211
13618
  active: node.active
13212
13619
  };
13620
+ if (node.name !== void 0) out.name = node.name;
13621
+ if (node.hideLabels !== void 0) out.hideLabels = node.hideLabels;
13213
13622
  if (node.collapsed !== void 0) out.collapsed = node.collapsed;
13214
13623
  if (node.preCollapseSize !== void 0) out.preCollapseSize = node.preCollapseSize;
13215
13624
  return out;
@@ -14259,14 +14668,12 @@ let TabListElement = class TabListElement extends i {
14259
14668
  if (!el) return;
14260
14669
  if (!this.#isVertical) {
14261
14670
  if (event.deltaY === 0 || el.scrollWidth <= el.clientWidth) return;
14262
- const prev = el.scrollLeft;
14263
- el.scrollLeft = prev + event.deltaY;
14264
- if (el.scrollLeft !== prev) event.preventDefault();
14671
+ el.scrollLeft = el.scrollLeft + event.deltaY;
14672
+ event.preventDefault();
14265
14673
  } else {
14266
14674
  if (event.deltaX === 0 || el.scrollHeight <= el.clientHeight) return;
14267
- const prev = el.scrollTop;
14268
- el.scrollTop = prev + event.deltaX;
14269
- if (el.scrollTop !== prev) event.preventDefault();
14675
+ el.scrollTop = el.scrollTop + event.deltaX;
14676
+ event.preventDefault();
14270
14677
  }
14271
14678
  };
14272
14679
  this.#onSlotClick = (event) => {
@@ -14353,8 +14760,11 @@ let TabListElement = class TabListElement extends i {
14353
14760
  return this.orientation === `vertical-stack` || this.orientation === `vertical-side`;
14354
14761
  }
14355
14762
  get #containerClass() {
14356
- const base = this.scrollable ? `${this.orientation} scrollable` : this.orientation;
14357
- return this.dragMode === `reorder` ? `${base} reorder` : base;
14763
+ return [
14764
+ this.scrollable ? `${this.orientation} scrollable` : this.orientation,
14765
+ this.scrollable ? this.#isVertical ? `scroll-fade-y` : `scroll-fade-x` : ``,
14766
+ this.dragMode === `reorder` ? `reorder` : ``
14767
+ ].filter(Boolean).join(` `);
14358
14768
  }
14359
14769
  render() {
14360
14770
  const caretStyle = this.#computeCaretStyle();
@@ -14636,7 +15046,11 @@ let TabListElement = class TabListElement extends i {
14636
15046
  return this.shadowRoot?.getElementById(`container`) ?? null;
14637
15047
  }
14638
15048
  static {
14639
- this.styles = [themeFallbacks, i$3`
15049
+ this.styles = [
15050
+ themeFallbacks,
15051
+ scrollFadeStyles(`x`),
15052
+ scrollFadeStyles(`y`),
15053
+ i$3`
14640
15054
  :host {
14641
15055
  display: inline-block;
14642
15056
  --activated-colour: var(--accent);
@@ -14719,8 +15133,12 @@ let TabListElement = class TabListElement extends i {
14719
15133
  overflow: hidden;
14720
15134
  }
14721
15135
  #container.horizontal.scrollable {
14722
- overflow-x: auto;
15136
+ overflow-x: scroll;
14723
15137
  --allow-overflow: visible;
15138
+ scrollbar-width: none;
15139
+ }
15140
+ #container.horizontal.scrollable::-webkit-scrollbar {
15141
+ display: none;
14724
15142
  }
14725
15143
  #container.horizontal.scrollable ::slotted(ixfx-tab-list-item) {
14726
15144
  flex-shrink: 0;
@@ -14733,9 +15151,13 @@ let TabListElement = class TabListElement extends i {
14733
15151
  flex-direction: column-reverse;
14734
15152
  }
14735
15153
  #container.vertical-stack.scrollable {
14736
- overflow-y: auto;
15154
+ overflow-y: scroll;
14737
15155
  max-height: var(--scroll-max-height);
14738
15156
  --allow-overflow: visible;
15157
+ scrollbar-width: none;
15158
+ }
15159
+ #container.vertical-stack.scrollable::-webkit-scrollbar {
15160
+ display: none;
14739
15161
  }
14740
15162
  #container.vertical-side {
14741
15163
  flex-direction: column;
@@ -14745,9 +15167,13 @@ let TabListElement = class TabListElement extends i {
14745
15167
  flex-direction: column-reverse;
14746
15168
  }
14747
15169
  #container.vertical-side.scrollable {
14748
- overflow-y: auto;
15170
+ overflow-y: scroll;
14749
15171
  max-height: var(--scroll-max-height);
14750
15172
  --allow-overflow: visible;
15173
+ scrollbar-width: none;
15174
+ }
15175
+ #container.vertical-side.scrollable::-webkit-scrollbar {
15176
+ display: none;
14751
15177
  }
14752
15178
 
14753
15179
  ::slotted(ixfx-tab-list-item) {
@@ -14790,7 +15216,8 @@ let TabListElement = class TabListElement extends i {
14790
15216
  ::slotted(.activated) {
14791
15217
  --colour: var(--activated-colour);
14792
15218
  }
14793
- `];
15219
+ `
15220
+ ];
14794
15221
  }
14795
15222
  };
14796
15223
  __decorate([property({ type: String })], TabListElement.prototype, "orientation", void 0);
@@ -14822,9 +15249,13 @@ let TabListItemElement = class TabListItemElement extends i {
14822
15249
  this.registry = void 0;
14823
15250
  this._compact = false;
14824
15251
  this._hasToolbarContent = false;
15252
+ this._labelObserver = null;
14825
15253
  this._onToolbarSlotChange = () => {
14826
15254
  this._updateToolbarContent();
14827
15255
  };
15256
+ this._onLabelSlotChange = () => {
15257
+ this._syncLabelSizer();
15258
+ };
14828
15259
  }
14829
15260
  connectedCallback() {
14830
15261
  super.connectedCallback();
@@ -14833,6 +15264,13 @@ let TabListItemElement = class TabListItemElement extends i {
14833
15264
  });
14834
15265
  this._resizeObserver.observe(this);
14835
15266
  (this.shadowRoot?.querySelector(`slot[name="toolbar"]`))?.addEventListener(`slotchange`, this._onToolbarSlotChange);
15267
+ (this.shadowRoot?.querySelector(`slot:not([name])`))?.addEventListener(`slotchange`, this._onLabelSlotChange);
15268
+ this._labelObserver = new MutationObserver(() => this._syncLabelSizer());
15269
+ this._labelObserver.observe(this, {
15270
+ childList: true,
15271
+ characterData: true,
15272
+ subtree: true
15273
+ });
14836
15274
  }
14837
15275
  disconnectedCallback() {
14838
15276
  super.disconnectedCallback();
@@ -14840,7 +15278,10 @@ let TabListItemElement = class TabListItemElement extends i {
14840
15278
  this._resizeObserver = void 0;
14841
15279
  this._commandCleanup?.();
14842
15280
  this._commandCleanup = void 0;
15281
+ this._labelObserver?.disconnect();
15282
+ this._labelObserver = null;
14843
15283
  (this.shadowRoot?.querySelector(`slot[name="toolbar"]`))?.removeEventListener(`slotchange`, this._onToolbarSlotChange);
15284
+ (this.shadowRoot?.querySelector(`slot:not([name])`))?.removeEventListener(`slotchange`, this._onLabelSlotChange);
14844
15285
  }
14845
15286
  updated(changed) {
14846
15287
  super.updated(changed);
@@ -14863,12 +15304,25 @@ let TabListItemElement = class TabListItemElement extends i {
14863
15304
  if (id === this.commandActive) this.click();
14864
15305
  });
14865
15306
  }
15307
+ /**
15308
+ * Mirror the default-slot text into `.label[data-label]` for the `::after`
15309
+ * width sizer. Slotted toolbar content is excluded.
15310
+ */
15311
+ _syncLabelSizer() {
15312
+ const slot = this.shadowRoot?.querySelector(`slot:not([name])`);
15313
+ const label = this.shadowRoot?.querySelector(`.label`);
15314
+ if (slot === null || slot === void 0 || label === null || label === void 0) return;
15315
+ const text = slot.assignedNodes().filter((node) => !(node instanceof HTMLElement && node.hasAttribute(`slot`))).map((node) => node.textContent ?? ``).join(``);
15316
+ if (text) label.setAttribute(`data-label`, text);
15317
+ else label.removeAttribute(`data-label`);
15318
+ }
14866
15319
  _updateToolbarContent() {
14867
15320
  const slot = this.shadowRoot?.querySelector(`slot[name="toolbar"]`);
14868
15321
  this._hasToolbarContent = (slot?.assignedElements().length ?? 0) > 0;
14869
15322
  }
14870
15323
  firstUpdated() {
14871
15324
  this._updateToolbarContent();
15325
+ this._syncLabelSizer();
14872
15326
  }
14873
15327
  render() {
14874
15328
  const showLabel = !this.hideLabels || !this.iconName;
@@ -14910,15 +15364,21 @@ let TabListItemElement = class TabListItemElement extends i {
14910
15364
  i$3`
14911
15365
  :host {
14912
15366
  --selected-tab-color: var(--ixfx-selected-tab-color, var(--surface-2-text));
15367
+ --selected-tab-icon-color: var(--ixfx-selected-tab-icon-color, var(--accent));
14913
15368
  --selected-tab-border-width: var(--ixfx-selected-tab-border-width, 1px);
14914
15369
  --selected-tab-weight: var(--ixfx-selected-tab-weight, bold);
15370
+ --selected-tab-text-decoration: var(--ixfx-selected-tab-text-decoration, none);
15371
+ --selected-tab-text-decoration-color: var(--ixfx-selected-tab-text-decoration-color, var(--accent));
15372
+ --selected-tab-text-decoration-thickness: var(--ixfx-selected-tab-text-decoration-thickness, 0px);
15373
+ --selected-tab-text-decoration-offset: var(--ixfx-selected-tab-text-decoration-offset, 2px);
14915
15374
  --selected-background: var(--ixfx-tab-selected-background, var(--surface-5));
14916
15375
  --tab-padding: var(--ixfx-tab-padding, var(--space-s));
14917
15376
  display: inline-flex;
14918
15377
  min-width: 0;
14919
15378
  user-select: none;
14920
15379
  box-sizing: border-box;
14921
- font-size: var(--text-xs);
15380
+ font-size: var(--text-m);
15381
+ color: inherit;
14922
15382
  /* No explicit height: items stretch (align-self: stretch via the
14923
15383
  list's flex container) so every tab in a strip shares one height
14924
15384
  even when toolbars give some items taller content. */
@@ -14933,7 +15393,18 @@ let TabListItemElement = class TabListItemElement extends i {
14933
15393
  font-weight: 500;
14934
15394
  border: var(--selected-tab-border-width) solid transparent;
14935
15395
  gap: var(--space-s);
15396
+ transition: background var(--transition, 0.15s);
15397
+ }
14936
15398
 
15399
+ /* Hover ("tickle") background. When --ixfx-tab-hover-background is
15400
+ unset each state keeps its resting background, so existing
15401
+ consumers see no change. */
15402
+ :host(:hover:not([selected])) > div {
15403
+ background: var(--ixfx-tab-hover-background, transparent);
15404
+ }
15405
+
15406
+ :host([selected]:hover) > div {
15407
+ background: var(--ixfx-tab-hover-background, var(--selected-background));
14937
15408
  }
14938
15409
 
14939
15410
  :host([selected]) > div {
@@ -14947,6 +15418,13 @@ let TabListItemElement = class TabListItemElement extends i {
14947
15418
  border-radius:var(--radius-m);
14948
15419
  }
14949
15420
 
15421
+ :host([selected]) .label {
15422
+ text-decoration: var(--selected-tab-text-decoration);
15423
+ text-decoration-color: var(--selected-tab-text-decoration-color);
15424
+ text-decoration-thickness: var(--selected-tab-text-decoration-thickness);
15425
+ text-decoration-offset: var(--selected-tab-text-decoration-offset);
15426
+ }
15427
+
14950
15428
  .label {
14951
15429
  overflow: var(--allow-overflow, hidden);
14952
15430
  text-overflow: ellipsis;
@@ -14955,10 +15433,29 @@ let TabListItemElement = class TabListItemElement extends i {
14955
15433
  flex: 1 1 auto;
14956
15434
  }
14957
15435
 
15436
+ /* Width sizer: an invisible copy of the label, always set at the
15437
+ selected weight, so activating a tab (bold) never shifts the strip.
15438
+ Text is mirrored from the default slot via the data-label attribute
15439
+ (see _syncLabelSizer). */
15440
+ .label::after {
15441
+ content: attr(data-label);
15442
+ display: block;
15443
+ height: 0;
15444
+ overflow: hidden;
15445
+ visibility: hidden;
15446
+ font-weight: var(--selected-tab-weight);
15447
+ }
15448
+
14958
15449
  ixfx-icon {
14959
15450
  flex-shrink: 0;
14960
15451
  }
14961
15452
 
15453
+ /* Leading icon (direct child — not close/toolbar icons) picks up the
15454
+ accent when selected, so icon-only tabs read as active. */
15455
+ :host([selected]) > div > ixfx-icon {
15456
+ color: var(--selected-tab-icon-color);
15457
+ }
15458
+
14962
15459
  .actions {
14963
15460
  display: flex;
14964
15461
  align-items: center;
@@ -15370,6 +15867,7 @@ let DockPanelElement = class DockPanelElement extends i {
15370
15867
  this.iconName = ``;
15371
15868
  this.closable = false;
15372
15869
  this.collapsible = false;
15870
+ this.kind = `any`;
15373
15871
  this.accepts = ``;
15374
15872
  this.presentation = `solo`;
15375
15873
  this._collapsed = false;
@@ -15423,6 +15921,10 @@ let DockPanelElement = class DockPanelElement extends i {
15423
15921
  }
15424
15922
  connectedCallback() {
15425
15923
  super.connectedCallback();
15924
+ if (this.hasAttribute(`title`)) {
15925
+ if (!this.hasAttribute(`label`)) this.title = this.getAttribute(`title`) ?? ``;
15926
+ this.removeAttribute(`title`);
15927
+ }
15426
15928
  this.addEventListener(`pointerdown`, this._onPointerDown);
15427
15929
  }
15428
15930
  disconnectedCallback() {
@@ -15514,13 +16016,20 @@ __decorate([property({
15514
16016
  attribute: `panel-id`,
15515
16017
  reflect: true
15516
16018
  })], DockPanelElement.prototype, "panelId", void 0);
15517
- __decorate([property({ type: String })], DockPanelElement.prototype, "title", void 0);
16019
+ __decorate([property({
16020
+ type: String,
16021
+ attribute: `label`
16022
+ })], DockPanelElement.prototype, "title", void 0);
15518
16023
  __decorate([property({
15519
16024
  type: String,
15520
16025
  attribute: `icon-name`
15521
16026
  })], DockPanelElement.prototype, "iconName", void 0);
15522
16027
  __decorate([property({ type: Boolean })], DockPanelElement.prototype, "closable", void 0);
15523
16028
  __decorate([property({ type: Boolean })], DockPanelElement.prototype, "collapsible", void 0);
16029
+ __decorate([property({
16030
+ type: String,
16031
+ reflect: true
16032
+ })], DockPanelElement.prototype, "kind", void 0);
15524
16033
  __decorate([property({
15525
16034
  type: String,
15526
16035
  attribute: `accepts`
@@ -15546,19 +16055,23 @@ let DockHostElement = class DockHostElement extends i {
15546
16055
  super(..._args);
15547
16056
  this.layout = null;
15548
16057
  this.accepts = `tabs horizontal vertical`;
16058
+ this.isolated = false;
15549
16059
  this._model = createStack([]);
15550
16060
  this._pool = [];
15551
16061
  this._dropZone = null;
15552
16062
  this._dragActive = false;
15553
16063
  this._junctions = [];
15554
16064
  this._toolbarsOut = /* @__PURE__ */ new Map();
16065
+ this._leafContentRules = /* @__PURE__ */ new Map();
15555
16066
  this._tabDragPending = null;
15556
16067
  this._junctionsKey = ``;
15557
16068
  this._junctionsRaf = null;
15558
16069
  this._hostResizeObserver = null;
16070
+ this._toolbarSlots = /* @__PURE__ */ new Set();
16071
+ this._toolbarObserver = null;
15559
16072
  this._onDragStart = (event) => {
15560
16073
  const { panelId, pointerId, clientX, clientY } = event.detail;
15561
- startDrag(panelId, this, pointerId, clientX, clientY);
16074
+ startDrag(panelId, this, pointerId, clientX, clientY, this._findPanelEl(panelId)?.kind);
15562
16075
  };
15563
16076
  this._onPoolSlotChange = () => {
15564
16077
  this._syncPool();
@@ -15605,11 +16118,12 @@ let DockHostElement = class DockHostElement extends i {
15605
16118
  const pathStr = this.shadowRoot?.getElementById(detail.id)?.getAttribute(`data-dock-path`);
15606
16119
  if (pathStr === null || pathStr === void 0) return;
15607
16120
  const path = parsePath(pathStr);
15608
- const paths = collectStackPaths(this._model).filter((p) => p.join(`-`) !== path.join(`-`));
15609
- const fallbackNode = paths.length > 0 ? findNode(this._model, paths[0]) : null;
15610
- const fallback = fallbackNode !== null && isStack(fallbackNode) ? fallbackNode : void 0;
16121
+ const closing = findNode(this._model, path);
16122
+ if (closing === null || !isStack(closing)) return;
16123
+ const moving = [...closing.panels];
15611
16124
  this._returnAllToolbars();
15612
- extractStack(this._model, path, fallback);
16125
+ extractStack(this._model, path);
16126
+ for (const panelId of moving) addPanelToStack(this._firstStackAllowing(this._panelKind(panelId)), panelId);
15613
16127
  this._commit();
15614
16128
  };
15615
16129
  this._onTabChange = (event, path) => {
@@ -15674,11 +16188,21 @@ let DockHostElement = class DockHostElement extends i {
15674
16188
  this.addEventListener(`dock-panel-drag-start`, this._onDragStart);
15675
16189
  this._hostResizeObserver = new ResizeObserver(() => this._scheduleJunctions());
15676
16190
  this._hostResizeObserver.observe(this);
16191
+ this._scanToolbarSlots();
16192
+ this._toolbarObserver = new MutationObserver(() => this._scanToolbarSlots());
16193
+ this._toolbarObserver.observe(this, {
16194
+ childList: true,
16195
+ subtree: true,
16196
+ attributes: true,
16197
+ attributeFilter: [`slot`]
16198
+ });
15677
16199
  }
15678
16200
  disconnectedCallback() {
15679
16201
  super.disconnectedCallback();
15680
16202
  unregisterDockHost(this);
15681
16203
  this.removeEventListener(`dock-panel-drag-start`, this._onDragStart);
16204
+ this._toolbarObserver?.disconnect();
16205
+ this._toolbarObserver = null;
15682
16206
  this._hostResizeObserver?.disconnect();
15683
16207
  this._hostResizeObserver = null;
15684
16208
  if (this._junctionsRaf !== null) {
@@ -15731,6 +16255,19 @@ let DockHostElement = class DockHostElement extends i {
15731
16255
  _scanPanels() {
15732
16256
  return Array.from(this.children).filter((el) => el instanceof HTMLElement && el.tagName === `IXFX-DOCK-PANEL` && Boolean(el.panelId));
15733
16257
  }
16258
+ /** Refresh the set of provided `toolbar-<name>` slots; re-render on change. */
16259
+ _scanToolbarSlots() {
16260
+ const found = /* @__PURE__ */ new Set();
16261
+ for (const child of this.children) {
16262
+ if (!(child instanceof HTMLElement)) continue;
16263
+ const slot = child.getAttribute(`slot`) ?? ``;
16264
+ if (slot.startsWith(`toolbar-`) && slot.length > `toolbar-`.length) found.add(slot);
16265
+ }
16266
+ if (found.size !== this._toolbarSlots.size || [...found].some((s) => !this._toolbarSlots.has(s))) {
16267
+ this._toolbarSlots = found;
16268
+ this.requestUpdate();
16269
+ }
16270
+ }
15734
16271
  _findPanelEl(panelId) {
15735
16272
  return this._pool.find((p) => p.panelId === panelId) ?? this._scanPanels().find((p) => p.panelId === panelId);
15736
16273
  }
@@ -15744,7 +16281,33 @@ let DockHostElement = class DockHostElement extends i {
15744
16281
  return fresh;
15745
16282
  }
15746
16283
  _appendPanelToFirstStack(panelId) {
15747
- addPanelToStack(this._model.kind === `stack` ? this._model : this._firstStack(), panelId);
16284
+ const kind = this._panelKind(panelId);
16285
+ if (this._model.kind === `stack` && this._stackAllowsKind(this._model, kind)) {
16286
+ addPanelToStack(this._model, panelId);
16287
+ return;
16288
+ }
16289
+ addPanelToStack(this._firstStackAllowing(kind), panelId);
16290
+ }
16291
+ /** Normalized content kind of a panel (`'any'` when unknown/unset). */
16292
+ _panelKind(panelId) {
16293
+ return normalizeKind(this._findPanelEl(panelId)?.kind);
16294
+ }
16295
+ /** Whether `kind` may live in `stack` under the current leaf rules. */
16296
+ _stackAllowsKind(stack, kind) {
16297
+ if (stack.name === void 0) return true;
16298
+ const rules = this._leafContentRules.get(stack.name);
16299
+ if (rules === void 0) return true;
16300
+ if (rules.deny.has(kind)) return false;
16301
+ return rules.allow === null || rules.allow.has(kind);
16302
+ }
16303
+ /** First stack (tree order) whose rules admit `kind`, else the first stack. */
16304
+ _firstStackAllowing(kind) {
16305
+ for (const path of collectStackPaths(this._model)) {
16306
+ const node = findNode(this._model, path);
16307
+ if (node !== null && isStack(node) && this._stackAllowsKind(node, kind)) return node;
16308
+ }
16309
+ console.warn(`[ixfx-dock-host] no leaf admits kind "${kind}", using first stack`);
16310
+ return this._firstStack();
15748
16311
  }
15749
16312
  _syncPool() {
15750
16313
  const current = this._scanPanels();
@@ -15786,7 +16349,34 @@ let DockHostElement = class DockHostElement extends i {
15786
16349
  this.requestUpdate();
15787
16350
  this._emitChange();
15788
16351
  }
16352
+ /**
16353
+ * Dock a panel. Returns false (without touching the DOM) when the
16354
+ * explicitly targeted container denies the panel's `kind`.
16355
+ */
15789
16356
  addPanel(panel, target) {
16357
+ const kind = panel instanceof HTMLElement ? normalizeKind(panel.kind) : normalizeKind(panel.kind);
16358
+ const op = target?.op ?? `tabs`;
16359
+ let stackPath = null;
16360
+ let splitTarget = null;
16361
+ if (target?.container !== void 0) {
16362
+ const namedPath = findStackPathByName(this._model, target.container);
16363
+ if (namedPath === null) console.warn(`[ixfx-dock-host] addPanel: unknown container "${target.container}", appending to first admitting stack`);
16364
+ else if (op === `tabs` && !this._stackAllowsKindAt(namedPath, kind)) {
16365
+ console.warn(`[ixfx-dock-host] addPanel: container "${target.container}" does not admit kind "${kind}"`);
16366
+ return false;
16367
+ } else stackPath = namedPath;
16368
+ } else if (target?.stack) {
16369
+ const found = findStackOf(this._model, target.stack);
16370
+ if (found === null) console.warn(`[ixfx-dock-host] addPanel: unknown stack panel "${target.stack}", appending to first admitting stack`);
16371
+ else if (op === `tabs` && !this._stackAllowsKindAt(found, kind)) {
16372
+ console.warn(`[ixfx-dock-host] addPanel: target stack does not admit kind "${kind}"`);
16373
+ return false;
16374
+ } else stackPath = found;
16375
+ }
16376
+ if (stackPath !== null && op !== `tabs`) {
16377
+ splitTarget = stackPath;
16378
+ stackPath = null;
16379
+ }
15790
16380
  let el;
15791
16381
  if (panel instanceof HTMLElement) el = panel;
15792
16382
  else {
@@ -15796,19 +16386,16 @@ let DockHostElement = class DockHostElement extends i {
15796
16386
  if (panel.iconName !== void 0) el.iconName = panel.iconName;
15797
16387
  if (panel.closable !== void 0) el.closable = panel.closable;
15798
16388
  if (panel.collapsible !== void 0) el.collapsible = panel.collapsible;
16389
+ if (panel.kind !== void 0) el.kind = panel.kind;
15799
16390
  }
15800
16391
  if (el.parentElement !== this) this.appendChild(el);
15801
16392
  if (!this._pool.some((p) => p.panelId === el.panelId)) this._pool = [...this._pool, el];
15802
16393
  this._returnAllToolbars();
15803
- const op = target?.op ?? `tabs`;
15804
- if (target?.stack) {
15805
- const stackPath = findStackOf(this._model, target.stack);
15806
- if (stackPath !== null) {
15807
- if (op === `tabs`) {
15808
- const stack = findNode(this._model, stackPath);
15809
- if (stack !== null && isStack(stack)) addPanelToStack(stack, el.panelId, target.index);
15810
- } else insertBeside(this._model, stackPath, opToDirection(op), createStack([el.panelId]), `after`);
15811
- }
16394
+ if (collectPanelIds(this._model).includes(el.panelId)) this._removeFromModel(el.panelId);
16395
+ if (splitTarget !== null) insertBeside(this._model, splitTarget, opToDirection(op), createStack([el.panelId]), `after`);
16396
+ else if (stackPath !== null) {
16397
+ const stack = findNode(this._model, stackPath);
16398
+ if (stack !== null && isStack(stack)) addPanelToStack(stack, el.panelId, target?.index);
15812
16399
  } else this._appendPanelToFirstStack(el.panelId);
15813
16400
  normalize(this._model);
15814
16401
  this.requestUpdate();
@@ -15818,6 +16405,12 @@ let DockHostElement = class DockHostElement extends i {
15818
16405
  composed: true
15819
16406
  }));
15820
16407
  this._emitChange();
16408
+ return true;
16409
+ }
16410
+ /** Whether the stack at `stackPath` admits `kind` under the leaf rules. */
16411
+ _stackAllowsKindAt(stackPath, kind) {
16412
+ const stack = findNode(this._model, stackPath);
16413
+ return stack !== null && isStack(stack) && this._stackAllowsKind(stack, kind);
15821
16414
  }
15822
16415
  removePanel(panelId) {
15823
16416
  if (!collectPanelIds(this._model).includes(panelId)) return false;
@@ -15853,6 +16446,123 @@ let DockHostElement = class DockHostElement extends i {
15853
16446
  getPanelIds() {
15854
16447
  return collectPanelIds(this._model);
15855
16448
  }
16449
+ /** Names of every named stack leaf, in tree order. */
16450
+ getContainerNames() {
16451
+ return collectContainerNames(this._model);
16452
+ }
16453
+ /**
16454
+ * Snapshot of a named container (`{ name, panels, active }`), or null when
16455
+ * no stack carries that name. First match wins when duplicated.
16456
+ */
16457
+ getContainer(name) {
16458
+ const path = findStackPathByName(this._model, name);
16459
+ if (path === null) return null;
16460
+ const node = findNode(this._model, path);
16461
+ if (node === null || !isStack(node) || node.name === void 0) return null;
16462
+ return {
16463
+ name: node.name,
16464
+ panels: [...node.panels],
16465
+ active: node.active
16466
+ };
16467
+ }
16468
+ /** Panel-ids in a named container, in tab order (`[]` when unknown/empty). */
16469
+ getContainerPanels(name) {
16470
+ return this.getContainer(name)?.panels ?? [];
16471
+ }
16472
+ /** Panel elements in a named container, in tab order — for iterating children. */
16473
+ getContainerElements(name) {
16474
+ return this.getContainerPanels(name).map((id) => this._findPanelEl(id)).filter((el) => el !== void 0);
16475
+ }
16476
+ /**
16477
+ * Restrict a named container to only the given panel kinds — e.g.
16478
+ * `setLeafExclusiveContents('editor', ['document'])`. `tabs` drops,
16479
+ * `addPanel`, and re-homing into the leaf then refuse other kinds, while
16480
+ * edge splits beside the leaf stay allowed (they create a new leaf).
16481
+ * Pass `[]` to seal the leaf (nothing admitted). Warns when no stack
16482
+ * currently carries `name`, but the rule is still stored and applies once
16483
+ * one appears. Rules are host policy: kept across `loadLayout()`, never
16484
+ * serialized.
16485
+ */
16486
+ setLeafExclusiveContents(name, kinds) {
16487
+ const key = name.trim();
16488
+ if (!key) {
16489
+ console.warn(`[ixfx-dock-host] setLeafExclusiveContents: empty container name`);
16490
+ return;
16491
+ }
16492
+ const allow = new Set(kinds.map((k) => k.trim()).filter((k) => k.length > 0));
16493
+ const entry = this._leafContentRules.get(key) ?? {
16494
+ allow: null,
16495
+ deny: /* @__PURE__ */ new Set()
16496
+ };
16497
+ entry.allow = allow;
16498
+ this._leafContentRules.set(key, entry);
16499
+ if (findStackPathByName(this._model, key) === null) console.warn(`[ixfx-dock-host] setLeafExclusiveContents: no container named "${key}" yet; rule stored`);
16500
+ }
16501
+ /**
16502
+ * Forbid the given panel kinds in a named container — e.g.
16503
+ * `setLeafDenyContents('footer', ['files', 'document'])`. Everything else
16504
+ * stays admitted. Pass `[]` to lift the denial. See
16505
+ * {@link setLeafExclusiveContents} for scope/ordering notes.
16506
+ */
16507
+ setLeafDenyContents(name, kinds) {
16508
+ const key = name.trim();
16509
+ if (!key) {
16510
+ console.warn(`[ixfx-dock-host] setLeafDenyContents: empty container name`);
16511
+ return;
16512
+ }
16513
+ const deny = new Set(kinds.map((k) => k.trim()).filter((k) => k.length > 0));
16514
+ const entry = this._leafContentRules.get(key) ?? {
16515
+ allow: null,
16516
+ deny: /* @__PURE__ */ new Set()
16517
+ };
16518
+ entry.deny = deny;
16519
+ if (entry.allow === null && entry.deny.size === 0) this._leafContentRules.delete(key);
16520
+ else this._leafContentRules.set(key, entry);
16521
+ if (findStackPathByName(this._model, key) === null) console.warn(`[ixfx-dock-host] setLeafDenyContents: no container named "${key}" yet; rule stored`);
16522
+ }
16523
+ /** Remove every content rule for a named container. */
16524
+ clearLeafContentRestrictions(name) {
16525
+ this._leafContentRules.delete(name.trim());
16526
+ }
16527
+ /** Copy of a container's rules, or null when unrestricted. */
16528
+ getLeafContentRestrictions(name) {
16529
+ const rules = this._leafContentRules.get(name.trim());
16530
+ if (rules === void 0) return null;
16531
+ return {
16532
+ allow: rules.allow === null ? null : [...rules.allow],
16533
+ deny: [...rules.deny]
16534
+ };
16535
+ }
16536
+ /**
16537
+ * Geometric neighbours of a leaf (`name` or numeric `path`) — what sits
16538
+ * above / below / left / right of it on screen. Each entry is
16539
+ * `{ name, path }` (`name` is null for unnamed leaves), ordered
16540
+ * most-overlapping first. Returns null when the leaf is unknown.
16541
+ */
16542
+ getNeighbours(leaf) {
16543
+ return getLeafNeighbours(this._model, leaf);
16544
+ }
16545
+ /**
16546
+ * Move a leaf one position in `direction`, swapping with the neighbour it
16547
+ * shares the longest edge with (both keep the remaining structure intact).
16548
+ * Returns false when the leaf is unknown or nothing adjoins that way.
16549
+ */
16550
+ moveLeaf(leaf, direction) {
16551
+ if (!moveLeafStep(this._model, leaf, direction)) return false;
16552
+ this._commit();
16553
+ return true;
16554
+ }
16555
+ /**
16556
+ * Move a leaf to an edge of the overall layout (e.g. toggle a sidebar from
16557
+ * `left` to `right`). The rest of the layout stays intact as a single
16558
+ * subtree and the leaf keeps its absolute size along the target axis.
16559
+ * Returns false when the leaf is unknown or the layout holds one leaf.
16560
+ */
16561
+ moveLeafToEdge(leaf, edge) {
16562
+ if (!moveLeafNodeToEdge(this._model, leaf, edge)) return false;
16563
+ this._commit();
16564
+ return true;
16565
+ }
15856
16566
  /**
15857
16567
  * Capture a structured-clone-safe {@link DockPanelSnapshot} of a panel
15858
16568
  * (identity, chrome attributes, and a best-effort `innerHTML`). Returns
@@ -15867,6 +16577,7 @@ let DockHostElement = class DockHostElement extends i {
15867
16577
  iconName: el.iconName,
15868
16578
  closable: el.closable,
15869
16579
  collapsible: el.collapsible,
16580
+ kind: normalizeKind(el.kind),
15870
16581
  accepts: el.accepts,
15871
16582
  contentHTML: el.innerHTML
15872
16583
  };
@@ -15890,10 +16601,11 @@ let DockHostElement = class DockHostElement extends i {
15890
16601
  * `accepts` / `canAccept`. Pure query: no model mutation, no overlay.
15891
16602
  *
15892
16603
  * `panelId` is passed to `canAccept`; use any stable id for an external
15893
- * panel (defaults to `"__external__"`).
16604
+ * panel (defaults to `"__external__"`). `kind` covers leaf content rules
16605
+ * for panels unknown to this host (defaults to `'any'`).
15894
16606
  */
15895
- queryDropTarget(clientX, clientY, panelId = `__external__`) {
15896
- const zone = this.computeDropZone(clientX, clientY, panelId);
16607
+ queryDropTarget(clientX, clientY, panelId = `__external__`, kind) {
16608
+ const zone = this.computeDropZone(clientX, clientY, panelId, kind);
15897
16609
  return zone === null ? null : zoneToTarget(zone);
15898
16610
  }
15899
16611
  /**
@@ -15902,9 +16614,9 @@ let DockHostElement = class DockHostElement extends i {
15902
16614
  * one. Call on every pointer update from the outer coordinator, then
15903
16615
  * {@link endExternalDrop} when the drag leaves or ends.
15904
16616
  */
15905
- previewExternalDrop(clientX, clientY, panelId = `__external__`) {
16617
+ previewExternalDrop(clientX, clientY, panelId = `__external__`, kind) {
15906
16618
  this.setDragActive(true);
15907
- const zone = this.computeDropZone(clientX, clientY, panelId);
16619
+ const zone = this.computeDropZone(clientX, clientY, panelId, kind);
15908
16620
  this.setDropZone(zone);
15909
16621
  return zone === null ? null : zoneToTarget(zone);
15910
16622
  }
@@ -15917,8 +16629,9 @@ let DockHostElement = class DockHostElement extends i {
15917
16629
  * Recreate a panel from a {@link DockPanelSnapshot} (or a bare
15918
16630
  * {@link DockPanelInit}) and dock it. With `at` viewport coordinates the
15919
16631
  * panel lands where a drop there would put it (respecting `accepts` /
15920
- * `canAccept`); without them, or when the point misses the host, it goes
15921
- * into the first stack. Returns the created element, or null when a panel
16632
+ * `canAccept` and leaf content rules); without them, or when the point
16633
+ * misses the host, it goes into the first stack admitting its kind.
16634
+ * Returns the created element, or null when a panel
15922
16635
  * with that id is already docked here.
15923
16636
  */
15924
16637
  acceptExternalPanel(snapshot, at) {
@@ -15929,6 +16642,7 @@ let DockHostElement = class DockHostElement extends i {
15929
16642
  if (snapshot.iconName !== void 0) el.iconName = snapshot.iconName;
15930
16643
  if (snapshot.closable !== void 0) el.closable = snapshot.closable;
15931
16644
  if (snapshot.collapsible !== void 0) el.collapsible = snapshot.collapsible;
16645
+ if (`kind` in snapshot && snapshot.kind !== void 0) el.kind = snapshot.kind;
15932
16646
  if (`accepts` in snapshot && snapshot.accepts) el.setAttribute(`accepts`, snapshot.accepts);
15933
16647
  if (`contentHTML` in snapshot && snapshot.contentHTML) el.innerHTML = snapshot.contentHTML;
15934
16648
  this._returnAllToolbars();
@@ -15954,9 +16668,10 @@ let DockHostElement = class DockHostElement extends i {
15954
16668
  * a zone with rects relative to the host, or null when the pointer is
15955
16669
  * outside the host or the candidate zone is disallowed.
15956
16670
  */
15957
- computeDropZone(clientX, clientY, panelId) {
16671
+ computeDropZone(clientX, clientY, panelId, kind) {
15958
16672
  const hostRect = this.getBoundingClientRect();
15959
16673
  if (clientX < hostRect.left || clientX > hostRect.right || clientY < hostRect.top || clientY > hostRect.bottom) return null;
16674
+ const resolvedKind = kind?.trim() ? kind.trim() : this._panelKind(panelId);
15960
16675
  const rel = (r) => ({
15961
16676
  x: r.left - hostRect.left,
15962
16677
  y: r.top - hostRect.top,
@@ -15984,7 +16699,7 @@ let DockHostElement = class DockHostElement extends i {
15984
16699
  rect: rel(leafRect),
15985
16700
  caretRect: this._stripCaretRect(strip, gapIndex, hostRect)
15986
16701
  };
15987
- return this._zoneAllowed(panelId, zone) ? zone : null;
16702
+ return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
15988
16703
  }
15989
16704
  }
15990
16705
  const relX = (clientX - leafRect.left) / Math.max(leafRect.width, 1);
@@ -15998,7 +16713,7 @@ let DockHostElement = class DockHostElement extends i {
15998
16713
  gapIndex: stack.panels.length,
15999
16714
  rect: rel(leafRect)
16000
16715
  };
16001
- return this._zoneAllowed(panelId, zone) ? zone : null;
16716
+ return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
16002
16717
  }
16003
16718
  const r = rel(leafRect);
16004
16719
  let operation;
@@ -16044,7 +16759,7 @@ let DockHostElement = class DockHostElement extends i {
16044
16759
  side,
16045
16760
  rect: zoneRect
16046
16761
  };
16047
- return this._zoneAllowed(panelId, zone) ? zone : null;
16762
+ return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
16048
16763
  }
16049
16764
  const fromLeft = clientX - hostRect.left;
16050
16765
  const fromRight = hostRect.right - clientX;
@@ -16100,15 +16815,18 @@ let DockHostElement = class DockHostElement extends i {
16100
16815
  side,
16101
16816
  rect: zoneRect
16102
16817
  };
16103
- return this._zoneAllowed(panelId, zone) ? zone : null;
16818
+ return this._zoneAllowed(panelId, zone, resolvedKind) ? zone : null;
16104
16819
  }
16105
- _zoneAllowed(panelId, zone) {
16820
+ _zoneAllowed(panelId, zone, kind) {
16106
16821
  if (!tokenAllows(this.accepts, operationToToken(zone.operation))) return false;
16107
16822
  const stack = findNode(this._model, zone.targetPath);
16108
16823
  if (stack !== null && isStack(stack) && stack.panels.length === 1) {
16109
16824
  const target = this._findPanelEl(stack.panels[0]);
16110
16825
  if (target && !panelAllows(target.accepts, operationToToken(zone.operation))) return false;
16111
16826
  }
16827
+ if (zone.operation === `tabs` && stack !== null && isStack(stack)) {
16828
+ if (!this._stackAllowsKind(stack, kind?.trim() ? kind.trim() : this._panelKind(panelId))) return false;
16829
+ }
16112
16830
  if (this.canAccept && !this.canAccept(panelId, zone.operation)) return false;
16113
16831
  return true;
16114
16832
  }
@@ -16123,22 +16841,32 @@ let DockHostElement = class DockHostElement extends i {
16123
16841
  this._appendPanelToFirstStack(panelId);
16124
16842
  normalize(this._model);
16125
16843
  }
16126
- this._applyZone(panelId, zone);
16844
+ this._applyZone(panelId, zone, sourceHost === void 0 || sourceHost === this);
16127
16845
  this._commit();
16128
16846
  }
16129
16847
  /**
16130
16848
  * Move `panelId` (already present somewhere in the model) to the position
16131
16849
  * described by `zone`. Shared by in-page drops and external panel adoption.
16132
16850
  */
16133
- _applyZone(panelId, zone) {
16851
+ _applyZone(panelId, zone, transferName = false) {
16134
16852
  const sourcePath = findStackOf(this._model, panelId);
16853
+ let vacated = null;
16135
16854
  if (sourcePath !== null) {
16136
16855
  const source = findNode(this._model, sourcePath);
16137
- if (source !== null && isStack(source)) removePanelFromStack(source, panelId);
16856
+ if (source !== null && isStack(source)) {
16857
+ removePanelFromStack(source, panelId);
16858
+ if (source.panels.length === 0) vacated = source;
16859
+ }
16138
16860
  }
16139
16861
  if (zone.operation === `tabs`) {
16140
16862
  const target = findNode(this._model, zone.targetPath);
16141
- if (target !== null && isStack(target)) addPanelToStack(target, panelId, zone.gapIndex);
16863
+ if (target !== null && isStack(target)) {
16864
+ addPanelToStack(target, panelId, zone.gapIndex);
16865
+ if (transferName && vacated !== null && vacated !== target && vacated.name !== void 0 && target.name === void 0) {
16866
+ target.name = vacated.name;
16867
+ delete vacated.name;
16868
+ }
16869
+ }
16142
16870
  } else insertBeside(this._model, zone.targetPath, opToDirection(zone.operation), createStack([panelId]), zone.side);
16143
16871
  }
16144
16872
  /**
@@ -16274,16 +17002,6 @@ let DockHostElement = class DockHostElement extends i {
16274
17002
  };
16275
17003
  window.addEventListener(`click`, swallow, true);
16276
17004
  }
16277
- _splitStack(path, op) {
16278
- const stack = findNode(this._model, path);
16279
- if (stack === null || !isStack(stack) || stack.panels.length === 0) return;
16280
- const activeId = stack.active;
16281
- this._returnAllToolbars();
16282
- removePanelFromStack(stack, activeId);
16283
- normalize(this._model);
16284
- insertBeside(this._model, path, opToDirection(op), createStack([activeId]), `after`);
16285
- this._commit();
16286
- }
16287
17005
  _removeFromModel(panelId) {
16288
17006
  const stackPath = findStackOf(this._model, panelId);
16289
17007
  if (stackPath === null) return;
@@ -16414,6 +17132,7 @@ let DockHostElement = class DockHostElement extends i {
16414
17132
  id=${`dock-leaf-${pathStr}`}
16415
17133
  class="leaf"
16416
17134
  data-dock-path=${pathStr}
17135
+ data-dock-name=${stack.name ?? A}
16417
17136
  size=${size === void 0 ? A : String(size)}
16418
17137
  min=${collapsible ? `1px` : A}
16419
17138
  when-small=${collapsible ? `collapse` : A}
@@ -16425,26 +17144,19 @@ let DockHostElement = class DockHostElement extends i {
16425
17144
  <slot name="empty"><span>Drop panels here</span></slot>
16426
17145
  </div>
16427
17146
  `);
17147
+ const toolbarSlot = stack.name !== void 0 && this._toolbarSlots.has(`toolbar-${stack.name}`) ? b`<slot name="toolbar-${stack.name}" slot="toolbar"></slot>` : A;
16428
17148
  return leafAttrs(b`
16429
17149
  <ixfx-tab-list
16430
17150
  class="strip"
16431
17151
  orientation="horizontal"
17152
+ scrollable
17153
+ shrink
17154
+ ?hide-labels=${stack.hideLabels === true}
16432
17155
  @change=${(e) => this._onTabChange(e, path)}
16433
17156
  @pointerdown=${this._onStripPointerDown}
16434
17157
  >
16435
17158
  ${repeat(stack.panels, (id) => id, (id) => this._renderTabItem(stack, id))}
16436
- <div slot="toolbar" class="group-toolbar">
16437
- <button
16438
- class="chrome-btn"
16439
- title="Split horizontally (top/bottom)"
16440
- @click=${() => this._splitStack(path, `split-horizontal`)}
16441
- >↕</button>
16442
- <button
16443
- class="chrome-btn"
16444
- title="Split vertically (left/right)"
16445
- @click=${() => this._splitStack(path, `split-vertical`)}
16446
- >↔</button>
16447
- </div>
17159
+ ${toolbarSlot}
16448
17160
  </ixfx-tab-list>
16449
17161
  <div class="leaf-body">
16450
17162
  <slot name="dock-panel-${stack.active}"></slot>
@@ -16524,9 +17236,14 @@ let DockHostElement = class DockHostElement extends i {
16524
17236
  .strip {
16525
17237
  flex-shrink: 0;
16526
17238
  border: 0;
16527
- /* The leaf background shows through selected tabs; the accent
16528
- underline indicator carries the selection. */
17239
+ /* The leaf background shows through selected tabs; a text underline
17240
+ (not the border indicator) carries the selection. */
16529
17241
  --ixfx-tab-selected-background: transparent;
17242
+ --ixfx-selected-tab-border-width: 0;
17243
+ --ixfx-selected-tab-text-decoration: underline;
17244
+ --ixfx-selected-tab-text-decoration-thickness: 2px;
17245
+ --ixfx-selected-tab-text-decoration-offset: 4px;
17246
+ --ixfx-tab-hover-background: var(--surface-4);
16530
17247
  }
16531
17248
 
16532
17249
  /* Uniform tab height: a panel toolbar button would otherwise inflate
@@ -16535,6 +17252,11 @@ let DockHostElement = class DockHostElement extends i {
16535
17252
  height: var(--dock-tab-height, 30px);
16536
17253
  }
16537
17254
 
17255
+ /* Breathing room between strip toolbars and the leaf edge. */
17256
+ .strip::part(list-toolbar) {
17257
+ padding-right: var(--space-xs);
17258
+ }
17259
+
16538
17260
  .leaf-body {
16539
17261
  flex: 1;
16540
17262
  min-height: 0;
@@ -16556,38 +17278,6 @@ let DockHostElement = class DockHostElement extends i {
16556
17278
  margin: var(--space-s);
16557
17279
  }
16558
17280
 
16559
- .group-toolbar {
16560
- display: flex;
16561
- gap: 2px;
16562
- padding: 0 var(--space-xs);
16563
- }
16564
-
16565
- .chrome-btn {
16566
- display: inline-flex;
16567
- align-items: center;
16568
- justify-content: center;
16569
- width: 1.6em;
16570
- height: 1.6em;
16571
- padding: 0;
16572
- background: none;
16573
- border: none;
16574
- border-radius: var(--radius-s);
16575
- color: inherit;
16576
- cursor: pointer;
16577
- opacity: var(--prominence-idle-opacity);
16578
- transition: background var(--transition), opacity var(--transition);
16579
- }
16580
-
16581
- .chrome-btn:hover {
16582
- background: var(--surface-5);
16583
- opacity: var(--prominence-tickled-opacity);
16584
- }
16585
-
16586
- .chrome-btn:focus-visible {
16587
- outline: 2px solid var(--accent);
16588
- outline-offset: 1px;
16589
- }
16590
-
16591
17281
  .overlay {
16592
17282
  position: absolute;
16593
17283
  inset: 0;
@@ -16657,6 +17347,10 @@ __decorate([property({
16657
17347
  type: String,
16658
17348
  attribute: `accepts`
16659
17349
  })], DockHostElement.prototype, "accepts", void 0);
17350
+ __decorate([property({
17351
+ type: Boolean,
17352
+ attribute: `isolated`
17353
+ })], DockHostElement.prototype, "isolated", void 0);
16660
17354
  __decorate([property({ attribute: false })], DockHostElement.prototype, "canAccept", void 0);
16661
17355
  __decorate([state()], DockHostElement.prototype, "_model", void 0);
16662
17356
  __decorate([state()], DockHostElement.prototype, "_pool", void 0);
@@ -16704,6 +17398,10 @@ function zoneToTarget(zone) {
16704
17398
  function cloneNode(node) {
16705
17399
  return JSON.parse(JSON.stringify(node));
16706
17400
  }
17401
+ /** Panel content kind, normalized (`'any'` when unset/blank). */
17402
+ function normalizeKind(kind) {
17403
+ return (kind?.trim() ?? ``) || `any`;
17404
+ }
16707
17405
  function parsePath(pathStr) {
16708
17406
  return pathStr.length === 0 ? [] : pathStr.split(`-`).filter((s) => s.length > 0).map(Number);
16709
17407
  }
@@ -17148,6 +17846,11 @@ function WheelNudgeMixin(superClass) {
17148
17846
  if (root && root.activeElement !== null) return true;
17149
17847
  return false;
17150
17848
  }
17849
+ /**
17850
+ * Apply one wheel tick. No-op by default — consumers override this to run
17851
+ * their arrow-key step math (see {@link WheelNudgeConsumer}).
17852
+ */
17853
+ _applyWheelDelta(_direction, _event) {}
17151
17854
  }
17152
17855
  __decorate([property({
17153
17856
  type: Boolean,
@@ -26003,7 +26706,7 @@ let NotificationPillElement = class NotificationPillElement extends i {
26003
26706
  #renderIcon() {
26004
26707
  if (!this.icon) return A;
26005
26708
  if (this.icon.startsWith(`<svg`)) return b`<span class="pill-icon">${unsafeHTML(this.icon)}</span>`;
26006
- if (!getIcon(this.icon)) return A;
26709
+ if (!hasIcon(this.icon)) return A;
26007
26710
  return b`<ixfx-icon class="pill-icon" name="${this.icon}"></ixfx-icon>`;
26008
26711
  }
26009
26712
  render() {
@@ -26802,7 +27505,7 @@ let TooltipElement = class TooltipElement extends i {
26802
27505
  #renderIcon() {
26803
27506
  if (!this.icon) return A;
26804
27507
  if (this.icon.startsWith(`<svg`)) return b`<span class="tooltip-icon" style="display:inline-flex;align-items:center;margin-right:0.25em">${unsafeHTML(this.icon)}</span>`;
26805
- if (!getIcon(this.icon)) return A;
27508
+ if (!hasIcon(this.icon)) return A;
26806
27509
  return b`<ixfx-icon class="tooltip-icon" style="margin-right:0.25em" name="${this.icon}"></ixfx-icon>`;
26807
27510
  }
26808
27511
  render() {
@@ -35813,6 +36516,6 @@ function init() {
35813
36516
  console.log(`Init`);
35814
36517
  }
35815
36518
  //#endregion
35816
- export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, DataGridElement, DetailListElement, DockHostElement, DockPanelElement, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, NotificationManager, NotificationPillElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, SliderInputElement, SlidingNumberElement, SnackbarElement, SnapContainer, SplitButton, SplitLayoutElement, SplitPanelElement, SwipeElement, TabController, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TextShimmerElement, TimelineController, TimelineTrackTabElement, TitlebarElement, ToolbarElement, ToolbarGroupElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeListElement, TwoSplitLayoutElement, VerticalListElement, XyPad, addPanelToStack, applyAgeModification, applyHighlights, applySnapshot, attachTimelineInteractions, buildHighlightTargets, buildModel, clearHighlights, collectDataResult, collectPanelIds, collectStackPaths, computeColour, createAcCompletionProvider, createElementIncrSearch, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createVerticalListAdapter, distribute, extractStack, findNode, findParentOf, findStackOf, getActiveDragPanelId, getIcon, hasIcon, iconBus, init, insertBeside, isDragging, isSplit, isStack, monitorIndeterminate, normalise, normalize, parseColourScaleAttr, parseLayout, pointerOverAnyHost, readBaseColour, registerIcon, removePanelFromStack, resolveCssVars, resolveGrid, resolveTracks, serializeLayout, setActive, svgToDataUri, timelineStyles, toSnapshot, walk, wrapRoot };
36519
+ export { AcTextElement, AcTokenElement, ButtonElement, Checkbox, ColourPicker, ColourPickerPopup, CrumbNavigationElement, CrumbPathController, CrumbPathElement, DataController, DataDisplayComponent, DataGridElement, DetailListElement, DockHostElement, DockPanelElement, EditableLabel, EditableNumber, FormElement, FormGroupElement, FormPartElement, GroupedItemListerElement, ICON_CARET_RIGHT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CLOSE, IncrSearchTreeController as IncrSearchMillerController, IncrSearchTreeController, IxfxHexEditorElement, IxfxIconElement, IxfxTimelineElement, LabelledRadialInput, LabelledRangeInput, LabelledSliderInput, LedElement, menu_exports as Menu, MillerBaseElement, MillerListElement, MillerTreeController, NarrowedTextElement, NotificationManager, NotificationPillElement, PanelElement, PanelGroupElement, PlotDataSeries, PlotHistogram, PlotMultiAxis, PlotSingleAxis, PlotXyAxis, PolarPad, RadialInputElement, RadioGroup, RangeElement, RangeInput, RangeMultiElement, RatingElement, SelectHorizElement, SliderInputElement, SlidingNumberElement, SnackbarElement, SnapContainer, SplitButton, SplitLayoutElement, SplitPanelElement, SwipeElement, TabController, TabListElement, TabListItemElement, TabPanelElement, TabPanelsElement, TextShimmerElement, TimelineController, TimelineTrackTabElement, TitlebarElement, ToolbarElement, ToolbarGroupElement, TooltipElement, TransitoryLabelElement, TreeBaseElement, TreeController, TreeDataModel, TreeListElement, TwoSplitLayoutElement, VerticalListElement, XyPad, addPanelToStack, applyAgeModification, applyHighlights, applySnapshot, attachTimelineInteractions, buildHighlightTargets, buildModel, clearHighlights, collectContainerNames, collectDataResult, collectPanelIds, collectStackPaths, computeColour, computeLeafGeoms, createAcCompletionProvider, createElementIncrSearch, createEmptyLayout, createEmptyStack, createFuzzySearch, createIncrSearch, createPillRenderer, createSplit, createStack, createTreeListAdapter, createVerticalListAdapter, distribute, extractStack, findNode, findParentOf, findStackOf, findStackPathByName, getActiveDragPanelId, getIcon, getIconEntry, getIconFont, getLeafNeighbours, hasIcon, iconBus, init, insertBeside, isDragging, isSplit, isStack, monitorIndeterminate, moveLeafNodeToEdge, moveLeafStep, normalise, normalize, parseColourScaleAttr, parseLayout, pointerOverAnyHost, readBaseColour, registerFontIcon, registerIcon, removePanelFromStack, resolveCssVars, resolveGrid, resolveLeafPath, resolveTracks, serializeLayout, setActive, setIconFont, svgToDataUri, swapLeaves, timelineStyles, toSnapshot, walk, wrapRoot };
35817
36520
 
35818
36521
  //# sourceMappingURL=index.js.map