' +
@@ -648,13 +695,22 @@ export function createSidebarPageSettingsRuntime(context) {
}
function createKnowledgeRagSettingsPanelHtml() {
+ return createKnowledgeRagSettingsPanelHtmlV2();
+ }
+
+ function createKnowledgeRagSettingsPanelHtmlV2() {
return '' +
'
' +
'
' +
'资料库问答' +
'' +
'
' +
- '
' +
+ '
' +
+ '' +
+ '' +
+ '' +
+ '
' +
+ '
' +
'
' +
'
' +
'
' +
@@ -679,6 +735,39 @@ export function createSidebarPageSettingsRuntime(context) {
'' +
'' +
'
' +
+ '
' +
+ '
' +
+ '' +
+ '' +
+ '
' +
+ '
' +
+ '输入关键词后点击搜索' +
+ '
' +
+ '
' +
+ '
' +
'
';
}
@@ -1890,6 +1979,125 @@ export function createSidebarPageSettingsRuntime(context) {
window.open(url, '_blank', 'noopener');
}
+ // --- 知识库详情 tab 切换 ---
+ function setActiveKnowledgeRagDetailTab(tabName) {
+ var popover = ensureKnowledgeRagSettingsPopover();
+ popover.querySelectorAll('[data-kb-rag-tab]').forEach(function(tab) {
+ var active = tab.getAttribute('data-kb-rag-tab') === tabName;
+ tab.classList.toggle('is-active', active);
+ tab.setAttribute('aria-selected', active ? 'true' : 'false');
+ });
+ popover.querySelectorAll('[data-kb-rag-panel]').forEach(function(panel) {
+ panel.hidden = panel.getAttribute('data-kb-rag-panel') !== tabName;
+ });
+ }
+
+ // --- 检索测试 ---
+ async function performKnowledgeRagSearch() {
+ var popover = ensureKnowledgeRagSettingsPopover();
+ var input = popover.querySelector('[data-kb-rag-search-input]');
+ var resultsEl = popover.querySelector('[data-kb-rag-search-results]');
+ if (!input || !resultsEl) return;
+ var query = (input.value || '').trim();
+ if (!query) {
+ resultsEl.innerHTML = '
请输入检索关键词';
+ return;
+ }
+ resultsEl.innerHTML = '
检索中...';
+ try {
+ var params = new URLSearchParams();
+ params.set('query', query);
+ params.set('mode', currentKnowledgeRagSearchParams().mode || 'hybrid');
+ params.set('top_k', String(currentKnowledgeRagSearchParams().top_k || 10));
+ var resp = await fetch('/api/knowledge-rag/search?' + params.toString(), {
+ method: 'POST',
+ headers: { 'content-type': 'application/json' },
+ body: JSON.stringify({ query: query })
+ });
+ if (!resp.ok) throw new Error('HTTP ' + resp.status);
+ var data = await resp.json();
+ var results = Array.isArray(data.results) ? data.results : (Array.isArray(data) ? data : []);
+ if (!results.length) {
+ resultsEl.innerHTML = '
未找到匹配结果
';
+ return;
+ }
+ var html = '';
+ results.forEach(function(r, i) {
+ var score = typeof r.score === 'number' ? r.score.toFixed(3) : (typeof r.distance === 'number' ? r.distance.toFixed(3) : '-');
+ var content = r.content || r.text || r.chunk || '';
+ var source = r.file_path || r.source || r.document_id || '';
+ var citationMarkdown = r.citationMarkdown || r.citation_markdown || '';
+ var preview = String(content).substring(0, 200);
+ html += '
' +
+ '' +
+ '
' + escapeHtml(String(source)) + '
' +
+ '
' + escapeHtml(preview) + (content.length > 200 ? '...' : '') + '
' +
+ (citationMarkdown ? '
' + escapeHtml(String(citationMarkdown).substring(0, 150)) + '
' : '') +
+ '
';
+ });
+ resultsEl.innerHTML = html;
+ } catch (err) {
+ resultsEl.innerHTML = '
检索失败: ' + escapeHtml(String(err.message || err)) + '
';
+ }
+ }
+
+ // --- 检索参数 ---
+ var _kbRagSearchParams = null;
+ function currentKnowledgeRagSearchParams() {
+ if (!_kbRagSearchParams) {
+ _kbRagSearchParams = { mode: 'hybrid', top_k: 10, similarity_threshold: 0.0 };
+ }
+ return _kbRagSearchParams;
+ }
+
+ function loadKnowledgeRagSearchParamsToForm() {
+ var popover = ensureKnowledgeRagSettingsPopover();
+ var params = currentKnowledgeRagSearchParams();
+ var modeSelect = popover.querySelector('[data-kb-rag-param="mode"]');
+ var topKInput = popover.querySelector('[data-kb-rag-param="top_k"]');
+ var thresholdRange = popover.querySelector('[data-kb-rag-param="similarity_threshold"]');
+ var thresholdValue = popover.querySelector('[data-kb-rag-param-value="similarity_threshold"]');
+ if (modeSelect) modeSelect.value = params.mode || 'hybrid';
+ if (topKInput) topKInput.value = String(params.top_k || 10);
+ if (thresholdRange) {
+ thresholdRange.value = String(params.similarity_threshold || 0);
+ if (thresholdValue) thresholdValue.textContent = String(params.similarity_threshold || 0);
+ }
+ }
+
+ function syncKnowledgeRagThresholdDisplay() {
+ var popover = ensureKnowledgeRagSettingsPopover();
+ var thresholdRange = popover.querySelector('[data-kb-rag-param="similarity_threshold"]');
+ var thresholdValue = popover.querySelector('[data-kb-rag-param-value="similarity_threshold"]');
+ if (thresholdRange && thresholdValue) {
+ var val = parseFloat(thresholdRange.value) || 0;
+ thresholdValue.textContent = val.toFixed(2);
+ }
+ }
+
+ function saveKnowledgeRagSearchParams() {
+ var popover = ensureKnowledgeRagSettingsPopover();
+ var modeSelect = popover.querySelector('[data-kb-rag-param="mode"]');
+ var topKInput = popover.querySelector('[data-kb-rag-param="top_k"]');
+ var thresholdRange = popover.querySelector('[data-kb-rag-param="similarity_threshold"]');
+ var params = {
+ mode: modeSelect ? modeSelect.value : 'hybrid',
+ top_k: topKInput ? (parseInt(topKInput.value, 10) || 10) : 10,
+ similarity_threshold: thresholdRange ? (parseFloat(thresholdRange.value) || 0) : 0
+ };
+ _kbRagSearchParams = params;
+ try {
+ fetch('/api/ui/preferences', {
+ method: 'PUT',
+ headers: { 'content-type': 'application/json' },
+ body: JSON.stringify({ updates: { knowledge_rag_search_params: params } })
+ }).catch(function(){});
+ } catch (_) {}
+ }
+
function knowledgeRagInputStatusText(status) {
if (status === 'running') return '索引中';
if (status === 'done') return '已提交';
@@ -1945,10 +2153,172 @@ export function createSidebarPageSettingsRuntime(context) {
tab.setAttribute('aria-selected', active ? 'true' : 'false');
});
popover.querySelectorAll('[data-page-settings-panel]').forEach(function(panel) {
+ if (tabName === 'extensions') {
+ refreshExtensionsPanel();
+ }
+ if (tabName === 'dashboard') {
+ refreshDashboardPanel();
+ }
+
panel.hidden = panel.getAttribute('data-page-settings-panel') !== tabName;
});
}
+ // --- 扩展面板子 tab 切换 ---
+ function setActiveExtensionsSubtab(subtabName) {
+ var popover = ensurePageSettingsPopover();
+ popover.querySelectorAll('[data-extensions-subtab]').forEach(function(tab) {
+ var active = tab.getAttribute('data-extensions-subtab') === subtabName;
+ tab.classList.toggle('is-active', active);
+ tab.setAttribute('aria-selected', active ? 'true' : 'false');
+ });
+ popover.querySelectorAll('[data-extensions-panel]').forEach(function(panel) {
+ panel.hidden = panel.getAttribute('data-extensions-panel') !== subtabName;
+ });
+ // 切换时懒加载对应面板数据
+ if (subtabName === 'knowledge') refreshKnowledgeExtensionsPanel();
+ else if (subtabName === 'skills') refreshSkillsExtensionsPanel();
+ else if (subtabName === 'mcp') refreshMcpExtensionsPanel();
+ else if (subtabName === 'tools') refreshToolsExtensionsPanel();
+ }
+
+ // 当切换到扩展 tab 时刷新当前子面板
+ function refreshExtensionsPanel() {
+ var popover = ensurePageSettingsPopover();
+ var activeSubtab = popover.querySelector('[data-extensions-subtab].is-active');
+ var subtabName = activeSubtab ? activeSubtab.getAttribute('data-extensions-subtab') : 'knowledge';
+ setActiveExtensionsSubtab(subtabName);
+ }
+
+ // --- 知识库子面板 ---
+ var _knowledgeRagExtensionsCache = null;
+ async function refreshKnowledgeExtensionsPanel() {
+ var statusEl = document.querySelector('[data-extensions-status="knowledge"]');
+ if (!statusEl) return;
+ statusEl.innerHTML = '
加载中...';
+ try {
+ var resp = await fetch('/api/knowledge-rag/status');
+ if (!resp.ok) throw new Error('HTTP ' + resp.status);
+ var data = await resp.json();
+ _knowledgeRagExtensionsCache = data;
+ var indexedCount = (data.files && data.files.length) || data.total_files || 0;
+ var statusText = data.ready ? '就绪' : '未就绪';
+ var statusClass = data.ready ? 'mnote-status-ready' : 'mnote-status-pending';
+ var lastIngest = data.last_ingest_time || '';
+ statusEl.innerHTML = '' +
+ '
' +
+ '
LightRAG 状态' + escapeHtml(String(statusText)) + '
' +
+ '
已索引文件' + indexedCount + ' 个
' +
+ (lastIngest ? '
最近更新' + escapeHtml(String(lastIngest)) + '
' : '') +
+ '
' +
+ '' +
+ '' +
+ '
' +
+ '
';
+ } catch (err) {
+ statusEl.innerHTML = '
无法连接 LightRAG 服务
';
+ }
+ }
+
+ // --- Skills 子面板 ---
+ async function refreshSkillsExtensionsPanel() {
+ var statusEl = document.querySelector('[data-extensions-status="skills"]');
+ if (!statusEl) return;
+ statusEl.innerHTML = '
加载中...';
+ try {
+ var resp = await fetch('/api/page-ai/agent-descriptors');
+ if (!resp.ok) throw new Error('HTTP ' + resp.status);
+ var data = await resp.json();
+ var descriptors = data.descriptors || data || [];
+ // 从 agent descriptor 中收集 skills 信息
+ var allSkills = [];
+ descriptors.forEach(function(desc) {
+ var caps = desc.capabilities || desc.capabilityStates || {};
+ if (caps.skills && Array.isArray(caps.skills)) {
+ caps.skills.forEach(function(s) { allSkills.push({ name: s, source: desc.name || desc.id || 'unknown' }); });
+ }
+ });
+ if (!allSkills.length) {
+ allSkills.push({ name: 'skill-read (内置)', source: 'hermes' });
+ }
+ var html = '';
+ allSkills.forEach(function(skill) {
+ html += '
' +
+ '
' + escapeHtml(skill.name) + '' + escapeHtml(skill.source) + '
' +
+ '
';
+ });
+ if (!html) html = '
暂无已安装 Skills
';
+ statusEl.innerHTML = html;
+ } catch (err) {
+ statusEl.innerHTML = '
无法加载 Skills 信息
';
+ }
+ }
+
+ // --- MCP 子面板 ---
+ async function refreshMcpExtensionsPanel() {
+ var statusEl = document.querySelector('[data-extensions-status="mcp"]');
+ if (!statusEl) return;
+ statusEl.innerHTML = '
加载中...';
+ try {
+ var resp = await fetch('/api/hermes/mcp/servers');
+ if (!resp.ok) throw new Error('HTTP ' + resp.status);
+ var servers = await resp.json();
+ var list = Array.isArray(servers) ? servers : (servers.servers || []);
+ var html = '';
+ if (list.length === 0) {
+ html = '
暂无已配置 MCP 服务器
';
+ } else {
+ list.forEach(function(srv) {
+ var name = srv.name || srv.id || '未命名';
+ var url = srv.url || srv.command || '';
+ var connected = srv.connected ? '已连接' : '未连接';
+ var connClass = srv.connected ? 'mnote-status-ready' : 'mnote-status-pending';
+ html += '
' +
+ '
' + escapeHtml(name) + '' + connected + '
' +
+ (url ? '
' + escapeHtml(String(url)) + '
' : '') +
+ '
';
+ });
+ }
+ statusEl.innerHTML = html;
+ } catch (err) {
+ statusEl.innerHTML = '
无法加载 MCP 配置
';
+ }
+ }
+
+ // --- Agent 工具子面板 ---
+ async function refreshToolsExtensionsPanel() {
+ var statusEl = document.querySelector('[data-extensions-status="tools"]');
+ if (!statusEl) return;
+ statusEl.innerHTML = '
加载中...';
+ try {
+ var resp = await fetch('/api/page-ai/agent-descriptors');
+ if (!resp.ok) throw new Error('HTTP ' + resp.status);
+ var data = await resp.json();
+ var descriptors = data.descriptors || data || [];
+ var html = '';
+ descriptors.forEach(function(desc) {
+ var tools = desc.tools || [];
+ var name = desc.name || desc.id || 'unknown';
+ if (!tools.length) return;
+ html += '
' +
+ '
' + escapeHtml(name) + '
';
+ tools.forEach(function(tool) {
+ var toolName = tool.name || tool.function_name || '';
+ var disabled = tool.disabled ? ' (已禁用)' : '';
+ html += '
' +
+ '' + escapeHtml(toolName) + '' +
+ (disabled ? '' + disabled + '' : '') +
+ '
';
+ });
+ html += '
';
+ });
+ if (!html) html = '
暂无 Agent 工具信息
';
+ statusEl.innerHTML = html;
+ } catch (err) {
+ statusEl.innerHTML = '
无法加载 Agent 工具信息
';
+ }
+ }
+
async function persistPageOptionsPatch(patch) {
var previous = Object.assign({}, currentPageOptions());
pageUiState.pageOptions = Object.assign({}, previous, patch);
@@ -2158,6 +2528,88 @@ export function createSidebarPageSettingsRuntime(context) {
if (isKnowledgeRagSettingsOpen()) void loadKnowledgeRagStatus(true);
});
+
+ // --- 仪表盘 ---
+ function refreshDashboardPanel() {
+ var popover = ensurePageSettingsPopover();
+ // 知识库状态
+ refreshDashboardKnowledgeCard(popover);
+ // Skills
+ refreshDashboardSkillsCard(popover);
+ // MCP
+ refreshDashboardMcpCard(popover);
+ // Agent runs
+ refreshDashboardAgentCard(popover);
+ }
+
+ async function refreshDashboardKnowledgeCard(popover) {
+ var el = popover.querySelector('[data-dashboard-value="knowledge"]');
+ if (!el) return;
+ el.textContent = '加载中...';
+ try {
+ var resp = await fetch('/api/knowledge-rag/status');
+ if (!resp.ok) throw new Error('HTTP ' + resp.status);
+ var data = await resp.json();
+ var count = (data.files && data.files.length) || data.total_files || 0;
+ var statusText = data.ready ? '就绪' : '未就绪';
+ el.textContent = count + ' 文件 / ' + statusText;
+ } catch (_) {
+ el.textContent = '不可用';
+ }
+ }
+
+ async function refreshDashboardSkillsCard(popover) {
+ var el = popover.querySelector('[data-dashboard-value="skills"]');
+ if (!el) return;
+ el.textContent = '加载中...';
+ try {
+ var resp = await fetch('/api/page-ai/agent-descriptors');
+ if (!resp.ok) throw new Error('HTTP ' + resp.status);
+ var data = await resp.json();
+ var descriptors = data.descriptors || data || [];
+ var count = 0;
+ descriptors.forEach(function(d) {
+ var caps = d.capabilities || d.capabilityStates || {};
+ if (caps.skills && Array.isArray(caps.skills)) count += caps.skills.length;
+ });
+ if (!count) count = 1; // skill-read 内置
+ el.textContent = count + ' 个';
+ } catch (_) {
+ el.textContent = '--';
+ }
+ }
+
+ async function refreshDashboardMcpCard(popover) {
+ var el = popover.querySelector('[data-dashboard-value="mcp"]');
+ if (!el) return;
+ el.textContent = '加载中...';
+ try {
+ var resp = await fetch('/api/hermes/mcp/servers');
+ if (!resp.ok) throw new Error('HTTP ' + resp.status);
+ var servers = await resp.json();
+ var list = Array.isArray(servers) ? servers : (servers.servers || []);
+ var connected = list.filter(function(s) { return s.connected; }).length;
+ el.textContent = list.length + ' / ' + connected + ' 已连接';
+ } catch (_) {
+ el.textContent = '--';
+ }
+ }
+
+ async function refreshDashboardAgentCard(popover) {
+ var el = popover.querySelector('[data-dashboard-value="agent"]');
+ if (!el) return;
+ el.textContent = '加载中...';
+ try {
+ var resp = await fetch('/api/hermes/client/runs?limit=100');
+ if (!resp.ok) throw new Error('HTTP ' + resp.status);
+ var data = await resp.json();
+ var runs = Array.isArray(data) ? data : (data.runs || []);
+ var recentCount = runs.length;
+ el.textContent = recentCount + ' 次';
+ } catch (_) {
+ el.textContent = '--';
+ }
+ }
return {
addLocalIndexRange,
applyPageOptionsToShell,
@@ -2191,6 +2643,12 @@ export function createSidebarPageSettingsRuntime(context) {
recordPageHistorySnapshot,
removeLocalIndexRange,
renderPageSettingsPopover,
+ refreshExtensionsPanel,
+ refreshDashboardPanel,
+
+ refreshKnowledgeExtensionsPanel,
+ setActiveExtensionsSubtab,
+
setActivePageSettingsTab,
refreshLocalIndex,
addKnowledgeRagSourceInput,
@@ -2199,6 +2657,10 @@ export function createSidebarPageSettingsRuntime(context) {
ingestSingleKnowledgeRagSource,
loadKnowledgeRagStatus,
openKnowledgeRagDashboard,
+ performKnowledgeRagSearch,
+ saveKnowledgeRagSearchParams,
+ setActiveKnowledgeRagDetailTab,
+
pruneKnowledgeRagRegistry,
removeKnowledgeRagSourceInput,
setKnowledgeRagSourceFilter,
diff --git a/rust/crates/mnote-web/browser/sidebar-tree-runtime.js b/rust/crates/mnote-web/browser/sidebar-tree-runtime.js
index 44b0de70..2d5f7f29 100644
--- a/rust/crates/mnote-web/browser/sidebar-tree-runtime.js
+++ b/rust/crates/mnote-web/browser/sidebar-tree-runtime.js
@@ -219,6 +219,13 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
const ingestSingleKnowledgeRagSource = (...args) => sidebarPageSettings.ingestSingleKnowledgeRagSource(...args);
const loadKnowledgeRagStatus = (...args) => sidebarPageSettings.loadKnowledgeRagStatus(...args);
const openKnowledgeRagDashboard = (...args) => sidebarPageSettings.openKnowledgeRagDashboard(...args);
+ const setActiveKnowledgeRagDetailTab = (...args) => sidebarPageSettings.setActiveKnowledgeRagDetailTab(...args);
+ const performKnowledgeRagSearch = (...args) => sidebarPageSettings.performKnowledgeRagSearch(...args);
+ const saveKnowledgeRagSearchParams = (...args) => sidebarPageSettings.saveKnowledgeRagSearchParams(...args);
+ const loadKnowledgeRagSearchParamsToForm = (...args) => sidebarPageSettings.loadKnowledgeRagSearchParamsToForm(...args);
+ const syncKnowledgeRagThresholdDisplay = (...args) => sidebarPageSettings.syncKnowledgeRagThresholdDisplay(...args);
+ const refreshDashboardPanel = (...args) => sidebarPageSettings.refreshDashboardPanel(...args);
+
const pruneKnowledgeRagRegistry = (...args) => sidebarPageSettings.pruneKnowledgeRagRegistry(...args);
const setKnowledgeRagSourceFilter = (...args) => sidebarPageSettings.setKnowledgeRagSourceFilter(...args);
const useKnowledgeRagFileTreeSelection = (...args) => sidebarPageSettings.useKnowledgeRagFileTreeSelection(...args);
@@ -2999,6 +3006,14 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
e.preventDefault();
setActivePageSettingsTab(pageSettingsTab.getAttribute('data-page-settings-tab') || 'page');
return;
+
+ var extensionsSubtab = closestAction(e.target, '[data-extensions-subtab]');
+ if (extensionsSubtab) {
+ e.preventDefault();
+ setActiveExtensionsSubtab(extensionsSubtab.getAttribute('data-extensions-subtab') || 'knowledge');
+ return;
+ }
+
}
var localIndexAction = closestAction(e.target, '[data-local-index-action]');
@@ -3027,6 +3042,15 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
if (knowledgeRagAction) {
e.preventDefault();
var knowledgeRagActionName = knowledgeRagAction.getAttribute('data-knowledge-rag-action') || '';
+ if (knowledgeRagActionName === 'manage') {
+ void openKnowledgeRagSettingsPopover();
+ return;
+ }
+ if (knowledgeRagActionName === 'refresh-status') {
+ void refreshKnowledgeExtensionsPanel();
+ return;
+ }
+
if (knowledgeRagActionName === 'refresh') {
void loadKnowledgeRagStatus(true);
return;
@@ -3069,6 +3093,51 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
}
}
+
+ var kbRagDetailTab = closestAction(e.target, '[data-kb-rag-tab]');
+ if (kbRagDetailTab) {
+ e.preventDefault();
+ var tabName = kbRagDetailTab.getAttribute('data-kb-rag-tab') || 'sources';
+ setActiveKnowledgeRagDetailTab(tabName);
+ if (tabName === 'params') loadKnowledgeRagSearchParamsToForm();
+ return;
+ }
+
+ var kbRagAction = closestAction(e.target, '[data-kb-rag-action]');
+ if (kbRagAction) {
+ e.preventDefault();
+ var kbRagActionName = kbRagAction.getAttribute('data-kb-rag-action') || '';
+ if (kbRagActionName === 'search') {
+ void performKnowledgeRagSearch();
+ return;
+ }
+ if (kbRagActionName === 'save-params') {
+ saveKnowledgeRagSearchParams();
+ return;
+ }
+ }
+
+ var kbRagSearchInput = closestAction(e.target, '[data-kb-rag-search-input]');
+ if (kbRagSearchInput && e.key === 'Enter') {
+ e.preventDefault();
+ void performKnowledgeRagSearch();
+ return;
+ }
+
+ var kbRagThresholdRange = closestAction(e.target, '[data-kb-rag-param="similarity_threshold"]');
+ if (kbRagThresholdRange && e.type === 'input') {
+ syncKnowledgeRagThresholdDisplay();
+ return;
+ }
+
+
+ var dashboardAction = closestAction(e.target, '[data-dashboard-action="refresh"]');
+ if (dashboardAction) {
+ e.preventDefault();
+ refreshDashboardPanel();
+ return;
+ }
+
var pageSettingsAction = closestAction(e.target, '[data-page-settings-action]');
if (pageSettingsAction) {
e.preventDefault();
diff --git a/rust/crates/mnote-web/src/hermes_tools/manifest.rs b/rust/crates/mnote-web/src/hermes_tools/manifest.rs
index 8b5cc71a..dcdbf57d 100644
--- a/rust/crates/mnote-web/src/hermes_tools/manifest.rs
+++ b/rust/crates/mnote-web/src/hermes_tools/manifest.rs
@@ -5,25 +5,76 @@ pub const MANIFEST_SCHEMA_VERSION: &str = "mnote.hermes_tool_manifest.v1";
pub const TOOL_SCHEMA_VERSION: &str = "mnote.hermes_tool.v1";
pub fn manifest() -> Value {
- let tools = annotate_tools_with_capabilities(vec![
- skill_read_tool(),
+ let tools = annotate_tools_with_capabilities(assemble_all_tools());
+ json!({
+ "schemaVersion": MANIFEST_SCHEMA_VERSION,
+ "plugin": {
+ "name": "mnote",
+ "description": "mnote 页面、树、artifact 与 edge 工具",
+ "runtimeOwner": "mnote-web",
+ "writeOwner": "rust-runtime-kernel"
+ },
+ "capabilities": skill::manifest_capabilities(),
+ "tools": tools
+ })
+}
+
+// ── 按能力域分组的工具组装函数 ──
+
+fn assemble_all_tools() -> Vec
{
+ let mut tools = Vec::new();
+ tools.push(skill_read_tool());
+ tools.append(&mut context_tools());
+ tools.append(&mut doc_tools());
+ tools.append(&mut knowledge_rag_tools());
+ tools.append(&mut block_tools());
+ tools.append(&mut page_tools());
+ tools.append(&mut mindmap_tools());
+ tools.append(&mut office_tools());
+ tools.append(&mut onlyoffice_tools());
+ tools.append(&mut artifact_tools());
+ tools
+}
+
+fn context_tools() -> Vec {
+ vec![
context_snapshot_tool(),
context_read_current_page_tool(),
context_resolve_target_tool(),
+ ]
+}
+
+fn doc_tools() -> Vec {
+ vec![
doc_fetch_tool(),
doc_find_tool(),
+ doc_markdown_edit_tool(),
+ doc_apply_block_ops_tool(),
+ doc_plan_update_tool(),
+ ]
+}
+
+fn knowledge_rag_tools() -> Vec {
+ vec![
knowledge_rag_status_tool(),
knowledge_rag_query_tool(),
knowledge_rag_section_context_tool(),
knowledge_rag_open_reference_tool(),
+ ]
+}
+
+fn block_tools() -> Vec {
+ vec![
block_fetch_tool(),
- doc_plan_update_tool(),
block_replace_tool(),
block_insert_after_tool(),
block_delete_tool(),
block_move_after_tool(),
- doc_apply_block_ops_tool(),
- doc_markdown_edit_tool(),
+ ]
+}
+
+fn page_tools() -> Vec {
+ vec![
page_get_tool(),
page_save_tool(),
available_tool(
@@ -36,11 +87,26 @@ pub fn manifest() -> Value {
"更新当前页面设置",
["page.write"],
),
+ ]
+}
+
+fn mindmap_tools() -> Vec {
+ vec![
mindmap_fetch_tool(),
mindmap_apply_ops_tool(),
mindmap_create_from_outline_tool(),
+ ]
+}
+
+fn office_tools() -> Vec {
+ vec![
office_fetch_summary_tool(),
office_propose_changes_tool(),
+ ]
+}
+
+fn onlyoffice_tools() -> Vec {
+ vec![
onlyoffice_session_current_tool(),
onlyoffice_capabilities_tool(),
onlyoffice_selection_get_tool(),
@@ -76,6 +142,11 @@ pub fn manifest() -> Value {
onlyoffice_presentation_add_table_tool(),
onlyoffice_presentation_clear_slide_tool(),
onlyoffice_presentation_add_shape_tool(),
+ ]
+}
+
+fn artifact_tools() -> Vec {
+ vec![
available_tool(
"mnote.artifact.create_summary",
"为当前页面创建或更新 AI Summary",
@@ -86,20 +157,11 @@ pub fn manifest() -> Value {
"基于当前页面创建新的 AI Note",
["artifact.write"],
),
- ]);
- json!({
- "schemaVersion": MANIFEST_SCHEMA_VERSION,
- "plugin": {
- "name": "mnote",
- "description": "mnote 页面、树、artifact 与 edge 工具",
- "runtimeOwner": "mnote-web",
- "writeOwner": "rust-runtime-kernel"
- },
- "capabilities": skill::manifest_capabilities(),
- "tools": tools
- })
+ ]
}
+
+
fn annotate_tools_with_capabilities(tools: Vec) -> Vec {
tools
.into_iter()
diff --git a/rust/crates/mnote-web/src/routes/mod.rs b/rust/crates/mnote-web/src/routes/mod.rs
index 3d749799..729e8429 100644
--- a/rust/crates/mnote-web/src/routes/mod.rs
+++ b/rust/crates/mnote-web/src/routes/mod.rs
@@ -221,6 +221,10 @@ pub fn build_router(state: AppState) -> Router {
"/api/mnote-browser-runtime/sidebar-attachment-open-runtime.js",
get(web_shell::sidebar_attachment_open_runtime_asset),
)
+ .route(
+ "/api/mnote-browser-runtime/agent-stream-event-router.js",
+ get(web_shell::agent_stream_event_router_asset),
+ )
.route(
"/api/mnote-browser-runtime/sidebar-page-ai-runtime.js",
get(web_shell::sidebar_page_ai_runtime_asset),
@@ -1305,6 +1309,7 @@ mod tests {
"/api/mnote-browser-runtime/sidebar-filetree-upload-runtime.js",
"/api/mnote-browser-runtime/sidebar-attachment-open-runtime.js",
"/api/mnote-browser-runtime/sidebar-page-ai-runtime.js",
+ "/api/mnote-browser-runtime/agent-stream-event-router.js",
"/api/mnote-browser-runtime/sidebar-page-ai-markdown-runtime.js",
"/api/mnote-browser-runtime/sidebar-page-ai-render-runtime.js",
"/api/mnote-browser-runtime/sidebar-page-ai-permission-runtime.js",
diff --git a/rust/crates/mnote-web/src/routes/web_shell.rs b/rust/crates/mnote-web/src/routes/web_shell.rs
index 107ea2dd..e159378d 100644
--- a/rust/crates/mnote-web/src/routes/web_shell.rs
+++ b/rust/crates/mnote-web/src/routes/web_shell.rs
@@ -2456,6 +2456,15 @@ pub async fn sidebar_tree_runtime_asset() -> Response {
.unwrap_or_else(|_| Response::new(Body::empty()))
}
+pub async fn agent_stream_event_router_asset() -> Response {
+ const JS: &str = include_str!("../../browser/agent-stream-event-router.js");
+ Response::builder()
+ .header("content-type", "application/javascript; charset=utf-8")
+ .header("cache-control", "public, max-age=3600")
+ .body(Body::from(JS))
+ .expect("agent-stream-event-router.js")
+}
+
pub async fn sidebar_page_ai_runtime_asset() -> Response {
const JS: &str = include_str!("../../browser/sidebar-page-ai-runtime.js");
Response::builder()
diff --git a/rust/crates/mnote-web/src/ssr/styles.rs b/rust/crates/mnote-web/src/ssr/styles.rs
index a1522e37..49b73183 100644
--- a/rust/crates/mnote-web/src/ssr/styles.rs
+++ b/rust/crates/mnote-web/src/ssr/styles.rs
@@ -4102,6 +4102,282 @@ body {
font-weight: 600;
}
+
+/* 扩展面板 */
+.mnote-extensions-panel {
+ gap: 12px;
+}
+.mnote-extensions-subtabs {
+ display: flex;
+ gap: 4px;
+ border-bottom: 1px solid #E5E4E4;
+ padding-bottom: 8px;
+}
+.mnote-extensions-subtab {
+ padding: 4px 12px;
+ border-radius: 6px;
+ font-size: 13px;
+ border: none;
+ background: transparent;
+ color: #787877;
+ cursor: pointer;
+}
+.mnote-extensions-subtab:hover {
+ background: #F4F3F3;
+}
+.mnote-extensions-subtab.is-active {
+ background: #F4F3F3;
+ color: #1B1C1C;
+ font-weight: 600;
+}
+.mnote-extensions-subpanel {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ max-height: 300px;
+ overflow-y: auto;
+}
+.mnote-extensions-item {
+ padding: 8px 10px;
+ background: #F9F8F8;
+ border-radius: 8px;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+.mnote-extensions-item-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 13px;
+}
+.mnote-extensions-label {
+ color: #787877;
+}
+.mnote-extensions-detail {
+ color: #AEADAB;
+ font-size: 12px;
+ max-width: 180px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.mnote-extensions-source {
+ color: #AEADAB;
+ font-size: 11px;
+}
+.mnote-extensions-agent-name {
+ font-weight: 600;
+ color: #1B1C1C;
+ font-size: 13px;
+ margin-bottom: 4px;
+}
+.mnote-extensions-tool-item {
+ padding: 2px 0 2px 12px;
+ font-size: 12px;
+ color: #585857;
+ display: flex;
+ justify-content: space-between;
+}
+.mnote-extensions-tool-name {
+ font-family: monospace;
+ font-size: 11px;
+}
+.mnote-extensions-tool-disabled {
+ color: #D15251;
+ font-size: 10px;
+}
+.mnote-extensions-loading {
+ color: #AEADAB;
+ font-size: 13px;
+}
+.mnote-extensions-error {
+ color: #D15251;
+ font-size: 13px;
+}
+.mnote-extensions-item-actions {
+ display: flex;
+ gap: 8px;
+ padding-top: 4px;
+}
+.mnote-status-ready {
+ color: #409440;
+ font-weight: 500;
+}
+.mnote-status-pending {
+
+/* 知识库详情面板 */
+.mnote-knowledge-rag-detail-tabs {
+ display: flex;
+ gap: 2px;
+ border-bottom: 1px solid #E5E4E4;
+ padding: 0 16px 0 16px;
+ margin-bottom: 8px;
+}
+.mnote-knowledge-rag-detail-tab {
+ padding: 6px 14px;
+ border-radius: 6px 6px 0 0;
+ font-size: 13px;
+ border: none;
+ background: transparent;
+ color: #787877;
+ cursor: pointer;
+}
+.mnote-knowledge-rag-detail-tab:hover {
+ background: #F4F3F3;
+}
+.mnote-knowledge-rag-detail-tab.is-active {
+ background: #F4F3F3;
+ color: #1B1C1C;
+ font-weight: 600;
+}
+.mnote-knowledge-rag-detail-panel {
+ padding: 0 16px 12px 16px;
+ max-height: 400px;
+ overflow-y: auto;
+}
+.mnote-kb-search-form {
+ display: flex;
+ gap: 8px;
+ margin-bottom: 12px;
+}
+.mnote-kb-search-input {
+ flex: 1;
+ padding: 6px 10px;
+ border: 1px solid #E5E4E4;
+ border-radius: 6px;
+ font-size: 13px;
+ outline: none;
+}
+.mnote-kb-search-input:focus {
+ border-color: #8F8E8C;
+}
+.mnote-kb-search-results {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+.mnote-kb-search-result-item {
+ padding: 8px 10px;
+ background: #F9F8F8;
+ border-radius: 8px;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+.mnote-kb-search-result-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+.mnote-kb-search-result-index {
+ font-weight: 600;
+ color: #1B1C1C;
+ font-size: 12px;
+}
+.mnote-kb-search-result-score {
+ font-size: 12px;
+ color: #409440;
+ font-weight: 500;
+}
+.mnote-kb-search-result-source {
+ font-size: 11px;
+ color: #AEADAB;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.mnote-kb-search-result-preview {
+ font-size: 12px;
+ color: #585857;
+ line-height: 1.5;
+}
+.mnote-kb-search-result-citation {
+ font-size: 11px;
+ color: #7B7A79;
+ font-family: monospace;
+}
+.mnote-kb-params-form {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+.mnote-kb-params-row {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+.mnote-kb-params-label {
+ font-size: 13px;
+ color: #585857;
+ min-width: 90px;
+}
+.mnote-kb-params-select {
+ flex: 1;
+ padding: 4px 8px;
+ border: 1px solid #E5E4E4;
+ border-radius: 6px;
+ font-size: 13px;
+ outline: none;
+ background: #fff;
+}
+.mnote-kb-params-number {
+ width: 80px;
+ padding: 4px 8px;
+ border: 1px solid #E5E4E4;
+ border-radius: 6px;
+ font-size: 13px;
+ outline: none;
+}
+.mnote-kb-params-range {
+ flex: 1;
+ accent-color: #409440;
+}
+.mnote-kb-params-range-value {
+
+/* 仪表盘 */
+.mnote-dashboard-panel {
+ padding: 4px 0;
+}
+.mnote-dashboard-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 10px;
+}
+.mnote-dashboard-card {
+ padding: 12px;
+ background: #F9F8F8;
+ border-radius: 8px;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+.mnote-dashboard-card-title {
+ font-size: 12px;
+ color: #AEADAB;
+ font-weight: 500;
+}
+.mnote-dashboard-card-value {
+ font-size: 18px;
+ font-weight: 700;
+ color: #1B1C1C;
+}
+.mnote-dashboard-card-desc {
+ font-size: 11px;
+ color: #AEADAB;
+}
+
+ font-size: 13px;
+ color: #1B1C1C;
+ font-weight: 500;
+ min-width: 32px;
+ text-align: right;
+}
+
+ color: #F09B26;
+ font-weight: 500;
+}
+
.wolai-page-settings-section {
display: flex;
flex-direction: column;
@@ -6437,6 +6713,6 @@ mod tests {
// 至少 2000 字符才能包含完整样式
assert!(MNOTE_CSS.len() > 2000);
// 当前整合了工作区壳、编辑器样式、树菜单、文件树图标、页面 AI、账号弹窗与授权管理控制面,仍保持在单文件可审阅范围内。
- assert!(MNOTE_CSS.len() < 145000);
+ assert!(MNOTE_CSS.len() < 158000);
}
}