Complete workbench P0 resource tab cutover

This commit is contained in:
lix-2026
2026-05-20 20:13:24 +08:00
parent acf1305a7e
commit e0b0e70fb8
12 changed files with 1159 additions and 3 deletions
+48 -2
View File
@@ -3184,6 +3184,19 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
return 'file';
};
const resolveResourceOpen = (input = {}) => {
const kind = normalizeResourceTabKind(input);
const badgeKind = resourceTabBadgeKind(input, kind);
const rawTarget = String(input?.openTarget || '').trim().toLowerCase();
const openTarget = rawTarget === 'new-window' || rawTarget === 'side' ? rawTarget : 'active-tab';
const editable = kind === 'markdown' || kind === 'text' || kind === 'code';
const defaultOpenMode = kind === 'office' && openTarget === 'active-tab' ? 'active-tab-iframe' : openTarget;
const viewerUrl = kind === 'office'
? String(input?.officeUrl || input?.href || '').trim()
: String(input?.href || '').trim();
return { editorKind: kind, badgeKind, defaultOpenMode, editable, viewerUrl, openTarget };
};
const touchResourceTabMru = (key) => {
const id = String(key || '').trim();
if (!id) return;
@@ -3193,6 +3206,11 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
if (resourceTabMru.length > resourceTabMruMax) resourceTabMru.length = resourceTabMruMax;
};
const setResourceTabLastActive = (key) => {
const entry = resourceTabRegistry.get(String(key || '').trim());
if (entry?.session) entry.session.lastActiveAt = Date.now();
};
const removeFromResourceTabMru = (key) => {
const index = resourceTabMru.indexOf(key);
if (index >= 0) resourceTabMru.splice(index, 1);
@@ -3239,7 +3257,10 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
const activateMainEditorTab = (objectIdentity) => {
const nodes = resourceTabHostNodes();
const activeResource = String(objectIdentity || '').trim();
if (activeResource) touchResourceTabMru(activeResource);
if (activeResource) {
touchResourceTabMru(activeResource);
setResourceTabLastActive(activeResource);
}
if (nodes.pageTab instanceof HTMLElement) {
const activePage = !activeResource;
nodes.pageTab.classList.toggle('is-active', activePage);
@@ -3289,6 +3310,20 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
activateMainEditorTab(lastActiveResourceTabKey());
};
const markResourceTabError = (entry) => {
if (!entry) return;
entry.kind = 'error';
if (entry.tab instanceof HTMLElement) {
entry.tab.setAttribute('data-mnote-tab-kind', 'error');
entry.tab.setAttribute('data-mnote-tab-badge-kind', 'file');
entry.tab.classList.add('is-error');
}
if (entry.panel instanceof HTMLElement) {
entry.panel.setAttribute('data-resource-kind', 'error');
entry.panel.innerHTML = '<div class="mnote-resource-tab-error" data-resource-tab-error="true"><div class="mnote-resource-tab-error-inner"><h1>资源打开失败</h1><p>无法加载此资源,请检查文件路径和访问权限。</p></div></div>';
}
};
const createResourceTabDom = (input) => {
const nodes = resourceTabHostNodes();
if (!(nodes.strip instanceof HTMLElement) || !(nodes.panelRoot instanceof HTMLElement)) return null;
@@ -3356,6 +3391,7 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
fileVersion: conflictDetectionKey,
lastExternalConflictDetectionKey: conflictDetectionKey,
readOnly: false,
lastActiveAt: 0,
dirty: false,
saving: false,
hasExternalConflict: false,
@@ -3464,7 +3500,7 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
return true;
} catch (error) {
console.warn('mnote resource tab 打开失败', error);
entry.panel.innerHTML = '<div class="mnote-resource-tab-text-shell" data-resource-tab-error="true">资源打开失败</div>';
markResourceTabError(entry);
return false;
}
};
@@ -3530,6 +3566,7 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
if (url instanceof URL) replaceUrlState(url);
return true;
},
resolveResourceOpen: (input) => resolveResourceOpen(input),
openResourceInActiveTab: openResourceInActiveTab,
closeSecondaryDocument: ({ url } = {}) => {
closeSecondaryPane(url instanceof URL ? url : currentUrl());
@@ -4246,6 +4283,15 @@ mod tests {
"const tiptapDocument = pageBodyTiptapDocument(pageBody, '', runtimeDescriptor.bootstrap);"
));
assert!(!html.contains("mnote-web-document-shell"));
// Resource open resolver contract
assert!(html.contains("normalizeResourceTabKind"));
assert!(html.contains("data-resource-tab-error"));
assert!(html.contains("resourceTabCloseGuardReason"));
assert!(html.contains("closeResourceTab"));
assert!(html.contains("resourceTabRegistry.delete(key)"));
assert!(html.contains("activateMainEditorTab(lastActiveResourceTabKey())"));
assert!(html.contains("resourceTabBadgeKind(input, kind)"));
}
#[tokio::test]