Close workbench P0 resource lifecycle gaps

This commit is contained in:
lix-2026
2026-05-20 20:48:18 +08:00
parent e0b0e70fb8
commit fe13444dbc
14 changed files with 610 additions and 7 deletions
+11
View File
@@ -3742,6 +3742,17 @@ fn build_tree_shell_html(
.map((entry) => entry.nodeId),
});
const buildFileTreeRuntimeEnvironment = () => ({
visibleRowIds: visibleFileTreeRowIds,
rows: Array.from(fileTreeRowById.values()).map((entry) => ({
rowId: entry.rowId,
rowKind: entry.rowKind,
documentId: entry.documentId || null,
assetId: entry.assetId || null,
})),
rootUri: sourceKind === "local_folder" ? rootUri || null : null,
});
const readPageRuntimeState = (action, item) => {
const actionKind = normalizeText(action?.kind).toLowerCase();
const nodeId = normalizeText(action?.nodeId || item?.nodeId);
+89 -1
View File
@@ -3254,6 +3254,81 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
});
};
const showResourceTabCloseGuardNotice = (entry, reason) => {
const nodes = resourceTabHostNodes();
const messages = {
dirty: '当前资源有未保存的修改,保存完成后再关闭。',
saving: '当前资源正在保存中,请稍后再关闭。',
hasExternalConflict: '当前资源存在外部冲突,请先处理冲突。',
};
const message = messages[reason] || '当前资源暂时无法关闭。';
let notice = document.getElementById('mnote-resource-close-guard-notice');
if (!notice) {
notice = document.createElement('div');
notice.id = 'mnote-resource-close-guard-notice';
notice.className = 'mnote-close-guard-notice';
notice.setAttribute('role', 'status');
notice.setAttribute('aria-live', 'polite');
notice.setAttribute('data-mnote-resource-close-guard', '');
const parent = nodes.strip?.parentNode;
if (parent instanceof HTMLElement) {
const panels = parent.querySelector('.mnote-main-tab-panels');
if (panels && panels.parentNode === parent) parent.insertBefore(notice, panels);
else parent.append(notice);
}
}
notice.textContent = `${entry?.title || '资源'}${message}`;
notice.setAttribute('data-mnote-resource-close-guard', reason || 'blocked');
notice.className = `mnote-close-guard-notice is-${reason || 'blocked'}`;
if (notice._mnoteHideTimer) window.clearTimeout(notice._mnoteHideTimer);
notice._mnoteHideTimer = window.setTimeout(() => {
notice.classList.add('is-hiding');
window.setTimeout(() => {
if (notice.parentNode) notice.remove();
}, 260);
}, 4000);
};
const cssSafe = (value) => {
const text = String(value || '');
if (window.CSS && typeof window.CSS.escape === 'function') return window.CSS.escape(text);
return text.replace(/["\\]/g, '\\$&');
};
const syncActiveResourceFileTreeRow = (activeResource) => {
document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-active="true"]').forEach((row) => {
if (row instanceof HTMLElement) row.setAttribute('data-active', 'false');
});
const entry = activeResource ? resourceTabRegistry.get(activeResource) : null;
if (!entry) return;
const assetId = String(entry.assetId || entry.session?.assetId || '').trim();
const path = String(entry.path || entry.session?.resourcePath || '').trim();
const identity = String(entry.objectIdentity || activeResource || '').trim();
if (assetId) {
const row = document.querySelector(`#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-asset-id="${cssSafe(assetId)}"]`);
if (row instanceof HTMLElement) {
row.setAttribute('data-active', 'true');
return;
}
}
const rows = document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-object-identity]');
for (const row of rows) {
if (!(row instanceof HTMLElement)) continue;
const objectIdentity = row.getAttribute('data-object-identity') || '';
if ((identity && objectIdentity.includes(identity)) || (path && objectIdentity.includes(path))) {
row.setAttribute('data-active', 'true');
return;
}
}
};
const syncActiveResourceUrlState = (activeResource) => {
const url = currentUrl();
if (activeResource) url.searchParams.set('resourceTab', activeResource);
else url.searchParams.delete('resourceTab');
replaceUrlState(url);
};
const activateMainEditorTab = (objectIdentity) => {
const nodes = resourceTabHostNodes();
const activeResource = String(objectIdentity || '').trim();
@@ -3279,6 +3354,8 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
if (entry.panel instanceof HTMLElement) entry.panel.hidden = !active;
syncResourceTabCloseGuard(entry);
});
syncActiveResourceFileTreeRow(activeResource);
syncActiveResourceUrlState(activeResource);
};
const bindMainEditorPageTab = () => {
@@ -3300,6 +3377,7 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
if (guardReason) {
console.warn(`mnote resource tab 关闭阻止: ${entry.title} (${guardReason})`);
syncResourceTabCloseGuard(entry);
showResourceTabCloseGuardNotice(entry, guardReason);
return;
}
removeFromResourceTabMru(key);
@@ -3358,7 +3436,17 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
panel.hidden = true;
nodes.strip.append(tab);
nodes.panelRoot.append(panel);
return { objectIdentity, title, kind, tab, panel, view: null, session: null };
return {
objectIdentity,
title,
kind,
tab,
panel,
view: null,
session: null,
assetId: String(input.assetId || '').trim(),
path: String(input.path || '').trim(),
};
};
const localResourceReadUrl = (rootUri, path) => {