This commit is contained in:
Zylan
2025-04-02 13:59:33 +08:00
parent 3e846cacfc
commit eea4e13005
5 changed files with 244 additions and 70 deletions

65
app.py
View File

@@ -4,7 +4,8 @@ import pyautogui
import base64
from io import BytesIO
import socket
from threading import Thread
from threading import Thread, Event
import threading
from PIL import Image
import pyperclip
from models import ModelFactory
@@ -31,6 +32,9 @@ socketio = SocketIO(
# 添加配置文件路径
CONFIG_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config')
# 跟踪用户生成任务的字典
generation_tasks = {}
# 初始化模型工厂
ModelFactory.initialize()
@@ -310,6 +314,27 @@ def handle_text_extraction(data):
'error': error_msg
}, room=request.sid)
@socketio.on('stop_generation')
def handle_stop_generation():
"""处理停止生成请求"""
sid = request.sid
print(f"接收到停止生成请求: {sid}")
if sid in generation_tasks:
# 设置停止标志
stop_event = generation_tasks[sid]
stop_event.set()
# 发送已停止状态
socketio.emit('claude_response', {
'status': 'stopped',
'content': '生成已停止'
}, room=sid)
print(f"已停止用户 {sid} 的生成任务")
else:
print(f"未找到用户 {sid} 的生成任务")
@socketio.on('analyze_text')
def handle_analyze_text(data):
try:
@@ -350,8 +375,23 @@ def handle_analyze_text(data):
'https': f"http://{settings.get('proxyHost')}:{settings.get('proxyPort')}"
}
for response in model_instance.analyze_text(text, proxies=proxies):
socketio.emit('claude_response', response)
# 创建用于停止生成的事件
sid = request.sid
stop_event = Event()
generation_tasks[sid] = stop_event
try:
for response in model_instance.analyze_text(text, proxies=proxies):
# 检查是否收到停止信号
if stop_event.is_set():
print(f"分析文本生成被用户 {sid} 停止")
break
socketio.emit('claude_response', response, room=sid)
finally:
# 清理任务
if sid in generation_tasks:
del generation_tasks[sid]
except Exception as e:
print(f"Error in analyze_text: {str(e)}")
@@ -398,8 +438,23 @@ def handle_analyze_image(data):
'https': f"http://{settings.get('proxyHost')}:{settings.get('proxyPort')}"
}
for response in model_instance.analyze_image(image_data, proxies=proxies):
socketio.emit('claude_response', response)
# 创建用于停止生成的事件
sid = request.sid
stop_event = Event()
generation_tasks[sid] = stop_event
try:
for response in model_instance.analyze_image(image_data, proxies=proxies):
# 检查是否收到停止信号
if stop_event.is_set():
print(f"分析图像生成被用户 {sid} 停止")
break
socketio.emit('claude_response', response, room=sid)
finally:
# 清理任务
if sid in generation_tasks:
del generation_tasks[sid]
except Exception as e:
print(f"Error in analyze_image: {str(e)}")

View File

@@ -1,5 +1,5 @@
{
"version": "1.1.0",
"version": "1.1.1",
"build_date": "2025-04-02",
"github_repo": "Zippland/Snap-Solver"
}

View File

@@ -46,32 +46,40 @@ class SnapSolver {
}
initializeElements() {
// Main elements
this.screenshotImg = document.getElementById('screenshotImg');
this.imagePreview = document.getElementById('imagePreview');
this.emptyState = document.getElementById('emptyState');
// 移除对裁剪按钮的引用
// 查找主要UI元素
this.connectionStatus = document.getElementById('connectionStatus');
this.captureBtn = document.getElementById('captureBtn');
this.emptyState = document.getElementById('emptyState');
this.imagePreview = document.getElementById('imagePreview');
this.screenshotImg = document.getElementById('screenshotImg');
this.sendToClaudeBtn = document.getElementById('sendToClaude');
this.extractTextBtn = document.getElementById('extractText');
this.extractedText = document.getElementById('extractedText');
this.sendExtractedTextBtn = document.getElementById('sendExtractedText');
this.cropContainer = document.getElementById('cropContainer');
this.manualTextInput = document.getElementById('manualTextInput');
this.claudePanel = document.getElementById('claudePanel');
this.responseContent = document.getElementById('responseContent');
this.closeClaudePanel = document.getElementById('closeClaudePanel');
this.thinkingSection = document.getElementById('thinkingSection');
this.thinkingContent = document.getElementById('thinkingContent');
this.thinkingToggle = document.getElementById('thinkingToggle');
this.connectionStatus = document.getElementById('connectionStatus');
this.statusLight = document.querySelector('.status-light');
this.progressLine = document.querySelector('.progress-line');
this.statusText = document.querySelector('.status-text');
this.analysisIndicator = document.querySelector('.analysis-indicator');
// Crop elements
this.thinkingContent = document.getElementById('thinkingContent');
this.responseContent = document.getElementById('responseContent');
this.cropContainer = document.getElementById('cropContainer');
this.cropCancel = document.getElementById('cropCancel');
this.cropConfirm = document.getElementById('cropConfirm');
this.stopGenerationBtn = document.getElementById('stopGenerationBtn');
// 处理按钮事件
if (this.closeClaudePanel) {
this.closeClaudePanel.addEventListener('click', () => {
this.claudePanel.classList.add('hidden');
});
}
// 处理停止生成按钮点击事件
if (this.stopGenerationBtn) {
this.stopGenerationBtn.addEventListener('click', () => {
this.stopGeneration();
});
}
}
initializeState() {
@@ -128,51 +136,52 @@ class SnapSolver {
}
updateStatusLight(status) {
// 更新状态指示器
if (this.analysisIndicator) {
this.analysisIndicator.className = 'analysis-indicator';
this.progressLine.className = 'progress-line';
switch (status) {
case 'started':
case 'streaming':
this.analysisIndicator.classList.add('processing');
this.progressLine.classList.add('processing');
this.statusText.textContent = '处理中...';
break;
case 'completed':
this.analysisIndicator.classList.add('completed');
this.progressLine.classList.add('completed');
this.statusText.textContent = '已完成';
break;
case 'error':
this.analysisIndicator.classList.add('error');
this.progressLine.classList.add('error');
this.statusText.textContent = '错误';
break;
default:
// 重置为默认状态
this.statusText.textContent = '准备中';
break;
}
} else if (this.statusLight) {
// 兼容旧版状态灯
this.statusLight.className = 'status-light';
switch (status) {
case 'started':
case 'streaming':
this.statusLight.classList.add('processing');
break;
case 'completed':
this.statusLight.classList.add('completed');
break;
case 'error':
this.statusLight.classList.add('error');
break;
default:
// Reset to default state
break;
}
const statusLight = document.querySelector('.status-light');
const progressLine = document.querySelector('.progress-line');
const statusText = document.querySelector('.status-text');
const analysisIndicator = document.querySelector('.analysis-indicator');
if (!statusLight || !progressLine || !statusText || !analysisIndicator) {
return;
}
analysisIndicator.style.display = 'flex';
statusLight.classList.remove('completed', 'error', 'working');
switch (status) {
case 'started':
case 'thinking':
case 'reasoning':
case 'streaming':
statusLight.classList.add('working');
progressLine.style.animation = 'progress-animation 2s linear infinite';
statusText.textContent = '生成中';
break;
case 'completed':
statusLight.classList.add('completed');
progressLine.style.animation = 'none';
progressLine.style.width = '100%';
statusText.textContent = '完成';
break;
case 'error':
statusLight.classList.add('error');
progressLine.style.animation = 'none';
progressLine.style.width = '100%';
statusText.textContent = '出错';
break;
case 'stopped':
statusLight.classList.add('error');
progressLine.style.animation = 'none';
progressLine.style.width = '100%';
statusText.textContent = '已停止';
break;
default:
analysisIndicator.style.display = 'none';
break;
}
}
@@ -372,6 +381,9 @@ class SnapSolver {
this.responseContent.innerHTML = '<div class="loading-message">分析进行中,请稍候...</div>';
this.responseContent.style.display = 'block';
}
// 显示停止生成按钮
this.showStopGenerationButton();
break;
case 'thinking':
@@ -406,6 +418,9 @@ class SnapSolver {
'fas fa-chevron-up' : 'fas fa-chevron-down';
}
}
// 确保停止按钮可见
this.showStopGenerationButton();
break;
case 'reasoning':
@@ -440,6 +455,9 @@ class SnapSolver {
'fas fa-chevron-up' : 'fas fa-chevron-down';
}
}
// 确保停止按钮可见
this.showStopGenerationButton();
break;
case 'thinking_complete':
@@ -529,6 +547,9 @@ class SnapSolver {
// 平滑滚动到最新内容
this.scrollToBottom();
}
// 确保停止按钮可见
this.showStopGenerationButton();
break;
case 'completed':
@@ -587,6 +608,27 @@ class SnapSolver {
// 滚动到结果内容底部
this.scrollToBottom();
}
// 隐藏停止生成按钮
this.hideStopGenerationButton();
break;
case 'stopped':
// 处理停止生成的响应
console.log('Generation stopped');
// 重新启用按钮
if (this.sendToClaudeBtn) this.sendToClaudeBtn.disabled = false;
if (this.sendExtractedTextBtn) this.sendExtractedTextBtn.disabled = false;
// 恢复界面
this.updateStatusLight('stopped');
// 隐藏停止生成按钮
this.hideStopGenerationButton();
// 显示提示信息
window.uiManager.showToast('已停止生成', 'info');
break;
case 'error':
@@ -604,6 +646,9 @@ class SnapSolver {
if (this.sendExtractedTextBtn) this.sendExtractedTextBtn.disabled = false;
window.uiManager.showToast('Analysis failed: ' + errorMessage, 'error');
// 隐藏停止生成按钮
this.hideStopGenerationButton();
break;
default:
@@ -620,6 +665,9 @@ class SnapSolver {
if (this.sendExtractedTextBtn) this.sendExtractedTextBtn.disabled = false;
window.uiManager.showToast('Unknown error occurred', 'error');
// 隐藏停止生成按钮
this.hideStopGenerationButton();
break;
}
});
@@ -1570,6 +1618,39 @@ class SnapSolver {
}
}
}
// 添加停止生成方法
stopGeneration() {
console.log('停止生成请求');
// 向服务器发送停止生成信号
if (this.socket && this.socket.connected) {
this.socket.emit('stop_generation');
// 显示提示
window.uiManager.showToast('正在停止生成...', 'info');
// 隐藏停止按钮
this.hideStopGenerationButton();
} else {
console.error('无法停止生成: Socket未连接');
window.uiManager.showToast('无法停止生成: 连接已断开', 'error');
}
}
// 显示停止生成按钮
showStopGenerationButton() {
if (this.stopGenerationBtn) {
this.stopGenerationBtn.classList.add('visible');
}
}
// 隐藏停止生成按钮
hideStopGenerationButton() {
if (this.stopGenerationBtn) {
this.stopGenerationBtn.classList.remove('visible');
}
}
}
// Initialize the application when the DOM is loaded

View File

@@ -10,7 +10,7 @@
--text-secondary: #666666;
--border-color: #e0e0e0;
--shadow-color: rgba(0, 0, 0, 0.1);
--error-color: #f44336;
--error-color: #e53935;
--success-color: #4CAF50;
--primary: #2196f3;
--primary-rgb: 33, 150, 243;
@@ -32,6 +32,7 @@
--accent: #4a6cf7;
--placeholder: #a0a0a0;
--disabled: #e6e6e6;
--error-hover-color: #c62828;
}
[data-theme="dark"] {
@@ -680,8 +681,8 @@ body {
.header-title {
display: flex;
flex-direction: column;
gap: 0.5rem;
align-items: center;
gap: 8px;
}
.header-title h2 {
@@ -2173,3 +2174,37 @@ button:disabled {
padding-top: 0.6rem;
}
}
/* 在适当位置添加停止生成按钮样式 */
.btn-stop-generation {
display: none;
background-color: var(--error-color);
color: white;
border: none;
border-radius: 4px;
width: 32px;
height: 32px;
padding: 0;
margin-left: 10px;
cursor: pointer;
transition: all 0.2s ease;
align-items: center;
justify-content: center;
}
.btn-stop-generation:hover {
background-color: var(--error-hover-color);
transform: scale(1.05);
}
.btn-stop-generation:active {
transform: scale(0.95);
}
.btn-stop-generation i {
font-size: 14px;
}
.btn-stop-generation.visible {
display: flex;
}

View File

@@ -100,6 +100,9 @@
<div class="progress-line"></div>
<div class="status-text">准备中</div>
</div>
<button id="stopGenerationBtn" class="btn-stop-generation" title="停止生成">
<i class="fas fa-stop"></i>
</button>
</div>
<button class="btn-icon" id="closeClaudePanel" title="关闭分析结果">
<i class="fas fa-times"></i>