mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 10:42:25 +08:00
✨ 新增智能文字輸入框,支援從剪貼簿貼上圖片功能,並更新相關提示信息。優化用戶回饋流程,改善界面交互體驗。
This commit is contained in:
parent
9ea5b8b5b7
commit
f148841cc0
@ -45,6 +45,33 @@ class FeedbackResult(TypedDict):
|
||||
images: List[dict]
|
||||
|
||||
|
||||
# ===== 自定義文字輸入框 =====
|
||||
class SmartTextEdit(QTextEdit):
|
||||
"""支援智能 Ctrl+V 的文字輸入框"""
|
||||
image_paste_requested = Signal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""處理按鍵事件,實現智能 Ctrl+V"""
|
||||
if event.key() == Qt.Key_V and event.modifiers() == Qt.ControlModifier:
|
||||
# 檢查剪貼簿是否有圖片
|
||||
clipboard = QApplication.clipboard()
|
||||
|
||||
if clipboard.mimeData().hasImage():
|
||||
# 如果有圖片,發送信號通知主窗口處理圖片貼上
|
||||
self.image_paste_requested.emit()
|
||||
# 不執行預設的文字貼上行為
|
||||
return
|
||||
else:
|
||||
# 如果沒有圖片,執行正常的文字貼上
|
||||
super().keyPressEvent(event)
|
||||
else:
|
||||
# 其他按鍵正常處理
|
||||
super().keyPressEvent(event)
|
||||
|
||||
|
||||
# ===== 圖片預覽元件 =====
|
||||
class ImagePreviewWidget(QLabel):
|
||||
"""圖片預覽元件"""
|
||||
@ -832,9 +859,11 @@ class FeedbackWindow(QMainWindow):
|
||||
feedback_layout.addWidget(self.feedback_description)
|
||||
|
||||
# 文字輸入框
|
||||
self.feedback_input = QTextEdit()
|
||||
self.feedback_input = SmartTextEdit()
|
||||
self.feedback_input.setPlaceholderText(t('feedback_placeholder'))
|
||||
self.feedback_input.setMinimumHeight(120)
|
||||
# 連接智能貼上信號
|
||||
self.feedback_input.image_paste_requested.connect(self._handle_image_paste_from_textarea)
|
||||
feedback_layout.addWidget(self.feedback_input)
|
||||
|
||||
layout.addWidget(feedback_group, stretch=2) # 給更多空間
|
||||
@ -990,6 +1019,19 @@ class FeedbackWindow(QMainWindow):
|
||||
}
|
||||
""")
|
||||
|
||||
def _handle_image_paste_from_textarea(self) -> None:
|
||||
"""處理從文字框智能貼上圖片的功能"""
|
||||
try:
|
||||
# 調用圖片上傳組件的剪貼簿貼上功能
|
||||
self.image_upload.paste_from_clipboard()
|
||||
|
||||
# 顯示智能貼上提示
|
||||
# 可以在這裡添加狀態提示,比如狀態欄或臨時通知
|
||||
debug_log("智能貼上:已將圖片從文字框貼到圖片區域")
|
||||
|
||||
except Exception as e:
|
||||
debug_log(f"智能貼上失敗: {e}")
|
||||
|
||||
def _run_command(self) -> None:
|
||||
"""執行命令"""
|
||||
command = self.command_input.text().strip()
|
||||
|
@ -20,7 +20,7 @@
|
||||
"feedback": {
|
||||
"title": "Your Feedback",
|
||||
"description": "Please describe your thoughts, suggestions, or changes needed for the AI work results.",
|
||||
"placeholder": "Please enter your feedback, suggestions, or questions here...\n\n💡 Tip: Press Ctrl+Enter to submit quickly"
|
||||
"placeholder": "Please enter your feedback, suggestions, or questions here...\n\n💡 Tips:\n• Press Ctrl+Enter to submit quickly\n• Press Ctrl+V to paste images from clipboard"
|
||||
},
|
||||
"command": {
|
||||
"title": "Command Execution",
|
||||
@ -35,11 +35,16 @@
|
||||
"clear": "Clear",
|
||||
"status": "{count} images selected",
|
||||
"statusWithSize": "{count} images selected (Total {size})",
|
||||
"dragHint": "🎯 Drag images here (PNG, JPG, JPEG, GIF, BMP, WebP)",
|
||||
"dragHint": "🎯 Drag images here or press Ctrl+V to paste from clipboard (PNG, JPG, JPEG, GIF, BMP, WebP)",
|
||||
"deleteConfirm": "Are you sure you want to remove image \"{filename}\"?",
|
||||
"deleteTitle": "Confirm Delete",
|
||||
"sizeWarning": "Image file size cannot exceed 1MB",
|
||||
"formatError": "Unsupported image format"
|
||||
"formatError": "Unsupported image format",
|
||||
"paste_images": "📋 Paste from Clipboard",
|
||||
"paste_failed": "Paste failed, no image in clipboard",
|
||||
"paste_no_image": "No image in clipboard to paste",
|
||||
"paste_image_from_textarea": "Image intelligently pasted from text area to image area",
|
||||
"images_clear": "Clear all images"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "Submit Feedback",
|
||||
|
@ -20,7 +20,7 @@
|
||||
"feedback": {
|
||||
"title": "您的反馈",
|
||||
"description": "请描述您对 AI 工作结果的想法、建议或需要修改的地方。",
|
||||
"placeholder": "请在这里输入您的反馈、建议或问题...\n\n💡 小提示:按 Ctrl+Enter 可快速提交反馈"
|
||||
"placeholder": "请在这里输入您的反馈、建议或问题...\n\n💡 小提示:\n• 按 Ctrl+Enter 可快速提交反馈\n• 按 Ctrl+V 可直接贴上剪贴板图片"
|
||||
},
|
||||
"command": {
|
||||
"title": "命令执行",
|
||||
@ -35,11 +35,16 @@
|
||||
"clear": "清除",
|
||||
"status": "已选择 {count} 张图片",
|
||||
"statusWithSize": "已选择 {count} 张图片 (总计 {size})",
|
||||
"dragHint": "🎯 拖拽图片到这里 (PNG、JPG、JPEG、GIF、BMP、WebP)",
|
||||
"dragHint": "🎯 拖拽图片到这里 或 按 Ctrl+V 贴上剪贴板图片 (PNG、JPG、JPEG、GIF、BMP、WebP)",
|
||||
"deleteConfirm": "确定要移除图片 \"{filename}\" 吗?",
|
||||
"deleteTitle": "确认删除",
|
||||
"sizeWarning": "图片文件大小不能超过 1MB",
|
||||
"formatError": "不支持的图片格式"
|
||||
"formatError": "不支持的图片格式",
|
||||
"paste_images": "📋 从剪贴板粘贴",
|
||||
"paste_failed": "粘贴失败,剪贴板中没有图片",
|
||||
"paste_no_image": "剪贴板中没有图片可粘贴",
|
||||
"paste_image_from_textarea": "已将图片从文本框智能贴到图片区域",
|
||||
"images_clear": "清除所有图片"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "提交反馈",
|
||||
|
@ -20,7 +20,7 @@
|
||||
"feedback": {
|
||||
"title": "您的回饋",
|
||||
"description": "請描述您對 AI 工作結果的想法、建議或需要修改的地方。",
|
||||
"placeholder": "請在這裡輸入您的回饋、建議或問題...\n\n💡 小提示:按 Ctrl+Enter 可快速提交回饋"
|
||||
"placeholder": "請在這裡輸入您的回饋、建議或問題...\n\n💡 小提示:\n• 按 Ctrl+Enter 可快速提交回饋\n• 按 Ctrl+V 可直接貼上剪貼簿圖片"
|
||||
},
|
||||
"command": {
|
||||
"title": "命令執行",
|
||||
@ -35,11 +35,16 @@
|
||||
"clear": "清除",
|
||||
"status": "已選擇 {count} 張圖片",
|
||||
"statusWithSize": "已選擇 {count} 張圖片 (總計 {size})",
|
||||
"dragHint": "🎯 拖拽圖片到這裡 (PNG、JPG、JPEG、GIF、BMP、WebP)",
|
||||
"dragHint": "🎯 拖拽圖片到這裡 或 按 Ctrl+V 貼上剪貼簿圖片 (PNG、JPG、JPEG、GIF、BMP、WebP)",
|
||||
"deleteConfirm": "確定要移除圖片 \"{filename}\" 嗎?",
|
||||
"deleteTitle": "確認刪除",
|
||||
"sizeWarning": "圖片文件大小不能超過 1MB",
|
||||
"formatError": "不支援的圖片格式"
|
||||
"formatError": "不支援的圖片格式",
|
||||
"paste_images": "📋 從剪貼簿貼上",
|
||||
"paste_failed": "貼上失敗,剪貼簿中沒有圖片",
|
||||
"paste_no_image": "剪貼簿中沒有圖片可貼上",
|
||||
"paste_image_from_textarea": "已將圖片從文字框智能貼到圖片區域",
|
||||
"images_clear": "清除所有圖片"
|
||||
},
|
||||
"buttons": {
|
||||
"submit": "提交回饋",
|
||||
|
@ -43,7 +43,7 @@ class I18nManager {
|
||||
feedback: {
|
||||
title: '💬 您的回饋',
|
||||
description: '請在這裡輸入您的回饋、建議或問題。您的意見將幫助 AI 更好地理解您的需求。',
|
||||
placeholder: '請在這裡輸入您的回饋、建議或問題...\n\n💡 小提示:按 Ctrl+Enter 可快速提交回饋'
|
||||
placeholder: '請在這裡輸入您的回饋、建議或問題...\n\n💡 小提示:\n• 按 Ctrl+Enter 可快速提交回饋\n• 按 Ctrl+V 可直接貼上剪貼簿圖片'
|
||||
},
|
||||
command: {
|
||||
title: '⚡ 命令執行',
|
||||
@ -55,7 +55,7 @@ class I18nManager {
|
||||
title: '🖼️ 圖片附件(可選)',
|
||||
status: '已選擇 {count} 張圖片',
|
||||
statusWithSize: '已選擇 {count} 張圖片 (總計 {size})',
|
||||
dragHint: '🎯 拖拽圖片到這裡 (PNG、JPG、JPEG、GIF、BMP、WebP)',
|
||||
dragHint: '🎯 拖拽圖片到這裡 或 按 Ctrl+V 貼上剪貼簿圖片 (PNG、JPG、JPEG、GIF、BMP、WebP)',
|
||||
deleteConfirm: '確定要移除圖片 "{filename}" 嗎?',
|
||||
deleteTitle: '確認刪除'
|
||||
},
|
||||
@ -75,6 +75,8 @@ class I18nManager {
|
||||
commandFinished: '命令執行完成',
|
||||
pasteSuccess: '已從剪貼板貼上圖片',
|
||||
pasteFailed: '無法從剪貼板獲取圖片',
|
||||
paste_no_image: '剪貼簿中沒有圖片可貼上',
|
||||
paste_image_from_textarea: '已將圖片從文字框智能貼到圖片區域',
|
||||
invalidFileType: '不支援的文件類型',
|
||||
fileTooLarge: '文件過大(最大 1MB)'
|
||||
},
|
||||
@ -100,7 +102,7 @@ class I18nManager {
|
||||
feedback: {
|
||||
title: '💬 Your Feedback',
|
||||
description: 'Please enter your feedback, suggestions, or questions here. Your input helps AI better understand your needs.',
|
||||
placeholder: 'Please enter your feedback, suggestions, or questions here...\n\n💡 Tip: Press Ctrl+Enter to submit quickly'
|
||||
placeholder: 'Please enter your feedback, suggestions, or questions here...\n\n💡 Tips:\n• Press Ctrl+Enter to submit quickly\n• Press Ctrl+V to paste images from clipboard'
|
||||
},
|
||||
command: {
|
||||
title: '⚡ Command Execution',
|
||||
@ -112,7 +114,7 @@ class I18nManager {
|
||||
title: '🖼️ Image Attachments (Optional)',
|
||||
status: '{count} images selected',
|
||||
statusWithSize: '{count} images selected (Total {size})',
|
||||
dragHint: '🎯 Drag images here (PNG, JPG, JPEG, GIF, BMP, WebP)',
|
||||
dragHint: '🎯 Drag images here or press Ctrl+V to paste from clipboard (PNG, JPG, JPEG, GIF, BMP, WebP)',
|
||||
deleteConfirm: 'Are you sure you want to remove image "{filename}"?',
|
||||
deleteTitle: 'Confirm Delete'
|
||||
},
|
||||
@ -132,6 +134,8 @@ class I18nManager {
|
||||
commandFinished: 'Command finished',
|
||||
pasteSuccess: 'Image pasted from clipboard',
|
||||
pasteFailed: 'Failed to get image from clipboard',
|
||||
paste_no_image: 'No image to paste from clipboard',
|
||||
paste_image_from_textarea: 'Image pasted from text area to image area',
|
||||
invalidFileType: 'Unsupported file type',
|
||||
fileTooLarge: 'File too large (max 1MB)'
|
||||
},
|
||||
@ -157,7 +161,7 @@ class I18nManager {
|
||||
feedback: {
|
||||
title: '💬 您的反馈',
|
||||
description: '请在这里输入您的反馈、建议或问题。您的意见将帮助 AI 更好地理解您的需求。',
|
||||
placeholder: '请在这里输入您的反馈、建议或问题...\n\n💡 小提示:按 Ctrl+Enter 可快速提交反馈'
|
||||
placeholder: '请在这里输入您的反馈、建议或问题...\n\n💡 小提示:\n• 按 Ctrl+Enter 可快速提交反馈\n• 按 Ctrl+V 可直接贴上剪贴板图片'
|
||||
},
|
||||
command: {
|
||||
title: '⚡ 命令执行',
|
||||
@ -169,7 +173,7 @@ class I18nManager {
|
||||
title: '🖼️ 图片附件(可选)',
|
||||
status: '已选择 {count} 张图片',
|
||||
statusWithSize: '已选择 {count} 张图片 (总计 {size})',
|
||||
dragHint: '🎯 拖拽图片到这里 (PNG、JPG、JPEG、GIF、BMP、WebP)',
|
||||
dragHint: '🎯 拖拽图片到这里 或 按 Ctrl+V 贴上剪贴板图片 (PNG、JPG、JPEG、GIF、BMP、WebP)',
|
||||
deleteConfirm: '确定要移除图片 "{filename}" 吗?',
|
||||
deleteTitle: '确认删除'
|
||||
},
|
||||
@ -189,6 +193,8 @@ class I18nManager {
|
||||
commandFinished: '命令执行完成',
|
||||
pasteSuccess: '已从剪贴板粘贴图片',
|
||||
pasteFailed: '无法从剪贴板获取图片',
|
||||
paste_no_image: '剪贴板中没有图片可粘贴',
|
||||
paste_image_from_textarea: '已将图片从文字框智能贴到图片区域',
|
||||
invalidFileType: '不支持的文件类型',
|
||||
fileTooLarge: '文件过大(最大 1MB)'
|
||||
},
|
||||
@ -361,6 +367,8 @@ class I18nManager {
|
||||
'command_finished': 'status.commandFinished',
|
||||
'paste_success': 'status.pasteSuccess',
|
||||
'paste_failed': 'status.pasteFailed',
|
||||
'paste_no_image': 'status.paste_no_image',
|
||||
'paste_image_from_textarea': 'status.paste_image_from_textarea',
|
||||
'invalid_file_type': 'status.invalidFileType',
|
||||
'file_too_large': 'status.fileTooLarge'
|
||||
};
|
||||
|
@ -656,7 +656,7 @@
|
||||
<textarea
|
||||
id="feedbackText"
|
||||
class="text-input"
|
||||
placeholder="請在這裡輸入您的回饋、建議或問題... 💡 小提示:按 Ctrl+Enter 可快速提交回饋"
|
||||
placeholder="請在這裡輸入您的回饋、建議或問題... 💡 小提示: • 按 Ctrl+Enter 可快速提交回饋 • 按 Ctrl+V 可直接貼上剪貼簿圖片"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
@ -669,7 +669,7 @@
|
||||
<button class="upload-btn danger" onclick="clearAllImages()" id="clearBtn">❌ 清除</button>
|
||||
</div>
|
||||
<div class="drop-zone" id="dropZone">
|
||||
🎯 拖拽圖片到這裡 (PNG、JPG、JPEG、GIF、BMP、WebP)
|
||||
🎯 拖拽圖片到這裡 或 按 Ctrl+V 貼上剪貼簿圖片 (PNG、JPG、JPEG、GIF、BMP、WebP)
|
||||
</div>
|
||||
<div class="image-status" id="imageStatus">已選擇 0 張圖片</div>
|
||||
<div class="image-preview-area" id="imagePreviewArea"></div>
|
||||
@ -1007,9 +1007,66 @@
|
||||
e.preventDefault();
|
||||
submitFeedback();
|
||||
}
|
||||
|
||||
// Ctrl+V 智能貼上
|
||||
if (e.ctrlKey && e.key === 'v') {
|
||||
// 先檢查剪貼簿是否包含圖片
|
||||
checkClipboardForImage().then(hasImage => {
|
||||
if (hasImage) {
|
||||
// 如果有圖片,無論焦點在哪裡都優先貼到圖片區域
|
||||
e.preventDefault();
|
||||
pasteFromClipboard();
|
||||
|
||||
// 提供額外的使用者提示
|
||||
const activeElement = document.activeElement;
|
||||
const isInTextArea = activeElement && activeElement.id === 'feedbackText';
|
||||
if (isInTextArea) {
|
||||
showStatusMessage(t('paste_image_from_textarea'), 'success');
|
||||
}
|
||||
} else {
|
||||
// 如果沒有圖片,檢查是否在文字輸入區域
|
||||
const activeElement = document.activeElement;
|
||||
const isTextInput = activeElement && (
|
||||
activeElement.tagName === 'TEXTAREA' ||
|
||||
activeElement.tagName === 'INPUT' ||
|
||||
activeElement.contentEditable === 'true'
|
||||
);
|
||||
|
||||
if (isTextInput) {
|
||||
// 在文字輸入區域且剪貼簿只有文字,允許正常的文字貼上
|
||||
// 不需要 preventDefault(),讓瀏覽器執行預設行為
|
||||
} else {
|
||||
// 不在文字輸入區域且沒有圖片時,提示用戶
|
||||
e.preventDefault();
|
||||
showStatusMessage(t('paste_no_image'), 'info');
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
// 如果檢查剪貼簿失敗,允許正常的文字貼上行為
|
||||
console.warn('檢查剪貼簿失敗:', err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 檢查剪貼簿是否包含圖片
|
||||
async function checkClipboardForImage() {
|
||||
try {
|
||||
const items = await navigator.clipboard.read();
|
||||
for (const item of items) {
|
||||
for (const type of item.types) {
|
||||
if (type.startsWith('image/')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (err) {
|
||||
// 如果無法讀取剪貼簿,返回 false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 分頁切換功能
|
||||
function switchTab(tabName) {
|
||||
// 隱藏所有分頁內容
|
||||
|
@ -69,7 +69,7 @@ def test_qt_gui():
|
||||
debug_log(" - X刪除按鈕")
|
||||
debug_log(" - 視窗大小調整")
|
||||
debug_log(" - 分割器調整")
|
||||
debug_log()
|
||||
debug_log("")
|
||||
|
||||
# 啟動 GUI
|
||||
result = feedback_ui(project_directory, prompt)
|
||||
|
@ -25,6 +25,9 @@ import webbrowser
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
import socket
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
# 添加專案根目錄到 Python 路徑
|
||||
|
Loading…
x
Reference in New Issue
Block a user