chore: update gitignore
e33b660e
2 file(s) · +1 −147
| 23 | 23 | *.sln |
|
| 24 | 24 | *.sw? |
|
| 25 | 25 | data |
|
| 26 | + | CLAUDE-2.md |
| 1 | - | # CLAUDE.md |
|
| 2 | - | ||
| 3 | - | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
|
| 4 | - | ||
| 5 | - | ## Project Overview |
|
| 6 | - | ||
| 7 | - | Alcove is an end-to-end encrypted RSS reader built with React, Vite, and Evolu (a local-first CRDT database). The app stores all data locally, syncs encrypted data to a WebSocket relay server, and supports mnemonic-based backup/restore. |
|
| 8 | - | ||
| 9 | - | ## Development Commands |
|
| 10 | - | ||
| 11 | - | ```bash |
|
| 12 | - | # Development server |
|
| 13 | - | npm run dev |
|
| 14 | - | ||
| 15 | - | # Build for production |
|
| 16 | - | npm run build |
|
| 17 | - | ||
| 18 | - | # Type check only |
|
| 19 | - | tsc -b |
|
| 20 | - | ||
| 21 | - | # Lint |
|
| 22 | - | npm run lint |
|
| 23 | - | ||
| 24 | - | # Preview production build |
|
| 25 | - | npm preview |
|
| 26 | - | ``` |
|
| 27 | - | ||
| 28 | - | ## Architecture |
|
| 29 | - | ||
| 30 | - | ### Database & State Management (Evolu) |
|
| 31 | - | ||
| 32 | - | **File:** `src/lib/evolu.ts` |
|
| 33 | - | ||
| 34 | - | Alcove uses Evolu, a local-first CRDT database that provides: |
|
| 35 | - | - End-to-end encrypted sync via WebSocket (`wss://relay.alcove.tools`) |
|
| 36 | - | - Mnemonic-based account backup/restore |
|
| 37 | - | - Soft-delete pattern (uses `isDeleted` flag, never hard deletes) |
|
| 38 | - | - Type-safe React hooks via `createUseEvolu` |
|
| 39 | - | ||
| 40 | - | **Critical Pattern:** Always use soft deletes: |
|
| 41 | - | ```typescript |
|
| 42 | - | // WRONG - breaks CRDT sync |
|
| 43 | - | evolu.delete(table, id); |
|
| 44 | - | ||
| 45 | - | // CORRECT - use isDeleted flag |
|
| 46 | - | evolu.update(table, { id, isDeleted: sqliteTrue }); |
|
| 47 | - | ``` |
|
| 48 | - | ||
| 49 | - | **Database Schema** (`src/lib/scheme.ts`): |
|
| 50 | - | - `rssFeed` - Feed metadata (url, title, description, category) |
|
| 51 | - | - `rssPost` - Individual posts (feedId, title, link, content, author, publishedDate) |
|
| 52 | - | - `readStatus` - Read/unread tracking per post |
|
| 53 | - | - `userPreferences` - User settings (theme, refresh interval) |
|
| 54 | - | ||
| 55 | - | All string fields use branded types with length constraints. Use pre-built queries in `evolu.ts` for type safety. |
|
| 56 | - | ||
| 57 | - | ### RSS Feed Processing |
|
| 58 | - | ||
| 59 | - | **File:** `src/lib/feed-operations.ts` |
|
| 60 | - | ||
| 61 | - | **Feed Discovery Flow:** |
|
| 62 | - | 1. Check if URL looks like direct feed (`looksLikeFeedUrl()`) |
|
| 63 | - | 2. If not, try common paths: `/feed`, `/rss.xml`, `/atom.xml`, etc. (`discoverFeed()`) |
|
| 64 | - | 3. Fetch with CORS proxy fallback (`fetchFeedWithFallback()`) |
|
| 65 | - | 4. Parse XML and detect format: RSS 2.0, Atom 1.0, or RDF (`parseFeedXml()`) |
|
| 66 | - | 5. Extract and sanitize data to match schema constraints |
|
| 67 | - | 6. Insert into database |
|
| 68 | - | ||
| 69 | - | **Content Extraction:** Handles multiple field variations (`content:encoded`, `content`, `description`, `summary`) and format differences (CDATA, nested objects, strings). |
|
| 70 | - | ||
| 71 | - | **IMPORTANT:** Always sanitize feed data with `sanitizeFeedData()` and `sanitizePostData()` before inserting to prevent schema validation errors from truncated strings. |
|
| 72 | - | ||
| 73 | - | ### OPML Import/Export |
|
| 74 | - | ||
| 75 | - | **File:** `src/lib/opml.ts` |
|
| 76 | - | ||
| 77 | - | - **Export:** Groups feeds by category, generates XML with hierarchy, triggers download |
|
| 78 | - | - **Import:** Parses OPML, fetches each feed's XML, extracts posts, inserts with category |
|
| 79 | - | - Progress tracking via Sonner toasts with success/error counts |
|
| 80 | - | ||
| 81 | - | ### Component Structure |
|
| 82 | - | ||
| 83 | - | ``` |
|
| 84 | - | App.tsx |
|
| 85 | - | ├── Initial onboarding (if no feeds) |
|
| 86 | - | ├── Dashboard (main app) |
|
| 87 | - | │ ├── AppSidebar |
|
| 88 | - | │ │ ├── NavFeeds (category-grouped feeds) |
|
| 89 | - | │ │ ├── PostsList (search, filter, read status) |
|
| 90 | - | │ │ └── NavUser (settings, OPML, backup/restore) |
|
| 91 | - | │ └── Post content view (markdown rendering) |
|
| 92 | - | ``` |
|
| 93 | - | ||
| 94 | - | **Mobile Responsiveness:** Uses `useSidebar` hook and `md:` breakpoints. Sidebar toggles between feeds/posts views on mobile. |
|
| 95 | - | ||
| 96 | - | **Markdown Rendering:** Posts use `react-markdown` with `rehype-sanitize` for XSS protection and `rehype-raw` for HTML support. Relative image URLs are converted to absolute. |
|
| 97 | - | ||
| 98 | - | ## Key Patterns |
|
| 99 | - | ||
| 100 | - | ### Type Narrowing |
|
| 101 | - | Always narrow query results to eliminate null fields: |
|
| 102 | - | ```typescript |
|
| 103 | - | const feeds = useQuery(allFeedsQuery).$narrowType(/* ... */); |
|
| 104 | - | ``` |
|
| 105 | - | ||
| 106 | - | ### Search & Filter |
|
| 107 | - | Client-side filtering for instant feedback: |
|
| 108 | - | ```typescript |
|
| 109 | - | const filtered = useMemo(() => { |
|
| 110 | - | return posts.filter(p => p.title.includes(searchQuery)) |
|
| 111 | - | .sort((a, b) => /* date desc */); |
|
| 112 | - | }, [posts, searchQuery]); |
|
| 113 | - | ``` |
|
| 114 | - | ||
| 115 | - | ### Error Handling |
|
| 116 | - | - User-friendly toast messages for errors |
|
| 117 | - | - Console logs for debugging |
|
| 118 | - | - Continue processing on partial failures (e.g., OPML import with some invalid feeds) |
|
| 119 | - | ||
| 120 | - | ### State Management |
|
| 121 | - | - React `useState` for UI state |
|
| 122 | - | - Evolu for persistent, synced data |
|
| 123 | - | - `localStorage` for initialization flags |
|
| 124 | - | ||
| 125 | - | ## Important Files |
|
| 126 | - | ||
| 127 | - | - `src/lib/evolu.ts` - Database setup, queries, hooks |
|
| 128 | - | - `src/lib/scheme.ts` - Database schema and types |
|
| 129 | - | - `src/lib/feed-operations.ts` - RSS fetching and parsing |
|
| 130 | - | - `src/lib/opml.ts` - OPML import/export |
|
| 131 | - | - `src/components/app-sidebar.tsx` - Main navigation |
|
| 132 | - | - `src/components/dashboard.tsx` - Post content view |
|
| 133 | - | - `src/App.tsx` - Root component and feed management |
|
| 134 | - | ||
| 135 | - | ## Path Aliases |
|
| 136 | - | ||
| 137 | - | TypeScript/Vite configured with `@/*` alias mapping to `./src/*`: |
|
| 138 | - | ```typescript |
|
| 139 | - | import { evolu } from "@/lib/evolu"; |
|
| 140 | - | ``` |
|
| 141 | - | ||
| 142 | - | ## Testing Feeds |
|
| 143 | - | ||
| 144 | - | When testing feed discovery or parsing, use these patterns: |
|
| 145 | - | - Common paths: `/feed`, `/rss.xml`, `/atom.xml`, `/rss`, `/feed.xml`, `/index.xml` |
|
| 146 | - | - Formats supported: RSS 2.0, Atom 1.0, RDF/RSS 1.0 |
|
| 147 | - | - CORS proxy: `https://corsproxy.io/?{encodeURIComponent(url)}` |