A desktop RSS reader built on Tauri 2 + Svelte 5, modeled on NetNewsWire. v0 is a working reader; future versions will add EPUB reading in the same interface and an Obsidian-style plugin system.
Prerequisites: Node 18+, Rust 1.77+, and platform-specific Tauri requirements (see docs).
npm install
npm run tauri devThe app launches with a three-pane layout (feeds / articles / reader). Click + in the sidebar to add a feed URL, or Import OPML in the toolbar to bring in an existing subscription list.
- Three-pane NetNewsWire-style layout
- RSS, Atom, and JSON Feed support (via feed-rs)
- Folder-based feed organization
- Read/unread and starred state, persisted in SQLite
- Unread, starred, and all-articles smart filters
- OPML import and export
- Conditional GET (ETag / Last-Modified) for efficient refresh
- Background refresh every 15 minutes
- Keyboard shortcuts:
j/knext/prev,spaceread-and-advance,rrefresh - Automatic light/dark theme following system preference
Plugin system, EPUB reading, cloud sync, full-text search, feed discovery, drag-and-drop feed reorganization. See docs/diary/2026-04-12-project-kickoff.md for the explicit v0 scope.
- Backend (Rust / Tauri):
src-tauri/— SQLite viarusqlite, HTTP viareqwest, feed parsing viafeed-rs. All commands exposed fromsrc-tauri/src/commands.rs. - Frontend (Svelte 5 + TypeScript + Vite):
src/— stores insrc/lib/stores.ts, single IPC entry pointsrc/lib/api.ts, typed event bussrc/lib/bus.ts.
Three "plugin-readiness" patterns are baked in: a command registry, a typed event bus, and slot props on the shell component. No plugin loader exists in v0 — these are shape decisions that cost nothing now and keep the door open later.
Every non-trivial decision in this codebase has a diary entry in docs/diary/. The folder is Obsidian-compatible: point a vault at it and wikilinks, frontmatter, and tags work natively.
New contributors should read docs/diary/README.md first, then the entries in chronological order. Each entry follows a fixed template: Context → Options → Decision → Consequences → Links.
src-tauri/src/
├── main.rs # Binary entry
├── lib.rs # Command registration, plugin init, DB bootstrap
├── commands.rs # #[tauri::command] wrappers
├── models.rs # Folder, Feed, Article, ArticleFilter, MarkAllScope
├── error.rs # AppError
├── state.rs # Managed Mutex<Connection>
├── db/ # schema.sql + queries.rs (all SQL here)
├── feeds/ # fetcher.rs (HTTP) + parser.rs (feed-rs)
└── opml.rs # OPML import/export
src/
├── App.svelte # Three-pane shell, slot hooks, keyboard shortcuts
├── main.ts # Svelte 5 mount entry
├── app.css # Theme variables, base styles
└── lib/
├── api.ts # Only place that calls invoke() — plugin seam
├── types.ts # TS mirror of Rust models
├── stores.ts # Svelte stores + helpers
├── bus.ts # Typed event bus — plugin seam
└── components/
├── FeedList.svelte
├── ArticleList.svelte
├── ArticleReader.svelte
└── Toolbar.svelte
docs/diary/ # Developer diaries (read as an Obsidian vault)
TBD.