From 229af563f799c9e9daad503d5353b6d29e7f3b34 Mon Sep 17 00:00:00 2001
From: hongjli <3117313295@qq.com>
Date: 星期五, 25 四月 2025 14:35:11 +0800
Subject: [PATCH] 聊天页面优化

---
 src/app/chat/page.tsx |  612 ++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 469 insertions(+), 143 deletions(-)

diff --git a/src/app/chat/page.tsx b/src/app/chat/page.tsx
index 21ff564..1301f70 100644
--- a/src/app/chat/page.tsx
+++ b/src/app/chat/page.tsx
@@ -11,7 +11,7 @@
 
 interface Message {
   role: 'user' | 'assistant';
-  content: string;
+  content: string | null;
   timestamp: number;
   id: string;
   conversation_id?: string;
@@ -48,8 +48,20 @@
   // 娣诲姞涓�涓柊鐨勭姸鎬佹潵鎺у埗娑堟伅鐨勬樉绀�
   const [showMessages, setShowMessages] = useState(false);
 
+  // 鍦ㄧ粍浠堕《閮ㄥ鍔犱竴涓己鍒舵洿鏂拌鏁板櫒
+  const [forceUpdateCounter, setForceUpdateCounter] = useState(0);
+
+  // 娣诲姞鐘舵�佹潵鎺у埗鎬濊�冨唴瀹圭殑鏄剧ず/闅愯棌
+  const [expandedThinkMessages, setExpandedThinkMessages] = useState<Record<string, boolean>>({});
+
+  // 鍦ㄧ粍浠堕《閮ㄦ坊鍔犳樉绀�/闅愯棌瀵嗙爜鐨勭姸鎬�
+  const [showApiKey, setShowApiKey] = useState(false);
+
   const messagesEndRef = useRef<HTMLDivElement>(null);
   const errorTimeoutRef = useRef<any>(null);
+
+  // 鍦ㄧ粍浠堕《閮ㄦ坊鍔犱竴涓紩鐢紝鐢ㄤ簬璺熻釜缁勪欢鏄惁宸插嵏杞�
+  const isMountedRef = useRef(true);
 
   useEffect(() => {
     // 鑾峰彇瀛樺偍鐨凙PI Key
@@ -98,6 +110,22 @@
       }
     };
     return cleanup;
+  }, []);
+
+  // 娣诲姞缁勪欢鍗歌浇鏃剁殑娓呯悊宸ヤ綔
+  useEffect(() => {
+    // 缁勪欢鎸傝浇鏃讹紝璁剧疆涓簍rue
+    isMountedRef.current = true;
+    
+    // 缁勪欢鍗歌浇鏃讹紝璁剧疆涓篺alse
+    return () => {
+      isMountedRef.current = false;
+      
+      // 娓呴櫎鎵�鏈夊畾鏃跺櫒
+      if (errorTimeoutRef.current) {
+        clearTimeout(errorTimeoutRef.current);
+      }
+    };
   }, []);
 
   const handleApiKeySubmit = () => {
@@ -149,6 +177,8 @@
     setCurrentMessageId(assistantMessage.id);
     setMessage('');
 
+    let controller: AbortController | null = new AbortController();
+    
     try {
       const response = await fetch(`${BASE_URL}/v1/chat-messages`, {
         method: 'POST',
@@ -164,112 +194,289 @@
           conversation_id: conversationId || '',
           user: 'abc-123',
           files: []
-        })
+        }),
+        signal: controller.signal // 娣诲姞涓淇″彿
       });
 
+      console.log("API鍝嶅簲鐘舵��:", response.status, response.statusText);
+      
       if (!response.ok) {
         const errorText = await response.text();
+        console.error("API閿欒鍝嶅簲:", errorText);
         let errorMessage;
         try {
           const errorJson = JSON.parse(errorText);
-          errorMessage = errorJson.error || `Error: ${response.status}`;
+          errorMessage = errorJson.error || `閿欒: ${response.status}`;
         } catch {
-          errorMessage = `Error: ${response.status}`;
+          errorMessage = `閿欒: ${response.status}`;
         }
         throw new Error(errorMessage);
       }
 
-      const reader = response.body?.getReader();
-      if (!reader) throw new Error('No reader available');
+      // 纭繚response.body瀛樺湪
+      if (!response.body) {
+        throw new Error('鍝嶅簲娌℃湁鎻愪緵鏁版嵁娴�');
+      }
 
+      const reader = response.body.getReader();
       const decoder = new TextDecoder();
       let buffer = '';
 
       while (true) {
-        const { done, value } = await reader.read();
-        if (done) {
-          setIsMessageComplete(true);
-          break;
-        }
+        try {
+          const { done, value } = await reader.read();
+          if (done) {
+            console.log("娴佸紡鍝嶅簲鎺ユ敹瀹屾瘯");
+            setIsMessageComplete(true);
+            break;
+          }
 
-        buffer += decoder.decode(value, { stream: true });
-        const lines = buffer.split('\n');
-        buffer = lines.pop() || '';
+          const chunk = decoder.decode(value, { stream: true });
+          console.log("鎺ユ敹鍒版暟鎹潡:", chunk);
+          buffer += chunk;
+          const lines = buffer.split('\n');
+          buffer = lines.pop() || '';
 
-        for (const line of lines) {
-          if (line.trim() === '') continue;
-          if (line.startsWith('data: ')) {
-            try {
-              const data = JSON.parse(line.slice(6));
-              
-              // 蹇界暐ping浜嬩欢
-              if (data.event === 'ping') continue;
+          for (const line of lines) {
+            if (line.trim() === '') continue;
+            
+            console.log("澶勭悊琛屾暟鎹�:", line);
+            
+            if (line.startsWith('data: ')) {
+              try {
+                const jsonStr = line.slice(6);
+                console.log("瀹屾暣鍘熷鏁版嵁:", line);
+                console.log("瑙f瀽JSON瀛楃涓�:", jsonStr);
+                const data = JSON.parse(jsonStr);
+                console.log("瑙f瀽鍚庣殑鏁版嵁瀵硅薄:", data);
+                
+                // 蹇界暐ping浜嬩欢
+                if (data.event === 'ping') {
+                  console.log("蹇界暐ping浜嬩欢");
+                  continue;
+                }
 
-              switch (data.event) {
-                case 'message':
-                  setMessages(prev => {
-                    const newMessages = [...prev];
-                    const lastMessage = newMessages[newMessages.length - 1];
-                    if (lastMessage?.role === 'assistant' && lastMessage.id === currentMessageId) {
-                      lastMessage.content = data.answer || lastMessage.content;
-                      if (data.message_id) {
-                        lastMessage.id = data.message_id;
-                        setCurrentMessageId(data.message_id);
+                switch (data.event) {
+                  case 'message':
+                    console.log(`鏀跺埌message浜嬩欢:`, data);
+                    
+                    // 鎻愬彇message鏁版嵁
+                    const messageId = data.message_id;
+                    const answerChunk = data.answer || '';
+                    const convId = data.conversation_id;
+                    
+                    // 濡傛灉涓嶅湪寰幆涓紝鐩存帴璋冪敤鏇存柊鍑芥暟
+                    handleMessageChunk(messageId, answerChunk, convId);
+                    break;
+
+                  case 'message_end':
+                    console.log('鏀跺埌message_end浜嬩欢:', data);
+                    
+                    // 妫�鏌ユ槸鍚︽湁鏈�缁堢瓟妗�
+                    if (data.metadata && data.id) {
+                      setIsMessageComplete(true);
+                      setConversationId(data.conversation_id || null);
+                    }
+                    break;
+                    
+                  case 'workflow_finished':
+                    console.log('宸ヤ綔娴佸畬鎴�:', data);
+                    
+                    // 妫�鏌ュ伐浣滄祦杈撳嚭鏄惁鏈夌瓟妗�
+                    if (data.data && data.data.outputs && data.data.outputs.answer) {
+                      // 濡傛灉宸ヤ綔娴佹湁鏈�缁堢瓟妗堬紝鏇存柊娑堟伅鍐呭
+                      const finalAnswer = data.data.outputs.answer;
+                      const messageId = data.message_id;
+                      
+                      if (finalAnswer) {
+                        console.log('浠庡伐浣滄祦鑾峰彇鏈�缁堢瓟妗�:', finalAnswer);
+                        handleFinalAnswer(messageId, finalAnswer, data.conversation_id);
                       }
                     }
-                    return newMessages;
-                  });
-                  break;
-
-                case 'message_end':
-                  if (data.conversation_id) {
-                    setConversationId(data.conversation_id);
-                    setMessages(prev => {
-                      const newMessages = [...prev];
-                      const lastMessage = newMessages[newMessages.length - 1];
-                      if (lastMessage?.role === 'assistant' && lastMessage.id === currentMessageId) {
-                        lastMessage.conversation_id = data.conversation_id;
-                        if (data.metadata) {
-                          lastMessage.metadata = data.metadata;
-                        }
+                    break;
+                    
+                  case 'node_finished':
+                    // 妫�鏌ヨ妭鐐圭被鍨嬫槸鍚︿负answer鑺傜偣
+                    if (data.data && data.data.node_type === 'answer' && data.data.outputs && data.data.outputs.answer) {
+                      console.log('浠巃nswer鑺傜偣鑾峰彇绛旀:', data.data.outputs.answer);
+                      const answer = data.data.outputs.answer;
+                      if (answer) {
+                        handleFinalAnswer(data.message_id, answer, data.conversation_id);
                       }
-                      return newMessages;
-                    });
-                  }
-                  setIsMessageComplete(true);
-                  break;
-
-                case 'error':
-                  console.error('Error event received:', data);
-                  setIsMessageComplete(true);
-                  throw new Error(data.message || '鍙戦�佹秷鎭椂鍑洪敊');
-              }
-            } catch (e) {
-              console.error('Error parsing SSE data:', e);
-              if (!messages[messages.length - 1]?.content) {
+                    }
+                    break;
+                    
+                  // 澶勭悊鍏朵粬宸ヤ綔娴佷簨浠�
+                  case 'workflow_started':
+                  case 'node_started':
+                    // 杩欎簺浜嬩欢鍙渶璁板綍锛屼笉闇�瑕佹洿鏂癠I
+                    console.log(`宸ヤ綔娴佷簨浠� ${data.event}:`, data);
+                    break;
+                    
+                  case 'error':
+                    console.error('鏈嶅姟鍣ㄨ繑鍥為敊璇簨浠�:', data);
+                    setIsMessageComplete(true);
+                    throw new Error(data.message || '鍙戦�佹秷鎭椂鍑洪敊');
+                }
+              } catch (e) {
+                console.error('瑙f瀽SSE鏁版嵁鍑洪敊:', e, '鍘熷琛�:', line);
                 setIsMessageComplete(true);
                 throw e;
               }
             }
           }
+        } catch (err) {
+          console.error('澶勭悊娴佸紡鍝嶅簲鏃跺嚭閿�:', err);
+          throw err;
         }
       }
     } catch (err) {
-      console.error('Chat error:', err);
-      showErrorMessage(err instanceof Error ? err.message : '鍙戦�佹秷鎭椂鍑洪敊');
+      // 妫�鏌ユ槸鍚︽槸涓閿欒
+      if (err instanceof Error && err.name === 'AbortError') {
+        console.log('璇锋眰琚腑姝紝鍙兘鏄粍浠跺嵏杞藉鑷寸殑');
+        return; // 涓閿欒涓嶉渶瑕佹樉绀虹粰鐢ㄦ埛
+      }
       
-      setMessages(prev => {
-        const newMessages = [...prev];
-        const lastMessage = newMessages[newMessages.length - 1];
-        if (lastMessage?.role === 'assistant') {
-          lastMessage.content = '鎶辨瓑锛屾秷鎭彂閫佸け璐ワ紝璇风◢鍚庨噸璇曘��';
-        }
-        return newMessages;
-      });
+      console.error('鑱婂ぉ璇锋眰閿欒:', err);
+      let errorMsg = err instanceof Error ? err.message : '鍙戦�佹秷鎭椂鍑洪敊';
+      
+      // 鍙湁鍦ㄧ粍浠朵粛鐒舵寕杞芥椂鎵嶆洿鏂癠I
+      if (isMountedRef.current) {
+        showErrorMessage(errorMsg);
+        
+        setMessages(prev => {
+          const newMessages = [...prev];
+          const lastMessage = newMessages[newMessages.length - 1];
+          if (lastMessage?.role === 'assistant') {
+            lastMessage.content = `鎶辨瓑锛屾棤娉曡幏鍙栧洖澶�: ${errorMsg}`;
+          }
+          return newMessages;
+        });
+      }
     } finally {
-      setIsStreaming(false);
+      // 娓呴櫎鎺у埗鍣�
+      controller = null;
+      
+      // 鍙湁鍦ㄧ粍浠朵粛鐒舵寕杞芥椂鎵嶆洿鏂癠I
+      if (isMountedRef.current) {
+        setIsStreaming(false);
+        setIsMessageComplete(true);
+      }
     }
   };
+
+  // 淇敼handleMessageChunk鍑芥暟锛屾坊鍔犵粍浠舵寕杞芥鏌�
+  const handleMessageChunk = useCallback((messageId: string, answerChunk: string, convId?: string) => {
+    // 濡傛灉缁勪欢宸插嵏杞斤紝鍒欎笉鎵ц鏇存柊
+    if (!isMountedRef.current) return;
+    
+    console.log(`澶勭悊娑堟伅鐗囨: ID=${messageId}, 鐗囨="${answerChunk}"`);
+    
+    // 浣跨敤鍑芥暟寮忔洿鏂扮‘淇濊幏鍙栨渶鏂扮姸鎬�
+    setMessages(prevMessages => {
+      // 濡傛灉缁勪欢宸插嵏杞斤紝鍒欎笉鎵ц鏇存柊
+      if (!isMountedRef.current) return prevMessages;
+      
+      // 鍒涘缓娑堟伅鏁扮粍鐨勬嫹璐�
+      const newMessages = [...prevMessages];
+      
+      // 鏌ユ壘鏈�鍚庝竴鏉″姪鎵嬫秷鎭�
+      const lastMessage = newMessages[newMessages.length - 1];
+      if (lastMessage?.role !== 'assistant') {
+        console.warn('鎵句笉鍒板姪鎵嬫秷鎭潵鏇存柊');
+        return prevMessages; // 涓嶉渶瑕佹洿鏂�
+      }
+      
+      // 鏇存柊娑堟伅鍐呭鍜孖D
+      const updatedContent = (lastMessage.content || '') + answerChunk;
+      
+      // 鍒涘缓娑堟伅鐨勬柊鍓湰浠ョ‘淇漅eact妫�娴嬪埌鍙樺寲
+      const updatedMessage = {
+        ...lastMessage,
+        id: messageId,
+        content: updatedContent,
+        timestamp: Date.now()
+      };
+      
+      if (convId) {
+        updatedMessage.conversation_id = convId;
+      }
+      
+      // 鏇存柊褰撳墠姝e湪澶勭悊鐨勬秷鎭疘D
+      if (isMountedRef.current) {
+        setCurrentMessageId(messageId);
+      }
+      
+      // 鏇挎崲鏈�鍚庝竴鏉℃秷鎭�
+      newMessages[newMessages.length - 1] = updatedMessage;
+      
+      console.log(`鏇存柊鍚庣殑鍐呭闀垮害: ${updatedContent.length}`);
+      
+      // 瑙﹀彂寮哄埗鏇存柊
+      if (isMountedRef.current) {
+        setTimeout(() => {
+          if (isMountedRef.current) {
+            setForceUpdateCounter(count => count + 1);
+          }
+        }, 0);
+      }
+      
+      return newMessages;
+    });
+  }, []);
+
+  // 淇敼handleFinalAnswer鍑芥暟锛屾坊鍔犵粍浠舵寕杞芥鏌�
+  const handleFinalAnswer = useCallback((messageId: string, answer: string, convId?: string) => {
+    // 濡傛灉缁勪欢宸插嵏杞斤紝鍒欎笉鎵ц鏇存柊
+    if (!isMountedRef.current) return;
+    
+    console.log(`璁剧疆鏈�缁堢瓟妗�: ID=${messageId}, 鍐呭="${answer}"`);
+    
+    setMessages(prevMessages => {
+      // 濡傛灉缁勪欢宸插嵏杞斤紝鍒欎笉鎵ц鏇存柊
+      if (!isMountedRef.current) return prevMessages;
+      
+      const newMessages = [...prevMessages];
+      const lastMessage = newMessages[newMessages.length - 1];
+      
+      if (lastMessage?.role !== 'assistant') {
+        console.warn('鎵句笉鍒板姪鎵嬫秷鎭潵鏇存柊鏈�缁堢瓟妗�');
+        return prevMessages;
+      }
+      
+      // 鍒涘缓娑堟伅鐨勬柊鍓湰锛岃缃渶缁堢瓟妗�
+      const updatedMessage = {
+        ...lastMessage,
+        id: messageId,
+        content: answer,
+        timestamp: Date.now()
+      };
+      
+      if (convId) {
+        updatedMessage.conversation_id = convId;
+      }
+      
+      // 鏇存柊鐘舵��
+      if (isMountedRef.current) {
+        setIsMessageComplete(true);
+        setCurrentMessageId(messageId);
+      }
+      
+      // 鏇挎崲鏈�鍚庝竴鏉℃秷鎭�
+      newMessages[newMessages.length - 1] = updatedMessage;
+      
+      // 瑙﹀彂寮哄埗鏇存柊
+      if (isMountedRef.current) {
+        setTimeout(() => {
+          if (isMountedRef.current) {
+            setForceUpdateCounter(count => count + 1);
+          }
+        }, 0);
+      }
+      
+      return newMessages;
+    });
+  }, []);
 
   // 鏇存柊鍙戦�佹寜閽殑绂佺敤鐘舵��
   const isSendDisabled = isStreaming || !message.trim() || !isMessageComplete;
@@ -287,6 +494,59 @@
       setShowMessages(true);
     }, 100);
     return () => clearTimeout(timer);
+  }, []);
+
+  // 娣诲姞鑷畾涔塁SS鍔ㄧ敾鏍峰紡
+  useEffect(() => {
+    const style = document.createElement('style');
+    style.innerHTML = `
+      @keyframes dotBounce {
+        0%, 80%, 100% { transform: translateY(0); }
+        40% { transform: translateY(-6px); }
+      }
+      .dot-animation span:nth-child(1) { animation: dotBounce 1.4s -0.32s infinite ease-in-out; }
+      .dot-animation span:nth-child(2) { animation: dotBounce 1.4s -0.16s infinite ease-in-out; }
+      .dot-animation span:nth-child(3) { animation: dotBounce 1.4s 0s infinite ease-in-out; }
+    `;
+    document.head.appendChild(style);
+
+    return () => {
+      document.head.removeChild(style);
+    };
+  }, []);
+
+  // 娣诲姞澶勭悊鎬濊�冨唴瀹圭殑杈呭姪鍑芥暟
+  const parseMessageContent = (content: string | null) => {
+    if (!content) return { mainContent: '', thinkContent: null };
+    
+    // 鍖归厤<think>鏍囩鍐呭 (涓嶅尯鍒嗗ぇ灏忓啓)
+    const thinkMatch = content.match(/<think>([\s\S]*?)<\/think>/i);
+    
+    if (thinkMatch) {
+      // 鎻愬彇鎬濊�冨唴瀹�
+      const thinkContent = thinkMatch[1].trim();
+      // 绉婚櫎<think>鏍囩,淇濈暀涓昏鍐呭
+      const mainContent = content.replace(/<think>[\s\S]*?<\/think>/i, '').trim();
+      return { mainContent, thinkContent };
+    }
+    
+    return { mainContent: content, thinkContent: null };
+  };
+
+  // 鍒囨崲鎬濊�冨唴瀹圭殑鏄剧ず/闅愯棌
+  const toggleThinkContent = (messageId: string) => {
+    setExpandedThinkMessages(prev => ({
+      ...prev,
+      [messageId]: !prev[messageId]
+    }));
+  };
+
+  // 娣诲姞缁勪欢鍗歌浇鏃剁殑娓呯悊鍑芥暟
+  useEffect(() => {
+    return () => {
+      // 缁勪欢鍗歌浇鏃剁殑娓呯悊宸ヤ綔
+      console.log('鑱婂ぉ缁勪欢鍗歌浇锛屾竻鐞嗚祫婧�');
+    };
   }, []);
 
   return (
@@ -307,13 +567,34 @@
               </button>
             </div>
             <div className="space-y-2">
-              <input
-                type="password"
-                value={apiKey}
-                onChange={(e) => setApiKey(e.target.value)}
-                placeholder="璇疯緭鍏ユ偍鐨� API Key"
-                className="w-full px-4 py-2 rounded-xl border border-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-100/50"
-              />
+              <div className="relative">
+                <input
+                  type={showApiKey ? "text" : "password"}
+                  value={apiKey}
+                  onChange={(e) => setApiKey(e.target.value)}
+                  placeholder="璇疯緭鍏ユ偍鐨� API Key"
+                  className="w-full px-4 py-2 rounded-xl border border-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-100/50"
+                />
+                <button
+                  type="button"
+                  onClick={() => setShowApiKey(!showApiKey)}
+                  className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
+                >
+                  {showApiKey ? (
+                    // 鐪肩潧鍏抽棴鍥炬爣
+                    <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
+                      <path fillRule="evenodd" d="M3.707 2.293a1 1 0 00-1.414 1.414l14 14a1 1 0 001.414-1.414l-1.473-1.473A10.014 10.014 0 0019.542 10C18.268 5.943 14.478 3 10 3a9.958 9.958 0 00-4.512 1.074l-1.78-1.781zm4.261 4.26l1.514 1.515a2.003 2.003 0 012.45 2.45l1.514 1.514a4 4 0 00-5.478-5.478z" clipRule="evenodd" />
+                      <path d="M12.454 16.697L9.75 13.992a4 4 0 01-3.742-3.741L2.335 6.578A9.98 9.98 0 00.458 10c1.274 4.057 5.065 7 9.542 7 .847 0 1.669-.105 2.454-.303z" />
+                    </svg>
+                  ) : (
+                    // 鐪肩潧鍥炬爣
+                    <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
+                      <path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
+                      <path fillRule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clipRule="evenodd" />
+                    </svg>
+                  )}
+                </button>
+              </div>
             </div>
             <div className="flex justify-end gap-2">
               <button
@@ -371,74 +652,119 @@
         <div className="flex-1 overflow-y-auto pt-20 pb-32">
           <div className="max-w-4xl mx-auto px-6">
             <div className={`space-y-6 ${showMessages ? 'opacity-100' : 'opacity-0'} transition-opacity duration-200`}>
-              {messages.map((msg, index) => (
-                <div
-                  key={msg.id}
-                  className={`flex items-start gap-4 ${msg.role === 'user' ? 'flex-row-reverse' : ''}`}
-                >
-                  <div className="relative flex-shrink-0">
-                    <div className="w-8 h-8 rounded-lg overflow-hidden shadow-inner bg-gray-50">
-                      <Image 
-                        src={msg.role === 'assistant' ? "/images/logo.jpg" : DEFAULT_USER_AVATAR}
-                        alt={msg.role === 'assistant' ? "AI鍔╂墜" : "鐢ㄦ埛"}
-                        width={32}
-                        height={32}
-                        className="w-full h-full object-cover"
-                      />
-                    </div>
-                    {msg.role === 'assistant' && (
-                      <div className="absolute -bottom-1 -right-1 w-3 h-3 bg-green-400 rounded-full border-2 border-white"></div>
-                    )}
-                  </div>
-                  <div className={`flex-1 min-w-0 ${msg.role === 'user' ? 'text-right' : ''}`}>
-                    <div className={`${
-                      msg.role === 'assistant' 
-                        ? 'bg-white rounded-xl shadow-sm border border-gray-100' 
-                        : 'bg-[#E8F4FF] rounded-xl'
-                      } inline-block max-w-[85%] relative min-h-[42px]`}>
-                      {msg.role === 'assistant' && !isMessageComplete && currentMessageId === msg.id && (
-                        <div className="absolute left-0 top-0 w-full h-full flex items-center justify-center">
-                          <div className="w-5 h-5 border-2 border-blue-500/30 border-t-blue-500/80 rounded-full animate-spin"></div>
-                        </div>
-                      )}
-                      <div className={`flex items-center px-4 min-h-[42px] ${
-                        msg.role === 'assistant' && !isMessageComplete && currentMessageId === msg.id ? 'invisible' : ''
-                      }`}>
-                        <div className="prose prose-sm max-w-none text-[14px] leading-[1.3]
-                          prose-p:my-0 prose-p:leading-[1.3]
-                          prose-headings:font-medium prose-headings:text-gray-800
-                          prose-h1:text-lg prose-h1:my-2
-                          prose-h2:text-base prose-h2:my-2
-                          prose-h3:text-base prose-h3:my-1.5
-                          prose-ul:my-1.5 prose-ul:pl-4 prose-li:my-0.5
-                          prose-ol:my-1.5 prose-ol:pl-4
-                          prose-code:px-1 prose-code:py-0.5 prose-code:bg-gray-100 prose-code:rounded prose-code:text-gray-800 prose-code:before:content-[''] prose-code:after:content-['']
-                          prose-pre:my-2 prose-pre:p-2.5 prose-pre:bg-gray-800 prose-pre:rounded-lg
-                          prose-a:text-blue-500 prose-a:no-underline hover:prose-a:underline
-                          prose-blockquote:my-2 prose-blockquote:pl-3 prose-blockquote:border-l-4 prose-blockquote:border-gray-200
-                          prose-strong:font-medium prose-strong:text-gray-800
-                          prose-table:my-2 prose-tr:border-gray-200 prose-td:py-1 prose-td:px-2">
-                          {msg.content.split('\n').map((line, i) => (
-                            <div key={i} className="my-0">
-                              {line.trim() && (
-                                <ReactMarkdown 
-                                  remarkPlugins={[remarkGfm]}
-                                  rehypePlugins={[rehypeRaw, rehypeSanitize]}
-                                >
-                                  {line.trim()}
-                                </ReactMarkdown>
-                              )}
-                            </div>
-                          ))}
-                        </div>
+              {messages.length === 0 ? (
+                <div className="flex items-center justify-center h-[200px] text-gray-400">
+                  <p>鍙戦�佹秷鎭紑濮嬪璇�</p>
+                </div>
+              ) : (
+                messages.map((msg, index) => (
+                  <div
+                    key={msg.id}
+                    className={`flex items-start gap-4 ${msg.role === 'user' ? 'flex-row-reverse' : ''}`}
+                  >
+                    <div className="relative flex-shrink-0">
+                      <div className="w-8 h-8 rounded-lg overflow-hidden shadow-inner bg-gray-50">
+                        <Image 
+                          src={msg.role === 'assistant' ? "/images/logo.jpg" : DEFAULT_USER_AVATAR}
+                          alt={msg.role === 'assistant' ? "AI鍔╂墜" : "鐢ㄦ埛"}
+                          width={32}
+                          height={32}
+                          className="w-full h-full object-cover"
+                        />
                       </div>
                     </div>
-                    <div className="mt-0.5 text-xs text-gray-400">
-                      {msg.role === 'user' && new Date(msg.timestamp).toLocaleTimeString()}
+                    <div className={`flex-1 min-w-0 ${msg.role === 'user' ? 'text-right' : ''}`}>
+                      <div className={`${
+                        msg.role === 'assistant' 
+                          ? 'bg-white rounded-xl shadow-sm border border-gray-100' 
+                          : 'bg-[#E8F4FF] rounded-xl'
+                        } inline-block max-w-[85%] relative overflow-hidden`}>
+                        
+                        {/* 璋冭瘯淇℃伅 - 绉婚櫎杩欓儴鍒� */}
+                        {/* 
+                        {process.env.NODE_ENV !== 'production' && (
+                          <div className="bg-gray-100 px-2 py-1 text-xs text-gray-500">
+                            闀垮害: {(msg.content || '').length} | 璁℃暟: {forceUpdateCounter}
+                          </div>
+                        )}
+                        */}
+                        
+                        {msg.role === 'assistant' && (
+                          <>{(() => {
+                            // 瑙f瀽娑堟伅鍐呭锛屾彁鍙栨�濊�冮儴鍒�
+                            const { mainContent, thinkContent } = parseMessageContent(msg.content);
+                            const isExpanded = expandedThinkMessages[msg.id] || false;
+                            const thinkingDuration = 8; // 鎬濊�冩椂闂�(绉�)
+                            
+                            return (
+                              <>
+                                {/* 鎬濊�冨唴瀹瑰尯鍩� (濡傛灉瀛樺湪) */}
+                                {thinkContent && (
+                                  <div className="border-b border-gray-200">
+                                    <button 
+                                      onClick={() => toggleThinkContent(msg.id)}
+                                      className="w-full flex items-center justify-between px-3 py-2 bg-gray-50 hover:bg-gray-100 transition-colors text-sm text-gray-700"
+                                    >
+                                      <div className="flex items-center space-x-2">
+                                        <span>宸叉繁搴︽�濊�� (鐢ㄦ椂 {thinkingDuration} 绉�)</span>
+                                      </div>
+                                      <svg 
+                                        className={`w-4 h-4 transform transition-transform ${isExpanded ? 'rotate-180' : ''}`}
+                                        fill="none" 
+                                        stroke="currentColor" 
+                                        viewBox="0 0 24 24"
+                                      >
+                                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
+                                      </svg>
+                                    </button>
+                                    
+                                    {/* 鍙姌鍙犵殑鎬濊�冨唴瀹� */}
+                                    {isExpanded && (
+                                      <div className="bg-gray-50 px-4 py-3 text-sm text-gray-700 border-t border-gray-200 whitespace-pre-wrap">
+                                        {thinkContent}
+                                      </div>
+                                    )}
+                                  </div>
+                                )}
+                                
+                                {/* 涓昏鍐呭 */}
+                                <div className="p-3">
+                                  <div className="text-gray-800 leading-relaxed whitespace-pre-wrap">
+                                    {mainContent || (msg.role === 'assistant' && !isMessageComplete ? '姝e湪鎬濊��...' : '')}
+                                  </div>
+                                </div>
+                                
+                                {/* 鍔犺浇鎸囩ず鍣� */}
+                                {msg.role === 'assistant' && !isMessageComplete && (
+                                  <div className="absolute bottom-1 right-2">
+                                    <div className="flex space-x-1">
+                                      <div className="w-1.5 h-1.5 rounded-full bg-blue-400 animate-pulse delay-0"></div>
+                                      <div className="w-1.5 h-1.5 rounded-full bg-blue-400 animate-pulse delay-150"></div>
+                                      <div className="w-1.5 h-1.5 rounded-full bg-blue-400 animate-pulse delay-300"></div>
+                                    </div>
+                                  </div>
+                                )}
+                              </>
+                            );
+                          })()}</>
+                        )}
+                        
+                        {/* 鐢ㄦ埛娑堟伅绠�鍗曟樉绀� */}
+                        {msg.role === 'user' && (
+                          <div className="p-3">
+                            <div className="text-gray-800 leading-relaxed whitespace-pre-wrap">
+                              {msg.content}
+                            </div>
+                          </div>
+                        )}
+                      </div>
+                      <div className="mt-0.5 text-xs text-gray-400">
+                        {msg.role === 'user' && new Date(msg.timestamp).toLocaleTimeString()}
+                      </div>
                     </div>
                   </div>
-                </div>
-              ))}
+                ))
+              )}
               <div ref={messagesEndRef} />
             </div>
           </div>

--
Gitblit v1.9.3