/** * ObsiGate — Inline editor host (ROADMAP #93) * * The edition surface (`#editor-container`, containing the CodeMirror editor or * the Forge iframe) used to live inside the full-screen `#editor-modal` overlay, * which covered both the document read view and the AI assistant sidebar. * * This module moves that container **into the content area of the document * being read** so editing replaces the read view instead of overlaying it: * * `#content-area` (or the active pane `.pane-content` in split view) * └── .editor-container.editor-inline ← the editor, replacing the document * * Keeping the same DOM nodes (same ids: `#editor-body`, `#editor-save`, …) means * CodeMirror, autosave, Yjs collaboration, the AI toolbar and the Forge iframe * keep working untouched — only their host moves. * * Classes: * `.editor-inline` — set on the container while it hosts an inline session * `.editor-inline-host` — set on the content area it replaces * `#editor-modal.editor-inline-mode` — the overlay is neutralised (transparent, * zero-size, no scroll) but keeps `active`, so the mobile * ribbon anchored to it (`.me-ribbon`) still works. */ export const INLINE_CONTAINER_CLASS = 'editor-inline'; export const INLINE_HOST_CLASS = 'editor-inline-host'; export const INLINE_MODAL_CLASS = 'editor-inline-mode'; /** The editor overlay element (`#editor-modal` when present). */ export function getEditorModal() { return document.getElementById('editor-modal'); } /** * The editor container, wherever it currently lives (modal or content area). * Falls back to a class lookup so the helper also works on a DOM where the id * was not set (tests, older markup). */ export function getEditorContainer() { return ( document.getElementById('editor-container') || document.querySelector('.editor-container.' + INLINE_CONTAINER_CLASS) ); } /** * Content area that should host the inline editor: the pane currently being * read. Mirrors viewer.js `getContentArea()` (multi-pane aware). */ export function getInlineHostArea() { if (typeof window !== 'undefined') { if (window._activePaneContentArea) return window._activePaneContentArea; if ( window.PaneManager && typeof window.PaneManager.isSplit === 'function' && window.PaneManager.isSplit() && typeof window.getActiveContentArea === 'function' ) { return window.getActiveContentArea(); } } return document.getElementById('content-area'); } /** True when an inline edition session currently owns a content area. */ export function isInlineEditorActive() { const container = getEditorContainer(); return !!(container && container.classList.contains(INLINE_CONTAINER_CLASS)); } /** * Query an element inside the editor container, wherever it currently lives * (modal or content area). Use it instead of `modal.querySelector(...)` for the * header/footer/brand, which move with the container in inline mode (#93). */ export function queryEditor(selector) { const container = getEditorContainer(); const scope = container || getEditorModal(); return scope ? scope.querySelector(selector) : null; } /** * Move the editor container into `area`, replacing the rendered document. * @returns {boolean} true when the container was mounted inline. */ export function mountEditorInline(area) { const container = getEditorContainer(); const modal = getEditorModal(); if (!container || !modal || !area) return false; // Already hosted by this very area — nothing to move. if (container.parentElement === area) { container.classList.add(INLINE_CONTAINER_CLASS); area.classList.add(INLINE_HOST_CLASS); modal.classList.add(INLINE_MODAL_CLASS); return true; } area.classList.add(INLINE_HOST_CLASS); area.replaceChildren(container); container.classList.add(INLINE_CONTAINER_CLASS); modal.classList.add(INLINE_MODAL_CLASS); return true; } /** * Move the editor container back into the modal and clear the inline classes. * The host area is emptied (its read view is gone) and returned so the caller * can re-render the document. * @returns {HTMLElement|null} the content area that must be re-rendered. */ export function unmountEditorInline() { const container = getEditorContainer(); const modal = getEditorModal(); if (!container || !modal) return null; const parent = container.parentElement; const host = parent && parent !== modal ? parent : null; container.classList.remove(INLINE_CONTAINER_CLASS); if (host) { host.classList.remove(INLINE_HOST_CLASS); if (typeof host.replaceChildren === 'function') host.replaceChildren(); else host.innerHTML = ''; } modal.classList.remove(INLINE_MODAL_CLASS); modal.appendChild(container); return host; }