Ledger is an Electron application built with React. It follows the standard Electron architecture with main and renderer processes.
┌─────────────────────────────────────────────────────────────┐
│ Main Process │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ main.ts │ │ lib/services/ │ │
│ │ (app lifecycle)│ │ (git ops) │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ┌────────┴────────────────────┴────────┐ │
│ │ lib/conveyor/handlers/ (IPC+Zod) │ │
│ └──────────────────────────────────────┘ │
└─────────────────────┬───────────────────────────────────────┘
│ IPC (conveyor: typed + validated)
┌─────────────────────┴───────────────────────────────────────┐
│ Preload Script │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ preload.ts - exposes electronAPI + conveyor │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────┬───────────────────────────────────────┘
│ contextBridge
┌─────────────────────┴───────────────────────────────────────┐
│ Renderer Process │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ app.tsx - React UI │ │
│ │ - State management (useState, useMemo) │ │
│ │ - Branch/Worktree/PR display │ │
│ │ - Filter/Sort controls │ │
│ │ - Context menus │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
ledger/
├── app/ # Renderer process (React)
│ ├── app.tsx # Main React component
│ ├── components/ # UI components
│ ├── styles/ # CSS styles
│ └── types/ # TypeScript declarations
│
├── lib/ # Main process code
│ ├── main/ # App entry, lifecycle
│ ├── services/ # Git operations (pure functions)
│ │ ├── branch/ # Branch operations
│ │ ├── commit/ # Commit operations
│ │ ├── stash/ # Stash operations
│ │ └── ... # Other domains
│ ├── conveyor/ # Typed IPC system
│ │ ├── schemas/ # Zod validation schemas
│ │ ├── handlers/ # IPC handlers
│ │ └── api/ # Renderer-side API
│ └── preload/ # Context bridge
│
├── tests/ # Playwright E2E tests
├── out/ # Build output (generated)
├── dist/ # Distribution files (generated)
└── resources/ # Build resources (icons, etc.)
| File | Purpose |
|---|---|
lib/main/main.ts |
App lifecycle, handler registration |
lib/services/* |
Git operations as pure functions |
lib/conveyor/handlers/* |
IPC handlers with Zod validation |
lib/main/settings-service.ts |
Read/write settings to disk |
| File | Purpose |
|---|---|
app/app.tsx |
Main React component, all UI logic |
app/styles/app.css |
All styling (CSS variables for theming) |
app/types/electron.d.ts |
TypeScript types for IPC API |
| File | Purpose |
|---|---|
lib/preload/preload.ts |
Exposes window.electronAPI + window.conveyor |
- User selects repo →
selectRepo()→ IPC →dialog.showOpenDialog()→ saves path - App loads →
loadSavedRepo()→ IPC → reads settings →setRepoPath() - Refresh →
refresh()→ parallel IPC calls:getBranchesWithMetadata()→ git branch + metadatagetWorktrees()→ git worktree listgetPullRequests()→ gh CLI
- User action (double-click, context menu) → IPC → git operation → refresh
IPC uses the conveyor system: typed channels with Zod validation.
Canonical sources:
lib/conveyor/schemas/- Channel definitions with Zod schemaslib/conveyor/handlers/- Handler implementationslib/conveyor/api/- Renderer-side typed API
To add a new IPC channel:
- Add pure function to
lib/services/(e.g.,branch-service.ts) - Add Zod schema in
lib/conveyor/schemas/ - Add handler in
lib/conveyor/handlers/ - Add API method in
lib/conveyor/api/ - Call via
window.conveyor.*from renderer
The app uses React's built-in hooks for most UI state:
useStatefor simple state (repoPath, branches, loading, etc.)useMemofor derived state (filtered/sorted branches)useCallbackfor memoized handlersuseEffectfor side effects (load repo on mount, auto-dismiss toasts)
For shared UI concerns (active panels, plugin navigation, persisted UI preferences), a small shared store keeps multiple components in sync and supports optional persistence. This keeps local state lightweight while still allowing cross-panel coordination when needed.