flexdesk 0.17.18 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +1 -0
  3. package/css/flexdesk.css +7547 -5891
  4. package/css/reset.css +1 -1
  5. package/css/tokens.css +30 -1
  6. package/dist/charts.js +83 -13
  7. package/dist/charts.js.map +2 -2
  8. package/dist/chunk-3BLIVOFD.js +778 -0
  9. package/dist/chunk-3BLIVOFD.js.map +7 -0
  10. package/dist/{chunk-7OL6F2AM.js → chunk-4X4SY3CP.js} +7 -5
  11. package/dist/chunk-4X4SY3CP.js.map +7 -0
  12. package/dist/chunk-DNJ3N4T4.js +82 -0
  13. package/dist/chunk-DNJ3N4T4.js.map +7 -0
  14. package/dist/{chunk-PGZ4EHSE.js → chunk-HL3IIHU3.js} +501 -359
  15. package/dist/chunk-HL3IIHU3.js.map +7 -0
  16. package/dist/{chunk-REJ5HLIM.js → chunk-HYJYYEPK.js} +2 -2
  17. package/dist/{chunk-7U4IB65H.js → chunk-LLBJ2SJH.js} +5 -3
  18. package/dist/{chunk-7U4IB65H.js.map → chunk-LLBJ2SJH.js.map} +1 -1
  19. package/dist/{chunk-XJNADRGR.js → chunk-NFZJ6P42.js} +188 -96
  20. package/dist/chunk-NFZJ6P42.js.map +7 -0
  21. package/dist/chunk-PAI2ZUWV.js +260 -0
  22. package/dist/chunk-PAI2ZUWV.js.map +7 -0
  23. package/dist/chunk-Q7BALEPX.js +527 -0
  24. package/dist/chunk-Q7BALEPX.js.map +7 -0
  25. package/dist/{chunk-2Z2ENHR3.js → chunk-QRBOIUU3.js} +7 -8
  26. package/dist/chunk-QRBOIUU3.js.map +7 -0
  27. package/dist/chunk-QXAOTGCC.js +40 -0
  28. package/dist/chunk-QXAOTGCC.js.map +7 -0
  29. package/dist/chunk-UKP22RUG.js +219 -0
  30. package/dist/chunk-UKP22RUG.js.map +7 -0
  31. package/dist/chunk-VLOAI3HS.js +43 -0
  32. package/dist/chunk-VLOAI3HS.js.map +7 -0
  33. package/dist/{chunk-WUFLGETN.js → chunk-WVFGV5FT.js} +2 -39
  34. package/dist/{chunk-WUFLGETN.js.map → chunk-WVFGV5FT.js.map} +3 -3
  35. package/dist/editor.js +3 -2
  36. package/dist/editor.js.map +1 -1
  37. package/dist/flexdesk.css +7547 -5891
  38. package/dist/flow.js +29 -97
  39. package/dist/flow.js.map +3 -3
  40. package/dist/git.js +1356 -0
  41. package/dist/git.js.map +7 -0
  42. package/dist/reset.css +1 -1
  43. package/dist/tiles.js +20 -15
  44. package/dist/tiles.js.map +2 -2
  45. package/dist/tokens.css +30 -1
  46. package/dist/widgets.js +4146 -1199
  47. package/dist/widgets.js.map +4 -4
  48. package/dist/wm.js +120 -32
  49. package/dist/wm.js.map +2 -2
  50. package/docs/README.md +9 -4
  51. package/docs/best-practices.md +2 -2
  52. package/docs/components/charts.md +40 -0
  53. package/docs/components/chat-shell.md +204 -0
  54. package/docs/components/conversation.md +10 -0
  55. package/docs/components/filter-builder.md +292 -0
  56. package/docs/components/forms-and-dialogs.md +30 -0
  57. package/docs/components/git.md +355 -0
  58. package/docs/components/input-fields.md +191 -2
  59. package/docs/components/keymap.md +8 -0
  60. package/docs/components/menus.md +86 -2
  61. package/docs/components/panels-and-desktops.md +16 -5
  62. package/docs/components/shell.md +108 -1
  63. package/docs/components/workspace-picker.md +193 -0
  64. package/docs/concepts/building-and-packaging.md +2 -2
  65. package/docs/concepts/packages-and-entry-points.md +14 -13
  66. package/docs/concepts/state-guard.md +3 -3
  67. package/docs/quickstart.md +1 -1
  68. package/git.js +13 -0
  69. package/package.json +6 -1
  70. package/src/charts/plot_config_editor.js +92 -6
  71. package/src/charts/plot_grid.js +6 -1
  72. package/src/flow/kit/dom.js +28 -119
  73. package/src/flow/kit/step_picker.js +1 -1
  74. package/src/flow/kit/value_picker.js +1 -1
  75. package/src/flow/outline/menu.js +1 -1
  76. package/src/git/diff_viewer.js +212 -0
  77. package/src/git/diff_window.js +51 -0
  78. package/src/git/git_graph.js +401 -0
  79. package/src/git/git_ops.js +61 -0
  80. package/src/git/git_panel.js +755 -0
  81. package/src/git/index.js +9 -0
  82. package/src/git/line_diff.js +202 -0
  83. package/src/tiles/tile_base.js +5 -4
  84. package/src/tiling/desktops.js +10 -8
  85. package/src/tiling/keymap.js +6 -5
  86. package/src/tiling/kind_taxonomy.js +15 -2
  87. package/src/tiling/shell.js +124 -17
  88. package/src/tiling/tile_renderer.js +2 -3
  89. package/src/ui/components/action_dropdown.js +4 -78
  90. package/src/ui/components/anchored_layer.js +255 -0
  91. package/src/ui/components/chat_shell.js +926 -0
  92. package/src/ui/components/chat_state_machine.js +223 -0
  93. package/src/ui/components/choice_controls.js +45 -2
  94. package/src/ui/components/color_field.js +3 -2
  95. package/src/ui/components/color_list_field.js +3 -2
  96. package/src/ui/components/combobox.js +422 -0
  97. package/src/ui/components/context_menu.js +8 -2
  98. package/src/ui/components/conversation_view.js +17 -2
  99. package/src/ui/components/data_table.js +2 -2
  100. package/src/ui/components/filter_builder.js +791 -0
  101. package/src/ui/components/filter_model.js +682 -0
  102. package/src/ui/components/inline_renamer.js +10 -5
  103. package/src/ui/components/managed_window.js +24 -47
  104. package/src/ui/components/modal.js +104 -117
  105. package/src/ui/components/option_list.js +160 -0
  106. package/src/ui/components/popover.js +94 -0
  107. package/src/ui/components/recent_items.js +169 -0
  108. package/src/ui/components/select.js +322 -0
  109. package/src/ui/components/tag_input.js +77 -8
  110. package/src/ui/components/workspace_picker.js +1253 -0
  111. package/src/ui/utils/fire_event.js +17 -0
  112. package/src/ui/utils/focusable.js +25 -0
  113. package/src/ui/utils/modal_host.js +27 -0
  114. package/src/ui/utils/tooltip_service.js +81 -14
  115. package/widgets.js +12 -2
  116. package/css/base.css +0 -10547
  117. package/css/overrides.css +0 -2596
  118. package/dist/chunk-2Z2ENHR3.js.map +0 -7
  119. package/dist/chunk-7JI7HHSY.js +0 -107
  120. package/dist/chunk-7JI7HHSY.js.map +0 -7
  121. package/dist/chunk-7OL6F2AM.js.map +0 -7
  122. package/dist/chunk-MIW7D6WC.js +0 -526
  123. package/dist/chunk-MIW7D6WC.js.map +0 -7
  124. package/dist/chunk-PGZ4EHSE.js.map +0 -7
  125. package/dist/chunk-XJNADRGR.js.map +0 -7
  126. /package/dist/{chunk-REJ5HLIM.js.map → chunk-HYJYYEPK.js.map} +0 -0
@@ -2,6 +2,6 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/editor/notebook_tab_bar.js"],
4
4
  "sourcesContent": ["/**\n * NotebookTabBar \u2014 tab strip showing open files, with dirty indicators,\n * close buttons, drag-and-drop reordering, double-click rename, and\n * right-click context menu.\n *\n * Uses the same `.tab` / `.tab-title` / `.tab-dirty` / `.tab-close` DOM structure\n * as the flow-page tab bar, so all editor tab bars share a unified appearance.\n *\n * Calls back into parent via:\n * onActivate(filePath)\n * onClose(filePath)\n * onCloseOthers(filePath)\n * onCloseToRight(filePath)\n * onCloseAll()\n * onCloseSaved()\n * onRename(oldPath, newPath)\n * onDuplicate(filePath)\n * onReorder(orderedPaths)\n * onRevealInExplorer(filePath)\n * onSplitRight(filePath)\n * onMoveToOtherPane(filePath)\n * onDropFromOtherPane(filePath) \u2014 cross-pane drag-and-drop\n */\n\nimport { DragReorder } from '../ui/components/drag_reorder.js';\nimport { revealInStrip, wheelStripSideways } from '../ui/utils/strip_scroll.js';\nimport { placeMenuInViewport, moveMenuFocus, focusMenuItem } from '../ui/utils/menu_viewport.js';\n\n/** The MIME type a dragged editor tab carries when the embedder names none. */\nexport const DEFAULT_TAB_DRAG_MIME = 'application/x-flexdesk-tab';\n\n/** Custom MIME type so tab bars and editor panes recognise cross-pane drags. */\nlet _tabMime = DEFAULT_TAB_DRAG_MIME;\n\n/**\n * Name the MIME type editor tabs carry while dragged. Every NotebookTabBar and\n * EditorPane in the document reads it at drag time, so they always agree.\n * Call at boot, before the first drag.\n *\n * @param {string} [mime=DEFAULT_TAB_DRAG_MIME]\n */\nexport function setTabDragMime(mime = DEFAULT_TAB_DRAG_MIME) {\n _tabMime = (typeof mime === 'string' && mime) ? mime : DEFAULT_TAB_DRAG_MIME;\n}\n\n/** @returns {string} The MIME type editor tabs carry while dragged. */\nexport function getTabDragMime() {\n return _tabMime;\n}\n\nexport class NotebookTabBar {\n /** @type {HTMLElement} */\n #container = null;\n\n /** @type {object[]} [{filePath, label, fileType, isDirty}] */\n #tabs = [];\n\n /** @type {string|null} */\n #activeTab = null;\n\n /** @type {object} callbacks */\n #callbacks = {};\n\n // \u2500\u2500\u2500 Context menu \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\n /** @type {HTMLElement|null} */\n #contextMenuEl = null;\n /** BUG-0040. What had focus when the menu opened. @type {Element|null} */\n #contextMenuReturnFocus = null;\n\n /** @type {Function|null} */\n #boundHideContextMenu = null;\n\n // \u2500\u2500\u2500 Drag and drop \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\n /** Same-pane reorder. Cross-pane drops stay in this class's own\n * container handlers (#onStripDrag*).\n * @type {DragReorder|null} */\n #dragReorder = null;\n\n mount(container, callbacks = {}) {\n this.#container = container;\n this.#callbacks = callbacks;\n this.#container.className = 'tabs notebook-tabs';\n this.#boundHideContextMenu = () => this.#hideContextMenu();\n this.#installStripDnDHandlers();\n // NOT PASSIVE: it has to be able to take the wheel from the page.\n this.#container.addEventListener('wheel', this.#onWheel, { passive: false });\n this.#dragReorder = new DragReorder({\n container: this.#container,\n itemSelector: '.tab',\n keyAttr: 'path',\n axis: 'x',\n draggableGuard: (e) => {\n if (e.target.closest('.twm-tab-close')) return false;\n const title = e.target.closest('.tab')?.querySelector('.twm-tab-title');\n return title?.getAttribute('contenteditable') !== 'true';\n },\n onDragStart: (e, key, item) => {\n // Carry the cross-pane MIME so other panes accept this tab.\n try { e.dataTransfer.setData(_tabMime, key); } catch (_) {}\n // C33. \u2026AND LET THE HOST ADD ITS OWN, which is the whole of\n // what a second consumer of this strip needs from it.\n //\n // `DragReorder` publishes this hook for exactly this purpose\n // (\"host hook to augment dataTransfer (e.g. add a cross-pane\n // MIME)\", `../ui/components/drag_reorder.js`), and this class\n // consumed it and stopped there. The tile renderer mounts one\n // of these per tile and needs to mark the drag as one of ITS\n // tabs \u2014 a different question from \"is this an editor tab\",\n // asked of the same gesture.\n //\n // ADDITIVE BY CONSTRUCTION: the tab MIME is set first and\n // unchanged, so `EditorPane`'s existing cross-pane drop cannot\n // notice that anyone else is listening.\n try { this.#callbacks.onDragStart?.(e, key, item); }\n catch (err) { console.error('[tab-bar] onDragStart threw', err); }\n },\n onReorder: (order) => this.#callbacks.onReorder?.(order),\n });\n this.#render();\n }\n\n /**\n * Sync tabs from ProjectModel state.\n * @param {Array<{ filePath: string, fileType: string, isDirty: boolean }>} tabs\n * @param {string|null} activeFilePath\n */\n update(tabs, activeFilePath) {\n this.#tabs = tabs;\n this.#activeTab = activeFilePath;\n this.#render();\n }\n\n markDirty(filePath, isDirty) {\n const tab = this.#tabs.find(t => t.filePath === filePath);\n if (tab) {\n tab.isDirty = isDirty;\n this.#render();\n }\n }\n\n dispose() {\n this.#hideContextMenu();\n this.#removeStripDnDHandlers();\n this.#container?.removeEventListener('wheel', this.#onWheel);\n if (this.#revealFrame) cancelAnimationFrame(this.#revealFrame);\n this.#revealFrame = 0;\n this.#dragReorder?.destroy();\n this.#dragReorder = null;\n this.#container = null;\n }\n\n // Material symbol per file kind, merged over the generic set. The map used to\n // hardcode EcoAgent's kinds \u2014 `scenario`, `dashboard`, `model` \u2014 so a generic\n // tab bar knew one application's file taxonomy. Pass `fileTypeIcons` to add\n // your own.\n static #FILE_TYPE_ICONS = {\n module: 'extension',\n test: 'fact_check',\n diff: 'difference',\n unknown: 'description',\n };\n\n static #EXTRA_ICONS = {};\n\n /**\n * Add your application's file kinds to the tab-bar icon map.\n *\n * The map used to hardcode `scenario: 'science'`, `dashboard: 'dashboard'` and\n * `model: 'schema'` \u2014 EcoAgent's file taxonomy, baked into a generic tab bar.\n *\n * @param {Object.<string, string>} icons fileType -> material symbol name\n */\n static setFileTypeIcons(icons = {}) {\n NotebookTabBar.#EXTRA_ICONS = { ...icons };\n }\n\n static #iconFor(fileType) {\n return NotebookTabBar.#EXTRA_ICONS[fileType]\n ?? NotebookTabBar.#FILE_TYPE_ICONS[fileType]\n ?? NotebookTabBar.#FILE_TYPE_ICONS.unknown;\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\n // Rendering\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\n\n #render() {\n if (!this.#container) return;\n\n // THE STRIP KEEPS ITS SCROLL ACROSS A REBUILD. Emptying it shrinks its\n // scroll width to nothing, which clamps `scrollLeft` to 0 \u2014 so every\n // render (a tab activated, a title changed) threw a strip scrolled to\n // its tenth tab back to its first.\n const keepLeft = this.#container.scrollLeft;\n this.#container.innerHTML = '';\n\n for (const tab of this.#tabs) {\n const isActive = tab.filePath === this.#activeTab;\n const rawPath = tab.filePath.replace(/^(diff|builtin):\\/\\//, '');\n const label = tab.label\n ?? (tab.fileType === 'diff'\n ? `\u2194 ${rawPath.split('/').pop()}`\n : rawPath.split('/').pop().replace(/\\.[^.]+$/, ''));\n const icon = NotebookTabBar.#iconFor(tab.fileType);\n\n const tabEl = document.createElement('div');\n tabEl.className = `tab${isActive ? ' active' : ''}${tab.isPreview ? ' tab--preview' : ''}`;\n tabEl.dataset.path = tab.filePath;\n tabEl.title = tab.filePath;\n\n tabEl.innerHTML = `\n <span class=\"material-symbols-outlined tab-icon\">${icon}</span>\n <span class=\"twm-tab-title\">${this.#escHtml(label)}</span>\n ${tab.isDirty ? '<span class=\"tab-dirty\">\\u25cf</span>' : ''}\n ${tab.readonly ? '' : '<button class=\"twm-tab-close\" title=\"Close tab\" aria-label=\"Close tab\">\\u00d7</button>'}\n `;\n\n // Click \u2192 activate\n tabEl.addEventListener('click', (e) => {\n if (e.target.closest('.twm-tab-close')) return;\n if (tabEl.querySelector('.twm-tab-title')?.getAttribute('contenteditable') === 'true') return;\n this.#callbacks.onActivate?.(tab.filePath);\n });\n\n // Close button\n tabEl.querySelector('.twm-tab-close')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.#callbacks.onClose?.(tab.filePath);\n });\n\n // Double-click \u2192 inline rename\n tabEl.addEventListener('dblclick', (e) => {\n if (e.target.closest('.twm-tab-close')) return;\n this.#beginInlineRename(tab.filePath);\n });\n\n // F2 \u2192 inline rename\n tabEl.addEventListener('keydown', (e) => {\n if (e.key === 'F2') {\n e.preventDefault();\n this.#beginInlineRename(tab.filePath);\n }\n });\n\n // Context menu\n tabEl.addEventListener('contextmenu', (e) => {\n e.preventDefault();\n e.stopPropagation();\n this.#showContextMenu(e.clientX, e.clientY, tab.filePath);\n });\n\n this.#container.appendChild(tabEl);\n }\n // Make the freshly-rendered tabs draggable-to-reorder.\n this.#dragReorder?.attach();\n\n this.#container.scrollLeft = keepLeft;\n // A tab that has just BECOME active \u2014 opened, switched to, moved here\n // from another pane \u2014 is scrolled into the strip. Only then: a render\n // for any other reason (a title, a dirty dot) leaves the strip where the\n // reader put it, rather than yanking it back to the active tab.\n const revealKey = `${this.#activeTab}\\u0000${this.#tabs.length}`;\n if (revealKey !== this.#revealedKey) {\n this.#revealedKey = revealKey;\n this.#revealActive(true);\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\n // Scrolling (1.5) \u2014 the strip scrolls rather than wrapping or shrinking its\n // tabs, and hides its scrollbar (a bar in a 26px strip eats a third of every\n // label). So it owes a mouse user every other way in: a plain wheel, the\n // active tab brought into view, and \u2014 in `DragReorder._autoScrollEdge` \u2014\n // the edges scrolling while a tab is dragged toward them.\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\n\n /** @type {string|null} the active tab + count last scrolled into view */\n #revealedKey = null;\n\n /** @type {number} a pending retry, for a strip not yet laid out */\n #revealFrame = 0;\n\n /** Bring the active tab fully into the strip (`revealInStrip`). */\n #revealActive(retry = false) {\n const el = this.#container;\n if (!el) return;\n const active = el.querySelector(':scope > .tab.active');\n if (!active) return;\n // Mounted but not laid out yet (the renderer updates a strip before it\n // attaches it). Once, on the next frame \u2014 not a loop.\n if (!revealInStrip(el, active) && retry && !this.#revealFrame\n && typeof requestAnimationFrame === 'function') {\n this.#revealFrame = requestAnimationFrame(() => {\n this.#revealFrame = 0;\n this.#revealActive(false);\n });\n }\n }\n\n /** A vertical wheel scrolls the strip sideways (`wheelStripSideways`). */\n #onWheel = (e) => wheelStripSideways(this.#container, e);\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\n // Drag and drop reordering (same-pane + cross-pane)\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\n\n #installStripDnDHandlers() {\n if (!this.#container) return;\n this.#container.addEventListener('dragover', this.#onStripDragOver);\n this.#container.addEventListener('drop', this.#onStripDrop);\n this.#container.addEventListener('dragleave', this.#onStripDragLeave);\n }\n\n #removeStripDnDHandlers() {\n if (!this.#container) return;\n this.#container.removeEventListener('dragover', this.#onStripDragOver);\n this.#container.removeEventListener('drop', this.#onStripDrop);\n this.#container.removeEventListener('dragleave', this.#onStripDragLeave);\n }\n\n /** Check whether a drag event carries a cross-pane tab (not from this bar).\n *\n * C33. A HOST MAY NAME A SECOND MIME, at mount, as\n * `callbacks.externalTabMimes` \u2014 the tile renderer's tabs are not editor\n * tabs and do not carry the editor tab MIME (getTabDragMime()), so without this the\n * strip in tile B would refuse a tab dragged out of tile A's strip. Empty\n * by default, so every existing consumer keeps exactly today's admission\n * test.\n *\n * THE `isActive()` EARLY RETURN STAYS FIRST AND STAYS UNCHANGED. It is the\n * one line that keeps this bar's own reorder from being read as a foreign\n * drop, and no widening of the MIME list may be allowed to reach past it.\n *\n * Only `types` is consulted, never `getData` \u2014 under the HTML5 protected\n * mode `getData` returns the empty string for every event except `drop`,\n * so a payload read here would find nothing and arm nothing. */\n #isExternalTabDrag(e) {\n if (this.#dragReorder?.isActive()) return false; // same-pane drag is active\n const extra = Array.isArray(this.#callbacks.externalTabMimes)\n ? this.#callbacks.externalTabMimes : [];\n try {\n const types = e.dataTransfer.types;\n return types.includes(_tabMime) || extra.some((m) => types.includes(m));\n } catch { return false; }\n }\n\n // Same-pane reorder is handled by the shared DragReorder module (set\n // up in mount()). These container handlers cover ONLY cross-pane tab\n // drops \u2014 gated on `#isExternalTabDrag`, which defers while our own\n // reorder drag is active.\n\n #onStripDragOver = (e) => {\n if (this.#isExternalTabDrag(e)) {\n e.preventDefault();\n try { e.dataTransfer.dropEffect = 'move'; } catch (_) {}\n this.#container.classList.add('notebook-tabs--drop-target');\n }\n };\n\n #onStripDrop = (e) => {\n if (this.#isExternalTabDrag(e)) {\n e.preventDefault();\n this.#container.classList.remove('notebook-tabs--drop-target');\n const filePath = e.dataTransfer.getData(_tabMime) || e.dataTransfer.getData('text/plain');\n if (filePath) {\n this.#callbacks.onDropFromOtherPane?.(filePath);\n }\n }\n };\n\n #onStripDragLeave = (e) => {\n if (!this.#container.contains(e.relatedTarget)) {\n this.#container.classList.remove('notebook-tabs--drop-target');\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\n // Context menu\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\n\n #showContextMenu(x, y, filePath) {\n this.#hideContextMenu();\n\n const idx = this.#tabs.findIndex(t => t.filePath === filePath);\n const total = this.#tabs.length;\n\n const allItems = [\n { label: 'Rename', icon: 'edit', action: 'rename', shortcut: 'F2' },\n { label: 'Duplicate', icon: 'content_copy', action: 'duplicate' },\n { separator: true },\n { label: 'Split Right', icon: 'vertical_split', action: 'split-right', shortcut: 'Ctrl+\\\\' },\n { label: 'Move to Other Pane',icon: 'swap_horiz', action: 'move-to-other' },\n { separator: true },\n { label: 'Close', icon: 'close', action: 'close', danger: true },\n { label: 'Close Others', icon: 'close_fullscreen', action: 'close-others', danger: true, disabled: total <= 1 },\n { label: 'Close to the Right',icon: 'tab_close', action: 'close-right', danger: true, disabled: idx === -1 || idx >= total - 1 },\n { label: 'Close All', icon: 'tab_close_inactive', action: 'close-all', danger: true },\n { label: 'Close Saved', icon: 'check_circle', action: 'close-saved', danger: true },\n { separator: true },\n { label: 'Reveal in Explorer', icon: 'folder_open', action: 'reveal' },\n ];\n\n const menuFilter = this.#callbacks.menuFilter;\n const items = menuFilter\n ? allItems.filter(item => item.separator || menuFilter(item.action, filePath) !== false)\n : allItems;\n\n // Collapse consecutive / leading / trailing separators\n const cleaned = [];\n for (const item of items) {\n if (item.separator) {\n if (cleaned.length > 0 && !cleaned[cleaned.length - 1].separator) cleaned.push(item);\n } else {\n cleaned.push(item);\n }\n }\n while (cleaned.length > 0 && cleaned[cleaned.length - 1].separator) cleaned.pop();\n\n const menu = document.createElement('div');\n menu.className = 'nb-context-menu';\n menu.setAttribute('role', 'menu');\n\n for (const item of cleaned) {\n if (item.separator) {\n const sep = document.createElement('div');\n sep.className = 'nb-context-menu__separator';\n menu.appendChild(sep);\n continue;\n }\n\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.setAttribute('role', 'menuitem');\n btn.className = 'nb-context-menu__item';\n if (item.danger) btn.classList.add('nb-context-menu__item--danger');\n if (item.disabled) btn.classList.add('nb-context-menu__item--disabled');\n\n btn.innerHTML = `<span class=\"material-symbols-outlined\">${item.icon}</span><span>${this.#escHtml(item.label)}</span>${\n item.shortcut ? `<span class=\"nb-context-menu__shortcut\">${item.shortcut}</span>` : ''\n }`;\n\n if (!item.disabled) {\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n this.#hideContextMenu();\n this.#handleContextMenuAction(item.action, filePath);\n });\n }\n menu.appendChild(btn);\n }\n\n this.#contextMenuReturnFocus = document.activeElement;\n document.body.appendChild(menu);\n this.#contextMenuEl = menu;\n\n // BUG-0040. Inside the window, at most its height, scrolling when\n // longer; the keyboard starts on the first item it can use.\n placeMenuInViewport(menu, x, y);\n focusMenuItem(this.#contextMenuItems()[0]);\n\n // Animate in\n requestAnimationFrame(() => menu.classList.add('visible'));\n\n // Dismiss on outside click or Escape\n setTimeout(() => {\n document.addEventListener('click', this.#boundHideContextMenu, true);\n document.addEventListener('contextmenu', this.#boundHideContextMenu, true);\n }, 0);\n document.addEventListener('keydown', this.#onKeyDown);\n }\n\n #hideContextMenu() {\n if (!this.#contextMenuEl) return;\n const returnTo = this.#contextMenuReturnFocus;\n const held = this.#contextMenuEl.contains(document.activeElement);\n this.#contextMenuEl.remove();\n this.#contextMenuEl = null;\n this.#contextMenuReturnFocus = null;\n document.removeEventListener('click', this.#boundHideContextMenu, true);\n document.removeEventListener('contextmenu', this.#boundHideContextMenu, true);\n document.removeEventListener('keydown', this.#onKeyDown);\n if (held && returnTo?.isConnected) returnTo.focus?.({ preventScroll: true });\n }\n\n /** BUG-0040. The menu's items the keyboard can land on. */\n #contextMenuItems() {\n return this.#contextMenuEl\n ? [...this.#contextMenuEl.querySelectorAll('.nb-context-menu__item:not(.nb-context-menu__item--disabled)')]\n : [];\n }\n\n #onKeyDown = (e) => {\n if (e.key === 'Escape') { this.#hideContextMenu(); return; }\n // BUG-0040. Up/Down/Home/End move between the items and scroll the\n // focused one into view; Enter and Space are the buttons' own.\n moveMenuFocus(e, this.#contextMenuItems());\n };\n\n #handleContextMenuAction(action, filePath) {\n switch (action) {\n case 'rename': this.#beginInlineRename(filePath); break;\n case 'duplicate': this.#callbacks.onDuplicate?.(filePath); break;\n case 'close': this.#callbacks.onClose?.(filePath); break;\n case 'close-others': this.#callbacks.onCloseOthers?.(filePath); break;\n case 'close-right': this.#callbacks.onCloseToRight?.(filePath); break;\n case 'close-all': this.#callbacks.onCloseAll?.(); break;\n case 'close-saved': this.#callbacks.onCloseSaved?.(); break;\n case 'split-right': this.#callbacks.onSplitRight?.(filePath); break;\n case 'move-to-other':this.#callbacks.onMoveToOtherPane?.(filePath); break;\n case 'reveal': this.#callbacks.onRevealInExplorer?.(filePath); break;\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\n // Inline rename (contenteditable on tab title)\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\n\n #beginInlineRename(filePath) {\n const tabEl = this.#container?.querySelector(`.tab[data-path=\"${CSS.escape(filePath)}\"]`);\n if (!tabEl) return;\n\n const title = tabEl.querySelector('.twm-tab-title');\n if (!title || title.getAttribute('contenteditable') === 'true') return;\n\n const tab = this.#tabs.find(t => t.filePath === filePath);\n if (!tab) return;\n\n // model.notebook, builtin, and readonly files cannot be renamed\n if (tab.fileType === 'model') return;\n if (filePath.startsWith('builtin://')) return;\n if (tab.readonly) return;\n\n const original = title.textContent || '';\n title.setAttribute('contenteditable', 'true');\n title.focus();\n\n try {\n const range = document.createRange();\n range.selectNodeContents(title);\n const sel = window.getSelection();\n sel.removeAllRanges();\n sel.addRange(range);\n } catch (_) { /* ignore */ }\n\n let committed = false;\n\n const finish = (commit) => {\n if (committed) return;\n committed = true;\n\n const newName = (title.textContent || '').trim();\n title.removeAttribute('contenteditable');\n title.removeEventListener('keydown', onKeyDown);\n title.removeEventListener('blur', onBlur, true);\n\n try { window.getSelection()?.removeAllRanges?.(); } catch (_) { /* ignore */ }\n\n if (commit && newName && newName !== original && /^[\\w][\\w.-]*$/.test(newName)) {\n title.textContent = newName;\n const dir = filePath.split('/').slice(0, -1).join('/');\n const ext = filePath.split('.').pop();\n const newPath = `${dir}/${newName}.${ext}`;\n this.#callbacks.onRename?.(filePath, newPath);\n } else {\n title.textContent = original;\n }\n };\n\n const onKeyDown = (e) => {\n if (e.key === 'Enter') { e.preventDefault(); finish(true); }\n else if (e.key === 'Escape') { e.preventDefault(); finish(false); }\n e.stopPropagation();\n };\n\n const onBlur = () => finish(true);\n\n title.addEventListener('keydown', onKeyDown);\n title.addEventListener('blur', onBlur, true);\n }\n\n #escHtml(s) {\n return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n }\n}\n"],
5
- "mappings": "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",
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,17 +2,27 @@ import {
2
2
  ColorField,
3
3
  ManagedWindow,
4
4
  ToggleField,
5
+ createCombobox,
6
+ fireEvent,
7
+ isHexColor,
8
+ normalizeHex
9
+ } from "./chunk-HL3IIHU3.js";
10
+ import {
5
11
  choiceList,
6
12
  findChoice,
7
13
  flatChoices,
8
- isHexColor,
9
- normalizeHex,
14
+ modalHost,
10
15
  selectBox,
11
16
  toggleChips
12
- } from "./chunk-PGZ4EHSE.js";
17
+ } from "./chunk-Q7BALEPX.js";
13
18
  import {
14
19
  DragReorder
15
- } from "./chunk-WUFLGETN.js";
20
+ } from "./chunk-WVFGV5FT.js";
21
+ import {
22
+ focusMenuItem,
23
+ moveMenuFocus,
24
+ placeMenuInViewport
25
+ } from "./chunk-QXAOTGCC.js";
16
26
 
17
27
  // src/ui/components/sortable_list.js
18
28
  var ROW_CLASS = "twm-sortable-list__row";
@@ -428,21 +438,14 @@ var ColorListField = class {
428
438
  #emit(commit) {
429
439
  this.#writeInput();
430
440
  if (this.input) {
431
- this.input.dispatchEvent(new Event("input", { bubbles: true }));
432
- if (commit) this.input.dispatchEvent(new Event("change", { bubbles: true }));
441
+ fireEvent(this.input, "input", { bubbles: true });
442
+ if (commit) fireEvent(this.input, "change", { bubbles: true });
433
443
  }
434
444
  this.onChange?.(this.getValue(), { commit });
435
445
  }
436
446
  };
437
447
 
438
448
  // src/ui/components/modal.js
439
- var _modalHost = null;
440
- function setModalHost(el) {
441
- _modalHost = el || null;
442
- }
443
- function modalHost() {
444
- return _modalHost;
445
- }
446
449
  var _modalSeq = 1;
447
450
  function openForm({ title, fields = [], defaults = {}, submitLabel = "OK", onFieldChange = null } = {}) {
448
451
  return new Promise((resolve) => {
@@ -461,7 +464,7 @@ function openForm({ title, fields = [], defaults = {}, submitLabel = "OK", onFie
461
464
  </form>
462
465
  `;
463
466
  const win = new ManagedWindow({
464
- container: _modalHost,
467
+ container: modalHost(),
465
468
  id: `twm-modal-${_modalSeq++}`,
466
469
  title: title || "Dialog",
467
470
  icon: "edit_note",
@@ -501,12 +504,7 @@ function openForm({ title, fields = [], defaults = {}, submitLabel = "OK", onFie
501
504
  body.querySelector('[data-action="cancel"]')?.addEventListener("click", () => close(null));
502
505
  const formFields = _formFields(fields);
503
506
  const optionsOf = new Map(formFields.map((f) => [f.name, f.options]));
504
- const comboHints = /* @__PURE__ */ new Map();
505
507
  for (const f of formFields) {
506
- if (f.type === "select" && f.create) {
507
- const update = _wireComboboxHint(body, f, () => optionsOf.get(f.name));
508
- if (update) comboHints.set(f.name, update);
509
- }
510
508
  if (f.type === "picker") _wirePicker(body, f);
511
509
  }
512
510
  const rowHidden = (name) => !!body.querySelector(`[data-field-row="${name}"]`)?.hidden;
@@ -540,13 +538,7 @@ function openForm({ title, fields = [], defaults = {}, submitLabel = "OK", onFie
540
538
  }
541
539
  win.show();
542
540
  const comboOption = (f, text) => findChoice(flatChoices(optionsOf.get(f.name)), text, true);
543
- const unavailable = (f, value) => {
544
- const c = controls.get(f.name);
545
- if (c) return c.unavailable();
546
- if (f.type !== "select" || !f.create || value == null || value === "") return null;
547
- const o = comboOption(f, value);
548
- return o?.disabled ? o.reason || "" : null;
549
- };
541
+ const unavailable = (f) => controls.get(f.name)?.unavailable() ?? null;
550
542
  for (const f of formFields) {
551
543
  const el = body.querySelector(`[name="${f.name}"]`);
552
544
  el?.addEventListener("input", () => clearFieldError(f.name));
@@ -592,11 +584,6 @@ function openForm({ title, fields = [], defaults = {}, submitLabel = "OK", onFie
592
584
  const el = body.querySelector(`[name="${name}"]`);
593
585
  if (!el) return void 0;
594
586
  if (el.type === "checkbox") return !!el.checked;
595
- const f = fieldOf(name);
596
- if (f?.type === "select" && f.create) {
597
- const match = comboOption(f, el.value);
598
- return match ? match.value : el.value;
599
- }
600
587
  return el.value;
601
588
  },
602
589
  set(name, value) {
@@ -611,28 +598,17 @@ function openForm({ title, fields = [], defaults = {}, submitLabel = "OK", onFie
611
598
  return;
612
599
  }
613
600
  const f = fieldOf(name);
614
- const shut = f?.type === "select" && f.create && comboOption(f, value)?.disabled;
615
- el.value = value == null || shut ? "" : String(value);
616
- comboHints.get(name)?.();
601
+ el.value = value == null ? "" : String(value);
617
602
  if (f?.type === "picker") {
618
603
  const display = body.querySelector(`[data-picker-display="${name}"]`);
619
604
  if (display) display.value = _pickerLabel(f, el.value);
620
605
  }
621
606
  },
622
607
  setOptions(name, options) {
623
- const f = fieldOf(name);
624
- if (!f) return;
625
608
  const c = controls.get(name);
626
- if (c?.setOptions) {
627
- optionsOf.set(name, options);
628
- c.setOptions(options);
629
- return;
630
- }
631
- if (f.type !== "select" || !f.create) return;
609
+ if (!c?.setOptions) return;
632
610
  optionsOf.set(name, options);
633
- const list = body.querySelector(`[data-datalist-for="${name}"]`);
634
- if (list) list.innerHTML = _datalistOptions(options);
635
- comboHints.get(name)?.();
611
+ c.setOptions(options);
636
612
  }
637
613
  };
638
614
  const fire = (name) => {
@@ -701,14 +677,14 @@ function openForm({ title, fields = [], defaults = {}, submitLabel = "OK", onFie
701
677
  for (const f of formFields) {
702
678
  clearFieldError(f.name);
703
679
  if (rowHidden(f.name)) {
704
- if (unavailable(f, data[f.name]) != null) data[f.name] = f.type === "radio" ? null : "";
680
+ if (unavailable(f) != null) data[f.name] = f.type === "radio" ? null : "";
705
681
  continue;
706
682
  }
707
683
  const v = data[f.name];
708
684
  let msg = f.required && _missing(f, v) ? "Required." : null;
709
685
  if (!msg && _outOfRange(f, v)) msg = _rangeMessage(f);
710
686
  if (!msg) {
711
- const reason = unavailable(f, v);
687
+ const reason = unavailable(f);
712
688
  if (reason != null) msg = reason ? `Not available: ${reason}` : "Not available.";
713
689
  }
714
690
  if (!msg) msg = controls.get(f.name)?.error?.() ?? null;
@@ -767,7 +743,7 @@ function openConfirm({
767
743
  body.append(messageEl, actionsEl);
768
744
  let _resolved = false;
769
745
  const win = new ManagedWindow({
770
- container: _modalHost,
746
+ container: modalHost(),
771
747
  id: `twm-confirm-${_modalSeq++}`,
772
748
  title: title || "Confirm",
773
749
  icon: icon || (danger ? "warning" : "help"),
@@ -814,7 +790,9 @@ function openModal({
814
790
  backdropBlur = void 0,
815
791
  backdropOpacity = void 0,
816
792
  maximizable = false,
817
- onMaximizeChange = null
793
+ onMaximizeChange = null,
794
+ beforeClose = null,
795
+ dismissOnBackdrop = false
818
796
  } = {}) {
819
797
  return new Promise((resolve) => {
820
798
  const body = document.createElement("div");
@@ -861,7 +839,7 @@ function openModal({
861
839
  _onMax = null;
862
840
  };
863
841
  const win = new ManagedWindow({
864
- container: _modalHost,
842
+ container: modalHost(),
865
843
  id: `twm-modal-${_modalSeq++}`,
866
844
  title,
867
845
  icon,
@@ -882,10 +860,11 @@ function openModal({
882
860
  canDrag: true,
883
861
  defaultWidth: width,
884
862
  defaultHeight: height,
885
- // Esc, the X and the backdrop all land here without passing through
886
- // `close`, so the listener is dropped here as well as there — the
887
- // three dismissals a user reaches for most are exactly the ones
888
- // that would otherwise leak it.
863
+ beforeClose,
864
+ // Esc, the X and an opted-in backdrop all land here without passing
865
+ // through `close`, so the listener is dropped here as well as there —
866
+ // the dismissals a user reaches for most are exactly the ones that
867
+ // would otherwise leak it.
889
868
  onClose: () => {
890
869
  _dropMaxListener();
891
870
  if (!_resolved) {
@@ -895,6 +874,10 @@ function openModal({
895
874
  }
896
875
  });
897
876
  win.show();
877
+ if (dismissOnBackdrop) {
878
+ win.backdropElement.addEventListener("mousedown", (e) => e.preventDefault());
879
+ win.backdropElement.addEventListener("click", () => win.close());
880
+ }
898
881
  if (maximizable && typeof onMaximizeChange === "function") {
899
882
  _onMax = (e) => {
900
883
  if (e.detail?.id !== win.id) return;
@@ -958,6 +941,7 @@ function openModal({
958
941
  });
959
942
  }
960
943
  requestAnimationFrame(() => {
944
+ if (bodyInner.contains(document.activeElement)) return;
961
945
  const field = bodyInner.querySelector(
962
946
  'input:not([type="hidden"]):not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly]), select:not([disabled])'
963
947
  );
@@ -1023,8 +1007,54 @@ function _rangeMessage(f) {
1023
1007
  function _offered(options) {
1024
1008
  return flatChoices(options).filter((o) => !o.disabled);
1025
1009
  }
1026
- function _datalistOptions(options) {
1027
- return _offered(options).map((o) => `<option value="${_esc(o.value)}"></option>`).join("");
1010
+ function _mountCombobox(slot, f, value) {
1011
+ let options = f.options;
1012
+ const named = (text) => findChoice(flatChoices(options), text, true);
1013
+ const shown = (v) => v == null || named(v)?.disabled ? "" : String(v);
1014
+ const hint = document.createElement("span");
1015
+ hint.className = "twm-combobox__hint";
1016
+ hint.hidden = true;
1017
+ const noun = f.create === true ? null : _createSpec(f).hint || "new entry";
1018
+ const updateHint = () => {
1019
+ const v = c.value.trim();
1020
+ const isNew = noun && v && !flatChoices(options).some((o) => String(o.value) === v);
1021
+ if (isNew) hint.textContent = `\u21B3 will create ${noun} \u201C${v}\u201D`;
1022
+ hint.hidden = !isNew;
1023
+ };
1024
+ const c = createCombobox({
1025
+ options: _offered(options),
1026
+ value: shown(value),
1027
+ name: f.name,
1028
+ placeholder: f.placeholder ?? "type to pick or create\u2026",
1029
+ // Enter keeps what was typed: this field exists to accept a value nobody listed.
1030
+ autoHighlight: false,
1031
+ onChange: () => updateHint()
1032
+ });
1033
+ if (f.required) c.input.required = true;
1034
+ c.el.appendChild(hint);
1035
+ slot.replaceWith(c.el);
1036
+ updateHint();
1037
+ return {
1038
+ read() {
1039
+ const o = named(c.value);
1040
+ return o ? o.value : c.value;
1041
+ },
1042
+ write(v) {
1043
+ c.value = shown(v);
1044
+ updateHint();
1045
+ },
1046
+ focus: c.focus,
1047
+ unavailable() {
1048
+ const o = c.value === "" ? void 0 : named(c.value);
1049
+ return o?.disabled ? o.reason || "" : null;
1050
+ },
1051
+ setOptions(next) {
1052
+ options = next;
1053
+ c.setOptions(_offered(next));
1054
+ updateHint();
1055
+ },
1056
+ dispose: c.destroy
1057
+ };
1028
1058
  }
1029
1059
  function _mountControl(host, f, value, onChange) {
1030
1060
  const slot = host.querySelector(`[data-control-slot="${f.name}"]`);
@@ -1067,6 +1097,7 @@ function _mountControl(host, f, value, onChange) {
1067
1097
  dispose: () => c2.dispose()
1068
1098
  };
1069
1099
  }
1100
+ if (f.type === "select" && f.create) return _mountCombobox(slot, f, value);
1070
1101
  const common = { keepMissing: false, loose: true };
1071
1102
  const disabledReason = (entry) => entry?.disabled ? entry.reason || "" : null;
1072
1103
  if (f.type === "select") {
@@ -1173,31 +1204,11 @@ function _wirePicker(host, f) {
1173
1204
  const isPair = typeof picked === "object";
1174
1205
  hidden.value = String((isPair ? picked.value : picked) ?? "");
1175
1206
  display.value = isPair && picked.label != null ? String(picked.label) : _pickerLabel(f, hidden.value);
1176
- hidden.dispatchEvent(new Event("change", { bubbles: true }));
1207
+ fireEvent(hidden, "change", { bubbles: true });
1177
1208
  };
1178
1209
  btn.addEventListener("click", open);
1179
1210
  display.addEventListener("click", open);
1180
1211
  }
1181
- function _wireComboboxHint(host, f, optionsOf) {
1182
- const input = host.querySelector(`input[name="${f.name}"]`);
1183
- const hintEl = host.querySelector(`.twm-combobox__hint[data-for="${f.name}"]`);
1184
- if (!input || !hintEl) return;
1185
- if (f.create === true) return;
1186
- const label = _createSpec(f).hint || "new entry";
1187
- const update = () => {
1188
- const v = input.value.trim();
1189
- const known = new Set(flatChoices(optionsOf()).map((o) => String(o.value)));
1190
- if (v && !known.has(v)) {
1191
- hintEl.textContent = `\u21B3 will create ${label} \u201C${v}\u201D`;
1192
- hintEl.hidden = false;
1193
- } else {
1194
- hintEl.hidden = true;
1195
- }
1196
- };
1197
- input.addEventListener("input", update);
1198
- update();
1199
- return update;
1200
- }
1201
1212
  function _renderEntry(f, defaults) {
1202
1213
  if (f && f.section != null) {
1203
1214
  const attr = f.name ? ` data-field-row="${f.name}"` : "";
@@ -1273,21 +1284,6 @@ function _renderField(f, value) {
1273
1284
  </div>
1274
1285
  `);
1275
1286
  }
1276
- if (f.type === "select" && f.create) {
1277
- const shown = findChoice(flatChoices(f.options), value, true)?.disabled ? "" : v;
1278
- const dlId = `ea-dl-${f.name}-${_modalSeq}`;
1279
- const opts = _datalistOptions(f.options);
1280
- const placeholder2 = f.placeholder ? `placeholder="${_esc(f.placeholder)}"` : 'placeholder="type to pick or create\u2026"';
1281
- return wrap(`
1282
- <div class="twm-combobox">
1283
- <input name="${f.name}" type="text" list="${dlId}"
1284
- value="${_esc(shown)}" ${placeholder2} ${required}
1285
- autocomplete="off">
1286
- <datalist id="${dlId}" data-datalist-for="${f.name}">${opts}</datalist>
1287
- <span class="twm-combobox__hint" data-for="${f.name}" hidden></span>
1288
- </div>
1289
- `);
1290
- }
1291
1287
  if (f.type === "select") return wrap(slot);
1292
1288
  const step = f.step != null ? `step="${f.step}"` : "";
1293
1289
  const placeholder = f.placeholder ? `placeholder="${_esc(f.placeholder)}"` : "";
@@ -1301,13 +1297,109 @@ function _esc(s) {
1301
1297
  return String(s == null ? "" : s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1302
1298
  }
1303
1299
 
1300
+ // src/ui/components/context_menu.js
1301
+ var _activeMenu = null;
1302
+ var _returnFocusTo = null;
1303
+ var _armTimer = null;
1304
+ function showContextMenu(x, y, items, onAction) {
1305
+ hideContextMenu();
1306
+ _returnFocusTo = document.activeElement;
1307
+ const menu = document.createElement("div");
1308
+ menu.className = "twm-context-menu ea-context-menu";
1309
+ menu.setAttribute("role", "menu");
1310
+ for (const it of items) {
1311
+ if (it.separator) {
1312
+ const sep = document.createElement("div");
1313
+ sep.className = "twm-context-menu__separator";
1314
+ sep.setAttribute("role", "separator");
1315
+ menu.appendChild(sep);
1316
+ continue;
1317
+ }
1318
+ const row = document.createElement("button");
1319
+ row.type = "button";
1320
+ row.setAttribute("role", "menuitem");
1321
+ let cls = "twm-context-menu-item";
1322
+ if (it.danger) cls += " twm-delete-node";
1323
+ if (it.disabled) cls += " disabled";
1324
+ row.className = cls;
1325
+ if (it.disabled) {
1326
+ row.disabled = true;
1327
+ row.setAttribute("aria-disabled", "true");
1328
+ }
1329
+ if (it.title) row.title = it.title;
1330
+ row.innerHTML = `
1331
+ <span class="material-symbols-outlined">${it.icon || ""}</span>
1332
+ <span>${escapeHtml(it.label)}</span>
1333
+ `;
1334
+ if (!it.disabled) {
1335
+ row.addEventListener("click", (e) => {
1336
+ e.stopPropagation();
1337
+ hideContextMenu();
1338
+ onAction?.(it.action);
1339
+ });
1340
+ }
1341
+ menu.appendChild(row);
1342
+ }
1343
+ (modalHost() || document.body).appendChild(menu);
1344
+ menu.style.display = "block";
1345
+ _activeMenu = menu;
1346
+ placeMenuInViewport(menu, x, y);
1347
+ _armTimer = setTimeout(() => {
1348
+ document.addEventListener("mousedown", _outsideHandler, { capture: true });
1349
+ }, 0);
1350
+ document.addEventListener("keydown", _keyHandler);
1351
+ window.addEventListener("scroll", _scrollHandler, { capture: true });
1352
+ focusMenuItem(_enabledItems(menu)[0]);
1353
+ }
1354
+ function hideContextMenu() {
1355
+ if (!_activeMenu) return;
1356
+ const returnTo = _returnFocusTo;
1357
+ const held = _activeMenu.contains(document.activeElement);
1358
+ _activeMenu.remove();
1359
+ _activeMenu = null;
1360
+ _returnFocusTo = null;
1361
+ clearTimeout(_armTimer);
1362
+ document.removeEventListener("mousedown", _outsideHandler, { capture: true });
1363
+ document.removeEventListener("keydown", _keyHandler);
1364
+ window.removeEventListener("scroll", _scrollHandler, { capture: true });
1365
+ if (held && returnTo?.isConnected) returnTo.focus?.({ preventScroll: true });
1366
+ }
1367
+ function _enabledItems(menu) {
1368
+ return [...menu.querySelectorAll(".twm-context-menu-item:not(.disabled)")];
1369
+ }
1370
+ function _keyHandler(e) {
1371
+ if (!_activeMenu) return;
1372
+ if (e.key === "Escape") {
1373
+ e.preventDefault();
1374
+ hideContextMenu();
1375
+ return;
1376
+ }
1377
+ moveMenuFocus(e, _enabledItems(_activeMenu));
1378
+ }
1379
+ function _scrollHandler(e) {
1380
+ if (_activeMenu && e.target?.nodeType && _activeMenu.contains(e.target)) return;
1381
+ hideContextMenu();
1382
+ }
1383
+ function _outsideHandler(e) {
1384
+ if (_activeMenu && !_activeMenu.contains(e.target)) hideContextMenu();
1385
+ }
1386
+ function escapeHtml(s) {
1387
+ return String(s).replace(/[&<>"']/g, (c) => ({
1388
+ "&": "&amp;",
1389
+ "<": "&lt;",
1390
+ ">": "&gt;",
1391
+ '"': "&quot;",
1392
+ "'": "&#39;"
1393
+ })[c]);
1394
+ }
1395
+
1304
1396
  export {
1305
1397
  SortableList,
1306
1398
  ColorListField,
1307
- setModalHost,
1308
- modalHost,
1309
1399
  openForm,
1310
1400
  openConfirm,
1311
- openModal
1401
+ openModal,
1402
+ showContextMenu,
1403
+ hideContextMenu
1312
1404
  };
1313
- //# sourceMappingURL=chunk-XJNADRGR.js.map
1405
+ //# sourceMappingURL=chunk-NFZJ6P42.js.map