Files
mnote/rust/crates/mnote-web/browser/local-upload-runtime.js
T
lix-2026 0a03572601 refactor: externalize local upload helpers
Add local-upload-runtime.js as a browser runtime module for upload and attachment helper functions, with sidebar fallback wrappers and a fixed runtime asset route.

While validating task479, fix the editor attachment delete path so DOM-selected local attachment links are mapped back to a Tiptap text selection before delete_selection(), preserving undo history and adjacent links.
2026-05-24 22:42:53 +08:00

162 lines
7.7 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 保持旧行为。
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 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 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';
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,
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
};