1025 lines
45 KiB
JavaScript
1025 lines
45 KiB
JavaScript
export const createSidebarWorkspaceRuntime = (dependencies = {}) => {
|
||
const {
|
||
cssEscape,
|
||
currentDocumentId,
|
||
escapeHtml,
|
||
parseJsonScript,
|
||
setCommandPending,
|
||
sidebarShellRuntimeFunction,
|
||
} = dependencies;
|
||
const MNOTE_SIDEBAR_TREE_MODE_KEY = 'mnote.sidebar.tree.mode';
|
||
const MNOTE_RECENT_LOCAL_ROOTS_KEY_PREFIX = 'mnote.localFolder.recentRoots:';
|
||
const MNOTE_LAST_CLOUD_WORKSPACE_KEY = 'mnote.workspace.lastCloudWorkspaceId';
|
||
|
||
function normalizeSidebarTreeMode(value) {
|
||
var mode = String(value || '').trim();
|
||
return mode === 'filetree' ? 'filetree' : 'page';
|
||
}
|
||
|
||
function readStoredSidebarTreeMode() {
|
||
var runtimeFn = sidebarShellRuntimeFunction('readStoredSidebarTreeMode');
|
||
if (runtimeFn) return runtimeFn();
|
||
var params = new URLSearchParams(window.location.search);
|
||
var fromUrl = normalizeSidebarTreeMode(params.get('treeView') || params.get('sidebarTree'));
|
||
if (params.has('treeView') || params.has('sidebarTree')) return fromUrl;
|
||
try {
|
||
var stored = window.sessionStorage ? window.sessionStorage.getItem(MNOTE_SIDEBAR_TREE_MODE_KEY) : '';
|
||
return normalizeSidebarTreeMode(stored);
|
||
} catch (_) {
|
||
return 'page';
|
||
}
|
||
}
|
||
|
||
function persistSidebarTreeMode(mode) {
|
||
var runtimeFn = sidebarShellRuntimeFunction('persistSidebarTreeMode');
|
||
if (runtimeFn) return runtimeFn(mode);
|
||
var normalized = normalizeSidebarTreeMode(mode);
|
||
document.documentElement.setAttribute('data-mnote-sidebar-tree-mode', normalized);
|
||
try {
|
||
if (window.sessionStorage) window.sessionStorage.setItem(MNOTE_SIDEBAR_TREE_MODE_KEY, normalized);
|
||
} catch (_) {}
|
||
return normalized;
|
||
}
|
||
|
||
function activeSidebarTreeMode() {
|
||
var runtimeFn = sidebarShellRuntimeFunction('activeSidebarTreeMode');
|
||
if (runtimeFn) return runtimeFn();
|
||
var active = document.querySelector('[data-mnote-sidebar-tree-tab][aria-selected="true"]');
|
||
if (active instanceof HTMLElement) {
|
||
return normalizeSidebarTreeMode(active.getAttribute('data-mnote-sidebar-tree-tab'));
|
||
}
|
||
return readStoredSidebarTreeMode();
|
||
}
|
||
|
||
function resolveWorkspaceId(trigger) {
|
||
var direct = (trigger.getAttribute('data-workspace-id') || '').trim();
|
||
if (direct) return direct;
|
||
var root = trigger.closest('[data-workspace-id]');
|
||
if (root) {
|
||
var value = (root.getAttribute('data-workspace-id') || '').trim();
|
||
if (value) return value;
|
||
}
|
||
var documentId = currentDocumentId();
|
||
if (documentId) {
|
||
var shell = document.querySelector('.document-shell[data-document-id="' + cssEscape(documentId) + '"][data-workspace-id]');
|
||
if (shell instanceof HTMLElement) {
|
||
var shellWorkspaceId = (shell.getAttribute('data-workspace-id') || '').trim();
|
||
if (shellWorkspaceId) return shellWorkspaceId;
|
||
}
|
||
}
|
||
var activePane = document.querySelector('[data-pane-visible="true"][data-pane-workspace-id]');
|
||
if (activePane instanceof HTMLElement) {
|
||
var paneWorkspaceId = (activePane.getAttribute('data-pane-workspace-id') || '').trim();
|
||
if (paneWorkspaceId) return paneWorkspaceId;
|
||
}
|
||
var anyDocumentShell = document.querySelector('.document-shell[data-workspace-id]');
|
||
if (anyDocumentShell instanceof HTMLElement) {
|
||
var documentWorkspaceId = (anyDocumentShell.getAttribute('data-workspace-id') || '').trim();
|
||
if (documentWorkspaceId) return documentWorkspaceId;
|
||
}
|
||
var fromDom = document.querySelector('[data-workspace-id]');
|
||
if (fromDom instanceof HTMLElement) {
|
||
var domWorkspaceId = (fromDom.getAttribute('data-workspace-id') || '').trim();
|
||
if (domWorkspaceId) return domWorkspaceId;
|
||
}
|
||
return (new URLSearchParams(window.location.search).get('workspaceId') || 'default').trim() || 'default';
|
||
}
|
||
|
||
function currentWorkspaceId() {
|
||
return resolveWorkspaceId(document.body);
|
||
}
|
||
|
||
function currentSourceKind() {
|
||
var params = new URLSearchParams(window.location.search);
|
||
var fromUrl = (params.get('sourceKind') || '').trim();
|
||
if (fromUrl) return fromUrl;
|
||
var fromBody = document.body instanceof HTMLElement ? (document.body.getAttribute('data-mnote-source-kind') || '').trim() : '';
|
||
if (fromBody) return fromBody;
|
||
if (
|
||
window.location.pathname === '/' &&
|
||
!(params.get('workspaceId') || '').trim() &&
|
||
!(params.get('rootUri') || '').trim() &&
|
||
!(params.get('pageId') || '').trim()
|
||
) {
|
||
return 'local_folder';
|
||
}
|
||
return 'local_folder';
|
||
}
|
||
|
||
function currentRootUri() {
|
||
var params = new URLSearchParams(window.location.search);
|
||
var fromUrl = (params.get('rootUri') || '').trim();
|
||
if (fromUrl) return fromUrl;
|
||
return document.body instanceof HTMLElement ? (document.body.getAttribute('data-mnote-root-uri') || '').trim() : '';
|
||
}
|
||
|
||
function rememberCloudWorkspaceId(workspaceId) {
|
||
var normalized = String(workspaceId || '').trim();
|
||
if (!normalized || normalized === 'local-folder' || normalized === 'default' || normalized.indexOf('local-ws:') === 0) return;
|
||
try {
|
||
if (window.localStorage) window.localStorage.setItem(MNOTE_LAST_CLOUD_WORKSPACE_KEY, normalized);
|
||
} catch (_) {}
|
||
}
|
||
|
||
function readCurrentCloudWorkspaceIdFromPage() {
|
||
if (currentSourceKind() === 'local_folder') return '';
|
||
var params = new URLSearchParams(window.location.search);
|
||
var fromUrl = (params.get('workspaceId') || '').trim();
|
||
if (fromUrl && fromUrl !== 'default' && fromUrl.indexOf('local-ws:') !== 0) return fromUrl;
|
||
var fromDom = document.querySelector('[data-workspace-id]');
|
||
if (fromDom instanceof HTMLElement) {
|
||
var value = (fromDom.getAttribute('data-workspace-id') || '').trim();
|
||
if (value && value !== 'local-folder' && value !== 'default' && value.indexOf('local-ws:') !== 0) return value;
|
||
}
|
||
return '';
|
||
}
|
||
|
||
function rememberCurrentCloudWorkspaceId() {
|
||
rememberCloudWorkspaceId(readCurrentCloudWorkspaceIdFromPage());
|
||
}
|
||
|
||
function readLastCloudWorkspaceId() {
|
||
try {
|
||
var stored = window.localStorage ? window.localStorage.getItem(MNOTE_LAST_CLOUD_WORKSPACE_KEY) : '';
|
||
if (stored && stored.trim() && stored.trim() !== 'default' && stored.trim().indexOf('local-ws:') !== 0) return stored.trim();
|
||
} catch (_) {}
|
||
var params = new URLSearchParams(window.location.search);
|
||
var fromUrl = (params.get('workspaceId') || '').trim();
|
||
if (fromUrl && currentSourceKind() !== 'local_folder' && fromUrl.indexOf('local-ws:') !== 0) return fromUrl;
|
||
var fromDom = document.querySelector('[data-workspace-id]');
|
||
if (fromDom instanceof HTMLElement) {
|
||
var value = (fromDom.getAttribute('data-workspace-id') || '').trim();
|
||
if (value && value !== 'local-folder' && value.indexOf('local-ws:') !== 0) return value;
|
||
}
|
||
return '';
|
||
}
|
||
|
||
function copyWorkspaceSourceParams(targetUrl) {
|
||
var params = new URLSearchParams(window.location.search);
|
||
['sourceKind', 'rootUri', 'fileTreeScope', 'secondaryDocumentId', 'secondarySourceKind', 'secondaryRootUri'].forEach(function(name) {
|
||
var value = (params.get(name) || '').trim();
|
||
if (value) targetUrl.searchParams.set(name, value);
|
||
});
|
||
var sourceKind = currentSourceKind();
|
||
var rootUri = currentRootUri();
|
||
if (sourceKind && !targetUrl.searchParams.get('sourceKind')) targetUrl.searchParams.set('sourceKind', sourceKind);
|
||
if (rootUri && !targetUrl.searchParams.get('rootUri')) targetUrl.searchParams.set('rootUri', rootUri);
|
||
}
|
||
|
||
function currentWorkspaceSourcePayload() {
|
||
var params = new URLSearchParams(window.location.search);
|
||
var payload = {};
|
||
['sourceKind', 'rootUri'].forEach(function(name) {
|
||
var value = (params.get(name) || '').trim();
|
||
if (value) payload[name] = value;
|
||
});
|
||
var sourceKind = currentSourceKind();
|
||
var rootUri = currentRootUri();
|
||
if (sourceKind && !payload.sourceKind) payload.sourceKind = sourceKind;
|
||
if (rootUri && !payload.rootUri) payload.rootUri = rootUri;
|
||
return payload;
|
||
}
|
||
|
||
function currentActorStorageId() {
|
||
var fromBody = document.body instanceof HTMLElement ? (document.body.getAttribute('data-mnote-actor-id') || '').trim() : '';
|
||
if (fromBody && fromBody !== 'anonymous') return fromBody;
|
||
return '';
|
||
}
|
||
|
||
function recentLocalRootsStorageKey() {
|
||
var actorId = currentActorStorageId();
|
||
return actorId ? MNOTE_RECENT_LOCAL_ROOTS_KEY_PREFIX + encodeURIComponent(actorId) : '';
|
||
}
|
||
|
||
function readRecentLocalRoots() {
|
||
try {
|
||
var storageKey = recentLocalRootsStorageKey();
|
||
if (!storageKey) return [];
|
||
var raw = window.localStorage ? window.localStorage.getItem(storageKey) : '';
|
||
var parsed = raw ? JSON.parse(raw) : [];
|
||
return Array.isArray(parsed) ? parsed.filter(function(value) {
|
||
return typeof value === 'string' && value.trim();
|
||
}).slice(0, 10) : [];
|
||
} catch (_) {
|
||
return [];
|
||
}
|
||
}
|
||
|
||
function rememberLocalRoot(rootUri) {
|
||
try {
|
||
if (!window.localStorage) return;
|
||
var storageKey = recentLocalRootsStorageKey();
|
||
if (!storageKey) return;
|
||
var roots = readRecentLocalRoots().filter(function(value) { return value !== rootUri; });
|
||
roots.unshift(rootUri);
|
||
window.localStorage.setItem(storageKey, JSON.stringify(roots.slice(0, 10)));
|
||
} catch (_) {}
|
||
}
|
||
|
||
function pathToFileRootUri(value) {
|
||
var trimmed = String(value || '').trim();
|
||
if (!trimmed) return '';
|
||
if (/^file:\/\//i.test(trimmed)) return trimmed;
|
||
if (trimmed.charAt(0) !== '/') return '';
|
||
return 'file://' + trimmed.split('/').map(function(part, index) {
|
||
return index === 0 ? '' : encodeURIComponent(part);
|
||
}).join('/');
|
||
}
|
||
|
||
function fileRootUriToPathInput(rootUri) {
|
||
var value = String(rootUri || '').replace(/^file:\/\//, '');
|
||
try {
|
||
return decodeURIComponent(value);
|
||
} catch (_) {
|
||
return value;
|
||
}
|
||
}
|
||
|
||
function recentLocalRootLabel(rootUri) {
|
||
var label = fileRootUriToPathInput(rootUri);
|
||
return label || '本地文件夹';
|
||
}
|
||
|
||
function normalizeGrantedLocalFolderRootUri(grant) {
|
||
var rootUri = String(grant && grant.rootUri || '').trim();
|
||
if (rootUri && /^file:\/\//i.test(rootUri)) return rootUri;
|
||
var rootPath = String(grant && grant.rootPath || '').trim();
|
||
if (rootPath) return pathToFileRootUri(rootPath);
|
||
if (rootUri) return pathToFileRootUri(rootUri);
|
||
return '';
|
||
}
|
||
|
||
function isDefaultWorkspaceAutoGrant(grant) {
|
||
var source = String(grant && grant.source || '').trim();
|
||
var workspaceId = String(grant && grant.workspaceId || '').trim();
|
||
var rootUri = String(grant && grant.rootUri || '').trim();
|
||
var permission = String(grant && grant.permission || '').trim();
|
||
var createdBy = String(grant && (grant.createdBy || grant.ownerUserId) || '').trim();
|
||
var targetUser = String(grant && (grant.userId || grant.targetUserId) || '').trim();
|
||
return source === 'auto'
|
||
&& workspaceId
|
||
&& permission === 'write'
|
||
&& createdBy === targetUser
|
||
&& /^local:\/\/users\/.+\/workspaces\/my-space$/.test(rootUri);
|
||
}
|
||
|
||
function openLocalFolderRoot(rootUri) {
|
||
if (currentSourceKind() !== 'local_folder') {
|
||
rememberCurrentCloudWorkspaceId();
|
||
}
|
||
rememberLocalRoot(rootUri);
|
||
var targetUrl = new URL('/', window.location.origin);
|
||
targetUrl.searchParams.set('treeView', 'filetree');
|
||
targetUrl.searchParams.set('sourceKind', 'local_folder');
|
||
targetUrl.searchParams.set('rootUri', rootUri);
|
||
window.location.href = targetUrl.toString();
|
||
}
|
||
|
||
function switchToCloudWorkspace() {
|
||
var targetUrl = new URL('/', window.location.origin);
|
||
targetUrl.searchParams.set('treeView', activeSidebarTreeMode() || 'filetree');
|
||
targetUrl.searchParams.set('mnoteHome', '1');
|
||
window.location.href = targetUrl.toString();
|
||
}
|
||
|
||
function closeTrashModal() {
|
||
var modal = document.querySelector('[data-testid="mnote-trash-modal"]');
|
||
if (!modal) return;
|
||
var workbench = modal.querySelector('[data-testid="mnote-trash-workbench"]');
|
||
if (workbench && workbench.__mnoteTrashEventSource && typeof workbench.__mnoteTrashEventSource.close === 'function') {
|
||
workbench.__mnoteTrashEventSource.close();
|
||
}
|
||
var fileScrollTop = Number(modal.getAttribute('data-file-scroll-top') || '0');
|
||
var fileRoot = document.getElementById('sidebar-file-tree-root');
|
||
if (fileRoot instanceof HTMLElement && Number.isFinite(fileScrollTop)) fileRoot.scrollTop = fileScrollTop;
|
||
if (modal.parentElement) modal.parentElement.removeChild(modal);
|
||
document.documentElement.removeAttribute('data-mnote-trash-modal-open');
|
||
}
|
||
|
||
function executeTrashWorkbenchScripts(parsed) {
|
||
parsed.querySelectorAll('script').forEach(function(script) {
|
||
var nextScript = document.createElement('script');
|
||
Array.from(script.attributes || []).forEach(function(attr) {
|
||
nextScript.setAttribute(attr.name, attr.value);
|
||
});
|
||
nextScript.textContent = script.textContent || '';
|
||
document.body.appendChild(nextScript);
|
||
if (nextScript.parentElement) nextScript.parentElement.removeChild(nextScript);
|
||
});
|
||
}
|
||
|
||
function renderLocalFolderTrashPlaceholder(content, rootUri) {
|
||
content.innerHTML = [
|
||
'<section class="mnote-trash-workbench" data-testid="mnote-trash-workbench" data-trash-source-kind="local_folder">',
|
||
'<header class="mnote-trash-header"><h1>本地文件夹垃圾箱</h1>',
|
||
'<p>本地删除项保存在当前目录的 <code>.mnote/trash</code> 与 <code>.mnote/trash-index.json</code> 中。</p></header>',
|
||
'<section class="mnote-trash-section"><div class="mnote-trash-empty">当前弹窗已保持在本地文件夹上下文:' + escapeHtml(rootUri || '未选择本地目录') + '</div></section>',
|
||
'</section>'
|
||
].join('');
|
||
}
|
||
|
||
function openTrashModal(trigger) {
|
||
var existing = document.querySelector('[data-testid="mnote-trash-modal"]');
|
||
if (existing) return;
|
||
var fileRoot = document.getElementById('sidebar-file-tree-root');
|
||
var activeRow = document.querySelector('#sidebar-file-tree-root .tree-row[data-selected="true"], #sidebar-tree-root .tree-row[data-active="true"]');
|
||
var overlay = document.createElement('div');
|
||
overlay.className = 'mnote-trash-modal';
|
||
overlay.setAttribute('data-testid', 'mnote-trash-modal');
|
||
overlay.setAttribute('role', 'dialog');
|
||
overlay.setAttribute('aria-modal', 'true');
|
||
overlay.setAttribute('aria-label', '垃圾箱');
|
||
overlay.setAttribute('data-file-scroll-top', fileRoot instanceof HTMLElement ? String(fileRoot.scrollTop) : '0');
|
||
if (activeRow instanceof HTMLElement) {
|
||
overlay.setAttribute('data-active-row-id', activeRow.getAttribute('data-row-id') || activeRow.getAttribute('data-node-id') || '');
|
||
}
|
||
overlay.innerHTML = '<div class="mnote-trash-modal__backdrop" data-mnote-trash-modal-close="true"></div>' +
|
||
'<section class="mnote-trash-modal__panel">' +
|
||
'<button type="button" class="mnote-trash-modal__close" data-testid="mnote-trash-modal-close" data-mnote-trash-modal-close="true" aria-label="关闭垃圾箱">×</button>' +
|
||
'<div class="mnote-trash-modal__content" data-testid="mnote-trash-modal-content"><div class="mnote-trash-empty">正在加载垃圾箱...</div></div>' +
|
||
'</section>';
|
||
document.body.appendChild(overlay);
|
||
document.documentElement.setAttribute('data-mnote-trash-modal-open', 'true');
|
||
var content = overlay.querySelector('[data-testid="mnote-trash-modal-content"]');
|
||
var sourceKind = currentSourceKind();
|
||
var workspaceId = resolveWorkspaceId(trigger || document.body);
|
||
var url = new URL('/trash', window.location.origin);
|
||
if (sourceKind === 'local_folder') {
|
||
var rootUri = currentRootUri();
|
||
if (!rootUri) {
|
||
renderLocalFolderTrashPlaceholder(content, '');
|
||
return;
|
||
}
|
||
url.searchParams.set('sourceKind', 'local_folder');
|
||
url.searchParams.set('rootUri', rootUri);
|
||
} else if (workspaceId) {
|
||
url.searchParams.set('workspaceId', workspaceId);
|
||
}
|
||
fetch(url.toString(), { headers: { 'x-mnote-trash-modal': '1' } }).then(function(response) {
|
||
return response.text().then(function(html) {
|
||
if (!response.ok) throw new Error('trash_modal_load_failed_' + response.status);
|
||
var parsed = new DOMParser().parseFromString(html, 'text/html');
|
||
var workbench = parsed.querySelector('[data-testid="mnote-trash-workbench"]');
|
||
if (!workbench) throw new Error('trash_modal_missing_workbench');
|
||
content.innerHTML = '';
|
||
content.appendChild(workbench);
|
||
executeTrashWorkbenchScripts(parsed);
|
||
});
|
||
}).catch(function(error) {
|
||
content.innerHTML = '<div class="mnote-trash-empty" data-testid="mnote-trash-modal-error">' + escapeHtml(error && error.message ? error.message : String(error)) + '</div>';
|
||
});
|
||
}
|
||
|
||
function closeLocalFolderDialog() {
|
||
var existing = document.querySelector('[data-testid="mnote-local-folder-dialog"]');
|
||
if (existing && existing.parentElement) existing.parentElement.removeChild(existing);
|
||
}
|
||
|
||
function readPathFromDirectoryFiles(files) {
|
||
var first = files && files.length ? files[0] : null;
|
||
if (!first) return '';
|
||
var rawPath = typeof first.path === 'string' ? first.path : '';
|
||
var relative = typeof first.webkitRelativePath === 'string' ? first.webkitRelativePath : '';
|
||
if (rawPath && relative) {
|
||
var suffix = relative.split('/').filter(Boolean).join('/');
|
||
if (suffix && rawPath.endsWith(suffix)) {
|
||
return rawPath.slice(0, rawPath.length - suffix.length).replace(/[\/\\]$/, '');
|
||
}
|
||
}
|
||
if (rawPath) return rawPath;
|
||
return '';
|
||
}
|
||
|
||
function requestBrowserFolderChoice(statusNode) {
|
||
return new Promise(function(resolve) {
|
||
var input = document.createElement('input');
|
||
input.type = 'file';
|
||
input.multiple = true;
|
||
input.setAttribute('webkitdirectory', '');
|
||
input.setAttribute('directory', '');
|
||
input.style.position = 'fixed';
|
||
input.style.left = '-9999px';
|
||
input.addEventListener('change', function() {
|
||
var selectedPath = readPathFromDirectoryFiles(input.files || []);
|
||
if (input.parentElement) input.parentElement.removeChild(input);
|
||
if (!selectedPath && statusNode instanceof HTMLElement) {
|
||
statusNode.textContent = '当前浏览器没有暴露本机绝对路径,请在下方输入路径。';
|
||
}
|
||
resolve(selectedPath);
|
||
}, { once: true });
|
||
document.body.appendChild(input);
|
||
input.click();
|
||
});
|
||
}
|
||
|
||
async function requestNativeFolderChoice(statusNode) {
|
||
var desktopPicker = window.__mnoteDesktop && typeof window.__mnoteDesktop.selectLocalFolder === 'function'
|
||
? window.__mnoteDesktop.selectLocalFolder
|
||
: null;
|
||
if (desktopPicker) {
|
||
var selected = await desktopPicker();
|
||
return typeof selected === 'string' ? selected : '';
|
||
}
|
||
if (typeof window.showDirectoryPicker === 'function') {
|
||
var handle = await window.showDirectoryPicker({ mode: 'read' });
|
||
var handlePath = handle && (handle.path || handle.mnotePath || handle.nativePath);
|
||
if (typeof handlePath === 'string' && handlePath.trim()) return handlePath;
|
||
if (statusNode instanceof HTMLElement) {
|
||
statusNode.textContent = '已选择“' + (handle && handle.name ? handle.name : '文件夹') + '”,但浏览器没有暴露本机绝对路径,请在下方确认路径。';
|
||
}
|
||
return '';
|
||
}
|
||
return requestBrowserFolderChoice(statusNode);
|
||
}
|
||
|
||
function openLocalFolderDialog(initialMessage) {
|
||
closeLocalFolderDialog();
|
||
var recent = readRecentLocalRoots();
|
||
var dialog = document.createElement('div');
|
||
dialog.className = 'mnote-local-folder-dialog';
|
||
dialog.setAttribute('data-testid', 'mnote-local-folder-dialog');
|
||
dialog.setAttribute('role', 'dialog');
|
||
dialog.setAttribute('aria-modal', 'true');
|
||
dialog.style.position = 'fixed';
|
||
dialog.style.inset = '0';
|
||
dialog.style.zIndex = '2147483646';
|
||
dialog.style.background = 'rgba(15, 23, 42, 0.28)';
|
||
dialog.style.display = 'flex';
|
||
dialog.style.alignItems = 'center';
|
||
dialog.style.justifyContent = 'center';
|
||
var card = document.createElement('div');
|
||
card.className = 'mnote-local-folder-dialog__card';
|
||
card.style.width = 'min(640px, calc(100vw - 32px))';
|
||
card.style.background = '#fff';
|
||
card.style.border = '1px solid rgba(27, 28, 28, 0.12)';
|
||
card.style.borderRadius = '8px';
|
||
card.style.padding = '16px';
|
||
card.style.boxShadow = '0 18px 48px rgba(15, 23, 42, 0.22)';
|
||
card.style.display = 'grid';
|
||
card.style.gap = '12px';
|
||
var title = document.createElement('h2');
|
||
title.textContent = '打开本地文件夹';
|
||
title.style.margin = '0';
|
||
title.style.fontSize = '18px';
|
||
title.style.lineHeight = '1.4';
|
||
var status = document.createElement('p');
|
||
status.className = 'mnote-local-folder-dialog__status';
|
||
status.setAttribute('data-testid', 'mnote-local-folder-status');
|
||
status.textContent = initialMessage || '选择一个本机文件夹,或输入绝对路径。';
|
||
status.style.margin = '0';
|
||
status.style.color = '#4b5563';
|
||
status.style.fontSize = '13px';
|
||
var input = document.createElement('input');
|
||
input.type = 'text';
|
||
input.autocomplete = 'off';
|
||
input.spellcheck = false;
|
||
input.placeholder = '/mnt/Data1T/mnote/design/04-tree-domain/done';
|
||
input.setAttribute('data-testid', 'mnote-local-folder-path-input');
|
||
input.value = recent.length ? fileRootUriToPathInput(recent[0]) : '';
|
||
input.style.width = '100%';
|
||
input.style.boxSizing = 'border-box';
|
||
input.style.border = '1px solid rgba(27, 28, 28, 0.18)';
|
||
input.style.borderRadius = '6px';
|
||
input.style.padding = '10px 12px';
|
||
input.style.fontSize = '14px';
|
||
var actions = document.createElement('div');
|
||
actions.className = 'mnote-local-folder-dialog__actions';
|
||
actions.style.display = 'flex';
|
||
actions.style.gap = '8px';
|
||
actions.style.justifyContent = 'flex-end';
|
||
var choose = document.createElement('button');
|
||
choose.type = 'button';
|
||
choose.textContent = '选择文件夹';
|
||
choose.setAttribute('data-testid', 'mnote-local-folder-native-picker');
|
||
var cancel = document.createElement('button');
|
||
cancel.type = 'button';
|
||
cancel.textContent = '取消';
|
||
var confirm = document.createElement('button');
|
||
confirm.type = 'button';
|
||
confirm.textContent = '打开';
|
||
confirm.setAttribute('data-testid', 'mnote-local-folder-open-confirm');
|
||
[choose, cancel, confirm].forEach(function(button) {
|
||
button.style.border = '1px solid rgba(27, 28, 28, 0.14)';
|
||
button.style.borderRadius = '6px';
|
||
button.style.padding = '8px 12px';
|
||
button.style.background = '#fff';
|
||
button.style.cursor = 'pointer';
|
||
button.style.fontSize = '14px';
|
||
});
|
||
function submit() {
|
||
var rootUri = pathToFileRootUri(input.value);
|
||
if (!rootUri) {
|
||
status.textContent = '请输入以 / 开头的绝对路径,或 file:// URI。';
|
||
input.focus();
|
||
return;
|
||
}
|
||
closeLocalFolderDialog();
|
||
openLocalFolderRoot(rootUri);
|
||
}
|
||
choose.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
requestNativeFolderChoice(status).then(function(selectedPath) {
|
||
if (selectedPath) {
|
||
input.value = selectedPath;
|
||
submit();
|
||
}
|
||
}).catch(function(error) {
|
||
status.textContent = error && error.name === 'AbortError'
|
||
? '已取消选择。'
|
||
: '无法打开系统文件夹选择器,请输入路径。';
|
||
});
|
||
});
|
||
cancel.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
closeLocalFolderDialog();
|
||
});
|
||
confirm.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
submit();
|
||
});
|
||
input.addEventListener('keydown', function(event) {
|
||
if (event.key === 'Enter') {
|
||
event.preventDefault();
|
||
submit();
|
||
}
|
||
});
|
||
actions.appendChild(choose);
|
||
actions.appendChild(cancel);
|
||
actions.appendChild(confirm);
|
||
card.appendChild(title);
|
||
card.appendChild(status);
|
||
card.appendChild(input);
|
||
var authorizedSection = document.createElement('div');
|
||
authorizedSection.className = 'mnote-local-folder-dialog__authorized';
|
||
authorizedSection.setAttribute('data-testid', 'mnote-local-folder-authorized-roots');
|
||
authorizedSection.hidden = true;
|
||
card.appendChild(authorizedSection);
|
||
fetch('/api/user/access-policy', {
|
||
method: 'GET',
|
||
headers: { 'accept': 'application/json' },
|
||
credentials: 'include'
|
||
}).then(function(response) {
|
||
return response.ok ? response.json() : null;
|
||
}).then(function(payload) {
|
||
var grants = payload && Array.isArray(payload.grants) ? payload.grants : [];
|
||
var activeGrants = grants.filter(function(grant) {
|
||
return grant && grant.active !== false && grant.status !== 'revoked';
|
||
});
|
||
if (!activeGrants.length) return;
|
||
authorizedSection.hidden = false;
|
||
var authorizedTitle = document.createElement('div');
|
||
authorizedTitle.style.fontSize = '12px';
|
||
authorizedTitle.style.color = '#6b7280';
|
||
authorizedTitle.textContent = '已授权文件夹';
|
||
var authorizedList = document.createElement('div');
|
||
authorizedList.className = 'mnote-local-folder-dialog__recent';
|
||
activeGrants.slice(0, 8).forEach(function(grant) {
|
||
if (isDefaultWorkspaceAutoGrant(grant)) return;
|
||
var rootUri = normalizeGrantedLocalFolderRootUri(grant);
|
||
if (!rootUri) return;
|
||
var button = document.createElement('button');
|
||
button.type = 'button';
|
||
button.setAttribute('data-testid', 'mnote-local-folder-authorized-root');
|
||
button.textContent = fileRootUriToPathInput(rootUri);
|
||
button.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
closeLocalFolderDialog();
|
||
openLocalFolderRoot(rootUri);
|
||
});
|
||
authorizedList.appendChild(button);
|
||
});
|
||
if (!authorizedList.childElementCount) return;
|
||
authorizedSection.appendChild(authorizedTitle);
|
||
authorizedSection.appendChild(authorizedList);
|
||
}).catch(function() {});
|
||
if (recent.length) {
|
||
var recentTitle = document.createElement('div');
|
||
recentTitle.style.fontSize = '12px';
|
||
recentTitle.style.color = '#6b7280';
|
||
recentTitle.textContent = '最近使用';
|
||
card.appendChild(recentTitle);
|
||
var recentList = document.createElement('div');
|
||
recentList.className = 'mnote-local-folder-dialog__recent';
|
||
recent.slice(0, 5).forEach(function(rootUri) {
|
||
var button = document.createElement('button');
|
||
button.type = 'button';
|
||
button.textContent = recentLocalRootLabel(rootUri);
|
||
button.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
closeLocalFolderDialog();
|
||
openLocalFolderRoot(rootUri);
|
||
});
|
||
recentList.appendChild(button);
|
||
});
|
||
card.appendChild(recentList);
|
||
}
|
||
card.appendChild(actions);
|
||
dialog.appendChild(card);
|
||
dialog.addEventListener('click', function(event) {
|
||
if (event.target === dialog) closeLocalFolderDialog();
|
||
});
|
||
document.body.appendChild(dialog);
|
||
input.focus();
|
||
input.select();
|
||
}
|
||
|
||
function requestOpenLocalFolder() {
|
||
openLocalFolderDialog('');
|
||
}
|
||
|
||
async function createDefaultLocalWorkspace(trigger) {
|
||
setCommandPending(trigger, true);
|
||
try {
|
||
var response = await fetch('/api/local-folder/workspaces/default', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json' },
|
||
body: '{}'
|
||
});
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok) {
|
||
throw new Error((payload && payload.message) || ('local_workspace_create_failed_' + response.status));
|
||
}
|
||
var workspace = payload && payload.workspace || {};
|
||
var rootUri = String(workspace.rootUri || '').trim();
|
||
if (!rootUri) throw new Error('local_workspace_create_missing_root_uri');
|
||
openLocalFolderRoot(rootUri);
|
||
} catch (error) {
|
||
var status = document.querySelector('[data-testid="mnote-local-folder-status"]');
|
||
if (status instanceof HTMLElement) {
|
||
status.textContent = error && error.message ? error.message : String(error);
|
||
} else {
|
||
openLocalFolderDialog(error && error.message ? error.message : String(error));
|
||
}
|
||
} finally {
|
||
setCommandPending(trigger, false);
|
||
}
|
||
}
|
||
|
||
function autoOpenRecentLocalRootOnHome() {
|
||
if (window.location.pathname !== '/') return false;
|
||
var params = new URLSearchParams(window.location.search);
|
||
if ((params.get('sourceKind') || '').trim()) return false;
|
||
if ((params.get('rootUri') || '').trim()) return false;
|
||
if ((params.get('workspaceId') || '').trim()) return false;
|
||
if ((params.get('pageId') || '').trim()) return false;
|
||
if ((params.get('mnoteHome') || '').trim() === '1') return false;
|
||
var recent = readRecentLocalRoots();
|
||
if (!recent.length) return false;
|
||
openLocalFolderRoot(recent[0]);
|
||
return true;
|
||
}
|
||
|
||
function closeWorkspaceSourceMenu() {
|
||
var existing = document.querySelector('[data-testid="mnote-workspace-source-menu"]');
|
||
if (existing && existing.parentElement) existing.parentElement.removeChild(existing);
|
||
document.querySelectorAll('[data-testid="mnote-workspace-source-trigger"]').forEach(function(trigger) {
|
||
if (trigger instanceof HTMLElement) trigger.setAttribute('aria-expanded', 'false');
|
||
});
|
||
}
|
||
|
||
function workspaceSourceLabel(rootUri) {
|
||
var label = String(rootUri || '').replace(/^file:\/\//, '');
|
||
try {
|
||
label = decodeURIComponent(label);
|
||
} catch (_) {}
|
||
return label || '本地文件夹';
|
||
}
|
||
|
||
function openWorkspaceSourceMenu(trigger) {
|
||
closeWorkspaceSourceMenu();
|
||
if (!(trigger instanceof HTMLElement)) return;
|
||
trigger.setAttribute('aria-expanded', 'true');
|
||
var menu = document.createElement('div');
|
||
menu.className = 'mnote-workspace-source-menu';
|
||
menu.setAttribute('data-testid', 'mnote-workspace-source-menu');
|
||
menu.setAttribute('role', 'menu');
|
||
var cloudButton = document.createElement('button');
|
||
cloudButton.type = 'button';
|
||
cloudButton.className = 'mnote-workspace-source-menu__item';
|
||
cloudButton.setAttribute('data-testid', 'mnote-switch-cloud-workspace');
|
||
cloudButton.setAttribute('role', 'menuitem');
|
||
cloudButton.textContent = '我的空间';
|
||
cloudButton.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
closeWorkspaceSourceMenu();
|
||
switchToCloudWorkspace();
|
||
});
|
||
menu.appendChild(cloudButton);
|
||
var recent = readRecentLocalRoots();
|
||
if (recent.length) {
|
||
var label = document.createElement('div');
|
||
label.className = 'mnote-workspace-source-menu__label';
|
||
label.textContent = '最近本地文件夹';
|
||
menu.appendChild(label);
|
||
recent.slice(0, 8).forEach(function(rootUri) {
|
||
var button = document.createElement('button');
|
||
button.type = 'button';
|
||
button.className = 'mnote-workspace-source-menu__item';
|
||
button.setAttribute('data-testid', 'mnote-recent-local-root');
|
||
button.setAttribute('data-root-uri', rootUri);
|
||
button.setAttribute('role', 'menuitem');
|
||
button.textContent = workspaceSourceLabel(rootUri);
|
||
button.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
closeWorkspaceSourceMenu();
|
||
openLocalFolderRoot(rootUri);
|
||
});
|
||
menu.appendChild(button);
|
||
});
|
||
}
|
||
var openOther = document.createElement('button');
|
||
openOther.type = 'button';
|
||
openOther.className = 'mnote-workspace-source-menu__item mnote-workspace-source-menu__item--primary';
|
||
openOther.setAttribute('data-testid', 'mnote-open-other-local-folder');
|
||
openOther.setAttribute('role', 'menuitem');
|
||
openOther.textContent = '打开其他本地文件夹';
|
||
openOther.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
closeWorkspaceSourceMenu();
|
||
requestOpenLocalFolder();
|
||
});
|
||
menu.appendChild(openOther);
|
||
trigger.closest('[data-testid="wolai-workspace-identity"]')?.appendChild(menu);
|
||
}
|
||
|
||
function closeAccountMenu() {
|
||
var existing = document.querySelector('[data-testid="mnote-account-menu"]');
|
||
if (existing && existing.parentElement) existing.parentElement.removeChild(existing);
|
||
document.querySelectorAll('[data-testid="mnote-account-menu-trigger"]').forEach(function(trigger) {
|
||
if (trigger instanceof HTMLElement) trigger.setAttribute('aria-expanded', 'false');
|
||
});
|
||
}
|
||
|
||
function closeProfileDialog() {
|
||
var existing = document.querySelector('[data-testid="mnote-profile-dialog"]');
|
||
if (existing && existing.parentElement) existing.parentElement.removeChild(existing);
|
||
}
|
||
|
||
function closeAdminAccessPolicyDialog() {
|
||
var existing = document.querySelector('[data-testid="mnote-admin-access-policy-modal"]');
|
||
if (existing && existing.parentElement) existing.parentElement.removeChild(existing);
|
||
document.documentElement.removeAttribute('data-mnote-admin-access-policy-modal-open');
|
||
}
|
||
|
||
function accountMenuFallbackSession() {
|
||
var body = document.body instanceof HTMLElement ? document.body : null;
|
||
var actorId = body ? String(body.getAttribute('data-mnote-actor-id') || '').trim() : '';
|
||
return {
|
||
ok: true,
|
||
name: actorId || '当前用户',
|
||
email: '',
|
||
userId: actorId || 'anonymous',
|
||
actorType: actorId && actorId !== 'anonymous' ? 'user' : 'anonymous',
|
||
authMode: 'browser'
|
||
};
|
||
}
|
||
|
||
function renderAccountInfo(menu, session) {
|
||
var nameNode = menu.querySelector('[data-account-info="name"]');
|
||
var emailNode = menu.querySelector('[data-account-info="email"]');
|
||
var idNode = menu.querySelector('[data-account-info="user-id"]');
|
||
var typeNode = menu.querySelector('[data-account-info="actor-type"]');
|
||
if (nameNode) nameNode.textContent = session.name || session.userId || '当前用户';
|
||
if (emailNode) emailNode.textContent = session.email || '未提供邮箱';
|
||
if (idNode) {
|
||
idNode.textContent = session.userId || 'anonymous';
|
||
idNode.setAttribute('title', session.userId || 'anonymous');
|
||
}
|
||
if (typeNode) typeNode.textContent = session.actorType || session.authMode || 'unknown';
|
||
var copyButton = menu.querySelector('[data-account-copy-user-id]');
|
||
if (copyButton instanceof HTMLElement) {
|
||
copyButton.dataset.copyValue = session.userId || 'anonymous';
|
||
copyButton.setAttribute('title', '复制用户 ID');
|
||
}
|
||
}
|
||
|
||
function sessionIsAdmin(session) {
|
||
return String(session && session.actorType || '').trim() === 'admin';
|
||
}
|
||
|
||
async function loadAccountInfo(menu) {
|
||
try {
|
||
var response = await fetch('/api/auth/session', {
|
||
method: 'GET',
|
||
headers: { 'accept': 'application/json' },
|
||
credentials: 'include'
|
||
});
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok || !payload) throw new Error('session_failed_' + response.status);
|
||
renderAccountInfo(menu, payload);
|
||
} catch (_) {
|
||
renderAccountInfo(menu, accountMenuFallbackSession());
|
||
}
|
||
}
|
||
|
||
async function fetchAccountSession() {
|
||
try {
|
||
var response = await fetch('/api/auth/session', {
|
||
method: 'GET',
|
||
headers: { 'accept': 'application/json' },
|
||
credentials: 'include'
|
||
});
|
||
var payload = await response.json().catch(function() { return null; });
|
||
if (!response.ok || !payload) throw new Error('session_failed_' + response.status);
|
||
return payload;
|
||
} catch (_) {
|
||
return accountMenuFallbackSession();
|
||
}
|
||
}
|
||
|
||
function openProfileDialog(session) {
|
||
closeProfileDialog();
|
||
closeAccountMenu();
|
||
var dialog = document.createElement('div');
|
||
dialog.className = 'mnote-profile-dialog';
|
||
dialog.setAttribute('data-testid', 'mnote-profile-dialog');
|
||
dialog.setAttribute('role', 'dialog');
|
||
dialog.setAttribute('aria-modal', 'true');
|
||
dialog.setAttribute('aria-labelledby', 'mnote-profile-dialog-title');
|
||
var userId = escapeHtml(session.userId || 'anonymous');
|
||
dialog.innerHTML =
|
||
'<div class="mnote-profile-dialog__backdrop" data-profile-dialog-close></div>' +
|
||
'<section class="mnote-profile-dialog__panel">' +
|
||
'<header class="mnote-profile-dialog__header">' +
|
||
'<div>' +
|
||
'<div class="mnote-profile-dialog__eyebrow">个人信息</div>' +
|
||
'<h2 id="mnote-profile-dialog-title">个人信息</h2>' +
|
||
'</div>' +
|
||
'<button type="button" class="mnote-profile-dialog__close" data-profile-dialog-close aria-label="关闭"><span class="material-symbols-outlined" data-icon="close" aria-hidden="true"></span></button>' +
|
||
'</header>' +
|
||
'<div class="mnote-profile-dialog__identity">' +
|
||
'<div class="mnote-profile-dialog__avatar">' + escapeHtml(String(session.name || session.email || session.userId || '用').slice(0, 1).toUpperCase()) + '</div>' +
|
||
'<div><strong>' + escapeHtml(session.name || session.userId || '当前用户') + '</strong><span>' + escapeHtml(session.email || '未提供邮箱') + '</span></div>' +
|
||
'</div>' +
|
||
'<dl class="mnote-profile-dialog__list">' +
|
||
'<div><dt>用户名</dt><dd>' + escapeHtml(session.name || '未设置') + '</dd></div>' +
|
||
'<div><dt>邮箱</dt><dd>' + escapeHtml(session.email || '未提供邮箱') + '</dd></div>' +
|
||
'<div><dt>用户 ID</dt><dd><code title="' + userId + '">' + userId + '</code><button type="button" data-profile-copy-user-id data-copy-value="' + userId + '"><span class="material-symbols-outlined" data-icon="content_copy" aria-hidden="true"></span></button></dd></div>' +
|
||
'<div><dt>身份</dt><dd>' + escapeHtml(session.actorType || session.authMode || 'unknown') + '</dd></div>' +
|
||
'</dl>' +
|
||
'</section>';
|
||
dialog.querySelectorAll('[data-profile-dialog-close]').forEach(function(button) {
|
||
button.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
closeProfileDialog();
|
||
});
|
||
});
|
||
var copyButton = dialog.querySelector('[data-profile-copy-user-id]');
|
||
if (copyButton) {
|
||
copyButton.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
void copyTreeContextValue(copyButton.getAttribute('data-copy-value') || '', 'profile-user-id');
|
||
});
|
||
}
|
||
document.body.appendChild(dialog);
|
||
var closeButton = dialog.querySelector('.mnote-profile-dialog__close');
|
||
if (closeButton instanceof HTMLElement) closeButton.focus();
|
||
}
|
||
|
||
function openAdminAccessPolicyDialog(trigger) {
|
||
closeAdminAccessPolicyDialog();
|
||
closeAccountMenu();
|
||
if (!(trigger instanceof HTMLElement)) return;
|
||
var role = trigger.getAttribute('data-access-policy-role') === 'admin' ? 'admin' : 'user';
|
||
var template = document.querySelector('[data-testid="mnote-admin-access-policy-template-' + role + '"]');
|
||
var dialog = document.createElement('div');
|
||
dialog.className = 'mnote-admin-policy-modal';
|
||
dialog.setAttribute('data-testid', 'mnote-admin-access-policy-modal');
|
||
dialog.setAttribute('role', 'dialog');
|
||
dialog.setAttribute('aria-modal', 'true');
|
||
dialog.setAttribute('aria-labelledby', 'mnote-admin-policy-dialog-title');
|
||
dialog.innerHTML =
|
||
'<div class="mnote-admin-policy-modal__backdrop" data-admin-access-policy-close></div>' +
|
||
'<section class="mnote-admin-policy-modal__panel">' +
|
||
'<button type="button" class="mnote-admin-policy-modal__close" data-admin-access-policy-close aria-label="关闭授权管理"><span class="material-symbols-outlined" data-icon="close" aria-hidden="true"></span></button>' +
|
||
'<div class="mnote-admin-policy-modal__content" data-testid="mnote-admin-access-policy-modal-content"></div>' +
|
||
'</section>';
|
||
var content = dialog.querySelector('[data-testid="mnote-admin-access-policy-modal-content"]');
|
||
if (content) {
|
||
content.innerHTML = template ? template.innerHTML : '<div class="mnote-admin-policy-empty">授权管理面板加载失败</div>';
|
||
}
|
||
if (typeof window.MNOTEInitAccessPolicyPanel === 'function') {
|
||
window.MNOTEInitAccessPolicyPanel(dialog);
|
||
}
|
||
dialog.querySelectorAll('[data-admin-access-policy-close]').forEach(function(button) {
|
||
button.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
closeAdminAccessPolicyDialog();
|
||
});
|
||
});
|
||
document.body.appendChild(dialog);
|
||
document.documentElement.setAttribute('data-mnote-admin-access-policy-modal-open', 'true');
|
||
var closeButton = dialog.querySelector('.mnote-admin-policy-modal__close');
|
||
if (closeButton instanceof HTMLElement) closeButton.focus();
|
||
}
|
||
|
||
async function signOutAccount(trigger) {
|
||
setCommandPending(trigger, true);
|
||
try {
|
||
var response = await fetch('/api/auth', {
|
||
method: 'POST',
|
||
headers: { 'content-type': 'application/json' },
|
||
credentials: 'include',
|
||
body: JSON.stringify({ action: 'auth:signOut', args: {} })
|
||
});
|
||
var payload = await response.json().catch(function() { return {}; });
|
||
payload = payload || {};
|
||
if (!response.ok || payload.error) {
|
||
throw new Error(payload.error || '退出登录失败');
|
||
}
|
||
window.location.assign('/auth');
|
||
} catch (error) {
|
||
var menu = document.querySelector('[data-testid="mnote-account-menu"]');
|
||
var errorNode = menu ? menu.querySelector('[data-account-error]') : null;
|
||
if (errorNode instanceof HTMLElement) {
|
||
errorNode.textContent = error && error.message ? error.message : '退出登录失败';
|
||
errorNode.hidden = false;
|
||
}
|
||
setCommandPending(trigger, false);
|
||
}
|
||
}
|
||
|
||
function openAccountMenu(trigger) {
|
||
closeAccountMenu();
|
||
if (!(trigger instanceof HTMLElement)) return;
|
||
trigger.setAttribute('aria-expanded', 'true');
|
||
var menu = document.createElement('div');
|
||
menu.className = 'mnote-workspace-source-menu mnote-account-menu';
|
||
menu.setAttribute('data-testid', 'mnote-account-menu');
|
||
menu.setAttribute('role', 'menu');
|
||
menu.innerHTML =
|
||
'<button type="button" class="mnote-workspace-source-menu__item mnote-account-menu__item" data-testid="mnote-account-profile" role="menuitem"><span class="material-symbols-outlined" data-icon="account_circle" aria-hidden="true"></span><span>个人信息</span></button>' +
|
||
'<button type="button" class="mnote-workspace-source-menu__item mnote-account-menu__item" data-testid="mnote-account-access-policy" role="menuitem" hidden><span class="material-symbols-outlined" data-icon="admin_panel_settings" aria-hidden="true"></span><span>授权管理</span></button>' +
|
||
'<button type="button" class="mnote-workspace-source-menu__item mnote-account-menu__item" data-testid="mnote-account-ai-management" role="menuitem" hidden><span class="material-symbols-outlined" data-icon="smart_toy" aria-hidden="true"></span><span>AI 管理</span></button>' +
|
||
'<button type="button" class="mnote-workspace-source-menu__item mnote-account-menu__logout" data-testid="mnote-account-sign-out" role="menuitem">退出登录</button>' +
|
||
'<div class="mnote-account-menu__error" data-account-error hidden></div>';
|
||
var sessionPromise = fetchAccountSession();
|
||
var profileButton = menu.querySelector('[data-testid="mnote-account-profile"]');
|
||
if (profileButton) {
|
||
profileButton.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
sessionPromise.then(openProfileDialog);
|
||
});
|
||
}
|
||
var accessPolicyLink = menu.querySelector('[data-testid="mnote-account-access-policy"]');
|
||
sessionPromise.then(function(session) {
|
||
if (!(accessPolicyLink instanceof HTMLElement)) return;
|
||
accessPolicyLink.hidden = false;
|
||
accessPolicyLink.setAttribute('data-access-policy-role', sessionIsAdmin(session) ? 'admin' : 'user');
|
||
});
|
||
if (accessPolicyLink) {
|
||
accessPolicyLink.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
openAdminAccessPolicyDialog(accessPolicyLink);
|
||
});
|
||
}
|
||
var aiManagementLink = menu.querySelector('[data-testid="mnote-account-ai-management"]');
|
||
sessionPromise.then(function(session) {
|
||
if (!(aiManagementLink instanceof HTMLElement)) return;
|
||
aiManagementLink.hidden = false;
|
||
aiManagementLink.setAttribute('data-ai-management-role', sessionIsAdmin(session) ? 'admin' : 'user');
|
||
});
|
||
if (aiManagementLink) {
|
||
aiManagementLink.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
sessionPromise.then(function(session) {
|
||
window.location.assign(sessionIsAdmin(session) ? '/admin/ai' : '/user/ai');
|
||
});
|
||
});
|
||
}
|
||
var signOutButton = menu.querySelector('[data-testid="mnote-account-sign-out"]');
|
||
if (signOutButton) {
|
||
signOutButton.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
void signOutAccount(signOutButton);
|
||
});
|
||
}
|
||
var host = trigger.closest('[data-testid="wolai-sidebar-quick-actions"]') || trigger.parentElement;
|
||
if (host) host.appendChild(menu);
|
||
}
|
||
|
||
|
||
return {
|
||
activeSidebarTreeMode,
|
||
autoOpenRecentLocalRootOnHome,
|
||
closeAccountMenu,
|
||
closeLocalFolderDialog,
|
||
closeTrashModal,
|
||
closeWorkspaceSourceMenu,
|
||
copyWorkspaceSourceParams,
|
||
createDefaultLocalWorkspace,
|
||
currentRootUri,
|
||
currentSourceKind,
|
||
currentWorkspaceId,
|
||
currentWorkspaceSourcePayload,
|
||
normalizeSidebarTreeMode,
|
||
openAccountMenu,
|
||
openTrashModal,
|
||
openWorkspaceSourceMenu,
|
||
persistSidebarTreeMode,
|
||
readStoredSidebarTreeMode,
|
||
requestOpenLocalFolder,
|
||
resolveWorkspaceId,
|
||
};
|
||
};
|