Skip to content

Commit 1d4cfd0

Browse files
committed
docs: add CLAUDE.md, expand .cursorrules, add .env.example for coding agent efficacy
- CLAUDE.md: comprehensive onboarding guide covering project overview, monorepo package roles, prerequisites, dev commands, Electron process model, IPC event system, dual-mode web app, Zustand state architecture, domain concepts, code style conventions, logging/error-handling patterns, testing setup, and known gotchas - .cursorrules: augmented with state management (Zustand slices), IPC event conventions, Effection async patterns, roarr logging, and serialize-error usage - .env.example: documents required Discogs API environment variables https://claude.ai/code/session_019upBEByJrhS932LwBV5q2C
1 parent 14c0dd5 commit 1d4cfd0

3 files changed

Lines changed: 270 additions & 0 deletions

File tree

.cursorrules

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,43 @@ Follow these rules when you write code:
1717
### Static Analysis
1818

1919
You may encounter linter errors when you edit code. Try to resolve them, but don't worry about auto-fixable ESLint errors like sorting imports or whitespace or exhaustive react hooks. Those can be fixed later using linting scripts.
20+
21+
### State Management (raga-web-app)
22+
23+
- State is managed with **Zustand** and split into slices under `src/store/slices/`.
24+
- Use the `createSelectors` utility for memoized subscriptions; avoid subscribing to the
25+
entire store object.
26+
- Add new state domains as a new slice file and compose it into the root store in
27+
`src/store/appStore.ts`.
28+
29+
### IPC Events (raga-app / raga-types)
30+
31+
- All IPC channel names and payload types live in `raga-types`:
32+
- Client events (renderer → server): `packages/raga-types/src/api/clientEvents.ts`
33+
- Server events (server → renderer): `packages/raga-types/src/api/serverEvents.ts`
34+
- Register new channels in the `ClientEventChannel` / `ServerEventChannel` const
35+
objects before implementing handlers.
36+
37+
### Async Patterns (utility process)
38+
39+
- Use **Effection** generator functions for complex async flows that need cancellation
40+
or structured concurrency in the Electron utility process (`raga-app/src/server/`).
41+
- Plain `async/await` is fine for simple one-shot operations.
42+
43+
### Logging
44+
45+
- Use `roarr` scoped loggers — **not** `console.log`:
46+
```ts
47+
import { createLogger } from "../common/logger.js";
48+
const log = createLogger("myModule");
49+
log.debug("message", { context });
50+
```
51+
52+
### Error Handling (IPC)
53+
54+
- Serialize errors before sending them over IPC using `serialize-error`:
55+
```ts
56+
import { serializeError } from "serialize-error";
57+
// include in server event payload:
58+
{ error: serializeError(err) }
59+
```

.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Environment variables for raga-app (Electron desktop app)
2+
# Copy this file to packages/raga-app/.env and fill in your credentials.
3+
4+
# Discogs API credentials — required for the genre lookup feature.
5+
# Obtain a consumer key & secret at https://www.discogs.com/settings/developers
6+
DISCOGS_CONSUMER_KEY=
7+
DISCOGS_CONSUMER_SECRET=

CLAUDE.md

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# Raga — Claude Code Guide
2+
3+
Raga is a music library management suite for DJs. It imports music libraries from
4+
[Swinsian](https://swinsian.com/), analyzes tracks (BPM, metadata), and exports
5+
libraries to formats compatible with [Rekordbox](https://rekordbox.com/) (Pioneer's
6+
industry-standard DJ software). It ships as an Electron desktop app, a portable web
7+
UI, a core Node.js library, and a standalone CLI.
8+
9+
---
10+
11+
## Monorepo Package Overview
12+
13+
This is a Yarn v4 workspace monorepo managed with Nx (task orchestration) and Lerna
14+
(versioning). All packages live under `packages/`.
15+
16+
| Package | Purpose | Published |
17+
|---------|---------|-----------|
18+
| `raga-types` | Shared TypeScript interfaces + IPC event channel definitions | No |
19+
| `raga-lib` | Core data-transformation library (plist parsing, FFmpeg conversion) | Yes (npm) |
20+
| `raga-web-app` | React/Vite UI — runs standalone in browser or embedded in Electron | No |
21+
| `raga-app` | Electron desktop app — wraps raga-web-app, adds IPC + file I/O | No |
22+
| `raga-cli` | CLI tool for Swinsian → Rekordbox batch conversion | No |
23+
24+
**Dependency order:** `raga-types``raga-lib``raga-app` / `raga-cli`; `raga-web-app` uses `raga-types`.
25+
26+
---
27+
28+
## Prerequisites
29+
30+
```bash
31+
node --version # Must be v24.x (see .nvmrc; use `nvm use` or corepack)
32+
yarn --version # v4.x (enabled via `corepack enable`)
33+
ffmpeg -version # Required for audio conversion features
34+
deno --version # v2.x — only needed to build the raga-cli standalone binary
35+
```
36+
37+
Install dependencies:
38+
39+
```bash
40+
corepack enable
41+
yarn install
42+
```
43+
44+
---
45+
46+
## Development Commands
47+
48+
All commands run from the repo root via Nx task orchestration:
49+
50+
```bash
51+
# Start dev servers
52+
yarn dev:web # Vite dev server for raga-web-app at http://localhost:3000
53+
yarn dev:electron # Full Electron app (also launches react-devtools)
54+
55+
# Build
56+
yarn build # TypeScript compilation for all packages (tsc)
57+
yarn dist # Package Electron app as distributable (.dmg / .deb)
58+
59+
# Type checking & linting
60+
yarn check-types # TypeScript type check (all packages)
61+
yarn check-lint # ESLint check (all packages)
62+
yarn fix-lint # Auto-fix ESLint issues
63+
yarn check-format # Prettier format check
64+
yarn fix-format # Auto-format with Prettier
65+
66+
# Testing
67+
yarn test # Run all Vitest suites
68+
```
69+
70+
Run checks before committing: `yarn check-types && yarn check-lint && yarn check-format`.
71+
72+
---
73+
74+
## Architecture
75+
76+
### Electron Process Model
77+
78+
`raga-app` uses three separate OS processes:
79+
80+
1. **Main process** (`src/main.ts`) — creates the browser window, routes IPC messages.
81+
2. **Utility process** (`src/server.ts`) — runs `@tinyhttp/app` HTTP server; handles
82+
CPU-heavy work: plist parsing, FFmpeg conversion, ID3 tag writing, Discogs API calls.
83+
3. **Renderer process** — the compiled `raga-web-app` React bundle.
84+
85+
```
86+
Renderer (React UI)
87+
↕ window.api (context bridge)
88+
Main process
89+
↕ MessageChannelMain / IPC
90+
Utility process (HTTP server on localhost)
91+
```
92+
93+
The preload script (`src/preload.ts`) exposes `window.api` to the renderer via
94+
`contextBridge`. It queues outgoing events until the utility process signals it is
95+
ready (`APP_SERVER_PING` / `APP_SERVER_READY` handshake).
96+
97+
### IPC Event System
98+
99+
Event channels and payloads are defined in `raga-types`:
100+
101+
- **Client events** (renderer → server): `packages/raga-types/src/api/clientEvents.ts`
102+
- **Server events** (server → renderer): `packages/raga-types/src/api/serverEvents.ts`
103+
104+
Channel names use camelCase string literals collected in the `ClientEventChannel` and
105+
`ServerEventChannel` const objects. Always add new events there first before
106+
implementing handlers.
107+
108+
### Web App Dual-Mode
109+
110+
`raga-web-app` detects its host environment at runtime via `window.api`:
111+
112+
- **Electron mode**`window.api` is present; full feature set via IPC.
113+
- **Standalone mode**`window.api` is absent; falls back to `src/webApi.ts` which
114+
provides mock data and no-op stubs (useful for browser-only development).
115+
116+
### State Management
117+
118+
`raga-web-app` uses **Zustand** with an immer + persist middleware stack:
119+
120+
- Root store: `packages/raga-web-app/src/store/appStore.ts`
121+
- State is split into slices under `src/store/slices/` (one file per domain)
122+
- Use the `createSelectors` utility for memoized per-key subscriptions — do not
123+
subscribe to the entire store object
124+
- Persisted state uses localStorage with a schema version key
125+
126+
### Async Patterns (Electron server)
127+
128+
Complex multi-step async operations in the utility process use the **Effection**
129+
generator pattern:
130+
131+
```ts
132+
import { run } from "effection";
133+
yield* run(function* () { /* ... */ });
134+
```
135+
136+
Use Effection for operations that need cancellation, timeouts, or structured
137+
concurrency. Simple one-shot async calls can use plain `async/await`.
138+
139+
---
140+
141+
## Key Domain Concepts
142+
143+
| Term | Meaning |
144+
|------|---------|
145+
| **Swinsian library** | A plist XML file exported from the Swinsian music player containing tracks and playlists |
146+
| **TrackDefinition** | The core track data structure (see `raga-types`); has ~25 fields: Track ID, Persistent ID, Location, BPM, Rating, Artist, Album, Genre, etc. |
147+
| **Persistent ID** | Hex-encoded unique identifier for a track, used by both Swinsian and Music.app/Rekordbox |
148+
| **Music.app / iTunes XML** | The plist format that Rekordbox ingests; raga converts Swinsian libraries into this format |
149+
| **BPM** | Beats per minute; analyzed via `web-audio-beat-detector` in the browser or stored as ID3 metadata |
150+
| **ID3 tags** | Metadata embedded in MP3 files; raga writes them via `node-taglib-sharp` |
151+
| **Audio Files Server** | The HTTP server in the utility process that serves converted MP3s to the web audio player |
152+
153+
---
154+
155+
## Code Style & Conventions
156+
157+
These are enforced by ESLint + Prettier; the CI will catch violations.
158+
159+
### TypeScript
160+
161+
- Strict mode is on everywhere. Avoid `any`.
162+
- Use the `type` keyword for type-only imports: `import type { Foo } from "./foo.js"`.
163+
- Import order is auto-managed by `simple-import-sort` — don't manually reorder imports.
164+
- `@typescript-eslint/consistent-type-imports` is enforced; run `yarn fix-lint` to auto-fix.
165+
166+
### React / UI
167+
168+
- Use **early returns** to reduce nesting.
169+
- Style with **CSS modules using Sass syntax** (`.module.scss` files); never write plain
170+
inline styles or non-module CSS.
171+
- Use the `classNames` package for conditional class names.
172+
- Name event handlers with the `handle` prefix: `handleClick`, `handleKeyDown`.
173+
- Add accessibility attributes on interactive elements: `tabIndex`, `aria-label`,
174+
`onKeyDown` alongside `onClick`.
175+
176+
### Logging
177+
178+
Use `roarr` scoped loggers — **not** `console.log`:
179+
180+
```ts
181+
import { createLogger } from "../common/logger.js";
182+
const log = createLogger("myModule");
183+
log.debug("message", { context });
184+
```
185+
186+
### Error handling across IPC
187+
188+
Serialize errors before sending them over IPC using `serialize-error`:
189+
190+
```ts
191+
import { serializeError } from "serialize-error";
192+
// in server event payload:
193+
{ error: serializeError(err) }
194+
```
195+
196+
---
197+
198+
## Testing
199+
200+
- Framework: **Vitest** (raga-web-app, raga-lib)
201+
- Test files are colocated next to source files as `*.test.ts` or `*.test.tsx`
202+
- Run all tests: `yarn test`
203+
- Run tests in watch mode (from web-app package): `yarn workspace @adahiya/raga-web-app test --watch`
204+
205+
---
206+
207+
## Known Gotchas
208+
209+
- **`skipLibCheck: true` in raga-app** — there are pre-existing third-party type
210+
conflicts in the Electron package. Do not attempt to resolve them; this is intentional.
211+
- **ffmpeg must be on PATH** — the audio conversion feature calls the system `ffmpeg`
212+
binary. Install it separately (e.g., `brew install ffmpeg`).
213+
- **Discogs API credentials** — genre lookup requires `DISCOGS_CONSUMER_KEY` and
214+
`DISCOGS_CONSUMER_SECRET` in `packages/raga-app/.env`. See `.env.example` at the
215+
repo root.
216+
- **glide-data-grid is canvas-based** — the track table (`TrackTable` component) uses
217+
`@glideapps/glide-data-grid` which renders to a `<canvas>` element, not DOM rows.
218+
Do not try to inspect or manipulate individual row DOM nodes.
219+
- **Node 24 required** — the project uses Node 24 APIs. The GitHub Actions workflow
220+
currently pins Node 20 (a known discrepancy); CircleCI uses the correct `cimg/node:24.12`.
221+
- **IPC event queuing** — the preload script queues renderer → server events until the
222+
`APP_SERVER_READY` signal arrives. If you add a new startup flow, be aware events
223+
sent before ready will be buffered and replayed.

0 commit comments

Comments
 (0)