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:
parent
5ce21eb280
commit
43409c523e
@ -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"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
10
package-lock.json
generated
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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>
|
||||
|
||||
649
static/src/components/chat/actions/toolRenderers.ts
Normal file
649
static/src/components/chat/actions/toolRenderers.ts
Normal 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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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']
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@ -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')
|
||||
: [],
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user