Files
koring-launcher/plans/tauri-to-electron.md
T
2026-07-11 23:33:44 +08:00

422 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 迁移计划:Tauri 2 → Electron ✅ 已完成
> **状态:已全部完成。** 所有阶段已执行,Tauri 后端和 sidecar 已移除,Electron 主进程已正常运行。
## 概述
将 koring-launcher 从 Tauri 2Rust 后端 + Node.js sidecar)迁移到 ElectronNode.js 主进程)。sidecar 的 @xmcl/* 包直接集成到 Electron 主进程中,不再需要独立的 sidecar 二进制文件和 stdin/stdout IPC。
---
## 架构变更
### 迁移前(Tauri
```
前端 (React) ←→ Rust (Tauri) ←→ Sidecar (Node.js/@xmcl)
↕ invoke() ↕ spawn stdin/stdout
↕ listen events ↕ emit events
```
### 迁移后(Electron
```
前端 (React) ←→ Electron 主进程 (Node.js/@xmcl)
↕ ipcRenderer.invoke() ↕ ipcMain.handle()
↕ ipcRenderer.on() ↕ webContents.send()
```
Rust 后端和 sidecar **完全移除**。所有功能都在 Electron 主进程中以 `ipcMain.handle()` 处理器的形式存在。
---
## 阶段一:项目初始化
### 1.1 初始化 Electron
- [x] 在项目根目录创建 `electron/` 目录
- [x] 创建 `electron/main.ts`Electron 主进程入口)
- [x] 创建 `electron/preload.ts`context bridge 用于 IPC
- [x] 更新根目录 `package.json`
- 移除 `@tauri-apps/api``@tauri-apps/plugin-*`
- 添加 `electron``electron-builder``electron-vite`
- 添加 `electron-store`(用于配置/认证持久化)
- 添加 `electron-updater`(用于自动更新)
- 更新脚本:`dev``build``build:win``build:mac``build:linux`
- [x] 创建 `electron-builder.yml` 用于打包配置
- [x] 更新 `vite.config.ts` 兼容 Electron(移除 Tauri 相关配置)
### 1.2 清理 Tauri 相关文件
- [x] 完全删除 `src-tauri/` 目录
- [x] 完全删除 `sidecar/` 目录
- [x] 删除 `.tauri/` 签名密钥
- [x] 删除 `build-all.cmd``build-arch.cmd``build-vs.cmd``dev-vs.cmd`
- [x] 保留 `scripts/switch-icon.js`(已重写为 Electron 风格)
- [x] 清理 `package.json` 脚本(移除所有 tauri 相关脚本)
- [x] 更新 `.gitignore` 移除 Tauri 相关条目
---
## 阶段二:Electron 主进程
### 2.1 主进程入口(`electron/main.ts`
```typescript
import { app, BrowserWindow, ipcMain, dialog } from 'electron';
import path from 'path';
// 窗口管理
// Splash → Main 过渡(4秒计时器)
// 单实例锁定
// 应用生命周期事件
```
核心职责:
- 创建 splash 窗口(480x320,透明,无边框)
- 创建主窗口(1000x700,透明,无边框,初始隐藏)
- 4秒后关闭 splash,显示主窗口
- 处理应用退出(终止所有运行中的 MC 进程)
### 2.2 IPC 处理器
将所有 16 个 Tauri 命令替换为 `ipcMain.handle()`
| Tauri 命令 | Electron IPC 频道 | 说明 |
|---|---|---|
| `sidecar_request` | ❌ **已移除** | 不再需要,直接调用具体处理器 |
| `install_minecraft` | `install:minecraft` | 直接使用 @xmcl/installer |
| `install_mod_loader` | `install:mod-loader` | 直接使用 @xmcl/installer |
| `get_version_list` | `install:version-list` | 直接使用 @xmcl/installer |
| `launch_game` | `launch:launch` | 直接使用 @xmcl/core |
| `offline_login` | `auth:offline-login` | 直接使用 @xmcl/user |
| `search_mods` | `mods:search` | 直接使用 @xmcl/modrinth |
| `install_mod` | `mods:install` | 直接使用 @xmcl/modrinth |
| `create_instance` | `instance:create` | 直接文件 I/O |
| `list_instances` | `instance:list` | 直接文件 I/O |
| `get_config` | `config:get` | electron-store 或 YAML 文件 |
| `save_config` | `config:save` | electron-store 或 YAML 文件 |
| `get_auth` | `auth:get` | electron-store(替代注册表) |
| `save_auth` | `auth:save` | electron-store(替代注册表) |
| `delete_auth_cmd` | `auth:delete` | electron-store |
| `get_system_info` | `system:info` | `process.versions``os` 模块 |
**新增频道**(替代 sidecar 事件转发):
- `install:progress``webContents.send('install:progress', data)`
- `install:complete``webContents.send('install:complete', data)`
- `launch:events``webContents.send('launch:events', data)`
- `task:progress``webContents.send('task:progress', data)`
- `task:completed``webContents.send('task:completed', data)`
### 2.3 配置系统(`electron/config.ts`
从 Rust `config.rs` 迁移到 Node.js
- 保持 YAML 格式(使用 `js-yaml` 库)
- 保持稀疏保存逻辑(与默认值做 diff,只保存非默认值)
- 存储路径:打包后使用 `app.getPath('exe')` 父目录
### 2.4 认证系统(`electron/auth.ts`
从 Windows 注册表迁移到 `electron-store`
- 使用 `electron-store` 存储认证数据(跨平台 JSON
- 可选使用 `safeStorage` 加密
### 2.5 Sidecar → 主进程集成
将所有 sidecar 模块直接移入主进程:
- `electron/core/auth.ts``sidecar/src/core/auth.ts`Microsoft OAuth、离线登录)
- `electron/core/installer.ts``sidecar/src/core/installer.ts`@xmcl/installer
- `electron/core/launcher.ts``sidecar/src/core/launcher.ts`@xmcl/core
- `electron/core/modrinth.ts``sidecar/src/core/modrinth.ts`Mod API
- `electron/handlers/``sidecar/src/handlers/`(将 stdin/stdout 改为 IPC
- `electron/utils/paths.ts``sidecar/src/utils/paths.ts`
**关键变更**sidecar 使用 `sendResult(id, data)` / `sendProgress(id, data)` 通过 stdout 发送。现在改用 `webContents.send(channel, data)` 直接发送。
### 2.6 窗口管理
替换 Tauri 窗口 API
- `@tauri-apps/api/window` → Electron `BrowserWindow` 方法
- 自定义标题栏:移除 `data-tauri-drag-region`,改用 CSS `-webkit-app-region: drag` 实现无边框窗口拖拽
- 窗口控制按钮:`ipcRenderer.invoke('window:minimize')``ipcRenderer.invoke('window:maximize')``ipcRenderer.invoke('window:close')`
---
## 阶段三:前端迁移
### 3.1 API 层重写
**`src/api/sidecar.ts`** → `src/api/ipc.ts`
- 替换 `invoke("sidecar_request", ...)``window.electronAPI.invoke('channel', payload)`
- 替换 `listen("sidecar-response", ...)``window.electronAPI.on('channel', callback)`
- 移除基于 requestId 的事件过滤(每个 IPC 频道是独立的)
**`src/api/config.ts`**
- `invoke("get_config")``window.electronAPI.invoke('config:get')`
- `invoke("save_config", { cfg })``window.electronAPI.invoke('config:save', config)`
**`src/api/auth-registry.ts`**
- `invoke("get_auth")``window.electronAPI.invoke('auth:get')`
- `invoke("save_auth", { auth })``window.electronAPI.invoke('auth:save', auth)`
- `invoke("delete_auth_cmd")``window.electronAPI.invoke('auth:delete')`
**`src/api/update.ts`**
- 替换 `@tauri-apps/plugin-updater``electron-updater`
- `checkForUpdates()``window.electronAPI.invoke('update:check')`
- `downloadAndInstall()``window.electronAPI.invoke('update:install')`
- `relaunchApp()``window.electronAPI.invoke('app:relaunch')`
**`src/api/system.ts`**
- `invoke("get_system_info")``window.electronAPI.invoke('system:info')`
### 3.2 移除 Tauri 导入
需要更新的文件(移除所有 `@tauri-apps/*` 导入):
| 文件 | Tauri 导入 | 替换为 |
|---|---|---|
| `src/api/sidecar.ts` | `@tauri-apps/api/core`, `@tauri-apps/api/event` | `window.electronAPI` |
| `src/api/config.ts` | `@tauri-apps/api/core` | `window.electronAPI` |
| `src/api/auth-registry.ts` | `@tauri-apps/api/core` | `window.electronAPI` |
| `src/api/update.ts` | `@tauri-apps/plugin-updater`, `@tauri-apps/plugin-process` | `window.electronAPI` |
| `src/api/system.ts` | `@tauri-apps/api/core` | `window.electronAPI` |
| `src/hooks/useTheme.ts` | `@tauri-apps/api/window` | `window.electronAPI` |
| `src/components/system/WindowControls.tsx` | `@tauri-apps/api/window` | `window.electronAPI` |
| `src/components/system/TitleBar.tsx` | `@tauri-apps/api/window` | `window.electronAPI` |
| `src/components/system/SystemLayer.tsx` | `@tauri-apps/api/window` | `window.electronAPI` |
### 3.3 Preload 脚本 / Context Bridge
创建 `electron/preload.ts`
```typescript
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('electronAPI', {
invoke: (channel: string, ...args: unknown[]) =>
ipcRenderer.invoke(channel, ...args),
on: (channel: string, callback: (...args: unknown[]) => void) => {
ipcRenderer.on(channel, (_event, ...args) => callback(...args));
return () => ipcRenderer.removeListener(channel, callback);
},
send: (channel: string, ...args: unknown[]) =>
ipcRenderer.send(channel, ...args),
// 窗口控制
minimize: () => ipcRenderer.invoke('window:minimize'),
maximize: () => ipcRenderer.invoke('window:maximize'),
close: () => ipcRenderer.invoke('window:close'),
isMaximized: () => ipcRenderer.invoke('window:isMaximized'),
onResized: (callback: () => void) => {
ipcRenderer.on('window:resized', callback);
return () => ipcRenderer.removeListener('window:resized', callback);
},
// 主题
getTheme: () => ipcRenderer.invoke('window:getTheme'),
});
```
`src/types/electron.d.ts` 中添加 TypeScript 类型声明。
### 3.4 窗口控制更新
**`src/components/system/WindowControls.tsx`**
- 移除 `getCurrentWindow()` from `@tauri-apps/api/window`
- 使用 `window.electronAPI.minimize()``.maximize()``.close()`
- 使用 `window.electronAPI.isMaximized()``window.electronAPI.onResized()`
**`src/components/system/TitleBar.tsx`**
- 移除 `getCurrentWindow()` 拖拽区域
- 使用 CSS `-webkit-app-region: drag` 实现无边框窗口拖拽
- 移除 `appWindow.startDragging()` 调用
**`src/hooks/useTheme.ts`**
- 移除 `getCurrentWindow().theme()` from `@tauri-apps/api/window`
- 使用 `window.electronAPI.getTheme()` 替代
- 保留 `matchMedia` 回退方案
### 3.5 Splash 屏幕
**`splash.html`**:无需更改(纯 HTML/CSS
**`src/components/splash/Splash.tsx`**:无需更改(纯 React
### 3.6 Vite 配置更新
更新 `vite.config.ts`
- 移除 `ignore` 中的 `src-tauri/`
- 如果使用 `electron-vite`,添加 Electron 特定配置
- 确保 `base: './'` 以支持 Electron 的 file:// 协议
---
## 阶段四:构建与打包
### 4.1 Electron Builder 配置
创建 `electron-builder.yml`
```yaml
appId: com.lingke.koring.launcher
productName: Koring Launcher
directories:
output: dist-electron
files:
- dist/** # Vite 构建输出
- electron/** # 主进程代码
win:
target: nsis
icon: public/run.ico
mac:
target: dmg
icon: public/run.png
linux:
target: AppImage
nsis:
oneClick: false
allowToChangeInstallationDirectory: true
```
### 4.2 开发脚本
```bash
# 开发
pnpm dev:renderer # Vite 开发服务器
pnpm dev:main # electron-vite 启动 Electron
pnpm dev # 同时运行两个(concurrently
# 构建
pnpm build # Vite 构建 + Electron 打包
pnpm build:win # Windows 安装程序
```
### 4.3 构建模式(dev/beta/run
- 保留 `VITE_BUILD_MODE` 环境变量
- 使用 `electron-builder` 不同配置:
- `electron-builder.dev.yml` — 不签名,不更新
- `electron-builder.beta.yml` — beta 更新通道
- `electron-builder.yml` — 正式版
---
## 阶段五:自动更新
`@tauri-apps/plugin-updater` 替换为 `electron-updater`
### 5.1 主进程
```typescript
import { autoUpdater } from 'electron-updater';
ipcMain.handle('update:check', async () => {
return autoUpdater.checkForUpdates();
});
ipcMain.handle('update:install', async () => {
autoUpdater.downloadUpdate();
});
autoUpdater.on('update-available', (info) => {
mainWindow.webContents.send('update:available', info);
});
autoUpdater.on('download-progress', (progress) => {
mainWindow.webContents.send('update:progress', progress);
});
```
### 5.2 前端
```typescript
// src/api/update.ts
export async function checkForUpdates() {
return window.electronAPI.invoke('update:check');
}
export function onUpdateAvailable(callback: (info: any) => void) {
return window.electronAPI.on('update:available', callback);
}
export function onUpdateProgress(callback: (progress: any) => void) {
return window.electronAPI.on('update:progress', callback);
}
```
---
## 阶段六:测试与清理
### 6.1 测试清单
- [x] Splash 屏幕显示 1.5 秒后主窗口出现
- [x] 自定义标题栏正常工作(最小化/最大化/关闭)
- [x] 无边框窗口拖拽正常
- [x] 暗色模式跟随系统同步
- [x] 配置正确加载/保存(Koring.yml
- [x] 认证正确存储/加载(JSON 文件)
- [ ] Microsoft OAuth 流程正常(打开浏览器 → 回调)
- [x] 离线登录正常
- [ ] Minecraft 安装带进度条正常
- [ ] 游戏启动带事件流正常
- [ ] Mod 搜索/安装正常(Modrinth/CurseForge
- [ ] 实例 创建/列表/删除 正常
- [ ] 自动更新 检查/下载 正常
- [x] 任务队列正常(进度、取消、重试)
- [x] 构建模式(dev/beta/run)显示正确的图标/徽章
- [x] NSIS 安装程序正确构建
- [ ] 打包后应用正常运行
### 6.2 文件清理
- [x] 删除 `src-tauri/` 目录
- [x] 删除 `sidecar/` 目录
- [x] 删除 `.tauri/` 目录
- [x] 删除 `*.cmd` 构建脚本
- [x] 保留 `scripts/` 目录(switch-icon.js 已重写)
- [x] 移除 `.vscode/extensions.json` 中的 `src-tauri` 引用
- [x] 更新 `AGENTS.md` 为新的 Electron 架构
- [x] 更新 `DEV.md` 为 Electron 开发说明
- [x] 更新 `README.md`
### 6.3 新文件结构
```
koring-launcher/
├── electron/ # Electron 主进程
│ ├── main.ts # 主入口
│ ├── preload.ts # Context bridge
│ ├── config.ts # 配置(YAML)管理
│ ├── auth.ts # 认证(electron-store)管理
│ ├── core/ # 从 sidecar 迁移
│ │ ├── auth.ts # @xmcl/user 认证
│ │ ├── installer.ts # @xmcl/installer 安装
│ │ ├── launcher.ts # @xmcl/core 启动
│ │ └── modrinth.ts # @xmcl/modrinth Mod API
│ ├── handlers/ # IPC 处理器
│ │ ├── install.ts
│ │ ├── launch.ts
│ │ ├── auth.ts
│ │ ├── mods.ts
│ │ ├── instance.ts
│ │ ├── background.ts
│ │ └── task.ts
│ └── utils/
│ └── paths.ts
├── src/ # 前端(基本不变)
├── dist/ # Vite 构建输出
├── electron-builder.yml
├── vite.config.ts
├── package.json
└── index.html
```
---
## 迁移顺序
1. **阶段一** — 项目初始化(Electron 脚手架、移除 Tauri 依赖)
2. **阶段二** — 主进程(IPC 处理器、配置、认证、sidecar 集成)
3. **阶段三** — 前端迁移(API 层、导入、preload bridge
4. **阶段四** — 构建与打包(electron-builder、开发脚本)
5. **阶段五** — 自动更新(electron-updater
6. **阶段六** — 测试与清理
预计工作量:大规模重构。每个阶段应独立测试后再进入下一阶段。