fix: stabilize local OCR and prod startup
This commit is contained in:
@@ -1506,6 +1506,12 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
const statusTextForLocalOcrJob = (job) => {
|
||||
const status = String(job?.status || '').trim();
|
||||
if (job?.stageLabel) return String(job.stageLabel);
|
||||
if (job?.taskKind === 'local_index') {
|
||||
return status === 'done' ? '索引已完成'
|
||||
: status === 'failed' ? '索引失败'
|
||||
: status === 'running' ? '索引中'
|
||||
: status || '未知';
|
||||
}
|
||||
return status === 'done' ? '已识别'
|
||||
: status === 'failed' ? '识别失败'
|
||||
: status === 'stale' ? '来源已变化'
|
||||
@@ -1513,6 +1519,11 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
: status || '未知';
|
||||
};
|
||||
|
||||
const localOcrDisplayStatus = (job, fallback = 'done') => {
|
||||
const status = String(job?.status || fallback).trim() || fallback;
|
||||
return job?.stale === true && status === 'done' ? 'stale' : status;
|
||||
};
|
||||
|
||||
const localOcrTaskCategory = (job) => {
|
||||
const status = String(job?.status || '').trim();
|
||||
if (['failed', 'stale', 'retry_scheduled'].includes(status)) return 'attention';
|
||||
@@ -1755,11 +1766,12 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
if (open instanceof HTMLButtonElement) {
|
||||
open.setAttribute('data-mnote-local-ocr-task-open', String(job.sourceRootRelativePath || ''));
|
||||
open.disabled = !job.ocrRootRelativePath;
|
||||
open.hidden = job.taskKind === 'local_index';
|
||||
}
|
||||
const retry = row.querySelector('[data-mnote-local-ocr-task-retry]');
|
||||
if (retry instanceof HTMLButtonElement) {
|
||||
retry.setAttribute('data-mnote-local-ocr-task-retry', String(job.sourceRootRelativePath || ''));
|
||||
retry.hidden = !['failed', 'stale'].includes(String(job.status || ''));
|
||||
retry.hidden = job.taskKind === 'local_index' || !['failed', 'stale'].includes(String(job.status || ''));
|
||||
}
|
||||
const clear = row.querySelector('[data-mnote-local-ocr-task-clear]');
|
||||
if (clear instanceof HTMLButtonElement) {
|
||||
@@ -1768,7 +1780,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
const deleteOcr = row.querySelector('[data-mnote-local-ocr-task-delete]');
|
||||
if (deleteOcr instanceof HTMLButtonElement) {
|
||||
deleteOcr.setAttribute('data-mnote-local-ocr-task-delete', String(job.sourceRootRelativePath || ''));
|
||||
deleteOcr.hidden = !job.ocrRootRelativePath;
|
||||
deleteOcr.hidden = job.taskKind === 'local_index' || !job.ocrRootRelativePath;
|
||||
}
|
||||
list.appendChild(row);
|
||||
});
|
||||
@@ -1882,7 +1894,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok || !payload || payload.ok !== true) {
|
||||
const message = payload?.error?.message || `local_ocr_job_failed_${response.status}`;
|
||||
const message = payload?.error?.message || payload?.message || payload?.error?.code || `local_ocr_job_failed_${response.status}`;
|
||||
const failedJob = {
|
||||
...pendingJob,
|
||||
status: 'failed',
|
||||
@@ -1896,7 +1908,8 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
throw new Error(message);
|
||||
}
|
||||
const job = payload.job && typeof payload.job === 'object' ? { ...payload.job, rootUri: entryRootUri } : null;
|
||||
setLocalOcrStatus(entry, String(job?.status || 'done'), job?.stale ? 'OCR 需更新' : 'OCR 已完成', job);
|
||||
const displayStatus = localOcrDisplayStatus(job, 'done');
|
||||
setLocalOcrStatus(entry, displayStatus, displayStatus === 'stale' ? 'OCR 需更新' : 'OCR 已完成', job);
|
||||
if (job) updateLocalOcrTaskState(job);
|
||||
return job;
|
||||
};
|
||||
@@ -2052,6 +2065,25 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('mnote:local-background-task-updated', (event) => {
|
||||
const task = event?.detail?.task && typeof event.detail.task === 'object' ? event.detail.task : null;
|
||||
if (!task) return;
|
||||
updateLocalOcrTaskState({
|
||||
taskKind: String(task.taskKind || 'local_index'),
|
||||
jobId: String(task.jobId || task.taskId || `local-index-${Date.now()}`),
|
||||
sourceRootRelativePath: String(task.sourceRootRelativePath || task.taskId || 'local-index'),
|
||||
rootUri: String(task.rootUri || localOcrTaskState.rootUri || currentWebShellRootUri() || '').trim(),
|
||||
ocrRootRelativePath: '',
|
||||
provider: String(task.provider || 'mnote-web'),
|
||||
status: String(task.status || 'running'),
|
||||
stageLabel: String(task.stageLabel || ''),
|
||||
stale: false,
|
||||
updatedAtMs: Number(task.updatedAtMs || Date.now()),
|
||||
finishedAtMs: task.finishedAtMs == null ? null : Number(task.finishedAtMs),
|
||||
error: task.error || null,
|
||||
});
|
||||
});
|
||||
|
||||
const renderLocalOcrToolbar = (entry) => {
|
||||
if (!isLocalOcrSourceEntry(entry) || !(entry.panel instanceof HTMLElement)) return;
|
||||
const toolbar = entry.panel.querySelector('[data-testid="mnote-local-ocr-toolbar"]');
|
||||
@@ -2092,7 +2124,8 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
void loadLocalOcrJobs(entry.rootUri).catch(() => undefined);
|
||||
void readLocalOcrStatus(entry).then((job) => {
|
||||
if (!job) return;
|
||||
setLocalOcrStatus(entry, job.stale ? 'stale' : String(job.status || 'done'), job.stale ? 'OCR 需更新' : 'OCR 已完成', job);
|
||||
const displayStatus = localOcrDisplayStatus(job, 'done');
|
||||
setLocalOcrStatus(entry, displayStatus, displayStatus === 'stale' ? 'OCR 需更新' : 'OCR 已完成', job);
|
||||
updateLocalOcrTaskState(job);
|
||||
}).catch(() => undefined);
|
||||
};
|
||||
|
||||
@@ -961,8 +961,28 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
}
|
||||
}
|
||||
|
||||
function emitLocalIndexBackgroundTask(status, label, error) {
|
||||
var now = Date.now();
|
||||
window.dispatchEvent(new CustomEvent('mnote:local-background-task-updated', {
|
||||
detail: {
|
||||
task: {
|
||||
taskKind: 'local_index',
|
||||
taskId: 'local-index',
|
||||
sourceRootRelativePath: '本地索引',
|
||||
rootUri: currentRootUri(),
|
||||
status: status,
|
||||
stageLabel: label,
|
||||
updatedAtMs: now,
|
||||
finishedAtMs: status === 'running' ? null : now,
|
||||
error: error || null
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
async function refreshLocalIndex() {
|
||||
if (!pageSettingsLocalIndexIsAvailable()) return;
|
||||
emitLocalIndexBackgroundTask('running', '本地索引重建中', null);
|
||||
pageUiState.localIndexSummary = Object.assign({}, pageUiState.localIndexSummary || {}, {
|
||||
scopeKey: pageSettingsLocalIndexScopeKey(),
|
||||
loading: true,
|
||||
@@ -982,8 +1002,10 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
if (!response.ok || !payload || payload.ok !== true) {
|
||||
throw new Error(payload && payload.error && payload.error.message ? payload.error.message : 'local_index_refresh_' + response.status);
|
||||
}
|
||||
emitLocalIndexBackgroundTask('done', '本地索引已完成', null);
|
||||
await loadPageSettingsLocalIndex(true);
|
||||
} catch (error) {
|
||||
emitLocalIndexBackgroundTask('failed', '本地索引失败', error instanceof Error ? error.message : String(error));
|
||||
pageUiState.localIndexSummary = Object.assign({}, pageUiState.localIndexSummary || {}, {
|
||||
scopeKey: pageSettingsLocalIndexScopeKey(),
|
||||
loading: false,
|
||||
|
||||
@@ -1712,7 +1712,7 @@ fn broadcast_ocr_job_update(state: &AppState, root: &Path, root_uri: &str, entry
|
||||
}
|
||||
|
||||
fn ocr_job_payload(root: &Path, entry: &OcrIndexEntry) -> Value {
|
||||
let stale = source_is_stale(root, entry);
|
||||
let stale = entry.status == "done" && source_is_stale(root, entry);
|
||||
json!({
|
||||
"jobId": entry.job_id,
|
||||
"ownerDocumentId": entry.owner_document_id,
|
||||
|
||||
Reference in New Issue
Block a user