Files
Snap-Solver/templates/index.html

947 lines
56 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<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>