From 8b426ed71d5c5efe774625da389accb850f22bd0 Mon Sep 17 00:00:00 2001 From: zhayujie Date: Thu, 16 Jul 2026 19:31:56 +0800 Subject: [PATCH] feat: optimize desktop client style - light theme colors, fix chat cancel logic, add clear-context feedback, polish thinking step icon/order, and align channels empty state with the web console --- .../renderer/src/components/MessageBubble.tsx | 9 ++++- .../renderer/src/components/MessageSteps.tsx | 7 ++-- desktop/src/renderer/src/i18n.ts | 12 ++++++- desktop/src/renderer/src/index.css | 12 +++---- .../src/renderer/src/pages/ChannelsPage.tsx | 21 +++++++++++- desktop/src/renderer/src/pages/ChatPage.tsx | 34 +++++++++++++------ 6 files changed, 73 insertions(+), 22 deletions(-) diff --git a/desktop/src/renderer/src/components/MessageBubble.tsx b/desktop/src/renderer/src/components/MessageBubble.tsx index 40b64507..0ba332ce 100644 --- a/desktop/src/renderer/src/components/MessageBubble.tsx +++ b/desktop/src/renderer/src/components/MessageBubble.tsx @@ -121,8 +121,15 @@ const MessageBubble: React.FC = ({ message, onRegenerate, on muted, separated from the final answer by a dashed divider. */} {(hasSteps || hasLiveReasoning) && (
- {hasLiveReasoning && } {hasSteps && } + {/* Live reasoning is the current, not-yet-committed thinking, so it + must render after all committed steps (tools/thinking), not at + the very top of the bubble. */} + {hasLiveReasoning && ( +
+ +
+ )}
)} diff --git a/desktop/src/renderer/src/components/MessageSteps.tsx b/desktop/src/renderer/src/components/MessageSteps.tsx index 176e8150..d03a70b8 100644 --- a/desktop/src/renderer/src/components/MessageSteps.tsx +++ b/desktop/src/renderer/src/components/MessageSteps.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react' -import { ChevronRight, Loader2, Check, X, Brain } from 'lucide-react' +import { ChevronRight, Loader2, Check, X, Lightbulb } from 'lucide-react' import type { MessageStep } from '../types' +import { t } from '../i18n' import Markdown from './Markdown' /** @@ -16,8 +17,8 @@ const ThinkingStep: React.FC<{ content: string; streaming?: boolean }> = ({ cont className="flex items-center gap-1.5 cursor-pointer hover:text-content-secondary select-none transition-colors" onClick={() => setExpanded((v) => !v)} > - - {streaming ? 'Thinking…' : 'Thought for a moment'} + + {streaming ? t('thinking_in_progress') : t('thinking_done')} {expanded && ( diff --git a/desktop/src/renderer/src/i18n.ts b/desktop/src/renderer/src/i18n.ts index 5d6669e2..bc836674 100644 --- a/desktop/src/renderer/src/i18n.ts +++ b/desktop/src/renderer/src/i18n.ts @@ -129,14 +129,17 @@ const translations: Record> = { msg_cancelled: '已中止', msg_self_learned: '自主学习', msg_stop: '停止', + thinking_in_progress: '思考中…', + thinking_done: '已深度思考', chat_clear_context: '清除上下文', + context_cleared: '— 以上内容已从上下文中移除 —', chat_load_earlier: '加载更早的消息', chat_send: '发送', chat_attach: '添加附件', slash_hint: '输入 / 查看命令', chat_welcome: '有什么可以帮你的?', chat_empty_hint: '发送一条消息开始对话', - welcome_subtitle: '我可以帮你解答问题、管理你的电脑、创建并执行技能,\n还能通过长期记忆不断成长。', + welcome_subtitle: '我可以帮你解决问题、管理你的电脑、创建并执行技能,\n还能通过长期记忆不断成长。', example_sys_title: '系统管理', example_sys_text: '查看工作空间里有哪些文件', example_task_title: '定时任务', @@ -288,6 +291,8 @@ const translations: Record> = { channels_connected_section: '已连接', channels_available_section: '可添加', channels_empty_connected: '暂无已连接的通道', + channels_empty: '暂未接入任何通道', + channels_empty_desc: '点击右上角「接入通道」按钮,即可将 CowAgent 接入微信、飞书、钉钉等消息通道', channels_qr_hint: '该通道通过扫码登录,请前往 Web 控制台完成扫码连接', channels_save_ok: '已保存', channels_save_error: '保存失败', @@ -512,7 +517,10 @@ const translations: Record> = { msg_cancelled: 'Cancelled', msg_self_learned: 'Self-learned', msg_stop: 'Stop', + thinking_in_progress: 'Thinking…', + thinking_done: 'Thought', chat_clear_context: 'Clear context', + context_cleared: '— Context above has been cleared —', chat_load_earlier: 'Load earlier messages', chat_send: 'Send', chat_attach: 'Attach file', @@ -671,6 +679,8 @@ const translations: Record> = { channels_connected_section: 'Connected', channels_available_section: 'Available', channels_empty_connected: 'No connected channels yet', + channels_empty: 'No channels connected', + channels_empty_desc: 'Click "Add channel" above to connect CowAgent to WeChat, Feishu, DingTalk and more', channels_qr_hint: 'This channel uses QR login — please connect it from the Web console', channels_save_ok: 'Saved', channels_save_error: 'Failed to save', diff --git a/desktop/src/renderer/src/index.css b/desktop/src/renderer/src/index.css index 43aaaa2b..cf651243 100644 --- a/desktop/src/renderer/src/index.css +++ b/desktop/src/renderer/src/index.css @@ -27,21 +27,21 @@ --danger-border: rgba(239, 68, 68, 0.3); --info: #3b82f6; - /* Light theme — aligned with the web console (Tailwind gray/slate palette) */ + /* Light theme — aligned with the web console: gray surfaces/borders + slate text */ --bg-base: #f9fafb; /* app background (gray-50) */ --bg-surface: #ffffff; /* panels, cards */ - --bg-surface-2: #f1f5f9; /* nested surfaces, hover fills (slate-100) */ + --bg-surface-2: #f3f4f6; /* nested surfaces, hover fills (gray-100) */ --bg-elevated: #ffffff; /* popovers, menus, modals */ - --bg-inset: #f1f5f9; /* inputs, code blocks (slate-100) */ + --bg-inset: #f3f4f6; /* inputs, code blocks (gray-100) */ --text-primary: #1e293b; /* headings, primary text (slate-800) */ --text-secondary: #475569; /* body, labels (slate-600) */ --text-tertiary: #64748b; /* hints, captions (slate-500) */ --text-disabled: #94a3b8; /* slate-400 */ - --border-default: #e2e8f0; /* slate-200 */ - --border-strong: #cbd5e1; /* slate-300 */ - --border-subtle: #f1f5f9; /* slate-100 */ + --border-default: #e5e7eb; /* gray-200 (web console border) */ + --border-strong: #d1d5db; /* gray-300 */ + --border-subtle: #f3f4f6; /* gray-100 */ --overlay: rgba(0, 0, 0, 0.4); --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 3px rgba(0, 0, 0, 0.06); diff --git a/desktop/src/renderer/src/pages/ChannelsPage.tsx b/desktop/src/renderer/src/pages/ChannelsPage.tsx index a956eaf9..73bce126 100644 --- a/desktop/src/renderer/src/pages/ChannelsPage.tsx +++ b/desktop/src/renderer/src/pages/ChannelsPage.tsx @@ -13,6 +13,7 @@ import { Headset, Hash, AtSign, + RadioTower, } from 'lucide-react' import { t, localizedLabel } from '../i18n' import apiClient from '../api/client' @@ -147,7 +148,25 @@ const ChannelsPage: React.FC = ({ baseUrl }) => { ) : (
{connected.length === 0 && !addOpen ? ( -

{t('channels_empty_connected')}

+
+ + + +

{t('channels_empty')}

+

+ {t('channels_empty_desc')} +

+ {available.length > 0 && ( +
+ + + + {t('channels_add')} + + +
+ )} +
) : ( connected.map((ch) => ) )} diff --git a/desktop/src/renderer/src/pages/ChatPage.tsx b/desktop/src/renderer/src/pages/ChatPage.tsx index b27d1b10..885da3ac 100644 --- a/desktop/src/renderer/src/pages/ChatPage.tsx +++ b/desktop/src/renderer/src/pages/ChatPage.tsx @@ -278,16 +278,30 @@ const ChatPage: React.FC = ({ baseUrl }) => {
) : (
- {messages.map((msg) => ( - - ))} + {messages.map((msg) => + msg.kind === 'divider' ? ( +
+ + {t('context_cleared')} + +
+ ) : ( + + ) + )}
)}