Architecture
Build System and Packaging
This document describes how Crab Desktop is built, tested, and packaged for distribution across macOS, Linux, and Windows.
Build Pipeline
The build process has three independent stages that can run in parallel, followed by a packaging step:
Build Commands
| Command | Description |
|---|---|
npm run build:agent | cargo build --release -p crab-desktop-agent |
npm run build:renderer | vite build (React app → dist/) |
npm run build:electron | tsc -p tsconfig.electron.json + rename to .cjs |
npm run build | All three in sequence |
npm run package | Build + electron-builder |
npm run dist | Generate icons + package |
Agent Build
The Rust agent is compiled as a standalone binary:
cargo build --release -p crab-desktop-agent
# Output: target/release/crab-desktop-agentDependencies
The agent links against several native libraries:
better-sqlite3(bundled) — for DuckDB/SQLite previewsopenssl(via rustls) — TLS for forge API callslibc— Unix statvfs for disk-free queries
Cross-Compilation
For multi-platform builds:
- macOS: Universal binary (aarch64 + x86_64) via
lipo - Linux: Built on the target architecture (no cross-compile)
- Windows: Built on Windows (MSVC toolchain)
Renderer Build
Vite builds the React application:
npx vite build
# Output: dist/index.html, dist/assets/*.js, dist/assets/*.cssConfiguration (vite.config.ts)
- Entry:
index.html(referencessrc/main.tsx) - Output:
dist/with content-hashed filenames - Code splitting: Dynamic imports for Monaco, xterm, xyflow
- CSS: Tailwind CSS processed via PostCSS
- Source maps: Generated for development, stripped for production
Key Optimizations
- Tree shaking: Unused exports eliminated
- Chunk splitting: Large dependencies (Monaco, Three.js) in separate chunks loaded on demand
- CSS purging: Tailwind removes unused utility classes
- Asset inlining: Small images/fonts inlined as data URIs
Electron Build
The main process TypeScript is compiled separately:
tsc -p tsconfig.electron.json
node scripts/rename-to-cjs.mjs
# Output: dist-electron/*.cjsThe rename step converts .js → .cjs because Electron's main
process uses CommonJS modules while the project's tsconfig targets
ES modules.
Packaging
electron-builder packages the app for distribution:
Configuration (electron-builder.yml)
appId: com.crab.desktop
productName: Crab Desktop
directories:
output: release
files:
- dist/**/*
- dist-electron/**/*
extraResources:
- from: ../target/release/crab-desktop-agent
to: agent
mac:
target: [dmg, zip]
category: public.app-category.developer-tools
hardenedRuntime: true
entitlements: build/entitlements.mac.plist
linux:
target: [AppImage, deb, rpm]
category: Development
win:
target: [nsis, portable]Platform-Specific Notes
macOS:
- Code signed with Apple Developer ID
- Notarized via
notarytool - Hardened runtime with entitlements for:
- Network access (forge APIs)
- File system access (repo operations)
- Process spawning (agent, terminals, git)
Linux:
- AppImage for universal distribution
.deband.rpmfor package managers- Desktop file and icon registration
Windows:
- NSIS installer with optional per-user install
- Portable
.exealternative - Authenticode code signing
Development Mode
npm run dev # Vite dev server with HMR
npm run dev:electron # Build electron + launch with dev rendererIn development:
- Vite serves the renderer with hot module replacement
- Electron loads from
http://localhost:5173instead ofdist/ - The agent binary is loaded from
target/release/(ortarget/debug/) - Source maps are available for all three layers
Testing
| Command | Framework | Scope |
|---|---|---|
npm run test | Vitest | Unit tests (renderer utilities) |
npm run test:a11y | Playwright + axe-core | Accessibility |
cargo test -p crab-desktop-agent | Cargo | Agent unit tests |
Test Infrastructure
- Vitest: Fast unit tests for renderer utilities (patch parsing, format helpers, state reducers)
- Playwright: E2E tests that launch the full Electron app
- axe-core: Automated accessibility audits
- proptest: Property-based tests for the agent (hunk anchoring, conflict detection)
- fast-check: Property-based tests for renderer (patch parsing)
Continuous Integration
The CI pipeline runs on every pull request:
- Lint:
eslint(renderer) +clippy(agent) - Type check:
tsc --noEmit(renderer + electron) - Unit tests: Vitest + cargo test
- Build: Full production build (all platforms)
- Package: electron-builder (smoke test, no signing)
- Accessibility: Playwright a11y suite
Release Process
- Bump version in
package.json - Update
CHANGELOG.md - Tag the release:
git tag v0.1.0 - CI builds and signs for all platforms
- Upload artifacts to release page
- Auto-update feed updated (Electron's
autoUpdater)