推进 OCR UI 与 provider 删除验收
This commit is contained in:
@@ -23,6 +23,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
openEditorAttachmentDownload,
|
||||
openEditorAttachmentEditTab,
|
||||
openEditorAttachmentNewWindow,
|
||||
openLocalResourceInActiveTab,
|
||||
refreshLocalFolderSidebarSnapshot,
|
||||
removeFileTreeAssetRow,
|
||||
revealFileTreeResource,
|
||||
@@ -379,9 +380,114 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
callback();
|
||||
}
|
||||
|
||||
function isLocalOcrSourceFileName(fileName) {
|
||||
return /\.(png|jpe?g|webp|gif|bmp|tiff?|pdf)$/i.test(String(fileName || '').trim());
|
||||
}
|
||||
|
||||
function localOcrSourceRelativePath(detail) {
|
||||
var workspacePath = detail && detail.workspacePath && typeof detail.workspacePath === 'object' ? detail.workspacePath : null;
|
||||
var relativePath = String(
|
||||
detail && detail.localRelativePath
|
||||
|| workspacePath && (workspacePath.relativePath || workspacePath.localRelativePath)
|
||||
|| ''
|
||||
).trim();
|
||||
if (!relativePath && detail && detail.assetId) relativePath = localFilePathFromAssetId(detail.assetId);
|
||||
return relativePath.replace(/^\/+/, '');
|
||||
}
|
||||
|
||||
function localOcrRootUri(detail, trigger) {
|
||||
var workspacePath = detail && detail.workspacePath && typeof detail.workspacePath === 'object' ? detail.workspacePath : null;
|
||||
var rootUri = String(
|
||||
detail && (detail.localRootUri || detail.rootUri)
|
||||
|| workspacePath && workspacePath.rootUri
|
||||
|| ''
|
||||
).trim();
|
||||
if (!rootUri && trigger && typeof trigger.closest === 'function') {
|
||||
var row = trigger.closest('.tree-row[data-shell-mode="filetree"]');
|
||||
if (row instanceof HTMLElement) rootUri = String(row.getAttribute('data-root-uri') || '').trim();
|
||||
}
|
||||
return rootUri || currentRootUri() || '';
|
||||
}
|
||||
|
||||
function supportsLocalOcr(detail) {
|
||||
var path = localOcrSourceRelativePath(detail);
|
||||
var title = String(detail && (detail.title || detail.fileName) || '').trim() || path.split('/').pop() || '';
|
||||
return Boolean(path && localOcrRootUri(detail, null) && isLocalOcrSourceFileName(title || path));
|
||||
}
|
||||
|
||||
function localOcrProvider() {
|
||||
var override = String(window.__MNOTE_LOCAL_OCR_PROVIDER || '').trim().toLowerCase();
|
||||
return override === 'mock' ? 'mock' : 'mineru';
|
||||
}
|
||||
|
||||
async function runLocalOcrForDetail(detail, trigger) {
|
||||
var sourceRootRelativePath = localOcrSourceRelativePath(detail);
|
||||
var rootUri = localOcrRootUri(detail, trigger);
|
||||
var documentId = String(detail && detail.documentId || currentDocumentId() || '').trim();
|
||||
if (!sourceRootRelativePath || !rootUri || !documentId) {
|
||||
throw new Error('缺少 OCR 来源、rootUri 或 owner documentId');
|
||||
}
|
||||
if (!isLocalOcrSourceFileName(String(detail && (detail.title || detail.fileName) || sourceRootRelativePath))) {
|
||||
throw new Error('OCR 仅支持图片和 PDF');
|
||||
}
|
||||
var provider = localOcrProvider();
|
||||
var body = {
|
||||
rootUri: rootUri,
|
||||
documentId: documentId,
|
||||
sourceRootRelativePath: sourceRootRelativePath,
|
||||
provider: provider
|
||||
};
|
||||
if (provider === 'mock') {
|
||||
body.mockMarkdown = '# OCR Result\n\n' + (detail.title || sourceRootRelativePath) + ' OCR menu smoke text';
|
||||
}
|
||||
document.documentElement.setAttribute('data-mnote-local-ocr-menu-status', 'running');
|
||||
var response = await fetch('/api/local-folder/ocr/jobs', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
var payload = await response.json().catch(function() { return null; });
|
||||
if (!response.ok || !payload || payload.ok !== true) {
|
||||
var message = payload && payload.error && payload.error.message ? payload.error.message : 'OCR 生成失败';
|
||||
document.documentElement.setAttribute('data-mnote-local-ocr-menu-status', 'failed');
|
||||
throw new Error(message);
|
||||
}
|
||||
var job = payload.job || {};
|
||||
document.documentElement.setAttribute('data-mnote-local-ocr-menu-status', String(job.status || 'done'));
|
||||
if (job.ocrRootRelativePath) {
|
||||
document.documentElement.setAttribute('data-mnote-local-ocr-menu-path', String(job.ocrRootRelativePath));
|
||||
if (typeof openLocalResourceInActiveTab === 'function') {
|
||||
await openLocalResourceInActiveTab({
|
||||
path: String(job.ocrRootRelativePath),
|
||||
title: String(job.ocrRootRelativePath).split('/').filter(Boolean).pop() || 'OCR',
|
||||
kind: 'markdown',
|
||||
assetId: 'local-ocr:' + String(job.ocrRootRelativePath),
|
||||
documentId: documentId,
|
||||
workspaceId: String(detail && detail.workspaceId || resolveWorkspaceId(document.body) || '').trim(),
|
||||
rootUri: rootUri
|
||||
});
|
||||
}
|
||||
}
|
||||
window.dispatchEvent(new CustomEvent('mnote:local-ocr-job-updated', {
|
||||
detail: { status: String(job.status || 'done'), job: job, rootUri: rootUri, sourceRootRelativePath: sourceRootRelativePath }
|
||||
}));
|
||||
return job;
|
||||
}
|
||||
|
||||
function handleTreeContextMenuAction(action, detail, trigger) {
|
||||
closeTreeContextMenu();
|
||||
detail = detail || {};
|
||||
if (action === 'local-ocr') {
|
||||
recordFileTreeAction('local-ocr', detail);
|
||||
recordFileTreeActionStatus('pending', detail);
|
||||
void runLocalOcrForDetail(detail, trigger).then(function(job) {
|
||||
recordFileTreeActionStatus(String(job && job.status || 'done'), detail);
|
||||
}).catch(function(error) {
|
||||
recordFileTreeActionStatus('failed', Object.assign({}, detail, { fallback: 'alert' }));
|
||||
window.alert(error && error.message ? error.message : 'OCR 生成失败');
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (detail.contextKind === 'attachment') {
|
||||
if (action === 'copy-link') {
|
||||
void copyTreeContextValue(detail.href || '', 'attachment-copy-link');
|
||||
@@ -841,6 +947,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
menu.setAttribute('data-command-context-target-kind', String(ctx['tree.targetResourceKind'] || ''));
|
||||
menu.setAttribute('data-command-context-editor-dirty', String(ctx['editor.dirty'] === true));
|
||||
menu.setAttribute('data-command-context-ai-can-write', String(ctx['ai.canWrite'] === true));
|
||||
var localOcrSupported = supportsLocalOcr(detail);
|
||||
var items = isAttachment ? [
|
||||
{ action: 'duplicate', icon: 'file_copy', label: '拷贝副本', shortcut: 'Ctrl + D' },
|
||||
{ action: 'delete-trash', icon: 'delete', label: '删除', shortcut: 'Del', danger: true, destructive: true, requiresApproval: true, when: '!workspace.readonly' },
|
||||
@@ -903,6 +1010,11 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
{ separator: true },
|
||||
{ action: 'delete-trash', icon: 'delete', label: '删除', shortcut: 'Del', danger: true, destructive: true, requiresApproval: true, when: '!workspace.readonly && !editor.dirty' }
|
||||
];
|
||||
if (localOcrSupported) {
|
||||
var ocrItem = { action: 'local-ocr', icon: 'auto_awesome', label: '生成 OCR', when: '!workspace.readonly' };
|
||||
var insertAt = isAttachment ? 11 : isAsset ? 3 : -1;
|
||||
if (insertAt >= 0) items.splice(insertAt, 0, ocrItem);
|
||||
}
|
||||
items.forEach(function(item) {
|
||||
if (item.when !== undefined && !evaluateSidebarFileTreeWhen(ctx, item.when)) {
|
||||
var reason = ctx['workspace.readonly'] === true
|
||||
|
||||
Reference in New Issue
Block a user