# AGENTS.md This document is designed to help AI Agents better understand and modify the Jumble project. ## Project Overview Jumble is a user-friendly Nostr client for exploring relay feeds. - **Project Name**: Jumble - **Main Tech Stack**: React 18 + TypeScript + Vite - **UI Framework**: Tailwind CSS + Radix UI - **State Management**: Jotai - **Core Protocol**: Nostr (using nostr-tools) ## Technical Architecture ### Core Dependencies - **Build Tool**: Vite 5.x - **Frontend Framework**: React 18.3.x + TypeScript - **Styling Solution**: - Tailwind CSS (primary styling framework) - Radix UI (unstyled component library) - next-themes (theme management) - tailwindcss-animate (animations) - **State Management**: Jotai 2.x - **Routing**: path-to-regexp (custom routing solution) - **Rich Text Editor**: TipTap 2.x - **Nostr Protocol**: nostr-tools 2.x - **Other Key Libraries**: - i18next (internationalization) - dayjs (date handling) - flexsearch (search) - qr-code-styling (QR codes) - yet-another-react-lightbox (image viewer) ### Project Structure ``` jumble/ ├── src/ │ ├── components/ # React components │ │ ├── ui/ # Base UI components (shadcn/ui style) │ │ └── ... # Other feature components │ ├── providers/ # React Context Providers │ ├── services/ # Business logic service layer │ ├── hooks/ # Custom React Hooks │ ├── lib/ # Utility functions and libraries │ ├── types/ # TypeScript type definitions │ ├── pages/ # Page components | | ├── primary # Primary page components (Left column) │ │ └── secondary # secondary page components (Right column) │ ├── layouts/ # Layout components │ ├── i18n/ # Internationalization resources | | ├── locales # Localization files │ │ └── index.tx # Basic i18n setup │ ├── assets/ # Static assets │ ├── App.tsx # App root component │ ├── PageManager.tsx # Page manager (custom routing logic) │ ├── routes # Route configuration | | ├── primary.tsx # Primary routes (Left column) │ │ └── secondary.tsx # Secondary routes (Right column) │ └── constants.ts # Constants definition ├── public/ # Public static assets └── resources/ # Design resources ``` ## Development Guide ### Environment Setup ### Environment Setup ```bash # Install dependencies npm install # Start development server npm run dev # Build for production npm run build # Lint code npm run lint # Format code npm run format ``` ## Code Conventions ### Component Development 1. **Component Structure**: Each major feature component is typically in its own folder, containing index.tsx and related sub-components 2. **Styling**: Use Tailwind CSS utility classes, complex components can use class-variance-authority (cva) 3. **Type Safety**: All components should have explicit TypeScript type definitions 4. **State Management**: - Use Jotai atoms for global state management - Use Context Providers for cross-component data ### Service Layer (Services) Service files located in `src/services/` encapsulate business logic: - `client.service.ts` - Nostr client core logic for fetching and publishing events - `indexed-db.service.ts` - IndexedDB data storage - `local-storage.service.ts` - LocalStorage management - `media-upload.service.ts` - Media upload service - `translation.service.ts` - Translation service - `lightning.service.ts` - Lightning Network integration - `relay-info.service.ts` - Relay information management - `blossom.service.ts` - Blossom integration - `custom-emoji.service.ts` - Custom emoji management - `libre-translate.service.ts` - LibreTranslate API integration - `media-manager.service.ts` - Managing media play state - `modal-manager.service.ts` - Managing modal stack for back navigation (ensures modals close one by one before actual page navigation) - `note-stats.service.ts` - Note statistics storage and retrieval (likes, zaps, reposts) - `poll-results.service.ts` - Poll results storage and retrieval - `post-editor-cache.service.ts` - Caching post editor content to prevent data loss - `web.push.service.ts` - Web metadata fetching for link previews ### Providers Architecture The app uses a multi-layered Provider nesting structure (see `App.tsx`): ``` ScreenSizeProvider └─ UserPreferencesProvider └─ ThemeProvider └─ ContentPolicyProvider └─ NostrProvider └─ ... (more providers) ``` Pay attention to Provider dependencies when modifying functionality. And some Providers are placed in `PageManager.tsx` because they need to use the `usePrimaryPage` and `useSecondaryPage` hooks. ### Routing System - Route configuration in `src/routes/primary.tsx` and `src/routes/secondary.tsx` - Using `PageManager.tsx` to manage page navigation, rendering, and state. Normally, you don't need to modify this file. - Primary pages (left column) use key-based navigation - Secondary pages (right column) use path-based navigation with stack support - More details in "Adding a New Page" section below ### Internationalization (i18n) - Translation files located in `src/i18n/locales/` - Using `react-i18next` for internationalization - Supported languages: ar, de, en, es, fa, fr, hi, hu, it, ja, ko, pl, pt-BR, pt-PT, ru, th, zh #### Adding New Language 1. Create a new file in `src/i18n/locales/` with the language code (e.g., `th.ts` for Thai) 2. According to `src/i18n/locales/en.ts`, add translation key-value pairs 3. Update `src/i18n/index.ts` to include the new language resource 4. Update `detectLanguage` function in `src/lib/utils.ts` to support detecting the new language ## Nostr Protocol Integration ### Core Concepts - **Events**: Nostr events (notes, profile updates, etc.). All data in Nostr is represented as events. They have different kinds (kinds) to represent different types of data. - **Relays**: Relay servers, which are WebSocket servers that store and forward Nostr events. - **NIPs**: Nostr Implementation Proposals ### Supported Event Kinds I mean kinds that are supported to be displayed in the feed. - Kind 1: Short Text Note - Kind 6: Repost - Kind 20: Picture Note - Kind 21: Video Note - Kind 22: Short Video Note - Kind 1068: Poll - Kind 1111: Comment - Kind 1222: Voice Note - Kind 1244: Voice Comment - Kind 9802: Highlight - Kind 30023: Long-Form Article - Kind 31987: Relay Review - Kind 34550: Community Definition - Kind 30311: Live Event - Kind 39000: Group Metadata More details you can find in `src/components/Note/`. If you want to add support for new kinds, you need to create new components under `src/components/Note/` and update `src/components/Note/index.tsx`. Please avoid modifying the framework, such as avatars, usernames, timestamps, and action buttons in the `Note` component. Only add content rendering logic for new types. ## Common Modification Scenarios ### Adding a New Component 1. Create a component folder in `src/components/` 2. Create `index.tsx` and necessary sub-components 3. Write styles using Tailwind CSS 4. If needed, add base UI components in `src/components/ui/` ### Adding a New Page #### Adding a Primary Page (Left Column) Primary pages are the main navigation pages that appear in the left column (or full screen on mobile). 1. **Create the page component**: ```bash # Create a new folder under src/pages/primary/ mkdir src/pages/primary/YourNewPage ``` 2. **Implement the component** (`src/pages/primary/YourNewPage/index.tsx`): ```tsx import PrimaryPageLayout from '@/layouts/PrimaryPageLayout' import { TPageRef } from '@/types' import { forwardRef } from 'react' const YourNewPage = forwardRef((_, ref) => { return ( }> {/* Your page content */} ) }) export default YourNewPage ``` **Important**: - Primary pages MUST use `forwardRef` - Wrap content with `PrimaryPageLayout` - The ref is used by PageManager for navigation control 3. **Register the route** in `src/routes/primary.tsx`: ```tsx import YourNewPage from '@/pages/primary/YourNewPage' const PRIMARY_ROUTE_CONFIGS: RouteConfig[] = [ // ... existing routes { key: 'yourNewPage', component: YourNewPage } ] ``` 4. **Navigate to the page** using the `usePrimaryPage` hook: ```tsx import { usePrimaryPage } from '@/PageManager' const { navigate } = usePrimaryPage() navigate('yourNewPage') ``` #### Adding a Secondary Page (Right Column) Secondary pages appear in the right column (or full screen on mobile) and support stack-based navigation. 1. **Create the page component**: ```bash # Create a new folder under src/pages/secondary/ mkdir src/pages/secondary/YourNewPage ``` 2. **Implement the component** (`src/pages/secondary/YourNewPage/index.tsx`): ```tsx import SecondaryPageLayout from '@/layouts/SecondaryPageLayout' import { forwardRef } from 'react' const YourNewPage = forwardRef(({ index }: { index?: number }, ref) => { return ( {/* Your page content */} ) }) export default YourNewPage ``` **Important**: - Secondary pages receive an `index` prop for stack navigation - Use `SecondaryPageLayout` for consistent styling - The ref enables navigation control 3. **Register the route** in `src/routes/secondary.tsx`: ```tsx import YourNewPage from '@/pages/secondary/YourNewPage' const SECONDARY_ROUTE_CONFIGS = [ // ... existing routes { path: '/your-path/:id', element: } ] ``` Add the corresponding path generation function in `src/lib/link.ts` for the new route: ```tsx export const toYourNewPage = (id: string) => `/your-path/${id}` ``` 4. **Navigate to the page**: ```tsx import { useSecondaryPage } from '@/PageManager' import { toYourNewPage } from '@/lib/link' const { push, pop } = useSecondaryPage() // Navigate to new page push(toYourNewPage('some-id')) // Navigate back pop() ``` 5. **Access route parameters**: ```tsx const YourNewPage = forwardRef(({ id, index }: { id?: string; index?: number }, ref) => { console.log('Route param id:', id) // ... }) ``` #### Key Differences | Aspect | Primary Pages | Secondary Pages | | -------------- | ----------------------------------- | ------------------------------- | | **Location** | Left column (main navigation) | Right column (detail view) | | **Navigation** | Replace-based (`navigate`) | Stack-based (`push`/`pop`) | | **Layout** | `PrimaryPageLayout` | `SecondaryPageLayout` | | **Routes** | Key-based (e.g., 'home', 'explore') | Path-based (e.g., '/notes/:id') | On mobile devices or single-column layouts, primary pages occupy the full screen, while secondary pages are accessed via stack navigation. When navigating to another primary page, it will clear the secondary page stack. ### Adding New State Management 1. For global state, create a new Provider in `src/providers/` 2. Add Provider in `App.tsx` in the correct dependency order Or create a singleton service in `src/services/` and use Jotai atoms for state management. ### Adding New Business Logic 1. Create a new service file in `src/services/` 2. Export singleton instance 3. Import and use in anywhere needed ### Style Modifications - Global styles: `src/index.css` - Tailwind configuration: `tailwind.config.js` - Component styles: Use Tailwind class names directly