chore: update gitignore e33b660e
Steve · 2025-11-21 19:41 2 file(s) · +1 −147
.gitignore +1 −0
23 23
*.sln
24 24
*.sw?
25 25
data
26 +
CLAUDE-2.md
CLAUDE.md (deleted) +0 −147
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)}`