fix: 修复 Rust OnlyOffice 附件打开链路
This commit is contained in:
@@ -25,6 +25,8 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
};
|
||||
var projectionRefreshTimer = 0;
|
||||
var activeTreeContextMenu = null;
|
||||
var activeEditorAttachmentLink = null;
|
||||
var attachmentActionsHideTimer = 0;
|
||||
var pageUiState = {
|
||||
pageOptions: null,
|
||||
historySnapshots: [],
|
||||
@@ -1024,6 +1026,608 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
window.dispatchEvent(new CustomEvent(name, { detail: detail }));
|
||||
}
|
||||
|
||||
function inferOnlyOfficeFileType(fileName, mimeType) {
|
||||
var name = String(fileName || '').trim().toLowerCase();
|
||||
var mt = String(mimeType || '').trim().toLowerCase();
|
||||
var ext = name.indexOf('.') >= 0 ? name.split('.').pop() : '';
|
||||
if (['doc', 'docx', 'odt', 'rtf'].indexOf(ext) >= 0) return ext;
|
||||
if (['ppt', 'pptx', 'odp'].indexOf(ext) >= 0) return ext;
|
||||
if (['xls', 'xlsx', 'ods', 'csv'].indexOf(ext) >= 0) return ext;
|
||||
if (ext === 'pdf') return ext;
|
||||
if (mt.indexOf('wordprocessingml') >= 0) return 'docx';
|
||||
if (mt.indexOf('presentationml') >= 0) return 'pptx';
|
||||
if (mt.indexOf('spreadsheetml') >= 0) return 'xlsx';
|
||||
if (mt.indexOf('pdf') >= 0) return 'pdf';
|
||||
return '';
|
||||
}
|
||||
|
||||
function buildOnlyOfficeOpenUrl(input) {
|
||||
var target = new URL('/onlyoffice', window.location.origin);
|
||||
target.searchParams.set('fileUrl', input.fileUrl || '');
|
||||
target.searchParams.set('fileName', input.fileName || '未命名资源');
|
||||
target.searchParams.set('fileType', input.fileType || 'docx');
|
||||
if (input.assetId) target.searchParams.set('assetId', input.assetId);
|
||||
if (input.documentId) target.searchParams.set('documentId', input.documentId);
|
||||
if (input.userId) target.searchParams.set('userId', input.userId);
|
||||
target.searchParams.set('mode', input.mode || 'edit');
|
||||
return target.toString();
|
||||
}
|
||||
|
||||
function buildOnlyOfficeOpenPath(input) {
|
||||
var params = new URLSearchParams();
|
||||
params.set('fileUrl', input.fileUrl || '');
|
||||
params.set('fileName', input.fileName || '未命名资源');
|
||||
params.set('fileType', input.fileType || 'docx');
|
||||
if (input.assetId) params.set('assetId', input.assetId);
|
||||
if (input.documentId) params.set('documentId', input.documentId);
|
||||
if (input.userId) params.set('userId', input.userId);
|
||||
params.set('mode', input.mode || 'edit');
|
||||
return '/onlyoffice?' + params.toString();
|
||||
}
|
||||
|
||||
async function fetchCurrentOnlyOfficeUserId() {
|
||||
try {
|
||||
var response = await fetch('/api/auth/whoami', {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
cache: 'no-store'
|
||||
});
|
||||
var payload = await response.json().catch(function() { return null; });
|
||||
return String(payload && payload.userId || '').trim();
|
||||
} catch (_error) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
async function openConvexAssetFromFileTree(detail) {
|
||||
var assetId = String(detail && detail.assetId || '').trim();
|
||||
if (!assetId) return;
|
||||
try {
|
||||
var response = await fetch('/api/media/sign?assetId=' + encodeURIComponent(assetId), {
|
||||
method: 'GET',
|
||||
credentials: 'include'
|
||||
});
|
||||
var payload = await response.json().catch(function() { return null; });
|
||||
if (!response.ok) {
|
||||
throw new Error(payload && payload.error ? payload.error : '生成签名链接失败');
|
||||
}
|
||||
var asset = payload && payload.asset && typeof payload.asset === 'object' ? payload.asset : {};
|
||||
var fileUrl = String(payload && payload.signedUrl || asset.file_url || asset.signed_url || '').trim();
|
||||
if (!fileUrl) throw new Error('附件链接不可用');
|
||||
var fileName = String(asset.file_name || detail.fileName || '未命名资源').trim() || '未命名资源';
|
||||
var fileType = inferOnlyOfficeFileType(fileName, asset.mime_type);
|
||||
if (fileType) {
|
||||
var userId = await fetchCurrentOnlyOfficeUserId();
|
||||
window.open(buildOnlyOfficeOpenUrl({
|
||||
fileUrl: fileUrl,
|
||||
fileName: fileName,
|
||||
fileType: fileType,
|
||||
assetId: assetId,
|
||||
documentId: String(asset.document_id || detail.documentId || '').trim(),
|
||||
userId: userId,
|
||||
mode: 'edit'
|
||||
}), '_blank', 'noopener,noreferrer');
|
||||
return;
|
||||
}
|
||||
window.open(fileUrl, '_blank', 'noopener,noreferrer');
|
||||
} catch (error) {
|
||||
window.alert(error && error.message ? error.message : '打开附件失败');
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('tree.asset.open', function(event) {
|
||||
void openConvexAssetFromFileTree(event.detail || {});
|
||||
});
|
||||
|
||||
function fileTreeRowsForUploadPreflight() {
|
||||
return Array.from(document.querySelectorAll('.tree-row[data-shell-mode="filetree"]')).map(function(row) {
|
||||
return {
|
||||
rowId: row.getAttribute('data-row-id') || '',
|
||||
rowKind: row.getAttribute('data-row-kind') || '',
|
||||
documentId: row.getAttribute('data-document-id') || row.getAttribute('data-doc-id') || null,
|
||||
assetId: row.getAttribute('data-asset-id') || null,
|
||||
assetDocumentId: row.getAttribute('data-document-id') || row.getAttribute('data-doc-id') || null,
|
||||
assetType: row.querySelector('.tree-kind-badge') ? row.querySelector('.tree-kind-badge').getAttribute('data-kind') : null,
|
||||
storagePath: null
|
||||
};
|
||||
}).filter(function(row) {
|
||||
return row.rowId || row.documentId || row.assetId;
|
||||
});
|
||||
}
|
||||
|
||||
function fileTreeDocumentWorkspacesForUploadPreflight(workspaceId) {
|
||||
var seen = new Set();
|
||||
return fileTreeRowsForUploadPreflight().filter(function(row) {
|
||||
if (!row.documentId || seen.has(row.documentId)) return false;
|
||||
seen.add(row.documentId);
|
||||
return true;
|
||||
}).map(function(row) {
|
||||
return { documentId: row.documentId, workspaceId: workspaceId || null };
|
||||
});
|
||||
}
|
||||
|
||||
async function preflightFileTreeUploadTarget(detail) {
|
||||
var workspaceId = String(detail && detail.workspaceId || resolveWorkspaceId(document.body) || '').trim();
|
||||
var body = {
|
||||
workspaceId: workspaceId || null,
|
||||
targetDocumentId: detail && detail.documentId ? String(detail.documentId) : null,
|
||||
targetRowId: detail && detail.targetRowId ? String(detail.targetRowId) : null,
|
||||
focusedRowId: sidebarFileTreeSelection.focusedRowId || null,
|
||||
activeDocumentId: currentDocumentId() || null,
|
||||
rows: fileTreeRowsForUploadPreflight(),
|
||||
documentWorkspaces: fileTreeDocumentWorkspacesForUploadPreflight(workspaceId)
|
||||
};
|
||||
var response = await fetch('/api/tree/filetree/upload-target-preflight', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
var payload = await response.json().catch(function() { return null; });
|
||||
if (!response.ok || !payload || !payload.plan) {
|
||||
throw new Error(payload && payload.error ? payload.error : '文件树上传目标预检失败');
|
||||
}
|
||||
return payload.plan;
|
||||
}
|
||||
|
||||
function fallbackFileTreeUploadTarget(detail) {
|
||||
var workspaceId = String(detail && detail.workspaceId || resolveWorkspaceId(document.body) || '').trim();
|
||||
var documentId = String(detail && detail.documentId || currentDocumentId() || '').trim();
|
||||
if (!workspaceId || !documentId) {
|
||||
throw new Error('请选择一个目标页面后再拖入文件');
|
||||
}
|
||||
return {
|
||||
workspaceId: workspaceId,
|
||||
targetDocumentId: documentId,
|
||||
targetMindmapId: null,
|
||||
targetSubPath: null
|
||||
};
|
||||
}
|
||||
|
||||
async function resolveFileTreeUploadTarget(detail) {
|
||||
try {
|
||||
return await preflightFileTreeUploadTarget(detail || {});
|
||||
} catch (error) {
|
||||
console.warn('[mnote upload] upload target preflight fallback', error);
|
||||
return fallbackFileTreeUploadTarget(detail || {});
|
||||
}
|
||||
}
|
||||
|
||||
function uploadedAssetTitle(asset) {
|
||||
return String(asset && (asset.file_name || asset.title || asset.name) || '未命名附件').trim() || '未命名附件';
|
||||
}
|
||||
|
||||
function uploadedAssetUrl(asset) {
|
||||
return String(asset && (asset.file_url || asset.signedUrl || asset.signed_url || asset.thumbnail_url) || '').trim();
|
||||
}
|
||||
|
||||
function uploadedAssetType(asset) {
|
||||
return String(asset && (asset.asset_type || asset.assetType || asset.mime_type || '') || '').trim();
|
||||
}
|
||||
|
||||
function uploadedAssetExtension(asset) {
|
||||
var match = uploadedAssetTitle(asset).toLowerCase().match(/\.([a-z0-9]+)$/);
|
||||
return match ? match[1] : '';
|
||||
}
|
||||
|
||||
function attachmentClassForFileName(fileName) {
|
||||
var name = String(fileName || '').trim().toLowerCase();
|
||||
var ext = name.indexOf('.') >= 0 ? name.split('.').pop() : '';
|
||||
if (['doc', 'docx', 'odt', 'rtf'].indexOf(ext) >= 0) return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-word';
|
||||
if (['ppt', 'pptx', 'odp'].indexOf(ext) >= 0) return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-ppt';
|
||||
if (['xls', 'xlsx', 'ods', 'csv'].indexOf(ext) >= 0) return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-sheet';
|
||||
if (ext === 'pdf') return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-pdf';
|
||||
return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-file';
|
||||
}
|
||||
|
||||
function uploadedAttachmentClass(asset) {
|
||||
return attachmentClassForFileName(uploadedAssetTitle(asset));
|
||||
}
|
||||
|
||||
function buildOnlyOfficeAssetOpenUrl(asset, userId) {
|
||||
var title = uploadedAssetTitle(asset);
|
||||
var fileType = inferOnlyOfficeFileType(title, asset && asset.mime_type);
|
||||
if (!fileType) return '';
|
||||
var assetId = String(asset && asset.id || '').trim();
|
||||
return buildOnlyOfficeOpenUrl({
|
||||
fileUrl: assetId ? '' : uploadedAssetUrl(asset),
|
||||
fileName: title,
|
||||
fileType: fileType,
|
||||
assetId: assetId,
|
||||
documentId: String(asset && (asset.document_id || asset.documentId) || currentDocumentId() || '').trim(),
|
||||
userId: userId || '',
|
||||
mode: 'edit'
|
||||
});
|
||||
}
|
||||
|
||||
function uploadedFileSize(asset) {
|
||||
var size = Number(asset && (asset.file_size || asset.fileSize) || 0);
|
||||
if (!Number.isFinite(size) || size <= 0) return '';
|
||||
if (size >= 1024 * 1024) return (size / 1024 / 1024).toFixed(size >= 10 * 1024 * 1024 ? 1 : 2) + ' MB';
|
||||
if (size >= 1024) return (size / 1024).toFixed(size >= 100 * 1024 ? 0 : 2) + ' KB';
|
||||
return String(Math.round(size)) + ' B';
|
||||
}
|
||||
|
||||
var attachmentMetaCache = Object.create(null);
|
||||
var attachmentMetaPending = Object.create(null);
|
||||
var legacyOfficeAttachmentIndex = null;
|
||||
var legacyOfficeAttachmentIndexPending = null;
|
||||
|
||||
function parseCurrentWorkspaceId() {
|
||||
return (new URLSearchParams(window.location.search).get('workspaceId') || '').trim();
|
||||
}
|
||||
|
||||
async function fetchLegacyOfficeAttachmentIndex() {
|
||||
if (legacyOfficeAttachmentIndex) return legacyOfficeAttachmentIndex;
|
||||
if (legacyOfficeAttachmentIndexPending) return legacyOfficeAttachmentIndexPending;
|
||||
var documentId = currentDocumentId();
|
||||
var workspaceId = parseCurrentWorkspaceId();
|
||||
if (!documentId || !workspaceId) {
|
||||
legacyOfficeAttachmentIndex = Object.create(null);
|
||||
return legacyOfficeAttachmentIndex;
|
||||
}
|
||||
legacyOfficeAttachmentIndexPending = fetch(
|
||||
'/api/tree/projections/file?documentId=' + encodeURIComponent(documentId) + '&workspaceId=' + encodeURIComponent(workspaceId),
|
||||
{
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
cache: 'no-store'
|
||||
}
|
||||
).then(function(response) {
|
||||
return response.json().catch(function() { return null; }).then(function(payload) {
|
||||
var items = payload && payload.ok && payload.result && Array.isArray(payload.result.items)
|
||||
? payload.result.items
|
||||
: [];
|
||||
var index = Object.create(null);
|
||||
items.forEach(function(item) {
|
||||
if (!item || item.rowKind !== 'asset') return;
|
||||
var title = String(item.title || '').trim();
|
||||
if (!title || index[title]) return;
|
||||
var fileType = inferOnlyOfficeFileType(title, '');
|
||||
if (!fileType) return;
|
||||
var rowId = String(item.rowId || '').trim();
|
||||
var assetId = String(item.assetId || '').trim();
|
||||
if (!assetId && rowId.indexOf('asset:') === 0) assetId = rowId.slice('asset:'.length);
|
||||
if (!assetId) return;
|
||||
index[title] = {
|
||||
assetId: assetId,
|
||||
fileName: title,
|
||||
fileType: fileType,
|
||||
documentId: documentId
|
||||
};
|
||||
});
|
||||
legacyOfficeAttachmentIndex = index;
|
||||
return index;
|
||||
});
|
||||
}).catch(function() {
|
||||
var empty = Object.create(null);
|
||||
legacyOfficeAttachmentIndex = empty;
|
||||
return empty;
|
||||
}).finally(function() {
|
||||
legacyOfficeAttachmentIndexPending = null;
|
||||
});
|
||||
return legacyOfficeAttachmentIndexPending;
|
||||
}
|
||||
|
||||
async function healLegacyOfficeAttachmentParagraphs() {
|
||||
var editor = document.querySelector('.editor-surface .ProseMirror');
|
||||
if (!(editor instanceof HTMLElement)) return;
|
||||
var index = await fetchLegacyOfficeAttachmentIndex();
|
||||
var paragraphs = Array.from(editor.querySelectorAll('p'));
|
||||
paragraphs.forEach(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;
|
||||
var detail = index[fileName];
|
||||
if (!detail) return;
|
||||
var link = document.createElement('a');
|
||||
link.textContent = fileName;
|
||||
link.setAttribute('href', buildOnlyOfficeOpenPath({
|
||||
fileUrl: '',
|
||||
fileName: detail.fileName,
|
||||
fileType: detail.fileType,
|
||||
assetId: detail.assetId,
|
||||
documentId: detail.documentId || currentDocumentId() || '',
|
||||
userId: '',
|
||||
mode: 'edit'
|
||||
}));
|
||||
link.setAttribute('data-mnote-attachment-link', 'true');
|
||||
link.setAttribute('data-asset-id', detail.assetId);
|
||||
attachmentClassForFileName(fileName).split(/\s+/).forEach(function(name) {
|
||||
if (name) link.classList.add(name);
|
||||
});
|
||||
paragraph.replaceChildren(link);
|
||||
enhanceEditorAttachmentLink(link);
|
||||
});
|
||||
}
|
||||
|
||||
function applyEditorAttachmentMeta(link, meta) {
|
||||
if (!(link instanceof HTMLAnchorElement) || !meta) return;
|
||||
if (meta.assetId) link.setAttribute('data-asset-id', meta.assetId);
|
||||
if (meta.fileSize) link.setAttribute('data-file-size', meta.fileSize);
|
||||
}
|
||||
|
||||
async function hydrateEditorAttachmentMeta(link) {
|
||||
if (!(link instanceof HTMLAnchorElement)) return;
|
||||
var detail = detailFromEditorAttachmentLink(link);
|
||||
var assetId = String(detail && detail.assetId || '').trim();
|
||||
if (!assetId) return;
|
||||
if (attachmentMetaCache[assetId]) {
|
||||
applyEditorAttachmentMeta(link, attachmentMetaCache[assetId]);
|
||||
return;
|
||||
}
|
||||
if (attachmentMetaPending[assetId]) {
|
||||
try { await attachmentMetaPending[assetId]; } catch (_) {}
|
||||
applyEditorAttachmentMeta(link, attachmentMetaCache[assetId]);
|
||||
return;
|
||||
}
|
||||
attachmentMetaPending[assetId] = fetch('/api/media/sign?assetId=' + encodeURIComponent(assetId), {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
cache: 'no-store'
|
||||
}).then(function(response) {
|
||||
return response.json().catch(function() { return null; }).then(function(payload) {
|
||||
if (!response.ok || !payload) return null;
|
||||
var asset = payload.asset && typeof payload.asset === 'object' ? payload.asset : {};
|
||||
var meta = {
|
||||
assetId: assetId,
|
||||
fileSize: uploadedFileSize(asset)
|
||||
};
|
||||
attachmentMetaCache[assetId] = meta;
|
||||
return meta;
|
||||
});
|
||||
}).catch(function() {
|
||||
return null;
|
||||
}).finally(function() {
|
||||
delete attachmentMetaPending[assetId];
|
||||
});
|
||||
try {
|
||||
var meta = await attachmentMetaPending[assetId];
|
||||
applyEditorAttachmentMeta(link, meta);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
function revealFileTreeRow(row) {
|
||||
if (!(row instanceof HTMLElement)) return;
|
||||
var node = row.closest('.tree-node');
|
||||
while (node && node.parentElement) {
|
||||
if (node.parentElement.classList && node.parentElement.classList.contains('tree-children')) {
|
||||
node.parentElement.classList.remove('tree-children--collapsed');
|
||||
var parentNode = node.parentElement.closest('.tree-node');
|
||||
var parentRow = parentNode ? parentNode.querySelector(':scope > .tree-row') : null;
|
||||
if (parentRow instanceof HTMLElement) {
|
||||
parentRow.setAttribute('aria-expanded', 'true');
|
||||
var toggle = parentRow.querySelector('[data-rust-action="toggle"]');
|
||||
if (toggle) toggle.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
}
|
||||
node = node.parentElement.closest('.tree-node');
|
||||
}
|
||||
try { row.scrollIntoView({ block: 'nearest' }); } catch (_) {}
|
||||
}
|
||||
|
||||
function revealFileTreeAssetRow(assetId) {
|
||||
if (!assetId) return false;
|
||||
var row = document.querySelector('.tree-row[data-shell-mode="filetree"][data-asset-id="' + cssEscape(assetId) + '"]');
|
||||
if (!(row instanceof HTMLElement)) return false;
|
||||
revealFileTreeRow(row);
|
||||
return true;
|
||||
}
|
||||
|
||||
function appendUploadedAssetRow(asset, documentId) {
|
||||
var assetId = String(asset && asset.id || '').trim();
|
||||
if (!assetId) return;
|
||||
if (revealFileTreeAssetRow(assetId)) return;
|
||||
var targetDocumentId = String(documentId || asset.document_id || asset.documentId || currentDocumentId() || '').trim();
|
||||
var parentRow = targetDocumentId
|
||||
? document.querySelector('.tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape('doc:' + targetDocumentId) + '"]')
|
||||
: null;
|
||||
if (!parentRow) parentRow = document.querySelector('.tree-row[data-shell-mode="filetree"][data-row-kind="document"]');
|
||||
var root = document.querySelector('#sidebar-file-tree-root .tree-root');
|
||||
if (!root && !parentRow) return;
|
||||
var parentLi = parentRow ? parentRow.closest('.tree-node') : null;
|
||||
var children = parentLi ? parentLi.querySelector(':scope > .tree-children') : null;
|
||||
if (parentLi && !children) {
|
||||
children = document.createElement('ul');
|
||||
children.className = 'tree-children';
|
||||
parentLi.appendChild(children);
|
||||
}
|
||||
if (children) {
|
||||
children.classList.remove('tree-children--collapsed');
|
||||
if (parentRow) {
|
||||
parentRow.setAttribute('aria-expanded', 'true');
|
||||
var toggle = parentRow.querySelector('[data-rust-action="toggle"]');
|
||||
if (toggle) toggle.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
}
|
||||
var container = children || root;
|
||||
var li = document.createElement('li');
|
||||
li.className = 'tree-node';
|
||||
li.setAttribute('data-node-id', 'asset:' + assetId);
|
||||
var title = uploadedAssetTitle(asset);
|
||||
var iconKind = uploadedAssetType(asset) || 'file';
|
||||
li.innerHTML =
|
||||
'<div class="tree-row" role="treeitem" aria-level="' + (parentRow ? '2' : '1') + '" aria-expanded="false" data-rust-rendered-row="filetree" data-testid="filetree-asset-row" data-row-id="' + escapeHtml('asset:' + assetId) + '" data-row-kind="asset" data-node-id="' + escapeHtml('asset:' + assetId) + '" data-document-id="' + escapeHtml(targetDocumentId) + '" data-doc-id="' + escapeHtml(targetDocumentId) + '" data-asset-id="' + escapeHtml(assetId) + '" data-shell-mode="filetree" data-selected="false" data-active="false" draggable="true">' +
|
||||
'<span class="tree-spacer" aria-hidden="true"></span><span class="tree-kind-badge" data-kind="' + escapeHtml(iconKind) + '" aria-hidden="true"></span>' +
|
||||
'<button type="button" class="tree-link" data-rust-action="open" data-row-id="' + escapeHtml('asset:' + assetId) + '" data-document-id="' + escapeHtml(targetDocumentId) + '" data-asset-id="' + escapeHtml(assetId) + '"><span class="tree-link-title">' + escapeHtml(title) + '</span></button>' +
|
||||
'<div class="tree-actions"><button type="button" class="tree-action" data-testid="filetree-action-menu" data-rust-action="menu" data-row-id="' + escapeHtml('asset:' + assetId) + '" aria-label="更多操作">…</button></div></div>';
|
||||
container.appendChild(li);
|
||||
revealFileTreeRow(li.querySelector('.tree-row'));
|
||||
document.documentElement.setAttribute('data-mnote-last-upload-asset-id', assetId);
|
||||
}
|
||||
|
||||
async function insertUploadedAssetIntoEditor(asset) {
|
||||
var editorRoot = document.querySelector('.editor-surface .ProseMirror');
|
||||
var editor = editorRoot && editorRoot.editor;
|
||||
if (!editor || !editor.chain) return false;
|
||||
var title = uploadedAssetTitle(asset);
|
||||
var url = uploadedAssetUrl(asset);
|
||||
var type = uploadedAssetType(asset);
|
||||
var assetId = String(asset && asset.id || '').trim();
|
||||
var sizeLabel = uploadedFileSize(asset);
|
||||
try {
|
||||
if (type === 'image' && url) {
|
||||
return editor.chain().focus().setImage({ src: url, alt: title, title: title }).run() === true;
|
||||
}
|
||||
var userId = '';
|
||||
var onlyOfficeUrl = buildOnlyOfficeAssetOpenUrl(asset, userId);
|
||||
if (onlyOfficeUrl && assetId) {
|
||||
userId = await fetchCurrentOnlyOfficeUserId();
|
||||
onlyOfficeUrl = buildOnlyOfficeAssetOpenUrl(asset, userId);
|
||||
}
|
||||
var href = onlyOfficeUrl || url;
|
||||
if (href) {
|
||||
var storedHref = onlyOfficeUrl
|
||||
? buildOnlyOfficeOpenPath({
|
||||
fileUrl: '',
|
||||
fileName: title,
|
||||
fileType: inferOnlyOfficeFileType(title, asset && asset.mime_type) || 'docx',
|
||||
assetId: assetId,
|
||||
documentId: String(asset && (asset.document_id || asset.documentId) || currentDocumentId() || '').trim(),
|
||||
userId: userId || '',
|
||||
mode: 'edit'
|
||||
})
|
||||
: href;
|
||||
var inserted = editor.chain().focus().insertContent({
|
||||
type: 'paragraph',
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: title,
|
||||
marks: [{
|
||||
type: 'link',
|
||||
attrs: {
|
||||
href: storedHref,
|
||||
target: '_blank',
|
||||
rel: 'noopener noreferrer nofollow',
|
||||
class: uploadedAttachmentClass(asset)
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}).run() === true;
|
||||
window.setTimeout(function() {
|
||||
enhanceEditorAttachmentLinks();
|
||||
var selector = assetId
|
||||
? '.editor-surface .ProseMirror a[href*="' + cssEscape(assetId) + '"]'
|
||||
: '.editor-surface .ProseMirror a';
|
||||
var link = document.querySelector(selector);
|
||||
if (link instanceof HTMLElement) {
|
||||
link.setAttribute('data-mnote-attachment-link', 'true');
|
||||
if (assetId) link.setAttribute('data-asset-id', assetId);
|
||||
if (sizeLabel) link.setAttribute('data-file-size', sizeLabel);
|
||||
}
|
||||
}, 0);
|
||||
return inserted;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('[mnote upload] insert uploaded asset failed', error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function uploadFileToMediaAsset(file, plan, options) {
|
||||
var form = new FormData();
|
||||
form.append('file', file);
|
||||
form.append('workspaceId', plan.workspaceId);
|
||||
form.append('documentId', plan.targetDocumentId);
|
||||
if (plan.targetMindmapId) form.append('mindmapId', plan.targetMindmapId);
|
||||
var response = await fetch('/api/media/upload', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
body: form
|
||||
});
|
||||
var payload = await response.json().catch(function() { return null; });
|
||||
if (!response.ok || !payload || !payload.asset) {
|
||||
throw new Error(payload && payload.error ? payload.error : '上传失败');
|
||||
}
|
||||
appendUploadedAssetRow(payload.asset, plan.targetDocumentId);
|
||||
if (options && options.insertIntoEditor) {
|
||||
await insertUploadedAssetIntoEditor(payload.asset);
|
||||
}
|
||||
window.dispatchEvent(new CustomEvent('wolai:assets-changed', {
|
||||
detail: { docId: plan.targetDocumentId, asset: payload.asset, assetIds: [payload.asset.id] }
|
||||
}));
|
||||
return payload.asset;
|
||||
}
|
||||
|
||||
async function uploadFilesWithResolvedTarget(files, detail, options) {
|
||||
var list = Array.from(files || []).filter(Boolean);
|
||||
if (!list.length) return [];
|
||||
var plan = await resolveFileTreeUploadTarget(detail || {});
|
||||
var uploaded = [];
|
||||
var errors = [];
|
||||
for (var i = 0; i < list.length; i += 1) {
|
||||
try {
|
||||
uploaded.push(await uploadFileToMediaAsset(list[i], plan, options || {}));
|
||||
} catch (error) {
|
||||
errors.push(list[i].name + ': ' + (error && error.message ? error.message : '上传失败'));
|
||||
}
|
||||
}
|
||||
if (errors.length) {
|
||||
window.alert('部分文件上传失败:\n' + errors.slice(0, 6).join('\n') + (errors.length > 6 ? '\n...' : ''));
|
||||
}
|
||||
return uploaded;
|
||||
}
|
||||
|
||||
function openEditorUploadFilePicker(detail) {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.multiple = detail && detail.multiple !== false;
|
||||
if (detail && detail.accept) input.accept = String(detail.accept);
|
||||
input.style.position = 'fixed';
|
||||
input.style.left = '-9999px';
|
||||
input.style.top = '-9999px';
|
||||
document.body.appendChild(input);
|
||||
input.addEventListener('change', function() {
|
||||
var files = Array.from(input.files || []);
|
||||
input.remove();
|
||||
void uploadFilesWithResolvedTarget(files, {
|
||||
workspaceId: resolveWorkspaceId(document.body),
|
||||
documentId: currentDocumentId(),
|
||||
targetRowId: null
|
||||
}, {
|
||||
insertIntoEditor: detail && detail.insertIntoEditor !== false
|
||||
});
|
||||
}, { once: true });
|
||||
input.click();
|
||||
}
|
||||
|
||||
window.addEventListener('mnote:editor-upload-request', function(event) {
|
||||
openEditorUploadFilePicker(event.detail || {});
|
||||
});
|
||||
|
||||
window.addEventListener('tree.filetree.external-drop', function(event) {
|
||||
var detail = event.detail || {};
|
||||
void uploadFilesWithResolvedTarget(detail.files || [], detail, {
|
||||
insertIntoEditor: String(detail.documentId || '') === currentDocumentId()
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('dragover', function(event) {
|
||||
var editorTarget = closestAction(event.target, '[data-testid="mnote-leptos-tiptap-editor-stage"], .editor-surface .ProseMirror');
|
||||
var hasFiles = event.dataTransfer && Array.prototype.indexOf.call(event.dataTransfer.types || [], 'Files') >= 0;
|
||||
if (!editorTarget || !hasFiles) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy';
|
||||
}, true);
|
||||
|
||||
document.addEventListener('drop', function(event) {
|
||||
var editorTarget = closestAction(event.target, '[data-testid="mnote-leptos-tiptap-editor-stage"], .editor-surface .ProseMirror');
|
||||
var files = event.dataTransfer ? Array.from(event.dataTransfer.files || []) : [];
|
||||
if (!editorTarget || !files.length) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
void uploadFilesWithResolvedTarget(files, {
|
||||
workspaceId: resolveWorkspaceId(document.body),
|
||||
documentId: currentDocumentId(),
|
||||
targetRowId: null
|
||||
}, {
|
||||
insertIntoEditor: true
|
||||
});
|
||||
}, true);
|
||||
|
||||
function rowTitle(row) {
|
||||
var title = row ? row.querySelector(':scope > .tree-link > .tree-link-title') : null;
|
||||
return title && title.textContent ? title.textContent.trim() : '无标题';
|
||||
@@ -1109,6 +1713,31 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
|
||||
function handleTreeContextMenuAction(action, detail, trigger) {
|
||||
closeTreeContextMenu();
|
||||
detail = detail || {};
|
||||
if (detail.contextKind === 'attachment') {
|
||||
if (action === 'copy-link') {
|
||||
void copyTreeContextValue(detail.href || '', 'attachment-copy-link');
|
||||
return;
|
||||
}
|
||||
if (action === 'download') {
|
||||
openEditorAttachmentDownload(detail);
|
||||
return;
|
||||
}
|
||||
if (action === 'popup-preview') {
|
||||
openEditorAttachmentDetail(detail);
|
||||
return;
|
||||
}
|
||||
if (action === 'right-preview') {
|
||||
dispatchSidebarEvent('tree.attachment.open-right', detail);
|
||||
return;
|
||||
}
|
||||
if (action === 'copy-id') {
|
||||
void copyTreeContextValue(detail.assetId || '', 'attachment-copy-id');
|
||||
return;
|
||||
}
|
||||
dispatchSidebarEvent('tree.attachment.action', { action: action, attachment: detail });
|
||||
return;
|
||||
}
|
||||
var documentId = detail.documentId || '';
|
||||
var workspaceId = detail.workspaceId || resolveWorkspaceId(trigger || document.body);
|
||||
var title = detail.title || '无标题';
|
||||
@@ -1221,13 +1850,32 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
|
||||
function openTreeContextMenu(kind, detail, x, y, trigger) {
|
||||
closeTreeContextMenu();
|
||||
detail = Object.assign({}, detail || {}, { contextKind: kind });
|
||||
var menu = document.createElement('div');
|
||||
menu.className = 'mnote-tree-context-menu';
|
||||
menu.setAttribute('role', 'menu');
|
||||
menu.setAttribute('data-testid', 'mnote-tree-context-menu');
|
||||
menu.setAttribute('data-kind', kind);
|
||||
var isAttachment = kind === 'attachment';
|
||||
var isAsset = kind === 'filetree' && detail.assetId && detail.rowKind !== 'document' && detail.rowKind !== 'index';
|
||||
var items = isAsset ? [
|
||||
var items = isAttachment ? [
|
||||
{ action: 'duplicate', icon: 'file_copy', label: '拷贝副本', shortcut: 'Ctrl + D' },
|
||||
{ action: 'delete-trash', icon: 'delete', label: '删除', shortcut: 'Del', danger: true },
|
||||
{ separator: true },
|
||||
{ action: 'copy-link', icon: 'link', label: '复制链接' },
|
||||
{ action: 'move-embed', icon: 'subdirectory_arrow_right', label: '移动/嵌入到...', shortcut: 'Alt+Shift+M/G' },
|
||||
{ action: 'history', icon: 'history', label: '块历史...' },
|
||||
{ separator: true },
|
||||
{ action: 'popup-preview', icon: 'preview', label: '弹窗预览' },
|
||||
{ action: 'right-preview', icon: 'right_panel_open', label: '右侧预览' },
|
||||
{ action: 'download', icon: 'download', label: '下载' },
|
||||
{ action: 'replace-file', icon: 'sync', label: '更换文件' },
|
||||
{ action: 'rename-attachment', icon: 'drive_file_rename_outline', label: '重命名' },
|
||||
{ action: 'comment', icon: 'mode_comment', label: '评论', shortcut: 'Ctrl+Alt+M' },
|
||||
{ action: 'caption', icon: 'notes', label: '添加说明文字' },
|
||||
{ separator: true },
|
||||
{ action: 'color', icon: 'format_paint', label: '颜色' }
|
||||
] : isAsset ? [
|
||||
{ action: 'open-right', icon: 'open_in_new', label: '在右侧边栏打开', shortcut: 'Alt + O' },
|
||||
{ action: 'move', icon: 'drive_file_move', label: '移动到...' },
|
||||
{ action: 'copy-id', icon: 'tag', label: '复制资源 ID' }
|
||||
@@ -2244,10 +2892,242 @@ const SIDEBAR_TREE_JS: &str = r##"
|
||||
else openPageSettingsPopover();
|
||||
}
|
||||
|
||||
function attachmentQueryParams(href) {
|
||||
try {
|
||||
return new URL(String(href || ''), window.location.origin).searchParams;
|
||||
} catch (_) {
|
||||
return new URLSearchParams();
|
||||
}
|
||||
}
|
||||
|
||||
function isOnlyOfficeAttachmentHref(href) {
|
||||
try {
|
||||
var url = new URL(String(href || ''), window.location.origin);
|
||||
return url.pathname === '/onlyoffice' && (url.searchParams.has('assetId') || url.searchParams.has('fileName'));
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeOnlyOfficeAttachmentHref(href) {
|
||||
try {
|
||||
var url = new URL(String(href || ''), window.location.origin);
|
||||
if (url.pathname !== '/onlyoffice') return String(href || '');
|
||||
return buildOnlyOfficeOpenUrl({
|
||||
fileUrl: url.searchParams.get('fileUrl') || '',
|
||||
fileName: url.searchParams.get('fileName') || '未命名附件',
|
||||
fileType: url.searchParams.get('fileType') || inferOnlyOfficeFileType(url.searchParams.get('fileName') || '', ''),
|
||||
assetId: url.searchParams.get('assetId') || '',
|
||||
documentId: url.searchParams.get('documentId') || currentDocumentId() || '',
|
||||
userId: url.searchParams.get('userId') || '',
|
||||
mode: url.searchParams.get('mode') || 'edit'
|
||||
});
|
||||
} catch (_) {
|
||||
return String(href || '');
|
||||
}
|
||||
}
|
||||
|
||||
function isOfficeFileName(fileName) {
|
||||
return Boolean(inferOnlyOfficeFileType(fileName, ''));
|
||||
}
|
||||
|
||||
function detailFromEditorAttachmentLink(link) {
|
||||
var rawHref = link instanceof HTMLAnchorElement ? link.href : '';
|
||||
var params = attachmentQueryParams(rawHref);
|
||||
var fileName = params.get('fileName') || (link ? link.textContent : '') || '未命名附件';
|
||||
var fileType = params.get('fileType') || inferOnlyOfficeFileType(fileName, '');
|
||||
var assetId = params.get('assetId') || (link ? link.getAttribute('data-asset-id') : '') || '';
|
||||
var fileUrl = params.get('fileUrl') || '';
|
||||
var href = rawHref;
|
||||
if (!isOnlyOfficeAttachmentHref(rawHref) && fileType) {
|
||||
fileUrl = rawHref;
|
||||
href = buildOnlyOfficeOpenUrl({
|
||||
fileUrl: fileUrl,
|
||||
fileName: fileName,
|
||||
fileType: fileType,
|
||||
assetId: assetId,
|
||||
documentId: currentDocumentId() || '',
|
||||
userId: '',
|
||||
mode: 'edit'
|
||||
});
|
||||
} else if (isOnlyOfficeAttachmentHref(rawHref)) {
|
||||
href = normalizeOnlyOfficeAttachmentHref(rawHref);
|
||||
}
|
||||
return {
|
||||
href: href,
|
||||
fileUrl: fileUrl,
|
||||
fileName: fileName,
|
||||
title: fileName,
|
||||
fileType: fileType,
|
||||
assetId: assetId,
|
||||
documentId: params.get('documentId') || currentDocumentId() || '',
|
||||
workspaceId: resolveWorkspaceId(document.body),
|
||||
fileSize: (link ? link.getAttribute('data-file-size') : '') || ''
|
||||
};
|
||||
}
|
||||
|
||||
function enhanceEditorAttachmentLink(link) {
|
||||
if (!(link instanceof HTMLAnchorElement)) return;
|
||||
var href = link.getAttribute('href') || '';
|
||||
var params = attachmentQueryParams(href);
|
||||
var fileName = params.get('fileName') || link.textContent || '';
|
||||
var className = link.getAttribute('class') || '';
|
||||
var shouldEnhance = isOnlyOfficeAttachmentHref(href)
|
||||
|| className.indexOf('mnote-uploaded-attachment-row') >= 0
|
||||
|| isOfficeFileName(fileName);
|
||||
if (!shouldEnhance) return;
|
||||
link.setAttribute('data-mnote-attachment-link', 'true');
|
||||
var assetId = params.get('assetId') || link.getAttribute('data-asset-id') || '';
|
||||
if (assetId) link.setAttribute('data-asset-id', assetId);
|
||||
if (isOnlyOfficeAttachmentHref(href)) {
|
||||
link.setAttribute('href', buildOnlyOfficeOpenPath({
|
||||
fileUrl: params.get('fileUrl') || '',
|
||||
fileName: fileName || '未命名附件',
|
||||
fileType: params.get('fileType') || inferOnlyOfficeFileType(fileName, ''),
|
||||
assetId: assetId,
|
||||
documentId: params.get('documentId') || currentDocumentId() || '',
|
||||
userId: params.get('userId') || '',
|
||||
mode: params.get('mode') || 'edit'
|
||||
}));
|
||||
}
|
||||
attachmentClassForFileName(fileName).split(/\s+/).forEach(function(name) {
|
||||
if (name) link.classList.add(name);
|
||||
});
|
||||
link.setAttribute('target', '_blank');
|
||||
link.setAttribute('rel', 'noopener noreferrer nofollow');
|
||||
void hydrateEditorAttachmentMeta(link);
|
||||
}
|
||||
|
||||
function enhanceEditorAttachmentLinks() {
|
||||
document.querySelectorAll('.editor-surface .ProseMirror a[href]').forEach(enhanceEditorAttachmentLink);
|
||||
void healLegacyOfficeAttachmentParagraphs();
|
||||
}
|
||||
|
||||
function ensureAttachmentActions() {
|
||||
var existing = document.querySelector('[data-testid="mnote-attachment-actions"]');
|
||||
if (existing instanceof HTMLElement) return existing;
|
||||
var actions = document.createElement('div');
|
||||
actions.className = 'mnote-attachment-actions';
|
||||
actions.setAttribute('data-testid', 'mnote-attachment-actions');
|
||||
actions.hidden = true;
|
||||
actions.innerHTML = '' +
|
||||
'<button type="button" class="mnote-attachment-action" data-testid="mnote-attachment-action-download" data-attachment-action="download" aria-label="下载附件"><span class="material-symbols-outlined" data-icon="download" aria-hidden="true"></span></button>' +
|
||||
'<button type="button" class="mnote-attachment-action" data-testid="mnote-attachment-action-menu" data-attachment-action="menu" aria-label="更多操作"><span class="material-symbols-outlined" data-icon="more_horiz" aria-hidden="true"></span></button>';
|
||||
actions.addEventListener('mouseenter', function() {
|
||||
if (attachmentActionsHideTimer) window.clearTimeout(attachmentActionsHideTimer);
|
||||
});
|
||||
actions.addEventListener('mouseleave', scheduleHideAttachmentActions);
|
||||
document.body.appendChild(actions);
|
||||
return actions;
|
||||
}
|
||||
|
||||
function positionAttachmentActions(link) {
|
||||
if (!(link instanceof HTMLElement)) return;
|
||||
var actions = ensureAttachmentActions();
|
||||
var rect = link.getBoundingClientRect();
|
||||
actions.hidden = false;
|
||||
actions.style.left = Math.min(window.innerWidth - 76, Math.max(8, rect.right + 6)) + 'px';
|
||||
actions.style.top = Math.max(8, rect.top + (rect.height - 28) / 2) + 'px';
|
||||
activeEditorAttachmentLink = link;
|
||||
}
|
||||
|
||||
function scheduleHideAttachmentActions() {
|
||||
if (attachmentActionsHideTimer) window.clearTimeout(attachmentActionsHideTimer);
|
||||
attachmentActionsHideTimer = window.setTimeout(function() {
|
||||
var actions = document.querySelector('[data-testid="mnote-attachment-actions"]');
|
||||
if (actions instanceof HTMLElement) actions.hidden = true;
|
||||
activeEditorAttachmentLink = null;
|
||||
}, 220);
|
||||
}
|
||||
|
||||
function openEditorAttachmentDetail(detail) {
|
||||
if (!detail || !detail.href) return;
|
||||
window.open(detail.href, '_blank', 'noopener,noreferrer');
|
||||
}
|
||||
|
||||
async function openEditorAttachmentDownload(detail) {
|
||||
if (!detail) return;
|
||||
if (detail.assetId) {
|
||||
try {
|
||||
var response = await fetch('/api/media/sign?assetId=' + encodeURIComponent(detail.assetId), {
|
||||
method: 'GET',
|
||||
credentials: 'include'
|
||||
});
|
||||
var payload = await response.json().catch(function() { return null; });
|
||||
var signedUrl = String(payload && payload.signedUrl || '').trim();
|
||||
if (response.ok && signedUrl) {
|
||||
window.open(signedUrl, '_blank', 'noopener,noreferrer');
|
||||
return;
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
var target = detail.fileUrl || detail.href;
|
||||
if (!target) return;
|
||||
window.open(target, '_blank', 'noopener,noreferrer');
|
||||
}
|
||||
|
||||
function openEditorAttachmentMenu(link, trigger) {
|
||||
var detail = detailFromEditorAttachmentLink(link);
|
||||
var rect = trigger && trigger.getBoundingClientRect ? trigger.getBoundingClientRect() : link.getBoundingClientRect();
|
||||
openTreeContextMenu('attachment', detail, rect.right, rect.bottom + 4, trigger || link);
|
||||
}
|
||||
|
||||
function openEditorAttachmentLink(link) {
|
||||
enhanceEditorAttachmentLink(link);
|
||||
openEditorAttachmentDetail(detailFromEditorAttachmentLink(link));
|
||||
}
|
||||
|
||||
enhanceEditorAttachmentLinks();
|
||||
var attachmentObserver = new MutationObserver(function() { enhanceEditorAttachmentLinks(); });
|
||||
attachmentObserver.observe(document.documentElement, { childList: true, subtree: true });
|
||||
|
||||
document.addEventListener('mouseover', function(event) {
|
||||
var link = closestAction(event.target, '.editor-surface .ProseMirror a[data-mnote-attachment-link="true"], .editor-surface .ProseMirror a.mnote-uploaded-attachment-row');
|
||||
if (!(link instanceof HTMLAnchorElement)) return;
|
||||
if (attachmentActionsHideTimer) window.clearTimeout(attachmentActionsHideTimer);
|
||||
enhanceEditorAttachmentLink(link);
|
||||
positionAttachmentActions(link);
|
||||
});
|
||||
|
||||
document.addEventListener('mouseout', function(event) {
|
||||
var link = closestAction(event.target, '.editor-surface .ProseMirror a[data-mnote-attachment-link="true"], .editor-surface .ProseMirror a.mnote-uploaded-attachment-row');
|
||||
if (!(link instanceof HTMLAnchorElement)) return;
|
||||
var next = event.relatedTarget;
|
||||
var actions = document.querySelector('[data-testid="mnote-attachment-actions"]');
|
||||
if (next && (link.contains(next) || (actions && actions.contains(next)))) return;
|
||||
scheduleHideAttachmentActions();
|
||||
});
|
||||
|
||||
document.addEventListener('click', function(e) {
|
||||
if (activeTreeContextMenu && activeTreeContextMenu.contains(e.target)) return;
|
||||
if (activeTreeContextMenu) closeTreeContextMenu();
|
||||
|
||||
var attachmentAction = closestAction(e.target, '[data-attachment-action]');
|
||||
if (attachmentAction) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
var link = activeEditorAttachmentLink;
|
||||
if (!(link instanceof HTMLAnchorElement)) return;
|
||||
var attachmentDetail = detailFromEditorAttachmentLink(link);
|
||||
var attachmentActionName = attachmentAction.getAttribute('data-attachment-action') || '';
|
||||
if (attachmentActionName === 'download') {
|
||||
openEditorAttachmentDownload(attachmentDetail);
|
||||
return;
|
||||
}
|
||||
if (attachmentActionName === 'menu') {
|
||||
openEditorAttachmentMenu(link, attachmentAction);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var editorAttachmentLink = closestAction(e.target, '.editor-surface .ProseMirror a[data-mnote-attachment-link="true"], .editor-surface .ProseMirror a.mnote-uploaded-attachment-row');
|
||||
if (editorAttachmentLink instanceof HTMLAnchorElement) {
|
||||
e.preventDefault();
|
||||
openEditorAttachmentLink(editorAttachmentLink);
|
||||
return;
|
||||
}
|
||||
|
||||
var historyClose = closestAction(e.target, '[data-page-history-action="close"]');
|
||||
if (historyClose) {
|
||||
e.preventDefault();
|
||||
@@ -3084,6 +3964,13 @@ mod tests {
|
||||
assert!(SIDEBAR_TREE_JS.contains("sidebar-file-tree-root"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("tree.filetree.open"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("tree.asset.open"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("openConvexAssetFromFileTree"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("/api/media/sign?assetId="));
|
||||
assert!(SIDEBAR_TREE_JS.contains("fetchCurrentOnlyOfficeUserId"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("/api/auth/whoami"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("buildOnlyOfficeOpenUrl"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("target.searchParams.set('userId'"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("window.open(buildOnlyOfficeOpenUrl"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("tree.filetree.internal-drop"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("tree.filetree.external-drop"));
|
||||
assert!(SIDEBAR_TREE_JS.contains("MNOTE_RECENT_LOCAL_ROOTS_KEY"));
|
||||
|
||||
@@ -168,13 +168,20 @@ a:hover {
|
||||
.material-symbols-outlined[data-icon="drive_file_move"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 6h6l2 2h8v10H4z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/%3E%3Cpath d='m13 12 3 3-3 3M8 15h8' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="delete"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5 6h14M9 6V4h6v2M8 6l1 14h6l1-14M10.5 10v6M13.5 10v6' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="right_panel_open"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 5h16v14H4zM14 5v14M8 12h6M11 9l3 3-3 3' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="preview"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2.5 12s3.5-6 9.5-6 9.5 6 9.5 6-3.5 6-9.5 6-9.5-6-9.5-6Z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/%3E%3Ccircle cx='12' cy='12' r='2.8' fill='none' stroke='black' stroke-width='2'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="download"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 4v10M8.5 11.5 12 15l3.5-3.5M5 19h14' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="notes"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6 5h12v14H6zM9 9h6M9 12h6M9 15h4' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="open_in_new"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 6H5v13h13v-3M12 5h7v7M10 14 19 5' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="share"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='18' cy='5' r='3' fill='none' stroke='black' stroke-width='2'/%3E%3Ccircle cx='6' cy='12' r='3' fill='none' stroke='black' stroke-width='2'/%3E%3Ccircle cx='18' cy='19' r='3' fill='none' stroke='black' stroke-width='2'/%3E%3Cpath d='m8.7 10.7 6.6-4.4M8.7 13.3l6.6 4.4' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E"); }
|
||||
|
||||
.material-symbols-outlined[data-icon="comment"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5 5h14v10H9l-4 4V5Z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="mode_comment"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5 5h14v10H9l-4 4V5Z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="person_add"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='9' cy='8' r='3.2' fill='none' stroke='black' stroke-width='2'/%3E%3Cpath d='M3.5 20a5.5 5.5 0 0 1 11 0M18 8v6M15 11h6' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="slideshow"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 5h16v12H4z' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/%3E%3Cpath d='m10 9 5 2-5 2zM9 21h6' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="article"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6 4h12v16H6zM9 8h6M9 12h6M9 16h4' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="sync"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20 6v5h-5M4 18v-5h5M7.5 9A6 6 0 0 1 18 6M16.5 15A6 6 0 0 1 6 18' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="drive_file_rename_outline"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m4 17-.5 3.5L7 20l11-11-3-3zM13 8l3 3M5 6h7' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="format_paint"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5 7h10l2 3-2 3H5l-2-3zM9 13v6' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="link"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9.5 14.5 14.5 9.5M10.8 6.2l1.1-1.1a4 4 0 0 1 5.7 5.7l-1.6 1.6M13.2 17.8l-1.1 1.1a4 4 0 0 1-5.7-5.7L8 11.6' fill='none' stroke='black' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E"); }
|
||||
.material-symbols-outlined[data-icon="content_copy"],
|
||||
.material-symbols-outlined[data-icon="file_copy"] { --mnote-icon-mask: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M8 8h10v12H8zM6 16H4V4h10v2' fill='none' stroke='black' stroke-width='2' stroke-linejoin='round'/%3E%3C/svg%3E"); }
|
||||
@@ -1750,6 +1757,75 @@ body {
|
||||
background: rgba(27, 28, 28, 0.08);
|
||||
}
|
||||
|
||||
.mnote-attachment-actions {
|
||||
position: fixed;
|
||||
z-index: 1100;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
height: 28px;
|
||||
padding: 2px;
|
||||
border: 1px solid rgba(27, 28, 28, 0.08);
|
||||
border-radius: 6px;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 6px 18px rgba(27, 28, 28, 0.12);
|
||||
}
|
||||
|
||||
.mnote-attachment-actions[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.mnote-attachment-action {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
background: transparent;
|
||||
color: #6D6A65;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mnote-attachment-action:hover {
|
||||
background: #F4F3F3;
|
||||
color: #37352F;
|
||||
}
|
||||
|
||||
.mnote-attachment-action .material-symbols-outlined {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.document-shell .editor-surface .ProseMirror a.mnote-uploaded-attachment-row {
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
gap: 7px !important;
|
||||
max-width: 100% !important;
|
||||
min-height: 28px !important;
|
||||
padding: 2px 4px !important;
|
||||
border-radius: 4px !important;
|
||||
color: #37352f !important;
|
||||
font-weight: 500 !important;
|
||||
line-height: 1.4 !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.document-shell .editor-surface .ProseMirror a.mnote-uploaded-attachment-row::after {
|
||||
content: "" !important;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.document-shell .editor-surface .ProseMirror a.mnote-uploaded-attachment-row[data-file-size]::after {
|
||||
content: "◔ " attr(data-file-size) !important;
|
||||
display: inline-block !important;
|
||||
margin-left: 4px !important;
|
||||
color: #9ca3af !important;
|
||||
font-size: 12px !important;
|
||||
line-height: 1 !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
.sidebar-tree .tree-node .tree-children .tree-row {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user