// MNote 主壳文件树运行时外置模块。 // 当前先承接 SIDEBAR_TREE_JS 中低风险的行数据解析 helper; // inline script 保留 fallback,避免模块加载顺序影响首屏行为。 function fileTreeRowKind(row) { if (!(row instanceof HTMLElement)) return ''; return String(row.getAttribute('data-row-kind') || '').trim(); } function fileTreeRowDocumentId(row) { if (!(row instanceof HTMLElement)) return ''; return String( row.getAttribute('data-document-id') || row.getAttribute('data-doc-id') || row.getAttribute('data-owner-document-id') || '', ).trim(); } function fileTreeRowAssetId(row, deps) { if (!(row instanceof HTMLElement)) return ''; var assetId = String(row.getAttribute('data-asset-id') || '').trim(); if (assetId) return assetId; var currentSourceKind = deps && typeof deps.currentSourceKind === 'function' ? deps.currentSourceKind : function() { return ''; }; if (currentSourceKind() === 'local_folder' && fileTreeRowKind(row) === 'asset') { return String(row.getAttribute('data-row-id') || '').trim(); } return ''; } function decodeLocalEncodedPath(value) { var path = String(value || '').trim().replace(/~([0-9A-Fa-f]{2})/g, '%$1'); if (!path) return ''; try { return decodeURIComponent(path); } catch (_) { return path; } } function fileTreeRowLocalRelativePath(row, deps) { if (!(row instanceof HTMLElement)) return ''; var direct = String(row.getAttribute('data-local-relative-path') || '').trim(); if (direct) return direct; var localFilePathFromAssetId = deps && typeof deps.localFilePathFromAssetId === 'function' ? deps.localFilePathFromAssetId : function() { return ''; }; var assetPath = localFilePathFromAssetId(fileTreeRowAssetId(row, deps)); if (assetPath) return assetPath; var rowId = String(row.getAttribute('data-row-id') || '').trim(); var nodeId = String(row.getAttribute('data-node-id') || '').trim(); var documentId = fileTreeRowDocumentId(row); var kind = fileTreeRowKind(row); if (rowId.indexOf('local:asset:') === 0) return rowId.slice('local:asset:'.length); if (rowId.indexOf('local:markdown:') === 0) return rowId.slice('local:markdown:'.length); if (rowId.indexOf('local:folder:') === 0) return rowId.slice('local:folder:'.length); if (rowId.indexOf('local:node:') === 0) return rowId.slice('local:node:'.length); if (nodeId.indexOf('local:node:') === 0) return nodeId.slice('local:node:'.length); if ((kind === 'document' || kind === 'doc' || kind === 'markdown') && documentId.indexOf('local-md:') === 0) { return decodeLocalEncodedPath(documentId.slice('local-md:'.length)); } if ((kind === 'folder' || kind === 'directory' || kind === 'index') && documentId.indexOf('local-dir:') === 0) { return decodeLocalEncodedPath(documentId.slice('local-dir:'.length)); } return ''; } function fileTreeRowLocalUploadTargetRelativePath(row, deps) { if (!(row instanceof HTMLElement)) return ''; var kind = fileTreeRowKind(row); var relativePath = fileTreeRowLocalRelativePath(row, deps); if (!relativePath) return ''; if (kind === 'folder' || kind === 'directory') return relativePath; var lastSlash = relativePath.lastIndexOf('/'); return lastSlash >= 0 ? relativePath.slice(0, lastSlash) : ''; } function parseObjectIdentityAttr(value) { var raw = String(value || '').trim(); if (!raw) return null; try { var parsed = JSON.parse(raw); return parsed && typeof parsed === 'object' ? parsed : { raw: raw }; } catch (_) { return { raw: raw }; } } function readWorkspacePathFromRow(row, deps) { if (!(row instanceof HTMLElement)) return null; deps = deps || {}; var currentSourceKind = typeof deps.currentSourceKind === 'function' ? deps.currentSourceKind : function() { return ''; }; var currentRootUri = typeof deps.currentRootUri === 'function' ? deps.currentRootUri : function() { return ''; }; var resolveWorkspaceId = typeof deps.resolveWorkspaceId === 'function' ? deps.resolveWorkspaceId : function() { return ''; }; var rowTitle = typeof deps.rowTitle === 'function' ? deps.rowTitle : function(target) { return String(target && target.textContent || '').trim(); }; var objectIdentityRaw = String(row.getAttribute('data-object-identity') || '').trim(); var objectIdentity = parseObjectIdentityAttr(objectIdentityRaw); var objectKind = String(row.getAttribute('data-object-kind') || (objectIdentity && objectIdentity.objectKind) || '').trim(); var relativePath = fileTreeRowLocalRelativePath(row, deps); var assetId = fileTreeRowAssetId(row, deps); var documentId = fileTreeRowDocumentId(row); var rowId = String(row.getAttribute('data-row-id') || '').trim(); var rowKind = fileTreeRowKind(row); if (!documentId && (rowKind === 'folder' || objectKind === 'index') && relativePath) { documentId = 'local-dir:' + relativePath.replace(/^\/+/, '').split('/').map(function(segment) { return encodeURIComponent(segment).replace(/%20/g, '~20'); }).join('~2F'); } var sourceKind = String(row.getAttribute('data-source-kind') || currentSourceKind() || '').trim(); var rootUri = String(row.getAttribute('data-root-uri') || currentRootUri() || '').trim(); return { schema: 'mnote.workspace_path.v1', workspaceId: String(row.getAttribute('data-workspace-id') || resolveWorkspaceId(row) || '').trim(), sourceKind: sourceKind, rootUri: rootUri, relativePath: relativePath, localRelativePath: relativePath, objectIdentity: objectIdentity, objectIdentityRaw: objectIdentityRaw, objectKind: objectKind, resourceKind: objectKind || rowKind, rowId: rowId, rowKind: rowKind, documentId: documentId, assetId: assetId, title: rowTitle(row), href: String(row.querySelector?.('.tree-link')?.getAttribute?.('href') || '').trim(), isLocalFolder: sourceKind === 'local_folder' || (!sourceKind && (rowId.indexOf('local:') === 0 || documentId.indexOf('local-md:') === 0 || documentId.indexOf('local-dir:') === 0)) }; } function isFileTreeDownloadableAssetRow(row, deps) { if (!(row instanceof HTMLElement)) return false; var kind = fileTreeRowKind(row); if (kind === 'document' || kind === 'doc' || kind === 'index' || kind === 'markdown' || kind === 'folder' || kind === 'directory') return false; return Boolean(fileTreeRowAssetId(row, deps)); } function isFileTreeDownloadableRow(row, deps) { if (!(row instanceof HTMLElement)) return false; var currentSourceKind = deps && typeof deps.currentSourceKind === 'function' ? deps.currentSourceKind : function() { return ''; }; if (currentSourceKind() !== 'local_folder') return isFileTreeDownloadableAssetRow(row, deps); return Boolean(fileTreeRowLocalRelativePath(row, deps) || isFileTreeDownloadableAssetRow(row, deps)); } function fileTreeAssetDownloadDetail(row, deps) { if (!isFileTreeDownloadableRow(row, deps)) return null; var rowTitle = deps && typeof deps.rowTitle === 'function' ? deps.rowTitle : function() { return ''; }; var resolveWorkspaceId = deps && typeof deps.resolveWorkspaceId === 'function' ? deps.resolveWorkspaceId : function() { return ''; }; var title = rowTitle(row); var relativePath = fileTreeRowLocalRelativePath(row, deps); var assetId = fileTreeRowAssetId(row, deps) || (relativePath ? 'local-file:' + relativePath : ''); return { contextKind: 'filetree', documentId: fileTreeRowDocumentId(row), rowId: row.getAttribute('data-row-id') || '', rowKind: fileTreeRowKind(row), assetId: assetId, localRelativePath: relativePath, workspacePath: readWorkspacePathFromRow(row, deps), title: title, fileName: title, workspaceId: resolveWorkspaceId(row) }; } function fileTreeRowsByRowIds(rowIds, deps) { var cssEscape = deps && typeof deps.cssEscape === 'function' ? deps.cssEscape : function(value) { return String(value).replace(/["\\]/g, '\\$&'); }; return (rowIds || []).map(function(rowId) { return document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(rowId) + '"]'); }).filter(function(row) { return row instanceof HTMLElement; }); } function fileTreeChildCount(documentId, deps) { if (!documentId) return 0; var cssEscape = deps && typeof deps.cssEscape === 'function' ? deps.cssEscape : function(value) { return String(value).replace(/["\\]/g, '\\$&'); }; var row = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape('doc:' + documentId) + '"]'); var node = row ? row.closest('.tree-node') : null; var children = node ? node.querySelectorAll(':scope > .tree-children > .tree-node > .tree-row[data-shell-mode="filetree"][data-row-kind="document"], :scope > .tree-children > .tree-node > .tree-row[data-shell-mode="filetree"][data-row-kind="doc"]') : []; return children.length; } function defaultEscapeHtml(value) { return String(value == null ? '' : value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function defaultObjectIdentityAttr(identity) { try { return JSON.stringify(identity || {}); } catch (_) { return ''; } } function readProjection(value) { if (!value || typeof value !== 'object') return null; if (value.result && typeof value.result === 'object') return value.result; if (value.snapshot && value.snapshot.tree) return value.snapshot.tree; if (value.data && value.data.tree) return value.data.tree; if (value.tree && typeof value.tree === 'object') return value.tree; return value; } function readSidebarDataset(value) { if (!value || typeof value !== 'object') return null; if (value.snapshot && value.snapshot.dataset && typeof value.snapshot.dataset === 'object') return value.snapshot.dataset; if (value.data && value.data.dataset && typeof value.data.dataset === 'object') return value.data.dataset; if (value.dataset && typeof value.dataset === 'object') return value.dataset; if (value.sidebar && typeof value.sidebar === 'object') return value.sidebar; return null; } function readDatasetProjection(value, snakeCaseKey, camelCaseKey) { var dataset = readSidebarDataset(value); if (!dataset || typeof dataset !== 'object') return null; if (dataset[snakeCaseKey] && typeof dataset[snakeCaseKey] === 'object') return dataset[snakeCaseKey]; if (dataset[camelCaseKey] && typeof dataset[camelCaseKey] === 'object') return dataset[camelCaseKey]; return null; } function projectionItems(projection) { var resolved = readProjection(projection); return resolved && Array.isArray(resolved.items) ? resolved.items : []; } function hasProjectionItems(projection) { var resolved = readProjection(projection); return Boolean(resolved && Array.isArray(resolved.items)); } function nodeIdOf(item) { return String(item && (item.nodeId || item.id || item.documentId) || '').trim(); } function rowIdOf(item) { return String(item && (item.rowId || item.nodeId || item.id) || '').trim(); } function parentIdOf(item) { return String(item && (item.parentNodeId || item.parentId || '') || '').trim(); } function titleOf(item) { return String(item && item.title || '无标题').trim() || '无标题'; } function fileWorkspaceRelativePath(item) { var meta = item && item.resourceMeta && typeof item.resourceMeta === 'object' ? item.resourceMeta : {}; var workspacePath = meta.workspacePath && typeof meta.workspacePath === 'object' ? meta.workspacePath : {}; var fromWorkspacePath = String(workspacePath.relativePath || '').trim(); if (fromWorkspacePath) return fromWorkspacePath; var extra = meta.extra && typeof meta.extra === 'object' ? meta.extra : {}; var source = extra.source && typeof extra.source === 'object' ? extra.source : {}; var fromSource = String(source.relativePath || '').trim(); if (fromSource) return fromSource; return String(item && (item.relativePath || item.rootRelativePath) || '').trim(); } function groupRowsByParent(rows) { rows = Array.isArray(rows) ? rows : []; var ids = new Set(rows.map(nodeIdOf).filter(Boolean)); var grouped = new Map(); rows.forEach(function(item) { var parentId = parentIdOf(item); if (!ids.has(parentId)) parentId = ''; if (!grouped.has(parentId)) grouped.set(parentId, []); grouped.get(parentId).push(item); }); return grouped; } function replaceSidebarTreeFromDocument(nextDocument, rootId) { var current = document.getElementById(rootId); var next = nextDocument ? nextDocument.getElementById(rootId) : null; if (!(current instanceof HTMLElement) || !(next instanceof HTMLElement)) return false; current.innerHTML = next.innerHTML; Array.from(next.attributes || []).forEach(function(attr) { current.setAttribute(attr.name, attr.value); }); return true; } function applyLocalFolderSidebarSnapshot(nextDocument, options) { options = options || {}; var pageRootId = String(options.pageRootId || 'sidebar-tree-root'); var fileRootId = String(options.fileRootId || 'sidebar-file-tree-root'); var appliedPage = replaceSidebarTreeFromDocument(nextDocument, pageRootId); var appliedFile = replaceSidebarTreeFromDocument(nextDocument, fileRootId); var applied = appliedPage || appliedFile; if (applied && typeof options.onApplied === 'function') options.onApplied({ pageRootApplied: appliedPage, fileRootApplied: appliedFile }); return applied; } function markLocalFolderWatchApplied(value) { var appliedValue = String(value || 'projection'); if (!document || !document.documentElement) return false; document.documentElement.setAttribute('data-mnote-local-folder-watch-applied', appliedValue); return true; } function revealFileTreeRow(row) { 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, deps) { if (!assetId) return false; var cssEscape = deps && typeof deps.cssEscape === 'function' ? deps.cssEscape : function(value) { return String(value).replace(/["\\]/g, '\\$&'); }; 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 localUploadedAssetRelativePath(asset, deps) { deps = deps || {}; var direct = String(asset && (asset.rootRelativePath || asset.root_relative_path || asset.relativePath || asset.relative_path) || '').trim(); if (direct) return direct; var localFilePathFromAssetId = typeof deps.localFilePathFromAssetId === 'function' ? deps.localFilePathFromAssetId : function(assetId) { var value = String(assetId || '').trim(); return value.indexOf('local-file:') === 0 ? value.slice('local-file:'.length) : value.indexOf('local:asset:') === 0 ? value.slice('local:asset:'.length) : ''; }; return localFilePathFromAssetId(String(asset && asset.id || '').trim()); } function parentRelativePathForUploadedAsset(asset, relativePath) { var target = String(asset && (asset.targetRelativePath || asset.target_relative_path) || '').trim(); if (target || Object.prototype.hasOwnProperty.call(asset || {}, 'targetRelativePath')) return target; var normalized = String(relativePath || '').trim().replace(/^\/+|\/+$/g, ''); var lastSlash = normalized.lastIndexOf('/'); return lastSlash >= 0 ? normalized.slice(0, lastSlash) : ''; } function findLocalFileTreeParentRowForAsset(asset, relativePath, deps) { deps = deps || {}; var cssEscape = typeof deps.cssEscape === 'function' ? deps.cssEscape : function(value) { return String(value).replace(/["\\]/g, '\\$&'); }; var currentSourceKind = typeof deps.currentSourceKind === 'function' ? deps.currentSourceKind : function() { return ''; }; if (currentSourceKind() !== 'local_folder') return null; var parentRelativePath = parentRelativePathForUploadedAsset(asset, relativePath); var selector = '#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-local-relative-path="' + cssEscape(parentRelativePath) + '"]'; if (parentRelativePath) { var parentRow = document.querySelector(selector); if (parentRow instanceof HTMLElement) return parentRow; } if (!parentRelativePath) { var root = document.querySelector('#sidebar-file-tree-root .tree-root'); return root instanceof HTMLElement ? null : null; } return null; } function appendUploadedAssetRow(asset, documentId, deps) { deps = deps || {}; var cssEscape = typeof deps.cssEscape === 'function' ? deps.cssEscape : function(value) { return String(value).replace(/["\\]/g, '\\$&'); }; var escapeHtml = typeof deps.escapeHtml === 'function' ? deps.escapeHtml : defaultEscapeHtml; var objectIdentityAttr = typeof deps.objectIdentityAttr === 'function' ? deps.objectIdentityAttr : defaultObjectIdentityAttr; var uploadedAssetTitle = typeof deps.uploadedAssetTitle === 'function' ? deps.uploadedAssetTitle : function(nextAsset) { return String(nextAsset && (nextAsset.file_name || nextAsset.title || nextAsset.name) || '未命名附件').trim() || '未命名附件'; }; var uploadedAssetType = typeof deps.uploadedAssetType === 'function' ? deps.uploadedAssetType : function(nextAsset) { return String(nextAsset && (nextAsset.asset_type || nextAsset.assetType || nextAsset.mime_type || '') || '').trim(); }; var fileTreeIconKindForFileName = typeof deps.fileTreeIconKindForFileName === 'function' ? deps.fileTreeIconKindForFileName : function() { return ''; }; var currentDocumentId = typeof deps.currentDocumentId === 'function' ? deps.currentDocumentId : function() { return ''; }; var currentSourceKind = typeof deps.currentSourceKind === 'function' ? deps.currentSourceKind : function() { return ''; }; var assetId = String(asset && asset.id || '').trim(); if (!assetId) return false; if (revealFileTreeAssetRow(assetId, { cssEscape: cssEscape })) 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 localRelativePath = localUploadedAssetRelativePath(asset, deps); var targetDocumentId = String(documentId || asset.document_id || asset.documentId || asset.ownerDocumentId || currentDocumentId() || '').trim(); var parentRow = findLocalFileTreeParentRowForAsset(asset, localRelativePath, deps); if (!parentRow) 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, rootUri: String(asset && (asset.rootUri || asset.root_uri) || '').trim() || null, relativePath: localRelativePath || null }; li.setAttribute('data-node-id', 'asset:' + assetId); var title = uploadedAssetTitle(asset); var iconKind = fileTreeIconKindForFileName(title) || uploadedAssetType(asset) || 'file'; if (objectKind === 'mindmap') iconKind = 'mindmap'; var ownerDocumentId = String(asset && (asset.ownerDocumentId || asset.owner_document_id) || targetDocumentId || '').trim(); var rootUri = String(asset && (asset.rootUri || asset.root_uri) || '').trim(); li.innerHTML = '