1- import React from 'react' ;
1+ import React , { useState } from 'react' ;
22import { AnimatePresence } from 'framer-motion' ;
3- import { Heart , User , Bot , Loader2 , Paperclip , Send , Link , ExternalLink , X , MessageSquarePlus } from 'lucide-react' ;
3+ import { User , Bot , Loader2 , Paperclip , Send , Link , ExternalLink , X , Sparkles , Mic , PenLine , Image , Music , Languages , Presentation , MoreHorizontal } from 'lucide-react' ;
44import {
55 ChatContainer as ChatContainerStyled ,
6- Header ,
7- Title ,
8- Subtitle ,
96 MessagesContainer ,
107 MessageBubble ,
118 Avatar ,
@@ -19,9 +16,15 @@ import {
1916 SuggestionChip ,
2017 WelcomeMessage ,
2118 LoadingIndicator ,
22- InputContainer ,
23- InputRow ,
19+ InputContainer
20+ } from '../styles' ;
21+ import {
22+ InputWrapper ,
23+ InputBox ,
2424 MessageInput ,
25+ InputActions ,
26+ LeftActions ,
27+ RightActions ,
2528 AttachmentButton ,
2629 SendButton ,
2730 FileInput ,
@@ -31,13 +34,28 @@ import {
3134 RemoveAttachmentButton ,
3235 URLPreview ,
3336 URLText ,
34- URLButton
35- } from '../styles' ;
37+ URLButton ,
38+ FeatureButton ,
39+ QuickActions ,
40+ QuickActionButton
41+ } from '../styles/input' ;
3642import { emotionLabels } from '../constants/emotions' ;
3743import { formatTimestamp , formatFileSize } from '../utils/formatters' ;
3844import TypewriterComponent from './TypewriterText' ;
3945import { getFileIcon } from '../utils/fileUtils' ;
4046
47+ // 获取问候语
48+ const getGreeting = ( ) => {
49+ const hour = new Date ( ) . getHours ( ) ;
50+ if ( hour < 6 ) return '夜深了' ;
51+ if ( hour < 9 ) return '早上好' ;
52+ if ( hour < 12 ) return '上午好' ;
53+ if ( hour < 14 ) return '中午好' ;
54+ if ( hour < 18 ) return '下午好' ;
55+ if ( hour < 22 ) return '晚上好' ;
56+ return '夜深了' ;
57+ } ;
58+
4159const ChatContainer = ( {
4260 messages,
4361 isLoading,
@@ -59,49 +77,52 @@ const ChatContainer = ({
5977 onSuggestionClick,
6078 onOpenFeedbackModal
6179} ) => {
80+ const [ deepThinkActive , setDeepThinkActive ] = useState ( false ) ;
81+
82+ const handleKeyDown = ( e ) => {
83+ if ( e . key === 'Enter' && ! e . shiftKey ) {
84+ e . preventDefault ( ) ;
85+ onSendMessage ( ) ;
86+ }
87+ if ( onTabNavigation ) {
88+ onTabNavigation ( e ) ;
89+ }
90+ } ;
91+
6292 return (
6393 < ChatContainerStyled
64- initial = { { opacity : 0 , x : 20 } }
65- animate = { { opacity : 1 , x : 0 } }
66- transition = { { duration : 0.5 } }
94+ initial = { { opacity : 0 } }
95+ animate = { { opacity : 1 } }
96+ transition = { { duration : 0.3 } }
6797 >
68- < Header >
69- < Title >
70- < Heart size = { 24 } />
71- 情感聊天机器人
72- </ Title >
73- < Subtitle > 温暖陪伴,理解倾听</ Subtitle >
74- </ Header >
75-
7698 < MessagesContainer >
7799 < AnimatePresence initial = { false } >
78100 { messages . length === 0 ? (
79101 < WelcomeMessage
80102 key = "welcome"
81- initial = { { opacity : 0 } }
82- animate = { { opacity : 1 } }
103+ initial = { { opacity : 0 , y : 20 } }
104+ animate = { { opacity : 1 , y : 0 } }
83105 exit = { { opacity : 0 } }
84- transition = { { delay : 0.5 } }
106+ transition = { { delay : 0.2 } }
85107 >
86- < h3 > 👋 你好!我是你的情感支持伙伴 </ h3 >
108+ < h3 > { getGreeting ( ) } ,小草 </ h3 >
87109 < p >
88- 我在这里倾听你的心声,理解你的感受。< br />
89- 无论是开心、难过、焦虑还是困惑,我都愿意陪伴你。< br />
90- 请随意分享你的想法和感受吧!
110+ 我是你的情感支持伙伴,随时倾听你的心声。< br />
111+ 无论开心、难过还是困惑,都可以和我聊聊。
91112 </ p >
92113 </ WelcomeMessage >
93114 ) : (
94115 messages . map ( ( message ) => (
95116 < MessageBubble
96117 key = { message . id }
97118 isUser = { message . role === 'user' }
98- initial = { { opacity : 0 , y : 20 } }
119+ initial = { { opacity : 0 , y : 15 } }
99120 animate = { { opacity : 1 , y : 0 } }
100- exit = { { opacity : 0 , y : - 20 } }
101- transition = { { duration : 0.3 } }
121+ exit = { { opacity : 0 } }
122+ transition = { { duration : 0.2 } }
102123 >
103124 < Avatar isUser = { message . role === 'user' } >
104- { message . role === 'user' ? < User size = { 20 } /> : < Bot size = { 20 } /> }
125+ { message . role === 'user' ? < User size = { 18 } /> : < Bot size = { 18 } /> }
105126 </ Avatar >
106127 < MessageWrapper >
107128 < MessageContent
@@ -111,9 +132,9 @@ const ChatContainer = ({
111132 { message . role === 'assistant' && ! message . isHistory ? (
112133 < TypewriterComponent
113134 text = { message . content }
114- speed = { message . emotion === 'sad' ? 40 : message . emotion === 'angry' ? 20 : message . emotion === 'happy' ? 25 : 30 }
135+ speed = { 30 }
115136 showCursor = { true }
116- cursorColor = { message . emotion === 'sad' ? '#74b9ff' : message . emotion === 'angry' ? '#ff7675' : message . emotion === 'happy' ? '#00b894' : '#333' }
137+ cursorColor = "#6366f1"
117138 isUser = { false }
118139 />
119140 ) : (
@@ -132,10 +153,9 @@ const ChatContainer = ({
132153 < FeedbackButtons >
133154 < FeedbackButton
134155 onClick = { ( ) => onOpenFeedbackModal ( message ) }
135- whileHover = { { scale : 1.05 } }
136- whileTap = { { scale : 0.95 } }
156+ whileHover = { { scale : 1.02 } }
157+ whileTap = { { scale : 0.98 } }
137158 >
138- < MessageSquarePlus size = { 14 } />
139159 反馈
140160 </ FeedbackButton >
141161 </ FeedbackButtons >
@@ -150,9 +170,9 @@ const ChatContainer = ({
150170 < LoadingIndicator
151171 initial = { { opacity : 0 , y : 10 } }
152172 animate = { { opacity : 1 , y : 0 } }
153- exit = { { opacity : 0 , y : - 10 } }
173+ exit = { { opacity : 0 } }
154174 >
155- < Loader2 size = { 18 } className = "spinner" />
175+ < Loader2 size = { 16 } className = "spinner" />
156176 < span > 正在思考中</ span >
157177 < span className = "dots" >
158178 < span > .</ span >
@@ -168,10 +188,10 @@ const ChatContainer = ({
168188 { suggestions . map ( ( suggestion , index ) => (
169189 < SuggestionChip
170190 key = { index }
171- initial = { { opacity : 0 , scale : 0.8 } }
191+ initial = { { opacity : 0 , scale : 0.9 } }
172192 animate = { { opacity : 1 , scale : 1 } }
173- exit = { { opacity : 0 , scale : 0.8 } }
174- transition = { { delay : index * 0.1 } }
193+ exit = { { opacity : 0 , scale : 0.9 } }
194+ transition = { { delay : index * 0.05 } }
175195 onClick = { ( ) => onSuggestionClick ( suggestion ) }
176196 >
177197 { suggestion }
@@ -192,7 +212,7 @@ const ChatContainer = ({
192212 animate = { { opacity : 1 , y : 0 } }
193213 exit = { { opacity : 0 , y : - 10 } }
194214 >
195- < Link size = { 16 } />
215+ < Link size = { 14 } />
196216 < URLText > { detectedURLs [ 0 ] } </ URLText >
197217 < URLButton onClick = { ( ) => window . open ( detectedURLs [ 0 ] , '_blank' ) } >
198218 < ExternalLink size = { 14 } />
@@ -207,15 +227,15 @@ const ChatContainer = ({
207227 { attachments . map ( ( attachment ) => (
208228 < AttachmentItem
209229 key = { attachment . id }
210- initial = { { opacity : 0 , scale : 0.8 } }
230+ initial = { { opacity : 0 , scale : 0.9 } }
211231 animate = { { opacity : 1 , scale : 1 } }
212- exit = { { opacity : 0 , scale : 0.8 } }
232+ exit = { { opacity : 0 , scale : 0.9 } }
213233 >
214234 < AttachmentIcon >
215235 { getFileIcon ( attachment . type ) }
216236 </ AttachmentIcon >
217237 < span > { attachment . name } </ span >
218- < span > ({ formatFileSize ( attachment . size ) } )</ span >
238+ < span style = { { color : '#999' } } > ({ formatFileSize ( attachment . size ) } )</ span >
219239 < RemoveAttachmentButton
220240 onClick = { ( ) => onRemoveAttachment ( attachment . id ) }
221241 >
@@ -227,45 +247,62 @@ const ChatContainer = ({
227247 </ AttachmentsPreview >
228248 ) }
229249
230- < InputRow >
231- < MessageInput
232- ref = { inputRef }
233- type = "text"
234- value = { inputValue }
235- onChange = { onInputChange }
236- onKeyPress = { onKeyPress }
237- onKeyDown = { onTabNavigation }
238- placeholder = "分享你的想法和感受..."
239- disabled = { isLoading }
240- aria-label = "消息输入框"
241- aria-describedby = "input-hint"
242- />
243- < AttachmentButton
244- ref = { attachmentButtonRef }
245- onClick = { ( ) => fileInputRef . current ?. click ( ) }
246- disabled = { isLoading }
247- onKeyDown = { onTabNavigation }
248- whileHover = { { scale : 1.05 } }
249- whileTap = { { scale : 0.95 } }
250- aria-label = "添加附件"
251- title = "添加附件 (图片、PDF、文档)"
252- >
253- < Paperclip size = { 20 } />
254- </ AttachmentButton >
255- < SendButton
256- ref = { sendButtonRef }
257- onClick = { onSendMessage }
258- disabled = { ( ! inputValue . trim ( ) && attachments . length === 0 ) || isLoading }
259- onKeyDown = { onTabNavigation }
260- whileHover = { { scale : 1.05 } }
261- whileTap = { { scale : 0.95 } }
262- aria-label = "发送消息"
263- aria-disabled = { ( ! inputValue . trim ( ) && attachments . length === 0 ) || isLoading }
264- title = "发送消息 (Enter)"
265- >
266- < Send size = { 20 } />
267- </ SendButton >
268- </ InputRow >
250+ < InputWrapper >
251+ < InputBox >
252+ < MessageInput
253+ ref = { inputRef }
254+ value = { inputValue }
255+ onChange = { onInputChange }
256+ onKeyDown = { handleKeyDown }
257+ placeholder = "发消息或输入 / 选择技能"
258+ disabled = { isLoading }
259+ rows = { 1 }
260+ />
261+ < InputActions >
262+ < LeftActions >
263+ < AttachmentButton
264+ ref = { attachmentButtonRef }
265+ onClick = { ( ) => fileInputRef . current ?. click ( ) }
266+ disabled = { isLoading }
267+ whileHover = { { scale : 1.05 } }
268+ whileTap = { { scale : 0.95 } }
269+ title = "添加附件"
270+ >
271+ < Paperclip size = { 18 } />
272+ </ AttachmentButton >
273+ < FeatureButton
274+ active = { deepThinkActive }
275+ onClick = { ( ) => setDeepThinkActive ( ! deepThinkActive ) }
276+ whileHover = { { scale : 1.02 } }
277+ whileTap = { { scale : 0.98 } }
278+ >
279+ < Sparkles size = { 14 } />
280+ 深度思考
281+ </ FeatureButton >
282+ </ LeftActions >
283+ < RightActions >
284+ < AttachmentButton
285+ whileHover = { { scale : 1.05 } }
286+ whileTap = { { scale : 0.95 } }
287+ title = "语音输入"
288+ >
289+ < Mic size = { 18 } />
290+ </ AttachmentButton >
291+ < SendButton
292+ ref = { sendButtonRef }
293+ onClick = { onSendMessage }
294+ disabled = { ( ! inputValue . trim ( ) && attachments . length === 0 ) || isLoading }
295+ whileHover = { { scale : 1.05 } }
296+ whileTap = { { scale : 0.95 } }
297+ title = "发送消息"
298+ >
299+ < Send size = { 16 } />
300+ </ SendButton >
301+ </ RightActions >
302+ </ InputActions >
303+ </ InputBox >
304+
305+ </ InputWrapper >
269306
270307 < FileInput
271308 ref = { fileInputRef }
0 commit comments