Files
mnote/rust/crates/mnote-web/browser/local-upload-runtime.js
T
lix-2026 4a7efd4a30 feat: Page AI Reasonix desktop session alignment, settings IA cleanup, knowledge RAG hardening
- ACP client/session manager: Reasonix desktop live session context
- Hermes tools: knowledge_rag tool manifest and skill updates
- Browser runtime: sidebar page AI permission/profile/render/session/tree modules
- Routes: hermes_client, hermes_tools, knowledge_rag, web_shell
- Scripts: reasonix ACP wrapper, LightRAG MCP, smoke tasks 159/558/559/561/562
- Skills: mnote-knowledge-rag and mnote-lightrag-bridge SKILL.md updates
2026-06-13 22:20:01 +08:00

785 lines
37 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// MNote 本地上传运行时外置模块。
// 当前先承接 SIDEBAR_TREE_JS 中的纯辅助函数。
// SIDEBAR_TREE_JS 会优先委托到 window.__mnoteLocalUploadRuntime
// 模块未加载时,inline fallback 保持旧行为。
import {
editorDocumentFromTiptapDocument,
legacyBlocksFromEditorDocument,
} from './document-tiptap-conversion-runtime.js';
function uploadedAssetTitle(asset) {
return String(asset && (asset.file_name || asset.title || asset.name) || '未命名附件').trim() || '未命名附件';
}
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;
var runtimeRoot = editorRoot.closest('[data-testid="mnote-leptos-tiptap-island-editor-root"]');
if (!(runtimeRoot instanceof HTMLElement) || typeof CustomEvent !== 'function') return;
try {
editorRoot.dispatchEvent(new Event('input', { bubbles: true }));
} catch (_) {
// ignore
}
var currentDocumentId = typeof deps.currentDocumentId === 'function' ? deps.currentDocumentId() : '';
var payload = {
protocol: 'mnote.leptos_tiptap.bridge.v1',
runtime: '8123-leptos-tiptap-runtime',
version: '1.1.0',
source: 'mnote:leptos-tiptap-spike',
event: 'mnote:leptos-tiptap-spike:change',
payload: {
documentId: String(currentDocumentId || '').trim() || null,
workspaceId: null,
title: '',
content: editor.getJSON(),
meta: {
dirtyCount: Date.now(),
editorFocused: document.activeElement === editorRoot || editorRoot.contains(document.activeElement),
slashOpen: false,
toolbarOpen: false,
selectedBlockIndex: null,
revision: null,
conflictDetectionKey: null,
readOnly: false
}
}
};
runtimeRoot.dispatchEvent(new CustomEvent('mnote:leptos-tiptap-spike:change', {
detail: payload,
bubbles: true
}));
}
function firstNonEmptyString(values) {
for (var i = 0; i < values.length; i += 1) {
var value = String(values[i] || '').trim();
if (value) return value;
}
return '';
}
function makeSaveOperationToken(prefix, documentId) {
var doc = String(documentId || 'unknown').replace(/[^a-zA-Z0-9._:-]+/g, '-').slice(0, 80);
var random = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
? crypto.randomUUID()
: Date.now().toString(36) + '-' + Math.random().toString(36).slice(2);
return prefix + ':' + doc + ':' + random;
}
function currentConflictDetectionKey(documentId, rootUri) {
try {
if (typeof window.__mnoteDebugDocumentSessions?.snapshot === 'function') {
var snapshot = window.__mnoteDebugDocumentSessions.snapshot();
var sessions = Array.isArray(snapshot && snapshot.sessions) ? snapshot.sessions : [];
var targetDocumentId = String(documentId || '').trim();
var targetRootUri = String(rootUri || '').trim();
for (var i = 0; i < sessions.length; i += 1) {
var session = sessions[i] || {};
if (targetDocumentId && String(session.documentId || '').trim() !== targetDocumentId) continue;
if (targetRootUri && String(session.rootUri || '').trim() !== targetRootUri) continue;
var sessionKey = firstNonEmptyString([
session.lastExternalConflictDetectionKey,
session.conflictDetectionKey,
session.fileVersion
]);
if (sessionKey) return sessionKey;
}
}
} catch (_) {
// 继续尝试从 page aggregate script 读取版本。
}
var script = document.getElementById('__MNOTE_PAGE_AGGREGATE__');
if (script) {
try {
var aggregate = JSON.parse(script.textContent || 'null');
var body = aggregate && typeof aggregate.body === 'object' ? aggregate.body : null;
var key = firstNonEmptyString([
body && body.conflictDetectionKey,
body && body.conflict_detection_key,
body && body.fileVersion,
body && body.file_version,
aggregate && aggregate.conflictDetectionKey,
aggregate && aggregate.conflict_detection_key,
aggregate && aggregate.fileVersion,
aggregate && aggregate.file_version
]);
if (key) return key;
} catch (_) {
// ignore
}
}
return '';
}
function localUploadSessionConflict(documentId, rootUri) {
try {
if (typeof window.__mnoteDebugDocumentSessions?.snapshot !== 'function') return null;
var snapshot = window.__mnoteDebugDocumentSessions.snapshot();
var sessions = Array.isArray(snapshot && snapshot.sessions) ? snapshot.sessions : [];
var targetDocumentId = String(documentId || '').trim();
var targetRootUri = String(rootUri || '').trim();
for (var i = 0; i < sessions.length; i += 1) {
var session = sessions[i] || {};
if (targetDocumentId && String(session.documentId || '').trim() !== targetDocumentId) continue;
if (targetRootUri && String(session.rootUri || '').trim() !== targetRootUri) continue;
var status = String(session.status || '').trim();
if (status === 'external-change-conflict' || session.hasExternalConflict === true || session.lastExternalConflictEnvelope) {
return {
status: status || 'external-change-conflict',
dirtyState: String(session.dirtyState || ''),
message: String(session.lastExternalConflictEnvelope?.message || '当前文档存在文件冲突,请先处理冲突后再上传附件')
};
}
}
} catch (_) {
return null;
}
return null;
}
function persistLocalFolderSelfChangeSuppression(documentId, expiresAt) {
var doc = String(documentId || '').trim();
if (!doc) return;
window.__mnoteLocalFolderSelfChangeSuppressions = window.__mnoteLocalFolderSelfChangeSuppressions || new Map();
window.__mnoteLocalFolderSelfChangeSuppressions.set(doc, expiresAt);
try {
var key = 'mnote.localFolder.selfChangeSuppressions.v1';
var existing = JSON.parse(window.sessionStorage.getItem(key) || '{}');
existing[doc] = expiresAt;
window.sessionStorage.setItem(key, JSON.stringify(existing));
} catch (_) {}
}
async function persistUploadedEditorChange(editor, asset, deps) {
deps = deps || {};
var isLocalAsset = typeof deps.isLocalUploadedAsset === 'function' ? deps.isLocalUploadedAsset(asset) : isLocalUploadedAsset(asset);
if (!isLocalAsset || !editor || typeof editor.getJSON !== 'function') return;
var documentId = String(
asset && (asset.document_id || asset.documentId)
|| (typeof deps.currentDocumentId === 'function' ? deps.currentDocumentId() : '')
|| ''
).trim();
var rootUri = String(
asset && (asset.root_uri || asset.rootUri)
|| (typeof deps.currentRootUri === 'function' ? deps.currentRootUri() : currentRootUri())
|| ''
).trim();
if (!documentId || !rootUri) return;
var tiptapDocument = editor.getJSON();
var editorDocument = editorDocumentFromTiptapDocument({ documentId: documentId }, tiptapDocument);
var content = legacyBlocksFromEditorDocument(editorDocument);
var savePayload = {
documentId: documentId,
sourceKind: 'local_folder',
rootUri: rootUri,
expectedFileVersion: currentConflictDetectionKey(documentId, rootUri),
writeIntentId: makeSaveOperationToken('intent:local-upload', documentId),
saveOperationId: makeSaveOperationToken('save:local-upload', documentId),
contentFormat: 'editorBlocks',
editorSource: 'local-upload-runtime',
editorDocument: editorDocument,
content: content,
tiptapDocument: tiptapDocument,
blockCount: Array.isArray(editorDocument.blocks) ? editorDocument.blocks.length : 0
};
persistLocalFolderSelfChangeSuppression(documentId, Date.now() + 5000);
var response = await fetch('/api/documents/save', {
method: 'POST',
credentials: 'include',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(savePayload)
});
var result = await response.json().catch(function() { return null; });
if (!response.ok || !result || result.ok !== true) {
var message = result && (result.message || (result.error && result.error.message)) || ('save_failed_' + response.status);
document.documentElement.setAttribute('data-mnote-last-upload-save-error', message);
if (response.status === 409 && asset && asset.id) {
document.documentElement.setAttribute('data-mnote-last-upload-orphaned-asset-id', String(asset.id || ''));
document.documentElement.setAttribute('data-mnote-last-upload-orphaned-policy', 'asset-kept-reference-unsaved-retry-required');
}
throw new Error(message);
}
var saved = result.result && typeof result.result === 'object' ? result.result : {};
var fileVersion = String(saved.fileVersion || saved.conflictDetectionKey || saved.conflict_detection_key || '').trim();
try {
window.dispatchEvent(new CustomEvent('mnote:local-upload-editor-save-completed', {
detail: {
documentId: documentId,
rootUri: rootUri,
fileVersion: fileVersion,
writeIntentId: String(saved.writeIntentId || savePayload.writeIntentId || '').trim(),
saveOperationId: String(saved.saveOperationId || savePayload.saveOperationId || '').trim(),
revision: Number.isInteger(saved.revision) ? saved.revision : null,
serialized: JSON.stringify(tiptapDocument),
source: 'local-upload-runtime'
}
}));
} catch (_) {}
document.documentElement.setAttribute('data-mnote-last-upload-saved', 'true');
document.documentElement.removeAttribute('data-mnote-last-upload-save-error');
document.documentElement.removeAttribute('data-mnote-last-upload-orphaned-asset-id');
document.documentElement.removeAttribute('data-mnote-last-upload-orphaned-policy');
}
function currentRootUri() {
var params = new URLSearchParams(window.location.search);
var fromUrl = (params.get('rootUri') || '').trim();
if (fromUrl) return fromUrl;
return document.body instanceof HTMLElement
? (document.body.getAttribute('data-mnote-root-uri') || '').trim()
: '';
}
function editorUploadRootFromElementWithDeps(target, deps) {
if (deps && typeof deps.editorUploadRootFromElement === 'function') {
return deps.editorUploadRootFromElement(target);
}
if (!(target instanceof Element)) return null;
var selector = String(deps && deps.rootSelector || '[data-editor-host-kind="leptos_tiptap_island"], [data-editor-host-kind="leptos_tiptap_resource"]');
var root = target.closest(selector);
return root instanceof HTMLElement ? root : null;
}
function resolveEditorUploadContext(detail, deps) {
var root = null;
var selector = detail && detail.editorRootSelector ? String(detail.editorRootSelector) : '';
if (selector) {
try {
var selected = document.querySelector(selector);
if (selected instanceof HTMLElement) root = selected;
} catch (_) {}
}
if (!root && window.__mnoteIntendedSlashRoot instanceof HTMLElement && window.__mnoteIntendedSlashRoot.isConnected) {
root = window.__mnoteIntendedSlashRoot;
}
if (!root && window.__mnoteLastEditorUploadRoot instanceof HTMLElement && window.__mnoteLastEditorUploadRoot.isConnected) {
root = window.__mnoteLastEditorUploadRoot;
}
if (!root && document.activeElement instanceof Element) {
root = editorUploadRootFromElementWithDeps(document.activeElement, deps);
}
if (!root) {
var rootSelector = String(deps && deps.rootSelector || '[data-editor-host-kind="leptos_tiptap_island"], [data-editor-host-kind="leptos_tiptap_resource"]');
var focused = document.querySelector(rootSelector + ' .ProseMirror:focus-within');
root = editorUploadRootFromElementWithDeps(focused, deps);
}
if (!root) {
root = document.querySelector('[data-editor-host-kind="leptos_tiptap_island"][data-pane-role="primary"]');
}
var pane = root instanceof Element ? root.closest('.document-pane[data-pane-role]') : null;
var shell = root instanceof Element ? root.closest('.document-shell[data-document-id]') : null;
var currentDocumentId = deps && typeof deps.currentDocumentId === 'function' ? deps.currentDocumentId : function() { return ''; };
var resolveWorkspaceId = deps && typeof deps.resolveWorkspaceId === 'function' ? deps.resolveWorkspaceId : function() { return ''; };
return {
root: root instanceof HTMLElement ? root : null,
documentId: String(
detail && detail.documentId
|| (root instanceof HTMLElement && root.getAttribute('data-document-id'))
|| (pane instanceof HTMLElement && pane.getAttribute('data-pane-document-id'))
|| (shell instanceof HTMLElement && shell.getAttribute('data-document-id'))
|| currentDocumentId()
|| ''
).trim(),
workspaceId: String(
detail && detail.workspaceId
|| (root instanceof HTMLElement && root.getAttribute('data-workspace-id'))
|| (pane instanceof HTMLElement && pane.getAttribute('data-pane-workspace-id'))
|| (shell instanceof HTMLElement && shell.getAttribute('data-workspace-id'))
|| resolveWorkspaceId(document.body)
|| ''
).trim()
};
}
function editorRootFromUploadOptions(options, deps) {
if (options && options.editorRoot instanceof HTMLElement) return options.editorRoot;
if (window.__mnoteIntendedSlashRoot instanceof HTMLElement && window.__mnoteIntendedSlashRoot.isConnected) {
return window.__mnoteIntendedSlashRoot;
}
if (window.__mnoteLastEditorUploadRoot instanceof HTMLElement && window.__mnoteLastEditorUploadRoot.isConnected) {
return window.__mnoteLastEditorUploadRoot;
}
var rootSelector = String(deps && deps.rootSelector || '[data-editor-host-kind="leptos_tiptap_island"], [data-editor-host-kind="leptos_tiptap_resource"]');
var focused = document.querySelector(rootSelector + ' .ProseMirror:focus-within');
return editorUploadRootFromElementWithDeps(focused, deps);
}
function openEditorUploadFilePicker(detail, deps) {
var uploadContext = resolveEditorUploadContext(detail || {}, deps || {});
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();
var resolveWorkspaceId = deps && typeof deps.resolveWorkspaceId === 'function' ? deps.resolveWorkspaceId : function() { return ''; };
var currentDocumentId = deps && typeof deps.currentDocumentId === 'function' ? deps.currentDocumentId : function() { return ''; };
var uploadFilesWithResolvedTarget = deps && typeof deps.uploadFilesWithResolvedTarget === 'function' ? deps.uploadFilesWithResolvedTarget : null;
if (!uploadFilesWithResolvedTarget) return;
void uploadFilesWithResolvedTarget(files, {
workspaceId: uploadContext.workspaceId || resolveWorkspaceId(document.body),
documentId: uploadContext.documentId || currentDocumentId(),
targetRowId: null,
uploadIntent: 'editor.markdown.attach'
}, {
insertIntoEditor: detail && detail.insertIntoEditor !== false,
editorRoot: uploadContext.root
});
}, { once: true });
input.click();
}
async function fetchWithTimeout(input, init, timeoutMs, label) {
var controller = typeof AbortController === 'function' ? new AbortController() : null;
var timer = 0;
var timeout = Math.max(1000, Number(timeoutMs) || 15000);
try {
if (controller) {
timer = window.setTimeout(function() {
controller.abort();
}, timeout);
}
var nextInit = Object.assign({}, init || {});
if (controller) nextInit.signal = controller.signal;
return await fetch(input, nextInit);
} catch (error) {
if (error && error.name === 'AbortError') {
throw new Error((label || '请求') + '超时');
}
throw error;
} finally {
if (timer) window.clearTimeout(timer);
}
}
function uploadTimeoutMsForFile(file, requestedMs) {
var requested = Number(requestedMs) || 0;
var size = Number(file && file.size || 0);
if (!Number.isFinite(size) || size <= 0) return Math.max(requested, 15000);
var sizeBased = 15000 + Math.ceil(size / (1024 * 1024)) * 12000;
return Math.min(10 * 60 * 1000, Math.max(requested, 60000, sizeBased));
}
async function uploadLocalFolderAsset(file, plan, context) {
var rootUri = String(context && context.rootUri || '').trim() || currentRootUri();
var documentId = String(
plan && plan.targetDocumentId
|| context && context.documentId
|| ''
).trim();
var hasFolderTarget = plan && Object.prototype.hasOwnProperty.call(plan, 'targetRelativePath');
var uploadIntent = String(
plan && plan.uploadIntent
|| (hasFolderTarget ? 'filetree.folder.drop' : 'editor.markdown.attach')
).trim();
if (!rootUri || !uploadIntent) {
throw new Error('本地 Markdown 上传缺少 rootUri 或 documentId');
}
var localForm = new FormData();
localForm.append('file', file);
localForm.append('rootUri', rootUri);
localForm.append('uploadIntent', uploadIntent);
if (documentId) localForm.append('documentId', documentId);
if (hasFolderTarget) localForm.append('targetRelativePath', String(plan.targetRelativePath || ''));
localForm.append('kind', file && String(file.type || '').indexOf('image/') === 0 ? 'image' : 'attachment');
var localResponse = await fetchWithTimeout('/api/local-folder/assets/upload', {
method: 'POST',
credentials: 'include',
body: localForm
}, uploadTimeoutMsForFile(file, context && context.timeoutMs), '本地上传');
var localPayload = await localResponse.json().catch(function() { return null; });
if (!localResponse.ok || !localPayload || !localPayload.asset) {
throw new Error(localPayload && localPayload.error ? localPayload.error : '上传失败');
}
return {
asset: localPayload.asset,
documentId: documentId
};
}
async function uploadMediaAsset(file, plan, context) {
document.documentElement.setAttribute('data-mnote-media-upload-retired', 'true');
throw new Error('旧 Convex Files 上传链已退役;local-first 附件请使用本地文件夹上传目标。');
}
async function insertUploadedAssetIntoEditor(asset, targetRoot, deps) {
deps = deps || {};
var editorRoot = targetRoot instanceof HTMLElement
? targetRoot.querySelector('.editor-surface .ProseMirror')
: document.querySelector('.editor-surface .ProseMirror');
if (targetRoot instanceof HTMLElement) window.__mnoteLastEditorUploadRoot = targetRoot;
var editor = editorRoot && editorRoot.editor;
if (!editor || !editor.chain) {
document.documentElement.setAttribute('data-mnote-last-upload-inserted', 'false');
document.documentElement.setAttribute('data-mnote-last-upload-insert-error', 'editor_unavailable');
return false;
}
var title = 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 imageUrl = localOpenUrl || fallbackUrl || markdownHref;
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' && imageUrl) {
var imageInserted = editor.chain().focus().setImage({ src: imageUrl, 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 href = markdownHref || url;
if (href) {
var inserted = editor.chain().focus().insertContent([
{
type: 'paragraph',
content: [{
type: 'text',
text: title,
marks: [{
type: 'link',
attrs: {
href: href,
target: '_blank',
rel: 'noopener noreferrer nofollow',
class: typeof deps.uploadedAttachmentClass === 'function' ? deps.uploadedAttachmentClass(asset) : uploadedAttachmentClass(asset)
}
}]
}]
},
{ type: 'paragraph' }
]).focus('end').run() === true;
if (inserted) {
document.documentElement.setAttribute('data-mnote-last-upload-inserted', 'true');
document.documentElement.setAttribute('data-mnote-last-upload-asset-id', assetId || '');
document.documentElement.removeAttribute('data-mnote-last-upload-insert-error');
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_content_failed');
}
window.setTimeout(function annotateUploadedLink(attempt) {
if (targetRoot instanceof HTMLElement) window.__mnoteLastEditorUploadRoot = targetRoot;
if (typeof deps.enhanceEditorAttachmentLinks === 'function') deps.enhanceEditorAttachmentLinks();
var cssEscape = typeof deps.cssEscape === 'function' ? deps.cssEscape : function(value) { return String(value || '').replace(/"/g, '\\"'); };
var selector = assetId
? '.editor-surface .ProseMirror a[href*="' + cssEscape(assetId) + '"]'
: '.editor-surface .ProseMirror a';
var link = targetRoot instanceof HTMLElement
? targetRoot.querySelector(selector)
: document.querySelector(selector);
if (!(link instanceof HTMLElement) && assetId) {
var scope = targetRoot instanceof HTMLElement ? targetRoot : document;
link = Array.from(scope.querySelectorAll('.editor-surface .ProseMirror a')).find(function(candidate) {
var href = String(candidate.getAttribute('href') || '');
try { href = decodeURIComponent(href); } catch (_) {}
return href.indexOf(assetId) >= 0;
}) || null;
}
if (link instanceof HTMLElement) {
if (assetId) link.setAttribute('data-asset-id', assetId);
if (sizeLabel) link.setAttribute('data-file-size', sizeLabel);
} else if (Number(attempt) < 20) {
window.setTimeout(function() { annotateUploadedLink(Number(attempt) + 1); }, 50);
}
}, 0, 0);
return inserted;
}
} catch (error) {
console.warn('[mnote upload] insert uploaded asset failed', error);
}
return false;
}
async function uploadFileToMediaAsset(file, plan, options, deps) {
deps = deps || {};
options = options || {};
var getCurrentSourceKind = typeof deps.currentSourceKind === 'function' ? deps.currentSourceKind : function() { return ''; };
var getCurrentRootUri = typeof deps.currentRootUri === 'function' ? deps.currentRootUri : currentRootUri;
var getCurrentDocumentId = typeof deps.currentDocumentId === 'function' ? deps.currentDocumentId : function() { return ''; };
var resolveEditorRoot = typeof deps.editorRootFromUploadOptions === 'function'
? deps.editorRootFromUploadOptions
: function(nextOptions) { return editorRootFromUploadOptions(nextOptions, deps); };
var insertIntoEditor = typeof deps.insertUploadedAssetIntoEditor === 'function'
? deps.insertUploadedAssetIntoEditor
: function(asset, targetRoot) { return insertUploadedAssetIntoEditor(asset, targetRoot, deps); };
var dispatch = typeof deps.dispatchEvent === 'function'
? deps.dispatchEvent
: function(event) { return window.dispatchEvent(event); };
var EventCtor = typeof deps.CustomEvent === 'function' ? deps.CustomEvent : window.CustomEvent;
if (getCurrentSourceKind() === 'local_folder') {
var rootUri = getCurrentRootUri();
var documentId = String(plan && plan.targetDocumentId || getCurrentDocumentId() || '').trim();
if (String(plan && plan.uploadIntent || '').trim() === 'editor.markdown.attach' && documentId) {
var conflict = localUploadSessionConflict(documentId, rootUri);
if (conflict) {
document.documentElement.setAttribute('data-mnote-last-upload-blocked-conflict', 'true');
document.documentElement.setAttribute('data-mnote-last-upload-save-error', conflict.message || '当前文档存在文件冲突,请先处理冲突后再上传附件');
throw new Error(conflict.message || '当前文档存在文件冲突,请先处理冲突后再上传附件');
}
document.documentElement.removeAttribute('data-mnote-last-upload-blocked-conflict');
}
if (String(plan && plan.uploadIntent || '').trim() === 'editor.markdown.attach' && documentId) {
persistLocalFolderSelfChangeSuppression(documentId, Date.now() + 8000);
}
var localResult = await uploadLocalFolderAsset(file, plan, {
rootUri: rootUri,
documentId: documentId,
timeoutMs: uploadTimeoutMsForFile(file, 15000)
});
var localAsset = localResult && localResult.asset ? localResult.asset : null;
if (!localAsset) {
throw new Error('上传失败');
}
if (options.insertIntoEditor) {
await insertIntoEditor(localAsset, resolveEditorRoot(options));
}
if (!options.insertIntoEditor && typeof deps.refreshLocalFolderSidebarSnapshot === 'function') {
void deps.refreshLocalFolderSidebarSnapshot();
}
dispatch(new EventCtor('wolai:local-assets-changed', {
detail: { docId: documentId, asset: localAsset, assetIds: [localAsset.id] }
}));
return localAsset;
}
var mediaResult = await uploadMediaAsset(file, plan, { timeoutMs: 15000 });
var mediaAsset = mediaResult && mediaResult.asset ? mediaResult.asset : null;
if (!mediaAsset) {
throw new Error('上传失败');
}
if (typeof deps.appendUploadedAssetRow === 'function') {
deps.appendUploadedAssetRow(mediaAsset, plan && plan.targetDocumentId);
}
if (options.insertIntoEditor) {
await insertIntoEditor(mediaAsset, resolveEditorRoot(options));
}
dispatch(new EventCtor('wolai:assets-changed', {
detail: {
docId: plan && plan.targetDocumentId,
asset: mediaAsset,
assetIds: [mediaAsset.id]
}
}));
return mediaAsset;
}
async function uploadFilesWithResolvedTarget(files, detail, options, deps) {
deps = deps || {};
var resolveFileTreeUploadTarget = typeof deps.resolveFileTreeUploadTarget === 'function' ? deps.resolveFileTreeUploadTarget : null;
var uploadFileToMediaAsset = typeof deps.uploadFileToMediaAsset === 'function' ? deps.uploadFileToMediaAsset : null;
if (!resolveFileTreeUploadTarget || !uploadFileToMediaAsset) {
throw new Error('上传运行时缺少目标解析或上传函数');
}
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) {
var message = '部分文件上传失败:\n' + errors.slice(0, 6).join('\n') + (errors.length > 6 ? '\n...' : '');
if (typeof deps.alert === 'function') {
deps.alert(message);
} else {
window.alert(message);
}
}
return uploaded;
}
function localAssetOpenUrl(asset, download, context) {
if (!isLocalUploadedAsset(asset)) return '';
var rootUri = String(context && context.rootUri || asset && (asset.rootUri || asset.root_uri) || '').trim() || currentRootUri();
var rootRelativePath = String(asset && (asset.rootRelativePath || asset.root_relative_path) || '').trim();
if (!rootUri || !rootRelativePath) return '';
var url = new URL('/api/local-folder/files/open', window.location.origin);
url.searchParams.set('rootUri', rootUri);
url.searchParams.set('path', rootRelativePath);
if (download) url.searchParams.set('download', 'true');
return url.toString();
}
function uploadedAssetType(asset) {
return String(asset && (asset.asset_type || asset.assetType || asset.mime_type || '') || '').trim();
}
function fileTreeIconKindForFileName(fileName) {
var name = String(fileName || '').trim().toLowerCase();
var ext = name.indexOf('.') >= 0 ? name.split('.').pop() : '';
if (['doc', 'docx', 'odt', 'rtf', 'ppt', 'pptx', 'odp', 'xls', 'xlsx', 'ods', 'csv'].indexOf(ext) >= 0) return 'office';
if (ext === 'md' || ext === 'markdown') return 'markdown';
if (ext === 'pdf') return 'pdf';
if (['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp'].indexOf(ext) >= 0) return 'image';
if (['html', 'htm', 'css', 'scss', 'less', 'js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs', 'vue', 'svelte', 'astro'].indexOf(ext) >= 0) return 'web';
if (['json', 'jsonc', 'json5', 'toml', 'yaml', 'yml', 'ini', 'env', 'xml', 'lock', 'hcl', 'tf', 'tfvars', 'nix', 'properties', 'conf', 'cfg', 'config', 'service', 'desktop', 'gitignore', 'gitattributes', 'editorconfig', 'npmrc', 'prettierrc', 'eslintrc'].indexOf(ext) >= 0 || ['.dockerignore', '.editorconfig', '.env', '.eslintrc', '.gitattributes', '.gitignore', '.npmrc', '.prettierrc', 'dockerfile', 'containerfile', 'makefile', 'cmakelists.txt', 'gemfile', 'rakefile', 'procfile'].indexOf(name) >= 0) return 'config';
if (['rs', 'py', 'go', 'java', 'c', 'cpp', 'h', 'hpp', 'cs', 'php', 'rb', 'sh', 'bash', 'zsh', 'sql', 'lua', 'dart', 'kt', 'kts', 'swift', 'scala', 'gradle', 'groovy', 'clj', 'ex', 'exs', 'erl', 'hrl', 'fs', 'fsx', 'r', 'jl', 'm', 'mm', 'pl', 'pm', 'ps1', 'bat', 'cmd', 'psm1', 'psd1', 'proto', 'graphql', 'gql', 'prisma', 'cmake', 'bazel', 'bzl'].indexOf(ext) >= 0) return 'code';
return '';
}
function isLocalUploadedAsset(asset) {
var id = String(asset && asset.id || '').trim();
return String(asset && asset.sourceKind || '').trim() === 'local_folder' || id.indexOf('local:asset:') === 0;
}
function uploadedAssetExtension(asset) {
var match = uploadedAssetTitle(asset).toLowerCase().match(/\.([a-z0-9]+)$/);
return match ? match[1] : '';
}
function isNonOfficeAttachmentName(name, ext) {
var codeFileNames = [
'.dockerignore', '.editorconfig', '.env', '.eslintrc', '.gitattributes', '.gitignore', '.npmrc', '.prettierrc',
'dockerfile', 'makefile', 'cmakelists.txt', 'gemfile', 'rakefile', 'procfile'
];
return [
'pdf', 'toml', 'json', 'yaml', 'yml', 'md', 'markdown', 'txt', 'ini', 'env', 'xml', 'html', 'htm', 'css', 'scss',
'less', 'js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs', 'py', 'rs', 'go', 'java', 'c', 'cpp', 'h', 'hpp', 'cs',
'php', 'rb', 'sh', 'bash', 'zsh', 'sql', 'lock', 'log', 'vue', 'svelte', 'astro', 'jsonc', 'json5',
'mts', 'cts', 'lua', 'dart', 'kt', 'kts', 'swift', 'scala', 'gradle', 'groovy', 'clj',
'ex', 'exs', 'erl', 'hrl', 'fs', 'fsx', 'r', 'jl', 'm', 'mm', 'pl', 'pm', 'ps1', 'bat', 'cmd',
'psm1', 'psd1', 'dockerfile', 'containerfile', 'proto', 'graphql', 'gql', 'prisma', 'tf', 'tfvars',
'hcl', 'nix', 'cmake', 'bazel', 'bzl', 'properties', 'conf', 'cfg', 'config', 'service', 'desktop',
'gitignore', 'gitattributes', 'editorconfig', 'npmrc', 'prettierrc', 'eslintrc'
].indexOf(ext) >= 0 || codeFileNames.indexOf(name) >= 0;
}
function attachmentExtensionFromFileName(fileName) {
var name = String(fileName || '').trim().toLowerCase();
return name.indexOf('.') >= 0 ? name.split('.').pop() : '';
}
function isPdfAttachmentFileName(fileName) {
return attachmentExtensionFromFileName(fileName) === 'pdf';
}
function isCodeAttachmentFileName(fileName) {
var name = String(fileName || '').trim().toLowerCase();
var ext = attachmentExtensionFromFileName(name);
return ext !== 'pdf' && isNonOfficeAttachmentName(name, ext);
}
function inferCodeAttachmentLanguage(fileName) {
var name = String(fileName || '').trim().toLowerCase();
var ext = attachmentExtensionFromFileName(name);
var byName = {
'dockerfile': 'dockerfile',
'makefile': 'makefile',
'cmakelists.txt': 'cmake',
'.gitignore': 'gitignore',
'.gitattributes': 'gitattributes',
'.editorconfig': 'ini',
'.env': 'dotenv'
};
if (byName[name]) return byName[name];
var byExt = {
bash: 'bash', bat: 'batch', c: 'c', cjs: 'javascript', cmd: 'batch', conf: 'text', cpp: 'cpp',
cs: 'csharp', css: 'css', cts: 'typescript', dart: 'dart', dockerfile: 'dockerfile', env: 'dotenv',
go: 'go', gql: 'graphql', gradle: 'groovy', graphql: 'graphql', h: 'c', hcl: 'hcl', hpp: 'cpp',
htm: 'html', html: 'html', ini: 'ini', java: 'java', js: 'javascript', json: 'json', json5: 'json',
jsonc: 'jsonc', jsx: 'javascript', kt: 'kotlin', kts: 'kotlin', less: 'less', log: 'text',
lua: 'lua', m: 'objective-c', markdown: 'markdown', md: 'markdown', mjs: 'javascript',
mts: 'typescript', nix: 'nix', php: 'php', pl: 'perl', pm: 'perl', prisma: 'prisma',
proto: 'protobuf', ps1: 'powershell', py: 'python', r: 'r', rb: 'ruby', rs: 'rust',
scss: 'scss', sh: 'bash', sql: 'sql', svelte: 'svelte', swift: 'swift', tf: 'terraform',
tfvars: 'terraform', toml: 'toml', ts: 'typescript', tsx: 'typescript', txt: 'text',
vue: 'vue', xml: 'xml', yaml: 'yaml', yml: 'yaml', zsh: 'bash'
};
return byExt[ext] || 'text';
}
function attachmentClassForFileName(fileName) {
var name = String(fileName || '').trim().toLowerCase();
var ext = name.indexOf('.') >= 0 ? name.split('.').pop() : '';
if (['doc', 'docx', 'odt', 'rtf'].indexOf(ext) >= 0) return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-word';
if (['ppt', 'pptx', 'odp'].indexOf(ext) >= 0) return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-ppt';
if (['xls', 'xlsx', 'ods', 'csv'].indexOf(ext) >= 0) return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-sheet';
if (ext === 'pdf') return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-pdf';
if (isNonOfficeAttachmentName(name, ext)) {
return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-code';
}
return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-file';
}
function uploadedAttachmentClass(asset) {
return attachmentClassForFileName(uploadedAssetTitle(asset));
}
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';
}
window.__mnoteLocalUploadRuntime = {
uploadedAssetTitle: uploadedAssetTitle,
uploadedAssetUrl: uploadedAssetUrl,
uploadedAssetMarkdownHref: uploadedAssetMarkdownHref,
resolveEditorUploadContext: resolveEditorUploadContext,
editorRootFromUploadOptions: editorRootFromUploadOptions,
openEditorUploadFilePicker: openEditorUploadFilePicker,
fetchWithTimeout: fetchWithTimeout,
uploadTimeoutMsForFile: uploadTimeoutMsForFile,
uploadLocalFolderAsset: uploadLocalFolderAsset,
uploadMediaAsset: uploadMediaAsset,
insertUploadedAssetIntoEditor: insertUploadedAssetIntoEditor,
uploadFileToMediaAsset: uploadFileToMediaAsset,
uploadFilesWithResolvedTarget: uploadFilesWithResolvedTarget,
localAssetOpenUrl: localAssetOpenUrl,
uploadedAssetType: uploadedAssetType,
fileTreeIconKindForFileName: fileTreeIconKindForFileName,
isLocalUploadedAsset: isLocalUploadedAsset,
uploadedAssetExtension: uploadedAssetExtension,
isNonOfficeAttachmentName: isNonOfficeAttachmentName,
attachmentExtensionFromFileName: attachmentExtensionFromFileName,
isPdfAttachmentFileName: isPdfAttachmentFileName,
isCodeAttachmentFileName: isCodeAttachmentFileName,
inferCodeAttachmentLanguage: inferCodeAttachmentLanguage,
attachmentClassForFileName: attachmentClassForFileName,
uploadedAttachmentClass: uploadedAttachmentClass,
uploadedFileSize: uploadedFileSize
};