feat: restore Wolai workspace navigation and assets

This commit is contained in:
Agent Board
2026-07-15 23:12:15 +08:00
parent 31a160fcc5
commit 6f9c7d3b58
48 changed files with 6734 additions and 517 deletions
@@ -470,18 +470,33 @@ import {
const topbarCurrent = document.querySelector('.wolai-breadcrumb-current [data-page-title-current]');
if (topbarCurrent instanceof HTMLElement) topbarCurrent.textContent = title;
window.dispatchEvent(new CustomEvent('mnote:primary-document-activated', {
detail: { documentId, workspaceId, title }
detail: {
documentId,
workspaceId,
title,
sourceKind: bootstrap.sourceKind || '',
rootUri: bootstrap.rootUri || '',
pageBlockNavigation: runtimeDescriptor.pageBlockNavigation === true,
}
}));
document.documentElement.removeAttribute('data-mnote-side-target-unsupported');
document.documentElement.removeAttribute('data-mnote-side-target-asset-id');
document.querySelectorAll('.tree-row[data-active="true"], .tree-row[data-selected="true"]').forEach((row) => {
const activeTreeTab = document.querySelector('[data-mnote-sidebar-tree-tab][aria-selected="true"]');
const activeTreeMode = activeTreeTab instanceof HTMLElement
&& activeTreeTab.getAttribute('data-mnote-sidebar-tree-tab') === 'filetree'
? 'filetree'
: 'page';
const activeTreeRows = activeTreeMode === 'filetree'
? '#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"]'
: '#sidebar-tree-root .tree-row[data-shell-mode="page"]';
document.querySelectorAll(`${activeTreeRows}[data-active="true"], ${activeTreeRows}[data-selected="true"]`).forEach((row) => {
if (row instanceof HTMLElement) {
row.setAttribute('data-active', 'false');
row.setAttribute('data-selected', 'false');
}
});
const escapedId = window.CSS && typeof window.CSS.escape === 'function' ? window.CSS.escape(documentId) : String(documentId).replace(/["\\]/g, '\\$&');
document.querySelectorAll(`.tree-row[data-node-id="${escapedId}"], .tree-row[data-document-id="${escapedId}"], .tree-row[data-doc-id="${escapedId}"]`).forEach((row) => {
document.querySelectorAll(`${activeTreeRows}[data-node-id="${escapedId}"], ${activeTreeRows}[data-document-id="${escapedId}"], ${activeTreeRows}[data-doc-id="${escapedId}"]`).forEach((row) => {
if (!(row instanceof HTMLElement)) return;
if (row.getAttribute('data-shell-mode') === 'filetree') {
row.setAttribute('data-selected', String(row.getAttribute('data-row-id') === `doc:${documentId}`));
@@ -610,7 +625,7 @@ import {
}
const bootstrap = buildBootstrapFromAggregate(aggregate, descriptor, paneRole);
syncPageAggregateScript({ pageAggregateScriptId: bootstrap.pageAggregateScriptId }, aggregate);
const runtimeDescriptor = { paneRole, root, observability, aggregate, bootstrap };
const runtimeDescriptor = { paneRole, root, observability, aggregate, bootstrap, pageBlockNavigation: descriptor.pageBlockNavigation === true };
updatePaneChrome(runtimeDescriptor);
if (paneRole === 'secondary' && workspace instanceof HTMLElement) {
workspace.setAttribute('data-has-secondary-pane', 'true');
@@ -677,6 +692,70 @@ import {
pushUrlState(url);
};
const openPrimaryDocumentFromPageBlockHref = (href) => {
const raw = typeof href === 'string' ? href.trim() : '';
if (!raw) return false;
let target = null;
try {
target = new URL(raw, window.location.href);
} catch (_) {
return false;
}
if (target.origin !== window.location.origin || !target.pathname.startsWith('/documents/')) return false;
const encodedId = target.pathname.slice('/documents/'.length).split('/')[0] || '';
const documentId = decodeURIComponent(encodedId).trim();
if (!documentId || typeof window.__mnoteDocumentPaneRuntime?.openPrimaryDocument !== 'function') return false;
const current = currentUrl();
const workspaceId = target.searchParams.get('workspaceId') || current.searchParams.get('workspaceId') || '';
const sourceKind = target.searchParams.get('sourceKind') || current.searchParams.get('sourceKind') || '';
const rootUri = target.searchParams.get('rootUri') || current.searchParams.get('rootUri') || '';
void window.__mnoteDocumentPaneRuntime.openPrimaryDocument({
documentId,
workspaceId,
sourceKind,
rootUri,
url: target,
pageBlockNavigation: true,
}).catch((error) => {
console.warn('mnote page block pane navigation failed, fallback to full navigation', error);
window.location.assign(target.pathname + target.search + target.hash);
});
return true;
};
const installPageBlockOpenCapture = () => {
if (window.__mnotePageBlockOpenCaptureInstalled === true) return;
window.__mnotePageBlockOpenCaptureInstalled = true;
document.addEventListener('mousedown', (event) => {
if (event.button !== 0) return;
const target = event.target;
const anchor = target instanceof Element ? target.closest('a.mnote-page-block-link') : null;
if (!(anchor instanceof HTMLAnchorElement)) return;
const href = anchor.getAttribute('href') || '';
if (!href) return;
// Tiptap's Link extension handles mouse down before the click bubble. Keep
// the target aside so that it cannot trigger a native document navigation.
anchor.setAttribute('data-mnote-page-block-href', href);
anchor.removeAttribute('href');
}, true);
document.addEventListener('click', (event) => {
const target = event.target;
const anchor = target instanceof Element ? target.closest('a.mnote-page-block-link') : null;
if (!(anchor instanceof HTMLAnchorElement)) return;
const href = anchor.getAttribute('data-mnote-page-block-href') || anchor.getAttribute('href') || '';
if (!href) return;
anchor.removeAttribute('data-mnote-page-block-href');
anchor.removeAttribute('href');
if (!openPrimaryDocumentFromPageBlockHref(href)) {
anchor.setAttribute('href', href);
return;
}
event.preventDefault();
event.stopPropagation();
if (typeof event.stopImmediatePropagation === 'function') event.stopImmediatePropagation();
}, true);
};
const setSecondaryEditorHostVisible = (visible) => {
const host = document.querySelector('[data-testid="mnote-secondary-editor-tab-host"]');
if (host instanceof HTMLElement) host.hidden = !visible;
@@ -973,10 +1052,11 @@ import {
};
window.__mnoteDocumentPaneRuntime = {
openPrimaryDocument: async ({ documentId, workspaceId, sourceKind, rootUri, url } = {}) => {
openPrimaryDocument: async ({ documentId, workspaceId, sourceKind, rootUri, url, pageBlockNavigation } = {}) => {
const id = typeof documentId === 'string' ? documentId.trim() : '';
if (!id) return false;
const descriptor = descriptorFromCurrentUrl('primary', id, { workspaceId, sourceKind, rootUri });
descriptor.pageBlockNavigation = pageBlockNavigation === true;
await replacePaneDocument('primary', descriptor);
activateMainEditorTab('', 'primary');
updatePrimaryUrl(descriptor, url instanceof URL ? url : null);
@@ -1037,6 +1117,7 @@ import {
return 1;
},
};
installPageBlockOpenCapture();
bindMainEditorPageTab('primary');
bindMainEditorPageTab('secondary');
@@ -138,6 +138,39 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
|| localMarkdownRelativePathFromDocumentId(session?.documentId)
);
const localFolderNavigationFallbackUrl = (session, reason) => {
const url = new URL('/', window.location.origin);
url.searchParams.set('sourceKind', 'local_folder');
if (session?.rootUri) url.searchParams.set('rootUri', session.rootUri);
url.searchParams.set('treeView', 'filetree');
const relativePath = sessionRelativePath(session);
const currentScope = String(new URL(window.location.href).searchParams.get('fileTreeScope') || '').trim();
const parentScope = relativePath.includes('/') ? relativePath.split('/').slice(0, -1).join('/') : '';
const scope = currentScope || parentScope;
if (scope) url.searchParams.set('fileTreeScope', scope);
if (session?.documentId) url.searchParams.set('missingPage', session.documentId);
if (reason) url.searchParams.set('routeGuard', reason);
return url;
};
const navigateLocalMarkdownDeletedFallback = (session, reason = 'local_markdown_deleted') => {
if (!session || session.sessionKind === 'resource') return false;
if (session.saveTimer) {
window.clearTimeout(session.saveTimer);
session.saveTimer = 0;
}
if (session.externalRefreshTimer) {
window.clearTimeout(session.externalRefreshTimer);
session.externalRefreshTimer = 0;
}
session.externalChangePending = false;
session.hasExternalConflict = false;
session.bufferDirtyState = 'Deleted';
clearSessionConflictSurface(session);
window.location.assign(localFolderNavigationFallbackUrl(session, reason).toString());
return true;
};
const localFolderEventItems = (payload) => {
if (!payload || typeof payload !== 'object') return [];
const changedPaths = Array.isArray(payload.changedPaths)
@@ -151,6 +184,7 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
relativePath: String(item.relativePath || item.relative_path || item.path || '').trim(),
documentId: String(item.documentId || item.document_id || '').trim(),
eventKind: String(item.eventKind || item.event_kind || item.changeType || item.change_type || '').trim(),
changeType: String(item.changeType || item.change_type || '').trim(),
selfWriteEcho: Boolean(item.selfWriteEcho || item.self_write_echo),
fileVersion: String(item.fileVersion || item.file_version || item.observedFileVersion || item.observed_file_version || '').trim(),
observedFileVersion: String(item.observedFileVersion || item.observed_file_version || '').trim(),
@@ -1167,19 +1201,7 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
const bufferState = await fetchSessionBufferState(session);
const dirtyState = String(bufferState?.dirtyState || session.bufferDirtyState || '').trim();
if (dirtyState === 'Deleted') {
session.lastExternalConflictEnvelope = {
code: 'local_markdown_deleted',
documentId: session.documentId,
rootUri: session.rootUri || null,
currentDiskVersion: bufferState?.fileVersion || session.conflictDetectionKey || session.fileVersion || null,
editorBaseVersion: session.conflictDetectionKey || session.fileVersion || null,
externalActor: bufferState?.externalActor || null,
dirtyState,
bufferFileVersion: bufferState?.fileVersion || session.fileVersion || null,
message: '当前本地 Markdown 文件已被删除,请恢复文件或关闭当前标签',
suggestedActions: ['keep_editor', 'open_diff'],
};
markSessionExternalConflict(session, session.lastExternalConflictEnvelope.message);
navigateLocalMarkdownDeletedFallback(session);
return;
}
if (session.hasExternalConflict) {
@@ -1209,13 +1231,24 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
cache: 'no-store',
headers: { accept: 'application/json' },
});
const payload = await response.json().catch(() => null);
if (!response.ok) {
const errorCode = String(payload?.error?.code || payload?.code || '').trim();
if (session.sourceKind === 'local_folder' && (response.status === 404 || errorCode === 'local_markdown_not_found')) {
navigateLocalMarkdownDeletedFallback(session, 'local_markdown_not_found');
return;
}
const conflictEnvelope = conflictEnvelopeFromResponse(payload);
if (response.status === 409 && conflictEnvelope) {
session.lastExternalConflictEnvelope = conflictEnvelope;
markSessionExternalConflict(session, conflictMessageFromEnvelope(session, conflictEnvelope));
return;
}
if (session.sourceKind === 'local_folder') {
markSessionExternalConflict(session, externalConflictMessage);
}
return;
}
const payload = await response.json();
const conflictEnvelope = conflictEnvelopeFromResponse(payload);
if (response.status === 409 && conflictEnvelope) {
session.lastExternalConflictEnvelope = conflictEnvelope;
@@ -1404,6 +1437,11 @@ export const createDocumentSessionRuntime = (dependencies = {}) => {
|| (!documentId && relativePath && relativeTarget && relativePath === relativeTarget)
);
if (!targetsCurrentDocument) return;
const changeType = String(item.changeType || '').trim();
if (changeType === 'deleted') {
navigateLocalMarkdownDeletedFallback(targetSession);
return;
}
if (item.selfWriteEcho === true) {
targetSession.externalChangePending = false;
targetSession.lastSelfSaveSignalAt = Date.now();
@@ -197,9 +197,67 @@ export const legacyBlockToTiptap = (block, index = 0) => {
}),
};
}
if (type === 'page_reference' || type === 'pageReference' || type === 'pagereference') {
const sourcePath = firstNonEmptyText(
block?.props?.sourcePath,
block?.props?.source_path,
block?.props?.href,
block?.props?.url,
blockAttrs?.sourcePath,
blockAttrs?.source_path,
blockAttrs?.href,
blockAttrs?.url,
blockAttrs?.pageId,
blockAttrs?.page_id
);
const title = firstNonEmptyText(
block?.props?.title,
block?.props?.name,
blockAttrs?.title,
blockAttrs?.name,
content,
sourcePath
) || '页面';
return {
type: 'paragraph',
attrs: withTextAlign({ blockId }),
content: [{
type: 'text',
text: title,
marks: [{
type: 'link',
attrs: {
href: sourcePath || '#',
target: '_self',
rel: 'noopener noreferrer nofollow',
class: 'mnote-page-block-link',
},
}],
}],
};
}
if (type === 'media') {
const sourcePath = firstNonEmptyText(block?.props?.sourcePath, block?.props?.url, block?.props?.src);
const name = firstNonEmptyText(block?.props?.name, block?.props?.fileName, block?.props?.title, sourcePath);
const sourcePath = firstNonEmptyText(
block?.props?.sourcePath,
block?.props?.source_path,
block?.props?.url,
block?.props?.src,
blockAttrs?.sourcePath,
blockAttrs?.source_path,
blockAttrs?.url,
blockAttrs?.src
);
const name = firstNonEmptyText(
block?.props?.name,
block?.props?.fileName,
block?.props?.file_name,
block?.props?.title,
blockAttrs?.name,
blockAttrs?.fileName,
blockAttrs?.file_name,
blockAttrs?.title,
sourcePath
);
const mediaContent = name
? [{
type: 'text',
@@ -336,7 +394,7 @@ export const localMarkdownDocumentIdFromRelativePath = (relativePath) => {
if (!normalized) return '';
return 'local-md:' + normalized
.split('/')
.map((part) => part.replace(/ /g, '~20'))
.map((part) => encodeURIComponent(part).replace(/%/g, '~'))
.join('~2F');
};
@@ -377,6 +435,48 @@ export const normalizeLocalAssetRelativePath = (value, context) => {
.join('/');
};
export const normalizeLocalPageRelativePath = (value, context) => {
const text = unwrapMarkdownLinkTarget(value);
if (!text || text.startsWith('#')) return '';
if (text.startsWith('/documents/')) return text;
if (text.startsWith('/api/') || text.startsWith('http://') || text.startsWith('https://') || text.startsWith('mailto:')) return '';
const baseDir = text.startsWith('/') ? '' : localMarkdownDirectoryFromDocumentId(context?.documentId);
const parts = (baseDir ? `${baseDir}/${text}` : text)
.replace(/\\/g, '/')
.split('/');
const normalized = [];
for (const part of parts) {
if (!part || part === '.') continue;
if (part === '..') {
if (!normalized.length) return '';
normalized.pop();
continue;
}
normalized.push(part);
}
return normalized.join('/');
};
export const localPageOpenUrlForTiptap = (value, context) => {
const text = unwrapMarkdownLinkTarget(value);
if (!context || context.sourceKind !== 'local_folder' || !context.rootUri) return text || value;
if (text.startsWith('/documents/')) {
const url = new URL(text, window.location.origin);
if (!url.searchParams.get('sourceKind')) url.searchParams.set('sourceKind', 'local_folder');
if (!url.searchParams.get('rootUri')) url.searchParams.set('rootUri', context.rootUri);
return `${url.pathname}${url.search}`;
}
const relativePath = normalizeLocalPageRelativePath(text, context);
if (!relativePath) return text || value;
const documentId = localMarkdownDocumentIdFromRelativePath(relativePath);
if (!documentId) return text || value;
const url = new URL(`/documents/${documentId}`, window.location.origin);
url.searchParams.set('sourceKind', 'local_folder');
url.searchParams.set('rootUri', context.rootUri);
url.searchParams.set('treeView', 'filetree');
return `${url.pathname}${url.search}`;
};
export const localFileOpenUrlForTiptap = (value, context) => {
if (!context || context.sourceKind !== 'local_folder' || !context.rootUri) return value;
const relativePath = normalizeLocalAssetRelativePath(value, context);
@@ -463,6 +563,8 @@ const attachmentRefForTiptapHref = (href, context) => {
const withAttachmentProjectionAttrs = (attrs, attachmentRef) => {
if (!attachmentRef || typeof attachmentRef !== 'object') return attrs;
const next = { ...attrs };
const fileSize = attachmentFileSizeText(attachmentRef);
if (fileSize) next['data-file-size'] = fileSize;
if (attachmentRef.exists === false) {
next.class = mergeClassNames(next.class, 'mnote-uploaded-attachment-missing');
next['data-mnote-attachment-missing'] = 'true';
@@ -476,6 +578,14 @@ const withAttachmentProjectionAttrs = (attrs, attachmentRef) => {
return next;
};
export const attachmentFileSizeText = (attachmentRef) => {
const size = Number(attachmentRef && (attachmentRef.fileSize || attachmentRef.file_size) || 0);
if (!Number.isFinite(size) || size <= 0) return '';
if (size >= 1024 * 1024) return `${(size / 1024 / 1024).toFixed(size >= 10 * 1024 * 1024 ? 1 : 2)} MB`;
if (size >= 1024) return `${(size / 1024).toFixed(size >= 100 * 1024 ? 0 : 2)} KB`;
return `${Math.round(size)} B`;
};
export const localizeTiptapAssetUrls = (node, context) => {
if (!node || typeof node !== 'object') return node;
if (node.type === 'image' && node.attrs && typeof node.attrs.src === 'string') {
@@ -490,12 +600,26 @@ export const localizeTiptapAssetUrls = (node, context) => {
node.marks = node.marks.map((mark) => {
if (!mark || mark.type !== 'link' || !mark.attrs || typeof mark.attrs.href !== 'string') return mark;
const href = String(mark.attrs.href || '').trim();
const markClass = String(mark.attrs.class || '');
if (markClass.split(/\s+/).includes('mnote-page-block-link')) {
return {
...mark,
attrs: {
...mark.attrs,
href: localPageOpenUrlForTiptap(href, context),
target: mark.attrs.target || '_self',
rel: mark.attrs.rel || 'noopener noreferrer nofollow',
class: mergeClassNames(mark.attrs.class, 'mnote-page-block-link'),
},
};
}
const attachmentClass = localAttachmentClassForTiptapHref(href);
const attachmentRef = attachmentRefForTiptapHref(href, context);
const localizedHref = attachmentClass ? localFileOpenUrlForTiptap(href, context) : href;
const attrs = attachmentClass
? withAttachmentProjectionAttrs({
...mark.attrs,
href,
href: localizedHref,
class: mergeClassNames(mark.attrs.class, attachmentClass),
target: mark.attrs.target || '_blank',
rel: mark.attrs.rel || 'noopener noreferrer nofollow',
@@ -92,6 +92,7 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
function localMarkdownAttachmentPathFromHref(href, documentId) {
var raw = String(href || '').trim();
if (!raw || raw.indexOf('#') === 0 || /^[a-z][a-z0-9+.-]*:/i.test(raw)) return '';
if (raw.indexOf('/documents/') === 0 || raw.indexOf('/api/') === 0) return '';
var documentPath = localMarkdownPathFromDocumentId(documentId);
if (!documentPath) return '';
var slashIndex = documentPath.lastIndexOf('/');
@@ -271,6 +272,7 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
async function refreshLocalAttachmentExistence(link) {
if (!(link instanceof HTMLAnchorElement)) return;
if (link.classList.contains('mnote-page-block-link')) return;
var href = link.getAttribute('href') || link.href || '';
var paneContext = editorAttachmentPaneContext(link);
var attachmentRef = attachmentRefForHref(href, paneContext, link ? link.textContent : '');
@@ -317,6 +319,7 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
function refreshEditorLocalAttachmentExistence() {
document.querySelectorAll(editorAttachmentEnhanceSelector + ', .editor-surface .ProseMirror a[href*="/api/local-folder/files/open"]').forEach(function(link) {
if (link instanceof HTMLAnchorElement && link.classList.contains('mnote-page-block-link')) return;
if (link instanceof HTMLAnchorElement) void refreshLocalAttachmentExistence(link);
});
}
@@ -418,16 +421,33 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
try {
absoluteHref = new URL(href, window.location.href).href;
} catch (_) {}
var localPath = normalizeLocalRelativePath(
localFileOpenPathFromHref(href) || localMarkdownAttachmentPathFromHref(href, paneContext && paneContext.documentId)
);
return refs.find(function(ref) {
if (!ref || typeof ref !== 'object') return false;
var refRelativePath = normalizeLocalRelativePath(ref.relativePath || '');
var refNormalizedHref = normalizeLocalRelativePath(ref.normalizedHref || ref.rawHref || '').replace(/^\.\//, '');
return String(ref.rawHref || '') === href
|| String(ref.normalizedHref || '') === href
|| String(ref.resolvedUri || '') === href
|| (absoluteHref && String(ref.resolvedUri || '') === absoluteHref)
|| (localPath && refRelativePath && refRelativePath === localPath)
|| (localPath && refNormalizedHref && localPath.endsWith('/' + refNormalizedHref))
|| (!href && textLabel && String(ref.label || '').trim() === textLabel);
}) || null;
}
function attachmentFileSizeLabel(attachmentRef) {
if (!attachmentRef || typeof attachmentRef !== 'object') return '';
if (typeof uploadedFileSize === 'function') return uploadedFileSize(attachmentRef);
var size = Number(attachmentRef.file_size || attachmentRef.fileSize || 0);
if (!Number.isFinite(size) || size <= 0) return '';
if (size >= 1024 * 1024) return (size / 1024 / 1024).toFixed(size >= 10 * 1024 * 1024 ? 1 : 2) + ' MB';
if (size >= 1024) return (size / 1024).toFixed(size >= 100 * 1024 ? 0 : 2) + ' KB';
return String(Math.round(size)) + ' B';
}
function detailFromEditorAttachmentLink(link) {
var rawHref = link instanceof HTMLAnchorElement ? (link.getAttribute('href') || '') : '';
var params = attachmentQueryParams(rawHref);
@@ -483,6 +503,7 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
function enhanceEditorAttachmentLink(link) {
if (!(link instanceof HTMLAnchorElement)) return;
var href = link.getAttribute('href') || '';
if (link.classList.contains('mnote-page-block-link')) return;
var params = attachmentQueryParams(href);
var paneContext = editorAttachmentPaneContext(link);
var attachmentRef = attachmentRefForHref(href, paneContext, link ? link.textContent : '');
@@ -509,6 +530,8 @@ export const createSidebarAttachmentOpenRuntime = (dependencies = {}) => {
if (typeof attachmentRef.authorized === 'boolean') {
setEditorAttachmentUnauthorizedState(link, attachmentRef.authorized === false);
}
var fileSizeLabel = attachmentFileSizeLabel(attachmentRef);
if (fileSizeLabel) setAttributeIfChanged(link, 'data-file-size', fileSizeLabel);
}
setAttributeIfChanged(link, 'target', '_blank');
setAttributeIfChanged(link, 'rel', 'noopener noreferrer nofollow');
@@ -24,6 +24,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
openEditorAttachmentEditTab,
openEditorAttachmentNewWindow,
openLocalResourceInActiveTab,
knowledgeHostUrl,
refreshLocalFolderSidebarSnapshot,
removeFileTreeAssetRow,
revealFileTreeResource,
@@ -467,7 +468,13 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
return;
}
if (action === 'open-knowledge-page') {
var knowledgeUrl = '/knowledge';
var knowledgeUrl = typeof knowledgeHostUrl === 'function'
? knowledgeHostUrl({
workspaceId: String(detail && detail.workspaceId || resolveWorkspaceId(trigger || document.body) || '').trim(),
sourceKind: currentSourceKind() || 'local_folder',
rootUri: knowledgeRagRootUri(detail, trigger)
})
: '/knowledge';
if (typeof window.open === 'function') {
window.open(knowledgeUrl, '_blank', 'noopener,noreferrer');
} else {
@@ -705,7 +705,7 @@ export function createSidebarPageSettingsRuntime(context) {
'<div class="mnote-weknora-kb-page-title">' +
'<span class="mnote-weknora-kb-page-eyebrow">LightRAG</span>' +
'<strong>知识库</strong>' +
'<span>由 MNote source registry 与 LightRAG 索引驱动</span>' +
'<span>资料、索引状态和检索</span>' +
'</div>' +
'<div class="mnote-weknora-kb-page-actions">' +
'<button type="button" data-knowledge-rag-action="refresh" title="刷新"><span class="material-symbols-outlined" data-icon="refresh" aria-hidden="true"></span></button>' +
@@ -714,44 +714,54 @@ export function createSidebarPageSettingsRuntime(context) {
'</div>' +
'</div>' +
'<div class="mnote-weknora-kb-page-status" data-testid="mnote-knowledge-rag-status"></div>' +
'<div class="mnote-weknora-kb-page-body">' +
'<aside class="mnote-weknora-kb-list-pane" data-testid="mnote-weknora-kb-list-pane">' +
'<div class="mnote-weknora-kb-page-body" data-kb-list-hidden="true">' +
'<aside class="mnote-weknora-kb-list-pane" data-testid="mnote-weknora-kb-list-pane" hidden>' +
'<div class="mnote-weknora-kb-list-head">' +
'<div><strong>资料库</strong><span data-testid="mnote-weknora-kb-list-count">0</span></div>' +
'<button type="button" data-knowledge-rag-action="focus-create-kb" title="LightRAG 当前使用单资料库" hidden><span class="material-symbols-outlined" data-icon="create_new_folder" aria-hidden="true"></span></button>' +
'<button type="button" data-knowledge-rag-action="toggle-create-kb" title="新建资料库"><span class="material-symbols-outlined" data-icon="create_new_folder" aria-hidden="true"></span></button>' +
'</div>' +
'<div class="mnote-weknora-kb-card-wrap" data-testid="mnote-weknora-kb-card-list"></div>' +
'<section class="mnote-weknora-kb-create-card" data-testid="mnote-weknora-kb-controls" hidden>' +
'<div class="mnote-weknora-kb-create-title"><span class="material-symbols-outlined" data-icon="folder_add" aria-hidden="true"></span><strong>新建逻辑资料库</strong></div>' +
'<input type="text" class="wolai-page-settings-index-path" data-testid="mnote-weknora-create-kb-name" data-knowledge-rag-create-kb-name="true" placeholder="知识库名称" spellcheck="false" />' +
'<input type="text" class="wolai-page-settings-index-path" data-testid="mnote-weknora-create-kb-description" data-knowledge-rag-create-kb-description="true" placeholder="描述,可留空" spellcheck="false" />' +
'<button type="button" class="wolai-page-settings-index-add" data-knowledge-rag-action="create-kb">创建知识库</button>' +
'<span class="mnote-knowledge-rag-input-status" data-testid="mnote-weknora-create-kb-status" data-knowledge-rag-create-kb-status hidden></span>' +
'</section>' +
'</aside>' +
'<section class="mnote-weknora-kb-detail-pane" data-testid="mnote-weknora-kb-detail-pane">' +
'<div class="mnote-weknora-kb-detail-hero" data-testid="mnote-weknora-kb-detail-hero">' +
'<div>' +
'<nav class="mnote-weknora-kb-breadcrumb" data-testid="mnote-weknora-kb-breadcrumb" aria-label="知识库面包屑"><span>知识库</span><span>/</span><strong data-testid="mnote-weknora-kb-breadcrumb-current">LightRAG 资料库</strong></nav>' +
'<span class="mnote-weknora-kb-type-pill"><span class="material-symbols-outlined" data-icon="folder" aria-hidden="true"></span>Source Registry</span>' +
'<span class="mnote-weknora-kb-type-pill"><span class="material-symbols-outlined" data-icon="folder" aria-hidden="true"></span>资料来源</span>' +
'<h2 data-testid="mnote-weknora-kb-detail-title">LightRAG 资料库</h2>' +
'<p data-testid="mnote-weknora-kb-detail-description">MNote 管理本地来源、引用定位和权限边界,LightRAG 负责索引与召回。</p>' +
'</div>' +
'<select class="mnote-kb-params-select" data-testid="mnote-weknora-kb-select" data-knowledge-rag-kb-select="true"><option value="">加载中...</option></select>' +
'</div>' +
'<div class="mnote-knowledge-rag-meta" data-testid="mnote-knowledge-rag-meta"></div>' +
'<div class="mnote-knowledge-rag-detail-tabs" role="tablist" aria-label="LightRAG 知识库详情">' +
'<button type="button" class="mnote-knowledge-rag-detail-tab is-active" role="tab" aria-selected="true" data-kb-rag-tab="sources" data-testid="mnote-weknora-tab-documents">来源</button>' +
'<button type="button" class="mnote-knowledge-rag-detail-tab" role="tab" aria-selected="false" data-kb-rag-tab="wiki" data-testid="mnote-weknora-tab-wiki">Wiki</button>' +
'<button type="button" class="mnote-knowledge-rag-detail-tab" role="tab" aria-selected="false" data-kb-rag-tab="graph" data-testid="mnote-weknora-tab-graph">Graph</button>' +
'<section class="mnote-knowledge-rag-kb-manager" data-testid="mnote-knowledge-rag-kb-manager">' +
'<div class="mnote-knowledge-rag-kb-manager-row">' +
'<label class="mnote-knowledge-rag-kb-select-field"><span>当前资料库</span><select class="mnote-kb-params-select" data-testid="mnote-weknora-kb-select" data-knowledge-rag-kb-select="true"><option value="">加载中...</option></select></label>' +
'<button type="button" data-knowledge-rag-action="toggle-create-kb"><span class="material-symbols-outlined" data-icon="folder_add" aria-hidden="true"></span>新建</button>' +
'<button type="button" data-knowledge-rag-action="delete-kb" data-knowledge-rag-delete-selected-kb="true"><span class="material-symbols-outlined" data-icon="delete" aria-hidden="true"></span>删除</button>' +
'</div>' +
'<section class="mnote-weknora-kb-create-card" data-testid="mnote-weknora-kb-controls" hidden>' +
'<div class="mnote-weknora-kb-create-title"><span class="material-symbols-outlined" data-icon="folder_add" aria-hidden="true"></span><strong>新建资料库</strong></div>' +
'<input type="text" class="wolai-page-settings-index-path" data-testid="mnote-weknora-create-kb-name" data-knowledge-rag-create-kb-name="true" placeholder="知识库名称" spellcheck="false" />' +
'<input type="text" class="wolai-page-settings-index-path" data-testid="mnote-weknora-create-kb-description" data-knowledge-rag-create-kb-description="true" placeholder="描述,可留空" spellcheck="false" />' +
'<button type="button" class="wolai-page-settings-index-add" data-knowledge-rag-action="create-kb">创建知识库</button>' +
'<span class="mnote-knowledge-rag-input-status" data-testid="mnote-weknora-create-kb-status" data-knowledge-rag-create-kb-status hidden></span>' +
'</section>' +
'<div class="mnote-knowledge-rag-kb-manager-note" data-testid="mnote-knowledge-rag-kb-manager-note"></div>' +
'</section>' +
'<details class="mnote-knowledge-rag-diagnostics" data-testid="mnote-knowledge-rag-diagnostics">' +
'<summary>诊断信息</summary>' +
'<div class="mnote-knowledge-rag-meta" data-testid="mnote-knowledge-rag-meta"></div>' +
'</details>' +
'<div class="mnote-knowledge-rag-detail-tabs" role="tablist" aria-label="当前资料库内容">' +
'<button type="button" class="mnote-knowledge-rag-detail-tab is-active" role="tab" aria-selected="true" data-kb-rag-tab="sources" data-testid="mnote-weknora-tab-documents">资料</button>' +
'<button type="button" class="mnote-knowledge-rag-detail-tab" role="tab" aria-selected="false" data-kb-rag-tab="wiki" data-testid="mnote-weknora-tab-wiki" hidden>Wiki</button>' +
'<button type="button" class="mnote-knowledge-rag-detail-tab" role="tab" aria-selected="false" data-kb-rag-tab="graph" data-testid="mnote-weknora-tab-graph" hidden>Graph</button>' +
'<button type="button" class="mnote-knowledge-rag-detail-tab" role="tab" aria-selected="false" data-kb-rag-tab="search">检索</button>' +
'</div>' +
'<div class="mnote-knowledge-rag-detail-panel" data-kb-rag-panel="sources">' +
'<div class="mnote-weknora-doc-layout" data-testid="mnote-weknora-document-management">' +
'<aside class="mnote-weknora-doc-sidebar" data-testid="mnote-weknora-doc-source-sidebar">' +
'<aside class="mnote-weknora-doc-sidebar" data-testid="mnote-weknora-doc-source-sidebar" hidden>' +
'<div class="mnote-weknora-doc-sidebar-head"><strong>来源</strong><span>MNote registry</span></div>' +
'<div class="mnote-weknora-doc-source-tags" data-testid="mnote-weknora-doc-source-tags">' +
'<button type="button" data-knowledge-rag-action="filter-sources" data-knowledge-rag-filter="all" data-active="true"><span class="material-symbols-outlined" data-icon="folder" aria-hidden="true"></span>全部来源</button>' +
'<button type="button" data-knowledge-rag-action="filter-sources" data-knowledge-rag-filter="all" data-active="true"><span class="material-symbols-outlined" data-icon="folder" aria-hidden="true"></span>全部资料</button>' +
'<button type="button" data-knowledge-rag-action="filter-sources" data-knowledge-rag-filter="indexed"><span class="material-symbols-outlined" data-icon="check_circle" aria-hidden="true"></span>已索引</button>' +
'<button type="button" data-knowledge-rag-action="filter-sources" data-knowledge-rag-filter="indexing"><span class="material-symbols-outlined" data-icon="sync" aria-hidden="true"></span>处理中</button>' +
'</div>' +
@@ -760,21 +770,23 @@ export function createSidebarPageSettingsRuntime(context) {
'</aside>' +
'<section class="mnote-weknora-doc-main">' +
'<div class="mnote-weknora-doc-toolbar">' +
'<div class="mnote-weknora-doc-search"><span class="material-symbols-outlined" data-icon="search" aria-hidden="true"></span><input type="text" data-testid="mnote-weknora-doc-filter-input" data-knowledge-rag-doc-filter-input="true" placeholder="搜索或筛选当前知识库文档" spellcheck="false" /></div>' +
'<div class="mnote-weknora-doc-search"><span class="material-symbols-outlined" data-icon="search" aria-hidden="true"></span><input type="text" data-testid="mnote-weknora-doc-filter-input" data-knowledge-rag-doc-filter-input="true" placeholder="搜索或筛选当前资料库内容" spellcheck="false" /></div>' +
'<div class="mnote-weknora-view-switch" role="group" aria-label="文档视图切换" data-testid="mnote-weknora-view-switch">' +
'<button type="button" class="is-active" data-knowledge-rag-view="list" aria-pressed="true" title="列表视图"><span class="material-symbols-outlined" data-icon="view_list" aria-hidden="true"></span></button>' +
'<button type="button" data-knowledge-rag-view="grid" aria-pressed="false" title="网格视图"><span class="material-symbols-outlined" data-icon="grid_view" aria-hidden="true"></span></button>' +
'</div>' +
'<button type="button" data-knowledge-rag-action="use-filetree-selection"><span class="material-symbols-outlined" data-icon="account_tree" aria-hidden="true"></span>添加文件夹</button>' +
'<button type="button" data-knowledge-rag-action="add-source"><span class="material-symbols-outlined" data-icon="upload_file" aria-hidden="true"></span>添加文档</button>' +
'<button type="button" data-knowledge-rag-action="use-filetree-selection"><span class="material-symbols-outlined" data-icon="account_tree" aria-hidden="true"></span>从文件树添加</button>' +
'<button type="button" class="mnote-knowledge-rag-primary-action" data-knowledge-rag-action="add-source"><span class="material-symbols-outlined" data-icon="upload_file" aria-hidden="true"></span>添加资料</button>' +
'<button type="button" data-knowledge-rag-action="ingest-active"><span class="material-symbols-outlined" data-icon="note_add" aria-hidden="true"></span>加入当前页</button>' +
'</div>' +
'<div class="mnote-knowledge-rag-source-inputs" data-testid="mnote-knowledge-rag-source-inputs">' +
knowledgeRagSourceInputRow('') +
'</div>' +
'<div class="mnote-weknora-doc-actions">' +
'<button type="button" data-knowledge-rag-action="ingest">索引到 LightRAG</button>' +
'<button type="button" data-knowledge-rag-action="prune">清除失效记录</button>' +
'<div class="mnote-knowledge-rag-source-composer" data-knowledge-rag-source-composer="true" hidden>' +
'<div class="mnote-knowledge-rag-source-inputs" data-testid="mnote-knowledge-rag-source-inputs">' +
knowledgeRagSourceInputRow('') +
'</div>' +
'<div class="mnote-weknora-doc-actions">' +
'<button type="button" class="mnote-knowledge-rag-primary-action" data-knowledge-rag-action="ingest">开始索引</button>' +
'<button type="button" data-knowledge-rag-action="prune">清除失效记录</button>' +
'</div>' +
'</div>' +
'<div class="mnote-knowledge-rag-source-filters" data-testid="mnote-knowledge-rag-source-filters"></div>' +
'<div class="mnote-weknora-doc-table" data-testid="mnote-knowledge-rag-sources" data-weknora-document-list-view="true"></div>' +
@@ -795,11 +807,11 @@ export function createSidebarPageSettingsRuntime(context) {
'</div>' +
'<div class="mnote-knowledge-rag-detail-panel" data-kb-rag-panel="search" hidden>' +
'<div class="mnote-kb-search-form">' +
'<input type="text" class="mnote-kb-search-input" data-kb-rag-search-input="true" placeholder="输入检索关键词...">' +
'<button type="button" class="wolai-page-settings-index-add" data-kb-rag-action="search">索</button>' +
'<div class="mnote-kb-search-box"><span class="material-symbols-outlined" data-icon="search" aria-hidden="true"></span><input type="text" class="mnote-kb-search-input" data-kb-rag-search-input="true" placeholder="在当前资料库中检索关键词" spellcheck="false"></div>' +
'<button type="button" class="wolai-page-settings-index-add" data-kb-rag-action="search"><span class="material-symbols-outlined" data-icon="search" aria-hidden="true"></span>检索</button>' +
'</div>' +
'<div class="mnote-kb-search-results" data-kb-rag-search-results="true">' +
'<span class="mnote-extensions-loading">输入关键词后用 LightRAG hybrid 检索</span>' +
'<div class="mnote-kb-search-empty">输入关键词后检索当前资料库</div>' +
'</div>' +
'</div>' +
'<div class="mnote-knowledge-rag-detail-panel" data-kb-rag-panel="params" hidden>' +
@@ -819,7 +831,7 @@ export function createSidebarPageSettingsRuntime(context) {
'</div>' +
'<div class="mnote-kb-params-row">' +
'<label class="mnote-kb-params-label">排序说明</label>' +
'<span class="mnote-kb-params-help">结果按 provider 排序分返回;兼容阈值字段保留在请求层,不作为默认主控。</span>' +
'<span class="mnote-kb-params-help">结果按相关度排序;高级阈值保留在请求层。</span>' +
'</div>' +
'<button type="button" class="wolai-page-settings-index-add" data-kb-rag-action="save-params">保存参数</button>' +
'</div>' +
@@ -1194,6 +1206,12 @@ export function createSidebarPageSettingsRuntime(context) {
'</div>';
}
function setKnowledgeRagSourceComposerVisible(visible) {
var popover = ensureKnowledgeRagSettingsPopover();
var composer = popover.querySelector('[data-knowledge-rag-source-composer]');
if (composer instanceof HTMLElement) composer.hidden = !visible;
}
function knowledgeRagSourceInputValues() {
var popover = ensureKnowledgeRagSettingsPopover();
return Array.from(popover.querySelectorAll('[data-knowledge-rag-source-input]')).map(function(input) {
@@ -1342,6 +1360,25 @@ export function createSidebarPageSettingsRuntime(context) {
return knowledgeRagStatusValue(status);
}
function knowledgeRagUserStatusLabel(status) {
var text = String(status || '').trim().toLowerCase();
if (!text) return '';
if (text === 'provider_missing') return '索引记录缺失';
if (text === 'failed' || text.includes('fail')) return '索引失败';
if (text === 'delete_submitted') return '正在移除索引';
if (text === 'delete_completed') return '已移除索引';
if (text === 'provider_mapped' || text === 'completed' || text === 'processed' || text === 'indexed') return '已索引';
if (['pending', 'submitted', 'scanning', 'parsing', 'parse', 'parsed', 'analyzing', 'analyze', 'processing', 'process'].includes(text)) return '处理中';
return text.replace(/_/g, ' ');
}
function knowledgeRagUserDescription(description) {
var text = String(description || '').trim();
if (!text) return '';
if (/source registry|provider|sidecar|chunk|kb id|knowledge base id/i.test(text)) return '';
return text;
}
function knowledgeRagProviderKnowledgeBaseIds(status) {
var config = status && status.providerConfig && typeof status.providerConfig === 'object' ? status.providerConfig : {};
var weknora = config.weknora && typeof config.weknora === 'object' ? config.weknora : {};
@@ -1415,11 +1452,12 @@ export function createSidebarPageSettingsRuntime(context) {
status: 'configured'
});
});
if (!output.length && ['lightrag', 'lightrag_legacy'].includes(knowledgeRagProviderName(status || {}))) {
output.push({
if (!seen['lightrag-default'] && ['lightrag', 'lightrag_legacy'].includes(knowledgeRagProviderName(status || {}))) {
seen['lightrag-default'] = true;
output.unshift({
providerKbId: 'lightrag-default',
name: '默认资料库',
description: 'LightRAG 单工作区索引,由 MNote source registry 划分范围',
description: '包含当前工作区的全部已索引资料',
sourceCount: ((status && status.registry && Array.isArray(status.registry.entries)) ? status.registry.entries.length : 0),
chunkCount: 0,
updatedAt: '',
@@ -1440,6 +1478,7 @@ export function createSidebarPageSettingsRuntime(context) {
function setKnowledgeRagSelectedProviderKnowledgeBaseId(providerKnowledgeBaseId) {
pageUiState.knowledgeRagSelectedProviderKnowledgeBaseId = String(providerKnowledgeBaseId || '').trim();
pageUiState.knowledgeRagSourceFilter = 'all';
renderKnowledgeRagSettings();
}
@@ -1491,22 +1530,47 @@ export function createSidebarPageSettingsRuntime(context) {
};
}
function knowledgeRagBaseStatusLabel(status) {
var text = String(status || 'active').trim().toLowerCase();
if (!text || text === 'active' || text === 'configured') return '可用';
if (text === 'processing' || text === 'indexing') return '处理中';
if (text === 'test' || text === 'preview' || text === 'local') return '测试';
if (text === 'failed' || text === 'error') return '异常';
return String(status || '可用').replace(/_/g, ' ');
}
function knowledgeRagOptimisticBases() {
return Array.isArray(pageUiState.knowledgeRagOptimisticBases) ? pageUiState.knowledgeRagOptimisticBases : [];
}
function knowledgeRagBaseIsOptimistic(providerKnowledgeBaseId) {
var selected = String(providerKnowledgeBaseId || '').trim();
return Boolean(selected && knowledgeRagOptimisticBases().some(function(base) {
return knowledgeRagBaseProviderKbId(base) === selected;
}));
}
function renderKnowledgeRagKnowledgeBaseCards(popover, status, entries, providerDocs) {
var listNode = popover.querySelector('[data-testid="mnote-weknora-kb-card-list"]');
var countNode = popover.querySelector('[data-testid="mnote-weknora-kb-list-count"]');
var listPane = popover.querySelector('[data-testid="mnote-weknora-kb-list-pane"]');
var pageBody = popover.querySelector('.mnote-weknora-kb-page-body');
if (!(listNode instanceof HTMLElement)) return;
var bases = knowledgeRagKnowledgeBases(status || {});
var selected = selectedKnowledgeRagProviderKnowledgeBaseId(status || {});
var providerName = knowledgeRagProviderName(status || {});
if (countNode instanceof HTMLElement) countNode.textContent = String(bases.length);
if (listPane instanceof HTMLElement) listPane.hidden = bases.length <= 1;
if (pageBody instanceof HTMLElement) pageBody.setAttribute('data-kb-list-hidden', bases.length <= 1 ? 'true' : 'false');
if (!bases.length) {
listNode.innerHTML = '<div class="wolai-page-settings-index-empty">还没有知识库来源</div>';
listNode.innerHTML = '<div class="wolai-page-settings-index-empty">还没有资料库</div>';
return;
}
listNode.innerHTML = bases.map(function(base) {
var stats = knowledgeRagBaseStats(base, entries, providerDocs);
var isActive = base.providerKbId === selected;
var baseStatus = String(base.status || 'active');
var baseStatusLabel = knowledgeRagBaseStatusLabel(baseStatus);
return '' +
'<button type="button" class="mnote-weknora-kb-card' + (isActive ? ' is-active' : '') + '" data-knowledge-rag-action="select-kb" data-knowledge-rag-kb-id="' + escapeHtml(base.providerKbId) + '" data-knowledge-rag-kb-provider="' + escapeHtml(providerName) + '" data-knowledge-rag-processing-count="' + escapeHtml(String(stats.processingCount)) + '">' +
'<span class="mnote-weknora-kb-card-star"><span class="material-symbols-outlined" data-icon="folder" aria-hidden="true"></span></span>' +
@@ -1519,12 +1583,13 @@ export function createSidebarPageSettingsRuntime(context) {
(stats.processingCount ? '<i class="is-processing"><span class="material-symbols-outlined" data-icon="sync" aria-hidden="true"></span>' + escapeHtml(String(stats.processingCount)) + '</i>' : '') +
'</span>' +
'</span>' +
'<span class="mnote-weknora-kb-card-status" data-status="' + escapeHtml(baseStatus) + '">' + escapeHtml(baseStatus) + '</span>' +
'<span class="mnote-weknora-kb-card-status" data-status="' + escapeHtml(baseStatus) + '">' + escapeHtml(baseStatusLabel) + '</span>' +
'</button>';
}).join('');
}
function renderKnowledgeRagKnowledgeBaseHero(popover, status, entries, providerDocs) {
var heroNode = popover.querySelector('[data-testid="mnote-weknora-kb-detail-hero"]');
var titleNode = popover.querySelector('[data-testid="mnote-weknora-kb-detail-title"]');
var descNode = popover.querySelector('[data-testid="mnote-weknora-kb-detail-description"]');
var breadcrumbNode = popover.querySelector('[data-testid="mnote-weknora-kb-breadcrumb-current"]');
@@ -1532,6 +1597,7 @@ export function createSidebarPageSettingsRuntime(context) {
var bases = knowledgeRagKnowledgeBases(status || {});
var selected = selectedKnowledgeRagProviderKnowledgeBaseId(status || {});
var base = bases.find(function(item) { return item.providerKbId === selected; }) || bases[0] || null;
if (heroNode instanceof HTMLElement) heroNode.hidden = true;
if (!base) {
titleNode.textContent = '未配置知识库';
descNode.textContent = '加入资料来源后,可在这里管理 MNote 授权的本地文件和引用。';
@@ -1541,27 +1607,66 @@ export function createSidebarPageSettingsRuntime(context) {
var stats = knowledgeRagBaseStats(base, entries, providerDocs);
titleNode.textContent = base.name;
if (breadcrumbNode instanceof HTMLElement) breadcrumbNode.textContent = base.name;
descNode.textContent = (base.description || '暂无描述') + ' · 文档 ' + stats.sourceCount + ' · chunk ' + stats.chunkCount + ' · processing ' + stats.processingCount;
var userDescription = knowledgeRagUserDescription(base.description);
descNode.textContent = stats.sourceCount + ' 个资料 · ' + stats.processingCount + ' 个处理中 · ' + stats.chunkCount + ' 个片段'
+ (userDescription ? ' · ' + userDescription : '');
}
function renderKnowledgeRagKnowledgeBaseControls(popover, status) {
var select = popover.querySelector('[data-knowledge-rag-kb-select]');
if (!(select instanceof HTMLSelectElement)) return;
function renderKnowledgeRagKnowledgeBaseControls(popover, status, entries, providerDocs) {
var selects = Array.from(popover.querySelectorAll('[data-knowledge-rag-kb-select]'))
.filter(function(select) { return select instanceof HTMLSelectElement; });
var deleteButton = popover.querySelector('[data-knowledge-rag-delete-selected-kb]');
var noteNode = popover.querySelector('[data-testid="mnote-knowledge-rag-kb-manager-note"]');
var bases = knowledgeRagKnowledgeBases(status || {});
var selected = selectedKnowledgeRagProviderKnowledgeBaseId(status || {});
if (!bases.length) {
select.innerHTML = '<option value="">未配置 KB</option>';
select.value = '';
select.disabled = true;
selects.forEach(function(select) {
select.innerHTML = '<option value="">未配置资料库</option>';
select.value = '';
select.disabled = true;
select.hidden = false;
});
if (deleteButton instanceof HTMLButtonElement) {
deleteButton.disabled = true;
deleteButton.title = '没有可删除的资料库';
}
if (noteNode instanceof HTMLElement) noteNode.textContent = '新建资料库后,可在这里切换当前工作区的资料范围。';
return;
}
select.disabled = false;
select.innerHTML = bases.map(function(base) {
var label = base.name + ' · ' + base.sourceCount + ' sources · ' + base.providerKbId;
var activeBase = bases.find(function(base) { return base.providerKbId === selected; }) || bases[0];
var selectedValue = activeBase.providerKbId;
var activeStats = knowledgeRagBaseStats(activeBase, entries || [], providerDocs || []);
var optionsHtml = bases.map(function(base) {
var stats = knowledgeRagBaseStats(base, entries || [], providerDocs || []);
var label = base.name + ' · ' + stats.sourceCount + ' 个资料';
return '<option value="' + escapeHtml(base.providerKbId) + '">' + escapeHtml(label) + '</option>';
}).join('');
select.value = selected || bases[0].providerKbId;
pageUiState.knowledgeRagSelectedProviderKnowledgeBaseId = select.value;
selects.forEach(function(select) {
select.disabled = false;
select.hidden = false;
select.innerHTML = optionsHtml;
select.value = selectedValue;
});
pageUiState.knowledgeRagSelectedProviderKnowledgeBaseId = selectedValue;
if (deleteButton instanceof HTMLButtonElement) {
var canDelete = selectedValue !== 'lightrag-default' && activeStats.sourceCount === 0 && activeStats.processingCount === 0;
deleteButton.disabled = !canDelete;
deleteButton.setAttribute('data-knowledge-rag-kb-id', selectedValue);
deleteButton.title = canDelete ? '删除当前空资料库' : (selectedValue === 'lightrag-default' ? '默认资料库不能删除' : '请先移除资料后再删除资料库');
}
if (noteNode instanceof HTMLElement) {
var providerName = knowledgeRagProviderName(status || {});
var activeName = activeBase.name || '当前资料库';
if (selectedValue === 'lightrag-default') {
noteNode.textContent = '默认资料库显示当前工作区全部已索引资料;新建资料库可管理独立资料范围。';
} else if (activeStats.sourceCount === 0 && activeStats.processingCount === 0) {
noteNode.textContent = activeName + ':空资料库,可添加资料或直接删除。';
} else if (providerName === 'weknora') {
noteNode.textContent = bases.length > 1 ? '已连接多个资料库,可用下拉框切换当前范围。' : '当前只有一个资料库,可新建后切换。';
} else {
noteNode.textContent = bases.length > 1 ? '当前可切换资料库;删除前需要先移除其中资料。' : '当前使用默认资料库,也可以新建一个资料库管理独立范围。';
}
}
}
function knowledgeRagKbSummaryHtml(status, entries, providerDocs) {
@@ -1581,8 +1686,8 @@ export function createSidebarPageSettingsRuntime(context) {
'<div class="mnote-weknora-kb-summary-card" data-testid="mnote-weknora-kb-summary" data-knowledge-rag-kb-id="' + escapeHtml(kbIds[0] || '') + '" data-knowledge-rag-kb-ids="' + escapeHtml(kbIds.join(',')) + '" data-knowledge-rag-kb-provider="' + escapeHtml(knowledgeRagProviderName(status || {})) + '" data-knowledge-rag-kb-status="configured">' +
'<div><span>知识库</span><strong>' + escapeHtml(activeCount ? String(activeCount) + ' 个' : '未配置') + '</strong></div>' +
'<div><span>文档</span><strong>' + escapeHtml(String((entries || []).length)) + '</strong></div>' +
'<div><span>Provider docs</span><strong>' + escapeHtml(String((providerDocs || []).length)) + '</strong></div>' +
'<div><span>Processing</span><strong data-knowledge-rag-processing-count="' + escapeHtml(String(processingCount)) + '">' + escapeHtml(String(processingCount)) + '</strong></div>' +
'<div><span>索引文档</span><strong>' + escapeHtml(String((providerDocs || []).length)) + '</strong></div>' +
'<div><span>处理中</span><strong data-knowledge-rag-processing-count="' + escapeHtml(String(processingCount)) + '">' + escapeHtml(String(processingCount)) + '</strong></div>' +
'</div>';
}
@@ -1797,8 +1902,8 @@ export function createSidebarPageSettingsRuntime(context) {
startKnowledgeRagPipelineWatcher(reason);
}
function knowledgeRagUnmappedProviderRows(status, entries) {
var docs = knowledgeRagProviderDocuments(status);
function knowledgeRagUnmappedProviderRows(status, entries, providerDocs) {
var docs = Array.isArray(providerDocs) ? providerDocs : knowledgeRagProviderDocuments(status);
if (!docs.length) return [];
var mappedDocIds = new Set();
var mappedFilePaths = new Set();
@@ -1846,14 +1951,14 @@ export function createSidebarPageSettingsRuntime(context) {
}, { all: 0, attention: 0, indexing: 0, failed: 0, indexed: 0, removed: 0, unmapped: 0 });
var current = knowledgeRagCurrentFilter();
var options = [
['all', '全部'],
['attention', '需处理'],
['indexing', '索引中'],
['indexing', '处理中'],
['failed', '失败/过期'],
['indexed', '已索引'],
['removed', '已移除'],
['unmapped', 'Provider 未映射'],
['all', '全部']
['indexed', '已索引']
];
if (counts.removed) options.push(['removed', '已移除']);
if (counts.unmapped) options.push(['unmapped', '未关联资料']);
filtersNode.innerHTML = options.map(function(item) {
var key = item[0];
var label = item[1];
@@ -1868,32 +1973,33 @@ export function createSidebarPageSettingsRuntime(context) {
}
}
function renderKnowledgeRagSources(popover, entries, status) {
function renderKnowledgeRagSources(popover, entries, status, providerDocs, activeBase) {
var sourcesNode = popover.querySelector('[data-testid="mnote-knowledge-rag-sources"]');
if (!(sourcesNode instanceof HTMLElement)) return;
var scopedProviderDocs = Array.isArray(providerDocs) ? providerDocs : knowledgeRagProviderDocuments(status || {});
var registryRows = (Array.isArray(entries) ? entries : []).map(function(entry) {
return { kind: 'registry', category: knowledgeRagRegistryEntryCategory(entry), entry: entry };
});
var rows = registryRows.concat(knowledgeRagUnmappedProviderRows(status || {}, entries));
var rows = registryRows.concat(knowledgeRagUnmappedProviderRows(status || {}, entries, scopedProviderDocs));
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">还没有登记到知识库 provider 的资料来源</div>';
var emptyName = activeBase && activeBase.name ? activeBase.name : '当前资料库';
sourcesNode.innerHTML = '<div class="wolai-page-settings-index-empty">' + escapeHtml(emptyName) + ' 还没有资料</div>';
return;
}
if (!items.length) {
sourcesNode.innerHTML = '<div class="wolai-page-settings-index-empty">当前分类没有资料来源</div>';
sourcesNode.innerHTML = '<div class="wolai-page-settings-index-empty">当前分类没有资料</div>';
return;
}
var pipelineProgress = knowledgeRagPipelineProgress(status || {});
var providerName = knowledgeRagProviderName(status || {});
var providerDocs = knowledgeRagProviderDocuments(status || {});
var providerDocsById = new Map();
var providerDocsByPath = new Map();
providerDocs.forEach(function(doc) {
scopedProviderDocs.forEach(function(doc) {
var docId = knowledgeRagProviderDocId(doc);
var filePath = String(doc && doc.filePath || '').trim();
if (docId) providerDocsById.set(docId, doc);
@@ -1907,28 +2013,28 @@ export function createSidebarPageSettingsRuntime(context) {
var providerId = knowledgeRagProviderDocId(doc);
var providerPath = String(doc.filePath || '');
var docStatus = knowledgeRagProviderStatusText(doc.status || doc.statusGroup || doc.processingStatus || doc.processing_status);
var docStatusLabel = knowledgeRagUserStatusLabel(docStatus) || '已记录';
var failedDoc = row.category === 'unmapped_failed';
var providerProgress = pipelineProgress && pipelineProgress.docId && pipelineProgress.docId === providerId ? pipelineProgress : null;
var knowledgeId = knowledgeRagProviderKnowledgeId(doc);
var providerTitle = providerPath || providerId || 'Provider 文档';
var providerTitle = providerPath || providerId || '索引文档';
var chunkCount = knowledgeRagProviderChunkCount(doc);
var providerMeta = 'provider=' + providerName + ' · 未映射到 MNote source registry · ' + docStatus
+ (providerId ? ' · provider id ' + providerId : '')
+ (chunkCount ? ' · chunk ' + chunkCount : '')
+ (providerPath ? '' : ' · 定位降级');
var providerMeta = '未关联本地资料 · ' + docStatusLabel
+ (chunkCount ? ' · ' + chunkCount + ' 个片段' : '')
+ (providerPath ? '' : ' · 无法直接定位原文件');
if (!providerProgress && !failedDoc && knowledgeRagDocumentIsInFlight(doc)) {
providerProgress = knowledgeRagIndeterminateProgress(status || {}, doc, docStatus);
}
return '' +
'<div class="mnote-knowledge-rag-source-row" data-knowledge-rag-source-row="true" data-knowledge-rag-source-kind="provider" data-knowledge-rag-provider="' + escapeHtml(providerName) + '" data-knowledge-rag-provider-id="' + escapeHtml(providerId) + '" data-knowledge-rag-knowledge-id="' + escapeHtml(knowledgeId) + '" data-knowledge-rag-chunk-count="' + escapeHtml(String(chunkCount || 0)) + '" data-knowledge-rag-processing-status="' + escapeHtml(docStatus) + '" data-knowledge-rag-open-reference-mode="provider-reference-fallback" data-knowledge-rag-source-path="' + escapeHtml(providerPath || providerId) + '">' +
'<span class="wolai-page-settings-index-status-dot" data-index-status="' + (failedDoc ? 'fault' : 'indexed') + '" title="' + escapeHtml(docStatus || 'Provider 文档') + '"></span>' +
'<span class="wolai-page-settings-index-status-dot" data-index-status="' + (failedDoc ? 'fault' : 'indexed') + '" title="' + escapeHtml(docStatusLabel || '索引文档') + '"></span>' +
'<div class="mnote-knowledge-rag-source-main">' +
'<strong title="' + escapeHtml(providerTitle) + '">' + escapeHtml(providerTitle) + '</strong>' +
'<span>' + escapeHtml(providerMeta) + '</span>' +
'<span class="mnote-weknora-source-aux"><i>' + escapeHtml(knowledgeId || 'provider-only') + '</i><i>' + escapeHtml(providerPath ? '引用可回跳' : '仅 provider 定位') + '</i></span>' +
'<span class="mnote-weknora-source-aux"><i>' + escapeHtml(knowledgeId || '索引记录') + '</i><i>' + escapeHtml(providerPath ? '引用可回跳' : '无法直接定位') + '</i></span>' +
knowledgeRagSourceProgressHtml(providerProgress) +
'</div>' +
'<div class="mnote-knowledge-rag-source-actions"><button type="button" disabled>只读</button></div>' +
'<div class="mnote-knowledge-rag-source-actions"></div>' +
'</div>';
}
var entry = row.entry || {};
@@ -1944,11 +2050,12 @@ export function createSidebarPageSettingsRuntime(context) {
var indexed = entry && (entry.indexedAtMs || providerStatus === 'provider_mapped' || providerStatus === 'completed' || providerStatus === 'processed') && 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 statusLabel = deletedDone ? 'Provider 已移除'
var rawProviderStatusLabel = knowledgeRagUserStatusLabel(providerStatus);
var statusLabel = deletedDone ? '已移除索引'
: deleting ? '删除已提交'
: deleted ? '已删除'
: stale ? '已过期'
: providerMissing ? 'Provider 索引缺失'
: providerMissing ? '索引记录缺失'
: failed ? '索引失败'
: indexed ? '已索引'
: (docId || providerStatus ? '正在索引' : '待提交');
@@ -1958,12 +2065,10 @@ export function createSidebarPageSettingsRuntime(context) {
var kbId = knowledgeRagEntryProviderKnowledgeBaseId(entry);
var metaParts = [
statusLabel,
'provider=' + providerName,
'MNote source registry',
kbId ? 'kb ' + kbId : '',
providerIds.length ? 'provider ids ' + providerIds.join(', ') : '',
chunkCount ? 'chunk ' + chunkCount : '',
providerStatus ? 'processing ' + providerStatus : ''
chunkCount ? chunkCount + ' 个片段' : '',
rawProviderStatusLabel && !indexed && rawProviderStatusLabel !== statusLabel ? rawProviderStatusLabel : '',
kbId ? '已绑定资料库' : '',
providerIds.length ? providerIds.length + ' 个索引记录' : ''
].filter(Boolean);
var meta = metaParts.join(' · ');
var sourcePath = String(entry && entry.sourceRootRelativePath || '');
@@ -1977,15 +2082,15 @@ export function createSidebarPageSettingsRuntime(context) {
'<div class="mnote-knowledge-rag-source-row" data-knowledge-rag-source-row="true" data-knowledge-rag-provider="' + escapeHtml(providerName) + '" data-knowledge-rag-provider-ids="' + escapeHtml(providerIds.join(',')) + '" data-knowledge-rag-kb-id="' + escapeHtml(kbId) + '" data-knowledge-rag-knowledge-id="' + escapeHtml(knowledgeId) + '" data-knowledge-rag-chunk-count="' + escapeHtml(String(chunkCount || 0)) + '" data-knowledge-rag-processing-status="' + escapeHtml(providerStatus || statusLabel) + '" data-knowledge-rag-open-reference-mode="source-registry-reference" data-knowledge-rag-source-path="' + escapeHtml(sourcePath) + '">' +
'<span class="wolai-page-settings-index-status-dot" data-index-status="' + kind + '" title="' + escapeHtml(statusLabel) + '"></span>' +
'<div class="mnote-knowledge-rag-source-main">' +
'<strong title="' + escapeHtml(title) + '">' + escapeHtml(title || '未命名来源') + '</strong>' +
'<strong title="' + escapeHtml(title) + '">' + escapeHtml(title || '未命名资料') + '</strong>' +
'<span>' + escapeHtml(meta) + '</span>' +
'<span class="mnote-weknora-source-aux"><i>' + escapeHtml(kbId || '未绑定 KB') + '</i><i>' + escapeHtml(knowledgeId || '待生成 citation') + '</i></span>' +
knowledgeRagSourceProgressHtml(entryProgress) +
'</div>' +
'<div class="mnote-knowledge-rag-source-actions">' +
'<button type="button" data-knowledge-rag-action="open-source-reference" data-knowledge-rag-source-path="' + escapeHtml(sourcePath) + '"' + (sourcePath ? '' : ' disabled') + '>打开引用</button>' +
'<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-delete-local-file-policy="preserve-local-file" data-knowledge-rag-source-path="' + escapeHtml(sourcePath) + '"' + (canDelete ? '' : ' disabled') + ' title="删除 provider index 与 registry 映射,不删除本地文件">除索引</button>' +
'<button type="button" data-knowledge-rag-action="open-source-reference" data-knowledge-rag-source-path="' + escapeHtml(sourcePath) + '"' + (sourcePath ? '' : ' disabled') + '>打开</button>' +
'<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-delete-local-file-policy="preserve-local-file" data-knowledge-rag-source-path="' + escapeHtml(sourcePath) + '"' + (canDelete ? '' : ' disabled') + ' title="只移除索引记录,不删除本地文件">除索引</button>' +
'</div>' +
'</div>';
}).join('');
@@ -2002,7 +2107,7 @@ export function createSidebarPageSettingsRuntime(context) {
statusNode.textContent = '知识库仅在本地工作区可用';
if (metaNode instanceof HTMLElement) metaNode.innerHTML = '';
renderKnowledgeRagSourceFilters(popover, []);
renderKnowledgeRagSources(popover, [], {});
renderKnowledgeRagSources(popover, [], {}, [], null);
if (dashboardButton instanceof HTMLButtonElement) dashboardButton.disabled = true;
return;
}
@@ -2012,7 +2117,7 @@ export function createSidebarPageSettingsRuntime(context) {
statusNode.textContent = '正在读取知识库 provider 状态...';
if (metaNode instanceof HTMLElement) metaNode.innerHTML = '';
renderKnowledgeRagSourceFilters(popover, []);
renderKnowledgeRagSources(popover, [], {});
renderKnowledgeRagSources(popover, [], {}, [], null);
if (dashboardButton instanceof HTMLButtonElement) dashboardButton.disabled = true;
return;
}
@@ -2020,7 +2125,7 @@ export function createSidebarPageSettingsRuntime(context) {
statusNode.textContent = '知识库 provider 状态读取失败:' + summary.error;
if (metaNode instanceof HTMLElement) metaNode.innerHTML = '';
renderKnowledgeRagSourceFilters(popover, []);
renderKnowledgeRagSources(popover, [], {});
renderKnowledgeRagSources(popover, [], {}, [], null);
if (dashboardButton instanceof HTMLButtonElement) dashboardButton.disabled = true;
return;
}
@@ -2046,12 +2151,21 @@ export function createSidebarPageSettingsRuntime(context) {
button.classList.toggle('is-active', active);
button.setAttribute('aria-pressed', active ? 'true' : 'false');
});
renderKnowledgeRagKnowledgeBaseControls(popover, status);
renderKnowledgeRagKnowledgeBaseControls(popover, status, entries, providerDocs);
renderKnowledgeRagKnowledgeBaseCards(popover, status, entries, providerDocs);
renderKnowledgeRagKnowledgeBaseHero(popover, status, entries, providerDocs);
var processingCount = knowledgeRagInFlightCount(entries, providerDocs);
var bases = knowledgeRagKnowledgeBases(status || {});
var selectedBaseId = selectedKnowledgeRagProviderKnowledgeBaseId(status || {});
var activeBase = bases.find(function(base) { return base.providerKbId === selectedBaseId; }) || bases[0] || null;
var activeStats = activeBase ? knowledgeRagBaseStats(activeBase, entries, providerDocs) : null;
var activeEntries = activeBase ? knowledgeRagEntriesForKb(entries, activeBase.providerKbId) : entries;
var activeProviderDocs = activeBase ? knowledgeRagProviderDocsForKb(providerDocs, activeBase.providerKbId) : providerDocs;
statusNode.textContent = healthy
? providerLabel + ' 就绪,MNote 登记 ' + entries.length + ' 个来源,provider 文档 ' + providerDocs.length + ' 个'
: providerLabel + ' 未就绪';
? (activeBase
? activeBase.name + ' · 服务可用 · ' + activeStats.sourceCount + ' 个资料' + (activeStats.processingCount ? ' · ' + activeStats.processingCount + ' 个处理中' : '')
: '知识库服务可用 · ' + entries.length + ' 个资料' + (processingCount ? ' · ' + processingCount + ' 个处理中' : ''))
: '知识库服务不可用,页面仍可编辑';
if (metaNode instanceof HTMLElement) {
var dashboardUrl = publicKnowledgeRagDashboardUrl(status);
var inputDir = String((status.legacyFallback && status.legacyFallback.inputDir) || status.inputDir || '');
@@ -2072,15 +2186,15 @@ export function createSidebarPageSettingsRuntime(context) {
: 'unknown';
metaNode.innerHTML = '' +
knowledgeRagKbSummaryHtml(status, entries, providerDocs) +
'<div><span>Provider</span><code>' + escapeHtml(providerLabel) + '</code></div>' +
'<div><span>Boundary</span><code>MNote source registry / LightRAG sidecar locator</code></div>' +
'<div><span>Dashboard</span><code>' + escapeHtml(dashboardUrl || '未配置') + '</code></div>' +
'<div><span>Model</span><code>' + escapeHtml(llmModel || '未上报') + (vlmModel ? ' / VLM ' + escapeHtml(vlmModel) : '') + '</code></div>' +
'<div><span>服务</span><code>' + escapeHtml(providerLabel) + '</code></div>' +
'<div><span>边界</span><code>MNote source registry / LightRAG sidecar locator</code></div>' +
'<div><span>控制台</span><code>' + escapeHtml(dashboardUrl || '未配置') + '</code></div>' +
'<div><span>模型</span><code>' + escapeHtml(llmModel || '未上报') + (vlmModel ? ' / VLM ' + escapeHtml(vlmModel) : '') + '</code></div>' +
'<div><span>Rerank</span><code>' + escapeHtml(rerankLabel + (rerankBinding ? ' · ' + rerankBinding : '') + (rerankModel ? ' · ' + rerankModel : '')) + '</code></div>' +
'<div><span>Input</span><code>' + escapeHtml(inputDir || '未配置') + '</code></div>' +
'<div><span>Storage</span><code>' + escapeHtml(workingDir || '未上报') + '</code></div>';
'<div><span>输入目录</span><code>' + escapeHtml(inputDir || '未配置') + '</code></div>' +
'<div><span>存储目录</span><code>' + escapeHtml(workingDir || '未上报') + '</code></div>';
}
renderKnowledgeRagSources(popover, entries, status);
renderKnowledgeRagSources(popover, activeEntries, status, activeProviderDocs, activeBase);
if (dashboardButton instanceof HTMLButtonElement) dashboardButton.disabled = !publicKnowledgeRagDashboardUrl(status);
}
@@ -2165,8 +2279,11 @@ export function createSidebarPageSettingsRuntime(context) {
function addKnowledgeRagSourceInput() {
var popover = ensureKnowledgeRagSettingsPopover();
var node = popover.querySelector('[data-testid="mnote-knowledge-rag-source-inputs"]');
var composer = popover.querySelector('[data-knowledge-rag-source-composer]');
if (!(node instanceof HTMLElement)) return;
node.insertAdjacentHTML('beforeend', knowledgeRagSourceInputRow(''));
var shouldAppend = !(composer instanceof HTMLElement && composer.hidden);
setKnowledgeRagSourceComposerVisible(true);
if (shouldAppend) 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();
@@ -2202,6 +2319,7 @@ export function createSidebarPageSettingsRuntime(context) {
var popover = ensureKnowledgeRagSettingsPopover();
var node = popover.querySelector('[data-testid="mnote-knowledge-rag-source-inputs"]');
if (!(node instanceof HTMLElement)) return;
setKnowledgeRagSourceComposerVisible(true);
var existing = knowledgeRagSourceInputValues();
var next = existing.concat(selectedPaths).filter(function(value, index, list) {
return list.indexOf(value) === index;
@@ -2249,6 +2367,78 @@ export function createSidebarPageSettingsRuntime(context) {
if (node instanceof HTMLElement) node.innerHTML = knowledgeRagSourceInputRow('');
}
function setKnowledgeRagCreateCardVisible(visible) {
var popover = ensureKnowledgeRagSettingsPopover();
var card = popover.querySelector('[data-testid="mnote-weknora-kb-controls"]');
var statusNode = popover.querySelector('[data-knowledge-rag-create-kb-status]');
if (card instanceof HTMLElement) card.hidden = !visible;
if (statusNode instanceof HTMLElement && visible) statusNode.hidden = true;
if (visible) {
var input = popover.querySelector('[data-knowledge-rag-create-kb-name]');
if (input instanceof HTMLInputElement) input.focus();
}
}
function toggleKnowledgeRagCreateCard() {
var popover = ensureKnowledgeRagSettingsPopover();
var card = popover.querySelector('[data-testid="mnote-weknora-kb-controls"]');
setKnowledgeRagCreateCardVisible(!(card instanceof HTMLElement) || card.hidden);
}
function setKnowledgeRagCreateStatus(text, status) {
var popover = ensureKnowledgeRagSettingsPopover();
var statusNode = popover.querySelector('[data-knowledge-rag-create-kb-status]');
if (statusNode instanceof HTMLElement) {
statusNode.textContent = String(text || '');
statusNode.setAttribute('data-status', status || 'done');
statusNode.hidden = !text;
}
}
async function deleteKnowledgeRagKnowledgeBase() {
var status = currentKnowledgeRagSummary().status || {};
var bases = knowledgeRagKnowledgeBases(status);
var selected = selectedKnowledgeRagProviderKnowledgeBaseId(status);
if (!selected || selected === 'lightrag-default') {
setKnowledgeRagCreateStatus('默认资料库不能删除', 'failed');
return;
}
var activeBase = bases.find(function(base) { return base.providerKbId === selected; }) || null;
var registry = status.registry && typeof status.registry === 'object' ? status.registry : {};
var entries = Array.isArray(registry.entries) ? registry.entries : [];
var activeStats = activeBase ? knowledgeRagBaseStats(activeBase, entries, knowledgeRagProviderDocuments(status)) : null;
if (activeStats && (activeStats.sourceCount > 0 || activeStats.processingCount > 0)) {
setKnowledgeRagCreateStatus('请先移除资料后再删除资料库', 'failed');
return;
}
setKnowledgeRagCreateStatus('删除中', 'running');
try {
var response = await fetch('/api/knowledge-rag/knowledge-bases', {
method: 'DELETE',
headers: { 'content-type': 'application/json', accept: 'application/json' },
body: JSON.stringify({
workspaceId: resolveWorkspaceId(document.body),
rootUri: currentRootUri(),
providerKnowledgeBaseId: selected
})
});
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_kb_' + response.status);
}
pageUiState.knowledgeRagOptimisticBases = knowledgeRagOptimisticBases().filter(function(base) {
return knowledgeRagBaseProviderKbId(base) !== selected;
});
pageUiState.knowledgeRagSelectedProviderKnowledgeBaseId = 'lightrag-default';
setKnowledgeRagCreateStatus('已删除资料库', 'done');
await loadKnowledgeRagStatus(true, { reason: 'delete-kb', notifyUpdated: true });
renderKnowledgeRagSettings();
notifyKnowledgeRagSourceUpdated({ action: 'delete-kb', result: payload, providerKnowledgeBaseId: selected });
} catch (error) {
setKnowledgeRagCreateStatus(error instanceof Error ? error.message : String(error), 'failed');
}
}
async function createKnowledgeRagKnowledgeBase() {
if (!knowledgeRagIsAvailable()) return;
var popover = ensureKnowledgeRagSettingsPopover();
@@ -2259,7 +2449,7 @@ export function createSidebarPageSettingsRuntime(context) {
var description = descriptionInput instanceof HTMLInputElement ? descriptionInput.value.trim() : '';
if (!name) {
if (statusNode instanceof HTMLElement) {
statusNode.textContent = '请填写 KB 名称';
statusNode.textContent = '请填写资料库名称';
statusNode.setAttribute('data-status', 'failed');
statusNode.hidden = false;
}
@@ -2308,6 +2498,7 @@ export function createSidebarPageSettingsRuntime(context) {
statusNode.setAttribute('data-status', 'done');
statusNode.hidden = false;
}
setKnowledgeRagCreateCardVisible(false);
await loadKnowledgeRagStatus(true, { reason: 'create-kb', notifyUpdated: true });
if (providerKnowledgeBaseId) {
pageUiState.knowledgeRagSelectedProviderKnowledgeBaseId = providerKnowledgeBaseId;
@@ -2385,6 +2576,7 @@ export function createSidebarPageSettingsRuntime(context) {
setKnowledgeRagInputStatuses(done, 'done');
await loadKnowledgeRagStatus(true);
resetKnowledgeRagSourceInputs();
setKnowledgeRagSourceComposerVisible(false);
notifyKnowledgeRagSourceUpdated({ action: 'ingest', sources: sources, result: payload });
scheduleKnowledgeRagStatusBridge('ingest');
} catch (error) {
@@ -2590,10 +2782,10 @@ export function createSidebarPageSettingsRuntime(context) {
if (!input || !resultsEl) return;
var query = (input.value || '').trim();
if (!query) {
resultsEl.innerHTML = '<span class="mnote-extensions-error">请输入检索关键词</span>';
resultsEl.innerHTML = '<div class="mnote-kb-search-empty">请输入检索关键词</div>';
return;
}
resultsEl.innerHTML = '<span class="mnote-extensions-loading">检索中...</span>';
resultsEl.innerHTML = '<div class="mnote-kb-search-empty">检索中...</div>';
try {
var params = new URLSearchParams();
params.set('query', query);
@@ -2623,7 +2815,7 @@ export function createSidebarPageSettingsRuntime(context) {
? data.results
: (Array.isArray(data.references) ? data.references : (Array.isArray(data) ? data : []));
if (!results.length) {
resultsEl.innerHTML = '<div class="mnote-extensions-item"><span>未找到匹配结果</span></div>';
resultsEl.innerHTML = '<div class="mnote-kb-search-empty">未找到匹配结果</div>';
return;
}
var html = '';
@@ -2640,7 +2832,7 @@ export function createSidebarPageSettingsRuntime(context) {
html += '<div class="mnote-kb-search-result-item" data-kb-rag-search-result="true" data-knowledge-rag-provider="' + escapeHtml(knowledgeRagProviderName(currentKnowledgeRagSummary().status || {})) + '" data-knowledge-rag-kb-id="' + escapeHtml(providerKnowledgeBaseId) + '" data-knowledge-rag-knowledge-id="' + escapeHtml(providerKnowledgeId) + '" data-knowledge-rag-chunk-id="' + escapeHtml(providerChunkId) + '" data-knowledge-rag-source-path="' + escapeHtml(String(source)) + '">' +
'<div class="mnote-kb-search-result-header">' +
'<span class="mnote-kb-search-result-index">#' + (i + 1) + '</span>' +
'<span class="mnote-kb-search-result-score" data-kb-rag-score-label="provider-ranking-score" data-kb-rag-score-is-similarity-percent="false">provider 排序分: ' + score + '</span>' +
'<span class="mnote-kb-search-result-score" data-kb-rag-score-label="relevance" data-kb-rag-score-is-similarity-percent="false">相关度 ' + score + '</span>' +
'</div>' +
'<div class="mnote-kb-search-result-source">' + escapeHtml(String(source)) + '</div>' +
'<div class="mnote-kb-search-result-preview">' + escapeHtml(preview) + (content.length > 200 ? '...' : '') + '</div>' +
@@ -2650,7 +2842,7 @@ export function createSidebarPageSettingsRuntime(context) {
});
resultsEl.innerHTML = html;
} catch (err) {
resultsEl.innerHTML = '<div class="mnote-extensions-item"><span class="mnote-extensions-error">检索失败: ' + escapeHtml(String(err.message || err)) + '</span></div>';
resultsEl.innerHTML = '<div class="mnote-kb-search-empty is-error">检索失败' + escapeHtml(String(err.message || err)) + '</div>';
}
}
@@ -3312,6 +3504,7 @@ export function createSidebarPageSettingsRuntime(context) {
loadKnowledgeRagStatus,
openKnowledgeRagDashboard,
createKnowledgeRagKnowledgeBase,
deleteKnowledgeRagKnowledgeBase,
loadKnowledgeRagSearchParamsToForm,
performKnowledgeRagSearch,
saveKnowledgeRagSearchParams,
@@ -3323,6 +3516,7 @@ export function createSidebarPageSettingsRuntime(context) {
setKnowledgeRagSelectedProviderKnowledgeBaseId,
setKnowledgeRagSourceFilter,
useKnowledgeRagFileTreeSelection,
toggleKnowledgeRagCreateCard,
togglePageSettingsPopover,
updatePageSettingsTriggerState,
writeGlobalShowHeadingNumbers,
@@ -41,6 +41,51 @@ export const createSidebarPageTreeRuntime = (dependencies = {}) => {
};
}
function selectSidebarPageTreeDocument(nodeId, options) {
var normalizedNodeId = String(nodeId || '').trim();
if (!normalizedNodeId) return false;
var targetRow = document.querySelector(
'#sidebar-tree-root .tree-row[data-shell-mode="page"][data-node-id="' + cssEscape(normalizedNodeId) + '"]'
);
if (!(targetRow instanceof HTMLElement)) return false;
var parentId = String(targetRow.getAttribute('data-parent-id') || '').trim();
var guard = 0;
while (parentId && guard < 512) {
var parentRow = document.querySelector(
'#sidebar-tree-root .tree-row[data-shell-mode="page"][data-node-id="' + cssEscape(parentId) + '"]'
);
if (!(parentRow instanceof HTMLElement)) break;
if (parentRow.getAttribute('aria-expanded') !== 'true') {
toggleChildren(parentRow, parentRow.querySelector('[data-rust-action="toggle"]'));
}
parentId = String(parentRow.getAttribute('data-parent-id') || '').trim();
guard += 1;
}
document.querySelectorAll('#sidebar-tree-root .tree-row[data-shell-mode="page"][data-active="true"]').forEach(function(row) {
if (!(row instanceof HTMLElement)) return;
row.setAttribute('data-active', 'false');
row.setAttribute('aria-selected', 'false');
row.removeAttribute('aria-current');
});
targetRow.setAttribute('data-active', 'true');
targetRow.setAttribute('aria-selected', 'true');
targetRow.setAttribute('aria-current', 'page');
if (!options || options.scrollIntoView !== false) {
try { targetRow.scrollIntoView({ block: 'nearest' }); } catch (_) {}
}
return true;
}
function selectActiveTreeDocument(nodeId, treeView, options) {
if (treeView === 'filetree') {
selectSidebarFileTreeDocument(nodeId, options);
return;
}
selectSidebarPageTreeDocument(nodeId, options);
}
function navigateToDocument(nodeId, workspaceId, options) {
if (!nodeId) return;
var treeView = normalizeSidebarTreeMode(options && options.treeView ? options.treeView : activeSidebarTreeMode());
@@ -49,22 +94,7 @@ export const createSidebarPageTreeRuntime = (dependencies = {}) => {
if (typeof window.__mnoteDocumentPaneRuntime?.activatePageTab === 'function') {
window.__mnoteDocumentPaneRuntime.activatePageTab();
}
document.querySelectorAll('.tree-row[data-active="true"], .tree-row[data-selected="true"]').forEach(function(row) {
if (row instanceof HTMLElement) {
row.setAttribute('data-active', 'false');
row.setAttribute('data-selected', 'false');
}
});
document.querySelectorAll('.tree-row[data-node-id="' + cssEscape(nodeId) + '"], .tree-row[data-document-id="' + cssEscape(nodeId) + '"], .tree-row[data-doc-id="' + cssEscape(nodeId) + '"]').forEach(function(row) {
if (row instanceof HTMLElement) {
if (row.getAttribute('data-shell-mode') === 'filetree') {
row.setAttribute('data-selected', String(row.getAttribute('data-row-id') === 'doc:' + nodeId));
} else {
row.setAttribute('data-active', 'true');
}
}
});
selectSidebarFileTreeDocument(nodeId, { scrollIntoView: true });
selectActiveTreeDocument(nodeId, treeView, { scrollIntoView: true });
return;
}
var targetUrl = new URL('/documents/' + encodeURIComponent(nodeId), window.location.origin);
@@ -88,7 +118,9 @@ export const createSidebarPageTreeRuntime = (dependencies = {}) => {
}).then(function(){
if (mnoteNavigationInFlight === url) mnoteNavigationInFlight = '';
document.documentElement.removeAttribute('data-mnote-navigation-pending');
selectSidebarFileTreeDocument(nodeId, { scrollIntoView: true });
if (treeView === 'filetree') {
selectSidebarFileTreeDocument(nodeId, { scrollIntoView: true });
}
}).catch(function(error){
console.warn('mnote pane 内导航失败,将回退整页导航', error);
if (window.__mnoteTreeLiveEventSource && typeof window.__mnoteTreeLiveEventSource.close === 'function') {
@@ -110,6 +142,7 @@ export const createSidebarPageTreeRuntime = (dependencies = {}) => {
async function createPage(trigger, parentId) {
var workspaceId = resolveWorkspaceId(trigger);
var effectiveParentId = (parentId || trigger.getAttribute('data-parent-id') || '').trim();
var treeView = activeSidebarTreeMode();
if (!workspaceId) return;
var result = await dispatchTreeCommand(trigger, {
action: 'create',
@@ -126,10 +159,12 @@ export const createSidebarPageTreeRuntime = (dependencies = {}) => {
if (typeof refreshLocalFolderAfterCommand === 'function') {
await refreshLocalFolderAfterCommand('create', result, { parentId: effectiveParentId || null });
}
selectSidebarFileTreeDocument(nextDocumentId, localCreateSelectionOptions(result));
if (treeView === 'filetree') {
selectSidebarFileTreeDocument(nextDocumentId, localCreateSelectionOptions(result));
}
document.documentElement.setAttribute('data-mnote-create-page-selected-document-id', nextDocumentId);
}
navigateToDocument(nextDocumentId, nextWorkspaceId, { treeView: activeSidebarTreeMode() });
navigateToDocument(nextDocumentId, nextWorkspaceId, { treeView: treeView });
}
function startPageDrag(pageRow, event) {
@@ -259,6 +294,7 @@ export const createSidebarPageTreeRuntime = (dependencies = {}) => {
return {
navigateToDocument,
createPage,
selectSidebarPageTreeDocument,
startPageDrag,
readPageDragNodeId,
clearPageDropFeedback,
@@ -46,6 +46,7 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
var fileTreeVisibleHydrateTimer = 0;
var fileTreeVisibleHydrateQueued = false;
var fileTreeCommandBatchRefreshParents = new Map();
var fileTreeRevealRequests = new Map();
var FILETREE_VISIBLE_EXPANDED_HYDRATE_MAX = 20;
var FILETREE_VISIBLE_EXPANDED_HYDRATE_BATCH = 2;
var FILETREE_EXPANSION_STORAGE_KEY = 'mnote.localFileTree.expandedRelativePaths.v1';
@@ -1795,32 +1796,46 @@ export const createSidebarTreeLiveApplyRuntime = (dependencies = {}) => {
ensureFileTreeLazyCacheScope();
var relativePath = normalizeFileTreeRelativePath(input.relativePath || input.relative_path || '');
var rowId = String(input.rowId || input.row_id || '').trim();
var parentChain = fileTreeParentChainForRelativePath(relativePath);
for (var index = 0; index < parentChain.length; index += 1) {
var parentRelativePath = parentChain[index];
var parentRow = fileTreeRowByRelativePath(parentRelativePath);
if (!(parentRow instanceof HTMLElement)) continue;
var button = parentRow.querySelector('[data-rust-action="toggle"]');
await getFileTreeChildren(parentRelativePath);
renderCachedFileTreeChildren(parentRow, button, parentRelativePath);
setTreeRowExpanded(parentRow, button, true);
}
var targetRow = rowId
? document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(rowId) + '"]')
: null;
if (!(targetRow instanceof HTMLElement) && relativePath) {
targetRow = fileTreeRowByRelativePath(relativePath);
}
if (!(targetRow instanceof HTMLElement)) return false;
var targetRowId = String(targetRow.getAttribute('data-row-id') || rowId || '').trim();
if (targetRowId) {
fileTreeViewState.selectedRowIds = new Set([targetRowId]);
fileTreeViewState.focusedRowId = targetRowId;
fileTreeViewState.activeRowId = targetRowId;
}
reprojectFileTreeSelectionState();
try { targetRow.scrollIntoView({ block: 'nearest' }); } catch (_) {}
return true;
var requestKey = [rootUri, currentFileTreeScope(), relativePath, rowId].join('\n');
if (fileTreeRevealRequests.has(requestKey)) return fileTreeRevealRequests.get(requestKey);
var request = (async function() {
var parentChain = fileTreeParentChainForRelativePath(relativePath);
for (var index = 0; index < parentChain.length; index += 1) {
var parentRelativePath = parentChain[index];
var parentRow = fileTreeRowByRelativePath(parentRelativePath);
if (!(parentRow instanceof HTMLElement)) continue;
var button = parentRow.querySelector('[data-rust-action="toggle"]');
var childrenLoaded = parentRow.getAttribute('data-filetree-children-loaded') === 'true';
var expanded = parentRow.getAttribute('aria-expanded') === 'true';
if (!childrenLoaded) {
await getFileTreeChildren(parentRelativePath);
renderCachedFileTreeChildren(parentRow, button, parentRelativePath);
}
if (!expanded) {
setTreeRowExpanded(parentRow, button, true);
}
}
var targetRow = rowId
? document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-row-id="' + cssEscape(rowId) + '"]')
: null;
if (!(targetRow instanceof HTMLElement) && relativePath) {
targetRow = fileTreeRowByRelativePath(relativePath);
}
if (!(targetRow instanceof HTMLElement)) return false;
var targetRowId = String(targetRow.getAttribute('data-row-id') || rowId || '').trim();
if (targetRowId) {
fileTreeViewState.selectedRowIds = new Set([targetRowId]);
fileTreeViewState.focusedRowId = targetRowId;
fileTreeViewState.activeRowId = targetRowId;
}
reprojectFileTreeSelectionState();
try { targetRow.scrollIntoView({ block: 'nearest' }); } catch (_) {}
return true;
})().finally(function() {
fileTreeRevealRequests.delete(requestKey);
});
fileTreeRevealRequests.set(requestKey, request);
return request;
}
function toggleChildren(row, button) {
@@ -221,6 +221,8 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
const loadKnowledgeRagStatus = (...args) => sidebarPageSettings.loadKnowledgeRagStatus(...args);
const openKnowledgeRagDashboard = (...args) => sidebarPageSettings.openKnowledgeRagDashboard(...args);
const createKnowledgeRagKnowledgeBase = (...args) => sidebarPageSettings.createKnowledgeRagKnowledgeBase(...args);
const deleteKnowledgeRagKnowledgeBase = (...args) => sidebarPageSettings.deleteKnowledgeRagKnowledgeBase(...args);
const toggleKnowledgeRagCreateCard = (...args) => sidebarPageSettings.toggleKnowledgeRagCreateCard(...args);
const setActiveKnowledgeRagDetailTab = (...args) => sidebarPageSettings.setActiveKnowledgeRagDetailTab(...args);
const performKnowledgeRagSearch = (...args) => sidebarPageSettings.performKnowledgeRagSearch(...args);
const saveKnowledgeRagSearchParams = (...args) => sidebarPageSettings.saveKnowledgeRagSearchParams(...args);
@@ -367,6 +369,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
openPageTreeContextMenu: (...args) => openPageTreeContextMenu(...args),
updateTitleEverywhere: (...args) => updateTitleEverywhere(...args),
});
const selectSidebarPageTreeDocument = (...args) => sidebarPageTree.selectSidebarPageTreeDocument(...args);
const fileTreePageTitle = (...args) => sidebarTreeLiveApply.fileTreePageTitle(...args);
const isFileTreePageRow = (...args) => sidebarTreeLiveApply.isFileTreePageRow(...args);
const normalizeFileTreePageRenameTitle = (...args) => sidebarTreeLiveApply.normalizeFileTreePageRenameTitle(...args);
@@ -476,6 +479,22 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
return true;
}
function knowledgeHostUrl(options) {
options = options || {};
var knowledgeUrl = new URL('/knowledge', window.location.origin);
var workspaceId = String(options.workspaceId || currentWorkspaceId() || resolveWorkspaceId(document.body) || '').trim();
var sourceKind = String(options.sourceKind || currentSourceKind() || '').trim();
var rootUri = String(options.rootUri || currentRootUri() || '').trim();
var treeView = String(options.treeView || activeSidebarTreeMode() || '').trim();
var fileTreeScope = String(options.fileTreeScope || currentFileTreeScope() || '').trim();
if (workspaceId) knowledgeUrl.searchParams.set('workspaceId', workspaceId);
if (sourceKind) knowledgeUrl.searchParams.set('sourceKind', sourceKind);
if (rootUri) knowledgeUrl.searchParams.set('rootUri', rootUri);
if (treeView) knowledgeUrl.searchParams.set('treeView', treeView);
if (fileTreeScope) knowledgeUrl.searchParams.set('fileTreeScope', fileTreeScope);
return knowledgeUrl.pathname + knowledgeUrl.search;
}
function currentTopbarTitle() {
var title = document.querySelector('[data-page-title-current="true"]');
return title && title.textContent ? title.textContent.trim() : '无标题';
@@ -2058,6 +2077,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
openEditorAttachmentEditTab: (...args) => openEditorAttachmentEditTab(...args),
openEditorAttachmentNewWindow: (...args) => openEditorAttachmentNewWindow(...args),
openLocalResourceInActiveTab: (...args) => openLocalResourceInActiveTab(...args),
knowledgeHostUrl,
refreshLocalFolderSidebarSnapshot,
removeFileTreeAssetRow,
revealFileTreeResource,
@@ -2162,7 +2182,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
'<span class="wolai-search-sort-control"><span>按创建时间</span><button type="button" class="wolai-search-sort-value" data-search-sort="created" aria-label="按创建时间范围">所有</button></span>' +
'</div>' +
'<div class="wolai-search-options-right">' +
'<span class="wolai-search-switch-control"><span>全盘知识库</span><button type="button" class="wolai-search-switch is-on" data-search-switch="knowledge" role="switch" aria-checked="true" aria-label="全盘知识库检索"></button></span>' +
'<span class="wolai-search-switch-control"><span>全盘知识库</span><button type="button" class="wolai-search-switch" data-search-switch="knowledge" role="switch" aria-checked="false" aria-label="全盘知识库检索"></button></span>' +
'<span class="wolai-search-switch-control"><span>页面内搜索</span><button type="button" class="wolai-search-switch" data-search-switch="page" role="switch" aria-checked="false" aria-label="页面内搜索"></button></span>' +
'<span class="wolai-search-switch-control"><span>折叠同来源</span><button type="button" class="wolai-search-switch is-on" data-search-switch="collapseSource" role="switch" aria-checked="true" aria-label="默认折叠相同来源结果"></button></span>' +
'</div>' +
@@ -2174,7 +2194,7 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
document.body.appendChild(overlay);
var input = overlay.querySelector('[data-testid="wolai-search-input"]');
var closeButton = overlay.querySelector('[data-testid="wolai-search-close"]');
applySearchSwitchState(overlay, { knowledge: true, collapseSource: readSearchCollapseSourcesDefault() });
applySearchSwitchState(overlay, { knowledge: false, collapseSource: readSearchCollapseSourcesDefault() });
var initialKnowledgeModeSelect = overlay.querySelector('[data-search-knowledge-mode]');
if (initialKnowledgeModeSelect && 'value' in initialKnowledgeModeSelect) initialKnowledgeModeSelect.value = 'exact';
if (input) input.addEventListener('input', function() {
@@ -2986,10 +3006,20 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
return;
}
var knowledgeHostTrigger = closestAction(e.target, '[data-mnote-action="open-knowledge-host"]');
if (knowledgeHostTrigger) {
var hostUrl = knowledgeHostUrl();
if (knowledgeHostTrigger instanceof HTMLAnchorElement) knowledgeHostTrigger.setAttribute('href', hostUrl);
if (knowledgeHostTrigger instanceof HTMLAnchorElement && (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1)) return;
e.preventDefault();
window.location.href = hostUrl;
return;
}
var knowledgeRagSettingsTrigger = closestAction(e.target, '[data-mnote-action="open-knowledge-rag-settings"]');
if (knowledgeRagSettingsTrigger) {
e.preventDefault();
window.location.href = '/knowledge';
window.location.href = knowledgeHostUrl();
return;
}
@@ -3069,7 +3099,16 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
void createKnowledgeRagKnowledgeBase();
return;
}
if (knowledgeRagActionName === 'delete-kb') {
void deleteKnowledgeRagKnowledgeBase();
return;
}
if (knowledgeRagActionName === 'toggle-create-kb') {
toggleKnowledgeRagCreateCard();
return;
}
if (knowledgeRagActionName === 'focus-create-kb') {
toggleKnowledgeRagCreateCard();
var createKbInput = document.querySelector('[data-knowledge-rag-create-kb-name]');
if (createKbInput instanceof HTMLInputElement) createKbInput.focus();
return;
@@ -3870,21 +3909,44 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
});
syncSidebarFileTreeSelection();
schedulePendingLocalFolderRestoreFocus();
window.addEventListener('mnote:primary-document-activated', function(event) {
var detail = event && event.detail ? event.detail : {};
var recentPageOptionsApply = { documentId: '', at: 0 };
function applyPrimaryPageOptionsOnce(documentId) {
var normalizedDocumentId = searchText(documentId || currentDocumentId() || '');
pageUiState.pageOptions = null;
applyPageOptionsToShell();
if (isPageSettingsOpen()) renderPageSettingsPopover();
selectSidebarFileTreeDocument(detail.documentId, { scrollIntoView: false });
recentPageOptionsApply = { documentId: normalizedDocumentId, at: Date.now() };
}
function hasRecentPageOptionsApply(documentId) {
var normalizedDocumentId = searchText(documentId || currentDocumentId() || '');
return normalizedDocumentId
&& recentPageOptionsApply.documentId === normalizedDocumentId
&& Date.now() - recentPageOptionsApply.at < 1000;
}
window.addEventListener('mnote:primary-document-activated', function(event) {
var detail = event && event.detail ? event.detail : {};
if (!hasRecentPageOptionsApply(detail.documentId)) {
applyPrimaryPageOptionsOnce(detail.documentId);
}
if (detail.pageBlockNavigation === true) {
detail.sidebarSelectionHandled = false;
return;
}
if (activeSidebarTreeMode() === 'filetree') {
detail.sidebarSelectionHandled = selectSidebarFileTreeDocument(detail.documentId, {
rootUri: detail.rootUri || currentRootUri(),
scrollIntoView: false
});
} else {
detail.sidebarSelectionHandled = selectSidebarPageTreeDocument(detail.documentId, { scrollIntoView: false });
}
});
window.addEventListener('mnote:page-aggregate-synced', function(event) {
var detail = event && event.detail ? event.detail : {};
if (detail.scriptId && detail.scriptId !== '__MNOTE_PAGE_AGGREGATE__') return;
var documentId = searchText(detail.documentId || '');
if (documentId && currentDocumentId() && documentId !== currentDocumentId()) return;
pageUiState.pageOptions = null;
applyPageOptionsToShell();
if (isPageSettingsOpen()) renderPageSettingsPopover();
applyPrimaryPageOptionsOnce(documentId);
});
restoreSidebarTreeTab();
if (window.location.pathname === '/debug/knowledge-rag' || window.location.pathname === '/knowledge') {