Improve local evidence search and AI capabilities
This commit is contained in:
@@ -563,10 +563,13 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
var skillSourceSelect = drawer.querySelector('[data-page-ai-skill-source-select]');
|
||||
if (skillSourceSelect instanceof HTMLSelectElement) {
|
||||
var activeSkillSource = pageAiCurrentSkillSource();
|
||||
skillSourceSelect.innerHTML = pageAiSkillSourceOptions().map(function(option) {
|
||||
var sourceOptions = pageAiSkillSourceOptions();
|
||||
skillSourceSelect.innerHTML = sourceOptions.map(function(option) {
|
||||
return '<option value="' + escapeHtml(option.value) + '"' + (option.value === activeSkillSource ? ' selected' : '') + '>' + escapeHtml(option.label) + '</option>';
|
||||
}).join('');
|
||||
skillSourceSelect.value = activeSkillSource;
|
||||
var sourceControl = skillSourceSelect.closest('[data-page-ai-skill-source-control]');
|
||||
if (sourceControl instanceof HTMLElement) sourceControl.hidden = sourceOptions.length <= 1;
|
||||
}
|
||||
var hermesBuiltinToggle = drawer.querySelector('[data-page-ai-hide-hermes-builtin]');
|
||||
if (hermesBuiltinToggle instanceof HTMLInputElement) {
|
||||
@@ -580,7 +583,7 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
if (skillList instanceof HTMLElement) {
|
||||
var skills = pageAiFilteredSkillEntries();
|
||||
if (!skills.length) {
|
||||
skillList.innerHTML = '<div class="wolai-page-ai-empty">没有匹配的技能。</div>';
|
||||
skillList.innerHTML = '<div class="wolai-page-ai-empty">没有匹配的能力。</div>';
|
||||
} else {
|
||||
var sourceParts = pageAiSkillSourceParts(pageAiCurrentSkillSource());
|
||||
var group = sourceParts.group;
|
||||
@@ -593,14 +596,33 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
'<span>' + escapeHtml(String(skills.length) + headingExtra) + '</span>' +
|
||||
'</button>' +
|
||||
(collapsed ? '' : skills.map(function(skill) {
|
||||
var sourceText = pageAiSkillOriginLabel(skill)
|
||||
+ (skill.configScope ? ' · ' + skill.configScope : '')
|
||||
+ (skill.readOnly ? ' · 只读' : '')
|
||||
+ (skill.modified ? ' · modified' : '');
|
||||
var toolCount = Number(skill.toolCount || pageAiNormalizeArray(skill.tools).length || 0);
|
||||
var disabledToolCount = Number(skill.disabledToolCount || 0);
|
||||
var capabilityMeta = [];
|
||||
if (toolCount > 0) capabilityMeta.push(String(toolCount) + ' 工具');
|
||||
if (disabledToolCount > 0) capabilityMeta.push(String(disabledToolCount) + ' 已关闭');
|
||||
if (pageAiNormalizeArray(skill.requiresContextRefs).length > 0) capabilityMeta.push('需要上下文');
|
||||
var categoryText = group === 'mnote' ? String(skill.categoryTitle || skill.category || '').trim() : '';
|
||||
var sourceMetaParts = [];
|
||||
if (group === 'mnote') {
|
||||
sourceMetaParts.push(categoryText || 'MNote');
|
||||
} else {
|
||||
sourceMetaParts.push(pageAiSkillOriginLabel(skill));
|
||||
sourceMetaParts.push('只读查看');
|
||||
}
|
||||
if (capabilityMeta.length) sourceMetaParts = sourceMetaParts.concat(capabilityMeta);
|
||||
if (skill.readOnly && group === 'mnote') sourceMetaParts.push('只读');
|
||||
if (skill.modified) sourceMetaParts.push('modified');
|
||||
var sourceText = sourceMetaParts.filter(Boolean).join(' · ');
|
||||
var description = String(skill.description || '').trim();
|
||||
var hasDescription = description && description !== '---' && description !== '无描述';
|
||||
var displayName = String(skill.title || skill.name || skill.id || '').trim();
|
||||
var disabled = skill.toggleable === false || skill.readOnly === true;
|
||||
var switchControl = group === 'mnote'
|
||||
? '<button type="button" class="wolai-page-ai-skill-switch' + (pageAiSkillEnabled(skill) ? ' is-on' : '') + '" data-page-ai-skill-toggle="' + escapeHtml(skill.id || skill.name) + '" data-page-ai-skill-group="' + escapeHtml(group) + '" data-page-ai-skill-profile="' + escapeHtml(skill.profile || sourceParts.profile || '') + '" data-page-ai-skill-kind="' + escapeHtml(skill.skillKind || '') + '" aria-pressed="' + (pageAiSkillEnabled(skill) ? 'true' : 'false') + '"' + (disabled ? ' disabled' : '') + '>' +
|
||||
'<span></span>' +
|
||||
'</button>'
|
||||
: '<span class="wolai-page-ai-skill-readonly-badge">查看</span>';
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-skill-row">' +
|
||||
'<div class="wolai-page-ai-skill-copy">' +
|
||||
@@ -610,9 +632,7 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
'</div>' +
|
||||
(hasDescription ? '<div class="wolai-page-ai-skill-desc">' + escapeHtml(description) + '</div>' : '') +
|
||||
'</div>' +
|
||||
'<button type="button" class="wolai-page-ai-skill-switch' + (pageAiSkillEnabled(skill) ? ' is-on' : '') + '" data-page-ai-skill-toggle="' + escapeHtml(skill.id || skill.name) + '" data-page-ai-skill-group="' + escapeHtml(group) + '" data-page-ai-skill-profile="' + escapeHtml(skill.profile || sourceParts.profile || '') + '" data-page-ai-skill-kind="' + escapeHtml(skill.skillKind || '') + '" aria-pressed="' + (pageAiSkillEnabled(skill) ? 'true' : 'false') + '"' + (disabled ? ' disabled' : '') + '>' +
|
||||
'<span></span>' +
|
||||
'</button>' +
|
||||
switchControl +
|
||||
'</div>';
|
||||
}).join('')) +
|
||||
'</section>';
|
||||
@@ -751,7 +771,7 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
'<div class="wolai-page-ai-header-actions">' +
|
||||
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="new-session" aria-label="新建 AI 会话" title="新建 AI 会话">+</button>' +
|
||||
'<button type="button" class="wolai-page-ai-icon" data-page-ai-action="history" aria-label="历史会话" title="历史会话">⌕</button>' +
|
||||
'<button type="button" class="wolai-page-ai-icon" data-page-ai-tab="skills" aria-label="技能" title="技能">' +
|
||||
'<button type="button" class="wolai-page-ai-icon" data-page-ai-tab="skills" aria-label="能力" title="能力">' +
|
||||
'<svg class="wolai-page-ai-icon-svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false">' +
|
||||
'<path d="M12 3l1.35 4.15L17.5 8.5l-4.15 1.35L12 14l-1.35-4.15L6.5 8.5l4.15-1.35L12 3z" />' +
|
||||
'<path d="M18.5 13l.8 2.4 2.2.8-2.2.8-.8 2.4-.8-2.4-2.2-.8 2.2-.8.8-2.4z" />' +
|
||||
@@ -856,17 +876,17 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
'<button type="button" class="wolai-page-ai-back" data-page-ai-tab="chat">← 聊天</button>' +
|
||||
'<div class="wolai-page-ai-settings-tabs" role="tablist" aria-label="页面 AI 设置">' +
|
||||
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="agent" role="tab" aria-selected="false">Agent</button>' +
|
||||
'<button type="button" class="wolai-page-ai-tab is-active" data-page-ai-tab="skills" role="tab" aria-selected="true">Skills</button>' +
|
||||
'<button type="button" class="wolai-page-ai-tab is-active" data-page-ai-tab="skills" role="tab" aria-selected="true">能力</button>' +
|
||||
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="runtime" role="tab" aria-selected="false">Runtime</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-ai-skills-toolbar">' +
|
||||
'<label class="wolai-page-ai-skill-search">' +
|
||||
'<span>搜索技能</span>' +
|
||||
'<input type="search" data-page-ai-skill-search placeholder="搜索技能…" />' +
|
||||
'<span>搜索能力</span>' +
|
||||
'<input type="search" data-page-ai-skill-search placeholder="搜索能力…" />' +
|
||||
'</label>' +
|
||||
'<label class="wolai-page-ai-profile-select" data-page-ai-skill-source-control>' +
|
||||
'<span>技能来源</span>' +
|
||||
'<span>能力来源</span>' +
|
||||
'<select data-page-ai-skill-source-select></select>' +
|
||||
'</label>' +
|
||||
'<label class="wolai-page-ai-skill-filter-toggle">' +
|
||||
@@ -882,7 +902,7 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
'<button type="button" class="wolai-page-ai-back" data-page-ai-tab="chat">← 聊天</button>' +
|
||||
'<div class="wolai-page-ai-settings-tabs" role="tablist" aria-label="页面 AI 设置">' +
|
||||
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="agent" role="tab" aria-selected="false">Agent</button>' +
|
||||
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="skills" role="tab" aria-selected="false">Skills</button>' +
|
||||
'<button type="button" class="wolai-page-ai-tab" data-page-ai-tab="skills" role="tab" aria-selected="false">能力</button>' +
|
||||
'<button type="button" class="wolai-page-ai-tab is-active" data-page-ai-tab="runtime" role="tab" aria-selected="true">Runtime</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
@@ -911,9 +931,9 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
'</div>' +
|
||||
'<div class="wolai-page-ai-tool-list" data-page-ai-queue-list></div>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-ai-tools-panel">' +
|
||||
'<div class="wolai-page-ai-tools-panel" hidden>' +
|
||||
'<div class="wolai-page-ai-tools-head">' +
|
||||
'<span>mnote tools</span>' +
|
||||
'<span>MNote 工具调试</span>' +
|
||||
'<span data-page-ai-last-tool>暂无 tool call</span>' +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-ai-inline-error" data-page-ai-tool-error hidden></div>' +
|
||||
@@ -982,6 +1002,231 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function pageAiConversationShouldStickToBottom(conversation) {
|
||||
var distance = conversation.scrollHeight - conversation.scrollTop - conversation.clientHeight;
|
||||
return distance <= 48;
|
||||
}
|
||||
|
||||
function pageAiCaptureOpenToolDetails(conversation) {
|
||||
var openToolDetails = {};
|
||||
conversation.querySelectorAll('[data-page-ai-collapse-card], [data-page-ai-tool-card]').forEach(function(card) {
|
||||
if (!(card instanceof HTMLElement)) return;
|
||||
var key = String(card.getAttribute('data-page-ai-collapse-id') || card.getAttribute('data-page-ai-tool-group-id') || card.getAttribute('data-page-ai-tool-call-id') || '').trim();
|
||||
var details = card.querySelector('[data-page-ai-collapse-details], .wolai-page-ai-tool-details');
|
||||
if (key && details instanceof HTMLDetailsElement && details.open) {
|
||||
openToolDetails[key] = true;
|
||||
}
|
||||
});
|
||||
return openToolDetails;
|
||||
}
|
||||
|
||||
function pageAiRestoreOpenToolDetails(conversation, openToolDetails) {
|
||||
if (!openToolDetails || typeof openToolDetails !== 'object') return;
|
||||
conversation.querySelectorAll('[data-page-ai-collapse-card], [data-page-ai-tool-card]').forEach(function(card) {
|
||||
if (!(card instanceof HTMLElement)) return;
|
||||
var key = String(card.getAttribute('data-page-ai-collapse-id') || card.getAttribute('data-page-ai-tool-group-id') || card.getAttribute('data-page-ai-tool-call-id') || '').trim();
|
||||
if (!key || openToolDetails[key] !== true) return;
|
||||
var details = card.querySelector('[data-page-ai-collapse-details], .wolai-page-ai-tool-details');
|
||||
if (details instanceof HTMLDetailsElement) details.open = true;
|
||||
});
|
||||
}
|
||||
|
||||
function pageAiRenderToolDetailRows(item) {
|
||||
var locationRows = Array.isArray(item.locations) && item.locations.length
|
||||
? '<div class="wolai-page-ai-tool-meta">位置:' + item.locations.map(function(loc) {
|
||||
return '<span style="display:inline-flex;align-items:center;gap:4px;margin-right:8px">' +
|
||||
'<span>' + escapeHtml(loc) + '</span>' +
|
||||
'<button type="button" class="wolai-page-ai-ghost" style="font-size:0.85em;padding:0 4px;min-width:unset" data-page-ai-open-location="' + escapeHtml(loc) + '" data-page-ai-tool-call-id="' + escapeHtml(item.toolCallId || '') + '" title="打开文件">打开</button>' +
|
||||
'</span>';
|
||||
}).join('') + '</div>'
|
||||
: '';
|
||||
return [
|
||||
locationRows,
|
||||
item.argsSummary ? '<div class="wolai-page-ai-tool-meta">参数 ' + escapeHtml(item.argsSummary) + '</div>' : '',
|
||||
item.resultSummary ? '<div class="wolai-page-ai-tool-meta">结果 ' + escapeHtml(item.resultSummary) + '</div>' : '',
|
||||
item.changedFiles && item.changedFiles.length ? '<pre class="wolai-page-ai-tool-meta wolai-page-ai-changed-files">' + escapeHtml(pageAiFormatChangedFiles(item.changedFiles)) + '</pre>' : '',
|
||||
(item.traceId || item.auditId) ? '<div class="wolai-page-ai-tool-meta">' + escapeHtml([item.traceId, item.auditId].filter(Boolean).join(' · ')) + '</div>' : ''
|
||||
].filter(Boolean).join('');
|
||||
}
|
||||
|
||||
function pageAiToolStatusLabel(status) {
|
||||
if (status === 'completed') return '完成';
|
||||
if (status === 'failed') return '失败';
|
||||
return '运行中';
|
||||
}
|
||||
|
||||
function pageAiToolGroupStatus(items) {
|
||||
if (items.some(function(item) { return item.status === 'failed'; })) return 'failed';
|
||||
if (items.some(function(item) { return item.status !== 'completed'; })) return 'running';
|
||||
return 'completed';
|
||||
}
|
||||
|
||||
function pageAiRenderToolGroup(items, groupIndex) {
|
||||
var tools = pageAiNormalizeArray(items);
|
||||
if (!tools.length) return '';
|
||||
var status = pageAiToolGroupStatus(tools);
|
||||
var completedCount = tools.filter(function(item) { return item.status === 'completed'; }).length;
|
||||
var failedCount = tools.filter(function(item) { return item.status === 'failed'; }).length;
|
||||
var runningCount = tools.length - completedCount - failedCount;
|
||||
var countParts = [String(tools.length) + ' 个'];
|
||||
if (completedCount) countParts.push('完成 ' + String(completedCount));
|
||||
if (failedCount) countParts.push('失败 ' + String(failedCount));
|
||||
if (runningCount) countParts.push('运行中 ' + String(runningCount));
|
||||
var firstToolId = String(tools[0] && tools[0].toolCallId || '').trim();
|
||||
var groupId = 'tool-group-' + String(groupIndex) + '-' + (firstToolId || String(tools.length));
|
||||
var rows = tools.map(function(item) {
|
||||
var statusLabel = pageAiToolStatusLabel(item.status);
|
||||
var meta = [statusLabel, item.toolKind, item.toolCallId].filter(Boolean).join(' · ');
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-tool-item" data-page-ai-tool-item data-page-ai-tool-call-id="' + escapeHtml(item.toolCallId || '') + '" data-page-ai-tool-status="' + escapeHtml(item.status || '') + '">' +
|
||||
'<div class="wolai-page-ai-tool-item-head">' +
|
||||
'<strong>' + escapeHtml(item.toolName || item.content || 'tool') + '</strong>' +
|
||||
'<span>' + escapeHtml(meta) + '</span>' +
|
||||
'</div>' +
|
||||
(pageAiRenderToolDetailRows(item) || '<div class="wolai-page-ai-tool-meta">暂无参数或结果详情。</div>') +
|
||||
'</div>';
|
||||
}).join('');
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--tool" data-page-ai-collapse-card data-page-ai-collapse-id="' + escapeHtml(groupId) + '" data-page-ai-tool-card data-page-ai-tool-group="true" data-page-ai-tool-group-id="' + escapeHtml(groupId) + '" data-page-ai-tool-status="' + escapeHtml(status) + '">' +
|
||||
'<div class="wolai-page-ai-message-role">工具</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' +
|
||||
'<details class="wolai-page-ai-tool-details wolai-page-ai-tool-group-details" data-page-ai-collapse-details>' +
|
||||
'<summary>' +
|
||||
'<strong>调用工具</strong>' +
|
||||
'<span>' + escapeHtml(countParts.join(' · ')) + '</span>' +
|
||||
'</summary>' +
|
||||
'<div class="wolai-page-ai-tool-group-list">' + rows + '</div>' +
|
||||
'</details>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function pageAiRenderThoughtGroup(items, groupIndex) {
|
||||
var thoughts = pageAiNormalizeArray(items).filter(function(item) {
|
||||
return item && item.kind === 'thought' && String(item.content || '').trim();
|
||||
});
|
||||
if (!thoughts.length) return '';
|
||||
var firstText = String(thoughts[0] && thoughts[0].content || '').slice(0, 24).replace(/\s+/g, ' ').trim();
|
||||
var groupId = 'thought-group-' + String(groupIndex) + '-' + String(thoughts.length) + '-' + (firstText || 'delta');
|
||||
var content = thoughts.map(function(item) {
|
||||
return String(item.content || '').trim();
|
||||
}).filter(Boolean).join('\n\n');
|
||||
var countLabel = thoughts.length > 1 ? String(thoughts.length) + ' 段' : '1 段';
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--assistant" data-page-ai-collapse-card data-page-ai-collapse-id="' + escapeHtml(groupId) + '" data-page-ai-thought-card="true">' +
|
||||
'<div class="wolai-page-ai-message-role">AI</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' +
|
||||
'<details class="wolai-page-ai-tool-details wolai-page-ai-thought-group-details" data-page-ai-collapse-details data-page-ai-thought-delta="true">' +
|
||||
'<summary>' +
|
||||
'<strong>思考过程</strong>' +
|
||||
'<span>' + escapeHtml(countLabel) + '</span>' +
|
||||
'</summary>' +
|
||||
'<div class="wolai-page-ai-thought-group-list">' + escapeHtml(content) + '</div>' +
|
||||
'</details>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function pageAiRenderNonToolMessage(item) {
|
||||
var roleLabel = item.role === 'user' ? '你' : (item.role === 'tool' ? '工具' : 'AI');
|
||||
if (item.kind === 'thought') {
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--assistant" data-page-ai-collapse-card data-page-ai-collapse-id="thought-single">' +
|
||||
'<div class="wolai-page-ai-message-role">AI</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' +
|
||||
'<details class="wolai-page-ai-tool-details wolai-page-ai-thought-group-details" data-page-ai-collapse-details data-page-ai-thought-delta="true">' +
|
||||
'<summary><strong>思考过程</strong><span>1 段</span></summary>' +
|
||||
'<div class="wolai-page-ai-thought-group-list">' + escapeHtml(item.content || '') + '</div>' +
|
||||
'</details>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
if (item.kind === 'permission') {
|
||||
var permissionActions = item.resolved ? '' : (
|
||||
'<div class="wolai-page-ai-message-actions">' +
|
||||
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="allow" data-page-ai-permission-id="' + escapeHtml(item.permissionId || '') + '">允许</button>' +
|
||||
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="deny" data-page-ai-permission-id="' + escapeHtml(item.permissionId || '') + '">拒绝</button>' +
|
||||
'</div>'
|
||||
);
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--tool" data-page-ai-permission-request="' + escapeHtml(item.permissionId || '') + '">' +
|
||||
'<div class="wolai-page-ai-message-role">权限</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' +
|
||||
'<strong>' + escapeHtml(item.toolName || 'session/request_permission') + '</strong><br />' +
|
||||
'<span>' + escapeHtml(item.argsSummary || item.content || '') + '</span>' +
|
||||
permissionActions +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
if (item.kind === 'plan') {
|
||||
var planEntries = Array.isArray(item.entries) ? item.entries : [];
|
||||
var listHtml = planEntries.map(function(entry) {
|
||||
return '<li style="margin:2px 0">' + escapeHtml(String(entry || '')) + '</li>';
|
||||
}).join('');
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--assistant" data-page-ai-plan="true">' +
|
||||
'<details class="wolai-page-ai-plan-details" open>' +
|
||||
'<summary class="wolai-page-ai-message-role" style="cursor:pointer;user-select:none;opacity:0.8;font-size:0.85em">执行计划 · ' + planEntries.length + ' 步</summary>' +
|
||||
'<div class="wolai-page-ai-message-text" style="margin-top:4px;opacity:0.75;font-size:0.9em;border-left:2px solid var(--wolai-border-color, #ddd);padding-left:8px">' +
|
||||
'<ol style="margin:4px 0;padding-left:20px">' + listHtml + '</ol>' +
|
||||
'</div>' +
|
||||
'</details>' +
|
||||
'</div>';
|
||||
}
|
||||
var streamingAttr = item.streaming ? ' data-page-ai-streaming="true"' : '';
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--' + escapeHtml(item.role || 'assistant') + '"' + streamingAttr + '>' +
|
||||
'<div class="wolai-page-ai-message-role">' + escapeHtml(roleLabel) + '</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' + (item.role === 'assistant' ? renderPageAiMarkdown(item.content || '') : escapeHtml(item.content || '')) + '</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function pageAiConversationRenderHtml(messages) {
|
||||
var turns = [];
|
||||
var current = null;
|
||||
function ensureTurn() {
|
||||
if (!current) current = { user: null, tools: [], status: [], assistants: [], others: [] };
|
||||
return current;
|
||||
}
|
||||
function flushTurn() {
|
||||
if (!current) return;
|
||||
turns.push(current);
|
||||
current = null;
|
||||
}
|
||||
pageAiNormalizeArray(messages).forEach(function(item) {
|
||||
if (item && item.role === 'user') {
|
||||
flushTurn();
|
||||
current = { user: item, tools: [], status: [], assistants: [], others: [] };
|
||||
return;
|
||||
}
|
||||
var turn = ensureTurn();
|
||||
if (item && item.role === 'tool' && item.kind !== 'permission') {
|
||||
turn.tools.push(item);
|
||||
} else if (item && (item.kind === 'thought' || item.kind === 'plan' || item.kind === 'permission')) {
|
||||
turn.status.push(item);
|
||||
} else if (item && item.role === 'assistant') {
|
||||
turn.assistants.push(item);
|
||||
} else {
|
||||
turn.others.push(item);
|
||||
}
|
||||
});
|
||||
flushTurn();
|
||||
var toolGroupIndex = 0;
|
||||
var thoughtGroupIndex = 0;
|
||||
return turns.map(function(turn) {
|
||||
var html = [];
|
||||
if (turn.user) html.push(pageAiRenderNonToolMessage(turn.user));
|
||||
if (turn.tools.length) html.push(pageAiRenderToolGroup(turn.tools, toolGroupIndex++));
|
||||
var thoughts = turn.status.filter(function(item) { return item && item.kind === 'thought'; });
|
||||
var otherStatus = turn.status.filter(function(item) { return !(item && item.kind === 'thought'); });
|
||||
if (thoughts.length) html.push(pageAiRenderThoughtGroup(thoughts, thoughtGroupIndex++));
|
||||
html = html.concat(otherStatus.map(pageAiRenderNonToolMessage));
|
||||
html = html.concat(turn.assistants.map(pageAiRenderNonToolMessage));
|
||||
html = html.concat(turn.others.map(pageAiRenderNonToolMessage));
|
||||
return html.join('');
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function renderPageAiConversation() {
|
||||
var drawer = ensurePageAiDrawer();
|
||||
renderPageAiSuggestions();
|
||||
@@ -1022,86 +1267,17 @@ export function createSidebarPageAiRenderRuntime(context) {
|
||||
conversation.innerHTML = '<div class="wolai-page-ai-empty">围绕当前页面提问,我会优先使用当前页内容、页面结构和已保存设置。</div>';
|
||||
return;
|
||||
}
|
||||
conversation.innerHTML = pageUiState.pageAiMessages.map(function(item) {
|
||||
var roleLabel = item.role === 'user' ? '你' : (item.role === 'tool' ? '工具' : 'AI');
|
||||
if (item.role === 'tool') {
|
||||
var statusLabel = item.status === 'completed' ? '完成' : (item.status === 'failed' ? '失败' : '运行中');
|
||||
var locationRows = Array.isArray(item.locations) && item.locations.length
|
||||
? '<div class="wolai-page-ai-tool-meta">位置:' + item.locations.map(function(loc, idx) {
|
||||
return '<span style="display:inline-flex;align-items:center;gap:4px;margin-right:8px">' +
|
||||
'<span>' + escapeHtml(loc) + '</span>' +
|
||||
'<button type="button" class="wolai-page-ai-ghost" style="font-size:0.85em;padding:0 4px;min-width:unset" data-page-ai-open-location="' + escapeHtml(loc) + '" data-page-ai-tool-call-id="' + escapeHtml(item.toolCallId || '') + '" title="打开文件">打开</button>' +
|
||||
'</span>';
|
||||
}).join('') + '</div>'
|
||||
: '';
|
||||
var detailRows = [
|
||||
locationRows,
|
||||
item.argsSummary ? '<div class="wolai-page-ai-tool-meta">参数 ' + escapeHtml(item.argsSummary) + '</div>' : '',
|
||||
item.resultSummary ? '<div class="wolai-page-ai-tool-meta">结果 ' + escapeHtml(item.resultSummary) + '</div>' : '',
|
||||
item.changedFiles && item.changedFiles.length ? '<pre class="wolai-page-ai-tool-meta wolai-page-ai-changed-files">' + escapeHtml(pageAiFormatChangedFiles(item.changedFiles)) + '</pre>' : '',
|
||||
(item.traceId || item.auditId) ? '<div class="wolai-page-ai-tool-meta">' + escapeHtml([item.traceId, item.auditId].filter(Boolean).join(' · ')) + '</div>' : ''
|
||||
].filter(Boolean).join('');
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--tool" data-page-ai-tool-card data-page-ai-tool-call-id="' + escapeHtml(item.toolCallId || '') + '" data-page-ai-tool-status="' + escapeHtml(item.status || '') + '">' +
|
||||
'<div class="wolai-page-ai-message-role">' + escapeHtml(roleLabel) + '</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' +
|
||||
'<details class="wolai-page-ai-tool-details">' +
|
||||
'<summary>' +
|
||||
'<strong>' + escapeHtml(item.toolName || item.content || 'tool') + '</strong>' +
|
||||
'<span>' + escapeHtml([statusLabel, item.toolKind, item.toolCallId].filter(Boolean).join(' · ')) + '</span>' +
|
||||
'</summary>' +
|
||||
(detailRows || '<div class="wolai-page-ai-tool-meta">暂无参数或结果详情。</div>') +
|
||||
'</details>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
if (item.kind === 'thought') {
|
||||
return '' +
|
||||
'<details class="wolai-page-ai-message wolai-page-ai-message--assistant" data-page-ai-thought-delta="true">' +
|
||||
'<summary class="wolai-page-ai-message-role" style="cursor:pointer;user-select:none;opacity:0.7;font-size:0.85em">思考过程</summary>' +
|
||||
'<div class="wolai-page-ai-message-text" style="margin-top:4px;opacity:0.6;font-size:0.9em;border-left:2px solid var(--wolai-border-color, #ddd);padding-left:8px">' + escapeHtml(item.content || '') + '</div>' +
|
||||
'</details>';
|
||||
}
|
||||
if (item.kind === 'permission') {
|
||||
var permissionActions = item.resolved ? '' : (
|
||||
'<div class="wolai-page-ai-message-actions">' +
|
||||
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="allow" data-page-ai-permission-id="' + escapeHtml(item.permissionId || '') + '">允许</button>' +
|
||||
'<button type="button" class="wolai-page-ai-ghost" data-page-ai-permission-action="deny" data-page-ai-permission-id="' + escapeHtml(item.permissionId || '') + '">拒绝</button>' +
|
||||
'</div>'
|
||||
);
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--tool" data-page-ai-permission-request="' + escapeHtml(item.permissionId || '') + '">' +
|
||||
'<div class="wolai-page-ai-message-role">权限</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' +
|
||||
'<strong>' + escapeHtml(item.toolName || 'session/request_permission') + '</strong><br />' +
|
||||
'<span>' + escapeHtml(item.argsSummary || item.content || '') + '</span>' +
|
||||
permissionActions +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
if (item.kind === 'plan') {
|
||||
var planEntries = Array.isArray(item.entries) ? item.entries : [];
|
||||
var listHtml = planEntries.map(function(entry, idx) {
|
||||
return '<li style="margin:2px 0">' + escapeHtml(String(entry || '')) + '</li>';
|
||||
}).join('');
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--assistant" data-page-ai-plan="true">' +
|
||||
'<details class="wolai-page-ai-plan-details" open>' +
|
||||
'<summary class="wolai-page-ai-message-role" style="cursor:pointer;user-select:none;opacity:0.8;font-size:0.85em">执行计划 · ' + planEntries.length + ' 步</summary>' +
|
||||
'<div class="wolai-page-ai-message-text" style="margin-top:4px;opacity:0.75;font-size:0.9em;border-left:2px solid var(--wolai-border-color, #ddd);padding-left:8px">' +
|
||||
'<ol style="margin:4px 0;padding-left:20px">' + listHtml + '</ol>' +
|
||||
'</div>' +
|
||||
'</details>' +
|
||||
'</div>';
|
||||
}
|
||||
var streamingAttr = item.streaming ? ' data-page-ai-streaming="true"' : '';
|
||||
return '' +
|
||||
'<div class="wolai-page-ai-message wolai-page-ai-message--' + escapeHtml(item.role || 'assistant') + '"' + streamingAttr + '>' +
|
||||
'<div class="wolai-page-ai-message-role">' + escapeHtml(roleLabel) + '</div>' +
|
||||
'<div class="wolai-page-ai-message-text">' + (item.role === 'assistant' ? renderPageAiMarkdown(item.content || '') : escapeHtml(item.content || '')) + '</div>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
conversation.scrollTop = conversation.scrollHeight;
|
||||
var shouldStickToBottom = pageAiConversationShouldStickToBottom(conversation);
|
||||
var previousScrollTop = conversation.scrollTop;
|
||||
var openToolDetails = pageAiCaptureOpenToolDetails(conversation);
|
||||
conversation.innerHTML = pageAiConversationRenderHtml(pageUiState.pageAiMessages);
|
||||
pageAiRestoreOpenToolDetails(conversation, openToolDetails);
|
||||
if (shouldStickToBottom) {
|
||||
conversation.scrollTop = conversation.scrollHeight;
|
||||
} else {
|
||||
var maxScrollTop = Math.max(0, conversation.scrollHeight - conversation.clientHeight);
|
||||
conversation.scrollTop = Math.min(previousScrollTop, maxScrollTop);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user