Improve local filetree view state and sidebar performance
This commit is contained in:
@@ -66,7 +66,7 @@ import {
|
||||
return window.__mnoteLeptosTiptapRuntimePromise;
|
||||
}
|
||||
window.__mnoteLeptosTiptapRuntimePromise = (async () => {
|
||||
const manifestResponse = await fetch('/api/leptos-tiptap-runtime/manifest.json', { cache: 'no-store' });
|
||||
const manifestResponse = await fetch('/api/leptos-tiptap-runtime/manifest.json');
|
||||
if (!manifestResponse.ok) throw new Error(`manifest_failed_${manifestResponse.status}`);
|
||||
const manifest = await manifestResponse.json();
|
||||
if (!manifest.entryAssetPath) throw new Error('island manifest 缺少 entryAssetPath');
|
||||
@@ -368,16 +368,19 @@ import {
|
||||
|
||||
const replacePaneDocument = async (paneRole, descriptor, options = {}) => {
|
||||
mindmapHost.unmountMindmapPane(paneRole);
|
||||
const runtime = await loadRuntime();
|
||||
const root = document.querySelector(`${ROOT_SELECTOR}[data-pane-role="${paneRole}"]`);
|
||||
const observability = document.querySelector(`[data-editor-host-observability][data-pane-role="${paneRole}"]`);
|
||||
if (!(root instanceof HTMLElement)) throw new Error(`pane_root_missing_${paneRole}`);
|
||||
const runtimePromise = loadRuntime();
|
||||
const aggregatePromise = options.aggregate
|
||||
? Promise.resolve(options.aggregate)
|
||||
: fetchPageAggregateForPane(descriptor);
|
||||
const previousView = paneViewRegistry.get(paneRole);
|
||||
const [runtime, aggregate] = await Promise.all([runtimePromise, aggregatePromise]);
|
||||
if (previousView) {
|
||||
unmountEditorViewBinding(previousView);
|
||||
paneViewRegistry.delete(paneRole);
|
||||
}
|
||||
const aggregate = options.aggregate || await fetchPageAggregateForPane(descriptor);
|
||||
const bootstrap = buildBootstrapFromAggregate(aggregate, descriptor, paneRole);
|
||||
syncPageAggregateScript({ pageAggregateScriptId: bootstrap.pageAggregateScriptId }, aggregate);
|
||||
const runtimeDescriptor = { paneRole, root, observability, aggregate, bootstrap };
|
||||
|
||||
@@ -24,7 +24,7 @@ function fileTreeRowAssetId(row, deps) {
|
||||
}
|
||||
|
||||
function decodeLocalEncodedPath(value) {
|
||||
var path = String(value || '').trim().replace(/~2F/g, '/');
|
||||
var path = String(value || '').trim().replace(/~([0-9A-Fa-f]{2})/g, '%$1');
|
||||
if (!path) return '';
|
||||
try {
|
||||
return decodeURIComponent(path);
|
||||
|
||||
@@ -5,6 +5,7 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
buildLocalOnlyOfficeOpenUrl,
|
||||
buildOnlyOfficeOpenPath,
|
||||
buildOnlyOfficeOpenUrl,
|
||||
closestAction: injectedClosestAction,
|
||||
currentDocumentId,
|
||||
currentRootUri,
|
||||
currentWorkspaceSourcePayload,
|
||||
@@ -23,6 +24,11 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
uploadedFileSize,
|
||||
} = dependencies;
|
||||
|
||||
const closestAction = typeof injectedClosestAction === 'function' ? injectedClosestAction : function(target, selector) {
|
||||
var node = target && target.nodeType === Node.TEXT_NODE ? target.parentElement : target;
|
||||
return node && typeof node.closest === 'function' ? node.closest(selector) : null;
|
||||
};
|
||||
|
||||
var activeEditorAttachmentLink = null;
|
||||
var attachmentActionsHideTimer = 0;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
createPage,
|
||||
cssEscape,
|
||||
currentDocumentId,
|
||||
currentRootUri,
|
||||
currentSourceKind,
|
||||
deleteSingleFileTreeAsset,
|
||||
dispatchSidebarEvent,
|
||||
@@ -23,6 +24,8 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
openEditorAttachmentEditTab,
|
||||
openEditorAttachmentNewWindow,
|
||||
refreshLocalFolderSidebarSnapshot,
|
||||
removeFileTreeAssetRow,
|
||||
revealFileTreeResource,
|
||||
resolveWorkspaceId,
|
||||
runtimeState,
|
||||
selectedSidebarFileTreeSelection,
|
||||
@@ -30,6 +33,12 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
validateFileTreeRename,
|
||||
} = dependencies;
|
||||
const sidebarFileTreeSelection = selectedSidebarFileTreeSelection;
|
||||
var fileTreeOperationBatchCounter = 0;
|
||||
|
||||
function nextFileTreeOperationBatchId(action) {
|
||||
fileTreeOperationBatchCounter += 1;
|
||||
return 'filetree-' + String(action || 'operation') + '-' + Date.now() + '-' + fileTreeOperationBatchCounter;
|
||||
}
|
||||
|
||||
function rowTitle(row) {
|
||||
var title = row ? row.querySelector(':scope > .tree-link > .tree-link-title') : null;
|
||||
@@ -59,8 +68,12 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var rowKind = row.getAttribute('data-row-kind') || '';
|
||||
var documentId = row.getAttribute('data-document-id') || row.getAttribute('data-doc-id') || '';
|
||||
var assetId = row.getAttribute('data-asset-id') || '';
|
||||
if (!documentId && !assetId) return false;
|
||||
if (rowKind && ['document', 'doc', 'index', 'markdown', 'asset'].indexOf(rowKind) < 0 && !assetId) return false;
|
||||
var rowId = row.getAttribute('data-row-id') || '';
|
||||
var localFolderSource = currentSourceKind() === 'local_folder';
|
||||
var localFolderDirectory = localFolderSource && (rowKind === 'folder' || rowKind === 'directory');
|
||||
var commandTargetId = documentId || (localFolderSource ? (assetId || rowId) : '');
|
||||
if (!commandTargetId && !assetId) return false;
|
||||
if (rowKind && ['document', 'doc', 'index', 'markdown', 'asset'].indexOf(rowKind) < 0 && !assetId && !localFolderDirectory) return false;
|
||||
var link = row.querySelector(':scope > .tree-link');
|
||||
var title = rowTitle(row);
|
||||
if (!(link instanceof HTMLElement)) return false;
|
||||
@@ -68,7 +81,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.className = 'tree-rename-input';
|
||||
input.setAttribute('data-rename-id', row.getAttribute('data-row-id') || '');
|
||||
input.setAttribute('data-rename-id', rowId);
|
||||
input.value = title;
|
||||
input.style.minWidth = '0';
|
||||
input.style.flex = '1 1 auto';
|
||||
@@ -117,7 +130,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var commandTitle = isFileTreePageRow(row) ? normalizeFileTreePageRenameTitle(nextTitle) : nextTitle;
|
||||
committing = true;
|
||||
input.disabled = true;
|
||||
var work = assetId
|
||||
var work = assetId && !localFolderSource
|
||||
? Promise.resolve().then(function(){
|
||||
document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-asset-id="' + cssEscape(assetId) + '"] .tree-link-title').forEach(function(titleNode) {
|
||||
titleNode.textContent = commandTitle;
|
||||
@@ -128,9 +141,11 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
: dispatchTreeCommand(row, {
|
||||
action: 'rename',
|
||||
workspaceId: resolveWorkspaceId(row),
|
||||
documentId: documentId,
|
||||
documentId: commandTargetId,
|
||||
title: commandTitle
|
||||
}).then(function(){ updateTitleEverywhere(documentId, commandTitle); });
|
||||
}).then(function(){
|
||||
if (!localFolderSource && documentId) updateTitleEverywhere(documentId, commandTitle);
|
||||
});
|
||||
void work.then(close).catch(function(error) {
|
||||
committing = false;
|
||||
input.disabled = false;
|
||||
@@ -293,6 +308,51 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
return detail.title || '';
|
||||
}
|
||||
|
||||
function localRootPathFromRootUri(rootUri) {
|
||||
var value = String(rootUri || '').trim();
|
||||
if (!value) return '';
|
||||
if (!/^file:\/\//i.test(value)) return value.charAt(0) === '/' ? value : '';
|
||||
var pathPart = value.replace(/^file:\/\//i, '');
|
||||
if (pathPart.indexOf('localhost/') === 0) pathPart = pathPart.slice('localhost'.length);
|
||||
if (pathPart.charAt(0) !== '/') pathPart = '/' + pathPart;
|
||||
try {
|
||||
return decodeURIComponent(pathPart);
|
||||
} catch (_) {
|
||||
return pathPart;
|
||||
}
|
||||
}
|
||||
|
||||
function joinLocalAbsolutePath(rootUri, relativePath) {
|
||||
var rootPath = localRootPathFromRootUri(rootUri);
|
||||
var normalized = decodeLocalEncodedPath(relativePath).replace(/^\/+/, '');
|
||||
if (!rootPath || !normalized) return rootPath || normalized;
|
||||
return rootPath.replace(/\/+$/g, '') + '/' + normalized;
|
||||
}
|
||||
|
||||
function fileTreeCopyLocalAbsolutePath(detail, trigger) {
|
||||
var rootUri = typeof currentRootUri === 'function' ? currentRootUri() : '';
|
||||
if (!rootUri && trigger && typeof trigger.closest === 'function') {
|
||||
var row = trigger.closest('.tree-row[data-shell-mode="filetree"]');
|
||||
if (row instanceof HTMLElement) rootUri = row.getAttribute('data-root-uri') || '';
|
||||
}
|
||||
if (!rootUri && document.body) rootUri = document.body.getAttribute('data-mnote-root-uri') || '';
|
||||
var relativePath = String(detail && detail.localRelativePath || '').trim();
|
||||
if (!relativePath && detail && detail.assetId) relativePath = localFilePathFromAssetId(detail.assetId);
|
||||
if (!relativePath && detail && detail.documentId) {
|
||||
relativePath = String(detail.documentId || '')
|
||||
.replace(/^local-md:/, '')
|
||||
.replace(/^local-dir:/, '');
|
||||
}
|
||||
if (!relativePath && detail && detail.rowId) {
|
||||
relativePath = String(detail.rowId || '')
|
||||
.replace(/^local:asset:/, '')
|
||||
.replace(/^local:markdown:/, '')
|
||||
.replace(/^local:folder:/, '')
|
||||
.replace(/^local:node:/, '');
|
||||
}
|
||||
return joinLocalAbsolutePath(rootUri, relativePath);
|
||||
}
|
||||
|
||||
function fileTreeMenuTargetParentId(detail, trigger) {
|
||||
var rowKind = String(detail && detail.rowKind || '').trim();
|
||||
var rowId = String(detail && detail.rowId || '').trim();
|
||||
@@ -361,6 +421,10 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var workspaceId = detail.workspaceId || resolveWorkspaceId(trigger || document.body);
|
||||
var title = detail.title || '无标题';
|
||||
var isAsset = detail.contextKind === 'filetree' && detail.assetId && detail.rowKind !== 'document' && detail.rowKind !== 'index';
|
||||
if (detail.contextKind === 'filetree' && action === 'toggle-sidebar-folder-shortcut') {
|
||||
dispatchSidebarEvent('tree.sidebarShortcut.toggleFolder', detail);
|
||||
return;
|
||||
}
|
||||
if (detail.contextKind === 'filetree' && action === 'download') {
|
||||
downloadSelectedFileTreeAssetRows(detail, trigger);
|
||||
return;
|
||||
@@ -408,7 +472,10 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
return;
|
||||
}
|
||||
if (action === 'copy-id') {
|
||||
void copyTreeContextValue(documentId || detail.assetId || detail.rowId || '', 'copy-id');
|
||||
var copyIdValue = currentSourceKind() === 'local_folder'
|
||||
? fileTreeCopyLocalAbsolutePath(detail, trigger)
|
||||
: '';
|
||||
void copyTreeContextValue(copyIdValue || documentId || detail.assetId || detail.rowId || '', 'copy-id');
|
||||
return;
|
||||
}
|
||||
if (action === 'delete-trash' && detail.contextKind === 'filetree') {
|
||||
@@ -776,6 +843,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
{ separator: true },
|
||||
{ action: 'new-file', icon: 'note_add', label: 'New File', when: '!workspace.readonly' },
|
||||
{ action: 'new-folder', icon: 'create_new_folder', label: 'New Folder', disabled: currentSourceKind() !== 'local_folder', title: currentSourceKind() === 'local_folder' ? '在当前目录下创建子文件夹' : '仅 local folder 支持创建文件夹', when: '!workspace.readonly' },
|
||||
{ action: 'toggle-sidebar-folder-shortcut', icon: 'star', label: '加入/取消星标置顶', disabled: currentSourceKind() !== 'local_folder' || (detail.rowKind !== 'folder' && detail.rowKind !== 'directory'), title: currentSourceKind() === 'local_folder' ? '把当前文件夹加入或移出星标置顶' : '仅 local folder 文件夹支持星标置顶' },
|
||||
{ action: 'paste-into', icon: 'content_paste', label: '粘贴到此处', disabled: !runtimeState.sidebarFileTreeClipboard, title: runtimeState.sidebarFileTreeClipboard ? '粘贴到当前文件树目标' : '剪贴板为空', when: '!workspace.readonly' },
|
||||
{ action: 'refresh', icon: 'refresh', label: 'Refresh' },
|
||||
{ action: 'collapse-all', icon: 'unfold_less', label: 'Collapse All' },
|
||||
@@ -915,22 +983,31 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var row = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape('doc:' + id) + '"]')
|
||||
|| document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-document-id="' + cssEscape(id) + '"]')
|
||||
|| document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-doc-id="' + cssEscape(id) + '"]');
|
||||
if (!(row instanceof HTMLElement)) return false;
|
||||
var rowId = row.getAttribute('data-row-id') || '';
|
||||
if (!rowId) return false;
|
||||
selectSidebarFileTreeRow(row, { ctrlKey: false, metaKey: false, shiftKey: false });
|
||||
document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-active="true"]').forEach(function(activeRow) {
|
||||
if (activeRow instanceof HTMLElement) activeRow.setAttribute('data-active', 'false');
|
||||
});
|
||||
row.setAttribute('data-active', 'true');
|
||||
if (options && options.scrollIntoView !== false) {
|
||||
try {
|
||||
row.scrollIntoView({ block: 'nearest' });
|
||||
} catch (_) {
|
||||
row.scrollIntoView();
|
||||
if (!(row instanceof HTMLElement)) {
|
||||
var relativePath = options && options.relativePath
|
||||
? String(options.relativePath || '').trim()
|
||||
: id.indexOf('local-md:') === 0
|
||||
? decodeLocalEncodedPath(id.slice('local-md:'.length))
|
||||
: '';
|
||||
var bundleParentPath = localMarkdownBundleParentPath(relativePath);
|
||||
var bundleRow = bundleParentPath ? visibleFileTreeRowByRelativePath(bundleParentPath) : null;
|
||||
if (bundleRow instanceof HTMLElement) {
|
||||
return activateSidebarFileTreeRow(bundleRow, options);
|
||||
}
|
||||
if (relativePath && typeof revealFileTreeResource === 'function') {
|
||||
void revealFileTreeResource({
|
||||
rootUri: options && options.rootUri,
|
||||
relativePath: relativePath,
|
||||
rowId: options && options.rowId,
|
||||
select: true,
|
||||
focus: true,
|
||||
scroll: options ? options.scrollIntoView !== false : true
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return activateSidebarFileTreeRow(row, options);
|
||||
}
|
||||
|
||||
function selectSidebarFileTreeRowById(rowId, options) {
|
||||
@@ -1063,7 +1140,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
function decodeLocalEncodedPath(value) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('decodeLocalEncodedPath');
|
||||
if (runtimeFn) return runtimeFn(value);
|
||||
var path = String(value || '').trim().replace(/~2F/g, '/');
|
||||
var path = String(value || '').trim().replace(/~([0-9A-Fa-f]{2})/g, '%$1');
|
||||
if (!path) return '';
|
||||
try {
|
||||
return decodeURIComponent(path);
|
||||
@@ -1072,6 +1149,62 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeFileTreeRelativePath(value) {
|
||||
var normalized = String(value || '').trim().replace(/^\/+|\/+$/g, '');
|
||||
return normalized === '.' ? '' : normalized;
|
||||
}
|
||||
|
||||
function parentRelativePathForFileTreePath(relativePath) {
|
||||
var normalized = normalizeFileTreeRelativePath(relativePath);
|
||||
if (!normalized) return '';
|
||||
var index = normalized.lastIndexOf('/');
|
||||
return index > 0 ? normalized.slice(0, index) : '';
|
||||
}
|
||||
|
||||
function fileNameStem(value) {
|
||||
var name = String(value || '').trim();
|
||||
var dot = name.lastIndexOf('.');
|
||||
return dot > 0 ? name.slice(0, dot) : name;
|
||||
}
|
||||
|
||||
function localMarkdownBundleParentPath(relativePath) {
|
||||
var normalized = normalizeFileTreeRelativePath(relativePath);
|
||||
if (!/\.md$/i.test(normalized)) return '';
|
||||
var parent = parentRelativePathForFileTreePath(normalized);
|
||||
if (!parent) return '';
|
||||
var fileName = normalized.slice(normalized.lastIndexOf('/') + 1);
|
||||
var parentName = parent.slice(parent.lastIndexOf('/') + 1);
|
||||
return fileNameStem(fileName) === parentName ? parent : '';
|
||||
}
|
||||
|
||||
function visibleFileTreeRowByRelativePath(relativePath) {
|
||||
var normalized = normalizeFileTreeRelativePath(relativePath);
|
||||
if (!normalized) return null;
|
||||
var row = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-local-relative-path="' + cssEscape(normalized) + '"]');
|
||||
if (!(row instanceof HTMLElement)) return null;
|
||||
if (row.closest('.tree-children--collapsed')) return null;
|
||||
return row;
|
||||
}
|
||||
|
||||
function activateSidebarFileTreeRow(row, options) {
|
||||
if (!(row instanceof HTMLElement)) return false;
|
||||
var rowId = row.getAttribute('data-row-id') || '';
|
||||
if (!rowId) return false;
|
||||
selectSidebarFileTreeRow(row, { ctrlKey: false, metaKey: false, shiftKey: false });
|
||||
document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-active="true"]').forEach(function(activeRow) {
|
||||
if (activeRow instanceof HTMLElement) activeRow.setAttribute('data-active', 'false');
|
||||
});
|
||||
row.setAttribute('data-active', 'true');
|
||||
if (options && options.scrollIntoView !== false) {
|
||||
try {
|
||||
row.scrollIntoView({ block: 'nearest' });
|
||||
} catch (_) {
|
||||
row.scrollIntoView();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function fileTreeRowLocalRelativePath(row) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('fileTreeRowLocalRelativePath');
|
||||
if (runtimeFn) return runtimeFn(row, fileTreeRuntimeDeps());
|
||||
@@ -1239,7 +1372,8 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var assetRows = [];
|
||||
rows.forEach(function(row) {
|
||||
var kind = fileTreeRowKind(row);
|
||||
if ((kind === 'document' || kind === 'doc' || kind === 'index' || kind === 'markdown') && fileTreeRowDocumentId(row)) {
|
||||
var documentId = fileTreeRowDocumentId(row);
|
||||
if ((kind === 'document' || kind === 'doc' || kind === 'index' || kind === 'markdown' || documentId.indexOf('local-md:') === 0) && documentId) {
|
||||
docRows.push(row);
|
||||
return;
|
||||
}
|
||||
@@ -1338,69 +1472,101 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
return children.length;
|
||||
}
|
||||
|
||||
function fileTreeMoveTargetParentId(targetRow) {
|
||||
if (!(targetRow instanceof HTMLElement)) return null;
|
||||
return fileTreeMenuTargetParentId({
|
||||
rowKind: fileTreeRowKind(targetRow),
|
||||
rowId: targetRow.getAttribute('data-row-id') || '',
|
||||
documentId: fileTreeRowDocumentId(targetRow)
|
||||
}, targetRow) || null;
|
||||
}
|
||||
|
||||
function fileTreeMoveSourceId(row) {
|
||||
if (!(row instanceof HTMLElement)) return '';
|
||||
return fileTreeRowDocumentId(row)
|
||||
|| (currentSourceKind() === 'local_folder'
|
||||
? (fileTreeRowAssetId(row) || row.getAttribute('data-row-id') || '')
|
||||
: fileTreeRowAssetId(row));
|
||||
}
|
||||
|
||||
async function moveSidebarFileTreeRows(rowIds, targetRow, options) {
|
||||
var rows = fileTreeRowsByRowIds(rowIds || []);
|
||||
if (rows.length === 0) return false;
|
||||
var copy = Boolean(options && options.copy);
|
||||
var targetParentId = fileTreeMoveTargetParentId(targetRow);
|
||||
var workspaceId = resolveWorkspaceId(targetRow || document.body);
|
||||
var writable = await ensureFileTreeWritableTarget('move', targetRow, rowIds || [], copy);
|
||||
if (!writable) return false;
|
||||
var batchId = nextFileTreeOperationBatchId(copy ? 'copy' : 'move');
|
||||
var plan = buildSidebarFileTreeDeletePlan(rows);
|
||||
var failures = [];
|
||||
if (copy) {
|
||||
dispatchSidebarEvent('tree.filetree.copy-requested', { rowIds: rowIds || [], targetDocumentId: targetParentId });
|
||||
recordFileTreeActionStatus('copy-requested', { documentId: targetParentId, batchId: batchId });
|
||||
return true;
|
||||
}
|
||||
var movableRows = plan.docRows.concat(plan.folderRows);
|
||||
if (currentSourceKind() === 'local_folder') {
|
||||
movableRows = movableRows.concat(plan.fileAssetRows, plan.mindmapRows, plan.tableRows);
|
||||
}
|
||||
for (var i = 0; i < movableRows.length; i += 1) {
|
||||
var sourceRow = movableRows[i];
|
||||
var sourceId = fileTreeMoveSourceId(sourceRow);
|
||||
if (!sourceId || sourceId === targetParentId) continue;
|
||||
try {
|
||||
await dispatchTreeCommand(targetRow || sourceRow, {
|
||||
action: 'move',
|
||||
batchId: batchId,
|
||||
workspaceId: workspaceId,
|
||||
documentId: sourceId,
|
||||
parentId: targetParentId,
|
||||
sortOrder: targetParentId ? fileTreeChildCount(targetParentId) + i : i
|
||||
});
|
||||
} catch (error) {
|
||||
failures.push(sourceId + ': ' + (error && error.message ? error.message : '移动失败'));
|
||||
}
|
||||
}
|
||||
if (currentSourceKind() !== 'local_folder') {
|
||||
var assetIds = plan.fileAssetRows.map(fileTreeRowAssetId).filter(Boolean);
|
||||
if (assetIds.length > 0) {
|
||||
try {
|
||||
await postSidebarFileTreeJson('/api/media/batch', {
|
||||
action: 'move',
|
||||
assetIds: assetIds,
|
||||
targetDocumentId: targetParentId
|
||||
});
|
||||
} catch (error) {
|
||||
failures.push(assetIds.join(',') + ': ' + (error && error.message ? error.message : '移动附件失败'));
|
||||
}
|
||||
}
|
||||
}
|
||||
window.dispatchEvent(new CustomEvent('tree:local-command-batch-complete', { detail: { batchId: batchId, action: 'move', failed: failures.length, count: movableRows.length } }));
|
||||
if (failures.length > 0) {
|
||||
recordFileTreeActionStatus('failed', { documentId: targetParentId, fallback: 'alert', batchId: batchId });
|
||||
window.alert('部分对象移动失败:' + failures.join(';'));
|
||||
return false;
|
||||
}
|
||||
recordFileTreeActionStatus('applied', { documentId: targetParentId, batchId: batchId });
|
||||
return true;
|
||||
}
|
||||
|
||||
async function pasteSidebarFileTreeClipboard(trigger) {
|
||||
if (!runtimeState.sidebarFileTreeClipboard || !Array.isArray(runtimeState.sidebarFileTreeClipboard.rowIds) || runtimeState.sidebarFileTreeClipboard.rowIds.length === 0) return false;
|
||||
var targetRow = trigger instanceof HTMLElement ? trigger : null;
|
||||
if (!targetRow && sidebarFileTreeSelection.focusedRowId) {
|
||||
targetRow = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(sidebarFileTreeSelection.focusedRowId) + '"]');
|
||||
}
|
||||
var targetDocumentId = fileTreeRowDocumentId(targetRow) || currentDocumentId();
|
||||
if (!targetDocumentId) return false;
|
||||
var targetDocumentId = fileTreeMoveTargetParentId(targetRow) || currentDocumentId();
|
||||
var action = runtimeState.sidebarFileTreeClipboard.action === 'cut' ? 'move' : 'copy';
|
||||
var rows = fileTreeRowsByRowIds(runtimeState.sidebarFileTreeClipboard.rowIds);
|
||||
if (rows.length === 0) return false;
|
||||
var writable = await ensureFileTreeWritableTarget('paste', targetRow, runtimeState.sidebarFileTreeClipboard.rowIds, action === 'copy');
|
||||
if (!writable) return false;
|
||||
recordFileTreeAction('paste', {
|
||||
rowId: targetRow ? targetRow.getAttribute('data-row-id') || '' : '',
|
||||
documentId: targetDocumentId,
|
||||
sourceRowIds: runtimeState.sidebarFileTreeClipboard.rowIds,
|
||||
clipboardAction: runtimeState.sidebarFileTreeClipboard.action
|
||||
});
|
||||
var plan = buildSidebarFileTreeDeletePlan(rows);
|
||||
var workspaceId = resolveWorkspaceId(targetRow || document.body);
|
||||
var failures = [];
|
||||
if (action === 'copy') {
|
||||
dispatchSidebarEvent('tree.filetree.copy-requested', { rowIds: runtimeState.sidebarFileTreeClipboard.rowIds, targetDocumentId: targetDocumentId });
|
||||
recordFileTreeActionStatus('copy-requested', { documentId: targetDocumentId });
|
||||
return true;
|
||||
}
|
||||
for (var i = 0; i < plan.docRows.length; i += 1) {
|
||||
var docRow = plan.docRows[i];
|
||||
var documentId = fileTreeRowDocumentId(docRow);
|
||||
if (!documentId || documentId === targetDocumentId) continue;
|
||||
try {
|
||||
await dispatchTreeCommand(targetRow || docRow, {
|
||||
action: 'move',
|
||||
workspaceId: workspaceId,
|
||||
documentId: documentId,
|
||||
parentId: targetDocumentId,
|
||||
sortOrder: fileTreeChildCount(targetDocumentId) + i
|
||||
});
|
||||
} catch (error) {
|
||||
failures.push(documentId + ': ' + (error && error.message ? error.message : '移动页面失败'));
|
||||
}
|
||||
}
|
||||
var assetIds = plan.fileAssetRows.map(fileTreeRowAssetId).filter(Boolean);
|
||||
if (assetIds.length > 0) {
|
||||
try {
|
||||
await postSidebarFileTreeJson('/api/media/batch', {
|
||||
action: 'move',
|
||||
assetIds: assetIds,
|
||||
targetDocumentId: targetDocumentId
|
||||
});
|
||||
} catch (error) {
|
||||
failures.push(assetIds.join(',') + ': ' + (error && error.message ? error.message : '移动附件失败'));
|
||||
}
|
||||
}
|
||||
if (failures.length > 0) {
|
||||
recordFileTreeActionStatus('failed', { documentId: targetDocumentId, fallback: 'alert' });
|
||||
window.alert('部分对象移动失败:' + failures.join(';'));
|
||||
return false;
|
||||
}
|
||||
runtimeState.sidebarFileTreeClipboard = null;
|
||||
recordFileTreeActionStatus('applied', { documentId: targetDocumentId });
|
||||
return true;
|
||||
var ok = await moveSidebarFileTreeRows(runtimeState.sidebarFileTreeClipboard.rowIds, targetRow, { copy: action === 'copy' });
|
||||
if (ok && action === 'move') runtimeState.sidebarFileTreeClipboard = null;
|
||||
return ok;
|
||||
}
|
||||
|
||||
async function deleteSelectedSidebarFileTreeRows(trigger) {
|
||||
@@ -1409,8 +1575,10 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
var total = plan.docRows.length + plan.folderRows.length + plan.fileAssetRows.length + plan.mindmapRows.length + plan.tableRows.length;
|
||||
if (total === 0) return false;
|
||||
if (!window.confirm(sidebarFileTreeDeleteConfirmText(plan))) return false;
|
||||
recordFileTreeAction('bulk-delete', { rowId: trigger instanceof HTMLElement ? trigger.getAttribute('data-row-id') || '' : '', count: total });
|
||||
recordFileTreeActionStatus('pending', { count: total });
|
||||
var batchId = nextFileTreeOperationBatchId('bulk-delete');
|
||||
document.documentElement.setAttribute('data-mnote-filetree-bulk-delete-batch-id', batchId);
|
||||
recordFileTreeAction('bulk-delete', { rowId: trigger instanceof HTMLElement ? trigger.getAttribute('data-row-id') || '' : '', count: total, batchId: batchId });
|
||||
recordFileTreeActionStatus('pending', { count: total, batchId: batchId });
|
||||
var failures = [];
|
||||
for (var i = 0; i < plan.docRows.length; i += 1) {
|
||||
var docRow = plan.docRows[i];
|
||||
@@ -1418,6 +1586,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
try {
|
||||
await dispatchTreeCommand(trigger || docRow, {
|
||||
action: 'archive',
|
||||
batchId: batchId,
|
||||
workspaceId: resolveWorkspaceId(docRow),
|
||||
documentId: documentId
|
||||
});
|
||||
@@ -1432,6 +1601,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
try {
|
||||
await dispatchTreeCommand(trigger || folderRow, {
|
||||
action: 'archive',
|
||||
batchId: batchId,
|
||||
workspaceId: resolveWorkspaceId(folderRow),
|
||||
documentId: folderId
|
||||
});
|
||||
@@ -1446,6 +1616,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
for (var lf = 0; lf < fileAssetIds.length; lf += 1) {
|
||||
await dispatchTreeCommand(trigger || plan.fileAssetRows[lf], {
|
||||
action: 'archive',
|
||||
batchId: batchId,
|
||||
workspaceId: resolveWorkspaceId(plan.fileAssetRows[lf]),
|
||||
documentId: fileAssetIds[lf]
|
||||
});
|
||||
@@ -1455,7 +1626,9 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
}
|
||||
fileAssetIds.forEach(removeFileTreeAssetRow);
|
||||
} catch (error) {
|
||||
failures = failures.concat(fileAssetIds);
|
||||
failures = failures.concat(fileAssetIds.map(function(assetId) {
|
||||
return assetId + ': ' + (error && error.message ? error.message : '删除附件失败');
|
||||
}));
|
||||
}
|
||||
}
|
||||
for (var m = 0; m < plan.mindmapRows.length; m += 1) {
|
||||
@@ -1465,6 +1638,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
if (currentSourceKind() === 'local_folder') {
|
||||
await dispatchTreeCommand(trigger || mindmapRow, {
|
||||
action: 'archive',
|
||||
batchId: batchId,
|
||||
workspaceId: resolveWorkspaceId(mindmapRow),
|
||||
documentId: mindmapId
|
||||
});
|
||||
@@ -1485,6 +1659,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
if (currentSourceKind() === 'local_folder') {
|
||||
await dispatchTreeCommand(trigger || tableRow, {
|
||||
action: 'archive',
|
||||
batchId: batchId,
|
||||
workspaceId: resolveWorkspaceId(tableRow),
|
||||
documentId: tableId
|
||||
});
|
||||
@@ -1502,13 +1677,15 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
sidebarFileTreeSelection.focusedRowId = null;
|
||||
syncSidebarFileTreeSelection();
|
||||
if (failures.length > 0) {
|
||||
recordFileTreeActionStatus('failed', { count: total, failures: failures.slice(0, 20), fallback: 'alert' });
|
||||
window.dispatchEvent(new CustomEvent('tree:local-command-batch-complete', { detail: { batchId: batchId, action: 'bulk-delete', failed: failures.length, count: total } }));
|
||||
recordFileTreeActionStatus('failed', { count: total, failures: failures.slice(0, 20), fallback: 'alert', batchId: batchId });
|
||||
window.alert('部分对象删除失败:' + failures.slice(0, 5).join(', ') + (failures.length > 5 ? '…' : ''));
|
||||
return false;
|
||||
}
|
||||
document.documentElement.setAttribute('data-mnote-filetree-bulk-delete-applied', 'true');
|
||||
recordFileTreeActionStatus('archived', { count: total, undo: 'trash-modal' });
|
||||
if (currentSourceKind() === 'local_folder') void refreshLocalFolderSidebarSnapshot();
|
||||
window.dispatchEvent(new CustomEvent('tree:local-command-batch-complete', { detail: { batchId: batchId, action: 'bulk-delete', failed: 0, count: total } }));
|
||||
recordFileTreeActionStatus('archived', { count: total, undo: 'trash-modal', batchId: batchId });
|
||||
if (currentSourceKind() !== 'local_folder') void refreshLocalFolderSidebarSnapshot();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1565,6 +1742,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
postSidebarFileTreeJson,
|
||||
fileTreeRowsByRowIds,
|
||||
fileTreeChildCount,
|
||||
moveSidebarFileTreeRows,
|
||||
pasteSidebarFileTreeClipboard,
|
||||
deleteSelectedSidebarFileTreeRows,
|
||||
};
|
||||
|
||||
@@ -45,25 +45,15 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
}
|
||||
|
||||
function readGlobalShowHeadingNumbers() {
|
||||
try {
|
||||
var raw = window.localStorage ? window.localStorage.getItem(MNOTE_GLOBAL_SHOW_HEADING_NUMBERS_KEY) : '';
|
||||
return raw === 'true' || raw === '1';
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
return Boolean(currentPageOptions().showHeadingNumbers);
|
||||
}
|
||||
|
||||
function writeGlobalShowHeadingNumbers(value) {
|
||||
try {
|
||||
if (window.localStorage) {
|
||||
window.localStorage.setItem(MNOTE_GLOBAL_SHOW_HEADING_NUMBERS_KEY, value ? 'true' : 'false');
|
||||
}
|
||||
} catch (_) {}
|
||||
document.documentElement.setAttribute('data-global-show-heading-numbers', String(Boolean(value)));
|
||||
void persistPageOptionsPatch({ showHeadingNumbers: Boolean(value) });
|
||||
}
|
||||
|
||||
function effectiveShowHeadingNumbers(options) {
|
||||
return readGlobalShowHeadingNumbers();
|
||||
return Boolean(options && options.showHeadingNumbers);
|
||||
}
|
||||
|
||||
function pageOptionIsSupported(name) {
|
||||
@@ -161,7 +151,7 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
|
||||
function applyPageOptionsToShell() {
|
||||
var options = currentPageOptions();
|
||||
var globalShowHeadingNumbers = readGlobalShowHeadingNumbers();
|
||||
var globalShowHeadingNumbers = Boolean(options.showHeadingNumbers);
|
||||
var showHeadingNumbers = effectiveShowHeadingNumbers(options);
|
||||
var shell = document.querySelector('.document-shell');
|
||||
var editorRoot = document.querySelector('[data-testid="mnote-leptos-tiptap-island-editor-root"]');
|
||||
@@ -623,21 +613,27 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
applyPageOptionsToShell();
|
||||
renderPageSettingsPopover();
|
||||
try {
|
||||
var response = await fetch('/api/documents/options', {
|
||||
method: 'POST',
|
||||
var response = await fetch('/api/ui/preferences', {
|
||||
method: 'PUT',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
documentId: currentDocumentId(),
|
||||
workspaceId: resolveWorkspaceId(document.body),
|
||||
...currentWorkspaceSourcePayload(),
|
||||
options: nextOptions,
|
||||
commandName: 'page.layout.updateOptions'
|
||||
updates: nextOptions
|
||||
})
|
||||
});
|
||||
var payload = await response.json().catch(function(){ return null; });
|
||||
if (!response.ok || !payload || payload.ok !== true) {
|
||||
throw new Error(payload && payload.error && payload.error.message ? payload.error.message : 'page_settings_save_failed_' + response.status);
|
||||
}
|
||||
var savedOptions = payload && payload.result && payload.result.pageOptions && typeof payload.result.pageOptions === 'object'
|
||||
? Object.assign(defaultPageOptions(), payload.result.pageOptions)
|
||||
: nextOptions;
|
||||
pageUiState.pageOptions = Object.assign({}, savedOptions);
|
||||
nextOptions = Object.assign({}, pageUiState.pageOptions);
|
||||
applyPageOptionsToShell();
|
||||
renderPageSettingsPopover();
|
||||
var script = document.getElementById('__MNOTE_PAGE_AGGREGATE__');
|
||||
if (script) {
|
||||
try {
|
||||
|
||||
@@ -54,6 +54,8 @@ export const createSidebarPageTreeRuntime = (dependencies = {}) => {
|
||||
var targetUrl = new URL('/documents/' + encodeURIComponent(nodeId), window.location.origin);
|
||||
if (workspaceId) targetUrl.searchParams.set('workspaceId', workspaceId);
|
||||
if (treeView === 'filetree') targetUrl.searchParams.set('treeView', 'filetree');
|
||||
var fileTreeScope = String(options && options.fileTreeScope || '').trim();
|
||||
if (fileTreeScope) targetUrl.searchParams.set('fileTreeScope', fileTreeScope);
|
||||
copyWorkspaceSourceParams(targetUrl);
|
||||
var url = targetUrl.pathname + targetUrl.search;
|
||||
if (mnoteNavigationInFlight === url) return;
|
||||
|
||||
@@ -18,11 +18,29 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
shortMindmapFileName,
|
||||
syncSidebarFileTreeSelection,
|
||||
} = dependencies;
|
||||
var fileTreeLazyChildrenCache = new Map();
|
||||
var fileTreeExpandedRelativePaths = new Set();
|
||||
var fileTreeViewState = {
|
||||
rootUri: '',
|
||||
scope: '',
|
||||
expandedParents: new Set(),
|
||||
selectedRowIds: new Set(),
|
||||
focusedRowId: '',
|
||||
activeRowId: '',
|
||||
scrollTop: 0,
|
||||
rowsByParent: new Map(),
|
||||
loadedParents: new Set(),
|
||||
loadingParents: new Map(),
|
||||
dirtyParents: new Set(),
|
||||
staleParents: new Set(),
|
||||
revisionByParent: new Map(),
|
||||
requestGeneration: 0,
|
||||
latestGenerationByParent: new Map()
|
||||
};
|
||||
var fileTreeState = fileTreeViewState;
|
||||
var fileTreeExpandedRelativePaths = fileTreeViewState.expandedParents;
|
||||
var fileTreeLazyCacheRootUri = '';
|
||||
var fileTreeExpansionStorageRootUri = '';
|
||||
var fileTreeExpansionRestoreTimer = 0;
|
||||
var fileTreeCommandBatchRefreshParents = new Map();
|
||||
var FILETREE_EXPANSION_STORAGE_KEY = 'mnote.localFileTree.expandedRelativePaths.v1';
|
||||
|
||||
function updateTitleEverywhere(documentId, title) {
|
||||
@@ -70,6 +88,8 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
function isFileTreePageRow(row) {
|
||||
if (!(row instanceof HTMLElement)) return false;
|
||||
if (row.getAttribute('data-asset-id')) return false;
|
||||
var documentId = row.getAttribute('data-document-id') || row.getAttribute('data-doc-id') || '';
|
||||
if (documentId.indexOf('local-md:') === 0) return true;
|
||||
var rowKind = row.getAttribute('data-row-kind') || '';
|
||||
return rowKind === 'document' || rowKind === 'doc' || rowKind === 'index' || rowKind === 'markdown';
|
||||
}
|
||||
@@ -295,6 +315,134 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
function parentRelativePathForPath(relativePath) {
|
||||
var normalized = String(relativePath || '').trim().replace(/^\/+|\/+$/g, '');
|
||||
if (!normalized || normalized === '.') return '';
|
||||
var index = normalized.lastIndexOf('/');
|
||||
return index > 0 ? normalized.slice(0, index) : '';
|
||||
}
|
||||
|
||||
function addCommandRefreshParent(parents, value) {
|
||||
var normalized = String(value || '').trim().replace(/^\/+|\/+$/g, '');
|
||||
if (normalized === '.') normalized = '';
|
||||
parents.add(normalized);
|
||||
}
|
||||
|
||||
function addAffectedParentsFromCommandResult(parents, result) {
|
||||
var affectedParents = Array.isArray(result && result.affectedParents)
|
||||
? result.affectedParents
|
||||
: Array.isArray(result && result.execution && result.execution.affectedParents)
|
||||
? result.execution.affectedParents
|
||||
: null;
|
||||
if (!affectedParents) return false;
|
||||
var before = parents.size;
|
||||
affectedParents.forEach(function(parent) {
|
||||
addCommandRefreshParent(parents, parent && (parent.relativePath || parent.relative_path));
|
||||
});
|
||||
return parents.size > before;
|
||||
}
|
||||
|
||||
function collectFileTreeRefreshParentsForCommand(result, body) {
|
||||
var parents = new Set();
|
||||
if (!addAffectedParentsFromCommandResult(parents, result)) {
|
||||
document.documentElement.setAttribute('data-mnote-filetree-command-refresh-fallback', 'legacy-path-fields');
|
||||
var values = [
|
||||
result && (result.parentRelativePath || result.parent_relative_path),
|
||||
result && result.execution && (result.execution.parentRelativePath || result.execution.parent_relative_path),
|
||||
result && (result.relativePath || result.relative_path),
|
||||
result && result.execution && (result.execution.relativePath || result.execution.relative_path),
|
||||
result && (result.previousRelativePath || result.previous_relative_path),
|
||||
result && result.execution && (result.execution.previousRelativePath || result.execution.previous_relative_path),
|
||||
body && (body.parentRelativePath || body.parent_relative_path)
|
||||
];
|
||||
values.forEach(function(value) {
|
||||
var normalized = String(value || '').trim();
|
||||
if (!normalized) return;
|
||||
if (normalized.indexOf('/') >= 0 || /\.[^/]+$/.test(normalized)) addCommandRefreshParent(parents, parentRelativePathForPath(normalized));
|
||||
else addCommandRefreshParent(parents, normalized);
|
||||
});
|
||||
}
|
||||
if (!parents.size) addCommandRefreshParent(parents, currentFileTreeScope());
|
||||
return parents;
|
||||
}
|
||||
|
||||
function fileTreeRefreshParentsForCommand(result, body) {
|
||||
var parents = collectFileTreeRefreshParentsForCommand(result, body);
|
||||
return Promise.all(Array.from(parents).map(function(parentRelativePath) {
|
||||
return refreshFileTreeParent(parentRelativePath).catch(function(error) {
|
||||
setTreeLiveApplyError(error && error.message ? error.message : '文件树局部刷新失败');
|
||||
return false;
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
function queueFileTreeBatchRefresh(batchId, result, body) {
|
||||
var normalizedBatchId = String(batchId || '').trim();
|
||||
if (!normalizedBatchId) return false;
|
||||
var batchParents = fileTreeCommandBatchRefreshParents.get(normalizedBatchId);
|
||||
if (!batchParents) {
|
||||
batchParents = new Set();
|
||||
fileTreeCommandBatchRefreshParents.set(normalizedBatchId, batchParents);
|
||||
}
|
||||
collectFileTreeRefreshParentsForCommand(result, body).forEach(function(parent) {
|
||||
batchParents.add(parent);
|
||||
});
|
||||
document.documentElement.setAttribute('data-mnote-filetree-batch-refresh-pending', normalizedBatchId);
|
||||
return true;
|
||||
}
|
||||
|
||||
function flushFileTreeBatchRefresh(batchId) {
|
||||
var normalizedBatchId = String(batchId || '').trim();
|
||||
if (!normalizedBatchId) return false;
|
||||
var parents = fileTreeCommandBatchRefreshParents.get(normalizedBatchId);
|
||||
fileTreeCommandBatchRefreshParents.delete(normalizedBatchId);
|
||||
if (!parents || !parents.size) return false;
|
||||
document.documentElement.setAttribute('data-mnote-filetree-batch-refresh-applied', normalizedBatchId);
|
||||
void Promise.all(Array.from(parents).map(function(parentRelativePath) {
|
||||
return refreshFileTreeParent(parentRelativePath).catch(function(error) {
|
||||
setTreeLiveApplyError(error && error.message ? error.message : '文件树批量刷新失败');
|
||||
return false;
|
||||
});
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
|
||||
function applyLocalFolderWatchBatch(payload) {
|
||||
var batch = payload && (payload.payload || payload);
|
||||
var affectedParents = Array.isArray(batch && batch.affectedParents)
|
||||
? batch.affectedParents
|
||||
: Array.isArray(batch && batch.affected_parents)
|
||||
? batch.affected_parents
|
||||
: [];
|
||||
if (!affectedParents.length) {
|
||||
setTreeLiveApplyError('local_folder_watch_batch_missing_affected_parents');
|
||||
return false;
|
||||
}
|
||||
var parents = new Set();
|
||||
affectedParents.forEach(function(parent) {
|
||||
addCommandRefreshParent(parents, parent && (parent.relativePath || parent.relative_path));
|
||||
});
|
||||
document.documentElement.setAttribute('data-mnote-local-folder-watch-batch-applied', String(batch.revision || 'true'));
|
||||
void Promise.all(Array.from(parents).map(function(parentRelativePath) {
|
||||
return refreshFileTreeParent(parentRelativePath).catch(function(error) {
|
||||
setTreeLiveApplyError(error && error.message ? error.message : '文件树 watch batch 刷新失败');
|
||||
return false;
|
||||
});
|
||||
})).then(function() {
|
||||
markLocalFolderWatchApplied('watch_batch');
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
function refreshLocalFolderAfterCommand(action, result, body) {
|
||||
if (!localCommandNeedsProjectionRefresh(action, result)) return;
|
||||
if (body && body.batchId) {
|
||||
queueFileTreeBatchRefresh(body.batchId, result, body);
|
||||
return;
|
||||
}
|
||||
void fileTreeRefreshParentsForCommand(result, body);
|
||||
}
|
||||
|
||||
function sortOrderFromDelta(data) {
|
||||
var raw = data && (data.sortOrder ?? data.sort_order);
|
||||
var value = Number(raw);
|
||||
@@ -440,6 +588,17 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
return String(item && (item.relativePath || item.rootRelativePath) || '').trim();
|
||||
}
|
||||
|
||||
function normalizeFileTreeRelativePath(value) {
|
||||
var normalized = String(value || '').trim().replace(/^\/+|\/+$/g, '');
|
||||
return normalized === '.' ? '' : normalized;
|
||||
}
|
||||
|
||||
function fileTreeRowByRelativePath(relativePath) {
|
||||
var normalized = normalizeFileTreeRelativePath(relativePath);
|
||||
if (!normalized) return null;
|
||||
return document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-local-relative-path="' + cssEscape(normalized) + '"]');
|
||||
}
|
||||
|
||||
function groupRowsByParent(rows) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('groupRowsByParent');
|
||||
if (runtimeFn) return runtimeFn(rows);
|
||||
@@ -454,6 +613,53 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
return grouped;
|
||||
}
|
||||
|
||||
function fileTreeParentKey(rootUri, parentRelativePath) {
|
||||
return String(rootUri || '').trim() + '\n' + String(parentRelativePath || '').trim();
|
||||
}
|
||||
|
||||
function currentFileTreeParentKey(parentRelativePath) {
|
||||
return fileTreeParentKey(currentRootUri(), parentRelativePath);
|
||||
}
|
||||
|
||||
function projectionParentRelativePath(projection) {
|
||||
var resolved = readProjection(projection);
|
||||
return String(resolved && (resolved.parentRelativePath || resolved.parent_relative_path) || '').trim();
|
||||
}
|
||||
|
||||
function isFileTreeRootProjectionParent(parentRelativePath) {
|
||||
return normalizeFileTreeRelativePath(parentRelativePath) === normalizeFileTreeRelativePath(currentFileTreeScope());
|
||||
}
|
||||
|
||||
function rememberFileTreeProjection(parentRelativePath, rows, projection) {
|
||||
var key = currentFileTreeParentKey(parentRelativePath);
|
||||
fileTreeState.rowsByParent.set(key, rows);
|
||||
fileTreeState.loadedParents.add(key);
|
||||
fileTreeState.dirtyParents.delete(key);
|
||||
fileTreeState.staleParents.delete(key);
|
||||
var resolved = readProjection(projection);
|
||||
var watchRevision = resolved && (resolved.watchRevision || resolved.watch_revision);
|
||||
if (watchRevision) fileTreeState.revisionByParent.set(key, watchRevision);
|
||||
}
|
||||
|
||||
function cachedFileTreeRows(parentRelativePath) {
|
||||
return fileTreeState.rowsByParent.get(currentFileTreeParentKey(parentRelativePath)) || [];
|
||||
}
|
||||
|
||||
function beginFileTreeRequest(key) {
|
||||
fileTreeState.requestGeneration += 1;
|
||||
fileTreeState.latestGenerationByParent.set(key, fileTreeState.requestGeneration);
|
||||
return fileTreeState.requestGeneration;
|
||||
}
|
||||
|
||||
function isLatestFileTreeRequest(key, generation) {
|
||||
return fileTreeState.latestGenerationByParent.get(key) === generation;
|
||||
}
|
||||
|
||||
function markFileTreeParentStale(key) {
|
||||
fileTreeState.staleParents.add(key);
|
||||
fileTreeState.dirtyParents.add(key);
|
||||
}
|
||||
|
||||
function pageTreeChevronSvg() {
|
||||
return '<svg class="tree-toggle-icon" viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" focusable="false"><path d="M7.84 14.955c.206 0 .37-.07.505-.21l4.277-4.179a.79.79 0 0 0 .264-.574.78.78 0 0 0-.258-.574L8.35 5.24a.7.7 0 0 0-.51-.21.721.721 0 0 0-.498 1.247l3.814 3.721-3.814 3.709a.721.721 0 0 0 .498 1.248"></path></svg>';
|
||||
}
|
||||
@@ -586,7 +792,7 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
var objectIdentity = fileObjectIdentity(item);
|
||||
var ownerDocumentId = fileOwnerDocumentId(item, documentId, objectIdentity);
|
||||
var iconKind = iconKindOf(item);
|
||||
var cachedChildren = relativePath ? (fileTreeLazyChildrenCache.get(relativePath) || []) : [];
|
||||
var cachedChildren = relativePath ? cachedFileTreeRows(relativePath) : [];
|
||||
var title = isFileTreeProjectionPageRow(rowKind, assetId)
|
||||
? fileTreePageTitle(rawTitle)
|
||||
: normalizeMindmapFileTreeTitle(rawTitle, assetId, iconKind, objectIdentity);
|
||||
@@ -615,9 +821,19 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
|
||||
function ensureFileTreeLazyCacheScope() {
|
||||
var rootUri = currentRootUri();
|
||||
if (rootUri === fileTreeLazyCacheRootUri) return;
|
||||
var scope = currentFileTreeScope();
|
||||
if (rootUri === fileTreeLazyCacheRootUri && scope === fileTreeState.scope) return;
|
||||
fileTreeLazyCacheRootUri = rootUri;
|
||||
fileTreeLazyChildrenCache.clear();
|
||||
fileTreeState.rootUri = rootUri;
|
||||
fileTreeState.scope = scope;
|
||||
fileTreeState.rowsByParent.clear();
|
||||
fileTreeState.loadedParents.clear();
|
||||
fileTreeState.loadingParents.clear();
|
||||
fileTreeState.dirtyParents.clear();
|
||||
fileTreeState.staleParents.clear();
|
||||
fileTreeState.revisionByParent.clear();
|
||||
fileTreeState.latestGenerationByParent.clear();
|
||||
fileTreeState.requestGeneration += 1;
|
||||
fileTreeExpandedRelativePaths.clear();
|
||||
loadStoredFileTreeExpansionState(rootUri);
|
||||
}
|
||||
@@ -683,15 +899,80 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
if (changed) persistFileTreeExpansionState();
|
||||
}
|
||||
|
||||
function rememberFileTreeSelectionState() {
|
||||
fileTreeViewState.selectedRowIds.clear();
|
||||
fileTreeViewState.focusedRowId = '';
|
||||
fileTreeViewState.activeRowId = '';
|
||||
document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"]').forEach(function(row) {
|
||||
if (!(row instanceof HTMLElement)) return;
|
||||
var rowId = String(row.getAttribute('data-row-id') || '').trim();
|
||||
if (!rowId) return;
|
||||
if (row.getAttribute('data-selected') === 'true') fileTreeViewState.selectedRowIds.add(rowId);
|
||||
if (row.getAttribute('data-focused') === 'true') fileTreeViewState.focusedRowId = rowId;
|
||||
if (row.getAttribute('data-active') === 'true') fileTreeViewState.activeRowId = rowId;
|
||||
});
|
||||
}
|
||||
|
||||
function reprojectFileTreeSelectionState() {
|
||||
document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"]').forEach(function(row) {
|
||||
if (!(row instanceof HTMLElement)) return;
|
||||
var rowId = String(row.getAttribute('data-row-id') || '').trim();
|
||||
row.setAttribute('data-selected', String(fileTreeViewState.selectedRowIds.has(rowId)));
|
||||
row.setAttribute('data-focused', String(rowId === fileTreeViewState.focusedRowId));
|
||||
if (fileTreeViewState.activeRowId) {
|
||||
row.setAttribute('data-active', String(rowId === fileTreeViewState.activeRowId));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderFileProjection(projection) {
|
||||
var tree = document.getElementById('sidebar-file-tree-root');
|
||||
if (!tree) return false;
|
||||
ensureFileTreeLazyCacheScope();
|
||||
rememberFileTreeExpansionState();
|
||||
rememberFileTreeSelectionState();
|
||||
var rows = projectionItems(projection);
|
||||
var parentRelativePath = projectionParentRelativePath(projection) || currentFileTreeScope();
|
||||
rememberFileTreeProjection(parentRelativePath, rows, projection);
|
||||
if (!isFileTreeRootProjectionParent(parentRelativePath)) {
|
||||
return patchFileTreeParentChildren(parentRelativePath, rows);
|
||||
}
|
||||
var activeId = currentDocumentId();
|
||||
var activeRowId = currentFileTreeActiveRowId();
|
||||
tree.innerHTML = '<ul class="tree-root" role="tree" data-rust-filetree-renderer="initial_v1">' + (rows.length ? renderFileRows('', groupRowsByParent(rows), activeId, activeRowId) : '') + '</ul>';
|
||||
var template = document.createElement('template');
|
||||
template.innerHTML = '<ul class="tree-root" role="tree" data-rust-filetree-renderer="initial_v1">' + (rows.length ? renderFileRows('', groupRowsByParent(rows), activeId, activeRowId) : '') + '</ul>';
|
||||
tree.replaceChildren(template.content.cloneNode(true));
|
||||
reprojectFileTreeSelectionState();
|
||||
scheduleRestorePersistedFileTreeExpansionState();
|
||||
return true;
|
||||
}
|
||||
|
||||
function patchFileTreeParentChildren(parentRelativePath, rows) {
|
||||
var row = fileTreeRowByRelativePath(parentRelativePath);
|
||||
if (!(row instanceof HTMLElement)) return true;
|
||||
var node = row.closest('.tree-node');
|
||||
if (!node) return true;
|
||||
rememberFileTreeSelectionState();
|
||||
var button = row.querySelector('[data-rust-action="toggle"]');
|
||||
var wasExpanded = row.getAttribute('aria-expanded') === 'true';
|
||||
var children = node.querySelector(':scope > .tree-children');
|
||||
if (!children && !wasExpanded && row.getAttribute('data-filetree-children-loaded') !== 'true') {
|
||||
return true;
|
||||
}
|
||||
if (!children) {
|
||||
children = document.createElement('ul');
|
||||
children.className = 'tree-children';
|
||||
node.appendChild(children);
|
||||
}
|
||||
var template = document.createElement('template');
|
||||
template.innerHTML = renderFileRows('', groupRowsByParent(rows), currentDocumentId(), currentFileTreeActiveRowId());
|
||||
children.replaceChildren(template.content.cloneNode(true));
|
||||
children.classList.toggle('tree-children--collapsed', !wasExpanded);
|
||||
row.setAttribute('data-filetree-children-loaded', 'true');
|
||||
row.removeAttribute('data-filetree-children-loading');
|
||||
setTreeRowExpanded(row, button, wasExpanded);
|
||||
syncSidebarFileTreeSelection();
|
||||
reprojectFileTreeSelectionState();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -702,6 +983,23 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
return renderedPage || renderedFile;
|
||||
}
|
||||
|
||||
function renderLiveSidebarSnapshot(payload) {
|
||||
var renderedPage = hasProjectionItems(payload) ? renderPageProjection(payload) : false;
|
||||
var fileProjection = readDatasetProjection(payload, 'kernel_file_tree_projection', 'kernelFileTreeProjection');
|
||||
var hasFileProjection = fileProjection && hasProjectionItems(fileProjection);
|
||||
if (currentFileTreeScope()) {
|
||||
if (!hasFileProjection) return renderedPage;
|
||||
var projectionParent = projectionParentRelativePath(fileProjection);
|
||||
if (isFileTreeRootProjectionParent(projectionParent)) {
|
||||
return renderFileProjection(fileProjection) || renderedPage;
|
||||
}
|
||||
document.documentElement.setAttribute('data-mnote-filetree-scoped-live-snapshot-ignored', projectionParent || 'root');
|
||||
return true;
|
||||
}
|
||||
var renderedFile = hasFileProjection ? renderFileProjection(fileProjection) : false;
|
||||
return renderedPage || renderedFile;
|
||||
}
|
||||
|
||||
function replaceSidebarTreeFromDocument(nextDocument, rootId) {
|
||||
var runtimeFn = fileTreeRuntimeFunction('replaceSidebarTreeFromDocument');
|
||||
if (runtimeFn) return runtimeFn(nextDocument, rootId);
|
||||
@@ -739,10 +1037,61 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
return true;
|
||||
}
|
||||
|
||||
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() : '';
|
||||
}
|
||||
|
||||
async function fetchFileTreeProjection(parentRelativePath, options) {
|
||||
options = options || {};
|
||||
var rootUri = currentRootUri();
|
||||
if (!rootUri) return null;
|
||||
var url = new URL(options.childrenOnly ? '/api/tree/projections/file/children' : '/api/tree/projections/file', window.location.origin);
|
||||
var workspaceId = currentWorkspaceId();
|
||||
if (workspaceId) url.searchParams.set('workspaceId', workspaceId);
|
||||
url.searchParams.set('sourceKind', 'local_folder');
|
||||
url.searchParams.set('rootUri', rootUri);
|
||||
if (parentRelativePath) url.searchParams.set('parentRelativePath', parentRelativePath);
|
||||
var currentId = currentDocumentId();
|
||||
if (!options.childrenOnly && currentId) url.searchParams.set('rootNodeId', currentId);
|
||||
var response = await fetch(url.toString(), { headers: { accept: 'application/json' } });
|
||||
var payload = await response.json().catch(function() { return null; });
|
||||
if (!response.ok) throw new Error(payload && (payload.error || payload.message) || 'filetree_projection_failed_' + response.status);
|
||||
return readProjection(payload && (payload.result || payload));
|
||||
}
|
||||
|
||||
async function refreshFileTreeParent(parentRelativePath) {
|
||||
ensureFileTreeLazyCacheScope();
|
||||
var key = currentFileTreeParentKey(parentRelativePath);
|
||||
fileTreeState.dirtyParents.add(key);
|
||||
if (!isFileTreeRootProjectionParent(parentRelativePath)) {
|
||||
var row = fileTreeRowByRelativePath(parentRelativePath);
|
||||
if (row instanceof HTMLElement && row.getAttribute('aria-expanded') !== 'true') {
|
||||
markFileTreeParentStale(key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (fileTreeState.loadingParents.has(key)) {
|
||||
return fileTreeState.loadingParents.get(key).then(function(rows) {
|
||||
fileTreeState.dirtyParents.delete(key);
|
||||
return isFileTreeRootProjectionParent(parentRelativePath)
|
||||
? renderFileProjection({ parentRelativePath: parentRelativePath, items: rows })
|
||||
: patchFileTreeParentChildren(parentRelativePath, rows);
|
||||
});
|
||||
}
|
||||
var projection = await fetchFileTreeProjection(parentRelativePath, { childrenOnly: false });
|
||||
if (!projection) return false;
|
||||
return renderFileProjection(projection);
|
||||
}
|
||||
|
||||
async function refreshLocalFolderSidebarSnapshot() {
|
||||
var workspaceId = currentWorkspaceId();
|
||||
var rootUri = currentRootUri();
|
||||
var currentId = currentDocumentId();
|
||||
var fileTreeScope = currentFileTreeScope();
|
||||
if (!workspaceId || !rootUri) return false;
|
||||
|
||||
var sidebarUrl = new URL('/api/tree/projections/sidebar', window.location.origin);
|
||||
@@ -750,22 +1099,24 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
sidebarUrl.searchParams.set('sourceKind', 'local_folder');
|
||||
sidebarUrl.searchParams.set('rootUri', rootUri);
|
||||
if (currentId) sidebarUrl.searchParams.set('rootNodeId', currentId);
|
||||
var fileUrl = new URL('/api/tree/projections/file', window.location.origin);
|
||||
fileUrl.searchParams.set('workspaceId', workspaceId);
|
||||
fileUrl.searchParams.set('sourceKind', 'local_folder');
|
||||
fileUrl.searchParams.set('rootUri', rootUri);
|
||||
if (currentId) fileUrl.searchParams.set('rootNodeId', currentId);
|
||||
|
||||
var responses = await Promise.all([
|
||||
var responses = await Promise.allSettled([
|
||||
fetch(sidebarUrl.toString(), { headers: { accept: 'application/json' } }),
|
||||
fetch(fileUrl.toString(), { headers: { accept: 'application/json' } })
|
||||
refreshFileTreeParent(fileTreeScope)
|
||||
]);
|
||||
if (!responses[0].ok && !responses[1].ok) return false;
|
||||
var sidebarResponse = responses[0].status === 'fulfilled' ? responses[0].value : null;
|
||||
var renderedFile = responses[1].status === 'fulfilled' ? responses[1].value : false;
|
||||
if ((!sidebarResponse || !sidebarResponse.ok) && !renderedFile) return false;
|
||||
|
||||
var sidebarPayload = responses[0].ok ? await responses[0].json().catch(function() { return null; }) : null;
|
||||
var filePayload = responses[1].ok ? await responses[1].json().catch(function() { return null; }) : null;
|
||||
var renderedPage = sidebarPayload ? renderSidebarSnapshot(sidebarPayload.result || sidebarPayload) : false;
|
||||
var renderedFile = filePayload ? renderFileProjection(filePayload.result || filePayload) : false;
|
||||
var sidebarPayload = sidebarResponse && sidebarResponse.ok ? await sidebarResponse.json().catch(function() { return null; }) : null;
|
||||
var resolvedSidebarPayload = sidebarPayload ? (sidebarPayload.result || sidebarPayload) : null;
|
||||
var renderedPage = resolvedSidebarPayload && hasProjectionItems(resolvedSidebarPayload)
|
||||
? renderPageProjection(resolvedSidebarPayload)
|
||||
: false;
|
||||
if (renderedFile && fileTreeScope) {
|
||||
var fileRoot = document.getElementById('sidebar-file-tree-root');
|
||||
if (fileRoot instanceof HTMLElement) fileRoot.setAttribute('data-mnote-filetree-scope', fileTreeScope);
|
||||
document.documentElement.setAttribute('data-mnote-filetree-scope', fileTreeScope);
|
||||
}
|
||||
if (!renderedPage && !renderedFile) return false;
|
||||
syncSidebarFileTreeSelection();
|
||||
schedulePendingLocalFolderRestoreFocus();
|
||||
@@ -783,22 +1134,35 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
scheduleRestorePersistedFileTreeExpansionState();
|
||||
var revision = '';
|
||||
var refreshTimer = 0;
|
||||
var treeLiveEventsActive = function() {
|
||||
var treeTransport = document.documentElement.getAttribute('data-mnote-tree-live-transport') || '';
|
||||
return treeTransport === 'local-folder-events';
|
||||
};
|
||||
var scheduleRefresh = function() {
|
||||
if (treeLiveEventsActive()) return;
|
||||
if (refreshTimer) return;
|
||||
refreshTimer = window.setTimeout(function() {
|
||||
refreshTimer = 0;
|
||||
if (treeLiveEventsActive()) return;
|
||||
var fileTreeScope = currentFileTreeScope();
|
||||
if (fileTreeScope) {
|
||||
markFileTreeParentStale(currentFileTreeParentKey(fileTreeScope));
|
||||
markLocalFolderWatchApplied('scope_stale');
|
||||
document.documentElement.setAttribute('data-mnote-filetree-scope-watch-stale', fileTreeScope);
|
||||
return;
|
||||
}
|
||||
void refreshLocalFolderSidebarSnapshot();
|
||||
}, 180);
|
||||
};
|
||||
var poll = async function() {
|
||||
if (document.hidden) return;
|
||||
// If tree live SSE transport is active for local_folder, skip polling (fallback)
|
||||
var treeTransport = document.documentElement.getAttribute('data-mnote-tree-live-transport') || '';
|
||||
if (treeTransport === 'local-folder-events') return;
|
||||
if (treeLiveEventsActive()) return;
|
||||
var url = new URL('/api/tree/local-folder-watch', window.location.origin);
|
||||
url.searchParams.set('rootUri', rootUri);
|
||||
var response = await fetch(url.toString(), { headers: { accept: 'application/json' } });
|
||||
if (!response.ok) return;
|
||||
if (treeLiveEventsActive()) return;
|
||||
var payload = await response.json();
|
||||
var nextRevision = payload && payload.result && typeof payload.result.revision === 'string'
|
||||
? payload.result.revision
|
||||
@@ -881,8 +1245,23 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
persistFileTreeExpansionState();
|
||||
}
|
||||
|
||||
function markExistingFileTreeChildrenLoaded(row, button) {
|
||||
if (!(row instanceof HTMLElement)) return false;
|
||||
var node = row.closest('.tree-node');
|
||||
if (!node) return false;
|
||||
var children = node.querySelector(':scope > .tree-children');
|
||||
if (!(children instanceof HTMLElement)) return false;
|
||||
row.setAttribute('data-filetree-children-loaded', 'true');
|
||||
children.classList.remove('tree-children--collapsed');
|
||||
setTreeRowExpanded(row, button, true);
|
||||
syncSidebarFileTreeSelection();
|
||||
return true;
|
||||
}
|
||||
|
||||
function renderCachedFileTreeChildren(row, button, relativePath) {
|
||||
var cachedRows = fileTreeLazyChildrenCache.get(relativePath) || [];
|
||||
var key = currentFileTreeParentKey(relativePath);
|
||||
if (fileTreeState.staleParents.has(key) || fileTreeState.dirtyParents.has(key)) return false;
|
||||
var cachedRows = cachedFileTreeRows(relativePath);
|
||||
if (!cachedRows.length) return false;
|
||||
var node = row.closest('.tree-node');
|
||||
if (!node) return false;
|
||||
@@ -892,7 +1271,9 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
children.className = 'tree-children';
|
||||
node.appendChild(children);
|
||||
}
|
||||
children.innerHTML = renderFileRows('', groupRowsByParent(cachedRows), currentDocumentId(), currentFileTreeActiveRowId());
|
||||
var template = document.createElement('template');
|
||||
template.innerHTML = renderFileRows('', groupRowsByParent(cachedRows), currentDocumentId(), currentFileTreeActiveRowId());
|
||||
children.replaceChildren(template.content.cloneNode(true));
|
||||
children.classList.remove('tree-children--collapsed');
|
||||
row.setAttribute('data-filetree-children-loaded', 'true');
|
||||
setTreeRowExpanded(row, button, true);
|
||||
@@ -900,31 +1281,47 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
return true;
|
||||
}
|
||||
|
||||
async function getFileTreeChildren(parentRelativePath) {
|
||||
ensureFileTreeLazyCacheScope();
|
||||
var key = currentFileTreeParentKey(parentRelativePath);
|
||||
if (fileTreeState.loadedParents.has(key) && !fileTreeState.dirtyParents.has(key) && !fileTreeState.staleParents.has(key)) {
|
||||
return fileTreeState.rowsByParent.get(key) || [];
|
||||
}
|
||||
if (fileTreeState.loadingParents.has(key)) {
|
||||
return fileTreeState.loadingParents.get(key);
|
||||
}
|
||||
var generation = beginFileTreeRequest(key);
|
||||
var promise = fetchFileTreeProjection(parentRelativePath, { childrenOnly: true }).then(function(projection) {
|
||||
if (!isLatestFileTreeRequest(key, generation)) {
|
||||
return fileTreeState.rowsByParent.get(key) || [];
|
||||
}
|
||||
var rows = projectionItems(projection);
|
||||
rememberFileTreeProjection(parentRelativePath, rows, projection);
|
||||
return rows;
|
||||
}).finally(function() {
|
||||
fileTreeState.loadingParents.delete(key);
|
||||
});
|
||||
fileTreeState.loadingParents.set(key, promise);
|
||||
return promise;
|
||||
}
|
||||
|
||||
async function loadFileTreeChildren(row, button) {
|
||||
if (!(row instanceof HTMLElement)) return false;
|
||||
if (row.getAttribute('data-shell-mode') !== 'filetree') return false;
|
||||
if (currentSourceKind() !== 'local_folder') return false;
|
||||
ensureFileTreeLazyCacheScope();
|
||||
var relativePath = localFileTreeRelativePathFromRow(row);
|
||||
if (relativePath && renderCachedFileTreeChildren(row, button, relativePath)) return true;
|
||||
var key = currentFileTreeParentKey(relativePath);
|
||||
var stale = fileTreeState.staleParents.has(key) || fileTreeState.dirtyParents.has(key);
|
||||
if (!stale && markExistingFileTreeChildrenLoaded(row, button)) return true;
|
||||
if (!stale && relativePath && renderCachedFileTreeChildren(row, button, relativePath)) return true;
|
||||
if (row.getAttribute('data-filetree-children-loaded') === 'true') return false;
|
||||
if (row.getAttribute('data-filetree-children-loading') === 'true') return true;
|
||||
var rootUri = currentRootUri();
|
||||
if (!rootUri || !relativePath) return false;
|
||||
setTreeRowExpanded(row, button, true);
|
||||
row.setAttribute('data-filetree-children-loading', 'true');
|
||||
try {
|
||||
var url = new URL('/api/tree/projections/file/children', window.location.origin);
|
||||
var workspaceId = currentWorkspaceId();
|
||||
if (workspaceId) url.searchParams.set('workspaceId', workspaceId);
|
||||
url.searchParams.set('sourceKind', 'local_folder');
|
||||
url.searchParams.set('rootUri', rootUri);
|
||||
url.searchParams.set('parentRelativePath', relativePath);
|
||||
var response = await fetch(url.toString(), { headers: { accept: 'application/json' } });
|
||||
var payload = await response.json().catch(function() { return null; });
|
||||
if (!response.ok) throw new Error(payload && (payload.error || payload.message) || 'filetree_children_failed_' + response.status);
|
||||
var projection = readProjection(payload && (payload.result || payload));
|
||||
var rows = projectionItems(projection);
|
||||
fileTreeLazyChildrenCache.set(relativePath, rows);
|
||||
var rows = await getFileTreeChildren(relativePath);
|
||||
if (!rows.length) {
|
||||
row.setAttribute('data-filetree-children-loaded', 'true');
|
||||
setTreeRowExpanded(row, button, true);
|
||||
@@ -939,6 +1336,53 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
function fileTreeParentChainForRelativePath(relativePath) {
|
||||
var normalized = normalizeFileTreeRelativePath(relativePath);
|
||||
if (!normalized) return [];
|
||||
var parts = normalized.split('/').filter(Boolean);
|
||||
parts.pop();
|
||||
var chain = [];
|
||||
for (var index = 0; index < parts.length; index += 1) {
|
||||
chain.push(parts.slice(0, index + 1).join('/'));
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
async function revealFileTreeResource(input) {
|
||||
input = input || {};
|
||||
var rootUri = String(input.rootUri || currentRootUri() || '').trim();
|
||||
if (!rootUri || rootUri !== currentRootUri()) return false;
|
||||
ensureFileTreeLazyCacheScope();
|
||||
var relativePath = normalizeFileTreeRelativePath(input.relativePath || input.relative_path || '');
|
||||
var rowId = String(input.rowId || input.row_id || '').trim();
|
||||
var parentChain = fileTreeParentChainForRelativePath(relativePath);
|
||||
for (var index = 0; index < parentChain.length; index += 1) {
|
||||
var parentRelativePath = parentChain[index];
|
||||
var parentRow = fileTreeRowByRelativePath(parentRelativePath);
|
||||
if (!(parentRow instanceof HTMLElement)) continue;
|
||||
var button = parentRow.querySelector('[data-rust-action="toggle"]');
|
||||
await getFileTreeChildren(parentRelativePath);
|
||||
renderCachedFileTreeChildren(parentRow, button, parentRelativePath);
|
||||
setTreeRowExpanded(parentRow, button, true);
|
||||
}
|
||||
var targetRow = rowId
|
||||
? document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(rowId) + '"]')
|
||||
: null;
|
||||
if (!(targetRow instanceof HTMLElement) && relativePath) {
|
||||
targetRow = fileTreeRowByRelativePath(relativePath);
|
||||
}
|
||||
if (!(targetRow instanceof HTMLElement)) return false;
|
||||
var targetRowId = String(targetRow.getAttribute('data-row-id') || rowId || '').trim();
|
||||
if (targetRowId) {
|
||||
fileTreeViewState.selectedRowIds = new Set([targetRowId]);
|
||||
fileTreeViewState.focusedRowId = targetRowId;
|
||||
fileTreeViewState.activeRowId = targetRowId;
|
||||
}
|
||||
reprojectFileTreeSelectionState();
|
||||
try { targetRow.scrollIntoView({ block: 'nearest' }); } catch (_) {}
|
||||
return true;
|
||||
}
|
||||
|
||||
function toggleChildren(row, button) {
|
||||
var li = row && row.parentElement;
|
||||
var children = li ? li.querySelector(':scope > .tree-children') : null;
|
||||
@@ -962,6 +1406,10 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
var relativePath = localFileTreeRelativePathFromRow(row);
|
||||
if (!relativePath || !fileTreeExpandedRelativePaths.has(relativePath)) return;
|
||||
var button = row.querySelector('[data-rust-action="toggle"]');
|
||||
if (markExistingFileTreeChildrenLoaded(row, button)) {
|
||||
restored = true;
|
||||
return;
|
||||
}
|
||||
if (row.getAttribute('data-filetree-children-loaded') === 'true') {
|
||||
setTreeRowExpanded(row, button, true);
|
||||
restored = true;
|
||||
@@ -998,7 +1446,7 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
var result = detail.result || {};
|
||||
if (action === 'create') {
|
||||
applyCreatedDocumentLocally(result, body.parentId || null, body.title || '新页面');
|
||||
if (localCommandNeedsProjectionRefresh(action, result)) void refreshLocalFolderSidebarSnapshot();
|
||||
refreshLocalFolderAfterCommand(action, result, body);
|
||||
return;
|
||||
}
|
||||
if (action === 'rename' && body.documentId && body.title) {
|
||||
@@ -1012,7 +1460,7 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
} else {
|
||||
updateTitleEverywhere(body.documentId, body.title);
|
||||
}
|
||||
if (localCommandNeedsProjectionRefresh(action, result)) void refreshLocalFolderSidebarSnapshot();
|
||||
refreshLocalFolderAfterCommand(action, result, body);
|
||||
document.documentElement.setAttribute('data-mnote-tree-local-command-applied', 'rename');
|
||||
return;
|
||||
}
|
||||
@@ -1020,20 +1468,25 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
if (applyMoveDocumentDelta({ documentId: body.documentId, parentId: body.parentId || null, sortOrder: body.sortOrder })) {
|
||||
document.documentElement.setAttribute('data-mnote-tree-local-command-applied', 'move');
|
||||
}
|
||||
if (localCommandNeedsProjectionRefresh(action, result)) void refreshLocalFolderSidebarSnapshot();
|
||||
refreshLocalFolderAfterCommand(action, result, body);
|
||||
return;
|
||||
}
|
||||
if ((action === 'purge' || action === 'delete' || action === 'archive') && body.documentId) {
|
||||
if (applyRemoveDocumentDelta({ documentId: body.documentId })) {
|
||||
document.documentElement.setAttribute('data-mnote-tree-local-command-applied', 'remove');
|
||||
}
|
||||
if (localCommandNeedsProjectionRefresh(action, result)) void refreshLocalFolderSidebarSnapshot();
|
||||
refreshLocalFolderAfterCommand(action, result, body);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('tree:local-command-batch-complete', function(event) {
|
||||
var detail = event.detail || {};
|
||||
flushFileTreeBatchRefresh(detail.batchId || detail.batch_id || '');
|
||||
});
|
||||
|
||||
window.addEventListener('tree:snapshot', function(event) {
|
||||
var payload = event.detail && event.detail.payload ? event.detail.payload : event.detail;
|
||||
if (renderSidebarSnapshot(payload)) {
|
||||
if (renderLiveSidebarSnapshot(payload)) {
|
||||
document.documentElement.setAttribute('data-mnote-tree-live-applied', 'snapshot');
|
||||
return;
|
||||
}
|
||||
@@ -1061,7 +1514,7 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
updateTitleEverywhere(doc.id || doc.documentId, doc.title || '无标题');
|
||||
});
|
||||
}
|
||||
if (renderSidebarSnapshot(payload)) {
|
||||
if (renderLiveSidebarSnapshot(payload)) {
|
||||
document.documentElement.setAttribute('data-mnote-tree-live-applied', 'delta');
|
||||
return;
|
||||
}
|
||||
@@ -1073,7 +1526,7 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
|
||||
window.addEventListener('tree:resync', function(event) {
|
||||
var payload = event.detail && event.detail.payload ? event.detail.payload : event.detail;
|
||||
if (renderSidebarSnapshot(payload)) {
|
||||
if (renderLiveSidebarSnapshot(payload)) {
|
||||
refreshEditorLocalAttachmentExistence();
|
||||
document.documentElement.setAttribute('data-mnote-tree-live-applied', 'resync');
|
||||
return;
|
||||
@@ -1081,6 +1534,18 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
document.documentElement.setAttribute('data-mnote-tree-live-applied', 'resync');
|
||||
setTreeLiveApplyError('tree_resync_missing_projection_payload');
|
||||
});
|
||||
|
||||
window.addEventListener('tree:local-folder-watch-batch', function(event) {
|
||||
var payload = event.detail && event.detail.payload ? event.detail.payload : event.detail;
|
||||
applyLocalFolderWatchBatch(payload);
|
||||
});
|
||||
|
||||
window.addEventListener('tree:error', function(event) {
|
||||
var payload = event.detail && event.detail.payload ? event.detail.payload : event.detail;
|
||||
var code = payload && (payload.code || payload.error || payload.message);
|
||||
document.documentElement.setAttribute('data-mnote-tree-live-error-schema', String(payload && payload.schema || ''));
|
||||
setTreeLiveApplyError(code || 'tree_live_error');
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -1091,14 +1556,17 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
commandDocumentTitle,
|
||||
deltaNeedsProjectionRefresh,
|
||||
fileTreePageTitle,
|
||||
flushFileTreeBatchRefresh,
|
||||
installTreeLiveApplyEventListeners,
|
||||
isFileTreePageRow,
|
||||
applyLocalFolderWatchBatch,
|
||||
localCommandNeedsProjectionRefresh,
|
||||
normalizeFileTreePageRenameTitle,
|
||||
objectIdentityAttr,
|
||||
refreshLocalFolderSidebarSnapshot,
|
||||
removeDocumentRowForMode,
|
||||
renderSidebarSnapshot,
|
||||
revealFileTreeResource,
|
||||
restorePersistedFileTreeExpansionState,
|
||||
setTreeLiveApplyError,
|
||||
startLocalFolderSidebarWatch,
|
||||
|
||||
@@ -301,13 +301,11 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
if (currentSourceKind() !== 'local_folder') return false;
|
||||
var workspaceId = resolveWorkspaceId(trigger || document.body);
|
||||
var effectiveParentId = String(parentId || '').trim();
|
||||
var title = window.prompt('新建文件夹', '新建文件夹');
|
||||
if (!title || !title.trim()) return false;
|
||||
var result = await dispatchTreeCommand(trigger || document.body, {
|
||||
action: 'create_folder',
|
||||
workspaceId: workspaceId,
|
||||
parentId: effectiveParentId || null,
|
||||
title: title.trim()
|
||||
title: '新建文件夹'
|
||||
});
|
||||
document.documentElement.setAttribute('data-mnote-filetree-folder-created', 'true');
|
||||
document.documentElement.setAttribute('data-mnote-filetree-folder-created-id', commandDocumentId(result, result.id || ''));
|
||||
@@ -357,6 +355,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
copyWorkspaceSourceParams,
|
||||
cssEscape,
|
||||
currentDocumentId,
|
||||
currentRootUri,
|
||||
currentSourceKind,
|
||||
dispatchTreeCommand,
|
||||
normalizeSidebarTreeMode,
|
||||
@@ -387,12 +386,385 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
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 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 = '<button type="button" class="mnote-tree-context-menu__item" role="menuitem" data-mnote-sidebar-shortcut-menu-action="open"><span class="material-symbols-outlined mnote-tree-context-menu__icon" data-icon="login" aria-hidden="true"></span><span class="mnote-tree-context-menu__label">在右侧边栏打开</span></button><div class="mnote-tree-context-menu__separator" role="separator"></div><button type="button" class="mnote-tree-context-menu__item" role="menuitem" data-mnote-sidebar-shortcut-menu-action="copy-link"><span class="material-symbols-outlined mnote-tree-context-menu__icon" data-icon="link" aria-hidden="true"></span><span class="mnote-tree-context-menu__label">复制访问链接</span></button><button type="button" class="mnote-tree-context-menu__item mnote-tree-context-menu__item--danger" role="menuitem" data-mnote-sidebar-shortcut-menu-action="remove"><span class="material-symbols-outlined mnote-tree-context-menu__icon" data-icon="star_off" aria-hidden="true"></span><span class="mnote-tree-context-menu__label">取消星标</span></button>';
|
||||
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 = '<span class="wolai-row-caret" aria-hidden="true">›</span><span class="wolai-row-icon"><span class="material-symbols-outlined wolai-row-symbol" data-icon="' + (kind === 'folder' ? 'folder_open' : 'home') + '" aria-hidden="true"></span></span><span class="wolai-row-title">' + escapeHtml(title) + '</span><button type="button" class="wolai-row-more" data-mnote-shortcut-action="menu" aria-label="更多操作" title="更多操作"><span class="material-symbols-outlined" data-icon="more_horiz" aria-hidden="true"></span></button>';
|
||||
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 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);
|
||||
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);
|
||||
}
|
||||
persistStarredFolderScope(workspaceId, rootUri, relativePath);
|
||||
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);
|
||||
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 sidebarResponse = await fetch(sidebarUrl.toString(), {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: { accept: 'application/json' },
|
||||
cache: 'no-store'
|
||||
});
|
||||
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 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) || 'scoped_filetree_failed_' + response.status);
|
||||
var sidebarProjection = sidebarPayload && (sidebarPayload.result || sidebarPayload) || {};
|
||||
var fileProjection = payload && (payload.result || payload);
|
||||
var rendered = renderSidebarSnapshot(Object.assign({}, sidebarProjection, {
|
||||
dataset: Object.assign({}, sidebarProjection.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);
|
||||
}
|
||||
return rendered;
|
||||
}
|
||||
|
||||
const sidebarFileTreeOpen = createSidebarFileTreeOpenRuntime({
|
||||
copyWorkspaceSourceParams,
|
||||
currentDocumentId,
|
||||
@@ -736,14 +1108,19 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
async function healLegacyOfficeAttachmentParagraphs() {
|
||||
var editor = document.querySelector('.editor-surface .ProseMirror');
|
||||
if (!(editor instanceof HTMLElement)) return;
|
||||
var index = await fetchLegacyOfficeAttachmentIndex();
|
||||
var paragraphs = Array.from(editor.querySelectorAll('p'));
|
||||
paragraphs.forEach(function(paragraph) {
|
||||
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');
|
||||
@@ -1545,6 +1922,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
createPage,
|
||||
cssEscape,
|
||||
currentDocumentId,
|
||||
currentRootUri,
|
||||
currentSourceKind,
|
||||
deleteSingleFileTreeAsset,
|
||||
dispatchSidebarEvent,
|
||||
@@ -1563,6 +1941,8 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
openEditorAttachmentEditTab: (...args) => openEditorAttachmentEditTab(...args),
|
||||
openEditorAttachmentNewWindow: (...args) => openEditorAttachmentNewWindow(...args),
|
||||
refreshLocalFolderSidebarSnapshot,
|
||||
removeFileTreeAssetRow,
|
||||
revealFileTreeResource,
|
||||
resolveWorkspaceId,
|
||||
runtimeState: sidebarFileTreeCommandState,
|
||||
selectedSidebarFileTreeSelection: sidebarFileTreeSelection,
|
||||
@@ -1620,6 +2000,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
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);
|
||||
|
||||
@@ -1891,6 +2272,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
buildLocalOnlyOfficeOpenUrl,
|
||||
buildOnlyOfficeOpenPath,
|
||||
buildOnlyOfficeOpenUrl,
|
||||
closestAction,
|
||||
currentDocumentId,
|
||||
currentRootUri,
|
||||
currentWorkspaceSourcePayload,
|
||||
@@ -2274,6 +2656,58 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
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();
|
||||
@@ -2295,6 +2729,16 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
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]');
|
||||
@@ -2345,7 +2789,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
return;
|
||||
}
|
||||
if ((rowKind === 'document' || rowKind === 'index' || rowKind === 'markdown') && documentId) {
|
||||
navigateToDocument(documentId, resolveWorkspaceId(fileRow), { treeView: 'filetree' });
|
||||
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, workspaceId: resolveWorkspaceId(fileRow), openTarget: e.shiftKey || e.ctrlKey || e.metaKey ? 'new-window' : 'active-tab' });
|
||||
}
|
||||
@@ -2355,6 +2799,29 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
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[data-mnote-attachment-link="true"], .editor-surface .ProseMirror a.mnote-uploaded-attachment-row, .editor-surface .ProseMirror a[href*="/api/local-folder/files/open"]');
|
||||
if (editorAttachmentLink instanceof HTMLAnchorElement) {
|
||||
|
||||
@@ -156,7 +156,7 @@ export const createSidebarWorkspaceRuntime = (dependencies = {}) => {
|
||||
|
||||
function copyWorkspaceSourceParams(targetUrl) {
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
['sourceKind', 'rootUri', 'secondaryDocumentId', 'secondarySourceKind', 'secondaryRootUri'].forEach(function(name) {
|
||||
['sourceKind', 'rootUri', 'fileTreeScope', 'secondaryDocumentId', 'secondarySourceKind', 'secondaryRootUri'].forEach(function(name) {
|
||||
var value = (params.get(name) || '').trim();
|
||||
if (value) targetUrl.searchParams.set(name, value);
|
||||
});
|
||||
|
||||
@@ -87,6 +87,18 @@
|
||||
dispatchTreeEvent('tree:resync', { payload: payload, revision: revision, bootstrap: bootstrap });
|
||||
});
|
||||
|
||||
source.addEventListener('watch_batch', function(event){
|
||||
var payload = JSON.parse(event.data || '{}');
|
||||
var revision = payload.revision || payload.cursor || event.lastEventId || null;
|
||||
document.documentElement.setAttribute('data-mnote-tree-live-revision', String(revision || ''));
|
||||
dispatchTreeEvent('tree:local-folder-watch-batch', { payload: payload, revision: revision, bootstrap: bootstrap });
|
||||
});
|
||||
|
||||
source.addEventListener('tree_error', function(event){
|
||||
var payload = JSON.parse(event.data || '{}');
|
||||
dispatchTreeEvent('tree:error', { payload: payload, bootstrap: bootstrap });
|
||||
});
|
||||
|
||||
source.addEventListener('block.delta', function(event){
|
||||
var payload = JSON.parse(event.data || '{}');
|
||||
dispatchTreeEvent('tree:block-delta', { payload: payload, bootstrap: bootstrap });
|
||||
|
||||
Reference in New Issue
Block a user