fix: 移除错误的对话切换跳转逻辑并修复工具执行返回值问题

主要修复:
1. 移除前端"取消跳转到正在运行的对话"的错误逻辑
   - 删除 switchConversation 中的任务检查和确认提示
   - 删除 createNewConversation 中的跳转回运行对话逻辑
   - 删除 loadConversation 中对未定义变量 hasActiveTask 的引用

2. 修复后端工具执行返回值问题
   - 修复 execute_tool_calls 在用户停止时返回 None 的 bug
   - 确保所有返回路径都返回包含 stopped 和 last_tool_call_time 的字典

3. 其他改进
   - 添加代码复制功能 (handleCopyCodeClick)
   - 移除 FocusPanel 相关代码
   - 更新个性化配置 (enhanced_tool_display)
   - 样式和主题优化

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
JOJO 2026-03-08 17:42:07 +08:00
parent 5ce21eb280
commit 43409c523e
27 changed files with 2725 additions and 271 deletions

View File

@ -607,12 +607,11 @@ class MainTerminalToolsDefinitionMixin:
"parameters": {
"type": "object",
"properties": self._inject_intent({
"memory_type": {"type": "string", "enum": ["main", "task"], "description": "记忆类型"},
"content": {"type": "string", "description": "条目内容。append/replace 时必填"},
"operation": {"type": "string", "enum": ["append", "replace", "delete"], "description": "操作类型"},
"index": {"type": "integer", "description": "要替换/删除的序号从1开始"}
}),
"required": ["memory_type", "operation"]
"required": ["operation"]
}
}
},

View File

@ -585,7 +585,6 @@ class MainTerminalToolsExecutionMixin:
}
elif tool_name == "update_memory":
memory_type = arguments["memory_type"]
operation = arguments["operation"]
content = arguments.get("content")
index = arguments.get("index")
@ -598,8 +597,9 @@ class MainTerminalToolsExecutionMixin:
elif operation == "delete" and (index is None or index <= 0):
result = {"success": False, "error": "delete 操作需要有效的 index"}
else:
# 统一使用 main 记忆类型
result = self.memory_manager.update_entries(
memory_type=memory_type,
memory_type="main",
operation=operation,
content=content,
index=index

View File

@ -41,6 +41,7 @@ DEFAULT_PERSONALIZATION_CONFIG: Dict[str, Any] = {
"default_model": "kimi-k2.5",
"image_compression": "original", # original / 1080p / 720p / 540p
"silent_tool_disable": False, # 禁用工具时不向模型插入提示
"enhanced_tool_display": True, # 增强工具显示
}
__all__ = [
@ -183,6 +184,12 @@ def sanitize_personalization_payload(
else:
base["silent_tool_disable"] = bool(base.get("silent_tool_disable"))
# 增强工具显示
if "enhanced_tool_display" in data:
base["enhanced_tool_display"] = bool(data.get("enhanced_tool_display"))
else:
base["enhanced_tool_display"] = bool(base.get("enhanced_tool_display", True))
return base

10
package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "4.1.0",
"dependencies": {
"@types/html2canvas": "^0.5.35",
"diff": "^8.0.3",
"enquirer": "^2.4.1",
"html2canvas": "^1.4.1",
"katex": "^0.16.9",
@ -2139,6 +2140,15 @@
"node": ">=0.10"
}
},
"node_modules/diff": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz",
"integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",

View File

@ -12,6 +12,7 @@
},
"dependencies": {
"@types/html2canvas": "^0.5.35",
"diff": "^8.0.3",
"enquirer": "^2.4.1",
"html2canvas": "^1.4.1",
"katex": "^0.16.9",

View File

@ -51,7 +51,7 @@ async def execute_tool_calls(*, web_terminal, tool_calls, sender, messages, clie
'reason': 'user_stop'
})
clear_stop_flag(client_sid, username)
return
return {"stopped": True, "last_tool_call_time": last_tool_call_time}
# 工具调用间隔控制
current_time = time.time()

View File

@ -127,7 +127,7 @@ def create_conversation(terminal: WebTerminal, workspace: UserWorkspace, usernam
"""创建新对话"""
try:
data = request.get_json() or {}
# 前端现在期望“新建对话”回到用户配置的默认模型/模式,
# 前端现在期望"新建对话"回到用户配置的默认模型/模式,
# 只有当客户端显式要求保留当前模式时才使用传入值。
preserve_mode = bool(data.get('preserve_mode'))
thinking_mode = data.get('thinking_mode') if preserve_mode and 'thinking_mode' in data else None

View File

@ -293,14 +293,6 @@
</main>
<div v-if="!isMobileViewport" class="resize-handle" @mousedown="startResize('right', $event)"></div>
<FocusPanel
v-if="!isMobileViewport"
:collapsed="rightCollapsed"
:width="rightWidth"
:icon-style="iconStyle"
:get-language-class="getLanguageClass"
/>
</div>
<PersonalizationDrawer />
@ -510,26 +502,6 @@
</div>
</div>
</transition>
<transition name="mobile-panel-overlay">
<div
v-if="isMobileViewport && activeMobileOverlay === 'focus'"
class="mobile-panel-overlay mobile-panel-overlay--right"
@click.self="closeMobileOverlay"
>
<div class="mobile-panel-sheet mobile-panel-sheet--focus">
<FocusPanel
class="mobile-overlay-content"
:collapsed="false"
:width="Math.min(rightWidth, 420)"
:icon-style="iconStyle"
:get-language-class="getLanguageClass"
:show-close-button="true"
@close="closeMobileOverlay"
/>
</div>
</div>
</transition>
</template>
</AppShell>
</template>
@ -542,7 +514,6 @@ import VideoPicker from './components/overlay/VideoPicker.vue';
const mobilePanelIcon = new URL('../icons/align-left.svg', import.meta.url).href;
const mobileMenuIcons = {
workspace: new URL('../icons/folder.svg', import.meta.url).href,
focus: new URL('../icons/eye.svg', import.meta.url).href,
personal: new URL('../icons/user.svg', import.meta.url).href
};

View File

@ -1,7 +1,6 @@
import ChatArea from '../components/chat/ChatArea.vue';
import ConversationSidebar from '../components/sidebar/ConversationSidebar.vue';
import LeftPanel from '../components/panels/LeftPanel.vue';
import FocusPanel from '../components/panels/FocusPanel.vue';
import TokenDrawer from '../components/token/TokenDrawer.vue';
import PersonalizationDrawer from '../components/personalization/PersonalizationDrawer.vue';
import LiquidGlassWidget from '../components/experiments/LiquidGlassWidget.vue';
@ -15,7 +14,6 @@ export const appComponents = {
ChatArea,
ConversationSidebar,
LeftPanel,
FocusPanel,
TokenDrawer,
PersonalizationDrawer,
LiquidGlassWidget,

View File

@ -59,6 +59,7 @@ export async function mounted() {
document.addEventListener('click', this.handleClickOutsidePanelMenu);
document.addEventListener('click', this.handleClickOutsideHeaderMenu);
document.addEventListener('click', this.handleClickOutsideMobileMenu);
document.addEventListener('click', this.handleCopyCodeClick);
window.addEventListener('popstate', this.handlePopState);
window.addEventListener('keydown', this.handleMobileOverlayEscape);
this.setupMobileViewportWatcher();
@ -89,6 +90,7 @@ export function beforeUnmount() {
document.removeEventListener('click', this.handleClickOutsidePanelMenu);
document.removeEventListener('click', this.handleClickOutsideHeaderMenu);
document.removeEventListener('click', this.handleClickOutsideMobileMenu);
document.removeEventListener('click', this.handleCopyCodeClick);
window.removeEventListener('popstate', this.handlePopState);
window.removeEventListener('keydown', this.handleMobileOverlayEscape);
this.teardownMobileViewportWatcher();

View File

@ -169,46 +169,6 @@ export const conversationMethods = {
return;
}
// 检查是否有运行中的任务,如果有则提示用户
let hasActiveTask = false;
try {
const { useTaskStore } = await import('../../stores/task');
const taskStore = useTaskStore();
if (taskStore.hasActiveTask) {
hasActiveTask = true;
// 显示提示
const confirmed = await this.confirmAction({
title: '切换对话',
message: '当前有任务正在执行,切换对话后任务会停止。确定要切换吗?',
confirmText: '切换',
cancelText: '取消'
});
if (!confirmed) {
// 用户取消切换
this.suppressTitleTyping = false;
this.titleReady = true;
return;
}
// 用户确认,停止任务
debugLog('[切换对话] 用户确认,正在停止任务...');
await taskStore.cancelTask();
taskStore.clearTask();
// 重置任务相关状态
this.streamingMessage = false;
this.taskInProgress = false;
this.stopRequested = false;
debugLog('[切换对话] 任务已停止');
}
} catch (error) {
console.error('[切换对话] 检查/停止任务失败:', error);
}
try {
// 1. 调用加载API
const response = await fetch(`/api/conversations/${conversationId}/load`, {
@ -245,16 +205,6 @@ export const conversationMethods = {
messagesLen: Array.isArray(this.messages) ? this.messages.length : 'n/a'
});
// 如果停止了任务,显示提示
if (hasActiveTask) {
this.uiPushToast({
title: '任务已停止',
message: '切换对话后,之前的任务已停止',
type: 'info',
duration: 3000
});
}
} else {
console.error('对话加载失败:', result.message);
this.suppressTitleTyping = false;

View File

@ -1170,5 +1170,55 @@ export const uiMethods = {
confirmAction(options = {}) {
return this.uiRequestConfirm(options);
},
async handleCopyCodeClick(event) {
const target = event.target;
if (!target || !target.classList || !target.classList.contains('copy-code-btn')) {
return;
}
const blockId = target.getAttribute('data-code');
if (!blockId) {
return;
}
// 防止重复点击
if (target.classList.contains('copied')) {
return;
}
const selector = `[data-code-id="${blockId.replace(/"/g, '\\"')}"]`;
const codeEl = document.querySelector(selector);
if (!codeEl) {
return;
}
const encoded = codeEl.getAttribute('data-original-code');
const content = encoded ? this.decodeHtmlEntities(encoded) : codeEl.textContent || '';
if (!content.trim()) {
return;
}
try {
await navigator.clipboard.writeText(content);
// 添加 copied 类,切换为对勾图标
target.classList.add('copied');
// 5秒后恢复
setTimeout(() => {
target.classList.remove('copied');
}, 5000);
} catch (error) {
console.warn('复制失败:', error);
}
},
decodeHtmlEntities(text) {
const textarea = document.createElement('textarea');
textarea.innerHTML = text;
return textarea.value;
}
};

View File

@ -81,7 +81,8 @@
</div>
<div class="collapsible-content" :style="contentStyle(blockKey(action, idx))">
<div class="content-inner">
<div v-if="action.tool?.name === 'web_search' && action.tool?.result">
<div v-if="shouldUseEnhancedDisplay && renderToolResult(action)" v-html="renderToolResult(action)"></div>
<div v-else-if="!shouldUseEnhancedDisplay && action.tool?.name === 'web_search' && action.tool?.result">
<div class="search-meta">
<div><strong>搜索内容</strong>{{ action.tool.result.query || action.tool.arguments?.query }}</div>
<div><strong>主题</strong>{{ formatSearchTopic(action.tool.result.filters || {}) }}</div>
@ -124,6 +125,8 @@
<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { usePersonalizationStore } from '@/stores/personalization';
import { renderEnhancedToolResult } from './actions/toolRenderers';
defineOptions({ name: 'StackedBlocks' });
@ -143,6 +146,17 @@ const props = defineProps<{
formatSearchDomains: (filters: Record<string, any>) => string;
}>();
// personalization store
const personalizationStore = usePersonalizationStore();
const shouldUseEnhancedDisplay = computed(() => {
return personalizationStore.form.enhanced_tool_display;
});
const renderToolResult = (action: any) => {
return renderEnhancedToolResult(action, props.formatSearchTopic, props.formatSearchTime, props.formatSearchDomains);
};
const VISIBLE_LIMIT = 6;
const shell = ref<HTMLElement | null>(null);

View File

@ -25,34 +25,7 @@
:ref="el => registerCollapseContent && registerCollapseContent(collapseKey || action.tool.id || action.id || 'tool', el)"
>
<div class="content-inner">
<div v-if="action.tool.name === 'web_search' && action.tool.result">
<div class="search-meta">
<div><strong>搜索内容</strong>{{ action.tool.result.query || action.tool.arguments.query }}</div>
<div><strong>主题</strong>{{ formatSearchTopic(action.tool.result.filters || {}) }}</div>
<div><strong>时间范围</strong>{{ formatSearchTime(action.tool.result.filters || {}) }}</div>
<div><strong>限定网站</strong>{{ formatSearchDomains(action.tool.result.filters || {}) }}</div>
<div><strong>结果数量</strong>{{ action.tool.result.total_results }}</div>
</div>
<div v-if="action.tool.result.results && action.tool.result.results.length" class="search-result-list">
<div v-for="item in action.tool.result.results" :key="item.url || item.index" class="search-result-item">
<div class="search-result-title">{{ item.title || '无标题' }}</div>
<div class="search-result-url">
<a v-if="item.url" :href="item.url" target="_blank">{{ item.url }}</a><span v-else>无可用链接</span>
</div>
</div>
</div>
<div v-else class="search-empty">未返回详细的搜索结果</div>
</div>
<div v-else-if="action.tool.name === 'run_python' && action.tool.result">
<div class="code-block">
<div class="code-label">代码</div>
<pre><code class="language-python">{{ action.tool.result.code || action.tool.arguments.code }}</code></pre>
</div>
<div v-if="action.tool.result.output" class="output-block">
<div class="output-label">输出</div>
<pre>{{ action.tool.result.output }}</pre>
</div>
</div>
<div v-if="shouldUseEnhancedDisplay" v-html="renderEnhancedToolResult()"></div>
<div v-else>
<pre>{{ JSON.stringify(action.tool.result || action.tool.arguments, null, 2) }}</pre>
</div>
@ -66,9 +39,12 @@
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { usePersonalizationStore } from '@/stores/personalization';
defineOptions({ name: 'ToolAction' });
defineProps<{
const props = defineProps<{
action: any;
expanded: boolean;
iconStyle: (key: string) => Record<string, string>;
@ -85,4 +61,891 @@ defineProps<{
}>();
defineEmits<{ (event: 'toggle'): void }>();
// store
const personalizationStore = usePersonalizationStore();
const shouldUseEnhancedDisplay = computed(() => {
return personalizationStore.form.enhanced_tool_display && props.action.tool.result;
});
function escapeHtml(text: string): string {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function formatBytes(bytes: number): string {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
}
function renderEnhancedToolResult(): string {
const tool = props.action.tool;
const result = tool.result;
const args = tool.arguments || {};
const name = tool.name;
//
if (name === 'web_search') {
return renderWebSearch(result, args);
} else if (name === 'extract_webpage') {
return renderExtractWebpage(result, args);
} else if (name === 'save_webpage') {
return renderSaveWebpage(result, args);
}
//
else if (name === 'create_file' || name === 'create_folder') {
return renderCreateFile(result, args);
} else if (name === 'write_file') {
return renderWriteFile(result, args);
} else if (name === 'edit_file') {
return renderEditFile(result, args);
} else if (name === 'append_to_file') {
return renderAppendToFile(result, args);
} else if (name === 'modify_file') {
return renderModifyFile(result, args);
} else if (name === 'delete_file') {
return renderDeleteFile(result, args);
} else if (name === 'rename_file') {
return renderRenameFile(result, args);
}
//
else if (name === 'read_file') {
return renderReadFile(result, args);
} else if (name === 'vlm_analyze') {
return renderVlmAnalyze(result, args);
} else if (name === 'ocr_image') {
return renderOcrImage(result, args);
} else if (name === 'view_image') {
return renderViewImage(result, args);
}
//
else if (name === 'terminal_session') {
return renderTerminalSession(result, args);
} else if (name === 'terminal_input') {
return renderTerminalInput(result, args);
} else if (name === 'terminal_snapshot') {
return renderTerminalSnapshot(result, args);
} else if (name === 'sleep') {
return renderSleep(result, args);
}
//
else if (name === 'run_command') {
return renderRunCommand(result, args);
} else if (name === 'run_python') {
return renderRunPython(result, args);
}
//
else if (name === 'update_memory') {
return renderUpdateMemory(result, args);
}
//
else if (name === 'todo_create') {
return renderTodoCreate(result, args);
} else if (name === 'todo_update_task') {
return renderTodoUpdate(result, args);
}
//
else if (name === 'trigger_easter_egg') {
return renderEasterEgg(result, args);
}
// JSON
return `<pre>${escapeHtml(JSON.stringify(result || args, null, 2))}</pre>`;
}
// ===== =====
function renderWebSearch(result: any, args: any): string {
const query = result.query || args.query || '';
const filters = result.filters || {};
const totalResults = result.total_results || 0;
const results = result.results || [];
let html = '<div class="tool-result-meta">';
html += `<div><strong>搜索内容:</strong>${escapeHtml(query)}</div>`;
html += `<div><strong>主题:</strong>${escapeHtml(props.formatSearchTopic(filters))}</div>`;
html += `<div><strong>时间范围:</strong>${escapeHtml(props.formatSearchTime(filters))}</div>`;
html += `<div><strong>限定网站:</strong>${escapeHtml(props.formatSearchDomains(filters))}</div>`;
html += `<div><strong>结果数量:</strong>${totalResults}</div>`;
html += '</div>';
if (results.length > 0) {
html += '<div class="search-result-list">';
results.forEach((item: any) => {
html += '<div class="search-result-item">';
html += `<div class="search-result-title">${escapeHtml(item.title || '无标题')}</div>`;
html += '<div class="search-result-url">';
if (item.url) {
html += `<a href="${escapeHtml(item.url)}" target="_blank">${escapeHtml(item.url)}</a>`;
} else {
html += '<span>无可用链接</span>';
}
html += '</div></div>';
});
html += '</div>';
} else {
html += '<div class="tool-result-empty">未返回详细的搜索结果。</div>';
}
return html;
}
function renderExtractWebpage(result: any, args: any): string {
const url = args.url || result.url || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
const content = result.content || result.text || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>URL</strong>${escapeHtml(url)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
if (content) {
html += '<div class="tool-result-content scrollable">';
html += `<pre>${escapeHtml(content)}</pre>`;
html += '</div>';
}
return html;
}
function renderSaveWebpage(result: any, args: any): string {
const url = args.url || result.url || '';
const path = result.path || args.save_path || '';
const status = result.success ? '✓ 已保存' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>URL</strong>${escapeHtml(url)}</div>`;
html += `<div><strong>保存路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
return html;
}
// ===== =====
function renderCreateFile(result: any, args: any): string {
const path = args.path || result.path || '';
const status = result.success ? '✓ 已创建' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
return html;
}
function renderWriteFile(result: any, args: any): string {
const path = args.path || result.path || '';
const status = result.success ? '✓ 已写入' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
return html;
}
function renderEditFile(result: any, args: any): string {
const path = args.path || result.path || '';
const status = result.success ? '✓ 已编辑' : '✗ 失败';
const replacements = args.replacements || [];
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
if (replacements.length > 0) {
html += '<div class="tool-result-diff scrollable">';
replacements.forEach((rep: any, idx: number) => {
if (idx > 0) html += '<div class="diff-separator">⋮</div>';
const oldText = rep.old_text || rep.old || '';
const newText = rep.new_text || rep.new || '';
if (oldText) {
const oldLines = oldText.split('\n');
oldLines.forEach((line: string) => {
html += `<div class="diff-line diff-remove">- ${escapeHtml(line)}</div>`;
});
}
if (newText) {
const newLines = newText.split('\n');
newLines.forEach((line: string) => {
html += `<div class="diff-line diff-add">+ ${escapeHtml(line)}</div>`;
});
}
});
html += '</div>';
}
return html;
}
function renderAppendToFile(result: any, args: any): string {
const path = args.path || result.path || '';
const status = result.success ? '✓ 已追加' : '✗ 失败';
const content = args.content || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
if (content) {
html += '<div class="tool-result-content scrollable">';
html += '<div class="content-label">追加内容:</div>';
html += `<pre>${escapeHtml(content)}</pre>`;
html += '</div>';
}
return html;
}
function renderModifyFile(result: any, args: any): string {
const path = args.path || result.path || '';
const status = result.success ? '✓ 已修改' : '✗ 失败';
const operations = args.operations || [];
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
if (operations.length > 0) {
html += '<div class="tool-result-diff scrollable">';
operations.forEach((op: any, idx: number) => {
if (idx > 0) html += '<div class="diff-separator">⋮</div>';
const type = op.type || op.operation;
const oldText = op.old_text || op.old || '';
const newText = op.new_text || op.new || '';
html += `<div class="diff-operation">[${idx + 1}] ${escapeHtml(type)}</div>`;
if (oldText) {
const oldLines = oldText.split('\n');
oldLines.forEach((line: string) => {
html += `<div class="diff-line diff-remove">- ${escapeHtml(line)}</div>`;
});
}
if (newText) {
const newLines = newText.split('\n');
newLines.forEach((line: string) => {
html += `<div class="diff-line diff-add">+ ${escapeHtml(line)}</div>`;
});
}
});
html += '</div>';
}
return html;
}
function renderDeleteFile(result: any, args: any): string {
const path = args.path || result.path || '';
const status = result.success ? '✓ 已删除' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
return html;
}
function renderRenameFile(result: any, args: any): string {
const oldPath = args.old_path || args.source || result.old_path || '';
const newPath = args.new_path || args.destination || result.new_path || '';
const status = result.success ? '✓ 已重命名' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(oldPath)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += `<div><strong>重命名:</strong>${escapeHtml(oldPath)}${escapeHtml(newPath)}</div>`;
html += '</div>';
return html;
}
// ===== =====
function renderReadFile(result: any, args: any): string {
const path = args.path || result.path || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
const size = result.size || result.file_size || 0;
const content = result.content || result.text || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
if (size > 0) {
html += `<div><strong>大小:</strong>${formatBytes(size)}</div>`;
}
html += '</div>';
if (content) {
html += '<div class="tool-result-content scrollable">';
html += `<pre>${escapeHtml(content)}</pre>`;
html += '</div>';
}
return html;
}
function renderVlmAnalyze(result: any, args: any): string {
const path = args.image_path || args.path || result.path || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
const analysis = result.analysis || result.result || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
if (analysis) {
html += '<div class="tool-result-content scrollable">';
html += '<div class="content-label">分析结果:</div>';
html += `<pre>${escapeHtml(analysis)}</pre>`;
html += '</div>';
}
return html;
}
function renderOcrImage(result: any, args: any): string {
const path = args.image_path || args.path || result.path || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
const size = result.size || result.file_size || 0;
const text = result.text || result.ocr_text || '';
const imageUrl = result.image_url || result.url || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
if (size > 0) {
html += `<div><strong>大小:</strong>${formatBytes(size)}</div>`;
}
html += '</div>';
if (imageUrl) {
html += `<div class="tool-result-image"><img src="${escapeHtml(imageUrl)}" alt="OCR图片" style="max-width: 100%; height: auto;" /></div>`;
}
if (text) {
html += '<div class="tool-result-content scrollable">';
html += '<div class="content-label">识别文本:</div>';
html += `<pre>${escapeHtml(text)}</pre>`;
html += '</div>';
}
return html;
}
function renderViewImage(result: any, args: any): string {
const path = args.image_path || args.path || result.path || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
const size = result.size || result.file_size || 0;
const imageUrl = result.image_url || result.url || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
if (size > 0) {
html += `<div><strong>大小:</strong>${formatBytes(size)}</div>`;
}
html += '</div>';
if (imageUrl) {
html += `<div class="tool-result-image"><img src="${escapeHtml(imageUrl)}" alt="查看图片" style="max-width: 100%; height: auto;" /></div>`;
}
return html;
}
// ===== =====
function renderTerminalSession(result: any, args: any): string {
const operation = args.operation || args.action || 'start';
const sessionName = args.session_name || args.name || result.session_name || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>操作:</strong>${escapeHtml(operation)}</div>`;
html += `<div><strong>终端名:</strong>${escapeHtml(sessionName)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
return html;
}
function renderTerminalInput(result: any, args: any): string {
const command = args.command || args.input || '';
const timeout = args.timeout || 30;
const output = result.output || result.result || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>指令:</strong>${escapeHtml(command)}</div>`;
html += `<div><strong>超时时间:</strong>${timeout}秒</div>`;
html += '</div>';
if (output) {
html += '<div class="tool-result-content scrollable">';
html += '<div class="content-label">输出:</div>';
html += `<pre>${escapeHtml(output)}</pre>`;
html += '</div>';
}
return html;
}
function renderTerminalSnapshot(result: any, args: any): string {
const sessionName = args.session_name || args.name || result.session_name || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
const startLine = args.start_line || 0;
const endLine = args.end_line || 0;
const content = result.content || result.output || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>状态:</strong>${status}</div>`;
html += `<div><strong>终端名:</strong>${escapeHtml(sessionName)}</div>`;
if (startLine || endLine) {
html += `<div><strong>行范围:</strong>${startLine} - ${endLine}</div>`;
}
html += '</div>';
if (content) {
html += '<div class="tool-result-content scrollable">';
html += '<div class="content-label">获取的内容:</div>';
html += `<pre>${escapeHtml(content)}</pre>`;
html += '</div>';
}
return html;
}
function renderSleep(result: any, args: any): string {
const seconds = args.seconds || args.duration || 0;
const status = result.success ? '✓ 完成' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>等待时间:</strong>${seconds}秒</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
return html;
}
// ===== =====
function renderRunCommand(result: any, args: any): string {
const command = args.command || '';
const timeout = args.timeout || 30;
const output = result.output || result.stdout || '';
const exitCode = result.exit_code !== undefined ? result.exit_code : result.returncode;
let html = '<div class="tool-result-meta">';
html += `<div><strong>指令:</strong>${escapeHtml(command)}</div>`;
html += `<div><strong>超时时间:</strong>${timeout}秒</div>`;
if (exitCode !== undefined) {
html += `<div><strong>退出码:</strong>${exitCode}</div>`;
}
html += '</div>';
if (output) {
html += '<div class="tool-result-content scrollable">';
html += '<div class="content-label">输出:</div>';
html += `<pre>${escapeHtml(output)}</pre>`;
html += '</div>';
}
return html;
}
function renderRunPython(result: any, args: any): string {
const code = result.code || args.code || '';
const output = result.output || '';
let html = '<div class="code-block">';
html += '<div class="code-label">代码:</div>';
html += `<pre><code class="language-python">${escapeHtml(code)}</code></pre>`;
html += '</div>';
if (output) {
html += '<div class="output-block">';
html += '<div class="output-label">输出:</div>';
html += `<pre>${escapeHtml(output)}</pre>`;
html += '</div>';
}
return html;
}
// ===== =====
function renderUpdateMemory(result: any, args: any): string {
const operations = args.operations || [];
const added = operations.filter((op: any) => op.action === 'add' || op.action === 'create');
const updated = operations.filter((op: any) => op.action === 'update' || op.action === 'modify');
const deleted = operations.filter((op: any) => op.action === 'delete' || op.action === 'remove');
let html = '<div class="tool-result-meta">';
html += `<div><strong>操作:</strong>添加 ${added.length} 条 | 更新 ${updated.length} 条 | 删除 ${deleted.length} 条</div>`;
html += '</div>';
html += '<div class="tool-result-content scrollable">';
if (added.length > 0) {
html += '<div class="memory-section">';
added.forEach((op: any) => {
html += `<div class="memory-item memory-add">+ ${escapeHtml(op.content || op.text || '')}</div>`;
});
html += '</div>';
}
if (updated.length > 0) {
html += '<div class="memory-section">';
updated.forEach((op: any) => {
html += `<div class="memory-item memory-update">✏️ ${escapeHtml(op.content || op.text || '')}</div>`;
});
html += '</div>';
}
if (deleted.length > 0) {
html += '<div class="memory-section">';
deleted.forEach((op: any) => {
html += `<div class="memory-item memory-delete">- ${escapeHtml(op.content || op.text || '')}</div>`;
});
html += '</div>';
}
html += '</div>';
return html;
}
// ===== =====
function renderTodoCreate(result: any, args: any): string {
const status = result.success ? '✓ 已创建' : '✗ 失败';
const title = args.title || result.title || '';
const tasks = args.tasks || result.tasks || [];
let html = '<div class="tool-result-meta">';
html += `<div><strong>状态:</strong>${status}</div>`;
html += `<div><strong>标题:</strong>${escapeHtml(title)}</div>`;
html += '</div>';
if (tasks.length > 0) {
html += '<div class="tool-result-content">';
html += '<div class="todo-list">';
tasks.forEach((task: any) => {
const taskText = typeof task === 'string' ? task : (task.text || task.title || '');
html += `<div class="todo-item"><input type="checkbox" disabled /> ${escapeHtml(taskText)}</div>`;
});
html += '</div>';
html += '</div>';
}
return html;
}
function renderTodoUpdate(result: any, args: any): string {
const status = result.success ? '✓ 已更新' : '✗ 失败';
const title = args.title || result.title || '';
const tasks = result.tasks || args.tasks || [];
let html = '<div class="tool-result-meta">';
html += `<div><strong>状态:</strong>${status}</div>`;
html += `<div><strong>标题:</strong>${escapeHtml(title)}</div>`;
html += '</div>';
if (tasks.length > 0) {
html += '<div class="tool-result-content">';
html += '<div class="todo-list">';
tasks.forEach((task: any) => {
const taskText = typeof task === 'string' ? task : (task.text || task.title || '');
const completed = typeof task === 'object' && (task.completed || task.done || task.checked);
html += `<div class="todo-item"><input type="checkbox" ${completed ? 'checked' : ''} disabled /> ${escapeHtml(taskText)}</div>`;
});
html += '</div>';
html += '</div>';
}
return html;
}
// ===== =====
function renderEasterEgg(result: any, args: any): string {
const status = result.success ? '✓ 已触发' : '✗ 失败';
const type = result.type || args.type || '';
const content = result.content || result.message || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>状态:</strong>${status}</div>`;
html += `<div><strong>类型:</strong>${escapeHtml(type)}</div>`;
html += '</div>';
if (content) {
html += '<div class="tool-result-content">';
html += `<div class="easter-egg-content">${escapeHtml(content)}</div>`;
html += '</div>';
}
return html;
}
</script>
<style>
/* 工具增强显示样式 - 不使用 scoped 以便应用到 v-html 内容 */
.tool-result-meta {
margin-bottom: 12px;
padding: 8px 12px;
background: rgba(0, 0, 0, 0.02);
border-radius: 6px;
font-size: 13px;
line-height: 1.6;
}
.tool-result-meta > div {
margin: 4px 0;
}
.tool-result-content {
margin-top: 12px;
}
.tool-result-content.scrollable {
max-height: 400px;
overflow-y: auto;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 6px;
padding: 8px;
background: rgba(0, 0, 0, 0.01);
}
.tool-result-content pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
font-size: 12px;
line-height: 1.5;
}
.content-label {
font-weight: 600;
margin-bottom: 8px;
font-size: 13px;
}
.tool-result-empty {
color: rgba(0, 0, 0, 0.5);
font-style: italic;
padding: 8px;
}
/* 搜索结果 */
.search-result-list {
margin-top: 12px;
}
.search-result-item {
padding: 10px;
margin-bottom: 8px;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 6px;
background: rgba(0, 0, 0, 0.01);
}
/* 暗色模式下的搜索结果 */
:root[data-theme='dark'] .search-result-item {
background: #2a2a2a;
border: 1px solid rgba(255, 255, 255, 0.08);
}
.search-result-title {
font-weight: 600;
margin-bottom: 4px;
font-size: 14px;
}
.search-result-url {
font-size: 12px;
color: rgba(0, 0, 0, 0.6);
}
:root[data-theme='dark'] .search-result-url {
color: rgba(255, 255, 255, 0.6);
}
.search-result-url a {
color: #0066cc;
text-decoration: none;
}
:root[data-theme='dark'] .search-result-url a {
color: #6b9fff;
}
.search-result-url a:hover {
text-decoration: underline;
}
/* 文件差异 */
.tool-result-diff {
margin-top: 12px;
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
font-size: 12px;
line-height: 1.5;
}
.tool-result-diff.scrollable {
max-height: 400px;
overflow-y: auto;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 6px;
padding: 8px;
background: rgba(0, 0, 0, 0.01);
}
.diff-line {
padding: 2px 4px;
margin: 1px 0;
white-space: pre-wrap;
word-wrap: break-word;
}
.diff-remove {
background: rgba(255, 0, 0, 0.1);
color: #c00;
}
.diff-add {
background: rgba(0, 255, 0, 0.1);
color: #0a0;
}
.diff-separator {
text-align: center;
color: rgba(0, 0, 0, 0.3);
margin: 8px 0;
}
.diff-operation {
font-weight: 600;
margin: 8px 0 4px;
color: rgba(0, 0, 0, 0.7);
}
/* 图片 */
.tool-result-image {
margin-top: 12px;
text-align: center;
}
.tool-result-image img {
max-width: 100%;
height: auto;
border-radius: 6px;
border: 1px solid rgba(0, 0, 0, 0.1);
}
/* 代码块 */
.code-block {
margin-top: 12px;
}
.code-label,
.output-label {
font-weight: 600;
margin-bottom: 6px;
font-size: 13px;
}
.code-block pre,
.output-block pre {
margin: 0;
padding: 12px;
background: rgba(0, 0, 0, 0.03);
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 6px;
overflow-x: auto;
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
font-size: 12px;
line-height: 1.5;
white-space: pre-wrap;
word-wrap: break-word;
}
.output-block {
margin-top: 12px;
}
/* 记忆 */
.memory-section {
margin-bottom: 12px;
}
.memory-item {
padding: 6px 8px;
margin: 4px 0;
border-radius: 4px;
font-size: 13px;
line-height: 1.5;
}
.memory-add {
background: rgba(0, 255, 0, 0.1);
color: #0a0;
}
.memory-update {
background: rgba(255, 165, 0, 0.1);
color: #f90;
}
.memory-delete {
background: rgba(255, 0, 0, 0.1);
color: #c00;
}
/* 待办事项 */
.todo-list {
padding: 8px;
}
.todo-item {
padding: 6px 0;
font-size: 13px;
line-height: 1.5;
display: flex;
align-items: center;
gap: 8px;
}
.todo-item input[type="checkbox"] {
margin: 0;
}
/* 彩蛋 */
.easter-egg-content {
padding: 12px;
background: linear-gradient(135deg, rgba(255, 215, 0, 0.1), rgba(255, 105, 180, 0.1));
border: 1px solid rgba(255, 215, 0, 0.3);
border-radius: 8px;
font-size: 14px;
line-height: 1.6;
}
</style>

View File

@ -0,0 +1,649 @@
// 工具增强显示的渲染函数
export function escapeHtml(text: string): string {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
export function formatBytes(bytes: number): string {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
}
export function renderEnhancedToolResult(
action: any,
formatSearchTopic: (filters: Record<string, any>) => string,
formatSearchTime: (filters: Record<string, any>) => string,
formatSearchDomains: (filters: Record<string, any>) => string
): string {
const tool = action.tool;
const result = tool?.result;
const args = tool?.arguments || {};
const name = tool?.name;
if (!result) {
return '';
}
// 网络检索类
if (name === 'web_search') {
return renderWebSearch(result, args, formatSearchTopic, formatSearchTime, formatSearchDomains);
} else if (name === 'extract_webpage') {
return renderExtractWebpage(result, args);
} else if (name === 'save_webpage') {
return renderSaveWebpage(result, args);
}
// 文件编辑类
else if (name === 'create_file' || name === 'create_folder') {
return renderCreateFile(result, args);
} else if (name === 'write_file') {
return renderWriteFile(result, args);
} else if (name === 'edit_file') {
return renderEditFile(result, args);
} else if (name === 'append_to_file') {
return renderAppendToFile(result, args);
} else if (name === 'modify_file') {
return renderModifyFile(result, args);
} else if (name === 'delete_file') {
return renderDeleteFile(result, args);
} else if (name === 'rename_file') {
return renderRenameFile(result, args);
}
// 阅读聚焦类
else if (name === 'read_file') {
return renderReadFile(result, args);
} else if (name === 'vlm_analyze') {
return renderVlmAnalyze(result, args);
} else if (name === 'ocr_image') {
return renderOcrImage(result, args);
} else if (name === 'view_image') {
return renderViewImage(result, args);
}
// 终端类
else if (name === 'terminal_session') {
return renderTerminalSession(result, args);
} else if (name === 'terminal_input') {
return renderTerminalInput(result, args);
} else if (name === 'terminal_snapshot') {
return renderTerminalSnapshot(result, args);
} else if (name === 'sleep') {
return renderSleep(result, args);
}
// 终端指令类
else if (name === 'run_command') {
return renderRunCommand(result, args);
} else if (name === 'run_python') {
return renderRunPython(result, args);
}
// 记忆类
else if (name === 'update_memory') {
return renderUpdateMemory(result, args);
}
// 待办事项类
else if (name === 'todo_create') {
return renderTodoCreate(result, args);
} else if (name === 'todo_update_task') {
return renderTodoUpdate(result, args);
}
// 彩蛋类
else if (name === 'trigger_easter_egg') {
return renderEasterEgg(result, args);
}
return '';
}
// 网络检索类渲染函数
function renderWebSearch(
result: any,
args: any,
formatSearchTopic: (filters: Record<string, any>) => string,
formatSearchTime: (filters: Record<string, any>) => string,
formatSearchDomains: (filters: Record<string, any>) => string
): string {
const query = result.query || args.query || '';
const filters = result.filters || {};
const totalResults = result.total_results || 0;
const results = result.results || [];
let html = '<div class="tool-result-meta">';
html += `<div><strong>搜索内容:</strong>${escapeHtml(query)}</div>`;
html += `<div><strong>主题:</strong>${escapeHtml(formatSearchTopic(filters))}</div>`;
html += `<div><strong>时间范围:</strong>${escapeHtml(formatSearchTime(filters))}</div>`;
html += `<div><strong>限定网站:</strong>${escapeHtml(formatSearchDomains(filters))}</div>`;
html += `<div><strong>结果数量:</strong>${totalResults}</div>`;
html += '</div>';
if (results.length > 0) {
html += '<div class="search-result-list">';
results.forEach((item: any) => {
html += '<div class="search-result-item">';
html += `<div class="search-result-title">${escapeHtml(item.title || '无标题')}</div>`;
html += '<div class="search-result-url">';
if (item.url) {
html += `<a href="${escapeHtml(item.url)}" target="_blank">${escapeHtml(item.url)}</a>`;
} else {
html += '<span>无可用链接</span>';
}
html += '</div></div>';
});
html += '</div>';
} else {
html += '<div class="tool-result-empty">未返回详细的搜索结果。</div>';
}
return html;
}
function renderExtractWebpage(result: any, args: any): string {
const url = args.url || result.url || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
const content = result.content || result.text || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>URL</strong>${escapeHtml(url)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
if (content) {
html += '<div class="tool-result-content scrollable">';
html += `<pre>${escapeHtml(content)}</pre>`;
html += '</div>';
}
return html;
}
function renderSaveWebpage(result: any, args: any): string {
const url = args.url || result.url || '';
const path = result.path || args.save_path || '';
const status = result.success ? '✓ 已保存' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>URL</strong>${escapeHtml(url)}</div>`;
html += `<div><strong>保存路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
return html;
}
// 文件编辑类渲染函数
function renderCreateFile(result: any, args: any): string {
const path = args.path || result.path || '';
const status = result.success ? '✓ 已创建' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
return html;
}
function renderWriteFile(result: any, args: any): string {
const path = args.path || result.path || '';
const status = result.success ? '✓ 已写入' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
return html;
}
function renderEditFile(result: any, args: any): string {
const path = args.file_path || args.path || result.path || '';
const status = result.success ? '✓ 已编辑' : '✗ 失败';
let oldString = args.old_string || '';
let newString = args.new_string || '';
// 处理转义的 \n
oldString = oldString.replace(/\\n/g, '\n');
newString = newString.replace(/\\n/g, '\n');
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
if (oldString || newString) {
html += '<div class="tool-result-diff scrollable">';
if (oldString) {
const oldLines = oldString.split('\n');
oldLines.forEach((line: string) => {
html += `<div class="diff-line diff-remove">- ${escapeHtml(line)}</div>`;
});
}
if (newString) {
const newLines = newString.split('\n');
newLines.forEach((line: string) => {
html += `<div class="diff-line diff-add">+ ${escapeHtml(line)}</div>`;
});
}
html += '</div>';
}
return html;
}
function renderAppendToFile(result: any, args: any): string {
const path = args.file_path || args.path || result.path || '';
const status = result.success ? '✓ 已追加' : '✗ 失败';
let content = args.content || '';
// 处理转义的 \n
content = content.replace(/\\n/g, '\n');
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
if (content) {
html += '<div class="tool-result-content scrollable">';
html += '<div class="content-label">追加内容:</div>';
const lines = content.split('\n');
html += '<div class="tool-result-diff">';
lines.forEach((line: string) => {
html += `<div class="diff-line diff-add">+ ${escapeHtml(line)}</div>`;
});
html += '</div>';
html += '</div>';
}
return html;
}
function renderModifyFile(result: any, args: any): string {
const path = args.file_path || args.path || result.path || '';
const status = result.success ? '✓ 已修改' : '✗ 失败';
const operations = args.operations || [];
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
if (operations.length > 0) {
html += '<div class="tool-result-diff scrollable">';
operations.forEach((op: any, idx: number) => {
if (idx > 0) html += '<div class="diff-separator">⋮</div>';
const type = op.type || op.operation;
let oldText = op.old_text || op.old || '';
let newText = op.new_text || op.new || '';
// 处理转义的 \n
oldText = oldText.replace(/\\n/g, '\n');
newText = newText.replace(/\\n/g, '\n');
html += `<div class="diff-operation">[${idx + 1}] ${escapeHtml(type)}</div>`;
if (oldText) {
const oldLines = oldText.split('\n');
oldLines.forEach((line: string) => {
html += `<div class="diff-line diff-remove">- ${escapeHtml(line)}</div>`;
});
}
if (newText) {
const newLines = newText.split('\n');
newLines.forEach((line: string) => {
html += `<div class="diff-line diff-add">+ ${escapeHtml(line)}</div>`;
});
}
});
html += '</div>';
}
return html;
}
function renderDeleteFile(result: any, args: any): string {
const path = args.path || result.path || '';
const status = result.success ? '✓ 已删除' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
return html;
}
function renderRenameFile(result: any, args: any): string {
const oldPath = args.old_path || args.source || result.old_path || '';
const newPath = args.new_path || args.destination || result.new_path || '';
const status = result.success ? '✓ 已重命名' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(oldPath)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += `<div><strong>重命名:</strong>${escapeHtml(oldPath)}${escapeHtml(newPath)}</div>`;
html += '</div>';
return html;
}
// 阅读聚焦类渲染函数
function renderReadFile(result: any, args: any): string {
const path = args.path || result.path || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
const size = result.size || result.file_size || 0;
const content = result.content || result.text || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
if (size > 0) {
html += `<div><strong>大小:</strong>${formatBytes(size)}</div>`;
}
html += '</div>';
if (content) {
html += '<div class="tool-result-content scrollable">';
html += `<pre>${escapeHtml(content)}</pre>`;
html += '</div>';
}
return html;
}
function renderVlmAnalyze(result: any, args: any): string {
const path = args.image_path || args.path || result.path || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
const analysis = result.analysis || result.result || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
if (analysis) {
html += '<div class="tool-result-content scrollable">';
html += '<div class="content-label">分析结果:</div>';
html += `<pre>${escapeHtml(analysis)}</pre>`;
html += '</div>';
}
return html;
}
function renderOcrImage(result: any, args: any): string {
const path = args.image_path || args.path || result.path || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
const size = result.size || result.file_size || 0;
const text = result.text || result.ocr_text || '';
const imageUrl = result.image_url || result.url || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
if (size > 0) {
html += `<div><strong>大小:</strong>${formatBytes(size)}</div>`;
}
html += '</div>';
if (imageUrl) {
html += `<div class="tool-result-image"><img src="${escapeHtml(imageUrl)}" alt="OCR图片" style="max-width: 100%; height: auto;" /></div>`;
}
if (text) {
html += '<div class="tool-result-content scrollable">';
html += '<div class="content-label">识别文本:</div>';
html += `<pre>${escapeHtml(text)}</pre>`;
html += '</div>';
}
return html;
}
function renderViewImage(result: any, args: any): string {
const path = args.image_path || args.path || result.path || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
const size = result.size || result.file_size || 0;
const imageUrl = result.image_url || result.url || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>路径:</strong>${escapeHtml(path)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
if (size > 0) {
html += `<div><strong>大小:</strong>${formatBytes(size)}</div>`;
}
html += '</div>';
if (imageUrl) {
html += `<div class="tool-result-image"><img src="${escapeHtml(imageUrl)}" alt="查看图片" style="max-width: 100%; height: auto;" /></div>`;
}
return html;
}
// 终端类渲染函数
function renderTerminalSession(result: any, args: any): string {
const operation = args.operation || args.action || 'start';
const sessionName = args.session_name || args.name || result.session_name || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>操作:</strong>${escapeHtml(operation)}</div>`;
html += `<div><strong>终端名:</strong>${escapeHtml(sessionName)}</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
return html;
}
function renderTerminalInput(result: any, args: any): string {
const command = args.command || args.input || '';
const timeout = args.timeout || 30;
const output = result.output || result.result || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>指令:</strong>${escapeHtml(command)}</div>`;
html += `<div><strong>超时时间:</strong>${timeout}秒</div>`;
html += '</div>';
if (output) {
html += '<div class="tool-result-content scrollable">';
html += '<div class="content-label">输出:</div>';
html += `<pre>${escapeHtml(output)}</pre>`;
html += '</div>';
}
return html;
}
function renderTerminalSnapshot(result: any, args: any): string {
const sessionName = args.session_name || args.name || result.session_name || '';
const status = result.success ? '✓ 成功' : '✗ 失败';
const startLine = args.start_line || 0;
const endLine = args.end_line || 0;
const content = result.content || result.output || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>状态:</strong>${status}</div>`;
html += `<div><strong>终端名:</strong>${escapeHtml(sessionName)}</div>`;
if (startLine || endLine) {
html += `<div><strong>行范围:</strong>${startLine} - ${endLine}</div>`;
}
html += '</div>';
if (content) {
html += '<div class="tool-result-content scrollable">';
html += '<div class="content-label">获取的内容:</div>';
html += `<pre>${escapeHtml(content)}</pre>`;
html += '</div>';
}
return html;
}
function renderSleep(result: any, args: any): string {
const seconds = args.seconds || args.duration || 0;
const status = result.success ? '✓ 完成' : '✗ 失败';
let html = '<div class="tool-result-meta">';
html += `<div><strong>等待时间:</strong>${seconds}秒</div>`;
html += `<div><strong>状态:</strong>${status}</div>`;
html += '</div>';
return html;
}
// 终端指令类渲染函数
function renderRunCommand(result: any, args: any): string {
const command = args.command || '';
const timeout = args.timeout || 30;
const output = result.output || result.stdout || '';
const exitCode = result.exit_code !== undefined ? result.exit_code : result.returncode;
let html = '<div class="tool-result-meta">';
html += `<div><strong>指令:</strong>${escapeHtml(command)}</div>`;
html += `<div><strong>超时时间:</strong>${timeout}秒</div>`;
if (exitCode !== undefined) {
html += `<div><strong>退出码:</strong>${exitCode}</div>`;
}
html += '</div>';
if (output) {
html += '<div class="tool-result-content scrollable">';
html += '<div class="content-label">输出:</div>';
html += `<pre>${escapeHtml(output)}</pre>`;
html += '</div>';
}
return html;
}
function renderRunPython(result: any, args: any): string {
const code = result.code || args.code || '';
const output = result.output || '';
let html = '<div class="code-block">';
html += '<div class="code-label">代码:</div>';
html += `<pre><code class="language-python">${escapeHtml(code)}</code></pre>`;
html += '</div>';
if (output) {
html += '<div class="output-block">';
html += '<div class="output-label">输出:</div>';
html += `<pre>${escapeHtml(output)}</pre>`;
html += '</div>';
}
return html;
}
// 记忆类渲染函数
function renderUpdateMemory(result: any, args: any): string {
const operation = args.operation || result.operation || '';
const content = args.content || '';
const index = args.index || result.index;
const count = result.count || 0;
let html = '<div class="tool-result-meta">';
if (operation === 'append') {
html += `<div>新增记忆</div>`;
html += `<div>${count}. ${escapeHtml(content)}</div>`;
} else if (operation === 'replace') {
html += `<div>更新记忆</div>`;
html += `<div>${index}. ${escapeHtml(content)}</div>`;
} else if (operation === 'delete') {
html += `<div>删除记忆</div>`;
html += `<div>${index}. ${escapeHtml(content || '已删除')}</div>`;
}
html += '</div>';
return html;
}
// 待办事项类渲染函数
function renderTodoCreate(result: any, args: any): string {
const status = result.success ? '✓ 已创建' : '✗ 失败';
const todoList = result.todo_list || {};
const title = todoList.title || args.title || '';
const tasks = todoList.tasks || args.tasks || [];
let html = '<div class="tool-result-meta">';
html += `<div><strong>状态:</strong>${status}</div>`;
html += `<div><strong>标题:</strong>${escapeHtml(title)}</div>`;
html += '</div>';
if (tasks.length > 0) {
html += '<div class="tool-result-content">';
html += '<div class="todo-list">';
tasks.forEach((task: any) => {
const taskText = typeof task === 'string' ? task : (task.text || task.title || task.content || '');
html += `<div class="todo-item"><input type="checkbox" disabled /> ${escapeHtml(taskText)}</div>`;
});
html += '</div>';
html += '</div>';
}
return html;
}
function renderTodoUpdate(result: any, args: any): string {
const status = result.success ? '✓ 已更新' : '✗ 失败';
const todoList = result.todo_list || {};
const title = todoList.title || '';
const tasks = todoList.tasks || [];
let html = '<div class="tool-result-meta">';
html += `<div><strong>状态:</strong>${status}</div>`;
html += `<div><strong>标题:</strong>${escapeHtml(title)}</div>`;
html += '</div>';
if (tasks.length > 0) {
html += '<div class="tool-result-content">';
html += '<div class="todo-list">';
tasks.forEach((task: any) => {
const taskText = typeof task === 'string' ? task : (task.text || task.title || task.content || '');
const completed = typeof task === 'object' && (task.status === 'done' || task.completed || task.done || task.checked);
html += `<div class="todo-item"><input type="checkbox" ${completed ? 'checked' : ''} disabled /> ${escapeHtml(taskText)}</div>`;
});
html += '</div>';
html += '</div>';
}
return html;
}
// 彩蛋类渲染函数
function renderEasterEgg(result: any, args: any): string {
const status = result.success ? '✓ 已触发' : '✗ 失败';
const type = result.type || args.type || '';
const content = result.content || result.message || '';
let html = '<div class="tool-result-meta">';
html += `<div><strong>状态:</strong>${status}</div>`;
html += `<div><strong>类型:</strong>${escapeHtml(type)}</div>`;
html += '</div>';
if (content) {
html += '<div class="tool-result-content">';
html += `<div class="easter-egg-content">${escapeHtml(content)}</div>`;
html += '</div>';
}
return html;
}

View File

@ -97,14 +97,6 @@
>
实时终端
</button>
<button
type="button"
class="menu-entry submenu-entry"
@click="$emit('toggle-focus-panel')"
:disabled="!isConnected"
>
聚焦面板
</button>
<button
type="button"
class="menu-entry submenu-entry"
@ -167,7 +159,6 @@ defineEmits<{
(event: 'toggle-settings'): void;
(event: 'update-tool-category', id: string, enabled: boolean): void;
(event: 'realtime-terminal'): void;
(event: 'toggle-focus-panel'): void;
(event: 'toggle-token-panel'): void;
(event: 'compress-conversation'): void;
(event: 'toggle-mode-menu'): void;

View File

@ -333,10 +333,33 @@
</section>
<section v-else-if="activeTab === 'behavior'" key="behavior" class="personal-page behavior-page">
<div class="behavior-section">
<div class="behavior-field">
<div class="behavior-field-header">
<span class="field-title">增强工具显示</span>
<p class="field-desc">开启后工具块将显示格式化的内容关闭则显示原始 JSON 数据默认开启</p>
</div>
<label class="toggle-row">
<input
type="checkbox"
:checked="form.enhanced_tool_display"
@change="personalization.updateField({ key: 'enhanced_tool_display', value: $event.target.checked })"
/>
<span class="fancy-check" aria-hidden="true">
<svg viewBox="0 0 64 64">
<path
d="M 0 16 V 56 A 8 8 90 0 0 8 64 H 56 A 8 8 90 0 0 64 56 V 8 A 8 8 90 0 0 56 0 H 8 A 8 8 90 0 0 0 8 V 16 L 32 48 L 64 16 V 8 A 8 8 90 0 0 56 0 H 8 A 8 8 90 0 0 0 8 V 56 A 8 8 90 0 0 8 64 H 56 A 8 8 90 0 0 64 56 V 16"
pathLength="575.0541381835938"
class="fancy-path"
></path>
</svg>
</span>
<span>显示格式化的工具结果更清晰易读</span>
</label>
</div>
<div class="behavior-field">
<div class="behavior-field-header">
<span class="field-title">堆叠块显示</span>
<p class="field-desc">使用新版堆叠动画展示思考/工具块超过 6 条自动收纳为更多默认开启</p>
<p class="field-desc">使用新版堆叠动画展示思考/工具块超过 6 条自动收纳为"更多"默认开启</p>
</div>
<label class="toggle-row">
<input
@ -902,15 +925,15 @@ const themeOptions: Array<{ id: ThemeKey; label: string; desc: string; swatches:
},
{
id: 'light',
label: '浅灰 · 明亮',
desc: '浅灰 + 白,清晰对比适合日间工作',
swatches: ['#f4f5f7', '#ffffff', '#4f8bff']
label: '明亮',
desc: '纯白底色 + 优雅灰,类似 ChatGPT 风格',
swatches: ['#ffffff', '#f7f7f8', '#6b7280']
},
{
id: 'dark',
label: '深灰 · 夜间',
label: '夜间',
desc: '深灰 + 黑,低亮度并保持彩色点缀',
swatches: ['#0f1115', '#1d2230', '#5ad1c9']
swatches: ['#1a1a1a', '#2a2a2a', '#3a3a3a']
}
];

View File

@ -7,6 +7,7 @@ interface PersonalForm {
auto_generate_title: boolean;
tool_intent_enabled: boolean;
silent_tool_disable: boolean;
enhanced_tool_display: boolean;
enabled_skills: string[];
self_identify: string;
user_name: string;
@ -62,6 +63,7 @@ const defaultForm = (): PersonalForm => ({
auto_generate_title: true,
tool_intent_enabled: true,
silent_tool_disable: false,
enhanced_tool_display: true,
enabled_skills: [],
self_identify: '',
user_name: '',
@ -193,6 +195,7 @@ export const usePersonalizationStore = defineStore('personalization', {
auto_generate_title: data.auto_generate_title !== false,
tool_intent_enabled: !!data.tool_intent_enabled,
silent_tool_disable: !!data.silent_tool_disable,
enhanced_tool_display: data.enhanced_tool_display !== false,
enabled_skills: Array.isArray(data.enabled_skills)
? data.enabled_skills.filter((item: unknown) => typeof item === 'string')
: [],

View File

@ -89,84 +89,86 @@
:root[data-theme='light'] {
color-scheme: light;
--claude-bg: #f5f5f4;
--claude-panel: rgba(255, 255, 255, 0.97);
--claude-left-rail: #faf9f7;
--claude-sidebar: rgba(255, 255, 255, 0.94);
--claude-border: rgba(26, 27, 30, 0.12);
--claude-border-strong: rgba(26, 27, 30, 0.2);
--claude-text: #1b1c1f;
--claude-text-secondary: #3c3f46;
--claude-text-tertiary: #5a5f69;
--claude-muted: rgba(27, 28, 31, 0.28);
--claude-accent: #4a4f58;
--claude-accent-strong: #2f343d;
--claude-deep: #6b6f78;
--claude-deep-strong: #4d515a;
--claude-highlight: rgba(74, 79, 88, 0.12);
--claude-button-hover: #3d424b;
--claude-button-active: #2f343c;
--claude-shadow: 0 16px 40px rgba(27, 28, 31, 0.12);
--claude-success: #3f6f4f;
--claude-warning: #7a6a3a;
/* 类似 ChatGPT 的明亮配色:白底黑字,优雅浅灰 */
--claude-bg: #ffffff;
--claude-panel: #ffffff;
--claude-left-rail: #f9f9f9;
--claude-sidebar: #ffffff;
--claude-border: rgba(0, 0, 0, 0.08);
--claude-border-strong: rgba(0, 0, 0, 0.12);
--claude-text: #0d0d0d;
--claude-text-secondary: #676767;
--claude-text-tertiary: #8e8e8e;
--claude-muted: rgba(0, 0, 0, 0.3);
--claude-accent: #acacac;
--claude-accent-strong: #8e8e8e;
--claude-deep: #c4c4c4;
--claude-deep-strong: #acacac;
--claude-highlight: rgba(0, 0, 0, 0.05);
--claude-button-hover: #d1d1d1;
--claude-button-active: #acacac;
--claude-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
--claude-success: #10b981;
--claude-warning: #f59e0b;
--theme-surface-card: #ffffff;
--theme-surface-strong: #ffffff;
--theme-surface-soft: rgba(255, 255, 255, 0.98);
--theme-surface-muted: rgba(255, 255, 255, 0.94);
--theme-overlay-scrim: rgba(17, 16, 14, 0.45);
--theme-shadow-strong: 0 32px 72px rgba(17, 16, 14, 0.18);
--theme-shadow-soft: 0 18px 42px rgba(17, 16, 14, 0.12);
--theme-shadow-mid: 0 20px 45px rgba(17, 16, 14, 0.1);
--theme-control-border: rgba(26, 27, 30, 0.12);
--theme-control-border-strong: rgba(26, 27, 30, 0.2);
--theme-switch-track: #dededd;
--theme-chip-bg: rgba(74, 79, 88, 0.08);
--theme-chip-border: rgba(74, 79, 88, 0.18);
--theme-badge-bg: rgba(74, 79, 88, 0.14);
--theme-tab-active: rgba(74, 79, 88, 0.12);
--theme-mobile-menu: rgba(255, 255, 255, 0.88);
--theme-mobile-menu-shadow: 0 14px 32px rgba(17, 16, 14, 0.16);
--theme-card-border-strong: rgba(26, 27, 30, 0.18);
--theme-surface-soft: #ffffff;
--theme-surface-muted: #fafafa;
--theme-overlay-scrim: rgba(0, 0, 0, 0.5);
--theme-shadow-strong: 0 2px 4px rgba(0, 0, 0, 0.08);
--theme-shadow-soft: 0 1px 2px rgba(0, 0, 0, 0.05);
--theme-shadow-mid: 0 1px 3px rgba(0, 0, 0, 0.06);
--theme-control-border: rgba(0, 0, 0, 0.08);
--theme-control-border-strong: rgba(0, 0, 0, 0.12);
--theme-switch-track: #ececec;
--theme-chip-bg: #f5f5f5;
--theme-chip-border: rgba(0, 0, 0, 0.08);
--theme-badge-bg: #f5f5f5;
--theme-tab-active: rgba(0, 0, 0, 0.05);
--theme-mobile-menu: rgba(255, 255, 255, 0.95);
--theme-mobile-menu-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
--theme-card-border-strong: rgba(0, 0, 0, 0.1);
}
:root[data-theme='dark'] {
color-scheme: dark;
--claude-bg: #08090d;
--claude-panel: rgba(16, 17, 23, 0.92);
--claude-left-rail: #0b0c11;
--claude-sidebar: rgba(13, 14, 19, 0.94);
/* 暗色主题:灰黑色底色,白色文字 */
--claude-bg: #1a1a1a;
--claude-panel: #1a1a1a;
--claude-left-rail: #0a0a0a;
--claude-sidebar: #0a0a0a;
--claude-border: rgba(255, 255, 255, 0.08);
--claude-border-strong: rgba(255, 255, 255, 0.14);
--claude-text: #f8f9fc;
--claude-text-secondary: #d7dbe8;
--claude-text-tertiary: #aeb6c7;
--claude-muted: rgba(248, 249, 252, 0.22);
--claude-accent: #62e0d2;
--claude-accent-strong: #3bb8aa;
--claude-deep: #8cc7ff;
--claude-deep-strong: #5b9fe6;
--claude-highlight: rgba(98, 224, 210, 0.2);
--claude-button-hover: #4ac8ba;
--claude-button-active: #37aa9e;
--claude-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
--claude-success: #82e8b3;
--claude-warning: #f0c76a;
--theme-surface-card: #0f1117;
--theme-surface-strong: #141823;
--theme-surface-soft: rgba(20, 24, 35, 0.95);
--theme-surface-muted: rgba(16, 19, 28, 0.92);
--theme-overlay-scrim: rgba(0, 0, 0, 0.72);
--theme-shadow-strong: 0 32px 80px rgba(0, 0, 0, 0.7);
--theme-shadow-soft: 0 18px 48px rgba(0, 0, 0, 0.55);
--theme-shadow-mid: 0 22px 50px rgba(0, 0, 0, 0.5);
--theme-control-border: rgba(255, 255, 255, 0.1);
--theme-control-border-strong: rgba(255, 255, 255, 0.16);
--theme-switch-track: #262b34;
--theme-chip-bg: rgba(98, 224, 210, 0.14);
--theme-chip-border: rgba(98, 224, 210, 0.26);
--theme-badge-bg: rgba(98, 224, 210, 0.18);
--theme-tab-active: rgba(98, 224, 210, 0.16);
--theme-mobile-menu: rgba(16, 19, 28, 0.78);
--theme-mobile-menu-shadow: 0 18px 40px rgba(0, 0, 0, 0.62);
--theme-card-border-strong: rgba(255, 255, 255, 0.16);
--claude-border-strong: rgba(255, 255, 255, 0.12);
--claude-text: #ffffff;
--claude-text-secondary: #a0a0a0;
--claude-text-tertiary: #707070;
--claude-muted: rgba(255, 255, 255, 0.3);
--claude-accent: #606060;
--claude-accent-strong: #505050;
--claude-deep: #808080;
--claude-deep-strong: #606060;
--claude-highlight: rgba(255, 255, 255, 0.05);
--claude-button-hover: #707070;
--claude-button-active: #505050;
--claude-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
--claude-success: #10b981;
--claude-warning: #f59e0b;
--theme-surface-card: #0a0a0a;
--theme-surface-strong: #1a1a1a;
--theme-surface-soft: #0f0f0f;
--theme-surface-muted: #141414;
--theme-overlay-scrim: rgba(0, 0, 0, 0.8);
--theme-shadow-strong: 0 4px 12px rgba(0, 0, 0, 0.6);
--theme-shadow-soft: 0 2px 6px rgba(0, 0, 0, 0.4);
--theme-shadow-mid: 0 3px 8px rgba(0, 0, 0, 0.5);
--theme-control-border: rgba(255, 255, 255, 0.08);
--theme-control-border-strong: rgba(255, 255, 255, 0.12);
--theme-switch-track: #2a2a2a;
--theme-chip-bg: #2a2a2a;
--theme-chip-border: rgba(255, 255, 255, 0.08);
--theme-badge-bg: #2a2a2a;
--theme-tab-active: rgba(255, 255, 255, 0.05);
--theme-mobile-menu: rgba(10, 10, 10, 0.95);
--theme-mobile-menu-shadow: 0 4px 12px rgba(0, 0, 0, 0.6);
--theme-card-border-strong: rgba(255, 255, 255, 0.1);
}

View File

@ -137,6 +137,14 @@
.chat-container--mobile {
padding-top: 44px;
}
.messages-area {
padding-top: 8px;
}
.message-block:first-child {
margin-top: 0;
}
}
.conversation-ribbon__selector {
@ -320,8 +328,8 @@
}
.scroll-lock-toggle.locked .scroll-lock-btn {
border-color: rgba(218, 119, 86, 0.32);
box-shadow: 0 0 10px rgba(218, 119, 86, 0.28);
border-color: var(--claude-accent);
box-shadow: 0 0 10px var(--claude-highlight);
}
.scroll-lock-btn svg {
@ -371,7 +379,7 @@
}
.assistant-message .message-text {
background: rgba(218, 119, 86, 0.12);
background: var(--claude-highlight);
border-left: 4px solid var(--claude-accent);
}
@ -457,24 +465,27 @@
}
.collapsible-header {
padding: 14px 20px;
padding: 9px 13px;
cursor: pointer;
display: flex;
align-items: center;
gap: 12px;
gap: 8px;
user-select: none;
background: rgba(255, 255, 255, 0.72);
transition: background-color 0.2s ease;
position: relative;
min-height: 36px;
max-height: 36px;
overflow: hidden;
}
.collapsible-header:hover {
background: rgba(218, 119, 86, 0.07);
background: var(--claude-highlight);
}
.arrow {
width: 20px;
height: 20px;
width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
@ -523,8 +534,8 @@
}
.content-inner {
padding: 20px 20px 20px 56px;
font-size: 14px;
padding: 13px 13px 13px 37px;
font-size: 13px;
line-height: 1.6;
color: var(--claude-text-secondary);
scrollbar-width: none;
@ -627,6 +638,10 @@
object-fit: contain;
}
body[data-theme='dark'] .more-icon {
filter: brightness(0) invert(1);
}
.more-copy {
display: flex;
flex-direction: column;
@ -717,6 +732,8 @@
font-size: 14px;
color: var(--claude-text);
font-weight: 500;
white-space: nowrap;
flex-shrink: 0;
}
.collapsible-block.processing .status-text {
@ -727,6 +744,11 @@
color: var(--claude-text-secondary);
font-size: 12px;
margin-left: 8px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex-shrink: 1;
min-width: 0;
}
@keyframes slideInFade {
@ -780,10 +802,10 @@
}
.chat-inline-image--error {
border: 1px dashed rgba(218, 119, 86, 0.6);
border: 1px dashed var(--claude-accent);
padding: 6px 10px;
border-radius: 12px;
background: rgba(218, 119, 86, 0.05);
background: var(--claude-highlight);
}
.chat-inline-image__error {
@ -827,19 +849,19 @@
width: 100%;
border-collapse: collapse;
margin: 16px 0;
background: rgba(255, 255, 255, 0.92);
background: var(--theme-surface-soft);
border-radius: 12px;
overflow: hidden;
box-shadow: 0 8px 20px rgba(61, 57, 41, 0.06);
box-shadow: var(--theme-shadow-soft);
}
.text-output .text-content thead {
background: rgba(218, 119, 86, 0.1);
background: var(--theme-chip-bg);
}
.text-output .text-content th,
.text-output .text-content td {
border: 1px solid rgba(118, 103, 84, 0.18);
border: 1px solid var(--claude-border);
padding: 10px 14px;
text-align: left;
vertical-align: middle;
@ -851,6 +873,12 @@
color: var(--claude-text);
}
.text-output .text-content hr {
margin: 32px 0;
border: none;
border-top: 1px solid var(--claude-border);
}
.system-action {
margin: 12px 0;
padding: 10px 14px;
@ -873,9 +901,9 @@
margin: 12px 0;
padding: 12px 16px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.82);
border-left: 4px solid rgba(218, 119, 86, 0.32);
box-shadow: inset 0 0 0 1px rgba(218, 119, 86, 0.05);
background: var(--theme-surface-soft);
border-left: 4px solid var(--claude-accent);
box-shadow: inset 0 0 0 1px var(--claude-border);
display: flex;
flex-direction: column;
gap: 8px;
@ -896,21 +924,21 @@
}
.code-block-wrapper {
border: 2px solid rgba(118, 103, 84, 0.25);
border: 2px solid var(--claude-border);
border-radius: 12px;
overflow: hidden;
margin: 16px 0;
background: rgba(255, 255, 255, 0.78);
background: var(--theme-surface-soft);
min-height: 80px;
}
.code-block-header {
background: rgba(218, 119, 86, 0.08);
background: var(--theme-chip-bg);
padding: 10px 16px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(118, 103, 84, 0.25);
border-bottom: 1px solid var(--claude-border);
}
.code-language {
@ -932,7 +960,7 @@
background-position: center;
background-size: 20px;
cursor: pointer;
transition: opacity 0.2s ease;
transition: all 0.2s ease;
}
.copy-code-btn:hover {
@ -940,7 +968,7 @@
}
.copy-code-btn.copied {
opacity: 0.35;
background-image: url('/static/icons/check.svg');
}
.code-block-wrapper pre {

View File

@ -52,18 +52,18 @@
.stadium-shell.is-focused,
.stadium-shell.has-text {
border-color: rgba(218, 119, 86, 0.32);
border-color: var(--claude-accent);
box-shadow:
0 2px 22px rgba(218, 119, 86, 0.18),
0 0 30px rgba(218, 119, 86, 0.16),
0 2px 22px var(--claude-highlight),
0 0 30px var(--claude-highlight),
0 22px 60px rgba(15, 23, 42, 0.22);
}
.stadium-shell.is-multiline.is-focused,
.stadium-shell.is-multiline.has-text {
box-shadow:
0 2px 28px rgba(218, 119, 86, 0.2),
0 0 36px rgba(218, 119, 86, 0.2),
0 2px 28px var(--claude-highlight),
0 0 36px var(--claude-highlight),
0 32px 86px rgba(15, 23, 42, 0.28);
}

View File

@ -1166,6 +1166,7 @@
.run-mode-title {
font-weight: 600;
font-size: 15px;
color: var(--claude-text);
}
.run-mode-badge {
@ -2392,3 +2393,844 @@
transform: translate(100%, 100%);
}
}
/* 明亮主题:移除光晕效果 */
body[data-theme='light'] {
.experiment-visual-glow,
.experiment-orb {
display: none;
}
.experiment-visual {
background: var(--theme-surface-card);
box-shadow: var(--theme-shadow-soft);
}
.stadium-shell.is-focused,
.stadium-shell.has-text,
.stadium-shell.is-multiline.is-focused,
.stadium-shell.is-multiline.has-text {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* 按钮背景色 - 使用浅灰色 */
.stadium-btn.send-btn {
background: #ececec;
color: #0d0d0d;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.stadium-btn.send-btn:hover:not(:disabled) {
background: #e0e0e0;
}
.stadium-btn.send-btn .send-icon {
border-left-color: #0d0d0d;
}
.stadium-btn.send-btn .stop-icon {
background-color: #0d0d0d;
}
.menu-entry:hover:not(:disabled) {
background: #f5f5f5;
}
.menu-entry.active {
background: #ececec;
}
.personal-page-btn {
background: #f5f5f5;
}
.conversation-personal-entry:not(.collapsed) .personal-page-btn {
background: #f5f5f5;
}
.conversation-personal-entry:not(.collapsed) .personal-page-btn:hover,
.conversation-personal-entry:not(.collapsed) .personal-page-btn:focus-visible {
background: #ececec;
}
.code-block-header {
background: #f5f5f5;
}
/* 机器人图标保持黑色 */
.status-logo {
color: #0d0d0d;
}
/* 思考模式切换按钮 - 统一浅灰色背景,只有图标变化 */
.mode-indicator {
background: #ececec !important;
color: #0d0d0d;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.mode-indicator:hover {
background: #e0e0e0 !important;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);
}
.mode-indicator:focus-visible {
box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.12);
}
.mode-indicator .icon {
color: #0d0d0d;
}
/* 返回工作区按钮 - 移除光晕 */
.personal-page-close {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.personal-page-close:hover {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);
}
/* 个人空间选择栏 - 文字颜色 */
.run-mode-title {
color: #0d0d0d !important;
}
.run-mode-desc {
color: #676767 !important;
}
/* 去掉选中后的文字变色 */
.run-mode-card.active .run-mode-title {
color: #0d0d0d !important;
}
.run-mode-card.active .run-mode-desc {
color: #676767 !important;
}
.run-mode-card:hover .run-mode-title {
color: #0d0d0d !important;
}
.run-mode-card:hover .run-mode-desc {
color: #676767 !important;
}
/* 个人空间左侧选项卡 - 文字颜色保持灰色,不随选中状态变化 */
.personal-tab-button {
color: #676767 !important;
}
.personal-tab-button.active {
color: #676767 !important;
}
/* 对话记录栏 - 统一使用浅灰色背景 */
.new-conversation-btn {
background: #ececec !important;
color: #0d0d0d !important;
border-color: rgba(0, 0, 0, 0.1) !important;
box-shadow: none !important;
}
.new-conversation-btn:hover {
background: #e0e0e0 !important;
box-shadow: none !important;
}
.conversation-item {
background: #ffffff !important;
box-shadow: none !important;
}
.conversation-item:hover {
background: #f5f5f5 !important;
box-shadow: none !important;
}
.conversation-item.active {
background: #f5f5f5 !important;
box-shadow: none !important;
}
.toggle-sidebar-btn {
background: #ececec !important;
border-color: rgba(0, 0, 0, 0.1) !important;
}
.toggle-sidebar-btn:hover {
background: #e0e0e0 !important;
}
.search-input {
background: #ececec !important;
}
.search-input:focus {
background: #e0e0e0 !important;
}
.conversation-personal-entry:not(.collapsed) .personal-page-btn {
background: #ececec !important;
}
.conversation-personal-entry:not(.collapsed) .personal-page-btn:hover,
.conversation-personal-entry:not(.collapsed) .personal-page-btn:focus-visible {
background: #e0e0e0 !important;
}
/* 删除按钮 - 使用深灰色 */
.conversation-action-btn.delete-btn {
background: #6b6b6b !important;
}
.conversation-action-btn.delete-btn:hover {
background: #5a5a5a !important;
}
/* 个人空间 - consideration 删除按钮 */
.consideration-remove {
color: #6b6b6b !important;
}
.consideration-remove:hover {
color: #0d0d0d !important;
}
/* 个人空间 - 整体背景色为侧边栏颜色,选项卡和卡片为白色 */
.personal-page-card {
background: #f9f9f9 !important;
}
.personal-section {
background: #ffffff !important;
}
.personal-tab-button {
background: #ffffff !important;
}
.personal-tab-button:hover {
background: #f5f5f5 !important;
}
.personal-tab-button.active {
background: #ffffff !important;
}
}
/* 暗色主题:移除光晕效果和彩色 */
body[data-theme='dark'] {
.experiment-visual-glow,
.experiment-orb {
display: none;
}
.experiment-visual {
background: var(--theme-surface-card);
box-shadow: var(--theme-shadow-soft);
}
.stadium-shell.is-focused,
.stadium-shell.has-text,
.stadium-shell.is-multiline.is-focused,
.stadium-shell.is-multiline.has-text {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}
/* 输入栏 - 改为灰黑色 */
.stadium-shell {
background: #2a2a2a;
border-color: rgba(255, 255, 255, 0.1);
}
.stadium-shell.is-focused,
.stadium-shell.has-text {
background: #2a2a2a;
border-color: rgba(255, 255, 255, 0.15);
}
/* 输入框文字颜色 */
.stadium-input {
color: #ffffff;
}
.stadium-input::placeholder {
color: #707070;
}
/* 按钮背景色 - 使用浅一些的灰色 */
.stadium-btn.send-btn {
background: #3a3a3a;
color: #ffffff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.stadium-btn.send-btn:hover:not(:disabled) {
background: #454545;
}
.stadium-btn.send-btn .send-icon {
border-left-color: #ffffff;
}
.stadium-btn.send-btn .stop-icon {
background-color: #ffffff;
}
/* 快捷菜单 - 改为灰黑色 */
.quick-menu {
background: #2a2a2a;
border-color: rgba(255, 255, 255, 0.1);
}
.quick-submenu {
background: #2a2a2a;
border-color: rgba(255, 255, 255, 0.1);
}
.menu-entry:hover:not(:disabled) {
background: #353535;
}
.menu-entry.active {
background: #353535;
}
.personal-page-btn {
background: #2a2a2a;
}
.conversation-personal-entry:not(.collapsed) .personal-page-btn {
background: #2a2a2a;
}
.conversation-personal-entry:not(.collapsed) .personal-page-btn:hover,
.conversation-personal-entry:not(.collapsed) .personal-page-btn:focus-visible {
background: #353535;
}
.code-block-header {
background: #2a2a2a;
}
.copy-code-btn {
filter: brightness(0) invert(1);
}
/* 机器人图标保持白色 */
.status-logo {
color: #ffffff;
}
/* 思考模式切换按钮 - 统一深灰色背景,只有图标变化 */
.mode-indicator {
background: #2a2a2a !important;
color: #ffffff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.mode-indicator:hover {
background: #353535 !important;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.4);
}
.mode-indicator:focus-visible {
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1), 0 3px 6px rgba(0, 0, 0, 0.4);
}
.mode-indicator .icon {
color: #ffffff;
}
/* 返回工作区按钮 - 移除光晕 */
.personal-page-close {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.personal-page-close:hover {
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.4);
}
/* 对话侧边栏图标颜色 */
.conversation-header button,
.workspace-immersive-btn {
color: #ffffff;
}
.conversation-header button svg,
.workspace-immersive-btn svg {
stroke: #ffffff;
fill: none;
}
/* 个人空间按钮 - 移除圆形外框 */
.personal-page-btn.icon-only {
background: transparent;
border: none;
}
.personal-page-btn.icon-only:hover {
background: transparent;
border: none;
}
/* 个人空间选择栏 - 文字颜色 */
.run-mode-title {
color: #ffffff !important;
}
.run-mode-desc {
color: #a0a0a0 !important;
}
/* 去掉选中后的文字变色 */
.run-mode-card.active .run-mode-title {
color: #ffffff !important;
}
.run-mode-card.active .run-mode-desc {
color: #a0a0a0 !important;
}
.run-mode-card:hover .run-mode-title {
color: #ffffff !important;
}
.run-mode-card:hover .run-mode-desc {
color: #a0a0a0 !important;
}
/* 个人空间左侧选项卡 - 暗色模式下文字白色,不随选中状态变化 */
.personal-tab-button {
color: #ffffff !important;
}
.personal-tab-button.active {
color: #ffffff !important;
}
/* 个人空间左侧选项卡描述文字 */
.personal-tab-desc {
color: #a0a0a0 !important;
}
/* 空对话欢迎语 - 白色 */
.blank-hero-text {
color: #ffffff !important;
}
/* 对话记录栏 - 统一使用深灰色背景 */
.new-conversation-btn {
background: #2a2a2a !important;
color: #ffffff !important;
border-color: rgba(255, 255, 255, 0.1) !important;
box-shadow: none !important;
}
.new-conversation-btn:hover {
background: #3a3a3a !important;
box-shadow: none !important;
}
.conversation-item {
background: #2a2a2a !important;
box-shadow: none !important;
}
.conversation-item:hover {
background: #3a3a3a !important;
box-shadow: none !important;
}
.conversation-item.active {
background: #3a3a3a !important;
box-shadow: none !important;
}
.toggle-sidebar-btn {
background: #2a2a2a !important;
border-color: rgba(255, 255, 255, 0.1) !important;
}
.toggle-sidebar-btn:hover {
background: #3a3a3a !important;
}
.search-input {
background: #2a2a2a !important;
}
.search-input:focus {
background: #3a3a3a !important;
}
.conversation-personal-entry:not(.collapsed) .personal-page-btn {
background: #2a2a2a !important;
}
.conversation-personal-entry:not(.collapsed) .personal-page-btn:hover,
.conversation-personal-entry:not(.collapsed) .personal-page-btn:focus-visible {
background: #3a3a3a !important;
}
/* 删除按钮 - 使用浅灰色 */
.conversation-action-btn.delete-btn {
background: #6b6b6b !important;
}
.conversation-action-btn.delete-btn:hover {
background: #7a7a7a !important;
}
/* 顶部栏 - 黑色背景,白色字体 */
.conversation-ribbon {
background: #1a1a1a !important;
}
.conversation-ribbon::after {
background: #1a1a1a !important;
}
.mobile-topbar-title {
color: #ffffff !important;
text-shadow: none !important;
}
.mobile-topbar-selector {
color: #ffffff !important;
}
.mobile-topbar-selector:hover {
color: #ffffff !important;
}
.mobile-topbar-selector.open {
color: #ffffff !important;
}
.mobile-selector-model {
color: #ffffff !important;
}
.mobile-selector-mode {
color: #a0a0a0 !important;
}
/* 手机端顶部栏 - 黑色背景 */
.mobile-panel-topbar {
background: #1a1a1a !important;
}
.mobile-panel-topbar::after {
background: #1a1a1a !important;
}
/* 手机端顶部栏菜单按钮 - 白色图标 */
.mobile-panel-fab {
color: #ffffff !important;
}
.mobile-panel-fab img {
filter: brightness(0) invert(1) !important;
}
/* 手机端展开菜单 - 黑色背景 */
.mobile-panel-menu {
background: #000000 !important;
}
/* 手机端菜单按钮 - SVG 白色 */
.mobile-menu-btn {
color: #ffffff !important;
}
.mobile-menu-svg {
filter: brightness(0) invert(1) !important;
}
.mobile-menu-btn img {
filter: brightness(0) invert(1) !important;
}
/* 用量统计 - 数字白色 */
.stat-value {
color: #ffffff !important;
}
/* 对话区域顶部的模型选择器 - 白色字体 */
.conversation-ribbon__selector {
color: #ffffff !important;
}
.conversation-ribbon__selector:hover {
color: #ffffff !important;
}
.conversation-ribbon__selector.open {
color: #ffffff !important;
}
.selector-model {
color: #ffffff !important;
}
.selector-mode {
color: #a0a0a0 !important;
}
.conversation-ribbon__text {
color: #ffffff !important;
}
/* 模型选择下拉菜单 - 和输入栏一样的底色 */
.model-mode-dropdown {
background: #2a2a2a !important;
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.5) !important;
}
.dropdown-title {
color: #a0a0a0 !important;
}
.dropdown-item {
color: #ffffff !important;
}
.dropdown-item:hover:not(.disabled) {
background: #3a3a3a !important;
}
.item-label {
color: #ffffff !important;
}
.item-desc {
color: #a0a0a0 !important;
}
/* 个人空间 - consideration 列表项的虚线边框 */
.consideration-item {
border-color: #6b6b6b !important;
}
/* 个人空间 - consideration 删除按钮 */
.consideration-remove {
color: #a0a0a0 !important;
}
.consideration-remove:hover {
color: #ffffff !important;
}
/* 个人空间 - 整体背景色为对话区域颜色,选项卡和卡片为纯黑色 */
.personal-page-card {
background: #1a1a1a !important;
}
.personal-section {
background: #000000 !important;
}
.personal-tab-button {
background: #000000 !important;
}
.personal-tab-button:hover {
background: #1a1a1a !important;
}
.personal-tab-button.active {
background: #1a1a1a !important;
}
/* 快捷菜单 - 和输入栏一样的底色 */
.quick-menu {
background: #2a2a2a !important;
border-color: rgba(255, 255, 255, 0.1) !important;
}
.quick-submenu {
background: #2a2a2a !important;
border-color: rgba(255, 255, 255, 0.1) !important;
}
.menu-entry {
color: #ffffff !important;
}
.menu-entry:hover:not(:disabled) {
background: #3a3a3a !important;
}
.menu-entry.active {
background: #3a3a3a !important;
}
.menu-entry .entry-arrow {
color: #a0a0a0 !important;
}
.submenu-label {
color: #ffffff !important;
}
/* 用户消息块 - 使用浅灰色 */
.user-message .message-text {
background: #2a2a2a;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
/* AI 消息块 - 使用浅灰色 */
.assistant-message .message-text {
background: #2a2a2a;
border-left-color: #505050;
}
/* 思考块和工具块 - 使用更深的颜色(类似选中状态) */
.thinking-block,
.tool-block,
.collapsible-block {
background: #1a1a1a;
}
.collapsible-header {
background: #1a1a1a;
}
.collapsible-header:hover {
background: #202020;
}
/* 代码块 - 代码区域纯黑底+白字 */
.code-block-wrapper {
background: #2a2a2a;
border-color: rgba(255, 255, 255, 0.1);
}
.text-output pre {
background: #0a0a0a !important;
color: #ffffff;
}
.text-output pre code {
background: transparent !important;
color: #ffffff;
}
/* 滚动锁定按钮 */
.scroll-lock-btn {
background: #2a2a2a;
border-color: rgba(255, 255, 255, 0.1);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
.scroll-lock-btn:hover {
background: #353535;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.4);
}
.scroll-lock-toggle.locked .scroll-lock-btn {
border-color: rgba(255, 255, 255, 0.2);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}
.scroll-lock-toggle.locked .scroll-lock-btn img {
filter: brightness(0) invert(1);
}
/* 左侧面板头部(文件/待办事项/子智能体标签栏) */
.sidebar-header {
background: #1a1a1a;
}
.sidebar-manage-btn {
background: #2a2a2a;
border-color: rgba(255, 255, 255, 0.1);
}
.sidebar-manage-btn:hover {
background: #353535;
border-color: rgba(255, 255, 255, 0.15);
}
.sidebar-view-toggle {
background: #2a2a2a;
border-color: rgba(255, 255, 255, 0.1);
}
.sidebar-view-toggle:hover {
background: #353535;
}
.panel-menu {
background: #2a2a2a;
border-color: rgba(255, 255, 255, 0.1);
}
.panel-menu button.active {
background: rgba(255, 255, 255, 0.1);
}
.todo-empty,
.sub-agent-empty,
.file-tree-empty {
background: #1a1a1a;
border-color: rgba(255, 255, 255, 0.1);
}
.todo-task {
background: #2a2a2a;
border-color: rgba(255, 255, 255, 0.1);
}
.folder-header,
.file-node {
background: transparent;
}
.folder-header:hover {
background: #2a2a2a;
}
.file-node.file-leaf:hover {
background: #2a2a2a;
}
/* 聚焦文件面板 */
.file-tab {
background: #1a1a1a;
border-color: rgba(255, 255, 255, 0.1);
}
.tab-header {
background: #2a2a2a;
}
.file-content {
background: #0a0a0a;
}
.file-content code {
color: #ffffff;
}
.focus-close-btn {
background: #2a2a2a;
}
.focus-close-btn:hover {
background: #353535;
}
/* 表格 */
.text-output .text-content table {
background: #2a2a2a;
}
.text-output .text-content thead {
background: #1a1a1a;
}
}

View File

@ -63,7 +63,7 @@
}
.tab-header {
background: rgba(218, 119, 86, 0.08);
background: var(--theme-chip-bg);
padding: 10px 16px;
display: flex;
justify-content: space-between;

View File

@ -9,7 +9,7 @@
}
.resize-handle:hover {
background: rgba(218, 119, 86, 0.22);
background: var(--claude-highlight);
}
.workspace-region {
@ -361,7 +361,7 @@
.file-tree {
flex: 1 1 auto;
min-height: 0;
padding: 12px 0 20px;
padding: 16px 20px 24px;
color: var(--claude-text);
overflow-y: auto;
-ms-overflow-style: none;
@ -381,7 +381,7 @@
}
.sub-agent-panel {
padding: 16px 16px 24px;
padding: 16px 20px 24px;
}
.sub-agent-cards {
@ -446,7 +446,7 @@
.file-tree-empty {
font-size: 14px;
color: var(--claude-text-secondary);
padding: 12px;
padding: 10px 14px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.8);
border: 1px dashed var(--claude-border);
@ -509,7 +509,7 @@
}
.folder-header:hover {
background: rgba(218, 119, 86, 0.12);
background: var(--claude-highlight);
transform: translateX(2px);
}
@ -554,7 +554,7 @@
}
.file-node.file-leaf:hover {
background: rgba(218, 119, 86, 0.1);
background: var(--claude-highlight);
}
.file-node .annotation {

View File

@ -39,7 +39,7 @@
}
.conversation-header {
padding: 18px 16px;
padding: 12px 16px;
border-bottom: 1px solid rgba(118, 103, 84, 0.12);
display: flex;
justify-content: space-between;
@ -50,7 +50,7 @@
position: sticky;
top: 0;
z-index: 60;
min-height: 68px;
min-height: 62px;
}
.conversation-header.collapsed-layout {
@ -227,7 +227,7 @@
}
.display-mode-btn.active {
background: rgba(218, 119, 86, 0.28);
background: var(--claude-highlight);
color: var(--claude-accent);
}
@ -404,14 +404,14 @@
.conversation-personal-entry {
margin-top: auto;
padding: 8px 16px 12px;
padding: 4px 16px 12px;
display: flex;
justify-content: center;
background-color: inherit;
}
.conversation-personal-entry.collapsed {
padding: 8px 0 10px;
padding: 4px 0 10px;
}
.personal-page-btn {
@ -451,7 +451,7 @@
.personal-page-btn:focus-visible {
outline: none;
box-shadow: 0 0 0 2px rgba(218, 119, 86, 0.15);
box-shadow: 0 0 0 2px var(--claude-highlight);
}
.personal-page-btn.icon-only {
@ -475,7 +475,7 @@
.conversation-personal-entry:not(.collapsed) .personal-page-btn:hover,
.conversation-personal-entry:not(.collapsed) .personal-page-btn:focus-visible {
background: rgba(218, 119, 86, 0.08);
background: var(--claude-highlight);
}
.personal-label {
@ -519,7 +519,7 @@
outline: none;
border-color: var(--claude-accent);
background: rgba(255, 255, 255, 0.85);
box-shadow: 0 0 0 3px rgba(218, 119, 86, 0.18);
box-shadow: 0 0 0 3px var(--claude-highlight);
}
.conversation-list {
@ -611,14 +611,14 @@ o-conversations {
.conversation-item:hover {
background: rgba(255, 255, 255, 0.85);
border-color: rgba(218, 119, 86, 0.35);
box-shadow: 0 10px 24px rgba(61, 57, 41, 0.08);
border-color: var(--claude-accent);
box-shadow: var(--theme-shadow-soft);
}
.conversation-item.active {
background: rgba(218, 119, 86, 0.18);
background: var(--claude-highlight);
border-color: var(--claude-accent);
box-shadow: 0 10px 28px rgba(189, 93, 58, 0.18);
box-shadow: var(--theme-shadow-soft);
}
.conversation-title {
@ -710,9 +710,9 @@ o-conversations {
}
.load-more-btn {
background: rgba(218, 119, 86, 0.12);
background: var(--claude-highlight);
color: var(--claude-accent);
border: 1px solid rgba(218, 119, 86, 0.35);
border: 1px solid var(--claude-accent);
padding: 6px 14px;
border-radius: 6px;
font-size: 12px;

View File

@ -8,6 +8,59 @@ const applyTheme = (theme: ThemeKey) => {
const root = document.documentElement;
root.setAttribute('data-theme', theme);
document.body.setAttribute('data-theme', theme);
// 调试信息
console.log('=== Theme Applied ===');
console.log('Theme:', theme);
// 检查样式是否生效
setTimeout(() => {
console.log('=== Elements Check ===');
// 输入栏底色
const stadiumShell = document.querySelector('.stadium-shell');
console.log('.stadium-shell exists:', !!stadiumShell);
if (stadiumShell) {
const styles = window.getComputedStyle(stadiumShell);
console.log('.stadium-shell background-color:', styles.backgroundColor);
}
// 对话区域顶部的模型选择器
const ribbonSelector = document.querySelector('.conversation-ribbon__selector');
console.log('.conversation-ribbon__selector exists:', !!ribbonSelector);
if (ribbonSelector) {
const styles = window.getComputedStyle(ribbonSelector);
console.log('.conversation-ribbon__selector color:', styles.color);
}
const selectorModel = document.querySelector('.selector-model');
if (selectorModel) {
const styles = window.getComputedStyle(selectorModel);
console.log('.selector-model color:', styles.color);
}
const selectorMode = document.querySelector('.selector-mode');
if (selectorMode) {
const styles = window.getComputedStyle(selectorMode);
console.log('.selector-mode color:', styles.color);
}
// 下拉菜单
const dropdown = document.querySelector('.model-mode-dropdown');
console.log('.model-mode-dropdown exists:', !!dropdown);
if (dropdown) {
const styles = window.getComputedStyle(dropdown);
console.log('.model-mode-dropdown background-color:', styles.backgroundColor);
}
const dropdownItem = document.querySelector('.dropdown-item');
if (dropdownItem) {
const styles = window.getComputedStyle(dropdownItem);
console.log('.dropdown-item color:', styles.color);
}
console.log('=== End Elements Check ===');
}, 100);
};
const loadTheme = (): ThemeKey => {

View File

@ -473,20 +473,18 @@ def _format_todo_update_task(result_data: Dict[str, Any]) -> str:
def _format_update_memory(result_data: Dict[str, Any]) -> str:
if not result_data.get("success"):
return _format_failure("update_memory", result_data)
mem_type = result_data.get("memory_type") or "main"
operation = result_data.get("operation") or "write"
label = "主记忆" if mem_type == "main" else "任务记忆"
idx = result_data.get("index")
count = result_data.get("count")
if operation == "append":
suffix = f"(共 {count} 条)" if count is not None else ""
return f"{label}已追加新条目{suffix}"
return f"记忆已追加新条目{suffix}"
if operation == "replace":
return f"{label}{idx} 条已替换。"
return f"记忆{idx} 条已替换。"
if operation == "delete":
suffix = f"(剩余 {count} 条)" if count is not None else ""
return f"{label}{idx} 条已删除{suffix}"
return f"{label}已更新。"
return f"记忆{idx} 条已删除{suffix}"
return f"记忆已更新。"
def _format_create_sub_agent(result_data: Dict[str, Any]) -> str: