import { createSidebarWorkspaceRuntime } from './sidebar-workspace-runtime.js'; import { createSidebarPageTreeRuntime } from './sidebar-page-tree-runtime.js'; import { createSidebarTreeLiveApplyRuntime } from './sidebar-tree-live-apply-runtime.js'; import { createSidebarFileTreeOpenRuntime } from './sidebar-filetree-open-runtime.js'; import { createSidebarAttachmentOpenRuntime } from './sidebar-attachment-open-runtime.js'; import { createSidebarFileTreeCommandRuntime } from './sidebar-filetree-command-runtime.js'; import { createSidebarFileTreeUploadRuntime } from './sidebar-filetree-upload-runtime.js'; import { createSidebarPageAiRuntime } from './sidebar-page-ai-runtime.js'; import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtime.js'; (function(){ if (window.__mnoteSidebarTreeRuntimeStarted) return; window.__mnoteSidebarTreeRuntimeStarted = true; var FILETREE_DRAG_MIME = 'application/x-mnote-filetree-row-ids'; var MNOTE_SIDEBAR_TREE_MODE_KEY = 'mnote.sidebar.tree.mode'; var MNOTE_RECENT_LOCAL_ROOTS_KEY_PREFIX = 'mnote.localFolder.recentRoots:'; var MNOTE_LAST_CLOUD_WORKSPACE_KEY = 'mnote.workspace.lastCloudWorkspaceId'; var MNOTE_GLOBAL_SHOW_HEADING_NUMBERS_KEY = 'mnote.global.showHeadingNumbers'; var mnoteNavigationInFlight = ''; var draggingFileTreeRowIds = []; var activeFileTreeDropRow = null; var sidebarFileTreeClipboard = null; var sidebarFileTreeSelection = { selectedRowIds: new Set(), anchorRowId: null, focusedRowId: null }; var activeTreeContextMenu = null; var pageUiState = { pageOptions: null, pageWidthPreferences: null, historySnapshots: [], pageSettingsOpen: false, localIndexSummary: { scopeKey: '', loading: false, error: '', backlinks: null, tags: null } }; function closestAction(target, selector) { var node = target && target.nodeType === Node.TEXT_NODE ? target.parentElement : target; return node && typeof node.closest === 'function' ? node.closest(selector) : null; } function sidebarShellRuntimeFunction(name) { var runtime = window.__mnoteSidebarShellRuntime; return runtime && typeof runtime[name] === 'function' ? runtime[name] : null; } function toggleWorkspaceSidebar(trigger) { var runtimeFn = sidebarShellRuntimeFunction('toggleWorkspaceSidebar'); if (runtimeFn) return runtimeFn(trigger); } function installWorkspaceSidebarResizer() { var runtimeFn = sidebarShellRuntimeFunction('installWorkspaceSidebarResizer'); if (runtimeFn) return runtimeFn(); } function escapeHtml(value) { return String(value == null ? '' : value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function cssEscape(value) { if (window.CSS && typeof window.CSS.escape === 'function') return window.CSS.escape(value); return String(value).replace(/["\\]/g, '\\$&'); } function parseJsonScript(id) { var node = document.getElementById(id); if (!node) return null; try { return JSON.parse(node.textContent || 'null'); } catch (_) { return null; } } function currentDocumentId() { var match = window.location.pathname.match(/^\/documents\/([^\/]+)/); if (!match) match = window.location.pathname.match(/^\/mindmap\/([^\/]+)\/([^\/]+)/); if (match) return decodeURIComponent(match[1]); var params = new URLSearchParams(window.location.search); var fromQuery = (params.get('documentId') || params.get('pageId') || '').trim(); if (fromQuery) return fromQuery; var activePane = document.querySelector('.document-pane[data-pane-role="primary"][data-pane-document-id], .document-pane[data-pane-visible="true"][data-pane-document-id]'); if (activePane instanceof HTMLElement) { var paneDocumentId = (activePane.getAttribute('data-pane-document-id') || '').trim(); if (paneDocumentId) return paneDocumentId; } var shell = document.querySelector('.document-shell[data-document-id]'); if (shell instanceof HTMLElement) return (shell.getAttribute('data-document-id') || '').trim(); return ''; } function localFolderSelfChangeSuppressions() { window.__mnoteLocalFolderSelfChangeSuppressions = window.__mnoteLocalFolderSelfChangeSuppressions || new Map(); return window.__mnoteLocalFolderSelfChangeSuppressions; } function persistLocalFolderSelfChangeSuppression(documentId, expiresAt) { var doc = String(documentId || '').trim(); if (!doc) return; localFolderSelfChangeSuppressions().set(doc, expiresAt); try { var key = 'mnote.localFolder.selfChangeSuppressions.v1'; var existing = JSON.parse(window.sessionStorage.getItem(key) || '{}'); existing[doc] = expiresAt; window.sessionStorage.setItem(key, JSON.stringify(existing)); } catch (_) {} } var EDITOR_UPLOAD_ROOT_SELECTOR = '[data-editor-host-kind="leptos_tiptap_island"], [data-editor-host-kind="leptos_tiptap_resource"]'; function editorUploadRootFromElement(target) { if (!(target instanceof Element)) return null; var root = target.closest(EDITOR_UPLOAD_ROOT_SELECTOR); return root instanceof HTMLElement ? root : null; } function rememberEditorUploadRootFromTarget(target) { var root = editorUploadRootFromElement(target); if (root instanceof HTMLElement) { window.__mnoteLastEditorUploadRoot = root; return root; } return null; } document.addEventListener('pointerdown', function(event) { rememberEditorUploadRootFromTarget(event.target); }, true); document.addEventListener('focusin', function(event) { rememberEditorUploadRootFromTarget(event.target); }, true); function currentFileTreeActiveRowId() { var explicitRowId = new URL(window.location.href).searchParams.get('restoreFocusRowId') || ''; if (explicitRowId) return explicitRowId; var mindmapMatch = window.location.pathname.match(/^\/mindmap\/([^\/]+)\/([^\/]+)/); if (mindmapMatch) return 'asset:' + decodeURIComponent(mindmapMatch[2]); var documentId = currentDocumentId(); return documentId ? 'doc:' + documentId : ''; } function currentPageAggregate() { return parseJsonScript('__MNOTE_PAGE_AGGREGATE__') || {}; } const sidebarWorkspace = createSidebarWorkspaceRuntime({ cssEscape, currentDocumentId, escapeHtml, parseJsonScript, setCommandPending: (...args) => setCommandPending(...args), sidebarShellRuntimeFunction, }); const normalizeSidebarTreeMode = (...args) => sidebarWorkspace.normalizeSidebarTreeMode(...args); const readStoredSidebarTreeMode = (...args) => sidebarWorkspace.readStoredSidebarTreeMode(...args); const persistSidebarTreeMode = (...args) => sidebarWorkspace.persistSidebarTreeMode(...args); const activeSidebarTreeMode = (...args) => sidebarWorkspace.activeSidebarTreeMode(...args); const resolveWorkspaceId = (...args) => sidebarWorkspace.resolveWorkspaceId(...args); const currentWorkspaceId = (...args) => sidebarWorkspace.currentWorkspaceId(...args); const currentSourceKind = (...args) => sidebarWorkspace.currentSourceKind(...args); const currentRootUri = (...args) => sidebarWorkspace.currentRootUri(...args); const copyWorkspaceSourceParams = (...args) => sidebarWorkspace.copyWorkspaceSourceParams(...args); const currentWorkspaceSourcePayload = (...args) => sidebarWorkspace.currentWorkspaceSourcePayload(...args); const openTrashModal = (...args) => sidebarWorkspace.openTrashModal(...args); const closeTrashModal = (...args) => sidebarWorkspace.closeTrashModal(...args); const closeLocalFolderDialog = (...args) => sidebarWorkspace.closeLocalFolderDialog(...args); const requestOpenLocalFolder = (...args) => sidebarWorkspace.requestOpenLocalFolder(...args); const createDefaultLocalWorkspace = (...args) => sidebarWorkspace.createDefaultLocalWorkspace(...args); const closeWorkspaceSourceMenu = (...args) => sidebarWorkspace.closeWorkspaceSourceMenu(...args); const openWorkspaceSourceMenu = (...args) => sidebarWorkspace.openWorkspaceSourceMenu(...args); const closeAccountMenu = (...args) => sidebarWorkspace.closeAccountMenu(...args); const openAccountMenu = (...args) => sidebarWorkspace.openAccountMenu(...args); sidebarWorkspace.autoOpenRecentLocalRootOnHome(); const sidebarPageSettings = createSidebarPageSettingsRuntime({ currentDocumentId, currentPageAggregate, currentRootUri, currentSourceKind, currentWorkspaceSourcePayload, escapeHtml, globalShowHeadingNumbersKey: MNOTE_GLOBAL_SHOW_HEADING_NUMBERS_KEY, pageUiState, resolveWorkspaceId, searchText, }); const applyPageOptionsToShell = (...args) => sidebarPageSettings.applyPageOptionsToShell(...args); const closePageHistoryDrawer = (...args) => sidebarPageSettings.closePageHistoryDrawer(...args); const closePageSettingsPopover = (...args) => sidebarPageSettings.closePageSettingsPopover(...args); const closePageShareDialog = (...args) => sidebarPageSettings.closePageShareDialog(...args); const currentPageOptions = (...args) => sidebarPageSettings.currentPageOptions(...args); const ensureHistorySnapshotsSeeded = (...args) => sidebarPageSettings.ensureHistorySnapshotsSeeded(...args); const isPageSettingsOpen = (...args) => sidebarPageSettings.isPageSettingsOpen(...args); const openPageHistoryDrawer = (...args) => sidebarPageSettings.openPageHistoryDrawer(...args); const openPageShareDialog = (...args) => sidebarPageSettings.openPageShareDialog(...args); const pageOptionIsSupported = (...args) => sidebarPageSettings.pageOptionIsSupported(...args); const persistPageOptionsPatch = (...args) => sidebarPageSettings.persistPageOptionsPatch(...args); const persistPageWidthPreference = (...args) => sidebarPageSettings.persistPageWidthPreference(...args); const recordPageHistorySnapshot = (...args) => sidebarPageSettings.recordPageHistorySnapshot(...args); const renderPageSettingsPopover = (...args) => sidebarPageSettings.renderPageSettingsPopover(...args); const setActivePageSettingsTab = (...args) => sidebarPageSettings.setActivePageSettingsTab(...args); const togglePageSettingsPopover = (...args) => sidebarPageSettings.togglePageSettingsPopover(...args); const updatePageSettingsTriggerState = (...args) => sidebarPageSettings.updatePageSettingsTriggerState(...args); const writeGlobalShowHeadingNumbers = (...args) => sidebarPageSettings.writeGlobalShowHeadingNumbers(...args); const loadPageWidthPreferences = (...args) => sidebarPageSettings.loadPageWidthPreferences(...args); function setCommandPending(trigger, pending) { if (!(trigger instanceof HTMLElement)) return; trigger.setAttribute('data-pending', pending ? 'true' : 'false'); if ('disabled' in trigger) { if (pending) trigger.setAttribute('disabled', 'disabled'); else trigger.removeAttribute('disabled'); } } async function dispatchTreeCommand(trigger, body) { setCommandPending(trigger, true); var commandBody = Object.assign({}, currentWorkspaceSourcePayload(), body || {}); try { var response = await fetch('/api/tree/commands', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(commandBody) }); var payload = await response.json().catch(function(){ return null; }); if (!response.ok || !payload || !payload.result) { throw new Error((payload && payload.message) || 'tree_command_failed_' + response.status); } window.dispatchEvent(new CustomEvent('tree:local-command', { detail: { body: commandBody, result: payload.result } })); setCommandPending(trigger, false); return payload.result; } catch (error) { setCommandPending(trigger, false); if (trigger instanceof HTMLElement) { trigger.setAttribute('data-error', error instanceof Error ? error.message : String(error)); } throw error; } } var sidebarPageTree = null; const navigateToDocument = (...args) => sidebarPageTree.navigateToDocument(...args); const createPage = (...args) => sidebarPageTree.createPage(...args); const startPageDrag = (...args) => sidebarPageTree.startPageDrag(...args); const readPageDragNodeId = (...args) => sidebarPageTree.readPageDragNodeId(...args); const clearPageDropFeedback = (...args) => sidebarPageTree.clearPageDropFeedback(...args); const setActivePageDropRow = (...args) => sidebarPageTree.setActivePageDropRow(...args); const clearPageDragState = (...args) => sidebarPageTree.clearPageDragState(...args); const canDropPage = (...args) => sidebarPageTree.canDropPage(...args); const pageDropPosition = (...args) => sidebarPageTree.pageDropPosition(...args); const resolvePageMoveTarget = (...args) => sidebarPageTree.resolvePageMoveTarget(...args); async function createFileTreeFolder(trigger, parentId) { if (currentSourceKind() !== 'local_folder') return false; var workspaceId = resolveWorkspaceId(trigger || document.body); var effectiveParentId = String(parentId || '').trim(); var result = await dispatchTreeCommand(trigger || document.body, { action: 'create_folder', workspaceId: workspaceId, parentId: effectiveParentId || null, title: '新建文件夹' }); document.documentElement.setAttribute('data-mnote-filetree-folder-created', 'true'); document.documentElement.setAttribute('data-mnote-filetree-folder-created-id', commandDocumentId(result, result.id || '')); void refreshLocalFolderSidebarSnapshot(); return true; } function applySidebarTreeTab(mode, shell) { var runtimeFn = sidebarShellRuntimeFunction('applySidebarTreeTab'); if (runtimeFn) return runtimeFn(mode, shell); } function switchSidebarTreeTab(trigger) { var runtimeFn = sidebarShellRuntimeFunction('switchSidebarTreeTab'); if (runtimeFn) return runtimeFn(trigger); } function restoreSidebarTreeTab() { var runtimeFn = sidebarShellRuntimeFunction('restoreSidebarTreeTab'); if (runtimeFn) return runtimeFn(); } const sidebarTreeLiveApply = createSidebarTreeLiveApplyRuntime({ activeSidebarTreeMode, cssEscape, currentDocumentId: (...args) => currentDocumentId(...args), currentFileTreeActiveRowId: (...args) => currentFileTreeActiveRowId(...args), currentRootUri, currentSourceKind, currentWorkspaceId, escapeHtml, fileTreeRuntimeFunction: (...args) => fileTreeRuntimeFunction(...args), localFilePathFromAssetId: (...args) => localFilePathFromAssetId(...args), navigateToDocument, refreshEditorLocalAttachmentExistence: (...args) => refreshEditorLocalAttachmentExistence(...args), restoreSidebarTreeTab, rowTitle: (...args) => rowTitle(...args), schedulePendingLocalFolderRestoreFocus: (...args) => schedulePendingLocalFolderRestoreFocus(...args), shortMindmapFileName: (...args) => shortMindmapFileName(...args), syncSidebarFileTreeSelection: (...args) => syncSidebarFileTreeSelection(...args), }); const updateTitleEverywhere = (...args) => sidebarTreeLiveApply.updateTitleEverywhere(...args); const commandDocumentId = (...args) => sidebarTreeLiveApply.commandDocumentId(...args); sidebarPageTree = createSidebarPageTreeRuntime({ activeSidebarTreeMode, commandDocumentId, copyWorkspaceSourceParams, cssEscape, currentDocumentId, currentRootUri, currentSourceKind, dispatchTreeCommand, normalizeSidebarTreeMode, persistLocalFolderSelfChangeSuppression, persistSidebarTreeMode, refreshLocalFolderSidebarSnapshot: (...args) => refreshLocalFolderSidebarSnapshot(...args), refreshLocalFolderAfterCommand: (...args) => refreshLocalFolderAfterCommand(...args), resolveWorkspaceId, rowCenter: (...args) => rowCenter(...args), selectSidebarFileTreeDocument: (...args) => selectSidebarFileTreeDocument(...args), toggleChildren: (...args) => toggleChildren(...args), dispatchSidebarEvent: (...args) => dispatchSidebarEvent(...args), openPageTreeContextMenu: (...args) => openPageTreeContextMenu(...args), updateTitleEverywhere: (...args) => updateTitleEverywhere(...args), }); const fileTreePageTitle = (...args) => sidebarTreeLiveApply.fileTreePageTitle(...args); const isFileTreePageRow = (...args) => sidebarTreeLiveApply.isFileTreePageRow(...args); const normalizeFileTreePageRenameTitle = (...args) => sidebarTreeLiveApply.normalizeFileTreePageRenameTitle(...args); const validateFileTreeRename = (...args) => sidebarTreeLiveApply.validateFileTreeRename(...args); const removeDocumentRowForMode = (...args) => sidebarTreeLiveApply.removeDocumentRowForMode(...args); const applyCreatedDocumentLocally = (...args) => sidebarTreeLiveApply.applyCreatedDocumentLocally(...args); const localCommandNeedsProjectionRefresh = (...args) => sidebarTreeLiveApply.localCommandNeedsProjectionRefresh(...args); const refreshLocalFolderAfterCommand = (...args) => sidebarTreeLiveApply.refreshLocalFolderAfterCommand(...args); const applyMoveDocumentDelta = (...args) => sidebarTreeLiveApply.applyMoveDocumentDelta(...args); const applyRemoveDocumentDelta = (...args) => sidebarTreeLiveApply.applyRemoveDocumentDelta(...args); const setTreeLiveApplyError = (...args) => sidebarTreeLiveApply.setTreeLiveApplyError(...args); const objectIdentityAttr = (...args) => sidebarTreeLiveApply.objectIdentityAttr(...args); const renderSidebarSnapshot = (...args) => sidebarTreeLiveApply.renderSidebarSnapshot(...args); const refreshLocalFolderSidebarSnapshot = (...args) => sidebarTreeLiveApply.refreshLocalFolderSidebarSnapshot(...args); const startLocalFolderSidebarWatch = (...args) => sidebarTreeLiveApply.startLocalFolderSidebarWatch(...args); const deltaNeedsProjectionRefresh = (...args) => sidebarTreeLiveApply.deltaNeedsProjectionRefresh(...args); const toggleChildren = (...args) => sidebarTreeLiveApply.toggleChildren(...args); const revealFileTreeResource = (...args) => sidebarTreeLiveApply.revealFileTreeResource(...args); function dispatchSidebarEvent(name, detail) { document.documentElement.setAttribute('data-mnote-last-tree-action', name); window.dispatchEvent(new CustomEvent(name, { detail: detail })); } function sidebarShortcutWorkspaceId() { return currentWorkspaceId() || (document.getElementById('sidebar-file-tree-root') && document.getElementById('sidebar-file-tree-root').getAttribute('data-workspace-id') || '') || (document.getElementById('sidebar-tree-root') && document.getElementById('sidebar-tree-root').getAttribute('data-workspace-id') || ''); } function sidebarShortcutSourceKind() { return currentSourceKind() || 'workspace'; } function currentFileTreeScope() { var params = new URLSearchParams(window.location.search); var fromUrl = String(params.get('fileTreeScope') || '').trim(); if (fromUrl) return fromUrl; var root = document.getElementById('sidebar-file-tree-root'); return root instanceof HTMLElement ? String(root.getAttribute('data-mnote-filetree-scope') || '').trim() : ''; } function recordNavigationRecent(payload) { var body = Object.assign({ sourceKind: currentSourceKind() || 'local_folder', rootUri: currentRootUri() || '', }, payload || {}); if (!body.kind || !body.rootUri || !body.title) return Promise.resolve(false); return fetch('/api/navigation/recent', { method: 'POST', credentials: 'include', headers: { accept: 'application/json', 'content-type': 'application/json' }, body: JSON.stringify(body) }).then(function(response) { return response.ok; }).catch(function(error) { console.warn('[mnote navigation] record recent failed', error); return false; }); } function openNavigationPageForFolder(rootUri, relativePath, workspaceId, title, options) { rootUri = String(rootUri || '').trim(); relativePath = String(relativePath || '').trim().replace(/^\/+|\/+$/g, ''); workspaceId = String(workspaceId || '').trim(); title = String(title || relativePath || '本地文件夹').trim(); if (!rootUri) return false; void recordNavigationRecent({ kind: 'folder', sourceKind: 'local_folder', rootUri: rootUri, relativePath: relativePath, title: title, workspaceId: workspaceId }); var targetUrl = new URL('/', window.location.origin); if (workspaceId) targetUrl.searchParams.set('workspaceId', workspaceId); targetUrl.searchParams.set('sourceKind', 'local_folder'); targetUrl.searchParams.set('rootUri', rootUri); targetUrl.searchParams.set('treeView', 'filetree'); if (relativePath) targetUrl.searchParams.set('fileTreeScope', relativePath); var url = targetUrl.pathname + targetUrl.search; if (options && options.newTab) { window.open(url, '_blank', 'noopener'); } else { window.location.assign(url); } return true; } function currentTopbarTitle() { var title = document.querySelector('[data-page-title-current="true"]'); return title && title.textContent ? title.textContent.trim() : '无标题'; } function fileTreeRowTitleForShortcut(row, fallback) { var title = row && row.querySelector ? row.querySelector(':scope > .tree-link > .tree-link-title') : null; return title && title.textContent ? title.textContent.trim() : (fallback || '文件夹'); } function sidebarShortcutRows() { return Array.from(document.querySelectorAll('.wolai-starred-section [data-mnote-shortcut-kind]')); } function shortcutMatches(row, kind, targetId, relativePath, documentId) { if (!(row instanceof HTMLElement)) return false; if ((row.getAttribute('data-mnote-shortcut-kind') || '') !== kind) return false; if (documentId && (row.getAttribute('data-mnote-shortcut-document-id') || row.getAttribute('data-document-id') || '') === documentId) return true; if (relativePath && (row.getAttribute('data-mnote-shortcut-relative-path') || '') === relativePath) return true; return Boolean(targetId && (row.getAttribute('data-mnote-shortcut-target-id') || row.getAttribute('data-node-id') || '') === targetId); } async function listSidebarShortcuts(workspaceId) { var url = new URL('/api/sidebar/shortcuts', window.location.origin); url.searchParams.set('workspaceId', workspaceId); var response = await fetch(url.toString(), { method: 'GET', credentials: 'include', headers: { accept: 'application/json' }, cache: 'no-store' }); var payload = await response.json().catch(function() { return null; }); if (!response.ok) throw new Error(payload && (payload.error || payload.message) || 'sidebar_shortcuts_list_failed_' + response.status); return payload && Array.isArray(payload.shortcuts) ? payload.shortcuts : []; } function shortcutRecordMatches(shortcut, kind, targetId, relativePath, documentId) { if (!shortcut || shortcut.kind !== kind) return false; var shortcutMetadata = shortcut && shortcut.metadata && typeof shortcut.metadata === 'object' ? shortcut.metadata : {}; var shortcutRootUri = String(shortcut.rootUri || shortcut.root_uri || shortcutMetadata.rootUri || shortcutMetadata.root_uri || '').trim(); var currentShortcutRootUri = currentRootUri(); if (shortcutRootUri && currentShortcutRootUri && shortcutRootUri !== currentShortcutRootUri) return false; if (documentId && String(shortcut.documentId || shortcut.document_id || '') === documentId) return true; if (relativePath && String(shortcut.relativePath || shortcut.relative_path || '') === relativePath) return true; return Boolean(targetId && String(shortcut.targetId || shortcut.target_id || '') === targetId); } async function findSidebarShortcut(payload) { var workspaceId = payload.workspaceId || sidebarShortcutWorkspaceId(); if (!workspaceId) return null; var shortcuts = await listSidebarShortcuts(workspaceId); return shortcuts.find(function(shortcut) { return shortcutRecordMatches(shortcut, payload.kind, payload.targetId, payload.relativePath, payload.documentId); }) || null; } async function upsertSidebarShortcut(payload) { var response = await fetch('/api/sidebar/shortcuts', { method: 'POST', credentials: 'include', headers: { accept: 'application/json', 'content-type': 'application/json' }, body: JSON.stringify(payload) }); var result = await response.json().catch(function() { return null; }); if (!response.ok) throw new Error(result && (result.error || result.message) || 'sidebar_shortcut_upsert_failed_' + response.status); return result && result.shortcut ? result.shortcut : null; } async function deleteSidebarShortcut(shortcutId) { if (!shortcutId) return false; var response = await fetch('/api/sidebar/shortcuts/' + encodeURIComponent(shortcutId), { method: 'DELETE', credentials: 'include', headers: { accept: 'application/json' } }); var result = await response.json().catch(function() { return null; }); if (!response.ok) throw new Error(result && (result.error || result.message) || 'sidebar_shortcut_delete_failed_' + response.status); return true; } function closeSidebarShortcutMenu() { var existing = document.querySelector('[data-testid="mnote-sidebar-shortcut-menu"]'); if (existing && existing.parentElement) existing.parentElement.removeChild(existing); document.querySelectorAll('[data-mnote-shortcut-action="menu"][aria-expanded="true"]').forEach(function(button) { button.setAttribute('aria-expanded', 'false'); }); } async function removeSidebarShortcutByRow(row) { if (!(row instanceof HTMLElement)) return false; var shortcutId = String(row.getAttribute('data-mnote-shortcut-id') || '').trim(); if (!shortcutId) return false; row.setAttribute('data-mnote-shortcut-pending', 'true'); try { await deleteSidebarShortcut(shortcutId); row.remove(); closeSidebarShortcutMenu(); document.documentElement.setAttribute('data-mnote-sidebar-shortcut-last-action', 'remove'); return true; } finally { if (row.isConnected) row.removeAttribute('data-mnote-shortcut-pending'); } } function openSidebarShortcutMenu(row, trigger) { if (!(row instanceof HTMLElement)) return; closeSidebarShortcutMenu(); if (trigger instanceof HTMLElement) trigger.setAttribute('aria-expanded', 'true'); var rect = trigger && trigger.getBoundingClientRect ? trigger.getBoundingClientRect() : row.getBoundingClientRect(); var menu = document.createElement('div'); menu.className = 'mnote-tree-context-menu'; menu.setAttribute('role', 'menu'); menu.setAttribute('data-testid', 'mnote-sidebar-shortcut-menu'); menu.innerHTML = ''; menu.__mnoteShortcutRow = row; document.body.appendChild(menu); var width = menu.offsetWidth || 220; var left = Math.min(Math.max(8, rect.right - width), Math.max(8, window.innerWidth - width - 8)); var top = Math.min(Math.max(8, rect.bottom + 4), Math.max(8, window.innerHeight - (menu.offsetHeight || 120) - 8)); menu.style.left = left + 'px'; menu.style.top = top + 'px'; } function removeSidebarShortcutRow(shortcut) { sidebarShortcutRows().forEach(function(row) { if (shortcutMatches( row, shortcut.kind, String(shortcut.targetId || shortcut.target_id || ''), String(shortcut.relativePath || shortcut.relative_path || ''), String(shortcut.documentId || shortcut.document_id || '') )) { row.remove(); } }); } function renderSidebarShortcutRow(shortcut) { if (!shortcut) return; var section = document.querySelector('.wolai-starred-section'); if (!(section instanceof HTMLElement)) return; removeSidebarShortcutRow(shortcut); var kind = String(shortcut.kind || '').trim(); var shortcutId = String(shortcut.id || shortcut.targetId || shortcut.target_id || '').trim(); var targetId = String(shortcut.targetId || shortcut.target_id || '').trim(); var relativePath = String(shortcut.relativePath || shortcut.relative_path || '').trim(); var documentId = String(shortcut.documentId || shortcut.document_id || '').trim(); var sourceKind = String(shortcut.sourceKind || shortcut.source_kind || '').trim(); var metadata = shortcut.metadata && typeof shortcut.metadata === 'object' ? shortcut.metadata : {}; var rootUri = String(shortcut.rootUri || shortcut.root_uri || metadata.rootUri || metadata.root_uri || '').trim(); var workspaceId = String(shortcut.workspaceId || shortcut.workspace_id || sidebarShortcutWorkspaceId() || '').trim(); var title = String(shortcut.title || (kind === 'folder' ? '文件夹' : '无标题')).trim(); var href = ''; if (documentId) { var targetUrl = new URL('/documents/' + encodeURIComponent(documentId), window.location.origin); if (workspaceId) targetUrl.searchParams.set('workspaceId', workspaceId); if (sourceKind) targetUrl.searchParams.set('sourceKind', sourceKind); if (rootUri) targetUrl.searchParams.set('rootUri', rootUri); href = targetUrl.pathname + targetUrl.search; } var row = document.createElement(href ? 'a' : 'div'); row.className = 'wolai-page-row'; if (href) row.setAttribute('href', href); else { row.setAttribute('role', 'button'); row.setAttribute('tabindex', '0'); } row.setAttribute('data-testid', 'wolai-sidebar-row'); row.setAttribute('data-node-id', shortcutId || targetId); row.setAttribute('data-document-id', documentId || shortcutId || targetId); if (shortcutId) row.setAttribute('data-mnote-shortcut-id', shortcutId); if (workspaceId) row.setAttribute('data-workspace-id', workspaceId); row.setAttribute('data-mnote-shortcut-kind', kind); if (sourceKind) row.setAttribute('data-mnote-shortcut-source-kind', sourceKind); row.setAttribute('data-mnote-shortcut-target-id', targetId); if (relativePath) row.setAttribute('data-mnote-shortcut-relative-path', relativePath); if (rootUri) row.setAttribute('data-mnote-shortcut-root-uri', rootUri); if (documentId) row.setAttribute('data-mnote-shortcut-document-id', documentId); row.setAttribute('data-depth', '0'); row.setAttribute('data-active', String(documentId && documentId === currentDocumentId())); row.innerHTML = '' + escapeHtml(title) + ''; section.appendChild(row); } async function toggleSidebarShortcut(payload) { var workspaceId = payload.workspaceId || sidebarShortcutWorkspaceId(); if (!workspaceId) return false; var normalized = Object.assign({}, payload, { workspaceId: workspaceId }); var existing = await findSidebarShortcut(normalized); if (existing && existing.id) { await deleteSidebarShortcut(existing.id); removeSidebarShortcutRow(existing); document.documentElement.setAttribute('data-mnote-sidebar-shortcut-last-action', 'remove'); return true; } var shortcut = await upsertSidebarShortcut(normalized); renderSidebarShortcutRow(shortcut); document.documentElement.setAttribute('data-mnote-sidebar-shortcut-last-action', 'upsert'); return true; } function currentPageShortcutPayload() { var documentId = currentDocumentId(); var workspaceId = sidebarShortcutWorkspaceId(); if (!documentId || !workspaceId) return null; return { workspaceId: workspaceId, kind: 'page', sourceKind: sidebarShortcutSourceKind(), targetId: documentId, documentId: documentId, title: currentTopbarTitle(), icon: 'star', rootUri: currentRootUri(), metadataJson: JSON.stringify({ rootUri: currentRootUri() }) }; } function folderShortcutPayload(detail, trigger) { detail = detail || {}; var row = trigger && trigger.closest ? trigger.closest('.tree-row[data-shell-mode="filetree"]') : trigger; var rowKind = String(detail.rowKind || (row && row.getAttribute('data-row-kind')) || '').trim(); if (rowKind !== 'folder' && rowKind !== 'directory') return null; var relativePath = String(detail.localRelativePath || (row && row.getAttribute('data-local-relative-path')) || '').trim(); if (!relativePath) return null; var workspaceId = String(detail.workspaceId || sidebarShortcutWorkspaceId() || '').trim(); if (!workspaceId) return null; var rowId = String(detail.rowId || (row && row.getAttribute('data-row-id')) || '').trim(); return { workspaceId: workspaceId, kind: 'folder', sourceKind: 'local_folder', targetId: rowId || ('folder:' + relativePath), relativePath: relativePath, title: detail.title || fileTreeRowTitleForShortcut(row, relativePath), icon: 'folder_open', rootUri: currentRootUri(), metadataJson: JSON.stringify({ rootUri: currentRootUri() }) }; } async function toggleCurrentPageSidebarShortcut(trigger) { var payload = currentPageShortcutPayload(); if (!payload) return; if (trigger instanceof HTMLElement) trigger.setAttribute('data-mnote-shortcut-pending', 'true'); try { await toggleSidebarShortcut(payload); } finally { if (trigger instanceof HTMLElement) trigger.removeAttribute('data-mnote-shortcut-pending'); } } async function toggleFolderSidebarShortcut(detail, trigger) { var payload = folderShortcutPayload(detail, trigger); if (!payload) return false; await toggleSidebarShortcut(payload); return true; } function ensureStarredFolderFileTreeHost(workspaceId) { var root = document.getElementById('sidebar-file-tree-root'); if (root instanceof HTMLElement) return root; var panel = document.getElementById('wolai-sidebar-file-tree-panel'); if (!(panel instanceof HTMLElement)) return null; var section = document.createElement('div'); section.className = 'sidebar-tree-section sidebar-file-tree-section'; root = document.createElement('div'); root.id = 'sidebar-file-tree-root'; root.className = 'sidebar-tree'; root.setAttribute('data-tree-shell-mode', 'filetree'); if (workspaceId) root.setAttribute('data-workspace-id', workspaceId); section.appendChild(root); panel.appendChild(section); return root; } function ensureStarredFolderPageTreeHost(workspaceId) { var root = document.getElementById('sidebar-tree-root'); if (root instanceof HTMLElement) return root; var panel = document.getElementById('wolai-sidebar-page-tree-panel') || document.querySelector('[data-mnote-sidebar-tree-panel="page"]'); if (!(panel instanceof HTMLElement)) return null; var section = document.createElement('div'); section.className = 'sidebar-tree-section'; root = document.createElement('div'); root.id = 'sidebar-tree-root'; root.className = 'sidebar-tree'; root.setAttribute('data-tree-shell-mode', 'page'); if (workspaceId) root.setAttribute('data-workspace-id', workspaceId); section.appendChild(root); panel.appendChild(section); return root; } function persistStarredFolderScope(workspaceId, rootUri, relativePath) { var targetUrl = new URL(window.location.href); if (workspaceId) targetUrl.searchParams.set('workspaceId', workspaceId); targetUrl.searchParams.set('sourceKind', 'local_folder'); targetUrl.searchParams.set('rootUri', rootUri); targetUrl.searchParams.set('treeView', 'filetree'); targetUrl.searchParams.set('fileTreeScope', relativePath); window.history.replaceState(window.history.state, '', targetUrl.pathname + targetUrl.search + targetUrl.hash); if (document.body instanceof HTMLElement) { document.body.setAttribute('data-mnote-source-kind', 'local_folder'); document.body.setAttribute('data-mnote-root-uri', rootUri); } } function readShortcutRootUri(row) { if (!(row instanceof HTMLElement)) return ''; return String(row.getAttribute('data-mnote-shortcut-root-uri') || '').trim(); } async function openStarredFolderShortcut(row) { if (!(row instanceof HTMLElement)) return false; var relativePath = String(row.getAttribute('data-mnote-shortcut-relative-path') || '').trim(); var workspaceId = String(row.getAttribute('data-workspace-id') || '').trim() || sidebarShortcutWorkspaceId(); var rootUri = readShortcutRootUri(row); if (!relativePath || !rootUri || !workspaceId) { document.documentElement.setAttribute('data-mnote-sidebar-shortcut-open-error', !rootUri ? 'missing_root_uri' : 'missing_target'); return false; } document.documentElement.removeAttribute('data-mnote-sidebar-shortcut-open-error'); var tab = document.querySelector('[data-mnote-sidebar-tree-tab="filetree"]'); if (tab instanceof HTMLElement) switchSidebarTreeTab(tab); var root = ensureStarredFolderFileTreeHost(workspaceId); var pageRoot = ensureStarredFolderPageTreeHost(workspaceId); if (root instanceof HTMLElement) { root.setAttribute('data-workspace-id', workspaceId); root.setAttribute('data-mnote-filetree-scope', relativePath); root.setAttribute('data-mnote-filetree-scope-title', row.textContent ? row.textContent.trim() : relativePath); } if (pageRoot instanceof HTMLElement) { pageRoot.setAttribute('data-workspace-id', workspaceId); pageRoot.setAttribute('data-mnote-page-tree-scope', relativePath); } persistStarredFolderScope(workspaceId, rootUri, relativePath); void recordNavigationRecent({ kind: 'folder', sourceKind: 'local_folder', rootUri: rootUri, relativePath: relativePath, title: row.textContent ? row.textContent.trim() : relativePath, workspaceId: workspaceId }); document.documentElement.setAttribute('data-mnote-filetree-scope', relativePath); var sidebarUrl = new URL('/api/tree/projections/sidebar', window.location.origin); sidebarUrl.searchParams.set('workspaceId', workspaceId); sidebarUrl.searchParams.set('sourceKind', 'local_folder'); sidebarUrl.searchParams.set('rootUri', rootUri); sidebarUrl.searchParams.set('parentRelativePath', relativePath); var url = new URL('/api/tree/projections/file', window.location.origin); url.searchParams.set('workspaceId', workspaceId); url.searchParams.set('sourceKind', 'local_folder'); url.searchParams.set('rootUri', rootUri); url.searchParams.set('parentRelativePath', relativePath); var sidebarRequest = fetch(sidebarUrl.toString(), { method: 'GET', credentials: 'include', headers: { accept: 'application/json' }, cache: 'no-store' }); var fileRequest = fetch(url.toString(), { method: 'GET', credentials: 'include', headers: { accept: 'application/json' }, cache: 'no-store' }); var response = await fileRequest; var payload = await response.json().catch(function() { return null; }); if (!response.ok) throw new Error(payload && (payload.error || payload.message) || 'scoped_filetree_failed_' + response.status); var fileProjection = payload && (payload.result || payload); var renderedFile = renderSidebarSnapshot({ dataset: { kernel_file_tree_projection: fileProjection } }); root = document.getElementById('sidebar-file-tree-root'); if (root instanceof HTMLElement) { root.setAttribute('data-workspace-id', workspaceId); root.setAttribute('data-mnote-filetree-scope', relativePath); root.setAttribute('data-mnote-filetree-scope-title', row.textContent ? row.textContent.trim() : relativePath); } var sidebarResponse = await sidebarRequest; var sidebarPayload = await sidebarResponse.json().catch(function() { return null; }); if (!sidebarResponse.ok) throw new Error(sidebarPayload && (sidebarPayload.error || sidebarPayload.message) || 'local_page_tree_failed_' + sidebarResponse.status); var sidebarProjection = sidebarPayload && (sidebarPayload.result || sidebarPayload) || {}; var renderedPage = renderSidebarSnapshot(Object.assign({}, sidebarProjection, { dataset: Object.assign({}, sidebarProjection.dataset || {}) })); root = document.getElementById('sidebar-file-tree-root'); if (root instanceof HTMLElement) { root.setAttribute('data-workspace-id', workspaceId); root.setAttribute('data-mnote-filetree-scope', relativePath); root.setAttribute('data-mnote-filetree-scope-title', row.textContent ? row.textContent.trim() : relativePath); } return renderedFile || renderedPage; } const sidebarFileTreeOpen = createSidebarFileTreeOpenRuntime({ copyWorkspaceSourceParams, currentDocumentId, currentRootUri, currentSourceKind, fileTreeIconKindForFileName: (...args) => fileTreeIconKindForFileName(...args), getNavigationInFlight: () => mnoteNavigationInFlight, isCodeAttachmentFileName: (...args) => isCodeAttachmentFileName(...args), isNonOfficeAttachmentName: (...args) => isNonOfficeAttachmentName(...args), isPdfAttachmentFileName: (...args) => isPdfAttachmentFileName(...args), openCodeEditorAttachment: (...args) => openCodeEditorAttachment(...args), resolveWorkspaceId, setNavigationInFlight: (value) => { mnoteNavigationInFlight = String(value || ''); }, shouldOpenLocalResourceInNewWindow: (...args) => shouldOpenLocalResourceInNewWindow(...args), uploadedFileSize: (...args) => uploadedFileSize(...args), }); const inferOnlyOfficeFileType = (...args) => sidebarFileTreeOpen.inferOnlyOfficeFileType(...args); const buildOnlyOfficeOpenUrl = (...args) => sidebarFileTreeOpen.buildOnlyOfficeOpenUrl(...args); const buildOnlyOfficeOpenPath = (...args) => sidebarFileTreeOpen.buildOnlyOfficeOpenPath(...args); const buildPdfPreviewOpenUrl = (...args) => sidebarFileTreeOpen.buildPdfPreviewOpenUrl(...args); const buildLocalOnlyOfficeOpenUrl = (...args) => sidebarFileTreeOpen.buildLocalOnlyOfficeOpenUrl(...args); const openLocalOfficeFileInActiveTab = (...args) => sidebarFileTreeOpen.openLocalOfficeFileInActiveTab(...args); const buildMindmapOpenPath = (...args) => sidebarFileTreeOpen.buildMindmapOpenPath(...args); const navigateToMindmapObject = (...args) => sidebarFileTreeOpen.navigateToMindmapObject(...args); const isMindmapAssetDetail = (...args) => sidebarFileTreeOpen.isMindmapAssetDetail(...args); const localFilePathFromAssetId = (...args) => sidebarFileTreeOpen.localFilePathFromAssetId(...args); const buildLocalFileOpenUrl = (...args) => sidebarFileTreeOpen.buildLocalFileOpenUrl(...args); const openLocalResourceInActiveTab = (...args) => sidebarFileTreeOpen.openLocalResourceInActiveTab(...args); const readFileTreeObjectIdentity = (...args) => sidebarFileTreeOpen.readFileTreeObjectIdentity(...args); const fetchCurrentOnlyOfficeUserId = (...args) => sidebarFileTreeOpen.fetchCurrentOnlyOfficeUserId(...args); const openConvexAssetFromFileTree = (...args) => sidebarFileTreeOpen.openConvexAssetFromFileTree(...args); var sidebarFileTreeUpload = null; const fileTreeRowsForUploadPreflight = (...args) => sidebarFileTreeUpload.fileTreeRowsForUploadPreflight(...args); const fileTreeRowCapabilities = (...args) => sidebarFileTreeUpload.fileTreeRowCapabilities(...args); const fileTreeRowIsReadonly = (...args) => sidebarFileTreeUpload.fileTreeRowIsReadonly(...args); const blockReadonlyFileTreeAction = (...args) => sidebarFileTreeUpload.blockReadonlyFileTreeAction(...args); const fileTreeDocumentParentsForPreflight = (...args) => sidebarFileTreeUpload.fileTreeDocumentParentsForPreflight(...args); const fileTreeTargetChildrenForPreflight = (...args) => sidebarFileTreeUpload.fileTreeTargetChildrenForPreflight(...args); const fileTreeDropPreflightRows = (...args) => sidebarFileTreeUpload.fileTreeDropPreflightRows(...args); const ensureFileTreeWritableTarget = (...args) => sidebarFileTreeUpload.ensureFileTreeWritableTarget(...args); const fileTreeDocumentWorkspacesForUploadPreflight = (...args) => sidebarFileTreeUpload.fileTreeDocumentWorkspacesForUploadPreflight(...args); const preflightFileTreeUploadTarget = (...args) => sidebarFileTreeUpload.preflightFileTreeUploadTarget(...args); const fallbackFileTreeUploadTarget = (...args) => sidebarFileTreeUpload.fallbackFileTreeUploadTarget(...args); const resolveFileTreeUploadTarget = (...args) => sidebarFileTreeUpload.resolveFileTreeUploadTarget(...args); function localUploadRuntimeFunction(name) { var runtime = window.__mnoteLocalUploadRuntime; var fn = runtime && runtime[name]; return typeof fn === 'function' ? fn : null; } function fileTreeRuntimeFunction(name) { var runtime = window.__mnoteFileTreeRuntime; var fn = runtime && runtime[name]; return typeof fn === 'function' ? fn : null; } function fileTreeRuntimeDeps() { return { currentSourceKind: currentSourceKind, currentRootUri: currentRootUri, currentDocumentId: currentDocumentId, localFilePathFromAssetId: localFilePathFromAssetId, rowTitle: rowTitle, resolveWorkspaceId: resolveWorkspaceId, cssEscape: cssEscape, escapeHtml: escapeHtml, objectIdentityAttr: objectIdentityAttr, uploadedAssetTitle: uploadedAssetTitle, uploadedAssetType: uploadedAssetType, fileTreeIconKindForFileName: fileTreeIconKindForFileName }; } function fileTreeSelectionRuntimeFunction(name) { var runtime = window.__mnoteFileTreeSelectionRuntime; var fn = runtime && runtime[name]; return typeof fn === 'function' ? fn : null; } function fileTreeSelectionRuntimeDeps() { return { cssEscape: cssEscape }; } function uploadedAssetTitle(asset) { var runtimeFn = localUploadRuntimeFunction('uploadedAssetTitle'); if (runtimeFn) return runtimeFn(asset); return String(asset && (asset.file_name || asset.title || asset.name) || '未命名附件').trim() || '未命名附件'; } function uploadedAssetUrl(asset) { var runtimeFn = localUploadRuntimeFunction('uploadedAssetUrl'); if (runtimeFn) return runtimeFn(asset); return String(asset && (asset.sourcePath || asset.file_url || asset.signedUrl || asset.signed_url || asset.thumbnail_url) || '').trim(); } function uploadedAssetMarkdownHref(asset) { var runtimeFn = localUploadRuntimeFunction('uploadedAssetMarkdownHref'); if (runtimeFn) return runtimeFn(asset); var href = String(asset && (asset.markdownHref || asset.markdown_href) || '').trim(); if (href) { var normalizedHref = href.replace(/\\/g, '/'); if (normalizedHref.indexOf('../') === 0 || normalizedHref.indexOf('/../') >= 0) return ''; return href; } var relativePath = String(asset && (asset.markdownRelativePath || asset.markdown_relative_path) || '').trim().replace(/\\/g, '/'); if (!relativePath) return ''; if (relativePath.indexOf('../') === 0 || relativePath.indexOf('/../') >= 0) return ''; if (relativePath.indexOf('./') === 0) return relativePath; return './' + relativePath; } function localAssetOpenUrl(asset, download) { var runtimeFn = localUploadRuntimeFunction('localAssetOpenUrl'); if (runtimeFn) return runtimeFn(asset, download, { rootUri: currentRootUri() }); if (!isLocalUploadedAsset(asset)) return ''; var rootUri = String(asset && (asset.rootUri || asset.root_uri) || '').trim() || currentRootUri(); var rootRelativePath = String(asset && (asset.rootRelativePath || asset.root_relative_path) || '').trim(); if (!rootUri || !rootRelativePath) return ''; var url = new URL('/api/local-folder/files/open', window.location.origin); url.searchParams.set('rootUri', rootUri); url.searchParams.set('path', rootRelativePath); if (download) url.searchParams.set('download', 'true'); return url.toString(); } function uploadedAssetType(asset) { var runtimeFn = localUploadRuntimeFunction('uploadedAssetType'); if (runtimeFn) return runtimeFn(asset); return String(asset && (asset.asset_type || asset.assetType || asset.mime_type || '') || '').trim(); } function fileTreeIconKindForFileName(fileName) { var runtimeFn = localUploadRuntimeFunction('fileTreeIconKindForFileName'); if (runtimeFn) return runtimeFn(fileName); var name = String(fileName || '').trim().toLowerCase(); var ext = name.indexOf('.') >= 0 ? name.split('.').pop() : ''; if (['doc', 'docx', 'odt', 'rtf', 'ppt', 'pptx', 'odp', 'xls', 'xlsx', 'ods', 'csv'].indexOf(ext) >= 0) return 'office'; if (ext === 'md' || ext === 'markdown') return 'markdown'; if (ext === 'pdf') return 'pdf'; if (['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp'].indexOf(ext) >= 0) return 'image'; if (['html', 'htm', 'css', 'scss', 'less', 'js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs', 'vue', 'svelte', 'astro'].indexOf(ext) >= 0) return 'web'; if (['json', 'jsonc', 'json5', 'toml', 'yaml', 'yml', 'ini', 'env', 'xml', 'lock', 'hcl', 'tf', 'tfvars', 'nix', 'properties', 'conf', 'cfg', 'config', 'service', 'desktop', 'gitignore', 'gitattributes', 'editorconfig', 'npmrc', 'prettierrc', 'eslintrc'].indexOf(ext) >= 0 || ['.dockerignore', '.editorconfig', '.env', '.eslintrc', '.gitattributes', '.gitignore', '.npmrc', '.prettierrc', 'dockerfile', 'containerfile', 'makefile', 'cmakelists.txt', 'gemfile', 'rakefile', 'procfile'].indexOf(name) >= 0) return 'config'; if (['rs', 'py', 'go', 'java', 'c', 'cpp', 'h', 'hpp', 'cs', 'php', 'rb', 'sh', 'bash', 'zsh', 'sql', 'lua', 'dart', 'kt', 'kts', 'swift', 'scala', 'gradle', 'groovy', 'clj', 'ex', 'exs', 'erl', 'hrl', 'fs', 'fsx', 'r', 'jl', 'm', 'mm', 'pl', 'pm', 'ps1', 'bat', 'cmd', 'psm1', 'psd1', 'proto', 'graphql', 'gql', 'prisma', 'cmake', 'bazel', 'bzl'].indexOf(ext) >= 0) return 'code'; return ''; } function shouldOpenLocalResourceInNewWindow(fileName) { return false; } function isLocalUploadedAsset(asset) { var runtimeFn = localUploadRuntimeFunction('isLocalUploadedAsset'); if (runtimeFn) return runtimeFn(asset); var id = String(asset && asset.id || '').trim(); return String(asset && asset.sourceKind || '').trim() === 'local_folder' || id.indexOf('local:asset:') === 0; } function uploadedAssetExtension(asset) { var runtimeFn = localUploadRuntimeFunction('uploadedAssetExtension'); if (runtimeFn) return runtimeFn(asset); var match = uploadedAssetTitle(asset).toLowerCase().match(/\.([a-z0-9]+)$/); return match ? match[1] : ''; } function isNonOfficeAttachmentName(name, ext) { var runtimeFn = localUploadRuntimeFunction('isNonOfficeAttachmentName'); if (runtimeFn) return runtimeFn(name, ext); var codeFileNames = [ '.dockerignore', '.editorconfig', '.env', '.eslintrc', '.gitattributes', '.gitignore', '.npmrc', '.prettierrc', 'dockerfile', 'makefile', 'cmakelists.txt', 'gemfile', 'rakefile', 'procfile' ]; return [ 'pdf', 'toml', 'json', 'yaml', 'yml', 'md', 'markdown', 'txt', 'ini', 'env', 'xml', 'html', 'htm', 'css', 'scss', 'less', 'js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs', 'py', 'rs', 'go', 'java', 'c', 'cpp', 'h', 'hpp', 'cs', 'php', 'rb', 'sh', 'bash', 'zsh', 'sql', 'lock', 'log', 'vue', 'svelte', 'astro', 'jsonc', 'json5', 'mts', 'cts', 'lua', 'dart', 'kt', 'kts', 'swift', 'scala', 'gradle', 'groovy', 'clj', 'ex', 'exs', 'erl', 'hrl', 'fs', 'fsx', 'r', 'jl', 'm', 'mm', 'pl', 'pm', 'ps1', 'bat', 'cmd', 'psm1', 'psd1', 'dockerfile', 'containerfile', 'proto', 'graphql', 'gql', 'prisma', 'tf', 'tfvars', 'hcl', 'nix', 'cmake', 'bazel', 'bzl', 'properties', 'conf', 'cfg', 'config', 'service', 'desktop', 'gitignore', 'gitattributes', 'editorconfig', 'npmrc', 'prettierrc', 'eslintrc' ].indexOf(ext) >= 0 || codeFileNames.indexOf(name) >= 0; } function attachmentExtensionFromFileName(fileName) { var runtimeFn = localUploadRuntimeFunction('attachmentExtensionFromFileName'); if (runtimeFn) return runtimeFn(fileName); var name = String(fileName || '').trim().toLowerCase(); return name.indexOf('.') >= 0 ? name.split('.').pop() : ''; } function isPdfAttachmentFileName(fileName) { var runtimeFn = localUploadRuntimeFunction('isPdfAttachmentFileName'); if (runtimeFn) return runtimeFn(fileName); return attachmentExtensionFromFileName(fileName) === 'pdf'; } function isCodeAttachmentFileName(fileName) { var runtimeFn = localUploadRuntimeFunction('isCodeAttachmentFileName'); if (runtimeFn) return runtimeFn(fileName); var name = String(fileName || '').trim().toLowerCase(); var ext = attachmentExtensionFromFileName(name); return ext !== 'pdf' && isNonOfficeAttachmentName(name, ext); } function inferCodeAttachmentLanguage(fileName) { var runtimeFn = localUploadRuntimeFunction('inferCodeAttachmentLanguage'); if (runtimeFn) return runtimeFn(fileName); var name = String(fileName || '').trim().toLowerCase(); var ext = attachmentExtensionFromFileName(name); var byName = { 'dockerfile': 'dockerfile', 'makefile': 'makefile', 'cmakelists.txt': 'cmake', '.gitignore': 'gitignore', '.gitattributes': 'gitattributes', '.editorconfig': 'ini', '.env': 'dotenv' }; if (byName[name]) return byName[name]; var byExt = { bash: 'bash', bat: 'batch', c: 'c', cjs: 'javascript', cmd: 'batch', conf: 'text', cpp: 'cpp', cs: 'csharp', css: 'css', cts: 'typescript', dart: 'dart', dockerfile: 'dockerfile', env: 'dotenv', go: 'go', gql: 'graphql', gradle: 'groovy', graphql: 'graphql', h: 'c', hcl: 'hcl', hpp: 'cpp', htm: 'html', html: 'html', ini: 'ini', java: 'java', js: 'javascript', json: 'json', json5: 'json', jsonc: 'jsonc', jsx: 'javascript', kt: 'kotlin', kts: 'kotlin', less: 'less', log: 'text', lua: 'lua', m: 'objective-c', markdown: 'markdown', md: 'markdown', mjs: 'javascript', mts: 'typescript', nix: 'nix', php: 'php', pl: 'perl', pm: 'perl', prisma: 'prisma', proto: 'protobuf', ps1: 'powershell', py: 'python', r: 'r', rb: 'ruby', rs: 'rust', scss: 'scss', sh: 'bash', sql: 'sql', svelte: 'svelte', swift: 'swift', tf: 'terraform', tfvars: 'terraform', toml: 'toml', ts: 'typescript', tsx: 'typescript', txt: 'text', vue: 'vue', xml: 'xml', yaml: 'yaml', yml: 'yaml', zsh: 'bash' }; return byExt[ext] || 'text'; } function attachmentClassForFileName(fileName) { var runtimeFn = localUploadRuntimeFunction('attachmentClassForFileName'); if (runtimeFn) return runtimeFn(fileName); var name = String(fileName || '').trim().toLowerCase(); var ext = name.indexOf('.') >= 0 ? name.split('.').pop() : ''; if (['doc', 'docx', 'odt', 'rtf'].indexOf(ext) >= 0) return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-word'; if (['ppt', 'pptx', 'odp'].indexOf(ext) >= 0) return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-ppt'; if (['xls', 'xlsx', 'ods', 'csv'].indexOf(ext) >= 0) return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-sheet'; if (ext === 'pdf') return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-pdf'; if (isNonOfficeAttachmentName(name, ext)) { return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-code'; } return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-file'; } function uploadedAttachmentClass(asset) { var runtimeFn = localUploadRuntimeFunction('uploadedAttachmentClass'); if (runtimeFn) return runtimeFn(asset); return attachmentClassForFileName(uploadedAssetTitle(asset)); } function buildOnlyOfficeAssetOpenUrl(asset, userId) { var title = uploadedAssetTitle(asset); var fileType = inferOnlyOfficeFileType(title, asset && asset.mime_type); if (!fileType) return ''; var assetId = String(asset && asset.id || '').trim(); if (isLocalUploadedAsset(asset)) { var localUrl = localAssetOpenUrl(asset, false); if (!localUrl) return ''; return buildOnlyOfficeOpenUrl({ fileUrl: localUrl, fileName: title, fileType: fileType, assetId: assetId, documentId: String(asset && (asset.document_id || asset.documentId) || currentDocumentId() || '').trim(), userId: userId || '', mode: 'view' }); } return buildOnlyOfficeOpenUrl({ fileUrl: assetId ? '' : uploadedAssetUrl(asset), fileName: title, fileType: fileType, assetId: assetId, documentId: String(asset && (asset.document_id || asset.documentId) || currentDocumentId() || '').trim(), userId: userId || '', mode: 'view' }); } function uploadedFileSize(asset) { var runtimeFn = localUploadRuntimeFunction('uploadedFileSize'); if (runtimeFn) return runtimeFn(asset); var size = Number(asset && (asset.file_size || asset.fileSize) || 0); if (!Number.isFinite(size) || size <= 0) return ''; if (size >= 1024 * 1024) return (size / 1024 / 1024).toFixed(size >= 10 * 1024 * 1024 ? 1 : 2) + ' MB'; if (size >= 1024) return (size / 1024).toFixed(size >= 100 * 1024 ? 0 : 2) + ' KB'; return String(Math.round(size)) + ' B'; } var attachmentMetaCache = Object.create(null); var attachmentMetaPending = Object.create(null); var legacyOfficeAttachmentIndex = null; var legacyOfficeAttachmentIndexPending = null; function parseCurrentWorkspaceId() { return (new URLSearchParams(window.location.search).get('workspaceId') || '').trim(); } async function fetchLegacyOfficeAttachmentIndex() { if (legacyOfficeAttachmentIndex) return legacyOfficeAttachmentIndex; if (legacyOfficeAttachmentIndexPending) return legacyOfficeAttachmentIndexPending; var documentId = currentDocumentId(); var workspaceId = parseCurrentWorkspaceId(); if (!documentId || !workspaceId) { legacyOfficeAttachmentIndex = Object.create(null); return legacyOfficeAttachmentIndex; } var projectionUrl = new URL('/api/tree/projections/file', window.location.origin); projectionUrl.searchParams.set('documentId', documentId); projectionUrl.searchParams.set('workspaceId', workspaceId); var sourcePayload = currentWorkspaceSourcePayload(); if (sourcePayload.sourceKind) projectionUrl.searchParams.set('sourceKind', sourcePayload.sourceKind); if (sourcePayload.rootUri) projectionUrl.searchParams.set('rootUri', sourcePayload.rootUri); legacyOfficeAttachmentIndexPending = fetch(projectionUrl.toString(), { method: 'GET', credentials: 'include', cache: 'no-store' }).then(function(response) { return response.json().catch(function() { return null; }).then(function(payload) { var items = payload && payload.ok && payload.result && Array.isArray(payload.result.items) ? payload.result.items : []; var index = Object.create(null); items.forEach(function(item) { if (!item || item.rowKind !== 'asset') return; var title = String(item.title || '').trim(); if (!title || index[title]) return; var fileType = inferOnlyOfficeFileType(title, ''); if (!fileType) return; var rowId = String(item.rowId || '').trim(); var assetId = String(item.assetId || '').trim(); if (!assetId && rowId.indexOf('asset:') === 0) assetId = rowId.slice('asset:'.length); if (!assetId) return; index[title] = { assetId: assetId, fileName: title, fileType: fileType, documentId: documentId }; }); legacyOfficeAttachmentIndex = index; return index; }); }).catch(function() { var empty = Object.create(null); legacyOfficeAttachmentIndex = empty; return empty; }).finally(function() { legacyOfficeAttachmentIndexPending = null; }); return legacyOfficeAttachmentIndexPending; } async function healLegacyOfficeAttachmentParagraphs() { var editor = document.querySelector('.editor-surface .ProseMirror'); if (!(editor instanceof HTMLElement)) return; var paragraphs = Array.from(editor.querySelectorAll('p')); var candidates = paragraphs.filter(function(paragraph) { if (!(paragraph instanceof HTMLParagraphElement)) return; if (paragraph.querySelector('a, img, video, audio, table, iframe, canvas')) return; if (paragraph.childNodes.length !== 1 || paragraph.firstChild?.nodeType !== Node.TEXT_NODE) return; var fileName = String(paragraph.textContent || '').trim(); if (!fileName) return; return Boolean(inferOnlyOfficeFileType(fileName, '')); }); if (!candidates.length) return; var index = await fetchLegacyOfficeAttachmentIndex(); candidates.forEach(function(paragraph) { var fileName = String(paragraph.textContent || '').trim(); var detail = index[fileName]; if (!detail) return; var link = document.createElement('a'); link.textContent = fileName; link.setAttribute('href', buildOnlyOfficeOpenPath({ fileUrl: '', fileName: detail.fileName, fileType: detail.fileType, assetId: detail.assetId, documentId: detail.documentId || currentDocumentId() || '', userId: '', mode: 'view' })); link.setAttribute('data-asset-id', detail.assetId); attachmentClassForFileName(fileName).split(/\s+/).forEach(function(name) { if (name) link.classList.add(name); }); paragraph.replaceChildren(link); enhanceEditorAttachmentLink(link); }); } function applyEditorAttachmentMeta(link, meta) { if (!(link instanceof HTMLAnchorElement) || !meta) return; if (meta.assetId) link.setAttribute('data-asset-id', meta.assetId); if (meta.fileSize) link.setAttribute('data-file-size', meta.fileSize); } async function hydrateEditorAttachmentMeta(link) { if (!(link instanceof HTMLAnchorElement)) return; var detail = detailFromEditorAttachmentLink(link); var assetId = String(detail && detail.assetId || '').trim(); if (!assetId) return; var localFilePath = localFilePathFromAssetId(assetId); if (localFilePath) { var localMeta = { assetId: assetId, fileSize: String(detail && detail.fileSize || '').trim() }; attachmentMetaCache[assetId] = localMeta; applyEditorAttachmentMeta(link, localMeta); return; } if (attachmentMetaCache[assetId]) { applyEditorAttachmentMeta(link, attachmentMetaCache[assetId]); return; } if (attachmentMetaPending[assetId]) { try { await attachmentMetaPending[assetId]; } catch (_) {} applyEditorAttachmentMeta(link, attachmentMetaCache[assetId]); return; } attachmentMetaPending[assetId] = fetch('/api/media/sign?assetId=' + encodeURIComponent(assetId), { method: 'GET', credentials: 'include', cache: 'no-store' }).then(function(response) { return response.json().catch(function() { return null; }).then(function(payload) { if (!response.ok || !payload) return null; var asset = payload.asset && typeof payload.asset === 'object' ? payload.asset : {}; var meta = { assetId: assetId, fileSize: uploadedFileSize(asset) }; attachmentMetaCache[assetId] = meta; return meta; }); }).catch(function() { return null; }).finally(function() { delete attachmentMetaPending[assetId]; }); try { var meta = await attachmentMetaPending[assetId]; applyEditorAttachmentMeta(link, meta); } catch (_) {} } function revealFileTreeRow(row) { var runtimeFn = fileTreeRuntimeFunction('revealFileTreeRow'); if (runtimeFn) return runtimeFn(row, fileTreeRuntimeDeps()); if (!(row instanceof HTMLElement)) return; var node = row.closest('.tree-node'); while (node && node.parentElement) { if (node.parentElement.classList && node.parentElement.classList.contains('tree-children')) { node.parentElement.classList.remove('tree-children--collapsed'); var parentNode = node.parentElement.closest('.tree-node'); var parentRow = parentNode ? parentNode.querySelector(':scope > .tree-row') : null; if (parentRow instanceof HTMLElement) { parentRow.setAttribute('aria-expanded', 'true'); var toggle = parentRow.querySelector('[data-rust-action="toggle"]'); if (toggle) toggle.setAttribute('aria-expanded', 'true'); } } node = node.parentElement.closest('.tree-node'); } try { row.scrollIntoView({ block: 'nearest' }); } catch (_) {} } function revealFileTreeAssetRow(assetId) { var runtimeFn = fileTreeRuntimeFunction('revealFileTreeAssetRow'); if (runtimeFn) return runtimeFn(assetId, fileTreeRuntimeDeps()); if (!assetId) return false; var row = document.querySelector('.tree-row[data-shell-mode="filetree"][data-asset-id="' + cssEscape(assetId) + '"]'); if (!(row instanceof HTMLElement)) return false; revealFileTreeRow(row); return true; } function appendUploadedAssetRow(asset, documentId) { var runtimeFn = fileTreeRuntimeFunction('appendUploadedAssetRow'); if (runtimeFn) return runtimeFn(asset, documentId, fileTreeRuntimeDeps()); var assetId = String(asset && asset.id || '').trim(); if (!assetId) return false; if (revealFileTreeAssetRow(assetId)) return true; var objectKind = String(asset && (asset.objectKind || asset.resourceKind || '') || '').trim(); if (!objectKind && (String(asset && (asset.asset_type || asset.assetType) || '').trim() === 'mindmap' || /\.mindmap\.json$/i.test(assetId))) { objectKind = 'mindmap'; } var targetDocumentId = String(documentId || asset.document_id || asset.documentId || currentDocumentId() || '').trim(); var parentRow = targetDocumentId ? document.querySelector('.tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape('doc:' + targetDocumentId) + '"]') : null; if (!parentRow && currentSourceKind() === 'local_folder' && objectKind === 'mindmap') return false; if (!parentRow) parentRow = document.querySelector('.tree-row[data-shell-mode="filetree"][data-row-kind="document"]'); var root = document.querySelector('#sidebar-file-tree-root .tree-root'); if (!root && !parentRow) return false; var parentLi = parentRow ? parentRow.closest('.tree-node') : null; var children = parentLi ? parentLi.querySelector(':scope > .tree-children') : null; if (parentLi && !children) { children = document.createElement('ul'); children.className = 'tree-children'; parentLi.appendChild(children); } if (children) { children.classList.remove('tree-children--collapsed'); if (parentRow) { parentRow.setAttribute('aria-expanded', 'true'); var toggle = parentRow.querySelector('[data-rust-action="toggle"]'); if (toggle) toggle.setAttribute('aria-expanded', 'true'); } } var container = children || root; var li = document.createElement('li'); li.className = 'tree-node'; var objectIdentity = { objectKind: objectKind || 'attachment', documentId: targetDocumentId || null, blockId: null, assetId: assetId }; li.setAttribute('data-node-id', 'asset:' + assetId); var title = uploadedAssetTitle(asset); var iconKind = fileTreeIconKindForFileName(title) || uploadedAssetType(asset) || 'file'; if (objectKind === 'mindmap') iconKind = 'mindmap'; li.innerHTML = ''; container.appendChild(li); revealFileTreeRow(li.querySelector('.tree-row')); document.documentElement.setAttribute('data-mnote-last-upload-asset-id', assetId); return true; } function removeFileTreeAssetRow(assetId) { var normalized = String(assetId || '').trim(); if (!normalized) return false; var row = document.querySelector('.tree-row[data-shell-mode="filetree"][data-asset-id="' + cssEscape(normalized) + '"]'); var node = row ? row.closest('.tree-node') : null; if (!node || !node.parentElement) return false; node.parentElement.removeChild(node); return true; } function applyAssetsChangedToFileTree(detail) { detail = detail || {}; var docId = String(detail.docId || detail.documentId || currentDocumentId() || '').trim(); var changed = false; var explicitAsset = detail.asset && typeof detail.asset === 'object' ? detail.asset : null; if (explicitAsset) { appendUploadedAssetRow(explicitAsset, docId); changed = true; } var mindmapAssetIds = Array.isArray(detail.mindmapAssetIds) ? detail.mindmapAssetIds : []; if (detail.mindmapDeleted === true) { mindmapAssetIds.forEach(function(assetId) { if (removeFileTreeAssetRow(assetId)) changed = true; }); } if (changed) { document.documentElement.setAttribute('data-mnote-assets-local-applied', 'true'); } } function mindmapAssetFromTarget(documentId, mindmapId, writeResult) { var docId = String(documentId || currentDocumentId() || '').trim(); var assetId = String(mindmapId || '').trim(); if (!docId || !assetId) return null; var result = writeResult && typeof writeResult === 'object' ? writeResult : {}; var resultAssetId = String(result.assetId || result.id || '').trim(); var relativePath = String(result.relativePath || result.rootRelativePath || '').trim(); var fileName = String(result.fileName || '').trim() || shortMindmapFileName(assetId); return { id: resultAssetId || (relativePath ? 'local-file:' + relativePath : assetId), document_id: docId, asset_type: 'mindmap', file_name: fileName, file_url: relativePath || ('/documents/' + encodeURIComponent(docId) + '/' + encodeURIComponent(fileName)), sourcePath: relativePath, rootRelativePath: relativePath, sourceKind: currentSourceKind(), rootUri: currentRootUri(), objectKind: 'mindmap' }; } function shortMindmapFileName(mindmapId) { var runtimeFn = fileTreeRuntimeFunction('shortMindmapFileName'); if (runtimeFn) return runtimeFn(mindmapId); var raw = String(mindmapId || '').trim(); var pathName = raw.indexOf('/') >= 0 ? raw.split('/').pop() : raw; if (/\.json$/i.test(pathName)) return pathName; var digits = raw.match(/(\d{4,})$/); var suffix = digits ? digits[1].slice(-6) : ''; return suffix ? '思维导图' + suffix + '.json' : '思维导图.json'; } function parseMindmapApiTarget(input) { var runtimeFn = fileTreeRuntimeFunction('parseMindmapApiTarget'); if (runtimeFn) return runtimeFn(input); try { var rawUrl = typeof input === 'string' ? input : input && typeof input.url === 'string' ? input.url : ''; if (!rawUrl) return null; var url = new URL(rawUrl, window.location.origin); var match = /^\/api\/mindmap\/([^/]+)\/([^/]+)$/.exec(url.pathname); if (!match) return null; return { documentId: decodeURIComponent(match[1]), mindmapId: decodeURIComponent(match[2]) }; } catch (_) { return null; } } function withLocalMindmapSourceParams(input) { var target = parseMindmapApiTarget(input); if (!target || currentSourceKind() !== 'local_folder') return input; var rootUri = currentRootUri(); if (!rootUri) return input; var rawUrl = typeof input === 'string' ? input : input && typeof input.url === 'string' ? input.url : ''; if (!rawUrl) return input; var url = new URL(rawUrl, window.location.origin); url.searchParams.set('sourceKind', 'local_folder'); url.searchParams.set('rootUri', rootUri); if (typeof input === 'string') return url.pathname + url.search; if (typeof Request !== 'undefined' && input instanceof Request) { return new Request(url.toString(), input); } return url.toString(); } function requestMethod(input, init) { var runtimeFn = fileTreeRuntimeFunction('requestMethod'); if (runtimeFn) return runtimeFn(input, init); return String( init && init.method ? init.method : input && typeof input.method === 'string' ? input.method : 'GET' ).toUpperCase(); } function requestBodyHasMindmapCreateOnly(init) { var runtimeFn = fileTreeRuntimeFunction('requestBodyHasMindmapCreateOnly'); if (runtimeFn) return runtimeFn(init); var body = init && typeof init.body === 'string' ? init.body : ''; if (!body) return false; try { var payload = JSON.parse(body); return payload && payload.createOnly === true; } catch (_) { return false; } } function applyMindmapApiMutationToFileTree(target, payload) { if (!target || !target.documentId || !target.mindmapId) return; var asset = mindmapAssetFromTarget(target.documentId, target.mindmapId, payload && payload.writeResult); if (!asset) return; var appended = appendUploadedAssetRow(asset, target.documentId); if (!appended && currentSourceKind() === 'local_folder') void refreshLocalFolderSidebarSnapshot(); document.documentElement.setAttribute('data-mnote-assets-local-applied', 'true'); document.documentElement.setAttribute('data-mnote-last-mindmap-asset-id', asset.id || target.mindmapId); } function installMindmapAssetFetchObserver() { if (window.__mnoteMindmapAssetFetchObserverInstalled === true) return; if (typeof window.fetch !== 'function') return; window.__mnoteMindmapAssetFetchObserverInstalled = true; var originalFetch = window.fetch.bind(window); window.fetch = function(input, init) { var nextInput = withLocalMindmapSourceParams(input); var target = parseMindmapApiTarget(nextInput); var method = requestMethod(input, init); var createOnly = requestBodyHasMindmapCreateOnly(init || {}); return originalFetch(nextInput, init).then(function(response) { if (target && method === 'POST' && response && response.ok) { var cloned = response.clone(); void cloned.json().then(function(payload) { if (createOnly || target.documentId === currentDocumentId()) { applyMindmapApiMutationToFileTree(target, payload); } }).catch(function() { if (createOnly || target.documentId === currentDocumentId()) { applyMindmapApiMutationToFileTree(target, null); } }); } return response; }); }; } async function archiveLocalFileTreeAsset(row, assetId) { if (!assetId) return false; await dispatchTreeCommand(row || document.body, { action: 'archive', workspaceId: resolveWorkspaceId(row || document.body), documentId: assetId }); removeFileTreeAssetRow(assetId); return true; } async function deleteSingleFileTreeAsset(detail, trigger) { var assetId = String(detail && detail.assetId || '').trim(); if (!assetId) return false; var row = trigger && trigger.closest ? trigger.closest('.tree-row[data-shell-mode="filetree"]') : null; if (!row) row = document.querySelector('.tree-row[data-shell-mode="filetree"][data-asset-id="' + cssEscape(assetId) + '"]'); if (currentSourceKind() === 'local_folder') { return archiveLocalFileTreeAsset(row, assetId); } var documentId = String(detail && detail.documentId || '').trim(); var kind = classifySidebarFileTreeAsset(row); if (kind === 'mindmap') { var response = await fetch('/api/mindmap/' + encodeURIComponent(documentId) + '/' + encodeURIComponent(assetId), { method: 'DELETE' }); if (!response.ok) throw new Error('mindmap_delete_failed_' + response.status); removeFileTreeAssetRow(assetId); return true; } if (kind === 'table') { var tableResponse = await fetch('/api/tables/' + encodeURIComponent(assetId), { method: 'DELETE' }); if (!tableResponse.ok) throw new Error('table_delete_failed_' + tableResponse.status); removeFileTreeAssetRow(assetId); return true; } await postSidebarFileTreeJson('/api/media/batch', { action: 'delete', assetIds: [assetId] }); removeFileTreeAssetRow(assetId); return true; } function resolveEditorUploadContext(detail) { var runtimeFn = localUploadRuntimeFunction('resolveEditorUploadContext'); if (runtimeFn) { return runtimeFn(detail || {}, { rootSelector: EDITOR_UPLOAD_ROOT_SELECTOR, editorUploadRootFromElement: editorUploadRootFromElement, currentDocumentId: currentDocumentId, resolveWorkspaceId: resolveWorkspaceId }); } var root = null; var selector = detail && detail.editorRootSelector ? String(detail.editorRootSelector) : ''; if (selector) { try { var selected = document.querySelector(selector); if (selected instanceof HTMLElement) root = selected; } catch (_) {} } if (!root && window.__mnoteIntendedSlashRoot instanceof HTMLElement && window.__mnoteIntendedSlashRoot.isConnected) { root = window.__mnoteIntendedSlashRoot; } if (!root && window.__mnoteLastEditorUploadRoot instanceof HTMLElement && window.__mnoteLastEditorUploadRoot.isConnected) { root = window.__mnoteLastEditorUploadRoot; } if (!root && document.activeElement instanceof Element) { root = editorUploadRootFromElement(document.activeElement); } if (!root) { var focused = document.querySelector(EDITOR_UPLOAD_ROOT_SELECTOR + ' .ProseMirror:focus-within'); root = editorUploadRootFromElement(focused); } if (!root) { root = document.querySelector('[data-editor-host-kind="leptos_tiptap_island"][data-pane-role="primary"]'); } var pane = root instanceof Element ? root.closest('.document-pane[data-pane-role]') : null; var shell = root instanceof Element ? root.closest('.document-shell[data-document-id]') : null; return { root: root instanceof HTMLElement ? root : null, documentId: String( detail && detail.documentId || (root instanceof HTMLElement && root.getAttribute('data-document-id')) || (pane instanceof HTMLElement && pane.getAttribute('data-pane-document-id')) || (shell instanceof HTMLElement && shell.getAttribute('data-document-id')) || currentDocumentId() || '' ).trim(), workspaceId: String( detail && detail.workspaceId || (root instanceof HTMLElement && root.getAttribute('data-workspace-id')) || (pane instanceof HTMLElement && pane.getAttribute('data-pane-workspace-id')) || (shell instanceof HTMLElement && shell.getAttribute('data-workspace-id')) || resolveWorkspaceId(document.body) || '' ).trim() }; } function editorRootFromUploadOptions(options) { var runtimeFn = localUploadRuntimeFunction('editorRootFromUploadOptions'); if (runtimeFn) { return runtimeFn(options || {}, { rootSelector: EDITOR_UPLOAD_ROOT_SELECTOR, editorUploadRootFromElement: editorUploadRootFromElement }); } if (options && options.editorRoot instanceof HTMLElement) return options.editorRoot; if (window.__mnoteIntendedSlashRoot instanceof HTMLElement && window.__mnoteIntendedSlashRoot.isConnected) { return window.__mnoteIntendedSlashRoot; } if (window.__mnoteLastEditorUploadRoot instanceof HTMLElement && window.__mnoteLastEditorUploadRoot.isConnected) { return window.__mnoteLastEditorUploadRoot; } var focused = document.querySelector(EDITOR_UPLOAD_ROOT_SELECTOR + ' .ProseMirror:focus-within'); return editorUploadRootFromElement(focused); } async function fetchWithTimeout(input, init, timeoutMs, label) { var runtimeFn = localUploadRuntimeFunction('fetchWithTimeout'); if (runtimeFn) { return await runtimeFn(input, init || {}, timeoutMs, label); } var controller = typeof AbortController === 'function' ? new AbortController() : null; var timer = 0; try { if (controller) { timer = window.setTimeout(function() { controller.abort(); }, Math.max(1000, Number(timeoutMs) || 15000)); } var nextInit = Object.assign({}, init || {}); if (controller) nextInit.signal = controller.signal; return await fetch(input, nextInit); } catch (error) { if (error && error.name === 'AbortError') { throw new Error((label || '请求') + '超时'); } throw error; } finally { if (timer) window.clearTimeout(timer); } } async function insertUploadedAssetIntoEditor(asset, targetRoot) { var runtimeFn = localUploadRuntimeFunction('insertUploadedAssetIntoEditor'); if (runtimeFn) { return await runtimeFn(asset, targetRoot, { uploadedAssetTitle: uploadedAssetTitle, localAssetOpenUrl: localAssetOpenUrl, uploadedAssetUrl: uploadedAssetUrl, uploadedAssetType: uploadedAssetType, isLocalUploadedAsset: isLocalUploadedAsset, buildOnlyOfficeAssetOpenUrl: buildOnlyOfficeAssetOpenUrl, fetchCurrentOnlyOfficeUserId: fetchCurrentOnlyOfficeUserId, buildOnlyOfficeOpenPath: buildOnlyOfficeOpenPath, inferOnlyOfficeFileType: inferOnlyOfficeFileType, currentDocumentId: currentDocumentId, currentRootUri: currentRootUri, uploadedAttachmentClass: uploadedAttachmentClass, uploadedFileSize: uploadedFileSize, cssEscape: cssEscape, enhanceEditorAttachmentLinks: enhanceEditorAttachmentLinks }); } var editorRoot = targetRoot instanceof HTMLElement ? targetRoot.querySelector('.editor-surface .ProseMirror') : document.querySelector('.editor-surface .ProseMirror'); if (targetRoot instanceof HTMLElement) window.__mnoteLastEditorUploadRoot = targetRoot; var editor = editorRoot && editorRoot.editor; if (!editor || !editor.chain) { document.documentElement.setAttribute('data-mnote-last-upload-inserted', 'false'); document.documentElement.setAttribute('data-mnote-last-upload-insert-error', 'editor_unavailable'); return false; } var title = uploadedAssetTitle(asset); var markdownHref = uploadedAssetMarkdownHref(asset); var url = markdownHref || localAssetOpenUrl(asset, false) || uploadedAssetUrl(asset); var type = uploadedAssetType(asset); var assetId = String(asset && asset.id || '').trim(); var sizeLabel = uploadedFileSize(asset); try { if (type === 'image' && url) { return editor.chain().focus().setImage({ src: url, alt: title, title: title }).run() === true; } var href = markdownHref || url; if (href) { var inserted = editor.chain().focus().insertContent([ { type: 'paragraph', content: [{ type: 'text', text: title, marks: [{ type: 'link', attrs: { href: href, target: '_blank', rel: 'noopener noreferrer nofollow', class: uploadedAttachmentClass(asset) } }] }] }, { type: 'paragraph' } ]).focus('end').run() === true; if (inserted) { document.documentElement.setAttribute('data-mnote-last-upload-inserted', 'true'); document.documentElement.setAttribute('data-mnote-last-upload-asset-id', assetId || ''); document.documentElement.removeAttribute('data-mnote-last-upload-insert-error'); } else { document.documentElement.setAttribute('data-mnote-last-upload-inserted', 'false'); document.documentElement.setAttribute('data-mnote-last-upload-insert-error', 'insert_content_failed'); } window.setTimeout(function annotateUploadedLink(attempt) { if (targetRoot instanceof HTMLElement) window.__mnoteLastEditorUploadRoot = targetRoot; enhanceEditorAttachmentLinks(); var selector = assetId ? '.editor-surface .ProseMirror a[href*="' + cssEscape(assetId) + '"]' : '.editor-surface .ProseMirror a'; var link = targetRoot instanceof HTMLElement ? targetRoot.querySelector(selector) : document.querySelector(selector); if (!(link instanceof HTMLElement) && assetId) { var scope = targetRoot instanceof HTMLElement ? targetRoot : document; link = Array.from(scope.querySelectorAll('.editor-surface .ProseMirror a')).find(function(candidate) { var href = String(candidate.getAttribute('href') || ''); try { href = decodeURIComponent(href); } catch (_) {} return href.indexOf(assetId) >= 0; }) || null; } if (link instanceof HTMLElement) { if (assetId) link.setAttribute('data-asset-id', assetId); if (sizeLabel) link.setAttribute('data-file-size', sizeLabel); } else if (Number(attempt) < 20) { window.setTimeout(function() { annotateUploadedLink(Number(attempt) + 1); }, 50); } }, 0, 0); return inserted; } } catch (error) { console.warn('[mnote upload] insert uploaded asset failed', error); } return false; } async function uploadFileToMediaAsset(file, plan, options) { var runtimeFn = localUploadRuntimeFunction('uploadFileToMediaAsset'); if (runtimeFn) { return await runtimeFn(file, plan, options || {}, { currentSourceKind: currentSourceKind, currentRootUri: currentRootUri, currentDocumentId: currentDocumentId, editorRootFromUploadOptions: editorRootFromUploadOptions, appendUploadedAssetRow: appendUploadedAssetRow, refreshLocalFolderSidebarSnapshot: refreshLocalFolderSidebarSnapshot, dispatchEvent: function(event) { return window.dispatchEvent(event); }, CustomEvent: CustomEvent, buildOnlyOfficeAssetOpenUrl: buildOnlyOfficeAssetOpenUrl, fetchCurrentOnlyOfficeUserId: fetchCurrentOnlyOfficeUserId, buildOnlyOfficeOpenPath: buildOnlyOfficeOpenPath, inferOnlyOfficeFileType: inferOnlyOfficeFileType, enhanceEditorAttachmentLinks: enhanceEditorAttachmentLinks, cssEscape: cssEscape }); } if (currentSourceKind() === 'local_folder') { var rootUri = currentRootUri(); var documentId = String(plan && plan.targetDocumentId || currentDocumentId() || '').trim(); var hasFolderTarget = plan && Object.prototype.hasOwnProperty.call(plan, 'targetRelativePath'); var uploadIntent = String(plan && plan.uploadIntent || (hasFolderTarget ? 'filetree.folder.drop' : 'editor.markdown.attach')).trim(); if (!rootUri || !uploadIntent) { throw new Error('本地 Markdown 上传缺少 rootUri 或 documentId'); } var localUploadRuntime = localUploadRuntimeFunction('uploadLocalFolderAsset'); var localPayload = null; if (localUploadRuntime) { var localResult = await localUploadRuntime(file, plan, { rootUri: rootUri, documentId: documentId, timeoutMs: 15000 }); localPayload = localResult && localResult.asset ? { asset: localResult.asset } : null; } else { var localForm = new FormData(); localForm.append('file', file); localForm.append('rootUri', rootUri); localForm.append('uploadIntent', uploadIntent); if (documentId) localForm.append('documentId', documentId); if (hasFolderTarget) localForm.append('targetRelativePath', String(plan.targetRelativePath || '')); localForm.append('kind', file && String(file.type || '').indexOf('image/') === 0 ? 'image' : 'attachment'); var localResponse = await fetchWithTimeout('/api/local-folder/assets/upload', { method: 'POST', credentials: 'include', body: localForm }, 15000, '本地上传'); localPayload = await localResponse.json().catch(function() { return null; }); if (!localResponse.ok || !localPayload || !localPayload.asset) { throw new Error(localPayload && localPayload.error ? localPayload.error : '上传失败'); } } if (options && options.insertIntoEditor) { await insertUploadedAssetIntoEditor(localPayload.asset, editorRootFromUploadOptions(options)); } if (!(options && options.insertIntoEditor)) void refreshLocalFolderSidebarSnapshot(); window.dispatchEvent(new CustomEvent('wolai:local-assets-changed', { detail: { docId: documentId, asset: localPayload.asset, assetIds: [localPayload.asset.id] } })); return localPayload.asset; } var mediaUploadRuntime = localUploadRuntimeFunction('uploadMediaAsset'); var payload = null; if (mediaUploadRuntime) { var mediaResult = await mediaUploadRuntime(file, plan, { timeoutMs: 15000 }); payload = mediaResult && mediaResult.asset ? { asset: mediaResult.asset } : null; if (!payload || !payload.asset) { throw new Error('上传失败'); } } else { var form = new FormData(); form.append('file', file); form.append('workspaceId', plan.workspaceId); form.append('documentId', plan.targetDocumentId); if (plan.targetMindmapId) form.append('mindmapId', plan.targetMindmapId); var response = await fetchWithTimeout('/api/media/upload', { method: 'POST', credentials: 'include', body: form }, 15000, '上传'); payload = await response.json().catch(function() { return null; }); if (!response.ok || !payload || !payload.asset) { throw new Error(payload && payload.error ? payload.error : '上传失败'); } } appendUploadedAssetRow(payload.asset, plan.targetDocumentId); if (options && options.insertIntoEditor) { await insertUploadedAssetIntoEditor(payload.asset, editorRootFromUploadOptions(options)); } window.dispatchEvent(new CustomEvent('wolai:assets-changed', { detail: { docId: plan.targetDocumentId, asset: payload.asset, assetIds: [payload.asset.id] } })); return payload.asset; } window.addEventListener('wolai:assets-changed', function(event) { applyAssetsChangedToFileTree(event.detail || {}); }); window.addEventListener('wolai:local-assets-changed', function(event) { applyAssetsChangedToFileTree(event.detail || {}); }); installMindmapAssetFetchObserver(); async function uploadFilesWithResolvedTarget(files, detail, options) { var runtimeFn = localUploadRuntimeFunction('uploadFilesWithResolvedTarget'); if (runtimeFn) { return await runtimeFn(files, detail || {}, options || {}, { resolveFileTreeUploadTarget: resolveFileTreeUploadTarget, uploadFileToMediaAsset: uploadFileToMediaAsset, alert: function(message) { window.alert(message); } }); } var list = Array.from(files || []).filter(Boolean); if (!list.length) return []; var plan = await resolveFileTreeUploadTarget(detail || {}); var uploaded = []; var errors = []; for (var i = 0; i < list.length; i += 1) { try { uploaded.push(await uploadFileToMediaAsset(list[i], plan, options || {})); } catch (error) { errors.push(list[i].name + ': ' + (error && error.message ? error.message : '上传失败')); } } if (errors.length) { window.alert('部分文件上传失败:\n' + errors.slice(0, 6).join('\n') + (errors.length > 6 ? '\n...' : '')); } return uploaded; } function openEditorUploadFilePicker(detail) { var runtimeFn = localUploadRuntimeFunction('openEditorUploadFilePicker'); if (runtimeFn) { return runtimeFn(detail || {}, { rootSelector: EDITOR_UPLOAD_ROOT_SELECTOR, editorUploadRootFromElement: editorUploadRootFromElement, currentDocumentId: currentDocumentId, resolveWorkspaceId: resolveWorkspaceId, uploadFilesWithResolvedTarget: uploadFilesWithResolvedTarget }); } var uploadContext = resolveEditorUploadContext(detail || {}); var input = document.createElement('input'); input.type = 'file'; input.multiple = detail && detail.multiple !== false; if (detail && detail.accept) input.accept = String(detail.accept); input.style.position = 'fixed'; input.style.left = '-9999px'; input.style.top = '-9999px'; document.body.appendChild(input); input.addEventListener('change', function() { var files = Array.from(input.files || []); input.remove(); void uploadFilesWithResolvedTarget(files, { workspaceId: uploadContext.workspaceId || resolveWorkspaceId(document.body), documentId: uploadContext.documentId || currentDocumentId(), targetRowId: null, uploadIntent: 'editor.markdown.attach' }, { insertIntoEditor: detail && detail.insertIntoEditor !== false, editorRoot: uploadContext.root }); }, { once: true }); input.click(); } window.addEventListener('mnote:editor-upload-request', function(event) { openEditorUploadFilePicker(event.detail || {}); }); window.addEventListener('tree.filetree.external-drop', function(event) { var detail = event.detail || {}; void uploadFilesWithResolvedTarget(detail.files || [], detail, { insertIntoEditor: String(detail.documentId || '') === currentDocumentId() }); }); document.addEventListener('dragover', function(event) { var editorTarget = closestAction(event.target, '[data-testid="mnote-leptos-tiptap-editor-stage"], .editor-surface .ProseMirror'); var hasFiles = event.dataTransfer && Array.prototype.indexOf.call(event.dataTransfer.types || [], 'Files') >= 0; if (!editorTarget || !hasFiles) return; event.preventDefault(); event.stopPropagation(); if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy'; }, true); document.addEventListener('drop', function(event) { var editorTarget = closestAction(event.target, '[data-testid="mnote-leptos-tiptap-editor-stage"], .editor-surface .ProseMirror'); var files = event.dataTransfer ? Array.from(event.dataTransfer.files || []) : []; if (!editorTarget || !files.length) return; event.preventDefault(); event.stopPropagation(); void uploadFilesWithResolvedTarget(files, { workspaceId: resolveWorkspaceId(document.body), documentId: currentDocumentId(), targetRowId: null, uploadIntent: 'editor.markdown.attach' }, { insertIntoEditor: true, editorRoot: editorUploadRootFromElement(editorTarget) }); }, true); const sidebarFileTreeCommandState = { get activeTreeContextMenu() { return activeTreeContextMenu; }, set activeTreeContextMenu(value) { activeTreeContextMenu = value; }, get sidebarFileTreeClipboard() { return sidebarFileTreeClipboard; }, set sidebarFileTreeClipboard(value) { sidebarFileTreeClipboard = value; }, }; const sidebarFileTreeCommand = createSidebarFileTreeCommandRuntime({ applyRemoveDocumentDelta, createFileTreeFolder, createPage, cssEscape, currentDocumentId, currentRootUri, currentSourceKind, deleteSingleFileTreeAsset, dispatchSidebarEvent, dispatchTreeCommand, ensureFileTreeWritableTarget, fileTreeRuntimeDeps, fileTreeRuntimeFunction, fileTreeSelectionRuntimeDeps, fileTreeSelectionRuntimeFunction, isFileTreePageRow, localFilePathFromAssetId, normalizeFileTreePageRenameTitle, openConvexAssetFromFileTree, openEditorAttachmentDetail: (...args) => openEditorAttachmentDetail(...args), openEditorAttachmentDownload: (...args) => openEditorAttachmentDownload(...args), openEditorAttachmentEditTab: (...args) => openEditorAttachmentEditTab(...args), openEditorAttachmentNewWindow: (...args) => openEditorAttachmentNewWindow(...args), refreshLocalFolderSidebarSnapshot, removeFileTreeAssetRow, revealFileTreeResource, resolveWorkspaceId, runtimeState: sidebarFileTreeCommandState, selectedSidebarFileTreeSelection: sidebarFileTreeSelection, updateTitleEverywhere, validateFileTreeRename, }); const rowTitle = (...args) => sidebarFileTreeCommand.rowTitle(...args); const rowCenter = (...args) => sidebarFileTreeCommand.rowCenter(...args); const renameFileTreeAsset = (...args) => sidebarFileTreeCommand.renameFileTreeAsset(...args); const beginFileTreeInlineRename = (...args) => sidebarFileTreeCommand.beginFileTreeInlineRename(...args); const closeTreeContextMenu = (...args) => sidebarFileTreeCommand.closeTreeContextMenu(...args); const copyTreeContextValue = (...args) => sidebarFileTreeCommand.copyTreeContextValue(...args); const triggerBrowserDownload = (...args) => sidebarFileTreeCommand.triggerBrowserDownload(...args); const recordFileTreeAction = (...args) => sidebarFileTreeCommand.recordFileTreeAction(...args); const recordFileTreeActionStatus = (...args) => sidebarFileTreeCommand.recordFileTreeActionStatus(...args); const documentHref = (...args) => sidebarFileTreeCommand.documentHref(...args); const convertToPreviousSiblingChild = (...args) => sidebarFileTreeCommand.convertToPreviousSiblingChild(...args); const fileTreeCopyPath = (...args) => sidebarFileTreeCommand.fileTreeCopyPath(...args); const fileTreeMenuTargetParentId = (...args) => sidebarFileTreeCommand.fileTreeMenuTargetParentId(...args); const withOfficeEditModeGuard = (...args) => sidebarFileTreeCommand.withOfficeEditModeGuard(...args); const handleTreeContextMenuAction = (...args) => sidebarFileTreeCommand.handleTreeContextMenuAction(...args); const appendTreeContextMenuButton = (...args) => sidebarFileTreeCommand.appendTreeContextMenuButton(...args); const buildSidebarFileTreeContext = (...args) => sidebarFileTreeCommand.buildSidebarFileTreeContext(...args); const evaluateSidebarFileTreeWhen = (...args) => sidebarFileTreeCommand.evaluateSidebarFileTreeWhen(...args); const openTreeContextMenu = (...args) => sidebarFileTreeCommand.openTreeContextMenu(...args); const openPageTreeContextMenu = (...args) => sidebarFileTreeCommand.openPageTreeContextMenu(...args); const openFileTreeContextMenu = (...args) => sidebarFileTreeCommand.openFileTreeContextMenu(...args); const visibleFileTreeRows = (...args) => sidebarFileTreeCommand.visibleFileTreeRows(...args); const syncSidebarFileTreeSelection = (...args) => sidebarFileTreeCommand.syncSidebarFileTreeSelection(...args); const selectSidebarFileTreeRow = (...args) => sidebarFileTreeCommand.selectSidebarFileTreeRow(...args); const selectSidebarFileTreeDocument = (...args) => sidebarFileTreeCommand.selectSidebarFileTreeDocument(...args); const selectSidebarFileTreeRowById = (...args) => sidebarFileTreeCommand.selectSidebarFileTreeRowById(...args); const pendingLocalFolderRestoreRowId = (...args) => sidebarFileTreeCommand.pendingLocalFolderRestoreRowId(...args); const clearPendingLocalFolderRestoreRowId = (...args) => sidebarFileTreeCommand.clearPendingLocalFolderRestoreRowId(...args); const applyPendingLocalFolderRestoreFocusOnce = (...args) => sidebarFileTreeCommand.applyPendingLocalFolderRestoreFocusOnce(...args); const schedulePendingLocalFolderRestoreFocus = (...args) => sidebarFileTreeCommand.schedulePendingLocalFolderRestoreFocus(...args); const selectedSidebarFileTreeRowIdsForDrag = (...args) => sidebarFileTreeCommand.selectedSidebarFileTreeRowIdsForDrag(...args); const selectedSidebarFileTreeRows = (...args) => sidebarFileTreeCommand.selectedSidebarFileTreeRows(...args); const fileTreeRowDocumentId = (...args) => sidebarFileTreeCommand.fileTreeRowDocumentId(...args); const fileTreeRowAssetId = (...args) => sidebarFileTreeCommand.fileTreeRowAssetId(...args); const decodeLocalEncodedPath = (...args) => sidebarFileTreeCommand.decodeLocalEncodedPath(...args); const fileTreeRowLocalRelativePath = (...args) => sidebarFileTreeCommand.fileTreeRowLocalRelativePath(...args); const fileTreeRowLocalUploadTargetRelativePath = (...args) => sidebarFileTreeCommand.fileTreeRowLocalUploadTargetRelativePath(...args); const readWorkspacePathFromRow = (...args) => sidebarFileTreeCommand.readWorkspacePathFromRow(...args); const fileTreeRowKind = (...args) => sidebarFileTreeCommand.fileTreeRowKind(...args); const isFileTreeDownloadableAssetRow = (...args) => sidebarFileTreeCommand.isFileTreeDownloadableAssetRow(...args); const isFileTreeDownloadableRow = (...args) => sidebarFileTreeCommand.isFileTreeDownloadableRow(...args); const fileTreeAssetDownloadDetail = (...args) => sidebarFileTreeCommand.fileTreeAssetDownloadDetail(...args); const selectedSidebarFileTreeRowsForDownload = (...args) => sidebarFileTreeCommand.selectedSidebarFileTreeRowsForDownload(...args); const selectedSidebarFileTreeAssetRowsForDownload = (...args) => sidebarFileTreeCommand.selectedSidebarFileTreeAssetRowsForDownload(...args); const downloadSelectedFileTreeAssetRows = (...args) => sidebarFileTreeCommand.downloadSelectedFileTreeAssetRows(...args); const hasSelectedDocumentAncestor = (...args) => sidebarFileTreeCommand.hasSelectedDocumentAncestor(...args); const classifySidebarFileTreeAsset = (...args) => sidebarFileTreeCommand.classifySidebarFileTreeAsset(...args); const buildSidebarFileTreeDeletePlan = (...args) => sidebarFileTreeCommand.buildSidebarFileTreeDeletePlan(...args); const sidebarFileTreeDeleteConfirmText = (...args) => sidebarFileTreeCommand.sidebarFileTreeDeleteConfirmText(...args); const postSidebarFileTreeJson = (...args) => sidebarFileTreeCommand.postSidebarFileTreeJson(...args); const fileTreeRowsByRowIds = (...args) => sidebarFileTreeCommand.fileTreeRowsByRowIds(...args); const fileTreeChildCount = (...args) => sidebarFileTreeCommand.fileTreeChildCount(...args); const moveSidebarFileTreeRows = (...args) => sidebarFileTreeCommand.moveSidebarFileTreeRows(...args); const pasteSidebarFileTreeClipboard = (...args) => sidebarFileTreeCommand.pasteSidebarFileTreeClipboard(...args); const deleteSelectedSidebarFileTreeRows = (...args) => sidebarFileTreeCommand.deleteSelectedSidebarFileTreeRows(...args); sidebarFileTreeUpload = createSidebarFileTreeUploadRuntime({ cssEscape, currentDocumentId, fileTreeRuntimeDeps, fileTreeRuntimeFunction, recordFileTreeAction, recordFileTreeActionStatus, resolveWorkspaceId, rowTitle, selectedSidebarFileTreeSelection: sidebarFileTreeSelection, }); function ensureSearchModal() { var existing = document.querySelector('[data-testid="wolai-search-modal"]'); if (existing instanceof HTMLElement) return existing; var overlay = document.createElement('div'); overlay.className = 'wolai-search-overlay'; overlay.setAttribute('data-testid', 'wolai-search-modal'); overlay.setAttribute('role', 'dialog'); overlay.setAttribute('aria-modal', 'true'); overlay.hidden = true; overlay.innerHTML = '' + '
' + '
' + '' + '' + '' + '
' + '' + '
' + '
' + '
'; document.body.appendChild(overlay); var input = overlay.querySelector('[data-testid="wolai-search-input"]'); var closeButton = overlay.querySelector('[data-testid="wolai-search-close"]'); if (input) input.addEventListener('input', scheduleSearchResultsRender); overlay.querySelectorAll('[data-search-switch]').forEach(function(button) { button.addEventListener('click', function() { var isOn = button.getAttribute('aria-checked') !== 'true'; button.setAttribute('aria-checked', isOn ? 'true' : 'false'); button.classList.toggle('is-on', isOn); scheduleSearchResultsRender(); }); }); if (closeButton) closeButton.addEventListener('click', closeSearchModal); overlay.addEventListener('click', function(event) { if (event.target === overlay) closeSearchModal(); }); return overlay; } var activeSearchRequestId = 0; var searchRenderTimer = 0; function searchText(value) { return String(value == null ? '' : value).replace(/\s+/g, ' ').trim(); } function currentWorkspaceName() { var name = document.querySelector('.sidebar-workspace-name'); return searchText(name && name.textContent) || '当前工作区'; } function currentDocumentId() { var shell = document.querySelector('.document-shell[data-document-id]'); var bodyId = document.body && document.body.getAttribute('data-document-id'); return searchText(shell && shell.getAttribute('data-document-id')) || searchText(bodyId); } function searchSwitchValue(overlay, name) { var button = overlay.querySelector('[data-search-switch="' + name + '"]'); return button instanceof HTMLElement && button.getAttribute('aria-checked') === 'true'; } function highlightedHtml(value) { return escapeHtml(value) .replace(/<mark>/g, '') .replace(/<\/mark>/g, ''); } function highlightSearchTitle(title, query) { var cleanTitle = searchText(title); var cleanQuery = searchText(query); if (!cleanQuery) return escapeHtml(cleanTitle); var index = cleanTitle.toLowerCase().indexOf(cleanQuery.toLowerCase()); if (index < 0) return escapeHtml(cleanTitle); return escapeHtml(cleanTitle.slice(0, index)) + '' + escapeHtml(cleanTitle.slice(index, index + cleanQuery.length)) + '' + escapeHtml(cleanTitle.slice(index + cleanQuery.length)); } function renderSearchRecentState(overlay) { var options = overlay.querySelector('[data-testid="wolai-search-options"]'); var meta = overlay.querySelector('[data-testid="wolai-search-result-meta"]'); var results = overlay.querySelector('[data-testid="wolai-search-results"]'); if (options instanceof HTMLElement) options.hidden = true; if (meta) meta.innerHTML = '最近浏览Ctrl + Enter 新窗口打开 / Alt + Enter 右侧边栏打开'; if (results) { results.innerHTML = '
暂无最近浏览
'; } } function scheduleSearchResultsRender() { window.clearTimeout(searchRenderTimer); searchRenderTimer = window.setTimeout(function() { void renderSearchResults(); }, 120); } async function renderSearchResults() { var overlay = document.querySelector('[data-testid="wolai-search-modal"]'); if (!(overlay instanceof HTMLElement)) return; var input = overlay.querySelector('[data-testid="wolai-search-input"]'); var options = overlay.querySelector('[data-testid="wolai-search-options"]'); var meta = overlay.querySelector('[data-testid="wolai-search-result-meta"]'); var results = overlay.querySelector('[data-testid="wolai-search-results"]'); if (!input || !meta || !results) return; var query = searchText(input.value); if (!query) { activeSearchRequestId += 1; renderSearchRecentState(overlay); return; } if (options instanceof HTMLElement) options.hidden = false; var requestId = ++activeSearchRequestId; meta.innerHTML = '搜索中…Ctrl + Enter 新窗口打开 / Alt + Enter 右侧边栏打开'; results.innerHTML = '
搜索中…
'; try { var response = await fetch('/api/search/documents', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ workspaceId: resolveWorkspaceId(document.body), sourceKind: currentSourceKind() || null, rootUri: currentRootUri() || null, documentId: currentDocumentId() || null, query: query, limit: 30, filters: { titleOnly: searchSwitchValue(overlay, 'title'), exact: searchSwitchValue(overlay, 'exact'), includeOcr: false, onlyCurrentPage: searchSwitchValue(overlay, 'page'), timeRange: 'any', timeField: 'updated' } }) }); var payload = await response.json(); if (requestId !== activeSearchRequestId) return; var items = Array.isArray(payload.results) ? payload.results : []; meta.innerHTML = '共 ' + items.length + ' 条匹配结果Ctrl + Enter 新窗口打开 / Alt + Enter 右侧边栏打开'; if (!items.length) { results.innerHTML = '
没有搜索结果
'; return; } results.innerHTML = items.map(function(item) { var title = searchText(item.title || item.name || item.documentTitle || '无标题'); var snippet = searchText(item.snippet || (Array.isArray(item.evidence) && item.evidence[0] && item.evidence[0].snippet) || ''); var path = searchText(item.path || item.parentTitle || item.workspaceName || currentWorkspaceName()); var resourceType = searchText(item.resourceType || item.resourceKind || 'page'); return ''; }).join(''); } catch (error) { if (requestId !== activeSearchRequestId) return; meta.innerHTML = '共 0 条匹配结果Ctrl + Enter 新窗口打开 / Alt + Enter 右侧边栏打开'; results.innerHTML = '
没有搜索结果
'; } } function isSearchModalOpen() { var overlay = document.querySelector('[data-testid="wolai-search-modal"]'); return overlay instanceof HTMLElement && !overlay.hidden; } function openSearchModal() { var overlay = ensureSearchModal(); overlay.hidden = false; document.documentElement.setAttribute('data-mnote-search-modal-open', 'true'); var input = overlay.querySelector('[data-testid="wolai-search-input"]'); if (input) input.setAttribute('placeholder', '在 ' + currentWorkspaceName() + ' 中搜索'); void renderSearchResults(); if (input) { setTimeout(function() { input.focus(); input.select(); }, 0); } } function closeSearchModal() { var overlay = document.querySelector('[data-testid="wolai-search-modal"]'); if (overlay instanceof HTMLElement) overlay.hidden = true; document.documentElement.removeAttribute('data-mnote-search-modal-open'); } function toggleSearchModal() { if (isSearchModalOpen()) closeSearchModal(); else openSearchModal(); } const sidebarPageAi = createSidebarPageAiRuntime({ buildLocalFileOpenUrl, currentDocumentId, currentPageAggregate, currentPageOptions, currentRootUri, currentSourceKind, escapeHtml, cssEscape, openLocalResourceInActiveTab, pageUiState, resolveWorkspaceId, searchText, }); const openPageAiDrawer = (...args) => sidebarPageAi.openPageAiDrawer(...args); const closePageAiDrawer = (...args) => sidebarPageAi.closePageAiDrawer(...args); const isPageAiDrawerOpen = (...args) => sidebarPageAi.isPageAiDrawerOpen(...args); const ensurePageAiDrawer = (...args) => sidebarPageAi.ensurePageAiDrawer(...args); const sendPageAiMessage = (...args) => sidebarPageAi.sendPageAiMessage(...args); const pageAiOpenHermesSettings = (...args) => sidebarPageAi.pageAiOpenHermesSettings(...args); const pageAiStopRun = (...args) => sidebarPageAi.pageAiStopRun(...args); const pageAiLoadGatewayHealth = (...args) => sidebarPageAi.pageAiLoadGatewayHealth(...args); const renderPageAiControls = (...args) => sidebarPageAi.renderPageAiControls(...args); const renderPageAiConversation = (...args) => sidebarPageAi.renderPageAiConversation(...args); const renderPageAiProviderButtons = (...args) => sidebarPageAi.renderPageAiProviderButtons(...args); const renderPageAiSuggestions = (...args) => sidebarPageAi.renderPageAiSuggestions(...args); const pageAiSaveProfileMemory = (...args) => sidebarPageAi.pageAiSaveProfileMemory(...args); const pageAiToggleSkill = (...args) => sidebarPageAi.pageAiToggleSkill(...args); const pageAiToggleTool = (...args) => sidebarPageAi.pageAiToggleTool(...args); const pageAiResumeBackendSession = (...args) => sidebarPageAi.pageAiResumeBackendSession(...args); const pageAiRenameBackendSession = (...args) => sidebarPageAi.pageAiRenameBackendSession(...args); const pageAiDeleteBackendSession = (...args) => sidebarPageAi.pageAiDeleteBackendSession(...args); const pageAiResolvePermission = (...args) => sidebarPageAi.pageAiResolvePermission(...args); const pageAiOpenLocation = (...args) => sidebarPageAi.pageAiOpenLocation(...args); const pageAiSetActiveSession = (...args) => sidebarPageAi.pageAiSetActiveSession(...args); const pageAiStartNewSession = (...args) => sidebarPageAi.pageAiStartNewSession(...args); const pageAiLoadSessions = (...args) => sidebarPageAi.pageAiLoadSessions(...args); const pageAiLoadBackendSessions = (...args) => sidebarPageAi.pageAiLoadBackendSessions(...args); const pageAiCancelQueuedRun = (...args) => sidebarPageAi.pageAiCancelQueuedRun(...args); const pageAiSearchBackendSessions = (...args) => sidebarPageAi.pageAiSearchBackendSessions(...args); const pageAiPersistSessions = (...args) => sidebarPageAi.pageAiPersistSessions(...args); const pageAiLoadProfiles = (...args) => sidebarPageAi.pageAiLoadProfiles(...args); const pageAiLoadProfileMemory = (...args) => sidebarPageAi.pageAiLoadProfileMemory(...args); const pageAiLoadSkills = (...args) => sidebarPageAi.pageAiLoadSkills(...args); const pageAiSwitchProfile = (...args) => sidebarPageAi.pageAiSwitchProfile(...args); const pageAiSetSkillSource = (...args) => sidebarPageAi.pageAiSetSkillSource(...args); const pageAiToggleSkillGroup = (...args) => sidebarPageAi.pageAiToggleSkillGroup(...args); const pageAiSetReasonixMemoryEnabled = (...args) => sidebarPageAi.pageAiSetReasonixMemoryEnabled(...args); const pageAiSetHideHermesBuiltinSkills = (...args) => sidebarPageAi.pageAiSetHideHermesBuiltinSkills(...args); const pageAiSetContextScope = (...args) => sidebarPageAi.pageAiSetContextScope(...args); const pageAiSetAgentId = (...args) => sidebarPageAi.pageAiSetAgentId(...args); const pageAiToggleContextRef = (...args) => sidebarPageAi.pageAiToggleContextRef(...args); const pageAiSetAgentPopoverOpen = (...args) => sidebarPageAi.pageAiSetAgentPopoverOpen(...args); const pageAiSetTargetPopoverOpen = (...args) => sidebarPageAi.pageAiSetTargetPopoverOpen(...args); const pageAiSelectTarget = (...args) => sidebarPageAi.pageAiSelectTarget(...args); const updatePageAiTriggerState = (...args) => sidebarPageAi.updatePageAiTriggerState(...args); const sidebarAttachmentOpen = createSidebarAttachmentOpenRuntime({ attachmentClassForFileName: (...args) => attachmentClassForFileName(...args), buildLocalFileOpenUrl, buildLocalOnlyOfficeOpenUrl, buildOnlyOfficeOpenPath, buildOnlyOfficeOpenUrl, buildPdfPreviewOpenUrl, closestAction, currentDocumentId, currentRootUri, currentWorkspaceSourcePayload, fileTreeIconKindForFileName, healLegacyOfficeAttachmentParagraphs, hydrateEditorAttachmentMeta, inferCodeAttachmentLanguage: (...args) => inferCodeAttachmentLanguage(...args), inferOnlyOfficeFileType, isCodeAttachmentFileName: (...args) => isCodeAttachmentFileName(...args), isPdfAttachmentFileName: (...args) => isPdfAttachmentFileName(...args), localFilePathFromAssetId, openLocalOfficeFileInActiveTab, openLocalResourceInActiveTab, openTreeContextMenu: (...args) => openTreeContextMenu(...args), resolveWorkspaceId, triggerBrowserDownload: (...args) => triggerBrowserDownload(...args), uploadedFileSize, }); const refreshEditorLocalAttachmentExistence = (...args) => sidebarAttachmentOpen.refreshEditorLocalAttachmentExistence(...args); const detailFromEditorAttachmentLink = (...args) => sidebarAttachmentOpen.detailFromEditorAttachmentLink(...args); const enhanceEditorAttachmentLink = (...args) => sidebarAttachmentOpen.enhanceEditorAttachmentLink(...args); const enhanceEditorAttachmentLinks = (...args) => sidebarAttachmentOpen.enhanceEditorAttachmentLinks(...args); const openEditorAttachmentDetail = (...args) => sidebarAttachmentOpen.openEditorAttachmentDetail(...args); const openEditorAttachmentNewWindow = (...args) => sidebarAttachmentOpen.openEditorAttachmentNewWindow(...args); const openEditorAttachmentEditTab = (...args) => sidebarAttachmentOpen.openEditorAttachmentEditTab(...args); const openCodeEditorAttachment = (...args) => sidebarAttachmentOpen.openCodeEditorAttachment(...args); const openEditorAttachmentDownload = (...args) => sidebarAttachmentOpen.openEditorAttachmentDownload(...args); document.addEventListener('click', function(e) { if (activeTreeContextMenu && activeTreeContextMenu.contains(e.target)) return; if (activeTreeContextMenu) closeTreeContextMenu(); var sidebarToggle = closestAction(e.target, '[data-mnote-action="toggle-sidebar"]'); if (sidebarToggle) { e.preventDefault(); toggleWorkspaceSidebar(sidebarToggle); return; } var attachmentAction = closestAction(e.target, '[data-attachment-action]'); if (attachmentAction) { e.preventDefault(); e.stopPropagation(); var link = activeEditorAttachmentLink; if (!(link instanceof HTMLAnchorElement)) return; var attachmentDetail = detailFromEditorAttachmentLink(link); var attachmentActionName = attachmentAction.getAttribute('data-attachment-action') || ''; if (attachmentActionName === 'download') { openEditorAttachmentDownload(attachmentDetail); return; } if (attachmentActionName === 'menu') { openEditorAttachmentMenu(link, attachmentAction); return; } return; } var editorAttachmentLink = closestAction(e.target, '.editor-surface .ProseMirror a.mnote-uploaded-attachment-row'); if (editorAttachmentLink instanceof HTMLAnchorElement) { e.preventDefault(); openEditorAttachmentLink(editorAttachmentLink); return; } var historyClose = closestAction(e.target, '[data-page-history-action="close"]'); if (historyClose) { e.preventDefault(); closePageHistoryDrawer(); return; } var shareClose = closestAction(e.target, '[data-page-share-action="close"]'); if (shareClose) { e.preventDefault(); closePageShareDialog(); return; } var shareCopy = closestAction(e.target, '[data-page-share-action="copy-link"]'); if (shareCopy) { e.preventDefault(); void copyTreeContextValue(window.location.href, 'page-share-link'); return; } var pageHistoryTrigger = closestAction(e.target, '[data-mnote-action="open-page-history"]'); if (pageHistoryTrigger) { e.preventDefault(); openPageHistoryDrawer(); return; } var pageSettingsTrigger = closestAction(e.target, '[data-mnote-action="open-page-settings"]'); if (pageSettingsTrigger) { e.preventDefault(); togglePageSettingsPopover(); return; } var pageSettingsPanel = closestAction(e.target, '.wolai-page-settings-panel'); if (isPageSettingsOpen() && !pageSettingsPanel) { closePageSettingsPopover(); } var pageSettingsTab = closestAction(e.target, '[data-page-settings-tab]'); if (pageSettingsTab) { e.preventDefault(); setActivePageSettingsTab(pageSettingsTab.getAttribute('data-page-settings-tab') || 'page'); return; } var pageSettingsAction = closestAction(e.target, '[data-page-settings-action]'); if (pageSettingsAction) { e.preventDefault(); var actionName = pageSettingsAction.getAttribute('data-page-settings-action') || ''; if (actionName === 'history') { openPageHistoryDrawer(); return; } if (actionName === 'share') { openPageShareDialog(); return; } return; } var pageAiTrigger = closestAction(e.target, '[data-mnote-action="open-page-ai"]'); if (pageAiTrigger) { e.preventDefault(); openPageAiDrawer(); return; } var searchTrigger = closestAction(e.target, '[data-mnote-action="open-search-modal"]'); if (searchTrigger) { e.preventDefault(); toggleSearchModal(); return; } var searchDialog = closestAction(e.target, '.wolai-search-dialog'); if (isSearchModalOpen() && !searchDialog) { closeSearchModal(); return; } var sourceMenuTrigger = closestAction(e.target, '[data-mnote-action="open-workspace-source-menu"]'); if (sourceMenuTrigger) { e.preventDefault(); var existingSourceMenu = document.querySelector('[data-testid="mnote-workspace-source-menu"]'); if (existingSourceMenu) closeWorkspaceSourceMenu(); else openWorkspaceSourceMenu(sourceMenuTrigger); return; } var sourceMenu = closestAction(e.target, '[data-testid="mnote-workspace-source-menu"]'); if (!sourceMenu) closeWorkspaceSourceMenu(); var accountMenuTrigger = closestAction(e.target, '[data-mnote-action="open-account-menu"]'); if (accountMenuTrigger) { e.preventDefault(); var existingAccountMenu = document.querySelector('[data-testid="mnote-account-menu"]'); if (existingAccountMenu) closeAccountMenu(); else openAccountMenu(accountMenuTrigger); return; } var accountMenu = closestAction(e.target, '[data-testid="mnote-account-menu"]'); if (!accountMenu) closeAccountMenu(); var trashTrigger = closestAction(e.target, '[data-mnote-action="open-trash-modal"]'); if (trashTrigger) { e.preventDefault(); openTrashModal(trashTrigger); return; } var trashClose = closestAction(e.target, '[data-mnote-trash-modal-close]'); if (trashClose) { e.preventDefault(); closeTrashModal(); return; } var tabTrigger = closestAction(e.target, '[data-mnote-sidebar-tree-tab]'); if (tabTrigger) { e.preventDefault(); switchSidebarTreeTab(tabTrigger); return; } var pageShortcutTrigger = closestAction(e.target, '[data-mnote-action="toggle-sidebar-shortcut"]'); if (pageShortcutTrigger) { e.preventDefault(); void toggleCurrentPageSidebarShortcut(pageShortcutTrigger); return; } var shortcutMenu = closestAction(e.target, '[data-testid="mnote-sidebar-shortcut-menu"]'); var shortcutMenuAction = closestAction(e.target, '[data-mnote-sidebar-shortcut-menu-action]'); if (shortcutMenuAction) { e.preventDefault(); var action = String(shortcutMenuAction.getAttribute('data-mnote-sidebar-shortcut-menu-action') || '').trim(); var shortcutRowFromMenu = shortcutMenu && shortcutMenu.__mnoteShortcutRow instanceof HTMLElement ? shortcutMenu.__mnoteShortcutRow : null; if (action === 'remove') { void removeSidebarShortcutByRow(shortcutRowFromMenu); return; } if (action === 'open') { closeSidebarShortcutMenu(); if (shortcutRowFromMenu && shortcutRowFromMenu.getAttribute('data-mnote-shortcut-kind') === 'folder') { void openStarredFolderShortcut(shortcutRowFromMenu); } else if (shortcutRowFromMenu && shortcutRowFromMenu.getAttribute('href')) { window.location.assign(shortcutRowFromMenu.getAttribute('href')); } return; } if (action === 'copy-link') { var href = shortcutRowFromMenu && shortcutRowFromMenu.getAttribute('href') || window.location.href; if (navigator.clipboard && href) void navigator.clipboard.writeText(new URL(href, window.location.origin).toString()); closeSidebarShortcutMenu(); return; } } if (!shortcutMenu) closeSidebarShortcutMenu(); var shortcutMenuTrigger = closestAction(e.target, '.wolai-starred-section [data-mnote-shortcut-action="menu"]'); if (shortcutMenuTrigger) { e.preventDefault(); var shortcutRow = shortcutMenuTrigger.closest('[data-mnote-shortcut-id]'); openSidebarShortcutMenu(shortcutRow, shortcutMenuTrigger); return; } var folderShortcutRow = closestAction(e.target, '.wolai-starred-section [data-mnote-shortcut-kind="folder"]'); if (folderShortcutRow) { e.preventDefault(); void openStarredFolderShortcut(folderShortcutRow); return; } var localFolderTrigger = closestAction(e.target, '[data-mnote-action="open-local-folder"]'); if (localFolderTrigger) { e.preventDefault(); requestOpenLocalFolder(); return; } var createLocalWorkspaceTrigger = closestAction(e.target, '[data-mnote-action="create-local-workspace"]'); if (createLocalWorkspaceTrigger) { e.preventDefault(); void createDefaultLocalWorkspace(createLocalWorkspaceTrigger); return; } var createTrigger = closestAction(e.target, '[data-mnote-action="create-page"]'); if (createTrigger) { e.preventDefault(); void createPage(createTrigger, null); return; } var createFolderTrigger = closestAction(e.target, '[data-mnote-action="create-folder"]'); if (createFolderTrigger) { e.preventDefault(); if (currentSourceKind() !== 'local_folder') return; var scope = currentFileTreeScope(); var parentId = scope ? 'local:folder:' + scope : null; void createFileTreeFolder(createFolderTrigger, parentId); return; } var fileTree = document.getElementById('sidebar-file-tree-root'); if (fileTree && fileTree.contains(e.target)) { var fileBtn = closestAction(e.target, '[data-rust-action]'); var fileRow = closestAction(e.target, '.tree-row[data-shell-mode="filetree"]'); if (!fileRow) return; var fileAction = fileBtn ? fileBtn.getAttribute('data-rust-action') : 'open'; var rowId = fileRow.getAttribute('data-row-id') || ''; var rowKind = fileRow.getAttribute('data-row-kind') || ''; var documentId = fileRow.getAttribute('data-document-id') || fileRow.getAttribute('data-doc-id') || ''; var ownerDocumentId = fileRow.getAttribute('data-owner-document-id') || documentId; var assetId = fileRow.getAttribute('data-asset-id') || ''; var assetType = ''; var kindBadge = fileRow.querySelector('.tree-kind-badge'); if (kindBadge instanceof HTMLElement) { assetType = kindBadge.getAttribute('data-kind') || ''; } if (fileAction === 'toggle') { e.preventDefault(); toggleChildren(fileRow, fileBtn); return; } if (fileAction === 'create') { e.preventDefault(); void createPage(fileBtn || fileRow, documentId || fileRow.getAttribute('data-node-id')); return; } if (fileAction === 'menu') { e.preventDefault(); if (rowId && !sidebarFileTreeSelection.selectedRowIds.has(rowId)) { selectSidebarFileTreeRow(fileRow, { ctrlKey: false, metaKey: false, shiftKey: false }); } var point = rowCenter(fileBtn || fileRow); dispatchSidebarEvent('tree.filetree.context-menu', { rowId: rowId, rowKind: rowKind, documentId: documentId || null, assetId: assetId || null }); openFileTreeContextMenu(fileRow, point.x, point.y, fileBtn || fileRow); return; } if (fileAction === 'open' && rowKind === 'folder') { e.preventDefault(); selectSidebarFileTreeRow(fileRow, { ctrlKey: e.ctrlKey, metaKey: e.metaKey, shiftKey: e.shiftKey }); toggleChildren(fileRow, fileRow.querySelector('[data-rust-action="toggle"]')); return; } e.preventDefault(); selectSidebarFileTreeRow(fileRow, { ctrlKey: e.ctrlKey, metaKey: e.metaKey, shiftKey: e.shiftKey }); var objectIdentity = readFileTreeObjectIdentity(fileRow); var workspacePath = readWorkspacePathFromRow(fileRow); dispatchSidebarEvent('tree.filetree.open', { rowId: rowId, rowKind: rowKind, documentId: ownerDocumentId || null, assetId: assetId || null, assetType: assetType || null, objectIdentity: objectIdentity, workspacePath: workspacePath, workspaceId: resolveWorkspaceId(fileRow) }); if (e.shiftKey || e.ctrlKey || e.metaKey) { return; } if ((rowKind === 'document' || rowKind === 'index' || rowKind === 'markdown') && documentId) { void recordNavigationRecent({ kind: 'page', sourceKind: currentSourceKind() || 'local_folder', rootUri: currentRootUri(), relativePath: String(fileRow.getAttribute('data-local-relative-path') || '').trim(), documentId: documentId, title: fileTreeRowTitleForShortcut(fileRow, documentId), workspaceId: resolveWorkspaceId(fileRow) }); navigateToDocument(documentId, resolveWorkspaceId(fileRow), { treeView: 'filetree', fileTreeScope: currentFileTreeScope() }); } else if (assetId) { dispatchSidebarEvent('tree.asset.open', { rowId: rowId, rowKind: rowKind, documentId: ownerDocumentId || null, assetId: assetId, assetType: assetType || null, objectIdentity: objectIdentity, workspacePath: workspacePath, workspaceId: resolveWorkspaceId(fileRow), openTarget: e.shiftKey || e.ctrlKey || e.metaKey ? 'new-window' : 'active-tab' }); } return; } if (sidebarPageTree.handlePageTreeClick(e, { closestAction: closestAction })) return; }); window.addEventListener('tree.sidebarShortcut.toggleFolder', function(event) { var detail = event.detail || {}; var row = detail.rowId ? document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(detail.rowId) + '"]') : null; void toggleFolderSidebarShortcut(detail, row); }); window.addEventListener('tree.filetree.internal-drop', function(event) { var detail = event.detail || {}; var rowIds = Array.isArray(detail.rowIds) ? detail.rowIds : []; if (!rowIds.length) return; var targetRow = detail.targetRowId ? document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(detail.targetRowId) + '"]') : null; recordFileTreeAction('internal-drop', { rowId: detail.targetRowId || '', sourceRowIds: rowIds, copy: Boolean(detail.copy) }); void moveSidebarFileTreeRows(rowIds, targetRow, { copy: Boolean(detail.copy) }); }); document.addEventListener('contextmenu', function(event) { var editorAttachmentLink = closestAction(event.target, '.editor-surface .ProseMirror a.mnote-uploaded-attachment-row'); if (editorAttachmentLink instanceof HTMLAnchorElement) { event.preventDefault(); event.stopPropagation(); enhanceEditorAttachmentLink(editorAttachmentLink); openEditorAttachmentMenu(editorAttachmentLink, editorAttachmentLink, { x: event.clientX, y: event.clientY }); return; } var fileRow = closestAction(event.target, '.tree-row[data-shell-mode="filetree"]'); if (fileRow) { event.preventDefault(); var contextRowId = fileRow.getAttribute('data-row-id') || ''; if (contextRowId && !sidebarFileTreeSelection.selectedRowIds.has(contextRowId)) { selectSidebarFileTreeRow(fileRow, { ctrlKey: false, metaKey: false, shiftKey: false }); } openFileTreeContextMenu(fileRow, event.clientX, event.clientY, fileRow); return; } if (sidebarPageTree.handlePageTreeContextMenu(event, { closestAction: closestAction })) return; }); document.addEventListener('keydown', function(event) { var keyTarget = event.target; var fileTreeRootForKey = document.getElementById('sidebar-file-tree-root'); var fileTreeRowForKey = closestAction(keyTarget, '.tree-row[data-shell-mode="filetree"]'); if (!fileTreeRowForKey && fileTreeRootForKey && fileTreeRootForKey.contains(document.activeElement)) { fileTreeRowForKey = closestAction(document.activeElement, '.tree-row[data-shell-mode="filetree"]'); } if (fileTreeRowForKey) { var _kbRT_ = window.__mnoteFileTreeKeyboardRuntime; if (_kbRT_ && typeof _kbRT_.handleFileTreeKeyDown === 'function') { var handled = _kbRT_.handleFileTreeKeyDown(event, { closestAction: closestAction, beginFileTreeInlineRename: beginFileTreeInlineRename, selectedSidebarFileTreeRows: selectedSidebarFileTreeRows, buildSidebarFileTreeContext: buildSidebarFileTreeContext, evaluateSidebarFileTreeWhen: evaluateSidebarFileTreeWhen, deleteSelectedSidebarFileTreeRows: deleteSelectedSidebarFileTreeRows, pasteSidebarFileTreeClipboard: pasteSidebarFileTreeClipboard, }); if (handled) return; } else { // inline fallback if (event.key === 'F2') { event.preventDefault(); var renameCtx = buildSidebarFileTreeContext('filetree', { targetRow: fileTreeRowForKey }); if (!evaluateSidebarFileTreeWhen(renameCtx, '!workspace.readonly && !editor.dirty')) { return; } beginFileTreeInlineRename(fileTreeRowForKey); return; } if ((event.ctrlKey || event.metaKey) && !event.altKey && event.key) { var shortcutKey = event.key.toLowerCase(); if (shortcutKey === 'c' || shortcutKey === 'x') { var selectedRows = selectedSidebarFileTreeRows(); var selectedRowIds = selectedRows.map(function(row) { return row.getAttribute('data-row-id') || ''; }).filter(Boolean); if (selectedRowIds.length > 0) { event.preventDefault(); sidebarFileTreeClipboard = { action: shortcutKey === 'x' ? 'cut' : 'copy', rowIds: selectedRowIds }; document.documentElement.setAttribute('data-mnote-filetree-clipboard-action', sidebarFileTreeClipboard.action); } return; } if (shortcutKey === 'v') { event.preventDefault(); var pasteCtx = buildSidebarFileTreeContext('filetree', { targetRow: fileTreeRowForKey }); if (!evaluateSidebarFileTreeWhen(pasteCtx, '!workspace.readonly')) { return; } void pasteSidebarFileTreeClipboard(fileTreeRowForKey); return; } } if (event.key === 'Delete' || event.key === 'Backspace') { event.preventDefault(); var delCtx = buildSidebarFileTreeContext('filetree', { targetRow: fileTreeRowForKey }); if (!evaluateSidebarFileTreeWhen(delCtx, '!workspace.readonly && !editor.dirty')) { return; } void deleteSelectedSidebarFileTreeRows(fileTreeRowForKey); return; } } } if (event.key === 'Escape' && isPageSettingsOpen()) { event.preventDefault(); closePageSettingsPopover(); return; } if ((event.metaKey || event.ctrlKey) && !event.shiftKey && event.key && event.key.toLowerCase() === 'p') { event.preventDefault(); toggleSearchModal(); return; } if (event.key === 'Escape') { closeTrashModal(); closeSearchModal(); closeTreeContextMenu(); } }); document.addEventListener('change', function(event) { var globalCheckbox = closestAction(event.target, '[data-global-option-checkbox="showHeadingNumbers"]'); if (globalCheckbox instanceof HTMLInputElement) { writeGlobalShowHeadingNumbers(globalCheckbox.checked); applyPageOptionsToShell(); renderPageSettingsPopover(); return; } var checkbox = closestAction(event.target, '[data-page-option-checkbox]'); if (checkbox instanceof HTMLInputElement) { var key = checkbox.getAttribute('data-page-option-checkbox') || ''; if (!pageOptionIsSupported(key)) return; var patch = {}; patch[key] = checkbox.checked; void persistPageOptionsPatch(patch); return; } var select = closestAction(event.target, '[data-page-option-select]'); if (select instanceof HTMLSelectElement) { var selectKey = select.getAttribute('data-page-option-select') || ''; var next = {}; if (selectKey === 'layoutDensity') next.layoutDensity = select.value; if (selectKey === 'pageFont') next.pageFont = select.value; if (Object.keys(next).length) { void persistPageOptionsPatch(next); } return; } var pageWidthSelect = closestAction(event.target, '[data-page-width-select]'); if (pageWidthSelect instanceof HTMLSelectElement) { var pageWidthType = pageWidthSelect.getAttribute('data-page-width-select') || ''; void persistPageWidthPreference(pageWidthType, pageWidthSelect.value); } }); sidebarPageAi.installPageAiDelegates(); function initializePageUiSurfaces() { pageUiState.pageOptions = null; pageUiState.pageWidthPreferences = null; applyPageOptionsToShell(); void loadPageWidthPreferences(); updatePageSettingsTriggerState(); updatePageAiTriggerState(); ensureHistorySnapshotsSeeded(); installWorkspaceSidebarResizer(); } window.__mnoteRecordPageHistorySnapshot = recordPageHistorySnapshot; window.__mnoteApplyPageOptionsToShell = applyPageOptionsToShell; function scheduleInitializePageUiSurfaces() { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function() { setTimeout(initializePageUiSurfaces, 0); }, { once: true }); return; } setTimeout(initializePageUiSurfaces, 0); } scheduleInitializePageUiSurfaces(); function mnoteDevHotReloadEnabled() { try { return new URL(import.meta.url).searchParams.has('devHot'); } catch (_) { return false; } } function installMnoteDevHotReload() { var bootId = ''; var failedOnce = false; var timer = 0; function tick() { fetch('/api/dev/hot-reload', { cache: 'no-store', headers: { accept: 'application/json' } }) .then(function(response) { return response.ok ? response.json() : null; }) .then(function(payload) { if (!payload || payload.enabled !== true || !payload.bootId) { if (!bootId && timer) window.clearInterval(timer); return; } document.documentElement.setAttribute('data-mnote-dev-hot-reload', 'enabled'); if (!bootId) { bootId = String(payload.bootId); return; } if (failedOnce || String(payload.bootId) !== bootId) { window.location.assign(window.location.href); } }) .catch(function() { if (bootId) failedOnce = true; }); } tick(); timer = window.setInterval(tick, 1000); } if (mnoteDevHotReloadEnabled()) { installMnoteDevHotReload(); } document.addEventListener('dragstart', function(event) { var pageRow = closestAction(event.target, '.tree-row[data-shell-mode="page"][draggable="true"]'); if (pageRow) { startPageDrag(pageRow, event); return; } var fileRow = closestAction(event.target, '.tree-row[data-shell-mode="filetree"][draggable="true"]'); if (fileRow) { var _dndRT_ = window.__mnoteFileTreeDndRuntime; if (_dndRT_ && typeof _dndRT_.startFileTreeDrag === 'function') { var dragInfo = _dndRT_.startFileTreeDrag(fileRow, { selectedSidebarFileTreeRowIdsForDrag: selectedSidebarFileTreeRowIdsForDrag, }); draggingFileTreeRowIds = _dndRT_.draggingFileTreeRowIds; if (event.dataTransfer && dragInfo) { event.dataTransfer.effectAllowed = dragInfo.effectAllowed || 'copyMove'; event.dataTransfer.setData(_dndRT_.FILETREE_DRAG_MIME, dragInfo.payload); event.dataTransfer.setData('application/x-mnote-file-tree', dragInfo.payload); event.dataTransfer.setData('text/plain', dragInfo.payload); } } else { // inline fallback draggingFileTreeRowIds = selectedSidebarFileTreeRowIdsForDrag(fileRow); if (event.dataTransfer) { var payload = JSON.stringify({ type: 'mnote-file-tree-dnd', version: 1, rowIds: draggingFileTreeRowIds }); event.dataTransfer.effectAllowed = 'copyMove'; event.dataTransfer.setData(FILETREE_DRAG_MIME, payload); event.dataTransfer.setData('application/x-mnote-file-tree', payload); event.dataTransfer.setData('text/plain', payload); } } } }); document.addEventListener('dragover', function(event) { var pageRow = closestAction(event.target, '.tree-row[data-shell-mode="page"]'); var sourceNodeId = readPageDragNodeId(event); if (pageRow && canDropPage(sourceNodeId, pageRow)) { event.preventDefault(); clearPageDropFeedback(); pageRow.setAttribute('data-drop-feedback', 'true'); pageRow.setAttribute('data-drop-position', pageDropPosition(event, pageRow)); setActivePageDropRow(pageRow); if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'; return; } var _dndRT_ = window.__mnoteFileTreeDndRuntime; if (_dndRT_ && typeof _dndRT_.handleFileTreeDragOver === 'function') { if (_dndRT_.handleFileTreeDragOver(event, { closestAction: closestAction })) { return; } } else { // inline fallback var fileTree = document.getElementById('sidebar-file-tree-root'); if (fileTree && fileTree.contains(event.target)) { var hasFiles = event.dataTransfer && Array.prototype.indexOf.call(event.dataTransfer.types || [], 'Files') >= 0; var hasInternal = event.dataTransfer && Array.prototype.indexOf.call(event.dataTransfer.types || [], FILETREE_DRAG_MIME) >= 0; if (!hasFiles && !hasInternal && draggingFileTreeRowIds.length === 0) return; event.preventDefault(); if (activeFileTreeDropRow instanceof HTMLElement) activeFileTreeDropRow.setAttribute('data-drop-target', 'false'); activeFileTreeDropRow = closestAction(event.target, '.tree-row[data-shell-mode="filetree"]') || fileTree; if (activeFileTreeDropRow instanceof HTMLElement) activeFileTreeDropRow.setAttribute('data-drop-target', 'true'); var copyModifier = event.altKey || event.ctrlKey || event.metaKey; if (event.dataTransfer) event.dataTransfer.dropEffect = hasFiles || copyModifier ? 'copy' : 'move'; } } }); document.addEventListener('drop', function(event) { var pageRow = closestAction(event.target, '.tree-row[data-shell-mode="page"]'); var sourceNodeId = readPageDragNodeId(event); if (pageRow && canDropPage(sourceNodeId, pageRow)) { event.preventDefault(); var position = pageDropPosition(event, pageRow); var target = resolvePageMoveTarget(pageRow, position); clearPageDragState(); void dispatchTreeCommand(pageRow, { action: 'move', workspaceId: resolveWorkspaceId(pageRow), documentId: sourceNodeId, parentId: target.parentId, sortOrder: target.sortOrder }); return; } var _dndRT_ = window.__mnoteFileTreeDndRuntime; if (_dndRT_ && typeof _dndRT_.handleFileTreeDrop === 'function') { _dndRT_.handleFileTreeDrop(event, { closestAction: closestAction, resolveWorkspaceId: resolveWorkspaceId, fileTreeRowLocalUploadTargetRelativePath: fileTreeRowLocalUploadTargetRelativePath, ensureFileTreeWritableTarget: ensureFileTreeWritableTarget, dispatchSidebarEvent: dispatchSidebarEvent, }); } else { // inline fallback var fileTree = document.getElementById('sidebar-file-tree-root'); if (fileTree && fileTree.contains(event.target)) { var targetRow = closestAction(event.target, '.tree-row[data-shell-mode="filetree"]'); var files = event.dataTransfer ? Array.from(event.dataTransfer.files || []) : []; var raw = event.dataTransfer ? event.dataTransfer.getData(FILETREE_DRAG_MIME) || event.dataTransfer.getData('application/x-mnote-file-tree') || '' : ''; var rowIds = draggingFileTreeRowIds.slice(); if (raw) { try { var parsed = JSON.parse(raw); if (Array.isArray(parsed.rowIds)) rowIds = parsed.rowIds; } catch (_) {} } if (!files.length && !rowIds.length) return; event.preventDefault(); var detail = { workspaceId: resolveWorkspaceId(targetRow || fileTree), targetRowId: targetRow ? targetRow.getAttribute('data-row-id') : null, targetRowKind: targetRow ? targetRow.getAttribute('data-row-kind') : 'root', documentId: targetRow ? targetRow.getAttribute('data-document-id') || targetRow.getAttribute('data-doc-id') : null, assetId: targetRow ? targetRow.getAttribute('data-asset-id') : null, targetRelativePath: targetRow ? fileTreeRowLocalUploadTargetRelativePath(targetRow) : '', uploadIntent: 'filetree.folder.drop' }; if (activeFileTreeDropRow instanceof HTMLElement) activeFileTreeDropRow.setAttribute('data-drop-target', 'false'); activeFileTreeDropRow = null; if (files.length) { dispatchSidebarEvent('tree.filetree.external-drop', Object.assign({}, detail, { files: files })); } else { void Promise.resolve(ensureFileTreeWritableTarget('drop', targetRow, rowIds, event.altKey === true || event.ctrlKey === true || event.metaKey === true)).then(function(writable) { if (!writable) return; dispatchSidebarEvent('tree.filetree.internal-drop', Object.assign({}, detail, { rowIds: rowIds, copy: event.altKey === true || event.ctrlKey === true || event.metaKey === true })); }); } draggingFileTreeRowIds = []; } } }); document.addEventListener('dragend', function() { clearPageDragState(); var _dndRT_ = window.__mnoteFileTreeDndRuntime; if (_dndRT_ && typeof _dndRT_.resetFileTreeDragState === 'function') { _dndRT_.resetFileTreeDragState(); } else { draggingFileTreeRowIds = []; clearPageDropFeedback(); if (activeFileTreeDropRow instanceof HTMLElement) activeFileTreeDropRow.setAttribute('data-drop-target', 'false'); activeFileTreeDropRow = null; } }); sidebarTreeLiveApply.installTreeLiveApplyEventListeners(); var tree = document.getElementById('sidebar-tree-root'); var activeId = currentDocumentId(); if (tree && activeId) { var activeRow = tree.querySelector('.tree-row[data-node-id="' + cssEscape(activeId) + '"]'); if (activeRow instanceof HTMLElement) activeRow.setAttribute('data-active', 'true'); } var initiallySelectedFileRows = document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-selected="true"]'); initiallySelectedFileRows.forEach(function(row) { if (!(row instanceof HTMLElement)) return; var rowId = row.getAttribute('data-row-id') || ''; if (!rowId) return; sidebarFileTreeSelection.selectedRowIds.add(rowId); if (!sidebarFileTreeSelection.anchorRowId) sidebarFileTreeSelection.anchorRowId = rowId; sidebarFileTreeSelection.focusedRowId = rowId; }); syncSidebarFileTreeSelection(); schedulePendingLocalFolderRestoreFocus(); window.addEventListener('mnote:primary-document-activated', function(event) { var detail = event && event.detail ? event.detail : {}; pageUiState.pageOptions = null; applyPageOptionsToShell(); if (isPageSettingsOpen()) renderPageSettingsPopover(); selectSidebarFileTreeDocument(detail.documentId, { scrollIntoView: false }); }); window.addEventListener('mnote:page-aggregate-synced', function(event) { var detail = event && event.detail ? event.detail : {}; if (detail.scriptId && detail.scriptId !== '__MNOTE_PAGE_AGGREGATE__') return; var documentId = searchText(detail.documentId || ''); if (documentId && currentDocumentId() && documentId !== currentDocumentId()) return; pageUiState.pageOptions = null; applyPageOptionsToShell(); if (isPageSettingsOpen()) renderPageSettingsPopover(); }); restoreSidebarTreeTab(); startLocalFolderSidebarWatch(); })();