diff --git a/web/src/app/home/pipelines/components/debug-dialog/DebugDialog.tsx b/web/src/app/home/pipelines/components/debug-dialog/DebugDialog.tsx index 7b0607e4..c45a7085 100644 --- a/web/src/app/home/pipelines/components/debug-dialog/DebugDialog.tsx +++ b/web/src/app/home/pipelines/components/debug-dialog/DebugDialog.tsx @@ -42,9 +42,24 @@ export default function DebugDialog({ const inputRef = useRef(null); const popoverRef = useRef(null); - const scrollToBottom = () => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - }; + // const scrollToBottom = () => { + // messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + // }; + + const scrollToBottom = useCallback(() => { + // 使用setTimeout确保在DOM更新后执行滚动 + setTimeout(() => { + const scrollArea = document.querySelector('.scroll-area') as HTMLElement; + if (scrollArea) { + scrollArea.scrollTo({ + top: scrollArea.scrollHeight, + behavior: 'smooth', + }); + } + // 同时确保messagesEndRef也滚动到视图 + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, 0); + }, []); const loadMessages = useCallback( async (pipelineId: string) => { @@ -60,10 +75,10 @@ export default function DebugDialog({ }, [sessionType], ); - + // 在useEffect中监听messages变化时滚动 useEffect(() => { scrollToBottom(); - }, [messages]); + }, [messages, scrollToBottom]); useEffect(() => { if (open) { @@ -175,7 +190,7 @@ export default function DebugDialog({ const botMessage: Message = { id: -1, role: 'assistant', - content: '', + content: '生成中...', timestamp: new Date().toISOString(), message_chain: [{ type: 'Plain', text: '' }], }; @@ -191,8 +206,9 @@ export default function DebugDialog({ setHasAt(false); try { - const botMessageId = botMessage.id; - let accumulatedContent = ''; + let fullContent = ''; // 保存完整内容 + let displayContent = ''; // 当前显示内容 + let typingInterval: NodeJS.Timeout; await httpClient.sendStreamingWebChatMessage( sessionType, @@ -201,40 +217,76 @@ export default function DebugDialog({ (data) => { // 处理流式响应数据 if (data.message) { - accumulatedContent += data.message.content; + // 更新完整内容 + fullContent = data.message.content; - // 更新bot消息 - setMessages((prevMessages) => { - const updatedMessages = [...prevMessages]; - // const botMessageIndex = updatedMessages.findIndex( - // (msg) => - // msg.id === botMessageId && msg.role === 'assistant', - // ); - // 使用索引来更新消息,而不是id匹配 - const botMessageIndex = updatedMessages.length - 1; + // 清除之前的打字效果 + if (typingInterval) { + clearInterval(typingInterval); + } - if (botMessageIndex !== -1) { - const updatedBotMessage = { - ...updatedMessages[botMessageIndex], - content: accumulatedContent, - message_chain: [ - { type: 'Plain', text: accumulatedContent }, - ], - }; - updatedMessages[botMessageIndex] = updatedBotMessage; + // 开始新的打字效果 + let currentPos = displayContent.length; + const targetContent = fullContent; + + typingInterval = setInterval(() => { + if (currentPos < targetContent.length) { + displayContent = targetContent.substring(0, currentPos + 1); + currentPos++; + + // 更新bot消息 + setMessages((prevMessages) => { + const updatedMessages = [...prevMessages]; + const botMessageIndex = updatedMessages.length - 1; + + if (botMessageIndex !== -1) { + const updatedBotMessage = { + ...updatedMessages[botMessageIndex], + content: displayContent, + message_chain: [ + { type: 'Plain', text: displayContent }, + ], + }; + updatedMessages[botMessageIndex] = updatedBotMessage; + } + setTimeout(scrollToBottom, 0); // 确保在状态更新后滚动 + return updatedMessages; + }); + } else { + clearInterval(typingInterval); } - - return updatedMessages; - }); + }, 30); // 调整这个值可以改变打字速度 } }, () => { // 流传输完成 console.log('Streaming completed'); + if (typingInterval) { + clearInterval(typingInterval); + } + // 确保最终内容完全显示 + setMessages((prevMessages) => { + const updatedMessages = [...prevMessages]; + const botMessageIndex = updatedMessages.length - 1; + + if (botMessageIndex !== -1) { + const updatedBotMessage = { + ...updatedMessages[botMessageIndex], + content: fullContent, + message_chain: [{ type: 'Plain', text: fullContent }], + }; + updatedMessages[botMessageIndex] = updatedBotMessage; + } + setTimeout(scrollToBottom, 0); // 确保在状态更新后滚动 + return updatedMessages; + }); }, (error) => { // 处理错误 console.error('Streaming error:', error); + if (typingInterval) { + clearInterval(typingInterval); + } if (sessionType === 'person') { toast.error(t('pipelines.debugDialog.sendFailed')); } diff --git a/web/src/app/infra/http/HttpClient.ts b/web/src/app/infra/http/HttpClient.ts index aeabc063..f6ff6a50 100644 --- a/web/src/app/infra/http/HttpClient.ts +++ b/web/src/app/infra/http/HttpClient.ts @@ -430,12 +430,12 @@ class HttpClient { // 处理完整的JSON对象 const lines = buffer.split('\n\n'); - buffer = ''; + buffer = lines.pop() || ''; for (const line of lines) { if (line.startsWith('data:')) { try { - const data = JSON.parse(line.slice(6)); + const data = JSON.parse(line.slice(5)); if (data.type === 'end') { // 流传输结束