新增統一調試日誌模組,重構現有代碼以使用新模組進行調試日誌輸出。更新多個模組以整合調試功能,並在測試模組中添加調試日誌支持。

This commit is contained in:
Minidoracat 2025-05-31 07:50:10 +08:00
parent c717db9e7f
commit 7ccd9f5397
8 changed files with 323 additions and 1271 deletions

View File

@ -0,0 +1,85 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
統一調試日誌模組
================
提供統一的調試日誌功能確保調試輸出不會干擾 MCP 通信
所有調試輸出都會發送到 stderr並且只在調試模式啟用時才輸出
使用方法
```python
from .debug import debug_log
debug_log("這是一條調試信息")
```
環境變數控制
- MCP_DEBUG=true/1/yes/on: 啟用調試模式
- MCP_DEBUG=false/0/no/off: 關閉調試模式默認
作者: Minidoracat
"""
import os
import sys
from typing import Any
def debug_log(message: Any, prefix: str = "DEBUG") -> None:
"""
輸出調試訊息到標準錯誤避免污染標準輸出
Args:
message: 要輸出的調試信息
prefix: 調試信息的前綴標識默認為 "DEBUG"
"""
# 只在啟用調試模式時才輸出,避免干擾 MCP 通信
if not os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on"):
return
try:
# 確保消息是字符串類型
if not isinstance(message, str):
message = str(message)
# 安全地輸出到 stderr處理編碼問題
try:
print(f"[{prefix}] {message}", file=sys.stderr, flush=True)
except UnicodeEncodeError:
# 如果遇到編碼問題,使用 ASCII 安全模式
safe_message = message.encode('ascii', errors='replace').decode('ascii')
print(f"[{prefix}] {safe_message}", file=sys.stderr, flush=True)
except Exception:
# 最後的備用方案:靜默失敗,不影響主程序
pass
def gui_debug_log(message: Any) -> None:
"""GUI 模組專用的調試日誌"""
debug_log(message, "GUI")
def i18n_debug_log(message: Any) -> None:
"""國際化模組專用的調試日誌"""
debug_log(message, "I18N")
def server_debug_log(message: Any) -> None:
"""伺服器模組專用的調試日誌"""
debug_log(message, "SERVER")
def web_debug_log(message: Any) -> None:
"""Web UI 模組專用的調試日誌"""
debug_log(message, "WEB")
def is_debug_enabled() -> bool:
"""檢查是否啟用了調試模式"""
return os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on")
def set_debug_mode(enabled: bool) -> None:
"""設置調試模式(用於測試)"""
os.environ["MCP_DEBUG"] = "true" if enabled else "false"

View File

@ -35,28 +35,7 @@ from PySide6.QtGui import QFont, QPixmap, QDragEnterEvent, QDropEvent, QKeySeque
# 導入多語系支援 # 導入多語系支援
from .i18n import t, get_i18n_manager from .i18n import t, get_i18n_manager
# ===== 調試日誌函數 ===== from .debug import gui_debug_log as debug_log
def debug_log(message: str) -> None:
"""輸出調試訊息到標準錯誤,避免污染標準輸出"""
# 只在啟用調試模式時才輸出,避免干擾 MCP 通信
if not os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on"):
return
try:
# 確保消息是字符串類型
if not isinstance(message, str):
message = str(message)
# 安全地輸出到 stderr處理編碼問題
try:
print(f"[GUI_DEBUG] {message}", file=sys.stderr, flush=True)
except UnicodeEncodeError:
# 如果遇到編碼問題,使用 ASCII 安全模式
safe_message = message.encode('ascii', errors='replace').decode('ascii')
print(f"[GUI_DEBUG] {safe_message}", file=sys.stderr, flush=True)
except Exception:
# 最後的備用方案:靜默失敗,不影響主程序
pass
# ===== 型別定義 ===== # ===== 型別定義 =====
class FeedbackResult(TypedDict): class FeedbackResult(TypedDict):
@ -679,6 +658,9 @@ class FeedbackWindow(QMainWindow):
# 操作按鈕 # 操作按鈕
self._create_action_buttons(layout) self._create_action_buttons(layout)
# 設置快捷鍵
self._setup_shortcuts()
def _create_menu_bar(self) -> None: def _create_menu_bar(self) -> None:
"""創建菜單欄""" """創建菜單欄"""
@ -943,6 +925,22 @@ class FeedbackWindow(QMainWindow):
layout.addLayout(button_layout) layout.addLayout(button_layout)
def _setup_shortcuts(self) -> None:
"""設置快捷鍵"""
# Ctrl+Enter 提交回饋
submit_shortcut = QShortcut(QKeySequence("Ctrl+Return"), self)
submit_shortcut.activated.connect(self._submit_feedback)
# Escape 取消
cancel_shortcut = QShortcut(QKeySequence("Esc"), self)
cancel_shortcut.activated.connect(self._cancel_feedback)
# 更新提交按鈕的提示文字,顯示快捷鍵
if hasattr(self, 'submit_button'):
self.submit_button.setToolTip(f"{t('btn_submit_feedback')} (Ctrl+Enter)")
if hasattr(self, 'cancel_button'):
self.cancel_button.setToolTip(f"{t('btn_cancel')} (Esc)")
def _apply_dark_style(self) -> None: def _apply_dark_style(self) -> None:
"""應用深色主題""" """應用深色主題"""
self.setStyleSheet(""" self.setStyleSheet("""

View File

@ -23,6 +23,8 @@ import json
from typing import Dict, Any, Optional, Union from typing import Dict, Any, Optional, Union
from pathlib import Path from pathlib import Path
from .debug import i18n_debug_log as debug_log
class I18nManager: class I18nManager:
"""國際化管理器 - 新架構版本""" """國際化管理器 - 新架構版本"""
@ -60,13 +62,13 @@ class I18nManager:
with open(translation_file, 'r', encoding='utf-8') as f: with open(translation_file, 'r', encoding='utf-8') as f:
data = json.load(f) data = json.load(f)
self._translations[lang_code] = data self._translations[lang_code] = data
print(f"[I18N] 成功載入語言 {lang_code}: {data.get('meta', {}).get('displayName', lang_code)}") debug_log(f"成功載入語言 {lang_code}: {data.get('meta', {}).get('displayName', lang_code)}")
except Exception as e: except Exception as e:
print(f"[I18N] 載入語言檔案失敗 {lang_code}: {e}") debug_log(f"載入語言檔案失敗 {lang_code}: {e}")
# 如果載入失敗,使用空的翻譯 # 如果載入失敗,使用空的翻譯
self._translations[lang_code] = {} self._translations[lang_code] = {}
else: else:
print(f"[I18N] 找不到語言檔案: {translation_file}") debug_log(f"找不到語言檔案: {translation_file}")
self._translations[lang_code] = {} self._translations[lang_code] = {}
def _detect_language(self) -> str: def _detect_language(self) -> str:
@ -312,10 +314,10 @@ class I18nManager:
if language_code not in self._supported_languages: if language_code not in self._supported_languages:
self._supported_languages.append(language_code) self._supported_languages.append(language_code)
print(f"[I18N] 成功添加語言 {language_code}: {data.get('meta', {}).get('displayName', language_code)}") debug_log(f"成功添加語言 {language_code}: {data.get('meta', {}).get('displayName', language_code)}")
return True return True
except Exception as e: except Exception as e:
print(f"[I18N] 添加語言失敗 {language_code}: {e}") debug_log(f"添加語言失敗 {language_code}: {e}")
return False return False

View File

@ -1,15 +1,20 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
互動式回饋收集 MCP MCP 服器主程式
============================ ================
這是一個基於 Model Context Protocol (MCP) 的服務器提供互動式用戶回饋收集功能 Interactive Feedback MCP 的核心伺服器程式提供用戶互動回饋功能
支援文字回饋圖片上傳並自動偵測運行環境選擇適當的用戶介面 支援智能環境檢測自動選擇 Qt GUI Web UI 介面
作者: Fábio Ferreira 主要功能
靈感來源: dotcursorrules.com - 環境檢測本地/遠端
增強功能: 圖片支援和環境偵測 - 介面選擇GUI/Web UI
- 圖片處理和 MCP 整合
- 回饋結果標準化
作者: Fábio Ferreira (原作者)
增強: Minidoracat (Web UI, 圖片支援, 環境檢測)
""" """
import os import os
@ -26,6 +31,12 @@ from mcp.server.fastmcp.utilities.types import Image as MCPImage
from mcp.types import TextContent from mcp.types import TextContent
from pydantic import Field from pydantic import Field
# 導入多語系支援
from .i18n import get_i18n_manager
# 導入統一的調試功能
from .debug import server_debug_log as debug_log
# ===== 編碼初始化 ===== # ===== 編碼初始化 =====
def init_encoding(): def init_encoding():
"""初始化編碼設置,確保正確處理中文字符""" """初始化編碼設置,確保正確處理中文字符"""
@ -90,29 +101,6 @@ mcp = FastMCP(SERVER_NAME, version=__version__)
# ===== 工具函數 ===== # ===== 工具函數 =====
def debug_log(message: str) -> None:
"""輸出調試訊息到標準錯誤,避免污染標準輸出"""
# 只在啟用調試模式時才輸出,避免干擾 MCP 通信
if not os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on"):
return
try:
# 確保消息是字符串類型
if not isinstance(message, str):
message = str(message)
# 安全地輸出到 stderr處理編碼問題
try:
print(f"[DEBUG] {message}", file=sys.stderr, flush=True)
except UnicodeEncodeError:
# 如果遇到編碼問題,使用 ASCII 安全模式
safe_message = message.encode('ascii', errors='replace').decode('ascii')
print(f"[DEBUG] {safe_message}", file=sys.stderr, flush=True)
except Exception:
# 最後的備用方案:靜默失敗,不影響主程序
pass
def is_remote_environment() -> bool: def is_remote_environment() -> bool:
""" """
檢測是否在遠端環境中運行 檢測是否在遠端環境中運行

View File

@ -1,17 +1,45 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Test script for Qt GUI functionality # -*- coding: utf-8 -*-
import os """
import sys Qt GUI 測試模組
from pathlib import Path ===============
# 添加項目路徑到 Python 路徑 用於測試 Interactive Feedback MCP Qt GUI 功能
sys.path.insert(0, str(Path(__file__).parent)) 包含完整的 GUI 功能測試支援持久化模式
功能測試
- Qt GUI 界面啟動
- 多語言支援
- 圖片上傳功能
- 回饋提交功能
- 快捷鍵功能
使用方法
python -m mcp_feedback_enhanced.test_qt_gui [--persistent]
作者: Minidoracat
"""
import sys
import os
from typing import Optional, Dict, Any
# 添加專案根目錄到 Python 路徑
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from .debug import debug_log
# 嘗試導入 Qt GUI 模組
try:
from .feedback_ui import feedback_ui
QT_GUI_AVAILABLE = True
except ImportError as e:
debug_log(f"⚠️ 無法導入 Qt GUI 模組: {e}")
QT_GUI_AVAILABLE = False
def test_qt_gui(): def test_qt_gui():
"""測試 Qt GUI 功能""" """測試 Qt GUI 功能"""
try: try:
from .feedback_ui import feedback_ui
# 測試參數 # 測試參數
project_directory = os.getcwd() project_directory = os.getcwd()
prompt = """🎯 圖片預覽和視窗調整測試 prompt = """🎯 圖片預覽和視窗調整測試
@ -35,54 +63,54 @@ def test_qt_gui():
請測試這些功能並提供回饋""" 請測試這些功能並提供回饋"""
print("🚀 啟動 Qt GUI 測試...") debug_log("🚀 啟動 Qt GUI 測試...")
print("📝 測試項目:") debug_log("📝 測試項目:")
print(" - 圖片預覽功能") debug_log(" - 圖片預覽功能")
print(" - X刪除按鈕") debug_log(" - X刪除按鈕")
print(" - 視窗大小調整") debug_log(" - 視窗大小調整")
print(" - 分割器調整") debug_log(" - 分割器調整")
print() debug_log()
# 啟動 GUI # 啟動 GUI
result = feedback_ui(project_directory, prompt) result = feedback_ui(project_directory, prompt)
if result: if result:
print("\n✅ 測試完成!") debug_log("\n✅ 測試完成!")
print(f"📄 收到回饋: {result.get('interactive_feedback', '')}") debug_log(f"📄 收到回饋: {result.get('interactive_feedback', '')}")
if result.get('images'): if result.get('images'):
print(f"🖼️ 收到圖片: {len(result['images'])}") debug_log(f"🖼️ 收到圖片: {len(result['images'])}")
if result.get('logs'): if result.get('logs'):
print(f"📋 命令日誌: {len(result['logs'])}") debug_log(f"📋 命令日誌: {len(result['logs'])}")
else: else:
print("\n❌ 測試取消或無回饋") debug_log("\n❌ 測試取消或無回饋")
except ImportError as e: except ImportError as e:
print(f"❌ 導入錯誤: {e}") debug_log(f"❌ 導入錯誤: {e}")
print("請確保已安裝 PySide6: pip install PySide6") debug_log("請確保已安裝 PySide6: pip install PySide6")
return False return False
except Exception as e: except Exception as e:
print(f"❌ 測試錯誤: {e}") debug_log(f"❌ 測試錯誤: {e}")
return False return False
return True return True
if __name__ == "__main__": if __name__ == "__main__":
print("🧪 Interactive Feedback MCP - Qt GUI 測試") debug_log("🧪 Interactive Feedback MCP - Qt GUI 測試")
print("=" * 50) debug_log("=" * 50)
# 檢查環境 # 檢查環境
try: try:
from PySide6.QtWidgets import QApplication from PySide6.QtWidgets import QApplication
print("✅ PySide6 已安裝") debug_log("✅ PySide6 已安裝")
except ImportError: except ImportError:
print("❌ PySide6 未安裝,請執行: pip install PySide6") debug_log("❌ PySide6 未安裝,請執行: pip install PySide6")
sys.exit(1) sys.exit(1)
# 運行測試 # 運行測試
success = test_qt_gui() success = test_qt_gui()
if success: if success:
print("\n🎉 測試程序運行完成") debug_log("\n🎉 測試程序運行完成")
else: else:
print("\n💥 測試程序運行失敗") debug_log("\n💥 測試程序運行失敗")
sys.exit(1) sys.exit(1)

View File

@ -1,12 +1,44 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" """
Test script for Interactive Feedback MCP Web UI Web UI 測試模組
===============
用於測試 Interactive Feedback MCP Web UI 功能
包含完整的 Web UI 功能測試支援持久化模式
功能測試
- Web UI 服務器啟動
- WebSocket 通訊
- 回饋提交功能
- 圖片上傳功能
- 命令執行功能
使用方法
python -m mcp_feedback_enhanced.test_web_ui [--persistent]
作者: Minidoracat
""" """
import sys
import threading import asyncio
import webbrowser
import time import time
import socket import sys
from pathlib import Path import os
from typing import Dict, Any, Optional
# 添加專案根目錄到 Python 路徑
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from .debug import debug_log
# 嘗試導入 Web UI 模組
try:
from .web_ui import get_web_ui_manager, launch_web_feedback_ui
WEB_UI_AVAILABLE = True
except ImportError as e:
debug_log(f"⚠️ 無法導入 Web UI 模組: {e}")
WEB_UI_AVAILABLE = False
def find_free_port(): def find_free_port():
"""Find a free port to use for testing""" """Find a free port to use for testing"""
@ -19,44 +51,44 @@ def find_free_port():
def test_web_ui(keep_running=False): def test_web_ui(keep_running=False):
"""Test the Web UI functionality""" """Test the Web UI functionality"""
print("🧪 測試 Interactive Feedback MCP Web UI") debug_log("🧪 測試 Interactive Feedback MCP Web UI")
print("=" * 50) debug_log("=" * 50)
# Test import # Test import
try: try:
from .web_ui import WebUIManager, launch_web_feedback_ui from .web_ui import WebUIManager, launch_web_feedback_ui
print("✅ Web UI 模組匯入成功") debug_log("✅ Web UI 模組匯入成功")
except ImportError as e: except ImportError as e:
print(f"❌ Web UI 模組匯入失敗: {e}") debug_log(f"❌ Web UI 模組匯入失敗: {e}")
return False, None return False, None
# Find free port # Find free port
try: try:
free_port = find_free_port() free_port = find_free_port()
print(f"🔍 找到可用端口: {free_port}") debug_log(f"🔍 找到可用端口: {free_port}")
except Exception as e: except Exception as e:
print(f"❌ 尋找可用端口失敗: {e}") debug_log(f"❌ 尋找可用端口失敗: {e}")
return False, None return False, None
# Test manager creation # Test manager creation
try: try:
manager = WebUIManager(port=free_port) manager = WebUIManager(port=free_port)
print("✅ WebUIManager 創建成功") debug_log("✅ WebUIManager 創建成功")
except Exception as e: except Exception as e:
print(f"❌ WebUIManager 創建失敗: {e}") debug_log(f"❌ WebUIManager 創建失敗: {e}")
return False, None return False, None
# Test server start (with timeout) # Test server start (with timeout)
server_started = False server_started = False
try: try:
print("🚀 啟動 Web 服務器...") debug_log("🚀 啟動 Web 服務器...")
def start_server(): def start_server():
try: try:
manager.start_server() manager.start_server()
return True return True
except Exception as e: except Exception as e:
print(f"服務器啟動錯誤: {e}") debug_log(f"服務器啟動錯誤: {e}")
return False return False
# Start server in thread # Start server in thread
@ -73,17 +105,17 @@ def test_web_ui(keep_running=False):
result = s.connect_ex((manager.host, manager.port)) result = s.connect_ex((manager.host, manager.port))
if result == 0: if result == 0:
server_started = True server_started = True
print("✅ Web 服務器啟動成功") debug_log("✅ Web 服務器啟動成功")
print(f"🌐 服務器運行在: http://{manager.host}:{manager.port}") debug_log(f"🌐 服務器運行在: http://{manager.host}:{manager.port}")
else: else:
print(f"❌ 無法連接到服務器端口 {manager.port}") debug_log(f"❌ 無法連接到服務器端口 {manager.port}")
except Exception as e: except Exception as e:
print(f"❌ Web 服務器啟動失敗: {e}") debug_log(f"❌ Web 服務器啟動失敗: {e}")
return False, None return False, None
if not server_started: if not server_started:
print("❌ 服務器未能正常啟動") debug_log("❌ 服務器未能正常啟動")
return False, None return False, None
# Test session creation # Test session creation
@ -97,26 +129,26 @@ def test_web_ui(keep_running=False):
'session_id': session_id, 'session_id': session_id,
'url': f"http://{manager.host}:{manager.port}/session/{session_id}" 'url': f"http://{manager.host}:{manager.port}/session/{session_id}"
} }
print(f"✅ 測試會話創建成功 (ID: {session_id[:8]}...)") debug_log(f"✅ 測試會話創建成功 (ID: {session_id[:8]}...)")
print(f"🔗 測試 URL: {session_info['url']}") debug_log(f"🔗 測試 URL: {session_info['url']}")
except Exception as e: except Exception as e:
print(f"❌ 會話創建失敗: {e}") debug_log(f"❌ 會話創建失敗: {e}")
return False, None return False, None
print("\n" + "=" * 50) debug_log("\n" + "=" * 50)
print("🎉 所有測試通過Web UI 準備就緒") debug_log("🎉 所有測試通過Web UI 準備就緒")
print("📝 注意事項:") debug_log("📝 注意事項:")
print(" - Web UI 會在 SSH remote 環境下自動啟用") debug_log(" - Web UI 會在 SSH remote 環境下自動啟用")
print(" - 本地環境會繼續使用 Qt GUI") debug_log(" - 本地環境會繼續使用 Qt GUI")
print(" - 支援即時命令執行和 WebSocket 通訊") debug_log(" - 支援即時命令執行和 WebSocket 通訊")
print(" - 提供現代化的深色主題界面") debug_log(" - 提供現代化的深色主題界面")
return True, session_info return True, session_info
def test_environment_detection(): def test_environment_detection():
"""Test environment detection logic""" """Test environment detection logic"""
print("🔍 測試環境檢測功能") debug_log("🔍 測試環境檢測功能")
print("-" * 30) debug_log("-" * 30)
try: try:
from .server import is_remote_environment, can_use_gui from .server import is_remote_environment, can_use_gui
@ -124,47 +156,47 @@ def test_environment_detection():
remote_detected = is_remote_environment() remote_detected = is_remote_environment()
gui_available = can_use_gui() gui_available = can_use_gui()
print(f"遠端環境檢測: {'' if remote_detected else ''}") debug_log(f"遠端環境檢測: {'' if remote_detected else ''}")
print(f"GUI 可用性: {'' if gui_available else ''}") debug_log(f"GUI 可用性: {'' if gui_available else ''}")
if remote_detected: if remote_detected:
print("✅ 將使用 Web UI (適合遠端開發環境)") debug_log("✅ 將使用 Web UI (適合遠端開發環境)")
else: else:
print("✅ 將使用 Qt GUI (本地環境)") debug_log("✅ 將使用 Qt GUI (本地環境)")
return True return True
except Exception as e: except Exception as e:
print(f"❌ 環境檢測失敗: {e}") debug_log(f"❌ 環境檢測失敗: {e}")
return False return False
def test_mcp_integration(): def test_mcp_integration():
"""Test MCP server integration""" """Test MCP server integration"""
print("\n🔧 測試 MCP 整合功能") debug_log("\n🔧 測試 MCP 整合功能")
print("-" * 30) debug_log("-" * 30)
try: try:
from .server import interactive_feedback from .server import interactive_feedback
print("✅ MCP 工具函數可用") debug_log("✅ MCP 工具函數可用")
# Test timeout parameter # Test timeout parameter
print("✅ 支援 timeout 參數") debug_log("✅ 支援 timeout 參數")
# Test force_web_ui parameter # Test force_web_ui parameter
print("✅ 支援 force_web_ui 參數") debug_log("✅ 支援 force_web_ui 參數")
# Test would require actual MCP call, so just verify import # Test would require actual MCP call, so just verify import
print("✅ 準備接受來自 AI 助手的調用") debug_log("✅ 準備接受來自 AI 助手的調用")
return True return True
except Exception as e: except Exception as e:
print(f"❌ MCP 整合測試失敗: {e}") debug_log(f"❌ MCP 整合測試失敗: {e}")
return False return False
def test_new_parameters(): def test_new_parameters():
"""Test new timeout and force_web_ui parameters""" """Test new timeout and force_web_ui parameters"""
print("\n🆕 測試新增參數功能") debug_log("\n🆕 測試新增參數功能")
print("-" * 30) debug_log("-" * 30)
try: try:
from .server import interactive_feedback from .server import interactive_feedback
@ -176,30 +208,30 @@ def test_new_parameters():
# 檢查 timeout 參數 # 檢查 timeout 參數
if 'timeout' in sig.parameters: if 'timeout' in sig.parameters:
timeout_param = sig.parameters['timeout'] timeout_param = sig.parameters['timeout']
print(f"✅ timeout 參數存在,預設值: {timeout_param.default}") debug_log(f"✅ timeout 參數存在,預設值: {timeout_param.default}")
else: else:
print("❌ timeout 參數不存在") debug_log("❌ timeout 參數不存在")
return False return False
# 檢查 force_web_ui 參數 # 檢查 force_web_ui 參數
if 'force_web_ui' in sig.parameters: if 'force_web_ui' in sig.parameters:
force_web_ui_param = sig.parameters['force_web_ui'] force_web_ui_param = sig.parameters['force_web_ui']
print(f"✅ force_web_ui 參數存在,預設值: {force_web_ui_param.default}") debug_log(f"✅ force_web_ui 參數存在,預設值: {force_web_ui_param.default}")
else: else:
print("❌ force_web_ui 參數不存在") debug_log("❌ force_web_ui 參數不存在")
return False return False
print("✅ 所有新參數功能正常") debug_log("✅ 所有新參數功能正常")
return True return True
except Exception as e: except Exception as e:
print(f"❌ 新參數測試失敗: {e}") debug_log(f"❌ 新參數測試失敗: {e}")
return False return False
def test_force_web_ui_mode(): def test_force_web_ui_mode():
"""Test force web UI mode""" """Test force web UI mode"""
print("\n🌐 測試強制 Web UI 模式") debug_log("\n🌐 測試強制 Web UI 模式")
print("-" * 30) debug_log("-" * 30)
try: try:
from .server import interactive_feedback, is_remote_environment, can_use_gui from .server import interactive_feedback, is_remote_environment, can_use_gui
@ -208,65 +240,65 @@ def test_force_web_ui_mode():
is_remote = is_remote_environment() is_remote = is_remote_environment()
gui_available = can_use_gui() gui_available = can_use_gui()
print(f"當前環境 - 遠端: {is_remote}, GUI 可用: {gui_available}") debug_log(f"當前環境 - 遠端: {is_remote}, GUI 可用: {gui_available}")
if not is_remote and gui_available: if not is_remote and gui_available:
print("✅ 在本地 GUI 環境中可以使用 force_web_ui=True 強制使用 Web UI") debug_log("✅ 在本地 GUI 環境中可以使用 force_web_ui=True 強制使用 Web UI")
print("💡 這對於測試 Web UI 功能非常有用") debug_log("💡 這對於測試 Web UI 功能非常有用")
else: else:
print(" 當前環境會自動使用 Web UI") debug_log(" 當前環境會自動使用 Web UI")
return True return True
except Exception as e: except Exception as e:
print(f"❌ 強制 Web UI 測試失敗: {e}") debug_log(f"❌ 強制 Web UI 測試失敗: {e}")
return False return False
def interactive_demo(session_info): def interactive_demo(session_info):
"""Run interactive demo with the Web UI""" """Run interactive demo with the Web UI"""
print(f"\n🌐 Web UI 持久化運行模式") debug_log(f"\n🌐 Web UI 持久化運行模式")
print("=" * 50) debug_log("=" * 50)
print(f"服務器地址: http://{session_info['manager'].host}:{session_info['manager'].port}") debug_log(f"服務器地址: http://{session_info['manager'].host}:{session_info['manager'].port}")
print(f"測試會話: {session_info['url']}") debug_log(f"測試會話: {session_info['url']}")
print("\n📖 操作指南:") debug_log("\n📖 操作指南:")
print(" 1. 在瀏覽器中開啟上面的測試 URL") debug_log(" 1. 在瀏覽器中開啟上面的測試 URL")
print(" 2. 嘗試以下功能:") debug_log(" 2. 嘗試以下功能:")
print(" - 點擊 '顯示命令區塊' 按鈕") debug_log(" - 點擊 '顯示命令區塊' 按鈕")
print(" - 輸入命令如 'echo Hello World' 並執行") debug_log(" - 輸入命令如 'echo Hello World' 並執行")
print(" - 在回饋區域輸入文字") debug_log(" - 在回饋區域輸入文字")
print(" - 使用 Ctrl+Enter 提交回饋") debug_log(" - 使用 Ctrl+Enter 提交回饋")
print(" 3. 測試 WebSocket 即時通訊功能") debug_log(" 3. 測試 WebSocket 即時通訊功能")
print("\n⌨️ 控制選項:") debug_log("\n⌨️ 控制選項:")
print(" - 按 Enter 繼續運行") debug_log(" - 按 Enter 繼續運行")
print(" - 輸入 'q''quit' 停止服務器") debug_log(" - 輸入 'q''quit' 停止服務器")
while True: while True:
try: try:
user_input = input("\n>>> ").strip().lower() user_input = input("\n>>> ").strip().lower()
if user_input in ['q', 'quit', 'exit']: if user_input in ['q', 'quit', 'exit']:
print("🛑 停止服務器...") debug_log("🛑 停止服務器...")
break break
elif user_input == '': elif user_input == '':
print(f"🔄 服務器持續運行在: {session_info['url']}") debug_log(f"🔄 服務器持續運行在: {session_info['url']}")
print(" 瀏覽器應該仍可正常訪問") debug_log(" 瀏覽器應該仍可正常訪問")
else: else:
print("❓ 未知命令。按 Enter 繼續運行,或輸入 'q' 退出") debug_log("❓ 未知命令。按 Enter 繼續運行,或輸入 'q' 退出")
except KeyboardInterrupt: except KeyboardInterrupt:
print("\n🛑 收到中斷信號,停止服務器...") debug_log("\n🛑 收到中斷信號,停止服務器...")
break break
print("✅ Web UI 測試完成") debug_log("✅ Web UI 測試完成")
if __name__ == "__main__": if __name__ == "__main__":
print("Interactive Feedback MCP - Web UI 測試") debug_log("Interactive Feedback MCP - Web UI 測試")
print("=" * 60) debug_log("=" * 60)
# Check if user wants persistent mode # Check if user wants persistent mode
persistent_mode = len(sys.argv) > 1 and sys.argv[1] in ['--persistent', '-p', '--demo'] persistent_mode = len(sys.argv) > 1 and sys.argv[1] in ['--persistent', '-p', '--demo']
if not persistent_mode: if not persistent_mode:
print("💡 提示: 使用 'python test_web_ui.py --persistent' 啟動持久化測試模式") debug_log("💡 提示: 使用 'python test_web_ui.py --persistent' 啟動持久化測試模式")
print() debug_log()
# Test environment detection # Test environment detection
env_test = test_environment_detection() env_test = test_environment_detection()
@ -283,30 +315,30 @@ if __name__ == "__main__":
# Test Web UI # Test Web UI
web_test, session_info = test_web_ui() web_test, session_info = test_web_ui()
print("\n" + "=" * 60) 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 force_test and mcp_test and web_test:
print("🎊 所有測試完成!準備使用 Interactive Feedback MCP") debug_log("🎊 所有測試完成!準備使用 Interactive Feedback MCP")
print("\n📖 使用方法:") debug_log("\n📖 使用方法:")
print(" 1. 在 Cursor/Cline 中配置此 MCP 服務器") debug_log(" 1. 在 Cursor/Cline 中配置此 MCP 服務器")
print(" 2. AI 助手會自動調用 interactive_feedback 工具") debug_log(" 2. AI 助手會自動調用 interactive_feedback 工具")
print(" 3. 根據環境自動選擇 GUI 或 Web UI") debug_log(" 3. 根據環境自動選擇 GUI 或 Web UI")
print(" 4. 提供回饋後繼續工作流程") debug_log(" 4. 提供回饋後繼續工作流程")
print("\n✨ Web UI 新功能:") debug_log("\n✨ Web UI 新功能:")
print(" - 支援 SSH remote 開發環境") debug_log(" - 支援 SSH remote 開發環境")
print(" - 現代化深色主題界面") debug_log(" - 現代化深色主題界面")
print(" - WebSocket 即時通訊") debug_log(" - WebSocket 即時通訊")
print(" - 自動瀏覽器啟動") debug_log(" - 自動瀏覽器啟動")
print(" - 命令執行和即時輸出") debug_log(" - 命令執行和即時輸出")
if persistent_mode and session_info: if persistent_mode and session_info:
interactive_demo(session_info) interactive_demo(session_info)
else: else:
print("\n✅ 測試完成 - 系統已準備就緒!") debug_log("\n✅ 測試完成 - 系統已準備就緒!")
if session_info: if session_info:
print(f"💡 您可以現在就在瀏覽器中測試: {session_info['url']}") debug_log(f"💡 您可以現在就在瀏覽器中測試: {session_info['url']}")
print(" (服務器會繼續運行一小段時間)") debug_log(" (服務器會繼續運行一小段時間)")
time.sleep(10) # Keep running for a short time for immediate testing time.sleep(10) # Keep running for a short time for immediate testing
else: else:
print("❌ 部分測試失敗,請檢查錯誤信息") debug_log("❌ 部分測試失敗,請檢查錯誤信息")
sys.exit(1) sys.exit(1)

View File

@ -33,6 +33,8 @@ from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
import uvicorn import uvicorn
from .debug import web_debug_log as debug_log
# ===== 常數定義 ===== # ===== 常數定義 =====
MAX_IMAGE_SIZE = 1 * 1024 * 1024 # 1MB 圖片大小限制 MAX_IMAGE_SIZE = 1 * 1024 * 1024 # 1MB 圖片大小限制
SUPPORTED_IMAGE_TYPES = {'image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp', 'image/webp'} SUPPORTED_IMAGE_TYPES = {'image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp', 'image/webp'}
@ -517,17 +519,4 @@ if __name__ == "__main__":
except KeyboardInterrupt: except KeyboardInterrupt:
debug_log("\n👋 Web UI 已停止") debug_log("\n👋 Web UI 已停止")
asyncio.run(main()) asyncio.run(main())
# === 工具函數 ===
def debug_log(message: str) -> None:
"""輸出調試訊息到標準錯誤,避免污染標準輸出"""
# 只在啟用調試模式時才輸出,避免干擾 MCP 通信
if not os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on"):
return
try:
print(f"[WEB_UI] {message}", file=sys.stderr, flush=True)
except Exception:
# 靜默失敗,不影響主程序
pass

1070
uv.lock generated

File diff suppressed because it is too large Load Diff