From 684b2f773bf71f43498f5493e1ea98e5a1bc3bb9 Mon Sep 17 00:00:00 2001 From: thzxx Date: Thu, 23 Apr 2026 15:42:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=B7=A5=E5=85=B7=E5=8D=A1=E7=89=87?= =?UTF-8?q?=E7=B2=BE=E7=AE=80=E3=80=81=E7=A7=BB=E9=99=A4token=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1=E3=80=81=E6=B6=88=E6=81=AF=E6=97=B6=E9=97=B4=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 聊天消息工具卡片只显示工具名+调用内容,不显示执行结果(工作空间工具页签已有完整结果) 2. 移除所有token统计相关代码:顶栏统计、消息内token/耗时显示、历史记录中的token显示 3. 每条消息显示具体时间(年月日 时分秒),formatTime 已含秒数 --- src/renderer/components/chat-area.ts | 125 +---------------------- src/renderer/components/history-modal.ts | 3 +- src/renderer/components/input-area.ts | 7 +- src/renderer/index.html | 1 - src/renderer/main.ts | 4 +- src/renderer/styles/style.css | 30 ------ src/renderer/utils/utils.ts | 2 +- 7 files changed, 6 insertions(+), 166 deletions(-) diff --git a/src/renderer/components/chat-area.ts b/src/renderer/components/chat-area.ts index f4b8ca9..c4650d3 100644 --- a/src/renderer/components/chat-area.ts +++ b/src/renderer/components/chat-area.ts @@ -184,13 +184,8 @@ export function appendMessageDOM(msg: ChatMessage, index: number): void { `; } - const stats: string[] = []; - const statClasses: string[] = []; - if (msg.timestamp) { stats.push(formatTime(msg.timestamp)); statClasses.push(''); } - if (msg.eval_count != null && msg.eval_count > 0) { stats.push(`${msg.eval_count} tokens`); statClasses.push('stat-tokens'); } - if (msg.total_duration != null && msg.total_duration > 0) { stats.push(`${(msg.total_duration / 1e9).toFixed(1)}s`); statClasses.push('stat-duration'); } - if (stats.length > 0) { - contentHtml += `
${stats.map((s, i) => `${s}`).join(' · ')}
`; + if (msg.timestamp) { + contentHtml += `
${formatTime(msg.timestamp)}
`; } div.innerHTML = ` @@ -246,96 +241,6 @@ function renderToolCallCard(tc: ToolCallRecord): string { if (args.selector) paramsHtml += `
选择器: ${escapeHtml(String(args.selector))}
`; if (args.action) paramsHtml += `
操作: ${escapeHtml(String(args.action))}
`; - let resultHtml = ''; - if (tc.result) { - if (tc.result.success) { - const r = tc.result as Record; - if (tc.name === 'read_file' && r.content) { - const content = String(r.content); - const lines = content.split('\n'); - const preview = lines.slice(0, 20).join('\n'); - resultHtml = `
${escapeHtml(preview)}${lines.length > 20 ? `\n... (共 ${r.lines || lines.length} 行)` : ''}
`; - resultHtml += `
📄 ${(r.size as number || 0) > 0 ? formatFileSize(r.size as number) : ''} · ${r.lines || '?'} 行
`; - } else if (tc.name === 'list_directory' && r.entries) { - const entries = r.entries as Array<{ name: string; type: string; size: number | null }>; - resultHtml = entries.slice(0, 30).map(e => - `
${e.type === 'directory' ? '📁' : '📄'} ${escapeHtml(e.name)}${e.size != null ? ` (${formatFileSize(e.size)})` : ''}
` - ).join(''); - if (entries.length > 30) resultHtml += `
... 共 ${(r.total as number) || entries.length} 项
`; - } else if (tc.name === 'search_files' && r.results) { - const results = r.results as Array<{ path: string; matches: Array<{ line: number; text: string }> }>; - resultHtml = results.slice(0, 10).map(file => - file.matches.slice(0, 3).map(m => - `
📄 ${escapeHtml(file.path)}:${m.line} ${escapeHtml(m.text).slice(0, 80)}
` - ).join('') - ).join(''); - resultHtml += `
共 ${r.total_files || '?'} 个文件,${r.total_matches || '?'} 处匹配
`; - } else if (tc.name === 'write_file') { - resultHtml = `
✅ 已写入 ${escapeHtml(String(r.path || ''))} (${formatFileSize(r.bytesWritten as number || 0)})
`; - } else if (tc.name === 'create_directory') { - resultHtml = `
✅ 已创建 ${escapeHtml(String(r.path || ''))}
`; - } else if (tc.name === 'delete_file') { - resultHtml = `
✅ 已删除 ${escapeHtml(String(r.path || ''))}
`; - } else if (tc.name === 'run_command') { - const stdout = String(r.stdout || '').slice(0, 1000); - const stderr = String(r.stderr || '').slice(0, 500); - if (stdout) resultHtml += `
${escapeHtml(stdout)}
`; - if (stderr) resultHtml += `
${escapeHtml(stderr)}
`; - resultHtml += `
exit: ${r.exitCode || 0} · ${r.duration || 0}ms
`; - } else if (tc.name === 'web_search' && r.results) { - const results = r.results as Array<{ title: string; url: string; snippet: string }>; - resultHtml = results.slice(0, 5).map((item, i) => - `
[${i + 1}] ${escapeHtml(item.title)}
${escapeHtml(item.url)}
` - ).join(''); - resultHtml += `
共 ${r.total || results.length} 条结果
`; - } else if (tc.name === 'web_fetch') { - const content = String(r.content || '').slice(0, 500); - resultHtml = `
${escapeHtml(content)}${(r.content as string || '').length > 500 ? '\n...' : ''}
`; - resultHtml += `
${r.length || content.length} chars
`; - } else if (tc.name === 'git' && r.action) { - if (r.commits) { - resultHtml = (r.commits as Array<{ hash: string; message: string }>).slice(0, 10).map(c => - `
${escapeHtml(c.hash)} ${escapeHtml(c.message)}
` - ).join(''); - } else if (r.branches) { - resultHtml = (r.branches as Array<{ name: string; current: boolean }>).map(b => - `
${b.current ? '→ ' : ''}${escapeHtml(b.name)}
` - ).join(''); - } else { - resultHtml = `
✅ ${r.action} 完成
`; - } - } else if (tc.name === 'memory_search' && r.results) { - const results = r.results as Array<{ type: string; content: string }>; - resultHtml = results.slice(0, 5).map(m => - `
${m.type === 'rule' ? '📏' : m.type === 'preference' ? '⚙️' : '📌'} ${escapeHtml(m.content).slice(0, 80)}
` - ).join(''); - } else if (tc.name === 'session_list' && r.sessions) { - const sessions = r.sessions as Array<{ title: string; messageCount: number }>; - resultHtml = sessions.slice(0, 5).map(s => - `
💬 ${escapeHtml(s.title)}
` - ).join(''); - } else if (tc.name?.startsWith('browser_')) { - if (tc.name === 'browser_screenshot' && r.png) { - resultHtml = `
📸 截图已生成
`; - } else if (tc.name === 'browser_extract') { - const text = String(r.text || '').slice(0, 300); - resultHtml = `
${escapeHtml(text)}
`; - resultHtml += `
链接: ${(r.links as unknown[])?.length || 0} 个
`; - } else if (r.title) { - resultHtml = `
📄 ${escapeHtml(r.title)}
`; - } else if (r.result) { - resultHtml = `
${escapeHtml(String(r.result).slice(0, 300))}
`; - } else { - resultHtml = `
✅ 完成
`; - } - } else { - resultHtml = `
✅ 操作成功
`; - } - } else { - resultHtml = `
❌ ${escapeHtml(tc.result.error || '未知错误')}
`; - } - } - return `
${icon} @@ -343,7 +248,6 @@ function renderToolCallCard(tc: ToolCallRecord): string { ${status}
${paramsHtml ? `
${paramsHtml}
` : ''} - ${resultHtml ? `
${resultHtml}
` : ''}
`; } @@ -403,19 +307,6 @@ export function updateLastAssistantMessage(content: string, think: string | null thinkBlock.querySelector('pre')!.textContent = think; } - if (stats) { - let statsDiv = lastMsg.querySelector('.msg-stats'); - if (!statsDiv) { - statsDiv = document.createElement('div'); - statsDiv.className = 'msg-stats'; - lastMsg.querySelector('.msg-body')!.appendChild(statsDiv); - } - const parts: string[] = []; - if (stats.eval_count != null && stats.eval_count > 0) parts.push(`${stats.eval_count} tokens`); - if (stats.total_duration != null && stats.total_duration > 0) parts.push(`${(stats.total_duration / 1e9).toFixed(1)}s`); - if (parts.length) statsDiv.innerHTML = parts.join(' · '); - } - scrollToBottom(); } @@ -507,18 +398,6 @@ export function enableAutoScroll(): void { if (scrollBtnEl) scrollBtnEl.style.display = 'none'; } -/** 统计当前会话总 token 消耗并更新 header 显示 */ -export function updateTotalTokens(): void { - const session = state.get(KEYS.CURRENT_SESSION); - const el = document.querySelector('#totalTokensValue'); - if (!el) return; - if (!session) { el.textContent = '0'; return; } - const total = session.messages - .filter(m => m.role === 'assistant' && m.eval_count) - .reduce((sum, m) => sum + (m.eval_count || 0), 0); - el.textContent = total > 0 ? total.toLocaleString() : '0'; -} - // ── 导出功能 ── async function nativeSaveFile(defaultName: string, content: string): Promise { diff --git a/src/renderer/components/history-modal.ts b/src/renderer/components/history-modal.ts index 3683075..04a5730 100644 --- a/src/renderer/components/history-modal.ts +++ b/src/renderer/components/history-modal.ts @@ -4,7 +4,7 @@ import { state, KEYS } from '../state/state.js'; import { debounce, escapeHtml, formatTime } from '../utils/utils.js'; -import { exportAsMarkdown, exportAsHtml, exportAsTxt, renderMessages, clearMessages, updateTotalTokens } from './chat-area.js'; +import { exportAsMarkdown, exportAsHtml, exportAsTxt, renderMessages, clearMessages } from './chat-area.js'; import { clearToolCardsExternal, clearTerminalExternal } from './workspace-panel.js'; import { showConfirm } from './prompt-modal.js'; import { ChatDB } from '../db/chat-db.js'; @@ -186,7 +186,6 @@ async function loadHistorySession(sessionId: string): Promise { clearMessages(); renderMessages(); - updateTotalTokens(); closeHistoryModal(); } diff --git a/src/renderer/components/input-area.ts b/src/renderer/components/input-area.ts index b881194..2957d6d 100644 --- a/src/renderer/components/input-area.ts +++ b/src/renderer/components/input-area.ts @@ -8,7 +8,7 @@ import { detectLanguage, truncate, escapeHtml, formatSize } from '../utils/utils import { getSelectedModel, isThinkEnabled, isVisionAvailable, isToolCallingSupported } from './model-bar.js'; import { renderMessages, clearMessagesDOM, appendAssistantPlaceholder, appendSystemMessage, - updateLastAssistantMessage, clearMessages, safeMarkdown, enableAutoScroll, updateTotalTokens, + updateLastAssistantMessage, clearMessages, safeMarkdown, enableAutoScroll, appendToolCallCardToPlaceholder, updateToolCallCardInPlaceholder, resetCurrentPlaceholder } from './chat-area.js'; import { showToast } from './toast.js'; @@ -371,7 +371,6 @@ async function handleRetry(): Promise { } renderMessages(); await saveCurrentSession(); - updateTotalTokens(); } }); } catch (err) { @@ -424,7 +423,6 @@ async function handleUndo(): Promise { clearMessagesDOM(); renderMessages(); - updateTotalTokens(); showToast('已撤销上一轮对话', 'success'); } @@ -503,7 +501,6 @@ async function handleCompress(): Promise { clearMessagesDOM(); renderMessages(); - updateTotalTokens(); logInfo(`上下文压缩完成: ${middle.length} 条 → 1 条摘要`); showToast(`已压缩 ${middle.length} 条消息为摘要`, 'success'); @@ -754,7 +751,6 @@ export async function sendMessage(): Promise { } await saveCurrentSession(); - updateTotalTokens(); // ── 自动提取记忆(非阻塞)── if (isMemoryEnabled() && freshSession.messages.length >= 10) { @@ -1096,7 +1092,6 @@ async function sendMessageWithAgentLoop(text: string, currentSession: ChatSessio } renderMessages(); await saveCurrentSession(); - updateTotalTokens(); } }); } catch (err) { diff --git a/src/renderer/index.html b/src/renderer/index.html index ae3b0d9..1945907 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -35,7 +35,6 @@ - 0
diff --git a/src/renderer/main.ts b/src/renderer/main.ts index 4547ca8..95831a2 100644 --- a/src/renderer/main.ts +++ b/src/renderer/main.ts @@ -15,7 +15,7 @@ import { initToast, showToast } from './components/toast.js'; import { initLightbox, closeLightbox } from './components/lightbox.js'; import { initHeader, checkConnection } from './components/header.js'; import { initModelBar, loadModels, setSelectedModel } from './components/model-bar.js'; -import { initChatArea, renderMessages, clearMessages, enableAutoScroll, updateTotalTokens } from './components/chat-area.js'; +import { initChatArea, renderMessages, clearMessages, enableAutoScroll } from './components/chat-area.js'; import { initInputArea } from './components/input-area.js'; import { initSettingsModal, closeSettingsModal } from './components/settings-modal.js'; import { initHistoryModal, closeHistoryModal } from './components/history-modal.js'; @@ -279,7 +279,6 @@ async function startNewSession(): Promise { clearMessages(); enableAutoScroll(); renderMessages(); - updateTotalTokens(); // ── 重置工作空间到空闲状态 ── clearToolCardsExternal(); @@ -403,7 +402,6 @@ async function init(): Promise { state.set('heartbeatInterval', heartbeatInterval); (document.querySelector('#chatInput') as HTMLElement)?.focus(); - updateTotalTokens(); logSuccess('应用初始化完成'); diff --git a/src/renderer/styles/style.css b/src/renderer/styles/style.css index f9454f1..d7573b6 100644 --- a/src/renderer/styles/style.css +++ b/src/renderer/styles/style.css @@ -374,28 +374,6 @@ html, body { opacity: 1; } -.total-tokens { - display: inline-flex; - align-items: center; - gap: 4px; - margin-left: 8px; - padding: 3px 10px; - font-size: 11px; - font-weight: 500; - color: var(--caution); - background: var(--caution-bg); - border: 1px solid rgba(212, 160, 60, 0.15); - border-radius: 20px; - white-space: nowrap; - user-select: none; -} - -.token-icon { - width: 14px; - height: 14px; - flex-shrink: 0; - color: var(--caution); -} /* ═══ 模型选择栏 ═══ */ .model-bar { @@ -1024,14 +1002,6 @@ html, body { color: var(--text-tertiary); } -.msg-stats .stat-tokens { - color: var(--caution); -} - -.msg-stats .stat-duration { - color: var(--accent); -} - /* ═══ 输入区域 ═══ */ .input-area { flex-shrink: 0; diff --git a/src/renderer/utils/utils.ts b/src/renderer/utils/utils.ts index 11bd006..8b39e08 100644 --- a/src/renderer/utils/utils.ts +++ b/src/renderer/utils/utils.ts @@ -11,7 +11,7 @@ export function generateId(): string { export function formatTime(ts: number): string { const d = new Date(ts); const pad = (n: number): string => String(n).padStart(2, '0'); - return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`; + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`; } /** 截断文本 */