mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 10:42:25 +08:00
✨ 將 gui 和 webui 的對話框加入 ctrl +v 就能複製貼上圖片的更能
This commit is contained in:
parent
f148841cc0
commit
0f56c7c5fb
@ -13,6 +13,7 @@ MCP Interactive Feedback Enhanced - 主程式入口
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import os
|
||||
|
||||
def main():
|
||||
"""主程式入口點"""
|
||||
@ -56,6 +57,9 @@ def run_server():
|
||||
|
||||
def run_tests(args):
|
||||
"""執行測試"""
|
||||
# 啟用調試模式以顯示測試過程
|
||||
os.environ["MCP_DEBUG"] = "true"
|
||||
|
||||
if args.web:
|
||||
print("🧪 執行 Web UI 測試...")
|
||||
from .test_web_ui import test_web_ui, interactive_demo
|
||||
|
@ -745,6 +745,24 @@ class FeedbackWindow(QMainWindow):
|
||||
if hasattr(self, 'summary_title'):
|
||||
self.summary_title.setText(t('ai_summary'))
|
||||
|
||||
# 更新摘要內容(如果是測試摘要)
|
||||
if hasattr(self, 'summary_text'):
|
||||
# 檢查是否為測試摘要,需要動態翻譯
|
||||
if self._is_test_summary():
|
||||
# 判斷是哪種測試類型並重新獲取翻譯
|
||||
if '圖片預覽' in self.summary or 'Image Preview' in self.summary or '图片预览' in self.summary:
|
||||
# Qt GUI 測試
|
||||
translated_summary = t('test.qtGuiSummary')
|
||||
elif 'Web UI' in self.summary:
|
||||
# Web UI 測試
|
||||
translated_summary = t('test.webUiSummary')
|
||||
else:
|
||||
translated_summary = self.summary
|
||||
|
||||
self.summary_text.setPlainText(translated_summary)
|
||||
# 更新儲存的摘要以保持一致
|
||||
self.summary = translated_summary
|
||||
|
||||
# 更新專案目錄標籤
|
||||
if hasattr(self, 'project_label'):
|
||||
self.project_label.setText(f"{t('project_directory')}: {self.project_dir}")
|
||||
@ -780,6 +798,38 @@ class FeedbackWindow(QMainWindow):
|
||||
if hasattr(self, 'output_title'):
|
||||
self.output_title.setText(t('command_output'))
|
||||
|
||||
def _is_test_summary(self) -> bool:
|
||||
"""檢查是否為測試摘要,使用更嚴格的檢測邏輯"""
|
||||
# 更嚴格的測試摘要特徵組合檢測
|
||||
test_patterns = [
|
||||
# Qt GUI 測試特徵
|
||||
('測試 Qt GUI 功能', '🎯 **功能測試項目'),
|
||||
('Test Qt GUI Functionality', '🎯 **Test Items'),
|
||||
('测试 Qt GUI 功能', '🎯 **功能测试项目'),
|
||||
|
||||
# Web UI 測試特徵
|
||||
('測試 Web UI 功能', '🎯 **功能測試項目'),
|
||||
('Test Web UI Functionality', '🎯 **Test Items'),
|
||||
('测试 Web UI 功能', '🎯 **功能测试项目'),
|
||||
|
||||
# 具體的測試項目描述
|
||||
('圖片上傳和預覽', '智能 Ctrl+V 圖片貼上'),
|
||||
('Image upload and preview', 'Smart Ctrl+V image paste'),
|
||||
('图片上传和预览', '智能 Ctrl+V 图片粘贴'),
|
||||
|
||||
# WebSocket 和服務器啟動描述
|
||||
('WebSocket 即時通訊', 'Web UI 服務器啟動'),
|
||||
('WebSocket real-time communication', 'Web UI server startup'),
|
||||
('WebSocket 即时通讯', 'Web UI 服务器启动')
|
||||
]
|
||||
|
||||
# 必須同時包含模式中的兩個特徵才認為是測試摘要
|
||||
for pattern1, pattern2 in test_patterns:
|
||||
if pattern1 in self.summary and pattern2 in self.summary:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _update_image_upload_texts(self) -> None:
|
||||
"""更新圖片上傳元件的文字"""
|
||||
if hasattr(self, 'image_upload'):
|
||||
@ -812,12 +862,12 @@ class FeedbackWindow(QMainWindow):
|
||||
|
||||
summary_layout.addLayout(header_layout)
|
||||
|
||||
# 摘要內容(可滾動的文本區域)
|
||||
summary_text = QTextEdit()
|
||||
summary_text.setPlainText(self.summary)
|
||||
summary_text.setReadOnly(True)
|
||||
summary_text.setMaximumHeight(120)
|
||||
summary_layout.addWidget(summary_text)
|
||||
# 摘要內容(可滾動的文本區域)- 儲存為實例變數以支援動態更新
|
||||
self.summary_text = QTextEdit()
|
||||
self.summary_text.setPlainText(self.summary)
|
||||
self.summary_text.setReadOnly(True)
|
||||
self.summary_text.setMaximumHeight(120)
|
||||
summary_layout.addWidget(self.summary_text)
|
||||
|
||||
layout.addWidget(summary_group)
|
||||
|
||||
|
@ -268,6 +268,10 @@ class I18nManager:
|
||||
'language_zh_tw': 'languageNames.zhTw',
|
||||
'language_en': 'languageNames.en',
|
||||
'language_zh_cn': 'languageNames.zhCn',
|
||||
|
||||
# 測試
|
||||
'test_qt_gui_summary': 'test.qtGuiSummary',
|
||||
'test_web_ui_summary': 'test.webUiSummary',
|
||||
}
|
||||
|
||||
# 檢查是否有對應的新鍵
|
||||
|
@ -82,5 +82,9 @@
|
||||
"zhTw": "繁體中文",
|
||||
"en": "English",
|
||||
"zhCn": "简体中文"
|
||||
},
|
||||
"test": {
|
||||
"qtGuiSummary": "🎯 Image Preview and Window Adjustment Test\n\nThis is a test session to verify the following features:\n\n✅ Test Items:\n1. Image upload and preview functionality\n2. Image X delete button in top-right corner\n3. Free window resizing\n4. Flexible splitter adjustment\n5. Dynamic layout of all areas\n6. Smart Ctrl+V image paste functionality\n\n📋 Test Steps:\n1. Try uploading some images (drag & drop, file selection, clipboard)\n2. Check if image preview displays correctly\n3. Click the X button in the top-right corner of images to delete them\n4. Try resizing the window, check if it can be freely adjusted\n5. Drag the splitter to adjust area sizes\n6. Press Ctrl+V in the text box to test smart paste functionality\n7. Provide any feedback or issues found\n\nPlease test these features and provide feedback!",
|
||||
"webUiSummary": "Test Web UI Functionality\n\n🎯 **Test Items:**\n- Web UI server startup and operation\n- WebSocket real-time communication\n- Feedback submission functionality\n- Image upload and preview\n- Command execution functionality\n- Smart Ctrl+V image paste\n- Multi-language interface switching\n\n📋 **Test Steps:**\n1. Test image upload (drag & drop, file selection, clipboard)\n2. Press Ctrl+V in text box to test smart paste\n3. Try switching languages (Traditional Chinese/Simplified Chinese/English)\n4. Test command execution functionality\n5. Submit feedback and images\n\nPlease test these features and provide feedback!"
|
||||
}
|
||||
}
|
@ -82,5 +82,9 @@
|
||||
"zhTw": "繁體中文",
|
||||
"en": "English",
|
||||
"zhCn": "简体中文"
|
||||
},
|
||||
"test": {
|
||||
"qtGuiSummary": "🎯 图片预览和窗口调整测试\n\n这是一个测试会话,用于验证以下功能:\n\n✅ 功能测试项目:\n1. 图片上传和预览功能\n2. 图片右上角X删除按钮\n3. 窗口自由调整大小\n4. 分割器的灵活调整\n5. 各区域的动态布局\n6. 智能 Ctrl+V 图片粘贴功能\n\n📋 测试步骤:\n1. 尝试上传一些图片(拖拽、文件选择、剪贴板)\n2. 检查图片预览是否正常显示\n3. 点击图片右上角的X按钮删除图片\n4. 尝试调整窗口大小,检查是否可以自由调整\n5. 拖动分割器调整各区域大小\n6. 在文本框内按 Ctrl+V 测试智能粘贴功能\n7. 提供任何回馈或发现的问题\n\n请测试这些功能并提供回馈!",
|
||||
"webUiSummary": "测试 Web UI 功能\n\n🎯 **功能测试项目:**\n- Web UI 服务器启动和运行\n- WebSocket 即时通讯\n- 回馈提交功能\n- 图片上传和预览\n- 命令执行功能\n- 智能 Ctrl+V 图片粘贴\n- 多语言界面切换\n\n📋 **测试步骤:**\n1. 测试图片上传(拖拽、选择文件、剪贴板)\n2. 在文本框内按 Ctrl+V 测试智能粘贴\n3. 尝试切换语言(繁中/简中/英文)\n4. 测试命令执行功能\n5. 提交回馈和图片\n\n请测试这些功能并提供回馈!"
|
||||
}
|
||||
}
|
@ -77,10 +77,14 @@
|
||||
"fileTooLarge": "文件過大(最大 1MB)"
|
||||
},
|
||||
"aiSummary": "AI 工作摘要",
|
||||
"languageSelector": "🌐 語言",
|
||||
"languageSelector": "🌐 語言選擇",
|
||||
"languageNames": {
|
||||
"zhTw": "繁體中文",
|
||||
"en": "English",
|
||||
"zhCn": "简体中文"
|
||||
},
|
||||
"test": {
|
||||
"qtGuiSummary": "🎯 圖片預覽和視窗調整測試\n\n這是一個測試會話,用於驗證以下功能:\n\n✅ 功能測試項目:\n1. 圖片上傳和預覽功能\n2. 圖片右上角X刪除按鈕\n3. 視窗自由調整大小\n4. 分割器的靈活調整\n5. 各區域的動態佈局\n6. 智能 Ctrl+V 圖片貼上功能\n\n📋 測試步驟:\n1. 嘗試上傳一些圖片(拖拽、文件選擇、剪貼板)\n2. 檢查圖片預覽是否正常顯示\n3. 點擊圖片右上角的X按鈕刪除圖片\n4. 嘗試調整視窗大小,檢查是否可以自由調整\n5. 拖動分割器調整各區域大小\n6. 在文字框內按 Ctrl+V 測試智能貼上功能\n7. 提供任何回饋或發現的問題\n\n請測試這些功能並提供回饋!",
|
||||
"webUiSummary": "測試 Web UI 功能\n\n🎯 **功能測試項目:**\n- Web UI 服務器啟動和運行\n- WebSocket 即時通訊\n- 回饋提交功能\n- 圖片上傳和預覽\n- 命令執行功能\n- 智能 Ctrl+V 圖片貼上\n- 多語言介面切換\n\n📋 **測試步驟:**\n1. 測試圖片上傳(拖拽、選擇檔案、剪貼簿)\n2. 在文字框內按 Ctrl+V 測試智能貼上\n3. 嘗試切換語言(繁中/簡中/英文)\n4. 測試命令執行功能\n5. 提交回饋和圖片\n\n請測試這些功能並提供回饋!"
|
||||
}
|
||||
}
|
@ -373,8 +373,7 @@ def launch_gui(project_dir: str, summary: str) -> dict:
|
||||
async def interactive_feedback(
|
||||
project_directory: Annotated[str, Field(description="專案目錄路徑")] = ".",
|
||||
summary: Annotated[str, Field(description="AI 工作完成的摘要說明")] = "我已完成了您請求的任務。",
|
||||
timeout: Annotated[int, Field(description="等待用戶回饋的超時時間(秒)")] = 600,
|
||||
force_web_ui: Annotated[bool, Field(description="強制使用 Web UI(用於測試或特殊需求)")] = False
|
||||
timeout: Annotated[int, Field(description="等待用戶回饋的超時時間(秒)")] = 600
|
||||
) -> List:
|
||||
"""
|
||||
收集用戶的互動回饋,支援文字和圖片
|
||||
@ -382,7 +381,7 @@ async def interactive_feedback(
|
||||
此工具會自動偵測運行環境:
|
||||
- 遠端環境:使用 Web UI
|
||||
- 本地環境:使用 Qt GUI
|
||||
- 可透過 force_web_ui 參數或 FORCE_WEB 環境變數強制使用 Web UI
|
||||
- 可透過 FORCE_WEB 環境變數強制使用 Web UI
|
||||
|
||||
用戶可以:
|
||||
1. 執行命令來驗證結果
|
||||
@ -390,6 +389,10 @@ async def interactive_feedback(
|
||||
3. 上傳圖片作為回饋
|
||||
4. 查看 AI 的工作摘要
|
||||
|
||||
介面控制(按優先級排序):
|
||||
1. **FORCE_WEB 環境變數**:在 mcp.json 中設置 "FORCE_WEB": "true"
|
||||
2. 自動檢測:根據運行環境自動選擇
|
||||
|
||||
調試模式:
|
||||
- 設置環境變數 MCP_DEBUG=true 可啟用詳細調試輸出
|
||||
- 生產環境建議關閉調試模式以避免輸出干擾
|
||||
@ -398,26 +401,26 @@ async def interactive_feedback(
|
||||
project_directory: 專案目錄路徑
|
||||
summary: AI 工作完成的摘要說明
|
||||
timeout: 等待用戶回饋的超時時間(秒),預設為 600 秒(10 分鐘)
|
||||
force_web_ui: 強制使用 Web UI,即使在本地環境也使用 Web UI(用於測試)
|
||||
|
||||
Returns:
|
||||
List: 包含 TextContent 和 MCPImage 對象的列表
|
||||
"""
|
||||
# 檢查環境變數,如果設定了 FORCE_WEB 就覆蓋 force_web_ui 參數
|
||||
# 檢查環境變數 FORCE_WEB
|
||||
force_web = False
|
||||
env_force_web = os.getenv("FORCE_WEB", "").lower()
|
||||
if env_force_web in ("true", "1", "yes", "on"):
|
||||
force_web_ui = True
|
||||
force_web = True
|
||||
debug_log("環境變數 FORCE_WEB 已啟用,強制使用 Web UI")
|
||||
elif env_force_web in ("false", "0", "no", "off"):
|
||||
force_web_ui = False
|
||||
force_web = False
|
||||
debug_log("環境變數 FORCE_WEB 已停用,使用預設邏輯")
|
||||
|
||||
# 環境偵測
|
||||
is_remote = is_remote_environment()
|
||||
can_gui = can_use_gui()
|
||||
use_web_ui = is_remote or not can_gui or force_web_ui
|
||||
use_web_ui = is_remote or not can_gui or force_web
|
||||
|
||||
debug_log(f"環境偵測結果 - 遠端: {is_remote}, GUI 可用: {can_gui}, 強制 Web UI: {force_web_ui}")
|
||||
debug_log(f"環境偵測結果 - 遠端: {is_remote}, GUI 可用: {can_gui}, 強制 Web UI: {force_web}")
|
||||
debug_log(f"決定使用介面: {'Web UI' if use_web_ui else 'Qt GUI'}")
|
||||
|
||||
try:
|
||||
|
@ -26,185 +26,27 @@ class I18nManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 獲取內嵌的備用翻譯
|
||||
* 獲取內嵌的備用翻譯(僅保留基本錯誤訊息)
|
||||
*/
|
||||
_getEmbeddedTranslations() {
|
||||
return {
|
||||
'zh-TW': {
|
||||
app: {
|
||||
title: 'Interactive Feedback MCP',
|
||||
projectDirectory: '專案目錄',
|
||||
language: '語言'
|
||||
},
|
||||
tabs: {
|
||||
feedback: '💬 回饋',
|
||||
command: '⚡ 命令'
|
||||
},
|
||||
feedback: {
|
||||
title: '💬 您的回饋',
|
||||
description: '請在這裡輸入您的回饋、建議或問題。您的意見將幫助 AI 更好地理解您的需求。',
|
||||
placeholder: '請在這裡輸入您的回饋、建議或問題...\n\n💡 小提示:\n• 按 Ctrl+Enter 可快速提交回饋\n• 按 Ctrl+V 可直接貼上剪貼簿圖片'
|
||||
},
|
||||
command: {
|
||||
title: '⚡ 命令執行',
|
||||
description: '您可以在此執行系統命令來驗證結果或獲取更多資訊。',
|
||||
placeholder: '輸入要執行的命令...',
|
||||
output: '命令輸出'
|
||||
},
|
||||
images: {
|
||||
title: '🖼️ 圖片附件(可選)',
|
||||
status: '已選擇 {count} 張圖片',
|
||||
statusWithSize: '已選擇 {count} 張圖片 (總計 {size})',
|
||||
dragHint: '🎯 拖拽圖片到這裡 或 按 Ctrl+V 貼上剪貼簿圖片 (PNG、JPG、JPEG、GIF、BMP、WebP)',
|
||||
deleteConfirm: '確定要移除圖片 "{filename}" 嗎?',
|
||||
deleteTitle: '確認刪除'
|
||||
},
|
||||
buttons: {
|
||||
selectFiles: '📁 選擇文件',
|
||||
pasteClipboard: '📋 剪貼板',
|
||||
clearAll: '✕ 清除',
|
||||
runCommand: '▶️ 執行',
|
||||
submitFeedback: '✅ 提交回饋',
|
||||
cancel: '❌ 取消'
|
||||
},
|
||||
status: {
|
||||
uploading: '上傳中...',
|
||||
uploadSuccess: '上傳成功',
|
||||
uploadFailed: '上傳失敗',
|
||||
commandRunning: '命令執行中...',
|
||||
commandFinished: '命令執行完成',
|
||||
pasteSuccess: '已從剪貼板貼上圖片',
|
||||
pasteFailed: '無法從剪貼板獲取圖片',
|
||||
paste_no_image: '剪貼簿中沒有圖片可貼上',
|
||||
paste_image_from_textarea: '已將圖片從文字框智能貼到圖片區域',
|
||||
invalidFileType: '不支援的文件類型',
|
||||
fileTooLarge: '文件過大(最大 1MB)'
|
||||
},
|
||||
aiSummary: '📋 AI 工作摘要',
|
||||
languageSelector: '🌐 語言選擇',
|
||||
languageNames: {
|
||||
zhTw: '繁體中文',
|
||||
en: 'English',
|
||||
zhCn: '简体中文'
|
||||
}
|
||||
app: { title: 'Interactive Feedback MCP' },
|
||||
loading: '載入中...',
|
||||
error: '載入失敗',
|
||||
retry: '重試'
|
||||
},
|
||||
|
||||
'en': {
|
||||
app: {
|
||||
title: 'Interactive Feedback MCP',
|
||||
projectDirectory: 'Project Directory',
|
||||
language: 'Language'
|
||||
},
|
||||
tabs: {
|
||||
feedback: '💬 Feedback',
|
||||
command: '⚡ Commands'
|
||||
},
|
||||
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💡 Tips:\n• Press Ctrl+Enter to submit quickly\n• Press Ctrl+V to paste images from clipboard'
|
||||
},
|
||||
command: {
|
||||
title: '⚡ Command Execution',
|
||||
description: 'You can execute system commands here to verify results or get additional information.',
|
||||
placeholder: 'Enter command to execute...',
|
||||
output: 'Command Output'
|
||||
},
|
||||
images: {
|
||||
title: '🖼️ Image Attachments (Optional)',
|
||||
status: '{count} images selected',
|
||||
statusWithSize: '{count} images selected (Total {size})',
|
||||
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'
|
||||
},
|
||||
buttons: {
|
||||
selectFiles: '📁 Select Files',
|
||||
pasteClipboard: '📋 Clipboard',
|
||||
clearAll: '✕ Clear',
|
||||
runCommand: '▶️ Run',
|
||||
submitFeedback: '✅ Submit Feedback',
|
||||
cancel: '❌ Cancel'
|
||||
},
|
||||
status: {
|
||||
uploading: 'Uploading...',
|
||||
uploadSuccess: 'Upload successful',
|
||||
uploadFailed: 'Upload failed',
|
||||
commandRunning: 'Command running...',
|
||||
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)'
|
||||
},
|
||||
aiSummary: '📋 AI Work Summary',
|
||||
languageSelector: '🌐 Language',
|
||||
languageNames: {
|
||||
zhTw: '繁體中文',
|
||||
en: 'English',
|
||||
zhCn: '简体中文'
|
||||
}
|
||||
app: { title: 'Interactive Feedback MCP' },
|
||||
loading: 'Loading...',
|
||||
error: 'Loading failed',
|
||||
retry: 'Retry'
|
||||
},
|
||||
|
||||
'zh-CN': {
|
||||
app: {
|
||||
title: 'Interactive Feedback MCP',
|
||||
projectDirectory: '项目目录',
|
||||
language: '语言'
|
||||
},
|
||||
tabs: {
|
||||
feedback: '💬 反馈',
|
||||
command: '⚡ 命令'
|
||||
},
|
||||
feedback: {
|
||||
title: '💬 您的反馈',
|
||||
description: '请在这里输入您的反馈、建议或问题。您的意见将帮助 AI 更好地理解您的需求。',
|
||||
placeholder: '请在这里输入您的反馈、建议或问题...\n\n💡 小提示:\n• 按 Ctrl+Enter 可快速提交反馈\n• 按 Ctrl+V 可直接贴上剪贴板图片'
|
||||
},
|
||||
command: {
|
||||
title: '⚡ 命令执行',
|
||||
description: '您可以在此执行系统命令来验证结果或获取更多信息。',
|
||||
placeholder: '输入要执行的命令...',
|
||||
output: '命令输出'
|
||||
},
|
||||
images: {
|
||||
title: '🖼️ 图片附件(可选)',
|
||||
status: '已选择 {count} 张图片',
|
||||
statusWithSize: '已选择 {count} 张图片 (总计 {size})',
|
||||
dragHint: '🎯 拖拽图片到这里 或 按 Ctrl+V 贴上剪贴板图片 (PNG、JPG、JPEG、GIF、BMP、WebP)',
|
||||
deleteConfirm: '确定要移除图片 "{filename}" 吗?',
|
||||
deleteTitle: '确认删除'
|
||||
},
|
||||
buttons: {
|
||||
selectFiles: '📁 选择文件',
|
||||
pasteClipboard: '📋 剪贴板',
|
||||
clearAll: '✕ 清除',
|
||||
runCommand: '▶️ 执行',
|
||||
submitFeedback: '✅ 提交反馈',
|
||||
cancel: '❌ 取消'
|
||||
},
|
||||
status: {
|
||||
uploading: '上传中...',
|
||||
uploadSuccess: '上传成功',
|
||||
uploadFailed: '上传失败',
|
||||
commandRunning: '命令执行中...',
|
||||
commandFinished: '命令执行完成',
|
||||
pasteSuccess: '已从剪贴板粘贴图片',
|
||||
pasteFailed: '无法从剪贴板获取图片',
|
||||
paste_no_image: '剪贴板中没有图片可粘贴',
|
||||
paste_image_from_textarea: '已将图片从文字框智能贴到图片区域',
|
||||
invalidFileType: '不支持的文件类型',
|
||||
fileTooLarge: '文件过大(最大 1MB)'
|
||||
},
|
||||
aiSummary: '📋 AI 工作摘要',
|
||||
languageSelector: '🌐 语言选择',
|
||||
languageNames: {
|
||||
zhTw: '繁體中文',
|
||||
en: 'English',
|
||||
zhCn: '简体中文'
|
||||
}
|
||||
app: { title: 'Interactive Feedback MCP' },
|
||||
loading: '加载中...',
|
||||
error: '加载失败',
|
||||
retry: '重试'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-TW" id="html-root">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
@ -103,7 +104,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.feedback-section, .summary-section {
|
||||
.feedback-section,
|
||||
.summary-section {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
@ -180,7 +182,8 @@
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.text-input, .command-input {
|
||||
.text-input,
|
||||
.command-input {
|
||||
width: 100%;
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-color);
|
||||
@ -199,13 +202,15 @@
|
||||
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.text-input:focus, .command-input:focus {
|
||||
.text-input:focus,
|
||||
.command-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent-color);
|
||||
box-shadow: 0 0 0 2px rgba(0, 122, 204, 0.2);
|
||||
}
|
||||
|
||||
.text-input::placeholder, .command-input::placeholder {
|
||||
.text-input::placeholder,
|
||||
.command-input::placeholder {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
@ -602,6 +607,7 @@
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="header-content">
|
||||
@ -653,11 +659,8 @@
|
||||
<div class="section-description" id="feedbackDescription">
|
||||
請在這裡輸入您的回饋、建議或問題。您的意見將幫助 AI 更好地理解您的需求。
|
||||
</div>
|
||||
<textarea
|
||||
id="feedbackText"
|
||||
class="text-input"
|
||||
placeholder="請在這裡輸入您的回饋、建議或問題... 💡 小提示: • 按 Ctrl+Enter 可快速提交回饋 • 按 Ctrl+V 可直接貼上剪貼簿圖片"
|
||||
></textarea>
|
||||
<textarea id="feedbackText" class="text-input"
|
||||
placeholder="請在這裡輸入您的回饋、建議或問題... 💡 小提示: • 按 Ctrl+Enter 可快速提交回饋 • 按 Ctrl+V 可直接貼上剪貼簿圖片"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- 圖片上傳區域 -->
|
||||
@ -665,7 +668,8 @@
|
||||
<h3 id="imagesTitle">🖼️ 圖片附件(可選)</h3>
|
||||
<div class="upload-buttons">
|
||||
<button class="upload-btn" onclick="selectFiles()" id="selectFilesBtn">📁 選擇文件</button>
|
||||
<button class="upload-btn success" onclick="pasteFromClipboard()" id="pasteBtn">📋 剪貼板</button>
|
||||
<button class="upload-btn success" onclick="pasteFromClipboard()" id="pasteBtn">📋
|
||||
剪貼板</button>
|
||||
<button class="upload-btn danger" onclick="clearAllImages()" id="clearBtn">❌ 清除</button>
|
||||
</div>
|
||||
<div class="drop-zone" id="dropZone">
|
||||
@ -685,13 +689,8 @@
|
||||
</div>
|
||||
<div class="command-section">
|
||||
<div class="command-input-wrapper">
|
||||
<input
|
||||
type="text"
|
||||
id="commandInput"
|
||||
class="command-input"
|
||||
placeholder="輸入要執行的命令..."
|
||||
onkeypress="if(event.key==='Enter') runCommand()"
|
||||
>
|
||||
<input type="text" id="commandInput" class="command-input" placeholder="輸入要執行的命令..."
|
||||
onkeypress="if(event.key==='Enter') runCommand()">
|
||||
</div>
|
||||
<button class="run-button" onclick="runCommand()" id="runBtn">▶️ 執行</button>
|
||||
</div>
|
||||
@ -755,14 +754,14 @@
|
||||
}
|
||||
|
||||
function setupWebSocketHandlers() {
|
||||
ws.onopen = function() {
|
||||
ws.onopen = function () {
|
||||
console.log('WebSocket 連接成功');
|
||||
isConnected = true;
|
||||
reconnectAttempts = 0;
|
||||
showNotification('WebSocket 連接成功', 'success');
|
||||
};
|
||||
|
||||
ws.onmessage = function(event) {
|
||||
ws.onmessage = function (event) {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
handleWebSocketMessage(data);
|
||||
@ -772,7 +771,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = function(event) {
|
||||
ws.onclose = function (event) {
|
||||
console.log('WebSocket 連接關閉:', event.code, event.reason);
|
||||
isConnected = false;
|
||||
|
||||
@ -782,7 +781,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
ws.onerror = function(error) {
|
||||
ws.onerror = function (error) {
|
||||
console.error('WebSocket 錯誤:', error);
|
||||
handleConnectionError();
|
||||
};
|
||||
@ -810,7 +809,15 @@
|
||||
}
|
||||
|
||||
// 頁面初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.addEventListener('DOMContentLoaded', async function () {
|
||||
// 先初始化 i18n 載入翻譯
|
||||
try {
|
||||
await window.initI18n();
|
||||
console.log('[I18N] 翻譯載入完成');
|
||||
} catch (error) {
|
||||
console.warn('[I18N] 翻譯載入失敗,使用內嵌翻譯:', error);
|
||||
}
|
||||
|
||||
// 初始化 WebSocket 連接
|
||||
initWebSocket();
|
||||
|
||||
@ -848,7 +855,7 @@
|
||||
// 語言選擇器事件
|
||||
const languageSelect = document.getElementById('languageSelect');
|
||||
if (languageSelect) {
|
||||
languageSelect.addEventListener('change', function(e) {
|
||||
languageSelect.addEventListener('change', function (e) {
|
||||
changeLanguage(e.target.value);
|
||||
});
|
||||
}
|
||||
@ -939,6 +946,43 @@
|
||||
const summaryTitle = document.getElementById('summaryTitle');
|
||||
if (summaryTitle) summaryTitle.innerHTML = t('ai_summary');
|
||||
|
||||
// 動態更新 AI 工作摘要內容(如果是測試內容)
|
||||
const summaryContent = document.getElementById('summaryContent');
|
||||
if (summaryContent) {
|
||||
const currentSummary = summaryContent.textContent || summaryContent.innerHTML;
|
||||
|
||||
// 更嚴格的測試摘要檢測邏輯 - 必須同時包含多個特徵
|
||||
const isTestSummary = (
|
||||
// Qt GUI 測試特徵組合
|
||||
(currentSummary.includes('測試 Qt GUI 功能') && currentSummary.includes('🎯 **功能測試項目')) ||
|
||||
(currentSummary.includes('Test Qt GUI Functionality') && currentSummary.includes('🎯 **Test Items')) ||
|
||||
(currentSummary.includes('测试 Qt GUI 功能') && currentSummary.includes('🎯 **功能测试项目')) ||
|
||||
|
||||
// Web UI 測試特徵組合
|
||||
(currentSummary.includes('測試 Web UI 功能') && currentSummary.includes('🎯 **功能測試項目')) ||
|
||||
(currentSummary.includes('Test Web UI Functionality') && currentSummary.includes('🎯 **Test Items')) ||
|
||||
(currentSummary.includes('测试 Web UI 功能') && currentSummary.includes('🎯 **功能测试项目')) ||
|
||||
|
||||
// 具體測試項目特徵組合
|
||||
(currentSummary.includes('圖片上傳和預覽') && currentSummary.includes('智能 Ctrl+V 圖片貼上')) ||
|
||||
(currentSummary.includes('Image upload and preview') && currentSummary.includes('Smart Ctrl+V image paste')) ||
|
||||
(currentSummary.includes('图片上传和预览') && currentSummary.includes('智能 Ctrl+V 图片粘贴')) ||
|
||||
|
||||
// WebSocket 和服務器特徵組合
|
||||
(currentSummary.includes('WebSocket 即時通訊') && currentSummary.includes('Web UI 服務器啟動')) ||
|
||||
(currentSummary.includes('WebSocket real-time communication') && currentSummary.includes('Web UI server startup')) ||
|
||||
(currentSummary.includes('WebSocket 即时通讯') && currentSummary.includes('Web UI 服务器启动'))
|
||||
);
|
||||
|
||||
if (isTestSummary) {
|
||||
// 使用對應語言的測試摘要
|
||||
const testSummary = t('test.webUiSummary');
|
||||
if (testSummary && testSummary !== 'test.webUiSummary') {
|
||||
summaryContent.textContent = testSummary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新分頁標籤
|
||||
const feedbackTabBtn = document.getElementById('feedbackTabBtn');
|
||||
if (feedbackTabBtn) feedbackTabBtn.innerHTML = t('feedback_tab');
|
||||
@ -1002,7 +1046,7 @@
|
||||
|
||||
function setupKeyboardShortcuts() {
|
||||
// Ctrl+Enter 快速提交
|
||||
document.addEventListener('keydown', function(e) {
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.ctrlKey && e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
submitFeedback();
|
||||
@ -1155,7 +1199,7 @@
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
reader.onload = function (e) {
|
||||
const imageData = {
|
||||
id: Date.now() + Math.random(),
|
||||
filename: file.name,
|
||||
@ -1466,7 +1510,7 @@
|
||||
}
|
||||
|
||||
// 監聽窗口大小變化
|
||||
window.addEventListener('resize', function() {
|
||||
window.addEventListener('resize', function () {
|
||||
updateImagePreviewArea();
|
||||
if (selectedImages.length > 0) {
|
||||
updateGridIndicator();
|
||||
@ -1502,7 +1546,7 @@
|
||||
function handleWebSocketMessage(data) {
|
||||
console.log('收到 WebSocket 消息:', data);
|
||||
|
||||
switch(data.type) {
|
||||
switch (data.type) {
|
||||
case 'command_output':
|
||||
appendCommandOutput(data.output);
|
||||
break;
|
||||
@ -1543,4 +1587,5 @@
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -28,6 +28,7 @@ from typing import Optional, Dict, Any
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
from .debug import debug_log
|
||||
from .i18n import t
|
||||
|
||||
# 嘗試導入 Qt GUI 模組
|
||||
try:
|
||||
@ -42,26 +43,8 @@ def test_qt_gui():
|
||||
try:
|
||||
# 測試參數
|
||||
project_directory = os.getcwd()
|
||||
prompt = """🎯 圖片預覽和視窗調整測試
|
||||
|
||||
這是一個測試會話,用於驗證以下功能:
|
||||
|
||||
✅ 功能測試項目:
|
||||
1. 圖片上傳和預覽功能
|
||||
2. 圖片右上角X刪除按鈕
|
||||
3. 視窗自由調整大小
|
||||
4. 分割器的靈活調整
|
||||
5. 各區域的動態佈局
|
||||
|
||||
📋 測試步驟:
|
||||
1. 嘗試上傳一些圖片(拖拽、文件選擇、剪貼板)
|
||||
2. 檢查圖片預覽是否正常顯示
|
||||
3. 點擊圖片右上角的X按鈕刪除圖片
|
||||
4. 嘗試調整視窗大小,檢查是否可以自由調整
|
||||
5. 拖動分割器調整各區域大小
|
||||
6. 提供任何回饋或發現的問題
|
||||
|
||||
請測試這些功能並提供回饋!"""
|
||||
# 使用國際化系統獲取測試摘要
|
||||
prompt = t('test.qtGuiSummary')
|
||||
|
||||
debug_log("🚀 啟動 Qt GUI 測試...")
|
||||
debug_log("📝 測試項目:")
|
||||
@ -69,6 +52,7 @@ def test_qt_gui():
|
||||
debug_log(" - X刪除按鈕")
|
||||
debug_log(" - 視窗大小調整")
|
||||
debug_log(" - 分割器調整")
|
||||
debug_log(" - 智能 Ctrl+V 功能")
|
||||
debug_log("")
|
||||
|
||||
# 啟動 GUI
|
||||
|
@ -34,6 +34,7 @@ from typing import Dict, Any, Optional
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
from .debug import debug_log
|
||||
from .i18n import t
|
||||
|
||||
# 嘗試導入 Web UI 模組
|
||||
try:
|
||||
@ -43,6 +44,10 @@ except ImportError as e:
|
||||
debug_log(f"⚠️ 無法導入 Web UI 模組: {e}")
|
||||
WEB_UI_AVAILABLE = False
|
||||
|
||||
def get_test_summary():
|
||||
"""獲取測試摘要,使用國際化系統"""
|
||||
return t('test.webUiSummary')
|
||||
|
||||
def find_free_port():
|
||||
"""Find a free port to use for testing"""
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
@ -125,7 +130,8 @@ def test_web_ui(keep_running=False):
|
||||
session_info = None
|
||||
try:
|
||||
project_dir = str(Path.cwd())
|
||||
summary = "測試 Web UI 功能"
|
||||
# 使用國際化系統獲取測試摘要
|
||||
summary = t('test.webUiSummary')
|
||||
session_id = manager.create_session(project_dir, summary)
|
||||
session_info = {
|
||||
'manager': manager,
|
||||
@ -145,6 +151,7 @@ def test_web_ui(keep_running=False):
|
||||
debug_log(" - 本地環境會繼續使用 Qt GUI")
|
||||
debug_log(" - 支援即時命令執行和 WebSocket 通訊")
|
||||
debug_log(" - 提供現代化的深色主題界面")
|
||||
debug_log(" - 支援智能 Ctrl+V 圖片貼上功能")
|
||||
|
||||
return True, session_info
|
||||
|
||||
@ -185,8 +192,8 @@ def test_mcp_integration():
|
||||
# Test timeout parameter
|
||||
debug_log("✅ 支援 timeout 參數")
|
||||
|
||||
# Test force_web_ui parameter
|
||||
debug_log("✅ 支援 force_web_ui 參數")
|
||||
# Test environment-based Web UI selection
|
||||
debug_log("✅ 支援基於環境變數的 Web UI 選擇")
|
||||
|
||||
# Test would require actual MCP call, so just verify import
|
||||
debug_log("✅ 準備接受來自 AI 助手的調用")
|
||||
@ -197,8 +204,8 @@ def test_mcp_integration():
|
||||
return False
|
||||
|
||||
def test_new_parameters():
|
||||
"""Test new timeout and force_web_ui parameters"""
|
||||
debug_log("\n🆕 測試新增參數功能")
|
||||
"""Test timeout parameter and environment variable support"""
|
||||
debug_log("\n🆕 測試參數功能")
|
||||
debug_log("-" * 30)
|
||||
|
||||
try:
|
||||
@ -216,45 +223,50 @@ def test_new_parameters():
|
||||
debug_log("❌ timeout 參數不存在")
|
||||
return False
|
||||
|
||||
# 檢查 force_web_ui 參數
|
||||
if 'force_web_ui' in sig.parameters:
|
||||
force_web_ui_param = sig.parameters['force_web_ui']
|
||||
debug_log(f"✅ force_web_ui 參數存在,預設值: {force_web_ui_param.default}")
|
||||
# 檢查環境變數支援
|
||||
import os
|
||||
current_force_web = os.getenv("FORCE_WEB")
|
||||
if current_force_web:
|
||||
debug_log(f"✅ 檢測到 FORCE_WEB 環境變數: {current_force_web}")
|
||||
else:
|
||||
debug_log("❌ force_web_ui 參數不存在")
|
||||
return False
|
||||
debug_log("ℹ️ FORCE_WEB 環境變數未設定(將使用預設邏輯)")
|
||||
|
||||
debug_log("✅ 所有新參數功能正常")
|
||||
debug_log("✅ 參數功能正常")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
debug_log(f"❌ 新參數測試失敗: {e}")
|
||||
debug_log(f"❌ 參數測試失敗: {e}")
|
||||
return False
|
||||
|
||||
def test_force_web_ui_mode():
|
||||
"""Test force web UI mode"""
|
||||
debug_log("\n🌐 測試強制 Web UI 模式")
|
||||
def test_environment_web_ui_mode():
|
||||
"""Test environment-based Web UI mode"""
|
||||
debug_log("\n🌐 測試環境變數控制 Web UI 模式")
|
||||
debug_log("-" * 30)
|
||||
|
||||
try:
|
||||
from .server import interactive_feedback, is_remote_environment, can_use_gui
|
||||
import os
|
||||
|
||||
# 顯示當前環境狀態
|
||||
is_remote = is_remote_environment()
|
||||
gui_available = can_use_gui()
|
||||
force_web_env = os.getenv("FORCE_WEB", "").lower()
|
||||
|
||||
debug_log(f"當前環境 - 遠端: {is_remote}, GUI 可用: {gui_available}")
|
||||
debug_log(f"FORCE_WEB 環境變數: {force_web_env or '未設定'}")
|
||||
|
||||
if not is_remote and gui_available:
|
||||
debug_log("✅ 在本地 GUI 環境中可以使用 force_web_ui=True 強制使用 Web UI")
|
||||
debug_log("💡 這對於測試 Web UI 功能非常有用")
|
||||
if force_web_env in ("true", "1", "yes", "on"):
|
||||
debug_log("✅ FORCE_WEB 已啟用,將強制使用 Web UI")
|
||||
elif not is_remote and gui_available:
|
||||
debug_log("ℹ️ 本地 GUI 環境,將使用 Qt GUI")
|
||||
debug_log("💡 可設定 FORCE_WEB=true 強制使用 Web UI 進行測試")
|
||||
else:
|
||||
debug_log("ℹ️ 當前環境會自動使用 Web UI")
|
||||
debug_log("ℹ️ 將自動使用 Web UI(遠端環境或 GUI 不可用)")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
debug_log(f"❌ 強制 Web UI 測試失敗: {e}")
|
||||
debug_log(f"❌ 環境變數測試失敗: {e}")
|
||||
return False
|
||||
|
||||
def interactive_demo(session_info):
|
||||
@ -309,8 +321,8 @@ if __name__ == "__main__":
|
||||
# Test new parameters
|
||||
params_test = test_new_parameters()
|
||||
|
||||
# Test force web UI mode
|
||||
force_test = test_force_web_ui_mode()
|
||||
# Test environment-based Web UI mode
|
||||
env_web_test = test_environment_web_ui_mode()
|
||||
|
||||
# Test MCP integration
|
||||
mcp_test = test_mcp_integration()
|
||||
@ -319,7 +331,7 @@ if __name__ == "__main__":
|
||||
web_test, session_info = test_web_ui()
|
||||
|
||||
debug_log("\n" + "=" * 60)
|
||||
if env_test and params_test and force_test and mcp_test and web_test:
|
||||
if env_test and params_test and env_web_test and mcp_test and web_test:
|
||||
debug_log("🎊 所有測試完成!準備使用 Interactive Feedback MCP")
|
||||
debug_log("\n📖 使用方法:")
|
||||
debug_log(" 1. 在 Cursor/Cline 中配置此 MCP 服務器")
|
||||
|
@ -298,6 +298,31 @@ class WebUIManager:
|
||||
else:
|
||||
return HTMLResponse(self._get_simple_feedback_html(session_id, session))
|
||||
|
||||
@self.app.get("/api/translations")
|
||||
async def get_translations():
|
||||
"""提供語系檔案 API"""
|
||||
try:
|
||||
translations = {}
|
||||
locales_dir = package_dir / "locales"
|
||||
|
||||
if locales_dir.exists():
|
||||
for lang_dir in locales_dir.iterdir():
|
||||
if lang_dir.is_dir():
|
||||
lang_code = lang_dir.name
|
||||
translation_file = lang_dir / "translations.json"
|
||||
|
||||
if translation_file.exists():
|
||||
try:
|
||||
with open(translation_file, 'r', encoding='utf-8') as f:
|
||||
translations[lang_code] = json.load(f)
|
||||
except Exception as e:
|
||||
debug_log(f"載入語言檔案失敗 {lang_code}: {e}")
|
||||
|
||||
return JSONResponse(translations)
|
||||
except Exception as e:
|
||||
debug_log(f"語系 API 錯誤: {e}")
|
||||
return JSONResponse({}, status_code=500)
|
||||
|
||||
@self.app.websocket("/ws/{session_id}")
|
||||
async def websocket_endpoint(websocket: WebSocket, session_id: str):
|
||||
"""WebSocket 連接處理"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user