forked from imbytecat/fullstack-starter
docs: 更新 AGENTS.md 适配 Electrobun 替代 Tauri
This commit is contained in:
94
AGENTS.md
94
AGENTS.md
@@ -4,14 +4,14 @@ Guidelines for AI agents working in this Bun monorepo.
|
|||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
> **⚠️ This project uses [Bun](https://bun.sh) exclusively as both the JavaScript runtime and package manager. Do NOT use Node.js / npm / yarn / pnpm. All commands start with `bun` — use `bun install` for dependencies and `bun run` / `bun <script>` for scripts. Never use `npm`, `npx`, or `node`.**
|
> **This project uses [Bun](https://bun.sh) exclusively as both the JavaScript runtime and package manager. Do NOT use Node.js / npm / yarn / pnpm. All commands start with `bun` — use `bun install` for dependencies and `bun run` / `bun <script>` for scripts. Never use `npm`, `npx`, or `node`.**
|
||||||
|
|
||||||
- **Monorepo**: Bun workspaces + Turborepo orchestration
|
- **Monorepo**: Bun workspaces + Turborepo orchestration
|
||||||
- **Runtime**: Bun (see `mise.toml` for version) — **NOT Node.js**
|
- **Runtime**: Bun (see `mise.toml` for version) — **NOT Node.js**
|
||||||
- **Package Manager**: Bun — **NOT npm / yarn / pnpm**
|
- **Package Manager**: Bun — **NOT npm / yarn / pnpm**
|
||||||
- **Apps**:
|
- **Apps**:
|
||||||
- `apps/server` - TanStack Start fullstack web app (see `apps/server/AGENTS.md`)
|
- `apps/server` - TanStack Start fullstack web app (see `apps/server/AGENTS.md`)
|
||||||
- `apps/desktop` - Tauri v2 desktop shell, loads server via sidecar (see `apps/desktop/AGENTS.md`)
|
- `apps/desktop` - Electrobun desktop shell, loads server in native window (see `apps/desktop/AGENTS.md`)
|
||||||
- **Packages**: `packages/utils`, `packages/tsconfig` (shared configs)
|
- **Packages**: `packages/utils`, `packages/tsconfig` (shared configs)
|
||||||
|
|
||||||
## Build / Lint / Test Commands
|
## Build / Lint / Test Commands
|
||||||
@@ -27,7 +27,8 @@ bun typecheck # TypeScript check across monorepo
|
|||||||
### Server App (`apps/server`)
|
### Server App (`apps/server`)
|
||||||
```bash
|
```bash
|
||||||
bun dev # Vite dev server (localhost:3000)
|
bun dev # Vite dev server (localhost:3000)
|
||||||
bun build # Production build → .output/
|
bun build # Production build -> .output/
|
||||||
|
bun compile # Compile to standalone binary
|
||||||
bun fix # Biome auto-fix
|
bun fix # Biome auto-fix
|
||||||
bun typecheck # TypeScript check
|
bun typecheck # TypeScript check
|
||||||
|
|
||||||
@@ -40,13 +41,11 @@ bun db:studio # Open Drizzle Studio
|
|||||||
|
|
||||||
### Desktop App (`apps/desktop`)
|
### Desktop App (`apps/desktop`)
|
||||||
```bash
|
```bash
|
||||||
bun dev # Copy sidecar + start Tauri dev
|
bun dev # Start Electrobun dev mode (requires server dev running)
|
||||||
bun build # Copy sidecar + build installer
|
bun build # Build canary release
|
||||||
|
bun build:stable # Build stable release
|
||||||
# Rust (from apps/desktop/src-tauri/)
|
bun fix # Biome auto-fix
|
||||||
cargo check # Compile check
|
bun typecheck # TypeScript check
|
||||||
cargo clippy # Linter
|
|
||||||
cargo fmt # Formatter
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
@@ -54,23 +53,17 @@ No test framework configured yet. When adding tests:
|
|||||||
```bash
|
```bash
|
||||||
bun test path/to/test.ts # Run single test file
|
bun test path/to/test.ts # Run single test file
|
||||||
bun test -t "pattern" # Run tests matching pattern
|
bun test -t "pattern" # Run tests matching pattern
|
||||||
cargo test test_name -- --nocapture # Rust single test with output
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Code Style (TypeScript)
|
## Code Style (TypeScript)
|
||||||
|
|
||||||
### Formatting (Biome)
|
### Formatting (Biome)
|
||||||
- **Indent**: 2 spaces
|
- **Indent**: 2 spaces | **Line endings**: LF
|
||||||
- **Line endings**: LF
|
- **Quotes**: Single `'` | **Semicolons**: Omit (ASI)
|
||||||
- **Quotes**: Single `'`
|
|
||||||
- **Semicolons**: Omit (ASI)
|
|
||||||
- **Arrow parentheses**: Always `(x) => x`
|
- **Arrow parentheses**: Always `(x) => x`
|
||||||
|
|
||||||
### Imports
|
### Imports
|
||||||
Biome auto-organizes. Order:
|
Biome auto-organizes. Order: 1) External packages → 2) Internal `@/*` aliases → 3) Type imports (`import type { ... }`)
|
||||||
1. External packages
|
|
||||||
2. Internal `@/*` aliases
|
|
||||||
3. Type imports (`import type { ... }`)
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { createFileRoute } from '@tanstack/react-router'
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
@@ -80,10 +73,7 @@ import type { ReactNode } from 'react'
|
|||||||
```
|
```
|
||||||
|
|
||||||
### TypeScript Strictness
|
### TypeScript Strictness
|
||||||
- `strict: true`
|
- `strict: true`, `noUncheckedIndexedAccess: true`, `noImplicitOverride: true`, `verbatimModuleSyntax: true`
|
||||||
- `noUncheckedIndexedAccess: true` - array/object access returns `T | undefined`
|
|
||||||
- `noImplicitOverride: true`
|
|
||||||
- `verbatimModuleSyntax: true`
|
|
||||||
- Use `@/*` path aliases (maps to `src/*`)
|
- Use `@/*` path aliases (maps to `src/*`)
|
||||||
|
|
||||||
### Naming Conventions
|
### Naming Conventions
|
||||||
@@ -97,41 +87,19 @@ import type { ReactNode } from 'react'
|
|||||||
| Types/Interfaces | PascalCase | `UserProfile` |
|
| Types/Interfaces | PascalCase | `UserProfile` |
|
||||||
|
|
||||||
### React Patterns
|
### React Patterns
|
||||||
```typescript
|
- Components: arrow functions (enforced by Biome)
|
||||||
// Components: arrow functions (enforced by Biome)
|
- Routes: TanStack Router file conventions (`export const Route = createFileRoute(...)`)
|
||||||
const MyComponent = ({ title }: { title: string }) => {
|
- Data fetching: `useSuspenseQuery(orpc.feature.list.queryOptions())`
|
||||||
return <div>{title}</div>
|
- Let React Compiler handle memoization (no manual `useMemo`/`useCallback`)
|
||||||
}
|
|
||||||
|
|
||||||
// Routes: TanStack Router file conventions
|
|
||||||
export const Route = createFileRoute('/')({
|
|
||||||
component: Home,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Data fetching: TanStack Query
|
|
||||||
const { data } = useSuspenseQuery(orpc.todo.list.queryOptions())
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error Handling
|
### Error Handling
|
||||||
- Use `try-catch` for async operations
|
- Use `try-catch` for async operations; throw descriptive errors
|
||||||
- Throw descriptive errors
|
|
||||||
- ORPC: Use `ORPCError` with proper codes (`NOT_FOUND`, `INPUT_VALIDATION_FAILED`)
|
- ORPC: Use `ORPCError` with proper codes (`NOT_FOUND`, `INPUT_VALIDATION_FAILED`)
|
||||||
- Never use empty catch blocks
|
- Never use empty catch blocks
|
||||||
|
|
||||||
## Code Style (Rust - Tauri)
|
|
||||||
|
|
||||||
- **Indent**: 4 spaces
|
|
||||||
- **Naming**: snake_case (functions), PascalCase (types), SCREAMING_SNAKE (consts)
|
|
||||||
- Use `expect("中文消息")` over `unwrap()`
|
|
||||||
- Async: `tokio` runtime, `tauri::async_runtime::spawn`
|
|
||||||
- Run `cargo fmt` and `cargo clippy` before commit
|
|
||||||
|
|
||||||
## Database (Drizzle ORM)
|
## Database (Drizzle ORM)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core'
|
|
||||||
import { sql } from 'drizzle-orm'
|
|
||||||
|
|
||||||
export const myTable = pgTable('my_table', {
|
export const myTable = pgTable('my_table', {
|
||||||
id: uuid().primaryKey().default(sql`uuidv7()`),
|
id: uuid().primaryKey().default(sql`uuidv7()`),
|
||||||
name: text().notNull(),
|
name: text().notNull(),
|
||||||
@@ -143,17 +111,22 @@ export const myTable = pgTable('my_table', {
|
|||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
- Use `@t3-oss/env-core` with Zod validation in `src/env.ts`
|
- Use `@t3-oss/env-core` with Zod validation in `src/env.ts`
|
||||||
- Server vars: no prefix
|
- Server vars: no prefix | Client vars: `VITE_` prefix required
|
||||||
- Client vars: `VITE_` prefix required
|
|
||||||
- Never commit `.env` files
|
- Never commit `.env` files
|
||||||
|
|
||||||
|
## Dependency Management
|
||||||
|
|
||||||
|
- All versions centralized in root `package.json` `catalog` field
|
||||||
|
- Workspace packages use `"catalog:"` — never hardcode versions
|
||||||
|
- Internal packages use `"workspace:*"` references
|
||||||
|
|
||||||
## Critical Rules
|
## Critical Rules
|
||||||
|
|
||||||
**DO:**
|
**DO:**
|
||||||
- Run `bun fix` before committing
|
- Run `bun fix` before committing
|
||||||
- Use `@/*` path aliases (not relative imports)
|
- Use `@/*` path aliases (not relative imports)
|
||||||
- Let React Compiler handle memoization (no manual `useMemo`/`useCallback`)
|
|
||||||
- Include `createdAt`/`updatedAt` on all tables
|
- Include `createdAt`/`updatedAt` on all tables
|
||||||
|
- Use `catalog:` for dependency versions
|
||||||
|
|
||||||
**DON'T:**
|
**DON'T:**
|
||||||
- Use `npm`, `npx`, `node`, `yarn`, `pnpm` — always use `bun` / `bunx`
|
- Use `npm`, `npx`, `node`, `yarn`, `pnpm` — always use `bun` / `bunx`
|
||||||
@@ -161,7 +134,7 @@ export const myTable = pgTable('my_table', {
|
|||||||
- Use `as any`, `@ts-ignore`, `@ts-expect-error`
|
- Use `as any`, `@ts-ignore`, `@ts-expect-error`
|
||||||
- Commit `.env` files
|
- Commit `.env` files
|
||||||
- Use empty catch blocks `catch(e) {}`
|
- Use empty catch blocks `catch(e) {}`
|
||||||
- Use `unwrap()` in Rust without `expect()`
|
- Hardcode dependency versions in workspace packages
|
||||||
|
|
||||||
## Git Workflow
|
## Git Workflow
|
||||||
|
|
||||||
@@ -185,20 +158,21 @@ export const myTable = pgTable('my_table', {
|
|||||||
│ │ │ ├── api/ # ORPC contracts, routers, middlewares
|
│ │ │ ├── api/ # ORPC contracts, routers, middlewares
|
||||||
│ │ │ └── db/ # Drizzle schema
|
│ │ │ └── db/ # Drizzle schema
|
||||||
│ │ └── AGENTS.md
|
│ │ └── AGENTS.md
|
||||||
│ └── desktop/ # Tauri v2 shell (no frontend src)
|
│ └── desktop/ # Electrobun desktop shell
|
||||||
│ ├── src-tauri/ # Rust Tauri code
|
│ ├── src/
|
||||||
│ │ ├── src/ # Rust source
|
│ │ └── bun/
|
||||||
│ │ └── binaries/ # Sidecar binaries
|
│ │ └── index.ts # Main process entry
|
||||||
|
│ ├── electrobun.config.ts # Electrobun configuration
|
||||||
│ └── AGENTS.md
|
│ └── AGENTS.md
|
||||||
├── packages/
|
├── packages/
|
||||||
│ ├── tsconfig/ # Shared TS configs
|
│ ├── tsconfig/ # Shared TS configs
|
||||||
│ └── utils/ # Shared utilities
|
│ └── utils/ # Shared utilities
|
||||||
├── biome.json # Linting/formatting config
|
├── biome.json # Linting/formatting config
|
||||||
├── turbo.json # Turbo task orchestration
|
├── turbo.json # Turbo task orchestration
|
||||||
└── package.json # Workspace root
|
└── package.json # Workspace root + dependency catalog
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
||||||
- `apps/server/AGENTS.md` - Detailed TanStack Start / ORPC patterns
|
- `apps/server/AGENTS.md` - Detailed TanStack Start / ORPC patterns
|
||||||
- `apps/desktop/AGENTS.md` - Rust / Tauri development guide
|
- `apps/desktop/AGENTS.md` - Electrobun desktop development guide
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
# AGENTS.md - Desktop App Guidelines
|
# AGENTS.md - Desktop App Guidelines
|
||||||
|
|
||||||
Electrobun desktop shell - loads the server app in a native window.
|
Electrobun desktop shell — loads the server app in a native window.
|
||||||
|
|
||||||
> **⚠️ This project uses Bun — NOT Node.js / npm. All commands use `bun`. Never use `npm`, `npx`, or `node`.**
|
> **⚠️ This project uses Bun — NOT Node.js / npm. All commands use `bun`. Never use `npm`, `npx`, or `node`.**
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
- **Type**: Electrobun desktop application
|
- **Type**: Electrobun desktop application
|
||||||
- **Design**: Bun main process + system webview (or CEF)
|
- **Design**: Bun main process + CEF renderer (Chromium Embedded Framework)
|
||||||
- **Dev mode**: Connects to `localhost:3000` (requires server dev running)
|
- **Dev mode**: Connects to `localhost:3000` (requires server dev running)
|
||||||
- **Prod mode**: Embeds server bundle and starts local HTTP server
|
- **Prod mode**: Embeds server bundle and starts local HTTP server
|
||||||
|
- **Config**: `electrobun.config.ts` — app metadata, build entrypoint, platform options
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
@@ -18,8 +19,14 @@ Electrobun desktop shell - loads the server app in a native window.
|
|||||||
bun dev # Start Electrobun dev mode
|
bun dev # Start Electrobun dev mode
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
bun build # Build canary release
|
bun build # Build canary release (current platform)
|
||||||
bun build:stable # Build stable release
|
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)
|
||||||
|
|
||||||
|
# Code Quality
|
||||||
|
bun fix # Biome auto-fix (lint + format)
|
||||||
|
bun typecheck # TypeScript check
|
||||||
```
|
```
|
||||||
|
|
||||||
## Directory Structure
|
## Directory Structure
|
||||||
@@ -28,20 +35,39 @@ bun build:stable # Build stable release
|
|||||||
apps/desktop/
|
apps/desktop/
|
||||||
├── src/
|
├── src/
|
||||||
│ └── bun/
|
│ └── bun/
|
||||||
│ └── index.ts # Electrobun main process entry
|
│ └── index.ts # Main process entry (BrowserWindow + server wait)
|
||||||
├── electrobun.config.ts # Electrobun configuration
|
├── electrobun.config.ts # App name, identifier, version, build config
|
||||||
├── package.json
|
├── package.json
|
||||||
└── tsconfig.json
|
├── tsconfig.json # Extends @furtherverse/tsconfig/bun.json
|
||||||
|
└── turbo.json # Build output config
|
||||||
```
|
```
|
||||||
|
|
||||||
## Development Workflow
|
## Development Workflow
|
||||||
|
|
||||||
1. **Start server dev first**: `cd ../server && bun dev`
|
1. **Start server dev first**: `bun dev` from `apps/server/`
|
||||||
2. **Start Electrobun**: `bun dev` (from apps/desktop/)
|
2. **Start Electrobun**: `bun dev` from `apps/desktop/`
|
||||||
3. Electrobun connects to localhost:3000
|
3. Desktop app polls `localhost:3000` until server responds, then opens window
|
||||||
|
|
||||||
## Electrobun Patterns
|
## Electrobun Patterns
|
||||||
|
|
||||||
|
### Config (`electrobun.config.ts`)
|
||||||
|
|
||||||
|
```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
|
### BrowserWindow
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
@@ -50,16 +76,30 @@ import { BrowserWindow } from 'electrobun/bun'
|
|||||||
new BrowserWindow({
|
new BrowserWindow({
|
||||||
title: 'My App',
|
title: 'My App',
|
||||||
url: 'http://localhost:3000',
|
url: 'http://localhost:3000',
|
||||||
frame: {
|
frame: { x: 100, y: 100, width: 1200, height: 800 },
|
||||||
x: 100,
|
renderer: 'cef',
|
||||||
y: 100,
|
|
||||||
width: 1200,
|
|
||||||
height: 800,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
### Events
|
### Server Readiness Check
|
||||||
|
|
||||||
|
```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
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Application Events
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import Electrobun from 'electrobun/bun'
|
import Electrobun from 'electrobun/bun'
|
||||||
@@ -73,10 +113,12 @@ Electrobun.events.on('will-quit', () => {
|
|||||||
|
|
||||||
**DO:**
|
**DO:**
|
||||||
- Run server dev before desktop dev
|
- Run server dev before desktop dev
|
||||||
- Use `catalog:` for dependencies
|
- Use `catalog:` for dependency versions
|
||||||
- Handle server startup gracefully
|
- Wait for server readiness before opening BrowserWindow
|
||||||
|
- Handle server timeout gracefully (exit with helpful error message)
|
||||||
|
|
||||||
**DON'T:**
|
**DON'T:**
|
||||||
- Use `npm`, `npx`, `node`, `yarn`, `pnpm` — always use `bun` / `bunx`
|
- Use `npm`, `npx`, `node`, `yarn`, `pnpm` — always use `bun` / `bunx`
|
||||||
- Hardcode dependency versions (use catalog)
|
- Hardcode dependency versions (use catalog)
|
||||||
- Block main thread during server wait
|
- Block main thread with synchronous waits
|
||||||
|
- Use `unwrap()`-style patterns without error handling
|
||||||
|
|||||||
Reference in New Issue
Block a user