准备好开始了吗?
-点击"截图"按钮捕获屏幕,然后使用AI分析图像或提取文本。您可以截取数学题、代码或任何需要帮助的内容。
+点击顶部状态栏的"相机"图标捕获屏幕,然后使用AI分析图像或提取文本。您可以截取数学题、代码或任何需要帮助的内容。
如果觉得好用,别忘了给项目点个 Star ⭐
diff --git a/static/js/main.js b/static/js/main.js
index fbb6502..440bdef 100644
--- a/static/js/main.js
+++ b/static/js/main.js
@@ -8,9 +8,34 @@ class SnapSolver {
this.originalImage = null;
this.croppedImage = null;
this.extractedContent = '';
- this.emitTimeout = null;
this.eventsSetup = false;
+ // Cache DOM elements
+ this.captureBtn = document.getElementById('captureBtn');
+ // 移除裁剪按钮引用
+ this.screenshotImg = document.getElementById('screenshotImg');
+ this.imagePreview = document.getElementById('imagePreview');
+ this.emptyState = document.getElementById('emptyState');
+ this.extractedText = document.getElementById('extractedText');
+ this.cropContainer = document.getElementById('cropContainer');
+ this.sendToClaudeBtn = document.getElementById('sendToClaude');
+ this.extractTextBtn = document.getElementById('extractText');
+ this.sendExtractedTextBtn = document.getElementById('sendExtractedText');
+ this.claudePanel = document.getElementById('claudePanel');
+ this.responseContent = document.getElementById('responseContent');
+ this.thinkingContent = document.getElementById('thinkingContent');
+ this.thinkingSection = document.getElementById('thinkingSection');
+ this.thinkingToggle = document.getElementById('thinkingToggle');
+ this.connectionStatus = document.getElementById('connectionStatus');
+ this.statusLight = document.querySelector('.status-light');
+ this.progressLine = document.querySelector('.progress-line');
+ this.statusText = document.querySelector('.status-text');
+ this.analysisIndicator = document.querySelector('.analysis-indicator');
+
+ // Crop elements
+ this.cropCancel = document.getElementById('cropCancel');
+ this.cropConfirm = document.getElementById('cropConfirm');
+
// 初始化应用
this.initialize();
}
@@ -20,11 +45,10 @@ class SnapSolver {
this.screenshotImg = document.getElementById('screenshotImg');
this.imagePreview = document.getElementById('imagePreview');
this.emptyState = document.getElementById('emptyState');
- this.cropBtn = document.getElementById('cropBtn');
+ // 移除对裁剪按钮的引用
this.captureBtn = document.getElementById('captureBtn');
this.sendToClaudeBtn = document.getElementById('sendToClaude');
this.extractTextBtn = document.getElementById('extractText');
- this.textEditor = document.getElementById('textEditor');
this.extractedText = document.getElementById('extractedText');
this.sendExtractedTextBtn = document.getElementById('sendExtractedText');
this.cropContainer = document.getElementById('cropContainer');
@@ -36,6 +60,9 @@ class SnapSolver {
this.thinkingToggle = document.getElementById('thinkingToggle');
this.connectionStatus = document.getElementById('connectionStatus');
this.statusLight = document.querySelector('.status-light');
+ this.progressLine = document.querySelector('.progress-line');
+ this.statusText = document.querySelector('.status-text');
+ this.analysisIndicator = document.querySelector('.analysis-indicator');
// Crop elements
this.cropCancel = document.getElementById('cropCancel');
@@ -47,7 +74,6 @@ class SnapSolver {
this.cropper = null;
this.croppedImage = null;
this.extractedContent = '';
- this.emitTimeout = null;
// 确保裁剪容器和其他面板初始为隐藏状态
if (this.cropContainer) {
@@ -97,21 +123,51 @@ class SnapSolver {
}
updateStatusLight(status) {
- this.statusLight.className = 'status-light';
- switch (status) {
- case 'started':
- case 'streaming':
- this.statusLight.classList.add('processing');
- break;
- case 'completed':
- this.statusLight.classList.add('completed');
- break;
- case 'error':
- this.statusLight.classList.add('error');
- break;
- default:
- // Reset to default state
- break;
+ // 更新状态指示器
+ if (this.analysisIndicator) {
+ this.analysisIndicator.className = 'analysis-indicator';
+ this.progressLine.className = 'progress-line';
+
+ switch (status) {
+ case 'started':
+ case 'streaming':
+ this.analysisIndicator.classList.add('processing');
+ this.progressLine.classList.add('processing');
+ this.statusText.textContent = '处理中...';
+ break;
+ case 'completed':
+ this.analysisIndicator.classList.add('completed');
+ this.progressLine.classList.add('completed');
+ this.statusText.textContent = '已完成';
+ break;
+ case 'error':
+ this.analysisIndicator.classList.add('error');
+ this.progressLine.classList.add('error');
+ this.statusText.textContent = '错误';
+ break;
+ default:
+ // 重置为默认状态
+ this.statusText.textContent = '准备中';
+ break;
+ }
+ } else if (this.statusLight) {
+ // 兼容旧版状态灯
+ this.statusLight.className = 'status-light';
+ switch (status) {
+ case 'started':
+ case 'streaming':
+ this.statusLight.classList.add('processing');
+ break;
+ case 'completed':
+ this.statusLight.classList.add('completed');
+ break;
+ case 'error':
+ this.statusLight.classList.add('error');
+ break;
+ default:
+ // Reset to default state
+ break;
+ }
}
}
@@ -176,7 +232,8 @@ class SnapSolver {
} catch (error) {
console.error('Connection error:', error);
this.updateConnectionStatus('重连失败', false);
- setTimeout(() => this.initializeConnection(), 5000);
+ // 移除setTimeout,让用户手动刷新页面重连
+ window.uiManager.showToast('连接服务器失败,请刷新页面重试', 'error');
}
}
@@ -206,7 +263,7 @@ class SnapSolver {
// 恢复按钮状态
this.captureBtn.disabled = false;
- this.captureBtn.innerHTML = '截图';
+ this.captureBtn.innerHTML = '';
// 初始化裁剪器
this.initializeCropper();
@@ -214,7 +271,7 @@ class SnapSolver {
window.uiManager.showToast('截图成功', 'success');
} else {
this.captureBtn.disabled = false;
- this.captureBtn.innerHTML = '截图';
+ this.captureBtn.innerHTML = '';
console.error('截图失败:', data.error);
window.uiManager.showToast('截图失败: ' + data.error, 'error');
}
@@ -223,7 +280,7 @@ class SnapSolver {
// 新版截图响应处理器
this.socket.on('screenshot_complete', (data) => {
this.captureBtn.disabled = false;
- this.captureBtn.innerHTML = '截图';
+ this.captureBtn.innerHTML = '';
if (data.success) {
// 显示截图预览
@@ -257,21 +314,17 @@ class SnapSolver {
this.extractTextBtn.disabled = false;
this.extractTextBtn.innerHTML = '提取文本';
- if (this.extractedText) {
- this.extractedText.disabled = false;
- }
-
- if (this.emitTimeout) {
- clearTimeout(this.emitTimeout);
- this.emitTimeout = null;
+ if (this.extractedText) {
+ this.extractedText.disabled = false;
}
// 检查是否有内容数据
if (data.content) {
- this.extractedText.value = data.content;
+ this.extractedText.value = data.content;
this.extractedContent = data.content;
- this.textEditor.classList.remove('hidden');
- this.sendExtractedTextBtn.disabled = false;
+ this.extractedText.classList.remove('hidden');
+ this.sendExtractedTextBtn.classList.remove('hidden');
+ this.sendExtractedTextBtn.disabled = false;
window.uiManager.showToast('文本提取成功', 'success');
} else if (data.error) {
@@ -322,35 +375,23 @@ class SnapSolver {
console.log('Received thinking content');
this.thinkingSection.classList.remove('hidden');
- // 记住用户的展开/折叠状态
- const wasExpanded = this.thinkingContent.classList.contains('expanded');
+ // 显示动态省略号
+ this.showThinkingAnimation(true);
- // 直接设置完整内容而不是追加
+ // 直接设置完整内容
this.setElementContent(this.thinkingContent, data.content);
// 添加打字动画效果
this.thinkingContent.classList.add('thinking-typing');
- // 根据之前的状态决定是否展开
- if (wasExpanded) {
- this.thinkingContent.classList.add('expanded');
- this.thinkingContent.classList.remove('collapsed');
-
- // 更新切换按钮图标
- const toggleIcon = document.querySelector('#thinkingToggle .toggle-btn i');
- if (toggleIcon) {
- toggleIcon.className = 'fas fa-chevron-up';
- }
- } else {
- // 初始状态为折叠
- this.thinkingContent.classList.add('collapsed');
- this.thinkingContent.classList.remove('expanded');
-
- // 更新切换按钮图标
- const toggleIcon = document.querySelector('#thinkingToggle .toggle-btn i');
- if (toggleIcon) {
- toggleIcon.className = 'fas fa-chevron-down';
- }
+ // 默认为折叠状态
+ this.thinkingContent.classList.add('collapsed');
+ this.thinkingContent.classList.remove('expanded');
+
+ // 更新切换按钮图标
+ const toggleIcon = document.querySelector('#thinkingToggle .toggle-btn i');
+ if (toggleIcon) {
+ toggleIcon.className = 'fas fa-chevron-down';
}
}
break;
@@ -361,35 +402,23 @@ class SnapSolver {
console.log('Received reasoning content');
this.thinkingSection.classList.remove('hidden');
- // 记住用户的展开/折叠状态
- const wasExpanded = this.thinkingContent.classList.contains('expanded');
+ // 显示动态省略号
+ this.showThinkingAnimation(true);
- // 直接设置完整内容而不是追加
+ // 直接设置完整内容
this.setElementContent(this.thinkingContent, data.content);
// 添加打字动画效果
this.thinkingContent.classList.add('thinking-typing');
- // 根据之前的状态决定是否展开
- if (wasExpanded) {
- this.thinkingContent.classList.add('expanded');
- this.thinkingContent.classList.remove('collapsed');
-
- // 更新切换按钮图标
- const toggleIcon = document.querySelector('#thinkingToggle .toggle-btn i');
- if (toggleIcon) {
- toggleIcon.className = 'fas fa-chevron-up';
- }
- } else {
- // 初始状态为折叠
- this.thinkingContent.classList.add('collapsed');
- this.thinkingContent.classList.remove('expanded');
-
- // 更新切换按钮图标
- const toggleIcon = document.querySelector('#thinkingToggle .toggle-btn i');
- if (toggleIcon) {
- toggleIcon.className = 'fas fa-chevron-down';
- }
+ // 默认为折叠状态
+ this.thinkingContent.classList.add('collapsed');
+ this.thinkingContent.classList.remove('expanded');
+
+ // 更新切换按钮图标
+ const toggleIcon = document.querySelector('#thinkingToggle .toggle-btn i');
+ if (toggleIcon) {
+ toggleIcon.className = 'fas fa-chevron-down';
}
}
break;
@@ -397,14 +426,23 @@ class SnapSolver {
case 'thinking_complete':
// 完整的思考内容
if (data.content && this.thinkingContent && this.thinkingSection) {
- console.log('Thinking complete');
+ console.log('思考过程完成');
this.thinkingSection.classList.remove('hidden');
+ // 停止动态省略号
+ this.showThinkingAnimation(false);
+
// 设置完整内容
this.setElementContent(this.thinkingContent, data.content);
// 移除打字动画
this.thinkingContent.classList.remove('thinking-typing');
+
+ // 确保图标正确显示
+ const toggleIcon = this.thinkingToggle.querySelector('.toggle-btn i');
+ if (toggleIcon) {
+ toggleIcon.className = 'fas fa-chevron-down';
+ }
}
break;
@@ -414,6 +452,9 @@ class SnapSolver {
console.log('Reasoning complete');
this.thinkingSection.classList.remove('hidden');
+ // 停止动态省略号
+ this.showThinkingAnimation(false);
+
// 设置完整内容
this.setElementContent(this.thinkingContent, data.content);
@@ -430,6 +471,9 @@ class SnapSolver {
this.setElementContent(this.responseContent, data.content);
this.responseContent.style.display = 'block';
+ // 停止省略号动画
+ this.showThinkingAnimation(false);
+
// 移除思考部分的打字动画
if (this.thinkingContent) {
this.thinkingContent.classList.remove('thinking-typing');
@@ -465,8 +509,8 @@ class SnapSolver {
toggleBtn.className = 'fas fa-chevron-down';
}
- // 添加明确的提示
- window.uiManager.showToast('分析完成,可点击"AI思考过程"查看详细思考内容', 'success');
+ // 简化提示信息
+ window.uiManager.showToast('分析完成', 'success');
} else {
// 没有思考内容,隐藏思考组件
this.thinkingSection.classList.add('hidden');
@@ -529,7 +573,12 @@ class SnapSolver {
// 显示思考区域
this.thinkingSection.classList.remove('hidden');
- this.thinkingContent.textContent = data.thinking;
+
+ // 显示动态省略号
+ this.showThinkingAnimation(true);
+
+ // 使用setElementContent方法处理Markdown
+ this.setElementContent(this.thinkingContent, data.thinking);
// 记住用户的展开/折叠状态
const wasExpanded = this.thinkingContent.classList.contains('expanded');
@@ -544,21 +593,22 @@ class SnapSolver {
}
});
- // 思考过程完成
+ // 思考过程完成 - Socket事件处理
this.socket.on('thinking_complete', (data) => {
- console.log('思考过程完成');
+ console.log('Socket接收到思考过程完成');
this.thinkingSection.classList.remove('hidden');
- this.thinkingContent.textContent = data.thinking;
+
+ // 停止动态省略号动画
+ this.showThinkingAnimation(false);
+
+ // 使用setElementContent方法处理Markdown
+ this.setElementContent(this.thinkingContent, data.thinking);
// 确保图标正确显示
const toggleIcon = this.thinkingToggle.querySelector('.toggle-btn i');
if (toggleIcon) {
toggleIcon.className = 'fas fa-chevron-down';
}
-
- // 添加提示信息
- const toast = window.uiManager.showToast('思考过程已完成,点击标题可查看详细思考过程', 'success');
- toast.style.zIndex = '1000';
});
// 分析完成
@@ -569,19 +619,19 @@ class SnapSolver {
// 显示分析结果
if (this.responseContent) {
- this.responseContent.innerHTML = data.response;
+ // 使用setElementContent方法处理Markdown
+ this.setElementContent(this.responseContent, data.response);
this.responseContent.style.display = 'block';
- // 滚动到结果区域
- setTimeout(() => {
- this.responseContent.scrollIntoView({ behavior: 'smooth' });
- }, 200);
+ // 直接滚动到结果区域,不使用setTimeout
+ this.responseContent.scrollIntoView({ behavior: 'smooth' });
}
// 确保思考部分完全显示(如果有的话)
if (data.thinking && this.thinkingSection && this.thinkingContent) {
this.thinkingSection.classList.remove('hidden');
- this.thinkingContent.textContent = data.thinking;
+ // 使用setElementContent方法处理Markdown
+ this.setElementContent(this.thinkingContent, data.thinking);
// 确保初始状态为折叠
this.thinkingContent.classList.remove('expanded');
@@ -592,7 +642,7 @@ class SnapSolver {
}
// 弹出提示
- window.uiManager.showToast('分析完成,可点击"AI思考过程"查看详细思考内容', 'success');
+ window.uiManager.showToast('分析完成', 'success');
}
});
}
@@ -601,8 +651,31 @@ class SnapSolver {
setElementContent(element, content) {
if (!element) return;
- // 直接设置内容
- element.textContent = content;
+ try {
+ // 检查marked是否已配置
+ if (typeof marked === 'undefined') {
+ console.warn('Marked库未加载,回退到纯文本显示');
+ element.textContent = content;
+ return;
+ }
+
+ // 使用marked库解析Markdown内容
+ const renderedHtml = marked.parse(content);
+
+ // 设置解析后的HTML内容
+ element.innerHTML = renderedHtml;
+
+ // 为未高亮的代码块应用语法高亮
+ if (window.hljs) {
+ element.querySelectorAll('pre code:not(.hljs)').forEach((block) => {
+ hljs.highlightElement(block);
+ });
+ }
+ } catch (error) {
+ console.error('Markdown解析错误:', error);
+ // 发生错误时回退到纯文本显示
+ element.textContent = content;
+ }
// 自动滚动到底部
element.scrollTop = element.scrollHeight;
@@ -678,26 +751,19 @@ class SnapSolver {
try {
this.captureBtn.disabled = true; // 禁用按钮防止重复点击
- this.captureBtn.innerHTML = '正在截图...';
+ this.captureBtn.innerHTML = '';
this.socket.emit('capture_screenshot', {});
} catch (error) {
console.error('Error starting capture:', error);
window.uiManager.showToast('启动截图失败', 'error');
this.captureBtn.disabled = false;
- this.captureBtn.innerHTML = '截取屏幕';
+ this.captureBtn.innerHTML = '';
}
});
}
setupCropEvents() {
- // Crop button
- this.cropBtn.addEventListener('click', () => {
- if (!this.checkConnectionBeforeAction()) return;
-
- if (this.screenshotImg.src) {
- this.initializeCropper();
- }
- });
+ // 移除裁剪按钮的点击事件监听
// Crop confirm button
document.getElementById('cropConfirm').addEventListener('click', () => {
@@ -762,12 +828,11 @@ class SnapSolver {
// Update the screenshot image with the cropped version
this.screenshotImg.src = this.croppedImage;
this.imagePreview.classList.remove('hidden');
- this.cropBtn.classList.remove('hidden');
// 根据当前选择的模型类型决定显示哪些按钮
this.updateImageActionButtons();
- window.uiManager.showToast('Cropping successful');
+ window.uiManager.showToast('裁剪成功');
} catch (error) {
console.error('Cropping error details:', {
message: error.message,
@@ -823,23 +888,16 @@ class SnapSolver {
return;
}
- // Show text editor and prepare UI
- this.textEditor.classList.remove('hidden');
+ // 显示文本框和按钮
+ this.extractedText.classList.remove('hidden');
+ this.sendExtractedTextBtn.classList.remove('hidden');
+
if (this.extractedText) {
this.extractedText.value = '正在提取文本...';
this.extractedText.disabled = true;
}
try {
- // 设置超时时间(15秒)以避免长时间无响应
- this.emitTimeout = setTimeout(() => {
- window.uiManager.showToast('文本提取超时,请重试或手动输入文本', 'error');
- this.extractTextBtn.disabled = false;
- this.extractTextBtn.innerHTML = '提取文本';
- this.extractedText.disabled = false;
- this.sendExtractedTextBtn.disabled = false;
- }, 15000);
-
this.socket.emit('extract_text', {
image: this.croppedImage.split(',')[1],
settings: {
@@ -983,22 +1041,6 @@ class SnapSolver {
}
}
- setupKeyboardShortcuts() {
- // Keyboard shortcuts for capture and crop
- document.addEventListener('keydown', (e) => {
- if (e.ctrlKey || e.metaKey) {
- switch(e.key) {
- case 'c':
- if (!this.captureBtn.disabled) this.captureBtn.click();
- break;
- case 'x':
- if (!this.cropBtn.disabled) this.cropBtn.click();
- break;
- }
- }
- });
- }
-
setupThinkingToggle() {
// 确保正确获取DOM元素
const thinkingSection = document.getElementById('thinkingSection');
@@ -1010,6 +1052,9 @@ class SnapSolver {
return;
}
+ // 初始化时隐藏动态省略号
+ this.showThinkingAnimation(false);
+
// 存储DOM引用
this.thinkingSection = thinkingSection;
this.thinkingToggle = thinkingToggle;
@@ -1034,7 +1079,6 @@ class SnapSolver {
if (toggleIcon) {
toggleIcon.className = 'fas fa-chevron-down';
}
- window.uiManager.showToast('思考内容已折叠', 'info');
} else {
console.log('展开思考内容');
// 添加展开状态
@@ -1043,9 +1087,14 @@ class SnapSolver {
toggleIcon.className = 'fas fa-chevron-up';
}
- // 移除自动滚动到底部的代码,让用户自行控制滚动位置
-
- window.uiManager.showToast('思考内容已展开', 'info');
+ // 当展开思考内容时,确保代码高亮生效
+ if (window.hljs) {
+ setTimeout(() => {
+ thinkingContent.querySelectorAll('pre code').forEach((block) => {
+ hljs.highlightElement(block);
+ });
+ }, 100); // 添加一点延迟,确保DOM已完全更新
+ }
}
};
@@ -1109,6 +1158,9 @@ class SnapSolver {
window.settingsManager = new SettingsManager();
window.app = this; // 便于从其他地方访问
+ // 初始化Markdown工具
+ this.initializeMarkdownTools();
+
// 建立与服务器的连接
this.connectToServer();
@@ -1140,20 +1192,74 @@ class SnapSolver {
}
});
+ // 监听页面卸载事件,清除所有计时器
+ window.addEventListener('beforeunload', this.cleanup.bind(this));
+
// 设置默认UI状态
this.enableInterface();
// 更新图像操作按钮
this.updateImageActionButtons();
- // 确保DOM完全加载后再次更新按钮状态(以防设置未完全加载)
- setTimeout(() => {
- this.updateImageActionButtons();
- console.log('延时更新图像操作按钮完成');
- }, 1000);
-
console.log('SnapSolver initialization complete');
}
+
+ // 初始化Markdown工具
+ initializeMarkdownTools() {
+ // 检查marked是否可用
+ if (typeof marked === 'undefined') {
+ console.warn('Marked.js 未加载,Markdown渲染将不可用');
+ return;
+ }
+
+ // 初始化marked设置
+ marked.setOptions({
+ gfm: true, // 启用GitHub风格的Markdown
+ breaks: true, // 将换行符转换为
+ pedantic: false, // 不使用原始markdown规范
+ sanitize: false, // 不要过滤HTML标签,允许一些HTML
+ smartLists: true, // 使用比原生markdown更智能的列表行为
+ smartypants: false, // 不要使用更智能的标点符号
+ xhtml: false, // 不使用自闭合标签
+ highlight: function(code, lang) {
+ // 如果highlight.js不可用,直接返回代码
+ if (typeof hljs === 'undefined') {
+ return code;
+ }
+
+ // 如果指定了语言且hljs支持
+ if (lang && hljs.getLanguage(lang)) {
+ try {
+ return hljs.highlight(code, { language: lang }).value;
+ } catch (err) {
+ console.error('代码高亮错误:', err);
+ }
+ }
+
+ // 尝试自动检测语言
+ try {
+ return hljs.highlightAuto(code).value;
+ } catch (err) {
+ console.error('自动语言检测错误:', err);
+ }
+
+ return code; // 使用默认编码效果
+ }
+ });
+
+ // 检查highlight.js是否可用
+ if (typeof hljs === 'undefined') {
+ console.warn('Highlight.js 未加载,代码高亮将不可用');
+ return;
+ }
+
+ // 配置hljs以支持自动语言检测
+ hljs.configure({
+ languages: ['javascript', 'python', 'java', 'cpp', 'csharp', 'html', 'css', 'json', 'xml', 'markdown', 'bash']
+ });
+
+ console.log('Markdown工具初始化完成');
+ }
handleResize() {
// 如果裁剪器存在,需要调整其大小和位置
@@ -1168,7 +1274,7 @@ class SnapSolver {
// 启用主要界面元素
if (this.captureBtn) {
this.captureBtn.disabled = false;
- this.captureBtn.innerHTML = '截图';
+ this.captureBtn.innerHTML = '';
}
// 显示默认的空白状态
@@ -1307,6 +1413,79 @@ class SnapSolver {
console.warn('按钮元素不可用');
}
}
+
+ checkClickOutside() {
+ // 点击其他区域时自动关闭悬浮窗
+ document.addEventListener('click', (e) => {
+ // 检查是否点击在设置面板、设置按钮或其子元素之外
+ if (
+ !e.target.closest('#settingsPanel') &&
+ !e.target.matches('#settingsToggle') &&
+ !e.target.closest('#settingsToggle') &&
+ !document.getElementById('settingsPanel').classList.contains('hidden')
+ ) {
+ document.getElementById('settingsPanel').classList.add('hidden');
+ }
+
+ // 检查是否点击在Claude面板、分析按钮或其子元素之外
+ if (
+ !e.target.closest('#claudePanel') &&
+ !e.target.matches('#sendToClaude') &&
+ !e.target.closest('#sendToClaude') &&
+ !e.target.matches('#extractText') &&
+ !e.target.closest('#extractText') &&
+ !e.target.matches('#sendExtractedText') &&
+ !e.target.closest('#sendExtractedText') &&
+ !this.claudePanel.classList.contains('hidden')
+ ) {
+ // 因为分析可能正在进行,不自动关闭Claude面板
+ // 但是可以考虑增加一个最小化功能
+ }
+ });
+ }
+
+ // 新增清理方法,移除计时器相关代码
+ cleanup() {
+ console.log('执行清理操作...');
+
+ // 清除所有Socket监听器
+ if (this.socket) {
+ this.socket.off('text_extracted');
+ this.socket.off('screenshot_response');
+ this.socket.off('screenshot_complete');
+ this.socket.off('request_acknowledged');
+ this.socket.off('claude_response');
+ this.socket.off('thinking');
+ this.socket.off('thinking_complete');
+ this.socket.off('analysis_complete');
+ }
+
+ // 销毁裁剪器实例
+ if (this.cropper) {
+ this.cropper.destroy();
+ this.cropper = null;
+ }
+
+ console.log('清理完成');
+ }
+
+ // 空方法替代键盘快捷键实现
+ setupKeyboardShortcuts() {
+ // 移动端应用不需要键盘快捷键
+ console.log('键盘快捷键已禁用(移动端应用)');
+ }
+
+ // 控制思考动态省略号显示
+ showThinkingAnimation(show) {
+ const dotsElement = document.querySelector('.thinking-title .dots-animation');
+ if (dotsElement) {
+ if (show) {
+ dotsElement.style.display = 'inline-block';
+ } else {
+ dotsElement.style.display = 'none';
+ }
+ }
+ }
}
// Initialize the application when the DOM is loaded
diff --git a/static/style.css b/static/style.css
index 39bf139..d7fb90c 100644
--- a/static/style.css
+++ b/static/style.css
@@ -255,9 +255,9 @@ body {
display: flex;
flex-direction: column;
align-items: center;
- gap: 1.5rem;
+ gap: 1rem;
margin-top: 1.5rem;
- padding: 1.5rem;
+ padding: 1rem;
background-color: var(--surface-alt);
border-radius: 0.75rem;
transition: all 0.3s ease;
@@ -265,24 +265,139 @@ body {
.analysis-button .button-group {
display: flex;
- gap: 1rem;
+ gap: 0.6rem;
width: 100%;
max-width: 500px;
justify-content: center;
}
+/* 提取的文本框样式 */
+.extracted-text-area {
+ width: 100%;
+ min-height: 150px;
+ padding: 1rem;
+ margin-top: 1.5rem;
+ border: 1.5px solid var(--border-color);
+ border-radius: 0.5rem;
+ background-color: var(--background);
+ color: var(--text-primary);
+ font-size: 1rem;
+ resize: vertical;
+ transition: all 0.2s ease;
+ line-height: 1.5;
+ max-width: 900px;
+ margin-left: auto;
+ margin-right: auto;
+ display: block;
+}
+
+.extracted-text-area:focus {
+ outline: none;
+ border-color: var(--primary);
+ box-shadow: 0 0 0 3px rgba(74, 108, 247, 0.15);
+}
+
+/* 发送文本按钮样式 */
+.send-text-btn {
+ margin-top: 1rem;
+ margin-left: auto;
+ margin-right: auto;
+ display: block;
+ min-width: 150px;
+ justify-content: center;
+ padding: 0.6rem 1rem;
+}
+
+@media (min-width: 769px) {
+ .send-text-btn {
+ margin-left: auto;
+ margin-right: 0;
+ max-width: 900px;
+ }
+
+ .extracted-text-area {
+ margin-bottom: 0;
+ }
+}
+
+@media (max-width: 480px) {
+ .extracted-text-area {
+ min-height: 120px;
+ padding: 0.75rem;
+ font-size: 0.9rem;
+ margin-top: 1rem;
+ }
+
+ .send-text-btn {
+ width: 100%;
+ padding: 0.6rem 0.8rem;
+ font-size: 0.8rem;
+ }
+}
+
+/* 图片操作按钮新样式 */
+.btn-action {
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
+ color: white;
+ padding: 0.6rem 1rem;
+ border: none;
+ border-radius: 0.4rem;
+ font-size: 0.85rem;
+ font-weight: 600;
+ cursor: pointer;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.4rem;
+ transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ position: relative;
+ overflow: hidden;
+}
+
+.btn-action i {
+ font-size: 1rem;
+}
+
+.btn-action:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
+ filter: brightness(1.05);
+}
+
+.btn-action:active {
+ transform: translateY(0);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.btn-action::after {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0));
+ opacity: 0;
+ transition: opacity 0.25s ease;
+}
+
+.btn-action:hover::after {
+ opacity: 1;
+}
+
.text-editor {
width: 100%;
display: flex;
flex-direction: column;
- gap: 1.5rem;
+ gap: 1rem;
background-color: var(--surface);
- padding: 1.5rem;
- border-radius: 0.75rem;
+ padding: 1rem;
+ border-radius: 0.5rem;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
border: 1px solid var(--border-color);
transition: all 0.3s ease;
- margin-top: 1.5rem;
+ margin-top: 1rem;
}
.text-editor:hover {
@@ -311,9 +426,10 @@ body {
/* 发送文本按钮样式 */
#sendExtractedText {
- width: 100%;
+ width: auto;
+ min-width: 120px;
justify-content: center;
- padding: 0.875rem;
+ padding: 0.6rem 1rem;
margin-top: 0.5rem;
}
@@ -402,7 +518,7 @@ body {
.response-content {
padding: 1rem;
- font-size: 0.95rem;
+ font-size: 0.85rem;
}
.thinking-content {
@@ -472,10 +588,10 @@ body {
}
.text-editor {
- padding: 1rem;
+ padding: 0.8rem;
border-radius: 0.5rem;
margin-top: 1rem;
- gap: 1rem;
+ gap: 0.8rem;
}
.text-editor textarea {
@@ -490,16 +606,40 @@ body {
}
#sendExtractedText {
- padding: 0.75rem;
+ padding: 0.6rem 0.8rem;
+ font-size: 0.8rem;
+ width: 100%;
+ align-self: stretch;
+ }
+
+ .btn-action {
+ padding: 0.5rem 0.8rem;
+ font-size: 0.75rem;
+ }
+
+ .btn-action i {
font-size: 0.9rem;
}
+
+ .analysis-button {
+ padding: 0.8rem;
+ }
+
+ .analysis-button .button-group {
+ gap: 0.5rem;
+ }
+
+ .response-content {
+ padding: 0.9rem;
+ font-size: 0.82rem;
+ }
}
/* Claude Panel */
.claude-panel {
background-color: var(--surface);
- border-radius: 1rem;
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+ border-radius: 0.75rem;
+ box-shadow: 0 3px 15px rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: column;
height: auto;
@@ -511,13 +651,13 @@ body {
}
.claude-panel:not(.hidden) {
- animation: panel-slide-in 0.4s cubic-bezier(0.19, 1, 0.22, 1) forwards;
+ animation: panel-slide-in 0.3s cubic-bezier(0.19, 1, 0.22, 1) forwards;
}
@keyframes panel-slide-in {
0% {
opacity: 0;
- transform: translateY(20px);
+ transform: translateY(15px);
}
100% {
opacity: 1;
@@ -529,84 +669,405 @@ body {
display: flex;
justify-content: space-between;
align-items: center;
- padding: 1.25rem;
+ padding: 1rem 1.25rem;
border-bottom: 1px solid var(--border-color);
background-color: var(--surface);
position: sticky;
top: 0;
z-index: 5;
+ backdrop-filter: blur(8px);
}
.header-title {
display: flex;
- align-items: center;
- gap: 1rem;
+ flex-direction: column;
+ gap: 0.5rem;
}
-.analysis-status {
+.header-title h2 {
+ font-size: 1.1rem;
+ font-weight: 600;
+ color: var(--text-primary);
+ margin: 0;
display: flex;
align-items: center;
+ gap: 0.5rem;
}
-.status-light {
- width: 12px;
- height: 12px;
- border-radius: 50%;
- background-color: var(--text-secondary);
- transition: background-color 0.3s ease;
- box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.05);
+.header-title h2 i {
+ color: var(--primary);
+ font-size: 1rem;
}
-.status-light.processing {
- background-color: #ffd700;
- animation: pulse 1.5s infinite;
- box-shadow: 0 0 0 2px rgba(255, 215, 0, 0.2);
+/* 新的状态指示器 */
+.analysis-indicator {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ font-size: 0.75rem;
+ color: var(--text-secondary);
}
-.status-light.completed {
+.progress-line {
+ height: 2px;
+ width: 60px;
+ background-color: var(--border-color);
+ border-radius: 1px;
+ overflow: hidden;
+ position: relative;
+}
+
+.progress-line::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+ width: 0%;
+ background-color: var(--primary);
+ border-radius: 1px;
+ transition: width 0.3s ease;
+}
+
+.progress-line.processing::before {
+ animation: progress-animation 2s ease-in-out infinite;
+}
+
+.progress-line.completed::before {
+ width: 100%;
background-color: var(--success);
- box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2);
}
-.status-light.error {
+.progress-line.error::before {
+ width: 100%;
background-color: var(--danger);
- box-shadow: 0 0 0 2px rgba(244, 67, 54, 0.2);
}
-@keyframes pulse {
+@keyframes progress-animation {
0% {
- opacity: 1;
- box-shadow: 0 0 0 2px rgba(255, 215, 0, 0.2);
+ width: 0%;
+ left: 0;
}
50% {
- opacity: 0.6;
- box-shadow: 0 0 0 4px rgba(255, 215, 0, 0.4);
+ width: 50%;
+ left: 25%;
}
100% {
- opacity: 1;
- box-shadow: 0 0 0 2px rgba(255, 215, 0, 0.2);
+ width: 0%;
+ left: 100%;
}
}
+.status-text {
+ font-weight: 500;
+ white-space: nowrap;
+ transition: color 0.3s ease;
+}
+
+.analysis-indicator.processing .status-text {
+ color: var(--primary);
+}
+
+.analysis-indicator.completed .status-text {
+ color: var(--success);
+}
+
+.analysis-indicator.error .status-text {
+ color: var(--danger);
+}
+
+/* Thinking Section */
+.thinking-section {
+ margin: 0.25rem 0 0.1rem 0;
+ border-top: none;
+ border-bottom: none;
+}
+
+.thinking-header {
+ padding: 0.5rem 1rem;
+ cursor: pointer;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ transition: all 0.2s ease;
+ background-color: rgba(var(--primary-rgb), 0.04);
+ border: none;
+ border-radius: 0.35rem;
+ margin: 0.25rem 1rem 0.15rem 1rem;
+ opacity: 0.9;
+}
+
+.thinking-header:hover {
+ background-color: rgba(var(--primary-rgb), 0.08);
+ opacity: 1;
+}
+
+.thinking-title {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.thinking-title i {
+ color: var(--text-secondary);
+ font-size: 0.9rem;
+ opacity: 0.85;
+}
+
+.thinking-title h3 {
+ margin: 0;
+ font-size: 0.9rem;
+ font-weight: 500;
+ color: var(--text-secondary);
+}
+
+.toggle-btn {
+ background: none;
+ border: none;
+ cursor: pointer;
+ color: var(--text-secondary);
+ padding: 0.2rem;
+ width: 24px;
+ height: 24px;
+ border-radius: 3px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s ease;
+ opacity: 0.8;
+}
+
+.toggle-btn:hover {
+ background-color: rgba(var(--primary-rgb), 0.1);
+ color: var(--primary);
+ opacity: 1;
+}
+
+.toggle-btn i {
+ font-size: 0.8rem;
+}
+
+.thinking-content {
+ padding: 0;
+ background-color: rgba(var(--primary-rgb), 0.02);
+ border-left: 1px solid rgba(var(--primary-rgb), 0.1);
+ margin: 0 1.5rem 0.3rem 1.75rem;
+ overflow: hidden;
+ transition: all 0.3s ease;
+ font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
+ font-size: 0.85rem;
+ line-height: 1.5;
+}
+
+/* 思考进行中的动态省略号 */
+@keyframes thinking-dots {
+ 0% { content: ''; }
+ 25% { content: '.'; }
+ 50% { content: '..'; }
+ 75% { content: '...'; }
+ 100% { content: ''; }
+}
+
+.thinking-title .dots-animation {
+ display: inline-block;
+ min-width: 24px;
+ margin-left: 4px;
+ height: 1em;
+ position: relative;
+}
+
+.thinking-title .dots-animation::after {
+ content: '';
+ animation: thinking-dots 1.5s infinite;
+ position: absolute;
+ left: 0;
+ color: var(--primary);
+ font-weight: bold;
+}
+
.response-content {
- padding: 1.5rem;
- background-color: #fff;
- border-radius: 6px;
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
- line-height: 1.6;
- white-space: pre-wrap;
+ padding: 1rem 1.5rem;
+ background-color: transparent;
+ border-radius: 0;
+ box-shadow: none;
+ line-height: 1.5;
+ white-space: normal;
overflow-wrap: break-word;
- margin-top: 1rem;
- font-size: 1rem;
+ margin-top: 0.1rem;
+ font-size: 0.9rem;
height: auto;
width: 100%;
}
+[data-theme="dark"] .response-content {
+ background-color: transparent;
+ box-shadow: none;
+}
+
+/* 添加Markdown样式 */
+.response-content h1,
+.response-content h2,
+.response-content h3,
+.response-content h4,
+.response-content h5,
+.response-content h6 {
+ margin-top: 1.3rem;
+ margin-bottom: 0.8rem;
+ font-weight: 600;
+ line-height: 1.25;
+ color: var(--text-primary);
+}
+
+.response-content h1 {
+ font-size: 1.8em;
+ border-bottom: 1px solid var(--border-color);
+ padding-bottom: 0.3em;
+}
+
+.response-content h2 {
+ font-size: 1.4em;
+ border-bottom: 1px solid var(--border-color);
+ padding-bottom: 0.3em;
+}
+
+.response-content h3 {
+ font-size: 1.2em;
+}
+
+.response-content h4 {
+ font-size: 1em;
+}
+
+.response-content h5 {
+ font-size: 0.875em;
+}
+
+.response-content h6 {
+ font-size: 0.85em;
+ color: var(--text-secondary);
+}
+
+.response-content p {
+ margin-top: 0;
+ margin-bottom: 0.8rem;
+}
+
+.response-content a {
+ color: var(--primary);
+ text-decoration: none;
+}
+
+.response-content a:hover {
+ text-decoration: underline;
+}
+
+.response-content blockquote {
+ margin: 0 0 0.8rem;
+ padding: 0 0.8rem;
+ color: var(--text-secondary);
+ border-left: 0.25rem solid var(--border-color);
+}
+
+.response-content code {
+ font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+ font-size: 0.9em;
+ padding: 0.2em 0.4em;
+ margin: 0;
+ background-color: rgba(var(--surface-alt-rgb), 0.5);
+ border-radius: 3px;
+}
+
+.response-content pre {
+ background-color: rgba(var(--surface-alt-rgb), 0.5);
+ border-radius: 6px;
+ padding: 0.8rem;
+ overflow: auto;
+ margin-bottom: 0.8rem;
+}
+
+.response-content pre code {
+ background-color: transparent;
+ padding: 0;
+ margin: 0;
+ font-size: 0.85em;
+ white-space: pre;
+ overflow-wrap: normal;
+}
+
+.response-content ul,
+.response-content ol {
+ margin-top: 0;
+ margin-bottom: 0.8rem;
+ padding-left: 1.8rem;
+}
+
+.response-content li + li {
+ margin-top: 0.2rem;
+}
+
+.response-content table {
+ display: block;
+ width: 100%;
+ overflow: auto;
+ border-spacing: 0;
+ border-collapse: collapse;
+ margin-bottom: 0.8rem;
+}
+
+.response-content table th {
+ font-weight: 600;
+ background-color: var(--surface-alt);
+}
+
+.response-content table th,
+.response-content table td {
+ padding: 5px 10px;
+ border: 1px solid var(--border-color);
+}
+
+.response-content table tr {
+ background-color: var(--surface);
+ border-top: 1px solid var(--border-color);
+}
+
+.response-content table tr:nth-child(2n) {
+ background-color: var(--surface-alt);
+}
+
+.response-content img {
+ max-width: 100%;
+ box-sizing: content-box;
+ margin: 0.8rem 0;
+ border-radius: 4px;
+}
+
+/* 特殊样式:任务列表 */
+.response-content input[type="checkbox"] {
+ margin-right: 0.5rem;
+}
+
[data-theme="dark"] .response-content {
background-color: rgba(var(--surface-rgb), 0.7);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
+/* 暗色模式下的代码块样式 */
+[data-theme="dark"] .response-content pre {
+ background-color: rgba(0, 0, 0, 0.2);
+}
+
+[data-theme="dark"] .response-content code {
+ background-color: rgba(0, 0, 0, 0.2);
+}
+
+[data-theme="dark"] .response-content table th {
+ background-color: rgba(var(--surface-alt-rgb), 0.3);
+}
+
+[data-theme="dark"] .response-content table tr:nth-child(2n) {
+ background-color: rgba(var(--surface-alt-rgb), 0.2);
+}
+
/* Settings Panel */
.settings-panel {
position: fixed;
@@ -1061,100 +1522,44 @@ button:disabled {
}
}
-/* Thinking Section */
-.thinking-section {
- margin-bottom: 1rem;
- border-bottom: 1px solid var(--border-color);
+/* 应用同样的Markdown样式到思考内容 */
+.thinking-content h1,
+.thinking-content h2,
+.thinking-content h3,
+.thinking-content h4,
+.thinking-content h5,
+.thinking-content h6,
+.thinking-content p,
+.thinking-content a,
+.thinking-content blockquote,
+.thinking-content code,
+.thinking-content pre,
+.thinking-content ul,
+.thinking-content ol,
+.thinking-content table,
+.thinking-content img {
+ /* 继承响应内容的样式,但字体更小 */
+ font-size: 90%;
}
-.thinking-header {
- padding: 1rem 1.5rem;
- cursor: pointer;
- display: flex;
- justify-content: space-between;
- align-items: center;
- transition: all 0.2s ease;
- background-color: rgba(var(--primary-rgb), 0.1);
- border: 2px solid transparent;
- border-radius: 6px;
- position: relative;
-}
-
-.thinking-header::after {
- position: absolute;
- right: 60px;
- color: var(--text-secondary);
- font-size: 0.8rem;
- opacity: 0;
- transition: opacity 0.2s ease;
-}
-
-.thinking-header:hover {
- background-color: rgba(var(--primary-rgb), 0.15);
- border: 2px dashed rgba(var(--primary-rgb), 0.3);
-}
-
-.thinking-header:hover::after {
- opacity: 0.7;
-}
-
-.thinking-title {
- display: flex;
- align-items: center;
- gap: 0.75rem;
-}
-
-.thinking-title i {
- color: var(--primary);
- font-size: 1.125rem;
-}
-
-.thinking-title h3 {
- margin: 0;
- font-size: 1rem;
- font-weight: 600;
- color: var(--text-primary);
-}
-
-.toggle-btn {
- background: none;
- border: none;
- cursor: pointer;
- color: var(--text-color);
- padding: 5px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: background-color 0.3s ease;
-}
-
-.toggle-btn:hover {
- background-color: var(--hover-color);
-}
-
-.thinking-content {
- padding: 0;
- background-color: rgba(var(--primary-rgb), 0.05);
- border-left: 3px solid rgba(var(--primary-rgb), 0.3);
- overflow: hidden;
- transition: max-height 0.3s ease, padding 0.3s ease;
- font-family: monospace;
- white-space: pre-wrap;
- font-size: 0.9rem;
- line-height: 1.5;
+.thinking-content pre code {
+ white-space: pre;
+ font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
}
+/* 思考内容的展开折叠样式 */
.thinking-content.collapsed {
max-height: 0;
- padding: 0 1.5rem;
- overflow: hidden;
+ padding: 0 1.25rem;
+ margin-bottom: 0;
+ opacity: 0;
}
.thinking-content.expanded {
max-height: none;
- padding: 1rem 1.5rem;
- overflow-y: visible;
+ padding: 0.75rem 1.25rem;
+ margin-bottom: 0.3rem;
+ opacity: 1;
}
/* Animation for thinking content */
@@ -1312,14 +1717,25 @@ button:disabled {
/* 大屏幕设备的布局 */
@media (min-width: 769px) {
.text-editor {
+ flex-direction: row;
+ align-items: flex-end;
max-width: 900px;
margin: 1.5rem auto;
+ gap: 1rem;
+ }
+
+ .text-editor textarea {
+ flex: 1;
+ min-height: 120px;
+ margin-bottom: 0;
}
#sendExtractedText {
width: auto;
- min-width: 200px;
+ min-width: 150px;
align-self: flex-end;
+ margin-top: 0;
+ flex: 0 0 auto;
}
}
@@ -1590,3 +2006,170 @@ button:disabled {
gap: 0.5rem;
}
}
+
+/* 截图按钮高亮样式 */
+.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%;
+ position: relative;
+}
+
+.capture-btn-highlight i {
+ font-size: 1.2em;
+}
+
+.capture-btn-highlight:hover {
+ background-color: #ff7e82 !important;
+ transform: scale(1.2);
+ box-shadow: 0 0 12px rgba(255, 90, 95, 0.7);
+}
+
+.capture-btn-highlight:active {
+ transform: scale(1.1);
+ box-shadow: 0 0 5px rgba(255, 90, 95, 0.4);
+}
+
+/* 添加呼吸动画效果 */
+@keyframes pulse {
+ 0% {
+ box-shadow: 0 0 0 0 rgba(255, 90, 95, 0.7);
+ }
+ 70% {
+ box-shadow: 0 0 0 10px rgba(255, 90, 95, 0);
+ }
+ 100% {
+ box-shadow: 0 0 0 0 rgba(255, 90, 95, 0);
+ }
+}
+
+.capture-btn-highlight:not(:disabled) {
+ animation: pulse 2s infinite;
+}
+
+/* 暗色模式适配 */
+[data-theme="dark"] .capture-btn-highlight {
+ background-color: #ff5a5f !important;
+ color: white !important;
+}
+
+@media (min-width: 769px) {
+ .text-editor {
+ display: flex;
+ flex-direction: row;
+ align-items: flex-end;
+ flex-wrap: wrap;
+ }
+
+ .text-editor textarea {
+ flex: 1;
+ margin-right: 1rem;
+ min-height: 120px;
+ }
+
+ #sendExtractedText {
+ margin-top: 0;
+ }
+}
+
+/* 移动设备上的思考过程样式优化 */
+@media (max-width: 768px) {
+ .thinking-section {
+ margin: 0.2rem 0 0.1rem 0;
+ }
+
+ .thinking-header {
+ padding: 0.45rem 0.8rem;
+ margin: 0.2rem 0.8rem 0.1rem 0.8rem;
+ border-radius: 0.35rem;
+ }
+
+ .thinking-title i {
+ font-size: 0.85rem;
+ }
+
+ .thinking-title h3 {
+ font-size: 0.85rem;
+ }
+
+ .thinking-title .dots-animation {
+ min-width: 16px;
+ }
+
+ .toggle-btn {
+ width: 22px;
+ height: 22px;
+ }
+
+ .thinking-content {
+ margin: 0 1.25rem 0.2rem 1.5rem;
+ font-size: 0.8rem;
+ }
+
+ .thinking-content.expanded {
+ padding: 0.6rem 1rem;
+ margin-bottom: 0.2rem;
+ }
+
+ .response-content {
+ padding: 0.8rem 1rem;
+ margin-top: 0.1rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .thinking-section {
+ margin: 0.15rem 0 0.1rem 0;
+ }
+
+ .thinking-header {
+ padding: 0.4rem 0.7rem;
+ margin: 0.15rem 0.65rem 0.1rem 0.65rem;
+ }
+
+ .thinking-title {
+ gap: 0.4rem;
+ }
+
+ .thinking-title i {
+ font-size: 0.8rem;
+ }
+
+ .thinking-title h3 {
+ font-size: 0.8rem;
+ }
+
+ .thinking-content {
+ margin: 0 1.1rem 0.15rem 1.3rem;
+ }
+
+ .thinking-content.expanded {
+ padding: 0.5rem 0.8rem;
+ margin-bottom: 0.15rem;
+ }
+
+ .response-content {
+ padding: 0.7rem 0.9rem;
+ margin-top: 0.1rem;
+ }
+}
+
+/* 思考过程和回复内容的分隔 */
+.thinking-section:not(.hidden) + .response-content {
+ border-top: 1px dashed rgba(var(--primary-rgb), 0.15);
+ padding-top: 0.8rem;
+}
+
+@media (max-width: 768px) {
+ .thinking-section:not(.hidden) + .response-content {
+ padding-top: 0.7rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .thinking-section:not(.hidden) + .response-content {
+ padding-top: 0.6rem;
+ }
+}
diff --git a/templates/index.html b/templates/index.html
index 668d0b3..a914d6b 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -21,6 +21,11 @@
};
+
+
+
+
+