forked from imbytecat/fullstack-starter
docs(desktop): 更新 AGENTS.md 文档与开发计划以反映最新实现
This commit is contained in:
@@ -1,124 +1,130 @@
|
||||
# AGENTS.md - Desktop App Guidelines
|
||||
|
||||
Electrobun desktop shell — loads the server app in a native window.
|
||||
Thin Electrobun shell hosting the fullstack server app.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
> **⚠️ This project uses Bun — NOT Node.js / npm. All commands use `bun`. Never use `npm`, `npx`, or `node`.**
|
||||
|
||||
- **Type**: Electrobun desktop application
|
||||
- **Design**: Server-driven desktop (thin native shell hosting web app)
|
||||
- **Runtime**: Bun (Main process) + CEF (Chromium Embedded Framework)
|
||||
- **Framework**: Electrobun
|
||||
- **Build**: Electrobun CLI + Turborepo
|
||||
|
||||
## Architecture
|
||||
|
||||
- **Type**: Electrobun desktop application
|
||||
- **Design**: Bun main process + CEF renderer (Chromium Embedded Framework)
|
||||
- **Dev mode**: Connects to `localhost:3000` (requires server dev running)
|
||||
- **Prod mode**: Embeds server bundle and starts local HTTP server
|
||||
- **Config**: `electrobun.config.ts` — app metadata, build entrypoint, platform options
|
||||
- **Server-driven design**: The desktop app is a "thin" native shell. It does not contain UI or business logic; it merely hosts the `apps/server` TanStack Start application in a native window.
|
||||
- **Dev mode**: Connects to an external Vite dev server at `localhost:3000`. Requires `apps/server` to be running separately.
|
||||
- **Prod mode**: Spawns an embedded TanStack Start server (Nitro) as a child process and loads the dynamically assigned local URL.
|
||||
- **Config**: `electrobun.config.ts` manages app metadata (identifier, name), build entries, and asset bundling.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Development (from apps/desktop/)
|
||||
bun dev # Start Electrobun dev mode
|
||||
# Development
|
||||
bun dev # Start Electrobun dev mode (requires server dev running)
|
||||
|
||||
# Build
|
||||
bun build # Build canary release (current platform)
|
||||
bun build:all # Build canary release (all platforms)
|
||||
bun build:stable # Build stable release (current platform)
|
||||
bun build:stable:all # Build stable release (all platforms)
|
||||
bun build # Build stable release (all platforms)
|
||||
|
||||
# Code Quality
|
||||
bun fix # Biome auto-fix (lint + format)
|
||||
bun fix # Biome auto-fix
|
||||
bun typecheck # TypeScript check
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
apps/desktop/
|
||||
.
|
||||
├── src/
|
||||
│ └── bun/
|
||||
│ └── index.ts # Main process entry (BrowserWindow + server wait)
|
||||
├── electrobun.config.ts # App name, identifier, version, build config
|
||||
├── package.json
|
||||
├── tsconfig.json # Extends @furtherverse/tsconfig/bun.json
|
||||
└── turbo.json # Build output config
|
||||
│ └── index.ts # Main process entry (Window management + server lifecycle)
|
||||
├── electrobun.config.ts # App metadata and build/copy configuration
|
||||
├── package.json # Scripts and dependencies
|
||||
├── turbo.json # Build pipeline dependencies (depends on server build)
|
||||
└── AGENTS.md # Desktop guidelines (this file)
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
1. **Start server dev first**: `bun dev` from `apps/server/`
|
||||
2. **Start Electrobun**: `bun dev` from `apps/desktop/`
|
||||
3. Desktop app polls `localhost:3000` until server responds, then opens window
|
||||
1. **Start server**: In `apps/server`, run `bun dev`.
|
||||
2. **Start desktop**: In `apps/desktop`, run `bun dev`.
|
||||
3. **Connection**: The desktop app polls `localhost:3000` until responsive, then opens the native window.
|
||||
|
||||
## Production Architecture
|
||||
|
||||
### Build Pipeline
|
||||
The desktop build is orchestrated by Turbo. It depends on the server's production build:
|
||||
- `turbo.json`: `@furtherverse/desktop#build` depends on `@furtherverse/server#build`.
|
||||
- `electrobun.config.ts`: Copies `../server/.output` to `server-output` folder within the app bundle.
|
||||
|
||||
### Server Lifecycle
|
||||
In production, the main process manages the embedded server:
|
||||
- **Spawn**: Spawns server from `server-output/server/index.mjs` using `Bun.spawn`.
|
||||
- **Port Allocation**: Server is started with `PORT=0` and `HOST=127.0.0.1`.
|
||||
- **Port Detection**: The main process parses the server's `stdout` using a regex to find the dynamically assigned port.
|
||||
- **Lifecycle**: The server process is tied to the app; it is killed on `SIGTERM`, `SIGINT`, or app exit. If the server process crashes, the app exits with an error.
|
||||
|
||||
## Environment Detection
|
||||
|
||||
The application determines its environment via the `ELECTROBUN_BUILD_ENV` variable, automatically set by the Electrobun CLI:
|
||||
|
||||
```typescript
|
||||
const isDev = () => {
|
||||
const env = process.env.ELECTROBUN_BUILD_ENV
|
||||
return !env || env === 'dev'
|
||||
}
|
||||
```
|
||||
|
||||
- **dev**: Development mode (connects to external host).
|
||||
- **canary / stable**: Production mode (starts embedded server).
|
||||
|
||||
## Environment Variables
|
||||
|
||||
- `ELECTROBUN_BUILD_ENV`: Auto-set by CLI. Determines whether to use local dev server or embedded server.
|
||||
- `DATABASE_URL`: Required by the server process. Must be passed through from the parent environment to the spawned child process.
|
||||
|
||||
## Electrobun Patterns
|
||||
|
||||
### Config (`electrobun.config.ts`)
|
||||
### BrowserWindow Configuration
|
||||
The main window uses the CEF renderer for consistency across platforms.
|
||||
|
||||
```typescript
|
||||
import type { ElectrobunConfig } from 'electrobun'
|
||||
|
||||
export default {
|
||||
app: {
|
||||
name: 'MyApp',
|
||||
identifier: 'com.example.myapp',
|
||||
version: '0.1.0',
|
||||
},
|
||||
build: {
|
||||
bun: { entrypoint: 'src/bun/index.ts' },
|
||||
linux: { bundleCEF: true },
|
||||
},
|
||||
} satisfies ElectrobunConfig
|
||||
```
|
||||
|
||||
### BrowserWindow
|
||||
|
||||
```typescript
|
||||
import { BrowserWindow } from 'electrobun/bun'
|
||||
|
||||
new BrowserWindow({
|
||||
title: 'My App',
|
||||
url: 'http://localhost:3000',
|
||||
frame: { x: 100, y: 100, width: 1200, height: 800 },
|
||||
title: 'Furtherverse',
|
||||
url: serverUrl,
|
||||
frame: {
|
||||
x: 100,
|
||||
y: 100,
|
||||
width: 1200,
|
||||
height: 800,
|
||||
},
|
||||
renderer: 'cef',
|
||||
})
|
||||
```
|
||||
|
||||
### Server Readiness Check
|
||||
### Path Aliases
|
||||
The main process uses `electrobun/bun` for native APIs and `PATHS` for locating bundled assets.
|
||||
|
||||
```typescript
|
||||
// Poll server before opening window — don't block indefinitely
|
||||
async function waitForServer(url: string, timeoutMs = 30000): Promise<boolean> {
|
||||
const start = Date.now()
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
try {
|
||||
const response = await fetch(url, { method: 'HEAD' })
|
||||
if (response.ok) return true
|
||||
} catch {
|
||||
await Bun.sleep(100)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
```
|
||||
import { BrowserWindow, PATHS } from 'electrobun/bun'
|
||||
|
||||
### Application Events
|
||||
|
||||
```typescript
|
||||
import Electrobun from 'electrobun/bun'
|
||||
|
||||
Electrobun.events.on('will-quit', () => {
|
||||
console.log('App quitting...')
|
||||
})
|
||||
// Locate the embedded server bundle
|
||||
const serverEntryPath = join(PATHS.VIEWS_FOLDER, '..', 'server-output', 'server', 'index.mjs')
|
||||
```
|
||||
|
||||
## Critical Rules
|
||||
|
||||
**DO:**
|
||||
- Run server dev before desktop dev
|
||||
- Use `catalog:` for dependency versions
|
||||
- Wait for server readiness before opening BrowserWindow
|
||||
- Handle server timeout gracefully (exit with helpful error message)
|
||||
- Use arrow functions for all components and utility functions.
|
||||
- Ensure `apps/server` is built before building `apps/desktop` (handled by Turbo).
|
||||
- Parse the server's stdout for the port instead of hardcoding ports in production.
|
||||
- Handle server process termination to avoid orphan processes.
|
||||
- Use `catalog:` for all dependency versions in `package.json`.
|
||||
|
||||
**DON'T:**
|
||||
- Use `npm`, `npx`, `node`, `yarn`, `pnpm` — always use `bun` / `bunx`
|
||||
- Hardcode dependency versions (use catalog)
|
||||
- Block main thread with synchronous waits
|
||||
- Use `unwrap()`-style patterns without error handling
|
||||
- Use `npm`, `npx`, `node`, `yarn`, or `pnpm`. Always use `bun`.
|
||||
- Hardcode `localhost:3000` for production builds.
|
||||
- Include UI components or business logic in the desktop app (keep it thin).
|
||||
- Use `as any` or `@ts-ignore`.
|
||||
|
||||
Reference in New Issue
Block a user