refactor: split document pane runtime

This commit is contained in:
lix-2026
2026-05-26 00:47:51 +08:00
parent b4d7360cd7
commit 6197ca5d3a
5 changed files with 199 additions and 146 deletions
@@ -1,3 +1,14 @@
import {
buildPaneRuntime,
clearSecondaryParams,
createSecondaryPaneWidthController,
currentUrl,
installSecondaryPaneCloseButtons,
installSecondaryPaneOpenListeners,
pushUrlState,
replaceUrlState,
secondaryQueryParamNames,
} from './document-pane-runtime.js';
import {
conflictDetectionKeyBelongsToSession,
conflictDetectionKeyFromBody,
@@ -37,154 +48,15 @@ import {
const panesBootstrap = parseJsonScript(PANES_BOOTSTRAP_ID);
if (!panesBootstrap || !Array.isArray(panesBootstrap.panes)) return;
const paneRouteConfig = {
primary: {
sourceKindParam: 'sourceKind',
rootUriParam: 'rootUri',
secondary: false,
},
secondary: {
documentIdParam: 'secondaryDocumentId',
sourceKindParam: 'secondarySourceKind',
rootUriParam: 'secondaryRootUri',
secondary: true,
},
};
const secondaryQueryParamNames = [
paneRouteConfig.secondary.documentIdParam,
paneRouteConfig.secondary.sourceKindParam,
paneRouteConfig.secondary.rootUriParam,
];
const currentUrl = () => new URL(window.location.href);
const replaceUrlState = (url) => {
window.history.replaceState({}, '', url.pathname + url.search + url.hash);
};
const pushUrlState = (url) => {
window.history.pushState({}, '', url.pathname + url.search + url.hash);
};
const clearSecondaryParams = () => {
const url = currentUrl();
secondaryQueryParamNames.forEach((name) => url.searchParams.delete(name));
replaceUrlState(url);
};
if (panesBootstrap.secondaryInvalid === true) clearSecondaryParams();
const secondaryUrlForDocument = (documentId, detail = {}) => {
const url = currentUrl();
url.searchParams.set(paneRouteConfig.secondary.documentIdParam, documentId);
const detailSourceKind = typeof detail.sourceKind === 'string' ? detail.sourceKind.trim() : '';
const detailRootUri = typeof detail.rootUri === 'string' ? detail.rootUri.trim() : '';
const primarySourceKind = (url.searchParams.get(paneRouteConfig.primary.sourceKindParam) || '').trim();
const primaryRootUri = (url.searchParams.get(paneRouteConfig.primary.rootUriParam) || '').trim();
const secondarySourceKind = detailSourceKind || primarySourceKind;
const secondaryRootUri = detailRootUri || primaryRootUri;
if (secondarySourceKind) url.searchParams.set(paneRouteConfig.secondary.sourceKindParam, secondarySourceKind);
else url.searchParams.delete(paneRouteConfig.secondary.sourceKindParam);
if (secondaryRootUri) url.searchParams.set(paneRouteConfig.secondary.rootUriParam, secondaryRootUri);
else url.searchParams.delete(paneRouteConfig.secondary.rootUriParam);
return { url, sourceKind: secondarySourceKind, rootUri: secondaryRootUri };
};
const openDocumentInSecondaryPane = (documentId, detail = {}) => {
const id = typeof documentId === 'string' ? documentId.trim() : '';
if (!id) return false;
const target = secondaryUrlForDocument(id, detail);
if (typeof window.__mnoteDocumentPaneRuntime?.openSecondaryDocument === 'function') {
void window.__mnoteDocumentPaneRuntime.openSecondaryDocument({
documentId: id,
workspaceId: typeof detail.workspaceId === 'string' ? detail.workspaceId.trim() : '',
sourceKind: target.sourceKind || null,
rootUri: target.rootUri || null,
url: target.url,
});
return true;
}
window.location.assign(target.url.pathname + target.url.search + target.url.hash);
return true;
};
window.addEventListener('tree.page.open-right', (event) => {
const detail = event?.detail && typeof event.detail === 'object' ? event.detail : {};
const documentId = typeof detail.documentId === 'string' ? detail.documentId.trim() : '';
openDocumentInSecondaryPane(documentId, detail);
});
window.addEventListener('tree.page.open', (event) => {
const detail = event?.detail && typeof event.detail === 'object' ? event.detail : {};
const openTarget = typeof detail.openTarget === 'string' ? detail.openTarget.trim().toLowerCase() : '';
if (openTarget !== 'side') return;
const documentId = typeof detail.documentId === 'string' ? detail.documentId.trim() : '';
openDocumentInSecondaryPane(documentId, detail);
});
document.querySelectorAll('[data-mnote-pane-close="secondary"]').forEach((button) => {
button.addEventListener('click', (event) => {
event.preventDefault();
const url = currentUrl();
secondaryQueryParamNames.forEach((name) => url.searchParams.delete(name));
if (typeof window.__mnoteDocumentPaneRuntime?.closeSecondaryDocument === 'function') {
window.__mnoteDocumentPaneRuntime.closeSecondaryDocument({ url });
return;
}
window.location.assign(url.pathname + url.search + url.hash);
});
});
const workspace = document.querySelector('[data-testid="mnote-document-workspace"]');
const resizer = document.querySelector('[data-document-pane-resizer="true"]');
const SECONDARY_WIDTH_KEY = 'mnote.document.secondary.width';
const applyStoredSecondaryWidth = () => {
if (!(workspace instanceof HTMLElement)) return;
if (workspace.getAttribute('data-has-secondary-pane') !== 'true') {
workspace.style.removeProperty('grid-template-columns');
return;
}
try {
const raw = window.localStorage ? window.localStorage.getItem(SECONDARY_WIDTH_KEY) : '';
const width = Number(raw || 0);
if (Number.isFinite(width) && width >= 320) {
workspace.style.gridTemplateColumns = `minmax(0, 1fr) 6px minmax(320px, ${Math.round(width)}px)`;
}
} catch (_) {}
};
installSecondaryPaneOpenListeners();
installSecondaryPaneCloseButtons();
const { applyStoredSecondaryWidth } = createSecondaryPaneWidthController();
applyStoredSecondaryWidth();
if (workspace instanceof HTMLElement && resizer instanceof HTMLElement) {
resizer.addEventListener('pointerdown', (event) => {
if (workspace.getAttribute('data-has-secondary-pane') !== 'true') return;
event.preventDefault();
const move = (nextEvent) => {
const rect = workspace.getBoundingClientRect();
const width = Math.min(Math.max(320, rect.right - nextEvent.clientX), Math.max(420, rect.width - 360));
workspace.style.gridTemplateColumns = `minmax(0, 1fr) 6px minmax(320px, ${Math.round(width)}px)`;
try {
if (window.localStorage) window.localStorage.setItem(SECONDARY_WIDTH_KEY, String(Math.round(width)));
} catch (_) {}
};
const up = () => {
window.removeEventListener('pointermove', move);
window.removeEventListener('pointerup', up);
};
window.addEventListener('pointermove', move);
window.addEventListener('pointerup', up);
});
}
const buildPaneRuntime = (paneDescriptor) => {
const paneRole = typeof paneDescriptor?.role === 'string' ? paneDescriptor.role : 'primary';
const paneRoot = document.querySelector(`${ROOT_SELECTOR}[data-pane-role="${paneRole}"]`);
const observability = document.querySelector(`[data-editor-host-observability][data-pane-role="${paneRole}"]`);
if (!(paneRoot instanceof HTMLElement)) return null;
if (!paneDescriptor?.aggregate || !paneDescriptor?.bootstrap) return null;
return {
paneRole,
root: paneRoot,
observability,
aggregate: paneDescriptor.aggregate,
bootstrap: paneDescriptor.bootstrap,
};
};
const paneRuntimes = panesBootstrap.panes.map(buildPaneRuntime).filter(Boolean);
const paneRuntimes = panesBootstrap.panes
.map((paneDescriptor) => buildPaneRuntime(paneDescriptor, ROOT_SELECTOR))
.filter(Boolean);
const loadRuntime = async () => {
if (window.__mnoteLeptosTiptapRuntimePromise) {