From 6b405bbf3caf9f012ce610b612a3cc3ba357ce11 Mon Sep 17 00:00:00 2001 From: HaoCheng Date: Sat, 25 Oct 2025 21:56:25 +0800 Subject: [PATCH] =?UTF-8?q?Feature:=20=E9=85=8D=E7=BD=AE=E5=89=AA=E5=88=87?= =?UTF-8?q?=E6=9D=BF=E5=8A=9F=E8=83=BD=E6=94=AF=E6=8C=81=EF=BC=8C=E7=8E=B0?= =?UTF-8?q?=E5=9C=A8=E6=94=AF=E6=8C=81=E7=BD=91=E9=A1=B5=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=AE=BF=E4=B8=BB=E6=9C=BA=E7=9A=84=E5=89=AA=E5=88=87=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 46 ++++++++++++++++++--------------------- static/js/main.js | 51 +++++++++++++++++++++++++++++++++++++++++++- static/style.css | 20 ++++++++++++++++- templates/index.html | 10 ++++++--- 4 files changed, 97 insertions(+), 30 deletions(-) diff --git a/app.py b/app.py index ee930c0..5d5fa59 100644 --- a/app.py +++ b/app.py @@ -1013,37 +1013,33 @@ def update_clipboard(): if not isinstance(text, str) or not text.strip(): return jsonify({"success": False, "message": "剪贴板内容不能为空"}), 400 - if not pyperclip.is_available(): - return jsonify({"success": False, "message": "服务器未配置剪贴板支持"}), 503 - - pyperclip.copy(text) - return jsonify({"success": True}) - except pyperclip.PyperclipException: - app.logger.exception("复制到剪贴板失败") - return jsonify({"success": False, "message": "复制到剪贴板失败,请检查服务器环境"}), 500 - except Exception: + # 直接尝试复制,不使用is_available()检查 + try: + pyperclip.copy(text) + return jsonify({"success": True}) + except Exception as e: + return jsonify({"success": False, "message": f"复制到剪贴板失败: {str(e)}"}), 500 + except Exception as e: app.logger.exception("更新剪贴板时发生异常") - return jsonify({"success": False, "message": "服务器内部错误"}), 500 + return jsonify({"success": False, "message": f"服务器内部错误: {str(e)}"}), 500 @app.route('/api/clipboard', methods=['GET']) def get_clipboard(): """从服务器剪贴板读取文本""" try: - if not pyperclip.is_available(): - return jsonify({"success": False, "message": "服务器未配置剪贴板支持"}), 503 - - text = pyperclip.paste() - if text is None: - text = "" - - return jsonify({ - "success": True, - "text": text, - "message": "成功读取剪贴板内容" - }) - except pyperclip.PyperclipException as e: - app.logger.exception("读取剪贴板失败") - return jsonify({"success": False, "message": f"读取剪贴板失败: {str(e)}"}), 500 + # 直接尝试读取,不使用is_available()检查 + try: + text = pyperclip.paste() + if text is None: + text = "" + + return jsonify({ + "success": True, + "text": text, + "message": "成功读取剪贴板内容" + }) + except Exception as e: + return jsonify({"success": False, "message": f"读取剪贴板失败: {str(e)}"}), 500 except Exception as e: app.logger.exception("读取剪贴板时发生异常") return jsonify({"success": False, "message": f"服务器内部错误: {str(e)}"}), 500 diff --git a/static/js/main.js b/static/js/main.js index b1a70e7..c927070 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -38,6 +38,7 @@ class SnapSolver { this.analysisIndicator = document.querySelector('.analysis-indicator'); this.clipboardTextarea = document.getElementById('clipboardText'); this.clipboardSendButton = document.getElementById('clipboardSend'); + this.clipboardReadButton = document.getElementById('clipboardRead'); this.clipboardStatus = document.getElementById('clipboardStatus'); // Crop elements @@ -73,6 +74,7 @@ class SnapSolver { this.stopGenerationBtn = document.getElementById('stopGenerationBtn'); this.clipboardTextarea = document.getElementById('clipboardText'); this.clipboardSendButton = document.getElementById('clipboardSend'); + this.clipboardReadButton = document.getElementById('clipboardRead'); this.clipboardStatus = document.getElementById('clipboardStatus'); // 处理按钮事件 @@ -951,16 +953,24 @@ class SnapSolver { } setupClipboardFeature() { - if (!this.clipboardTextarea || !this.clipboardSendButton) { + if (!this.clipboardTextarea || !this.clipboardSendButton || !this.clipboardReadButton) { console.warn('Clipboard controls not found in DOM'); return; } + // 读取剪贴板按钮事件 + this.clipboardReadButton.addEventListener('click', (event) => { + event.preventDefault(); + this.readClipboardText(); + }); + + // 发送到剪贴板按钮事件 this.clipboardSendButton.addEventListener('click', (event) => { event.preventDefault(); this.sendClipboardText(); }); + // 键盘快捷键:Ctrl/Cmd + Enter 发送到剪贴板 this.clipboardTextarea.addEventListener('keydown', (event) => { if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') { event.preventDefault(); @@ -1021,6 +1031,45 @@ class SnapSolver { this.clipboardStatus.dataset.status = status; } + async readClipboardText() { + if (!this.clipboardTextarea || !this.clipboardReadButton) return; + + this.updateClipboardStatus('读取中...', 'pending'); + this.clipboardReadButton.disabled = true; + + try { + const response = await fetch('/api/clipboard', { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }); + const result = await response.json().catch(() => ({})); + + if (response.ok && result?.success) { + // 将读取到的内容填入文本框 + this.clipboardTextarea.value = result.text || ''; + + const successMessage = result.text ? + `成功读取剪贴板内容 (${result.text.length} 字符)` : + '剪贴板为空'; + this.updateClipboardStatus(successMessage, 'success'); + window.uiManager?.showToast(successMessage, 'success'); + } else { + const errorMessage = result?.message || '读取失败,请稍后重试'; + this.updateClipboardStatus(errorMessage, 'error'); + window.uiManager?.showToast(errorMessage, 'error'); + } + } catch (error) { + console.error('Failed to read clipboard text:', error); + const networkErrorMessage = '网络错误,读取失败'; + this.updateClipboardStatus(networkErrorMessage, 'error'); + window.uiManager?.showToast(networkErrorMessage, 'error'); + } finally { + this.clipboardReadButton.disabled = false; + } + } + async sendClipboardText() { if (!this.clipboardTextarea) return; diff --git a/static/style.css b/static/style.css index 2a3e162..17e9f3b 100644 --- a/static/style.css +++ b/static/style.css @@ -3515,12 +3515,30 @@ textarea:focus { margin-top: 0.75rem; } -.clipboard-send-btn { +.clipboard-send-btn, +.clipboard-read-btn { display: inline-flex; align-items: center; gap: 0.5rem; } +.clipboard-read-btn { + background-color: var(--primary); + color: white; + border: 1px solid var(--primary); +} + +.clipboard-read-btn:hover { + background-color: var(--primary-dark); + border-color: var(--primary-dark); +} + +.clipboard-read-btn:disabled { + background-color: var(--text-tertiary); + border-color: var(--text-tertiary); + cursor: not-allowed; +} + .clipboard-status { font-size: 0.85rem; color: var(--text-tertiary); diff --git a/templates/index.html b/templates/index.html index dd19607..f434c9a 100644 --- a/templates/index.html +++ b/templates/index.html @@ -168,11 +168,15 @@
-

发送到服务端剪贴板

-

输入内容后点击按钮或按 Ctrl/Cmd + Enter 快速发送

+

剪贴板操作

+

读取宿主机剪贴板内容或发送内容到服务端剪贴板

- +
+