forked from coracle/flotilla
Add AGENTS.md
This commit is contained in:
@@ -0,0 +1,254 @@
|
||||
## Project Overview
|
||||
|
||||
Flotilla is a Nostr "relays as groups" community chat client. It implements NIP-29 (relay-based groups) to create Discord-like spaces (servers) and rooms (channels).
|
||||
|
||||
**Tech Stack:**
|
||||
|
||||
- SvelteKit 5.48+ with TypeScript 5.9+
|
||||
- Capacitor for cross-platform (Web/PWA, Android, iOS)
|
||||
- TailwindCSS + DaisyUI for styling
|
||||
- Welshman library suite for Nostr protocol
|
||||
- IndexedDB for local storage
|
||||
- Vite for building
|
||||
|
||||
**Key Concepts:**
|
||||
|
||||
- **Spaces** - Relays used as community groups (like Discord servers)
|
||||
- **Rooms** - NIP-29 groups within spaces (like Discord channels), identified by `h`
|
||||
- **Chats** - Direct message conversations (NIP-04/NIP-44 encrypted)
|
||||
|
||||
## Architecture & Dependency Graph
|
||||
|
||||
The project follows a **strict acyclic dependency hierarchy**:
|
||||
|
||||
```
|
||||
routes/ (top layer - can depend on anything)
|
||||
↓
|
||||
app/components/ (can depend on app/* and lib/*)
|
||||
↓
|
||||
app/core/ & app/util/ (can only depend on lib/*)
|
||||
↓
|
||||
lib/ (can only depend on external libraries)
|
||||
↓
|
||||
external libraries (bottom layer)
|
||||
```
|
||||
|
||||
**Import Ordering Convention (CRITICAL):**
|
||||
Always sort imports by dependency level:
|
||||
|
||||
1. Third-party libraries first
|
||||
2. Then `lib/` imports
|
||||
3. Then `app/` imports
|
||||
|
||||
Example:
|
||||
|
||||
```typescript
|
||||
import {derived} from "svelte/store"
|
||||
import {throttle} from "throttle-debounce"
|
||||
import {Dialog} from "$lib/components"
|
||||
import {repository} from "$app/core/state"
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── lib/ # Generic reusable code
|
||||
│ ├── components/ # 38 UI components (Button, Dialog, etc.)
|
||||
│ ├── html.ts # DOM utilities
|
||||
│ ├── indexeddb.ts # IndexedDB helpers
|
||||
│ └── util.ts # Generic utilities
|
||||
│
|
||||
├── app/
|
||||
│ ├── core/
|
||||
│ │ ├── state.ts # State management, stores, constants (687 lines)
|
||||
│ │ ├── commands.ts # Publishing events and other write operations (440+ lines)
|
||||
│ │ ├── requests.ts # Loading data from network (191 lines)
|
||||
│ │ ├── sync.ts # Data synchronization (296 lines)
|
||||
│ │ └── storage.ts # IndexedDB setup
|
||||
│ │
|
||||
│ ├── util/
|
||||
│ │ ├── notifications.ts # Push notifications (731 lines)
|
||||
│ │ ├── policies.ts # Relay policies
|
||||
│ │ ├── routes.ts # Routing helpers
|
||||
│ │ ├── modal.ts # Modal management
|
||||
│ │ ├── toast.ts # Toast notifications
|
||||
│ │ ├── theme.ts # Theme switching
|
||||
│ │ └── keyboard.ts # Keyboard handling
|
||||
│ │
|
||||
│ ├── editor/ # Rich text editor config
|
||||
│ │ ├── index.ts # TipTap setup with Nostr integration
|
||||
│ │ ├── EditorContent.svelte
|
||||
│ │ └── MentionNodeView.ts
|
||||
│ │
|
||||
│ └── components/ # 188 app-specific components
|
||||
│ ├── Space*.svelte # Space/relay management
|
||||
│ ├── Room*.svelte # Room/channel management
|
||||
│ ├── Chat*.svelte # Direct messaging
|
||||
│ ├── Profile*.svelte # User profiles
|
||||
│ ├── Thread*.svelte # Threaded posts
|
||||
│ └── ...
|
||||
│
|
||||
├── routes/ # SvelteKit file-based routing
|
||||
│ ├── +layout.svelte # Root layout (sync logic here)
|
||||
│ ├── spaces/ # Space management
|
||||
│ │ └── [relay]/ # Specific space
|
||||
│ │ ├── chat/ # Space chat
|
||||
│ │ ├── threads/ # Thread posts
|
||||
│ │ ├── calendar/ # Events
|
||||
│ │ └── [h]/ # Specific room (h = room id)
|
||||
│ ├── chat/ # Direct messages
|
||||
│ ├── settings/ # User settings
|
||||
│ └── [bech32]/ # Bech32 entity viewer
|
||||
│
|
||||
├── assets/icons/ # ~1,277 SVG icons
|
||||
├── app.html # HTML template
|
||||
├── app.css # Global styles
|
||||
└── types.d.ts # Type definitions
|
||||
```
|
||||
|
||||
## State Management
|
||||
|
||||
**Core Principles:**
|
||||
|
||||
- Use Svelte 4 **stores** for all state (NOT runes outside UI components)
|
||||
- Most global state flows through Welshman's `repository` (unidirectional)
|
||||
- Query state using `deriveEventsMapped` or `deriveProfile` etc
|
||||
- Update state by publishing events via `publishThunk`
|
||||
|
||||
**Thunks:**
|
||||
|
||||
- Reduce UI latency by handling signatures and sending in background
|
||||
- Return status that should be displayed to user
|
||||
- Allow cancellation and error handling
|
||||
- Immediately publish to local repository for optimistic updates
|
||||
|
||||
## Nostr Integration
|
||||
|
||||
**Welshman Library Suite:**
|
||||
|
||||
- `@welshman/app` - High-level state (pubkey, signer, repository, tracker)
|
||||
- `@welshman/net` - Network layer (Pool, Socket, load, pull, request)
|
||||
- `@welshman/store` - Svelte integration (deriveEventsMapped, etc.)
|
||||
- `@welshman/util` - Event utilities (kinds, tags, validation)
|
||||
- `@welshman/signer` - Signing abstraction (NIP-01, NIP-07, NIP-46)
|
||||
- `@welshman/router` - Relay routing (inbox/outbox model)
|
||||
- `@welshman/editor` - Rich text editor with Nostr
|
||||
- `@welshman/content` - Content parsing
|
||||
- `@welshman/feeds` - Feed management
|
||||
|
||||
**Key NIPs Implemented:**
|
||||
|
||||
- NIP-01: Basic protocol
|
||||
- NIP-44/59/17: Encrypted DMs
|
||||
- NIP-07: Browser extension signing
|
||||
- NIP-19: Bech32 encoding
|
||||
- NIP-29: Relay-based Groups
|
||||
- NIP-42: Relay authentication
|
||||
- NIP-43: Relay membership
|
||||
- NIP-46: Nostr Connect (remote signing)
|
||||
- NIP-57: Lightning Zaps
|
||||
|
||||
## Development Conventions
|
||||
|
||||
**Component Parameterization:**
|
||||
|
||||
- Only pass entity identifiers (`url` for spaces, `h` for rooms)
|
||||
- Derive all other data inside the component from identifiers
|
||||
- Example: Don't pass `members` prop, derive it from `h` inside component
|
||||
|
||||
**Code Style:**
|
||||
|
||||
- **No `null`** - only use `undefined`
|
||||
- Svelte 5 runes (`$state`, `$derived`, `$effect`) only in UI components
|
||||
- TailwindCSS and DaisyUI styling
|
||||
- Only add comments for really weird stuff
|
||||
- Do not call functions in components unless a parameter is reactive. Instead, use a svelte store or rune to make it reactive.
|
||||
- Do not use `any`. If there are type errors related to `unknown`, they are likely because the upstream definition of the data is incorrect.
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Adding a New Component
|
||||
|
||||
1. Determine if it's generic (`lib/components/`) or app-specific (`app/components/`)
|
||||
2. Follow naming convention: `PascalCase.svelte`
|
||||
3. Import in dependency order (3rd party → lib → app)
|
||||
4. Use stores for state, runes only for UI reactivity
|
||||
|
||||
### Creating a New Route
|
||||
|
||||
1. Add to `src/routes/` following SvelteKit conventions
|
||||
2. Use `+page.svelte` for page component
|
||||
3. Use `+layout.svelte` for shared layouts
|
||||
4. Top-level sync logic goes in root `+layout.svelte`
|
||||
|
||||
### Loading Data from Network
|
||||
|
||||
1. Use utilities from `app/core/requests.ts`
|
||||
2. Or create derived stores in `app/core/state.ts`
|
||||
3. Use `load`, `pull`, or `request` from `@welshman/net`
|
||||
|
||||
### Publishing Events
|
||||
|
||||
1. Create `make*` function to build event template
|
||||
2. Create `publish*` function using `publishThunk`
|
||||
3. Display thunk status to user (for cancel/error handling)
|
||||
4. These go in in `app/core/commands.ts`
|
||||
|
||||
### Managing Modals/Toasts
|
||||
|
||||
- Import from `app/util/modal.ts` or `app/util/toast.ts`
|
||||
- Pass component objects with parameters
|
||||
- Use `$state.snapshot` if calling component might unmount
|
||||
|
||||
## Development Workflow
|
||||
|
||||
Agents should not run the dev server or build the app. Instead, use the following commands:
|
||||
|
||||
```bash
|
||||
pnpm run format # Format changed files
|
||||
pnpm run lint # Check formatting and linting
|
||||
pnpm run check # Type check
|
||||
```
|
||||
|
||||
**Welshman Development:**
|
||||
|
||||
- Clone welshman to parent directory
|
||||
- Use `./link_deps` script to link local welshman packages
|
||||
- Avoid committing `pnpm.overrides` changes
|
||||
|
||||
**Git Workflow:**
|
||||
|
||||
- `master` branch auto-deploys to production
|
||||
- Work on feature branches based on `dev` branch
|
||||
- Pre-commit hooks run lint/typecheck automatically
|
||||
|
||||
## Environment Variables
|
||||
|
||||
See `.env.template` for all options.
|
||||
|
||||
## Important Files to Reference
|
||||
|
||||
- **src/app/core/state.ts** - All stores and constants
|
||||
- **src/app/core/sync.ts** - Data synchronization
|
||||
- **src/app/core/requests.ts** - Utilities for requesting data
|
||||
- **src/app/core/commands.ts** - Publishing patterns
|
||||
- **src/app/util/notifications.ts** - Notification badges and push notifications
|
||||
- **src/routes/+layout.svelte** - Top-level sync logic
|
||||
|
||||
## Mobile Development
|
||||
|
||||
**Capacitor Integration:**
|
||||
|
||||
- Android: Full support, APK builds via `pnpm run release:android`
|
||||
- iOS: Full support (zaps disabled due to App Store policy)
|
||||
- PWA: Progressive Web App with service worker
|
||||
|
||||
**Native Features:**
|
||||
|
||||
- Push notifications (FCM/APNs)
|
||||
- Deep linking (nostr: and https: URLs)
|
||||
- Native signing plugin
|
||||
- Keyboard management
|
||||
- Safe area handling
|
||||
- Badge management
|
||||
Reference in New Issue
Block a user