fix local markdown attachment regressions
This commit is contained in:
@@ -36,6 +36,19 @@ import {
|
||||
const ERROR_EVENT = `${EVENT_PREFIX}:error`;
|
||||
const COMMAND_EVENT = `${EVENT_PREFIX}:command`;
|
||||
const BRIDGE_PROTOCOL = 'mnote.leptos_tiptap.bridge.v1';
|
||||
const DEV_HOT_BUSTER = (() => {
|
||||
try {
|
||||
return new URL(import.meta.url).searchParams.get('devHot') || '';
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
})();
|
||||
|
||||
const withDevHot = (path) => {
|
||||
const url = new URL(path, window.location.origin);
|
||||
if (DEV_HOT_BUSTER) url.searchParams.set('devHot', DEV_HOT_BUSTER);
|
||||
return url.toString();
|
||||
};
|
||||
|
||||
const parseJsonScript = (id) => {
|
||||
const node = document.getElementById(id);
|
||||
@@ -66,12 +79,12 @@ import {
|
||||
return window.__mnoteLeptosTiptapRuntimePromise;
|
||||
}
|
||||
window.__mnoteLeptosTiptapRuntimePromise = (async () => {
|
||||
const manifestResponse = await fetch('/api/leptos-tiptap-runtime/manifest.json');
|
||||
const manifestResponse = await fetch(withDevHot('/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');
|
||||
const entryUrl = `/api/leptos-tiptap-runtime/${manifest.entryAssetPath}`;
|
||||
const wasmUrl = manifest.wasmAssetPath ? `/api/leptos-tiptap-runtime/${manifest.wasmAssetPath}` : undefined;
|
||||
const entryUrl = withDevHot(`/api/leptos-tiptap-runtime/${manifest.entryAssetPath}`);
|
||||
const wasmUrl = manifest.wasmAssetPath ? withDevHot(`/api/leptos-tiptap-runtime/${manifest.wasmAssetPath}`) : undefined;
|
||||
const runtime = await import(entryUrl);
|
||||
if (typeof runtime.default !== 'function' || typeof runtime.mount !== 'function' || typeof runtime.unmount !== 'function') {
|
||||
throw new Error('island runtime 导出不完整');
|
||||
@@ -204,6 +217,7 @@ import {
|
||||
window.dispatchEvent(new CustomEvent('mnote:page-aggregate-synced', {
|
||||
detail: { scriptId, documentId: aggregateDocumentId(nextAggregate) }
|
||||
}));
|
||||
enhanceEditorAttachmentLinksSoon();
|
||||
} catch (error) {
|
||||
console.warn('mnote Page Aggregate script 同步失败', error);
|
||||
}
|
||||
|
||||
@@ -363,27 +363,77 @@ export const mergeClassNames = (...values) => {
|
||||
|
||||
export const localAttachmentClassForTiptapHref = (href) => {
|
||||
const localFilePath = localFileOpenPathFromTiptapHref(href);
|
||||
return localFilePath ? attachmentClassForFileName(fileNameFromPath(localFilePath)) : '';
|
||||
if (localFilePath) return attachmentClassForFileName(fileNameFromPath(localFilePath));
|
||||
const value = String(href || '').trim();
|
||||
if (!value || isExternalOrSpecialUrl(value)) return '';
|
||||
if (value.startsWith('../') || value.includes('/../')) return '';
|
||||
return attachmentClassForFileName(fileNameFromPath(value));
|
||||
};
|
||||
|
||||
const attachmentRefsFromContext = (context) => {
|
||||
if (Array.isArray(context?.attachmentRefs)) return context.attachmentRefs;
|
||||
if (Array.isArray(context?.attachment_refs)) return context.attachment_refs;
|
||||
if (Array.isArray(context?.body?.attachmentRefs)) return context.body.attachmentRefs;
|
||||
if (Array.isArray(context?.body?.attachment_refs)) return context.body.attachment_refs;
|
||||
if (Array.isArray(context?.latestAggregate?.body?.attachmentRefs)) return context.latestAggregate.body.attachmentRefs;
|
||||
if (Array.isArray(context?.latestAggregate?.body?.attachment_refs)) return context.latestAggregate.body.attachment_refs;
|
||||
if (Array.isArray(context?.aggregate?.body?.attachmentRefs)) return context.aggregate.body.attachmentRefs;
|
||||
if (Array.isArray(context?.aggregate?.body?.attachment_refs)) return context.aggregate.body.attachment_refs;
|
||||
return [];
|
||||
};
|
||||
|
||||
const attachmentRefForTiptapHref = (href, context) => {
|
||||
const value = String(href || '').trim();
|
||||
if (!value) return null;
|
||||
return attachmentRefsFromContext(context).find((ref) => (
|
||||
ref && typeof ref === 'object' && (
|
||||
String(ref.rawHref || '') === value
|
||||
|| String(ref.normalizedHref || '') === value
|
||||
|| String(ref.resolvedUri || '') === value
|
||||
)
|
||||
)) || null;
|
||||
};
|
||||
|
||||
const withAttachmentProjectionAttrs = (attrs, attachmentRef) => {
|
||||
if (!attachmentRef || typeof attachmentRef !== 'object') return attrs;
|
||||
const next = { ...attrs };
|
||||
if (attachmentRef.exists === false) {
|
||||
next.class = mergeClassNames(next.class, 'mnote-uploaded-attachment-missing');
|
||||
next['data-mnote-attachment-missing'] = 'true';
|
||||
next['aria-label'] = `${String(attachmentRef.label || '附件')}(文件不存在)`;
|
||||
}
|
||||
if (attachmentRef.authorized === false) {
|
||||
next.class = mergeClassNames(next.class, 'mnote-uploaded-attachment-unauthorized');
|
||||
next['data-mnote-attachment-unauthorized'] = 'true';
|
||||
next['aria-label'] = `${String(attachmentRef.label || '附件')}(无权访问)`;
|
||||
}
|
||||
return next;
|
||||
};
|
||||
|
||||
export const localizeTiptapAssetUrls = (node, context) => {
|
||||
if (!node || typeof node !== 'object') return node;
|
||||
if (node.type === 'image' && node.attrs && typeof node.attrs.src === 'string') {
|
||||
node.attrs = { ...node.attrs, src: localFileOpenUrlForTiptap(node.attrs.src, context) };
|
||||
const originalSrc = String(node.attrs.src || '').trim();
|
||||
node.attrs = {
|
||||
...node.attrs,
|
||||
src: localFileOpenUrlForTiptap(originalSrc, context),
|
||||
mnoteMarkdownSrc: node.attrs.mnoteMarkdownSrc || originalSrc,
|
||||
};
|
||||
}
|
||||
if (Array.isArray(node.marks)) {
|
||||
node.marks = node.marks.map((mark) => {
|
||||
if (!mark || mark.type !== 'link' || !mark.attrs || typeof mark.attrs.href !== 'string') return mark;
|
||||
const href = localFileOpenUrlForTiptap(mark.attrs.href, context);
|
||||
const href = String(mark.attrs.href || '').trim();
|
||||
const attachmentClass = localAttachmentClassForTiptapHref(href);
|
||||
const attachmentRef = attachmentRefForTiptapHref(href, context);
|
||||
const attrs = attachmentClass
|
||||
? {
|
||||
? withAttachmentProjectionAttrs({
|
||||
...mark.attrs,
|
||||
href,
|
||||
class: mergeClassNames(mark.attrs.class, attachmentClass),
|
||||
target: mark.attrs.target || '_blank',
|
||||
rel: mark.attrs.rel || 'noopener noreferrer nofollow',
|
||||
}
|
||||
}, attachmentRef)
|
||||
: { ...mark.attrs, href };
|
||||
return { ...mark, attrs };
|
||||
});
|
||||
@@ -405,12 +455,13 @@ export const pageBodyTiptapDocumentSource = (body, fallbackText = '') => {
|
||||
};
|
||||
|
||||
export const pageBodyTiptapDocument = (body, fallbackText = '', context = null) => {
|
||||
const projectionContext = { ...(context || {}), body };
|
||||
if (pageBodyTiptapDocumentSource(body, fallbackText) === 'local_markdown.content') {
|
||||
return localizeTiptapAssetUrls(toTiptapDocument(body.content, fallbackText), context);
|
||||
return localizeTiptapAssetUrls(toTiptapDocument(body.content, fallbackText), projectionContext);
|
||||
}
|
||||
const blockDocument = body?.blockDocument || body?.block_document || body?.editorDocument || body?.editor_document || null;
|
||||
if (blockDocument) return localizeTiptapAssetUrls(toTiptapDocument(blockDocument, fallbackText), context);
|
||||
return localizeTiptapAssetUrls(toTiptapDocument(body?.content, fallbackText), context);
|
||||
if (blockDocument) return localizeTiptapAssetUrls(toTiptapDocument(blockDocument, fallbackText), projectionContext);
|
||||
return localizeTiptapAssetUrls(toTiptapDocument(body?.content, fallbackText), projectionContext);
|
||||
};
|
||||
|
||||
export const inlineTextNodes = (node) => {
|
||||
@@ -524,7 +575,7 @@ export const tiptapNodeToEditorBlock = (node, index) => {
|
||||
if (node?.type === 'blockquote') return { blockId, blockType: 'quote', props: {}, contentNodes: inlineTextNodes(firstChild(node)), childBlockIds: [] };
|
||||
if (node?.type === 'codeBlock') return { blockId, blockType: 'code_block', props: { language: typeof node?.attrs?.language === 'string' ? node.attrs.language : null }, contentNodes: inlineTextNodes(node), childBlockIds: [] };
|
||||
if (node?.type === 'horizontalRule') return { blockId, blockType: 'divider', props: {}, contentNodes: [], childBlockIds: [] };
|
||||
if (node?.type === 'image') return { blockId, blockType: 'image', props: { src: node?.attrs?.src || '', alt: node?.attrs?.alt || null, title: node?.attrs?.title || null, tiptapImage: node }, contentNodes: [], childBlockIds: [] };
|
||||
if (node?.type === 'image') return { blockId, blockType: 'image', props: { src: node?.attrs?.mnoteMarkdownSrc || node?.attrs?.src || '', alt: node?.attrs?.alt || null, title: node?.attrs?.title || null, tiptapImage: node }, contentNodes: [], childBlockIds: [] };
|
||||
if (node?.type === 'tocNode') return { blockId, blockType: 'toc', props: { tiptapTocNode: node }, contentNodes: [], childBlockIds: [] };
|
||||
if (node?.type === 'table') return { blockId, blockType: 'table', props: { tiptapTable: node }, contentNodes: inlineTextNodes(node), childBlockIds: [] };
|
||||
return null;
|
||||
|
||||
@@ -15,6 +15,20 @@ function uploadedAssetUrl(asset) {
|
||||
return String(asset && (asset.sourcePath || asset.file_url || asset.signedUrl || asset.signed_url || asset.thumbnail_url) || '').trim();
|
||||
}
|
||||
|
||||
function uploadedAssetMarkdownHref(asset) {
|
||||
var href = String(asset && (asset.markdownHref || asset.markdown_href) || '').trim();
|
||||
if (href) {
|
||||
var normalizedHref = href.replace(/\\/g, '/');
|
||||
if (normalizedHref.indexOf('../') === 0 || normalizedHref.indexOf('/../') >= 0) return '';
|
||||
return href;
|
||||
}
|
||||
var relativePath = String(asset && (asset.markdownRelativePath || asset.markdown_relative_path) || '').trim().replace(/\\/g, '/');
|
||||
if (!relativePath) return '';
|
||||
if (relativePath.indexOf('../') === 0 || relativePath.indexOf('/../') >= 0) return '';
|
||||
if (relativePath.indexOf('./') === 0) return relativePath;
|
||||
return './' + relativePath;
|
||||
}
|
||||
|
||||
function dispatchUploadedEditorChange(editorRoot, editor, deps) {
|
||||
deps = deps || {};
|
||||
if (!(editorRoot instanceof HTMLElement) || !editor || typeof editor.getJSON !== 'function') return;
|
||||
@@ -431,37 +445,31 @@ async function insertUploadedAssetIntoEditor(asset, targetRoot, deps) {
|
||||
return false;
|
||||
}
|
||||
var title = typeof deps.uploadedAssetTitle === 'function' ? deps.uploadedAssetTitle(asset) : uploadedAssetTitle(asset);
|
||||
var markdownHref = typeof deps.uploadedAssetMarkdownHref === 'function' ? deps.uploadedAssetMarkdownHref(asset) : uploadedAssetMarkdownHref(asset);
|
||||
var localOpenUrl = typeof deps.localAssetOpenUrl === 'function' ? deps.localAssetOpenUrl(asset, false) : localAssetOpenUrl(asset, false);
|
||||
var fallbackUrl = typeof deps.uploadedAssetUrl === 'function' ? deps.uploadedAssetUrl(asset) : uploadedAssetUrl(asset);
|
||||
var url = localOpenUrl || fallbackUrl;
|
||||
var url = markdownHref || localOpenUrl || fallbackUrl;
|
||||
var type = typeof deps.uploadedAssetType === 'function' ? deps.uploadedAssetType(asset) : uploadedAssetType(asset);
|
||||
var assetId = String(asset && asset.id || '').trim();
|
||||
var sizeLabel = typeof deps.uploadedFileSize === 'function' ? deps.uploadedFileSize(asset) : uploadedFileSize(asset);
|
||||
try {
|
||||
if (type === 'image' && url) {
|
||||
return editor.chain().focus().setImage({ src: url, alt: title, title: title }).run() === true;
|
||||
var imageInserted = editor.chain().focus().setImage({ src: url, alt: title, title: title }).run() === true;
|
||||
if (imageInserted) {
|
||||
document.documentElement.setAttribute('data-mnote-last-upload-inserted', 'true');
|
||||
document.documentElement.setAttribute('data-mnote-last-upload-asset-id', assetId || '');
|
||||
document.documentElement.removeAttribute('data-mnote-last-upload-insert-error');
|
||||
dispatchUploadedEditorChange(editorRoot, editor, deps);
|
||||
await persistUploadedEditorChange(editor, asset, deps);
|
||||
} else {
|
||||
document.documentElement.setAttribute('data-mnote-last-upload-inserted', 'false');
|
||||
document.documentElement.setAttribute('data-mnote-last-upload-insert-error', 'insert_image_failed');
|
||||
}
|
||||
return imageInserted;
|
||||
}
|
||||
var isLocalAsset = typeof deps.isLocalUploadedAsset === 'function' ? deps.isLocalUploadedAsset(asset) : isLocalUploadedAsset(asset);
|
||||
var userId = '';
|
||||
var buildOnlyOfficeAssetOpenUrl = typeof deps.buildOnlyOfficeAssetOpenUrl === 'function' ? deps.buildOnlyOfficeAssetOpenUrl : function() { return ''; };
|
||||
var onlyOfficeUrl = buildOnlyOfficeAssetOpenUrl(asset, userId);
|
||||
if (onlyOfficeUrl && assetId) {
|
||||
userId = typeof deps.fetchCurrentOnlyOfficeUserId === 'function' ? await deps.fetchCurrentOnlyOfficeUserId() : '';
|
||||
onlyOfficeUrl = buildOnlyOfficeAssetOpenUrl(asset, userId);
|
||||
}
|
||||
var href = onlyOfficeUrl || url;
|
||||
var href = markdownHref || url;
|
||||
if (href) {
|
||||
var storedHref = onlyOfficeUrl
|
||||
? (isLocalAsset ? onlyOfficeUrl : deps.buildOnlyOfficeOpenPath({
|
||||
fileUrl: '',
|
||||
fileName: title,
|
||||
fileType: deps.inferOnlyOfficeFileType(title, asset && asset.mime_type) || 'docx',
|
||||
assetId: assetId,
|
||||
documentId: String(asset && (asset.document_id || asset.documentId) || deps.currentDocumentId() || '').trim(),
|
||||
userId: userId || '',
|
||||
mode: 'view'
|
||||
}))
|
||||
: href;
|
||||
var inserted = editor.chain().focus().insertContent([
|
||||
{
|
||||
type: 'paragraph',
|
||||
@@ -471,7 +479,7 @@ async function insertUploadedAssetIntoEditor(asset, targetRoot, deps) {
|
||||
marks: [{
|
||||
type: 'link',
|
||||
attrs: {
|
||||
href: storedHref,
|
||||
href: href,
|
||||
target: '_blank',
|
||||
rel: 'noopener noreferrer nofollow',
|
||||
class: typeof deps.uploadedAttachmentClass === 'function' ? deps.uploadedAttachmentClass(asset) : uploadedAttachmentClass(asset)
|
||||
@@ -510,7 +518,6 @@ async function insertUploadedAssetIntoEditor(asset, targetRoot, deps) {
|
||||
}) || null;
|
||||
}
|
||||
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);
|
||||
} else if (Number(attempt) < 20) {
|
||||
@@ -755,6 +762,7 @@ function uploadedFileSize(asset) {
|
||||
window.__mnoteLocalUploadRuntime = {
|
||||
uploadedAssetTitle: uploadedAssetTitle,
|
||||
uploadedAssetUrl: uploadedAssetUrl,
|
||||
uploadedAssetMarkdownHref: uploadedAssetMarkdownHref,
|
||||
resolveEditorUploadContext: resolveEditorUploadContext,
|
||||
editorRootFromUploadOptions: editorRootFromUploadOptions,
|
||||
openEditorUploadFilePicker: openEditorUploadFilePicker,
|
||||
|
||||
@@ -11,7 +11,6 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
currentRootUri,
|
||||
currentWorkspaceSourcePayload,
|
||||
fileTreeIconKindForFileName,
|
||||
healLegacyOfficeAttachmentParagraphs,
|
||||
hydrateEditorAttachmentMeta: injectedHydrateEditorAttachmentMeta,
|
||||
inferCodeAttachmentLanguage,
|
||||
inferOnlyOfficeFileType,
|
||||
@@ -38,7 +37,8 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
var attachmentActionsHideTimer = 0;
|
||||
var editorAttachmentMissingByKey = new Map();
|
||||
var editorAttachmentRefreshSeqByKey = new Map();
|
||||
var editorAttachmentLinkSelector = '.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"], .editor-surface .ProseMirror a[href*="/office-preview"]';
|
||||
var editorAttachmentLinkSelector = '.editor-surface .ProseMirror a.mnote-uploaded-attachment-row';
|
||||
var editorAttachmentEnhanceSelector = '.editor-surface .ProseMirror a[href], ' + editorAttachmentLinkSelector;
|
||||
|
||||
function attachmentQueryParams(href) {
|
||||
try {
|
||||
@@ -58,6 +58,51 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
function decodeLocalEncodedPath(value) {
|
||||
var path = String(value || '').trim().replace(/~([0-9A-Fa-f]{2})/g, '%$1');
|
||||
if (!path) return '';
|
||||
try {
|
||||
return decodeURIComponent(path);
|
||||
} catch (_) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
function localMarkdownPathFromDocumentId(documentId) {
|
||||
var raw = String(documentId || '').trim();
|
||||
if (raw.indexOf('local-md:') !== 0) return '';
|
||||
return decodeLocalEncodedPath(raw.slice('local-md:'.length));
|
||||
}
|
||||
|
||||
function normalizeLocalRelativePath(path) {
|
||||
var parts = String(path || '').replace(/\\/g, '/').split('/');
|
||||
var normalized = [];
|
||||
for (var i = 0; i < parts.length; i += 1) {
|
||||
var part = parts[i];
|
||||
if (!part || part === '.') continue;
|
||||
if (part === '..') {
|
||||
if (normalized.length) normalized.pop();
|
||||
continue;
|
||||
}
|
||||
normalized.push(part);
|
||||
}
|
||||
return normalized.join('/');
|
||||
}
|
||||
|
||||
function localMarkdownAttachmentPathFromHref(href, documentId) {
|
||||
var raw = String(href || '').trim();
|
||||
if (!raw || raw.indexOf('#') === 0 || /^[a-z][a-z0-9+.-]*:/i.test(raw)) return '';
|
||||
var documentPath = localMarkdownPathFromDocumentId(documentId);
|
||||
if (!documentPath) return '';
|
||||
var slashIndex = documentPath.lastIndexOf('/');
|
||||
var documentDir = slashIndex >= 0 ? documentPath.slice(0, slashIndex) : '';
|
||||
var decoded = raw;
|
||||
try { decoded = decodeURIComponent(raw); } catch (_) {}
|
||||
if (decoded.indexOf('../') === 0 || decoded.indexOf('/../') >= 0) return '';
|
||||
var joined = documentDir ? documentDir + '/' + decoded : decoded;
|
||||
return normalizeLocalRelativePath(joined);
|
||||
}
|
||||
|
||||
function localFileOpenRootUriFromHref(href) {
|
||||
try {
|
||||
var url = new URL(String(href || ''), window.location.origin);
|
||||
@@ -84,6 +129,77 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
function buildLocalFileOpenUrlForRoot(relativePath, rootUri, download) {
|
||||
var effectiveRootUri = String(rootUri || currentRootUri() || '').trim();
|
||||
if (!effectiveRootUri || !relativePath) return '';
|
||||
var url = new URL('/api/local-folder/files/open', window.location.origin);
|
||||
url.searchParams.set('rootUri', effectiveRootUri);
|
||||
url.searchParams.set('path', relativePath);
|
||||
if (download) url.searchParams.set('download', 'true');
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
function fileUriToPath(uri) {
|
||||
var value = String(uri || '').trim();
|
||||
if (value.indexOf('file://') !== 0) return '';
|
||||
try {
|
||||
var url = new URL(value);
|
||||
return decodeURIComponent(url.pathname || '');
|
||||
} catch (_) {
|
||||
var raw = value.slice('file://'.length);
|
||||
if (raw.indexOf('localhost/') === 0) raw = raw.slice('localhost'.length);
|
||||
if (raw.charAt(0) !== '/') raw = '/' + raw;
|
||||
try { raw = decodeURIComponent(raw); } catch (_) {}
|
||||
return raw;
|
||||
}
|
||||
}
|
||||
|
||||
function fileUriFromPath(path) {
|
||||
var value = String(path || '').trim();
|
||||
if (!value) return '';
|
||||
return 'file://' + value.split('/').map(function(part, index) {
|
||||
return index === 0 ? '' : encodeURIComponent(part);
|
||||
}).join('/');
|
||||
}
|
||||
|
||||
function localAttachmentOpenParts(rawHref, attachmentRef, paneContext) {
|
||||
if (attachmentRef && typeof attachmentRef === 'object') {
|
||||
var refRelativePath = String(attachmentRef.relativePath || '').trim();
|
||||
if (refRelativePath) {
|
||||
return {
|
||||
rootUri: String(attachmentRef.ownerRootUri || currentRootUri() || '').trim(),
|
||||
relativePath: refRelativePath
|
||||
};
|
||||
}
|
||||
var absolutePath = String(attachmentRef.resolvedAbsolutePath || '').trim()
|
||||
|| fileUriToPath(attachmentRef.resolvedUri);
|
||||
if (absolutePath) {
|
||||
var slash = absolutePath.lastIndexOf('/');
|
||||
if (slash > 0) {
|
||||
return {
|
||||
rootUri: fileUriFromPath(absolutePath.slice(0, slash)),
|
||||
relativePath: absolutePath.slice(slash + 1)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
var apiPath = localFileOpenPathFromHref(rawHref);
|
||||
if (apiPath) {
|
||||
return {
|
||||
rootUri: localFileOpenRootUriFromHref(rawHref) || currentRootUri() || '',
|
||||
relativePath: apiPath
|
||||
};
|
||||
}
|
||||
var markdownPath = localMarkdownAttachmentPathFromHref(rawHref, paneContext && paneContext.documentId);
|
||||
if (markdownPath) {
|
||||
return {
|
||||
rootUri: currentRootUri() || '',
|
||||
relativePath: markdownPath
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildPdfPreviewOpenUrl(fileUrl, fileName) {
|
||||
if (typeof injectedBuildPdfPreviewOpenUrl === 'function') {
|
||||
return injectedBuildPdfPreviewOpenUrl(fileUrl, fileName);
|
||||
@@ -112,24 +228,43 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
function setEditorAttachmentUnauthorizedState(link, unauthorized) {
|
||||
if (!(link instanceof HTMLAnchorElement)) return;
|
||||
var value = Boolean(unauthorized);
|
||||
if (value) {
|
||||
if (link.getAttribute('data-mnote-attachment-unauthorized') === 'true' && link.classList.contains('mnote-uploaded-attachment-unauthorized')) return;
|
||||
setAttributeIfChanged(link, 'data-mnote-attachment-unauthorized', 'true');
|
||||
link.classList.add('mnote-uploaded-attachment-unauthorized');
|
||||
setAttributeIfChanged(link, 'aria-label', (link.textContent || '附件') + '(无权访问)');
|
||||
} else {
|
||||
if (link.getAttribute('data-mnote-attachment-unauthorized') !== 'true') return;
|
||||
link.removeAttribute('data-mnote-attachment-unauthorized');
|
||||
link.classList.remove('mnote-uploaded-attachment-unauthorized');
|
||||
if (link.getAttribute('data-mnote-attachment-missing') !== 'true') link.removeAttribute('aria-label');
|
||||
}
|
||||
}
|
||||
|
||||
function setAttributeIfChanged(element, name, value) {
|
||||
var nextValue = String(value || '');
|
||||
if (element.getAttribute(name) === nextValue) return;
|
||||
element.setAttribute(name, nextValue);
|
||||
}
|
||||
|
||||
function setEditorAttachmentMissingStateByHref(href, missing) {
|
||||
var targetKey = localFileOpenKeyFromHref(href);
|
||||
function setEditorAttachmentMissingStateByHref(href, missing, targetKeyOverride) {
|
||||
var targetKey = String(targetKeyOverride || '').trim() || localFileOpenKeyFromHref(href);
|
||||
if (!targetKey) return;
|
||||
if (missing) {
|
||||
editorAttachmentMissingByKey.set(targetKey, true);
|
||||
} else {
|
||||
editorAttachmentMissingByKey.delete(targetKey);
|
||||
}
|
||||
document.querySelectorAll('.editor-surface .ProseMirror a[href*="/api/local-folder/files/open"]').forEach(function(candidate) {
|
||||
document.querySelectorAll(editorAttachmentEnhanceSelector + ', .editor-surface .ProseMirror a[href*="/api/local-folder/files/open"]').forEach(function(candidate) {
|
||||
if (!(candidate instanceof HTMLAnchorElement)) return;
|
||||
var candidateHref = candidate.getAttribute('href') || candidate.href || '';
|
||||
if (localFileOpenKeyFromHref(candidateHref) !== targetKey) return;
|
||||
var candidateRef = attachmentRefForHref(candidateHref, editorAttachmentPaneContext(candidate), candidate.textContent || '');
|
||||
var candidateParts = localAttachmentOpenParts(candidateHref, candidateRef, editorAttachmentPaneContext(candidate));
|
||||
var candidateKey = candidateParts ? String(candidateParts.rootUri || '').trim() + '\n' + String(candidateParts.relativePath || '').trim() : localFileOpenKeyFromHref(candidateHref);
|
||||
if (candidateKey !== targetKey) return;
|
||||
setEditorAttachmentMissingState(candidate, missing);
|
||||
});
|
||||
}
|
||||
@@ -137,35 +272,41 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
async function refreshLocalAttachmentExistence(link) {
|
||||
if (!(link instanceof HTMLAnchorElement)) return;
|
||||
var href = link.getAttribute('href') || link.href || '';
|
||||
var attachmentKey = localFileOpenKeyFromHref(href);
|
||||
var paneContext = editorAttachmentPaneContext(link);
|
||||
var attachmentRef = attachmentRefForHref(href, paneContext, link ? link.textContent : '');
|
||||
var openParts = localAttachmentOpenParts(href, attachmentRef, paneContext);
|
||||
var attachmentKey = openParts ? String(openParts.rootUri || '').trim() + '\n' + String(openParts.relativePath || '').trim() : localFileOpenKeyFromHref(href);
|
||||
if (!attachmentKey) return;
|
||||
var refreshSeq = (editorAttachmentRefreshSeqByKey.get(attachmentKey) || 0) + 1;
|
||||
editorAttachmentRefreshSeqByKey.set(attachmentKey, refreshSeq);
|
||||
var localFilePath = localFileOpenPathFromHref(href);
|
||||
var localFilePath = openParts ? openParts.relativePath : localFileOpenPathFromHref(href);
|
||||
if (!localFilePath) return;
|
||||
var statusUrl = buildLocalFileStatusUrl(localFilePath, localFileOpenRootUriFromHref(href));
|
||||
var statusUrl = buildLocalFileStatusUrl(localFilePath, openParts ? openParts.rootUri : localFileOpenRootUriFromHref(href));
|
||||
if (!statusUrl) return;
|
||||
try {
|
||||
var response = await fetch(statusUrl, { headers: { accept: 'application/json' }, cache: 'no-store' });
|
||||
var payload = await response.json().catch(function() { return null; });
|
||||
if (editorAttachmentRefreshSeqByKey.get(attachmentKey) !== refreshSeq) return;
|
||||
if (!response.ok || !payload || payload.ok !== true || !payload.result) {
|
||||
setEditorAttachmentStatusErrorByHref(href, String(response.status || 'stat_failed'));
|
||||
setEditorAttachmentStatusErrorByHref(href, String(response.status || 'stat_failed'), attachmentKey);
|
||||
return;
|
||||
}
|
||||
var exists = Boolean(response.ok && payload && payload.ok === true && payload.result && payload.result.exists === true);
|
||||
setEditorAttachmentStatusErrorByHref(href, '');
|
||||
setEditorAttachmentMissingStateByHref(href, !exists);
|
||||
setEditorAttachmentStatusErrorByHref(href, '', attachmentKey);
|
||||
setEditorAttachmentMissingStateByHref(href, !exists, attachmentKey);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
function setEditorAttachmentStatusErrorByHref(href, status) {
|
||||
var targetKey = localFileOpenKeyFromHref(href);
|
||||
function setEditorAttachmentStatusErrorByHref(href, status, targetKeyOverride) {
|
||||
var targetKey = String(targetKeyOverride || '').trim() || localFileOpenKeyFromHref(href);
|
||||
if (!targetKey) return;
|
||||
document.querySelectorAll('.editor-surface .ProseMirror a[href*="/api/local-folder/files/open"]').forEach(function(candidate) {
|
||||
document.querySelectorAll(editorAttachmentEnhanceSelector + ', .editor-surface .ProseMirror a[href*="/api/local-folder/files/open"]').forEach(function(candidate) {
|
||||
if (!(candidate instanceof HTMLAnchorElement)) return;
|
||||
var candidateHref = candidate.getAttribute('href') || candidate.href || '';
|
||||
if (localFileOpenKeyFromHref(candidateHref) !== targetKey) return;
|
||||
var candidateRef = attachmentRefForHref(candidateHref, editorAttachmentPaneContext(candidate), candidate.textContent || '');
|
||||
var candidateParts = localAttachmentOpenParts(candidateHref, candidateRef, editorAttachmentPaneContext(candidate));
|
||||
var candidateKey = candidateParts ? String(candidateParts.rootUri || '').trim() + '\n' + String(candidateParts.relativePath || '').trim() : localFileOpenKeyFromHref(candidateHref);
|
||||
if (candidateKey !== targetKey) return;
|
||||
if (status) {
|
||||
setAttributeIfChanged(candidate, 'data-mnote-attachment-status-error', status);
|
||||
} else {
|
||||
@@ -175,15 +316,18 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
}
|
||||
|
||||
function refreshEditorLocalAttachmentExistence() {
|
||||
document.querySelectorAll('.editor-surface .ProseMirror a[href*="/api/local-folder/files/open"]').forEach(function(link) {
|
||||
document.querySelectorAll(editorAttachmentEnhanceSelector + ', .editor-surface .ProseMirror a[href*="/api/local-folder/files/open"]').forEach(function(link) {
|
||||
if (link instanceof HTMLAnchorElement) void refreshLocalAttachmentExistence(link);
|
||||
});
|
||||
}
|
||||
window.__mnoteRefreshEditorLocalAttachmentExistence = refreshEditorLocalAttachmentExistence;
|
||||
|
||||
var attachmentExistenceRefreshFrame = 0;
|
||||
function scheduleEditorLocalAttachmentExistenceRefresh() {
|
||||
[120, 500, 1200].forEach(function(delayMs) {
|
||||
window.setTimeout(refreshEditorLocalAttachmentExistence, delayMs);
|
||||
if (attachmentExistenceRefreshFrame) return;
|
||||
attachmentExistenceRefreshFrame = window.requestAnimationFrame(function() {
|
||||
attachmentExistenceRefreshFrame = 0;
|
||||
refreshEditorLocalAttachmentExistence();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -254,19 +398,57 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
};
|
||||
}
|
||||
|
||||
function currentPageAggregate() {
|
||||
var script = document.getElementById('__MNOTE_PAGE_AGGREGATE__');
|
||||
if (!(script instanceof HTMLScriptElement)) return null;
|
||||
try {
|
||||
return JSON.parse(script.textContent || 'null');
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function attachmentRefForHref(rawHref, paneContext, label) {
|
||||
var aggregate = currentPageAggregate();
|
||||
var refs = Array.isArray(aggregate?.body?.attachmentRefs) ? aggregate.body.attachmentRefs : [];
|
||||
if (!refs.length) return null;
|
||||
var href = String(rawHref || '').trim();
|
||||
var textLabel = String(label || '').trim();
|
||||
var absoluteHref = '';
|
||||
try {
|
||||
absoluteHref = new URL(href, window.location.href).href;
|
||||
} catch (_) {}
|
||||
return refs.find(function(ref) {
|
||||
if (!ref || typeof ref !== 'object') return false;
|
||||
return String(ref.rawHref || '') === href
|
||||
|| String(ref.normalizedHref || '') === href
|
||||
|| String(ref.resolvedUri || '') === href
|
||||
|| (absoluteHref && String(ref.resolvedUri || '') === absoluteHref)
|
||||
|| (!href && textLabel && String(ref.label || '').trim() === textLabel);
|
||||
}) || null;
|
||||
}
|
||||
|
||||
function detailFromEditorAttachmentLink(link) {
|
||||
var rawHref = link instanceof HTMLAnchorElement ? link.href : '';
|
||||
var rawHref = link instanceof HTMLAnchorElement ? (link.getAttribute('href') || '') : '';
|
||||
var params = attachmentQueryParams(rawHref);
|
||||
var paneContext = editorAttachmentPaneContext(link);
|
||||
var localFilePath = localFileOpenPathFromHref(rawHref);
|
||||
var fileName = params.get('fileName') || fileNameFromPath(localFilePath) || (link ? link.textContent : '') || '未命名附件';
|
||||
var fileType = params.get('fileType') || inferOnlyOfficeFileType(fileName, '');
|
||||
var attachmentRef = attachmentRefForHref(rawHref, paneContext, link ? link.textContent : '');
|
||||
if (!rawHref && attachmentRef) rawHref = String(attachmentRef.rawHref || attachmentRef.normalizedHref || attachmentRef.resolvedUri || '').trim();
|
||||
var openParts = localAttachmentOpenParts(rawHref, attachmentRef, paneContext);
|
||||
var localFilePath = openParts && openParts.relativePath ? openParts.relativePath : (
|
||||
String(attachmentRef?.relativePath || '').trim()
|
||||
|| localFileOpenPathFromHref(rawHref)
|
||||
|| localMarkdownAttachmentPathFromHref(rawHref, paneContext.documentId)
|
||||
);
|
||||
var localRootUri = openParts && openParts.rootUri ? openParts.rootUri : currentRootUri();
|
||||
var fileName = params.get('fileName') || String(attachmentRef?.label || '').trim() || fileNameFromPath(localFilePath) || (link ? link.textContent : '') || '未命名附件';
|
||||
var fileType = params.get('fileType') || inferOnlyOfficeFileType(fileName, '') || String(attachmentRef?.ext || '').trim();
|
||||
var assetId = params.get('assetId') || (link ? link.getAttribute('data-asset-id') : '') || (localFilePath ? 'local-file:' + localFilePath : '');
|
||||
var fileUrl = params.get('fileUrl') || '';
|
||||
var fileUrl = params.get('fileUrl') || (localFilePath ? buildLocalFileOpenUrlForRoot(localFilePath, localRootUri, false) : '') || String(attachmentRef?.resolvedUri || '').trim();
|
||||
var documentId = params.get('documentId') || paneContext.documentId || '';
|
||||
var href = rawHref;
|
||||
if (!isOnlyOfficeAttachmentHref(rawHref) && fileType) {
|
||||
fileUrl = rawHref;
|
||||
fileUrl = localFilePath ? buildLocalFileOpenUrlForRoot(localFilePath, localRootUri, false) : rawHref;
|
||||
href = buildOnlyOfficeOpenUrl({
|
||||
fileUrl: fileUrl,
|
||||
fileName: fileName,
|
||||
@@ -289,33 +471,53 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
documentId: documentId,
|
||||
workspaceId: paneContext.workspaceId,
|
||||
paneRole: paneContext.paneRole,
|
||||
fileSize: (link ? link.getAttribute('data-file-size') : '') || ''
|
||||
localRootUri: localRootUri,
|
||||
localRelativePath: localFilePath,
|
||||
fileSize: (link ? link.getAttribute('data-file-size') : '') || '',
|
||||
attachmentRef: attachmentRef || null,
|
||||
authorized: typeof attachmentRef?.authorized === 'boolean' ? attachmentRef.authorized : null,
|
||||
exists: typeof attachmentRef?.exists === 'boolean' ? attachmentRef.exists : null,
|
||||
openKind: String(attachmentRef?.openKind || '').trim()
|
||||
};
|
||||
}
|
||||
|
||||
function enhanceEditorAttachmentLink(link) {
|
||||
if (!(link instanceof HTMLAnchorElement)) return;
|
||||
var href = link.getAttribute('href') || '';
|
||||
var params = attachmentQueryParams(href);
|
||||
var localFilePath = localFileOpenPathFromHref(href);
|
||||
var paneContext = editorAttachmentPaneContext(link);
|
||||
var fileName = params.get('fileName') || fileNameFromPath(localFilePath) || link.textContent || '';
|
||||
var attachmentRef = attachmentRefForHref(href, paneContext, link ? link.textContent : '');
|
||||
if (!href && attachmentRef) href = String(attachmentRef.rawHref || attachmentRef.normalizedHref || attachmentRef.resolvedUri || '').trim();
|
||||
var openParts = localAttachmentOpenParts(href, attachmentRef, paneContext);
|
||||
var localFilePath = openParts && openParts.relativePath ? openParts.relativePath : (localFileOpenPathFromHref(href) || localMarkdownAttachmentPathFromHref(href, paneContext.documentId));
|
||||
var resolvedPathName = fileNameFromPath(localFilePath || fileUriToPath(attachmentRef && attachmentRef.resolvedUri));
|
||||
var fileName = params.get('fileName') || resolvedPathName || link.textContent || '';
|
||||
var className = link.getAttribute('class') || '';
|
||||
var shouldEnhance = isOnlyOfficeAttachmentHref(href)
|
||||
|| className.indexOf('mnote-uploaded-attachment-row') >= 0
|
||||
|| isOfficeFileName(fileName)
|
||||
|| Boolean(localFilePath);
|
||||
if (!shouldEnhance) return;
|
||||
setAttributeIfChanged(link, 'data-mnote-attachment-link', 'true');
|
||||
var assetId = params.get('assetId') || link.getAttribute('data-asset-id') || (localFilePath ? 'local-file:' + localFilePath : '');
|
||||
if (assetId) setAttributeIfChanged(link, 'data-asset-id', assetId);
|
||||
attachmentClassForFileName(fileName).split(/\s+/).forEach(function(name) {
|
||||
if (name) link.classList.add(name);
|
||||
});
|
||||
if (attachmentRef && typeof attachmentRef === 'object') {
|
||||
if (typeof attachmentRef.exists === 'boolean') {
|
||||
setEditorAttachmentMissingState(link, attachmentRef.exists === false);
|
||||
}
|
||||
if (typeof attachmentRef.authorized === 'boolean') {
|
||||
setEditorAttachmentUnauthorizedState(link, attachmentRef.authorized === false);
|
||||
}
|
||||
}
|
||||
setAttributeIfChanged(link, 'target', '_blank');
|
||||
setAttributeIfChanged(link, 'rel', 'noopener noreferrer nofollow');
|
||||
if (localFilePath && !isOnlyOfficeAttachmentHref(href)) {
|
||||
setEditorAttachmentMissingState(link, editorAttachmentMissingByKey.has(localFileOpenKeyFromHref(href)));
|
||||
var localAttachmentKey = openParts
|
||||
? String(openParts.rootUri || '').trim() + '\n' + String(openParts.relativePath || '').trim()
|
||||
: localFileOpenKeyFromHref(href);
|
||||
var isProjectionMissing = typeof attachmentRef?.exists === 'boolean' && attachmentRef.exists === false;
|
||||
setEditorAttachmentMissingState(link, isProjectionMissing || editorAttachmentMissingByKey.has(localAttachmentKey));
|
||||
void refreshLocalAttachmentExistence(link);
|
||||
return;
|
||||
}
|
||||
@@ -334,26 +536,12 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
}
|
||||
|
||||
function enhanceEditorAttachmentLinks() {
|
||||
document.querySelectorAll('.editor-surface .ProseMirror a[href]').forEach(enhanceEditorAttachmentLink);
|
||||
void healLegacyOfficeAttachmentParagraphs();
|
||||
document.querySelectorAll(editorAttachmentEnhanceSelector).forEach(enhanceEditorAttachmentLink);
|
||||
}
|
||||
window.__mnoteEnhanceEditorAttachmentLinks = function() {
|
||||
observeEditorAttachmentRoots();
|
||||
enhanceEditorAttachmentLinks();
|
||||
};
|
||||
var attachmentInitialEnhanceAttempts = 0;
|
||||
var attachmentInitialEnhanceTimer = window.setInterval(function() {
|
||||
attachmentInitialEnhanceAttempts += 1;
|
||||
enhanceEditorAttachmentLinks();
|
||||
if (attachmentInitialEnhanceAttempts >= 120) window.clearInterval(attachmentInitialEnhanceTimer);
|
||||
}, 500);
|
||||
var attachmentExistenceRefreshTimer = window.setInterval(function() {
|
||||
refreshEditorLocalAttachmentExistence();
|
||||
}, 2500);
|
||||
window.addEventListener('beforeunload', function() {
|
||||
if (attachmentExistenceRefreshTimer) window.clearInterval(attachmentExistenceRefreshTimer);
|
||||
attachmentExistenceRefreshTimer = 0;
|
||||
});
|
||||
var lastEditorAttachmentMouseOpen = { at: 0, href: '' };
|
||||
|
||||
function ensureAttachmentActions() {
|
||||
@@ -394,25 +582,53 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
}
|
||||
|
||||
async function openEditorAttachmentDetail(detail) {
|
||||
if (!detail || !detail.href) return;
|
||||
var localFilePath = localFilePathFromAssetId(detail.assetId);
|
||||
if (!detail) return;
|
||||
if (detail.authorized === false) {
|
||||
document.documentElement.setAttribute('data-mnote-attachment-open-blocked', 'unauthorized');
|
||||
return;
|
||||
}
|
||||
if (detail.exists === false) {
|
||||
document.documentElement.setAttribute('data-mnote-attachment-open-blocked', 'missing');
|
||||
return;
|
||||
}
|
||||
if (!detail.href) return;
|
||||
document.documentElement.removeAttribute('data-mnote-attachment-open-blocked');
|
||||
var localFilePath = localFilePathFromAssetId(detail.assetId) || String(detail.localRelativePath || '').trim();
|
||||
if (localFilePath) {
|
||||
if (await openLocalOfficeFileInActiveTab(detail, 'view')) return;
|
||||
var localRootUri = String(detail.localRootUri || currentRootUri() || '').trim();
|
||||
if (!detail.localRootUri && await openLocalOfficeFileInActiveTab(detail, 'view')) return;
|
||||
var localFileName = detail.fileName || localFilePath.split('/').pop() || localFilePath;
|
||||
var localFileUrl = buildLocalFileOpenUrl(localFilePath, false);
|
||||
var localFileUrl = buildLocalFileOpenUrlForRoot(localFilePath, localRootUri, false);
|
||||
var localOpenUrl = isPdfAttachmentFileName(localFileName) ? buildPdfPreviewOpenUrl(localFileUrl, localFileName) : localFileUrl;
|
||||
var localOfficeType = inferOnlyOfficeFileType(localFileName, '');
|
||||
var localOfficeUrl = localOfficeType
|
||||
? buildOnlyOfficeOpenUrl({
|
||||
fileUrl: localFileUrl,
|
||||
fileName: localFileName,
|
||||
fileType: localOfficeType,
|
||||
assetId: detail.assetId || ('local-file:' + localFilePath),
|
||||
documentId: detail.documentId || currentDocumentId() || '',
|
||||
userId: '',
|
||||
mode: 'view',
|
||||
workspaceId: detail.workspaceId || resolveWorkspaceId(document.body) || '',
|
||||
sourceKind: 'local_folder',
|
||||
rootUri: localRootUri
|
||||
})
|
||||
: '';
|
||||
// 非 Office 本地文件:用对应图标类型打开 active tab,失败则新窗口
|
||||
void openLocalResourceInActiveTab({
|
||||
path: localFilePath,
|
||||
title: localFileName,
|
||||
kind: fileTreeIconKindForFileName(localFileName),
|
||||
kind: localOfficeType ? 'office' : fileTreeIconKindForFileName(localFileName),
|
||||
assetId: detail.assetId,
|
||||
documentId: detail.documentId || currentDocumentId() || '',
|
||||
workspaceId: detail.workspaceId || resolveWorkspaceId(document.body) || '',
|
||||
href: localOpenUrl,
|
||||
officeUrl: localOfficeUrl,
|
||||
rootUri: localRootUri,
|
||||
paneRole: detail.paneRole || 'primary'
|
||||
}).then(function(opened) {
|
||||
if (!opened) window.open(localOpenUrl || detail.href, '_blank', 'noopener,noreferrer');
|
||||
if (!opened) window.open(localOfficeUrl || localOpenUrl || detail.href, '_blank', 'noopener,noreferrer');
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -631,9 +847,9 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
|
||||
async function openEditorAttachmentDownload(detail) {
|
||||
if (!detail) return;
|
||||
var localFilePath = localFilePathFromAssetId(detail.assetId);
|
||||
var localFilePath = localFilePathFromAssetId(detail.assetId) || String(detail.localRelativePath || '').trim();
|
||||
if (localFilePath) {
|
||||
var localDownloadUrl = buildLocalFileOpenUrl(localFilePath, true);
|
||||
var localDownloadUrl = buildLocalFileOpenUrlForRoot(localFilePath, detail.localRootUri || currentRootUri(), true);
|
||||
if (localDownloadUrl) {
|
||||
triggerBrowserDownload(localDownloadUrl);
|
||||
return;
|
||||
@@ -693,8 +909,8 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
}
|
||||
function addedNodeMayContainEditorAttachmentLink(node) {
|
||||
return node instanceof HTMLElement && (
|
||||
node.matches('.editor-surface .ProseMirror, .editor-surface .ProseMirror a[href], .editor-surface .ProseMirror p, .editor-surface .ProseMirror span, .editor-surface .ProseMirror div')
|
||||
|| node.querySelector?.('.editor-surface .ProseMirror, .editor-surface .ProseMirror a[href]')
|
||||
node.matches('.editor-surface .ProseMirror, .editor-surface .ProseMirror a[href], .editor-surface .ProseMirror a.mnote-uploaded-attachment-row, .editor-surface .ProseMirror p, .editor-surface .ProseMirror span, .editor-surface .ProseMirror div')
|
||||
|| node.querySelector?.('.editor-surface .ProseMirror, .editor-surface .ProseMirror a[href], .editor-surface .ProseMirror a.mnote-uploaded-attachment-row')
|
||||
);
|
||||
}
|
||||
function observeEditorAttachmentRoots() {
|
||||
@@ -706,10 +922,34 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
attachmentEditorObserver.observe(editor, { childList: true, subtree: true });
|
||||
});
|
||||
}
|
||||
var attachmentAggregateObserver = null;
|
||||
var attachmentObservedAggregateScript = null;
|
||||
function observePageAggregateScript() {
|
||||
if (typeof MutationObserver !== 'function') return;
|
||||
var script = document.getElementById('__MNOTE_PAGE_AGGREGATE__');
|
||||
if (!(script instanceof HTMLScriptElement)) return;
|
||||
if (script === attachmentObservedAggregateScript) return;
|
||||
if (attachmentAggregateObserver) attachmentAggregateObserver.disconnect();
|
||||
attachmentObservedAggregateScript = script;
|
||||
attachmentAggregateObserver = new MutationObserver(function() {
|
||||
scheduleEditorAttachmentEnhance();
|
||||
scheduleEditorLocalAttachmentExistenceRefresh();
|
||||
});
|
||||
attachmentAggregateObserver.observe(script, {
|
||||
attributes: true,
|
||||
characterData: true,
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
attachmentEditorObserver = new MutationObserver(function(records) {
|
||||
var shouldEnhance = Array.isArray(records) && records.some(function(record) {
|
||||
if (!record || record.type !== 'childList') return false;
|
||||
return Array.from(record.addedNodes || []).some(function(node) {
|
||||
if (node instanceof HTMLElement && node.id === '__MNOTE_PAGE_AGGREGATE__') {
|
||||
observePageAggregateScript();
|
||||
return true;
|
||||
}
|
||||
return addedNodeMayContainEditorAttachmentLink(node);
|
||||
});
|
||||
});
|
||||
@@ -718,10 +958,21 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
});
|
||||
attachmentEditorObserver.observe(document.documentElement, { childList: true, subtree: true });
|
||||
observeEditorAttachmentRoots();
|
||||
observePageAggregateScript();
|
||||
window.addEventListener('mnote:editor-attachment-links-changed', function() {
|
||||
window.__mnoteEnhanceEditorAttachmentLinks();
|
||||
scheduleEditorAttachmentEnhance();
|
||||
});
|
||||
window.addEventListener('mnote:page-aggregate-synced', function() {
|
||||
window.__mnoteEnhanceEditorAttachmentLinks();
|
||||
scheduleEditorAttachmentEnhance();
|
||||
scheduleEditorLocalAttachmentExistenceRefresh();
|
||||
});
|
||||
window.addEventListener('mnote:primary-document-activated', function() {
|
||||
window.__mnoteEnhanceEditorAttachmentLinks();
|
||||
scheduleEditorAttachmentEnhance();
|
||||
scheduleEditorLocalAttachmentExistenceRefresh();
|
||||
});
|
||||
window.addEventListener('tree:delta', scheduleEditorLocalAttachmentExistenceRefresh);
|
||||
window.addEventListener('tree:resync', scheduleEditorLocalAttachmentExistenceRefresh);
|
||||
[
|
||||
@@ -732,18 +983,8 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
|
||||
window.addEventListener(eventName, function() {
|
||||
enhanceEditorAttachmentLinks();
|
||||
observeEditorAttachmentRoots();
|
||||
window.setTimeout(function() {
|
||||
enhanceEditorAttachmentLinks();
|
||||
observeEditorAttachmentRoots();
|
||||
}, 120);
|
||||
}, true);
|
||||
});
|
||||
[120, 500, 1200, 2500].forEach(function(delayMs) {
|
||||
window.setTimeout(function() {
|
||||
enhanceEditorAttachmentLinks();
|
||||
observeEditorAttachmentRoots();
|
||||
}, delayMs);
|
||||
});
|
||||
|
||||
function interceptEditorAttachmentLink(event) {
|
||||
var editorAttachmentLink = closestAction(event.target, editorAttachmentLinkSelector);
|
||||
|
||||
@@ -1028,20 +1028,35 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
function selectSidebarFileTreeDocument(documentId, options) {
|
||||
var id = String(documentId || '').trim();
|
||||
if (!id) return false;
|
||||
var row = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape('doc:' + id) + '"]')
|
||||
var relativePath = options && options.relativePath
|
||||
? String(options.relativePath || '').trim()
|
||||
: id.indexOf('local-md:') === 0
|
||||
? decodeLocalEncodedPath(id.slice('local-md:'.length))
|
||||
: '';
|
||||
var targetRowId = options && options.rowId ? String(options.rowId || '').trim() : '';
|
||||
var row = targetRowId
|
||||
? document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(targetRowId) + '"]')
|
||||
: null;
|
||||
if (!(row instanceof HTMLElement)) {
|
||||
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 && relativePath && fileTreeRowLocalRelativePath(row) !== relativePath) {
|
||||
row = null;
|
||||
}
|
||||
if (row instanceof HTMLElement && row.closest('.tree-children--collapsed') && 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;
|
||||
}
|
||||
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,
|
||||
@@ -1050,9 +1065,19 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
select: true,
|
||||
focus: true,
|
||||
scroll: options ? options.scrollIntoView !== false : true
|
||||
});
|
||||
}).then(function(revealed) {
|
||||
if (revealed) return;
|
||||
var fallbackParentPath = localMarkdownBundleParentPath(relativePath);
|
||||
var fallbackBundleRow = fallbackParentPath ? visibleFileTreeRowByRelativePath(fallbackParentPath) : null;
|
||||
if (fallbackBundleRow instanceof HTMLElement) activateSidebarFileTreeRow(fallbackBundleRow, options);
|
||||
}).catch(function() {});
|
||||
return true;
|
||||
}
|
||||
var bundleParentPath = localMarkdownBundleParentPath(relativePath);
|
||||
var bundleRow = bundleParentPath ? visibleFileTreeRowByRelativePath(bundleParentPath) : null;
|
||||
if (bundleRow instanceof HTMLElement) {
|
||||
return activateSidebarFileTreeRow(bundleRow, options);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return activateSidebarFileTreeRow(row, options);
|
||||
@@ -1116,25 +1141,33 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
}
|
||||
document.documentElement.setAttribute('data-mnote-local-folder-restore-focused-row-id', rowId);
|
||||
document.documentElement.setAttribute('data-mnote-local-folder-restore-focus-status', 'focused');
|
||||
clearPendingLocalFolderRestoreRowId();
|
||||
return true;
|
||||
}
|
||||
|
||||
function schedulePendingLocalFolderRestoreFocus() {
|
||||
function schedulePendingLocalFolderRestoreFocus(options) {
|
||||
if (!pendingLocalFolderRestoreRowId()) return false;
|
||||
if (window.__mnotePendingLocalFolderRestoreFocusTimer) return false;
|
||||
var attempts = 0;
|
||||
var focused = false;
|
||||
focused = applyPendingLocalFolderRestoreFocusOnce() || focused;
|
||||
window.__mnotePendingLocalFolderRestoreFocusTimer = window.setInterval(function() {
|
||||
attempts += 1;
|
||||
focused = applyPendingLocalFolderRestoreFocusOnce() || focused;
|
||||
if (attempts >= 20) {
|
||||
if (focused) clearPendingLocalFolderRestoreRowId();
|
||||
else document.documentElement.setAttribute('data-mnote-local-folder-restore-focus-status', 'timeout');
|
||||
window.clearInterval(window.__mnotePendingLocalFolderRestoreFocusTimer);
|
||||
window.__mnotePendingLocalFolderRestoreFocusTimer = 0;
|
||||
}
|
||||
}, 250);
|
||||
var reason = options && options.reason ? String(options.reason) : '';
|
||||
var attempt = Math.max(0, Number(options && options.attempt || 0));
|
||||
var focused = applyPendingLocalFolderRestoreFocusOnce();
|
||||
if (focused) return true;
|
||||
if (attempt >= 4) {
|
||||
document.documentElement.setAttribute('data-mnote-local-folder-restore-focus-status', 'timeout');
|
||||
return false;
|
||||
}
|
||||
if (window.__mnotePendingLocalFolderRestoreFocusFrame) {
|
||||
window.cancelAnimationFrame(window.__mnotePendingLocalFolderRestoreFocusFrame);
|
||||
window.__mnotePendingLocalFolderRestoreFocusFrame = 0;
|
||||
}
|
||||
window.__mnotePendingLocalFolderRestoreFocusFrame = window.requestAnimationFrame(function() {
|
||||
window.__mnotePendingLocalFolderRestoreFocusFrame = 0;
|
||||
window.setTimeout(function() {
|
||||
schedulePendingLocalFolderRestoreFocus({
|
||||
reason: reason || 'retry',
|
||||
attempt: attempt + 1,
|
||||
});
|
||||
}, 60);
|
||||
});
|
||||
return focused;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,21 @@ export const createSidebarPageTreeRuntime = (dependencies = {}) => {
|
||||
var draggingPageNodeId = '';
|
||||
var activePageDropRow = null;
|
||||
|
||||
function localCreateSelectionOptions(result) {
|
||||
var selectTarget = result && result.selectTarget && typeof result.selectTarget === 'object'
|
||||
? result.selectTarget
|
||||
: result && result.revealTarget && typeof result.revealTarget === 'object'
|
||||
? result.revealTarget
|
||||
: null;
|
||||
if (!selectTarget) return { scrollIntoView: true };
|
||||
return {
|
||||
scrollIntoView: true,
|
||||
rootUri: result.rootUri || '',
|
||||
relativePath: selectTarget.relativePath || selectTarget.relative_path || '',
|
||||
rowId: selectTarget.rowId || selectTarget.row_id || ''
|
||||
};
|
||||
}
|
||||
|
||||
function navigateToDocument(nodeId, workspaceId, options) {
|
||||
if (!nodeId) return;
|
||||
var treeView = normalizeSidebarTreeMode(options && options.treeView ? options.treeView : activeSidebarTreeMode());
|
||||
@@ -111,7 +126,7 @@ export const createSidebarPageTreeRuntime = (dependencies = {}) => {
|
||||
if (typeof refreshLocalFolderAfterCommand === 'function') {
|
||||
await refreshLocalFolderAfterCommand('create', result, { parentId: effectiveParentId || null });
|
||||
}
|
||||
selectSidebarFileTreeDocument(nextDocumentId, { scrollIntoView: true });
|
||||
selectSidebarFileTreeDocument(nextDocumentId, localCreateSelectionOptions(result));
|
||||
document.documentElement.setAttribute('data-mnote-create-page-selected-document-id', nextDocumentId);
|
||||
}
|
||||
navigateToDocument(nextDocumentId, nextWorkspaceId, { treeView: activeSidebarTreeMode() });
|
||||
|
||||
@@ -423,17 +423,17 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
void refreshLocalFolderSidebarSnapshot();
|
||||
return true;
|
||||
}
|
||||
var changedPaths = Array.isArray(batch && batch.changedPaths)
|
||||
? batch.changedPaths
|
||||
: Array.isArray(batch && batch.changed_paths)
|
||||
? batch.changed_paths
|
||||
: [];
|
||||
var affectedParents = Array.isArray(batch && batch.affectedParents)
|
||||
? batch.affectedParents
|
||||
: Array.isArray(batch && batch.affected_parents)
|
||||
? batch.affected_parents
|
||||
: [];
|
||||
if (!affectedParents.length) {
|
||||
var changedPaths = Array.isArray(batch && batch.changedPaths)
|
||||
? batch.changedPaths
|
||||
: Array.isArray(batch && batch.changed_paths)
|
||||
? batch.changed_paths
|
||||
: [];
|
||||
affectedParents = changedPaths.map(function(item) {
|
||||
var relativePath = String(item && (item.relativePath || item.relative_path) || '').trim();
|
||||
return { relativePath: parentRelativePathForPath(relativePath), reason: 'derived-from-changed-path' };
|
||||
@@ -452,6 +452,10 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
affectedParents.forEach(function(parent) {
|
||||
addCommandRefreshParent(parents, parent && (parent.relativePath || parent.relative_path));
|
||||
});
|
||||
var needsSidebarRefresh = changedPaths.some(function(item) {
|
||||
var relativePath = String(item && (item.relativePath || item.relative_path) || '').trim().toLowerCase();
|
||||
return relativePath.endsWith('.md') || relativePath.endsWith('.markdown');
|
||||
});
|
||||
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) {
|
||||
@@ -459,6 +463,9 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
return false;
|
||||
});
|
||||
})).then(function() {
|
||||
if (needsSidebarRefresh) {
|
||||
void refreshLocalFolderSidebarSnapshot();
|
||||
}
|
||||
markLocalFolderWatchApplied('watch_batch');
|
||||
});
|
||||
return true;
|
||||
@@ -1391,6 +1398,7 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
if (runtimeFn) return runtimeFn(value);
|
||||
var appliedValue = String(value || 'projection');
|
||||
document.documentElement.setAttribute('data-mnote-local-folder-watch-applied', appliedValue);
|
||||
document.documentElement.removeAttribute('data-mnote-local-folder-watch-disabled');
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1492,55 +1500,11 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
if (!rootUri) return;
|
||||
ensureFileTreeLazyCacheScope();
|
||||
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)
|
||||
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
|
||||
: '';
|
||||
if (!nextRevision) return;
|
||||
if (!revision) {
|
||||
revision = nextRevision;
|
||||
return;
|
||||
}
|
||||
if (nextRevision !== revision) {
|
||||
revision = nextRevision;
|
||||
scheduleRefresh();
|
||||
}
|
||||
};
|
||||
window.setInterval(function() {
|
||||
void poll();
|
||||
}, 1200);
|
||||
void poll();
|
||||
var treeTransport = document.documentElement.getAttribute('data-mnote-tree-live-transport') || '';
|
||||
if (treeTransport !== 'local-folder-events') {
|
||||
document.documentElement.setAttribute('data-mnote-local-folder-watch-applied', 'static');
|
||||
document.documentElement.setAttribute('data-mnote-local-folder-watch-disabled', 'events-required');
|
||||
}
|
||||
}
|
||||
|
||||
function isTitleOnlyDocumentPatch(candidate) {
|
||||
|
||||
@@ -961,6 +961,22 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
return String(asset && (asset.sourcePath || asset.file_url || asset.signedUrl || asset.signed_url || asset.thumbnail_url) || '').trim();
|
||||
}
|
||||
|
||||
function uploadedAssetMarkdownHref(asset) {
|
||||
var runtimeFn = localUploadRuntimeFunction('uploadedAssetMarkdownHref');
|
||||
if (runtimeFn) return runtimeFn(asset);
|
||||
var href = String(asset && (asset.markdownHref || asset.markdown_href) || '').trim();
|
||||
if (href) {
|
||||
var normalizedHref = href.replace(/\\/g, '/');
|
||||
if (normalizedHref.indexOf('../') === 0 || normalizedHref.indexOf('/../') >= 0) return '';
|
||||
return href;
|
||||
}
|
||||
var relativePath = String(asset && (asset.markdownRelativePath || asset.markdown_relative_path) || '').trim().replace(/\\/g, '/');
|
||||
if (!relativePath) return '';
|
||||
if (relativePath.indexOf('../') === 0 || relativePath.indexOf('/../') >= 0) return '';
|
||||
if (relativePath.indexOf('./') === 0) return relativePath;
|
||||
return './' + relativePath;
|
||||
}
|
||||
|
||||
function localAssetOpenUrl(asset, download) {
|
||||
var runtimeFn = localUploadRuntimeFunction('localAssetOpenUrl');
|
||||
if (runtimeFn) return runtimeFn(asset, download, { rootUri: currentRootUri() });
|
||||
@@ -1238,7 +1254,6 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
userId: '',
|
||||
mode: 'view'
|
||||
}));
|
||||
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);
|
||||
@@ -1733,7 +1748,8 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
return false;
|
||||
}
|
||||
var title = uploadedAssetTitle(asset);
|
||||
var url = localAssetOpenUrl(asset, false) || uploadedAssetUrl(asset);
|
||||
var markdownHref = uploadedAssetMarkdownHref(asset);
|
||||
var url = markdownHref || localAssetOpenUrl(asset, false) || uploadedAssetUrl(asset);
|
||||
var type = uploadedAssetType(asset);
|
||||
var assetId = String(asset && asset.id || '').trim();
|
||||
var sizeLabel = uploadedFileSize(asset);
|
||||
@@ -1741,26 +1757,8 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
if (type === 'image' && url) {
|
||||
return editor.chain().focus().setImage({ src: url, alt: title, title: title }).run() === true;
|
||||
}
|
||||
var isLocalAsset = isLocalUploadedAsset(asset);
|
||||
var userId = '';
|
||||
var onlyOfficeUrl = buildOnlyOfficeAssetOpenUrl(asset, userId);
|
||||
if (onlyOfficeUrl && assetId) {
|
||||
userId = await fetchCurrentOnlyOfficeUserId();
|
||||
onlyOfficeUrl = buildOnlyOfficeAssetOpenUrl(asset, userId);
|
||||
}
|
||||
var href = onlyOfficeUrl || url;
|
||||
var href = markdownHref || url;
|
||||
if (href) {
|
||||
var storedHref = onlyOfficeUrl
|
||||
? (isLocalAsset ? 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: 'view'
|
||||
}))
|
||||
: href;
|
||||
var inserted = editor.chain().focus().insertContent([
|
||||
{
|
||||
type: 'paragraph',
|
||||
@@ -1770,7 +1768,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
marks: [{
|
||||
type: 'link',
|
||||
attrs: {
|
||||
href: storedHref,
|
||||
href: href,
|
||||
target: '_blank',
|
||||
rel: 'noopener noreferrer nofollow',
|
||||
class: uploadedAttachmentClass(asset)
|
||||
@@ -1806,7 +1804,6 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
}) || null;
|
||||
}
|
||||
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);
|
||||
} else if (Number(attempt) < 20) {
|
||||
@@ -2452,7 +2449,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
return;
|
||||
}
|
||||
|
||||
var editorAttachmentLink = closestAction(e.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"], .editor-surface .ProseMirror a[href*="/office-preview"]');
|
||||
var editorAttachmentLink = closestAction(e.target, '.editor-surface .ProseMirror a.mnote-uploaded-attachment-row');
|
||||
if (editorAttachmentLink instanceof HTMLAnchorElement) {
|
||||
e.preventDefault();
|
||||
openEditorAttachmentLink(editorAttachmentLink);
|
||||
@@ -2969,7 +2966,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
});
|
||||
|
||||
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"], .editor-surface .ProseMirror a[href*="/office-preview"]');
|
||||
var editorAttachmentLink = closestAction(event.target, '.editor-surface .ProseMirror a.mnote-uploaded-attachment-row');
|
||||
if (editorAttachmentLink instanceof HTMLAnchorElement) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
@@ -186,8 +186,9 @@
|
||||
return;
|
||||
}
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var sourceKind = (params.get('sourceKind') || '').trim();
|
||||
if (sourceKind === 'local_folder') {
|
||||
var sourceKind = (params.get('sourceKind') || '').trim()
|
||||
|| (document.body instanceof HTMLElement ? (document.body.getAttribute('data-mnote-source-kind') || '').trim() : '');
|
||||
if (sourceKind === 'local_folder' || bootstrap.transport === 'local-folder-events') {
|
||||
applyTransport('local-folder-events');
|
||||
applyStatus('connecting');
|
||||
var localRootUri = (params.get('rootUri') || '').trim()
|
||||
@@ -199,7 +200,7 @@
|
||||
startWithSse(bootstrap, '', url);
|
||||
return;
|
||||
}
|
||||
// No rootUri or EventSource unavailable — mark static and let polling fallback handle it
|
||||
// No rootUri or EventSource unavailable — stay static and let caller surface events-required
|
||||
applyTransport('local-folder-static');
|
||||
applyStatus('static');
|
||||
return;
|
||||
|
||||
@@ -979,6 +979,11 @@ function startTreeShellRuntime() {
|
||||
});
|
||||
if (!response.ok) return false;
|
||||
const nextState = parseTreeShellStateFromHtml(await response.text());
|
||||
document.documentElement.setAttribute(
|
||||
"data-mnote-local-folder-watch-applied",
|
||||
options.appliedValue || "projection",
|
||||
);
|
||||
document.documentElement.removeAttribute("data-mnote-local-folder-watch-disabled");
|
||||
return applyTreeShellStateSnapshot(nextState, options);
|
||||
};
|
||||
|
||||
@@ -988,6 +993,16 @@ function startTreeShellRuntime() {
|
||||
}, 80);
|
||||
};
|
||||
|
||||
let localFolderEventRefreshPending = false;
|
||||
const scheduleLocalFolderEventRefresh = (options = {}) => {
|
||||
if (localFolderEventRefreshPending) return;
|
||||
localFolderEventRefreshPending = true;
|
||||
window.setTimeout(() => {
|
||||
localFolderEventRefreshPending = false;
|
||||
void refreshLocalFolderSnapshot(options);
|
||||
}, 120);
|
||||
};
|
||||
|
||||
const addTreeItemLocally = (item) => {
|
||||
if (!item?.nodeId || itemById.has(item.nodeId)) return false;
|
||||
normalizedItems.push(item);
|
||||
@@ -1096,41 +1111,49 @@ function startTreeShellRuntime() {
|
||||
};
|
||||
|
||||
if (sourceKind === "local_folder" && rootUri) {
|
||||
let localWatchRevision = initialLocalWatchRevision;
|
||||
let localWatchRefreshTimer = 0;
|
||||
const refreshFromLocalWatch = () => {
|
||||
if (localWatchRefreshTimer) return;
|
||||
localWatchRefreshTimer = window.setTimeout(() => {
|
||||
localWatchRefreshTimer = 0;
|
||||
void refreshLocalFolderSnapshot();
|
||||
}, 180);
|
||||
let localFolderWatchRevision = initialLocalWatchRevision;
|
||||
const applyLocalFolderRevision = (revision) => {
|
||||
const nextRevision = normalizeText(revision);
|
||||
if (!nextRevision || nextRevision === localFolderWatchRevision) return false;
|
||||
localFolderWatchRevision = nextRevision;
|
||||
document.documentElement.setAttribute("data-mnote-tree-live-revision", nextRevision);
|
||||
return true;
|
||||
};
|
||||
const pollLocalFolderRevision = async () => {
|
||||
if (busy || document.hidden) return;
|
||||
const url = new URL("/api/tree/local-folder-watch", window.location.origin);
|
||||
url.searchParams.set("rootUri", rootUri);
|
||||
const response = await fetch(url.toString(), { headers: { "accept": "application/json" } });
|
||||
if (!response.ok) return;
|
||||
const payload = await response.json();
|
||||
const nextRevision =
|
||||
payload &&
|
||||
payload.result &&
|
||||
typeof payload.result.revision === "string"
|
||||
? payload.result.revision
|
||||
: "";
|
||||
if (!nextRevision) return;
|
||||
if (!localWatchRevision) {
|
||||
localWatchRevision = nextRevision;
|
||||
return;
|
||||
}
|
||||
if (nextRevision !== localWatchRevision) {
|
||||
localWatchRevision = nextRevision;
|
||||
refreshFromLocalWatch();
|
||||
}
|
||||
};
|
||||
window.setInterval(() => {
|
||||
void pollLocalFolderRevision();
|
||||
}, 1200);
|
||||
window.addEventListener("tree:snapshot", function(event) {
|
||||
const detail = event && event.detail ? event.detail : {};
|
||||
const payload = detail.payload || detail;
|
||||
if (!payload || payload.sourceKind !== "local_folder") return;
|
||||
if (!applyLocalFolderRevision(payload.revision || detail.revision)) return;
|
||||
scheduleLocalFolderEventRefresh({ appliedValue: "snapshot" });
|
||||
});
|
||||
window.addEventListener("tree:resync", function(event) {
|
||||
const detail = event && event.detail ? event.detail : {};
|
||||
const payload = detail.payload || detail;
|
||||
if (!payload || payload.sourceKind !== "local_folder") return;
|
||||
if (!applyLocalFolderRevision(payload.revision || detail.revision)) return;
|
||||
scheduleLocalFolderEventRefresh({ appliedValue: "resync" });
|
||||
});
|
||||
window.addEventListener("tree:local-folder-watch-batch", function(event) {
|
||||
const detail = event && event.detail ? event.detail : {};
|
||||
const payload = detail.payload || detail;
|
||||
if (!payload || payload.sourceKind !== "local_folder") return;
|
||||
if (!applyLocalFolderRevision(payload.revision || detail.revision)) return;
|
||||
scheduleLocalFolderEventRefresh({ appliedValue: "watch_batch" });
|
||||
});
|
||||
window.addEventListener("tree:error", function(event) {
|
||||
const detail = event && event.detail ? event.detail : {};
|
||||
const payload = detail.payload || detail;
|
||||
if (!payload || payload.sourceKind !== "local_folder") return;
|
||||
document.documentElement.setAttribute(
|
||||
"data-mnote-tree-live-error",
|
||||
normalizeText(payload.code || payload.error || payload.message, "tree_live_error"),
|
||||
);
|
||||
});
|
||||
const transport = document.documentElement.getAttribute("data-mnote-tree-live-transport") || "";
|
||||
if (transport !== "local-folder-events") {
|
||||
document.documentElement.setAttribute("data-mnote-local-folder-watch-applied", "static");
|
||||
document.documentElement.setAttribute("data-mnote-local-folder-watch-disabled", "events-required");
|
||||
}
|
||||
}
|
||||
|
||||
const readErrorMessage = async (response) => {
|
||||
|
||||
Reference in New Issue
Block a user