feat(rag): replace LiteParse flows with LightRAG provider
This commit is contained in:
@@ -258,6 +258,26 @@ import {
|
||||
if (window.CSS && typeof window.CSS.escape === 'function') return window.CSS.escape(text);
|
||||
return text.replace(/["\\]/g, '\\$&');
|
||||
};
|
||||
const localFileOpenUrl = (rootUri, path) => {
|
||||
const normalizedRootUri = String(rootUri || '').trim();
|
||||
const normalizedPath = String(path || '').trim();
|
||||
if (!normalizedRootUri || !normalizedPath) return '';
|
||||
const url = new URL('/api/local-folder/files/open', window.location.origin);
|
||||
url.searchParams.set('rootUri', normalizedRootUri);
|
||||
url.searchParams.set('path', normalizedPath);
|
||||
return url.toString();
|
||||
};
|
||||
const resourceHrefFromUrlState = (rootUri, path, title) => {
|
||||
const fileUrl = localFileOpenUrl(rootUri, path);
|
||||
if (!fileUrl) return '';
|
||||
if (/\.pdf$/i.test(String(title || path || ''))) {
|
||||
const url = new URL('/pdf-preview', window.location.origin);
|
||||
url.searchParams.set('fileUrl', fileUrl);
|
||||
url.searchParams.set('fileName', title || path);
|
||||
return url.toString();
|
||||
}
|
||||
return fileUrl;
|
||||
};
|
||||
const applyDocumentEvidenceLocatorFromUrl = (root) => {
|
||||
if (!(root instanceof HTMLElement)) return;
|
||||
const url = currentUrl();
|
||||
@@ -289,7 +309,7 @@ import {
|
||||
const url = currentUrl();
|
||||
const raw = String(url.searchParams.get('resourceTab') || '').trim();
|
||||
if (!raw) return null;
|
||||
const objectIdentity = raw.includes('::') ? raw.slice(raw.indexOf('::') + 2) : raw;
|
||||
const objectIdentity = raw.startsWith('resource:') ? raw : (raw.includes('::') ? raw.slice(raw.indexOf('::') + 2) : raw);
|
||||
if (!objectIdentity.startsWith('resource:file:')) return null;
|
||||
const rootUri = currentWebShellRootUri();
|
||||
if (!rootUri) return null;
|
||||
@@ -299,6 +319,7 @@ import {
|
||||
const path = rest.slice(prefix.length).replace(/^\/+/, '');
|
||||
if (!path) return null;
|
||||
const title = path.split('/').filter(Boolean).pop() || path;
|
||||
const href = resourceHrefFromUrlState(rootUri, path, title);
|
||||
return {
|
||||
objectIdentity,
|
||||
documentId: currentWebShellDocumentId(),
|
||||
@@ -309,6 +330,8 @@ import {
|
||||
assetId: `local:asset:${path}`,
|
||||
title,
|
||||
fileName: title,
|
||||
href,
|
||||
officeUrl: href,
|
||||
openTarget: 'active-tab',
|
||||
page: url.searchParams.get('page') || undefined,
|
||||
bbox: url.searchParams.get('bbox') || undefined,
|
||||
@@ -1021,18 +1044,22 @@ import {
|
||||
});
|
||||
}, { once: true });
|
||||
|
||||
const restoreResourceTabFromUrl = () => {
|
||||
const restoreInput = restoreResourceTabInputFromUrl();
|
||||
if (!restoreInput) return Promise.resolve(false);
|
||||
return openResourceInActiveTab(restoreInput).catch((error) => {
|
||||
console.warn('mnote resource tab URL 恢复失败', error);
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
if (paneRuntimes.length) {
|
||||
Promise.all(paneRuntimes.map((paneRuntime) => mountPane(paneRuntime))).then(() => {
|
||||
const restoreInput = restoreResourceTabInputFromUrl();
|
||||
if (restoreInput) {
|
||||
return openResourceInActiveTab(restoreInput).catch((error) => {
|
||||
console.warn('mnote resource tab URL 恢复失败', error);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
return false;
|
||||
return restoreResourceTabFromUrl();
|
||||
}).catch((error) => {
|
||||
console.error('mnote multi-pane editor mount failed', error);
|
||||
});
|
||||
} else {
|
||||
void restoreResourceTabFromUrl();
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -1373,17 +1373,17 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
const statusNode = entry.panel.querySelector('[data-mnote-local-ocr-status-text]');
|
||||
if (statusNode instanceof HTMLElement) {
|
||||
statusNode.textContent = message || (
|
||||
normalizedStatus === 'done' ? 'OCR 已完成'
|
||||
: normalizedStatus === 'running' ? 'OCR 处理中'
|
||||
: normalizedStatus === 'failed' ? 'OCR 失败'
|
||||
: normalizedStatus === 'stale' ? 'OCR 需更新'
|
||||
: 'OCR 未生成'
|
||||
normalizedStatus === 'done' ? '资料库已索引'
|
||||
: normalizedStatus === 'running' ? '资料库索引中'
|
||||
: normalizedStatus === 'failed' ? '资料库索引失败'
|
||||
: normalizedStatus === 'stale' ? '资料库需更新'
|
||||
: '资料库未索引'
|
||||
);
|
||||
}
|
||||
const openButton = entry.panel.querySelector('[data-mnote-local-ocr-action="open"]');
|
||||
if (openButton instanceof HTMLButtonElement) openButton.disabled = !entry.localOcrJob?.ocrRootRelativePath;
|
||||
if (openButton instanceof HTMLButtonElement) openButton.disabled = true;
|
||||
const insertButton = entry.panel.querySelector('[data-mnote-local-ocr-action="insert"]');
|
||||
if (insertButton instanceof HTMLButtonElement) insertButton.disabled = !entry.localOcrJob?.ocrRootRelativePath;
|
||||
if (insertButton instanceof HTMLButtonElement) insertButton.disabled = true;
|
||||
window.dispatchEvent(new CustomEvent('mnote:local-ocr-job-updated', {
|
||||
detail: {
|
||||
status: normalizedStatus,
|
||||
@@ -1394,15 +1394,50 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
}));
|
||||
};
|
||||
|
||||
const knowledgeRagJobFromRegistryEntry = (registryEntry, rootUri) => {
|
||||
if (!registryEntry || typeof registryEntry !== 'object') return null;
|
||||
const sourceRootRelativePath = String(registryEntry.sourceRootRelativePath || registryEntry.source_path || '').trim();
|
||||
if (!sourceRootRelativePath) return null;
|
||||
const indexed = Boolean(registryEntry.indexedAtMs || registryEntry.indexed_at_ms) && Boolean(registryEntry.lightRagDocId || registryEntry.light_rag_doc_id);
|
||||
const failed = registryEntry.deletedAtMs != null || registryEntry.deleted_at_ms != null || registryEntry.stale === true;
|
||||
const status = failed ? 'failed' : indexed ? 'done' : 'running';
|
||||
return {
|
||||
taskKind: 'knowledge_rag',
|
||||
jobId: String(registryEntry.sourceId || registryEntry.source_id || `knowledge-rag:${sourceRootRelativePath}`),
|
||||
sourceRootRelativePath,
|
||||
rootUri: String(registryEntry.rootUri || rootUri || '').trim(),
|
||||
ocrRootRelativePath: '',
|
||||
provider: 'lightrag',
|
||||
status,
|
||||
stageLabel: status === 'done' ? '资料库已索引' : status === 'failed' ? '资料库需重建' : '资料库索引中',
|
||||
stale: registryEntry.stale === true,
|
||||
updatedAtMs: Number(registryEntry.updatedAtMs || registryEntry.updated_at_ms || Date.now()),
|
||||
finishedAtMs: status === 'done' ? Number(registryEntry.indexedAtMs || registryEntry.indexed_at_ms || Date.now()) : null,
|
||||
error: status === 'failed' ? 'knowledge_rag_source_stale_or_deleted' : '',
|
||||
lightRagDocId: String(registryEntry.lightRagDocId || registryEntry.light_rag_doc_id || ''),
|
||||
lightRagFilePath: String(registryEntry.lightRagFilePath || registryEntry.light_rag_file_path || ''),
|
||||
};
|
||||
};
|
||||
|
||||
const knowledgeRagRegistryEntries = (payload) => {
|
||||
const registry = payload && payload.registry && typeof payload.registry === 'object' ? payload.registry : null;
|
||||
return Array.isArray(registry?.entries) ? registry.entries : [];
|
||||
};
|
||||
|
||||
const readLocalOcrStatus = async (entry) => {
|
||||
if (!isLocalOcrSourceEntry(entry)) return null;
|
||||
const url = new URL('/api/local-folder/ocr/status', window.location.origin);
|
||||
const url = new URL('/api/knowledge-rag/status', window.location.origin);
|
||||
url.searchParams.set('rootUri', entry.rootUri);
|
||||
url.searchParams.set('sourceRootRelativePath', entry.path);
|
||||
const workspaceId = String(entry.workspaceId || currentWebShellWorkspaceId() || '').trim();
|
||||
if (workspaceId) url.searchParams.set('workspaceId', workspaceId);
|
||||
const response = await fetch(url.toString(), { cache: 'no-store', headers: { accept: 'application/json' } });
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok || !payload || payload.ok !== true) return null;
|
||||
return payload.job || null;
|
||||
const targetPath = String(entry.path || '').trim().replace(/\\/g, '/').replace(/^\/+/, '');
|
||||
const registryEntry = knowledgeRagRegistryEntries(payload).find((candidate) => {
|
||||
return String(candidate?.sourceRootRelativePath || '').trim().replace(/\\/g, '/').replace(/^\/+/, '') === targetPath;
|
||||
});
|
||||
return knowledgeRagJobFromRegistryEntry(registryEntry, entry.rootUri);
|
||||
};
|
||||
|
||||
const localOcrTaskState = {
|
||||
@@ -1475,7 +1510,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
toggle.addEventListener('click', (event) => {
|
||||
if (toggle.getAttribute('data-mnote-action') === 'open-ocr-settings') {
|
||||
event.preventDefault();
|
||||
window.dispatchEvent(new CustomEvent('mnote:open-local-ocr-settings'));
|
||||
window.dispatchEvent(new CustomEvent('mnote:open-knowledge-rag-settings'));
|
||||
return;
|
||||
}
|
||||
void runManualLocalOcrForActiveTarget(toggle).catch((error) => {
|
||||
@@ -1506,7 +1541,7 @@ 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') {
|
||||
if (job?.taskKind === 'local_index' || job?.taskKind === 'knowledge_rag') {
|
||||
return status === 'done' ? '索引已完成'
|
||||
: status === 'failed' ? '索引失败'
|
||||
: status === 'running' ? '索引中'
|
||||
@@ -1557,20 +1592,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
document.body.appendChild(dock);
|
||||
}
|
||||
let toggle = document.querySelector('[data-testid="mnote-local-ocr-task-toggle"]');
|
||||
if (!(toggle instanceof HTMLButtonElement)) {
|
||||
const actions = document.querySelector('.wolai-topbar-actions');
|
||||
toggle = document.createElement('button');
|
||||
toggle.type = 'button';
|
||||
toggle.className = 'wolai-icon-button mnote-local-ocr-task-toggle';
|
||||
toggle.setAttribute('title', 'OCR 设置');
|
||||
toggle.setAttribute('aria-label', 'OCR 设置');
|
||||
toggle.setAttribute('data-testid', 'mnote-local-ocr-task-toggle');
|
||||
toggle.setAttribute('data-mnote-action', 'open-ocr-settings');
|
||||
toggle.innerHTML = '<span class="material-symbols-outlined" data-icon="document_scanner" aria-hidden="true"></span><span class="mnote-local-ocr-task-badge" data-mnote-local-ocr-task-count hidden>0</span>';
|
||||
if (actions instanceof HTMLElement) actions.appendChild(toggle);
|
||||
else document.body.appendChild(toggle);
|
||||
}
|
||||
bindLocalOcrTopbarAction();
|
||||
if (toggle instanceof HTMLButtonElement) bindLocalOcrTopbarAction();
|
||||
if (dock.getAttribute('data-mnote-local-ocr-bound') === 'true') return dock;
|
||||
dock.setAttribute('data-mnote-local-ocr-bound', 'true');
|
||||
dock.addEventListener('click', (event) => {
|
||||
@@ -1617,23 +1639,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
}
|
||||
const openButton = event.target instanceof Element ? event.target.closest('[data-mnote-local-ocr-task-open]') : null;
|
||||
if (openButton instanceof HTMLElement) {
|
||||
const sourcePath = openButton.getAttribute('data-mnote-local-ocr-task-open') || '';
|
||||
const job = localOcrTaskState.jobsBySource.get(sourcePath) || null;
|
||||
if (job) {
|
||||
void openResourceInActiveTab({
|
||||
kind: 'markdown',
|
||||
title: String(job.ocrRootRelativePath || '').split('/').filter(Boolean).pop() || 'OCR',
|
||||
path: job.ocrRootRelativePath,
|
||||
objectIdentity: `local-ocr:${job.ocrRootRelativePath}`,
|
||||
assetId: `local-ocr:${job.ocrRootRelativePath}`,
|
||||
documentId: job.ownerDocumentId || currentWebShellDocumentId() || '',
|
||||
ownerDocumentId: job.ownerDocumentId || currentWebShellDocumentId() || '',
|
||||
workspaceId: currentWebShellWorkspaceId() || '',
|
||||
sourceKind: 'local_folder',
|
||||
rootUri: localOcrTaskState.rootUri || '',
|
||||
resourceKind: 'markdown',
|
||||
}).catch((error) => console.warn('mnote local OCR 任务打开失败', error));
|
||||
}
|
||||
window.dispatchEvent(new CustomEvent('mnote:open-knowledge-rag-settings'));
|
||||
}
|
||||
const retryButton = event.target instanceof Element ? event.target.closest('[data-mnote-local-ocr-task-retry]') : null;
|
||||
if (retryButton instanceof HTMLElement) {
|
||||
@@ -1670,7 +1676,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
.filter((node) => node instanceof HTMLButtonElement);
|
||||
taskToggles.forEach((toggle) => {
|
||||
const opensSettings = toggle.getAttribute('data-mnote-action') === 'open-ocr-settings';
|
||||
const label = runningCount > 0 ? `${runningCount} 个 OCR 处理中,打开 OCR 设置` : (jobs.length > 0 ? `${jobs.length} 个 OCR 任务,打开 OCR 设置` : 'OCR 设置');
|
||||
const label = runningCount > 0 ? `${runningCount} 个资料库索引处理中,打开资料库设置` : (jobs.length > 0 ? `${jobs.length} 个资料库任务,打开资料库设置` : '资料库设置');
|
||||
const taskLabel = runningCount > 0 ? `${runningCount} 个后台任务正在运行` : (jobs.length > 0 ? `${jobs.length} 个后台任务` : '后台任务');
|
||||
toggle.setAttribute('title', opensSettings ? label : taskLabel);
|
||||
toggle.setAttribute('aria-label', opensSettings ? label : taskLabel);
|
||||
@@ -1681,7 +1687,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
badge.textContent = String(runningCount > 0 ? runningCount : jobs.length);
|
||||
badge.hidden = jobs.length === 0;
|
||||
} else {
|
||||
toggle.textContent = runningCount > 0 ? `OCR ${runningCount} 处理中` : `OCR ${jobs.length}`;
|
||||
toggle.textContent = runningCount > 0 ? `索引 ${runningCount} 处理中` : `索引 ${jobs.length}`;
|
||||
}
|
||||
});
|
||||
const drawer = dock.querySelector('[data-testid="mnote-local-ocr-task-drawer"]');
|
||||
@@ -1731,7 +1737,7 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
row.setAttribute('data-mnote-local-ocr-task-row', String(job.sourceRootRelativePath || ''));
|
||||
row.setAttribute('data-mnote-local-ocr-task-status', String(job.status || ''));
|
||||
row.setAttribute('data-mnote-local-ocr-task-category', localOcrTaskCategory(job));
|
||||
const title = String(job.sourceRootRelativePath || '').split('/').filter(Boolean).pop() || 'OCR';
|
||||
const title = String(job.sourceRootRelativePath || '').split('/').filter(Boolean).pop() || '资料库索引';
|
||||
const category = localOcrTaskCategory(job);
|
||||
const progress = localOcrTaskProgress(job);
|
||||
row.innerHTML = '<div class="mnote-local-ocr-task-main"><div class="mnote-local-ocr-task-title-line"><strong></strong><em></em></div><span></span><div class="mnote-local-ocr-task-progress" role="progressbar"><i></i></div></div><div class="mnote-local-ocr-task-actions"><button type="button" data-mnote-local-ocr-task-open>打开</button><button type="button" data-mnote-local-ocr-task-retry>重试</button><button type="button" data-mnote-local-ocr-task-clear>清除</button><button type="button" data-mnote-local-ocr-task-delete>删除</button></div>';
|
||||
@@ -1766,7 +1772,7 @@ 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';
|
||||
open.hidden = job.taskKind === 'local_index' || job.taskKind === 'knowledge_rag';
|
||||
}
|
||||
const retry = row.querySelector('[data-mnote-local-ocr-task-retry]');
|
||||
if (retry instanceof HTMLButtonElement) {
|
||||
@@ -1780,7 +1786,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.taskKind === 'local_index' || !job.ocrRootRelativePath;
|
||||
deleteOcr.hidden = job.taskKind === 'local_index' || !String(job.sourceRootRelativePath || '').trim();
|
||||
}
|
||||
list.appendChild(row);
|
||||
});
|
||||
@@ -1790,13 +1796,15 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
const normalizedRoot = String(rootUri || '').trim();
|
||||
if (!normalizedRoot) return;
|
||||
localOcrTaskState.rootUri = normalizedRoot;
|
||||
const url = new URL('/api/local-folder/ocr/jobs', window.location.origin);
|
||||
const url = new URL('/api/knowledge-rag/status', window.location.origin);
|
||||
url.searchParams.set('rootUri', normalizedRoot);
|
||||
const workspaceId = String(currentWebShellWorkspaceId() || '').trim();
|
||||
if (workspaceId) url.searchParams.set('workspaceId', workspaceId);
|
||||
const response = await fetch(url.toString(), { cache: 'no-store', headers: { accept: 'application/json' } });
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok || !payload || payload.ok !== true) return;
|
||||
(Array.isArray(payload.jobs) ? payload.jobs : []).forEach((job) => {
|
||||
if (job && typeof job === 'object') job.rootUri = normalizedRoot;
|
||||
knowledgeRagRegistryEntries(payload).forEach((entry) => {
|
||||
const job = knowledgeRagJobFromRegistryEntry(entry, normalizedRoot);
|
||||
updateLocalOcrTaskState(job);
|
||||
});
|
||||
renderLocalOcrTaskDock();
|
||||
@@ -1827,74 +1835,37 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
};
|
||||
|
||||
const openLocalOcrSidecar = async (entry, job) => {
|
||||
const target = job || entry?.localOcrJob || null;
|
||||
const ocrPath = String(target?.ocrRootRelativePath || '').trim();
|
||||
if (!ocrPath || typeof openResourceInActiveTab !== 'function') return false;
|
||||
const title = ocrPath.split('/').filter(Boolean).pop() || 'OCR';
|
||||
return await openResourceInActiveTab({
|
||||
kind: 'markdown',
|
||||
title,
|
||||
path: ocrPath,
|
||||
objectIdentity: `local-ocr:${ocrPath}`,
|
||||
assetId: `local-ocr:${ocrPath}`,
|
||||
documentId: String(entry.ownerDocumentId || entry.documentId || currentWebShellDocumentId() || '').trim(),
|
||||
ownerDocumentId: String(entry.ownerDocumentId || entry.documentId || currentWebShellDocumentId() || '').trim(),
|
||||
workspaceId: String(entry.workspaceId || currentWebShellWorkspaceId() || '').trim(),
|
||||
sourceKind: 'local_folder',
|
||||
rootUri: String(entry.rootUri || '').trim(),
|
||||
resourceKind: 'markdown',
|
||||
paneRole: normalizePaneRole(entry.paneRole),
|
||||
});
|
||||
window.dispatchEvent(new CustomEvent('mnote:open-knowledge-rag-settings'));
|
||||
return false;
|
||||
};
|
||||
|
||||
const insertLocalOcrLink = async (entry, job) => {
|
||||
const target = job || entry?.localOcrJob || null;
|
||||
const ocrPath = String(target?.ocrRootRelativePath || '').trim();
|
||||
if (!ocrPath) return false;
|
||||
const response = await fetch('/api/local-folder/ocr/insert', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||||
body: JSON.stringify({
|
||||
rootUri: String(entry.rootUri || '').trim(),
|
||||
documentId: String(entry.ownerDocumentId || entry.documentId || currentWebShellDocumentId() || '').trim(),
|
||||
ocrRootRelativePath: ocrPath,
|
||||
mode: 'link',
|
||||
}),
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok || !payload || payload.ok !== true) {
|
||||
throw new Error(payload?.error?.message || `local_ocr_insert_failed_${response.status}`);
|
||||
}
|
||||
setLocalOcrStatus(entry, 'done', 'OCR 链接已插入正文', target);
|
||||
return true;
|
||||
throw new Error('OCR sidecar 已退役,请使用 LightRAG 资料库引用与问答。');
|
||||
};
|
||||
|
||||
const createLocalOcrJob = async (entry) => {
|
||||
if (!isLocalOcrSourceEntry(entry)) return null;
|
||||
const provider = localOcrProvider();
|
||||
const entryRootUri = String(entry.rootUri || '').trim();
|
||||
if (entryRootUri) localOcrTaskState.rootUri = entryRootUri;
|
||||
const startedAt = Date.now();
|
||||
const pendingJob = localOcrJobSnapshotFromEntry(entry, 'running', provider, '处理中', startedAt);
|
||||
setLocalOcrStatus(entry, 'running', 'OCR 处理中', pendingJob);
|
||||
const pendingJob = localOcrJobSnapshotFromEntry(entry, 'running', 'lightrag', '资料库索引中', startedAt);
|
||||
pendingJob.taskKind = 'knowledge_rag';
|
||||
setLocalOcrStatus(entry, 'running', '资料库索引中', pendingJob);
|
||||
updateLocalOcrTaskState(pendingJob);
|
||||
const workspaceId = String(entry.workspaceId || currentWebShellWorkspaceId() || '').trim();
|
||||
const body = {
|
||||
workspaceId,
|
||||
rootUri: String(entry.rootUri || '').trim(),
|
||||
documentId: String(entry.ownerDocumentId || entry.documentId || currentWebShellDocumentId() || '').trim(),
|
||||
sourceRootRelativePath: String(entry.path || '').trim(),
|
||||
provider,
|
||||
sources: [{ sourcePath: String(entry.path || '').trim() }],
|
||||
};
|
||||
if (provider === 'mock') {
|
||||
body.mockMarkdown = `# OCR Result\n\n${entry.title || entry.path} OCR UI smoke text`;
|
||||
}
|
||||
const response = await fetch('/api/local-folder/ocr/jobs', {
|
||||
const response = await fetch('/api/knowledge-rag/ingest', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok || !payload || payload.ok !== true) {
|
||||
const message = payload?.error?.message || payload?.message || payload?.error?.code || `local_ocr_job_failed_${response.status}`;
|
||||
const message = payload?.error?.message || payload?.message || payload?.error?.code || `knowledge_rag_ingest_failed_${response.status}`;
|
||||
const failedJob = {
|
||||
...pendingJob,
|
||||
status: 'failed',
|
||||
@@ -1907,10 +1878,22 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
updateLocalOcrTaskState(failedJob);
|
||||
throw new Error(message);
|
||||
}
|
||||
const job = payload.job && typeof payload.job === 'object' ? { ...payload.job, rootUri: entryRootUri } : null;
|
||||
const registryEntry = knowledgeRagRegistryEntries(payload).find((candidate) => {
|
||||
return String(candidate?.sourceRootRelativePath || '').trim().replace(/\\/g, '/').replace(/^\/+/, '') === String(entry.path || '').trim().replace(/\\/g, '/').replace(/^\/+/, '');
|
||||
});
|
||||
const job = knowledgeRagJobFromRegistryEntry(registryEntry, entryRootUri) || {
|
||||
...pendingJob,
|
||||
status: payload.retryRequired ? 'running' : 'done',
|
||||
stageLabel: payload.retryRequired ? 'LightRAG 忙碌,等待重试' : '资料库已提交索引',
|
||||
updatedAtMs: Date.now(),
|
||||
finishedAtMs: payload.retryRequired ? null : Date.now(),
|
||||
};
|
||||
const displayStatus = localOcrDisplayStatus(job, 'done');
|
||||
setLocalOcrStatus(entry, displayStatus, displayStatus === 'stale' ? 'OCR 需更新' : 'OCR 已完成', job);
|
||||
setLocalOcrStatus(entry, displayStatus, displayStatus === 'stale' ? '资料库需更新' : statusTextForLocalOcrJob(job), job);
|
||||
if (job) updateLocalOcrTaskState(job);
|
||||
window.dispatchEvent(new CustomEvent('mnote:knowledge-rag-source-updated', {
|
||||
detail: { rootUri: entryRootUri, workspaceId, sourceRootRelativePath: String(entry.path || '').trim(), result: payload }
|
||||
}));
|
||||
return job;
|
||||
};
|
||||
|
||||
@@ -1919,27 +1902,24 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
if (!sourcePath) return false;
|
||||
const job = localOcrTaskState.jobsBySource.get(sourcePath) || null;
|
||||
const rootUri = String(job?.rootUri || localOcrTaskState.rootUri || currentWebShellRootUri() || '').trim();
|
||||
const response = await fetch('/api/local-folder/ocr/delete', {
|
||||
const response = await fetch('/api/knowledge-rag/delete-source', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||||
body: JSON.stringify({
|
||||
rootUri,
|
||||
sourceRootRelativePath: sourcePath,
|
||||
workspaceId: String(currentWebShellWorkspaceId() || '').trim(),
|
||||
sourcePath,
|
||||
}),
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
if (!response.ok || !payload || payload.ok !== true) {
|
||||
throw new Error(payload?.error?.message || `local_ocr_delete_failed_${response.status}`);
|
||||
}
|
||||
if (payload.deleted !== true) {
|
||||
throw new Error('local_ocr_delete_noop');
|
||||
throw new Error(payload?.error?.message || `knowledge_rag_delete_failed_${response.status}`);
|
||||
}
|
||||
localOcrTaskState.jobsBySource.delete(sourcePath);
|
||||
renderLocalOcrTaskDock();
|
||||
const ocrPath = String(job?.ocrRootRelativePath || '').trim();
|
||||
if (ocrPath) {
|
||||
dispatchLocalOcrFileTreeRefresh({ ...job, status: 'stale', updatedAtMs: Date.now(), ocrRootRelativePath: ocrPath }, localOcrTaskState.rootUri);
|
||||
}
|
||||
window.dispatchEvent(new CustomEvent('mnote:knowledge-rag-source-updated', {
|
||||
detail: { rootUri, workspaceId: String(currentWebShellWorkspaceId() || '').trim(), sourceRootRelativePath: sourcePath, result: payload }
|
||||
}));
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -2118,14 +2098,14 @@ export const createResourceTabRuntime = (dependencies = {}) => {
|
||||
});
|
||||
});
|
||||
}
|
||||
setLocalOcrStatus(entry, 'idle', 'OCR 未生成', null);
|
||||
setLocalOcrStatus(entry, 'idle', '资料库未索引', null);
|
||||
ensureLocalOcrTaskDock();
|
||||
ensureLocalOcrTaskEvents(entry.rootUri);
|
||||
void loadLocalOcrJobs(entry.rootUri).catch(() => undefined);
|
||||
void readLocalOcrStatus(entry).then((job) => {
|
||||
if (!job) return;
|
||||
const displayStatus = localOcrDisplayStatus(job, 'done');
|
||||
setLocalOcrStatus(entry, displayStatus, displayStatus === 'stale' ? 'OCR 需更新' : 'OCR 已完成', job);
|
||||
setLocalOcrStatus(entry, displayStatus, displayStatus === 'stale' ? '资料库需更新' : statusTextForLocalOcrJob(job), job);
|
||||
updateLocalOcrTaskState(job);
|
||||
}).catch(() => undefined);
|
||||
};
|
||||
|
||||
@@ -421,70 +421,77 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|
||||
}
|
||||
|
||||
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', {
|
||||
var payload = await ingestKnowledgeRagForDetail(detail, trigger).catch(function(error) {
|
||||
document.documentElement.setAttribute('data-mnote-local-ocr-menu-status', 'failed');
|
||||
throw error;
|
||||
});
|
||||
var sourceRootRelativePath = localOcrSourceRelativePath(detail)
|
||||
|| String(detail && (detail.localRelativePath || detail.path || detail.title) || '').trim();
|
||||
var rootUri = localOcrRootUri(detail, trigger);
|
||||
var status = payload && payload.retryRequired ? 'retry' : 'done';
|
||||
document.documentElement.setAttribute('data-mnote-local-ocr-menu-status', status);
|
||||
document.documentElement.setAttribute('data-mnote-local-ocr-menu-path', sourceRootRelativePath);
|
||||
window.dispatchEvent(new CustomEvent('mnote:local-ocr-job-updated', {
|
||||
detail: { status: status, job: payload, rootUri: rootUri, sourceRootRelativePath: sourceRootRelativePath }
|
||||
}));
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function ingestKnowledgeRagForDetail(detail, trigger) {
|
||||
var sourceRootRelativePath = localOcrSourceRelativePath(detail)
|
||||
|| String(detail && (detail.localRelativePath || detail.path || detail.title) || '').trim();
|
||||
var rootUri = localOcrRootUri(detail, trigger);
|
||||
if (!sourceRootRelativePath || !rootUri) {
|
||||
throw new Error('缺少资料库来源或 rootUri');
|
||||
}
|
||||
var workspaceId = String(detail && detail.workspaceId || resolveWorkspaceId(document.body) || '').trim();
|
||||
document.documentElement.setAttribute('data-mnote-knowledge-rag-menu-status', 'running');
|
||||
document.documentElement.setAttribute('data-mnote-knowledge-rag-menu-path', sourceRootRelativePath);
|
||||
var response = await fetch('/api/knowledge-rag/ingest', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
body: JSON.stringify({
|
||||
workspaceId: workspaceId,
|
||||
rootUri: rootUri,
|
||||
sources: [{ sourcePath: sourceRootRelativePath }]
|
||||
})
|
||||
});
|
||||
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');
|
||||
if (!response.ok || !payload || payload.ok === false) {
|
||||
var message = payload && payload.message ? payload.message : '资料库索引失败';
|
||||
document.documentElement.setAttribute('data-mnote-knowledge-rag-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 }
|
||||
document.documentElement.setAttribute('data-mnote-knowledge-rag-menu-status', payload.retryRequired ? 'retry' : 'done');
|
||||
window.dispatchEvent(new CustomEvent('mnote:knowledge-rag-source-updated', {
|
||||
detail: { rootUri: rootUri, workspaceId: workspaceId, sourceRootRelativePath: sourceRootRelativePath, result: payload }
|
||||
}));
|
||||
return job;
|
||||
return payload;
|
||||
}
|
||||
|
||||
function handleTreeContextMenuAction(action, detail, trigger) {
|
||||
closeTreeContextMenu();
|
||||
detail = detail || {};
|
||||
if (action === 'local-ocr') {
|
||||
recordFileTreeAction('local-ocr', detail);
|
||||
recordFileTreeAction('knowledge-rag-index', detail);
|
||||
recordFileTreeActionStatus('pending', detail);
|
||||
void runLocalOcrForDetail(detail, trigger).then(function(job) {
|
||||
recordFileTreeActionStatus(String(job && job.status || 'done'), detail);
|
||||
recordFileTreeActionStatus(job && job.retryRequired ? 'retry' : 'done', detail);
|
||||
}).catch(function(error) {
|
||||
recordFileTreeActionStatus('failed', Object.assign({}, detail, { fallback: 'alert' }));
|
||||
window.alert(error && error.message ? error.message : 'OCR 生成失败');
|
||||
window.alert(error && error.message ? error.message : '资料库索引失败');
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (action === 'knowledge-rag-index') {
|
||||
recordFileTreeAction('knowledge-rag-index', detail);
|
||||
recordFileTreeActionStatus('pending', detail);
|
||||
void ingestKnowledgeRagForDetail(detail, trigger).then(function() {
|
||||
recordFileTreeActionStatus('done', detail);
|
||||
}).catch(function(error) {
|
||||
recordFileTreeActionStatus('failed', Object.assign({}, detail, { fallback: 'alert' }));
|
||||
window.alert(error && error.message ? error.message : '资料库索引失败');
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -1010,10 +1017,10 @@ 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);
|
||||
if (currentSourceKind() === 'local_folder' && detail.rowKind !== 'index') {
|
||||
var ragItem = { action: 'knowledge-rag-index', icon: 'travel_explore', label: '加入资料库索引', when: '!workspace.readonly' };
|
||||
var insertAt = isAttachment ? 11 : isAsset ? 3 : 3;
|
||||
if (insertAt >= 0) items.splice(insertAt, 0, ragItem);
|
||||
}
|
||||
items.forEach(function(item) {
|
||||
if (item.when !== undefined && !evaluateSidebarFileTreeWhen(ctx, item.when)) {
|
||||
|
||||
@@ -443,43 +443,7 @@ export function createSidebarPageAiTargetRuntime(context) {
|
||||
}
|
||||
|
||||
async function fetchPageAiOcrSidecarContext(editorTarget) {
|
||||
if (currentSourceKind() !== 'local_folder') return null;
|
||||
var target = editorTarget && typeof editorTarget === 'object' ? editorTarget : {};
|
||||
var workspacePath = target.workspacePath && typeof target.workspacePath === 'object' ? target.workspacePath : {};
|
||||
var relativePath = String(workspacePath.relativePath || target.path || '').trim();
|
||||
var resourceKind = String(target.resourceKind || target.editorKind || workspacePath.resourceKind || '').trim();
|
||||
if (!relativePath || !pageAiOcrEligiblePath(relativePath) || !pageAiOcrEligibleResourceKind(resourceKind)) return null;
|
||||
var rootUri = String(workspacePath.rootUri || currentRootUri() || '').trim();
|
||||
if (!rootUri) return null;
|
||||
try {
|
||||
var statusUrl = new URL('/api/local-folder/ocr/status', window.location.origin);
|
||||
statusUrl.searchParams.set('rootUri', rootUri);
|
||||
statusUrl.searchParams.set('sourceRootRelativePath', relativePath);
|
||||
var statusResponse = await fetch(statusUrl.toString(), { cache: 'no-store', headers: { accept: 'application/json' } });
|
||||
var statusPayload = await statusResponse.json().catch(function() { return null; });
|
||||
var job = statusResponse.ok && statusPayload && statusPayload.ok === true ? statusPayload.job : null;
|
||||
var ocrPath = String(job && job.ocrRootRelativePath || '').trim();
|
||||
if (!ocrPath || String(job.status || '') === 'failed') return null;
|
||||
var readUrl = new URL('/api/local-folder/ocr/read', window.location.origin);
|
||||
readUrl.searchParams.set('rootUri', rootUri);
|
||||
readUrl.searchParams.set('ocrRootRelativePath', ocrPath);
|
||||
var readResponse = await fetch(readUrl.toString(), { cache: 'no-store', headers: { accept: 'application/json' } });
|
||||
var readPayload = await readResponse.json().catch(function() { return null; });
|
||||
if (!readResponse.ok || !readPayload || readPayload.ok !== true) return null;
|
||||
return {
|
||||
schema: 'mnote.local_ocr_context.v1',
|
||||
source: 'local_ocr_sidecar',
|
||||
sourceRootRelativePath: relativePath,
|
||||
ocrRootRelativePath: ocrPath,
|
||||
status: String(job.status || ''),
|
||||
stale: job.stale === true,
|
||||
provider: String(job.provider || ''),
|
||||
modelVersion: String(job.modelVersion || ''),
|
||||
plainTextPreview: pageAiOcrBodyPreview(readPayload.markdown || ''),
|
||||
};
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function pageAiEnrichOcrContextRefs(contextRefs, agentTargetPackage, editorTarget) {
|
||||
|
||||
@@ -71,6 +71,15 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
return Object.assign({ 'localOcr.autoEnabled': false }, pageUiState.localOcrPreferences || {});
|
||||
}
|
||||
|
||||
function currentKnowledgeRagSummary() {
|
||||
return pageUiState.knowledgeRagSummary || {};
|
||||
}
|
||||
|
||||
var knowledgeRagBridgeTimer = 0;
|
||||
var knowledgeRagBridgeGeneration = 0;
|
||||
var knowledgeRagBridgeAttempt = 0;
|
||||
var knowledgeRagBridgeDelays = [1200, 2000, 3200, 5000, 8000, 13000, 21000, 34000];
|
||||
|
||||
function pageWidthModeLabel(mode) {
|
||||
if (mode === 'inherit') return '继承默认';
|
||||
if (mode === 'readable') return '阅读';
|
||||
@@ -337,6 +346,12 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
ocrTrigger.setAttribute('data-state', ocrOpen ? 'open' : 'closed');
|
||||
ocrTrigger.setAttribute('aria-expanded', ocrOpen ? 'true' : 'false');
|
||||
}
|
||||
var ragTrigger = document.querySelector('[data-testid="mnote-knowledge-rag-settings-toggle"]');
|
||||
var ragOpen = isKnowledgeRagSettingsOpen();
|
||||
if (ragTrigger instanceof HTMLElement) {
|
||||
ragTrigger.setAttribute('data-state', ragOpen ? 'open' : 'closed');
|
||||
ragTrigger.setAttribute('aria-expanded', ragOpen ? 'true' : 'false');
|
||||
}
|
||||
}
|
||||
|
||||
function createPageOptionRow(key, type) {
|
||||
@@ -380,8 +395,8 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
return '' +
|
||||
'<label class="wolai-page-setting-row" data-page-setting-row="localOcrAutoEnabled">' +
|
||||
'<span class="wolai-page-setting-copy">' +
|
||||
'<span class="wolai-page-setting-label">自动 OCR</span>' +
|
||||
'<span class="wolai-page-setting-hint">默认关闭;仅作为搜索和 AI 索引补充处理图片与图片型 PDF</span>' +
|
||||
'<span class="wolai-page-setting-label">资料库自动索引</span>' +
|
||||
'<span class="wolai-page-setting-hint">LiteParse/OCR sidecar 已退役;图片、PDF、Office 统一由 LightRAG 资料库处理</span>' +
|
||||
'</span>' +
|
||||
'<input type="checkbox" class="wolai-page-setting-checkbox" data-local-ocr-option-checkbox="autoEnabled" />' +
|
||||
'</label>';
|
||||
@@ -659,27 +674,53 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
}
|
||||
|
||||
function ensureLocalOcrSettingsPopover() {
|
||||
var existing = document.querySelector('[data-testid="mnote-local-ocr-settings-popover"]');
|
||||
if (existing instanceof HTMLElement) return existing;
|
||||
var popover = document.createElement('div');
|
||||
popover.className = 'wolai-page-settings-popover mnote-local-ocr-settings-popover';
|
||||
popover.setAttribute('data-testid', 'mnote-local-ocr-settings-popover');
|
||||
popover.setAttribute('data-mnote-surface', 'local-ocr-settings');
|
||||
popover.hidden = true;
|
||||
popover.innerHTML = '' +
|
||||
'<div class="wolai-page-settings-panel mnote-settings-panel mnote-local-ocr-settings-panel" role="dialog" aria-modal="false" aria-label="OCR 设置">' +
|
||||
return ensureKnowledgeRagSettingsPopover();
|
||||
}
|
||||
|
||||
function createKnowledgeRagSettingsPanelHtml() {
|
||||
return '' +
|
||||
'<div class="wolai-page-settings-panel mnote-settings-panel mnote-knowledge-rag-settings-panel" role="dialog" aria-modal="false" aria-label="资料库问答设置">' +
|
||||
'<div class="mnote-settings-panel-head">' +
|
||||
'<strong>OCR 设置</strong>' +
|
||||
'<button type="button" class="mnote-settings-panel-close" data-settings-action="close" aria-label="关闭 OCR 设置">×</button>' +
|
||||
'<strong>资料库问答</strong>' +
|
||||
'<button type="button" class="mnote-settings-panel-close" data-settings-action="close" aria-label="关闭资料库问答设置">×</button>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-settings-section">' +
|
||||
createLocalOcrAutoRow() +
|
||||
'<div class="wolai-page-settings-index-actions">' +
|
||||
'<button type="button" data-local-ocr-settings-action="run-active">识别当前资源</button>' +
|
||||
'<button type="button" data-local-ocr-settings-action="tasks">查看 OCR 任务</button>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-settings-index-panel" data-testid="mnote-knowledge-rag-settings-panel">' +
|
||||
'<div class="wolai-page-settings-index-status" data-testid="mnote-knowledge-rag-status"></div>' +
|
||||
'<div class="mnote-knowledge-rag-meta" data-testid="mnote-knowledge-rag-meta"></div>' +
|
||||
'<section class="wolai-page-settings-index-group mnote-knowledge-rag-control-group">' +
|
||||
'<div class="wolai-page-settings-index-title">资料操作</div>' +
|
||||
'<div class="mnote-knowledge-rag-source-inputs" data-testid="mnote-knowledge-rag-source-inputs">' +
|
||||
knowledgeRagSourceInputRow('') +
|
||||
'</div>' +
|
||||
'<div class="mnote-knowledge-rag-source-tools">' +
|
||||
'<button type="button" class="wolai-page-settings-index-add" data-knowledge-rag-action="add-source">新增来源</button>' +
|
||||
'<button type="button" class="wolai-page-settings-index-add" data-knowledge-rag-action="use-filetree-selection">使用选中文件树项</button>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-settings-index-actions">' +
|
||||
'<button type="button" data-knowledge-rag-action="refresh">刷新状态</button>' +
|
||||
'<button type="button" data-knowledge-rag-action="ingest">索引来源</button>' +
|
||||
'<button type="button" data-knowledge-rag-action="prune">清除失效记录</button>' +
|
||||
'<button type="button" data-knowledge-rag-action="open-dashboard">打开 LightRAG</button>' +
|
||||
'</div>' +
|
||||
'</section>' +
|
||||
'<section class="wolai-page-settings-index-group">' +
|
||||
'<div class="wolai-page-settings-index-title">资料来源</div>' +
|
||||
'<div class="mnote-knowledge-rag-source-filters" data-testid="mnote-knowledge-rag-source-filters"></div>' +
|
||||
'<div class="mnote-knowledge-rag-source-list" data-testid="mnote-knowledge-rag-sources"></div>' +
|
||||
'</section>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function ensureKnowledgeRagSettingsPopover() {
|
||||
var existing = document.querySelector('[data-testid="mnote-knowledge-rag-settings-popover"]');
|
||||
if (existing instanceof HTMLElement) return existing;
|
||||
var popover = document.createElement('div');
|
||||
popover.className = 'wolai-page-settings-popover mnote-knowledge-rag-settings-popover';
|
||||
popover.setAttribute('data-testid', 'mnote-knowledge-rag-settings-popover');
|
||||
popover.setAttribute('data-mnote-surface', 'knowledge-rag-settings');
|
||||
popover.hidden = true;
|
||||
popover.innerHTML = createKnowledgeRagSettingsPanelHtml();
|
||||
document.body.appendChild(popover);
|
||||
return popover;
|
||||
}
|
||||
@@ -1015,6 +1056,656 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
}
|
||||
}
|
||||
|
||||
function knowledgeRagScopeKey() {
|
||||
return [
|
||||
currentSourceKind(),
|
||||
currentRootUri(),
|
||||
resolveWorkspaceId(document.body)
|
||||
].join('|');
|
||||
}
|
||||
|
||||
function knowledgeRagIsAvailable() {
|
||||
return currentSourceKind() === 'local_folder' && Boolean(currentRootUri());
|
||||
}
|
||||
|
||||
function knowledgeRagSourceInputRow(value) {
|
||||
return '' +
|
||||
'<div class="mnote-knowledge-rag-source-input-row" data-knowledge-rag-source-input-row="true">' +
|
||||
'<input type="text" class="wolai-page-settings-index-path" data-knowledge-rag-source-input="true" value="' + escapeHtml(value || '') + '" placeholder="文件或目录,例如 docs/book.pdf / docs" spellcheck="false" />' +
|
||||
'<span class="mnote-knowledge-rag-input-status" data-knowledge-rag-input-status-label="true" hidden></span>' +
|
||||
'<button type="button" class="wolai-page-settings-index-remove" data-knowledge-rag-action="remove-source" aria-label="删除资料来源">×</button>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function knowledgeRagSourceInputValues() {
|
||||
var popover = ensureKnowledgeRagSettingsPopover();
|
||||
return Array.from(popover.querySelectorAll('[data-knowledge-rag-source-input]')).map(function(input) {
|
||||
return input instanceof HTMLInputElement ? input.value.trim() : '';
|
||||
}).filter(Boolean).filter(function(value, index, list) {
|
||||
return list.indexOf(value) === index;
|
||||
});
|
||||
}
|
||||
|
||||
function knowledgeRagCurrentFilter() {
|
||||
return pageUiState.knowledgeRagSourceFilter || 'attention';
|
||||
}
|
||||
|
||||
function setKnowledgeRagSourceFilter(filter) {
|
||||
pageUiState.knowledgeRagSourceFilter = String(filter || 'attention').trim() || 'attention';
|
||||
renderKnowledgeRagSettings();
|
||||
}
|
||||
|
||||
function knowledgeRagRegistryEntryCategory(entry) {
|
||||
var deleted = entry && entry.deletedAtMs;
|
||||
var stale = entry && entry.stale;
|
||||
var providerStatus = String(entry && entry.lightRagStatus || '').trim();
|
||||
var failed = providerStatus === 'failed';
|
||||
var deleting = providerStatus === 'delete_submitted';
|
||||
var deletedDone = providerStatus === 'delete_completed';
|
||||
var docId = String(entry && entry.lightRagDocId || '');
|
||||
var indexed = entry && entry.indexedAtMs && docId && !deleted && !stale && !failed && !deleting && !deletedDone;
|
||||
if (deletedDone) return 'removed';
|
||||
if (failed || stale || deleted) return 'failed';
|
||||
if (indexed) return 'indexed';
|
||||
return 'indexing';
|
||||
}
|
||||
|
||||
function knowledgeRagProviderDocuments(status) {
|
||||
var documents = status && status.documents && typeof status.documents === 'object' ? status.documents : {};
|
||||
return Array.isArray(documents.documents) ? documents.documents : [];
|
||||
}
|
||||
|
||||
function knowledgeRagStatusHasInFlight(status) {
|
||||
var registry = status && status.registry && typeof status.registry === 'object' ? status.registry : {};
|
||||
var entries = Array.isArray(registry.entries) ? registry.entries : [];
|
||||
if (entries.some(function(entry) {
|
||||
var providerStatus = String(entry && entry.lightRagStatus || '').trim();
|
||||
return !entry?.deletedAtMs && !entry?.stale && !(entry?.indexedAtMs && entry?.lightRagDocId)
|
||||
&& ['submitted', 'pending', 'processing', 'parsed', ''].includes(providerStatus);
|
||||
})) return true;
|
||||
var groups = status && status.documents && status.documents.rawStatusGroups && typeof status.documents.rawStatusGroups === 'object'
|
||||
? status.documents.rawStatusGroups
|
||||
: {};
|
||||
return ['pending', 'parse', 'parsed', 'analyze', 'processing', 'process'].some(function(key) {
|
||||
return Number(groups[key] || 0) > 0;
|
||||
});
|
||||
}
|
||||
|
||||
function scheduleKnowledgeRagStatusBridge(reason) {
|
||||
if (!knowledgeRagIsAvailable()) return;
|
||||
if (knowledgeRagBridgeTimer) window.clearTimeout(knowledgeRagBridgeTimer);
|
||||
knowledgeRagBridgeGeneration += 1;
|
||||
knowledgeRagBridgeAttempt = 0;
|
||||
runKnowledgeRagStatusBridge(knowledgeRagBridgeGeneration, reason || 'source-updated');
|
||||
}
|
||||
|
||||
function runKnowledgeRagStatusBridge(generation, reason) {
|
||||
if (generation !== knowledgeRagBridgeGeneration || !knowledgeRagIsAvailable()) return;
|
||||
var delay = knowledgeRagBridgeDelays[Math.min(knowledgeRagBridgeAttempt, knowledgeRagBridgeDelays.length - 1)];
|
||||
knowledgeRagBridgeTimer = window.setTimeout(async function() {
|
||||
if (generation !== knowledgeRagBridgeGeneration || !knowledgeRagIsAvailable()) return;
|
||||
knowledgeRagBridgeAttempt += 1;
|
||||
await loadKnowledgeRagStatus(true, { silentLoading: true, notifyUpdated: true, reason: reason || 'bridge' });
|
||||
var status = currentKnowledgeRagSummary().status || {};
|
||||
if (knowledgeRagStatusHasInFlight(status) && knowledgeRagBridgeAttempt < knowledgeRagBridgeDelays.length) {
|
||||
runKnowledgeRagStatusBridge(generation, reason || 'bridge');
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
|
||||
function knowledgeRagUnmappedProviderRows(status, entries) {
|
||||
var docs = knowledgeRagProviderDocuments(status);
|
||||
if (!docs.length) return [];
|
||||
var mappedDocIds = new Set();
|
||||
var mappedFilePaths = new Set();
|
||||
(Array.isArray(entries) ? entries : []).forEach(function(entry) {
|
||||
var docId = String(entry && entry.lightRagDocId || '').trim();
|
||||
var filePath = String(entry && entry.lightRagFilePath || '').trim();
|
||||
if (docId) mappedDocIds.add(docId);
|
||||
if (filePath) mappedFilePaths.add(filePath);
|
||||
});
|
||||
return docs.filter(function(doc) {
|
||||
var docId = String(doc && doc.id || '').trim();
|
||||
var filePath = String(doc && doc.filePath || '').trim();
|
||||
return !(docId && mappedDocIds.has(docId)) && !(filePath && mappedFilePaths.has(filePath));
|
||||
}).map(function(doc) {
|
||||
return {
|
||||
kind: 'provider',
|
||||
category: String(doc.status || doc.statusGroup || '').toLowerCase().includes('fail') ? 'unmapped_failed' : 'unmapped',
|
||||
doc: doc
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function knowledgeRagVisibleForFilter(row, filter) {
|
||||
if (filter === 'all') return true;
|
||||
if (filter === 'attention') return row.category !== 'indexed';
|
||||
if (filter === 'unmapped') return row.category === 'unmapped' || row.category === 'unmapped_failed';
|
||||
if (filter === 'failed') return row.category === 'failed' || row.category === 'unmapped_failed';
|
||||
return row.category === filter;
|
||||
}
|
||||
|
||||
function renderKnowledgeRagSourceFilters(popover, rows) {
|
||||
var filtersNode = popover.querySelector('[data-testid="mnote-knowledge-rag-source-filters"]');
|
||||
if (!(filtersNode instanceof HTMLElement)) return;
|
||||
var counts = rows.reduce(function(acc, row) {
|
||||
acc.all += 1;
|
||||
if (row.category !== 'indexed') acc.attention += 1;
|
||||
if (row.category === 'indexing') acc.indexing += 1;
|
||||
if (row.category === 'failed' || row.category === 'unmapped_failed') acc.failed += 1;
|
||||
if (row.category === 'indexed') acc.indexed += 1;
|
||||
if (row.category === 'removed') acc.removed += 1;
|
||||
if (row.category === 'unmapped' || row.category === 'unmapped_failed') acc.unmapped += 1;
|
||||
return acc;
|
||||
}, { all: 0, attention: 0, indexing: 0, failed: 0, indexed: 0, removed: 0, unmapped: 0 });
|
||||
var current = knowledgeRagCurrentFilter();
|
||||
var options = [
|
||||
['attention', '需处理'],
|
||||
['indexing', '索引中'],
|
||||
['failed', '失败/过期'],
|
||||
['indexed', '已索引'],
|
||||
['removed', '已移除'],
|
||||
['unmapped', 'LightRAG 未映射'],
|
||||
['all', '全部']
|
||||
];
|
||||
filtersNode.innerHTML = options.map(function(item) {
|
||||
var key = item[0];
|
||||
var label = item[1];
|
||||
return '<button type="button" data-knowledge-rag-action="filter-sources" data-knowledge-rag-filter="' + key + '" data-active="' + (current === key ? 'true' : 'false') + '">' + escapeHtml(label) + ' <span>' + String(counts[key] || 0) + '</span></button>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function renderKnowledgeRagSources(popover, entries, status) {
|
||||
var sourcesNode = popover.querySelector('[data-testid="mnote-knowledge-rag-sources"]');
|
||||
if (!(sourcesNode instanceof HTMLElement)) return;
|
||||
var registryRows = (Array.isArray(entries) ? entries : []).map(function(entry) {
|
||||
return { kind: 'registry', category: knowledgeRagRegistryEntryCategory(entry), entry: entry };
|
||||
});
|
||||
var rows = registryRows.concat(knowledgeRagUnmappedProviderRows(status || {}, entries));
|
||||
renderKnowledgeRagSourceFilters(popover, rows);
|
||||
var filter = knowledgeRagCurrentFilter();
|
||||
var items = rows.filter(function(row) {
|
||||
return knowledgeRagVisibleForFilter(row, filter);
|
||||
});
|
||||
if (!rows.length) {
|
||||
sourcesNode.innerHTML = '<div class="wolai-page-settings-index-empty">还没有登记到 LightRAG 的资料来源</div>';
|
||||
return;
|
||||
}
|
||||
if (!items.length) {
|
||||
sourcesNode.innerHTML = '<div class="wolai-page-settings-index-empty">当前分类没有资料来源</div>';
|
||||
return;
|
||||
}
|
||||
sourcesNode.innerHTML = items.map(function(row) {
|
||||
if (row.kind === 'provider') {
|
||||
var doc = row.doc || {};
|
||||
var providerPath = String(doc.filePath || doc.id || '');
|
||||
var docStatus = String(doc.status || doc.statusGroup || '');
|
||||
var failedDoc = row.category === 'unmapped_failed';
|
||||
return '' +
|
||||
'<div class="mnote-knowledge-rag-source-row" data-knowledge-rag-source-row="true" data-knowledge-rag-source-kind="provider" data-knowledge-rag-source-path="' + escapeHtml(providerPath) + '">' +
|
||||
'<span class="wolai-page-settings-index-status-dot" data-index-status="' + (failedDoc ? 'fault' : 'indexed') + '" title="' + escapeHtml(docStatus || 'LightRAG 文档') + '"></span>' +
|
||||
'<div class="mnote-knowledge-rag-source-main">' +
|
||||
'<strong title="' + escapeHtml(providerPath) + '">' + escapeHtml(providerPath || 'LightRAG 文档') + '</strong>' +
|
||||
'<span>' + escapeHtml('LightRAG 未映射 · ' + (docStatus || 'unknown') + (doc.id ? ' · ' + doc.id : '')) + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="mnote-knowledge-rag-source-actions"><button type="button" disabled>只读</button></div>' +
|
||||
'</div>';
|
||||
}
|
||||
var entry = row.entry || {};
|
||||
var deleted = entry && entry.deletedAtMs;
|
||||
var stale = entry && entry.stale;
|
||||
var providerStatus = String(entry && entry.lightRagStatus || '').trim();
|
||||
var failed = providerStatus === 'failed';
|
||||
var deleting = providerStatus === 'delete_submitted';
|
||||
var deletedDone = providerStatus === 'delete_completed';
|
||||
var docId = String(entry && entry.lightRagDocId || '');
|
||||
var indexed = entry && entry.indexedAtMs && docId && !deleted && !stale && !failed && !deleting && !deletedDone;
|
||||
var kind = deletedDone ? '' : (deleting ? 'indexing' : (failed || stale || deleted ? 'fault' : (indexed ? 'indexed' : 'indexing')));
|
||||
var title = String(entry && entry.sourceRootRelativePath || entry && entry.lightRagFilePath || '');
|
||||
var status = deletedDone ? 'LightRAG 已移除'
|
||||
: deleting ? '删除已提交'
|
||||
: deleted ? '已删除'
|
||||
: stale ? '已过期'
|
||||
: failed ? '索引失败'
|
||||
: indexed ? '已索引'
|
||||
: (docId || providerStatus ? '正在索引' : '待提交');
|
||||
var meta = docId ? status + ' · ' + docId : status;
|
||||
var sourcePath = String(entry && entry.sourceRootRelativePath || '');
|
||||
var canReindex = Boolean(sourcePath && !deleting);
|
||||
var canDelete = Boolean(sourcePath && !deleted && !deletedDone && !deleting);
|
||||
return '' +
|
||||
'<div class="mnote-knowledge-rag-source-row" data-knowledge-rag-source-row="true" data-knowledge-rag-source-path="' + escapeHtml(sourcePath) + '">' +
|
||||
'<span class="wolai-page-settings-index-status-dot" data-index-status="' + kind + '" title="' + escapeHtml(status) + '"></span>' +
|
||||
'<div class="mnote-knowledge-rag-source-main">' +
|
||||
'<strong title="' + escapeHtml(title) + '">' + escapeHtml(title || '未命名来源') + '</strong>' +
|
||||
'<span>' + escapeHtml(meta) + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="mnote-knowledge-rag-source-actions">' +
|
||||
'<button type="button" data-knowledge-rag-action="reindex-source" data-knowledge-rag-source-path="' + escapeHtml(sourcePath) + '"' + (canReindex ? '' : ' disabled') + '>重索引</button>' +
|
||||
'<button type="button" data-knowledge-rag-action="delete-source" data-knowledge-rag-source-path="' + escapeHtml(sourcePath) + '"' + (canDelete ? '' : ' disabled') + '>删除索引</button>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function renderKnowledgeRagSettings(popover) {
|
||||
popover = popover || ensureKnowledgeRagSettingsPopover();
|
||||
var statusNode = popover.querySelector('[data-testid="mnote-knowledge-rag-status"]');
|
||||
var metaNode = popover.querySelector('[data-testid="mnote-knowledge-rag-meta"]');
|
||||
var dashboardButton = popover.querySelector('[data-knowledge-rag-action="open-dashboard"]');
|
||||
if (!(statusNode instanceof HTMLElement)) return;
|
||||
|
||||
if (!knowledgeRagIsAvailable()) {
|
||||
statusNode.textContent = '资料库问答仅在本地工作区可用';
|
||||
if (metaNode instanceof HTMLElement) metaNode.innerHTML = '';
|
||||
renderKnowledgeRagSourceFilters(popover, []);
|
||||
renderKnowledgeRagSources(popover, [], {});
|
||||
if (dashboardButton instanceof HTMLButtonElement) dashboardButton.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var summary = currentKnowledgeRagSummary();
|
||||
if (summary.loading) {
|
||||
statusNode.textContent = '正在读取 LightRAG 状态...';
|
||||
if (metaNode instanceof HTMLElement) metaNode.innerHTML = '';
|
||||
renderKnowledgeRagSourceFilters(popover, []);
|
||||
renderKnowledgeRagSources(popover, [], {});
|
||||
if (dashboardButton instanceof HTMLButtonElement) dashboardButton.disabled = true;
|
||||
return;
|
||||
}
|
||||
if (summary.error) {
|
||||
statusNode.textContent = 'LightRAG 状态读取失败:' + summary.error;
|
||||
if (metaNode instanceof HTMLElement) metaNode.innerHTML = '';
|
||||
renderKnowledgeRagSourceFilters(popover, []);
|
||||
renderKnowledgeRagSources(popover, [], {});
|
||||
if (dashboardButton instanceof HTMLButtonElement) dashboardButton.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var status = summary.status || {};
|
||||
var health = status.health && typeof status.health === 'object' ? status.health : {};
|
||||
var healthBody = health.health && typeof health.health === 'object' ? health.health : {};
|
||||
var healthy = health.ok === true && String(healthBody.status || '') === 'healthy';
|
||||
var registry = status.registry && typeof status.registry === 'object' ? status.registry : {};
|
||||
var entries = Array.isArray(registry.entries) ? registry.entries : [];
|
||||
var providerDocs = knowledgeRagProviderDocuments(status);
|
||||
statusNode.textContent = healthy
|
||||
? 'LightRAG healthy,MNote 登记 ' + entries.length + ' 个来源,LightRAG 文档 ' + providerDocs.length + ' 个'
|
||||
: 'LightRAG 未就绪';
|
||||
if (metaNode instanceof HTMLElement) {
|
||||
var dashboardUrl = publicKnowledgeRagDashboardUrl(status);
|
||||
var inputDir = String(status.inputDir || '');
|
||||
var workingDir = String(healthBody.working_directory || '');
|
||||
var config = healthBody.configuration && typeof healthBody.configuration === 'object' ? healthBody.configuration : {};
|
||||
var llmModel = String(config.llm_model || config.llmModel || '');
|
||||
var vlmModel = String(config.vlm_model || config.vlmModel || '');
|
||||
metaNode.innerHTML = '' +
|
||||
'<div><span>Dashboard</span><code>' + escapeHtml(dashboardUrl || '未配置') + '</code></div>' +
|
||||
'<div><span>Model</span><code>' + escapeHtml(llmModel || '未上报') + (vlmModel ? ' / VLM ' + escapeHtml(vlmModel) : '') + '</code></div>' +
|
||||
'<div><span>Input</span><code>' + escapeHtml(inputDir || '未配置') + '</code></div>' +
|
||||
'<div><span>Storage</span><code>' + escapeHtml(workingDir || '未上报') + '</code></div>';
|
||||
}
|
||||
renderKnowledgeRagSources(popover, entries, status);
|
||||
if (dashboardButton instanceof HTMLButtonElement) dashboardButton.disabled = !publicKnowledgeRagDashboardUrl(status);
|
||||
}
|
||||
|
||||
function publicKnowledgeRagDashboardUrl(status) {
|
||||
var url = String(status && (status.dashboardUrl || status.endpoint) || '').trim();
|
||||
if (!url) return '';
|
||||
try {
|
||||
var parsed = new URL(url, window.location.href);
|
||||
if (['127.0.0.1', 'localhost', '::1'].includes(parsed.hostname) && window.location.hostname && !['127.0.0.1', 'localhost', '::1'].includes(window.location.hostname)) {
|
||||
parsed.hostname = window.location.hostname;
|
||||
return parsed.toString();
|
||||
}
|
||||
} catch (_) {}
|
||||
return url;
|
||||
}
|
||||
|
||||
async function loadKnowledgeRagStatus(force, options) {
|
||||
options = options || {};
|
||||
if (!knowledgeRagIsAvailable()) {
|
||||
renderKnowledgeRagSettings();
|
||||
return;
|
||||
}
|
||||
var scopeKey = knowledgeRagScopeKey();
|
||||
var current = currentKnowledgeRagSummary();
|
||||
if (!force && current.scopeKey === scopeKey && !current.error && !current.loading) {
|
||||
renderKnowledgeRagSettings();
|
||||
return;
|
||||
}
|
||||
if (!options.silentLoading) {
|
||||
pageUiState.knowledgeRagSummary = {
|
||||
scopeKey: scopeKey,
|
||||
loading: true,
|
||||
error: ''
|
||||
};
|
||||
renderKnowledgeRagSettings();
|
||||
}
|
||||
try {
|
||||
var params = new URLSearchParams();
|
||||
params.set('workspaceId', resolveWorkspaceId(document.body));
|
||||
params.set('rootUri', currentRootUri());
|
||||
var response = await fetch('/api/knowledge-rag/status?' + params.toString(), {
|
||||
headers: { accept: 'application/json' }
|
||||
});
|
||||
var payload = await response.json().catch(function(){ return null; });
|
||||
if (!response.ok || !payload || payload.ok !== true) {
|
||||
throw new Error(payload && payload.message ? payload.message : 'knowledge_rag_status_' + response.status);
|
||||
}
|
||||
pageUiState.knowledgeRagSummary = {
|
||||
scopeKey: scopeKey,
|
||||
loading: false,
|
||||
error: '',
|
||||
status: payload
|
||||
};
|
||||
if (options.notifyUpdated) {
|
||||
notifyKnowledgeRagSourceUpdated({ action: 'status-sync', reason: options.reason || 'bridge', status: payload });
|
||||
}
|
||||
} catch (error) {
|
||||
pageUiState.knowledgeRagSummary = {
|
||||
scopeKey: scopeKey,
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
status: {}
|
||||
};
|
||||
}
|
||||
renderKnowledgeRagSettings();
|
||||
}
|
||||
|
||||
function addKnowledgeRagSourceInput() {
|
||||
var popover = ensureKnowledgeRagSettingsPopover();
|
||||
var node = popover.querySelector('[data-testid="mnote-knowledge-rag-source-inputs"]');
|
||||
if (!(node instanceof HTMLElement)) return;
|
||||
node.insertAdjacentHTML('beforeend', knowledgeRagSourceInputRow(''));
|
||||
var inputs = Array.from(node.querySelectorAll('[data-knowledge-rag-source-input]'));
|
||||
var last = inputs[inputs.length - 1];
|
||||
if (last instanceof HTMLInputElement) last.focus();
|
||||
}
|
||||
|
||||
function knowledgeRagFileTreeSelectionPaths() {
|
||||
var rows = Array.from(document.querySelectorAll(
|
||||
'#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-selected="true"][data-local-relative-path]'
|
||||
));
|
||||
if (!rows.length) {
|
||||
rows = Array.from(document.querySelectorAll(
|
||||
'#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-active="true"][data-local-relative-path]'
|
||||
));
|
||||
}
|
||||
return rows.map(function(row) {
|
||||
return String(row.getAttribute('data-local-relative-path') || '').trim();
|
||||
}).filter(Boolean).filter(function(value, index, list) {
|
||||
return list.indexOf(value) === index;
|
||||
});
|
||||
}
|
||||
|
||||
function useKnowledgeRagFileTreeSelection() {
|
||||
var selectedPaths = knowledgeRagFileTreeSelectionPaths();
|
||||
if (!selectedPaths.length) {
|
||||
pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
|
||||
scopeKey: knowledgeRagScopeKey(),
|
||||
loading: false,
|
||||
error: '请先在文件树中选中要索引的文件或目录'
|
||||
});
|
||||
renderKnowledgeRagSettings();
|
||||
return;
|
||||
}
|
||||
var popover = ensureKnowledgeRagSettingsPopover();
|
||||
var node = popover.querySelector('[data-testid="mnote-knowledge-rag-source-inputs"]');
|
||||
if (!(node instanceof HTMLElement)) return;
|
||||
var existing = knowledgeRagSourceInputValues();
|
||||
var next = existing.concat(selectedPaths).filter(function(value, index, list) {
|
||||
return list.indexOf(value) === index;
|
||||
});
|
||||
node.innerHTML = next.map(knowledgeRagSourceInputRow).join('');
|
||||
}
|
||||
|
||||
function notifyKnowledgeRagSourceUpdated(detail) {
|
||||
window.dispatchEvent(new CustomEvent('mnote:knowledge-rag-source-updated', {
|
||||
detail: Object.assign({
|
||||
rootUri: currentRootUri(),
|
||||
workspaceId: resolveWorkspaceId(document.body)
|
||||
}, detail || {})
|
||||
}));
|
||||
}
|
||||
|
||||
function removeKnowledgeRagSourceInput(button) {
|
||||
var row = button && button.closest ? button.closest('[data-knowledge-rag-source-input-row]') : null;
|
||||
if (row instanceof HTMLElement) row.remove();
|
||||
var popover = ensureKnowledgeRagSettingsPopover();
|
||||
var node = popover.querySelector('[data-testid="mnote-knowledge-rag-source-inputs"]');
|
||||
if (node instanceof HTMLElement && !node.querySelector('[data-knowledge-rag-source-input]')) {
|
||||
node.innerHTML = knowledgeRagSourceInputRow('');
|
||||
}
|
||||
}
|
||||
|
||||
function resetKnowledgeRagSourceInputs() {
|
||||
var popover = ensureKnowledgeRagSettingsPopover();
|
||||
var node = popover.querySelector('[data-testid="mnote-knowledge-rag-source-inputs"]');
|
||||
if (node instanceof HTMLElement) node.innerHTML = knowledgeRagSourceInputRow('');
|
||||
}
|
||||
|
||||
async function ingestKnowledgeRagSources() {
|
||||
if (!knowledgeRagIsAvailable()) return;
|
||||
var sources = knowledgeRagSourceInputValues();
|
||||
if (!sources.length) {
|
||||
pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
|
||||
scopeKey: knowledgeRagScopeKey(),
|
||||
loading: false,
|
||||
error: '请先填写要索引的资料路径'
|
||||
});
|
||||
renderKnowledgeRagSettings();
|
||||
return;
|
||||
}
|
||||
setKnowledgeRagInputStatuses(Object.fromEntries(sources.map(function(path) {
|
||||
return [path, 'running'];
|
||||
})));
|
||||
pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
|
||||
scopeKey: knowledgeRagScopeKey(),
|
||||
loading: true,
|
||||
error: ''
|
||||
});
|
||||
renderKnowledgeRagSettings();
|
||||
try {
|
||||
var response = await fetch('/api/knowledge-rag/ingest', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||||
body: JSON.stringify({
|
||||
workspaceId: resolveWorkspaceId(document.body),
|
||||
rootUri: currentRootUri(),
|
||||
sources: sources.map(function(path) { return { sourcePath: path }; })
|
||||
})
|
||||
});
|
||||
var payload = await response.json().catch(function(){ return null; });
|
||||
if (!response.ok || !payload) {
|
||||
throw new Error(payload && payload.message ? payload.message : 'knowledge_rag_ingest_' + response.status);
|
||||
}
|
||||
if (payload.retryRequired) {
|
||||
setKnowledgeRagInputStatuses(Object.fromEntries(sources.map(function(path) {
|
||||
return [path, 'retry'];
|
||||
})));
|
||||
await loadKnowledgeRagStatus(true);
|
||||
notifyKnowledgeRagSourceUpdated({ action: 'ingest', sources: sources, result: payload, retryRequired: true });
|
||||
scheduleKnowledgeRagStatusBridge('ingest-retry');
|
||||
pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
|
||||
scopeKey: knowledgeRagScopeKey(),
|
||||
loading: false,
|
||||
error: 'LightRAG 正在处理其他任务,请稍后刷新或重试'
|
||||
});
|
||||
renderKnowledgeRagSettings();
|
||||
return;
|
||||
}
|
||||
var configured = Array.isArray(payload.configuredSources) ? payload.configuredSources : [];
|
||||
var done = {};
|
||||
configured.forEach(function(item) {
|
||||
var requested = String(item && item.requestedSourcePath || item && item.sourceRootRelativePath || '').trim();
|
||||
if (requested) done[requested] = 'done';
|
||||
});
|
||||
setKnowledgeRagInputStatuses(done, 'done');
|
||||
await loadKnowledgeRagStatus(true);
|
||||
resetKnowledgeRagSourceInputs();
|
||||
notifyKnowledgeRagSourceUpdated({ action: 'ingest', sources: sources, result: payload });
|
||||
scheduleKnowledgeRagStatusBridge('ingest');
|
||||
} catch (error) {
|
||||
setKnowledgeRagInputStatuses(Object.fromEntries(sources.map(function(path) {
|
||||
return [path, 'failed'];
|
||||
})));
|
||||
pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
|
||||
scopeKey: knowledgeRagScopeKey(),
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
renderKnowledgeRagSettings();
|
||||
}
|
||||
}
|
||||
|
||||
async function ingestSingleKnowledgeRagSource(sourcePath) {
|
||||
sourcePath = String(sourcePath || '').trim();
|
||||
if (!sourcePath || !knowledgeRagIsAvailable()) return;
|
||||
setKnowledgeRagInputStatuses({ [sourcePath]: 'running' });
|
||||
pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
|
||||
scopeKey: knowledgeRagScopeKey(),
|
||||
loading: true,
|
||||
error: ''
|
||||
});
|
||||
renderKnowledgeRagSettings();
|
||||
try {
|
||||
var response = await fetch('/api/knowledge-rag/ingest', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||||
body: JSON.stringify({
|
||||
workspaceId: resolveWorkspaceId(document.body),
|
||||
rootUri: currentRootUri(),
|
||||
sources: [{ sourcePath: sourcePath }],
|
||||
force: true
|
||||
})
|
||||
});
|
||||
var payload = await response.json().catch(function(){ return null; });
|
||||
if (!response.ok || !payload) {
|
||||
throw new Error(payload && payload.message ? payload.message : 'knowledge_rag_reindex_' + response.status);
|
||||
}
|
||||
if (payload.retryRequired) {
|
||||
await loadKnowledgeRagStatus(true);
|
||||
notifyKnowledgeRagSourceUpdated({ action: 'reindex', sourceRootRelativePath: sourcePath, result: payload, retryRequired: true });
|
||||
scheduleKnowledgeRagStatusBridge('reindex-retry');
|
||||
pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
|
||||
scopeKey: knowledgeRagScopeKey(),
|
||||
loading: false,
|
||||
error: 'LightRAG 正在处理其他任务,请稍后重试'
|
||||
});
|
||||
renderKnowledgeRagSettings();
|
||||
return;
|
||||
}
|
||||
await loadKnowledgeRagStatus(true);
|
||||
notifyKnowledgeRagSourceUpdated({ action: 'reindex', sourceRootRelativePath: sourcePath, result: payload });
|
||||
scheduleKnowledgeRagStatusBridge('reindex');
|
||||
} catch (error) {
|
||||
pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
|
||||
scopeKey: knowledgeRagScopeKey(),
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
renderKnowledgeRagSettings();
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteKnowledgeRagSource(sourcePath) {
|
||||
sourcePath = String(sourcePath || '').trim();
|
||||
if (!sourcePath || !knowledgeRagIsAvailable()) return;
|
||||
pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
|
||||
scopeKey: knowledgeRagScopeKey(),
|
||||
loading: true,
|
||||
error: ''
|
||||
});
|
||||
renderKnowledgeRagSettings();
|
||||
try {
|
||||
var response = await fetch('/api/knowledge-rag/delete-source', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||||
body: JSON.stringify({
|
||||
workspaceId: resolveWorkspaceId(document.body),
|
||||
rootUri: currentRootUri(),
|
||||
sourcePath: sourcePath
|
||||
})
|
||||
});
|
||||
var payload = await response.json().catch(function(){ return null; });
|
||||
if (!response.ok || !payload || payload.ok !== true) {
|
||||
throw new Error(payload && payload.message ? payload.message : 'knowledge_rag_delete_' + response.status);
|
||||
}
|
||||
await loadKnowledgeRagStatus(true);
|
||||
notifyKnowledgeRagSourceUpdated({ action: 'delete', sourceRootRelativePath: sourcePath, result: payload });
|
||||
} catch (error) {
|
||||
pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
|
||||
scopeKey: knowledgeRagScopeKey(),
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
renderKnowledgeRagSettings();
|
||||
}
|
||||
}
|
||||
|
||||
async function pruneKnowledgeRagRegistry() {
|
||||
if (!knowledgeRagIsAvailable()) return;
|
||||
pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
|
||||
scopeKey: knowledgeRagScopeKey(),
|
||||
loading: true,
|
||||
error: ''
|
||||
});
|
||||
renderKnowledgeRagSettings();
|
||||
try {
|
||||
var response = await fetch('/api/knowledge-rag/prune-registry', {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
||||
body: JSON.stringify({
|
||||
workspaceId: resolveWorkspaceId(document.body),
|
||||
rootUri: currentRootUri()
|
||||
})
|
||||
});
|
||||
var payload = await response.json().catch(function(){ return null; });
|
||||
if (!response.ok || !payload || payload.ok !== true) {
|
||||
throw new Error(payload && payload.message ? payload.message : 'knowledge_rag_prune_' + response.status);
|
||||
}
|
||||
await loadKnowledgeRagStatus(true);
|
||||
notifyKnowledgeRagSourceUpdated({ action: 'prune', result: payload });
|
||||
} catch (error) {
|
||||
pageUiState.knowledgeRagSummary = Object.assign({}, currentKnowledgeRagSummary(), {
|
||||
scopeKey: knowledgeRagScopeKey(),
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
});
|
||||
renderKnowledgeRagSettings();
|
||||
}
|
||||
}
|
||||
|
||||
function openKnowledgeRagDashboard() {
|
||||
var status = currentKnowledgeRagSummary().status || {};
|
||||
var url = publicKnowledgeRagDashboardUrl(status);
|
||||
if (!url) return;
|
||||
window.open(url, '_blank', 'noopener');
|
||||
}
|
||||
|
||||
function knowledgeRagInputStatusText(status) {
|
||||
if (status === 'running') return '索引中';
|
||||
if (status === 'done') return '已提交';
|
||||
if (status === 'retry') return '需重试';
|
||||
if (status === 'failed') return '失败';
|
||||
return '待索引';
|
||||
}
|
||||
|
||||
function setKnowledgeRagInputStatuses(statusByPath, fallbackStatus) {
|
||||
var popover = ensureKnowledgeRagSettingsPopover();
|
||||
Array.from(popover.querySelectorAll('[data-knowledge-rag-source-input-row]')).forEach(function(row) {
|
||||
var input = row.querySelector('[data-knowledge-rag-source-input]');
|
||||
var label = row.querySelector('[data-knowledge-rag-input-status-label]');
|
||||
if (!(input instanceof HTMLInputElement) || !(label instanceof HTMLElement)) return;
|
||||
var value = input.value.trim();
|
||||
var status = statusByPath && statusByPath[value] || fallbackStatus || 'idle';
|
||||
label.textContent = knowledgeRagInputStatusText(status);
|
||||
label.setAttribute('data-status', status);
|
||||
label.hidden = status === 'idle';
|
||||
});
|
||||
}
|
||||
|
||||
function renderPageSettingsPopover() {
|
||||
var popover = ensurePageSettingsPopover();
|
||||
var options = currentPageOptions();
|
||||
@@ -1222,14 +1913,20 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
return popover instanceof HTMLElement && !popover.hidden;
|
||||
}
|
||||
|
||||
function isKnowledgeRagSettingsOpen() {
|
||||
var popover = document.querySelector('[data-testid="mnote-knowledge-rag-settings-popover"]');
|
||||
return popover instanceof HTMLElement && !popover.hidden;
|
||||
}
|
||||
|
||||
function isAnySettingsOpen() {
|
||||
return isPageSettingsOpen() || isLocalIndexSettingsOpen() || isLocalOcrSettingsOpen();
|
||||
return isPageSettingsOpen() || isLocalIndexSettingsOpen() || isLocalOcrSettingsOpen() || isKnowledgeRagSettingsOpen();
|
||||
}
|
||||
|
||||
function openPageSettingsPopover(initialTab) {
|
||||
if (!currentDocumentId()) return;
|
||||
closeLocalIndexSettingsPopover();
|
||||
closeLocalOcrSettingsPopover();
|
||||
closeKnowledgeRagSettingsPopover();
|
||||
var popover = ensurePageSettingsPopover();
|
||||
renderPageSettingsPopover();
|
||||
setActivePageSettingsTab(initialTab || 'page');
|
||||
@@ -1241,6 +1938,7 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
function openPageIndexSettingsPopover() {
|
||||
closePageSettingsPopover();
|
||||
closeLocalOcrSettingsPopover();
|
||||
closeKnowledgeRagSettingsPopover();
|
||||
var popover = ensureLocalIndexSettingsPopover();
|
||||
renderPageSettingsLocalIndex(popover);
|
||||
popover.hidden = false;
|
||||
@@ -1249,11 +1947,21 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
}
|
||||
|
||||
function openLocalOcrSettingsPopover() {
|
||||
openKnowledgeRagSettingsPopover();
|
||||
}
|
||||
|
||||
function openKnowledgeRagSettingsPopover() {
|
||||
closePageSettingsPopover();
|
||||
closeLocalIndexSettingsPopover();
|
||||
var popover = ensureLocalOcrSettingsPopover();
|
||||
renderLocalOcrOptions(popover);
|
||||
closeLocalOcrSettingsPopover();
|
||||
var popover = ensureKnowledgeRagSettingsPopover();
|
||||
renderKnowledgeRagSettings(popover);
|
||||
popover.hidden = false;
|
||||
void loadKnowledgeRagStatus(false).then(function() {
|
||||
if (knowledgeRagStatusHasInFlight(currentKnowledgeRagSummary().status || {})) {
|
||||
scheduleKnowledgeRagStatusBridge('open-panel');
|
||||
}
|
||||
});
|
||||
updateStandaloneSettingsTriggerState();
|
||||
}
|
||||
|
||||
@@ -1276,10 +1984,17 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
updateStandaloneSettingsTriggerState();
|
||||
}
|
||||
|
||||
function closeKnowledgeRagSettingsPopover() {
|
||||
var popover = document.querySelector('[data-testid="mnote-knowledge-rag-settings-popover"]');
|
||||
if (popover instanceof HTMLElement) popover.hidden = true;
|
||||
updateStandaloneSettingsTriggerState();
|
||||
}
|
||||
|
||||
function closeAllSettingsPopovers() {
|
||||
closePageSettingsPopover();
|
||||
closeLocalIndexSettingsPopover();
|
||||
closeLocalOcrSettingsPopover();
|
||||
closeKnowledgeRagSettingsPopover();
|
||||
}
|
||||
|
||||
function togglePageSettingsPopover() {
|
||||
@@ -1295,27 +2010,36 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
openLocalOcrSettingsPopover();
|
||||
});
|
||||
|
||||
window.addEventListener('mnote:knowledge-rag-source-updated', function() {
|
||||
if (isKnowledgeRagSettingsOpen()) void loadKnowledgeRagStatus(true);
|
||||
});
|
||||
|
||||
return {
|
||||
addLocalIndexRange,
|
||||
applyPageOptionsToShell,
|
||||
closeAllSettingsPopovers,
|
||||
closeKnowledgeRagSettingsPopover,
|
||||
closePageHistoryDrawer,
|
||||
closeLocalIndexSettingsPopover,
|
||||
closeLocalOcrSettingsPopover,
|
||||
closePageSettingsPopover,
|
||||
closePageShareDialog,
|
||||
currentLocalOcrPreferences,
|
||||
currentKnowledgeRagSummary,
|
||||
currentPageOptions,
|
||||
ensureHistorySnapshotsSeeded,
|
||||
ensureKnowledgeRagSettingsPopover,
|
||||
ensureLocalIndexSettingsPopover,
|
||||
ensureLocalOcrSettingsPopover,
|
||||
ensurePageHistoryDrawer,
|
||||
ensurePageSettingsPopover,
|
||||
ensurePageShareDialog,
|
||||
isAnySettingsOpen,
|
||||
isKnowledgeRagSettingsOpen,
|
||||
isLocalIndexSettingsOpen,
|
||||
isLocalOcrSettingsOpen,
|
||||
isPageSettingsOpen,
|
||||
openKnowledgeRagSettingsPopover,
|
||||
openPageHistoryDrawer,
|
||||
openLocalOcrSettingsPopover,
|
||||
openPageSettingsPopover,
|
||||
@@ -1331,6 +2055,16 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
renderPageSettingsPopover,
|
||||
setActivePageSettingsTab,
|
||||
refreshLocalIndex,
|
||||
addKnowledgeRagSourceInput,
|
||||
deleteKnowledgeRagSource,
|
||||
ingestKnowledgeRagSources,
|
||||
ingestSingleKnowledgeRagSource,
|
||||
loadKnowledgeRagStatus,
|
||||
openKnowledgeRagDashboard,
|
||||
pruneKnowledgeRagRegistry,
|
||||
removeKnowledgeRagSourceInput,
|
||||
setKnowledgeRagSourceFilter,
|
||||
useKnowledgeRagFileTreeSelection,
|
||||
togglePageSettingsPopover,
|
||||
updatePageSettingsTriggerState,
|
||||
writeGlobalShowHeadingNumbers,
|
||||
|
||||
@@ -1163,7 +1163,7 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
|
||||
var ownerDocumentId = fileOwnerDocumentId(item, documentId, objectIdentity);
|
||||
var iconKind = iconKindOf(item);
|
||||
var indexStatus = String((item && item.indexStatus) || (item && item.resourceMeta && item.resourceMeta.indexStatus) || '').trim();
|
||||
if (indexStatus !== 'indexed' && indexStatus !== 'failed') indexStatus = '';
|
||||
if (!['indexed', 'indexing', 'failed'].includes(indexStatus)) indexStatus = '';
|
||||
var cachedChildren = relativePath ? cachedFileTreeRows(relativePath) : [];
|
||||
var title = isFileTreeProjectionPageRow(rowKind, assetId)
|
||||
? fileTreePageTitle(rawTitle)
|
||||
|
||||
@@ -210,14 +210,25 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
const openPageHistoryDrawer = (...args) => sidebarPageSettings.openPageHistoryDrawer(...args);
|
||||
const openPageIndexSettingsPopover = (...args) => sidebarPageSettings.openPageIndexSettingsPopover(...args);
|
||||
const openLocalOcrSettingsPopover = (...args) => sidebarPageSettings.openLocalOcrSettingsPopover(...args);
|
||||
const openKnowledgeRagSettingsPopover = (...args) => sidebarPageSettings.openKnowledgeRagSettingsPopover(...args);
|
||||
const openPageShareDialog = (...args) => sidebarPageSettings.openPageShareDialog(...args);
|
||||
const pageOptionIsSupported = (...args) => sidebarPageSettings.pageOptionIsSupported(...args);
|
||||
const addLocalIndexRange = (...args) => sidebarPageSettings.addLocalIndexRange(...args);
|
||||
const addKnowledgeRagSourceInput = (...args) => sidebarPageSettings.addKnowledgeRagSourceInput(...args);
|
||||
const deleteKnowledgeRagSource = (...args) => sidebarPageSettings.deleteKnowledgeRagSource(...args);
|
||||
const ingestKnowledgeRagSources = (...args) => sidebarPageSettings.ingestKnowledgeRagSources(...args);
|
||||
const ingestSingleKnowledgeRagSource = (...args) => sidebarPageSettings.ingestSingleKnowledgeRagSource(...args);
|
||||
const loadKnowledgeRagStatus = (...args) => sidebarPageSettings.loadKnowledgeRagStatus(...args);
|
||||
const openKnowledgeRagDashboard = (...args) => sidebarPageSettings.openKnowledgeRagDashboard(...args);
|
||||
const pruneKnowledgeRagRegistry = (...args) => sidebarPageSettings.pruneKnowledgeRagRegistry(...args);
|
||||
const setKnowledgeRagSourceFilter = (...args) => sidebarPageSettings.setKnowledgeRagSourceFilter(...args);
|
||||
const useKnowledgeRagFileTreeSelection = (...args) => sidebarPageSettings.useKnowledgeRagFileTreeSelection(...args);
|
||||
const persistLocalOcrAutoPreference = (...args) => sidebarPageSettings.persistLocalOcrAutoPreference(...args);
|
||||
const persistLocalIndexSettings = (...args) => sidebarPageSettings.persistLocalIndexSettings(...args);
|
||||
const persistPageOptionsPatch = (...args) => sidebarPageSettings.persistPageOptionsPatch(...args);
|
||||
const persistPageWidthPreference = (...args) => sidebarPageSettings.persistPageWidthPreference(...args);
|
||||
const refreshLocalIndex = (...args) => sidebarPageSettings.refreshLocalIndex(...args);
|
||||
const removeKnowledgeRagSourceInput = (...args) => sidebarPageSettings.removeKnowledgeRagSourceInput(...args);
|
||||
const removeLocalIndexRange = (...args) => sidebarPageSettings.removeLocalIndexRange(...args);
|
||||
const recordPageHistorySnapshot = (...args) => sidebarPageSettings.recordPageHistorySnapshot(...args);
|
||||
const renderPageSettingsPopover = (...args) => sidebarPageSettings.renderPageSettingsPopover(...args);
|
||||
@@ -1919,6 +1930,9 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
window.addEventListener('wolai:local-assets-changed', function(event) {
|
||||
applyAssetsChangedToFileTree(event.detail || {});
|
||||
});
|
||||
window.addEventListener('mnote:knowledge-rag-source-updated', function() {
|
||||
if (currentSourceKind() === 'local_folder') void refreshLocalFolderSidebarSnapshot();
|
||||
});
|
||||
installMindmapAssetFetchObserver();
|
||||
|
||||
async function uploadFilesWithResolvedTarget(files, detail, options) {
|
||||
@@ -2670,23 +2684,28 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
var indexSettingsTrigger = closestAction(e.target, '[data-mnote-action="open-index-settings"]');
|
||||
if (indexSettingsTrigger) {
|
||||
e.preventDefault();
|
||||
openPageIndexSettingsPopover();
|
||||
openKnowledgeRagSettingsPopover();
|
||||
return;
|
||||
}
|
||||
|
||||
var knowledgeRagSettingsTrigger = closestAction(e.target, '[data-mnote-action="open-knowledge-rag-settings"]');
|
||||
if (knowledgeRagSettingsTrigger) {
|
||||
e.preventDefault();
|
||||
openKnowledgeRagSettingsPopover();
|
||||
return;
|
||||
}
|
||||
|
||||
var ocrTaskTrigger = closestAction(e.target, '[data-mnote-action="toggle-ocr-tasks"]');
|
||||
if (ocrTaskTrigger) {
|
||||
e.preventDefault();
|
||||
window.dispatchEvent(new CustomEvent('mnote:local-ocr-settings-action', {
|
||||
detail: { action: 'tasks' }
|
||||
}));
|
||||
openKnowledgeRagSettingsPopover();
|
||||
return;
|
||||
}
|
||||
|
||||
var ocrSettingsTrigger = closestAction(e.target, '[data-mnote-action="open-ocr-settings"]');
|
||||
if (ocrSettingsTrigger) {
|
||||
e.preventDefault();
|
||||
openLocalOcrSettingsPopover();
|
||||
openKnowledgeRagSettingsPopover();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2742,6 +2761,52 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
|
||||
return;
|
||||
}
|
||||
|
||||
var knowledgeRagAction = closestAction(e.target, '[data-knowledge-rag-action]');
|
||||
if (knowledgeRagAction) {
|
||||
e.preventDefault();
|
||||
var knowledgeRagActionName = knowledgeRagAction.getAttribute('data-knowledge-rag-action') || '';
|
||||
if (knowledgeRagActionName === 'refresh') {
|
||||
void loadKnowledgeRagStatus(true);
|
||||
return;
|
||||
}
|
||||
if (knowledgeRagActionName === 'add-source') {
|
||||
addKnowledgeRagSourceInput();
|
||||
return;
|
||||
}
|
||||
if (knowledgeRagActionName === 'remove-source') {
|
||||
removeKnowledgeRagSourceInput(knowledgeRagAction);
|
||||
return;
|
||||
}
|
||||
if (knowledgeRagActionName === 'use-filetree-selection') {
|
||||
useKnowledgeRagFileTreeSelection();
|
||||
return;
|
||||
}
|
||||
if (knowledgeRagActionName === 'ingest') {
|
||||
void ingestKnowledgeRagSources();
|
||||
return;
|
||||
}
|
||||
if (knowledgeRagActionName === 'reindex-source') {
|
||||
void ingestSingleKnowledgeRagSource(knowledgeRagAction.getAttribute('data-knowledge-rag-source-path') || '');
|
||||
return;
|
||||
}
|
||||
if (knowledgeRagActionName === 'delete-source') {
|
||||
void deleteKnowledgeRagSource(knowledgeRagAction.getAttribute('data-knowledge-rag-source-path') || '');
|
||||
return;
|
||||
}
|
||||
if (knowledgeRagActionName === 'prune') {
|
||||
void pruneKnowledgeRagRegistry();
|
||||
return;
|
||||
}
|
||||
if (knowledgeRagActionName === 'filter-sources') {
|
||||
setKnowledgeRagSourceFilter(knowledgeRagAction.getAttribute('data-knowledge-rag-filter') || 'attention');
|
||||
return;
|
||||
}
|
||||
if (knowledgeRagActionName === 'open-dashboard') {
|
||||
openKnowledgeRagDashboard();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var pageSettingsAction = closestAction(e.target, '[data-page-settings-action]');
|
||||
if (pageSettingsAction) {
|
||||
e.preventDefault();
|
||||
|
||||
Reference in New Issue
Block a user