mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 02:22:26 +08:00
✨ 新增 Tauri 桌面應用
This commit is contained in:
parent
f2ad48c3a5
commit
e961a2c1c8
152
.github/workflows/build-desktop.yml
vendored
Normal file
152
.github/workflows/build-desktop.yml
vendored
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
name: Build Desktop Applications
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch: # 手動觸發
|
||||||
|
inputs:
|
||||||
|
platforms:
|
||||||
|
description: '選擇要構建的平台'
|
||||||
|
required: true
|
||||||
|
default: 'all'
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- all
|
||||||
|
- windows
|
||||||
|
- macos
|
||||||
|
- linux
|
||||||
|
upload_artifacts:
|
||||||
|
description: '是否上傳構建產物'
|
||||||
|
required: true
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'src-tauri/**' # 桌面應用代碼變更時自動觸發
|
||||||
|
- 'scripts/build_desktop.py'
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'src-tauri/**'
|
||||||
|
- 'scripts/build_desktop.py'
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# 多平台桌面應用構建
|
||||||
|
build-desktop:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false # 允許部分平台失敗
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
binary: mcp-feedback-enhanced-desktop.exe
|
||||||
|
name: windows
|
||||||
|
enabled: ${{ github.event.inputs.platforms == 'all' || github.event.inputs.platforms == 'windows' || github.event.inputs.platforms == '' }}
|
||||||
|
- os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
binary: mcp-feedback-enhanced-desktop
|
||||||
|
name: macos-intel
|
||||||
|
enabled: ${{ github.event.inputs.platforms == 'all' || github.event.inputs.platforms == 'macos' || github.event.inputs.platforms == '' }}
|
||||||
|
- os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
binary: mcp-feedback-enhanced-desktop
|
||||||
|
name: macos-arm64
|
||||||
|
enabled: ${{ github.event.inputs.platforms == 'all' || github.event.inputs.platforms == 'macos' || github.event.inputs.platforms == '' }}
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
binary: mcp-feedback-enhanced-desktop
|
||||||
|
name: linux
|
||||||
|
enabled: ${{ github.event.inputs.platforms == 'all' || github.event.inputs.platforms == 'linux' || github.event.inputs.platforms == '' }}
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
if: ${{ matrix.enabled != 'false' }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v4
|
||||||
|
with:
|
||||||
|
version: "latest"
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
run: uv python install
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: uv sync --dev
|
||||||
|
|
||||||
|
- name: Cache Rust dependencies
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cargo/registry
|
||||||
|
~/.cargo/git
|
||||||
|
src-tauri/target
|
||||||
|
key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-cargo-${{ matrix.target }}-
|
||||||
|
${{ runner.os }}-cargo-
|
||||||
|
|
||||||
|
- name: Build desktop application for ${{ matrix.name }}
|
||||||
|
run: |
|
||||||
|
cd src-tauri
|
||||||
|
cargo build --release --target ${{ matrix.target }} --bin mcp-feedback-enhanced-desktop
|
||||||
|
|
||||||
|
- name: Verify build output
|
||||||
|
run: |
|
||||||
|
echo "🔍 檢查構建產物..."
|
||||||
|
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
||||||
|
ls -la "src-tauri/target/${{ matrix.target }}/release/${{ matrix.binary }}" || echo "❌ Windows 二進制文件不存在"
|
||||||
|
else
|
||||||
|
ls -la "src-tauri/target/${{ matrix.target }}/release/${{ matrix.binary }}" || echo "❌ ${{ matrix.name }} 二進制文件不存在"
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Upload desktop binary
|
||||||
|
if: ${{ github.event.inputs.upload_artifacts != 'false' }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: desktop-${{ matrix.name }}
|
||||||
|
path: src-tauri/target/${{ matrix.target }}/release/${{ matrix.binary }}
|
||||||
|
retention-days: 30 # 保留 30 天
|
||||||
|
compression-level: 6
|
||||||
|
|
||||||
|
# 構建摘要
|
||||||
|
build-summary:
|
||||||
|
needs: build-desktop
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: always()
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Generate build summary
|
||||||
|
run: |
|
||||||
|
echo "## 🖥️ 桌面應用構建摘要" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "### 構建結果" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# 檢查各平台構建狀態
|
||||||
|
if [ "${{ needs.build-desktop.result }}" = "success" ]; then
|
||||||
|
echo "✅ 所有平台構建成功" >> $GITHUB_STEP_SUMMARY
|
||||||
|
elif [ "${{ needs.build-desktop.result }}" = "failure" ]; then
|
||||||
|
echo "❌ 部分平台構建失敗" >> $GITHUB_STEP_SUMMARY
|
||||||
|
else
|
||||||
|
echo "⚠️ 構建狀態: ${{ needs.build-desktop.result }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "### 下一步" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- 構建產物已上傳到 GitHub Artifacts" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- 可以在發佈流程中使用這些二進制文件" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "- 如需重新構建,請手動觸發此工作流程" >> $GITHUB_STEP_SUMMARY
|
129
.github/workflows/publish.yml
vendored
129
.github/workflows/publish.yml
vendored
@ -16,6 +16,15 @@ on:
|
|||||||
description: 'Custom version number (e.g., 2.5.0) - overrides version_type if provided'
|
description: 'Custom version number (e.g., 2.5.0) - overrides version_type if provided'
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
|
include_desktop:
|
||||||
|
description: '是否包含桌面應用二進制文件'
|
||||||
|
required: true
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
desktop_build_run_id:
|
||||||
|
description: '桌面應用構建的 Run ID(可選,留空使用最新的成功構建)'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
@ -38,6 +47,9 @@ jobs:
|
|||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
run: uv python install
|
run: uv python install
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
uv sync --dev
|
uv sync --dev
|
||||||
@ -275,6 +287,101 @@ jobs:
|
|||||||
- Auto-generated release from workflow"
|
- Auto-generated release from workflow"
|
||||||
git tag "v${{ steps.bump_version.outputs.new }}"
|
git tag "v${{ steps.bump_version.outputs.new }}"
|
||||||
|
|
||||||
|
- name: Download desktop binaries from latest build
|
||||||
|
if: ${{ github.event.inputs.include_desktop == 'true' }}
|
||||||
|
uses: dawidd6/action-download-artifact@v6
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
workflow: build-desktop.yml
|
||||||
|
run_id: ${{ github.event.inputs.desktop_build_run_id }}
|
||||||
|
path: desktop-artifacts
|
||||||
|
if_no_artifact_found: warn
|
||||||
|
|
||||||
|
- name: Prepare multi-platform desktop binaries
|
||||||
|
if: ${{ github.event.inputs.include_desktop == 'true' }}
|
||||||
|
run: |
|
||||||
|
# 檢查是否有桌面應用構建產物
|
||||||
|
if [ ! -d "desktop-artifacts" ] || [ -z "$(ls -A desktop-artifacts 2>/dev/null)" ]; then
|
||||||
|
echo "⚠️ 警告:沒有找到桌面應用構建產物"
|
||||||
|
echo "💡 提示:請先運行 'Build Desktop Applications' 工作流程"
|
||||||
|
echo "🔧 或者設置 include_desktop 為 false 來跳過桌面應用"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 創建桌面應用目錄
|
||||||
|
mkdir -p src/mcp_feedback_enhanced/desktop_release
|
||||||
|
|
||||||
|
# 複製所有平台的二進制文件並重命名
|
||||||
|
echo "📦 準備多平台桌面應用二進制文件..."
|
||||||
|
|
||||||
|
# 檢查並複製各平台文件
|
||||||
|
if [ -f "desktop-artifacts/desktop-windows/mcp-feedback-enhanced-desktop.exe" ]; then
|
||||||
|
cp desktop-artifacts/desktop-windows/mcp-feedback-enhanced-desktop.exe src/mcp_feedback_enhanced/desktop_release/
|
||||||
|
echo "✅ Windows 二進制文件已複製"
|
||||||
|
else
|
||||||
|
echo "⚠️ Windows 二進制文件不存在"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "desktop-artifacts/desktop-macos-intel/mcp-feedback-enhanced-desktop" ]; then
|
||||||
|
cp desktop-artifacts/desktop-macos-intel/mcp-feedback-enhanced-desktop src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-intel
|
||||||
|
echo "✅ macOS Intel 二進制文件已複製"
|
||||||
|
else
|
||||||
|
echo "⚠️ macOS Intel 二進制文件不存在"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "desktop-artifacts/desktop-macos-arm64/mcp-feedback-enhanced-desktop" ]; then
|
||||||
|
cp desktop-artifacts/desktop-macos-arm64/mcp-feedback-enhanced-desktop src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-arm64
|
||||||
|
echo "✅ macOS ARM64 二進制文件已複製"
|
||||||
|
else
|
||||||
|
echo "⚠️ macOS ARM64 二進制文件不存在"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "desktop-artifacts/desktop-linux/mcp-feedback-enhanced-desktop" ]; then
|
||||||
|
cp desktop-artifacts/desktop-linux/mcp-feedback-enhanced-desktop src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-linux
|
||||||
|
echo "✅ Linux 二進制文件已複製"
|
||||||
|
else
|
||||||
|
echo "⚠️ Linux 二進制文件不存在"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 設置執行權限
|
||||||
|
chmod +x src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-* 2>/dev/null || true
|
||||||
|
|
||||||
|
# 創建 __init__.py
|
||||||
|
echo '"""桌面應用程式二進制檔案"""' > src/mcp_feedback_enhanced/desktop_release/__init__.py
|
||||||
|
|
||||||
|
# 顯示文件列表
|
||||||
|
echo "📦 最終的桌面應用二進制文件:"
|
||||||
|
ls -la src/mcp_feedback_enhanced/desktop_release/
|
||||||
|
|
||||||
|
- name: Build desktop applications locally (fallback)
|
||||||
|
if: ${{ github.event.inputs.include_desktop == 'true' }}
|
||||||
|
run: |
|
||||||
|
# 如果沒有預構建的桌面應用,嘗試本地構建
|
||||||
|
if [ ! -d "src/mcp_feedback_enhanced/desktop_release" ] || [ -z "$(ls -A src/mcp_feedback_enhanced/desktop_release 2>/dev/null)" ]; then
|
||||||
|
echo "🔧 沒有找到預構建的桌面應用,嘗試本地構建..."
|
||||||
|
echo "⚠️ 注意:本地構建可能只支援當前平台"
|
||||||
|
|
||||||
|
# 安裝 Rust(如果還沒安裝)
|
||||||
|
if ! command -v cargo &> /dev/null; then
|
||||||
|
echo "📦 安裝 Rust..."
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
source ~/.cargo/env
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 運行本地構建腳本
|
||||||
|
python scripts/build_desktop.py --release
|
||||||
|
|
||||||
|
echo "✅ 本地桌面應用構建完成"
|
||||||
|
else
|
||||||
|
echo "✅ 使用預構建的桌面應用"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Skip desktop applications
|
||||||
|
if: ${{ github.event.inputs.include_desktop != 'true' }}
|
||||||
|
run: |
|
||||||
|
echo "⏭️ 跳過桌面應用,僅發佈 Web 版本"
|
||||||
|
echo "💡 用戶將只能使用 Web 模式,無法使用桌面模式"
|
||||||
|
|
||||||
- name: Build package
|
- name: Build package
|
||||||
run: uv build
|
run: uv build
|
||||||
|
|
||||||
@ -311,10 +418,32 @@ jobs:
|
|||||||
echo "📦 Published to PyPI: https://pypi.org/project/mcp-feedback-enhanced/"
|
echo "📦 Published to PyPI: https://pypi.org/project/mcp-feedback-enhanced/"
|
||||||
echo "🚀 GitHub Release: https://github.com/Minidoracat/mcp-feedback-enhanced/releases/tag/v${{ steps.bump_version.outputs.new }}"
|
echo "🚀 GitHub Release: https://github.com/Minidoracat/mcp-feedback-enhanced/releases/tag/v${{ steps.bump_version.outputs.new }}"
|
||||||
echo "📝 Release notes generated from CHANGELOG files"
|
echo "📝 Release notes generated from CHANGELOG files"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 顯示桌面應用狀態
|
||||||
|
if [ "${{ github.event.inputs.include_desktop }}" = "true" ]; then
|
||||||
|
echo "🖥️ 桌面應用狀態:"
|
||||||
|
if [ -d "src/mcp_feedback_enhanced/desktop_release" ] && [ -n "$(ls -A src/mcp_feedback_enhanced/desktop_release 2>/dev/null)" ]; then
|
||||||
|
echo " ✅ 桌面應用已包含在發佈中"
|
||||||
|
echo " 📱 支援的平台:"
|
||||||
|
[ -f "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop.exe" ] && echo " - Windows x64"
|
||||||
|
[ -f "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-intel" ] && echo " - macOS Intel"
|
||||||
|
[ -f "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-macos-arm64" ] && echo " - macOS Apple Silicon"
|
||||||
|
[ -f "src/mcp_feedback_enhanced/desktop_release/mcp-feedback-enhanced-desktop-linux" ] && echo " - Linux x64"
|
||||||
|
else
|
||||||
|
echo " ⚠️ 桌面應用未包含(可能構建失敗)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "🖥️ 桌面應用狀態:⏭️ 已跳過(僅 Web 版本)"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "✅ Next steps:"
|
echo "✅ Next steps:"
|
||||||
echo " - Check the release on GitHub"
|
echo " - Check the release on GitHub"
|
||||||
echo " - Verify the package on PyPI"
|
echo " - Verify the package on PyPI"
|
||||||
echo " - Test installation with: uvx mcp-feedback-enhanced@v${{ steps.bump_version.outputs.new }}"
|
echo " - Test installation with: uvx mcp-feedback-enhanced@v${{ steps.bump_version.outputs.new }}"
|
||||||
|
if [ "${{ github.event.inputs.include_desktop }}" = "true" ]; then
|
||||||
|
echo " - Test desktop mode with: uvx mcp-feedback-enhanced@v${{ steps.bump_version.outputs.new }} test --desktop"
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
echo "📋 Note: Make sure CHANGELOG files are updated for future releases"
|
echo "📋 Note: Make sure CHANGELOG files are updated for future releases"
|
||||||
|
26
.gitignore
vendored
26
.gitignore
vendored
@ -63,3 +63,29 @@ ui_settings.json
|
|||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
|
||||||
|
# Rust/Tauri build artifacts
|
||||||
|
src-tauri/target/
|
||||||
|
src-tauri/Cargo.lock
|
||||||
|
src-tauri/WixTools/
|
||||||
|
src-tauri/gen/
|
||||||
|
|
||||||
|
# Desktop application binaries (these should be built and copied by build script)
|
||||||
|
src/mcp_feedback_enhanced/desktop_release/*
|
||||||
|
src/mcp_feedback_enhanced/desktop_app/
|
||||||
|
src/mcp_feedback_enhanced/desktop/mcp-feedback-enhanced-desktop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Tauri bundle outputs
|
||||||
|
src-tauri/target/bundle/
|
||||||
|
src-tauri/target/release/bundle/
|
||||||
|
|
||||||
|
# Node.js (if using Tauri with frontend framework)
|
||||||
|
node_modules/
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# Temporary build files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
41
Makefile
41
Makefile
@ -3,7 +3,7 @@
|
|||||||
# Compatible with Windows PowerShell and Unix systems
|
# Compatible with Windows PowerShell and Unix systems
|
||||||
# 兼容 Windows PowerShell 和 Unix 系統
|
# 兼容 Windows PowerShell 和 Unix 系統
|
||||||
|
|
||||||
.PHONY: help install install-dev install-hooks lint format type-check test clean pre-commit-run pre-commit-all update-deps
|
.PHONY: help install install-dev install-hooks lint format type-check test clean pre-commit-run pre-commit-all update-deps check-rust build-desktop build-desktop-release test-desktop clean-desktop build-all test-all
|
||||||
|
|
||||||
# 預設目標 - 顯示幫助訊息
|
# 預設目標 - 顯示幫助訊息
|
||||||
help: ## Show this help message
|
help: ## Show this help message
|
||||||
@ -36,6 +36,13 @@ help: ## Show this help message
|
|||||||
@echo " bump-major Bump major version"
|
@echo " bump-major Bump major version"
|
||||||
@echo " ci Simulate CI pipeline locally"
|
@echo " ci Simulate CI pipeline locally"
|
||||||
@echo " quick-check Quick check with auto-fix"
|
@echo " quick-check Quick check with auto-fix"
|
||||||
|
@echo ""
|
||||||
|
@echo "Desktop Application Commands:"
|
||||||
|
@echo " build-desktop Build desktop application (debug)"
|
||||||
|
@echo " build-desktop-release Build desktop application (release)"
|
||||||
|
@echo " test-desktop Test desktop application"
|
||||||
|
@echo " clean-desktop Clean desktop build artifacts"
|
||||||
|
@echo " check-rust Check Rust development environment"
|
||||||
|
|
||||||
# 安裝相關命令
|
# 安裝相關命令
|
||||||
install: ## Install the package
|
install: ## Install the package
|
||||||
@ -142,3 +149,35 @@ quick-check: lint-fix format type-check ## Quick check with auto-fix (recommende
|
|||||||
# Windows PowerShell 專用命令
|
# Windows PowerShell 專用命令
|
||||||
ps-clean: ## PowerShell version of clean (Windows)
|
ps-clean: ## PowerShell version of clean (Windows)
|
||||||
powershell -Command "Get-ChildItem -Path . -Recurse -Name '__pycache__' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue; Get-ChildItem -Path . -Recurse -Name '*.pyc' | Remove-Item -Force -ErrorAction SilentlyContinue; @('.mypy_cache', '.ruff_cache', '.pytest_cache', 'htmlcov', 'dist', 'build') | ForEach-Object { if (Test-Path $$_) { Remove-Item $$_ -Recurse -Force } }"
|
powershell -Command "Get-ChildItem -Path . -Recurse -Name '__pycache__' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue; Get-ChildItem -Path . -Recurse -Name '*.pyc' | Remove-Item -Force -ErrorAction SilentlyContinue; @('.mypy_cache', '.ruff_cache', '.pytest_cache', 'htmlcov', 'dist', 'build') | ForEach-Object { if (Test-Path $$_) { Remove-Item $$_ -Recurse -Force } }"
|
||||||
|
|
||||||
|
# 桌面應用程式相關命令
|
||||||
|
check-rust: ## Check Rust development environment
|
||||||
|
@echo "🔍 Checking Rust environment..."
|
||||||
|
@rustc --version || (echo "❌ Rust not installed. Please visit https://rustup.rs/" && exit 1)
|
||||||
|
@cargo --version || (echo "❌ Cargo not installed" && exit 1)
|
||||||
|
@cargo install --list | grep tauri-cli || (echo "⚠️ Tauri CLI not installed, installing..." && cargo install tauri-cli)
|
||||||
|
@echo "✅ Rust environment check completed"
|
||||||
|
|
||||||
|
build-desktop: ## Build desktop application (debug mode)
|
||||||
|
@echo "🔨 Building desktop application (debug)..."
|
||||||
|
uv run python scripts/build_desktop.py
|
||||||
|
|
||||||
|
build-desktop-release: ## Build desktop application (release mode)
|
||||||
|
@echo "🚀 Building desktop application (release)..."
|
||||||
|
uv run python scripts/build_desktop.py --release
|
||||||
|
|
||||||
|
test-desktop: build-desktop ## Test desktop application
|
||||||
|
@echo "🖥️ Testing desktop application..."
|
||||||
|
uv run python -m mcp_feedback_enhanced test --desktop
|
||||||
|
|
||||||
|
clean-desktop: ## Clean desktop build artifacts
|
||||||
|
@echo "🧹 Cleaning desktop build artifacts..."
|
||||||
|
uv run python scripts/build_desktop.py --clean
|
||||||
|
|
||||||
|
# 完整構建流程(包含桌面應用程式)
|
||||||
|
build-all: clean build-desktop-release build ## Build complete package with desktop app
|
||||||
|
@echo "🎉 Complete build finished!"
|
||||||
|
|
||||||
|
# 測試所有功能
|
||||||
|
test-all: test test-desktop ## Run all tests including desktop
|
||||||
|
@echo "✅ All tests completed!"
|
||||||
|
244
docs/DESKTOP_BUILD.md
Normal file
244
docs/DESKTOP_BUILD.md
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
# 桌面應用程式構建指南
|
||||||
|
|
||||||
|
本文檔說明如何構建 MCP Feedback Enhanced 的桌面應用程式。
|
||||||
|
|
||||||
|
## 先決條件
|
||||||
|
|
||||||
|
### 必需工具
|
||||||
|
|
||||||
|
1. **Python 3.8+**
|
||||||
|
```bash
|
||||||
|
python --version
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Rust 工具鏈**
|
||||||
|
```bash
|
||||||
|
# 安裝 Rust (如果尚未安裝)
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||||
|
|
||||||
|
# 驗證安裝
|
||||||
|
rustc --version
|
||||||
|
cargo --version
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Tauri CLI** (自動安裝)
|
||||||
|
```bash
|
||||||
|
cargo install tauri-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
### 開發依賴
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安裝 Python 開發依賴
|
||||||
|
uv sync --dev
|
||||||
|
|
||||||
|
# 或使用 pip
|
||||||
|
pip install -e ".[dev]"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 構建方法
|
||||||
|
|
||||||
|
### 方法 1: 使用 Makefile (推薦)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 構建 Debug 版本
|
||||||
|
make build-desktop
|
||||||
|
|
||||||
|
# 構建 Release 版本
|
||||||
|
make build-desktop-release
|
||||||
|
|
||||||
|
# 構建並測試
|
||||||
|
make test-desktop
|
||||||
|
|
||||||
|
# 清理構建產物
|
||||||
|
make clean-desktop
|
||||||
|
|
||||||
|
# 完整構建流程 (包含 PyPI 包)
|
||||||
|
make build-all
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方法 2: 直接使用 Python 腳本
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 構建 Debug 版本
|
||||||
|
python scripts/build_desktop.py
|
||||||
|
|
||||||
|
# 構建 Release 版本
|
||||||
|
python scripts/build_desktop.py --release
|
||||||
|
|
||||||
|
# 清理構建產物
|
||||||
|
python scripts/build_desktop.py --clean
|
||||||
|
|
||||||
|
# 查看幫助
|
||||||
|
python scripts/build_desktop.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
## 構建產物
|
||||||
|
|
||||||
|
構建完成後,產物將位於:
|
||||||
|
|
||||||
|
```
|
||||||
|
src/mcp_feedback_enhanced/
|
||||||
|
├── desktop_release/ # 發佈用二進制文件
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── mcp-feedback-enhanced-desktop.exe # Windows
|
||||||
|
│ ├── mcp-feedback-enhanced-desktop-macos-intel # macOS Intel
|
||||||
|
│ ├── mcp-feedback-enhanced-desktop-macos-arm64 # macOS Apple Silicon
|
||||||
|
│ └── mcp-feedback-enhanced-desktop-linux # Linux
|
||||||
|
├── desktop_app/ # 發佈用 Python 模組
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ └── desktop_app.py
|
||||||
|
└── ...
|
||||||
|
|
||||||
|
src-tauri/python/mcp_feedback_enhanced_desktop/ # 開發環境模組
|
||||||
|
├── __init__.py
|
||||||
|
├── desktop_app.py
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 多平台支援
|
||||||
|
|
||||||
|
構建腳本會自動構建以下平台的二進制文件:
|
||||||
|
|
||||||
|
- **Windows**: `mcp-feedback-enhanced-desktop.exe`
|
||||||
|
- **macOS Intel**: `mcp-feedback-enhanced-desktop-macos-intel`
|
||||||
|
- **macOS Apple Silicon**: `mcp-feedback-enhanced-desktop-macos-arm64`
|
||||||
|
- **Linux**: `mcp-feedback-enhanced-desktop-linux`
|
||||||
|
|
||||||
|
桌面應用會根據運行平台自動選擇對應的二進制文件。
|
||||||
|
|
||||||
|
## 測試桌面應用程式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 方法 1: 直接測試
|
||||||
|
python -m mcp_feedback_enhanced test --desktop
|
||||||
|
|
||||||
|
# 方法 2: 使用 Makefile
|
||||||
|
make test-desktop
|
||||||
|
```
|
||||||
|
|
||||||
|
## 跨平台注意事項
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
- 桌面應用程式不會顯示額外的 CMD 視窗
|
||||||
|
- 二進制檔案: `mcp-feedback-enhanced-desktop.exe`
|
||||||
|
|
||||||
|
### Linux/macOS
|
||||||
|
- 二進制檔案: `mcp-feedback-enhanced-desktop`
|
||||||
|
- 自動設置執行權限
|
||||||
|
|
||||||
|
## 故障排除
|
||||||
|
|
||||||
|
### 常見問題
|
||||||
|
|
||||||
|
1. **Rust 未安裝**
|
||||||
|
```
|
||||||
|
❌ Rust 未安裝,請訪問 https://rustup.rs/
|
||||||
|
```
|
||||||
|
解決方案: 安裝 Rust 工具鏈
|
||||||
|
|
||||||
|
2. **Tauri CLI 未安裝**
|
||||||
|
```
|
||||||
|
⚠️ Tauri CLI 未安裝,正在安裝...
|
||||||
|
```
|
||||||
|
解決方案: 腳本會自動安裝,或手動執行 `cargo install tauri-cli`
|
||||||
|
|
||||||
|
3. **構建失敗**
|
||||||
|
```
|
||||||
|
❌ 構建失敗
|
||||||
|
```
|
||||||
|
解決方案: 檢查 Rust 環境,清理後重新構建
|
||||||
|
```bash
|
||||||
|
make clean-desktop
|
||||||
|
make build-desktop
|
||||||
|
```
|
||||||
|
|
||||||
|
### 檢查環境
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 檢查 Rust 環境
|
||||||
|
make check-rust
|
||||||
|
|
||||||
|
# 檢查所有依賴
|
||||||
|
make dev-setup
|
||||||
|
```
|
||||||
|
|
||||||
|
### 跨平台編譯要求
|
||||||
|
|
||||||
|
構建腳本會自動安裝以下 Rust targets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 這些 targets 會自動安裝,無需手動執行
|
||||||
|
rustup target add x86_64-pc-windows-msvc # Windows
|
||||||
|
rustup target add x86_64-apple-darwin # macOS Intel
|
||||||
|
rustup target add aarch64-apple-darwin # macOS Apple Silicon
|
||||||
|
rustup target add x86_64-unknown-linux-gnu # Linux
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**:
|
||||||
|
- 本地構建通常只能成功構建當前平台的二進制文件
|
||||||
|
- 跨平台編譯需要複雜的工具鏈配置(C 編譯器、系統庫等)
|
||||||
|
- **完整的多平台支援在 GitHub Actions CI 中實現**
|
||||||
|
- 發佈到 PyPI 時會自動包含所有平台的二進制文件
|
||||||
|
|
||||||
|
## 自動化構建
|
||||||
|
|
||||||
|
### CI/CD 多平台構建
|
||||||
|
|
||||||
|
GitHub Actions 會在各自的原生平台上構建桌面應用:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# 多平台構建策略
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: windows-latest # Windows 原生構建
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
- os: macos-latest # macOS 原生構建
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
- os: ubuntu-latest # Linux 原生構建
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
```
|
||||||
|
|
||||||
|
這確保了:
|
||||||
|
- ✅ 每個平台在其原生環境中構建
|
||||||
|
- ✅ 避免跨平台編譯的複雜性
|
||||||
|
- ✅ 最終 PyPI 包包含所有平台的二進制文件
|
||||||
|
|
||||||
|
### 本地自動化
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 完整的開發工作流程
|
||||||
|
make dev-setup # 初始化環境
|
||||||
|
make quick-check # 程式碼檢查
|
||||||
|
make build-all # 構建所有組件
|
||||||
|
make test-all # 測試所有功能
|
||||||
|
```
|
||||||
|
|
||||||
|
## 發布流程
|
||||||
|
|
||||||
|
1. **構建發布版本**
|
||||||
|
```bash
|
||||||
|
make build-desktop-release
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **構建 PyPI 包**
|
||||||
|
```bash
|
||||||
|
make build-all
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **驗證包內容**
|
||||||
|
```bash
|
||||||
|
# 檢查桌面應用程式是否包含在包中
|
||||||
|
tar -tf dist/*.tar.gz | grep desktop
|
||||||
|
```
|
||||||
|
|
||||||
|
桌面應用程式現在已完全集成到 PyPI 包中,包含所有主要平台的二進制文件,用戶安裝後可直接使用 `--desktop` 選項啟動。
|
||||||
|
|
||||||
|
### 平台兼容性
|
||||||
|
|
||||||
|
- **Windows**: 支援 x64 架構
|
||||||
|
- **macOS**: 支援 Intel 和 Apple Silicon (M1/M2) 架構
|
||||||
|
- **Linux**: 支援 x64 架構
|
||||||
|
|
||||||
|
桌面應用會自動檢測運行平台並選擇對應的二進制文件。
|
122
docs/WORKFLOWS.md
Normal file
122
docs/WORKFLOWS.md
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
# GitHub Actions 工作流程說明
|
||||||
|
|
||||||
|
本項目使用雙工作流程架構來優化構建和發佈流程。
|
||||||
|
|
||||||
|
## 🏗️ 工作流程架構
|
||||||
|
|
||||||
|
### 1. 桌面應用構建工作流程 (build-desktop.yml)
|
||||||
|
|
||||||
|
**用途**: 專門負責構建多平台桌面應用二進制文件
|
||||||
|
|
||||||
|
**觸發條件**:
|
||||||
|
- 手動觸發 (workflow_dispatch)
|
||||||
|
- 桌面應用代碼變更時自動觸發 (`src-tauri/**`, `scripts/build_desktop.py`)
|
||||||
|
- Pull Request 中的桌面應用變更
|
||||||
|
|
||||||
|
**功能**:
|
||||||
|
- 在各自原生平台上構建桌面應用
|
||||||
|
- 支援選擇性平台構建
|
||||||
|
- 上傳構建產物到 GitHub Artifacts (保留 30 天)
|
||||||
|
- 提供詳細的構建摘要
|
||||||
|
|
||||||
|
**支援平台**:
|
||||||
|
- Windows x64 (`windows-latest`)
|
||||||
|
- macOS Intel (`macos-latest` + `x86_64-apple-darwin`)
|
||||||
|
- macOS Apple Silicon (`macos-latest` + `aarch64-apple-darwin`)
|
||||||
|
- Linux x64 (`ubuntu-latest`)
|
||||||
|
|
||||||
|
### 2. 發佈工作流程 (publish.yml)
|
||||||
|
|
||||||
|
**用途**: 負責版本管理和 PyPI 發佈
|
||||||
|
|
||||||
|
**觸發條件**:
|
||||||
|
- 手動觸發 (workflow_dispatch)
|
||||||
|
|
||||||
|
**功能**:
|
||||||
|
- 自動或手動版本號管理
|
||||||
|
- 可選擇是否包含桌面應用
|
||||||
|
- 從最新的桌面應用構建下載二進制文件
|
||||||
|
- 發佈到 PyPI
|
||||||
|
- 創建 GitHub Release
|
||||||
|
|
||||||
|
## 🚀 使用方式
|
||||||
|
|
||||||
|
### 開發桌面應用時
|
||||||
|
|
||||||
|
1. **修改桌面應用代碼** (`src-tauri/` 目錄)
|
||||||
|
2. **自動觸發構建** - 推送到 main 分支會自動觸發桌面應用構建
|
||||||
|
3. **手動觸發構建** (可選) - 在 GitHub Actions 頁面手動運行 "Build Desktop Applications"
|
||||||
|
|
||||||
|
### 發佈新版本時
|
||||||
|
|
||||||
|
1. **確保桌面應用已構建** - 檢查最新的 "Build Desktop Applications" 工作流程是否成功
|
||||||
|
2. **手動觸發發佈** - 在 GitHub Actions 頁面運行 "Auto Release to PyPI"
|
||||||
|
3. **選擇發佈選項**:
|
||||||
|
- `version_type`: patch/minor/major (或使用 custom_version)
|
||||||
|
- `include_desktop`: 是否包含桌面應用 (預設: true)
|
||||||
|
- `desktop_build_run_id`: 指定特定的構建 ID (可選)
|
||||||
|
|
||||||
|
## 📋 最佳實踐
|
||||||
|
|
||||||
|
### 桌面應用構建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 本地測試桌面應用構建
|
||||||
|
python scripts/build_desktop.py --release
|
||||||
|
|
||||||
|
# 檢查構建產物
|
||||||
|
ls -la src/mcp_feedback_enhanced/desktop_release/
|
||||||
|
ls -la src/mcp_feedback_enhanced/desktop_app/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 發佈流程
|
||||||
|
|
||||||
|
1. **準備發佈**:
|
||||||
|
- 更新 CHANGELOG 文件
|
||||||
|
- 確保桌面應用構建成功
|
||||||
|
- 測試本地功能
|
||||||
|
|
||||||
|
2. **執行發佈**:
|
||||||
|
- 手動觸發 "Auto Release to PyPI" 工作流程
|
||||||
|
- 選擇適當的版本類型
|
||||||
|
- 確認包含桌面應用 (如果需要)
|
||||||
|
|
||||||
|
3. **發佈後驗證**:
|
||||||
|
- 檢查 PyPI 上的新版本
|
||||||
|
- 測試安裝: `uvx mcp-feedback-enhanced@latest`
|
||||||
|
- 測試桌面模式: `uvx mcp-feedback-enhanced@latest test --desktop`
|
||||||
|
|
||||||
|
## 🔧 故障排除
|
||||||
|
|
||||||
|
### 桌面應用構建失敗
|
||||||
|
|
||||||
|
1. **檢查構建日誌** - 查看 GitHub Actions 中的詳細錯誤信息
|
||||||
|
2. **平台特定問題**:
|
||||||
|
- macOS: 可能缺少 Xcode 命令行工具
|
||||||
|
- Linux: 可能缺少系統依賴 (GTK, Cairo 等)
|
||||||
|
- Windows: 通常構建成功
|
||||||
|
|
||||||
|
3. **本地測試** - 在對應平台上運行本地構建腳本
|
||||||
|
|
||||||
|
### 發佈時桌面應用缺失
|
||||||
|
|
||||||
|
1. **檢查構建狀態** - 確保最新的桌面應用構建成功
|
||||||
|
2. **手動指定構建** - 使用 `desktop_build_run_id` 參數指定特定的成功構建
|
||||||
|
3. **跳過桌面應用** - 設置 `include_desktop: false` 僅發佈 Web 版本
|
||||||
|
|
||||||
|
## 📊 工作流程優勢
|
||||||
|
|
||||||
|
### 效率提升
|
||||||
|
- **分離關注點**: 構建和發佈獨立進行
|
||||||
|
- **避免重複構建**: 不是每次發佈都需要重新構建桌面應用
|
||||||
|
- **快速發佈**: 發佈流程更快速,特別是僅修改 Python 代碼時
|
||||||
|
|
||||||
|
### 靈活性
|
||||||
|
- **選擇性構建**: 可以只構建特定平台
|
||||||
|
- **選擇性發佈**: 可以選擇是否包含桌面應用
|
||||||
|
- **版本控制**: 可以使用不同的桌面應用構建版本
|
||||||
|
|
||||||
|
### 可靠性
|
||||||
|
- **原生構建**: 每個平台在其原生環境中構建
|
||||||
|
- **構建緩存**: 利用 GitHub Actions 緩存加速構建
|
||||||
|
- **錯誤隔離**: 桌面應用構建失敗不會影響 Web 版本發佈
|
15
examples/mcp-config-desktop.json
Normal file
15
examples/mcp-config-desktop.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"mcp-feedback-enhanced": {
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["mcp-feedback-enhanced@latest"],
|
||||||
|
"timeout": 600,
|
||||||
|
"env": {
|
||||||
|
"MCP_DESKTOP_MODE": "true",
|
||||||
|
"MCP_WEB_PORT": "8765",
|
||||||
|
"MCP_DEBUG": "false"
|
||||||
|
},
|
||||||
|
"autoApprove": ["interactive_feedback"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
examples/mcp-config-web.json
Normal file
15
examples/mcp-config-web.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"mcp-feedback-enhanced": {
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["mcp-feedback-enhanced@latest"],
|
||||||
|
"timeout": 600,
|
||||||
|
"env": {
|
||||||
|
"MCP_DESKTOP_MODE": "false",
|
||||||
|
"MCP_WEB_PORT": "8765",
|
||||||
|
"MCP_DEBUG": "false"
|
||||||
|
},
|
||||||
|
"autoApprove": ["interactive_feedback"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,12 +45,20 @@ mcp-feedback-enhanced = "mcp_feedback_enhanced.__main__:main"
|
|||||||
interactive-feedback-mcp = "mcp_feedback_enhanced.__main__:main"
|
interactive-feedback-mcp = "mcp_feedback_enhanced.__main__:main"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["hatchling"]
|
requires = [
|
||||||
|
"hatchling",
|
||||||
|
"maturin>=1.8.7",
|
||||||
|
"setuptools-rust>=1.11.1"
|
||||||
|
]
|
||||||
build-backend = "hatchling.build"
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
[tool.hatch.build.targets.wheel]
|
[tool.hatch.build.targets.wheel]
|
||||||
packages = ["src/mcp_feedback_enhanced"]
|
packages = ["src/mcp_feedback_enhanced"]
|
||||||
|
|
||||||
|
# 包含桌面應用程式二進制檔案
|
||||||
|
[tool.hatch.build.targets.wheel.force-include]
|
||||||
|
"src/mcp_feedback_enhanced/desktop_release" = "mcp_feedback_enhanced/desktop_release"
|
||||||
|
|
||||||
[tool.uv]
|
[tool.uv]
|
||||||
dev-dependencies = [
|
dev-dependencies = [
|
||||||
"bump2version>=1.0.1",
|
"bump2version>=1.0.1",
|
||||||
@ -61,6 +69,9 @@ dev-dependencies = [
|
|||||||
"ruff>=0.11.0",
|
"ruff>=0.11.0",
|
||||||
"mypy>=1.16.0",
|
"mypy>=1.16.0",
|
||||||
"pre-commit>=4.0.0",
|
"pre-commit>=4.0.0",
|
||||||
|
"maturin>=1.8.7",
|
||||||
|
"setuptools-rust>=1.11.1",
|
||||||
|
"pillow>=11.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
# ===== Ruff 配置 =====
|
# ===== Ruff 配置 =====
|
||||||
|
437
scripts/build_desktop.py
Normal file
437
scripts/build_desktop.py
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
桌面應用程式構建腳本
|
||||||
|
|
||||||
|
此腳本負責構建 Tauri 桌面應用程式和 Python 擴展模組,
|
||||||
|
確保在 PyPI 發布時包含預編譯的二進制檔案。
|
||||||
|
|
||||||
|
使用方法:
|
||||||
|
python scripts/build_desktop.py [--release] [--clean]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def run_command(
|
||||||
|
cmd: list[str], cwd: str = None, check: bool = True, show_info: bool = True
|
||||||
|
) -> subprocess.CompletedProcess:
|
||||||
|
"""執行命令並返回結果"""
|
||||||
|
if show_info:
|
||||||
|
print(f"🔧 執行命令: {' '.join(cmd)}")
|
||||||
|
if cwd:
|
||||||
|
print(f"📁 工作目錄: {cwd}")
|
||||||
|
|
||||||
|
result = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, check=False)
|
||||||
|
|
||||||
|
# 處理標準輸出
|
||||||
|
if result.stdout and show_info:
|
||||||
|
print("📤 輸出:")
|
||||||
|
print(result.stdout.strip())
|
||||||
|
|
||||||
|
# 智能處理標準錯誤 - 區分信息和真正的錯誤
|
||||||
|
if result.stderr:
|
||||||
|
stderr_lines = result.stderr.strip().split("\n")
|
||||||
|
info_lines = []
|
||||||
|
error_lines = []
|
||||||
|
|
||||||
|
for line in stderr_lines:
|
||||||
|
stripped_line = line.strip()
|
||||||
|
if not stripped_line:
|
||||||
|
continue
|
||||||
|
# 識別信息性消息和正常編譯輸出
|
||||||
|
if (
|
||||||
|
stripped_line.startswith("info:")
|
||||||
|
or "is up to date" in stripped_line
|
||||||
|
or "downloading component" in stripped_line
|
||||||
|
or "installing component" in stripped_line
|
||||||
|
or stripped_line.startswith("Compiling")
|
||||||
|
or stripped_line.startswith("Finished")
|
||||||
|
or stripped_line.startswith("Building")
|
||||||
|
or "target(s) in" in stripped_line
|
||||||
|
):
|
||||||
|
info_lines.append(stripped_line)
|
||||||
|
else:
|
||||||
|
error_lines.append(stripped_line)
|
||||||
|
|
||||||
|
# 顯示信息性消息
|
||||||
|
if info_lines and show_info:
|
||||||
|
print("ℹ️ 信息:")
|
||||||
|
for line in info_lines:
|
||||||
|
print(f" {line}")
|
||||||
|
|
||||||
|
# 顯示真正的錯誤
|
||||||
|
if error_lines:
|
||||||
|
print("❌ 錯誤:")
|
||||||
|
for line in error_lines:
|
||||||
|
print(f" {line}")
|
||||||
|
|
||||||
|
if check and result.returncode != 0:
|
||||||
|
raise subprocess.CalledProcessError(result.returncode, cmd)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def check_rust_environment():
|
||||||
|
"""檢查 Rust 開發環境"""
|
||||||
|
print("🔍 檢查 Rust 開發環境...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = run_command(["rustc", "--version"])
|
||||||
|
print(f"✅ Rust 編譯器: {result.stdout.strip()}")
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
|
print("❌ 未找到 Rust 編譯器")
|
||||||
|
print("💡 請安裝 Rust: https://rustup.rs/")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = run_command(["cargo", "--version"])
|
||||||
|
print(f"✅ Cargo: {result.stdout.strip()}")
|
||||||
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
|
print("❌ 未找到 Cargo")
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = run_command(["cargo", "install", "--list"])
|
||||||
|
if "tauri-cli" in result.stdout:
|
||||||
|
print("✅ Tauri CLI 已安裝")
|
||||||
|
else:
|
||||||
|
print("⚠️ Tauri CLI 未安裝,嘗試安裝...")
|
||||||
|
run_command(["cargo", "install", "tauri-cli"])
|
||||||
|
print("✅ Tauri CLI 安裝完成")
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
print("❌ 無法安裝 Tauri CLI")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def install_rust_targets():
|
||||||
|
"""安裝跨平台編譯所需的 Rust targets"""
|
||||||
|
print("🎯 安裝跨平台編譯 targets...")
|
||||||
|
|
||||||
|
# 定義需要的 targets
|
||||||
|
targets = [
|
||||||
|
("x86_64-pc-windows-msvc", "Windows x64"),
|
||||||
|
("x86_64-apple-darwin", "macOS Intel"),
|
||||||
|
("aarch64-apple-darwin", "macOS Apple Silicon"),
|
||||||
|
("x86_64-unknown-linux-gnu", "Linux x64"),
|
||||||
|
]
|
||||||
|
|
||||||
|
installed_count = 0
|
||||||
|
updated_count = 0
|
||||||
|
|
||||||
|
for target, description in targets:
|
||||||
|
print(f"📦 檢查 target: {target} ({description})")
|
||||||
|
try:
|
||||||
|
result = run_command(
|
||||||
|
["rustup", "target", "add", target], check=False, show_info=False
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
# 檢查是否是新安裝還是已存在
|
||||||
|
if "is up to date" in result.stderr:
|
||||||
|
print(f"✅ {description} - 已是最新版本")
|
||||||
|
updated_count += 1
|
||||||
|
elif "installing component" in result.stderr:
|
||||||
|
print(f"🆕 {description} - 新安裝完成")
|
||||||
|
installed_count += 1
|
||||||
|
else:
|
||||||
|
print(f"✅ {description} - 安裝成功")
|
||||||
|
installed_count += 1
|
||||||
|
else:
|
||||||
|
print(f"⚠️ {description} - 安裝失敗")
|
||||||
|
if result.stderr:
|
||||||
|
print(f" 錯誤: {result.stderr.strip()}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ 安裝 {description} 時發生錯誤: {e}")
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"✅ Rust targets 檢查完成 (新安裝: {installed_count}, 已存在: {updated_count})"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def clean_build_artifacts(project_root: Path):
|
||||||
|
"""清理構建產物"""
|
||||||
|
print("🧹 清理構建產物...")
|
||||||
|
|
||||||
|
# 清理 Rust 構建產物
|
||||||
|
rust_target = project_root / "src-tauri" / "target"
|
||||||
|
if rust_target.exists():
|
||||||
|
print(f"清理 Rust target 目錄: {rust_target}")
|
||||||
|
shutil.rmtree(rust_target)
|
||||||
|
|
||||||
|
# 清理 Python 構建產物
|
||||||
|
python_build_dirs = [
|
||||||
|
project_root / "build",
|
||||||
|
project_root / "dist",
|
||||||
|
project_root / "*.egg-info",
|
||||||
|
]
|
||||||
|
|
||||||
|
for build_dir in python_build_dirs:
|
||||||
|
if build_dir.exists():
|
||||||
|
print(f"清理 Python 構建目錄: {build_dir}")
|
||||||
|
if build_dir.is_dir():
|
||||||
|
shutil.rmtree(build_dir)
|
||||||
|
else:
|
||||||
|
build_dir.unlink()
|
||||||
|
|
||||||
|
|
||||||
|
def build_rust_extension(project_root: Path, release: bool = True):
|
||||||
|
"""構建 Rust 擴展模組"""
|
||||||
|
print("🔨 構建 Rust 擴展模組...")
|
||||||
|
|
||||||
|
src_tauri = project_root / "src-tauri"
|
||||||
|
if not src_tauri.exists():
|
||||||
|
raise FileNotFoundError(f"src-tauri 目錄不存在: {src_tauri}")
|
||||||
|
|
||||||
|
# 構建 Rust 庫
|
||||||
|
build_cmd = ["cargo", "build"]
|
||||||
|
if release:
|
||||||
|
build_cmd.append("--release")
|
||||||
|
|
||||||
|
run_command(build_cmd, cwd=str(src_tauri))
|
||||||
|
print("✅ Rust 擴展模組構建完成")
|
||||||
|
|
||||||
|
|
||||||
|
def build_tauri_app_multiplatform(project_root: Path, release: bool = True):
|
||||||
|
"""構建多平台 Tauri 桌面應用程式"""
|
||||||
|
print("🖥️ 構建多平台 Tauri 桌面應用程式...")
|
||||||
|
|
||||||
|
src_tauri = project_root / "src-tauri"
|
||||||
|
|
||||||
|
# 定義目標平台
|
||||||
|
targets = [
|
||||||
|
("x86_64-pc-windows-msvc", "mcp-feedback-enhanced-desktop.exe"),
|
||||||
|
("x86_64-apple-darwin", "mcp-feedback-enhanced-desktop"),
|
||||||
|
("aarch64-apple-darwin", "mcp-feedback-enhanced-desktop"),
|
||||||
|
("x86_64-unknown-linux-gnu", "mcp-feedback-enhanced-desktop"),
|
||||||
|
]
|
||||||
|
|
||||||
|
successful_builds = []
|
||||||
|
|
||||||
|
# 平台描述映射
|
||||||
|
platform_descriptions = {
|
||||||
|
"x86_64-pc-windows-msvc": "Windows x64",
|
||||||
|
"x86_64-apple-darwin": "macOS Intel",
|
||||||
|
"aarch64-apple-darwin": "macOS Apple Silicon",
|
||||||
|
"x86_64-unknown-linux-gnu": "Linux x64",
|
||||||
|
}
|
||||||
|
|
||||||
|
for target, binary_name in targets:
|
||||||
|
description = platform_descriptions.get(target, target)
|
||||||
|
print(f"🔨 構建 {description} ({target})...")
|
||||||
|
|
||||||
|
# 構建命令
|
||||||
|
build_cmd = [
|
||||||
|
"cargo",
|
||||||
|
"build",
|
||||||
|
"--bin",
|
||||||
|
"mcp-feedback-enhanced-desktop",
|
||||||
|
"--target",
|
||||||
|
target,
|
||||||
|
]
|
||||||
|
if release:
|
||||||
|
build_cmd.append("--release")
|
||||||
|
|
||||||
|
try:
|
||||||
|
run_command(build_cmd, cwd=str(src_tauri), show_info=False)
|
||||||
|
successful_builds.append((target, binary_name))
|
||||||
|
print(f"✅ {description} 構建成功")
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"⚠️ {description} 構建失敗")
|
||||||
|
print("💡 可能缺少該平台的編譯工具鏈或依賴")
|
||||||
|
# 顯示具體錯誤信息
|
||||||
|
if hasattr(e, "stderr") and e.stderr:
|
||||||
|
print(f" 錯誤詳情: {e.stderr.strip()}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ {description} 構建時發生未預期錯誤: {e}")
|
||||||
|
|
||||||
|
if successful_builds:
|
||||||
|
print(f"✅ 成功構建 {len(successful_builds)} 個平台")
|
||||||
|
|
||||||
|
# 如果只構建了當前平台,給出提示
|
||||||
|
if len(successful_builds) == 1:
|
||||||
|
print("")
|
||||||
|
print("💡 注意:只成功構建了當前平台的二進制文件")
|
||||||
|
print(" 其他平台的構建失敗通常是因為缺少跨平台編譯工具鏈")
|
||||||
|
print(" 完整的多平台支援將在 GitHub Actions CI 中完成")
|
||||||
|
print(" 發佈到 PyPI 時會包含所有平台的二進制文件")
|
||||||
|
|
||||||
|
return successful_builds
|
||||||
|
print("❌ 所有平台構建都失敗了")
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def copy_multiplatform_artifacts(
|
||||||
|
project_root: Path, successful_builds: list, release: bool = True
|
||||||
|
):
|
||||||
|
"""複製多平台構建產物到適當位置"""
|
||||||
|
print("📦 複製多平台構建產物...")
|
||||||
|
|
||||||
|
src_tauri = project_root / "src-tauri"
|
||||||
|
build_type = "release" if release else "debug"
|
||||||
|
|
||||||
|
# 創建目標目錄
|
||||||
|
desktop_dir = project_root / "src" / "mcp_feedback_enhanced" / "desktop_release"
|
||||||
|
desktop_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# 定義平台到文件名的映射
|
||||||
|
platform_mapping = {
|
||||||
|
"x86_64-pc-windows-msvc": "mcp-feedback-enhanced-desktop.exe",
|
||||||
|
"x86_64-apple-darwin": "mcp-feedback-enhanced-desktop-macos-intel",
|
||||||
|
"aarch64-apple-darwin": "mcp-feedback-enhanced-desktop-macos-arm64",
|
||||||
|
"x86_64-unknown-linux-gnu": "mcp-feedback-enhanced-desktop-linux",
|
||||||
|
}
|
||||||
|
|
||||||
|
copied_files = []
|
||||||
|
|
||||||
|
for target, original_binary_name in successful_builds:
|
||||||
|
# 源文件路徑
|
||||||
|
src_file = src_tauri / "target" / target / build_type / original_binary_name
|
||||||
|
|
||||||
|
# 目標文件名
|
||||||
|
dst_filename = platform_mapping.get(target, original_binary_name)
|
||||||
|
dst_file = desktop_dir / dst_filename
|
||||||
|
|
||||||
|
if src_file.exists():
|
||||||
|
shutil.copy2(src_file, dst_file)
|
||||||
|
# 設置執行權限(非 Windows)
|
||||||
|
# 0o755 權限是必要的,因為這些是可執行的二進制檔案
|
||||||
|
if not dst_filename.endswith(".exe"):
|
||||||
|
os.chmod(dst_file, 0o755) # noqa: S103
|
||||||
|
copied_files.append(dst_filename)
|
||||||
|
print(f"✅ 複製 {target} 二進制檔案: {src_file} -> {dst_file}")
|
||||||
|
else:
|
||||||
|
print(f"⚠️ 找不到 {target} 的二進制檔案: {src_file}")
|
||||||
|
|
||||||
|
if not copied_files:
|
||||||
|
print("⚠️ 沒有找到可複製的二進制檔案")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 創建 __init__.py 文件,讓 desktop 目錄成為 Python 包
|
||||||
|
desktop_init = desktop_dir / "__init__.py"
|
||||||
|
if not desktop_init.exists():
|
||||||
|
desktop_init.write_text('"""桌面應用程式二進制檔案"""', encoding="utf-8")
|
||||||
|
print(f"✅ 創建 __init__.py: {desktop_init}")
|
||||||
|
|
||||||
|
print(f"✅ 成功複製 {len(copied_files)} 個平台的二進制檔案")
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def copy_desktop_python_module(project_root: Path):
|
||||||
|
"""複製桌面應用 Python 模組到發佈位置"""
|
||||||
|
print("📦 複製桌面應用 Python 模組...")
|
||||||
|
|
||||||
|
# 源路徑和目標路徑
|
||||||
|
python_src = project_root / "src-tauri" / "python" / "mcp_feedback_enhanced_desktop"
|
||||||
|
python_dst = project_root / "src" / "mcp_feedback_enhanced" / "desktop_app"
|
||||||
|
|
||||||
|
if not python_src.exists():
|
||||||
|
print(f"⚠️ 源模組不存在: {python_src}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 如果目標目錄存在,先刪除
|
||||||
|
if python_dst.exists():
|
||||||
|
shutil.rmtree(python_dst)
|
||||||
|
print(f"🗑️ 清理舊的模組目錄: {python_dst}")
|
||||||
|
|
||||||
|
# 複製模組
|
||||||
|
shutil.copytree(python_src, python_dst)
|
||||||
|
print(f"✅ 複製桌面應用模組: {python_src} -> {python_dst}")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函數"""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="構建 MCP Feedback Enhanced 桌面應用程式",
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog="""
|
||||||
|
範例:
|
||||||
|
python scripts/build_desktop.py # 構建 Debug 版本
|
||||||
|
python scripts/build_desktop.py --release # 構建 Release 版本
|
||||||
|
python scripts/build_desktop.py --clean # 清理構建產物
|
||||||
|
|
||||||
|
構建完成後,可以使用以下命令測試:
|
||||||
|
python -m mcp_feedback_enhanced test --desktop
|
||||||
|
|
||||||
|
或使用 Makefile:
|
||||||
|
make build-desktop # 構建 Debug 版本
|
||||||
|
make build-desktop-release # 構建 Release 版本
|
||||||
|
make test-desktop # 構建並測試
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--release", action="store_true", help="構建發布版本 (預設為 Debug)"
|
||||||
|
)
|
||||||
|
parser.add_argument("--clean", action="store_true", help="清理構建產物")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# 獲取專案根目錄
|
||||||
|
project_root = Path(__file__).parent.parent.resolve()
|
||||||
|
print(f"專案根目錄: {project_root}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 清理構建產物(如果需要)
|
||||||
|
if args.clean:
|
||||||
|
clean_build_artifacts(project_root)
|
||||||
|
|
||||||
|
# 檢查 Rust 環境
|
||||||
|
if not check_rust_environment():
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# 安裝跨平台編譯 targets
|
||||||
|
install_rust_targets()
|
||||||
|
|
||||||
|
# 構建 Rust 擴展
|
||||||
|
build_rust_extension(project_root, args.release)
|
||||||
|
|
||||||
|
# 構建多平台 Tauri 應用程式
|
||||||
|
successful_builds = build_tauri_app_multiplatform(project_root, args.release)
|
||||||
|
|
||||||
|
if not successful_builds:
|
||||||
|
print("❌ 沒有成功構建任何平台")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# 複製多平台構建產物
|
||||||
|
if not copy_multiplatform_artifacts(
|
||||||
|
project_root, successful_builds, args.release
|
||||||
|
):
|
||||||
|
print("⚠️ 構建產物複製失敗,但 Rust 編譯成功")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 複製桌面應用 Python 模組
|
||||||
|
if not copy_desktop_python_module(project_root):
|
||||||
|
print("⚠️ 桌面應用模組複製失敗")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("🎉 多平台桌面應用程式構建完成!")
|
||||||
|
print("")
|
||||||
|
print("📍 構建產物位置:")
|
||||||
|
print(" 多平台二進制檔案: src/mcp_feedback_enhanced/desktop_release/")
|
||||||
|
print(" 桌面應用模組: src/mcp_feedback_enhanced/desktop_app/")
|
||||||
|
print(" 開發環境模組: src-tauri/python/mcp_feedback_enhanced_desktop/")
|
||||||
|
print("")
|
||||||
|
print("🌍 支援的平台:")
|
||||||
|
for target, _ in successful_builds:
|
||||||
|
print(f" ✅ {target}")
|
||||||
|
print("")
|
||||||
|
print("🚀 下一步:")
|
||||||
|
print(" 測試桌面應用程式: python -m mcp_feedback_enhanced test --desktop")
|
||||||
|
print(" 或使用 Makefile: make test-desktop")
|
||||||
|
print(" 構建發布包: make build-all")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 構建失敗: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
60
src-tauri/Cargo.toml
Normal file
60
src-tauri/Cargo.toml
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
[package]
|
||||||
|
name = "mcp-feedback-enhanced-desktop"
|
||||||
|
version = "2.4.3"
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.70"
|
||||||
|
description = "Desktop application for MCP Feedback Enhanced using Tauri"
|
||||||
|
authors = ["Minidoracat <minidora0702@gmail.com>"]
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
# 設置 crate 類型為 cdylib,用於 Python 擴展
|
||||||
|
[lib]
|
||||||
|
name = "mcp_feedback_enhanced_desktop_lib"
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
# 二進制目標
|
||||||
|
[[bin]]
|
||||||
|
name = "mcp-feedback-enhanced-desktop"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Tauri 核心依賴
|
||||||
|
tauri = { version = "2.2", features = ["custom-protocol"] }
|
||||||
|
tauri-plugin-shell = "2.2"
|
||||||
|
|
||||||
|
# PyO3 用於 Python 綁定
|
||||||
|
pyo3 = { version = "0.22", features = ["extension-module"] }
|
||||||
|
|
||||||
|
# 序列化支援
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
|
||||||
|
# 異步運行時
|
||||||
|
tokio = { version = "1.0", features = ["full"] }
|
||||||
|
|
||||||
|
# 日誌記錄
|
||||||
|
log = "0.4"
|
||||||
|
env_logger = "0.11"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { version = "2.2", features = [] }
|
||||||
|
|
||||||
|
# 開發配置文件
|
||||||
|
[profile.dev]
|
||||||
|
incremental = true
|
||||||
|
debug = true
|
||||||
|
|
||||||
|
# 發布配置文件
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
lto = true
|
||||||
|
opt-level = "s"
|
||||||
|
panic = "abort"
|
||||||
|
strip = true
|
||||||
|
|
||||||
|
# 專用於打包的配置文件
|
||||||
|
[profile.bundle-dev]
|
||||||
|
inherits = "dev"
|
||||||
|
|
||||||
|
[profile.bundle-release]
|
||||||
|
inherits = "release"
|
3
src-tauri/build.rs
Normal file
3
src-tauri/build.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
tauri_build::build()
|
||||||
|
}
|
102
src-tauri/generate_icons.py
Normal file
102
src-tauri/generate_icons.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
生成基本的應用程式圖標
|
||||||
|
|
||||||
|
這個腳本會生成 Tauri 應用程式所需的基本圖標文件。
|
||||||
|
在實際部署中,應該使用專業的圖標設計。
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
|
|
||||||
|
def create_simple_icon(size, output_path):
|
||||||
|
"""創建簡單的圖標"""
|
||||||
|
# 創建圖像
|
||||||
|
img = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
|
||||||
|
# 繪製簡單的圖標 - 一個帶邊框的圓形
|
||||||
|
margin = size // 8
|
||||||
|
draw.ellipse(
|
||||||
|
[margin, margin, size - margin, size - margin],
|
||||||
|
fill=(52, 152, 219, 255),
|
||||||
|
outline=(41, 128, 185, 255),
|
||||||
|
width=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 在中心繪製 "MCP" 文字
|
||||||
|
try:
|
||||||
|
# 嘗試使用系統字體
|
||||||
|
from PIL import ImageFont
|
||||||
|
|
||||||
|
font_size = size // 4
|
||||||
|
try:
|
||||||
|
font = ImageFont.truetype("arial.ttf", font_size)
|
||||||
|
except:
|
||||||
|
font = ImageFont.load_default()
|
||||||
|
except ImportError:
|
||||||
|
font = None
|
||||||
|
|
||||||
|
text = "MCP"
|
||||||
|
if font:
|
||||||
|
bbox = draw.textbbox((0, 0), text, font=font)
|
||||||
|
text_width = bbox[2] - bbox[0]
|
||||||
|
text_height = bbox[3] - bbox[1]
|
||||||
|
else:
|
||||||
|
text_width = len(text) * (size // 8)
|
||||||
|
text_height = size // 6
|
||||||
|
|
||||||
|
text_x = (size - text_width) // 2
|
||||||
|
text_y = (size - text_height) // 2
|
||||||
|
|
||||||
|
draw.text((text_x, text_y), text, fill=(255, 255, 255, 255), font=font)
|
||||||
|
|
||||||
|
# 保存圖像
|
||||||
|
img.save(output_path)
|
||||||
|
print(f"已生成圖標: {output_path}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函數"""
|
||||||
|
icons_dir = "icons"
|
||||||
|
os.makedirs(icons_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# 生成不同尺寸的 PNG 圖標
|
||||||
|
sizes = [32, 128, 256]
|
||||||
|
for size in sizes:
|
||||||
|
if size == 128:
|
||||||
|
# 生成普通和 2x 版本
|
||||||
|
create_simple_icon(size, f"{icons_dir}/{size}x{size}.png")
|
||||||
|
create_simple_icon(size * 2, f"{icons_dir}/{size}x{size}@2x.png")
|
||||||
|
else:
|
||||||
|
create_simple_icon(size, f"{icons_dir}/{size}x{size}.png")
|
||||||
|
|
||||||
|
# 為 Windows 創建 ICO 文件
|
||||||
|
try:
|
||||||
|
img_256 = Image.open(f"{icons_dir}/256x256.png")
|
||||||
|
img_256.save(
|
||||||
|
f"{icons_dir}/icon.ico",
|
||||||
|
format="ICO",
|
||||||
|
sizes=[(256, 256), (128, 128), (64, 64), (32, 32), (16, 16)],
|
||||||
|
)
|
||||||
|
print(f"已生成 Windows 圖標: {icons_dir}/icon.ico")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"生成 ICO 文件失敗: {e}")
|
||||||
|
|
||||||
|
# 為 macOS 創建 ICNS 文件(需要額外工具)
|
||||||
|
print("注意:macOS ICNS 文件需要使用專門的工具生成")
|
||||||
|
print("可以使用在線工具或 iconutil 命令將 PNG 轉換為 ICNS")
|
||||||
|
|
||||||
|
print("圖標生成完成!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except ImportError:
|
||||||
|
print("錯誤:需要安裝 Pillow 庫")
|
||||||
|
print("請運行:pip install Pillow")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"生成圖標時發生錯誤: {e}")
|
BIN
src-tauri/icons/128x128.png
Normal file
BIN
src-tauri/icons/128x128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
BIN
src-tauri/icons/128x128@2x.png
Normal file
BIN
src-tauri/icons/128x128@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
BIN
src-tauri/icons/256x256.png
Normal file
BIN
src-tauri/icons/256x256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
BIN
src-tauri/icons/32x32.png
Normal file
BIN
src-tauri/icons/32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 550 B |
BIN
src-tauri/icons/icon.icns
Normal file
BIN
src-tauri/icons/icon.icns
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
BIN
src-tauri/icons/icon.ico
Normal file
BIN
src-tauri/icons/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
39
src-tauri/pyproject.toml
Normal file
39
src-tauri/pyproject.toml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
[project]
|
||||||
|
name = "mcp-feedback-enhanced-desktop"
|
||||||
|
version = "2.4.3"
|
||||||
|
description = "Desktop application extension for MCP Feedback Enhanced"
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
dependencies = [
|
||||||
|
"mcp-feedback-enhanced>=2.4.3"
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.entry-points.pytauri]
|
||||||
|
ext_mod = "mcp_feedback_enhanced_desktop.ext_mod"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = [
|
||||||
|
"setuptools>=61",
|
||||||
|
"setuptools-rust>=1.11.1",
|
||||||
|
"maturin>=1.8.7"
|
||||||
|
]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
# Maturin 配置
|
||||||
|
[tool.maturin]
|
||||||
|
# Python 源碼目錄
|
||||||
|
python-source = "python"
|
||||||
|
# 模組名稱
|
||||||
|
module-name = "mcp_feedback_enhanced_desktop.ext_mod"
|
||||||
|
# 必要的功能特性
|
||||||
|
features = ["pyo3/extension-module", "tauri/custom-protocol"]
|
||||||
|
# 使用 Git 作為 sdist 生成器
|
||||||
|
sdist-generator = "git"
|
||||||
|
# 包含前端資源
|
||||||
|
include = [
|
||||||
|
{ path = "../src/mcp_feedback_enhanced/web/static/**/*", format = "sdist" }
|
||||||
|
]
|
||||||
|
|
||||||
|
# 支援 Python 穩定 ABI
|
||||||
|
[tool.maturin.abi3]
|
||||||
|
enabled = true
|
||||||
|
minimum = "3.11"
|
30
src-tauri/python/mcp_feedback_enhanced_desktop/__init__.py
Normal file
30
src-tauri/python/mcp_feedback_enhanced_desktop/__init__.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
MCP Feedback Enhanced Desktop Application
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
基於 Tauri 的桌面應用程式包裝器,為 MCP Feedback Enhanced 提供原生桌面體驗。
|
||||||
|
|
||||||
|
主要功能:
|
||||||
|
- 原生桌面應用程式界面
|
||||||
|
- 整合現有的 Web UI 功能
|
||||||
|
- 跨平台支援(Windows、macOS、Linux)
|
||||||
|
- 無需瀏覽器的獨立運行環境
|
||||||
|
|
||||||
|
作者: Minidoracat
|
||||||
|
版本: 2.4.3
|
||||||
|
"""
|
||||||
|
|
||||||
|
__version__ = "2.4.3"
|
||||||
|
__author__ = "Minidoracat"
|
||||||
|
__email__ = "minidora0702@gmail.com"
|
||||||
|
|
||||||
|
from .desktop_app import DesktopApp, launch_desktop_app
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"DesktopApp",
|
||||||
|
"__author__",
|
||||||
|
"__version__",
|
||||||
|
"launch_desktop_app",
|
||||||
|
]
|
334
src-tauri/python/mcp_feedback_enhanced_desktop/desktop_app.py
Normal file
334
src-tauri/python/mcp_feedback_enhanced_desktop/desktop_app.py
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
桌面應用程式主要模組
|
||||||
|
|
||||||
|
此模組提供桌面應用程式的核心功能,包括:
|
||||||
|
- 桌面模式檢測
|
||||||
|
- Tauri 應用程式啟動
|
||||||
|
- 與現有 Web UI 的整合
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
# 導入現有的 MCP Feedback Enhanced 模組
|
||||||
|
try:
|
||||||
|
from mcp_feedback_enhanced.debug import server_debug_log as debug_log
|
||||||
|
from mcp_feedback_enhanced.web.main import WebUIManager, get_web_ui_manager
|
||||||
|
except ImportError as e:
|
||||||
|
print(f"無法導入 MCP Feedback Enhanced 模組: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
class DesktopApp:
|
||||||
|
"""桌面應用程式管理器"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.web_manager: WebUIManager | None = None
|
||||||
|
self.desktop_mode = False
|
||||||
|
self.app_handle = None
|
||||||
|
|
||||||
|
def set_desktop_mode(self, enabled: bool = True):
|
||||||
|
"""設置桌面模式"""
|
||||||
|
self.desktop_mode = enabled
|
||||||
|
if enabled:
|
||||||
|
# 設置環境變數,防止開啟瀏覽器
|
||||||
|
os.environ["MCP_DESKTOP_MODE"] = "true"
|
||||||
|
debug_log("桌面模式已啟用,將禁止開啟瀏覽器")
|
||||||
|
else:
|
||||||
|
os.environ.pop("MCP_DESKTOP_MODE", None)
|
||||||
|
debug_log("桌面模式已禁用")
|
||||||
|
|
||||||
|
def is_desktop_mode(self) -> bool:
|
||||||
|
"""檢查是否為桌面模式"""
|
||||||
|
return (
|
||||||
|
self.desktop_mode
|
||||||
|
or os.environ.get("MCP_DESKTOP_MODE", "").lower() == "true"
|
||||||
|
)
|
||||||
|
|
||||||
|
async def start_web_backend(self) -> str:
|
||||||
|
"""啟動 Web 後端服務"""
|
||||||
|
debug_log("啟動 Web 後端服務...")
|
||||||
|
|
||||||
|
# 獲取 Web UI 管理器
|
||||||
|
self.web_manager = get_web_ui_manager()
|
||||||
|
|
||||||
|
# 設置桌面模式,禁止自動開啟瀏覽器
|
||||||
|
self.set_desktop_mode(True)
|
||||||
|
|
||||||
|
# 啟動服務器
|
||||||
|
if (
|
||||||
|
self.web_manager.server_thread is None
|
||||||
|
or not self.web_manager.server_thread.is_alive()
|
||||||
|
):
|
||||||
|
self.web_manager.start_server()
|
||||||
|
|
||||||
|
# 等待服務器啟動
|
||||||
|
max_wait = 10 # 最多等待 10 秒
|
||||||
|
wait_count = 0
|
||||||
|
while wait_count < max_wait:
|
||||||
|
if (
|
||||||
|
self.web_manager.server_thread
|
||||||
|
and self.web_manager.server_thread.is_alive()
|
||||||
|
):
|
||||||
|
break
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
wait_count += 0.5
|
||||||
|
|
||||||
|
if not (
|
||||||
|
self.web_manager.server_thread and self.web_manager.server_thread.is_alive()
|
||||||
|
):
|
||||||
|
raise RuntimeError("Web 服務器啟動失敗")
|
||||||
|
|
||||||
|
server_url = self.web_manager.get_server_url()
|
||||||
|
debug_log(f"Web 後端服務已啟動: {server_url}")
|
||||||
|
return server_url
|
||||||
|
|
||||||
|
def create_test_session(self):
|
||||||
|
"""創建測試會話"""
|
||||||
|
if not self.web_manager:
|
||||||
|
raise RuntimeError("Web 管理器未初始化")
|
||||||
|
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
session_id = self.web_manager.create_session(
|
||||||
|
temp_dir, "桌面應用程式測試 - 驗證 Tauri 整合功能"
|
||||||
|
)
|
||||||
|
debug_log(f"測試會話已創建: {session_id}")
|
||||||
|
return session_id
|
||||||
|
|
||||||
|
async def launch_tauri_app(self, server_url: str):
|
||||||
|
"""啟動 Tauri 桌面應用程式"""
|
||||||
|
debug_log("正在啟動 Tauri 桌面視窗...")
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# 找到 Tauri 可執行檔案
|
||||||
|
# 首先嘗試從打包後的位置找(PyPI 安裝後的位置)
|
||||||
|
try:
|
||||||
|
from mcp_feedback_enhanced.desktop_release import __file__ as desktop_init
|
||||||
|
|
||||||
|
desktop_dir = Path(desktop_init).parent
|
||||||
|
|
||||||
|
# 根據平台選擇對應的二進制文件
|
||||||
|
import platform
|
||||||
|
|
||||||
|
system = platform.system().lower()
|
||||||
|
machine = platform.machine().lower()
|
||||||
|
|
||||||
|
# 定義平台到二進制文件的映射
|
||||||
|
if system == "windows":
|
||||||
|
tauri_exe = desktop_dir / "mcp-feedback-enhanced-desktop.exe"
|
||||||
|
elif system == "darwin": # macOS
|
||||||
|
# 檢測 Apple Silicon 或 Intel
|
||||||
|
if machine in ["arm64", "aarch64"]:
|
||||||
|
tauri_exe = (
|
||||||
|
desktop_dir / "mcp-feedback-enhanced-desktop-macos-arm64"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
tauri_exe = (
|
||||||
|
desktop_dir / "mcp-feedback-enhanced-desktop-macos-intel"
|
||||||
|
)
|
||||||
|
elif system == "linux":
|
||||||
|
tauri_exe = desktop_dir / "mcp-feedback-enhanced-desktop-linux"
|
||||||
|
else:
|
||||||
|
# 回退到通用名稱
|
||||||
|
tauri_exe = desktop_dir / "mcp-feedback-enhanced-desktop"
|
||||||
|
|
||||||
|
if tauri_exe.exists():
|
||||||
|
debug_log(f"找到打包後的 Tauri 可執行檔案: {tauri_exe}")
|
||||||
|
else:
|
||||||
|
# 嘗試回退選項
|
||||||
|
fallback_files = [
|
||||||
|
desktop_dir / "mcp-feedback-enhanced-desktop.exe",
|
||||||
|
desktop_dir / "mcp-feedback-enhanced-desktop-macos-intel",
|
||||||
|
desktop_dir / "mcp-feedback-enhanced-desktop-macos-arm64",
|
||||||
|
desktop_dir / "mcp-feedback-enhanced-desktop-linux",
|
||||||
|
desktop_dir / "mcp-feedback-enhanced-desktop",
|
||||||
|
]
|
||||||
|
|
||||||
|
for fallback in fallback_files:
|
||||||
|
if fallback.exists():
|
||||||
|
tauri_exe = fallback
|
||||||
|
debug_log(f"使用回退的可執行檔案: {tauri_exe}")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise FileNotFoundError(
|
||||||
|
f"找不到任何可執行檔案,檢查的路徑: {tauri_exe}"
|
||||||
|
)
|
||||||
|
|
||||||
|
except (ImportError, FileNotFoundError):
|
||||||
|
# 回退到開發環境路徑
|
||||||
|
debug_log("未找到打包後的可執行檔案,嘗試開發環境路徑...")
|
||||||
|
project_root = Path(__file__).parent.parent.parent.parent
|
||||||
|
tauri_exe = (
|
||||||
|
project_root
|
||||||
|
/ "src-tauri"
|
||||||
|
/ "target"
|
||||||
|
/ "debug"
|
||||||
|
/ "mcp-feedback-enhanced-desktop.exe"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not tauri_exe.exists():
|
||||||
|
# 嘗試其他可能的路徑
|
||||||
|
tauri_exe = (
|
||||||
|
project_root
|
||||||
|
/ "src-tauri"
|
||||||
|
/ "target"
|
||||||
|
/ "debug"
|
||||||
|
/ "mcp-feedback-enhanced-desktop"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not tauri_exe.exists():
|
||||||
|
# 嘗試 release 版本
|
||||||
|
tauri_exe = (
|
||||||
|
project_root
|
||||||
|
/ "src-tauri"
|
||||||
|
/ "target"
|
||||||
|
/ "release"
|
||||||
|
/ "mcp-feedback-enhanced-desktop.exe"
|
||||||
|
)
|
||||||
|
if not tauri_exe.exists():
|
||||||
|
tauri_exe = (
|
||||||
|
project_root
|
||||||
|
/ "src-tauri"
|
||||||
|
/ "target"
|
||||||
|
/ "release"
|
||||||
|
/ "mcp-feedback-enhanced-desktop"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not tauri_exe.exists():
|
||||||
|
raise FileNotFoundError(
|
||||||
|
"找不到 Tauri 可執行檔案,已嘗試的路徑包括開發和發布目錄"
|
||||||
|
) from None
|
||||||
|
|
||||||
|
debug_log(f"找到 Tauri 可執行檔案: {tauri_exe}")
|
||||||
|
|
||||||
|
# 設置環境變數
|
||||||
|
env = os.environ.copy()
|
||||||
|
env["MCP_DESKTOP_MODE"] = "true"
|
||||||
|
env["MCP_WEB_URL"] = server_url
|
||||||
|
|
||||||
|
# 啟動 Tauri 應用程式
|
||||||
|
try:
|
||||||
|
# Windows 下隱藏控制台視窗
|
||||||
|
creation_flags = 0
|
||||||
|
if os.name == "nt":
|
||||||
|
creation_flags = subprocess.CREATE_NO_WINDOW
|
||||||
|
|
||||||
|
self.app_handle = subprocess.Popen(
|
||||||
|
[str(tauri_exe)],
|
||||||
|
env=env,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
creationflags=creation_flags,
|
||||||
|
)
|
||||||
|
debug_log("Tauri 桌面應用程式已啟動")
|
||||||
|
|
||||||
|
# 等待一下確保應用程式啟動
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
debug_log(f"啟動 Tauri 應用程式失敗: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""停止桌面應用程式"""
|
||||||
|
debug_log("正在停止桌面應用程式...")
|
||||||
|
|
||||||
|
# 停止 Tauri 應用程式
|
||||||
|
if self.app_handle:
|
||||||
|
try:
|
||||||
|
self.app_handle.terminate()
|
||||||
|
self.app_handle.wait(timeout=5)
|
||||||
|
debug_log("Tauri 應用程式已停止")
|
||||||
|
except Exception as e:
|
||||||
|
debug_log(f"停止 Tauri 應用程式時發生錯誤: {e}")
|
||||||
|
try:
|
||||||
|
self.app_handle.kill()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
self.app_handle = None
|
||||||
|
|
||||||
|
if self.web_manager:
|
||||||
|
# 注意:不停止 Web 服務器,保持持久性
|
||||||
|
debug_log("Web 服務器保持運行狀態")
|
||||||
|
|
||||||
|
# 注意:不清除桌面模式設置,保持 MCP_DESKTOP_MODE 環境變數
|
||||||
|
# 這樣下次 MCP 調用時仍然會啟動桌面應用程式
|
||||||
|
# self.set_desktop_mode(False) # 註釋掉這行
|
||||||
|
debug_log("桌面應用程式已停止")
|
||||||
|
|
||||||
|
|
||||||
|
async def launch_desktop_app(test_mode: bool = False) -> DesktopApp:
|
||||||
|
"""啟動桌面應用程式
|
||||||
|
|
||||||
|
Args:
|
||||||
|
test_mode: 是否為測試模式,測試模式下會創建測試會話
|
||||||
|
"""
|
||||||
|
debug_log("正在啟動桌面應用程式...")
|
||||||
|
|
||||||
|
app = DesktopApp()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 啟動 Web 後端
|
||||||
|
server_url = await app.start_web_backend()
|
||||||
|
|
||||||
|
if test_mode:
|
||||||
|
# 測試模式:創建測試會話
|
||||||
|
debug_log("測試模式:創建測試會話")
|
||||||
|
app.create_test_session()
|
||||||
|
else:
|
||||||
|
# MCP 調用模式:使用現有會話
|
||||||
|
debug_log("MCP 調用模式:使用現有 MCP 會話,不創建新的測試會話")
|
||||||
|
|
||||||
|
# 啟動 Tauri 桌面應用程式
|
||||||
|
await app.launch_tauri_app(server_url)
|
||||||
|
|
||||||
|
debug_log(f"桌面應用程式已啟動,後端服務: {server_url}")
|
||||||
|
return app
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
debug_log(f"桌面應用程式啟動失敗: {e}")
|
||||||
|
app.stop()
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def run_desktop_app():
|
||||||
|
"""同步方式運行桌面應用程式"""
|
||||||
|
try:
|
||||||
|
# 設置事件循環策略(Windows)
|
||||||
|
if sys.platform == "win32":
|
||||||
|
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
|
||||||
|
|
||||||
|
# 運行應用程式
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
|
||||||
|
app = loop.run_until_complete(launch_desktop_app())
|
||||||
|
|
||||||
|
# 保持應用程式運行
|
||||||
|
debug_log("桌面應用程式正在運行,按 Ctrl+C 停止...")
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
time.sleep(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
debug_log("收到停止信號...")
|
||||||
|
finally:
|
||||||
|
app.stop()
|
||||||
|
loop.close()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"桌面應用程式運行失敗: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run_desktop_app()
|
132
src-tauri/src/lib.rs
Normal file
132
src-tauri/src/lib.rs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
use pyo3::prelude::*;
|
||||||
|
use tauri::{Builder, Context, Manager};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
// 全局狀態管理
|
||||||
|
static APP_STATE: Mutex<Option<tauri::AppHandle>> = Mutex::new(None);
|
||||||
|
|
||||||
|
/// Tauri 應用程式狀態
|
||||||
|
#[derive(Default)]
|
||||||
|
struct AppState {
|
||||||
|
web_url: String,
|
||||||
|
desktop_mode: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 生成 Tauri 上下文
|
||||||
|
pub fn tauri_generate_context() -> Context {
|
||||||
|
tauri::generate_context!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 創建 Tauri 應用程式構建器
|
||||||
|
pub fn create_tauri_builder() -> Builder<tauri::Wry> {
|
||||||
|
Builder::default()
|
||||||
|
.plugin(tauri_plugin_shell::init())
|
||||||
|
.manage(AppState::default())
|
||||||
|
.setup(|app| {
|
||||||
|
// 儲存應用程式句柄到全局狀態
|
||||||
|
{
|
||||||
|
let mut state = APP_STATE.lock().unwrap();
|
||||||
|
*state = Some(app.handle().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 設置應用程式狀態
|
||||||
|
let _app_state = app.state::<AppState>();
|
||||||
|
{
|
||||||
|
// 這裡可以設置初始狀態
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Tauri 應用程式已初始化");
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
get_web_url,
|
||||||
|
set_web_url,
|
||||||
|
is_desktop_mode,
|
||||||
|
set_desktop_mode
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 獲取 Web URL
|
||||||
|
#[tauri::command]
|
||||||
|
fn get_web_url(state: tauri::State<AppState>) -> String {
|
||||||
|
state.web_url.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 設置 Web URL
|
||||||
|
#[tauri::command]
|
||||||
|
fn set_web_url(url: String, _state: tauri::State<AppState>) {
|
||||||
|
// 注意:這裡需要使用內部可變性,但 tauri::State 不支援
|
||||||
|
// 實際實現中可能需要使用 Mutex 或其他同步原語
|
||||||
|
println!("設置 Web URL: {}", url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 檢查是否為桌面模式
|
||||||
|
#[tauri::command]
|
||||||
|
fn is_desktop_mode(state: tauri::State<AppState>) -> bool {
|
||||||
|
state.desktop_mode
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 設置桌面模式
|
||||||
|
#[tauri::command]
|
||||||
|
fn set_desktop_mode(enabled: bool, _state: tauri::State<AppState>) {
|
||||||
|
println!("設置桌面模式: {}", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PyO3 模組定義
|
||||||
|
#[pymodule]
|
||||||
|
#[pyo3(name = "ext_mod")]
|
||||||
|
pub mod ext_mod {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[pymodule_init]
|
||||||
|
fn init(module: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||||
|
// 註冊 context_factory 函數
|
||||||
|
module.add_function(wrap_pyfunction!(context_factory, module)?)?;
|
||||||
|
|
||||||
|
// 註冊 builder_factory 函數
|
||||||
|
module.add_function(wrap_pyfunction!(builder_factory, module)?)?;
|
||||||
|
|
||||||
|
// 註冊 run_app 函數
|
||||||
|
module.add_function(wrap_pyfunction!(run_app, module)?)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 創建 Tauri 上下文的工廠函數
|
||||||
|
#[pyfunction]
|
||||||
|
fn context_factory() -> PyResult<String> {
|
||||||
|
// 返回序列化的上下文信息
|
||||||
|
// 實際實現中,這裡應該返回可以被 Python 使用的上下文
|
||||||
|
Ok("tauri_context".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 創建 Tauri 構建器的工廠函數
|
||||||
|
#[pyfunction]
|
||||||
|
fn builder_factory() -> PyResult<String> {
|
||||||
|
// 返回序列化的構建器信息
|
||||||
|
// 實際實現中,這裡應該返回可以被 Python 使用的構建器
|
||||||
|
Ok("tauri_builder".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 運行 Tauri 應用程式
|
||||||
|
#[pyfunction]
|
||||||
|
fn run_app(web_url: String) -> PyResult<i32> {
|
||||||
|
println!("正在啟動 Tauri 應用程式,Web URL: {}", web_url);
|
||||||
|
|
||||||
|
// 創建並運行 Tauri 應用程式
|
||||||
|
let _builder = create_tauri_builder();
|
||||||
|
let _context = tauri_generate_context();
|
||||||
|
|
||||||
|
// 在實際實現中,這裡需要處理異步運行
|
||||||
|
// 目前返回成功狀態
|
||||||
|
match std::thread::spawn(move || {
|
||||||
|
// 這裡應該運行 Tauri 應用程式
|
||||||
|
// builder.run(context)
|
||||||
|
println!("Tauri 應用程式線程已啟動");
|
||||||
|
0
|
||||||
|
}).join() {
|
||||||
|
Ok(code) => Ok(code),
|
||||||
|
Err(_) => Ok(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
src-tauri/src/main.rs
Normal file
79
src-tauri/src/main.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// Prevents additional console window on Windows in both debug and release, DO NOT REMOVE!!
|
||||||
|
#![cfg_attr(target_os = "windows", windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
use tauri::{Builder, Manager};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
// 全局狀態管理
|
||||||
|
static APP_STATE: Mutex<Option<tauri::AppHandle>> = Mutex::new(None);
|
||||||
|
|
||||||
|
/// Tauri 應用程式狀態
|
||||||
|
#[derive(Default)]
|
||||||
|
struct AppState {
|
||||||
|
web_url: String,
|
||||||
|
desktop_mode: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 獲取 Web URL
|
||||||
|
#[tauri::command]
|
||||||
|
fn get_web_url(state: tauri::State<AppState>) -> String {
|
||||||
|
state.web_url.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 設置 Web URL
|
||||||
|
#[tauri::command]
|
||||||
|
fn set_web_url(url: String, _state: tauri::State<AppState>) {
|
||||||
|
println!("設置 Web URL: {}", url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 檢查是否為桌面模式
|
||||||
|
#[tauri::command]
|
||||||
|
fn is_desktop_mode(state: tauri::State<AppState>) -> bool {
|
||||||
|
state.desktop_mode
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 設置桌面模式
|
||||||
|
#[tauri::command]
|
||||||
|
fn set_desktop_mode(enabled: bool, _state: tauri::State<AppState>) {
|
||||||
|
println!("設置桌面模式: {}", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// 初始化日誌
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
println!("正在啟動 MCP Feedback Enhanced 桌面應用程式...");
|
||||||
|
|
||||||
|
// 創建 Tauri 應用程式
|
||||||
|
Builder::default()
|
||||||
|
.plugin(tauri_plugin_shell::init())
|
||||||
|
.manage(AppState::default())
|
||||||
|
.setup(|app| {
|
||||||
|
// 儲存應用程式句柄到全局狀態
|
||||||
|
{
|
||||||
|
let mut state = APP_STATE.lock().unwrap();
|
||||||
|
*state = Some(app.handle().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查是否有 MCP_WEB_URL 環境變數
|
||||||
|
if let Ok(web_url) = std::env::var("MCP_WEB_URL") {
|
||||||
|
println!("檢測到 Web URL: {}", web_url);
|
||||||
|
|
||||||
|
// 獲取主視窗並導航到 Web URL
|
||||||
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
|
let _ = window.navigate(web_url.parse().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Tauri 應用程式已初始化");
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
get_web_url,
|
||||||
|
set_web_url,
|
||||||
|
is_desktop_mode,
|
||||||
|
set_desktop_mode
|
||||||
|
])
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("運行 Tauri 應用程式時發生錯誤");
|
||||||
|
}
|
70
src-tauri/tauri.conf.json
Normal file
70
src-tauri/tauri.conf.json
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"productName": "MCP Feedback Enhanced",
|
||||||
|
"version": "2.4.3",
|
||||||
|
"identifier": "com.minidoracat.mcp-feedback-enhanced",
|
||||||
|
"build": {
|
||||||
|
"frontendDist": "../src/mcp_feedback_enhanced/web/static",
|
||||||
|
"devUrl": "http://127.0.0.1:8765",
|
||||||
|
"beforeDevCommand": "",
|
||||||
|
"beforeBuildCommand": ""
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"withGlobalTauri": true,
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"title": "MCP Feedback Enhanced",
|
||||||
|
"width": 1200,
|
||||||
|
"height": 800,
|
||||||
|
"minWidth": 800,
|
||||||
|
"minHeight": 600,
|
||||||
|
"resizable": true,
|
||||||
|
"fullscreen": false,
|
||||||
|
"decorations": true,
|
||||||
|
"alwaysOnTop": false,
|
||||||
|
"skipTaskbar": false,
|
||||||
|
"center": true,
|
||||||
|
"url": "index.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"security": {
|
||||||
|
"csp": "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' ws: wss: http: https:; font-src 'self' data:;"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"targets": "all",
|
||||||
|
"icon": [
|
||||||
|
"icons/32x32.png",
|
||||||
|
"icons/128x128.png",
|
||||||
|
"icons/128x128@2x.png",
|
||||||
|
"icons/icon.icns",
|
||||||
|
"icons/icon.ico"
|
||||||
|
],
|
||||||
|
"resources": [],
|
||||||
|
"externalBin": [],
|
||||||
|
"copyright": "Copyright © 2024 Minidoracat",
|
||||||
|
"category": "DeveloperTool",
|
||||||
|
"shortDescription": "Enhanced MCP server for interactive user feedback",
|
||||||
|
"longDescription": "An enhanced MCP server that provides interactive user feedback functionality for AI-assisted development, featuring Web UI with intelligent environment detection.",
|
||||||
|
"windows": {
|
||||||
|
"certificateThumbprint": null,
|
||||||
|
"digestAlgorithm": "sha256",
|
||||||
|
"timestampUrl": ""
|
||||||
|
},
|
||||||
|
"macOS": {
|
||||||
|
"frameworks": [],
|
||||||
|
"minimumSystemVersion": "10.13",
|
||||||
|
"exceptionDomain": ""
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"deb": {
|
||||||
|
"depends": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
|
"shell": {
|
||||||
|
"open": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -47,6 +47,9 @@ def main():
|
|||||||
test_parser.add_argument(
|
test_parser.add_argument(
|
||||||
"--web", action="store_true", help="測試 Web UI (自動持續運行)"
|
"--web", action="store_true", help="測試 Web UI (自動持續運行)"
|
||||||
)
|
)
|
||||||
|
test_parser.add_argument(
|
||||||
|
"--desktop", action="store_true", help="啟動桌面應用程式模式"
|
||||||
|
)
|
||||||
test_parser.add_argument(
|
test_parser.add_argument(
|
||||||
"--timeout", type=int, default=60, help="測試超時時間 (秒)"
|
"--timeout", type=int, default=60, help="測試超時時間 (秒)"
|
||||||
)
|
)
|
||||||
@ -100,10 +103,16 @@ def run_tests(args):
|
|||||||
success = test_web_ui_simple()
|
success = test_web_ui_simple()
|
||||||
if not success:
|
if not success:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
elif args.desktop:
|
||||||
|
print("🖥️ 啟動桌面應用程式...")
|
||||||
|
success = test_desktop_app()
|
||||||
|
if not success:
|
||||||
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
print("❌ 測試功能已簡化")
|
print("❌ 測試功能已簡化")
|
||||||
print("💡 可用的測試選項:")
|
print("💡 可用的測試選項:")
|
||||||
print(" --web 測試 Web UI")
|
print(" --web 測試 Web UI")
|
||||||
|
print(" --desktop 啟動桌面應用程式")
|
||||||
print("💡 對於開發者:使用 'uv run pytest' 執行完整測試")
|
print("💡 對於開發者:使用 'uv run pytest' 執行完整測試")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@ -185,6 +194,117 @@ def test_web_ui_simple():
|
|||||||
os.environ.pop("MCP_WEB_PORT", None)
|
os.environ.pop("MCP_WEB_PORT", None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_desktop_app():
|
||||||
|
"""測試桌面應用程式"""
|
||||||
|
try:
|
||||||
|
print("🔧 檢查桌面應用程式依賴...")
|
||||||
|
|
||||||
|
# 檢查是否有 Tauri 桌面模組
|
||||||
|
try:
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# 嘗試導入桌面應用程式模組
|
||||||
|
def import_desktop_app():
|
||||||
|
# 首先嘗試從發佈包位置導入
|
||||||
|
try:
|
||||||
|
from .desktop_app import launch_desktop_app as desktop_func
|
||||||
|
|
||||||
|
print("✅ 找到發佈包中的桌面應用程式模組")
|
||||||
|
return desktop_func
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 回退到開發環境路徑
|
||||||
|
tauri_python_path = os.path.join(
|
||||||
|
os.path.dirname(__file__), "..", "..", "src-tauri", "python"
|
||||||
|
)
|
||||||
|
if os.path.exists(tauri_python_path):
|
||||||
|
sys.path.insert(0, tauri_python_path)
|
||||||
|
print(f"✅ 找到 Tauri Python 模組路徑: {tauri_python_path}")
|
||||||
|
try:
|
||||||
|
from mcp_feedback_enhanced_desktop import ( # type: ignore
|
||||||
|
launch_desktop_app as dev_func,
|
||||||
|
)
|
||||||
|
|
||||||
|
return dev_func
|
||||||
|
except ImportError:
|
||||||
|
print("❌ 無法從開發環境路徑導入桌面應用程式模組")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
print(f"⚠️ Tauri Python 模組路徑不存在: {tauri_python_path}")
|
||||||
|
print("💡 請確保已正確建立 PyTauri 專案結構")
|
||||||
|
return None
|
||||||
|
|
||||||
|
launch_desktop_app_func = import_desktop_app()
|
||||||
|
if launch_desktop_app_func is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("✅ 桌面應用程式模組導入成功")
|
||||||
|
|
||||||
|
except ImportError as e:
|
||||||
|
print(f"❌ 無法導入桌面應用程式模組: {e}")
|
||||||
|
print(
|
||||||
|
"💡 請確保已執行 'make build-desktop' 或 'python scripts/build_desktop.py'"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("🚀 啟動桌面應用程式...")
|
||||||
|
|
||||||
|
# 設置桌面模式環境變數
|
||||||
|
os.environ["MCP_DESKTOP_MODE"] = "true"
|
||||||
|
|
||||||
|
# 使用 asyncio 啟動桌面應用程式
|
||||||
|
if sys.platform == "win32":
|
||||||
|
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
|
||||||
|
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 使用 WebUIManager 來管理桌面應用實例
|
||||||
|
from .web.main import get_web_ui_manager
|
||||||
|
|
||||||
|
manager = get_web_ui_manager()
|
||||||
|
|
||||||
|
# 啟動桌面應用並保存實例到 manager
|
||||||
|
app = loop.run_until_complete(launch_desktop_app_func(test_mode=True))
|
||||||
|
manager.desktop_app_instance = app
|
||||||
|
|
||||||
|
print("✅ 桌面應用程式啟動成功")
|
||||||
|
print("💡 桌面應用程式正在運行,按 Ctrl+C 停止...")
|
||||||
|
|
||||||
|
# 保持應用程式運行
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
import time
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n🛑 停止桌面應用程式...")
|
||||||
|
app.stop()
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 桌面應用程式啟動失敗: {e}")
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
loop.close()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 桌面應用程式測試失敗: {e}")
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
# 清理環境變數
|
||||||
|
os.environ.pop("MCP_DESKTOP_MODE", None)
|
||||||
|
|
||||||
|
|
||||||
async def wait_for_process(process):
|
async def wait_for_process(process):
|
||||||
"""等待進程結束"""
|
"""等待進程結束"""
|
||||||
try:
|
try:
|
||||||
|
1
src/mcp_feedback_enhanced/desktop_release/__init__.py
Normal file
1
src/mcp_feedback_enhanced/desktop_release/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"桌面應用程式二進制檔案"
|
@ -603,6 +603,14 @@ def main():
|
|||||||
# 檢查是否啟用調試模式
|
# 檢查是否啟用調試模式
|
||||||
debug_enabled = os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on")
|
debug_enabled = os.getenv("MCP_DEBUG", "").lower() in ("true", "1", "yes", "on")
|
||||||
|
|
||||||
|
# 檢查是否啟用桌面模式
|
||||||
|
desktop_mode = os.getenv("MCP_DESKTOP_MODE", "").lower() in (
|
||||||
|
"true",
|
||||||
|
"1",
|
||||||
|
"yes",
|
||||||
|
"on",
|
||||||
|
)
|
||||||
|
|
||||||
if debug_enabled:
|
if debug_enabled:
|
||||||
debug_log("🚀 啟動互動式回饋收集 MCP 服務器")
|
debug_log("🚀 啟動互動式回饋收集 MCP 服務器")
|
||||||
debug_log(f" 服務器名稱: {SERVER_NAME}")
|
debug_log(f" 服務器名稱: {SERVER_NAME}")
|
||||||
@ -611,6 +619,7 @@ def main():
|
|||||||
debug_log(f" 編碼初始化: {'成功' if _encoding_initialized else '失敗'}")
|
debug_log(f" 編碼初始化: {'成功' if _encoding_initialized else '失敗'}")
|
||||||
debug_log(f" 遠端環境: {is_remote_environment()}")
|
debug_log(f" 遠端環境: {is_remote_environment()}")
|
||||||
debug_log(f" WSL 環境: {is_wsl_environment()}")
|
debug_log(f" WSL 環境: {is_wsl_environment()}")
|
||||||
|
debug_log(f" 桌面模式: {'啟用' if desktop_mode else '禁用'}")
|
||||||
debug_log(" 介面類型: Web UI")
|
debug_log(" 介面類型: Web UI")
|
||||||
debug_log(" 等待來自 AI 助手的調用...")
|
debug_log(" 等待來自 AI 助手的調用...")
|
||||||
debug_log("準備啟動 MCP 伺服器...")
|
debug_log("準備啟動 MCP 伺服器...")
|
||||||
|
@ -115,6 +115,7 @@ class WebUIManager:
|
|||||||
|
|
||||||
self.server_thread: threading.Thread | None = None
|
self.server_thread: threading.Thread | None = None
|
||||||
self.server_process = None
|
self.server_process = None
|
||||||
|
self.desktop_app_instance: Any = None # 桌面應用實例引用
|
||||||
|
|
||||||
# 初始化標記,用於追蹤異步初始化狀態
|
# 初始化標記,用於追蹤異步初始化狀態
|
||||||
self._initialization_complete = False
|
self._initialization_complete = False
|
||||||
@ -563,10 +564,15 @@ class WebUIManager:
|
|||||||
"""智能開啟瀏覽器 - 檢測是否已有活躍標籤頁
|
"""智能開啟瀏覽器 - 檢測是否已有活躍標籤頁
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: True 表示檢測到活躍標籤頁,False 表示開啟了新視窗
|
bool: True 表示檢測到活躍標籤頁或桌面模式,False 表示開啟了新視窗
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# 檢查是否為桌面模式
|
||||||
|
if os.environ.get("MCP_DESKTOP_MODE", "").lower() == "true":
|
||||||
|
debug_log("檢測到桌面模式,跳過瀏覽器開啟")
|
||||||
|
return True
|
||||||
|
|
||||||
# 檢查是否有活躍標籤頁
|
# 檢查是否有活躍標籤頁
|
||||||
has_active_tabs = await self._check_active_tabs()
|
has_active_tabs = await self._check_active_tabs()
|
||||||
|
|
||||||
@ -585,6 +591,83 @@ class WebUIManager:
|
|||||||
self.open_browser(url)
|
self.open_browser(url)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
async def launch_desktop_app(self, url: str) -> bool:
|
||||||
|
"""
|
||||||
|
啟動桌面應用程式
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url: Web 服務 URL
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True 表示成功啟動桌面應用程式
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 嘗試導入桌面應用程式模組
|
||||||
|
def import_desktop_app():
|
||||||
|
# 首先嘗試從發佈包位置導入
|
||||||
|
try:
|
||||||
|
from mcp_feedback_enhanced.desktop_app import (
|
||||||
|
launch_desktop_app as desktop_func,
|
||||||
|
)
|
||||||
|
|
||||||
|
debug_log("使用發佈包中的桌面應用程式模組")
|
||||||
|
return desktop_func
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 回退到開發環境路徑
|
||||||
|
import sys
|
||||||
|
|
||||||
|
project_root = os.path.dirname(
|
||||||
|
os.path.dirname(os.path.dirname(__file__))
|
||||||
|
)
|
||||||
|
desktop_module_path = os.path.join(project_root, "src-tauri", "python")
|
||||||
|
if desktop_module_path not in sys.path:
|
||||||
|
sys.path.insert(0, desktop_module_path)
|
||||||
|
try:
|
||||||
|
from mcp_feedback_enhanced_desktop import ( # type: ignore
|
||||||
|
launch_desktop_app as dev_func,
|
||||||
|
)
|
||||||
|
|
||||||
|
debug_log("使用開發環境桌面應用程式模組")
|
||||||
|
return dev_func
|
||||||
|
except ImportError:
|
||||||
|
debug_log("無法從開發環境路徑導入桌面應用程式模組")
|
||||||
|
raise
|
||||||
|
|
||||||
|
launch_desktop_app_func = import_desktop_app()
|
||||||
|
|
||||||
|
# 啟動桌面應用程式
|
||||||
|
desktop_app = await launch_desktop_app_func()
|
||||||
|
# 保存桌面應用實例引用,以便後續控制
|
||||||
|
self.desktop_app_instance = desktop_app
|
||||||
|
debug_log("桌面應用程式啟動成功")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except ImportError as e:
|
||||||
|
debug_log(f"無法導入桌面應用程式模組: {e}")
|
||||||
|
debug_log("回退到瀏覽器模式...")
|
||||||
|
self.open_browser(url)
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
debug_log(f"桌面應用程式啟動失敗: {e}")
|
||||||
|
debug_log("回退到瀏覽器模式...")
|
||||||
|
self.open_browser(url)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def close_desktop_app(self):
|
||||||
|
"""關閉桌面應用程式"""
|
||||||
|
if self.desktop_app_instance:
|
||||||
|
try:
|
||||||
|
debug_log("正在關閉桌面應用程式...")
|
||||||
|
self.desktop_app_instance.stop()
|
||||||
|
self.desktop_app_instance = None
|
||||||
|
debug_log("桌面應用程式已關閉")
|
||||||
|
except Exception as e:
|
||||||
|
debug_log(f"關閉桌面應用程式失敗: {e}")
|
||||||
|
else:
|
||||||
|
debug_log("沒有活躍的桌面應用程式實例")
|
||||||
|
|
||||||
async def notify_session_update(self, session):
|
async def notify_session_update(self, session):
|
||||||
"""向活躍標籤頁發送會話更新通知"""
|
"""向活躍標籤頁發送會話更新通知"""
|
||||||
try:
|
try:
|
||||||
@ -1012,9 +1095,19 @@ async def launch_web_feedback_ui(
|
|||||||
if manager.server_thread is None or not manager.server_thread.is_alive():
|
if manager.server_thread is None or not manager.server_thread.is_alive():
|
||||||
manager.start_server()
|
manager.start_server()
|
||||||
|
|
||||||
# 使用根路徑 URL 並智能開啟瀏覽器
|
# 檢查是否為桌面模式
|
||||||
|
desktop_mode = os.environ.get("MCP_DESKTOP_MODE", "").lower() == "true"
|
||||||
|
|
||||||
|
# 使用根路徑 URL
|
||||||
feedback_url = manager.get_server_url() # 直接使用根路徑
|
feedback_url = manager.get_server_url() # 直接使用根路徑
|
||||||
has_active_tabs = await manager.smart_open_browser(feedback_url)
|
|
||||||
|
if desktop_mode:
|
||||||
|
# 桌面模式:啟動桌面應用程式
|
||||||
|
debug_log("檢測到桌面模式,啟動桌面應用程式...")
|
||||||
|
has_active_tabs = await manager.launch_desktop_app(feedback_url)
|
||||||
|
else:
|
||||||
|
# Web 模式:智能開啟瀏覽器
|
||||||
|
has_active_tabs = await manager.smart_open_browser(feedback_url)
|
||||||
|
|
||||||
debug_log(f"[DEBUG] 服務器地址: {feedback_url}")
|
debug_log(f"[DEBUG] 服務器地址: {feedback_url}")
|
||||||
|
|
||||||
|
@ -421,6 +421,23 @@ class WebFeedbackSession:
|
|||||||
"status": self.status.value,
|
"status": self.status.value,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 檢查是否為桌面模式,如果是則立即關閉桌面應用程式
|
||||||
|
import os
|
||||||
|
|
||||||
|
if os.environ.get("MCP_DESKTOP_MODE", "").lower() == "true":
|
||||||
|
debug_log("桌面模式:反饋提交後立即關閉桌面應用程式")
|
||||||
|
|
||||||
|
# 立即關閉桌面應用程式,無延遲
|
||||||
|
try:
|
||||||
|
from ..main import get_web_ui_manager
|
||||||
|
|
||||||
|
manager = get_web_ui_manager()
|
||||||
|
manager.close_desktop_app()
|
||||||
|
debug_log("桌面應用程式立即關閉成功")
|
||||||
|
except Exception as close_error:
|
||||||
|
debug_log(f"立即關閉桌面應用程式失敗: {close_error}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_log(f"發送反饋確認失敗: {e}")
|
debug_log(f"發送反饋確認失敗: {e}")
|
||||||
|
|
||||||
|
2
src/mcp_feedback_enhanced/web/static/favicon.ico
Normal file
2
src/mcp_feedback_enhanced/web/static/favicon.ico
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# 這是一個佔位符文件,實際的 favicon 需要是二進制格式
|
||||||
|
# 在實際部署中,應該使用真正的 .ico 文件
|
158
src/mcp_feedback_enhanced/web/static/index.html
Normal file
158
src/mcp_feedback_enhanced/web/static/index.html
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-TW">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>MCP Feedback Enhanced - 桌面版</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
opacity: 0;
|
||||||
|
animation: fadeIn 1s ease-in-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
opacity: 0;
|
||||||
|
animation: fadeIn 1s ease-in-out 0.5s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border: 4px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-top: 4px solid white;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
margin: 0 auto 1rem;
|
||||||
|
opacity: 0;
|
||||||
|
animation: fadeIn 1s ease-in-out 1s forwards, spin 1s linear 1s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
font-size: 1rem;
|
||||||
|
opacity: 0.8;
|
||||||
|
animation: fadeIn 1s ease-in-out 1.5s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #ff6b6b;
|
||||||
|
background: rgba(255, 107, 107, 0.1);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-top: 1rem;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="loading-container">
|
||||||
|
<div class="logo">🖥️ MCP Feedback Enhanced</div>
|
||||||
|
<div class="subtitle">桌面版正在啟動...</div>
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<div class="status" id="status">正在連接到後端服務...</div>
|
||||||
|
<div class="error" id="error"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 檢測是否在 Tauri 環境中
|
||||||
|
const isTauri = window.__TAURI__ !== undefined;
|
||||||
|
|
||||||
|
// 後端服務 URL
|
||||||
|
const backendUrl = 'http://127.0.0.1:8765';
|
||||||
|
|
||||||
|
// 狀態更新函數
|
||||||
|
function updateStatus(message) {
|
||||||
|
document.getElementById('status').textContent = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 錯誤顯示函數
|
||||||
|
function showError(message) {
|
||||||
|
const errorDiv = document.getElementById('error');
|
||||||
|
errorDiv.textContent = message;
|
||||||
|
errorDiv.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 檢查後端服務是否可用
|
||||||
|
async function checkBackendService() {
|
||||||
|
try {
|
||||||
|
updateStatus('正在檢查後端服務...');
|
||||||
|
const response = await fetch(backendUrl + '/health', {
|
||||||
|
method: 'GET',
|
||||||
|
timeout: 5000
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
updateStatus('後端服務已就緒,正在載入界面...');
|
||||||
|
// 延遲一下再重定向,讓用戶看到狀態
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = backendUrl;
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
throw new Error(`後端服務回應錯誤: ${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('檢查後端服務失敗:', error);
|
||||||
|
updateStatus('正在重試連接...');
|
||||||
|
|
||||||
|
// 如果是網路錯誤,直接嘗試重定向
|
||||||
|
if (error.name === 'TypeError' || error.message.includes('fetch')) {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = backendUrl;
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
showError('無法連接到後端服務,請確保服務正在運行');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不是 Tauri 環境,直接重定向
|
||||||
|
if (!isTauri) {
|
||||||
|
updateStatus('正在重定向到 Web 界面...');
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = backendUrl;
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
// Tauri 環境中,檢查後端服務
|
||||||
|
setTimeout(checkBackendService, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 5 秒後還沒有重定向,強制重定向
|
||||||
|
setTimeout(() => {
|
||||||
|
if (window.location.href.includes('index.html')) {
|
||||||
|
updateStatus('強制重定向到後端服務...');
|
||||||
|
window.location.href = backendUrl;
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -561,6 +561,10 @@
|
|||||||
console.log('🔄 收到會話更新訊息:', data.session_info);
|
console.log('🔄 收到會話更新訊息:', data.session_info);
|
||||||
this.handleSessionUpdated(data);
|
this.handleSessionUpdated(data);
|
||||||
break;
|
break;
|
||||||
|
case 'desktop_close_request':
|
||||||
|
console.log('🖥️ 收到桌面關閉請求');
|
||||||
|
this.handleDesktopCloseRequest(data);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -596,6 +600,34 @@
|
|||||||
console.log('反饋已提交,頁面保持開啟狀態');
|
console.log('反饋已提交,頁面保持開啟狀態');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 處理桌面關閉請求
|
||||||
|
*/
|
||||||
|
FeedbackApp.prototype.handleDesktopCloseRequest = function(data) {
|
||||||
|
console.log('🖥️ 處理桌面關閉請求:', data.message);
|
||||||
|
|
||||||
|
// 顯示關閉訊息
|
||||||
|
const closeMessage = data.message || '正在關閉桌面應用程式...';
|
||||||
|
window.MCPFeedback.Utils.showMessage(closeMessage, window.MCPFeedback.Utils.CONSTANTS.MESSAGE_INFO);
|
||||||
|
|
||||||
|
// 檢查是否在 Tauri 環境中
|
||||||
|
if (window.__TAURI__) {
|
||||||
|
console.log('🖥️ 檢測到 Tauri 環境,關閉桌面視窗');
|
||||||
|
try {
|
||||||
|
// 使用 Tauri API 關閉視窗
|
||||||
|
window.__TAURI__.window.getCurrent().close();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('關閉 Tauri 視窗失敗:', error);
|
||||||
|
// 備用方案:關閉瀏覽器視窗
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('🖥️ 非 Tauri 環境,嘗試關閉瀏覽器視窗');
|
||||||
|
// 在瀏覽器環境中嘗試關閉視窗
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 處理會話更新
|
* 處理會話更新
|
||||||
*/
|
*/
|
||||||
|
@ -48,6 +48,18 @@ def is_wsl_environment() -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_desktop_mode() -> bool:
|
||||||
|
"""
|
||||||
|
檢測是否為桌面模式
|
||||||
|
|
||||||
|
當設置了 MCP_DESKTOP_MODE 環境變數時,禁止開啟瀏覽器
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True 表示桌面模式,False 表示 Web 模式
|
||||||
|
"""
|
||||||
|
return os.environ.get("MCP_DESKTOP_MODE", "").lower() == "true"
|
||||||
|
|
||||||
|
|
||||||
def open_browser_in_wsl(url: str) -> None:
|
def open_browser_in_wsl(url: str) -> None:
|
||||||
"""
|
"""
|
||||||
在 WSL 環境中開啟 Windows 瀏覽器
|
在 WSL 環境中開啟 Windows 瀏覽器
|
||||||
@ -117,6 +129,11 @@ def smart_browser_open(url: str) -> None:
|
|||||||
Args:
|
Args:
|
||||||
url: 要開啟的 URL
|
url: 要開啟的 URL
|
||||||
"""
|
"""
|
||||||
|
# 檢查是否為桌面模式
|
||||||
|
if is_desktop_mode():
|
||||||
|
debug_log("檢測到桌面模式,跳過瀏覽器開啟")
|
||||||
|
return
|
||||||
|
|
||||||
if is_wsl_environment():
|
if is_wsl_environment():
|
||||||
debug_log("檢測到 WSL 環境,使用 WSL 專用瀏覽器啟動方式")
|
debug_log("檢測到 WSL 環境,使用 WSL 專用瀏覽器啟動方式")
|
||||||
open_browser_in_wsl(url)
|
open_browser_in_wsl(url)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user