From 4921d98d0a9beff17e8de470e76aec9cab4dc87c Mon Sep 17 00:00:00 2001 From: Gnoloah Date: Fri, 12 Sep 2025 12:06:35 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E4=BF=AE=E5=A4=8D=E8=B1=86=E5=8C=85?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E6=8F=90=E7=A4=BA=E8=AF=8D=E4=B8=8D=E8=B5=B7?= =?UTF-8?q?=E4=BD=9C=E7=94=A8=E9=97=AE=E9=A2=98=EF=BC=8C=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E6=A0=BC=E5=BC=8F=E6=B8=B2=E6=9F=93=E4=B8=8D?= =?UTF-8?q?=E6=AD=A3=E7=A1=AE=E9=97=AE=E9=A2=98=EF=BC=9B=202.=20=E6=88=AA?= =?UTF-8?q?=E5=9B=BE=E6=94=AF=E6=8C=81=E7=AA=97=E5=8F=A3=E5=A4=A7=E5=B0=8F?= =?UTF-8?q?=E4=B8=8E=E4=BD=8D=E7=BD=AE=E8=AE=B0=E5=BF=86=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E5=9C=A8=E5=AE=8C=E6=88=90=E6=88=AA=E5=9B=BE=E5=90=8E=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=8F=91=E9=80=81=E7=BB=99AI=EF=BC=9B=203.=20?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=A1=B5=E9=9D=A2=E5=B8=83=E5=B1=80=EF=BC=8C?= =?UTF-8?q?=E9=80=82=E5=90=88=E7=9B=B2=E6=8C=89=E5=BC=80=E5=A7=8B=E6=88=AA?= =?UTF-8?q?=E5=9B=BE=EF=BC=8C=E7=BB=93=E6=9E=9C=E7=AA=97=E5=8F=A3=E7=BD=AE?= =?UTF-8?q?=E4=BA=8E=E6=88=AA=E5=9B=BE=E4=B9=8B=E4=B8=8A.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/models.json | 12 +++- config/version.json | 2 +- models/doubao.py | 29 ++++++-- models/google.py | 2 +- static/js/main.js | 98 ++++++++++++++++++++++--- static/style.css | 167 +++++++++++++++++++++++++++++++++++++------ templates/index.html | 126 +++++++++++++++++++++----------- 7 files changed, 354 insertions(+), 82 deletions(-) diff --git a/config/models.json b/config/models.json index fe35fd8..2958112 100644 --- a/config/models.json +++ b/config/models.json @@ -110,7 +110,15 @@ "supportsMultimodal": true, "isReasoning": false, "version": "latest", - "description": "Google更快速的Gemini 2.5 Flash模型,支持图像理解,有免费配额" + "description": "Google最新的Gemini 2.5 Flash模型,支持图像理解,速度更快,性能更好" + }, + "gemini-2.0-flash": { + "name": "Gemini 2.0 Flash", + "provider": "google", + "supportsMultimodal": true, + "isReasoning": false, + "version": "latest", + "description": "Google更快速的Gemini 2.0 Flash模型,支持图像理解,有免费配额" }, "doubao-seed-1-6-250615": { "name": "Doubao-Seed-1.6", @@ -121,4 +129,4 @@ "description": "支持auto/thinking/non-thinking三种思考模式、支持多模态、256K长上下文" } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/config/version.json b/config/version.json index b4eba3f..be03306 100644 --- a/config/version.json +++ b/config/version.json @@ -1,5 +1,5 @@ { - "version": "1.3.0", + "version": "1.4.0", "build_date": "2025-04-11", "github_repo": "Zippland/Snap-Solver" } \ No newline at end of file diff --git a/models/doubao.py b/models/doubao.py index 68d7925..bbce3c4 100644 --- a/models/doubao.py +++ b/models/doubao.py @@ -74,9 +74,16 @@ class DoubaoModel(BaseModel): "Content-Type": "application/json" } - # 构建消息 - 根据官方API文档,暂时不使用系统提示词 + # 构建消息 - 添加系统提示词 messages = [] + # 添加系统提示词 + if self.system_prompt: + messages.append({ + "role": "system", + "content": self.system_prompt + }) + # 添加用户查询 user_content = text if self.language and self.language != 'auto': @@ -221,6 +228,16 @@ class DoubaoModel(BaseModel): elif image_data.startswith('iVBORw0KGgo'): # PNG magic number in base64 image_format = "png" + # 构建消息 + messages = [] + + # 添加系统提示词 + if self.system_prompt: + messages.append({ + "role": "system", + "content": self.system_prompt + }) + user_content = [ { "type": "text", @@ -234,12 +251,10 @@ class DoubaoModel(BaseModel): } ] - messages = [ - { - "role": "user", - "content": user_content - } - ] + messages.append({ + "role": "user", + "content": user_content + }) # 处理推理配置 thinking = { diff --git a/models/google.py b/models/google.py index fe9f210..e135070 100644 --- a/models/google.py +++ b/models/google.py @@ -53,7 +53,7 @@ class GoogleModel(BaseModel): def get_model_identifier(self) -> str: """返回默认的模型标识符""" - return "gemini-2.5-flash" # 使用有免费配额的模型作为默认值 + return "gemini-2.0-flash" # 使用有免费配额的模型作为默认值 def analyze_text(self, text: str, proxies: dict = None) -> Generator[dict, None, None]: """流式生成文本响应""" diff --git a/static/js/main.js b/static/js/main.js index 13b2686..c25ed1f 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -657,12 +657,24 @@ class SnapSolver { case 'error': console.error('Analysis error:', data.error); - const errorMessage = data.error || 'Unknown error occurred'; + + // 安全处理错误消息,确保它是字符串 + let errorMessage = 'Unknown error occurred'; + if (data.error) { + if (typeof data.error === 'string') { + errorMessage = data.error; + } else if (typeof data.error === 'object') { + // 如果是对象,尝试获取消息字段或转换为JSON + errorMessage = data.error.message || data.error.error || JSON.stringify(data.error); + } else { + errorMessage = String(data.error); + } + } // 显示错误信息 - if (errorMessage && this.responseContent) { - const currentText = this.responseContent.textContent || ''; - this.setElementContent(this.responseContent, currentText + '\nError: ' + errorMessage); + if (this.responseContent) { + // 不要尝试获取现有内容,直接显示错误信息 + this.setElementContent(this.responseContent, 'Error: ' + errorMessage); } // 重新启用按钮 @@ -803,11 +815,35 @@ class SnapSolver { setElementContent(element, content) { if (!element) return; + // 首先确保content是字符串 + if (typeof content !== 'string') { + if (content === null || content === undefined) { + content = ''; + } else if (typeof content === 'object') { + // 对于对象,尝试获取有意义的字符串表示 + if (content.error || content.message) { + content = content.error || content.message; + } else if (content.toString && typeof content.toString === 'function' && content.toString() !== '[object Object]') { + content = content.toString(); + } else { + // 作为最后手段,使用JSON.stringify + try { + content = JSON.stringify(content, null, 2); + } catch (e) { + content = '[Complex Object]'; + } + } + } else { + content = String(content); + } + } + try { // 检查marked是否已配置 if (typeof marked === 'undefined') { console.warn('Marked库未加载,回退到纯文本显示'); - element.textContent = content; + // 即使回退到纯文本,也要保留换行和基本格式 + element.innerHTML = content.replace(/\n/g, '
'); return; } @@ -825,8 +861,8 @@ class SnapSolver { } } catch (error) { console.error('Markdown解析错误:', error); - // 发生错误时回退到纯文本显示 - element.textContent = content; + // 发生错误时也保留换行格式 + element.innerHTML = content.replace(/\n/g, '
'); } // 自动滚动到底部 @@ -868,7 +904,14 @@ class SnapSolver { dragMode: 'move', aspectRatio: NaN, modal: true, - background: true + background: true, + ready: function() { + // 如果有上次保存的裁剪框数据,应用它 + if (self.lastCropBoxData) { + self.cropper.setCropBoxData(self.lastCropBoxData); + console.log('Applied saved crop box data'); + } + } }); } catch (error) { console.error('Failed to initialize cropper', error); @@ -940,6 +983,9 @@ class SnapSolver { setupCropEvents() { // 移除裁剪按钮的点击事件监听 + // 存储裁剪框数据 + this.lastCropBoxData = null; + // Crop confirm button document.getElementById('cropConfirm').addEventListener('click', () => { if (!this.checkConnectionBeforeAction()) return; @@ -957,6 +1003,9 @@ class SnapSolver { const cropBoxData = this.cropper.getCropBoxData(); console.log('Crop box data:', cropBoxData); + // 保存裁剪框数据以便下次使用 + this.lastCropBoxData = cropBoxData; + if (!cropBoxData || typeof cropBoxData.width !== 'number' || typeof cropBoxData.height !== 'number') { throw new Error('Invalid crop box data'); } @@ -1008,6 +1057,22 @@ class SnapSolver { this.updateImageActionButtons(); window.uiManager.showToast('裁剪成功'); + + // 获取当前模型信息 + const settings = window.settingsManager.getSettings(); + const supportsMultimodal = settings.modelInfo?.supportsMultimodal || false; + + // 如果模型支持多模态,自动发送至AI + if (supportsMultimodal) { + setTimeout(() => { + // 显示Claude分析面板 + this.claudePanel.classList.remove('hidden'); + this.emptyState.classList.add('hidden'); + + // 发送图像到Claude进行分析 + this.sendImageToClaude(this.croppedImage); + }, 500); // 短暂延迟以确保UI更新 + } } catch (error) { console.error('Cropping error details:', { message: error.message, @@ -1036,6 +1101,15 @@ class SnapSolver { this.imagePreview.classList.add('hidden'); document.querySelector('.crop-area').innerHTML = ''; }); + + // Crop reset button + document.getElementById('cropReset').addEventListener('click', () => { + if (this.cropper) { + // 重置裁剪区域到默认状态 + this.cropper.reset(); + window.uiManager.showToast('已重置裁剪区域'); + } + }); } setupAnalysisEvents() { @@ -1167,7 +1241,7 @@ class SnapSolver { } }); } catch (error) { - this.responseContent.textContent = 'Error: Failed to send text for analysis - ' + error.message; + this.setElementContent(this.responseContent, 'Error: Failed to send text for analysis - ' + error.message); this.sendExtractedTextBtn.disabled = false; window.uiManager.showToast('发送文本进行分析失败', 'error'); } @@ -1346,7 +1420,7 @@ class SnapSolver { // 注意:Claude面板的显示已经在点击事件中处理,这里不再重复 } catch (error) { - this.responseContent.textContent = 'Error: ' + error.message; + this.setElementContent(this.responseContent, 'Error: ' + error.message); window.uiManager.showToast('发送图片分析失败', 'error'); this.sendToClaudeBtn.disabled = false; } @@ -1453,6 +1527,8 @@ class SnapSolver { smartLists: true, // 使用比原生markdown更智能的列表行为 smartypants: false, // 不要使用更智能的标点符号 xhtml: false, // 不使用自闭合标签 + mangle: false, // 不混淆邮箱地址 + headerIds: false, // 不生成header ID highlight: function(code, lang) { // 如果highlight.js不可用,直接返回代码 if (typeof hljs === 'undefined') { @@ -1478,7 +1554,7 @@ class SnapSolver { return code; // 使用默认编码效果 } }); - + // 配置hljs以支持自动语言检测 try { hljs.configure({ diff --git a/static/style.css b/static/style.css index 2df0e26..2641e51 100644 --- a/static/style.css +++ b/static/style.css @@ -22,7 +22,8 @@ --highlight-bg-color-dark: #2d333b; --button-hover: #f3f4f6; --button-active: #ebecf0; - --header-height: 60px; + --header-height: 40px; + --header-height: 40px; --transition-speed: 0.3s; --screen-md: 768px; --screen-sm: 480px; @@ -131,12 +132,19 @@ body { /* Header Styles */ .app-header { background-color: var(--surface); - padding: 0.75rem 1rem; + padding: 0.3rem 1rem; + padding: 0.3rem 1rem; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); z-index: 100; position: sticky; top: 0; border-bottom: 1px solid var(--border-color); + height: var(--header-height); + display: flex; + align-items: center; + height: var(--header-height); + display: flex; + align-items: center; } .header-content { @@ -149,6 +157,12 @@ body { width: 100%; } +.header-middle { + display: flex; + align-items: center; + gap: 0.5rem; +} + .app-header h1 { font-size: 1.3rem; color: var(--primary); @@ -163,7 +177,7 @@ body { font-size: 0.8rem; padding: 0.2rem 0.5rem; border-radius: 1rem; - margin: 0 0.5rem; + margin: 0; white-space: nowrap; font-weight: 600; display: inline-flex; @@ -206,11 +220,14 @@ body { .header-buttons { display: flex; gap: 0.5rem; + align-items: center; /* 确保按钮垂直居中 */ + align-items: center; /* 确保按钮垂直居中 */ } .header-buttons .btn-icon { width: 36px; - height: 36px; + height: 32px; + height: 32px; border-radius: 50%; display: flex; align-items: center; @@ -221,6 +238,16 @@ body { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } +.capture-btn-highlight { + background-color: var(--primary) !important; + color: white !important; + box-shadow: 0 4px 12px rgba(var(--primary-rgb), 0.3) !important; + width: auto !important; + min-width: 100px !important; + border-radius: 18px !important; + padding: 0 8px !important; +} + .header-buttons .btn-icon:hover { transform: translateY(-2px); background-color: var(--hover-color); @@ -236,8 +263,10 @@ body { .app-main { flex: 1; display: flex; - padding: 1.5rem; - gap: 1.5rem; + padding: 0.5rem 1rem; /* 减小垂直内边距和水平内边距 */ + gap: 1rem; + padding: 0.5rem 1rem; /* 减小垂直内边距和水平内边距 */ + gap: 1rem; position: relative; overflow: hidden; background-color: var(--background); @@ -375,20 +404,27 @@ body { .btn-action { background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%); color: white; - padding: 0.6rem 1rem; + padding: 0.8rem 1.5rem; + padding: 0.8rem 1.5rem; border: none; - border-radius: 0.4rem; - font-size: 0.85rem; + border-radius: 2rem; + font-size: 1rem; + border-radius: 2rem; + font-size: 1rem; font-weight: 600; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; - gap: 0.4rem; + gap: 0.5rem; + gap: 0.5rem; transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); position: relative; overflow: hidden; + min-width: 180px; + min-width: 180px; } .btn-action i { @@ -515,6 +551,10 @@ body { font-size: 1.1rem; } + .header-middle { + gap: 0.3rem; + } + #connectionStatus { font-size: 0.7rem; padding: 0.15rem 0.4rem; @@ -525,6 +565,12 @@ body { height: 32px; } + .capture-btn-highlight { + min-width: 90px !important; + padding: 0 6px !important; + font-size: 0.85rem !important; + } + .toolbar-buttons { flex-direction: row; gap: 0.5rem; @@ -548,9 +594,40 @@ body { } .claude-panel { - border-radius: 0.75rem; - min-height: 200px; - } + border-radius: 0.75rem; + min-height: 200px; +} + +.claude-panel .response-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.25rem 0.75rem; /* 调整垂直内边距 */ + background-color: var(--surface-alt); + border-bottom: 1px solid var(--border-color); + border-top-left-radius: 0.75rem; + border-top-right-radius: 0.75rem; + font-weight: 600; + color: var(--text-primary); + font-size: 1rem; +} + border-radius: 0.75rem; + min-height: 200px; +} + +.claude-panel .response-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.25rem 0.75rem; /* 调整垂直内边距 */ + background-color: var(--surface-alt); + border-bottom: 1px solid var(--border-color); + border-top-left-radius: 0.75rem; + border-top-right-radius: 0.75rem; + font-weight: 600; + color: var(--text-primary); + font-size: 1rem; +} .response-content { padding: 1rem; @@ -590,10 +667,20 @@ body { font-size: 1rem; } + .header-middle { + gap: 0.2rem; + } + + .header-middle .btn-icon { + width: 28px; + height: 28px; + } + #connectionStatus { max-width: 70px; text-overflow: ellipsis; overflow: hidden; + font-size: 0.65rem; } .header-buttons .btn-icon { @@ -602,6 +689,12 @@ body { font-size: 0.85rem; } + .capture-btn-highlight { + min-width: 80px !important; + padding: 0 4px !important; + font-size: 0.8rem !important; + } + .toolbar-buttons { justify-content: center; } @@ -684,6 +777,10 @@ body { transition: all 0.3s ease; overflow: visible; width: 100%; + position: relative; + margin-bottom: 1.5rem; + position: relative; + margin-bottom: 1.5rem; } .claude-panel:not(.hidden) { @@ -705,7 +802,8 @@ body { display: flex; justify-content: space-between; align-items: center; - padding: 1rem 1.25rem; + padding: 0.25rem 1.25rem; /* 进一步减小垂直内边距 */ + padding: 0.25rem 1.25rem; /* 进一步减小垂直内边距 */ border-bottom: 1px solid var(--border-color); background-color: var(--surface); position: sticky; @@ -1603,6 +1701,26 @@ button:disabled { background-color: var(--surface); } +.crop-actions-top { + position: absolute; + top: 0; + left: 0; + right: 0; + z-index: 10; + background-color: rgba(0, 0, 0, 0.7); + border-bottom: 1px solid rgba(255, 255, 255, 0.2); +} + +.crop-actions-top { + position: absolute; + top: 0; + left: 0; + right: 0; + z-index: 10; + background-color: rgba(0, 0, 0, 0.7); + border-bottom: 1px solid rgba(255, 255, 255, 0.2); +} + /* Animations */ @keyframes toast-in { from { @@ -2948,10 +3066,17 @@ button:disabled { .capture-btn-highlight { background-color: #ff5a5f !important; color: white !important; - transform: scale(1.15); - box-shadow: 0 0 8px rgba(255, 90, 95, 0.5); - border-radius: 50%; + /* transform: scale(1.3); */ /* 移除整体缩放 */ + box-shadow: 0 0 12px rgba(255, 90, 95, 0.6); + border-radius: 0.5rem; /* 调整为长条状的圆角 */ + /* transform: scale(1.3); */ /* 移除整体缩放 */ + box-shadow: 0 0 12px rgba(255, 90, 95, 0.6); + border-radius: 0.5rem; /* 调整为长条状的圆角 */ position: relative; + width: 100px !important; /* 增加宽度 */ + height: 36px !important; /* 保持与普通按钮相同的高度 */ + width: 100px !important; /* 增加宽度 */ + height: 36px !important; /* 保持与普通按钮相同的高度 */ } .capture-btn-highlight i { @@ -2960,12 +3085,14 @@ button:disabled { .capture-btn-highlight:hover { background-color: #ff7e82 !important; - transform: scale(1.2); + /* transform: scale(1.2); */ + /* transform: scale(1.2); */ box-shadow: 0 0 12px rgba(255, 90, 95, 0.7); } .capture-btn-highlight:active { - transform: scale(1.1); + /* transform: scale(1.1); */ + /* transform: scale(1.1); */ box-shadow: 0 0 5px rgba(255, 90, 95, 0.4); } diff --git a/templates/index.html b/templates/index.html index 0a2f075..269970d 100644 --- a/templates/index.html +++ b/templates/index.html @@ -32,17 +32,20 @@

Snap Solver v{{ update_info.current_version }}

-
未连接
-
- +
+
未连接
+
+
+
@@ -61,6 +64,68 @@
+ + + +
@@ -100,37 +165,6 @@
- -