flexdesk 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/css/base.css +1484 -181
  2. package/css/flexdesk.css +1311 -18
  3. package/css/overrides.css +44 -0
  4. package/css/tokens.css +45 -0
  5. package/dist/charts.js +5 -3
  6. package/dist/charts.js.map +1 -1
  7. package/dist/{chunk-DVU44T77.js → chunk-ELXVW542.js} +196 -75
  8. package/dist/chunk-ELXVW542.js.map +7 -0
  9. package/dist/chunk-LH5TSOZW.js +1237 -0
  10. package/dist/chunk-LH5TSOZW.js.map +7 -0
  11. package/dist/{chunk-TLZUUFOE.js → chunk-O5OHMWBB.js} +10 -2
  12. package/dist/chunk-O5OHMWBB.js.map +7 -0
  13. package/dist/{chunk-CT4YXXLP.js → chunk-QIU5S2RU.js} +371 -73
  14. package/dist/chunk-QIU5S2RU.js.map +7 -0
  15. package/dist/chunk-QNQHQ24V.js +408 -0
  16. package/dist/chunk-QNQHQ24V.js.map +7 -0
  17. package/dist/{chunk-DRYCDMEG.js → chunk-XKDTIT4Q.js} +168 -12
  18. package/dist/chunk-XKDTIT4Q.js.map +7 -0
  19. package/dist/editor.js +3 -380
  20. package/dist/editor.js.map +3 -3
  21. package/dist/flexdesk.css +1311 -18
  22. package/dist/tiles.js +168 -41
  23. package/dist/tiles.js.map +2 -2
  24. package/dist/tokens.css +45 -0
  25. package/dist/widgets.js +44 -14
  26. package/dist/widgets.js.map +2 -2
  27. package/dist/wm.js +2983 -142
  28. package/dist/wm.js.map +4 -4
  29. package/package.json +3 -2
  30. package/src/charts/chart_types.js +167 -0
  31. package/src/charts/plotly_wrapper.js +178 -10
  32. package/src/editor/notebook_tab_bar.js +39 -3
  33. package/src/tiles/tile_base.js +143 -35
  34. package/src/tiles/tile_grid.js +52 -1
  35. package/src/tiling/command_palette.js +71 -18
  36. package/src/tiling/desktops.js +36 -12
  37. package/src/tiling/keymap.js +24 -4
  38. package/src/tiling/shell.js +135 -24
  39. package/src/tiling/tab_strip.js +184 -0
  40. package/src/tiling/tile_breadcrumb.js +34 -2
  41. package/src/tiling/tile_renderer.js +1386 -21
  42. package/src/tiling/tile_tab_menu.js +101 -0
  43. package/src/tiling/tile_tree.js +82 -0
  44. package/src/tiling/wm.js +2352 -74
  45. package/src/ui/components/action_dropdown.js +34 -3
  46. package/src/ui/components/autocomplete_field.js +65 -13
  47. package/src/ui/components/context_menu.js +79 -8
  48. package/src/ui/components/data_table.js +508 -84
  49. package/src/ui/components/managed_window.js +928 -36
  50. package/src/ui/components/modal.js +214 -8
  51. package/dist/chunk-CT4YXXLP.js.map +0 -7
  52. package/dist/chunk-DRYCDMEG.js.map +0 -7
  53. package/dist/chunk-DVU44T77.js.map +0 -7
  54. package/dist/chunk-TLZUUFOE.js.map +0 -7
  55. package/dist/chunk-UCJ2WD4D.js +0 -625
  56. package/dist/chunk-UCJ2WD4D.js.map +0 -7
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ManagedWindow
3
- } from "./chunk-UCJ2WD4D.js";
3
+ } from "./chunk-LH5TSOZW.js";
4
4
 
5
5
  // src/help/help_registry.js
6
6
  var EMPTY_PROVIDER = Object.freeze({
@@ -361,74 +361,14 @@ if (typeof document !== "undefined") {
361
361
  setupGlobalHelpShortcut();
362
362
  }
363
363
 
364
- // src/ui/components/context_menu.js
365
- var _activeMenu = null;
366
- function showContextMenu(x, y, items, onAction) {
367
- hideContextMenu();
368
- const menu = document.createElement("div");
369
- menu.className = "twm-context-menu ea-context-menu";
370
- for (const it of items) {
371
- if (it.separator) {
372
- const sep = document.createElement("div");
373
- sep.className = "twm-context-menu__separator";
374
- menu.appendChild(sep);
375
- continue;
376
- }
377
- const row = document.createElement("div");
378
- let cls = "twm-context-menu-item";
379
- if (it.danger) cls += " twm-delete-node";
380
- if (it.disabled) cls += " disabled";
381
- row.className = cls;
382
- row.innerHTML = `
383
- <span class="material-symbols-outlined">${it.icon || ""}</span>
384
- <span>${escapeHtml(it.label)}</span>
385
- `;
386
- if (!it.disabled) {
387
- row.addEventListener("click", (e) => {
388
- e.stopPropagation();
389
- hideContextMenu();
390
- onAction?.(it.action);
391
- });
392
- }
393
- menu.appendChild(row);
394
- }
395
- document.body.appendChild(menu);
396
- menu.style.display = "block";
397
- _activeMenu = menu;
398
- const rect = menu.getBoundingClientRect();
399
- const left = Math.min(x, window.innerWidth - rect.width - 8);
400
- const top = Math.min(y, window.innerHeight - rect.height - 8);
401
- menu.style.left = `${Math.max(0, left)}px`;
402
- menu.style.top = `${Math.max(0, top)}px`;
403
- setTimeout(() => {
404
- document.addEventListener("mousedown", _outsideHandler, { once: true, capture: true });
405
- }, 0);
406
- document.addEventListener("keydown", _escHandler);
407
- window.addEventListener("scroll", hideContextMenu, { once: true, capture: true });
408
- }
409
- function hideContextMenu() {
410
- if (!_activeMenu) return;
411
- _activeMenu.remove();
412
- _activeMenu = null;
413
- document.removeEventListener("keydown", _escHandler);
414
- }
415
- function _outsideHandler(e) {
416
- if (_activeMenu && !_activeMenu.contains(e.target)) hideContextMenu();
364
+ // src/ui/components/modal.js
365
+ var _modalHost = null;
366
+ function setModalHost(el) {
367
+ _modalHost = el || null;
417
368
  }
418
- function _escHandler(e) {
419
- if (e.key === "Escape") hideContextMenu();
369
+ function modalHost() {
370
+ return _modalHost;
420
371
  }
421
- function escapeHtml(s) {
422
- return String(s).replace(/[&<>"']/g, (c) => ({
423
- "&": "&amp;",
424
- "<": "&lt;",
425
- ">": "&gt;",
426
- '"': "&quot;",
427
- "'": "&#39;"
428
- })[c]);
429
- }
430
-
431
- // src/ui/components/modal.js
432
372
  var _modalSeq = 1;
433
373
  function openForm({ title, fields = [], defaults = {}, submitLabel = "OK" } = {}) {
434
374
  return new Promise((resolve) => {
@@ -445,6 +385,7 @@ function openForm({ title, fields = [], defaults = {}, submitLabel = "OK" } = {}
445
385
  </form>
446
386
  `;
447
387
  const win = new ManagedWindow({
388
+ container: _modalHost,
448
389
  id: `twm-modal-${_modalSeq++}`,
449
390
  title: title || "Dialog",
450
391
  icon: "edit_note",
@@ -594,6 +535,7 @@ function openConfirm({
594
535
  `;
595
536
  let _resolved = false;
596
537
  const win = new ManagedWindow({
538
+ container: _modalHost,
597
539
  id: `twm-confirm-${_modalSeq++}`,
598
540
  title: title || "Confirm",
599
541
  icon: icon || (danger ? "warning" : "help"),
@@ -638,7 +580,9 @@ function openModal({
638
580
  height = 480,
639
581
  onMount = null,
640
582
  backdropBlur = void 0,
641
- backdropOpacity = void 0
583
+ backdropOpacity = void 0,
584
+ maximizable = false,
585
+ onMaximizeChange = null
642
586
  } = {}) {
643
587
  return new Promise((resolve) => {
644
588
  const body = document.createElement("div");
@@ -657,13 +601,35 @@ function openModal({
657
601
  if (a.primary) cls += " twm-btn--primary";
658
602
  if (a.danger) cls += " twm-btn--danger";
659
603
  btn.className = cls;
660
- btn.textContent = String(a.label || "");
604
+ if (a.icon) {
605
+ const glyph = document.createElement("span");
606
+ glyph.className = "material-symbols-outlined twm-btn__glyph";
607
+ glyph.textContent = String(a.icon);
608
+ glyph.setAttribute("aria-hidden", "true");
609
+ const text = document.createElement("span");
610
+ text.className = "twm-btn__label";
611
+ text.textContent = String(a.label || "");
612
+ btn.append(glyph, text);
613
+ } else {
614
+ btn.textContent = String(a.label || "");
615
+ }
661
616
  btn.dataset.actionIdx = String(i);
617
+ if (a.disabled) btn.disabled = true;
618
+ if (a.value !== void 0 && a.value !== null) {
619
+ btn.dataset.actionValue = String(a.value);
620
+ }
662
621
  actionsEl.appendChild(btn);
663
622
  });
664
623
  body.appendChild(actionsEl);
665
624
  let _resolved = false;
625
+ let _onMax = null;
626
+ const _dropMaxListener = () => {
627
+ if (!_onMax) return;
628
+ window.removeEventListener("managed-window-maximized", _onMax);
629
+ _onMax = null;
630
+ };
666
631
  const win = new ManagedWindow({
632
+ container: _modalHost,
667
633
  id: `twm-modal-${_modalSeq++}`,
668
634
  title,
669
635
  icon,
@@ -672,12 +638,24 @@ function openModal({
672
638
  backdropBlur,
673
639
  backdropOpacity,
674
640
  canMinimize: false,
675
- canMaximize: false,
641
+ // C29. OPT-IN, and `false` is still the default — a confirmation
642
+ // and a two-field form have nothing to do with the extra room, and
643
+ // a button that grows a dialog nobody wanted grown is noise in the
644
+ // one place a user looks for the X. `onMaximize` is deliberately
645
+ // NOT set: see the C29 note in the header — with nothing claiming
646
+ // the gesture, `toggleMaximize` runs the rectangle, which is the
647
+ // only thing maximise can mean for a window with no tile.
648
+ canMaximize: !!maximizable,
676
649
  canResize: true,
677
650
  canDrag: true,
678
651
  defaultWidth: width,
679
652
  defaultHeight: height,
653
+ // Esc, the X and the backdrop all land here without passing through
654
+ // `close`, so the listener is dropped here as well as there — the
655
+ // three dismissals a user reaches for most are exactly the ones
656
+ // that would otherwise leak it.
680
657
  onClose: () => {
658
+ _dropMaxListener();
681
659
  if (!_resolved) {
682
660
  _resolved = true;
683
661
  resolve(null);
@@ -685,9 +663,21 @@ function openModal({
685
663
  }
686
664
  });
687
665
  win.show();
666
+ if (maximizable && typeof onMaximizeChange === "function") {
667
+ _onMax = (e) => {
668
+ if (e.detail?.id !== win.id) return;
669
+ try {
670
+ onMaximizeChange(!!e.detail.maximized, bodyInner);
671
+ } catch (err) {
672
+ console.warn("[modal] onMaximizeChange threw", err);
673
+ }
674
+ };
675
+ window.addEventListener("managed-window-maximized", _onMax);
676
+ }
688
677
  const close = (value) => {
689
678
  if (_resolved) return;
690
679
  _resolved = true;
680
+ _dropMaxListener();
691
681
  resolve(value);
692
682
  try {
693
683
  win.close({ force: true });
@@ -710,16 +700,43 @@ function openModal({
710
700
  }
711
701
  close(a.value);
712
702
  });
703
+ const controls = {
704
+ // NOT `CSS.escape`. It is a browser global that jsdom does not
705
+ // provide, and this file is mounted under jsdom by four render
706
+ // tests — so reaching for it turns "the dialog is valid" into a
707
+ // ReferenceError in every one of them, and into nothing at all in a
708
+ // headless consumer. An attribute selector needs `"` and `\`
709
+ // escaped and nothing else.
710
+ actionButton: (value) => actionsEl.querySelector(
711
+ `[data-action-value="${String(value).replace(/["\\]/g, "\\$&")}"]`
712
+ ),
713
+ setActionEnabled(value, enabled) {
714
+ const btn = controls.actionButton(value);
715
+ if (btn) btn.disabled = !enabled;
716
+ return !!btn;
717
+ }
718
+ };
713
719
  if (typeof onMount === "function") {
714
720
  requestAnimationFrame(() => {
715
721
  try {
716
- onMount(bodyInner);
722
+ onMount(bodyInner, controls);
717
723
  } catch (err) {
718
724
  console.warn(err);
719
725
  }
720
726
  });
721
727
  }
722
728
  requestAnimationFrame(() => {
729
+ const field = bodyInner.querySelector(
730
+ 'input:not([type="hidden"]):not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly]), select:not([disabled])'
731
+ );
732
+ if (field) {
733
+ field.focus();
734
+ try {
735
+ field.setSelectionRange?.(field.value.length, field.value.length);
736
+ } catch {
737
+ }
738
+ return;
739
+ }
723
740
  const idx = acts.findIndex((a) => a.primary);
724
741
  const which = idx >= 0 ? idx : acts.length - 1;
725
742
  actionsEl.querySelector(`[data-action-idx="${which}"]`)?.focus();
@@ -840,16 +857,120 @@ function _esc(s) {
840
857
  return String(s == null ? "" : s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
841
858
  }
842
859
 
860
+ // src/ui/components/context_menu.js
861
+ var _activeMenu = null;
862
+ var _returnFocusTo = null;
863
+ function showContextMenu(x, y, items, onAction) {
864
+ hideContextMenu();
865
+ _returnFocusTo = document.activeElement;
866
+ const menu = document.createElement("div");
867
+ menu.className = "twm-context-menu ea-context-menu";
868
+ menu.setAttribute("role", "menu");
869
+ for (const it of items) {
870
+ if (it.separator) {
871
+ const sep = document.createElement("div");
872
+ sep.className = "twm-context-menu__separator";
873
+ sep.setAttribute("role", "separator");
874
+ menu.appendChild(sep);
875
+ continue;
876
+ }
877
+ const row = document.createElement("button");
878
+ row.type = "button";
879
+ row.setAttribute("role", "menuitem");
880
+ let cls = "twm-context-menu-item";
881
+ if (it.danger) cls += " twm-delete-node";
882
+ if (it.disabled) cls += " disabled";
883
+ row.className = cls;
884
+ if (it.disabled) {
885
+ row.disabled = true;
886
+ row.setAttribute("aria-disabled", "true");
887
+ }
888
+ if (it.title) row.title = it.title;
889
+ row.innerHTML = `
890
+ <span class="material-symbols-outlined">${it.icon || ""}</span>
891
+ <span>${escapeHtml(it.label)}</span>
892
+ `;
893
+ if (!it.disabled) {
894
+ row.addEventListener("click", (e) => {
895
+ e.stopPropagation();
896
+ hideContextMenu();
897
+ onAction?.(it.action);
898
+ });
899
+ }
900
+ menu.appendChild(row);
901
+ }
902
+ (modalHost() || document.body).appendChild(menu);
903
+ menu.style.display = "block";
904
+ _activeMenu = menu;
905
+ const rect = menu.getBoundingClientRect();
906
+ const left = Math.min(x, window.innerWidth - rect.width - 8);
907
+ const top = Math.min(y, window.innerHeight - rect.height - 8);
908
+ menu.style.left = `${Math.max(0, left)}px`;
909
+ menu.style.top = `${Math.max(0, top)}px`;
910
+ setTimeout(() => {
911
+ document.addEventListener("mousedown", _outsideHandler, { once: true, capture: true });
912
+ }, 0);
913
+ document.addEventListener("keydown", _keyHandler);
914
+ window.addEventListener("scroll", hideContextMenu, { once: true, capture: true });
915
+ _enabledItems(menu)[0]?.focus({ preventScroll: true });
916
+ }
917
+ function hideContextMenu() {
918
+ if (!_activeMenu) return;
919
+ const returnTo = _returnFocusTo;
920
+ const held = _activeMenu.contains(document.activeElement);
921
+ _activeMenu.remove();
922
+ _activeMenu = null;
923
+ _returnFocusTo = null;
924
+ document.removeEventListener("keydown", _keyHandler);
925
+ if (held && returnTo?.isConnected) returnTo.focus?.({ preventScroll: true });
926
+ }
927
+ function _enabledItems(menu) {
928
+ return [...menu.querySelectorAll(".twm-context-menu-item:not(.disabled)")];
929
+ }
930
+ function _keyHandler(e) {
931
+ if (!_activeMenu) return;
932
+ if (e.key === "Escape") {
933
+ e.preventDefault();
934
+ hideContextMenu();
935
+ return;
936
+ }
937
+ const items = _enabledItems(_activeMenu);
938
+ if (items.length === 0) return;
939
+ const at = items.indexOf(document.activeElement);
940
+ let next = null;
941
+ if (e.key === "ArrowDown") next = items[(at + 1 + items.length) % items.length];
942
+ else if (e.key === "ArrowUp") next = items[(at - 1 + items.length) % items.length];
943
+ else if (e.key === "Home") next = items[0];
944
+ else if (e.key === "End") next = items[items.length - 1];
945
+ if (!next) return;
946
+ e.preventDefault();
947
+ next.focus({ preventScroll: true });
948
+ }
949
+ function _outsideHandler(e) {
950
+ if (_activeMenu && !_activeMenu.contains(e.target)) hideContextMenu();
951
+ }
952
+ function escapeHtml(s) {
953
+ return String(s).replace(/[&<>"']/g, (c) => ({
954
+ "&": "&amp;",
955
+ "<": "&lt;",
956
+ ">": "&gt;",
957
+ '"': "&quot;",
958
+ "'": "&#39;"
959
+ })[c]);
960
+ }
961
+
843
962
  export {
844
963
  setHelpProvider,
845
964
  helpProvider,
846
965
  helpCategories,
847
966
  helpCopy,
848
967
  HelpModal,
849
- showContextMenu,
850
- hideContextMenu,
968
+ setModalHost,
969
+ modalHost,
851
970
  openForm,
852
971
  openConfirm,
853
- openModal
972
+ openModal,
973
+ showContextMenu,
974
+ hideContextMenu
854
975
  };
855
- //# sourceMappingURL=chunk-DVU44T77.js.map
976
+ //# sourceMappingURL=chunk-ELXVW542.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/help/help_registry.js", "../src/help/help_modal.js", "../src/ui/components/modal.js", "../src/ui/components/context_menu.js"],
4
+ "sourcesContent": ["/**\n * Where the help modal gets its help.\n *\n * HelpModal used to `import { getHelpService, HELP_TOPICS } from './help_service.js'` \u2014\n * and help_service.js is 249 domain words of EcoAgent prose about sectors,\n * currencies, agents and markets. That import was the last framework -> domain\n * edge in the core: a generic modal reaching into one application's manual.\n *\n * The modal now asks this registry, and the application fills it. If nobody\n * fills it, the modal opens and says there is no help \u2014 which is the correct\n * behaviour for a library that ships no documentation about your app.\n *\n * @typedef {Object} HelpProvider\n * @property {() => string[]} getCategories\n * @property {(category: string) => Array<{id: string, title: string}>} getTopicsByCategory\n * @property {(id: string) => Promise<Object|null>} loadTopic\n * @property {(query: string) => Array<Object>} searchTopics\n *\n * @typedef {Object.<string, {label: string, icon: string}>} HelpCategories\n * Keyed by category id; ITERATION ORDER IS DISPLAY ORDER.\n *\n * @typedef {{title: string, welcome: string}} HelpCopy\n * The landing heading. The modal used to hardcode \"EcoAgent Help\" and\n * \"Welcome to EcoAgent!\".\n */\n\n/** A provider with nothing in it. Not an error \u2014 just an app with no help. */\nconst EMPTY_PROVIDER = Object.freeze({\n getCategories: () => [],\n getTopicsByCategory: () => [],\n loadTopic: async () => null,\n searchTopics: () => [],\n});\n\nconst DEFAULT_COPY = Object.freeze({\n title: 'Help',\n welcome: 'Select a topic from the sidebar or browse the categories below.',\n});\n\nlet _provider = EMPTY_PROVIDER;\nlet _categories = Object.freeze({});\nlet _copy = DEFAULT_COPY;\n\n/**\n * @param {HelpProvider} provider\n * @param {HelpCategories} [categories] Display labels + icons, in display order.\n * @param {Partial<HelpCopy>} [copy] Landing heading and welcome line.\n */\nexport function setHelpProvider(provider, categories = {}, copy = {}) {\n _provider = provider ?? EMPTY_PROVIDER;\n _categories = Object.freeze({ ...categories });\n _copy = Object.freeze({ ...DEFAULT_COPY, ...copy });\n}\n\nexport function helpProvider() {\n return _provider;\n}\n\nexport function helpCategories() {\n return _categories;\n}\n\nexport function helpCopy() {\n return _copy;\n}\n", "/**\n * HelpModal - Modal dialog for displaying help content\n *\n * Features:\n * - Markdown rendering with syntax highlighting\n * - Table of contents navigation\n * - Topic search\n * - Category browsing\n * - Keyboard navigation (Escape to close)\n */\n\nimport { helpProvider, helpCategories, helpCopy } from './help_registry.js';\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// HelpModal Class\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nlet _activeModal = null;\n\nexport class HelpModal {\n /** The application's help, resolved at use time. */\n get _service() { return helpProvider(); }\n\n get _categories() { return helpCategories(); }\n\n get _copy() { return helpCopy(); }\n\n constructor() {\n // Read LAZILY, not here. A HelpModal can be constructed at boot (the\n // command palette holds one) before the application has registered its\n // help, and a constructor read would freeze the empty default forever.\n this._overlay = null;\n this._modal = null;\n this._currentTopic = null;\n this._searchQuery = '';\n this._boundKeyHandler = this._handleKeyDown.bind(this);\n }\n\n /**\n * Open the help modal.\n * @param {string} [topicId] - Initial topic to display\n */\n async open(topicId = null) {\n // Close any existing modal\n if (_activeModal && _activeModal !== this) {\n _activeModal.close();\n }\n _activeModal = this;\n\n this._createModal();\n document.body.appendChild(this._overlay);\n\n // Add keyboard handler\n document.addEventListener('keydown', this._boundKeyHandler);\n\n // Focus search input\n const searchInput = this._modal.querySelector('.help-search-input');\n if (searchInput) {\n setTimeout(() => searchInput.focus(), 100);\n }\n\n // Load initial topic or show index\n if (topicId) {\n await this._loadTopic(topicId);\n } else {\n this._showIndex();\n }\n }\n\n /**\n * Close the help modal.\n */\n close() {\n if (this._overlay) {\n document.removeEventListener('keydown', this._boundKeyHandler);\n this._overlay.remove();\n this._overlay = null;\n this._modal = null;\n }\n if (_activeModal === this) {\n _activeModal = null;\n }\n }\n\n /**\n * Create the modal DOM structure.\n * @private\n */\n _createModal() {\n // Overlay\n this._overlay = document.createElement('div');\n this._overlay.className = 'help-modal-overlay';\n this._overlay.addEventListener('click', (e) => {\n if (e.target === this._overlay) this.close();\n });\n\n // Modal container\n this._modal = document.createElement('div');\n this._modal.className = 'help-modal';\n this._modal.innerHTML = `\n <div class=\"help-modal__header\">\n <div class=\"help-modal__title\">\n <span class=\"material-symbols-outlined\">help</span>\n <span>Help</span>\n </div>\n <button class=\"help-modal__close\" title=\"Close (Esc)\">\n <span class=\"material-symbols-outlined\">close</span>\n </button>\n </div>\n <div class=\"help-modal__search\">\n <span class=\"material-symbols-outlined\">search</span>\n <input type=\"text\" class=\"help-search-input\" placeholder=\"Search help topics...\" autocomplete=\"off\">\n </div>\n <div class=\"help-modal__body\">\n <nav class=\"help-modal__sidebar\">\n <div class=\"help-sidebar__categories\"></div>\n </nav>\n <main class=\"help-modal__content\">\n <div class=\"help-content__loading\">Loading...</div>\n </main>\n </div>\n `;\n\n // Event handlers\n const closeBtn = this._modal.querySelector('.help-modal__close');\n closeBtn.addEventListener('click', () => this.close());\n\n const searchInput = this._modal.querySelector('.help-search-input');\n searchInput.addEventListener('input', (e) => this._handleSearch(e.target.value));\n searchInput.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n if (e.target.value) {\n e.target.value = '';\n this._handleSearch('');\n e.stopPropagation();\n }\n }\n });\n\n // Build sidebar\n this._buildSidebar();\n\n this._overlay.appendChild(this._modal);\n }\n\n /**\n * Build the sidebar navigation.\n * @private\n */\n _buildSidebar() {\n const container = this._modal.querySelector('.help-sidebar__categories');\n const categories = this._service.getCategories();\n\n // Sort categories by order\n const categoryOrder = Object.keys(this._categories);\n categories.sort((a, b) => {\n const orderA = categoryOrder.indexOf(a);\n const orderB = categoryOrder.indexOf(b);\n return (orderA === -1 ? 99 : orderA) - (orderB === -1 ? 99 : orderB);\n });\n\n container.innerHTML = categories.map(category => {\n const topics = this._service.getTopicsByCategory(category);\n const label = this._categories[category]?.label || category;\n const icon = this._categories[category]?.icon || 'folder';\n\n return `\n <div class=\"help-category\" data-category=\"${category}\">\n <div class=\"help-category__header\">\n <span class=\"material-symbols-outlined\">${icon}</span>\n <span>${label}</span>\n </div>\n <ul class=\"help-category__topics\">\n ${topics.map(topic => `\n <li class=\"help-topic-item\" data-topic=\"${topic.id}\">\n ${topic.title}\n </li>\n `).join('')}\n </ul>\n </div>\n `;\n }).join('');\n\n // Add click handlers\n container.querySelectorAll('.help-topic-item').forEach(item => {\n item.addEventListener('click', () => {\n this._loadTopic(item.dataset.topic);\n });\n });\n\n container.querySelectorAll('.help-category__header').forEach(header => {\n header.addEventListener('click', () => {\n header.parentElement.classList.toggle('collapsed');\n });\n });\n }\n\n /**\n * Show the help index page.\n * @private\n */\n _showIndex() {\n const content = this._modal.querySelector('.help-modal__content');\n const categories = this._service.getCategories();\n\n const categoryOrder = Object.keys(this._categories);\n categories.sort((a, b) => {\n const orderA = categoryOrder.indexOf(a);\n const orderB = categoryOrder.indexOf(b);\n return (orderA === -1 ? 99 : orderA) - (orderB === -1 ? 99 : orderB);\n });\n\n content.innerHTML = `\n <div class=\"help-index\">\n <h1>${this._copy.title}</h1>\n <p>${this._copy.welcome}</p>\n\n <div class=\"help-index__grid\">\n ${categories.map(category => {\n const topics = this._service.getTopicsByCategory(category);\n const label = this._categories[category]?.label || category;\n const icon = this._categories[category]?.icon || 'folder';\n\n return `\n <div class=\"help-index__category\" data-category=\"${category}\">\n <div class=\"help-index__category-header\">\n <span class=\"material-symbols-outlined\">${icon}</span>\n <span>${label}</span>\n </div>\n <ul class=\"help-index__topics\">\n ${topics.slice(0, 3).map(topic => `\n <li class=\"help-index__topic\" data-topic=\"${topic.id}\">\n ${topic.title}\n </li>\n `).join('')}\n ${topics.length > 3 ? `<li class=\"help-index__more\">+${topics.length - 3} more</li>` : ''}\n </ul>\n </div>\n `;\n }).join('')}\n </div>\n </div>\n `;\n\n // Add click handlers\n content.querySelectorAll('.help-index__topic').forEach(item => {\n item.addEventListener('click', () => {\n this._loadTopic(item.dataset.topic);\n });\n });\n }\n\n /**\n * Load and display a topic.\n * @param {string} topicId - Topic ID\n * @private\n */\n async _loadTopic(topicId) {\n const content = this._modal.querySelector('.help-modal__content');\n content.innerHTML = '<div class=\"help-content__loading\">Loading...</div>';\n\n // Update sidebar selection\n this._modal.querySelectorAll('.help-topic-item').forEach(item => {\n item.classList.toggle('active', item.dataset.topic === topicId);\n });\n\n try {\n const topic = await this._service.loadTopic(topicId);\n this._currentTopic = topicId;\n\n content.innerHTML = `\n <div class=\"help-content__article\">\n <div class=\"help-content__breadcrumb\">\n <a href=\"#\" class=\"help-breadcrumb__home\" title=\"Help Index\">\n <span class=\"material-symbols-outlined\">home</span>\n </a>\n <span class=\"help-breadcrumb__separator\">/</span>\n <span class=\"help-breadcrumb__title\">${topic.title}</span>\n </div>\n <article class=\"help-article\">\n ${topic.html}\n </article>\n </div>\n `;\n\n // Add breadcrumb handler\n content.querySelector('.help-breadcrumb__home').addEventListener('click', (e) => {\n e.preventDefault();\n this._showIndex();\n });\n } catch (error) {\n content.innerHTML = `\n <div class=\"help-content__error\">\n <span class=\"material-symbols-outlined\">error</span>\n <p>Failed to load help topic.</p>\n </div>\n `;\n }\n }\n\n /**\n * Handle search input.\n * @param {string} query - Search query\n * @private\n */\n _handleSearch(query) {\n this._searchQuery = query;\n const content = this._modal.querySelector('.help-modal__content');\n\n if (!query.trim()) {\n this._showIndex();\n return;\n }\n\n const results = this._service.searchTopics(query);\n\n if (results.length === 0) {\n content.innerHTML = `\n <div class=\"help-search-results\">\n <h2>Search Results</h2>\n <p class=\"help-search-empty\">No results found for \"${query}\"</p>\n </div>\n `;\n return;\n }\n\n content.innerHTML = `\n <div class=\"help-search-results\">\n <h2>Search Results</h2>\n <p class=\"help-search-count\">${results.length} result${results.length !== 1 ? 's' : ''} for \"${query}\"</p>\n <ul class=\"help-search-list\">\n ${results.map(topic => `\n <li class=\"help-search-item\" data-topic=\"${topic.id}\">\n <span class=\"help-search-item__title\">${topic.title}</span>\n <span class=\"help-search-item__category\">${this._categories[topic.category]?.label || topic.category}</span>\n </li>\n `).join('')}\n </ul>\n </div>\n `;\n\n // Add click handlers\n content.querySelectorAll('.help-search-item').forEach(item => {\n item.addEventListener('click', () => {\n this._loadTopic(item.dataset.topic);\n });\n });\n }\n\n /**\n * Handle keyboard events.\n * @param {KeyboardEvent} event\n * @private\n */\n _handleKeyDown(event) {\n if (event.key === 'Escape') {\n this.close();\n }\n }\n\n /**\n * Static method to open help modal.\n * @param {string} [topicId] - Initial topic\n * @returns {HelpModal}\n */\n static open(topicId = null) {\n const modal = new HelpModal();\n modal.open(topicId);\n return modal;\n }\n\n /**\n * Get the currently active modal.\n * @returns {HelpModal|null}\n */\n static getActive() {\n return _activeModal;\n }\n}\n\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// Global Help Shortcut\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n/**\n * Open help modal with the `?` key. (F1 is reserved for top-nav Home.)\n */\nfunction setupGlobalHelpShortcut() {\n document.addEventListener('keydown', (event) => {\n if (event.key !== '?') return;\n // Don't hijack `?` typed into a field (search boxes, editors).\n const t = event.target;\n if (t?.closest?.(\n 'input, textarea, select, [contenteditable=\"true\"]')) return;\n event.preventDefault();\n if (_activeModal) {\n _activeModal.close();\n } else {\n HelpModal.open();\n }\n });\n}\n\n// Initialize global shortcut\nif (typeof document !== 'undefined') {\n setupGlobalHelpShortcut();\n}\n\nexport default HelpModal;\n", "/**\n * Modal helpers \u2014 prompt with form, yes/no confirm.\n *\n * Built on the canonical `ManagedWindow` (with `modal: true`) so dialogs\n * go through the same window system as everything else: same chrome,\n * same backdrop, same Esc-to-close behaviour, same z-stacking.\n *\n * Returns Promises that resolve with the form data (or null on cancel)\n * / a boolean (confirm).\n *\n * Field schema:\n * { name, label, type='text', default, step?, options?, required?,\n * placeholder?, hint?, validator?, rows?, create? }\n *\n * - type: 'text' | 'number' | 'password' | 'select' | 'textarea' |\n * 'checkbox'\n * - hint: small dim help line rendered under the field\n * - validator: fn(value, allValues) => string | null (inline error)\n * - rows: number of textarea rows (only used for type='textarea')\n *\n * Section dividers \u2014 entries of the form `{ section: 'Label' }` are\n * rendered as a small uppercase heading that groups the fields below\n * them. Useful for longer forms.\n *\n * Dependent-property pick-or-create \u2014 a `select` field that carries a\n * `create` spec renders as a **type-ahead combobox** instead of a\n * dropdown: the user picks an existing option OR types a new value.\n * On submit, a value that isn't an existing option is created via\n * `create.onCreate(value)` before the form resolves:\n *\n * {\n * name: 'asset_kind', label: 'Asset kind', type: 'select',\n * options: [...], // existing values (autocomplete)\n * create: {\n * onCreate: async (typed) => finalValue | null, // make the entity\n * hint: 'new asset kind', // shown when the typed value is new\n * },\n * }\n */\n\nimport { ManagedWindow } from '../../ui/components/managed_window.js';\n\n/**\n * C25. WHERE A DIALOG OPENS, when the application spans more than one window.\n *\n * Every modal here is a `ManagedWindow` with no `container`, so it mounts into\n * `document.body` \u2014 and \"document\" is the document this MODULE was loaded in.\n * A consumer that opens a second browser window with `window.open` and builds\n * its DOM from the opener's realm (which is how a pop-out grid works: same\n * modules, same JS context, a different document) therefore gets its dialogs in\n * the window it popped OUT of. The user presses \"Add column\" on their second\n * monitor and a dialog appears on the first, behind whatever is there.\n *\n * Passing a container at each call site was the other option and it is worse:\n * twenty-one of them across eleven files, every one of which would have to be\n * given a document it has no other reason to know about, and any one missed is\n * this bug again with no way to see it from here.\n *\n * So the HOST is ambient and the consumer sets it when its focus moves. Null \u2014\n * every consumer today \u2014 means `document.body`, exactly as before.\n */\nlet _modalHost = null;\nexport function setModalHost(el) { _modalHost = el || null; }\nexport function modalHost() { return _modalHost; }\n\nlet _modalSeq = 1;\n\nexport function openForm({ title, fields = [], defaults = {}, submitLabel = 'OK' } = {}) {\n return new Promise((resolve) => {\n // Build the form body \u2014 same markup as the legacy overlay,\n // minus the outer .ea-modal wrapper (ManagedWindow owns the\n // chrome now).\n const body = document.createElement('div');\n body.className = 'twm-modal__body-host';\n body.innerHTML = `\n <form class=\"twm-modal__form\">\n ${fields.map((f) => _renderEntry(f, defaults[f.name] ?? f.default)).join('')}\n <div class=\"twm-modal__error\" data-role=\"form-error\" hidden></div>\n <div class=\"twm-modal__actions\">\n <button type=\"button\" class=\"twm-btn\" data-action=\"cancel\">Cancel</button>\n <button type=\"submit\" class=\"twm-btn twm-btn--primary\">${submitLabel}</button>\n </div>\n </form>\n `;\n\n const win = new ManagedWindow({\n container: _modalHost,\n id: `twm-modal-${_modalSeq++}`,\n title: title || 'Dialog',\n icon: 'edit_note',\n content: body,\n modal: true,\n canMinimize: false,\n canMaximize: false,\n canResize: false,\n canDrag: true,\n defaultWidth: _estimateWidth(fields),\n defaultHeight: _estimateHeight(fields),\n onClose: () => { if (!_resolved) { _resolved = true; resolve(null); } },\n });\n win.show();\n\n let _resolved = false;\n const close = (result) => {\n if (_resolved) return;\n _resolved = true;\n resolve(result);\n try { win.close({ force: true }); } catch {}\n };\n\n const errEl = body.querySelector('[data-role=\"form-error\"]');\n const showError = (msg) => {\n if (!errEl) return;\n errEl.textContent = msg;\n errEl.hidden = !msg;\n };\n body.querySelector('[data-action=\"cancel\"]')?.addEventListener('click', () => close(null));\n\n const formFields = fields.filter((f) => !f.section);\n for (const f of formFields) {\n if (f.type === 'select' && f.create) _wireComboboxHint(body, f);\n }\n\n const clearFieldError = (name) => {\n const row = body.querySelector(`[data-field-row=\"${name}\"]`);\n const errEl = body.querySelector(`[data-field-error=\"${name}\"]`);\n if (row) row.classList.remove('twm-is-invalid');\n if (errEl) { errEl.textContent = ''; errEl.hidden = true; }\n };\n const setFieldError = (name, msg) => {\n const row = body.querySelector(`[data-field-row=\"${name}\"]`);\n const errEl = body.querySelector(`[data-field-error=\"${name}\"]`);\n if (row) row.classList.add('twm-is-invalid');\n if (errEl) { errEl.textContent = msg; errEl.hidden = false; }\n };\n // Clear per-field errors on input so the user sees the row recover.\n for (const f of formFields) {\n const el = body.querySelector(`[name=\"${f.name}\"]`);\n el?.addEventListener('input', () => clearFieldError(f.name));\n el?.addEventListener('change', () => clearFieldError(f.name));\n }\n\n body.querySelector('form').addEventListener('submit', async (e) => {\n e.preventDefault();\n const submitBtn = body.querySelector('button[type=\"submit\"]');\n showError('');\n const data = {};\n for (const f of formFields) {\n const el = body.querySelector(`[name=\"${f.name}\"]`);\n if (!el) continue;\n let v;\n if (f.type === 'checkbox') {\n v = !!el.checked;\n } else if (f.type === 'number') {\n v = (el.value === '' ? null : Number(el.value));\n } else {\n v = el.value;\n }\n if (f.type === 'select' && f.create) {\n const known = new Set(_optionValues(f.options));\n if (v && !known.has(v)) {\n if (submitBtn) submitBtn.disabled = true;\n let created;\n let failMsg = null;\n try { created = await f.create.onCreate(v); }\n catch (err) { created = null; failMsg = err?.message || String(err); }\n if (submitBtn) submitBtn.disabled = false;\n if (created == null) {\n const hint = (f.create && f.create.hint) || f.label.toLowerCase();\n showError(failMsg\n ? `Couldn't create ${hint} \"${v}\": ${failMsg}`\n : `Couldn't create ${hint} \"${v}\". See the toast for details.`);\n return;\n }\n v = created;\n }\n }\n data[f.name] = v;\n }\n // Per-field validators run after collection so they can see\n // peer values (e.g. confirm-password matches password).\n let firstBadName = null;\n for (const f of formFields) {\n clearFieldError(f.name);\n if (typeof f.validator !== 'function') continue;\n let msg = null;\n try { msg = f.validator(data[f.name], data); } catch (err) { msg = err?.message || String(err); }\n if (msg) {\n setFieldError(f.name, msg);\n if (!firstBadName) firstBadName = f.name;\n }\n }\n if (firstBadName) {\n body.querySelector(`[name=\"${firstBadName}\"]`)?.focus();\n return;\n }\n close(data);\n });\n\n // Focus first field on next frame so the managed-window mount\n // settles before we steal focus.\n requestAnimationFrame(() => {\n body.querySelector('input, select, textarea')?.focus();\n });\n });\n}\n\n\nexport function openConfirm({\n title, message, confirmLabel = 'OK',\n cancelLabel = 'Cancel',\n danger = false, icon = null,\n} = {}) {\n return new Promise((resolve) => {\n const body = document.createElement('div');\n body.className = 'twm-modal__body-host';\n const cls = danger ? 'twm-btn twm-btn--danger' : 'twm-btn twm-btn--primary';\n body.innerHTML = `\n <div class=\"twm-modal__body\">${message}</div>\n <div class=\"twm-modal__actions\">\n <button type=\"button\" class=\"twm-btn\" data-action=\"cancel\">${cancelLabel}</button>\n <button type=\"button\" class=\"${cls}\" data-action=\"confirm\">${confirmLabel}</button>\n </div>\n `;\n\n let _resolved = false;\n const win = new ManagedWindow({\n container: _modalHost,\n id: `twm-confirm-${_modalSeq++}`,\n title: title || 'Confirm',\n icon: icon || (danger ? 'warning' : 'help'),\n content: body,\n modal: true,\n canMinimize: false,\n canMaximize: false,\n canResize: false,\n canDrag: true,\n defaultWidth: 380,\n defaultHeight: 180,\n onClose: () => { if (!_resolved) { _resolved = true; resolve(false); } },\n });\n win.show();\n const close = (v) => {\n if (_resolved) return;\n _resolved = true;\n resolve(v);\n try { win.close({ force: true }); } catch {}\n };\n body.querySelector('[data-action=\"cancel\"]').addEventListener('click', () => close(false));\n body.querySelector('[data-action=\"confirm\"]').addEventListener('click', () => close(true));\n requestAnimationFrame(() => {\n body.querySelector(`[data-action=\"${danger ? 'cancel' : 'confirm'}\"]`)?.focus();\n });\n });\n}\n\n\n/**\n * `openModal` \u2014 rich-content modal. The third helper alongside\n * `openForm` (field-based) and `openConfirm` (yes/no).\n *\n * Use when the modal body is arbitrary DOM the caller renders itself\n * \u2014 a chart, a Monaco editor, a table, a diff view. The shell still\n * goes through ManagedWindow so the chrome, focus trap, Esc handling,\n * and z-stacking match every other modal in the app.\n *\n * Schema:\n * - `title` window title (string)\n * - `icon` optional material-symbols-outlined glyph for the\n * title bar; defaults to `info`\n * - `content` HTMLElement appended into the body. Caller owns\n * rendering \u2014 `openModal` doesn't size or style the\n * internal content beyond making it scrollable.\n * - `actions` `[{ label, value, primary?, danger?, icon? }, \u2026]`. If\n * omitted, defaults to a single `[Close]` action that\n * resolves with `null`. The first action with\n * `primary: true` is the rightmost; otherwise the\n * last action wins. Esc / X / backdrop resolve with\n * `null` regardless of what's in `actions`. `icon` is\n * an optional material-symbols-outlined ligature drawn\n * BEFORE the label; when it is given the label lives in\n * a `.twm-btn__label` span, because an icon font's\n * ligature name is itself text and would otherwise be\n * read back as part of the label.\n * - `width`,\n * `height` initial size in px. Defaults: 640 \u00D7 480.\n * - `onMount` optional `(contentEl) => void` invoked once after\n * the content is appended \u2014 handy when the caller\n * needs the bounding rect to size a chart / Monaco /\n * SVG inside the body.\n * - `backdropBlur` backdrop blur radius in px (`0` disables it).\n * - `backdropOpacity` backdrop dim, 0 (clear) \u2026 1 (opaque). Use a low\n * value (with `backdropBlur: 0`) to keep the background\n * legible \u2014 e.g. a live-applied editor where you want to\n * watch the content behind update. Omit both to inherit\n * the default look (blur 2px over a 50% dim).\n * - `maximizable` C29. Draw a maximise button in the title bar.\n * Default `false`, which is byte-for-byte what every\n * existing caller has always got.\n * - `onMaximizeChange` `(maximized, contentEl) => void`, called on each\n * flip. Only useful with `maximizable`.\n *\n * \u2550\u2550 C29. WHY A DIALOG'S MAXIMISE IS THE GEOMETRIC ONE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * Everywhere else in this library maximise means BACK TO TILE: the window\n * stops being a window and its content returns to the tile it came out of.\n * That is R14, it is the product owner's ruling \u2014 *\"maximize here means back\n * to tile\"* \u2014 and it holds for the chrome button, the title bar's\n * double-click, an embedder's window menu and the aero-snap top edge alike.\n *\n * A DIALOG HAS NO TILE. It was never lifted out of one; it is modal, it owns\n * the screen until it resolves, and \"back to tile\" names a destination that\n * does not exist for it. So here maximise can only mean the rectangle, and\n * that is not a contradiction of the ruling but its boundary: THE RULING IS\n * ABOUT WINDOWS THAT CAME FROM A TILE. Do not \"fix\" this into a dock \u2014 there\n * is nothing to dock into, and `bringBackWindow` would refuse it anyway.\n *\n * Mechanically that means setting no `onMaximize` at all rather than reaching\n * for `toggleMaximize({claimable: false})`: with nothing claiming the gesture,\n * the ordinary path IS the rectangle, and the escape hatch is for consumers\n * who have claimed it.\n *\n * The dialog stays a dialog while maximised \u2014 `modal: true` is untouched, so\n * the backdrop, the Esc handler and the Tab focus trap (all of which hang off\n * `this.element` and the top-most-window test, not off geometry) go on working\n * exactly as they did.\n *\n * Returns a Promise resolving with the chosen action's `value`, or\n * `null` if the user dismissed the modal.\n */\nexport function openModal({\n title = 'Dialog',\n icon = 'info',\n content = null,\n actions = null,\n width = 640,\n height = 480,\n onMount = null,\n backdropBlur = undefined,\n backdropOpacity = undefined,\n maximizable = false,\n onMaximizeChange = null,\n} = {}) {\n return new Promise((resolve) => {\n const body = document.createElement('div');\n body.className = 'twm-modal__body-host';\n\n const bodyInner = document.createElement('div');\n bodyInner.className = 'twm-modal__body twm-modal__body--rich';\n if (content instanceof HTMLElement) bodyInner.appendChild(content);\n body.appendChild(bodyInner);\n\n // Actions row \u2014 default = single Close button. Esc / backdrop /\n // X all bypass this and resolve with null.\n const acts = Array.isArray(actions) && actions.length > 0\n ? actions\n : [{ label: 'Close', value: null }];\n const actionsEl = document.createElement('div');\n actionsEl.className = 'twm-modal__actions';\n acts.forEach((a, i) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n let cls = 'twm-btn';\n if (a.primary) cls += ' twm-btn--primary';\n if (a.danger) cls += ' twm-btn--danger';\n btn.className = cls;\n // C26. AN ACTION MAY CARRY A GLYPH \u2014 additively, and only when it\n // asks for one. Without `icon` the button is byte-identical to what\n // every existing caller gets today: one text node, so a consumer\n // reading `btn.textContent` to find its own button keeps working.\n //\n // With one, the label moves into its own span so that reading it\n // back is still possible: an icon font's ligature name IS text\n // content (\"save\" renders as a glyph but reads as the word), so a\n // bare `textContent` on a button with an icon would answer\n // \"saveSave\". `.twm-btn__label` is the answer to \"what does this\n // button say\", and `aria-hidden` on the glyph is what keeps a screen\n // reader from saying the ligature name out loud beside the label.\n if (a.icon) {\n const glyph = document.createElement('span');\n glyph.className = 'material-symbols-outlined twm-btn__glyph';\n glyph.textContent = String(a.icon);\n glyph.setAttribute('aria-hidden', 'true');\n const text = document.createElement('span');\n text.className = 'twm-btn__label';\n text.textContent = String(a.label || '');\n btn.append(glyph, text);\n } else {\n btn.textContent = String(a.label || '');\n }\n btn.dataset.actionIdx = String(i);\n // C27. AN ACTION MAY START DISABLED, and be enabled later.\n //\n // Every action here dismisses the dialog and resolves the promise \u2014\n // that is the documented contract, and it is why a caller cannot\n // \"refuse\" a submit: by the time it sees the value, the form and\n // everything typed into it are gone. So a dialog whose primary\n // action is not yet valid has had exactly two options: let the user\n // press it and lose their work to a toast, or reach into this markup\n // from outside, which is the coupling C6 exists to remove.\n //\n // `disabled` plus the controller handed to `onMount` is the third.\n // Absent \u2014 every existing caller \u2014 the button is enabled exactly as\n // before.\n if (a.disabled) btn.disabled = true;\n if (a.value !== undefined && a.value !== null) {\n btn.dataset.actionValue = String(a.value);\n }\n actionsEl.appendChild(btn);\n });\n body.appendChild(actionsEl);\n\n let _resolved = false;\n // DECLARED BEFORE THE WINDOW, INSTALLED AFTER IT. `onClose` below closes\n // over `_dropMaxListener`, and `show()` runs between the two \u2014 so\n // leaving the declaration down where the listener is installed would\n // stretch a temporal dead zone across a call that could come back\n // through `onClose`. The cost of getting that wrong is a ReferenceError\n // inside a dismissal, which is the least debuggable place in this file.\n let _onMax = null;\n const _dropMaxListener = () => {\n if (!_onMax) return;\n window.removeEventListener('managed-window-maximized', _onMax);\n _onMax = null;\n };\n const win = new ManagedWindow({\n container: _modalHost,\n id: `twm-modal-${_modalSeq++}`,\n title,\n icon,\n content: body,\n modal: true,\n backdropBlur,\n backdropOpacity,\n canMinimize: false,\n // C29. OPT-IN, and `false` is still the default \u2014 a confirmation\n // and a two-field form have nothing to do with the extra room, and\n // a button that grows a dialog nobody wanted grown is noise in the\n // one place a user looks for the X. `onMaximize` is deliberately\n // NOT set: see the C29 note in the header \u2014 with nothing claiming\n // the gesture, `toggleMaximize` runs the rectangle, which is the\n // only thing maximise can mean for a window with no tile.\n canMaximize: !!maximizable,\n canResize: true,\n canDrag: true,\n defaultWidth: width,\n defaultHeight: height,\n // Esc, the X and the backdrop all land here without passing through\n // `close`, so the listener is dropped here as well as there \u2014 the\n // three dismissals a user reaches for most are exactly the ones\n // that would otherwise leak it.\n onClose: () => {\n _dropMaxListener();\n if (!_resolved) { _resolved = true; resolve(null); }\n },\n });\n win.show();\n\n // C29. THE STATE, FOR A CALLER WHOSE LAYOUT DEPENDS ON IT.\n //\n // `managed-window-maximized` (C16) is the framework's own edge and it\n // is global, so it is filtered on the id \u2014 the alternative, wrapping\n // `win.toggleMaximize`, would miss `_restoreState`'s replay of a\n // persisted maximised window and any consumer calling the method\n // directly. Removed on close: a dialog is short-lived and twenty of\n // them over a session leaving listeners behind is a leak with no\n // symptom until the twenty-first.\n //\n // The CSS half needs no listener at all \u2014 `ManagedWindow` toggles\n // `.twm-managed-window--maximized` on its own element, which is an\n // ancestor of everything in here, so a stylesheet can respond without\n // any of this. The callback is for the layout a stylesheet cannot\n // reach: content whose height was fixed by the CALLER's own rules.\n if (maximizable && typeof onMaximizeChange === 'function') {\n _onMax = (e) => {\n if (e.detail?.id !== win.id) return;\n try { onMaximizeChange(!!e.detail.maximized, bodyInner); }\n catch (err) { console.warn('[modal] onMaximizeChange threw', err); }\n };\n window.addEventListener('managed-window-maximized', _onMax);\n }\n\n const close = (value) => {\n if (_resolved) return;\n _resolved = true;\n _dropMaxListener();\n resolve(value);\n try { win.close({ force: true }); } catch {}\n };\n actionsEl.addEventListener('click', (e) => {\n const btn = e.target.closest('[data-action-idx]');\n if (!btn) return;\n const idx = Number(btn.dataset.actionIdx);\n const a = acts[idx];\n if (!a) return;\n // `keepOpen` actions are side-effects (Copy, Apply\n // Without Close, etc.): if the action's `onClick` returns\n // anything (or just runs), the modal stays open. Use this\n // for Copy buttons and the like.\n if (a.keepOpen) {\n // `onClick` receives `close` so the handler can\n // validate, do an async side-effect, and only then\n // dismiss the modal with whatever value it chooses.\n // Returning early without calling `close` leaves the\n // modal open (which is the whole point of keepOpen).\n try { a.onClick?.(close); } catch (err) { console.warn(err); }\n return;\n }\n close(a.value);\n });\n\n /**\n * C27. What `onMount` is handed alongside the body, so a form can keep\n * its own submit honest without knowing this file's markup.\n *\n * Addressed by an action's VALUE rather than its index: an index is a\n * fact about the order the caller happened to list them in, and a caller\n * that inserted a \"Save and add another\" in the middle would silently\n * start disabling Cancel.\n */\n const controls = {\n // NOT `CSS.escape`. It is a browser global that jsdom does not\n // provide, and this file is mounted under jsdom by four render\n // tests \u2014 so reaching for it turns \"the dialog is valid\" into a\n // ReferenceError in every one of them, and into nothing at all in a\n // headless consumer. An attribute selector needs `\"` and `\\`\n // escaped and nothing else.\n actionButton: (value) => actionsEl.querySelector(\n `[data-action-value=\"${String(value).replace(/[\"\\\\]/g, '\\\\$&')}\"]`),\n setActionEnabled(value, enabled) {\n const btn = controls.actionButton(value);\n if (btn) btn.disabled = !enabled;\n return !!btn;\n },\n };\n\n // onMount runs after the next frame so layout has settled and\n // the inner content has a real bounding box for sizing.\n if (typeof onMount === 'function') {\n requestAnimationFrame(() => {\n try { onMount(bodyInner, controls); } catch (err) { console.warn(err); }\n });\n }\n // C28. THE FIRST FIELD, IF THERE IS ONE. Otherwise the primary button.\n //\n // A dialog that asks for a name should let you type it: *\"on any 'new'\n // dialog I would like to have the first input focused by default, so\n // that I can directly start typing.\"* Focusing the action button first\n // means every new table, project and column begins with a click into a\n // field that was the only place the caret could sensibly have been.\n //\n // The button remains the fallback, which is what keeps Enter useful on\n // a dialog that asks nothing \u2014 a confirmation has no field, and there\n // the primary action IS the answer.\n //\n // DISABLED CONTROLS ARE SKIPPED, and so is anything `readonly`: a form\n // whose first control is a read-only statement panel (C27's disabled\n // primary is the sibling case) would swallow the caret into a box that\n // cannot take it.\n requestAnimationFrame(() => {\n const field = bodyInner.querySelector(\n 'input:not([type=\"hidden\"]):not([disabled]):not([readonly]),'\n + ' textarea:not([disabled]):not([readonly]),'\n + ' select:not([disabled])');\n if (field) {\n field.focus();\n // The caret at the END of whatever is already there, not\n // selecting it: an edit dialog opens on a value the user means\n // to amend, and a selected value is one keystroke from gone.\n try { field.setSelectionRange?.(field.value.length, field.value.length); }\n catch { /* a `select`, or an input type with no selection */ }\n return;\n }\n const idx = acts.findIndex((a) => a.primary);\n const which = idx >= 0 ? idx : acts.length - 1;\n actionsEl.querySelector(`[data-action-idx=\"${which}\"]`)?.focus();\n });\n });\n}\n\n\n// \u2500\u2500\u2500 helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction _estimateHeight(fields) {\n // Header (~36) + actions row (~46) + section dividers (~32 each)\n // + per-field rows (~46 text, ~140 textarea, ~32 checkbox)\n // + body padding. Clamped to 200..80vh.\n let body = 0;\n for (const f of fields) {\n if (f.section) body += 32;\n else if (f.type === 'textarea') {\n const rows = Number(f.rows) || 4;\n body += 32 + rows * 18;\n if (f.hint) body += 16;\n }\n else if (f.type === 'checkbox') body += 32;\n else { body += 46; if (f.hint) body += 16; }\n }\n const base = 36 + 46 + 28 + body;\n const max = Math.floor(window.innerHeight * 0.8);\n return Math.max(200, Math.min(base, max));\n}\n\nfunction _estimateWidth(fields) {\n // Forms with a textarea or section divider get the wider layout so\n // multi-line content + grouped fields breathe; otherwise 480 px is\n // enough for label + single-line input pairs.\n const wide = fields.some((f) => f && (f.type === 'textarea' || f.section));\n return wide ? 640 : 480;\n}\n\nfunction _optionValues(options) {\n return (options || []).map((o) => (typeof o === 'object') ? o.value : o);\n}\n\n\n/** Show / hide the \"\u21B3 will create \u2026\" hint as the user types into a\n * create-capable combobox. */\nfunction _wireComboboxHint(host, f) {\n const input = host.querySelector(`input[name=\"${f.name}\"]`);\n const hintEl = host.querySelector(`.twm-combobox__hint[data-for=\"${f.name}\"]`);\n if (!input || !hintEl) return;\n const known = new Set(_optionValues(f.options));\n const label = (f.create && f.create.hint) || 'new entry';\n const update = () => {\n const v = input.value.trim();\n if (v && !known.has(v)) {\n hintEl.textContent = `\u21B3 will create ${label} \u201C${v}\u201D`;\n hintEl.hidden = false;\n } else {\n hintEl.hidden = true;\n }\n };\n input.addEventListener('input', update);\n update();\n}\n\n\nfunction _renderEntry(f, value) {\n if (f && f.section != null) {\n return `<div class=\"twm-modal__section\">${_esc(f.section)}</div>`;\n }\n return _renderField(f, value);\n}\n\nfunction _renderField(f, value) {\n const required = f.required ? 'required' : '';\n const rowMod = (f.type === 'textarea') ? ' twm-modal__row--multiline' : '';\n const hintHtml = f.hint\n ? `<div class=\"twm-modal__hint--field\">${_esc(f.hint)}</div>` : '';\n const errHtml = `<div class=\"twm-modal__field-error\" data-field-error=\"${f.name}\" hidden></div>`;\n const wrap = (inner) => `\n <label class=\"twm-modal__row${rowMod}\" data-field-row=\"${f.name}\">\n <span>${_esc(f.label || '')}</span>\n ${inner}\n ${hintHtml}\n ${errHtml}\n </label>\n `;\n\n if (f.type === 'checkbox') {\n const checked = (value === true || value === 'true') ? 'checked' : '';\n return `\n <label class=\"twm-modal__row\" data-field-row=\"${f.name}\">\n <span></span>\n <span class=\"twm-modal__check\">\n <input name=\"${f.name}\" type=\"checkbox\" ${checked}>\n <span>${_esc(f.label || '')}</span>\n </span>\n ${hintHtml}\n ${errHtml}\n </label>\n `;\n }\n\n if (f.type === 'textarea') {\n const v = value == null ? '' : String(value);\n const rows = f.rows != null ? `rows=\"${Number(f.rows) || 4}\"` : 'rows=\"4\"';\n const placeholder = f.placeholder ? `placeholder=\"${_esc(f.placeholder)}\"` : '';\n return wrap(`<textarea name=\"${f.name}\" ${rows} ${placeholder} ${required}>${_esc(v)}</textarea>`);\n }\n\n const v = value == null ? '' : String(value);\n if (f.type === 'select' && f.create) {\n const dlId = `ea-dl-${f.name}-${_modalSeq}`;\n const opts = _optionValues(f.options)\n .map((ov) => `<option value=\"${_esc(ov)}\"></option>`).join('');\n const placeholder = f.placeholder\n ? `placeholder=\"${_esc(f.placeholder)}\"` : 'placeholder=\"type to pick or create\u2026\"';\n return wrap(`\n <div class=\"twm-combobox\">\n <input name=\"${f.name}\" type=\"text\" list=\"${dlId}\"\n value=\"${_esc(v)}\" ${placeholder} ${required}\n autocomplete=\"off\">\n <datalist id=\"${dlId}\">${opts}</datalist>\n <span class=\"twm-combobox__hint\" data-for=\"${f.name}\" hidden></span>\n </div>\n `);\n }\n if (f.type === 'select') {\n const opts = (f.options || []).map((o) => {\n const ov = (typeof o === 'object') ? o.value : o;\n const ol = (typeof o === 'object') ? o.label : o;\n return `<option value=\"${_esc(ov)}\" ${ov === v ? 'selected' : ''}>${_esc(ol)}</option>`;\n }).join('');\n return wrap(`<select name=\"${f.name}\" ${required}>${opts}</select>`);\n }\n const step = f.step != null ? `step=\"${f.step}\"` : '';\n const placeholder = f.placeholder ? `placeholder=\"${_esc(f.placeholder)}\"` : '';\n const type = (f.type === 'number' || f.type === 'password') ? f.type : 'text';\n return wrap(`<input name=\"${f.name}\" type=\"${type}\" value=\"${_esc(v)}\" ${step} ${placeholder} ${required}>`);\n}\n\nfunction _esc(s) {\n return String(s == null ? '' : s)\n .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n", "/**\n * context_menu.js \u2014 small popup menu that matches Ecosim's `.context-menu`\n * idiom (see main_new.css line 9713 and ui/js/ui/components/data_table.js).\n *\n * Usage:\n * showContextMenu(x, y, [\n * { label: 'Open', icon: 'open_in_new', action: 'open' },\n * { separator: true },\n * { label: 'Delete', icon: 'delete', action: 'delete', danger: true },\n * ], (action) => { ... });\n *\n * The menu auto-closes on outside click / scroll / Escape.\n *\n * \u2500\u2500 It is operable by keyboard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * Items were bare `<div>`s with a click listener: no role, no tab stop, no\n * arrow-key movement, and `showContextMenu` never moved focus into the menu.\n * A consumer that opened this from Shift+F10 \u2014 the rail in Tables does \u2014 put a\n * menu on screen that the keyboard could only dismiss. Every command in it was\n * unreachable without a pointer, and nothing said so.\n *\n * So an item is a `<button role=\"menuitem\">`, the first enabled one takes focus\n * when the menu opens, Up/Down/Home/End move between them and wrap, Enter and\n * Space activate, Escape closes, and focus returns to whatever had it before.\n * Disabled items are skipped by the arrows rather than focusable-but-inert.\n * `<button>` rather than a div with `tabindex`: the browser then gives Enter and\n * Space for free and screen readers announce it without further help.\n */\n\nimport { modalHost } from './modal.js';\n\nlet _activeMenu = null;\nlet _returnFocusTo = null;\n\nexport function showContextMenu(x, y, items, onAction) {\n hideContextMenu();\n\n // Whatever had focus when the menu opened gets it back when the menu\n // closes. Without this a keyboard user who presses Escape is returned to\n // `document.body` and has to tab back to where they were.\n _returnFocusTo = document.activeElement;\n\n const menu = document.createElement('div');\n menu.className = 'twm-context-menu ea-context-menu';\n menu.setAttribute('role', 'menu');\n\n for (const it of items) {\n if (it.separator) {\n const sep = document.createElement('div');\n sep.className = 'twm-context-menu__separator';\n sep.setAttribute('role', 'separator');\n menu.appendChild(sep);\n continue;\n }\n const row = document.createElement('button');\n row.type = 'button';\n row.setAttribute('role', 'menuitem');\n let cls = 'twm-context-menu-item';\n if (it.danger) cls += ' twm-delete-node';\n if (it.disabled) cls += ' disabled';\n row.className = cls;\n if (it.disabled) {\n row.disabled = true;\n row.setAttribute('aria-disabled', 'true');\n }\n // A DISABLED ROW WITH NO EXPLANATION IS A DEAD CONTROL. `title` was\n // accepted by callers and rendered by nothing \u2014 the item was built with\n // one, the row silently dropped it, and the user got a greyed line with\n // no way to learn why. It is the same shape as the classes this\n // repository keeps finding: no error, no throw, and invisible to every\n // test that checks the row is disabled.\n if (it.title) row.title = it.title;\n row.innerHTML = `\n <span class=\"material-symbols-outlined\">${it.icon || ''}</span>\n <span>${escapeHtml(it.label)}</span>\n `;\n if (!it.disabled) {\n row.addEventListener('click', (e) => {\n e.stopPropagation();\n hideContextMenu();\n onAction?.(it.action);\n });\n }\n menu.appendChild(row);\n }\n\n // C25. THE SAME QUESTION A MODAL ASKS: which window is the user in? A\n // consumer spanning two browser windows sets the host when its focus moves;\n // null \u2014 every consumer today \u2014 is `document.body`, exactly as before. A\n // menu in the wrong window is worse than a modal in the wrong window,\n // because it is positioned at coordinates from the OTHER one.\n (modalHost() || document.body).appendChild(menu);\n menu.style.display = 'block';\n _activeMenu = menu;\n\n // Position with viewport clamping.\n const rect = menu.getBoundingClientRect();\n const left = Math.min(x, window.innerWidth - rect.width - 8);\n const top = Math.min(y, window.innerHeight - rect.height - 8);\n menu.style.left = `${Math.max(0, left)}px`;\n menu.style.top = `${Math.max(0, top)}px`;\n\n // Close on outside click / scroll / Escape.\n setTimeout(() => {\n document.addEventListener('mousedown', _outsideHandler, { once: true, capture: true });\n }, 0);\n document.addEventListener('keydown', _keyHandler);\n window.addEventListener('scroll', hideContextMenu, { once: true, capture: true });\n\n // Focus the first item the keyboard can actually use. `preventScroll` so a\n // menu opened near the bottom of a long page does not jump it.\n _enabledItems(menu)[0]?.focus({ preventScroll: true });\n}\n\nexport function hideContextMenu() {\n if (!_activeMenu) return;\n const returnTo = _returnFocusTo;\n const held = _activeMenu.contains(document.activeElement);\n _activeMenu.remove();\n _activeMenu = null;\n _returnFocusTo = null;\n document.removeEventListener('keydown', _keyHandler);\n // Only take focus back if the menu still had it. A click elsewhere has\n // already moved focus deliberately and must not be undone.\n if (held && returnTo?.isConnected) returnTo.focus?.({ preventScroll: true });\n}\n\nfunction _enabledItems(menu) {\n return [...menu.querySelectorAll('.twm-context-menu-item:not(.disabled)')];\n}\n\n/** Up/Down/Home/End move; the list WRAPS, which is what a menu of four items\n * wants and what every desktop menu does. Enter and Space are the button's\n * own, so they are not bound here. */\nfunction _keyHandler(e) {\n if (!_activeMenu) return;\n if (e.key === 'Escape') { e.preventDefault(); hideContextMenu(); return; }\n const items = _enabledItems(_activeMenu);\n if (items.length === 0) return;\n const at = items.indexOf(document.activeElement);\n let next = null;\n if (e.key === 'ArrowDown') next = items[(at + 1 + items.length) % items.length];\n else if (e.key === 'ArrowUp') next = items[(at - 1 + items.length) % items.length];\n else if (e.key === 'Home') next = items[0];\n else if (e.key === 'End') next = items[items.length - 1];\n if (!next) return;\n e.preventDefault();\n next.focus({ preventScroll: true });\n}\n\nfunction _outsideHandler(e) {\n if (_activeMenu && !_activeMenu.contains(e.target)) hideContextMenu();\n}\n\nfunction escapeHtml(s) {\n return String(s).replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["errEl", "v", "placeholder"]
7
+ }