收口 MNote P0 P1 P2 审查尾项
- 归档 OnlyOffice live bridge、Page AI、mindmap、design governance 与相关 bug 条目 - 补齐 MinerU OCR 后端 runtime 合同与 smoke/test 基线 - 收口 ChatOnly/Doubao、ObjectIdentity、Page Aggregate compat 与 runtime owner 文档口径 验证: - cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1 - cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_bridge -- --test-threads=1 - git diff --check - git diff --cached --check - codegraph index . --force && codegraph status . - codegraph sync . && codegraph status .
This commit is contained in:
@@ -64,6 +64,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
const resourceTabMru = { primary: [], secondary: [] };
|
||||
const resourceTabMruMax = 20;
|
||||
const resourceTabCloseGuardAttribute = 'data-resource-tab-close-guarded';
|
||||
let onlyofficeBridgeReadyListenerBound = false;
|
||||
|
||||
const normalizePaneRole = (paneRole) => String(paneRole || '').trim() === 'secondary' ? 'secondary' : 'primary';
|
||||
|
||||
@@ -127,6 +128,30 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
}
|
||||
};
|
||||
|
||||
const currentFileTreeWorkspacePath = () => {
|
||||
try {
|
||||
const row = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-local-relative-path]');
|
||||
const reader = window.__mnoteFileTreeRuntime?.readWorkspacePathFromRow;
|
||||
if (row instanceof HTMLElement && typeof reader === 'function') {
|
||||
const workspacePath = reader(row);
|
||||
if (workspacePath && typeof workspacePath === 'object') {
|
||||
return {
|
||||
...workspacePath,
|
||||
sourceKind: String(workspacePath.sourceKind || row.getAttribute('data-source-kind') || '').trim(),
|
||||
rootUri: String(workspacePath.rootUri || row.getAttribute('data-root-uri') || '').trim(),
|
||||
};
|
||||
}
|
||||
}
|
||||
if (row instanceof HTMLElement) {
|
||||
return {
|
||||
sourceKind: String(row.getAttribute('data-source-kind') || '').trim(),
|
||||
rootUri: String(row.getAttribute('data-root-uri') || '').trim(),
|
||||
};
|
||||
}
|
||||
} catch (_) {}
|
||||
return null;
|
||||
};
|
||||
|
||||
const currentWebShellDocumentId = () => {
|
||||
const explicit = typeof currentDocumentId === 'function' ? String(currentDocumentId() || '').trim() : '';
|
||||
if (explicit) return explicit;
|
||||
@@ -157,17 +182,23 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
|
||||
const currentWebShellSourceKind = () => {
|
||||
try {
|
||||
return currentUrl().searchParams.get('sourceKind') || '';
|
||||
return currentUrl().searchParams.get('sourceKind')
|
||||
|| document.body?.dataset?.mnoteSourceKind
|
||||
|| currentFileTreeWorkspacePath()?.sourceKind
|
||||
|| '';
|
||||
} catch (_) {
|
||||
return '';
|
||||
return document.body?.dataset?.mnoteSourceKind || currentFileTreeWorkspacePath()?.sourceKind || '';
|
||||
}
|
||||
};
|
||||
|
||||
const currentWebShellRootUri = () => {
|
||||
try {
|
||||
return currentUrl().searchParams.get('rootUri') || '';
|
||||
return currentUrl().searchParams.get('rootUri')
|
||||
|| document.body?.dataset?.mnoteRootUri
|
||||
|| currentFileTreeWorkspacePath()?.rootUri
|
||||
|| '';
|
||||
} catch (_) {
|
||||
return '';
|
||||
return document.body?.dataset?.mnoteRootUri || currentFileTreeWorkspacePath()?.rootUri || '';
|
||||
}
|
||||
};
|
||||
|
||||
@@ -196,7 +227,9 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
resourceKind: String(fromInput.resourceKind || fromInput.objectKind || resourceKind || '').trim(),
|
||||
};
|
||||
}
|
||||
const id = String(objectIdentity || documentId || assetId || relativePath || '').trim();
|
||||
const structuredObjectIdentity = objectIdentity && typeof objectIdentity === 'object' ? objectIdentity : null;
|
||||
const objectIdentityText = structuredObjectIdentity ? '' : String(objectIdentity || '').trim();
|
||||
const id = String(objectIdentityText || documentId || assetId || relativePath || '').trim();
|
||||
if (!id) return null;
|
||||
return {
|
||||
schema: 'mnote.workspace_path.v1',
|
||||
@@ -205,7 +238,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
rootUri: String(rootUri || '').trim(),
|
||||
relativePath: String(relativePath || '').trim(),
|
||||
documentId: String(documentId || '').trim(),
|
||||
objectIdentity: String(objectIdentity || '').trim(),
|
||||
objectIdentity: structuredObjectIdentity || objectIdentityText,
|
||||
assetId: String(assetId || '').trim(),
|
||||
resourceKind: String(resourceKind || '').trim(),
|
||||
};
|
||||
@@ -371,6 +404,18 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
});
|
||||
};
|
||||
|
||||
const officeBridgeDebugForEntry = (entry) => {
|
||||
if (!entry?.panel || !(entry.panel instanceof HTMLElement)) return null;
|
||||
const frame = entry.panel.querySelector('iframe.mnote-resource-tab-frame');
|
||||
if (!(frame instanceof HTMLIFrameElement)) return null;
|
||||
try {
|
||||
const debug = frame.contentWindow?.__MNOTE_ONLYOFFICE_DEBUG__;
|
||||
return debug && typeof debug === 'object' ? debug : null;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const openEditorsSnapshotEntry = (entry, key, generatedAt = Date.now()) => {
|
||||
const active = entry?.tab instanceof HTMLElement
|
||||
? entry.tab.getAttribute('aria-selected') === 'true'
|
||||
@@ -384,6 +429,13 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
const assetId = String(entry?.assetId || entry?.session?.assetId || '').trim();
|
||||
const kind = normalizeResourceTabKind(entry);
|
||||
const dirtyState = resourceTabCloseGuardReason(entry?.session);
|
||||
const officeBridgeDebug = kind === 'office' ? officeBridgeDebugForEntry(entry) : null;
|
||||
const onlyofficeSessionId = String(
|
||||
officeBridgeDebug?.bridgeSessionId
|
||||
|| entry?.onlyofficeSessionId
|
||||
|| entry?.bridgeSessionId
|
||||
|| '',
|
||||
).trim();
|
||||
return {
|
||||
objectIdentity,
|
||||
workspacePath: buildWorkspacePath({
|
||||
@@ -411,6 +463,11 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
dirtyGuard: dirtyState,
|
||||
assetId,
|
||||
path: relativePath,
|
||||
onlyofficeSessionId,
|
||||
bridgeSessionId: onlyofficeSessionId,
|
||||
bridgeSessionReady: Boolean(onlyofficeSessionId),
|
||||
bridgeDocumentId: String(officeBridgeDebug?.documentId || '').trim(),
|
||||
bridgeAssetId: String(officeBridgeDebug?.assetId || '').trim(),
|
||||
lastActiveAt: Number(entry?.session?.lastActiveAt || entry?.lastActiveAt || 0) || (active ? generatedAt : 0),
|
||||
preview: false,
|
||||
pinned: false,
|
||||
@@ -427,6 +484,12 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
const rootUri = currentWebShellRootUri();
|
||||
const relativePath = sourceKind === 'local_folder' ? localMarkdownRelativePathFromDocumentId(documentId) : '';
|
||||
const objectIdentity = `page:${paneRole}`;
|
||||
const workspaceObjectIdentity = {
|
||||
objectKind: 'page',
|
||||
documentId,
|
||||
blockId: null,
|
||||
assetId: null,
|
||||
};
|
||||
const active = nodes.pageTab instanceof HTMLElement
|
||||
? nodes.pageTab.getAttribute('aria-selected') === 'true'
|
||||
: false;
|
||||
@@ -443,7 +506,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
rootUri,
|
||||
relativePath,
|
||||
documentId,
|
||||
objectIdentity,
|
||||
objectIdentity: workspaceObjectIdentity,
|
||||
assetId: '',
|
||||
resourceKind: 'page',
|
||||
}),
|
||||
@@ -1035,6 +1098,17 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
markIntendedSlashRoot(entry);
|
||||
};
|
||||
|
||||
const ensureOnlyofficeBridgeReadyListener = () => {
|
||||
if (onlyofficeBridgeReadyListenerBound) return;
|
||||
onlyofficeBridgeReadyListenerBound = true;
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.origin !== window.location.origin) return;
|
||||
const detail = event.data && typeof event.data === 'object' ? event.data : null;
|
||||
if (!detail || detail.type !== 'mnote:onlyoffice-bridge-ready') return;
|
||||
syncOpenEditorsSnapshot();
|
||||
});
|
||||
};
|
||||
|
||||
const openPassiveResourceTab = (entry, input) => {
|
||||
const href = String(input.officeUrl || input.href || '').trim();
|
||||
if (entry.kind === 'image') {
|
||||
@@ -1051,6 +1125,12 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
const frame = entry.panel.querySelector('iframe');
|
||||
if (frame instanceof HTMLIFrameElement) {
|
||||
frame.title = entry.title;
|
||||
if (entry.kind === 'office') {
|
||||
ensureOnlyofficeBridgeReadyListener();
|
||||
frame.addEventListener('load', () => {
|
||||
syncOpenEditorsSnapshot();
|
||||
}, { once: true });
|
||||
}
|
||||
frame.src = href;
|
||||
}
|
||||
installPassiveResourceWatch(entry);
|
||||
|
||||
Reference in New Issue
Block a user