forked from imbytecat/fullstack-starter
refactor(desktop): 从 Tauri 迁移到 Electrobun
- 移除 Tauri v2 代码 (src-tauri/, copy.ts) - 添加 Electrobun 配置和入口 (electrobun.config.ts, src/bun/index.ts) - 更新 package.json 使用 catalog 管理 electrobun 依赖 - 移除 server 中的 @tauri-apps/api 依赖 - 更新 AGENTS.md 文档
This commit is contained in:
@@ -1,51 +1,33 @@
|
||||
# AGENTS.md - Desktop App Guidelines
|
||||
|
||||
Tauri v2 desktop shell - a lightweight wrapper that loads the server app via sidecar.
|
||||
Electrobun desktop shell - loads the server app in a native window.
|
||||
|
||||
## Architecture
|
||||
|
||||
- **Type**: Tauri v2 desktop application (shell only)
|
||||
- **Design**: Tauri provides native desktop APIs; all web logic handled by sidecar
|
||||
- **Sidecar**: The compiled server binary runs as a child process
|
||||
- **Type**: Electrobun desktop application
|
||||
- **Design**: Bun main process + system webview (or CEF)
|
||||
- **Dev mode**: Connects to `localhost:3000` (requires server dev running)
|
||||
- **Prod mode**: Automatically starts sidecar binary
|
||||
|
||||
**This app has NO frontend src** - it loads the server app entirely.
|
||||
- **Prod mode**: Embeds server bundle and starts local HTTP server
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Development (from apps/desktop/)
|
||||
bun dev # Copy sidecar + start Tauri dev
|
||||
bun dev # Start Electrobun dev mode
|
||||
|
||||
# Build
|
||||
bun build # Copy sidecar + build Tauri installer
|
||||
|
||||
# Rust Commands (from src-tauri/)
|
||||
cargo check # Compile check
|
||||
cargo clippy # Linter
|
||||
cargo fmt # Formatter
|
||||
cargo test # Run tests
|
||||
cargo test test_name -- --nocapture # Single test with output
|
||||
bun build # Build canary release
|
||||
bun build:stable # Build stable release
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
apps/desktop/
|
||||
├── src-tauri/ # Rust Tauri code
|
||||
│ ├── src/
|
||||
│ │ ├── main.rs # Entry point (calls lib::run)
|
||||
│ │ ├── lib.rs # Core app logic (plugins, commands, state)
|
||||
│ │ ├── commands/
|
||||
│ │ │ └── mod.rs # Native desktop commands
|
||||
│ │ └── sidecar.rs # Sidecar process management
|
||||
│ ├── binaries/ # Sidecar binaries (copied from server build)
|
||||
│ ├── capabilities/ # Tauri v2 permission config
|
||||
│ ├── icons/ # App icons
|
||||
│ ├── Cargo.toml # Rust dependencies
|
||||
│ └── tauri.conf.json # Tauri configuration
|
||||
├── copy.ts # Script to copy server binary to binaries/
|
||||
├── src/
|
||||
│ └── bun/
|
||||
│ └── index.ts # Electrobun main process entry
|
||||
├── electrobun.config.ts # Electrobun configuration
|
||||
├── package.json
|
||||
└── tsconfig.json
|
||||
```
|
||||
@@ -53,119 +35,45 @@ apps/desktop/
|
||||
## Development Workflow
|
||||
|
||||
1. **Start server dev first**: `cd ../server && bun dev`
|
||||
2. **Start Tauri**: `bun dev` (from apps/desktop/)
|
||||
3. Tauri connects to localhost:3000 with HMR support
|
||||
2. **Start Electrobun**: `bun dev` (from apps/desktop/)
|
||||
3. Electrobun connects to localhost:3000
|
||||
|
||||
## Rust Code Style
|
||||
## Electrobun Patterns
|
||||
|
||||
### Formatting
|
||||
- **Indent**: 4 spaces
|
||||
- **Line width**: 100 chars
|
||||
- Run `cargo fmt` before commit
|
||||
### BrowserWindow
|
||||
|
||||
### Naming
|
||||
| Type | Convention | Example |
|
||||
|------|------------|---------|
|
||||
| Functions/variables | snake_case | `find_available_port` |
|
||||
| Types/structs/enums | PascalCase | `SidecarProcess` |
|
||||
| Constants | SCREAMING_SNAKE | `DEFAULT_PORT` |
|
||||
```typescript
|
||||
import { BrowserWindow } from 'electrobun/bun'
|
||||
|
||||
### Imports
|
||||
```rust
|
||||
// Order: std → external crates → internal modules (separated by blank lines)
|
||||
use std::sync::Mutex;
|
||||
|
||||
use tauri::Manager;
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
|
||||
use crate::sidecar::SidecarProcess;
|
||||
new BrowserWindow({
|
||||
title: 'My App',
|
||||
url: 'http://localhost:3000',
|
||||
frame: {
|
||||
x: 100,
|
||||
y: 100,
|
||||
width: 1200,
|
||||
height: 800,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
```rust
|
||||
// Use expect() with Chinese error messages
|
||||
let sidecar = app_handle
|
||||
.shell()
|
||||
.sidecar("server")
|
||||
.expect("无法找到 server sidecar");
|
||||
### Events
|
||||
|
||||
// Log with emoji for clear feedback
|
||||
println!("✓ Sidecar 启动成功!");
|
||||
eprintln!("✗ Sidecar 启动失败");
|
||||
```
|
||||
```typescript
|
||||
import Electrobun from 'electrobun/bun'
|
||||
|
||||
### Async Code
|
||||
```rust
|
||||
// Use Tauri's async runtime for spawning
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let port = find_available_port(3000).await;
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
## Tauri Patterns
|
||||
|
||||
### Command Definition
|
||||
```rust
|
||||
#[tauri::command]
|
||||
fn greet(name: &str) -> String {
|
||||
format!("Hello, {}!", name)
|
||||
}
|
||||
|
||||
// Register in Builder
|
||||
.invoke_handler(tauri::generate_handler![commands::greet])
|
||||
```
|
||||
|
||||
### State Management
|
||||
```rust
|
||||
struct SidecarProcess(Mutex<Option<CommandChild>>);
|
||||
|
||||
// Register state
|
||||
app.manage(SidecarProcess(Mutex::new(None)));
|
||||
|
||||
// Access state
|
||||
if let Some(state) = app_handle.try_state::<SidecarProcess>() {
|
||||
*state.0.lock().unwrap() = Some(child);
|
||||
}
|
||||
```
|
||||
|
||||
### Sidecar Lifecycle
|
||||
```rust
|
||||
// Start sidecar with environment
|
||||
let sidecar = app_handle
|
||||
.shell()
|
||||
.sidecar("server")
|
||||
.expect("无法找到 server sidecar")
|
||||
.env("PORT", port.to_string());
|
||||
|
||||
// Cleanup on exit
|
||||
match event {
|
||||
tauri::RunEvent::ExitRequested { .. } | tauri::RunEvent::Exit => {
|
||||
if let Some(child) = process.take() {
|
||||
let _ = child.kill();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Electrobun.events.on('will-quit', () => {
|
||||
console.log('App quitting...')
|
||||
})
|
||||
```
|
||||
|
||||
## Critical Rules
|
||||
|
||||
**DO:**
|
||||
- Run `cargo fmt` and `cargo clippy` before commit
|
||||
- Use `expect("中文消息")` instead of `unwrap()`
|
||||
- Always cleanup sidecar on app exit
|
||||
- Declare sidecar in `tauri.conf.json` → `bundle.externalBin`
|
||||
- Run server dev before desktop dev
|
||||
- Use `catalog:` for dependencies
|
||||
- Handle server startup gracefully
|
||||
|
||||
**DON'T:**
|
||||
- Edit `gen/schemas/` (auto-generated)
|
||||
- Use `unwrap()` in production code without context
|
||||
- Block the async runtime (use `spawn_blocking`)
|
||||
|
||||
## Pre-commit Checklist
|
||||
|
||||
- [ ] `cargo fmt` - formatting
|
||||
- [ ] `cargo clippy` - linting
|
||||
- [ ] `cargo check` - compiles
|
||||
- [ ] `cargo test` - tests pass
|
||||
- [ ] Tauri app starts and exits cleanly
|
||||
- Hardcode dependency versions (use catalog)
|
||||
- Block main thread during server wait
|
||||
|
||||
Reference in New Issue
Block a user