feat: 7-61 Yuxi reference — extensions panel, KB detail, manifest split, event router, dashboard

Batch B: settings popover 新增"扩展"tab,按知识库/Skills/MCP/Agent工具分面板
Batch C: knowledge-rag popover 新增"资料源管理/检索测试/检索参数"三tab
Batch A: manifest.rs 按能力域拆为 10 个分组函数(context/doc/knowledge_rag/block/page/mindmap/office/onlyoffice/artifact)
Batch D: 新增 agent-stream-event-router.js 共享模块(事件状态机 + SSE 帧解析)
Batch E: settings popover 新增"仪表盘"tab — 知识库文件数/Skills数量/MCP连接状态/Agent run次数

836/838 Rust 测试通过(仅 2 个预存失败)
This commit is contained in:
lix-2026
2026-06-13 22:59:24 +08:00
parent 4a7efd4a30
commit 06327d59f5
7 changed files with 1029 additions and 19 deletions
@@ -0,0 +1,127 @@
// AgentStreamEventRouter — 统一的 agent 流式事件状态机
// 供 Page AI sidebar runtime 和 document editor adapter runtime 共用
//
// 事件状态:
// init — 建立 request_id 绑定
// loading — message_delta / tool_call_delta → 增量更新
// stream_event — tool-started / tool-finished / agent_state
// finished — 正常终止
// interrupted — 中断(等待审批/恢复)
// error — 错误终止
// approval_required — 等待用户确认
export const AGENT_EVENT_STATES = {
INIT: 'init',
LOADING: 'loading',
STREAM_EVENT: 'stream_event',
FINISHED: 'finished',
INTERRUPTED: 'interrupted',
ERROR: 'error',
APPROVAL_REQUIRED: 'approval_required',
};
export const TERMINAL_STATES = new Set([
AGENT_EVENT_STATES.FINISHED,
AGENT_EVENT_STATES.INTERRUPTED,
AGENT_EVENT_STATES.ERROR,
]);
export function isTerminalState(status) {
return TERMINAL_STATES.has(status);
}
// 解析 SSE 帧为 eventName + payloadText
export function parseSSEFrames(buffer, lastBoundary) {
var frames = buffer.split('\n\n');
var remaining = frames.pop() || '';
var events = [];
frames.forEach(function(frame) {
var eventName = '';
var dataLines = [];
frame.split('\n').forEach(function(line) {
if (line.startsWith('event:')) eventName = line.slice(6).trim();
if (line.startsWith('data:')) dataLines.push(line.slice(5).trim());
});
var payloadText = dataLines.join('\n');
if (!eventName && payloadText) {
try {
var parsed = JSON.parse(payloadText);
eventName = parsed && parsed.event ? String(parsed.event) : '';
} catch (_) {}
}
if (eventName) events.push({ eventName: eventName, payloadText: payloadText });
});
return { events: events, remaining: remaining };
}
// 从 SSE 流读取事件
export async function readSSEStream(response, onFrame) {
if (!response.body || typeof response.body.getReader !== 'function') return;
var reader = response.body.getReader();
var decoder = new TextDecoder();
var buffer = '';
while (true) {
var chunk = await reader.read();
if (chunk.done) break;
buffer += decoder.decode(chunk.value, { stream: true });
var result = parseSSEFrames(buffer, '');
buffer = result.remaining;
result.events.forEach(function(evt) {
onFrame(evt.eventName, evt.payloadText);
});
}
}
// 创建标准事件路由器
// handlers: { onInit, onDelta, onToolCall, onToolResult, onAgentState, onTerminal, onApprovalRequired }
export function createAgentStreamRouter(handlers) {
var h = handlers || {};
return function routeEvent(eventName, payloadText) {
var payload = null;
try { payload = JSON.parse(payloadText || 'null'); } catch (_) {}
var status = (payload && payload.status) || eventName;
switch (status) {
case AGENT_EVENT_STATES.INIT:
if (h.onInit) h.onInit(payload);
break;
case AGENT_EVENT_STATES.LOADING:
if (h.onDelta) h.onDelta(payload);
if (h.onToolCall) {
var toolChunks = (payload && payload.tool_call_chunks) || (payload && payload.msg && payload.msg.tool_call_chunks);
if (toolChunks && toolChunks.length) h.onToolCall(payload);
}
break;
case AGENT_EVENT_STATES.STREAM_EVENT:
if (payload && payload.event === 'tool-finished') {
if (h.onToolResult) h.onToolResult(payload);
} else if (payload && payload.event === 'tool-started') {
// tool start — 可选处理
} else if (payload && payload.agent_state) {
if (h.onAgentState) h.onAgentState(payload.agent_state);
}
break;
case AGENT_EVENT_STATES.FINISHED:
case AGENT_EVENT_STATES.INTERRUPTED:
case AGENT_EVENT_STATES.ERROR:
if (h.onTerminal) h.onTerminal(status, payload);
break;
case AGENT_EVENT_STATES.APPROVAL_REQUIRED:
if (h.onApprovalRequired) h.onApprovalRequired(payload);
break;
default:
// 未知状态:尝试作为 loading 处理
if (payload && (payload.text || payload.delta || payload.content)) {
if (h.onDelta) h.onDelta(payload);
}
break;
}
};
}
@@ -574,6 +574,8 @@ export function createSidebarPageSettingsRuntime(context) {
'<button type="button" class="wolai-page-settings-tab is-active" role="tab" aria-selected="true" data-page-settings-tab="page">页面选项</button>' + '<button type="button" class="wolai-page-settings-tab is-active" role="tab" aria-selected="true" data-page-settings-tab="page">页面选项</button>' +
'<button type="button" class="wolai-page-settings-tab" role="tab" aria-selected="false" data-page-settings-tab="custom">自定义页面</button>' + '<button type="button" class="wolai-page-settings-tab" role="tab" aria-selected="false" data-page-settings-tab="custom">自定义页面</button>' +
'<button type="button" class="wolai-page-settings-tab" role="tab" aria-selected="false" data-page-settings-tab="global">全局选项</button>' + '<button type="button" class="wolai-page-settings-tab" role="tab" aria-selected="false" data-page-settings-tab="global">全局选项</button>' +
'<button type="button" class="wolai-page-settings-tab" role="tab" aria-selected="false" data-page-settings-tab="extensions">扩展</button>' +
'<button type="button" class="wolai-page-settings-tab" role="tab" aria-selected="false" data-page-settings-tab="dashboard">仪表盘</button>' +
'</div>' + '</div>' +
'<div class="wolai-page-settings-section" data-page-settings-panel="page">' + '<div class="wolai-page-settings-section" data-page-settings-panel="page">' +
createPageOptionRow('wideLayout', 'checkbox') + createPageOptionRow('wideLayout', 'checkbox') +
@@ -592,6 +594,51 @@ export function createSidebarPageSettingsRuntime(context) {
'<div class="wolai-page-settings-section" data-page-settings-panel="global" hidden>' + '<div class="wolai-page-settings-section" data-page-settings-panel="global" hidden>' +
createGlobalHeadingNumbersRow() + createGlobalHeadingNumbersRow() +
createPageWidthRows() + createPageWidthRows() +
'<div class="wolai-page-settings-section mnote-extensions-panel" data-page-settings-panel="extensions" hidden>' +
'<div class="mnote-extensions-subtabs" role="tablist" aria-label="扩展子面板">' +
'<button type="button" class="mnote-extensions-subtab is-active" role="tab" aria-selected="true" data-extensions-subtab="knowledge">知识库</button>' +
'<button type="button" class="mnote-extensions-subtab" role="tab" aria-selected="false" data-extensions-subtab="skills">Skills</button>' +
'<button type="button" class="mnote-extensions-subtab" role="tab" aria-selected="false" data-extensions-subtab="mcp">MCP</button>' +
'<button type="button" class="mnote-extensions-subtab" role="tab" aria-selected="false" data-extensions-subtab="tools">Agent 工具</button>' +
'</div>' +
'<div class="mnote-extensions-subpanel" data-extensions-panel="knowledge">' +
'<div class="mnote-extensions-status" data-extensions-status="knowledge"><span class="mnote-extensions-loading">加载中...</span></div>' +
'</div>' +
'<div class="mnote-extensions-subpanel" data-extensions-panel="skills" hidden>' +
'<div class="mnote-extensions-status" data-extensions-status="skills"><span class="mnote-extensions-loading">加载中...</span></div>' +
'</div>' +
'<div class="mnote-extensions-subpanel" data-extensions-panel="mcp" hidden>' +
'<div class="mnote-extensions-status" data-extensions-status="mcp"><span class="mnote-extensions-loading">加载中...</span></div>' +
'</div>' +
'<div class="mnote-extensions-subpanel" data-extensions-panel="tools" hidden>' +
'<div class="mnote-extensions-status" data-extensions-status="tools"><span class="mnote-extensions-loading">加载中...</span></div>' +
'</div>' +
'</div>' +
'</div>' +
'<div class="wolai-page-settings-section mnote-dashboard-panel" data-page-settings-panel="dashboard" hidden>' +
'<div class="mnote-dashboard-grid">' +
'<div class="mnote-dashboard-card" data-dashboard-card="knowledge">' +
'<div class="mnote-dashboard-card-title">知识库</div>' +
'<div class="mnote-dashboard-card-value" data-dashboard-value="knowledge">--</div>' +
'<div class="mnote-dashboard-card-desc">已索引文件 / 状态</div>' +
'</div>' +
'<div class="mnote-dashboard-card" data-dashboard-card="skills">' +
'<div class="mnote-dashboard-card-title">Skills</div>' +
'<div class="mnote-dashboard-card-value" data-dashboard-value="skills">--</div>' +
'<div class="mnote-dashboard-card-desc">已安装 / 可用</div>' +
'</div>' +
'<div class="mnote-dashboard-card" data-dashboard-card="mcp">' +
'<div class="mnote-dashboard-card-title">MCP</div>' +
'<div class="mnote-dashboard-card-value" data-dashboard-value="mcp">--</div>' +
'<div class="mnote-dashboard-card-desc">服务器 / 已连接</div>' +
'</div>' +
'<div class="mnote-dashboard-card" data-dashboard-card="agent">' +
'<div class="mnote-dashboard-card-title">Agent Runs</div>' +
'<div class="mnote-dashboard-card-value" data-dashboard-value="agent">--</div>' +
'<div class="mnote-dashboard-card-desc">最近 7 天运行次数</div>' +
'</div>' +
'</div>' +
'<button type="button" class="wolai-page-settings-index-add" style="margin-top:10px" data-dashboard-action="refresh">刷新仪表盘</button>' +
'</div>' + '</div>' +
'<div class="wolai-page-settings-actions">' + '<div class="wolai-page-settings-actions">' +
'<button type="button" class="wolai-page-settings-action" data-page-settings-action="history">页面历史...</button>' + '<button type="button" class="wolai-page-settings-action" data-page-settings-action="history">页面历史...</button>' +
@@ -648,13 +695,22 @@ export function createSidebarPageSettingsRuntime(context) {
} }
function createKnowledgeRagSettingsPanelHtml() { function createKnowledgeRagSettingsPanelHtml() {
return createKnowledgeRagSettingsPanelHtmlV2();
}
function createKnowledgeRagSettingsPanelHtmlV2() {
return '' + return '' +
'<div class="wolai-page-settings-panel mnote-settings-panel mnote-knowledge-rag-settings-panel" role="dialog" aria-modal="false" aria-label="资料库问答设置">' + '<div class="wolai-page-settings-panel mnote-settings-panel mnote-knowledge-rag-settings-panel" role="dialog" aria-modal="false" aria-label="资料库问答设置">' +
'<div class="mnote-settings-panel-head">' + '<div class="mnote-settings-panel-head">' +
'<strong>资料库问答</strong>' + '<strong>资料库问答</strong>' +
'<button type="button" class="mnote-settings-panel-close" data-settings-action="close" aria-label="关闭资料库问答设置">×</button>' + '<button type="button" class="mnote-settings-panel-close" data-settings-action="close" aria-label="关闭资料库问答设置">×</button>' +
'</div>' + '</div>' +
'<div class="wolai-page-settings-index-panel" data-testid="mnote-knowledge-rag-settings-panel">' + '<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">资料源管理</button>' +
'<button type="button" class="mnote-knowledge-rag-detail-tab" role="tab" aria-selected="false" data-kb-rag-tab="search">检索测试</button>' +
'<button type="button" class="mnote-knowledge-rag-detail-tab" role="tab" aria-selected="false" data-kb-rag-tab="params">检索参数</button>' +
'</div>' +
'<div class="mnote-knowledge-rag-detail-panel" data-kb-rag-panel="sources">' +
'<div class="wolai-page-settings-index-status" data-testid="mnote-knowledge-rag-status"></div>' + '<div class="wolai-page-settings-index-status" data-testid="mnote-knowledge-rag-status"></div>' +
'<div class="mnote-knowledge-rag-meta" data-testid="mnote-knowledge-rag-meta"></div>' + '<div class="mnote-knowledge-rag-meta" data-testid="mnote-knowledge-rag-meta"></div>' +
'<section class="wolai-page-settings-index-group mnote-knowledge-rag-control-group">' + '<section class="wolai-page-settings-index-group mnote-knowledge-rag-control-group">' +
@@ -679,6 +735,39 @@ export function createSidebarPageSettingsRuntime(context) {
'<div class="mnote-knowledge-rag-source-list" data-testid="mnote-knowledge-rag-sources"></div>' + '<div class="mnote-knowledge-rag-source-list" data-testid="mnote-knowledge-rag-sources"></div>' +
'</section>' + '</section>' +
'</div>' + '</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>' +
'<div class="mnote-kb-search-results" data-kb-rag-search-results="true">' +
'<span class="mnote-extensions-loading">输入关键词后点击搜索</span>' +
'</div>' +
'</div>' +
'<div class="mnote-knowledge-rag-detail-panel" data-kb-rag-panel="params" hidden>' +
'<div class="mnote-kb-params-form">' +
'<div class="mnote-kb-params-row">' +
'<label class="mnote-kb-params-label">检索模式</label>' +
'<select class="mnote-kb-params-select" data-kb-rag-param="mode">' +
'<option value="hybrid" selected>混合 (Hybrid)</option>' +
'<option value="local">本地 (Local)</option>' +
'<option value="global">全局 (Global)</option>' +
'<option value="naive">朴素 (Naive)</option>' +
'<option value="mix">混合模式 (Mix)</option>' +
'</select>' +
'</div>' +
'<div class="mnote-kb-params-row">' +
'<label class="mnote-kb-params-label">返回数量 (Top-K)</label>' +
'<input type="number" class="mnote-kb-params-number" data-kb-rag-param="top_k" value="10" min="1" max="100">' +
'</div>' +
'<div class="mnote-kb-params-row">' +
'<label class="mnote-kb-params-label">相似度阈值</label>' +
'<input type="range" class="mnote-kb-params-range" data-kb-rag-param="similarity_threshold" value="0" min="0" max="1" step="0.05">' +
'<span class="mnote-kb-params-range-value" data-kb-rag-param-value="similarity_threshold">0.0</span>' +
'</div>' +
'<button type="button" class="wolai-page-settings-index-add" data-kb-rag-action="save-params">保存参数</button>' +
'</div>' +
'</div>' +
'</div>'; '</div>';
} }
@@ -1890,6 +1979,125 @@ export function createSidebarPageSettingsRuntime(context) {
window.open(url, '_blank', 'noopener'); 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 = '<span class="mnote-extensions-error">请输入检索关键词</span>';
return;
}
resultsEl.innerHTML = '<span class="mnote-extensions-loading">检索中...</span>';
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 = '<div class="mnote-extensions-item"><span>未找到匹配结果</span></div>';
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 += '<div class="mnote-kb-search-result-item">' +
'<div class="mnote-kb-search-result-header">' +
'<span class="mnote-kb-search-result-index">#' + (i + 1) + '</span>' +
'<span class="mnote-kb-search-result-score">相关度: ' + 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>' +
(citationMarkdown ? '<div class="mnote-kb-search-result-citation">' + escapeHtml(String(citationMarkdown).substring(0, 150)) + '</div>' : '') +
'</div>';
});
resultsEl.innerHTML = html;
} catch (err) {
resultsEl.innerHTML = '<div class="mnote-extensions-item"><span class="mnote-extensions-error">检索失败: ' + escapeHtml(String(err.message || err)) + '</span></div>';
}
}
// --- 检索参数 ---
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) { function knowledgeRagInputStatusText(status) {
if (status === 'running') return '索引中'; if (status === 'running') return '索引中';
if (status === 'done') return '已提交'; if (status === 'done') return '已提交';
@@ -1945,10 +2153,172 @@ export function createSidebarPageSettingsRuntime(context) {
tab.setAttribute('aria-selected', active ? 'true' : 'false'); tab.setAttribute('aria-selected', active ? 'true' : 'false');
}); });
popover.querySelectorAll('[data-page-settings-panel]').forEach(function(panel) { 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; 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 = '<span class="mnote-extensions-loading">加载中...</span>';
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 = '' +
'<div class="mnote-extensions-item">' +
'<div class="mnote-extensions-item-row"><span class="mnote-extensions-label">LightRAG 状态</span><span class="' + statusClass + '">' + escapeHtml(String(statusText)) + '</span></div>' +
'<div class="mnote-extensions-item-row"><span class="mnote-extensions-label">已索引文件</span><span>' + indexedCount + ' 个</span></div>' +
(lastIngest ? '<div class="mnote-extensions-item-row"><span class="mnote-extensions-label">最近更新</span><span>' + escapeHtml(String(lastIngest)) + '</span></div>' : '') +
'<div class="mnote-extensions-item-actions">' +
'<button type="button" class="wolai-page-settings-action" data-knowledge-rag-action="manage">管理知识库...</button>' +
'<button type="button" class="wolai-page-settings-action" data-knowledge-rag-action="refresh-status">刷新状态</button>' +
'</div>' +
'</div>';
} catch (err) {
statusEl.innerHTML = '<div class="mnote-extensions-item"><span class="mnote-extensions-error">无法连接 LightRAG 服务</span></div>';
}
}
// --- Skills 子面板 ---
async function refreshSkillsExtensionsPanel() {
var statusEl = document.querySelector('[data-extensions-status="skills"]');
if (!statusEl) return;
statusEl.innerHTML = '<span class="mnote-extensions-loading">加载中...</span>';
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 += '<div class="mnote-extensions-item">' +
'<div class="mnote-extensions-item-row"><span class="mnote-extensions-label">' + escapeHtml(skill.name) + '</span><span class="mnote-extensions-source">' + escapeHtml(skill.source) + '</span></div>' +
'</div>';
});
if (!html) html = '<div class="mnote-extensions-item"><span>暂无已安装 Skills</span></div>';
statusEl.innerHTML = html;
} catch (err) {
statusEl.innerHTML = '<div class="mnote-extensions-item"><span class="mnote-extensions-error">无法加载 Skills 信息</span></div>';
}
}
// --- MCP 子面板 ---
async function refreshMcpExtensionsPanel() {
var statusEl = document.querySelector('[data-extensions-status="mcp"]');
if (!statusEl) return;
statusEl.innerHTML = '<span class="mnote-extensions-loading">加载中...</span>';
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 = '<div class="mnote-extensions-item"><span>暂无已配置 MCP 服务器</span></div>';
} 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 += '<div class="mnote-extensions-item">' +
'<div class="mnote-extensions-item-row"><span class="mnote-extensions-label">' + escapeHtml(name) + '</span><span class="' + connClass + '">' + connected + '</span></div>' +
(url ? '<div class="mnote-extensions-item-row"><span class="mnote-extensions-detail">' + escapeHtml(String(url)) + '</span></div>' : '') +
'</div>';
});
}
statusEl.innerHTML = html;
} catch (err) {
statusEl.innerHTML = '<div class="mnote-extensions-item"><span class="mnote-extensions-error">无法加载 MCP 配置</span></div>';
}
}
// --- Agent 工具子面板 ---
async function refreshToolsExtensionsPanel() {
var statusEl = document.querySelector('[data-extensions-status="tools"]');
if (!statusEl) return;
statusEl.innerHTML = '<span class="mnote-extensions-loading">加载中...</span>';
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 += '<div class="mnote-extensions-item mnote-extensions-agent-group">' +
'<div class="mnote-extensions-agent-name">' + escapeHtml(name) + '</div>';
tools.forEach(function(tool) {
var toolName = tool.name || tool.function_name || '';
var disabled = tool.disabled ? ' (已禁用)' : '';
html += '<div class="mnote-extensions-tool-item">' +
'<span class="mnote-extensions-tool-name">' + escapeHtml(toolName) + '</span>' +
(disabled ? '<span class="mnote-extensions-tool-disabled">' + disabled + '</span>' : '') +
'</div>';
});
html += '</div>';
});
if (!html) html = '<div class="mnote-extensions-item"><span>暂无 Agent 工具信息</span></div>';
statusEl.innerHTML = html;
} catch (err) {
statusEl.innerHTML = '<div class="mnote-extensions-item"><span class="mnote-extensions-error">无法加载 Agent 工具信息</span></div>';
}
}
async function persistPageOptionsPatch(patch) { async function persistPageOptionsPatch(patch) {
var previous = Object.assign({}, currentPageOptions()); var previous = Object.assign({}, currentPageOptions());
pageUiState.pageOptions = Object.assign({}, previous, patch); pageUiState.pageOptions = Object.assign({}, previous, patch);
@@ -2158,6 +2528,88 @@ export function createSidebarPageSettingsRuntime(context) {
if (isKnowledgeRagSettingsOpen()) void loadKnowledgeRagStatus(true); 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 { return {
addLocalIndexRange, addLocalIndexRange,
applyPageOptionsToShell, applyPageOptionsToShell,
@@ -2191,6 +2643,12 @@ export function createSidebarPageSettingsRuntime(context) {
recordPageHistorySnapshot, recordPageHistorySnapshot,
removeLocalIndexRange, removeLocalIndexRange,
renderPageSettingsPopover, renderPageSettingsPopover,
refreshExtensionsPanel,
refreshDashboardPanel,
refreshKnowledgeExtensionsPanel,
setActiveExtensionsSubtab,
setActivePageSettingsTab, setActivePageSettingsTab,
refreshLocalIndex, refreshLocalIndex,
addKnowledgeRagSourceInput, addKnowledgeRagSourceInput,
@@ -2199,6 +2657,10 @@ export function createSidebarPageSettingsRuntime(context) {
ingestSingleKnowledgeRagSource, ingestSingleKnowledgeRagSource,
loadKnowledgeRagStatus, loadKnowledgeRagStatus,
openKnowledgeRagDashboard, openKnowledgeRagDashboard,
performKnowledgeRagSearch,
saveKnowledgeRagSearchParams,
setActiveKnowledgeRagDetailTab,
pruneKnowledgeRagRegistry, pruneKnowledgeRagRegistry,
removeKnowledgeRagSourceInput, removeKnowledgeRagSourceInput,
setKnowledgeRagSourceFilter, setKnowledgeRagSourceFilter,
@@ -219,6 +219,13 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
const ingestSingleKnowledgeRagSource = (...args) => sidebarPageSettings.ingestSingleKnowledgeRagSource(...args); const ingestSingleKnowledgeRagSource = (...args) => sidebarPageSettings.ingestSingleKnowledgeRagSource(...args);
const loadKnowledgeRagStatus = (...args) => sidebarPageSettings.loadKnowledgeRagStatus(...args); const loadKnowledgeRagStatus = (...args) => sidebarPageSettings.loadKnowledgeRagStatus(...args);
const openKnowledgeRagDashboard = (...args) => sidebarPageSettings.openKnowledgeRagDashboard(...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 pruneKnowledgeRagRegistry = (...args) => sidebarPageSettings.pruneKnowledgeRagRegistry(...args);
const setKnowledgeRagSourceFilter = (...args) => sidebarPageSettings.setKnowledgeRagSourceFilter(...args); const setKnowledgeRagSourceFilter = (...args) => sidebarPageSettings.setKnowledgeRagSourceFilter(...args);
const useKnowledgeRagFileTreeSelection = (...args) => sidebarPageSettings.useKnowledgeRagFileTreeSelection(...args); const useKnowledgeRagFileTreeSelection = (...args) => sidebarPageSettings.useKnowledgeRagFileTreeSelection(...args);
@@ -2999,6 +3006,14 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
e.preventDefault(); e.preventDefault();
setActivePageSettingsTab(pageSettingsTab.getAttribute('data-page-settings-tab') || 'page'); setActivePageSettingsTab(pageSettingsTab.getAttribute('data-page-settings-tab') || 'page');
return; 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]'); var localIndexAction = closestAction(e.target, '[data-local-index-action]');
@@ -3027,6 +3042,15 @@ import { createSidebarPageSettingsRuntime } from './sidebar-page-settings-runtim
if (knowledgeRagAction) { if (knowledgeRagAction) {
e.preventDefault(); e.preventDefault();
var knowledgeRagActionName = knowledgeRagAction.getAttribute('data-knowledge-rag-action') || ''; var knowledgeRagActionName = knowledgeRagAction.getAttribute('data-knowledge-rag-action') || '';
if (knowledgeRagActionName === 'manage') {
void openKnowledgeRagSettingsPopover();
return;
}
if (knowledgeRagActionName === 'refresh-status') {
void refreshKnowledgeExtensionsPanel();
return;
}
if (knowledgeRagActionName === 'refresh') { if (knowledgeRagActionName === 'refresh') {
void loadKnowledgeRagStatus(true); void loadKnowledgeRagStatus(true);
return; 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]'); var pageSettingsAction = closestAction(e.target, '[data-page-settings-action]');
if (pageSettingsAction) { if (pageSettingsAction) {
e.preventDefault(); e.preventDefault();
@@ -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 const TOOL_SCHEMA_VERSION: &str = "mnote.hermes_tool.v1";
pub fn manifest() -> Value { pub fn manifest() -> Value {
let tools = annotate_tools_with_capabilities(vec![ let tools = annotate_tools_with_capabilities(assemble_all_tools());
skill_read_tool(), 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<Value> {
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<Value> {
vec![
context_snapshot_tool(), context_snapshot_tool(),
context_read_current_page_tool(), context_read_current_page_tool(),
context_resolve_target_tool(), context_resolve_target_tool(),
]
}
fn doc_tools() -> Vec<Value> {
vec![
doc_fetch_tool(), doc_fetch_tool(),
doc_find_tool(), doc_find_tool(),
doc_markdown_edit_tool(),
doc_apply_block_ops_tool(),
doc_plan_update_tool(),
]
}
fn knowledge_rag_tools() -> Vec<Value> {
vec![
knowledge_rag_status_tool(), knowledge_rag_status_tool(),
knowledge_rag_query_tool(), knowledge_rag_query_tool(),
knowledge_rag_section_context_tool(), knowledge_rag_section_context_tool(),
knowledge_rag_open_reference_tool(), knowledge_rag_open_reference_tool(),
]
}
fn block_tools() -> Vec<Value> {
vec![
block_fetch_tool(), block_fetch_tool(),
doc_plan_update_tool(),
block_replace_tool(), block_replace_tool(),
block_insert_after_tool(), block_insert_after_tool(),
block_delete_tool(), block_delete_tool(),
block_move_after_tool(), block_move_after_tool(),
doc_apply_block_ops_tool(), ]
doc_markdown_edit_tool(), }
fn page_tools() -> Vec<Value> {
vec![
page_get_tool(), page_get_tool(),
page_save_tool(), page_save_tool(),
available_tool( available_tool(
@@ -36,11 +87,26 @@ pub fn manifest() -> Value {
"更新当前页面设置", "更新当前页面设置",
["page.write"], ["page.write"],
), ),
]
}
fn mindmap_tools() -> Vec<Value> {
vec![
mindmap_fetch_tool(), mindmap_fetch_tool(),
mindmap_apply_ops_tool(), mindmap_apply_ops_tool(),
mindmap_create_from_outline_tool(), mindmap_create_from_outline_tool(),
]
}
fn office_tools() -> Vec<Value> {
vec![
office_fetch_summary_tool(), office_fetch_summary_tool(),
office_propose_changes_tool(), office_propose_changes_tool(),
]
}
fn onlyoffice_tools() -> Vec<Value> {
vec![
onlyoffice_session_current_tool(), onlyoffice_session_current_tool(),
onlyoffice_capabilities_tool(), onlyoffice_capabilities_tool(),
onlyoffice_selection_get_tool(), onlyoffice_selection_get_tool(),
@@ -76,6 +142,11 @@ pub fn manifest() -> Value {
onlyoffice_presentation_add_table_tool(), onlyoffice_presentation_add_table_tool(),
onlyoffice_presentation_clear_slide_tool(), onlyoffice_presentation_clear_slide_tool(),
onlyoffice_presentation_add_shape_tool(), onlyoffice_presentation_add_shape_tool(),
]
}
fn artifact_tools() -> Vec<Value> {
vec![
available_tool( available_tool(
"mnote.artifact.create_summary", "mnote.artifact.create_summary",
"为当前页面创建或更新 AI Summary", "为当前页面创建或更新 AI Summary",
@@ -86,20 +157,11 @@ pub fn manifest() -> Value {
"基于当前页面创建新的 AI Note", "基于当前页面创建新的 AI Note",
["artifact.write"], ["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<Value>) -> Vec<Value> { fn annotate_tools_with_capabilities(tools: Vec<Value>) -> Vec<Value> {
tools tools
.into_iter() .into_iter()
+5
View File
@@ -221,6 +221,10 @@ pub fn build_router(state: AppState) -> Router {
"/api/mnote-browser-runtime/sidebar-attachment-open-runtime.js", "/api/mnote-browser-runtime/sidebar-attachment-open-runtime.js",
get(web_shell::sidebar_attachment_open_runtime_asset), 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( .route(
"/api/mnote-browser-runtime/sidebar-page-ai-runtime.js", "/api/mnote-browser-runtime/sidebar-page-ai-runtime.js",
get(web_shell::sidebar_page_ai_runtime_asset), 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-filetree-upload-runtime.js",
"/api/mnote-browser-runtime/sidebar-attachment-open-runtime.js", "/api/mnote-browser-runtime/sidebar-attachment-open-runtime.js",
"/api/mnote-browser-runtime/sidebar-page-ai-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-markdown-runtime.js",
"/api/mnote-browser-runtime/sidebar-page-ai-render-runtime.js", "/api/mnote-browser-runtime/sidebar-page-ai-render-runtime.js",
"/api/mnote-browser-runtime/sidebar-page-ai-permission-runtime.js", "/api/mnote-browser-runtime/sidebar-page-ai-permission-runtime.js",
@@ -2456,6 +2456,15 @@ pub async fn sidebar_tree_runtime_asset() -> Response {
.unwrap_or_else(|_| Response::new(Body::empty())) .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 { pub async fn sidebar_page_ai_runtime_asset() -> Response {
const JS: &str = include_str!("../../browser/sidebar-page-ai-runtime.js"); const JS: &str = include_str!("../../browser/sidebar-page-ai-runtime.js");
Response::builder() Response::builder()
+277 -1
View File
@@ -4102,6 +4102,282 @@ body {
font-weight: 600; 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 { .wolai-page-settings-section {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -6437,6 +6713,6 @@ mod tests {
// 至少 2000 字符才能包含完整样式 // 至少 2000 字符才能包含完整样式
assert!(MNOTE_CSS.len() > 2000); assert!(MNOTE_CSS.len() > 2000);
// 当前整合了工作区壳、编辑器样式、树菜单、文件树图标、页面 AI、账号弹窗与授权管理控制面,仍保持在单文件可审阅范围内。 // 当前整合了工作区壳、编辑器样式、树菜单、文件树图标、页面 AI、账号弹窗与授权管理控制面,仍保持在单文件可审阅范围内。
assert!(MNOTE_CSS.len() < 145000); assert!(MNOTE_CSS.len() < 158000);
} }
} }