export const createMindmapHostRuntime = ({ loadRuntime, paneViewRegistry, unmountEditorViewBinding, pushUrlState, rootSelector, currentDocumentId, } = {}) => { const mindmapPaneViewRegistry = new Map(); const selector = rootSelector || '[data-testid="mnote-leptos-tiptap-island-editor-root"]'; const unmountMindmapPane = (paneRole) => { const view = mindmapPaneViewRegistry.get(paneRole); if (!view) return; mindmapPaneViewRegistry.delete(paneRole); if (view.mountId != null && view.runtime && typeof view.runtime.unmount === 'function') { try { view.runtime.unmount(view.mountId); } catch (error) { console.warn('mnote mindmap pane unmount failed', error); } } if (view.root instanceof HTMLElement) { view.root.removeAttribute('data-runtime-mount-id'); view.root.removeAttribute('data-mnote-object-editor'); view.root.removeAttribute('data-mnote-object-identity'); view.root.removeAttribute('data-mnote-mindmap-id'); view.root.replaceChildren(); } }; const parseJsonScriptFromDocument = (doc, id) => { const node = doc?.getElementById?.(id); if (!node) return null; try { return JSON.parse(node.textContent || 'null'); } catch (error) { console.warn(`mnote mindmap shell JSON 解析失败: ${id}`, error); return null; } }; const fetchMindmapShellBootstrap = async (url) => { const response = await fetch(url.toString(), { cache: 'no-store', credentials: 'include', headers: { accept: 'text/html' }, }); if (!response.ok) throw new Error(`mindmap_shell_failed_${response.status}`); const html = await response.text(); const parsed = new DOMParser().parseFromString(html, 'text/html'); const bootstrap = parseJsonScriptFromDocument(parsed, '__MNOTE_MINDMAP_EDITOR_BOOTSTRAP__'); if (!bootstrap || typeof bootstrap !== 'object') throw new Error('mindmap_shell_missing_bootstrap'); const contract = parseJsonScriptFromDocument(parsed, '__MNOTE_MINDMAP_SHELL__') || {}; const title = String(bootstrap.title || parsed.querySelector('title')?.textContent || '思维导图').trim() || '思维导图'; return { bootstrap, contract, title }; }; const setPrimaryMindmapSelection = (documentId, mindmapId) => { const cssEscape = window.CSS && typeof window.CSS.escape === 'function' ? window.CSS.escape : (value) => String(value).replace(/["\\]/g, '\\$&'); const escapedDocId = cssEscape(documentId); const escapedMindmapId = cssEscape(mindmapId); document.querySelectorAll('.tree-row[data-active="true"], .tree-row[data-selected="true"]').forEach((row) => { if (!(row instanceof HTMLElement)) return; row.setAttribute('data-active', 'false'); row.setAttribute('data-selected', 'false'); }); document.querySelectorAll(`.tree-row[data-shell-mode="page"][data-node-id="${escapedDocId}"]`).forEach((row) => { if (row instanceof HTMLElement) row.setAttribute('data-active', 'true'); }); document.querySelectorAll(`.tree-row[data-shell-mode="filetree"][data-asset-id="${escapedMindmapId}"]`).forEach((row) => { if (row instanceof HTMLElement) row.setAttribute('data-selected', 'true'); }); }; const updatePrimaryMindmapChrome = ({ documentId, mindmapId, workspaceId, title, root }) => { const pane = root.closest('[data-document-pane="true"]'); if (pane instanceof HTMLElement) { pane.setAttribute('data-pane-document-id', `__mindmap_object__:${documentId}:${mindmapId}`); pane.setAttribute('data-pane-workspace-id', workspaceId || ''); pane.setAttribute('data-pane-visible', 'true'); pane.hidden = false; } const shell = root.closest('.document-shell'); if (shell instanceof HTMLElement) { shell.setAttribute('data-editor-host', 'mindmap_object'); shell.setAttribute('data-document-id', documentId); shell.setAttribute('data-workspace-id', workspaceId || ''); shell.setAttribute('data-mindmap-id', mindmapId); } document.body.dataset.documentId = documentId; document.body.dataset.mindmapId = mindmapId; document.body.dataset.mnoteShell = 'mindmap'; document.title = title; document.querySelectorAll('[data-page-title-input="true"][data-pane-role="primary"]').forEach((node) => { if (!(node instanceof HTMLTextAreaElement)) return; node.value = title; node.setAttribute('data-document-id', documentId); node.setAttribute('data-workspace-id', workspaceId || ''); node.setAttribute('data-title-last-saved', title); node.setAttribute('data-title-save-status', 'saved'); node.style.height = 'auto'; node.style.height = `${Math.max(48, node.scrollHeight)}px`; }); document.querySelectorAll('[data-page-title-current="true"]').forEach((node) => { if (node instanceof HTMLElement) node.textContent = title; }); const topbarCurrent = document.querySelector('.wolai-breadcrumb-current [data-page-title-current]'); if (topbarCurrent instanceof HTMLElement) topbarCurrent.textContent = title; root.setAttribute('data-mnote-object-editor', 'mindmap'); root.setAttribute('data-mnote-object-identity', `resource:mindmap:${documentId}:${mindmapId}`); root.setAttribute('data-mnote-mindmap-id', mindmapId); setPrimaryMindmapSelection(documentId, mindmapId); }; const replacePrimaryPaneMindmap = async ({ documentId, mindmapId, workspaceId, url }) => { const targetUrl = url instanceof URL ? url : new URL(`/mindmap/${encodeURIComponent(documentId)}/${encodeURIComponent(mindmapId)}`, window.location.origin); const root = document.querySelector(`${selector}[data-pane-role="primary"]`); const observability = document.querySelector('[data-editor-host-observability][data-pane-role="primary"]'); if (!(root instanceof HTMLElement)) throw new Error('primary_pane_root_missing'); const runtime = await loadRuntime(); const { bootstrap, title } = await fetchMindmapShellBootstrap(targetUrl); const previousView = paneViewRegistry?.get?.('primary'); if (previousView) { unmountEditorViewBinding(previousView); paneViewRegistry.delete('primary'); } unmountMindmapPane('primary'); if (observability instanceof HTMLElement) { observability.setAttribute('data-editor-host-active', 'mindmap_object'); observability.setAttribute('data-editor-host-status', 'mounting'); } root.replaceChildren(); root.setAttribute('data-runtime-editor-status', 'booting'); updatePrimaryMindmapChrome({ documentId, mindmapId, workspaceId, title, root }); const mountId = runtime.mount(root, bootstrap); root.setAttribute('data-runtime-mount-id', String(mountId)); root.setAttribute('data-editor-host-kind', 'mindmap_object'); if (observability instanceof HTMLElement) { observability.setAttribute('data-editor-host-status', 'mounted'); } mindmapPaneViewRegistry.set('primary', { paneRole: 'primary', root, runtime, mountId }); pushUrlState(targetUrl); return true; }; const openMindmapResourceTab = async (entry, input) => { const documentId = String(input.documentId || currentDocumentId?.() || '').trim(); const mindmapId = String(input.mindmapId || input.assetId || '').trim(); if (!documentId || !mindmapId) throw new Error('mindmap_resource_identity_missing'); const targetUrl = new URL(`/mindmap/${encodeURIComponent(documentId)}/${encodeURIComponent(mindmapId)}`, window.location.origin); const runtime = await loadRuntime(); const { bootstrap, title } = await fetchMindmapShellBootstrap(targetUrl); entry.title = String(input.title || title || '思维导图').trim() || '思维导图'; const titleNode = entry.tab?.querySelector?.('.mnote-main-tab-title'); if (titleNode) titleNode.textContent = entry.title; entry.panel.innerHTML = '
'; entry.panel.setAttribute('data-mnote-object-editor', 'mindmap'); entry.panel.setAttribute('data-mnote-object-identity', entry.objectIdentity); entry.panel.setAttribute('data-mnote-mindmap-id', mindmapId); const root = entry.panel.querySelector('[data-testid="mnote-mindmap-editor-root"]'); if (!(root instanceof HTMLElement)) throw new Error('mindmap_resource_root_missing'); root.setAttribute('data-mnote-object-editor', 'mindmap'); root.setAttribute('data-mnote-object-identity', entry.objectIdentity); root.setAttribute('data-mnote-mindmap-id', mindmapId); root.setAttribute('data-document-id', documentId); const mountId = runtime.mount(root, bootstrap); root.setAttribute('data-runtime-mount-id', String(mountId)); root.setAttribute('data-editor-host-kind', 'mindmap_resource_tab'); root.setAttribute('data-runtime-editor-status', 'mounted'); entry.view = null; entry.session = null; entry.mindmapRuntime = { runtime, mountId, root }; }; return { fetchMindmapShellBootstrap, openMindmapResourceTab, replacePrimaryPaneMindmap, unmountMindmapPane, }; };