feat(rag): replace LiteParse flows with LightRAG provider

This commit is contained in:
lix-2026
2026-06-07 01:10:31 +08:00
parent f46c2fb5d0
commit 1f25364374
40 changed files with 7232 additions and 2350 deletions
@@ -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 healthyMNote 登记 ' + 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,