chore: checkpoint turso and ai runtime work

This commit is contained in:
Agent Board
2026-07-03 23:20:16 +08:00
parent a75b3d11f9
commit 36d027a4a1
67 changed files with 4224 additions and 914 deletions
@@ -415,7 +415,7 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
|| String(detail && (detail.localRelativePath || detail.path || detail.title) || '').trim();
var rootUri = knowledgeRagRootUri(detail, trigger);
if (!sourceRootRelativePath || !rootUri) {
throw new Error('缺少资料库来源或 rootUri');
throw new Error('缺少知识库来源或 rootUri');
}
var workspaceId = String(detail && detail.workspaceId || resolveWorkspaceId(document.body) || '').trim();
document.documentElement.setAttribute('data-mnote-knowledge-rag-menu-status', 'running');
@@ -426,12 +426,14 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
body: JSON.stringify({
workspaceId: workspaceId,
rootUri: rootUri,
sources: [{ sourcePath: sourceRootRelativePath }]
sources: [{ sourcePath: sourceRootRelativePath }],
knowledgeBaseId: String(detail && detail.kbId || '').trim() || undefined,
providerKnowledgeBaseId: String(detail && (detail.providerKbId || detail.kbId) || '').trim() || undefined
})
});
var payload = await response.json().catch(function() { return null; });
if (!response.ok || !payload || payload.ok === false) {
var message = payload && payload.message ? payload.message : '资料库索引失败';
var message = payload && payload.message ? payload.message : '知识库索引失败';
document.documentElement.setAttribute('data-mnote-knowledge-rag-menu-status', 'failed');
throw new Error(message);
}
@@ -450,12 +452,30 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
recordFileTreeActionStatus('pending', detail);
void ingestKnowledgeRagForDetail(detail, trigger).then(function() {
recordFileTreeActionStatus('done', detail);
if (window.mnote && window.mnote.toast) {
window.mnote.toast('已加入知识库', { kind: 'success' });
}
}).catch(function(error) {
var msg = error && error.message ? error.message : '知识库索引失败';
recordFileTreeActionStatus('failed', Object.assign({}, detail, { fallback: 'alert' }));
window.alert(error && error.message ? error.message : '资料库索引失败');
if (window.mnote && window.mnote.toast) {
window.mnote.toast(msg, { kind: 'error' });
} else {
window.alert(msg);
}
});
return;
}
if (action === 'open-knowledge-page') {
var knowledgeUrl = '/knowledge';
if (typeof window.open === 'function') {
window.open(knowledgeUrl, '_blank', 'noopener,noreferrer');
} else {
window.location.href = knowledgeUrl;
}
return;
}
if (detail.contextKind === 'attachment') {
if (action === 'copy-link') {
void copyTreeContextValue(detail.href || '', 'attachment-copy-link');
@@ -683,6 +703,148 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
menu.appendChild(sep);
return;
}
// Submenu root: render parent button with child flyout
if (item.children && Array.isArray(item.children) && item.children.length > 0) {
if (typeof injectKnowledgeSubmenuStyles === 'function') injectKnowledgeSubmenuStyles();
var rootBtn = document.createElement('button');
rootBtn.type = 'button';
rootBtn.className = 'mnote-tree-context-menu__item mnote-tree-context-menu__item--submenu-root';
rootBtn.setAttribute('role', 'menuitem');
rootBtn.setAttribute('data-action', item.action || 'knowledge-rag-root');
rootBtn.setAttribute('data-command-id', item.commandId || item.action || 'knowledge-rag-root');
if (item.when) rootBtn.setAttribute('data-command-when', item.when);
if (item.danger || item.destructive) rootBtn.setAttribute('data-destructive', 'true');
if (item.requiresApproval) rootBtn.setAttribute('data-requires-approval', 'true');
rootBtn.disabled = item.disabled === true;
if (item.title) rootBtn.title = item.title;
if (item.disabled === true && item.title) rootBtn.setAttribute('data-disabled-reason', item.title);
var rootIcon = document.createElement('span');
rootIcon.className = 'material-symbols-outlined mnote-tree-context-menu__icon';
rootIcon.setAttribute('aria-hidden', 'true');
rootIcon.setAttribute('data-icon', item.icon || 'radio_button_unchecked');
var rootLabel = document.createElement('span');
rootLabel.className = 'mnote-tree-context-menu__label';
rootLabel.textContent = item.label;
var arrow = document.createElement('span');
arrow.className = 'mnote-tree-context-menu__submenu-arrow';
arrow.textContent = '▶';
arrow.setAttribute('aria-hidden', 'true');
rootBtn.appendChild(rootIcon);
rootBtn.appendChild(rootLabel);
rootBtn.appendChild(arrow);
var submenu = document.createElement('div');
submenu.className = 'mnote-tree-context-menu__submenu';
submenu.setAttribute('data-testid', 'mnote-knowledge-submenu');
submenu.setAttribute('role', 'menu');
function renderSubmenuChildren(children) {
submenu.textContent = '';
children.forEach(function(child) {
if (child.separator) {
var childSep = document.createElement('div');
childSep.className = 'mnote-tree-context-menu__separator';
childSep.setAttribute('role', 'separator');
submenu.appendChild(childSep);
return;
}
var childBtn = document.createElement('button');
childBtn.type = 'button';
childBtn.className = child.danger ? 'mnote-tree-context-menu__item mnote-tree-context-menu__item--danger' : 'mnote-tree-context-menu__item';
if (child.heading) childBtn.className += ' mnote-tree-context-menu__item--heading';
childBtn.setAttribute('role', 'menuitem');
childBtn.setAttribute('data-action', child.action || '');
childBtn.setAttribute('data-command-id', child.commandId || child.action || '');
if (child.when) childBtn.setAttribute('data-command-when', child.when);
childBtn.disabled = child.disabled === true || child.heading === true;
if (child.title) childBtn.title = child.title;
if (child.disabled === true && child.title) childBtn.setAttribute('data-disabled-reason', child.title);
var childIcon = document.createElement('span');
childIcon.className = 'material-symbols-outlined mnote-tree-context-menu__icon';
childIcon.setAttribute('aria-hidden', 'true');
childIcon.setAttribute('data-icon', child.icon || 'radio_button_unchecked');
var childLabel = document.createElement('span');
childLabel.className = 'mnote-tree-context-menu__label';
childLabel.textContent = child.label;
childBtn.appendChild(childIcon);
childBtn.appendChild(childLabel);
if (child.shortcut) {
var childShortcut = document.createElement('span');
childShortcut.className = 'mnote-tree-context-menu__shortcut';
childShortcut.textContent = child.shortcut;
childBtn.appendChild(childShortcut);
}
var childDetail = child.kbId ? Object.assign({}, detail, { kbId: child.kbId, providerKbId: child.providerKbId || child.kbId }) : detail;
(function(act, actDetail, actTrigger) {
childBtn.addEventListener('click', function(event) {
event.preventDefault();
event.stopPropagation();
if (!act || childBtn.disabled) return;
handleTreeContextMenuAction(act, actDetail, actTrigger);
});
})(child.action, childDetail, trigger);
submenu.appendChild(childBtn);
});
}
renderSubmenuChildren(item.children);
rootBtn.appendChild(submenu);
var submenuShown = false;
var submenuHideTimer = null;
var lastSubmenuPointerY = 0;
function knowledgeSubmenuPosition(pointerY) {
var btnRect = rootBtn.getBoundingClientRect();
submenu.style.display = 'block';
submenu.style.visibility = 'hidden';
var smRect = submenu.getBoundingClientRect();
var leftPos = btnRect.right + 4;
var anchorY = Number(pointerY || lastSubmenuPointerY || 0);
var topPos = anchorY > 0 ? anchorY - 18 : btnRect.top;
if (leftPos + smRect.width > window.innerWidth - 8) {
leftPos = Math.max(8, btnRect.left - smRect.width - 4);
}
if (topPos + smRect.height > window.innerHeight - 8) {
topPos = Math.max(8, window.innerHeight - smRect.height - 8);
}
submenu.style.left = leftPos + 'px';
submenu.style.top = topPos + 'px';
submenu.style.visibility = 'visible';
}
function knowledgeSubmenuShow(pointerY) {
if (Number(pointerY || 0) > 0) lastSubmenuPointerY = pointerY;
if (submenuHideTimer) { window.clearTimeout(submenuHideTimer); submenuHideTimer = null; }
if (!submenuShown) { knowledgeSubmenuPosition(pointerY); submenuShown = true; rootBtn.classList.add('mnote-tree-context-menu__item--submenu-open'); }
}
function knowledgeSubmenuScheduleHide() {
if (submenuHideTimer) window.clearTimeout(submenuHideTimer);
submenuHideTimer = window.setTimeout(function() {
if (submenuShown) { submenu.style.display = 'none'; submenuShown = false; rootBtn.classList.remove('mnote-tree-context-menu__item--submenu-open'); }
submenuHideTimer = null;
}, 150);
}
rootBtn.addEventListener('mouseenter', function(event) { if (!rootBtn.disabled) knowledgeSubmenuShow(event.clientY); });
rootBtn.addEventListener('mousemove', function(event) {
if (rootBtn.disabled || !submenuShown) return;
lastSubmenuPointerY = event.clientY;
});
rootBtn.addEventListener('mouseleave', function(event) {
if (event.relatedTarget && (submenu.contains(event.relatedTarget) || rootBtn.contains(event.relatedTarget))) return;
knowledgeSubmenuScheduleHide();
});
submenu.addEventListener('mouseenter', function() {
if (submenuHideTimer) { window.clearTimeout(submenuHideTimer); submenuHideTimer = null; }
});
submenu.addEventListener('mouseleave', function(event) {
if (event.relatedTarget && (rootBtn.contains(event.relatedTarget) || submenu.contains(event.relatedTarget))) return;
knowledgeSubmenuScheduleHide();
});
if (typeof item.loadChildren === 'function') {
item.loadChildren().then(function(children) {
if (!Array.isArray(children) || children.length === 0) return;
renderSubmenuChildren(children);
if (submenuShown) knowledgeSubmenuPosition(lastSubmenuPointerY);
}).catch(function() {});
}
menu.appendChild(rootBtn);
return;
}
var button = document.createElement('button');
button.type = 'button';
button.className = item.danger ? 'mnote-tree-context-menu__item mnote-tree-context-menu__item--danger' : 'mnote-tree-context-menu__item';
@@ -718,6 +880,106 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
menu.appendChild(button);
}
// ── 知识库二级菜单 CSS 注入(仅一次) ──
var __mnoteKnowledgeSubmenuStylesInjected = false;
function injectKnowledgeSubmenuStyles() {
if (__mnoteKnowledgeSubmenuStylesInjected) return;
__mnoteKnowledgeSubmenuStylesInjected = true;
var style = document.createElement('style');
style.id = 'mnote-knowledge-submenu-styles';
style.textContent = '.mnote-tree-context-menu__item--submenu-root{position:relative}.mnote-tree-context-menu__submenu{position:fixed;z-index:calc(var(--wolai-z-context-menu,9999) + 1);width:240px;min-width:220px;max-width:min(340px,calc(100vw - 16px));padding:6px;border:1px solid rgba(27,28,28,.08);border-radius:6px;background:#FFF;box-shadow:0 8px 20px rgba(27,28,28,.12);backdrop-filter:blur(24px);color:var(--atelier-text);display:none}.mnote-tree-context-menu__submenu-arrow{margin-left:auto;font-size:11px;color:#6D6A65;padding-left:12px}.mnote-tree-context-menu__item--submenu-open{background:#F4F3F3}.mnote-tree-context-menu__item--heading{font-size:12px;color:#6D6A65;background:transparent!important;cursor:default}';
document.head.appendChild(style);
}
// ── 知识库列表获取:只读 MNote 后端 registry/status,避免前端直连 WeKnora service key。 ──
function fetchKnowledgeBaseList(detail) {
var rootUri = knowledgeRagRootUri(detail, null);
if (!rootUri) return Promise.resolve([]);
var workspaceId = String(detail && detail.workspaceId || resolveWorkspaceId(document.body) || '').trim();
var sourcePath = knowledgeRagSourceRelativePath(detail);
var statusUrl = '/api/knowledge-rag/status?rootUri=' + encodeURIComponent(rootUri);
if (workspaceId) statusUrl += '&workspaceId=' + encodeURIComponent(workspaceId);
return fetch(statusUrl, { credentials: 'include', headers: { accept: 'application/json' } })
.then(function(response) { return response.ok ? response.json() : null; })
.then(function(payload) {
// 构建 knowledgeBase → 已索引 sourcePath 快速查找
var indexedByKb = {};
var registry = payload && payload.registry;
if (registry && Array.isArray(registry.entries) && sourcePath) {
registry.entries.forEach(function(entry) {
var entryKbId = String(entry.providerKnowledgeBaseId || entry.provider_kb_id || '').trim();
var entryPath = String(entry.sourceRootRelativePath || entry.source_root_relative_path || '').trim();
if (entryKbId && entryPath && entry.indexedAtMs && entryPath === sourcePath) {
if (!indexedByKb[entryKbId]) indexedByKb[entryKbId] = {};
indexedByKb[entryKbId][entryPath] = true;
}
});
}
var bases = payload && payload.knowledgeBases && Array.isArray(payload.knowledgeBases.bases)
? payload.knowledgeBases.bases
: [];
return bases.filter(function(base) {
return base && base.canWrite !== false && (base.providerKbId || base.provider_kb_id || base.baseId || base.base_id);
}).map(function(base) {
var providerKbId = String(base.providerKbId || base.provider_kb_id || '').trim();
var baseId = String(base.baseId || base.base_id || providerKbId).trim();
var kbId = providerKbId || baseId;
return {
id: baseId,
providerKbId: kbId,
name: String(base.name || baseId || providerKbId || '未命名知识库').trim(),
sourceCount: Number(base.sourceCount || base.source_count || 0) || 0,
sourceAlreadyIndexed: Boolean(sourcePath && indexedByKb[kbId] && indexedByKb[kbId][sourcePath])
};
});
}).catch(function() { return []; });
}
function buildKnowledgeRagSubmenuChildren(detail, writableBases) {
var children = [
{ action: 'knowledge-rag-index', icon: 'travel_explore', label: '索引到默认知识库', when: '!workspace.readonly' },
{ separator: true },
{ heading: true, icon: 'history', label: '最近使用知识库' }
];
if (writableBases && writableBases.length > 0) {
writableBases.slice(0, 3).forEach(function(base) {
var indexed = base.sourceAlreadyIndexed;
children.push({
action: 'knowledge-rag-index',
icon: indexed ? 'check_circle' : 'database',
label: (indexed ? '✓ ' : '') + base.name,
kbId: base.id,
providerKbId: base.providerKbId,
title: indexed ? '重新索引' : (base.sourceCount ? ('已有 ' + base.sourceCount + ' 个来源') : '加入此知识库'),
when: '!workspace.readonly'
});
});
} else {
children.push({ action: '', icon: 'database', label: '暂无可写知识库', disabled: true });
}
children.push({ heading: true, icon: 'folder_managed', label: '当前 workspace 知识库' });
(writableBases || []).slice(3).forEach(function(base) {
var indexed = base.sourceAlreadyIndexed;
children.push({
action: 'knowledge-rag-index',
icon: indexed ? 'check_circle' : 'database',
label: (indexed ? '✓ ' : '') + base.name,
kbId: base.id,
providerKbId: base.providerKbId,
title: indexed ? '重新索引' : (base.sourceCount ? ('已有 ' + base.sourceCount + ' 个来源') : '加入此知识库'),
when: '!workspace.readonly'
});
});
children.push(
{ separator: true },
{ action: 'open-knowledge-page', icon: 'add', label: '新建知识库...' },
{ action: 'open-knowledge-page', icon: 'settings', label: '管理知识库...' }
);
return children;
}
// ── CommandContext 启用态辅助:与 Rust core-protocol CommandContext 保持同一口径 ──
function currentOpenEditorsSnapshotForCommandContext() {
@@ -979,7 +1241,13 @@ export const createSidebarFileTreeCommandRuntime = (dependencies = {}) => {
{ action: 'delete-trash', icon: 'delete', label: '删除', shortcut: 'Del', danger: true, destructive: true, requiresApproval: true, when: '!workspace.readonly && !editor.dirty' }
];
if (currentSourceKind() === 'local_folder' && detail.rowKind !== 'index' && knowledgeRagSupported) {
var ragItem = { action: 'knowledge-rag-index', icon: 'travel_explore', label: '加入资料库索引', when: '!workspace.readonly' };
var ragChildren = buildKnowledgeRagSubmenuChildren(detail, []);
var ragItem = { action: 'knowledge-rag-root', icon: 'travel_explore', label: '加入知识库', children: ragChildren, when: '!workspace.readonly' };
ragItem.loadChildren = function() {
return fetchKnowledgeBaseList(detail).then(function(bases) {
return buildKnowledgeRagSubmenuChildren(detail, bases);
});
};
var insertAt = isAttachment ? 11 : isAsset ? 3 : 3;
if (insertAt >= 0) items.splice(insertAt, 0, ragItem);
}