mirror of
https://github.com/Zippland/Snap-Solver.git
synced 2026-01-19 09:41:15 +08:00
367 lines
19 KiB
HTML
367 lines
19 KiB
HTML
<!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="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">
|
||
<link rel="stylesheet" href="{{ url_for('static', filename='style.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://cdn.jsdelivr.net/npm/highlight.js@11.8.0/styles/github.min.css">
|
||
<script src="https://cdn.jsdelivr.net/npm/highlight.js@11.8.0/highlight.min.js"></script>
|
||
</head>
|
||
<body class="app-container">
|
||
<header class="app-header">
|
||
<div class="header-content">
|
||
<h1>Snap Solver</h1>
|
||
<div id="connectionStatus" class="status disconnected">未连接</div>
|
||
<div class="header-buttons">
|
||
<button id="captureBtn" class="btn-icon capture-btn-highlight" title="截图" disabled>
|
||
<i class="fas fa-camera"></i>
|
||
</button>
|
||
<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>
|
||
</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 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">如果觉得好用,别忘了给项目点个 Star ⭐</p>
|
||
</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 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>
|
||
</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>
|
||
|
||
<aside id="settingsPanel" class="settings-panel hidden">
|
||
<div class="panel-header">
|
||
<h2>设置</h2>
|
||
<button class="btn-icon" id="closeSettings">
|
||
<i class="fas fa-times"></i>
|
||
</button>
|
||
</div>
|
||
<div class="settings-content">
|
||
<!-- 1. 首先是最常用的AI模型选择部分 -->
|
||
<div class="settings-section">
|
||
<h3><i class="fas fa-robot"></i> 模型设置</h3>
|
||
<div class="setting-group">
|
||
<label for="modelSelect"><i class="fas fa-microchip"></i> 选择模型</label>
|
||
<select id="modelSelect" class="select-styled">
|
||
<!-- 选项将通过JavaScript动态加载 -->
|
||
</select>
|
||
<div id="modelVersionInfo" class="model-version-info">
|
||
<i class="fas fa-info-circle"></i> 版本: <span id="modelVersionText">-</span>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label for="maxTokens"><i class="fas fa-text-width"></i> 最大输出Token</label>
|
||
<input type="number" id="maxTokens" min="1000" max="128000" step="1000" value="8192" class="input-styled">
|
||
</div>
|
||
<div class="setting-group reasoning-setting-group">
|
||
<label for="reasoningDepth"><i class="fas fa-brain"></i> 推理深度</label>
|
||
<select id="reasoningDepth" class="select-styled">
|
||
<option value="standard">标准模式 (快速响应)</option>
|
||
<option value="extended">深度思考 (更详细分析)</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-group think-budget-group">
|
||
<label for="thinkBudgetPercent"><i class="fas fa-hourglass-half"></i> 思考预算占比</label>
|
||
<div class="range-group">
|
||
<input type="range" id="thinkBudgetPercent" min="10" max="80" step="5" value="50">
|
||
<span id="thinkBudgetPercentValue">50%</span>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label for="temperature"><i class="fas fa-thermometer-half"></i> 温度</label>
|
||
<div class="range-group">
|
||
<input type="range" id="temperature" min="0" max="1" step="0.1" value="0.7">
|
||
<span id="temperatureValue">0.7</span>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group">
|
||
<label for="systemPrompt"><i class="fas fa-comment-alt"></i> 系统提示词</label>
|
||
<textarea id="systemPrompt" rows="3">您是一位专业的问题解决专家。请逐步分析问题,找出问题所在,并提供详细的解决方案。始终使用用户偏好的语言回答。</textarea>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 2. 所有API密钥集中在一个区域 -->
|
||
<div class="settings-section">
|
||
<div class="collapsible-header" id="apiKeysCollapseToggle">
|
||
<h3><i class="fas fa-key"></i> API密钥设置</h3>
|
||
<button class="toggle-btn">
|
||
<i class="fas fa-chevron-down"></i>
|
||
</button>
|
||
</div>
|
||
<div class="collapsible-content collapsed" id="apiKeysContent">
|
||
<div class="setting-group api-key-group">
|
||
<label for="AnthropicApiKey">Anthropic API Key</label>
|
||
<div class="input-group">
|
||
<input type="password" id="AnthropicApiKey" placeholder="输入 Anthropic API key">
|
||
<button class="btn-icon toggle-api-key">
|
||
<i class="fas fa-eye"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group api-key-group">
|
||
<label for="OpenaiApiKey">OpenAI API Key</label>
|
||
<div class="input-group">
|
||
<input type="password" id="OpenaiApiKey" placeholder="输入 OpenAI API key">
|
||
<button class="btn-icon toggle-api-key">
|
||
<i class="fas fa-eye"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group api-key-group">
|
||
<label for="DeepseekApiKey">DeepSeek API Key</label>
|
||
<div class="input-group">
|
||
<input type="password" id="DeepseekApiKey" placeholder="输入 DeepSeek API key">
|
||
<button class="btn-icon toggle-api-key">
|
||
<i class="fas fa-eye"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group api-key-group">
|
||
<label for="AlibabaApiKey">Alibaba API Key</label>
|
||
<div class="input-group">
|
||
<input type="password" id="AlibabaApiKey" placeholder="输入 Alibaba API key">
|
||
<button class="btn-icon toggle-api-key">
|
||
<i class="fas fa-eye"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group api-key-group">
|
||
<label for="mathpixAppId">Mathpix App ID</label>
|
||
<div class="input-group">
|
||
<input type="password" id="mathpixAppId" placeholder="输入 Mathpix App ID">
|
||
<button class="btn-icon toggle-api-key">
|
||
<i class="fas fa-eye"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="setting-group api-key-group">
|
||
<label for="mathpixAppKey">Mathpix App Key</label>
|
||
<div class="input-group">
|
||
<input type="password" id="mathpixAppKey" placeholder="输入 Mathpix App Key">
|
||
<button class="btn-icon toggle-api-key">
|
||
<i class="fas fa-eye"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 3. 不常用的其他设置放在后面 -->
|
||
<div class="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-wrapper">
|
||
<div class="crop-area"></div>
|
||
</div>
|
||
<div class="crop-actions">
|
||
<button id="cropCancel" class="btn-secondary">
|
||
<i class="fas fa-times"></i>
|
||
<span>取消</span>
|
||
</button>
|
||
<button id="cropConfirm" class="btn-primary">
|
||
<i class="fas fa-check"></i>
|
||
<span>确认</span>
|
||
</button>
|
||
</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 class="version-badge">v<span id="currentVersion">{{ update_info.current_version }}</span></span></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>
|
||
|
||
<script src="{{ url_for('static', filename='js/ui.js') }}"></script>
|
||
<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>
|