Align resource tabs with workbench review
This commit is contained in:
@@ -1393,6 +1393,9 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
const paneViewRegistry = new Map();
|
||||
const mindmapPaneViewRegistry = new Map();
|
||||
const resourceTabRegistry = new Map();
|
||||
const resourceTabMru = [];
|
||||
const resourceTabMruMax = 20;
|
||||
const resourceTabCloseGuardAttribute = 'data-resource-tab-close-guarded';
|
||||
let nextViewId = 1;
|
||||
const externalConflictMessage = '本地 Markdown 文件已在外部更新,请刷新或保存前先处理冲突';
|
||||
const treeExternalConflictMessage = '当前页面已在其它窗口更新,请刷新或保存前先处理冲突';
|
||||
@@ -2137,9 +2140,11 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
setSessionStatus(session, 'dirty');
|
||||
queueSessionSave(session);
|
||||
}
|
||||
syncResourceSessionTabGuards(session);
|
||||
} catch (error) {
|
||||
session.saving = false;
|
||||
setSessionStatus(session, 'error', error instanceof Error ? error.message : String(error));
|
||||
syncResourceSessionTabGuards(session);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2868,6 +2873,7 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
}
|
||||
}
|
||||
session.dirty = session.currentSerialized !== session.lastPersistedSerialized;
|
||||
syncResourceSessionTabGuards(session);
|
||||
if (serialized !== previousSerialized) {
|
||||
broadcastSessionContent(session, view);
|
||||
}
|
||||
@@ -3151,7 +3157,7 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
if (/\.(xls|xlsx|ods|csv)$/i.test(title)) return 'sheet';
|
||||
return 'word';
|
||||
}
|
||||
if (kind === 'pdf') return 'ppt';
|
||||
if (kind === 'pdf') return 'pdf';
|
||||
if (kind === 'markdown' || kind === 'text' || kind === 'code') return 'code';
|
||||
if (kind === 'image') return 'image';
|
||||
return 'file';
|
||||
@@ -3178,13 +3184,67 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
return 'file';
|
||||
};
|
||||
|
||||
const touchResourceTabMru = (key) => {
|
||||
const id = String(key || '').trim();
|
||||
if (!id) return;
|
||||
const index = resourceTabMru.indexOf(id);
|
||||
if (index >= 0) resourceTabMru.splice(index, 1);
|
||||
resourceTabMru.unshift(id);
|
||||
if (resourceTabMru.length > resourceTabMruMax) resourceTabMru.length = resourceTabMruMax;
|
||||
};
|
||||
|
||||
const removeFromResourceTabMru = (key) => {
|
||||
const index = resourceTabMru.indexOf(key);
|
||||
if (index >= 0) resourceTabMru.splice(index, 1);
|
||||
};
|
||||
|
||||
const lastActiveResourceTabKey = () => {
|
||||
for (const key of resourceTabMru) {
|
||||
if (resourceTabRegistry.has(key)) return key;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
const resourceTabCloseGuardReason = (session) => {
|
||||
if (!session) return '';
|
||||
const hasUnsavedChanges = session.dirty
|
||||
|| Boolean(session.saveTimer)
|
||||
|| sessionHasRecentLocalInput(session)
|
||||
|| (session.currentSerialized && session.currentSerialized !== session.lastPersistedSerialized);
|
||||
if (hasUnsavedChanges) return 'dirty';
|
||||
if (session.saving) return 'saving';
|
||||
if (session.hasExternalConflict) return 'hasExternalConflict';
|
||||
return '';
|
||||
};
|
||||
|
||||
const syncResourceTabCloseGuard = (entry) => {
|
||||
if (!entry?.tab || !(entry.tab instanceof HTMLElement)) return;
|
||||
const reason = resourceTabCloseGuardReason(entry.session);
|
||||
if (reason) {
|
||||
entry.tab.setAttribute(resourceTabCloseGuardAttribute, reason);
|
||||
entry.tab.classList.add('is-close-guarded');
|
||||
} else {
|
||||
entry.tab.removeAttribute(resourceTabCloseGuardAttribute);
|
||||
entry.tab.classList.remove('is-close-guarded');
|
||||
}
|
||||
};
|
||||
|
||||
const syncResourceSessionTabGuards = (session) => {
|
||||
if (!session || session.sessionKind !== 'resource') return;
|
||||
resourceTabRegistry.forEach((entry) => {
|
||||
if (entry.session === session) syncResourceTabCloseGuard(entry);
|
||||
});
|
||||
};
|
||||
|
||||
const activateMainEditorTab = (objectIdentity) => {
|
||||
const nodes = resourceTabHostNodes();
|
||||
const activeResource = String(objectIdentity || '').trim();
|
||||
if (activeResource) touchResourceTabMru(activeResource);
|
||||
if (nodes.pageTab instanceof HTMLElement) {
|
||||
const activePage = !activeResource;
|
||||
nodes.pageTab.classList.toggle('is-active', activePage);
|
||||
nodes.pageTab.setAttribute('aria-selected', activePage ? 'true' : 'false');
|
||||
nodes.pageTab.setAttribute('tabindex', activePage ? '0' : '-1');
|
||||
}
|
||||
if (nodes.pagePanel instanceof HTMLElement) nodes.pagePanel.hidden = Boolean(activeResource);
|
||||
if (nodes.host instanceof HTMLElement) nodes.host.hidden = !activeResource;
|
||||
@@ -3193,8 +3253,10 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
if (entry.tab instanceof HTMLElement) {
|
||||
entry.tab.classList.toggle('is-active', active);
|
||||
entry.tab.setAttribute('aria-selected', active ? 'true' : 'false');
|
||||
entry.tab.setAttribute('tabindex', active ? '0' : '-1');
|
||||
}
|
||||
if (entry.panel instanceof HTMLElement) entry.panel.hidden = !active;
|
||||
syncResourceTabCloseGuard(entry);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3213,11 +3275,18 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
const key = String(objectIdentity || '').trim();
|
||||
const entry = resourceTabRegistry.get(key);
|
||||
if (!entry) return;
|
||||
const guardReason = resourceTabCloseGuardReason(entry.session);
|
||||
if (guardReason) {
|
||||
console.warn(`mnote resource tab 关闭阻止: ${entry.title} (${guardReason})`);
|
||||
syncResourceTabCloseGuard(entry);
|
||||
return;
|
||||
}
|
||||
removeFromResourceTabMru(key);
|
||||
if (entry.view) unmountEditorViewBinding(entry.view, { releaseSession: true });
|
||||
if (entry.tab instanceof HTMLElement) entry.tab.remove();
|
||||
if (entry.panel instanceof HTMLElement) entry.panel.remove();
|
||||
resourceTabRegistry.delete(key);
|
||||
activateMainEditorTab('');
|
||||
activateMainEditorTab(lastActiveResourceTabKey());
|
||||
};
|
||||
|
||||
const createResourceTabDom = (input) => {
|
||||
@@ -3233,6 +3302,7 @@ pub(crate) fn render_editor_island_adapter_script() -> &'static str {
|
||||
tab.setAttribute('data-mnote-main-tab', objectIdentity);
|
||||
tab.setAttribute('data-mnote-tab-kind', kind);
|
||||
tab.setAttribute('data-mnote-tab-badge-kind', resourceTabBadgeKind(input, kind));
|
||||
tab.setAttribute('tabindex', '-1');
|
||||
tab.innerHTML = '<span class="mnote-main-tab-badge" aria-hidden="true"></span><span class="mnote-main-tab-title"></span><span class="mnote-main-tab-close" role="button" aria-label="关闭标签页">×</span>';
|
||||
const titleNode = tab.querySelector('.mnote-main-tab-title');
|
||||
if (titleNode) titleNode.textContent = title;
|
||||
|
||||
Reference in New Issue
Block a user