Files
grimoire/CLAUDE.md
2025-12-11 12:40:29 +01:00

87 lines
3.6 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
Grimoire is a Nostr protocol explorer and developer tool. It's a tiling window manager interface where each window is a Nostr "app" (profile viewer, event feed, NIP documentation, etc.). Commands are launched Unix-style via Cmd+K palette.
**Stack**: React 19 + TypeScript + Vite + TailwindCSS + Jotai + Dexie + Applesauce
## Core Architecture
### Dual State System
**UI State** (`src/core/state.ts` + `src/core/logic.ts`):
- Jotai atom persisted to localStorage
- Pure functions for all mutations: `(state, payload) => newState`
- Manages workspaces, windows, layout tree, active account
**Nostr State** (`src/services/event-store.ts`):
- Singleton `EventStore` from applesauce-core
- Single source of truth for all Nostr events
- Reactive: components subscribe via hooks, auto-update on new events
- Handles replaceable events automatically (profiles, contact lists, etc.)
**Critical**: Don't create new EventStore or RelayPool instances - use the singletons in `src/services/`
### Window System
Windows are rendered in a recursive binary split layout (via `react-mosaic-component`):
- Each window has: `id` (UUID), `appId` (type identifier), `title`, `props`
- Layout is a tree: leaf nodes are window IDs, branch nodes split space
- **Never manipulate layout tree directly** - use callbacks from mosaic
Workspaces are virtual desktops, each with its own layout tree.
### Command System
`src/types/man.ts` defines all commands as Unix man pages:
- Each command has an `appId` (which app to open) and `argParser` (CLI → props)
- Parsers can be async (e.g., resolving NIP-05 addresses)
- Command pattern: user types `profile alice@example.com` → parser resolves → opens ProfileViewer with props
### Reactive Nostr Pattern
Applesauce uses RxJS observables for reactive data flow:
1. Events arrive from relays → added to EventStore
2. Queries/hooks subscribe to EventStore observables
3. Components re-render automatically when events update
4. Replaceable events (kind 0, 3, 10000-19999, 30000-39999) auto-replace older versions
Use hooks like `useProfile()`, `useNostrEvent()`, `useTimeline()` - they handle subscriptions.
## Key Conventions
- **Path Alias**: `@/` = `./src/`
- **Styling**: Tailwind + HSL CSS variables (theme tokens defined in `index.css`)
- **Types**: Prefer types from `applesauce-core`, extend in `src/types/` when needed
- **File Organization**: By domain (`nostr/`, `ui/`, `services/`, `hooks/`, `lib/`)
- **State Logic**: All UI state mutations go through `src/core/logic.ts` pure functions
## Important Patterns
**Adding New Commands**:
1. Add entry to `manPages` in `src/types/man.ts`
2. Create parser in `src/lib/*-parser.ts` if argument parsing needed
3. Create viewer component for the `appId`
4. Wire viewer into window rendering (`WindowTitle.tsx`)
**Working with Nostr Data**:
- Event data comes from singleton EventStore (reactive)
- Metadata cached in Dexie (`src/services/db.ts`) for offline access
- Active account stored in Jotai state, synced via `useAccountSync` hook
- Use inbox/outbox relay pattern for user relay lists
**Mosaic Layout**:
- Layout mutations via `updateLayout()` callback only
- Don't traverse or modify layout tree manually
- Adding/removing windows handled by `logic.ts` functions
## Critical Notes
- React 19 features in use (ensure compatibility)
- LocalStorage persistence has quota handling built-in
- Dark mode is default (controlled via HTML class)
- EventStore handles event deduplication and replaceability automatically