將 gui 和 webui 的對話框加入 ctrl +v 就能複製貼上圖片的更能

This commit is contained in:
Minidoracat 2025-06-02 13:13:08 +08:00
parent f148841cc0
commit 0f56c7c5fb
12 changed files with 331 additions and 350 deletions

View File

@ -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

View File

@ -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)

View File

@ -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',
}
# 檢查是否有對應的新鍵

View File

@ -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!"
}
}

View File

@ -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请测试这些功能并提供回馈"
}
}

View File

@ -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請測試這些功能並提供回饋"
}
}

View File

@ -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:

View File

@ -26,185 +26,27 @@ class I18nManager {
}
/**
* 獲取內嵌的備用翻譯
* 獲取內嵌的備用翻譯僅保留基本錯誤訊息
*/
_getEmbeddedTranslations() {
return {
'zh-TW': {
app: {
title: 'Interactive Feedback MCP',
projectDirectory: '專案目錄',
language: '語言'
app: { title: 'Interactive Feedback MCP' },
loading: '載入中...',
error: '載入失敗',
retry: '重試'
},
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: '简体中文'
}
},
'en': {
app: {
title: 'Interactive Feedback MCP',
projectDirectory: 'Project Directory',
language: 'Language'
app: { title: 'Interactive Feedback MCP' },
loading: 'Loading...',
error: 'Loading failed',
retry: 'Retry'
},
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: '简体中文'
}
},
'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: '重试'
}
};
}

View File

@ -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="請在這裡輸入您的回饋、建議或問題...&#10;&#10;💡 小提示:&#10;• 按 Ctrl+Enter 可快速提交回饋&#10;• 按 Ctrl+V 可直接貼上剪貼簿圖片"
></textarea>
<textarea id="feedbackText" class="text-input"
placeholder="請在這裡輸入您的回饋、建議或問題...&#10;&#10;💡 小提示:&#10;• 按 Ctrl+Enter 可快速提交回饋&#10;• 按 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>

View File

@ -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

View File

@ -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 服務器")

View File

@ -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 連接處理"""