<p align="center">
<pre>
ββββββββββββββββββββββββββββ βββββββ
ββββββββββββββββββββββββββββββββββββββ
ββββββ ββββββ ββββββ βββ ββββββ βββ
ββββββ ββββββ ββββββ βββ ββββββ βββ
βββ βββββββββββββββββββββββββββββββ
βββ βββββββββββββββββββββ βββββββ
</pre>
<br>
<b>(βα΄₯β) Your terminal RSS companion</b>
<br><br>
<a href="https://ricardodantas.github.io/feedo/">π Website</a> β’
<a href="#installation">Installation</a> β’
<a href="#features">Features</a> β’
<a href="#usage">Usage</a> β’
<a href="#configuration">Configuration</a> β’
<a href="#keybindings">Keybindings</a>
</p>
---
A **beautiful**, **fast**, and **modern** terminal RSS reader built with Rust.
Think [Reeder](https://reederapp.com/) meets the command line.
<br>
## πΈ Screenshots
### Main View

*Three-panel layout: feeds, articles, and content preview with Dracula theme*
### Search

*Real-time search across all your feeds*
### Add Feed

*Auto-discover RSS/Atom feeds from any URL*
<br>
## β¨ Features
| π¨ **Beautiful TUI** | Clean three-panel interface with rounded borders and smooth navigation |
| π **Feed Discovery** | Auto-detect RSS/Atom feeds from any URL β just paste a website |
| π΄ **Offline Mode** | Articles cached locally β read without internet, read states persist |
| βοΈ **Cloud Sync** | Sync with FreshRSS, Miniflux, Inoreader via Google Reader API |
| π **Smart Folders** | Organize feeds into collapsible folders with custom emoji icons |
| π **Instant Search** | Find articles across all feeds with real-time filtering |
| π **15 Themes** | Dracula, Nord, Catppuccin, Gruvbox, Tokyo Night, Solarized, and more |
| π₯ **OPML Support** | Import/export subscriptions for easy migration |
| π€ **Social Sharing** | Share articles to X, Mastodon, and Bluesky with one keypress |
| β‘ **Blazingly Fast** | Async feed fetching with Tokio β no UI blocking |
| π¦ **Memory Safe** | Written in 100% safe Rust with zero unsafe code |
| π **Cross-Platform** | Linux, macOS, Windows β same config path everywhere |
## π Installation
### Homebrew (macOS/Linux)
```bash
brew install ricardodantas/tap/feedo
```
### Cargo (All Platforms)
```bash
cargo install feedo
```
### From Source
```bash
# Clone the repository
git clone https://github.com/ricardodantas/feedo.git
cd feedo
# Build with optimizations
cargo build --release
# Run it!
./target/release/feedo
```
### Pre-built Binaries
Download from [GitHub Releases](https://github.com/ricardodantas/feedo/releases) β available for Linux (x64, ARM64, musl), macOS (Intel, Apple Silicon), and Windows.
### Requirements
- A terminal with Unicode support
- That's it!
<br>
## π Usage
### Basic Commands
```bash
# Launch the TUI
feedo
# Import feeds from another reader
feedo --import subscriptions.opml
# Backup your feeds
feedo --export backup.opml
# Show help
feedo --help
```
### Sync Commands
```bash
# Configure sync with your server
feedo sync login <server> <username> <password>
# Check sync status
feedo sync status
# Run full sync (import feeds + sync read states)
feedo sync
```
**Tip:** Once configured, press `S` in the TUI to sync without leaving the app!
### Adding Feeds
Press `n` in the app to add a new feed. Just paste any URL β Feedo will auto-discover the RSS/Atom feed:
```
ββ β Add Feed βββββββββββββββββββββββββββββββββββββββββββ
β π https://blog.rust-lang.orgβ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
Feedo tries:
1. The URL directly (if it's already a feed)
2. `<link rel="alternate">` tags in HTML
3. Common paths like `/feed`, `/rss`, `/atom.xml`
### First Run
On first launch, Feedo creates a default configuration with some starter feeds:
- **Tech**: Hacker News, Lobsters
- **News**: BBC World
Feel free to modify `~/.config/feedo/config.json` to add your own!
<br>
## β¨οΈ Keybindings
### Navigation
| `j` / `β` | Move down |
| `k` / `β` | Move up |
| `l` / `β` / `Enter` | Select / Enter |
| `h` / `β` | Go back |
| `g` | Jump to top |
| `G` | Jump to bottom |
| `Tab` | Switch between panels |
| `v` | Toggle content preview |
### Actions
| `n` | Add new feed (with auto-discovery) |
| `d` / `Delete` | Delete selected feed/folder |
| `r` | Refresh all feeds |
| `S` | Sync with server (if configured) |
| `o` | Open article in browser |
| `s` | Share article |
| `Space` | Toggle read/unread |
| `a` | Mark all as read (selected feed) |
| `/` | Open search |
| `t` | Open theme picker |
| `?` / `F1` | Show keyboard shortcuts |
| `A` | About Feedo |
| `q` | Quit |
### Share Mode
| `β` / `β` | Navigate platforms |
| `Enter` | Share to selected platform |
| `x` | Quick share to X (Twitter) |
| `m` | Quick share to Mastodon |
| `b` | Quick share to Bluesky |
| `Esc` | Cancel |
### Add Feed Mode
| `Enter` | Discover feeds / Confirm |
| `β` / `β` | Select feed (if multiple found) |
| `Esc` | Cancel / Go back |
### Search Mode
| `Enter` | Go to selected result |
| `β` / `β` | Navigate results |
| `Esc` | Cancel search |
### Theme Picker
| `β` / `β` | Browse themes (live preview) |
| `Enter` | Apply and save theme |
| `Esc` | Cancel |
<br>
## βοΈ Configuration
### Config Location
Feedo uses `~/.config/feedo/` on **all platforms** for consistency:
| Linux | `~/.config/feedo/config.json` |
| macOS | `~/.config/feedo/config.json` |
| Windows | `%USERPROFILE%\.config\feedo\config.json` |
### Example Configuration
```json
{
"folders": [
{
"name": "Tech",
"icon": "π»",
"expanded": true,
"feeds": [
{
"name": "Hacker News",
"url": "https://hnrss.org/frontpage"
},
{
"name": "Lobsters",
"url": "https://lobste.rs/rss"
},
{
"name": "This Week in Rust",
"url": "https://this-week-in-rust.org/rss.xml"
}
]
},
{
"name": "News",
"icon": "π°",
"expanded": false,
"feeds": [
{
"name": "BBC World",
"url": "https://feeds.bbci.co.uk/news/world/rss.xml"
}
]
}
],
"feeds": [
{
"name": "xkcd",
"url": "https://xkcd.com/rss.xml"
}
],
"theme": {
"name": "dracula"
},
"refresh_interval": 30
}
```
### Offline Mode & Cache
Feedo automatically caches all articles for offline reading:
| `~/.config/feedo/data/cache.json` | Cached articles and read states |
**How it works:**
- Articles are cached after each successful fetch
- Read/unread states persist between sessions
- When offline, you can still browse all previously fetched articles
- Cache is updated on every refresh and saved on exit
**No configuration needed** β offline mode works automatically!
### βοΈ Cloud Sync
Feedo supports syncing with RSS servers that implement the **Google Reader API**.
#### β
Tested & Working
| **FreshRSS** | `https://your-server/api/greader.php` | Self-hosted, recommended |
| **Miniflux** | `https://your-server/v1/` | Self-hosted, lightweight |
| **The Old Reader** | `https://theoldreader.com` | Hosted, free tier |
| **BazQux** | `https://bazqux.com` | Hosted, free trial |
#### β οΈ Not Currently Supported
| **Inoreader** | Requires OAuth 2.0 / App registration (not plain password login) |
| **Feedly** | Uses proprietary API, not Google Reader compatible |
#### Quick Start: FreshRSS (Docker)
```bash
# 1. Spin up FreshRSS
podman run -d --name freshrss -p 8080:80 freshrss/freshrss
# Or: docker run -d --name freshrss -p 8080:80 freshrss/freshrss
# 2. Visit http://localhost:8080 and complete the setup wizard
# - Create your admin account
# - Choose SQLite for simplicity
# 3. Enable API access:
# Settings β Authentication β β Allow API access
# 4. Set API password:
# Settings β Profile β API password (can be different from login password)
# 5. Connect Feedo
feedo sync login http://localhost:8080/api/greader.php your_user your_api_password
feedo sync status # Verify connection
feedo sync # Run first sync
```
#### Quick Start: Miniflux (Docker)
```bash
# 1. Spin up Miniflux with PostgreSQL
podman run -d --name miniflux-db -e POSTGRES_USER=miniflux \
-e POSTGRES_PASSWORD=secret -e POSTGRES_DB=miniflux postgres:15
podman run -d --name miniflux -p 8080:8080 \
-e DATABASE_URL="postgres://miniflux:secret@miniflux-db/miniflux?sslmode=disable" \
-e RUN_MIGRATIONS=1 \
-e CREATE_ADMIN=1 \
-e ADMIN_USERNAME=admin \
-e ADMIN_PASSWORD=admin123 \
miniflux/miniflux:latest
# Or with docker-compose (recommended for production)
# 2. Visit http://localhost:8080 and login with admin/admin123
# 3. Enable Fever API (used for Google Reader compatibility):
# Settings β Integrations β Fever β Enable & set password
# 4. Connect Feedo (note: Miniflux uses /v1/ endpoint)
feedo sync login http://localhost:8080/v1/ admin your_fever_password
feedo sync status
feedo sync
```
**Tip:** For production, see [FreshRSS Docker docs](https://github.com/FreshRSS/FreshRSS/tree/edge/Docker) or [Miniflux installation guide](https://miniflux.app/docs/installation.html).
#### Setup
```bash
# Configure your sync server
feedo sync login https://freshrss.example.com/api/greader.php myuser mypassword
# Verify connection
feedo sync status
```
#### What syncs?
| **Server β Local** | Subscriptions (feeds + folders) |
| **Server β Local** | Read states |
| **Local β Server** | Read states |
#### Running Sync
**From CLI:**
```bash
feedo sync
```
**From TUI:**
Press `S` (shift+s) β the status bar shows a βοΈ indicator when sync is configured.
#### Config Example
After running `feedo sync login`, your config will include:
```json
{
"sync": {
"provider": "freshrss",
"server": "https://freshrss.example.com/api/greader.php",
"username": "myuser",
"password": "mypassword"
}
}
```
**Note:** For security, consider using an API password or app-specific password if your service supports it.
### Theme Colors
Feedo comes with **15 popular themes** from the terminal/editor world:
| `dracula` | π¦ Dark purple aesthetic (default) |
| `one-dark-pro` | βοΈ Atom's iconic dark theme |
| `nord` | βοΈ Arctic, bluish color palette |
| `catppuccin-mocha` | π± Warm pastel dark theme |
| `catppuccin-latte` | β Warm pastel light theme |
| `gruvbox-dark` | πΈ Retro groove colors |
| `gruvbox-light` | π» Retro groove, light variant |
| `tokyo-night` | π Futuristic dark blue |
| `solarized-dark` | βοΈ Precision colors, dark |
| `solarized-light` | π Precision colors, light |
| `monokai-pro` | π¨ Classic syntax highlighting |
| `rose-pine` | πΉ Soho vibes with natural pine |
| `kanagawa` | π Inspired by Hokusai's art |
| `everforest` | π² Comfortable green forest |
| `cyberpunk` | π Neon-soaked futuristic |
**Example config:**
```json
{
"theme": {
"name": "catppuccin-mocha"
}
}
```
<br>
## ποΈ Architecture
```
src/
βββ main.rs # Entry point, CLI handling
βββ lib.rs # Library root, public API
β
βββ app/ # Application core
β βββ mod.rs # App state, event loop, orchestration
β
βββ config/ # Configuration management
β βββ mod.rs # Module exports
β βββ data.rs # Config structs, load/save logic
β
βββ feed/ # Feed management
β βββ mod.rs # Module exports
β βββ item.rs # FeedItem struct
β βββ manager.rs # FeedManager, Folder, async fetching
β βββ parser.rs # RSS/Atom parsing with feed-rs
β
βββ opml/ # OPML import/export
β βββ mod.rs # Full OPML 2.0 support
β
βββ theme/ # Theming system
β βββ mod.rs # AccentColor, Theme configuration
β
βββ ui/ # Terminal UI
βββ mod.rs # Module exports
βββ state.rs # UI state machine
βββ input.rs # Keyboard input handling
βββ render.rs # Rendering logic, layouts
βββ widgets/ # Custom ratatui widgets
```
### Design Principles
- **Separation of Concerns** β Each module has a single responsibility
- **Async by Default** β Network operations never block the UI
- **Immutable State** β UI state is explicit and predictable
- **Error Propagation** β Errors bubble up with context via `color-eyre`
- **Zero Unsafe** β Memory safety guaranteed by the compiler
<br>
## πΊοΈ Roadmap
- [x] **Feed Discovery** β Auto-detect RSS from any URL β
- [x] **Offline Mode** β Cache articles for reading without internet β
- [x] **Cloud Sync** β Sync with FreshRSS, Miniflux via Google Reader API β
- [ ] **Custom Keybindings** β Full key remapping
- [ ] **Notifications** β Desktop alerts for new articles
<br>
## π€ Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing`)
5. Open a Pull Request
<br>
## π License
MIT Β© Ricardo Dantas
---
<p align="center">
<b>Made with β€οΈ and π¦</b>
<br><br>
<code>(βα΄₯β)</code> Happy reading!
</p>