flexdesk 0.17.4 → 0.17.6

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/editor/editor_pane.js", "../src/editor/monaco_loader.js", "../src/editor/monaco_editor_factory.js", "../src/editor/notebook_search.js", "../src/editor/notebook_undo_manager.js", "../src/editor/split_pane_container.js"],
4
- "sourcesContent": ["/**\r\n * EditorPane \u2014 one half (or whole) of the split editor.\r\n *\r\n * Each pane owns:\r\n * - A NotebookTabBar instance\r\n * - An editor container where the active file's editor is mounted\r\n * - A list of file paths assigned to this pane\r\n * - The active file path within this pane\r\n *\r\n * The pane does NOT mount editors itself \u2014 it delegates to the parent\r\n * (NotebookPage) via callbacks, since editor mounting requires access to\r\n * ProjectModel, symbol index, plot controller, etc.\r\n *\r\n * Layout:\r\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\u2510\r\n * \u2502 [Tab1] [Tab2] \u2502 \u2190 NotebookTabBar\r\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\u2524\r\n * \u2502 \u2502\r\n * \u2502 Editor content \u2502 \u2190 editorContainer\r\n * \u2502 \u2502\r\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\u2518\r\n */\r\n\r\nimport { NotebookTabBar, getTabDragMime } from './notebook_tab_bar.js';\r\n\r\nexport class EditorPane {\r\n /** @type {string} */\r\n #id;\r\n\r\n /** @type {HTMLElement} root element */\r\n #el = null;\r\n\r\n /** @type {NotebookTabBar} */\r\n #tabBar = null;\r\n\r\n /** @type {HTMLElement} */\r\n #editorContainer = null;\r\n\r\n /** @type {string[]} file paths in tab order */\r\n #filePaths = [];\r\n\r\n /** @type {string|null} */\r\n #activeFilePath = null;\r\n\r\n /** @type {string|null} file path of the preview (transient) tab, if any */\r\n #previewFilePath = null;\r\n\r\n /** @type {object} callbacks from parent */\r\n #callbacks = {};\r\n\r\n /** @type {boolean} whether this pane is the focused/active pane */\r\n #focused = false;\r\n\r\n /**\r\n * @param {string} id \u2014 unique pane identifier ('left' or 'right')\r\n * @param {object} callbacks\r\n * @param {Function} callbacks.onActivate \u2014 (paneId, filePath) => void\r\n * @param {Function} callbacks.onClose \u2014 (paneId, filePath) => void\r\n * @param {Function} callbacks.onCloseOthers \u2014 (paneId, filePath) => void\r\n * @param {Function} callbacks.onCloseToRight \u2014 (paneId, filePath) => void\r\n * @param {Function} callbacks.onCloseAll \u2014 (paneId) => void\r\n * @param {Function} callbacks.onCloseSaved \u2014 (paneId) => void\r\n * @param {Function} callbacks.onRename \u2014 (oldPath, newPath) => void\r\n * @param {Function} callbacks.onDuplicate \u2014 (filePath) => void\r\n * @param {Function} callbacks.onReorder \u2014 (paneId, orderedPaths) => void\r\n * @param {Function} callbacks.onRevealInExplorer \u2014 (filePath) => void\r\n * @param {Function} callbacks.onFocus \u2014 (paneId) => void\r\n * @param {Function} callbacks.onSplitRight \u2014 (paneId, filePath) => void\r\n * @param {Function} callbacks.onMoveToOtherPane \u2014 (paneId, filePath) => void\r\n */\r\n constructor(id, callbacks = {}) {\r\n this.#id = id;\r\n this.#callbacks = callbacks;\r\n }\r\n\r\n get id() { return this.#id; }\r\n get activeFilePath() { return this.#activeFilePath; }\r\n get filePaths() { return [...this.#filePaths]; }\r\n get editorContainer() { return this.#editorContainer; }\r\n get element() { return this.#el; }\r\n get isEmpty() { return this.#filePaths.length === 0; }\r\n\r\n /**\r\n * Build DOM and mount the tab bar.\r\n * @returns {HTMLElement} the root element (to be placed in SplitPaneContainer)\r\n */\r\n mount() {\r\n this.#el = document.createElement('div');\r\n this.#el.className = 'editor-pane';\r\n this.#el.dataset.paneId = this.#id;\r\n\r\n // Focus tracking \u2014 clicking anywhere in the pane focuses it\r\n this.#el.addEventListener('mousedown', () => {\r\n this.#callbacks.onFocus?.(this.#id);\r\n }, true);\r\n\r\n // Tab bar\r\n const tabBarEl = document.createElement('div');\r\n this.#el.appendChild(tabBarEl);\r\n\r\n this.#tabBar = new NotebookTabBar();\r\n this.#tabBar.mount(tabBarEl, {\r\n onActivate: (path) => this.#callbacks.onActivate?.(this.#id, path),\r\n onClose: (path) => this.#callbacks.onClose?.(this.#id, path),\r\n onCloseOthers: (path) => this.#callbacks.onCloseOthers?.(this.#id, path),\r\n onCloseToRight: (path) => this.#callbacks.onCloseToRight?.(this.#id, path),\r\n onCloseAll: () => this.#callbacks.onCloseAll?.(this.#id),\r\n onCloseSaved: () => this.#callbacks.onCloseSaved?.(this.#id),\r\n onRename: (o, n) => this.#callbacks.onRename?.(o, n),\r\n onDuplicate: (path) => this.#callbacks.onDuplicate?.(path),\r\n onReorder: (paths) => this.#callbacks.onReorder?.(this.#id, paths),\r\n onRevealInExplorer:(path) => this.#callbacks.onRevealInExplorer?.(path),\r\n onSplitRight: (path) => this.#callbacks.onSplitRight?.(this.#id, path),\r\n onMoveToOtherPane: (path) => this.#callbacks.onMoveToOtherPane?.(this.#id, path),\r\n onDropFromOtherPane: (path) => this.#callbacks.onDropFromOtherPane?.(this.#id, path),\r\n });\r\n\r\n // Editor body wrapper \u2014 positioned container that fills remaining\r\n // flex space. The actual editor container is absolutely positioned\r\n // inside, so its height is ALWAYS the wrapper's height, regardless\r\n // of content. This makes the scroll chain bulletproof \u2014 no flex\r\n // ancestor chain needed for the scrollbar to work.\r\n const body = document.createElement('div');\r\n body.className = 'editor-pane__body';\r\n this.#el.appendChild(body);\r\n\r\n // Editor container (absolutely positioned inside wrapper)\r\n this.#editorContainer = document.createElement('div');\r\n this.#editorContainer.className = 'notebook-editor-container';\r\n body.appendChild(this.#editorContainer);\r\n\r\n // Accept cross-pane tab drops on the editor area too\r\n this.#editorContainer.addEventListener('dragover', (e) => {\r\n if (this.#isExternalTabDrag(e)) {\r\n e.preventDefault();\r\n try { e.dataTransfer.dropEffect = 'move'; } catch {}\r\n this.#editorContainer.classList.add('notebook-editor-container--drop-target');\r\n }\r\n });\r\n this.#editorContainer.addEventListener('drop', (e) => {\r\n if (this.#isExternalTabDrag(e)) {\r\n e.preventDefault();\r\n this.#editorContainer.classList.remove('notebook-editor-container--drop-target');\r\n const filePath = e.dataTransfer.getData(getTabDragMime())\r\n || e.dataTransfer.getData('text/plain');\r\n if (filePath) {\r\n this.#callbacks.onDropFromOtherPane?.(this.#id, filePath);\r\n }\r\n }\r\n });\r\n this.#editorContainer.addEventListener('dragleave', (e) => {\r\n if (!this.#editorContainer.contains(e.relatedTarget)) {\r\n this.#editorContainer.classList.remove('notebook-editor-container--drop-target');\r\n }\r\n });\r\n\r\n return this.#el;\r\n }\r\n\r\n /**\r\n * Update the file list and active tab.\r\n * @param {Array<{filePath: string, fileType: string, isDirty: boolean}>} tabs\r\n * @param {string|null} activeFilePath\r\n */\r\n syncTabBar(tabs, activeFilePath) {\r\n this.#tabBar?.update(tabs, activeFilePath);\r\n }\r\n\r\n /**\r\n * Set the files assigned to this pane.\r\n * @param {string[]} filePaths\r\n */\r\n setFilePaths(filePaths) {\r\n this.#filePaths = [...filePaths];\r\n }\r\n\r\n /**\r\n * Add a file to this pane's tabs (at end).\r\n * @param {string} filePath\r\n */\r\n addFile(filePath) {\r\n if (!this.#filePaths.includes(filePath)) {\r\n this.#filePaths.push(filePath);\r\n }\r\n }\r\n\r\n /**\r\n * Remove a file from this pane's tabs.\r\n * @param {string} filePath\r\n */\r\n removeFile(filePath) {\r\n this.#filePaths = this.#filePaths.filter(p => p !== filePath);\r\n if (this.#previewFilePath === filePath) this.#previewFilePath = null;\r\n if (this.#activeFilePath === filePath) {\r\n this.#activeFilePath = this.#filePaths.length > 0\r\n ? this.#filePaths[this.#filePaths.length - 1]\r\n : null;\r\n }\r\n }\r\n\r\n hasFile(filePath) {\r\n return this.#filePaths.includes(filePath);\r\n }\r\n\r\n setActiveFile(filePath) {\r\n this.#activeFilePath = filePath;\r\n }\r\n\r\n setFocused(focused) {\r\n this.#focused = focused;\r\n this.#el?.classList.toggle('editor-pane--focused', focused);\r\n }\r\n\r\n markDirty(filePath, isDirty) {\r\n this.#tabBar?.markDirty(filePath, isDirty);\r\n }\r\n\r\n reorderFiles(orderedPaths) {\r\n // Keep only paths that belong to this pane\r\n const valid = orderedPaths.filter(p => this.#filePaths.includes(p));\r\n // Append any missing (shouldn't happen)\r\n for (const p of this.#filePaths) {\r\n if (!valid.includes(p)) valid.push(p);\r\n }\r\n this.#filePaths = valid;\r\n }\r\n\r\n renameFile(oldPath, newPath) {\r\n const idx = this.#filePaths.indexOf(oldPath);\r\n if (idx !== -1) this.#filePaths[idx] = newPath;\r\n if (this.#activeFilePath === oldPath) this.#activeFilePath = newPath;\r\n if (this.#previewFilePath === oldPath) this.#previewFilePath = newPath;\r\n }\r\n\r\n // \u2500\u2500\u2500 Preview (transient) tab \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n /** @returns {string|null} the preview file path, or null if no preview tab */\r\n get previewFilePath() { return this.#previewFilePath; }\r\n\r\n /** Mark a file as the preview tab. Only one preview tab per pane. */\r\n setPreview(filePath) { this.#previewFilePath = filePath; }\r\n\r\n /** Whether a file is the preview (transient) tab. */\r\n isPreview(filePath) { return this.#previewFilePath === filePath; }\r\n\r\n /** Pin the preview tab (promote to permanent). */\r\n pinPreview() { this.#previewFilePath = null; }\r\n\r\n /** Check if a drag event is carrying a tab from another pane. */\r\n #isExternalTabDrag(e) {\r\n try { return e.dataTransfer.types.includes(getTabDragMime()); } catch { return false; }\r\n }\r\n\r\n dispose() {\r\n this.#tabBar?.dispose();\r\n this.#tabBar = null;\r\n this.#el?.remove();\r\n this.#el = null;\r\n this.#editorContainer = null;\r\n this.#filePaths = [];\r\n this.#activeFilePath = null;\r\n this.#previewFilePath = null;\r\n }\r\n}\r\n", "/**\n * Monaco Loader \u2014 wraps the AMD require() bootstrap into an ES module Promise.\n *\n * Monaco ships as AMD modules. vs/loader.js is injected dynamically on demand\n * (NOT via a global <script> tag) so it does not pollute window.define before\n * Plotly loads. Plotly auto-detects AMD define and would wrap itself as a module\n * instead of setting window.Plotly if loader.js ran at page load.\n */\n\nlet _resolveMonaco;\n\n/**\n * The attempt in flight, the one that succeeded, or one that failed for good.\n *\n * Whether a failure is retryable depends on how far the attempt got:\n *\n * - vs/loader.js did not load (a 404, a wrong path): nothing is left behind,\n * so the attempt is cleared and the next initMonaco() starts over. The flag\n * this replaced was set BEFORE loader.js had loaded, so one 404 blocked\n * every retry for the life of the page (BUG-0023).\n * - Monaco's AMD loader reported an error (vs/editor/editor.main or one of\n * its dependencies failed): FINAL for the page. window.require is now\n * Monaco's loader, which records the failed module as complete-with-error\n * and hands every later require() of it the same error \u2014 it has no undef.\n * Retrying cannot help, so the rejected attempt is kept and every caller\n * gets that one error. Recovering takes a page reload.\n * @type {Promise<typeof monaco> | null}\n */\nlet _attempt = null;\n\n/**\n * Resolves with the `monaco` namespace once any initMonaco() call has loaded it.\n *\n * It never rejects, because a failed attempt is not final \u2014 a later\n * initMonaco() may still succeed. To learn that an attempt FAILED, await\n * initMonaco() (or getEditorFactory()), which rejects.\n * @type {Promise<typeof monaco>}\n */\nexport const monacoReady = new Promise(resolve => { _resolveMonaco = resolve; });\n\nfunction injectLoaderScript(src) {\n return new Promise((resolve, reject) => {\n const s = document.createElement('script');\n s.src = src;\n s.onload = resolve;\n s.onerror = () => {\n // Take the dead tag out, so a retry is a fresh request and not a\n // second tag beside the first.\n s.remove?.();\n reject(new Error(`Failed to load Monaco loader: ${src}`));\n };\n document.head.appendChild(s);\n });\n}\n\nfunction injectMonacoCss(basePath) {\n const id = 'monaco-editor-css';\n const href = `${basePath}/vs/editor/editor.main.css`;\n const existing = document.getElementById(id);\n if (existing) {\n // A retry with a corrected path must not keep the stylesheet of the bad one.\n if (existing.getAttribute?.('href') !== href) existing.setAttribute?.('href', href);\n return;\n }\n const link = document.createElement('link');\n link.id = id;\n link.rel = 'stylesheet';\n link.href = href;\n document.head.appendChild(link);\n}\n\nfunction requireEditorMain(basePath) {\n return new Promise((resolve, reject) => {\n window.require.config({\n paths: { vs: `${basePath}/vs` },\n 'vs/nls': { availableLanguages: {} },\n });\n window.require(['vs/editor/editor.main'], () => {\n if (window.monaco) resolve(window.monaco);\n else reject(new Error(\n `[twm/editor] vs/editor/editor.main loaded from ${basePath}/vs but did not `\n + 'set window.monaco.'));\n }, (err) => {\n reject(new Error(\n `[twm/editor] Failed to load Monaco from ${basePath}/vs: ${err?.message ?? err}. `\n + 'Monaco\\'s AMD loader caches this failure, so it is final for this page: '\n + 'fix the path and reload.'));\n });\n });\n}\n\nasync function loadMonaco(basePath) {\n // Inject CSS and loader.js dynamically \u2014 kept out of the HTML to avoid global bleed\n injectMonacoCss(basePath);\n if (!window.require) {\n const src = `${basePath}/vs/loader.js`;\n await injectLoaderScript(src);\n if (!window.require) {\n throw new Error(\n `[twm/editor] ${src} loaded but did not define window.require; `\n + 'is it Monaco\\'s vs/loader.js?');\n }\n }\n return requireEditorMain(basePath);\n}\n\n/**\n * Call once when the notebook page initialises.\n * Where Monaco lives is the CONSUMER's business.\n *\n * `basePath` defaulted to 'vendor/monaco' \u2014 a path into ONE application's tree,\n * which would 404 for anyone else. Exactly the bug plotly_wrapper.js had, and it\n * takes exactly the same fix: no default, and say so if nobody supplies one.\n *\n * Monaco is a PEER dependency \u2014 14 MB this bundle deliberately does not contain.\n *\n * Idempotent: while an attempt is in flight or has succeeded, every caller\n * gets the same promise, whatever path it passes or whether it passes one \u2014\n * so getEditorFactory() piggybacks on a load the consumer started here.\n *\n * The returned promise REJECTS when no path is given and no attempt exists,\n * when loader.js fails to load, or when Monaco's AMD loader reports an error.\n * After a loader.js failure the next call tries again (with the path it is\n * given). An AMD-level failure is final for the page: every later call gets\n * the same rejection, and only a reload recovers (see `_attempt`).\n *\n * @param {string} [basePath] Path prefix for the vs/ directory. REQUIRED\n * unless an attempt is already in flight or has succeeded.\n * @returns {Promise<typeof monaco>}\n */\nexport function initMonaco(basePath) {\n if (_attempt) return _attempt;\n if (!basePath) {\n return Promise.reject(new Error(\n '[twm/editor] initMonaco() needs the path to your copy of Monaco, e.g. '\n + \"initMonaco('vendor/monaco'). Monaco is a peer dependency; the library \"\n + 'does not guess where you put it.'));\n }\n const attempt = loadMonaco(basePath).then(\n (m) => { _resolveMonaco(m); return m; },\n (err) => {\n // Monaco's AMD loader is installed only once loader.js has run; a\n // failure before that leaves nothing cached and can be retried.\n if (!window.require && _attempt === attempt) _attempt = null;\n throw err;\n });\n _attempt = attempt;\n return attempt;\n}\n", "/**\n * Monaco Editor Factory\n *\n * Creates and manages Monaco editor instances for cells.\n * Handles:\n * - Creating full editors (multi-line, for CodeCells)\n * - Creating single-line inline editors (for ParameterCell values, expression fields)\n * - Theme registration (built-in dark and light themes, names set by\n * setEditorThemeNames(); default flexdesk-dark, flexdesk-light)\n * - Shared editor options\n * - Instance lifecycle (dispose on cell removal)\n */\n\nimport { monacoReady, initMonaco } from './monaco_loader.js';\n// The default language is the CONSUMER's. This imported ECOLANG_ID \u2014 EcoAgent's\n// expression language \u2014 so a generic Monaco factory shipped knowing the name of one\n// application's DSL. Register your language with Monaco and name it here.\nlet _defaultLanguage = 'plaintext';\n\n/** @param {string} id A language id already registered with Monaco. */\nexport function setDefaultLanguage(id) {\n if (typeof id === 'string' && id) _defaultLanguage = id;\n}\n\n/** Dispose a Monaco editor, swallowing the benign \"Canceled\" error from pending async ops. */\nfunction safeDispose(editor) {\n try { editor.dispose(); } catch (e) { if (e?.message !== 'Canceled') throw e; }\n}\n\n// \u2500\u2500\u2500 Theme definitions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nconst DARK_THEME = {\n base: 'vs-dark',\n inherit: true,\n rules: [\n { token: 'comment', foreground: '6a9955', fontStyle: 'italic' },\n { token: 'keyword', foreground: '569cd6', fontStyle: 'bold' },\n { token: 'keyword.directive', foreground: 'c586c0', fontStyle: 'bold' },\n { token: 'keyword.operator', foreground: '569cd6' },\n { token: 'constant', foreground: 'c586c0' },\n { token: 'number', foreground: 'b5cea8' },\n { token: 'string', foreground: 'ce9178' },\n { token: 'type.identifier', foreground: '4ec9b0', fontStyle: 'bold' },\n { token: 'support.function', foreground: 'a6e22e' },\n { token: 'identifier', foreground: '4fc1ff' },\n { token: 'operator', foreground: 'd4d4d4' },\n { token: 'delimiter', foreground: '808080' },\n ],\n colors: {\n // Layer-2 input well \u2014 almost black, matches the EcoAgent\n // three-layer contrast pattern (chrome #1a1a1a, content\n // #212121, input/code #0a0a0a). Makes editable surfaces\n // visually recess from the surrounding cards.\n 'editor.background': '#0a0a0a',\n 'editor.foreground': '#e0e0e0',\n 'editor.lineHighlightBackground': '#161616',\n 'editor.selectionBackground': '#264f78',\n 'editor.inactiveSelectionBackground': '#1f3350',\n 'editorLineNumber.foreground': '#5a5a5a',\n 'editorLineNumber.activeForeground': '#c6c6c6',\n 'editorCursor.foreground': '#39ff14',\n 'editor.findMatchBackground': '#9e6a03',\n 'editor.findMatchHighlightBackground': '#f2cc6030',\n 'editorIndentGuide.background1': '#2e2e2e',\n 'editorIndentGuide.activeBackground1': '#5a5a5a',\n 'editorGutter.background': '#0a0a0a',\n // Pop-ups stay slightly lighter so they read as floating\n // surfaces above the editor body, not part of it.\n 'editorWidget.background': '#1a1a1a',\n 'editorSuggestWidget.background': '#1a1a1a',\n 'editorSuggestWidget.border': '#383838',\n 'editorSuggestWidget.selectedBackground': '#094771',\n 'scrollbarSlider.background': '#4a4a4d40',\n 'scrollbarSlider.hoverBackground': '#4a4a4d70',\n },\n};\n\nconst LIGHT_THEME = {\n base: 'vs',\n inherit: true,\n rules: [\n { token: 'comment', foreground: '6a737d', fontStyle: 'italic' },\n { token: 'keyword', foreground: 'd73a49', fontStyle: 'bold' },\n { token: 'keyword.directive', foreground: '6f42c1', fontStyle: 'bold' },\n { token: 'keyword.operator', foreground: 'd73a49' },\n { token: 'constant', foreground: 'e36209' },\n { token: 'number', foreground: '005cc5' },\n { token: 'string', foreground: '22863a' },\n { token: 'type.identifier', foreground: '005cc5', fontStyle: 'bold' },\n { token: 'support.function', foreground: '6f42c1' },\n { token: 'identifier', foreground: '24292e' },\n { token: 'operator', foreground: 'd73a49' },\n { token: 'delimiter', foreground: '586069' },\n ],\n colors: {\n 'editor.background': '#ffffff',\n 'editor.foreground': '#24292e',\n 'editor.lineHighlightBackground': '#f6f8fa',\n 'editor.selectionBackground': '#c8d3e6',\n 'editorLineNumber.foreground': '#babbbd',\n 'editorLineNumber.activeForeground': '#586069',\n 'editorCursor.foreground': '#044289',\n 'editorGutter.background': '#ffffff',\n 'editorWidget.background': '#f6f8fa',\n 'editorSuggestWidget.background': '#f6f8fa',\n 'editorSuggestWidget.border': '#e1e4e8',\n 'scrollbarSlider.background': '#babbbd40',\n 'scrollbarSlider.hoverBackground': '#babbbd70',\n },\n};\n\n/** The names the built-in themes are registered under when the embedder\n * names none. */\nexport const DEFAULT_EDITOR_THEME_NAMES = Object.freeze({\n dark: 'flexdesk-dark',\n light: 'flexdesk-light',\n});\n\nlet _themeNames = DEFAULT_EDITOR_THEME_NAMES;\n\n/**\n * Name the built-in dark and light Monaco themes. Call before the first\n * factory is created: a factory starts on the dark name, and init() registers\n * the built-in themes under the names current at that moment. An embedder with\n * its own theme defines it with `monaco.editor.defineTheme(name, \u2026)` and passes\n * that name to `factory.setTheme(name)` instead.\n *\n * @param {{ dark?: string, light?: string }} [names]\n */\nexport function setEditorThemeNames({ dark, light } = {}) {\n _themeNames = Object.freeze({\n dark: (typeof dark === 'string' && dark) ? dark : DEFAULT_EDITOR_THEME_NAMES.dark,\n light: (typeof light === 'string' && light) ? light : DEFAULT_EDITOR_THEME_NAMES.light,\n });\n}\n\n/** @returns {{ dark: string, light: string }} The built-in themes' names. */\nexport function getEditorThemeNames() {\n return _themeNames;\n}\n\n/** Monaco instance -> names already defined on it, so each name is defined once. */\nconst _definedThemes = new WeakMap();\n\nfunction ensureThemes(monaco) {\n let defined = _definedThemes.get(monaco);\n if (!defined) { defined = new Set(); _definedThemes.set(monaco, defined); }\n for (const [name, theme] of [[_themeNames.dark, DARK_THEME], [_themeNames.light, LIGHT_THEME]]) {\n if (defined.has(name)) continue;\n defined.add(name);\n monaco.editor.defineTheme(name, theme);\n }\n}\n\n// \u2500\u2500\u2500 Shared editor options \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction baseOptions(overrides = {}) {\n return {\n language: _defaultLanguage,\n theme: _themeNames.dark,\n fontFamily: 'Consolas, \"Courier New\", monospace',\n fontSize: 13,\n lineHeight: 19.5,\n tabSize: 4,\n insertSpaces: true,\n automaticLayout: true,\n minimap: { enabled: false },\n scrollBeyondLastLine: false,\n renderLineHighlight: 'gutter',\n matchBrackets: 'always',\n padding: { top: 8, bottom: 8 },\n overviewRulerLanes: 0,\n hideCursorInOverviewRuler: true,\n overviewRulerBorder: false,\n scrollbar: { handleMouseWheel: false },\n folding: true,\n lineDecorationsWidth: 0,\n // Reserve a couple extra gutter columns so the right-aligned line\n // numbers get some breathing room on the left instead of sitting flush\n // against the editor edge (digits stay right-aligned; the slack lands\n // on the left). Covers up to 5-digit files too.\n lineNumbersMinChars: 5,\n suggest: {\n showKeywords: true,\n showFunctions: true,\n showVariables: true,\n showConstants: true,\n showModules: true,\n snippetsPreventQuickSuggestions: false,\n },\n quickSuggestions: { other: true, comments: false, strings: false },\n parameterHints: { enabled: true },\n wordWrap: 'off',\n fixedOverflowWidgets: true,\n ...overrides,\n };\n}\n\nfunction singleLineOptions(overrides = {}) {\n return baseOptions({\n lineNumbers: 'off',\n lineDecorationsWidth: 0,\n lineNumbersMinChars: 0,\n glyphMargin: false,\n folding: false,\n renderLineHighlight: 'none',\n scrollbar: { vertical: 'hidden', horizontal: 'hidden', handleMouseWheel: false },\n wordWrap: 'off',\n overviewRulerLanes: 0,\n padding: { top: 3, bottom: 3 },\n ...overrides,\n });\n}\n\n/** A cap is a positive number of px, or `Infinity` for none. */\nfunction checkMaxHeight(px) {\n if (typeof px !== 'number' || Number.isNaN(px) || px <= 0) {\n throw new RangeError(`maxHeight must be a positive number of px or Infinity, got ${String(px)}`);\n }\n return px;\n}\n\n// \u2500\u2500\u2500 Factory \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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/**\n * @typedef {{ editor: object, dispose: () => void, getValue: () => string, setValue: (v: string) => void, onDidChange: (cb: () => void) => object, setMaxHeight?: (px: number) => void }} EditorHandle\n */\n\nexport class MonacoEditorFactory {\n #monaco = null;\n #currentTheme = _themeNames.dark;\n\n constructor() {}\n\n async init() {\n this.#monaco = await monacoReady;\n ensureThemes(this.#monaco);\n return this;\n }\n\n get monaco() { return this.#monaco; }\n\n /**\n * Set global theme for all editors.\n * @param {string} theme A theme name Monaco knows: one of\n * getEditorThemeNames(), or one the embedder defined itself.\n */\n setTheme(theme) {\n this.#currentTheme = theme;\n this.#monaco?.editor.setTheme(theme);\n }\n\n /**\n * Create a full multi-line editor.\n *\n * Unless `noAutoHeight` is set, the editor sizes `container` to its content,\n * never below `setMinHeight()` (80px by default) and never above `maxHeight`.\n * Past the cap, Monaco scrolls internally and handles the mouse wheel; below\n * it, Monaco leaves the wheel alone, so it reaches whatever scrolls around\n * the editor.\n *\n * @param {HTMLElement} container\n * @param {string} initialValue\n * @param {{ language?: string, readOnly?: boolean, lineNumbers?: string, maxHeight?: number, noAutoHeight?: boolean } & object} [overrides]\n * `maxHeight`: the auto-height cap in px, `Infinity` (the default) for none.\n * `noAutoHeight`: leave the container's size to its layout; the wheel always scrolls the editor.\n * @returns {EditorHandle}\n */\n createEditor(container, initialValue = '', overrides = {}) {\n if (!this.#monaco) throw new Error('MonacoEditorFactory not initialized');\n\n const { noAutoHeight, maxHeight: maxHeightOption = Infinity, ...editorOverrides } = overrides;\n let maxHeight = checkMaxHeight(maxHeightOption);\n\n const editor = this.#monaco.editor.create(container, {\n ...baseOptions({ theme: this.#currentTheme }),\n value: initialValue,\n ...editorOverrides,\n });\n\n // Monaco's handleMouseWheel is toggled with the height: off while the\n // editor fits its content (the wheel scrolls the page around it), on only\n // when the content overflows the cap and the editor has something to scroll.\n let minHeight = 80;\n let heightDisposable = null;\n let updateHeight = null;\n if (noAutoHeight) {\n editor.updateOptions({ scrollbar: { handleMouseWheel: true } });\n } else {\n updateHeight = () => {\n const rawContent = editor.getContentHeight();\n const contentHeight = Math.min(maxHeight, Math.max(minHeight, rawContent));\n container.style.height = `${contentHeight}px`;\n editor.layout();\n editor.updateOptions({ scrollbar: { handleMouseWheel: rawContent > maxHeight } });\n };\n heightDisposable = editor.onDidContentSizeChange(updateHeight);\n updateHeight();\n }\n\n // Scroll the find widget into view when it becomes visible.\n // The editor auto-sizes to content (no internal scroll), so the notebook\n // container scrolls instead. The find widget sits at top:0 of the editor,\n // which may be above the viewport for tall cells.\n let findWidgetObserver = null;\n const observeFindWidget = () => {\n const overflowGuard = container.querySelector('.overflow-guard');\n if (!overflowGuard) return;\n\n findWidgetObserver = new MutationObserver(() => {\n const fw = container.querySelector('.find-widget.visible');\n if (fw) fw.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n });\n findWidgetObserver.observe(overflowGuard, {\n subtree: true,\n attributes: true,\n attributeFilter: ['class'],\n });\n };\n // Defer slightly \u2014 Monaco builds DOM asynchronously after create()\n requestAnimationFrame(observeFindWidget);\n\n return {\n editor,\n getValue: () => editor.getValue(),\n setValue: (v) => { if (editor.getValue() !== v) editor.setValue(v); },\n onDidChange: (cb) => editor.onDidChangeModelContent(cb),\n focus: () => editor.focus(),\n layout: () => editor.layout(),\n setMinHeight: (h) => { minHeight = h; },\n /** The auto-height cap in px, `Infinity` for none; applies at once. No effect with `noAutoHeight`. */\n setMaxHeight: (px) => {\n maxHeight = checkMaxHeight(px);\n updateHeight?.();\n },\n updateHeight,\n dispose: () => {\n findWidgetObserver?.disconnect();\n heightDisposable?.dispose();\n safeDispose(editor);\n },\n };\n }\n\n /**\n * Create a single-line inline editor (for ParameterCell value, expression fields).\n *\n * @param {HTMLElement} container\n * @param {string} initialValue\n * @param {{ language?: string, placeholder?: string } & object} [overrides]\n * @returns {EditorHandle}\n */\n createInlineEditor(container, initialValue = '', overrides = {}) {\n if (!this.#monaco) throw new Error('MonacoEditorFactory not initialized');\n\n container.style.height = '26px';\n\n const editor = this.#monaco.editor.create(container, {\n ...singleLineOptions({ theme: this.#currentTheme }),\n value: initialValue,\n ...overrides,\n });\n\n // Prevent newlines in single-line mode\n editor.onKeyDown((e) => {\n if (e.keyCode === this.#monaco.KeyCode.Enter) {\n e.preventDefault();\n e.stopPropagation();\n }\n });\n\n return {\n editor,\n getValue: () => editor.getValue(),\n setValue: (v) => { if (editor.getValue() !== v) editor.setValue(v); },\n onDidChange: (cb) => editor.onDidChangeModelContent(cb),\n focus: () => editor.focus(),\n layout: () => editor.layout(),\n dispose: () => safeDispose(editor),\n };\n }\n\n /**\n * Create a plain text editor (for documentation cells with markdown).\n * Uses VS Code's built-in markdown language, no EcoLang features.\n *\n * @param {HTMLElement} container\n * @param {string} initialValue\n * @returns {EditorHandle}\n */\n createMarkdownEditor(container, initialValue = '') {\n return this.createEditor(container, initialValue, {\n language: 'markdown',\n wordWrap: 'on',\n lineNumbers: 'off',\n folding: false,\n quickSuggestions: false,\n });\n }\n\n /**\n * Create a read-only DSL viewer (shown in the \"DSL\" tab of a Godley/parameter cell).\n *\n * @param {HTMLElement} container\n * @param {string} dslContent\n * @returns {EditorHandle}\n */\n createDslViewer(container, dslContent = '') {\n return this.createEditor(container, dslContent, {\n readOnly: true,\n lineNumbers: 'off',\n glyphMargin: false,\n folding: false,\n });\n }\n\n /**\n * Create a side-by-side diff editor for comparing two versions of a file.\n *\n * @param {HTMLElement} container\n * @param {string} originalContent - Left side (e.g. HEAD version)\n * @param {string} modifiedContent - Right side (e.g. working copy)\n * @param {{ language?: string, readOnly?: boolean }} [options]\n * @returns {{ editor: object, dispose: Function, setModels: Function }}\n */\n createDiffEditor(container, originalContent = '', modifiedContent = '', options = {}) {\n const monaco = this.#monaco;\n const language = options.language || 'plaintext';\n\n // The pair currently attached to the diff editor. setModels() replaces it, and\n // dispose() frees whichever pair is attached at that moment \u2014 not the first one.\n let models = {\n original: monaco.editor.createModel(originalContent, language),\n modified: monaco.editor.createModel(modifiedContent, language),\n };\n\n const diffEditor = monaco.editor.createDiffEditor(container, {\n theme: this.#currentTheme,\n readOnly: options.readOnly !== false,\n renderSideBySide: true,\n automaticLayout: true,\n minimap: { enabled: false },\n scrollBeyondLastLine: false,\n lineNumbers: 'on',\n glyphMargin: false,\n folding: true,\n renderOverviewRuler: false,\n enableSplitViewResizing: true,\n });\n\n diffEditor.setModel(models);\n\n return {\n editor: diffEditor,\n setModels(original, modified, lang) {\n const previous = models;\n models = {\n original: monaco.editor.createModel(original, lang || language),\n modified: monaco.editor.createModel(modified, lang || language),\n };\n // Attach the new pair first: a model must not be disposed while the\n // diff editor still holds it.\n diffEditor.setModel(models);\n safeDispose(previous.original);\n safeDispose(previous.modified);\n },\n dispose() {\n safeDispose(diffEditor);\n safeDispose(models.original);\n safeDispose(models.modified);\n },\n };\n }\n}\n\n/**\n * The singleton, as a promise: concurrent callers share one load, and a FAILED\n * load clears it so the next call can retry.\n * @type {Promise<MonacoEditorFactory> | null}\n */\nlet _factoryPromise = null;\n\n// Where the app keeps Monaco. Set once, at boot.\nlet _monacoBasePath = null;\n\n/** @param {string} basePath */\nexport function setMonacoBasePath(basePath) {\n if (typeof basePath === 'string' && basePath) _monacoBasePath = basePath;\n}\n\n/**\n * The shared editor factory, once Monaco has loaded.\n *\n * Uses a load already started by initMonaco(path), so a consumer that calls\n * initMonaco() itself needs no setMonacoBasePath(). Otherwise it starts one\n * from setMonacoBasePath()'s path.\n *\n * Rejects \u2014 rather than pending forever (BUG-0023) \u2014 with initMonaco()'s error\n * when no path was set and no load was started, or when Monaco cannot be\n * loaded. A later call asks initMonaco() again: after a loader.js failure that\n * is a fresh attempt (fix the path first); after an AMD-level failure it is\n * the same rejection, because that one is final for the page.\n *\n * @returns {Promise<MonacoEditorFactory>}\n */\nexport function getEditorFactory() {\n if (!_factoryPromise) {\n _factoryPromise = initMonaco(_monacoBasePath)\n .then(() => new MonacoEditorFactory().init())\n .catch((err) => { _factoryPromise = null; throw err; });\n }\n return _factoryPromise;\n}\n", "/**\r\n * NotebookSearchBar \u2014 VS Code-style find/replace bar for notebook-level search.\r\n *\r\n * Searches across all cells using getSearchableText(), highlights matches\r\n * via cell-specific applySearchHighlights(), and supports replace for\r\n * Monaco-based cells (code, documentation).\r\n *\r\n * Mounted at the top of the notebook-editor-container (sticky positioned).\r\n */\r\n\r\n// \u2500\u2500\u2500 Match descriptor \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * @typedef {{ cellId: string, indexInCell: number, charOffset: number }} SearchMatch\r\n */\r\n\r\n// \u2500\u2500\u2500 Component \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nexport class NotebookSearchBar {\r\n /** @type {HTMLElement} parent container (.notebook-editor-container) */\r\n #parent = null;\r\n /** @type {HTMLElement} the search bar root element */\r\n #el = null;\r\n /** @type {HTMLInputElement} */\r\n #searchInput = null;\r\n /** @type {HTMLInputElement} */\r\n #replaceInput = null;\r\n /** @type {HTMLElement} match info label */\r\n #matchInfo = null;\r\n\r\n // State\r\n #isOpen = false;\r\n #showReplace = false;\r\n #caseSensitive = false;\r\n #wholeWord = false;\r\n #useRegex = false;\r\n\r\n // Match tracking\r\n /** @type {SearchMatch[]} */\r\n #matches = [];\r\n #currentIndex = -1;\r\n\r\n // Callbacks into NotebookEditor\r\n #getCells = null; // () => [{ id, type }]\r\n #getCellRenderer = null; // (id) => CellBase|null\r\n #getCellElement = null; // (id) => HTMLElement|null\r\n #scrollToCell = null; // (id) => void\r\n\r\n /**\r\n * @param {HTMLElement} parent \u2014 .notebook-editor-container\r\n * @param {{ getCells, getCellRenderer, getCellElement, scrollToCell }} callbacks\r\n */\r\n constructor(parent, { getCells, getCellRenderer, getCellElement, scrollToCell }) {\r\n this.#parent = parent;\r\n this.#getCells = getCells;\r\n this.#getCellRenderer = getCellRenderer;\r\n this.#getCellElement = getCellElement;\r\n this.#scrollToCell = scrollToCell;\r\n\r\n this.#render();\r\n }\r\n\r\n get isOpen() { return this.#isOpen; }\r\n\r\n // \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n open(showReplace = false) {\r\n this.#isOpen = true;\r\n this.#showReplace = showReplace;\r\n this.#el.classList.add('nb-search-bar--open');\r\n this.#el.querySelector('.nb-search-bar__replace-row').style.display =\r\n showReplace ? '' : 'none';\r\n this.#updateToggleIcon();\r\n\r\n // Pre-fill with current selection text (if any)\r\n const sel = window.getSelection()?.toString()?.trim();\r\n if (sel && sel.length < 200 && !sel.includes('\\n')) {\r\n this.#searchInput.value = sel;\r\n }\r\n\r\n this.#searchInput.focus();\r\n this.#searchInput.select();\r\n\r\n if (this.#searchInput.value) this.#runSearch();\r\n }\r\n\r\n close() {\r\n this.#isOpen = false;\r\n this.#el.classList.remove('nb-search-bar--open');\r\n this.#clearAllHighlights();\r\n this.#matches = [];\r\n this.#currentIndex = -1;\r\n this.#updateMatchInfo();\r\n }\r\n\r\n dispose() {\r\n this.#clearAllHighlights();\r\n this.#el?.remove();\r\n this.#el = null;\r\n }\r\n\r\n // \u2500\u2500\u2500 Rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #render() {\r\n const el = document.createElement('div');\r\n el.className = 'nb-search-bar';\r\n el.innerHTML = `\r\n <div class=\"nb-search-bar__find-row\">\r\n <button class=\"nb-search-bar__btn nb-search-bar__toggle-replace\" title=\"Toggle Replace\">\r\n <span class=\"material-symbols-outlined\">chevron_right</span>\r\n </button>\r\n <div class=\"nb-search-bar__input-wrap\">\r\n <input class=\"nb-search-bar__input nb-search-bar__find-input\"\r\n type=\"text\" placeholder=\"Find\" spellcheck=\"false\" />\r\n </div>\r\n <span class=\"nb-search-bar__match-info\">No results</span>\r\n <button class=\"nb-search-bar__btn\" data-action=\"prev\" title=\"Previous Match (Shift+Enter)\">\r\n <span class=\"material-symbols-outlined\">arrow_upward</span>\r\n </button>\r\n <button class=\"nb-search-bar__btn\" data-action=\"next\" title=\"Next Match (Enter)\">\r\n <span class=\"material-symbols-outlined\">arrow_downward</span>\r\n </button>\r\n <div class=\"nb-search-bar__separator\"></div>\r\n <button class=\"nb-search-bar__btn nb-search-bar__btn--toggle\" data-action=\"case\"\r\n title=\"Match Case (Alt+C)\">Aa</button>\r\n <button class=\"nb-search-bar__btn nb-search-bar__btn--toggle\" data-action=\"word\"\r\n title=\"Match Whole Word (Alt+W)\">\r\n <span class=\"nb-search-bar__icon-word\">ab</span>\r\n </button>\r\n <button class=\"nb-search-bar__btn nb-search-bar__btn--toggle\" data-action=\"regex\"\r\n title=\"Use Regular Expression (Alt+R)\">.*</button>\r\n <div class=\"nb-search-bar__separator\"></div>\r\n <button class=\"nb-search-bar__btn\" data-action=\"close\" title=\"Close (Escape)\">\r\n <span class=\"material-symbols-outlined\">close</span>\r\n </button>\r\n </div>\r\n <div class=\"nb-search-bar__replace-row\" style=\"display:none\">\r\n <div class=\"nb-search-bar__spacer\"></div>\r\n <div class=\"nb-search-bar__input-wrap\">\r\n <input class=\"nb-search-bar__input nb-search-bar__replace-input\"\r\n type=\"text\" placeholder=\"Replace\" spellcheck=\"false\" />\r\n </div>\r\n <button class=\"nb-search-bar__btn\" data-action=\"replace\" title=\"Replace (Ctrl+Shift+1)\">\r\n <span class=\"material-symbols-outlined\">find_replace</span>\r\n </button>\r\n <button class=\"nb-search-bar__btn\" data-action=\"replace-all\" title=\"Replace All (Ctrl+Alt+Enter)\">\r\n <span class=\"material-symbols-outlined\">swap_horiz</span>\r\n </button>\r\n </div>\r\n `;\r\n\r\n this.#el = el;\r\n this.#searchInput = el.querySelector('.nb-search-bar__find-input');\r\n this.#replaceInput = el.querySelector('.nb-search-bar__replace-input');\r\n this.#matchInfo = el.querySelector('.nb-search-bar__match-info');\r\n\r\n this.#bindEvents();\r\n this.#parent.prepend(el);\r\n }\r\n\r\n #bindEvents() {\r\n // Search input\r\n this.#searchInput.addEventListener('input', () => this.#runSearch());\r\n\r\n this.#searchInput.addEventListener('keydown', (e) => {\r\n if (e.key === 'Enter' && !e.shiftKey) {\r\n e.preventDefault();\r\n this.#navigateNext();\r\n } else if (e.key === 'Enter' && e.shiftKey) {\r\n e.preventDefault();\r\n this.#navigatePrev();\r\n } else if (e.key === 'Escape') {\r\n e.preventDefault();\r\n this.close();\r\n }\r\n });\r\n\r\n // Replace input\r\n this.#replaceInput.addEventListener('keydown', (e) => {\r\n if (e.key === 'Enter' && !e.shiftKey) {\r\n e.preventDefault();\r\n this.#doReplace();\r\n } else if (e.key === 'Escape') {\r\n e.preventDefault();\r\n this.close();\r\n }\r\n });\r\n\r\n // Buttons\r\n this.#el.addEventListener('click', (e) => {\r\n const btn = e.target.closest('[data-action]');\r\n if (!btn) return;\r\n switch (btn.dataset.action) {\r\n case 'prev': this.#navigatePrev(); break;\r\n case 'next': this.#navigateNext(); break;\r\n case 'close': this.close(); break;\r\n case 'replace': this.#doReplace(); break;\r\n case 'replace-all': this.#doReplaceAll(); break;\r\n case 'case':\r\n this.#caseSensitive = !this.#caseSensitive;\r\n btn.classList.toggle('nb-search-bar__btn--active', this.#caseSensitive);\r\n this.#runSearch();\r\n break;\r\n case 'word':\r\n this.#wholeWord = !this.#wholeWord;\r\n btn.classList.toggle('nb-search-bar__btn--active', this.#wholeWord);\r\n this.#runSearch();\r\n break;\r\n case 'regex':\r\n this.#useRegex = !this.#useRegex;\r\n btn.classList.toggle('nb-search-bar__btn--active', this.#useRegex);\r\n this.#runSearch();\r\n break;\r\n }\r\n });\r\n\r\n // Toggle replace row\r\n this.#el.querySelector('.nb-search-bar__toggle-replace').addEventListener('click', () => {\r\n this.#showReplace = !this.#showReplace;\r\n this.#el.querySelector('.nb-search-bar__replace-row').style.display =\r\n this.#showReplace ? '' : 'none';\r\n this.#updateToggleIcon();\r\n });\r\n\r\n // Alt shortcuts for toggles\r\n this.#el.addEventListener('keydown', (e) => {\r\n if (e.altKey && e.key === 'c') {\r\n e.preventDefault();\r\n this.#caseSensitive = !this.#caseSensitive;\r\n this.#el.querySelector('[data-action=\"case\"]').classList.toggle('nb-search-bar__btn--active', this.#caseSensitive);\r\n this.#runSearch();\r\n } else if (e.altKey && e.key === 'w') {\r\n e.preventDefault();\r\n this.#wholeWord = !this.#wholeWord;\r\n this.#el.querySelector('[data-action=\"word\"]').classList.toggle('nb-search-bar__btn--active', this.#wholeWord);\r\n this.#runSearch();\r\n } else if (e.altKey && e.key === 'r') {\r\n e.preventDefault();\r\n this.#useRegex = !this.#useRegex;\r\n this.#el.querySelector('[data-action=\"regex\"]').classList.toggle('nb-search-bar__btn--active', this.#useRegex);\r\n this.#runSearch();\r\n }\r\n });\r\n\r\n // Prevent keydown from bubbling to NotebookEditor (avoids undo/redo interference)\r\n // Also suppress browser Ctrl+F/Ctrl+H when the search bar already has focus\r\n this.#el.addEventListener('keydown', (e) => {\r\n e.stopPropagation();\r\n if (e.ctrlKey && (e.key === 'f' || e.key === 'h')) {\r\n e.preventDefault();\r\n if (e.key === 'h' && !this.#showReplace) {\r\n this.#showReplace = true;\r\n this.#el.querySelector('.nb-search-bar__replace-row').style.display = '';\r\n this.#updateToggleIcon();\r\n }\r\n }\r\n });\r\n }\r\n\r\n #updateToggleIcon() {\r\n const icon = this.#el.querySelector('.nb-search-bar__toggle-replace .material-symbols-outlined');\r\n if (icon) icon.textContent = this.#showReplace ? 'expand_more' : 'chevron_right';\r\n }\r\n\r\n #updateMatchInfo() {\r\n if (!this.#matchInfo) return;\r\n if (this.#matches.length === 0) {\r\n const hasQuery = this.#searchInput?.value?.length > 0;\r\n this.#matchInfo.textContent = hasQuery ? 'No results' : '';\r\n this.#matchInfo.classList.toggle('nb-search-bar__match-info--no-results', hasQuery);\r\n } else {\r\n this.#matchInfo.textContent = `${this.#currentIndex + 1} of ${this.#matches.length}`;\r\n this.#matchInfo.classList.remove('nb-search-bar__match-info--no-results');\r\n }\r\n }\r\n\r\n // \u2500\u2500\u2500 Search logic \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #buildQuery() {\r\n const raw = this.#searchInput.value;\r\n if (!raw) return null;\r\n\r\n let pattern;\r\n if (this.#useRegex) {\r\n try { pattern = raw; new RegExp(pattern); }\r\n catch { return null; } // invalid regex\r\n } else {\r\n pattern = raw.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n }\r\n\r\n if (this.#wholeWord) pattern = `\\\\b${pattern}\\\\b`;\r\n\r\n const flags = 'g' + (this.#caseSensitive ? '' : 'i');\r\n try { return new RegExp(pattern, flags); }\r\n catch { return null; }\r\n }\r\n\r\n #runSearch() {\r\n this.#clearAllHighlights();\r\n this.#matches = [];\r\n this.#currentIndex = -1;\r\n\r\n const query = this.#buildQuery();\r\n if (!query) {\r\n this.#updateMatchInfo();\r\n return;\r\n }\r\n\r\n const cells = this.#getCells();\r\n\r\n for (const { id } of cells) {\r\n const renderer = this.#getCellRenderer(id);\r\n if (!renderer) continue;\r\n\r\n const text = renderer.getSearchableText();\r\n if (!text) continue;\r\n\r\n query.lastIndex = 0;\r\n let m;\r\n let indexInCell = 0;\r\n while ((m = query.exec(text)) !== null) {\r\n this.#matches.push({ cellId: id, indexInCell, charOffset: m.index });\r\n indexInCell++;\r\n if (!query.global) break;\r\n }\r\n }\r\n\r\n if (this.#matches.length > 0) {\r\n this.#currentIndex = 0;\r\n this.#applyHighlights();\r\n }\r\n\r\n this.#updateMatchInfo();\r\n }\r\n\r\n // \u2500\u2500\u2500 Navigation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #navigateNext() {\r\n if (this.#matches.length === 0) return;\r\n this.#currentIndex = (this.#currentIndex + 1) % this.#matches.length;\r\n this.#applyHighlights();\r\n this.#scrollToCurrentMatch();\r\n this.#updateMatchInfo();\r\n }\r\n\r\n #navigatePrev() {\r\n if (this.#matches.length === 0) return;\r\n this.#currentIndex = (this.#currentIndex - 1 + this.#matches.length) % this.#matches.length;\r\n this.#applyHighlights();\r\n this.#scrollToCurrentMatch();\r\n this.#updateMatchInfo();\r\n }\r\n\r\n #scrollToCurrentMatch() {\r\n const match = this.#matches[this.#currentIndex];\r\n if (!match) return;\r\n this.#scrollToCell(match.cellId);\r\n\r\n // Also scroll the specific highlight into view if possible\r\n requestAnimationFrame(() => {\r\n const cellEl = this.#getCellElement(match.cellId);\r\n if (!cellEl) return;\r\n const active = cellEl.querySelector('.nb-search-match--active');\r\n if (active) active.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\r\n });\r\n }\r\n\r\n // \u2500\u2500\u2500 Highlighting \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #applyHighlights() {\r\n const query = this.#buildQuery();\r\n if (!query) return;\r\n\r\n // Group matches by cell\r\n const matchesByCell = new Map();\r\n for (const m of this.#matches) {\r\n if (!matchesByCell.has(m.cellId)) matchesByCell.set(m.cellId, []);\r\n matchesByCell.get(m.cellId).push(m);\r\n }\r\n\r\n const currentMatch = this.#matches[this.#currentIndex];\r\n\r\n // Apply highlights to each cell that has matches\r\n const cells = this.#getCells();\r\n for (const { id } of cells) {\r\n const renderer = this.#getCellRenderer(id);\r\n if (!renderer) continue;\r\n\r\n const cellEl = this.#getCellElement(id);\r\n\r\n if (matchesByCell.has(id)) {\r\n const activeOffset = (currentMatch?.cellId === id)\r\n ? currentMatch.charOffset\r\n : -1;\r\n renderer.applySearchHighlights(query, activeOffset);\r\n cellEl?.classList.add('notebook-cell--search-match');\r\n cellEl?.classList.toggle('notebook-cell--search-active', currentMatch?.cellId === id);\r\n } else {\r\n renderer.clearSearchHighlights();\r\n cellEl?.classList.remove('notebook-cell--search-match', 'notebook-cell--search-active');\r\n }\r\n }\r\n }\r\n\r\n #clearAllHighlights() {\r\n const cells = this.#getCells();\r\n for (const { id } of cells) {\r\n const renderer = this.#getCellRenderer(id);\r\n renderer?.clearSearchHighlights();\r\n const cellEl = this.#getCellElement(id);\r\n cellEl?.classList.remove('notebook-cell--search-match', 'notebook-cell--search-active');\r\n }\r\n }\r\n\r\n // \u2500\u2500\u2500 Replace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #doReplace() {\r\n if (this.#matches.length === 0 || this.#currentIndex < 0) return;\r\n\r\n const match = this.#matches[this.#currentIndex];\r\n const renderer = this.#getCellRenderer(match.cellId);\r\n if (!renderer) return;\r\n\r\n const query = this.#buildQuery();\r\n if (!query) return;\r\n\r\n const replacement = this.#replaceInput.value;\r\n const success = renderer.replaceMatch(query, match.indexInCell, replacement);\r\n\r\n if (success) {\r\n // Re-run search (match count changed), try to stay near same position\r\n const wasIndex = this.#currentIndex;\r\n this.#runSearch();\r\n if (this.#matches.length > 0) {\r\n this.#currentIndex = Math.min(wasIndex, this.#matches.length - 1);\r\n this.#applyHighlights();\r\n this.#scrollToCurrentMatch();\r\n this.#updateMatchInfo();\r\n }\r\n }\r\n }\r\n\r\n #doReplaceAll() {\r\n const query = this.#buildQuery();\r\n if (!query || this.#matches.length === 0) return;\r\n\r\n const replacement = this.#replaceInput.value;\r\n\r\n // Collect unique cell IDs that have matches\r\n const cellIds = [...new Set(this.#matches.map(m => m.cellId))];\r\n let totalReplaced = 0;\r\n\r\n for (const cellId of cellIds) {\r\n const renderer = this.#getCellRenderer(cellId);\r\n if (renderer) totalReplaced += renderer.replaceAll(query, replacement);\r\n }\r\n\r\n if (totalReplaced > 0) this.#runSearch();\r\n }\r\n}\r\n", "/**\r\n * NotebookUndoManager\r\n *\r\n * Command-pattern undo/redo for notebook cell operations.\r\n *\r\n * Each command: { description: string, undo: () => (void | Promise), redo: () => (void | Promise) }\r\n *\r\n * Operations tracked by NotebookEditor:\r\n * - Cell add\r\n * - Cell delete\r\n * - Cell move (up/down, drag-reorder)\r\n * - Cell config change (debounced per-cell snapshot)\r\n */\r\nexport class NotebookUndoManager {\r\n\r\n /** @type {Array<{ description: string, undo: Function, redo: Function }>} */\r\n #undoStack = [];\r\n\r\n /** @type {Array<{ description: string, undo: Function, redo: Function }>} */\r\n #redoStack = [];\r\n\r\n /** @type {number} */\r\n #maxSize;\r\n\r\n /** @type {Function|null} Called with (canUndo: boolean, canRedo: boolean) after each change */\r\n #onStateChange;\r\n\r\n /** @type {boolean} Prevents re-entrant undo/redo */\r\n #applying = false;\r\n\r\n /**\r\n * @param {{ maxSize?: number, onStateChange?: Function }} opts\r\n */\r\n constructor({ maxSize = 100, onStateChange = null } = {}) {\r\n this.#maxSize = maxSize;\r\n this.#onStateChange = onStateChange;\r\n }\r\n\r\n // \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n get canUndo() { return this.#undoStack.length > 0; }\r\n get canRedo() { return this.#redoStack.length > 0; }\r\n\r\n /**\r\n * Record a new command. Clears the redo stack.\r\n * Should NOT be called from inside undo/redo callbacks.\r\n *\r\n * @param {{ description: string, undo: Function, redo: Function }} command\r\n */\r\n push(command) {\r\n if (this.#applying) return; // ignore changes triggered by undo/redo\r\n this.#undoStack.push(command);\r\n if (this.#undoStack.length > this.#maxSize) {\r\n this.#undoStack.shift();\r\n }\r\n this.#redoStack = [];\r\n this.#notify();\r\n }\r\n\r\n /**\r\n * Undo the last command.\r\n * @returns {Promise<boolean>} true if something was undone\r\n */\r\n async undo() {\r\n const cmd = this.#undoStack.pop();\r\n if (!cmd) return false;\r\n this.#applying = true;\r\n try {\r\n await cmd.undo();\r\n } finally {\r\n this.#applying = false;\r\n }\r\n this.#redoStack.push(cmd);\r\n this.#notify();\r\n return true;\r\n }\r\n\r\n /**\r\n * Redo the last undone command.\r\n * @returns {Promise<boolean>} true if something was redone\r\n */\r\n async redo() {\r\n const cmd = this.#redoStack.pop();\r\n if (!cmd) return false;\r\n this.#applying = true;\r\n try {\r\n await cmd.redo();\r\n } finally {\r\n this.#applying = false;\r\n }\r\n this.#undoStack.push(cmd);\r\n this.#notify();\r\n return true;\r\n }\r\n\r\n /** Reset both stacks (e.g., after file load). */\r\n clear() {\r\n this.#undoStack = [];\r\n this.#redoStack = [];\r\n this.#notify();\r\n }\r\n\r\n // \u2500\u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #notify() {\r\n this.#onStateChange?.(this.canUndo, this.canRedo);\r\n }\r\n}\r\n", "/**\r\n * SplitPaneContainer \u2014 horizontal split with a draggable divider.\r\n *\r\n * Manages exactly 1 or 2 child panes. When only one pane exists, the divider\r\n * is hidden and the single pane fills the container. When split, both panes\r\n * share space according to a ratio (default 50/50), adjustable via drag.\r\n *\r\n * Usage:\r\n * const split = new SplitPaneContainer();\r\n * split.mount(parentEl);\r\n * split.setPanes(leftEl); // single pane\r\n * split.setPanes(leftEl, rightEl); // split view\r\n * split.setRatio(0.6); // 60/40 split\r\n * split.unsplit(); // collapse to single pane\r\n */\r\n\r\nexport class SplitPaneContainer {\r\n /** @type {HTMLElement} */\r\n #container = null;\r\n\r\n /** @type {HTMLElement} */\r\n #pane1 = null;\r\n\r\n /** @type {HTMLElement} */\r\n #pane2 = null;\r\n\r\n /** @type {HTMLElement} */\r\n #divider = null;\r\n\r\n /** @type {number} 0..1, fraction of width for pane 1 */\r\n #ratio = 0.5;\r\n\r\n /** @type {boolean} */\r\n #isSplit = false;\r\n\r\n /** @type {Function|null} */\r\n #onRatioChanged = null;\r\n\r\n // Drag state\r\n #dragging = false;\r\n #boundOnMouseMove = null;\r\n #boundOnMouseUp = null;\r\n\r\n /**\r\n * @param {{ onRatioChanged?: (ratio: number) => void }} opts\r\n */\r\n constructor(opts = {}) {\r\n this.#onRatioChanged = opts.onRatioChanged ?? null;\r\n }\r\n\r\n get isSplit() { return this.#isSplit; }\r\n get ratio() { return this.#ratio; }\r\n\r\n mount(container) {\r\n this.#container = container;\r\n this.#container.classList.add('split-pane-container');\r\n this.#container.innerHTML = '';\r\n\r\n // Divider\r\n this.#divider = document.createElement('div');\r\n this.#divider.className = 'split-pane-divider';\r\n this.#divider.addEventListener('mousedown', (e) => this.#onDividerMouseDown(e));\r\n\r\n this.#applyLayout();\r\n }\r\n\r\n /**\r\n * Set pane elements. Pass one element for single view, two for split.\r\n * @param {HTMLElement} pane1\r\n * @param {HTMLElement} [pane2]\r\n */\r\n setPanes(pane1, pane2) {\r\n this.#pane1 = pane1;\r\n this.#pane2 = pane2 ?? null;\r\n this.#isSplit = !!pane2;\r\n this.#applyLayout();\r\n }\r\n\r\n /**\r\n * Set the split ratio (fraction for left pane).\r\n * @param {number} ratio \u2014 0.2..0.8\r\n */\r\n setRatio(ratio) {\r\n this.#ratio = Math.max(0.2, Math.min(0.8, ratio));\r\n if (this.#isSplit) this.#applySizes();\r\n }\r\n\r\n /**\r\n * Collapse to single pane (left). Returns the removed right pane element.\r\n * @returns {HTMLElement|null}\r\n */\r\n unsplit() {\r\n if (!this.#isSplit) return null;\r\n const removed = this.#pane2;\r\n this.#pane2 = null;\r\n this.#isSplit = false;\r\n this.#applyLayout();\r\n return removed;\r\n }\r\n\r\n dispose() {\r\n this.#stopDrag();\r\n this.#container = null;\r\n this.#pane1 = null;\r\n this.#pane2 = null;\r\n this.#divider = null;\r\n }\r\n\r\n // \u2500\u2500\u2500 Layout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #applyLayout() {\r\n if (!this.#container) return;\r\n this.#container.innerHTML = '';\r\n\r\n if (!this.#pane1) return;\r\n\r\n // ADD the class, never assign it: a pane is the caller's element\r\n // (an EditorPane root is `.editor-pane`), and its own classes are\r\n // what its stylesheet reaches it by.\r\n if (!this.#isSplit) {\r\n // Single pane: fills container\r\n this.#pane1.classList.add('split-pane');\r\n this.#pane1.style.flex = '1 1 0';\r\n this.#pane1.style.minWidth = '0';\r\n this.#container.appendChild(this.#pane1);\r\n this.#container.classList.remove('split-pane-container--split');\r\n } else {\r\n // Split view\r\n this.#pane1.classList.add('split-pane');\r\n this.#pane2.classList.add('split-pane');\r\n this.#pane1.style.minWidth = '0';\r\n this.#pane2.style.minWidth = '0';\r\n\r\n this.#container.appendChild(this.#pane1);\r\n this.#container.appendChild(this.#divider);\r\n this.#container.appendChild(this.#pane2);\r\n this.#container.classList.add('split-pane-container--split');\r\n\r\n this.#applySizes();\r\n }\r\n }\r\n\r\n #applySizes() {\r\n if (!this.#pane1 || !this.#pane2) return;\r\n // Use flex-grow proportional to ratio\r\n const r = this.#ratio;\r\n this.#pane1.style.flex = `${r} 1 0`;\r\n this.#pane2.style.flex = `${1 - r} 1 0`;\r\n }\r\n\r\n // \u2500\u2500\u2500 Divider drag \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #onDividerMouseDown(e) {\r\n if (e.button !== 0) return;\r\n e.preventDefault();\r\n this.#dragging = true;\r\n this.#divider.classList.add('split-pane-divider--active');\r\n document.body.style.cursor = 'col-resize';\r\n\r\n // Prevent text selection and iframes capturing mouse\r\n document.body.style.userSelect = 'none';\r\n this.#container.querySelectorAll('iframe').forEach(f => f.style.pointerEvents = 'none');\r\n\r\n this.#boundOnMouseMove = (e) => this.#onMouseMove(e);\r\n this.#boundOnMouseUp = () => this.#onMouseUp();\r\n document.addEventListener('mousemove', this.#boundOnMouseMove);\r\n document.addEventListener('mouseup', this.#boundOnMouseUp);\r\n }\r\n\r\n #onMouseMove(e) {\r\n if (!this.#dragging || !this.#container) return;\r\n const rect = this.#container.getBoundingClientRect();\r\n const dividerWidth = this.#divider.offsetWidth;\r\n const usable = rect.width - dividerWidth;\r\n if (usable <= 0) return;\r\n\r\n const x = e.clientX - rect.left - dividerWidth / 2;\r\n const ratio = Math.max(0.2, Math.min(0.8, x / usable));\r\n this.#ratio = ratio;\r\n this.#applySizes();\r\n }\r\n\r\n #onMouseUp() {\r\n this.#stopDrag();\r\n this.#onRatioChanged?.(this.#ratio);\r\n }\r\n\r\n #stopDrag() {\r\n if (!this.#dragging) return;\r\n this.#dragging = false;\r\n this.#divider?.classList.remove('split-pane-divider--active');\r\n document.body.style.cursor = '';\r\n document.body.style.userSelect = '';\r\n this.#container?.querySelectorAll('iframe').forEach(f => f.style.pointerEvents = '');\r\n document.removeEventListener('mousemove', this.#boundOnMouseMove);\r\n document.removeEventListener('mouseup', this.#boundOnMouseUp);\r\n this.#boundOnMouseMove = null;\r\n this.#boundOnMouseUp = null;\r\n }\r\n}\r\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\r\n * EditorPane \u2014 one half (or whole) of the split editor.\r\n *\r\n * Each pane owns:\r\n * - A NotebookTabBar instance\r\n * - An editor container where the active file's editor is mounted\r\n * - A list of file paths assigned to this pane\r\n * - The active file path within this pane\r\n *\r\n * The pane does NOT mount editors itself \u2014 it delegates to the parent\r\n * (NotebookPage) via callbacks, since editor mounting requires access to\r\n * ProjectModel, symbol index, plot controller, etc.\r\n *\r\n * Layout:\r\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\u2510\r\n * \u2502 [Tab1] [Tab2] \u2502 \u2190 NotebookTabBar\r\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\u2524\r\n * \u2502 \u2502\r\n * \u2502 Editor content \u2502 \u2190 editorContainer\r\n * \u2502 \u2502\r\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\u2518\r\n */\r\n\r\nimport { NotebookTabBar, getTabDragMime } from './notebook_tab_bar.js';\r\n\r\nexport class EditorPane {\r\n /** @type {string} */\r\n #id;\r\n\r\n /** @type {HTMLElement} root element */\r\n #el = null;\r\n\r\n /** @type {NotebookTabBar} */\r\n #tabBar = null;\r\n\r\n /** @type {HTMLElement} */\r\n #editorContainer = null;\r\n\r\n /** @type {string[]} file paths in tab order */\r\n #filePaths = [];\r\n\r\n /** @type {string|null} */\r\n #activeFilePath = null;\r\n\r\n /** @type {string|null} file path of the preview (transient) tab, if any */\r\n #previewFilePath = null;\r\n\r\n /** @type {object} callbacks from parent */\r\n #callbacks = {};\r\n\r\n /** @type {boolean} whether this pane is the focused/active pane */\r\n #focused = false;\r\n\r\n /**\r\n * @param {string} id \u2014 unique pane identifier ('left' or 'right')\r\n * @param {object} callbacks\r\n * @param {Function} callbacks.onActivate \u2014 (paneId, filePath) => void\r\n * @param {Function} callbacks.onClose \u2014 (paneId, filePath) => void\r\n * @param {Function} callbacks.onCloseOthers \u2014 (paneId, filePath) => void\r\n * @param {Function} callbacks.onCloseToRight \u2014 (paneId, filePath) => void\r\n * @param {Function} callbacks.onCloseAll \u2014 (paneId) => void\r\n * @param {Function} callbacks.onCloseSaved \u2014 (paneId) => void\r\n * @param {Function} callbacks.onRename \u2014 (oldPath, newPath) => void\r\n * @param {Function} callbacks.onDuplicate \u2014 (filePath) => void\r\n * @param {Function} callbacks.onReorder \u2014 (paneId, orderedPaths) => void\r\n * @param {Function} callbacks.onRevealInExplorer \u2014 (filePath) => void\r\n * @param {Function} callbacks.onFocus \u2014 (paneId) => void\r\n * @param {Function} callbacks.onSplitRight \u2014 (paneId, filePath) => void\r\n * @param {Function} callbacks.onMoveToOtherPane \u2014 (paneId, filePath) => void\r\n */\r\n constructor(id, callbacks = {}) {\r\n this.#id = id;\r\n this.#callbacks = callbacks;\r\n }\r\n\r\n get id() { return this.#id; }\r\n get activeFilePath() { return this.#activeFilePath; }\r\n get filePaths() { return [...this.#filePaths]; }\r\n get editorContainer() { return this.#editorContainer; }\r\n get element() { return this.#el; }\r\n get isEmpty() { return this.#filePaths.length === 0; }\r\n\r\n /**\r\n * Build DOM and mount the tab bar.\r\n * @returns {HTMLElement} the root element (to be placed in SplitPaneContainer)\r\n */\r\n mount() {\r\n this.#el = document.createElement('div');\r\n this.#el.className = 'editor-pane';\r\n this.#el.dataset.paneId = this.#id;\r\n\r\n // Focus tracking \u2014 clicking anywhere in the pane focuses it\r\n this.#el.addEventListener('mousedown', () => {\r\n this.#callbacks.onFocus?.(this.#id);\r\n }, true);\r\n\r\n // Tab bar\r\n const tabBarEl = document.createElement('div');\r\n this.#el.appendChild(tabBarEl);\r\n\r\n this.#tabBar = new NotebookTabBar();\r\n this.#tabBar.mount(tabBarEl, {\r\n onActivate: (path) => this.#callbacks.onActivate?.(this.#id, path),\r\n onClose: (path) => this.#callbacks.onClose?.(this.#id, path),\r\n onCloseOthers: (path) => this.#callbacks.onCloseOthers?.(this.#id, path),\r\n onCloseToRight: (path) => this.#callbacks.onCloseToRight?.(this.#id, path),\r\n onCloseAll: () => this.#callbacks.onCloseAll?.(this.#id),\r\n onCloseSaved: () => this.#callbacks.onCloseSaved?.(this.#id),\r\n onRename: (o, n) => this.#callbacks.onRename?.(o, n),\r\n onDuplicate: (path) => this.#callbacks.onDuplicate?.(path),\r\n onReorder: (paths) => this.#callbacks.onReorder?.(this.#id, paths),\r\n onRevealInExplorer:(path) => this.#callbacks.onRevealInExplorer?.(path),\r\n onSplitRight: (path) => this.#callbacks.onSplitRight?.(this.#id, path),\r\n onMoveToOtherPane: (path) => this.#callbacks.onMoveToOtherPane?.(this.#id, path),\r\n onDropFromOtherPane: (path) => this.#callbacks.onDropFromOtherPane?.(this.#id, path),\r\n });\r\n\r\n // Editor body wrapper \u2014 positioned container that fills remaining\r\n // flex space. The actual editor container is absolutely positioned\r\n // inside, so its height is ALWAYS the wrapper's height, regardless\r\n // of content. This makes the scroll chain bulletproof \u2014 no flex\r\n // ancestor chain needed for the scrollbar to work.\r\n const body = document.createElement('div');\r\n body.className = 'editor-pane__body';\r\n this.#el.appendChild(body);\r\n\r\n // Editor container (absolutely positioned inside wrapper)\r\n this.#editorContainer = document.createElement('div');\r\n this.#editorContainer.className = 'notebook-editor-container';\r\n body.appendChild(this.#editorContainer);\r\n\r\n // Accept cross-pane tab drops on the editor area too\r\n this.#editorContainer.addEventListener('dragover', (e) => {\r\n if (this.#isExternalTabDrag(e)) {\r\n e.preventDefault();\r\n try { e.dataTransfer.dropEffect = 'move'; } catch {}\r\n this.#editorContainer.classList.add('notebook-editor-container--drop-target');\r\n }\r\n });\r\n this.#editorContainer.addEventListener('drop', (e) => {\r\n if (this.#isExternalTabDrag(e)) {\r\n e.preventDefault();\r\n this.#editorContainer.classList.remove('notebook-editor-container--drop-target');\r\n const filePath = e.dataTransfer.getData(getTabDragMime())\r\n || e.dataTransfer.getData('text/plain');\r\n if (filePath) {\r\n this.#callbacks.onDropFromOtherPane?.(this.#id, filePath);\r\n }\r\n }\r\n });\r\n this.#editorContainer.addEventListener('dragleave', (e) => {\r\n if (!this.#editorContainer.contains(e.relatedTarget)) {\r\n this.#editorContainer.classList.remove('notebook-editor-container--drop-target');\r\n }\r\n });\r\n\r\n return this.#el;\r\n }\r\n\r\n /**\r\n * Update the file list and active tab.\r\n * @param {Array<{filePath: string, fileType: string, isDirty: boolean}>} tabs\r\n * @param {string|null} activeFilePath\r\n */\r\n syncTabBar(tabs, activeFilePath) {\r\n this.#tabBar?.update(tabs, activeFilePath);\r\n }\r\n\r\n /**\r\n * Set the files assigned to this pane.\r\n * @param {string[]} filePaths\r\n */\r\n setFilePaths(filePaths) {\r\n this.#filePaths = [...filePaths];\r\n }\r\n\r\n /**\r\n * Add a file to this pane's tabs (at end).\r\n * @param {string} filePath\r\n */\r\n addFile(filePath) {\r\n if (!this.#filePaths.includes(filePath)) {\r\n this.#filePaths.push(filePath);\r\n }\r\n }\r\n\r\n /**\r\n * Remove a file from this pane's tabs.\r\n * @param {string} filePath\r\n */\r\n removeFile(filePath) {\r\n this.#filePaths = this.#filePaths.filter(p => p !== filePath);\r\n if (this.#previewFilePath === filePath) this.#previewFilePath = null;\r\n if (this.#activeFilePath === filePath) {\r\n this.#activeFilePath = this.#filePaths.length > 0\r\n ? this.#filePaths[this.#filePaths.length - 1]\r\n : null;\r\n }\r\n }\r\n\r\n hasFile(filePath) {\r\n return this.#filePaths.includes(filePath);\r\n }\r\n\r\n setActiveFile(filePath) {\r\n this.#activeFilePath = filePath;\r\n }\r\n\r\n setFocused(focused) {\r\n this.#focused = focused;\r\n this.#el?.classList.toggle('editor-pane--focused', focused);\r\n }\r\n\r\n markDirty(filePath, isDirty) {\r\n this.#tabBar?.markDirty(filePath, isDirty);\r\n }\r\n\r\n reorderFiles(orderedPaths) {\r\n // Keep only paths that belong to this pane\r\n const valid = orderedPaths.filter(p => this.#filePaths.includes(p));\r\n // Append any missing (shouldn't happen)\r\n for (const p of this.#filePaths) {\r\n if (!valid.includes(p)) valid.push(p);\r\n }\r\n this.#filePaths = valid;\r\n }\r\n\r\n renameFile(oldPath, newPath) {\r\n const idx = this.#filePaths.indexOf(oldPath);\r\n if (idx !== -1) this.#filePaths[idx] = newPath;\r\n if (this.#activeFilePath === oldPath) this.#activeFilePath = newPath;\r\n if (this.#previewFilePath === oldPath) this.#previewFilePath = newPath;\r\n }\r\n\r\n // \u2500\u2500\u2500 Preview (transient) tab \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n /** @returns {string|null} the preview file path, or null if no preview tab */\r\n get previewFilePath() { return this.#previewFilePath; }\r\n\r\n /** Mark a file as the preview tab. Only one preview tab per pane. */\r\n setPreview(filePath) { this.#previewFilePath = filePath; }\r\n\r\n /** Whether a file is the preview (transient) tab. */\r\n isPreview(filePath) { return this.#previewFilePath === filePath; }\r\n\r\n /** Pin the preview tab (promote to permanent). */\r\n pinPreview() { this.#previewFilePath = null; }\r\n\r\n /** Check if a drag event is carrying a tab from another pane. */\r\n #isExternalTabDrag(e) {\r\n try { return e.dataTransfer.types.includes(getTabDragMime()); } catch { return false; }\r\n }\r\n\r\n dispose() {\r\n this.#tabBar?.dispose();\r\n this.#tabBar = null;\r\n this.#el?.remove();\r\n this.#el = null;\r\n this.#editorContainer = null;\r\n this.#filePaths = [];\r\n this.#activeFilePath = null;\r\n this.#previewFilePath = null;\r\n }\r\n}\r\n", "/**\n * Monaco Loader \u2014 wraps the AMD require() bootstrap into an ES module Promise.\n *\n * Monaco ships as AMD modules. vs/loader.js is injected dynamically on demand\n * (NOT via a global <script> tag) so it does not pollute window.define before\n * Plotly loads. Plotly auto-detects AMD define and would wrap itself as a module\n * instead of setting window.Plotly if loader.js ran at page load.\n */\n\nlet _resolveMonaco;\n\n/**\n * The attempt in flight, the one that succeeded, or one that failed for good.\n *\n * Whether a failure is retryable depends on how far the attempt got:\n *\n * - vs/loader.js did not load (a 404, a wrong path): nothing is left behind,\n * so the attempt is cleared and the next initMonaco() starts over. The flag\n * this replaced was set BEFORE loader.js had loaded, so one 404 blocked\n * every retry for the life of the page (BUG-0023).\n * - Monaco's AMD loader reported an error (vs/editor/editor.main or one of\n * its dependencies failed): FINAL for the page. window.require is now\n * Monaco's loader, which records the failed module as complete-with-error\n * and hands every later require() of it the same error \u2014 it has no undef.\n * Retrying cannot help, so the rejected attempt is kept and every caller\n * gets that one error. Recovering takes a page reload.\n * @type {Promise<typeof monaco> | null}\n */\nlet _attempt = null;\n\n/**\n * Resolves with the `monaco` namespace once any initMonaco() call has loaded it.\n *\n * It never rejects, because a failed attempt is not final \u2014 a later\n * initMonaco() may still succeed. To learn that an attempt FAILED, await\n * initMonaco() (or getEditorFactory()), which rejects.\n * @type {Promise<typeof monaco>}\n */\nexport const monacoReady = new Promise(resolve => { _resolveMonaco = resolve; });\n\n/**\n * Monaco's asynchronous CancellationError is benign; keep it off the page.\n *\n * Monaco starts timers it never awaits: its WordHighlighter calls\n * `runDelayer.trigger()` (a 50 ms Delayer) when an editor gets focus and\n * drops the promise. Dispose the editor inside those 50 ms and\n * `Delayer.dispose() -> cancel()` rejects that promise with a CancellationError\n * (name and message both 'Canceled') that nothing handles, so the browser\n * reports an uncaught 'Canceled' (BUG-0038). VS Code hides these with its own\n * unhandledrejection handler; standalone Monaco has none, so FlexDesk installs\n * one here: it calls preventDefault() for exactly that error and leaves every\n * other rejection to surface as before.\n *\n * Installed once per page, by the first initMonaco() that starts a load, and\n * kept for the life of the page \u2014 like Monaco itself, which cannot be unloaded.\n */\nlet _cancellationGuardInstalled = false;\n\nfunction isMonacoCancellation(reason) {\n return reason?.name === 'Canceled' && reason?.message === 'Canceled';\n}\n\nfunction installCancellationGuard() {\n if (_cancellationGuardInstalled) return;\n _cancellationGuardInstalled = true;\n // `?.`: the loader suites' stand-in pages (tests/monaco_env.mjs and the\n // suites that set `window = globalThis` under Node) have no event target.\n window.addEventListener?.('unhandledrejection', (event) => {\n if (isMonacoCancellation(event.reason)) event.preventDefault();\n });\n}\n\nfunction injectLoaderScript(src) {\n return new Promise((resolve, reject) => {\n const s = document.createElement('script');\n s.src = src;\n s.onload = resolve;\n s.onerror = () => {\n // Take the dead tag out, so a retry is a fresh request and not a\n // second tag beside the first.\n s.remove?.();\n reject(new Error(`Failed to load Monaco loader: ${src}`));\n };\n document.head.appendChild(s);\n });\n}\n\nfunction injectMonacoCss(basePath) {\n const id = 'monaco-editor-css';\n const href = `${basePath}/vs/editor/editor.main.css`;\n const existing = document.getElementById(id);\n if (existing) {\n // A retry with a corrected path must not keep the stylesheet of the bad one.\n if (existing.getAttribute?.('href') !== href) existing.setAttribute?.('href', href);\n return;\n }\n const link = document.createElement('link');\n link.id = id;\n link.rel = 'stylesheet';\n link.href = href;\n document.head.appendChild(link);\n}\n\nfunction requireEditorMain(basePath) {\n return new Promise((resolve, reject) => {\n window.require.config({\n paths: { vs: `${basePath}/vs` },\n 'vs/nls': { availableLanguages: {} },\n });\n window.require(['vs/editor/editor.main'], () => {\n if (window.monaco) resolve(window.monaco);\n else reject(new Error(\n `[twm/editor] vs/editor/editor.main loaded from ${basePath}/vs but did not `\n + 'set window.monaco.'));\n }, (err) => {\n reject(new Error(\n `[twm/editor] Failed to load Monaco from ${basePath}/vs: ${err?.message ?? err}. `\n + 'Monaco\\'s AMD loader caches this failure, so it is final for this page: '\n + 'fix the path and reload.'));\n });\n });\n}\n\nasync function loadMonaco(basePath) {\n // Inject CSS and loader.js dynamically \u2014 kept out of the HTML to avoid global bleed\n injectMonacoCss(basePath);\n if (!window.require) {\n const src = `${basePath}/vs/loader.js`;\n await injectLoaderScript(src);\n if (!window.require) {\n throw new Error(\n `[twm/editor] ${src} loaded but did not define window.require; `\n + 'is it Monaco\\'s vs/loader.js?');\n }\n }\n return requireEditorMain(basePath);\n}\n\n/**\n * Call once when the notebook page initialises.\n * Where Monaco lives is the CONSUMER's business.\n *\n * `basePath` defaulted to 'vendor/monaco' \u2014 a path into ONE application's tree,\n * which would 404 for anyone else. Exactly the bug plotly_wrapper.js had, and it\n * takes exactly the same fix: no default, and say so if nobody supplies one.\n *\n * Monaco is a PEER dependency \u2014 14 MB this bundle deliberately does not contain.\n *\n * Idempotent: while an attempt is in flight or has succeeded, every caller\n * gets the same promise, whatever path it passes or whether it passes one \u2014\n * so getEditorFactory() piggybacks on a load the consumer started here.\n *\n * The returned promise REJECTS when no path is given and no attempt exists,\n * when loader.js fails to load, or when Monaco's AMD loader reports an error.\n * After a loader.js failure the next call tries again (with the path it is\n * given). An AMD-level failure is final for the page: every later call gets\n * the same rejection, and only a reload recovers (see `_attempt`).\n *\n * The first call that starts a load also installs one window\n * 'unhandledrejection' listener that hides Monaco's benign asynchronous\n * CancellationError, and nothing else (see `installCancellationGuard`).\n *\n * @param {string} [basePath] Path prefix for the vs/ directory. REQUIRED\n * unless an attempt is already in flight or has succeeded.\n * @returns {Promise<typeof monaco>}\n */\nexport function initMonaco(basePath) {\n if (_attempt) return _attempt;\n if (!basePath) {\n return Promise.reject(new Error(\n '[twm/editor] initMonaco() needs the path to your copy of Monaco, e.g. '\n + \"initMonaco('vendor/monaco'). Monaco is a peer dependency; the library \"\n + 'does not guess where you put it.'));\n }\n installCancellationGuard();\n const attempt = loadMonaco(basePath).then(\n (m) => { _resolveMonaco(m); return m; },\n (err) => {\n // Monaco's AMD loader is installed only once loader.js has run; a\n // failure before that leaves nothing cached and can be retried.\n if (!window.require && _attempt === attempt) _attempt = null;\n throw err;\n });\n _attempt = attempt;\n return attempt;\n}\n", "/**\n * Monaco Editor Factory\n *\n * Creates and manages Monaco editor instances for cells.\n * Handles:\n * - Creating full editors (multi-line, for CodeCells)\n * - Creating single-line inline editors (for ParameterCell values, expression fields)\n * - Theme registration (built-in dark and light themes, names set by\n * setEditorThemeNames(); default flexdesk-dark, flexdesk-light)\n * - Shared editor options\n * - Instance lifecycle (dispose on cell removal)\n */\n\nimport { monacoReady, initMonaco } from './monaco_loader.js';\n// The default language is the CONSUMER's. This imported ECOLANG_ID \u2014 EcoAgent's\n// expression language \u2014 so a generic Monaco factory shipped knowing the name of one\n// application's DSL. Register your language with Monaco and name it here.\nlet _defaultLanguage = 'plaintext';\n\n/** @param {string} id A language id already registered with Monaco. */\nexport function setDefaultLanguage(id) {\n if (typeof id === 'string' && id) _defaultLanguage = id;\n}\n\n/**\n * Dispose a Monaco editor, swallowing a \"Canceled\" error that `dispose()` throws\n * synchronously. It cannot catch the asynchronous kind \u2014 a pending Delayer's\n * promise rejected by the dispose, which surfaces later as an unhandled\n * rejection; the loader's window listener hides those (see\n * `installCancellationGuard` in monaco_loader.js, BUG-0038).\n */\nfunction safeDispose(editor) {\n try { editor.dispose(); } catch (e) { if (e?.message !== 'Canceled') throw e; }\n}\n\n// \u2500\u2500\u2500 Theme definitions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\nconst DARK_THEME = {\n base: 'vs-dark',\n inherit: true,\n rules: [\n { token: 'comment', foreground: '6a9955', fontStyle: 'italic' },\n { token: 'keyword', foreground: '569cd6', fontStyle: 'bold' },\n { token: 'keyword.directive', foreground: 'c586c0', fontStyle: 'bold' },\n { token: 'keyword.operator', foreground: '569cd6' },\n { token: 'constant', foreground: 'c586c0' },\n { token: 'number', foreground: 'b5cea8' },\n { token: 'string', foreground: 'ce9178' },\n { token: 'type.identifier', foreground: '4ec9b0', fontStyle: 'bold' },\n { token: 'support.function', foreground: 'a6e22e' },\n { token: 'identifier', foreground: '4fc1ff' },\n { token: 'operator', foreground: 'd4d4d4' },\n { token: 'delimiter', foreground: '808080' },\n ],\n colors: {\n // Layer-2 input well \u2014 almost black, matches the EcoAgent\n // three-layer contrast pattern (chrome #1a1a1a, content\n // #212121, input/code #0a0a0a). Makes editable surfaces\n // visually recess from the surrounding cards.\n 'editor.background': '#0a0a0a',\n 'editor.foreground': '#e0e0e0',\n 'editor.lineHighlightBackground': '#161616',\n 'editor.selectionBackground': '#264f78',\n 'editor.inactiveSelectionBackground': '#1f3350',\n 'editorLineNumber.foreground': '#5a5a5a',\n 'editorLineNumber.activeForeground': '#c6c6c6',\n 'editorCursor.foreground': '#39ff14',\n 'editor.findMatchBackground': '#9e6a03',\n 'editor.findMatchHighlightBackground': '#f2cc6030',\n 'editorIndentGuide.background1': '#2e2e2e',\n 'editorIndentGuide.activeBackground1': '#5a5a5a',\n 'editorGutter.background': '#0a0a0a',\n // Pop-ups stay slightly lighter so they read as floating\n // surfaces above the editor body, not part of it.\n 'editorWidget.background': '#1a1a1a',\n 'editorSuggestWidget.background': '#1a1a1a',\n 'editorSuggestWidget.border': '#383838',\n 'editorSuggestWidget.selectedBackground': '#094771',\n 'scrollbarSlider.background': '#4a4a4d40',\n 'scrollbarSlider.hoverBackground': '#4a4a4d70',\n },\n};\n\nconst LIGHT_THEME = {\n base: 'vs',\n inherit: true,\n rules: [\n { token: 'comment', foreground: '6a737d', fontStyle: 'italic' },\n { token: 'keyword', foreground: 'd73a49', fontStyle: 'bold' },\n { token: 'keyword.directive', foreground: '6f42c1', fontStyle: 'bold' },\n { token: 'keyword.operator', foreground: 'd73a49' },\n { token: 'constant', foreground: 'e36209' },\n { token: 'number', foreground: '005cc5' },\n { token: 'string', foreground: '22863a' },\n { token: 'type.identifier', foreground: '005cc5', fontStyle: 'bold' },\n { token: 'support.function', foreground: '6f42c1' },\n { token: 'identifier', foreground: '24292e' },\n { token: 'operator', foreground: 'd73a49' },\n { token: 'delimiter', foreground: '586069' },\n ],\n colors: {\n 'editor.background': '#ffffff',\n 'editor.foreground': '#24292e',\n 'editor.lineHighlightBackground': '#f6f8fa',\n 'editor.selectionBackground': '#c8d3e6',\n 'editorLineNumber.foreground': '#babbbd',\n 'editorLineNumber.activeForeground': '#586069',\n 'editorCursor.foreground': '#044289',\n 'editorGutter.background': '#ffffff',\n 'editorWidget.background': '#f6f8fa',\n 'editorSuggestWidget.background': '#f6f8fa',\n 'editorSuggestWidget.border': '#e1e4e8',\n 'scrollbarSlider.background': '#babbbd40',\n 'scrollbarSlider.hoverBackground': '#babbbd70',\n },\n};\n\n/** The names the built-in themes are registered under when the embedder\n * names none. */\nexport const DEFAULT_EDITOR_THEME_NAMES = Object.freeze({\n dark: 'flexdesk-dark',\n light: 'flexdesk-light',\n});\n\nlet _themeNames = DEFAULT_EDITOR_THEME_NAMES;\n\n/**\n * Name the built-in dark and light Monaco themes. Call before the first\n * factory is created: a factory starts on the dark name, and init() registers\n * the built-in themes under the names current at that moment. An embedder with\n * its own theme defines it with `monaco.editor.defineTheme(name, \u2026)` and passes\n * that name to `factory.setTheme(name)` instead.\n *\n * @param {{ dark?: string, light?: string }} [names]\n */\nexport function setEditorThemeNames({ dark, light } = {}) {\n _themeNames = Object.freeze({\n dark: (typeof dark === 'string' && dark) ? dark : DEFAULT_EDITOR_THEME_NAMES.dark,\n light: (typeof light === 'string' && light) ? light : DEFAULT_EDITOR_THEME_NAMES.light,\n });\n}\n\n/** @returns {{ dark: string, light: string }} The built-in themes' names. */\nexport function getEditorThemeNames() {\n return _themeNames;\n}\n\n/** Monaco instance -> names already defined on it, so each name is defined once. */\nconst _definedThemes = new WeakMap();\n\nfunction ensureThemes(monaco) {\n let defined = _definedThemes.get(monaco);\n if (!defined) { defined = new Set(); _definedThemes.set(monaco, defined); }\n for (const [name, theme] of [[_themeNames.dark, DARK_THEME], [_themeNames.light, LIGHT_THEME]]) {\n if (defined.has(name)) continue;\n defined.add(name);\n monaco.editor.defineTheme(name, theme);\n }\n}\n\n// \u2500\u2500\u2500 Shared editor options \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction baseOptions(overrides = {}) {\n return {\n language: _defaultLanguage,\n theme: _themeNames.dark,\n fontFamily: 'Consolas, \"Courier New\", monospace',\n fontSize: 13,\n lineHeight: 19.5,\n tabSize: 4,\n insertSpaces: true,\n automaticLayout: true,\n minimap: { enabled: false },\n scrollBeyondLastLine: false,\n renderLineHighlight: 'gutter',\n matchBrackets: 'always',\n padding: { top: 8, bottom: 8 },\n overviewRulerLanes: 0,\n hideCursorInOverviewRuler: true,\n overviewRulerBorder: false,\n scrollbar: { handleMouseWheel: false },\n folding: true,\n lineDecorationsWidth: 0,\n // Reserve a couple extra gutter columns so the right-aligned line\n // numbers get some breathing room on the left instead of sitting flush\n // against the editor edge (digits stay right-aligned; the slack lands\n // on the left). Covers up to 5-digit files too.\n lineNumbersMinChars: 5,\n suggest: {\n showKeywords: true,\n showFunctions: true,\n showVariables: true,\n showConstants: true,\n showModules: true,\n snippetsPreventQuickSuggestions: false,\n },\n quickSuggestions: { other: true, comments: false, strings: false },\n parameterHints: { enabled: true },\n wordWrap: 'off',\n fixedOverflowWidgets: true,\n ...overrides,\n };\n}\n\nfunction singleLineOptions(overrides = {}) {\n return baseOptions({\n lineNumbers: 'off',\n lineDecorationsWidth: 0,\n lineNumbersMinChars: 0,\n glyphMargin: false,\n folding: false,\n renderLineHighlight: 'none',\n scrollbar: { vertical: 'hidden', horizontal: 'hidden', handleMouseWheel: false },\n wordWrap: 'off',\n overviewRulerLanes: 0,\n padding: { top: 3, bottom: 3 },\n ...overrides,\n });\n}\n\n/** A cap is a positive number of px, or `Infinity` for none. */\nfunction checkMaxHeight(px) {\n if (typeof px !== 'number' || Number.isNaN(px) || px <= 0) {\n throw new RangeError(`maxHeight must be a positive number of px or Infinity, got ${String(px)}`);\n }\n return px;\n}\n\n// \u2500\u2500\u2500 Factory \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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/**\n * @typedef {{ editor: object, dispose: () => void, getValue: () => string, setValue: (v: string) => void, onDidChange: (cb: () => void) => object, setMaxHeight?: (px: number) => void }} EditorHandle\n */\n\nexport class MonacoEditorFactory {\n #monaco = null;\n #currentTheme = _themeNames.dark;\n\n constructor() {}\n\n async init() {\n this.#monaco = await monacoReady;\n ensureThemes(this.#monaco);\n return this;\n }\n\n get monaco() { return this.#monaco; }\n\n /**\n * Set global theme for all editors.\n * @param {string} theme A theme name Monaco knows: one of\n * getEditorThemeNames(), or one the embedder defined itself.\n */\n setTheme(theme) {\n this.#currentTheme = theme;\n this.#monaco?.editor.setTheme(theme);\n }\n\n /**\n * Create a full multi-line editor.\n *\n * Unless `noAutoHeight` is set, the editor sizes `container` to its content,\n * never below `setMinHeight()` (80px by default) and never above `maxHeight`.\n * Past the cap, Monaco scrolls internally and handles the mouse wheel; below\n * it, Monaco leaves the wheel alone, so it reaches whatever scrolls around\n * the editor.\n *\n * @param {HTMLElement} container\n * @param {string} initialValue\n * @param {{ language?: string, readOnly?: boolean, lineNumbers?: string, maxHeight?: number, noAutoHeight?: boolean } & object} [overrides]\n * `maxHeight`: the auto-height cap in px, `Infinity` (the default) for none.\n * `noAutoHeight`: leave the container's size to its layout; the wheel always scrolls the editor.\n * @returns {EditorHandle}\n */\n createEditor(container, initialValue = '', overrides = {}) {\n if (!this.#monaco) throw new Error('MonacoEditorFactory not initialized');\n\n const { noAutoHeight, maxHeight: maxHeightOption = Infinity, ...editorOverrides } = overrides;\n let maxHeight = checkMaxHeight(maxHeightOption);\n\n const editor = this.#monaco.editor.create(container, {\n ...baseOptions({ theme: this.#currentTheme }),\n value: initialValue,\n ...editorOverrides,\n });\n\n // Monaco's handleMouseWheel is toggled with the height: off while the\n // editor fits its content (the wheel scrolls the page around it), on only\n // when the content overflows the cap and the editor has something to scroll.\n let minHeight = 80;\n let heightDisposable = null;\n let updateHeight = null;\n if (noAutoHeight) {\n editor.updateOptions({ scrollbar: { handleMouseWheel: true } });\n } else {\n updateHeight = () => {\n const rawContent = editor.getContentHeight();\n const contentHeight = Math.min(maxHeight, Math.max(minHeight, rawContent));\n container.style.height = `${contentHeight}px`;\n editor.layout();\n editor.updateOptions({ scrollbar: { handleMouseWheel: rawContent > maxHeight } });\n };\n heightDisposable = editor.onDidContentSizeChange(updateHeight);\n updateHeight();\n }\n\n // Scroll the find widget into view when it becomes visible.\n // The editor auto-sizes to content (no internal scroll), so the notebook\n // container scrolls instead. The find widget sits at top:0 of the editor,\n // which may be above the viewport for tall cells.\n let findWidgetObserver = null;\n const observeFindWidget = () => {\n const overflowGuard = container.querySelector('.overflow-guard');\n if (!overflowGuard) return;\n\n findWidgetObserver = new MutationObserver(() => {\n const fw = container.querySelector('.find-widget.visible');\n if (fw) fw.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n });\n findWidgetObserver.observe(overflowGuard, {\n subtree: true,\n attributes: true,\n attributeFilter: ['class'],\n });\n };\n // Defer slightly \u2014 Monaco builds DOM asynchronously after create()\n requestAnimationFrame(observeFindWidget);\n\n return {\n editor,\n getValue: () => editor.getValue(),\n setValue: (v) => { if (editor.getValue() !== v) editor.setValue(v); },\n onDidChange: (cb) => editor.onDidChangeModelContent(cb),\n focus: () => editor.focus(),\n layout: () => editor.layout(),\n setMinHeight: (h) => { minHeight = h; },\n /** The auto-height cap in px, `Infinity` for none; applies at once. No effect with `noAutoHeight`. */\n setMaxHeight: (px) => {\n maxHeight = checkMaxHeight(px);\n updateHeight?.();\n },\n updateHeight,\n dispose: () => {\n findWidgetObserver?.disconnect();\n heightDisposable?.dispose();\n safeDispose(editor);\n },\n };\n }\n\n /**\n * Create a single-line inline editor (for ParameterCell value, expression fields).\n *\n * @param {HTMLElement} container\n * @param {string} initialValue\n * @param {{ language?: string, placeholder?: string } & object} [overrides]\n * @returns {EditorHandle}\n */\n createInlineEditor(container, initialValue = '', overrides = {}) {\n if (!this.#monaco) throw new Error('MonacoEditorFactory not initialized');\n\n container.style.height = '26px';\n\n const editor = this.#monaco.editor.create(container, {\n ...singleLineOptions({ theme: this.#currentTheme }),\n value: initialValue,\n ...overrides,\n });\n\n // Prevent newlines in single-line mode\n editor.onKeyDown((e) => {\n if (e.keyCode === this.#monaco.KeyCode.Enter) {\n e.preventDefault();\n e.stopPropagation();\n }\n });\n\n return {\n editor,\n getValue: () => editor.getValue(),\n setValue: (v) => { if (editor.getValue() !== v) editor.setValue(v); },\n onDidChange: (cb) => editor.onDidChangeModelContent(cb),\n focus: () => editor.focus(),\n layout: () => editor.layout(),\n dispose: () => safeDispose(editor),\n };\n }\n\n /**\n * Create a plain text editor (for documentation cells with markdown).\n * Uses VS Code's built-in markdown language, no EcoLang features.\n *\n * @param {HTMLElement} container\n * @param {string} initialValue\n * @returns {EditorHandle}\n */\n createMarkdownEditor(container, initialValue = '') {\n return this.createEditor(container, initialValue, {\n language: 'markdown',\n wordWrap: 'on',\n lineNumbers: 'off',\n folding: false,\n quickSuggestions: false,\n });\n }\n\n /**\n * Create a read-only DSL viewer (shown in the \"DSL\" tab of a Godley/parameter cell).\n *\n * @param {HTMLElement} container\n * @param {string} dslContent\n * @returns {EditorHandle}\n */\n createDslViewer(container, dslContent = '') {\n return this.createEditor(container, dslContent, {\n readOnly: true,\n lineNumbers: 'off',\n glyphMargin: false,\n folding: false,\n });\n }\n\n /**\n * Create a side-by-side diff editor for comparing two versions of a file.\n *\n * @param {HTMLElement} container\n * @param {string} originalContent - Left side (e.g. HEAD version)\n * @param {string} modifiedContent - Right side (e.g. working copy)\n * @param {{ language?: string, readOnly?: boolean }} [options]\n * @returns {{ editor: object, dispose: Function, setModels: Function }}\n */\n createDiffEditor(container, originalContent = '', modifiedContent = '', options = {}) {\n const monaco = this.#monaco;\n const language = options.language || 'plaintext';\n\n // The pair currently attached to the diff editor. setModels() replaces it, and\n // dispose() frees whichever pair is attached at that moment \u2014 not the first one.\n let models = {\n original: monaco.editor.createModel(originalContent, language),\n modified: monaco.editor.createModel(modifiedContent, language),\n };\n\n const diffEditor = monaco.editor.createDiffEditor(container, {\n theme: this.#currentTheme,\n readOnly: options.readOnly !== false,\n renderSideBySide: true,\n automaticLayout: true,\n minimap: { enabled: false },\n scrollBeyondLastLine: false,\n lineNumbers: 'on',\n glyphMargin: false,\n folding: true,\n renderOverviewRuler: false,\n enableSplitViewResizing: true,\n });\n\n diffEditor.setModel(models);\n\n return {\n editor: diffEditor,\n setModels(original, modified, lang) {\n const previous = models;\n models = {\n original: monaco.editor.createModel(original, lang || language),\n modified: monaco.editor.createModel(modified, lang || language),\n };\n // Attach the new pair first: a model must not be disposed while the\n // diff editor still holds it.\n diffEditor.setModel(models);\n safeDispose(previous.original);\n safeDispose(previous.modified);\n },\n dispose() {\n safeDispose(diffEditor);\n safeDispose(models.original);\n safeDispose(models.modified);\n },\n };\n }\n}\n\n/**\n * The singleton, as a promise: concurrent callers share one load, and a FAILED\n * load clears it so the next call can retry.\n * @type {Promise<MonacoEditorFactory> | null}\n */\nlet _factoryPromise = null;\n\n// Where the app keeps Monaco. Set once, at boot.\nlet _monacoBasePath = null;\n\n/** @param {string} basePath */\nexport function setMonacoBasePath(basePath) {\n if (typeof basePath === 'string' && basePath) _monacoBasePath = basePath;\n}\n\n/**\n * The shared editor factory, once Monaco has loaded.\n *\n * Uses a load already started by initMonaco(path), so a consumer that calls\n * initMonaco() itself needs no setMonacoBasePath(). Otherwise it starts one\n * from setMonacoBasePath()'s path.\n *\n * Rejects \u2014 rather than pending forever (BUG-0023) \u2014 with initMonaco()'s error\n * when no path was set and no load was started, or when Monaco cannot be\n * loaded. A later call asks initMonaco() again: after a loader.js failure that\n * is a fresh attempt (fix the path first); after an AMD-level failure it is\n * the same rejection, because that one is final for the page.\n *\n * @returns {Promise<MonacoEditorFactory>}\n */\nexport function getEditorFactory() {\n if (!_factoryPromise) {\n _factoryPromise = initMonaco(_monacoBasePath)\n .then(() => new MonacoEditorFactory().init())\n .catch((err) => { _factoryPromise = null; throw err; });\n }\n return _factoryPromise;\n}\n", "/**\r\n * NotebookSearchBar \u2014 VS Code-style find/replace bar for notebook-level search.\r\n *\r\n * Searches across all cells using getSearchableText(), highlights matches\r\n * via cell-specific applySearchHighlights(), and supports replace for\r\n * Monaco-based cells (code, documentation).\r\n *\r\n * Mounted at the top of the notebook-editor-container (sticky positioned).\r\n */\r\n\r\n// \u2500\u2500\u2500 Match descriptor \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n/**\r\n * @typedef {{ cellId: string, indexInCell: number, charOffset: number }} SearchMatch\r\n */\r\n\r\n// \u2500\u2500\u2500 Component \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\nexport class NotebookSearchBar {\r\n /** @type {HTMLElement} parent container (.notebook-editor-container) */\r\n #parent = null;\r\n /** @type {HTMLElement} the search bar root element */\r\n #el = null;\r\n /** @type {HTMLInputElement} */\r\n #searchInput = null;\r\n /** @type {HTMLInputElement} */\r\n #replaceInput = null;\r\n /** @type {HTMLElement} match info label */\r\n #matchInfo = null;\r\n\r\n // State\r\n #isOpen = false;\r\n #showReplace = false;\r\n #caseSensitive = false;\r\n #wholeWord = false;\r\n #useRegex = false;\r\n\r\n // Match tracking\r\n /** @type {SearchMatch[]} */\r\n #matches = [];\r\n #currentIndex = -1;\r\n\r\n // Callbacks into NotebookEditor\r\n #getCells = null; // () => [{ id, type }]\r\n #getCellRenderer = null; // (id) => CellBase|null\r\n #getCellElement = null; // (id) => HTMLElement|null\r\n #scrollToCell = null; // (id) => void\r\n\r\n /**\r\n * @param {HTMLElement} parent \u2014 .notebook-editor-container\r\n * @param {{ getCells, getCellRenderer, getCellElement, scrollToCell }} callbacks\r\n */\r\n constructor(parent, { getCells, getCellRenderer, getCellElement, scrollToCell }) {\r\n this.#parent = parent;\r\n this.#getCells = getCells;\r\n this.#getCellRenderer = getCellRenderer;\r\n this.#getCellElement = getCellElement;\r\n this.#scrollToCell = scrollToCell;\r\n\r\n this.#render();\r\n }\r\n\r\n get isOpen() { return this.#isOpen; }\r\n\r\n // \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n open(showReplace = false) {\r\n this.#isOpen = true;\r\n this.#showReplace = showReplace;\r\n this.#el.classList.add('nb-search-bar--open');\r\n this.#el.querySelector('.nb-search-bar__replace-row').style.display =\r\n showReplace ? '' : 'none';\r\n this.#updateToggleIcon();\r\n\r\n // Pre-fill with current selection text (if any)\r\n const sel = window.getSelection()?.toString()?.trim();\r\n if (sel && sel.length < 200 && !sel.includes('\\n')) {\r\n this.#searchInput.value = sel;\r\n }\r\n\r\n this.#searchInput.focus();\r\n this.#searchInput.select();\r\n\r\n if (this.#searchInput.value) this.#runSearch();\r\n }\r\n\r\n close() {\r\n this.#isOpen = false;\r\n this.#el.classList.remove('nb-search-bar--open');\r\n this.#clearAllHighlights();\r\n this.#matches = [];\r\n this.#currentIndex = -1;\r\n this.#updateMatchInfo();\r\n }\r\n\r\n dispose() {\r\n this.#clearAllHighlights();\r\n this.#el?.remove();\r\n this.#el = null;\r\n }\r\n\r\n // \u2500\u2500\u2500 Rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #render() {\r\n const el = document.createElement('div');\r\n el.className = 'nb-search-bar';\r\n el.innerHTML = `\r\n <div class=\"nb-search-bar__find-row\">\r\n <button class=\"nb-search-bar__btn nb-search-bar__toggle-replace\" title=\"Toggle Replace\">\r\n <span class=\"material-symbols-outlined\">chevron_right</span>\r\n </button>\r\n <div class=\"nb-search-bar__input-wrap\">\r\n <input class=\"nb-search-bar__input nb-search-bar__find-input\"\r\n type=\"text\" placeholder=\"Find\" spellcheck=\"false\" />\r\n </div>\r\n <span class=\"nb-search-bar__match-info\">No results</span>\r\n <button class=\"nb-search-bar__btn\" data-action=\"prev\" title=\"Previous Match (Shift+Enter)\">\r\n <span class=\"material-symbols-outlined\">arrow_upward</span>\r\n </button>\r\n <button class=\"nb-search-bar__btn\" data-action=\"next\" title=\"Next Match (Enter)\">\r\n <span class=\"material-symbols-outlined\">arrow_downward</span>\r\n </button>\r\n <div class=\"nb-search-bar__separator\"></div>\r\n <button class=\"nb-search-bar__btn nb-search-bar__btn--toggle\" data-action=\"case\"\r\n title=\"Match Case (Alt+C)\">Aa</button>\r\n <button class=\"nb-search-bar__btn nb-search-bar__btn--toggle\" data-action=\"word\"\r\n title=\"Match Whole Word (Alt+W)\">\r\n <span class=\"nb-search-bar__icon-word\">ab</span>\r\n </button>\r\n <button class=\"nb-search-bar__btn nb-search-bar__btn--toggle\" data-action=\"regex\"\r\n title=\"Use Regular Expression (Alt+R)\">.*</button>\r\n <div class=\"nb-search-bar__separator\"></div>\r\n <button class=\"nb-search-bar__btn\" data-action=\"close\" title=\"Close (Escape)\">\r\n <span class=\"material-symbols-outlined\">close</span>\r\n </button>\r\n </div>\r\n <div class=\"nb-search-bar__replace-row\" style=\"display:none\">\r\n <div class=\"nb-search-bar__spacer\"></div>\r\n <div class=\"nb-search-bar__input-wrap\">\r\n <input class=\"nb-search-bar__input nb-search-bar__replace-input\"\r\n type=\"text\" placeholder=\"Replace\" spellcheck=\"false\" />\r\n </div>\r\n <button class=\"nb-search-bar__btn\" data-action=\"replace\" title=\"Replace (Ctrl+Shift+1)\">\r\n <span class=\"material-symbols-outlined\">find_replace</span>\r\n </button>\r\n <button class=\"nb-search-bar__btn\" data-action=\"replace-all\" title=\"Replace All (Ctrl+Alt+Enter)\">\r\n <span class=\"material-symbols-outlined\">swap_horiz</span>\r\n </button>\r\n </div>\r\n `;\r\n\r\n this.#el = el;\r\n this.#searchInput = el.querySelector('.nb-search-bar__find-input');\r\n this.#replaceInput = el.querySelector('.nb-search-bar__replace-input');\r\n this.#matchInfo = el.querySelector('.nb-search-bar__match-info');\r\n\r\n this.#bindEvents();\r\n this.#parent.prepend(el);\r\n }\r\n\r\n #bindEvents() {\r\n // Search input\r\n this.#searchInput.addEventListener('input', () => this.#runSearch());\r\n\r\n this.#searchInput.addEventListener('keydown', (e) => {\r\n if (e.key === 'Enter' && !e.shiftKey) {\r\n e.preventDefault();\r\n this.#navigateNext();\r\n } else if (e.key === 'Enter' && e.shiftKey) {\r\n e.preventDefault();\r\n this.#navigatePrev();\r\n } else if (e.key === 'Escape') {\r\n e.preventDefault();\r\n this.close();\r\n }\r\n });\r\n\r\n // Replace input\r\n this.#replaceInput.addEventListener('keydown', (e) => {\r\n if (e.key === 'Enter' && !e.shiftKey) {\r\n e.preventDefault();\r\n this.#doReplace();\r\n } else if (e.key === 'Escape') {\r\n e.preventDefault();\r\n this.close();\r\n }\r\n });\r\n\r\n // Buttons\r\n this.#el.addEventListener('click', (e) => {\r\n const btn = e.target.closest('[data-action]');\r\n if (!btn) return;\r\n switch (btn.dataset.action) {\r\n case 'prev': this.#navigatePrev(); break;\r\n case 'next': this.#navigateNext(); break;\r\n case 'close': this.close(); break;\r\n case 'replace': this.#doReplace(); break;\r\n case 'replace-all': this.#doReplaceAll(); break;\r\n case 'case':\r\n this.#caseSensitive = !this.#caseSensitive;\r\n btn.classList.toggle('nb-search-bar__btn--active', this.#caseSensitive);\r\n this.#runSearch();\r\n break;\r\n case 'word':\r\n this.#wholeWord = !this.#wholeWord;\r\n btn.classList.toggle('nb-search-bar__btn--active', this.#wholeWord);\r\n this.#runSearch();\r\n break;\r\n case 'regex':\r\n this.#useRegex = !this.#useRegex;\r\n btn.classList.toggle('nb-search-bar__btn--active', this.#useRegex);\r\n this.#runSearch();\r\n break;\r\n }\r\n });\r\n\r\n // Toggle replace row\r\n this.#el.querySelector('.nb-search-bar__toggle-replace').addEventListener('click', () => {\r\n this.#showReplace = !this.#showReplace;\r\n this.#el.querySelector('.nb-search-bar__replace-row').style.display =\r\n this.#showReplace ? '' : 'none';\r\n this.#updateToggleIcon();\r\n });\r\n\r\n // Alt shortcuts for toggles\r\n this.#el.addEventListener('keydown', (e) => {\r\n if (e.altKey && e.key === 'c') {\r\n e.preventDefault();\r\n this.#caseSensitive = !this.#caseSensitive;\r\n this.#el.querySelector('[data-action=\"case\"]').classList.toggle('nb-search-bar__btn--active', this.#caseSensitive);\r\n this.#runSearch();\r\n } else if (e.altKey && e.key === 'w') {\r\n e.preventDefault();\r\n this.#wholeWord = !this.#wholeWord;\r\n this.#el.querySelector('[data-action=\"word\"]').classList.toggle('nb-search-bar__btn--active', this.#wholeWord);\r\n this.#runSearch();\r\n } else if (e.altKey && e.key === 'r') {\r\n e.preventDefault();\r\n this.#useRegex = !this.#useRegex;\r\n this.#el.querySelector('[data-action=\"regex\"]').classList.toggle('nb-search-bar__btn--active', this.#useRegex);\r\n this.#runSearch();\r\n }\r\n });\r\n\r\n // Prevent keydown from bubbling to NotebookEditor (avoids undo/redo interference)\r\n // Also suppress browser Ctrl+F/Ctrl+H when the search bar already has focus\r\n this.#el.addEventListener('keydown', (e) => {\r\n e.stopPropagation();\r\n if (e.ctrlKey && (e.key === 'f' || e.key === 'h')) {\r\n e.preventDefault();\r\n if (e.key === 'h' && !this.#showReplace) {\r\n this.#showReplace = true;\r\n this.#el.querySelector('.nb-search-bar__replace-row').style.display = '';\r\n this.#updateToggleIcon();\r\n }\r\n }\r\n });\r\n }\r\n\r\n #updateToggleIcon() {\r\n const icon = this.#el.querySelector('.nb-search-bar__toggle-replace .material-symbols-outlined');\r\n if (icon) icon.textContent = this.#showReplace ? 'expand_more' : 'chevron_right';\r\n }\r\n\r\n #updateMatchInfo() {\r\n if (!this.#matchInfo) return;\r\n if (this.#matches.length === 0) {\r\n const hasQuery = this.#searchInput?.value?.length > 0;\r\n this.#matchInfo.textContent = hasQuery ? 'No results' : '';\r\n this.#matchInfo.classList.toggle('nb-search-bar__match-info--no-results', hasQuery);\r\n } else {\r\n this.#matchInfo.textContent = `${this.#currentIndex + 1} of ${this.#matches.length}`;\r\n this.#matchInfo.classList.remove('nb-search-bar__match-info--no-results');\r\n }\r\n }\r\n\r\n // \u2500\u2500\u2500 Search logic \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #buildQuery() {\r\n const raw = this.#searchInput.value;\r\n if (!raw) return null;\r\n\r\n let pattern;\r\n if (this.#useRegex) {\r\n try { pattern = raw; new RegExp(pattern); }\r\n catch { return null; } // invalid regex\r\n } else {\r\n pattern = raw.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n }\r\n\r\n if (this.#wholeWord) pattern = `\\\\b${pattern}\\\\b`;\r\n\r\n const flags = 'g' + (this.#caseSensitive ? '' : 'i');\r\n try { return new RegExp(pattern, flags); }\r\n catch { return null; }\r\n }\r\n\r\n #runSearch() {\r\n this.#clearAllHighlights();\r\n this.#matches = [];\r\n this.#currentIndex = -1;\r\n\r\n const query = this.#buildQuery();\r\n if (!query) {\r\n this.#updateMatchInfo();\r\n return;\r\n }\r\n\r\n const cells = this.#getCells();\r\n\r\n for (const { id } of cells) {\r\n const renderer = this.#getCellRenderer(id);\r\n if (!renderer) continue;\r\n\r\n const text = renderer.getSearchableText();\r\n if (!text) continue;\r\n\r\n query.lastIndex = 0;\r\n let m;\r\n let indexInCell = 0;\r\n while ((m = query.exec(text)) !== null) {\r\n this.#matches.push({ cellId: id, indexInCell, charOffset: m.index });\r\n indexInCell++;\r\n if (!query.global) break;\r\n }\r\n }\r\n\r\n if (this.#matches.length > 0) {\r\n this.#currentIndex = 0;\r\n this.#applyHighlights();\r\n }\r\n\r\n this.#updateMatchInfo();\r\n }\r\n\r\n // \u2500\u2500\u2500 Navigation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #navigateNext() {\r\n if (this.#matches.length === 0) return;\r\n this.#currentIndex = (this.#currentIndex + 1) % this.#matches.length;\r\n this.#applyHighlights();\r\n this.#scrollToCurrentMatch();\r\n this.#updateMatchInfo();\r\n }\r\n\r\n #navigatePrev() {\r\n if (this.#matches.length === 0) return;\r\n this.#currentIndex = (this.#currentIndex - 1 + this.#matches.length) % this.#matches.length;\r\n this.#applyHighlights();\r\n this.#scrollToCurrentMatch();\r\n this.#updateMatchInfo();\r\n }\r\n\r\n #scrollToCurrentMatch() {\r\n const match = this.#matches[this.#currentIndex];\r\n if (!match) return;\r\n this.#scrollToCell(match.cellId);\r\n\r\n // Also scroll the specific highlight into view if possible\r\n requestAnimationFrame(() => {\r\n const cellEl = this.#getCellElement(match.cellId);\r\n if (!cellEl) return;\r\n const active = cellEl.querySelector('.nb-search-match--active');\r\n if (active) active.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\r\n });\r\n }\r\n\r\n // \u2500\u2500\u2500 Highlighting \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #applyHighlights() {\r\n const query = this.#buildQuery();\r\n if (!query) return;\r\n\r\n // Group matches by cell\r\n const matchesByCell = new Map();\r\n for (const m of this.#matches) {\r\n if (!matchesByCell.has(m.cellId)) matchesByCell.set(m.cellId, []);\r\n matchesByCell.get(m.cellId).push(m);\r\n }\r\n\r\n const currentMatch = this.#matches[this.#currentIndex];\r\n\r\n // Apply highlights to each cell that has matches\r\n const cells = this.#getCells();\r\n for (const { id } of cells) {\r\n const renderer = this.#getCellRenderer(id);\r\n if (!renderer) continue;\r\n\r\n const cellEl = this.#getCellElement(id);\r\n\r\n if (matchesByCell.has(id)) {\r\n const activeOffset = (currentMatch?.cellId === id)\r\n ? currentMatch.charOffset\r\n : -1;\r\n renderer.applySearchHighlights(query, activeOffset);\r\n cellEl?.classList.add('notebook-cell--search-match');\r\n cellEl?.classList.toggle('notebook-cell--search-active', currentMatch?.cellId === id);\r\n } else {\r\n renderer.clearSearchHighlights();\r\n cellEl?.classList.remove('notebook-cell--search-match', 'notebook-cell--search-active');\r\n }\r\n }\r\n }\r\n\r\n #clearAllHighlights() {\r\n const cells = this.#getCells();\r\n for (const { id } of cells) {\r\n const renderer = this.#getCellRenderer(id);\r\n renderer?.clearSearchHighlights();\r\n const cellEl = this.#getCellElement(id);\r\n cellEl?.classList.remove('notebook-cell--search-match', 'notebook-cell--search-active');\r\n }\r\n }\r\n\r\n // \u2500\u2500\u2500 Replace \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #doReplace() {\r\n if (this.#matches.length === 0 || this.#currentIndex < 0) return;\r\n\r\n const match = this.#matches[this.#currentIndex];\r\n const renderer = this.#getCellRenderer(match.cellId);\r\n if (!renderer) return;\r\n\r\n const query = this.#buildQuery();\r\n if (!query) return;\r\n\r\n const replacement = this.#replaceInput.value;\r\n const success = renderer.replaceMatch(query, match.indexInCell, replacement);\r\n\r\n if (success) {\r\n // Re-run search (match count changed), try to stay near same position\r\n const wasIndex = this.#currentIndex;\r\n this.#runSearch();\r\n if (this.#matches.length > 0) {\r\n this.#currentIndex = Math.min(wasIndex, this.#matches.length - 1);\r\n this.#applyHighlights();\r\n this.#scrollToCurrentMatch();\r\n this.#updateMatchInfo();\r\n }\r\n }\r\n }\r\n\r\n #doReplaceAll() {\r\n const query = this.#buildQuery();\r\n if (!query || this.#matches.length === 0) return;\r\n\r\n const replacement = this.#replaceInput.value;\r\n\r\n // Collect unique cell IDs that have matches\r\n const cellIds = [...new Set(this.#matches.map(m => m.cellId))];\r\n let totalReplaced = 0;\r\n\r\n for (const cellId of cellIds) {\r\n const renderer = this.#getCellRenderer(cellId);\r\n if (renderer) totalReplaced += renderer.replaceAll(query, replacement);\r\n }\r\n\r\n if (totalReplaced > 0) this.#runSearch();\r\n }\r\n}\r\n", "/**\r\n * NotebookUndoManager\r\n *\r\n * Command-pattern undo/redo for notebook cell operations.\r\n *\r\n * Each command: { description: string, undo: () => (void | Promise), redo: () => (void | Promise) }\r\n *\r\n * Operations tracked by NotebookEditor:\r\n * - Cell add\r\n * - Cell delete\r\n * - Cell move (up/down, drag-reorder)\r\n * - Cell config change (debounced per-cell snapshot)\r\n */\r\nexport class NotebookUndoManager {\r\n\r\n /** @type {Array<{ description: string, undo: Function, redo: Function }>} */\r\n #undoStack = [];\r\n\r\n /** @type {Array<{ description: string, undo: Function, redo: Function }>} */\r\n #redoStack = [];\r\n\r\n /** @type {number} */\r\n #maxSize;\r\n\r\n /** @type {Function|null} Called with (canUndo: boolean, canRedo: boolean) after each change */\r\n #onStateChange;\r\n\r\n /** @type {boolean} Prevents re-entrant undo/redo */\r\n #applying = false;\r\n\r\n /**\r\n * @param {{ maxSize?: number, onStateChange?: Function }} opts\r\n */\r\n constructor({ maxSize = 100, onStateChange = null } = {}) {\r\n this.#maxSize = maxSize;\r\n this.#onStateChange = onStateChange;\r\n }\r\n\r\n // \u2500\u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n get canUndo() { return this.#undoStack.length > 0; }\r\n get canRedo() { return this.#redoStack.length > 0; }\r\n\r\n /**\r\n * Record a new command. Clears the redo stack.\r\n * Should NOT be called from inside undo/redo callbacks.\r\n *\r\n * @param {{ description: string, undo: Function, redo: Function }} command\r\n */\r\n push(command) {\r\n if (this.#applying) return; // ignore changes triggered by undo/redo\r\n this.#undoStack.push(command);\r\n if (this.#undoStack.length > this.#maxSize) {\r\n this.#undoStack.shift();\r\n }\r\n this.#redoStack = [];\r\n this.#notify();\r\n }\r\n\r\n /**\r\n * Undo the last command.\r\n * @returns {Promise<boolean>} true if something was undone\r\n */\r\n async undo() {\r\n const cmd = this.#undoStack.pop();\r\n if (!cmd) return false;\r\n this.#applying = true;\r\n try {\r\n await cmd.undo();\r\n } finally {\r\n this.#applying = false;\r\n }\r\n this.#redoStack.push(cmd);\r\n this.#notify();\r\n return true;\r\n }\r\n\r\n /**\r\n * Redo the last undone command.\r\n * @returns {Promise<boolean>} true if something was redone\r\n */\r\n async redo() {\r\n const cmd = this.#redoStack.pop();\r\n if (!cmd) return false;\r\n this.#applying = true;\r\n try {\r\n await cmd.redo();\r\n } finally {\r\n this.#applying = false;\r\n }\r\n this.#undoStack.push(cmd);\r\n this.#notify();\r\n return true;\r\n }\r\n\r\n /** Reset both stacks (e.g., after file load). */\r\n clear() {\r\n this.#undoStack = [];\r\n this.#redoStack = [];\r\n this.#notify();\r\n }\r\n\r\n // \u2500\u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #notify() {\r\n this.#onStateChange?.(this.canUndo, this.canRedo);\r\n }\r\n}\r\n", "/**\r\n * SplitPaneContainer \u2014 horizontal split with a draggable divider.\r\n *\r\n * Manages exactly 1 or 2 child panes. When only one pane exists, the divider\r\n * is hidden and the single pane fills the container. When split, both panes\r\n * share space according to a ratio (default 50/50), adjustable via drag.\r\n *\r\n * Usage:\r\n * const split = new SplitPaneContainer();\r\n * split.mount(parentEl);\r\n * split.setPanes(leftEl); // single pane\r\n * split.setPanes(leftEl, rightEl); // split view\r\n * split.setRatio(0.6); // 60/40 split\r\n * split.unsplit(); // collapse to single pane\r\n */\r\n\r\nexport class SplitPaneContainer {\r\n /** @type {HTMLElement} */\r\n #container = null;\r\n\r\n /** @type {HTMLElement} */\r\n #pane1 = null;\r\n\r\n /** @type {HTMLElement} */\r\n #pane2 = null;\r\n\r\n /** @type {HTMLElement} */\r\n #divider = null;\r\n\r\n /** @type {number} 0..1, fraction of width for pane 1 */\r\n #ratio = 0.5;\r\n\r\n /** @type {boolean} */\r\n #isSplit = false;\r\n\r\n /** @type {Function|null} */\r\n #onRatioChanged = null;\r\n\r\n // Drag state\r\n #dragging = false;\r\n #boundOnMouseMove = null;\r\n #boundOnMouseUp = null;\r\n\r\n /**\r\n * @param {{ onRatioChanged?: (ratio: number) => void }} opts\r\n */\r\n constructor(opts = {}) {\r\n this.#onRatioChanged = opts.onRatioChanged ?? null;\r\n }\r\n\r\n get isSplit() { return this.#isSplit; }\r\n get ratio() { return this.#ratio; }\r\n\r\n mount(container) {\r\n this.#container = container;\r\n this.#container.classList.add('split-pane-container');\r\n this.#container.innerHTML = '';\r\n\r\n // Divider\r\n this.#divider = document.createElement('div');\r\n this.#divider.className = 'split-pane-divider';\r\n this.#divider.addEventListener('mousedown', (e) => this.#onDividerMouseDown(e));\r\n\r\n this.#applyLayout();\r\n }\r\n\r\n /**\r\n * Set pane elements. Pass one element for single view, two for split.\r\n * @param {HTMLElement} pane1\r\n * @param {HTMLElement} [pane2]\r\n */\r\n setPanes(pane1, pane2) {\r\n this.#pane1 = pane1;\r\n this.#pane2 = pane2 ?? null;\r\n this.#isSplit = !!pane2;\r\n this.#applyLayout();\r\n }\r\n\r\n /**\r\n * Set the split ratio (fraction for left pane).\r\n * @param {number} ratio \u2014 0.2..0.8\r\n */\r\n setRatio(ratio) {\r\n this.#ratio = Math.max(0.2, Math.min(0.8, ratio));\r\n if (this.#isSplit) this.#applySizes();\r\n }\r\n\r\n /**\r\n * Collapse to single pane (left). Returns the removed right pane element.\r\n * @returns {HTMLElement|null}\r\n */\r\n unsplit() {\r\n if (!this.#isSplit) return null;\r\n const removed = this.#pane2;\r\n this.#pane2 = null;\r\n this.#isSplit = false;\r\n this.#applyLayout();\r\n return removed;\r\n }\r\n\r\n dispose() {\r\n this.#stopDrag();\r\n this.#container = null;\r\n this.#pane1 = null;\r\n this.#pane2 = null;\r\n this.#divider = null;\r\n }\r\n\r\n // \u2500\u2500\u2500 Layout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #applyLayout() {\r\n if (!this.#container) return;\r\n this.#container.innerHTML = '';\r\n\r\n if (!this.#pane1) return;\r\n\r\n // ADD the class, never assign it: a pane is the caller's element\r\n // (an EditorPane root is `.editor-pane`), and its own classes are\r\n // what its stylesheet reaches it by.\r\n if (!this.#isSplit) {\r\n // Single pane: fills container\r\n this.#pane1.classList.add('split-pane');\r\n this.#pane1.style.flex = '1 1 0';\r\n this.#pane1.style.minWidth = '0';\r\n this.#container.appendChild(this.#pane1);\r\n this.#container.classList.remove('split-pane-container--split');\r\n } else {\r\n // Split view\r\n this.#pane1.classList.add('split-pane');\r\n this.#pane2.classList.add('split-pane');\r\n this.#pane1.style.minWidth = '0';\r\n this.#pane2.style.minWidth = '0';\r\n\r\n this.#container.appendChild(this.#pane1);\r\n this.#container.appendChild(this.#divider);\r\n this.#container.appendChild(this.#pane2);\r\n this.#container.classList.add('split-pane-container--split');\r\n\r\n this.#applySizes();\r\n }\r\n }\r\n\r\n #applySizes() {\r\n if (!this.#pane1 || !this.#pane2) return;\r\n // Use flex-grow proportional to ratio\r\n const r = this.#ratio;\r\n this.#pane1.style.flex = `${r} 1 0`;\r\n this.#pane2.style.flex = `${1 - r} 1 0`;\r\n }\r\n\r\n // \u2500\u2500\u2500 Divider drag \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n\r\n #onDividerMouseDown(e) {\r\n if (e.button !== 0) return;\r\n e.preventDefault();\r\n this.#dragging = true;\r\n this.#divider.classList.add('split-pane-divider--active');\r\n document.body.style.cursor = 'col-resize';\r\n\r\n // Prevent text selection and iframes capturing mouse\r\n document.body.style.userSelect = 'none';\r\n this.#container.querySelectorAll('iframe').forEach(f => f.style.pointerEvents = 'none');\r\n\r\n this.#boundOnMouseMove = (e) => this.#onMouseMove(e);\r\n this.#boundOnMouseUp = () => this.#onMouseUp();\r\n document.addEventListener('mousemove', this.#boundOnMouseMove);\r\n document.addEventListener('mouseup', this.#boundOnMouseUp);\r\n }\r\n\r\n #onMouseMove(e) {\r\n if (!this.#dragging || !this.#container) return;\r\n const rect = this.#container.getBoundingClientRect();\r\n const dividerWidth = this.#divider.offsetWidth;\r\n const usable = rect.width - dividerWidth;\r\n if (usable <= 0) return;\r\n\r\n const x = e.clientX - rect.left - dividerWidth / 2;\r\n const ratio = Math.max(0.2, Math.min(0.8, x / usable));\r\n this.#ratio = ratio;\r\n this.#applySizes();\r\n }\r\n\r\n #onMouseUp() {\r\n this.#stopDrag();\r\n this.#onRatioChanged?.(this.#ratio);\r\n }\r\n\r\n #stopDrag() {\r\n if (!this.#dragging) return;\r\n this.#dragging = false;\r\n this.#divider?.classList.remove('split-pane-divider--active');\r\n document.body.style.cursor = '';\r\n document.body.style.userSelect = '';\r\n this.#container?.querySelectorAll('iframe').forEach(f => f.style.pointerEvents = '');\r\n document.removeEventListener('mousemove', this.#boundOnMouseMove);\r\n document.removeEventListener('mouseup', this.#boundOnMouseUp);\r\n this.#boundOnMouseMove = null;\r\n this.#boundOnMouseUp = null;\r\n }\r\n}\r\n"],
5
+ "mappings": ";;;;;;;;;;;AAyBO,IAAM,aAAN,MAAiB;AAAA;AAAA,EAEpB;AAAA;AAAA,EAGA,MAAM;AAAA;AAAA,EAGN,UAAU;AAAA;AAAA,EAGV,mBAAmB;AAAA;AAAA,EAGnB,aAAa,CAAC;AAAA;AAAA,EAGd,kBAAkB;AAAA;AAAA,EAGlB,mBAAmB;AAAA;AAAA,EAGnB,aAAa,CAAC;AAAA;AAAA,EAGd,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBX,YAAY,IAAI,YAAY,CAAC,GAAG;AAC5B,SAAK,MAAM;AACX,SAAK,aAAa;AAAA,EACtB;AAAA,EAEA,IAAI,KAAK;AAAE,WAAO,KAAK;AAAA,EAAK;AAAA,EAC5B,IAAI,iBAAiB;AAAE,WAAO,KAAK;AAAA,EAAiB;AAAA,EACpD,IAAI,YAAY;AAAE,WAAO,CAAC,GAAG,KAAK,UAAU;AAAA,EAAG;AAAA,EAC/C,IAAI,kBAAkB;AAAE,WAAO,KAAK;AAAA,EAAkB;AAAA,EACtD,IAAI,UAAU;AAAE,WAAO,KAAK;AAAA,EAAK;AAAA,EACjC,IAAI,UAAU;AAAE,WAAO,KAAK,WAAW,WAAW;AAAA,EAAG;AAAA;AAAA;AAAA;AAAA;AAAA,EAMrD,QAAQ;AACJ,SAAK,MAAM,SAAS,cAAc,KAAK;AACvC,SAAK,IAAI,YAAY;AACrB,SAAK,IAAI,QAAQ,SAAS,KAAK;AAG/B,SAAK,IAAI,iBAAiB,aAAa,MAAM;AACzC,WAAK,WAAW,UAAU,KAAK,GAAG;AAAA,IACtC,GAAG,IAAI;AAGP,UAAM,WAAW,SAAS,cAAc,KAAK;AAC7C,SAAK,IAAI,YAAY,QAAQ;AAE7B,SAAK,UAAU,IAAI,eAAe;AAClC,SAAK,QAAQ,MAAM,UAAU;AAAA,MACzB,YAAmB,CAAC,SAAS,KAAK,WAAW,aAAa,KAAK,KAAK,IAAI;AAAA,MACxE,SAAmB,CAAC,SAAS,KAAK,WAAW,UAAU,KAAK,KAAK,IAAI;AAAA,MACrE,eAAmB,CAAC,SAAS,KAAK,WAAW,gBAAgB,KAAK,KAAK,IAAI;AAAA,MAC3E,gBAAmB,CAAC,SAAS,KAAK,WAAW,iBAAiB,KAAK,KAAK,IAAI;AAAA,MAC5E,YAAmB,MAAU,KAAK,WAAW,aAAa,KAAK,GAAG;AAAA,MAClE,cAAmB,MAAU,KAAK,WAAW,eAAe,KAAK,GAAG;AAAA,MACpE,UAAmB,CAAC,GAAG,MAAM,KAAK,WAAW,WAAW,GAAG,CAAC;AAAA,MAC5D,aAAmB,CAAC,SAAS,KAAK,WAAW,cAAc,IAAI;AAAA,MAC/D,WAAmB,CAAC,UAAU,KAAK,WAAW,YAAY,KAAK,KAAK,KAAK;AAAA,MACzE,oBAAmB,CAAC,SAAS,KAAK,WAAW,qBAAqB,IAAI;AAAA,MACtE,cAAmB,CAAC,SAAS,KAAK,WAAW,eAAe,KAAK,KAAK,IAAI;AAAA,MAC1E,mBAAmB,CAAC,SAAS,KAAK,WAAW,oBAAoB,KAAK,KAAK,IAAI;AAAA,MAC/E,qBAAqB,CAAC,SAAS,KAAK,WAAW,sBAAsB,KAAK,KAAK,IAAI;AAAA,IACvF,CAAC;AAOD,UAAM,OAAO,SAAS,cAAc,KAAK;AACzC,SAAK,YAAY;AACjB,SAAK,IAAI,YAAY,IAAI;AAGzB,SAAK,mBAAmB,SAAS,cAAc,KAAK;AACpD,SAAK,iBAAiB,YAAY;AAClC,SAAK,YAAY,KAAK,gBAAgB;AAGtC,SAAK,iBAAiB,iBAAiB,YAAY,CAAC,MAAM;AACtD,UAAI,KAAK,mBAAmB,CAAC,GAAG;AAC5B,UAAE,eAAe;AACjB,YAAI;AAAE,YAAE,aAAa,aAAa;AAAA,QAAQ,QAAQ;AAAA,QAAC;AACnD,aAAK,iBAAiB,UAAU,IAAI,wCAAwC;AAAA,MAChF;AAAA,IACJ,CAAC;AACD,SAAK,iBAAiB,iBAAiB,QAAQ,CAAC,MAAM;AAClD,UAAI,KAAK,mBAAmB,CAAC,GAAG;AAC5B,UAAE,eAAe;AACjB,aAAK,iBAAiB,UAAU,OAAO,wCAAwC;AAC/E,cAAM,WAAW,EAAE,aAAa,QAAQ,eAAe,CAAC,KACjD,EAAE,aAAa,QAAQ,YAAY;AAC1C,YAAI,UAAU;AACV,eAAK,WAAW,sBAAsB,KAAK,KAAK,QAAQ;AAAA,QAC5D;AAAA,MACJ;AAAA,IACJ,CAAC;AACD,SAAK,iBAAiB,iBAAiB,aAAa,CAAC,MAAM;AACvD,UAAI,CAAC,KAAK,iBAAiB,SAAS,EAAE,aAAa,GAAG;AAClD,aAAK,iBAAiB,UAAU,OAAO,wCAAwC;AAAA,MACnF;AAAA,IACJ,CAAC;AAED,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAW,MAAM,gBAAgB;AAC7B,SAAK,SAAS,OAAO,MAAM,cAAc;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,WAAW;AACpB,SAAK,aAAa,CAAC,GAAG,SAAS;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ,UAAU;AACd,QAAI,CAAC,KAAK,WAAW,SAAS,QAAQ,GAAG;AACrC,WAAK,WAAW,KAAK,QAAQ;AAAA,IACjC;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,WAAW,UAAU;AACjB,SAAK,aAAa,KAAK,WAAW,OAAO,OAAK,MAAM,QAAQ;AAC5D,QAAI,KAAK,qBAAqB,SAAU,MAAK,mBAAmB;AAChE,QAAI,KAAK,oBAAoB,UAAU;AACnC,WAAK,kBAAkB,KAAK,WAAW,SAAS,IAC1C,KAAK,WAAW,KAAK,WAAW,SAAS,CAAC,IAC1C;AAAA,IACV;AAAA,EACJ;AAAA,EAEA,QAAQ,UAAU;AACd,WAAO,KAAK,WAAW,SAAS,QAAQ;AAAA,EAC5C;AAAA,EAEA,cAAc,UAAU;AACpB,SAAK,kBAAkB;AAAA,EAC3B;AAAA,EAEA,WAAW,SAAS;AAChB,SAAK,WAAW;AAChB,SAAK,KAAK,UAAU,OAAO,wBAAwB,OAAO;AAAA,EAC9D;AAAA,EAEA,UAAU,UAAU,SAAS;AACzB,SAAK,SAAS,UAAU,UAAU,OAAO;AAAA,EAC7C;AAAA,EAEA,aAAa,cAAc;AAEvB,UAAM,QAAQ,aAAa,OAAO,OAAK,KAAK,WAAW,SAAS,CAAC,CAAC;AAElE,eAAW,KAAK,KAAK,YAAY;AAC7B,UAAI,CAAC,MAAM,SAAS,CAAC,EAAG,OAAM,KAAK,CAAC;AAAA,IACxC;AACA,SAAK,aAAa;AAAA,EACtB;AAAA,EAEA,WAAW,SAAS,SAAS;AACzB,UAAM,MAAM,KAAK,WAAW,QAAQ,OAAO;AAC3C,QAAI,QAAQ,GAAI,MAAK,WAAW,GAAG,IAAI;AACvC,QAAI,KAAK,oBAAoB,QAAS,MAAK,kBAAkB;AAC7D,QAAI,KAAK,qBAAqB,QAAS,MAAK,mBAAmB;AAAA,EACnE;AAAA;AAAA;AAAA,EAKA,IAAI,kBAAkB;AAAE,WAAO,KAAK;AAAA,EAAkB;AAAA;AAAA,EAGtD,WAAW,UAAU;AAAE,SAAK,mBAAmB;AAAA,EAAU;AAAA;AAAA,EAGzD,UAAU,UAAU;AAAE,WAAO,KAAK,qBAAqB;AAAA,EAAU;AAAA;AAAA,EAGjE,aAAa;AAAE,SAAK,mBAAmB;AAAA,EAAM;AAAA;AAAA,EAG7C,mBAAmB,GAAG;AAClB,QAAI;AAAE,aAAO,EAAE,aAAa,MAAM,SAAS,eAAe,CAAC;AAAA,IAAG,QAAQ;AAAE,aAAO;AAAA,IAAO;AAAA,EAC1F;AAAA,EAEA,UAAU;AACN,SAAK,SAAS,QAAQ;AACtB,SAAK,UAAU;AACf,SAAK,KAAK,OAAO;AACjB,SAAK,MAAM;AACX,SAAK,mBAAmB;AACxB,SAAK,aAAa,CAAC;AACnB,SAAK,kBAAkB;AACvB,SAAK,mBAAmB;AAAA,EAC5B;AACJ;;;AC9PA,IAAI;AAmBJ,IAAI,WAAW;AAUR,IAAM,cAAc,IAAI,QAAQ,aAAW;AAAE,mBAAiB;AAAS,CAAC;AAkB/E,IAAI,8BAA8B;AAElC,SAAS,qBAAqB,QAAQ;AAClC,SAAO,QAAQ,SAAS,cAAc,QAAQ,YAAY;AAC9D;AAEA,SAAS,2BAA2B;AAChC,MAAI,4BAA6B;AACjC,gCAA8B;AAG9B,SAAO,mBAAmB,sBAAsB,CAAC,UAAU;AACvD,QAAI,qBAAqB,MAAM,MAAM,EAAG,OAAM,eAAe;AAAA,EACjE,CAAC;AACL;AAEA,SAAS,mBAAmB,KAAK;AAC7B,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACpC,UAAM,IAAI,SAAS,cAAc,QAAQ;AACzC,MAAE,MAAM;AACR,MAAE,SAAS;AACX,MAAE,UAAU,MAAM;AAGd,QAAE,SAAS;AACX,aAAO,IAAI,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAAA,IAC5D;AACA,aAAS,KAAK,YAAY,CAAC;AAAA,EAC/B,CAAC;AACL;AAEA,SAAS,gBAAgB,UAAU;AAC/B,QAAM,KAAK;AACX,QAAM,OAAO,GAAG,QAAQ;AACxB,QAAM,WAAW,SAAS,eAAe,EAAE;AAC3C,MAAI,UAAU;AAEV,QAAI,SAAS,eAAe,MAAM,MAAM,KAAM,UAAS,eAAe,QAAQ,IAAI;AAClF;AAAA,EACJ;AACA,QAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,OAAK,KAAK;AACV,OAAK,MAAM;AACX,OAAK,OAAO;AACZ,WAAS,KAAK,YAAY,IAAI;AAClC;AAEA,SAAS,kBAAkB,UAAU;AACjC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACpC,WAAO,QAAQ,OAAO;AAAA,MAClB,OAAO,EAAE,IAAI,GAAG,QAAQ,MAAM;AAAA,MAC9B,UAAU,EAAE,oBAAoB,CAAC,EAAE;AAAA,IACvC,CAAC;AACD,WAAO,QAAQ,CAAC,uBAAuB,GAAG,MAAM;AAC5C,UAAI,OAAO,OAAQ,SAAQ,OAAO,MAAM;AAAA,UACnC,QAAO,IAAI;AAAA,QACZ,kDAAkD,QAAQ;AAAA,MACpC,CAAC;AAAA,IAC/B,GAAG,CAAC,QAAQ;AACR,aAAO,IAAI;AAAA,QACP,2CAA2C,QAAQ,QAAQ,KAAK,WAAW,GAAG;AAAA,MAElD,CAAC;AAAA,IACrC,CAAC;AAAA,EACL,CAAC;AACL;AAEA,eAAe,WAAW,UAAU;AAEhC,kBAAgB,QAAQ;AACxB,MAAI,CAAC,OAAO,SAAS;AACjB,UAAM,MAAM,GAAG,QAAQ;AACvB,UAAM,mBAAmB,GAAG;AAC5B,QAAI,CAAC,OAAO,SAAS;AACjB,YAAM,IAAI;AAAA,QACN,gBAAgB,GAAG;AAAA,MACc;AAAA,IACzC;AAAA,EACJ;AACA,SAAO,kBAAkB,QAAQ;AACrC;AA8BO,SAAS,WAAW,UAAU;AACjC,MAAI,SAAU,QAAO;AACrB,MAAI,CAAC,UAAU;AACX,WAAO,QAAQ,OAAO,IAAI;AAAA,MACtB;AAAA,IAEoC,CAAC;AAAA,EAC7C;AACA,2BAAyB;AACzB,QAAM,UAAU,WAAW,QAAQ,EAAE;AAAA,IACjC,CAAC,MAAM;AAAE,qBAAe,CAAC;AAAG,aAAO;AAAA,IAAG;AAAA,IACtC,CAAC,QAAQ;AAGL,UAAI,CAAC,OAAO,WAAW,aAAa,QAAS,YAAW;AACxD,YAAM;AAAA,IACV;AAAA,EAAC;AACL,aAAW;AACX,SAAO;AACX;;;ACxKA,IAAI,mBAAmB;AAGhB,SAAS,mBAAmB,IAAI;AACnC,MAAI,OAAO,OAAO,YAAY,GAAI,oBAAmB;AACzD;AASA,SAAS,YAAY,QAAQ;AACzB,MAAI;AAAE,WAAO,QAAQ;AAAA,EAAG,SAAS,GAAG;AAAE,QAAI,GAAG,YAAY,WAAY,OAAM;AAAA,EAAG;AAClF;AAIA,IAAM,aAAa;AAAA,EACf,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,IACH,EAAE,OAAO,WAAqB,YAAY,UAAU,WAAW,SAAS;AAAA,IACxE,EAAE,OAAO,WAAqB,YAAY,UAAU,WAAW,OAAO;AAAA,IACtE,EAAE,OAAO,qBAAqB,YAAY,UAAU,WAAW,OAAO;AAAA,IACtE,EAAE,OAAO,oBAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,YAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,UAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,UAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,mBAAqB,YAAY,UAAU,WAAW,OAAO;AAAA,IACtE,EAAE,OAAO,oBAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,cAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,YAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,aAAqB,YAAY,SAAS;AAAA,EACvD;AAAA,EACA,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,IAKJ,qBAAmC;AAAA,IACnC,qBAAmC;AAAA,IACnC,kCAAmC;AAAA,IACnC,8BAAmC;AAAA,IACnC,sCAAsC;AAAA,IACtC,+BAAmC;AAAA,IACnC,qCAAqC;AAAA,IACrC,2BAAmC;AAAA,IACnC,8BAAmC;AAAA,IACnC,uCAAuC;AAAA,IACvC,iCAAmC;AAAA,IACnC,uCAAuC;AAAA,IACvC,2BAAmC;AAAA;AAAA;AAAA,IAGnC,2BAAmC;AAAA,IACnC,kCAAmC;AAAA,IACnC,8BAAmC;AAAA,IACnC,0CAA0C;AAAA,IAC1C,8BAAmC;AAAA,IACnC,mCAAmC;AAAA,EACvC;AACJ;AAEA,IAAM,cAAc;AAAA,EAChB,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,IACH,EAAE,OAAO,WAAqB,YAAY,UAAU,WAAW,SAAS;AAAA,IACxE,EAAE,OAAO,WAAqB,YAAY,UAAU,WAAW,OAAO;AAAA,IACtE,EAAE,OAAO,qBAAqB,YAAY,UAAU,WAAW,OAAO;AAAA,IACtE,EAAE,OAAO,oBAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,YAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,UAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,UAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,mBAAqB,YAAY,UAAU,WAAW,OAAO;AAAA,IACtE,EAAE,OAAO,oBAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,cAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,YAAqB,YAAY,SAAS;AAAA,IACnD,EAAE,OAAO,aAAqB,YAAY,SAAS;AAAA,EACvD;AAAA,EACA,QAAQ;AAAA,IACJ,qBAAmC;AAAA,IACnC,qBAAmC;AAAA,IACnC,kCAAmC;AAAA,IACnC,8BAAmC;AAAA,IACnC,+BAAmC;AAAA,IACnC,qCAAqC;AAAA,IACrC,2BAAmC;AAAA,IACnC,2BAAmC;AAAA,IACnC,2BAAmC;AAAA,IACnC,kCAAmC;AAAA,IACnC,8BAAmC;AAAA,IACnC,8BAAmC;AAAA,IACnC,mCAAmC;AAAA,EACvC;AACJ;AAIO,IAAM,6BAA6B,OAAO,OAAO;AAAA,EACpD,MAAM;AAAA,EACN,OAAO;AACX,CAAC;AAED,IAAI,cAAc;AAWX,SAAS,oBAAoB,EAAE,MAAM,MAAM,IAAI,CAAC,GAAG;AACtD,gBAAc,OAAO,OAAO;AAAA,IACxB,MAAO,OAAO,SAAS,YAAY,OAAQ,OAAO,2BAA2B;AAAA,IAC7E,OAAQ,OAAO,UAAU,YAAY,QAAS,QAAQ,2BAA2B;AAAA,EACrF,CAAC;AACL;AAGO,SAAS,sBAAsB;AAClC,SAAO;AACX;AAGA,IAAM,iBAAiB,oBAAI,QAAQ;AAEnC,SAAS,aAAa,QAAQ;AAC1B,MAAI,UAAU,eAAe,IAAI,MAAM;AACvC,MAAI,CAAC,SAAS;AAAE,cAAU,oBAAI,IAAI;AAAG,mBAAe,IAAI,QAAQ,OAAO;AAAA,EAAG;AAC1E,aAAW,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,YAAY,MAAM,UAAU,GAAG,CAAC,YAAY,OAAO,WAAW,CAAC,GAAG;AAC5F,QAAI,QAAQ,IAAI,IAAI,EAAG;AACvB,YAAQ,IAAI,IAAI;AAChB,WAAO,OAAO,YAAY,MAAM,KAAK;AAAA,EACzC;AACJ;AAIA,SAAS,YAAY,YAAY,CAAC,GAAG;AACjC,SAAO;AAAA,IACH,UAAU;AAAA,IACV,OAAO,YAAY;AAAA,IACnB,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,SAAS,EAAE,SAAS,MAAM;AAAA,IAC1B,sBAAsB;AAAA,IACtB,qBAAqB;AAAA,IACrB,eAAe;AAAA,IACf,SAAS,EAAE,KAAK,GAAG,QAAQ,EAAE;AAAA,IAC7B,oBAAoB;AAAA,IACpB,2BAA2B;AAAA,IAC3B,qBAAqB;AAAA,IACrB,WAAW,EAAE,kBAAkB,MAAM;AAAA,IACrC,SAAS;AAAA,IACT,sBAAsB;AAAA;AAAA;AAAA;AAAA;AAAA,IAKtB,qBAAqB;AAAA,IACrB,SAAS;AAAA,MACL,cAAc;AAAA,MACd,eAAe;AAAA,MACf,eAAe;AAAA,MACf,eAAe;AAAA,MACf,aAAa;AAAA,MACb,iCAAiC;AAAA,IACrC;AAAA,IACA,kBAAkB,EAAE,OAAO,MAAM,UAAU,OAAO,SAAS,MAAM;AAAA,IACjE,gBAAgB,EAAE,SAAS,KAAK;AAAA,IAChC,UAAU;AAAA,IACV,sBAAsB;AAAA,IACtB,GAAG;AAAA,EACP;AACJ;AAEA,SAAS,kBAAkB,YAAY,CAAC,GAAG;AACvC,SAAO,YAAY;AAAA,IACf,aAAa;AAAA,IACb,sBAAsB;AAAA,IACtB,qBAAqB;AAAA,IACrB,aAAa;AAAA,IACb,SAAS;AAAA,IACT,qBAAqB;AAAA,IACrB,WAAW,EAAE,UAAU,UAAU,YAAY,UAAU,kBAAkB,MAAM;AAAA,IAC/E,UAAU;AAAA,IACV,oBAAoB;AAAA,IACpB,SAAS,EAAE,KAAK,GAAG,QAAQ,EAAE;AAAA,IAC7B,GAAG;AAAA,EACP,CAAC;AACL;AAGA,SAAS,eAAe,IAAI;AACxB,MAAI,OAAO,OAAO,YAAY,OAAO,MAAM,EAAE,KAAK,MAAM,GAAG;AACvD,UAAM,IAAI,WAAW,8DAA8D,OAAO,EAAE,CAAC,EAAE;AAAA,EACnG;AACA,SAAO;AACX;AAQO,IAAM,sBAAN,MAA0B;AAAA,EAC7B,UAAU;AAAA,EACV,gBAAgB,YAAY;AAAA,EAE5B,cAAc;AAAA,EAAC;AAAA,EAEf,MAAM,OAAO;AACT,SAAK,UAAU,MAAM;AACrB,iBAAa,KAAK,OAAO;AACzB,WAAO;AAAA,EACX;AAAA,EAEA,IAAI,SAAS;AAAE,WAAO,KAAK;AAAA,EAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpC,SAAS,OAAO;AACZ,SAAK,gBAAgB;AACrB,SAAK,SAAS,OAAO,SAAS,KAAK;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,aAAa,WAAW,eAAe,IAAI,YAAY,CAAC,GAAG;AACvD,QAAI,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,qCAAqC;AAExE,UAAM,EAAE,cAAc,WAAW,kBAAkB,UAAU,GAAG,gBAAgB,IAAI;AACpF,QAAI,YAAY,eAAe,eAAe;AAE9C,UAAM,SAAS,KAAK,QAAQ,OAAO,OAAO,WAAW;AAAA,MACjD,GAAG,YAAY,EAAE,OAAO,KAAK,cAAc,CAAC;AAAA,MAC5C,OAAO;AAAA,MACP,GAAG;AAAA,IACP,CAAC;AAKD,QAAI,YAAY;AAChB,QAAI,mBAAmB;AACvB,QAAI,eAAe;AACnB,QAAI,cAAc;AACd,aAAO,cAAc,EAAE,WAAW,EAAE,kBAAkB,KAAK,EAAE,CAAC;AAAA,IAClE,OAAO;AACH,qBAAe,MAAM;AACjB,cAAM,aAAa,OAAO,iBAAiB;AAC3C,cAAM,gBAAgB,KAAK,IAAI,WAAW,KAAK,IAAI,WAAW,UAAU,CAAC;AACzE,kBAAU,MAAM,SAAS,GAAG,aAAa;AACzC,eAAO,OAAO;AACd,eAAO,cAAc,EAAE,WAAW,EAAE,kBAAkB,aAAa,UAAU,EAAE,CAAC;AAAA,MACpF;AACA,yBAAmB,OAAO,uBAAuB,YAAY;AAC7D,mBAAa;AAAA,IACjB;AAMA,QAAI,qBAAqB;AACzB,UAAM,oBAAoB,MAAM;AAC5B,YAAM,gBAAgB,UAAU,cAAc,iBAAiB;AAC/D,UAAI,CAAC,cAAe;AAEpB,2BAAqB,IAAI,iBAAiB,MAAM;AAC5C,cAAM,KAAK,UAAU,cAAc,sBAAsB;AACzD,YAAI,GAAI,IAAG,eAAe,EAAE,OAAO,WAAW,UAAU,SAAS,CAAC;AAAA,MACtE,CAAC;AACD,yBAAmB,QAAQ,eAAe;AAAA,QACtC,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,iBAAiB,CAAC,OAAO;AAAA,MAC7B,CAAC;AAAA,IACL;AAEA,0BAAsB,iBAAiB;AAEvC,WAAO;AAAA,MACH;AAAA,MACA,UAAU,MAAM,OAAO,SAAS;AAAA,MAChC,UAAU,CAAC,MAAM;AAAE,YAAI,OAAO,SAAS,MAAM,EAAG,QAAO,SAAS,CAAC;AAAA,MAAG;AAAA,MACpE,aAAa,CAAC,OAAO,OAAO,wBAAwB,EAAE;AAAA,MACtD,OAAO,MAAM,OAAO,MAAM;AAAA,MAC1B,QAAQ,MAAM,OAAO,OAAO;AAAA,MAC5B,cAAc,CAAC,MAAM;AAAE,oBAAY;AAAA,MAAG;AAAA;AAAA,MAEtC,cAAc,CAAC,OAAO;AAClB,oBAAY,eAAe,EAAE;AAC7B,uBAAe;AAAA,MACnB;AAAA,MACA;AAAA,MACA,SAAS,MAAM;AACX,4BAAoB,WAAW;AAC/B,0BAAkB,QAAQ;AAC1B,oBAAY,MAAM;AAAA,MACtB;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,mBAAmB,WAAW,eAAe,IAAI,YAAY,CAAC,GAAG;AAC7D,QAAI,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,qCAAqC;AAExE,cAAU,MAAM,SAAS;AAEzB,UAAM,SAAS,KAAK,QAAQ,OAAO,OAAO,WAAW;AAAA,MACjD,GAAG,kBAAkB,EAAE,OAAO,KAAK,cAAc,CAAC;AAAA,MAClD,OAAO;AAAA,MACP,GAAG;AAAA,IACP,CAAC;AAGD,WAAO,UAAU,CAAC,MAAM;AACpB,UAAI,EAAE,YAAY,KAAK,QAAQ,QAAQ,OAAO;AAC1C,UAAE,eAAe;AACjB,UAAE,gBAAgB;AAAA,MACtB;AAAA,IACJ,CAAC;AAED,WAAO;AAAA,MACH;AAAA,MACA,UAAU,MAAM,OAAO,SAAS;AAAA,MAChC,UAAU,CAAC,MAAM;AAAE,YAAI,OAAO,SAAS,MAAM,EAAG,QAAO,SAAS,CAAC;AAAA,MAAG;AAAA,MACpE,aAAa,CAAC,OAAO,OAAO,wBAAwB,EAAE;AAAA,MACtD,OAAO,MAAM,OAAO,MAAM;AAAA,MAC1B,QAAQ,MAAM,OAAO,OAAO;AAAA,MAC5B,SAAS,MAAM,YAAY,MAAM;AAAA,IACrC;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,qBAAqB,WAAW,eAAe,IAAI;AAC/C,WAAO,KAAK,aAAa,WAAW,cAAc;AAAA,MAC9C,UAAU;AAAA,MACV,UAAU;AAAA,MACV,aAAa;AAAA,MACb,SAAS;AAAA,MACT,kBAAkB;AAAA,IACtB,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,gBAAgB,WAAW,aAAa,IAAI;AACxC,WAAO,KAAK,aAAa,WAAW,YAAY;AAAA,MAC5C,UAAU;AAAA,MACV,aAAa;AAAA,MACb,aAAa;AAAA,MACb,SAAS;AAAA,IACb,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,iBAAiB,WAAW,kBAAkB,IAAI,kBAAkB,IAAI,UAAU,CAAC,GAAG;AAClF,UAAM,SAAS,KAAK;AACpB,UAAM,WAAW,QAAQ,YAAY;AAIrC,QAAI,SAAS;AAAA,MACT,UAAU,OAAO,OAAO,YAAY,iBAAiB,QAAQ;AAAA,MAC7D,UAAU,OAAO,OAAO,YAAY,iBAAiB,QAAQ;AAAA,IACjE;AAEA,UAAM,aAAa,OAAO,OAAO,iBAAiB,WAAW;AAAA,MACzD,OAAO,KAAK;AAAA,MACZ,UAAU,QAAQ,aAAa;AAAA,MAC/B,kBAAkB;AAAA,MAClB,iBAAiB;AAAA,MACjB,SAAS,EAAE,SAAS,MAAM;AAAA,MAC1B,sBAAsB;AAAA,MACtB,aAAa;AAAA,MACb,aAAa;AAAA,MACb,SAAS;AAAA,MACT,qBAAqB;AAAA,MACrB,yBAAyB;AAAA,IAC7B,CAAC;AAED,eAAW,SAAS,MAAM;AAE1B,WAAO;AAAA,MACH,QAAQ;AAAA,MACR,UAAU,UAAU,UAAU,MAAM;AAChC,cAAM,WAAW;AACjB,iBAAS;AAAA,UACL,UAAU,OAAO,OAAO,YAAY,UAAU,QAAQ,QAAQ;AAAA,UAC9D,UAAU,OAAO,OAAO,YAAY,UAAU,QAAQ,QAAQ;AAAA,QAClE;AAGA,mBAAW,SAAS,MAAM;AAC1B,oBAAY,SAAS,QAAQ;AAC7B,oBAAY,SAAS,QAAQ;AAAA,MACjC;AAAA,MACA,UAAU;AACN,oBAAY,UAAU;AACtB,oBAAY,OAAO,QAAQ;AAC3B,oBAAY,OAAO,QAAQ;AAAA,MAC/B;AAAA,IACJ;AAAA,EACJ;AACJ;AAOA,IAAI,kBAAkB;AAGtB,IAAI,kBAAkB;AAGf,SAAS,kBAAkB,UAAU;AACxC,MAAI,OAAO,aAAa,YAAY,SAAU,mBAAkB;AACpE;AAiBO,SAAS,mBAAmB;AAC/B,MAAI,CAAC,iBAAiB;AAClB,sBAAkB,WAAW,eAAe,EACvC,KAAK,MAAM,IAAI,oBAAoB,EAAE,KAAK,CAAC,EAC3C,MAAM,CAAC,QAAQ;AAAE,wBAAkB;AAAM,YAAM;AAAA,IAAK,CAAC;AAAA,EAC9D;AACA,SAAO;AACX;;;ACpfO,IAAM,oBAAN,MAAwB;AAAA;AAAA,EAE3B,UAAU;AAAA;AAAA,EAEV,MAAM;AAAA;AAAA,EAEN,eAAe;AAAA;AAAA,EAEf,gBAAgB;AAAA;AAAA,EAEhB,aAAa;AAAA;AAAA,EAGb,UAAU;AAAA,EACV,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,YAAY;AAAA;AAAA;AAAA,EAIZ,WAAW,CAAC;AAAA,EACZ,gBAAgB;AAAA;AAAA,EAGhB,YAAY;AAAA;AAAA,EACZ,mBAAmB;AAAA;AAAA,EACnB,kBAAkB;AAAA;AAAA,EAClB,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMhB,YAAY,QAAQ,EAAE,UAAU,iBAAiB,gBAAgB,aAAa,GAAG;AAC7E,SAAK,UAAU;AACf,SAAK,YAAY;AACjB,SAAK,mBAAmB;AACxB,SAAK,kBAAkB;AACvB,SAAK,gBAAgB;AAErB,SAAK,QAAQ;AAAA,EACjB;AAAA,EAEA,IAAI,SAAS;AAAE,WAAO,KAAK;AAAA,EAAS;AAAA;AAAA,EAIpC,KAAK,cAAc,OAAO;AACtB,SAAK,UAAU;AACf,SAAK,eAAe;AACpB,SAAK,IAAI,UAAU,IAAI,qBAAqB;AAC5C,SAAK,IAAI,cAAc,6BAA6B,EAAE,MAAM,UACxD,cAAc,KAAK;AACvB,SAAK,kBAAkB;AAGvB,UAAM,MAAM,OAAO,aAAa,GAAG,SAAS,GAAG,KAAK;AACpD,QAAI,OAAO,IAAI,SAAS,OAAO,CAAC,IAAI,SAAS,IAAI,GAAG;AAChD,WAAK,aAAa,QAAQ;AAAA,IAC9B;AAEA,SAAK,aAAa,MAAM;AACxB,SAAK,aAAa,OAAO;AAEzB,QAAI,KAAK,aAAa,MAAO,MAAK,WAAW;AAAA,EACjD;AAAA,EAEA,QAAQ;AACJ,SAAK,UAAU;AACf,SAAK,IAAI,UAAU,OAAO,qBAAqB;AAC/C,SAAK,oBAAoB;AACzB,SAAK,WAAW,CAAC;AACjB,SAAK,gBAAgB;AACrB,SAAK,iBAAiB;AAAA,EAC1B;AAAA,EAEA,UAAU;AACN,SAAK,oBAAoB;AACzB,SAAK,KAAK,OAAO;AACjB,SAAK,MAAM;AAAA,EACf;AAAA;AAAA,EAIA,UAAU;AACN,UAAM,KAAK,SAAS,cAAc,KAAK;AACvC,OAAG,YAAY;AACf,OAAG,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6Cf,SAAK,MAAM;AACX,SAAK,eAAe,GAAG,cAAc,4BAA4B;AACjE,SAAK,gBAAgB,GAAG,cAAc,+BAA+B;AACrE,SAAK,aAAa,GAAG,cAAc,4BAA4B;AAE/D,SAAK,YAAY;AACjB,SAAK,QAAQ,QAAQ,EAAE;AAAA,EAC3B;AAAA,EAEA,cAAc;AAEV,SAAK,aAAa,iBAAiB,SAAS,MAAM,KAAK,WAAW,CAAC;AAEnE,SAAK,aAAa,iBAAiB,WAAW,CAAC,MAAM;AACjD,UAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AAClC,UAAE,eAAe;AACjB,aAAK,cAAc;AAAA,MACvB,WAAW,EAAE,QAAQ,WAAW,EAAE,UAAU;AACxC,UAAE,eAAe;AACjB,aAAK,cAAc;AAAA,MACvB,WAAW,EAAE,QAAQ,UAAU;AAC3B,UAAE,eAAe;AACjB,aAAK,MAAM;AAAA,MACf;AAAA,IACJ,CAAC;AAGD,SAAK,cAAc,iBAAiB,WAAW,CAAC,MAAM;AAClD,UAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AAClC,UAAE,eAAe;AACjB,aAAK,WAAW;AAAA,MACpB,WAAW,EAAE,QAAQ,UAAU;AAC3B,UAAE,eAAe;AACjB,aAAK,MAAM;AAAA,MACf;AAAA,IACJ,CAAC;AAGD,SAAK,IAAI,iBAAiB,SAAS,CAAC,MAAM;AACtC,YAAM,MAAM,EAAE,OAAO,QAAQ,eAAe;AAC5C,UAAI,CAAC,IAAK;AACV,cAAQ,IAAI,QAAQ,QAAQ;AAAA,QACxB,KAAK;AAAe,eAAK,cAAc;AAAG;AAAA,QAC1C,KAAK;AAAe,eAAK,cAAc;AAAG;AAAA,QAC1C,KAAK;AAAe,eAAK,MAAM;AAAG;AAAA,QAClC,KAAK;AAAe,eAAK,WAAW;AAAG;AAAA,QACvC,KAAK;AAAe,eAAK,cAAc;AAAG;AAAA,QAC1C,KAAK;AACD,eAAK,iBAAiB,CAAC,KAAK;AAC5B,cAAI,UAAU,OAAO,8BAA8B,KAAK,cAAc;AACtE,eAAK,WAAW;AAChB;AAAA,QACJ,KAAK;AACD,eAAK,aAAa,CAAC,KAAK;AACxB,cAAI,UAAU,OAAO,8BAA8B,KAAK,UAAU;AAClE,eAAK,WAAW;AAChB;AAAA,QACJ,KAAK;AACD,eAAK,YAAY,CAAC,KAAK;AACvB,cAAI,UAAU,OAAO,8BAA8B,KAAK,SAAS;AACjE,eAAK,WAAW;AAChB;AAAA,MACR;AAAA,IACJ,CAAC;AAGD,SAAK,IAAI,cAAc,gCAAgC,EAAE,iBAAiB,SAAS,MAAM;AACrF,WAAK,eAAe,CAAC,KAAK;AAC1B,WAAK,IAAI,cAAc,6BAA6B,EAAE,MAAM,UACxD,KAAK,eAAe,KAAK;AAC7B,WAAK,kBAAkB;AAAA,IAC3B,CAAC;AAGD,SAAK,IAAI,iBAAiB,WAAW,CAAC,MAAM;AACxC,UAAI,EAAE,UAAU,EAAE,QAAQ,KAAK;AAC3B,UAAE,eAAe;AACjB,aAAK,iBAAiB,CAAC,KAAK;AAC5B,aAAK,IAAI,cAAc,sBAAsB,EAAE,UAAU,OAAO,8BAA8B,KAAK,cAAc;AACjH,aAAK,WAAW;AAAA,MACpB,WAAW,EAAE,UAAU,EAAE,QAAQ,KAAK;AAClC,UAAE,eAAe;AACjB,aAAK,aAAa,CAAC,KAAK;AACxB,aAAK,IAAI,cAAc,sBAAsB,EAAE,UAAU,OAAO,8BAA8B,KAAK,UAAU;AAC7G,aAAK,WAAW;AAAA,MACpB,WAAW,EAAE,UAAU,EAAE,QAAQ,KAAK;AAClC,UAAE,eAAe;AACjB,aAAK,YAAY,CAAC,KAAK;AACvB,aAAK,IAAI,cAAc,uBAAuB,EAAE,UAAU,OAAO,8BAA8B,KAAK,SAAS;AAC7G,aAAK,WAAW;AAAA,MACpB;AAAA,IACJ,CAAC;AAID,SAAK,IAAI,iBAAiB,WAAW,CAAC,MAAM;AACxC,QAAE,gBAAgB;AAClB,UAAI,EAAE,YAAY,EAAE,QAAQ,OAAO,EAAE,QAAQ,MAAM;AAC/C,UAAE,eAAe;AACjB,YAAI,EAAE,QAAQ,OAAO,CAAC,KAAK,cAAc;AACrC,eAAK,eAAe;AACpB,eAAK,IAAI,cAAc,6BAA6B,EAAE,MAAM,UAAU;AACtE,eAAK,kBAAkB;AAAA,QAC3B;AAAA,MACJ;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EAEA,oBAAoB;AAChB,UAAM,OAAO,KAAK,IAAI,cAAc,2DAA2D;AAC/F,QAAI,KAAM,MAAK,cAAc,KAAK,eAAe,gBAAgB;AAAA,EACrE;AAAA,EAEA,mBAAmB;AACf,QAAI,CAAC,KAAK,WAAY;AACtB,QAAI,KAAK,SAAS,WAAW,GAAG;AAC5B,YAAM,WAAW,KAAK,cAAc,OAAO,SAAS;AACpD,WAAK,WAAW,cAAc,WAAW,eAAe;AACxD,WAAK,WAAW,UAAU,OAAO,yCAAyC,QAAQ;AAAA,IACtF,OAAO;AACH,WAAK,WAAW,cAAc,GAAG,KAAK,gBAAgB,CAAC,OAAO,KAAK,SAAS,MAAM;AAClF,WAAK,WAAW,UAAU,OAAO,uCAAuC;AAAA,IAC5E;AAAA,EACJ;AAAA;AAAA,EAIA,cAAc;AACV,UAAM,MAAM,KAAK,aAAa;AAC9B,QAAI,CAAC,IAAK,QAAO;AAEjB,QAAI;AACJ,QAAI,KAAK,WAAW;AAChB,UAAI;AAAE,kBAAU;AAAK,YAAI,OAAO,OAAO;AAAA,MAAG,QACpC;AAAE,eAAO;AAAA,MAAM;AAAA,IACzB,OAAO;AACH,gBAAU,IAAI,QAAQ,uBAAuB,MAAM;AAAA,IACvD;AAEA,QAAI,KAAK,WAAY,WAAU,MAAM,OAAO;AAE5C,UAAM,QAAQ,OAAO,KAAK,iBAAiB,KAAK;AAChD,QAAI;AAAE,aAAO,IAAI,OAAO,SAAS,KAAK;AAAA,IAAG,QACnC;AAAE,aAAO;AAAA,IAAM;AAAA,EACzB;AAAA,EAEA,aAAa;AACT,SAAK,oBAAoB;AACzB,SAAK,WAAW,CAAC;AACjB,SAAK,gBAAgB;AAErB,UAAM,QAAQ,KAAK,YAAY;AAC/B,QAAI,CAAC,OAAO;AACR,WAAK,iBAAiB;AACtB;AAAA,IACJ;AAEA,UAAM,QAAQ,KAAK,UAAU;AAE7B,eAAW,EAAE,GAAG,KAAK,OAAO;AACxB,YAAM,WAAW,KAAK,iBAAiB,EAAE;AACzC,UAAI,CAAC,SAAU;AAEf,YAAM,OAAO,SAAS,kBAAkB;AACxC,UAAI,CAAC,KAAM;AAEX,YAAM,YAAY;AAClB,UAAI;AACJ,UAAI,cAAc;AAClB,cAAQ,IAAI,MAAM,KAAK,IAAI,OAAO,MAAM;AACpC,aAAK,SAAS,KAAK,EAAE,QAAQ,IAAI,aAAa,YAAY,EAAE,MAAM,CAAC;AACnE;AACA,YAAI,CAAC,MAAM,OAAQ;AAAA,MACvB;AAAA,IACJ;AAEA,QAAI,KAAK,SAAS,SAAS,GAAG;AAC1B,WAAK,gBAAgB;AACrB,WAAK,iBAAiB;AAAA,IAC1B;AAEA,SAAK,iBAAiB;AAAA,EAC1B;AAAA;AAAA,EAIA,gBAAgB;AACZ,QAAI,KAAK,SAAS,WAAW,EAAG;AAChC,SAAK,iBAAiB,KAAK,gBAAgB,KAAK,KAAK,SAAS;AAC9D,SAAK,iBAAiB;AACtB,SAAK,sBAAsB;AAC3B,SAAK,iBAAiB;AAAA,EAC1B;AAAA,EAEA,gBAAgB;AACZ,QAAI,KAAK,SAAS,WAAW,EAAG;AAChC,SAAK,iBAAiB,KAAK,gBAAgB,IAAI,KAAK,SAAS,UAAU,KAAK,SAAS;AACrF,SAAK,iBAAiB;AACtB,SAAK,sBAAsB;AAC3B,SAAK,iBAAiB;AAAA,EAC1B;AAAA,EAEA,wBAAwB;AACpB,UAAM,QAAQ,KAAK,SAAS,KAAK,aAAa;AAC9C,QAAI,CAAC,MAAO;AACZ,SAAK,cAAc,MAAM,MAAM;AAG/B,0BAAsB,MAAM;AACxB,YAAM,SAAS,KAAK,gBAAgB,MAAM,MAAM;AAChD,UAAI,CAAC,OAAQ;AACb,YAAM,SAAS,OAAO,cAAc,0BAA0B;AAC9D,UAAI,OAAQ,QAAO,eAAe,EAAE,OAAO,WAAW,UAAU,SAAS,CAAC;AAAA,IAC9E,CAAC;AAAA,EACL;AAAA;AAAA,EAIA,mBAAmB;AACf,UAAM,QAAQ,KAAK,YAAY;AAC/B,QAAI,CAAC,MAAO;AAGZ,UAAM,gBAAgB,oBAAI,IAAI;AAC9B,eAAW,KAAK,KAAK,UAAU;AAC3B,UAAI,CAAC,cAAc,IAAI,EAAE,MAAM,EAAG,eAAc,IAAI,EAAE,QAAQ,CAAC,CAAC;AAChE,oBAAc,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC;AAAA,IACtC;AAEA,UAAM,eAAe,KAAK,SAAS,KAAK,aAAa;AAGrD,UAAM,QAAQ,KAAK,UAAU;AAC7B,eAAW,EAAE,GAAG,KAAK,OAAO;AACxB,YAAM,WAAW,KAAK,iBAAiB,EAAE;AACzC,UAAI,CAAC,SAAU;AAEf,YAAM,SAAS,KAAK,gBAAgB,EAAE;AAEtC,UAAI,cAAc,IAAI,EAAE,GAAG;AACvB,cAAM,eAAgB,cAAc,WAAW,KACzC,aAAa,aACb;AACN,iBAAS,sBAAsB,OAAO,YAAY;AAClD,gBAAQ,UAAU,IAAI,6BAA6B;AACnD,gBAAQ,UAAU,OAAO,gCAAgC,cAAc,WAAW,EAAE;AAAA,MACxF,OAAO;AACH,iBAAS,sBAAsB;AAC/B,gBAAQ,UAAU,OAAO,+BAA+B,8BAA8B;AAAA,MAC1F;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,sBAAsB;AAClB,UAAM,QAAQ,KAAK,UAAU;AAC7B,eAAW,EAAE,GAAG,KAAK,OAAO;AACxB,YAAM,WAAW,KAAK,iBAAiB,EAAE;AACzC,gBAAU,sBAAsB;AAChC,YAAM,SAAS,KAAK,gBAAgB,EAAE;AACtC,cAAQ,UAAU,OAAO,+BAA+B,8BAA8B;AAAA,IAC1F;AAAA,EACJ;AAAA;AAAA,EAIA,aAAa;AACT,QAAI,KAAK,SAAS,WAAW,KAAK,KAAK,gBAAgB,EAAG;AAE1D,UAAM,QAAQ,KAAK,SAAS,KAAK,aAAa;AAC9C,UAAM,WAAW,KAAK,iBAAiB,MAAM,MAAM;AACnD,QAAI,CAAC,SAAU;AAEf,UAAM,QAAQ,KAAK,YAAY;AAC/B,QAAI,CAAC,MAAO;AAEZ,UAAM,cAAc,KAAK,cAAc;AACvC,UAAM,UAAU,SAAS,aAAa,OAAO,MAAM,aAAa,WAAW;AAE3E,QAAI,SAAS;AAET,YAAM,WAAW,KAAK;AACtB,WAAK,WAAW;AAChB,UAAI,KAAK,SAAS,SAAS,GAAG;AAC1B,aAAK,gBAAgB,KAAK,IAAI,UAAU,KAAK,SAAS,SAAS,CAAC;AAChE,aAAK,iBAAiB;AACtB,aAAK,sBAAsB;AAC3B,aAAK,iBAAiB;AAAA,MAC1B;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,gBAAgB;AACZ,UAAM,QAAQ,KAAK,YAAY;AAC/B,QAAI,CAAC,SAAS,KAAK,SAAS,WAAW,EAAG;AAE1C,UAAM,cAAc,KAAK,cAAc;AAGvC,UAAM,UAAU,CAAC,GAAG,IAAI,IAAI,KAAK,SAAS,IAAI,OAAK,EAAE,MAAM,CAAC,CAAC;AAC7D,QAAI,gBAAgB;AAEpB,eAAW,UAAU,SAAS;AAC1B,YAAM,WAAW,KAAK,iBAAiB,MAAM;AAC7C,UAAI,SAAU,kBAAiB,SAAS,WAAW,OAAO,WAAW;AAAA,IACzE;AAEA,QAAI,gBAAgB,EAAG,MAAK,WAAW;AAAA,EAC3C;AACJ;;;AC9bO,IAAM,sBAAN,MAA0B;AAAA;AAAA,EAG7B,aAAa,CAAC;AAAA;AAAA,EAGd,aAAa,CAAC;AAAA;AAAA,EAGd;AAAA;AAAA,EAGA;AAAA;AAAA,EAGA,YAAY;AAAA;AAAA;AAAA;AAAA,EAKZ,YAAY,EAAE,UAAU,KAAK,gBAAgB,KAAK,IAAI,CAAC,GAAG;AACtD,SAAK,WAAW;AAChB,SAAK,iBAAiB;AAAA,EAC1B;AAAA;AAAA,EAIA,IAAI,UAAU;AAAE,WAAO,KAAK,WAAW,SAAS;AAAA,EAAG;AAAA,EACnD,IAAI,UAAU;AAAE,WAAO,KAAK,WAAW,SAAS;AAAA,EAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQnD,KAAK,SAAS;AACV,QAAI,KAAK,UAAW;AACpB,SAAK,WAAW,KAAK,OAAO;AAC5B,QAAI,KAAK,WAAW,SAAS,KAAK,UAAU;AACxC,WAAK,WAAW,MAAM;AAAA,IAC1B;AACA,SAAK,aAAa,CAAC;AACnB,SAAK,QAAQ;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO;AACT,UAAM,MAAM,KAAK,WAAW,IAAI;AAChC,QAAI,CAAC,IAAK,QAAO;AACjB,SAAK,YAAY;AACjB,QAAI;AACA,YAAM,IAAI,KAAK;AAAA,IACnB,UAAE;AACE,WAAK,YAAY;AAAA,IACrB;AACA,SAAK,WAAW,KAAK,GAAG;AACxB,SAAK,QAAQ;AACb,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO;AACT,UAAM,MAAM,KAAK,WAAW,IAAI;AAChC,QAAI,CAAC,IAAK,QAAO;AACjB,SAAK,YAAY;AACjB,QAAI;AACA,YAAM,IAAI,KAAK;AAAA,IACnB,UAAE;AACE,WAAK,YAAY;AAAA,IACrB;AACA,SAAK,WAAW,KAAK,GAAG;AACxB,SAAK,QAAQ;AACb,WAAO;AAAA,EACX;AAAA;AAAA,EAGA,QAAQ;AACJ,SAAK,aAAa,CAAC;AACnB,SAAK,aAAa,CAAC;AACnB,SAAK,QAAQ;AAAA,EACjB;AAAA;AAAA,EAIA,UAAU;AACN,SAAK,iBAAiB,KAAK,SAAS,KAAK,OAAO;AAAA,EACpD;AACJ;;;AC3FO,IAAM,qBAAN,MAAyB;AAAA;AAAA,EAE5B,aAAa;AAAA;AAAA,EAGb,SAAS;AAAA;AAAA,EAGT,SAAS;AAAA;AAAA,EAGT,WAAW;AAAA;AAAA,EAGX,SAAS;AAAA;AAAA,EAGT,WAAW;AAAA;AAAA,EAGX,kBAAkB;AAAA;AAAA,EAGlB,YAAY;AAAA,EACZ,oBAAoB;AAAA,EACpB,kBAAkB;AAAA;AAAA;AAAA;AAAA,EAKlB,YAAY,OAAO,CAAC,GAAG;AACnB,SAAK,kBAAkB,KAAK,kBAAkB;AAAA,EAClD;AAAA,EAEA,IAAI,UAAU;AAAE,WAAO,KAAK;AAAA,EAAU;AAAA,EACtC,IAAI,QAAU;AAAE,WAAO,KAAK;AAAA,EAAQ;AAAA,EAEpC,MAAM,WAAW;AACb,SAAK,aAAa;AAClB,SAAK,WAAW,UAAU,IAAI,sBAAsB;AACpD,SAAK,WAAW,YAAY;AAG5B,SAAK,WAAW,SAAS,cAAc,KAAK;AAC5C,SAAK,SAAS,YAAY;AAC1B,SAAK,SAAS,iBAAiB,aAAa,CAAC,MAAM,KAAK,oBAAoB,CAAC,CAAC;AAE9E,SAAK,aAAa;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAS,OAAO,OAAO;AACnB,SAAK,SAAS;AACd,SAAK,SAAS,SAAS;AACvB,SAAK,WAAW,CAAC,CAAC;AAClB,SAAK,aAAa;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,OAAO;AACZ,SAAK,SAAS,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAChD,QAAI,KAAK,SAAU,MAAK,YAAY;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU;AACN,QAAI,CAAC,KAAK,SAAU,QAAO;AAC3B,UAAM,UAAU,KAAK;AACrB,SAAK,SAAS;AACd,SAAK,WAAW;AAChB,SAAK,aAAa;AAClB,WAAO;AAAA,EACX;AAAA,EAEA,UAAU;AACN,SAAK,UAAU;AACf,SAAK,aAAa;AAClB,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,WAAW;AAAA,EACpB;AAAA;AAAA,EAIA,eAAe;AACX,QAAI,CAAC,KAAK,WAAY;AACtB,SAAK,WAAW,YAAY;AAE5B,QAAI,CAAC,KAAK,OAAQ;AAKlB,QAAI,CAAC,KAAK,UAAU;AAEhB,WAAK,OAAO,UAAU,IAAI,YAAY;AACtC,WAAK,OAAO,MAAM,OAAO;AACzB,WAAK,OAAO,MAAM,WAAW;AAC7B,WAAK,WAAW,YAAY,KAAK,MAAM;AACvC,WAAK,WAAW,UAAU,OAAO,6BAA6B;AAAA,IAClE,OAAO;AAEH,WAAK,OAAO,UAAU,IAAI,YAAY;AACtC,WAAK,OAAO,UAAU,IAAI,YAAY;AACtC,WAAK,OAAO,MAAM,WAAW;AAC7B,WAAK,OAAO,MAAM,WAAW;AAE7B,WAAK,WAAW,YAAY,KAAK,MAAM;AACvC,WAAK,WAAW,YAAY,KAAK,QAAQ;AACzC,WAAK,WAAW,YAAY,KAAK,MAAM;AACvC,WAAK,WAAW,UAAU,IAAI,6BAA6B;AAE3D,WAAK,YAAY;AAAA,IACrB;AAAA,EACJ;AAAA,EAEA,cAAc;AACV,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,OAAQ;AAElC,UAAM,IAAI,KAAK;AACf,SAAK,OAAO,MAAM,OAAO,GAAG,CAAC;AAC7B,SAAK,OAAO,MAAM,OAAO,GAAG,IAAI,CAAC;AAAA,EACrC;AAAA;AAAA,EAIA,oBAAoB,GAAG;AACnB,QAAI,EAAE,WAAW,EAAG;AACpB,MAAE,eAAe;AACjB,SAAK,YAAY;AACjB,SAAK,SAAS,UAAU,IAAI,4BAA4B;AACxD,aAAS,KAAK,MAAM,SAAS;AAG7B,aAAS,KAAK,MAAM,aAAa;AACjC,SAAK,WAAW,iBAAiB,QAAQ,EAAE,QAAQ,OAAK,EAAE,MAAM,gBAAgB,MAAM;AAEtF,SAAK,oBAAoB,CAACA,OAAM,KAAK,aAAaA,EAAC;AACnD,SAAK,kBAAkB,MAAM,KAAK,WAAW;AAC7C,aAAS,iBAAiB,aAAa,KAAK,iBAAiB;AAC7D,aAAS,iBAAiB,WAAW,KAAK,eAAe;AAAA,EAC7D;AAAA,EAEA,aAAa,GAAG;AACZ,QAAI,CAAC,KAAK,aAAa,CAAC,KAAK,WAAY;AACzC,UAAM,OAAO,KAAK,WAAW,sBAAsB;AACnD,UAAM,eAAe,KAAK,SAAS;AACnC,UAAM,SAAS,KAAK,QAAQ;AAC5B,QAAI,UAAU,EAAG;AAEjB,UAAM,IAAI,EAAE,UAAU,KAAK,OAAO,eAAe;AACjD,UAAM,QAAQ,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,IAAI,MAAM,CAAC;AACrD,SAAK,SAAS;AACd,SAAK,YAAY;AAAA,EACrB;AAAA,EAEA,aAAa;AACT,SAAK,UAAU;AACf,SAAK,kBAAkB,KAAK,MAAM;AAAA,EACtC;AAAA,EAEA,YAAY;AACR,QAAI,CAAC,KAAK,UAAW;AACrB,SAAK,YAAY;AACjB,SAAK,UAAU,UAAU,OAAO,4BAA4B;AAC5D,aAAS,KAAK,MAAM,SAAS;AAC7B,aAAS,KAAK,MAAM,aAAa;AACjC,SAAK,YAAY,iBAAiB,QAAQ,EAAE,QAAQ,OAAK,EAAE,MAAM,gBAAgB,EAAE;AACnF,aAAS,oBAAoB,aAAa,KAAK,iBAAiB;AAChE,aAAS,oBAAoB,WAAW,KAAK,eAAe;AAC5D,SAAK,oBAAoB;AACzB,SAAK,kBAAkB;AAAA,EAC3B;AACJ;",
6
6
  "names": ["e"]
7
7
  }
@@ -4,6 +4,30 @@
4
4
 
5
5
  Plotly-backed charts: the wrapper, the chart types, subplot layouts, downsampling, the plot grid, the plot-config editor and the two plot windows. Plotly itself is not in the bundle; call `setPlotlySource()` with the path to your copy. The plot window's `streamEvents` option is described with the [other application-neutral options](../concepts/domain-neutrality.md#no-application-globals-the-gallery-picker-and-plot-window-events-0170).
6
6
 
7
+ ## A data series with no saved plot config opens (0.17.5)
8
+
9
+ `openDataSeriesPlotWindow` (and `new DataSeriesPlotWindow`, the module's
10
+ default export) asks `getPlotConfig(seriesId)` for the series' plot config.
11
+ When that answers nothing, the window builds a default one with
12
+ `DataSeriesPlotWindow.createDefaultConfig(headers, numericColumns, classifyHeader)`:
13
+ X is the first numeric column the classifier calls `'time'` (else the first
14
+ numeric column), and Y is up to three of the other numeric columns. The window
15
+ passes the `classifyHeader` it was given, so an application whose time column
16
+ is `year` or `date_index` gets that column on X; without one the default
17
+ classifier calls `t`, `time`, `step` and `period` time.
18
+
19
+ ```js
20
+ openDataSeriesPlotWindow({ seriesId, seriesName, headers, dataHub, services,
21
+ getPlotConfig, savePlotConfig,
22
+ classifyHeader: (h) => (h === 'year' ? 'time' : 'indicator') }); // X is `year`
23
+ ```
24
+
25
+ The third parameter of `createDefaultConfig` is new and optional: called with
26
+ two arguments it uses the default classifier. Until 0.17.5 the static method
27
+ read `this.classifyHeader`, which a class does not have, so opening any series
28
+ with no saved plot config threw `TypeError: this.classifyHeader is not a
29
+ function` (BUG-0037). `tests/data_series_plot_default_config.test.mjs` holds it.
30
+
7
31
  ## Derived series: `seriesTransforms` (0.17.0)
8
32
 
9
33
  A plot can draw series worked out from its own: a trend, a moving average, a
@@ -20,3 +20,24 @@ A non-positive or non-number cap throws a `RangeError`. With `noAutoHeight`
20
20
  the container's own layout decides the size and `setMaxHeight` has no effect.
21
21
  Before 0.17.0 the cap was a fixed 400px, switched on by `data-code-max-height="true"`
22
22
  on a `.notebook-editor-container` ancestor; that attribute is no longer read.
23
+
24
+ ## Monaco's `Canceled` rejections are hidden (0.17.6)
25
+
26
+ When an editor gets focus, Monaco's word highlighter starts a 50 ms timer and
27
+ never catches its promise. Dispose the editor inside those 50 ms (re-render a
28
+ view right after focus moves back to it, as an Edit > Undo from the menu bar
29
+ does) and Monaco rejects that promise with its CancellationError, which nothing
30
+ handles. VS Code hides these with its own handler; standalone Monaco has none.
31
+
32
+ The first `initMonaco()` that starts a load now installs one window
33
+ `unhandledrejection` listener. It calls `preventDefault()` only when the
34
+ rejection's reason has `name === 'Canceled'` and `message === 'Canceled'`, which
35
+ is Monaco's CancellationError. Every other rejection still reaches the page and
36
+ its console. The listener is installed once per page, however often
37
+ `initMonaco()` is called or retried, and stays for the life of the page, as
38
+ Monaco does.
39
+
40
+ Until 0.17.6 the browser logged an uncaught `Canceled: Canceled` from
41
+ `Delayer.cancel`. The editor handle's `dispose()` caught only a `Canceled` that
42
+ Monaco's `dispose()` throws at once, not this later one.
43
+ `tests/monaco_cancellation_guard.test.mjs` holds it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flexdesk",
3
- "version": "0.17.4",
3
+ "version": "0.17.6",
4
4
  "description": "A tiling window manager and widget set for desktop-class web applications. Zero runtime dependencies.",
5
5
  "license": "MIT",
6
6
  "repository": {