diff --git a/app.py b/app.py index 94793f2..559dbe1 100644 --- a/app.py +++ b/app.py @@ -81,8 +81,9 @@ def stream_model_response(response_generator, sid): }, room=sid) print("Sent initial status to client") - # 跟踪已发送的内容 - previous_thinking_content = "" + # 维护服务端缓冲区以累积完整内容 + response_buffer = "" + thinking_buffer = "" # 流式处理响应 for response in response_generator: @@ -99,34 +100,37 @@ def stream_model_response(response_generator, sid): # 根据不同的状态进行处理 if status == 'thinking': - # 确保只发送新增的思考内容 - if previous_thinking_content and content.startswith(previous_thinking_content): - # 只发送新增部分 - new_content = content[len(previous_thinking_content):] - if new_content: # 只有当有新内容时才发送 - print(f"Streaming thinking content: {len(new_content)} chars") - socketio.emit('claude_response', { - 'status': 'thinking', - 'content': new_content - }, room=sid) - else: - # 直接发送全部内容(首次或内容不连续的情况) - print(f"Streaming thinking content (reset): {len(content)} chars") - socketio.emit('claude_response', { - 'status': 'thinking', - 'content': content - }, room=sid) + # 累积思考内容到缓冲区 + thinking_buffer += content - # 更新已发送的思考内容记录 - previous_thinking_content = content + # 发送完整的思考内容 + print(f"Streaming thinking content: {len(thinking_buffer)} chars") + socketio.emit('claude_response', { + 'status': 'thinking', + 'content': thinking_buffer + }, room=sid) + elif status == 'thinking_complete': + # 直接使用完整的思考内容 + thinking_buffer = content # 使用服务器提供的完整内容 + + print(f"Thinking complete, total length: {len(thinking_buffer)} chars") + socketio.emit('claude_response', { + 'status': 'thinking_complete', + 'content': thinking_buffer + }, room=sid) + elif status == 'streaming': # 流式输出正常内容 if content: - print(f"Streaming response content: {len(content)} chars") + # 累积到服务端缓冲区 + response_buffer += content + + # 发送完整的内容 + print(f"Streaming response content: {len(response_buffer)} chars") socketio.emit('claude_response', { 'status': 'streaming', - 'content': content + 'content': response_buffer }, room=sid) else: @@ -134,9 +138,7 @@ def stream_model_response(response_generator, sid): socketio.emit('claude_response', response, room=sid) # 调试信息 - if status == 'thinking_complete': - print(f"Thinking complete, total length: {len(content)} chars") - elif status == 'completed': + if status == 'completed': print("Response completed") elif status == 'error': print(f"Error: {response.get('error', 'Unknown error')}") diff --git a/static/js/main.js b/static/js/main.js index 9057ced..1898813 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -66,10 +66,6 @@ class SnapSolver { latex: '' }; - // 新增:流式输出的内存缓冲区 - this.responseBuffer = ""; - this.thinkingBuffer = ""; - // 确保裁剪容器和其他面板初始为隐藏状态 if (this.cropContainer) { this.cropContainer.classList.add('hidden'); @@ -236,9 +232,6 @@ class SnapSolver { switch (data.status) { case 'started': console.log('Analysis started'); - // 重置内存缓冲区 - this.responseBuffer = ""; - this.thinkingBuffer = ""; // 清空显示内容 this.responseContent.innerHTML = ''; this.thinkingContent.innerHTML = ''; @@ -250,14 +243,11 @@ class SnapSolver { case 'thinking': // 处理思考内容 if (data.content) { - console.log('Received thinking:', data.content); + console.log('Received thinking content'); this.thinkingSection.classList.remove('hidden'); - // 添加到内存缓冲区 - this.thinkingBuffer += data.content; - - // 安全地更新DOM - this.updateElementContent(this.thinkingContent, this.thinkingBuffer); + // 直接设置完整内容而不是追加 + this.setElementContent(this.thinkingContent, data.content); // 添加打字动画效果 this.thinkingContent.classList.add('thinking-typing'); @@ -270,9 +260,8 @@ class SnapSolver { console.log('Thinking complete'); this.thinkingSection.classList.remove('hidden'); - // 重置内存缓冲区并更新 - this.thinkingBuffer = data.content; - this.updateElementContent(this.thinkingContent, this.thinkingBuffer); + // 设置完整内容 + this.setElementContent(this.thinkingContent, data.content); // 移除打字动画 this.thinkingContent.classList.remove('thinking-typing'); @@ -281,13 +270,10 @@ class SnapSolver { case 'streaming': if (data.content) { - console.log('Received content:', data.content); + console.log('Received content'); - // 添加到内存缓冲区 - this.responseBuffer += data.content; - - // 安全地更新DOM - this.updateElementContent(this.responseContent, this.responseBuffer); + // 直接设置完整内容 + this.setElementContent(this.responseContent, data.content); // 移除思考部分的打字动画 this.thinkingContent.classList.remove('thinking-typing'); @@ -298,17 +284,25 @@ class SnapSolver { console.log('Analysis completed'); this.sendToClaudeBtn.disabled = false; this.sendExtractedTextBtn.disabled = false; - this.addToHistory(this.croppedImage, this.responseBuffer, this.thinkingBuffer); + + // 保存到历史记录 + const responseText = this.responseContent.textContent || ''; + const thinkingText = this.thinkingContent.textContent || ''; + this.addToHistory(this.croppedImage, responseText, thinkingText); + window.showToast('Analysis completed successfully'); break; case 'error': console.error('Analysis error:', data.error); const errorMessage = data.error || 'Unknown error occurred'; - // 错误信息添加到缓冲区 - this.responseBuffer += '\nError: ' + errorMessage; - // 更新DOM - this.updateElementContent(this.responseContent, this.responseBuffer); + + // 显示错误信息 + if (errorMessage) { + const currentText = this.responseContent.textContent || ''; + this.setElementContent(this.responseContent, currentText + '\nError: ' + errorMessage); + } + this.sendToClaudeBtn.disabled = false; this.sendExtractedTextBtn.disabled = false; window.showToast('Analysis failed: ' + errorMessage, 'error'); @@ -317,10 +311,8 @@ class SnapSolver { default: console.warn('Unknown response status:', data.status); if (data.error) { - // 错误信息添加到缓冲区 - this.responseBuffer += '\nError: ' + data.error; - // 更新DOM - this.updateElementContent(this.responseContent, this.responseBuffer); + const currentText = this.responseContent.textContent || ''; + this.setElementContent(this.responseContent, currentText + '\nError: ' + data.error); this.sendToClaudeBtn.disabled = false; this.sendExtractedTextBtn.disabled = false; window.showToast('Unknown error occurred', 'error'); @@ -336,25 +328,12 @@ class SnapSolver { }); } - // 新增:安全更新DOM内容的辅助方法 - updateElementContent(element, content) { + // 新方法:安全设置DOM内容的方法(替代updateElementContent) + setElementContent(element, content) { if (!element) return; - // 创建文档片段以提高性能 - const fragment = document.createDocumentFragment(); - const tempDiv = document.createElement('div'); - - // 设置内容 - tempDiv.textContent = content; - - // 将所有子节点移动到文档片段 - while (tempDiv.firstChild) { - fragment.appendChild(tempDiv.firstChild); - } - - // 清空目标元素并添加文档片段 - element.innerHTML = ''; - element.appendChild(fragment); + // 直接设置内容 + element.textContent = content; // 自动滚动到底部 element.scrollTop = element.scrollHeight;