diff --git a/core/main_terminal_parts/tools_definition.py b/core/main_terminal_parts/tools_definition.py index 7e37b30..6b60a38 100644 --- a/core/main_terminal_parts/tools_definition.py +++ b/core/main_terminal_parts/tools_definition.py @@ -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"] } } }, diff --git a/core/main_terminal_parts/tools_execution.py b/core/main_terminal_parts/tools_execution.py index ac442c9..31d4c9f 100644 --- a/core/main_terminal_parts/tools_execution.py +++ b/core/main_terminal_parts/tools_execution.py @@ -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 diff --git a/modules/personalization_manager.py b/modules/personalization_manager.py index 78b4e4b..0e12fd0 100644 --- a/modules/personalization_manager.py +++ b/modules/personalization_manager.py @@ -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 diff --git a/package-lock.json b/package-lock.json index 09ef73a..c9d0399 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 67b7031..595743d 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/server/chat_flow_tool_loop.py b/server/chat_flow_tool_loop.py index 5502555..7db104f 100644 --- a/server/chat_flow_tool_loop.py +++ b/server/chat_flow_tool_loop.py @@ -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() diff --git a/server/conversation.py b/server/conversation.py index 83af8f0..8886087 100644 --- a/server/conversation.py +++ b/server/conversation.py @@ -127,14 +127,14 @@ 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 run_mode = data.get('mode') if preserve_mode and 'mode' in data else None - + result = terminal.create_new_conversation(thinking_mode=thinking_mode, run_mode=run_mode) - + if result["success"]: session['run_mode'] = terminal.run_mode session['thinking_mode'] = terminal.thinking_mode @@ -143,13 +143,13 @@ def create_conversation(terminal: WebTerminal, workspace: UserWorkspace, usernam 'action': 'created', 'conversation_id': result["conversation_id"] }, room=f"user_{username}") - + # 广播当前对话切换事件 socketio.emit('conversation_changed', { 'conversation_id': result["conversation_id"], 'title': "新对话" }, room=f"user_{username}") - + return jsonify(result), 201 else: return jsonify(result), 500 @@ -208,7 +208,7 @@ def load_conversation(conversation_id, terminal: WebTerminal, workspace: UserWor """加载特定对话""" try: result = terminal.load_conversation(conversation_id) - + if result["success"]: # 广播对话切换事件 socketio.emit('conversation_changed', { @@ -216,21 +216,21 @@ def load_conversation(conversation_id, terminal: WebTerminal, workspace: UserWor 'title': result.get("title", "未知对话"), 'messages_count': result.get("messages_count", 0) }, room=f"user_{username}") - + # 广播系统状态更新(因为当前对话改变了) status = terminal.get_status() socketio.emit('status_update', status, room=f"user_{username}") - + # 清理和重置相关UI状态 socketio.emit('conversation_loaded', { 'conversation_id': conversation_id, 'clear_ui': True # 提示前端清理当前UI状态 }, room=f"user_{username}") - + return jsonify(result) else: return jsonify(result), 404 if "不存在" in result.get("message", "") else 500 - + except Exception as e: print(f"[API] 加载对话错误: {e}") return jsonify({ diff --git a/static/src/App.vue b/static/src/App.vue index 6106a72..6d69d9e 100644 --- a/static/src/App.vue +++ b/static/src/App.vue @@ -293,14 +293,6 @@
- - @@ -510,26 +502,6 @@ - - -
-
- -
-
-
@@ -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 }; diff --git a/static/src/app/components.ts b/static/src/app/components.ts index 87d2228..5a4e473 100644 --- a/static/src/app/components.ts +++ b/static/src/app/components.ts @@ -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, diff --git a/static/src/app/lifecycle.ts b/static/src/app/lifecycle.ts index e7bbdb2..6fbfa3e 100644 --- a/static/src/app/lifecycle.ts +++ b/static/src/app/lifecycle.ts @@ -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(); diff --git a/static/src/app/methods/conversation.ts b/static/src/app/methods/conversation.ts index 83d2070..8cb44ac 100644 --- a/static/src/app/methods/conversation.ts +++ b/static/src/app/methods/conversation.ts @@ -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; diff --git a/static/src/app/methods/ui.ts b/static/src/app/methods/ui.ts index 66c4d14..5c113dc 100644 --- a/static/src/app/methods/ui.ts +++ b/static/src/app/methods/ui.ts @@ -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; } }; diff --git a/static/src/components/chat/StackedBlocks.vue b/static/src/components/chat/StackedBlocks.vue index 79bd0c1..fe29dc5 100644 --- a/static/src/components/chat/StackedBlocks.vue +++ b/static/src/components/chat/StackedBlocks.vue @@ -81,7 +81,8 @@
-
+
+
搜索内容:{{ action.tool.result.query || action.tool.arguments?.query }}
主题:{{ formatSearchTopic(action.tool.result.filters || {}) }}
@@ -124,6 +125,8 @@ + + diff --git a/static/src/components/chat/actions/toolRenderers.ts b/static/src/components/chat/actions/toolRenderers.ts new file mode 100644 index 0000000..7e9646a --- /dev/null +++ b/static/src/components/chat/actions/toolRenderers.ts @@ -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, + formatSearchTime: (filters: Record) => string, + formatSearchDomains: (filters: Record) => 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, + formatSearchTime: (filters: Record) => string, + formatSearchDomains: (filters: Record) => string +): string { + const query = result.query || args.query || ''; + const filters = result.filters || {}; + const totalResults = result.total_results || 0; + const results = result.results || []; + + let html = '
'; + html += `
搜索内容:${escapeHtml(query)}
`; + html += `
主题:${escapeHtml(formatSearchTopic(filters))}
`; + html += `
时间范围:${escapeHtml(formatSearchTime(filters))}
`; + html += `
限定网站:${escapeHtml(formatSearchDomains(filters))}
`; + html += `
结果数量:${totalResults}
`; + html += '
'; + + if (results.length > 0) { + html += '
'; + results.forEach((item: any) => { + html += '
'; + html += `
${escapeHtml(item.title || '无标题')}
`; + html += '
'; + if (item.url) { + html += `${escapeHtml(item.url)}`; + } else { + html += '无可用链接'; + } + html += '
'; + }); + html += '
'; + } else { + html += '
未返回详细的搜索结果。
'; + } + + 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 = '
'; + html += `
URL:${escapeHtml(url)}
`; + html += `
状态:${status}
`; + html += '
'; + + if (content) { + html += '
'; + html += `
${escapeHtml(content)}
`; + html += '
'; + } + + 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 = '
'; + html += `
URL:${escapeHtml(url)}
`; + html += `
保存路径:${escapeHtml(path)}
`; + html += `
状态:${status}
`; + html += '
'; + + return html; +} + +// 文件编辑类渲染函数 +function renderCreateFile(result: any, args: any): string { + const path = args.path || result.path || ''; + const status = result.success ? '✓ 已创建' : '✗ 失败'; + + let html = '
'; + html += `
路径:${escapeHtml(path)}
`; + html += `
状态:${status}
`; + html += '
'; + + return html; +} + +function renderWriteFile(result: any, args: any): string { + const path = args.path || result.path || ''; + const status = result.success ? '✓ 已写入' : '✗ 失败'; + + let html = '
'; + html += `
路径:${escapeHtml(path)}
`; + html += `
状态:${status}
`; + html += '
'; + + 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 = '
'; + html += `
路径:${escapeHtml(path)}
`; + html += `
状态:${status}
`; + html += '
'; + + if (oldString || newString) { + html += '
'; + + if (oldString) { + const oldLines = oldString.split('\n'); + oldLines.forEach((line: string) => { + html += `
- ${escapeHtml(line)}
`; + }); + } + + if (newString) { + const newLines = newString.split('\n'); + newLines.forEach((line: string) => { + html += `
+ ${escapeHtml(line)}
`; + }); + } + + html += '
'; + } + + 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 = '
'; + html += `
路径:${escapeHtml(path)}
`; + html += `
状态:${status}
`; + html += '
'; + + if (content) { + html += '
'; + html += ''; + const lines = content.split('\n'); + html += '
'; + lines.forEach((line: string) => { + html += `
+ ${escapeHtml(line)}
`; + }); + html += '
'; + html += '
'; + } + + 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 = '
'; + html += `
路径:${escapeHtml(path)}
`; + html += `
状态:${status}
`; + html += '
'; + + if (operations.length > 0) { + html += '
'; + operations.forEach((op: any, idx: number) => { + if (idx > 0) html += '
'; + + 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 += `
[${idx + 1}] ${escapeHtml(type)}
`; + + if (oldText) { + const oldLines = oldText.split('\n'); + oldLines.forEach((line: string) => { + html += `
- ${escapeHtml(line)}
`; + }); + } + + if (newText) { + const newLines = newText.split('\n'); + newLines.forEach((line: string) => { + html += `
+ ${escapeHtml(line)}
`; + }); + } + }); + html += '
'; + } + + return html; +} + +function renderDeleteFile(result: any, args: any): string { + const path = args.path || result.path || ''; + const status = result.success ? '✓ 已删除' : '✗ 失败'; + + let html = '
'; + html += `
路径:${escapeHtml(path)}
`; + html += `
状态:${status}
`; + html += '
'; + + 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 = '
'; + html += `
路径:${escapeHtml(oldPath)}
`; + html += `
状态:${status}
`; + html += `
重命名:${escapeHtml(oldPath)} → ${escapeHtml(newPath)}
`; + html += '
'; + + 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 = '
'; + html += `
路径:${escapeHtml(path)}
`; + html += `
状态:${status}
`; + if (size > 0) { + html += `
大小:${formatBytes(size)}
`; + } + html += '
'; + + if (content) { + html += '
'; + html += `
${escapeHtml(content)}
`; + html += '
'; + } + + 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 = '
'; + html += `
路径:${escapeHtml(path)}
`; + html += `
状态:${status}
`; + html += '
'; + + if (analysis) { + html += '
'; + html += ''; + html += `
${escapeHtml(analysis)}
`; + html += '
'; + } + + 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 = '
'; + html += `
路径:${escapeHtml(path)}
`; + html += `
状态:${status}
`; + if (size > 0) { + html += `
大小:${formatBytes(size)}
`; + } + html += '
'; + + if (imageUrl) { + html += `
OCR图片
`; + } + + if (text) { + html += '
'; + html += ''; + html += `
${escapeHtml(text)}
`; + html += '
'; + } + + 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 = '
'; + html += `
路径:${escapeHtml(path)}
`; + html += `
状态:${status}
`; + if (size > 0) { + html += `
大小:${formatBytes(size)}
`; + } + html += '
'; + + if (imageUrl) { + html += `
查看图片
`; + } + + 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 = '
'; + html += `
操作:${escapeHtml(operation)}
`; + html += `
终端名:${escapeHtml(sessionName)}
`; + html += `
状态:${status}
`; + html += '
'; + + 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 = '
'; + html += `
指令:${escapeHtml(command)}
`; + html += `
超时时间:${timeout}秒
`; + html += '
'; + + if (output) { + html += '
'; + html += ''; + html += `
${escapeHtml(output)}
`; + html += '
'; + } + + 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 = '
'; + html += `
状态:${status}
`; + html += `
终端名:${escapeHtml(sessionName)}
`; + if (startLine || endLine) { + html += `
行范围:${startLine} - ${endLine}
`; + } + html += '
'; + + if (content) { + html += '
'; + html += ''; + html += `
${escapeHtml(content)}
`; + html += '
'; + } + + return html; +} + +function renderSleep(result: any, args: any): string { + const seconds = args.seconds || args.duration || 0; + const status = result.success ? '✓ 完成' : '✗ 失败'; + + let html = '
'; + html += `
等待时间:${seconds}秒
`; + html += `
状态:${status}
`; + html += '
'; + + 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 = '
'; + html += `
指令:${escapeHtml(command)}
`; + html += `
超时时间:${timeout}秒
`; + if (exitCode !== undefined) { + html += `
退出码:${exitCode}
`; + } + html += '
'; + + if (output) { + html += '
'; + html += ''; + html += `
${escapeHtml(output)}
`; + html += '
'; + } + + return html; +} + +function renderRunPython(result: any, args: any): string { + const code = result.code || args.code || ''; + const output = result.output || ''; + + let html = '
'; + html += '
代码:
'; + html += `
${escapeHtml(code)}
`; + html += '
'; + + if (output) { + html += '
'; + html += '
输出:
'; + html += `
${escapeHtml(output)}
`; + html += '
'; + } + + 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 = '
'; + + if (operation === 'append') { + html += `
新增记忆
`; + html += `
${count}. ${escapeHtml(content)}
`; + } else if (operation === 'replace') { + html += `
更新记忆
`; + html += `
${index}. ${escapeHtml(content)}
`; + } else if (operation === 'delete') { + html += `
删除记忆
`; + html += `
${index}. ${escapeHtml(content || '已删除')}
`; + } + + html += '
'; + + 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 = '
'; + html += `
状态:${status}
`; + html += `
标题:${escapeHtml(title)}
`; + html += '
'; + + if (tasks.length > 0) { + html += '
'; + html += '
'; + tasks.forEach((task: any) => { + const taskText = typeof task === 'string' ? task : (task.text || task.title || task.content || ''); + html += `
${escapeHtml(taskText)}
`; + }); + html += '
'; + html += '
'; + } + + 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 = '
'; + html += `
状态:${status}
`; + html += `
标题:${escapeHtml(title)}
`; + html += '
'; + + if (tasks.length > 0) { + html += '
'; + html += '
'; + 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 += `
${escapeHtml(taskText)}
`; + }); + html += '
'; + html += '
'; + } + + 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 = '
'; + html += `
状态:${status}
`; + html += `
类型:${escapeHtml(type)}
`; + html += '
'; + + if (content) { + html += '
'; + html += `
${escapeHtml(content)}
`; + html += '
'; + } + + return html; +} diff --git a/static/src/components/input/QuickMenu.vue b/static/src/components/input/QuickMenu.vue index 03cb954..e22e97f 100644 --- a/static/src/components/input/QuickMenu.vue +++ b/static/src/components/input/QuickMenu.vue @@ -97,14 +97,6 @@ > 实时终端 -