From a213cddf55645b0d3d86c115a418f48459161e7e Mon Sep 17 00:00:00 2001
From: gaoziman <2942894660@qq.com>
Date: Thu, 18 Dec 2025 11:29:52 +0800
Subject: [PATCH] =?UTF-8?q?refactor(features):=20=E4=BC=98=E5=8C=96?=
=?UTF-8?q?=E8=81=8A=E5=A4=A9=E8=BE=93=E5=85=A5=E5=92=8C=E6=B6=88=E6=81=AF?=
=?UTF-8?q?=E6=B0=94=E6=B3=A1=E7=BB=84=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
ChatInput:
- 修复中文输入法回车误发送问题
- 移除未使用的历史记录按钮
MessageBubble:
- 集成 MarkdownRenderer 实现富文本渲染
- 添加 AI 思考内容折叠展示
- 添加流式输出状态和错误提示
- 优化复制功能添加成功反馈
---
src/components/features/ChatInput.tsx | 14 +-
src/components/features/MessageBubble.tsx | 163 ++++++++++++----------
2 files changed, 91 insertions(+), 86 deletions(-)
diff --git a/src/components/features/ChatInput.tsx b/src/components/features/ChatInput.tsx
index 27352a6..7a61433 100644
--- a/src/components/features/ChatInput.tsx
+++ b/src/components/features/ChatInput.tsx
@@ -1,7 +1,7 @@
'use client';
import { useState } from 'react';
-import { Plus, Clock, ArrowUp } from 'lucide-react';
+import { Plus, ArrowUp } from 'lucide-react';
import { ModelSelector } from './ModelSelector';
import { ToolsDropdown } from './ToolsDropdown';
import { cn } from '@/lib/utils';
@@ -40,7 +40,9 @@ export function ChatInput({
};
const handleKeyDown = (e: React.KeyboardEvent) => {
- if (e.key === 'Enter' && !e.shiftKey) {
+ // 检查是否正在使用输入法组合(如中文输入法选词时按回车)
+ // isComposing 为 true 时,表示用户正在用输入法选词,此时不应发送消息
+ if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) {
e.preventDefault();
handleSend();
}
@@ -85,14 +87,6 @@ export function ChatInput({
onToolToggle={onToolToggle}
onEnableAllToggle={onEnableAllTools}
/>
-
- {/* 历史记录 */}
-
{/* 右侧按钮 */}
diff --git a/src/components/features/MessageBubble.tsx b/src/components/features/MessageBubble.tsx
index 395b8d4..9d922d5 100644
--- a/src/components/features/MessageBubble.tsx
+++ b/src/components/features/MessageBubble.tsx
@@ -1,47 +1,35 @@
'use client';
-import { Copy, ThumbsUp, ThumbsDown, RefreshCw } from 'lucide-react';
+import { useState } from 'react';
+import { Copy, ThumbsUp, ThumbsDown, RefreshCw, ChevronDown, ChevronUp, Brain, Loader2, AlertCircle, Check } from 'lucide-react';
import { Avatar } from '@/components/ui/Avatar';
import { AILogo } from '@/components/ui/AILogo';
+import { MarkdownRenderer } from '@/components/markdown/MarkdownRenderer';
import { cn } from '@/lib/utils';
import type { Message, User } from '@/types';
interface MessageBubbleProps {
message: Message;
user?: User;
+ thinkingContent?: string;
+ isStreaming?: boolean;
+ error?: string;
}
-export function MessageBubble({ message, user }: MessageBubbleProps) {
+export function MessageBubble({ message, user, thinkingContent, isStreaming, error }: MessageBubbleProps) {
const isUser = message.role === 'user';
+ const [thinkingExpanded, setThinkingExpanded] = useState(false);
+ const [copied, setCopied] = useState(false);
- // 简单的 Markdown 渲染
- const renderContent = (content: string) => {
- // 处理代码块
- const parts = content.split(/(`[^`]+`)/g);
- return parts.map((part, index) => {
- if (part.startsWith('`') && part.endsWith('`')) {
- return (
-
- {part.slice(1, -1)}
-
- );
- }
- // 处理粗体
- const boldParts = part.split(/(\*\*[^*]+\*\*)/g);
- return boldParts.map((boldPart, boldIndex) => {
- if (boldPart.startsWith('**') && boldPart.endsWith('**')) {
- return (
-
- {boldPart.slice(2, -2)}
-
- );
- }
- return {boldPart};
- });
- });
+ // 复制消息内容
+ const handleCopy = async () => {
+ try {
+ await navigator.clipboard.writeText(message.content);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ } catch (error) {
+ console.error('Failed to copy:', error);
+ }
};
if (isUser) {
@@ -66,55 +54,78 @@ export function MessageBubble({ message, user }: MessageBubbleProps) {
{/* 消息内容 */}
- {renderContent(paragraph)} -
- ); - })} + {/* 思考内容 */} + {thinkingContent && ( +
+ {thinkingContent}
+
+