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

3.6 KiB

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