flexdesk 0.3.0 → 0.4.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.
package/dist/wm.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/tiling/command_palette.js", "../src/tiling/content_registry.js", "../src/tiling/tile_tree.js", "../src/tiling/desktops.js", "../src/tiling/entity_sources.js", "../src/tiling/keymap.js", "../src/tiling/kind_taxonomy.js", "../src/tiling/panel_keys.js", "../src/tiling/landing_table.js", "../src/tiling/loading_overlay.js", "../src/tiling/nav_panel.js", "../src/tiling/tile_breadcrumb.js", "../src/tiling/page_factory.js", "../src/tiling/tile_renderer.js", "../src/tiling/tab_strip.js", "../src/tiling/wm.js", "../src/tiling/tile_tab_menu.js", "../src/tiling/shell.js"],
4
- "sourcesContent": ["/**\n * command_palette.js \u2014 Ctrl+K center popup.\n *\n * \u250C\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\u2510\n * \u2502 search\u2026 \u2502\n * \u251C\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\u2524\n * \u2502 [chip] [chip] [chip] [chip] \u2502 top-nav shortcut chips\n * \u2502 [chip] [chip] \u2502\n * \u2502 \u2502\n * \u2502 \u2610 left nav \u2610 right panel \u2610 bottom \u2502 panel toggles\n * \u251C\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\u2524\n * \u2502 matched entity rows\u2026 \u2502 search results\n * \u2514\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\u2518\n *\n * The chips are the injected taxonomy's top-nav kinds; clicking one\n * loads it into the primary tile. The toggles flip the corresponding\n * panel-tile on/off. Typing searches the injected entity catalog\n * (fetched lazily on first open).\n */\n\nimport { HelpModal } from '../help/help_modal.js';\n\nconst ROOT_ID = 'twm-cmdpal';\n\n// Non-entity commands surfaced in the palette's search results. They\n// carry an `action` so `commit()` runs them instead of opening a tile.\nconst STATIC_COMMANDS = [\n { kind: 'command', id: 'shortcuts', label: 'Keyboard shortcuts',\n hint: 'Show all key bindings (?)', icon: 'keyboard',\n action: () => HelpModal.open('keyboard-shortcuts') },\n];\n\n/** @param taxonomy injected ontology \u2014 supplies the chip strip + row icons\n * @param catalog injected entity catalog \u2014 supplies the searchable rows\n * @param placeholder input placeholder. It names the embedder's entity types\n * (\"try foo:bar\"), so the embedder owns the string.\n * @param onPick C30. `(pick) => truthy` CLAIMS the open, exactly the way\n * `ManagedWindow.onMaximize` claims the maximise gesture.\n * See `commit` for what it is for; omitted, the palette\n * behaves as it always has. */\nexport function createCommandPalette({ wm, api, taxonomy, catalog, placeholder = 'Search\u2026',\n onPick = null }) {\n if (!taxonomy) throw new Error('createCommandPalette: a taxonomy is required');\n if (!catalog) throw new Error('createCommandPalette: an entity catalog is required');\n let overlay = null;\n let entities = [];\n\n const isOpen = () => !!overlay;\n const close = () => {\n if (!overlay) return;\n overlay.remove();\n overlay = null;\n };\n const toggle = () => { isOpen() ? close() : open(); };\n\n const open = async () => {\n if (overlay) return;\n overlay = document.createElement('div');\n overlay.id = ROOT_ID;\n overlay.className = 'twm-cmdpal-overlay';\n overlay.innerHTML = _markup(taxonomy, placeholder, wm);\n document.body.appendChild(overlay);\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) close();\n });\n\n const input = overlay.querySelector('.twm-cmdpal__input');\n const list = overlay.querySelector('[data-role=\"list\"]');\n const chipSlot = overlay.querySelector('[data-role=\"type-chip\"]');\n const chips = overlay.querySelectorAll('[data-shortcut]');\n const toggles = overlay.querySelectorAll('[data-toggle]');\n\n // Reflect current panel state in toggle buttons.\n for (const btn of toggles) {\n const which = btn.dataset.toggle;\n btn.classList.toggle('twm-chip--on', wm.isPanelOpen(which));\n }\n\n chips.forEach((btn) => btn.addEventListener('click', () => {\n const kind = btn.dataset.shortcut;\n wm.openInPrimary(kind);\n close();\n }));\n toggles.forEach((btn) => btn.addEventListener('click', () => {\n const which = btn.dataset.toggle;\n wm.togglePanel(which);\n btn.classList.toggle('twm-chip--on', wm.isPanelOpen(which));\n }));\n\n // Scoped-search state. Typing `<prefix>:` converts the prefix\n // into a visual chip that lives to the left of the input;\n // subsequent typing only matches entities whose navKind is in\n // the chip's set. Cleared by clicking the chip's \u00D7, by pressing\n // Backspace when the input is empty, or by Esc (which closes the\n // palette). A prefix can resolve to MULTIPLE navKinds, so the\n // user's mental model can win over the embedder's internal\n // taxonomy. The resolver lives on the injected catalog, next to\n // the alias table it consults.\n let filtered = [];\n let active = 0;\n let typeFilter = null; // null | string[] (navKinds)\n let typeFilterLabel = null; // chip text, derived from the resolved kinds\n\n const renderChip = () => {\n if (!typeFilter) {\n chipSlot.innerHTML = '';\n return;\n }\n // Display: first kind's icon/label from the taxonomy. A\n // multi-kind alias gets the leading kind's label, which\n // reads as narrower than the alias actually is \u2014 so we\n // override with the typed alias (passed from `refilter`)\n // when it carries information the taxonomy can't infer.\n const head = typeFilter[0];\n const meta = taxonomy.meta(head);\n const label = typeFilterLabel || meta?.label || head;\n chipSlot.innerHTML = `\n <span class=\"twm-cmdpal__type-chip\" title=\"Filtering by ${_esc(label)} (${typeFilter.length} kind${typeFilter.length === 1 ? '' : 's'})\">\n <span class=\"material-symbols-outlined twm-cmdpal__type-chip-icon\">${meta?.icon || 'arrow_right'}</span>\n <span class=\"twm-cmdpal__type-chip-label\">${_esc(label)}</span>\n <button type=\"button\" class=\"twm-cmdpal__type-chip-x\"\n data-action=\"clear-type\"\n aria-label=\"Clear type filter\">\u00D7</button>\n </span>\n `;\n chipSlot.querySelector('[data-action=\"clear-type\"]')\n ?.addEventListener('click', (ev) => {\n ev.preventDefault();\n typeFilter = null;\n typeFilterLabel = null;\n renderChip();\n input.focus();\n refilter();\n });\n };\n\n const render = () => {\n list.innerHTML = filtered.slice(0, 200).map((e, i) => `\n <div class=\"twm-cmdpal__item${i === active ? ' twm-cmdpal__item--active' : ''}\"\n data-idx=\"${i}\">\n <span class=\"material-symbols-outlined twm-cmdpal__icon\">${e.icon || 'arrow_right'}</span>\n <span class=\"twm-cmdpal__label\">${_esc(e.label || e.id)}</span>\n <span class=\"twm-cmdpal__kind\">${e.kind}</span>\n <span class=\"twm-cmdpal__hint\">${_esc(e.hint || '')}</span>\n </div>\n `).join('') || '';\n list.querySelectorAll('[data-idx]').forEach((el) => {\n el.addEventListener('click', () => commit(Number(el.dataset.idx)));\n });\n };\n /**\n * \u2550\u2550 C30. THE PALETTE IS A DOOR, NOT A PLACEMENT POLICY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * `openInPrimary` is the wrong verb for a picked ENTITY and it was the\n * only one here. It RESETS the primary leaf to a single fresh tab \u2014\n * its own doc says so, and says why: navigation from outside the tile\n * is a page change, and a page change replaces the page. So picking a\n * table from Ctrl+K threw away every other tab in that leaf, ignored\n * whatever rule the embedder has about where its entities open (a tab\n * beside its siblings, a floating window on a canvas pane, a split),\n * and bypassed any per-mount-site bookkeeping the embedder keeps \u2014\n * for Tables, the grid registry keyed on `(mount site, table)`, which\n * is what holds staged edits across a re-render.\n *\n * The result was a door that did something different from every other\n * door onto the same object: the rail opened a table one way, the\n * palette another, and only the palette destroyed work. Reported as\n * \"open table via Ctrl+K does not work\" \u2014 which is exactly what it\n * looks like from the outside when the tab you were in is replaced.\n *\n * The library cannot decide this. It does not know what a `table` is,\n * which is the whole point of the injected taxonomy and catalogue. So\n * the embedder is offered the pick and may CLAIM it, on the same\n * contract as `ManagedWindow.onMaximize` (`managed_window.js:230`):\n * return truthy and nothing else happens. Guarded, because a throwing\n * embedder must not leave the palette closed over a half-done open \u2014\n * and unclaimed picks still land in the primary tile, so no existing\n * embedder changes behaviour by upgrading.\n *\n * `pick` is the catalogue row verbatim (`{kind, id, label, hint,\n * props?}`) rather than a re-shaped argument: the embedder wrote the\n * `shape` that produced it, so it is the one party that can read it.\n */\n const commit = (i) => {\n const pick = filtered[i];\n if (!pick) return;\n close();\n if (pick.action) { try { pick.action(); } catch (err) { console.error(err); } return; }\n if (onPick) {\n let handled = false;\n try { handled = onPick(pick) ?? false; }\n catch (err) { console.error('[cmdpal] onPick threw', err); }\n if (handled) return;\n }\n wm.openInPrimary(pick.kind, pick.props || { id: pick.id, label: pick.label });\n };\n\n /** Look for a `<prefix>:` at the start of the raw input. If the\n * prefix resolves to one or more known kinds, promote it to\n * the visual chip and strip it from the input field. Returns\n * true when a chip was set so the caller can decide whether\n * to re-render. */\n const tryConsumePrefix = () => {\n if (typeFilter) return false;\n const m = input.value.match(/^([a-zA-Z][a-zA-Z0-9_-]*):/);\n if (!m) return false;\n const kinds = catalog.resolveTypePrefix(m[1]);\n if (!kinds || kinds.length === 0) return false;\n typeFilter = kinds;\n typeFilterLabel = _titleCase(m[1]);\n input.value = input.value.slice(m[0].length);\n renderChip();\n return true;\n };\n\n const refilter = () => {\n tryConsumePrefix();\n const q = input.value.trim().toLowerCase();\n const filterSet = typeFilter ? new Set(typeFilter) : null;\n const pool = filterSet\n ? entities.filter((e) => filterSet.has(e.kind))\n : entities;\n if (!q) {\n // Scoped + empty query \u2192 list every entity of that\n // kind so the user sees what's available. Unscoped +\n // empty query \u2192 blank list (matches the old behavior\n // where typing nothing meant nothing).\n filtered = typeFilter\n ? pool.slice().sort((a, b) =>\n String(a.label || a.id).localeCompare(b.label || b.id))\n : [];\n } else {\n filtered = _search(pool, q);\n }\n active = 0;\n render();\n };\n input.addEventListener('input', refilter);\n input.addEventListener('keydown', (e) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n active = Math.min(filtered.length - 1, active + 1);\n render();\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n active = Math.max(0, active - 1);\n render();\n } else if (e.key === 'Enter') {\n e.preventDefault();\n commit(active);\n } else if (e.key === 'Escape') {\n close();\n } else if (e.key === 'Backspace' && input.value === '' && typeFilter) {\n // Empty input + active chip \u2192 consume Backspace to drop\n // the chip. Same idiom as Gmail / Linear / VS Code's\n // command palette: the chip behaves like a token before\n // the caret.\n e.preventDefault();\n typeFilter = null;\n typeFilterLabel = null;\n renderChip();\n refilter();\n }\n });\n input.focus();\n\n // Commands are searchable instantly; entity rows are lazy-loaded\n // \u2014 the palette stays usable for shortcuts/commands while we wait.\n // The set is rebuilt on every open so the palette reflects\n // entities the user just added this session (cheap; the catalog\n // runs every source in parallel).\n entities = _decorateIcons(STATIC_COMMANDS.slice(), taxonomy);\n if (api) {\n try {\n entities = _decorateIcons(\n [...STATIC_COMMANDS, ...await catalog.loadAll(api)], taxonomy);\n if (input.value) refilter();\n } catch (err) {\n console.warn('[cmdpal] entity load failed', err);\n }\n }\n };\n\n return { open, close, toggle, isOpen };\n}\n\nfunction _markup(taxonomy, placeholder, wm) {\n // ONLY THE PANELS THIS SHELL CAN ACTUALLY SHOW.\n //\n // The three toggles were hardcoded, so the palette offered `panel:left` to\n // an embedder that has no factory for it \u2014 and `togglePanel` then\n // canonicalized a leaf whose only possible rendering is \"no factory\n // registered yet\", permanently, because the renderer caches tile DOM per\n // (kind, props). C14 closed that path at boot; this closes the other end of\n // it. A panel with no factory is not a panel this shell can show, and the\n // registry is the thing that knows.\n const PANEL_CHIPS = [\n { side: 'left', icon: 'menu', label: 'Left nav' },\n { side: 'right', icon: 'dock_to_left', label: 'Right panel' },\n { side: 'bottom', icon: 'dock_to_bottom', label: 'Bottom panel' },\n ];\n const panelToggles = PANEL_CHIPS\n .filter((p) => wm.content?.has?.(`panel:${p.side}`) !== false)\n .map((p) => `\n <button class=\"twm-chip\" data-toggle=\"${p.side}\">\n <span class=\"material-symbols-outlined\">${p.icon}</span>\n ${p.label}\n </button>`).join('');\n\n const chips = taxonomy.topNavEntries().map((k) => `\n <button class=\"twm-chip\" data-shortcut=\"${k.kind}\">\n <span class=\"material-symbols-outlined\">${k.icon}</span>\n ${k.label}\n </button>\n `).join('');\n return `\n <div class=\"twm-cmdpal\" role=\"dialog\" aria-label=\"Command palette\">\n <div class=\"twm-cmdpal__inputrow\">\n <span class=\"twm-cmdpal__type-chip-slot\" data-role=\"type-chip\"></span>\n <input type=\"text\" class=\"twm-cmdpal__input\"\n placeholder=\"${_esc(placeholder)}\"\n autocomplete=\"off\" />\n </div>\n <div class=\"twm-cmdpal__chips\">${chips}</div>\n <div class=\"twm-cmdpal__toggles\">${panelToggles}</div>\n <div class=\"twm-cmdpal__list\" data-role=\"list\"></div>\n <div class=\"twm-cmdpal__footer\">\n <span><kbd>\u2191</kbd><kbd>\u2193</kbd> navigate</span>\n <span><kbd>Enter</kbd> open</span>\n <span><kbd>Esc</kbd> close</span>\n </div>\n </div>\n `;\n}\n\n/** Attach an icon per row by consulting the injected taxonomy. Keeps the\n * palette and breadcrumb visually consistent: one source of truth for\n * what icon represents a given kind. */\nfunction _decorateIcons(rows, taxonomy) {\n return rows.map((r) => {\n if (r.icon) return r;\n const meta = taxonomy.meta(r.kind);\n return { ...r, icon: meta?.icon || 'arrow_right' };\n });\n}\n\nfunction _search(entities, q) {\n const scored = [];\n for (const e of entities) {\n const id = String(e.id || '').toLowerCase();\n const label = String(e.label || '').toLowerCase();\n const hint = String(e.hint || '').toLowerCase();\n let r;\n if (id === q || label === q) r = 0;\n else if (id.startsWith(q)) r = 1;\n else if (label.startsWith(q)) r = 2;\n else if (id.includes(q)) r = 3;\n else if (label.includes(q)) r = 4;\n else if (hint.includes(q)) r = 5;\n else continue;\n scored.push({ e, r });\n }\n scored.sort((a, b) => a.r - b.r || a.e.label.localeCompare(b.e.label));\n return scored.map((x) => x.e);\n}\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n}\n\n/** Display form for a typed prefix: capitalise the first letter, and\n * swap any underscores/hyphens for spaces so a multi-word alias reads\n * as prose. Used for the scope chip's label, so it reflects what the\n * user actually typed rather than the kind it resolved to. */\nfunction _titleCase(s) {\n const str = String(s || '').replace(/[_-]+/g, ' ').trim();\n if (!str) return '';\n return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();\n}\n", "/**\n * content_registry.js \u2014 maps a content kind string to a factory that\n * mounts that content into a tile body element.\n *\n * A content factory has the shape:\n *\n * (hostEl, props, ctx) -> { destroy?(), title?, ... }\n *\n * The tile renderer calls the factory once when the leaf is first\n * mounted with a given (kind, props) pair, and `destroy()` on unmount.\n *\n * \u2500\u2500 ONE REGISTRY PER SHELL \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 * This module used to hold a module-level `Map` plus a `register(kind, fn)`\n * export. Two problems, both structural:\n *\n * 1. Two shells on one page shared their kind -> factory table, so a\n * foreign-ontology shell silently inherited the other embedder's\n * factories. A module singleton is the global we are removing,\n * wearing a hat.\n * 2. `register()` could be called AFTER the first mount. A leaf mounted\n * against a missing factory renders the placeholder and the renderer\n * caches that DOM per (kind, props) \u2014 a later registration invalidates\n * nothing, so an entire restored session stays wrong.\n *\n * `createContentRegistry(map)` fixes both: the map is a REQUIRED\n * constructor argument of `createShell()`, validated up-front, and frozen\n * before the WM is built. There is nothing left to call late.\n *\n * This module knows no kind names but one \u2014 `window-placeholder`, which is\n * the WM's OWN content (shown in a tile whose content was promoted into a\n * managed window). Defaulting it here means an embedder cannot forget it\n * and get a \"no factory registered\" tile the first time it promotes.\n */\n\nexport const PLACEHOLDER_KIND = 'window-placeholder';\n\n/** Shown in a tile while its content lives in a managed window. Pure\n * `twm-*` markup; clicking the button demotes the window back. */\nfunction windowPlaceholderFactory(hostEl, props, ctx) {\n hostEl.innerHTML = `\n <div class=\"twm-window-placeholder\">\n <span class=\"material-symbols-outlined twm-window-placeholder__icon\">open_in_new</span>\n <div class=\"twm-window-placeholder__title\">${_esc(props.originalTitle || props.originalKind || 'content')}</div>\n <div class=\"twm-window-placeholder__hint\">is open in a managed window</div>\n <button class=\"twm-window-placeholder__btn\" data-action=\"bring-back\">\n Bring back to this tile\n </button>\n </div>\n `;\n hostEl.querySelector('[data-action=\"bring-back\"]')?.addEventListener('click', () => {\n ctx.wm?.bringBackWindow?.(props.windowId);\n });\n return { title: `${props.originalTitle || props.originalKind} (window)` };\n}\n\n/**\n * Build a shell-scoped content registry.\n *\n * @param {Object<string, Function>} map kind -> content factory\n * @returns {{ has(kind): boolean, mount(kind, hostEl, props, ctx): object }}\n */\nexport function createContentRegistry(map = {}) {\n if (!map || typeof map !== 'object') {\n throw new TypeError('createContentRegistry: `content` must be an object of kind -> factory');\n }\n for (const [kind, factory] of Object.entries(map)) {\n if (typeof factory !== 'function') {\n throw new TypeError(\n `createContentRegistry: factory for \"${kind}\" must be a function`);\n }\n }\n const factories = new Map(Object.entries(map));\n if (!factories.has(PLACEHOLDER_KIND)) {\n factories.set(PLACEHOLDER_KIND, windowPlaceholderFactory);\n }\n\n return Object.freeze({\n has: (kind) => factories.has(kind),\n\n mount: (kind, hostEl, props, ctx) => {\n const factory = factories.get(kind) || _placeholder(kind);\n try {\n return factory(hostEl, props || {}, ctx || {}) || {};\n } catch (err) {\n console.error('[content_registry] mount failed for', kind, err);\n hostEl.innerHTML =\n `<div class=\"twm-tile-error\">Mount failed: ${err && err.message || err}</div>`;\n return {};\n }\n },\n });\n}\n\nfunction _placeholder(kind) {\n return (hostEl) => {\n hostEl.innerHTML = `\n <div class=\"tile-placeholder\">\n <div class=\"tile-placeholder__title\">${kind}</div>\n <div class=\"tile-placeholder__hint\">no factory registered yet</div>\n </div>\n `;\n return { title: kind };\n };\n}\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => (\n { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]\n ));\n}\n", "/**\n * tile_tree.js \u2014 i3/sway-style binary tree for the tiling window manager.\n *\n * A workspace is one tree of Node objects:\n *\n * Node = { id, kind: 'split' | 'leaf', parentId, ... }\n * Split: { dir: 'h' | 'v', children: [childId, ...], sizes: [number, ...] }\n * Leaf: { tabs: [{kind, props, title}], activeTabIdx, content, title }\n *\n * **Tabs**. Each leaf carries an ordered list of tab specs and an active\n * index. The active tab is mirrored onto the legacy `content` /\n * `title` fields so the rest of the codebase (renderer, WM,\n * breadcrumb, navigateBack, \u2026) keeps reading `leaf.content.kind`\n * unchanged. Single-tab leaves behave identically to today; multi-tab\n * leaves expose a small TabBar at the bottom of the tile.\n *\n * The tree carries no DOM. A separate renderer maps it to flex containers.\n * Mutations return new node-id arrays; consumers re-render after each call.\n */\n\nlet _idCounter = 1;\nfunction _newId(prefix) { return `${prefix}-${_idCounter++}`; }\n\nfunction _isPanel(leaf) {\n return String(leaf?.content?.kind || '').startsWith('panel:');\n}\n\n/** Mirror the active tab back onto `leaf.content` / `leaf.title` so the\n * rest of the system sees no diff. Called after every tab mutation. */\nfunction _syncActiveTab(leaf) {\n if (!leaf || leaf.kind !== 'leaf') return;\n const tabs = leaf.tabs || [];\n if (tabs.length === 0) {\n leaf.content = null;\n leaf.title = leaf.title || '';\n leaf.activeTabIdx = 0;\n return;\n }\n if (leaf.activeTabIdx == null\n || leaf.activeTabIdx < 0\n || leaf.activeTabIdx >= tabs.length) {\n leaf.activeTabIdx = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));\n }\n const t = tabs[leaf.activeTabIdx];\n leaf.content = { kind: t.kind, props: t.props || {} };\n leaf.title = t.title || t.kind || '';\n}\n\nexport function makeLeaf({ content = null, title = '' } = {}) {\n const leaf = {\n id: _newId('leaf'),\n kind: 'leaf',\n parentId: null,\n // Tabs are the source of truth; content/title mirror tabs[active].\n tabs: content\n ? [{ kind: content.kind, props: content.props || {}, title: title || content.kind || '' }]\n : [],\n activeTabIdx: content ? 0 : 0,\n content,\n title,\n // Per-top-nav-page tab archive. When a top-nav button switches\n // this leaf to another page, the current `tabs` + `activeTabIdx`\n // get stashed here under the LEAVING page's key, and the target\n // page's archived list (if any) gets restored. This is what\n // makes a tile remember its tab set when the user clicks away to\n // another top-nav page and back. Keyed by top-nav kind \u2014 see the\n // taxonomy's `topNavFor`.\n pageTabs: {},\n };\n _syncActiveTab(leaf);\n return leaf;\n}\n\nexport function makeSplit({ dir = 'h', children = [], sizes = null } = {}) {\n return {\n id: _newId('split'),\n kind: 'split',\n parentId: null,\n dir,\n children: children.slice(),\n sizes: sizes ? sizes.slice() : children.map(() => 1),\n };\n}\n\n/**\n * TileTree \u2014 holds the node table and root, exposes mutation operations.\n * Every mutation operates by node id; the tree never hands out raw nodes\n * to callers (defensive copies via .get()).\n */\nexport class TileTree {\n constructor() {\n this.nodes = new Map();\n this.rootId = null;\n this.focusedLeafId = null;\n // Stack of recently-focused leaves. New content loads target the\n // top of this stack \u2014 i.e. the last leaf the user focused.\n this.focusStack = [];\n }\n\n // \u2500\u2500 Read 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\n get(id) { return this.nodes.get(id) || null; }\n root() { return this.rootId ? this.get(this.rootId) : null; }\n focused() { return this.focusedLeafId ? this.get(this.focusedLeafId) : null; }\n\n leaves() {\n const out = [];\n const walk = (id) => {\n const n = this.get(id);\n if (!n) return;\n if (n.kind === 'leaf') out.push(n);\n else n.children.forEach(walk);\n };\n if (this.rootId) walk(this.rootId);\n return out;\n }\n\n // \u2500\u2500 Construction \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 setRoot(node) {\n this._register(node);\n this.rootId = node.id;\n if (node.kind === 'leaf') {\n this.focus(node.id);\n }\n return node.id;\n }\n\n _register(node) {\n this.nodes.set(node.id, node);\n if (node.kind === 'split') {\n for (const cid of node.children) {\n const c = this.get(cid);\n if (c) c.parentId = node.id;\n }\n }\n }\n\n // \u2500\u2500 Focus \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 focus(leafId) {\n if (!this.nodes.has(leafId)) return;\n const node = this.get(leafId);\n if (node.kind !== 'leaf') return;\n this.focusedLeafId = leafId;\n this.focusStack = this.focusStack.filter((id) => id !== leafId);\n this.focusStack.unshift(leafId);\n }\n\n /** The leaf where \"open content\" should land \u2014 top of focus stack,\n * skipping panel:* tiles which are chrome, not content. */\n primaryLeafId() {\n for (const id of this.focusStack) {\n const n = this.nodes.get(id);\n if (n && n.kind === 'leaf' && !_isPanel(n)) return id;\n }\n for (const leaf of this.leaves()) {\n if (!_isPanel(leaf)) return leaf.id;\n }\n return null;\n }\n\n // \u2500\u2500 Mutations \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 /** Replace the leaf's tab list with a single tab carrying `content`.\n * Closes any existing tabs in that leaf \u2014 this is the \"open in\n * place\" / \"replace tile\" path that the WM has always used. */\n setLeafContent(leafId, content, title = '') {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n if (content) {\n n.tabs = [{\n kind: content.kind,\n props: content.props || {},\n title: title || content.kind || '',\n }];\n n.activeTabIdx = 0;\n } else {\n n.tabs = [];\n n.activeTabIdx = 0;\n }\n _syncActiveTab(n);\n if (title) n.title = title;\n return true;\n }\n\n /** Page-aware swap: archive the leaf's current tab list under its\n * current top-nav key, then restore the target top-nav's archived\n * list (or initialize one with the requested kind/props). Used\n * by top-nav / palette navigation so each top-nav page keeps its\n * own tab set across page switches.\n *\n * Parameters:\n * leafId The primary leaf.\n * target { kind, props, title } \u2014 what the user clicked.\n * currentTopNav Top-nav of the leaf's current page (may be null).\n * targetTopNav Top-nav of the target kind (must be set).\n *\n * When `currentTopNav === targetTopNav` (already on the right\n * page), the swap is skipped and we either focus an existing\n * matching tab or replace the active one. This is what makes\n * clicking the top-nav button for the page you're already on\n * feel like \"Home for this page\" without resetting tabs. */\n swapToPage(leafId, target, currentTopNav, targetTopNav, opts = {}) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n if (!target || !target.kind) return false;\n n.pageTabs = n.pageTabs || {};\n const recordHistory = opts.recordHistory !== false;\n\n // Helper: mirror what replaceActiveTabContent does \u2014 push the\n // outgoing tab onto the (incoming) tab's history stack so\n // Backspace can walk back. `extra` is an optional annotation\n // (e.g. `{ topNav: currentTopNav }`) for cross-page entries so\n // navigateBack can re-route via swapToPage when popping.\n const _pushPrev = (history, prev, next, extra) => {\n if (!recordHistory || !prev || !prev.kind) return history;\n const last = history[history.length - 1];\n const sameAsPrev = last\n && last.kind === prev.kind\n && String(last.props?.id ?? '') === String(prev.props?.id ?? '');\n const sameAsTarget = next\n && prev.kind === next.kind\n && String(prev.props?.id ?? '') === String(next.props?.id ?? '');\n if (!sameAsPrev && !sameAsTarget) {\n history.push({\n kind: prev.kind,\n props: prev.props || {},\n title: prev.title || '',\n ...(extra || {}),\n });\n }\n return history;\n };\n\n const same = currentTopNav && currentTopNav === targetTopNav;\n if (same) {\n const tabs = n.tabs || [];\n const matchIdx = tabs.findIndex((t) =>\n t.kind === target.kind\n && String(t.props?.id ?? '') === String(target.props?.id ?? ''));\n if (matchIdx >= 0) {\n n.activeTabIdx = matchIdx;\n } else if (tabs.length === 0) {\n n.tabs = [{\n kind: target.kind,\n props: target.props || {},\n title: target.title || target.kind,\n history: [],\n }];\n n.activeTabIdx = 0;\n } else {\n const prev = tabs[n.activeTabIdx];\n const history = Array.isArray(prev?.history) ? prev.history : [];\n _pushPrev(history, prev, target, null);\n tabs[n.activeTabIdx] = {\n kind: target.kind,\n props: target.props || {},\n title: target.title || target.kind,\n history,\n };\n }\n _syncActiveTab(n);\n return true;\n }\n\n // Snapshot the source tab BEFORE archiving \u2014 used to seed the\n // target tab's history with a cross-page back entry.\n const srcTabs = Array.isArray(n.tabs) ? n.tabs : [];\n const srcActive = srcTabs[n.activeTabIdx];\n const sourceEntry = (currentTopNav && srcActive?.kind) ? {\n kind: srcActive.kind,\n props: srcActive.props || {},\n title: srcActive.title || '',\n topNav: currentTopNav,\n } : null;\n\n // Archive the current page \u2014 preserve `history` so the user\n // doesn't lose in-tile back depth when they walk away and\n // return to this page.\n if (currentTopNav && srcTabs.length > 0) {\n // Don't archive transient tabs (add-row forms etc.) \u2014 they\n // shouldn't reappear when the user returns to this page.\n const archived = srcTabs\n .filter((t) => !t.transient)\n .map((t) => ({\n kind: t.kind, props: { ...(t.props || {}) }, title: t.title,\n history: Array.isArray(t.history) ? t.history.slice() : [],\n }));\n n.pageTabs[currentTopNav] = {\n tabs: archived,\n activeTabIdx: Math.max(0, Math.min(archived.length - 1, n.activeTabIdx || 0)),\n };\n }\n\n // Restore the target page, or initialize fresh.\n const saved = n.pageTabs[targetTopNav];\n if (saved && Array.isArray(saved.tabs) && saved.tabs.length > 0) {\n n.tabs = saved.tabs.map((t) => ({\n kind: t.kind, props: { ...(t.props || {}) }, title: t.title,\n history: Array.isArray(t.history) ? t.history.slice() : [],\n }));\n n.activeTabIdx = Math.max(0,\n Math.min(n.tabs.length - 1, saved.activeTabIdx || 0));\n // The caller asked for a specific entity (props.id) OR for a\n // page kind that ISN'T the nav category itself (e.g. `settings`\n // lives under the `home` topNav). In both cases we must surface\n // the requested target rather than silently showing whatever\n // the restored page happened to hold. A bare top-nav click\n // (target.kind === targetTopNav, no id) falls through and just\n // restores the saved tabs as-is.\n const wantsTarget = target.props?.id != null\n || target.kind !== targetTopNav;\n if (wantsTarget) {\n const wantId = target.props?.id != null;\n const matchIdx = n.tabs.findIndex((t) =>\n t.kind === target.kind\n && (!wantId\n || String(t.props?.id ?? '') === String(target.props.id)));\n if (matchIdx >= 0) {\n n.activeTabIdx = matchIdx;\n } else {\n const history = [];\n if (sourceEntry) _pushPrev(history, sourceEntry, target, { topNav: currentTopNav });\n n.tabs.push({\n kind: target.kind,\n props: target.props || {},\n title: target.title || target.kind,\n history,\n });\n n.activeTabIdx = n.tabs.length - 1;\n }\n }\n } else {\n const history = [];\n if (sourceEntry) _pushPrev(history, sourceEntry, target, { topNav: currentTopNav });\n n.tabs = [{\n kind: target.kind,\n props: target.props || {},\n title: target.title || target.kind,\n history,\n }];\n n.activeTabIdx = 0;\n }\n _syncActiveTab(n);\n return true;\n }\n\n /** Update any tab whose (kind, props.id) matches the given pair to\n * reflect a new label. Called from the WM in response to the\n * embedder's entity-renamed bus event, so tab titles stay live as\n * the underlying entity is renamed. */\n renameMatchingTabs(kind, id, newLabel) {\n if (!kind || !id || !newLabel) return false;\n let touched = false;\n const visit = (tabs) => {\n if (!Array.isArray(tabs)) return;\n for (const t of tabs) {\n if (t.kind === kind && String(t.props?.id ?? '') === String(id)) {\n t.title = String(newLabel);\n if (t.props) t.props.label = String(newLabel);\n touched = true;\n }\n }\n };\n for (const n of this.nodes.values()) {\n if (n.kind !== 'leaf') continue;\n visit(n.tabs);\n if (n.pageTabs) {\n for (const arc of Object.values(n.pageTabs)) visit(arc?.tabs);\n }\n _syncActiveTab(n);\n }\n return touched;\n }\n\n /** Replace the active tab's content in place, preserving every\n * other tab in the leaf. This is the \"in-tile navigation\" path:\n * clicking a row on a landing page, walking back via Backspace,\n * or clicking a breadcrumb segment should swap THIS tab's\n * content without disturbing the user's other open tabs.\n *\n * By default the previous content is pushed onto the active\n * tab's `history` stack so the user can Backspace through their\n * own click-path (per-tab \"browser back\"). Callers that are\n * themselves popping the history pass `{ recordHistory: false }`\n * so we don't double-record. Same applies to programmatic loads\n * that aren't user navigations.\n *\n * Falls back to `setLeafContent` for empty leaves so the WM\n * never has to special-case the initial load. Returns true on\n * success. */\n replaceActiveTabContent(leafId, content, title = '', opts = {}) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n const tabs = Array.isArray(n.tabs) ? n.tabs : [];\n if (tabs.length === 0) {\n return this.setLeafContent(leafId, content, title);\n }\n if (!content) return false;\n const idx = Math.max(0, Math.min(tabs.length - 1, n.activeTabIdx || 0));\n const prev = tabs[idx];\n const recordHistory = opts.recordHistory !== false;\n const history = Array.isArray(prev?.history) ? prev.history : [];\n if (recordHistory && prev && prev.kind) {\n // Avoid pushing duplicates when the user re-clicks the\n // same row \u2014 same kind + same primary id \u2192 no-op.\n const last = history[history.length - 1];\n const sameAsPrev = last\n && last.kind === prev.kind\n && String(last.props?.id ?? '') === String(prev.props?.id ?? '');\n const sameAsTarget = prev.kind === content.kind\n && String(prev.props?.id ?? '') === String(content.props?.id ?? '');\n if (!sameAsPrev && !sameAsTarget) {\n history.push({\n kind: prev.kind,\n props: prev.props || {},\n title: prev.title || '',\n });\n }\n }\n tabs[idx] = {\n kind: content.kind,\n props: content.props || {},\n title: title || content.kind || '',\n history,\n };\n _syncActiveTab(n);\n return true;\n }\n\n /** Merge `patch` into the active tab's `props`. Used when an\n * in-tile editor surfaces metadata that should be captured in\n * history snapshots (e.g. which sub-tab is open) so Backspace\n * restores the user's view, not the default landing. Does not\n * re-render or push history \u2014 pure metadata write. */\n updateActiveTabProps(leafId, patch) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n const tabs = Array.isArray(n.tabs) ? n.tabs : [];\n if (tabs.length === 0) return false;\n const idx = Math.max(0, Math.min(tabs.length - 1, n.activeTabIdx || 0));\n const cur = tabs[idx];\n if (!cur) return false;\n cur.props = { ...(cur.props || {}), ...(patch || {}) };\n return true;\n }\n\n /** Pop one entry off the active tab's history stack and return it,\n * or null when empty. Doesn't mutate the tab beyond the stack \u2014\n * the caller is expected to apply the returned content via\n * `replaceActiveTabContent(..., { recordHistory: false })`. */\n popActiveTabHistory(leafId) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return null;\n const tabs = Array.isArray(n.tabs) ? n.tabs : [];\n if (tabs.length === 0) return null;\n const idx = Math.max(0, Math.min(tabs.length - 1, n.activeTabIdx || 0));\n const cur = tabs[idx];\n const history = Array.isArray(cur?.history) ? cur.history : [];\n if (history.length === 0) return null;\n return history.pop();\n }\n\n /** Inspect the active tab's history stack without popping. Used\n * by the keymap to show / hide the back affordance and for\n * diagnostics. */\n activeTabHistory(leafId) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return [];\n const tabs = Array.isArray(n.tabs) ? n.tabs : [];\n if (tabs.length === 0) return [];\n const idx = Math.max(0, Math.min(tabs.length - 1, n.activeTabIdx || 0));\n const cur = tabs[idx];\n return Array.isArray(cur?.history) ? cur.history : [];\n }\n\n /** Append a new tab to a leaf and focus it. Returns the new tab's\n * index, or -1 on failure. Panel tiles never grow tabs (they're\n * chrome). */\n appendLeafTab(leafId, content, title = '', opts = {}) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return -1;\n if (_isPanel(n)) return -1;\n if (!content) return -1;\n const tab = {\n kind: content.kind,\n props: content.props || {},\n title: title || content.kind || '',\n // Transient tabs (e.g. an add-row form) are not persisted /\n // restored \u2014 see serialize() + the pageTabs archive.\n ...(opts.transient ? { transient: true } : {}),\n };\n n.tabs = Array.isArray(n.tabs) ? n.tabs : [];\n n.tabs.push(tab);\n n.activeTabIdx = n.tabs.length - 1;\n _syncActiveTab(n);\n return n.activeTabIdx;\n }\n\n /** Switch the active tab on a leaf. No-op if `idx` is out of range. */\n setActiveLeafTab(leafId, idx) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n if (!Array.isArray(n.tabs) || idx < 0 || idx >= n.tabs.length) return false;\n n.activeTabIdx = idx;\n _syncActiveTab(n);\n return true;\n }\n\n /** Remove a tab. If the active tab was removed, the previous one\n * becomes active. If the last tab was removed, the leaf's content\n * drops to null (and the WM should treat that as an empty tile \u2014\n * same as before tabs existed). Returns the (possibly clamped)\n * active index after the operation, or -1 on failure. */\n removeLeafTab(leafId, idx) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return -1;\n if (!Array.isArray(n.tabs) || idx < 0 || idx >= n.tabs.length) return -1;\n n.tabs.splice(idx, 1);\n if (n.tabs.length === 0) {\n n.activeTabIdx = 0;\n _syncActiveTab(n);\n return 0;\n }\n if (idx < n.activeTabIdx) n.activeTabIdx -= 1;\n else if (idx === n.activeTabIdx) n.activeTabIdx = Math.max(0, idx - 1);\n _syncActiveTab(n);\n return n.activeTabIdx;\n }\n\n /** Bulk-close every tab in a leaf except the one at `keepIdx`.\n * Used by the tab right-click \"Close others\" action. */\n closeOtherTabs(leafId, keepIdx) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n const tabs = n.tabs || [];\n if (keepIdx < 0 || keepIdx >= tabs.length) return false;\n n.tabs = [tabs[keepIdx]];\n n.activeTabIdx = 0;\n _syncActiveTab(n);\n return true;\n }\n\n /** Close every tab after `idx`. */\n closeTabsAfter(leafId, idx) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n const tabs = n.tabs || [];\n if (idx < 0 || idx >= tabs.length - 1) return false;\n n.tabs = tabs.slice(0, idx + 1);\n if (n.activeTabIdx > idx) n.activeTabIdx = idx;\n _syncActiveTab(n);\n return true;\n }\n\n /** Close every tab before `idx`. */\n closeTabsBefore(leafId, idx) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n const tabs = n.tabs || [];\n if (idx <= 0 || idx >= tabs.length) return false;\n n.tabs = tabs.slice(idx);\n n.activeTabIdx = Math.max(0, n.activeTabIdx - idx);\n _syncActiveTab(n);\n return true;\n }\n\n /** Reorder a tab from `from` to `to` within the same leaf. */\n moveLeafTab(leafId, from, to) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n const tabs = n.tabs || [];\n if (from < 0 || from >= tabs.length || to < 0 || to >= tabs.length) return false;\n const moved = tabs.splice(from, 1)[0];\n tabs.splice(to, 0, moved);\n // Keep the moved tab focused if it was the active one.\n if (n.activeTabIdx === from) n.activeTabIdx = to;\n else if (from < n.activeTabIdx && to >= n.activeTabIdx) n.activeTabIdx -= 1;\n else if (from > n.activeTabIdx && to <= n.activeTabIdx) n.activeTabIdx += 1;\n _syncActiveTab(n);\n return true;\n }\n\n /**\n * C33. MOVE ONE TAB FROM ONE LEAF TO ANOTHER, AS ONE MUTATION.\n *\n * Every other tab operation on this class takes ONE `leafId`, and that was\n * a complete description of the model until a tab could be dragged into a\n * different tile: `appendLeafTab`, `removeLeafTab`, `moveLeafTab` and the\n * three bulk closes all begin and end inside a single leaf. The renderer's\n * refusal to wire `onDropFromOtherPane` named this absence as one of its\n * two reasons (`tile_renderer.js`, `_topTabCallbacks`); this is that half.\n *\n * ONE FUNCTION RATHER THAN A COMPOSE, and that is why it lives here rather\n * than in the WM. `removeLeafTab` then `appendLeafTab` is two mutations\n * with a moment between them in which the tab exists nowhere \u2014 and the\n * second can FAIL (a panel destination refuses tabs), which leaves the tree\n * short one tab and nothing on screen saying where it went. Every guard is\n * therefore taken before the first splice.\n *\n * TWO NODES ARE RE-SYNCED, WHICH IS WHAT MAKES THIS DIFFERENT. `tabs` is\n * the source of truth and `content`/`title` mirror `tabs[active]`\n * (`_syncActiveTab`). Every existing mutation touches one leaf, so one\n * re-sync is right; this one touches two, and skipping the SOURCE's leaves\n * a pane whose chrome still names \u2014 and whose body still mounts \u2014 a tab it\n * no longer holds. That is the mistake this operation invites, and it is\n * what `web/js/shell/tab_drop.test.mjs` asserts against in the consumer.\n *\n * A same-leaf call is a REORDER and delegates, so there is one\n * implementation of \"a tab changed position within its strip\" rather than\n * two that will disagree about the active-index clamp.\n *\n * @param {string} fromLeafId\n * @param {number} fromIdx\n * @param {string} toLeafId\n * @param {number} [toIdx=-1] where to insert; -1 (or past the end) appends\n * @returns {{ok: boolean, toIdx: number, emptied: boolean}|null} null when\n * the move was refused. `emptied` tells the caller the source pane now\n * holds nothing, which is its cue to re-seed rather than leave a blank\n * tile \u2014 the never-empty-tile invariant is the WM's to keep, not this\n * class's.\n */\n moveTabToLeaf(fromLeafId, fromIdx, toLeafId, toIdx = -1) {\n const from = this.get(fromLeafId);\n const to = this.get(toLeafId);\n if (!from || from.kind !== 'leaf') return null;\n if (!to || to.kind !== 'leaf') return null;\n const tabs = Array.isArray(from.tabs) ? from.tabs : [];\n if (!Number.isInteger(fromIdx) || fromIdx < 0 || fromIdx >= tabs.length) return null;\n // Panel tiles are chrome, not content, and never grow tabs \u2014 the same\n // refusal `appendLeafTab` makes, taken here BEFORE anything is spliced\n // so a refused destination cannot cost the source its tab.\n if (_isPanel(to)) return null;\n if (fromLeafId === toLeafId) {\n const dest = (!Number.isInteger(toIdx) || toIdx < 0 || toIdx >= tabs.length)\n ? tabs.length - 1 : toIdx;\n if (!this.moveLeafTab(fromLeafId, fromIdx, dest)) return null;\n return { ok: true, toIdx: dest, emptied: false };\n }\n\n const [moved] = from.tabs.splice(fromIdx, 1);\n // THE SOURCE'S ACTIVE INDEX, WITH `removeLeafTab`'S OWN THREE BRANCHES.\n // Spelled out rather than delegated because `removeLeafTab` re-syncs\n // and returns, and the splice above has already happened; the\n // arithmetic is the contract, so it is written where it can be compared\n // against the original line for line.\n if (from.tabs.length === 0) from.activeTabIdx = 0;\n else if (fromIdx < from.activeTabIdx) from.activeTabIdx -= 1;\n else if (fromIdx === from.activeTabIdx) from.activeTabIdx = Math.max(0, fromIdx - 1);\n\n to.tabs = Array.isArray(to.tabs) ? to.tabs : [];\n const at = (!Number.isInteger(toIdx) || toIdx < 0 || toIdx > to.tabs.length)\n ? to.tabs.length : toIdx;\n to.tabs.splice(at, 0, moved);\n // THE ARRIVING TAB IS THE ONE YOU WANTED TO SEE. A drop is a deliberate\n // act naming one tab and one destination; leaving the destination on\n // whatever it was already showing would make the gesture look like it\n // did nothing, which is the failure mode of every silent move.\n to.activeTabIdx = at;\n\n _syncActiveTab(from);\n _syncActiveTab(to);\n return { ok: true, toIdx: at, emptied: from.tabs.length === 0 };\n }\n\n /**\n * Split a leaf in the given direction; existing content stays in the\n * original leaf, a new empty leaf is added next to it. Returns the\n * new leaf id, or null on failure.\n */\n split(leafId, dir /* 'h' | 'v' */) {\n const leaf = this.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return null;\n\n const newLeaf = makeLeaf();\n // Case 1: root split. Wrap root in a new Split.\n if (!leaf.parentId) {\n const wrap = makeSplit({ dir, children: [leaf.id, newLeaf.id] });\n this._register(newLeaf);\n this._register(wrap);\n this.rootId = wrap.id;\n leaf.parentId = wrap.id;\n newLeaf.parentId = wrap.id;\n this.focus(newLeaf.id);\n return newLeaf.id;\n }\n\n const parent = this.get(leaf.parentId);\n const idx = parent.children.indexOf(leaf.id);\n if (idx < 0) return null;\n\n // Case 2: parent already matches the split direction \u2014 insert\n // the new leaf as a sibling.\n if (parent.dir === dir) {\n parent.children.splice(idx + 1, 0, newLeaf.id);\n const avg = parent.sizes.reduce((a, b) => a + b, 0) / parent.children.length;\n parent.sizes.splice(idx + 1, 0, avg);\n newLeaf.parentId = parent.id;\n this._register(newLeaf);\n this.focus(newLeaf.id);\n return newLeaf.id;\n }\n\n // Case 3: cross-direction \u2014 wrap the original leaf in a new\n // Split that contains [leaf, newLeaf]. Replace leaf in parent.\n const wrap = makeSplit({ dir, children: [leaf.id, newLeaf.id] });\n wrap.parentId = parent.id;\n parent.children[idx] = wrap.id;\n leaf.parentId = wrap.id;\n newLeaf.parentId = wrap.id;\n this._register(newLeaf);\n this._register(wrap);\n this.focus(newLeaf.id);\n return newLeaf.id;\n }\n\n /**\n * Close a leaf, collapsing any single-child splits that result. If\n * the last leaf is closed, leaves an empty tree (no root) \u2014 caller\n * is expected to repopulate.\n */\n close(leafId) {\n const leaf = this.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return false;\n\n // Root leaf \u2014 clear the tree.\n if (!leaf.parentId) {\n this.nodes.clear();\n this.rootId = null;\n this.focusedLeafId = null;\n this.focusStack = [];\n return true;\n }\n\n const parent = this.get(leaf.parentId);\n const idx = parent.children.indexOf(leaf.id);\n parent.children.splice(idx, 1);\n parent.sizes.splice(idx, 1);\n this.nodes.delete(leaf.id);\n this.focusStack = this.focusStack.filter((id) => id !== leaf.id);\n\n // Parent collapse \u2014 if a Split has only one child left, replace\n // it with that child in *its* parent (or as root).\n this._collapse(parent);\n\n if (this.focusedLeafId === leaf.id) {\n const remaining = this.leaves();\n this.focusedLeafId = remaining.length ? remaining[0].id : null;\n if (this.focusedLeafId) this.focus(this.focusedLeafId);\n }\n return true;\n }\n\n _collapse(node) {\n if (!node || node.kind !== 'split') return;\n if (node.children.length > 1) return;\n if (node.children.length === 0) {\n // Empty split \u2014 remove from its parent (or clear root).\n if (!node.parentId) {\n this.nodes.delete(node.id);\n this.rootId = null;\n return;\n }\n const gp = this.get(node.parentId);\n const idx = gp.children.indexOf(node.id);\n gp.children.splice(idx, 1);\n gp.sizes.splice(idx, 1);\n this.nodes.delete(node.id);\n this._collapse(gp);\n return;\n }\n // Exactly one child \u2014 promote it.\n const only = this.get(node.children[0]);\n if (!node.parentId) {\n this.rootId = only.id;\n only.parentId = null;\n } else {\n const gp = this.get(node.parentId);\n const idx = gp.children.indexOf(node.id);\n gp.children[idx] = only.id;\n only.parentId = gp.id;\n }\n this.nodes.delete(node.id);\n }\n\n // \u2500\u2500 Directional focus \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 * Move focus from current leaf in the given direction. Walks up the\n * tree looking for an ancestor split whose direction matches the\n * move (h for left/right, v for up/down) and which has a sibling on\n * the requested side; then descends along the matching edge.\n */\n focusDir(dir /* 'left' | 'right' | 'up' | 'down' */) {\n const cur = this.focused();\n if (!cur) return false;\n const wantDir = (dir === 'left' || dir === 'right') ? 'h' : 'v';\n const forward = (dir === 'right' || dir === 'down');\n\n let child = cur;\n let parent = child.parentId ? this.get(child.parentId) : null;\n while (parent) {\n if (parent.dir === wantDir) {\n const idx = parent.children.indexOf(child.id);\n const sibIdx = forward ? idx + 1 : idx - 1;\n if (sibIdx >= 0 && sibIdx < parent.children.length) {\n const target = this._descendEdge(parent.children[sibIdx],\n wantDir, !forward);\n if (target) {\n this.focus(target);\n return true;\n }\n }\n }\n child = parent;\n parent = child.parentId ? this.get(child.parentId) : null;\n }\n return false;\n }\n\n /**\n * Walk down to a leaf. For the matching direction we take the\n * `edge` end (first/last). For the orthogonal direction we just\n * take the first child.\n */\n _descendEdge(nodeId, matchDir, takeFirst) {\n let n = this.get(nodeId);\n while (n && n.kind !== 'leaf') {\n const idx = n.dir === matchDir\n ? (takeFirst ? 0 : n.children.length - 1)\n : 0;\n n = this.get(n.children[idx]);\n }\n return n ? n.id : null;\n }\n\n // \u2500\u2500 Move tile \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 * Move the focused leaf one position in the given direction. Simple\n * version: swap with the immediate sibling in the matching parent;\n * if no such sibling, no-op (deep moves come later if needed).\n */\n moveDir(dir) {\n const cur = this.focused();\n if (!cur || !cur.parentId) return false;\n const wantDir = (dir === 'left' || dir === 'right') ? 'h' : 'v';\n const forward = (dir === 'right' || dir === 'down');\n\n let child = cur;\n let parent = this.get(child.parentId);\n while (parent && parent.dir !== wantDir) {\n child = parent;\n parent = child.parentId ? this.get(child.parentId) : null;\n }\n if (!parent) return false;\n const idx = parent.children.indexOf(child.id);\n const swap = forward ? idx + 1 : idx - 1;\n if (swap < 0 || swap >= parent.children.length) return false;\n [parent.children[idx], parent.children[swap]] =\n [parent.children[swap], parent.children[idx]];\n [parent.sizes[idx], parent.sizes[swap]] =\n [parent.sizes[swap], parent.sizes[idx]];\n return true;\n }\n\n // \u2500\u2500 Resize \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 setSplitSizes(splitId, sizes) {\n const n = this.get(splitId);\n if (!n || n.kind !== 'split') return false;\n if (sizes.length !== n.children.length) return false;\n n.sizes = sizes.slice();\n return true;\n }\n\n // \u2500\u2500 Serialization (for desktops persistence) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n serialize() {\n if (!this.rootId) return null;\n const dump = (id) => {\n const n = this.get(id);\n if (n.kind === 'leaf') {\n // Transient tabs (add-row forms etc.) are not persisted.\n const liveTabs = (Array.isArray(n.tabs) ? n.tabs : [])\n .filter((t) => !t.transient)\n .map((t) => ({ kind: t.kind, props: t.props, title: t.title }));\n return {\n kind: 'leaf',\n title: n.title,\n content: n.content,\n tabs: liveTabs,\n activeTabIdx: Math.max(0, Math.min(liveTabs.length - 1, n.activeTabIdx || 0)),\n // Page-archive: per-top-nav-kind saved tab lists so\n // switching to a previously-visited page restores\n // its tabs across reloads.\n pageTabs: n.pageTabs\n ? Object.fromEntries(\n Object.entries(n.pageTabs).map(([k, v]) => {\n const pt = (v.tabs || [])\n .filter((t) => !t.transient)\n .map((t) => ({ kind: t.kind, props: t.props, title: t.title }));\n return [k, {\n tabs: pt,\n activeTabIdx: Math.max(0, Math.min(pt.length - 1, v.activeTabIdx || 0)),\n }];\n }))\n : {},\n };\n }\n return {\n kind: 'split', dir: n.dir, sizes: n.sizes.slice(),\n children: n.children.map(dump),\n };\n };\n return {\n root: dump(this.rootId),\n focusedTitle: this.focused()?.title || null,\n };\n }\n\n static deserialize(blob) {\n const t = new TileTree();\n if (!blob || !blob.root) return t;\n const build = (raw) => {\n if (raw.kind === 'leaf') {\n // Forward-compat with the pre-tabs layout: an old\n // serialized leaf carries `content` but no `tabs`.\n // makeLeaf will derive a single-tab list from `content`.\n const leaf = makeLeaf({ content: raw.content, title: raw.title });\n if (Array.isArray(raw.tabs) && raw.tabs.length > 0) {\n leaf.tabs = raw.tabs.map((tab) => ({\n kind: tab.kind,\n props: tab.props || {},\n title: tab.title || tab.kind || '',\n }));\n leaf.activeTabIdx = Math.max(0,\n Math.min(leaf.tabs.length - 1, raw.activeTabIdx || 0));\n _syncActiveTab(leaf);\n }\n if (raw.pageTabs && typeof raw.pageTabs === 'object') {\n leaf.pageTabs = {};\n for (const [k, v] of Object.entries(raw.pageTabs)) {\n if (!v || !Array.isArray(v.tabs)) continue;\n leaf.pageTabs[k] = {\n tabs: v.tabs.map((t) => ({\n kind: t.kind,\n props: t.props || {},\n title: t.title || t.kind || '',\n })),\n activeTabIdx: v.activeTabIdx || 0,\n };\n }\n }\n return leaf;\n }\n const children = raw.children.map(build);\n const split = makeSplit({\n dir: raw.dir, children: children.map((c) => c.id),\n sizes: raw.sizes,\n });\n children.forEach((c) => t._register(c));\n return split;\n };\n const root = build(blob.root);\n t.setRoot(root);\n return t;\n }\n}\n", "/**\n * desktops.js \u2014 independent tile trees per virtual desktop, persisted\n * through the host's `state` capability under the logical key `desktops`.\n * Where that key actually lands is the embedder's business (see the host's\n * `resolvePath`); this module only names it.\n *\n * Each desktop = { id, label, tree (TileTree), windows ([]), panels }.\n * Switching desktops swaps the active tree under the renderer.\n */\n\nimport { TileTree, makeLeaf } from './tile_tree.js';\n\n/** What a FRESH desktop's panel tiles start as, when the embedder says nothing.\n *\n * C14. It is a DEFAULT, not a constant. An embedder whose navigator and\n * inspector are its own chrome OUTSIDE the WM root \u2014 an icon rail, say \u2014 has\n * no `panel:left` factory to mount, and every desktop it creates would open\n * with two \"no factory registered yet\" placeholders wedged either side of its\n * content. It cannot fix that after the fact: `wm.load()` canonicalizes the\n * panels in before it returns, and the renderer caches tile DOM per\n * (kind, props). The only place the answer can be given is here, before the\n * first desktop exists \u2014 which is what `panelDefaults` is for. */\nconst DEFAULT_PANEL_STATE = {\n left: true,\n right: true,\n bottom: true,\n};\n\n/**\n * `seed()` yields the leaf a fresh (or emptied) desktop starts with. It comes\n * from the taxonomy's root kind \u2014 see WindowManager, which supplies it. The\n * WM used to hardcode `{ kind: 'home', title: 'Home' }` here, which meant an\n * embedder with a different ontology still got an EcoAgent Home tile it had\n * never registered, and got the content_registry placeholder instead. There is\n * deliberately NO default: a silent 'home' fallback is the exact bug this\n * parameter exists to remove.\n */\nfunction _makeDesktop(label, seed, panelDefaults = DEFAULT_PANEL_STATE) {\n const tree = new TileTree();\n tree.setRoot(makeLeaf(seed()));\n return {\n id: `desk-${Math.random().toString(36).slice(2, 8)}`,\n label,\n tree,\n windows: [],\n // Boot default: whatever the embedder asked for, left + right + bottom\n // when it asked for nothing. Names are assigned by wm._canonicalize via\n // PANEL_TITLES.\n panels: { ...panelDefaults },\n };\n}\n\nexport class DesktopManager {\n /**\n * @param {object} opts\n * @param {function} opts.seed builds the root leaf. Required.\n * @param {object} [opts.panelDefaults] C14. Which panel tiles a fresh\n * desktop opens with, merged over `DEFAULT_PANEL_STATE`. Omitted, every\n * desktop opens with all three \u2014 today's behaviour, unchanged.\n */\n constructor({ seed, panelDefaults = null } = {}) {\n if (typeof seed !== 'function') {\n throw new Error('DesktopManager: a `seed` function is required (the taxonomy root leaf)');\n }\n this.seed = seed;\n this.panelDefaults = { ...DEFAULT_PANEL_STATE, ...(panelDefaults || {}) };\n this.desktops = [_makeDesktop('1', seed, this.panelDefaults)];\n this.activeIdx = 0;\n }\n\n active() { return this.desktops[this.activeIdx]; }\n\n switchTo(idx) {\n if (idx < 0 || idx >= this.desktops.length) return false;\n if (idx === this.activeIdx) return false;\n this.activeIdx = idx;\n return true;\n }\n\n ensureCount(n) {\n while (this.desktops.length < n) {\n this.desktops.push(_makeDesktop(\n String(this.desktops.length + 1), this.seed, this.panelDefaults));\n }\n }\n\n addDesktop(label = null) {\n const d = _makeDesktop(\n label || String(this.desktops.length + 1), this.seed, this.panelDefaults);\n this.desktops.push(d);\n return d;\n }\n\n serialize() {\n return {\n activeIdx: this.activeIdx,\n desktops: this.desktops.map((d) => ({\n id: d.id, label: d.label,\n tree: d.tree.serialize(),\n panels: { ...d.panels },\n // windows serialized at WM level since we don't own\n // managed-window state in this module.\n })),\n };\n }\n\n static deserialize(blob, { seed, panelDefaults = null } = {}) {\n const m = new DesktopManager({ seed, panelDefaults });\n if (!blob || !Array.isArray(blob.desktops) || blob.desktops.length === 0) return m;\n m.desktops = blob.desktops.map((raw) => ({\n id: raw.id || `desk-${Math.random().toString(36).slice(2,8)}`,\n label: raw.label || '?',\n tree: raw.tree ? TileTree.deserialize(raw.tree) : new TileTree(),\n windows: [],\n // A RESTORED desktop's own answer wins over the default: the user\n // closed that panel, and re-opening it on every reload is the bug\n // this merge order avoids. The default only fills a key the stored\n // blob predates.\n panels: { ...m.panelDefaults, ...(raw.panels || {}) },\n }));\n // Empty tree \u2192 seed with the taxonomy root so the desktop is usable.\n for (const d of m.desktops) {\n if (!d.tree.rootId) d.tree.setRoot(makeLeaf(seed()));\n }\n m.activeIdx = Math.min(Math.max(0, blob.activeIdx | 0), m.desktops.length - 1);\n return m;\n }\n}\n\n/**\n * Persistence helpers. They take a host `state` capability; a host without\n * one is legal and they no-op. We don't fail loudly \u2014 the WM stays functional\n * against a host that cannot persist (e.g. the standalone demo).\n */\n\nconst DESKTOPS_KEY = 'desktops';\n\nexport async function loadDesktops(state) {\n if (!state) return null;\n try {\n return await state.read(DESKTOPS_KEY);\n } catch (err) {\n console.warn('[desktops] load failed', err);\n return null;\n }\n}\n\nexport async function saveDesktops(state, blob) {\n if (!state) return false;\n try {\n await state.write(DESKTOPS_KEY, blob);\n return true;\n } catch (err) {\n console.warn('[desktops] save failed', err);\n return false;\n }\n}\n", "/**\n * entity_sources.js \u2014 the entity-source PORT.\n *\n * The command palette, the tile hamburger menu, and any other \"find me a\n * thing\" surface need a list of every searchable entity. Where those\n * entities come from \u2014 which endpoint returns them, what a row looks\n * like, what to call it \u2014 is the EMBEDDER's knowledge, not the shell's.\n *\n * So the source list is INJECTED. The embedder describes its sources and\n * hands them to `createEntityCatalog()` at boot. Nothing in this file\n * names an entity type or a bridge route.\n *\n * ## EntitySource\n *\n * navKind The content kind used to open a row via `wm.navigate`.\n * The taxonomy resolves its icon + label.\n *\n * list `(api) => Promise<rawList>` \u2014 per-source fetcher. Used by\n * the fan-out path, and by `loadOne` when a caller wants a\n * subset rather than the whole catalog.\n *\n * pluck? `(raw) => Array` \u2014 extract the actual list when the\n * endpoint wraps it. Defaults to \"raw is an array, else\n * raw.items if that's an array, else []\".\n *\n * shape `(row) => { id, label, hint? } | null` \u2014 normalise one raw\n * row to the record shape every search surface consumes.\n * Return `null` to skip the row.\n *\n * ## Aggregation\n *\n * An embedder whose backend can answer for every source in ONE call\n * passes `aggregate: (api) => Promise<{ [navKind]: rawList } | null>`.\n * The catalog prefers it and fans out only for the navKinds it failed to\n * answer for. Absent \u21D2 straight fan-out. Absent capability, absent key:\n * the shell never probes for a named endpoint of its own accord.\n *\n * Errors from any single source are logged and treated as \"empty\", so\n * one broken endpoint can't blank the entire palette.\n */\n\nexport function createEntityCatalog({ sources, aliases = {}, aggregate = null } = {}) {\n if (!Array.isArray(sources)) {\n throw new TypeError('createEntityCatalog: `sources` must be an array');\n }\n for (const s of sources) {\n if (!s?.navKind || typeof s.list !== 'function' || typeof s.shape !== 'function') {\n throw new TypeError(\n `createEntityCatalog: source '${s?.navKind ?? '?'}' needs navKind + list() + shape()`);\n }\n }\n if (aggregate != null && typeof aggregate !== 'function') {\n throw new TypeError('createEntityCatalog: `aggregate` must be a function when present');\n }\n\n const _sources = Object.freeze(sources.map((s) => Object.freeze({ ...s })));\n const _byKind = new Map(_sources.map((s) => [s.navKind, s]));\n const _aliases = Object.freeze({ ...aliases });\n\n const get = (navKind) => _byKind.get(navKind) || null;\n\n /** Unwrap one source's raw payload into a row array. */\n const _extract = (src, raw) => {\n const list = src.pluck\n ? src.pluck(raw)\n : (Array.isArray(raw) ? raw\n : Array.isArray(raw?.items) ? raw.items\n : []);\n return Array.isArray(list) ? list : [];\n };\n\n /** Shape a source's rows, dropping the ones `shape` rejects or\n * throws on. Shared by the palette AND the tile menu so their\n * semantics cannot drift apart \u2014 the menu used to re-implement\n * this loop by hand. */\n const shapeRows = (navKind, rows) => {\n const src = _byKind.get(navKind);\n if (!src || !Array.isArray(rows)) return [];\n const out = [];\n for (const row of rows) {\n let shaped;\n try { shaped = src.shape(row); }\n catch (err) {\n console.warn('[entity-catalog]', navKind, 'shape failed', err, row);\n continue;\n }\n if (!shaped || !shaped.id) continue;\n out.push(shaped);\n }\n return out;\n };\n\n /** Fetch ONE source's raw rows. Used by surfaces that only care\n * about a subset of the catalog. */\n const loadOne = async (navKind, api) => {\n const src = _byKind.get(navKind);\n if (!src || !api) return [];\n try { return _extract(src, await src.list(api)); }\n catch (err) {\n console.warn('[entity-catalog]', navKind, 'fetch failed', err);\n return [];\n }\n };\n\n /** Fetch every source's raw rows, grouped by navKind.\n *\n * The aggregator runs first when the embedder supplied one; the\n * fan-out only fires for sources it didn't answer for (it threw, it\n * isn't there, or it doesn't know a newly-added navKind yet).\n *\n * Shape: `{ [navKind]: rawRow[] }` \u2014 every navKind is always\n * present (empty on failure) so consumers can destructure safely. */\n const loadGrouped = async (api) => {\n const out = {};\n for (const s of _sources) out[s.navKind] = [];\n if (!api) return out;\n\n let bulk = null;\n if (aggregate) {\n try { bulk = await aggregate(api); }\n catch (err) {\n console.warn('[entity-catalog] aggregate failed, falling back to fan-out', err);\n }\n if (bulk && typeof bulk !== 'object') bulk = null;\n }\n\n const missing = [];\n for (const src of _sources) {\n const raw = bulk ? bulk[src.navKind] : undefined;\n if (raw == null) { missing.push(src); continue; }\n out[src.navKind] = _extract(src, raw);\n }\n if (missing.length === 0) return out;\n\n await Promise.all(missing.map(async (src) => {\n out[src.navKind] = await loadOne(src.navKind, api);\n }));\n return out;\n };\n\n /** Flat-and-shaped form for search surfaces. Builds on\n * `loadGrouped` so there is a single fetch path and the palette /\n * explorer can't drift apart in coverage. */\n const loadAll = async (api) => {\n const grouped = await loadGrouped(api);\n const out = [];\n for (const src of _sources) {\n for (const shaped of shapeRows(src.navKind, grouped[src.navKind] || [])) {\n out.push({ kind: src.navKind, ...shaped });\n }\n }\n return out;\n };\n\n /** Resolve a user-typed type prefix to a list of canonical navKinds.\n * Accepts the embedder's aliases first, then navKinds verbatim,\n * then the underscore spelling of a hyphenated kind. Returns an\n * array of navKinds on hit, `null` otherwise. */\n const resolveTypePrefix = (prefix) => {\n const p = String(prefix || '').toLowerCase().trim();\n if (!p) return null;\n if (_aliases[p]) return _aliases[p];\n for (const src of _sources) {\n if (src.navKind === p) return [src.navKind];\n if (src.navKind.replace(/-/g, '_') === p) return [src.navKind];\n }\n return null;\n };\n\n return Object.freeze({\n sources: _sources,\n get,\n loadOne,\n loadGrouped,\n loadAll,\n shapeRows,\n resolveTypePrefix,\n });\n}\n", "/**\n * keymap.js \u2014 Alt-based shortcuts for the tiling window manager.\n *\n * Alt+H split focused tile horizontally (new empty pane)\n * Alt+V split focused tile vertically (new empty pane)\n * Alt+Shift+H open focused tile's content in a horizontal split\n * Alt+Shift+V open focused tile's content in a vertical split\n * Alt+T open focused tile's content in a new tab\n * Alt+N open focused tile's content in a new window\n * Alt+W close focused tile\n * Alt+F promote focused tile <-> managed window (moves it)\n * Alt+ArrowLeft/Right/Up/Down move focus\n * Alt+Shift+Arrow\u2026 swap focused tile with neighbour\n * Alt+1..9 switch desktop\n * Alt+Shift+1..9 move focused tile to desktop N\n * Ctrl+ArrowLeft/Right iterate virtual desktops\n * Shift+ArrowLeft/Right iterate bottom-panel tabs (when present)\n * Ctrl+Tab / Ctrl+Shift+Tab next / previous main-panel tab (focused tile)\n * F1..F8 jump to the Nth top-nav page\n * Ctrl+K open command palette\n * ? open Help (lists every shortcut) \u2014 bound globally\n * by help_modal.js, not here\n * Esc close palette / managed-modal\n *\n * The full user-facing reference is the Help modal's \"Keyboard shortcuts\"\n * topic (ui/js/help/help_service.js) \u2014 keep that table in sync when\n * binding new chords here.\n *\n * The handler refuses to act when focus is in an editable field, unless\n * the chord uses the Alt modifier (which is never typed into a field).\n *\n * \u2500\u2500 It returns a disposer, and that is not decoration \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * The listener is on `document` and is bound to ONE shell's `wm` and\n * `palette`. An embedder that can build a second shell in the same page \u2014\n * Tables rebuilds its whole shell when you switch project, because every open\n * tab names a table by id \u2014 would otherwise leave the first one's handler\n * attached for ever. Two handlers is not \"twice as responsive\": `Ctrl+K`\n * toggles the dead palette open and the live one closed in the same keystroke,\n * and `Alt+W` closes a tile in a tree nobody can see. `createShell().dispose()`\n * calls this.\n */\n\nexport function installKeymap({ wm, palette, ...opts } = {}) {\n const onKeyDown = (e) => {\n const inField = e.target?.closest?.(\n 'input, textarea, select, [contenteditable=\"true\"]');\n\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k' && !e.shiftKey) {\n e.preventDefault();\n palette.toggle();\n return;\n }\n // \u2500\u2500 Ctrl+Tab / Ctrl+Shift+Tab \u2192 next / previous main-panel tab in the\n // focused tile (wraps). Works everywhere (Tab isn't a text key here);\n // no-op when the tile has a single tab.\n if ((e.ctrlKey || e.metaKey) && !e.altKey && e.key === 'Tab') {\n e.preventDefault();\n wm.cycleFocusedTab(e.shiftKey ? -1 : 1);\n return;\n }\n if (e.key === 'Escape') {\n if (palette.isOpen()) { palette.close(); return; }\n // Let managed-window system handle its own Esc.\n }\n // Backspace = walk one step up the breadcrumb hierarchy. Skips\n // when the user is typing in a field so it doesn't steal\n // text-deletion.\n if (e.key === 'Backspace' && !inField && !e.metaKey && !e.ctrlKey) {\n e.preventDefault();\n wm.navigateBack?.();\n return;\n }\n\n // \u2500\u2500 F1..F8 \u2192 jump straight to the Nth top-nav page. F-keys never\n // type, but we still skip while a field is focused so editing\n // flows (query editor, filters) keep keyboard focus.\n const fMatch = /^F([1-9]|1[0-2])$/.exec(e.key);\n if (fMatch && !inField && !e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) {\n // The selector is a CONFIG option, not a constant. An embedder that\n // moves its sections out of the top bar \u2014 into an icon rail, say \u2014\n // would otherwise find F1..F8 silently doing nothing: the query\n // returns an empty NodeList, `idx < btns.length` is false, and the\n // handler falls through without a hint that anything is bound.\n // Passing `navSelector` is how such an embedder keeps the keys.\n const btns = document.querySelectorAll(\n opts.navSelector\n || '.twm-global-top-bar .twm-bar-center.twm-top-nav .twm-top-nav__btn');\n const idx = Number(fMatch[1]) - 1;\n if (idx < btns.length) {\n e.preventDefault();\n btns[idx].click();\n return;\n }\n }\n\n // \u2500\u2500 Shift+Left/Right \u2192 previous/next bottom-panel tab. No-op when\n // no bottom panel is mounted. Skipped in fields so Shift+Arrow\n // keeps selecting text there.\n if (e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey && !inField\n && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) {\n const tabs = [...document.querySelectorAll('.twm-bp__tabs .twm-bp__tab')];\n if (tabs.length) {\n const cur = tabs.findIndex((t) => t.classList.contains('twm-bp__tab--on'));\n const dir = e.key === 'ArrowRight' ? 1 : -1;\n const next = ((cur < 0 ? 0 : cur) + dir + tabs.length) % tabs.length;\n e.preventDefault();\n tabs[next].click();\n return;\n }\n }\n\n // \u2500\u2500 Ctrl+Left/Right \u2192 iterate virtual desktops (wraps).\n if (e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && !inField\n && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) {\n e.preventDefault();\n wm.cycleDesktop(e.key === 'ArrowRight' ? 1 : -1);\n return;\n }\n\n if (!e.altKey) return;\n // Browser Alt+letter triggers menu-access in some browsers \u2014 always preventDefault.\n const key = e.key.toLowerCase();\n\n // Desktop switch / move \u2014 Alt+digit\n if (/^[1-9]$/.test(e.key)) {\n e.preventDefault();\n const idx = Number(e.key) - 1;\n if (e.shiftKey) wm.moveFocusedToDesktop(idx);\n else wm.switchDesktop(idx);\n return;\n }\n\n switch (key) {\n // Plain Alt+H/V split into an empty pane; with Shift they open\n // the focused tile's own content in the new split.\n case 'h':\n e.preventDefault();\n e.shiftKey ? wm.splitFocusedWith('h') : wm.split('h');\n return;\n case 'v':\n e.preventDefault();\n e.shiftKey ? wm.splitFocusedWith('v') : wm.split('v');\n return;\n case 't': e.preventDefault(); wm.openFocusedInTab(); return;\n case 'n': e.preventDefault(); wm.openFocusedInWindow(); return;\n case 'w': e.preventDefault(); wm.closeFocused(); return;\n case 'f': e.preventDefault(); wm.toggleManagedFocused(); return;\n case 'arrowleft':\n e.preventDefault();\n e.shiftKey ? wm.moveFocused('left') : wm.focusDir('left');\n return;\n case 'arrowright':\n e.preventDefault();\n e.shiftKey ? wm.moveFocused('right') : wm.focusDir('right');\n return;\n case 'arrowup':\n e.preventDefault();\n e.shiftKey ? wm.moveFocused('up') : wm.focusDir('up');\n return;\n case 'arrowdown':\n e.preventDefault();\n e.shiftKey ? wm.moveFocused('down') : wm.focusDir('down');\n return;\n }\n\n // Suppress accidental browser menu-access on bare Alt+letter\n // chords we don't bind (so the user doesn't get a Firefox/Chromium\n // menu popping open mid-flow).\n if (key.length === 1 && /[a-z]/.test(key) && !inField) {\n e.preventDefault();\n }\n };\n\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n}\n", "/**\n * kind_taxonomy.js \u2014 the taxonomy PORT.\n *\n * A \"kind\" is a content type the user can navigate to. The tiling shell\n * needs to know four things about one: what to call it, what icon it\n * wears, which top-nav slot owns it, and what sits above it. It must\n * NOT know what any of them mean.\n *\n * So the taxonomy is INJECTED, not imported. The embedder describes its\n * own ontology and hands it to `createTaxonomy()` at boot; the shell\n * closes over the result. Nothing in this file names a kind, and the\n * framework runs unchanged on a completely different vocabulary.\n *\n * Consumed by:\n *\n * - install.js \u2192 top-nav buttons + active-state highlight\n * - tile_breadcrumb.js \u2192 breadcrumb segments\n * - wm.js navigateBack \u2192 Backspace parent\n * - command_palette.js \u2192 palette chip strip + row icons\n * - tile_tab_menu.js \u2192 which sources a tile's hamburger lists\n *\n * ## KindDef\n *\n * label Long form, used in breadcrumb segments + palette.\n * shortLabel Optional \u22644-char chip text for the top-nav.\n * Falls back to `label` when omitted.\n * icon Material Symbols name.\n * topNav Top-nav category this kind belongs to. Determines which\n * button lights up + which segment the breadcrumb inserts\n * after the root. Omit on the top-nav kind itself.\n * isTopNav True iff this kind owns a slot in the top-nav strip.\n * order Sort key for top-nav display (lower = leftward).\n * appGlobal True iff the kind is not scoped to the root entity (a\n * preferences page, say). Its breadcrumb omits the root.\n * sources Entity-source ids the tile hamburger lists when a tile\n * is on this page. Only meaningful on top-nav kinds; the\n * root's list is the fallback.\n *\n * ancestors? `(props) => [{ kind, id, label? }]` \u2014 the entity-level\n * ancestors of ONE instance, root-most first, EXCLUDING\n * the top-nav segment and the node itself. This is the\n * hook that replaces every hardcoded \"split the id to\n * find its parent\" branch: naming conventions are the\n * embedder's business, not the shell's.\n * labelOf? `(props) => string` \u2014 display label for ONE instance.\n * Defaults to `props.label`, then `props.id`.\n */\n\n/** Build a taxonomy from an embedder's kind map. Validated eagerly and\n * frozen \u2014 a typo in a `topNav` pointer is a boot error, not a chrome\n * surface that silently renders nothing six clicks later. */\nexport function createTaxonomy({ kinds, root } = {}) {\n if (!kinds || typeof kinds !== 'object') {\n throw new TypeError('createTaxonomy: `kinds` must be an object of KindDefs');\n }\n if (!root || !kinds[root]) {\n throw new Error(`createTaxonomy: root kind '${root}' is not in the taxonomy`);\n }\n for (const [kind, def] of Object.entries(kinds)) {\n if (!def || typeof def !== 'object') {\n throw new TypeError(`createTaxonomy: '${kind}' is not a KindDef`);\n }\n if (def.topNav && !kinds[def.topNav]) {\n throw new Error(\n `createTaxonomy: '${kind}' hangs off unknown topNav '${def.topNav}'`);\n }\n if (def.isTopNav && def.topNav) {\n throw new Error(\n `createTaxonomy: '${kind}' is both a top-nav kind and owned by one`);\n }\n }\n const _kinds = Object.freeze({ ...kinds });\n\n /** Lookup. `undefined` for unknown kinds \u2014 chrome code treats that\n * as \"render the kind name verbatim\". */\n const meta = (kind) => _kinds[kind];\n\n /** Top-nav category id that owns `kind`. For a top-nav kind itself,\n * returns the same kind. `undefined` when the kind isn't in the\n * taxonomy (caller renders a fallback). */\n const topNavFor = (kind) => {\n const m = _kinds[kind];\n if (!m) return undefined;\n return m.isTopNav ? kind : m.topNav;\n };\n\n /** Backspace target \u2014 walk one step up the KIND hierarchy. A\n * top-nav page goes to the root; everything else goes to its\n * top-nav. The root itself returns `null`. */\n const parentKindFor = (kind) => {\n if (kind === root) return null;\n const m = _kinds[kind];\n if (!m) return null;\n if (m.isTopNav) return root;\n return m.topNav || root;\n };\n\n /** Entity-level ancestors of one instance, root-most first. Empty\n * unless the KindDef supplies an `ancestors` hook. Normalised to\n * `{ kind, props }` so callers hand the result straight to\n * `wm.navigate`. A throwing hook is a warning, never a crash: the\n * breadcrumb degrades to its generic form. */\n const ancestors = (kind, props) => {\n const fn = _kinds[kind]?.ancestors;\n if (typeof fn !== 'function') return [];\n let out;\n try { out = fn(props || {}); }\n catch (err) {\n console.warn('[taxonomy] ancestors() threw for', kind, err);\n return [];\n }\n if (!Array.isArray(out)) return [];\n return out\n .filter((a) => a && a.kind)\n .map((a) => ({ kind: a.kind, props: { id: a.id, label: a.label ?? a.id } }));\n };\n\n /** The nearest entity-level ancestor, or `null`. */\n const parentOf = (kind, props) => {\n const chain = ancestors(kind, props);\n return chain.length ? chain[chain.length - 1] : null;\n };\n\n /** Display label for one instance of `kind`. */\n const labelOf = (kind, props) => {\n const fn = _kinds[kind]?.labelOf;\n if (typeof fn === 'function') {\n try {\n const s = fn(props || {});\n if (s) return String(s);\n } catch (err) {\n console.warn('[taxonomy] labelOf() threw for', kind, err);\n }\n }\n if (props?.label) return String(props.label);\n return props?.id != null ? String(props.id) : '';\n };\n\n /** Entity-source ids a tile on `kind` should offer. Falls back to\n * the root's list, which is the \"everything\" default. */\n const sourcesFor = (kind) => {\n const s = _kinds[kind]?.sources ?? _kinds[root]?.sources;\n return Array.isArray(s) ? s : [];\n };\n\n /** Top-nav entries in display order:\n * { kind, label (short), longLabel, icon }\n * `label` is the chip text; `longLabel` is what the palette shows. */\n const topNavEntries = () =>\n Object.entries(_kinds)\n .filter(([, m]) => m.isTopNav)\n .sort((a, b) => (a[1].order ?? 1000) - (b[1].order ?? 1000))\n .map(([kind, m]) => ({\n kind,\n label: m.shortLabel || m.label,\n longLabel: m.label,\n icon: m.icon,\n }));\n\n return Object.freeze({\n root,\n kinds: _kinds,\n meta,\n topNavFor,\n parentKindFor,\n ancestors,\n parentOf,\n labelOf,\n sourcesFor,\n topNavEntries,\n });\n}\n", "/**\n * panel_keys.js \u2014 central keyboard router for panel-local navigation.\n *\n * THE PROBLEM IT SOLVES\n * ---------------------\n * Several panels want plain-key navigation (\u2191/\u2193 to move a cursor, Enter to\n * open, single letters for actions): the landing tables, the top-nav\n * overview pages, and any future browser panel. Each one used to attach its\n * OWN `document` keydown listener gated by the heuristic\n *\n * hostEl.contains(document.activeElement) || activeElement === <body>\n *\n * That heuristic is wrong as soon as there is more than one tile. When the\n * user selects a different tile, focus usually rests on `<body>`, so EVERY\n * mounted landing's listener still fires \u2014 the \"main\" panel keeps eating\n * arrow keys even though another panel is selected, and two panels can move\n * their cursors at once. The selected tile is never actually consulted.\n *\n * THE CONCEPT\n * -----------\n * One listener, one authority. Panels do not listen on `document`\n * themselves; they REGISTER a handler bound to their host element. On each\n * keydown the router asks the window manager which leaf is currently\n * focused (the single source of truth \u2014 `WindowManager.focusedLeafEl()`)\n * and dispatches the event to the handler whose host lives inside that leaf\n * \u2014 and to no one else. A panel automatically receives keys exactly when,\n * and only when, it is the selected tile.\n *\n * Content rendered OUTSIDE the tile tree (a promoted managed window or a\n * floating shell) has no focused-leaf concept; for those hosts the router\n * falls back to the focus-containment scope so they keep working.\n *\n * USAGE\n * -----\n * import { registerPanelKeys } from './panel_keys.js';\n * const off = registerPanelKeys(hostEl, (ev) => { ... });\n * // later, on teardown:\n * off();\n *\n * The handler is responsible for its own key semantics (which keys, whether\n * to skip editable targets, when to preventDefault). The router only decides\n * WHETHER this panel is the active keyboard target. A handler that calls\n * `ev.preventDefault()` stops the event from reaching any other handler in\n * the same leaf.\n *\n * When no router has been installed (unit tests, or a landing used outside\n * the WM), `registerPanelKeys` degrades to a self-contained listener using\n * the legacy focus-containment scope, so callers work in isolation too.\n */\n\nlet _router = null;\n\n/** Install the singleton router. Called once by the WindowManager. */\nexport function installPanelKeyRouter(wm) {\n if (_router) _router.destroy();\n _router = new PanelKeyRouter(wm);\n return _router;\n}\n\n/** The installed router, or null if the WM hasn't built one yet. */\nexport function getPanelKeyRouter() { return _router; }\n\n/** Tear down the singleton (test cleanup / shell teardown). */\nexport function uninstallPanelKeyRouter() {\n _router?.destroy();\n _router = null;\n}\n\n/**\n * Register `handler` as the key handler for `hostEl`'s panel. Returns an\n * unregister function \u2014 call it on teardown. Routes through the installed\n * router when present, otherwise falls back to a legacy scoped listener so\n * the panel still works outside the WM.\n */\nexport function registerPanelKeys(hostEl, handler) {\n if (!hostEl || typeof handler !== 'function') return () => {};\n if (_router) return _router.register(hostEl, handler);\n return _legacyRegister(hostEl, handler);\n}\n\nexport class PanelKeyRouter {\n constructor(wm) {\n this._wm = wm;\n this._entries = []; // { hostEl, handler }\n this._onKey = this._dispatch.bind(this);\n document.addEventListener('keydown', this._onKey);\n }\n\n register(hostEl, handler) {\n const entry = { hostEl, handler };\n this._entries.push(entry);\n return () => {\n const i = this._entries.indexOf(entry);\n if (i >= 0) this._entries.splice(i, 1);\n };\n }\n\n destroy() {\n document.removeEventListener('keydown', this._onKey);\n this._entries.length = 0;\n }\n\n /** Is `hostEl` the panel that should receive keys right now? */\n _isActiveTarget(hostEl) {\n if (!hostEl?.isConnected) return false;\n const leaf = hostEl.closest?.('.twm-leaf');\n if (leaf) {\n // Inside the tile tree: only the focused leaf is live. This is\n // the whole point \u2014 compare against the WM's single source of\n // truth, not against `document.activeElement`.\n return leaf === this._wm?.focusedLeafEl?.();\n }\n // Outside any tile (managed window / floating shell): no focused\n // leaf to consult, so fall back to focus containment.\n return hostEl.contains(document.activeElement)\n || document.activeElement === document.body;\n }\n\n _dispatch(ev) {\n // Snapshot: a handler may unregister (e.g. navigate away) mid-loop.\n for (const entry of [...this._entries]) {\n if (!this._isActiveTarget(entry.hostEl)) continue;\n try { entry.handler(ev); }\n catch (err) { console.warn('[panel-keys] handler threw', err); }\n // A handler that consumed the key ends the chain so a second\n // handler in the same leaf (e.g. the action-footer shortcuts)\n // doesn't double-fire on the same press.\n if (ev.defaultPrevented) return;\n }\n }\n}\n\n/** Legacy fallback used only when no router is installed. Mirrors the old\n * per-panel scope check so isolated/test usage keeps its previous\n * behavior. */\nfunction _legacyRegister(hostEl, handler) {\n const wrapped = (ev) => {\n if (!hostEl.isConnected) return;\n if (!hostEl.contains(document.activeElement)\n && document.activeElement !== document.body) return;\n handler(ev);\n };\n document.addEventListener('keydown', wrapped);\n return () => document.removeEventListener('keydown', wrapped);\n}\n", "/**\n * landing_table.js \u2014 shared helpers for the \"navigation table\"\n * pattern used by SFC / Markets / Scenarios / KPIs landings (and the\n * market-archetype instances table). Three responsibilities:\n *\n * 1. Single-click row \u2192 open the entity (handled by callers).\n * 2. Right-click row \u2192 custom context menu with Open / Edit / Delete\n * (replaces DataTable's default copy-as-TSV menu for these tables;\n * the default still applies to data tables outside this helper).\n * 3. Renders an \"actions\" column with inline edit + delete icons so\n * the user can act on a row without opening it first.\n *\n * Callers configure the per-kind behaviour via the `entityActions`\n * object: `{ open(row), edit(row), delete(row) }` \u2014 any of which may be\n * omitted to hide the corresponding action.\n */\n\nimport { showContextMenu } from '../ui/components/context_menu.js';\nimport { registerPanelKeys } from './panel_keys.js';\n\n/** Attach the navigation-table interactions to a host element that\n * contains a `<table>` already rendered by DataTable. Returns a\n * teardown function.\n *\n * `entityActions` maps action names to handlers. The canonical names\n * are `open` / `edit` / `delete` (plus `openInTab` / `openInWindow`),\n * but ANY `data-twm-action=\"<name>\"` button or context-menu item\n * dispatches to `entityActions[<name>]` \u2014 so tree landings can wire\n * `toggle`, `contract`, etc. through the same robust path instead of\n * hand-rolling their own tbody listeners (which is how the Agents /\n * Markets pages used to drift out of sync with this helper).\n *\n * `options.buildMenu(row) => items[]` overrides the default\n * Open / Edit / Delete right-click menu for tree-shaped rows that need\n * conditional entries (expand/collapse, contract detail). Each item's\n * `action` is dispatched the same way as a button. */\nexport function attachLandingTableBehavior(host, getRow, entityActions, options = {}) {\n if (!host) return () => {};\n\n // Delegate from the stable `host` container, NOT the <tbody>. DataTable\n // rebuilds its <tbody> on every internal re-render \u2014 pagination, filter,\n // sort, and the post-layout column-width sync \u2014 and each re-render\n // re-installs ITS OWN row listeners (data_table.js `_installInteractions`)\n // but knows nothing about ours. A listener bound directly to the tbody\n // therefore goes silently dead after the first re-render: events still\n // reach the live tbody (DataTable's copy menu still pops) while our\n // handlers sit orphaned on a detached node. Binding on `host` and\n // resolving the row at event time survives any number of tbody rebuilds.\n\n // Dispatch an action name to its handler. The `open-tab` /\n // `open-window` menu aliases map onto the `openInTab` /\n // `openInWindow` handlers; every other name (including `edit`,\n // `delete`, and tree-only verbs like `toggle` / `contract`) routes\n // straight to `entityActions[name]`.\n const dispatch = (action, row) => {\n if (!row) return;\n const fn = action === 'open-tab' ? entityActions.openInTab\n : action === 'open-window' ? entityActions.openInWindow\n : entityActions[action];\n try { fn?.(row); }\n catch (e) { console.warn(`[landing] action \"${action}\" failed`, e); }\n };\n\n // The BODY <tr> for an event, re-found live each time. Excludes the\n // header / filter row (it lives in the separate non-scrolling header\n // wrap, not inside `.preview-table-wrap`'s tbody).\n const bodyRowFor = (ev) => {\n const tr = ev.target.closest('tr');\n if (!tr || !tr.closest('tbody')) return null;\n const wrap = tr.closest('.twm-preview-table-wrap');\n if (!wrap || !host.contains(wrap)) return null;\n return tr;\n };\n\n const onClick = (ev) => {\n // Inline action buttons short-circuit the row-open default and\n // suppress every other listener (DataTable's row-selection\n // listener, etc.). `stopImmediatePropagation` is load-bearing.\n const actionBtn = ev.target.closest('[data-twm-action]');\n if (actionBtn && host.contains(actionBtn)) {\n ev.preventDefault();\n ev.stopPropagation();\n ev.stopImmediatePropagation();\n const tr = actionBtn.closest('tr');\n dispatch(actionBtn.dataset.twmAction, tr ? getRow(tr.__rowIndex) : null);\n return;\n }\n // Plain row click \u2192 open. Ignore clicks on interactive controls\n // (buttons, filter inputs, links) and anything outside a body row\n // (pagination strip, header, empty space).\n if (ev.target.closest('button, a, input, select, textarea')) return;\n const tr = bodyRowFor(ev);\n if (tr) dispatch('open', getRow(tr.__rowIndex));\n };\n\n const defaultMenu = (row) => {\n const items = [];\n if (entityActions.open) {\n items.push({ label: 'Open', icon: 'open_in_new', action: 'open' });\n if (entityActions.openInTab) {\n items.push({ label: 'Open in new tab', icon: 'tab', action: 'open-tab' });\n }\n if (entityActions.openInWindow) {\n items.push({ label: 'Open in new window', icon: 'open_in_full',\n action: 'open-window' });\n }\n }\n if (entityActions.edit) items.push({ label: 'Rename / edit', icon: 'edit', action: 'edit' });\n if (entityActions.delete) {\n if (items.length) items.push({ separator: true });\n items.push({ label: 'Delete', icon: 'delete', action: 'delete', danger: true });\n }\n return items;\n };\n const buildMenu = typeof options.buildMenu === 'function'\n ? options.buildMenu\n : defaultMenu;\n\n const onContextMenu = (ev) => {\n const tr = bodyRowFor(ev);\n if (!tr) return;\n ev.preventDefault();\n // Bypass DataTable's own copy-as-TSV/CSV menu by stopping\n // propagation \u2014 the right-click for a navigation row is about the\n // *entity*, not the cell text. Capture phase on `host` runs before\n // DataTable's bubble-phase contextmenu on the tbody.\n ev.stopPropagation();\n const row = getRow(tr.__rowIndex);\n if (!row) return;\n const items = buildMenu(row) || [];\n if (items.length === 0) return;\n showContextMenu(ev.clientX, ev.clientY, items, (action) => dispatch(action, row));\n };\n\n // Click in bubble phase (after DataTable's row-selection); contextmenu\n // in CAPTURE phase so ours runs before DataTable's bubble copy menu.\n host.addEventListener('click', onClick);\n host.addEventListener('contextmenu', onContextMenu, true);\n return () => {\n host.removeEventListener('click', onClick);\n host.removeEventListener('contextmenu', onContextMenu, true);\n };\n}\n\n/** Wire pane-focus highlighting. SfcLandingTab gives each\n * `.ea-sfc-landing__pane` an `--focused` modifier when it's the\n * active pane \u2014 that's what paints the blue head + accent title.\n * Other landings need the same treatment to read as consistent.\n *\n * Single-pane landings can call this once: the only pane stays\n * focused at all times. Multi-pane landings get mousedown wiring\n * per pane and a reflectFocus() helper they can call from keyboard\n * navigation if they implement it. */\nexport function wireLandingPaneFocus(hostEl) {\n const panes = Array.from(hostEl.querySelectorAll('.twm-landing__pane'));\n if (panes.length === 0) return { focus: () => {}, panes };\n let focused = panes[0];\n const reflect = () => {\n for (const p of panes) {\n p.classList.toggle('twm-landing__pane--focused', p === focused);\n }\n };\n reflect();\n for (const p of panes) {\n p.addEventListener('mousedown', () => {\n if (focused === p) return;\n focused = p;\n reflect();\n });\n }\n return {\n focus: (pane) => { if (pane && panes.includes(pane)) { focused = pane; reflect(); } },\n panes,\n };\n}\n\n/** Arrow-key navigation for a landing page's DataTable.\n *\n * - ArrowDown / ArrowUp moves a row cursor within `host`.\n * - Enter (and the optional `activateKey`, e.g. `o`) opens the row.\n * - Home / End jump to the first / last row.\n * - Captures the keys at the document level so the user doesn't have\n * to click inside the table first \u2014 same UX as sfc_landing_tab.\n *\n * The visible cursor is the `ea-row-cursor` CSS class on the active\n * `<tr>`; the row also `scrollIntoView({block:'nearest'})` so the\n * selection stays visible during keyboard scroll-through.\n *\n * Returns `{ teardown, refresh, focusFirst }`. Call `refresh()` after\n * the table re-renders so the cursor lands on the right row again.\n */\nexport function attachLandingKeyboardNav(host, getRows, onActivate, options = {}) {\n if (!host) return { teardown: () => {}, refresh: () => {}, focusFirst: () => {} };\n\n let cursor = 0;\n const tbody = () => host.querySelector('tbody');\n\n const repaintCursor = () => {\n const tb = tbody();\n if (!tb) return;\n const rows = getRows() || [];\n if (rows.length === 0) { cursor = 0; return; }\n if (cursor >= rows.length) cursor = rows.length - 1;\n if (cursor < 0) cursor = 0;\n let active = null;\n tb.querySelectorAll('tr').forEach((tr) => {\n const on = tr.__rowIndex === cursor;\n tr.classList.toggle('twm-row-cursor', on);\n if (on) active = tr;\n });\n if (active) active.scrollIntoView({ block: 'nearest' });\n };\n\n const move = (delta) => {\n const rows = getRows() || [];\n if (rows.length === 0) return;\n cursor = Math.max(0, Math.min(rows.length - 1, cursor + delta));\n repaintCursor();\n };\n\n const open = () => {\n const rows = getRows() || [];\n const row = rows[cursor];\n if (row) onActivate(row);\n };\n\n const onKey = (ev) => {\n if (!host.isConnected) return;\n // Stay out of the way of inputs and unrelated focus targets.\n const t = ev.target;\n const editable = t && (\n t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' ||\n t.tagName === 'SELECT' || t.isContentEditable);\n if (editable) return;\n // Scope (which tile is selected) is owned by the panel-key router.\n\n if (ev.key === 'ArrowDown') { ev.preventDefault(); move(+1); }\n else if (ev.key === 'ArrowUp') { ev.preventDefault(); move(-1); }\n else if (ev.key === 'Home') { ev.preventDefault(); cursor = 0; repaintCursor(); }\n else if (ev.key === 'End') {\n ev.preventDefault();\n const rows = getRows() || [];\n cursor = Math.max(0, rows.length - 1);\n repaintCursor();\n }\n else if (ev.key === 'Enter') { ev.preventDefault(); open(); }\n else if (options.activateKey && ev.key === options.activateKey\n && !ev.metaKey && !ev.ctrlKey && !ev.altKey) {\n ev.preventDefault(); open();\n }\n };\n\n const offKey = registerPanelKeys(host, onKey);\n // Re-paint immediately so the cursor is visible from first render.\n setTimeout(repaintCursor, 0);\n\n return {\n teardown: () => offKey(),\n refresh: () => repaintCursor(),\n focusFirst: () => { cursor = 0; repaintCursor(); },\n };\n}\n\n\n/** Unified landing-page keyboard shell.\n *\n * Wires the four behaviors every landing should share, in one call:\n *\n * - Pane focus highlight (`.ea-sfc-landing__pane--focused`) \u2014 click\n * on a pane or Tab to it, the header band lights up.\n * - ArrowUp / ArrowDown moves the row cursor within the focused\n * pane's table; Home / End jump to first / last. The visible cursor\n * is `.ea-row-cursor` on the active `<tr>`.\n * - Tab / Shift+Tab cycles between panes. Single-pane landings just\n * no-op the Tab \u2014 the behaviour is a property of the shell, not\n * the page.\n * - Enter activates the cursor row via `onActivate(row)`.\n * - ArrowRight / ArrowLeft expand / collapse the cursor row when\n * the pane provides `onExpandToggle` + `isExpanded` (tree-shaped\n * tables; harmless on flat tables that don't define them).\n *\n * Each pane spec:\n * {\n * paneEl, // .ea-sfc-landing__pane element\n * getRows, // () => Array<row>\n * onActivate, // (row) => void \u2014 Enter\n * onExpandToggle?, // (row, expand:bool) => void\n * isExpanded?, // (row) => bool\n * isExpandable?, // (row) => bool \u2014 gates Left/Right\n * tableHost?, // override pane.querySelector('.twm-landing__table')\n * }\n *\n * Returns `{ teardown, refresh, focusPane(idx) }`. Call `refresh()` after\n * the pane tables re-render so the cursor lands on the right row again.\n *\n * Backspace is intentionally **not** handled here \u2014 `keymap.js` owns\n * it globally and walks `wm.navigateBack()` regardless of which\n * landing is showing. */\nexport function attachLandingShell(hostEl, { panes, initialPane = 0 } = {}) {\n if (!hostEl || !Array.isArray(panes) || panes.length === 0) {\n return { teardown: () => {}, refresh: () => {}, focusPane: () => {} };\n }\n\n const state = panes.map((spec) => ({\n spec,\n paneEl: spec.paneEl,\n tableHost: spec.tableHost\n || spec.paneEl?.querySelector('.twm-landing__table')\n || spec.paneEl,\n cursor: 0,\n }));\n let focusedIdx = Math.max(0, Math.min(panes.length - 1, initialPane));\n\n const reflectPaneFocus = () => {\n state.forEach((s, i) => {\n s.paneEl?.classList.toggle(\n 'twm-landing__pane--focused', i === focusedIdx);\n });\n };\n\n const repaintCursor = (idx = focusedIdx) => {\n const s = state[idx];\n if (!s) return;\n const tbody = s.tableHost?.querySelector('tbody');\n if (!tbody) return;\n const rows = s.spec.getRows?.() || [];\n if (rows.length === 0) { s.cursor = 0; return; }\n if (s.cursor >= rows.length) s.cursor = rows.length - 1;\n if (s.cursor < 0) s.cursor = 0;\n let active = null;\n tbody.querySelectorAll('tr').forEach((tr) => {\n const on = tr.__rowIndex === s.cursor;\n tr.classList.toggle('twm-row-cursor', on);\n if (on) active = tr;\n });\n if (active && idx === focusedIdx) {\n active.scrollIntoView({ block: 'nearest' });\n }\n };\n\n const move = (delta) => {\n const s = state[focusedIdx];\n if (!s) return;\n const rows = s.spec.getRows?.() || [];\n if (rows.length === 0) return;\n s.cursor = Math.max(0, Math.min(rows.length - 1, s.cursor + delta));\n repaintCursor();\n };\n\n const jump = (toEnd) => {\n const s = state[focusedIdx];\n if (!s) return;\n const rows = s.spec.getRows?.() || [];\n s.cursor = toEnd ? Math.max(0, rows.length - 1) : 0;\n repaintCursor();\n };\n\n const currentRow = () => {\n const s = state[focusedIdx];\n if (!s) return null;\n const rows = s.spec.getRows?.() || [];\n return rows[s.cursor] ?? null;\n };\n\n const activate = () => {\n const row = currentRow();\n if (!row) return;\n try { state[focusedIdx].spec.onActivate?.(row); }\n catch (err) { console.warn('[landing-shell] activate failed', err); }\n };\n\n /** Open the current row in a new tab on the tile. Pane spec opts in\n * by providing `onActivateInTab(row)`; the default-Enter handler\n * remains `onActivate`. Falls back to `onActivate` so panes that\n * haven't been migrated still respond to Ctrl+Enter. */\n const activateInTab = () => {\n const row = currentRow();\n if (!row) return;\n const spec = state[focusedIdx].spec;\n const fn = spec.onActivateInTab || spec.onActivate;\n try { fn?.(row); }\n catch (err) { console.warn('[landing-shell] activate-tab failed', err); }\n };\n\n /** Open the current row in a fresh managed window. Same opt-in\n * pattern as activateInTab \u2014 panes provide `onActivateInWindow`\n * for Shift+Enter, fall back to onActivate when absent. */\n const activateInWindow = () => {\n const row = currentRow();\n if (!row) return;\n const spec = state[focusedIdx].spec;\n const fn = spec.onActivateInWindow || spec.onActivate;\n try { fn?.(row); }\n catch (err) { console.warn('[landing-shell] activate-window failed', err); }\n };\n\n const expandToggle = (expand) => {\n const s = state[focusedIdx];\n if (!s?.spec.onExpandToggle) return false;\n const row = currentRow();\n if (!row) return false;\n if (s.spec.isExpandable && !s.spec.isExpandable(row)) return false;\n const currentlyExpanded = s.spec.isExpanded\n ? !!s.spec.isExpanded(row)\n : false;\n if (expand === currentlyExpanded) return false;\n try { s.spec.onExpandToggle(row, expand); }\n catch (err) { console.warn('[landing-shell] expand failed', err); }\n return true;\n };\n\n const focusPane = (idx) => {\n if (idx < 0 || idx >= state.length) return;\n if (idx === focusedIdx) return;\n focusedIdx = idx;\n reflectPaneFocus();\n repaintCursor();\n };\n\n const cyclePane = (dir) => {\n if (state.length < 2) return;\n focusPane((focusedIdx + dir + state.length) % state.length);\n };\n\n const onMouseDown = (ev) => {\n // Find which pane the click landed in. Inline action buttons\n // and the like still focus the surrounding pane.\n const paneEl = ev.target.closest?.('.twm-landing__pane');\n if (!paneEl) return;\n const idx = state.findIndex((s) => s.paneEl === paneEl);\n if (idx < 0 || idx === focusedIdx) return;\n focusedIdx = idx;\n reflectPaneFocus();\n repaintCursor();\n };\n\n const onKey = (ev) => {\n if (!hostEl.isConnected) return;\n // Scope is owned by the panel-key router: this handler only runs\n // when our tile is the selected one. We still skip editable\n // targets so typing in a field inside the tile isn't hijacked.\n const t = ev.target;\n const editable = t && (\n t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' ||\n t.tagName === 'SELECT' || t.isContentEditable);\n if (editable) return;\n // Browser-style activation chords (caught BEFORE the modifier\n // guard so the WM keymap doesn't swallow them):\n // Ctrl/Cmd + Enter \u2192 open in new TAB on this tile\n // Shift + Enter \u2192 open in new managed WINDOW\n if (ev.key === 'Enter' && (ev.metaKey || ev.ctrlKey) && !ev.altKey) {\n ev.preventDefault();\n activateInTab();\n return;\n }\n if (ev.key === 'Enter' && ev.shiftKey && !ev.altKey\n && !ev.metaKey && !ev.ctrlKey) {\n ev.preventDefault();\n activateInWindow();\n return;\n }\n // Other modifier chords belong to the WM / browser.\n if (ev.metaKey || ev.ctrlKey || ev.altKey || ev.shiftKey) return;\n\n switch (ev.key) {\n case 'ArrowDown':\n ev.preventDefault(); move(+1); return;\n case 'ArrowUp':\n ev.preventDefault(); move(-1); return;\n case 'Home':\n ev.preventDefault(); jump(false); return;\n case 'End':\n ev.preventDefault(); jump(true); return;\n case 'Enter':\n ev.preventDefault(); activate(); return;\n case 'Tab':\n // Cycle panes; single-pane case still preventDefault so\n // focus doesn't escape into the browser chrome.\n ev.preventDefault();\n cyclePane(ev.shiftKey ? -1 : +1);\n return;\n case 'ArrowRight':\n if (expandToggle(true)) ev.preventDefault();\n return;\n case 'ArrowLeft':\n if (expandToggle(false)) ev.preventDefault();\n return;\n }\n };\n\n hostEl.addEventListener('mousedown', onMouseDown);\n const offKey = registerPanelKeys(hostEl, onKey);\n reflectPaneFocus();\n // Initial cursor paint runs after the synchronous mount returns so\n // tables that render asynchronously already have their tbody.\n setTimeout(() => state.forEach((_, i) => repaintCursor(i)), 0);\n\n // Park native focus on hostEl when our leaf is the active one. The\n // panel-key router no longer depends on this for arrow routing (it\n // keys off the focused leaf, not document.activeElement), but parking\n // focus keeps native focus semantics \u2014 scroll-into-view, :focus-within\n // styling, and screen-reader context \u2014 anchored to the live tile.\n //\n // Guarded by the WM's `.twm-leaf--focused` class so we don't steal\n // focus when another tile owns the user's attention. The\n // requestAnimationFrame second pass catches the case where the\n // focus class is applied a frame after mount.\n const parkFocusIfActive = () => {\n const leaf = hostEl.closest?.('.twm-leaf');\n // Page is rendered outside a tile (managed window / floating\n // shell) \u2014 there is no focused-leaf concept; safe to take focus.\n const inTile = !!leaf;\n if (inTile && !leaf.classList.contains('twm-leaf--focused')) return;\n if (hostEl.contains(document.activeElement)) return;\n if (hostEl.tabIndex == null || hostEl.tabIndex < -1) hostEl.tabIndex = -1;\n try { hostEl.focus({ preventScroll: true }); } catch {}\n };\n parkFocusIfActive();\n requestAnimationFrame(parkFocusIfActive);\n\n return {\n teardown: () => {\n hostEl.removeEventListener('mousedown', onMouseDown);\n offKey();\n },\n refresh: () => state.forEach((_, i) => repaintCursor(i)),\n focusPane,\n currentRow,\n };\n}\n\n\n/** Standardized action-footer renderer.\n *\n * Each action: `{ id, label, icon, shortcut?, title?, onClick }`.\n *\n * When `shortcut` is set (single letter), the button label gets a\n * `[N]` suffix and pressing that key (without modifiers, outside any\n * text input, while focus is in `hostEl` or on the body) fires the\n * action. The visible chip + the keydown handler are the same source\n * of truth, so users can never wonder which letter triggers what.\n *\n * Returns `{ teardown, refresh(actions) }`. */\n\n\n// \u2500\u2500\u2500 #143 \u2014 shared landing shell \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/** Mount the boilerplate every landing repeats: a detail-header with\n * title + stat fields, a single `.ea-sfc-landing__pane` with title +\n * keyboard-shortcut hint, the DataTable host, and a footer slot for\n * `mountLandingActions` to fill.\n *\n * Callers receive `{paneEl, tableHost, footerEl, setStat(key, value)}`\n * \u2014 `paneEl` feeds straight into `attachLandingShell({panes: [{paneEl,\n * \u2026}]})`; `tableHost` is where `new DataTable(host, \u2026)` mounts;\n * `footerEl` is the second arg to `mountLandingActions`; `setStat`\n * patches a single stat by `data-role` without rebuilding the header.\n *\n * HTML output is identical to what each landing built by hand before\n * this helper existed \u2014 only the source location moves.\n *\n * @param {HTMLElement} hostEl\n * @param {object} opts\n * @param {string} opts.title \u2014 `<h2>` text on the left of the header.\n * @param {Array<{key,label,initial?}>} [opts.stats] \u2014 header chip per stat. `key` is the `data-role` for setStat(); `label` is the muted label above the value; `initial` defaults to `0`.\n * @param {object} opts.pane\n * @param {string} opts.pane.key \u2014 the `data-pane=\"\u2026\"` value (also doubles as the table-host's `data-role=\"<key>-host\"`).\n * @param {string} opts.pane.title \u2014 pane-head title.\n * @param {string} opts.pane.hint \u2014 pane-head keyboard-shortcut hint.\n * @returns {{headerEl: HTMLElement, paneEl: HTMLElement, tableHost: HTMLElement, footerEl: HTMLElement, setStat: (key: string, value: any) => void}}\n */\nexport function mountLandingShell(hostEl, opts) {\n const {\n title = '',\n stats = [],\n pane = { key: 'rows', title: '', hint: '' },\n } = opts || {};\n const paneKey = String(pane.key || 'rows');\n\n const _esc = (s) => String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n\n const statsHtml = (stats || []).map((s) => `\n <span class=\"twm-landing__stat twm-entity-header__field\">\n <span>${_esc(s.label || '')}</span>\n <strong data-role=\"${_esc(s.key)}\">${_esc(s.initial != null ? s.initial : 0)}</strong>\n </span>\n `).join('');\n\n hostEl.classList.add('twm-landing');\n hostEl.innerHTML = `\n <header class=\"twm-entity-header\">\n <h2>${_esc(title)}</h2>\n ${statsHtml}\n <span class=\"twm-entity-header__spacer\"></span>\n </header>\n <div class=\"twm-landing__split twm-landing__split--single\">\n <section class=\"twm-landing__pane\" data-pane=\"${_esc(paneKey)}\">\n <div class=\"twm-landing__pane-head\">\n <span class=\"twm-landing__pane-title\">${_esc(pane.title || '')}</span>\n <span class=\"twm-landing__pane-hint\">${_esc(pane.hint || '')}</span>\n </div>\n <div class=\"twm-landing__table\" data-role=\"${_esc(paneKey)}-host\"></div>\n </section>\n </div>\n <footer class=\"twm-landing__actions\" data-role=\"actions\"></footer>\n `;\n\n const headerEl = hostEl.querySelector('.twm-entity-header');\n const paneEl = hostEl.querySelector(`[data-pane=\"${paneKey}\"]`);\n const tableHost = hostEl.querySelector(`[data-role=\"${paneKey}-host\"]`);\n const footerEl = hostEl.querySelector('[data-role=\"actions\"]');\n\n const setStat = (key, value) => {\n const el = hostEl.querySelector(`[data-role=\"${key}\"]`);\n if (el) el.textContent = String(value);\n };\n\n return { headerEl, paneEl, tableHost, footerEl, setStat };\n}\n\n\nexport function mountLandingActions(footerEl, actions, hostEl) {\n if (!footerEl) return { teardown: () => {}, refresh: () => {} };\n let current = [];\n const renderInto = (list) => {\n current = (list || []).filter(Boolean);\n footerEl.innerHTML = '';\n for (const a of current) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'twm-action-btn';\n if (a.id) btn.dataset.action = a.id;\n if (a.title) btn.title = a.title;\n const icon = a.icon\n ? `<span class=\"material-symbols-outlined\">${_esc(a.icon)}</span>`\n : '';\n const sc = a.shortcut\n ? ` <span class=\"twm-action-btn__kbd\">[${_esc(a.shortcut.toUpperCase())}]</span>`\n : '';\n btn.innerHTML = `${icon}<span>${_esc(a.label || '')}</span>${sc}`;\n btn.addEventListener('click', (ev) => {\n ev.preventDefault();\n try { a.onClick?.(ev); }\n catch (err) { console.warn('[landing-actions] click failed', err); }\n });\n footerEl.appendChild(btn);\n }\n };\n\n const onKey = (ev) => {\n if (!footerEl.isConnected) return;\n if (ev.metaKey || ev.ctrlKey || ev.altKey) return;\n // Scope is owned by the panel-key router; we only guard editable\n // targets so single-letter shortcuts don't fire while typing.\n const t = ev.target;\n const editable = t && (\n t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' ||\n t.tagName === 'SELECT' || t.isContentEditable);\n if (editable) return;\n const k = ev.key.length === 1 ? ev.key.toLowerCase() : ev.key;\n const hit = current.find((a) =>\n a.shortcut && String(a.shortcut).toLowerCase() === k);\n if (!hit) return;\n ev.preventDefault();\n try { hit.onClick?.(ev); }\n catch (err) { console.warn('[landing-actions] shortcut failed', err); }\n };\n\n renderInto(actions);\n const scopeEl = hostEl || footerEl.closest('.twm-landing') || footerEl;\n const offKey = registerPanelKeys(scopeEl, onKey);\n return {\n teardown: () => offKey(),\n refresh: renderInto,\n };\n}\n\n\n/** A DataTable `renderCell` hook that paints inline Edit + Delete icon\n * buttons in the configured action column. Use as:\n *\n * renderCell: actionsCellRenderer(actionsColIdx, { edit: true, delete: true })\n *\n * Other columns fall through to the default text renderer. */\nexport function actionsCellRenderer(actionsColIdx, options = { edit: true, delete: true }) {\n return (td, _value, colIdx) => {\n if (colIdx !== actionsColIdx) return false;\n const parts = [];\n if (options.edit) {\n parts.push(`<button type=\"button\" class=\"twm-row-action\"\n data-twm-action=\"edit\"\n title=\"Edit\"\n aria-label=\"Edit\">\n <span class=\"material-symbols-outlined\">edit</span>\n </button>`);\n }\n if (options.delete) {\n parts.push(`<button type=\"button\" class=\"twm-row-action twm-row-action--danger\"\n data-twm-action=\"delete\"\n title=\"Delete\"\n aria-label=\"Delete\">\n <span class=\"material-symbols-outlined\">delete</span>\n </button>`);\n }\n td.classList.add('twm-row-actions-cell');\n td.innerHTML = parts.join('');\n return true;\n };\n}\n\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n}\n", "/**\n * loading_overlay.js \u2014 the canonical \"content is loading\" overlay.\n *\n * An absolutely-positioned spinner layer dropped over a slot while an async\n * mount is in flight, with an 80ms grace period so cache hits / instant\n * renders never strobe a spinner. Returns a `done()` that removes it.\n *\n * Uses the global `twm-tile-loading` styles (ui/css/tiling/host.css). Shared\n * by the WM page-stub shell and the agent Code tab's split view.\n *\n * const done = makeLoadingOverlay(slot);\n * try { await mount(); } finally { done(); }\n */\n\n/** Drop a loading overlay over `slot` and return a `done()` to remove it.\n * The overlay stays invisible for `delay` ms (default 80) so fast renders\n * don't flash a spinner. `slot` must be a positioned ancestor (or pass one\n * that is); the overlay is `position:absolute; inset:0`. */\nexport function makeLoadingOverlay(slot, { delay = 80 } = {}) {\n const overlay = document.createElement('div');\n overlay.className = 'twm-tile-loading twm-tile-loading--pending';\n overlay.innerHTML =\n '<div class=\"twm-tile-loading__spinner\" aria-label=\"Loading\"></div>';\n slot.appendChild(overlay);\n // Promote to visible only if the mount hasn't finished within the grace.\n const showTimer = setTimeout(() => {\n overlay.classList.remove('twm-tile-loading--pending');\n }, delay);\n return () => {\n clearTimeout(showTimer);\n overlay.remove();\n };\n}\n", "/**\n * nav_panel.js \u2014 the left navigation panel-tile: a mode strip, a shared\n * filter box, and one lazily-mounted pane per mode.\n *\n * \u250C\u2500[ mode ][ mode ][ mode ]\u2500\u2500\u2500\u2500\u2500\u2510 mode tabs\n * \u2502 \u250C\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\u2510 \u2502\n * \u2502 \u2502 Filter\u2026 \u2502 \u2502 unified filter (sidebar-controls)\n * \u2502 \u2514\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\u2518 \u2502\n * \u2502 \u250C\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\u2510 \u2502\n * \u2502 \u2502 (pane body) \u2502 \u2502\n * \u2502 \u2514\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\u2518 \u2502\n *\n * The panel owns the strip, the filter and the pane hosts. It owns NONE of\n * the panes: the embedder passes them in. There used to be three hardcoded\n * modes here (a file tree, an object tree, a git view) plus a dead category\n * browser; all of that is the embedder's, and lives on its side now.\n *\n * A mode:\n *\n * {\n * id, icon, label,\n * mount(paneEl, { wm, eventBus }) -> {\n * activate?(), // called every time the mode is selected (may be async)\n * filter?(query), // the shared filter box changed\n * destroy?(),\n * }\n * }\n *\n * `mount()` runs at most once, the first time its mode is selected.\n * `activate()` runs on every selection, INCLUDING the first \u2014 a pane that\n * has to rebuild on each visit (a git status list, say) does its work there.\n * The filter query is remembered per mode.\n */\n\nexport function mountNavPanel(hostEl, {\n modes = [],\n wm = null,\n eventBus = null,\n filterPlaceholder = 'Filter\u2026',\n} = {}) {\n if (!Array.isArray(modes) || modes.length === 0) {\n throw new Error('mountNavPanel: at least one mode is required');\n }\n\n hostEl.classList.add('twm-nav');\n hostEl.innerHTML = `\n <div class=\"twm-nav__modes\" data-role=\"modes\"></div>\n <div class=\"twm-sidebar-controls twm-nav__filter\">\n <span class=\"material-symbols-outlined twm-sidebar-search-icon\">search</span>\n <input class=\"twm-data-page__search twm-nav__filter-input\"\n type=\"search\" placeholder=\"${_esc(filterPlaceholder)}\"\n autocomplete=\"off\" />\n </div>\n <div class=\"twm-nav__body\" data-role=\"body\"></div>\n `;\n const modesEl = hostEl.querySelector('[data-role=\"modes\"]');\n const bodyEl = hostEl.querySelector('[data-role=\"body\"]');\n const filterEl = hostEl.querySelector('.twm-nav__filter-input');\n\n modesEl.innerHTML = modes.map((m) => `\n <button class=\"twm-nav__mode twm-has-tooltip\" data-mode=\"${_esc(m.id)}\"\n data-tooltip=\"${_esc(m.label)}\">\n <span class=\"material-symbols-outlined\">${_esc(m.icon)}</span>\n <span>${_esc(m.label)}</span>\n </button>\n `).join('');\n\n const byId = new Map(modes.map((m) => [m.id, m]));\n const hosts = {}; // mode id -> pane host element\n const panes = {}; // mode id -> the object the mode's mount() returned\n const filterByMode = Object.fromEntries(modes.map((m) => [m.id, '']));\n let mode = modes[0].id;\n\n const ensureHost = (id) => {\n if (hosts[id]) return hosts[id];\n const el = document.createElement('div');\n el.className = `twm-nav__pane twm-nav__pane--${id}`;\n el.style.display = 'none';\n bodyEl.appendChild(el);\n hosts[id] = el;\n return el;\n };\n\n const activate = async (next) => {\n const def = byId.get(next);\n if (!def) return;\n mode = next;\n for (const m of modes) {\n ensureHost(m.id).style.display = (m.id === next) ? 'flex' : 'none';\n }\n modesEl.querySelectorAll('[data-mode]').forEach((b) =>\n b.classList.toggle('twm-nav__mode--on', b.dataset.mode === next));\n filterEl.value = filterByMode[next] || '';\n\n const paneEl = ensureHost(next);\n if (!panes[next]) {\n try { panes[next] = def.mount(paneEl, { wm, eventBus }) || {}; }\n catch (err) {\n console.error('[nav] mode mount failed', next, err);\n panes[next] = {};\n }\n }\n // `activate()` runs on EVERY selection, including the first. A pane that\n // must rebuild on each visit (a git status list) does its work here.\n try { await panes[next].activate?.(); }\n catch (err) { console.error('[nav] mode activate failed', next, err); }\n _applyFilter();\n };\n\n const _applyFilter = () => {\n const q = filterByMode[mode] || '';\n try { panes[mode]?.filter?.(q); }\n catch (err) { console.warn('[nav] filter failed', mode, err); }\n };\n\n filterEl.addEventListener('input', () => {\n filterByMode[mode] = filterEl.value;\n _applyFilter();\n });\n\n modesEl.addEventListener('click', (e) => {\n const btn = e.target.closest('[data-mode]');\n if (btn) activate(btn.dataset.mode);\n });\n\n activate(mode);\n\n return {\n refresh: () => activate(mode),\n destroy: () => {\n for (const p of Object.values(panes)) {\n try { p?.destroy?.(); } catch { /* ignore */ }\n }\n },\n };\n}\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => (\n { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]\n ));\n}\n", "/**\n * tile_breadcrumb.js \u2014 per-tile breadcrumb strip rendered above each\n * page's content.\n *\n * Reuses the existing `topbar-breadcrumb__*` CSS classes so the look\n * matches the rest of the app (notebook editor, etc.). Segments are\n * built from the leaf's content kind + props.id, and clicking a\n * non-current segment routes through wm.navigate so the user jumps to\n * that level.\n *\n * The path is assembled from FOUR taxonomy questions, none of which this\n * module answers itself:\n *\n * 1. `ctx.rootCrumb` the leading segment + how its label stays fresh\n * 2. `taxonomy.topNavFor` which section owns the kind\n * 3. `taxonomy.ancestors` the entity-level chain above ONE instance\n * 4. `taxonomy.labelOf` what to call that instance\n *\n * (3) is the one that matters. Deriving an instance's parent from its id\n * is a naming convention, and naming conventions belong to the embedder;\n * this file used to hardcode two of them.\n */\n\n/** Build a breadcrumb strip element + render initial state. Returns\n * `{ el, destroy }`. The strip is wired to the WM (clicks open content\n * in the primary tile) and, when the embedder supplies a `rootCrumb`\n * with a `subscribe` hook, to whatever keeps the root label fresh. */\nexport function mountTileBreadcrumb(kind, props, ctx) {\n const eventBus = ctx?.eventBus;\n const taxonomy = ctx?.taxonomy;\n const rootCrumb = ctx?.rootCrumb || null;\n if (!taxonomy) {\n console.error('[breadcrumb] no taxonomy in ctx \u2014 cannot render');\n return { el: document.createElement('nav'), destroy: () => {} };\n }\n const root = document.createElement('nav');\n root.className = 'twm-topbar-breadcrumb twm-tile-breadcrumb';\n root.setAttribute('aria-label', 'Tile breadcrumb');\n\n // Look up wm late (at click time) instead of capturing it once at\n // mount. ctx.wm should always be set by the renderer, but fall\n // back to the global escape hatch (set by install.js after\n // wm.load()) so a closure that lost ctx.wm \u2014 or a mount called\n // before the renderer threaded wm through \u2014 still navigates.\n const getWm = () => ctx?.wm || window.__twm?.wm || null;\n\n // Breadcrumb segments route through the canonical `wm.navigate`\n // helper so the same three-axis routing (window \u2192 replace window,\n // tile \u2192 replace active tab, panel/none \u2192 primary) lives in one\n // place. Critically, this is what makes the breadcrumb work in\n // WINDOWED mode \u2014 passing `ctx` keeps windowId in scope so the\n // segment click stays inside the floating window instead of\n // jumping back to the primary tile.\n const navigate = (k, p = {}) => {\n const wm = getWm();\n if (!wm) {\n console.error('[breadcrumb] no WM available \u2014 click ignored', k);\n return;\n }\n if (typeof wm.navigate === 'function') {\n wm.navigate(k, p, { ctx, dest: 'origin' });\n } else if (typeof wm.openInPrimary === 'function') {\n // Fallback for older WM revisions.\n wm.openInPrimary(k, p);\n } else {\n console.error('[breadcrumb] WM has no navigate/openInPrimary');\n }\n };\n\n // The root segment's label is the embedder's to supply and to keep\n // fresh (it names whatever the root entity is \u2014 a project, a\n // workspace, a document). Absent `rootCrumb` \u21D2 no root segment, and\n // the breadcrumb simply starts at the top-nav.\n let rootLabel = rootCrumb?.label || '';\n\n // Read at RENDER time, not captured: the trail grows with every\n // click-through, and a breadcrumb that showed the trail as it was when the\n // tile mounted would be describing a journey the user has since continued.\n const trailOf = () => {\n if (typeof ctx?.trailSegments !== 'function') return [];\n try { return ctx.trailSegments() || []; }\n catch (err) { console.warn('[breadcrumb] trailSegments threw', err); return []; }\n };\n const render = () => {\n _renderInto(root,\n _segments(kind, props, taxonomy, rootCrumb, rootLabel, navigate, trailOf()));\n };\n render();\n\n let unsubscribe = null;\n if (typeof rootCrumb?.subscribe === 'function') {\n try {\n unsubscribe = rootCrumb.subscribe((next) => {\n if (next?.label) rootLabel = next.label;\n render();\n }, { api: ctx?.api, host: ctx?.host, eventBus });\n } catch (err) {\n console.warn('[breadcrumb] rootCrumb.subscribe threw', err);\n }\n }\n\n return {\n el: root,\n /** Repaint. A trail-driven breadcrumb changes without the tile\n * remounting \u2014 following a lookup replaces the active tab's content in\n * place \u2014 so the embedder that grew the trail says when. */\n refresh: render,\n destroy: () => {\n try { unsubscribe?.(); }\n catch (err) { console.warn('[breadcrumb] rootCrumb teardown threw', err); }\n },\n };\n}\n\nfunction _segments(kind, props, taxonomy, rootCrumb, rootLabel, navigate, trail) {\n const segs = [];\n const meta = taxonomy.meta(kind);\n\n // App-global kinds (e.g. a preferences page) aren't scoped to the\n // root entity \u2014 they open with no project loaded and persist to\n // app-level storage \u2014 so their breadcrumb has no root ancestor.\n if (meta?.appGlobal) {\n return [{ icon: meta.icon, label: meta.label, onClick: null }];\n }\n\n // 1. The root segment \u2192 clicks back to the root page.\n if (rootCrumb) {\n segs.push({\n icon: rootCrumb.icon,\n label: rootLabel || rootCrumb.label,\n onClick: () => navigate(rootCrumb.navKind ?? taxonomy.root),\n });\n }\n\n // 2. The top-nav segment. Skipped on the root itself (the root\n // segment already names it) and when the kind isn't in the\n // taxonomy at all.\n const topNav = taxonomy.topNavFor(kind);\n const topNavMeta = topNav ? taxonomy.meta(topNav) : null;\n if (topNav && topNav !== taxonomy.root && topNavMeta) {\n segs.push({\n icon: topNavMeta.icon,\n label: topNavMeta.label,\n onClick: () => navigate(topNav),\n });\n }\n\n // 2b. THE TRAIL. An embedder whose navigation is a walk rather than a\n // descent \u2014 click a customer, follow a lookup to its region, follow\n // that to a country \u2014 has a real path that the taxonomy cannot know,\n // because none of those is an ANCESTOR of the next. The tree already\n // records it: every in-tile navigation pushes the outgoing content\n // onto the active tab's `history` stack, which is what Backspace pops.\n //\n // `ctx.trailSegments` hands that stack over, already shaped. Absent, or\n // empty, this is a no-op and the breadcrumb is exactly the ancestor\n // walk it has always been.\n for (const step of (trail || [])) {\n if (!step?.kind) continue;\n segs.push({\n icon: taxonomy.meta(step.kind)?.icon || 'description',\n label: step.title || step.props?.label || step.props?.id || step.kind,\n onClick: () => navigate(step.kind, step.props || {}),\n });\n }\n\n // 3. Entity-level ancestors, root-most first \u2014 supplied by the\n // taxonomy, not by string surgery on the id.\n for (const anc of taxonomy.ancestors(kind, props)) {\n segs.push({\n icon: taxonomy.meta(anc.kind)?.icon || 'description',\n label: anc.props.label ?? anc.props.id,\n onClick: () => navigate(anc.kind, anc.props),\n });\n }\n\n // 4. The node itself.\n const hasEntity = !!props?.id;\n if (meta) {\n if (hasEntity) {\n segs.push({\n icon: meta.icon,\n label: taxonomy.labelOf(kind, props),\n onClick: null,\n });\n } else if (kind !== topNav) {\n // The kind IS the top-nav (e.g. opening a landing page).\n // Don't duplicate the chip \u2014 the segment above already\n // names it.\n segs.push({\n icon: meta.icon,\n label: meta.label,\n onClick: null,\n });\n }\n } else if (hasEntity) {\n // Kind isn't in the taxonomy yet \u2014 still surface the entity\n // name so the user sees where they are, just without an icon.\n segs.push({\n icon: 'description',\n label: props.label || props.id,\n onClick: null,\n });\n }\n return segs;\n}\n\nfunction _renderInto(container, segments) {\n container.innerHTML = '';\n if (!segments?.length) return;\n const ol = document.createElement('ol');\n ol.className = 'twm-topbar-breadcrumb__list';\n segments.forEach((seg, i) => {\n const li = document.createElement('li');\n li.className = 'twm-topbar-breadcrumb__item';\n const isLast = i === segments.length - 1;\n if (isLast || !seg.onClick) {\n li.classList.add('twm-topbar-breadcrumb__item--current');\n li.innerHTML = `\n <span class=\"material-symbols-outlined twm-topbar-breadcrumb__icon\">${_esc(seg.icon)}</span>\n <span class=\"twm-topbar-breadcrumb__current\">${_esc(seg.label)}</span>\n `;\n } else {\n const btn = document.createElement('button');\n btn.className = 'twm-topbar-breadcrumb__link';\n btn.type = 'button';\n btn.innerHTML = `\n <span class=\"material-symbols-outlined twm-topbar-breadcrumb__icon\">${_esc(seg.icon)}</span>\n <span>${_esc(seg.label)}</span>\n `;\n btn.addEventListener('click', () => {\n btn.classList.add('twm-tile-breadcrumb__link--flash');\n setTimeout(() => {\n btn.classList.remove('twm-tile-breadcrumb__link--flash');\n }, 180);\n try { seg.onClick(); }\n catch (e) { console.error('[breadcrumb] segment click threw', e); }\n });\n li.appendChild(btn);\n const sep = document.createElement('span');\n sep.className = 'twm-topbar-breadcrumb__separator';\n sep.textContent = '\u203A';\n li.appendChild(sep);\n }\n ol.appendChild(li);\n });\n container.appendChild(ol);\n}\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n}\n", "/**\n * page_factory.js \u2014 the MECHANISM behind a tile's content, with no\n * knowledge of what that content is about.\n *\n * `createPageFactories()` returns the four wrappers an embedder uses to\n * turn its own page/tab modules into content factories:\n *\n * tabFactory(kind, factory) breadcrumb page-shell + a mounted tab\n * bareTabFactory(kind, factory) same, minus the breadcrumb (transient forms)\n * stubPageFactory(kind, render) breadcrumb + a plain render fn + loading overlay\n * mountTabBody / makeShim the pieces the three are built from\n *\n * \u2500\u2500 makeShim: the navigation contract \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 * Mounted content receives a `workspaceTabs` object. That object IS the\n * library's public navigation API in embryo:\n *\n * openTab open a kind in THIS tile (openFromContext)\n * openInTab open it as a new TAB in this tile (openInTabFromContext)\n * openInWindow open it in a fresh managed window\n * updateProps persist editor sub-state into this tile's tab props\n * closeTab close this tab (falls back to closing the tile)\n * notifyChanged broadcast \"I mutated the project\" so sidebars refresh\n *\n * \u2500\u2500 refreshOn / events: no bus-name literals here \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * This module used to hardcode five bus names (`<domain>:run:tick`, \u2026) and\n * one mutation event. Both are now INJECTED:\n *\n * refreshOn { [busEvent]: reasonString } fanned into tab.refresh(reason)\n * events { projectChanged? } emitted by close/notifyChanged\n *\n * An absent key means an absent subscription / an absent emit, so an\n * embedder with no run loop simply passes `{}` and nothing fires.\n */\n\nimport { makeLoadingOverlay } from './loading_overlay.js';\nimport { mountTileBreadcrumb } from './tile_breadcrumb.js';\n\n/**\n * @param {object} cfg\n * @param {object} cfg.eventBus the embedder's bus (may be null)\n * @param {object} cfg.events { projectChanged?: string }\n * @param {object} cfg.refreshOn { [busEventName]: reason } -> tab.refresh(reason)\n */\nexport function createPageFactories({ eventBus = null, events = {}, refreshOn = {} } = {}) {\n const projectChanged = events?.projectChanged || null;\n\n // The `workspaceTabs` API tile content calls to open/close tabs and\n // broadcast mutations. Shared by breadcrumbed and bare tabs.\n const makeShim = (ctx) => ({\n // Route through openFromContext so navigation triggered from a\n // split tile / window stays in that container.\n openTab: ({ kind: k, entityId, label, icon, subTab }) =>\n ctx.wm?.openFromContext(ctx, k, { id: entityId, label, icon, subTab }),\n // Open the same content as a NEW TAB in the current tile.\n openInTab: ({ kind: k, entityId, label, icon, subTab }) =>\n ctx.wm?.openInTabFromContext(ctx, k, { id: entityId, label, icon, subTab }),\n // Open the same content in a fresh managed window.\n openInWindow: ({ kind: k, entityId, label, icon, subTab }) =>\n ctx.wm?.navigate(k, { id: entityId, label, icon, subTab }, { ctx, dest: 'window' }),\n // Persist editor sub-state into this tile's WM tab props.\n updateProps: (patch) => {\n try { ctx.wm?.updateActiveTabProps?.(ctx.leafId, patch); }\n catch (err) { console.warn('[shim] updateProps failed', err); }\n },\n // Close just THIS tab (not the whole tile); falls back to closing\n // the tile when it's the last tab. Also broadcasts so sidebars /\n // landings / nav refresh against the mutated project state.\n closeTab: (_id) => {\n if (projectChanged) {\n try { eventBus?.emit?.(projectChanged, { source: 'twm-tab-close' }); } catch {}\n }\n try { ctx.wm?.closeActiveTab?.(ctx.leafId); } catch (err) {\n console.warn('[shim] closeTab failed', err);\n }\n },\n // Non-closing mutations (Save/New/Rename) broadcast so listeners refresh.\n notifyChanged: (domain) => {\n if (!projectChanged) return;\n try {\n eventBus?.emit?.(projectChanged,\n { source: 'tab-mutation', domain: domain || null });\n } catch {}\n },\n registerProvider: () => {},\n getActiveTab: () => null,\n });\n\n // Run a content factory inside a content slot: build the shim, mount\n // the tab, drive the loading overlay, and fan the embedder's declared\n // events into the tab's refresh(reason) hook (tabs that don't\n // self-subscribe would otherwise freeze mid-run). Returns a teardown\n // that removes listeners + disposes the tab.\n const mountTabBody = (factory, contentSlot, props, ctx) => {\n const shim = makeShim(ctx);\n const tab = factory(contentSlot, props?.id ?? props?.entityId ?? null, {\n logger: null, eventBus, workspaceTabs: shim,\n wm: ctx.wm, leafId: ctx.leafId, windowId: ctx.windowId,\n });\n const hideLoading = makeLoadingOverlay(contentSlot);\n try {\n const ret = tab?.mount?.(props);\n if (ret && typeof ret.then === 'function') {\n ret.then(hideLoading, (err) => {\n console.error('[page] mount failed', err);\n hideLoading();\n });\n } else {\n requestAnimationFrame(hideLoading);\n }\n } catch (err) {\n console.error('[page] mount failed', err);\n hideLoading();\n }\n const listeners = {};\n if (typeof tab?.refresh === 'function' && eventBus?.on) {\n for (const [busEvent, reason] of Object.entries(refreshOn || {})) {\n const fn = () => {\n try { tab.refresh(reason); }\n catch (err) { console.warn('[page] refresh failed', busEvent, err); }\n };\n listeners[busEvent] = fn;\n eventBus.on(busEvent, fn);\n }\n }\n return () => {\n for (const [n, fn] of Object.entries(listeners)) {\n try { eventBus?.off?.(n, fn); } catch {}\n }\n try { tab?.dispose?.(); tab?.unmount?.(); } catch {}\n };\n };\n\n /** Wrap a tab factory in a \"page shell\" \u2014 breadcrumb strip at the top,\n * content fills the rest. `kind` is captured at build time so the\n * breadcrumb knows which crumbs to build. */\n const tabFactory = (kind, factory) => (host, props, ctx) => {\n host.classList.add('twm-page-shell');\n host.innerHTML = '';\n const contentSlot = document.createElement('div');\n contentSlot.className = 'twm-page-shell__content';\n // Focusable via JS so `document.activeElement` lands inside the\n // page when the tile is activated.\n contentSlot.tabIndex = -1;\n\n // A breadcrumb navigates the host TILE \u2014 meaningless for an entity\n // opened in a standalone managed window, where we want only the\n // entity view. Skip the crumb when mounted in a window.\n let crumb = null;\n if (!ctx?.windowId) {\n const breadcrumbSlot = document.createElement('div');\n breadcrumbSlot.className = 'twm-page-shell__breadcrumb';\n host.appendChild(breadcrumbSlot);\n crumb = mountTileBreadcrumb(kind, props, { ...ctx, eventBus });\n breadcrumbSlot.appendChild(crumb.el);\n }\n host.appendChild(contentSlot);\n\n const teardown = mountTabBody(factory, contentSlot, props, ctx);\n return {\n destroy: () => {\n teardown();\n try { crumb?.destroy(); } catch {}\n },\n };\n };\n\n // Like tabFactory but with NO breadcrumb chrome \u2014 for transient /\n // form content (e.g. an add-row entry mask) that isn't an entity\n // view. Content still gets the full ctx (wm/leafId) + shim, so it\n // can self-close via wm.closeActiveTab.\n const bareTabFactory = (kind, factory) => (host, props, ctx) => {\n host.classList.add('twm-page-shell');\n host.innerHTML = '';\n const contentSlot = document.createElement('div');\n contentSlot.className = 'twm-page-shell__content';\n contentSlot.tabIndex = -1;\n host.appendChild(contentSlot);\n return { destroy: mountTabBody(factory, contentSlot, props, ctx) };\n };\n\n /** Page with a breadcrumb at the top and a plain `render(slot, props, ctx)`\n * body \u2014 for content that isn't a full tab object. If `render` returns a\n * `ready` promise the loading overlay waits on it. */\n const stubPageFactory = (kind, render) => (host, props, ctx) => {\n host.classList.add('twm-page-shell');\n host.innerHTML = '';\n const breadcrumbSlot = document.createElement('div');\n breadcrumbSlot.className = 'twm-page-shell__breadcrumb';\n const contentSlot = document.createElement('div');\n contentSlot.className = 'twm-page-shell__content';\n contentSlot.tabIndex = -1;\n host.appendChild(breadcrumbSlot);\n host.appendChild(contentSlot);\n const crumb = mountTileBreadcrumb(kind, props, { ...ctx, eventBus });\n breadcrumbSlot.appendChild(crumb.el);\n const hideLoading = makeLoadingOverlay(contentSlot);\n const ret = render(contentSlot, props, ctx) || {};\n // Most landings kick off their own async refresh after the\n // synchronous render returns. If render exposes a `ready`\n // promise we await it; otherwise hide on the next frame.\n if (ret.ready && typeof ret.ready.then === 'function') {\n ret.ready.then(hideLoading, hideLoading);\n } else {\n requestAnimationFrame(hideLoading);\n }\n return {\n title: ret.title,\n destroy: () => { try { crumb.destroy(); } catch {} ret.destroy?.(); },\n };\n };\n\n return Object.freeze({\n makeShim, mountTabBody, tabFactory, bareTabFactory, stubPageFactory,\n });\n}\n", "/**\n * tile_renderer.js \u2014 maps a TileTree into nested flex containers, with\n * resize splitters between siblings and a chrome strip per leaf.\n *\n * The renderer keeps a small per-leaf DOM cache so content factories\n * mount exactly once per (kind, props) pair \u2014 switching the focused\n * leaf or resizing doesn't re-mount tabs / pages.\n *\n * \u2500\u2500 C22. TWO TAB LAYOUTS, AND THE TREE KNOWS ABOUT NEITHER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * A leaf's tabs live in the TREE (`Leaf: { tabs, activeTabIdx }`) and are the\n * source of truth; `content`/`title` mirror `tabs[active]`. Where the strip is\n * DRAWN, and what it looks like, is therefore a rendering decision and nothing\n * else \u2014 which is why both layouts share one tab model, one action vocabulary\n * (`switch` / `close` / `move` / `menu` / `open-menu`, all through\n * `ctx.onLeafTabAction`) and one set of tree mutations in the WM.\n *\n * `bottom` \u2014 the framework's own spreadsheet strip under the tile body.\n * THE DEFAULT, and it stays the default: an existing embedder\n * upgrading must not find its panes rearranged.\n * `top` \u2014 `NotebookTabBar` (`../editor/notebook_tab_bar.js`), the editor\n * tab strip, mounted BETWEEN the chrome and the body. The chrome\n * is the pane's identity \u2014 its glyph, its title, its verbs \u2014 and\n * the tabs are its contents, so the tabs go under it, not over.\n *\n * The tab bar is reused rather than reimplemented. It already has the\n * behaviours a top strip is expected to have \u2014 drag-to-reorder, a scrolling\n * overflow, close buttons, dirty dots \u2014 and a second implementation of them\n * here is a second set of the same bugs.\n *\n * \u2500\u2500 The layout is also readable and writable as an ATTRIBUTE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * `tabLayout` is a `createShell` option like `snapPromotion` and\n * `promoteInPlace`, but unlike those it is something a USER may reasonably\n * change while looking at the thing it changes. An embedder's settings pane\n * holds no renderer reference \u2014 it holds, at most, the element it handed to\n * `createShell` \u2014 so the renderer mirrors the option onto its own root as\n * `data-twm-tabs` and OBSERVES it. Setting that attribute on the root element\n * is then a complete, reference-free way to switch layout live, and the same\n * attribute is the hook a stylesheet keys off. `setTabLayout()` is the direct\n * route for anyone who does hold the renderer.\n *\n * Switching is deliberately cheap: only the strip is rebuilt and the tab bar\n * element is MOVED between its two positions. The body is never detached, so\n * no content factory is unmounted and nothing mounted in a tile \u2014 a grid with\n * staged edits, an editor with an undo stack \u2014 notices that it happened.\n *\n * \u2500\u2500 C33. A TAB DRAGGED INTO A TILE \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 * *\"I would like to be able to drag & drop tabs into tiles, too.\"* A tab could\n * already be dragged WITHIN its own strip, and a WINDOW could already be\n * dropped onto a tile; between the two there was nothing.\n *\n * \u2550\u2550 TWO DRAG MECHANISMS, AND THEY CANNOT BE ONE GESTURE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * This codebase drags things in two incompatible ways, and the choice here was\n * made for us. Tab reorder is NATIVE HTML5 DRAG-AND-DROP \u2014 `draggable=\"true\"`\n * plus `dragstart`/`dragover`/`drop` \u2014 in both layouts: the `top` strip gets it\n * from the shared `DragReorder`, the `bottom` strip hand-rolls the same three\n * events. Window snapping is POINTER EVENTS\n * (`../ui/components/managed_window.js`, `_dragState` with a `pointermove`\n * listener on `document`). **Once a native drag begins the browser stops\n * dispatching `pointermove` for its whole duration**, so a tab drag can never\n * reach the pointer-driven snap controller, and a pointer-driven tab drag would\n * be a second mechanism sitting beside the reorder that already works. So: this\n * is HTML5 DnD, and it extends the gesture that exists rather than competing\n * with it.\n *\n * \u2550\u2550 THE GEOMETRY IS SHARED ANYWAY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * What could not be shared is the transport; what MUST be shared is the answer.\n * `wm.tabDropProbe` (R18) runs the same zone matrix a window drop runs, through\n * the same `_dropZoneFor` tail (R17) \u2014 so a 28px edge means SPLIT and a centre\n * means TAB-or-FILL for both, and the vocabulary is learned once. Product\n * owner, 2026-08-27: the edge drop splits, \"reusing the window snap-zone\n * vocabulary\".\n *\n * \u2550\u2550 THE SINGLE-TAB TILE HAS NO STRIP, SO THE CHROME IS THE HANDLE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * Both layouts hide the bar below two tabs (`_renderTabBar`), which is the\n * common case \u2014 one table in one tile \u2014 so a feature that needs a tab strip to\n * grab is a feature most tiles cannot offer. Product owner, 2026-08-27: the\n * tile chrome / breadcrumb is the drag source for a single-tab leaf. With one\n * tab there is no ambiguity to resolve \u2014 \"move this tab\" and \"move what is in\n * this pane\" name the same thing \u2014 which is exactly why it is limited to that\n * case: on a multi-tab leaf the strip already names each tab individually and a\n * chrome drag would have to guess which one was meant.\n *\n * IT DOES NOT COST THE CHROME ITS EXISTING GESTURES \u2014 BUT IT DID, AND THE\n * PARAGRAPH THAT USED TO SIT HERE IS WHY. It read: the pull \"loses cleanly\"\n * to a native drag, and \"is also still reachable, on every part of the chrome\n * a single-tab leaf's title does not cover\". That is a description of\n * `draggable` on the TITLE. `_syncChromeDragSource` sets it on the CHROME \u2014\n * the whole strip \u2014 so there was no part left over, and since both layouts\n * hide the tab strip below two tabs, a single-tab leaf is the ORDINARY tile\n * rather than an edge case. Pull-down-to-float was dead application-wide, and\n * the sentence saying it was fine sat in this file the whole time.\n *\n * The two gestures are separated by DIRECTION now, through one predicate\n * (`_isDownwardPull`) that both of them read: a downward grab is the float,\n * and `dragstart` refuses the native drag so the pointer goes back to the\n * pull; anything else is the tab drag and is untouched. The double-click\n * cannot collide with either \u2014 a `dblclick` carries no movement, so no drag\n * ever starts.\n *\n * A CLAIM ABOUT A BROWSER NEEDS A BROWSER. Nothing in this repository's suites\n * can begin a native drag \u2014 jsdom dispatches only the drag events a test hands\n * it \u2014 so this collision was invisible to every gate on both sides and stayed\n * that way through review. It was settled by driving real mouse input at\n * Edge 152 over CDP: as shipped, a 140px pull on a table tile's chrome gives\n * `dragstart`, `pointercancel`, `dragend` and no window; with the refusal,\n * `pointermove` resumes and the pull fires at 26px; sideways and diagonal\n * grabs still drag.\n *\n * A pane whose content VETOED `promote` (C20) is not a drag source either, and\n * that is one rule rather than two: a tab you may not lift out of its pane is a\n * tab you may not drag into another one. It is what keeps an embedder's master\n * tile \u2014 the ground floating windows stand on \u2014 from being dragged away.\n *\n * \u2550\u2550 THE DRAG STATE LIVES ON THE RENDERER \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * `_tabDrag` is an instance field, not a per-leaf closure, and that is\n * structural. The bottom strip's existing reorder keeps `dragFromIdx` in a\n * closure built per leaf render, so leaf B's handlers cannot see that leaf A\n * started a drag \u2014 which is fine for a reorder that begins and ends in one\n * strip and useless for one that crosses tiles.\n *\n * The identity has to live there for a second reason: under the HTML5 protected\n * mode `dataTransfer.getData()` returns the empty string for every event except\n * `drop`, so the source leaf CANNOT be read while deciding whether to arm.\n * `TILE_TAB_MIME` is therefore a type-only marker \u2014 an admission ticket, not a\n * message \u2014 and everything else is read off `_tabDrag`.\n *\n * \u2550\u2550 AND THE LISTENERS ARE BOUND ONCE, ON THE ROOT \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()` does `this.root.innerHTML = ''`, so a listener attached to a leaf\n * wrap during a repaint-heavy gesture is gone by the next frame; the\n * constructor's `mousedown` is the pattern and these follow it. For the same\n * reason NOTHING on the `dragover` path may call `render()` \u2014 rebuilding the\n * wrap that holds the drag source aborts the native drag, and the DOM\n * afterwards reads perfectly correct, which is the exact shape of the\n * five-times-reported `mousedown`-repaint defect one mechanism over.\n *\n * OUT OF SCOPE, deliberately and in writing: dragging a tab out of a FLOATING\n * WINDOW's strip (its tabs live in the WM's window record, not in the tree, so\n * the source policy is a different function's); dropping on empty space to\n * float a window (in HTML5 DnD \"dropped on nothing\" is `dragend` with no\n * `drop`, which is also what Escape produces, so it would float a window every\n * time a user changed their mind); and crossing DESKTOPS (only the active one\n * is rendered, so there is nothing to hit).\n */\n\nimport { NotebookTabBar } from '../editor/notebook_tab_bar.js';\n\nconst SPLITTER_PX = 4;\n/** The two tab layouts, and the framework's default. `bottom` is the default\n * because it is what every existing embedder already renders. */\nconst TAB_LAYOUTS = ['bottom', 'top'];\nconst DEFAULT_TAB_LAYOUT = 'bottom';\n\n/** Anything that is not one of the two layouts is the default rather than an\n * error: this value arrives from a DOM attribute and from persisted user\n * settings, and neither is a place to throw. */\nfunction _normalizeTabLayout(value) {\n return TAB_LAYOUTS.includes(value) ? value : DEFAULT_TAB_LAYOUT;\n}\n\n/**\n * `NotebookTabBar`'s vocabulary is FILE PATHS, and a tile tab is not a file.\n *\n * The path is only ever an identity string to it \u2014 the key its reorder uses,\n * the value it compares against `activeFilePath`, the selector it renames by \u2014\n * so a leaf's tab INDEX serves, and is the one identity a tab list is\n * guaranteed to have (the tree gives tabs no ids and this renderer may not\n * add any).\n *\n * The `builtin://` prefix is not decoration. `#beginInlineRename` refuses to\n * start on a path with it (`notebook_tab_bar.js:402`), which is exactly what a\n * tile tab needs: double-click rename would rename a TAB, and a tab is a view\n * of an entity whose name lives somewhere this renderer cannot reach. A rename\n * that silently does nothing is worse than no rename, and using the component's\n * own guard beats disabling the gesture from outside.\n */\nfunction _tabKey(idx) { return `builtin://tab/${idx}`; }\n\nfunction _tabKeyIndex(key) {\n const n = Number(String(key ?? '').replace('builtin://tab/', ''));\n return Number.isInteger(n) && n >= 0 ? n : -1;\n}\n/** How far the chrome must be pulled DOWN before the pane lifts out as a\n * floating window. Far enough that focusing a tile by clicking its header\n * never becomes one by accident. */\nconst TILE_LIFT_PX = 24;\n/** WHAT IS NOT A GRIP, for both of the chrome's float gestures.\n *\n * A tile's chrome is a grip with controls sitting on it \u2014 the split, float\n * and close buttons on the right, whatever the content contributed on the\n * left, and (defensively; the strip is a sibling of the chrome today, not a\n * child) a tab. Neither the downward pull nor the double-click may fire when\n * the press landed on one of those.\n *\n * ONE CONSTANT, TWO LISTENERS, and that is the point of writing it down\n * rather than repeating the selector: two doors onto one verb that disagree\n * about where the door is are worse than one door. A button added to this\n * strip later must become inert under BOTH gestures at once, and it cannot\n * do that if each of them carries its own copy of the rule. */\nconst CHROME_NO_FLOAT = 'button, .twm-leaf__tab';\n\n/**\n * IS THIS GESTURE THE DOWNWARD PULL? Asked in TWO places that must not be\n * allowed to disagree, which is the only reason it is a function.\n *\n * The pull asks it of every `pointermove`, to decide whether 24px of travel\n * has become a float. `_syncChromeDragSource`'s `dragstart` asks it of the\n * first few pixels, to decide whether to REFUSE the native drag and leave the\n * pointer to the pull. Two answers to \"which way is this going\" would produce\n * a gesture that is neither: the drag declines, the pull never accepts, and\n * the chrome does nothing at all.\n *\n * `sideways <= down` rather than `<`, so a gesture exactly on the diagonal\n * belongs to the pull in both readers \u2014 the pull's own test has always been\n * `sideways > down` rejects, so this is the same boundary written once.\n */\nfunction _isDownwardPull(down, sideways) {\n return down > 0 && sideways <= down;\n}\n\n/**\n * C33. THE MARKER THAT SAYS \"THIS DRAG IS ONE OF OURS\", AND NOTHING ELSE.\n *\n * It carries the literal string `'1'` because it carries no information at all.\n * Under the HTML5 protected-mode rules `dataTransfer.getData()` answers the\n * empty string for every event of a drag except `drop`, and only `types` is\n * readable during `dragenter`/`dragover` \u2014 which is precisely when the decision\n * to arm a drop zone has to be made. So the source leaf and tab index travel in\n * `TileRenderer._tabDrag` and this type is an admission ticket.\n *\n * Encoding the leaf id into the type NAME (`\u2026/tile-tab/leaf-7`) would make it\n * readable on dragover and is the trap: `types` would then be unbounded, every\n * consumer would have to parse it, and the string would become a wire format\n * nobody declared. One constant, one meaning.\n */\nexport const TILE_TAB_MIME = 'application/x-twm-tile-tab';\n\n/** Does this drag carry a tile tab? The ONLY admission test available during\n * `dragover`, per the note on `TILE_TAB_MIME`. */\nfunction _isTileTabDrag(e) {\n try { return !!e.dataTransfer?.types?.includes(TILE_TAB_MIME); }\n catch { return false; }\n}\n\nexport class TileRenderer {\n /** C33. Readable from a consumer that holds only the renderer \u2014 the Tables\n * drop suite drives real drag events and has to build a `dataTransfer`\n * stub that this renderer will admit. */\n static get TAB_MIME() { return TILE_TAB_MIME; }\n\n constructor({ root, tree, content, ctx, onFocusChange, onAfterRender,\n tabLayout = null }) {\n if (!content || typeof content.mount !== 'function') {\n throw new Error('TileRenderer: a content registry is required');\n }\n this.root = root;\n this.tree = tree;\n // Shell-scoped kind -> factory table (see content_registry.js).\n this.content = content;\n this.ctx = ctx || {};\n this.onFocusChange = onFocusChange || (() => {});\n /** Fired after every repaint, once the tree's DOM is settled.\n *\n * A LEAF'S WRAP IS REBUILT whenever its content key changes \u2014 a tab\n * added, a tab removed, the leaf re-seeded \u2014 and anything an embedder\n * parented INTO that wrap goes with the old one. A window contained in\n * a pane (C21) is exactly that: promote a second tab out of a\n * two-tab leaf and the first promotion's window is silently removed\n * from the document, because its container was the wrap that the\n * second promotion's re-render replaced.\n *\n * The renderer cannot know about windows, so it says \"I have\n * repainted\" and the WM re-homes what it owns. */\n this.onAfterRender = onAfterRender || (() => {});\n // leafId -> { wrapEl, bodyEl, chromeEl, content, kindKey, tabStrip }\n this._leafCache = new Map();\n this._drag = null;\n /** C33. The tab currently being carried: `{leafId, idx, el}`, or null.\n * On the RENDERER rather than in a per-leaf closure \u2014 see the header:\n * the bottom strip's `dragFromIdx` is per leaf render, and a drag that\n * crosses tiles needs an identity leaf B's handlers can read. */\n this._tabDrag = null;\n /** The chrome pull-down's live pointer \u2014 `{startX, startY, x, y}` \u2014\n * while a press is held on a leaf chrome, else null. Read by the\n * `dragstart` in `_syncChromeDragSource`, which has to decide whether\n * this gesture is the float and get out of its way. Here rather than\n * in the pull's own closure for `_tabDrag`'s reason: the reader is a\n * method bound to a different element. */\n this._chromePull = null;\n /** The last `wm.tabDropProbe` answer, stashed on `dragover` because\n * `drop` must not re-probe: the pointer can be one pixel outside the\n * zone the preview drew, and the rectangle drawn is the rectangle the\n * drop delivers (C15). */\n this._tabDropProbe = null;\n this._tabDropPreviewEl = null;\n /** Set by `createShell().dispose()`. A renderer whose shell is gone must\n * not paint: an embedder that rebuilds its shell over the same root\n * would otherwise find a stale callback repainting the previous tree\n * on top of the live one, and the two would fight over one element. */\n this.disposed = false;\n this.root.classList.add('twm-root');\n // C22. The ATTRIBUTE WINS over the config when it is already set, and\n // that order is the point: an embedder that persists this per user has\n // its answer before `createShell` is called, on the element it owns,\n // and does not have to race the shell's construction to apply it.\n this.tabLayout = _normalizeTabLayout(\n this.root.dataset?.twmTabs ?? tabLayout ?? DEFAULT_TAB_LAYOUT);\n if (this.root.dataset) this.root.dataset.twmTabs = this.tabLayout;\n // \u2026and stays live. `setTabLayout` writes the attribute back, so this\n // fires once with nothing to do rather than looping.\n this._layoutObserver = typeof MutationObserver === 'function'\n ? new MutationObserver(() => this.setTabLayout(this.root.dataset?.twmTabs))\n : null;\n this._layoutObserver?.observe(this.root,\n { attributes: true, attributeFilter: ['data-twm-tabs'] });\n this.root.addEventListener('mousedown', this._onMouseDown.bind(this));\n // C33. ON THE ROOT, IN THE CONSTRUCTOR, FOR THE SAME REASON THE\n // `mousedown` ABOVE IS. `render()` clears the root, so a listener bound\n // to a leaf wrap is a listener that survives until the next repaint \u2014\n // and a drag is exactly the situation in which repaints happen.\n this.root.addEventListener('dragover', this._onTabDragOver.bind(this));\n this.root.addEventListener('drop', this._onTabDrop.bind(this));\n this.root.addEventListener('dragleave', this._onTabDragLeave.bind(this));\n // `dragend` FIRES ON THE SOURCE AND IS THE ONLY GUARANTEED END. A drag\n // cancelled with Escape, or released over a window that refused it,\n // produces `dragend` and no `drop` at all \u2014 so every scrap of painted\n // state is torn down here rather than in the drop.\n this.root.addEventListener('dragend', this._onTabDragEnd.bind(this));\n }\n\n render() {\n if (this.disposed) return;\n // Detaching a wrap (remove() then re-append() in `_mount`) wipes\n // `scrollTop` on every scrollable descendant of that wrap.\n // Snapshot per-leaf so we can restore after re-attach \u2014 keyed\n // by element refs that survive the detach.\n const savedScrolls = new Map(); // leafId -> [{el, top}, \u2026]\n for (const [leafId, entry] of this._leafCache.entries()) {\n const snap = [];\n entry.wrapEl.querySelectorAll('*').forEach((el) => {\n if (el.scrollTop > 0 || el.scrollLeft > 0) {\n snap.push({ el, top: el.scrollTop, left: el.scrollLeft });\n }\n });\n savedScrolls.set(leafId, snap);\n entry.wrapEl.remove();\n }\n // R13. A WINDOW PARENTED TO THE ROOT IS NOT A TILE, AND IS NOT THE\n // RENDERER'S TO THROW AWAY. `innerHTML = ''` below is indiscriminate \u2014\n // it destroys every direct child \u2014 and the window manager parents\n // floating windows here deliberately: FOR THE LENGTH OF EVERY DRAG\n // (`dragHost`, R1), which is the live route and not a rare one, since a\n // repaint during a drag needs nothing more exotic than the drag itself\n // changing focus or a splitter moving. It was also permanent for a\n // window snapped to fill the layer; R14 removed that mode \u2014 the top\n // edge docks now \u2014 but the drag remains, and a consumer calling the\n // public `toggleMaximize({claimable: false})` on a window the WM has\n // left on its drag host puts one up here for good. Any repaint while a\n // window was up here deleted its element silently: no error, nothing\n // thrown, and a `ManagedWindow` object still perfectly alive with\n // nothing on screen \u2014 the same shape of bug as the leaf wraps above,\n // which is why they are detached and re-attached too rather than merely\n // skipped. The tiles have to be rebuilt around them.\n const floats = [...this.root.children].filter((el) =>\n el.classList?.contains('twm-managed-window')\n || el.classList?.contains('twm-managed-window__backdrop'));\n for (const el of floats) el.remove();\n this.root.innerHTML = '';\n const tree = this.tree;\n if (!tree.rootId) {\n const empty = document.createElement('div');\n empty.className = 'twm-empty';\n empty.textContent = 'Empty desktop \u2014 Ctrl+K to open something.';\n this.root.appendChild(empty);\n this._cleanCache(new Set());\n // An empty desktop is still a desktop a window can be floating\n // over, and it is the one branch that returns early.\n for (const el of floats) this.root.appendChild(el);\n return;\n }\n const liveLeafIds = new Set();\n this._mount(tree.rootId, this.root, liveLeafIds);\n this._cleanCache(liveLeafIds);\n // LAST, and before `onAfterRender` \u2014 which is where the WM re-homes\n // the contained ones into their rebuilt wraps, and it can only move an\n // element that is still in the document.\n for (const el of floats) this.root.appendChild(el);\n this._updateFocusClasses();\n\n // Restore scrolls \u2014 once synchronously and once next frame so\n // any layout reflow between detach/reattach doesn't leave the\n // browser's clamped value in place.\n const restore = () => {\n for (const [leafId, snap] of savedScrolls) {\n if (!this._leafCache.has(leafId)) continue;\n for (const { el, top, left } of snap) {\n if (el.isConnected) {\n el.scrollTop = top;\n el.scrollLeft = left;\n }\n }\n }\n };\n restore();\n requestAnimationFrame(restore);\n try { this.onAfterRender(); }\n catch (err) { console.error('[tile-renderer] onAfterRender threw', err); }\n }\n\n /** Unmount everything and stop painting. `dispose()` on the shell calls\n * this: `root.innerHTML = ''` detaches DOM without telling a single content\n * factory, so a page module that installed a `window` listener or an\n * interval keeps both, invisibly, for the life of the tab. */\n destroy() {\n this._layoutObserver?.disconnect();\n this._layoutObserver = null;\n // C33. The drop preview is parented to `document.body`, not to the\n // root, so `_cleanCache` cannot reach it: a shell torn down mid-drag\n // would leave a blue rectangle painted over the page with nothing left\n // that knows how to remove it.\n this._clearTabDropPreview();\n this._tabDrag = null;\n this._tabDropProbe = null;\n this._cleanCache(new Set());\n this.disposed = true;\n }\n\n _cleanCache(liveSet) {\n for (const [leafId, entry] of [...this._leafCache.entries()]) {\n if (!liveSet.has(leafId)) {\n try { entry.content?.destroy?.(); } catch (_) {}\n // C22. The top strip is a COMPONENT, not markup: it holds a\n // DragReorder with container listeners and a context menu\n // parented to `document.body`. Dropping the element it lives in\n // leaves both, invisibly, for the life of the page \u2014 the same\n // reason `destroy()` exists on the content above it.\n this._disposeTabStrip(entry);\n entry.wrapEl.remove();\n this._leafCache.delete(leafId);\n }\n }\n }\n\n _mount(nodeId, parentEl, liveSet) {\n const node = this.tree.get(nodeId);\n if (!node) return;\n if (node.kind === 'leaf') {\n liveSet.add(node.id);\n const el = this._leafEl(node);\n parentEl.appendChild(el);\n return;\n }\n // Split node \u2014 flex container with N children + N-1 splitters.\n const split = document.createElement('div');\n split.className = `twm-split twm-split--${node.dir}`;\n split.dataset.splitId = node.id;\n parentEl.appendChild(split);\n const total = node.sizes.reduce((a, b) => a + b, 0) || node.children.length;\n node.children.forEach((cid, i) => {\n const slot = document.createElement('div');\n slot.className = 'twm-slot';\n const frac = (node.sizes[i] || 1) / total;\n slot.style.flex = `${frac} ${frac} 0`;\n split.appendChild(slot);\n this._mount(cid, slot, liveSet);\n if (i < node.children.length - 1) {\n const splitter = document.createElement('div');\n splitter.className = `twm-splitter twm-splitter--${node.dir}`;\n splitter.dataset.splitId = node.id;\n splitter.dataset.slotIdx = String(i);\n split.appendChild(splitter);\n }\n });\n }\n\n _leafEl(leaf) {\n // Tab strip key is part of the cache key so the strip's chrome\n // (which tabs exist, which one is active) re-renders when tabs\n // mutate, but the active tab's body itself stays cached when the\n // user clicks back to a tab they already visited.\n const activeTab = (Array.isArray(leaf.tabs) && leaf.tabs.length > 0)\n ? leaf.tabs[Math.max(0, Math.min(leaf.tabs.length - 1, leaf.activeTabIdx || 0))]\n : null;\n const kindKey = this._leafKindKey(leaf);\n\n let entry = this._leafCache.get(leaf.id);\n if (entry && entry.kindKey === kindKey) {\n entry.titleEl.textContent = leaf.title || (leaf.content ? leaf.content.kind : 'empty');\n // C19. Re-read the glyph beside the title, and for the same reason:\n // a cached leaf is NOT re-mounted, so anything the chrome shows\n // that can change while the content stays put has to be refreshed\n // here or it is frozen at whatever it was when the tile was built.\n _paintLeafIcon(entry.iconEl, leaf, entry.content, this.ctx);\n return entry.wrapEl;\n }\n // Build fresh.\n if (entry) {\n // DELETED, NOT JUST SUPERSEDED. The map still held this entry while\n // the content's own `destroy` ran, so anything reaching the\n // renderer from inside that teardown \u2014 `leafChrome`, and\n // `rebaselineLeaf`, which would have written a key onto a wrap\n // already removed from the document \u2014 read a corpse. The rebuild\n // re-registers under the same id a few lines below, so the only\n // window this closes is the one nothing should be looking in.\n this._leafCache.delete(leaf.id);\n try { entry.content?.destroy?.(); } catch (_) {}\n this._disposeTabStrip(entry);\n entry.wrapEl.remove();\n }\n const wrap = document.createElement('div');\n wrap.className = 'twm-leaf';\n wrap.dataset.leafId = leaf.id;\n // No tabIndex on the wrap \u2014 that would capture focus on click\n // and break the `hostEl.contains(document.activeElement)` guard\n // many tab modules use to scope their keyboard handlers. The\n // body is given tabIndex=-1 below so we can focus the body\n // (which IS the hostEl seen by the mounted tab content) on\n // tile activation.\n const chrome = document.createElement('div');\n chrome.className = 'twm-leaf__chrome';\n // C19. The glyph a floating window has always had, in the tile chrome\n // too \u2014 `ManagedWindow._build` puts one left of its title\n // (`../ui/components/managed_window.js:588-590`) and a tile, which is\n // the SAME content in a different state, had nothing there. Two ways of\n // looking at one table should not disagree about what a table looks\n // like.\n const icon = document.createElement('span');\n icon.className = 'twm-leaf__icon material-symbols-outlined';\n const title = document.createElement('span');\n title.className = 'twm-leaf__title';\n title.textContent = leaf.title || (leaf.content ? leaf.content.kind : 'empty');\n const actions = document.createElement('span');\n actions.className = 'twm-leaf__actions';\n // C18. Content-contributed chrome actions land HERE, to the left of the\n // structural ones, and the divider is not decoration: the buttons on\n // the right act on the TILE (split it, float it, close it) and the ones\n // on the left act on what is IN it (this table's settings, this table\n // in a browser window). Two kinds of verb in one strip with nothing\n // between them is how \"close\" gets read as \"close the table\".\n const contentActions = document.createElement('span');\n contentActions.className = 'twm-leaf__actions twm-leaf__actions--content';\n // Panel tiles (left nav / right / bottom) can't be promoted to\n // managed windows \u2014 they're chrome, not content. Hide the\n // promote button in their chrome.\n const isPanel = String(leaf.content?.kind || '').startsWith('panel:');\n // Split + promote are structural actions on a content tile; panels\n // (left nav / right / bottom) are chrome, not content, so they only\n // get the close affordance.\n actions.innerHTML = `\n ${isPanel ? '' : `\n <button class=\"twm-leaf__btn\" data-action=\"split-h\" title=\"Split horizontally (Alt+H)\">\n <span class=\"material-symbols-outlined\">splitscreen_vertical_add</span>\n </button>\n <button class=\"twm-leaf__btn\" data-action=\"split-v\" title=\"Split vertically (Alt+V)\">\n <span class=\"material-symbols-outlined\">splitscreen_add</span>\n </button>\n <button class=\"twm-leaf__btn\" data-action=\"promote\" title=\"Float this pane as a window (Alt+F)\">\n <span class=\"material-symbols-outlined\">web_asset</span>\n </button>`}\n <button class=\"twm-leaf__btn\" data-action=\"close\" title=\"Close (Alt+W)\">\n <span class=\"material-symbols-outlined\">close</span>\n </button>\n `;\n chrome.append(icon, title, contentActions, actions);\n // Painted BEFORE the mount as well as after it, so a kind whose factory\n // supplies no icon of its own \u2014 and every kind that existed before this\n // did \u2014 still wears the taxonomy's glyph from the first frame rather\n // than acquiring one a mount later.\n _paintLeafIcon(icon, leaf, null, this.ctx);\n const body = document.createElement('div');\n body.className = 'twm-leaf__body';\n body.tabIndex = -1; // focusable via JS so document.activeElement\n // lands inside the body when the tile is\n // activated (keyboard-handler scope check).\n // The tab strip \u2014 only rendered when the leaf carries more than one\n // tab. It is built as part of the leaf element, not of the body, so it\n // stays pinned at its edge of the tile regardless of body scroll.\n const tabBar = document.createElement('div');\n tabBar.className = 'twm-leaf__tabbar';\n if (!Array.isArray(leaf.tabs) || leaf.tabs.length <= 1) {\n tabBar.classList.add('twm-leaf__tabbar--hidden');\n }\n // C22. CHROME FIRST IN BOTH LAYOUTS. The chrome is the pane's identity\n // and the tabs are what is inside it; a strip above the title would say\n // the tabs own the pane rather than the other way round.\n wrap.append(chrome, ...(this.tabLayout === 'top' ? [tabBar, body]\n : [body, tabBar]));\n\n wrap.addEventListener('mousedown', (e) => {\n if (e.target.closest('.twm-splitter')) return;\n this.tree.focus(leaf.id);\n this._updateFocusClasses();\n this.onFocusChange(leaf.id);\n // Park focus on the deepest focus host inside the body so\n // page modules whose keydown scope is\n // `hostEl.contains(document.activeElement)` actually see\n // the activation. The page-shell wrapper, when present,\n // is what tab modules treat as `hostEl`; falling back to\n // the leaf body itself is fine for panel tiles.\n const tgt = e.target;\n const focusable = tgt.closest?.(\n 'input, textarea, select, button, a, [contenteditable=\"true\"], [tabindex]');\n if (!focusable || !body.contains(focusable)) {\n setTimeout(() => {\n const ps = body.querySelector('.twm-page-shell__content')\n ?? body;\n if (ps.tabIndex == null || ps.tabIndex < -1) ps.tabIndex = -1;\n ps.focus({ preventScroll: true });\n }, 0);\n }\n });\n // \u2500\u2500 PULL THE HEADER DOWN TO FLOAT THE PANE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n //\n // The inverse of the gesture everyone already knows: a maximised window\n // is un-maximised by dragging its titlebar away from the edge, and a\n // tile is a pane maximised into its slot. So dragging a tile's chrome\n // DOWNWARD lifts it out as a floating window \u2014 the same thing the\n // promote button does, reached the way a window manager teaches you to\n // reach it.\n //\n // Downward only, and past a real threshold. A tile's chrome is also\n // where you click to focus the pane, and a click carries a few pixels\n // of movement with it; anything less than a deliberate pull is a click.\n // Sideways is left alone: it means nothing here, and claiming it would\n // make the strip feel like it grabs the pointer.\n chrome.addEventListener('pointerdown', (e) => {\n if (e.button !== 0) return;\n if (e.target.closest(CHROME_NO_FLOAT)) return;\n const startY = e.clientY;\n const startX = e.clientX;\n // PUBLISHED ON THE RENDERER, for `_syncChromeDragSource` to read.\n //\n // The native drag beats the pull to the pointer and there is no\n // threshold at which it does not: Chromium starts one after about\n // 5px of travel while the pull needs 24, so the ONLY moment the\n // direction of this gesture can still be acted on is inside the\n // `dragstart` the drag announces itself with. That handler has no\n // other way to learn it \u2014 a `dragstart`'s own `clientX`/`clientY`\n // read 0 for a drag begun this way (measured, Edge 152 /\n // Chromium 152), so the event cannot answer the question being\n // asked of it and this record has to.\n //\n // On the renderer rather than in this closure for `_tabDrag`'s\n // reason: the reader is a method bound to a different element.\n const pull = { startX, startY, x: startX, y: startY, lifted: false };\n this._chromePull = pull;\n const onMove = (move) => {\n pull.x = move.clientX;\n pull.y = move.clientY;\n if (pull.lifted) return;\n const down = move.clientY - startY;\n const sideways = Math.abs(move.clientX - startX);\n if (down < TILE_LIFT_PX || !_isDownwardPull(down, sideways)) return;\n pull.lifted = true;\n // ONLY `pointermove` COMES OFF HERE, AND THE RECORD STAYS \u2014\n // `cleanup()` would take it, and taking it is a defect.\n //\n // A `pointermove` can be COALESCED: one event may carry the\n // whole 24px and promote on its first delivery, and Blink\n // hands that move to script BEFORE starting the drag it\n // crossed the threshold for \u2014 so a `dragstart` still arrives\n // afterwards, and `_syncChromeDragSource` has to be able to\n // read the gesture to refuse it. Without the record it did\n // not, and a tab drag began on the pane that had just floated\n // out of the tile. `pointerup` below is what clears it.\n window.removeEventListener('pointermove', onMove);\n this.ctx.onLeafAction?.(leaf.id, 'promote');\n };\n const cleanup = () => {\n if (this._chromePull === pull) this._chromePull = null;\n window.removeEventListener('pointermove', onMove);\n window.removeEventListener('pointerup', cleanup);\n window.removeEventListener('pointercancel', cleanup);\n };\n window.addEventListener('pointermove', onMove);\n window.addEventListener('pointerup', cleanup);\n window.addEventListener('pointercancel', cleanup);\n });\n\n // \u2500\u2500 \u2026OR DOUBLE-CLICK IT, WHICH IS THE GESTURE THAT ALREADY HAD A\n // RETURN JOURNEY AND NO OUTWARD ONE \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 // Double-clicking a FLOATING window's title bar docks it back into a\n // tile \u2014 `managed_window.js:919` binds it to `toggleMaximize()`, whose\n // claimed verb under this WM is `bringBackWindow` (\"maximise means back\n // to tile\", R7 \u2014 see the `onMaximize` `wm._promote` builds). So the\n // half of the toggle that PUTS a\n // pane back was spelled as a double-click on a header, and the half\n // that takes it out was not: a user who learned the docking gesture had\n // to reach for the float button, or discover the pull above, to undo\n // what a double-click had just done. One gesture, one strip, both\n // directions.\n //\n // The pull and this are two doors onto ONE verb, not two verbs. Both go\n // through `onLeafAction(leaf.id, 'promote')`, so the WM's guards apply\n // to both without knowing there are two \u2014 a panel tile and a window\n // placeholder are refused in `wm._floatableLeaf` and a\n // second double-click on an already-floated pane finds a re-seeded\n // start tile rather than the content, which is the tree telling the\n // truth rather than a case to special-case here.\n //\n // NATIVE `dblclick`, NOT A HAND-ROLLED CLICK COUNT \u2014 and that is a\n // claim about the handlers above, not a preference. C28\n // (`managed_window.js:1305`) is the case that says why: its pointerdown\n // re-parented the window element out of the document before the pointer\n // had moved, Blink and Gecko drop the pending `click` when the pressed\n // element leaves the document, and the title bar's `dblclick` therefore\n // never fired once in a real browser while looking perfectly correct in\n // jsdom. Nothing on this strip does that. The wrap's `mousedown`\n // toggles a class and calls `onFocusChange`, which the WM turns into\n // `wm._notifyChange` \u2014 a callback and two bus emits, no repaint; its\n // deferred `body.focus()` moves focus and moves\n // nothing else, and focus does not reset a click count. The pull's\n // `pointerdown` only registers window listeners and neither\n // `preventDefault`s nor captures the pointer. The chrome the first\n // click lands on is still in the document, still the same element, when\n // the second one lands.\n //\n // No `e.button` test, unlike the pull: `dblclick` is dispatched for the\n // primary button only. No `preventDefault`, and NO NEW CSS: the strip\n // is already `user-select: none` (`css/overrides.css:1001`, mirrored\n // into the generated `css/flexdesk.css:4743`), so a double-click here\n // cannot word-select the title on its way to floating the pane.\n chrome.addEventListener('dblclick', (e) => {\n if (e.target.closest(CHROME_NO_FLOAT)) return;\n this.ctx.onLeafAction?.(leaf.id, 'promote');\n });\n\n // Right-click on the chrome \u2192 tile context menu (split, close,\n // promote, move to desktop). Listening on `chrome` only so the\n // tile body keeps its own contextmenu (e.g. DataTable's).\n chrome.addEventListener('contextmenu', (e) => {\n e.preventDefault();\n this.tree.focus(leaf.id);\n this._updateFocusClasses();\n this.onFocusChange(leaf.id);\n this.ctx.onTileContextMenu?.(leaf.id, e.clientX, e.clientY);\n });\n actions.addEventListener('click', (e) => {\n const btn = e.target.closest('button');\n if (!btn) return;\n e.stopPropagation();\n this.ctx.onLeafAction?.(leaf.id, btn.dataset.action);\n });\n\n // Mount content. The per-leaf ctx carries `leafId` so the\n // mounted content can route subsequent navigation (e.g.\n // `workspaceTabs.openTab(...)`) back into its own tile via\n // `wm.openFromContext(ctx, ...)` instead of always hitting\n // the primary tile.\n let content = {};\n if (leaf.content) {\n const leafCtx = { ...this.ctx, leafId: leaf.id };\n // Mount with the ACTIVE TAB's props, not `content.props`.\n // `wm.updateActiveTabProps` (the canonical per-tile view-state\n // store \u2014 survives reload + desktop switches via desktops.json)\n // writes only into `tabs[active].props` and deliberately leaves\n // `content.props` untouched so the cache key (kindKey) stays\n // stable and a tab-prop change doesn't churn-rebuild the leaf.\n // The trade-off: `content.props` goes stale, so a leaf rebuilt\n // after a desktop round-trip must read the fresh tab props here.\n const activeProps = activeTab?.props ?? leaf.content.props;\n content = this.content.mount(leaf.content.kind, body, activeProps, leafCtx);\n if (content.title) title.textContent = content.title;\n // C19. `icon` is the natural sibling of `title` and is read the\n // same way: AFTER the mount, because that is the first moment the\n // content knows what it is \u2014 a canvas pane you emptied is the same\n // KIND as the overview and wants a different glyph.\n _paintLeafIcon(icon, leaf, content, this.ctx);\n // C18. A content factory may return `chromeActions`:\n // `[{icon, title, onClick}]`. Read AFTER the mount, because that is\n // the first moment the content knows what it can offer \u2014 a table\n // does not know it has settings until it has a definition.\n _paintContentActions(contentActions, content.chromeActions);\n // C20. A content factory may also VETO structural verbs:\n // `chrome: { promote: false, close: false }`. Read after the mount\n // for the same reason `chromeActions` is \u2014 it is the first moment\n // the content knows what it is.\n //\n // The case that forced it: an embedder's MASTER tile. It is the\n // ground floating windows stand on and the thing you are left with\n // when everything else is closed, so \"float this pane as a window\"\n // and \"close this pane\" are offers to destroy the only surface the\n // application has. The renderer already made this exact judgement\n // for `panel:*` kinds by hardcoding it; this is that judgement,\n // available to anyone.\n _vetoStructuralActions(actions, content.chrome);\n } else {\n body.innerHTML = `<div class=\"tile-placeholder\"><div class=\"tile-placeholder__hint\">empty tile</div></div>`;\n }\n entry = { wrapEl: wrap, bodyEl: body, chromeEl: chrome,\n titleEl: title, iconEl: icon, content,\n // DERIVED HERE, NOT REUSED FROM ABOVE. `kindKey` was computed\n // before the mount; content that records its sub-tab or its\n // scroll offset WHILE mounting has already written to the\n // tree by now, so the value captured earlier is stale and the\n // very next repaint would tear down the tile that had just\n // said where it was.\n kindKey: this._leafKindKey(leaf), tabBarEl: tabBar,\n // C22. The `top` layout's NotebookTabBar, and the child it\n // mounts into. Null under the `bottom` layout, which is plain\n // markup this file writes itself.\n tabStrip: null, tabStripHostEl: null };\n this._leafCache.set(leaf.id, entry);\n\n // Render tab strip last so it has access to the cached entry.\n this._renderTabBar(leaf, entry);\n return wrap;\n }\n\n /**\n * The cache key for a leaf: everything that must change before its wrap is\n * torn down and rebuilt. Extracted so `rebaselineLeaf` below computes the\n * same string this does \u2014 two spellings of one key is a cache that misses\n * on every render or never misses at all, and both look like working code.\n */\n _leafKindKey(leaf) {\n const tabFingerprint = (leaf.tabs || []).map((t) =>\n `${t.kind}::${JSON.stringify(t.props || {})}`).join('|') + `#${leaf.activeTabIdx || 0}`;\n return leaf.content\n ? `${leaf.content.kind}::${JSON.stringify(leaf.content.props || {})}::tabs:${tabFingerprint}`\n : '__empty__';\n }\n\n /**\n * The cache key a leaf has RIGHT NOW, for a caller that is about to change\n * the tree and wants to say what it expected to be changing from. See\n * `rebaselineLeaf`.\n */\n leafKey(leafId) {\n const leaf = this.tree?.get?.(leafId);\n return (leaf && leaf.kind === 'leaf') ? this._leafKindKey(leaf) : null;\n }\n\n /**\n * C34. ACCEPT A PROPS WRITE THE CONTENT MADE ABOUT ITSELF, WITHOUT\n * REBUILDING THE TILE THAT MADE IT.\n *\n * The cache key above includes every tab's props, and that is right for\n * NAVIGATION: a `table` tab whose `id` changes is different content and the\n * tile must be re-mounted. It is exactly wrong for VIEW STATE. The\n * framework hands every tile a `workspaceTabs.updateProps(patch)`\n * (`page_factory.js`) documented as *\"persist editor sub-state into this\n * tile's WM tab props\"* \u2014 a scroll offset, an open section, a selected\n * sub-tab. Writing one changed the fingerprint, so the NEXT repaint (a\n * focus change, a tab switch, a window promoted three tiles away) missed\n * the cache, destroyed the content and mounted it again. The tile was torn\n * down BY the call that existed to let it remember something, and because\n * the rebuild reads the props back the result looked almost right \u2014 the\n * editor came back at the saved scroll position, with everything uncommitted\n * in it gone.\n *\n * So the key is re-baselined instead: the entry keeps its live DOM and\n * starts answering to the new props. The next real navigation still misses\n * and still rebuilds, because that changes the kind or the tab set and this\n * only ever accepts what is already on screen.\n *\n * \u2550\u2550 IT ACCEPTS ONLY THE DELTA IT WAS CALLED FOR \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * The key is re-derived from the tree as it is NOW, so a first version of\n * this swallowed every difference at once \u2014 including a change the tree had\n * taken and the renderer had not drawn yet. Any mutation that does not\n * repaint (`updateActiveTabProps` is itself one, and an embedder writing\n * through `TileTree` directly is another) followed by a props write would\n * have been accepted onto a wrap still showing the OLD content, and the\n * tile would never have re-mounted: one thing drawn under another's title,\n * permanently, with nothing left that knows the two disagree.\n *\n * `expected` is the fix and it is the caller's own honesty: the key it read\n * BEFORE its write. If the cached entry is not still at that key, something\n * else has changed since the tile was mounted and this is not the caller's\n * to accept \u2014 refuse, and let the ordinary miss rebuild it.\n *\n * A structural test was tried first and is not enough. *Same kind, different\n * props* is a scroll offset AND it is a navigation to another table; nothing\n * in the leaf can tell them apart, because the difference is which caller\n * asked. `expected` asks the caller.\n *\n * @param {string} leafId\n * @param {string} [expected] the key the caller read before its own write.\n * Omitted means \"accept whatever is there\", which is what the first\n * version did and is kept only so an older caller does not silently\n * change behaviour \u2014 every caller in this tree passes one.\n * @returns {boolean} whether a cached wrap was re-baselined\n */\n rebaselineLeaf(leafId, expected = undefined) {\n const entry = this._leafCache.get(leafId);\n const leaf = this.tree?.get?.(leafId);\n if (!entry || !leaf || leaf.kind !== 'leaf') return false;\n if (expected !== undefined && entry.kindKey !== expected) return false;\n entry.kindKey = this._leafKindKey(leaf);\n return true;\n }\n\n /**\n * C22. Change the tab layout of a LIVE renderer.\n *\n * Only the strip is rebuilt. The tab bar element is MOVED between its two\n * positions and the body is never detached, so no content factory is\n * unmounted \u2014 which is the whole reason this is a method rather than\n * \"rebuild the shell with the other option\". A grid holding staged edits\n * must not lose them because someone changed where its tabs are drawn.\n *\n * Accepts anything: the value arrives from a DOM attribute and from\n * persisted user settings, and an unknown one means the default.\n */\n setTabLayout(layout) {\n const next = _normalizeTabLayout(layout);\n // The attribute is written back below, which re-enters through the\n // observer; this is where that stops.\n if (next === this.tabLayout) return;\n this.tabLayout = next;\n if (this.root.dataset && this.root.dataset.twmTabs !== next) {\n this.root.dataset.twmTabs = next;\n }\n if (this.disposed) return;\n for (const [leafId, entry] of this._leafCache) {\n this._placeTabBar(entry);\n // The two strips are different components, not two skins of one:\n // whichever was there is torn down and the other is built.\n this._disposeTabStrip(entry);\n entry.tabBarEl.innerHTML = '';\n const leaf = this.tree.get(leafId);\n if (leaf) this._renderTabBar(leaf, entry);\n }\n }\n\n /** Put a leaf's tab bar on the side the current layout says. Moving an\n * attached element is a re-parent, not a rebuild \u2014 the body keeps its\n * DOM, its listeners and its scroll. */\n _placeTabBar(entry) {\n const { wrapEl, bodyEl, tabBarEl } = entry;\n if (!wrapEl || !tabBarEl || !bodyEl) return;\n if (this.tabLayout === 'top') wrapEl.insertBefore(tabBarEl, bodyEl);\n else wrapEl.appendChild(tabBarEl);\n }\n\n /** Tear down the `top` layout's component, if this leaf has one. Safe to\n * call on a leaf that never had one, and on one that already lost it. */\n _disposeTabStrip(entry) {\n if (!entry?.tabStrip) return;\n try { entry.tabStrip.dispose(); } catch (err) {\n console.error('[tile] tab strip dispose threw', err);\n }\n entry.tabStrip = null;\n entry.tabStripHostEl = null;\n }\n\n /** Paint a leaf's tab strip in whichever layout is current. The strip is\n * hidden for single-tab leaves in BOTH layouts \u2014 the common case, and the\n * reason existing single-pane layouts read as identical to today. */\n _renderTabBar(leaf, entry) {\n // C33. BEFORE the single-tab early return, because the single-tab leaf\n // is exactly the case this answers: no strip is drawn, so the chrome is\n // the only thing left to grab.\n this._syncChromeDragSource(leaf, entry);\n const bar = entry.tabBarEl;\n if (!bar) return;\n const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];\n bar.classList.toggle('twm-leaf__tabbar--top', this.tabLayout === 'top');\n if (tabs.length <= 1) {\n bar.classList.add('twm-leaf__tabbar--hidden');\n this._disposeTabStrip(entry);\n bar.innerHTML = '';\n return;\n }\n bar.classList.remove('twm-leaf__tabbar--hidden');\n if (this.tabLayout === 'top') this._renderTopTabBar(leaf, entry, tabs);\n else this._renderBottomTabBar(leaf, entry, tabs);\n }\n\n /**\n * C33. THE CHROME IS THE DRAG SOURCE FOR A LEAF THAT HAS ONE TAB.\n *\n * Product owner, 2026-08-27. The full argument is in the file header; what\n * this function owns is the THREE conditions and why each is a condition\n * rather than a preference:\n *\n * EXACTLY ONE TAB. With two or more, the strip is drawn and names each\n * tab; a chrome drag would then have to guess which one was meant, and\n * guessing is what the strip exists to avoid. With exactly one, \"this\n * tab\" and \"what is in this pane\" are the same thing.\n *\n * NOT A PANEL. Panel tiles are chrome, not content \u2014 the same exclusion\n * `_floatableLeaf` and `_snapProbe` already make, and for the same reason:\n * there is nothing in them that belongs anywhere else.\n *\n * THE CONTENT DID NOT VETO `promote` (C20). One rule instead of two: a\n * tab you may not lift out of its pane is a tab you may not drag into\n * another one. This is what excludes an embedder's master tile \u2014 the\n * ground its floating windows stand on \u2014 whose whole reason for existing\n * is that it stays where it is.\n *\n * THE LISTENERS ARE BOUND ONCE PER CHROME ELEMENT and the ATTRIBUTE is\n * re-decided on every pass. That split is deliberate: `draggable` changes\n * the moment a second tab arrives, while the chrome element itself survives\n * for as long as its wrap does, and re-adding a listener on every render\n * would stack one per repaint.\n */\n _syncChromeDragSource(leaf, entry) {\n const chrome = entry?.chromeEl;\n if (!chrome) return;\n const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];\n const isPanel = String(leaf.content?.kind || '').startsWith('panel:');\n const vetoed = this.leafChrome(leaf.id)?.promote === false;\n const on = tabs.length === 1 && !isPanel && !vetoed;\n if (on) chrome.setAttribute('draggable', 'true');\n else chrome.removeAttribute('draggable');\n if (chrome.__twmTabDragBound) return;\n chrome.__twmTabDragBound = true;\n chrome.addEventListener('dragstart', (ev) => {\n // A drag that began on a BUTTON is not a drag of the pane. Buttons\n // are not draggable on their own, but a draggable ancestor makes\n // them so \u2014 hence the same guard the two float gestures apply, from\n // the same constant, so a control added to this strip later becomes\n // inert under all three at once.\n if (ev.target?.closest?.(CHROME_NO_FLOAT)) { ev.preventDefault(); return; }\n // \u2550\u2550 A DOWNWARD GESTURE IS THE PULL, AND IT HAS TO BE REFUSED\n // HERE, BECAUSE NOTHING LATER CAN \u2550\u2550\u2550\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 // The file header used to claim these two gestures could not\n // collide: *\"the pull \u2026 loses cleanly \u2026 The pull is also still\n // reachable, on every part of the chrome a single-tab leaf's title\n // does not cover.\"* That describes `draggable` on the TITLE. It is\n // set on the CHROME, four lines above \u2014 the whole strip \u2014 so the\n // pull was not merely losing where the title sits, it had no\n // reachable surface left on any single-tab tile. And below two tabs\n // the strip is hidden, so single-tab is the common case: the\n // reported symptom is *\"pulling down a tile at title bar does not\n // anymore turn it into a managed window\"*, and it was every tile.\n //\n // MEASURED, against the running application in Edge 152 rather\n // than in jsdom, which cannot start a native drag and therefore\n // cannot see any of this: press the chrome and pull down 140px and\n // the sequence is `dragstart`, `pointercancel`, `dragend`, with\n // zero windows promoted. The drag arrives at ~5-16px of travel and\n // `pointercancel` tears the pull's listeners down at ~8, so the\n // 24px threshold is never reached and never can be.\n //\n // Refusing the drag here is what hands the pointer back: a\n // `dragstart` that is cancelled starts no drag, so no\n // `pointercancel` is fired and `pointermove` simply resumes \u2014\n // measured, same harness, the pull then fires at 26px. That is\n // also the house idiom for declining a native drag\n // (`DragReorder._start`'s `draggableGuard`, `drag_reorder.js`).\n //\n // C33 IS UNTOUCHED BY IT. A sideways or diagonal grab reports\n // `down <= sideways`, is not refused, and drags exactly as before\n // \u2014 verified in the same browser run. What this costs is the one\n // gesture that was ambiguous by construction: you can no longer\n // start a tab drag by pulling straight DOWN out of the chrome,\n // which is the direction the float has claimed since before the\n // tab drag existed.\n //\n // `_chromePull` OUTLIVES THE PROMOTE, and that is load-bearing\n // rather than tidy: a coalesced `pointermove` can carry the whole\n // 24px and promote on its first delivery, and Blink hands that\n // move to script BEFORE starting the drag it crossed the threshold\n // for \u2014 so a `dragstart` still arrives afterwards. Clearing the\n // record on lift left this read with nothing, so the drag was not\n // refused and a tab drag began on the pane that had just floated\n // out of the tile. The record's last coordinates are the lift's,\n // which are downward by definition, so the same test answers.\n const pull = this._chromePull;\n if (pull && _isDownwardPull(pull.y - pull.startY,\n Math.abs(pull.x - pull.startX))) {\n ev.preventDefault();\n return;\n }\n // Re-read the count rather than trusting the attribute: the tree\n // can gain a tab between a render and a gesture, and a stale\n // `draggable` would carry tab 0 out of a pane that now has three.\n const live = this.tree.get(leaf.id);\n if ((live?.tabs || []).length !== 1) { ev.preventDefault(); return; }\n this._beginTabDrag(ev, leaf.id, 0, chrome, { seedPlainText: true });\n });\n chrome.addEventListener('dragend', () => this._onTabDragEnd());\n }\n\n /**\n * C22. The `top` layout: `NotebookTabBar`, the editor tab strip.\n *\n * The component is mounted into a CHILD of the bar rather than into the bar\n * itself, because `mount()` assigns `container.className = 'tabs\n * notebook-tabs'` (`notebook_tab_bar.js:61`) \u2014 handing it `.twm-leaf__tabbar`\n * would take that class, and with it the strip's height, its background and\n * `--hidden`, off the element this file still controls.\n *\n * Every gesture routes through the SAME `ctx.onLeafTabAction` vocabulary the\n * bottom strip uses, so the WM's tree mutations, its persistence and its\n * change notifications are reached by one path from both layouts.\n */\n _renderTopTabBar(leaf, entry, tabs) {\n const bar = entry.tabBarEl;\n if (!entry.tabStrip) {\n bar.innerHTML = '';\n const host = document.createElement('div');\n bar.appendChild(host);\n const strip = new NotebookTabBar();\n strip.mount(host, this._topTabCallbacks(leaf.id));\n entry.tabStrip = strip;\n entry.tabStripHostEl = host;\n // ONE TAB CONTEXT MENU IN THE APPLICATION, not two.\n //\n // `NotebookTabBar` opens its own `.nb-context-menu` on right-click,\n // whose verbs are a notebook's (rename, duplicate, reveal in\n // explorer) and whose close verbs duplicate \u2014 in a different\n // typeface \u2014 the menu the WM already builds for the bottom strip\n // (`wm._showTabContextMenu`). A user who switches layout must not\n // discover that right-clicking a tab now means something else.\n //\n // CAPTURE phase, on the container: the component binds its handler\n // on each tab element in the bubble phase, so stopping the event on\n // the way DOWN is what keeps its menu from opening at all. There is\n // no callback for this \u2014 the menu is not a callback, it is the\n // component's own DOM.\n host.addEventListener('contextmenu', (ev) => {\n const tabEl = ev.target.closest?.('.tab');\n if (!tabEl) return;\n ev.preventDefault();\n ev.stopPropagation();\n const idx = this._topTabIndex(host, tabEl);\n if (idx < 0) return;\n this.ctx.onLeafTabAction?.(leaf.id, 'menu',\n { idx, x: ev.clientX, y: ev.clientY });\n }, true);\n }\n const activeIdx = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));\n entry.tabStrip.update(\n tabs.map((t, i) => ({\n filePath: _tabKey(i),\n label: t.title || t.kind || '',\n // The component's `fileType` picks a glyph out of a static map\n // of FILE kinds. A tile's kinds are the embedder's, and the\n // taxonomy already answers for them \u2014 see `_paintTopTabs`.\n fileType: t.kind || 'unknown',\n // Nothing sets `dirty` on a tab spec today, so the dot is never\n // drawn. Read anyway, because the day a tile can say it holds\n // unsaved work this is where it says it, and the alternative is\n // a second place to remember.\n isDirty: !!t.dirty,\n })),\n _tabKey(activeIdx));\n this._paintTopTabs(entry.tabStripHostEl, tabs);\n }\n\n /**\n * The two things `NotebookTabBar` derives from a vocabulary a tile does not\n * have, corrected in one pass over the DOM it just wrote.\n *\n * Its tooltip is the file PATH (`notebook_tab_bar.js:163`) and its glyph\n * comes from a static fileType map (`:135`). Ours are `builtin://tab/3` and\n * a content kind, so left alone a tab would advertise its own array index\n * and wear the generic `description` glyph \u2014 while the tile chrome an inch\n * above it shows the taxonomy's icon for exactly the same kind.\n *\n * Reaching into a component's DOM is worth one paragraph of justification.\n * The alternative for the glyph is `NotebookTabBar.setFileTypeIcons()`,\n * which is STATIC and REPLACES the whole map \u2014 so a page that also uses the\n * editor would find its own file icons deleted by whichever of the two\n * rendered last. The class names used here are the component's published\n * contract, stated in its header comment.\n */\n _paintTopTabs(hostEl, tabs) {\n if (!hostEl) return;\n const els = hostEl.querySelectorAll('.tab');\n els.forEach((el, i) => {\n const spec = tabs[i];\n if (!spec) return;\n el.title = spec.title || spec.kind || '';\n const icon = spec.kind ? this.ctx?.taxonomy?.meta?.(spec.kind)?.icon : null;\n const glyph = el.querySelector('.tab-icon');\n if (glyph && icon) glyph.textContent = icon;\n else if (glyph && !icon) glyph.hidden = true;\n });\n }\n\n /** Which tab an element in the top strip is, by DOM position. Position\n * rather than the `data-path` key because the key is only ever the index\n * and reading it back would be a second, parallel answer to the same\n * question. */\n _topTabIndex(hostEl, tabEl) {\n return Array.prototype.indexOf.call(hostEl.querySelectorAll('.tab'), tabEl);\n }\n\n /** The callbacks `NotebookTabBar` calls. Everything the component offers\n * that a tile tab cannot honour is deliberately absent rather than stubbed\n * \u2014 `onRename` is refused by the `builtin://` key, and the rest\n * (`onDuplicate`, `onSplitRight`, `onRevealInExplorer`, `onCloseAll`, \u2026)\n * are only ever reached from the context menu this renderer suppresses.\n *\n * `onDropFromOtherPane` IS NOW WIRED, AND THIS IS THE RECORD OF WHY IT WAS\n * NOT. The refusal read: *\"its payload is the dragged tab's key alone,\n * which carries no source-leaf identity, and `TileTree` has no\n * move-a-tab-between-leaves operation to receive it. Wiring it would need\n * both, and both are tree changes.\"* Both were true and C33 built both.\n * `TileTree.moveTabToLeaf` is the tree operation; `TileRenderer._tabDrag`\n * is the source identity \u2014 held on the renderer rather than in the payload\n * because HTML5's protected mode makes the payload unreadable at the only\n * moment it would be needed (see `TILE_TAB_MIME`). The payload handed to\n * the callback is therefore still ignored, exactly as the refusal said it\n * would have to be.\n *\n * A DROP ON A FOREIGN STRIP APPENDS. `NotebookTabBar` hands the callback a\n * key and no event, so there is no pointer position to derive a slot from\n * \u2014 and inventing one for this strip and not for the bottom one would give\n * the two layouts different answers to the same gesture. \"Add this tab to\n * that tile\" is what was asked for; where it sits in the strip is a\n * reorder away, in the mechanism that already does reorders. */\n _topTabCallbacks(leafId) {\n return {\n // C33. `DragReorder` calls this through `NotebookTabBar`, which\n // sets its own `application/x-ecosim-tab` first and unchanged \u2014 so\n // an editor pane sharing the page cannot notice that tiles are\n // dragging tabs too.\n onDragStart: (ev, key, item) => {\n const idx = _tabKeyIndex(key);\n if (idx < 0) return;\n this._beginTabDrag(ev, leafId, idx, item || null);\n },\n // What lets THIS strip admit a tab dragged out of another tile's\n // strip: `#isExternalTabDrag` tests `TAB_MIME` plus whatever the\n // host names here, and defers while its own reorder is running.\n externalTabMimes: [TILE_TAB_MIME],\n onDropFromOtherPane: () => {\n const src = this._tabDrag;\n this._onTabDragEnd();\n if (!src || src.leafId === leafId) return;\n this.ctx.onLeafTabAction?.(src.leafId, 'drop-into', {\n idx: src.idx,\n target: { leafId, mode: 'tab', toIdx: -1 },\n });\n },\n onActivate: (key) => {\n const idx = _tabKeyIndex(key);\n if (idx >= 0) this.ctx.onLeafTabAction?.(leafId, 'switch', { idx });\n },\n onClose: (key) => {\n const idx = _tabKeyIndex(key);\n if (idx >= 0) this.ctx.onLeafTabAction?.(leafId, 'close', { idx });\n },\n // DRAG-TO-REORDER ARRIVES AS A PERMUTATION, and the tree moves ONE\n // tab at a time (`TileTree.moveLeafTab(leafId, from, to)`). They\n // reconcile because a drag only ever moves one element: every other\n // key shifts by exactly one place, so the element that travelled\n // furthest between the two orders IS the one that was dragged.\n onReorder: (order) => {\n const leaf = this.tree.get(leafId);\n const count = (leaf?.tabs || []).length;\n if (!Array.isArray(order) || order.length !== count) return;\n let from = -1;\n let to = -1;\n let furthest = 0;\n order.forEach((key, newIdx) => {\n const oldIdx = _tabKeyIndex(key);\n if (oldIdx < 0) return;\n const travelled = Math.abs(newIdx - oldIdx);\n if (travelled > furthest) {\n furthest = travelled;\n from = oldIdx;\n to = newIdx;\n }\n });\n if (from < 0 || from === to) return;\n this.ctx.onLeafTabAction?.(leafId, 'move', { from, to });\n },\n };\n }\n\n /** The `bottom` layout \u2014 the framework's own strip, unchanged. Hamburger\n * button at the start, then one trapezoid-shaped tab per stored tab spec. */\n _renderBottomTabBar(leaf, entry, tabs) {\n const bar = entry.tabBarEl;\n const activeIdx = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));\n bar.innerHTML = `\n <button type=\"button\" class=\"twm-leaf__tab-hamburger\"\n data-action=\"tab-menu\"\n title=\"Show open tabs\" aria-label=\"Show open tabs\">\n <span class=\"material-symbols-outlined\">menu</span>\n </button>\n <ol class=\"twm-leaf__tabs\" role=\"tablist\">\n ${tabs.map((t, i) => `\n <li class=\"twm-leaf__tab${i === activeIdx ? ' twm-leaf__tab--on' : ''}\"\n role=\"tab\" data-tab-idx=\"${i}\"\n title=\"${_esc(t.title || t.kind)}\"\n draggable=\"true\">\n <span class=\"twm-leaf__tab-label\">${_esc(t.title || t.kind)}</span>\n <button type=\"button\" class=\"twm-leaf__tab-close\"\n data-action=\"tab-close\" data-tab-idx=\"${i}\"\n title=\"Close this tab\"\n aria-label=\"Close tab\">\n <span class=\"material-symbols-outlined\">close</span>\n </button>\n </li>\n `).join('')}\n </ol>\n `;\n\n // Tab activation. Click anywhere on a tab (except its \u00D7 button)\n // switches to it; the \u00D7 closes it.\n bar.querySelectorAll('.twm-leaf__tab').forEach((li) => {\n li.addEventListener('click', (ev) => {\n if (ev.target.closest('[data-action=\"tab-close\"]')) return;\n const idx = Number(li.dataset.tabIdx);\n this.ctx.onLeafTabAction?.(leaf.id, 'switch', { idx });\n });\n // Right-click \u2192 browser-style tab context menu (close, close\n // others, close right / left). The WM mounts the actual menu\n // \u2014 we just hand it the (idx, x, y) anchor.\n li.addEventListener('contextmenu', (ev) => {\n ev.preventDefault();\n ev.stopPropagation();\n const idx = Number(li.dataset.tabIdx);\n this.ctx.onLeafTabAction?.(leaf.id, 'menu',\n { idx, x: ev.clientX, y: ev.clientY });\n });\n });\n bar.querySelectorAll('[data-action=\"tab-close\"]').forEach((btn) => {\n btn.addEventListener('click', (ev) => {\n ev.preventDefault();\n ev.stopPropagation();\n const idx = Number(btn.dataset.tabIdx);\n this.ctx.onLeafTabAction?.(leaf.id, 'close', { idx });\n });\n });\n bar.querySelector('[data-action=\"tab-menu\"]')\n ?.addEventListener('click', (ev) => {\n ev.preventDefault();\n const r = ev.currentTarget.getBoundingClientRect();\n this.ctx.onLeafTabAction?.(leaf.id, 'open-menu',\n { x: r.left, y: r.top });\n });\n\n // Drag-to-reorder. Stash the source idx in dataTransfer; the\n // drop target reads it and emits a 'move' action. We don't use\n // HTML5 setData('text/plain') for the live state because the\n // dragstart/drop pair runs entirely within the same tab list.\n //\n // C33. \u2026WHICH IS EXACTLY WHY `dragFromIdx` IS NOT ENOUGH ANY MORE. It\n // is a closure built per leaf render, so leaf B's handlers cannot see\n // that leaf A started a drag. It stays, unchanged, as the in-strip\n // reorder's own state \u2014 the gesture it was written for still begins and\n // ends in one list \u2014 and the cross-tile identity is taken on the\n // renderer beside it. Two facts, two homes, and neither one guessing.\n let dragFromIdx = null;\n bar.querySelectorAll('.twm-leaf__tab').forEach((li) => {\n li.addEventListener('dragstart', (ev) => {\n dragFromIdx = Number(li.dataset.tabIdx);\n ev.dataTransfer.effectAllowed = 'move';\n // Firefox refuses to start a drag unless setData is\n // called \u2014 supply a placeholder string.\n try { ev.dataTransfer.setData('text/plain', String(dragFromIdx)); }\n catch {}\n this._beginTabDrag(ev, leaf.id, dragFromIdx, li);\n });\n // C33. THE DRAGGED TAB NOW LOOKS DRAGGED. `DragReorder` has put\n // `.dragging` on the top strip's tab since it was written; this\n // strip's hand-rolled reorder set no class at all, so a drag here\n // looked exactly like a click that did nothing until the tab\n // arrived somewhere else. One rule serves both strips.\n li.addEventListener('dragend', () => { dragFromIdx = null; this._onTabDragEnd(); });\n li.addEventListener('dragover', (ev) => {\n if (dragFromIdx == null) return;\n ev.preventDefault();\n ev.dataTransfer.dropEffect = 'move';\n });\n li.addEventListener('drop', (ev) => {\n if (dragFromIdx == null) return;\n ev.preventDefault();\n const toIdx = Number(li.dataset.tabIdx);\n if (toIdx !== dragFromIdx) {\n this.ctx.onLeafTabAction?.(leaf.id, 'move',\n { from: dragFromIdx, to: toIdx });\n }\n dragFromIdx = null;\n });\n });\n\n // \u2500\u2500 C33. AND A TAB FROM ANOTHER TILE, DROPPED ON THIS STRIP \u2500\u2500\u2500\u2500\u2500\u2500\n //\n // The container level, not the tab level, because the useful target is\n // \"this strip\" rather than \"this tab\": the per-tab handlers above\n // return without `preventDefault` when `dragFromIdx` is null, which is\n // always true of a foreign drag, so the event reaches here by bubbling\n // and the two never contend.\n //\n // The gate is the same shape as `NotebookTabBar.#isExternalTabDrag`:\n // the type-only marker, plus a source that is not this leaf. The\n // `dragleave` is guarded with `contains(relatedTarget)` because\n // `dragleave` fires every time the pointer crosses into a CHILD, and\n // without the guard the strip would unhighlight itself the moment the\n // pointer touched a tab inside it \u2014 the same fix `editor_pane.js` and\n // `notebook_tab_bar.js` already carry.\n // BOUND ONCE PER BAR ELEMENT, not once per render. `_renderBottomTabBar`\n // rewrites `bar.innerHTML` \u2014 which takes the per-tab listeners with the\n // `<li>`s they were on \u2014 but the BAR survives, and `setTabLayout` calls\n // straight back in on the same element. Without this flag a user who\n // switched layout twice would get three copies of the drop handler and\n // three `drop-into` actions from one release.\n const foreign = (ev) => !!this._tabDrag\n && this._tabDrag.leafId !== leaf.id\n && _isTileTabDrag(ev);\n if (bar.__twmBarDropBound) return;\n bar.__twmBarDropBound = true;\n bar.addEventListener('dragover', (ev) => {\n if (!foreign(ev)) return;\n ev.preventDefault();\n try { ev.dataTransfer.dropEffect = 'move'; } catch {}\n // The tile zone and the strip zone must never be armed at once, and\n // this is the TILE zone that goes \u2014 never `_clearTabDropZone`,\n // which would take the class added on the next line straight back\n // off. See `_onTabDragOver`: this handler runs FIRST (the bar is a\n // descendant of the root, and both listeners are on the bubble\n // phase), so anything it paints has to survive what runs after it.\n this._clearTileDropZone();\n bar.classList.add('twm-leaf__tabbar--drop-target');\n });\n bar.addEventListener('dragleave', (ev) => {\n if (!bar.contains(ev.relatedTarget)) {\n bar.classList.remove('twm-leaf__tabbar--drop-target');\n }\n });\n bar.addEventListener('drop', (ev) => {\n if (!foreign(ev)) return;\n ev.preventDefault();\n bar.classList.remove('twm-leaf__tabbar--drop-target');\n const src = this._tabDrag;\n this._onTabDragEnd();\n // APPEND, like the top strip's drop does, and for the same reason:\n // one gesture must not mean two things depending on where the user\n // draws their tabs. See `_topTabCallbacks`.\n this.ctx.onLeafTabAction?.(src.leafId, 'drop-into', {\n idx: src.idx,\n target: { leafId: leaf.id, mode: 'tab', toIdx: -1 },\n });\n });\n }\n\n // \u2550\u2550 C33. THE TAB DRAG \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 /** Take the identity of the tab now being carried, and mark it.\n *\n * `seedPlainText` is for the CHROME source only. The two strips already\n * set `text/plain` themselves \u2014 `DragReorder._start` writes the reorder\n * key, the bottom strip writes the index \u2014 and overwriting either would\n * hand `NotebookTabBar.#onStripDrop`'s `getData(TAB_MIME) ||\n * getData('text/plain')` fallback a number where it expects a key. The\n * chrome has no such writer and Firefox refuses to begin a drag with an\n * empty `dataTransfer`, so it supplies its own. */\n _beginTabDrag(ev, leafId, idx, el, { seedPlainText = false } = {}) {\n this._tabDrag = { leafId, idx, el: el || null };\n this._tabDropProbe = null;\n try {\n ev.dataTransfer.effectAllowed = 'move';\n ev.dataTransfer.setData(TILE_TAB_MIME, '1');\n if (seedPlainText) ev.dataTransfer.setData('text/plain', String(idx));\n } catch { /* a synthetic event, or a dataTransfer in protected mode */ }\n el?.classList?.add('dragging');\n }\n\n /**\n * Arm \u2014 or refuse \u2014 the tile under the pointer.\n *\n * NOTHING HERE MAY RENDER. `render()` clears the root, which detaches the\n * element the browser is dragging, and the browser cancels the gesture the\n * moment that happens. The DOM afterwards reads perfectly correct, which is\n * what makes this failure so hard to see; it is the drag-and-drop cousin of\n * the `mousedown`-repaint defect recorded five times against the taskbar.\n *\n * `preventDefault()` is not decoration either: without it the browser\n * refuses the drop outright and `drop` never fires, which reads exactly\n * like a broken handler.\n */\n _onTabDragOver(e) {\n const src = this._tabDrag;\n if (!src || !_isTileTabDrag(e)) return;\n // A STRIP OWNS ITS OWN DROPS. Inside a tab bar the question is a\n // position in a list, not a zone of a tile, and both strips answer it\n // themselves \u2014 so this hands the event on rather than arming a zone\n // behind them.\n //\n // `_clearTileDropZone` and NOT `_clearTabDropZone`: the strip's handler\n // has ALREADY RUN by the time this one does \u2014 it is bound on a\n // descendant of the root and both are bubble-phase \u2014 so clearing the\n // strip classes here would remove the highlight it just painted, on\n // every `dragover`, and the strip would arm and disarm invisibly for\n // the whole gesture.\n if (e.target?.closest?.('.twm-leaf__tabbar')) { this._clearTileDropZone(); return; }\n const probe = this.ctx.wm?.tabDropProbe?.(e, { sourceLeafId: src.leafId }) || null;\n this._tabDropProbe = probe;\n if (!probe) { this._clearTabDropZone(); return; }\n e.preventDefault();\n try { e.dataTransfer.dropEffect = 'move'; } catch {}\n for (const [leafId, entry] of this._leafCache) {\n entry.wrapEl.classList.toggle('twm-leaf--drop-target', leafId === probe.leafId);\n }\n this._showTabDropPreview(probe.rect);\n }\n\n /** Release. The zone that was ARMED is the zone that runs \u2014 the stashed\n * probe rather than a fresh one \u2014 because C15's rule is that the rectangle\n * drawn during the drag is the rectangle the drop delivers, and a pointer\n * one pixel outside the band at release must not quietly mean something\n * else. */\n _onTabDrop(e) {\n const src = this._tabDrag;\n const probe = this._tabDropProbe;\n if (!src || !_isTileTabDrag(e)) return;\n if (e.target?.closest?.('.twm-leaf__tabbar')) return;\n e.preventDefault();\n this._onTabDragEnd();\n if (!probe) return;\n this.ctx.onLeafTabAction?.(src.leafId, 'drop-into', {\n idx: src.idx,\n target: {\n leafId: probe.leafId,\n mode: probe.mode,\n // The same derivation `_snapCommit` applies to a window drop\n // (`wm.js`, the `_dock` literal) \u2014 one reading of a side, so a\n // tab and a window cannot land on opposite halves of one edge.\n dir: (probe.side === 'left' || probe.side === 'right') ? 'h' : 'v',\n before: (probe.side === 'left' || probe.side === 'top'),\n toIdx: -1,\n },\n });\n }\n\n /** Leaving the root entirely disarms, and only that. The drag is still\n * live \u2014 it may come back \u2014 so `_tabDrag` survives and only the painting\n * goes. */\n _onTabDragLeave(e) {\n if (!this._tabDrag) return;\n if (this.root.contains(e.relatedTarget)) return;\n this._clearTabDropZone();\n this._tabDropProbe = null;\n }\n\n /** The only guaranteed end of a drag. Escape produces this and no `drop`;\n * so does a release over a target that refused. Idempotent, because the\n * drop path calls it too and `dragend` still arrives afterwards. */\n _onTabDragEnd() {\n this._tabDrag?.el?.classList?.remove('dragging');\n this._tabDrag = null;\n this._tabDropProbe = null;\n this._clearTabDropZone();\n }\n\n /** The TILE zone only \u2014 the outlined pane and the preview rectangle. Split\n * out from the whole because a strip that has just armed itself must not\n * be disarmed by the root handler running behind it. */\n _clearTileDropZone() {\n for (const [, entry] of this._leafCache) {\n entry.wrapEl.classList.remove('twm-leaf--drop-target');\n }\n this._clearTabDropPreview();\n }\n\n /** Everything: the tile zone and both strips'. The end of a gesture, where\n * nothing may be left painted. */\n _clearTabDropZone() {\n this._clearTileDropZone();\n for (const [, entry] of this._leafCache) {\n entry.tabBarEl?.classList?.remove('twm-leaf__tabbar--drop-target');\n }\n }\n\n /** The rectangle a release would fill.\n *\n * IT IS THE WINDOW DROP'S OWN PREVIEW ELEMENT \u2014 same two classes, same\n * stylesheet rules (`css/base.css`, `.twm-snap-preview`) \u2014 so a tab drop\n * and a window drop cannot come to disagree about what a drop looks like.\n * `--viewport` is what makes `position: fixed` apply, and that is required\n * rather than cosmetic: the rectangle came from `getBoundingClientRect` on\n * a leaf, which speaks viewport pixels.\n *\n * Parented to `document.body` and not to the root, for R13's reason:\n * `render()`'s `innerHTML = ''` takes every direct child of the root, and\n * a repaint during a drag needs nothing more exotic than the drag itself. */\n _showTabDropPreview(rect) {\n if (!rect) { this._clearTabDropPreview(); return; }\n if (!this._tabDropPreviewEl) {\n const el = document.createElement('div');\n el.className = 'twm-snap-preview twm-snap-preview--viewport';\n el.setAttribute('aria-hidden', 'true');\n this._tabDropPreviewEl = el;\n }\n const el = this._tabDropPreviewEl;\n Object.assign(el.style, {\n left: `${rect.left ?? rect.x}px`, top: `${rect.top ?? rect.y}px`,\n width: `${rect.width}px`, height: `${rect.height}px`,\n });\n if (el.parentNode !== document.body) document.body.appendChild(el);\n }\n\n _clearTabDropPreview() {\n this._tabDropPreviewEl?.remove();\n }\n\n _updateFocusClasses() {\n const focused = this.tree.focusedLeafId;\n for (const [leafId, entry] of this._leafCache) {\n entry.wrapEl.classList.toggle('twm-leaf--focused', leafId === focused);\n }\n }\n\n /** The `.twm-leaf` wrap element for a leaf id, or null if it isn't\n * currently rendered. Used by the panel-key router to resolve which\n * DOM subtree owns keyboard input. */\n leafEl(leafId) {\n return this._leafCache.get(leafId)?.wrapEl || null;\n }\n\n /** C20, extended. The `chrome` veto object the mounted content declared \u2014\n * `{ promote: false, close: false }` \u2014 or null when the leaf is not\n * rendered or its content declared nothing.\n *\n * IT EXISTS BECAUSE A VETO PAINTED ON A BUTTON IS NOT A VETO. C20 landed\n * as `_vetoStructuralActions`, which removes the button from this strip \u2014\n * and a removed button is only the door the CONTENT can see. The verb has\n * three other doors: the tile's right-click menu (`shell.js`'s \"Float this\n * pane as a window\"), the chrome pull-down, and the chrome's double-click.\n * All three reach `WindowManager.floatPane` without passing this file, so\n * a master tile that declared itself unfloatable was floated by any of\n * them \u2014 reproduced: the ground pane floats, every window standing on it\n * is force-closed, and the pane is re-seeded WITHOUT its `canvas` prop.\n *\n * So the WM asks the renderer what the content said, and enforces it in\n * `_floatableLeaf` where every door already converges. The renderer stays\n * the only place that knows what was mounted; the WM stays the only place\n * that decides whether a verb runs. `closeFocused` now reads `close` the\n * same way, for the same reason and after the same defect: Alt+W, the tile\n * context menu and the tab strip's \u00D7 all closed a pane whose own button\n * was greyed out with a tooltip saying it could not be.\n *\n * \u2550\u2550 THIS RETURNS THE FACTORY'S LIVE OBJECT, AND THAT IS A CONTRACT \u2550\u2550\n *\n * Not a copy and not a snapshot. `chrome` is READ ONCE, at mount \u2014 a\n * repaint of a cached leaf re-reads only the title and the glyph \u2014 so a\n * veto whose ANSWER CHANGES over the life of the tile must be kept up to\n * date by the content that stated it, by mutating the object it returned.\n * Tables' ground pane is exactly that case: its close is refused only\n * while it is the last content pane, and it re-syncs on `wm:changed`.\n * Repainting the button alone is not enough now that a verb consults this\n * \u2014 a stale `{disabled: true}` refuses a close every affordance on screen\n * says is available, which is the same class of lie as a dead control. */\n leafChrome(leafId) {\n return this._leafCache.get(leafId)?.content?.chrome || null;\n }\n\n // \u2500\u2500 Drag-resize \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 _onMouseDown(e) {\n const splitter = e.target.closest('.twm-splitter');\n if (!splitter) return;\n e.preventDefault();\n const splitId = splitter.dataset.splitId;\n const slotIdx = Number(splitter.dataset.slotIdx);\n const split = this.tree.get(splitId);\n if (!split) return;\n const containerEl = splitter.parentElement;\n const horizontal = split.dir === 'h';\n const rect = containerEl.getBoundingClientRect();\n const totalPx = horizontal ? rect.width : rect.height;\n this._drag = {\n splitId, slotIdx, horizontal, totalPx,\n startSizes: split.sizes.slice(),\n startPos: horizontal ? e.clientX : e.clientY,\n split,\n };\n document.body.classList.add('twm-dragging');\n const move = (ev) => this._onMouseMove(ev);\n const up = (ev) => {\n this._onMouseUp(ev);\n document.removeEventListener('mousemove', move);\n document.removeEventListener('mouseup', up);\n };\n document.addEventListener('mousemove', move);\n document.addEventListener('mouseup', up);\n }\n\n _onMouseMove(e) {\n if (!this._drag) return;\n const { horizontal, totalPx, startSizes, startPos, slotIdx, split } = this._drag;\n const delta = (horizontal ? e.clientX : e.clientY) - startPos;\n const totalSize = startSizes.reduce((a, b) => a + b, 0);\n const px = totalPx - SPLITTER_PX * (split.children.length - 1);\n if (px <= 0) return;\n const deltaFrac = (delta / px) * totalSize;\n const minFrac = totalSize * 0.05;\n const newSizes = startSizes.slice();\n newSizes[slotIdx] = Math.max(minFrac, startSizes[slotIdx] + deltaFrac);\n newSizes[slotIdx + 1] = Math.max(minFrac, startSizes[slotIdx + 1] - deltaFrac);\n // Snap back to total to avoid drift.\n const sum = newSizes.reduce((a, b) => a + b, 0);\n const scale = totalSize / sum;\n for (let i = 0; i < newSizes.length; i++) newSizes[i] *= scale;\n this.tree.setSplitSizes(split.id, newSizes);\n // Cheap update: just rewrite flex on the slots.\n const containerEl = document.querySelector(`.twm-split[data-split-id=\"${split.id}\"]`);\n if (!containerEl) return;\n const slots = containerEl.querySelectorAll(':scope > .twm-slot');\n slots.forEach((slot, i) => {\n const frac = newSizes[i] / totalSize;\n slot.style.flex = `${frac} ${frac} 0`;\n });\n }\n\n _onMouseUp() {\n this._drag = null;\n document.body.classList.remove('twm-dragging');\n }\n}\n\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n}\n\n/**\n * C19. Decide, and paint, the glyph left of a tile's title.\n *\n * TWO SOURCES, IN THIS ORDER, and the order is the whole design. The content\n * factory's `content.icon` wins because only the content knows what this\n * PARTICULAR thing is \u2014 one table has a cog, the next has a calendar, and the\n * taxonomy has no opinion about either. Everything else falls back to\n * `taxonomy.meta(kind).icon`, which is the glyph the top-nav, the command\n * palette and the breadcrumb already draw for that kind \u2014 so an embedder who\n * changes nothing at all still gets a tile chrome that agrees with the three\n * surfaces around it, and an embedder who has never returned an `icon` from a\n * factory gets one for free.\n *\n * A kind with neither \u2014 a `panel:*` tile, whose kinds are the framework's own\n * and are deliberately not in anybody's taxonomy \u2014 is HIDDEN rather than blank.\n * The chrome is a flex row with a gap; an empty span would still take its gap\n * and push the title off the left margin by a glyph's worth of nothing.\n */\nfunction _paintLeafIcon(iconEl, leaf, content, ctx) {\n if (!iconEl) return;\n const kind = leaf?.content?.kind;\n const name = content?.icon || (kind ? ctx?.taxonomy?.meta?.(kind)?.icon : null);\n iconEl.textContent = name || '';\n iconEl.hidden = !name;\n}\n\n/**\n * C18. Paint a content factory's own chrome buttons into its tile's strip.\n *\n * The framework owns the tile and its verbs; the embedder owns the content and\n * ITS verbs, and until now had nowhere to put them. Tables ended up hanging a\n * table's settings and its pop-out off the grid's own toolbar, one row further\n * down, where they read as things you do to the ROWS.\n *\n * Deliberately not a registry and not a config: the factory returns them from\n * the same object it already returns `title` and `destroy` on, so they arrive\n * with the mount and leave with it.\n */\nfunction _paintContentActions(hostEl, specs) {\n hostEl.innerHTML = '';\n if (!Array.isArray(specs) || specs.length === 0) {\n hostEl.hidden = true;\n return;\n }\n hostEl.hidden = false;\n for (const spec of specs) {\n if (!spec || !spec.icon) continue;\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'twm-leaf__btn';\n btn.title = spec.title || '';\n btn.setAttribute('aria-label', spec.title || '');\n btn.innerHTML =\n `<span class=\"material-symbols-outlined\">${spec.icon}</span>`;\n // `stopPropagation` so a click does not also reach the leaf's own\n // mousedown, which focuses the tile and then parks focus inside the\n // body \u2014 stealing it back from whatever the action just opened.\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n try { spec.onClick?.(); } catch (err) { console.error('[tile] action threw', err); }\n });\n hostEl.appendChild(btn);\n }\n}\n\n/**\n * C20. Let a content factory say a structural verb does not apply to it.\n *\n * Three answers, and the difference between the last two is a real one:\n *\n * (absent) / true the verb applies. The default, so an embedder\n * that says nothing gets every button as before.\n * `false` the verb is MEANINGLESS here \u2014 remove it. A\n * panel tile cannot be floated; there is nothing to\n * explain, and a permanently dead control is\n * clutter that teaches nothing.\n * `{ disabled, title }` the verb applies to this KIND of pane but not\n * right now. Keep the button, grey it, and say\n * why \u2014 \"you may close a pane, but not the last\n * one\" is a rule the user should be able to\n * discover by pointing at the thing it governs,\n * rather than by noticing that a button they\n * remember is missing.\n */\nfunction _vetoStructuralActions(hostEl, chrome) {\n if (!chrome) return;\n for (const [action, rule] of Object.entries(chrome)) {\n const btn = hostEl.querySelector(`[data-action=\"${action}\"]`);\n if (!btn) continue;\n if (rule === false) { btn.remove(); continue; }\n if (rule && typeof rule === 'object' && rule.disabled) {\n btn.disabled = true;\n btn.setAttribute('aria-disabled', 'true');\n btn.classList.add('twm-leaf__btn--disabled');\n if (rule.title) btn.title = rule.title;\n }\n }\n}\n", "/**\n * tab_strip.js \u2014 ONE tab strip, over one ordered list of tab specs.\n *\n * \u2500\u2500 Why this is a module and not a method \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 * C22 taught `tile_renderer.js` to draw a leaf's tabs with `NotebookTabBar`,\n * the editor's strip, and that code is not really about leaves: it is about\n * translating between a component whose vocabulary is FILE PATHS and a list of\n * `{kind, props, title}` that has no identity of its own. R8 needs exactly that\n * translation a second time \u2014 a pane floated into a window takes its tab strip\n * with it, because the pane IS its tabs and a window showing only one of them\n * is not the pane \u2014 and the strip in the window is not attached to a leaf at\n * all. Its tabs live in the WM's window record.\n *\n * So the translation moves here, where neither side owns it, and both sides\n * reach it the same way. A second implementation of it in `wm.js` would be a\n * second copy of the four corrections below, and they would drift apart\n * silently: none of them is a syntax error and none throws at import.\n *\n * \u2500\u2500 The four corrections, and why each one exists \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 * 1. IDENTITY. `NotebookTabBar` keys everything \u2014 its reorder, its active\n * comparison, its rename selector \u2014 on a file path. A tab list has no ids\n * (the tree gives tabs none and neither does a window record), so the\n * INDEX serves, and it is the one identity such a list is guaranteed to\n * have. The `builtin://` prefix is not decoration: `#beginInlineRename`\n * refuses to start on a path carrying it (`notebook_tab_bar.js:402`),\n * which is what a tab needs \u2014 double-click rename would rename a TAB, and\n * a tab is a view of an entity whose name lives somewhere neither caller\n * can reach.\n * 2. THE GLYPH. The component picks one out of a static map of FILE kinds.\n * Ours are the embedder's kinds and the taxonomy already answers for them.\n * `setFileTypeIcons()` is not the way: it is STATIC and REPLACES the whole\n * map, so a page that also uses the editor would find its own file icons\n * deleted by whichever of the two rendered last.\n * 3. THE TOOLTIP. Its title attribute is the file PATH \u2014 ours would read\n * `builtin://tab/3`, i.e. a tab advertising its own array index.\n * 4. ONE TAB CONTEXT MENU IN THE APPLICATION, not two. The component opens\n * its own `.nb-context-menu` whose verbs are a notebook's (rename,\n * duplicate, reveal in explorer). The caller has a menu already; the\n * component's is suppressed in the CAPTURE phase, because its handler is\n * bound on each tab element in the bubble phase and stopping the event on\n * the way DOWN is what keeps its menu from opening at all.\n *\n * The class names touched here are the component's published contract, stated\n * in its own header comment.\n */\n\nimport { NotebookTabBar } from '../editor/notebook_tab_bar.js';\n\n/** A tab's identity, for a component that only understands paths. See (1). */\nexport function tabKey(idx) { return `builtin://tab/${idx}`; }\n\n/** \u2026and back. `-1` for anything that is not one of ours. */\nexport function tabKeyIndex(key) {\n const n = Number(String(key ?? '').replace('builtin://tab/', ''));\n return Number.isInteger(n) && n >= 0 ? n : -1;\n}\n\n/**\n * DRAG-TO-REORDER ARRIVES AS A PERMUTATION, and every tab model here moves ONE\n * tab at a time (`TileTree.moveLeafTab(leafId, from, to)`, and the window\n * record's own splice). They reconcile because a drag only ever moves one\n * element: every other key shifts by exactly one place, so the element that\n * travelled furthest between the two orders IS the one that was dragged.\n *\n * @returns {{from: number, to: number}|null} null when the order is not a\n * permutation of `count` tabs, or when nothing actually moved.\n */\nexport function reorderToMove(order, count) {\n if (!Array.isArray(order) || order.length !== count) return null;\n let from = -1;\n let to = -1;\n let furthest = 0;\n order.forEach((key, newIdx) => {\n const oldIdx = tabKeyIndex(key);\n if (oldIdx < 0) return;\n const travelled = Math.abs(newIdx - oldIdx);\n if (travelled > furthest) { furthest = travelled; from = oldIdx; to = newIdx; }\n });\n if (from < 0 || from === to) return null;\n return { from, to };\n}\n\n/**\n * Mount a tab strip into `hostEl` and drive it from a plain tab list.\n *\n * Every gesture leaves through ONE callback with the SAME vocabulary the tile\n * renderer's two layouts already use \u2014 `switch` / `close` / `move` / `menu` \u2014\n * so a caller wires one function and the WM's tree mutations, its persistence\n * and its change notifications stay reachable by one path from every strip in\n * the application.\n *\n * @param {object} cfg\n * @param {HTMLElement} cfg.hostEl the bar element. The strip mounts into a\n * CHILD of it, because `mount()` assigns `container.className = 'tabs\n * notebook-tabs'` (`notebook_tab_bar.js:61`) \u2014 handing it the bar would take\n * that class, and with it the bar's height, background and `--hidden`, off\n * the element the caller still controls.\n * @param {object} [cfg.taxonomy] asked for each kind's glyph. Absent \u21D2 the\n * glyph is hidden rather than wrong.\n * @param {(action: 'switch'|'close'|'move'|'menu', data: object) => void} cfg.onAction\n * @returns {{update: (tabs: object[], activeIdx: number) => void, dispose: () => void}}\n */\nexport function createTabStrip({ hostEl, taxonomy = null, onAction }) {\n const host = document.createElement('div');\n hostEl.appendChild(host);\n const strip = new NotebookTabBar();\n let tabs = [];\n\n strip.mount(host, {\n onActivate: (key) => {\n const idx = tabKeyIndex(key);\n if (idx >= 0) onAction?.('switch', { idx });\n },\n onClose: (key) => {\n const idx = tabKeyIndex(key);\n if (idx >= 0) onAction?.('close', { idx });\n },\n onReorder: (order) => {\n const move = reorderToMove(order, tabs.length);\n if (move) onAction?.('move', move);\n },\n // Everything else the component offers that a tab here cannot honour is\n // deliberately absent rather than stubbed \u2014 `onRename` is refused by the\n // `builtin://` key, and the rest (`onDuplicate`, `onSplitRight`,\n // `onRevealInExplorer`, `onCloseAll`, \u2026) are only ever reached from the\n // context menu suppressed below.\n });\n\n // See (4). Capture phase, on the container.\n const onContextMenu = (ev) => {\n const tabEl = ev.target.closest?.('.tab');\n if (!tabEl) return;\n ev.preventDefault();\n ev.stopPropagation();\n const idx = Array.prototype.indexOf.call(host.querySelectorAll('.tab'), tabEl);\n if (idx < 0) return;\n onAction?.('menu', { idx, x: ev.clientX, y: ev.clientY });\n };\n host.addEventListener('contextmenu', onContextMenu, true);\n\n /** See (2) and (3) \u2014 one pass over the DOM the component just wrote. */\n const repaint = () => {\n host.querySelectorAll('.tab').forEach((el, i) => {\n const spec = tabs[i];\n if (!spec) return;\n el.title = spec.title || spec.kind || '';\n const icon = spec.kind ? taxonomy?.meta?.(spec.kind)?.icon : null;\n const glyph = el.querySelector('.tab-icon');\n if (!glyph) return;\n if (icon) { glyph.textContent = icon; glyph.hidden = false; }\n else glyph.hidden = true;\n });\n };\n\n return {\n /** @param {Array<{kind: string, props?: object, title?: string, dirty?: boolean}>} next */\n update(next, activeIdx) {\n tabs = Array.isArray(next) ? next : [];\n const active = Math.max(0, Math.min(tabs.length - 1, activeIdx || 0));\n strip.update(\n tabs.map((t, i) => ({\n filePath: tabKey(i),\n label: t.title || t.kind || '',\n fileType: t.kind || 'unknown',\n // Nothing sets `dirty` on a tab spec today, so the dot is\n // never drawn. Read anyway, because the day a tab can say it\n // holds unsaved work this is where it says it, and the\n // alternative is a second place to remember.\n isDirty: !!t.dirty,\n })),\n tabKey(active));\n repaint();\n },\n dispose() {\n host.removeEventListener('contextmenu', onContextMenu, true);\n try { strip.dispose(); } catch (err) {\n console.error('[tab-strip] dispose threw', err);\n }\n host.remove();\n },\n };\n}\n", "/**\n * wm.js \u2014 WindowManager facade. Owns the desktop manager, the active\n * tree's renderer, the managed-window stack, and the panel-tile state.\n * Exposes a small imperative API consumed by the palette, keymap, top\n * bar, bottom bar, and nav panel.\n *\n * wm.openInPrimary(kind, props) load a content into the primary tile\n * wm.split(dir) split the focused tile\n * wm.closeFocused() close the focused tile\n * wm.focusDir(dir) / wm.moveFocused(dir)\n * wm.toggleManagedFocused() tile <-> managed window\n * wm.togglePanel(side) left | right | bottom\n * wm.isPanelOpen(side) boolean\n * wm.switchDesktop(idx) / wm.addDesktop() / wm.moveFocusedToDesktop(idx)\n */\n\nimport { DesktopManager, saveDesktops, loadDesktops } from './desktops.js';\nimport { TileRenderer } from './tile_renderer.js';\nimport { makeLeaf } from './tile_tree.js';\nimport { PLACEHOLDER_KIND } from './content_registry.js';\nimport { installPanelKeyRouter } from './panel_keys.js';\nimport { ManagedWindow } from '../ui/components/managed_window.js';\nimport { showContextMenu } from '../ui/components/context_menu.js';\nimport { createTabStrip } from './tab_strip.js';\n\nconst PANEL_KINDS = new Set(['panel:left', 'panel:right', 'panel:bottom']);\n\nconst PANEL_TITLES = {\n left: 'Navigator',\n right: 'Inspector',\n bottom: 'Console',\n};\n\nexport class WindowManager {\n constructor({ rootEl, api, ctx, onChange, eventBus, host, taxonomy, events, content,\n panelDefaults = null, snapPromotion = false,\n promoteInPlace = false, tabLayout = null }) {\n if (!taxonomy) throw new Error('WindowManager: a taxonomy is required');\n if (!content || typeof content.mount !== 'function') {\n throw new Error('WindowManager: a content registry is required '\n + '(createContentRegistry / createShell owns it)');\n }\n this.rootEl = rootEl;\n // The shell-scoped content registry (kind -> factory). NOT a module\n // singleton: two shells on one page must not share a kind table, and\n // nothing may register a kind after the first mount.\n this.content = content;\n this.api = api || null;\n // The host port. `this.api` stays: 20 domain page factories still read\n // `ctx.api` \u2014 that coupling is P3's problem, not the host port's.\n this.host = host || null;\n // The injected ontology (see tiling/kind_taxonomy.js). The WM asks it\n // for Backspace parents and top-nav sections; it names no kind itself.\n this.taxonomy = taxonomy;\n // Bus events the shell reacts to. Absent key \u21D2 no subscription.\n this.events = events || {};\n this.ctx = ctx || {};\n this.eventBus = eventBus || null;\n this.onChange = onChange || (() => {});\n\n // The leaf a fresh or emptied desktop starts with. Comes from the\n // taxonomy's root kind \u2014 NOT a hardcoded 'home'. An embedder with a\n // different ontology used to get an EcoAgent Home tile it had never\n // registered, and therefore the content_registry placeholder.\n this._rootLeaf = () => {\n const kind = this.taxonomy.root;\n return {\n content: { kind, props: {} },\n title: this.taxonomy.meta(kind)?.label || kind,\n };\n };\n // C14. Which panel tiles a fresh desktop opens with. An embedder whose\n // navigator and inspector live outside the WM root passes `false` for\n // them and gets no placeholder tiles it never registered a factory for.\n this.panelDefaults = panelDefaults || null;\n /** C15. Whether a window this WM promoted may be dropped back INTO the\n * tree. Default off: it changes what a drag to an edge does, and an\n * existing embedder upgrading the library must not find its promoted\n * windows behaving differently. */\n this.snapPromotion = !!snapPromotion;\n /** Whether a window promoted out of a tile stays CONFINED to the pane it\n * came from, rather than floating over the whole root. Default off: it\n * changes where a promoted window can be dragged, and an existing\n * embedder upgrading must not find its windows suddenly clipped. */\n this.promoteInPlace = !!promoteInPlace;\n this._snapCtl = null;\n this.desktops = new DesktopManager({\n seed: this._rootLeaf, panelDefaults: this.panelDefaults });\n this.renderer = new TileRenderer({\n root: rootEl,\n tree: this.desktops.active().tree,\n content,\n /** C22. Where a multi-tab leaf draws its tabs \u2014 `'bottom'`\n * (the framework's own spreadsheet strip, and the DEFAULT so no\n * existing embedder's panes rearrange on upgrade) or `'top'`\n * (the editor tab bar, between the chrome and the body).\n * The renderer also mirrors this onto the root as\n * `data-twm-tabs` and watches it, so an embedder can change it\n * live without holding a renderer reference. */\n tabLayout,\n ctx: {\n ...this.ctx,\n wm: this,\n onLeafAction: (leafId, action) => this._leafAction(leafId, action),\n onLeafTabAction: (leafId, action, data) =>\n this._leafTabAction(leafId, action, data),\n },\n onFocusChange: () => this._notifyChange(),\n onAfterRender: () => this._rehomeContainedWindows(),\n });\n this._persistTimer = null;\n // Window-id \u2192 leaf-id mapping so a managed-window demote can\n // restore its original tile slot.\n this._windowToLeaf = new Map();\n\n // Central keyboard router: panels register key handlers and the\n // router dispatches each keydown only to the panel inside the\n // currently focused leaf (see panel_keys.js). One authority for\n // \"which panel owns the keyboard\" instead of every panel guessing\n // from document.activeElement.\n this.panelKeys = installPanelKeyRouter(this);\n\n // Live-update tab titles when entities are renamed. The embedder\n // names the event (`events.entityRenamed`); its per-entity editors\n // fire it on Save. Walks every leaf's `tabs` and `pageTabs` archive\n // so an archived page's tab labels stay accurate too. No event\n // configured \u21D2 no subscription, and tab titles are simply static.\n const renamedEvent = this.events.entityRenamed;\n if (renamedEvent) {\n this.eventBus?.on?.(renamedEvent, ({ kind, entityId, label } = {}) => {\n if (!kind || !entityId || !label) return;\n let touched = false;\n for (const d of this.desktops.desktops) {\n if (d.tree.renameMatchingTabs?.(kind, entityId, label)) {\n touched = true;\n }\n }\n if (touched) {\n this.renderer.render();\n this._persist();\n }\n });\n }\n }\n\n /** C22. Change the tab layout of every tile, live. Delegates to the\n * renderer, which moves each strip rather than rebuilding the tiles \u2014 so\n * nothing mounted in a tile is unmounted and no staged work is lost.\n *\n * Not persisted here: which layout a user prefers is a USER setting, and\n * the WM persists LAYOUT (`desktops`). An embedder that stores it does so\n * under its own key and passes it back as `createShell({ tabLayout })`. */\n setTabLayout(layout) {\n this.renderer.setTabLayout(layout);\n }\n\n /** Emit on bus + call onChange. Use this instead of the bare callback\n * so other surfaces (palette, top-bar toggles, page shortcuts) can\n * subscribe through the existing event system. */\n _notifyChange(reason = null) {\n try { this.onChange(reason); } catch (err) { console.error('[wm] onChange threw', err); }\n try { this.eventBus?.emit?.('wm:changed', { reason, wm: this }); }\n catch (err) { console.warn('[wm] event emit failed', err); }\n // Emit the canonical `workspace:tabs:activated` event for the\n // current primary leaf \u2014 the existing sidebars (ProjectSidebar\n // etc.) already listen for this to highlight the active row.\n try {\n const tree = this._tree();\n const id = tree.primaryLeafId();\n const leaf = id ? tree.get(id) : null;\n const kind = leaf?.content?.kind;\n if (kind && kind !== 'window-placeholder') {\n this.eventBus?.emit?.('workspace:tabs:activated', {\n kind,\n entityId: leaf.content.props?.id ?? null,\n label: leaf.title,\n from: reason || 'wm',\n });\n }\n } catch (_) {}\n }\n\n // \u2500\u2500 Persistence \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 async load() {\n const blob = await loadDesktops(this.host?.state);\n if (blob) {\n this.desktops = DesktopManager.deserialize(blob, {\n seed: this._rootLeaf, panelDefaults: this.panelDefaults });\n this.renderer.tree = this.desktops.active().tree;\n }\n // Normalize: managed windows don't survive a reload, so any\n // window-placeholder leaves restore their original content.\n for (const d of this.desktops.desktops) {\n for (const leaf of d.tree.leaves()) {\n if (leaf.content?.kind === PLACEHOLDER_KIND) {\n const p = leaf.content.props || {};\n if (p.originalKind) {\n d.tree.setLeafContent(leaf.id,\n { kind: p.originalKind, props: p.originalProps || {} },\n p.originalTitle || p.originalKind);\n }\n }\n }\n this._canonicalize(d.tree, d);\n }\n this.renderer.tree = this.desktops.active().tree;\n this.renderer.render();\n this._notifyChange();\n }\n\n _persist() {\n if (this._persistTimer) clearTimeout(this._persistTimer);\n this._persistTimer = setTimeout(async () => {\n this._persistTimer = null;\n await saveDesktops(this.host?.state, this.desktops.serialize());\n }, 500);\n }\n\n // \u2500\u2500 Content loading \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 /** Load content into the primary tile (last-active non-panel leaf).\n /** Walk back one step. Bound to Backspace.\n *\n * Priority order:\n * 1. Active tab's per-tab history stack (browser-style \"back\"):\n * any in-tile navigation (relationships click-through,\n * landing-row open, \"Open\" buttons inside tables) pushed\n * the previous state, and Backspace pops it. This is the\n * canonical user-facing back affordance.\n * 2. When the history is empty (the user landed on this page\n * via top-nav / palette / direct link), fall back to the\n * canonical taxonomy walk: a sub-page goes to its\n * top-nav, a top-nav page goes to Home.\n *\n * Adding a new kind to the injected taxonomy wires the fallback path\n * for Backspace + breadcrumb + top-nav highlight in one shot.\n * Adding a new \"Open from X\" click that should be a back-able\n * navigation just needs to use one of the in-tile routing\n * methods (openFromContext / openInLeaf / navigateActiveTab) \u2014\n * they all record history by default. */\n navigateBack() {\n const tree = this._tree();\n // Prefer the FOCUSED leaf \u2014 that's where the user was just\n // interacting (Open click, breadcrumb step, etc.). Falls back\n // to the primary leaf for keyboard-driven Backspace when no\n // tile is explicitly focused (panels excluded). If the focused\n // leaf is a panel, walk back on the primary content leaf\n // instead (Backspace from the nav panel should still navigate\n // the user's content area).\n const focusedId = tree.focusedLeafId;\n const primaryId = tree.primaryLeafId();\n const focusedLeaf = focusedId ? tree.get(focusedId) : null;\n const focusedKind = focusedLeaf?.content?.kind || '';\n const id = (focusedId && focusedKind\n && !focusedKind.startsWith('panel:')\n && focusedKind !== 'window-placeholder')\n ? focusedId : primaryId;\n if (!id) return;\n // (1) Per-tab history pop, if anything was recorded.\n const prior = tree.popActiveTabHistory?.(id);\n if (prior && prior.kind) {\n // Cross-page back entries carry a `topNav` marker \u2014 pop\n // routes via swapToPage so we land on the correct page.\n const leafNow = tree.get(id);\n const curKind = leafNow?.content?.kind;\n const curTopNav = curKind ? this.taxonomy.topNavFor(curKind) : null;\n if (prior.topNav && curTopNav && prior.topNav !== curTopNav) {\n tree.swapToPage?.(\n id,\n { kind: prior.kind, props: prior.props || {},\n title: prior.title || _tabTitle(prior.kind, prior.props) },\n curTopNav, prior.topNav,\n { recordHistory: false },\n );\n } else {\n tree.replaceActiveTabContent(\n id,\n { kind: prior.kind, props: prior.props || {} },\n prior.title || _tabTitle(prior.kind, prior.props),\n { recordHistory: false },\n );\n }\n tree.focus(id);\n this.renderer.render();\n this._persist();\n this._notifyChange('navigate-back');\n return;\n }\n // (2) Taxonomy fallback \u2014 the user's path is gone, so walk the\n // canonical parent instead. An ENTITY-level parent wins (the\n // instance one level up, e.g. the parent an id names); failing\n // that, the KIND-level parent (this page's section).\n const leaf = tree.get(id);\n if (!leaf?.content) return;\n const { kind, props } = leaf.content;\n const up = this.taxonomy.parentOf(kind, props);\n if (up) { this.navigateActiveTab(id, up.kind, up.props); return; }\n const parent = this.taxonomy.parentKindFor(kind);\n if (parent) this.navigateActiveTab(id, parent);\n }\n\n /** True when `navigateBack` would do something user-visible \u2014 i.e.\n * the active tab has per-tab history, OR the current kind has a\n * taxonomy parent. Used by the breadcrumb to grey out / hide the\n * Back button when there's nowhere to go. */\n canNavigateBack() {\n const tree = this._tree();\n const focusedId = tree.focusedLeafId;\n const primaryId = tree.primaryLeafId();\n const focusedLeaf = focusedId ? tree.get(focusedId) : null;\n const focusedKind = focusedLeaf?.content?.kind || '';\n const id = (focusedId && focusedKind\n && !focusedKind.startsWith('panel:')\n && focusedKind !== 'window-placeholder')\n ? focusedId : primaryId;\n if (!id) return false;\n const history = tree.activeTabHistory?.(id) || [];\n if (history.length > 0) return true;\n const leaf = tree.get(id);\n const kind = leaf?.content?.kind;\n return !!(kind && this.taxonomy.parentKindFor(kind));\n }\n\n /** Persist editor sub-state (e.g. active sub-tab) into the active\n * tab's WM props so future history snapshots restore the user's\n * view rather than the default landing. Pure metadata write \u2014 no\n * re-render, no history push. */\n updateActiveTabProps(leafId, patch) {\n const tree = this._tree();\n if (!tree?.updateActiveTabProps) return;\n // READ BEFORE THE WRITE. This is what `rebaselineLeaf` checks the\n // cached entry against, and it is the whole safety of accepting a props\n // change onto a live tile: if the mounted wrap is not still at the key\n // the tree had a moment ago, something else changed that this call has\n // no business swallowing.\n const expected = this.renderer?.leafKey?.(leafId) ?? undefined;\n tree.updateActiveTabProps(leafId, patch);\n // C34. THE TILE THAT SAVED ITS STATE MUST NOT BE REBUILT BY SAVING IT.\n // The renderer caches a leaf's wrap under a key that includes every\n // tab's props, so this write invalidated the tile it was called from \u2014\n // not here, but on whatever repainted next, which made it look like an\n // unrelated bug. `rebaselineLeaf` accepts the new props onto the live\n // entry; the argument is in its docstring.\n this.renderer?.rebaselineLeaf?.(leafId, expected);\n this._persist();\n }\n\n /** Navigate inside the current tab \u2014 preserves every other tab in\n * the leaf. Used by Backspace + breadcrumb segments + anything\n * else that should walk WITHIN the tile rather than reset it. */\n navigateActiveTab(leafId, kind, props = {}) {\n const tree = this._tree();\n const leaf = tree.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') {\n this.openInPrimary(kind, props);\n return;\n }\n tree.replaceActiveTabContent(leafId, { kind, props }, _tabTitle(kind, props));\n tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange('navigate-active-tab');\n }\n\n /** Route a navigation request to the right container based on\n * where it came from:\n *\n * - `ctx.windowId` set \u2192 replace the managed-window content (the\n * user is navigating inside a windowed tile; the window stays).\n * - `ctx.leafId` set AND that leaf is NOT `panel:left` \u2192 replace\n * the leaf's content in place (the user is in a split tile;\n * the click stays in that tile).\n * - Otherwise (palette, top-nav shortcuts, click in the\n * navigator panel, backspace, plain bus event) \u2192 primary tile.\n *\n * The left nav (`panel:left`) is the only tile whose clicks\n * intentionally jump out to the primary tile \u2014 every other\n * container is self-contained. */\n openFromContext(ctx, kind, props = {}) {\n if (ctx?.windowId && this._windowToLeaf.has(ctx.windowId)) {\n this.openInWindow(ctx.windowId, kind, props);\n return;\n }\n if (ctx?.leafId) {\n const tree = this._tree();\n const leaf = tree.get(ctx.leafId);\n const k = leaf?.content?.kind;\n if (leaf && k && k !== 'panel:left' && !k.startsWith('panel:')) {\n this.openInLeaf(ctx.leafId, kind, props);\n return;\n }\n }\n this.openInPrimary(kind, props);\n }\n\n /** Replace the leaf's **active tab** in place \u2014 preserves every\n * other tab. This is the routing for navigation that originated\n * INSIDE the tile: clicking a row on a landing page, walking a\n * breadcrumb segment, Backspace. Outside-the-tile navigation\n * (top-nav, palette, side-nav) uses `openInPrimary` instead, which\n * resets the leaf to a single fresh tab.\n *\n * For a leaf that has only one tab (the common case before the\n * tab feature shipped), the behavior is identical to the old\n * setLeafContent: one tab in, one tab out. */\n openInLeaf(leafId, kind, props = {}) {\n const tree = this._tree();\n const leaf = tree.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') {\n this.openInPrimary(kind, props);\n return;\n }\n tree.replaceActiveTabContent(leafId, { kind, props }, _tabTitle(kind, props));\n tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n\n /** Replace a managed window's content in place. Tears down the\n * previous mount, mounts the new kind into the same body element,\n * and updates the window's title. */\n openInWindow(winId, kind, props = {}) {\n const rec = this._windowToLeaf.get(winId);\n if (!rec) { this.openInPrimary(kind, props); return; }\n try { rec.mountInfo?.destroy?.(); } catch {}\n // R8. THE BODY, NOT THE CONTENT ELEMENT. A floated pane's window holds\n // a tab strip above its body, and both are children of `contentEl` \u2014\n // so clearing `contentEl` here would delete the strip, silently, on\n // the first in-window navigation, and the window would keep every one\n // of its tabs in a list nothing could reach. `_navigateWindow` builds\n // a window with no strip and no body, hence the fallback.\n const host = rec.bodyEl || rec.contentEl;\n host.innerHTML = '';\n const mountInfo = this.content.mount(kind, host, props,\n { ...this.ctx, wm: this, windowId: winId });\n rec.mountInfo = mountInfo;\n rec.original = {\n kind, props: { ...(props || {}) },\n title: mountInfo?.title || kind,\n };\n // R8. And the TAB the window is showing is now that content, so the\n // strip renames with it and a later dock puts back what is on screen\n // rather than what the tab was called when it was promoted.\n const tab = (rec.tabs || [])[rec.activeTabIdx];\n if (tab) {\n tab.kind = kind;\n tab.props = { ...(props || {}) };\n tab.title = rec.original.title;\n rec.strip?.update(rec.tabs, rec.activeTabIdx);\n }\n // Update window title (DOM + the ManagedWindow instance).\n this._setWindowTitle(rec, rec.original.title);\n // `rec.original` now holds the latest content, so a later \"back to\n // tile\" docks the current view (not the kind first promoted). The\n // window owns no tile in the tree, so there is no leaf to update.\n this._persist();\n this._notifyChange('window-content-changed');\n }\n\n /** Load content into the primary tile (last-active non-panel leaf).\n * If only panel tiles exist (or the tree is empty), spawn a new\n * content leaf and canonicalize so the panels wrap it. */\n openInPrimary(kind, props = {}) {\n const tree = this._tree();\n let leafId = tree.primaryLeafId();\n let spawned = false;\n if (!leafId) {\n leafId = this._spawnContentLeaf(tree);\n spawned = true;\n }\n if (!leafId) return;\n // Page-aware swap: archive the leaf's current page tabs and\n // restore (or initialize) the target page's. This is what makes\n // a tile remember its tab set when the user clicks away to\n // another top-nav page and back. `swapToPage` is a no-op when\n // the leaf has no current page (just-spawned content leaf) \u2014\n // the default branch initializes a fresh single tab.\n const leaf = tree.get(leafId);\n const currentTopNav = leaf?.content?.kind\n ? this.taxonomy.topNavFor(leaf.content.kind) || null\n : null;\n const targetTopNav = this.taxonomy.topNavFor(kind) || kind;\n const title = _tabTitle(kind, props);\n tree.swapToPage(leafId,\n { kind, props, title },\n currentTopNav, targetTopNav);\n tree.focus(leafId);\n if (spawned) this._canonicalize(tree, this.desktops.active());\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n\n // \u2500\u2500 Split / close / focus / move \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 split(dir) {\n const tree = this._tree();\n const focused = tree.focusedLeafId;\n if (!focused) return;\n // Chrome split buttons mirror Alt+H / Alt+V. The freshly created\n // pane is SEEDED with the default HOME content (the taxonomy root)\n // rather than left empty \u2014 the never-empty-tile invariant: a tile\n // in the grid always holds content the user can act on, and the\n // main tile is never left blank.\n const newId = tree.split(focused, dir);\n if (newId) this._seedHome(tree, newId);\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n\n /** Seed a leaf with the default HOME content (the taxonomy root kind).\n * Used to keep the never-empty-tile invariant: the pane freed by a\n * split, or emptied when its last tab floats into a window, is\n * re-homed instead of destroyed or left blank. Embedder-agnostic \u2014\n * the HOME kind comes from the taxonomy, exactly like a fresh\n * desktop's seed leaf. */\n _seedHome(tree, leafId) {\n const seed = this._rootLeaf();\n tree.setLeafContent(leafId, seed.content, seed.title);\n }\n\n /** Split `leafId` along `dir` and mount `kind`/`props` in the freshly\n * created sibling \u2014 the \"open this content in a new split\" primitive\n * behind the code-pane split buttons, the per-pane context menu and\n * Alt+Shift+H / Alt+Shift+V. The source leaf keeps its content\n * untouched; the new pane gets a FRESH mount (so e.g. opening a Code\n * pane in a split never dismounts the original). Returns the new\n * leaf id, or null if the leaf can't be split. */\n splitLeafWith(leafId, dir, kind, props = {}, title = '') {\n const tree = this._tree();\n const src = leafId ? tree.get(leafId) : null;\n if (!src || src.kind !== 'leaf') return null;\n const newId = tree.split(leafId, dir);\n if (!newId) return null;\n tree.setLeafContent(newId, { kind, props }, title || _tabTitle(kind, props));\n tree.focus(newId);\n this.renderer.render();\n this._persist();\n this._notifyChange('split-with');\n return newId;\n }\n\n /** Resolve the focused tile's active content for the keyboard-driven\n * \"open focused tile elsewhere\" chords (Alt+T / Alt+N / Alt+Shift+H /\n * Alt+Shift+V). Returns null for panels, window placeholders and\n * empty tiles \u2014 none of which can be meaningfully duplicated. */\n _focusedContent() {\n const tree = this._tree();\n const id = tree.focusedLeafId;\n const leaf = id ? tree.get(id) : null;\n if (!leaf || leaf.kind !== 'leaf' || !leaf.content) return null;\n const kind = leaf.content.kind;\n if (!kind || kind === PLACEHOLDER_KIND || kind.startsWith('panel:')) return null;\n return { leafId: id, kind, props: leaf.content.props || {}, title: leaf.title };\n }\n\n /** Alt+T \u2014 open the focused tile's content in a new tab on that tile. */\n openFocusedInTab() {\n const c = this._focusedContent();\n if (!c) return;\n this.openInTabFromContext({ leafId: c.leafId }, c.kind, c.props);\n }\n\n /** Alt+N \u2014 open the focused tile's content in a fresh managed window.\n * Unlike Alt+F (promote) this DUPLICATES: the source tile stays put. */\n openFocusedInWindow() {\n const c = this._focusedContent();\n if (!c) return;\n this._navigateWindow(c.kind, c.props);\n }\n\n /** Alt+Shift+H / Alt+Shift+V \u2014 split the focused tile and open a fresh\n * copy of its content in the new pane. Falls back to an empty split\n * when the focused tile has no duplicable content (e.g. a panel). */\n splitFocusedWith(dir) {\n const c = this._focusedContent();\n if (!c) { this.split(dir); return; }\n this.splitLeafWith(c.leafId, dir, c.kind, c.props, c.title);\n }\n\n closeFocused() {\n const tree = this._tree();\n const focusedId = tree.focusedLeafId;\n if (!focusedId) return;\n const leaf = tree.get(focusedId);\n const kind = leaf?.content?.kind;\n\n // \u2550\u2550 C20. THE CONTENT'S VETO IS HONOURED HERE, NOT ONLY PAINTED \u2550\u2550\n //\n // `chrome: { close: false }` \u2014 or `{ close: { disabled, title } }`,\n // which is the same refusal with a sentence attached \u2014 is read at mount\n // and `_vetoStructuralActions` acts on it by disabling the \u00D7 in the\n // tile's chrome. That is ONE door. This verb has three more: Alt+W\n // (`keymap.js`), the tile's right-click *Close tile* (`shell.js`), and\n // the tab strip's \u00D7 on a leaf's LAST tab (`_leafTabAction`, which\n // delegates straight to here). All three closed a pane whose own button\n // was greyed out with a tooltip promising it could not be closed.\n //\n // This is the same asymmetry `_floatableLeaf` was written to end for\n // `promote`, resolved the same way and for the same reason: the\n // renderer stays the only thing that knows what was mounted, and the WM\n // stays the only thing that decides whether a verb runs. A veto the\n // content states once holds for every door, or it is decoration.\n //\n // Silent rather than noisy: the control that offers this is already\n // disabled and already carries the explanation, so a keystroke that\n // does nothing is consistent with what the screen says.\n const closeChrome = this.renderer?.leafChrome?.(focusedId)?.close;\n if (closeChrome === false || closeChrome?.disabled === true) return;\n\n // Closing a placeholder closes its window \u2014 the window's onClose\n // handler restores the leaf, then we close that leaf too.\n if (kind === PLACEHOLDER_KIND) {\n const winId = leaf.content?.props?.windowId;\n const rec = winId ? this._windowToLeaf.get(winId) : null;\n if (rec) {\n // Drop the mapping so the onClose handler doesn't restore.\n this._windowToLeaf.delete(winId);\n try { rec.window.close({ force: true }); } catch {}\n }\n tree.close(focusedId);\n }\n // Closing a panel: also flip the desktop's panel state off so\n // the top-bar toggle button reflects the closure.\n else if (PANEL_KINDS.has(kind)) {\n const d = this.desktops.active();\n const side = kind.split(':')[1];\n d.panels[side] = false;\n this._canonicalize(tree, d);\n }\n else {\n tree.close(focusedId);\n }\n\n // THE MASTER TILE COMES BACK.\n //\n // Closing the last content tile used to leave the panels occupying the\n // whole WM, on the reasoning that `openInPrimary` would spawn a new leaf\n // when something needed one. That is true and it is not enough: the\n // content area is also the GROUND floating windows stand on, so an\n // embedder that promotes a tile into a window and then closes the tile\n // it came from is left with a window floating over nothing, and no\n // surface to drop it back onto. There is nowhere to put it and nothing\n // saying what to do next.\n //\n // So the invariant is now the same one `toggleManagedFocused` already\n // keeps for a lone tile: there is ALWAYS a content leaf, and it holds\n // the taxonomy root. `_seedHome` is the same call, so the tile that\n // comes back is the same tile a fresh desktop starts with.\n if (!tree.leaves().some((l) => !String(l.content?.kind || '').startsWith('panel:'))) {\n const spawned = this._spawnContentLeaf(tree);\n if (spawned) this._seedHome(tree, spawned);\n }\n\n // Re-canonicalize so the remaining panels fill the area properly (no\n // leftover wrap from the closed content leaf).\n this._canonicalize(tree, this.desktops.active());\n\n this.renderer.render();\n this._persist();\n this._notifyChange('tile-closed');\n }\n\n /** Close the ACTIVE tab of a leaf (e.g. a Cancel button inside the\n * tab's content). Mirrors the tab-strip \u00D7 handler: remove just that\n * tab, or close the whole tile if it was the last one. Lets content\n * self-close without nuking sibling tabs. */\n closeActiveTab(leafId) {\n const tree = this._tree();\n const leaf = leafId ? tree.get(leafId) : null;\n if (!leaf || leaf.kind !== 'leaf') {\n // No tabbed leaf (e.g. content in a managed window) \u2014 fall\n // back to closing the focused container.\n this.closeFocused();\n return;\n }\n const tabs = leaf.tabs || [];\n if (tabs.length <= 1) {\n tree.focus(leafId);\n this.closeFocused();\n return;\n }\n const idx = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));\n tree.removeLeafTab(leafId, idx);\n tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange('twm-tab-close');\n }\n\n /** Insert a new empty content leaf at the centre of the layout\n * (wraps the current root in an h-split with the content on the\n * left, 4:1 ratio). If the tree is empty, becomes the root. */\n _spawnContentLeaf(tree) {\n const newLeaf = makeLeaf({ content: null, title: '' });\n if (!tree.rootId) { tree.setRoot(newLeaf); return newLeaf.id; }\n tree._register(newLeaf);\n const split = {\n id: `split-spawn-${Date.now().toString(36)}`,\n kind: 'split', parentId: null, dir: 'h',\n children: [newLeaf.id, tree.rootId], sizes: [4, 1],\n };\n tree.nodes.set(split.id, split);\n const oldRoot = tree.get(tree.rootId);\n if (oldRoot) oldRoot.parentId = split.id;\n newLeaf.parentId = split.id;\n tree.rootId = split.id;\n tree.focus(newLeaf.id);\n return newLeaf.id;\n }\n\n focusDir(dir) {\n if (this._tree().focusDir(dir)) {\n this.renderer._updateFocusClasses();\n this._notifyChange();\n }\n }\n\n /** The `.twm-leaf` DOM element of the currently focused leaf on the\n * active desktop, or null. The panel-key router consults this to\n * decide which panel owns the keyboard \u2014 it is the single source of\n * truth for \"the selected tile\". */\n focusedLeafEl() {\n const id = this._tree().focusedLeafId;\n return id ? this.renderer.leafEl(id) : null;\n }\n\n moveFocused(dir) {\n if (this._tree().moveDir(dir)) {\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n }\n\n // \u2500\u2500 Tile <-> Managed window \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 /** Float the focused pane into a managed window.\n *\n * R8. THE WHOLE PANE, not its active tab. This used to float one tab and\n * leave the rest behind, which made \"float this pane as a window\" a\n * different verb from the one its own tooltip named: a pane with three\n * tables in it became a window holding one and a pane holding two, and\n * nothing on screen said which of the three you were going to get. The\n * product owner's words are the whole specification \u2014 *\"to window includes\n * the tab-strip\"* \u2014 so the tabs travel with the pane and the strip is\n * rendered INSIDE the window.\n *\n * Floating ONE tab is still available and is still wanted; it moved to\n * where it was always meant to be, which is the right-click menu on the\n * tab itself (R9, `floatTabAsWindow`). A verb that acts on one tab belongs\n * on that tab, not on the pane's chrome.\n *\n * Never-empty-tile invariant, unchanged: the emptied pane is RE-SEEDED\n * with the default HOME content rather than destroyed, so the grid never\n * ends up with a missing or blank main tile. */\n toggleManagedFocused() {\n const tree = this._tree();\n const focused = tree.focused();\n if (!focused) return null;\n return this.floatPane(focused.id);\n }\n\n /** R8. Float a pane \u2014 every tab, with the strip \u2014 into a managed window.\n * Returns the window id, or null when the leaf is not something that can\n * be floated. */\n floatPane(leafId) {\n const leaf = this._floatableLeaf(leafId);\n if (!leaf) return null;\n const tabs = _leafTabSpecs(leaf);\n const active = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));\n return this._promote(leafId, tabs, active, { wholePane: true });\n }\n\n /** R9. Float ONE tab of a pane into a managed window, leaving its siblings\n * where they are \u2014 which is exactly what `toggleManagedFocused` did before\n * R8, so the behaviour survives, it just moved to the gesture that names\n * it. The tab's right-click menu is the only caller. */\n floatTabAsWindow(leafId, idx) {\n const leaf = this._floatableLeaf(leafId);\n if (!leaf) return null;\n const tabs = _leafTabSpecs(leaf);\n if (!Number.isInteger(idx) || idx < 0 || idx >= tabs.length) return null;\n return this._promote(leafId, [tabs[idx]], 0, { wholePane: false, tabIdx: idx });\n }\n\n /**\n * C33. MOVE ONE TAB INTO ANOTHER TILE \u2014 the same verb as `floatTabAsWindow`\n * above with a TILE as the destination instead of a window, which is why it\n * sits beside it.\n *\n * \u2550\u2550 THE ORDER IS LOAD-BEARING \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * Four steps, and three of them are in this order for a reason that a\n * plausible-looking rewrite would destroy:\n *\n * (a) READ THE TAB SPEC FIRST. `fromIdx` is an ARRAY INDEX \u2014 the only\n * identity a tile tab has (`tile_renderer._tabKey`) \u2014 so it is stale\n * the instant anything splices a tab list. Everything below works\n * from the copy taken here.\n *\n * (b) SPLIT BEFORE REMOVING. When the destination IS the source pane \u2014\n * \"tear this tab off into a split beside its siblings\" \u2014 removing\n * first can empty that pane and send it through `_seedHome`, so the\n * split would then be splitting a freshly seeded ground rather than\n * the pane the preview drew. Splitting first cannot go wrong in the\n * other direction: `tree.split` never touches tabs.\n *\n * (c) THE MOVE ITSELF IS ONE TREE CALL for `tab` \u2014 `moveTabToLeaf`, which\n * exists so the tab cannot be in flight between two mutations \u2014 and\n * remove-then-`setLeafContent` for `fill`/`split`, where the\n * destination is ground or brand new and REPLACING is the point.\n *\n * (d) RE-SEED AND MERGE, exactly as `_promote` does when the last tab\n * leaves a pane (`_seedHome` then `_mergeStartTiles`). A pane is\n * never left blank, and two grounds never end up side by side with a\n * splitter between them for no reason.\n *\n * \u2550\u2550 `wm:tab-moved` IS EMITTED BEFORE THE REPAINT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * An embedder that keys live content by leaf id \u2014 the Tables grid registry\n * does, on `(leaf, table)`, because a DOM element exists in exactly one\n * place \u2014 has to re-key BEFORE the render mounts the destination, or the\n * destination misses its entry, builds a second grid, and the source tile's\n * deferred teardown destroys the first one along with everything typed into\n * it and not yet committed. Emitting after the render would lose that race\n * silently, which is the failure this repository keeps recording. The tree\n * is already correct at this point; only the DOM is stale.\n *\n * \u2550\u2550 WHAT THIS DELIBERATELY DOES NOT DO \u2550\u2550\u2550\u2550\u2550\u2550\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 * A tab is not dragged OUT OF A FLOATING WINDOW's strip, and a tab dropped\n * on empty space does not become a window. Both are refused by omission\n * rather than half-built, and both have a reason. A window's tabs live in\n * `_windowToLeaf`'s record and not in the tree, so their source policy is\n * `_windowTabAction`'s and not this function's. And \"dropped on nothing\" in\n * HTML5 drag-and-drop is `dragend` with no `drop` \u2014 which is also exactly\n * what pressing Escape produces, so floating a window on it would float one\n * every time a user changed their mind. Crossing DESKTOPS is out for a\n * third reason: only the active desktop is rendered, so there is no target\n * to hit.\n *\n * @param {string} fromLeafId\n * @param {number} fromIdx\n * @param {{leafId: string, mode?: 'tab'|'fill'|'split', dir?: 'h'|'v',\n * before?: boolean, toIdx?: number}} target a `tabDropProbe`\n * answer, translated by the renderer\n * @returns {string|null} the leaf the tab landed in, or null if refused\n */\n moveTabInto(fromLeafId, fromIdx, target = {}) {\n const tree = this._tree();\n const from = tree.get(fromLeafId);\n if (!from || from.kind !== 'leaf') return null;\n const tabs = Array.isArray(from.tabs) ? from.tabs : [];\n if (!Number.isInteger(fromIdx) || fromIdx < 0 || fromIdx >= tabs.length) return null;\n let destId = target?.leafId || null;\n const dest = destId ? tree.get(destId) : null;\n if (!dest || dest.kind !== 'leaf') return null;\n if (String(dest.content?.kind || '').startsWith('panel:')) return null;\n const mode = (target.mode === 'fill' || target.mode === 'split')\n ? target.mode : 'tab';\n // The two no-ops `tabDropProbe` already refuses, refused a second time\n // here because this is a PUBLIC verb and a keyboard or palette caller\n // never went through the probe: a tab dropped into the pane it already\n // lives in, and a single-tab pane split against itself.\n if (destId === fromLeafId && mode !== 'split') return null;\n if (destId === fromLeafId && tabs.length <= 1) return null;\n\n // (a) the spec, copied before any mutation makes the index a lie.\n const src = tabs[fromIdx];\n const spec = { kind: src.kind, props: { ...(src.props || {}) },\n title: src.title || src.kind || '' };\n\n // (b) the split, before the removal.\n if (mode === 'split') {\n const newId = tree.split(destId, target.dir === 'v' ? 'v' : 'h');\n if (!newId) return null;\n // THE PREVIEW PROMISED HALF OF THAT TILE, and `TileTree.split` only\n // delivers a half when it has to WRAP \u2014 into a row that already\n // runs this way it INSERTS, handing the newcomer the average of the\n // existing sizes. The same two corrections `_onManagedWindowClosed`\n // applies to a window drop (C15), for the same reason and in the\n // same order.\n _halveInto(tree, destId, newId);\n if (target.before) _swapSiblings(tree, destId, newId);\n destId = newId;\n }\n\n // (c) the move.\n if (mode === 'tab') {\n const moved = tree.moveTabToLeaf(fromLeafId, fromIdx, destId,\n Number.isInteger(target.toIdx) ? target.toIdx : -1);\n if (!moved?.ok) return null;\n } else {\n tree.removeLeafTab(fromLeafId, fromIdx);\n tree.setLeafContent(destId, { kind: spec.kind, props: spec.props }, spec.title);\n }\n\n // (d) the source pane, if the last tab just left it.\n if (!((tree.get(fromLeafId)?.tabs || []).length)) {\n this._seedHome(tree, fromLeafId);\n this._mergeStartTiles(tree, fromLeafId);\n }\n this._canonicalize(tree, this.desktops.active());\n\n // BEFORE THE REPAINT. See the block comment above \u2014 this is the moment\n // the tree is right and the DOM has not moved yet, and it is the only\n // moment an embedder can re-key content it holds by leaf id.\n try {\n this.eventBus?.emit?.('wm:tab-moved',\n { fromLeafId, toLeafId: destId, tab: spec, mode });\n } catch (err) { console.warn('[wm] tab-moved emit failed', err); }\n\n if (tree.get(destId)) tree.focus(destId);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-moved');\n return destId;\n }\n\n /** The guards both float verbs share. A panel tile is chrome, not content;\n * a window placeholder is already a window; an empty tile has nothing to\n * carry \u2014 and, since C20 was extended, content that declared itself\n * unfloatable is not floated by ANY door.\n *\n * THE LAST ONE IS WHY THIS FUNCTION IS THE RIGHT PLACE. C20 let a content\n * factory return `chrome: { promote: false }`, and the renderer honoured\n * it by not PAINTING the float button. That is one door of four: the\n * tile's right-click menu has offered \"Float this pane as a window\" all\n * along (`shell.js`'s `_tileContextMenu`, whose only guard is\n * `isPanel || !leaf.content`), the chrome pull-down asks for `promote`,\n * and so now does the chrome's double-click. Each of them arrives here.\n *\n * The case it protects is an embedder's MASTER tile: the ground that\n * floating windows stand on. Floating it promotes the ground into a\n * window, which force-closes every window standing on it and re-seeds the\n * pane WITHOUT the props that made it a ground \u2014 reproduced end to end\n * before this guard existed. A veto the content states once should hold\n * for every gesture, not only the one the renderer draws. */\n _floatableLeaf(leafId) {\n const leaf = leafId ? this._tree().get(leafId) : null;\n if (!leaf || leaf.kind !== 'leaf' || !leaf.content) return null;\n if (PANEL_KINDS.has(leaf.content.kind)) return null;\n if (leaf.content.kind === PLACEHOLDER_KIND) return null;\n // `=== false` rather than falsy: content that says nothing about\n // `promote` stays floatable, which is what every existing embedder\n // relies on. Only an explicit refusal refuses.\n if (this.renderer?.leafChrome?.(leaf.id)?.promote === false) return null;\n return leaf;\n }\n\n /**\n * The promote itself: build the window, mount the active tab in it, and\n * take the tabs out of the tree.\n *\n * @param {string} leafId the pane the tabs are coming out of\n * @param {object[]} tabs `{kind, props, title}`, in order\n * @param {number} activeTabIdx which of them the window shows first\n * @param {{wholePane: boolean, tabIdx?: number}} opts\n */\n _promote(leafId, tabs, activeTabIdx, { wholePane, tabIdx = -1 }) {\n const tree = this._tree();\n const leaf = tree.get(leafId);\n const desktopIdx = this.desktops.activeIdx;\n const tabCount = Array.isArray(leaf.tabs) ? leaf.tabs.length : 1;\n const active = Math.max(0, Math.min(tabs.length - 1, activeTabIdx || 0));\n const original = { ...tabs[active], props: { ...(tabs[active].props || {}) } };\n\n const contentEl = document.createElement('div');\n contentEl.className = 'twm-window-content';\n contentEl.style.cssText = 'display:flex; flex-direction:column; flex:1; min-width:0; min-height:0; height:100%;';\n // R8. THE STRIP IS INSIDE THE WINDOW. A pane's tabs and a pane's body\n // are one thing \u2014 *\"to window includes the tab-strip\"* \u2014 so the window\n // gets both, in the order the `top` layout draws them, and the content\n // mounts into the BODY rather than into the window's content element.\n // Everything that used to write straight into `contentEl` (see\n // `openInWindow`) has to write into the body now, or it takes the strip\n // with it the first time the window's content is replaced.\n const tabBarEl = document.createElement('div');\n tabBarEl.className = 'twm-window-tabbar';\n const bodyEl = document.createElement('div');\n bodyEl.className = 'twm-window-body';\n bodyEl.style.cssText = 'display:flex; flex-direction:column; flex:1; min-width:0; min-height:0;';\n contentEl.append(tabBarEl, bodyEl);\n\n const winId = `twm-mw-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 5)}`;\n const mountInfo = this.content.mount(original.kind, bodyEl, original.props,\n { ...this.ctx, wm: this, windowId: winId });\n const win = new ManagedWindow({\n id: winId,\n title: mountInfo?.title || original.title,\n icon: 'web_asset',\n content: contentEl,\n canMinimize: true,\n canMaximize: true,\n canResize: true,\n modal: false,\n // C15. Dropping a promoted window on a tile PUTS IT BACK \u2014 as that\n // tile's content, as a split of it, or as one of its tabs. Off\n // unless the embedder asked, because it changes what a drag to an\n // edge means.\n snap: this.snapPromotion,\n snapController: this.snapPromotion ? this._snapController() : null,\n // R1. THE PANE IS A BOX WITH `overflow: hidden`. A window contained\n // to one (C21) cannot be dragged a single pixel outside it, so\n // \"drag a window from one tile to another\" \u2014 the gesture all three\n // drop behaviours are built on \u2014 was not merely awkward, it was\n // invisible. For the length of a drag the window is re-parented\n // here, to the root every tile is inside; on release it goes back\n // into a pane, either the one it was dropped on or the one it came\n // from. Resolved per drag: the root outlives any tile, and a tile\n // grabbed once does not survive its own repaint.\n dragHost: () => this.rootEl,\n dragBounds: () => this._tileBounds(),\n // R7. MAXIMISE MEANS BACK TO TILE. This window came OUT of the\n // tree; the useful thing to do with it is put it back, and filling\n // the screen with it is the one gesture that makes putting it back\n // harder. So the maximize button docks \u2014 and the separate demote\n // button the WM used to inject beside it is gone, because two\n // buttons for one verb is how you get a chrome nobody reads.\n onMaximize: () => this.bringBackWindow(winId),\n maximizeIcon: 'close_fullscreen',\n maximizeTitle: 'Back to tile',\n onClose: () => this._onManagedWindowClosed(winId, null),\n });\n\n this._windowToLeaf.set(winId, {\n // The floated tabs now live in the window, not the tree. leafId\n // is null so \"back to tile\" re-docks into the desktop's primary\n // tile (see _onManagedWindowClosed) \u2014 the source tile itself\n // survives (re-seeded with HOME when it was emptied).\n leafId: null, desktopIdx, original, mountInfo, window: win,\n contentEl, bodyEl, tabBarEl,\n // R8. THE PANE'S TABS TRAVEL WITH IT, and this is where they live\n // while the window is open. `original` still mirrors the ACTIVE one\n // so every existing reader of the record \u2014 `openInWindow`, each of\n // `_onManagedWindowClosed`'s docks \u2014 keeps working unchanged; the\n // list beside it is what makes a dock restore ALL of them.\n tabs, activeTabIdx: active, strip: null,\n // \u2550\u2550 C21. WRITTEN HERE, BEFORE THE TREE IS TOUCHED \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n //\n // `homeLeafId` is the pane this window stands on, and it used to be\n // assigned at the BOTTOM of this function \u2014 after `_seedHome`,\n // after `_mergeStartTiles`, after the repaint. That ordering\n // destroyed a tile per promotion, and it looked like a window bug\n // because a window is what the user had just moved.\n //\n // `_mergeStartTiles` (below) refuses to merge a start tile that has\n // a window standing on it, and `_paneHoldsWindows` answers that\n // question two ways: THIS FIELD, and a DOM probe for a\n // `.twm-managed-window` inside the leaf. At the old assignment point\n // neither could be true yet \u2014 the field was unwritten and the window\n // had not been `moveTo`'d into the pane \u2014 so the pane that was one\n // line away from becoming this window's ground answered *nothing\n // floats here* and was merged into its neighbour.\n //\n // Three panes floated one after another ended as ONE pane: the\n // first promotion left a start tile, the second merged its own\n // freshly-seeded tile away, and so did the third. Every window\n // after the first was then left with a `homeLeafId` naming a leaf\n // the tree no longer had \u2014 so the `moveTo` below was skipped and the\n // window never became contained, `_rehomeContainedWindows` found no\n // element to re-home it into, and `bringBackWindow` fell through to\n // the PRIMARY tile and docked as a tab onto the ground ANOTHER\n // window was standing on. That is the whole of the reported\n // *\"expand one to a tile > influences others or even tiles lost\"*.\n //\n // Writing it here is the smallest fix that closes all of it: a\n // guard that already existed starts being able to see the window it\n // was written to protect. The `moveTo` stays at the bottom, because\n // it needs the wrap the repaint rebuilds.\n homeLeafId: this.promoteInPlace ? leafId : null,\n // Set to true by bringBackWindow so the close path knows to\n // restore the content instead of destroying it.\n _demoting: false,\n });\n this._syncWindowTabs(winId);\n\n // R9. ONE TAB out of a multi-tab pane: the siblings stay put. R8: the\n // pane is emptied and re-seeded, because the whole of it left.\n if (!wholePane && tabCount > 1) tree.removeLeafTab(leafId, tabIdx);\n else this._seedHome(tree, leafId);\n\n // R10. The pane that just emptied is a START TILE now, and so may be\n // the pane beside it. Two of them side by side are one surface with a\n // splitter through it for no reason \u2014 see `_mergeStartTiles`, which is\n // where the rest of that argument lives.\n this._mergeStartTiles(tree, leafId);\n\n tree.focus(leafId);\n this._canonicalize(tree, this.desktops.active());\n\n this.renderer.render();\n\n // CONFINED TO THE PANE IT CAME FROM, when the embedder asked for it.\n //\n // Done AFTER the render and BEFORE `show()`, and both halves of that\n // matter. After, because promoting re-seeds the source leaf, which\n // changes its cache key and REBUILDS its wrap \u2014 the element grabbed\n // before the render is destroyed by it. Before, because `moveTo` on a\n // window that has not been built yet simply records the container\n // (`managed_window.js` returns early on a null element), so `show()`\n // mounts straight into the pane instead of appearing at the viewport\n // and jumping.\n //\n // The LEAF WRAP, not the `.twm-slot` around it: slots are recreated on\n // every render (`tile_renderer.js` clears the root and rebuilds them)\n // while leaf wraps are cached and re-parented, so a window contained to\n // a slot would be orphaned the first time anything repainted. The wrap\n // fills the slot, so the two are the same box.\n if (this.promoteInPlace) {\n // `homeLeafId` was written with the record, above, and the argument\n // for that is there. It is what `_rehomeContainedWindows` re-parents\n // against after every repaint \u2014 without it the SECOND promotion out\n // of a two-tab pane deletes the first one's window: the leaf's wrap\n // is rebuilt when its tab set changes, and the first window was\n // parented into the wrap that the rebuild threw away.\n const paneEl = this.renderer.leafEl(leafId);\n if (paneEl) win.moveTo(paneEl);\n }\n\n win.show();\n this._decorateManagedWindow(win, winId);\n this._persist();\n this._notifyChange('window-promoted');\n return winId;\n }\n\n // \u2550\u2550 R8. The tab strip inside a floated pane \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 * Draw (or hide) a window's tab strip, and keep its title honest.\n *\n * Hidden below two tabs, exactly as a pane's strip is\n * (`tile_renderer._renderTabBar`): the common case is one tab, and a strip\n * naming the one thing you are already looking at is a line of chrome\n * saying nothing. Building the strip lazily also means a window promoted\n * out of a single-tab pane costs no `NotebookTabBar` at all.\n */\n _syncWindowTabs(winId) {\n const rec = this._windowToLeaf.get(winId);\n if (!rec) return;\n const tabs = rec.tabs || [];\n rec.activeTabIdx = Math.max(0, Math.min(tabs.length - 1, rec.activeTabIdx || 0));\n if (tabs.length <= 1) {\n try { rec.strip?.dispose(); } catch { /* already gone */ }\n rec.strip = null;\n rec.tabBarEl?.classList.add('twm-window-tabbar--hidden');\n return;\n }\n rec.tabBarEl?.classList.remove('twm-window-tabbar--hidden');\n if (!rec.strip) {\n rec.strip = createTabStrip({\n hostEl: rec.tabBarEl,\n taxonomy: this.taxonomy,\n onAction: (action, data) => this._windowTabAction(winId, action, data),\n });\n }\n rec.strip.update(tabs, rec.activeTabIdx);\n }\n\n /** The window strip's half of `_leafTabAction` \u2014 the same four verbs\n * against the window record instead of against the tree. */\n _windowTabAction(winId, action, data = {}) {\n const rec = this._windowToLeaf.get(winId);\n if (!rec) return;\n const tabs = rec.tabs || [];\n if (action === 'switch') {\n this.showWindowTab(winId, data.idx);\n return;\n }\n if (action === 'close') {\n if (data.idx < 0 || data.idx >= tabs.length) return;\n // THE LAST TAB CLOSES THE WINDOW, which is what closing the last\n // tab of a pane does to the pane (`_leafTabAction`). A window with\n // no content in it is a title bar over nothing.\n if (tabs.length <= 1) {\n try { rec.window.close({ force: true }); } catch { /* gone */ }\n return;\n }\n tabs.splice(data.idx, 1);\n if (data.idx < rec.activeTabIdx) rec.activeTabIdx -= 1;\n else if (data.idx === rec.activeTabIdx) {\n rec.activeTabIdx = Math.max(0, data.idx - 1);\n this._mountWindowTab(winId);\n }\n this._syncWindowTabs(winId);\n this._notifyChange('window-tab-close');\n return;\n }\n if (action === 'move') {\n const { from, to } = data;\n if (from == null || to == null) return;\n if (from < 0 || from >= tabs.length || to < 0 || to >= tabs.length) return;\n const moved = tabs.splice(from, 1)[0];\n tabs.splice(to, 0, moved);\n // The same index arithmetic `TileTree.moveLeafTab` does, for the\n // same reason: the tab that was showing must still be showing.\n if (rec.activeTabIdx === from) rec.activeTabIdx = to;\n else if (from < rec.activeTabIdx && to >= rec.activeTabIdx) rec.activeTabIdx -= 1;\n else if (from > rec.activeTabIdx && to <= rec.activeTabIdx) rec.activeTabIdx += 1;\n this._syncWindowTabs(winId);\n return;\n }\n if (action === 'menu') this._showWindowTabContextMenu(winId, data.idx, data.x, data.y);\n }\n\n /** Show one of a floated pane's tabs. Public because a window is the only\n * place this list exists \u2014 nothing else can reach it. */\n showWindowTab(winId, idx) {\n const rec = this._windowToLeaf.get(winId);\n if (!rec) return false;\n const tabs = rec.tabs || [];\n if (!Number.isInteger(idx) || idx < 0 || idx >= tabs.length) return false;\n if (idx === rec.activeTabIdx) return true;\n rec.activeTabIdx = idx;\n this._mountWindowTab(winId);\n this._syncWindowTabs(winId);\n this._persist();\n this._notifyChange('window-tab-switch');\n return true;\n }\n\n /** Tear the current mount down and mount the active tab in its place.\n * `rec.original` follows, so a later dock puts back what is on screen. */\n _mountWindowTab(winId) {\n const rec = this._windowToLeaf.get(winId);\n if (!rec) return;\n const tab = (rec.tabs || [])[rec.activeTabIdx];\n if (!tab) return;\n try { rec.mountInfo?.destroy?.(); } catch { /* factory threw on the way out */ }\n rec.bodyEl.innerHTML = '';\n rec.mountInfo = this.content.mount(tab.kind, rec.bodyEl, tab.props || {},\n { ...this.ctx, wm: this, windowId: winId });\n rec.original = { kind: tab.kind, props: { ...(tab.props || {}) },\n title: rec.mountInfo?.title || tab.title || tab.kind };\n this._setWindowTitle(rec, rec.original.title);\n }\n\n /** The window's title, in both places it is kept. */\n _setWindowTitle(rec, title) {\n try {\n const titleEl = rec.window?.element?.querySelector('.twm-managed-window__title');\n if (titleEl) titleEl.textContent = title;\n if (rec.window) rec.window.title = title;\n } catch { /* the window is already torn down */ }\n }\n\n /** The window strip's context menu. Deliberately the close verbs and\n * nothing else: a tab in a window is already out of the tree, so\n * \"open in a window\" \u2014 the verb R9 adds to a PANE's tab menu \u2014 has\n * nowhere further to go. */\n _showWindowTabContextMenu(winId, idx, x, y) {\n const rec = this._windowToLeaf.get(winId);\n const tabs = rec?.tabs || [];\n if (!tabs.length) return;\n const items = [{ label: 'Close tab', icon: 'close', action: 'close' }];\n if (tabs.length > 1) {\n items.push({ label: 'Close other tabs', icon: 'tab_close', action: 'close-others' });\n }\n showContextMenu(x, y, items, (action) => {\n const live = this._windowToLeaf.get(winId);\n if (!live) return;\n if (action === 'close') this._windowTabAction(winId, 'close', { idx });\n else if (action === 'close-others') {\n const keep = live.tabs[idx];\n if (!keep) return;\n // RE-MOUNT ONLY IF THE KEPT TAB IS NOT THE ONE ON SCREEN.\n // Mounting is destroy-then-build, and a content factory that\n // rebuilds is a factory that can lose what is staged in it \u2014\n // \"close the other tabs\" must not be a way to discard the edits\n // in the tab you kept.\n const remount = idx !== live.activeTabIdx;\n live.tabs = [keep];\n live.activeTabIdx = 0;\n if (remount) this._mountWindowTab(winId);\n this._syncWindowTabs(winId);\n this._notifyChange('window-tab-close-others');\n }\n });\n }\n\n /**\n * Re-parent every pane-contained window into its pane's CURRENT wrap.\n *\n * Called after each repaint. A leaf's wrap is cached per (kind, props, tab\n * fingerprint) and rebuilt when any of those change, so a window parented\n * into it is thrown away with the old wrap \u2014 silently, because nothing\n * throws and the window object is still perfectly alive.\n *\n * `moveTo` returns false when the container has not changed, so this is a\n * no-op on every repaint that did not rebuild the pane in question.\n */\n _rehomeContainedWindows() {\n if (!this.promoteInPlace) return;\n for (const [, rec] of this._windowToLeaf) {\n if (!rec.homeLeafId || !rec.window) continue;\n // R1. NOT WHILE IT IS BEING DRAGGED OUT. A window mid-escape is\n // parented to the root deliberately and is following the pointer in\n // the root's coordinates; re-homing it into its pane here would clip\n // it, re-clamp it, and leave the rest of the drag computing deltas\n // against a frame that moved underneath it. The escape puts it back\n // itself on release, and adopts the current wrap when it does.\n if (rec.window.dragOrigin) continue;\n // R13, NOW A GUARD RATHER THAN A PATH. NOR WHILE IT IS MAXIMISED\n // ONTO THE DRAG HOST. A window filling the layer is parented to the\n // ROOT of necessity \u2014 `.twm-leaf` is `overflow: hidden`, so a\n // window bigger than one tile cannot be a child of one \u2014 and\n // `moveTo` re-clamps into the new container without converting, so\n // re-homing it here would shrink it back into the pane on the first\n // repaint after the gesture.\n //\n // WHICH ROUTE STILL REACHES IT, honestly stated, because a guard\n // whose route nobody can name is dead code wearing a comment. R14\n // removed the one this was WRITTEN for: the aero-snap top edge used\n // to maximise onto the layer and now docks, so no gesture in this\n // layer leaves a window maximised on the root any more. What\n // remains is the library API \u2014 `toggleMaximize({claimable: false})`\n // is public, documented on `ManagedWindow` as the way past a\n // consumer's claim, and an embedder that calls it on an adopted\n // window sitting on the drag host produces exactly this state. It\n // costs one comparison to keep and a silently shrinking window to\n // remove, so it stays.\n //\n // BOTH CONDITIONS, and neither alone. `isMaximized` by itself would\n // also skip a window maximised INSIDE its pane \u2014 that one still has\n // to follow its wrap when the wrap is rebuilt, which is the whole\n // job of this pass, and `snap_zones.test.mjs` asserts it. The\n // container by itself would skip a window the escape left on the\n // root because its origin was destroyed mid-drag, which is\n // precisely the case this pass exists to adopt.\n if (rec.window.isMaximized && rec.window.container === this.rootEl) continue;\n\n // \u2550\u2550 C21. A WINDOW BELONGS TO ONE DESKTOP \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n //\n // \u2026and is in the document only while that desktop is on screen.\n // That has always been TRUE and was never STATED: switching\n // desktops evicts the old tree's leaves from the wrap cache\n // (`tile_renderer._cleanCache`), which takes the windows standing\n // on them out of the document as a side effect. Saying it here is\n // what lets `moveWindowToDesktop` actually move a contained window\n // \u2014 it re-points the record and this pass takes the element off\n // the page the window has just left. Coming back re-parents it:\n // the destination's wraps are rebuilt on arrival, so `moveTo` sees\n // a container it has not seen and appends.\n if (rec.desktopIdx !== this.desktops.activeIdx) {\n if (!rec.homeContainer && rec.window.element?.isConnected) {\n try { rec.window.element.remove(); } catch { /* already out */ }\n }\n continue;\n }\n\n // \u2550\u2550 A HOME LEAF THAT HAS LEFT THE TREE IS REPAIRED, NOT SKIPPED \u2550\u2550\n //\n // *\"tiles must only influence tiles, not windows\"* \u2014 the product\n // owner, 2026-09-01. A tile can be destroyed by four gestures\n // (`closeFocused`, the tab strip's \u00D7 on a last tab,\n // `moveFocusedToDesktop`, and `_mergeStartTiles`) and NONE of them\n // asked what was standing on it. `_paneHoldsWindows` \u2014 the guard\n // written for exactly that question \u2014 is consulted from one place\n // in the whole file, and no close path is it.\n //\n // What happened when a pane with a window on it was closed:\n // `_cleanCache` removed the wrap, and the window's element with it,\n // twenty-three lines BEFORE this pass runs. The window was left\n // alive with `isVisible: true` and `isMinimized: false` \u2014 so it was\n // on no surface AND in no taskbar (Tables' strip lists the\n // minimised), holding a mounted grid and its staged edits, with no\n // chrome to press and no route back. Reproduced end to end.\n //\n // Repairing it HERE rather than at each of the four call sites is\n // deliberate: this pass already runs after every render, it already\n // owns *where a contained window is parented*, and a fifth gesture\n // that closes a leaf gets the same answer without knowing this rule\n // exists. The window keeps floating \u2014 it re-homes onto the tile\n // that survived rather than docking itself \u2014 which is the ruling:\n // a tile operation may move a window, never convert or destroy it.\n //\n // NOT for an adopted window (`homeContainer`): the embedder resolves\n // its own ground and owns the lifetime of the box it stands in.\n if (!rec.homeContainer && rec.homeLeafId) {\n const tree = this.desktops.desktops[rec.desktopIdx]?.tree;\n if (tree && !tree.get(rec.homeLeafId)) {\n // A TREE WITH NO CONTENT LEAF GETS ONE. `closeFocused`\n // keeps that invariant itself, but `moveFocusedToDesktop`\n // did not \u2014 with a panel open it leaves a page holding\n // nothing but chrome, and then there is no ground for the\n // windows that were standing there. Spawning it here is the\n // same call `closeFocused` makes and the same tile a fresh\n // desktop starts with; leaving `homeLeafId` dangling would\n // put the window back in the state above.\n let survivor = tree.primaryLeafId();\n if (!survivor) {\n const spawned = this._spawnContentLeaf(tree);\n if (spawned) {\n this._seedHome(tree, spawned);\n this._canonicalize(tree, this.desktops.desktops[rec.desktopIdx]);\n survivor = tree.primaryLeafId();\n }\n }\n if (survivor) rec.homeLeafId = survivor;\n }\n }\n\n // R11. AN ADOPTED WINDOW IS CONTAINED TO A BOX INSIDE THE PANE,\n // not to the pane itself \u2014 a canvas ground sits within the leaf\n // wrap, under whatever chrome the embedder draws above it. Re-homing\n // such a window to the wrap would lift it out of its ground and\n // stand it over that chrome, which is a slower and stranger version\n // of the bug this whole function exists to prevent.\n const paneEl = rec.homeContainer\n ? (rec.homeContainer() || null)\n : this.renderer.leafEl(rec.homeLeafId);\n // NO PANE ELEMENT, ON THE ACTIVE DESKTOP, AFTER THE REPAIR ABOVE.\n // What is left is an embedder's ground that has not mounted yet and\n // a tree with no content leaf at all \u2014 neither of which is a window\n // to rescue. The sentence that used to stand here said \"closed, or\n // on another desktop \u2026 rather than orphaning it into a detached\n // node\", and both halves were wrong: the other desktop is handled\n // above, and by the time this ran on a CLOSED pane the node was\n // already detached \u2014 `_cleanCache` (`tile_renderer.js`) runs before\n // `onAfterRender`, not after it.\n if (!paneEl) continue;\n if (paneEl === rec.window.container) {\n // THE CONTAINER IS UNCHANGED AND THE ELEMENT IS NOT IN IT.\n //\n // The desktop rule above takes a window off the page it has\n // left by removing the ELEMENT; it deliberately leaves\n // `container` alone, because the wrap is still the window's\n // home and re-pointing it would lose that. Coming back through\n // a desktop SWITCH is safe \u2014 the wrap is evicted and rebuilt,\n // so `moveTo` sees a new container and appends. Coming back any\n // other way is not: `moveWindowToDesktop` and `removeDesktop`\n // re-point `desktopIdx` and render with the same tree on\n // screen, so the wrap is still cached, `moveTo` returns false\n // for an unchanged container, and the window stayed detached\n // for good \u2014 the exact state this whole pass exists to prevent,\n // reached through the desktop door instead of the close door.\n //\n // `appendChild` rather than `moveTo`: nothing about the\n // window's home has changed, so there is no clamp to redo, no\n // resize observer to rebuild and no `managed-window-moved` to\n // announce. It is the same element going back into the same\n // box.\n const el = rec.window.element;\n if (el && !el.isConnected) {\n try { paneEl.appendChild(el); }\n catch (err) { console.warn('[wm] re-attach failed', err); }\n }\n continue;\n }\n try { rec.window.moveTo(paneEl); }\n catch (err) { console.warn('[wm] re-home failed', err); }\n }\n }\n\n /**\n * C21, as a verb a consumer can call: put THIS window back where it belongs\n * and say whether it moved.\n *\n * The taskbar needs it. A minimised window's element may be out of the\n * document \u2014 its desktop is not on screen, or its pane was closed \u2014 and\n * un-minimising it in that state clears `isMinimized` (so its button\n * disappears, the last handle on it) while showing nothing. `restore` has\n * to be able to repair the window BEFORE it makes it visible, and\n * `_rehomeContainedWindows` is the thing that knows how; it was simply not\n * reachable, and `taskbar.js`'s own docstring asserted it ran for these\n * windows when the guard above meant it did not.\n *\n * IT SWITCHES DESKTOPS WHEN IT HAS TO, and that is the half a bare re-home\n * cannot do. A window belongs to one desktop; if that desktop is not on\n * screen, the honest answer to *show me this window* is the one every\n * taskbar in every window manager gives \u2014 go to where it lives. Restoring\n * it onto the page the user happens to be looking at would move a window\n * between pages as a side effect of asking to see it, and that is a tile\n * decision being made by a window verb.\n *\n * @param {object} win a live ManagedWindow\n * @returns {boolean} whether this WM owns it (and so has revealed it)\n */\n revealWindow(win) {\n if (!win) return false;\n let rec = null;\n for (const [, r] of this._windowToLeaf) {\n if (r.window === win) { rec = r; break; }\n }\n if (!rec) return false;\n // AN UNCONTAINED WINDOW HAS NO PAGE, SO THERE IS NOTHING TO REVEAL.\n // `_navigateWindow`'s windows (Alt+N, \"Open a copy in a window\") float\n // over the root and stay visible on every desktop; `desktopIdx` on such\n // a record is only where it happened to be OPENED. Switching to it\n // would throw the user off the page they are on to show them a window\n // that was already in front of them. Returning false hands the restore\n // back to the embedder, which is right for the canvas registry's\n // windows too.\n if (!rec.homeLeafId && !rec.homeContainer) return false;\n if (rec.desktopIdx !== this.desktops.activeIdx\n && this.desktops.desktops[rec.desktopIdx]) {\n // Renders, and the render re-homes \u2014 so there is nothing to do\n // afterwards, and doing it twice would be a second repaint.\n this.switchDesktop(rec.desktopIdx);\n } else {\n this._rehomeContainedWindows();\n }\n return true;\n }\n\n /**\n * R12. The rectangle an ESCAPED window may occupy \u2014 the tiles, and not the\n * panels \u2014 in the root's own coordinates.\n *\n * R1 let a window leave its pane so it could reach another one, and the\n * cheapest box to let it leave into is the root every tile shares. But the\n * root holds the docked panels too, so the bottom edge stopped being an\n * edge: a window could be dragged down over the bottom panel and dropped\n * there, half-covering a surface that has its own scroll and its own\n * chrome, with no way to tell it had happened except that it looked wrong.\n *\n * The answer is the UNION OF THE CONTENT LEAVES rather than \"the root minus\n * the panel I know about\": panels dock left, right and bottom, an embedder\n * may show any combination of them, and each one may be collapsed. A union\n * of the tiles is right for all of those without enumerating any of them,\n * and it degrades to the root when a desktop is somehow all panel.\n */\n _tileBounds() {\n const root = this.rootEl;\n if (!root) return null;\n const layer = this._layerRect();\n if (!layer) return null;\n const rootRect = root.getBoundingClientRect();\n // Into the host's coordinates. `left`/`top` are written against the\n // PADDING box, so the root's own border comes off as well \u2014 the same\n // conversion the escape does, and for the same reason.\n const ox = rootRect.left + root.clientLeft - root.scrollLeft;\n const oy = rootRect.top + root.clientTop - root.scrollTop;\n return { minX: layer.left - ox, minY: layer.top - oy,\n width: layer.width, height: layer.height };\n }\n\n /**\n * R13. THE LAYER, in the VIEWPORT pixels a hit-test speaks \u2014 the union of\n * the content leaves, before it is converted into anybody's coordinates.\n *\n * This is `_tileBounds` with the last step taken off, and it stays a\n * separate function rather than being folded back into it because the two\n * frames have different readers: `_tileBounds` answers `dragBounds`, which\n * `ManagedWindow._bounds()` uses to clamp a window in the ROOT's\n * coordinates, and this answers anything measuring against the page.\n *\n * R14 REMOVED ITS OTHER READER. R13's maximise preview was drawn from here\n * so that it would be the same measurement `toggleMaximize` would deliver\n * through `dragBounds` \u2014 C15's rule, THE PREVIEW MAY NOT PROMISE A\n * RECTANGLE THE DROP DOES NOT DELIVER, applied to the one mode that did not\n * dock. There is no such mode now: the top edge docks like every other\n * zone, its preview is the TILE (`_homeDockTarget`), and the layer's only\n * remaining job is the clamp. Kept as its own function because the clamp\n * still needs the union of the CONTENT leaves rather than the root, which\n * is a definition, not a call site.\n *\n * The union of the CONTENT LEAVES rather than the root, for the reason\n * `_tileBounds` gives at length: the root holds the docked panels too.\n *\n * Null when nothing has a box yet \u2014 a layout that has not happened, a\n * desktop whose tiles are all zero-sized. Every caller treats that as \"do\n * not promise anything\", which is the only honest answer available.\n */\n _layerRect() {\n let l = Infinity, tp = Infinity, r = -Infinity, b = -Infinity;\n for (const leaf of this._tree().leaves()) {\n if (PANEL_KINDS.has(leaf.content?.kind)) continue;\n const el = this.renderer.leafEl(leaf.id);\n if (!el) continue;\n const box = el.getBoundingClientRect();\n if (!box.width || !box.height) continue;\n l = Math.min(l, box.left); tp = Math.min(tp, box.top);\n r = Math.max(r, box.right); b = Math.max(b, box.bottom);\n }\n if (!Number.isFinite(l)) return null;\n return { left: l, top: tp, width: r - l, height: b - tp };\n }\n\n /** Dock the window's content back into the tile it came from \u2014 or, when it\n * came from none, into the desktop's primary tile \u2014 then close the window.\n *\n * R7. This is what the MAXIMIZE button now does, so it is reached far more\n * often than it was as a button of its own, and \"somewhere other than where\n * the window came from\" stopped being a defensible answer. Promoting a pane\n * re-seeds it with the root kind \u2014 ground for the window to stand on \u2014 so\n * FILLING that pane is the exact inverse: the content goes back where it\n * was lifted from, replacing the ground it has been standing on.\n *\n * A home pane that has since acquired content is a different story. The\n * user opened something there, and replacing it would destroy work the\n * window knows nothing about, so the content joins it as a tab instead.\n * With no home pane at all \u2014 an Alt+N window, or any window under an\n * embedder that does not confine promotions \u2014 this is the primary-tile tab\n * it has always been. */\n bringBackWindow(windowId) {\n const rec = this._windowToLeaf.get(windowId);\n // C28. IT SAYS SO WHEN IT DOES NOTHING. This resolves through\n // `_windowToLeaf` and is silent for a window the WM never built and\n // never adopted \u2014 correct, since there is no tile to bring such a\n // window back to. The `onMaximize` wrappers below returned `true`\n // regardless, so `toggleMaximize` treated the gesture as CLAIMED and\n // returned early, and the window neither docked nor maximised: the\n // button and the double-click both did nothing at all, which is a\n // dead control rather than a limitation anybody can read. Returning\n // false lets the geometric maximise happen instead \u2014 the answer every\n // other window manager gives a window with nowhere to go back to.\n if (!rec) return false;\n const tree = this.desktops.desktops[rec.desktopIdx]?.tree;\n const home = rec.homeLeafId ? tree?.get(rec.homeLeafId) : null;\n if (home && home.kind === 'leaf') {\n rec._dock = { leafId: rec.homeLeafId,\n mode: this._isStartTile(home) ? 'fill' : 'tab' };\n }\n rec._demoting = true;\n try { rec.window.close({ force: true }); }\n catch (err) { console.warn('[wm] bringBack: close failed', err); }\n return true;\n }\n\n /**\n * R11. ADOPT A WINDOW THE EMBEDDER BUILT ITSELF.\n *\n * Everything R1\u2013R10 gave a window \u2014 escaping its pane for the length of a\n * drag, going half-transparent once it is outside, the edge/body/ground\n * drops, maximise meaning *back to tile* \u2014 is wired in `_promote`, and so\n * belongs only to windows this WM lifted out of the tree. An embedder that\n * stands its own `ManagedWindow` on a pane (`snap: true` against the pane's\n * ground) got none of it: `_snapCommit` resolves the window through\n * `_windowToLeaf` and returns false for one it never built, so every drop\n * previewed correctly and then quietly did nothing.\n *\n * The fix is not to make the WM build those windows \u2014 the embedder has its\n * own reasons for the ones it builds, and taking that over would mean\n * taking over their content, their identity and their lifetime. It is to\n * let a window JOIN the tree's world after the fact, which needs exactly\n * two things: the drag options set on the component, and a record saying\n * what content to restore when the window is docked.\n *\n * CALL THIS BEFORE `show()`. `maximizeIcon` is read when the chrome is\n * built (`managed_window.js:703`) and the chrome is built lazily by `show`\n * (`:281`), so a window adopted afterwards would carry the right behaviour\n * behind a button still drawing a square.\n *\n * TEARDOWN STAYS THE EMBEDDER'S. `mountInfo` is optional and normally\n * omitted: a window that already destroys its own content in its `onClose`\n * would otherwise destroy it twice, once here and once there. The\n * embedder's handler is chained, not replaced, and runs after this one \u2014 so\n * a dock has already re-mounted the content into the tile by the time the\n * window's own teardown disposes of the copy that was floating.\n *\n * @param {object} win a live ManagedWindow, not yet shown\n * @param {object} spec\n * @param {string} spec.kind content kind to restore into a tile\n * @param {object} [spec.props] its props\n * @param {string} [spec.title] the tab title after a dock\n * @param {string} [spec.homeLeafId] the pane it stands on: what \"back to\n * tile\" targets, and what the probe stays silent inside\n * @param {function} [spec.homeContainer] `() => HTMLElement` \u2014 the box\n * WITHIN that pane the window is contained to. A canvas pane's\n * ground is not the leaf wrap, and re-homing to the wrap after a\n * repaint would lift the window out of the ground it belongs to.\n * @param {object} [spec.mountInfo] `{destroy}`, if teardown is ours\n * @returns {string|null} the window id, or null if it could not be adopted\n */\n adoptWindow(win, spec = {}) {\n const winId = win?.id;\n if (!winId || !spec.kind) return null;\n if (this._windowToLeaf.has(winId)) return winId;\n\n // The same bag `_promote` builds, and deliberately the same values: two\n // windows on one desktop behaving differently under the same gesture is\n // the kind of difference a user reads as a bug in whichever one they\n // tried second.\n if (this.snapPromotion) {\n win.snap = win.snap && win.canDrag && win.canResize;\n win.snapController = this._snapController();\n }\n win.dragHost = () => this.rootEl;\n win.dragBounds = () => this._tileBounds();\n win.onMaximize = () => this.bringBackWindow(winId);\n win.maximizeIcon = 'close_fullscreen';\n win.maximizeTitle = 'Back to tile';\n\n const original = { kind: spec.kind, props: spec.props || {},\n title: spec.title || spec.kind };\n this._windowToLeaf.set(winId, {\n leafId: null,\n desktopIdx: this.desktops.activeIdx,\n original,\n mountInfo: spec.mountInfo || null,\n window: win,\n contentEl: null, bodyEl: null, tabBarEl: null,\n tabs: [original], activeTabIdx: 0, strip: null,\n homeLeafId: spec.homeLeafId || null,\n homeContainer: spec.homeContainer || null,\n adopted: true,\n _demoting: false,\n });\n\n const prior = win.onClose;\n win.onClose = () => {\n this._onManagedWindowClosed(winId, null);\n prior?.();\n };\n return winId;\n }\n\n // \u2550\u2550 C15. Snap-to-promote \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * The snap controller a promoted window is given. It answers the two\n * questions ManagedWindow's own C11 snap cannot, because both are about a\n * tree it does not know exists:\n *\n * probe which TILE is under the pointer, and \u2014 since R15 \u2014 which edge\n * of it THE DRAGGED WINDOW'S OWN BORDERS have reached, and what\n * would dropping there actually produce: a half of that tile, a\n * quarter of the layer, the tile entire, or (R13, at the top edge\n * of the pane the window already stands on) the whole layer,\n * which is the one answer that is not a dock at all. The preview\n * draws exactly that rectangle, because a preview that promises a\n * half and delivers a quarter is worse than no preview.\n * commit put the window in the tree \u2014 or, for R13's maximise, leave it\n * floating and give it the layer. Over an EMPTY tile the dock is\n * unambiguous and happens on release. Over an OCCUPIED tile the\n * edges are unambiguous too \u2014 the drag chose a side, so the\n * side is the split \u2014 and only the CENTRE was ever genuinely a\n * question, which is why it is the zone that changed most.\n *\n * Built once and reused: the probe runs per pointermove and allocating a\n * closure per window per drag is free, but the memo keeps the identity\n * stable for anyone comparing controllers.\n */\n _snapController() {\n if (this._snapCtl) return this._snapCtl;\n this._snapCtl = {\n probe: (e, win) => this._snapProbe(e, win),\n commit: (probe, win) => this._snapCommit(probe, win),\n };\n return this._snapCtl;\n }\n\n /**\n * How close to a tile's edge the DRAGGED WINDOW'S matching edge must come\n * for a dock to arm \u2014 in PIXELS, and a narrow band. Since R15 it is also\n * the minimum distance the drag must have travelled toward that edge\n * inside the window's own pane; `_snapSide` argues both, and this is the\n * one constant either of them is measured in.\n *\n * This was a third of the tile, measured as a fraction, with the remaining\n * middle ninth treated as a fourth zone that offered a three-way choice.\n * Both halves of that were wrong, and together they made docking the\n * DEFAULT rather than a deliberate gesture:\n *\n * - A fraction means the band grows with the tile. On a maximised layer\n * a \"third\" is several hundred pixels, so a window could not be moved\n * anywhere near the left half of the screen without arming a split.\n * - The centre zone armed over the whole middle of every tile and\n * previewed the ENTIRE tile, so simply picking a window up and moving\n * it a few pixels lit the whole pane. Every move looked like a dock\n * because every move WAS one.\n *\n * Aero snap is an edge gesture: you push THE WINDOW at an edge \u2014 which is\n * what R15 finally made it measure. So the band is a fixed 28px from the\n * edge, and what lies past it is decided by the\n * pane rather than by the pointer: in the window's OWN pane the centre\n * arms nothing at all and the drop is simply a window that moved (R2), and\n * in any other pane it is the non-destructive tab or fill of R5/R6. The\n * band itself never grows with the tile, which is the whole of the fix.\n * Docking a whole tile is also still available without any drag at all \u2014\n * the \"back to tile\" button in the window's own chrome, which names the\n * destination instead of guessing it.\n */\n static get SNAP_EDGE_PX() { return 28; }\n\n _snapProbe(e, win) {\n const leafEl = this._leafElAt(e.clientX, e.clientY, win);\n if (!leafEl) return null;\n // R2, NARROWED BY R13. THE PANE IT CAME FROM ARMS AT ITS EDGES AND\n // NOWHERE ELSE \u2014 its centre still arms nothing at all.\n //\n // The rule was a blanket one: the origin pane armed nothing anywhere,\n // neither an edge nor the centre. It answered a real complaint \u2014 a\n // window that lives on a pane could not be nudged two pixels without\n // the pane lighting up to say it was about to swallow it \u2014 but it\n // answered it with more than the complaint asked for. A nudge happens\n // in the MIDDLE of a pane, which is where a window sits and where a\n // hand moving it goes; the 28px edge bands are somewhere a drag only\n // arrives on purpose \u2014 which under R15, where the bands are tested\n // against the WINDOW's borders and a window can already be sitting in\n // one before the hand touches it, is true only because `_snapSide`'s\n // direction guard makes it true. That guard is R2's other half and it\n // is why it applies in this pane and nowhere else.\n // So the centre is still silent, the nudge is still\n // fixed in full, and the edges of a window's own pane now mean what\n // they mean in every other pane \u2014 with one addition, TOP, which in the\n // pane a window already occupies has nothing to split and means\n // MAXIMISE instead (see below).\n //\n // DECIDED HERE, SPENT BELOW. `dragOrigin` is in hand at this point and\n // the answer cannot change while the probe runs, but which zone it\n // applies to is not known until `side` is. The origin is the window's\n // own answer (`dragOrigin` \u2014 the container its drag escaped), so the\n // two halves of the rule cannot disagree: the same boundary that turns\n // the window half-transparent is the one that arms the probe.\n //\n // A window with no origin \u2014 one opened straight into a float, which\n // never belonged to a pane \u2014 has no inside to be in, and every pane\n // under it is foreign. It arms everywhere, which is correct: there is\n // no \"just moving it around at home\" for a window with no home. It also\n // never maximises by this gesture, and that is not an omission \u2014 see\n // the maximise branch, which can only promise the layer for a window\n // whose bounds are the layer.\n //\n // CONTAINS, not equals. The WM confines its own promotions to the leaf\n // WRAP, but an embedder is free to confine a window to a box it built\n // inside the leaf \u2014 a canvas pane whose ground is a sibling of its own\n // taskbar strip does exactly that \u2014 and that window's home pane is the\n // leaf around it. `contains` is true of the element itself, so the\n // WM's own case is the same test.\n const own = !!(win?.dragOrigin && leafEl.contains(win.dragOrigin));\n const leafId = leafEl.dataset.leafId;\n // THE ACTIVE DESKTOP, and the dock has to agree. A promoted window is\n // global \u2014 it stays on screen across a desktop switch \u2014 so the tile\n // under the pointer belongs to whichever desktop is showing, not to the\n // one the window was promoted from. `_onManagedWindowClosed` applies\n // `_dock` against `rec.desktopIdx`, so the drop carries the index it\n // was made on and re-homes the record to it.\n const desktopIdx = this.desktops.activeIdx;\n const leaf = this._tree().get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return null;\n // Panel tiles are chrome, not content. A window dropped on the\n // navigator has nowhere to go and the preview must not suggest it has.\n if (String(leaf.content?.kind || '').startsWith('panel:')) return null;\n\n const r = leafEl.getBoundingClientRect();\n // R4, RE-AIMED BY R15. PIXELS FROM EACH EDGE, not fractions of the\n // tile \u2014 and the edges measured are now THE DRAGGED WINDOW'S, not the\n // pointer's. `_snapSide` owns that argument in full, including the\n // corner precedence and the one case that still falls back to the\n // pointer.\n //\n // One exclusion survives unchanged: a leaf with NO content at all has\n // no edges, because splitting nothing produces two nothings and the\n // preview would be drawing a half of a pane that has nothing to halve.\n const side = leaf.content ? this._snapSide(r, e, win, own) : null;\n // R14 (was R13). TOP, IN THE WINDOW'S OWN PANE, IS *BACK TO TILE*.\n //\n // Everywhere else `top` splits off the pane's upper half. In the pane\n // the window is already floating over, that is the one edge with\n // nothing to say \u2014 splitting a pane in order to put a window into the\n // half of it the window already covers is a gesture whose only effect\n // is work to undo. Aero's answer is the one worth copying: the top edge\n // is the \"make this as big as it goes\" edge.\n //\n // R13 read that as the GEOMETRIC maximise and made \"as big as it goes\"\n // mean THE LAYER. The product owner overruled it \u2014 *\"putting a window\n // to maximize > maximize here means back to tile\"* \u2014 and, asked how far\n // that went, chose everywhere, adding *\"this is not yet correct for the\n // snap at top gesture\"*, which names this zone as the part still wrong.\n // So there is now ONE meaning of maximise in the whole layer: the\n // window stops being a window and its content goes back into a tile.\n // The chrome button, the title bar's double-click, the embedder's\n // window menu and this edge are four doors onto one verb, which is the\n // only arrangement in which a user learns it once. No geometric\n // maximise remains reachable by gesture; `toggleMaximize({claimable:\n // false})` stays on `ManagedWindow` as the library escape hatch for a\n // consumer that wants the rectangle, and nothing in here calls it.\n //\n // THE RECT IS THE TILE, and it must be the tile the DOCK picks rather\n // than the tile the pointer happens to be in. That is C15's rule, which\n // R13 obeyed in the other direction, and it is why this branch resolves\n // its destination up front instead of committing blind:\n // `_homeDockTarget` is `bringBackWindow`'s own resolution read out, so\n // the preview and the drop cannot disagree \u2014 one function, called from\n // both halves.\n //\n // NOTHING TO PROMISE, NOTHING DRAWN. A window the WM never adopted, a\n // home leaf on a desktop that is not showing, a layout that has not\n // happened: `_homeDockTarget` answers null and the top edge falls\n // SILENT, which is exactly what the CENTRE of the window's own pane\n // already does (R2's surviving half, a dozen lines below). That is not\n // the dead-control failure the disabled-item convention exists to\n // avoid \u2014 the release still runs `_endDragEscape`, which puts the\n // window back where the drag found it, a visible outcome and the same\n // one the centre gives. Arming, previewing a tile and THEN declining on\n // release is the bait-and-switch, and it is the thing forbidden here.\n if (own && side === 'top') {\n const home = this._homeDockTarget(win);\n if (!home) return null;\n return { key: `${home.leafId}:home`, rect: home.rect,\n leafId: home.leafId, desktopIdx, side, mode: 'home',\n leafRect: r };\n }\n return this._dropZoneFor({ leafId, leaf, r, side, own, desktopIdx });\n }\n\n /**\n * R17 (C33). THE ZONE MATRIX'S TAIL \u2014 SPLIT / NOTHING / TAB / FILL \u2014 SHARED\n * BY THE TWO THINGS THAT CAN BE DROPPED ON A TILE.\n *\n * A dragged WINDOW and a dragged TAB ask the same question of a pane: given\n * that the pointer is in this leaf and the edge test answered `side`, what\n * would releasing here produce? Every answer below was written for the\n * window drop and every one of them is right for a tab, so this is an\n * extraction and not a generalisation \u2014 `_snapProbe` keeps everything ABOVE\n * it unchanged, including R14's `own && side === 'top'` home branch, which\n * is a window's alone (a tab has no window to bring back) and therefore\n * stays where it was, between the side computation and this call.\n *\n * The alternative was a second copy in `tabDropProbe`, and a second copy of\n * a matrix the product owner has already revised four times (R2, R4, R5/R6,\n * R14) is a guarantee that the two gestures will one day disagree about\n * what the centre of a start tile means. `web/js/shell/snap_zones.test.mjs`\n * in the Tables consumer asserts every cell of the window matrix and is the\n * regression gate on this extraction: byte-identical window behaviour is\n * the whole of its back-compatibility claim.\n */\n _dropZoneFor({ leafId, leaf, r, side, own, desktopIdx }) {\n if (side) {\n return { key: `${leafId}:${side}`, rect: _halfOf(r, side),\n leafId, desktopIdx, side, mode: 'split', leafRect: r };\n }\n // R2, THE HALF THAT SURVIVED. Past the edge bands, inside the pane it\n // came from, a window is being MOVED and not docked: no key, no\n // preview, and on release `_endDragEscape` simply puts it back where\n // the drag left it. This sits after the edges and before the centre\n // because it is only the centre it refuses \u2014 an own-pane leaf with no\n // content has no edges either (`side` stays null above), and refusing\n // that is right for the same reason: there is nothing to dock into that\n // the window is not already standing on.\n //\n // A TAB READS THIS CELL THE SAME WAY, arrived at from the other side:\n // dropping a tab into the pane it already lives in is a gesture whose\n // only effect is work to undo, which is R2's argument with the word\n // \"nudge\" removed.\n if (own) return null;\n\n // R5/R6. THE CENTRE IS NO LONGER NOTHING \u2014 it is the other two thirds\n // of the model, and which one it is depends on what the pane already is.\n //\n // a pane with content \u2192 a new TAB in it. Non-destructive, and the\n // honest reading of \"dropping where an\n // existing tile is would cause a new tab\".\n // a START tile \u2192 the window FILLS the pane, which is the\n // same verb as \"back to tile\": the window\n // stops floating and becomes that pane's\n // content. Under R14 that is what the top\n // edge of the window's OWN pane now means as\n // well \u2014 the difference between the two is\n // only which tile is named, this one the pane\n // under the pointer and R14's the pane the\n // window came from. A start tile is ground \u2014\n // there is nothing there to lose, which is\n // exactly why this one may replace rather\n // than append.\n //\n // The preview is the WHOLE pane for both, because the whole pane is\n // what the window ends up occupying either way. C15's rule stands: the\n // preview may not promise a rectangle the drop does not deliver.\n const fills = !leaf.content || this._isStartTile(leaf);\n return { key: `${leafId}:${fills ? 'fill' : 'tab'}`, rect: _halfOf(r, null),\n leafId, desktopIdx, side: null,\n mode: fills ? 'fill' : 'tab', leafRect: r };\n }\n\n /**\n * R18 (C33). THE SAME PROBE, FOR A DRAGGED TAB \u2014 PUBLIC, because the\n * renderer is what holds the drag and the renderer is not the WM.\n *\n * \u2550\u2550 WHY THIS IS NOT `_snapProbe(e, null)` \u2550\u2550\u2550\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 * It very nearly is, and the geometry underneath is literally the same\n * code: `_snapSide(r, e, null, false)` falls to the POINTER-distance branch\n * by construction \u2014 `_draggedRect(null)` is null, so `dist` takes the\n * `e.clientX/Y` arm and `along` scores every edge zero. A tab has no\n * rectangle being dragged and no `_dragState`, and that is not a gap to\n * paper over: the pointer IS the whole gesture for a tab, which is exactly\n * the pre-R15 model that `_snapSide`'s fallback preserves.\n *\n * Two things differ, and neither could be expressed by passing a null\n * window to `_snapProbe`:\n *\n * R14's HOME BRANCH IS A WINDOW'S. `own && side === 'top'` means \"put the\n * window back in its tile\", and a tab is already in a tile. Reaching that\n * branch with `win === null` would ask `_homeDockTarget(null)`, which\n * answers null, so the top edge of the source pane would fall silent\n * rather than split \u2014 a hole in the matrix produced by inheritance.\n *\n * A SINGLE-TAB SOURCE PANE ARMS NOTHING, ANYWHERE. `_dropZoneFor` already\n * silences the source pane's CENTRE; its edges are useful for a pane with\n * siblings (\"tear this tab off into a split beside the others\") and are a\n * wash for a pane with one tab, where the outcome is the pane's only\n * content in one half and a freshly seeded ground in the other. That is a\n * preview promising something no one wants, so it is refused BEFORE the\n * preview is drawn rather than at the drop \u2014 C15's rule is that the\n * rectangle drawn is the one released, and the honest way to keep it is\n * never to draw one.\n *\n * `own: false` is passed to `_snapSide` deliberately. Its `own` parameter\n * gates R2's direction guard, which measures a WINDOW's travel out of\n * `_dragState`; a tab drag has none, so `guarded` would be false anyway and\n * passing `true` would only obscure that. `own` still governs the centre,\n * which is why it goes to `_dropZoneFor` and not to `_snapSide`.\n *\n * @param {{clientX: number, clientY: number}} e the pointer, mid-drag\n * @param {{sourceLeafId?: string}} [opts] the leaf the tab left\n * @returns {object|null} the same probe shape a window drop produces\n */\n tabDropProbe(e, { sourceLeafId = null } = {}) {\n const leafEl = this._leafElAt(e.clientX, e.clientY, null);\n if (!leafEl) return null;\n const leafId = leafEl.dataset.leafId;\n const tree = this._tree();\n const leaf = tree.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return null;\n // Panel tiles are chrome, not content \u2014 the same refusal `_snapProbe`\n // makes, for the same reason: there is nowhere for the drop to go and\n // the preview must not suggest there is.\n if (String(leaf.content?.kind || '').startsWith('panel:')) return null;\n const own = !!sourceLeafId && leafId === sourceLeafId;\n if (own) {\n const src = tree.get(sourceLeafId);\n const count = Array.isArray(src?.tabs) ? src.tabs.length : 0;\n if (count <= 1) return null;\n }\n const r = leafEl.getBoundingClientRect();\n const side = leaf.content ? this._snapSide(r, e, null, false) : null;\n return this._dropZoneFor({ leafId, leaf, r, side, own,\n desktopIdx: this.desktops.activeIdx });\n }\n\n /**\n * R15. WHICH EDGE OF THE PANE THE *WINDOW* IS BEING PUSHED INTO.\n *\n * \u2550\u2550 THE BUG THIS EXISTS TO FIX \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * The band was measured from the POINTER, and the pointer is wherever the\n * hand happened to grab the title bar. Grab a 900px window in the middle\n * of its bar and shove it right: `dragBounds` clamps it, its right border\n * sits hard against the layer's right edge, and the pointer is still 450px\n * away from that edge \u2014 outside every band, so nothing arms and the window\n * simply stops dead against the side of the screen. Reported twice:\n * *\"snapping enables based on mouse position but actually it needs to\n * enable based on the dragged window bounds (e.g. window right border\n * distance from right snapping area)\"*.\n *\n * The bigger the window the worse it got, and the gesture only ever worked\n * if you happened to grab near the edge you were aiming at \u2014 the bottom\n * edge was effectively unreachable for any tall window, because a title bar\n * is at the TOP of the thing you are dragging.\n *\n * So each of the four distances is now between the window's own border and\n * the matching border of the pane. `right` arms when the window's right\n * border comes within the band of the pane's right border, and so on round.\n *\n * \u2550\u2550 WHAT DID *NOT* CHANGE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * WHICH PANE is still the pointer's answer (`_leafElAt`), and so is `own`.\n * The zone matrix is about a pane \u2014 the window's own pane means something\n * different from any other pane \u2014 and a window can lie across three of\n * them at once while the pointer is in exactly one. Only the question\n * *\"which edge of THIS pane\"* moved onto the window's rectangle; the\n * question *\"which pane\"* was never the one the product owner complained\n * about. Everything downstream is untouched: the preview is still\n * `_halfOf(paneRect, side)`, so the rectangle drawn is the rectangle the\n * drop delivers, and a `side` reaching the branches below means exactly\n * what it meant before.\n *\n * \u2550\u2550 SHORTFALL CLAMPED AT ZERO, BECAUSE A WINDOW OVERHANGS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * (R16 corrects R15 here. R15 said *unsigned*, and unsigned was wrong;\n * the paragraph below is why, and `_snapSide` carries the measurement.)\n *\n * The pointer is inside the pane by construction \u2014 `_leafElAt` found the\n * pane by hit-testing it \u2014 so a signed distance was always positive. A\n * WINDOW has no such guarantee: it is clamped to the layer, not to the\n * pane, so a window wider than the pane under the pointer sticks out of\n * both sides of it and its border is 20px PAST the pane's border rather\n * than 20px short of it. Both readings are \"hard against that edge\".\n *\n * R15 spelled that `Math.abs`, and `Math.abs` only holds the reading while\n * the overhang stays inside the band. Past that it counts UP again, so the\n * zone armed and then DISARMED as the shove continued, and a window\n * meaningfully wider than the pane armed nothing at all. The right spelling\n * is a shortfall clamped at zero: **past the edge IS the edge**, at\n * distance zero, and it stays there however far the shove carries it.\n *\n * \u2550\u2550 THE DIRECTION GUARD, WHICH IS WHAT KEEPS R2 ALIVE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * Edge-based testing has a failure the pointer never had: a window that is\n * ALREADY at an edge is in that band before the drag starts. A window\n * parked at the left of its pane would arm a left split on the first\n * millimetre of any drag, and a window that fills its pane would arm on\n * every drag in every direction \u2014 which is precisely the *\"every move\n * looked like a dock because every move WAS one\"* failure `SNAP_EDGE_PX`\n * was written to end, arriving from the other direction.\n *\n * So in the window's OWN pane an edge arms only if the drag actually\n * carried the window at it: the pointer must have travelled more than one\n * band's width toward that edge since the press. A nudge (R2's complaint,\n * and the surviving reason the own-pane centre is silent) moves a handful\n * of pixels and arms nothing; a shove moves hundreds and arms the edge it\n * was aimed at. The band's own width is the unit, because a movement\n * smaller than the band cannot be the difference between being in it and\n * not.\n *\n * IN ANY OTHER PANE THE GUARD IS OFF, deliberately. R2 is a rule about the\n * pane a window already lives on \u2014 the only place a \"nudge\" exists. Drag a\n * window rightwards out of pane A and into pane B and its LEFT border is\n * what enters pane B first: with the guard on, aiming at the left half of\n * the pane to your right would be impossible, since arriving there always\n * means travelling right. The window is translucent by then (R3) and every\n * drop on a foreign pane docks, so there is no nudge to protect.\n *\n * The displacement is read from `ManagedWindow._dragState.startX/startY`,\n * the POINTER's position at the press \u2014 not from the window's own x/y,\n * which stop changing the moment the clamp bites while the gesture very\n * much continues. A caller with no drag state (a synthetic probe, an\n * embedder driving the controller by hand) yields no displacement at all\n * and the guard is skipped rather than failing closed: it can only ever\n * suppress an edge, never invent one.\n *\n * \u2550\u2550 CORNERS: THE PRECEDENCE, MADE EXPLICIT \u2550\u2550\u2550\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 * Two edges can be in range at once, and with window borders that is no\n * longer the rarity it was with a pointer \u2014 shove a window into a corner\n * and the clamp puts BOTH borders at distance zero, exactly. Under R16 it\n * is not even a corner case: a window as wide as its pane is at zero on\n * the left AND the right for every horizontal position it can occupy, and\n * a floated canvas pane's window is *exactly* that wide. So the order is\n * stated rather than left to whichever way the loop happens to run:\n *\n * 1. NEAREST WINS. Unchanged from R4, and it is what keeps a corner from\n * being a dead spot: one of the two is always closer.\n * 2. ON A TIE, THE EDGE THE DRAG PUSHED TOWARD WINS. (R16: *toward that\n * edge*, signed \u2014 R15 said \"the axis pushed furthest\" and spelled it\n * `Math.abs`, which gives the two ends of one axis the SAME score, so\n * a left/right tie never broke at all and 'left' won every time by\n * loop order.) The honest tie-break is the gesture: shove it\n * rightwards and you get the right zone, upwards and you get the top\n * zone. Every zone stays reachable and which one you get is something\n * a hand can aim.\n * 3. STILL TIED \u2014 a perfect diagonal, or a probe with no drag state \u2014\n * falls to the fixed order left, right, top, bottom. That is the order\n * the R4 loop already resolved ties in (`Object.entries` insertion\n * order, with a strict `<`), kept so the pointer fallback below\n * answers exactly what it answered before.\n *\n * \u2550\u2550 THE FALLBACK \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * With no measurable window rectangle \u2014 no element, detached, or a box of\n * zero area because layout has not happened \u2014 there is nothing to measure\n * and the pointer is the only information in the room. That path is the\n * pre-R15 code, unchanged, signed distances and all. It is what a headless\n * probe gets (jsdom lays nothing out, so every `getBoundingClientRect` is\n * zero), and it is why `web/js/shell/snap_zones.test.mjs` in the Tables\n * consumer still asserts the same matrix against the same coordinates.\n *\n * @param {DOMRect} r the pane, in viewport pixels\n * @param {{clientX: number, clientY: number}} e the pointer\n * @param {object} win the ManagedWindow being dragged\n * @param {boolean} own is `r` the pane this window's drag escaped?\n * @returns {'left'|'right'|'top'|'bottom'|null}\n */\n _snapSide(r, e, win, own) {\n const edge = WindowManager.SNAP_EDGE_PX;\n const w = this._draggedRect(win);\n // The displacement is read whenever there is one, because BOTH rules\n // below want it and they do not want it in the same places: the corner\n // tie-break is about aim and applies in every pane, while the guard is\n // R2's rule about nudging a window at home and applies in one.\n const push = w ? this._dragPush(e, win) : null;\n const guarded = !!(own && push);\n // R16. SHORTFALL, CLAMPED AT ZERO \u2014 *not* `Math.abs`.\n //\n // R15 measured `Math.abs(r.right - w.right)`, which conflates the two\n // sides of an edge: a border 20px SHORT of the pane's border and one\n // 20px PAST it both read as 20. The reading past the edge is wrong,\n // and wrong in the direction that breaks the gesture. Two defects came\n // out of it and both are this one line of arithmetic:\n //\n // THE BAND OPENED AND THEN CLOSED AGAIN. Shove a window right; its\n // right border approaches the pane's right border, enters the 28px\n // band, reaches it \u2014 and keeps going. One pixel past, `abs` starts\n // counting UP again, and 28px past the edge the zone disarms.\n // Measured on the real code: a 300px window in PANE_A armed `right`\n // only for pointer deltas 173..227, and was null on either side. **A\n // firm shove disarmed the snap that a hesitant one armed** \u2014 the\n // product owner's original complaint arriving from the far side.\n //\n // AND A WINDOW WIDER THAN THE PANE ARMED NOTHING \u2014 though that case\n // is exactly what `abs` was reached for. The R15 docstring argues\n // for it because such a window \"sticks out of both sides and its\n // border is 20px PAST the pane's rather than 20px short\", which is\n // true; what it misses is that `abs` holds that reading only while\n // the overhang stays under 28px. Overhang by more and every edge\n // reads out-of-band.\n //\n // Clamping at zero says what that prose always meant: **past the edge\n // IS the edge.** A border level with the pane's, or beyond it, sits at\n // distance zero and is fully armed, and stays armed however much\n // further the shove carries it. \"How hard are you pushing past it\" was\n // never a measure of aim.\n //\n // This makes a same-axis TIE ordinary rather than a corner curiosity:\n // a window wider than its pane is hard against the left and the right\n // border at once, both at zero. Breaking that tie is what the signed\n // `along` below is for, and the two changes only make sense together.\n //\n // The pointer fallback is untouched: the pointer is inside the pane by\n // construction \u2014 `_leafElAt` hit-tested it \u2014 so its distances cannot\n // go negative and there is nothing to clamp.\n const dist = w\n ? { left: Math.max(0, w.left - r.left),\n right: Math.max(0, r.right - w.right),\n top: Math.max(0, w.top - r.top),\n bottom: Math.max(0, r.bottom - w.bottom) }\n : { left: e.clientX - r.left,\n right: r.right - e.clientX,\n top: e.clientY - r.top,\n bottom: r.bottom - e.clientY };\n const toward = (name) => {\n if (!guarded) return true;\n if (name === 'left') return push.x <= -edge;\n if (name === 'right') return push.x >= edge;\n if (name === 'top') return push.y <= -edge;\n return push.y >= edge;\n };\n // R16. HOW FAR THE GESTURE TRAVELLED **TOWARD THIS EDGE** \u2014 signed to\n // the edge it is asked about, not to its axis.\n //\n // R15 wrote `Math.abs(push.x)` for both 'left' and 'right', which\n // makes the tie-break IDENTICAL for the two members of a same-axis\n // pair. `p > best.p` is then `|push.x| > |push.x|` \u2014 false, always \u2014\n // so the tie fell through to the fixed loop order and **'left' won\n // every time, whichever way the window was shoved**.\n //\n // That was not the corner curiosity it looked like. Whenever a\n // window's width equals the pane's, `dist.left === dist.right` at\n // EVERY horizontal position, so the whole band answered 'left'; and a\n // canvas pane's window is sized `min(820, container.clientWidth)`,\n // which for any pane up to 822px wide comes out exactly equal to the\n // leaf's border-box width. So the common case \u2014 float a pane's window\n // and push it at the pane next door \u2014 always split left. Under R16's\n // clamped distance the tie is commoner still, because a window wider\n // than its pane now ties at zero on both edges by construction.\n //\n // Signing it makes the tie-break say what rule 2 always claimed:\n // *\"the axis the drag pushed furthest wins\"* \u2014 read as the DIRECTION\n // the drag pushed, which is the only reading a hand can aim. Shove\n // right, get right. A push away from the edge scores negative and\n // loses to any edge that was actually aimed at.\n //\n // Still zero for every edge when there is no drag state, so rule 3\n // falls through to the fixed order exactly as before.\n const along = (name) => {\n const dx = push?.x ?? 0, dy = push?.y ?? 0;\n if (name === 'left') return -dx;\n if (name === 'right') return dx;\n if (name === 'top') return -dy;\n return dy;\n };\n // THE FIXED ORDER IS RULE 3. Do not sort this array.\n let best = null;\n for (const name of ['left', 'right', 'top', 'bottom']) {\n const d = dist[name];\n if (!(d < edge) || !toward(name)) continue;\n const p = along(name);\n if (!best || d < best.d || (d === best.d && p > best.p)) best = { name, d, p };\n }\n return best ? best.name : null;\n }\n\n /** R15. The dragged window's rectangle in VIEWPORT pixels \u2014 the frame a\n * leaf's `getBoundingClientRect` speaks, so the two are directly\n * comparable \u2014 or null when there is nothing to measure.\n *\n * Read off the element rather than computed from `win.x/y/width/height`,\n * because those are in whatever container the window is currently parented\n * to and mid-drag that is the drag host, not the pane being probed.\n *\n * A zero-area box is \"nothing to measure\" rather than a rectangle at the\n * origin: it is what an unlaid-out document gives, and treating it as real\n * would put every window in the top-left corner of every pane. Same test\n * `_homeDockTarget` applies to a leaf, for the same reason. */\n _draggedRect(win) {\n const el = win?.element;\n if (!el || el.isConnected === false) return null;\n if (typeof el.getBoundingClientRect !== 'function') return null;\n const b = el.getBoundingClientRect();\n if (!b || !b.width || !b.height) return null;\n return b;\n }\n\n /** R15. How far the POINTER has travelled since the press that began this\n * drag, or null if this window is not in a drag the WM can see.\n *\n * The pointer rather than the window: `_applyPosition` clamps the window\n * to `dragBounds`, so a window shoved at the edge of the layer stops\n * moving while the gesture continues \u2014 and \"it stopped because it is\n * against the edge\" is exactly the situation the guard must not read as\n * \"it is not being pushed\". */\n _dragPush(e, win) {\n const ds = win?._dragState;\n if (!ds || typeof ds.startX !== 'number' || typeof ds.startY !== 'number') return null;\n return { x: e.clientX - ds.startX, y: e.clientY - ds.startY };\n }\n\n /** The topmost `.twm-leaf` under the pointer that is not part of the window\n * being dragged. `elementsFromPoint` rather than `elementFromPoint`: the\n * dragged window IS under the pointer \u2014 it is what the pointer is holding\n * \u2014 and a hit-test that stops at the first element only ever finds it.\n *\n * R15 left this alone on purpose: WHICH pane is still the pointer's\n * answer, and only WHICH EDGE of it moved onto the window's borders. See\n * `_snapSide`. */\n _leafElAt(x, y, win) {\n const stack = document.elementsFromPoint(x, y);\n for (const el of stack) {\n if (win?.element && win.element.contains(el)) continue;\n const leafEl = el.closest?.('.twm-leaf');\n if (leafEl && this.rootEl.contains(leafEl)) return leafEl;\n }\n return null;\n }\n\n /**\n * R14. THE TILE \"BACK TO TILE\" WOULD PUT THIS WINDOW IN, and the rectangle\n * that draws it \u2014 in the VIEWPORT pixels a snap preview is positioned in.\n *\n * `bringBackWindow` resolves its destination privately and then closes the\n * window to reach it, which is everything a button press needs and useless\n * to a PREVIEW. C15's rule is that the rectangle drawn during a drag is the\n * one the drop delivers, and under R14 the drop delivers A TILE \u2014 so the\n * resolution has to be readable before the gesture is committed. Reading it\n * out here is what makes the top edge honest: the probe draws what this\n * returns and `_snapCommit` calls `bringBackWindow`, which resolves the\n * same way, from the same record, against the same tree.\n *\n * It is deliberately NOT a second copy of that resolution reduced to \"the\n * home leaf\". `bringBackWindow`'s fall-through \u2014 no home leaf, or one the\n * tree no longer has \u2014 is the desktop's PRIMARY tile, which is where\n * `_onManagedWindowClosed` sends a demotion carrying no `_dock`; a probe\n * that previewed the home leaf and then landed in the primary tile would be\n * the bait-and-switch with extra steps.\n *\n * `renderer.leafEl` only knows the leaves of the desktop currently on\n * screen, which is the property that makes the desktop check implicit: a\n * window whose home is on another desktop resolves to no element, this\n * answers null, and the top edge arms nothing rather than previewing a\n * rectangle on a desktop the user cannot see.\n *\n * @param {object} win a live ManagedWindow\n * @returns {{leafId: string, rect: DOMRect}|null} null when there is\n * nothing honest to promise: a window the WM never adopted, a desktop\n * that has gone, a tree with no content leaf at all, or a leaf with no\n * measurable box (a layout that has not happened yet).\n */\n _homeDockTarget(win) {\n const rec = [...this._windowToLeaf.values()].find((r) => r.window === win);\n if (!rec) return null;\n const tree = this.desktops.desktops[rec.desktopIdx]?.tree;\n if (!tree) return null;\n const home = rec.homeLeafId ? tree.get(rec.homeLeafId) : null;\n const leafId = (home && home.kind === 'leaf')\n ? rec.homeLeafId\n : tree.primaryLeafId();\n if (!leafId) return null;\n const el = this.renderer.leafEl(leafId);\n if (!el) return null;\n const rect = el.getBoundingClientRect();\n if (!rect.width || !rect.height) return null;\n return { leafId, rect };\n }\n\n _snapCommit(probe, win) {\n if (!probe) return false;\n const rec = [...this._windowToLeaf.entries()]\n .find(([, r]) => r.window === win);\n if (!rec) return false;\n const [winId, record] = rec;\n const { leafId, desktopIdx, side } = probe;\n\n // R14. THE TOP EDGE OF THE WINDOW'S OWN PANE DOCKS IT HOME.\n //\n // It goes through `bringBackWindow` rather than through\n // `dockWindowInto` below, and that is the point rather than a shortcut:\n // \"back to tile\" is ONE verb with one implementation, and it is the\n // implementation that knows a home pane which has since acquired\n // content must be joined as a tab instead of replaced. Naming the leaf\n // here and calling `dockWindowInto` would be a second copy of that\n // rule, and a second copy is a copy that disagrees within a release.\n //\n // IT RETURNS BEFORE `record.desktopIdx` IS REWRITTEN below, for the\n // reason R13's branch did: the destination was resolved from the\n // RECORD's own desktop by `_homeDockTarget`, so rewriting the record to\n // the desktop the pointer is on would send `bringBackWindow` looking\n // for the home leaf in a tree that does not contain it \u2014 and it would\n // then fall through to that desktop's primary tile, which is not the\n // rectangle the preview drew.\n //\n // WHAT USED TO BE HERE: `toggleMaximize({ claimable: false })`, the\n // geometric maximise, with `{claimable: false}` load-bearing precisely\n // so the call would NOT reach `bringBackWindow`. The ruling inverts\n // that \u2014 the dock is now the whole intent \u2014 so the escape hatch is\n // gone from this file altogether. It stays on `ManagedWindow` as a\n // public API for a consumer that genuinely wants a rectangle (a dialog\n // has no tile to go back to, so `openModal`'s maximise is exactly\n // that); nothing in the tiling layer calls it any more.\n if (probe.mode === 'home') {\n // NOT TAKEN UNLESS IT REALLY WAS \u2014 the same rule the dock below\n // follows, and reachable here: `bringBackWindow` returns false for\n // a window whose record has gone between the probe and the release.\n // Claiming the drop after it did nothing would leave the preview\n // painted over the page with nothing left to remove it, and the\n // window abandoned wherever the pointer let go; returning false\n // hands the release back to `_endDragEscape`, which puts the window\n // back in its pane.\n if (!this.bringBackWindow(winId)) return false;\n win.clearSnapPreview();\n return true;\n }\n\n // The window's home follows the drop. Without this the dock is applied\n // to the tree of the desktop the window was PROMOTED from, and the tile\n // the user aimed at is on the one they are looking at.\n if (typeof desktopIdx === 'number') record.desktopIdx = desktopIdx;\n\n // THE DRAG ALREADY ANSWERED, so there is nothing to ask \u2014 the probe\n // decided between the outcomes on the way in, and the preview has\n // been drawing that answer for as long as the pointer has been there. A\n // menu on release would be asking a question the user has already spent\n // the whole drag answering.\n //\n // `dir` and `before` are read only by the split branch; a tab or a fill\n // ignores them, which is why they can be computed unconditionally from\n // a `side` that is null for both.\n const docked = this.dockWindowInto(winId, {\n leafId,\n mode: probe.mode || 'split',\n dir: (side === 'left' || side === 'right') ? 'h' : 'v',\n before: (side === 'left' || side === 'top'),\n });\n // NOT TAKEN unless it really was. A truthy return tells the window the\n // controller owns it AND owns the preview, so returning true after a\n // failed dock would leave a rectangle painted over the page with\n // nothing left to remove it, and a window sitting wherever the drag\n // abandoned it instead of back in its pane.\n if (!docked) return false;\n win.clearSnapPreview();\n return true;\n }\n\n /**\n * Put a floating window's content back into the tree at a NAMED place.\n *\n * `bringBackWindow` is this with `{ mode: 'tab' }` against the primary tile\n * \u2014 the answer when the user pressed a button in the window's own chrome\n * and named no destination. A drop names one.\n *\n * The window is CLOSED to do it, exactly as a demote is: the content\n * factory re-mounts inside the tile, and a factory that must not lose live\n * state across that boundary is the embedder's problem to solve (it is why\n * the registry is keyed on (kind, props) rather than on a DOM node).\n *\n * @param {string} windowId\n * @param {{leafId: string, mode: 'fill'|'tab'|'split', dir?: 'h'|'v',\n * before?: boolean}} target\n */\n dockWindowInto(windowId, target) {\n const rec = this._windowToLeaf.get(windowId);\n if (!rec || !target?.leafId) return false;\n rec._dock = { ...target };\n rec._demoting = true;\n try { rec.window.close({ force: true }); }\n catch (err) { console.warn('[wm] dock: close failed', err); return false; }\n return true;\n }\n\n /**\n * Move a managed window to another desktop.\n *\n * \u2550\u2550 IT USED TO REWRITE ONE INTEGER, AND THAT MOVED NOTHING \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * The sentence that stood here \u2014 *\"the window itself stays on screen\n * (managed windows are global)\"* \u2014 was true of a window floating over the\n * root and false of every window this WM promotes under `promoteInPlace`,\n * which is CONTAINED IN A TILE (C21). Rewriting `desktopIdx` left such a\n * window standing in the pane it was already in, on the page the user was\n * already looking at: *Move to desktop Views* appeared to do nothing at\n * all. Then *Back to tile* resolved against the new desktop's tree and\n * docked the content onto a page nobody was watching, so the window\n * vanished here and its table turned up over there.\n *\n * Three things move it for real. The index, so every later resolution\n * agrees. The HOME LEAF, re-pointed at a ground that exists in the\n * destination \u2014 without it the record names a leaf of the tree it just\n * left, and `_rehomeContainedWindows` would either skip it forever or\n * repair it to a pane on the wrong page. And a render, which is where the\n * element is taken off the page the window has left (or parented into its\n * new ground, when the destination is the desktop on screen).\n *\n * An ADOPTED window (`homeContainer`) keeps its own resolution: the\n * embedder owns the box it stands in, and re-pointing a leaf id it does not\n * read would be a change with no effect wearing the look of one.\n */\n moveWindowToDesktop(windowId, targetIdx) {\n const rec = this._windowToLeaf.get(windowId);\n if (!rec) return;\n if (rec.desktopIdx === targetIdx) return;\n this.desktops.ensureCount(targetIdx + 1);\n rec.desktopIdx = targetIdx;\n if (!rec.homeContainer && rec.homeLeafId) {\n const target = this.desktops.desktops[targetIdx]?.tree;\n // `|| null`, NEVER `|| rec.homeLeafId`. Falling back to the id it\n // already had keeps a leaf of the tree the window is LEAVING, and\n // `_rehomeContainedWindows` would then find that id perfectly alive\n // in the wrong tree and never repair it. Null means \"not contained\n // anywhere\", which the pass understands and which the repair below\n // fixes on the first render of the destination.\n rec.homeLeafId = target?.primaryLeafId() || null;\n }\n this.renderer.render();\n this._persist();\n this._notifyChange('window-moved');\n }\n\n /**\n * R8. Put a floated pane's tabs back into a leaf \u2014 ALL of them, in the\n * order they had, with the one that was showing still showing.\n *\n * Every dock goes through here, and that is the point: `bringBackWindow`,\n * a drop on a tile's body, a drop on an edge and the fall-through when the\n * named destination vanished are four routes to one question \u2014 *where do\n * these tabs go* \u2014 and four copies of the answer would disagree about the\n * third one within a release. A window promoted before R8 (or by\n * `_navigateWindow`, which never had tabs) carries no list, so `original`\n * is the fallback and the single-tab path reduces to exactly what this\n * replaced.\n *\n * `replace` is the difference between filling a leaf and joining one: a\n * fresh split leaf and a `fill` drop want the first tab to BECOME the\n * leaf's content, while a `tab` drop and \"back to tile\" append beside what\n * is already there.\n */\n _restoreTabs(tree, leafId, rec, { replace }) {\n if (!leafId) return false;\n const tabs = (Array.isArray(rec.tabs) && rec.tabs.length)\n ? rec.tabs\n : [{ kind: rec.original.kind, props: rec.original.props,\n title: rec.original.title }];\n const active = Math.max(0, Math.min(tabs.length - 1, rec.activeTabIdx || 0));\n let firstIdx = -1;\n tabs.forEach((tab, i) => {\n const content = { kind: tab.kind, props: tab.props || {} };\n const title = tab.title || tab.kind || '';\n if (i === 0 && replace) {\n tree.setLeafContent(leafId, content, title);\n firstIdx = 0;\n return;\n }\n // `appendLeafTab` returns -1 for a panel leaf, which is the one\n // leaf that never grows tabs. Nothing docks onto a panel \u2014 the\n // probe refuses one and `bringBackWindow` targets the primary tile\n // \u2014 so this is a guard, not a path.\n const at = tree.appendLeafTab(leafId, content, title);\n if (at >= 0 && firstIdx < 0) firstIdx = at;\n });\n if (firstIdx < 0) return false;\n tree.setActiveLeafTab(leafId, firstIdx + active);\n tree.focus(leafId);\n return true;\n }\n\n _onManagedWindowClosed(winId, mountInfo) {\n const rec = this._windowToLeaf.get(winId);\n this._windowToLeaf.delete(winId);\n if (!rec) return;\n // R8. `rec.mountInfo` IS THE LIVE ONE. The window's mount changes when\n // its tab changes (`_mountWindowTab`) and when its content is replaced\n // in place (`openInWindow`), so the closure `_promote` captured at\n // build time names a mount that was torn down long ago \u2014 destroying it\n // twice while the CURRENT one is never destroyed at all, which is a\n // leaked page module with a `window` listener on it. `_navigateWindow`\n // still passes its own and it is the same object, so the argument\n // stays for the callers that have nothing else.\n try { (rec.mountInfo || mountInfo)?.destroy?.(); } catch {}\n try { rec.strip?.dispose(); } catch { /* never mounted, or already gone */ }\n const tree = this.desktops.desktops[rec.desktopIdx]?.tree;\n if (!tree) return;\n\n if (rec._demoting && rec._dock) {\n // C15. A DROP named its destination, so none of the primary-tile\n // reasoning below applies. A destination that vanished between the\n // release and the close (the tile was closed by a keystroke while a\n // choice menu was open) falls through to the primary tile rather\n // than dropping the content on the floor.\n const target = tree.get(rec._dock.leafId);\n if (target && target.kind === 'leaf') {\n const { mode, dir, before } = rec._dock;\n if (mode === 'tab') {\n this._restoreTabs(tree, rec._dock.leafId, rec, { replace: false });\n } else if (mode === 'split') {\n const newId = tree.split(rec._dock.leafId, dir);\n if (newId) {\n this._restoreTabs(tree, newId, rec, { replace: true });\n // THE PREVIEW PROMISED HALF OF THAT TILE. `TileTree.split`\n // delivers it only when it has to WRAP; when the parent\n // split already runs this direction it INSERTS a sibling\n // and gives it the average of the row's existing sizes\n // (`tile_tree.js`, case 2), so a drop into a three-pane\n // row hands the newcomer a third and shrinks everyone\n // else. Halving the source's own weight and giving the\n // other half to the new leaf makes the two panes split\n // the space the source had \u2014 which is what was drawn.\n _halveInto(tree, rec._dock.leafId, newId);\n // `split` always appends the new leaf AFTER its source.\n // A drop on the LEFT or TOP edge means the window\n // belongs on that side, so the two swap places \u2014 the\n // preview drew the left half and the left half is where\n // it must land.\n if (before) _swapSiblings(tree, rec._dock.leafId, newId);\n tree.focus(newId);\n }\n } else {\n this._restoreTabs(tree, rec._dock.leafId, rec, { replace: true });\n }\n } else {\n this._restoreTabs(tree, tree.primaryLeafId(), rec, { replace: false });\n }\n } else if (rec._demoting) {\n // \"Back to tile\": the source tile was closed when the window was\n // promoted, so dock the (latest) content into the desktop's\n // primary tile as a new tab, spawning a tile when it has none.\n let pid = tree.primaryLeafId();\n let spawned = false;\n if (!pid) { pid = this._spawnContentLeaf(tree); spawned = true; }\n if (pid) {\n this._restoreTabs(tree, pid, rec, { replace: false });\n if (spawned) this._canonicalize(tree, this.desktops.desktops[rec.desktopIdx]);\n }\n }\n // Plain close (rec._demoting === false): the window and its content\n // are discarded. The source tile was already closed on promote, so\n // there is nothing left in the tree to clean up.\n if (this.desktops.active().tree === tree) this.renderer.render();\n this._persist();\n this._notifyChange(rec._demoting ? 'window-demoted' : 'window-closed');\n }\n\n /** Post-show DOM hook: wire a right-click context menu on the topbar.\n *\n * R7. IT USED TO INJECT A BUTTON HERE, and that is the whole of what\n * changed. \"Back to tile\" was a fourth button squeezed left of Close,\n * built by reaching into four of ManagedWindow's internal class names \u2014\n * the coupling C6 exists to avoid \u2014 and it sat next to a MAXIMIZE button\n * that did the one thing a window lifted out of a tile has no use for.\n * Now the maximize button IS \"back to tile\" (`onMaximize`, passed where\n * the window is built), so the verb has one control instead of two and\n * this hook has no markup of its own to keep in step.\n *\n * Gone with it: the rule that hid MINIMIZE while the window was maximised.\n * It existed because minimising a full-screen window strands it \u2014 nothing\n * on screen points at it any more \u2014 and a window that cannot maximise\n * cannot be in that state at all. `managed-window-maximized` (C16) still\n * fires for everyone else, and `--suppressed` is still styled for the next\n * consumer that needs to hide one of these. */\n _decorateManagedWindow(win, winId) {\n const topbar = win.element?.querySelector?.('.twm-managed-window__topbar');\n if (!topbar) return;\n\n topbar.addEventListener('contextmenu', (e) => {\n e.preventDefault();\n const rec = this._windowToLeaf.get(winId);\n if (!rec) return;\n const items = [\n { label: 'Back to tile', icon: 'close_fullscreen', action: 'back' },\n ];\n if (this.desktops.desktops.length > 1) {\n items.push({ separator: true });\n for (const [i, d] of this.desktops.desktops.entries()) {\n if (i === rec.desktopIdx) continue;\n items.push({\n label: `Move to desktop ${d.label}`, icon: 'sweep',\n action: `move:${i}`,\n });\n }\n }\n items.push({ separator: true });\n items.push({ label: 'Close window', icon: 'close',\n action: 'close', danger: true });\n showContextMenu(e.clientX, e.clientY, items, (action) => {\n if (action === 'back') this.bringBackWindow(winId);\n else if (action === 'close') {\n try { win.close({ force: true }); } catch {}\n }\n else if (action?.startsWith?.('move:')) {\n this.moveWindowToDesktop(winId, Number(action.slice(5)));\n }\n });\n });\n }\n\n // \u2550\u2550 R10. Adjacent start tiles are one start tile \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n /**\n * Merge every run of side-by-side START TILES into one.\n *\n * A start tile is a leaf holding the taxonomy ROOT \u2014 the pane a fresh\n * desktop opens with, and the pane `_seedHome` puts back when a tile is\n * emptied. It is not a document: it is the ground, the empty canvas, the\n * \"nothing is open here\" surface. So two of them side by side are ONE\n * surface with a splitter drawn through it for no reason, and the splitter\n * is worse than decoration \u2014 it offers to resize a boundary between two\n * things that are the same thing.\n *\n * This is deliberately NOT run on every tree change, and the reason is\n * `split()`: splitting a start tile seeds the new pane with the root kind\n * too (the never-empty-tile invariant), so a merge on every mutation would\n * undo an Alt+H the instant it happened. It runs where the product owner\n * put it \u2014 *\"when a maximized (tiled) panel is window-ized, all adjacent\n * non-panel (start tile) tiles get merged to one\"* \u2014 and is public so an\n * embedder that empties a pane its own way can ask for the same tidy-up.\n *\n * THREE THINGS ARE NEVER MERGED, and each one is a way to lose work:\n *\n * - `panel:*` leaves. They are chrome, not content; the navigator is not\n * a start tile and a panel BETWEEN two start tiles means those two are\n * not adjacent.\n * - A start tile with WINDOWS STANDING ON IT. The whole point of the\n * surface is that things float on it, and closing the leaf takes its\n * ground \u2014 and every window clamped to it \u2014 out of the document. When\n * one of a pair is occupied the other merges INTO it; when both are,\n * neither moves.\n * - A start tile holding tabs, live or archived. `leaf.content.kind`\n * names the ACTIVE tab only, and a pane whose other tabs are tables, or\n * whose `pageTabs` archive holds the three tables a rail click put\n * there, is a pane with work in it wearing a start tile's face.\n *\n * @param {TileTree} tree\n * @param {string|null} preferLeafId the leaf to keep when a run is\n * otherwise a free choice \u2014 the pane the caller just emptied, so the\n * merged surface is the one the user is looking at.\n * @returns {number} how many leaves were absorbed.\n */\n _mergeStartTiles(tree, preferLeafId = null) {\n if (!tree) return 0;\n let absorbed = 0;\n // Pairwise, restarting after each close. `TileTree.close` splices the\n // parent's children AND may collapse the parent into ITS parent, so\n // every index in flight is stale the moment one leaf goes; restarting\n // is the cheap way to be right rather than the clever way to be wrong.\n // It terminates because each pass removes exactly one leaf.\n for (;;) {\n const pair = this._nextMergeablePair(tree, preferLeafId);\n if (!pair) break;\n const { split, keepIdx, dropIdx, keepId, dropId } = pair;\n // THE SPACE GOES WITH IT. `close` splices the size out of the row,\n // which hands it to every sibling in proportion; the merged pane is\n // supposed to occupy what the two of them occupied, so the keeper\n // takes it first.\n split.sizes[keepIdx] = (split.sizes[keepIdx] || 1) + (split.sizes[dropIdx] || 1);\n const hadFocus = tree.focusedLeafId === dropId;\n tree.close(dropId);\n // A LOOP THAT CANNOT END IS WORSE THAN A SPLITTER NOBODY WANTED.\n // The loop's termination argument is \"each pass removes one leaf\";\n // if `close` ever declines \u2014 a leaf that is not a leaf, a tree\n // mutated underneath us \u2014 that argument fails silently and the tab\n // freezes. Stopping is the correct answer to a merge that did not\n // happen.\n if (tree.nodes.has(dropId)) break;\n if (hadFocus) tree.focus(keepId);\n absorbed += 1;\n }\n return absorbed;\n }\n\n /** The first two adjacent start tiles that may be merged, and which of\n * them survives. Null when there are none. */\n _nextMergeablePair(tree, preferLeafId) {\n for (const node of [...tree.nodes.values()]) {\n if (node.kind !== 'split') continue;\n // The snapshot above can name a split a previous pass collapsed.\n if (!tree.nodes.has(node.id)) continue;\n for (let i = 0; i < node.children.length - 1; i += 1) {\n const a = tree.get(node.children[i]);\n const b = tree.get(node.children[i + 1]);\n if (!this._isStartTile(a) || !this._isStartTile(b)) continue;\n const aHolds = this._paneHoldsWindows(a.id);\n const bHolds = this._paneHoldsWindows(b.id);\n if (aHolds && bHolds) continue;\n let keepIdx = i;\n if (bHolds) keepIdx = i + 1;\n else if (!aHolds && node.children[i + 1] === preferLeafId) keepIdx = i + 1;\n const dropIdx = keepIdx === i ? i + 1 : i;\n return { split: node, keepIdx, dropIdx,\n keepId: node.children[keepIdx], dropId: node.children[dropIdx] };\n }\n }\n return null;\n }\n\n /** Is this leaf the empty ground and nothing else? See the three\n * exclusions in `_mergeStartTiles`. */\n _isStartTile(leaf) {\n if (!leaf || leaf.kind !== 'leaf') return false;\n const kind = leaf.content?.kind;\n if (!kind || kind !== this.taxonomy.root) return false;\n if (PANEL_KINDS.has(kind) || kind === PLACEHOLDER_KIND) return false;\n if ((Array.isArray(leaf.tabs) ? leaf.tabs.length : 0) > 1) return false;\n for (const page of Object.values(leaf.pageTabs || {})) {\n if (Array.isArray(page?.tabs) && page.tabs.length) return false;\n }\n return true;\n }\n\n /**\n * Does anything float on this pane?\n *\n * Two sources, because there are two kinds of window and the WM only knows\n * about one of them. `homeLeafId` is set for a window this WM contained in\n * its own pane (C21); an EMBEDDER's windows \u2014 a canvas pane that opens its\n * own `ManagedWindow` against the pane's ground \u2014 are not in\n * `_windowToLeaf` at all, and the only honest way to see them is to look.\n * The DOM answer covers both, and covers a window whose record has been\n * dropped but whose element is still standing.\n */\n _paneHoldsWindows(leafId) {\n for (const [, rec] of this._windowToLeaf) {\n if (rec.homeLeafId === leafId) return true;\n }\n const el = this.renderer.leafEl?.(leafId);\n return !!el?.querySelector?.('.twm-managed-window');\n }\n\n /** R10, as a verb an embedder can use. Merges, then repaints and persists\n * \u2014 the promote path calls `_mergeStartTiles` directly because it is\n * already going to do all three. */\n mergeStartTiles(preferLeafId = null) {\n const tree = this._tree();\n const absorbed = this._mergeStartTiles(tree, preferLeafId);\n if (!absorbed) return 0;\n this._canonicalize(tree, this.desktops.active());\n this.renderer.render();\n this._persist();\n this._notifyChange('start-tiles-merged');\n return absorbed;\n }\n\n // \u2500\u2500 Panel-tiles (left nav / right / bottom) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n /** Panels are virtual: they live in the active desktop's tree as\n * leaves with content kinds 'panel:left', 'panel:right',\n * 'panel:bottom'. Toggling either inserts them at the appropriate\n * edge (a recursive split) or closes that leaf. */\n isPanelOpen(side) {\n const d = this.desktops.active();\n return !!d.panels[side];\n }\n\n togglePanel(side) {\n const d = this.desktops.active();\n d.panels[side] = !d.panels[side];\n this._canonicalize(this._tree(), d);\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n\n /** Rebuild the tree so panel:* leaves sit in the canonical positions:\n *\n * v-split\n * \u251C h-split \u2190 the \"content row\"\n * \u2502 \u251C panel:left (if open)\n * \u2502 \u251C content (whatever non-panel subtree)\n * \u2502 \u2514 panel:right (if open)\n * \u2514 panel:bottom (if open, full width below the content row)\n *\n * Existing panel leaves are detached + re-attached (same id), so the\n * renderer cache keeps the mounted content (nav filter state, etc.). */\n _canonicalize(tree, desktop) {\n // 1. Find existing panel leaves.\n const panel = {};\n for (const leaf of tree.leaves()) {\n const kind = leaf.content?.kind;\n if (kind === 'panel:left') panel.left = leaf;\n else if (kind === 'panel:right') panel.right = leaf;\n else if (kind === 'panel:bottom') panel.bottom = leaf;\n }\n\n // 2. Detach each panel leaf from its current parent. (We don't\n // use tree.close because that deletes the node; we want to\n // preserve identity so the renderer keeps the mounted body.)\n const detach = (leaf) => {\n if (!leaf) return;\n const pid = leaf.parentId;\n if (!pid) {\n tree.rootId = null;\n leaf.parentId = null;\n return;\n }\n const parent = tree.get(pid);\n if (!parent) return;\n const idx = parent.children.indexOf(leaf.id);\n if (idx >= 0) {\n parent.children.splice(idx, 1);\n parent.sizes.splice(idx, 1);\n }\n leaf.parentId = null;\n tree._collapse(parent);\n };\n detach(panel.left); detach(panel.right); detach(panel.bottom);\n\n // 3. The content subtree may legitimately be empty \u2014 the user\n // is allowed to close the last main tile and let panels\n // take over the entire WM area. If so, seed root from the\n // first requested panel; otherwise content stays as root.\n let seededSide = null;\n if (!tree.rootId) {\n seededSide = desktop.panels.left ? 'left'\n : desktop.panels.bottom ? 'bottom'\n : desktop.panels.right ? 'right'\n : null;\n if (seededSide) {\n const seed = panel[seededSide] || makeLeaf({\n content: { kind: `panel:${seededSide}`, props: {} },\n title: PANEL_TITLES[seededSide] || seededSide,\n });\n if (panel[seededSide] && PANEL_TITLES[seededSide]) {\n panel[seededSide].title = PANEL_TITLES[seededSide];\n }\n if (!panel[seededSide]) tree._register(seed);\n if (!tree.nodes.has(seed.id)) tree.nodes.set(seed.id, seed);\n seed.parentId = null;\n tree.rootId = seed.id;\n }\n // else: nothing requested; tree stays empty (renderer shows\n // the \"Empty desktop\" placeholder).\n }\n\n // 4. Wrap content row with left + right (h-split), then wrap\n // that with bottom (v-split) so bottom spans full width.\n const wrap = (panelLeaf, side) => {\n // Re-register the leaf if it was orphaned by detach.\n if (panelLeaf && !tree.nodes.has(panelLeaf.id)) {\n tree.nodes.set(panelLeaf.id, panelLeaf);\n }\n const leaf = panelLeaf || makeLeaf({\n content: { kind: `panel:${side}`, props: {} },\n title: PANEL_TITLES[side] || side,\n });\n // Heal old leaves whose stored title is still 'left'/etc.\n if (panelLeaf && PANEL_TITLES[side] && panelLeaf.title !== PANEL_TITLES[side]) {\n panelLeaf.title = PANEL_TITLES[side];\n }\n if (!panelLeaf) tree._register(leaf);\n const before = (side === 'left');\n const dir = (side === 'bottom') ? 'v' : 'h';\n const children = before ? [leaf.id, tree.rootId] : [tree.rootId, leaf.id];\n const sizes = (side === 'bottom') ? [4, 1]\n : (side === 'left') ? [1, 4]\n : [4, 1];\n const split = {\n id: `split-panel-${side}-${Math.random().toString(36).slice(2, 7)}`,\n kind: 'split', parentId: null, dir, children, sizes,\n };\n tree.nodes.set(split.id, split);\n const oldRoot = tree.get(tree.rootId);\n if (oldRoot) oldRoot.parentId = split.id;\n leaf.parentId = split.id;\n tree.rootId = split.id;\n };\n // Skip the side already consumed as the seed root (would\n // otherwise wrap a fresh duplicate leaf around the existing\n // panel-as-root).\n if (desktop.panels.left && seededSide !== 'left' && tree.rootId) wrap(panel.left, 'left');\n if (desktop.panels.right && seededSide !== 'right' && tree.rootId) wrap(panel.right, 'right');\n if (desktop.panels.bottom && seededSide !== 'bottom' && tree.rootId) wrap(panel.bottom, 'bottom');\n\n // GC orphaned nodes (panel leaves whose `panels.X` flipped to\n // false stayed in tree.nodes after detach).\n const reachable = new Set();\n const walk = (id) => {\n if (!id || reachable.has(id)) return;\n reachable.add(id);\n const n = tree.get(id);\n if (n?.kind === 'split') n.children.forEach(walk);\n };\n walk(tree.rootId);\n for (const id of [...tree.nodes.keys()]) {\n if (!reachable.has(id)) tree.nodes.delete(id);\n }\n }\n\n _findPanelLeaf(side) {\n const kind = `panel:${side}`;\n for (const leaf of this._tree().leaves()) {\n if (leaf.content?.kind === kind) return leaf;\n }\n return null;\n }\n\n // \u2500\u2500 Desktops \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 switchDesktop(idx) {\n this.desktops.ensureCount(idx + 1);\n if (!this.desktops.switchTo(idx)) return;\n const d = this.desktops.active();\n // Runtime-created desktops (ensureCount / addDesktop) are bare\n // home-leaf trees whose panels haven't been materialized yet \u2014\n // only desktops present at load() were canonicalized. Without\n // this, the new desktop shows no panels while the toggle buttons\n // (which read `d.panels`) report them open. Canonicalize on every\n // switch: it's idempotent for already-canonical trees.\n this._canonicalize(d.tree, d);\n this.renderer.tree = d.tree;\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n\n addDesktop() {\n this.desktops.addDesktop();\n this._notifyChange();\n this._persist();\n }\n\n /** Step to the previous (-1) or next (+1) existing desktop, wrapping\n * around. No-op with a single desktop. Iterates only desktops that\n * already exist \u2014 use switchDesktop(idx) to create-on-demand. */\n cycleDesktop(dir) {\n const m = this.desktops;\n const n = m.desktops.length;\n if (n <= 1) return;\n const next = (m.activeIdx + (dir < 0 ? -1 : 1) + n) % n;\n this.switchDesktop(next);\n }\n\n /** Cycle the focused tile's main-panel tabs (Ctrl+Tab / Ctrl+Shift+Tab).\n * Wraps; no-op when the focused leaf has 0 or 1 tabs. */\n cycleFocusedTab(dir) {\n const tree = this._tree();\n const leafId = tree.focusedLeafId;\n const leaf = leafId ? tree.get(leafId) : null;\n const tabs = (leaf && Array.isArray(leaf.tabs)) ? leaf.tabs : [];\n if (tabs.length <= 1) return;\n const cur = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));\n const next = (cur + (dir < 0 ? -1 : 1) + tabs.length) % tabs.length;\n this._leafTabAction(leafId, 'switch', { idx: next });\n }\n\n /**\n * Remove a desktop \u2014 AND RE-INDEX THE WINDOWS, which is the half that was\n * missing.\n *\n * `desktopIdx` on a window record is an ARRAY INDEX into `desktops`, so a\n * splice silently re-points every record above the removed one at its\n * neighbour. Nothing threw and nothing looked wrong: the window kept\n * floating, and the next *Back to tile* resolved `rec._dock` against the\n * WRONG TREE. `_onManagedWindowClosed` reads `desktops[rec.desktopIdx]`,\n * finds a tree that never held this window, and its `if (!tree) return`\n * closes the window and drops the content on the floor \u2014 staged edits\n * included, with no error and nothing on screen to say a table was lost.\n *\n * Windows homed on the desktop being removed do not die with it. The\n * ruling is that a tile operation may move a window and never destroy it,\n * and removing a desktop is the largest tile operation there is: they come\n * across to the desktop that ends up active, re-homed onto its ground by\n * `_rehomeContainedWindows` on the render below.\n */\n removeDesktop(idx) {\n const m = this.desktops;\n if (m.desktops.length <= 1) return false;\n if (idx < 0 || idx >= m.desktops.length) return false;\n m.desktops.splice(idx, 1);\n // THE SPLICE MOVES THE ACTIVE DESKTOP TOO, and the clamp below never\n // said so. Removing a desktop BELOW the one you are on shifts it down\n // by one, so an unchanged `activeIdx` then names its neighbour: the\n // desktop you were working in is replaced on screen by the next one\n // along, the bar highlights the wrong button, and the wrong index is\n // persisted. The clamp only ever caught removing the LAST desktop.\n // Pre-existing, and load-bearing for the loop below, which reads\n // `activeIdx` to decide where the orphaned windows go.\n if (idx < m.activeIdx) m.activeIdx -= 1;\n if (m.activeIdx >= m.desktops.length) m.activeIdx = m.desktops.length - 1;\n // AFTER the splice and AFTER `activeIdx` is repaired, so the survivors\n // resolve against the array the rest of this method uses.\n for (const [, rec] of this._windowToLeaf) {\n if (rec.desktopIdx === idx) {\n rec.desktopIdx = m.activeIdx;\n // ONLY A WINDOW THAT WAS ALREADY CONTAINED, which is what\n // `rec.homeLeafId` says and what `moveWindowToDesktop`'s guard\n // tests. `!rec.homeContainer` alone also catches a FREE-FLOATING\n // window \u2014 `_navigateWindow`'s, which has neither field \u2014 and\n // handing one a `homeLeafId` silently converts it into a\n // pane-contained window: it jumps into a tile, can no longer be\n // dragged outside it, and is then removed from the document by\n // the desktop rule the first time you switch pages.\n if (!rec.homeContainer && rec.homeLeafId) {\n rec.homeLeafId = m.active().tree.primaryLeafId() || null;\n }\n } else if (rec.desktopIdx > idx) {\n rec.desktopIdx -= 1;\n }\n }\n this.renderer.tree = m.active().tree;\n this.renderer.render();\n this._persist();\n this._notifyChange();\n return true;\n }\n\n moveFocusedToDesktop(idx) {\n const tree = this._tree();\n const focused = tree.focused();\n if (!focused || !focused.content) return;\n // C20. IT DESTROYS A LEAF, SO IT ANSWERS TO THE SAME VETO `closeFocused`\n // does. A move is not a close from the pane's point of view, but it is\n // exactly a close from the PAGE's: this was the fourth door out of a\n // content veto, and the one nobody thought to look at because its label\n // says \"Move\".\n const closeChrome = this.renderer?.leafChrome?.(focused.id)?.close;\n if (closeChrome === false || closeChrome?.disabled === true) return;\n const payload = { kind: focused.content.kind, props: focused.content.props, title: focused.title };\n this.desktops.ensureCount(idx + 1);\n const target = this.desktops.desktops[idx];\n // Place into the target tree's primary leaf.\n const targetTree = target.tree;\n const primary = targetTree.primaryLeafId();\n if (primary) targetTree.setLeafContent(primary, { kind: payload.kind, props: payload.props }, payload.title);\n // Remove from current desktop.\n tree.close(focused.id);\n if (!tree.rootId) tree.setRoot(makeLeaf(this._rootLeaf()));\n // THE SAME NEVER-EMPTY-CONTENT INVARIANT `closeFocused` KEEPS, and it\n // was missing here. `!tree.rootId` above only catches a tree with\n // nothing left at all; with a panel open the root survives and the page\n // is left showing chrome and no ground \u2014 so the windows that were\n // standing on the moved pane had nowhere to be re-homed to.\n if (!tree.leaves().some((l) => !String(l.content?.kind || '').startsWith('panel:'))) {\n const spawned = this._spawnContentLeaf(tree);\n if (spawned) this._seedHome(tree, spawned);\n }\n this._canonicalize(tree, this.desktops.active());\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n\n // \u2500\u2500 Internals \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 _tree() { return this.desktops.active().tree; }\n\n _leafAction(leafId, action) {\n const tree = this._tree();\n tree.focus(leafId);\n if (action === 'close') this.closeFocused();\n else if (action === 'promote') this.toggleManagedFocused();\n // Chrome split buttons mirror Alt+H / Alt+V: structural split of\n // this tile into a fresh empty pane (the user then fills it).\n else if (action === 'split-h') this.split('h');\n else if (action === 'split-v') this.split('v');\n }\n\n /** Tab-strip event dispatcher. The renderer fires actions\n * (`switch` / `close` / `move` / `open-menu`) and the WM\n * translates them into tile-tree mutations + a re-render. */\n _leafTabAction(leafId, action, data = {}) {\n const tree = this._tree();\n const leaf = tree.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return;\n if (action === 'switch') {\n tree.setActiveLeafTab(leafId, data.idx);\n tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-switch');\n return;\n }\n if (action === 'close') {\n const tabs = leaf.tabs || [];\n // Closing the last remaining tab closes the tile \u2014 same\n // behavior as Alt+W on a single-tab leaf today.\n if (tabs.length <= 1) {\n tree.focus(leafId);\n this.closeFocused();\n return;\n }\n tree.removeLeafTab(leafId, data.idx);\n tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange('twm-tab-close');\n return;\n }\n if (action === 'move') {\n tree.moveLeafTab(leafId, data.from, data.to);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-move');\n return;\n }\n if (action === 'menu') {\n this._showTabContextMenu(leafId, data.idx, data.x, data.y);\n return;\n }\n if (action === 'to-window') {\n // R9. `floatTabAsWindow` renders, persists and notifies on its own\n // \u2014 it is the same promote path the pane's chrome uses \u2014 so there\n // is deliberately nothing after it here.\n this.floatTabAsWindow(leafId, data.idx);\n return;\n }\n if (action === 'drop-into') {\n // C33. The tab-drop's one door into the tree. Fired against the\n // SOURCE leaf, because `_leafTabAction`'s first argument is always\n // the leaf whose tab is being acted on \u2014 the destination travels in\n // `data.target`, which is a `tabDropProbe` answer.\n //\n // Like `to-window` above, `moveTabInto` renders, persists and\n // notifies on its own, so there is deliberately nothing after it.\n this.moveTabInto(leafId, data.idx, data.target);\n return;\n }\n if (action === 'close-others') {\n tree.closeOtherTabs(leafId, data.idx);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-close-others');\n return;\n }\n if (action === 'close-right') {\n tree.closeTabsAfter(leafId, data.idx);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-close-right');\n return;\n }\n if (action === 'close-left') {\n tree.closeTabsBefore(leafId, data.idx);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-close-left');\n return;\n }\n if (action === 'open-menu') {\n // Defer to a per-WM callback if anyone wired one (the\n // hamburger menu mounts via this hook). The renderer hands\n // us screen coordinates so the menu can anchor below the\n // hamburger button.\n try {\n this.ctx?.onTileTabMenu?.(leafId, data.x, data.y);\n } catch (err) {\n console.warn('[wm] tab menu hook failed', err);\n }\n }\n }\n\n /** Central navigation entry point. Every nav action \u2014 left menu,\n * breadcrumb, landing-row click, in-tile reference, panel\n * reference, entity-to-entity \u2014 should route through this so the\n * one holistic routing concept lives in one place.\n *\n * A link declares its target as TWO axes:\n *\n * `opts.dest` \u2014 WHERE the content lands:\n * 'main' \u2192 the primary content tile (default).\n * The nav panel always uses this.\n * 'origin' \u2192 the tile/window the click came from\n * (needs `ctx`). Breadcrumbs use this so\n * a segment click stays in the current\n * tile (and inside a floating window when\n * windowed).\n * 'window' \u2192 a fresh managed window.\n * `opts.newTab` \u2014 HOW it lands in that destination:\n * false \u2192 replace the destination's active tab\n * (default). true \u2192 append a NEW tab.\n * Ignored for 'window' (one window = one\n * content).\n *\n * `opts.ctx` \u2014 the caller's mount context (`leafId`,\n * `windowId`); required for `dest:'origin'`.\n * `opts.transient` \u2014 the appended tab is not persisted/restored\n * (e.g. an add-row form). Only meaningful with\n * `newTab:true`.\n *\n * Back-compat: the legacy `opts.target` enum still works and maps\n * onto the axes \u2014 'auto'\u2192origin, 'tab'\u2192origin+newTab,\n * 'primary'\u2192main, 'window'\u2192window. Prefer the two-axis form.\n *\n * The lower-level primitives (`openFromContext`,\n * `openInTabFromContext`, `openInTabInPrimary`, `openInPrimary`,\n * `openInWindow`) stay internal; callers prefer `wm.navigate(...)`. */\n navigate(kind, props = {}, opts = {}) {\n const { ctx = null, transient = false } = opts;\n // Resolve the two axes, honoring the legacy `target` alias.\n let { dest = 'main', newTab = false } = opts;\n if (opts.target != null) {\n switch (opts.target) {\n case 'window': dest = 'window'; newTab = false; break;\n case 'primary': dest = 'main'; newTab = false; break;\n case 'tab': dest = 'origin'; newTab = true; break;\n case 'split-h': dest = 'split-h'; break;\n case 'split-v': dest = 'split-v'; break;\n case 'auto':\n default: dest = 'origin'; newTab = false; break;\n }\n }\n // Split destinations: split the originating tile (or, lacking a\n // tile context, the focused tile) and mount a fresh instance in\n // the new pane. This is the routing for the code-pane \"open in\n // horizontal/vertical split\" buttons.\n if (dest === 'split-h' || dest === 'split-v') {\n const dir = dest === 'split-h' ? 'h' : 'v';\n const leafId = ctx?.leafId || this._tree().focusedLeafId;\n return this.splitLeafWith(leafId, dir, kind, props, _tabTitle(kind, props));\n }\n if (dest === 'window') return this._navigateWindow(kind, props);\n if (dest === 'main') {\n return newTab\n ? this.openInTabInPrimary(kind, props, transient)\n : this.openInPrimary(kind, props);\n }\n // dest === 'origin'\n return newTab\n ? this._navigateTab(ctx, kind, props, transient)\n : this._navigateAuto(ctx, kind, props);\n }\n\n _navigateAuto(ctx, kind, props) {\n // Windowed content \u2192 replace window in place.\n if (ctx?.windowId && this._windowToLeaf.has(ctx.windowId)) {\n this.openInWindow(ctx.windowId, kind, props);\n return;\n }\n // In-tile content \u2192 replace ACTIVE TAB only (preserves siblings).\n // Panel tiles (left/right/bottom) fall through \u2014 their clicks\n // target the primary content tile, not the panel itself.\n if (ctx?.leafId) {\n const tree = this._tree();\n const leaf = tree.get(ctx.leafId);\n const k = leaf?.content?.kind;\n if (leaf && k && !k.startsWith('panel:')) {\n this.openInLeaf(ctx.leafId, kind, props);\n return;\n }\n }\n // Outside the tile (top-nav, panel, palette) \u2192 primary tile,\n // page-aware swap that preserves per-page tab archives.\n this.openInPrimary(kind, props);\n }\n\n _navigateTab(ctx, kind, props, transient = false) {\n // Windows aren't tabbed \u2014 \"open in tab\" inside a window just\n // replaces the window's content.\n if (ctx?.windowId && this._windowToLeaf.has(ctx.windowId)) {\n this.openInWindow(ctx.windowId, kind, props);\n return;\n }\n this.openInTabFromContext(ctx || {}, kind, props, transient);\n }\n\n /** Spawn a fresh ManagedWindow with the requested content. No\n * source leaf \u2014 closing the window just disposes the content. */\n _navigateWindow(kind, props) {\n const winId = `twm-mw-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 5)}`;\n const contentEl = document.createElement('div');\n contentEl.className = 'twm-window-content';\n contentEl.style.cssText = 'display:flex; flex-direction:column; flex:1; min-width:0; min-height:0; height:100%;';\n const title = _tabTitle(kind, props);\n const mountInfo = this.content.mount(kind, contentEl, props,\n { ...this.ctx, wm: this, windowId: winId });\n const win = new ManagedWindow({\n id: winId,\n title: mountInfo?.title || title,\n icon: 'web_asset',\n content: contentEl,\n canMinimize: true,\n canMaximize: true,\n canResize: true,\n modal: false,\n // C15, and this is the SECOND of the two places the WM builds a\n // window. Alt+N and \"Open in new window\" produce a window that is\n // every bit as dockable as a promoted one, and a window that can be\n // dragged onto a tile in one case and not the other is a rule\n // nobody can learn.\n snap: this.snapPromotion,\n snapController: this.snapPromotion ? this._snapController() : null,\n // R1. THE PANE IS A BOX WITH `overflow: hidden`. A window contained\n // to one (C21) cannot be dragged a single pixel outside it, so\n // \"drag a window from one tile to another\" \u2014 the gesture all three\n // drop behaviours are built on \u2014 was not merely awkward, it was\n // invisible. For the length of a drag the window is re-parented\n // here, to the root every tile is inside; on release it goes back\n // into a pane, either the one it was dropped on or the one it came\n // from. Resolved per drag: the root outlives any tile, and a tile\n // grabbed once does not survive its own repaint.\n dragHost: () => this.rootEl,\n dragBounds: () => this._tileBounds(),\n // R7. MAXIMISE MEANS BACK TO TILE. This window came OUT of the\n // tree; the useful thing to do with it is put it back, and filling\n // the screen with it is the one gesture that makes putting it back\n // harder. So the maximize button docks \u2014 and the separate demote\n // button the WM used to inject beside it is gone, because two\n // buttons for one verb is how you get a chrome nobody reads.\n onMaximize: () => this.bringBackWindow(winId),\n maximizeIcon: 'close_fullscreen',\n maximizeTitle: 'Back to tile',\n onClose: () => this._onManagedWindowClosed(winId, mountInfo),\n });\n // DECORATED ONCE, below, after the record exists. It was called here as\n // well, so every Alt+N window carried two topbar context-menu handlers\n // \u2014 and, until R7 removed it, two \"back to tile\" buttons.\n // leafId is null \u2014 `_onManagedWindowClosed` already short-circuits\n // both branches when there's no source leaf, so the close path\n // just disposes the content and drops the map entry.\n this._windowToLeaf.set(winId, {\n leafId: null,\n desktopIdx: this.desktops.activeIdx,\n original: { kind, props: { ...(props || {}) },\n title: mountInfo?.title || title },\n mountInfo, window: win, contentEl,\n _demoting: false,\n });\n win.show();\n // Cascade: a fresh window centers by default, so opening one from\n // another window would land exactly on top \u2014 reading as the\n // calling window being replaced. Offset by the current window\n // count (cycling every 6) so each new window is slightly inset\n // from the last.\n const _n = this._windowToLeaf.size; // includes this new window\n if (_n > 1 && typeof win._applyPosition === 'function') {\n const step = ((_n - 1) % 6) * 28;\n win.x = (win.x || 0) + step;\n win.y = (win.y || 0) + step;\n win._applyPosition();\n }\n this._decorateManagedWindow(win, winId);\n this._notifyChange('window-spawned');\n }\n\n /** Browser-style tab context menu. Items reflect the leaf's current\n * tab list \u2014 \"Close others\" is hidden when only one tab is open,\n * \"Close to the right / left\" are hidden at the edges. The actual\n * mutations route back through `_leafTabAction` so persistence +\n * notify stay in one place. */\n _showTabContextMenu(leafId, idx, x, y) {\n const tree = this._tree();\n const leaf = tree.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return;\n const tabs = leaf.tabs || [];\n if (tabs.length === 0) return;\n const items = [];\n // R9. THE PER-TAB VERB LIVES ON THE TAB.\n //\n // Floating one tab out is what the pane's chrome button used to do, and\n // it was the wrong home for it: the button is on the PANE and named\n // \"float this pane as a window\", so it now floats the pane (R8) and\n // this is where the single-tab version went. A user who wants one of\n // three tables in a window right-clicks that table's tab, which is\n // where every other per-tab verb already is.\n //\n // Offered above the close verbs, and separated from them: it is the\n // only item here that does not destroy something, and a menu whose\n // first four entries all close things teaches the eye to skip it.\n const tab = tabs[idx];\n if (tab && !String(tab.kind || '').startsWith('panel:')\n && tab.kind !== PLACEHOLDER_KIND) {\n items.push({ label: 'Open in a window', icon: 'web_asset', action: 'to-window' });\n items.push({ separator: true });\n }\n items.push({ label: 'Close tab', icon: 'close', action: 'close' });\n if (tabs.length > 1) {\n items.push({ label: 'Close other tabs', icon: 'tab_close', action: 'close-others' });\n }\n if (idx < tabs.length - 1) {\n items.push({ label: 'Close tabs to the right', icon: 'chevron_right',\n action: 'close-right' });\n }\n if (idx > 0) {\n items.push({ label: 'Close tabs to the left', icon: 'chevron_left',\n action: 'close-left' });\n }\n showContextMenu(x, y, items, (action) => {\n this._leafTabAction(leafId, action, { idx });\n });\n }\n\n /** Open content in a new tab on the leaf where the call originated.\n * Mirrors `openFromContext` (windowed / split-leaf / primary\n * routing) but uses `appendLeafTab` so the existing content\n * stays in place as a tab. */\n openInTabFromContext(ctx, kind, props = {}, transient = false) {\n // Managed-window content: just open in the window \u2014 managed\n // windows aren't tabbed (one window = one content).\n if (ctx?.windowId && this._windowToLeaf.has(ctx.windowId)) {\n this.openInWindow(ctx.windowId, kind, props);\n return;\n }\n const tree = this._tree();\n let leafId = ctx?.leafId;\n if (!leafId) leafId = tree.primaryLeafId();\n const leaf = leafId ? tree.get(leafId) : null;\n const k = leaf?.content?.kind;\n if (!leaf || !leafId || (k && k.startsWith('panel:'))) {\n // Panel tiles can't host content tabs \u2014 fall back to the\n // primary leaf (matches openFromContext fall-through).\n this.openInPrimary(kind, props);\n return;\n }\n tree.appendLeafTab(leafId, { kind, props }, _tabTitle(kind, props), { transient });\n tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-open');\n }\n\n /** Open content as a NEW tab in the primary content tile, regardless\n * of where the call came from (the `dest:'main', newTab:true` path).\n * Unlike `openInTabFromContext`, this never falls back to a replace:\n * it targets the primary leaf directly and always appends, so a\n * click from outside the tile system (e.g. the bottom-panel\n * \"Add row\" button, which passes no ctx) reliably lands as a sibling\n * tab in the main tile rather than swapping its content. */\n openInTabInPrimary(kind, props = {}, transient = false) {\n const tree = this._tree();\n const leafId = tree.primaryLeafId();\n // No content tile on this desktop (e.g. a panels-only layout) \u2014\n // open in a managed window rather than spawning a bare tile. The\n // caller asked for \"a tab in the main tile\"; with no main tile to\n // tab into, a floating window is the least-surprising fallback.\n if (!leafId) { this._navigateWindow(kind, props); return; }\n tree.appendLeafTab(leafId, { kind, props }, _tabTitle(kind, props), { transient });\n tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-open');\n }\n}\n\n\n/** Derive a tab's display title from the content it carries \u2014 entity\n * label first, then id, then the bare kind. Used everywhere a tab is\n * created, so the bar names what the user opened rather than the kind\n * of thing it is. The rename-bus listener in the WM keeps these in\n * sync when the underlying entity is renamed. */\nfunction _tabTitle(kind, props) {\n if (props && typeof props.label === 'string' && props.label) return props.label;\n if (props && (typeof props.id === 'string' || typeof props.id === 'number')\n && String(props.id)) return String(props.id);\n return kind || '';\n}\n\n/** R8. A leaf's tabs as the plain specs a window record holds \u2014 deep enough\n * that editing one afterwards cannot reach back into the tree, which matters\n * because a floated tab keeps being edited (`openInWindow` renames it) while\n * the leaf it came from is still alive. A leaf with no tab list at all is\n * pre-tabs data; its mirrored `content`/`title` are the one tab it has. */\nfunction _leafTabSpecs(leaf) {\n const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];\n if (tabs.length) {\n return tabs.map((t) => ({\n kind: t.kind, props: { ...(t.props || {}) },\n title: t.title || t.kind || '',\n }));\n }\n return [{\n kind: leaf.content.kind,\n props: { ...(leaf.content.props || {}) },\n title: leaf.title || leaf.content.kind || '',\n }];\n}\n\n/** One half of a rectangle, in the same viewport pixels it arrived in. This is\n * what makes the drop preview honest: the rectangle a `left` drop on a tile\n * produces is half of THAT TILE, which \u2014 when the tile is itself half the\n * layer \u2014 is a quarter of the layer, and the preview says so. */\nfunction _halfOf(r, side) {\n const w = Math.round(r.width / 2);\n const h = Math.round(r.height / 2);\n switch (side) {\n case 'left': return { left: r.left, top: r.top, width: w, height: r.height };\n case 'right': return { left: r.left + r.width - w, top: r.top, width: w, height: r.height };\n case 'top': return { left: r.left, top: r.top, width: r.width, height: h };\n case 'bottom': return { left: r.left, top: r.top + r.height - h, width: r.width, height: h };\n default: return { left: r.left, top: r.top, width: r.width, height: r.height };\n }\n}\n\n/** Give `newId` half of `sourceId`'s share of their shared split, leaving every\n * other pane in the row untouched. This is what \"the preview showed half of\n * that tile\" means once the split is an insertion rather than a wrap. */\nfunction _halveInto(tree, sourceId, newId) {\n const src = tree.get(sourceId);\n if (!src) return false;\n const parent = tree.get(src.parentId);\n if (!parent || parent.kind !== 'split') return false;\n const i = parent.children.indexOf(sourceId);\n const j = parent.children.indexOf(newId);\n if (i < 0 || j < 0) return false;\n // The wrap case already splits a fresh two-child node evenly; only the\n // insertion case has a size to correct, and correcting an even one is a\n // no-op anyway.\n const share = (parent.sizes[i] ?? 1) / 2;\n parent.sizes[i] = share;\n parent.sizes[j] = share;\n return true;\n}\n\n/** Swap two sibling leaves inside their shared split. `TileTree.split` always\n * appends, and \"the window goes on the left\" is a legitimate outcome of a\n * drop; this is the one-line difference between the two. */\nfunction _swapSiblings(tree, aId, bId) {\n const a = tree.get(aId);\n const b = tree.get(bId);\n if (!a || !b || a.parentId !== b.parentId) return false;\n const parent = tree.get(a.parentId);\n if (!parent || parent.kind !== 'split') return false;\n const i = parent.children.indexOf(aId);\n const j = parent.children.indexOf(bId);\n if (i < 0 || j < 0) return false;\n parent.children[i] = bId;\n parent.children[j] = aId;\n // `sizes` IS PARALLEL TO `children` \u2014 `makeSplit` builds them together and\n // `TileTree.split` splices both at the same index. Swapping one and not the\n // other does not swap two panes, it makes them trade widths in place, which\n // looks like the drop landing on the correct side and then resizing itself\n // for no reason. `TileTree.moveDir` swaps both; so does this.\n const size = parent.sizes[i];\n parent.sizes[i] = parent.sizes[j];\n parent.sizes[j] = size;\n return true;\n}\n\n", "/**\n * tile_tab_menu.js \u2014 pop-out menu mounted by the tile's hamburger\n * button. Surfaces the same content the tile's *home page* would\n * list, grouped into one section per source table, with a built-in\n * search, per-section pagination, and full keyboard navigation.\n *\n * Click (or Enter on the active row) to open the entity in a NEW TAB\n * on the same tile. The menu only exists when the tab bar is visible\n * (\u22652 tabs).\n *\n * Source resolution: the menu asks the injected taxonomy which entity\n * sources a tile on this page should list (`taxonomy.sourcesFor`), then\n * asks the injected catalog to fetch and shape them. It knows the name\n * of neither. Which sources a page offers is one field on the\n * embedder's KindDef; it used to be a hardcoded table right here.\n *\n * Keyboard interactions:\n * \u2191 \u2193 move the row cursor across ALL visible items (the\n * cursor flows section-to-section, not just within one).\n * Home/End jump to the first / last visible row.\n * PageDown/ flip the focused section's pager when no search is active.\n * PageUp\n * Enter pick the cursor row.\n * Esc close.\n *\n * Search semantics:\n * - Match runs against the shaped (label, id, hint) of every raw row,\n * so the menu's search behaves the same as the command palette's.\n * - Pagination is OFF when a query is active \u2014 all matches show flat\n * within each section. Without a query, each section caps to\n * `PAGE_SIZE` rows with prev/next controls.\n */\n\n/** Items per category in browse mode (no search). Tuned small so the\n * menu stays compact; users page through with prev/next or type to\n * search the entire set. */\nconst PAGE_SIZE = 10;\n\n\n/** Open the menu anchored at (x, y). Closes on Escape, click-outside,\n * or selection. Returns a teardown closure for the caller, but the\n * menu also self-disposes on close. */\nexport function openTileTabMenu({\n x, y, leafKind, onPick, api, taxonomy, entities,\n}) {\n if (!taxonomy || !entities) {\n console.error('[tile-tab-menu] taxonomy + entity catalog are required');\n return () => {};\n }\n // Resolve which sources to list for the leaf's current page. An\n // unknown kind falls back to the root's list, which is the\n // \"everything\" default.\n const topNav = taxonomy.topNavFor(leafKind) || leafKind;\n const sources = taxonomy.sourcesFor(topNav)\n .map((nav) => entities.get(nav))\n .filter(Boolean);\n\n // Build the overlay + panel chrome.\n const overlay = document.createElement('div');\n overlay.className = 'twm-tile-tabmenu-overlay';\n overlay.innerHTML = `\n <div class=\"twm-tile-tabmenu\" role=\"dialog\" aria-label=\"Open in new tab\">\n <header class=\"twm-tile-tabmenu__head\">\n <span class=\"material-symbols-outlined twm-tile-tabmenu__head-icon\">tab</span>\n <span class=\"twm-tile-tabmenu__head-label\">Open in new tab</span>\n <button type=\"button\" class=\"twm-tile-tabmenu__close\"\n data-action=\"close\"\n aria-label=\"Close\">\u00D7</button>\n </header>\n <div class=\"twm-tile-tabmenu__search\">\n <span class=\"material-symbols-outlined\">search</span>\n <input type=\"search\"\n data-role=\"search\"\n placeholder=\"Filter rows across all sections\u2026\"\n autocomplete=\"off\" />\n </div>\n <div class=\"twm-tile-tabmenu__body\" data-role=\"body\">\n <div class=\"twm-tile-tabmenu__loading\">Loading\u2026</div>\n </div>\n </div>\n `;\n document.body.appendChild(overlay);\n\n // Anchor below the hamburger. clip to viewport so it never opens\n // off-screen when the tile sits at the right edge.\n const panel = overlay.querySelector('.twm-tile-tabmenu');\n panel.style.position = 'fixed';\n panel.style.left = `${Math.max(8, Math.min(window.innerWidth - 360, x))}px`;\n panel.style.top = `${Math.max(8, Math.min(window.innerHeight - 480, y - 480 + 22))}px`;\n\n let alive = true;\n let _query = '';\n let _cursor = 0; // index into _visibleItems\n let _visibleItems = []; // flat list rebuilt on every render\n // navKind \u2192 { rows, page, shaped (pre-shaped per row in source order) }\n const sectionState = new Map(sources.map((s) =>\n [s.navKind, { rows: [], page: 0, shaped: [] }]));\n\n const close = () => {\n if (!alive) return;\n alive = false;\n document.removeEventListener('mousedown', onOutside, true);\n document.removeEventListener('keydown', onKey, true);\n overlay.remove();\n };\n const onOutside = (ev) => {\n if (!overlay.contains(ev.target)) close();\n };\n\n const moveCursor = (delta) => {\n if (_visibleItems.length === 0) return;\n _cursor = Math.max(0, Math.min(_visibleItems.length - 1, _cursor + delta));\n _paintCursor();\n };\n const jumpCursor = (toEnd) => {\n if (_visibleItems.length === 0) return;\n _cursor = toEnd ? _visibleItems.length - 1 : 0;\n _paintCursor();\n };\n const pickCursor = () => {\n const cur = _visibleItems[_cursor];\n if (!cur) return;\n close();\n try { onPick?.(cur.navKind, cur.shaped); }\n catch (err) { console.warn('[tile-tab-menu] pick failed', err); }\n };\n\n const onKey = (ev) => {\n if (!alive) return;\n // Ignore arrow/Enter typed inside the search box's IME, etc.\n if (ev.isComposing) return;\n switch (ev.key) {\n case 'Escape':\n ev.preventDefault(); close(); return;\n case 'ArrowDown':\n ev.preventDefault(); moveCursor(+1); return;\n case 'ArrowUp':\n ev.preventDefault(); moveCursor(-1); return;\n case 'Home':\n ev.preventDefault(); jumpCursor(false); return;\n case 'End':\n ev.preventDefault(); jumpCursor(true); return;\n case 'Enter':\n ev.preventDefault(); pickCursor(); return;\n case 'PageDown': {\n if (_query) return;\n ev.preventDefault();\n const cur = _visibleItems[_cursor];\n const st = cur ? sectionState.get(cur.navKind) : null;\n if (st) {\n const pages = _pageCountFor(cur.navKind);\n if (st.page < pages - 1) { st.page += 1; _render(); }\n }\n return;\n }\n case 'PageUp': {\n if (_query) return;\n ev.preventDefault();\n const cur = _visibleItems[_cursor];\n const st = cur ? sectionState.get(cur.navKind) : null;\n if (st && st.page > 0) { st.page -= 1; _render(); }\n return;\n }\n }\n };\n document.addEventListener('mousedown', onOutside, true);\n document.addEventListener('keydown', onKey, true);\n overlay.querySelector('[data-action=\"close\"]')\n .addEventListener('click', close);\n\n const searchInput = overlay.querySelector('[data-role=\"search\"]');\n searchInput.addEventListener('input', () => {\n _query = searchInput.value.trim().toLowerCase();\n for (const st of sectionState.values()) st.page = 0;\n _cursor = 0;\n _render();\n });\n\n const bodyEl = overlay.querySelector('[data-role=\"body\"]');\n\n const _pageCountFor = (navKind) => {\n const st = sectionState.get(navKind);\n if (!st) return 1;\n const total = (_query\n ? st.shaped.filter((s) => _matchesShaped(s, _query))\n : st.shaped).length;\n return Math.max(1, Math.ceil(total / PAGE_SIZE));\n };\n\n const _render = () => {\n if (!alive) return;\n _visibleItems = [];\n const sections = [];\n for (const src of sources) {\n const st = sectionState.get(src.navKind);\n const meta = taxonomy.meta(src.navKind);\n const all = st.shaped;\n const filtered = _query\n ? all.filter((s) => _matchesShaped(s, _query))\n : all;\n const total = filtered.length;\n // Pagination is OFF during search \u2014 show every match. In\n // browse mode each section caps to PAGE_SIZE.\n let view;\n let page = 0;\n let pages = 1;\n if (_query) {\n view = filtered;\n } else {\n pages = Math.max(1, Math.ceil(total / PAGE_SIZE));\n page = Math.min(st.page, pages - 1);\n const start = page * PAGE_SIZE;\n view = filtered.slice(start, start + PAGE_SIZE);\n }\n // Append to the flat cursor list, in render order.\n for (const shaped of view) {\n _visibleItems.push({ navKind: src.navKind, shaped });\n }\n sections.push({ src, meta, view, total, page, pages });\n }\n if (_cursor >= _visibleItems.length) {\n _cursor = Math.max(0, _visibleItems.length - 1);\n }\n let flatIdx = 0;\n bodyEl.innerHTML = sections.map((s) => {\n const items = s.view.map((shaped) => {\n const idx = flatIdx++;\n const label = shaped.label || shaped.id || '';\n const hint = shaped.hint || '';\n return `\n <li class=\"twm-tile-tabmenu__item${idx === _cursor ? ' twm-tile-tabmenu__item--cursor' : ''}\"\n data-flat-idx=\"${idx}\">\n <span class=\"twm-tile-tabmenu__item-label\">${_esc(label)}</span>\n ${hint ? `<span class=\"twm-tile-tabmenu__item-hint\">${_esc(hint)}</span>` : ''}\n </li>\n `;\n }).join('');\n return `\n <section class=\"twm-tile-tabmenu__section\"\n data-section=\"${_esc(s.src.navKind)}\">\n <header class=\"twm-tile-tabmenu__section-head\">\n <span class=\"material-symbols-outlined\">${_esc(s.meta?.icon || 'arrow_right')}</span>\n <span class=\"twm-tile-tabmenu__section-label\">${_esc(s.meta?.label || s.src.navKind)}</span>\n <span class=\"twm-tile-tabmenu__section-count\">${s.total}</span>\n </header>\n ${s.view.length === 0 ? `\n <div class=\"twm-tile-tabmenu__empty\">${\n _query ? 'No matches.' : '\u2014 none \u2014'\n }</div>\n ` : `<ul class=\"twm-tile-tabmenu__list\">${items}</ul>`}\n ${(!_query && s.pages > 1) ? `\n <div class=\"twm-tile-tabmenu__pager\">\n <button type=\"button\" data-action=\"prev\"\n data-section=\"${_esc(s.src.navKind)}\"\n ${s.page === 0 ? 'disabled' : ''}>&lt;</button>\n <span>${s.page + 1} / ${s.pages}</span>\n <button type=\"button\" data-action=\"next\"\n data-section=\"${_esc(s.src.navKind)}\"\n ${s.page >= s.pages - 1 ? 'disabled' : ''}>&gt;</button>\n </div>\n ` : ''}\n </section>\n `;\n }).join('');\n bodyEl.querySelectorAll('.twm-tile-tabmenu__item').forEach((li) => {\n const idx = Number(li.dataset.flatIdx);\n li.addEventListener('mousemove', () => {\n if (_cursor !== idx) { _cursor = idx; _paintCursor(); }\n });\n li.addEventListener('click', () => {\n _cursor = idx;\n pickCursor();\n });\n });\n bodyEl.querySelectorAll('[data-action=\"prev\"], [data-action=\"next\"]').forEach((btn) => {\n btn.addEventListener('click', () => {\n const navKind = btn.dataset.section;\n const st = sectionState.get(navKind);\n if (!st) return;\n st.page += (btn.dataset.action === 'next') ? 1 : -1;\n _render();\n });\n });\n };\n\n const _paintCursor = () => {\n bodyEl.querySelectorAll('.twm-tile-tabmenu__item').forEach((li) => {\n const idx = Number(li.dataset.flatIdx);\n const on = idx === _cursor;\n li.classList.toggle('twm-tile-tabmenu__item--cursor', on);\n if (on) li.scrollIntoView({ block: 'nearest' });\n });\n };\n\n // Kick off the fetch \u2014 in parallel, and only for the sources this\n // tile cares about. `loadOne` + `shapeRows` are the catalog's own\n // primitives, so this menu and the command palette cannot drift\n // apart on what a row is or which rows get dropped (they used to:\n // this loop was a hand-rolled copy of the palette's).\n (async () => {\n await Promise.all(sources.map(async (src) => {\n const st = sectionState.get(src.navKind);\n if (!st) return;\n st.rows = await entities.loadOne(src.navKind, api);\n // Pre-shape once and stash so search/render don't re-shape\n // on every keystroke. Rows that shape() rejects are dropped.\n st.shaped = entities.shapeRows(src.navKind, st.rows);\n }));\n _render();\n // Park the cursor on the first visible row so Enter picks\n // something immediately after open.\n if (_visibleItems.length > 0) {\n _cursor = 0;\n _paintCursor();\n }\n })();\n\n searchInput.focus();\n return close;\n}\n\n\n/** Open a compact menu anchored to the leaf's hamburger button that\n * lists the leaf's OPEN TABS for quick switching (a browser-style tab\n * overflow list). Clicking a row switches to that tab. Closes on\n * Escape, click-outside, or selection.\n *\n * Anchored to the button: the tab strip sits at the bottom of the tile,\n * so the menu opens UPWARD, its bottom edge pinned just above the\n * hamburger at (x, y) = the button's top-left corner.\n *\n * This is deliberately NOT the same surface as `openTileTabMenu` above\n * (which browses a page's content sources to open NEW tabs). The\n * hamburger's job is \"show me the tabs I already have open here\".\n *\n * @param {{x:number, y:number,\n * tabs: Array<{kind:string, title?:string}>,\n * activeIdx?:number,\n * onPick:(idx:number)=>void}} opts\n * @returns {() => void} teardown closure (the menu also self-disposes).\n */\nexport function openTileTabSwitcher({ x, y, tabs, activeIdx = 0, onPick }) {\n const list = Array.isArray(tabs) ? tabs : [];\n if (list.length === 0) return () => {};\n\n const overlay = document.createElement('div');\n overlay.className = 'twm-tile-tabswitch-overlay';\n const rows = list.map((t, i) => `\n <li class=\"twm-tile-tabswitch__item${i === activeIdx ? ' twm-tile-tabswitch__item--on' : ''}\"\n data-idx=\"${i}\" role=\"option\" aria-selected=\"${i === activeIdx}\">\n <span class=\"material-symbols-outlined twm-tile-tabswitch__check\">${i === activeIdx ? 'check' : ''}</span>\n <span class=\"twm-tile-tabswitch__label\">${_esc(t.title || t.kind || 'Tab')}</span>\n </li>\n `).join('');\n overlay.innerHTML = `\n <div class=\"twm-tile-tabswitch\" role=\"dialog\" aria-label=\"Open tabs\">\n <header class=\"twm-tile-tabswitch__head\">\n <span class=\"material-symbols-outlined\">tab</span>\n <span class=\"twm-tile-tabswitch__head-label\">Open tabs</span>\n </header>\n <ul class=\"twm-tile-tabswitch__list\" role=\"listbox\">${rows}</ul>\n </div>\n `;\n document.body.appendChild(overlay);\n\n const panel = overlay.querySelector('.twm-tile-tabswitch');\n const W = 260;\n panel.style.position = 'fixed';\n panel.style.left = `${Math.max(8, Math.min(window.innerWidth - W - 8, x))}px`;\n // Open upward: pin the panel's bottom just above the hamburger.\n panel.style.bottom = `${Math.max(8, window.innerHeight - y + 4)}px`;\n panel.style.maxHeight = `${Math.max(120, y - 16)}px`;\n\n let alive = true;\n let cursor = Math.max(0, Math.min(list.length - 1, activeIdx));\n\n const close = () => {\n if (!alive) return;\n alive = false;\n document.removeEventListener('mousedown', onOutside, true);\n document.removeEventListener('keydown', onKey, true);\n overlay.remove();\n };\n const onOutside = (ev) => { if (!overlay.contains(ev.target)) close(); };\n const pick = (idx) => {\n close();\n try { onPick?.(idx); }\n catch (err) { console.warn('[tile-tab-switch] pick failed', err); }\n };\n const paint = () => {\n overlay.querySelectorAll('.twm-tile-tabswitch__item').forEach((li) => {\n const on = Number(li.dataset.idx) === cursor;\n li.classList.toggle('twm-tile-tabswitch__item--cursor', on);\n if (on) li.scrollIntoView({ block: 'nearest' });\n });\n };\n const onKey = (ev) => {\n if (!alive || ev.isComposing) return;\n switch (ev.key) {\n case 'Escape': ev.preventDefault(); close(); return;\n case 'ArrowDown': ev.preventDefault();\n cursor = Math.min(list.length - 1, cursor + 1); paint(); return;\n case 'ArrowUp': ev.preventDefault();\n cursor = Math.max(0, cursor - 1); paint(); return;\n case 'Home': ev.preventDefault(); cursor = 0; paint(); return;\n case 'End': ev.preventDefault(); cursor = list.length - 1; paint(); return;\n case 'Enter': ev.preventDefault(); pick(cursor); return;\n }\n };\n overlay.querySelectorAll('.twm-tile-tabswitch__item').forEach((li) => {\n const idx = Number(li.dataset.idx);\n li.addEventListener('mousemove', () => {\n if (cursor !== idx) { cursor = idx; paint(); }\n });\n li.addEventListener('click', () => pick(idx));\n });\n document.addEventListener('mousedown', onOutside, true);\n document.addEventListener('keydown', onKey, true);\n paint();\n return close;\n}\n\n\n/** Match a shaped record (`{id, label, hint}`) against `query`.\n * Case-insensitive substring against any field. Mirrors the command\n * palette's matching so search semantics are consistent across both\n * surfaces. */\nfunction _matchesShaped(shaped, query) {\n if (!shaped) return false;\n const id = String(shaped.id ?? '').toLowerCase();\n const label = String(shaped.label ?? '').toLowerCase();\n const hint = String(shaped.hint ?? '').toLowerCase();\n return id.includes(query) || label.includes(query) || hint.includes(query);\n}\n\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n}\n", "/**\n * shell.js \u2014 `createShell()`, the library's composition entry point.\n *\n * Requires: root, taxonomy, entities, content\n * Hosts: chrome (ELEMENTS, never selectors), host port, bus, stores\n * Owns: the content registry, the WM, the palette, the keymap, the\n * chrome painters, the tile context menus.\n * Knows: nothing about the embedder. No `document.body`, no selector it\n * did not author, no bus-event string, no content kind.\n *\n * \u2500\u2500 The seam \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\u2500\u2500\n * The embedder does everything up to \"hand the framework a root element and\n * a content map\", and everything after `createShell()` resolves:\n *\n * embedder: gate (is there anything to show?) -> install its own widgets\n * -> build/clear the root element -> build the content map\n * library: registry -> WM -> palette -> keymap -> chrome -> wm.load()\n * embedder: publish the shell, install post-mount extras\n *\n * That order is not stylistic. The widgets decorate DOM the embedder then\n * DELETES to make room for the root element, so `root` cannot exist before\n * the widgets have run \u2014 which is exactly why `createShell` does not try to\n * own a `prepare`/`widgets` lifecycle. It takes a root element that already\n * exists and mounts into it.\n *\n * \u2500\u2500 Content is a constructor argument \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 * `content` is REQUIRED, validated, and frozen into a per-shell registry\n * BEFORE the WM is built. There is no module-level `register()` to call\n * late. That matters: a leaf mounted against a missing factory renders a\n * placeholder, and the renderer caches tile DOM per (kind, props) \u2014 a late\n * registration invalidates nothing, so a whole restored session stays wrong.\n * Making registration impossible after the first mount is the fix.\n *\n * \u2500\u2500 Chrome is elements, not selectors \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 * A selector parameter would be the coupling wearing a hat: the framework\n * would still have to know that a \"top-nav host\" is a thing you find by CSS\n * in someone else's markup. Handing it a node is the smallest honest\n * abstraction \u2014 and an embedder with no chrome passes `chrome: {}` and gets\n * a working WM with none.\n */\n\nimport { WindowManager } from './wm.js';\nimport { createContentRegistry } from './content_registry.js';\nimport { createCommandPalette } from './command_palette.js';\nimport { installKeymap } from './keymap.js';\nimport { openTileTabSwitcher } from './tile_tab_menu.js';\nimport { showContextMenu } from '../ui/components/context_menu.js';\nimport { openForm } from '../ui/components/modal.js';\n\n/**\n * @param {object} cfg\n * @param {Element} cfg.root the element the WM mounts into\n * @param {object} cfg.taxonomy createTaxonomy(...) \u2014 required\n * @param {object} cfg.entities createEntityCatalog(...) \u2014 required\n * @param {object} cfg.content { [kind]: (hostEl, props, ctx) => {...} }\n * @param {object} [cfg.host] the host port (createHost)\n * @param {object} [cfg.api] an opaque back-end handle, threaded into\n * ctx.api for content factories. The library\n * never calls a method on it; it only passes\n * it through (the palette + tile tab menu\n * hand it to the entity catalog's fetchers).\n * @param {object} [cfg.eventBus]\n * @param {object} [cfg.logger]\n * @param {object} [cfg.tableStore] threaded into ctx for table persistence\n * @param {object} [cfg.events] bus-event NAMES the WM listens for\n * @param {object} [cfg.rootCrumb] the breadcrumb's leading segment\n * @param {object} [cfg.palette] { placeholder, onPick } \u2014 C30. `onPick`\n * is `(pick) => truthy` and CLAIMS the\n * open of a picked entity, so an embedder\n * with a rule about where its own entities\n * belong applies that rule whichever door\n * was used. Unclaimed picks still reset\n * the primary tile: the behaviour every\n * embedder has today.\n * @param {object} [cfg.panels] C14. Which panel TILES a fresh desktop\n * opens with \u2014 `{left, right, bottom}`,\n * merged over all-three-open. An embedder\n * whose navigator is its own chrome passes\n * `{left: false, right: false}` and gets no\n * tile it never registered a factory for.\n * @param {boolean} [cfg.promoteInPlace] C21. Whether a window promoted out\n * of a tile stays confined to that pane\n * rather than floating over the whole root.\n * @param {boolean} [cfg.snapPromotion] C15. Whether dragging a promoted\n * window onto a tile puts it back in the\n * tree. Default off \u2014 it changes what a\n * drag to an edge does.\n * @param {'top'|'bottom'} [cfg.tabLayout] C22. Where a multi-tab leaf draws its\n * tabs. `'bottom'` is the framework's own\n * spreadsheet strip under the tile body\n * and is the DEFAULT, so no existing\n * embedder's panes rearrange on upgrade;\n * `'top'` mounts the editor tab bar\n * between the chrome and the body. The\n * renderer mirrors this onto `root` as\n * `data-twm-tabs` and watches it, so an\n * embedder whose settings pane holds only\n * the root element can change it live.\n * @param {object} [cfg.chrome] { topNav?, paletteButton?, desktops?,\n * panelToggles?: { left?, right?, bottom? } }\n * @returns {Promise<object>} the frozen shell\n */\nexport async function createShell({\n root,\n taxonomy,\n entities,\n content,\n host = null,\n api = null,\n eventBus = null,\n logger = null,\n tableStore = null,\n events = {},\n rootCrumb = null,\n palette: paletteCfg = {},\n panels = null,\n snapPromotion = false,\n promoteInPlace = false,\n tabLayout = null,\n chrome = {},\n // An embedder that moved its sections out of the top bar \u2014 into an icon\n // rail, say \u2014 passes the selector its own buttons match, and F1..F8 keep\n // working. Omitted, the default top-bar selector applies.\n navSelector = null,\n} = {}) {\n // A bad shell is a BOOT error, not a runtime surprise. Same doctrine as\n // createHost / createTaxonomy.\n if (!root || typeof root.appendChild !== 'function') {\n throw new TypeError('createShell: `root` must be an element');\n }\n if (!taxonomy) throw new Error('createShell: a taxonomy is required');\n if (!entities) throw new Error('createShell: an entity catalog is required');\n\n const log = logger ?? { info(){}, warn(){}, error(){}, debug(){} };\n const registry = createContentRegistry(content); // throws on a non-function\n\n let wm = null;\n let topNavEl = null;\n let desktopsEl = null;\n let toggles = null;\n\n const syncChrome = () => {\n if (!wm) return;\n syncPanelToggles(toggles, wm);\n syncDesktopBar(desktopsEl, wm);\n syncTopNav(topNavEl, wm);\n };\n\n wm = new WindowManager({\n rootEl: root,\n content: registry,\n api,\n eventBus,\n host,\n taxonomy,\n events,\n panelDefaults: panels,\n snapPromotion,\n promoteInPlace,\n tabLayout,\n // `ctx` is the delivery vehicle for leaf-mounted chrome: tile_renderer\n // spreads it into every content factory, which is how the breadcrumb\n // gets `taxonomy` + `rootCrumb` without a content factory knowing they\n // exist. The two `onTile*Menu` keys are read with OPTIONAL CHAINING in\n // tile_renderer \u2014 drop one and right-click silently does nothing.\n ctx: {\n api, eventBus, host, tableStore,\n taxonomy, entities, rootCrumb, events,\n onTileContextMenu: (leafId, x, y) => _tileContextMenu(wm, leafId, x, y),\n onTileTabMenu: (leafId, x, y) => _tileTabMenu(wm, leafId, x, y),\n },\n onChange: () => syncChrome(),\n });\n\n const palette = createCommandPalette({\n wm, api, taxonomy, catalog: entities, ...paletteCfg,\n });\n // `navSelector` lets an embedder that moved its sections out of the top\n // bar keep F1..F8 working. Omitted, the default top-bar selector applies \u2014\n // the behaviour every existing embedder has today.\n const disposeKeymap = installKeymap({ wm, palette, navSelector });\n\n // Chrome \u2014 painted into the ELEMENTS the embedder handed over. An absent\n // key means absent chrome, not a crash.\n const paletteBtn = mountPaletteButton(chrome.paletteButton, palette);\n topNavEl = mountTopNav(chrome.topNav, taxonomy, wm);\n desktopsEl = mountDesktopBar(chrome.desktops, wm);\n // NOTE: bindPanelToggles CLONES the buttons (to strip whatever state the\n // embedder's own machinery left on them) and returns the FRESH nodes. The\n // originals are detached from here on \u2014 sync against the returned map, not\n // against `chrome.panelToggles`.\n toggles = bindPanelToggles(chrome.panelToggles, wm);\n\n // Belt-and-braces: anything else that mutates the WM fans out to chrome.\n eventBus?.on?.('wm:changed', syncChrome);\n\n await wm.load(); // \u2190 FIRST MOUNT\n syncChrome();\n\n log.info?.('shell mounted');\n\n return Object.freeze({\n wm,\n palette,\n taxonomy,\n entities,\n root,\n content: registry,\n chrome: Object.freeze({\n paletteButtonEl: paletteBtn,\n topNavEl,\n desktopsEl,\n panelToggles: toggles,\n }),\n // A shell that can be built can be built TWICE \u2014 an embedder that\n // rebuilds on a context change (a different project, a different\n // workspace) does exactly that. Everything this function installs\n // outside `root` has to come off, or the second shell shares the page\n // with the first one's keyboard.\n dispose: () => {\n try { eventBus?.off?.('wm:changed', syncChrome); } catch { /* ignore */ }\n try { disposeKeymap?.(); } catch { /* ignore */ }\n try { palette?.close?.(); } catch { /* ignore */ }\n // Every content factory gets its `destroy()`. Clearing the root\n // element would detach the DOM and tell none of them, so an ERD's\n // `window` keydown listener or a pane's interval would outlive the\n // shell that mounted it. This also stops the renderer painting, so\n // anything still holding a reference to this wm \u2014 a callback\n // captured before a rebuild, a promise that has not settled \u2014\n // cannot repaint a dead tree into a root the live shell now owns.\n try { wm.renderer.destroy(); } catch (err) { log.warn?.('renderer teardown', err); }\n },\n });\n}\n\n\n// \u2550\u2550 Chrome painters \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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// Each takes an ELEMENT (or nothing) and returns whatever the sync half\n// needs. None of them queries the document.\n\n/** One shortcut button per top-nav kind in the taxonomy, painted into the\n * element the embedder gave us. Clicking opens that kind in the primary tile. */\nfunction mountTopNav(hostEl, taxonomy, wm) {\n if (!hostEl) return null;\n hostEl.innerHTML = '';\n hostEl.classList.add('twm-top-nav');\n for (const k of taxonomy.topNavEntries()) {\n const btn = document.createElement('button');\n btn.className = 'twm-top-nav__btn twm-has-tooltip';\n btn.dataset.kind = k.kind;\n btn.dataset.tooltip = k.label;\n btn.dataset.tooltipPlacement = 'bottom';\n btn.setAttribute('aria-label', k.label);\n btn.innerHTML = `\n <span class=\"material-symbols-outlined twm-top-nav__icon\">${k.icon}</span>\n <span class=\"twm-top-nav__label\">${k.label}</span>\n `;\n hostEl.appendChild(btn);\n }\n hostEl.addEventListener('click', (e) => {\n const btn = e.target.closest('[data-kind]');\n if (!btn) return;\n wm.openInPrimary(btn.dataset.kind);\n });\n return hostEl;\n}\n\nfunction syncTopNav(hostEl, wm) {\n if (!hostEl) return;\n const tree = wm.desktops.active().tree;\n const primaryId = tree.primaryLeafId();\n const primaryKind = primaryId ? tree.get(primaryId)?.content?.kind : null;\n // topNavFor maps an entity kind onto its owning top-nav category. Falls\n // back to the primary kind so unknown kinds at least try to match a button.\n const topNavKind = wm.taxonomy.topNavFor(primaryKind) || primaryKind;\n hostEl.querySelectorAll('[data-kind]').forEach((b) => {\n b.classList.toggle('twm-top-nav__btn--on', b.dataset.kind === topNavKind);\n });\n}\n\n/**\n * The palette button, REBOUND rather than reused.\n *\n * This used to return an existing `#twm-palette-btn` untouched, which is right\n * only while a page mounts one shell and keeps it. An embedder that rebuilds\n * its shell \u2014 Tables does, on every project switch \u2014 got its FIRST shell's\n * button back, still wired to the FIRST shell's palette. Clicking it opened a\n * palette over a disposed window manager, and picking a result called\n * `openInPrimary` on a tree nobody could see, whose renderer then painted it\n * into the root the live shell now owns.\n *\n * So the stale node is replaced, which is exactly what `bindPanelToggles` does\n * one function below and for the same reason.\n */\nfunction mountPaletteButton(hostEl, palette) {\n if (!hostEl) return null;\n hostEl.querySelector('#twm-palette-btn')?.remove();\n const btn = document.createElement('button');\n btn.id = 'twm-palette-btn';\n btn.className = 'twm-panel-toggle-btn twm-has-tooltip';\n btn.dataset.tooltip = 'Command palette (Ctrl+K)';\n btn.dataset.tooltipPlacement = 'bottom';\n btn.setAttribute('aria-label', 'Open command palette');\n btn.innerHTML = '<span class=\"material-symbols-outlined\">menu_open</span>';\n btn.addEventListener('click', () => palette.toggle());\n hostEl.insertBefore(btn, hostEl.firstChild);\n return btn;\n}\n\n/**\n * Take ownership of the embedder's three panel-toggle buttons.\n *\n * cloneNode copies any state the embedder's own machinery left on a button\n * (.active/.pinned/.disabled, the disabled attr, aria-pressed). We strip it so\n * the WM is the sole, clean source of truth \u2014 `syncPanelToggles` manages only\n * `.panel-toggle-btn--on` from here on. The clone keeps the id, so an embedder\n * that drives a toggle by `getElementById(...).click()` still works.\n *\n * @returns {{left?:Element, right?:Element, bottom?:Element}} the FRESH nodes\n */\nfunction bindPanelToggles(map, wm) {\n const fresh = {};\n for (const side of ['left', 'right', 'bottom']) {\n const btn = map?.[side];\n if (!btn) continue;\n const el = btn.cloneNode(true);\n el.classList.remove('active', 'pinned', 'disabled');\n el.disabled = false;\n el.removeAttribute('aria-disabled');\n el.removeAttribute('aria-pressed');\n btn.replaceWith(el);\n el.addEventListener('click', () => wm.togglePanel(side));\n fresh[side] = el;\n }\n return fresh;\n}\n\nfunction syncPanelToggles(map, wm) {\n for (const side of ['left', 'right', 'bottom']) {\n const btn = map?.[side];\n if (!btn) continue;\n btn.classList.toggle('twm-panel-toggle-btn--on', wm.isPanelOpen(side));\n }\n}\n\nfunction mountDesktopBar(hostEl, wm) {\n if (!hostEl) return null;\n let el = hostEl.querySelector('#twm-desktops');\n if (!el) {\n el = document.createElement('div');\n el.id = 'twm-desktops';\n el.className = 'twm-desktops';\n hostEl.appendChild(el);\n }\n el.addEventListener('click', (e) => {\n const btn = e.target.closest('[data-desktop]');\n if (!btn) return;\n const v = btn.dataset.desktop;\n if (v === '+') { wm.addDesktop(); syncDesktopBar(el, wm); return; }\n wm.switchDesktop(Number(v));\n });\n // Double-click a desktop chip \u2192 inline rename. Faster than the context\n // menu's Rename\u2026 for the common case. Persists via wm._persist so the new\n // label survives a restart.\n el.addEventListener('dblclick', async (e) => {\n const btn = e.target.closest('[data-desktop]');\n if (!btn || btn.dataset.desktop === '+') return;\n e.preventDefault();\n const idx = Number(btn.dataset.desktop);\n const d = wm.desktops.desktops[idx];\n if (!d) return;\n const result = await openForm({\n title: 'Rename desktop',\n fields: [{ name: 'label', label: 'Name', type: 'text', required: true,\n hint: 'Up to 24 characters. Shown in the bottom-bar chip and the Alt+N tooltip.' }],\n defaults: { label: d.label },\n submitLabel: 'Rename',\n });\n if (!result || !result.label) return;\n d.label = String(result.label).slice(0, 24);\n syncDesktopBar(el, wm);\n wm._persist();\n });\n el.addEventListener('contextmenu', (e) => {\n const btn = e.target.closest('[data-desktop]');\n if (!btn || btn.dataset.desktop === '+') return;\n e.preventDefault();\n const idx = Number(btn.dataset.desktop);\n const m = wm.desktops;\n const d = m.desktops[idx];\n const canDelete = m.desktops.length > 1;\n showContextMenu(e.clientX, e.clientY, [\n { label: `Switch to ${d.label}`, icon: 'desktop_windows',\n action: 'switch', disabled: idx === m.activeIdx },\n { label: 'Rename\u2026', icon: 'edit', action: 'rename' },\n { separator: true },\n { label: 'New desktop', icon: 'add', action: 'add' },\n { label: 'Delete desktop', icon: 'delete', action: 'delete',\n danger: true, disabled: !canDelete },\n ], async (action) => {\n if (action === 'switch') wm.switchDesktop(idx);\n else if (action === 'rename') {\n const result = await openForm({\n title: 'Rename desktop',\n fields: [{ name: 'label', label: 'Name', type: 'text', required: true }],\n defaults: { label: d.label },\n submitLabel: 'Rename',\n });\n if (!result || !result.label) return;\n d.label = String(result.label).slice(0, 24);\n syncDesktopBar(el, wm);\n wm._persist();\n }\n else if (action === 'add') { wm.addDesktop(); syncDesktopBar(el, wm); }\n else if (action === 'delete') { wm.removeDesktop(idx); syncDesktopBar(el, wm); }\n });\n });\n return el;\n}\n\nfunction syncDesktopBar(el, wm) {\n if (!el) return;\n const m = wm.desktops;\n el.innerHTML =\n m.desktops.map((d, i) => `\n <button class=\"twm-desk__btn${i === m.activeIdx ? ' twm-desk__btn--on' : ''}\"\n data-desktop=\"${i}\"\n title=\"Desktop ${d.label} (Alt+${i + 1}) \u00B7 right-click to delete\">\n ${d.label}\n </button>`).join('')\n + `<button class=\"twm-desk__btn twm-desk__btn--add\"\n data-desktop=\"+\" title=\"Add desktop\">+</button>`;\n}\n\n\n// \u2550\u2550 Tile menus \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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/** Hamburger button in a tile's tab bar. Opens a compact menu anchored to\n * the button that lists the leaf's OPEN TABS for quick switching (a\n * browser-style tab overflow list). Clicking a row activates that tab.\n * The leaf id is captured at click time, so a focus change between click\n * and pick doesn't reroute it. */\nfunction _tileTabMenu(wm, leafId, x, y) {\n const tree = wm.desktops.active().tree;\n const leaf = tree.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return;\n const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];\n if (tabs.length === 0) return;\n openTileTabSwitcher({\n x, y,\n tabs,\n activeIdx: Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0)),\n onPick: (idx) => {\n tree.setActiveLeafTab(leafId, idx);\n tree.focus(leafId);\n wm.renderer.render();\n wm._persist?.();\n wm._notifyChange?.('tab-switch-from-menu');\n },\n });\n}\n\n/** Right-click on a tile's chrome strip: structural actions on that leaf. */\nfunction _tileContextMenu(wm, leafId, x, y) {\n const tree = wm.desktops.active().tree;\n const leaf = tree.get(leafId);\n if (!leaf) return;\n const isPanel = String(leaf.content?.kind || '').startsWith('panel:');\n tree.focus(leafId);\n wm.renderer._updateFocusClasses();\n\n const items = [\n { label: 'Split horizontally', icon: 'splitscreen_vertical_add', action: 'split-h' },\n { label: 'Split vertically', icon: 'splitscreen_add', action: 'split-v' },\n { separator: true },\n { label: 'Open in new tab', icon: 'tab',\n action: 'open-tab', disabled: isPanel || !leaf.content },\n // TWO DIFFERENT GLYPHS FOR TWO DIFFERENT DESTINATIONS. `web_asset` is a\n // window INSIDE the application \u2014 the same glyph `ManagedWindow` uses\n // for itself \u2014 and an embedder that can also send content to a real\n // browser window keeps `open_in_new`, which is the universal \"this\n // leaves the page\". One glyph for both is how a user learns that the\n // two commands are the same command, and then loses a window looking\n // for it on the other screen.\n { label: 'Open a copy in a window', icon: 'web_asset',\n action: 'open-window', disabled: isPanel || !leaf.content },\n // C20, THE OTHER HALF \u2014 and it was missing while the `close` half\n // below carried a paragraph explaining why it could not be.\n //\n // `_floatableLeaf` (`wm.js`) refuses to float content that declared\n // `chrome: { promote: false }`, and every door converges there \u2014 so\n // this row offered the verb, enabled, and returned null. The chrome's\n // own float BUTTON does not have the problem: C20 removes it from the\n // strip. That asymmetry is what hid this: the affordance the reader\n // checks is correct, and the menu one layer down is not.\n //\n // `=== false` EXACTLY, because that is the test the verb makes\n // (`wm.js`, `_floatableLeaf`: *\"content that says nothing about\n // `promote` stays floatable\"*). A falsy test here would grey the row\n // on every leaf whose content returned no `chrome` at all, which is\n // most of them \u2014 a menu disagreeing with its verb in the generous\n // direction is a dead control; in the mean direction it is a missing\n // feature, and this file has shipped one of each.\n { label: 'Float this pane as a window', icon: 'web_asset',\n action: 'promote',\n disabled: isPanel || !leaf.content\n || wm.renderer?.leafChrome?.(leafId)?.promote === false },\n ];\n if (wm.desktops.desktops.length > 1 && !isPanel) {\n for (const [i, d] of wm.desktops.desktops.entries()) {\n if (i === wm.desktops.activeIdx) continue;\n items.push({ label: `Move to desktop ${d.label}`, icon: 'sweep',\n action: `move:${i}` });\n }\n }\n items.push({ separator: true });\n // C20. THE MENU ROW CARRIES THE VETO, AND THE REASON WITH IT.\n //\n // `closeFocused` refuses when the content vetoed `close`, which is what\n // makes the greyed \u00D7 in the chrome honest \u2014 and left this row offering the\n // same verb, enabled, doing nothing. A control that silently no-ops is the\n // dead-control failure this file has fixed three times already. The\n // tooltip is the content's own sentence, so the two doors explain the\n // refusal identically rather than one explaining it and one not.\n const closeVeto = wm.renderer?.leafChrome?.(leafId)?.close;\n const closeVetoed = closeVeto === false || closeVeto?.disabled === true;\n items.push({ label: 'Close tile', icon: 'close', action: 'close',\n danger: true, disabled: isPanel || closeVetoed,\n title: closeVetoed ? (closeVeto?.title || undefined) : undefined });\n\n showContextMenu(x, y, items, (action) => {\n if (action === 'split-h') wm.split('h');\n else if (action === 'split-v') wm.split('v');\n else if (action === 'open-tab') {\n const c = leaf.content;\n if (c) wm.openInTabFromContext({ leafId }, c.kind, c.props || {});\n }\n else if (action === 'open-window') {\n const c = leaf.content;\n if (c) wm.navigate(c.kind, c.props || {}, { target: 'window' });\n }\n else if (action === 'promote') wm.toggleManagedFocused();\n else if (action === 'close') wm.closeFocused();\n else if (action?.startsWith?.('move:')) {\n wm.moveFocusedToDesktop(Number(action.slice(5)));\n }\n });\n}\n"],
5
- "mappings": "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",
3
+ "sources": ["../src/tiling/command_palette.js", "../src/tiling/content_registry.js", "../src/tiling/tile_tree.js", "../src/tiling/desktops.js", "../src/tiling/entity_sources.js", "../src/tiling/keymap.js", "../src/tiling/kind_taxonomy.js", "../src/tiling/panel_keys.js", "../src/tiling/landing_table.js", "../src/tiling/loading_overlay.js", "../src/tiling/nav_panel.js", "../src/tiling/tile_breadcrumb.js", "../src/tiling/page_factory.js", "../src/tiling/tile_renderer.js", "../src/tiling/tab_strip.js", "../src/tiling/wm.js", "../src/tiling/tile_tab_menu.js", "../src/tiling/zoom.js", "../src/tiling/shell.js"],
4
+ "sourcesContent": ["/**\n * command_palette.js \u2014 Ctrl+K center popup.\n *\n * \u250C\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\u2510\n * \u2502 search\u2026 \u2502\n * \u251C\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\u2524\n * \u2502 [chip] [chip] [chip] [chip] \u2502 top-nav shortcut chips\n * \u2502 [chip] [chip] \u2502\n * \u2502 \u2502\n * \u2502 \u2610 left nav \u2610 right panel \u2610 bottom \u2502 panel toggles\n * \u251C\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\u2524\n * \u2502 matched entity rows\u2026 \u2502 search results\n * \u2514\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\u2518\n *\n * The chips are the injected taxonomy's top-nav kinds; clicking one\n * loads it into the primary tile. The toggles flip the corresponding\n * panel-tile on/off. Typing searches the injected entity catalog\n * (fetched lazily on first open).\n */\n\nimport { HelpModal } from '../help/help_modal.js';\n\nconst ROOT_ID = 'twm-cmdpal';\n\n// Non-entity commands surfaced in the palette's search results. They\n// carry an `action` so `commit()` runs them instead of opening a tile.\nconst STATIC_COMMANDS = [\n { kind: 'command', id: 'shortcuts', label: 'Keyboard shortcuts',\n hint: 'Show all key bindings (?)', icon: 'keyboard',\n action: () => HelpModal.open('keyboard-shortcuts') },\n];\n\n/** @param taxonomy injected ontology \u2014 supplies the chip strip + row icons\n * @param catalog injected entity catalog \u2014 supplies the searchable rows\n * @param placeholder input placeholder. It names the embedder's entity types\n * (\"try foo:bar\"), so the embedder owns the string.\n * @param onPick C30. `(pick) => truthy` CLAIMS the open, exactly the way\n * `ManagedWindow.onMaximize` claims the maximise gesture.\n * See `commit` for what it is for; omitted, the palette\n * behaves as it always has. */\nexport function createCommandPalette({ wm, api, taxonomy, catalog, placeholder = 'Search\u2026',\n onPick = null }) {\n if (!taxonomy) throw new Error('createCommandPalette: a taxonomy is required');\n if (!catalog) throw new Error('createCommandPalette: an entity catalog is required');\n let overlay = null;\n let entities = [];\n\n const isOpen = () => !!overlay;\n const close = () => {\n if (!overlay) return;\n overlay.remove();\n overlay = null;\n };\n const toggle = () => { isOpen() ? close() : open(); };\n\n const open = async () => {\n if (overlay) return;\n overlay = document.createElement('div');\n overlay.id = ROOT_ID;\n overlay.className = 'twm-cmdpal-overlay';\n overlay.innerHTML = _markup(taxonomy, placeholder, wm);\n document.body.appendChild(overlay);\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) close();\n });\n\n const input = overlay.querySelector('.twm-cmdpal__input');\n const list = overlay.querySelector('[data-role=\"list\"]');\n const chipSlot = overlay.querySelector('[data-role=\"type-chip\"]');\n const chips = overlay.querySelectorAll('[data-shortcut]');\n const toggles = overlay.querySelectorAll('[data-toggle]');\n\n // Reflect current panel state in toggle buttons.\n for (const btn of toggles) {\n const which = btn.dataset.toggle;\n btn.classList.toggle('twm-chip--on', wm.isPanelOpen(which));\n }\n\n chips.forEach((btn) => btn.addEventListener('click', () => {\n const kind = btn.dataset.shortcut;\n wm.openInPrimary(kind);\n close();\n }));\n toggles.forEach((btn) => btn.addEventListener('click', () => {\n const which = btn.dataset.toggle;\n wm.togglePanel(which);\n btn.classList.toggle('twm-chip--on', wm.isPanelOpen(which));\n }));\n\n // Scoped-search state. Typing `<prefix>:` converts the prefix\n // into a visual chip that lives to the left of the input;\n // subsequent typing only matches entities whose navKind is in\n // the chip's set. Cleared by clicking the chip's \u00D7, by pressing\n // Backspace when the input is empty, or by Esc (which closes the\n // palette). A prefix can resolve to MULTIPLE navKinds, so the\n // user's mental model can win over the embedder's internal\n // taxonomy. The resolver lives on the injected catalog, next to\n // the alias table it consults.\n let filtered = [];\n let active = 0;\n let typeFilter = null; // null | string[] (navKinds)\n let typeFilterLabel = null; // chip text, derived from the resolved kinds\n\n const renderChip = () => {\n if (!typeFilter) {\n chipSlot.innerHTML = '';\n return;\n }\n // Display: first kind's icon/label from the taxonomy. A\n // multi-kind alias gets the leading kind's label, which\n // reads as narrower than the alias actually is \u2014 so we\n // override with the typed alias (passed from `refilter`)\n // when it carries information the taxonomy can't infer.\n const head = typeFilter[0];\n const meta = taxonomy.meta(head);\n const label = typeFilterLabel || meta?.label || head;\n chipSlot.innerHTML = `\n <span class=\"twm-cmdpal__type-chip\" title=\"Filtering by ${_esc(label)} (${typeFilter.length} kind${typeFilter.length === 1 ? '' : 's'})\">\n <span class=\"material-symbols-outlined twm-cmdpal__type-chip-icon\">${meta?.icon || 'arrow_right'}</span>\n <span class=\"twm-cmdpal__type-chip-label\">${_esc(label)}</span>\n <button type=\"button\" class=\"twm-cmdpal__type-chip-x\"\n data-action=\"clear-type\"\n aria-label=\"Clear type filter\">\u00D7</button>\n </span>\n `;\n chipSlot.querySelector('[data-action=\"clear-type\"]')\n ?.addEventListener('click', (ev) => {\n ev.preventDefault();\n typeFilter = null;\n typeFilterLabel = null;\n renderChip();\n input.focus();\n refilter();\n });\n };\n\n const render = () => {\n list.innerHTML = filtered.slice(0, 200).map((e, i) => `\n <div class=\"twm-cmdpal__item${i === active ? ' twm-cmdpal__item--active' : ''}\"\n data-idx=\"${i}\">\n <span class=\"material-symbols-outlined twm-cmdpal__icon\">${e.icon || 'arrow_right'}</span>\n <span class=\"twm-cmdpal__label\">${_esc(e.label || e.id)}</span>\n <span class=\"twm-cmdpal__kind\">${e.kind}</span>\n <span class=\"twm-cmdpal__hint\">${_esc(e.hint || '')}</span>\n </div>\n `).join('') || '';\n list.querySelectorAll('[data-idx]').forEach((el) => {\n el.addEventListener('click', () => commit(Number(el.dataset.idx)));\n });\n };\n /**\n * \u2550\u2550 C30. THE PALETTE IS A DOOR, NOT A PLACEMENT POLICY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * `openInPrimary` is the wrong verb for a picked ENTITY and it was the\n * only one here. It RESETS the primary leaf to a single fresh tab \u2014\n * its own doc says so, and says why: navigation from outside the tile\n * is a page change, and a page change replaces the page. So picking a\n * table from Ctrl+K threw away every other tab in that leaf, ignored\n * whatever rule the embedder has about where its entities open (a tab\n * beside its siblings, a floating window on a canvas pane, a split),\n * and bypassed any per-mount-site bookkeeping the embedder keeps \u2014\n * for Tables, the grid registry keyed on `(mount site, table)`, which\n * is what holds staged edits across a re-render.\n *\n * The result was a door that did something different from every other\n * door onto the same object: the rail opened a table one way, the\n * palette another, and only the palette destroyed work. Reported as\n * \"open table via Ctrl+K does not work\" \u2014 which is exactly what it\n * looks like from the outside when the tab you were in is replaced.\n *\n * The library cannot decide this. It does not know what a `table` is,\n * which is the whole point of the injected taxonomy and catalogue. So\n * the embedder is offered the pick and may CLAIM it, on the same\n * contract as `ManagedWindow.onMaximize` (`managed_window.js:230`):\n * return truthy and nothing else happens. Guarded, because a throwing\n * embedder must not leave the palette closed over a half-done open \u2014\n * and unclaimed picks still land in the primary tile, so no existing\n * embedder changes behaviour by upgrading.\n *\n * `pick` is the catalogue row verbatim (`{kind, id, label, hint,\n * props?}`) rather than a re-shaped argument: the embedder wrote the\n * `shape` that produced it, so it is the one party that can read it.\n */\n const commit = (i) => {\n const pick = filtered[i];\n if (!pick) return;\n close();\n if (pick.action) { try { pick.action(); } catch (err) { console.error(err); } return; }\n if (onPick) {\n let handled = false;\n try { handled = onPick(pick) ?? false; }\n catch (err) { console.error('[cmdpal] onPick threw', err); }\n if (handled) return;\n }\n wm.openInPrimary(pick.kind, pick.props || { id: pick.id, label: pick.label });\n };\n\n /** Look for a `<prefix>:` at the start of the raw input. If the\n * prefix resolves to one or more known kinds, promote it to\n * the visual chip and strip it from the input field. Returns\n * true when a chip was set so the caller can decide whether\n * to re-render. */\n const tryConsumePrefix = () => {\n if (typeFilter) return false;\n const m = input.value.match(/^([a-zA-Z][a-zA-Z0-9_-]*):/);\n if (!m) return false;\n const kinds = catalog.resolveTypePrefix(m[1]);\n if (!kinds || kinds.length === 0) return false;\n typeFilter = kinds;\n typeFilterLabel = _titleCase(m[1]);\n input.value = input.value.slice(m[0].length);\n renderChip();\n return true;\n };\n\n const refilter = () => {\n tryConsumePrefix();\n const q = input.value.trim().toLowerCase();\n const filterSet = typeFilter ? new Set(typeFilter) : null;\n const pool = filterSet\n ? entities.filter((e) => filterSet.has(e.kind))\n : entities;\n if (!q) {\n // Scoped + empty query \u2192 list every entity of that\n // kind so the user sees what's available. Unscoped +\n // empty query \u2192 blank list (matches the old behavior\n // where typing nothing meant nothing).\n filtered = typeFilter\n ? pool.slice().sort((a, b) =>\n String(a.label || a.id).localeCompare(b.label || b.id))\n : [];\n } else {\n filtered = _search(pool, q);\n }\n active = 0;\n render();\n };\n input.addEventListener('input', refilter);\n input.addEventListener('keydown', (e) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n active = Math.min(filtered.length - 1, active + 1);\n render();\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n active = Math.max(0, active - 1);\n render();\n } else if (e.key === 'Enter') {\n e.preventDefault();\n commit(active);\n } else if (e.key === 'Escape') {\n close();\n } else if (e.key === 'Backspace' && input.value === '' && typeFilter) {\n // Empty input + active chip \u2192 consume Backspace to drop\n // the chip. Same idiom as Gmail / Linear / VS Code's\n // command palette: the chip behaves like a token before\n // the caret.\n e.preventDefault();\n typeFilter = null;\n typeFilterLabel = null;\n renderChip();\n refilter();\n }\n });\n input.focus();\n\n // Commands are searchable instantly; entity rows are lazy-loaded\n // \u2014 the palette stays usable for shortcuts/commands while we wait.\n // The set is rebuilt on every open so the palette reflects\n // entities the user just added this session (cheap; the catalog\n // runs every source in parallel).\n entities = _decorateIcons(STATIC_COMMANDS.slice(), taxonomy);\n if (api) {\n try {\n entities = _decorateIcons(\n [...STATIC_COMMANDS, ...await catalog.loadAll(api)], taxonomy);\n if (input.value) refilter();\n } catch (err) {\n console.warn('[cmdpal] entity load failed', err);\n }\n }\n };\n\n return { open, close, toggle, isOpen };\n}\n\nfunction _markup(taxonomy, placeholder, wm) {\n // ONLY THE PANELS THIS SHELL CAN ACTUALLY SHOW.\n //\n // The three toggles were hardcoded, so the palette offered `panel:left` to\n // an embedder that has no factory for it \u2014 and `togglePanel` then\n // canonicalized a leaf whose only possible rendering is \"no factory\n // registered yet\", permanently, because the renderer caches tile DOM per\n // (kind, props). C14 closed that path at boot; this closes the other end of\n // it. A panel with no factory is not a panel this shell can show, and the\n // registry is the thing that knows.\n const PANEL_CHIPS = [\n { side: 'left', icon: 'menu', label: 'Left nav' },\n { side: 'right', icon: 'dock_to_left', label: 'Right panel' },\n { side: 'bottom', icon: 'dock_to_bottom', label: 'Bottom panel' },\n ];\n const panelToggles = PANEL_CHIPS\n .filter((p) => wm.content?.has?.(`panel:${p.side}`) !== false)\n .map((p) => `\n <button class=\"twm-chip\" data-toggle=\"${p.side}\">\n <span class=\"material-symbols-outlined\">${p.icon}</span>\n ${p.label}\n </button>`).join('');\n\n const chips = taxonomy.topNavEntries().map((k) => `\n <button class=\"twm-chip\" data-shortcut=\"${k.kind}\">\n <span class=\"material-symbols-outlined\">${k.icon}</span>\n ${k.label}\n </button>\n `).join('');\n return `\n <div class=\"twm-cmdpal\" role=\"dialog\" aria-label=\"Command palette\">\n <div class=\"twm-cmdpal__inputrow\">\n <span class=\"twm-cmdpal__type-chip-slot\" data-role=\"type-chip\"></span>\n <input type=\"text\" class=\"twm-cmdpal__input\"\n placeholder=\"${_esc(placeholder)}\"\n autocomplete=\"off\" />\n </div>\n <div class=\"twm-cmdpal__chips\">${chips}</div>\n <div class=\"twm-cmdpal__toggles\">${panelToggles}</div>\n <div class=\"twm-cmdpal__list\" data-role=\"list\"></div>\n <div class=\"twm-cmdpal__footer\">\n <span><kbd>\u2191</kbd><kbd>\u2193</kbd> navigate</span>\n <span><kbd>Enter</kbd> open</span>\n <span><kbd>Esc</kbd> close</span>\n </div>\n </div>\n `;\n}\n\n/** Attach an icon per row by consulting the injected taxonomy. Keeps the\n * palette and breadcrumb visually consistent: one source of truth for\n * what icon represents a given kind. */\nfunction _decorateIcons(rows, taxonomy) {\n return rows.map((r) => {\n if (r.icon) return r;\n const meta = taxonomy.meta(r.kind);\n return { ...r, icon: meta?.icon || 'arrow_right' };\n });\n}\n\nfunction _search(entities, q) {\n const scored = [];\n for (const e of entities) {\n const id = String(e.id || '').toLowerCase();\n const label = String(e.label || '').toLowerCase();\n const hint = String(e.hint || '').toLowerCase();\n let r;\n if (id === q || label === q) r = 0;\n else if (id.startsWith(q)) r = 1;\n else if (label.startsWith(q)) r = 2;\n else if (id.includes(q)) r = 3;\n else if (label.includes(q)) r = 4;\n else if (hint.includes(q)) r = 5;\n else continue;\n scored.push({ e, r });\n }\n scored.sort((a, b) => a.r - b.r || a.e.label.localeCompare(b.e.label));\n return scored.map((x) => x.e);\n}\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n}\n\n/** Display form for a typed prefix: capitalise the first letter, and\n * swap any underscores/hyphens for spaces so a multi-word alias reads\n * as prose. Used for the scope chip's label, so it reflects what the\n * user actually typed rather than the kind it resolved to. */\nfunction _titleCase(s) {\n const str = String(s || '').replace(/[_-]+/g, ' ').trim();\n if (!str) return '';\n return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();\n}\n", "/**\n * content_registry.js \u2014 maps a content kind string to a factory that\n * mounts that content into a tile body element.\n *\n * A content factory has the shape:\n *\n * (hostEl, props, ctx) -> { destroy?(), title?, ... }\n *\n * The tile renderer calls the factory once when the leaf is first\n * mounted with a given (kind, props) pair, and `destroy()` on unmount.\n *\n * \u2500\u2500 ONE REGISTRY PER SHELL \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 * This module used to hold a module-level `Map` plus a `register(kind, fn)`\n * export. Two problems, both structural:\n *\n * 1. Two shells on one page shared their kind -> factory table, so a\n * foreign-ontology shell silently inherited the other embedder's\n * factories. A module singleton is the global we are removing,\n * wearing a hat.\n * 2. `register()` could be called AFTER the first mount. A leaf mounted\n * against a missing factory renders the placeholder and the renderer\n * caches that DOM per (kind, props) \u2014 a later registration invalidates\n * nothing, so an entire restored session stays wrong.\n *\n * `createContentRegistry(map)` fixes both: the map is a REQUIRED\n * constructor argument of `createShell()`, validated up-front, and frozen\n * before the WM is built. There is nothing left to call late.\n *\n * This module knows no kind names but one \u2014 `window-placeholder`, which is\n * the WM's OWN content (shown in a tile whose content was promoted into a\n * managed window). Defaulting it here means an embedder cannot forget it\n * and get a \"no factory registered\" tile the first time it promotes.\n */\n\nexport const PLACEHOLDER_KIND = 'window-placeholder';\n\n/** Shown in a tile while its content lives in a managed window. Pure\n * `twm-*` markup; clicking the button demotes the window back. */\nfunction windowPlaceholderFactory(hostEl, props, ctx) {\n hostEl.innerHTML = `\n <div class=\"twm-window-placeholder\">\n <span class=\"material-symbols-outlined twm-window-placeholder__icon\">open_in_new</span>\n <div class=\"twm-window-placeholder__title\">${_esc(props.originalTitle || props.originalKind || 'content')}</div>\n <div class=\"twm-window-placeholder__hint\">is open in a managed window</div>\n <button class=\"twm-window-placeholder__btn\" data-action=\"bring-back\">\n Bring back to this tile\n </button>\n </div>\n `;\n hostEl.querySelector('[data-action=\"bring-back\"]')?.addEventListener('click', () => {\n ctx.wm?.bringBackWindow?.(props.windowId);\n });\n return { title: `${props.originalTitle || props.originalKind} (window)` };\n}\n\n/**\n * Build a shell-scoped content registry.\n *\n * @param {Object<string, Function>} map kind -> content factory\n * @returns {{ has(kind): boolean, mount(kind, hostEl, props, ctx): object }}\n */\nexport function createContentRegistry(map = {}) {\n if (!map || typeof map !== 'object') {\n throw new TypeError('createContentRegistry: `content` must be an object of kind -> factory');\n }\n for (const [kind, factory] of Object.entries(map)) {\n if (typeof factory !== 'function') {\n throw new TypeError(\n `createContentRegistry: factory for \"${kind}\" must be a function`);\n }\n }\n const factories = new Map(Object.entries(map));\n if (!factories.has(PLACEHOLDER_KIND)) {\n factories.set(PLACEHOLDER_KIND, windowPlaceholderFactory);\n }\n\n return Object.freeze({\n has: (kind) => factories.has(kind),\n\n mount: (kind, hostEl, props, ctx) => {\n const factory = factories.get(kind) || _placeholder(kind);\n try {\n return factory(hostEl, props || {}, ctx || {}) || {};\n } catch (err) {\n console.error('[content_registry] mount failed for', kind, err);\n hostEl.innerHTML =\n `<div class=\"twm-tile-error\">Mount failed: ${err && err.message || err}</div>`;\n return {};\n }\n },\n });\n}\n\nfunction _placeholder(kind) {\n return (hostEl) => {\n hostEl.innerHTML = `\n <div class=\"tile-placeholder\">\n <div class=\"tile-placeholder__title\">${kind}</div>\n <div class=\"tile-placeholder__hint\">no factory registered yet</div>\n </div>\n `;\n return { title: kind };\n };\n}\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => (\n { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]\n ));\n}\n", "/**\n * tile_tree.js \u2014 i3/sway-style binary tree for the tiling window manager.\n *\n * A workspace is one tree of Node objects:\n *\n * Node = { id, kind: 'split' | 'leaf', parentId, ... }\n * Split: { dir: 'h' | 'v', children: [childId, ...], sizes: [number, ...] }\n * Leaf: { tabs: [{kind, props, title}], activeTabIdx, content, title }\n *\n * **Tabs**. Each leaf carries an ordered list of tab specs and an active\n * index. The active tab is mirrored onto the legacy `content` /\n * `title` fields so the rest of the codebase (renderer, WM,\n * breadcrumb, navigateBack, \u2026) keeps reading `leaf.content.kind`\n * unchanged. Single-tab leaves behave identically to today; multi-tab\n * leaves expose a small TabBar at the bottom of the tile.\n *\n * The tree carries no DOM. A separate renderer maps it to flex containers.\n * Mutations return new node-id arrays; consumers re-render after each call.\n */\n\nlet _idCounter = 1;\nfunction _newId(prefix) { return `${prefix}-${_idCounter++}`; }\n\nfunction _isPanel(leaf) {\n return String(leaf?.content?.kind || '').startsWith('panel:');\n}\n\n/** Mirror the active tab back onto `leaf.content` / `leaf.title` so the\n * rest of the system sees no diff. Called after every tab mutation. */\nfunction _syncActiveTab(leaf) {\n if (!leaf || leaf.kind !== 'leaf') return;\n const tabs = leaf.tabs || [];\n if (tabs.length === 0) {\n leaf.content = null;\n leaf.title = leaf.title || '';\n leaf.activeTabIdx = 0;\n return;\n }\n if (leaf.activeTabIdx == null\n || leaf.activeTabIdx < 0\n || leaf.activeTabIdx >= tabs.length) {\n leaf.activeTabIdx = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));\n }\n const t = tabs[leaf.activeTabIdx];\n leaf.content = { kind: t.kind, props: t.props || {} };\n leaf.title = t.title || t.kind || '';\n}\n\nexport function makeLeaf({ content = null, title = '' } = {}) {\n const leaf = {\n id: _newId('leaf'),\n kind: 'leaf',\n parentId: null,\n // Tabs are the source of truth; content/title mirror tabs[active].\n tabs: content\n ? [{ kind: content.kind, props: content.props || {}, title: title || content.kind || '' }]\n : [],\n activeTabIdx: content ? 0 : 0,\n content,\n title,\n // Per-top-nav-page tab archive. When a top-nav button switches\n // this leaf to another page, the current `tabs` + `activeTabIdx`\n // get stashed here under the LEAVING page's key, and the target\n // page's archived list (if any) gets restored. This is what\n // makes a tile remember its tab set when the user clicks away to\n // another top-nav page and back. Keyed by top-nav kind \u2014 see the\n // taxonomy's `topNavFor`.\n pageTabs: {},\n };\n _syncActiveTab(leaf);\n return leaf;\n}\n\nexport function makeSplit({ dir = 'h', children = [], sizes = null } = {}) {\n return {\n id: _newId('split'),\n kind: 'split',\n parentId: null,\n dir,\n children: children.slice(),\n sizes: sizes ? sizes.slice() : children.map(() => 1),\n };\n}\n\n/**\n * TileTree \u2014 holds the node table and root, exposes mutation operations.\n * Every mutation operates by node id; the tree never hands out raw nodes\n * to callers (defensive copies via .get()).\n */\nexport class TileTree {\n constructor() {\n this.nodes = new Map();\n this.rootId = null;\n this.focusedLeafId = null;\n // Stack of recently-focused leaves. New content loads target the\n // top of this stack \u2014 i.e. the last leaf the user focused.\n this.focusStack = [];\n }\n\n // \u2500\u2500 Read 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\n get(id) { return this.nodes.get(id) || null; }\n root() { return this.rootId ? this.get(this.rootId) : null; }\n focused() { return this.focusedLeafId ? this.get(this.focusedLeafId) : null; }\n\n leaves() {\n const out = [];\n const walk = (id) => {\n const n = this.get(id);\n if (!n) return;\n if (n.kind === 'leaf') out.push(n);\n else n.children.forEach(walk);\n };\n if (this.rootId) walk(this.rootId);\n return out;\n }\n\n // \u2500\u2500 Construction \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 setRoot(node) {\n this._register(node);\n this.rootId = node.id;\n if (node.kind === 'leaf') {\n this.focus(node.id);\n }\n return node.id;\n }\n\n _register(node) {\n this.nodes.set(node.id, node);\n if (node.kind === 'split') {\n for (const cid of node.children) {\n const c = this.get(cid);\n if (c) c.parentId = node.id;\n }\n }\n }\n\n // \u2500\u2500 Focus \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 focus(leafId) {\n if (!this.nodes.has(leafId)) return;\n const node = this.get(leafId);\n if (node.kind !== 'leaf') return;\n this.focusedLeafId = leafId;\n this.focusStack = this.focusStack.filter((id) => id !== leafId);\n this.focusStack.unshift(leafId);\n }\n\n /** The leaf where \"open content\" should land \u2014 top of focus stack,\n * skipping panel:* tiles which are chrome, not content. */\n primaryLeafId() {\n for (const id of this.focusStack) {\n const n = this.nodes.get(id);\n if (n && n.kind === 'leaf' && !_isPanel(n)) return id;\n }\n for (const leaf of this.leaves()) {\n if (!_isPanel(leaf)) return leaf.id;\n }\n return null;\n }\n\n // \u2500\u2500 Mutations \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 /** Replace the leaf's tab list with a single tab carrying `content`.\n * Closes any existing tabs in that leaf \u2014 this is the \"open in\n * place\" / \"replace tile\" path that the WM has always used. */\n setLeafContent(leafId, content, title = '') {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n if (content) {\n n.tabs = [{\n kind: content.kind,\n props: content.props || {},\n title: title || content.kind || '',\n }];\n n.activeTabIdx = 0;\n } else {\n n.tabs = [];\n n.activeTabIdx = 0;\n }\n _syncActiveTab(n);\n if (title) n.title = title;\n return true;\n }\n\n /** Page-aware swap: archive the leaf's current tab list under its\n * current top-nav key, then restore the target top-nav's archived\n * list (or initialize one with the requested kind/props). Used\n * by top-nav / palette navigation so each top-nav page keeps its\n * own tab set across page switches.\n *\n * Parameters:\n * leafId The primary leaf.\n * target { kind, props, title } \u2014 what the user clicked.\n * currentTopNav Top-nav of the leaf's current page (may be null).\n * targetTopNav Top-nav of the target kind (must be set).\n *\n * When `currentTopNav === targetTopNav` (already on the right\n * page), the swap is skipped and we either focus an existing\n * matching tab or replace the active one. This is what makes\n * clicking the top-nav button for the page you're already on\n * feel like \"Home for this page\" without resetting tabs. */\n swapToPage(leafId, target, currentTopNav, targetTopNav, opts = {}) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n if (!target || !target.kind) return false;\n n.pageTabs = n.pageTabs || {};\n const recordHistory = opts.recordHistory !== false;\n\n // Helper: mirror what replaceActiveTabContent does \u2014 push the\n // outgoing tab onto the (incoming) tab's history stack so\n // Backspace can walk back. `extra` is an optional annotation\n // (e.g. `{ topNav: currentTopNav }`) for cross-page entries so\n // navigateBack can re-route via swapToPage when popping.\n const _pushPrev = (history, prev, next, extra) => {\n if (!recordHistory || !prev || !prev.kind) return history;\n const last = history[history.length - 1];\n const sameAsPrev = last\n && last.kind === prev.kind\n && String(last.props?.id ?? '') === String(prev.props?.id ?? '');\n const sameAsTarget = next\n && prev.kind === next.kind\n && String(prev.props?.id ?? '') === String(next.props?.id ?? '');\n if (!sameAsPrev && !sameAsTarget) {\n history.push({\n kind: prev.kind,\n props: prev.props || {},\n title: prev.title || '',\n ...(extra || {}),\n });\n }\n return history;\n };\n\n const same = currentTopNav && currentTopNav === targetTopNav;\n if (same) {\n const tabs = n.tabs || [];\n const matchIdx = tabs.findIndex((t) =>\n t.kind === target.kind\n && String(t.props?.id ?? '') === String(target.props?.id ?? ''));\n if (matchIdx >= 0) {\n n.activeTabIdx = matchIdx;\n } else if (tabs.length === 0) {\n n.tabs = [{\n kind: target.kind,\n props: target.props || {},\n title: target.title || target.kind,\n history: [],\n }];\n n.activeTabIdx = 0;\n } else {\n const prev = tabs[n.activeTabIdx];\n const history = Array.isArray(prev?.history) ? prev.history : [];\n _pushPrev(history, prev, target, null);\n tabs[n.activeTabIdx] = {\n kind: target.kind,\n props: target.props || {},\n title: target.title || target.kind,\n history,\n };\n }\n _syncActiveTab(n);\n return true;\n }\n\n // Snapshot the source tab BEFORE archiving \u2014 used to seed the\n // target tab's history with a cross-page back entry.\n const srcTabs = Array.isArray(n.tabs) ? n.tabs : [];\n const srcActive = srcTabs[n.activeTabIdx];\n const sourceEntry = (currentTopNav && srcActive?.kind) ? {\n kind: srcActive.kind,\n props: srcActive.props || {},\n title: srcActive.title || '',\n topNav: currentTopNav,\n } : null;\n\n // Archive the current page \u2014 preserve `history` so the user\n // doesn't lose in-tile back depth when they walk away and\n // return to this page.\n if (currentTopNav && srcTabs.length > 0) {\n // Don't archive transient tabs (add-row forms etc.) \u2014 they\n // shouldn't reappear when the user returns to this page.\n const archived = srcTabs\n .filter((t) => !t.transient)\n .map((t) => ({\n kind: t.kind, props: { ...(t.props || {}) }, title: t.title,\n history: Array.isArray(t.history) ? t.history.slice() : [],\n }));\n n.pageTabs[currentTopNav] = {\n tabs: archived,\n activeTabIdx: Math.max(0, Math.min(archived.length - 1, n.activeTabIdx || 0)),\n };\n }\n\n // Restore the target page, or initialize fresh.\n const saved = n.pageTabs[targetTopNav];\n if (saved && Array.isArray(saved.tabs) && saved.tabs.length > 0) {\n n.tabs = saved.tabs.map((t) => ({\n kind: t.kind, props: { ...(t.props || {}) }, title: t.title,\n history: Array.isArray(t.history) ? t.history.slice() : [],\n }));\n n.activeTabIdx = Math.max(0,\n Math.min(n.tabs.length - 1, saved.activeTabIdx || 0));\n // The caller asked for a specific entity (props.id), for a page\n // kind that ISN'T the nav category itself (e.g. `settings` lives\n // under the `home` topNav), or for this page WITH PARTICULAR PROPS\n // (a filter, an ad-hoc expression). In all three we must surface the\n // requested target rather than silently showing whatever the\n // restored page happened to hold.\n //\n // THE THIRD CASE WAS MISSING, and it is not a corner: \"show me\n // everything open on bo\" is `kind: 'backlog'` with an `expr` and no\n // id \u2014 no entity, and `backlog` IS its own nav category \u2014 so it fell\n // through to the bare-restore branch and put back whatever the page\n // last held. If that was a record you had been reading, the tile did\n // not visibly change at all, and the click looked broken. (Found and\n // fixed in BugDesk's fork first; this is that fix, upstream.)\n //\n // A BARE page switch still restores as-is, which is the whole point\n // of archiving tabs: clicking a top-nav chip carries no props and\n // must put back the tab you left open.\n const wantsTarget = target.props?.id != null\n || target.kind !== targetTopNav\n || Object.keys(target.props || {}).length > 0;\n if (wantsTarget) {\n const wantId = target.props?.id != null;\n const matchIdx = n.tabs.findIndex((t) =>\n t.kind === target.kind\n && (!wantId\n || String(t.props?.id ?? '') === String(target.props.id)));\n if (matchIdx >= 0) {\n n.activeTabIdx = matchIdx;\n } else {\n const history = [];\n if (sourceEntry) _pushPrev(history, sourceEntry, target, { topNav: currentTopNav });\n n.tabs.push({\n kind: target.kind,\n props: target.props || {},\n title: target.title || target.kind,\n history,\n });\n n.activeTabIdx = n.tabs.length - 1;\n }\n }\n } else {\n const history = [];\n if (sourceEntry) _pushPrev(history, sourceEntry, target, { topNav: currentTopNav });\n n.tabs = [{\n kind: target.kind,\n props: target.props || {},\n title: target.title || target.kind,\n history,\n }];\n n.activeTabIdx = 0;\n }\n _syncActiveTab(n);\n return true;\n }\n\n /** Update any tab whose (kind, props.id) matches the given pair to\n * reflect a new label. Called from the WM in response to the\n * embedder's entity-renamed bus event, so tab titles stay live as\n * the underlying entity is renamed. */\n renameMatchingTabs(kind, id, newLabel) {\n if (!kind || !id || !newLabel) return false;\n let touched = false;\n const visit = (tabs) => {\n if (!Array.isArray(tabs)) return;\n for (const t of tabs) {\n if (t.kind === kind && String(t.props?.id ?? '') === String(id)) {\n t.title = String(newLabel);\n if (t.props) t.props.label = String(newLabel);\n touched = true;\n }\n }\n };\n for (const n of this.nodes.values()) {\n if (n.kind !== 'leaf') continue;\n visit(n.tabs);\n if (n.pageTabs) {\n for (const arc of Object.values(n.pageTabs)) visit(arc?.tabs);\n }\n _syncActiveTab(n);\n }\n return touched;\n }\n\n /** Replace the active tab's content in place, preserving every\n * other tab in the leaf. This is the \"in-tile navigation\" path:\n * clicking a row on a landing page, walking back via Backspace,\n * or clicking a breadcrumb segment should swap THIS tab's\n * content without disturbing the user's other open tabs.\n *\n * By default the previous content is pushed onto the active\n * tab's `history` stack so the user can Backspace through their\n * own click-path (per-tab \"browser back\"). Callers that are\n * themselves popping the history pass `{ recordHistory: false }`\n * so we don't double-record. Same applies to programmatic loads\n * that aren't user navigations.\n *\n * Falls back to `setLeafContent` for empty leaves so the WM\n * never has to special-case the initial load. Returns true on\n * success. */\n replaceActiveTabContent(leafId, content, title = '', opts = {}) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n const tabs = Array.isArray(n.tabs) ? n.tabs : [];\n if (tabs.length === 0) {\n return this.setLeafContent(leafId, content, title);\n }\n if (!content) return false;\n const idx = Math.max(0, Math.min(tabs.length - 1, n.activeTabIdx || 0));\n const prev = tabs[idx];\n const recordHistory = opts.recordHistory !== false;\n const history = Array.isArray(prev?.history) ? prev.history : [];\n if (recordHistory && prev && prev.kind) {\n // Avoid pushing duplicates when the user re-clicks the\n // same row \u2014 same kind + same primary id \u2192 no-op.\n const last = history[history.length - 1];\n const sameAsPrev = last\n && last.kind === prev.kind\n && String(last.props?.id ?? '') === String(prev.props?.id ?? '');\n const sameAsTarget = prev.kind === content.kind\n && String(prev.props?.id ?? '') === String(content.props?.id ?? '');\n if (!sameAsPrev && !sameAsTarget) {\n history.push({\n kind: prev.kind,\n props: prev.props || {},\n title: prev.title || '',\n });\n }\n }\n tabs[idx] = {\n kind: content.kind,\n props: content.props || {},\n title: title || content.kind || '',\n history,\n };\n _syncActiveTab(n);\n return true;\n }\n\n /** Merge `patch` into the active tab's `props`. Used when an\n * in-tile editor surfaces metadata that should be captured in\n * history snapshots (e.g. which sub-tab is open) so Backspace\n * restores the user's view, not the default landing. Does not\n * re-render or push history \u2014 pure metadata write. */\n updateActiveTabProps(leafId, patch) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n const tabs = Array.isArray(n.tabs) ? n.tabs : [];\n if (tabs.length === 0) return false;\n const idx = Math.max(0, Math.min(tabs.length - 1, n.activeTabIdx || 0));\n const cur = tabs[idx];\n if (!cur) return false;\n cur.props = { ...(cur.props || {}), ...(patch || {}) };\n return true;\n }\n\n /** Pop one entry off the active tab's history stack and return it,\n * or null when empty. Doesn't mutate the tab beyond the stack \u2014\n * the caller is expected to apply the returned content via\n * `replaceActiveTabContent(..., { recordHistory: false })`. */\n popActiveTabHistory(leafId) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return null;\n const tabs = Array.isArray(n.tabs) ? n.tabs : [];\n if (tabs.length === 0) return null;\n const idx = Math.max(0, Math.min(tabs.length - 1, n.activeTabIdx || 0));\n const cur = tabs[idx];\n const history = Array.isArray(cur?.history) ? cur.history : [];\n if (history.length === 0) return null;\n return history.pop();\n }\n\n /** Inspect the active tab's history stack without popping. Used\n * by the keymap to show / hide the back affordance and for\n * diagnostics. */\n activeTabHistory(leafId) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return [];\n const tabs = Array.isArray(n.tabs) ? n.tabs : [];\n if (tabs.length === 0) return [];\n const idx = Math.max(0, Math.min(tabs.length - 1, n.activeTabIdx || 0));\n const cur = tabs[idx];\n return Array.isArray(cur?.history) ? cur.history : [];\n }\n\n /** Append a new tab to a leaf and focus it. Returns the new tab's\n * index, or -1 on failure. Panel tiles never grow tabs (they're\n * chrome). */\n appendLeafTab(leafId, content, title = '', opts = {}) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return -1;\n if (_isPanel(n)) return -1;\n if (!content) return -1;\n const tab = {\n kind: content.kind,\n props: content.props || {},\n title: title || content.kind || '',\n // Transient tabs (e.g. an add-row form) are not persisted /\n // restored \u2014 see serialize() + the pageTabs archive.\n ...(opts.transient ? { transient: true } : {}),\n };\n n.tabs = Array.isArray(n.tabs) ? n.tabs : [];\n n.tabs.push(tab);\n // `background` appends WITHOUT switching to it \u2014 the caller asked for a\n // tab to come back to, not for the page to change under them. Without\n // this, \"open in a background tab\" was indistinguishable from an\n // ordinary click: the tab arrived and took the screen with it.\n //\n // Returns the new tab's index either way, NOT `activeTabIdx`: for a\n // background tab those differ, and a caller that wants to address the\n // tab it just made needs the index of that tab.\n if (!opts.background) {\n n.activeTabIdx = n.tabs.length - 1;\n _syncActiveTab(n);\n }\n return n.tabs.length - 1;\n }\n\n /** Switch the active tab on a leaf. No-op if `idx` is out of range. */\n setActiveLeafTab(leafId, idx) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n if (!Array.isArray(n.tabs) || idx < 0 || idx >= n.tabs.length) return false;\n n.activeTabIdx = idx;\n _syncActiveTab(n);\n return true;\n }\n\n /** Remove a tab. If the active tab was removed, the previous one\n * becomes active. If the last tab was removed, the leaf's content\n * drops to null (and the WM should treat that as an empty tile \u2014\n * same as before tabs existed). Returns the (possibly clamped)\n * active index after the operation, or -1 on failure. */\n removeLeafTab(leafId, idx) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return -1;\n if (!Array.isArray(n.tabs) || idx < 0 || idx >= n.tabs.length) return -1;\n n.tabs.splice(idx, 1);\n if (n.tabs.length === 0) {\n n.activeTabIdx = 0;\n _syncActiveTab(n);\n return 0;\n }\n if (idx < n.activeTabIdx) n.activeTabIdx -= 1;\n else if (idx === n.activeTabIdx) n.activeTabIdx = Math.max(0, idx - 1);\n _syncActiveTab(n);\n return n.activeTabIdx;\n }\n\n /** Bulk-close every tab in a leaf except the one at `keepIdx`.\n * Used by the tab right-click \"Close others\" action. */\n closeOtherTabs(leafId, keepIdx) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n const tabs = n.tabs || [];\n if (keepIdx < 0 || keepIdx >= tabs.length) return false;\n n.tabs = [tabs[keepIdx]];\n n.activeTabIdx = 0;\n _syncActiveTab(n);\n return true;\n }\n\n /** Close every tab after `idx`. */\n closeTabsAfter(leafId, idx) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n const tabs = n.tabs || [];\n if (idx < 0 || idx >= tabs.length - 1) return false;\n n.tabs = tabs.slice(0, idx + 1);\n if (n.activeTabIdx > idx) n.activeTabIdx = idx;\n _syncActiveTab(n);\n return true;\n }\n\n /** Close every tab before `idx`. */\n closeTabsBefore(leafId, idx) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n const tabs = n.tabs || [];\n if (idx <= 0 || idx >= tabs.length) return false;\n n.tabs = tabs.slice(idx);\n n.activeTabIdx = Math.max(0, n.activeTabIdx - idx);\n _syncActiveTab(n);\n return true;\n }\n\n /** Reorder a tab from `from` to `to` within the same leaf. */\n moveLeafTab(leafId, from, to) {\n const n = this.get(leafId);\n if (!n || n.kind !== 'leaf') return false;\n const tabs = n.tabs || [];\n if (from < 0 || from >= tabs.length || to < 0 || to >= tabs.length) return false;\n const moved = tabs.splice(from, 1)[0];\n tabs.splice(to, 0, moved);\n // Keep the moved tab focused if it was the active one.\n if (n.activeTabIdx === from) n.activeTabIdx = to;\n else if (from < n.activeTabIdx && to >= n.activeTabIdx) n.activeTabIdx -= 1;\n else if (from > n.activeTabIdx && to <= n.activeTabIdx) n.activeTabIdx += 1;\n _syncActiveTab(n);\n return true;\n }\n\n /**\n * C33. MOVE ONE TAB FROM ONE LEAF TO ANOTHER, AS ONE MUTATION.\n *\n * Every other tab operation on this class takes ONE `leafId`, and that was\n * a complete description of the model until a tab could be dragged into a\n * different tile: `appendLeafTab`, `removeLeafTab`, `moveLeafTab` and the\n * three bulk closes all begin and end inside a single leaf. The renderer's\n * refusal to wire `onDropFromOtherPane` named this absence as one of its\n * two reasons (`tile_renderer.js`, `_topTabCallbacks`); this is that half.\n *\n * ONE FUNCTION RATHER THAN A COMPOSE, and that is why it lives here rather\n * than in the WM. `removeLeafTab` then `appendLeafTab` is two mutations\n * with a moment between them in which the tab exists nowhere \u2014 and the\n * second can FAIL (a panel destination refuses tabs), which leaves the tree\n * short one tab and nothing on screen saying where it went. Every guard is\n * therefore taken before the first splice.\n *\n * TWO NODES ARE RE-SYNCED, WHICH IS WHAT MAKES THIS DIFFERENT. `tabs` is\n * the source of truth and `content`/`title` mirror `tabs[active]`\n * (`_syncActiveTab`). Every existing mutation touches one leaf, so one\n * re-sync is right; this one touches two, and skipping the SOURCE's leaves\n * a pane whose chrome still names \u2014 and whose body still mounts \u2014 a tab it\n * no longer holds. That is the mistake this operation invites, and it is\n * what `web/js/shell/tab_drop.test.mjs` asserts against in the consumer.\n *\n * A same-leaf call is a REORDER and delegates, so there is one\n * implementation of \"a tab changed position within its strip\" rather than\n * two that will disagree about the active-index clamp.\n *\n * @param {string} fromLeafId\n * @param {number} fromIdx\n * @param {string} toLeafId\n * @param {number} [toIdx=-1] where to insert; -1 (or past the end) appends\n * @returns {{ok: boolean, toIdx: number, emptied: boolean}|null} null when\n * the move was refused. `emptied` tells the caller the source pane now\n * holds nothing, which is its cue to re-seed rather than leave a blank\n * tile \u2014 the never-empty-tile invariant is the WM's to keep, not this\n * class's.\n */\n moveTabToLeaf(fromLeafId, fromIdx, toLeafId, toIdx = -1) {\n const from = this.get(fromLeafId);\n const to = this.get(toLeafId);\n if (!from || from.kind !== 'leaf') return null;\n if (!to || to.kind !== 'leaf') return null;\n const tabs = Array.isArray(from.tabs) ? from.tabs : [];\n if (!Number.isInteger(fromIdx) || fromIdx < 0 || fromIdx >= tabs.length) return null;\n // Panel tiles are chrome, not content, and never grow tabs \u2014 the same\n // refusal `appendLeafTab` makes, taken here BEFORE anything is spliced\n // so a refused destination cannot cost the source its tab.\n if (_isPanel(to)) return null;\n if (fromLeafId === toLeafId) {\n const dest = (!Number.isInteger(toIdx) || toIdx < 0 || toIdx >= tabs.length)\n ? tabs.length - 1 : toIdx;\n if (!this.moveLeafTab(fromLeafId, fromIdx, dest)) return null;\n return { ok: true, toIdx: dest, emptied: false };\n }\n\n const [moved] = from.tabs.splice(fromIdx, 1);\n // THE SOURCE'S ACTIVE INDEX, WITH `removeLeafTab`'S OWN THREE BRANCHES.\n // Spelled out rather than delegated because `removeLeafTab` re-syncs\n // and returns, and the splice above has already happened; the\n // arithmetic is the contract, so it is written where it can be compared\n // against the original line for line.\n if (from.tabs.length === 0) from.activeTabIdx = 0;\n else if (fromIdx < from.activeTabIdx) from.activeTabIdx -= 1;\n else if (fromIdx === from.activeTabIdx) from.activeTabIdx = Math.max(0, fromIdx - 1);\n\n to.tabs = Array.isArray(to.tabs) ? to.tabs : [];\n const at = (!Number.isInteger(toIdx) || toIdx < 0 || toIdx > to.tabs.length)\n ? to.tabs.length : toIdx;\n to.tabs.splice(at, 0, moved);\n // THE ARRIVING TAB IS THE ONE YOU WANTED TO SEE. A drop is a deliberate\n // act naming one tab and one destination; leaving the destination on\n // whatever it was already showing would make the gesture look like it\n // did nothing, which is the failure mode of every silent move.\n to.activeTabIdx = at;\n\n _syncActiveTab(from);\n _syncActiveTab(to);\n return { ok: true, toIdx: at, emptied: from.tabs.length === 0 };\n }\n\n /**\n * Split a leaf in the given direction; existing content stays in the\n * original leaf, a new empty leaf is added next to it. Returns the\n * new leaf id, or null on failure.\n */\n split(leafId, dir /* 'h' | 'v' */) {\n const leaf = this.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return null;\n\n const newLeaf = makeLeaf();\n // Case 1: root split. Wrap root in a new Split.\n if (!leaf.parentId) {\n const wrap = makeSplit({ dir, children: [leaf.id, newLeaf.id] });\n this._register(newLeaf);\n this._register(wrap);\n this.rootId = wrap.id;\n leaf.parentId = wrap.id;\n newLeaf.parentId = wrap.id;\n this.focus(newLeaf.id);\n return newLeaf.id;\n }\n\n const parent = this.get(leaf.parentId);\n const idx = parent.children.indexOf(leaf.id);\n if (idx < 0) return null;\n\n // Case 2: parent already matches the split direction \u2014 insert\n // the new leaf as a sibling.\n if (parent.dir === dir) {\n parent.children.splice(idx + 1, 0, newLeaf.id);\n const avg = parent.sizes.reduce((a, b) => a + b, 0) / parent.children.length;\n parent.sizes.splice(idx + 1, 0, avg);\n newLeaf.parentId = parent.id;\n this._register(newLeaf);\n this.focus(newLeaf.id);\n return newLeaf.id;\n }\n\n // Case 3: cross-direction \u2014 wrap the original leaf in a new\n // Split that contains [leaf, newLeaf]. Replace leaf in parent.\n const wrap = makeSplit({ dir, children: [leaf.id, newLeaf.id] });\n wrap.parentId = parent.id;\n parent.children[idx] = wrap.id;\n leaf.parentId = wrap.id;\n newLeaf.parentId = wrap.id;\n this._register(newLeaf);\n this._register(wrap);\n this.focus(newLeaf.id);\n return newLeaf.id;\n }\n\n /**\n * Close a leaf, collapsing any single-child splits that result. If\n * the last leaf is closed, leaves an empty tree (no root) \u2014 caller\n * is expected to repopulate.\n */\n close(leafId) {\n const leaf = this.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return false;\n\n // Root leaf \u2014 clear the tree.\n if (!leaf.parentId) {\n this.nodes.clear();\n this.rootId = null;\n this.focusedLeafId = null;\n this.focusStack = [];\n return true;\n }\n\n const parent = this.get(leaf.parentId);\n const idx = parent.children.indexOf(leaf.id);\n parent.children.splice(idx, 1);\n parent.sizes.splice(idx, 1);\n this.nodes.delete(leaf.id);\n this.focusStack = this.focusStack.filter((id) => id !== leaf.id);\n\n // Parent collapse \u2014 if a Split has only one child left, replace\n // it with that child in *its* parent (or as root).\n this._collapse(parent);\n\n if (this.focusedLeafId === leaf.id) {\n const remaining = this.leaves();\n this.focusedLeafId = remaining.length ? remaining[0].id : null;\n if (this.focusedLeafId) this.focus(this.focusedLeafId);\n }\n return true;\n }\n\n _collapse(node) {\n if (!node || node.kind !== 'split') return;\n if (node.children.length > 1) return;\n if (node.children.length === 0) {\n // Empty split \u2014 remove from its parent (or clear root).\n if (!node.parentId) {\n this.nodes.delete(node.id);\n this.rootId = null;\n return;\n }\n const gp = this.get(node.parentId);\n const idx = gp.children.indexOf(node.id);\n gp.children.splice(idx, 1);\n gp.sizes.splice(idx, 1);\n this.nodes.delete(node.id);\n this._collapse(gp);\n return;\n }\n // Exactly one child \u2014 promote it.\n const only = this.get(node.children[0]);\n if (!node.parentId) {\n this.rootId = only.id;\n only.parentId = null;\n } else {\n const gp = this.get(node.parentId);\n const idx = gp.children.indexOf(node.id);\n gp.children[idx] = only.id;\n only.parentId = gp.id;\n }\n this.nodes.delete(node.id);\n }\n\n // \u2500\u2500 Directional focus \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 * Move focus from current leaf in the given direction. Walks up the\n * tree looking for an ancestor split whose direction matches the\n * move (h for left/right, v for up/down) and which has a sibling on\n * the requested side; then descends along the matching edge.\n */\n focusDir(dir /* 'left' | 'right' | 'up' | 'down' */) {\n const cur = this.focused();\n if (!cur) return false;\n const wantDir = (dir === 'left' || dir === 'right') ? 'h' : 'v';\n const forward = (dir === 'right' || dir === 'down');\n\n let child = cur;\n let parent = child.parentId ? this.get(child.parentId) : null;\n while (parent) {\n if (parent.dir === wantDir) {\n const idx = parent.children.indexOf(child.id);\n const sibIdx = forward ? idx + 1 : idx - 1;\n if (sibIdx >= 0 && sibIdx < parent.children.length) {\n const target = this._descendEdge(parent.children[sibIdx],\n wantDir, !forward);\n if (target) {\n this.focus(target);\n return true;\n }\n }\n }\n child = parent;\n parent = child.parentId ? this.get(child.parentId) : null;\n }\n return false;\n }\n\n /**\n * Walk down to a leaf. For the matching direction we take the\n * `edge` end (first/last). For the orthogonal direction we just\n * take the first child.\n */\n _descendEdge(nodeId, matchDir, takeFirst) {\n let n = this.get(nodeId);\n while (n && n.kind !== 'leaf') {\n const idx = n.dir === matchDir\n ? (takeFirst ? 0 : n.children.length - 1)\n : 0;\n n = this.get(n.children[idx]);\n }\n return n ? n.id : null;\n }\n\n // \u2500\u2500 Move tile \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 * Move the focused leaf one position in the given direction. Simple\n * version: swap with the immediate sibling in the matching parent;\n * if no such sibling, no-op (deep moves come later if needed).\n */\n moveDir(dir) {\n const cur = this.focused();\n if (!cur || !cur.parentId) return false;\n const wantDir = (dir === 'left' || dir === 'right') ? 'h' : 'v';\n const forward = (dir === 'right' || dir === 'down');\n\n let child = cur;\n let parent = this.get(child.parentId);\n while (parent && parent.dir !== wantDir) {\n child = parent;\n parent = child.parentId ? this.get(child.parentId) : null;\n }\n if (!parent) return false;\n const idx = parent.children.indexOf(child.id);\n const swap = forward ? idx + 1 : idx - 1;\n if (swap < 0 || swap >= parent.children.length) return false;\n [parent.children[idx], parent.children[swap]] =\n [parent.children[swap], parent.children[idx]];\n [parent.sizes[idx], parent.sizes[swap]] =\n [parent.sizes[swap], parent.sizes[idx]];\n return true;\n }\n\n // \u2500\u2500 Resize \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 setSplitSizes(splitId, sizes) {\n const n = this.get(splitId);\n if (!n || n.kind !== 'split') return false;\n if (sizes.length !== n.children.length) return false;\n n.sizes = sizes.slice();\n return true;\n }\n\n // \u2500\u2500 Serialization (for desktops persistence) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n serialize() {\n if (!this.rootId) return null;\n const dump = (id) => {\n const n = this.get(id);\n if (n.kind === 'leaf') {\n // Transient tabs (add-row forms etc.) are not persisted.\n const liveTabs = (Array.isArray(n.tabs) ? n.tabs : [])\n .filter((t) => !t.transient)\n .map((t) => ({ kind: t.kind, props: t.props, title: t.title }));\n return {\n kind: 'leaf',\n title: n.title,\n content: n.content,\n tabs: liveTabs,\n activeTabIdx: Math.max(0, Math.min(liveTabs.length - 1, n.activeTabIdx || 0)),\n // Page-archive: per-top-nav-kind saved tab lists so\n // switching to a previously-visited page restores\n // its tabs across reloads.\n pageTabs: n.pageTabs\n ? Object.fromEntries(\n Object.entries(n.pageTabs).map(([k, v]) => {\n const pt = (v.tabs || [])\n .filter((t) => !t.transient)\n .map((t) => ({ kind: t.kind, props: t.props, title: t.title }));\n return [k, {\n tabs: pt,\n activeTabIdx: Math.max(0, Math.min(pt.length - 1, v.activeTabIdx || 0)),\n }];\n }))\n : {},\n };\n }\n return {\n kind: 'split', dir: n.dir, sizes: n.sizes.slice(),\n children: n.children.map(dump),\n };\n };\n return {\n root: dump(this.rootId),\n focusedTitle: this.focused()?.title || null,\n };\n }\n\n static deserialize(blob) {\n const t = new TileTree();\n if (!blob || !blob.root) return t;\n const build = (raw) => {\n if (raw.kind === 'leaf') {\n // Forward-compat with the pre-tabs layout: an old\n // serialized leaf carries `content` but no `tabs`.\n // makeLeaf will derive a single-tab list from `content`.\n const leaf = makeLeaf({ content: raw.content, title: raw.title });\n if (Array.isArray(raw.tabs) && raw.tabs.length > 0) {\n leaf.tabs = raw.tabs.map((tab) => ({\n kind: tab.kind,\n props: tab.props || {},\n title: tab.title || tab.kind || '',\n }));\n leaf.activeTabIdx = Math.max(0,\n Math.min(leaf.tabs.length - 1, raw.activeTabIdx || 0));\n _syncActiveTab(leaf);\n }\n if (raw.pageTabs && typeof raw.pageTabs === 'object') {\n leaf.pageTabs = {};\n for (const [k, v] of Object.entries(raw.pageTabs)) {\n if (!v || !Array.isArray(v.tabs)) continue;\n leaf.pageTabs[k] = {\n tabs: v.tabs.map((t) => ({\n kind: t.kind,\n props: t.props || {},\n title: t.title || t.kind || '',\n })),\n activeTabIdx: v.activeTabIdx || 0,\n };\n }\n }\n return leaf;\n }\n const children = raw.children.map(build);\n const split = makeSplit({\n dir: raw.dir, children: children.map((c) => c.id),\n sizes: raw.sizes,\n });\n children.forEach((c) => t._register(c));\n return split;\n };\n const root = build(blob.root);\n t.setRoot(root);\n return t;\n }\n}\n", "/**\n * desktops.js \u2014 independent tile trees per virtual desktop, persisted\n * through the host's `state` capability under the logical key `desktops`.\n * Where that key actually lands is the embedder's business (see the host's\n * `resolvePath`); this module only names it.\n *\n * Each desktop = { id, label, tree (TileTree), windows ([]), panels }.\n * Switching desktops swaps the active tree under the renderer.\n */\n\nimport { TileTree, makeLeaf } from './tile_tree.js';\n\n/** What a FRESH desktop's panel tiles start as, when the embedder says nothing.\n *\n * C14. It is a DEFAULT, not a constant. An embedder whose navigator and\n * inspector are its own chrome OUTSIDE the WM root \u2014 an icon rail, say \u2014 has\n * no `panel:left` factory to mount, and every desktop it creates would open\n * with two \"no factory registered yet\" placeholders wedged either side of its\n * content. It cannot fix that after the fact: `wm.load()` canonicalizes the\n * panels in before it returns, and the renderer caches tile DOM per\n * (kind, props). The only place the answer can be given is here, before the\n * first desktop exists \u2014 which is what `panelDefaults` is for. */\nconst DEFAULT_PANEL_STATE = {\n left: true,\n right: true,\n bottom: true,\n};\n\n/**\n * `seed()` yields the leaf a fresh (or emptied) desktop starts with. It comes\n * from the taxonomy's root kind \u2014 see WindowManager, which supplies it. The\n * WM used to hardcode `{ kind: 'home', title: 'Home' }` here, which meant an\n * embedder with a different ontology still got an EcoAgent Home tile it had\n * never registered, and got the content_registry placeholder instead. There is\n * deliberately NO default: a silent 'home' fallback is the exact bug this\n * parameter exists to remove.\n */\nfunction _makeDesktop(label, seed, panelDefaults = DEFAULT_PANEL_STATE) {\n const tree = new TileTree();\n tree.setRoot(makeLeaf(seed()));\n return {\n id: `desk-${Math.random().toString(36).slice(2, 8)}`,\n label,\n tree,\n windows: [],\n // Boot default: whatever the embedder asked for, left + right + bottom\n // when it asked for nothing. Names are assigned by wm._canonicalize via\n // PANEL_TITLES.\n panels: { ...panelDefaults },\n };\n}\n\nexport class DesktopManager {\n /**\n * @param {object} opts\n * @param {function} opts.seed builds the root leaf. Required.\n * @param {object} [opts.panelDefaults] C14. Which panel tiles a fresh\n * desktop opens with, merged over `DEFAULT_PANEL_STATE`. Omitted, every\n * desktop opens with all three \u2014 today's behaviour, unchanged.\n */\n constructor({ seed, panelDefaults = null } = {}) {\n if (typeof seed !== 'function') {\n throw new Error('DesktopManager: a `seed` function is required (the taxonomy root leaf)');\n }\n this.seed = seed;\n this.panelDefaults = { ...DEFAULT_PANEL_STATE, ...(panelDefaults || {}) };\n this.desktops = [_makeDesktop('1', seed, this.panelDefaults)];\n this.activeIdx = 0;\n }\n\n active() { return this.desktops[this.activeIdx]; }\n\n switchTo(idx) {\n if (idx < 0 || idx >= this.desktops.length) return false;\n if (idx === this.activeIdx) return false;\n this.activeIdx = idx;\n return true;\n }\n\n ensureCount(n) {\n while (this.desktops.length < n) {\n this.desktops.push(_makeDesktop(\n String(this.desktops.length + 1), this.seed, this.panelDefaults));\n }\n }\n\n addDesktop(label = null) {\n const d = _makeDesktop(\n label || String(this.desktops.length + 1), this.seed, this.panelDefaults);\n this.desktops.push(d);\n return d;\n }\n\n serialize() {\n return {\n activeIdx: this.activeIdx,\n desktops: this.desktops.map((d) => ({\n id: d.id, label: d.label,\n tree: d.tree.serialize(),\n panels: { ...d.panels },\n // windows serialized at WM level since we don't own\n // managed-window state in this module.\n })),\n };\n }\n\n static deserialize(blob, { seed, panelDefaults = null } = {}) {\n const m = new DesktopManager({ seed, panelDefaults });\n if (!blob || !Array.isArray(blob.desktops) || blob.desktops.length === 0) return m;\n m.desktops = blob.desktops.map((raw) => ({\n id: raw.id || `desk-${Math.random().toString(36).slice(2,8)}`,\n label: raw.label || '?',\n tree: raw.tree ? TileTree.deserialize(raw.tree) : new TileTree(),\n windows: [],\n // A RESTORED desktop's own answer wins over the default: the user\n // closed that panel, and re-opening it on every reload is the bug\n // this merge order avoids. The default only fills a key the stored\n // blob predates.\n panels: { ...m.panelDefaults, ...(raw.panels || {}) },\n }));\n // Empty tree \u2192 seed with the taxonomy root so the desktop is usable.\n for (const d of m.desktops) {\n if (!d.tree.rootId) d.tree.setRoot(makeLeaf(seed()));\n }\n m.activeIdx = Math.min(Math.max(0, blob.activeIdx | 0), m.desktops.length - 1);\n return m;\n }\n}\n\n/**\n * Persistence helpers. They take a host `state` capability; a host without\n * one is legal and they no-op. We don't fail loudly \u2014 the WM stays functional\n * against a host that cannot persist (e.g. the standalone demo).\n */\n\nconst DESKTOPS_KEY = 'desktops';\n\nexport async function loadDesktops(state) {\n if (!state) return null;\n try {\n return await state.read(DESKTOPS_KEY);\n } catch (err) {\n console.warn('[desktops] load failed', err);\n return null;\n }\n}\n\nexport async function saveDesktops(state, blob) {\n if (!state) return false;\n try {\n await state.write(DESKTOPS_KEY, blob);\n return true;\n } catch (err) {\n console.warn('[desktops] save failed', err);\n return false;\n }\n}\n", "/**\n * entity_sources.js \u2014 the entity-source PORT.\n *\n * The command palette, the tile hamburger menu, and any other \"find me a\n * thing\" surface need a list of every searchable entity. Where those\n * entities come from \u2014 which endpoint returns them, what a row looks\n * like, what to call it \u2014 is the EMBEDDER's knowledge, not the shell's.\n *\n * So the source list is INJECTED. The embedder describes its sources and\n * hands them to `createEntityCatalog()` at boot. Nothing in this file\n * names an entity type or a bridge route.\n *\n * ## EntitySource\n *\n * navKind The content kind used to open a row via `wm.navigate`.\n * The taxonomy resolves its icon + label.\n *\n * list `(api) => Promise<rawList>` \u2014 per-source fetcher. Used by\n * the fan-out path, and by `loadOne` when a caller wants a\n * subset rather than the whole catalog.\n *\n * pluck? `(raw) => Array` \u2014 extract the actual list when the\n * endpoint wraps it. Defaults to \"raw is an array, else\n * raw.items if that's an array, else []\".\n *\n * shape `(row) => { id, label, hint? } | null` \u2014 normalise one raw\n * row to the record shape every search surface consumes.\n * Return `null` to skip the row.\n *\n * ## Aggregation\n *\n * An embedder whose backend can answer for every source in ONE call\n * passes `aggregate: (api) => Promise<{ [navKind]: rawList } | null>`.\n * The catalog prefers it and fans out only for the navKinds it failed to\n * answer for. Absent \u21D2 straight fan-out. Absent capability, absent key:\n * the shell never probes for a named endpoint of its own accord.\n *\n * Errors from any single source are logged and treated as \"empty\", so\n * one broken endpoint can't blank the entire palette.\n */\n\nexport function createEntityCatalog({ sources, aliases = {}, aggregate = null } = {}) {\n if (!Array.isArray(sources)) {\n throw new TypeError('createEntityCatalog: `sources` must be an array');\n }\n for (const s of sources) {\n if (!s?.navKind || typeof s.list !== 'function' || typeof s.shape !== 'function') {\n throw new TypeError(\n `createEntityCatalog: source '${s?.navKind ?? '?'}' needs navKind + list() + shape()`);\n }\n }\n if (aggregate != null && typeof aggregate !== 'function') {\n throw new TypeError('createEntityCatalog: `aggregate` must be a function when present');\n }\n\n const _sources = Object.freeze(sources.map((s) => Object.freeze({ ...s })));\n const _byKind = new Map(_sources.map((s) => [s.navKind, s]));\n const _aliases = Object.freeze({ ...aliases });\n\n const get = (navKind) => _byKind.get(navKind) || null;\n\n /** Unwrap one source's raw payload into a row array. */\n const _extract = (src, raw) => {\n const list = src.pluck\n ? src.pluck(raw)\n : (Array.isArray(raw) ? raw\n : Array.isArray(raw?.items) ? raw.items\n : []);\n return Array.isArray(list) ? list : [];\n };\n\n /** Shape a source's rows, dropping the ones `shape` rejects or\n * throws on. Shared by the palette AND the tile menu so their\n * semantics cannot drift apart \u2014 the menu used to re-implement\n * this loop by hand. */\n const shapeRows = (navKind, rows) => {\n const src = _byKind.get(navKind);\n if (!src || !Array.isArray(rows)) return [];\n const out = [];\n for (const row of rows) {\n let shaped;\n try { shaped = src.shape(row); }\n catch (err) {\n console.warn('[entity-catalog]', navKind, 'shape failed', err, row);\n continue;\n }\n if (!shaped || !shaped.id) continue;\n out.push(shaped);\n }\n return out;\n };\n\n /** Fetch ONE source's raw rows. Used by surfaces that only care\n * about a subset of the catalog. */\n const loadOne = async (navKind, api) => {\n const src = _byKind.get(navKind);\n if (!src || !api) return [];\n try { return _extract(src, await src.list(api)); }\n catch (err) {\n console.warn('[entity-catalog]', navKind, 'fetch failed', err);\n return [];\n }\n };\n\n /** Fetch every source's raw rows, grouped by navKind.\n *\n * The aggregator runs first when the embedder supplied one; the\n * fan-out only fires for sources it didn't answer for (it threw, it\n * isn't there, or it doesn't know a newly-added navKind yet).\n *\n * Shape: `{ [navKind]: rawRow[] }` \u2014 every navKind is always\n * present (empty on failure) so consumers can destructure safely. */\n const loadGrouped = async (api) => {\n const out = {};\n for (const s of _sources) out[s.navKind] = [];\n if (!api) return out;\n\n let bulk = null;\n if (aggregate) {\n try { bulk = await aggregate(api); }\n catch (err) {\n console.warn('[entity-catalog] aggregate failed, falling back to fan-out', err);\n }\n if (bulk && typeof bulk !== 'object') bulk = null;\n }\n\n const missing = [];\n for (const src of _sources) {\n const raw = bulk ? bulk[src.navKind] : undefined;\n if (raw == null) { missing.push(src); continue; }\n out[src.navKind] = _extract(src, raw);\n }\n if (missing.length === 0) return out;\n\n await Promise.all(missing.map(async (src) => {\n out[src.navKind] = await loadOne(src.navKind, api);\n }));\n return out;\n };\n\n /** Flat-and-shaped form for search surfaces. Builds on\n * `loadGrouped` so there is a single fetch path and the palette /\n * explorer can't drift apart in coverage. */\n const loadAll = async (api) => {\n const grouped = await loadGrouped(api);\n const out = [];\n for (const src of _sources) {\n for (const shaped of shapeRows(src.navKind, grouped[src.navKind] || [])) {\n out.push({ kind: src.navKind, ...shaped });\n }\n }\n return out;\n };\n\n /** Resolve a user-typed type prefix to a list of canonical navKinds.\n * Accepts the embedder's aliases first, then navKinds verbatim,\n * then the underscore spelling of a hyphenated kind. Returns an\n * array of navKinds on hit, `null` otherwise. */\n const resolveTypePrefix = (prefix) => {\n const p = String(prefix || '').toLowerCase().trim();\n if (!p) return null;\n if (_aliases[p]) return _aliases[p];\n for (const src of _sources) {\n if (src.navKind === p) return [src.navKind];\n if (src.navKind.replace(/-/g, '_') === p) return [src.navKind];\n }\n return null;\n };\n\n return Object.freeze({\n sources: _sources,\n get,\n loadOne,\n loadGrouped,\n loadAll,\n shapeRows,\n resolveTypePrefix,\n });\n}\n", "/**\n * keymap.js \u2014 Alt-based shortcuts for the tiling window manager.\n *\n * Alt+H split focused tile horizontally (new empty pane)\n * Alt+V split focused tile vertically (new empty pane)\n * Alt+Shift+H open focused tile's content in a horizontal split\n * Alt+Shift+V open focused tile's content in a vertical split\n * Alt+T open focused tile's content in a new tab\n * Alt+N open focused tile's content in a new window\n * Alt+W close focused tile\n * Alt+F promote focused tile <-> managed window (moves it)\n * Alt+ArrowLeft/Right/Up/Down move focus\n * Alt+Shift+Arrow\u2026 swap focused tile with neighbour\n * Alt+1..9 switch desktop\n * Alt+Shift+1..9 move focused tile to desktop N\n * Ctrl+ArrowLeft/Right iterate virtual desktops\n * Shift+ArrowLeft/Right iterate bottom-panel tabs (when present)\n * Ctrl+Tab / Ctrl+Shift+Tab next / previous main-panel tab (focused tile)\n * F1..F8 jump to the Nth top-nav page\n * Ctrl+K open command palette\n * ? open Help (lists every shortcut) \u2014 bound globally\n * by help_modal.js, not here\n * Esc close palette / managed-modal\n *\n * The full user-facing reference is the Help modal's \"Keyboard shortcuts\"\n * topic (ui/js/help/help_service.js) \u2014 keep that table in sync when\n * binding new chords here.\n *\n * The handler refuses to act when focus is in an editable field, unless\n * the chord uses the Alt modifier (which is never typed into a field).\n *\n * \u2500\u2500 It returns a disposer, and that is not decoration \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * The listener is on `document` and is bound to ONE shell's `wm` and\n * `palette`. An embedder that can build a second shell in the same page \u2014\n * Tables rebuilds its whole shell when you switch project, because every open\n * tab names a table by id \u2014 would otherwise leave the first one's handler\n * attached for ever. Two handlers is not \"twice as responsive\": `Ctrl+K`\n * toggles the dead palette open and the live one closed in the same keystroke,\n * and `Alt+W` closes a tile in a tree nobody can see. `createShell().dispose()`\n * calls this.\n */\n\nexport function installKeymap({ wm, palette, ...opts } = {}) {\n const onKeyDown = (e) => {\n const inField = e.target?.closest?.(\n 'input, textarea, select, [contenteditable=\"true\"]');\n\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k' && !e.shiftKey) {\n e.preventDefault();\n palette.toggle();\n return;\n }\n // \u2500\u2500 Ctrl+Tab / Ctrl+Shift+Tab \u2192 next / previous main-panel tab in the\n // focused tile (wraps). Works everywhere (Tab isn't a text key here);\n // no-op when the tile has a single tab.\n if ((e.ctrlKey || e.metaKey) && !e.altKey && e.key === 'Tab') {\n e.preventDefault();\n wm.cycleFocusedTab(e.shiftKey ? -1 : 1);\n return;\n }\n if (e.key === 'Escape') {\n if (palette.isOpen()) { palette.close(); return; }\n // Let managed-window system handle its own Esc.\n }\n // Backspace = walk one step up the breadcrumb hierarchy. Skips\n // when the user is typing in a field so it doesn't steal\n // text-deletion.\n if (e.key === 'Backspace' && !inField && !e.metaKey && !e.ctrlKey) {\n e.preventDefault();\n wm.navigateBack?.();\n return;\n }\n\n // \u2500\u2500 F1..F8 \u2192 jump straight to the Nth top-nav page. F-keys never\n // type, but we still skip while a field is focused so editing\n // flows (query editor, filters) keep keyboard focus.\n const fMatch = /^F([1-9]|1[0-2])$/.exec(e.key);\n if (fMatch && !inField && !e.altKey && !e.ctrlKey && !e.metaKey && !e.shiftKey) {\n // The selector is a CONFIG option, not a constant. An embedder that\n // moves its sections out of the top bar \u2014 into an icon rail, say \u2014\n // would otherwise find F1..F8 silently doing nothing: the query\n // returns an empty NodeList, `idx < btns.length` is false, and the\n // handler falls through without a hint that anything is bound.\n // Passing `navSelector` is how such an embedder keeps the keys.\n const btns = document.querySelectorAll(\n opts.navSelector\n || '.twm-global-top-bar .twm-bar-center.twm-top-nav .twm-top-nav__btn');\n const idx = Number(fMatch[1]) - 1;\n if (idx < btns.length) {\n e.preventDefault();\n btns[idx].click();\n return;\n }\n }\n\n // \u2500\u2500 Shift+Left/Right \u2192 previous/next bottom-panel tab. No-op when\n // no bottom panel is mounted. Skipped in fields so Shift+Arrow\n // keeps selecting text there.\n if (e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey && !inField\n && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) {\n const tabs = [...document.querySelectorAll('.twm-bp__tabs .twm-bp__tab')];\n if (tabs.length) {\n const cur = tabs.findIndex((t) => t.classList.contains('twm-bp__tab--on'));\n const dir = e.key === 'ArrowRight' ? 1 : -1;\n const next = ((cur < 0 ? 0 : cur) + dir + tabs.length) % tabs.length;\n e.preventDefault();\n tabs[next].click();\n return;\n }\n }\n\n // \u2500\u2500 Ctrl+Left/Right \u2192 iterate virtual desktops (wraps).\n if (e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey && !inField\n && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) {\n e.preventDefault();\n wm.cycleDesktop(e.key === 'ArrowRight' ? 1 : -1);\n return;\n }\n\n if (!e.altKey) return;\n // Browser Alt+letter triggers menu-access in some browsers \u2014 always preventDefault.\n const key = e.key.toLowerCase();\n\n // Desktop switch / move \u2014 Alt+digit\n if (/^[1-9]$/.test(e.key)) {\n e.preventDefault();\n const idx = Number(e.key) - 1;\n if (e.shiftKey) wm.moveFocusedToDesktop(idx);\n else wm.switchDesktop(idx);\n return;\n }\n\n switch (key) {\n // Plain Alt+H/V split into an empty pane; with Shift they open\n // the focused tile's own content in the new split.\n case 'h':\n e.preventDefault();\n e.shiftKey ? wm.splitFocusedWith('h') : wm.split('h');\n return;\n case 'v':\n e.preventDefault();\n e.shiftKey ? wm.splitFocusedWith('v') : wm.split('v');\n return;\n case 't': e.preventDefault(); wm.openFocusedInTab(); return;\n case 'n': e.preventDefault(); wm.openFocusedInWindow(); return;\n case 'w': e.preventDefault(); wm.closeFocused(); return;\n case 'f': e.preventDefault(); wm.toggleManagedFocused(); return;\n case 'arrowleft':\n e.preventDefault();\n e.shiftKey ? wm.moveFocused('left') : wm.focusDir('left');\n return;\n case 'arrowright':\n e.preventDefault();\n e.shiftKey ? wm.moveFocused('right') : wm.focusDir('right');\n return;\n case 'arrowup':\n e.preventDefault();\n e.shiftKey ? wm.moveFocused('up') : wm.focusDir('up');\n return;\n case 'arrowdown':\n e.preventDefault();\n e.shiftKey ? wm.moveFocused('down') : wm.focusDir('down');\n return;\n }\n\n // Suppress accidental browser menu-access on bare Alt+letter\n // chords we don't bind (so the user doesn't get a Firefox/Chromium\n // menu popping open mid-flow).\n if (key.length === 1 && /[a-z]/.test(key) && !inField) {\n e.preventDefault();\n }\n };\n\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n}\n", "/**\n * kind_taxonomy.js \u2014 the taxonomy PORT.\n *\n * A \"kind\" is a content type the user can navigate to. The tiling shell\n * needs to know four things about one: what to call it, what icon it\n * wears, which top-nav slot owns it, and what sits above it. It must\n * NOT know what any of them mean.\n *\n * So the taxonomy is INJECTED, not imported. The embedder describes its\n * own ontology and hands it to `createTaxonomy()` at boot; the shell\n * closes over the result. Nothing in this file names a kind, and the\n * framework runs unchanged on a completely different vocabulary.\n *\n * Consumed by:\n *\n * - install.js \u2192 top-nav buttons + active-state highlight\n * - tile_breadcrumb.js \u2192 breadcrumb segments\n * - wm.js navigateBack \u2192 Backspace parent\n * - command_palette.js \u2192 palette chip strip + row icons\n * - tile_tab_menu.js \u2192 which sources a tile's hamburger lists\n *\n * ## KindDef\n *\n * label Long form, used in breadcrumb segments + palette.\n * shortLabel Optional \u22644-char chip text for the top-nav.\n * Falls back to `label` when omitted.\n * icon Material Symbols name.\n * topNav Top-nav category this kind belongs to. Determines which\n * button lights up + which segment the breadcrumb inserts\n * after the root. Omit on the top-nav kind itself.\n * isTopNav True iff this kind owns a slot in the top-nav strip.\n * order Sort key for top-nav display (lower = leftward).\n * appGlobal True iff the kind is not scoped to the root entity (a\n * preferences page, say). Its breadcrumb omits the root.\n * sources Entity-source ids the tile hamburger lists when a tile\n * is on this page. Only meaningful on top-nav kinds; the\n * root's list is the fallback.\n *\n * ancestors? `(props) => [{ kind, id, label? }]` \u2014 the entity-level\n * ancestors of ONE instance, root-most first, EXCLUDING\n * the top-nav segment and the node itself. This is the\n * hook that replaces every hardcoded \"split the id to\n * find its parent\" branch: naming conventions are the\n * embedder's business, not the shell's.\n * labelOf? `(props) => string` \u2014 display label for ONE instance.\n * Defaults to `props.label`, then `props.id`.\n */\n\n/** Build a taxonomy from an embedder's kind map. Validated eagerly and\n * frozen \u2014 a typo in a `topNav` pointer is a boot error, not a chrome\n * surface that silently renders nothing six clicks later. */\nexport function createTaxonomy({ kinds, root } = {}) {\n if (!kinds || typeof kinds !== 'object') {\n throw new TypeError('createTaxonomy: `kinds` must be an object of KindDefs');\n }\n if (!root || !kinds[root]) {\n throw new Error(`createTaxonomy: root kind '${root}' is not in the taxonomy`);\n }\n for (const [kind, def] of Object.entries(kinds)) {\n if (!def || typeof def !== 'object') {\n throw new TypeError(`createTaxonomy: '${kind}' is not a KindDef`);\n }\n if (def.topNav && !kinds[def.topNav]) {\n throw new Error(\n `createTaxonomy: '${kind}' hangs off unknown topNav '${def.topNav}'`);\n }\n if (def.isTopNav && def.topNav) {\n throw new Error(\n `createTaxonomy: '${kind}' is both a top-nav kind and owned by one`);\n }\n }\n const _kinds = Object.freeze({ ...kinds });\n\n /** Lookup. `undefined` for unknown kinds \u2014 chrome code treats that\n * as \"render the kind name verbatim\". */\n const meta = (kind) => _kinds[kind];\n\n /** Top-nav category id that owns `kind`. For a top-nav kind itself,\n * returns the same kind. `undefined` when the kind isn't in the\n * taxonomy (caller renders a fallback). */\n const topNavFor = (kind) => {\n const m = _kinds[kind];\n if (!m) return undefined;\n return m.isTopNav ? kind : m.topNav;\n };\n\n /** Backspace target \u2014 walk one step up the KIND hierarchy. A\n * top-nav page goes to the root; everything else goes to its\n * top-nav. The root itself returns `null`. */\n const parentKindFor = (kind) => {\n if (kind === root) return null;\n const m = _kinds[kind];\n if (!m) return null;\n if (m.isTopNav) return root;\n return m.topNav || root;\n };\n\n /** Entity-level ancestors of one instance, root-most first. Empty\n * unless the KindDef supplies an `ancestors` hook. Normalised to\n * `{ kind, props }` so callers hand the result straight to\n * `wm.navigate`. A throwing hook is a warning, never a crash: the\n * breadcrumb degrades to its generic form. */\n const ancestors = (kind, props) => {\n const fn = _kinds[kind]?.ancestors;\n if (typeof fn !== 'function') return [];\n let out;\n try { out = fn(props || {}); }\n catch (err) {\n console.warn('[taxonomy] ancestors() threw for', kind, err);\n return [];\n }\n if (!Array.isArray(out)) return [];\n return out\n .filter((a) => a && a.kind)\n .map((a) => ({ kind: a.kind, props: { id: a.id, label: a.label ?? a.id } }));\n };\n\n /** The nearest entity-level ancestor, or `null`. */\n const parentOf = (kind, props) => {\n const chain = ancestors(kind, props);\n return chain.length ? chain[chain.length - 1] : null;\n };\n\n /** Display label for one instance of `kind`. */\n const labelOf = (kind, props) => {\n const fn = _kinds[kind]?.labelOf;\n if (typeof fn === 'function') {\n try {\n const s = fn(props || {});\n if (s) return String(s);\n } catch (err) {\n console.warn('[taxonomy] labelOf() threw for', kind, err);\n }\n }\n if (props?.label) return String(props.label);\n return props?.id != null ? String(props.id) : '';\n };\n\n /** Entity-source ids a tile on `kind` should offer. Falls back to\n * the root's list, which is the \"everything\" default. */\n const sourcesFor = (kind) => {\n const s = _kinds[kind]?.sources ?? _kinds[root]?.sources;\n return Array.isArray(s) ? s : [];\n };\n\n /** Top-nav entries in display order:\n * { kind, label (short), longLabel, icon }\n * `label` is the chip text; `longLabel` is what the palette shows. */\n const topNavEntries = () =>\n Object.entries(_kinds)\n .filter(([, m]) => m.isTopNav)\n .sort((a, b) => (a[1].order ?? 1000) - (b[1].order ?? 1000))\n .map(([kind, m]) => ({\n kind,\n label: m.shortLabel || m.label,\n longLabel: m.label,\n icon: m.icon,\n }));\n\n return Object.freeze({\n root,\n kinds: _kinds,\n meta,\n topNavFor,\n parentKindFor,\n ancestors,\n parentOf,\n labelOf,\n sourcesFor,\n topNavEntries,\n });\n}\n", "/**\n * panel_keys.js \u2014 central keyboard router for panel-local navigation.\n *\n * THE PROBLEM IT SOLVES\n * ---------------------\n * Several panels want plain-key navigation (\u2191/\u2193 to move a cursor, Enter to\n * open, single letters for actions): the landing tables, the top-nav\n * overview pages, and any future browser panel. Each one used to attach its\n * OWN `document` keydown listener gated by the heuristic\n *\n * hostEl.contains(document.activeElement) || activeElement === <body>\n *\n * That heuristic is wrong as soon as there is more than one tile. When the\n * user selects a different tile, focus usually rests on `<body>`, so EVERY\n * mounted landing's listener still fires \u2014 the \"main\" panel keeps eating\n * arrow keys even though another panel is selected, and two panels can move\n * their cursors at once. The selected tile is never actually consulted.\n *\n * THE CONCEPT\n * -----------\n * One listener, one authority. Panels do not listen on `document`\n * themselves; they REGISTER a handler bound to their host element. On each\n * keydown the router asks the window manager which leaf is currently\n * focused (the single source of truth \u2014 `WindowManager.focusedLeafEl()`)\n * and dispatches the event to the handler whose host lives inside that leaf\n * \u2014 and to no one else. A panel automatically receives keys exactly when,\n * and only when, it is the selected tile.\n *\n * Content rendered OUTSIDE the tile tree (a promoted managed window or a\n * floating shell) has no focused-leaf concept; for those hosts the router\n * falls back to the focus-containment scope so they keep working.\n *\n * USAGE\n * -----\n * import { registerPanelKeys } from './panel_keys.js';\n * const off = registerPanelKeys(hostEl, (ev) => { ... });\n * // later, on teardown:\n * off();\n *\n * The handler is responsible for its own key semantics (which keys, whether\n * to skip editable targets, when to preventDefault). The router only decides\n * WHETHER this panel is the active keyboard target. A handler that calls\n * `ev.preventDefault()` stops the event from reaching any other handler in\n * the same leaf.\n *\n * When no router has been installed (unit tests, or a landing used outside\n * the WM), `registerPanelKeys` degrades to a self-contained listener using\n * the legacy focus-containment scope, so callers work in isolation too.\n */\n\nlet _router = null;\n\n/** Install the singleton router. Called once by the WindowManager. */\nexport function installPanelKeyRouter(wm) {\n if (_router) _router.destroy();\n _router = new PanelKeyRouter(wm);\n return _router;\n}\n\n/** The installed router, or null if the WM hasn't built one yet. */\nexport function getPanelKeyRouter() { return _router; }\n\n/** Tear down the singleton (test cleanup / shell teardown). */\nexport function uninstallPanelKeyRouter() {\n _router?.destroy();\n _router = null;\n}\n\n/**\n * Register `handler` as the key handler for `hostEl`'s panel. Returns an\n * unregister function \u2014 call it on teardown. Routes through the installed\n * router when present, otherwise falls back to a legacy scoped listener so\n * the panel still works outside the WM.\n */\nexport function registerPanelKeys(hostEl, handler) {\n if (!hostEl || typeof handler !== 'function') return () => {};\n if (_router) return _router.register(hostEl, handler);\n return _legacyRegister(hostEl, handler);\n}\n\nexport class PanelKeyRouter {\n constructor(wm) {\n this._wm = wm;\n this._entries = []; // { hostEl, handler }\n this._onKey = this._dispatch.bind(this);\n document.addEventListener('keydown', this._onKey);\n }\n\n register(hostEl, handler) {\n const entry = { hostEl, handler };\n this._entries.push(entry);\n return () => {\n const i = this._entries.indexOf(entry);\n if (i >= 0) this._entries.splice(i, 1);\n };\n }\n\n destroy() {\n document.removeEventListener('keydown', this._onKey);\n this._entries.length = 0;\n }\n\n /** Is `hostEl` the panel that should receive keys right now? */\n _isActiveTarget(hostEl) {\n if (!hostEl?.isConnected) return false;\n const leaf = hostEl.closest?.('.twm-leaf');\n if (leaf) {\n // Inside the tile tree: only the focused leaf is live. This is\n // the whole point \u2014 compare against the WM's single source of\n // truth, not against `document.activeElement`.\n return leaf === this._wm?.focusedLeafEl?.();\n }\n // Outside any tile (managed window / floating shell): no focused\n // leaf to consult, so fall back to focus containment.\n return hostEl.contains(document.activeElement)\n || document.activeElement === document.body;\n }\n\n _dispatch(ev) {\n // Snapshot: a handler may unregister (e.g. navigate away) mid-loop.\n for (const entry of [...this._entries]) {\n if (!this._isActiveTarget(entry.hostEl)) continue;\n try { entry.handler(ev); }\n catch (err) { console.warn('[panel-keys] handler threw', err); }\n // A handler that consumed the key ends the chain so a second\n // handler in the same leaf (e.g. the action-footer shortcuts)\n // doesn't double-fire on the same press.\n if (ev.defaultPrevented) return;\n }\n }\n}\n\n/** Legacy fallback used only when no router is installed. Mirrors the old\n * per-panel scope check so isolated/test usage keeps its previous\n * behavior. */\nfunction _legacyRegister(hostEl, handler) {\n const wrapped = (ev) => {\n if (!hostEl.isConnected) return;\n if (!hostEl.contains(document.activeElement)\n && document.activeElement !== document.body) return;\n handler(ev);\n };\n document.addEventListener('keydown', wrapped);\n return () => document.removeEventListener('keydown', wrapped);\n}\n", "/**\n * landing_table.js \u2014 shared helpers for the \"navigation table\"\n * pattern used by SFC / Markets / Scenarios / KPIs landings (and the\n * market-archetype instances table). Three responsibilities:\n *\n * 1. Single-click row \u2192 open the entity (handled by callers).\n * 2. Right-click row \u2192 custom context menu with Open / Edit / Delete\n * (replaces DataTable's default copy-as-TSV menu for these tables;\n * the default still applies to data tables outside this helper).\n * 3. Renders an \"actions\" column with inline edit + delete icons so\n * the user can act on a row without opening it first.\n *\n * Callers configure the per-kind behaviour via the `entityActions`\n * object: `{ open(row), edit(row), delete(row) }` \u2014 any of which may be\n * omitted to hide the corresponding action.\n */\n\nimport { showContextMenu } from '../ui/components/context_menu.js';\nimport { registerPanelKeys } from './panel_keys.js';\n\n/** Attach the navigation-table interactions to a host element that\n * contains a `<table>` already rendered by DataTable. Returns a\n * teardown function.\n *\n * `entityActions` maps action names to handlers. The canonical names\n * are `open` / `edit` / `delete` (plus `openInTab` / `openInWindow`),\n * but ANY `data-twm-action=\"<name>\"` button or context-menu item\n * dispatches to `entityActions[<name>]` \u2014 so tree landings can wire\n * `toggle`, `contract`, etc. through the same robust path instead of\n * hand-rolling their own tbody listeners (which is how the Agents /\n * Markets pages used to drift out of sync with this helper).\n *\n * `options.buildMenu(row) => items[]` overrides the default\n * Open / Edit / Delete right-click menu for tree-shaped rows that need\n * conditional entries (expand/collapse, contract detail). Each item's\n * `action` is dispatched the same way as a button. */\nexport function attachLandingTableBehavior(host, getRow, entityActions, options = {}) {\n if (!host) return () => {};\n\n // Delegate from the stable `host` container, NOT the <tbody>. DataTable\n // rebuilds its <tbody> on every internal re-render \u2014 pagination, filter,\n // sort, and the post-layout column-width sync \u2014 and each re-render\n // re-installs ITS OWN row listeners (data_table.js `_installInteractions`)\n // but knows nothing about ours. A listener bound directly to the tbody\n // therefore goes silently dead after the first re-render: events still\n // reach the live tbody (DataTable's copy menu still pops) while our\n // handlers sit orphaned on a detached node. Binding on `host` and\n // resolving the row at event time survives any number of tbody rebuilds.\n\n // Dispatch an action name to its handler. The `open-tab` /\n // `open-window` menu aliases map onto the `openInTab` /\n // `openInWindow` handlers; every other name (including `edit`,\n // `delete`, and tree-only verbs like `toggle` / `contract`) routes\n // straight to `entityActions[name]`.\n const dispatch = (action, row) => {\n if (!row) return;\n const fn = action === 'open-tab' ? entityActions.openInTab\n : action === 'open-window' ? entityActions.openInWindow\n : entityActions[action];\n try { fn?.(row); }\n catch (e) { console.warn(`[landing] action \"${action}\" failed`, e); }\n };\n\n // The BODY <tr> for an event, re-found live each time. Excludes the\n // header / filter row (it lives in the separate non-scrolling header\n // wrap, not inside `.preview-table-wrap`'s tbody).\n const bodyRowFor = (ev) => {\n const tr = ev.target.closest('tr');\n if (!tr || !tr.closest('tbody')) return null;\n const wrap = tr.closest('.twm-preview-table-wrap');\n if (!wrap || !host.contains(wrap)) return null;\n return tr;\n };\n\n const onClick = (ev) => {\n // Inline action buttons short-circuit the row-open default and\n // suppress every other listener (DataTable's row-selection\n // listener, etc.). `stopImmediatePropagation` is load-bearing.\n const actionBtn = ev.target.closest('[data-twm-action]');\n if (actionBtn && host.contains(actionBtn)) {\n ev.preventDefault();\n ev.stopPropagation();\n ev.stopImmediatePropagation();\n const tr = actionBtn.closest('tr');\n dispatch(actionBtn.dataset.twmAction, tr ? getRow(tr.__rowIndex) : null);\n return;\n }\n // Plain row click \u2192 open. Ignore clicks on interactive controls\n // (buttons, filter inputs, links) and anything outside a body row\n // (pagination strip, header, empty space).\n if (ev.target.closest('button, a, input, select, textarea')) return;\n const tr = bodyRowFor(ev);\n if (tr) dispatch('open', getRow(tr.__rowIndex));\n };\n\n const defaultMenu = (row) => {\n const items = [];\n if (entityActions.open) {\n items.push({ label: 'Open', icon: 'open_in_new', action: 'open' });\n if (entityActions.openInTab) {\n items.push({ label: 'Open in new tab', icon: 'tab', action: 'open-tab' });\n }\n if (entityActions.openInWindow) {\n items.push({ label: 'Open in new window', icon: 'open_in_full',\n action: 'open-window' });\n }\n }\n if (entityActions.edit) items.push({ label: 'Rename / edit', icon: 'edit', action: 'edit' });\n if (entityActions.delete) {\n if (items.length) items.push({ separator: true });\n items.push({ label: 'Delete', icon: 'delete', action: 'delete', danger: true });\n }\n return items;\n };\n const buildMenu = typeof options.buildMenu === 'function'\n ? options.buildMenu\n : defaultMenu;\n\n const onContextMenu = (ev) => {\n const tr = bodyRowFor(ev);\n if (!tr) return;\n ev.preventDefault();\n // Bypass DataTable's own copy-as-TSV/CSV menu by stopping\n // propagation \u2014 the right-click for a navigation row is about the\n // *entity*, not the cell text. Capture phase on `host` runs before\n // DataTable's bubble-phase contextmenu on the tbody.\n ev.stopPropagation();\n const row = getRow(tr.__rowIndex);\n if (!row) return;\n const items = buildMenu(row) || [];\n if (items.length === 0) return;\n showContextMenu(ev.clientX, ev.clientY, items, (action) => dispatch(action, row));\n };\n\n // Click in bubble phase (after DataTable's row-selection); contextmenu\n // in CAPTURE phase so ours runs before DataTable's bubble copy menu.\n host.addEventListener('click', onClick);\n host.addEventListener('contextmenu', onContextMenu, true);\n return () => {\n host.removeEventListener('click', onClick);\n host.removeEventListener('contextmenu', onContextMenu, true);\n };\n}\n\n/** Wire pane-focus highlighting. SfcLandingTab gives each\n * `.ea-sfc-landing__pane` an `--focused` modifier when it's the\n * active pane \u2014 that's what paints the blue head + accent title.\n * Other landings need the same treatment to read as consistent.\n *\n * Single-pane landings can call this once: the only pane stays\n * focused at all times. Multi-pane landings get mousedown wiring\n * per pane and a reflectFocus() helper they can call from keyboard\n * navigation if they implement it. */\nexport function wireLandingPaneFocus(hostEl) {\n const panes = Array.from(hostEl.querySelectorAll('.twm-landing__pane'));\n if (panes.length === 0) return { focus: () => {}, panes };\n let focused = panes[0];\n const reflect = () => {\n for (const p of panes) {\n p.classList.toggle('twm-landing__pane--focused', p === focused);\n }\n };\n reflect();\n for (const p of panes) {\n p.addEventListener('mousedown', () => {\n if (focused === p) return;\n focused = p;\n reflect();\n });\n }\n return {\n focus: (pane) => { if (pane && panes.includes(pane)) { focused = pane; reflect(); } },\n panes,\n };\n}\n\n/** Arrow-key navigation for a landing page's DataTable.\n *\n * - ArrowDown / ArrowUp moves a row cursor within `host`.\n * - Enter (and the optional `activateKey`, e.g. `o`) opens the row.\n * - Home / End jump to the first / last row.\n * - Captures the keys at the document level so the user doesn't have\n * to click inside the table first \u2014 same UX as sfc_landing_tab.\n *\n * The visible cursor is the `ea-row-cursor` CSS class on the active\n * `<tr>`; the row also `scrollIntoView({block:'nearest'})` so the\n * selection stays visible during keyboard scroll-through.\n *\n * Returns `{ teardown, refresh, focusFirst }`. Call `refresh()` after\n * the table re-renders so the cursor lands on the right row again.\n */\nexport function attachLandingKeyboardNav(host, getRows, onActivate, options = {}) {\n if (!host) return { teardown: () => {}, refresh: () => {}, focusFirst: () => {} };\n\n let cursor = 0;\n const tbody = () => host.querySelector('tbody');\n\n const repaintCursor = () => {\n const tb = tbody();\n if (!tb) return;\n const rows = getRows() || [];\n if (rows.length === 0) { cursor = 0; return; }\n if (cursor >= rows.length) cursor = rows.length - 1;\n if (cursor < 0) cursor = 0;\n let active = null;\n tb.querySelectorAll('tr').forEach((tr) => {\n const on = tr.__rowIndex === cursor;\n tr.classList.toggle('twm-row-cursor', on);\n if (on) active = tr;\n });\n if (active) active.scrollIntoView({ block: 'nearest' });\n };\n\n const move = (delta) => {\n const rows = getRows() || [];\n if (rows.length === 0) return;\n cursor = Math.max(0, Math.min(rows.length - 1, cursor + delta));\n repaintCursor();\n };\n\n const open = () => {\n const rows = getRows() || [];\n const row = rows[cursor];\n if (row) onActivate(row);\n };\n\n const onKey = (ev) => {\n if (!host.isConnected) return;\n // Stay out of the way of inputs and unrelated focus targets.\n const t = ev.target;\n const editable = t && (\n t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' ||\n t.tagName === 'SELECT' || t.isContentEditable);\n if (editable) return;\n // Scope (which tile is selected) is owned by the panel-key router.\n\n if (ev.key === 'ArrowDown') { ev.preventDefault(); move(+1); }\n else if (ev.key === 'ArrowUp') { ev.preventDefault(); move(-1); }\n else if (ev.key === 'Home') { ev.preventDefault(); cursor = 0; repaintCursor(); }\n else if (ev.key === 'End') {\n ev.preventDefault();\n const rows = getRows() || [];\n cursor = Math.max(0, rows.length - 1);\n repaintCursor();\n }\n else if (ev.key === 'Enter') { ev.preventDefault(); open(); }\n else if (options.activateKey && ev.key === options.activateKey\n && !ev.metaKey && !ev.ctrlKey && !ev.altKey) {\n ev.preventDefault(); open();\n }\n };\n\n const offKey = registerPanelKeys(host, onKey);\n // Re-paint immediately so the cursor is visible from first render.\n setTimeout(repaintCursor, 0);\n\n return {\n teardown: () => offKey(),\n refresh: () => repaintCursor(),\n focusFirst: () => { cursor = 0; repaintCursor(); },\n };\n}\n\n\n/** Unified landing-page keyboard shell.\n *\n * Wires the four behaviors every landing should share, in one call:\n *\n * - Pane focus highlight (`.ea-sfc-landing__pane--focused`) \u2014 click\n * on a pane or Tab to it, the header band lights up.\n * - ArrowUp / ArrowDown moves the row cursor within the focused\n * pane's table; Home / End jump to first / last. The visible cursor\n * is `.ea-row-cursor` on the active `<tr>`.\n * - Tab / Shift+Tab cycles between panes. Single-pane landings just\n * no-op the Tab \u2014 the behaviour is a property of the shell, not\n * the page.\n * - Enter activates the cursor row via `onActivate(row)`.\n * - ArrowRight / ArrowLeft expand / collapse the cursor row when\n * the pane provides `onExpandToggle` + `isExpanded` (tree-shaped\n * tables; harmless on flat tables that don't define them).\n *\n * Each pane spec:\n * {\n * paneEl, // .ea-sfc-landing__pane element\n * getRows, // () => Array<row>\n * onActivate, // (row) => void \u2014 Enter\n * onExpandToggle?, // (row, expand:bool) => void\n * isExpanded?, // (row) => bool\n * isExpandable?, // (row) => bool \u2014 gates Left/Right\n * tableHost?, // override pane.querySelector('.twm-landing__table')\n * }\n *\n * Returns `{ teardown, refresh, focusPane(idx) }`. Call `refresh()` after\n * the pane tables re-render so the cursor lands on the right row again.\n *\n * Backspace is intentionally **not** handled here \u2014 `keymap.js` owns\n * it globally and walks `wm.navigateBack()` regardless of which\n * landing is showing. */\nexport function attachLandingShell(hostEl, { panes, initialPane = 0 } = {}) {\n if (!hostEl || !Array.isArray(panes) || panes.length === 0) {\n return { teardown: () => {}, refresh: () => {}, focusPane: () => {} };\n }\n\n const state = panes.map((spec) => ({\n spec,\n paneEl: spec.paneEl,\n tableHost: spec.tableHost\n || spec.paneEl?.querySelector('.twm-landing__table')\n || spec.paneEl,\n cursor: 0,\n }));\n let focusedIdx = Math.max(0, Math.min(panes.length - 1, initialPane));\n\n const reflectPaneFocus = () => {\n state.forEach((s, i) => {\n s.paneEl?.classList.toggle(\n 'twm-landing__pane--focused', i === focusedIdx);\n });\n };\n\n const repaintCursor = (idx = focusedIdx) => {\n const s = state[idx];\n if (!s) return;\n const tbody = s.tableHost?.querySelector('tbody');\n if (!tbody) return;\n const rows = s.spec.getRows?.() || [];\n if (rows.length === 0) { s.cursor = 0; return; }\n if (s.cursor >= rows.length) s.cursor = rows.length - 1;\n if (s.cursor < 0) s.cursor = 0;\n let active = null;\n tbody.querySelectorAll('tr').forEach((tr) => {\n const on = tr.__rowIndex === s.cursor;\n tr.classList.toggle('twm-row-cursor', on);\n if (on) active = tr;\n });\n if (active && idx === focusedIdx) {\n active.scrollIntoView({ block: 'nearest' });\n }\n };\n\n const move = (delta) => {\n const s = state[focusedIdx];\n if (!s) return;\n const rows = s.spec.getRows?.() || [];\n if (rows.length === 0) return;\n s.cursor = Math.max(0, Math.min(rows.length - 1, s.cursor + delta));\n repaintCursor();\n };\n\n const jump = (toEnd) => {\n const s = state[focusedIdx];\n if (!s) return;\n const rows = s.spec.getRows?.() || [];\n s.cursor = toEnd ? Math.max(0, rows.length - 1) : 0;\n repaintCursor();\n };\n\n const currentRow = () => {\n const s = state[focusedIdx];\n if (!s) return null;\n const rows = s.spec.getRows?.() || [];\n return rows[s.cursor] ?? null;\n };\n\n const activate = () => {\n const row = currentRow();\n if (!row) return;\n try { state[focusedIdx].spec.onActivate?.(row); }\n catch (err) { console.warn('[landing-shell] activate failed', err); }\n };\n\n /** Open the current row in a new tab on the tile. Pane spec opts in\n * by providing `onActivateInTab(row)`; the default-Enter handler\n * remains `onActivate`. Falls back to `onActivate` so panes that\n * haven't been migrated still respond to Ctrl+Enter. */\n const activateInTab = () => {\n const row = currentRow();\n if (!row) return;\n const spec = state[focusedIdx].spec;\n const fn = spec.onActivateInTab || spec.onActivate;\n try { fn?.(row); }\n catch (err) { console.warn('[landing-shell] activate-tab failed', err); }\n };\n\n /** Open the current row in a fresh managed window. Same opt-in\n * pattern as activateInTab \u2014 panes provide `onActivateInWindow`\n * for Shift+Enter, fall back to onActivate when absent. */\n const activateInWindow = () => {\n const row = currentRow();\n if (!row) return;\n const spec = state[focusedIdx].spec;\n const fn = spec.onActivateInWindow || spec.onActivate;\n try { fn?.(row); }\n catch (err) { console.warn('[landing-shell] activate-window failed', err); }\n };\n\n const expandToggle = (expand) => {\n const s = state[focusedIdx];\n if (!s?.spec.onExpandToggle) return false;\n const row = currentRow();\n if (!row) return false;\n if (s.spec.isExpandable && !s.spec.isExpandable(row)) return false;\n const currentlyExpanded = s.spec.isExpanded\n ? !!s.spec.isExpanded(row)\n : false;\n if (expand === currentlyExpanded) return false;\n try { s.spec.onExpandToggle(row, expand); }\n catch (err) { console.warn('[landing-shell] expand failed', err); }\n return true;\n };\n\n const focusPane = (idx) => {\n if (idx < 0 || idx >= state.length) return;\n if (idx === focusedIdx) return;\n focusedIdx = idx;\n reflectPaneFocus();\n repaintCursor();\n };\n\n const cyclePane = (dir) => {\n if (state.length < 2) return;\n focusPane((focusedIdx + dir + state.length) % state.length);\n };\n\n const onMouseDown = (ev) => {\n // Find which pane the click landed in. Inline action buttons\n // and the like still focus the surrounding pane.\n const paneEl = ev.target.closest?.('.twm-landing__pane');\n if (!paneEl) return;\n const idx = state.findIndex((s) => s.paneEl === paneEl);\n if (idx < 0 || idx === focusedIdx) return;\n focusedIdx = idx;\n reflectPaneFocus();\n repaintCursor();\n };\n\n const onKey = (ev) => {\n if (!hostEl.isConnected) return;\n // Scope is owned by the panel-key router: this handler only runs\n // when our tile is the selected one. We still skip editable\n // targets so typing in a field inside the tile isn't hijacked.\n const t = ev.target;\n const editable = t && (\n t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' ||\n t.tagName === 'SELECT' || t.isContentEditable);\n if (editable) return;\n // Browser-style activation chords (caught BEFORE the modifier\n // guard so the WM keymap doesn't swallow them):\n // Ctrl/Cmd + Enter \u2192 open in new TAB on this tile\n // Shift + Enter \u2192 open in new managed WINDOW\n if (ev.key === 'Enter' && (ev.metaKey || ev.ctrlKey) && !ev.altKey) {\n ev.preventDefault();\n activateInTab();\n return;\n }\n if (ev.key === 'Enter' && ev.shiftKey && !ev.altKey\n && !ev.metaKey && !ev.ctrlKey) {\n ev.preventDefault();\n activateInWindow();\n return;\n }\n // Other modifier chords belong to the WM / browser.\n if (ev.metaKey || ev.ctrlKey || ev.altKey || ev.shiftKey) return;\n\n switch (ev.key) {\n case 'ArrowDown':\n ev.preventDefault(); move(+1); return;\n case 'ArrowUp':\n ev.preventDefault(); move(-1); return;\n case 'Home':\n ev.preventDefault(); jump(false); return;\n case 'End':\n ev.preventDefault(); jump(true); return;\n case 'Enter':\n ev.preventDefault(); activate(); return;\n case 'Tab':\n // Cycle panes; single-pane case still preventDefault so\n // focus doesn't escape into the browser chrome.\n ev.preventDefault();\n cyclePane(ev.shiftKey ? -1 : +1);\n return;\n case 'ArrowRight':\n if (expandToggle(true)) ev.preventDefault();\n return;\n case 'ArrowLeft':\n if (expandToggle(false)) ev.preventDefault();\n return;\n }\n };\n\n hostEl.addEventListener('mousedown', onMouseDown);\n const offKey = registerPanelKeys(hostEl, onKey);\n reflectPaneFocus();\n // Initial cursor paint runs after the synchronous mount returns so\n // tables that render asynchronously already have their tbody.\n setTimeout(() => state.forEach((_, i) => repaintCursor(i)), 0);\n\n // Park native focus on hostEl when our leaf is the active one. The\n // panel-key router no longer depends on this for arrow routing (it\n // keys off the focused leaf, not document.activeElement), but parking\n // focus keeps native focus semantics \u2014 scroll-into-view, :focus-within\n // styling, and screen-reader context \u2014 anchored to the live tile.\n //\n // Guarded by the WM's `.twm-leaf--focused` class so we don't steal\n // focus when another tile owns the user's attention. The\n // requestAnimationFrame second pass catches the case where the\n // focus class is applied a frame after mount.\n const parkFocusIfActive = () => {\n const leaf = hostEl.closest?.('.twm-leaf');\n // Page is rendered outside a tile (managed window / floating\n // shell) \u2014 there is no focused-leaf concept; safe to take focus.\n const inTile = !!leaf;\n if (inTile && !leaf.classList.contains('twm-leaf--focused')) return;\n if (hostEl.contains(document.activeElement)) return;\n if (hostEl.tabIndex == null || hostEl.tabIndex < -1) hostEl.tabIndex = -1;\n try { hostEl.focus({ preventScroll: true }); } catch {}\n };\n parkFocusIfActive();\n requestAnimationFrame(parkFocusIfActive);\n\n return {\n teardown: () => {\n hostEl.removeEventListener('mousedown', onMouseDown);\n offKey();\n },\n refresh: () => state.forEach((_, i) => repaintCursor(i)),\n focusPane,\n currentRow,\n };\n}\n\n\n/** Standardized action-footer renderer.\n *\n * Each action: `{ id, label, icon, shortcut?, title?, onClick }`.\n *\n * When `shortcut` is set (single letter), the button label gets a\n * `[N]` suffix and pressing that key (without modifiers, outside any\n * text input, while focus is in `hostEl` or on the body) fires the\n * action. The visible chip + the keydown handler are the same source\n * of truth, so users can never wonder which letter triggers what.\n *\n * Returns `{ teardown, refresh(actions) }`. */\n\n\n// \u2500\u2500\u2500 #143 \u2014 shared landing shell \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/** Mount the boilerplate every landing repeats: a detail-header with\n * title + stat fields, a single `.ea-sfc-landing__pane` with title +\n * keyboard-shortcut hint, the DataTable host, and a footer slot for\n * `mountLandingActions` to fill.\n *\n * Callers receive `{paneEl, tableHost, footerEl, setStat(key, value)}`\n * \u2014 `paneEl` feeds straight into `attachLandingShell({panes: [{paneEl,\n * \u2026}]})`; `tableHost` is where `new DataTable(host, \u2026)` mounts;\n * `footerEl` is the second arg to `mountLandingActions`; `setStat`\n * patches a single stat by `data-role` without rebuilding the header.\n *\n * HTML output is identical to what each landing built by hand before\n * this helper existed \u2014 only the source location moves.\n *\n * @param {HTMLElement} hostEl\n * @param {object} opts\n * @param {string} opts.title \u2014 `<h2>` text on the left of the header.\n * @param {Array<{key,label,initial?}>} [opts.stats] \u2014 header chip per stat. `key` is the `data-role` for setStat(); `label` is the muted label above the value; `initial` defaults to `0`.\n * @param {object} opts.pane\n * @param {string} opts.pane.key \u2014 the `data-pane=\"\u2026\"` value (also doubles as the table-host's `data-role=\"<key>-host\"`).\n * @param {string} opts.pane.title \u2014 pane-head title.\n * @param {string} opts.pane.hint \u2014 pane-head keyboard-shortcut hint.\n * @returns {{headerEl: HTMLElement, paneEl: HTMLElement, tableHost: HTMLElement, footerEl: HTMLElement, setStat: (key: string, value: any) => void}}\n */\nexport function mountLandingShell(hostEl, opts) {\n const {\n title = '',\n stats = [],\n pane = { key: 'rows', title: '', hint: '' },\n } = opts || {};\n const paneKey = String(pane.key || 'rows');\n\n const _esc = (s) => String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n\n const statsHtml = (stats || []).map((s) => `\n <span class=\"twm-landing__stat twm-entity-header__field\">\n <span>${_esc(s.label || '')}</span>\n <strong data-role=\"${_esc(s.key)}\">${_esc(s.initial != null ? s.initial : 0)}</strong>\n </span>\n `).join('');\n\n hostEl.classList.add('twm-landing');\n hostEl.innerHTML = `\n <header class=\"twm-entity-header\">\n <h2>${_esc(title)}</h2>\n ${statsHtml}\n <span class=\"twm-entity-header__spacer\"></span>\n </header>\n <div class=\"twm-landing__split twm-landing__split--single\">\n <section class=\"twm-landing__pane\" data-pane=\"${_esc(paneKey)}\">\n <div class=\"twm-landing__pane-head\">\n <span class=\"twm-landing__pane-title\">${_esc(pane.title || '')}</span>\n <span class=\"twm-landing__pane-hint\">${_esc(pane.hint || '')}</span>\n </div>\n <div class=\"twm-landing__table\" data-role=\"${_esc(paneKey)}-host\"></div>\n </section>\n </div>\n <footer class=\"twm-landing__actions\" data-role=\"actions\"></footer>\n `;\n\n const headerEl = hostEl.querySelector('.twm-entity-header');\n const paneEl = hostEl.querySelector(`[data-pane=\"${paneKey}\"]`);\n const tableHost = hostEl.querySelector(`[data-role=\"${paneKey}-host\"]`);\n const footerEl = hostEl.querySelector('[data-role=\"actions\"]');\n\n const setStat = (key, value) => {\n const el = hostEl.querySelector(`[data-role=\"${key}\"]`);\n if (el) el.textContent = String(value);\n };\n\n return { headerEl, paneEl, tableHost, footerEl, setStat };\n}\n\n\nexport function mountLandingActions(footerEl, actions, hostEl) {\n if (!footerEl) return { teardown: () => {}, refresh: () => {} };\n let current = [];\n const renderInto = (list) => {\n current = (list || []).filter(Boolean);\n footerEl.innerHTML = '';\n for (const a of current) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'twm-action-btn';\n if (a.id) btn.dataset.action = a.id;\n if (a.title) btn.title = a.title;\n const icon = a.icon\n ? `<span class=\"material-symbols-outlined\">${_esc(a.icon)}</span>`\n : '';\n const sc = a.shortcut\n ? ` <span class=\"twm-action-btn__kbd\">[${_esc(a.shortcut.toUpperCase())}]</span>`\n : '';\n btn.innerHTML = `${icon}<span>${_esc(a.label || '')}</span>${sc}`;\n btn.addEventListener('click', (ev) => {\n ev.preventDefault();\n try { a.onClick?.(ev); }\n catch (err) { console.warn('[landing-actions] click failed', err); }\n });\n footerEl.appendChild(btn);\n }\n };\n\n const onKey = (ev) => {\n if (!footerEl.isConnected) return;\n if (ev.metaKey || ev.ctrlKey || ev.altKey) return;\n // Scope is owned by the panel-key router; we only guard editable\n // targets so single-letter shortcuts don't fire while typing.\n const t = ev.target;\n const editable = t && (\n t.tagName === 'INPUT' || t.tagName === 'TEXTAREA' ||\n t.tagName === 'SELECT' || t.isContentEditable);\n if (editable) return;\n const k = ev.key.length === 1 ? ev.key.toLowerCase() : ev.key;\n const hit = current.find((a) =>\n a.shortcut && String(a.shortcut).toLowerCase() === k);\n if (!hit) return;\n ev.preventDefault();\n try { hit.onClick?.(ev); }\n catch (err) { console.warn('[landing-actions] shortcut failed', err); }\n };\n\n renderInto(actions);\n const scopeEl = hostEl || footerEl.closest('.twm-landing') || footerEl;\n const offKey = registerPanelKeys(scopeEl, onKey);\n return {\n teardown: () => offKey(),\n refresh: renderInto,\n };\n}\n\n\n/** A DataTable `renderCell` hook that paints inline Edit + Delete icon\n * buttons in the configured action column. Use as:\n *\n * renderCell: actionsCellRenderer(actionsColIdx, { edit: true, delete: true })\n *\n * Other columns fall through to the default text renderer. */\nexport function actionsCellRenderer(actionsColIdx, options = { edit: true, delete: true }) {\n return (td, _value, colIdx) => {\n if (colIdx !== actionsColIdx) return false;\n const parts = [];\n if (options.edit) {\n parts.push(`<button type=\"button\" class=\"twm-row-action\"\n data-twm-action=\"edit\"\n title=\"Edit\"\n aria-label=\"Edit\">\n <span class=\"material-symbols-outlined\">edit</span>\n </button>`);\n }\n if (options.delete) {\n parts.push(`<button type=\"button\" class=\"twm-row-action twm-row-action--danger\"\n data-twm-action=\"delete\"\n title=\"Delete\"\n aria-label=\"Delete\">\n <span class=\"material-symbols-outlined\">delete</span>\n </button>`);\n }\n td.classList.add('twm-row-actions-cell');\n td.innerHTML = parts.join('');\n return true;\n };\n}\n\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n}\n", "/**\n * loading_overlay.js \u2014 the canonical \"content is loading\" overlay.\n *\n * An absolutely-positioned spinner layer dropped over a slot while an async\n * mount is in flight, with an 80ms grace period so cache hits / instant\n * renders never strobe a spinner. Returns a `done()` that removes it.\n *\n * Uses the global `twm-tile-loading` styles (ui/css/tiling/host.css). Shared\n * by the WM page-stub shell and the agent Code tab's split view.\n *\n * const done = makeLoadingOverlay(slot);\n * try { await mount(); } finally { done(); }\n */\n\n/** Drop a loading overlay over `slot` and return a `done()` to remove it.\n * The overlay stays invisible for `delay` ms (default 80) so fast renders\n * don't flash a spinner. `slot` must be a positioned ancestor (or pass one\n * that is); the overlay is `position:absolute; inset:0`. */\nexport function makeLoadingOverlay(slot, { delay = 80 } = {}) {\n const overlay = document.createElement('div');\n overlay.className = 'twm-tile-loading twm-tile-loading--pending';\n overlay.innerHTML =\n '<div class=\"twm-tile-loading__spinner\" aria-label=\"Loading\"></div>';\n slot.appendChild(overlay);\n // Promote to visible only if the mount hasn't finished within the grace.\n const showTimer = setTimeout(() => {\n overlay.classList.remove('twm-tile-loading--pending');\n }, delay);\n return () => {\n clearTimeout(showTimer);\n overlay.remove();\n };\n}\n", "/**\n * nav_panel.js \u2014 the left navigation panel-tile: a mode strip, a shared\n * filter box, and one lazily-mounted pane per mode.\n *\n * \u250C\u2500[ mode ][ mode ][ mode ]\u2500\u2500\u2500\u2500\u2500\u2510 mode tabs\n * \u2502 \u250C\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\u2510 \u2502\n * \u2502 \u2502 Filter\u2026 \u2502 \u2502 unified filter (sidebar-controls)\n * \u2502 \u2514\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\u2518 \u2502\n * \u2502 \u250C\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\u2510 \u2502\n * \u2502 \u2502 (pane body) \u2502 \u2502\n * \u2502 \u2514\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\u2518 \u2502\n *\n * The panel owns the strip, the filter and the pane hosts. It owns NONE of\n * the panes: the embedder passes them in. There used to be three hardcoded\n * modes here (a file tree, an object tree, a git view) plus a dead category\n * browser; all of that is the embedder's, and lives on its side now.\n *\n * A mode:\n *\n * {\n * id, icon, label,\n * mount(paneEl, { wm, eventBus }) -> {\n * activate?(), // called every time the mode is selected (may be async)\n * filter?(query), // the shared filter box changed\n * destroy?(),\n * }\n * }\n *\n * `mount()` runs at most once, the first time its mode is selected.\n * `activate()` runs on every selection, INCLUDING the first \u2014 a pane that\n * has to rebuild on each visit (a git status list, say) does its work there.\n * The filter query is remembered per mode.\n */\n\nexport function mountNavPanel(hostEl, {\n modes = [],\n wm = null,\n eventBus = null,\n filterPlaceholder = 'Filter\u2026',\n} = {}) {\n if (!Array.isArray(modes) || modes.length === 0) {\n throw new Error('mountNavPanel: at least one mode is required');\n }\n\n hostEl.classList.add('twm-nav');\n hostEl.innerHTML = `\n <div class=\"twm-nav__modes\" data-role=\"modes\"></div>\n <div class=\"twm-sidebar-controls twm-nav__filter\">\n <span class=\"material-symbols-outlined twm-sidebar-search-icon\">search</span>\n <input class=\"twm-data-page__search twm-nav__filter-input\"\n type=\"search\" placeholder=\"${_esc(filterPlaceholder)}\"\n autocomplete=\"off\" />\n </div>\n <div class=\"twm-nav__body\" data-role=\"body\"></div>\n `;\n const modesEl = hostEl.querySelector('[data-role=\"modes\"]');\n const bodyEl = hostEl.querySelector('[data-role=\"body\"]');\n const filterEl = hostEl.querySelector('.twm-nav__filter-input');\n\n modesEl.innerHTML = modes.map((m) => `\n <button class=\"twm-nav__mode twm-has-tooltip\" data-mode=\"${_esc(m.id)}\"\n data-tooltip=\"${_esc(m.label)}\">\n <span class=\"material-symbols-outlined\">${_esc(m.icon)}</span>\n <span>${_esc(m.label)}</span>\n </button>\n `).join('');\n\n const byId = new Map(modes.map((m) => [m.id, m]));\n const hosts = {}; // mode id -> pane host element\n const panes = {}; // mode id -> the object the mode's mount() returned\n const filterByMode = Object.fromEntries(modes.map((m) => [m.id, '']));\n let mode = modes[0].id;\n\n const ensureHost = (id) => {\n if (hosts[id]) return hosts[id];\n const el = document.createElement('div');\n el.className = `twm-nav__pane twm-nav__pane--${id}`;\n el.style.display = 'none';\n bodyEl.appendChild(el);\n hosts[id] = el;\n return el;\n };\n\n const activate = async (next) => {\n const def = byId.get(next);\n if (!def) return;\n mode = next;\n for (const m of modes) {\n ensureHost(m.id).style.display = (m.id === next) ? 'flex' : 'none';\n }\n modesEl.querySelectorAll('[data-mode]').forEach((b) =>\n b.classList.toggle('twm-nav__mode--on', b.dataset.mode === next));\n filterEl.value = filterByMode[next] || '';\n\n const paneEl = ensureHost(next);\n if (!panes[next]) {\n try { panes[next] = def.mount(paneEl, { wm, eventBus }) || {}; }\n catch (err) {\n console.error('[nav] mode mount failed', next, err);\n panes[next] = {};\n }\n }\n // `activate()` runs on EVERY selection, including the first. A pane that\n // must rebuild on each visit (a git status list) does its work here.\n try { await panes[next].activate?.(); }\n catch (err) { console.error('[nav] mode activate failed', next, err); }\n _applyFilter();\n };\n\n const _applyFilter = () => {\n const q = filterByMode[mode] || '';\n try { panes[mode]?.filter?.(q); }\n catch (err) { console.warn('[nav] filter failed', mode, err); }\n };\n\n filterEl.addEventListener('input', () => {\n filterByMode[mode] = filterEl.value;\n _applyFilter();\n });\n\n modesEl.addEventListener('click', (e) => {\n const btn = e.target.closest('[data-mode]');\n if (btn) activate(btn.dataset.mode);\n });\n\n activate(mode);\n\n return {\n refresh: () => activate(mode),\n destroy: () => {\n for (const p of Object.values(panes)) {\n try { p?.destroy?.(); } catch { /* ignore */ }\n }\n },\n };\n}\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => (\n { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]\n ));\n}\n", "/**\n * tile_breadcrumb.js \u2014 per-tile breadcrumb strip rendered above each\n * page's content.\n *\n * Reuses the existing `topbar-breadcrumb__*` CSS classes so the look\n * matches the rest of the app (notebook editor, etc.). Segments are\n * built from the leaf's content kind + props.id, and clicking a\n * non-current segment routes through wm.navigate so the user jumps to\n * that level.\n *\n * The path is assembled from FOUR taxonomy questions, none of which this\n * module answers itself:\n *\n * 1. `ctx.rootCrumb` the leading segment + how its label stays fresh\n * 2. `taxonomy.topNavFor` which section owns the kind\n * 3. `taxonomy.ancestors` the entity-level chain above ONE instance\n * 4. `taxonomy.labelOf` what to call that instance\n *\n * (3) is the one that matters. Deriving an instance's parent from its id\n * is a naming convention, and naming conventions belong to the embedder;\n * this file used to hardcode two of them.\n */\n\n/** Build a breadcrumb strip element + render initial state. Returns\n * `{ el, destroy }`. The strip is wired to the WM (clicks open content\n * in the primary tile) and, when the embedder supplies a `rootCrumb`\n * with a `subscribe` hook, to whatever keeps the root label fresh. */\nexport function mountTileBreadcrumb(kind, props, ctx) {\n const eventBus = ctx?.eventBus;\n const taxonomy = ctx?.taxonomy;\n const rootCrumb = ctx?.rootCrumb || null;\n if (!taxonomy) {\n console.error('[breadcrumb] no taxonomy in ctx \u2014 cannot render');\n return { el: document.createElement('nav'), destroy: () => {} };\n }\n const root = document.createElement('nav');\n root.className = 'twm-topbar-breadcrumb twm-tile-breadcrumb';\n root.setAttribute('aria-label', 'Tile breadcrumb');\n\n // Look up wm late (at click time) instead of capturing it once at\n // mount. ctx.wm should always be set by the renderer, but fall\n // back to the global escape hatch (set by install.js after\n // wm.load()) so a closure that lost ctx.wm \u2014 or a mount called\n // before the renderer threaded wm through \u2014 still navigates.\n const getWm = () => ctx?.wm || window.__twm?.wm || null;\n\n // Breadcrumb segments route through the canonical `wm.navigate`\n // helper so the same three-axis routing (window \u2192 replace window,\n // tile \u2192 replace active tab, panel/none \u2192 primary) lives in one\n // place. Critically, this is what makes the breadcrumb work in\n // WINDOWED mode \u2014 passing `ctx` keeps windowId in scope so the\n // segment click stays inside the floating window instead of\n // jumping back to the primary tile.\n const navigate = (k, p = {}) => {\n const wm = getWm();\n if (!wm) {\n console.error('[breadcrumb] no WM available \u2014 click ignored', k);\n return;\n }\n if (typeof wm.navigate === 'function') {\n wm.navigate(k, p, { ctx, dest: 'origin' });\n } else if (typeof wm.openInPrimary === 'function') {\n // Fallback for older WM revisions.\n wm.openInPrimary(k, p);\n } else {\n console.error('[breadcrumb] WM has no navigate/openInPrimary');\n }\n };\n\n // The root segment's label is the embedder's to supply and to keep\n // fresh (it names whatever the root entity is \u2014 a project, a\n // workspace, a document). Absent `rootCrumb` \u21D2 no root segment, and\n // the breadcrumb simply starts at the top-nav.\n let rootLabel = rootCrumb?.label || '';\n\n // Read at RENDER time, not captured: the trail grows with every\n // click-through, and a breadcrumb that showed the trail as it was when the\n // tile mounted would be describing a journey the user has since continued.\n const trailOf = () => {\n if (typeof ctx?.trailSegments !== 'function') return [];\n try { return ctx.trailSegments() || []; }\n catch (err) { console.warn('[breadcrumb] trailSegments threw', err); return []; }\n };\n const render = () => {\n _renderInto(root,\n _segments(kind, props, taxonomy, rootCrumb, rootLabel, navigate, trailOf()));\n };\n render();\n\n let unsubscribe = null;\n if (typeof rootCrumb?.subscribe === 'function') {\n try {\n unsubscribe = rootCrumb.subscribe((next) => {\n if (next?.label) rootLabel = next.label;\n render();\n }, { api: ctx?.api, host: ctx?.host, eventBus });\n } catch (err) {\n console.warn('[breadcrumb] rootCrumb.subscribe threw', err);\n }\n }\n\n return {\n el: root,\n /** Repaint. A trail-driven breadcrumb changes without the tile\n * remounting \u2014 following a lookup replaces the active tab's content in\n * place \u2014 so the embedder that grew the trail says when. */\n refresh: render,\n destroy: () => {\n try { unsubscribe?.(); }\n catch (err) { console.warn('[breadcrumb] rootCrumb teardown threw', err); }\n },\n };\n}\n\nfunction _segments(kind, props, taxonomy, rootCrumb, rootLabel, navigate, trail) {\n const segs = [];\n const meta = taxonomy.meta(kind);\n\n // App-global kinds (e.g. a preferences page) aren't scoped to the\n // root entity \u2014 they open with no project loaded and persist to\n // app-level storage \u2014 so their breadcrumb has no root ancestor.\n if (meta?.appGlobal) {\n return [{ icon: meta.icon, label: meta.label, onClick: null }];\n }\n\n // 1. The root segment \u2192 clicks back to the root page.\n if (rootCrumb) {\n segs.push({\n icon: rootCrumb.icon,\n label: rootLabel || rootCrumb.label,\n onClick: () => navigate(rootCrumb.navKind ?? taxonomy.root),\n });\n }\n\n // 2. The top-nav segment. Skipped on the root itself (the root\n // segment already names it) and when the kind isn't in the\n // taxonomy at all.\n const topNav = taxonomy.topNavFor(kind);\n const topNavMeta = topNav ? taxonomy.meta(topNav) : null;\n if (topNav && topNav !== taxonomy.root && topNavMeta) {\n segs.push({\n icon: topNavMeta.icon,\n label: topNavMeta.label,\n onClick: () => navigate(topNav),\n });\n }\n\n // 2b. THE TRAIL. An embedder whose navigation is a walk rather than a\n // descent \u2014 click a customer, follow a lookup to its region, follow\n // that to a country \u2014 has a real path that the taxonomy cannot know,\n // because none of those is an ANCESTOR of the next. The tree already\n // records it: every in-tile navigation pushes the outgoing content\n // onto the active tab's `history` stack, which is what Backspace pops.\n //\n // `ctx.trailSegments` hands that stack over, already shaped. Absent, or\n // empty, this is a no-op and the breadcrumb is exactly the ancestor\n // walk it has always been.\n for (const step of (trail || [])) {\n if (!step?.kind) continue;\n segs.push({\n icon: taxonomy.meta(step.kind)?.icon || 'description',\n label: step.title || step.props?.label || step.props?.id || step.kind,\n onClick: () => navigate(step.kind, step.props || {}),\n });\n }\n\n // 3. Entity-level ancestors, root-most first \u2014 supplied by the\n // taxonomy, not by string surgery on the id.\n for (const anc of taxonomy.ancestors(kind, props)) {\n segs.push({\n icon: taxonomy.meta(anc.kind)?.icon || 'description',\n label: anc.props.label ?? anc.props.id,\n onClick: () => navigate(anc.kind, anc.props),\n });\n }\n\n // 4. The node itself.\n const hasEntity = !!props?.id;\n if (meta) {\n if (hasEntity) {\n segs.push({\n icon: meta.icon,\n label: taxonomy.labelOf(kind, props),\n onClick: null,\n });\n } else if (kind !== topNav) {\n // The kind IS the top-nav (e.g. opening a landing page).\n // Don't duplicate the chip \u2014 the segment above already\n // names it.\n segs.push({\n icon: meta.icon,\n label: meta.label,\n onClick: null,\n });\n }\n } else if (hasEntity) {\n // Kind isn't in the taxonomy yet \u2014 still surface the entity\n // name so the user sees where they are, just without an icon.\n segs.push({\n icon: 'description',\n label: props.label || props.id,\n onClick: null,\n });\n }\n return segs;\n}\n\nfunction _renderInto(container, segments) {\n container.innerHTML = '';\n if (!segments?.length) return;\n const ol = document.createElement('ol');\n ol.className = 'twm-topbar-breadcrumb__list';\n segments.forEach((seg, i) => {\n const li = document.createElement('li');\n li.className = 'twm-topbar-breadcrumb__item';\n const isLast = i === segments.length - 1;\n if (isLast || !seg.onClick) {\n li.classList.add('twm-topbar-breadcrumb__item--current');\n li.innerHTML = `\n <span class=\"material-symbols-outlined twm-topbar-breadcrumb__icon\">${_esc(seg.icon)}</span>\n <span class=\"twm-topbar-breadcrumb__current\">${_esc(seg.label)}</span>\n `;\n } else {\n const btn = document.createElement('button');\n btn.className = 'twm-topbar-breadcrumb__link';\n btn.type = 'button';\n btn.innerHTML = `\n <span class=\"material-symbols-outlined twm-topbar-breadcrumb__icon\">${_esc(seg.icon)}</span>\n <span>${_esc(seg.label)}</span>\n `;\n btn.addEventListener('click', () => {\n btn.classList.add('twm-tile-breadcrumb__link--flash');\n setTimeout(() => {\n btn.classList.remove('twm-tile-breadcrumb__link--flash');\n }, 180);\n try { seg.onClick(); }\n catch (e) { console.error('[breadcrumb] segment click threw', e); }\n });\n li.appendChild(btn);\n const sep = document.createElement('span');\n sep.className = 'twm-topbar-breadcrumb__separator';\n sep.textContent = '\u203A';\n li.appendChild(sep);\n }\n ol.appendChild(li);\n });\n container.appendChild(ol);\n}\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n}\n", "/**\n * page_factory.js \u2014 the MECHANISM behind a tile's content, with no\n * knowledge of what that content is about.\n *\n * `createPageFactories()` returns the four wrappers an embedder uses to\n * turn its own page/tab modules into content factories:\n *\n * tabFactory(kind, factory) breadcrumb page-shell + a mounted tab\n * bareTabFactory(kind, factory) same, minus the breadcrumb (transient forms)\n * stubPageFactory(kind, render) breadcrumb + a plain render fn + loading overlay\n * mountTabBody / makeShim the pieces the three are built from\n *\n * \u2500\u2500 makeShim: the navigation contract \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 * Mounted content receives a `workspaceTabs` object. That object IS the\n * library's public navigation API in embryo:\n *\n * openTab open a kind in THIS tile (openFromContext)\n * openInTab open it as a new TAB in this tile (openInTabFromContext)\n * openInWindow open it in a fresh managed window\n * updateProps persist editor sub-state into this tile's tab props\n * closeTab close this tab (falls back to closing the tile)\n * notifyChanged broadcast \"I mutated the project\" so sidebars refresh\n *\n * \u2500\u2500 refreshOn / events: no bus-name literals here \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * This module used to hardcode five bus names (`<domain>:run:tick`, \u2026) and\n * one mutation event. Both are now INJECTED:\n *\n * refreshOn { [busEvent]: reasonString } fanned into tab.refresh(reason)\n * events { projectChanged? } emitted by close/notifyChanged\n *\n * An absent key means an absent subscription / an absent emit, so an\n * embedder with no run loop simply passes `{}` and nothing fires.\n */\n\nimport { makeLoadingOverlay } from './loading_overlay.js';\nimport { mountTileBreadcrumb } from './tile_breadcrumb.js';\n\n/**\n * @param {object} cfg\n * @param {object} cfg.eventBus the embedder's bus (may be null)\n * @param {object} cfg.events { projectChanged?: string }\n * @param {object} cfg.refreshOn { [busEventName]: reason } -> tab.refresh(reason)\n */\nexport function createPageFactories({ eventBus = null, events = {}, refreshOn = {} } = {}) {\n const projectChanged = events?.projectChanged || null;\n\n // The `workspaceTabs` API tile content calls to open/close tabs and\n // broadcast mutations. Shared by breadcrumbed and bare tabs.\n const makeShim = (ctx) => ({\n // Route through openFromContext so navigation triggered from a\n // split tile / window stays in that container.\n openTab: ({ kind: k, entityId, label, icon, subTab }) =>\n ctx.wm?.openFromContext(ctx, k, { id: entityId, label, icon, subTab }),\n // Open the same content as a NEW TAB in the current tile.\n openInTab: ({ kind: k, entityId, label, icon, subTab }) =>\n ctx.wm?.openInTabFromContext(ctx, k, { id: entityId, label, icon, subTab }),\n // Open the same content in a fresh managed window.\n openInWindow: ({ kind: k, entityId, label, icon, subTab }) =>\n ctx.wm?.navigate(k, { id: entityId, label, icon, subTab }, { ctx, dest: 'window' }),\n // Persist editor sub-state into this tile's WM tab props.\n updateProps: (patch) => {\n try { ctx.wm?.updateActiveTabProps?.(ctx.leafId, patch); }\n catch (err) { console.warn('[shim] updateProps failed', err); }\n },\n // Close just THIS tab (not the whole tile); falls back to closing\n // the tile when it's the last tab. Also broadcasts so sidebars /\n // landings / nav refresh against the mutated project state.\n closeTab: (_id) => {\n if (projectChanged) {\n try { eventBus?.emit?.(projectChanged, { source: 'twm-tab-close' }); } catch {}\n }\n try { ctx.wm?.closeActiveTab?.(ctx.leafId); } catch (err) {\n console.warn('[shim] closeTab failed', err);\n }\n },\n // Non-closing mutations (Save/New/Rename) broadcast so listeners refresh.\n notifyChanged: (domain) => {\n if (!projectChanged) return;\n try {\n eventBus?.emit?.(projectChanged,\n { source: 'tab-mutation', domain: domain || null });\n } catch {}\n },\n registerProvider: () => {},\n getActiveTab: () => null,\n });\n\n // Run a content factory inside a content slot: build the shim, mount\n // the tab, drive the loading overlay, and fan the embedder's declared\n // events into the tab's refresh(reason) hook (tabs that don't\n // self-subscribe would otherwise freeze mid-run). Returns a teardown\n // that removes listeners + disposes the tab.\n const mountTabBody = (factory, contentSlot, props, ctx) => {\n const shim = makeShim(ctx);\n const tab = factory(contentSlot, props?.id ?? props?.entityId ?? null, {\n logger: null, eventBus, workspaceTabs: shim,\n wm: ctx.wm, leafId: ctx.leafId, windowId: ctx.windowId,\n });\n const hideLoading = makeLoadingOverlay(contentSlot);\n try {\n const ret = tab?.mount?.(props);\n if (ret && typeof ret.then === 'function') {\n ret.then(hideLoading, (err) => {\n console.error('[page] mount failed', err);\n hideLoading();\n });\n } else {\n requestAnimationFrame(hideLoading);\n }\n } catch (err) {\n console.error('[page] mount failed', err);\n hideLoading();\n }\n const listeners = {};\n if (typeof tab?.refresh === 'function' && eventBus?.on) {\n for (const [busEvent, reason] of Object.entries(refreshOn || {})) {\n const fn = () => {\n try { tab.refresh(reason); }\n catch (err) { console.warn('[page] refresh failed', busEvent, err); }\n };\n listeners[busEvent] = fn;\n eventBus.on(busEvent, fn);\n }\n }\n return () => {\n for (const [n, fn] of Object.entries(listeners)) {\n try { eventBus?.off?.(n, fn); } catch {}\n }\n try { tab?.dispose?.(); tab?.unmount?.(); } catch {}\n };\n };\n\n /** Wrap a tab factory in a \"page shell\" \u2014 breadcrumb strip at the top,\n * content fills the rest. `kind` is captured at build time so the\n * breadcrumb knows which crumbs to build. */\n const tabFactory = (kind, factory) => (host, props, ctx) => {\n host.classList.add('twm-page-shell');\n host.innerHTML = '';\n const contentSlot = document.createElement('div');\n contentSlot.className = 'twm-page-shell__content';\n // Focusable via JS so `document.activeElement` lands inside the\n // page when the tile is activated.\n contentSlot.tabIndex = -1;\n\n // A breadcrumb navigates the host TILE \u2014 meaningless for an entity\n // opened in a standalone managed window, where we want only the\n // entity view. Skip the crumb when mounted in a window.\n let crumb = null;\n if (!ctx?.windowId) {\n const breadcrumbSlot = document.createElement('div');\n breadcrumbSlot.className = 'twm-page-shell__breadcrumb';\n host.appendChild(breadcrumbSlot);\n crumb = mountTileBreadcrumb(kind, props, { ...ctx, eventBus });\n breadcrumbSlot.appendChild(crumb.el);\n }\n host.appendChild(contentSlot);\n\n const teardown = mountTabBody(factory, contentSlot, props, ctx);\n return {\n destroy: () => {\n teardown();\n try { crumb?.destroy(); } catch {}\n },\n };\n };\n\n // Like tabFactory but with NO breadcrumb chrome \u2014 for transient /\n // form content (e.g. an add-row entry mask) that isn't an entity\n // view. Content still gets the full ctx (wm/leafId) + shim, so it\n // can self-close via wm.closeActiveTab.\n const bareTabFactory = (kind, factory) => (host, props, ctx) => {\n host.classList.add('twm-page-shell');\n host.innerHTML = '';\n const contentSlot = document.createElement('div');\n contentSlot.className = 'twm-page-shell__content';\n contentSlot.tabIndex = -1;\n host.appendChild(contentSlot);\n return { destroy: mountTabBody(factory, contentSlot, props, ctx) };\n };\n\n /** Page with a breadcrumb at the top and a plain `render(slot, props, ctx)`\n * body \u2014 for content that isn't a full tab object. If `render` returns a\n * `ready` promise the loading overlay waits on it. */\n const stubPageFactory = (kind, render) => (host, props, ctx) => {\n host.classList.add('twm-page-shell');\n host.innerHTML = '';\n const breadcrumbSlot = document.createElement('div');\n breadcrumbSlot.className = 'twm-page-shell__breadcrumb';\n const contentSlot = document.createElement('div');\n contentSlot.className = 'twm-page-shell__content';\n contentSlot.tabIndex = -1;\n host.appendChild(breadcrumbSlot);\n host.appendChild(contentSlot);\n const crumb = mountTileBreadcrumb(kind, props, { ...ctx, eventBus });\n breadcrumbSlot.appendChild(crumb.el);\n const hideLoading = makeLoadingOverlay(contentSlot);\n const ret = render(contentSlot, props, ctx) || {};\n // Most landings kick off their own async refresh after the\n // synchronous render returns. If render exposes a `ready`\n // promise we await it; otherwise hide on the next frame.\n if (ret.ready && typeof ret.ready.then === 'function') {\n ret.ready.then(hideLoading, hideLoading);\n } else {\n requestAnimationFrame(hideLoading);\n }\n return {\n title: ret.title,\n destroy: () => { try { crumb.destroy(); } catch {} ret.destroy?.(); },\n };\n };\n\n return Object.freeze({\n makeShim, mountTabBody, tabFactory, bareTabFactory, stubPageFactory,\n });\n}\n", "/**\n * tile_renderer.js \u2014 maps a TileTree into nested flex containers, with\n * resize splitters between siblings and a chrome strip per leaf.\n *\n * The renderer keeps a small per-leaf DOM cache so content factories\n * mount exactly once per (kind, props) pair \u2014 switching the focused\n * leaf or resizing doesn't re-mount tabs / pages.\n *\n * \u2500\u2500 C22. TWO TAB LAYOUTS, AND THE TREE KNOWS ABOUT NEITHER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * A leaf's tabs live in the TREE (`Leaf: { tabs, activeTabIdx }`) and are the\n * source of truth; `content`/`title` mirror `tabs[active]`. Where the strip is\n * DRAWN, and what it looks like, is therefore a rendering decision and nothing\n * else \u2014 which is why both layouts share one tab model, one action vocabulary\n * (`switch` / `close` / `move` / `menu` / `open-menu`, all through\n * `ctx.onLeafTabAction`) and one set of tree mutations in the WM.\n *\n * `bottom` \u2014 the framework's own spreadsheet strip under the tile body.\n * THE DEFAULT, and it stays the default: an existing embedder\n * upgrading must not find its panes rearranged.\n * `top` \u2014 `NotebookTabBar` (`../editor/notebook_tab_bar.js`), the editor\n * tab strip, mounted BETWEEN the chrome and the body. The chrome\n * is the pane's identity \u2014 its glyph, its title, its verbs \u2014 and\n * the tabs are its contents, so the tabs go under it, not over.\n *\n * The tab bar is reused rather than reimplemented. It already has the\n * behaviours a top strip is expected to have \u2014 drag-to-reorder, a scrolling\n * overflow, close buttons, dirty dots \u2014 and a second implementation of them\n * here is a second set of the same bugs.\n *\n * \u2500\u2500 The layout is also readable and writable as an ATTRIBUTE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * `tabLayout` is a `createShell` option like `snapPromotion` and\n * `promoteInPlace`, but unlike those it is something a USER may reasonably\n * change while looking at the thing it changes. An embedder's settings pane\n * holds no renderer reference \u2014 it holds, at most, the element it handed to\n * `createShell` \u2014 so the renderer mirrors the option onto its own root as\n * `data-twm-tabs` and OBSERVES it. Setting that attribute on the root element\n * is then a complete, reference-free way to switch layout live, and the same\n * attribute is the hook a stylesheet keys off. `setTabLayout()` is the direct\n * route for anyone who does hold the renderer.\n *\n * Switching is deliberately cheap: only the strip is rebuilt and the tab bar\n * element is MOVED between its two positions. The body is never detached, so\n * no content factory is unmounted and nothing mounted in a tile \u2014 a grid with\n * staged edits, an editor with an undo stack \u2014 notices that it happened.\n *\n * \u2500\u2500 C33. A TAB DRAGGED INTO A TILE \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 * *\"I would like to be able to drag & drop tabs into tiles, too.\"* A tab could\n * already be dragged WITHIN its own strip, and a WINDOW could already be\n * dropped onto a tile; between the two there was nothing.\n *\n * \u2550\u2550 TWO DRAG MECHANISMS, AND THEY CANNOT BE ONE GESTURE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * This codebase drags things in two incompatible ways, and the choice here was\n * made for us. Tab reorder is NATIVE HTML5 DRAG-AND-DROP \u2014 `draggable=\"true\"`\n * plus `dragstart`/`dragover`/`drop` \u2014 in both layouts: the `top` strip gets it\n * from the shared `DragReorder`, the `bottom` strip hand-rolls the same three\n * events. Window snapping is POINTER EVENTS\n * (`../ui/components/managed_window.js`, `_dragState` with a `pointermove`\n * listener on `document`). **Once a native drag begins the browser stops\n * dispatching `pointermove` for its whole duration**, so a tab drag can never\n * reach the pointer-driven snap controller, and a pointer-driven tab drag would\n * be a second mechanism sitting beside the reorder that already works. So: this\n * is HTML5 DnD, and it extends the gesture that exists rather than competing\n * with it.\n *\n * \u2550\u2550 THE GEOMETRY IS SHARED ANYWAY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * What could not be shared is the transport; what MUST be shared is the answer.\n * `wm.tabDropProbe` (R18) runs the same zone matrix a window drop runs, through\n * the same `_dropZoneFor` tail (R17) \u2014 so a 28px edge means SPLIT and a centre\n * means TAB-or-FILL for both, and the vocabulary is learned once. Product\n * owner, 2026-08-27: the edge drop splits, \"reusing the window snap-zone\n * vocabulary\".\n *\n * \u2550\u2550 THE SINGLE-TAB TILE HAS NO STRIP, SO THE CHROME IS THE HANDLE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * Both layouts hide the bar below two tabs (`_renderTabBar`), which is the\n * common case \u2014 one table in one tile \u2014 so a feature that needs a tab strip to\n * grab is a feature most tiles cannot offer. Product owner, 2026-08-27: the\n * tile chrome / breadcrumb is the drag source for a single-tab leaf. With one\n * tab there is no ambiguity to resolve \u2014 \"move this tab\" and \"move what is in\n * this pane\" name the same thing \u2014 which is exactly why it is limited to that\n * case: on a multi-tab leaf the strip already names each tab individually and a\n * chrome drag would have to guess which one was meant.\n *\n * IT DOES NOT COST THE CHROME ITS EXISTING GESTURES \u2014 BUT IT DID, AND THE\n * PARAGRAPH THAT USED TO SIT HERE IS WHY. It read: the pull \"loses cleanly\"\n * to a native drag, and \"is also still reachable, on every part of the chrome\n * a single-tab leaf's title does not cover\". That is a description of\n * `draggable` on the TITLE. `_syncChromeDragSource` sets it on the CHROME \u2014\n * the whole strip \u2014 so there was no part left over, and since both layouts\n * hide the tab strip below two tabs, a single-tab leaf is the ORDINARY tile\n * rather than an edge case. Pull-down-to-float was dead application-wide, and\n * the sentence saying it was fine sat in this file the whole time.\n *\n * The two gestures are separated by DIRECTION now, through one predicate\n * (`_isDownwardPull`) that both of them read: a downward grab is the float,\n * and `dragstart` refuses the native drag so the pointer goes back to the\n * pull; anything else is the tab drag and is untouched. The double-click\n * cannot collide with either \u2014 a `dblclick` carries no movement, so no drag\n * ever starts.\n *\n * A CLAIM ABOUT A BROWSER NEEDS A BROWSER. Nothing in this repository's suites\n * can begin a native drag \u2014 jsdom dispatches only the drag events a test hands\n * it \u2014 so this collision was invisible to every gate on both sides and stayed\n * that way through review. It was settled by driving real mouse input at\n * Edge 152 over CDP: as shipped, a 140px pull on a table tile's chrome gives\n * `dragstart`, `pointercancel`, `dragend` and no window; with the refusal,\n * `pointermove` resumes and the pull fires at 26px; sideways and diagonal\n * grabs still drag.\n *\n * A pane whose content VETOED `promote` (C20) is not a drag source either, and\n * that is one rule rather than two: a tab you may not lift out of its pane is a\n * tab you may not drag into another one. It is what keeps an embedder's master\n * tile \u2014 the ground floating windows stand on \u2014 from being dragged away.\n *\n * \u2550\u2550 THE DRAG STATE LIVES ON THE RENDERER \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * `_tabDrag` is an instance field, not a per-leaf closure, and that is\n * structural. The bottom strip's existing reorder keeps `dragFromIdx` in a\n * closure built per leaf render, so leaf B's handlers cannot see that leaf A\n * started a drag \u2014 which is fine for a reorder that begins and ends in one\n * strip and useless for one that crosses tiles.\n *\n * The identity has to live there for a second reason: under the HTML5 protected\n * mode `dataTransfer.getData()` returns the empty string for every event except\n * `drop`, so the source leaf CANNOT be read while deciding whether to arm.\n * `TILE_TAB_MIME` is therefore a type-only marker \u2014 an admission ticket, not a\n * message \u2014 and everything else is read off `_tabDrag`.\n *\n * \u2550\u2550 AND THE LISTENERS ARE BOUND ONCE, ON THE ROOT \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()` does `this.root.innerHTML = ''`, so a listener attached to a leaf\n * wrap during a repaint-heavy gesture is gone by the next frame; the\n * constructor's `mousedown` is the pattern and these follow it. For the same\n * reason NOTHING on the `dragover` path may call `render()` \u2014 rebuilding the\n * wrap that holds the drag source aborts the native drag, and the DOM\n * afterwards reads perfectly correct, which is the exact shape of the\n * five-times-reported `mousedown`-repaint defect one mechanism over.\n *\n * OUT OF SCOPE, deliberately and in writing: dragging a tab out of a FLOATING\n * WINDOW's strip (its tabs live in the WM's window record, not in the tree, so\n * the source policy is a different function's); dropping on empty space to\n * float a window (in HTML5 DnD \"dropped on nothing\" is `dragend` with no\n * `drop`, which is also what Escape produces, so it would float a window every\n * time a user changed their mind); and crossing DESKTOPS (only the active one\n * is rendered, so there is nothing to hit).\n */\n\nimport { NotebookTabBar } from '../editor/notebook_tab_bar.js';\n\nconst SPLITTER_PX = 4;\n/** The two tab layouts, and the framework's default. `bottom` is the default\n * because it is what every existing embedder already renders. */\nconst TAB_LAYOUTS = ['bottom', 'top'];\nconst DEFAULT_TAB_LAYOUT = 'bottom';\n\n/** Anything that is not one of the two layouts is the default rather than an\n * error: this value arrives from a DOM attribute and from persisted user\n * settings, and neither is a place to throw. */\nfunction _normalizeTabLayout(value) {\n return TAB_LAYOUTS.includes(value) ? value : DEFAULT_TAB_LAYOUT;\n}\n\n/**\n * `NotebookTabBar`'s vocabulary is FILE PATHS, and a tile tab is not a file.\n *\n * The path is only ever an identity string to it \u2014 the key its reorder uses,\n * the value it compares against `activeFilePath`, the selector it renames by \u2014\n * so a leaf's tab INDEX serves, and is the one identity a tab list is\n * guaranteed to have (the tree gives tabs no ids and this renderer may not\n * add any).\n *\n * The `builtin://` prefix is not decoration. `#beginInlineRename` refuses to\n * start on a path with it (`notebook_tab_bar.js:402`), which is exactly what a\n * tile tab needs: double-click rename would rename a TAB, and a tab is a view\n * of an entity whose name lives somewhere this renderer cannot reach. A rename\n * that silently does nothing is worse than no rename, and using the component's\n * own guard beats disabling the gesture from outside.\n */\nfunction _tabKey(idx) { return `builtin://tab/${idx}`; }\n\nfunction _tabKeyIndex(key) {\n const n = Number(String(key ?? '').replace('builtin://tab/', ''));\n return Number.isInteger(n) && n >= 0 ? n : -1;\n}\n/** How far the chrome must be pulled DOWN before the pane lifts out as a\n * floating window. Far enough that focusing a tile by clicking its header\n * never becomes one by accident. */\nconst TILE_LIFT_PX = 24;\n/** WHAT IS NOT A GRIP, for both of the chrome's float gestures.\n *\n * A tile's chrome is a grip with controls sitting on it \u2014 the split, float\n * and close buttons on the right, whatever the content contributed on the\n * left, and (defensively; the strip is a sibling of the chrome today, not a\n * child) a tab. Neither the downward pull nor the double-click may fire when\n * the press landed on one of those.\n *\n * ONE CONSTANT, TWO LISTENERS, and that is the point of writing it down\n * rather than repeating the selector: two doors onto one verb that disagree\n * about where the door is are worse than one door. A button added to this\n * strip later must become inert under BOTH gestures at once, and it cannot\n * do that if each of them carries its own copy of the rule. */\nconst CHROME_NO_FLOAT = 'button, .twm-leaf__tab';\n\n/**\n * IS THIS GESTURE THE DOWNWARD PULL? Asked in TWO places that must not be\n * allowed to disagree, which is the only reason it is a function.\n *\n * The pull asks it of every `pointermove`, to decide whether 24px of travel\n * has become a float. `_syncChromeDragSource`'s `dragstart` asks it of the\n * first few pixels, to decide whether to REFUSE the native drag and leave the\n * pointer to the pull. Two answers to \"which way is this going\" would produce\n * a gesture that is neither: the drag declines, the pull never accepts, and\n * the chrome does nothing at all.\n *\n * `sideways <= down` rather than `<`, so a gesture exactly on the diagonal\n * belongs to the pull in both readers \u2014 the pull's own test has always been\n * `sideways > down` rejects, so this is the same boundary written once.\n */\nfunction _isDownwardPull(down, sideways) {\n return down > 0 && sideways <= down;\n}\n\n/**\n * C33. THE MARKER THAT SAYS \"THIS DRAG IS ONE OF OURS\", AND NOTHING ELSE.\n *\n * It carries the literal string `'1'` because it carries no information at all.\n * Under the HTML5 protected-mode rules `dataTransfer.getData()` answers the\n * empty string for every event of a drag except `drop`, and only `types` is\n * readable during `dragenter`/`dragover` \u2014 which is precisely when the decision\n * to arm a drop zone has to be made. So the source leaf and tab index travel in\n * `TileRenderer._tabDrag` and this type is an admission ticket.\n *\n * Encoding the leaf id into the type NAME (`\u2026/tile-tab/leaf-7`) would make it\n * readable on dragover and is the trap: `types` would then be unbounded, every\n * consumer would have to parse it, and the string would become a wire format\n * nobody declared. One constant, one meaning.\n */\nexport const TILE_TAB_MIME = 'application/x-twm-tile-tab';\n\n/** Does this drag carry a tile tab? The ONLY admission test available during\n * `dragover`, per the note on `TILE_TAB_MIME`. */\nfunction _isTileTabDrag(e) {\n try { return !!e.dataTransfer?.types?.includes(TILE_TAB_MIME); }\n catch { return false; }\n}\n\nexport class TileRenderer {\n /** C33. Readable from a consumer that holds only the renderer \u2014 the Tables\n * drop suite drives real drag events and has to build a `dataTransfer`\n * stub that this renderer will admit. */\n static get TAB_MIME() { return TILE_TAB_MIME; }\n\n constructor({ root, tree, content, ctx, onFocusChange, onAfterRender,\n tabLayout = null }) {\n if (!content || typeof content.mount !== 'function') {\n throw new Error('TileRenderer: a content registry is required');\n }\n this.root = root;\n this.tree = tree;\n // Shell-scoped kind -> factory table (see content_registry.js).\n this.content = content;\n this.ctx = ctx || {};\n this.onFocusChange = onFocusChange || (() => {});\n /** Fired after every repaint, once the tree's DOM is settled.\n *\n * A LEAF'S WRAP IS REBUILT whenever its content key changes \u2014 a tab\n * added, a tab removed, the leaf re-seeded \u2014 and anything an embedder\n * parented INTO that wrap goes with the old one. A window contained in\n * a pane (C21) is exactly that: promote a second tab out of a\n * two-tab leaf and the first promotion's window is silently removed\n * from the document, because its container was the wrap that the\n * second promotion's re-render replaced.\n *\n * The renderer cannot know about windows, so it says \"I have\n * repainted\" and the WM re-homes what it owns. */\n this.onAfterRender = onAfterRender || (() => {});\n // leafId -> { wrapEl, bodyEl, chromeEl, content, kindKey, tabStrip }\n this._leafCache = new Map();\n this._drag = null;\n /** C33. The tab currently being carried: `{leafId, idx, el}`, or null.\n * On the RENDERER rather than in a per-leaf closure \u2014 see the header:\n * the bottom strip's `dragFromIdx` is per leaf render, and a drag that\n * crosses tiles needs an identity leaf B's handlers can read. */\n this._tabDrag = null;\n /** The chrome pull-down's live pointer \u2014 `{startX, startY, x, y}` \u2014\n * while a press is held on a leaf chrome, else null. Read by the\n * `dragstart` in `_syncChromeDragSource`, which has to decide whether\n * this gesture is the float and get out of its way. Here rather than\n * in the pull's own closure for `_tabDrag`'s reason: the reader is a\n * method bound to a different element. */\n this._chromePull = null;\n /** The last `wm.tabDropProbe` answer, stashed on `dragover` because\n * `drop` must not re-probe: the pointer can be one pixel outside the\n * zone the preview drew, and the rectangle drawn is the rectangle the\n * drop delivers (C15). */\n this._tabDropProbe = null;\n this._tabDropPreviewEl = null;\n /** Set by `createShell().dispose()`. A renderer whose shell is gone must\n * not paint: an embedder that rebuilds its shell over the same root\n * would otherwise find a stale callback repainting the previous tree\n * on top of the live one, and the two would fight over one element. */\n this.disposed = false;\n this.root.classList.add('twm-root');\n // C22. The ATTRIBUTE WINS over the config when it is already set, and\n // that order is the point: an embedder that persists this per user has\n // its answer before `createShell` is called, on the element it owns,\n // and does not have to race the shell's construction to apply it.\n this.tabLayout = _normalizeTabLayout(\n this.root.dataset?.twmTabs ?? tabLayout ?? DEFAULT_TAB_LAYOUT);\n if (this.root.dataset) this.root.dataset.twmTabs = this.tabLayout;\n // \u2026and stays live. `setTabLayout` writes the attribute back, so this\n // fires once with nothing to do rather than looping.\n this._layoutObserver = typeof MutationObserver === 'function'\n ? new MutationObserver(() => this.setTabLayout(this.root.dataset?.twmTabs))\n : null;\n this._layoutObserver?.observe(this.root,\n { attributes: true, attributeFilter: ['data-twm-tabs'] });\n this.root.addEventListener('mousedown', this._onMouseDown.bind(this));\n // C33. ON THE ROOT, IN THE CONSTRUCTOR, FOR THE SAME REASON THE\n // `mousedown` ABOVE IS. `render()` clears the root, so a listener bound\n // to a leaf wrap is a listener that survives until the next repaint \u2014\n // and a drag is exactly the situation in which repaints happen.\n this.root.addEventListener('dragover', this._onTabDragOver.bind(this));\n this.root.addEventListener('drop', this._onTabDrop.bind(this));\n this.root.addEventListener('dragleave', this._onTabDragLeave.bind(this));\n // `dragend` FIRES ON THE SOURCE AND IS THE ONLY GUARANTEED END. A drag\n // cancelled with Escape, or released over a window that refused it,\n // produces `dragend` and no `drop` at all \u2014 so every scrap of painted\n // state is torn down here rather than in the drop.\n this.root.addEventListener('dragend', this._onTabDragEnd.bind(this));\n }\n\n render() {\n if (this.disposed) return;\n // Detaching a wrap (remove() then re-append() in `_mount`) wipes\n // `scrollTop` on every scrollable descendant of that wrap.\n // Snapshot per-leaf so we can restore after re-attach \u2014 keyed\n // by element refs that survive the detach.\n const savedScrolls = new Map(); // leafId -> [{el, top}, \u2026]\n for (const [leafId, entry] of this._leafCache.entries()) {\n const snap = [];\n entry.wrapEl.querySelectorAll('*').forEach((el) => {\n if (el.scrollTop > 0 || el.scrollLeft > 0) {\n snap.push({ el, top: el.scrollTop, left: el.scrollLeft });\n }\n });\n savedScrolls.set(leafId, snap);\n entry.wrapEl.remove();\n }\n // R13. A WINDOW PARENTED TO THE ROOT IS NOT A TILE, AND IS NOT THE\n // RENDERER'S TO THROW AWAY. `innerHTML = ''` below is indiscriminate \u2014\n // it destroys every direct child \u2014 and the window manager parents\n // floating windows here deliberately: FOR THE LENGTH OF EVERY DRAG\n // (`dragHost`, R1), which is the live route and not a rare one, since a\n // repaint during a drag needs nothing more exotic than the drag itself\n // changing focus or a splitter moving. It was also permanent for a\n // window snapped to fill the layer; R14 removed that mode \u2014 the top\n // edge docks now \u2014 but the drag remains, and a consumer calling the\n // public `toggleMaximize({claimable: false})` on a window the WM has\n // left on its drag host puts one up here for good. Any repaint while a\n // window was up here deleted its element silently: no error, nothing\n // thrown, and a `ManagedWindow` object still perfectly alive with\n // nothing on screen \u2014 the same shape of bug as the leaf wraps above,\n // which is why they are detached and re-attached too rather than merely\n // skipped. The tiles have to be rebuilt around them.\n const floats = [...this.root.children].filter((el) =>\n el.classList?.contains('twm-managed-window')\n || el.classList?.contains('twm-managed-window__backdrop'));\n for (const el of floats) el.remove();\n this.root.innerHTML = '';\n const tree = this.tree;\n if (!tree.rootId) {\n const empty = document.createElement('div');\n empty.className = 'twm-empty';\n empty.textContent = 'Empty desktop \u2014 Ctrl+K to open something.';\n this.root.appendChild(empty);\n this._cleanCache(new Set());\n // An empty desktop is still a desktop a window can be floating\n // over, and it is the one branch that returns early.\n for (const el of floats) this.root.appendChild(el);\n return;\n }\n const liveLeafIds = new Set();\n this._mount(tree.rootId, this.root, liveLeafIds);\n this._cleanCache(liveLeafIds);\n // LAST, and before `onAfterRender` \u2014 which is where the WM re-homes\n // the contained ones into their rebuilt wraps, and it can only move an\n // element that is still in the document.\n for (const el of floats) this.root.appendChild(el);\n this._updateFocusClasses();\n\n // Restore scrolls \u2014 once synchronously and once next frame so\n // any layout reflow between detach/reattach doesn't leave the\n // browser's clamped value in place.\n const restore = () => {\n for (const [leafId, snap] of savedScrolls) {\n if (!this._leafCache.has(leafId)) continue;\n for (const { el, top, left } of snap) {\n if (el.isConnected) {\n el.scrollTop = top;\n el.scrollLeft = left;\n }\n }\n }\n };\n restore();\n requestAnimationFrame(restore);\n try { this.onAfterRender(); }\n catch (err) { console.error('[tile-renderer] onAfterRender threw', err); }\n }\n\n /** Unmount everything and stop painting. `dispose()` on the shell calls\n * this: `root.innerHTML = ''` detaches DOM without telling a single content\n * factory, so a page module that installed a `window` listener or an\n * interval keeps both, invisibly, for the life of the tab. */\n destroy() {\n this._layoutObserver?.disconnect();\n this._layoutObserver = null;\n // C33. The drop preview is parented to `document.body`, not to the\n // root, so `_cleanCache` cannot reach it: a shell torn down mid-drag\n // would leave a blue rectangle painted over the page with nothing left\n // that knows how to remove it.\n this._clearTabDropPreview();\n this._tabDrag = null;\n this._tabDropProbe = null;\n this._cleanCache(new Set());\n this.disposed = true;\n }\n\n _cleanCache(liveSet) {\n for (const [leafId, entry] of [...this._leafCache.entries()]) {\n if (!liveSet.has(leafId)) {\n try { entry.content?.destroy?.(); } catch (_) {}\n // C22. The top strip is a COMPONENT, not markup: it holds a\n // DragReorder with container listeners and a context menu\n // parented to `document.body`. Dropping the element it lives in\n // leaves both, invisibly, for the life of the page \u2014 the same\n // reason `destroy()` exists on the content above it.\n this._disposeTabStrip(entry);\n entry.wrapEl.remove();\n this._leafCache.delete(leafId);\n }\n }\n }\n\n _mount(nodeId, parentEl, liveSet) {\n const node = this.tree.get(nodeId);\n if (!node) return;\n if (node.kind === 'leaf') {\n liveSet.add(node.id);\n const el = this._leafEl(node);\n parentEl.appendChild(el);\n return;\n }\n // Split node \u2014 flex container with N children + N-1 splitters.\n const split = document.createElement('div');\n split.className = `twm-split twm-split--${node.dir}`;\n split.dataset.splitId = node.id;\n parentEl.appendChild(split);\n const total = node.sizes.reduce((a, b) => a + b, 0) || node.children.length;\n node.children.forEach((cid, i) => {\n const slot = document.createElement('div');\n slot.className = 'twm-slot';\n const frac = (node.sizes[i] || 1) / total;\n slot.style.flex = `${frac} ${frac} 0`;\n split.appendChild(slot);\n this._mount(cid, slot, liveSet);\n if (i < node.children.length - 1) {\n const splitter = document.createElement('div');\n splitter.className = `twm-splitter twm-splitter--${node.dir}`;\n splitter.dataset.splitId = node.id;\n splitter.dataset.slotIdx = String(i);\n split.appendChild(splitter);\n }\n });\n }\n\n _leafEl(leaf) {\n // Tab strip key is part of the cache key so the strip's chrome\n // (which tabs exist, which one is active) re-renders when tabs\n // mutate, but the active tab's body itself stays cached when the\n // user clicks back to a tab they already visited.\n const activeTab = (Array.isArray(leaf.tabs) && leaf.tabs.length > 0)\n ? leaf.tabs[Math.max(0, Math.min(leaf.tabs.length - 1, leaf.activeTabIdx || 0))]\n : null;\n const kindKey = this._leafKindKey(leaf);\n\n let entry = this._leafCache.get(leaf.id);\n if (entry && entry.kindKey === kindKey) {\n entry.titleEl.textContent = leaf.title || (leaf.content ? leaf.content.kind : 'empty');\n // C19. Re-read the glyph beside the title, and for the same reason:\n // a cached leaf is NOT re-mounted, so anything the chrome shows\n // that can change while the content stays put has to be refreshed\n // here or it is frozen at whatever it was when the tile was built.\n _paintLeafIcon(entry.iconEl, leaf, entry.content, this.ctx);\n return entry.wrapEl;\n }\n // Build fresh.\n if (entry) {\n // DELETED, NOT JUST SUPERSEDED. The map still held this entry while\n // the content's own `destroy` ran, so anything reaching the\n // renderer from inside that teardown \u2014 `leafChrome`, and\n // `rebaselineLeaf`, which would have written a key onto a wrap\n // already removed from the document \u2014 read a corpse. The rebuild\n // re-registers under the same id a few lines below, so the only\n // window this closes is the one nothing should be looking in.\n this._leafCache.delete(leaf.id);\n try { entry.content?.destroy?.(); } catch (_) {}\n this._disposeTabStrip(entry);\n entry.wrapEl.remove();\n }\n const wrap = document.createElement('div');\n wrap.className = 'twm-leaf';\n wrap.dataset.leafId = leaf.id;\n // No tabIndex on the wrap \u2014 that would capture focus on click\n // and break the `hostEl.contains(document.activeElement)` guard\n // many tab modules use to scope their keyboard handlers. The\n // body is given tabIndex=-1 below so we can focus the body\n // (which IS the hostEl seen by the mounted tab content) on\n // tile activation.\n const chrome = document.createElement('div');\n chrome.className = 'twm-leaf__chrome';\n // C19. The glyph a floating window has always had, in the tile chrome\n // too \u2014 `ManagedWindow._build` puts one left of its title\n // (`../ui/components/managed_window.js:588-590`) and a tile, which is\n // the SAME content in a different state, had nothing there. Two ways of\n // looking at one table should not disagree about what a table looks\n // like.\n const icon = document.createElement('span');\n icon.className = 'twm-leaf__icon material-symbols-outlined';\n const title = document.createElement('span');\n title.className = 'twm-leaf__title';\n title.textContent = leaf.title || (leaf.content ? leaf.content.kind : 'empty');\n const actions = document.createElement('span');\n actions.className = 'twm-leaf__actions';\n // C18. Content-contributed chrome actions land HERE, to the left of the\n // structural ones, and the divider is not decoration: the buttons on\n // the right act on the TILE (split it, float it, close it) and the ones\n // on the left act on what is IN it (this table's settings, this table\n // in a browser window). Two kinds of verb in one strip with nothing\n // between them is how \"close\" gets read as \"close the table\".\n const contentActions = document.createElement('span');\n contentActions.className = 'twm-leaf__actions twm-leaf__actions--content';\n // Panel tiles (left nav / right / bottom) can't be promoted to\n // managed windows \u2014 they're chrome, not content. Hide the\n // promote button in their chrome.\n const isPanel = String(leaf.content?.kind || '').startsWith('panel:');\n // Split + promote are structural actions on a content tile; panels\n // (left nav / right / bottom) are chrome, not content, so they only\n // get the close affordance.\n actions.innerHTML = `\n ${isPanel ? '' : `\n <button class=\"twm-leaf__btn\" data-action=\"split-h\" title=\"Split horizontally (Alt+H)\">\n <span class=\"material-symbols-outlined\">splitscreen_vertical_add</span>\n </button>\n <button class=\"twm-leaf__btn\" data-action=\"split-v\" title=\"Split vertically (Alt+V)\">\n <span class=\"material-symbols-outlined\">splitscreen_add</span>\n </button>\n <button class=\"twm-leaf__btn\" data-action=\"promote\" title=\"Float this pane as a window (Alt+F)\">\n <span class=\"material-symbols-outlined\">web_asset</span>\n </button>`}\n <button class=\"twm-leaf__btn\" data-action=\"close\" title=\"Close (Alt+W)\">\n <span class=\"material-symbols-outlined\">close</span>\n </button>\n `;\n chrome.append(icon, title, contentActions, actions);\n // Painted BEFORE the mount as well as after it, so a kind whose factory\n // supplies no icon of its own \u2014 and every kind that existed before this\n // did \u2014 still wears the taxonomy's glyph from the first frame rather\n // than acquiring one a mount later.\n _paintLeafIcon(icon, leaf, null, this.ctx);\n const body = document.createElement('div');\n body.className = 'twm-leaf__body';\n body.tabIndex = -1; // focusable via JS so document.activeElement\n // lands inside the body when the tile is\n // activated (keyboard-handler scope check).\n // The tab strip \u2014 only rendered when the leaf carries more than one\n // tab. It is built as part of the leaf element, not of the body, so it\n // stays pinned at its edge of the tile regardless of body scroll.\n const tabBar = document.createElement('div');\n tabBar.className = 'twm-leaf__tabbar';\n if (!Array.isArray(leaf.tabs) || leaf.tabs.length <= 1) {\n tabBar.classList.add('twm-leaf__tabbar--hidden');\n }\n // C22. CHROME FIRST IN BOTH LAYOUTS. The chrome is the pane's identity\n // and the tabs are what is inside it; a strip above the title would say\n // the tabs own the pane rather than the other way round.\n wrap.append(chrome, ...(this.tabLayout === 'top' ? [tabBar, body]\n : [body, tabBar]));\n\n wrap.addEventListener('mousedown', (e) => {\n if (e.target.closest('.twm-splitter')) return;\n this.tree.focus(leaf.id);\n this._updateFocusClasses();\n this.onFocusChange(leaf.id);\n // Park focus on the deepest focus host inside the body so\n // page modules whose keydown scope is\n // `hostEl.contains(document.activeElement)` actually see\n // the activation. The page-shell wrapper, when present,\n // is what tab modules treat as `hostEl`; falling back to\n // the leaf body itself is fine for panel tiles.\n const tgt = e.target;\n const focusable = tgt.closest?.(\n 'input, textarea, select, button, a, [contenteditable=\"true\"], [tabindex]');\n if (!focusable || !body.contains(focusable)) {\n setTimeout(() => {\n const ps = body.querySelector('.twm-page-shell__content')\n ?? body;\n if (ps.tabIndex == null || ps.tabIndex < -1) ps.tabIndex = -1;\n ps.focus({ preventScroll: true });\n }, 0);\n }\n });\n // \u2500\u2500 PULL THE HEADER DOWN TO FLOAT THE PANE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n //\n // The inverse of the gesture everyone already knows: a maximised window\n // is un-maximised by dragging its titlebar away from the edge, and a\n // tile is a pane maximised into its slot. So dragging a tile's chrome\n // DOWNWARD lifts it out as a floating window \u2014 the same thing the\n // promote button does, reached the way a window manager teaches you to\n // reach it.\n //\n // Downward only, and past a real threshold. A tile's chrome is also\n // where you click to focus the pane, and a click carries a few pixels\n // of movement with it; anything less than a deliberate pull is a click.\n // Sideways is left alone: it means nothing here, and claiming it would\n // make the strip feel like it grabs the pointer.\n chrome.addEventListener('pointerdown', (e) => {\n if (e.button !== 0) return;\n if (e.target.closest(CHROME_NO_FLOAT)) return;\n const startY = e.clientY;\n const startX = e.clientX;\n // PUBLISHED ON THE RENDERER, for `_syncChromeDragSource` to read.\n //\n // The native drag beats the pull to the pointer and there is no\n // threshold at which it does not: Chromium starts one after about\n // 5px of travel while the pull needs 24, so the ONLY moment the\n // direction of this gesture can still be acted on is inside the\n // `dragstart` the drag announces itself with. That handler has no\n // other way to learn it \u2014 a `dragstart`'s own `clientX`/`clientY`\n // read 0 for a drag begun this way (measured, Edge 152 /\n // Chromium 152), so the event cannot answer the question being\n // asked of it and this record has to.\n //\n // On the renderer rather than in this closure for `_tabDrag`'s\n // reason: the reader is a method bound to a different element.\n const pull = { startX, startY, x: startX, y: startY, lifted: false };\n this._chromePull = pull;\n const onMove = (move) => {\n pull.x = move.clientX;\n pull.y = move.clientY;\n if (pull.lifted) return;\n const down = move.clientY - startY;\n const sideways = Math.abs(move.clientX - startX);\n if (down < TILE_LIFT_PX || !_isDownwardPull(down, sideways)) return;\n pull.lifted = true;\n // ONLY `pointermove` COMES OFF HERE, AND THE RECORD STAYS \u2014\n // `cleanup()` would take it, and taking it is a defect.\n //\n // A `pointermove` can be COALESCED: one event may carry the\n // whole 24px and promote on its first delivery, and Blink\n // hands that move to script BEFORE starting the drag it\n // crossed the threshold for \u2014 so a `dragstart` still arrives\n // afterwards, and `_syncChromeDragSource` has to be able to\n // read the gesture to refuse it. Without the record it did\n // not, and a tab drag began on the pane that had just floated\n // out of the tile. `pointerup` below is what clears it.\n window.removeEventListener('pointermove', onMove);\n this.ctx.onLeafAction?.(leaf.id, 'promote');\n };\n const cleanup = () => {\n if (this._chromePull === pull) this._chromePull = null;\n window.removeEventListener('pointermove', onMove);\n window.removeEventListener('pointerup', cleanup);\n window.removeEventListener('pointercancel', cleanup);\n };\n window.addEventListener('pointermove', onMove);\n window.addEventListener('pointerup', cleanup);\n window.addEventListener('pointercancel', cleanup);\n });\n\n // \u2500\u2500 \u2026OR DOUBLE-CLICK IT, WHICH IS THE GESTURE THAT ALREADY HAD A\n // RETURN JOURNEY AND NO OUTWARD ONE \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 // Double-clicking a FLOATING window's title bar docks it back into a\n // tile \u2014 `managed_window.js:919` binds it to `toggleMaximize()`, whose\n // claimed verb under this WM is `bringBackWindow` (\"maximise means back\n // to tile\", R7 \u2014 see the `onMaximize` `wm._promote` builds). So the\n // half of the toggle that PUTS a\n // pane back was spelled as a double-click on a header, and the half\n // that takes it out was not: a user who learned the docking gesture had\n // to reach for the float button, or discover the pull above, to undo\n // what a double-click had just done. One gesture, one strip, both\n // directions.\n //\n // The pull and this are two doors onto ONE verb, not two verbs. Both go\n // through `onLeafAction(leaf.id, 'promote')`, so the WM's guards apply\n // to both without knowing there are two \u2014 a panel tile and a window\n // placeholder are refused in `wm._floatableLeaf` and a\n // second double-click on an already-floated pane finds a re-seeded\n // start tile rather than the content, which is the tree telling the\n // truth rather than a case to special-case here.\n //\n // NATIVE `dblclick`, NOT A HAND-ROLLED CLICK COUNT \u2014 and that is a\n // claim about the handlers above, not a preference. C28\n // (`managed_window.js:1305`) is the case that says why: its pointerdown\n // re-parented the window element out of the document before the pointer\n // had moved, Blink and Gecko drop the pending `click` when the pressed\n // element leaves the document, and the title bar's `dblclick` therefore\n // never fired once in a real browser while looking perfectly correct in\n // jsdom. Nothing on this strip does that. The wrap's `mousedown`\n // toggles a class and calls `onFocusChange`, which the WM turns into\n // `wm._notifyChange` \u2014 a callback and two bus emits, no repaint; its\n // deferred `body.focus()` moves focus and moves\n // nothing else, and focus does not reset a click count. The pull's\n // `pointerdown` only registers window listeners and neither\n // `preventDefault`s nor captures the pointer. The chrome the first\n // click lands on is still in the document, still the same element, when\n // the second one lands.\n //\n // No `e.button` test, unlike the pull: `dblclick` is dispatched for the\n // primary button only. No `preventDefault`, and NO NEW CSS: the strip\n // is already `user-select: none` (`css/overrides.css:1001`, mirrored\n // into the generated `css/flexdesk.css:4743`), so a double-click here\n // cannot word-select the title on its way to floating the pane.\n chrome.addEventListener('dblclick', (e) => {\n if (e.target.closest(CHROME_NO_FLOAT)) return;\n this.ctx.onLeafAction?.(leaf.id, 'promote');\n });\n\n // Right-click on the chrome \u2192 tile context menu (split, close,\n // promote, move to desktop). Listening on `chrome` only so the\n // tile body keeps its own contextmenu (e.g. DataTable's).\n chrome.addEventListener('contextmenu', (e) => {\n e.preventDefault();\n this.tree.focus(leaf.id);\n this._updateFocusClasses();\n this.onFocusChange(leaf.id);\n this.ctx.onTileContextMenu?.(leaf.id, e.clientX, e.clientY);\n });\n actions.addEventListener('click', (e) => {\n const btn = e.target.closest('button');\n if (!btn) return;\n e.stopPropagation();\n this.ctx.onLeafAction?.(leaf.id, btn.dataset.action);\n });\n\n // Mount content. The per-leaf ctx carries `leafId` so the\n // mounted content can route subsequent navigation (e.g.\n // `workspaceTabs.openTab(...)`) back into its own tile via\n // `wm.openFromContext(ctx, ...)` instead of always hitting\n // the primary tile.\n let content = {};\n if (leaf.content) {\n const leafCtx = { ...this.ctx, leafId: leaf.id };\n // Mount with the ACTIVE TAB's props, not `content.props`.\n // `wm.updateActiveTabProps` (the canonical per-tile view-state\n // store \u2014 survives reload + desktop switches via desktops.json)\n // writes only into `tabs[active].props` and deliberately leaves\n // `content.props` untouched so the cache key (kindKey) stays\n // stable and a tab-prop change doesn't churn-rebuild the leaf.\n // The trade-off: `content.props` goes stale, so a leaf rebuilt\n // after a desktop round-trip must read the fresh tab props here.\n const activeProps = activeTab?.props ?? leaf.content.props;\n content = this.content.mount(leaf.content.kind, body, activeProps, leafCtx);\n if (content.title) title.textContent = content.title;\n // C19. `icon` is the natural sibling of `title` and is read the\n // same way: AFTER the mount, because that is the first moment the\n // content knows what it is \u2014 a canvas pane you emptied is the same\n // KIND as the overview and wants a different glyph.\n _paintLeafIcon(icon, leaf, content, this.ctx);\n // C18. A content factory may return `chromeActions`:\n // `[{icon, title, onClick}]`. Read AFTER the mount, because that is\n // the first moment the content knows what it can offer \u2014 a table\n // does not know it has settings until it has a definition.\n _paintContentActions(contentActions, content.chromeActions);\n // C20. A content factory may also VETO structural verbs:\n // `chrome: { promote: false, close: false }`. Read after the mount\n // for the same reason `chromeActions` is \u2014 it is the first moment\n // the content knows what it is.\n //\n // The case that forced it: an embedder's MASTER tile. It is the\n // ground floating windows stand on and the thing you are left with\n // when everything else is closed, so \"float this pane as a window\"\n // and \"close this pane\" are offers to destroy the only surface the\n // application has. The renderer already made this exact judgement\n // for `panel:*` kinds by hardcoding it; this is that judgement,\n // available to anyone.\n _vetoStructuralActions(actions, content.chrome);\n } else {\n body.innerHTML = `<div class=\"tile-placeholder\"><div class=\"tile-placeholder__hint\">empty tile</div></div>`;\n }\n entry = { wrapEl: wrap, bodyEl: body, chromeEl: chrome,\n titleEl: title, iconEl: icon, content,\n // DERIVED HERE, NOT REUSED FROM ABOVE. `kindKey` was computed\n // before the mount; content that records its sub-tab or its\n // scroll offset WHILE mounting has already written to the\n // tree by now, so the value captured earlier is stale and the\n // very next repaint would tear down the tile that had just\n // said where it was.\n kindKey: this._leafKindKey(leaf), tabBarEl: tabBar,\n // C22. The `top` layout's NotebookTabBar, and the child it\n // mounts into. Null under the `bottom` layout, which is plain\n // markup this file writes itself.\n tabStrip: null, tabStripHostEl: null };\n this._leafCache.set(leaf.id, entry);\n\n // Render tab strip last so it has access to the cached entry.\n this._renderTabBar(leaf, entry);\n return wrap;\n }\n\n /**\n * The cache key for a leaf: everything that must change before its wrap is\n * torn down and rebuilt. Extracted so `rebaselineLeaf` below computes the\n * same string this does \u2014 two spellings of one key is a cache that misses\n * on every render or never misses at all, and both look like working code.\n */\n _leafKindKey(leaf) {\n const tabFingerprint = (leaf.tabs || []).map((t) =>\n `${t.kind}::${JSON.stringify(t.props || {})}`).join('|') + `#${leaf.activeTabIdx || 0}`;\n return leaf.content\n ? `${leaf.content.kind}::${JSON.stringify(leaf.content.props || {})}::tabs:${tabFingerprint}`\n : '__empty__';\n }\n\n /**\n * The cache key a leaf has RIGHT NOW, for a caller that is about to change\n * the tree and wants to say what it expected to be changing from. See\n * `rebaselineLeaf`.\n */\n leafKey(leafId) {\n const leaf = this.tree?.get?.(leafId);\n return (leaf && leaf.kind === 'leaf') ? this._leafKindKey(leaf) : null;\n }\n\n /**\n * C34. ACCEPT A PROPS WRITE THE CONTENT MADE ABOUT ITSELF, WITHOUT\n * REBUILDING THE TILE THAT MADE IT.\n *\n * The cache key above includes every tab's props, and that is right for\n * NAVIGATION: a `table` tab whose `id` changes is different content and the\n * tile must be re-mounted. It is exactly wrong for VIEW STATE. The\n * framework hands every tile a `workspaceTabs.updateProps(patch)`\n * (`page_factory.js`) documented as *\"persist editor sub-state into this\n * tile's WM tab props\"* \u2014 a scroll offset, an open section, a selected\n * sub-tab. Writing one changed the fingerprint, so the NEXT repaint (a\n * focus change, a tab switch, a window promoted three tiles away) missed\n * the cache, destroyed the content and mounted it again. The tile was torn\n * down BY the call that existed to let it remember something, and because\n * the rebuild reads the props back the result looked almost right \u2014 the\n * editor came back at the saved scroll position, with everything uncommitted\n * in it gone.\n *\n * So the key is re-baselined instead: the entry keeps its live DOM and\n * starts answering to the new props. The next real navigation still misses\n * and still rebuilds, because that changes the kind or the tab set and this\n * only ever accepts what is already on screen.\n *\n * \u2550\u2550 IT ACCEPTS ONLY THE DELTA IT WAS CALLED FOR \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * The key is re-derived from the tree as it is NOW, so a first version of\n * this swallowed every difference at once \u2014 including a change the tree had\n * taken and the renderer had not drawn yet. Any mutation that does not\n * repaint (`updateActiveTabProps` is itself one, and an embedder writing\n * through `TileTree` directly is another) followed by a props write would\n * have been accepted onto a wrap still showing the OLD content, and the\n * tile would never have re-mounted: one thing drawn under another's title,\n * permanently, with nothing left that knows the two disagree.\n *\n * `expected` is the fix and it is the caller's own honesty: the key it read\n * BEFORE its write. If the cached entry is not still at that key, something\n * else has changed since the tile was mounted and this is not the caller's\n * to accept \u2014 refuse, and let the ordinary miss rebuild it.\n *\n * A structural test was tried first and is not enough. *Same kind, different\n * props* is a scroll offset AND it is a navigation to another table; nothing\n * in the leaf can tell them apart, because the difference is which caller\n * asked. `expected` asks the caller.\n *\n * @param {string} leafId\n * @param {string} [expected] the key the caller read before its own write.\n * Omitted means \"accept whatever is there\", which is what the first\n * version did and is kept only so an older caller does not silently\n * change behaviour \u2014 every caller in this tree passes one.\n * @returns {boolean} whether a cached wrap was re-baselined\n */\n rebaselineLeaf(leafId, expected = undefined) {\n const entry = this._leafCache.get(leafId);\n const leaf = this.tree?.get?.(leafId);\n if (!entry || !leaf || leaf.kind !== 'leaf') return false;\n if (expected !== undefined && entry.kindKey !== expected) return false;\n entry.kindKey = this._leafKindKey(leaf);\n return true;\n }\n\n /**\n * C22. Change the tab layout of a LIVE renderer.\n *\n * Only the strip is rebuilt. The tab bar element is MOVED between its two\n * positions and the body is never detached, so no content factory is\n * unmounted \u2014 which is the whole reason this is a method rather than\n * \"rebuild the shell with the other option\". A grid holding staged edits\n * must not lose them because someone changed where its tabs are drawn.\n *\n * Accepts anything: the value arrives from a DOM attribute and from\n * persisted user settings, and an unknown one means the default.\n */\n setTabLayout(layout) {\n const next = _normalizeTabLayout(layout);\n // The attribute is written back below, which re-enters through the\n // observer; this is where that stops.\n if (next === this.tabLayout) return;\n this.tabLayout = next;\n if (this.root.dataset && this.root.dataset.twmTabs !== next) {\n this.root.dataset.twmTabs = next;\n }\n if (this.disposed) return;\n for (const [leafId, entry] of this._leafCache) {\n this._placeTabBar(entry);\n // The two strips are different components, not two skins of one:\n // whichever was there is torn down and the other is built.\n this._disposeTabStrip(entry);\n entry.tabBarEl.innerHTML = '';\n const leaf = this.tree.get(leafId);\n if (leaf) this._renderTabBar(leaf, entry);\n }\n }\n\n /** Put a leaf's tab bar on the side the current layout says. Moving an\n * attached element is a re-parent, not a rebuild \u2014 the body keeps its\n * DOM, its listeners and its scroll. */\n _placeTabBar(entry) {\n const { wrapEl, bodyEl, tabBarEl } = entry;\n if (!wrapEl || !tabBarEl || !bodyEl) return;\n if (this.tabLayout === 'top') wrapEl.insertBefore(tabBarEl, bodyEl);\n else wrapEl.appendChild(tabBarEl);\n }\n\n /** Tear down the `top` layout's component, if this leaf has one. Safe to\n * call on a leaf that never had one, and on one that already lost it. */\n _disposeTabStrip(entry) {\n if (!entry?.tabStrip) return;\n try { entry.tabStrip.dispose(); } catch (err) {\n console.error('[tile] tab strip dispose threw', err);\n }\n entry.tabStrip = null;\n entry.tabStripHostEl = null;\n }\n\n /** Paint a leaf's tab strip in whichever layout is current. The strip is\n * hidden for single-tab leaves in BOTH layouts \u2014 the common case, and the\n * reason existing single-pane layouts read as identical to today. */\n _renderTabBar(leaf, entry) {\n // C33. BEFORE the single-tab early return, because the single-tab leaf\n // is exactly the case this answers: no strip is drawn, so the chrome is\n // the only thing left to grab.\n this._syncChromeDragSource(leaf, entry);\n const bar = entry.tabBarEl;\n if (!bar) return;\n const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];\n bar.classList.toggle('twm-leaf__tabbar--top', this.tabLayout === 'top');\n if (tabs.length <= 1) {\n bar.classList.add('twm-leaf__tabbar--hidden');\n this._disposeTabStrip(entry);\n bar.innerHTML = '';\n return;\n }\n bar.classList.remove('twm-leaf__tabbar--hidden');\n if (this.tabLayout === 'top') this._renderTopTabBar(leaf, entry, tabs);\n else this._renderBottomTabBar(leaf, entry, tabs);\n }\n\n /**\n * C33. THE CHROME IS THE DRAG SOURCE FOR A LEAF THAT HAS ONE TAB.\n *\n * Product owner, 2026-08-27. The full argument is in the file header; what\n * this function owns is the THREE conditions and why each is a condition\n * rather than a preference:\n *\n * EXACTLY ONE TAB. With two or more, the strip is drawn and names each\n * tab; a chrome drag would then have to guess which one was meant, and\n * guessing is what the strip exists to avoid. With exactly one, \"this\n * tab\" and \"what is in this pane\" are the same thing.\n *\n * NOT A PANEL. Panel tiles are chrome, not content \u2014 the same exclusion\n * `_floatableLeaf` and `_snapProbe` already make, and for the same reason:\n * there is nothing in them that belongs anywhere else.\n *\n * THE CONTENT DID NOT VETO `promote` (C20). One rule instead of two: a\n * tab you may not lift out of its pane is a tab you may not drag into\n * another one. This is what excludes an embedder's master tile \u2014 the\n * ground its floating windows stand on \u2014 whose whole reason for existing\n * is that it stays where it is.\n *\n * THE LISTENERS ARE BOUND ONCE PER CHROME ELEMENT and the ATTRIBUTE is\n * re-decided on every pass. That split is deliberate: `draggable` changes\n * the moment a second tab arrives, while the chrome element itself survives\n * for as long as its wrap does, and re-adding a listener on every render\n * would stack one per repaint.\n */\n _syncChromeDragSource(leaf, entry) {\n const chrome = entry?.chromeEl;\n if (!chrome) return;\n const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];\n const isPanel = String(leaf.content?.kind || '').startsWith('panel:');\n const vetoed = this.leafChrome(leaf.id)?.promote === false;\n const on = tabs.length === 1 && !isPanel && !vetoed;\n if (on) chrome.setAttribute('draggable', 'true');\n else chrome.removeAttribute('draggable');\n if (chrome.__twmTabDragBound) return;\n chrome.__twmTabDragBound = true;\n chrome.addEventListener('dragstart', (ev) => {\n // A drag that began on a BUTTON is not a drag of the pane. Buttons\n // are not draggable on their own, but a draggable ancestor makes\n // them so \u2014 hence the same guard the two float gestures apply, from\n // the same constant, so a control added to this strip later becomes\n // inert under all three at once.\n if (ev.target?.closest?.(CHROME_NO_FLOAT)) { ev.preventDefault(); return; }\n // \u2550\u2550 A DOWNWARD GESTURE IS THE PULL, AND IT HAS TO BE REFUSED\n // HERE, BECAUSE NOTHING LATER CAN \u2550\u2550\u2550\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 // The file header used to claim these two gestures could not\n // collide: *\"the pull \u2026 loses cleanly \u2026 The pull is also still\n // reachable, on every part of the chrome a single-tab leaf's title\n // does not cover.\"* That describes `draggable` on the TITLE. It is\n // set on the CHROME, four lines above \u2014 the whole strip \u2014 so the\n // pull was not merely losing where the title sits, it had no\n // reachable surface left on any single-tab tile. And below two tabs\n // the strip is hidden, so single-tab is the common case: the\n // reported symptom is *\"pulling down a tile at title bar does not\n // anymore turn it into a managed window\"*, and it was every tile.\n //\n // MEASURED, against the running application in Edge 152 rather\n // than in jsdom, which cannot start a native drag and therefore\n // cannot see any of this: press the chrome and pull down 140px and\n // the sequence is `dragstart`, `pointercancel`, `dragend`, with\n // zero windows promoted. The drag arrives at ~5-16px of travel and\n // `pointercancel` tears the pull's listeners down at ~8, so the\n // 24px threshold is never reached and never can be.\n //\n // Refusing the drag here is what hands the pointer back: a\n // `dragstart` that is cancelled starts no drag, so no\n // `pointercancel` is fired and `pointermove` simply resumes \u2014\n // measured, same harness, the pull then fires at 26px. That is\n // also the house idiom for declining a native drag\n // (`DragReorder._start`'s `draggableGuard`, `drag_reorder.js`).\n //\n // C33 IS UNTOUCHED BY IT. A sideways or diagonal grab reports\n // `down <= sideways`, is not refused, and drags exactly as before\n // \u2014 verified in the same browser run. What this costs is the one\n // gesture that was ambiguous by construction: you can no longer\n // start a tab drag by pulling straight DOWN out of the chrome,\n // which is the direction the float has claimed since before the\n // tab drag existed.\n //\n // `_chromePull` OUTLIVES THE PROMOTE, and that is load-bearing\n // rather than tidy: a coalesced `pointermove` can carry the whole\n // 24px and promote on its first delivery, and Blink hands that\n // move to script BEFORE starting the drag it crossed the threshold\n // for \u2014 so a `dragstart` still arrives afterwards. Clearing the\n // record on lift left this read with nothing, so the drag was not\n // refused and a tab drag began on the pane that had just floated\n // out of the tile. The record's last coordinates are the lift's,\n // which are downward by definition, so the same test answers.\n const pull = this._chromePull;\n if (pull && _isDownwardPull(pull.y - pull.startY,\n Math.abs(pull.x - pull.startX))) {\n ev.preventDefault();\n return;\n }\n // Re-read the count rather than trusting the attribute: the tree\n // can gain a tab between a render and a gesture, and a stale\n // `draggable` would carry tab 0 out of a pane that now has three.\n const live = this.tree.get(leaf.id);\n if ((live?.tabs || []).length !== 1) { ev.preventDefault(); return; }\n this._beginTabDrag(ev, leaf.id, 0, chrome, { seedPlainText: true });\n });\n chrome.addEventListener('dragend', () => this._onTabDragEnd());\n }\n\n /**\n * C22. The `top` layout: `NotebookTabBar`, the editor tab strip.\n *\n * The component is mounted into a CHILD of the bar rather than into the bar\n * itself, because `mount()` assigns `container.className = 'tabs\n * notebook-tabs'` (`notebook_tab_bar.js:61`) \u2014 handing it `.twm-leaf__tabbar`\n * would take that class, and with it the strip's height, its background and\n * `--hidden`, off the element this file still controls.\n *\n * Every gesture routes through the SAME `ctx.onLeafTabAction` vocabulary the\n * bottom strip uses, so the WM's tree mutations, its persistence and its\n * change notifications are reached by one path from both layouts.\n */\n _renderTopTabBar(leaf, entry, tabs) {\n const bar = entry.tabBarEl;\n if (!entry.tabStrip) {\n bar.innerHTML = '';\n const host = document.createElement('div');\n bar.appendChild(host);\n const strip = new NotebookTabBar();\n strip.mount(host, this._topTabCallbacks(leaf.id));\n entry.tabStrip = strip;\n entry.tabStripHostEl = host;\n // ONE TAB CONTEXT MENU IN THE APPLICATION, not two.\n //\n // `NotebookTabBar` opens its own `.nb-context-menu` on right-click,\n // whose verbs are a notebook's (rename, duplicate, reveal in\n // explorer) and whose close verbs duplicate \u2014 in a different\n // typeface \u2014 the menu the WM already builds for the bottom strip\n // (`wm._showTabContextMenu`). A user who switches layout must not\n // discover that right-clicking a tab now means something else.\n //\n // CAPTURE phase, on the container: the component binds its handler\n // on each tab element in the bubble phase, so stopping the event on\n // the way DOWN is what keeps its menu from opening at all. There is\n // no callback for this \u2014 the menu is not a callback, it is the\n // component's own DOM.\n host.addEventListener('contextmenu', (ev) => {\n const tabEl = ev.target.closest?.('.tab');\n if (!tabEl) return;\n ev.preventDefault();\n ev.stopPropagation();\n const idx = this._topTabIndex(host, tabEl);\n if (idx < 0) return;\n this.ctx.onLeafTabAction?.(leaf.id, 'menu',\n { idx, x: ev.clientX, y: ev.clientY });\n }, true);\n }\n const activeIdx = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));\n entry.tabStrip.update(\n tabs.map((t, i) => ({\n filePath: _tabKey(i),\n label: t.title || t.kind || '',\n // The component's `fileType` picks a glyph out of a static map\n // of FILE kinds. A tile's kinds are the embedder's, and the\n // taxonomy already answers for them \u2014 see `_paintTopTabs`.\n fileType: t.kind || 'unknown',\n // Nothing sets `dirty` on a tab spec today, so the dot is never\n // drawn. Read anyway, because the day a tile can say it holds\n // unsaved work this is where it says it, and the alternative is\n // a second place to remember.\n isDirty: !!t.dirty,\n })),\n _tabKey(activeIdx));\n this._paintTopTabs(entry.tabStripHostEl, tabs);\n }\n\n /**\n * The two things `NotebookTabBar` derives from a vocabulary a tile does not\n * have, corrected in one pass over the DOM it just wrote.\n *\n * Its tooltip is the file PATH (`notebook_tab_bar.js:163`) and its glyph\n * comes from a static fileType map (`:135`). Ours are `builtin://tab/3` and\n * a content kind, so left alone a tab would advertise its own array index\n * and wear the generic `description` glyph \u2014 while the tile chrome an inch\n * above it shows the taxonomy's icon for exactly the same kind.\n *\n * Reaching into a component's DOM is worth one paragraph of justification.\n * The alternative for the glyph is `NotebookTabBar.setFileTypeIcons()`,\n * which is STATIC and REPLACES the whole map \u2014 so a page that also uses the\n * editor would find its own file icons deleted by whichever of the two\n * rendered last. The class names used here are the component's published\n * contract, stated in its header comment.\n */\n _paintTopTabs(hostEl, tabs) {\n if (!hostEl) return;\n const els = hostEl.querySelectorAll('.tab');\n els.forEach((el, i) => {\n const spec = tabs[i];\n if (!spec) return;\n el.title = spec.title || spec.kind || '';\n const icon = spec.kind ? this.ctx?.taxonomy?.meta?.(spec.kind)?.icon : null;\n const glyph = el.querySelector('.tab-icon');\n if (glyph && icon) glyph.textContent = icon;\n else if (glyph && !icon) glyph.hidden = true;\n });\n }\n\n /** Which tab an element in the top strip is, by DOM position. Position\n * rather than the `data-path` key because the key is only ever the index\n * and reading it back would be a second, parallel answer to the same\n * question. */\n _topTabIndex(hostEl, tabEl) {\n return Array.prototype.indexOf.call(hostEl.querySelectorAll('.tab'), tabEl);\n }\n\n /** The callbacks `NotebookTabBar` calls. Everything the component offers\n * that a tile tab cannot honour is deliberately absent rather than stubbed\n * \u2014 `onRename` is refused by the `builtin://` key, and the rest\n * (`onDuplicate`, `onSplitRight`, `onRevealInExplorer`, `onCloseAll`, \u2026)\n * are only ever reached from the context menu this renderer suppresses.\n *\n * `onDropFromOtherPane` IS NOW WIRED, AND THIS IS THE RECORD OF WHY IT WAS\n * NOT. The refusal read: *\"its payload is the dragged tab's key alone,\n * which carries no source-leaf identity, and `TileTree` has no\n * move-a-tab-between-leaves operation to receive it. Wiring it would need\n * both, and both are tree changes.\"* Both were true and C33 built both.\n * `TileTree.moveTabToLeaf` is the tree operation; `TileRenderer._tabDrag`\n * is the source identity \u2014 held on the renderer rather than in the payload\n * because HTML5's protected mode makes the payload unreadable at the only\n * moment it would be needed (see `TILE_TAB_MIME`). The payload handed to\n * the callback is therefore still ignored, exactly as the refusal said it\n * would have to be.\n *\n * A DROP ON A FOREIGN STRIP APPENDS. `NotebookTabBar` hands the callback a\n * key and no event, so there is no pointer position to derive a slot from\n * \u2014 and inventing one for this strip and not for the bottom one would give\n * the two layouts different answers to the same gesture. \"Add this tab to\n * that tile\" is what was asked for; where it sits in the strip is a\n * reorder away, in the mechanism that already does reorders. */\n _topTabCallbacks(leafId) {\n return {\n // C33. `DragReorder` calls this through `NotebookTabBar`, which\n // sets its own `application/x-ecosim-tab` first and unchanged \u2014 so\n // an editor pane sharing the page cannot notice that tiles are\n // dragging tabs too.\n onDragStart: (ev, key, item) => {\n const idx = _tabKeyIndex(key);\n if (idx < 0) return;\n this._beginTabDrag(ev, leafId, idx, item || null);\n },\n // What lets THIS strip admit a tab dragged out of another tile's\n // strip: `#isExternalTabDrag` tests `TAB_MIME` plus whatever the\n // host names here, and defers while its own reorder is running.\n externalTabMimes: [TILE_TAB_MIME],\n onDropFromOtherPane: () => {\n const src = this._tabDrag;\n this._onTabDragEnd();\n if (!src || src.leafId === leafId) return;\n this.ctx.onLeafTabAction?.(src.leafId, 'drop-into', {\n idx: src.idx,\n target: { leafId, mode: 'tab', toIdx: -1 },\n });\n },\n onActivate: (key) => {\n const idx = _tabKeyIndex(key);\n if (idx >= 0) this.ctx.onLeafTabAction?.(leafId, 'switch', { idx });\n },\n onClose: (key) => {\n const idx = _tabKeyIndex(key);\n if (idx >= 0) this.ctx.onLeafTabAction?.(leafId, 'close', { idx });\n },\n // DRAG-TO-REORDER ARRIVES AS A PERMUTATION, and the tree moves ONE\n // tab at a time (`TileTree.moveLeafTab(leafId, from, to)`). They\n // reconcile because a drag only ever moves one element: every other\n // key shifts by exactly one place, so the element that travelled\n // furthest between the two orders IS the one that was dragged.\n onReorder: (order) => {\n const leaf = this.tree.get(leafId);\n const count = (leaf?.tabs || []).length;\n if (!Array.isArray(order) || order.length !== count) return;\n let from = -1;\n let to = -1;\n let furthest = 0;\n order.forEach((key, newIdx) => {\n const oldIdx = _tabKeyIndex(key);\n if (oldIdx < 0) return;\n const travelled = Math.abs(newIdx - oldIdx);\n if (travelled > furthest) {\n furthest = travelled;\n from = oldIdx;\n to = newIdx;\n }\n });\n if (from < 0 || from === to) return;\n this.ctx.onLeafTabAction?.(leafId, 'move', { from, to });\n },\n };\n }\n\n /** The `bottom` layout \u2014 the framework's own strip, unchanged. Hamburger\n * button at the start, then one trapezoid-shaped tab per stored tab spec. */\n _renderBottomTabBar(leaf, entry, tabs) {\n const bar = entry.tabBarEl;\n const activeIdx = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));\n bar.innerHTML = `\n <button type=\"button\" class=\"twm-leaf__tab-hamburger\"\n data-action=\"tab-menu\"\n title=\"Show open tabs\" aria-label=\"Show open tabs\">\n <span class=\"material-symbols-outlined\">menu</span>\n </button>\n <ol class=\"twm-leaf__tabs\" role=\"tablist\">\n ${tabs.map((t, i) => `\n <li class=\"twm-leaf__tab${i === activeIdx ? ' twm-leaf__tab--on' : ''}\"\n role=\"tab\" data-tab-idx=\"${i}\"\n title=\"${_esc(t.title || t.kind)}\"\n draggable=\"true\">\n <span class=\"twm-leaf__tab-label\">${_esc(t.title || t.kind)}</span>\n <button type=\"button\" class=\"twm-leaf__tab-close\"\n data-action=\"tab-close\" data-tab-idx=\"${i}\"\n title=\"Close this tab\"\n aria-label=\"Close tab\">\n <span class=\"material-symbols-outlined\">close</span>\n </button>\n </li>\n `).join('')}\n </ol>\n `;\n\n // Tab activation. Click anywhere on a tab (except its \u00D7 button)\n // switches to it; the \u00D7 closes it.\n bar.querySelectorAll('.twm-leaf__tab').forEach((li) => {\n li.addEventListener('click', (ev) => {\n if (ev.target.closest('[data-action=\"tab-close\"]')) return;\n const idx = Number(li.dataset.tabIdx);\n this.ctx.onLeafTabAction?.(leaf.id, 'switch', { idx });\n });\n // Right-click \u2192 browser-style tab context menu (close, close\n // others, close right / left). The WM mounts the actual menu\n // \u2014 we just hand it the (idx, x, y) anchor.\n li.addEventListener('contextmenu', (ev) => {\n ev.preventDefault();\n ev.stopPropagation();\n const idx = Number(li.dataset.tabIdx);\n this.ctx.onLeafTabAction?.(leaf.id, 'menu',\n { idx, x: ev.clientX, y: ev.clientY });\n });\n });\n bar.querySelectorAll('[data-action=\"tab-close\"]').forEach((btn) => {\n btn.addEventListener('click', (ev) => {\n ev.preventDefault();\n ev.stopPropagation();\n const idx = Number(btn.dataset.tabIdx);\n this.ctx.onLeafTabAction?.(leaf.id, 'close', { idx });\n });\n });\n bar.querySelector('[data-action=\"tab-menu\"]')\n ?.addEventListener('click', (ev) => {\n ev.preventDefault();\n const r = ev.currentTarget.getBoundingClientRect();\n this.ctx.onLeafTabAction?.(leaf.id, 'open-menu',\n { x: r.left, y: r.top });\n });\n\n // Drag-to-reorder. Stash the source idx in dataTransfer; the\n // drop target reads it and emits a 'move' action. We don't use\n // HTML5 setData('text/plain') for the live state because the\n // dragstart/drop pair runs entirely within the same tab list.\n //\n // C33. \u2026WHICH IS EXACTLY WHY `dragFromIdx` IS NOT ENOUGH ANY MORE. It\n // is a closure built per leaf render, so leaf B's handlers cannot see\n // that leaf A started a drag. It stays, unchanged, as the in-strip\n // reorder's own state \u2014 the gesture it was written for still begins and\n // ends in one list \u2014 and the cross-tile identity is taken on the\n // renderer beside it. Two facts, two homes, and neither one guessing.\n let dragFromIdx = null;\n bar.querySelectorAll('.twm-leaf__tab').forEach((li) => {\n li.addEventListener('dragstart', (ev) => {\n dragFromIdx = Number(li.dataset.tabIdx);\n ev.dataTransfer.effectAllowed = 'move';\n // Firefox refuses to start a drag unless setData is\n // called \u2014 supply a placeholder string.\n try { ev.dataTransfer.setData('text/plain', String(dragFromIdx)); }\n catch {}\n this._beginTabDrag(ev, leaf.id, dragFromIdx, li);\n });\n // C33. THE DRAGGED TAB NOW LOOKS DRAGGED. `DragReorder` has put\n // `.dragging` on the top strip's tab since it was written; this\n // strip's hand-rolled reorder set no class at all, so a drag here\n // looked exactly like a click that did nothing until the tab\n // arrived somewhere else. One rule serves both strips.\n li.addEventListener('dragend', () => { dragFromIdx = null; this._onTabDragEnd(); });\n li.addEventListener('dragover', (ev) => {\n if (dragFromIdx == null) return;\n ev.preventDefault();\n ev.dataTransfer.dropEffect = 'move';\n });\n li.addEventListener('drop', (ev) => {\n if (dragFromIdx == null) return;\n ev.preventDefault();\n const toIdx = Number(li.dataset.tabIdx);\n if (toIdx !== dragFromIdx) {\n this.ctx.onLeafTabAction?.(leaf.id, 'move',\n { from: dragFromIdx, to: toIdx });\n }\n dragFromIdx = null;\n });\n });\n\n // \u2500\u2500 C33. AND A TAB FROM ANOTHER TILE, DROPPED ON THIS STRIP \u2500\u2500\u2500\u2500\u2500\u2500\n //\n // The container level, not the tab level, because the useful target is\n // \"this strip\" rather than \"this tab\": the per-tab handlers above\n // return without `preventDefault` when `dragFromIdx` is null, which is\n // always true of a foreign drag, so the event reaches here by bubbling\n // and the two never contend.\n //\n // The gate is the same shape as `NotebookTabBar.#isExternalTabDrag`:\n // the type-only marker, plus a source that is not this leaf. The\n // `dragleave` is guarded with `contains(relatedTarget)` because\n // `dragleave` fires every time the pointer crosses into a CHILD, and\n // without the guard the strip would unhighlight itself the moment the\n // pointer touched a tab inside it \u2014 the same fix `editor_pane.js` and\n // `notebook_tab_bar.js` already carry.\n // BOUND ONCE PER BAR ELEMENT, not once per render. `_renderBottomTabBar`\n // rewrites `bar.innerHTML` \u2014 which takes the per-tab listeners with the\n // `<li>`s they were on \u2014 but the BAR survives, and `setTabLayout` calls\n // straight back in on the same element. Without this flag a user who\n // switched layout twice would get three copies of the drop handler and\n // three `drop-into` actions from one release.\n const foreign = (ev) => !!this._tabDrag\n && this._tabDrag.leafId !== leaf.id\n && _isTileTabDrag(ev);\n if (bar.__twmBarDropBound) return;\n bar.__twmBarDropBound = true;\n bar.addEventListener('dragover', (ev) => {\n if (!foreign(ev)) return;\n ev.preventDefault();\n try { ev.dataTransfer.dropEffect = 'move'; } catch {}\n // The tile zone and the strip zone must never be armed at once, and\n // this is the TILE zone that goes \u2014 never `_clearTabDropZone`,\n // which would take the class added on the next line straight back\n // off. See `_onTabDragOver`: this handler runs FIRST (the bar is a\n // descendant of the root, and both listeners are on the bubble\n // phase), so anything it paints has to survive what runs after it.\n this._clearTileDropZone();\n bar.classList.add('twm-leaf__tabbar--drop-target');\n });\n bar.addEventListener('dragleave', (ev) => {\n if (!bar.contains(ev.relatedTarget)) {\n bar.classList.remove('twm-leaf__tabbar--drop-target');\n }\n });\n bar.addEventListener('drop', (ev) => {\n if (!foreign(ev)) return;\n ev.preventDefault();\n bar.classList.remove('twm-leaf__tabbar--drop-target');\n const src = this._tabDrag;\n this._onTabDragEnd();\n // APPEND, like the top strip's drop does, and for the same reason:\n // one gesture must not mean two things depending on where the user\n // draws their tabs. See `_topTabCallbacks`.\n this.ctx.onLeafTabAction?.(src.leafId, 'drop-into', {\n idx: src.idx,\n target: { leafId: leaf.id, mode: 'tab', toIdx: -1 },\n });\n });\n }\n\n // \u2550\u2550 C33. THE TAB DRAG \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 /** Take the identity of the tab now being carried, and mark it.\n *\n * `seedPlainText` is for the CHROME source only. The two strips already\n * set `text/plain` themselves \u2014 `DragReorder._start` writes the reorder\n * key, the bottom strip writes the index \u2014 and overwriting either would\n * hand `NotebookTabBar.#onStripDrop`'s `getData(TAB_MIME) ||\n * getData('text/plain')` fallback a number where it expects a key. The\n * chrome has no such writer and Firefox refuses to begin a drag with an\n * empty `dataTransfer`, so it supplies its own. */\n _beginTabDrag(ev, leafId, idx, el, { seedPlainText = false } = {}) {\n this._tabDrag = { leafId, idx, el: el || null };\n this._tabDropProbe = null;\n try {\n ev.dataTransfer.effectAllowed = 'move';\n ev.dataTransfer.setData(TILE_TAB_MIME, '1');\n if (seedPlainText) ev.dataTransfer.setData('text/plain', String(idx));\n } catch { /* a synthetic event, or a dataTransfer in protected mode */ }\n el?.classList?.add('dragging');\n }\n\n /**\n * Arm \u2014 or refuse \u2014 the tile under the pointer.\n *\n * NOTHING HERE MAY RENDER. `render()` clears the root, which detaches the\n * element the browser is dragging, and the browser cancels the gesture the\n * moment that happens. The DOM afterwards reads perfectly correct, which is\n * what makes this failure so hard to see; it is the drag-and-drop cousin of\n * the `mousedown`-repaint defect recorded five times against the taskbar.\n *\n * `preventDefault()` is not decoration either: without it the browser\n * refuses the drop outright and `drop` never fires, which reads exactly\n * like a broken handler.\n */\n _onTabDragOver(e) {\n const src = this._tabDrag;\n if (!src || !_isTileTabDrag(e)) return;\n // A STRIP OWNS ITS OWN DROPS. Inside a tab bar the question is a\n // position in a list, not a zone of a tile, and both strips answer it\n // themselves \u2014 so this hands the event on rather than arming a zone\n // behind them.\n //\n // `_clearTileDropZone` and NOT `_clearTabDropZone`: the strip's handler\n // has ALREADY RUN by the time this one does \u2014 it is bound on a\n // descendant of the root and both are bubble-phase \u2014 so clearing the\n // strip classes here would remove the highlight it just painted, on\n // every `dragover`, and the strip would arm and disarm invisibly for\n // the whole gesture.\n if (e.target?.closest?.('.twm-leaf__tabbar')) { this._clearTileDropZone(); return; }\n const probe = this.ctx.wm?.tabDropProbe?.(e, { sourceLeafId: src.leafId }) || null;\n this._tabDropProbe = probe;\n if (!probe) { this._clearTabDropZone(); return; }\n e.preventDefault();\n try { e.dataTransfer.dropEffect = 'move'; } catch {}\n for (const [leafId, entry] of this._leafCache) {\n entry.wrapEl.classList.toggle('twm-leaf--drop-target', leafId === probe.leafId);\n }\n this._showTabDropPreview(probe.rect);\n }\n\n /** Release. The zone that was ARMED is the zone that runs \u2014 the stashed\n * probe rather than a fresh one \u2014 because C15's rule is that the rectangle\n * drawn during the drag is the rectangle the drop delivers, and a pointer\n * one pixel outside the band at release must not quietly mean something\n * else. */\n _onTabDrop(e) {\n const src = this._tabDrag;\n const probe = this._tabDropProbe;\n if (!src || !_isTileTabDrag(e)) return;\n if (e.target?.closest?.('.twm-leaf__tabbar')) return;\n e.preventDefault();\n this._onTabDragEnd();\n if (!probe) return;\n this.ctx.onLeafTabAction?.(src.leafId, 'drop-into', {\n idx: src.idx,\n target: {\n leafId: probe.leafId,\n mode: probe.mode,\n // The same derivation `_snapCommit` applies to a window drop\n // (`wm.js`, the `_dock` literal) \u2014 one reading of a side, so a\n // tab and a window cannot land on opposite halves of one edge.\n dir: (probe.side === 'left' || probe.side === 'right') ? 'h' : 'v',\n before: (probe.side === 'left' || probe.side === 'top'),\n toIdx: -1,\n },\n });\n }\n\n /** Leaving the root entirely disarms, and only that. The drag is still\n * live \u2014 it may come back \u2014 so `_tabDrag` survives and only the painting\n * goes. */\n _onTabDragLeave(e) {\n if (!this._tabDrag) return;\n if (this.root.contains(e.relatedTarget)) return;\n this._clearTabDropZone();\n this._tabDropProbe = null;\n }\n\n /** The only guaranteed end of a drag. Escape produces this and no `drop`;\n * so does a release over a target that refused. Idempotent, because the\n * drop path calls it too and `dragend` still arrives afterwards. */\n _onTabDragEnd() {\n this._tabDrag?.el?.classList?.remove('dragging');\n this._tabDrag = null;\n this._tabDropProbe = null;\n this._clearTabDropZone();\n }\n\n /** The TILE zone only \u2014 the outlined pane and the preview rectangle. Split\n * out from the whole because a strip that has just armed itself must not\n * be disarmed by the root handler running behind it. */\n _clearTileDropZone() {\n for (const [, entry] of this._leafCache) {\n entry.wrapEl.classList.remove('twm-leaf--drop-target');\n }\n this._clearTabDropPreview();\n }\n\n /** Everything: the tile zone and both strips'. The end of a gesture, where\n * nothing may be left painted. */\n _clearTabDropZone() {\n this._clearTileDropZone();\n for (const [, entry] of this._leafCache) {\n entry.tabBarEl?.classList?.remove('twm-leaf__tabbar--drop-target');\n }\n }\n\n /** The rectangle a release would fill.\n *\n * IT IS THE WINDOW DROP'S OWN PREVIEW ELEMENT \u2014 same two classes, same\n * stylesheet rules (`css/base.css`, `.twm-snap-preview`) \u2014 so a tab drop\n * and a window drop cannot come to disagree about what a drop looks like.\n * `--viewport` is what makes `position: fixed` apply, and that is required\n * rather than cosmetic: the rectangle came from `getBoundingClientRect` on\n * a leaf, which speaks viewport pixels.\n *\n * Parented to `document.body` and not to the root, for R13's reason:\n * `render()`'s `innerHTML = ''` takes every direct child of the root, and\n * a repaint during a drag needs nothing more exotic than the drag itself. */\n _showTabDropPreview(rect) {\n if (!rect) { this._clearTabDropPreview(); return; }\n if (!this._tabDropPreviewEl) {\n const el = document.createElement('div');\n el.className = 'twm-snap-preview twm-snap-preview--viewport';\n el.setAttribute('aria-hidden', 'true');\n this._tabDropPreviewEl = el;\n }\n const el = this._tabDropPreviewEl;\n Object.assign(el.style, {\n left: `${rect.left ?? rect.x}px`, top: `${rect.top ?? rect.y}px`,\n width: `${rect.width}px`, height: `${rect.height}px`,\n });\n if (el.parentNode !== document.body) document.body.appendChild(el);\n }\n\n _clearTabDropPreview() {\n this._tabDropPreviewEl?.remove();\n }\n\n _updateFocusClasses() {\n const focused = this.tree.focusedLeafId;\n for (const [leafId, entry] of this._leafCache) {\n entry.wrapEl.classList.toggle('twm-leaf--focused', leafId === focused);\n }\n }\n\n /** The `.twm-leaf` wrap element for a leaf id, or null if it isn't\n * currently rendered. Used by the panel-key router to resolve which\n * DOM subtree owns keyboard input. */\n leafEl(leafId) {\n return this._leafCache.get(leafId)?.wrapEl || null;\n }\n\n /** C20, extended. The `chrome` veto object the mounted content declared \u2014\n * `{ promote: false, close: false }` \u2014 or null when the leaf is not\n * rendered or its content declared nothing.\n *\n * IT EXISTS BECAUSE A VETO PAINTED ON A BUTTON IS NOT A VETO. C20 landed\n * as `_vetoStructuralActions`, which removes the button from this strip \u2014\n * and a removed button is only the door the CONTENT can see. The verb has\n * three other doors: the tile's right-click menu (`shell.js`'s \"Float this\n * pane as a window\"), the chrome pull-down, and the chrome's double-click.\n * All three reach `WindowManager.floatPane` without passing this file, so\n * a master tile that declared itself unfloatable was floated by any of\n * them \u2014 reproduced: the ground pane floats, every window standing on it\n * is force-closed, and the pane is re-seeded WITHOUT its `canvas` prop.\n *\n * So the WM asks the renderer what the content said, and enforces it in\n * `_floatableLeaf` where every door already converges. The renderer stays\n * the only place that knows what was mounted; the WM stays the only place\n * that decides whether a verb runs. `closeFocused` now reads `close` the\n * same way, for the same reason and after the same defect: Alt+W, the tile\n * context menu and the tab strip's \u00D7 all closed a pane whose own button\n * was greyed out with a tooltip saying it could not be.\n *\n * \u2550\u2550 THIS RETURNS THE FACTORY'S LIVE OBJECT, AND THAT IS A CONTRACT \u2550\u2550\n *\n * Not a copy and not a snapshot. `chrome` is READ ONCE, at mount \u2014 a\n * repaint of a cached leaf re-reads only the title and the glyph \u2014 so a\n * veto whose ANSWER CHANGES over the life of the tile must be kept up to\n * date by the content that stated it, by mutating the object it returned.\n * Tables' ground pane is exactly that case: its close is refused only\n * while it is the last content pane, and it re-syncs on `wm:changed`.\n * Repainting the button alone is not enough now that a verb consults this\n * \u2014 a stale `{disabled: true}` refuses a close every affordance on screen\n * says is available, which is the same class of lie as a dead control. */\n leafChrome(leafId) {\n return this._leafCache.get(leafId)?.content?.chrome || null;\n }\n\n // \u2500\u2500 Drag-resize \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 _onMouseDown(e) {\n const splitter = e.target.closest('.twm-splitter');\n if (!splitter) return;\n e.preventDefault();\n const splitId = splitter.dataset.splitId;\n const slotIdx = Number(splitter.dataset.slotIdx);\n const split = this.tree.get(splitId);\n if (!split) return;\n const containerEl = splitter.parentElement;\n const horizontal = split.dir === 'h';\n const rect = containerEl.getBoundingClientRect();\n const totalPx = horizontal ? rect.width : rect.height;\n this._drag = {\n splitId, slotIdx, horizontal, totalPx,\n startSizes: split.sizes.slice(),\n startPos: horizontal ? e.clientX : e.clientY,\n split,\n };\n document.body.classList.add('twm-dragging');\n const move = (ev) => this._onMouseMove(ev);\n const up = (ev) => {\n this._onMouseUp(ev);\n document.removeEventListener('mousemove', move);\n document.removeEventListener('mouseup', up);\n };\n document.addEventListener('mousemove', move);\n document.addEventListener('mouseup', up);\n }\n\n _onMouseMove(e) {\n if (!this._drag) return;\n const { horizontal, totalPx, startSizes, startPos, slotIdx, split } = this._drag;\n const delta = (horizontal ? e.clientX : e.clientY) - startPos;\n const totalSize = startSizes.reduce((a, b) => a + b, 0);\n const px = totalPx - SPLITTER_PX * (split.children.length - 1);\n if (px <= 0) return;\n const deltaFrac = (delta / px) * totalSize;\n const minFrac = totalSize * 0.05;\n const newSizes = startSizes.slice();\n newSizes[slotIdx] = Math.max(minFrac, startSizes[slotIdx] + deltaFrac);\n newSizes[slotIdx + 1] = Math.max(minFrac, startSizes[slotIdx + 1] - deltaFrac);\n // Snap back to total to avoid drift.\n const sum = newSizes.reduce((a, b) => a + b, 0);\n const scale = totalSize / sum;\n for (let i = 0; i < newSizes.length; i++) newSizes[i] *= scale;\n this.tree.setSplitSizes(split.id, newSizes);\n // Cheap update: just rewrite flex on the slots.\n const containerEl = document.querySelector(`.twm-split[data-split-id=\"${split.id}\"]`);\n if (!containerEl) return;\n const slots = containerEl.querySelectorAll(':scope > .twm-slot');\n slots.forEach((slot, i) => {\n const frac = newSizes[i] / totalSize;\n slot.style.flex = `${frac} ${frac} 0`;\n });\n }\n\n _onMouseUp() {\n this._drag = null;\n document.body.classList.remove('twm-dragging');\n }\n}\n\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n}\n\n/**\n * C19. Decide, and paint, the glyph left of a tile's title.\n *\n * TWO SOURCES, IN THIS ORDER, and the order is the whole design. The content\n * factory's `content.icon` wins because only the content knows what this\n * PARTICULAR thing is \u2014 one table has a cog, the next has a calendar, and the\n * taxonomy has no opinion about either. Everything else falls back to\n * `taxonomy.meta(kind).icon`, which is the glyph the top-nav, the command\n * palette and the breadcrumb already draw for that kind \u2014 so an embedder who\n * changes nothing at all still gets a tile chrome that agrees with the three\n * surfaces around it, and an embedder who has never returned an `icon` from a\n * factory gets one for free.\n *\n * A kind with neither \u2014 a `panel:*` tile, whose kinds are the framework's own\n * and are deliberately not in anybody's taxonomy \u2014 is HIDDEN rather than blank.\n * The chrome is a flex row with a gap; an empty span would still take its gap\n * and push the title off the left margin by a glyph's worth of nothing.\n */\nfunction _paintLeafIcon(iconEl, leaf, content, ctx) {\n if (!iconEl) return;\n const kind = leaf?.content?.kind;\n const name = content?.icon || (kind ? ctx?.taxonomy?.meta?.(kind)?.icon : null);\n iconEl.textContent = name || '';\n iconEl.hidden = !name;\n}\n\n/**\n * C18. Paint a content factory's own chrome buttons into its tile's strip.\n *\n * The framework owns the tile and its verbs; the embedder owns the content and\n * ITS verbs, and until now had nowhere to put them. Tables ended up hanging a\n * table's settings and its pop-out off the grid's own toolbar, one row further\n * down, where they read as things you do to the ROWS.\n *\n * Deliberately not a registry and not a config: the factory returns them from\n * the same object it already returns `title` and `destroy` on, so they arrive\n * with the mount and leave with it.\n */\nfunction _paintContentActions(hostEl, specs) {\n hostEl.innerHTML = '';\n if (!Array.isArray(specs) || specs.length === 0) {\n hostEl.hidden = true;\n return;\n }\n hostEl.hidden = false;\n for (const spec of specs) {\n if (!spec || !spec.icon) continue;\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'twm-leaf__btn';\n btn.title = spec.title || '';\n btn.setAttribute('aria-label', spec.title || '');\n btn.innerHTML =\n `<span class=\"material-symbols-outlined\">${spec.icon}</span>`;\n // `stopPropagation` so a click does not also reach the leaf's own\n // mousedown, which focuses the tile and then parks focus inside the\n // body \u2014 stealing it back from whatever the action just opened.\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n try { spec.onClick?.(); } catch (err) { console.error('[tile] action threw', err); }\n });\n hostEl.appendChild(btn);\n }\n}\n\n/**\n * C20. Let a content factory say a structural verb does not apply to it.\n *\n * Three answers, and the difference between the last two is a real one:\n *\n * (absent) / true the verb applies. The default, so an embedder\n * that says nothing gets every button as before.\n * `false` the verb is MEANINGLESS here \u2014 remove it. A\n * panel tile cannot be floated; there is nothing to\n * explain, and a permanently dead control is\n * clutter that teaches nothing.\n * `{ disabled, title }` the verb applies to this KIND of pane but not\n * right now. Keep the button, grey it, and say\n * why \u2014 \"you may close a pane, but not the last\n * one\" is a rule the user should be able to\n * discover by pointing at the thing it governs,\n * rather than by noticing that a button they\n * remember is missing.\n */\nfunction _vetoStructuralActions(hostEl, chrome) {\n if (!chrome) return;\n for (const [action, rule] of Object.entries(chrome)) {\n const btn = hostEl.querySelector(`[data-action=\"${action}\"]`);\n if (!btn) continue;\n if (rule === false) { btn.remove(); continue; }\n if (rule && typeof rule === 'object' && rule.disabled) {\n btn.disabled = true;\n btn.setAttribute('aria-disabled', 'true');\n btn.classList.add('twm-leaf__btn--disabled');\n if (rule.title) btn.title = rule.title;\n }\n }\n}\n", "/**\n * tab_strip.js \u2014 ONE tab strip, over one ordered list of tab specs.\n *\n * \u2500\u2500 Why this is a module and not a method \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 * C22 taught `tile_renderer.js` to draw a leaf's tabs with `NotebookTabBar`,\n * the editor's strip, and that code is not really about leaves: it is about\n * translating between a component whose vocabulary is FILE PATHS and a list of\n * `{kind, props, title}` that has no identity of its own. R8 needs exactly that\n * translation a second time \u2014 a pane floated into a window takes its tab strip\n * with it, because the pane IS its tabs and a window showing only one of them\n * is not the pane \u2014 and the strip in the window is not attached to a leaf at\n * all. Its tabs live in the WM's window record.\n *\n * So the translation moves here, where neither side owns it, and both sides\n * reach it the same way. A second implementation of it in `wm.js` would be a\n * second copy of the four corrections below, and they would drift apart\n * silently: none of them is a syntax error and none throws at import.\n *\n * \u2500\u2500 The four corrections, and why each one exists \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 * 1. IDENTITY. `NotebookTabBar` keys everything \u2014 its reorder, its active\n * comparison, its rename selector \u2014 on a file path. A tab list has no ids\n * (the tree gives tabs none and neither does a window record), so the\n * INDEX serves, and it is the one identity such a list is guaranteed to\n * have. The `builtin://` prefix is not decoration: `#beginInlineRename`\n * refuses to start on a path carrying it (`notebook_tab_bar.js:402`),\n * which is what a tab needs \u2014 double-click rename would rename a TAB, and\n * a tab is a view of an entity whose name lives somewhere neither caller\n * can reach.\n * 2. THE GLYPH. The component picks one out of a static map of FILE kinds.\n * Ours are the embedder's kinds and the taxonomy already answers for them.\n * `setFileTypeIcons()` is not the way: it is STATIC and REPLACES the whole\n * map, so a page that also uses the editor would find its own file icons\n * deleted by whichever of the two rendered last.\n * 3. THE TOOLTIP. Its title attribute is the file PATH \u2014 ours would read\n * `builtin://tab/3`, i.e. a tab advertising its own array index.\n * 4. ONE TAB CONTEXT MENU IN THE APPLICATION, not two. The component opens\n * its own `.nb-context-menu` whose verbs are a notebook's (rename,\n * duplicate, reveal in explorer). The caller has a menu already; the\n * component's is suppressed in the CAPTURE phase, because its handler is\n * bound on each tab element in the bubble phase and stopping the event on\n * the way DOWN is what keeps its menu from opening at all.\n *\n * The class names touched here are the component's published contract, stated\n * in its own header comment.\n */\n\nimport { NotebookTabBar } from '../editor/notebook_tab_bar.js';\n\n/** A tab's identity, for a component that only understands paths. See (1). */\nexport function tabKey(idx) { return `builtin://tab/${idx}`; }\n\n/** \u2026and back. `-1` for anything that is not one of ours. */\nexport function tabKeyIndex(key) {\n const n = Number(String(key ?? '').replace('builtin://tab/', ''));\n return Number.isInteger(n) && n >= 0 ? n : -1;\n}\n\n/**\n * DRAG-TO-REORDER ARRIVES AS A PERMUTATION, and every tab model here moves ONE\n * tab at a time (`TileTree.moveLeafTab(leafId, from, to)`, and the window\n * record's own splice). They reconcile because a drag only ever moves one\n * element: every other key shifts by exactly one place, so the element that\n * travelled furthest between the two orders IS the one that was dragged.\n *\n * @returns {{from: number, to: number}|null} null when the order is not a\n * permutation of `count` tabs, or when nothing actually moved.\n */\nexport function reorderToMove(order, count) {\n if (!Array.isArray(order) || order.length !== count) return null;\n let from = -1;\n let to = -1;\n let furthest = 0;\n order.forEach((key, newIdx) => {\n const oldIdx = tabKeyIndex(key);\n if (oldIdx < 0) return;\n const travelled = Math.abs(newIdx - oldIdx);\n if (travelled > furthest) { furthest = travelled; from = oldIdx; to = newIdx; }\n });\n if (from < 0 || from === to) return null;\n return { from, to };\n}\n\n/**\n * Mount a tab strip into `hostEl` and drive it from a plain tab list.\n *\n * Every gesture leaves through ONE callback with the SAME vocabulary the tile\n * renderer's two layouts already use \u2014 `switch` / `close` / `move` / `menu` \u2014\n * so a caller wires one function and the WM's tree mutations, its persistence\n * and its change notifications stay reachable by one path from every strip in\n * the application.\n *\n * @param {object} cfg\n * @param {HTMLElement} cfg.hostEl the bar element. The strip mounts into a\n * CHILD of it, because `mount()` assigns `container.className = 'tabs\n * notebook-tabs'` (`notebook_tab_bar.js:61`) \u2014 handing it the bar would take\n * that class, and with it the bar's height, background and `--hidden`, off\n * the element the caller still controls.\n * @param {object} [cfg.taxonomy] asked for each kind's glyph. Absent \u21D2 the\n * glyph is hidden rather than wrong.\n * @param {(action: 'switch'|'close'|'move'|'menu', data: object) => void} cfg.onAction\n * @returns {{update: (tabs: object[], activeIdx: number) => void, dispose: () => void}}\n */\nexport function createTabStrip({ hostEl, taxonomy = null, onAction }) {\n const host = document.createElement('div');\n hostEl.appendChild(host);\n const strip = new NotebookTabBar();\n let tabs = [];\n\n strip.mount(host, {\n onActivate: (key) => {\n const idx = tabKeyIndex(key);\n if (idx >= 0) onAction?.('switch', { idx });\n },\n onClose: (key) => {\n const idx = tabKeyIndex(key);\n if (idx >= 0) onAction?.('close', { idx });\n },\n onReorder: (order) => {\n const move = reorderToMove(order, tabs.length);\n if (move) onAction?.('move', move);\n },\n // Everything else the component offers that a tab here cannot honour is\n // deliberately absent rather than stubbed \u2014 `onRename` is refused by the\n // `builtin://` key, and the rest (`onDuplicate`, `onSplitRight`,\n // `onRevealInExplorer`, `onCloseAll`, \u2026) are only ever reached from the\n // context menu suppressed below.\n });\n\n // See (4). Capture phase, on the container.\n const onContextMenu = (ev) => {\n const tabEl = ev.target.closest?.('.tab');\n if (!tabEl) return;\n ev.preventDefault();\n ev.stopPropagation();\n const idx = Array.prototype.indexOf.call(host.querySelectorAll('.tab'), tabEl);\n if (idx < 0) return;\n onAction?.('menu', { idx, x: ev.clientX, y: ev.clientY });\n };\n host.addEventListener('contextmenu', onContextMenu, true);\n\n /** See (2) and (3) \u2014 one pass over the DOM the component just wrote. */\n const repaint = () => {\n host.querySelectorAll('.tab').forEach((el, i) => {\n const spec = tabs[i];\n if (!spec) return;\n el.title = spec.title || spec.kind || '';\n const icon = spec.kind ? taxonomy?.meta?.(spec.kind)?.icon : null;\n const glyph = el.querySelector('.tab-icon');\n if (!glyph) return;\n if (icon) { glyph.textContent = icon; glyph.hidden = false; }\n else glyph.hidden = true;\n });\n };\n\n return {\n /** @param {Array<{kind: string, props?: object, title?: string, dirty?: boolean}>} next */\n update(next, activeIdx) {\n tabs = Array.isArray(next) ? next : [];\n const active = Math.max(0, Math.min(tabs.length - 1, activeIdx || 0));\n strip.update(\n tabs.map((t, i) => ({\n filePath: tabKey(i),\n label: t.title || t.kind || '',\n fileType: t.kind || 'unknown',\n // Nothing sets `dirty` on a tab spec today, so the dot is\n // never drawn. Read anyway, because the day a tab can say it\n // holds unsaved work this is where it says it, and the\n // alternative is a second place to remember.\n isDirty: !!t.dirty,\n })),\n tabKey(active));\n repaint();\n },\n dispose() {\n host.removeEventListener('contextmenu', onContextMenu, true);\n try { strip.dispose(); } catch (err) {\n console.error('[tab-strip] dispose threw', err);\n }\n host.remove();\n },\n };\n}\n", "/**\n * wm.js \u2014 WindowManager facade. Owns the desktop manager, the active\n * tree's renderer, the managed-window stack, and the panel-tile state.\n * Exposes a small imperative API consumed by the palette, keymap, top\n * bar, bottom bar, and nav panel.\n *\n * wm.openInPrimary(kind, props) load a content into the primary tile\n * wm.split(dir) split the focused tile\n * wm.closeFocused() close the focused tile\n * wm.focusDir(dir) / wm.moveFocused(dir)\n * wm.toggleManagedFocused() tile <-> managed window\n * wm.togglePanel(side) left | right | bottom\n * wm.isPanelOpen(side) boolean\n * wm.switchDesktop(idx) / wm.addDesktop() / wm.moveFocusedToDesktop(idx)\n */\n\nimport { DesktopManager, saveDesktops, loadDesktops } from './desktops.js';\nimport { TileRenderer } from './tile_renderer.js';\nimport { makeLeaf } from './tile_tree.js';\nimport { PLACEHOLDER_KIND } from './content_registry.js';\nimport { installPanelKeyRouter } from './panel_keys.js';\nimport { ManagedWindow } from '../ui/components/managed_window.js';\nimport { showContextMenu } from '../ui/components/context_menu.js';\nimport { createTabStrip } from './tab_strip.js';\n\nconst PANEL_KINDS = new Set(['panel:left', 'panel:right', 'panel:bottom']);\n\nconst PANEL_TITLES = {\n left: 'Navigator',\n right: 'Inspector',\n bottom: 'Console',\n};\n\nexport class WindowManager {\n constructor({ rootEl, api, ctx, onChange, eventBus, host, taxonomy, events, content,\n panelDefaults = null, snapPromotion = false,\n promoteInPlace = false, tabLayout = null }) {\n if (!taxonomy) throw new Error('WindowManager: a taxonomy is required');\n if (!content || typeof content.mount !== 'function') {\n throw new Error('WindowManager: a content registry is required '\n + '(createContentRegistry / createShell owns it)');\n }\n this.rootEl = rootEl;\n // The shell-scoped content registry (kind -> factory). NOT a module\n // singleton: two shells on one page must not share a kind table, and\n // nothing may register a kind after the first mount.\n this.content = content;\n this.api = api || null;\n // The host port. `this.api` stays: 20 domain page factories still read\n // `ctx.api` \u2014 that coupling is P3's problem, not the host port's.\n this.host = host || null;\n // The injected ontology (see tiling/kind_taxonomy.js). The WM asks it\n // for Backspace parents and top-nav sections; it names no kind itself.\n this.taxonomy = taxonomy;\n // Bus events the shell reacts to. Absent key \u21D2 no subscription.\n this.events = events || {};\n this.ctx = ctx || {};\n this.eventBus = eventBus || null;\n this.onChange = onChange || (() => {});\n\n // The leaf a fresh or emptied desktop starts with. Comes from the\n // taxonomy's root kind \u2014 NOT a hardcoded 'home'. An embedder with a\n // different ontology used to get an EcoAgent Home tile it had never\n // registered, and therefore the content_registry placeholder.\n this._rootLeaf = () => {\n const kind = this.taxonomy.root;\n return {\n content: { kind, props: {} },\n title: this.taxonomy.meta(kind)?.label || kind,\n };\n };\n // C14. Which panel tiles a fresh desktop opens with. An embedder whose\n // navigator and inspector live outside the WM root passes `false` for\n // them and gets no placeholder tiles it never registered a factory for.\n this.panelDefaults = panelDefaults || null;\n /** C15. Whether a window this WM promoted may be dropped back INTO the\n * tree. Default off: it changes what a drag to an edge does, and an\n * existing embedder upgrading the library must not find its promoted\n * windows behaving differently. */\n this.snapPromotion = !!snapPromotion;\n /** Whether a window promoted out of a tile stays CONFINED to the pane it\n * came from, rather than floating over the whole root. Default off: it\n * changes where a promoted window can be dragged, and an existing\n * embedder upgrading must not find its windows suddenly clipped. */\n this.promoteInPlace = !!promoteInPlace;\n this._snapCtl = null;\n this.desktops = new DesktopManager({\n seed: this._rootLeaf, panelDefaults: this.panelDefaults });\n this.renderer = new TileRenderer({\n root: rootEl,\n tree: this.desktops.active().tree,\n content,\n /** C22. Where a multi-tab leaf draws its tabs \u2014 `'bottom'`\n * (the framework's own spreadsheet strip, and the DEFAULT so no\n * existing embedder's panes rearrange on upgrade) or `'top'`\n * (the editor tab bar, between the chrome and the body).\n * The renderer also mirrors this onto the root as\n * `data-twm-tabs` and watches it, so an embedder can change it\n * live without holding a renderer reference. */\n tabLayout,\n ctx: {\n ...this.ctx,\n wm: this,\n onLeafAction: (leafId, action) => this._leafAction(leafId, action),\n onLeafTabAction: (leafId, action, data) =>\n this._leafTabAction(leafId, action, data),\n },\n onFocusChange: () => this._notifyChange(),\n onAfterRender: () => this._rehomeContainedWindows(),\n });\n this._persistTimer = null;\n // Window-id \u2192 leaf-id mapping so a managed-window demote can\n // restore its original tile slot.\n this._windowToLeaf = new Map();\n\n // Central keyboard router: panels register key handlers and the\n // router dispatches each keydown only to the panel inside the\n // currently focused leaf (see panel_keys.js). One authority for\n // \"which panel owns the keyboard\" instead of every panel guessing\n // from document.activeElement.\n this.panelKeys = installPanelKeyRouter(this);\n\n // Live-update tab titles when entities are renamed. The embedder\n // names the event (`events.entityRenamed`); its per-entity editors\n // fire it on Save. Walks every leaf's `tabs` and `pageTabs` archive\n // so an archived page's tab labels stay accurate too. No event\n // configured \u21D2 no subscription, and tab titles are simply static.\n const renamedEvent = this.events.entityRenamed;\n if (renamedEvent) {\n this.eventBus?.on?.(renamedEvent, ({ kind, entityId, label } = {}) => {\n if (!kind || !entityId || !label) return;\n let touched = false;\n for (const d of this.desktops.desktops) {\n if (d.tree.renameMatchingTabs?.(kind, entityId, label)) {\n touched = true;\n }\n }\n if (touched) {\n this.renderer.render();\n this._persist();\n }\n });\n }\n }\n\n /** C22. Change the tab layout of every tile, live. Delegates to the\n * renderer, which moves each strip rather than rebuilding the tiles \u2014 so\n * nothing mounted in a tile is unmounted and no staged work is lost.\n *\n * Not persisted here: which layout a user prefers is a USER setting, and\n * the WM persists LAYOUT (`desktops`). An embedder that stores it does so\n * under its own key and passes it back as `createShell({ tabLayout })`. */\n setTabLayout(layout) {\n this.renderer.setTabLayout(layout);\n }\n\n /** Emit on bus + call onChange. Use this instead of the bare callback\n * so other surfaces (palette, top-bar toggles, page shortcuts) can\n * subscribe through the existing event system. */\n _notifyChange(reason = null) {\n try { this.onChange(reason); } catch (err) { console.error('[wm] onChange threw', err); }\n try { this.eventBus?.emit?.('wm:changed', { reason, wm: this }); }\n catch (err) { console.warn('[wm] event emit failed', err); }\n // Emit the canonical `workspace:tabs:activated` event for the\n // current primary leaf \u2014 the existing sidebars (ProjectSidebar\n // etc.) already listen for this to highlight the active row.\n try {\n const tree = this._tree();\n const id = tree.primaryLeafId();\n const leaf = id ? tree.get(id) : null;\n const kind = leaf?.content?.kind;\n if (kind && kind !== 'window-placeholder') {\n this.eventBus?.emit?.('workspace:tabs:activated', {\n kind,\n entityId: leaf.content.props?.id ?? null,\n label: leaf.title,\n from: reason || 'wm',\n });\n }\n } catch (_) {}\n }\n\n // \u2500\u2500 Persistence \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 async load() {\n const blob = await loadDesktops(this.host?.state);\n if (blob) {\n this.desktops = DesktopManager.deserialize(blob, {\n seed: this._rootLeaf, panelDefaults: this.panelDefaults });\n this.renderer.tree = this.desktops.active().tree;\n }\n // Normalize: managed windows don't survive a reload, so any\n // window-placeholder leaves restore their original content.\n for (const d of this.desktops.desktops) {\n for (const leaf of d.tree.leaves()) {\n if (leaf.content?.kind === PLACEHOLDER_KIND) {\n const p = leaf.content.props || {};\n if (p.originalKind) {\n d.tree.setLeafContent(leaf.id,\n { kind: p.originalKind, props: p.originalProps || {} },\n p.originalTitle || p.originalKind);\n }\n }\n }\n this._canonicalize(d.tree, d);\n }\n this.renderer.tree = this.desktops.active().tree;\n this.renderer.render();\n this._notifyChange();\n }\n\n _persist() {\n if (this._persistTimer) clearTimeout(this._persistTimer);\n this._persistTimer = setTimeout(async () => {\n this._persistTimer = null;\n await saveDesktops(this.host?.state, this.desktops.serialize());\n }, 500);\n }\n\n // \u2500\u2500 Content loading \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 /** Load content into the primary tile (last-active non-panel leaf).\n /** Walk back one step. Bound to Backspace.\n *\n * Priority order:\n * 1. Active tab's per-tab history stack (browser-style \"back\"):\n * any in-tile navigation (relationships click-through,\n * landing-row open, \"Open\" buttons inside tables) pushed\n * the previous state, and Backspace pops it. This is the\n * canonical user-facing back affordance.\n * 2. When the history is empty (the user landed on this page\n * via top-nav / palette / direct link), fall back to the\n * canonical taxonomy walk: a sub-page goes to its\n * top-nav, a top-nav page goes to Home.\n *\n * Adding a new kind to the injected taxonomy wires the fallback path\n * for Backspace + breadcrumb + top-nav highlight in one shot.\n * Adding a new \"Open from X\" click that should be a back-able\n * navigation just needs to use one of the in-tile routing\n * methods (openFromContext / openInLeaf / navigateActiveTab) \u2014\n * they all record history by default. */\n navigateBack() {\n const tree = this._tree();\n // Prefer the FOCUSED leaf \u2014 that's where the user was just\n // interacting (Open click, breadcrumb step, etc.). Falls back\n // to the primary leaf for keyboard-driven Backspace when no\n // tile is explicitly focused (panels excluded). If the focused\n // leaf is a panel, walk back on the primary content leaf\n // instead (Backspace from the nav panel should still navigate\n // the user's content area).\n const focusedId = tree.focusedLeafId;\n const primaryId = tree.primaryLeafId();\n const focusedLeaf = focusedId ? tree.get(focusedId) : null;\n const focusedKind = focusedLeaf?.content?.kind || '';\n const id = (focusedId && focusedKind\n && !focusedKind.startsWith('panel:')\n && focusedKind !== 'window-placeholder')\n ? focusedId : primaryId;\n if (!id) return;\n // (1) Per-tab history pop, if anything was recorded.\n const prior = tree.popActiveTabHistory?.(id);\n if (prior && prior.kind) {\n // Cross-page back entries carry a `topNav` marker \u2014 pop\n // routes via swapToPage so we land on the correct page.\n const leafNow = tree.get(id);\n const curKind = leafNow?.content?.kind;\n const curTopNav = curKind ? this.taxonomy.topNavFor(curKind) : null;\n if (prior.topNav && curTopNav && prior.topNav !== curTopNav) {\n tree.swapToPage?.(\n id,\n { kind: prior.kind, props: prior.props || {},\n title: prior.title || _tabTitle(prior.kind, prior.props) },\n curTopNav, prior.topNav,\n { recordHistory: false },\n );\n } else {\n tree.replaceActiveTabContent(\n id,\n { kind: prior.kind, props: prior.props || {} },\n prior.title || _tabTitle(prior.kind, prior.props),\n { recordHistory: false },\n );\n }\n tree.focus(id);\n this.renderer.render();\n this._persist();\n this._notifyChange('navigate-back');\n return;\n }\n // (2) Taxonomy fallback \u2014 the user's path is gone, so walk the\n // canonical parent instead. An ENTITY-level parent wins (the\n // instance one level up, e.g. the parent an id names); failing\n // that, the KIND-level parent (this page's section).\n const leaf = tree.get(id);\n if (!leaf?.content) return;\n const { kind, props } = leaf.content;\n const up = this.taxonomy.parentOf(kind, props);\n if (up) { this.navigateActiveTab(id, up.kind, up.props); return; }\n const parent = this.taxonomy.parentKindFor(kind);\n if (parent) this.navigateActiveTab(id, parent);\n }\n\n /** True when `navigateBack` would do something user-visible \u2014 i.e.\n * the active tab has per-tab history, OR the current kind has a\n * taxonomy parent. Used by the breadcrumb to grey out / hide the\n * Back button when there's nowhere to go. */\n canNavigateBack() {\n const tree = this._tree();\n const focusedId = tree.focusedLeafId;\n const primaryId = tree.primaryLeafId();\n const focusedLeaf = focusedId ? tree.get(focusedId) : null;\n const focusedKind = focusedLeaf?.content?.kind || '';\n const id = (focusedId && focusedKind\n && !focusedKind.startsWith('panel:')\n && focusedKind !== 'window-placeholder')\n ? focusedId : primaryId;\n if (!id) return false;\n const history = tree.activeTabHistory?.(id) || [];\n if (history.length > 0) return true;\n const leaf = tree.get(id);\n const kind = leaf?.content?.kind;\n return !!(kind && this.taxonomy.parentKindFor(kind));\n }\n\n /** Persist editor sub-state (e.g. active sub-tab) into the active\n * tab's WM props so future history snapshots restore the user's\n * view rather than the default landing. Pure metadata write \u2014 no\n * re-render, no history push. */\n updateActiveTabProps(leafId, patch) {\n const tree = this._tree();\n if (!tree?.updateActiveTabProps) return;\n // READ BEFORE THE WRITE. This is what `rebaselineLeaf` checks the\n // cached entry against, and it is the whole safety of accepting a props\n // change onto a live tile: if the mounted wrap is not still at the key\n // the tree had a moment ago, something else changed that this call has\n // no business swallowing.\n const expected = this.renderer?.leafKey?.(leafId) ?? undefined;\n tree.updateActiveTabProps(leafId, patch);\n // C34. THE TILE THAT SAVED ITS STATE MUST NOT BE REBUILT BY SAVING IT.\n // The renderer caches a leaf's wrap under a key that includes every\n // tab's props, so this write invalidated the tile it was called from \u2014\n // not here, but on whatever repainted next, which made it look like an\n // unrelated bug. `rebaselineLeaf` accepts the new props onto the live\n // entry; the argument is in its docstring.\n this.renderer?.rebaselineLeaf?.(leafId, expected);\n this._persist();\n }\n\n /** Navigate inside the current tab \u2014 preserves every other tab in\n * the leaf. Used by Backspace + breadcrumb segments + anything\n * else that should walk WITHIN the tile rather than reset it. */\n navigateActiveTab(leafId, kind, props = {}) {\n const tree = this._tree();\n const leaf = tree.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') {\n this.openInPrimary(kind, props);\n return;\n }\n tree.replaceActiveTabContent(leafId, { kind, props }, _tabTitle(kind, props));\n tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange('navigate-active-tab');\n }\n\n /** Route a navigation request to the right container based on\n * where it came from:\n *\n * - `ctx.windowId` set \u2192 replace the managed-window content (the\n * user is navigating inside a windowed tile; the window stays).\n * - `ctx.leafId` set AND that leaf is NOT `panel:left` \u2192 replace\n * the leaf's content in place (the user is in a split tile;\n * the click stays in that tile).\n * - Otherwise (palette, top-nav shortcuts, click in the\n * navigator panel, backspace, plain bus event) \u2192 primary tile.\n *\n * The left nav (`panel:left`) is the only tile whose clicks\n * intentionally jump out to the primary tile \u2014 every other\n * container is self-contained. */\n openFromContext(ctx, kind, props = {}) {\n if (ctx?.windowId && this._windowToLeaf.has(ctx.windowId)) {\n this.openInWindow(ctx.windowId, kind, props);\n return;\n }\n if (ctx?.leafId) {\n const tree = this._tree();\n const leaf = tree.get(ctx.leafId);\n const k = leaf?.content?.kind;\n if (leaf && k && k !== 'panel:left' && !k.startsWith('panel:')) {\n this.openInLeaf(ctx.leafId, kind, props);\n return;\n }\n }\n this.openInPrimary(kind, props);\n }\n\n /** Replace the leaf's **active tab** in place \u2014 preserves every\n * other tab. This is the routing for navigation that originated\n * INSIDE the tile: clicking a row on a landing page, walking a\n * breadcrumb segment, Backspace. Outside-the-tile navigation\n * (top-nav, palette, side-nav) uses `openInPrimary` instead, which\n * resets the leaf to a single fresh tab.\n *\n * For a leaf that has only one tab (the common case before the\n * tab feature shipped), the behavior is identical to the old\n * setLeafContent: one tab in, one tab out. */\n openInLeaf(leafId, kind, props = {}) {\n const tree = this._tree();\n const leaf = tree.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') {\n this.openInPrimary(kind, props);\n return;\n }\n tree.replaceActiveTabContent(leafId, { kind, props }, _tabTitle(kind, props));\n tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n\n /** Replace a managed window's content in place. Tears down the\n * previous mount, mounts the new kind into the same body element,\n * and updates the window's title. */\n openInWindow(winId, kind, props = {}) {\n const rec = this._windowToLeaf.get(winId);\n if (!rec) { this.openInPrimary(kind, props); return; }\n try { rec.mountInfo?.destroy?.(); } catch {}\n // R8. THE BODY, NOT THE CONTENT ELEMENT. A floated pane's window holds\n // a tab strip above its body, and both are children of `contentEl` \u2014\n // so clearing `contentEl` here would delete the strip, silently, on\n // the first in-window navigation, and the window would keep every one\n // of its tabs in a list nothing could reach. `_navigateWindow` builds\n // a window with no strip and no body, hence the fallback.\n const host = rec.bodyEl || rec.contentEl;\n host.innerHTML = '';\n const mountInfo = this.content.mount(kind, host, props,\n { ...this.ctx, wm: this, windowId: winId });\n rec.mountInfo = mountInfo;\n rec.original = {\n kind, props: { ...(props || {}) },\n title: mountInfo?.title || kind,\n };\n // R8. And the TAB the window is showing is now that content, so the\n // strip renames with it and a later dock puts back what is on screen\n // rather than what the tab was called when it was promoted.\n const tab = (rec.tabs || [])[rec.activeTabIdx];\n if (tab) {\n tab.kind = kind;\n tab.props = { ...(props || {}) };\n tab.title = rec.original.title;\n rec.strip?.update(rec.tabs, rec.activeTabIdx);\n }\n // Update window title (DOM + the ManagedWindow instance).\n this._setWindowTitle(rec, rec.original.title);\n // `rec.original` now holds the latest content, so a later \"back to\n // tile\" docks the current view (not the kind first promoted). The\n // window owns no tile in the tree, so there is no leaf to update.\n this._persist();\n this._notifyChange('window-content-changed');\n }\n\n /** Load content into the primary tile (last-active non-panel leaf).\n * If only panel tiles exist (or the tree is empty), spawn a new\n * content leaf and canonicalize so the panels wrap it. */\n openInPrimary(kind, props = {}) {\n const tree = this._tree();\n let leafId = tree.primaryLeafId();\n let spawned = false;\n if (!leafId) {\n leafId = this._spawnContentLeaf(tree);\n spawned = true;\n }\n if (!leafId) return;\n // Page-aware swap: archive the leaf's current page tabs and\n // restore (or initialize) the target page's. This is what makes\n // a tile remember its tab set when the user clicks away to\n // another top-nav page and back. `swapToPage` is a no-op when\n // the leaf has no current page (just-spawned content leaf) \u2014\n // the default branch initializes a fresh single tab.\n const leaf = tree.get(leafId);\n const currentTopNav = leaf?.content?.kind\n ? this.taxonomy.topNavFor(leaf.content.kind) || null\n : null;\n const targetTopNav = this.taxonomy.topNavFor(kind) || kind;\n const title = _tabTitle(kind, props);\n tree.swapToPage(leafId,\n { kind, props, title },\n currentTopNav, targetTopNav);\n tree.focus(leafId);\n if (spawned) this._canonicalize(tree, this.desktops.active());\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n\n // \u2500\u2500 Split / close / focus / move \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 split(dir) {\n const tree = this._tree();\n const focused = tree.focusedLeafId;\n if (!focused) return;\n // Chrome split buttons mirror Alt+H / Alt+V. The freshly created\n // pane is SEEDED with the default HOME content (the taxonomy root)\n // rather than left empty \u2014 the never-empty-tile invariant: a tile\n // in the grid always holds content the user can act on, and the\n // main tile is never left blank.\n const newId = tree.split(focused, dir);\n if (newId) this._seedHome(tree, newId);\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n\n /** Seed a leaf with the default HOME content (the taxonomy root kind).\n * Used to keep the never-empty-tile invariant: the pane freed by a\n * split, or emptied when its last tab floats into a window, is\n * re-homed instead of destroyed or left blank. Embedder-agnostic \u2014\n * the HOME kind comes from the taxonomy, exactly like a fresh\n * desktop's seed leaf. */\n _seedHome(tree, leafId) {\n const seed = this._rootLeaf();\n tree.setLeafContent(leafId, seed.content, seed.title);\n }\n\n /** Split `leafId` along `dir` and mount `kind`/`props` in the freshly\n * created sibling \u2014 the \"open this content in a new split\" primitive\n * behind the code-pane split buttons, the per-pane context menu and\n * Alt+Shift+H / Alt+Shift+V. The source leaf keeps its content\n * untouched; the new pane gets a FRESH mount (so e.g. opening a Code\n * pane in a split never dismounts the original). Returns the new\n * leaf id, or null if the leaf can't be split. */\n splitLeafWith(leafId, dir, kind, props = {}, title = '') {\n const tree = this._tree();\n const src = leafId ? tree.get(leafId) : null;\n if (!src || src.kind !== 'leaf') return null;\n const newId = tree.split(leafId, dir);\n if (!newId) return null;\n tree.setLeafContent(newId, { kind, props }, title || _tabTitle(kind, props));\n tree.focus(newId);\n this.renderer.render();\n this._persist();\n this._notifyChange('split-with');\n return newId;\n }\n\n /** Resolve the focused tile's active content for the keyboard-driven\n * \"open focused tile elsewhere\" chords (Alt+T / Alt+N / Alt+Shift+H /\n * Alt+Shift+V). Returns null for panels, window placeholders and\n * empty tiles \u2014 none of which can be meaningfully duplicated. */\n _focusedContent() {\n const tree = this._tree();\n const id = tree.focusedLeafId;\n const leaf = id ? tree.get(id) : null;\n if (!leaf || leaf.kind !== 'leaf' || !leaf.content) return null;\n const kind = leaf.content.kind;\n if (!kind || kind === PLACEHOLDER_KIND || kind.startsWith('panel:')) return null;\n return { leafId: id, kind, props: leaf.content.props || {}, title: leaf.title };\n }\n\n /** Alt+T \u2014 open the focused tile's content in a new tab on that tile. */\n openFocusedInTab() {\n const c = this._focusedContent();\n if (!c) return;\n this.openInTabFromContext({ leafId: c.leafId }, c.kind, c.props);\n }\n\n /** Alt+N \u2014 open the focused tile's content in a fresh managed window.\n * Unlike Alt+F (promote) this DUPLICATES: the source tile stays put. */\n openFocusedInWindow() {\n const c = this._focusedContent();\n if (!c) return;\n this._navigateWindow(c.kind, c.props);\n }\n\n /** Alt+Shift+H / Alt+Shift+V \u2014 split the focused tile and open a fresh\n * copy of its content in the new pane. Falls back to an empty split\n * when the focused tile has no duplicable content (e.g. a panel). */\n splitFocusedWith(dir) {\n const c = this._focusedContent();\n if (!c) { this.split(dir); return; }\n this.splitLeafWith(c.leafId, dir, c.kind, c.props, c.title);\n }\n\n closeFocused() {\n const tree = this._tree();\n const focusedId = tree.focusedLeafId;\n if (!focusedId) return;\n const leaf = tree.get(focusedId);\n const kind = leaf?.content?.kind;\n\n // \u2550\u2550 C20. THE CONTENT'S VETO IS HONOURED HERE, NOT ONLY PAINTED \u2550\u2550\n //\n // `chrome: { close: false }` \u2014 or `{ close: { disabled, title } }`,\n // which is the same refusal with a sentence attached \u2014 is read at mount\n // and `_vetoStructuralActions` acts on it by disabling the \u00D7 in the\n // tile's chrome. That is ONE door. This verb has three more: Alt+W\n // (`keymap.js`), the tile's right-click *Close tile* (`shell.js`), and\n // the tab strip's \u00D7 on a leaf's LAST tab (`_leafTabAction`, which\n // delegates straight to here). All three closed a pane whose own button\n // was greyed out with a tooltip promising it could not be closed.\n //\n // This is the same asymmetry `_floatableLeaf` was written to end for\n // `promote`, resolved the same way and for the same reason: the\n // renderer stays the only thing that knows what was mounted, and the WM\n // stays the only thing that decides whether a verb runs. A veto the\n // content states once holds for every door, or it is decoration.\n //\n // Silent rather than noisy: the control that offers this is already\n // disabled and already carries the explanation, so a keystroke that\n // does nothing is consistent with what the screen says.\n const closeChrome = this.renderer?.leafChrome?.(focusedId)?.close;\n if (closeChrome === false || closeChrome?.disabled === true) return;\n\n // Closing a placeholder closes its window \u2014 the window's onClose\n // handler restores the leaf, then we close that leaf too.\n if (kind === PLACEHOLDER_KIND) {\n const winId = leaf.content?.props?.windowId;\n const rec = winId ? this._windowToLeaf.get(winId) : null;\n if (rec) {\n // Drop the mapping so the onClose handler doesn't restore.\n this._windowToLeaf.delete(winId);\n try { rec.window.close({ force: true }); } catch {}\n }\n tree.close(focusedId);\n }\n // Closing a panel: also flip the desktop's panel state off so\n // the top-bar toggle button reflects the closure.\n else if (PANEL_KINDS.has(kind)) {\n const d = this.desktops.active();\n const side = kind.split(':')[1];\n d.panels[side] = false;\n this._canonicalize(tree, d);\n }\n else {\n tree.close(focusedId);\n }\n\n // THE MASTER TILE COMES BACK.\n //\n // Closing the last content tile used to leave the panels occupying the\n // whole WM, on the reasoning that `openInPrimary` would spawn a new leaf\n // when something needed one. That is true and it is not enough: the\n // content area is also the GROUND floating windows stand on, so an\n // embedder that promotes a tile into a window and then closes the tile\n // it came from is left with a window floating over nothing, and no\n // surface to drop it back onto. There is nowhere to put it and nothing\n // saying what to do next.\n //\n // So the invariant is now the same one `toggleManagedFocused` already\n // keeps for a lone tile: there is ALWAYS a content leaf, and it holds\n // the taxonomy root. `_seedHome` is the same call, so the tile that\n // comes back is the same tile a fresh desktop starts with.\n if (!tree.leaves().some((l) => !String(l.content?.kind || '').startsWith('panel:'))) {\n const spawned = this._spawnContentLeaf(tree);\n if (spawned) this._seedHome(tree, spawned);\n }\n\n // Re-canonicalize so the remaining panels fill the area properly (no\n // leftover wrap from the closed content leaf).\n this._canonicalize(tree, this.desktops.active());\n\n this.renderer.render();\n this._persist();\n this._notifyChange('tile-closed');\n }\n\n /** Close the ACTIVE tab of a leaf (e.g. a Cancel button inside the\n * tab's content). Mirrors the tab-strip \u00D7 handler: remove just that\n * tab, or close the whole tile if it was the last one. Lets content\n * self-close without nuking sibling tabs. */\n closeActiveTab(leafId) {\n const tree = this._tree();\n const leaf = leafId ? tree.get(leafId) : null;\n if (!leaf || leaf.kind !== 'leaf') {\n // No tabbed leaf (e.g. content in a managed window) \u2014 fall\n // back to closing the focused container.\n this.closeFocused();\n return;\n }\n const tabs = leaf.tabs || [];\n if (tabs.length <= 1) {\n tree.focus(leafId);\n this.closeFocused();\n return;\n }\n const idx = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));\n tree.removeLeafTab(leafId, idx);\n tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange('twm-tab-close');\n }\n\n /** Insert a new empty content leaf at the centre of the layout\n * (wraps the current root in an h-split with the content on the\n * left, 4:1 ratio). If the tree is empty, becomes the root. */\n _spawnContentLeaf(tree) {\n const newLeaf = makeLeaf({ content: null, title: '' });\n if (!tree.rootId) { tree.setRoot(newLeaf); return newLeaf.id; }\n tree._register(newLeaf);\n const split = {\n id: `split-spawn-${Date.now().toString(36)}`,\n kind: 'split', parentId: null, dir: 'h',\n children: [newLeaf.id, tree.rootId], sizes: [4, 1],\n };\n tree.nodes.set(split.id, split);\n const oldRoot = tree.get(tree.rootId);\n if (oldRoot) oldRoot.parentId = split.id;\n newLeaf.parentId = split.id;\n tree.rootId = split.id;\n tree.focus(newLeaf.id);\n return newLeaf.id;\n }\n\n focusDir(dir) {\n if (this._tree().focusDir(dir)) {\n this.renderer._updateFocusClasses();\n this._notifyChange();\n }\n }\n\n /** The `.twm-leaf` DOM element of the currently focused leaf on the\n * active desktop, or null. The panel-key router consults this to\n * decide which panel owns the keyboard \u2014 it is the single source of\n * truth for \"the selected tile\". */\n focusedLeafEl() {\n const id = this._tree().focusedLeafId;\n return id ? this.renderer.leafEl(id) : null;\n }\n\n moveFocused(dir) {\n if (this._tree().moveDir(dir)) {\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n }\n\n // \u2500\u2500 Tile <-> Managed window \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 /** Float the focused pane into a managed window.\n *\n * R8. THE WHOLE PANE, not its active tab. This used to float one tab and\n * leave the rest behind, which made \"float this pane as a window\" a\n * different verb from the one its own tooltip named: a pane with three\n * tables in it became a window holding one and a pane holding two, and\n * nothing on screen said which of the three you were going to get. The\n * product owner's words are the whole specification \u2014 *\"to window includes\n * the tab-strip\"* \u2014 so the tabs travel with the pane and the strip is\n * rendered INSIDE the window.\n *\n * Floating ONE tab is still available and is still wanted; it moved to\n * where it was always meant to be, which is the right-click menu on the\n * tab itself (R9, `floatTabAsWindow`). A verb that acts on one tab belongs\n * on that tab, not on the pane's chrome.\n *\n * Never-empty-tile invariant, unchanged: the emptied pane is RE-SEEDED\n * with the default HOME content rather than destroyed, so the grid never\n * ends up with a missing or blank main tile. */\n toggleManagedFocused() {\n const tree = this._tree();\n const focused = tree.focused();\n if (!focused) return null;\n return this.floatPane(focused.id);\n }\n\n /** R8. Float a pane \u2014 every tab, with the strip \u2014 into a managed window.\n * Returns the window id, or null when the leaf is not something that can\n * be floated. */\n floatPane(leafId) {\n const leaf = this._floatableLeaf(leafId);\n if (!leaf) return null;\n const tabs = _leafTabSpecs(leaf);\n const active = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));\n return this._promote(leafId, tabs, active, { wholePane: true });\n }\n\n /** R9. Float ONE tab of a pane into a managed window, leaving its siblings\n * where they are \u2014 which is exactly what `toggleManagedFocused` did before\n * R8, so the behaviour survives, it just moved to the gesture that names\n * it. The tab's right-click menu is the only caller. */\n floatTabAsWindow(leafId, idx) {\n const leaf = this._floatableLeaf(leafId);\n if (!leaf) return null;\n const tabs = _leafTabSpecs(leaf);\n if (!Number.isInteger(idx) || idx < 0 || idx >= tabs.length) return null;\n return this._promote(leafId, [tabs[idx]], 0, { wholePane: false, tabIdx: idx });\n }\n\n /**\n * C33. MOVE ONE TAB INTO ANOTHER TILE \u2014 the same verb as `floatTabAsWindow`\n * above with a TILE as the destination instead of a window, which is why it\n * sits beside it.\n *\n * \u2550\u2550 THE ORDER IS LOAD-BEARING \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * Four steps, and three of them are in this order for a reason that a\n * plausible-looking rewrite would destroy:\n *\n * (a) READ THE TAB SPEC FIRST. `fromIdx` is an ARRAY INDEX \u2014 the only\n * identity a tile tab has (`tile_renderer._tabKey`) \u2014 so it is stale\n * the instant anything splices a tab list. Everything below works\n * from the copy taken here.\n *\n * (b) SPLIT BEFORE REMOVING. When the destination IS the source pane \u2014\n * \"tear this tab off into a split beside its siblings\" \u2014 removing\n * first can empty that pane and send it through `_seedHome`, so the\n * split would then be splitting a freshly seeded ground rather than\n * the pane the preview drew. Splitting first cannot go wrong in the\n * other direction: `tree.split` never touches tabs.\n *\n * (c) THE MOVE ITSELF IS ONE TREE CALL for `tab` \u2014 `moveTabToLeaf`, which\n * exists so the tab cannot be in flight between two mutations \u2014 and\n * remove-then-`setLeafContent` for `fill`/`split`, where the\n * destination is ground or brand new and REPLACING is the point.\n *\n * (d) RE-SEED AND MERGE, exactly as `_promote` does when the last tab\n * leaves a pane (`_seedHome` then `_mergeStartTiles`). A pane is\n * never left blank, and two grounds never end up side by side with a\n * splitter between them for no reason.\n *\n * \u2550\u2550 `wm:tab-moved` IS EMITTED BEFORE THE REPAINT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * An embedder that keys live content by leaf id \u2014 the Tables grid registry\n * does, on `(leaf, table)`, because a DOM element exists in exactly one\n * place \u2014 has to re-key BEFORE the render mounts the destination, or the\n * destination misses its entry, builds a second grid, and the source tile's\n * deferred teardown destroys the first one along with everything typed into\n * it and not yet committed. Emitting after the render would lose that race\n * silently, which is the failure this repository keeps recording. The tree\n * is already correct at this point; only the DOM is stale.\n *\n * \u2550\u2550 WHAT THIS DELIBERATELY DOES NOT DO \u2550\u2550\u2550\u2550\u2550\u2550\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 * A tab is not dragged OUT OF A FLOATING WINDOW's strip, and a tab dropped\n * on empty space does not become a window. Both are refused by omission\n * rather than half-built, and both have a reason. A window's tabs live in\n * `_windowToLeaf`'s record and not in the tree, so their source policy is\n * `_windowTabAction`'s and not this function's. And \"dropped on nothing\" in\n * HTML5 drag-and-drop is `dragend` with no `drop` \u2014 which is also exactly\n * what pressing Escape produces, so floating a window on it would float one\n * every time a user changed their mind. Crossing DESKTOPS is out for a\n * third reason: only the active desktop is rendered, so there is no target\n * to hit.\n *\n * @param {string} fromLeafId\n * @param {number} fromIdx\n * @param {{leafId: string, mode?: 'tab'|'fill'|'split', dir?: 'h'|'v',\n * before?: boolean, toIdx?: number}} target a `tabDropProbe`\n * answer, translated by the renderer\n * @returns {string|null} the leaf the tab landed in, or null if refused\n */\n moveTabInto(fromLeafId, fromIdx, target = {}) {\n const tree = this._tree();\n const from = tree.get(fromLeafId);\n if (!from || from.kind !== 'leaf') return null;\n const tabs = Array.isArray(from.tabs) ? from.tabs : [];\n if (!Number.isInteger(fromIdx) || fromIdx < 0 || fromIdx >= tabs.length) return null;\n let destId = target?.leafId || null;\n const dest = destId ? tree.get(destId) : null;\n if (!dest || dest.kind !== 'leaf') return null;\n if (String(dest.content?.kind || '').startsWith('panel:')) return null;\n const mode = (target.mode === 'fill' || target.mode === 'split')\n ? target.mode : 'tab';\n // The two no-ops `tabDropProbe` already refuses, refused a second time\n // here because this is a PUBLIC verb and a keyboard or palette caller\n // never went through the probe: a tab dropped into the pane it already\n // lives in, and a single-tab pane split against itself.\n if (destId === fromLeafId && mode !== 'split') return null;\n if (destId === fromLeafId && tabs.length <= 1) return null;\n\n // (a) the spec, copied before any mutation makes the index a lie.\n const src = tabs[fromIdx];\n const spec = { kind: src.kind, props: { ...(src.props || {}) },\n title: src.title || src.kind || '' };\n\n // (b) the split, before the removal.\n if (mode === 'split') {\n const newId = tree.split(destId, target.dir === 'v' ? 'v' : 'h');\n if (!newId) return null;\n // THE PREVIEW PROMISED HALF OF THAT TILE, and `TileTree.split` only\n // delivers a half when it has to WRAP \u2014 into a row that already\n // runs this way it INSERTS, handing the newcomer the average of the\n // existing sizes. The same two corrections `_onManagedWindowClosed`\n // applies to a window drop (C15), for the same reason and in the\n // same order.\n _halveInto(tree, destId, newId);\n if (target.before) _swapSiblings(tree, destId, newId);\n destId = newId;\n }\n\n // (c) the move.\n if (mode === 'tab') {\n const moved = tree.moveTabToLeaf(fromLeafId, fromIdx, destId,\n Number.isInteger(target.toIdx) ? target.toIdx : -1);\n if (!moved?.ok) return null;\n } else {\n tree.removeLeafTab(fromLeafId, fromIdx);\n tree.setLeafContent(destId, { kind: spec.kind, props: spec.props }, spec.title);\n }\n\n // (d) the source pane, if the last tab just left it.\n if (!((tree.get(fromLeafId)?.tabs || []).length)) {\n this._seedHome(tree, fromLeafId);\n this._mergeStartTiles(tree, fromLeafId);\n }\n this._canonicalize(tree, this.desktops.active());\n\n // BEFORE THE REPAINT. See the block comment above \u2014 this is the moment\n // the tree is right and the DOM has not moved yet, and it is the only\n // moment an embedder can re-key content it holds by leaf id.\n try {\n this.eventBus?.emit?.('wm:tab-moved',\n { fromLeafId, toLeafId: destId, tab: spec, mode });\n } catch (err) { console.warn('[wm] tab-moved emit failed', err); }\n\n if (tree.get(destId)) tree.focus(destId);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-moved');\n return destId;\n }\n\n /** The guards both float verbs share. A panel tile is chrome, not content;\n * a window placeholder is already a window; an empty tile has nothing to\n * carry \u2014 and, since C20 was extended, content that declared itself\n * unfloatable is not floated by ANY door.\n *\n * THE LAST ONE IS WHY THIS FUNCTION IS THE RIGHT PLACE. C20 let a content\n * factory return `chrome: { promote: false }`, and the renderer honoured\n * it by not PAINTING the float button. That is one door of four: the\n * tile's right-click menu has offered \"Float this pane as a window\" all\n * along (`shell.js`'s `_tileContextMenu`, whose only guard is\n * `isPanel || !leaf.content`), the chrome pull-down asks for `promote`,\n * and so now does the chrome's double-click. Each of them arrives here.\n *\n * The case it protects is an embedder's MASTER tile: the ground that\n * floating windows stand on. Floating it promotes the ground into a\n * window, which force-closes every window standing on it and re-seeds the\n * pane WITHOUT the props that made it a ground \u2014 reproduced end to end\n * before this guard existed. A veto the content states once should hold\n * for every gesture, not only the one the renderer draws. */\n _floatableLeaf(leafId) {\n const leaf = leafId ? this._tree().get(leafId) : null;\n if (!leaf || leaf.kind !== 'leaf' || !leaf.content) return null;\n if (PANEL_KINDS.has(leaf.content.kind)) return null;\n if (leaf.content.kind === PLACEHOLDER_KIND) return null;\n // `=== false` rather than falsy: content that says nothing about\n // `promote` stays floatable, which is what every existing embedder\n // relies on. Only an explicit refusal refuses.\n if (this.renderer?.leafChrome?.(leaf.id)?.promote === false) return null;\n return leaf;\n }\n\n /**\n * The promote itself: build the window, mount the active tab in it, and\n * take the tabs out of the tree.\n *\n * @param {string} leafId the pane the tabs are coming out of\n * @param {object[]} tabs `{kind, props, title}`, in order\n * @param {number} activeTabIdx which of them the window shows first\n * @param {{wholePane: boolean, tabIdx?: number}} opts\n */\n _promote(leafId, tabs, activeTabIdx, { wholePane, tabIdx = -1 }) {\n const tree = this._tree();\n const leaf = tree.get(leafId);\n const desktopIdx = this.desktops.activeIdx;\n const tabCount = Array.isArray(leaf.tabs) ? leaf.tabs.length : 1;\n const active = Math.max(0, Math.min(tabs.length - 1, activeTabIdx || 0));\n const original = { ...tabs[active], props: { ...(tabs[active].props || {}) } };\n\n const contentEl = document.createElement('div');\n contentEl.className = 'twm-window-content';\n contentEl.style.cssText = 'display:flex; flex-direction:column; flex:1; min-width:0; min-height:0; height:100%;';\n // R8. THE STRIP IS INSIDE THE WINDOW. A pane's tabs and a pane's body\n // are one thing \u2014 *\"to window includes the tab-strip\"* \u2014 so the window\n // gets both, in the order the `top` layout draws them, and the content\n // mounts into the BODY rather than into the window's content element.\n // Everything that used to write straight into `contentEl` (see\n // `openInWindow`) has to write into the body now, or it takes the strip\n // with it the first time the window's content is replaced.\n const tabBarEl = document.createElement('div');\n tabBarEl.className = 'twm-window-tabbar';\n const bodyEl = document.createElement('div');\n bodyEl.className = 'twm-window-body';\n bodyEl.style.cssText = 'display:flex; flex-direction:column; flex:1; min-width:0; min-height:0;';\n contentEl.append(tabBarEl, bodyEl);\n\n const winId = `twm-mw-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 5)}`;\n const mountInfo = this.content.mount(original.kind, bodyEl, original.props,\n { ...this.ctx, wm: this, windowId: winId });\n const win = new ManagedWindow({\n id: winId,\n title: mountInfo?.title || original.title,\n icon: 'web_asset',\n content: contentEl,\n canMinimize: true,\n canMaximize: true,\n canResize: true,\n modal: false,\n // C15. Dropping a promoted window on a tile PUTS IT BACK \u2014 as that\n // tile's content, as a split of it, or as one of its tabs. Off\n // unless the embedder asked, because it changes what a drag to an\n // edge means.\n snap: this.snapPromotion,\n snapController: this.snapPromotion ? this._snapController() : null,\n // R1. THE PANE IS A BOX WITH `overflow: hidden`. A window contained\n // to one (C21) cannot be dragged a single pixel outside it, so\n // \"drag a window from one tile to another\" \u2014 the gesture all three\n // drop behaviours are built on \u2014 was not merely awkward, it was\n // invisible. For the length of a drag the window is re-parented\n // here, to the root every tile is inside; on release it goes back\n // into a pane, either the one it was dropped on or the one it came\n // from. Resolved per drag: the root outlives any tile, and a tile\n // grabbed once does not survive its own repaint.\n dragHost: () => this.rootEl,\n dragBounds: () => this._tileBounds(),\n // R7. MAXIMISE MEANS BACK TO TILE. This window came OUT of the\n // tree; the useful thing to do with it is put it back, and filling\n // the screen with it is the one gesture that makes putting it back\n // harder. So the maximize button docks \u2014 and the separate demote\n // button the WM used to inject beside it is gone, because two\n // buttons for one verb is how you get a chrome nobody reads.\n onMaximize: () => this.bringBackWindow(winId),\n maximizeIcon: 'close_fullscreen',\n maximizeTitle: 'Back to tile',\n onClose: () => this._onManagedWindowClosed(winId, null),\n });\n\n this._windowToLeaf.set(winId, {\n // The floated tabs now live in the window, not the tree. leafId\n // is null so \"back to tile\" re-docks into the desktop's primary\n // tile (see _onManagedWindowClosed) \u2014 the source tile itself\n // survives (re-seeded with HOME when it was emptied).\n leafId: null, desktopIdx, original, mountInfo, window: win,\n contentEl, bodyEl, tabBarEl,\n // R8. THE PANE'S TABS TRAVEL WITH IT, and this is where they live\n // while the window is open. `original` still mirrors the ACTIVE one\n // so every existing reader of the record \u2014 `openInWindow`, each of\n // `_onManagedWindowClosed`'s docks \u2014 keeps working unchanged; the\n // list beside it is what makes a dock restore ALL of them.\n tabs, activeTabIdx: active, strip: null,\n // \u2550\u2550 C21. WRITTEN HERE, BEFORE THE TREE IS TOUCHED \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n //\n // `homeLeafId` is the pane this window stands on, and it used to be\n // assigned at the BOTTOM of this function \u2014 after `_seedHome`,\n // after `_mergeStartTiles`, after the repaint. That ordering\n // destroyed a tile per promotion, and it looked like a window bug\n // because a window is what the user had just moved.\n //\n // `_mergeStartTiles` (below) refuses to merge a start tile that has\n // a window standing on it, and `_paneHoldsWindows` answers that\n // question two ways: THIS FIELD, and a DOM probe for a\n // `.twm-managed-window` inside the leaf. At the old assignment point\n // neither could be true yet \u2014 the field was unwritten and the window\n // had not been `moveTo`'d into the pane \u2014 so the pane that was one\n // line away from becoming this window's ground answered *nothing\n // floats here* and was merged into its neighbour.\n //\n // Three panes floated one after another ended as ONE pane: the\n // first promotion left a start tile, the second merged its own\n // freshly-seeded tile away, and so did the third. Every window\n // after the first was then left with a `homeLeafId` naming a leaf\n // the tree no longer had \u2014 so the `moveTo` below was skipped and the\n // window never became contained, `_rehomeContainedWindows` found no\n // element to re-home it into, and `bringBackWindow` fell through to\n // the PRIMARY tile and docked as a tab onto the ground ANOTHER\n // window was standing on. That is the whole of the reported\n // *\"expand one to a tile > influences others or even tiles lost\"*.\n //\n // Writing it here is the smallest fix that closes all of it: a\n // guard that already existed starts being able to see the window it\n // was written to protect. The `moveTo` stays at the bottom, because\n // it needs the wrap the repaint rebuilds.\n homeLeafId: this.promoteInPlace ? leafId : null,\n // Set to true by bringBackWindow so the close path knows to\n // restore the content instead of destroying it.\n _demoting: false,\n });\n this._syncWindowTabs(winId);\n\n // R9. ONE TAB out of a multi-tab pane: the siblings stay put. R8: the\n // pane is emptied and re-seeded, because the whole of it left.\n if (!wholePane && tabCount > 1) tree.removeLeafTab(leafId, tabIdx);\n else this._seedHome(tree, leafId);\n\n // R10. The pane that just emptied is a START TILE now, and so may be\n // the pane beside it. Two of them side by side are one surface with a\n // splitter through it for no reason \u2014 see `_mergeStartTiles`, which is\n // where the rest of that argument lives.\n this._mergeStartTiles(tree, leafId);\n\n tree.focus(leafId);\n this._canonicalize(tree, this.desktops.active());\n\n this.renderer.render();\n\n // CONFINED TO THE PANE IT CAME FROM, when the embedder asked for it.\n //\n // Done AFTER the render and BEFORE `show()`, and both halves of that\n // matter. After, because promoting re-seeds the source leaf, which\n // changes its cache key and REBUILDS its wrap \u2014 the element grabbed\n // before the render is destroyed by it. Before, because `moveTo` on a\n // window that has not been built yet simply records the container\n // (`managed_window.js` returns early on a null element), so `show()`\n // mounts straight into the pane instead of appearing at the viewport\n // and jumping.\n //\n // The LEAF WRAP, not the `.twm-slot` around it: slots are recreated on\n // every render (`tile_renderer.js` clears the root and rebuilds them)\n // while leaf wraps are cached and re-parented, so a window contained to\n // a slot would be orphaned the first time anything repainted. The wrap\n // fills the slot, so the two are the same box.\n if (this.promoteInPlace) {\n // `homeLeafId` was written with the record, above, and the argument\n // for that is there. It is what `_rehomeContainedWindows` re-parents\n // against after every repaint \u2014 without it the SECOND promotion out\n // of a two-tab pane deletes the first one's window: the leaf's wrap\n // is rebuilt when its tab set changes, and the first window was\n // parented into the wrap that the rebuild threw away.\n const paneEl = this.renderer.leafEl(leafId);\n if (paneEl) win.moveTo(paneEl);\n }\n\n win.show();\n this._decorateManagedWindow(win, winId);\n this._persist();\n this._notifyChange('window-promoted');\n return winId;\n }\n\n // \u2550\u2550 R8. The tab strip inside a floated pane \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 * Draw (or hide) a window's tab strip, and keep its title honest.\n *\n * Hidden below two tabs, exactly as a pane's strip is\n * (`tile_renderer._renderTabBar`): the common case is one tab, and a strip\n * naming the one thing you are already looking at is a line of chrome\n * saying nothing. Building the strip lazily also means a window promoted\n * out of a single-tab pane costs no `NotebookTabBar` at all.\n */\n _syncWindowTabs(winId) {\n const rec = this._windowToLeaf.get(winId);\n if (!rec) return;\n const tabs = rec.tabs || [];\n rec.activeTabIdx = Math.max(0, Math.min(tabs.length - 1, rec.activeTabIdx || 0));\n if (tabs.length <= 1) {\n try { rec.strip?.dispose(); } catch { /* already gone */ }\n rec.strip = null;\n rec.tabBarEl?.classList.add('twm-window-tabbar--hidden');\n return;\n }\n rec.tabBarEl?.classList.remove('twm-window-tabbar--hidden');\n if (!rec.strip) {\n rec.strip = createTabStrip({\n hostEl: rec.tabBarEl,\n taxonomy: this.taxonomy,\n onAction: (action, data) => this._windowTabAction(winId, action, data),\n });\n }\n rec.strip.update(tabs, rec.activeTabIdx);\n }\n\n /** The window strip's half of `_leafTabAction` \u2014 the same four verbs\n * against the window record instead of against the tree. */\n _windowTabAction(winId, action, data = {}) {\n const rec = this._windowToLeaf.get(winId);\n if (!rec) return;\n const tabs = rec.tabs || [];\n if (action === 'switch') {\n this.showWindowTab(winId, data.idx);\n return;\n }\n if (action === 'close') {\n if (data.idx < 0 || data.idx >= tabs.length) return;\n // THE LAST TAB CLOSES THE WINDOW, which is what closing the last\n // tab of a pane does to the pane (`_leafTabAction`). A window with\n // no content in it is a title bar over nothing.\n if (tabs.length <= 1) {\n try { rec.window.close({ force: true }); } catch { /* gone */ }\n return;\n }\n tabs.splice(data.idx, 1);\n if (data.idx < rec.activeTabIdx) rec.activeTabIdx -= 1;\n else if (data.idx === rec.activeTabIdx) {\n rec.activeTabIdx = Math.max(0, data.idx - 1);\n this._mountWindowTab(winId);\n }\n this._syncWindowTabs(winId);\n this._notifyChange('window-tab-close');\n return;\n }\n if (action === 'move') {\n const { from, to } = data;\n if (from == null || to == null) return;\n if (from < 0 || from >= tabs.length || to < 0 || to >= tabs.length) return;\n const moved = tabs.splice(from, 1)[0];\n tabs.splice(to, 0, moved);\n // The same index arithmetic `TileTree.moveLeafTab` does, for the\n // same reason: the tab that was showing must still be showing.\n if (rec.activeTabIdx === from) rec.activeTabIdx = to;\n else if (from < rec.activeTabIdx && to >= rec.activeTabIdx) rec.activeTabIdx -= 1;\n else if (from > rec.activeTabIdx && to <= rec.activeTabIdx) rec.activeTabIdx += 1;\n this._syncWindowTabs(winId);\n return;\n }\n if (action === 'menu') this._showWindowTabContextMenu(winId, data.idx, data.x, data.y);\n }\n\n /** Show one of a floated pane's tabs. Public because a window is the only\n * place this list exists \u2014 nothing else can reach it. */\n showWindowTab(winId, idx) {\n const rec = this._windowToLeaf.get(winId);\n if (!rec) return false;\n const tabs = rec.tabs || [];\n if (!Number.isInteger(idx) || idx < 0 || idx >= tabs.length) return false;\n if (idx === rec.activeTabIdx) return true;\n rec.activeTabIdx = idx;\n this._mountWindowTab(winId);\n this._syncWindowTabs(winId);\n this._persist();\n this._notifyChange('window-tab-switch');\n return true;\n }\n\n /** Tear the current mount down and mount the active tab in its place.\n * `rec.original` follows, so a later dock puts back what is on screen. */\n _mountWindowTab(winId) {\n const rec = this._windowToLeaf.get(winId);\n if (!rec) return;\n const tab = (rec.tabs || [])[rec.activeTabIdx];\n if (!tab) return;\n try { rec.mountInfo?.destroy?.(); } catch { /* factory threw on the way out */ }\n rec.bodyEl.innerHTML = '';\n rec.mountInfo = this.content.mount(tab.kind, rec.bodyEl, tab.props || {},\n { ...this.ctx, wm: this, windowId: winId });\n rec.original = { kind: tab.kind, props: { ...(tab.props || {}) },\n title: rec.mountInfo?.title || tab.title || tab.kind };\n this._setWindowTitle(rec, rec.original.title);\n }\n\n /** The window's title, in both places it is kept. */\n _setWindowTitle(rec, title) {\n try {\n const titleEl = rec.window?.element?.querySelector('.twm-managed-window__title');\n if (titleEl) titleEl.textContent = title;\n if (rec.window) rec.window.title = title;\n } catch { /* the window is already torn down */ }\n }\n\n /** The window strip's context menu. Deliberately the close verbs and\n * nothing else: a tab in a window is already out of the tree, so\n * \"open in a window\" \u2014 the verb R9 adds to a PANE's tab menu \u2014 has\n * nowhere further to go. */\n _showWindowTabContextMenu(winId, idx, x, y) {\n const rec = this._windowToLeaf.get(winId);\n const tabs = rec?.tabs || [];\n if (!tabs.length) return;\n const items = [{ label: 'Close tab', icon: 'close', action: 'close' }];\n if (tabs.length > 1) {\n items.push({ label: 'Close other tabs', icon: 'tab_close', action: 'close-others' });\n }\n showContextMenu(x, y, items, (action) => {\n const live = this._windowToLeaf.get(winId);\n if (!live) return;\n if (action === 'close') this._windowTabAction(winId, 'close', { idx });\n else if (action === 'close-others') {\n const keep = live.tabs[idx];\n if (!keep) return;\n // RE-MOUNT ONLY IF THE KEPT TAB IS NOT THE ONE ON SCREEN.\n // Mounting is destroy-then-build, and a content factory that\n // rebuilds is a factory that can lose what is staged in it \u2014\n // \"close the other tabs\" must not be a way to discard the edits\n // in the tab you kept.\n const remount = idx !== live.activeTabIdx;\n live.tabs = [keep];\n live.activeTabIdx = 0;\n if (remount) this._mountWindowTab(winId);\n this._syncWindowTabs(winId);\n this._notifyChange('window-tab-close-others');\n }\n });\n }\n\n /**\n * Re-parent every pane-contained window into its pane's CURRENT wrap.\n *\n * Called after each repaint. A leaf's wrap is cached per (kind, props, tab\n * fingerprint) and rebuilt when any of those change, so a window parented\n * into it is thrown away with the old wrap \u2014 silently, because nothing\n * throws and the window object is still perfectly alive.\n *\n * `moveTo` returns false when the container has not changed, so this is a\n * no-op on every repaint that did not rebuild the pane in question.\n */\n _rehomeContainedWindows() {\n if (!this.promoteInPlace) return;\n for (const [, rec] of this._windowToLeaf) {\n if (!rec.homeLeafId || !rec.window) continue;\n // R1. NOT WHILE IT IS BEING DRAGGED OUT. A window mid-escape is\n // parented to the root deliberately and is following the pointer in\n // the root's coordinates; re-homing it into its pane here would clip\n // it, re-clamp it, and leave the rest of the drag computing deltas\n // against a frame that moved underneath it. The escape puts it back\n // itself on release, and adopts the current wrap when it does.\n if (rec.window.dragOrigin) continue;\n // R13, NOW A GUARD RATHER THAN A PATH. NOR WHILE IT IS MAXIMISED\n // ONTO THE DRAG HOST. A window filling the layer is parented to the\n // ROOT of necessity \u2014 `.twm-leaf` is `overflow: hidden`, so a\n // window bigger than one tile cannot be a child of one \u2014 and\n // `moveTo` re-clamps into the new container without converting, so\n // re-homing it here would shrink it back into the pane on the first\n // repaint after the gesture.\n //\n // WHICH ROUTE STILL REACHES IT, honestly stated, because a guard\n // whose route nobody can name is dead code wearing a comment. R14\n // removed the one this was WRITTEN for: the aero-snap top edge used\n // to maximise onto the layer and now docks, so no gesture in this\n // layer leaves a window maximised on the root any more. What\n // remains is the library API \u2014 `toggleMaximize({claimable: false})`\n // is public, documented on `ManagedWindow` as the way past a\n // consumer's claim, and an embedder that calls it on an adopted\n // window sitting on the drag host produces exactly this state. It\n // costs one comparison to keep and a silently shrinking window to\n // remove, so it stays.\n //\n // BOTH CONDITIONS, and neither alone. `isMaximized` by itself would\n // also skip a window maximised INSIDE its pane \u2014 that one still has\n // to follow its wrap when the wrap is rebuilt, which is the whole\n // job of this pass, and `snap_zones.test.mjs` asserts it. The\n // container by itself would skip a window the escape left on the\n // root because its origin was destroyed mid-drag, which is\n // precisely the case this pass exists to adopt.\n if (rec.window.isMaximized && rec.window.container === this.rootEl) continue;\n\n // \u2550\u2550 C21. A WINDOW BELONGS TO ONE DESKTOP \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n //\n // \u2026and is in the document only while that desktop is on screen.\n // That has always been TRUE and was never STATED: switching\n // desktops evicts the old tree's leaves from the wrap cache\n // (`tile_renderer._cleanCache`), which takes the windows standing\n // on them out of the document as a side effect. Saying it here is\n // what lets `moveWindowToDesktop` actually move a contained window\n // \u2014 it re-points the record and this pass takes the element off\n // the page the window has just left. Coming back re-parents it:\n // the destination's wraps are rebuilt on arrival, so `moveTo` sees\n // a container it has not seen and appends.\n if (rec.desktopIdx !== this.desktops.activeIdx) {\n if (!rec.homeContainer && rec.window.element?.isConnected) {\n try { rec.window.element.remove(); } catch { /* already out */ }\n }\n continue;\n }\n\n // \u2550\u2550 A HOME LEAF THAT HAS LEFT THE TREE IS REPAIRED, NOT SKIPPED \u2550\u2550\n //\n // *\"tiles must only influence tiles, not windows\"* \u2014 the product\n // owner, 2026-09-01. A tile can be destroyed by four gestures\n // (`closeFocused`, the tab strip's \u00D7 on a last tab,\n // `moveFocusedToDesktop`, and `_mergeStartTiles`) and NONE of them\n // asked what was standing on it. `_paneHoldsWindows` \u2014 the guard\n // written for exactly that question \u2014 is consulted from one place\n // in the whole file, and no close path is it.\n //\n // What happened when a pane with a window on it was closed:\n // `_cleanCache` removed the wrap, and the window's element with it,\n // twenty-three lines BEFORE this pass runs. The window was left\n // alive with `isVisible: true` and `isMinimized: false` \u2014 so it was\n // on no surface AND in no taskbar (Tables' strip lists the\n // minimised), holding a mounted grid and its staged edits, with no\n // chrome to press and no route back. Reproduced end to end.\n //\n // Repairing it HERE rather than at each of the four call sites is\n // deliberate: this pass already runs after every render, it already\n // owns *where a contained window is parented*, and a fifth gesture\n // that closes a leaf gets the same answer without knowing this rule\n // exists. The window keeps floating \u2014 it re-homes onto the tile\n // that survived rather than docking itself \u2014 which is the ruling:\n // a tile operation may move a window, never convert or destroy it.\n //\n // NOT for an adopted window (`homeContainer`): the embedder resolves\n // its own ground and owns the lifetime of the box it stands in.\n if (!rec.homeContainer && rec.homeLeafId) {\n const tree = this.desktops.desktops[rec.desktopIdx]?.tree;\n if (tree && !tree.get(rec.homeLeafId)) {\n // A TREE WITH NO CONTENT LEAF GETS ONE. `closeFocused`\n // keeps that invariant itself, but `moveFocusedToDesktop`\n // did not \u2014 with a panel open it leaves a page holding\n // nothing but chrome, and then there is no ground for the\n // windows that were standing there. Spawning it here is the\n // same call `closeFocused` makes and the same tile a fresh\n // desktop starts with; leaving `homeLeafId` dangling would\n // put the window back in the state above.\n let survivor = tree.primaryLeafId();\n if (!survivor) {\n const spawned = this._spawnContentLeaf(tree);\n if (spawned) {\n this._seedHome(tree, spawned);\n this._canonicalize(tree, this.desktops.desktops[rec.desktopIdx]);\n survivor = tree.primaryLeafId();\n }\n }\n if (survivor) rec.homeLeafId = survivor;\n }\n }\n\n // R11. AN ADOPTED WINDOW IS CONTAINED TO A BOX INSIDE THE PANE,\n // not to the pane itself \u2014 a canvas ground sits within the leaf\n // wrap, under whatever chrome the embedder draws above it. Re-homing\n // such a window to the wrap would lift it out of its ground and\n // stand it over that chrome, which is a slower and stranger version\n // of the bug this whole function exists to prevent.\n const paneEl = rec.homeContainer\n ? (rec.homeContainer() || null)\n : this.renderer.leafEl(rec.homeLeafId);\n // NO PANE ELEMENT, ON THE ACTIVE DESKTOP, AFTER THE REPAIR ABOVE.\n // What is left is an embedder's ground that has not mounted yet and\n // a tree with no content leaf at all \u2014 neither of which is a window\n // to rescue. The sentence that used to stand here said \"closed, or\n // on another desktop \u2026 rather than orphaning it into a detached\n // node\", and both halves were wrong: the other desktop is handled\n // above, and by the time this ran on a CLOSED pane the node was\n // already detached \u2014 `_cleanCache` (`tile_renderer.js`) runs before\n // `onAfterRender`, not after it.\n if (!paneEl) continue;\n if (paneEl === rec.window.container) {\n // THE CONTAINER IS UNCHANGED AND THE ELEMENT IS NOT IN IT.\n //\n // The desktop rule above takes a window off the page it has\n // left by removing the ELEMENT; it deliberately leaves\n // `container` alone, because the wrap is still the window's\n // home and re-pointing it would lose that. Coming back through\n // a desktop SWITCH is safe \u2014 the wrap is evicted and rebuilt,\n // so `moveTo` sees a new container and appends. Coming back any\n // other way is not: `moveWindowToDesktop` and `removeDesktop`\n // re-point `desktopIdx` and render with the same tree on\n // screen, so the wrap is still cached, `moveTo` returns false\n // for an unchanged container, and the window stayed detached\n // for good \u2014 the exact state this whole pass exists to prevent,\n // reached through the desktop door instead of the close door.\n //\n // `appendChild` rather than `moveTo`: nothing about the\n // window's home has changed, so there is no clamp to redo, no\n // resize observer to rebuild and no `managed-window-moved` to\n // announce. It is the same element going back into the same\n // box.\n const el = rec.window.element;\n if (el && !el.isConnected) {\n try { paneEl.appendChild(el); }\n catch (err) { console.warn('[wm] re-attach failed', err); }\n }\n continue;\n }\n try { rec.window.moveTo(paneEl); }\n catch (err) { console.warn('[wm] re-home failed', err); }\n }\n }\n\n /**\n * C21, as a verb a consumer can call: put THIS window back where it belongs\n * and say whether it moved.\n *\n * The taskbar needs it. A minimised window's element may be out of the\n * document \u2014 its desktop is not on screen, or its pane was closed \u2014 and\n * un-minimising it in that state clears `isMinimized` (so its button\n * disappears, the last handle on it) while showing nothing. `restore` has\n * to be able to repair the window BEFORE it makes it visible, and\n * `_rehomeContainedWindows` is the thing that knows how; it was simply not\n * reachable, and `taskbar.js`'s own docstring asserted it ran for these\n * windows when the guard above meant it did not.\n *\n * IT SWITCHES DESKTOPS WHEN IT HAS TO, and that is the half a bare re-home\n * cannot do. A window belongs to one desktop; if that desktop is not on\n * screen, the honest answer to *show me this window* is the one every\n * taskbar in every window manager gives \u2014 go to where it lives. Restoring\n * it onto the page the user happens to be looking at would move a window\n * between pages as a side effect of asking to see it, and that is a tile\n * decision being made by a window verb.\n *\n * @param {object} win a live ManagedWindow\n * @returns {boolean} whether this WM owns it (and so has revealed it)\n */\n revealWindow(win) {\n if (!win) return false;\n let rec = null;\n for (const [, r] of this._windowToLeaf) {\n if (r.window === win) { rec = r; break; }\n }\n if (!rec) return false;\n // AN UNCONTAINED WINDOW HAS NO PAGE, SO THERE IS NOTHING TO REVEAL.\n // `_navigateWindow`'s windows (Alt+N, \"Open a copy in a window\") float\n // over the root and stay visible on every desktop; `desktopIdx` on such\n // a record is only where it happened to be OPENED. Switching to it\n // would throw the user off the page they are on to show them a window\n // that was already in front of them. Returning false hands the restore\n // back to the embedder, which is right for the canvas registry's\n // windows too.\n if (!rec.homeLeafId && !rec.homeContainer) return false;\n if (rec.desktopIdx !== this.desktops.activeIdx\n && this.desktops.desktops[rec.desktopIdx]) {\n // Renders, and the render re-homes \u2014 so there is nothing to do\n // afterwards, and doing it twice would be a second repaint.\n this.switchDesktop(rec.desktopIdx);\n } else {\n this._rehomeContainedWindows();\n }\n return true;\n }\n\n /**\n * R12. The rectangle an ESCAPED window may occupy \u2014 the tiles, and not the\n * panels \u2014 in the root's own coordinates.\n *\n * R1 let a window leave its pane so it could reach another one, and the\n * cheapest box to let it leave into is the root every tile shares. But the\n * root holds the docked panels too, so the bottom edge stopped being an\n * edge: a window could be dragged down over the bottom panel and dropped\n * there, half-covering a surface that has its own scroll and its own\n * chrome, with no way to tell it had happened except that it looked wrong.\n *\n * The answer is the UNION OF THE CONTENT LEAVES rather than \"the root minus\n * the panel I know about\": panels dock left, right and bottom, an embedder\n * may show any combination of them, and each one may be collapsed. A union\n * of the tiles is right for all of those without enumerating any of them,\n * and it degrades to the root when a desktop is somehow all panel.\n */\n _tileBounds() {\n const root = this.rootEl;\n if (!root) return null;\n const layer = this._layerRect();\n if (!layer) return null;\n const rootRect = root.getBoundingClientRect();\n // Into the host's coordinates. `left`/`top` are written against the\n // PADDING box, so the root's own border comes off as well \u2014 the same\n // conversion the escape does, and for the same reason.\n const ox = rootRect.left + root.clientLeft - root.scrollLeft;\n const oy = rootRect.top + root.clientTop - root.scrollTop;\n return { minX: layer.left - ox, minY: layer.top - oy,\n width: layer.width, height: layer.height };\n }\n\n /**\n * R13. THE LAYER, in the VIEWPORT pixels a hit-test speaks \u2014 the union of\n * the content leaves, before it is converted into anybody's coordinates.\n *\n * This is `_tileBounds` with the last step taken off, and it stays a\n * separate function rather than being folded back into it because the two\n * frames have different readers: `_tileBounds` answers `dragBounds`, which\n * `ManagedWindow._bounds()` uses to clamp a window in the ROOT's\n * coordinates, and this answers anything measuring against the page.\n *\n * R14 REMOVED ITS OTHER READER. R13's maximise preview was drawn from here\n * so that it would be the same measurement `toggleMaximize` would deliver\n * through `dragBounds` \u2014 C15's rule, THE PREVIEW MAY NOT PROMISE A\n * RECTANGLE THE DROP DOES NOT DELIVER, applied to the one mode that did not\n * dock. There is no such mode now: the top edge docks like every other\n * zone, its preview is the TILE (`_homeDockTarget`), and the layer's only\n * remaining job is the clamp. Kept as its own function because the clamp\n * still needs the union of the CONTENT leaves rather than the root, which\n * is a definition, not a call site.\n *\n * The union of the CONTENT LEAVES rather than the root, for the reason\n * `_tileBounds` gives at length: the root holds the docked panels too.\n *\n * Null when nothing has a box yet \u2014 a layout that has not happened, a\n * desktop whose tiles are all zero-sized. Every caller treats that as \"do\n * not promise anything\", which is the only honest answer available.\n */\n _layerRect() {\n let l = Infinity, tp = Infinity, r = -Infinity, b = -Infinity;\n for (const leaf of this._tree().leaves()) {\n if (PANEL_KINDS.has(leaf.content?.kind)) continue;\n const el = this.renderer.leafEl(leaf.id);\n if (!el) continue;\n const box = el.getBoundingClientRect();\n if (!box.width || !box.height) continue;\n l = Math.min(l, box.left); tp = Math.min(tp, box.top);\n r = Math.max(r, box.right); b = Math.max(b, box.bottom);\n }\n if (!Number.isFinite(l)) return null;\n return { left: l, top: tp, width: r - l, height: b - tp };\n }\n\n /** Dock the window's content back into the tile it came from \u2014 or, when it\n * came from none, into the desktop's primary tile \u2014 then close the window.\n *\n * R7. This is what the MAXIMIZE button now does, so it is reached far more\n * often than it was as a button of its own, and \"somewhere other than where\n * the window came from\" stopped being a defensible answer. Promoting a pane\n * re-seeds it with the root kind \u2014 ground for the window to stand on \u2014 so\n * FILLING that pane is the exact inverse: the content goes back where it\n * was lifted from, replacing the ground it has been standing on.\n *\n * A home pane that has since acquired content is a different story. The\n * user opened something there, and replacing it would destroy work the\n * window knows nothing about, so the content joins it as a tab instead.\n * With no home pane at all \u2014 an Alt+N window, or any window under an\n * embedder that does not confine promotions \u2014 this is the primary-tile tab\n * it has always been. */\n bringBackWindow(windowId) {\n const rec = this._windowToLeaf.get(windowId);\n // C28. IT SAYS SO WHEN IT DOES NOTHING. This resolves through\n // `_windowToLeaf` and is silent for a window the WM never built and\n // never adopted \u2014 correct, since there is no tile to bring such a\n // window back to. The `onMaximize` wrappers below returned `true`\n // regardless, so `toggleMaximize` treated the gesture as CLAIMED and\n // returned early, and the window neither docked nor maximised: the\n // button and the double-click both did nothing at all, which is a\n // dead control rather than a limitation anybody can read. Returning\n // false lets the geometric maximise happen instead \u2014 the answer every\n // other window manager gives a window with nowhere to go back to.\n if (!rec) return false;\n const tree = this.desktops.desktops[rec.desktopIdx]?.tree;\n const home = rec.homeLeafId ? tree?.get(rec.homeLeafId) : null;\n if (home && home.kind === 'leaf') {\n rec._dock = { leafId: rec.homeLeafId,\n mode: this._isStartTile(home) ? 'fill' : 'tab' };\n }\n rec._demoting = true;\n try { rec.window.close({ force: true }); }\n catch (err) { console.warn('[wm] bringBack: close failed', err); }\n return true;\n }\n\n /**\n * R11. ADOPT A WINDOW THE EMBEDDER BUILT ITSELF.\n *\n * Everything R1\u2013R10 gave a window \u2014 escaping its pane for the length of a\n * drag, going half-transparent once it is outside, the edge/body/ground\n * drops, maximise meaning *back to tile* \u2014 is wired in `_promote`, and so\n * belongs only to windows this WM lifted out of the tree. An embedder that\n * stands its own `ManagedWindow` on a pane (`snap: true` against the pane's\n * ground) got none of it: `_snapCommit` resolves the window through\n * `_windowToLeaf` and returns false for one it never built, so every drop\n * previewed correctly and then quietly did nothing.\n *\n * The fix is not to make the WM build those windows \u2014 the embedder has its\n * own reasons for the ones it builds, and taking that over would mean\n * taking over their content, their identity and their lifetime. It is to\n * let a window JOIN the tree's world after the fact, which needs exactly\n * two things: the drag options set on the component, and a record saying\n * what content to restore when the window is docked.\n *\n * CALL THIS BEFORE `show()`. `maximizeIcon` is read when the chrome is\n * built (`managed_window.js:703`) and the chrome is built lazily by `show`\n * (`:281`), so a window adopted afterwards would carry the right behaviour\n * behind a button still drawing a square.\n *\n * TEARDOWN STAYS THE EMBEDDER'S. `mountInfo` is optional and normally\n * omitted: a window that already destroys its own content in its `onClose`\n * would otherwise destroy it twice, once here and once there. The\n * embedder's handler is chained, not replaced, and runs after this one \u2014 so\n * a dock has already re-mounted the content into the tile by the time the\n * window's own teardown disposes of the copy that was floating.\n *\n * @param {object} win a live ManagedWindow, not yet shown\n * @param {object} spec\n * @param {string} spec.kind content kind to restore into a tile\n * @param {object} [spec.props] its props\n * @param {string} [spec.title] the tab title after a dock\n * @param {string} [spec.homeLeafId] the pane it stands on: what \"back to\n * tile\" targets, and what the probe stays silent inside\n * @param {function} [spec.homeContainer] `() => HTMLElement` \u2014 the box\n * WITHIN that pane the window is contained to. A canvas pane's\n * ground is not the leaf wrap, and re-homing to the wrap after a\n * repaint would lift the window out of the ground it belongs to.\n * @param {object} [spec.mountInfo] `{destroy}`, if teardown is ours\n * @returns {string|null} the window id, or null if it could not be adopted\n */\n adoptWindow(win, spec = {}) {\n const winId = win?.id;\n if (!winId || !spec.kind) return null;\n if (this._windowToLeaf.has(winId)) return winId;\n\n // The same bag `_promote` builds, and deliberately the same values: two\n // windows on one desktop behaving differently under the same gesture is\n // the kind of difference a user reads as a bug in whichever one they\n // tried second.\n if (this.snapPromotion) {\n win.snap = win.snap && win.canDrag && win.canResize;\n win.snapController = this._snapController();\n }\n win.dragHost = () => this.rootEl;\n win.dragBounds = () => this._tileBounds();\n win.onMaximize = () => this.bringBackWindow(winId);\n win.maximizeIcon = 'close_fullscreen';\n win.maximizeTitle = 'Back to tile';\n\n const original = { kind: spec.kind, props: spec.props || {},\n title: spec.title || spec.kind };\n this._windowToLeaf.set(winId, {\n leafId: null,\n desktopIdx: this.desktops.activeIdx,\n original,\n mountInfo: spec.mountInfo || null,\n window: win,\n contentEl: null, bodyEl: null, tabBarEl: null,\n tabs: [original], activeTabIdx: 0, strip: null,\n homeLeafId: spec.homeLeafId || null,\n homeContainer: spec.homeContainer || null,\n adopted: true,\n _demoting: false,\n });\n\n const prior = win.onClose;\n win.onClose = () => {\n this._onManagedWindowClosed(winId, null);\n prior?.();\n };\n return winId;\n }\n\n // \u2550\u2550 C15. Snap-to-promote \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * The snap controller a promoted window is given. It answers the two\n * questions ManagedWindow's own C11 snap cannot, because both are about a\n * tree it does not know exists:\n *\n * probe which TILE is under the pointer, and \u2014 since R15 \u2014 which edge\n * of it THE DRAGGED WINDOW'S OWN BORDERS have reached, and what\n * would dropping there actually produce: a half of that tile, a\n * quarter of the layer, the tile entire, or (R13, at the top edge\n * of the pane the window already stands on) the whole layer,\n * which is the one answer that is not a dock at all. The preview\n * draws exactly that rectangle, because a preview that promises a\n * half and delivers a quarter is worse than no preview.\n * commit put the window in the tree \u2014 or, for R13's maximise, leave it\n * floating and give it the layer. Over an EMPTY tile the dock is\n * unambiguous and happens on release. Over an OCCUPIED tile the\n * edges are unambiguous too \u2014 the drag chose a side, so the\n * side is the split \u2014 and only the CENTRE was ever genuinely a\n * question, which is why it is the zone that changed most.\n *\n * Built once and reused: the probe runs per pointermove and allocating a\n * closure per window per drag is free, but the memo keeps the identity\n * stable for anyone comparing controllers.\n */\n _snapController() {\n if (this._snapCtl) return this._snapCtl;\n this._snapCtl = {\n probe: (e, win) => this._snapProbe(e, win),\n commit: (probe, win) => this._snapCommit(probe, win),\n };\n return this._snapCtl;\n }\n\n /**\n * How close to a tile's edge the DRAGGED WINDOW'S matching edge must come\n * for a dock to arm \u2014 in PIXELS, and a narrow band. Since R15 it is also\n * the minimum distance the drag must have travelled toward that edge\n * inside the window's own pane; `_snapSide` argues both, and this is the\n * one constant either of them is measured in.\n *\n * This was a third of the tile, measured as a fraction, with the remaining\n * middle ninth treated as a fourth zone that offered a three-way choice.\n * Both halves of that were wrong, and together they made docking the\n * DEFAULT rather than a deliberate gesture:\n *\n * - A fraction means the band grows with the tile. On a maximised layer\n * a \"third\" is several hundred pixels, so a window could not be moved\n * anywhere near the left half of the screen without arming a split.\n * - The centre zone armed over the whole middle of every tile and\n * previewed the ENTIRE tile, so simply picking a window up and moving\n * it a few pixels lit the whole pane. Every move looked like a dock\n * because every move WAS one.\n *\n * Aero snap is an edge gesture: you push THE WINDOW at an edge \u2014 which is\n * what R15 finally made it measure. So the band is a fixed 28px from the\n * edge, and what lies past it is decided by the\n * pane rather than by the pointer: in the window's OWN pane the centre\n * arms nothing at all and the drop is simply a window that moved (R2), and\n * in any other pane it is the non-destructive tab or fill of R5/R6. The\n * band itself never grows with the tile, which is the whole of the fix.\n * Docking a whole tile is also still available without any drag at all \u2014\n * the \"back to tile\" button in the window's own chrome, which names the\n * destination instead of guessing it.\n */\n static get SNAP_EDGE_PX() { return 28; }\n\n _snapProbe(e, win) {\n const leafEl = this._leafElAt(e.clientX, e.clientY, win);\n if (!leafEl) return null;\n // R2, NARROWED BY R13. THE PANE IT CAME FROM ARMS AT ITS EDGES AND\n // NOWHERE ELSE \u2014 its centre still arms nothing at all.\n //\n // The rule was a blanket one: the origin pane armed nothing anywhere,\n // neither an edge nor the centre. It answered a real complaint \u2014 a\n // window that lives on a pane could not be nudged two pixels without\n // the pane lighting up to say it was about to swallow it \u2014 but it\n // answered it with more than the complaint asked for. A nudge happens\n // in the MIDDLE of a pane, which is where a window sits and where a\n // hand moving it goes; the 28px edge bands are somewhere a drag only\n // arrives on purpose \u2014 which under R15, where the bands are tested\n // against the WINDOW's borders and a window can already be sitting in\n // one before the hand touches it, is true only because `_snapSide`'s\n // direction guard makes it true. That guard is R2's other half and it\n // is why it applies in this pane and nowhere else.\n // So the centre is still silent, the nudge is still\n // fixed in full, and the edges of a window's own pane now mean what\n // they mean in every other pane \u2014 with one addition, TOP, which in the\n // pane a window already occupies has nothing to split and means\n // MAXIMISE instead (see below).\n //\n // DECIDED HERE, SPENT BELOW. `dragOrigin` is in hand at this point and\n // the answer cannot change while the probe runs, but which zone it\n // applies to is not known until `side` is. The origin is the window's\n // own answer (`dragOrigin` \u2014 the container its drag escaped), so the\n // two halves of the rule cannot disagree: the same boundary that turns\n // the window half-transparent is the one that arms the probe.\n //\n // A window with no origin \u2014 one opened straight into a float, which\n // never belonged to a pane \u2014 has no inside to be in, and every pane\n // under it is foreign. It arms everywhere, which is correct: there is\n // no \"just moving it around at home\" for a window with no home. It also\n // never maximises by this gesture, and that is not an omission \u2014 see\n // the maximise branch, which can only promise the layer for a window\n // whose bounds are the layer.\n //\n // CONTAINS, not equals. The WM confines its own promotions to the leaf\n // WRAP, but an embedder is free to confine a window to a box it built\n // inside the leaf \u2014 a canvas pane whose ground is a sibling of its own\n // taskbar strip does exactly that \u2014 and that window's home pane is the\n // leaf around it. `contains` is true of the element itself, so the\n // WM's own case is the same test.\n const own = !!(win?.dragOrigin && leafEl.contains(win.dragOrigin));\n const leafId = leafEl.dataset.leafId;\n // THE ACTIVE DESKTOP, and the dock has to agree. A promoted window is\n // global \u2014 it stays on screen across a desktop switch \u2014 so the tile\n // under the pointer belongs to whichever desktop is showing, not to the\n // one the window was promoted from. `_onManagedWindowClosed` applies\n // `_dock` against `rec.desktopIdx`, so the drop carries the index it\n // was made on and re-homes the record to it.\n const desktopIdx = this.desktops.activeIdx;\n const leaf = this._tree().get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return null;\n // Panel tiles are chrome, not content. A window dropped on the\n // navigator has nowhere to go and the preview must not suggest it has.\n if (String(leaf.content?.kind || '').startsWith('panel:')) return null;\n\n const r = leafEl.getBoundingClientRect();\n // R4, RE-AIMED BY R15. PIXELS FROM EACH EDGE, not fractions of the\n // tile \u2014 and the edges measured are now THE DRAGGED WINDOW'S, not the\n // pointer's. `_snapSide` owns that argument in full, including the\n // corner precedence and the one case that still falls back to the\n // pointer.\n //\n // One exclusion survives unchanged: a leaf with NO content at all has\n // no edges, because splitting nothing produces two nothings and the\n // preview would be drawing a half of a pane that has nothing to halve.\n const side = leaf.content ? this._snapSide(r, e, win, own) : null;\n // R14 (was R13). TOP, IN THE WINDOW'S OWN PANE, IS *BACK TO TILE*.\n //\n // Everywhere else `top` splits off the pane's upper half. In the pane\n // the window is already floating over, that is the one edge with\n // nothing to say \u2014 splitting a pane in order to put a window into the\n // half of it the window already covers is a gesture whose only effect\n // is work to undo. Aero's answer is the one worth copying: the top edge\n // is the \"make this as big as it goes\" edge.\n //\n // R13 read that as the GEOMETRIC maximise and made \"as big as it goes\"\n // mean THE LAYER. The product owner overruled it \u2014 *\"putting a window\n // to maximize > maximize here means back to tile\"* \u2014 and, asked how far\n // that went, chose everywhere, adding *\"this is not yet correct for the\n // snap at top gesture\"*, which names this zone as the part still wrong.\n // So there is now ONE meaning of maximise in the whole layer: the\n // window stops being a window and its content goes back into a tile.\n // The chrome button, the title bar's double-click, the embedder's\n // window menu and this edge are four doors onto one verb, which is the\n // only arrangement in which a user learns it once. No geometric\n // maximise remains reachable by gesture; `toggleMaximize({claimable:\n // false})` stays on `ManagedWindow` as the library escape hatch for a\n // consumer that wants the rectangle, and nothing in here calls it.\n //\n // THE RECT IS THE TILE, and it must be the tile the DOCK picks rather\n // than the tile the pointer happens to be in. That is C15's rule, which\n // R13 obeyed in the other direction, and it is why this branch resolves\n // its destination up front instead of committing blind:\n // `_homeDockTarget` is `bringBackWindow`'s own resolution read out, so\n // the preview and the drop cannot disagree \u2014 one function, called from\n // both halves.\n //\n // NOTHING TO PROMISE, NOTHING DRAWN. A window the WM never adopted, a\n // home leaf on a desktop that is not showing, a layout that has not\n // happened: `_homeDockTarget` answers null and the top edge falls\n // SILENT, which is exactly what the CENTRE of the window's own pane\n // already does (R2's surviving half, a dozen lines below). That is not\n // the dead-control failure the disabled-item convention exists to\n // avoid \u2014 the release still runs `_endDragEscape`, which puts the\n // window back where the drag found it, a visible outcome and the same\n // one the centre gives. Arming, previewing a tile and THEN declining on\n // release is the bait-and-switch, and it is the thing forbidden here.\n if (own && side === 'top') {\n const home = this._homeDockTarget(win);\n if (!home) return null;\n return { key: `${home.leafId}:home`, rect: home.rect,\n leafId: home.leafId, desktopIdx, side, mode: 'home',\n leafRect: r };\n }\n return this._dropZoneFor({ leafId, leaf, r, side, own, desktopIdx });\n }\n\n /**\n * R17 (C33). THE ZONE MATRIX'S TAIL \u2014 SPLIT / NOTHING / TAB / FILL \u2014 SHARED\n * BY THE TWO THINGS THAT CAN BE DROPPED ON A TILE.\n *\n * A dragged WINDOW and a dragged TAB ask the same question of a pane: given\n * that the pointer is in this leaf and the edge test answered `side`, what\n * would releasing here produce? Every answer below was written for the\n * window drop and every one of them is right for a tab, so this is an\n * extraction and not a generalisation \u2014 `_snapProbe` keeps everything ABOVE\n * it unchanged, including R14's `own && side === 'top'` home branch, which\n * is a window's alone (a tab has no window to bring back) and therefore\n * stays where it was, between the side computation and this call.\n *\n * The alternative was a second copy in `tabDropProbe`, and a second copy of\n * a matrix the product owner has already revised four times (R2, R4, R5/R6,\n * R14) is a guarantee that the two gestures will one day disagree about\n * what the centre of a start tile means. `web/js/shell/snap_zones.test.mjs`\n * in the Tables consumer asserts every cell of the window matrix and is the\n * regression gate on this extraction: byte-identical window behaviour is\n * the whole of its back-compatibility claim.\n */\n _dropZoneFor({ leafId, leaf, r, side, own, desktopIdx }) {\n if (side) {\n return { key: `${leafId}:${side}`, rect: _halfOf(r, side),\n leafId, desktopIdx, side, mode: 'split', leafRect: r };\n }\n // R2, THE HALF THAT SURVIVED. Past the edge bands, inside the pane it\n // came from, a window is being MOVED and not docked: no key, no\n // preview, and on release `_endDragEscape` simply puts it back where\n // the drag left it. This sits after the edges and before the centre\n // because it is only the centre it refuses \u2014 an own-pane leaf with no\n // content has no edges either (`side` stays null above), and refusing\n // that is right for the same reason: there is nothing to dock into that\n // the window is not already standing on.\n //\n // A TAB READS THIS CELL THE SAME WAY, arrived at from the other side:\n // dropping a tab into the pane it already lives in is a gesture whose\n // only effect is work to undo, which is R2's argument with the word\n // \"nudge\" removed.\n if (own) return null;\n\n // R5/R6. THE CENTRE IS NO LONGER NOTHING \u2014 it is the other two thirds\n // of the model, and which one it is depends on what the pane already is.\n //\n // a pane with content \u2192 a new TAB in it. Non-destructive, and the\n // honest reading of \"dropping where an\n // existing tile is would cause a new tab\".\n // a START tile \u2192 the window FILLS the pane, which is the\n // same verb as \"back to tile\": the window\n // stops floating and becomes that pane's\n // content. Under R14 that is what the top\n // edge of the window's OWN pane now means as\n // well \u2014 the difference between the two is\n // only which tile is named, this one the pane\n // under the pointer and R14's the pane the\n // window came from. A start tile is ground \u2014\n // there is nothing there to lose, which is\n // exactly why this one may replace rather\n // than append.\n //\n // The preview is the WHOLE pane for both, because the whole pane is\n // what the window ends up occupying either way. C15's rule stands: the\n // preview may not promise a rectangle the drop does not deliver.\n const fills = !leaf.content || this._isStartTile(leaf);\n return { key: `${leafId}:${fills ? 'fill' : 'tab'}`, rect: _halfOf(r, null),\n leafId, desktopIdx, side: null,\n mode: fills ? 'fill' : 'tab', leafRect: r };\n }\n\n /**\n * R18 (C33). THE SAME PROBE, FOR A DRAGGED TAB \u2014 PUBLIC, because the\n * renderer is what holds the drag and the renderer is not the WM.\n *\n * \u2550\u2550 WHY THIS IS NOT `_snapProbe(e, null)` \u2550\u2550\u2550\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 * It very nearly is, and the geometry underneath is literally the same\n * code: `_snapSide(r, e, null, false)` falls to the POINTER-distance branch\n * by construction \u2014 `_draggedRect(null)` is null, so `dist` takes the\n * `e.clientX/Y` arm and `along` scores every edge zero. A tab has no\n * rectangle being dragged and no `_dragState`, and that is not a gap to\n * paper over: the pointer IS the whole gesture for a tab, which is exactly\n * the pre-R15 model that `_snapSide`'s fallback preserves.\n *\n * Two things differ, and neither could be expressed by passing a null\n * window to `_snapProbe`:\n *\n * R14's HOME BRANCH IS A WINDOW'S. `own && side === 'top'` means \"put the\n * window back in its tile\", and a tab is already in a tile. Reaching that\n * branch with `win === null` would ask `_homeDockTarget(null)`, which\n * answers null, so the top edge of the source pane would fall silent\n * rather than split \u2014 a hole in the matrix produced by inheritance.\n *\n * A SINGLE-TAB SOURCE PANE ARMS NOTHING, ANYWHERE. `_dropZoneFor` already\n * silences the source pane's CENTRE; its edges are useful for a pane with\n * siblings (\"tear this tab off into a split beside the others\") and are a\n * wash for a pane with one tab, where the outcome is the pane's only\n * content in one half and a freshly seeded ground in the other. That is a\n * preview promising something no one wants, so it is refused BEFORE the\n * preview is drawn rather than at the drop \u2014 C15's rule is that the\n * rectangle drawn is the one released, and the honest way to keep it is\n * never to draw one.\n *\n * `own: false` is passed to `_snapSide` deliberately. Its `own` parameter\n * gates R2's direction guard, which measures a WINDOW's travel out of\n * `_dragState`; a tab drag has none, so `guarded` would be false anyway and\n * passing `true` would only obscure that. `own` still governs the centre,\n * which is why it goes to `_dropZoneFor` and not to `_snapSide`.\n *\n * @param {{clientX: number, clientY: number}} e the pointer, mid-drag\n * @param {{sourceLeafId?: string}} [opts] the leaf the tab left\n * @returns {object|null} the same probe shape a window drop produces\n */\n tabDropProbe(e, { sourceLeafId = null } = {}) {\n const leafEl = this._leafElAt(e.clientX, e.clientY, null);\n if (!leafEl) return null;\n const leafId = leafEl.dataset.leafId;\n const tree = this._tree();\n const leaf = tree.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return null;\n // Panel tiles are chrome, not content \u2014 the same refusal `_snapProbe`\n // makes, for the same reason: there is nowhere for the drop to go and\n // the preview must not suggest there is.\n if (String(leaf.content?.kind || '').startsWith('panel:')) return null;\n const own = !!sourceLeafId && leafId === sourceLeafId;\n if (own) {\n const src = tree.get(sourceLeafId);\n const count = Array.isArray(src?.tabs) ? src.tabs.length : 0;\n if (count <= 1) return null;\n }\n const r = leafEl.getBoundingClientRect();\n const side = leaf.content ? this._snapSide(r, e, null, false) : null;\n return this._dropZoneFor({ leafId, leaf, r, side, own,\n desktopIdx: this.desktops.activeIdx });\n }\n\n /**\n * R15. WHICH EDGE OF THE PANE THE *WINDOW* IS BEING PUSHED INTO.\n *\n * \u2550\u2550 THE BUG THIS EXISTS TO FIX \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * The band was measured from the POINTER, and the pointer is wherever the\n * hand happened to grab the title bar. Grab a 900px window in the middle\n * of its bar and shove it right: `dragBounds` clamps it, its right border\n * sits hard against the layer's right edge, and the pointer is still 450px\n * away from that edge \u2014 outside every band, so nothing arms and the window\n * simply stops dead against the side of the screen. Reported twice:\n * *\"snapping enables based on mouse position but actually it needs to\n * enable based on the dragged window bounds (e.g. window right border\n * distance from right snapping area)\"*.\n *\n * The bigger the window the worse it got, and the gesture only ever worked\n * if you happened to grab near the edge you were aiming at \u2014 the bottom\n * edge was effectively unreachable for any tall window, because a title bar\n * is at the TOP of the thing you are dragging.\n *\n * So each of the four distances is now between the window's own border and\n * the matching border of the pane. `right` arms when the window's right\n * border comes within the band of the pane's right border, and so on round.\n *\n * \u2550\u2550 WHAT DID *NOT* CHANGE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * WHICH PANE is still the pointer's answer (`_leafElAt`), and so is `own`.\n * The zone matrix is about a pane \u2014 the window's own pane means something\n * different from any other pane \u2014 and a window can lie across three of\n * them at once while the pointer is in exactly one. Only the question\n * *\"which edge of THIS pane\"* moved onto the window's rectangle; the\n * question *\"which pane\"* was never the one the product owner complained\n * about. Everything downstream is untouched: the preview is still\n * `_halfOf(paneRect, side)`, so the rectangle drawn is the rectangle the\n * drop delivers, and a `side` reaching the branches below means exactly\n * what it meant before.\n *\n * \u2550\u2550 SHORTFALL CLAMPED AT ZERO, BECAUSE A WINDOW OVERHANGS \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * (R16 corrects R15 here. R15 said *unsigned*, and unsigned was wrong;\n * the paragraph below is why, and `_snapSide` carries the measurement.)\n *\n * The pointer is inside the pane by construction \u2014 `_leafElAt` found the\n * pane by hit-testing it \u2014 so a signed distance was always positive. A\n * WINDOW has no such guarantee: it is clamped to the layer, not to the\n * pane, so a window wider than the pane under the pointer sticks out of\n * both sides of it and its border is 20px PAST the pane's border rather\n * than 20px short of it. Both readings are \"hard against that edge\".\n *\n * R15 spelled that `Math.abs`, and `Math.abs` only holds the reading while\n * the overhang stays inside the band. Past that it counts UP again, so the\n * zone armed and then DISARMED as the shove continued, and a window\n * meaningfully wider than the pane armed nothing at all. The right spelling\n * is a shortfall clamped at zero: **past the edge IS the edge**, at\n * distance zero, and it stays there however far the shove carries it.\n *\n * \u2550\u2550 THE DIRECTION GUARD, WHICH IS WHAT KEEPS R2 ALIVE \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * Edge-based testing has a failure the pointer never had: a window that is\n * ALREADY at an edge is in that band before the drag starts. A window\n * parked at the left of its pane would arm a left split on the first\n * millimetre of any drag, and a window that fills its pane would arm on\n * every drag in every direction \u2014 which is precisely the *\"every move\n * looked like a dock because every move WAS one\"* failure `SNAP_EDGE_PX`\n * was written to end, arriving from the other direction.\n *\n * So in the window's OWN pane an edge arms only if the drag actually\n * carried the window at it: the pointer must have travelled more than one\n * band's width toward that edge since the press. A nudge (R2's complaint,\n * and the surviving reason the own-pane centre is silent) moves a handful\n * of pixels and arms nothing; a shove moves hundreds and arms the edge it\n * was aimed at. The band's own width is the unit, because a movement\n * smaller than the band cannot be the difference between being in it and\n * not.\n *\n * IN ANY OTHER PANE THE GUARD IS OFF, deliberately. R2 is a rule about the\n * pane a window already lives on \u2014 the only place a \"nudge\" exists. Drag a\n * window rightwards out of pane A and into pane B and its LEFT border is\n * what enters pane B first: with the guard on, aiming at the left half of\n * the pane to your right would be impossible, since arriving there always\n * means travelling right. The window is translucent by then (R3) and every\n * drop on a foreign pane docks, so there is no nudge to protect.\n *\n * The displacement is read from `ManagedWindow._dragState.startX/startY`,\n * the POINTER's position at the press \u2014 not from the window's own x/y,\n * which stop changing the moment the clamp bites while the gesture very\n * much continues. A caller with no drag state (a synthetic probe, an\n * embedder driving the controller by hand) yields no displacement at all\n * and the guard is skipped rather than failing closed: it can only ever\n * suppress an edge, never invent one.\n *\n * \u2550\u2550 CORNERS: THE PRECEDENCE, MADE EXPLICIT \u2550\u2550\u2550\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 * Two edges can be in range at once, and with window borders that is no\n * longer the rarity it was with a pointer \u2014 shove a window into a corner\n * and the clamp puts BOTH borders at distance zero, exactly. Under R16 it\n * is not even a corner case: a window as wide as its pane is at zero on\n * the left AND the right for every horizontal position it can occupy, and\n * a floated canvas pane's window is *exactly* that wide. So the order is\n * stated rather than left to whichever way the loop happens to run:\n *\n * 1. NEAREST WINS. Unchanged from R4, and it is what keeps a corner from\n * being a dead spot: one of the two is always closer.\n * 2. ON A TIE, THE EDGE THE DRAG PUSHED TOWARD WINS. (R16: *toward that\n * edge*, signed \u2014 R15 said \"the axis pushed furthest\" and spelled it\n * `Math.abs`, which gives the two ends of one axis the SAME score, so\n * a left/right tie never broke at all and 'left' won every time by\n * loop order.) The honest tie-break is the gesture: shove it\n * rightwards and you get the right zone, upwards and you get the top\n * zone. Every zone stays reachable and which one you get is something\n * a hand can aim.\n * 3. STILL TIED \u2014 a perfect diagonal, or a probe with no drag state \u2014\n * falls to the fixed order left, right, top, bottom. That is the order\n * the R4 loop already resolved ties in (`Object.entries` insertion\n * order, with a strict `<`), kept so the pointer fallback below\n * answers exactly what it answered before.\n *\n * \u2550\u2550 THE FALLBACK \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 * With no measurable window rectangle \u2014 no element, detached, or a box of\n * zero area because layout has not happened \u2014 there is nothing to measure\n * and the pointer is the only information in the room. That path is the\n * pre-R15 code, unchanged, signed distances and all. It is what a headless\n * probe gets (jsdom lays nothing out, so every `getBoundingClientRect` is\n * zero), and it is why `web/js/shell/snap_zones.test.mjs` in the Tables\n * consumer still asserts the same matrix against the same coordinates.\n *\n * @param {DOMRect} r the pane, in viewport pixels\n * @param {{clientX: number, clientY: number}} e the pointer\n * @param {object} win the ManagedWindow being dragged\n * @param {boolean} own is `r` the pane this window's drag escaped?\n * @returns {'left'|'right'|'top'|'bottom'|null}\n */\n _snapSide(r, e, win, own) {\n const edge = WindowManager.SNAP_EDGE_PX;\n const w = this._draggedRect(win);\n // The displacement is read whenever there is one, because BOTH rules\n // below want it and they do not want it in the same places: the corner\n // tie-break is about aim and applies in every pane, while the guard is\n // R2's rule about nudging a window at home and applies in one.\n const push = w ? this._dragPush(e, win) : null;\n const guarded = !!(own && push);\n // R16. SHORTFALL, CLAMPED AT ZERO \u2014 *not* `Math.abs`.\n //\n // R15 measured `Math.abs(r.right - w.right)`, which conflates the two\n // sides of an edge: a border 20px SHORT of the pane's border and one\n // 20px PAST it both read as 20. The reading past the edge is wrong,\n // and wrong in the direction that breaks the gesture. Two defects came\n // out of it and both are this one line of arithmetic:\n //\n // THE BAND OPENED AND THEN CLOSED AGAIN. Shove a window right; its\n // right border approaches the pane's right border, enters the 28px\n // band, reaches it \u2014 and keeps going. One pixel past, `abs` starts\n // counting UP again, and 28px past the edge the zone disarms.\n // Measured on the real code: a 300px window in PANE_A armed `right`\n // only for pointer deltas 173..227, and was null on either side. **A\n // firm shove disarmed the snap that a hesitant one armed** \u2014 the\n // product owner's original complaint arriving from the far side.\n //\n // AND A WINDOW WIDER THAN THE PANE ARMED NOTHING \u2014 though that case\n // is exactly what `abs` was reached for. The R15 docstring argues\n // for it because such a window \"sticks out of both sides and its\n // border is 20px PAST the pane's rather than 20px short\", which is\n // true; what it misses is that `abs` holds that reading only while\n // the overhang stays under 28px. Overhang by more and every edge\n // reads out-of-band.\n //\n // Clamping at zero says what that prose always meant: **past the edge\n // IS the edge.** A border level with the pane's, or beyond it, sits at\n // distance zero and is fully armed, and stays armed however much\n // further the shove carries it. \"How hard are you pushing past it\" was\n // never a measure of aim.\n //\n // This makes a same-axis TIE ordinary rather than a corner curiosity:\n // a window wider than its pane is hard against the left and the right\n // border at once, both at zero. Breaking that tie is what the signed\n // `along` below is for, and the two changes only make sense together.\n //\n // The pointer fallback is untouched: the pointer is inside the pane by\n // construction \u2014 `_leafElAt` hit-tested it \u2014 so its distances cannot\n // go negative and there is nothing to clamp.\n const dist = w\n ? { left: Math.max(0, w.left - r.left),\n right: Math.max(0, r.right - w.right),\n top: Math.max(0, w.top - r.top),\n bottom: Math.max(0, r.bottom - w.bottom) }\n : { left: e.clientX - r.left,\n right: r.right - e.clientX,\n top: e.clientY - r.top,\n bottom: r.bottom - e.clientY };\n const toward = (name) => {\n if (!guarded) return true;\n if (name === 'left') return push.x <= -edge;\n if (name === 'right') return push.x >= edge;\n if (name === 'top') return push.y <= -edge;\n return push.y >= edge;\n };\n // R16. HOW FAR THE GESTURE TRAVELLED **TOWARD THIS EDGE** \u2014 signed to\n // the edge it is asked about, not to its axis.\n //\n // R15 wrote `Math.abs(push.x)` for both 'left' and 'right', which\n // makes the tie-break IDENTICAL for the two members of a same-axis\n // pair. `p > best.p` is then `|push.x| > |push.x|` \u2014 false, always \u2014\n // so the tie fell through to the fixed loop order and **'left' won\n // every time, whichever way the window was shoved**.\n //\n // That was not the corner curiosity it looked like. Whenever a\n // window's width equals the pane's, `dist.left === dist.right` at\n // EVERY horizontal position, so the whole band answered 'left'; and a\n // canvas pane's window is sized `min(820, container.clientWidth)`,\n // which for any pane up to 822px wide comes out exactly equal to the\n // leaf's border-box width. So the common case \u2014 float a pane's window\n // and push it at the pane next door \u2014 always split left. Under R16's\n // clamped distance the tie is commoner still, because a window wider\n // than its pane now ties at zero on both edges by construction.\n //\n // Signing it makes the tie-break say what rule 2 always claimed:\n // *\"the axis the drag pushed furthest wins\"* \u2014 read as the DIRECTION\n // the drag pushed, which is the only reading a hand can aim. Shove\n // right, get right. A push away from the edge scores negative and\n // loses to any edge that was actually aimed at.\n //\n // Still zero for every edge when there is no drag state, so rule 3\n // falls through to the fixed order exactly as before.\n const along = (name) => {\n const dx = push?.x ?? 0, dy = push?.y ?? 0;\n if (name === 'left') return -dx;\n if (name === 'right') return dx;\n if (name === 'top') return -dy;\n return dy;\n };\n // THE FIXED ORDER IS RULE 3. Do not sort this array.\n let best = null;\n for (const name of ['left', 'right', 'top', 'bottom']) {\n const d = dist[name];\n if (!(d < edge) || !toward(name)) continue;\n const p = along(name);\n if (!best || d < best.d || (d === best.d && p > best.p)) best = { name, d, p };\n }\n return best ? best.name : null;\n }\n\n /** R15. The dragged window's rectangle in VIEWPORT pixels \u2014 the frame a\n * leaf's `getBoundingClientRect` speaks, so the two are directly\n * comparable \u2014 or null when there is nothing to measure.\n *\n * Read off the element rather than computed from `win.x/y/width/height`,\n * because those are in whatever container the window is currently parented\n * to and mid-drag that is the drag host, not the pane being probed.\n *\n * A zero-area box is \"nothing to measure\" rather than a rectangle at the\n * origin: it is what an unlaid-out document gives, and treating it as real\n * would put every window in the top-left corner of every pane. Same test\n * `_homeDockTarget` applies to a leaf, for the same reason. */\n _draggedRect(win) {\n const el = win?.element;\n if (!el || el.isConnected === false) return null;\n if (typeof el.getBoundingClientRect !== 'function') return null;\n const b = el.getBoundingClientRect();\n if (!b || !b.width || !b.height) return null;\n return b;\n }\n\n /** R15. How far the POINTER has travelled since the press that began this\n * drag, or null if this window is not in a drag the WM can see.\n *\n * The pointer rather than the window: `_applyPosition` clamps the window\n * to `dragBounds`, so a window shoved at the edge of the layer stops\n * moving while the gesture continues \u2014 and \"it stopped because it is\n * against the edge\" is exactly the situation the guard must not read as\n * \"it is not being pushed\". */\n _dragPush(e, win) {\n const ds = win?._dragState;\n if (!ds || typeof ds.startX !== 'number' || typeof ds.startY !== 'number') return null;\n return { x: e.clientX - ds.startX, y: e.clientY - ds.startY };\n }\n\n /** The topmost `.twm-leaf` under the pointer that is not part of the window\n * being dragged. `elementsFromPoint` rather than `elementFromPoint`: the\n * dragged window IS under the pointer \u2014 it is what the pointer is holding\n * \u2014 and a hit-test that stops at the first element only ever finds it.\n *\n * R15 left this alone on purpose: WHICH pane is still the pointer's\n * answer, and only WHICH EDGE of it moved onto the window's borders. See\n * `_snapSide`. */\n _leafElAt(x, y, win) {\n const stack = document.elementsFromPoint(x, y);\n for (const el of stack) {\n if (win?.element && win.element.contains(el)) continue;\n const leafEl = el.closest?.('.twm-leaf');\n if (leafEl && this.rootEl.contains(leafEl)) return leafEl;\n }\n return null;\n }\n\n /**\n * R14. THE TILE \"BACK TO TILE\" WOULD PUT THIS WINDOW IN, and the rectangle\n * that draws it \u2014 in the VIEWPORT pixels a snap preview is positioned in.\n *\n * `bringBackWindow` resolves its destination privately and then closes the\n * window to reach it, which is everything a button press needs and useless\n * to a PREVIEW. C15's rule is that the rectangle drawn during a drag is the\n * one the drop delivers, and under R14 the drop delivers A TILE \u2014 so the\n * resolution has to be readable before the gesture is committed. Reading it\n * out here is what makes the top edge honest: the probe draws what this\n * returns and `_snapCommit` calls `bringBackWindow`, which resolves the\n * same way, from the same record, against the same tree.\n *\n * It is deliberately NOT a second copy of that resolution reduced to \"the\n * home leaf\". `bringBackWindow`'s fall-through \u2014 no home leaf, or one the\n * tree no longer has \u2014 is the desktop's PRIMARY tile, which is where\n * `_onManagedWindowClosed` sends a demotion carrying no `_dock`; a probe\n * that previewed the home leaf and then landed in the primary tile would be\n * the bait-and-switch with extra steps.\n *\n * `renderer.leafEl` only knows the leaves of the desktop currently on\n * screen, which is the property that makes the desktop check implicit: a\n * window whose home is on another desktop resolves to no element, this\n * answers null, and the top edge arms nothing rather than previewing a\n * rectangle on a desktop the user cannot see.\n *\n * @param {object} win a live ManagedWindow\n * @returns {{leafId: string, rect: DOMRect}|null} null when there is\n * nothing honest to promise: a window the WM never adopted, a desktop\n * that has gone, a tree with no content leaf at all, or a leaf with no\n * measurable box (a layout that has not happened yet).\n */\n _homeDockTarget(win) {\n const rec = [...this._windowToLeaf.values()].find((r) => r.window === win);\n if (!rec) return null;\n const tree = this.desktops.desktops[rec.desktopIdx]?.tree;\n if (!tree) return null;\n const home = rec.homeLeafId ? tree.get(rec.homeLeafId) : null;\n const leafId = (home && home.kind === 'leaf')\n ? rec.homeLeafId\n : tree.primaryLeafId();\n if (!leafId) return null;\n const el = this.renderer.leafEl(leafId);\n if (!el) return null;\n const rect = el.getBoundingClientRect();\n if (!rect.width || !rect.height) return null;\n return { leafId, rect };\n }\n\n _snapCommit(probe, win) {\n if (!probe) return false;\n const rec = [...this._windowToLeaf.entries()]\n .find(([, r]) => r.window === win);\n if (!rec) return false;\n const [winId, record] = rec;\n const { leafId, desktopIdx, side } = probe;\n\n // R14. THE TOP EDGE OF THE WINDOW'S OWN PANE DOCKS IT HOME.\n //\n // It goes through `bringBackWindow` rather than through\n // `dockWindowInto` below, and that is the point rather than a shortcut:\n // \"back to tile\" is ONE verb with one implementation, and it is the\n // implementation that knows a home pane which has since acquired\n // content must be joined as a tab instead of replaced. Naming the leaf\n // here and calling `dockWindowInto` would be a second copy of that\n // rule, and a second copy is a copy that disagrees within a release.\n //\n // IT RETURNS BEFORE `record.desktopIdx` IS REWRITTEN below, for the\n // reason R13's branch did: the destination was resolved from the\n // RECORD's own desktop by `_homeDockTarget`, so rewriting the record to\n // the desktop the pointer is on would send `bringBackWindow` looking\n // for the home leaf in a tree that does not contain it \u2014 and it would\n // then fall through to that desktop's primary tile, which is not the\n // rectangle the preview drew.\n //\n // WHAT USED TO BE HERE: `toggleMaximize({ claimable: false })`, the\n // geometric maximise, with `{claimable: false}` load-bearing precisely\n // so the call would NOT reach `bringBackWindow`. The ruling inverts\n // that \u2014 the dock is now the whole intent \u2014 so the escape hatch is\n // gone from this file altogether. It stays on `ManagedWindow` as a\n // public API for a consumer that genuinely wants a rectangle (a dialog\n // has no tile to go back to, so `openModal`'s maximise is exactly\n // that); nothing in the tiling layer calls it any more.\n if (probe.mode === 'home') {\n // NOT TAKEN UNLESS IT REALLY WAS \u2014 the same rule the dock below\n // follows, and reachable here: `bringBackWindow` returns false for\n // a window whose record has gone between the probe and the release.\n // Claiming the drop after it did nothing would leave the preview\n // painted over the page with nothing left to remove it, and the\n // window abandoned wherever the pointer let go; returning false\n // hands the release back to `_endDragEscape`, which puts the window\n // back in its pane.\n if (!this.bringBackWindow(winId)) return false;\n win.clearSnapPreview();\n return true;\n }\n\n // The window's home follows the drop. Without this the dock is applied\n // to the tree of the desktop the window was PROMOTED from, and the tile\n // the user aimed at is on the one they are looking at.\n if (typeof desktopIdx === 'number') record.desktopIdx = desktopIdx;\n\n // THE DRAG ALREADY ANSWERED, so there is nothing to ask \u2014 the probe\n // decided between the outcomes on the way in, and the preview has\n // been drawing that answer for as long as the pointer has been there. A\n // menu on release would be asking a question the user has already spent\n // the whole drag answering.\n //\n // `dir` and `before` are read only by the split branch; a tab or a fill\n // ignores them, which is why they can be computed unconditionally from\n // a `side` that is null for both.\n const docked = this.dockWindowInto(winId, {\n leafId,\n mode: probe.mode || 'split',\n dir: (side === 'left' || side === 'right') ? 'h' : 'v',\n before: (side === 'left' || side === 'top'),\n });\n // NOT TAKEN unless it really was. A truthy return tells the window the\n // controller owns it AND owns the preview, so returning true after a\n // failed dock would leave a rectangle painted over the page with\n // nothing left to remove it, and a window sitting wherever the drag\n // abandoned it instead of back in its pane.\n if (!docked) return false;\n win.clearSnapPreview();\n return true;\n }\n\n /**\n * Put a floating window's content back into the tree at a NAMED place.\n *\n * `bringBackWindow` is this with `{ mode: 'tab' }` against the primary tile\n * \u2014 the answer when the user pressed a button in the window's own chrome\n * and named no destination. A drop names one.\n *\n * The window is CLOSED to do it, exactly as a demote is: the content\n * factory re-mounts inside the tile, and a factory that must not lose live\n * state across that boundary is the embedder's problem to solve (it is why\n * the registry is keyed on (kind, props) rather than on a DOM node).\n *\n * @param {string} windowId\n * @param {{leafId: string, mode: 'fill'|'tab'|'split', dir?: 'h'|'v',\n * before?: boolean}} target\n */\n dockWindowInto(windowId, target) {\n const rec = this._windowToLeaf.get(windowId);\n if (!rec || !target?.leafId) return false;\n rec._dock = { ...target };\n rec._demoting = true;\n try { rec.window.close({ force: true }); }\n catch (err) { console.warn('[wm] dock: close failed', err); return false; }\n return true;\n }\n\n /**\n * Move a managed window to another desktop.\n *\n * \u2550\u2550 IT USED TO REWRITE ONE INTEGER, AND THAT MOVED NOTHING \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n *\n * The sentence that stood here \u2014 *\"the window itself stays on screen\n * (managed windows are global)\"* \u2014 was true of a window floating over the\n * root and false of every window this WM promotes under `promoteInPlace`,\n * which is CONTAINED IN A TILE (C21). Rewriting `desktopIdx` left such a\n * window standing in the pane it was already in, on the page the user was\n * already looking at: *Move to desktop Views* appeared to do nothing at\n * all. Then *Back to tile* resolved against the new desktop's tree and\n * docked the content onto a page nobody was watching, so the window\n * vanished here and its table turned up over there.\n *\n * Three things move it for real. The index, so every later resolution\n * agrees. The HOME LEAF, re-pointed at a ground that exists in the\n * destination \u2014 without it the record names a leaf of the tree it just\n * left, and `_rehomeContainedWindows` would either skip it forever or\n * repair it to a pane on the wrong page. And a render, which is where the\n * element is taken off the page the window has left (or parented into its\n * new ground, when the destination is the desktop on screen).\n *\n * An ADOPTED window (`homeContainer`) keeps its own resolution: the\n * embedder owns the box it stands in, and re-pointing a leaf id it does not\n * read would be a change with no effect wearing the look of one.\n */\n moveWindowToDesktop(windowId, targetIdx) {\n const rec = this._windowToLeaf.get(windowId);\n if (!rec) return;\n if (rec.desktopIdx === targetIdx) return;\n this.desktops.ensureCount(targetIdx + 1);\n rec.desktopIdx = targetIdx;\n if (!rec.homeContainer && rec.homeLeafId) {\n const target = this.desktops.desktops[targetIdx]?.tree;\n // `|| null`, NEVER `|| rec.homeLeafId`. Falling back to the id it\n // already had keeps a leaf of the tree the window is LEAVING, and\n // `_rehomeContainedWindows` would then find that id perfectly alive\n // in the wrong tree and never repair it. Null means \"not contained\n // anywhere\", which the pass understands and which the repair below\n // fixes on the first render of the destination.\n rec.homeLeafId = target?.primaryLeafId() || null;\n }\n this.renderer.render();\n this._persist();\n this._notifyChange('window-moved');\n }\n\n /**\n * R8. Put a floated pane's tabs back into a leaf \u2014 ALL of them, in the\n * order they had, with the one that was showing still showing.\n *\n * Every dock goes through here, and that is the point: `bringBackWindow`,\n * a drop on a tile's body, a drop on an edge and the fall-through when the\n * named destination vanished are four routes to one question \u2014 *where do\n * these tabs go* \u2014 and four copies of the answer would disagree about the\n * third one within a release. A window promoted before R8 (or by\n * `_navigateWindow`, which never had tabs) carries no list, so `original`\n * is the fallback and the single-tab path reduces to exactly what this\n * replaced.\n *\n * `replace` is the difference between filling a leaf and joining one: a\n * fresh split leaf and a `fill` drop want the first tab to BECOME the\n * leaf's content, while a `tab` drop and \"back to tile\" append beside what\n * is already there.\n */\n _restoreTabs(tree, leafId, rec, { replace }) {\n if (!leafId) return false;\n const tabs = (Array.isArray(rec.tabs) && rec.tabs.length)\n ? rec.tabs\n : [{ kind: rec.original.kind, props: rec.original.props,\n title: rec.original.title }];\n const active = Math.max(0, Math.min(tabs.length - 1, rec.activeTabIdx || 0));\n let firstIdx = -1;\n tabs.forEach((tab, i) => {\n const content = { kind: tab.kind, props: tab.props || {} };\n const title = tab.title || tab.kind || '';\n if (i === 0 && replace) {\n tree.setLeafContent(leafId, content, title);\n firstIdx = 0;\n return;\n }\n // `appendLeafTab` returns -1 for a panel leaf, which is the one\n // leaf that never grows tabs. Nothing docks onto a panel \u2014 the\n // probe refuses one and `bringBackWindow` targets the primary tile\n // \u2014 so this is a guard, not a path.\n const at = tree.appendLeafTab(leafId, content, title);\n if (at >= 0 && firstIdx < 0) firstIdx = at;\n });\n if (firstIdx < 0) return false;\n tree.setActiveLeafTab(leafId, firstIdx + active);\n tree.focus(leafId);\n return true;\n }\n\n _onManagedWindowClosed(winId, mountInfo) {\n const rec = this._windowToLeaf.get(winId);\n this._windowToLeaf.delete(winId);\n if (!rec) return;\n // R8. `rec.mountInfo` IS THE LIVE ONE. The window's mount changes when\n // its tab changes (`_mountWindowTab`) and when its content is replaced\n // in place (`openInWindow`), so the closure `_promote` captured at\n // build time names a mount that was torn down long ago \u2014 destroying it\n // twice while the CURRENT one is never destroyed at all, which is a\n // leaked page module with a `window` listener on it. `_navigateWindow`\n // still passes its own and it is the same object, so the argument\n // stays for the callers that have nothing else.\n try { (rec.mountInfo || mountInfo)?.destroy?.(); } catch {}\n try { rec.strip?.dispose(); } catch { /* never mounted, or already gone */ }\n const tree = this.desktops.desktops[rec.desktopIdx]?.tree;\n if (!tree) return;\n\n if (rec._demoting && rec._dock) {\n // C15. A DROP named its destination, so none of the primary-tile\n // reasoning below applies. A destination that vanished between the\n // release and the close (the tile was closed by a keystroke while a\n // choice menu was open) falls through to the primary tile rather\n // than dropping the content on the floor.\n const target = tree.get(rec._dock.leafId);\n if (target && target.kind === 'leaf') {\n const { mode, dir, before } = rec._dock;\n if (mode === 'tab') {\n this._restoreTabs(tree, rec._dock.leafId, rec, { replace: false });\n } else if (mode === 'split') {\n const newId = tree.split(rec._dock.leafId, dir);\n if (newId) {\n this._restoreTabs(tree, newId, rec, { replace: true });\n // THE PREVIEW PROMISED HALF OF THAT TILE. `TileTree.split`\n // delivers it only when it has to WRAP; when the parent\n // split already runs this direction it INSERTS a sibling\n // and gives it the average of the row's existing sizes\n // (`tile_tree.js`, case 2), so a drop into a three-pane\n // row hands the newcomer a third and shrinks everyone\n // else. Halving the source's own weight and giving the\n // other half to the new leaf makes the two panes split\n // the space the source had \u2014 which is what was drawn.\n _halveInto(tree, rec._dock.leafId, newId);\n // `split` always appends the new leaf AFTER its source.\n // A drop on the LEFT or TOP edge means the window\n // belongs on that side, so the two swap places \u2014 the\n // preview drew the left half and the left half is where\n // it must land.\n if (before) _swapSiblings(tree, rec._dock.leafId, newId);\n tree.focus(newId);\n }\n } else {\n this._restoreTabs(tree, rec._dock.leafId, rec, { replace: true });\n }\n } else {\n this._restoreTabs(tree, tree.primaryLeafId(), rec, { replace: false });\n }\n } else if (rec._demoting) {\n // \"Back to tile\": the source tile was closed when the window was\n // promoted, so dock the (latest) content into the desktop's\n // primary tile as a new tab, spawning a tile when it has none.\n let pid = tree.primaryLeafId();\n let spawned = false;\n if (!pid) { pid = this._spawnContentLeaf(tree); spawned = true; }\n if (pid) {\n this._restoreTabs(tree, pid, rec, { replace: false });\n if (spawned) this._canonicalize(tree, this.desktops.desktops[rec.desktopIdx]);\n }\n }\n // Plain close (rec._demoting === false): the window and its content\n // are discarded. The source tile was already closed on promote, so\n // there is nothing left in the tree to clean up.\n if (this.desktops.active().tree === tree) this.renderer.render();\n this._persist();\n this._notifyChange(rec._demoting ? 'window-demoted' : 'window-closed');\n }\n\n /** Post-show DOM hook: wire a right-click context menu on the topbar.\n *\n * R7. IT USED TO INJECT A BUTTON HERE, and that is the whole of what\n * changed. \"Back to tile\" was a fourth button squeezed left of Close,\n * built by reaching into four of ManagedWindow's internal class names \u2014\n * the coupling C6 exists to avoid \u2014 and it sat next to a MAXIMIZE button\n * that did the one thing a window lifted out of a tile has no use for.\n * Now the maximize button IS \"back to tile\" (`onMaximize`, passed where\n * the window is built), so the verb has one control instead of two and\n * this hook has no markup of its own to keep in step.\n *\n * Gone with it: the rule that hid MINIMIZE while the window was maximised.\n * It existed because minimising a full-screen window strands it \u2014 nothing\n * on screen points at it any more \u2014 and a window that cannot maximise\n * cannot be in that state at all. `managed-window-maximized` (C16) still\n * fires for everyone else, and `--suppressed` is still styled for the next\n * consumer that needs to hide one of these. */\n _decorateManagedWindow(win, winId) {\n const topbar = win.element?.querySelector?.('.twm-managed-window__topbar');\n if (!topbar) return;\n\n topbar.addEventListener('contextmenu', (e) => {\n e.preventDefault();\n const rec = this._windowToLeaf.get(winId);\n if (!rec) return;\n const items = [\n { label: 'Back to tile', icon: 'close_fullscreen', action: 'back' },\n ];\n if (this.desktops.desktops.length > 1) {\n items.push({ separator: true });\n for (const [i, d] of this.desktops.desktops.entries()) {\n if (i === rec.desktopIdx) continue;\n items.push({\n label: `Move to desktop ${d.label}`, icon: 'sweep',\n action: `move:${i}`,\n });\n }\n }\n items.push({ separator: true });\n items.push({ label: 'Close window', icon: 'close',\n action: 'close', danger: true });\n showContextMenu(e.clientX, e.clientY, items, (action) => {\n if (action === 'back') this.bringBackWindow(winId);\n else if (action === 'close') {\n try { win.close({ force: true }); } catch {}\n }\n else if (action?.startsWith?.('move:')) {\n this.moveWindowToDesktop(winId, Number(action.slice(5)));\n }\n });\n });\n }\n\n // \u2550\u2550 R10. Adjacent start tiles are one start tile \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n /**\n * Merge every run of side-by-side START TILES into one.\n *\n * A start tile is a leaf holding the taxonomy ROOT \u2014 the pane a fresh\n * desktop opens with, and the pane `_seedHome` puts back when a tile is\n * emptied. It is not a document: it is the ground, the empty canvas, the\n * \"nothing is open here\" surface. So two of them side by side are ONE\n * surface with a splitter drawn through it for no reason, and the splitter\n * is worse than decoration \u2014 it offers to resize a boundary between two\n * things that are the same thing.\n *\n * This is deliberately NOT run on every tree change, and the reason is\n * `split()`: splitting a start tile seeds the new pane with the root kind\n * too (the never-empty-tile invariant), so a merge on every mutation would\n * undo an Alt+H the instant it happened. It runs where the product owner\n * put it \u2014 *\"when a maximized (tiled) panel is window-ized, all adjacent\n * non-panel (start tile) tiles get merged to one\"* \u2014 and is public so an\n * embedder that empties a pane its own way can ask for the same tidy-up.\n *\n * THREE THINGS ARE NEVER MERGED, and each one is a way to lose work:\n *\n * - `panel:*` leaves. They are chrome, not content; the navigator is not\n * a start tile and a panel BETWEEN two start tiles means those two are\n * not adjacent.\n * - A start tile with WINDOWS STANDING ON IT. The whole point of the\n * surface is that things float on it, and closing the leaf takes its\n * ground \u2014 and every window clamped to it \u2014 out of the document. When\n * one of a pair is occupied the other merges INTO it; when both are,\n * neither moves.\n * - A start tile holding tabs, live or archived. `leaf.content.kind`\n * names the ACTIVE tab only, and a pane whose other tabs are tables, or\n * whose `pageTabs` archive holds the three tables a rail click put\n * there, is a pane with work in it wearing a start tile's face.\n *\n * @param {TileTree} tree\n * @param {string|null} preferLeafId the leaf to keep when a run is\n * otherwise a free choice \u2014 the pane the caller just emptied, so the\n * merged surface is the one the user is looking at.\n * @returns {number} how many leaves were absorbed.\n */\n _mergeStartTiles(tree, preferLeafId = null) {\n if (!tree) return 0;\n let absorbed = 0;\n // Pairwise, restarting after each close. `TileTree.close` splices the\n // parent's children AND may collapse the parent into ITS parent, so\n // every index in flight is stale the moment one leaf goes; restarting\n // is the cheap way to be right rather than the clever way to be wrong.\n // It terminates because each pass removes exactly one leaf.\n for (;;) {\n const pair = this._nextMergeablePair(tree, preferLeafId);\n if (!pair) break;\n const { split, keepIdx, dropIdx, keepId, dropId } = pair;\n // THE SPACE GOES WITH IT. `close` splices the size out of the row,\n // which hands it to every sibling in proportion; the merged pane is\n // supposed to occupy what the two of them occupied, so the keeper\n // takes it first.\n split.sizes[keepIdx] = (split.sizes[keepIdx] || 1) + (split.sizes[dropIdx] || 1);\n const hadFocus = tree.focusedLeafId === dropId;\n tree.close(dropId);\n // A LOOP THAT CANNOT END IS WORSE THAN A SPLITTER NOBODY WANTED.\n // The loop's termination argument is \"each pass removes one leaf\";\n // if `close` ever declines \u2014 a leaf that is not a leaf, a tree\n // mutated underneath us \u2014 that argument fails silently and the tab\n // freezes. Stopping is the correct answer to a merge that did not\n // happen.\n if (tree.nodes.has(dropId)) break;\n if (hadFocus) tree.focus(keepId);\n absorbed += 1;\n }\n return absorbed;\n }\n\n /** The first two adjacent start tiles that may be merged, and which of\n * them survives. Null when there are none. */\n _nextMergeablePair(tree, preferLeafId) {\n for (const node of [...tree.nodes.values()]) {\n if (node.kind !== 'split') continue;\n // The snapshot above can name a split a previous pass collapsed.\n if (!tree.nodes.has(node.id)) continue;\n for (let i = 0; i < node.children.length - 1; i += 1) {\n const a = tree.get(node.children[i]);\n const b = tree.get(node.children[i + 1]);\n if (!this._isStartTile(a) || !this._isStartTile(b)) continue;\n const aHolds = this._paneHoldsWindows(a.id);\n const bHolds = this._paneHoldsWindows(b.id);\n if (aHolds && bHolds) continue;\n let keepIdx = i;\n if (bHolds) keepIdx = i + 1;\n else if (!aHolds && node.children[i + 1] === preferLeafId) keepIdx = i + 1;\n const dropIdx = keepIdx === i ? i + 1 : i;\n return { split: node, keepIdx, dropIdx,\n keepId: node.children[keepIdx], dropId: node.children[dropIdx] };\n }\n }\n return null;\n }\n\n /** Is this leaf the empty ground and nothing else? See the three\n * exclusions in `_mergeStartTiles`. */\n _isStartTile(leaf) {\n if (!leaf || leaf.kind !== 'leaf') return false;\n const kind = leaf.content?.kind;\n if (!kind || kind !== this.taxonomy.root) return false;\n if (PANEL_KINDS.has(kind) || kind === PLACEHOLDER_KIND) return false;\n if ((Array.isArray(leaf.tabs) ? leaf.tabs.length : 0) > 1) return false;\n for (const page of Object.values(leaf.pageTabs || {})) {\n if (Array.isArray(page?.tabs) && page.tabs.length) return false;\n }\n return true;\n }\n\n /**\n * Does anything float on this pane?\n *\n * Two sources, because there are two kinds of window and the WM only knows\n * about one of them. `homeLeafId` is set for a window this WM contained in\n * its own pane (C21); an EMBEDDER's windows \u2014 a canvas pane that opens its\n * own `ManagedWindow` against the pane's ground \u2014 are not in\n * `_windowToLeaf` at all, and the only honest way to see them is to look.\n * The DOM answer covers both, and covers a window whose record has been\n * dropped but whose element is still standing.\n */\n _paneHoldsWindows(leafId) {\n for (const [, rec] of this._windowToLeaf) {\n if (rec.homeLeafId === leafId) return true;\n }\n const el = this.renderer.leafEl?.(leafId);\n return !!el?.querySelector?.('.twm-managed-window');\n }\n\n /** R10, as a verb an embedder can use. Merges, then repaints and persists\n * \u2014 the promote path calls `_mergeStartTiles` directly because it is\n * already going to do all three. */\n mergeStartTiles(preferLeafId = null) {\n const tree = this._tree();\n const absorbed = this._mergeStartTiles(tree, preferLeafId);\n if (!absorbed) return 0;\n this._canonicalize(tree, this.desktops.active());\n this.renderer.render();\n this._persist();\n this._notifyChange('start-tiles-merged');\n return absorbed;\n }\n\n // \u2500\u2500 Panel-tiles (left nav / right / bottom) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n /** Panels are virtual: they live in the active desktop's tree as\n * leaves with content kinds 'panel:left', 'panel:right',\n * 'panel:bottom'. Toggling either inserts them at the appropriate\n * edge (a recursive split) or closes that leaf. */\n isPanelOpen(side) {\n const d = this.desktops.active();\n return !!d.panels[side];\n }\n\n togglePanel(side) {\n const d = this.desktops.active();\n d.panels[side] = !d.panels[side];\n this._canonicalize(this._tree(), d);\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n\n /** Rebuild the tree so panel:* leaves sit in the canonical positions:\n *\n * v-split\n * \u251C h-split \u2190 the \"content row\"\n * \u2502 \u251C panel:left (if open)\n * \u2502 \u251C content (whatever non-panel subtree)\n * \u2502 \u2514 panel:right (if open)\n * \u2514 panel:bottom (if open, full width below the content row)\n *\n * Existing panel leaves are detached + re-attached (same id), so the\n * renderer cache keeps the mounted content (nav filter state, etc.). */\n _canonicalize(tree, desktop) {\n // 1. Find existing panel leaves.\n const panel = {};\n for (const leaf of tree.leaves()) {\n const kind = leaf.content?.kind;\n if (kind === 'panel:left') panel.left = leaf;\n else if (kind === 'panel:right') panel.right = leaf;\n else if (kind === 'panel:bottom') panel.bottom = leaf;\n }\n\n // 2. Detach each panel leaf from its current parent. (We don't\n // use tree.close because that deletes the node; we want to\n // preserve identity so the renderer keeps the mounted body.)\n const detach = (leaf) => {\n if (!leaf) return;\n const pid = leaf.parentId;\n if (!pid) {\n tree.rootId = null;\n leaf.parentId = null;\n return;\n }\n const parent = tree.get(pid);\n if (!parent) return;\n const idx = parent.children.indexOf(leaf.id);\n if (idx >= 0) {\n parent.children.splice(idx, 1);\n parent.sizes.splice(idx, 1);\n }\n leaf.parentId = null;\n tree._collapse(parent);\n };\n detach(panel.left); detach(panel.right); detach(panel.bottom);\n\n // 3. The content subtree may legitimately be empty \u2014 the user\n // is allowed to close the last main tile and let panels\n // take over the entire WM area. If so, seed root from the\n // first requested panel; otherwise content stays as root.\n let seededSide = null;\n if (!tree.rootId) {\n seededSide = desktop.panels.left ? 'left'\n : desktop.panels.bottom ? 'bottom'\n : desktop.panels.right ? 'right'\n : null;\n if (seededSide) {\n const seed = panel[seededSide] || makeLeaf({\n content: { kind: `panel:${seededSide}`, props: {} },\n title: PANEL_TITLES[seededSide] || seededSide,\n });\n if (panel[seededSide] && PANEL_TITLES[seededSide]) {\n panel[seededSide].title = PANEL_TITLES[seededSide];\n }\n if (!panel[seededSide]) tree._register(seed);\n if (!tree.nodes.has(seed.id)) tree.nodes.set(seed.id, seed);\n seed.parentId = null;\n tree.rootId = seed.id;\n }\n // else: nothing requested; tree stays empty (renderer shows\n // the \"Empty desktop\" placeholder).\n }\n\n // 4. Wrap content row with left + right (h-split), then wrap\n // that with bottom (v-split) so bottom spans full width.\n const wrap = (panelLeaf, side) => {\n // Re-register the leaf if it was orphaned by detach.\n if (panelLeaf && !tree.nodes.has(panelLeaf.id)) {\n tree.nodes.set(panelLeaf.id, panelLeaf);\n }\n const leaf = panelLeaf || makeLeaf({\n content: { kind: `panel:${side}`, props: {} },\n title: PANEL_TITLES[side] || side,\n });\n // Heal old leaves whose stored title is still 'left'/etc.\n if (panelLeaf && PANEL_TITLES[side] && panelLeaf.title !== PANEL_TITLES[side]) {\n panelLeaf.title = PANEL_TITLES[side];\n }\n if (!panelLeaf) tree._register(leaf);\n const before = (side === 'left');\n const dir = (side === 'bottom') ? 'v' : 'h';\n const children = before ? [leaf.id, tree.rootId] : [tree.rootId, leaf.id];\n const sizes = (side === 'bottom') ? [4, 1]\n : (side === 'left') ? [1, 4]\n : [4, 1];\n const split = {\n id: `split-panel-${side}-${Math.random().toString(36).slice(2, 7)}`,\n kind: 'split', parentId: null, dir, children, sizes,\n };\n tree.nodes.set(split.id, split);\n const oldRoot = tree.get(tree.rootId);\n if (oldRoot) oldRoot.parentId = split.id;\n leaf.parentId = split.id;\n tree.rootId = split.id;\n };\n // Skip the side already consumed as the seed root (would\n // otherwise wrap a fresh duplicate leaf around the existing\n // panel-as-root).\n if (desktop.panels.left && seededSide !== 'left' && tree.rootId) wrap(panel.left, 'left');\n if (desktop.panels.right && seededSide !== 'right' && tree.rootId) wrap(panel.right, 'right');\n if (desktop.panels.bottom && seededSide !== 'bottom' && tree.rootId) wrap(panel.bottom, 'bottom');\n\n // GC orphaned nodes (panel leaves whose `panels.X` flipped to\n // false stayed in tree.nodes after detach).\n const reachable = new Set();\n const walk = (id) => {\n if (!id || reachable.has(id)) return;\n reachable.add(id);\n const n = tree.get(id);\n if (n?.kind === 'split') n.children.forEach(walk);\n };\n walk(tree.rootId);\n for (const id of [...tree.nodes.keys()]) {\n if (!reachable.has(id)) tree.nodes.delete(id);\n }\n }\n\n _findPanelLeaf(side) {\n const kind = `panel:${side}`;\n for (const leaf of this._tree().leaves()) {\n if (leaf.content?.kind === kind) return leaf;\n }\n return null;\n }\n\n // \u2500\u2500 Desktops \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 switchDesktop(idx) {\n this.desktops.ensureCount(idx + 1);\n if (!this.desktops.switchTo(idx)) return;\n const d = this.desktops.active();\n // Runtime-created desktops (ensureCount / addDesktop) are bare\n // home-leaf trees whose panels haven't been materialized yet \u2014\n // only desktops present at load() were canonicalized. Without\n // this, the new desktop shows no panels while the toggle buttons\n // (which read `d.panels`) report them open. Canonicalize on every\n // switch: it's idempotent for already-canonical trees.\n this._canonicalize(d.tree, d);\n this.renderer.tree = d.tree;\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n\n addDesktop() {\n this.desktops.addDesktop();\n this._notifyChange();\n this._persist();\n }\n\n /** Step to the previous (-1) or next (+1) existing desktop, wrapping\n * around. No-op with a single desktop. Iterates only desktops that\n * already exist \u2014 use switchDesktop(idx) to create-on-demand. */\n cycleDesktop(dir) {\n const m = this.desktops;\n const n = m.desktops.length;\n if (n <= 1) return;\n const next = (m.activeIdx + (dir < 0 ? -1 : 1) + n) % n;\n this.switchDesktop(next);\n }\n\n /** Cycle the focused tile's main-panel tabs (Ctrl+Tab / Ctrl+Shift+Tab).\n * Wraps; no-op when the focused leaf has 0 or 1 tabs. */\n cycleFocusedTab(dir) {\n const tree = this._tree();\n const leafId = tree.focusedLeafId;\n const leaf = leafId ? tree.get(leafId) : null;\n const tabs = (leaf && Array.isArray(leaf.tabs)) ? leaf.tabs : [];\n if (tabs.length <= 1) return;\n const cur = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));\n const next = (cur + (dir < 0 ? -1 : 1) + tabs.length) % tabs.length;\n this._leafTabAction(leafId, 'switch', { idx: next });\n }\n\n /**\n * Remove a desktop \u2014 AND RE-INDEX THE WINDOWS, which is the half that was\n * missing.\n *\n * `desktopIdx` on a window record is an ARRAY INDEX into `desktops`, so a\n * splice silently re-points every record above the removed one at its\n * neighbour. Nothing threw and nothing looked wrong: the window kept\n * floating, and the next *Back to tile* resolved `rec._dock` against the\n * WRONG TREE. `_onManagedWindowClosed` reads `desktops[rec.desktopIdx]`,\n * finds a tree that never held this window, and its `if (!tree) return`\n * closes the window and drops the content on the floor \u2014 staged edits\n * included, with no error and nothing on screen to say a table was lost.\n *\n * Windows homed on the desktop being removed do not die with it. The\n * ruling is that a tile operation may move a window and never destroy it,\n * and removing a desktop is the largest tile operation there is: they come\n * across to the desktop that ends up active, re-homed onto its ground by\n * `_rehomeContainedWindows` on the render below.\n */\n removeDesktop(idx) {\n const m = this.desktops;\n if (m.desktops.length <= 1) return false;\n if (idx < 0 || idx >= m.desktops.length) return false;\n m.desktops.splice(idx, 1);\n // THE SPLICE MOVES THE ACTIVE DESKTOP TOO, and the clamp below never\n // said so. Removing a desktop BELOW the one you are on shifts it down\n // by one, so an unchanged `activeIdx` then names its neighbour: the\n // desktop you were working in is replaced on screen by the next one\n // along, the bar highlights the wrong button, and the wrong index is\n // persisted. The clamp only ever caught removing the LAST desktop.\n // Pre-existing, and load-bearing for the loop below, which reads\n // `activeIdx` to decide where the orphaned windows go.\n if (idx < m.activeIdx) m.activeIdx -= 1;\n if (m.activeIdx >= m.desktops.length) m.activeIdx = m.desktops.length - 1;\n // AFTER the splice and AFTER `activeIdx` is repaired, so the survivors\n // resolve against the array the rest of this method uses.\n for (const [, rec] of this._windowToLeaf) {\n if (rec.desktopIdx === idx) {\n rec.desktopIdx = m.activeIdx;\n // ONLY A WINDOW THAT WAS ALREADY CONTAINED, which is what\n // `rec.homeLeafId` says and what `moveWindowToDesktop`'s guard\n // tests. `!rec.homeContainer` alone also catches a FREE-FLOATING\n // window \u2014 `_navigateWindow`'s, which has neither field \u2014 and\n // handing one a `homeLeafId` silently converts it into a\n // pane-contained window: it jumps into a tile, can no longer be\n // dragged outside it, and is then removed from the document by\n // the desktop rule the first time you switch pages.\n if (!rec.homeContainer && rec.homeLeafId) {\n rec.homeLeafId = m.active().tree.primaryLeafId() || null;\n }\n } else if (rec.desktopIdx > idx) {\n rec.desktopIdx -= 1;\n }\n }\n this.renderer.tree = m.active().tree;\n this.renderer.render();\n this._persist();\n this._notifyChange();\n return true;\n }\n\n moveFocusedToDesktop(idx) {\n const tree = this._tree();\n const focused = tree.focused();\n if (!focused || !focused.content) return;\n // C20. IT DESTROYS A LEAF, SO IT ANSWERS TO THE SAME VETO `closeFocused`\n // does. A move is not a close from the pane's point of view, but it is\n // exactly a close from the PAGE's: this was the fourth door out of a\n // content veto, and the one nobody thought to look at because its label\n // says \"Move\".\n const closeChrome = this.renderer?.leafChrome?.(focused.id)?.close;\n if (closeChrome === false || closeChrome?.disabled === true) return;\n const payload = { kind: focused.content.kind, props: focused.content.props, title: focused.title };\n this.desktops.ensureCount(idx + 1);\n const target = this.desktops.desktops[idx];\n // Place into the target tree's primary leaf.\n const targetTree = target.tree;\n const primary = targetTree.primaryLeafId();\n if (primary) targetTree.setLeafContent(primary, { kind: payload.kind, props: payload.props }, payload.title);\n // Remove from current desktop.\n tree.close(focused.id);\n if (!tree.rootId) tree.setRoot(makeLeaf(this._rootLeaf()));\n // THE SAME NEVER-EMPTY-CONTENT INVARIANT `closeFocused` KEEPS, and it\n // was missing here. `!tree.rootId` above only catches a tree with\n // nothing left at all; with a panel open the root survives and the page\n // is left showing chrome and no ground \u2014 so the windows that were\n // standing on the moved pane had nowhere to be re-homed to.\n if (!tree.leaves().some((l) => !String(l.content?.kind || '').startsWith('panel:'))) {\n const spawned = this._spawnContentLeaf(tree);\n if (spawned) this._seedHome(tree, spawned);\n }\n this._canonicalize(tree, this.desktops.active());\n this.renderer.render();\n this._persist();\n this._notifyChange();\n }\n\n // \u2500\u2500 Internals \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 _tree() { return this.desktops.active().tree; }\n\n _leafAction(leafId, action) {\n const tree = this._tree();\n tree.focus(leafId);\n if (action === 'close') this.closeFocused();\n else if (action === 'promote') this.toggleManagedFocused();\n // Chrome split buttons mirror Alt+H / Alt+V: structural split of\n // this tile into a fresh empty pane (the user then fills it).\n else if (action === 'split-h') this.split('h');\n else if (action === 'split-v') this.split('v');\n }\n\n /** Tab-strip event dispatcher. The renderer fires actions\n * (`switch` / `close` / `move` / `open-menu`) and the WM\n * translates them into tile-tree mutations + a re-render. */\n _leafTabAction(leafId, action, data = {}) {\n const tree = this._tree();\n const leaf = tree.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return;\n if (action === 'switch') {\n tree.setActiveLeafTab(leafId, data.idx);\n tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-switch');\n return;\n }\n if (action === 'close') {\n const tabs = leaf.tabs || [];\n // Closing the last remaining tab closes the tile \u2014 same\n // behavior as Alt+W on a single-tab leaf today.\n if (tabs.length <= 1) {\n tree.focus(leafId);\n this.closeFocused();\n return;\n }\n tree.removeLeafTab(leafId, data.idx);\n tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange('twm-tab-close');\n return;\n }\n if (action === 'move') {\n tree.moveLeafTab(leafId, data.from, data.to);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-move');\n return;\n }\n if (action === 'menu') {\n this._showTabContextMenu(leafId, data.idx, data.x, data.y);\n return;\n }\n if (action === 'to-window') {\n // R9. `floatTabAsWindow` renders, persists and notifies on its own\n // \u2014 it is the same promote path the pane's chrome uses \u2014 so there\n // is deliberately nothing after it here.\n this.floatTabAsWindow(leafId, data.idx);\n return;\n }\n if (action === 'drop-into') {\n // C33. The tab-drop's one door into the tree. Fired against the\n // SOURCE leaf, because `_leafTabAction`'s first argument is always\n // the leaf whose tab is being acted on \u2014 the destination travels in\n // `data.target`, which is a `tabDropProbe` answer.\n //\n // Like `to-window` above, `moveTabInto` renders, persists and\n // notifies on its own, so there is deliberately nothing after it.\n this.moveTabInto(leafId, data.idx, data.target);\n return;\n }\n if (action === 'close-others') {\n tree.closeOtherTabs(leafId, data.idx);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-close-others');\n return;\n }\n if (action === 'close-right') {\n tree.closeTabsAfter(leafId, data.idx);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-close-right');\n return;\n }\n if (action === 'close-left') {\n tree.closeTabsBefore(leafId, data.idx);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-close-left');\n return;\n }\n if (action === 'open-menu') {\n // Defer to a per-WM callback if anyone wired one (the\n // hamburger menu mounts via this hook). The renderer hands\n // us screen coordinates so the menu can anchor below the\n // hamburger button.\n try {\n this.ctx?.onTileTabMenu?.(leafId, data.x, data.y);\n } catch (err) {\n console.warn('[wm] tab menu hook failed', err);\n }\n }\n }\n\n /** Central navigation entry point. Every nav action \u2014 left menu,\n * breadcrumb, landing-row click, in-tile reference, panel\n * reference, entity-to-entity \u2014 should route through this so the\n * one holistic routing concept lives in one place.\n *\n * A link declares its target as TWO axes:\n *\n * `opts.dest` \u2014 WHERE the content lands:\n * 'main' \u2192 the primary content tile (default).\n * The nav panel always uses this.\n * 'origin' \u2192 the tile/window the click came from\n * (needs `ctx`). Breadcrumbs use this so\n * a segment click stays in the current\n * tile (and inside a floating window when\n * windowed).\n * 'window' \u2192 a fresh managed window.\n * `opts.newTab` \u2014 HOW it lands in that destination:\n * false \u2192 replace the destination's active tab\n * (default). true \u2192 append a NEW tab.\n * Ignored for 'window' (one window = one\n * content).\n *\n * `opts.ctx` \u2014 the caller's mount context (`leafId`,\n * `windowId`); required for `dest:'origin'`.\n * `opts.transient` \u2014 the appended tab is not persisted/restored\n * (e.g. an add-row form). Only meaningful with\n * `newTab:true`.\n * `opts.background` \u2014 with `newTab`, append the tab WITHOUT switching to\n * it or focusing its tile. \"Open in a background tab\"\n * means the page you are reading stays in front;\n * without it the tab arrives and takes the screen,\n * which is what an ordinary click already does.\n *\n * Back-compat: the legacy `opts.target` enum still works and maps\n * onto the axes \u2014 'auto'\u2192origin, 'tab'\u2192origin+newTab,\n * 'primary'\u2192main, 'window'\u2192window. Prefer the two-axis form.\n *\n * The lower-level primitives (`openFromContext`,\n * `openInTabFromContext`, `openInTabInPrimary`, `openInPrimary`,\n * `openInWindow`) stay internal; callers prefer `wm.navigate(...)`. */\n navigate(kind, props = {}, opts = {}) {\n const { ctx = null, transient = false } = opts;\n // Append the tab but stay where you are. Only meaningful with `newTab`.\n const background = !!opts.background;\n // Resolve the two axes, honoring the legacy `target` alias.\n let { dest = 'main', newTab = false } = opts;\n if (opts.target != null) {\n switch (opts.target) {\n case 'window': dest = 'window'; newTab = false; break;\n case 'primary': dest = 'main'; newTab = false; break;\n case 'tab': dest = 'origin'; newTab = true; break;\n case 'split-h': dest = 'split-h'; break;\n case 'split-v': dest = 'split-v'; break;\n case 'auto':\n default: dest = 'origin'; newTab = false; break;\n }\n }\n // Split destinations: split the originating tile (or, lacking a\n // tile context, the focused tile) and mount a fresh instance in\n // the new pane. This is the routing for the code-pane \"open in\n // horizontal/vertical split\" buttons.\n if (dest === 'split-h' || dest === 'split-v') {\n const dir = dest === 'split-h' ? 'h' : 'v';\n const leafId = ctx?.leafId || this._tree().focusedLeafId;\n return this.splitLeafWith(leafId, dir, kind, props, _tabTitle(kind, props));\n }\n if (dest === 'window') return this._navigateWindow(kind, props);\n if (dest === 'main') {\n return newTab\n ? this.openInTabInPrimary(kind, props, transient, background)\n : this.openInPrimary(kind, props);\n }\n // dest === 'origin'\n return newTab\n ? this._navigateTab(ctx, kind, props, transient, background)\n : this._navigateAuto(ctx, kind, props);\n }\n\n _navigateAuto(ctx, kind, props) {\n // Windowed content \u2192 replace window in place.\n if (ctx?.windowId && this._windowToLeaf.has(ctx.windowId)) {\n this.openInWindow(ctx.windowId, kind, props);\n return;\n }\n // In-tile content \u2192 replace ACTIVE TAB only (preserves siblings).\n // Panel tiles (left/right/bottom) fall through \u2014 their clicks\n // target the primary content tile, not the panel itself.\n if (ctx?.leafId) {\n const tree = this._tree();\n const leaf = tree.get(ctx.leafId);\n const k = leaf?.content?.kind;\n if (leaf && k && !k.startsWith('panel:')) {\n this.openInLeaf(ctx.leafId, kind, props);\n return;\n }\n }\n // Outside the tile (top-nav, panel, palette) \u2192 primary tile,\n // page-aware swap that preserves per-page tab archives.\n this.openInPrimary(kind, props);\n }\n\n _navigateTab(ctx, kind, props, transient = false, background = false) {\n // Windows aren't tabbed \u2014 \"open in tab\" inside a window just\n // replaces the window's content.\n if (ctx?.windowId && this._windowToLeaf.has(ctx.windowId)) {\n this.openInWindow(ctx.windowId, kind, props);\n return;\n }\n this.openInTabFromContext(ctx || {}, kind, props, transient, background);\n }\n\n /** Spawn a fresh ManagedWindow with the requested content. No\n * source leaf \u2014 closing the window just disposes the content. */\n _navigateWindow(kind, props) {\n const winId = `twm-mw-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 5)}`;\n const contentEl = document.createElement('div');\n contentEl.className = 'twm-window-content';\n contentEl.style.cssText = 'display:flex; flex-direction:column; flex:1; min-width:0; min-height:0; height:100%;';\n const title = _tabTitle(kind, props);\n const mountInfo = this.content.mount(kind, contentEl, props,\n { ...this.ctx, wm: this, windowId: winId });\n const win = new ManagedWindow({\n id: winId,\n title: mountInfo?.title || title,\n icon: 'web_asset',\n content: contentEl,\n canMinimize: true,\n canMaximize: true,\n canResize: true,\n modal: false,\n // C15, and this is the SECOND of the two places the WM builds a\n // window. Alt+N and \"Open in new window\" produce a window that is\n // every bit as dockable as a promoted one, and a window that can be\n // dragged onto a tile in one case and not the other is a rule\n // nobody can learn.\n snap: this.snapPromotion,\n snapController: this.snapPromotion ? this._snapController() : null,\n // R1. THE PANE IS A BOX WITH `overflow: hidden`. A window contained\n // to one (C21) cannot be dragged a single pixel outside it, so\n // \"drag a window from one tile to another\" \u2014 the gesture all three\n // drop behaviours are built on \u2014 was not merely awkward, it was\n // invisible. For the length of a drag the window is re-parented\n // here, to the root every tile is inside; on release it goes back\n // into a pane, either the one it was dropped on or the one it came\n // from. Resolved per drag: the root outlives any tile, and a tile\n // grabbed once does not survive its own repaint.\n dragHost: () => this.rootEl,\n dragBounds: () => this._tileBounds(),\n // R7. MAXIMISE MEANS BACK TO TILE. This window came OUT of the\n // tree; the useful thing to do with it is put it back, and filling\n // the screen with it is the one gesture that makes putting it back\n // harder. So the maximize button docks \u2014 and the separate demote\n // button the WM used to inject beside it is gone, because two\n // buttons for one verb is how you get a chrome nobody reads.\n onMaximize: () => this.bringBackWindow(winId),\n maximizeIcon: 'close_fullscreen',\n maximizeTitle: 'Back to tile',\n onClose: () => this._onManagedWindowClosed(winId, mountInfo),\n });\n // DECORATED ONCE, below, after the record exists. It was called here as\n // well, so every Alt+N window carried two topbar context-menu handlers\n // \u2014 and, until R7 removed it, two \"back to tile\" buttons.\n // leafId is null \u2014 `_onManagedWindowClosed` already short-circuits\n // both branches when there's no source leaf, so the close path\n // just disposes the content and drops the map entry.\n this._windowToLeaf.set(winId, {\n leafId: null,\n desktopIdx: this.desktops.activeIdx,\n original: { kind, props: { ...(props || {}) },\n title: mountInfo?.title || title },\n mountInfo, window: win, contentEl,\n _demoting: false,\n });\n win.show();\n // Cascade: a fresh window centers by default, so opening one from\n // another window would land exactly on top \u2014 reading as the\n // calling window being replaced. Offset by the current window\n // count (cycling every 6) so each new window is slightly inset\n // from the last.\n const _n = this._windowToLeaf.size; // includes this new window\n if (_n > 1 && typeof win._applyPosition === 'function') {\n const step = ((_n - 1) % 6) * 28;\n win.x = (win.x || 0) + step;\n win.y = (win.y || 0) + step;\n win._applyPosition();\n }\n this._decorateManagedWindow(win, winId);\n this._notifyChange('window-spawned');\n }\n\n /** Browser-style tab context menu. Items reflect the leaf's current\n * tab list \u2014 \"Close others\" is hidden when only one tab is open,\n * \"Close to the right / left\" are hidden at the edges. The actual\n * mutations route back through `_leafTabAction` so persistence +\n * notify stay in one place. */\n _showTabContextMenu(leafId, idx, x, y) {\n const tree = this._tree();\n const leaf = tree.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return;\n const tabs = leaf.tabs || [];\n if (tabs.length === 0) return;\n const items = [];\n // R9. THE PER-TAB VERB LIVES ON THE TAB.\n //\n // Floating one tab out is what the pane's chrome button used to do, and\n // it was the wrong home for it: the button is on the PANE and named\n // \"float this pane as a window\", so it now floats the pane (R8) and\n // this is where the single-tab version went. A user who wants one of\n // three tables in a window right-clicks that table's tab, which is\n // where every other per-tab verb already is.\n //\n // Offered above the close verbs, and separated from them: it is the\n // only item here that does not destroy something, and a menu whose\n // first four entries all close things teaches the eye to skip it.\n const tab = tabs[idx];\n if (tab && !String(tab.kind || '').startsWith('panel:')\n && tab.kind !== PLACEHOLDER_KIND) {\n items.push({ label: 'Open in a window', icon: 'web_asset', action: 'to-window' });\n items.push({ separator: true });\n }\n items.push({ label: 'Close tab', icon: 'close', action: 'close' });\n if (tabs.length > 1) {\n items.push({ label: 'Close other tabs', icon: 'tab_close', action: 'close-others' });\n }\n if (idx < tabs.length - 1) {\n items.push({ label: 'Close tabs to the right', icon: 'chevron_right',\n action: 'close-right' });\n }\n if (idx > 0) {\n items.push({ label: 'Close tabs to the left', icon: 'chevron_left',\n action: 'close-left' });\n }\n showContextMenu(x, y, items, (action) => {\n this._leafTabAction(leafId, action, { idx });\n });\n }\n\n /** Open content in a new tab on the leaf where the call originated.\n * Mirrors `openFromContext` (windowed / split-leaf / primary\n * routing) but uses `appendLeafTab` so the existing content\n * stays in place as a tab. */\n openInTabFromContext(ctx, kind, props = {}, transient = false, background = false) {\n // Managed-window content: just open in the window \u2014 managed\n // windows aren't tabbed (one window = one content).\n if (ctx?.windowId && this._windowToLeaf.has(ctx.windowId)) {\n this.openInWindow(ctx.windowId, kind, props);\n return;\n }\n const tree = this._tree();\n let leafId = ctx?.leafId;\n if (!leafId) leafId = tree.primaryLeafId();\n const leaf = leafId ? tree.get(leafId) : null;\n const k = leaf?.content?.kind;\n if (!leaf || !leafId || (k && k.startsWith('panel:'))) {\n // Panel tiles can't host content tabs \u2014 fall back to the\n // primary leaf (matches openFromContext fall-through).\n this.openInPrimary(kind, props);\n return;\n }\n tree.appendLeafTab(leafId, { kind, props }, _tabTitle(kind, props),\n { transient, background });\n // A BACKGROUND tab must not steal the tile's focus either \u2014 the point\n // is that the user stays exactly where they were.\n if (!background) tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-open');\n }\n\n /** Open content as a NEW tab in the primary content tile, regardless\n * of where the call came from (the `dest:'main', newTab:true` path).\n * Unlike `openInTabFromContext`, this never falls back to a replace:\n * it targets the primary leaf directly and always appends, so a\n * click from outside the tile system (e.g. the bottom-panel\n * \"Add row\" button, which passes no ctx) reliably lands as a sibling\n * tab in the main tile rather than swapping its content. */\n openInTabInPrimary(kind, props = {}, transient = false, background = false) {\n const tree = this._tree();\n const leafId = tree.primaryLeafId();\n // No content tile on this desktop (e.g. a panels-only layout) \u2014\n // open in a managed window rather than spawning a bare tile. The\n // caller asked for \"a tab in the main tile\"; with no main tile to\n // tab into, a floating window is the least-surprising fallback.\n if (!leafId) { this._navigateWindow(kind, props); return; }\n tree.appendLeafTab(leafId, { kind, props }, _tabTitle(kind, props),\n { transient, background });\n // A BACKGROUND tab must not steal the tile's focus either \u2014 the point\n // is that the user stays exactly where they were.\n if (!background) tree.focus(leafId);\n this.renderer.render();\n this._persist();\n this._notifyChange('tab-open');\n }\n}\n\n\n/** Derive a tab's display title from the content it carries \u2014 entity\n * label first, then id, then the bare kind. Used everywhere a tab is\n * created, so the bar names what the user opened rather than the kind\n * of thing it is. The rename-bus listener in the WM keeps these in\n * sync when the underlying entity is renamed. */\nfunction _tabTitle(kind, props) {\n if (props && typeof props.label === 'string' && props.label) return props.label;\n if (props && (typeof props.id === 'string' || typeof props.id === 'number')\n && String(props.id)) return String(props.id);\n return kind || '';\n}\n\n/** R8. A leaf's tabs as the plain specs a window record holds \u2014 deep enough\n * that editing one afterwards cannot reach back into the tree, which matters\n * because a floated tab keeps being edited (`openInWindow` renames it) while\n * the leaf it came from is still alive. A leaf with no tab list at all is\n * pre-tabs data; its mirrored `content`/`title` are the one tab it has. */\nfunction _leafTabSpecs(leaf) {\n const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];\n if (tabs.length) {\n return tabs.map((t) => ({\n kind: t.kind, props: { ...(t.props || {}) },\n title: t.title || t.kind || '',\n }));\n }\n return [{\n kind: leaf.content.kind,\n props: { ...(leaf.content.props || {}) },\n title: leaf.title || leaf.content.kind || '',\n }];\n}\n\n/** One half of a rectangle, in the same viewport pixels it arrived in. This is\n * what makes the drop preview honest: the rectangle a `left` drop on a tile\n * produces is half of THAT TILE, which \u2014 when the tile is itself half the\n * layer \u2014 is a quarter of the layer, and the preview says so. */\nfunction _halfOf(r, side) {\n const w = Math.round(r.width / 2);\n const h = Math.round(r.height / 2);\n switch (side) {\n case 'left': return { left: r.left, top: r.top, width: w, height: r.height };\n case 'right': return { left: r.left + r.width - w, top: r.top, width: w, height: r.height };\n case 'top': return { left: r.left, top: r.top, width: r.width, height: h };\n case 'bottom': return { left: r.left, top: r.top + r.height - h, width: r.width, height: h };\n default: return { left: r.left, top: r.top, width: r.width, height: r.height };\n }\n}\n\n/** Give `newId` half of `sourceId`'s share of their shared split, leaving every\n * other pane in the row untouched. This is what \"the preview showed half of\n * that tile\" means once the split is an insertion rather than a wrap. */\nfunction _halveInto(tree, sourceId, newId) {\n const src = tree.get(sourceId);\n if (!src) return false;\n const parent = tree.get(src.parentId);\n if (!parent || parent.kind !== 'split') return false;\n const i = parent.children.indexOf(sourceId);\n const j = parent.children.indexOf(newId);\n if (i < 0 || j < 0) return false;\n // The wrap case already splits a fresh two-child node evenly; only the\n // insertion case has a size to correct, and correcting an even one is a\n // no-op anyway.\n const share = (parent.sizes[i] ?? 1) / 2;\n parent.sizes[i] = share;\n parent.sizes[j] = share;\n return true;\n}\n\n/** Swap two sibling leaves inside their shared split. `TileTree.split` always\n * appends, and \"the window goes on the left\" is a legitimate outcome of a\n * drop; this is the one-line difference between the two. */\nfunction _swapSiblings(tree, aId, bId) {\n const a = tree.get(aId);\n const b = tree.get(bId);\n if (!a || !b || a.parentId !== b.parentId) return false;\n const parent = tree.get(a.parentId);\n if (!parent || parent.kind !== 'split') return false;\n const i = parent.children.indexOf(aId);\n const j = parent.children.indexOf(bId);\n if (i < 0 || j < 0) return false;\n parent.children[i] = bId;\n parent.children[j] = aId;\n // `sizes` IS PARALLEL TO `children` \u2014 `makeSplit` builds them together and\n // `TileTree.split` splices both at the same index. Swapping one and not the\n // other does not swap two panes, it makes them trade widths in place, which\n // looks like the drop landing on the correct side and then resizing itself\n // for no reason. `TileTree.moveDir` swaps both; so does this.\n const size = parent.sizes[i];\n parent.sizes[i] = parent.sizes[j];\n parent.sizes[j] = size;\n return true;\n}\n\n", "/**\n * tile_tab_menu.js \u2014 pop-out menu mounted by the tile's hamburger\n * button. Surfaces the same content the tile's *home page* would\n * list, grouped into one section per source table, with a built-in\n * search, per-section pagination, and full keyboard navigation.\n *\n * Click (or Enter on the active row) to open the entity in a NEW TAB\n * on the same tile. The menu only exists when the tab bar is visible\n * (\u22652 tabs).\n *\n * Source resolution: the menu asks the injected taxonomy which entity\n * sources a tile on this page should list (`taxonomy.sourcesFor`), then\n * asks the injected catalog to fetch and shape them. It knows the name\n * of neither. Which sources a page offers is one field on the\n * embedder's KindDef; it used to be a hardcoded table right here.\n *\n * Keyboard interactions:\n * \u2191 \u2193 move the row cursor across ALL visible items (the\n * cursor flows section-to-section, not just within one).\n * Home/End jump to the first / last visible row.\n * PageDown/ flip the focused section's pager when no search is active.\n * PageUp\n * Enter pick the cursor row.\n * Esc close.\n *\n * Search semantics:\n * - Match runs against the shaped (label, id, hint) of every raw row,\n * so the menu's search behaves the same as the command palette's.\n * - Pagination is OFF when a query is active \u2014 all matches show flat\n * within each section. Without a query, each section caps to\n * `PAGE_SIZE` rows with prev/next controls.\n */\n\n/** Items per category in browse mode (no search). Tuned small so the\n * menu stays compact; users page through with prev/next or type to\n * search the entire set. */\nconst PAGE_SIZE = 10;\n\n\n/** Open the menu anchored at (x, y). Closes on Escape, click-outside,\n * or selection. Returns a teardown closure for the caller, but the\n * menu also self-disposes on close. */\nexport function openTileTabMenu({\n x, y, leafKind, onPick, api, taxonomy, entities,\n}) {\n if (!taxonomy || !entities) {\n console.error('[tile-tab-menu] taxonomy + entity catalog are required');\n return () => {};\n }\n // Resolve which sources to list for the leaf's current page. An\n // unknown kind falls back to the root's list, which is the\n // \"everything\" default.\n const topNav = taxonomy.topNavFor(leafKind) || leafKind;\n const sources = taxonomy.sourcesFor(topNav)\n .map((nav) => entities.get(nav))\n .filter(Boolean);\n\n // Build the overlay + panel chrome.\n const overlay = document.createElement('div');\n overlay.className = 'twm-tile-tabmenu-overlay';\n overlay.innerHTML = `\n <div class=\"twm-tile-tabmenu\" role=\"dialog\" aria-label=\"Open in new tab\">\n <header class=\"twm-tile-tabmenu__head\">\n <span class=\"material-symbols-outlined twm-tile-tabmenu__head-icon\">tab</span>\n <span class=\"twm-tile-tabmenu__head-label\">Open in new tab</span>\n <button type=\"button\" class=\"twm-tile-tabmenu__close\"\n data-action=\"close\"\n aria-label=\"Close\">\u00D7</button>\n </header>\n <div class=\"twm-tile-tabmenu__search\">\n <span class=\"material-symbols-outlined\">search</span>\n <input type=\"search\"\n data-role=\"search\"\n placeholder=\"Filter rows across all sections\u2026\"\n autocomplete=\"off\" />\n </div>\n <div class=\"twm-tile-tabmenu__body\" data-role=\"body\">\n <div class=\"twm-tile-tabmenu__loading\">Loading\u2026</div>\n </div>\n </div>\n `;\n document.body.appendChild(overlay);\n\n // Anchor below the hamburger. clip to viewport so it never opens\n // off-screen when the tile sits at the right edge.\n const panel = overlay.querySelector('.twm-tile-tabmenu');\n panel.style.position = 'fixed';\n panel.style.left = `${Math.max(8, Math.min(window.innerWidth - 360, x))}px`;\n panel.style.top = `${Math.max(8, Math.min(window.innerHeight - 480, y - 480 + 22))}px`;\n\n let alive = true;\n let _query = '';\n let _cursor = 0; // index into _visibleItems\n let _visibleItems = []; // flat list rebuilt on every render\n // navKind \u2192 { rows, page, shaped (pre-shaped per row in source order) }\n const sectionState = new Map(sources.map((s) =>\n [s.navKind, { rows: [], page: 0, shaped: [] }]));\n\n const close = () => {\n if (!alive) return;\n alive = false;\n document.removeEventListener('mousedown', onOutside, true);\n document.removeEventListener('keydown', onKey, true);\n overlay.remove();\n };\n const onOutside = (ev) => {\n if (!overlay.contains(ev.target)) close();\n };\n\n const moveCursor = (delta) => {\n if (_visibleItems.length === 0) return;\n _cursor = Math.max(0, Math.min(_visibleItems.length - 1, _cursor + delta));\n _paintCursor();\n };\n const jumpCursor = (toEnd) => {\n if (_visibleItems.length === 0) return;\n _cursor = toEnd ? _visibleItems.length - 1 : 0;\n _paintCursor();\n };\n const pickCursor = () => {\n const cur = _visibleItems[_cursor];\n if (!cur) return;\n close();\n try { onPick?.(cur.navKind, cur.shaped); }\n catch (err) { console.warn('[tile-tab-menu] pick failed', err); }\n };\n\n const onKey = (ev) => {\n if (!alive) return;\n // Ignore arrow/Enter typed inside the search box's IME, etc.\n if (ev.isComposing) return;\n switch (ev.key) {\n case 'Escape':\n ev.preventDefault(); close(); return;\n case 'ArrowDown':\n ev.preventDefault(); moveCursor(+1); return;\n case 'ArrowUp':\n ev.preventDefault(); moveCursor(-1); return;\n case 'Home':\n ev.preventDefault(); jumpCursor(false); return;\n case 'End':\n ev.preventDefault(); jumpCursor(true); return;\n case 'Enter':\n ev.preventDefault(); pickCursor(); return;\n case 'PageDown': {\n if (_query) return;\n ev.preventDefault();\n const cur = _visibleItems[_cursor];\n const st = cur ? sectionState.get(cur.navKind) : null;\n if (st) {\n const pages = _pageCountFor(cur.navKind);\n if (st.page < pages - 1) { st.page += 1; _render(); }\n }\n return;\n }\n case 'PageUp': {\n if (_query) return;\n ev.preventDefault();\n const cur = _visibleItems[_cursor];\n const st = cur ? sectionState.get(cur.navKind) : null;\n if (st && st.page > 0) { st.page -= 1; _render(); }\n return;\n }\n }\n };\n document.addEventListener('mousedown', onOutside, true);\n document.addEventListener('keydown', onKey, true);\n overlay.querySelector('[data-action=\"close\"]')\n .addEventListener('click', close);\n\n const searchInput = overlay.querySelector('[data-role=\"search\"]');\n searchInput.addEventListener('input', () => {\n _query = searchInput.value.trim().toLowerCase();\n for (const st of sectionState.values()) st.page = 0;\n _cursor = 0;\n _render();\n });\n\n const bodyEl = overlay.querySelector('[data-role=\"body\"]');\n\n const _pageCountFor = (navKind) => {\n const st = sectionState.get(navKind);\n if (!st) return 1;\n const total = (_query\n ? st.shaped.filter((s) => _matchesShaped(s, _query))\n : st.shaped).length;\n return Math.max(1, Math.ceil(total / PAGE_SIZE));\n };\n\n const _render = () => {\n if (!alive) return;\n _visibleItems = [];\n const sections = [];\n for (const src of sources) {\n const st = sectionState.get(src.navKind);\n const meta = taxonomy.meta(src.navKind);\n const all = st.shaped;\n const filtered = _query\n ? all.filter((s) => _matchesShaped(s, _query))\n : all;\n const total = filtered.length;\n // Pagination is OFF during search \u2014 show every match. In\n // browse mode each section caps to PAGE_SIZE.\n let view;\n let page = 0;\n let pages = 1;\n if (_query) {\n view = filtered;\n } else {\n pages = Math.max(1, Math.ceil(total / PAGE_SIZE));\n page = Math.min(st.page, pages - 1);\n const start = page * PAGE_SIZE;\n view = filtered.slice(start, start + PAGE_SIZE);\n }\n // Append to the flat cursor list, in render order.\n for (const shaped of view) {\n _visibleItems.push({ navKind: src.navKind, shaped });\n }\n sections.push({ src, meta, view, total, page, pages });\n }\n if (_cursor >= _visibleItems.length) {\n _cursor = Math.max(0, _visibleItems.length - 1);\n }\n let flatIdx = 0;\n bodyEl.innerHTML = sections.map((s) => {\n const items = s.view.map((shaped) => {\n const idx = flatIdx++;\n const label = shaped.label || shaped.id || '';\n const hint = shaped.hint || '';\n return `\n <li class=\"twm-tile-tabmenu__item${idx === _cursor ? ' twm-tile-tabmenu__item--cursor' : ''}\"\n data-flat-idx=\"${idx}\">\n <span class=\"twm-tile-tabmenu__item-label\">${_esc(label)}</span>\n ${hint ? `<span class=\"twm-tile-tabmenu__item-hint\">${_esc(hint)}</span>` : ''}\n </li>\n `;\n }).join('');\n return `\n <section class=\"twm-tile-tabmenu__section\"\n data-section=\"${_esc(s.src.navKind)}\">\n <header class=\"twm-tile-tabmenu__section-head\">\n <span class=\"material-symbols-outlined\">${_esc(s.meta?.icon || 'arrow_right')}</span>\n <span class=\"twm-tile-tabmenu__section-label\">${_esc(s.meta?.label || s.src.navKind)}</span>\n <span class=\"twm-tile-tabmenu__section-count\">${s.total}</span>\n </header>\n ${s.view.length === 0 ? `\n <div class=\"twm-tile-tabmenu__empty\">${\n _query ? 'No matches.' : '\u2014 none \u2014'\n }</div>\n ` : `<ul class=\"twm-tile-tabmenu__list\">${items}</ul>`}\n ${(!_query && s.pages > 1) ? `\n <div class=\"twm-tile-tabmenu__pager\">\n <button type=\"button\" data-action=\"prev\"\n data-section=\"${_esc(s.src.navKind)}\"\n ${s.page === 0 ? 'disabled' : ''}>&lt;</button>\n <span>${s.page + 1} / ${s.pages}</span>\n <button type=\"button\" data-action=\"next\"\n data-section=\"${_esc(s.src.navKind)}\"\n ${s.page >= s.pages - 1 ? 'disabled' : ''}>&gt;</button>\n </div>\n ` : ''}\n </section>\n `;\n }).join('');\n bodyEl.querySelectorAll('.twm-tile-tabmenu__item').forEach((li) => {\n const idx = Number(li.dataset.flatIdx);\n li.addEventListener('mousemove', () => {\n if (_cursor !== idx) { _cursor = idx; _paintCursor(); }\n });\n li.addEventListener('click', () => {\n _cursor = idx;\n pickCursor();\n });\n });\n bodyEl.querySelectorAll('[data-action=\"prev\"], [data-action=\"next\"]').forEach((btn) => {\n btn.addEventListener('click', () => {\n const navKind = btn.dataset.section;\n const st = sectionState.get(navKind);\n if (!st) return;\n st.page += (btn.dataset.action === 'next') ? 1 : -1;\n _render();\n });\n });\n };\n\n const _paintCursor = () => {\n bodyEl.querySelectorAll('.twm-tile-tabmenu__item').forEach((li) => {\n const idx = Number(li.dataset.flatIdx);\n const on = idx === _cursor;\n li.classList.toggle('twm-tile-tabmenu__item--cursor', on);\n if (on) li.scrollIntoView({ block: 'nearest' });\n });\n };\n\n // Kick off the fetch \u2014 in parallel, and only for the sources this\n // tile cares about. `loadOne` + `shapeRows` are the catalog's own\n // primitives, so this menu and the command palette cannot drift\n // apart on what a row is or which rows get dropped (they used to:\n // this loop was a hand-rolled copy of the palette's).\n (async () => {\n await Promise.all(sources.map(async (src) => {\n const st = sectionState.get(src.navKind);\n if (!st) return;\n st.rows = await entities.loadOne(src.navKind, api);\n // Pre-shape once and stash so search/render don't re-shape\n // on every keystroke. Rows that shape() rejects are dropped.\n st.shaped = entities.shapeRows(src.navKind, st.rows);\n }));\n _render();\n // Park the cursor on the first visible row so Enter picks\n // something immediately after open.\n if (_visibleItems.length > 0) {\n _cursor = 0;\n _paintCursor();\n }\n })();\n\n searchInput.focus();\n return close;\n}\n\n\n/** Open a compact menu anchored to the leaf's hamburger button that\n * lists the leaf's OPEN TABS for quick switching (a browser-style tab\n * overflow list). Clicking a row switches to that tab. Closes on\n * Escape, click-outside, or selection.\n *\n * Anchored to the button: the tab strip sits at the bottom of the tile,\n * so the menu opens UPWARD, its bottom edge pinned just above the\n * hamburger at (x, y) = the button's top-left corner.\n *\n * This is deliberately NOT the same surface as `openTileTabMenu` above\n * (which browses a page's content sources to open NEW tabs). The\n * hamburger's job is \"show me the tabs I already have open here\".\n *\n * @param {{x:number, y:number,\n * tabs: Array<{kind:string, title?:string}>,\n * activeIdx?:number,\n * onPick:(idx:number)=>void}} opts\n * @returns {() => void} teardown closure (the menu also self-disposes).\n */\nexport function openTileTabSwitcher({ x, y, tabs, activeIdx = 0, onPick }) {\n const list = Array.isArray(tabs) ? tabs : [];\n if (list.length === 0) return () => {};\n\n const overlay = document.createElement('div');\n overlay.className = 'twm-tile-tabswitch-overlay';\n const rows = list.map((t, i) => `\n <li class=\"twm-tile-tabswitch__item${i === activeIdx ? ' twm-tile-tabswitch__item--on' : ''}\"\n data-idx=\"${i}\" role=\"option\" aria-selected=\"${i === activeIdx}\">\n <span class=\"material-symbols-outlined twm-tile-tabswitch__check\">${i === activeIdx ? 'check' : ''}</span>\n <span class=\"twm-tile-tabswitch__label\">${_esc(t.title || t.kind || 'Tab')}</span>\n </li>\n `).join('');\n overlay.innerHTML = `\n <div class=\"twm-tile-tabswitch\" role=\"dialog\" aria-label=\"Open tabs\">\n <header class=\"twm-tile-tabswitch__head\">\n <span class=\"material-symbols-outlined\">tab</span>\n <span class=\"twm-tile-tabswitch__head-label\">Open tabs</span>\n </header>\n <ul class=\"twm-tile-tabswitch__list\" role=\"listbox\">${rows}</ul>\n </div>\n `;\n document.body.appendChild(overlay);\n\n const panel = overlay.querySelector('.twm-tile-tabswitch');\n const W = 260;\n panel.style.position = 'fixed';\n panel.style.left = `${Math.max(8, Math.min(window.innerWidth - W - 8, x))}px`;\n // Open upward: pin the panel's bottom just above the hamburger.\n panel.style.bottom = `${Math.max(8, window.innerHeight - y + 4)}px`;\n panel.style.maxHeight = `${Math.max(120, y - 16)}px`;\n\n let alive = true;\n let cursor = Math.max(0, Math.min(list.length - 1, activeIdx));\n\n const close = () => {\n if (!alive) return;\n alive = false;\n document.removeEventListener('mousedown', onOutside, true);\n document.removeEventListener('keydown', onKey, true);\n overlay.remove();\n };\n const onOutside = (ev) => { if (!overlay.contains(ev.target)) close(); };\n const pick = (idx) => {\n close();\n try { onPick?.(idx); }\n catch (err) { console.warn('[tile-tab-switch] pick failed', err); }\n };\n const paint = () => {\n overlay.querySelectorAll('.twm-tile-tabswitch__item').forEach((li) => {\n const on = Number(li.dataset.idx) === cursor;\n li.classList.toggle('twm-tile-tabswitch__item--cursor', on);\n if (on) li.scrollIntoView({ block: 'nearest' });\n });\n };\n const onKey = (ev) => {\n if (!alive || ev.isComposing) return;\n switch (ev.key) {\n case 'Escape': ev.preventDefault(); close(); return;\n case 'ArrowDown': ev.preventDefault();\n cursor = Math.min(list.length - 1, cursor + 1); paint(); return;\n case 'ArrowUp': ev.preventDefault();\n cursor = Math.max(0, cursor - 1); paint(); return;\n case 'Home': ev.preventDefault(); cursor = 0; paint(); return;\n case 'End': ev.preventDefault(); cursor = list.length - 1; paint(); return;\n case 'Enter': ev.preventDefault(); pick(cursor); return;\n }\n };\n overlay.querySelectorAll('.twm-tile-tabswitch__item').forEach((li) => {\n const idx = Number(li.dataset.idx);\n li.addEventListener('mousemove', () => {\n if (cursor !== idx) { cursor = idx; paint(); }\n });\n li.addEventListener('click', () => pick(idx));\n });\n document.addEventListener('mousedown', onOutside, true);\n document.addEventListener('keydown', onKey, true);\n paint();\n return close;\n}\n\n\n/** Match a shaped record (`{id, label, hint}`) against `query`.\n * Case-insensitive substring against any field. Mirrors the command\n * palette's matching so search semantics are consistent across both\n * surfaces. */\nfunction _matchesShaped(shaped, query) {\n if (!shaped) return false;\n const id = String(shaped.id ?? '').toLowerCase();\n const label = String(shaped.label ?? '').toLowerCase();\n const hint = String(shaped.hint ?? '').toLowerCase();\n return id.includes(query) || label.includes(query) || hint.includes(query);\n}\n\n\nfunction _esc(s) {\n return String(s ?? '').replace(/[&<>\"']/g, (c) => ({\n '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;',\n }[c]));\n}\n", "/**\n * zoom.js \u2014 the shell's content zoom: a \u2212 / track / + / readout control, and\n * the one place that decides what \"zoom the workspace\" is allowed to touch.\n *\n * Opt-in, like every shell feature that changes what the user sees: an embedder\n * that passes no `chrome.zoom` element gets no control and no scaling, so no\n * existing consumer changes by upgrading. The control is Excel's \u2014 a continuous\n * track with a detent-free middle, buttons either side that move in tens, and a\n * readout that is itself the reset \u2014 because that is the shape people already\n * know, and it is the one Tables shipped in its own status bar before this was\n * lifted into the framework.\n *\n * \u2500\u2500 WHAT IT SCALES, AND WHAT IT MUST NOT \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 * It scales CONTENT SURFACES and nothing a window is dragged across:\n *\n * .twm-leaf__body what a tile's content factory mounted into\n * .twm-window-content what a promoted window's content mounted into\n *\n * and it does NOT scale the root, a leaf wrap, tile chrome, tab bars, or a\n * window frame. That line is load-bearing rather than a matter of taste. CSS\n * `zoom` establishes a scaled coordinate space, and FlexDesk's window drag,\n * resize and snap all do arithmetic between the pointer (viewport pixels) and a\n * window's `left`/`top` (the pixels of whatever contains it). A contained window\n * (C21) lives in a LEAF WRAP and is re-parented to the ROOT for the length of a\n * drag (R1); zoom either of those and every drag drifts by the zoom factor, and\n * every C15 snap probe measures a tile in units the pointer is not in. Tile\n * bodies and window content sit BELOW all of that geometry, so scaling them\n * changes the text and none of the maths.\n *\n * \u2500\u2500 HOW IT IS APPLIED \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 * A CSS variable and a class, both on `root` \u2014 the element the embedder handed\n * the shell. Never `document.body`, never a selector the framework did not\n * author (the doctrine at the top of shell.js). Tiles and windows are created\n * and destroyed long after any given change, so anything written element by\n * element would have to be re-applied on every mount; a variable on the root is\n * read by whatever exists at the time.\n *\n * The class is what keeps 100% free of any declaration. A rule that always said\n * `zoom: var(--twm-zoom, 1)` would establish a scaled coordinate space even at\n * 1, so at the default the class comes off and no `zoom` applies anywhere \u2014 an\n * unzoomed shell lays out byte-for-byte as it did before this existed.\n *\n * A window an embedder mounts on `document.body` itself is outside the shell's\n * root and therefore outside this: it is not the shell's to scale. Every window\n * the WM promotes under `promoteInPlace` (C21) is inside the root, and so is\n * every window for the duration of a drag.\n *\n * \u2500\u2500 `zoom`, NOT `transform: scale()` \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 * `zoom` reflows: text stays on the pixel grid, and a scroll container still\n * measures the content it is scrolling. A transform would blur the text and\n * leave the layout box at its old size, so a zoomed-in table would overflow a\n * pane that did not know it had grown.\n */\n\n/** The range. Below 50% a data row stops being readable; above 200% a typical\n * row no longer fits its own columns. */\nexport const ZOOM_MIN = 50;\nexport const ZOOM_MAX = 200;\n\n/** The track's granularity \u2014 fine, because a control you have to aim is one\n * people stop using. */\nexport const ZOOM_STEP = 5;\n\n/** The BUTTONS' step: Excel's split, where the track is continuous and the \u2212 / +\n * cover ground. Commensurate with ZOOM_STEP by construction, so a button press\n * always lands on a notch and repeated presses cannot drift. */\nexport const ZOOM_NUDGE = 10;\n\n/**\n * Where the shell opens, and where a reset returns. Load-bearing: `applyZoom`\n * removes the class at exactly this value, and that is what makes \"unzoomed\"\n * mean \"no declaration at all\". Only true while the default IS 100.\n */\nexport const ZOOM_DEFAULT = 100;\n\n/** The logical key under the host's `state` capability. A per-person display\n * preference, persisted the same way the desktops are. */\nexport const ZOOM_STATE_KEY = 'zoom';\n\n/**\n * A number from anywhere \u2014 a slider, a stored preference, a caller \u2014 as a zoom\n * the shell will accept.\n *\n * `null`, `undefined` and `''` are \"no value\" and read as the default. They are\n * checked BEFORE the cast because `Number()` turns both `null` and `''` into 0 \u2014\n * a perfectly finite number that would then clamp to the floor, so a host with\n * no saved zoom, or a read that failed, would silently open the shell at 50%.\n *\n * It QUANTISES, which is what makes 100% reachable: the track is stepped, so a\n * stored 97 has to land on a notch rather than sit between two where neither the\n * track nor the buttons can leave it.\n */\nexport function clampZoom(value) {\n if (value == null || value === '') return ZOOM_DEFAULT;\n const n = Number(value);\n if (!Number.isFinite(n)) return ZOOM_DEFAULT;\n const stepped = Math.round(n / ZOOM_STEP) * ZOOM_STEP;\n return Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, stepped));\n}\n\n/**\n * Apply one zoom to a shell root. Idempotent. Takes the ELEMENT, never finds it.\n *\n * @param {Element} root the shell's root\n * @param {number} percent already clamped\n */\nexport function applyZoom(root, percent) {\n if (!root?.style) return;\n root.style.setProperty('--twm-zoom', String(percent / 100));\n root.classList.toggle('twm-zoomed', percent !== ZOOM_DEFAULT);\n}\n\n/**\n * Paint the control into the element the embedder handed over, restore the saved\n * zoom, and keep the root in step.\n *\n * @param {Element|null} hostEl where the control goes; absent \u2192 no control\n * @param {object} opts\n * @param {Element} opts.root the shell root the zoom applies to\n * @param {object} [opts.host] the host port; its `state` persists the zoom\n * @param {string} [opts.stateKey]\n * @param {Function} [opts.onChange] `(percent) => void`, after every change\n * @returns {{el: Element, get: () => number, set: (percent: number) => void,\n * ready: Promise<number>, dispose: () => void} | null}\n */\nexport function mountZoomControl(hostEl, { root, host = null, stateKey = ZOOM_STATE_KEY, onChange } = {}) {\n if (!hostEl || !root) return null;\n\n const doc = hostEl.ownerDocument;\n let current = ZOOM_DEFAULT;\n let disposed = false;\n\n const el = doc.createElement('div');\n el.className = 'twm-zoom';\n\n const stepButton = (label, title, delta) => {\n const b = doc.createElement('button');\n b.type = 'button';\n b.className = 'twm-zoom__step';\n b.textContent = label;\n b.title = title;\n b.setAttribute('aria-label', title);\n b.addEventListener('click', () => commit(clampZoom(current + delta)));\n return b;\n };\n\n const slider = doc.createElement('input');\n slider.type = 'range';\n slider.className = 'twm-zoom__slider';\n slider.min = String(ZOOM_MIN);\n slider.max = String(ZOOM_MAX);\n slider.step = String(ZOOM_STEP);\n slider.value = String(ZOOM_DEFAULT);\n slider.title = `Zoom the workspace, ${ZOOM_MIN}\u2013${ZOOM_MAX}% \u2014 double-click to reset.`;\n slider.setAttribute('aria-label', 'Zoom the workspace');\n // `input`, NOT `change`: the readout has to follow the thumb while it is\n // being dragged, or the number under the mouse is the number you left.\n slider.addEventListener('input', () => commit(clampZoom(slider.value)));\n // A double-click on the track resets. It arrives after two mousedowns that\n // each set the value and fire `input`, so the honest description is \"one real\n // write, then the reset\", and a brief jump to wherever you clicked is visible\n // before it snaps back. Excel's track does exactly that. It must not be\n // \"fixed\" by swallowing `input` \u2014 that is the event the drag is made of.\n slider.addEventListener('dblclick', () => commit(ZOOM_DEFAULT));\n\n // The readout IS the reset, where Excel puts it and where a hand already is.\n // A separate \"100%\" button would be a fourth control in a bar 20px tall, and a\n // percentage nobody can click answers the question while refusing the obvious\n // next request.\n const readout = doc.createElement('button');\n readout.type = 'button';\n readout.className = 'twm-zoom__value';\n readout.title = `Back to ${ZOOM_DEFAULT}%`;\n readout.addEventListener('click', () => commit(ZOOM_DEFAULT));\n\n el.append(\n stepButton('\u2212', `Zoom out ${ZOOM_NUDGE}%`, -ZOOM_NUDGE),\n slider,\n stepButton('+', `Zoom in ${ZOOM_NUDGE}%`, ZOOM_NUDGE),\n readout,\n );\n hostEl.appendChild(el);\n\n /** Paint the control and the root. No persistence \u2014 used by the restore,\n * where writing back what was just read would be a pointless round trip. */\n const paint = (percent) => {\n current = percent;\n slider.value = String(percent);\n readout.textContent = `${percent}%`;\n applyZoom(root, percent);\n };\n\n // Debounced, because a drag fires `input` per pixel and each one would\n // otherwise be a write through the host. A host without `state` is legal,\n // exactly as it is for the desktops: the control still works, the zoom just\n // does not survive a reload.\n let saveTimer = 0;\n const persist = (percent) => {\n const state = host?.state;\n if (!state) return;\n clearTimeout(saveTimer);\n saveTimer = setTimeout(() => {\n Promise.resolve()\n .then(() => state.write(stateKey, percent))\n .catch((err) => console.warn('[zoom] save failed', err));\n }, 400);\n };\n\n const commit = (percent) => {\n if (disposed || percent === current) return;\n paint(percent);\n persist(percent);\n try { onChange?.(percent); } catch (err) { console.warn('[zoom] onChange threw', err); }\n };\n\n paint(ZOOM_DEFAULT);\n\n // Restored after the control is already on screen and usable: a zoom is not\n // worth blocking a first paint on, and a failed read costs the default. The\n // promise is returned so an embedder that DOES want to wait before mounting \u2014\n // to avoid content painting at 100% and then jumping \u2014 can.\n const ready = Promise.resolve()\n .then(() => host?.state?.read?.(stateKey))\n .then((saved) => {\n if (!disposed && saved != null) paint(clampZoom(saved));\n return current;\n })\n .catch((err) => {\n console.warn('[zoom] load failed', err);\n return current;\n });\n\n return {\n el,\n get: () => current,\n set: (percent) => commit(clampZoom(percent)),\n ready,\n dispose: () => {\n disposed = true;\n clearTimeout(saveTimer);\n el.remove();\n applyZoom(root, ZOOM_DEFAULT);\n },\n };\n}\n", "/**\n * shell.js \u2014 `createShell()`, the library's composition entry point.\n *\n * Requires: root, taxonomy, entities, content\n * Hosts: chrome (ELEMENTS, never selectors), host port, bus, stores\n * Owns: the content registry, the WM, the palette, the keymap, the\n * chrome painters, the tile context menus.\n * Knows: nothing about the embedder. No `document.body`, no selector it\n * did not author, no bus-event string, no content kind.\n *\n * \u2500\u2500 The seam \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\u2500\u2500\n * The embedder does everything up to \"hand the framework a root element and\n * a content map\", and everything after `createShell()` resolves:\n *\n * embedder: gate (is there anything to show?) -> install its own widgets\n * -> build/clear the root element -> build the content map\n * library: registry -> WM -> palette -> keymap -> chrome -> wm.load()\n * embedder: publish the shell, install post-mount extras\n *\n * That order is not stylistic. The widgets decorate DOM the embedder then\n * DELETES to make room for the root element, so `root` cannot exist before\n * the widgets have run \u2014 which is exactly why `createShell` does not try to\n * own a `prepare`/`widgets` lifecycle. It takes a root element that already\n * exists and mounts into it.\n *\n * \u2500\u2500 Content is a constructor argument \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 * `content` is REQUIRED, validated, and frozen into a per-shell registry\n * BEFORE the WM is built. There is no module-level `register()` to call\n * late. That matters: a leaf mounted against a missing factory renders a\n * placeholder, and the renderer caches tile DOM per (kind, props) \u2014 a late\n * registration invalidates nothing, so a whole restored session stays wrong.\n * Making registration impossible after the first mount is the fix.\n *\n * \u2500\u2500 Chrome is elements, not selectors \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 * A selector parameter would be the coupling wearing a hat: the framework\n * would still have to know that a \"top-nav host\" is a thing you find by CSS\n * in someone else's markup. Handing it a node is the smallest honest\n * abstraction \u2014 and an embedder with no chrome passes `chrome: {}` and gets\n * a working WM with none.\n */\n\nimport { WindowManager } from './wm.js';\nimport { createContentRegistry } from './content_registry.js';\nimport { createCommandPalette } from './command_palette.js';\nimport { installKeymap } from './keymap.js';\nimport { openTileTabSwitcher } from './tile_tab_menu.js';\nimport { mountZoomControl } from './zoom.js';\nimport { showContextMenu } from '../ui/components/context_menu.js';\nimport { openForm } from '../ui/components/modal.js';\n\n/**\n * @param {object} cfg\n * @param {Element} cfg.root the element the WM mounts into\n * @param {object} cfg.taxonomy createTaxonomy(...) \u2014 required\n * @param {object} cfg.entities createEntityCatalog(...) \u2014 required\n * @param {object} cfg.content { [kind]: (hostEl, props, ctx) => {...} }\n * @param {object} [cfg.host] the host port (createHost)\n * @param {object} [cfg.api] an opaque back-end handle, threaded into\n * ctx.api for content factories. The library\n * never calls a method on it; it only passes\n * it through (the palette + tile tab menu\n * hand it to the entity catalog's fetchers).\n * @param {object} [cfg.eventBus]\n * @param {object} [cfg.logger]\n * @param {object} [cfg.tableStore] threaded into ctx for table persistence\n * @param {object} [cfg.events] bus-event NAMES the WM listens for\n * @param {object} [cfg.rootCrumb] the breadcrumb's leading segment\n * @param {object} [cfg.palette] { placeholder, onPick } \u2014 C30. `onPick`\n * is `(pick) => truthy` and CLAIMS the\n * open of a picked entity, so an embedder\n * with a rule about where its own entities\n * belong applies that rule whichever door\n * was used. Unclaimed picks still reset\n * the primary tile: the behaviour every\n * embedder has today.\n * @param {object} [cfg.panels] C14. Which panel TILES a fresh desktop\n * opens with \u2014 `{left, right, bottom}`,\n * merged over all-three-open. An embedder\n * whose navigator is its own chrome passes\n * `{left: false, right: false}` and gets no\n * tile it never registered a factory for.\n * @param {boolean} [cfg.promoteInPlace] C21. Whether a window promoted out\n * of a tile stays confined to that pane\n * rather than floating over the whole root.\n * @param {boolean} [cfg.snapPromotion] C15. Whether dragging a promoted\n * window onto a tile puts it back in the\n * tree. Default off \u2014 it changes what a\n * drag to an edge does.\n * @param {'top'|'bottom'} [cfg.tabLayout] C22. Where a multi-tab leaf draws its\n * tabs. `'bottom'` is the framework's own\n * spreadsheet strip under the tile body\n * and is the DEFAULT, so no existing\n * embedder's panes rearrange on upgrade;\n * `'top'` mounts the editor tab bar\n * between the chrome and the body. The\n * renderer mirrors this onto `root` as\n * `data-twm-tabs` and watches it, so an\n * embedder whose settings pane holds only\n * the root element can change it live.\n * @param {object} [cfg.chrome] { topNav?, paletteButton?, desktops?,\n * panelToggles?: { left?, right?, bottom? },\n * zoom? }\n * `zoom` is C31: the element the content\n * zoom control is painted into \u2014 see\n * zoom.js. Absent, there is no control and\n * nothing is ever scaled.\n * @returns {Promise<object>} the frozen shell\n */\nexport async function createShell({\n root,\n taxonomy,\n entities,\n content,\n host = null,\n api = null,\n eventBus = null,\n logger = null,\n tableStore = null,\n events = {},\n rootCrumb = null,\n palette: paletteCfg = {},\n panels = null,\n snapPromotion = false,\n promoteInPlace = false,\n tabLayout = null,\n chrome = {},\n // An embedder that moved its sections out of the top bar \u2014 into an icon\n // rail, say \u2014 passes the selector its own buttons match, and F1..F8 keep\n // working. Omitted, the default top-bar selector applies.\n navSelector = null,\n} = {}) {\n // A bad shell is a BOOT error, not a runtime surprise. Same doctrine as\n // createHost / createTaxonomy.\n if (!root || typeof root.appendChild !== 'function') {\n throw new TypeError('createShell: `root` must be an element');\n }\n if (!taxonomy) throw new Error('createShell: a taxonomy is required');\n if (!entities) throw new Error('createShell: an entity catalog is required');\n\n const log = logger ?? { info(){}, warn(){}, error(){}, debug(){} };\n const registry = createContentRegistry(content); // throws on a non-function\n\n let wm = null;\n let topNavEl = null;\n let desktopsEl = null;\n let toggles = null;\n\n const syncChrome = () => {\n if (!wm) return;\n syncPanelToggles(toggles, wm);\n syncDesktopBar(desktopsEl, wm);\n syncTopNav(topNavEl, wm);\n };\n\n wm = new WindowManager({\n rootEl: root,\n content: registry,\n api,\n eventBus,\n host,\n taxonomy,\n events,\n panelDefaults: panels,\n snapPromotion,\n promoteInPlace,\n tabLayout,\n // `ctx` is the delivery vehicle for leaf-mounted chrome: tile_renderer\n // spreads it into every content factory, which is how the breadcrumb\n // gets `taxonomy` + `rootCrumb` without a content factory knowing they\n // exist. The two `onTile*Menu` keys are read with OPTIONAL CHAINING in\n // tile_renderer \u2014 drop one and right-click silently does nothing.\n ctx: {\n api, eventBus, host, tableStore,\n taxonomy, entities, rootCrumb, events,\n onTileContextMenu: (leafId, x, y) => _tileContextMenu(wm, leafId, x, y),\n onTileTabMenu: (leafId, x, y) => _tileTabMenu(wm, leafId, x, y),\n },\n onChange: () => syncChrome(),\n });\n\n const palette = createCommandPalette({\n wm, api, taxonomy, catalog: entities, ...paletteCfg,\n });\n // `navSelector` lets an embedder that moved its sections out of the top\n // bar keep F1..F8 working. Omitted, the default top-bar selector applies \u2014\n // the behaviour every existing embedder has today.\n const disposeKeymap = installKeymap({ wm, palette, navSelector });\n\n // Chrome \u2014 painted into the ELEMENTS the embedder handed over. An absent\n // key means absent chrome, not a crash.\n const paletteBtn = mountPaletteButton(chrome.paletteButton, palette);\n topNavEl = mountTopNav(chrome.topNav, taxonomy, wm);\n desktopsEl = mountDesktopBar(chrome.desktops, wm);\n // C31. The content zoom. Painted with the rest of the chrome and, unlike the\n // rest, AWAITED before the first mount: its saved value is applied to `root`\n // as a CSS variable, and a shell that mounted first would paint every tile at\n // 100% and then visibly jump to the user's zoom a moment later. A host with no\n // saved zoom \u2014 or no `state` at all \u2014 resolves immediately to the default.\n const zoom = mountZoomControl(chrome.zoom, { root, host });\n if (zoom) await zoom.ready;\n // NOTE: bindPanelToggles CLONES the buttons (to strip whatever state the\n // embedder's own machinery left on them) and returns the FRESH nodes. The\n // originals are detached from here on \u2014 sync against the returned map, not\n // against `chrome.panelToggles`.\n toggles = bindPanelToggles(chrome.panelToggles, wm);\n\n // Belt-and-braces: anything else that mutates the WM fans out to chrome.\n eventBus?.on?.('wm:changed', syncChrome);\n\n await wm.load(); // \u2190 FIRST MOUNT\n syncChrome();\n\n log.info?.('shell mounted');\n\n return Object.freeze({\n wm,\n palette,\n taxonomy,\n entities,\n root,\n content: registry,\n chrome: Object.freeze({\n paletteButtonEl: paletteBtn,\n topNavEl,\n desktopsEl,\n panelToggles: toggles,\n // `get`/`set` so an embedder can drive the zoom from its own settings\n // pane, or read it, without reaching into the control's DOM.\n zoom,\n }),\n // A shell that can be built can be built TWICE \u2014 an embedder that\n // rebuilds on a context change (a different project, a different\n // workspace) does exactly that. Everything this function installs\n // outside `root` has to come off, or the second shell shares the page\n // with the first one's keyboard.\n dispose: () => {\n try { eventBus?.off?.('wm:changed', syncChrome); } catch { /* ignore */ }\n try { disposeKeymap?.(); } catch { /* ignore */ }\n try { palette?.close?.(); } catch { /* ignore */ }\n // Every content factory gets its `destroy()`. Clearing the root\n // element would detach the DOM and tell none of them, so an ERD's\n // `window` keydown listener or a pane's interval would outlive the\n // shell that mounted it. This also stops the renderer painting, so\n // anything still holding a reference to this wm \u2014 a callback\n // captured before a rebuild, a promise that has not settled \u2014\n // cannot repaint a dead tree into a root the live shell now owns.\n try { wm.renderer.destroy(); } catch (err) { log.warn?.('renderer teardown', err); }\n // The zoom lives on `root` as a variable and a class. A rebuilt shell on\n // the same root must not inherit the old one's scale with no control on\n // screen to change it, so dispose puts the root back to 100%.\n try { zoom?.dispose(); } catch { /* ignore */ }\n },\n });\n}\n\n\n// \u2550\u2550 Chrome painters \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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// Each takes an ELEMENT (or nothing) and returns whatever the sync half\n// needs. None of them queries the document.\n\n/** One shortcut button per top-nav kind in the taxonomy, painted into the\n * element the embedder gave us. Clicking opens that kind in the primary tile. */\nfunction mountTopNav(hostEl, taxonomy, wm) {\n if (!hostEl) return null;\n hostEl.innerHTML = '';\n hostEl.classList.add('twm-top-nav');\n for (const k of taxonomy.topNavEntries()) {\n const btn = document.createElement('button');\n btn.className = 'twm-top-nav__btn twm-has-tooltip';\n btn.dataset.kind = k.kind;\n btn.dataset.tooltip = k.label;\n btn.dataset.tooltipPlacement = 'bottom';\n btn.setAttribute('aria-label', k.label);\n btn.innerHTML = `\n <span class=\"material-symbols-outlined twm-top-nav__icon\">${k.icon}</span>\n <span class=\"twm-top-nav__label\">${k.label}</span>\n `;\n hostEl.appendChild(btn);\n }\n hostEl.addEventListener('click', (e) => {\n const btn = e.target.closest('[data-kind]');\n if (!btn) return;\n wm.openInPrimary(btn.dataset.kind);\n });\n return hostEl;\n}\n\nfunction syncTopNav(hostEl, wm) {\n if (!hostEl) return;\n const tree = wm.desktops.active().tree;\n const primaryId = tree.primaryLeafId();\n const primaryKind = primaryId ? tree.get(primaryId)?.content?.kind : null;\n // topNavFor maps an entity kind onto its owning top-nav category. Falls\n // back to the primary kind so unknown kinds at least try to match a button.\n const topNavKind = wm.taxonomy.topNavFor(primaryKind) || primaryKind;\n hostEl.querySelectorAll('[data-kind]').forEach((b) => {\n b.classList.toggle('twm-top-nav__btn--on', b.dataset.kind === topNavKind);\n });\n}\n\n/**\n * The palette button, REBOUND rather than reused.\n *\n * This used to return an existing `#twm-palette-btn` untouched, which is right\n * only while a page mounts one shell and keeps it. An embedder that rebuilds\n * its shell \u2014 Tables does, on every project switch \u2014 got its FIRST shell's\n * button back, still wired to the FIRST shell's palette. Clicking it opened a\n * palette over a disposed window manager, and picking a result called\n * `openInPrimary` on a tree nobody could see, whose renderer then painted it\n * into the root the live shell now owns.\n *\n * So the stale node is replaced, which is exactly what `bindPanelToggles` does\n * one function below and for the same reason.\n */\nfunction mountPaletteButton(hostEl, palette) {\n if (!hostEl) return null;\n hostEl.querySelector('#twm-palette-btn')?.remove();\n const btn = document.createElement('button');\n btn.id = 'twm-palette-btn';\n btn.className = 'twm-panel-toggle-btn twm-has-tooltip';\n btn.dataset.tooltip = 'Command palette (Ctrl+K)';\n btn.dataset.tooltipPlacement = 'bottom';\n btn.setAttribute('aria-label', 'Open command palette');\n btn.innerHTML = '<span class=\"material-symbols-outlined\">menu_open</span>';\n btn.addEventListener('click', () => palette.toggle());\n hostEl.insertBefore(btn, hostEl.firstChild);\n return btn;\n}\n\n/**\n * Take ownership of the embedder's three panel-toggle buttons.\n *\n * cloneNode copies any state the embedder's own machinery left on a button\n * (.active/.pinned/.disabled, the disabled attr, aria-pressed). We strip it so\n * the WM is the sole, clean source of truth \u2014 `syncPanelToggles` manages only\n * `.panel-toggle-btn--on` from here on. The clone keeps the id, so an embedder\n * that drives a toggle by `getElementById(...).click()` still works.\n *\n * @returns {{left?:Element, right?:Element, bottom?:Element}} the FRESH nodes\n */\nfunction bindPanelToggles(map, wm) {\n const fresh = {};\n for (const side of ['left', 'right', 'bottom']) {\n const btn = map?.[side];\n if (!btn) continue;\n const el = btn.cloneNode(true);\n el.classList.remove('active', 'pinned', 'disabled');\n el.disabled = false;\n el.removeAttribute('aria-disabled');\n el.removeAttribute('aria-pressed');\n btn.replaceWith(el);\n el.addEventListener('click', () => wm.togglePanel(side));\n fresh[side] = el;\n }\n return fresh;\n}\n\nfunction syncPanelToggles(map, wm) {\n for (const side of ['left', 'right', 'bottom']) {\n const btn = map?.[side];\n if (!btn) continue;\n btn.classList.toggle('twm-panel-toggle-btn--on', wm.isPanelOpen(side));\n }\n}\n\nfunction mountDesktopBar(hostEl, wm) {\n if (!hostEl) return null;\n let el = hostEl.querySelector('#twm-desktops');\n if (!el) {\n el = document.createElement('div');\n el.id = 'twm-desktops';\n el.className = 'twm-desktops';\n hostEl.appendChild(el);\n }\n el.addEventListener('click', (e) => {\n const btn = e.target.closest('[data-desktop]');\n if (!btn) return;\n const v = btn.dataset.desktop;\n if (v === '+') { wm.addDesktop(); syncDesktopBar(el, wm); return; }\n wm.switchDesktop(Number(v));\n });\n // Double-click a desktop chip \u2192 inline rename. Faster than the context\n // menu's Rename\u2026 for the common case. Persists via wm._persist so the new\n // label survives a restart.\n el.addEventListener('dblclick', async (e) => {\n const btn = e.target.closest('[data-desktop]');\n if (!btn || btn.dataset.desktop === '+') return;\n e.preventDefault();\n const idx = Number(btn.dataset.desktop);\n const d = wm.desktops.desktops[idx];\n if (!d) return;\n const result = await openForm({\n title: 'Rename desktop',\n fields: [{ name: 'label', label: 'Name', type: 'text', required: true,\n hint: 'Up to 24 characters. Shown in the bottom-bar chip and the Alt+N tooltip.' }],\n defaults: { label: d.label },\n submitLabel: 'Rename',\n });\n if (!result || !result.label) return;\n d.label = String(result.label).slice(0, 24);\n syncDesktopBar(el, wm);\n wm._persist();\n });\n el.addEventListener('contextmenu', (e) => {\n const btn = e.target.closest('[data-desktop]');\n if (!btn || btn.dataset.desktop === '+') return;\n e.preventDefault();\n const idx = Number(btn.dataset.desktop);\n const m = wm.desktops;\n const d = m.desktops[idx];\n const canDelete = m.desktops.length > 1;\n showContextMenu(e.clientX, e.clientY, [\n { label: `Switch to ${d.label}`, icon: 'desktop_windows',\n action: 'switch', disabled: idx === m.activeIdx },\n { label: 'Rename\u2026', icon: 'edit', action: 'rename' },\n { separator: true },\n { label: 'New desktop', icon: 'add', action: 'add' },\n { label: 'Delete desktop', icon: 'delete', action: 'delete',\n danger: true, disabled: !canDelete },\n ], async (action) => {\n if (action === 'switch') wm.switchDesktop(idx);\n else if (action === 'rename') {\n const result = await openForm({\n title: 'Rename desktop',\n fields: [{ name: 'label', label: 'Name', type: 'text', required: true }],\n defaults: { label: d.label },\n submitLabel: 'Rename',\n });\n if (!result || !result.label) return;\n d.label = String(result.label).slice(0, 24);\n syncDesktopBar(el, wm);\n wm._persist();\n }\n else if (action === 'add') { wm.addDesktop(); syncDesktopBar(el, wm); }\n else if (action === 'delete') { wm.removeDesktop(idx); syncDesktopBar(el, wm); }\n });\n });\n return el;\n}\n\nfunction syncDesktopBar(el, wm) {\n if (!el) return;\n const m = wm.desktops;\n el.innerHTML =\n m.desktops.map((d, i) => `\n <button class=\"twm-desk__btn${i === m.activeIdx ? ' twm-desk__btn--on' : ''}\"\n data-desktop=\"${i}\"\n title=\"Desktop ${d.label} (Alt+${i + 1}) \u00B7 right-click to delete\">\n ${d.label}\n </button>`).join('')\n + `<button class=\"twm-desk__btn twm-desk__btn--add\"\n data-desktop=\"+\" title=\"Add desktop\">+</button>`;\n}\n\n\n// \u2550\u2550 Tile menus \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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/** Hamburger button in a tile's tab bar. Opens a compact menu anchored to\n * the button that lists the leaf's OPEN TABS for quick switching (a\n * browser-style tab overflow list). Clicking a row activates that tab.\n * The leaf id is captured at click time, so a focus change between click\n * and pick doesn't reroute it. */\nfunction _tileTabMenu(wm, leafId, x, y) {\n const tree = wm.desktops.active().tree;\n const leaf = tree.get(leafId);\n if (!leaf || leaf.kind !== 'leaf') return;\n const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];\n if (tabs.length === 0) return;\n openTileTabSwitcher({\n x, y,\n tabs,\n activeIdx: Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0)),\n onPick: (idx) => {\n tree.setActiveLeafTab(leafId, idx);\n tree.focus(leafId);\n wm.renderer.render();\n wm._persist?.();\n wm._notifyChange?.('tab-switch-from-menu');\n },\n });\n}\n\n/** Right-click on a tile's chrome strip: structural actions on that leaf. */\nfunction _tileContextMenu(wm, leafId, x, y) {\n const tree = wm.desktops.active().tree;\n const leaf = tree.get(leafId);\n if (!leaf) return;\n const isPanel = String(leaf.content?.kind || '').startsWith('panel:');\n tree.focus(leafId);\n wm.renderer._updateFocusClasses();\n\n const items = [\n { label: 'Split horizontally', icon: 'splitscreen_vertical_add', action: 'split-h' },\n { label: 'Split vertically', icon: 'splitscreen_add', action: 'split-v' },\n { separator: true },\n { label: 'Open in new tab', icon: 'tab',\n action: 'open-tab', disabled: isPanel || !leaf.content },\n // TWO DIFFERENT GLYPHS FOR TWO DIFFERENT DESTINATIONS. `web_asset` is a\n // window INSIDE the application \u2014 the same glyph `ManagedWindow` uses\n // for itself \u2014 and an embedder that can also send content to a real\n // browser window keeps `open_in_new`, which is the universal \"this\n // leaves the page\". One glyph for both is how a user learns that the\n // two commands are the same command, and then loses a window looking\n // for it on the other screen.\n { label: 'Open a copy in a window', icon: 'web_asset',\n action: 'open-window', disabled: isPanel || !leaf.content },\n // C20, THE OTHER HALF \u2014 and it was missing while the `close` half\n // below carried a paragraph explaining why it could not be.\n //\n // `_floatableLeaf` (`wm.js`) refuses to float content that declared\n // `chrome: { promote: false }`, and every door converges there \u2014 so\n // this row offered the verb, enabled, and returned null. The chrome's\n // own float BUTTON does not have the problem: C20 removes it from the\n // strip. That asymmetry is what hid this: the affordance the reader\n // checks is correct, and the menu one layer down is not.\n //\n // `=== false` EXACTLY, because that is the test the verb makes\n // (`wm.js`, `_floatableLeaf`: *\"content that says nothing about\n // `promote` stays floatable\"*). A falsy test here would grey the row\n // on every leaf whose content returned no `chrome` at all, which is\n // most of them \u2014 a menu disagreeing with its verb in the generous\n // direction is a dead control; in the mean direction it is a missing\n // feature, and this file has shipped one of each.\n { label: 'Float this pane as a window', icon: 'web_asset',\n action: 'promote',\n disabled: isPanel || !leaf.content\n || wm.renderer?.leafChrome?.(leafId)?.promote === false },\n ];\n if (wm.desktops.desktops.length > 1 && !isPanel) {\n for (const [i, d] of wm.desktops.desktops.entries()) {\n if (i === wm.desktops.activeIdx) continue;\n items.push({ label: `Move to desktop ${d.label}`, icon: 'sweep',\n action: `move:${i}` });\n }\n }\n items.push({ separator: true });\n // C20. THE MENU ROW CARRIES THE VETO, AND THE REASON WITH IT.\n //\n // `closeFocused` refuses when the content vetoed `close`, which is what\n // makes the greyed \u00D7 in the chrome honest \u2014 and left this row offering the\n // same verb, enabled, doing nothing. A control that silently no-ops is the\n // dead-control failure this file has fixed three times already. The\n // tooltip is the content's own sentence, so the two doors explain the\n // refusal identically rather than one explaining it and one not.\n const closeVeto = wm.renderer?.leafChrome?.(leafId)?.close;\n const closeVetoed = closeVeto === false || closeVeto?.disabled === true;\n items.push({ label: 'Close tile', icon: 'close', action: 'close',\n danger: true, disabled: isPanel || closeVetoed,\n title: closeVetoed ? (closeVeto?.title || undefined) : undefined });\n\n showContextMenu(x, y, items, (action) => {\n if (action === 'split-h') wm.split('h');\n else if (action === 'split-v') wm.split('v');\n else if (action === 'open-tab') {\n const c = leaf.content;\n if (c) wm.openInTabFromContext({ leafId }, c.kind, c.props || {});\n }\n else if (action === 'open-window') {\n const c = leaf.content;\n if (c) wm.navigate(c.kind, c.props || {}, { target: 'window' });\n }\n else if (action === 'promote') wm.toggleManagedFocused();\n else if (action === 'close') wm.closeFocused();\n else if (action?.startsWith?.('move:')) {\n wm.moveFocusedToDesktop(Number(action.slice(5)));\n }\n });\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;AAsBA,IAAM,UAAU;AAIhB,IAAM,kBAAkB;AAAA,EACpB;AAAA,IAAE,MAAM;AAAA,IAAW,IAAI;AAAA,IAAa,OAAO;AAAA,IACzC,MAAM;AAAA,IAA6B,MAAM;AAAA,IACzC,QAAQ,MAAM,UAAU,KAAK,oBAAoB;AAAA,EAAE;AACzD;AAUO,SAAS,qBAAqB;AAAA,EAAE;AAAA,EAAI;AAAA,EAAK;AAAA,EAAU;AAAA,EAAS,cAAc;AAAA,EAC1C,SAAS;AAAK,GAAG;AACpD,MAAI,CAAC,SAAU,OAAM,IAAI,MAAM,8CAA8C;AAC7E,MAAI,CAAC,QAAU,OAAM,IAAI,MAAM,qDAAqD;AACpF,MAAI,UAAU;AACd,MAAI,WAAW,CAAC;AAEhB,QAAM,SAAS,MAAM,CAAC,CAAC;AACvB,QAAM,QAAQ,MAAM;AAChB,QAAI,CAAC,QAAS;AACd,YAAQ,OAAO;AACf,cAAU;AAAA,EACd;AACA,QAAM,SAAS,MAAM;AAAE,WAAO,IAAI,MAAM,IAAI,KAAK;AAAA,EAAG;AAEpD,QAAM,OAAO,YAAY;AACrB,QAAI,QAAS;AACb,cAAU,SAAS,cAAc,KAAK;AACtC,YAAQ,KAAK;AACb,YAAQ,YAAY;AACpB,YAAQ,YAAY,QAAQ,UAAU,aAAa,EAAE;AACrD,aAAS,KAAK,YAAY,OAAO;AACjC,YAAQ,iBAAiB,SAAS,CAAC,MAAM;AACrC,UAAI,EAAE,WAAW,QAAS,OAAM;AAAA,IACpC,CAAC;AAED,UAAM,QAAQ,QAAQ,cAAc,oBAAoB;AACxD,UAAM,OAAO,QAAQ,cAAc,oBAAoB;AACvD,UAAM,WAAW,QAAQ,cAAc,yBAAyB;AAChE,UAAM,QAAQ,QAAQ,iBAAiB,iBAAiB;AACxD,UAAM,UAAU,QAAQ,iBAAiB,eAAe;AAGxD,eAAW,OAAO,SAAS;AACvB,YAAM,QAAQ,IAAI,QAAQ;AAC1B,UAAI,UAAU,OAAO,gBAAgB,GAAG,YAAY,KAAK,CAAC;AAAA,IAC9D;AAEA,UAAM,QAAQ,CAAC,QAAQ,IAAI,iBAAiB,SAAS,MAAM;AACvD,YAAM,OAAO,IAAI,QAAQ;AACzB,SAAG,cAAc,IAAI;AACrB,YAAM;AAAA,IACV,CAAC,CAAC;AACF,YAAQ,QAAQ,CAAC,QAAQ,IAAI,iBAAiB,SAAS,MAAM;AACzD,YAAM,QAAQ,IAAI,QAAQ;AAC1B,SAAG,YAAY,KAAK;AACpB,UAAI,UAAU,OAAO,gBAAgB,GAAG,YAAY,KAAK,CAAC;AAAA,IAC9D,CAAC,CAAC;AAWF,QAAI,WAAW,CAAC;AAChB,QAAI,SAAS;AACb,QAAI,aAAa;AACjB,QAAI,kBAAkB;AAEtB,UAAM,aAAa,MAAM;AACrB,UAAI,CAAC,YAAY;AACb,iBAAS,YAAY;AACrB;AAAA,MACJ;AAMA,YAAM,OAAO,WAAW,CAAC;AACzB,YAAM,OAAO,SAAS,KAAK,IAAI;AAC/B,YAAM,QAAQ,mBAAmB,MAAM,SAAS;AAChD,eAAS,YAAY;AAAA,0EACyC,KAAK,KAAK,CAAC,KAAK,WAAW,MAAM,QAAQ,WAAW,WAAW,IAAI,KAAK,GAAG;AAAA,yFAC5D,MAAM,QAAQ,aAAa;AAAA,gEACpD,KAAK,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAM/D,eAAS,cAAc,4BAA4B,GAC7C,iBAAiB,SAAS,CAAC,OAAO;AAChC,WAAG,eAAe;AAClB,qBAAa;AACb,0BAAkB;AAClB,mBAAW;AACX,cAAM,MAAM;AACZ,iBAAS;AAAA,MACb,CAAC;AAAA,IACT;AAEA,UAAM,SAAS,MAAM;AACjB,WAAK,YAAY,SAAS,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,GAAG,MAAM;AAAA,8CACpB,MAAM,SAAS,8BAA8B,EAAE;AAAA,iCAC5D,CAAC;AAAA,+EAC6C,EAAE,QAAQ,aAAa;AAAA,sDAChD,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC;AAAA,qDACtB,EAAE,IAAI;AAAA,qDACN,KAAK,EAAE,QAAQ,EAAE,CAAC;AAAA;AAAA,aAE1D,EAAE,KAAK,EAAE,KAAK;AACf,WAAK,iBAAiB,YAAY,EAAE,QAAQ,CAAC,OAAO;AAChD,WAAG,iBAAiB,SAAS,MAAM,OAAO,OAAO,GAAG,QAAQ,GAAG,CAAC,CAAC;AAAA,MACrE,CAAC;AAAA,IACL;AAkCA,UAAM,SAAS,CAAC,MAAM;AAClB,YAAM,OAAO,SAAS,CAAC;AACvB,UAAI,CAAC,KAAM;AACX,YAAM;AACN,UAAI,KAAK,QAAQ;AAAE,YAAI;AAAE,eAAK,OAAO;AAAA,QAAG,SAAS,KAAK;AAAE,kBAAQ,MAAM,GAAG;AAAA,QAAG;AAAE;AAAA,MAAQ;AACtF,UAAI,QAAQ;AACR,YAAI,UAAU;AACd,YAAI;AAAE,oBAAU,OAAO,IAAI,KAAK;AAAA,QAAO,SAChC,KAAK;AAAE,kBAAQ,MAAM,yBAAyB,GAAG;AAAA,QAAG;AAC3D,YAAI,QAAS;AAAA,MACjB;AACA,SAAG,cAAc,KAAK,MAAM,KAAK,SAAS,EAAE,IAAI,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC;AAAA,IAChF;AAOA,UAAM,mBAAmB,MAAM;AAC3B,UAAI,WAAY,QAAO;AACvB,YAAM,IAAI,MAAM,MAAM,MAAM,4BAA4B;AACxD,UAAI,CAAC,EAAG,QAAO;AACf,YAAM,QAAQ,QAAQ,kBAAkB,EAAE,CAAC,CAAC;AAC5C,UAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO;AACzC,mBAAa;AACb,wBAAkB,WAAW,EAAE,CAAC,CAAC;AACjC,YAAM,QAAQ,MAAM,MAAM,MAAM,EAAE,CAAC,EAAE,MAAM;AAC3C,iBAAW;AACX,aAAO;AAAA,IACX;AAEA,UAAM,WAAW,MAAM;AACnB,uBAAiB;AACjB,YAAM,IAAI,MAAM,MAAM,KAAK,EAAE,YAAY;AACzC,YAAM,YAAY,aAAa,IAAI,IAAI,UAAU,IAAI;AACrD,YAAM,OAAO,YACP,SAAS,OAAO,CAAC,MAAM,UAAU,IAAI,EAAE,IAAI,CAAC,IAC5C;AACN,UAAI,CAAC,GAAG;AAKJ,mBAAW,aACL,KAAK,MAAM,EAAE,KAAK,CAAC,GAAG,MACpB,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE,CAAC,IACxD,CAAC;AAAA,MACX,OAAO;AACH,mBAAW,QAAQ,MAAM,CAAC;AAAA,MAC9B;AACA,eAAS;AACT,aAAO;AAAA,IACX;AACA,UAAM,iBAAiB,SAAS,QAAQ;AACxC,UAAM,iBAAiB,WAAW,CAAC,MAAM;AACrC,UAAI,EAAE,QAAQ,aAAa;AACvB,UAAE,eAAe;AACjB,iBAAS,KAAK,IAAI,SAAS,SAAS,GAAG,SAAS,CAAC;AACjD,eAAO;AAAA,MACX,WAAW,EAAE,QAAQ,WAAW;AAC5B,UAAE,eAAe;AACjB,iBAAS,KAAK,IAAI,GAAG,SAAS,CAAC;AAC/B,eAAO;AAAA,MACX,WAAW,EAAE,QAAQ,SAAS;AAC1B,UAAE,eAAe;AACjB,eAAO,MAAM;AAAA,MACjB,WAAW,EAAE,QAAQ,UAAU;AAC3B,cAAM;AAAA,MACV,WAAW,EAAE,QAAQ,eAAe,MAAM,UAAU,MAAM,YAAY;AAKlE,UAAE,eAAe;AACjB,qBAAa;AACb,0BAAkB;AAClB,mBAAW;AACX,iBAAS;AAAA,MACb;AAAA,IACJ,CAAC;AACD,UAAM,MAAM;AAOZ,eAAW,eAAe,gBAAgB,MAAM,GAAG,QAAQ;AAC3D,QAAI,KAAK;AACL,UAAI;AACA,mBAAW;AAAA,UACP,CAAC,GAAG,iBAAiB,GAAG,MAAM,QAAQ,QAAQ,GAAG,CAAC;AAAA,UAAG;AAAA,QAAQ;AACjE,YAAI,MAAM,MAAO,UAAS;AAAA,MAC9B,SAAS,KAAK;AACV,gBAAQ,KAAK,+BAA+B,GAAG;AAAA,MACnD;AAAA,IACJ;AAAA,EACJ;AAEA,SAAO,EAAE,MAAM,OAAO,QAAQ,OAAO;AACzC;AAEA,SAAS,QAAQ,UAAU,aAAa,IAAI;AAUxC,QAAM,cAAc;AAAA,IAChB,EAAE,MAAM,QAAU,MAAM,QAAmB,OAAO,WAAW;AAAA,IAC7D,EAAE,MAAM,SAAU,MAAM,gBAAmB,OAAO,cAAc;AAAA,IAChE,EAAE,MAAM,UAAU,MAAM,kBAAmB,OAAO,eAAe;AAAA,EACrE;AACA,QAAM,eAAe,YAChB,OAAO,CAAC,MAAM,GAAG,SAAS,MAAM,SAAS,EAAE,IAAI,EAAE,MAAM,KAAK,EAC5D,IAAI,CAAC,MAAM;AAAA,wDACoC,EAAE,IAAI;AAAA,8DACA,EAAE,IAAI;AAAA,sBAC9C,EAAE,KAAK;AAAA,0BACH,EAAE,KAAK,EAAE;AAE/B,QAAM,QAAQ,SAAS,cAAc,EAAE,IAAI,CAAC,MAAM;AAAA,kDACJ,EAAE,IAAI;AAAA,sDACF,EAAE,IAAI;AAAA,cAC9C,EAAE,KAAK;AAAA;AAAA,KAEhB,EAAE,KAAK,EAAE;AACV,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,sCAK2B,KAAK,WAAW,CAAC;AAAA;AAAA;AAAA,6CAGV,KAAK;AAAA,+CACH,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAS3D;AAKA,SAAS,eAAe,MAAM,UAAU;AACpC,SAAO,KAAK,IAAI,CAAC,MAAM;AACnB,QAAI,EAAE,KAAM,QAAO;AACnB,UAAM,OAAO,SAAS,KAAK,EAAE,IAAI;AACjC,WAAO,EAAE,GAAG,GAAG,MAAM,MAAM,QAAQ,cAAc;AAAA,EACrD,CAAC;AACL;AAEA,SAAS,QAAQ,UAAU,GAAG;AAC1B,QAAM,SAAS,CAAC;AAChB,aAAW,KAAK,UAAU;AACtB,UAAM,KAAK,OAAO,EAAE,MAAM,EAAE,EAAE,YAAY;AAC1C,UAAM,QAAQ,OAAO,EAAE,SAAS,EAAE,EAAE,YAAY;AAChD,UAAM,OAAO,OAAO,EAAE,QAAQ,EAAE,EAAE,YAAY;AAC9C,QAAI;AACJ,QAAI,OAAO,KAAK,UAAU,EAAG,KAAI;AAAA,aACxB,GAAG,WAAW,CAAC,EAAM,KAAI;AAAA,aACzB,MAAM,WAAW,CAAC,EAAG,KAAI;AAAA,aACzB,GAAG,SAAS,CAAC,EAAQ,KAAI;AAAA,aACzB,MAAM,SAAS,CAAC,EAAK,KAAI;AAAA,aACzB,KAAK,SAAS,CAAC,EAAM,KAAI;AAAA,QAC7B;AACL,WAAO,KAAK,EAAE,GAAG,EAAE,CAAC;AAAA,EACxB;AACA,SAAO,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,MAAM,cAAc,EAAE,EAAE,KAAK,CAAC;AACrE,SAAO,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;AAChC;AAEA,SAAS,KAAK,GAAG;AACb,SAAO,OAAO,KAAK,EAAE,EAAE,QAAQ,YAAY,CAAC,OAAO;AAAA,IAC/C,KAAK;AAAA,IAAS,KAAK;AAAA,IAAQ,KAAK;AAAA,IAAQ,KAAK;AAAA,IAAU,KAAK;AAAA,EAChE,GAAE,CAAC,CAAE;AACT;AAMA,SAAS,WAAW,GAAG;AACnB,QAAM,MAAM,OAAO,KAAK,EAAE,EAAE,QAAQ,UAAU,GAAG,EAAE,KAAK;AACxD,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,IAAI,OAAO,CAAC,EAAE,YAAY,IAAI,IAAI,MAAM,CAAC,EAAE,YAAY;AAClE;;;AC1VO,IAAM,mBAAmB;AAIhC,SAAS,yBAAyB,QAAQ,OAAO,KAAK;AAClD,SAAO,YAAY;AAAA;AAAA;AAAA,yDAGkCA,MAAK,MAAM,iBAAiB,MAAM,gBAAgB,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOjH,SAAO,cAAc,4BAA4B,GAAG,iBAAiB,SAAS,MAAM;AAChF,QAAI,IAAI,kBAAkB,MAAM,QAAQ;AAAA,EAC5C,CAAC;AACD,SAAO,EAAE,OAAO,GAAG,MAAM,iBAAiB,MAAM,YAAY,YAAY;AAC5E;AAQO,SAAS,sBAAsB,MAAM,CAAC,GAAG;AAC5C,MAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACjC,UAAM,IAAI,UAAU,uEAAuE;AAAA,EAC/F;AACA,aAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC/C,QAAI,OAAO,YAAY,YAAY;AAC/B,YAAM,IAAI;AAAA,QACN,uCAAuC,IAAI;AAAA,MAAsB;AAAA,IACzE;AAAA,EACJ;AACA,QAAM,YAAY,IAAI,IAAI,OAAO,QAAQ,GAAG,CAAC;AAC7C,MAAI,CAAC,UAAU,IAAI,gBAAgB,GAAG;AAClC,cAAU,IAAI,kBAAkB,wBAAwB;AAAA,EAC5D;AAEA,SAAO,OAAO,OAAO;AAAA,IACjB,KAAK,CAAC,SAAS,UAAU,IAAI,IAAI;AAAA,IAEjC,OAAO,CAAC,MAAM,QAAQ,OAAO,QAAQ;AACjC,YAAM,UAAU,UAAU,IAAI,IAAI,KAAK,aAAa,IAAI;AACxD,UAAI;AACA,eAAO,QAAQ,QAAQ,SAAS,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC;AAAA,MACvD,SAAS,KAAK;AACV,gBAAQ,MAAM,uCAAuC,MAAM,GAAG;AAC9D,eAAO,YACH,6CAA6C,OAAO,IAAI,WAAW,GAAG;AAC1E,eAAO,CAAC;AAAA,MACZ;AAAA,IACJ;AAAA,EACJ,CAAC;AACL;AAEA,SAAS,aAAa,MAAM;AACxB,SAAO,CAAC,WAAW;AACf,WAAO,YAAY;AAAA;AAAA,uDAE4B,IAAI;AAAA;AAAA;AAAA;AAInD,WAAO,EAAE,OAAO,KAAK;AAAA,EACzB;AACJ;AAEA,SAASA,MAAK,GAAG;AACb,SAAO,OAAO,KAAK,EAAE,EAAE,QAAQ,YAAY,CAAC,OACxC,EAAE,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ,GAAE,CAAC,CAC5E;AACL;;;ACzFA,IAAI,aAAa;AACjB,SAAS,OAAO,QAAQ;AAAE,SAAO,GAAG,MAAM,IAAI,YAAY;AAAI;AAE9D,SAAS,SAAS,MAAM;AACpB,SAAO,OAAO,MAAM,SAAS,QAAQ,EAAE,EAAE,WAAW,QAAQ;AAChE;AAIA,SAAS,eAAe,MAAM;AAC1B,MAAI,CAAC,QAAQ,KAAK,SAAS,OAAQ;AACnC,QAAM,OAAO,KAAK,QAAQ,CAAC;AAC3B,MAAI,KAAK,WAAW,GAAG;AACnB,SAAK,UAAU;AACf,SAAK,QAAQ,KAAK,SAAS;AAC3B,SAAK,eAAe;AACpB;AAAA,EACJ;AACA,MAAI,KAAK,gBAAgB,QAClB,KAAK,eAAe,KACpB,KAAK,gBAAgB,KAAK,QAAQ;AACrC,SAAK,eAAe,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,KAAK,gBAAgB,CAAC,CAAC;AAAA,EACrF;AACA,QAAM,IAAI,KAAK,KAAK,YAAY;AAChC,OAAK,UAAU,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,SAAS,CAAC,EAAE;AACpD,OAAK,QAAQ,EAAE,SAAS,EAAE,QAAQ;AACtC;AAEO,SAAS,SAAS,EAAE,UAAU,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG;AAC1D,QAAM,OAAO;AAAA,IACT,IAAI,OAAO,MAAM;AAAA,IACjB,MAAM;AAAA,IACN,UAAU;AAAA;AAAA,IAEV,MAAM,UACA,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,QAAQ,SAAS,CAAC,GAAG,OAAO,SAAS,QAAQ,QAAQ,GAAG,CAAC,IACvF,CAAC;AAAA,IACP,cAAc,UAAU,IAAI;AAAA,IAC5B;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,UAAU,CAAC;AAAA,EACf;AACA,iBAAe,IAAI;AACnB,SAAO;AACX;AAEO,SAAS,UAAU,EAAE,MAAM,KAAK,WAAW,CAAC,GAAG,QAAQ,KAAK,IAAI,CAAC,GAAG;AACvE,SAAO;AAAA,IACH,IAAI,OAAO,OAAO;AAAA,IAClB,MAAM;AAAA,IACN,UAAU;AAAA,IACV;AAAA,IACA,UAAU,SAAS,MAAM;AAAA,IACzB,OAAO,QAAQ,MAAM,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;AAAA,EACvD;AACJ;AAOO,IAAM,WAAN,MAAM,UAAS;AAAA,EAClB,cAAc;AACV,SAAK,QAAQ,oBAAI,IAAI;AACrB,SAAK,SAAS;AACd,SAAK,gBAAgB;AAGrB,SAAK,aAAa,CAAC;AAAA,EACvB;AAAA;AAAA,EAGA,IAAI,IAAI;AAAE,WAAO,KAAK,MAAM,IAAI,EAAE,KAAK;AAAA,EAAM;AAAA,EAC7C,OAAO;AAAE,WAAO,KAAK,SAAS,KAAK,IAAI,KAAK,MAAM,IAAI;AAAA,EAAM;AAAA,EAC5D,UAAU;AAAE,WAAO,KAAK,gBAAgB,KAAK,IAAI,KAAK,aAAa,IAAI;AAAA,EAAM;AAAA,EAE7E,SAAS;AACL,UAAM,MAAM,CAAC;AACb,UAAM,OAAO,CAAC,OAAO;AACjB,YAAM,IAAI,KAAK,IAAI,EAAE;AACrB,UAAI,CAAC,EAAG;AACR,UAAI,EAAE,SAAS,OAAQ,KAAI,KAAK,CAAC;AAAA,UAC5B,GAAE,SAAS,QAAQ,IAAI;AAAA,IAChC;AACA,QAAI,KAAK,OAAQ,MAAK,KAAK,MAAM;AACjC,WAAO;AAAA,EACX;AAAA;AAAA,EAGA,QAAQ,MAAM;AACV,SAAK,UAAU,IAAI;AACnB,SAAK,SAAS,KAAK;AACnB,QAAI,KAAK,SAAS,QAAQ;AACtB,WAAK,MAAM,KAAK,EAAE;AAAA,IACtB;AACA,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,UAAU,MAAM;AACZ,SAAK,MAAM,IAAI,KAAK,IAAI,IAAI;AAC5B,QAAI,KAAK,SAAS,SAAS;AACvB,iBAAW,OAAO,KAAK,UAAU;AAC7B,cAAM,IAAI,KAAK,IAAI,GAAG;AACtB,YAAI,EAAG,GAAE,WAAW,KAAK;AAAA,MAC7B;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA,EAGA,MAAM,QAAQ;AACV,QAAI,CAAC,KAAK,MAAM,IAAI,MAAM,EAAG;AAC7B,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,QAAI,KAAK,SAAS,OAAQ;AAC1B,SAAK,gBAAgB;AACrB,SAAK,aAAa,KAAK,WAAW,OAAO,CAAC,OAAO,OAAO,MAAM;AAC9D,SAAK,WAAW,QAAQ,MAAM;AAAA,EAClC;AAAA;AAAA;AAAA,EAIA,gBAAgB;AACZ,eAAW,MAAM,KAAK,YAAY;AAC9B,YAAM,IAAI,KAAK,MAAM,IAAI,EAAE;AAC3B,UAAI,KAAK,EAAE,SAAS,UAAU,CAAC,SAAS,CAAC,EAAG,QAAO;AAAA,IACvD;AACA,eAAW,QAAQ,KAAK,OAAO,GAAG;AAC9B,UAAI,CAAC,SAAS,IAAI,EAAG,QAAO,KAAK;AAAA,IACrC;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAe,QAAQ,SAAS,QAAQ,IAAI;AACxC,UAAM,IAAI,KAAK,IAAI,MAAM;AACzB,QAAI,CAAC,KAAK,EAAE,SAAS,OAAQ,QAAO;AACpC,QAAI,SAAS;AACT,QAAE,OAAO,CAAC;AAAA,QACN,MAAO,QAAQ;AAAA,QACf,OAAO,QAAQ,SAAS,CAAC;AAAA,QACzB,OAAO,SAAS,QAAQ,QAAQ;AAAA,MACpC,CAAC;AACD,QAAE,eAAe;AAAA,IACrB,OAAO;AACH,QAAE,OAAO,CAAC;AACV,QAAE,eAAe;AAAA,IACrB;AACA,mBAAe,CAAC;AAChB,QAAI,MAAO,GAAE,QAAQ;AACrB,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,WAAW,QAAQ,QAAQ,eAAe,cAAc,OAAO,CAAC,GAAG;AAC/D,UAAM,IAAI,KAAK,IAAI,MAAM;AACzB,QAAI,CAAC,KAAK,EAAE,SAAS,OAAQ,QAAO;AACpC,QAAI,CAAC,UAAU,CAAC,OAAO,KAAM,QAAO;AACpC,MAAE,WAAW,EAAE,YAAY,CAAC;AAC5B,UAAM,gBAAgB,KAAK,kBAAkB;AAO7C,UAAM,YAAY,CAAC,SAAS,MAAM,MAAM,UAAU;AAC9C,UAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,KAAK,KAAM,QAAO;AAClD,YAAM,OAAO,QAAQ,QAAQ,SAAS,CAAC;AACvC,YAAM,aAAa,QACZ,KAAK,SAAS,KAAK,QACnB,OAAO,KAAK,OAAO,MAAM,EAAE,MAAM,OAAO,KAAK,OAAO,MAAM,EAAE;AACnE,YAAM,eAAe,QACd,KAAK,SAAS,KAAK,QACnB,OAAO,KAAK,OAAO,MAAM,EAAE,MAAM,OAAO,KAAK,OAAO,MAAM,EAAE;AACnE,UAAI,CAAC,cAAc,CAAC,cAAc;AAC9B,gBAAQ,KAAK;AAAA,UACT,MAAO,KAAK;AAAA,UACZ,OAAO,KAAK,SAAS,CAAC;AAAA,UACtB,OAAO,KAAK,SAAS;AAAA,UACrB,GAAI,SAAS,CAAC;AAAA,QAClB,CAAC;AAAA,MACL;AACA,aAAO;AAAA,IACX;AAEA,UAAM,OAAO,iBAAiB,kBAAkB;AAChD,QAAI,MAAM;AACN,YAAM,OAAO,EAAE,QAAQ,CAAC;AACxB,YAAM,WAAW,KAAK,UAAU,CAAC,MAC7B,EAAE,SAAS,OAAO,QACf,OAAO,EAAE,OAAO,MAAM,EAAE,MAAM,OAAO,OAAO,OAAO,MAAM,EAAE,CAAC;AACnE,UAAI,YAAY,GAAG;AACf,UAAE,eAAe;AAAA,MACrB,WAAW,KAAK,WAAW,GAAG;AAC1B,UAAE,OAAO,CAAC;AAAA,UACN,MAAM,OAAO;AAAA,UACb,OAAO,OAAO,SAAS,CAAC;AAAA,UACxB,OAAO,OAAO,SAAS,OAAO;AAAA,UAC9B,SAAS,CAAC;AAAA,QACd,CAAC;AACD,UAAE,eAAe;AAAA,MACrB,OAAO;AACH,cAAM,OAAO,KAAK,EAAE,YAAY;AAChC,cAAM,UAAU,MAAM,QAAQ,MAAM,OAAO,IAAI,KAAK,UAAU,CAAC;AAC/D,kBAAU,SAAS,MAAM,QAAQ,IAAI;AACrC,aAAK,EAAE,YAAY,IAAI;AAAA,UACnB,MAAM,OAAO;AAAA,UACb,OAAO,OAAO,SAAS,CAAC;AAAA,UACxB,OAAO,OAAO,SAAS,OAAO;AAAA,UAC9B;AAAA,QACJ;AAAA,MACJ;AACA,qBAAe,CAAC;AAChB,aAAO;AAAA,IACX;AAIA,UAAM,UAAU,MAAM,QAAQ,EAAE,IAAI,IAAI,EAAE,OAAO,CAAC;AAClD,UAAM,YAAY,QAAQ,EAAE,YAAY;AACxC,UAAM,cAAe,iBAAiB,WAAW,OAAQ;AAAA,MACrD,MAAO,UAAU;AAAA,MACjB,OAAO,UAAU,SAAS,CAAC;AAAA,MAC3B,OAAO,UAAU,SAAS;AAAA,MAC1B,QAAQ;AAAA,IACZ,IAAI;AAKJ,QAAI,iBAAiB,QAAQ,SAAS,GAAG;AAGrC,YAAM,WAAW,QACZ,OAAO,CAAC,MAAM,CAAC,EAAE,SAAS,EAC1B,IAAI,CAAC,OAAO;AAAA,QACT,MAAM,EAAE;AAAA,QAAM,OAAO,EAAE,GAAI,EAAE,SAAS,CAAC,EAAG;AAAA,QAAG,OAAO,EAAE;AAAA,QACtD,SAAS,MAAM,QAAQ,EAAE,OAAO,IAAI,EAAE,QAAQ,MAAM,IAAI,CAAC;AAAA,MAC7D,EAAE;AACN,QAAE,SAAS,aAAa,IAAI;AAAA,QACxB,MAAM;AAAA,QACN,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,SAAS,GAAG,EAAE,gBAAgB,CAAC,CAAC;AAAA,MAChF;AAAA,IACJ;AAGA,UAAM,QAAQ,EAAE,SAAS,YAAY;AACrC,QAAI,SAAS,MAAM,QAAQ,MAAM,IAAI,KAAK,MAAM,KAAK,SAAS,GAAG;AAC7D,QAAE,OAAO,MAAM,KAAK,IAAI,CAAC,OAAO;AAAA,QAC5B,MAAM,EAAE;AAAA,QAAM,OAAO,EAAE,GAAI,EAAE,SAAS,CAAC,EAAG;AAAA,QAAG,OAAO,EAAE;AAAA,QACtD,SAAS,MAAM,QAAQ,EAAE,OAAO,IAAI,EAAE,QAAQ,MAAM,IAAI,CAAC;AAAA,MAC7D,EAAE;AACF,QAAE,eAAe,KAAK;AAAA,QAAI;AAAA,QACtB,KAAK,IAAI,EAAE,KAAK,SAAS,GAAG,MAAM,gBAAgB,CAAC;AAAA,MAAC;AAmBxD,YAAM,cAAc,OAAO,OAAO,MAAM,QACpB,OAAO,SAAS,gBAChB,OAAO,KAAK,OAAO,SAAS,CAAC,CAAC,EAAE,SAAS;AAC7D,UAAI,aAAa;AACb,cAAM,SAAS,OAAO,OAAO,MAAM;AACnC,cAAM,WAAW,EAAE,KAAK,UAAU,CAAC,MAC/B,EAAE,SAAS,OAAO,SACd,CAAC,UACE,OAAO,EAAE,OAAO,MAAM,EAAE,MAAM,OAAO,OAAO,MAAM,EAAE,EAAE;AACjE,YAAI,YAAY,GAAG;AACf,YAAE,eAAe;AAAA,QACrB,OAAO;AACH,gBAAM,UAAU,CAAC;AACjB,cAAI,YAAa,WAAU,SAAS,aAAa,QAAQ,EAAE,QAAQ,cAAc,CAAC;AAClF,YAAE,KAAK,KAAK;AAAA,YACR,MAAM,OAAO;AAAA,YACb,OAAO,OAAO,SAAS,CAAC;AAAA,YACxB,OAAO,OAAO,SAAS,OAAO;AAAA,YAC9B;AAAA,UACJ,CAAC;AACD,YAAE,eAAe,EAAE,KAAK,SAAS;AAAA,QACrC;AAAA,MACJ;AAAA,IACJ,OAAO;AACH,YAAM,UAAU,CAAC;AACjB,UAAI,YAAa,WAAU,SAAS,aAAa,QAAQ,EAAE,QAAQ,cAAc,CAAC;AAClF,QAAE,OAAO,CAAC;AAAA,QACN,MAAM,OAAO;AAAA,QACb,OAAO,OAAO,SAAS,CAAC;AAAA,QACxB,OAAO,OAAO,SAAS,OAAO;AAAA,QAC9B;AAAA,MACJ,CAAC;AACD,QAAE,eAAe;AAAA,IACrB;AACA,mBAAe,CAAC;AAChB,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAAmB,MAAM,IAAI,UAAU;AACnC,QAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAU,QAAO;AACtC,QAAI,UAAU;AACd,UAAM,QAAQ,CAAC,SAAS;AACpB,UAAI,CAAC,MAAM,QAAQ,IAAI,EAAG;AAC1B,iBAAW,KAAK,MAAM;AAClB,YAAI,EAAE,SAAS,QAAQ,OAAO,EAAE,OAAO,MAAM,EAAE,MAAM,OAAO,EAAE,GAAG;AAC7D,YAAE,QAAQ,OAAO,QAAQ;AACzB,cAAI,EAAE,MAAO,GAAE,MAAM,QAAQ,OAAO,QAAQ;AAC5C,oBAAU;AAAA,QACd;AAAA,MACJ;AAAA,IACJ;AACA,eAAW,KAAK,KAAK,MAAM,OAAO,GAAG;AACjC,UAAI,EAAE,SAAS,OAAQ;AACvB,YAAM,EAAE,IAAI;AACZ,UAAI,EAAE,UAAU;AACZ,mBAAW,OAAO,OAAO,OAAO,EAAE,QAAQ,EAAG,OAAM,KAAK,IAAI;AAAA,MAChE;AACA,qBAAe,CAAC;AAAA,IACpB;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,wBAAwB,QAAQ,SAAS,QAAQ,IAAI,OAAO,CAAC,GAAG;AAC5D,UAAM,IAAI,KAAK,IAAI,MAAM;AACzB,QAAI,CAAC,KAAK,EAAE,SAAS,OAAQ,QAAO;AACpC,UAAM,OAAO,MAAM,QAAQ,EAAE,IAAI,IAAI,EAAE,OAAO,CAAC;AAC/C,QAAI,KAAK,WAAW,GAAG;AACnB,aAAO,KAAK,eAAe,QAAQ,SAAS,KAAK;AAAA,IACrD;AACA,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,EAAE,gBAAgB,CAAC,CAAC;AACtE,UAAM,OAAO,KAAK,GAAG;AACrB,UAAM,gBAAgB,KAAK,kBAAkB;AAC7C,UAAM,UAAU,MAAM,QAAQ,MAAM,OAAO,IAAI,KAAK,UAAU,CAAC;AAC/D,QAAI,iBAAiB,QAAQ,KAAK,MAAM;AAGpC,YAAM,OAAO,QAAQ,QAAQ,SAAS,CAAC;AACvC,YAAM,aAAa,QACZ,KAAK,SAAS,KAAK,QACnB,OAAO,KAAK,OAAO,MAAM,EAAE,MAAM,OAAO,KAAK,OAAO,MAAM,EAAE;AACnE,YAAM,eAAe,KAAK,SAAS,QAAQ,QACpC,OAAO,KAAK,OAAO,MAAM,EAAE,MAAM,OAAO,QAAQ,OAAO,MAAM,EAAE;AACtE,UAAI,CAAC,cAAc,CAAC,cAAc;AAC9B,gBAAQ,KAAK;AAAA,UACT,MAAO,KAAK;AAAA,UACZ,OAAO,KAAK,SAAS,CAAC;AAAA,UACtB,OAAO,KAAK,SAAS;AAAA,QACzB,CAAC;AAAA,MACL;AAAA,IACJ;AACA,SAAK,GAAG,IAAI;AAAA,MACR,MAAO,QAAQ;AAAA,MACf,OAAO,QAAQ,SAAS,CAAC;AAAA,MACzB,OAAO,SAAS,QAAQ,QAAQ;AAAA,MAChC;AAAA,IACJ;AACA,mBAAe,CAAC;AAChB,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBAAqB,QAAQ,OAAO;AAChC,UAAM,IAAI,KAAK,IAAI,MAAM;AACzB,QAAI,CAAC,KAAK,EAAE,SAAS,OAAQ,QAAO;AACpC,UAAM,OAAO,MAAM,QAAQ,EAAE,IAAI,IAAI,EAAE,OAAO,CAAC;AAC/C,QAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,UAAM,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,EAAE,gBAAgB,CAAC,CAAC;AACtE,UAAM,MAAM,KAAK,GAAG;AACpB,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI,QAAQ,EAAE,GAAI,IAAI,SAAS,CAAC,GAAI,GAAI,SAAS,CAAC,EAAG;AACrD,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,oBAAoB,QAAQ;AACxB,UAAM,IAAI,KAAK,IAAI,MAAM;AACzB,QAAI,CAAC,KAAK,EAAE,SAAS,OAAQ,QAAO;AACpC,UAAM,OAAO,MAAM,QAAQ,EAAE,IAAI,IAAI,EAAE,OAAO,CAAC;AAC/C,QAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,UAAM,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,EAAE,gBAAgB,CAAC,CAAC;AACtE,UAAM,MAAM,KAAK,GAAG;AACpB,UAAM,UAAU,MAAM,QAAQ,KAAK,OAAO,IAAI,IAAI,UAAU,CAAC;AAC7D,QAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,WAAO,QAAQ,IAAI;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiB,QAAQ;AACrB,UAAM,IAAI,KAAK,IAAI,MAAM;AACzB,QAAI,CAAC,KAAK,EAAE,SAAS,OAAQ,QAAO,CAAC;AACrC,UAAM,OAAO,MAAM,QAAQ,EAAE,IAAI,IAAI,EAAE,OAAO,CAAC;AAC/C,QAAI,KAAK,WAAW,EAAG,QAAO,CAAC;AAC/B,UAAM,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,EAAE,gBAAgB,CAAC,CAAC;AACtE,UAAM,MAAM,KAAK,GAAG;AACpB,WAAO,MAAM,QAAQ,KAAK,OAAO,IAAI,IAAI,UAAU,CAAC;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,QAAQ,SAAS,QAAQ,IAAI,OAAO,CAAC,GAAG;AAClD,UAAM,IAAI,KAAK,IAAI,MAAM;AACzB,QAAI,CAAC,KAAK,EAAE,SAAS,OAAQ,QAAO;AACpC,QAAI,SAAS,CAAC,EAAG,QAAO;AACxB,QAAI,CAAC,QAAS,QAAO;AACrB,UAAM,MAAM;AAAA,MACR,MAAO,QAAQ;AAAA,MACf,OAAO,QAAQ,SAAS,CAAC;AAAA,MACzB,OAAO,SAAS,QAAQ,QAAQ;AAAA;AAAA;AAAA,MAGhC,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,IAAI,CAAC;AAAA,IAChD;AACA,MAAE,OAAO,MAAM,QAAQ,EAAE,IAAI,IAAI,EAAE,OAAO,CAAC;AAC3C,MAAE,KAAK,KAAK,GAAG;AASf,QAAI,CAAC,KAAK,YAAY;AAClB,QAAE,eAAe,EAAE,KAAK,SAAS;AACjC,qBAAe,CAAC;AAAA,IACpB;AACA,WAAO,EAAE,KAAK,SAAS;AAAA,EAC3B;AAAA;AAAA,EAGA,iBAAiB,QAAQ,KAAK;AAC1B,UAAM,IAAI,KAAK,IAAI,MAAM;AACzB,QAAI,CAAC,KAAK,EAAE,SAAS,OAAQ,QAAO;AACpC,QAAI,CAAC,MAAM,QAAQ,EAAE,IAAI,KAAK,MAAM,KAAK,OAAO,EAAE,KAAK,OAAQ,QAAO;AACtE,MAAE,eAAe;AACjB,mBAAe,CAAC;AAChB,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAc,QAAQ,KAAK;AACvB,UAAM,IAAI,KAAK,IAAI,MAAM;AACzB,QAAI,CAAC,KAAK,EAAE,SAAS,OAAQ,QAAO;AACpC,QAAI,CAAC,MAAM,QAAQ,EAAE,IAAI,KAAK,MAAM,KAAK,OAAO,EAAE,KAAK,OAAQ,QAAO;AACtE,MAAE,KAAK,OAAO,KAAK,CAAC;AACpB,QAAI,EAAE,KAAK,WAAW,GAAG;AACrB,QAAE,eAAe;AACjB,qBAAe,CAAC;AAChB,aAAO;AAAA,IACX;AACA,QAAI,MAAM,EAAE,aAAc,GAAE,gBAAgB;AAAA,aACnC,QAAQ,EAAE,aAAc,GAAE,eAAe,KAAK,IAAI,GAAG,MAAM,CAAC;AACrE,mBAAe,CAAC;AAChB,WAAO,EAAE;AAAA,EACb;AAAA;AAAA;AAAA,EAIA,eAAe,QAAQ,SAAS;AAC5B,UAAM,IAAI,KAAK,IAAI,MAAM;AACzB,QAAI,CAAC,KAAK,EAAE,SAAS,OAAQ,QAAO;AACpC,UAAM,OAAO,EAAE,QAAQ,CAAC;AACxB,QAAI,UAAU,KAAK,WAAW,KAAK,OAAQ,QAAO;AAClD,MAAE,OAAO,CAAC,KAAK,OAAO,CAAC;AACvB,MAAE,eAAe;AACjB,mBAAe,CAAC;AAChB,WAAO;AAAA,EACX;AAAA;AAAA,EAGA,eAAe,QAAQ,KAAK;AACxB,UAAM,IAAI,KAAK,IAAI,MAAM;AACzB,QAAI,CAAC,KAAK,EAAE,SAAS,OAAQ,QAAO;AACpC,UAAM,OAAO,EAAE,QAAQ,CAAC;AACxB,QAAI,MAAM,KAAK,OAAO,KAAK,SAAS,EAAG,QAAO;AAC9C,MAAE,OAAO,KAAK,MAAM,GAAG,MAAM,CAAC;AAC9B,QAAI,EAAE,eAAe,IAAK,GAAE,eAAe;AAC3C,mBAAe,CAAC;AAChB,WAAO;AAAA,EACX;AAAA;AAAA,EAGA,gBAAgB,QAAQ,KAAK;AACzB,UAAM,IAAI,KAAK,IAAI,MAAM;AACzB,QAAI,CAAC,KAAK,EAAE,SAAS,OAAQ,QAAO;AACpC,UAAM,OAAO,EAAE,QAAQ,CAAC;AACxB,QAAI,OAAO,KAAK,OAAO,KAAK,OAAQ,QAAO;AAC3C,MAAE,OAAO,KAAK,MAAM,GAAG;AACvB,MAAE,eAAe,KAAK,IAAI,GAAG,EAAE,eAAe,GAAG;AACjD,mBAAe,CAAC;AAChB,WAAO;AAAA,EACX;AAAA;AAAA,EAGA,YAAY,QAAQ,MAAM,IAAI;AAC1B,UAAM,IAAI,KAAK,IAAI,MAAM;AACzB,QAAI,CAAC,KAAK,EAAE,SAAS,OAAQ,QAAO;AACpC,UAAM,OAAO,EAAE,QAAQ,CAAC;AACxB,QAAI,OAAO,KAAK,QAAQ,KAAK,UAAU,KAAK,KAAK,MAAM,KAAK,OAAQ,QAAO;AAC3E,UAAM,QAAQ,KAAK,OAAO,MAAM,CAAC,EAAE,CAAC;AACpC,SAAK,OAAO,IAAI,GAAG,KAAK;AAExB,QAAI,EAAE,iBAAiB,KAAM,GAAE,eAAe;AAAA,aACrC,OAAO,EAAE,gBAAgB,MAAM,EAAE,aAAc,GAAE,gBAAgB;AAAA,aACjE,OAAO,EAAE,gBAAgB,MAAM,EAAE,aAAc,GAAE,gBAAgB;AAC1E,mBAAe,CAAC;AAChB,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyCA,cAAc,YAAY,SAAS,UAAU,QAAQ,IAAI;AACrD,UAAM,OAAO,KAAK,IAAI,UAAU;AAChC,UAAM,KAAK,KAAK,IAAI,QAAQ;AAC5B,QAAI,CAAC,QAAQ,KAAK,SAAS,OAAQ,QAAO;AAC1C,QAAI,CAAC,MAAM,GAAG,SAAS,OAAQ,QAAO;AACtC,UAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC;AACrD,QAAI,CAAC,OAAO,UAAU,OAAO,KAAK,UAAU,KAAK,WAAW,KAAK,OAAQ,QAAO;AAIhF,QAAI,SAAS,EAAE,EAAG,QAAO;AACzB,QAAI,eAAe,UAAU;AACzB,YAAM,OAAQ,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,KAAK,SAAS,KAAK,SAC/D,KAAK,SAAS,IAAI;AACxB,UAAI,CAAC,KAAK,YAAY,YAAY,SAAS,IAAI,EAAG,QAAO;AACzD,aAAO,EAAE,IAAI,MAAM,OAAO,MAAM,SAAS,MAAM;AAAA,IACnD;AAEA,UAAM,CAAC,KAAK,IAAI,KAAK,KAAK,OAAO,SAAS,CAAC;AAM3C,QAAI,KAAK,KAAK,WAAW,EAAG,MAAK,eAAe;AAAA,aACvC,UAAU,KAAK,aAAc,MAAK,gBAAgB;AAAA,aAClD,YAAY,KAAK,aAAc,MAAK,eAAe,KAAK,IAAI,GAAG,UAAU,CAAC;AAEnF,OAAG,OAAO,MAAM,QAAQ,GAAG,IAAI,IAAI,GAAG,OAAO,CAAC;AAC9C,UAAM,KAAM,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,KAAK,QAAQ,GAAG,KAAK,SAC/D,GAAG,KAAK,SAAS;AACvB,OAAG,KAAK,OAAO,IAAI,GAAG,KAAK;AAK3B,OAAG,eAAe;AAElB,mBAAe,IAAI;AACnB,mBAAe,EAAE;AACjB,WAAO,EAAE,IAAI,MAAM,OAAO,IAAI,SAAS,KAAK,KAAK,WAAW,EAAE;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ,KAAqB;AAC/B,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,QAAI,CAAC,QAAQ,KAAK,SAAS,OAAQ,QAAO;AAE1C,UAAM,UAAU,SAAS;AAEzB,QAAI,CAAC,KAAK,UAAU;AAChB,YAAMC,QAAO,UAAU,EAAE,KAAK,UAAU,CAAC,KAAK,IAAI,QAAQ,EAAE,EAAE,CAAC;AAC/D,WAAK,UAAU,OAAO;AACtB,WAAK,UAAUA,KAAI;AACnB,WAAK,SAASA,MAAK;AACnB,WAAK,WAAWA,MAAK;AACrB,cAAQ,WAAWA,MAAK;AACxB,WAAK,MAAM,QAAQ,EAAE;AACrB,aAAO,QAAQ;AAAA,IACnB;AAEA,UAAM,SAAS,KAAK,IAAI,KAAK,QAAQ;AACrC,UAAM,MAAM,OAAO,SAAS,QAAQ,KAAK,EAAE;AAC3C,QAAI,MAAM,EAAG,QAAO;AAIpB,QAAI,OAAO,QAAQ,KAAK;AACpB,aAAO,SAAS,OAAO,MAAM,GAAG,GAAG,QAAQ,EAAE;AAC7C,YAAM,MAAM,OAAO,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,OAAO,SAAS;AACtE,aAAO,MAAM,OAAO,MAAM,GAAG,GAAG,GAAG;AACnC,cAAQ,WAAW,OAAO;AAC1B,WAAK,UAAU,OAAO;AACtB,WAAK,MAAM,QAAQ,EAAE;AACrB,aAAO,QAAQ;AAAA,IACnB;AAIA,UAAM,OAAO,UAAU,EAAE,KAAK,UAAU,CAAC,KAAK,IAAI,QAAQ,EAAE,EAAE,CAAC;AAC/D,SAAK,WAAW,OAAO;AACvB,WAAO,SAAS,GAAG,IAAI,KAAK;AAC5B,SAAK,WAAW,KAAK;AACrB,YAAQ,WAAW,KAAK;AACxB,SAAK,UAAU,OAAO;AACtB,SAAK,UAAU,IAAI;AACnB,SAAK,MAAM,QAAQ,EAAE;AACrB,WAAO,QAAQ;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ;AACV,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,QAAI,CAAC,QAAQ,KAAK,SAAS,OAAQ,QAAO;AAG1C,QAAI,CAAC,KAAK,UAAU;AAChB,WAAK,MAAM,MAAM;AACjB,WAAK,SAAS;AACd,WAAK,gBAAgB;AACrB,WAAK,aAAa,CAAC;AACnB,aAAO;AAAA,IACX;AAEA,UAAM,SAAS,KAAK,IAAI,KAAK,QAAQ;AACrC,UAAM,MAAM,OAAO,SAAS,QAAQ,KAAK,EAAE;AAC3C,WAAO,SAAS,OAAO,KAAK,CAAC;AAC7B,WAAO,MAAM,OAAO,KAAK,CAAC;AAC1B,SAAK,MAAM,OAAO,KAAK,EAAE;AACzB,SAAK,aAAa,KAAK,WAAW,OAAO,CAAC,OAAO,OAAO,KAAK,EAAE;AAI/D,SAAK,UAAU,MAAM;AAErB,QAAI,KAAK,kBAAkB,KAAK,IAAI;AAChC,YAAM,YAAY,KAAK,OAAO;AAC9B,WAAK,gBAAgB,UAAU,SAAS,UAAU,CAAC,EAAE,KAAK;AAC1D,UAAI,KAAK,cAAe,MAAK,MAAM,KAAK,aAAa;AAAA,IACzD;AACA,WAAO;AAAA,EACX;AAAA,EAEA,UAAU,MAAM;AACZ,QAAI,CAAC,QAAQ,KAAK,SAAS,QAAS;AACpC,QAAI,KAAK,SAAS,SAAS,EAAG;AAC9B,QAAI,KAAK,SAAS,WAAW,GAAG;AAE5B,UAAI,CAAC,KAAK,UAAU;AAChB,aAAK,MAAM,OAAO,KAAK,EAAE;AACzB,aAAK,SAAS;AACd;AAAA,MACJ;AACA,YAAM,KAAK,KAAK,IAAI,KAAK,QAAQ;AACjC,YAAM,MAAM,GAAG,SAAS,QAAQ,KAAK,EAAE;AACvC,SAAG,SAAS,OAAO,KAAK,CAAC;AACzB,SAAG,MAAM,OAAO,KAAK,CAAC;AACtB,WAAK,MAAM,OAAO,KAAK,EAAE;AACzB,WAAK,UAAU,EAAE;AACjB;AAAA,IACJ;AAEA,UAAM,OAAO,KAAK,IAAI,KAAK,SAAS,CAAC,CAAC;AACtC,QAAI,CAAC,KAAK,UAAU;AAChB,WAAK,SAAS,KAAK;AACnB,WAAK,WAAW;AAAA,IACpB,OAAO;AACH,YAAM,KAAK,KAAK,IAAI,KAAK,QAAQ;AACjC,YAAM,MAAM,GAAG,SAAS,QAAQ,KAAK,EAAE;AACvC,SAAG,SAAS,GAAG,IAAI,KAAK;AACxB,WAAK,WAAW,GAAG;AAAA,IACvB;AACA,SAAK,MAAM,OAAO,KAAK,EAAE;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,SAAS,KAA4C;AACjD,UAAM,MAAM,KAAK,QAAQ;AACzB,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,UAAW,QAAQ,UAAU,QAAQ,UAAW,MAAM;AAC5D,UAAM,UAAW,QAAQ,WAAW,QAAQ;AAE5C,QAAI,QAAQ;AACZ,QAAI,SAAS,MAAM,WAAW,KAAK,IAAI,MAAM,QAAQ,IAAI;AACzD,WAAO,QAAQ;AACX,UAAI,OAAO,QAAQ,SAAS;AACxB,cAAM,MAAM,OAAO,SAAS,QAAQ,MAAM,EAAE;AAC5C,cAAM,SAAS,UAAU,MAAM,IAAI,MAAM;AACzC,YAAI,UAAU,KAAK,SAAS,OAAO,SAAS,QAAQ;AAChD,gBAAM,SAAS,KAAK;AAAA,YAAa,OAAO,SAAS,MAAM;AAAA,YACnD;AAAA,YAAS,CAAC;AAAA,UAAO;AACrB,cAAI,QAAQ;AACR,iBAAK,MAAM,MAAM;AACjB,mBAAO;AAAA,UACX;AAAA,QACJ;AAAA,MACJ;AACA,cAAQ;AACR,eAAS,MAAM,WAAW,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,IACzD;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAa,QAAQ,UAAU,WAAW;AACtC,QAAI,IAAI,KAAK,IAAI,MAAM;AACvB,WAAO,KAAK,EAAE,SAAS,QAAQ;AAC3B,YAAM,MAAM,EAAE,QAAQ,WACf,YAAY,IAAI,EAAE,SAAS,SAAS,IACrC;AACN,UAAI,KAAK,IAAI,EAAE,SAAS,GAAG,CAAC;AAAA,IAChC;AACA,WAAO,IAAI,EAAE,KAAK;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,QAAQ,KAAK;AACT,UAAM,MAAM,KAAK,QAAQ;AACzB,QAAI,CAAC,OAAO,CAAC,IAAI,SAAU,QAAO;AAClC,UAAM,UAAW,QAAQ,UAAU,QAAQ,UAAW,MAAM;AAC5D,UAAM,UAAW,QAAQ,WAAW,QAAQ;AAE5C,QAAI,QAAQ;AACZ,QAAI,SAAS,KAAK,IAAI,MAAM,QAAQ;AACpC,WAAO,UAAU,OAAO,QAAQ,SAAS;AACrC,cAAQ;AACR,eAAS,MAAM,WAAW,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,IACzD;AACA,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,MAAM,OAAO,SAAS,QAAQ,MAAM,EAAE;AAC5C,UAAM,OAAO,UAAU,MAAM,IAAI,MAAM;AACvC,QAAI,OAAO,KAAK,QAAQ,OAAO,SAAS,OAAQ,QAAO;AACvD,KAAC,OAAO,SAAS,GAAG,GAAG,OAAO,SAAS,IAAI,CAAC,IACxC,CAAC,OAAO,SAAS,IAAI,GAAG,OAAO,SAAS,GAAG,CAAC;AAChD,KAAC,OAAO,MAAM,GAAG,GAAG,OAAO,MAAM,IAAI,CAAC,IAClC,CAAC,OAAO,MAAM,IAAI,GAAG,OAAO,MAAM,GAAG,CAAC;AAC1C,WAAO;AAAA,EACX;AAAA;AAAA,EAGA,cAAc,SAAS,OAAO;AAC1B,UAAM,IAAI,KAAK,IAAI,OAAO;AAC1B,QAAI,CAAC,KAAK,EAAE,SAAS,QAAS,QAAO;AACrC,QAAI,MAAM,WAAW,EAAE,SAAS,OAAQ,QAAO;AAC/C,MAAE,QAAQ,MAAM,MAAM;AACtB,WAAO;AAAA,EACX;AAAA;AAAA,EAGA,YAAY;AACR,QAAI,CAAC,KAAK,OAAQ,QAAO;AACzB,UAAM,OAAO,CAAC,OAAO;AACjB,YAAM,IAAI,KAAK,IAAI,EAAE;AACrB,UAAI,EAAE,SAAS,QAAQ;AAEnB,cAAM,YAAY,MAAM,QAAQ,EAAE,IAAI,IAAI,EAAE,OAAO,CAAC,GAC/C,OAAO,CAAC,MAAM,CAAC,EAAE,SAAS,EAC1B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,OAAO,OAAO,EAAE,MAAM,EAAE;AAClE,eAAO;AAAA,UACH,MAAM;AAAA,UACN,OAAO,EAAE;AAAA,UACT,SAAS,EAAE;AAAA,UACX,MAAM;AAAA,UACN,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,SAAS,GAAG,EAAE,gBAAgB,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA,UAI5E,UAAU,EAAE,WACN,OAAO;AAAA,YACL,OAAO,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM;AACvC,oBAAM,MAAM,EAAE,QAAQ,CAAC,GAClB,OAAO,CAAC,MAAM,CAAC,EAAE,SAAS,EAC1B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,OAAO,OAAO,EAAE,MAAM,EAAE;AAClE,qBAAO,CAAC,GAAG;AAAA,gBACP,MAAM;AAAA,gBACN,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,SAAS,GAAG,EAAE,gBAAgB,CAAC,CAAC;AAAA,cAC1E,CAAC;AAAA,YACL,CAAC;AAAA,UAAC,IACJ,CAAC;AAAA,QACX;AAAA,MACJ;AACA,aAAO;AAAA,QACH,MAAM;AAAA,QAAS,KAAK,EAAE;AAAA,QAAK,OAAO,EAAE,MAAM,MAAM;AAAA,QAChD,UAAU,EAAE,SAAS,IAAI,IAAI;AAAA,MACjC;AAAA,IACJ;AACA,WAAO;AAAA,MACH,MAAM,KAAK,KAAK,MAAM;AAAA,MACtB,cAAc,KAAK,QAAQ,GAAG,SAAS;AAAA,IAC3C;AAAA,EACJ;AAAA,EAEA,OAAO,YAAY,MAAM;AACrB,UAAM,IAAI,IAAI,UAAS;AACvB,QAAI,CAAC,QAAQ,CAAC,KAAK,KAAM,QAAO;AAChC,UAAM,QAAQ,CAAC,QAAQ;AACnB,UAAI,IAAI,SAAS,QAAQ;AAIrB,cAAM,OAAO,SAAS,EAAE,SAAS,IAAI,SAAS,OAAO,IAAI,MAAM,CAAC;AAChE,YAAI,MAAM,QAAQ,IAAI,IAAI,KAAK,IAAI,KAAK,SAAS,GAAG;AAChD,eAAK,OAAO,IAAI,KAAK,IAAI,CAAC,SAAS;AAAA,YAC/B,MAAO,IAAI;AAAA,YACX,OAAO,IAAI,SAAS,CAAC;AAAA,YACrB,OAAO,IAAI,SAAS,IAAI,QAAQ;AAAA,UACpC,EAAE;AACF,eAAK,eAAe,KAAK;AAAA,YAAI;AAAA,YACzB,KAAK,IAAI,KAAK,KAAK,SAAS,GAAG,IAAI,gBAAgB,CAAC;AAAA,UAAC;AACzD,yBAAe,IAAI;AAAA,QACvB;AACA,YAAI,IAAI,YAAY,OAAO,IAAI,aAAa,UAAU;AAClD,eAAK,WAAW,CAAC;AACjB,qBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI,QAAQ,GAAG;AAC/C,gBAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,EAAE,IAAI,EAAG;AAClC,iBAAK,SAAS,CAAC,IAAI;AAAA,cACf,MAAM,EAAE,KAAK,IAAI,CAACC,QAAO;AAAA,gBACrB,MAAOA,GAAE;AAAA,gBACT,OAAOA,GAAE,SAAS,CAAC;AAAA,gBACnB,OAAOA,GAAE,SAASA,GAAE,QAAQ;AAAA,cAChC,EAAE;AAAA,cACF,cAAc,EAAE,gBAAgB;AAAA,YACpC;AAAA,UACJ;AAAA,QACJ;AACA,eAAO;AAAA,MACX;AACA,YAAM,WAAW,IAAI,SAAS,IAAI,KAAK;AACvC,YAAM,QAAQ,UAAU;AAAA,QACpB,KAAK,IAAI;AAAA,QAAK,UAAU,SAAS,IAAI,CAAC,MAAM,EAAE,EAAE;AAAA,QAChD,OAAO,IAAI;AAAA,MACf,CAAC;AACD,eAAS,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AACtC,aAAO;AAAA,IACX;AACA,UAAM,OAAO,MAAM,KAAK,IAAI;AAC5B,MAAE,QAAQ,IAAI;AACd,WAAO;AAAA,EACX;AACJ;;;ACj8BA,IAAM,sBAAsB;AAAA,EACxB,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AACZ;AAWA,SAAS,aAAa,OAAO,MAAM,gBAAgB,qBAAqB;AACpE,QAAM,OAAO,IAAI,SAAS;AAC1B,OAAK,QAAQ,SAAS,KAAK,CAAC,CAAC;AAC7B,SAAO;AAAA,IACH,IAAI,QAAQ,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,IAClD;AAAA,IACA;AAAA,IACA,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA,IAIV,QAAQ,EAAE,GAAG,cAAc;AAAA,EAC/B;AACJ;AAEO,IAAM,iBAAN,MAAM,gBAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQxB,YAAY,EAAE,MAAM,gBAAgB,KAAK,IAAI,CAAC,GAAG;AAC7C,QAAI,OAAO,SAAS,YAAY;AAC5B,YAAM,IAAI,MAAM,wEAAwE;AAAA,IAC5F;AACA,SAAK,OAAO;AACZ,SAAK,gBAAgB,EAAE,GAAG,qBAAqB,GAAI,iBAAiB,CAAC,EAAG;AACxE,SAAK,WAAW,CAAC,aAAa,KAAK,MAAM,KAAK,aAAa,CAAC;AAC5D,SAAK,YAAY;AAAA,EACrB;AAAA,EAEA,SAAS;AAAE,WAAO,KAAK,SAAS,KAAK,SAAS;AAAA,EAAG;AAAA,EAEjD,SAAS,KAAK;AACV,QAAI,MAAM,KAAK,OAAO,KAAK,SAAS,OAAQ,QAAO;AACnD,QAAI,QAAQ,KAAK,UAAW,QAAO;AACnC,SAAK,YAAY;AACjB,WAAO;AAAA,EACX;AAAA,EAEA,YAAY,GAAG;AACX,WAAO,KAAK,SAAS,SAAS,GAAG;AAC7B,WAAK,SAAS,KAAK;AAAA,QACf,OAAO,KAAK,SAAS,SAAS,CAAC;AAAA,QAAG,KAAK;AAAA,QAAM,KAAK;AAAA,MAAa,CAAC;AAAA,IACxE;AAAA,EACJ;AAAA,EAEA,WAAW,QAAQ,MAAM;AACrB,UAAM,IAAI;AAAA,MACN,SAAS,OAAO,KAAK,SAAS,SAAS,CAAC;AAAA,MAAG,KAAK;AAAA,MAAM,KAAK;AAAA,IAAa;AAC5E,SAAK,SAAS,KAAK,CAAC;AACpB,WAAO;AAAA,EACX;AAAA,EAEA,YAAY;AACR,WAAO;AAAA,MACH,WAAW,KAAK;AAAA,MAChB,UAAU,KAAK,SAAS,IAAI,CAAC,OAAO;AAAA,QAChC,IAAI,EAAE;AAAA,QAAI,OAAO,EAAE;AAAA,QACnB,MAAM,EAAE,KAAK,UAAU;AAAA,QACvB,QAAQ,EAAE,GAAG,EAAE,OAAO;AAAA;AAAA;AAAA,MAG1B,EAAE;AAAA,IACN;AAAA,EACJ;AAAA,EAEA,OAAO,YAAY,MAAM,EAAE,MAAM,gBAAgB,KAAK,IAAI,CAAC,GAAG;AAC1D,UAAM,IAAI,IAAI,gBAAe,EAAE,MAAM,cAAc,CAAC;AACpD,QAAI,CAAC,QAAQ,CAAC,MAAM,QAAQ,KAAK,QAAQ,KAAK,KAAK,SAAS,WAAW,EAAG,QAAO;AACjF,MAAE,WAAW,KAAK,SAAS,IAAI,CAAC,SAAS;AAAA,MACrC,IAAI,IAAI,MAAM,QAAQ,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAE,CAAC,CAAC;AAAA,MAC3D,OAAO,IAAI,SAAS;AAAA,MACpB,MAAM,IAAI,OAAO,SAAS,YAAY,IAAI,IAAI,IAAI,IAAI,SAAS;AAAA,MAC/D,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,MAKV,QAAQ,EAAE,GAAG,EAAE,eAAe,GAAI,IAAI,UAAU,CAAC,EAAG;AAAA,IACxD,EAAE;AAEF,eAAW,KAAK,EAAE,UAAU;AACxB,UAAI,CAAC,EAAE,KAAK,OAAQ,GAAE,KAAK,QAAQ,SAAS,KAAK,CAAC,CAAC;AAAA,IACvD;AACA,MAAE,YAAY,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,YAAY,CAAC,GAAG,EAAE,SAAS,SAAS,CAAC;AAC7E,WAAO;AAAA,EACX;AACJ;AAQA,IAAM,eAAe;AAErB,eAAsB,aAAa,OAAO;AACtC,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACA,WAAO,MAAM,MAAM,KAAK,YAAY;AAAA,EACxC,SAAS,KAAK;AACV,YAAQ,KAAK,0BAA0B,GAAG;AAC1C,WAAO;AAAA,EACX;AACJ;AAEA,eAAsB,aAAa,OAAO,MAAM;AAC5C,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACA,UAAM,MAAM,MAAM,cAAc,IAAI;AACpC,WAAO;AAAA,EACX,SAAS,KAAK;AACV,YAAQ,KAAK,0BAA0B,GAAG;AAC1C,WAAO;AAAA,EACX;AACJ;;;ACnHO,SAAS,oBAAoB,EAAE,SAAS,UAAU,CAAC,GAAG,YAAY,KAAK,IAAI,CAAC,GAAG;AAClF,MAAI,CAAC,MAAM,QAAQ,OAAO,GAAG;AACzB,UAAM,IAAI,UAAU,iDAAiD;AAAA,EACzE;AACA,aAAW,KAAK,SAAS;AACrB,QAAI,CAAC,GAAG,WAAW,OAAO,EAAE,SAAS,cAAc,OAAO,EAAE,UAAU,YAAY;AAC9E,YAAM,IAAI;AAAA,QACN,gCAAgC,GAAG,WAAW,GAAG;AAAA,MAAoC;AAAA,IAC7F;AAAA,EACJ;AACA,MAAI,aAAa,QAAQ,OAAO,cAAc,YAAY;AACtD,UAAM,IAAI,UAAU,kEAAkE;AAAA,EAC1F;AAEA,QAAM,WAAW,OAAO,OAAO,QAAQ,IAAI,CAAC,MAAM,OAAO,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;AAC1E,QAAM,UAAW,IAAI,IAAI,SAAS,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;AAC5D,QAAM,WAAW,OAAO,OAAO,EAAE,GAAG,QAAQ,CAAC;AAE7C,QAAM,MAAM,CAAC,YAAY,QAAQ,IAAI,OAAO,KAAK;AAGjD,QAAM,WAAW,CAAC,KAAK,QAAQ;AAC3B,UAAM,OAAO,IAAI,QACX,IAAI,MAAM,GAAG,IACZ,MAAM,QAAQ,GAAG,IAAI,MACpB,MAAM,QAAQ,KAAK,KAAK,IAAI,IAAI,QAChC,CAAC;AACT,WAAO,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;AAAA,EACzC;AAMA,QAAM,YAAY,CAAC,SAAS,SAAS;AACjC,UAAM,MAAM,QAAQ,IAAI,OAAO;AAC/B,QAAI,CAAC,OAAO,CAAC,MAAM,QAAQ,IAAI,EAAG,QAAO,CAAC;AAC1C,UAAM,MAAM,CAAC;AACb,eAAW,OAAO,MAAM;AACpB,UAAI;AACJ,UAAI;AAAE,iBAAS,IAAI,MAAM,GAAG;AAAA,MAAG,SACxB,KAAK;AACR,gBAAQ,KAAK,oBAAoB,SAAS,gBAAgB,KAAK,GAAG;AAClE;AAAA,MACJ;AACA,UAAI,CAAC,UAAU,CAAC,OAAO,GAAI;AAC3B,UAAI,KAAK,MAAM;AAAA,IACnB;AACA,WAAO;AAAA,EACX;AAIA,QAAM,UAAU,OAAO,SAAS,QAAQ;AACpC,UAAM,MAAM,QAAQ,IAAI,OAAO;AAC/B,QAAI,CAAC,OAAO,CAAC,IAAK,QAAO,CAAC;AAC1B,QAAI;AAAE,aAAO,SAAS,KAAK,MAAM,IAAI,KAAK,GAAG,CAAC;AAAA,IAAG,SAC1C,KAAK;AACR,cAAQ,KAAK,oBAAoB,SAAS,gBAAgB,GAAG;AAC7D,aAAO,CAAC;AAAA,IACZ;AAAA,EACJ;AAUA,QAAM,cAAc,OAAO,QAAQ;AAC/B,UAAM,MAAM,CAAC;AACb,eAAW,KAAK,SAAU,KAAI,EAAE,OAAO,IAAI,CAAC;AAC5C,QAAI,CAAC,IAAK,QAAO;AAEjB,QAAI,OAAO;AACX,QAAI,WAAW;AACX,UAAI;AAAE,eAAO,MAAM,UAAU,GAAG;AAAA,MAAG,SAC5B,KAAK;AACR,gBAAQ,KAAK,8DAA8D,GAAG;AAAA,MAClF;AACA,UAAI,QAAQ,OAAO,SAAS,SAAU,QAAO;AAAA,IACjD;AAEA,UAAM,UAAU,CAAC;AACjB,eAAW,OAAO,UAAU;AACxB,YAAM,MAAM,OAAO,KAAK,IAAI,OAAO,IAAI;AACvC,UAAI,OAAO,MAAM;AAAE,gBAAQ,KAAK,GAAG;AAAG;AAAA,MAAU;AAChD,UAAI,IAAI,OAAO,IAAI,SAAS,KAAK,GAAG;AAAA,IACxC;AACA,QAAI,QAAQ,WAAW,EAAG,QAAO;AAEjC,UAAM,QAAQ,IAAI,QAAQ,IAAI,OAAO,QAAQ;AACzC,UAAI,IAAI,OAAO,IAAI,MAAM,QAAQ,IAAI,SAAS,GAAG;AAAA,IACrD,CAAC,CAAC;AACF,WAAO;AAAA,EACX;AAKA,QAAM,UAAU,OAAO,QAAQ;AAC3B,UAAM,UAAU,MAAM,YAAY,GAAG;AACrC,UAAM,MAAM,CAAC;AACb,eAAW,OAAO,UAAU;AACxB,iBAAW,UAAU,UAAU,IAAI,SAAS,QAAQ,IAAI,OAAO,KAAK,CAAC,CAAC,GAAG;AACrE,YAAI,KAAK,EAAE,MAAM,IAAI,SAAS,GAAG,OAAO,CAAC;AAAA,MAC7C;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AAMA,QAAM,oBAAoB,CAAC,WAAW;AAClC,UAAM,IAAI,OAAO,UAAU,EAAE,EAAE,YAAY,EAAE,KAAK;AAClD,QAAI,CAAC,EAAG,QAAO;AACf,QAAI,SAAS,CAAC,EAAG,QAAO,SAAS,CAAC;AAClC,eAAW,OAAO,UAAU;AACxB,UAAI,IAAI,YAAY,EAAG,QAAO,CAAC,IAAI,OAAO;AAC1C,UAAI,IAAI,QAAQ,QAAQ,MAAM,GAAG,MAAM,EAAG,QAAO,CAAC,IAAI,OAAO;AAAA,IACjE;AACA,WAAO;AAAA,EACX;AAEA,SAAO,OAAO,OAAO;AAAA,IACjB,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ,CAAC;AACL;;;ACxIO,SAAS,cAAc,EAAE,IAAI,SAAS,GAAG,KAAK,IAAI,CAAC,GAAG;AACzD,QAAM,YAAY,CAAC,MAAM;AACrB,UAAM,UAAU,EAAE,QAAQ;AAAA,MACtB;AAAA,IAAmD;AAEvD,SAAK,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,YAAY,MAAM,OAAO,CAAC,EAAE,UAAU;AACxE,QAAE,eAAe;AACjB,cAAQ,OAAO;AACf;AAAA,IACJ;AAIA,SAAK,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,UAAU,EAAE,QAAQ,OAAO;AAC1D,QAAE,eAAe;AACjB,SAAG,gBAAgB,EAAE,WAAW,KAAK,CAAC;AACtC;AAAA,IACJ;AACA,QAAI,EAAE,QAAQ,UAAU;AACpB,UAAI,QAAQ,OAAO,GAAG;AAAE,gBAAQ,MAAM;AAAG;AAAA,MAAQ;AAAA,IAErD;AAIA,QAAI,EAAE,QAAQ,eAAe,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,EAAE,SAAS;AAC/D,QAAE,eAAe;AACjB,SAAG,eAAe;AAClB;AAAA,IACJ;AAKA,UAAM,SAAS,oBAAoB,KAAK,EAAE,GAAG;AAC7C,QAAI,UAAU,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,EAAE,WAAW,CAAC,EAAE,WAAW,CAAC,EAAE,UAAU;AAO5E,YAAM,OAAO,SAAS;AAAA,QAClB,KAAK,eACF;AAAA,MAAmE;AAC1E,YAAM,MAAM,OAAO,OAAO,CAAC,CAAC,IAAI;AAChC,UAAI,MAAM,KAAK,QAAQ;AACnB,UAAE,eAAe;AACjB,aAAK,GAAG,EAAE,MAAM;AAChB;AAAA,MACJ;AAAA,IACJ;AAKA,QAAI,EAAE,YAAY,CAAC,EAAE,UAAU,CAAC,EAAE,WAAW,CAAC,EAAE,WAAW,CAAC,YACpD,EAAE,QAAQ,eAAe,EAAE,QAAQ,eAAe;AACtD,YAAM,OAAO,CAAC,GAAG,SAAS,iBAAiB,4BAA4B,CAAC;AACxE,UAAI,KAAK,QAAQ;AACb,cAAM,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,UAAU,SAAS,iBAAiB,CAAC;AACzE,cAAM,MAAM,EAAE,QAAQ,eAAe,IAAI;AACzC,cAAM,SAAS,MAAM,IAAI,IAAI,OAAO,MAAM,KAAK,UAAU,KAAK;AAC9D,UAAE,eAAe;AACjB,aAAK,IAAI,EAAE,MAAM;AACjB;AAAA,MACJ;AAAA,IACJ;AAGA,QAAI,EAAE,WAAW,CAAC,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,EAAE,WAAW,CAAC,YACpD,EAAE,QAAQ,eAAe,EAAE,QAAQ,eAAe;AACtD,QAAE,eAAe;AACjB,SAAG,aAAa,EAAE,QAAQ,eAAe,IAAI,EAAE;AAC/C;AAAA,IACJ;AAEA,QAAI,CAAC,EAAE,OAAQ;AAEf,UAAM,MAAM,EAAE,IAAI,YAAY;AAG9B,QAAI,UAAU,KAAK,EAAE,GAAG,GAAG;AACvB,QAAE,eAAe;AACjB,YAAM,MAAM,OAAO,EAAE,GAAG,IAAI;AAC5B,UAAI,EAAE,SAAU,IAAG,qBAAqB,GAAG;AAAA,UACtC,IAAG,cAAc,GAAG;AACzB;AAAA,IACJ;AAEA,YAAQ,KAAK;AAAA;AAAA;AAAA,MAGT,KAAK;AACD,UAAE,eAAe;AACjB,UAAE,WAAW,GAAG,iBAAiB,GAAG,IAAI,GAAG,MAAM,GAAG;AACpD;AAAA,MACJ,KAAK;AACD,UAAE,eAAe;AACjB,UAAE,WAAW,GAAG,iBAAiB,GAAG,IAAI,GAAG,MAAM,GAAG;AACpD;AAAA,MACJ,KAAK;AAAK,UAAE,eAAe;AAAG,WAAG,iBAAiB;AAAG;AAAA,MACrD,KAAK;AAAK,UAAE,eAAe;AAAG,WAAG,oBAAoB;AAAG;AAAA,MACxD,KAAK;AAAK,UAAE,eAAe;AAAG,WAAG,aAAa;AAAG;AAAA,MACjD,KAAK;AAAK,UAAE,eAAe;AAAG,WAAG,qBAAqB;AAAG;AAAA,MACzD,KAAK;AACD,UAAE,eAAe;AACjB,UAAE,WAAW,GAAG,YAAY,MAAM,IAAI,GAAG,SAAS,MAAM;AACxD;AAAA,MACJ,KAAK;AACD,UAAE,eAAe;AACjB,UAAE,WAAW,GAAG,YAAY,OAAO,IAAI,GAAG,SAAS,OAAO;AAC1D;AAAA,MACJ,KAAK;AACD,UAAE,eAAe;AACjB,UAAE,WAAW,GAAG,YAAY,IAAI,IAAI,GAAG,SAAS,IAAI;AACpD;AAAA,MACJ,KAAK;AACD,UAAE,eAAe;AACjB,UAAE,WAAW,GAAG,YAAY,MAAM,IAAI,GAAG,SAAS,MAAM;AACxD;AAAA,IACR;AAKA,QAAI,IAAI,WAAW,KAAK,QAAQ,KAAK,GAAG,KAAK,CAAC,SAAS;AACnD,QAAE,eAAe;AAAA,IACrB;AAAA,EACJ;AAEA,WAAS,iBAAiB,WAAW,SAAS;AAC9C,SAAO,MAAM,SAAS,oBAAoB,WAAW,SAAS;AAClE;;;AC5HO,SAAS,eAAe,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG;AACjD,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACrC,UAAM,IAAI,UAAU,uDAAuD;AAAA,EAC/E;AACA,MAAI,CAAC,QAAQ,CAAC,MAAM,IAAI,GAAG;AACvB,UAAM,IAAI,MAAM,8BAA8B,IAAI,0BAA0B;AAAA,EAChF;AACA,aAAW,CAAC,MAAM,GAAG,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC7C,QAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACjC,YAAM,IAAI,UAAU,oBAAoB,IAAI,oBAAoB;AAAA,IACpE;AACA,QAAI,IAAI,UAAU,CAAC,MAAM,IAAI,MAAM,GAAG;AAClC,YAAM,IAAI;AAAA,QACN,oBAAoB,IAAI,+BAA+B,IAAI,MAAM;AAAA,MAAG;AAAA,IAC5E;AACA,QAAI,IAAI,YAAY,IAAI,QAAQ;AAC5B,YAAM,IAAI;AAAA,QACN,oBAAoB,IAAI;AAAA,MAA2C;AAAA,IAC3E;AAAA,EACJ;AACA,QAAM,SAAS,OAAO,OAAO,EAAE,GAAG,MAAM,CAAC;AAIzC,QAAM,OAAO,CAAC,SAAS,OAAO,IAAI;AAKlC,QAAM,YAAY,CAAC,SAAS;AACxB,UAAM,IAAI,OAAO,IAAI;AACrB,QAAI,CAAC,EAAG,QAAO;AACf,WAAO,EAAE,WAAW,OAAO,EAAE;AAAA,EACjC;AAKA,QAAM,gBAAgB,CAAC,SAAS;AAC5B,QAAI,SAAS,KAAM,QAAO;AAC1B,UAAM,IAAI,OAAO,IAAI;AACrB,QAAI,CAAC,EAAG,QAAO;AACf,QAAI,EAAE,SAAU,QAAO;AACvB,WAAO,EAAE,UAAU;AAAA,EACvB;AAOA,QAAM,YAAY,CAAC,MAAM,UAAU;AAC/B,UAAM,KAAK,OAAO,IAAI,GAAG;AACzB,QAAI,OAAO,OAAO,WAAY,QAAO,CAAC;AACtC,QAAI;AACJ,QAAI;AAAE,YAAM,GAAG,SAAS,CAAC,CAAC;AAAA,IAAG,SACtB,KAAK;AACR,cAAQ,KAAK,oCAAoC,MAAM,GAAG;AAC1D,aAAO,CAAC;AAAA,IACZ;AACA,QAAI,CAAC,MAAM,QAAQ,GAAG,EAAG,QAAO,CAAC;AACjC,WAAO,IACF,OAAO,CAAC,MAAM,KAAK,EAAE,IAAI,EACzB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,IAAI,EAAE,IAAI,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE;AAAA,EACnF;AAGA,QAAM,WAAW,CAAC,MAAM,UAAU;AAC9B,UAAM,QAAQ,UAAU,MAAM,KAAK;AACnC,WAAO,MAAM,SAAS,MAAM,MAAM,SAAS,CAAC,IAAI;AAAA,EACpD;AAGA,QAAM,UAAU,CAAC,MAAM,UAAU;AAC7B,UAAM,KAAK,OAAO,IAAI,GAAG;AACzB,QAAI,OAAO,OAAO,YAAY;AAC1B,UAAI;AACA,cAAM,IAAI,GAAG,SAAS,CAAC,CAAC;AACxB,YAAI,EAAG,QAAO,OAAO,CAAC;AAAA,MAC1B,SAAS,KAAK;AACV,gBAAQ,KAAK,kCAAkC,MAAM,GAAG;AAAA,MAC5D;AAAA,IACJ;AACA,QAAI,OAAO,MAAO,QAAO,OAAO,MAAM,KAAK;AAC3C,WAAO,OAAO,MAAM,OAAO,OAAO,MAAM,EAAE,IAAI;AAAA,EAClD;AAIA,QAAM,aAAa,CAAC,SAAS;AACzB,UAAM,IAAI,OAAO,IAAI,GAAG,WAAW,OAAO,IAAI,GAAG;AACjD,WAAO,MAAM,QAAQ,CAAC,IAAI,IAAI,CAAC;AAAA,EACnC;AAKA,QAAM,gBAAgB,MAClB,OAAO,QAAQ,MAAM,EAChB,OAAO,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,EAC5B,KAAK,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,SAAS,QAAS,EAAE,CAAC,EAAE,SAAS,IAAK,EAC1D,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO;AAAA,IACjB;AAAA,IACA,OAAW,EAAE,cAAc,EAAE;AAAA,IAC7B,WAAW,EAAE;AAAA,IACb,MAAW,EAAE;AAAA,EACjB,EAAE;AAEV,SAAO,OAAO,OAAO;AAAA,IACjB;AAAA,IACA,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ,CAAC;AACL;;;ACzHA,IAAI,UAAU;AAGP,SAAS,sBAAsB,IAAI;AACtC,MAAI,QAAS,SAAQ,QAAQ;AAC7B,YAAU,IAAI,eAAe,EAAE;AAC/B,SAAO;AACX;AAGO,SAAS,oBAAoB;AAAE,SAAO;AAAS;AAG/C,SAAS,0BAA0B;AACtC,WAAS,QAAQ;AACjB,YAAU;AACd;AAQO,SAAS,kBAAkB,QAAQ,SAAS;AAC/C,MAAI,CAAC,UAAU,OAAO,YAAY,WAAY,QAAO,MAAM;AAAA,EAAC;AAC5D,MAAI,QAAS,QAAO,QAAQ,SAAS,QAAQ,OAAO;AACpD,SAAO,gBAAgB,QAAQ,OAAO;AAC1C;AAEO,IAAM,iBAAN,MAAqB;AAAA,EACxB,YAAY,IAAI;AACZ,SAAK,MAAM;AACX,SAAK,WAAW,CAAC;AACjB,SAAK,SAAS,KAAK,UAAU,KAAK,IAAI;AACtC,aAAS,iBAAiB,WAAW,KAAK,MAAM;AAAA,EACpD;AAAA,EAEA,SAAS,QAAQ,SAAS;AACtB,UAAM,QAAQ,EAAE,QAAQ,QAAQ;AAChC,SAAK,SAAS,KAAK,KAAK;AACxB,WAAO,MAAM;AACT,YAAM,IAAI,KAAK,SAAS,QAAQ,KAAK;AACrC,UAAI,KAAK,EAAG,MAAK,SAAS,OAAO,GAAG,CAAC;AAAA,IACzC;AAAA,EACJ;AAAA,EAEA,UAAU;AACN,aAAS,oBAAoB,WAAW,KAAK,MAAM;AACnD,SAAK,SAAS,SAAS;AAAA,EAC3B;AAAA;AAAA,EAGA,gBAAgB,QAAQ;AACpB,QAAI,CAAC,QAAQ,YAAa,QAAO;AACjC,UAAM,OAAO,OAAO,UAAU,WAAW;AACzC,QAAI,MAAM;AAIN,aAAO,SAAS,KAAK,KAAK,gBAAgB;AAAA,IAC9C;AAGA,WAAO,OAAO,SAAS,SAAS,aAAa,KACtC,SAAS,kBAAkB,SAAS;AAAA,EAC/C;AAAA,EAEA,UAAU,IAAI;AAEV,eAAW,SAAS,CAAC,GAAG,KAAK,QAAQ,GAAG;AACpC,UAAI,CAAC,KAAK,gBAAgB,MAAM,MAAM,EAAG;AACzC,UAAI;AAAE,cAAM,QAAQ,EAAE;AAAA,MAAG,SAClB,KAAK;AAAE,gBAAQ,KAAK,8BAA8B,GAAG;AAAA,MAAG;AAI/D,UAAI,GAAG,iBAAkB;AAAA,IAC7B;AAAA,EACJ;AACJ;AAKA,SAAS,gBAAgB,QAAQ,SAAS;AACtC,QAAM,UAAU,CAAC,OAAO;AACpB,QAAI,CAAC,OAAO,YAAa;AACzB,QAAI,CAAC,OAAO,SAAS,SAAS,aAAa,KACpC,SAAS,kBAAkB,SAAS,KAAM;AACjD,YAAQ,EAAE;AAAA,EACd;AACA,WAAS,iBAAiB,WAAW,OAAO;AAC5C,SAAO,MAAM,SAAS,oBAAoB,WAAW,OAAO;AAChE;;;AC5GO,SAAS,2BAA2B,MAAM,QAAQ,eAAe,UAAU,CAAC,GAAG;AAClF,MAAI,CAAC,KAAM,QAAO,MAAM;AAAA,EAAC;AAiBzB,QAAM,WAAW,CAAC,QAAQ,QAAQ;AAC9B,QAAI,CAAC,IAAK;AACV,UAAM,KAAK,WAAW,aAAgB,cAAc,YACzC,WAAW,gBAAgB,cAAc,eACd,cAAc,MAAM;AAC1D,QAAI;AAAE,WAAK,GAAG;AAAA,IAAG,SACV,GAAG;AAAE,cAAQ,KAAK,qBAAqB,MAAM,YAAY,CAAC;AAAA,IAAG;AAAA,EACxE;AAKA,QAAM,aAAa,CAAC,OAAO;AACvB,UAAM,KAAK,GAAG,OAAO,QAAQ,IAAI;AACjC,QAAI,CAAC,MAAM,CAAC,GAAG,QAAQ,OAAO,EAAG,QAAO;AACxC,UAAM,OAAO,GAAG,QAAQ,yBAAyB;AACjD,QAAI,CAAC,QAAQ,CAAC,KAAK,SAAS,IAAI,EAAG,QAAO;AAC1C,WAAO;AAAA,EACX;AAEA,QAAM,UAAU,CAAC,OAAO;AAIpB,UAAM,YAAY,GAAG,OAAO,QAAQ,mBAAmB;AACvD,QAAI,aAAa,KAAK,SAAS,SAAS,GAAG;AACvC,SAAG,eAAe;AAClB,SAAG,gBAAgB;AACnB,SAAG,yBAAyB;AAC5B,YAAMC,MAAK,UAAU,QAAQ,IAAI;AACjC,eAAS,UAAU,QAAQ,WAAWA,MAAK,OAAOA,IAAG,UAAU,IAAI,IAAI;AACvE;AAAA,IACJ;AAIA,QAAI,GAAG,OAAO,QAAQ,oCAAoC,EAAG;AAC7D,UAAM,KAAK,WAAW,EAAE;AACxB,QAAI,GAAI,UAAS,QAAQ,OAAO,GAAG,UAAU,CAAC;AAAA,EAClD;AAEA,QAAM,cAAc,CAAC,QAAQ;AACzB,UAAM,QAAQ,CAAC;AACf,QAAI,cAAc,MAAM;AACpB,YAAM,KAAK,EAAE,OAAO,QAAiB,MAAM,eAAe,QAAQ,OAAO,CAAC;AAC1E,UAAI,cAAc,WAAW;AACzB,cAAM,KAAK,EAAE,OAAO,mBAAmB,MAAM,OAAQ,QAAQ,WAAW,CAAC;AAAA,MAC7E;AACA,UAAI,cAAc,cAAc;AAC5B,cAAM,KAAK;AAAA,UAAE,OAAO;AAAA,UAAsB,MAAM;AAAA,UACnC,QAAQ;AAAA,QAAc,CAAC;AAAA,MACxC;AAAA,IACJ;AACA,QAAI,cAAc,KAAQ,OAAM,KAAK,EAAE,OAAO,iBAAiB,MAAM,QAAa,QAAQ,OAAS,CAAC;AACpG,QAAI,cAAc,QAAQ;AACtB,UAAI,MAAM,OAAQ,OAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAChD,YAAM,KAAK,EAAE,OAAO,UAAU,MAAM,UAAU,QAAQ,UAAU,QAAQ,KAAK,CAAC;AAAA,IAClF;AACA,WAAO;AAAA,EACX;AACA,QAAM,YAAY,OAAO,QAAQ,cAAc,aACzC,QAAQ,YACR;AAEN,QAAM,gBAAgB,CAAC,OAAO;AAC1B,UAAM,KAAK,WAAW,EAAE;AACxB,QAAI,CAAC,GAAI;AACT,OAAG,eAAe;AAKlB,OAAG,gBAAgB;AACnB,UAAM,MAAM,OAAO,GAAG,UAAU;AAChC,QAAI,CAAC,IAAK;AACV,UAAM,QAAQ,UAAU,GAAG,KAAK,CAAC;AACjC,QAAI,MAAM,WAAW,EAAG;AACxB,oBAAgB,GAAG,SAAS,GAAG,SAAS,OAAO,CAAC,WAAW,SAAS,QAAQ,GAAG,CAAC;AAAA,EACpF;AAIA,OAAK,iBAAiB,SAAS,OAAO;AACtC,OAAK,iBAAiB,eAAe,eAAe,IAAI;AACxD,SAAO,MAAM;AACT,SAAK,oBAAoB,SAAS,OAAO;AACzC,SAAK,oBAAoB,eAAe,eAAe,IAAI;AAAA,EAC/D;AACJ;AAWO,SAAS,qBAAqB,QAAQ;AACzC,QAAM,QAAQ,MAAM,KAAK,OAAO,iBAAiB,oBAAoB,CAAC;AACtE,MAAI,MAAM,WAAW,EAAG,QAAO,EAAE,OAAO,MAAM;AAAA,EAAC,GAAG,MAAM;AACxD,MAAI,UAAU,MAAM,CAAC;AACrB,QAAM,UAAU,MAAM;AAClB,eAAW,KAAK,OAAO;AACnB,QAAE,UAAU,OAAO,8BAA8B,MAAM,OAAO;AAAA,IAClE;AAAA,EACJ;AACA,UAAQ;AACR,aAAW,KAAK,OAAO;AACnB,MAAE,iBAAiB,aAAa,MAAM;AAClC,UAAI,YAAY,EAAG;AACnB,gBAAU;AACV,cAAQ;AAAA,IACZ,CAAC;AAAA,EACL;AACA,SAAO;AAAA,IACH,OAAO,CAAC,SAAS;AAAE,UAAI,QAAQ,MAAM,SAAS,IAAI,GAAG;AAAE,kBAAU;AAAM,gBAAQ;AAAA,MAAG;AAAA,IAAE;AAAA,IACpF;AAAA,EACJ;AACJ;AAiBO,SAAS,yBAAyB,MAAM,SAAS,YAAY,UAAU,CAAC,GAAG;AAC9E,MAAI,CAAC,KAAM,QAAO,EAAE,UAAU,MAAM;AAAA,EAAC,GAAG,SAAS,MAAM;AAAA,EAAC,GAAG,YAAY,MAAM;AAAA,EAAC,EAAE;AAEhF,MAAI,SAAS;AACb,QAAM,QAAQ,MAAM,KAAK,cAAc,OAAO;AAE9C,QAAM,gBAAgB,MAAM;AACxB,UAAM,KAAK,MAAM;AACjB,QAAI,CAAC,GAAI;AACT,UAAM,OAAO,QAAQ,KAAK,CAAC;AAC3B,QAAI,KAAK,WAAW,GAAG;AAAE,eAAS;AAAG;AAAA,IAAQ;AAC7C,QAAI,UAAU,KAAK,OAAQ,UAAS,KAAK,SAAS;AAClD,QAAI,SAAS,EAAG,UAAS;AACzB,QAAI,SAAS;AACb,OAAG,iBAAiB,IAAI,EAAE,QAAQ,CAAC,OAAO;AACtC,YAAM,KAAK,GAAG,eAAe;AAC7B,SAAG,UAAU,OAAO,kBAAkB,EAAE;AACxC,UAAI,GAAI,UAAS;AAAA,IACrB,CAAC;AACD,QAAI,OAAQ,QAAO,eAAe,EAAE,OAAO,UAAU,CAAC;AAAA,EAC1D;AAEA,QAAM,OAAO,CAAC,UAAU;AACpB,UAAM,OAAO,QAAQ,KAAK,CAAC;AAC3B,QAAI,KAAK,WAAW,EAAG;AACvB,aAAS,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,SAAS,KAAK,CAAC;AAC9D,kBAAc;AAAA,EAClB;AAEA,QAAM,OAAO,MAAM;AACf,UAAM,OAAO,QAAQ,KAAK,CAAC;AAC3B,UAAM,MAAM,KAAK,MAAM;AACvB,QAAI,IAAK,YAAW,GAAG;AAAA,EAC3B;AAEA,QAAM,QAAQ,CAAC,OAAO;AAClB,QAAI,CAAC,KAAK,YAAa;AAEvB,UAAM,IAAI,GAAG;AACb,UAAM,WAAW,MACb,EAAE,YAAY,WAAW,EAAE,YAAY,cACvC,EAAE,YAAY,YAAY,EAAE;AAChC,QAAI,SAAU;AAGd,QAAI,GAAG,QAAQ,aAAkB;AAAE,SAAG,eAAe;AAAG,WAAK,CAAE;AAAA,IAAG,WACzD,GAAG,QAAQ,WAAa;AAAE,SAAG,eAAe;AAAG,WAAK,EAAE;AAAA,IAAG,WACzD,GAAG,QAAQ,QAAa;AAAE,SAAG,eAAe;AAAG,eAAS;AAAG,oBAAc;AAAA,IAAG,WAC5E,GAAG,QAAQ,OAAa;AAC7B,SAAG,eAAe;AAClB,YAAM,OAAO,QAAQ,KAAK,CAAC;AAC3B,eAAS,KAAK,IAAI,GAAG,KAAK,SAAS,CAAC;AACpC,oBAAc;AAAA,IAClB,WACS,GAAG,QAAQ,SAAa;AAAE,SAAG,eAAe;AAAG,WAAK;AAAA,IAAG,WACvD,QAAQ,eAAe,GAAG,QAAQ,QAAQ,eACvC,CAAC,GAAG,WAAW,CAAC,GAAG,WAAW,CAAC,GAAG,QAAQ;AAClD,SAAG,eAAe;AAAG,WAAK;AAAA,IAC9B;AAAA,EACJ;AAEA,QAAM,SAAS,kBAAkB,MAAM,KAAK;AAE5C,aAAW,eAAe,CAAC;AAE3B,SAAO;AAAA,IACH,UAAU,MAAM,OAAO;AAAA,IACvB,SAAU,MAAM,cAAc;AAAA,IAC9B,YAAY,MAAM;AAAE,eAAS;AAAG,oBAAc;AAAA,IAAG;AAAA,EACrD;AACJ;AAqCO,SAAS,mBAAmB,QAAQ,EAAE,OAAO,cAAc,EAAE,IAAI,CAAC,GAAG;AACxE,MAAI,CAAC,UAAU,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;AACxD,WAAO,EAAE,UAAU,MAAM;AAAA,IAAC,GAAG,SAAS,MAAM;AAAA,IAAC,GAAG,WAAW,MAAM;AAAA,IAAC,EAAE;AAAA,EACxE;AAEA,QAAM,QAAQ,MAAM,IAAI,CAAC,UAAU;AAAA,IAC/B;AAAA,IACA,QAAQ,KAAK;AAAA,IACb,WAAW,KAAK,aACT,KAAK,QAAQ,cAAc,qBAAqB,KAChD,KAAK;AAAA,IACZ,QAAQ;AAAA,EACZ,EAAE;AACF,MAAI,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,MAAM,SAAS,GAAG,WAAW,CAAC;AAEpE,QAAM,mBAAmB,MAAM;AAC3B,UAAM,QAAQ,CAAC,GAAG,MAAM;AACpB,QAAE,QAAQ,UAAU;AAAA,QAChB;AAAA,QAA8B,MAAM;AAAA,MAAU;AAAA,IACtD,CAAC;AAAA,EACL;AAEA,QAAM,gBAAgB,CAAC,MAAM,eAAe;AACxC,UAAM,IAAI,MAAM,GAAG;AACnB,QAAI,CAAC,EAAG;AACR,UAAM,QAAQ,EAAE,WAAW,cAAc,OAAO;AAChD,QAAI,CAAC,MAAO;AACZ,UAAM,OAAO,EAAE,KAAK,UAAU,KAAK,CAAC;AACpC,QAAI,KAAK,WAAW,GAAG;AAAE,QAAE,SAAS;AAAG;AAAA,IAAQ;AAC/C,QAAI,EAAE,UAAU,KAAK,OAAQ,GAAE,SAAS,KAAK,SAAS;AACtD,QAAI,EAAE,SAAS,EAAG,GAAE,SAAS;AAC7B,QAAI,SAAS;AACb,UAAM,iBAAiB,IAAI,EAAE,QAAQ,CAAC,OAAO;AACzC,YAAM,KAAK,GAAG,eAAe,EAAE;AAC/B,SAAG,UAAU,OAAO,kBAAkB,EAAE;AACxC,UAAI,GAAI,UAAS;AAAA,IACrB,CAAC;AACD,QAAI,UAAU,QAAQ,YAAY;AAC9B,aAAO,eAAe,EAAE,OAAO,UAAU,CAAC;AAAA,IAC9C;AAAA,EACJ;AAEA,QAAM,OAAO,CAAC,UAAU;AACpB,UAAM,IAAI,MAAM,UAAU;AAC1B,QAAI,CAAC,EAAG;AACR,UAAM,OAAO,EAAE,KAAK,UAAU,KAAK,CAAC;AACpC,QAAI,KAAK,WAAW,EAAG;AACvB,MAAE,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,EAAE,SAAS,KAAK,CAAC;AAClE,kBAAc;AAAA,EAClB;AAEA,QAAM,OAAO,CAAC,UAAU;AACpB,UAAM,IAAI,MAAM,UAAU;AAC1B,QAAI,CAAC,EAAG;AACR,UAAM,OAAO,EAAE,KAAK,UAAU,KAAK,CAAC;AACpC,MAAE,SAAS,QAAQ,KAAK,IAAI,GAAG,KAAK,SAAS,CAAC,IAAI;AAClD,kBAAc;AAAA,EAClB;AAEA,QAAM,aAAa,MAAM;AACrB,UAAM,IAAI,MAAM,UAAU;AAC1B,QAAI,CAAC,EAAG,QAAO;AACf,UAAM,OAAO,EAAE,KAAK,UAAU,KAAK,CAAC;AACpC,WAAO,KAAK,EAAE,MAAM,KAAK;AAAA,EAC7B;AAEA,QAAM,WAAW,MAAM;AACnB,UAAM,MAAM,WAAW;AACvB,QAAI,CAAC,IAAK;AACV,QAAI;AAAE,YAAM,UAAU,EAAE,KAAK,aAAa,GAAG;AAAA,IAAG,SACzC,KAAK;AAAE,cAAQ,KAAK,mCAAmC,GAAG;AAAA,IAAG;AAAA,EACxE;AAMA,QAAM,gBAAgB,MAAM;AACxB,UAAM,MAAM,WAAW;AACvB,QAAI,CAAC,IAAK;AACV,UAAM,OAAO,MAAM,UAAU,EAAE;AAC/B,UAAM,KAAK,KAAK,mBAAmB,KAAK;AACxC,QAAI;AAAE,WAAK,GAAG;AAAA,IAAG,SACV,KAAK;AAAE,cAAQ,KAAK,uCAAuC,GAAG;AAAA,IAAG;AAAA,EAC5E;AAKA,QAAM,mBAAmB,MAAM;AAC3B,UAAM,MAAM,WAAW;AACvB,QAAI,CAAC,IAAK;AACV,UAAM,OAAO,MAAM,UAAU,EAAE;AAC/B,UAAM,KAAK,KAAK,sBAAsB,KAAK;AAC3C,QAAI;AAAE,WAAK,GAAG;AAAA,IAAG,SACV,KAAK;AAAE,cAAQ,KAAK,0CAA0C,GAAG;AAAA,IAAG;AAAA,EAC/E;AAEA,QAAM,eAAe,CAAC,WAAW;AAC7B,UAAM,IAAI,MAAM,UAAU;AAC1B,QAAI,CAAC,GAAG,KAAK,eAAgB,QAAO;AACpC,UAAM,MAAM,WAAW;AACvB,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI,EAAE,KAAK,gBAAgB,CAAC,EAAE,KAAK,aAAa,GAAG,EAAG,QAAO;AAC7D,UAAM,oBAAoB,EAAE,KAAK,aAC3B,CAAC,CAAC,EAAE,KAAK,WAAW,GAAG,IACvB;AACN,QAAI,WAAW,kBAAmB,QAAO;AACzC,QAAI;AAAE,QAAE,KAAK,eAAe,KAAK,MAAM;AAAA,IAAG,SACnC,KAAK;AAAE,cAAQ,KAAK,iCAAiC,GAAG;AAAA,IAAG;AAClE,WAAO;AAAA,EACX;AAEA,QAAM,YAAY,CAAC,QAAQ;AACvB,QAAI,MAAM,KAAK,OAAO,MAAM,OAAQ;AACpC,QAAI,QAAQ,WAAY;AACxB,iBAAa;AACb,qBAAiB;AACjB,kBAAc;AAAA,EAClB;AAEA,QAAM,YAAY,CAAC,QAAQ;AACvB,QAAI,MAAM,SAAS,EAAG;AACtB,eAAW,aAAa,MAAM,MAAM,UAAU,MAAM,MAAM;AAAA,EAC9D;AAEA,QAAM,cAAc,CAAC,OAAO;AAGxB,UAAM,SAAS,GAAG,OAAO,UAAU,oBAAoB;AACvD,QAAI,CAAC,OAAQ;AACb,UAAM,MAAM,MAAM,UAAU,CAAC,MAAM,EAAE,WAAW,MAAM;AACtD,QAAI,MAAM,KAAK,QAAQ,WAAY;AACnC,iBAAa;AACb,qBAAiB;AACjB,kBAAc;AAAA,EAClB;AAEA,QAAM,QAAQ,CAAC,OAAO;AAClB,QAAI,CAAC,OAAO,YAAa;AAIzB,UAAM,IAAI,GAAG;AACb,UAAM,WAAW,MACb,EAAE,YAAY,WAAW,EAAE,YAAY,cACvC,EAAE,YAAY,YAAY,EAAE;AAChC,QAAI,SAAU;AAKd,QAAI,GAAG,QAAQ,YAAY,GAAG,WAAW,GAAG,YAAY,CAAC,GAAG,QAAQ;AAChE,SAAG,eAAe;AAClB,oBAAc;AACd;AAAA,IACJ;AACA,QAAI,GAAG,QAAQ,WAAW,GAAG,YAAY,CAAC,GAAG,UACtC,CAAC,GAAG,WAAW,CAAC,GAAG,SAAS;AAC/B,SAAG,eAAe;AAClB,uBAAiB;AACjB;AAAA,IACJ;AAEA,QAAI,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,SAAU;AAE1D,YAAQ,GAAG,KAAK;AAAA,MACZ,KAAK;AACD,WAAG,eAAe;AAAG,aAAK,CAAE;AAAG;AAAA,MACnC,KAAK;AACD,WAAG,eAAe;AAAG,aAAK,EAAE;AAAG;AAAA,MACnC,KAAK;AACD,WAAG,eAAe;AAAG,aAAK,KAAK;AAAG;AAAA,MACtC,KAAK;AACD,WAAG,eAAe;AAAG,aAAK,IAAI;AAAG;AAAA,MACrC,KAAK;AACD,WAAG,eAAe;AAAG,iBAAS;AAAG;AAAA,MACrC,KAAK;AAGD,WAAG,eAAe;AAClB,kBAAU,GAAG,WAAW,KAAK,CAAE;AAC/B;AAAA,MACJ,KAAK;AACD,YAAI,aAAa,IAAI,EAAG,IAAG,eAAe;AAC1C;AAAA,MACJ,KAAK;AACD,YAAI,aAAa,KAAK,EAAG,IAAG,eAAe;AAC3C;AAAA,IACR;AAAA,EACJ;AAEA,SAAO,iBAAiB,aAAa,WAAW;AAChD,QAAM,SAAS,kBAAkB,QAAQ,KAAK;AAC9C,mBAAiB;AAGjB,aAAW,MAAM,MAAM,QAAQ,CAAC,GAAG,MAAM,cAAc,CAAC,CAAC,GAAG,CAAC;AAY7D,QAAM,oBAAoB,MAAM;AAC5B,UAAM,OAAO,OAAO,UAAU,WAAW;AAGzC,UAAM,SAAS,CAAC,CAAC;AACjB,QAAI,UAAU,CAAC,KAAK,UAAU,SAAS,mBAAmB,EAAG;AAC7D,QAAI,OAAO,SAAS,SAAS,aAAa,EAAG;AAC7C,QAAI,OAAO,YAAY,QAAQ,OAAO,WAAW,GAAI,QAAO,WAAW;AACvE,QAAI;AAAE,aAAO,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,IAAG,QAAQ;AAAA,IAAC;AAAA,EAC1D;AACA,oBAAkB;AAClB,wBAAsB,iBAAiB;AAEvC,SAAO;AAAA,IACH,UAAU,MAAM;AACZ,aAAO,oBAAoB,aAAa,WAAW;AACnD,aAAO;AAAA,IACX;AAAA,IACA,SAAS,MAAM,MAAM,QAAQ,CAAC,GAAG,MAAM,cAAc,CAAC,CAAC;AAAA,IACvD;AAAA,IACA;AAAA,EACJ;AACJ;AAyCO,SAAS,kBAAkB,QAAQ,MAAM;AAC5C,QAAM;AAAA,IACF,QAAQ;AAAA,IACR,QAAQ,CAAC;AAAA,IACT,OAAQ,EAAE,KAAK,QAAQ,OAAO,IAAI,MAAM,GAAG;AAAA,EAC/C,IAAI,QAAQ,CAAC;AACb,QAAM,UAAU,OAAO,KAAK,OAAO,MAAM;AAEzC,QAAMC,QAAO,CAAC,MAAM,OAAO,KAAK,EAAE,EAAE,QAAQ,YAAY,CAAC,OAAO;AAAA,IAC5D,KAAK;AAAA,IAAS,KAAK;AAAA,IAAQ,KAAK;AAAA,IAAQ,KAAK;AAAA,IAAU,KAAK;AAAA,EAChE,GAAE,CAAC,CAAE;AAEL,QAAM,aAAa,SAAS,CAAC,GAAG,IAAI,CAAC,MAAM;AAAA;AAAA,oBAE3BA,MAAK,EAAE,SAAS,EAAE,CAAC;AAAA,iCACNA,MAAK,EAAE,GAAG,CAAC,KAAKA,MAAK,EAAE,WAAW,OAAO,EAAE,UAAU,CAAC,CAAC;AAAA;AAAA,KAEnF,EAAE,KAAK,EAAE;AAEV,SAAO,UAAU,IAAI,aAAa;AAClC,SAAO,YAAY;AAAA;AAAA,kBAELA,MAAK,KAAK,CAAC;AAAA,cACf,SAAS;AAAA;AAAA;AAAA;AAAA,4DAIqCA,MAAK,OAAO,CAAC;AAAA;AAAA,4DAEbA,MAAK,KAAK,SAAS,EAAE,CAAC;AAAA,2DACvBA,MAAK,KAAK,QAAQ,EAAE,CAAC;AAAA;AAAA,6DAEnBA,MAAK,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA;AAMtE,QAAM,WAAY,OAAO,cAAc,oBAAoB;AAC3D,QAAM,SAAY,OAAO,cAAc,eAAe,OAAO,IAAI;AACjE,QAAM,YAAY,OAAO,cAAc,eAAe,OAAO,SAAS;AACtE,QAAM,WAAY,OAAO,cAAc,uBAAuB;AAE9D,QAAM,UAAU,CAAC,KAAK,UAAU;AAC5B,UAAM,KAAK,OAAO,cAAc,eAAe,GAAG,IAAI;AACtD,QAAI,GAAI,IAAG,cAAc,OAAO,KAAK;AAAA,EACzC;AAEA,SAAO,EAAE,UAAU,QAAQ,WAAW,UAAU,QAAQ;AAC5D;AAGO,SAAS,oBAAoB,UAAU,SAAS,QAAQ;AAC3D,MAAI,CAAC,SAAU,QAAO,EAAE,UAAU,MAAM;AAAA,EAAC,GAAG,SAAS,MAAM;AAAA,EAAC,EAAE;AAC9D,MAAI,UAAU,CAAC;AACf,QAAM,aAAa,CAAC,SAAS;AACzB,eAAW,QAAQ,CAAC,GAAG,OAAO,OAAO;AACrC,aAAS,YAAY;AACrB,eAAW,KAAK,SAAS;AACrB,YAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,UAAI,OAAO;AACX,UAAI,YAAY;AAChB,UAAI,EAAE,GAAO,KAAI,QAAQ,SAAS,EAAE;AACpC,UAAI,EAAE,MAAO,KAAI,QAAQ,EAAE;AAC3B,YAAM,OAAO,EAAE,OACT,2CAA2CA,MAAK,EAAE,IAAI,CAAC,YACvD;AACN,YAAM,KAAK,EAAE,WACP,uCAAuCA,MAAK,EAAE,SAAS,YAAY,CAAC,CAAC,aACrE;AACN,UAAI,YAAY,GAAG,IAAI,SAASA,MAAK,EAAE,SAAS,EAAE,CAAC,UAAU,EAAE;AAC/D,UAAI,iBAAiB,SAAS,CAAC,OAAO;AAClC,WAAG,eAAe;AAClB,YAAI;AAAE,YAAE,UAAU,EAAE;AAAA,QAAG,SAChB,KAAK;AAAE,kBAAQ,KAAK,kCAAkC,GAAG;AAAA,QAAG;AAAA,MACvE,CAAC;AACD,eAAS,YAAY,GAAG;AAAA,IAC5B;AAAA,EACJ;AAEA,QAAM,QAAQ,CAAC,OAAO;AAClB,QAAI,CAAC,SAAS,YAAa;AAC3B,QAAI,GAAG,WAAW,GAAG,WAAW,GAAG,OAAQ;AAG3C,UAAM,IAAI,GAAG;AACb,UAAM,WAAW,MACb,EAAE,YAAY,WAAW,EAAE,YAAY,cACvC,EAAE,YAAY,YAAY,EAAE;AAChC,QAAI,SAAU;AACd,UAAM,IAAI,GAAG,IAAI,WAAW,IAAI,GAAG,IAAI,YAAY,IAAI,GAAG;AAC1D,UAAM,MAAM,QAAQ,KAAK,CAAC,MACtB,EAAE,YAAY,OAAO,EAAE,QAAQ,EAAE,YAAY,MAAM,CAAC;AACxD,QAAI,CAAC,IAAK;AACV,OAAG,eAAe;AAClB,QAAI;AAAE,UAAI,UAAU,EAAE;AAAA,IAAG,SAClB,KAAK;AAAE,cAAQ,KAAK,qCAAqC,GAAG;AAAA,IAAG;AAAA,EAC1E;AAEA,aAAW,OAAO;AAClB,QAAM,UAAU,UAAU,SAAS,QAAQ,cAAc,KAAK;AAC9D,QAAM,SAAS,kBAAkB,SAAS,KAAK;AAC/C,SAAO;AAAA,IACH,UAAU,MAAM,OAAO;AAAA,IACvB,SAAS;AAAA,EACb;AACJ;AASO,SAAS,oBAAoB,eAAe,UAAU,EAAE,MAAM,MAAM,QAAQ,KAAK,GAAG;AACvF,SAAO,CAAC,IAAI,QAAQ,WAAW;AAC3B,QAAI,WAAW,cAAe,QAAO;AACrC,UAAM,QAAQ,CAAC;AACf,QAAI,QAAQ,MAAM;AACd,YAAM,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,kCAKW;AAAA,IAC1B;AACA,QAAI,QAAQ,QAAQ;AAChB,YAAM,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,kCAKW;AAAA,IAC1B;AACA,OAAG,UAAU,IAAI,sBAAsB;AACvC,OAAG,YAAY,MAAM,KAAK,EAAE;AAC5B,WAAO;AAAA,EACX;AACJ;AAGA,SAASA,MAAK,GAAG;AACb,SAAO,OAAO,KAAK,EAAE,EAAE,QAAQ,YAAY,CAAC,OAAO;AAAA,IAC/C,KAAK;AAAA,IAAS,KAAK;AAAA,IAAQ,KAAK;AAAA,IAAQ,KAAK;AAAA,IAAU,KAAK;AAAA,EAChE,GAAE,CAAC,CAAE;AACT;;;AC1rBO,SAAS,mBAAmB,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC,GAAG;AAC1D,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY;AACpB,UAAQ,YACJ;AACJ,OAAK,YAAY,OAAO;AAExB,QAAM,YAAY,WAAW,MAAM;AAC/B,YAAQ,UAAU,OAAO,2BAA2B;AAAA,EACxD,GAAG,KAAK;AACR,SAAO,MAAM;AACT,iBAAa,SAAS;AACtB,YAAQ,OAAO;AAAA,EACnB;AACJ;;;ACEO,SAAS,cAAc,QAAQ;AAAA,EAClC,QAAQ,CAAC;AAAA,EACT,KAAK;AAAA,EACL,WAAW;AAAA,EACX,oBAAoB;AACxB,IAAI,CAAC,GAAG;AACJ,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;AAC7C,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAClE;AAEA,SAAO,UAAU,IAAI,SAAS;AAC9B,SAAO,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,gDAKyBC,MAAK,iBAAiB,CAAC;AAAA;AAAA;AAAA;AAAA;AAKnE,QAAM,UAAU,OAAO,cAAc,qBAAqB;AAC1D,QAAM,SAAU,OAAO,cAAc,oBAAoB;AACzD,QAAM,WAAW,OAAO,cAAc,wBAAwB;AAE9D,UAAQ,YAAY,MAAM,IAAI,CAAC,MAAM;AAAA,mEAC0BA,MAAK,EAAE,EAAE,CAAC;AAAA,gCAC7CA,MAAK,EAAE,KAAK,CAAC;AAAA,sDACSA,MAAK,EAAE,IAAI,CAAC;AAAA,oBAC9CA,MAAK,EAAE,KAAK,CAAC;AAAA;AAAA,KAE5B,EAAE,KAAK,EAAE;AAEV,QAAM,OAAO,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAChD,QAAM,QAAQ,CAAC;AACf,QAAM,QAAQ,CAAC;AACf,QAAM,eAAe,OAAO,YAAY,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AACpE,MAAI,OAAO,MAAM,CAAC,EAAE;AAEpB,QAAM,aAAa,CAAC,OAAO;AACvB,QAAI,MAAM,EAAE,EAAG,QAAO,MAAM,EAAE;AAC9B,UAAM,KAAK,SAAS,cAAc,KAAK;AACvC,OAAG,YAAY,gCAAgC,EAAE;AACjD,OAAG,MAAM,UAAU;AACnB,WAAO,YAAY,EAAE;AACrB,UAAM,EAAE,IAAI;AACZ,WAAO;AAAA,EACX;AAEA,QAAM,WAAW,OAAO,SAAS;AAC7B,UAAM,MAAM,KAAK,IAAI,IAAI;AACzB,QAAI,CAAC,IAAK;AACV,WAAO;AACP,eAAW,KAAK,OAAO;AACnB,iBAAW,EAAE,EAAE,EAAE,MAAM,UAAW,EAAE,OAAO,OAAQ,SAAS;AAAA,IAChE;AACA,YAAQ,iBAAiB,aAAa,EAAE,QAAQ,CAAC,MAC7C,EAAE,UAAU,OAAO,qBAAqB,EAAE,QAAQ,SAAS,IAAI,CAAC;AACpE,aAAS,QAAQ,aAAa,IAAI,KAAK;AAEvC,UAAM,SAAS,WAAW,IAAI;AAC9B,QAAI,CAAC,MAAM,IAAI,GAAG;AACd,UAAI;AAAE,cAAM,IAAI,IAAI,IAAI,MAAM,QAAQ,EAAE,IAAI,SAAS,CAAC,KAAK,CAAC;AAAA,MAAG,SACxD,KAAK;AACR,gBAAQ,MAAM,2BAA2B,MAAM,GAAG;AAClD,cAAM,IAAI,IAAI,CAAC;AAAA,MACnB;AAAA,IACJ;AAGA,QAAI;AAAE,YAAM,MAAM,IAAI,EAAE,WAAW;AAAA,IAAG,SAC/B,KAAK;AAAE,cAAQ,MAAM,8BAA8B,MAAM,GAAG;AAAA,IAAG;AACtE,iBAAa;AAAA,EACjB;AAEA,QAAM,eAAe,MAAM;AACvB,UAAM,IAAI,aAAa,IAAI,KAAK;AAChC,QAAI;AAAE,YAAM,IAAI,GAAG,SAAS,CAAC;AAAA,IAAG,SACzB,KAAK;AAAE,cAAQ,KAAK,uBAAuB,MAAM,GAAG;AAAA,IAAG;AAAA,EAClE;AAEA,WAAS,iBAAiB,SAAS,MAAM;AACrC,iBAAa,IAAI,IAAI,SAAS;AAC9B,iBAAa;AAAA,EACjB,CAAC;AAED,UAAQ,iBAAiB,SAAS,CAAC,MAAM;AACrC,UAAM,MAAM,EAAE,OAAO,QAAQ,aAAa;AAC1C,QAAI,IAAK,UAAS,IAAI,QAAQ,IAAI;AAAA,EACtC,CAAC;AAED,WAAS,IAAI;AAEb,SAAO;AAAA,IACH,SAAS,MAAM,SAAS,IAAI;AAAA,IAC5B,SAAS,MAAM;AACX,iBAAW,KAAK,OAAO,OAAO,KAAK,GAAG;AAClC,YAAI;AAAE,aAAG,UAAU;AAAA,QAAG,QAAQ;AAAA,QAAe;AAAA,MACjD;AAAA,IACJ;AAAA,EACJ;AACJ;AAEA,SAASA,MAAK,GAAG;AACb,SAAO,OAAO,KAAK,EAAE,EAAE,QAAQ,YAAY,CAAC,OACxC,EAAE,KAAK,SAAS,KAAK,QAAQ,KAAK,QAAQ,KAAK,UAAU,KAAK,QAAQ,GAAE,CAAC,CAC5E;AACL;;;AClHO,SAAS,oBAAoB,MAAM,OAAO,KAAK;AAClD,QAAM,WAAY,KAAK;AACvB,QAAM,WAAY,KAAK;AACvB,QAAM,YAAY,KAAK,aAAa;AACpC,MAAI,CAAC,UAAU;AACX,YAAQ,MAAM,sDAAiD;AAC/D,WAAO,EAAE,IAAI,SAAS,cAAc,KAAK,GAAG,SAAS,MAAM;AAAA,IAAC,EAAE;AAAA,EAClE;AACA,QAAM,OAAO,SAAS,cAAc,KAAK;AACzC,OAAK,YAAY;AACjB,OAAK,aAAa,cAAc,iBAAiB;AAOjD,QAAM,QAAQ,MAAM,KAAK,MAAM,OAAO,OAAO,MAAM;AASnD,QAAM,WAAW,CAAC,GAAG,IAAI,CAAC,MAAM;AAC5B,UAAM,KAAK,MAAM;AACjB,QAAI,CAAC,IAAI;AACL,cAAQ,MAAM,qDAAgD,CAAC;AAC/D;AAAA,IACJ;AACA,QAAI,OAAO,GAAG,aAAa,YAAY;AACnC,SAAG,SAAS,GAAG,GAAG,EAAE,KAAK,MAAM,SAAS,CAAC;AAAA,IAC7C,WAAW,OAAO,GAAG,kBAAkB,YAAY;AAE/C,SAAG,cAAc,GAAG,CAAC;AAAA,IACzB,OAAO;AACH,cAAQ,MAAM,+CAA+C;AAAA,IACjE;AAAA,EACJ;AAMA,MAAI,YAAY,WAAW,SAAS;AAKpC,QAAM,UAAU,MAAM;AAClB,QAAI,OAAO,KAAK,kBAAkB,WAAY,QAAO,CAAC;AACtD,QAAI;AAAE,aAAO,IAAI,cAAc,KAAK,CAAC;AAAA,IAAG,SACjC,KAAK;AAAE,cAAQ,KAAK,oCAAoC,GAAG;AAAG,aAAO,CAAC;AAAA,IAAG;AAAA,EACpF;AACA,QAAM,SAAS,MAAM;AACjB;AAAA,MAAY;AAAA,MACR,UAAU,MAAM,OAAO,UAAU,WAAW,WAAW,UAAU,QAAQ,CAAC;AAAA,IAAC;AAAA,EACnF;AACA,SAAO;AAEP,MAAI,cAAc;AAClB,MAAI,OAAO,WAAW,cAAc,YAAY;AAC5C,QAAI;AACA,oBAAc,UAAU,UAAU,CAAC,SAAS;AACxC,YAAI,MAAM,MAAO,aAAY,KAAK;AAClC,eAAO;AAAA,MACX,GAAG,EAAE,KAAK,KAAK,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC;AAAA,IACnD,SAAS,KAAK;AACV,cAAQ,KAAK,0CAA0C,GAAG;AAAA,IAC9D;AAAA,EACJ;AAEA,SAAO;AAAA,IACH,IAAI;AAAA;AAAA;AAAA;AAAA,IAIJ,SAAS;AAAA,IACT,SAAS,MAAM;AACX,UAAI;AAAE,sBAAc;AAAA,MAAG,SAChB,KAAK;AAAE,gBAAQ,KAAK,yCAAyC,GAAG;AAAA,MAAG;AAAA,IAC9E;AAAA,EACJ;AACJ;AAEA,SAAS,UAAU,MAAM,OAAO,UAAU,WAAW,WAAW,UAAU,OAAO;AAC7E,QAAM,OAAO,CAAC;AACd,QAAM,OAAO,SAAS,KAAK,IAAI;AAK/B,MAAI,MAAM,WAAW;AACjB,WAAO,CAAC,EAAE,MAAM,KAAK,MAAM,OAAO,KAAK,OAAO,SAAS,KAAK,CAAC;AAAA,EACjE;AAGA,MAAI,WAAW;AACX,SAAK,KAAK;AAAA,MACN,MAAO,UAAU;AAAA,MACjB,OAAO,aAAa,UAAU;AAAA,MAC9B,SAAS,MAAM,SAAS,UAAU,WAAW,SAAS,IAAI;AAAA,IAC9D,CAAC;AAAA,EACL;AAKA,QAAM,SAAa,SAAS,UAAU,IAAI;AAC1C,QAAM,aAAa,SAAS,SAAS,KAAK,MAAM,IAAI;AACpD,MAAI,UAAU,WAAW,SAAS,QAAQ,YAAY;AAClD,SAAK,KAAK;AAAA,MACN,MAAO,WAAW;AAAA,MAClB,OAAO,WAAW;AAAA,MAClB,SAAS,MAAM,SAAS,MAAM;AAAA,IAClC,CAAC;AAAA,EACL;AAYA,aAAW,QAAS,SAAS,CAAC,GAAI;AAC9B,QAAI,CAAC,MAAM,KAAM;AACjB,SAAK,KAAK;AAAA,MACN,MAAO,SAAS,KAAK,KAAK,IAAI,GAAG,QAAQ;AAAA,MACzC,OAAO,KAAK,SAAS,KAAK,OAAO,SAAS,KAAK,OAAO,MAAM,KAAK;AAAA,MACjE,SAAS,MAAM,SAAS,KAAK,MAAM,KAAK,SAAS,CAAC,CAAC;AAAA,IACvD,CAAC;AAAA,EACL;AAIA,aAAW,OAAO,SAAS,UAAU,MAAM,KAAK,GAAG;AAC/C,SAAK,KAAK;AAAA,MACN,MAAO,SAAS,KAAK,IAAI,IAAI,GAAG,QAAQ;AAAA,MACxC,OAAO,IAAI,MAAM,SAAS,IAAI,MAAM;AAAA,MACpC,SAAS,MAAM,SAAS,IAAI,MAAM,IAAI,KAAK;AAAA,IAC/C,CAAC;AAAA,EACL;AAGA,QAAM,YAAY,CAAC,CAAC,OAAO;AAC3B,MAAI,MAAM;AACN,QAAI,WAAW;AACX,WAAK,KAAK;AAAA,QACN,MAAO,KAAK;AAAA,QACZ,OAAO,SAAS,QAAQ,MAAM,KAAK;AAAA,QACnC,SAAS;AAAA,MACb,CAAC;AAAA,IACL,WAAW,SAAS,QAAQ;AAIxB,WAAK,KAAK;AAAA,QACN,MAAO,KAAK;AAAA,QACZ,OAAO,KAAK;AAAA,QACZ,SAAS;AAAA,MACb,CAAC;AAAA,IACL;AAAA,EACJ,WAAW,WAAW;AAGlB,SAAK,KAAK;AAAA,MACN,MAAO;AAAA,MACP,OAAO,MAAM,SAAS,MAAM;AAAA,MAC5B,SAAS;AAAA,IACb,CAAC;AAAA,EACL;AACA,SAAO;AACX;AAEA,SAAS,YAAY,WAAW,UAAU;AACtC,YAAU,YAAY;AACtB,MAAI,CAAC,UAAU,OAAQ;AACvB,QAAM,KAAK,SAAS,cAAc,IAAI;AACtC,KAAG,YAAY;AACf,WAAS,QAAQ,CAAC,KAAK,MAAM;AACzB,UAAM,KAAK,SAAS,cAAc,IAAI;AACtC,OAAG,YAAY;AACf,UAAM,SAAS,MAAM,SAAS,SAAS;AACvC,QAAI,UAAU,CAAC,IAAI,SAAS;AACxB,SAAG,UAAU,IAAI,sCAAsC;AACvD,SAAG,YAAY;AAAA,sFAC2DC,MAAK,IAAI,IAAI,CAAC;AAAA,+DACrCA,MAAK,IAAI,KAAK,CAAC;AAAA;AAAA,IAEtE,OAAO;AACH,YAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,UAAI,YAAY;AAChB,UAAI,OAAO;AACX,UAAI,YAAY;AAAA,sFAC0DA,MAAK,IAAI,IAAI,CAAC;AAAA,wBAC5EA,MAAK,IAAI,KAAK,CAAC;AAAA;AAE3B,UAAI,iBAAiB,SAAS,MAAM;AAChC,YAAI,UAAU,IAAI,kCAAkC;AACpD,mBAAW,MAAM;AACb,cAAI,UAAU,OAAO,kCAAkC;AAAA,QAC3D,GAAG,GAAG;AACN,YAAI;AAAE,cAAI,QAAQ;AAAA,QAAG,SACd,GAAG;AAAE,kBAAQ,MAAM,oCAAoC,CAAC;AAAA,QAAG;AAAA,MACtE,CAAC;AACD,SAAG,YAAY,GAAG;AAClB,YAAM,MAAM,SAAS,cAAc,MAAM;AACzC,UAAI,YAAY;AAChB,UAAI,cAAc;AAClB,SAAG,YAAY,GAAG;AAAA,IACtB;AACA,OAAG,YAAY,EAAE;AAAA,EACrB,CAAC;AACD,YAAU,YAAY,EAAE;AAC5B;AAEA,SAASA,MAAK,GAAG;AACb,SAAO,OAAO,KAAK,EAAE,EAAE,QAAQ,YAAY,CAAC,OAAO;AAAA,IAC/C,KAAK;AAAA,IAAS,KAAK;AAAA,IAAQ,KAAK;AAAA,IAAQ,KAAK;AAAA,IAAU,KAAK;AAAA,EAChE,GAAE,CAAC,CAAE;AACT;;;AClNO,SAAS,oBAAoB,EAAE,WAAW,MAAM,SAAS,CAAC,GAAG,YAAY,CAAC,EAAE,IAAI,CAAC,GAAG;AACvF,QAAM,iBAAiB,QAAQ,kBAAkB;AAIjD,QAAM,WAAW,CAAC,SAAS;AAAA;AAAA;AAAA,IAGvB,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,OAAO,MAAM,OAAO,MAC/C,IAAI,IAAI,gBAAgB,KAAK,GAAG,EAAE,IAAI,UAAU,OAAO,MAAM,OAAO,CAAC;AAAA;AAAA,IAEzE,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,OAAO,MAAM,OAAO,MACjD,IAAI,IAAI,qBAAqB,KAAK,GAAG,EAAE,IAAI,UAAU,OAAO,MAAM,OAAO,CAAC;AAAA;AAAA,IAE9E,cAAc,CAAC,EAAE,MAAM,GAAG,UAAU,OAAO,MAAM,OAAO,MACpD,IAAI,IAAI,SAAS,GAAG,EAAE,IAAI,UAAU,OAAO,MAAM,OAAO,GAAG,EAAE,KAAK,MAAM,SAAS,CAAC;AAAA;AAAA,IAEtF,aAAa,CAAC,UAAU;AACpB,UAAI;AAAE,YAAI,IAAI,uBAAuB,IAAI,QAAQ,KAAK;AAAA,MAAG,SAClD,KAAK;AAAE,gBAAQ,KAAK,6BAA6B,GAAG;AAAA,MAAG;AAAA,IAClE;AAAA;AAAA;AAAA;AAAA,IAIA,UAAU,CAAC,QAAQ;AACf,UAAI,gBAAgB;AAChB,YAAI;AAAE,oBAAU,OAAO,gBAAgB,EAAE,QAAQ,gBAAgB,CAAC;AAAA,QAAG,QAAQ;AAAA,QAAC;AAAA,MAClF;AACA,UAAI;AAAE,YAAI,IAAI,iBAAiB,IAAI,MAAM;AAAA,MAAG,SAAS,KAAK;AACtD,gBAAQ,KAAK,0BAA0B,GAAG;AAAA,MAC9C;AAAA,IACJ;AAAA;AAAA,IAEA,eAAe,CAAC,WAAW;AACvB,UAAI,CAAC,eAAgB;AACrB,UAAI;AACA,kBAAU;AAAA,UAAO;AAAA,UACb,EAAE,QAAQ,gBAAgB,QAAQ,UAAU,KAAK;AAAA,QAAC;AAAA,MAC1D,QAAQ;AAAA,MAAC;AAAA,IACb;AAAA,IACA,kBAAkB,MAAM;AAAA,IAAC;AAAA,IACzB,cAAc,MAAM;AAAA,EACxB;AAOA,QAAM,eAAe,CAAC,SAAS,aAAa,OAAO,QAAQ;AACvD,UAAM,OAAO,SAAS,GAAG;AACzB,UAAM,MAAM,QAAQ,aAAa,OAAO,MAAM,OAAO,YAAY,MAAM;AAAA,MACnE,QAAQ;AAAA,MAAM;AAAA,MAAU,eAAe;AAAA,MACvC,IAAI,IAAI;AAAA,MAAI,QAAQ,IAAI;AAAA,MAAQ,UAAU,IAAI;AAAA,IAClD,CAAC;AACD,UAAM,cAAc,mBAAmB,WAAW;AAClD,QAAI;AACA,YAAM,MAAM,KAAK,QAAQ,KAAK;AAC9B,UAAI,OAAO,OAAO,IAAI,SAAS,YAAY;AACvC,YAAI,KAAK,aAAa,CAAC,QAAQ;AAC3B,kBAAQ,MAAM,uBAAuB,GAAG;AACxC,sBAAY;AAAA,QAChB,CAAC;AAAA,MACL,OAAO;AACH,8BAAsB,WAAW;AAAA,MACrC;AAAA,IACJ,SAAS,KAAK;AACV,cAAQ,MAAM,uBAAuB,GAAG;AACxC,kBAAY;AAAA,IAChB;AACA,UAAM,YAAY,CAAC;AACnB,QAAI,OAAO,KAAK,YAAY,cAAc,UAAU,IAAI;AACpD,iBAAW,CAAC,UAAU,MAAM,KAAK,OAAO,QAAQ,aAAa,CAAC,CAAC,GAAG;AAC9D,cAAM,KAAK,MAAM;AACb,cAAI;AAAE,gBAAI,QAAQ,MAAM;AAAA,UAAG,SACpB,KAAK;AAAE,oBAAQ,KAAK,yBAAyB,UAAU,GAAG;AAAA,UAAG;AAAA,QACxE;AACA,kBAAU,QAAQ,IAAI;AACtB,iBAAS,GAAG,UAAU,EAAE;AAAA,MAC5B;AAAA,IACJ;AACA,WAAO,MAAM;AACT,iBAAW,CAAC,GAAG,EAAE,KAAK,OAAO,QAAQ,SAAS,GAAG;AAC7C,YAAI;AAAE,oBAAU,MAAM,GAAG,EAAE;AAAA,QAAG,QAAQ;AAAA,QAAC;AAAA,MAC3C;AACA,UAAI;AAAE,aAAK,UAAU;AAAG,aAAK,UAAU;AAAA,MAAG,QAAQ;AAAA,MAAC;AAAA,IACvD;AAAA,EACJ;AAKA,QAAM,aAAa,CAAC,MAAM,YAAY,CAAC,MAAM,OAAO,QAAQ;AACxD,SAAK,UAAU,IAAI,gBAAgB;AACnC,SAAK,YAAY;AACjB,UAAM,cAAc,SAAS,cAAc,KAAK;AAChD,gBAAY,YAAY;AAGxB,gBAAY,WAAW;AAKvB,QAAI,QAAQ;AACZ,QAAI,CAAC,KAAK,UAAU;AAChB,YAAM,iBAAiB,SAAS,cAAc,KAAK;AACnD,qBAAe,YAAY;AAC3B,WAAK,YAAY,cAAc;AAC/B,cAAQ,oBAAoB,MAAM,OAAO,EAAE,GAAG,KAAK,SAAS,CAAC;AAC7D,qBAAe,YAAY,MAAM,EAAE;AAAA,IACvC;AACA,SAAK,YAAY,WAAW;AAE5B,UAAM,WAAW,aAAa,SAAS,aAAa,OAAO,GAAG;AAC9D,WAAO;AAAA,MACH,SAAS,MAAM;AACX,iBAAS;AACT,YAAI;AAAE,iBAAO,QAAQ;AAAA,QAAG,QAAQ;AAAA,QAAC;AAAA,MACrC;AAAA,IACJ;AAAA,EACJ;AAMA,QAAM,iBAAiB,CAAC,MAAM,YAAY,CAAC,MAAM,OAAO,QAAQ;AAC5D,SAAK,UAAU,IAAI,gBAAgB;AACnC,SAAK,YAAY;AACjB,UAAM,cAAc,SAAS,cAAc,KAAK;AAChD,gBAAY,YAAY;AACxB,gBAAY,WAAW;AACvB,SAAK,YAAY,WAAW;AAC5B,WAAO,EAAE,SAAS,aAAa,SAAS,aAAa,OAAO,GAAG,EAAE;AAAA,EACrE;AAKA,QAAM,kBAAkB,CAAC,MAAM,WAAW,CAAC,MAAM,OAAO,QAAQ;AAC5D,SAAK,UAAU,IAAI,gBAAgB;AACnC,SAAK,YAAY;AACjB,UAAM,iBAAiB,SAAS,cAAc,KAAK;AACnD,mBAAe,YAAY;AAC3B,UAAM,cAAc,SAAS,cAAc,KAAK;AAChD,gBAAY,YAAY;AACxB,gBAAY,WAAW;AACvB,SAAK,YAAY,cAAc;AAC/B,SAAK,YAAY,WAAW;AAC5B,UAAM,QAAQ,oBAAoB,MAAM,OAAO,EAAE,GAAG,KAAK,SAAS,CAAC;AACnE,mBAAe,YAAY,MAAM,EAAE;AACnC,UAAM,cAAc,mBAAmB,WAAW;AAClD,UAAM,MAAM,OAAO,aAAa,OAAO,GAAG,KAAK,CAAC;AAIhD,QAAI,IAAI,SAAS,OAAO,IAAI,MAAM,SAAS,YAAY;AACnD,UAAI,MAAM,KAAK,aAAa,WAAW;AAAA,IAC3C,OAAO;AACH,4BAAsB,WAAW;AAAA,IACrC;AACA,WAAO;AAAA,MACH,OAAO,IAAI;AAAA,MACX,SAAS,MAAM;AAAE,YAAI;AAAE,gBAAM,QAAQ;AAAA,QAAG,QAAQ;AAAA,QAAC;AAAE,YAAI,UAAU;AAAA,MAAG;AAAA,IACxE;AAAA,EACJ;AAEA,SAAO,OAAO,OAAO;AAAA,IACjB;AAAA,IAAU;AAAA,IAAc;AAAA,IAAY;AAAA,IAAgB;AAAA,EACxD,CAAC;AACL;;;AC5DA,IAAM,cAAc;AAGpB,IAAM,cAAc,CAAC,UAAU,KAAK;AACpC,IAAM,qBAAqB;AAK3B,SAAS,oBAAoB,OAAO;AAChC,SAAO,YAAY,SAAS,KAAK,IAAI,QAAQ;AACjD;AAkBA,SAAS,QAAQ,KAAK;AAAE,SAAO,iBAAiB,GAAG;AAAI;AAEvD,SAAS,aAAa,KAAK;AACvB,QAAM,IAAI,OAAO,OAAO,OAAO,EAAE,EAAE,QAAQ,kBAAkB,EAAE,CAAC;AAChE,SAAO,OAAO,UAAU,CAAC,KAAK,KAAK,IAAI,IAAI;AAC/C;AAIA,IAAM,eAAe;AAcrB,IAAM,kBAAkB;AAiBxB,SAAS,gBAAgB,MAAM,UAAU;AACrC,SAAO,OAAO,KAAK,YAAY;AACnC;AAiBO,IAAM,gBAAgB;AAI7B,SAAS,eAAe,GAAG;AACvB,MAAI;AAAE,WAAO,CAAC,CAAC,EAAE,cAAc,OAAO,SAAS,aAAa;AAAA,EAAG,QACzD;AAAE,WAAO;AAAA,EAAO;AAC1B;AAEO,IAAM,eAAN,MAAmB;AAAA;AAAA;AAAA;AAAA,EAItB,WAAW,WAAW;AAAE,WAAO;AAAA,EAAe;AAAA,EAE9C,YAAY;AAAA,IAAE;AAAA,IAAM;AAAA,IAAM;AAAA,IAAS;AAAA,IAAK;AAAA,IAAe;AAAA,IACzC,YAAY;AAAA,EAAK,GAAG;AAC9B,QAAI,CAAC,WAAW,OAAO,QAAQ,UAAU,YAAY;AACjD,YAAM,IAAI,MAAM,8CAA8C;AAAA,IAClE;AACA,SAAK,OAAO;AACZ,SAAK,OAAO;AAEZ,SAAK,UAAU;AACf,SAAK,MAAM,OAAO,CAAC;AACnB,SAAK,gBAAgB,kBAAkB,MAAM;AAAA,IAAC;AAa9C,SAAK,gBAAgB,kBAAkB,MAAM;AAAA,IAAC;AAE9C,SAAK,aAAa,oBAAI,IAAI;AAC1B,SAAK,QAAQ;AAKb,SAAK,WAAW;AAOhB,SAAK,cAAc;AAKnB,SAAK,gBAAgB;AACrB,SAAK,oBAAoB;AAKzB,SAAK,WAAW;AAChB,SAAK,KAAK,UAAU,IAAI,UAAU;AAKlC,SAAK,YAAY;AAAA,MACb,KAAK,KAAK,SAAS,WAAW,aAAa;AAAA,IAAkB;AACjE,QAAI,KAAK,KAAK,QAAS,MAAK,KAAK,QAAQ,UAAU,KAAK;AAGxD,SAAK,kBAAkB,OAAO,qBAAqB,aAC7C,IAAI,iBAAiB,MAAM,KAAK,aAAa,KAAK,KAAK,SAAS,OAAO,CAAC,IACxE;AACN,SAAK,iBAAiB;AAAA,MAAQ,KAAK;AAAA,MAC/B,EAAE,YAAY,MAAM,iBAAiB,CAAC,eAAe,EAAE;AAAA,IAAC;AAC5D,SAAK,KAAK,iBAAiB,aAAa,KAAK,aAAa,KAAK,IAAI,CAAC;AAKpE,SAAK,KAAK,iBAAiB,YAAY,KAAK,eAAe,KAAK,IAAI,CAAC;AACrE,SAAK,KAAK,iBAAiB,QAAQ,KAAK,WAAW,KAAK,IAAI,CAAC;AAC7D,SAAK,KAAK,iBAAiB,aAAa,KAAK,gBAAgB,KAAK,IAAI,CAAC;AAKvE,SAAK,KAAK,iBAAiB,WAAW,KAAK,cAAc,KAAK,IAAI,CAAC;AAAA,EACvE;AAAA,EAEA,SAAS;AACL,QAAI,KAAK,SAAU;AAKnB,UAAM,eAAe,oBAAI,IAAI;AAC7B,eAAW,CAAC,QAAQ,KAAK,KAAK,KAAK,WAAW,QAAQ,GAAG;AACrD,YAAM,OAAO,CAAC;AACd,YAAM,OAAO,iBAAiB,GAAG,EAAE,QAAQ,CAAC,OAAO;AAC/C,YAAI,GAAG,YAAY,KAAK,GAAG,aAAa,GAAG;AACvC,eAAK,KAAK,EAAE,IAAI,KAAK,GAAG,WAAW,MAAM,GAAG,WAAW,CAAC;AAAA,QAC5D;AAAA,MACJ,CAAC;AACD,mBAAa,IAAI,QAAQ,IAAI;AAC7B,YAAM,OAAO,OAAO;AAAA,IACxB;AAiBA,UAAM,SAAS,CAAC,GAAG,KAAK,KAAK,QAAQ,EAAE,OAAO,CAAC,OAC3C,GAAG,WAAW,SAAS,oBAAoB,KACxC,GAAG,WAAW,SAAS,8BAA8B,CAAC;AAC7D,eAAW,MAAM,OAAQ,IAAG,OAAO;AACnC,SAAK,KAAK,YAAY;AACtB,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,KAAK,QAAQ;AACd,YAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,YAAM,YAAY;AAClB,YAAM,cAAc;AACpB,WAAK,KAAK,YAAY,KAAK;AAC3B,WAAK,YAAY,oBAAI,IAAI,CAAC;AAG1B,iBAAW,MAAM,OAAQ,MAAK,KAAK,YAAY,EAAE;AACjD;AAAA,IACJ;AACA,UAAM,cAAc,oBAAI,IAAI;AAC5B,SAAK,OAAO,KAAK,QAAQ,KAAK,MAAM,WAAW;AAC/C,SAAK,YAAY,WAAW;AAI5B,eAAW,MAAM,OAAQ,MAAK,KAAK,YAAY,EAAE;AACjD,SAAK,oBAAoB;AAKzB,UAAM,UAAU,MAAM;AAClB,iBAAW,CAAC,QAAQ,IAAI,KAAK,cAAc;AACvC,YAAI,CAAC,KAAK,WAAW,IAAI,MAAM,EAAG;AAClC,mBAAW,EAAE,IAAI,KAAK,KAAK,KAAK,MAAM;AAClC,cAAI,GAAG,aAAa;AAChB,eAAG,YAAY;AACf,eAAG,aAAa;AAAA,UACpB;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AACA,YAAQ;AACR,0BAAsB,OAAO;AAC7B,QAAI;AAAE,WAAK,cAAc;AAAA,IAAG,SACrB,KAAK;AAAE,cAAQ,MAAM,uCAAuC,GAAG;AAAA,IAAG;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU;AACN,SAAK,iBAAiB,WAAW;AACjC,SAAK,kBAAkB;AAKvB,SAAK,qBAAqB;AAC1B,SAAK,WAAW;AAChB,SAAK,gBAAgB;AACrB,SAAK,YAAY,oBAAI,IAAI,CAAC;AAC1B,SAAK,WAAW;AAAA,EACpB;AAAA,EAEA,YAAY,SAAS;AACjB,eAAW,CAAC,QAAQ,KAAK,KAAK,CAAC,GAAG,KAAK,WAAW,QAAQ,CAAC,GAAG;AAC1D,UAAI,CAAC,QAAQ,IAAI,MAAM,GAAG;AACtB,YAAI;AAAE,gBAAM,SAAS,UAAU;AAAA,QAAG,SAAS,GAAG;AAAA,QAAC;AAM/C,aAAK,iBAAiB,KAAK;AAC3B,cAAM,OAAO,OAAO;AACpB,aAAK,WAAW,OAAO,MAAM;AAAA,MACjC;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,OAAO,QAAQ,UAAU,SAAS;AAC9B,UAAM,OAAO,KAAK,KAAK,IAAI,MAAM;AACjC,QAAI,CAAC,KAAM;AACX,QAAI,KAAK,SAAS,QAAQ;AACtB,cAAQ,IAAI,KAAK,EAAE;AACnB,YAAM,KAAK,KAAK,QAAQ,IAAI;AAC5B,eAAS,YAAY,EAAE;AACvB;AAAA,IACJ;AAEA,UAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,UAAM,YAAY,wBAAwB,KAAK,GAAG;AAClD,UAAM,QAAQ,UAAU,KAAK;AAC7B,aAAS,YAAY,KAAK;AAC1B,UAAM,QAAQ,KAAK,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS;AACrE,SAAK,SAAS,QAAQ,CAAC,KAAK,MAAM;AAC9B,YAAM,OAAO,SAAS,cAAc,KAAK;AACzC,WAAK,YAAY;AACjB,YAAM,QAAQ,KAAK,MAAM,CAAC,KAAK,KAAK;AACpC,WAAK,MAAM,OAAO,GAAG,IAAI,IAAI,IAAI;AACjC,YAAM,YAAY,IAAI;AACtB,WAAK,OAAO,KAAK,MAAM,OAAO;AAC9B,UAAI,IAAI,KAAK,SAAS,SAAS,GAAG;AAC9B,cAAM,WAAW,SAAS,cAAc,KAAK;AAC7C,iBAAS,YAAY,8BAA8B,KAAK,GAAG;AAC3D,iBAAS,QAAQ,UAAU,KAAK;AAChC,iBAAS,QAAQ,UAAU,OAAO,CAAC;AACnC,cAAM,YAAY,QAAQ;AAAA,MAC9B;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EAEA,QAAQ,MAAM;AAKV,UAAM,YAAa,MAAM,QAAQ,KAAK,IAAI,KAAK,KAAK,KAAK,SAAS,IAC5D,KAAK,KAAK,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,SAAS,GAAG,KAAK,gBAAgB,CAAC,CAAC,CAAC,IAC7E;AACN,UAAM,UAAU,KAAK,aAAa,IAAI;AAEtC,QAAI,QAAQ,KAAK,WAAW,IAAI,KAAK,EAAE;AACvC,QAAI,SAAS,MAAM,YAAY,SAAS;AACpC,YAAM,QAAQ,cAAc,KAAK,UAAU,KAAK,UAAU,KAAK,QAAQ,OAAO;AAK9E,qBAAe,MAAM,QAAQ,MAAM,MAAM,SAAS,KAAK,GAAG;AAC1D,aAAO,MAAM;AAAA,IACjB;AAEA,QAAI,OAAO;AAQP,WAAK,WAAW,OAAO,KAAK,EAAE;AAC9B,UAAI;AAAE,cAAM,SAAS,UAAU;AAAA,MAAG,SAAS,GAAG;AAAA,MAAC;AAC/C,WAAK,iBAAiB,KAAK;AAC3B,YAAM,OAAO,OAAO;AAAA,IACxB;AACA,UAAM,OAAO,SAAS,cAAc,KAAK;AACzC,SAAK,YAAY;AACjB,SAAK,QAAQ,SAAS,KAAK;AAO3B,UAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,WAAO,YAAY;AAOnB,UAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,SAAK,YAAY;AACjB,UAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,UAAM,YAAY;AAClB,UAAM,cAAc,KAAK,UAAU,KAAK,UAAU,KAAK,QAAQ,OAAO;AACtE,UAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,YAAQ,YAAY;AAOpB,UAAM,iBAAiB,SAAS,cAAc,MAAM;AACpD,mBAAe,YAAY;AAI3B,UAAM,UAAU,OAAO,KAAK,SAAS,QAAQ,EAAE,EAAE,WAAW,QAAQ;AAIpE,YAAQ,YAAY;AAAA,cACd,UAAU,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BASH;AAAA;AAAA;AAAA;AAAA;AAKlB,WAAO,OAAO,MAAM,OAAO,gBAAgB,OAAO;AAKlD,mBAAe,MAAM,MAAM,MAAM,KAAK,GAAG;AACzC,UAAM,OAAO,SAAS,cAAc,KAAK;AACzC,SAAK,YAAY;AACjB,SAAK,WAAW;AAMhB,UAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,WAAO,YAAY;AACnB,QAAI,CAAC,MAAM,QAAQ,KAAK,IAAI,KAAK,KAAK,KAAK,UAAU,GAAG;AACpD,aAAO,UAAU,IAAI,0BAA0B;AAAA,IACnD;AAIA,SAAK,OAAO,QAAQ,GAAI,KAAK,cAAc,QAAQ,CAAC,QAAQ,IAAI,IACd,CAAC,MAAM,MAAM,CAAE;AAEjE,SAAK,iBAAiB,aAAa,CAAC,MAAM;AACtC,UAAI,EAAE,OAAO,QAAQ,eAAe,EAAG;AACvC,WAAK,KAAK,MAAM,KAAK,EAAE;AACvB,WAAK,oBAAoB;AACzB,WAAK,cAAc,KAAK,EAAE;AAO1B,YAAM,MAAM,EAAE;AACd,YAAM,YAAY,IAAI;AAAA,QAClB;AAAA,MAA0E;AAC9E,UAAI,CAAC,aAAa,CAAC,KAAK,SAAS,SAAS,GAAG;AACzC,mBAAW,MAAM;AACb,gBAAM,KAAK,KAAK,cAAc,0BAA0B,KAC7C;AACX,cAAI,GAAG,YAAY,QAAQ,GAAG,WAAW,GAAI,IAAG,WAAW;AAC3D,aAAG,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,QACpC,GAAG,CAAC;AAAA,MACR;AAAA,IACJ,CAAC;AAeD,WAAO,iBAAiB,eAAe,CAAC,MAAM;AAC1C,UAAI,EAAE,WAAW,EAAG;AACpB,UAAI,EAAE,OAAO,QAAQ,eAAe,EAAG;AACvC,YAAM,SAAS,EAAE;AACjB,YAAM,SAAS,EAAE;AAejB,YAAM,OAAO,EAAE,QAAQ,QAAQ,GAAG,QAAQ,GAAG,QAAQ,QAAQ,MAAM;AACnE,WAAK,cAAc;AACnB,YAAM,SAAS,CAAC,SAAS;AACrB,aAAK,IAAI,KAAK;AACd,aAAK,IAAI,KAAK;AACd,YAAI,KAAK,OAAQ;AACjB,cAAM,OAAO,KAAK,UAAU;AAC5B,cAAM,WAAW,KAAK,IAAI,KAAK,UAAU,MAAM;AAC/C,YAAI,OAAO,gBAAgB,CAAC,gBAAgB,MAAM,QAAQ,EAAG;AAC7D,aAAK,SAAS;AAYd,eAAO,oBAAoB,eAAe,MAAM;AAChD,aAAK,IAAI,eAAe,KAAK,IAAI,SAAS;AAAA,MAC9C;AACA,YAAM,UAAU,MAAM;AAClB,YAAI,KAAK,gBAAgB,KAAM,MAAK,cAAc;AAClD,eAAO,oBAAoB,eAAe,MAAM;AAChD,eAAO,oBAAoB,aAAa,OAAO;AAC/C,eAAO,oBAAoB,iBAAiB,OAAO;AAAA,MACvD;AACA,aAAO,iBAAiB,eAAe,MAAM;AAC7C,aAAO,iBAAiB,aAAa,OAAO;AAC5C,aAAO,iBAAiB,iBAAiB,OAAO;AAAA,IACpD,CAAC;AA8CD,WAAO,iBAAiB,YAAY,CAAC,MAAM;AACvC,UAAI,EAAE,OAAO,QAAQ,eAAe,EAAG;AACvC,WAAK,IAAI,eAAe,KAAK,IAAI,SAAS;AAAA,IAC9C,CAAC;AAKD,WAAO,iBAAiB,eAAe,CAAC,MAAM;AAC1C,QAAE,eAAe;AACjB,WAAK,KAAK,MAAM,KAAK,EAAE;AACvB,WAAK,oBAAoB;AACzB,WAAK,cAAc,KAAK,EAAE;AAC1B,WAAK,IAAI,oBAAoB,KAAK,IAAI,EAAE,SAAS,EAAE,OAAO;AAAA,IAC9D,CAAC;AACD,YAAQ,iBAAiB,SAAS,CAAC,MAAM;AACrC,YAAM,MAAM,EAAE,OAAO,QAAQ,QAAQ;AACrC,UAAI,CAAC,IAAK;AACV,QAAE,gBAAgB;AAClB,WAAK,IAAI,eAAe,KAAK,IAAI,IAAI,QAAQ,MAAM;AAAA,IACvD,CAAC;AAOD,QAAI,UAAU,CAAC;AACf,QAAI,KAAK,SAAS;AACd,YAAM,UAAU,EAAE,GAAG,KAAK,KAAK,QAAQ,KAAK,GAAG;AAS/C,YAAM,cAAc,WAAW,SAAS,KAAK,QAAQ;AACrD,gBAAU,KAAK,QAAQ,MAAM,KAAK,QAAQ,MAAM,MAAM,aAAa,OAAO;AAC1E,UAAI,QAAQ,MAAO,OAAM,cAAc,QAAQ;AAK/C,qBAAe,MAAM,MAAM,SAAS,KAAK,GAAG;AAK5C,2BAAqB,gBAAgB,QAAQ,aAAa;AAa1D,6BAAuB,SAAS,QAAQ,MAAM;AAAA,IAClD,OAAO;AACH,WAAK,YAAY;AAAA,IACrB;AACA,YAAQ;AAAA,MAAE,QAAQ;AAAA,MAAM,QAAQ;AAAA,MAAM,UAAU;AAAA,MACtC,SAAS;AAAA,MAAO,QAAQ;AAAA,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAO9B,SAAS,KAAK,aAAa,IAAI;AAAA,MAAG,UAAU;AAAA;AAAA;AAAA;AAAA,MAI5C,UAAU;AAAA,MAAM,gBAAgB;AAAA,IAAK;AAC/C,SAAK,WAAW,IAAI,KAAK,IAAI,KAAK;AAGlC,SAAK,cAAc,MAAM,KAAK;AAC9B,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,MAAM;AACf,UAAM,kBAAkB,KAAK,QAAQ,CAAC,GAAG,IAAI,CAAC,MAC1C,GAAG,EAAE,IAAI,KAAK,KAAK,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,GAAG,IAAI,IAAI,KAAK,gBAAgB,CAAC;AACzF,WAAO,KAAK,UACN,GAAG,KAAK,QAAQ,IAAI,KAAK,KAAK,UAAU,KAAK,QAAQ,SAAS,CAAC,CAAC,CAAC,UAAU,cAAc,KACzF;AAAA,EACV;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,QAAQ;AACZ,UAAM,OAAO,KAAK,MAAM,MAAM,MAAM;AACpC,WAAQ,QAAQ,KAAK,SAAS,SAAU,KAAK,aAAa,IAAI,IAAI;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqDA,eAAe,QAAQ,WAAW,QAAW;AACzC,UAAM,QAAQ,KAAK,WAAW,IAAI,MAAM;AACxC,UAAM,OAAO,KAAK,MAAM,MAAM,MAAM;AACpC,QAAI,CAAC,SAAS,CAAC,QAAQ,KAAK,SAAS,OAAQ,QAAO;AACpD,QAAI,aAAa,UAAa,MAAM,YAAY,SAAU,QAAO;AACjE,UAAM,UAAU,KAAK,aAAa,IAAI;AACtC,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,aAAa,QAAQ;AACjB,UAAM,OAAO,oBAAoB,MAAM;AAGvC,QAAI,SAAS,KAAK,UAAW;AAC7B,SAAK,YAAY;AACjB,QAAI,KAAK,KAAK,WAAW,KAAK,KAAK,QAAQ,YAAY,MAAM;AACzD,WAAK,KAAK,QAAQ,UAAU;AAAA,IAChC;AACA,QAAI,KAAK,SAAU;AACnB,eAAW,CAAC,QAAQ,KAAK,KAAK,KAAK,YAAY;AAC3C,WAAK,aAAa,KAAK;AAGvB,WAAK,iBAAiB,KAAK;AAC3B,YAAM,SAAS,YAAY;AAC3B,YAAM,OAAO,KAAK,KAAK,IAAI,MAAM;AACjC,UAAI,KAAM,MAAK,cAAc,MAAM,KAAK;AAAA,IAC5C;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,OAAO;AAChB,UAAM,EAAE,QAAQ,QAAQ,SAAS,IAAI;AACrC,QAAI,CAAC,UAAU,CAAC,YAAY,CAAC,OAAQ;AACrC,QAAI,KAAK,cAAc,MAAO,QAAO,aAAa,UAAU,MAAM;AAAA,QAC7D,QAAO,YAAY,QAAQ;AAAA,EACpC;AAAA;AAAA;AAAA,EAIA,iBAAiB,OAAO;AACpB,QAAI,CAAC,OAAO,SAAU;AACtB,QAAI;AAAE,YAAM,SAAS,QAAQ;AAAA,IAAG,SAAS,KAAK;AAC1C,cAAQ,MAAM,kCAAkC,GAAG;AAAA,IACvD;AACA,UAAM,WAAW;AACjB,UAAM,iBAAiB;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,MAAM,OAAO;AAIvB,SAAK,sBAAsB,MAAM,KAAK;AACtC,UAAM,MAAM,MAAM;AAClB,QAAI,CAAC,IAAK;AACV,UAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC;AACrD,QAAI,UAAU,OAAO,yBAAyB,KAAK,cAAc,KAAK;AACtE,QAAI,KAAK,UAAU,GAAG;AAClB,UAAI,UAAU,IAAI,0BAA0B;AAC5C,WAAK,iBAAiB,KAAK;AAC3B,UAAI,YAAY;AAChB;AAAA,IACJ;AACA,QAAI,UAAU,OAAO,0BAA0B;AAC/C,QAAI,KAAK,cAAc,MAAO,MAAK,iBAAiB,MAAM,OAAO,IAAI;AAAA,QAChE,MAAK,oBAAoB,MAAM,OAAO,IAAI;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,sBAAsB,MAAM,OAAO;AAC/B,UAAM,SAAS,OAAO;AACtB,QAAI,CAAC,OAAQ;AACb,UAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC;AACrD,UAAM,UAAU,OAAO,KAAK,SAAS,QAAQ,EAAE,EAAE,WAAW,QAAQ;AACpE,UAAM,SAAS,KAAK,WAAW,KAAK,EAAE,GAAG,YAAY;AACrD,UAAM,KAAK,KAAK,WAAW,KAAK,CAAC,WAAW,CAAC;AAC7C,QAAI,GAAI,QAAO,aAAa,aAAa,MAAM;AAAA,QAC1C,QAAO,gBAAgB,WAAW;AACvC,QAAI,OAAO,kBAAmB;AAC9B,WAAO,oBAAoB;AAC3B,WAAO,iBAAiB,aAAa,CAAC,OAAO;AAMzC,UAAI,GAAG,QAAQ,UAAU,eAAe,GAAG;AAAE,WAAG,eAAe;AAAG;AAAA,MAAQ;AA+C1E,YAAM,OAAO,KAAK;AAClB,UAAI,QAAQ;AAAA,QAAgB,KAAK,IAAI,KAAK;AAAA,QACd,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM;AAAA,MAAC,GAAG;AACzD,WAAG,eAAe;AAClB;AAAA,MACJ;AAIA,YAAM,OAAO,KAAK,KAAK,IAAI,KAAK,EAAE;AAClC,WAAK,MAAM,QAAQ,CAAC,GAAG,WAAW,GAAG;AAAE,WAAG,eAAe;AAAG;AAAA,MAAQ;AACpE,WAAK,cAAc,IAAI,KAAK,IAAI,GAAG,QAAQ,EAAE,eAAe,KAAK,CAAC;AAAA,IACtE,CAAC;AACD,WAAO,iBAAiB,WAAW,MAAM,KAAK,cAAc,CAAC;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,iBAAiB,MAAM,OAAO,MAAM;AAChC,UAAM,MAAM,MAAM;AAClB,QAAI,CAAC,MAAM,UAAU;AACjB,UAAI,YAAY;AAChB,YAAM,OAAO,SAAS,cAAc,KAAK;AACzC,UAAI,YAAY,IAAI;AACpB,YAAM,QAAQ,IAAI,eAAe;AACjC,YAAM,MAAM,MAAM,KAAK,iBAAiB,KAAK,EAAE,CAAC;AAChD,YAAM,WAAW;AACjB,YAAM,iBAAiB;AAevB,WAAK,iBAAiB,eAAe,CAAC,OAAO;AACzC,cAAM,QAAQ,GAAG,OAAO,UAAU,MAAM;AACxC,YAAI,CAAC,MAAO;AACZ,WAAG,eAAe;AAClB,WAAG,gBAAgB;AACnB,cAAM,MAAM,KAAK,aAAa,MAAM,KAAK;AACzC,YAAI,MAAM,EAAG;AACb,aAAK,IAAI;AAAA,UAAkB,KAAK;AAAA,UAAI;AAAA,UAChC,EAAE,KAAK,GAAG,GAAG,SAAS,GAAG,GAAG,QAAQ;AAAA,QAAC;AAAA,MAC7C,GAAG,IAAI;AAAA,IACX;AACA,UAAM,YAAY,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,KAAK,gBAAgB,CAAC,CAAC;AAC/E,UAAM,SAAS;AAAA,MACX,KAAK,IAAI,CAAC,GAAG,OAAO;AAAA,QAChB,UAAU,QAAQ,CAAC;AAAA,QACnB,OAAO,EAAE,SAAS,EAAE,QAAQ;AAAA;AAAA;AAAA;AAAA,QAI5B,UAAU,EAAE,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,QAKpB,SAAS,CAAC,CAAC,EAAE;AAAA,MACjB,EAAE;AAAA,MACF,QAAQ,SAAS;AAAA,IAAC;AACtB,SAAK,cAAc,MAAM,gBAAgB,IAAI;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,cAAc,QAAQ,MAAM;AACxB,QAAI,CAAC,OAAQ;AACb,UAAM,MAAM,OAAO,iBAAiB,MAAM;AAC1C,QAAI,QAAQ,CAAC,IAAI,MAAM;AACnB,YAAM,OAAO,KAAK,CAAC;AACnB,UAAI,CAAC,KAAM;AACX,SAAG,QAAQ,KAAK,SAAS,KAAK,QAAQ;AACtC,YAAM,OAAO,KAAK,OAAO,KAAK,KAAK,UAAU,OAAO,KAAK,IAAI,GAAG,OAAO;AACvE,YAAM,QAAQ,GAAG,cAAc,WAAW;AAC1C,UAAI,SAAS,KAAM,OAAM,cAAc;AAAA,eAC9B,SAAS,CAAC,KAAM,OAAM,SAAS;AAAA,IAC5C,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,QAAQ,OAAO;AACxB,WAAO,MAAM,UAAU,QAAQ,KAAK,OAAO,iBAAiB,MAAM,GAAG,KAAK;AAAA,EAC9E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BA,iBAAiB,QAAQ;AACrB,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA,MAKH,aAAa,CAAC,IAAI,KAAK,SAAS;AAC5B,cAAM,MAAM,aAAa,GAAG;AAC5B,YAAI,MAAM,EAAG;AACb,aAAK,cAAc,IAAI,QAAQ,KAAK,QAAQ,IAAI;AAAA,MACpD;AAAA;AAAA;AAAA;AAAA,MAIA,kBAAkB,CAAC,aAAa;AAAA,MAChC,qBAAqB,MAAM;AACvB,cAAM,MAAM,KAAK;AACjB,aAAK,cAAc;AACnB,YAAI,CAAC,OAAO,IAAI,WAAW,OAAQ;AACnC,aAAK,IAAI,kBAAkB,IAAI,QAAQ,aAAa;AAAA,UAChD,KAAK,IAAI;AAAA,UACT,QAAQ,EAAE,QAAQ,MAAM,OAAO,OAAO,GAAG;AAAA,QAC7C,CAAC;AAAA,MACL;AAAA,MACA,YAAY,CAAC,QAAQ;AACjB,cAAM,MAAM,aAAa,GAAG;AAC5B,YAAI,OAAO,EAAG,MAAK,IAAI,kBAAkB,QAAQ,UAAU,EAAE,IAAI,CAAC;AAAA,MACtE;AAAA,MACA,SAAS,CAAC,QAAQ;AACd,cAAM,MAAM,aAAa,GAAG;AAC5B,YAAI,OAAO,EAAG,MAAK,IAAI,kBAAkB,QAAQ,SAAS,EAAE,IAAI,CAAC;AAAA,MACrE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,WAAW,CAAC,UAAU;AAClB,cAAM,OAAO,KAAK,KAAK,IAAI,MAAM;AACjC,cAAM,SAAS,MAAM,QAAQ,CAAC,GAAG;AACjC,YAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,MAAO;AACrD,YAAI,OAAO;AACX,YAAI,KAAK;AACT,YAAI,WAAW;AACf,cAAM,QAAQ,CAAC,KAAK,WAAW;AAC3B,gBAAM,SAAS,aAAa,GAAG;AAC/B,cAAI,SAAS,EAAG;AAChB,gBAAM,YAAY,KAAK,IAAI,SAAS,MAAM;AAC1C,cAAI,YAAY,UAAU;AACtB,uBAAW;AACX,mBAAO;AACP,iBAAK;AAAA,UACT;AAAA,QACJ,CAAC;AACD,YAAI,OAAO,KAAK,SAAS,GAAI;AAC7B,aAAK,IAAI,kBAAkB,QAAQ,QAAQ,EAAE,MAAM,GAAG,CAAC;AAAA,MAC3D;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA,EAIA,oBAAoB,MAAM,OAAO,MAAM;AACnC,UAAM,MAAM,MAAM;AAClB,UAAM,YAAY,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,KAAK,gBAAgB,CAAC,CAAC;AAC/E,QAAI,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAON,KAAK,IAAI,CAAC,GAAG,MAAM;AAAA,8CACS,MAAM,YAAY,uBAAuB,EAAE;AAAA,mDACtC,CAAC;AAAA,iCACnBC,MAAK,EAAE,SAAS,EAAE,IAAI,CAAC;AAAA;AAAA,4DAEIA,MAAK,EAAE,SAAS,EAAE,IAAI,CAAC;AAAA;AAAA,wEAEX,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAMxD,EAAE,KAAK,EAAE,CAAC;AAAA;AAAA;AAMnB,QAAI,iBAAiB,gBAAgB,EAAE,QAAQ,CAAC,OAAO;AACnD,SAAG,iBAAiB,SAAS,CAAC,OAAO;AACjC,YAAI,GAAG,OAAO,QAAQ,2BAA2B,EAAG;AACpD,cAAM,MAAM,OAAO,GAAG,QAAQ,MAAM;AACpC,aAAK,IAAI,kBAAkB,KAAK,IAAI,UAAU,EAAE,IAAI,CAAC;AAAA,MACzD,CAAC;AAID,SAAG,iBAAiB,eAAe,CAAC,OAAO;AACvC,WAAG,eAAe;AAClB,WAAG,gBAAgB;AACnB,cAAM,MAAM,OAAO,GAAG,QAAQ,MAAM;AACpC,aAAK,IAAI;AAAA,UAAkB,KAAK;AAAA,UAAI;AAAA,UAChC,EAAE,KAAK,GAAG,GAAG,SAAS,GAAG,GAAG,QAAQ;AAAA,QAAC;AAAA,MAC7C,CAAC;AAAA,IACL,CAAC;AACD,QAAI,iBAAiB,2BAA2B,EAAE,QAAQ,CAAC,QAAQ;AAC/D,UAAI,iBAAiB,SAAS,CAAC,OAAO;AAClC,WAAG,eAAe;AAClB,WAAG,gBAAgB;AACnB,cAAM,MAAM,OAAO,IAAI,QAAQ,MAAM;AACrC,aAAK,IAAI,kBAAkB,KAAK,IAAI,SAAS,EAAE,IAAI,CAAC;AAAA,MACxD,CAAC;AAAA,IACL,CAAC;AACD,QAAI,cAAc,0BAA0B,GACtC,iBAAiB,SAAS,CAAC,OAAO;AAChC,SAAG,eAAe;AAClB,YAAM,IAAI,GAAG,cAAc,sBAAsB;AACjD,WAAK,IAAI;AAAA,QAAkB,KAAK;AAAA,QAAI;AAAA,QAChC,EAAE,GAAG,EAAE,MAAM,GAAG,EAAE,IAAI;AAAA,MAAC;AAAA,IAC/B,CAAC;AAaL,QAAI,cAAc;AAClB,QAAI,iBAAiB,gBAAgB,EAAE,QAAQ,CAAC,OAAO;AACnD,SAAG,iBAAiB,aAAa,CAAC,OAAO;AACrC,sBAAc,OAAO,GAAG,QAAQ,MAAM;AACtC,WAAG,aAAa,gBAAgB;AAGhC,YAAI;AAAE,aAAG,aAAa,QAAQ,cAAc,OAAO,WAAW,CAAC;AAAA,QAAG,QAC5D;AAAA,QAAC;AACP,aAAK,cAAc,IAAI,KAAK,IAAI,aAAa,EAAE;AAAA,MACnD,CAAC;AAMD,SAAG,iBAAiB,WAAW,MAAM;AAAE,sBAAc;AAAM,aAAK,cAAc;AAAA,MAAG,CAAC;AAClF,SAAG,iBAAiB,YAAY,CAAC,OAAO;AACpC,YAAI,eAAe,KAAM;AACzB,WAAG,eAAe;AAClB,WAAG,aAAa,aAAa;AAAA,MACjC,CAAC;AACD,SAAG,iBAAiB,QAAQ,CAAC,OAAO;AAChC,YAAI,eAAe,KAAM;AACzB,WAAG,eAAe;AAClB,cAAM,QAAQ,OAAO,GAAG,QAAQ,MAAM;AACtC,YAAI,UAAU,aAAa;AACvB,eAAK,IAAI;AAAA,YAAkB,KAAK;AAAA,YAAI;AAAA,YAChC,EAAE,MAAM,aAAa,IAAI,MAAM;AAAA,UAAC;AAAA,QACxC;AACA,sBAAc;AAAA,MAClB,CAAC;AAAA,IACL,CAAC;AAuBD,UAAM,UAAU,CAAC,OAAO,CAAC,CAAC,KAAK,YACxB,KAAK,SAAS,WAAW,KAAK,MAC9B,eAAe,EAAE;AACxB,QAAI,IAAI,kBAAmB;AAC3B,QAAI,oBAAoB;AACxB,QAAI,iBAAiB,YAAY,CAAC,OAAO;AACrC,UAAI,CAAC,QAAQ,EAAE,EAAG;AAClB,SAAG,eAAe;AAClB,UAAI;AAAE,WAAG,aAAa,aAAa;AAAA,MAAQ,QAAQ;AAAA,MAAC;AAOpD,WAAK,mBAAmB;AACxB,UAAI,UAAU,IAAI,+BAA+B;AAAA,IACrD,CAAC;AACD,QAAI,iBAAiB,aAAa,CAAC,OAAO;AACtC,UAAI,CAAC,IAAI,SAAS,GAAG,aAAa,GAAG;AACjC,YAAI,UAAU,OAAO,+BAA+B;AAAA,MACxD;AAAA,IACJ,CAAC;AACD,QAAI,iBAAiB,QAAQ,CAAC,OAAO;AACjC,UAAI,CAAC,QAAQ,EAAE,EAAG;AAClB,SAAG,eAAe;AAClB,UAAI,UAAU,OAAO,+BAA+B;AACpD,YAAM,MAAM,KAAK;AACjB,WAAK,cAAc;AAInB,WAAK,IAAI,kBAAkB,IAAI,QAAQ,aAAa;AAAA,QAChD,KAAK,IAAI;AAAA,QACT,QAAQ,EAAE,QAAQ,KAAK,IAAI,MAAM,OAAO,OAAO,GAAG;AAAA,MACtD,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,cAAc,IAAI,QAAQ,KAAK,IAAI,EAAE,gBAAgB,MAAM,IAAI,CAAC,GAAG;AAC/D,SAAK,WAAW,EAAE,QAAQ,KAAK,IAAI,MAAM,KAAK;AAC9C,SAAK,gBAAgB;AACrB,QAAI;AACA,SAAG,aAAa,gBAAgB;AAChC,SAAG,aAAa,QAAQ,eAAe,GAAG;AAC1C,UAAI,cAAe,IAAG,aAAa,QAAQ,cAAc,OAAO,GAAG,CAAC;AAAA,IACxE,QAAQ;AAAA,IAA+D;AACvE,QAAI,WAAW,IAAI,UAAU;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,eAAe,GAAG;AACd,UAAM,MAAM,KAAK;AACjB,QAAI,CAAC,OAAO,CAAC,eAAe,CAAC,EAAG;AAYhC,QAAI,EAAE,QAAQ,UAAU,mBAAmB,GAAG;AAAE,WAAK,mBAAmB;AAAG;AAAA,IAAQ;AACnF,UAAM,QAAQ,KAAK,IAAI,IAAI,eAAe,GAAG,EAAE,cAAc,IAAI,OAAO,CAAC,KAAK;AAC9E,SAAK,gBAAgB;AACrB,QAAI,CAAC,OAAO;AAAE,WAAK,kBAAkB;AAAG;AAAA,IAAQ;AAChD,MAAE,eAAe;AACjB,QAAI;AAAE,QAAE,aAAa,aAAa;AAAA,IAAQ,QAAQ;AAAA,IAAC;AACnD,eAAW,CAAC,QAAQ,KAAK,KAAK,KAAK,YAAY;AAC3C,YAAM,OAAO,UAAU,OAAO,yBAAyB,WAAW,MAAM,MAAM;AAAA,IAClF;AACA,SAAK,oBAAoB,MAAM,IAAI;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAW,GAAG;AACV,UAAM,MAAM,KAAK;AACjB,UAAM,QAAQ,KAAK;AACnB,QAAI,CAAC,OAAO,CAAC,eAAe,CAAC,EAAG;AAChC,QAAI,EAAE,QAAQ,UAAU,mBAAmB,EAAG;AAC9C,MAAE,eAAe;AACjB,SAAK,cAAc;AACnB,QAAI,CAAC,MAAO;AACZ,SAAK,IAAI,kBAAkB,IAAI,QAAQ,aAAa;AAAA,MAChD,KAAK,IAAI;AAAA,MACT,QAAQ;AAAA,QACJ,QAAQ,MAAM;AAAA,QACd,MAAM,MAAM;AAAA;AAAA;AAAA;AAAA,QAIZ,KAAM,MAAM,SAAS,UAAU,MAAM,SAAS,UAAW,MAAM;AAAA,QAC/D,QAAS,MAAM,SAAS,UAAU,MAAM,SAAS;AAAA,QACjD,OAAO;AAAA,MACX;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,GAAG;AACf,QAAI,CAAC,KAAK,SAAU;AACpB,QAAI,KAAK,KAAK,SAAS,EAAE,aAAa,EAAG;AACzC,SAAK,kBAAkB;AACvB,SAAK,gBAAgB;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB;AACZ,SAAK,UAAU,IAAI,WAAW,OAAO,UAAU;AAC/C,SAAK,WAAW;AAChB,SAAK,gBAAgB;AACrB,SAAK,kBAAkB;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,qBAAqB;AACjB,eAAW,CAAC,EAAE,KAAK,KAAK,KAAK,YAAY;AACrC,YAAM,OAAO,UAAU,OAAO,uBAAuB;AAAA,IACzD;AACA,SAAK,qBAAqB;AAAA,EAC9B;AAAA;AAAA;AAAA,EAIA,oBAAoB;AAChB,SAAK,mBAAmB;AACxB,eAAW,CAAC,EAAE,KAAK,KAAK,KAAK,YAAY;AACrC,YAAM,UAAU,WAAW,OAAO,+BAA+B;AAAA,IACrE;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,oBAAoB,MAAM;AACtB,QAAI,CAAC,MAAM;AAAE,WAAK,qBAAqB;AAAG;AAAA,IAAQ;AAClD,QAAI,CAAC,KAAK,mBAAmB;AACzB,YAAMC,MAAK,SAAS,cAAc,KAAK;AACvC,MAAAA,IAAG,YAAY;AACf,MAAAA,IAAG,aAAa,eAAe,MAAM;AACrC,WAAK,oBAAoBA;AAAA,IAC7B;AACA,UAAM,KAAK,KAAK;AAChB,WAAO,OAAO,GAAG,OAAO;AAAA,MACpB,MAAM,GAAG,KAAK,QAAQ,KAAK,CAAC;AAAA,MAAM,KAAK,GAAG,KAAK,OAAO,KAAK,CAAC;AAAA,MAC5D,OAAO,GAAG,KAAK,KAAK;AAAA,MAAM,QAAQ,GAAG,KAAK,MAAM;AAAA,IACpD,CAAC;AACD,QAAI,GAAG,eAAe,SAAS,KAAM,UAAS,KAAK,YAAY,EAAE;AAAA,EACrE;AAAA,EAEA,uBAAuB;AACnB,SAAK,mBAAmB,OAAO;AAAA,EACnC;AAAA,EAEA,sBAAsB;AAClB,UAAM,UAAU,KAAK,KAAK;AAC1B,eAAW,CAAC,QAAQ,KAAK,KAAK,KAAK,YAAY;AAC3C,YAAM,OAAO,UAAU,OAAO,qBAAqB,WAAW,OAAO;AAAA,IACzE;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,QAAQ;AACX,WAAO,KAAK,WAAW,IAAI,MAAM,GAAG,UAAU;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmCA,WAAW,QAAQ;AACf,WAAO,KAAK,WAAW,IAAI,MAAM,GAAG,SAAS,UAAU;AAAA,EAC3D;AAAA;AAAA,EAGA,aAAa,GAAG;AACZ,UAAM,WAAW,EAAE,OAAO,QAAQ,eAAe;AACjD,QAAI,CAAC,SAAU;AACf,MAAE,eAAe;AACjB,UAAM,UAAU,SAAS,QAAQ;AACjC,UAAM,UAAU,OAAO,SAAS,QAAQ,OAAO;AAC/C,UAAM,QAAQ,KAAK,KAAK,IAAI,OAAO;AACnC,QAAI,CAAC,MAAO;AACZ,UAAM,cAAc,SAAS;AAC7B,UAAM,aAAa,MAAM,QAAQ;AACjC,UAAM,OAAO,YAAY,sBAAsB;AAC/C,UAAM,UAAU,aAAa,KAAK,QAAQ,KAAK;AAC/C,SAAK,QAAQ;AAAA,MACT;AAAA,MAAS;AAAA,MAAS;AAAA,MAAY;AAAA,MAC9B,YAAY,MAAM,MAAM,MAAM;AAAA,MAC9B,UAAU,aAAa,EAAE,UAAU,EAAE;AAAA,MACrC;AAAA,IACJ;AACA,aAAS,KAAK,UAAU,IAAI,cAAc;AAC1C,UAAM,OAAO,CAAC,OAAO,KAAK,aAAa,EAAE;AACzC,UAAM,KAAK,CAAC,OAAO;AACf,WAAK,WAAW,EAAE;AAClB,eAAS,oBAAoB,aAAa,IAAI;AAC9C,eAAS,oBAAoB,WAAW,EAAE;AAAA,IAC9C;AACA,aAAS,iBAAiB,aAAa,IAAI;AAC3C,aAAS,iBAAiB,WAAW,EAAE;AAAA,EAC3C;AAAA,EAEA,aAAa,GAAG;AACZ,QAAI,CAAC,KAAK,MAAO;AACjB,UAAM,EAAE,YAAY,SAAS,YAAY,UAAU,SAAS,MAAM,IAAI,KAAK;AAC3E,UAAM,SAAS,aAAa,EAAE,UAAU,EAAE,WAAW;AACrD,UAAM,YAAY,WAAW,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AACtD,UAAM,KAAK,UAAU,eAAe,MAAM,SAAS,SAAS;AAC5D,QAAI,MAAM,EAAG;AACb,UAAM,YAAa,QAAQ,KAAM;AACjC,UAAM,UAAU,YAAY;AAC5B,UAAM,WAAW,WAAW,MAAM;AAClC,aAAS,OAAO,IAAI,KAAK,IAAI,SAAS,WAAW,OAAO,IAAI,SAAS;AACrE,aAAS,UAAU,CAAC,IAAI,KAAK,IAAI,SAAS,WAAW,UAAU,CAAC,IAAI,SAAS;AAE7E,UAAM,MAAM,SAAS,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AAC9C,UAAM,QAAQ,YAAY;AAC1B,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,IAAK,UAAS,CAAC,KAAK;AACzD,SAAK,KAAK,cAAc,MAAM,IAAI,QAAQ;AAE1C,UAAM,cAAc,SAAS,cAAc,6BAA6B,MAAM,EAAE,IAAI;AACpF,QAAI,CAAC,YAAa;AAClB,UAAM,QAAQ,YAAY,iBAAiB,oBAAoB;AAC/D,UAAM,QAAQ,CAAC,MAAM,MAAM;AACvB,YAAM,OAAO,SAAS,CAAC,IAAI;AAC3B,WAAK,MAAM,OAAO,GAAG,IAAI,IAAI,IAAI;AAAA,IACrC,CAAC;AAAA,EACL;AAAA,EAEA,aAAa;AACT,SAAK,QAAQ;AACb,aAAS,KAAK,UAAU,OAAO,cAAc;AAAA,EACjD;AACJ;AAGA,SAASD,MAAK,GAAG;AACb,SAAO,OAAO,KAAK,EAAE,EAAE,QAAQ,YAAY,CAAC,OAAO;AAAA,IAC/C,KAAK;AAAA,IAAS,KAAK;AAAA,IAAQ,KAAK;AAAA,IAAQ,KAAK;AAAA,IAAU,KAAK;AAAA,EAChE,GAAE,CAAC,CAAE;AACT;AAoBA,SAAS,eAAe,QAAQ,MAAM,SAAS,KAAK;AAChD,MAAI,CAAC,OAAQ;AACb,QAAM,OAAO,MAAM,SAAS;AAC5B,QAAM,OAAO,SAAS,SAAS,OAAO,KAAK,UAAU,OAAO,IAAI,GAAG,OAAO;AAC1E,SAAO,cAAc,QAAQ;AAC7B,SAAO,SAAS,CAAC;AACrB;AAcA,SAAS,qBAAqB,QAAQ,OAAO;AACzC,SAAO,YAAY;AACnB,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;AAC7C,WAAO,SAAS;AAChB;AAAA,EACJ;AACA,SAAO,SAAS;AAChB,aAAW,QAAQ,OAAO;AACtB,QAAI,CAAC,QAAQ,CAAC,KAAK,KAAM;AACzB,UAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,QAAI,OAAO;AACX,QAAI,YAAY;AAChB,QAAI,QAAQ,KAAK,SAAS;AAC1B,QAAI,aAAa,cAAc,KAAK,SAAS,EAAE;AAC/C,QAAI,YACA,2CAA2C,KAAK,IAAI;AAIxD,QAAI,iBAAiB,SAAS,CAAC,MAAM;AACjC,QAAE,gBAAgB;AAClB,UAAI;AAAE,aAAK,UAAU;AAAA,MAAG,SAAS,KAAK;AAAE,gBAAQ,MAAM,uBAAuB,GAAG;AAAA,MAAG;AAAA,IACvF,CAAC;AACD,WAAO,YAAY,GAAG;AAAA,EAC1B;AACJ;AAqBA,SAAS,uBAAuB,QAAQ,QAAQ;AAC5C,MAAI,CAAC,OAAQ;AACb,aAAW,CAAC,QAAQ,IAAI,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAM,MAAM,OAAO,cAAc,iBAAiB,MAAM,IAAI;AAC5D,QAAI,CAAC,IAAK;AACV,QAAI,SAAS,OAAO;AAAE,UAAI,OAAO;AAAG;AAAA,IAAU;AAC9C,QAAI,QAAQ,OAAO,SAAS,YAAY,KAAK,UAAU;AACnD,UAAI,WAAW;AACf,UAAI,aAAa,iBAAiB,MAAM;AACxC,UAAI,UAAU,IAAI,yBAAyB;AAC3C,UAAI,KAAK,MAAO,KAAI,QAAQ,KAAK;AAAA,IACrC;AAAA,EACJ;AACJ;;;AC5uDO,SAAS,OAAO,KAAK;AAAE,SAAO,iBAAiB,GAAG;AAAI;AAGtD,SAAS,YAAY,KAAK;AAC7B,QAAM,IAAI,OAAO,OAAO,OAAO,EAAE,EAAE,QAAQ,kBAAkB,EAAE,CAAC;AAChE,SAAO,OAAO,UAAU,CAAC,KAAK,KAAK,IAAI,IAAI;AAC/C;AAYO,SAAS,cAAc,OAAO,OAAO;AACxC,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,MAAO,QAAO;AAC5D,MAAI,OAAO;AACX,MAAI,KAAK;AACT,MAAI,WAAW;AACf,QAAM,QAAQ,CAAC,KAAK,WAAW;AAC3B,UAAM,SAAS,YAAY,GAAG;AAC9B,QAAI,SAAS,EAAG;AAChB,UAAM,YAAY,KAAK,IAAI,SAAS,MAAM;AAC1C,QAAI,YAAY,UAAU;AAAE,iBAAW;AAAW,aAAO;AAAQ,WAAK;AAAA,IAAQ;AAAA,EAClF,CAAC;AACD,MAAI,OAAO,KAAK,SAAS,GAAI,QAAO;AACpC,SAAO,EAAE,MAAM,GAAG;AACtB;AAsBO,SAAS,eAAe,EAAE,QAAQ,WAAW,MAAM,SAAS,GAAG;AAClE,QAAM,OAAO,SAAS,cAAc,KAAK;AACzC,SAAO,YAAY,IAAI;AACvB,QAAM,QAAQ,IAAI,eAAe;AACjC,MAAI,OAAO,CAAC;AAEZ,QAAM,MAAM,MAAM;AAAA,IACd,YAAY,CAAC,QAAQ;AACjB,YAAM,MAAM,YAAY,GAAG;AAC3B,UAAI,OAAO,EAAG,YAAW,UAAU,EAAE,IAAI,CAAC;AAAA,IAC9C;AAAA,IACA,SAAS,CAAC,QAAQ;AACd,YAAM,MAAM,YAAY,GAAG;AAC3B,UAAI,OAAO,EAAG,YAAW,SAAS,EAAE,IAAI,CAAC;AAAA,IAC7C;AAAA,IACA,WAAW,CAAC,UAAU;AAClB,YAAM,OAAO,cAAc,OAAO,KAAK,MAAM;AAC7C,UAAI,KAAM,YAAW,QAAQ,IAAI;AAAA,IACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMJ,CAAC;AAGD,QAAM,gBAAgB,CAAC,OAAO;AAC1B,UAAM,QAAQ,GAAG,OAAO,UAAU,MAAM;AACxC,QAAI,CAAC,MAAO;AACZ,OAAG,eAAe;AAClB,OAAG,gBAAgB;AACnB,UAAM,MAAM,MAAM,UAAU,QAAQ,KAAK,KAAK,iBAAiB,MAAM,GAAG,KAAK;AAC7E,QAAI,MAAM,EAAG;AACb,eAAW,QAAQ,EAAE,KAAK,GAAG,GAAG,SAAS,GAAG,GAAG,QAAQ,CAAC;AAAA,EAC5D;AACA,OAAK,iBAAiB,eAAe,eAAe,IAAI;AAGxD,QAAM,UAAU,MAAM;AAClB,SAAK,iBAAiB,MAAM,EAAE,QAAQ,CAAC,IAAI,MAAM;AAC7C,YAAM,OAAO,KAAK,CAAC;AACnB,UAAI,CAAC,KAAM;AACX,SAAG,QAAQ,KAAK,SAAS,KAAK,QAAQ;AACtC,YAAM,OAAO,KAAK,OAAO,UAAU,OAAO,KAAK,IAAI,GAAG,OAAO;AAC7D,YAAM,QAAQ,GAAG,cAAc,WAAW;AAC1C,UAAI,CAAC,MAAO;AACZ,UAAI,MAAM;AAAE,cAAM,cAAc;AAAM,cAAM,SAAS;AAAA,MAAO,MACvD,OAAM,SAAS;AAAA,IACxB,CAAC;AAAA,EACL;AAEA,SAAO;AAAA;AAAA,IAEH,OAAO,MAAM,WAAW;AACpB,aAAO,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;AACrC,YAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,aAAa,CAAC,CAAC;AACpE,YAAM;AAAA,QACF,KAAK,IAAI,CAAC,GAAG,OAAO;AAAA,UAChB,UAAU,OAAO,CAAC;AAAA,UAClB,OAAO,EAAE,SAAS,EAAE,QAAQ;AAAA,UAC5B,UAAU,EAAE,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,UAKpB,SAAS,CAAC,CAAC,EAAE;AAAA,QACjB,EAAE;AAAA,QACF,OAAO,MAAM;AAAA,MAAC;AAClB,cAAQ;AAAA,IACZ;AAAA,IACA,UAAU;AACN,WAAK,oBAAoB,eAAe,eAAe,IAAI;AAC3D,UAAI;AAAE,cAAM,QAAQ;AAAA,MAAG,SAAS,KAAK;AACjC,gBAAQ,MAAM,6BAA6B,GAAG;AAAA,MAClD;AACA,WAAK,OAAO;AAAA,IAChB;AAAA,EACJ;AACJ;;;AC9JA,IAAM,cAAc,oBAAI,IAAI,CAAC,cAAc,eAAe,cAAc,CAAC;AAEzE,IAAM,eAAe;AAAA,EACjB,MAAQ;AAAA,EACR,OAAQ;AAAA,EACR,QAAQ;AACZ;AAEO,IAAM,gBAAN,MAAM,eAAc;AAAA,EACvB,YAAY;AAAA,IAAE;AAAA,IAAQ;AAAA,IAAK;AAAA,IAAK;AAAA,IAAU;AAAA,IAAU;AAAA,IAAM;AAAA,IAAU;AAAA,IAAQ;AAAA,IAC9D,gBAAgB;AAAA,IAAM,gBAAgB;AAAA,IACtC,iBAAiB;AAAA,IAAO,YAAY;AAAA,EAAK,GAAG;AACtD,QAAI,CAAC,SAAU,OAAM,IAAI,MAAM,uCAAuC;AACtE,QAAI,CAAC,WAAW,OAAO,QAAQ,UAAU,YAAY;AACjD,YAAM,IAAI,MAAM,6FACqC;AAAA,IACzD;AACA,SAAK,SAAS;AAId,SAAK,UAAU;AACf,SAAK,MAAM,OAAO;AAGlB,SAAK,OAAO,QAAQ;AAGpB,SAAK,WAAW;AAEhB,SAAK,SAAS,UAAU,CAAC;AACzB,SAAK,MAAM,OAAO,CAAC;AACnB,SAAK,WAAW,YAAY;AAC5B,SAAK,WAAW,aAAa,MAAM;AAAA,IAAC;AAMpC,SAAK,YAAY,MAAM;AACnB,YAAM,OAAO,KAAK,SAAS;AAC3B,aAAO;AAAA,QACH,SAAS,EAAE,MAAM,OAAO,CAAC,EAAE;AAAA,QAC3B,OAAO,KAAK,SAAS,KAAK,IAAI,GAAG,SAAS;AAAA,MAC9C;AAAA,IACJ;AAIA,SAAK,gBAAgB,iBAAiB;AAKtC,SAAK,gBAAgB,CAAC,CAAC;AAKvB,SAAK,iBAAiB,CAAC,CAAC;AACxB,SAAK,WAAW;AAChB,SAAK,WAAW,IAAI,eAAe;AAAA,MAC/B,MAAM,KAAK;AAAA,MAAW,eAAe,KAAK;AAAA,IAAc,CAAC;AAC7D,SAAK,WAAW,IAAI,aAAa;AAAA,MAC7B,MAAM;AAAA,MACN,MAAM,KAAK,SAAS,OAAO,EAAE;AAAA,MAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQA;AAAA,MACA,KAAK;AAAA,QACD,GAAG,KAAK;AAAA,QACR,IAAI;AAAA,QACJ,cAAc,CAAC,QAAQ,WAAW,KAAK,YAAY,QAAQ,MAAM;AAAA,QACjE,iBAAiB,CAAC,QAAQ,QAAQ,SAC9B,KAAK,eAAe,QAAQ,QAAQ,IAAI;AAAA,MAChD;AAAA,MACA,eAAe,MAAM,KAAK,cAAc;AAAA,MACxC,eAAe,MAAM,KAAK,wBAAwB;AAAA,IACtD,CAAC;AACD,SAAK,gBAAgB;AAGrB,SAAK,gBAAgB,oBAAI,IAAI;AAO7B,SAAK,YAAY,sBAAsB,IAAI;AAO3C,UAAM,eAAe,KAAK,OAAO;AACjC,QAAI,cAAc;AACd,WAAK,UAAU,KAAK,cAAc,CAAC,EAAE,MAAM,UAAU,MAAM,IAAI,CAAC,MAAM;AAClE,YAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,MAAO;AAClC,YAAI,UAAU;AACd,mBAAW,KAAK,KAAK,SAAS,UAAU;AACpC,cAAI,EAAE,KAAK,qBAAqB,MAAM,UAAU,KAAK,GAAG;AACpD,sBAAU;AAAA,UACd;AAAA,QACJ;AACA,YAAI,SAAS;AACT,eAAK,SAAS,OAAO;AACrB,eAAK,SAAS;AAAA,QAClB;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,aAAa,QAAQ;AACjB,SAAK,SAAS,aAAa,MAAM;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,SAAS,MAAM;AACzB,QAAI;AAAE,WAAK,SAAS,MAAM;AAAA,IAAG,SAAS,KAAK;AAAE,cAAQ,MAAM,uBAAuB,GAAG;AAAA,IAAG;AACxF,QAAI;AAAE,WAAK,UAAU,OAAO,cAAc,EAAE,QAAQ,IAAI,KAAK,CAAC;AAAA,IAAG,SAC1D,KAAK;AAAE,cAAQ,KAAK,0BAA0B,GAAG;AAAA,IAAG;AAI3D,QAAI;AACA,YAAM,OAAO,KAAK,MAAM;AACxB,YAAM,KAAK,KAAK,cAAc;AAC9B,YAAM,OAAO,KAAK,KAAK,IAAI,EAAE,IAAI;AACjC,YAAM,OAAO,MAAM,SAAS;AAC5B,UAAI,QAAQ,SAAS,sBAAsB;AACvC,aAAK,UAAU,OAAO,4BAA4B;AAAA,UAC9C;AAAA,UACA,UAAU,KAAK,QAAQ,OAAO,MAAM;AAAA,UACpC,OAAO,KAAK;AAAA,UACZ,MAAM,UAAU;AAAA,QACpB,CAAC;AAAA,MACL;AAAA,IACJ,SAAS,GAAG;AAAA,IAAC;AAAA,EACjB;AAAA;AAAA,EAGA,MAAM,OAAO;AACT,UAAM,OAAO,MAAM,aAAa,KAAK,MAAM,KAAK;AAChD,QAAI,MAAM;AACN,WAAK,WAAW,eAAe,YAAY,MAAM;AAAA,QAC7C,MAAM,KAAK;AAAA,QAAW,eAAe,KAAK;AAAA,MAAc,CAAC;AAC7D,WAAK,SAAS,OAAO,KAAK,SAAS,OAAO,EAAE;AAAA,IAChD;AAGA,eAAW,KAAK,KAAK,SAAS,UAAU;AACpC,iBAAW,QAAQ,EAAE,KAAK,OAAO,GAAG;AAChC,YAAI,KAAK,SAAS,SAAS,kBAAkB;AACzC,gBAAM,IAAI,KAAK,QAAQ,SAAS,CAAC;AACjC,cAAI,EAAE,cAAc;AAChB,cAAE,KAAK;AAAA,cAAe,KAAK;AAAA,cACvB,EAAE,MAAM,EAAE,cAAc,OAAO,EAAE,iBAAiB,CAAC,EAAE;AAAA,cACrD,EAAE,iBAAiB,EAAE;AAAA,YAAY;AAAA,UACzC;AAAA,QACJ;AAAA,MACJ;AACA,WAAK,cAAc,EAAE,MAAM,CAAC;AAAA,IAChC;AACA,SAAK,SAAS,OAAO,KAAK,SAAS,OAAO,EAAE;AAC5C,SAAK,SAAS,OAAO;AACrB,SAAK,cAAc;AAAA,EACvB;AAAA,EAEA,WAAW;AACP,QAAI,KAAK,cAAe,cAAa,KAAK,aAAa;AACvD,SAAK,gBAAgB,WAAW,YAAY;AACxC,WAAK,gBAAgB;AACrB,YAAM,aAAa,KAAK,MAAM,OAAO,KAAK,SAAS,UAAU,CAAC;AAAA,IAClE,GAAG,GAAG;AAAA,EACV;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,eAAe;AACX,UAAM,OAAO,KAAK,MAAM;AAQxB,UAAM,YAAY,KAAK;AACvB,UAAM,YAAY,KAAK,cAAc;AACrC,UAAM,cAAc,YAAY,KAAK,IAAI,SAAS,IAAI;AACtD,UAAM,cAAc,aAAa,SAAS,QAAQ;AAClD,UAAM,KAAM,aAAa,eACV,CAAC,YAAY,WAAW,QAAQ,KAChC,gBAAgB,uBACzB,YAAY;AAClB,QAAI,CAAC,GAAI;AAET,UAAM,QAAQ,KAAK,sBAAsB,EAAE;AAC3C,QAAI,SAAS,MAAM,MAAM;AAGrB,YAAM,UAAU,KAAK,IAAI,EAAE;AAC3B,YAAM,UAAU,SAAS,SAAS;AAClC,YAAM,YAAY,UAAU,KAAK,SAAS,UAAU,OAAO,IAAI;AAC/D,UAAI,MAAM,UAAU,aAAa,MAAM,WAAW,WAAW;AACzD,aAAK;AAAA,UACD;AAAA,UACA;AAAA,YAAE,MAAM,MAAM;AAAA,YAAM,OAAO,MAAM,SAAS,CAAC;AAAA,YACzC,OAAO,MAAM,SAAS,UAAU,MAAM,MAAM,MAAM,KAAK;AAAA,UAAE;AAAA,UAC3D;AAAA,UAAW,MAAM;AAAA,UACjB,EAAE,eAAe,MAAM;AAAA,QAC3B;AAAA,MACJ,OAAO;AACH,aAAK;AAAA,UACD;AAAA,UACA,EAAE,MAAM,MAAM,MAAM,OAAO,MAAM,SAAS,CAAC,EAAE;AAAA,UAC7C,MAAM,SAAS,UAAU,MAAM,MAAM,MAAM,KAAK;AAAA,UAChD,EAAE,eAAe,MAAM;AAAA,QAC3B;AAAA,MACJ;AACA,WAAK,MAAM,EAAE;AACb,WAAK,SAAS,OAAO;AACrB,WAAK,SAAS;AACd,WAAK,cAAc,eAAe;AAClC;AAAA,IACJ;AAKA,UAAM,OAAO,KAAK,IAAI,EAAE;AACxB,QAAI,CAAC,MAAM,QAAS;AACpB,UAAM,EAAE,MAAM,MAAM,IAAI,KAAK;AAC7B,UAAM,KAAK,KAAK,SAAS,SAAS,MAAM,KAAK;AAC7C,QAAI,IAAI;AAAE,WAAK,kBAAkB,IAAI,GAAG,MAAM,GAAG,KAAK;AAAG;AAAA,IAAQ;AACjE,UAAM,SAAS,KAAK,SAAS,cAAc,IAAI;AAC/C,QAAI,OAAQ,MAAK,kBAAkB,IAAI,MAAM;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,kBAAkB;AACd,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,YAAY,KAAK;AACvB,UAAM,YAAY,KAAK,cAAc;AACrC,UAAM,cAAc,YAAY,KAAK,IAAI,SAAS,IAAI;AACtD,UAAM,cAAc,aAAa,SAAS,QAAQ;AAClD,UAAM,KAAM,aAAa,eACV,CAAC,YAAY,WAAW,QAAQ,KAChC,gBAAgB,uBACzB,YAAY;AAClB,QAAI,CAAC,GAAI,QAAO;AAChB,UAAM,UAAU,KAAK,mBAAmB,EAAE,KAAK,CAAC;AAChD,QAAI,QAAQ,SAAS,EAAG,QAAO;AAC/B,UAAM,OAAO,KAAK,IAAI,EAAE;AACxB,UAAM,OAAO,MAAM,SAAS;AAC5B,WAAO,CAAC,EAAE,QAAQ,KAAK,SAAS,cAAc,IAAI;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,qBAAqB,QAAQ,OAAO;AAChC,UAAM,OAAO,KAAK,MAAM;AACxB,QAAI,CAAC,MAAM,qBAAsB;AAMjC,UAAM,WAAW,KAAK,UAAU,UAAU,MAAM,KAAK;AACrD,SAAK,qBAAqB,QAAQ,KAAK;AAOvC,SAAK,UAAU,iBAAiB,QAAQ,QAAQ;AAChD,SAAK,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAkB,QAAQ,MAAM,QAAQ,CAAC,GAAG;AACxC,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,QAAI,CAAC,QAAQ,KAAK,SAAS,QAAQ;AAC/B,WAAK,cAAc,MAAM,KAAK;AAC9B;AAAA,IACJ;AACA,SAAK,wBAAwB,QAAQ,EAAE,MAAM,MAAM,GAAG,UAAU,MAAM,KAAK,CAAC;AAC5E,SAAK,MAAM,MAAM;AACjB,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc,qBAAqB;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,gBAAgB,KAAK,MAAM,QAAQ,CAAC,GAAG;AACnC,QAAI,KAAK,YAAY,KAAK,cAAc,IAAI,IAAI,QAAQ,GAAG;AACvD,WAAK,aAAa,IAAI,UAAU,MAAM,KAAK;AAC3C;AAAA,IACJ;AACA,QAAI,KAAK,QAAQ;AACb,YAAM,OAAO,KAAK,MAAM;AACxB,YAAM,OAAO,KAAK,IAAI,IAAI,MAAM;AAChC,YAAM,IAAI,MAAM,SAAS;AACzB,UAAI,QAAQ,KAAK,MAAM,gBAAgB,CAAC,EAAE,WAAW,QAAQ,GAAG;AAC5D,aAAK,WAAW,IAAI,QAAQ,MAAM,KAAK;AACvC;AAAA,MACJ;AAAA,IACJ;AACA,SAAK,cAAc,MAAM,KAAK;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,WAAW,QAAQ,MAAM,QAAQ,CAAC,GAAG;AACjC,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,QAAI,CAAC,QAAQ,KAAK,SAAS,QAAQ;AAC/B,WAAK,cAAc,MAAM,KAAK;AAC9B;AAAA,IACJ;AACA,SAAK,wBAAwB,QAAQ,EAAE,MAAM,MAAM,GAAG,UAAU,MAAM,KAAK,CAAC;AAC5E,SAAK,MAAM,MAAM;AACjB,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,OAAO,MAAM,QAAQ,CAAC,GAAG;AAClC,UAAM,MAAM,KAAK,cAAc,IAAI,KAAK;AACxC,QAAI,CAAC,KAAK;AAAE,WAAK,cAAc,MAAM,KAAK;AAAG;AAAA,IAAQ;AACrD,QAAI;AAAE,UAAI,WAAW,UAAU;AAAA,IAAG,QAAQ;AAAA,IAAC;AAO3C,UAAM,OAAO,IAAI,UAAU,IAAI;AAC/B,SAAK,YAAY;AACjB,UAAM,YAAY,KAAK,QAAQ;AAAA,MAAM;AAAA,MAAM;AAAA,MAAM;AAAA,MAC7C,EAAE,GAAG,KAAK,KAAK,IAAI,MAAM,UAAU,MAAM;AAAA,IAAC;AAC9C,QAAI,YAAY;AAChB,QAAI,WAAW;AAAA,MACX;AAAA,MAAM,OAAO,EAAE,GAAI,SAAS,CAAC,EAAG;AAAA,MAChC,OAAO,WAAW,SAAS;AAAA,IAC/B;AAIA,UAAM,OAAO,IAAI,QAAQ,CAAC,GAAG,IAAI,YAAY;AAC7C,QAAI,KAAK;AACL,UAAI,OAAO;AACX,UAAI,QAAQ,EAAE,GAAI,SAAS,CAAC,EAAG;AAC/B,UAAI,QAAQ,IAAI,SAAS;AACzB,UAAI,OAAO,OAAO,IAAI,MAAM,IAAI,YAAY;AAAA,IAChD;AAEA,SAAK,gBAAgB,KAAK,IAAI,SAAS,KAAK;AAI5C,SAAK,SAAS;AACd,SAAK,cAAc,wBAAwB;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,MAAM,QAAQ,CAAC,GAAG;AAC5B,UAAM,OAAO,KAAK,MAAM;AACxB,QAAI,SAAS,KAAK,cAAc;AAChC,QAAI,UAAU;AACd,QAAI,CAAC,QAAQ;AACT,eAAS,KAAK,kBAAkB,IAAI;AACpC,gBAAU;AAAA,IACd;AACA,QAAI,CAAC,OAAQ;AAOb,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,UAAM,gBAAgB,MAAM,SAAS,OAC/B,KAAK,SAAS,UAAU,KAAK,QAAQ,IAAI,KAAK,OAC9C;AACN,UAAM,eAAgB,KAAK,SAAS,UAAU,IAAI,KAAK;AACvD,UAAM,QAAQ,UAAU,MAAM,KAAK;AACnC,SAAK;AAAA,MAAW;AAAA,MACZ,EAAE,MAAM,OAAO,MAAM;AAAA,MACrB;AAAA,MAAe;AAAA,IAAY;AAC/B,SAAK,MAAM,MAAM;AACjB,QAAI,QAAS,MAAK,cAAc,MAAM,KAAK,SAAS,OAAO,CAAC;AAC5D,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc;AAAA,EACvB;AAAA;AAAA,EAGA,MAAM,KAAK;AACP,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,UAAU,KAAK;AACrB,QAAI,CAAC,QAAS;AAMd,UAAM,QAAQ,KAAK,MAAM,SAAS,GAAG;AACrC,QAAI,MAAO,MAAK,UAAU,MAAM,KAAK;AACrC,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,UAAU,MAAM,QAAQ;AACpB,UAAM,OAAO,KAAK,UAAU;AAC5B,SAAK,eAAe,QAAQ,KAAK,SAAS,KAAK,KAAK;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,cAAc,QAAQ,KAAK,MAAM,QAAQ,CAAC,GAAG,QAAQ,IAAI;AACrD,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,MAAM,SAAS,KAAK,IAAI,MAAM,IAAI;AACxC,QAAI,CAAC,OAAO,IAAI,SAAS,OAAQ,QAAO;AACxC,UAAM,QAAQ,KAAK,MAAM,QAAQ,GAAG;AACpC,QAAI,CAAC,MAAO,QAAO;AACnB,SAAK,eAAe,OAAO,EAAE,MAAM,MAAM,GAAG,SAAS,UAAU,MAAM,KAAK,CAAC;AAC3E,SAAK,MAAM,KAAK;AAChB,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc,YAAY;AAC/B,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,kBAAkB;AACd,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,KAAK,KAAK;AAChB,UAAM,OAAO,KAAK,KAAK,IAAI,EAAE,IAAI;AACjC,QAAI,CAAC,QAAQ,KAAK,SAAS,UAAU,CAAC,KAAK,QAAS,QAAO;AAC3D,UAAM,OAAO,KAAK,QAAQ;AAC1B,QAAI,CAAC,QAAQ,SAAS,oBAAoB,KAAK,WAAW,QAAQ,EAAG,QAAO;AAC5E,WAAO,EAAE,QAAQ,IAAI,MAAM,OAAO,KAAK,QAAQ,SAAS,CAAC,GAAG,OAAO,KAAK,MAAM;AAAA,EAClF;AAAA;AAAA,EAGA,mBAAmB;AACf,UAAM,IAAI,KAAK,gBAAgB;AAC/B,QAAI,CAAC,EAAG;AACR,SAAK,qBAAqB,EAAE,QAAQ,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,KAAK;AAAA,EACnE;AAAA;AAAA;AAAA,EAIA,sBAAsB;AAClB,UAAM,IAAI,KAAK,gBAAgB;AAC/B,QAAI,CAAC,EAAG;AACR,SAAK,gBAAgB,EAAE,MAAM,EAAE,KAAK;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiB,KAAK;AAClB,UAAM,IAAI,KAAK,gBAAgB;AAC/B,QAAI,CAAC,GAAG;AAAE,WAAK,MAAM,GAAG;AAAG;AAAA,IAAQ;AACnC,SAAK,cAAc,EAAE,QAAQ,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK;AAAA,EAC9D;AAAA,EAEA,eAAe;AACX,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,YAAY,KAAK;AACvB,QAAI,CAAC,UAAW;AAChB,UAAM,OAAO,KAAK,IAAI,SAAS;AAC/B,UAAM,OAAO,MAAM,SAAS;AAsB5B,UAAM,cAAc,KAAK,UAAU,aAAa,SAAS,GAAG;AAC5D,QAAI,gBAAgB,SAAS,aAAa,aAAa,KAAM;AAI7D,QAAI,SAAS,kBAAkB;AAC3B,YAAM,QAAQ,KAAK,SAAS,OAAO;AACnC,YAAM,MAAM,QAAQ,KAAK,cAAc,IAAI,KAAK,IAAI;AACpD,UAAI,KAAK;AAEL,aAAK,cAAc,OAAO,KAAK;AAC/B,YAAI;AAAE,cAAI,OAAO,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,QAAG,QAAQ;AAAA,QAAC;AAAA,MACtD;AACA,WAAK,MAAM,SAAS;AAAA,IACxB,WAGS,YAAY,IAAI,IAAI,GAAG;AAC5B,YAAM,IAAI,KAAK,SAAS,OAAO;AAC/B,YAAM,OAAO,KAAK,MAAM,GAAG,EAAE,CAAC;AAC9B,QAAE,OAAO,IAAI,IAAI;AACjB,WAAK,cAAc,MAAM,CAAC;AAAA,IAC9B,OACK;AACD,WAAK,MAAM,SAAS;AAAA,IACxB;AAiBA,QAAI,CAAC,KAAK,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,EAAE,WAAW,QAAQ,CAAC,GAAG;AACjF,YAAM,UAAU,KAAK,kBAAkB,IAAI;AAC3C,UAAI,QAAS,MAAK,UAAU,MAAM,OAAO;AAAA,IAC7C;AAIA,SAAK,cAAc,MAAM,KAAK,SAAS,OAAO,CAAC;AAE/C,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc,aAAa;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAe,QAAQ;AACnB,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,OAAO,SAAS,KAAK,IAAI,MAAM,IAAI;AACzC,QAAI,CAAC,QAAQ,KAAK,SAAS,QAAQ;AAG/B,WAAK,aAAa;AAClB;AAAA,IACJ;AACA,UAAM,OAAO,KAAK,QAAQ,CAAC;AAC3B,QAAI,KAAK,UAAU,GAAG;AAClB,WAAK,MAAM,MAAM;AACjB,WAAK,aAAa;AAClB;AAAA,IACJ;AACA,UAAM,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,KAAK,gBAAgB,CAAC,CAAC;AACzE,SAAK,cAAc,QAAQ,GAAG;AAC9B,SAAK,MAAM,MAAM;AACjB,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc,eAAe;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAkB,MAAM;AACpB,UAAM,UAAU,SAAS,EAAE,SAAS,MAAM,OAAO,GAAG,CAAC;AACrD,QAAI,CAAC,KAAK,QAAQ;AAAE,WAAK,QAAQ,OAAO;AAAG,aAAO,QAAQ;AAAA,IAAI;AAC9D,SAAK,UAAU,OAAO;AACtB,UAAM,QAAQ;AAAA,MACV,IAAI,eAAe,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC;AAAA,MAC1C,MAAM;AAAA,MAAS,UAAU;AAAA,MAAM,KAAK;AAAA,MACpC,UAAU,CAAC,QAAQ,IAAI,KAAK,MAAM;AAAA,MAAG,OAAO,CAAC,GAAG,CAAC;AAAA,IACrD;AACA,SAAK,MAAM,IAAI,MAAM,IAAI,KAAK;AAC9B,UAAM,UAAU,KAAK,IAAI,KAAK,MAAM;AACpC,QAAI,QAAS,SAAQ,WAAW,MAAM;AACtC,YAAQ,WAAW,MAAM;AACzB,SAAK,SAAS,MAAM;AACpB,SAAK,MAAM,QAAQ,EAAE;AACrB,WAAO,QAAQ;AAAA,EACnB;AAAA,EAEA,SAAS,KAAK;AACV,QAAI,KAAK,MAAM,EAAE,SAAS,GAAG,GAAG;AAC5B,WAAK,SAAS,oBAAoB;AAClC,WAAK,cAAc;AAAA,IACvB;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAgB;AACZ,UAAM,KAAK,KAAK,MAAM,EAAE;AACxB,WAAO,KAAK,KAAK,SAAS,OAAO,EAAE,IAAI;AAAA,EAC3C;AAAA,EAEA,YAAY,KAAK;AACb,QAAI,KAAK,MAAM,EAAE,QAAQ,GAAG,GAAG;AAC3B,WAAK,SAAS,OAAO;AACrB,WAAK,SAAS;AACd,WAAK,cAAc;AAAA,IACvB;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,uBAAuB;AACnB,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,UAAU,KAAK,QAAQ;AAC7B,QAAI,CAAC,QAAS,QAAO;AACrB,WAAO,KAAK,UAAU,QAAQ,EAAE;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,QAAQ;AACd,UAAM,OAAO,KAAK,eAAe,MAAM;AACvC,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,OAAO,cAAc,IAAI;AAC/B,UAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,KAAK,gBAAgB,CAAC,CAAC;AAC5E,WAAO,KAAK,SAAS,QAAQ,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,iBAAiB,QAAQ,KAAK;AAC1B,UAAM,OAAO,KAAK,eAAe,MAAM;AACvC,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,OAAO,cAAc,IAAI;AAC/B,QAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,KAAK,OAAO,KAAK,OAAQ,QAAO;AACpE,WAAO,KAAK,SAAS,QAAQ,CAAC,KAAK,GAAG,CAAC,GAAG,GAAG,EAAE,WAAW,OAAO,QAAQ,IAAI,CAAC;AAAA,EAClF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiEA,YAAY,YAAY,SAAS,SAAS,CAAC,GAAG;AAC1C,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,OAAO,KAAK,IAAI,UAAU;AAChC,QAAI,CAAC,QAAQ,KAAK,SAAS,OAAQ,QAAO;AAC1C,UAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC;AACrD,QAAI,CAAC,OAAO,UAAU,OAAO,KAAK,UAAU,KAAK,WAAW,KAAK,OAAQ,QAAO;AAChF,QAAI,SAAS,QAAQ,UAAU;AAC/B,UAAM,OAAO,SAAS,KAAK,IAAI,MAAM,IAAI;AACzC,QAAI,CAAC,QAAQ,KAAK,SAAS,OAAQ,QAAO;AAC1C,QAAI,OAAO,KAAK,SAAS,QAAQ,EAAE,EAAE,WAAW,QAAQ,EAAG,QAAO;AAClE,UAAM,OAAQ,OAAO,SAAS,UAAU,OAAO,SAAS,UAClD,OAAO,OAAO;AAKpB,QAAI,WAAW,cAAc,SAAS,QAAS,QAAO;AACtD,QAAI,WAAW,cAAc,KAAK,UAAU,EAAG,QAAO;AAGtD,UAAM,MAAM,KAAK,OAAO;AACxB,UAAM,OAAO;AAAA,MAAE,MAAM,IAAI;AAAA,MAAM,OAAO,EAAE,GAAI,IAAI,SAAS,CAAC,EAAG;AAAA,MAC9C,OAAO,IAAI,SAAS,IAAI,QAAQ;AAAA,IAAG;AAGlD,QAAI,SAAS,SAAS;AAClB,YAAM,QAAQ,KAAK,MAAM,QAAQ,OAAO,QAAQ,MAAM,MAAM,GAAG;AAC/D,UAAI,CAAC,MAAO,QAAO;AAOnB,iBAAW,MAAM,QAAQ,KAAK;AAC9B,UAAI,OAAO,OAAQ,eAAc,MAAM,QAAQ,KAAK;AACpD,eAAS;AAAA,IACb;AAGA,QAAI,SAAS,OAAO;AAChB,YAAM,QAAQ,KAAK;AAAA,QAAc;AAAA,QAAY;AAAA,QAAS;AAAA,QACrB,OAAO,UAAU,OAAO,KAAK,IAAI,OAAO,QAAQ;AAAA,MAAE;AACnF,UAAI,CAAC,OAAO,GAAI,QAAO;AAAA,IAC3B,OAAO;AACH,WAAK,cAAc,YAAY,OAAO;AACtC,WAAK,eAAe,QAAQ,EAAE,MAAM,KAAK,MAAM,OAAO,KAAK,MAAM,GAAG,KAAK,KAAK;AAAA,IAClF;AAGA,QAAI,EAAG,KAAK,IAAI,UAAU,GAAG,QAAQ,CAAC,GAAG,QAAS;AAC9C,WAAK,UAAU,MAAM,UAAU;AAC/B,WAAK,iBAAiB,MAAM,UAAU;AAAA,IAC1C;AACA,SAAK,cAAc,MAAM,KAAK,SAAS,OAAO,CAAC;AAK/C,QAAI;AACA,WAAK,UAAU;AAAA,QAAO;AAAA,QAClB,EAAE,YAAY,UAAU,QAAQ,KAAK,MAAM,KAAK;AAAA,MAAC;AAAA,IACzD,SAAS,KAAK;AAAE,cAAQ,KAAK,8BAA8B,GAAG;AAAA,IAAG;AAEjE,QAAI,KAAK,IAAI,MAAM,EAAG,MAAK,MAAM,MAAM;AACvC,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc,WAAW;AAC9B,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,eAAe,QAAQ;AACnB,UAAM,OAAO,SAAS,KAAK,MAAM,EAAE,IAAI,MAAM,IAAI;AACjD,QAAI,CAAC,QAAQ,KAAK,SAAS,UAAU,CAAC,KAAK,QAAS,QAAO;AAC3D,QAAI,YAAY,IAAI,KAAK,QAAQ,IAAI,EAAG,QAAO;AAC/C,QAAI,KAAK,QAAQ,SAAS,iBAAkB,QAAO;AAInD,QAAI,KAAK,UAAU,aAAa,KAAK,EAAE,GAAG,YAAY,MAAO,QAAO;AACpE,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,SAAS,QAAQ,MAAM,cAAc,EAAE,WAAW,SAAS,GAAG,GAAG;AAC7D,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,UAAM,aAAa,KAAK,SAAS;AACjC,UAAM,WAAW,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;AAC/D,UAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,gBAAgB,CAAC,CAAC;AACvE,UAAM,WAAW,EAAE,GAAG,KAAK,MAAM,GAAG,OAAO,EAAE,GAAI,KAAK,MAAM,EAAE,SAAS,CAAC,EAAG,EAAE;AAE7E,UAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,cAAU,YAAY;AACtB,cAAU,MAAM,UAAU;AAQ1B,UAAM,WAAW,SAAS,cAAc,KAAK;AAC7C,aAAS,YAAY;AACrB,UAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,WAAO,YAAY;AACnB,WAAO,MAAM,UAAU;AACvB,cAAU,OAAO,UAAU,MAAM;AAEjC,UAAM,QAAQ,UAAU,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AACzF,UAAM,YAAY,KAAK,QAAQ;AAAA,MAAM,SAAS;AAAA,MAAM;AAAA,MAAQ,SAAS;AAAA,MACjE,EAAE,GAAG,KAAK,KAAK,IAAI,MAAM,UAAU,MAAM;AAAA,IAAC;AAC9C,UAAM,MAAM,IAAI,cAAc;AAAA,MAC1B,IAAI;AAAA,MACJ,OAAO,WAAW,SAAS,SAAS;AAAA,MACpC,MAAM;AAAA,MACN,SAAS;AAAA,MACT,aAAa;AAAA,MACb,aAAa;AAAA,MACb,WAAW;AAAA,MACX,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,MAKP,MAAM,KAAK;AAAA,MACX,gBAAgB,KAAK,gBAAgB,KAAK,gBAAgB,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAU9D,UAAU,MAAM,KAAK;AAAA,MACrB,YAAY,MAAM,KAAK,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOnC,YAAY,MAAM,KAAK,gBAAgB,KAAK;AAAA,MAC5C,cAAc;AAAA,MACd,eAAe;AAAA,MACf,SAAS,MAAM,KAAK,uBAAuB,OAAO,IAAI;AAAA,IAC1D,CAAC;AAED,SAAK,cAAc,IAAI,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,MAK1B,QAAQ;AAAA,MAAM;AAAA,MAAY;AAAA,MAAU;AAAA,MAAW,QAAQ;AAAA,MACvD;AAAA,MAAW;AAAA,MAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMnB;AAAA,MAAM,cAAc;AAAA,MAAQ,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAiCnC,YAAY,KAAK,iBAAiB,SAAS;AAAA;AAAA;AAAA,MAG3C,WAAW;AAAA,IACf,CAAC;AACD,SAAK,gBAAgB,KAAK;AAI1B,QAAI,CAAC,aAAa,WAAW,EAAG,MAAK,cAAc,QAAQ,MAAM;AAAA,QAC5D,MAAK,UAAU,MAAM,MAAM;AAMhC,SAAK,iBAAiB,MAAM,MAAM;AAElC,SAAK,MAAM,MAAM;AACjB,SAAK,cAAc,MAAM,KAAK,SAAS,OAAO,CAAC;AAE/C,SAAK,SAAS,OAAO;AAkBrB,QAAI,KAAK,gBAAgB;AAOrB,YAAM,SAAS,KAAK,SAAS,OAAO,MAAM;AAC1C,UAAI,OAAQ,KAAI,OAAO,MAAM;AAAA,IACjC;AAEA,QAAI,KAAK;AACT,SAAK,uBAAuB,KAAK,KAAK;AACtC,SAAK,SAAS;AACd,SAAK,cAAc,iBAAiB;AACpC,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,gBAAgB,OAAO;AACnB,UAAM,MAAM,KAAK,cAAc,IAAI,KAAK;AACxC,QAAI,CAAC,IAAK;AACV,UAAM,OAAO,IAAI,QAAQ,CAAC;AAC1B,QAAI,eAAe,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,IAAI,gBAAgB,CAAC,CAAC;AAC/E,QAAI,KAAK,UAAU,GAAG;AAClB,UAAI;AAAE,YAAI,OAAO,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAAqB;AACzD,UAAI,QAAQ;AACZ,UAAI,UAAU,UAAU,IAAI,2BAA2B;AACvD;AAAA,IACJ;AACA,QAAI,UAAU,UAAU,OAAO,2BAA2B;AAC1D,QAAI,CAAC,IAAI,OAAO;AACZ,UAAI,QAAQ,eAAe;AAAA,QACvB,QAAQ,IAAI;AAAA,QACZ,UAAU,KAAK;AAAA,QACf,UAAU,CAAC,QAAQ,SAAS,KAAK,iBAAiB,OAAO,QAAQ,IAAI;AAAA,MACzE,CAAC;AAAA,IACL;AACA,QAAI,MAAM,OAAO,MAAM,IAAI,YAAY;AAAA,EAC3C;AAAA;AAAA;AAAA,EAIA,iBAAiB,OAAO,QAAQ,OAAO,CAAC,GAAG;AACvC,UAAM,MAAM,KAAK,cAAc,IAAI,KAAK;AACxC,QAAI,CAAC,IAAK;AACV,UAAM,OAAO,IAAI,QAAQ,CAAC;AAC1B,QAAI,WAAW,UAAU;AACrB,WAAK,cAAc,OAAO,KAAK,GAAG;AAClC;AAAA,IACJ;AACA,QAAI,WAAW,SAAS;AACpB,UAAI,KAAK,MAAM,KAAK,KAAK,OAAO,KAAK,OAAQ;AAI7C,UAAI,KAAK,UAAU,GAAG;AAClB,YAAI;AAAE,cAAI,OAAO,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,QAAG,QAAQ;AAAA,QAAa;AAC9D;AAAA,MACJ;AACA,WAAK,OAAO,KAAK,KAAK,CAAC;AACvB,UAAI,KAAK,MAAM,IAAI,aAAc,KAAI,gBAAgB;AAAA,eAC5C,KAAK,QAAQ,IAAI,cAAc;AACpC,YAAI,eAAe,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC;AAC3C,aAAK,gBAAgB,KAAK;AAAA,MAC9B;AACA,WAAK,gBAAgB,KAAK;AAC1B,WAAK,cAAc,kBAAkB;AACrC;AAAA,IACJ;AACA,QAAI,WAAW,QAAQ;AACnB,YAAM,EAAE,MAAM,GAAG,IAAI;AACrB,UAAI,QAAQ,QAAQ,MAAM,KAAM;AAChC,UAAI,OAAO,KAAK,QAAQ,KAAK,UAAU,KAAK,KAAK,MAAM,KAAK,OAAQ;AACpE,YAAM,QAAQ,KAAK,OAAO,MAAM,CAAC,EAAE,CAAC;AACpC,WAAK,OAAO,IAAI,GAAG,KAAK;AAGxB,UAAI,IAAI,iBAAiB,KAAM,KAAI,eAAe;AAAA,eACzC,OAAO,IAAI,gBAAgB,MAAM,IAAI,aAAc,KAAI,gBAAgB;AAAA,eACvE,OAAO,IAAI,gBAAgB,MAAM,IAAI,aAAc,KAAI,gBAAgB;AAChF,WAAK,gBAAgB,KAAK;AAC1B;AAAA,IACJ;AACA,QAAI,WAAW,OAAQ,MAAK,0BAA0B,OAAO,KAAK,KAAK,KAAK,GAAG,KAAK,CAAC;AAAA,EACzF;AAAA;AAAA;AAAA,EAIA,cAAc,OAAO,KAAK;AACtB,UAAM,MAAM,KAAK,cAAc,IAAI,KAAK;AACxC,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,OAAO,IAAI,QAAQ,CAAC;AAC1B,QAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,KAAK,OAAO,KAAK,OAAQ,QAAO;AACpE,QAAI,QAAQ,IAAI,aAAc,QAAO;AACrC,QAAI,eAAe;AACnB,SAAK,gBAAgB,KAAK;AAC1B,SAAK,gBAAgB,KAAK;AAC1B,SAAK,SAAS;AACd,SAAK,cAAc,mBAAmB;AACtC,WAAO;AAAA,EACX;AAAA;AAAA;AAAA,EAIA,gBAAgB,OAAO;AACnB,UAAM,MAAM,KAAK,cAAc,IAAI,KAAK;AACxC,QAAI,CAAC,IAAK;AACV,UAAM,OAAO,IAAI,QAAQ,CAAC,GAAG,IAAI,YAAY;AAC7C,QAAI,CAAC,IAAK;AACV,QAAI;AAAE,UAAI,WAAW,UAAU;AAAA,IAAG,QAAQ;AAAA,IAAqC;AAC/E,QAAI,OAAO,YAAY;AACvB,QAAI,YAAY,KAAK,QAAQ;AAAA,MAAM,IAAI;AAAA,MAAM,IAAI;AAAA,MAAQ,IAAI,SAAS,CAAC;AAAA,MACnE,EAAE,GAAG,KAAK,KAAK,IAAI,MAAM,UAAU,MAAM;AAAA,IAAC;AAC9C,QAAI,WAAW;AAAA,MAAE,MAAM,IAAI;AAAA,MAAM,OAAO,EAAE,GAAI,IAAI,SAAS,CAAC,EAAG;AAAA,MAC9C,OAAO,IAAI,WAAW,SAAS,IAAI,SAAS,IAAI;AAAA,IAAK;AACtE,SAAK,gBAAgB,KAAK,IAAI,SAAS,KAAK;AAAA,EAChD;AAAA;AAAA,EAGA,gBAAgB,KAAK,OAAO;AACxB,QAAI;AACA,YAAM,UAAU,IAAI,QAAQ,SAAS,cAAc,4BAA4B;AAC/E,UAAI,QAAS,SAAQ,cAAc;AACnC,UAAI,IAAI,OAAQ,KAAI,OAAO,QAAQ;AAAA,IACvC,QAAQ;AAAA,IAAwC;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,0BAA0B,OAAO,KAAK,GAAG,GAAG;AACxC,UAAM,MAAM,KAAK,cAAc,IAAI,KAAK;AACxC,UAAM,OAAO,KAAK,QAAQ,CAAC;AAC3B,QAAI,CAAC,KAAK,OAAQ;AAClB,UAAM,QAAQ,CAAC,EAAE,OAAO,aAAa,MAAM,SAAS,QAAQ,QAAQ,CAAC;AACrE,QAAI,KAAK,SAAS,GAAG;AACjB,YAAM,KAAK,EAAE,OAAO,oBAAoB,MAAM,aAAa,QAAQ,eAAe,CAAC;AAAA,IACvF;AACA,oBAAgB,GAAG,GAAG,OAAO,CAAC,WAAW;AACrC,YAAM,OAAO,KAAK,cAAc,IAAI,KAAK;AACzC,UAAI,CAAC,KAAM;AACX,UAAI,WAAW,QAAS,MAAK,iBAAiB,OAAO,SAAS,EAAE,IAAI,CAAC;AAAA,eAC5D,WAAW,gBAAgB;AAChC,cAAM,OAAO,KAAK,KAAK,GAAG;AAC1B,YAAI,CAAC,KAAM;AAMX,cAAM,UAAU,QAAQ,KAAK;AAC7B,aAAK,OAAO,CAAC,IAAI;AACjB,aAAK,eAAe;AACpB,YAAI,QAAS,MAAK,gBAAgB,KAAK;AACvC,aAAK,gBAAgB,KAAK;AAC1B,aAAK,cAAc,yBAAyB;AAAA,MAChD;AAAA,IACJ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,0BAA0B;AACtB,QAAI,CAAC,KAAK,eAAgB;AAC1B,eAAW,CAAC,EAAE,GAAG,KAAK,KAAK,eAAe;AACtC,UAAI,CAAC,IAAI,cAAc,CAAC,IAAI,OAAQ;AAOpC,UAAI,IAAI,OAAO,WAAY;AA4B3B,UAAI,IAAI,OAAO,eAAe,IAAI,OAAO,cAAc,KAAK,OAAQ;AAcpE,UAAI,IAAI,eAAe,KAAK,SAAS,WAAW;AAC5C,YAAI,CAAC,IAAI,iBAAiB,IAAI,OAAO,SAAS,aAAa;AACvD,cAAI;AAAE,gBAAI,OAAO,QAAQ,OAAO;AAAA,UAAG,QAAQ;AAAA,UAAoB;AAAA,QACnE;AACA;AAAA,MACJ;AA8BA,UAAI,CAAC,IAAI,iBAAiB,IAAI,YAAY;AACtC,cAAM,OAAO,KAAK,SAAS,SAAS,IAAI,UAAU,GAAG;AACrD,YAAI,QAAQ,CAAC,KAAK,IAAI,IAAI,UAAU,GAAG;AASnC,cAAI,WAAW,KAAK,cAAc;AAClC,cAAI,CAAC,UAAU;AACX,kBAAM,UAAU,KAAK,kBAAkB,IAAI;AAC3C,gBAAI,SAAS;AACT,mBAAK,UAAU,MAAM,OAAO;AAC5B,mBAAK,cAAc,MAAM,KAAK,SAAS,SAAS,IAAI,UAAU,CAAC;AAC/D,yBAAW,KAAK,cAAc;AAAA,YAClC;AAAA,UACJ;AACA,cAAI,SAAU,KAAI,aAAa;AAAA,QACnC;AAAA,MACJ;AAQA,YAAM,SAAS,IAAI,gBACZ,IAAI,cAAc,KAAK,OACxB,KAAK,SAAS,OAAO,IAAI,UAAU;AAUzC,UAAI,CAAC,OAAQ;AACb,UAAI,WAAW,IAAI,OAAO,WAAW;AAqBjC,cAAM,KAAK,IAAI,OAAO;AACtB,YAAI,MAAM,CAAC,GAAG,aAAa;AACvB,cAAI;AAAE,mBAAO,YAAY,EAAE;AAAA,UAAG,SACvB,KAAK;AAAE,oBAAQ,KAAK,yBAAyB,GAAG;AAAA,UAAG;AAAA,QAC9D;AACA;AAAA,MACJ;AACA,UAAI;AAAE,YAAI,OAAO,OAAO,MAAM;AAAA,MAAG,SAC1B,KAAK;AAAE,gBAAQ,KAAK,uBAAuB,GAAG;AAAA,MAAG;AAAA,IAC5D;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BA,aAAa,KAAK;AACd,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI,MAAM;AACV,eAAW,CAAC,EAAE,CAAC,KAAK,KAAK,eAAe;AACpC,UAAI,EAAE,WAAW,KAAK;AAAE,cAAM;AAAG;AAAA,MAAO;AAAA,IAC5C;AACA,QAAI,CAAC,IAAK,QAAO;AASjB,QAAI,CAAC,IAAI,cAAc,CAAC,IAAI,cAAe,QAAO;AAClD,QAAI,IAAI,eAAe,KAAK,SAAS,aAC9B,KAAK,SAAS,SAAS,IAAI,UAAU,GAAG;AAG3C,WAAK,cAAc,IAAI,UAAU;AAAA,IACrC,OAAO;AACH,WAAK,wBAAwB;AAAA,IACjC;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,cAAc;AACV,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,QAAQ,KAAK,WAAW;AAC9B,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,WAAW,KAAK,sBAAsB;AAI5C,UAAM,KAAK,SAAS,OAAO,KAAK,aAAa,KAAK;AAClD,UAAM,KAAK,SAAS,MAAM,KAAK,YAAY,KAAK;AAChD,WAAO;AAAA,MAAE,MAAM,MAAM,OAAO;AAAA,MAAI,MAAM,MAAM,MAAM;AAAA,MACzC,OAAO,MAAM;AAAA,MAAO,QAAQ,MAAM;AAAA,IAAO;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BA,aAAa;AACT,QAAI,IAAI,UAAU,KAAK,UAAU,IAAI,WAAW,IAAI;AACpD,eAAW,QAAQ,KAAK,MAAM,EAAE,OAAO,GAAG;AACtC,UAAI,YAAY,IAAI,KAAK,SAAS,IAAI,EAAG;AACzC,YAAM,KAAK,KAAK,SAAS,OAAO,KAAK,EAAE;AACvC,UAAI,CAAC,GAAI;AACT,YAAM,MAAM,GAAG,sBAAsB;AACrC,UAAI,CAAC,IAAI,SAAS,CAAC,IAAI,OAAQ;AAC/B,UAAI,KAAK,IAAI,GAAG,IAAI,IAAI;AAAG,WAAK,KAAK,IAAI,IAAI,IAAI,GAAG;AACpD,UAAI,KAAK,IAAI,GAAG,IAAI,KAAK;AAAG,UAAI,KAAK,IAAI,GAAG,IAAI,MAAM;AAAA,IAC1D;AACA,QAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,WAAO,EAAE,MAAM,GAAG,KAAK,IAAI,OAAO,IAAI,GAAG,QAAQ,IAAI,GAAG;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,gBAAgB,UAAU;AACtB,UAAM,MAAM,KAAK,cAAc,IAAI,QAAQ;AAW3C,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,OAAO,KAAK,SAAS,SAAS,IAAI,UAAU,GAAG;AACrD,UAAM,OAAO,IAAI,aAAa,MAAM,IAAI,IAAI,UAAU,IAAI;AAC1D,QAAI,QAAQ,KAAK,SAAS,QAAQ;AAC9B,UAAI,QAAQ;AAAA,QAAE,QAAQ,IAAI;AAAA,QACZ,MAAM,KAAK,aAAa,IAAI,IAAI,SAAS;AAAA,MAAM;AAAA,IACjE;AACA,QAAI,YAAY;AAChB,QAAI;AAAE,UAAI,OAAO,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,IAAG,SAClC,KAAK;AAAE,cAAQ,KAAK,gCAAgC,GAAG;AAAA,IAAG;AACjE,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+CA,YAAY,KAAK,OAAO,CAAC,GAAG;AACxB,UAAM,QAAQ,KAAK;AACnB,QAAI,CAAC,SAAS,CAAC,KAAK,KAAM,QAAO;AACjC,QAAI,KAAK,cAAc,IAAI,KAAK,EAAG,QAAO;AAM1C,QAAI,KAAK,eAAe;AACpB,UAAI,OAAO,IAAI,QAAQ,IAAI,WAAW,IAAI;AAC1C,UAAI,iBAAiB,KAAK,gBAAgB;AAAA,IAC9C;AACA,QAAI,WAAW,MAAM,KAAK;AAC1B,QAAI,aAAa,MAAM,KAAK,YAAY;AACxC,QAAI,aAAa,MAAM,KAAK,gBAAgB,KAAK;AACjD,QAAI,eAAe;AACnB,QAAI,gBAAgB;AAEpB,UAAM,WAAW;AAAA,MAAE,MAAM,KAAK;AAAA,MAAM,OAAO,KAAK,SAAS,CAAC;AAAA,MACvC,OAAO,KAAK,SAAS,KAAK;AAAA,IAAK;AAClD,SAAK,cAAc,IAAI,OAAO;AAAA,MAC1B,QAAQ;AAAA,MACR,YAAY,KAAK,SAAS;AAAA,MAC1B;AAAA,MACA,WAAW,KAAK,aAAa;AAAA,MAC7B,QAAQ;AAAA,MACR,WAAW;AAAA,MAAM,QAAQ;AAAA,MAAM,UAAU;AAAA,MACzC,MAAM,CAAC,QAAQ;AAAA,MAAG,cAAc;AAAA,MAAG,OAAO;AAAA,MAC1C,YAAY,KAAK,cAAc;AAAA,MAC/B,eAAe,KAAK,iBAAiB;AAAA,MACrC,SAAS;AAAA,MACT,WAAW;AAAA,IACf,CAAC;AAED,UAAM,QAAQ,IAAI;AAClB,QAAI,UAAU,MAAM;AAChB,WAAK,uBAAuB,OAAO,IAAI;AACvC,cAAQ;AAAA,IACZ;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2BA,kBAAkB;AACd,QAAI,KAAK,SAAU,QAAO,KAAK;AAC/B,SAAK,WAAW;AAAA,MACZ,OAAO,CAAC,GAAG,QAAQ,KAAK,WAAW,GAAG,GAAG;AAAA,MACzC,QAAQ,CAAC,OAAO,QAAQ,KAAK,YAAY,OAAO,GAAG;AAAA,IACvD;AACA,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiCA,WAAW,eAAe;AAAE,WAAO;AAAA,EAAI;AAAA,EAEvC,WAAW,GAAG,KAAK;AACf,UAAM,SAAS,KAAK,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG;AACvD,QAAI,CAAC,OAAQ,QAAO;AA2CpB,UAAM,MAAM,CAAC,EAAE,KAAK,cAAc,OAAO,SAAS,IAAI,UAAU;AAChE,UAAM,SAAS,OAAO,QAAQ;AAO9B,UAAM,aAAa,KAAK,SAAS;AACjC,UAAM,OAAO,KAAK,MAAM,EAAE,IAAI,MAAM;AACpC,QAAI,CAAC,QAAQ,KAAK,SAAS,OAAQ,QAAO;AAG1C,QAAI,OAAO,KAAK,SAAS,QAAQ,EAAE,EAAE,WAAW,QAAQ,EAAG,QAAO;AAElE,UAAM,IAAI,OAAO,sBAAsB;AAUvC,UAAM,OAAO,KAAK,UAAU,KAAK,UAAU,GAAG,GAAG,KAAK,GAAG,IAAI;AA0C7D,QAAI,OAAO,SAAS,OAAO;AACvB,YAAM,OAAO,KAAK,gBAAgB,GAAG;AACrC,UAAI,CAAC,KAAM,QAAO;AAClB,aAAO;AAAA,QAAE,KAAK,GAAG,KAAK,MAAM;AAAA,QAAS,MAAM,KAAK;AAAA,QACvC,QAAQ,KAAK;AAAA,QAAQ;AAAA,QAAY;AAAA,QAAM,MAAM;AAAA,QAC7C,UAAU;AAAA,MAAE;AAAA,IACzB;AACA,WAAO,KAAK,aAAa,EAAE,QAAQ,MAAM,GAAG,MAAM,KAAK,WAAW,CAAC;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,aAAa,EAAE,QAAQ,MAAM,GAAG,MAAM,KAAK,WAAW,GAAG;AACrD,QAAI,MAAM;AACN,aAAO;AAAA,QAAE,KAAK,GAAG,MAAM,IAAI,IAAI;AAAA,QAAI,MAAM,QAAQ,GAAG,IAAI;AAAA,QAC/C;AAAA,QAAQ;AAAA,QAAY;AAAA,QAAM,MAAM;AAAA,QAAS,UAAU;AAAA,MAAE;AAAA,IAClE;AAcA,QAAI,IAAK,QAAO;AAwBhB,UAAM,QAAQ,CAAC,KAAK,WAAW,KAAK,aAAa,IAAI;AACrD,WAAO;AAAA,MAAE,KAAK,GAAG,MAAM,IAAI,QAAQ,SAAS,KAAK;AAAA,MAAI,MAAM,QAAQ,GAAG,IAAI;AAAA,MACjE;AAAA,MAAQ;AAAA,MAAY,MAAM;AAAA,MAC1B,MAAM,QAAQ,SAAS;AAAA,MAAO,UAAU;AAAA,IAAE;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6CA,aAAa,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC,GAAG;AAC1C,UAAM,SAAS,KAAK,UAAU,EAAE,SAAS,EAAE,SAAS,IAAI;AACxD,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,SAAS,OAAO,QAAQ;AAC9B,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,QAAI,CAAC,QAAQ,KAAK,SAAS,OAAQ,QAAO;AAI1C,QAAI,OAAO,KAAK,SAAS,QAAQ,EAAE,EAAE,WAAW,QAAQ,EAAG,QAAO;AAClE,UAAM,MAAM,CAAC,CAAC,gBAAgB,WAAW;AACzC,QAAI,KAAK;AACL,YAAM,MAAM,KAAK,IAAI,YAAY;AACjC,YAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS;AAC3D,UAAI,SAAS,EAAG,QAAO;AAAA,IAC3B;AACA,UAAM,IAAI,OAAO,sBAAsB;AACvC,UAAM,OAAO,KAAK,UAAU,KAAK,UAAU,GAAG,GAAG,MAAM,KAAK,IAAI;AAChE,WAAO,KAAK,aAAa;AAAA,MAAE;AAAA,MAAQ;AAAA,MAAM;AAAA,MAAG;AAAA,MAAM;AAAA,MACvB,YAAY,KAAK,SAAS;AAAA,IAAU,CAAC;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuIA,UAAU,GAAG,GAAG,KAAK,KAAK;AACtB,UAAM,OAAO,eAAc;AAC3B,UAAM,IAAI,KAAK,aAAa,GAAG;AAK/B,UAAM,OAAO,IAAI,KAAK,UAAU,GAAG,GAAG,IAAI;AAC1C,UAAM,UAAU,CAAC,EAAE,OAAO;AAwC1B,UAAM,OAAO,IACP;AAAA,MAAE,MAAQ,KAAK,IAAI,GAAG,EAAE,OAAO,EAAE,IAAI;AAAA,MACnC,OAAQ,KAAK,IAAI,GAAG,EAAE,QAAQ,EAAE,KAAK;AAAA,MACrC,KAAQ,KAAK,IAAI,GAAG,EAAE,MAAM,EAAE,GAAG;AAAA,MACjC,QAAQ,KAAK,IAAI,GAAG,EAAE,SAAS,EAAE,MAAM;AAAA,IAAE,IAC3C;AAAA,MAAE,MAAQ,EAAE,UAAU,EAAE;AAAA,MACtB,OAAQ,EAAE,QAAQ,EAAE;AAAA,MACpB,KAAQ,EAAE,UAAU,EAAE;AAAA,MACtB,QAAQ,EAAE,SAAS,EAAE;AAAA,IAAQ;AACrC,UAAM,SAAS,CAAC,SAAS;AACrB,UAAI,CAAC,QAAS,QAAO;AACrB,UAAI,SAAS,OAAS,QAAO,KAAK,KAAK,CAAC;AACxC,UAAI,SAAS,QAAS,QAAO,KAAK,KAAK;AACvC,UAAI,SAAS,MAAS,QAAO,KAAK,KAAK,CAAC;AACxC,aAAO,KAAK,KAAK;AAAA,IACrB;AA4BA,UAAM,QAAQ,CAAC,SAAS;AACpB,YAAM,KAAK,MAAM,KAAK,GAAG,KAAK,MAAM,KAAK;AACzC,UAAI,SAAS,OAAS,QAAO,CAAC;AAC9B,UAAI,SAAS,QAAS,QAAO;AAC7B,UAAI,SAAS,MAAS,QAAO,CAAC;AAC9B,aAAO;AAAA,IACX;AAEA,QAAI,OAAO;AACX,eAAW,QAAQ,CAAC,QAAQ,SAAS,OAAO,QAAQ,GAAG;AACnD,YAAM,IAAI,KAAK,IAAI;AACnB,UAAI,EAAE,IAAI,SAAS,CAAC,OAAO,IAAI,EAAG;AAClC,YAAM,IAAI,MAAM,IAAI;AACpB,UAAI,CAAC,QAAQ,IAAI,KAAK,KAAM,MAAM,KAAK,KAAK,IAAI,KAAK,EAAI,QAAO,EAAE,MAAM,GAAG,EAAE;AAAA,IACjF;AACA,WAAO,OAAO,KAAK,OAAO;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,aAAa,KAAK;AACd,UAAM,KAAK,KAAK;AAChB,QAAI,CAAC,MAAM,GAAG,gBAAgB,MAAO,QAAO;AAC5C,QAAI,OAAO,GAAG,0BAA0B,WAAY,QAAO;AAC3D,UAAM,IAAI,GAAG,sBAAsB;AACnC,QAAI,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,EAAE,OAAQ,QAAO;AACxC,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,UAAU,GAAG,KAAK;AACd,UAAM,KAAK,KAAK;AAChB,QAAI,CAAC,MAAM,OAAO,GAAG,WAAW,YAAY,OAAO,GAAG,WAAW,SAAU,QAAO;AAClF,WAAO,EAAE,GAAG,EAAE,UAAU,GAAG,QAAQ,GAAG,EAAE,UAAU,GAAG,OAAO;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,UAAU,GAAG,GAAG,KAAK;AACjB,UAAM,QAAQ,SAAS,kBAAkB,GAAG,CAAC;AAC7C,eAAW,MAAM,OAAO;AACpB,UAAI,KAAK,WAAW,IAAI,QAAQ,SAAS,EAAE,EAAG;AAC9C,YAAM,SAAS,GAAG,UAAU,WAAW;AACvC,UAAI,UAAU,KAAK,OAAO,SAAS,MAAM,EAAG,QAAO;AAAA,IACvD;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkCA,gBAAgB,KAAK;AACjB,UAAM,MAAM,CAAC,GAAG,KAAK,cAAc,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG;AACzE,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,OAAO,KAAK,SAAS,SAAS,IAAI,UAAU,GAAG;AACrD,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,OAAO,IAAI,aAAa,KAAK,IAAI,IAAI,UAAU,IAAI;AACzD,UAAM,SAAU,QAAQ,KAAK,SAAS,SAChC,IAAI,aACJ,KAAK,cAAc;AACzB,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,KAAK,KAAK,SAAS,OAAO,MAAM;AACtC,QAAI,CAAC,GAAI,QAAO;AAChB,UAAM,OAAO,GAAG,sBAAsB;AACtC,QAAI,CAAC,KAAK,SAAS,CAAC,KAAK,OAAQ,QAAO;AACxC,WAAO,EAAE,QAAQ,KAAK;AAAA,EAC1B;AAAA,EAEA,YAAY,OAAO,KAAK;AACpB,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,MAAM,CAAC,GAAG,KAAK,cAAc,QAAQ,CAAC,EACvC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,GAAG;AACrC,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,CAAC,OAAO,MAAM,IAAI;AACxB,UAAM,EAAE,QAAQ,YAAY,KAAK,IAAI;AA4BrC,QAAI,MAAM,SAAS,QAAQ;AASvB,UAAI,CAAC,KAAK,gBAAgB,KAAK,EAAG,QAAO;AACzC,UAAI,iBAAiB;AACrB,aAAO;AAAA,IACX;AAKA,QAAI,OAAO,eAAe,SAAU,QAAO,aAAa;AAWxD,UAAM,SAAS,KAAK,eAAe,OAAO;AAAA,MACtC;AAAA,MACA,MAAM,MAAM,QAAQ;AAAA,MACpB,KAAM,SAAS,UAAU,SAAS,UAAW,MAAM;AAAA,MACnD,QAAS,SAAS,UAAU,SAAS;AAAA,IACzC,CAAC;AAMD,QAAI,CAAC,OAAQ,QAAO;AACpB,QAAI,iBAAiB;AACrB,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,eAAe,UAAU,QAAQ;AAC7B,UAAM,MAAM,KAAK,cAAc,IAAI,QAAQ;AAC3C,QAAI,CAAC,OAAO,CAAC,QAAQ,OAAQ,QAAO;AACpC,QAAI,QAAQ,EAAE,GAAG,OAAO;AACxB,QAAI,YAAY;AAChB,QAAI;AAAE,UAAI,OAAO,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,IAAG,SAClC,KAAK;AAAE,cAAQ,KAAK,2BAA2B,GAAG;AAAG,aAAO;AAAA,IAAO;AAC1E,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BA,oBAAoB,UAAU,WAAW;AACrC,UAAM,MAAM,KAAK,cAAc,IAAI,QAAQ;AAC3C,QAAI,CAAC,IAAK;AACV,QAAI,IAAI,eAAe,UAAW;AAClC,SAAK,SAAS,YAAY,YAAY,CAAC;AACvC,QAAI,aAAa;AACjB,QAAI,CAAC,IAAI,iBAAiB,IAAI,YAAY;AACtC,YAAM,SAAS,KAAK,SAAS,SAAS,SAAS,GAAG;AAOlD,UAAI,aAAa,QAAQ,cAAc,KAAK;AAAA,IAChD;AACA,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc,cAAc;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,aAAa,MAAM,QAAQ,KAAK,EAAE,QAAQ,GAAG;AACzC,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,OAAQ,MAAM,QAAQ,IAAI,IAAI,KAAK,IAAI,KAAK,SAC5C,IAAI,OACJ,CAAC;AAAA,MAAE,MAAM,IAAI,SAAS;AAAA,MAAM,OAAO,IAAI,SAAS;AAAA,MAC7C,OAAO,IAAI,SAAS;AAAA,IAAM,CAAC;AACpC,UAAM,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,IAAI,gBAAgB,CAAC,CAAC;AAC3E,QAAI,WAAW;AACf,SAAK,QAAQ,CAAC,KAAK,MAAM;AACrB,YAAM,UAAU,EAAE,MAAM,IAAI,MAAM,OAAO,IAAI,SAAS,CAAC,EAAE;AACzD,YAAM,QAAQ,IAAI,SAAS,IAAI,QAAQ;AACvC,UAAI,MAAM,KAAK,SAAS;AACpB,aAAK,eAAe,QAAQ,SAAS,KAAK;AAC1C,mBAAW;AACX;AAAA,MACJ;AAKA,YAAM,KAAK,KAAK,cAAc,QAAQ,SAAS,KAAK;AACpD,UAAI,MAAM,KAAK,WAAW,EAAG,YAAW;AAAA,IAC5C,CAAC;AACD,QAAI,WAAW,EAAG,QAAO;AACzB,SAAK,iBAAiB,QAAQ,WAAW,MAAM;AAC/C,SAAK,MAAM,MAAM;AACjB,WAAO;AAAA,EACX;AAAA,EAEA,uBAAuB,OAAO,WAAW;AACrC,UAAM,MAAM,KAAK,cAAc,IAAI,KAAK;AACxC,SAAK,cAAc,OAAO,KAAK;AAC/B,QAAI,CAAC,IAAK;AASV,QAAI;AAAE,OAAC,IAAI,aAAa,YAAY,UAAU;AAAA,IAAG,QAAQ;AAAA,IAAC;AAC1D,QAAI;AAAE,UAAI,OAAO,QAAQ;AAAA,IAAG,QAAQ;AAAA,IAAuC;AAC3E,UAAM,OAAO,KAAK,SAAS,SAAS,IAAI,UAAU,GAAG;AACrD,QAAI,CAAC,KAAM;AAEX,QAAI,IAAI,aAAa,IAAI,OAAO;AAM5B,YAAM,SAAS,KAAK,IAAI,IAAI,MAAM,MAAM;AACxC,UAAI,UAAU,OAAO,SAAS,QAAQ;AAClC,cAAM,EAAE,MAAM,KAAK,OAAO,IAAI,IAAI;AAClC,YAAI,SAAS,OAAO;AAChB,eAAK,aAAa,MAAM,IAAI,MAAM,QAAQ,KAAK,EAAE,SAAS,MAAM,CAAC;AAAA,QACrE,WAAW,SAAS,SAAS;AACzB,gBAAM,QAAQ,KAAK,MAAM,IAAI,MAAM,QAAQ,GAAG;AAC9C,cAAI,OAAO;AACP,iBAAK,aAAa,MAAM,OAAO,KAAK,EAAE,SAAS,KAAK,CAAC;AAUrD,uBAAW,MAAM,IAAI,MAAM,QAAQ,KAAK;AAMxC,gBAAI,OAAQ,eAAc,MAAM,IAAI,MAAM,QAAQ,KAAK;AACvD,iBAAK,MAAM,KAAK;AAAA,UACpB;AAAA,QACJ,OAAO;AACH,eAAK,aAAa,MAAM,IAAI,MAAM,QAAQ,KAAK,EAAE,SAAS,KAAK,CAAC;AAAA,QACpE;AAAA,MACJ,OAAO;AACH,aAAK,aAAa,MAAM,KAAK,cAAc,GAAG,KAAK,EAAE,SAAS,MAAM,CAAC;AAAA,MACzE;AAAA,IACJ,WAAW,IAAI,WAAW;AAItB,UAAI,MAAM,KAAK,cAAc;AAC7B,UAAI,UAAU;AACd,UAAI,CAAC,KAAK;AAAE,cAAM,KAAK,kBAAkB,IAAI;AAAG,kBAAU;AAAA,MAAM;AAChE,UAAI,KAAK;AACL,aAAK,aAAa,MAAM,KAAK,KAAK,EAAE,SAAS,MAAM,CAAC;AACpD,YAAI,QAAS,MAAK,cAAc,MAAM,KAAK,SAAS,SAAS,IAAI,UAAU,CAAC;AAAA,MAChF;AAAA,IACJ;AAIA,QAAI,KAAK,SAAS,OAAO,EAAE,SAAS,KAAM,MAAK,SAAS,OAAO;AAC/D,SAAK,SAAS;AACd,SAAK,cAAc,IAAI,YAAY,mBAAmB,eAAe;AAAA,EACzE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,uBAAuB,KAAK,OAAO;AAC/B,UAAM,SAAS,IAAI,SAAS,gBAAgB,6BAA6B;AACzE,QAAI,CAAC,OAAQ;AAEb,WAAO,iBAAiB,eAAe,CAAC,MAAM;AAC1C,QAAE,eAAe;AACjB,YAAM,MAAM,KAAK,cAAc,IAAI,KAAK;AACxC,UAAI,CAAC,IAAK;AACV,YAAM,QAAQ;AAAA,QACV,EAAE,OAAO,gBAAgB,MAAM,oBAAoB,QAAQ,OAAO;AAAA,MACtE;AACA,UAAI,KAAK,SAAS,SAAS,SAAS,GAAG;AACnC,cAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAC9B,mBAAW,CAAC,GAAG,CAAC,KAAK,KAAK,SAAS,SAAS,QAAQ,GAAG;AACnD,cAAI,MAAM,IAAI,WAAY;AAC1B,gBAAM,KAAK;AAAA,YACP,OAAO,mBAAmB,EAAE,KAAK;AAAA,YAAI,MAAM;AAAA,YAC3C,QAAQ,QAAQ,CAAC;AAAA,UACrB,CAAC;AAAA,QACL;AAAA,MACJ;AACA,YAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAC9B,YAAM,KAAK;AAAA,QAAE,OAAO;AAAA,QAAgB,MAAM;AAAA,QAC7B,QAAQ;AAAA,QAAS,QAAQ;AAAA,MAAK,CAAC;AAC5C,sBAAgB,EAAE,SAAS,EAAE,SAAS,OAAO,CAAC,WAAW;AACrD,YAAI,WAAW,OAAQ,MAAK,gBAAgB,KAAK;AAAA,iBACxC,WAAW,SAAS;AACzB,cAAI;AAAE,gBAAI,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,UAAG,QAAQ;AAAA,UAAC;AAAA,QAC/C,WACS,QAAQ,aAAa,OAAO,GAAG;AACpC,eAAK,oBAAoB,OAAO,OAAO,OAAO,MAAM,CAAC,CAAC,CAAC;AAAA,QAC3D;AAAA,MACJ,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2CA,iBAAiB,MAAM,eAAe,MAAM;AACxC,QAAI,CAAC,KAAM,QAAO;AAClB,QAAI,WAAW;AAMf,eAAS;AACL,YAAM,OAAO,KAAK,mBAAmB,MAAM,YAAY;AACvD,UAAI,CAAC,KAAM;AACX,YAAM,EAAE,OAAO,SAAS,SAAS,QAAQ,OAAO,IAAI;AAKpD,YAAM,MAAM,OAAO,KAAK,MAAM,MAAM,OAAO,KAAK,MAAM,MAAM,MAAM,OAAO,KAAK;AAC9E,YAAM,WAAW,KAAK,kBAAkB;AACxC,WAAK,MAAM,MAAM;AAOjB,UAAI,KAAK,MAAM,IAAI,MAAM,EAAG;AAC5B,UAAI,SAAU,MAAK,MAAM,MAAM;AAC/B,kBAAY;AAAA,IAChB;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA,EAIA,mBAAmB,MAAM,cAAc;AACnC,eAAW,QAAQ,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC,GAAG;AACzC,UAAI,KAAK,SAAS,QAAS;AAE3B,UAAI,CAAC,KAAK,MAAM,IAAI,KAAK,EAAE,EAAG;AAC9B,eAAS,IAAI,GAAG,IAAI,KAAK,SAAS,SAAS,GAAG,KAAK,GAAG;AAClD,cAAM,IAAI,KAAK,IAAI,KAAK,SAAS,CAAC,CAAC;AACnC,cAAM,IAAI,KAAK,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC;AACvC,YAAI,CAAC,KAAK,aAAa,CAAC,KAAK,CAAC,KAAK,aAAa,CAAC,EAAG;AACpD,cAAM,SAAS,KAAK,kBAAkB,EAAE,EAAE;AAC1C,cAAM,SAAS,KAAK,kBAAkB,EAAE,EAAE;AAC1C,YAAI,UAAU,OAAQ;AACtB,YAAI,UAAU;AACd,YAAI,OAAQ,WAAU,IAAI;AAAA,iBACjB,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,MAAM,aAAc,WAAU,IAAI;AACzE,cAAM,UAAU,YAAY,IAAI,IAAI,IAAI;AACxC,eAAO;AAAA,UAAE,OAAO;AAAA,UAAM;AAAA,UAAS;AAAA,UACtB,QAAQ,KAAK,SAAS,OAAO;AAAA,UAAG,QAAQ,KAAK,SAAS,OAAO;AAAA,QAAE;AAAA,MAC5E;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA,EAIA,aAAa,MAAM;AACf,QAAI,CAAC,QAAQ,KAAK,SAAS,OAAQ,QAAO;AAC1C,UAAM,OAAO,KAAK,SAAS;AAC3B,QAAI,CAAC,QAAQ,SAAS,KAAK,SAAS,KAAM,QAAO;AACjD,QAAI,YAAY,IAAI,IAAI,KAAK,SAAS,iBAAkB,QAAO;AAC/D,SAAK,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,KAAK,EAAG,QAAO;AAClE,eAAW,QAAQ,OAAO,OAAO,KAAK,YAAY,CAAC,CAAC,GAAG;AACnD,UAAI,MAAM,QAAQ,MAAM,IAAI,KAAK,KAAK,KAAK,OAAQ,QAAO;AAAA,IAC9D;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,kBAAkB,QAAQ;AACtB,eAAW,CAAC,EAAE,GAAG,KAAK,KAAK,eAAe;AACtC,UAAI,IAAI,eAAe,OAAQ,QAAO;AAAA,IAC1C;AACA,UAAM,KAAK,KAAK,SAAS,SAAS,MAAM;AACxC,WAAO,CAAC,CAAC,IAAI,gBAAgB,qBAAqB;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,eAAe,MAAM;AACjC,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,WAAW,KAAK,iBAAiB,MAAM,YAAY;AACzD,QAAI,CAAC,SAAU,QAAO;AACtB,SAAK,cAAc,MAAM,KAAK,SAAS,OAAO,CAAC;AAC/C,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc,oBAAoB;AACvC,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAY,MAAM;AACd,UAAM,IAAI,KAAK,SAAS,OAAO;AAC/B,WAAO,CAAC,CAAC,EAAE,OAAO,IAAI;AAAA,EAC1B;AAAA,EAEA,YAAY,MAAM;AACd,UAAM,IAAI,KAAK,SAAS,OAAO;AAC/B,MAAE,OAAO,IAAI,IAAI,CAAC,EAAE,OAAO,IAAI;AAC/B,SAAK,cAAc,KAAK,MAAM,GAAG,CAAC;AAClC,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,cAAc,MAAM,SAAS;AAEzB,UAAM,QAAQ,CAAC;AACf,eAAW,QAAQ,KAAK,OAAO,GAAG;AAC9B,YAAM,OAAO,KAAK,SAAS;AAC3B,UAAI,SAAS,aAAgB,OAAM,OAAS;AAAA,eACnC,SAAS,cAAgB,OAAM,QAAS;AAAA,eACxC,SAAS,eAAgB,OAAM,SAAS;AAAA,IACrD;AAKA,UAAM,SAAS,CAAC,SAAS;AACrB,UAAI,CAAC,KAAM;AACX,YAAM,MAAM,KAAK;AACjB,UAAI,CAAC,KAAK;AACN,aAAK,SAAS;AACd,aAAK,WAAW;AAChB;AAAA,MACJ;AACA,YAAM,SAAS,KAAK,IAAI,GAAG;AAC3B,UAAI,CAAC,OAAQ;AACb,YAAM,MAAM,OAAO,SAAS,QAAQ,KAAK,EAAE;AAC3C,UAAI,OAAO,GAAG;AACV,eAAO,SAAS,OAAO,KAAK,CAAC;AAC7B,eAAO,MAAM,OAAO,KAAK,CAAC;AAAA,MAC9B;AACA,WAAK,WAAW;AAChB,WAAK,UAAU,MAAM;AAAA,IACzB;AACA,WAAO,MAAM,IAAI;AAAG,WAAO,MAAM,KAAK;AAAG,WAAO,MAAM,MAAM;AAM5D,QAAI,aAAa;AACjB,QAAI,CAAC,KAAK,QAAQ;AACd,mBAAa,QAAQ,OAAO,OAAS,SACxB,QAAQ,OAAO,SAAS,WACxB,QAAQ,OAAO,QAAS,UACxB;AACb,UAAI,YAAY;AACZ,cAAM,OAAO,MAAM,UAAU,KAAK,SAAS;AAAA,UACvC,SAAS,EAAE,MAAM,SAAS,UAAU,IAAI,OAAO,CAAC,EAAE;AAAA,UAClD,OAAO,aAAa,UAAU,KAAK;AAAA,QACvC,CAAC;AACD,YAAI,MAAM,UAAU,KAAK,aAAa,UAAU,GAAG;AAC/C,gBAAM,UAAU,EAAE,QAAQ,aAAa,UAAU;AAAA,QACrD;AACA,YAAI,CAAC,MAAM,UAAU,EAAG,MAAK,UAAU,IAAI;AAC3C,YAAI,CAAC,KAAK,MAAM,IAAI,KAAK,EAAE,EAAG,MAAK,MAAM,IAAI,KAAK,IAAI,IAAI;AAC1D,aAAK,WAAW;AAChB,aAAK,SAAS,KAAK;AAAA,MACvB;AAAA,IAGJ;AAIA,UAAM,OAAO,CAAC,WAAW,SAAS;AAE9B,UAAI,aAAa,CAAC,KAAK,MAAM,IAAI,UAAU,EAAE,GAAG;AAC5C,aAAK,MAAM,IAAI,UAAU,IAAI,SAAS;AAAA,MAC1C;AACA,YAAM,OAAO,aAAa,SAAS;AAAA,QAC/B,SAAS,EAAE,MAAM,SAAS,IAAI,IAAI,OAAO,CAAC,EAAE;AAAA,QAC5C,OAAO,aAAa,IAAI,KAAK;AAAA,MACjC,CAAC;AAED,UAAI,aAAa,aAAa,IAAI,KAAK,UAAU,UAAU,aAAa,IAAI,GAAG;AAC3E,kBAAU,QAAQ,aAAa,IAAI;AAAA,MACvC;AACA,UAAI,CAAC,UAAW,MAAK,UAAU,IAAI;AACnC,YAAM,SAAU,SAAS;AACzB,YAAM,MAAO,SAAS,WAAY,MAAM;AACxC,YAAM,WAAW,SAAS,CAAC,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC,KAAK,QAAQ,KAAK,EAAE;AACxE,YAAM,QAAS,SAAS,WAAY,CAAC,GAAG,CAAC,IAC1B,SAAS,SAAY,CAAC,GAAG,CAAC,IACL,CAAC,GAAG,CAAC;AACzC,YAAM,QAAQ;AAAA,QACV,IAAI,eAAe,IAAI,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,QACjE,MAAM;AAAA,QAAS,UAAU;AAAA,QAAM;AAAA,QAAK;AAAA,QAAU;AAAA,MAClD;AACA,WAAK,MAAM,IAAI,MAAM,IAAI,KAAK;AAC9B,YAAM,UAAU,KAAK,IAAI,KAAK,MAAM;AACpC,UAAI,QAAS,SAAQ,WAAW,MAAM;AACtC,WAAK,WAAW,MAAM;AACtB,WAAK,SAAS,MAAM;AAAA,IACxB;AAIA,QAAI,QAAQ,OAAO,QAAU,eAAe,UAAY,KAAK,OAAQ,MAAK,MAAM,MAAQ,MAAM;AAC9F,QAAI,QAAQ,OAAO,SAAU,eAAe,WAAY,KAAK,OAAQ,MAAK,MAAM,OAAQ,OAAO;AAC/F,QAAI,QAAQ,OAAO,UAAU,eAAe,YAAY,KAAK,OAAQ,MAAK,MAAM,QAAQ,QAAQ;AAIhG,UAAM,YAAY,oBAAI,IAAI;AAC1B,UAAM,OAAO,CAAC,OAAO;AACjB,UAAI,CAAC,MAAM,UAAU,IAAI,EAAE,EAAG;AAC9B,gBAAU,IAAI,EAAE;AAChB,YAAM,IAAI,KAAK,IAAI,EAAE;AACrB,UAAI,GAAG,SAAS,QAAS,GAAE,SAAS,QAAQ,IAAI;AAAA,IACpD;AACA,SAAK,KAAK,MAAM;AAChB,eAAW,MAAM,CAAC,GAAG,KAAK,MAAM,KAAK,CAAC,GAAG;AACrC,UAAI,CAAC,UAAU,IAAI,EAAE,EAAG,MAAK,MAAM,OAAO,EAAE;AAAA,IAChD;AAAA,EACJ;AAAA,EAEA,eAAe,MAAM;AACjB,UAAM,OAAO,SAAS,IAAI;AAC1B,eAAW,QAAQ,KAAK,MAAM,EAAE,OAAO,GAAG;AACtC,UAAI,KAAK,SAAS,SAAS,KAAM,QAAO;AAAA,IAC5C;AACA,WAAO;AAAA,EACX;AAAA;AAAA,EAGA,cAAc,KAAK;AACf,SAAK,SAAS,YAAY,MAAM,CAAC;AACjC,QAAI,CAAC,KAAK,SAAS,SAAS,GAAG,EAAG;AAClC,UAAM,IAAI,KAAK,SAAS,OAAO;AAO/B,SAAK,cAAc,EAAE,MAAM,CAAC;AAC5B,SAAK,SAAS,OAAO,EAAE;AACvB,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc;AAAA,EACvB;AAAA,EAEA,aAAa;AACT,SAAK,SAAS,WAAW;AACzB,SAAK,cAAc;AACnB,SAAK,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,KAAK;AACd,UAAM,IAAI,KAAK;AACf,UAAM,IAAI,EAAE,SAAS;AACrB,QAAI,KAAK,EAAG;AACZ,UAAM,QAAQ,EAAE,aAAa,MAAM,IAAI,KAAK,KAAK,KAAK;AACtD,SAAK,cAAc,IAAI;AAAA,EAC3B;AAAA;AAAA;AAAA,EAIA,gBAAgB,KAAK;AACjB,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,SAAS,KAAK;AACpB,UAAM,OAAO,SAAS,KAAK,IAAI,MAAM,IAAI;AACzC,UAAM,OAAQ,QAAQ,MAAM,QAAQ,KAAK,IAAI,IAAK,KAAK,OAAO,CAAC;AAC/D,QAAI,KAAK,UAAU,EAAG;AACtB,UAAM,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,KAAK,gBAAgB,CAAC,CAAC;AACzE,UAAM,QAAQ,OAAO,MAAM,IAAI,KAAK,KAAK,KAAK,UAAU,KAAK;AAC7D,SAAK,eAAe,QAAQ,UAAU,EAAE,KAAK,KAAK,CAAC;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,cAAc,KAAK;AACf,UAAM,IAAI,KAAK;AACf,QAAI,EAAE,SAAS,UAAU,EAAG,QAAO;AACnC,QAAI,MAAM,KAAK,OAAO,EAAE,SAAS,OAAQ,QAAO;AAChD,MAAE,SAAS,OAAO,KAAK,CAAC;AASxB,QAAI,MAAM,EAAE,UAAW,GAAE,aAAa;AACtC,QAAI,EAAE,aAAa,EAAE,SAAS,OAAQ,GAAE,YAAY,EAAE,SAAS,SAAS;AAGxE,eAAW,CAAC,EAAE,GAAG,KAAK,KAAK,eAAe;AACtC,UAAI,IAAI,eAAe,KAAK;AACxB,YAAI,aAAa,EAAE;AASnB,YAAI,CAAC,IAAI,iBAAiB,IAAI,YAAY;AACtC,cAAI,aAAa,EAAE,OAAO,EAAE,KAAK,cAAc,KAAK;AAAA,QACxD;AAAA,MACJ,WAAW,IAAI,aAAa,KAAK;AAC7B,YAAI,cAAc;AAAA,MACtB;AAAA,IACJ;AACA,SAAK,SAAS,OAAO,EAAE,OAAO,EAAE;AAChC,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc;AACnB,WAAO;AAAA,EACX;AAAA,EAEA,qBAAqB,KAAK;AACtB,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,UAAU,KAAK,QAAQ;AAC7B,QAAI,CAAC,WAAW,CAAC,QAAQ,QAAS;AAMlC,UAAM,cAAc,KAAK,UAAU,aAAa,QAAQ,EAAE,GAAG;AAC7D,QAAI,gBAAgB,SAAS,aAAa,aAAa,KAAM;AAC7D,UAAM,UAAU,EAAE,MAAM,QAAQ,QAAQ,MAAM,OAAO,QAAQ,QAAQ,OAAO,OAAO,QAAQ,MAAM;AACjG,SAAK,SAAS,YAAY,MAAM,CAAC;AACjC,UAAM,SAAS,KAAK,SAAS,SAAS,GAAG;AAEzC,UAAM,aAAa,OAAO;AAC1B,UAAM,UAAU,WAAW,cAAc;AACzC,QAAI,QAAS,YAAW,eAAe,SAAS,EAAE,MAAM,QAAQ,MAAM,OAAO,QAAQ,MAAM,GAAG,QAAQ,KAAK;AAE3G,SAAK,MAAM,QAAQ,EAAE;AACrB,QAAI,CAAC,KAAK,OAAQ,MAAK,QAAQ,SAAS,KAAK,UAAU,CAAC,CAAC;AAMzD,QAAI,CAAC,KAAK,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,EAAE,WAAW,QAAQ,CAAC,GAAG;AACjF,YAAM,UAAU,KAAK,kBAAkB,IAAI;AAC3C,UAAI,QAAS,MAAK,UAAU,MAAM,OAAO;AAAA,IAC7C;AACA,SAAK,cAAc,MAAM,KAAK,SAAS,OAAO,CAAC;AAC/C,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc;AAAA,EACvB;AAAA;AAAA,EAGA,QAAQ;AAAE,WAAO,KAAK,SAAS,OAAO,EAAE;AAAA,EAAM;AAAA,EAE9C,YAAY,QAAQ,QAAQ;AACxB,UAAM,OAAO,KAAK,MAAM;AACxB,SAAK,MAAM,MAAM;AACjB,QAAI,WAAW,QAAS,MAAK,aAAa;AAAA,aACjC,WAAW,UAAW,MAAK,qBAAqB;AAAA,aAGhD,WAAW,UAAW,MAAK,MAAM,GAAG;AAAA,aACpC,WAAW,UAAW,MAAK,MAAM,GAAG;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,QAAQ,QAAQ,OAAO,CAAC,GAAG;AACtC,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,QAAI,CAAC,QAAQ,KAAK,SAAS,OAAQ;AACnC,QAAI,WAAW,UAAU;AACrB,WAAK,iBAAiB,QAAQ,KAAK,GAAG;AACtC,WAAK,MAAM,MAAM;AACjB,WAAK,SAAS,OAAO;AACrB,WAAK,SAAS;AACd,WAAK,cAAc,YAAY;AAC/B;AAAA,IACJ;AACA,QAAI,WAAW,SAAS;AACpB,YAAM,OAAO,KAAK,QAAQ,CAAC;AAG3B,UAAI,KAAK,UAAU,GAAG;AAClB,aAAK,MAAM,MAAM;AACjB,aAAK,aAAa;AAClB;AAAA,MACJ;AACA,WAAK,cAAc,QAAQ,KAAK,GAAG;AACnC,WAAK,MAAM,MAAM;AACjB,WAAK,SAAS,OAAO;AACrB,WAAK,SAAS;AACd,WAAK,cAAc,eAAe;AAClC;AAAA,IACJ;AACA,QAAI,WAAW,QAAQ;AACnB,WAAK,YAAY,QAAQ,KAAK,MAAM,KAAK,EAAE;AAC3C,WAAK,SAAS,OAAO;AACrB,WAAK,SAAS;AACd,WAAK,cAAc,UAAU;AAC7B;AAAA,IACJ;AACA,QAAI,WAAW,QAAQ;AACnB,WAAK,oBAAoB,QAAQ,KAAK,KAAK,KAAK,GAAG,KAAK,CAAC;AACzD;AAAA,IACJ;AACA,QAAI,WAAW,aAAa;AAIxB,WAAK,iBAAiB,QAAQ,KAAK,GAAG;AACtC;AAAA,IACJ;AACA,QAAI,WAAW,aAAa;AAQxB,WAAK,YAAY,QAAQ,KAAK,KAAK,KAAK,MAAM;AAC9C;AAAA,IACJ;AACA,QAAI,WAAW,gBAAgB;AAC3B,WAAK,eAAe,QAAQ,KAAK,GAAG;AACpC,WAAK,SAAS,OAAO;AACrB,WAAK,SAAS;AACd,WAAK,cAAc,kBAAkB;AACrC;AAAA,IACJ;AACA,QAAI,WAAW,eAAe;AAC1B,WAAK,eAAe,QAAQ,KAAK,GAAG;AACpC,WAAK,SAAS,OAAO;AACrB,WAAK,SAAS;AACd,WAAK,cAAc,iBAAiB;AACpC;AAAA,IACJ;AACA,QAAI,WAAW,cAAc;AACzB,WAAK,gBAAgB,QAAQ,KAAK,GAAG;AACrC,WAAK,SAAS,OAAO;AACrB,WAAK,SAAS;AACd,WAAK,cAAc,gBAAgB;AACnC;AAAA,IACJ;AACA,QAAI,WAAW,aAAa;AAKxB,UAAI;AACA,aAAK,KAAK,gBAAgB,QAAQ,KAAK,GAAG,KAAK,CAAC;AAAA,MACpD,SAAS,KAAK;AACV,gBAAQ,KAAK,6BAA6B,GAAG;AAAA,MACjD;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0CA,SAAS,MAAM,QAAQ,CAAC,GAAG,OAAO,CAAC,GAAG;AAClC,UAAM,EAAE,MAAM,MAAM,YAAY,MAAM,IAAI;AAE1C,UAAM,aAAa,CAAC,CAAC,KAAK;AAE1B,QAAI,EAAE,OAAO,QAAQ,SAAS,MAAM,IAAI;AACxC,QAAI,KAAK,UAAU,MAAM;AACrB,cAAQ,KAAK,QAAQ;AAAA,QACjB,KAAK;AAAW,iBAAO;AAAW,mBAAS;AAAO;AAAA,QAClD,KAAK;AAAW,iBAAO;AAAW,mBAAS;AAAO;AAAA,QAClD,KAAK;AAAW,iBAAO;AAAW,mBAAS;AAAO;AAAA,QAClD,KAAK;AAAW,iBAAO;AAAW;AAAA,QAClC,KAAK;AAAW,iBAAO;AAAW;AAAA,QAClC,KAAK;AAAA,QACL;AAAgB,iBAAO;AAAW,mBAAS;AAAO;AAAA,MACtD;AAAA,IACJ;AAKA,QAAI,SAAS,aAAa,SAAS,WAAW;AAC1C,YAAM,MAAM,SAAS,YAAY,MAAM;AACvC,YAAM,SAAS,KAAK,UAAU,KAAK,MAAM,EAAE;AAC3C,aAAO,KAAK,cAAc,QAAQ,KAAK,MAAM,OAAO,UAAU,MAAM,KAAK,CAAC;AAAA,IAC9E;AACA,QAAI,SAAS,SAAU,QAAO,KAAK,gBAAgB,MAAM,KAAK;AAC9D,QAAI,SAAS,QAAQ;AACjB,aAAO,SACD,KAAK,mBAAmB,MAAM,OAAO,WAAW,UAAU,IAC1D,KAAK,cAAc,MAAM,KAAK;AAAA,IACxC;AAEA,WAAO,SACD,KAAK,aAAa,KAAK,MAAM,OAAO,WAAW,UAAU,IACzD,KAAK,cAAc,KAAK,MAAM,KAAK;AAAA,EAC7C;AAAA,EAEA,cAAc,KAAK,MAAM,OAAO;AAE5B,QAAI,KAAK,YAAY,KAAK,cAAc,IAAI,IAAI,QAAQ,GAAG;AACvD,WAAK,aAAa,IAAI,UAAU,MAAM,KAAK;AAC3C;AAAA,IACJ;AAIA,QAAI,KAAK,QAAQ;AACb,YAAM,OAAO,KAAK,MAAM;AACxB,YAAM,OAAO,KAAK,IAAI,IAAI,MAAM;AAChC,YAAM,IAAI,MAAM,SAAS;AACzB,UAAI,QAAQ,KAAK,CAAC,EAAE,WAAW,QAAQ,GAAG;AACtC,aAAK,WAAW,IAAI,QAAQ,MAAM,KAAK;AACvC;AAAA,MACJ;AAAA,IACJ;AAGA,SAAK,cAAc,MAAM,KAAK;AAAA,EAClC;AAAA,EAEA,aAAa,KAAK,MAAM,OAAO,YAAY,OAAO,aAAa,OAAO;AAGlE,QAAI,KAAK,YAAY,KAAK,cAAc,IAAI,IAAI,QAAQ,GAAG;AACvD,WAAK,aAAa,IAAI,UAAU,MAAM,KAAK;AAC3C;AAAA,IACJ;AACA,SAAK,qBAAqB,OAAO,CAAC,GAAG,MAAM,OAAO,WAAW,UAAU;AAAA,EAC3E;AAAA;AAAA;AAAA,EAIA,gBAAgB,MAAM,OAAO;AACzB,UAAM,QAAQ,UAAU,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AACzF,UAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,cAAU,YAAY;AACtB,cAAU,MAAM,UAAU;AAC1B,UAAM,QAAQ,UAAU,MAAM,KAAK;AACnC,UAAM,YAAY,KAAK,QAAQ;AAAA,MAAM;AAAA,MAAM;AAAA,MAAW;AAAA,MAClD,EAAE,GAAG,KAAK,KAAK,IAAI,MAAM,UAAU,MAAM;AAAA,IAAC;AAC9C,UAAM,MAAM,IAAI,cAAc;AAAA,MAC1B,IAAI;AAAA,MACJ,OAAO,WAAW,SAAS;AAAA,MAC3B,MAAM;AAAA,MACN,SAAS;AAAA,MACT,aAAa;AAAA,MACb,aAAa;AAAA,MACb,WAAW;AAAA,MACX,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMP,MAAM,KAAK;AAAA,MACX,gBAAgB,KAAK,gBAAgB,KAAK,gBAAgB,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAU9D,UAAU,MAAM,KAAK;AAAA,MACrB,YAAY,MAAM,KAAK,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAOnC,YAAY,MAAM,KAAK,gBAAgB,KAAK;AAAA,MAC5C,cAAc;AAAA,MACd,eAAe;AAAA,MACf,SAAS,MAAM,KAAK,uBAAuB,OAAO,SAAS;AAAA,IAC/D,CAAC;AAOD,SAAK,cAAc,IAAI,OAAO;AAAA,MAC1B,QAAQ;AAAA,MACR,YAAY,KAAK,SAAS;AAAA,MAC1B,UAAU;AAAA,QAAE;AAAA,QAAM,OAAO,EAAE,GAAI,SAAS,CAAC,EAAG;AAAA,QAChC,OAAO,WAAW,SAAS;AAAA,MAAM;AAAA,MAC7C;AAAA,MAAW,QAAQ;AAAA,MAAK;AAAA,MACxB,WAAW;AAAA,IACf,CAAC;AACD,QAAI,KAAK;AAMT,UAAM,KAAK,KAAK,cAAc;AAC9B,QAAI,KAAK,KAAK,OAAO,IAAI,mBAAmB,YAAY;AACpD,YAAM,QAAS,KAAK,KAAK,IAAK;AAC9B,UAAI,KAAK,IAAI,KAAK,KAAK;AACvB,UAAI,KAAK,IAAI,KAAK,KAAK;AACvB,UAAI,eAAe;AAAA,IACvB;AACA,SAAK,uBAAuB,KAAK,KAAK;AACtC,SAAK,cAAc,gBAAgB;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,oBAAoB,QAAQ,KAAK,GAAG,GAAG;AACnC,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,QAAI,CAAC,QAAQ,KAAK,SAAS,OAAQ;AACnC,UAAM,OAAO,KAAK,QAAQ,CAAC;AAC3B,QAAI,KAAK,WAAW,EAAG;AACvB,UAAM,QAAQ,CAAC;AAaf,UAAM,MAAM,KAAK,GAAG;AACpB,QAAI,OAAO,CAAC,OAAO,IAAI,QAAQ,EAAE,EAAE,WAAW,QAAQ,KAC3C,IAAI,SAAS,kBAAkB;AACtC,YAAM,KAAK,EAAE,OAAO,oBAAoB,MAAM,aAAa,QAAQ,YAAY,CAAC;AAChF,YAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,IAClC;AACA,UAAM,KAAK,EAAE,OAAO,aAAa,MAAM,SAAS,QAAQ,QAAQ,CAAC;AACjE,QAAI,KAAK,SAAS,GAAG;AACjB,YAAM,KAAK,EAAE,OAAO,oBAAoB,MAAM,aAAa,QAAQ,eAAe,CAAC;AAAA,IACvF;AACA,QAAI,MAAM,KAAK,SAAS,GAAG;AACvB,YAAM,KAAK;AAAA,QAAE,OAAO;AAAA,QAA2B,MAAM;AAAA,QACxC,QAAQ;AAAA,MAAc,CAAC;AAAA,IACxC;AACA,QAAI,MAAM,GAAG;AACT,YAAM,KAAK;AAAA,QAAE,OAAO;AAAA,QAA0B,MAAM;AAAA,QACvC,QAAQ;AAAA,MAAa,CAAC;AAAA,IACvC;AACA,oBAAgB,GAAG,GAAG,OAAO,CAAC,WAAW;AACrC,WAAK,eAAe,QAAQ,QAAQ,EAAE,IAAI,CAAC;AAAA,IAC/C,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,qBAAqB,KAAK,MAAM,QAAQ,CAAC,GAAG,YAAY,OAAO,aAAa,OAAO;AAG/E,QAAI,KAAK,YAAY,KAAK,cAAc,IAAI,IAAI,QAAQ,GAAG;AACvD,WAAK,aAAa,IAAI,UAAU,MAAM,KAAK;AAC3C;AAAA,IACJ;AACA,UAAM,OAAO,KAAK,MAAM;AACxB,QAAI,SAAS,KAAK;AAClB,QAAI,CAAC,OAAQ,UAAS,KAAK,cAAc;AACzC,UAAM,OAAO,SAAS,KAAK,IAAI,MAAM,IAAI;AACzC,UAAM,IAAI,MAAM,SAAS;AACzB,QAAI,CAAC,QAAQ,CAAC,UAAW,KAAK,EAAE,WAAW,QAAQ,GAAI;AAGnD,WAAK,cAAc,MAAM,KAAK;AAC9B;AAAA,IACJ;AACA,SAAK;AAAA,MAAc;AAAA,MAAQ,EAAE,MAAM,MAAM;AAAA,MAAG,UAAU,MAAM,KAAK;AAAA,MAC7D,EAAE,WAAW,WAAW;AAAA,IAAC;AAG7B,QAAI,CAAC,WAAY,MAAK,MAAM,MAAM;AAClC,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc,UAAU;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,mBAAmB,MAAM,QAAQ,CAAC,GAAG,YAAY,OAAO,aAAa,OAAO;AACxE,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,SAAS,KAAK,cAAc;AAKlC,QAAI,CAAC,QAAQ;AAAE,WAAK,gBAAgB,MAAM,KAAK;AAAG;AAAA,IAAQ;AAC1D,SAAK;AAAA,MAAc;AAAA,MAAQ,EAAE,MAAM,MAAM;AAAA,MAAG,UAAU,MAAM,KAAK;AAAA,MAC7D,EAAE,WAAW,WAAW;AAAA,IAAC;AAG7B,QAAI,CAAC,WAAY,MAAK,MAAM,MAAM;AAClC,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS;AACd,SAAK,cAAc,UAAU;AAAA,EACjC;AACJ;AAQA,SAAS,UAAU,MAAM,OAAO;AAC5B,MAAI,SAAS,OAAO,MAAM,UAAU,YAAY,MAAM,MAAO,QAAO,MAAM;AAC1E,MAAI,UAAU,OAAO,MAAM,OAAO,YAAY,OAAO,MAAM,OAAO,aAC3D,OAAO,MAAM,EAAE,EAAG,QAAO,OAAO,MAAM,EAAE;AAC/C,SAAO,QAAQ;AACnB;AAOA,SAAS,cAAc,MAAM;AACzB,QAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC;AACrD,MAAI,KAAK,QAAQ;AACb,WAAO,KAAK,IAAI,CAAC,OAAO;AAAA,MACpB,MAAM,EAAE;AAAA,MAAM,OAAO,EAAE,GAAI,EAAE,SAAS,CAAC,EAAG;AAAA,MAC1C,OAAO,EAAE,SAAS,EAAE,QAAQ;AAAA,IAChC,EAAE;AAAA,EACN;AACA,SAAO,CAAC;AAAA,IACJ,MAAM,KAAK,QAAQ;AAAA,IACnB,OAAO,EAAE,GAAI,KAAK,QAAQ,SAAS,CAAC,EAAG;AAAA,IACvC,OAAO,KAAK,SAAS,KAAK,QAAQ,QAAQ;AAAA,EAC9C,CAAC;AACL;AAMA,SAAS,QAAQ,GAAG,MAAM;AACtB,QAAM,IAAI,KAAK,MAAM,EAAE,QAAQ,CAAC;AAChC,QAAM,IAAI,KAAK,MAAM,EAAE,SAAS,CAAC;AACjC,UAAQ,MAAM;AAAA,IACV,KAAK;AAAU,aAAO,EAAE,MAAM,EAAE,MAAc,KAAK,EAAE,KAAS,OAAO,GAAS,QAAQ,EAAE,OAAO;AAAA,IAC/F,KAAK;AAAU,aAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,KAAK,EAAE,KAAK,OAAO,GAAO,QAAQ,EAAE,OAAO;AAAA,IAC/F,KAAK;AAAU,aAAO,EAAE,MAAM,EAAE,MAAc,KAAK,EAAE,KAAS,OAAO,EAAE,OAAO,QAAQ,EAAE;AAAA,IACxF,KAAK;AAAU,aAAO,EAAE,MAAM,EAAE,MAAM,KAAK,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,EAAE,OAAO,QAAQ,EAAE;AAAA,IAC3F;AAAe,aAAO,EAAE,MAAM,EAAE,MAAM,KAAK,EAAE,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,OAAO;AAAA,EACvF;AACJ;AAKA,SAAS,WAAW,MAAM,UAAU,OAAO;AACvC,QAAM,MAAM,KAAK,IAAI,QAAQ;AAC7B,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,SAAS,KAAK,IAAI,IAAI,QAAQ;AACpC,MAAI,CAAC,UAAU,OAAO,SAAS,QAAS,QAAO;AAC/C,QAAM,IAAI,OAAO,SAAS,QAAQ,QAAQ;AAC1C,QAAM,IAAI,OAAO,SAAS,QAAQ,KAAK;AACvC,MAAI,IAAI,KAAK,IAAI,EAAG,QAAO;AAI3B,QAAM,SAAS,OAAO,MAAM,CAAC,KAAK,KAAK;AACvC,SAAO,MAAM,CAAC,IAAI;AAClB,SAAO,MAAM,CAAC,IAAI;AAClB,SAAO;AACX;AAKA,SAAS,cAAc,MAAM,KAAK,KAAK;AACnC,QAAM,IAAI,KAAK,IAAI,GAAG;AACtB,QAAM,IAAI,KAAK,IAAI,GAAG;AACtB,MAAI,CAAC,KAAK,CAAC,KAAK,EAAE,aAAa,EAAE,SAAU,QAAO;AAClD,QAAM,SAAS,KAAK,IAAI,EAAE,QAAQ;AAClC,MAAI,CAAC,UAAU,OAAO,SAAS,QAAS,QAAO;AAC/C,QAAM,IAAI,OAAO,SAAS,QAAQ,GAAG;AACrC,QAAM,IAAI,OAAO,SAAS,QAAQ,GAAG;AACrC,MAAI,IAAI,KAAK,IAAI,EAAG,QAAO;AAC3B,SAAO,SAAS,CAAC,IAAI;AACrB,SAAO,SAAS,CAAC,IAAI;AAMrB,QAAM,OAAO,OAAO,MAAM,CAAC;AAC3B,SAAO,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC;AAChC,SAAO,MAAM,CAAC,IAAI;AAClB,SAAO;AACX;;;ACrhHA,IAAM,YAAY;AAMX,SAAS,gBAAgB;AAAA,EAC5B;AAAA,EAAG;AAAA,EAAG;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAK;AAAA,EAAU;AAC3C,GAAG;AACC,MAAI,CAAC,YAAY,CAAC,UAAU;AACxB,YAAQ,MAAM,wDAAwD;AACtE,WAAO,MAAM;AAAA,IAAC;AAAA,EAClB;AAIA,QAAM,SAAU,SAAS,UAAU,QAAQ,KAAK;AAChD,QAAM,UAAU,SAAS,WAAW,MAAM,EACrC,IAAI,CAAC,QAAQ,SAAS,IAAI,GAAG,CAAC,EAC9B,OAAO,OAAO;AAGnB,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY;AACpB,UAAQ,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBpB,WAAS,KAAK,YAAY,OAAO;AAIjC,QAAM,QAAQ,QAAQ,cAAc,mBAAmB;AACvD,QAAM,MAAM,WAAW;AACvB,QAAM,MAAM,OAAO,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,aAAa,KAAK,CAAC,CAAC,CAAC;AACvE,QAAM,MAAM,MAAO,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,cAAc,KAAK,IAAI,MAAM,EAAE,CAAC,CAAC;AAEnF,MAAI,QAAQ;AACZ,MAAI,SAAS;AACb,MAAI,UAAU;AACd,MAAI,gBAAgB,CAAC;AAErB,QAAM,eAAe,IAAI,IAAI,QAAQ,IAAI,CAAC,MACtC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;AAEnD,QAAM,QAAQ,MAAM;AAChB,QAAI,CAAC,MAAO;AACZ,YAAQ;AACR,aAAS,oBAAoB,aAAa,WAAW,IAAI;AACzD,aAAS,oBAAoB,WAAW,OAAO,IAAI;AACnD,YAAQ,OAAO;AAAA,EACnB;AACA,QAAM,YAAY,CAAC,OAAO;AACtB,QAAI,CAAC,QAAQ,SAAS,GAAG,MAAM,EAAG,OAAM;AAAA,EAC5C;AAEA,QAAM,aAAa,CAAC,UAAU;AAC1B,QAAI,cAAc,WAAW,EAAG;AAChC,cAAU,KAAK,IAAI,GAAG,KAAK,IAAI,cAAc,SAAS,GAAG,UAAU,KAAK,CAAC;AACzE,iBAAa;AAAA,EACjB;AACA,QAAM,aAAa,CAAC,UAAU;AAC1B,QAAI,cAAc,WAAW,EAAG;AAChC,cAAU,QAAQ,cAAc,SAAS,IAAI;AAC7C,iBAAa;AAAA,EACjB;AACA,QAAM,aAAa,MAAM;AACrB,UAAM,MAAM,cAAc,OAAO;AACjC,QAAI,CAAC,IAAK;AACV,UAAM;AACN,QAAI;AAAE,eAAS,IAAI,SAAS,IAAI,MAAM;AAAA,IAAG,SAClC,KAAK;AAAE,cAAQ,KAAK,+BAA+B,GAAG;AAAA,IAAG;AAAA,EACpE;AAEA,QAAM,QAAQ,CAAC,OAAO;AAClB,QAAI,CAAC,MAAO;AAEZ,QAAI,GAAG,YAAa;AACpB,YAAQ,GAAG,KAAK;AAAA,MACZ,KAAK;AACD,WAAG,eAAe;AAAG,cAAM;AAAG;AAAA,MAClC,KAAK;AACD,WAAG,eAAe;AAAG,mBAAW,CAAE;AAAG;AAAA,MACzC,KAAK;AACD,WAAG,eAAe;AAAG,mBAAW,EAAE;AAAG;AAAA,MACzC,KAAK;AACD,WAAG,eAAe;AAAG,mBAAW,KAAK;AAAG;AAAA,MAC5C,KAAK;AACD,WAAG,eAAe;AAAG,mBAAW,IAAI;AAAG;AAAA,MAC3C,KAAK;AACD,WAAG,eAAe;AAAG,mBAAW;AAAG;AAAA,MACvC,KAAK,YAAY;AACb,YAAI,OAAQ;AACZ,WAAG,eAAe;AAClB,cAAM,MAAM,cAAc,OAAO;AACjC,cAAM,KAAM,MAAM,aAAa,IAAI,IAAI,OAAO,IAAI;AAClD,YAAI,IAAI;AACJ,gBAAM,QAAQ,cAAc,IAAI,OAAO;AACvC,cAAI,GAAG,OAAO,QAAQ,GAAG;AAAE,eAAG,QAAQ;AAAG,oBAAQ;AAAA,UAAG;AAAA,QACxD;AACA;AAAA,MACJ;AAAA,MACA,KAAK,UAAU;AACX,YAAI,OAAQ;AACZ,WAAG,eAAe;AAClB,cAAM,MAAM,cAAc,OAAO;AACjC,cAAM,KAAM,MAAM,aAAa,IAAI,IAAI,OAAO,IAAI;AAClD,YAAI,MAAM,GAAG,OAAO,GAAG;AAAE,aAAG,QAAQ;AAAG,kBAAQ;AAAA,QAAG;AAClD;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACA,WAAS,iBAAiB,aAAa,WAAW,IAAI;AACtD,WAAS,iBAAiB,WAAW,OAAO,IAAI;AAChD,UAAQ,cAAc,uBAAuB,EACxC,iBAAiB,SAAS,KAAK;AAEpC,QAAM,cAAc,QAAQ,cAAc,sBAAsB;AAChE,cAAY,iBAAiB,SAAS,MAAM;AACxC,aAAS,YAAY,MAAM,KAAK,EAAE,YAAY;AAC9C,eAAW,MAAM,aAAa,OAAO,EAAG,IAAG,OAAO;AAClD,cAAU;AACV,YAAQ;AAAA,EACZ,CAAC;AAED,QAAM,SAAS,QAAQ,cAAc,oBAAoB;AAEzD,QAAM,gBAAgB,CAAC,YAAY;AAC/B,UAAM,KAAK,aAAa,IAAI,OAAO;AACnC,QAAI,CAAC,GAAI,QAAO;AAChB,UAAM,SAAS,SACT,GAAG,OAAO,OAAO,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,IACjD,GAAG,QAAQ;AACjB,WAAO,KAAK,IAAI,GAAG,KAAK,KAAK,QAAQ,SAAS,CAAC;AAAA,EACnD;AAEA,QAAM,UAAU,MAAM;AAClB,QAAI,CAAC,MAAO;AACZ,oBAAgB,CAAC;AACjB,UAAM,WAAW,CAAC;AAClB,eAAW,OAAO,SAAS;AACvB,YAAM,KAAK,aAAa,IAAI,IAAI,OAAO;AACvC,YAAM,OAAO,SAAS,KAAK,IAAI,OAAO;AACtC,YAAM,MAAM,GAAG;AACf,YAAM,WAAW,SACX,IAAI,OAAO,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,IAC3C;AACN,YAAM,QAAQ,SAAS;AAGvB,UAAI;AACJ,UAAI,OAAO;AACX,UAAI,QAAQ;AACZ,UAAI,QAAQ;AACR,eAAO;AAAA,MACX,OAAO;AACH,gBAAQ,KAAK,IAAI,GAAG,KAAK,KAAK,QAAQ,SAAS,CAAC;AAChD,eAAQ,KAAK,IAAI,GAAG,MAAM,QAAQ,CAAC;AACnC,cAAM,QAAQ,OAAO;AACrB,eAAQ,SAAS,MAAM,OAAO,QAAQ,SAAS;AAAA,MACnD;AAEA,iBAAW,UAAU,MAAM;AACvB,sBAAc,KAAK,EAAE,SAAS,IAAI,SAAS,OAAO,CAAC;AAAA,MACvD;AACA,eAAS,KAAK,EAAE,KAAK,MAAM,MAAM,OAAO,MAAM,MAAM,CAAC;AAAA,IACzD;AACA,QAAI,WAAW,cAAc,QAAQ;AACjC,gBAAU,KAAK,IAAI,GAAG,cAAc,SAAS,CAAC;AAAA,IAClD;AACA,QAAI,UAAU;AACd,WAAO,YAAY,SAAS,IAAI,CAAC,MAAM;AACnC,YAAM,QAAQ,EAAE,KAAK,IAAI,CAAC,WAAW;AACjC,cAAM,MAAM;AACZ,cAAM,QAAQ,OAAO,SAAS,OAAO,MAAM;AAC3C,cAAM,OAAQ,OAAO,QAAS;AAC9B,eAAO;AAAA,uDACgC,QAAQ,UAAU,oCAAoC,EAAE;AAAA,yCACtE,GAAG;AAAA,qEACyBE,MAAK,KAAK,CAAC;AAAA,0BACtD,OAAO,6CAA6CA,MAAK,IAAI,CAAC,YAAY,EAAE;AAAA;AAAA;AAAA,MAG1F,CAAC,EAAE,KAAK,EAAE;AACV,aAAO;AAAA;AAAA,yCAEsBA,MAAK,EAAE,IAAI,OAAO,CAAC;AAAA;AAAA,kEAEMA,MAAK,EAAE,MAAM,QAAQ,aAAa,CAAC;AAAA,wEAC7BA,MAAK,EAAE,MAAM,SAAS,EAAE,IAAI,OAAO,CAAC;AAAA,wEACpC,EAAE,KAAK;AAAA;AAAA,sBAEzD,EAAE,KAAK,WAAW,IAAI;AAAA,+DAEhB,SAAS,gBAAgB,oBAC7B;AAAA,wBACA,sCAAsC,KAAK,OAAO;AAAA,sBACnD,CAAC,UAAU,EAAE,QAAQ,IAAK;AAAA;AAAA;AAAA,oDAGGA,MAAK,EAAE,IAAI,OAAO,CAAC;AAAA,sCACjC,EAAE,SAAS,IAAI,aAAa,EAAE;AAAA,oCAChC,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK;AAAA;AAAA,oDAEPA,MAAK,EAAE,IAAI,OAAO,CAAC;AAAA,sCACjC,EAAE,QAAQ,EAAE,QAAQ,IAAI,aAAa,EAAE;AAAA;AAAA,wBAErD,EAAE;AAAA;AAAA;AAAA,IAGlB,CAAC,EAAE,KAAK,EAAE;AACV,WAAO,iBAAiB,yBAAyB,EAAE,QAAQ,CAAC,OAAO;AAC/D,YAAM,MAAM,OAAO,GAAG,QAAQ,OAAO;AACrC,SAAG,iBAAiB,aAAa,MAAM;AACnC,YAAI,YAAY,KAAK;AAAE,oBAAU;AAAK,uBAAa;AAAA,QAAG;AAAA,MAC1D,CAAC;AACD,SAAG,iBAAiB,SAAS,MAAM;AAC/B,kBAAU;AACV,mBAAW;AAAA,MACf,CAAC;AAAA,IACL,CAAC;AACD,WAAO,iBAAiB,4CAA4C,EAAE,QAAQ,CAAC,QAAQ;AACnF,UAAI,iBAAiB,SAAS,MAAM;AAChC,cAAM,UAAU,IAAI,QAAQ;AAC5B,cAAM,KAAK,aAAa,IAAI,OAAO;AACnC,YAAI,CAAC,GAAI;AACT,WAAG,QAAS,IAAI,QAAQ,WAAW,SAAU,IAAI;AACjD,gBAAQ;AAAA,MACZ,CAAC;AAAA,IACL,CAAC;AAAA,EACL;AAEA,QAAM,eAAe,MAAM;AACvB,WAAO,iBAAiB,yBAAyB,EAAE,QAAQ,CAAC,OAAO;AAC/D,YAAM,MAAM,OAAO,GAAG,QAAQ,OAAO;AACrC,YAAM,KAAM,QAAQ;AACpB,SAAG,UAAU,OAAO,kCAAkC,EAAE;AACxD,UAAI,GAAI,IAAG,eAAe,EAAE,OAAO,UAAU,CAAC;AAAA,IAClD,CAAC;AAAA,EACL;AAOA,GAAC,YAAY;AACT,UAAM,QAAQ,IAAI,QAAQ,IAAI,OAAO,QAAQ;AACzC,YAAM,KAAK,aAAa,IAAI,IAAI,OAAO;AACvC,UAAI,CAAC,GAAI;AACT,SAAG,OAAO,MAAM,SAAS,QAAQ,IAAI,SAAS,GAAG;AAGjD,SAAG,SAAS,SAAS,UAAU,IAAI,SAAS,GAAG,IAAI;AAAA,IACvD,CAAC,CAAC;AACF,YAAQ;AAGR,QAAI,cAAc,SAAS,GAAG;AAC1B,gBAAU;AACV,mBAAa;AAAA,IACjB;AAAA,EACJ,GAAG;AAEH,cAAY,MAAM;AAClB,SAAO;AACX;AAsBO,SAAS,oBAAoB,EAAE,GAAG,GAAG,MAAM,YAAY,GAAG,OAAO,GAAG;AACvE,QAAM,OAAO,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;AAC3C,MAAI,KAAK,WAAW,EAAG,QAAO,MAAM;AAAA,EAAC;AAErC,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,UAAQ,YAAY;AACpB,QAAM,OAAO,KAAK,IAAI,CAAC,GAAG,MAAM;AAAA,6CACS,MAAM,YAAY,kCAAkC,EAAE;AAAA,wBAC3E,CAAC,kCAAkC,MAAM,SAAS;AAAA,gFACM,MAAM,YAAY,UAAU,EAAE;AAAA,sDACxDA,MAAK,EAAE,SAAS,EAAE,QAAQ,KAAK,CAAC;AAAA;AAAA,KAEjF,EAAE,KAAK,EAAE;AACV,UAAQ,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kEAM0C,IAAI;AAAA;AAAA;AAGlE,WAAS,KAAK,YAAY,OAAO;AAEjC,QAAM,QAAQ,QAAQ,cAAc,qBAAqB;AACzD,QAAM,IAAI;AACV,QAAM,MAAM,WAAW;AACvB,QAAM,MAAM,OAAO,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,aAAa,IAAI,GAAG,CAAC,CAAC,CAAC;AAEzE,QAAM,MAAM,SAAS,GAAG,KAAK,IAAI,GAAG,OAAO,cAAc,IAAI,CAAC,CAAC;AAC/D,QAAM,MAAM,YAAY,GAAG,KAAK,IAAI,KAAK,IAAI,EAAE,CAAC;AAEhD,MAAI,QAAQ;AACZ,MAAI,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,SAAS,CAAC;AAE7D,QAAM,QAAQ,MAAM;AAChB,QAAI,CAAC,MAAO;AACZ,YAAQ;AACR,aAAS,oBAAoB,aAAa,WAAW,IAAI;AACzD,aAAS,oBAAoB,WAAW,OAAO,IAAI;AACnD,YAAQ,OAAO;AAAA,EACnB;AACA,QAAM,YAAY,CAAC,OAAO;AAAE,QAAI,CAAC,QAAQ,SAAS,GAAG,MAAM,EAAG,OAAM;AAAA,EAAG;AACvE,QAAM,OAAO,CAAC,QAAQ;AAClB,UAAM;AACN,QAAI;AAAE,eAAS,GAAG;AAAA,IAAG,SACd,KAAK;AAAE,cAAQ,KAAK,iCAAiC,GAAG;AAAA,IAAG;AAAA,EACtE;AACA,QAAM,QAAQ,MAAM;AAChB,YAAQ,iBAAiB,2BAA2B,EAAE,QAAQ,CAAC,OAAO;AAClE,YAAM,KAAK,OAAO,GAAG,QAAQ,GAAG,MAAM;AACtC,SAAG,UAAU,OAAO,oCAAoC,EAAE;AAC1D,UAAI,GAAI,IAAG,eAAe,EAAE,OAAO,UAAU,CAAC;AAAA,IAClD,CAAC;AAAA,EACL;AACA,QAAM,QAAQ,CAAC,OAAO;AAClB,QAAI,CAAC,SAAS,GAAG,YAAa;AAC9B,YAAQ,GAAG,KAAK;AAAA,MACZ,KAAK;AAAa,WAAG,eAAe;AAAG,cAAM;AAAG;AAAA,MAChD,KAAK;AAAa,WAAG,eAAe;AAChC,iBAAS,KAAK,IAAI,KAAK,SAAS,GAAG,SAAS,CAAC;AAAG,cAAM;AAAG;AAAA,MAC7D,KAAK;AAAa,WAAG,eAAe;AAChC,iBAAS,KAAK,IAAI,GAAG,SAAS,CAAC;AAAG,cAAM;AAAG;AAAA,MAC/C,KAAK;AAAa,WAAG,eAAe;AAAG,iBAAS;AAAG,cAAM;AAAG;AAAA,MAC5D,KAAK;AAAa,WAAG,eAAe;AAAG,iBAAS,KAAK,SAAS;AAAG,cAAM;AAAG;AAAA,MAC1E,KAAK;AAAa,WAAG,eAAe;AAAG,aAAK,MAAM;AAAG;AAAA,IACzD;AAAA,EACJ;AACA,UAAQ,iBAAiB,2BAA2B,EAAE,QAAQ,CAAC,OAAO;AAClE,UAAM,MAAM,OAAO,GAAG,QAAQ,GAAG;AACjC,OAAG,iBAAiB,aAAa,MAAM;AACnC,UAAI,WAAW,KAAK;AAAE,iBAAS;AAAK,cAAM;AAAA,MAAG;AAAA,IACjD,CAAC;AACD,OAAG,iBAAiB,SAAS,MAAM,KAAK,GAAG,CAAC;AAAA,EAChD,CAAC;AACD,WAAS,iBAAiB,aAAa,WAAW,IAAI;AACtD,WAAS,iBAAiB,WAAW,OAAO,IAAI;AAChD,QAAM;AACN,SAAO;AACX;AAOA,SAAS,eAAe,QAAQ,OAAO;AACnC,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,KAAQ,OAAO,OAAO,MAAS,EAAE,EAAE,YAAY;AACrD,QAAM,QAAQ,OAAO,OAAO,SAAS,EAAE,EAAE,YAAY;AACrD,QAAM,OAAQ,OAAO,OAAO,QAAS,EAAE,EAAE,YAAY;AACrD,SAAO,GAAG,SAAS,KAAK,KAAK,MAAM,SAAS,KAAK,KAAK,KAAK,SAAS,KAAK;AAC7E;AAGA,SAASA,MAAK,GAAG;AACb,SAAO,OAAO,KAAK,EAAE,EAAE,QAAQ,YAAY,CAAC,OAAO;AAAA,IAC/C,KAAK;AAAA,IAAS,KAAK;AAAA,IAAQ,KAAK;AAAA,IAAQ,KAAK;AAAA,IAAU,KAAK;AAAA,EAChE,GAAE,CAAC,CAAE;AACT;;;AC7XO,IAAM,WAAW;AACjB,IAAM,WAAW;AAIjB,IAAM,YAAY;AAKlB,IAAM,aAAa;AAOnB,IAAM,eAAe;AAIrB,IAAM,iBAAiB;AAevB,SAAS,UAAU,OAAO;AAC7B,MAAI,SAAS,QAAQ,UAAU,GAAI,QAAO;AAC1C,QAAM,IAAI,OAAO,KAAK;AACtB,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,QAAM,UAAU,KAAK,MAAM,IAAI,SAAS,IAAI;AAC5C,SAAO,KAAK,IAAI,UAAU,KAAK,IAAI,UAAU,OAAO,CAAC;AACzD;AAQO,SAAS,UAAU,MAAM,SAAS;AACrC,MAAI,CAAC,MAAM,MAAO;AAClB,OAAK,MAAM,YAAY,cAAc,OAAO,UAAU,GAAG,CAAC;AAC1D,OAAK,UAAU,OAAO,cAAc,YAAY,YAAY;AAChE;AAeO,SAAS,iBAAiB,QAAQ,EAAE,MAAM,OAAO,MAAM,WAAW,gBAAgB,SAAS,IAAI,CAAC,GAAG;AACtG,MAAI,CAAC,UAAU,CAAC,KAAM,QAAO;AAE7B,QAAM,MAAM,OAAO;AACnB,MAAI,UAAU;AACd,MAAI,WAAW;AAEf,QAAM,KAAK,IAAI,cAAc,KAAK;AAClC,KAAG,YAAY;AAEf,QAAM,aAAa,CAAC,OAAO,OAAO,UAAU;AACxC,UAAM,IAAI,IAAI,cAAc,QAAQ;AACpC,MAAE,OAAO;AACT,MAAE,YAAY;AACd,MAAE,cAAc;AAChB,MAAE,QAAQ;AACV,MAAE,aAAa,cAAc,KAAK;AAClC,MAAE,iBAAiB,SAAS,MAAM,OAAO,UAAU,UAAU,KAAK,CAAC,CAAC;AACpE,WAAO;AAAA,EACX;AAEA,QAAM,SAAS,IAAI,cAAc,OAAO;AACxC,SAAO,OAAO;AACd,SAAO,YAAY;AACnB,SAAO,MAAM,OAAO,QAAQ;AAC5B,SAAO,MAAM,OAAO,QAAQ;AAC5B,SAAO,OAAO,OAAO,SAAS;AAC9B,SAAO,QAAQ,OAAO,YAAY;AAClC,SAAO,QAAQ,uBAAuB,QAAQ,SAAI,QAAQ;AAC1D,SAAO,aAAa,cAAc,oBAAoB;AAGtD,SAAO,iBAAiB,SAAS,MAAM,OAAO,UAAU,OAAO,KAAK,CAAC,CAAC;AAMtE,SAAO,iBAAiB,YAAY,MAAM,OAAO,YAAY,CAAC;AAM9D,QAAM,UAAU,IAAI,cAAc,QAAQ;AAC1C,UAAQ,OAAO;AACf,UAAQ,YAAY;AACpB,UAAQ,QAAQ,WAAW,YAAY;AACvC,UAAQ,iBAAiB,SAAS,MAAM,OAAO,YAAY,CAAC;AAE5D,KAAG;AAAA,IACC,WAAW,UAAK,YAAY,UAAU,KAAK,CAAC,UAAU;AAAA,IACtD;AAAA,IACA,WAAW,KAAK,WAAW,UAAU,KAAK,UAAU;AAAA,IACpD;AAAA,EACJ;AACA,SAAO,YAAY,EAAE;AAIrB,QAAM,QAAQ,CAAC,YAAY;AACvB,cAAU;AACV,WAAO,QAAQ,OAAO,OAAO;AAC7B,YAAQ,cAAc,GAAG,OAAO;AAChC,cAAU,MAAM,OAAO;AAAA,EAC3B;AAMA,MAAI,YAAY;AAChB,QAAM,UAAU,CAAC,YAAY;AACzB,UAAM,QAAQ,MAAM;AACpB,QAAI,CAAC,MAAO;AACZ,iBAAa,SAAS;AACtB,gBAAY,WAAW,MAAM;AACzB,cAAQ,QAAQ,EACX,KAAK,MAAM,MAAM,MAAM,UAAU,OAAO,CAAC,EACzC,MAAM,CAAC,QAAQ,QAAQ,KAAK,sBAAsB,GAAG,CAAC;AAAA,IAC/D,GAAG,GAAG;AAAA,EACV;AAEA,QAAM,SAAS,CAAC,YAAY;AACxB,QAAI,YAAY,YAAY,QAAS;AACrC,UAAM,OAAO;AACb,YAAQ,OAAO;AACf,QAAI;AAAE,iBAAW,OAAO;AAAA,IAAG,SAAS,KAAK;AAAE,cAAQ,KAAK,yBAAyB,GAAG;AAAA,IAAG;AAAA,EAC3F;AAEA,QAAM,YAAY;AAMlB,QAAM,QAAQ,QAAQ,QAAQ,EACzB,KAAK,MAAM,MAAM,OAAO,OAAO,QAAQ,CAAC,EACxC,KAAK,CAAC,UAAU;AACb,QAAI,CAAC,YAAY,SAAS,KAAM,OAAM,UAAU,KAAK,CAAC;AACtD,WAAO;AAAA,EACX,CAAC,EACA,MAAM,CAAC,QAAQ;AACZ,YAAQ,KAAK,sBAAsB,GAAG;AACtC,WAAO;AAAA,EACX,CAAC;AAEL,SAAO;AAAA,IACH;AAAA,IACA,KAAK,MAAM;AAAA,IACX,KAAK,CAAC,YAAY,OAAO,UAAU,OAAO,CAAC;AAAA,IAC3C;AAAA,IACA,SAAS,MAAM;AACX,iBAAW;AACX,mBAAa,SAAS;AACtB,SAAG,OAAO;AACV,gBAAU,MAAM,YAAY;AAAA,IAChC;AAAA,EACJ;AACJ;;;AC3IA,eAAsB,YAAY;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,MAAM;AAAA,EACN,WAAW;AAAA,EACX,SAAS;AAAA,EACT,aAAa;AAAA,EACb,SAAS,CAAC;AAAA,EACV,YAAY;AAAA,EACZ,SAAS,aAAa,CAAC;AAAA,EACvB,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA,EAIV,cAAc;AAClB,IAAI,CAAC,GAAG;AAGJ,MAAI,CAAC,QAAQ,OAAO,KAAK,gBAAgB,YAAY;AACjD,UAAM,IAAI,UAAU,wCAAwC;AAAA,EAChE;AACA,MAAI,CAAC,SAAU,OAAM,IAAI,MAAM,qCAAqC;AACpE,MAAI,CAAC,SAAU,OAAM,IAAI,MAAM,4CAA4C;AAE3E,QAAM,MAAM,UAAU,EAAE,OAAM;AAAA,EAAC,GAAG,OAAM;AAAA,EAAC,GAAG,QAAO;AAAA,EAAC,GAAG,QAAO;AAAA,EAAC,EAAE;AACjE,QAAM,WAAW,sBAAsB,OAAO;AAE9C,MAAI,KAAK;AACT,MAAI,WAAW;AACf,MAAI,aAAa;AACjB,MAAI,UAAU;AAEd,QAAM,aAAa,MAAM;AACrB,QAAI,CAAC,GAAI;AACT,qBAAiB,SAAS,EAAE;AAC5B,mBAAe,YAAY,EAAE;AAC7B,eAAW,UAAU,EAAE;AAAA,EAC3B;AAEA,OAAK,IAAI,cAAc;AAAA,IACnB,QAAQ;AAAA,IACR,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,KAAK;AAAA,MACD;AAAA,MAAK;AAAA,MAAU;AAAA,MAAM;AAAA,MACrB;AAAA,MAAU;AAAA,MAAU;AAAA,MAAW;AAAA,MAC/B,mBAAmB,CAAC,QAAQ,GAAG,MAAM,iBAAiB,IAAI,QAAQ,GAAG,CAAC;AAAA,MACtE,eAAmB,CAAC,QAAQ,GAAG,MAAM,aAAa,IAAI,QAAQ,GAAG,CAAC;AAAA,IACtE;AAAA,IACA,UAAU,MAAM,WAAW;AAAA,EAC/B,CAAC;AAED,QAAM,UAAU,qBAAqB;AAAA,IACjC;AAAA,IAAI;AAAA,IAAK;AAAA,IAAU,SAAS;AAAA,IAAU,GAAG;AAAA,EAC7C,CAAC;AAID,QAAM,gBAAgB,cAAc,EAAE,IAAI,SAAS,YAAY,CAAC;AAIhE,QAAM,aAAa,mBAAmB,OAAO,eAAe,OAAO;AACnE,aAAW,YAAY,OAAO,QAAQ,UAAU,EAAE;AAClD,eAAa,gBAAgB,OAAO,UAAU,EAAE;AAMhD,QAAM,OAAO,iBAAiB,OAAO,MAAM,EAAE,MAAM,KAAK,CAAC;AACzD,MAAI,KAAM,OAAM,KAAK;AAKrB,YAAU,iBAAiB,OAAO,cAAc,EAAE;AAGlD,YAAU,KAAK,cAAc,UAAU;AAEvC,QAAM,GAAG,KAAK;AACd,aAAW;AAEX,MAAI,OAAO,eAAe;AAE1B,SAAO,OAAO,OAAO;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,QAAQ,OAAO,OAAO;AAAA,MAClB,iBAAiB;AAAA,MACjB;AAAA,MACA;AAAA,MACA,cAAc;AAAA;AAAA;AAAA,MAGd;AAAA,IACJ,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMD,SAAS,MAAM;AACX,UAAI;AAAE,kBAAU,MAAM,cAAc,UAAU;AAAA,MAAG,QAAQ;AAAA,MAAe;AACxE,UAAI;AAAE,wBAAgB;AAAA,MAAG,QAAQ;AAAA,MAAe;AAChD,UAAI;AAAE,iBAAS,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAAe;AAQjD,UAAI;AAAE,WAAG,SAAS,QAAQ;AAAA,MAAG,SAAS,KAAK;AAAE,YAAI,OAAO,qBAAqB,GAAG;AAAA,MAAG;AAInF,UAAI;AAAE,cAAM,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAAe;AAAA,IAClD;AAAA,EACJ,CAAC;AACL;AASA,SAAS,YAAY,QAAQ,UAAU,IAAI;AACvC,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,YAAY;AACnB,SAAO,UAAU,IAAI,aAAa;AAClC,aAAW,KAAK,SAAS,cAAc,GAAG;AACtC,UAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,QAAI,YAAY;AAChB,QAAI,QAAQ,OAAO,EAAE;AACrB,QAAI,QAAQ,UAAU,EAAE;AACxB,QAAI,QAAQ,mBAAmB;AAC/B,QAAI,aAAa,cAAc,EAAE,KAAK;AACtC,QAAI,YAAY;AAAA,wEACgD,EAAE,IAAI;AAAA,+CAC/B,EAAE,KAAK;AAAA;AAE9C,WAAO,YAAY,GAAG;AAAA,EAC1B;AACA,SAAO,iBAAiB,SAAS,CAAC,MAAM;AACpC,UAAM,MAAM,EAAE,OAAO,QAAQ,aAAa;AAC1C,QAAI,CAAC,IAAK;AACV,OAAG,cAAc,IAAI,QAAQ,IAAI;AAAA,EACrC,CAAC;AACD,SAAO;AACX;AAEA,SAAS,WAAW,QAAQ,IAAI;AAC5B,MAAI,CAAC,OAAQ;AACb,QAAM,OAAO,GAAG,SAAS,OAAO,EAAE;AAClC,QAAM,YAAY,KAAK,cAAc;AACrC,QAAM,cAAc,YAAY,KAAK,IAAI,SAAS,GAAG,SAAS,OAAO;AAGrE,QAAM,aAAa,GAAG,SAAS,UAAU,WAAW,KAAK;AACzD,SAAO,iBAAiB,aAAa,EAAE,QAAQ,CAAC,MAAM;AAClD,MAAE,UAAU,OAAO,wBAAwB,EAAE,QAAQ,SAAS,UAAU;AAAA,EAC5E,CAAC;AACL;AAgBA,SAAS,mBAAmB,QAAQ,SAAS;AACzC,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,cAAc,kBAAkB,GAAG,OAAO;AACjD,QAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,MAAI,KAAK;AACT,MAAI,YAAY;AAChB,MAAI,QAAQ,UAAU;AACtB,MAAI,QAAQ,mBAAmB;AAC/B,MAAI,aAAa,cAAc,sBAAsB;AACrD,MAAI,YAAY;AAChB,MAAI,iBAAiB,SAAS,MAAM,QAAQ,OAAO,CAAC;AACpD,SAAO,aAAa,KAAK,OAAO,UAAU;AAC1C,SAAO;AACX;AAaA,SAAS,iBAAiB,KAAK,IAAI;AAC/B,QAAM,QAAQ,CAAC;AACf,aAAW,QAAQ,CAAC,QAAQ,SAAS,QAAQ,GAAG;AAC5C,UAAM,MAAM,MAAM,IAAI;AACtB,QAAI,CAAC,IAAK;AACV,UAAM,KAAK,IAAI,UAAU,IAAI;AAC7B,OAAG,UAAU,OAAO,UAAU,UAAU,UAAU;AAClD,OAAG,WAAW;AACd,OAAG,gBAAgB,eAAe;AAClC,OAAG,gBAAgB,cAAc;AACjC,QAAI,YAAY,EAAE;AAClB,OAAG,iBAAiB,SAAS,MAAM,GAAG,YAAY,IAAI,CAAC;AACvD,UAAM,IAAI,IAAI;AAAA,EAClB;AACA,SAAO;AACX;AAEA,SAAS,iBAAiB,KAAK,IAAI;AAC/B,aAAW,QAAQ,CAAC,QAAQ,SAAS,QAAQ,GAAG;AAC5C,UAAM,MAAM,MAAM,IAAI;AACtB,QAAI,CAAC,IAAK;AACV,QAAI,UAAU,OAAO,4BAA4B,GAAG,YAAY,IAAI,CAAC;AAAA,EACzE;AACJ;AAEA,SAAS,gBAAgB,QAAQ,IAAI;AACjC,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,KAAK,OAAO,cAAc,eAAe;AAC7C,MAAI,CAAC,IAAI;AACL,SAAK,SAAS,cAAc,KAAK;AACjC,OAAG,KAAK;AACR,OAAG,YAAY;AACf,WAAO,YAAY,EAAE;AAAA,EACzB;AACA,KAAG,iBAAiB,SAAS,CAAC,MAAM;AAChC,UAAM,MAAM,EAAE,OAAO,QAAQ,gBAAgB;AAC7C,QAAI,CAAC,IAAK;AACV,UAAM,IAAI,IAAI,QAAQ;AACtB,QAAI,MAAM,KAAK;AAAE,SAAG,WAAW;AAAG,qBAAe,IAAI,EAAE;AAAG;AAAA,IAAQ;AAClE,OAAG,cAAc,OAAO,CAAC,CAAC;AAAA,EAC9B,CAAC;AAID,KAAG,iBAAiB,YAAY,OAAO,MAAM;AACzC,UAAM,MAAM,EAAE,OAAO,QAAQ,gBAAgB;AAC7C,QAAI,CAAC,OAAO,IAAI,QAAQ,YAAY,IAAK;AACzC,MAAE,eAAe;AACjB,UAAM,MAAM,OAAO,IAAI,QAAQ,OAAO;AACtC,UAAM,IAAI,GAAG,SAAS,SAAS,GAAG;AAClC,QAAI,CAAC,EAAG;AACR,UAAM,SAAS,MAAM,SAAS;AAAA,MAC1B,OAAO;AAAA,MACP,QAAQ,CAAC;AAAA,QAAE,MAAM;AAAA,QAAS,OAAO;AAAA,QAAQ,MAAM;AAAA,QAAQ,UAAU;AAAA,QACtD,MAAM;AAAA,MAA2E,CAAC;AAAA,MAC7F,UAAU,EAAE,OAAO,EAAE,MAAM;AAAA,MAC3B,aAAa;AAAA,IACjB,CAAC;AACD,QAAI,CAAC,UAAU,CAAC,OAAO,MAAO;AAC9B,MAAE,QAAQ,OAAO,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAC1C,mBAAe,IAAI,EAAE;AACrB,OAAG,SAAS;AAAA,EAChB,CAAC;AACD,KAAG,iBAAiB,eAAe,CAAC,MAAM;AACtC,UAAM,MAAM,EAAE,OAAO,QAAQ,gBAAgB;AAC7C,QAAI,CAAC,OAAO,IAAI,QAAQ,YAAY,IAAK;AACzC,MAAE,eAAe;AACjB,UAAM,MAAM,OAAO,IAAI,QAAQ,OAAO;AACtC,UAAM,IAAI,GAAG;AACb,UAAM,IAAI,EAAE,SAAS,GAAG;AACxB,UAAM,YAAY,EAAE,SAAS,SAAS;AACtC,oBAAgB,EAAE,SAAS,EAAE,SAAS;AAAA,MAClC;AAAA,QAAE,OAAO,aAAa,EAAE,KAAK;AAAA,QAAI,MAAM;AAAA,QACrC,QAAQ;AAAA,QAAU,UAAU,QAAQ,EAAE;AAAA,MAAU;AAAA,MAClD,EAAE,OAAO,gBAAgB,MAAM,QAAQ,QAAQ,SAAS;AAAA,MACxD,EAAE,WAAW,KAAK;AAAA,MAClB,EAAE,OAAO,eAAgB,MAAM,OAAQ,QAAQ,MAAM;AAAA,MACrD;AAAA,QAAE,OAAO;AAAA,QAAkB,MAAM;AAAA,QAAU,QAAQ;AAAA,QACjD,QAAQ;AAAA,QAAM,UAAU,CAAC;AAAA,MAAU;AAAA,IACzC,GAAG,OAAO,WAAW;AACjB,UAAI,WAAW,SAAU,IAAG,cAAc,GAAG;AAAA,eACpC,WAAW,UAAU;AAC1B,cAAM,SAAS,MAAM,SAAS;AAAA,UAC1B,OAAO;AAAA,UACP,QAAQ,CAAC,EAAE,MAAM,SAAS,OAAO,QAAQ,MAAM,QAAQ,UAAU,KAAK,CAAC;AAAA,UACvE,UAAU,EAAE,OAAO,EAAE,MAAM;AAAA,UAC3B,aAAa;AAAA,QACjB,CAAC;AACD,YAAI,CAAC,UAAU,CAAC,OAAO,MAAO;AAC9B,UAAE,QAAQ,OAAO,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAC1C,uBAAe,IAAI,EAAE;AACrB,WAAG,SAAS;AAAA,MAChB,WACS,WAAW,OAAU;AAAE,WAAG,WAAW;AAAG,uBAAe,IAAI,EAAE;AAAA,MAAG,WAChE,WAAW,UAAU;AAAE,WAAG,cAAc,GAAG;AAAG,uBAAe,IAAI,EAAE;AAAA,MAAG;AAAA,IACnF,CAAC;AAAA,EACL,CAAC;AACD,SAAO;AACX;AAEA,SAAS,eAAe,IAAI,IAAI;AAC5B,MAAI,CAAC,GAAI;AACT,QAAM,IAAI,GAAG;AACb,KAAG,YACC,EAAE,SAAS,IAAI,CAAC,GAAG,MAAM;AAAA,0CACS,MAAM,EAAE,YAAY,uBAAuB,EAAE;AAAA,oCACnD,CAAC;AAAA,qCACA,EAAE,KAAK,SAAS,IAAI,CAAC;AAAA,kBACxC,EAAE,KAAK;AAAA,sBACH,EAAE,KAAK,EAAE,IACrB;AAAA;AAEV;AAUA,SAAS,aAAa,IAAI,QAAQ,GAAG,GAAG;AACpC,QAAM,OAAO,GAAG,SAAS,OAAO,EAAE;AAClC,QAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,MAAI,CAAC,QAAQ,KAAK,SAAS,OAAQ;AACnC,QAAM,OAAO,MAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC;AACrD,MAAI,KAAK,WAAW,EAAG;AACvB,sBAAoB;AAAA,IAChB;AAAA,IAAG;AAAA,IACH;AAAA,IACA,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,SAAS,GAAG,KAAK,gBAAgB,CAAC,CAAC;AAAA,IACxE,QAAQ,CAAC,QAAQ;AACb,WAAK,iBAAiB,QAAQ,GAAG;AACjC,WAAK,MAAM,MAAM;AACjB,SAAG,SAAS,OAAO;AACnB,SAAG,WAAW;AACd,SAAG,gBAAgB,sBAAsB;AAAA,IAC7C;AAAA,EACJ,CAAC;AACL;AAGA,SAAS,iBAAiB,IAAI,QAAQ,GAAG,GAAG;AACxC,QAAM,OAAO,GAAG,SAAS,OAAO,EAAE;AAClC,QAAM,OAAO,KAAK,IAAI,MAAM;AAC5B,MAAI,CAAC,KAAM;AACX,QAAM,UAAU,OAAO,KAAK,SAAS,QAAQ,EAAE,EAAE,WAAW,QAAQ;AACpE,OAAK,MAAM,MAAM;AACjB,KAAG,SAAS,oBAAoB;AAEhC,QAAM,QAAQ;AAAA,IACV,EAAE,OAAO,sBAAsB,MAAM,4BAA4B,QAAQ,UAAU;AAAA,IACnF,EAAE,OAAO,oBAAsB,MAAM,mBAA4B,QAAQ,UAAU;AAAA,IACnF,EAAE,WAAW,KAAK;AAAA,IAClB;AAAA,MAAE,OAAO;AAAA,MAAsB,MAAM;AAAA,MACnC,QAAQ;AAAA,MAAe,UAAU,WAAW,CAAC,KAAK;AAAA,IAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQ5D;AAAA,MAAE,OAAO;AAAA,MAA2B,MAAM;AAAA,MACxC,QAAQ;AAAA,MAAe,UAAU,WAAW,CAAC,KAAK;AAAA,IAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkB5D;AAAA,MAAE,OAAO;AAAA,MAA+B,MAAM;AAAA,MAC5C,QAAQ;AAAA,MACR,UAAU,WAAW,CAAC,KAAK,WACd,GAAG,UAAU,aAAa,MAAM,GAAG,YAAY;AAAA,IAAM;AAAA,EACxE;AACA,MAAI,GAAG,SAAS,SAAS,SAAS,KAAK,CAAC,SAAS;AAC7C,eAAW,CAAC,GAAG,CAAC,KAAK,GAAG,SAAS,SAAS,QAAQ,GAAG;AACjD,UAAI,MAAM,GAAG,SAAS,UAAW;AACjC,YAAM,KAAK;AAAA,QAAE,OAAO,mBAAmB,EAAE,KAAK;AAAA,QAAI,MAAM;AAAA,QAC3C,QAAQ,QAAQ,CAAC;AAAA,MAAG,CAAC;AAAA,IACtC;AAAA,EACJ;AACA,QAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAS9B,QAAM,YAAY,GAAG,UAAU,aAAa,MAAM,GAAG;AACrD,QAAM,cAAc,cAAc,SAAS,WAAW,aAAa;AACnE,QAAM,KAAK;AAAA,IAAE,OAAO;AAAA,IAAc,MAAM;AAAA,IAAS,QAAQ;AAAA,IAC5C,QAAQ;AAAA,IAAM,UAAU,WAAW;AAAA,IACnC,OAAO,cAAe,WAAW,SAAS,SAAa;AAAA,EAAU,CAAC;AAE/E,kBAAgB,GAAG,GAAG,OAAO,CAAC,WAAW;AACrC,QAAI,WAAW,UAAW,IAAG,MAAM,GAAG;AAAA,aAC7B,WAAW,UAAW,IAAG,MAAM,GAAG;AAAA,aAClC,WAAW,YAAY;AAC5B,YAAM,IAAI,KAAK;AACf,UAAI,EAAG,IAAG,qBAAqB,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAAA,IACpE,WACS,WAAW,eAAe;AAC/B,YAAM,IAAI,KAAK;AACf,UAAI,EAAG,IAAG,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,GAAG,EAAE,QAAQ,SAAS,CAAC;AAAA,IAClE,WACS,WAAW,UAAW,IAAG,qBAAqB;AAAA,aAC9C,WAAW,QAAW,IAAG,aAAa;AAAA,aACtC,QAAQ,aAAa,OAAO,GAAG;AACpC,SAAG,qBAAqB,OAAO,OAAO,MAAM,CAAC,CAAC,CAAC;AAAA,IACnD;AAAA,EACJ,CAAC;AACL;",
6
6
  "names": ["_esc", "wrap", "t", "tr", "_esc", "_esc", "_esc", "_esc", "el", "_esc"]
7
7
  }