# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Leaflet is a full-stack TypeScript web app for shared writing and social publishing, built on Bluesky (AT Protocol). Users create collaborative documents ("Leaflets") and publish them as blog posts/newsletters ("Publications") that others can follow.

## Tech Stack

- **Frontend**: React 19 + Next.js 16 (App Router, Turbopack)
- **Database**: PostgreSQL via Supabase + Drizzle ORM
- **Real-time sync**: Replicache for optimistic updates and offline support
- **Editor**: ProseMirror with Yjs CRDT
- **Social**: AT Protocol (@atproto packages) for Bluesky integration
- **Jobs**: Inngest for async serverless functions

## Commands

```bash
npm run dev                     # Start Next.js dev server (port 3000)
npm run start-appview-dev       # Start AT Protocol appview service
npm run start-feed-service-dev  # Start feed subscription service
npm run lint                    # ESLint
npx tsc                         # TypeScript type checking (used in CI)
npm run generate-db-types       # Regenerate Supabase types after schema changes
npm run lexgen                  # Regenerate AT Protocol types from lexicons
```

## Architecture

### Data Flow

1. **Client mutations** go through Replicache (`src/replicache/mutations.ts`) for optimistic local updates
2. **Server actions** in `actions/` persist changes to Supabase
3. **CVR sync** reconciles client and server state via Replicache

### Document Model

Documents are composed of **blocks** (text, image, embed, code, poll, etc.). Block components live in `components/Blocks/`. Blocks are rendered in linear lists by `components/Blocks/index.tsx` and in an xy canvas by `components/Canvas.tsx`.

### AT Protocol / Bluesky

- **Lexicons** (`lexicons/`) define schemas for reading and writing records to the AT Protocol network and users' PDSs
- **Appview** (`appview/`) consumes the firehose to index published content
- **Feeds** (`feeds/`) provides subscription feeds for publications

### Lexicon Workflow

The source of truth for lexicon schemas is the TypeScript files in `lexicons/src/` (e.g. `facet.ts`, `publication.ts`, `blocks.ts`). The JSON files in `lexicons/pub/` are **generated output** — do not edit them directly, as `npm run lexgen` will overwrite them.

To add or modify a lexicon:
1. Edit the relevant source file in `lexicons/src/`
2. Run `npm run lexgen` to regenerate both the JSON schemas and the TypeScript API types in `lexicons/api/`

### Key Directories

- `app/` - Next.js App Router pages and API routes
- `actions/` - Server actions (mutations, auth, subscriptions)
- `components/` - React components
- `src/` - Shared utilities, hooks, Replicache setup
- `appview/` - AT Protocol firehose consumer for indexing
- `feeds/` - Feed service for publication subscriptions
- `lexicons/` - AT Protocol schema definitions
- `supabase/migrations/` - Database schema

### Patterns

- **Server actions**: Export async functions with `'use server'` directive, return `Result<T>` from `src/result.ts`
- **Replicache mutations**: Named handlers in `src/replicache/mutations.ts`, keep server mutations idempotent
- **React contexts**: `DocumentProvider`, `LeafletContentProvider` for page-level data
- **Inngest functions**: Async jobs in `app/api/inngest/functions/`
- **Icons**: Icon components live in `components/Icons/`. Each icon is a named export in its own file (e.g. `RefreshSmall.tsx`), imports `Props` from `./Props`, spreads `{...props}` on the `<svg>` element, and uses `fill="currentColor"` instead of hardcoded colors like `fill="black"`.
- **Popovers and menus**: Use the existing `Popover` (`components/Popover`), `Menu`, and `MenuItem` (`components/Menu`) components — do not create new popover/menu primitives. Avoid using buttons inside Popover or Modal triggers unless using a specific button component. If button is being used as trigger, always add an "asChild" prop to the Menu or Popover
