forked from imbytecat/fullstack-starter
- 新增根目录 AGENTS.md 作为 monorepo 总览 - 移动 desktop AGENTS.md 从 src-tauri/ 到 apps/desktop/ - 修正 server AGENTS.md 目录结构 (src/server/api/ 而非 src/orpc/) - 明确 desktop 为纯 Tauri 壳子,无前端代码,通过 sidecar 加载 server
172 lines
4.5 KiB
Markdown
172 lines
4.5 KiB
Markdown
# AGENTS.md - Desktop App Guidelines
|
|
|
|
Tauri v2 desktop shell - a lightweight wrapper that loads the server app via sidecar.
|
|
|
|
## 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
|
|
- **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.
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
# Development (from apps/desktop/)
|
|
bun dev # Copy sidecar + start Tauri dev
|
|
|
|
# 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
|
|
```
|
|
|
|
## 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/
|
|
├── package.json
|
|
└── tsconfig.json
|
|
```
|
|
|
|
## 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
|
|
|
|
## Rust Code Style
|
|
|
|
### Formatting
|
|
- **Indent**: 4 spaces
|
|
- **Line width**: 100 chars
|
|
- Run `cargo fmt` before commit
|
|
|
|
### Naming
|
|
| Type | Convention | Example |
|
|
|------|------------|---------|
|
|
| Functions/variables | snake_case | `find_available_port` |
|
|
| Types/structs/enums | PascalCase | `SidecarProcess` |
|
|
| Constants | SCREAMING_SNAKE | `DEFAULT_PORT` |
|
|
|
|
### 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;
|
|
```
|
|
|
|
### Error Handling
|
|
```rust
|
|
// Use expect() with Chinese error messages
|
|
let sidecar = app_handle
|
|
.shell()
|
|
.sidecar("server")
|
|
.expect("无法找到 server sidecar");
|
|
|
|
// Log with emoji for clear feedback
|
|
println!("✓ Sidecar 启动成功!");
|
|
eprintln!("✗ Sidecar 启动失败");
|
|
```
|
|
|
|
### 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();
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
```
|
|
|
|
## 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`
|
|
|
|
**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
|