Spaces:
Sleeping
Sleeping
| <html lang="en" data-theme="light"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"> | |
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
| <!-- Safari兼容性设置 --> | |
| <meta name="apple-mobile-web-app-capable" content="yes"> | |
| <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | |
| <meta name="mobile-web-app-capable" content="yes"> | |
| <title>Snap Solver</title> | |
| <link rel="icon" href="/static/favicon.ico"> | |
| <link rel="stylesheet" href="/static/style.css"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.css"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script> | |
| <script> | |
| // 帮助Safari调试 | |
| window.onerror = function(message, source, lineno, colno, error) { | |
| console.error("Error caught: ", message, "at", source, ":", lineno, ":", colno, error); | |
| return false; | |
| }; | |
| </script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.js"></script> | |
| <!-- 添加Markdown解析库 --> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <!-- 添加代码高亮库 --> | |
| <link rel="stylesheet" href="https://unpkg.com/@highlightjs/cdn-assets@11.8.0/styles/github.min.css"> | |
| <script src="https://unpkg.com/@highlightjs/cdn-assets@11.8.0/highlight.min.js"></script> | |
| </head> | |
| <body class="app-container"> | |
| <header class="app-header"> | |
| <div class="header-content"> | |
| <h1>Snap Solver <span class="version-badge">v<span id="currentVersion">{{ update_info.current_version }}</span></span></h1> | |
| <div class="header-middle"> | |
| <button id="themeToggle" class="btn-icon" title="切换主题"> | |
| <i class="fas fa-moon"></i> | |
| </button> | |
| <button id="settingsToggle" class="btn-icon" title="设置"> | |
| <i class="fas fa-cog"></i> | |
| </button> | |
| <div id="connectionStatus" class="status disconnected">未连接</div> | |
| </div> | |
| <div class="header-buttons"> | |
| <button id="captureBtn" class="btn-icon capture-btn-highlight" title="截图" disabled> | |
| <i class="fas fa-camera"></i> | |
| <span>开始截图</span> | |
| </button> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- 更新通知条 --> | |
| <div id="updateNotice" class="update-notice hidden"> | |
| <div class="update-notice-content"> | |
| <i class="fas fa-arrow-alt-circle-up"></i> | |
| <span>发现新版本: <span id="updateVersion"></span></span> | |
| <a id="updateLink" href="#" target="_blank" class="update-link">查看更新</a> | |
| <button id="closeUpdateNotice" class="btn-icon"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <main class="app-main"> | |
| <div class="content-panel"> | |
| <div id="claudePanel" class="claude-panel hidden"> | |
| <div class="panel-header"> | |
| <div class="header-title"> | |
| <h2><i class="fas fa-chart-bar"></i> 分析结果</h2> | |
| <div class="analysis-indicator"> | |
| <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> | |
| </button> | |
| </div> | |
| <div id="thinkingSection" class="thinking-section hidden"> | |
| <div class="thinking-header" id="thinkingToggle" title="点击查看AI思考过程"> | |
| <div class="thinking-title"> | |
| <i class="fas fa-brain"></i> | |
| <h3>思考过程<span class="dots-animation"></span></h3> | |
| </div> | |
| <button class="toggle-btn"> | |
| <i class="fas fa-chevron-down"></i> | |
| </button> | |
| </div> | |
| <div id="thinkingContent" class="thinking-content collapsed"></div> | |
| </div> | |
| <div id="responseContent" class="response-content"></div> | |
| </div> | |
| <div id="claudePanel" class="claude-panel hidden"> | |
| <div class="panel-header"> | |
| <div class="header-title"> | |
| <h2><i class="fas fa-chart-bar"></i> 分析结果</h2> | |
| <div class="analysis-indicator"> | |
| <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> | |
| </button> | |
| </div> | |
| <div id="thinkingSection" class="thinking-section hidden"> | |
| <div class="thinking-header" id="thinkingToggle" title="点击查看AI思考过程"> | |
| <div class="thinking-title"> | |
| <i class="fas fa-brain"></i> | |
| <h3>思考过程<span class="dots-animation"></span></h3> | |
| </div> | |
| <button class="toggle-btn"> | |
| <i class="fas fa-chevron-down"></i> | |
| </button> | |
| </div> | |
| <div id="thinkingContent" class="thinking-content collapsed"></div> | |
| </div> | |
| <div id="responseContent" class="response-content"></div> | |
| </div> | |
| <div class="capture-section"> | |
| <div id="emptyState" class="empty-state"> | |
| <i class="fas fa-camera-retro"></i> | |
| <h3>准备好开始了吗?</h3> | |
| <p>点击顶部状态栏的"相机"图标捕获屏幕,然后使用AI分析图像或提取文本。您可以截取数学题、代码或任何需要帮助的内容。</p> | |
| <p class="star-prompt">如果觉得好用,别忘了给Github点个 Star ⭐</p> | |
| <div class="empty-state-social"> | |
| <a href="https://github.com/Zippland/Snap-Solver/" target="_blank" class="social-link github-link"> | |
| <span>GitHub</span> | |
| </a> | |
| <a href="https://www.xiaohongshu.com/user/profile/623e8b080000000010007721" target="_blank" class="social-link xiaohongshu-link"> | |
| <span>小红书</span> | |
| </a> | |
| </div> | |
| </div> | |
| <div id="imagePreview" class="image-preview hidden"> | |
| <div class="image-container"> | |
| <img id="screenshotImg" src="" alt="截图预览"> | |
| </div> | |
| <div class="analysis-button"> | |
| <div class="button-group"> | |
| <button id="sendToClaude" class="btn-action hidden"> | |
| <i class="fas fa-robot"></i> | |
| <span>发送至AI</span> | |
| </button> | |
| <button id="extractText" class="btn-action hidden"> | |
| <i class="fas fa-font"></i> | |
| <span>提取文本</span> | |
| </button> | |
| </div> | |
| </div> | |
| <textarea id="extractedText" class="extracted-text-area hidden" rows="6" placeholder="提取的文本将显示在这里..."></textarea> | |
| <button id="sendExtractedText" class="btn-action send-text-btn hidden"> | |
| <i class="fas fa-paper-plane"></i> | |
| <span>发送文本至AI</span> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="clipboard-panel"> | |
| <div class="clipboard-header"> | |
| <h3><i class="fas fa-clipboard"></i> 剪贴板操作</h3> | |
| <p class="clipboard-hint">读取宿主机剪贴板内容或发送内容到服务端剪贴板</p> | |
| </div> | |
| <textarea id="clipboardText" rows="4" placeholder="剪贴板内容将显示在这里,也可以手动输入内容"></textarea> | |
| <div class="clipboard-actions"> | |
| <button id="clipboardRead" class="btn-action clipboard-read-btn" type="button"> | |
| <i class="fas fa-download"></i> | |
| <span>读取剪贴板</span> | |
| </button> | |
| <button id="clipboardSend" class="btn-action clipboard-send-btn" type="button"> | |
| <i class="fas fa-clipboard-check"></i> | |
| <span>发送至剪贴板</span> | |
| </button> | |
| <span id="clipboardStatus" class="clipboard-status" aria-live="polite"></span> | |
| </div> | |
| </div> | |
| </div> | |
| <aside id="settingsPanel" class="settings-panel"> | |
| <div class="settings-header"> | |
| <h2><i class="fas fa-cog"></i> 设置</h2> | |
| <button id="closeSettings" class="btn-icon"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="settings-content"> | |
| <!-- 1. 首先是最常用的AI模型选择部分 --> | |
| <div class="settings-section model-settings"> | |
| <h3><i class="fas fa-robot"></i> 模型设置</h3> | |
| <div class="setting-group"> | |
| <div class="model-control"> | |
| <label for="modelSelect"><i class="fas fa-microchip"></i> AI模型</label> | |
| <!-- 简化模型选择器结构 --> | |
| <div class="model-selector" id="modelSelector"> | |
| <div class="model-display"> | |
| <div class="model-display-icon"> | |
| <i class="fas fa-robot"></i> | |
| </div> | |
| <div class="model-display-info"> | |
| <div class="model-display-name" id="currentModelName">选择模型</div> | |
| <div class="model-display-provider" id="currentModelProvider"></div> | |
| </div> | |
| <div class="model-display-badges" id="modelBadges"> | |
| <!-- 能力图标由JS生成 --> | |
| </div> | |
| <i class="fas fa-chevron-down model-selector-arrow"></i> | |
| </div> | |
| </div> | |
| <!-- 保留原始下拉框用于保持兼容性 --> | |
| <select id="modelSelect" class="hidden"> | |
| <!-- 选项通过JS添加 --> | |
| </select> | |
| <div id="modelVersionInfo" class="model-version-info"> | |
| <i class="fas fa-info-circle"></i> <span id="modelVersionText">-</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="setting-group"> | |
| <div class="token-control"> | |
| <div class="token-label"> | |
| <label for="maxTokens"><i class="fas fa-text-width"></i> 最大输出Token</label> | |
| </div> | |
| <div class="token-slider-container"> | |
| <input type="range" id="maxTokens" class="token-slider" min="1000" max="128000" step="1000" value="8192"> | |
| <span class="token-value" id="maxTokensValue">8192</span> | |
| </div> | |
| <div class="token-markers"> | |
| <span>1K</span> | |
| <span>32K</span> | |
| <span>64K</span> | |
| <span>96K</span> | |
| <span>128K</span> | |
| </div> | |
| <div class="token-presets"> | |
| <button class="token-preset" data-value="4000">简短</button> | |
| <button class="token-preset" data-value="16000">标准</button> | |
| <button class="token-preset" data-value="64000">详细</button> | |
| <button class="token-preset" data-value="128000">最大</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="setting-group reasoning-setting-group"> | |
| <div class="reasoning-control"> | |
| <div class="reasoning-label"> | |
| <label for="reasoningDepth"><i class="fas fa-brain"></i> 推理深度</label> | |
| </div> | |
| <div class="reasoning-selector"> | |
| <div class="reasoning-option" data-value="standard"> | |
| <i class="fas fa-bolt"></i> | |
| <span class="option-name">标准模式</span> | |
| <span class="option-desc">快速响应,即时生成</span> | |
| </div> | |
| <div class="reasoning-option" data-value="extended"> | |
| <i class="fas fa-lightbulb"></i> | |
| <span class="option-name">深度思考</span> | |
| <span class="option-desc">更详细的分析与推理</span> | |
| </div> | |
| </div> | |
| <select id="reasoningDepth" class="hidden"> | |
| <option value="standard">标准模式 (快速响应)</option> | |
| <option value="extended">深度思考 (更详细分析)</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="setting-group doubao-thinking-group" style="display: none;"> | |
| <div class="doubao-thinking-control"> | |
| <div class="doubao-thinking-label"> | |
| <label for="doubaoThinkingMode"><i class="fas fa-cogs"></i> 豆包深度思考模式</label> | |
| </div> | |
| <div class="doubao-thinking-selector"> | |
| <div class="doubao-thinking-option active" data-value="auto"> | |
| <i class="fas fa-magic"></i> | |
| <span class="option-name">自动模式</span> | |
| <span class="option-desc">由AI自动决定是否使用深度思考</span> | |
| </div> | |
| <div class="doubao-thinking-option" data-value="enabled"> | |
| <i class="fas fa-brain"></i> | |
| <span class="option-name">开启思考</span> | |
| <span class="option-desc">强制启用深度思考过程</span> | |
| </div> | |
| <div class="doubao-thinking-option" data-value="disabled"> | |
| <i class="fas fa-bolt"></i> | |
| <span class="option-name">关闭思考</span> | |
| <span class="option-desc">禁用深度思考,快速响应</span> | |
| </div> | |
| </div> | |
| <select id="doubaoThinkingMode" class="hidden"> | |
| <option value="auto">自动模式</option> | |
| <option value="enabled">开启思考</option> | |
| <option value="disabled">关闭思考</option> | |
| </select> | |
| <div class="doubao-thinking-desc"> | |
| <div class="doubao-desc-item"> | |
| <i class="fas fa-info-circle"></i> | |
| <span><strong>自动模式:</strong>AI根据问题复杂度自动决定</span> | |
| </div> | |
| <div class="doubao-desc-item"> | |
| <i class="fas fa-lightbulb"></i> | |
| <span><strong>开启思考:</strong>显示完整的思考推理过程</span> | |
| </div> | |
| <div class="doubao-desc-item"> | |
| <i class="fas fa-rocket"></i> | |
| <span><strong>关闭思考:</strong>直接给出答案,响应更快</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="setting-group think-budget-group"> | |
| <div class="think-budget-control"> | |
| <div class="think-budget-label"> | |
| <label for="thinkBudgetPercent"><i class="fas fa-hourglass-half"></i> 思考预算占比</label> | |
| </div> | |
| <div class="think-slider-container"> | |
| <input type="range" id="thinkBudgetPercent" class="think-slider" min="10" max="80" step="5" value="50"> | |
| <span class="think-value-badge" id="thinkBudgetPercentValue">50%</span> | |
| </div> | |
| <div class="think-budget-markers"> | |
| <span>10%</span> | |
| <span>30%</span> | |
| <span>50%</span> | |
| <span>70%</span> | |
| <span>80%</span> | |
| </div> | |
| <div class="think-budget-presets"> | |
| <button class="think-preset" data-value="20">少量</button> | |
| <button class="think-preset" data-value="50">平衡</button> | |
| <button class="think-preset" data-value="70">深入</button> | |
| </div> | |
| <div class="think-budget-desc"> | |
| <div class="think-desc-item"> | |
| <i class="fas fa-tachometer-alt"></i> | |
| <span>低占比 = 更快响应速度</span> | |
| </div> | |
| <div class="think-desc-item"> | |
| <i class="fas fa-search-plus"></i> | |
| <span>高占比 = 更深入的分析</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 已删除重复的豆包思考模式UI元素 --> | |
| <div class="setting-group"> | |
| <div class="temperature-control"> | |
| <div class="temperature-label"> | |
| <label for="temperature"><i class="fas fa-thermometer-half"></i> 温度</label> | |
| </div> | |
| <input type="range" id="temperature" class="temperature-slider" min="0" max="1" step="0.1" value="0.7"> | |
| <div class="temperature-markers"> | |
| <span>0</span> | |
| <span>0.2</span> | |
| <span>0.4</span> | |
| <span>0.6</span> | |
| <span>0.8</span> | |
| <span>1</span> | |
| </div> | |
| <div class="temperature-description"> | |
| <span class="temperature-low">精确</span> | |
| <span class="temperature-high">创意</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 系统提示词部分 - 简化设计 --> | |
| <div class="settings-section prompt-settings"> | |
| <h3><i class="fas fa-comment-alt"></i> 系统提示词</h3> | |
| <div class="setting-group prompt-setting-group"> | |
| <div class="prompt-container"> | |
| <div class="prompt-actions"> | |
| <select id="promptSelect" title="选择预设提示词"> | |
| </select> | |
| <div class="prompt-buttons"> | |
| <button id="savePromptBtn" class="icon-btn" title="编辑当前提示词"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| <button id="newPromptBtn" class="icon-btn" title="新建提示词"> | |
| <i class="fas fa-plus"></i> | |
| </button> | |
| <button id="deletePromptBtn" class="icon-btn" title="删除当前提示词"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="prompt-preview"> | |
| <div id="promptDescription" class="prompt-description"> | |
| <p>您是一位专业的问题解决专家。请逐步分析问题,找出问题所在,并提供详细的解决方案。始终使用用户偏好的语言回答。</p> | |
| </div> | |
| <div class="prompt-preview-overlay"> | |
| <div class="prompt-edit-hint"> | |
| <!-- 移除重复的编辑图标 --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <textarea id="systemPrompt" class="hidden">您是一位专业的问题解决专家。请逐步分析问题,找出问题所在,并提供详细的解决方案。始终使用用户偏好的语言回答。</textarea> | |
| </div> | |
| </div> | |
| <!-- OCR设置部分 --> | |
| <div class="settings-section ocr-settings"> | |
| <h3><i class="fas fa-font"></i> OCR 源设置</h3> | |
| <div class="setting-group"> | |
| <div class="ocr-source-control"> | |
| <div class="ocr-source-selector"> | |
| <select id="ocrSourceSelect" class="ocr-source-select"> | |
| <option value="auto">自动选择</option> | |
| <option value="baidu">百度OCR</option> | |
| <option value="mathpix">Mathpix</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 2. 所有API密钥集中在一个区域 --> | |
| <div class="settings-section api-key-settings"> | |
| <h3><i class="fas fa-key"></i> API密钥设置</h3> | |
| <!-- API密钥状态显示与编辑区域 --> | |
| <div class="api-keys-list" id="apiKeysList"> | |
| <div class="api-key-status"> | |
| <span class="key-name">Anthropic API:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="AnthropicApiKeyStatus" class="key-status" data-key="AnthropicApiKey">未设置</span> | |
| <button class="btn-icon edit-api-key" data-key-type="AnthropicApiKey" title="编辑此密钥"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="password" class="key-input" data-key-type="AnthropicApiKey" placeholder="输入 Anthropic API key"> | |
| <button class="btn-icon toggle-visibility"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button class="btn-icon save-api-key" data-key-type="AnthropicApiKey" title="保存密钥"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-status"> | |
| <span class="key-name">OpenAI API:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="OpenaiApiKeyStatus" class="key-status" data-key="OpenaiApiKey">未设置</span> | |
| <button class="btn-icon edit-api-key" data-key-type="OpenaiApiKey" title="编辑此密钥"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="password" class="key-input" data-key-type="OpenaiApiKey" placeholder="输入 OpenAI API key"> | |
| <button class="btn-icon toggle-visibility"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button class="btn-icon save-api-key" data-key-type="OpenaiApiKey" title="保存密钥"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-status"> | |
| <span class="key-name">DeepSeek API:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="DeepseekApiKeyStatus" class="key-status" data-key="DeepseekApiKey">未设置</span> | |
| <button class="btn-icon edit-api-key" data-key-type="DeepseekApiKey" title="编辑此密钥"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="password" class="key-input" data-key-type="DeepseekApiKey" placeholder="输入 DeepSeek API key"> | |
| <button class="btn-icon toggle-visibility"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button class="btn-icon save-api-key" data-key-type="DeepseekApiKey" title="保存密钥"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-status"> | |
| <span class="key-name">Alibaba API:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="AlibabaApiKeyStatus" class="key-status" data-key="AlibabaApiKey">未设置</span> | |
| <button class="btn-icon edit-api-key" data-key-type="AlibabaApiKey" title="编辑此密钥"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="password" class="key-input" data-key-type="AlibabaApiKey" placeholder="输入 Alibaba API key"> | |
| <button class="btn-icon toggle-visibility"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button class="btn-icon save-api-key" data-key-type="AlibabaApiKey" title="保存密钥"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-status"> | |
| <span class="key-name">Google API:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="GoogleApiKeyStatus" class="key-status" data-key="GoogleApiKey">未设置</span> | |
| <button class="btn-icon edit-api-key" data-key-type="GoogleApiKey" title="编辑此密钥"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="password" class="key-input" data-key-type="GoogleApiKey" placeholder="输入 Google API key"> | |
| <button class="btn-icon toggle-visibility"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button class="btn-icon save-api-key" data-key-type="GoogleApiKey" title="保存密钥"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-status"> | |
| <span class="key-name">Doubao API:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="DoubaoApiKeyStatus" class="key-status" data-key="DoubaoApiKey">未设置</span> | |
| <button class="btn-icon edit-api-key" data-key-type="DoubaoApiKey" title="编辑此密钥"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="password" class="key-input" data-key-type="DoubaoApiKey" placeholder="输入Doubao API key"> | |
| <button class="btn-icon toggle-visibility"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button class="btn-icon save-api-key" data-key-type="DoubaoApiKey" title="保存密钥"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 百度OCR API Key配置 --> | |
| <div class="api-key-status"> | |
| <span class="key-name">百度OCR API Key:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="BaiduApiKeyStatus" class="key-status" data-key="BaiduApiKey">未设置</span> | |
| <button class="btn-icon edit-api-key" data-key-type="BaiduApiKey" title="编辑此密钥"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="password" class="key-input" data-key-type="BaiduApiKey" placeholder="输入百度OCR API Key"> | |
| <button class="btn-icon toggle-visibility"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button class="btn-icon save-api-key" data-key-type="BaiduApiKey" title="保存密钥"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-status"> | |
| <span class="key-name">百度OCR Secret Key:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="BaiduSecretKeyStatus" class="key-status" data-key="BaiduSecretKey">未设置</span> | |
| <button class="btn-icon edit-api-key" data-key-type="BaiduSecretKey" title="编辑此密钥"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="password" class="key-input" data-key-type="BaiduSecretKey" placeholder="输入百度OCR Secret Key"> | |
| <button class="btn-icon toggle-visibility"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button class="btn-icon save-api-key" data-key-type="BaiduSecretKey" title="保存密钥"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-status"> | |
| <span class="key-name">Mathpix App ID:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="MathpixAppIdStatus" class="key-status" data-key="MathpixAppId">未设置</span> | |
| <button class="btn-icon edit-api-key" data-key-type="MathpixAppId" title="编辑此密钥"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="password" class="key-input" data-key-type="MathpixAppId" placeholder="输入 Mathpix App ID"> | |
| <button class="btn-icon toggle-visibility"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button class="btn-icon save-api-key" data-key-type="MathpixAppId" title="保存密钥"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-status"> | |
| <span class="key-name">Mathpix App Key:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="MathpixAppKeyStatus" class="key-status" data-key="MathpixAppKey">未设置</span> | |
| <button class="btn-icon edit-api-key" data-key-type="MathpixAppKey" title="编辑此密钥"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="password" class="key-input" data-key-type="MathpixAppKey" placeholder="输入 Mathpix App Key"> | |
| <button class="btn-icon toggle-visibility"> | |
| <i class="fas fa-eye"></i> | |
| </button> | |
| <button class="btn-icon save-api-key" data-key-type="MathpixAppKey" title="保存密钥"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 添加中转 API url 设置区域 --> | |
| <div class="settings-section api-url-settings"> | |
| <h3><i class="fas fa-link"></i> 中转 API url 设置</h3> | |
| <div class="setting-group"> | |
| <div class="api-keys-list" id="apiBaseUrlsList"> | |
| <div class="api-key-status"> | |
| <span class="key-name">Anthropic API URL:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="AnthropicApiBaseUrlStatus" class="key-status" data-key="AnthropicApiBaseUrl">未设置</span> | |
| <button class="btn-icon edit-api-base-url" data-key-type="AnthropicApiBaseUrl" title="编辑此URL"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="text" class="key-input" data-key-type="AnthropicApiBaseUrl" placeholder="https://api.anthropic.com/v1"> | |
| <button class="btn-icon save-api-base-url" data-key-type="AnthropicApiBaseUrl" title="保存URL"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-status"> | |
| <span class="key-name">OpenAI API URL:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="OpenaiApiBaseUrlStatus" class="key-status" data-key="OpenaiApiBaseUrl">未设置</span> | |
| <button class="btn-icon edit-api-base-url" data-key-type="OpenaiApiBaseUrl" title="编辑此URL"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="text" class="key-input" data-key-type="OpenaiApiBaseUrl" placeholder="https://api.openai.com/v1"> | |
| <button class="btn-icon save-api-base-url" data-key-type="OpenaiApiBaseUrl" title="保存URL"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-status"> | |
| <span class="key-name">DeepSeek API URL:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="DeepseekApiBaseUrlStatus" class="key-status" data-key="DeepseekApiBaseUrl">未设置</span> | |
| <button class="btn-icon edit-api-base-url" data-key-type="DeepseekApiBaseUrl" title="编辑此URL"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="text" class="key-input" data-key-type="DeepseekApiBaseUrl" placeholder="https://api.deepseek.com/v1"> | |
| <button class="btn-icon save-api-base-url" data-key-type="DeepseekApiBaseUrl" title="保存URL"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-status"> | |
| <span class="key-name">Alibaba API URL:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="AlibabaApiBaseUrlStatus" class="key-status" data-key="AlibabaApiBaseUrl">未设置</span> | |
| <button class="btn-icon edit-api-base-url" data-key-type="AlibabaApiBaseUrl" title="编辑此URL"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="text" class="key-input" data-key-type="AlibabaApiBaseUrl" placeholder="https://dashscope.aliyuncs.com/api/v1"> | |
| <button class="btn-icon save-api-base-url" data-key-type="AlibabaApiBaseUrl" title="保存URL"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-status"> | |
| <span class="key-name">Google API URL:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="GoogleApiBaseUrlStatus" class="key-status" data-key="GoogleApiBaseUrl">未设置</span> | |
| <button class="btn-icon edit-api-base-url" data-key-type="GoogleApiBaseUrl" title="编辑此URL"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="text" class="key-input" data-key-type="GoogleApiBaseUrl" placeholder="https://generativelanguage.googleapis.com/v1beta"> | |
| <button class="btn-icon save-api-base-url" data-key-type="GoogleApiBaseUrl" title="保存URL"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="api-key-status"> | |
| <span class="key-name">Doubao API URL:</span> | |
| <div class="key-status-wrapper"> | |
| <!-- 显示状态 --> | |
| <div class="key-display"> | |
| <span id="DoubaoApiBaseUrlStatus" class="key-status" data-key="DoubaoApiBaseUrl">未设置</span> | |
| <button class="btn-icon edit-api-base-url" data-key-type="DoubaoApiBaseUrl" title="编辑此URL"> | |
| <i class="fas fa-edit"></i> | |
| </button> | |
| </div> | |
| <!-- 编辑状态 --> | |
| <div class="key-edit hidden"> | |
| <input type="text" class="key-input" data-key-type="DoubaoApiBaseUrl" placeholder="https://ark.cn-beijing.volces.com/api/v3"> | |
| <button class="btn-icon save-api-base-url" data-key-type="DoubaoApiBaseUrl" title="保存URL"> | |
| <i class="fas fa-save"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 3. 不常用的其他设置放在后面 --> | |
| <div class="settings-section proxy-settings-section"> | |
| <h3><i class="fas fa-cog"></i> 其他设置</h3> | |
| <div class="setting-group"> | |
| <label for="language"><i class="fas fa-language"></i> 语言</label> | |
| <input type="text" id="language" value="中文" placeholder="输入首选语言"> | |
| </div> | |
| <div class="setting-group"> | |
| <label class="checkbox-label"> | |
| <input type="checkbox" id="proxyEnabled"> | |
| <span>启用 VPN 代理</span> | |
| </label> | |
| </div> | |
| <div id="proxySettings" class="proxy-settings"> | |
| <div class="setting-group"> | |
| <label for="proxyHost"><i class="fas fa-server"></i> 代理主机</label> | |
| <input type="text" id="proxyHost" value="127.0.0.1" placeholder="输入代理主机"> | |
| </div> | |
| <div class="setting-group"> | |
| <label for="proxyPort"><i class="fas fa-plug"></i> 代理端口</label> | |
| <input type="number" id="proxyPort" value="4780" placeholder="输入代理端口"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </aside> | |
| </main> | |
| <div id="cropContainer" class="crop-container hidden"> | |
| <div class="crop-actions crop-actions-top"> | |
| <button id="cropCancel" class="btn-secondary"> | |
| <i class="fas fa-times"></i> | |
| <span>取消</span> | |
| </button> | |
| <div class="crop-bottom-buttons"> | |
| <button id="cropReset" class="btn-secondary crop-half-btn"> | |
| <i class="fas fa-undo"></i> | |
| <span>重置</span> | |
| </button> | |
| <button id="cropConfirm" class="btn-secondary crop-half-btn"> | |
| <i class="fas fa-check"></i> | |
| <span>确认</span> | |
| </button> | |
| </div> | |
| <button id="cropSendToAI" class="btn-primary"> | |
| <i class="fas fa-paper-plane"></i> | |
| <span>发送</span> | |
| </button> | |
| </div> | |
| <div class="crop-wrapper"> | |
| <div class="crop-area"></div> | |
| </div> | |
| </div> | |
| <div id="toastContainer" class="toast-container"></div> | |
| <footer class="app-footer"> | |
| <div class="footer-content"> | |
| <div class="footer-text"> | |
| <span>© 2024 Snap-Solver</span> | |
| </div> | |
| <div class="footer-links"> | |
| <a href="https://github.com/Zippland/Snap-Solver/" target="_blank" class="footer-link"> | |
| <span class="star-icon">⭐</span> | |
| <span>GitHub</span> | |
| </a> | |
| <a href="https://www.xiaohongshu.com/user/profile/623e8b080000000010007721?xsec_token=YBdeHZTp_aVwi1Ijmras5CgQC6pxlpd4RmozT8Hr_-NCA%3D&xsec_source=app_share&xhsshare=CopyLink&appuid=623e8b080000000010007721&apptime=1742201089&share_id=a2704ab48e2c4e1aa321ce63168811b5&share_channel=copy_link" target="_blank" class="footer-link xiaohongshu-link"> | |
| <i class="fas fa-book"></i> | |
| <span>小红书</span> | |
| </a> | |
| </div> | |
| </div> | |
| </footer> | |
| <!-- 提示词对话框 --> | |
| <div class="dialog-overlay" id="promptDialogOverlay"></div> | |
| <div class="prompt-dialog" id="promptDialog"> | |
| <h3>添加/编辑提示词</h3> | |
| <div class="form-group"> | |
| <label for="promptId">提示词ID</label> | |
| <input type="text" id="promptId" placeholder="英文字母和下划线,如math_problems"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="promptName">名称</label> | |
| <input type="text" id="promptName" placeholder="提示词名称"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="promptContent">内容</label> | |
| <textarea id="promptContent" placeholder="输入提示词内容..."></textarea> | |
| </div> | |
| <div class="form-group"> | |
| <label for="promptDescriptionEdit">描述(可选)</label> | |
| <input type="text" id="promptDescriptionEdit" placeholder="简短描述"> | |
| </div> | |
| <div class="dialog-buttons"> | |
| <button class="cancel-btn" id="cancelPromptBtn">取消</button> | |
| <button class="save-btn" id="confirmPromptBtn">保存</button> | |
| </div> | |
| </div> | |
| <!-- 确保按照正确的顺序加载脚本 --> | |
| <!-- 先加载UI管理器,确保它能在DOM加载完成后初始化 --> | |
| <script src="{{ url_for('static', filename='js/ui.js') }}"></script> | |
| <!-- 然后加载设置管理器,它依赖UI管理器 --> | |
| <script src="{{ url_for('static', filename='js/settings.js') }}"></script> | |
| <!-- 最后加载主应用逻辑 --> | |
| <script src="{{ url_for('static', filename='js/main.js') }}"></script> | |
| <!-- 更新检查初始化 --> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // 初始化更新检查 | |
| try { | |
| const updateInfo = JSON.parse('{{ update_info|tojson|safe }}'); | |
| if (updateInfo && updateInfo.has_update) { | |
| showUpdateNotice(updateInfo); | |
| } | |
| // 24小时后再次检查更新 | |
| setTimeout(checkForUpdates, 24 * 60 * 60 * 1000); | |
| } catch (error) { | |
| console.error('更新检查初始化失败:', error); | |
| } | |
| }); | |
| function showUpdateNotice(updateInfo) { | |
| const updateNotice = document.getElementById('updateNotice'); | |
| const updateVersion = document.getElementById('updateVersion'); | |
| const updateLink = document.getElementById('updateLink'); | |
| if (updateInfo.latest_version) { | |
| updateVersion.textContent = updateInfo.latest_version; | |
| } | |
| if (updateInfo.release_url) { | |
| updateLink.href = updateInfo.release_url; | |
| } else { | |
| updateLink.href = 'https://github.com/Zippland/Snap-Solver/releases/latest'; | |
| } | |
| updateNotice.classList.remove('hidden'); | |
| // 绑定关闭按钮事件 | |
| document.getElementById('closeUpdateNotice').addEventListener('click', function() { | |
| updateNotice.classList.add('hidden'); | |
| // 记住用户已关闭此版本的通知 | |
| localStorage.setItem('dismissedUpdate', updateInfo.latest_version); | |
| }); | |
| } | |
| function checkForUpdates() { | |
| fetch('/api/check-update') | |
| .then(function(response) { return response.json(); }) | |
| .then(function(updateInfo) { | |
| const dismissedVersion = localStorage.getItem('dismissedUpdate'); | |
| // 只有当有更新且用户没有关闭过此版本的通知时才显示 | |
| if (updateInfo.has_update && dismissedVersion !== updateInfo.latest_version) { | |
| showUpdateNotice(updateInfo); | |
| } | |
| }) | |
| .catch(function(error) { console.error('检查更新失败:', error); }); | |
| } | |
| </script> | |
| </body> | |
| </html> | |