- Rust 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
* feat(config): add ui.show_preview to open preview pane at launch The dashboard preview pane always starts hidden, and the only way to get it back is pressing 'p' every single session. If you live in preview mode — say, reading reddit megathreads — that's a pointless ritual. The reason is that preview visibility was a runtime-only bool, hard-coded to false in App::new with no config behind it. Let's fix that: add ui.show_preview (default false, so nobody's setup changes), read once at startup. 'p' still toggles at runtime, and the new key shows up in `feedr config list`, get/set, and the TUI config editor like every other setting. While at it, fix test_toggle_preview_pane: it asserted the launch default, which now depends on whatever config.toml happens to be on the developer's machine. Tests that read your personal dotfiles are not tests, they're surprises. Fixes #40. * feat: media attachments, inline Kitty images, and clean HTML rendering Feedr has been treating every feed item as a bag of text, which is fine for blogs and useless for everything else. YouTube channels, podcasts, and web comics all ship their actual payload as <media:content>, <enclosure>, or Atom <content src> — and we were throwing all of it away. So, three things, which honestly should have been three commits: Parse media attachments and thumbnails into FeedItem, and expose them to macros as %m (primary media URL), %M (its MIME type) and %i (thumbnail). These are *orthogonal* to %u — no silent fallback to the page URL, because "mpv %m" quietly playing a web page instead of the episode is exactly the kind of helpfulness nobody asked for. Absent media expands to "". Render thumbnails inline in the detail view via the Kitty graphics protocol (kitty, Ghostty, WezTerm; silent no-op elsewhere, including tmux, which eats APC sequences). The trick: the detail view reserves a blank strip and the escapes are emitted *after* terminal.draw() returns, so ratatui's diff never fights us over those cells. Fetches run on background threads gated by is_safe_auto_url AND a redirect policy that re-validates every hop — thumbnails are hostile feed content fetched with no user action, so a public-looking URL that 302s into your router does not get to win. Decoding runs under explicit limits (a 5 MB PNG that inflates to gigabytes is treated as the attack it is), the decoded cache is LRU-capped, and frames with a visible modal suppress the image so it can't paint over the help overlay. Stop rendering RSS summaries through html2text's defaults. Reddit-style layout tables came out as box-drawn | columns that mangled on rewrap, and every anchor grew [N] markers plus a footnote dump of URLs nobody can click in a TUI. Flatten table markup before conversion and use a decorator that keeps emphasis but drops link annotations. The article URL is already in the header; we don't need it forty more times at the bottom. * build: hold image at 0.25.6 to keep the toolchain floor at 1.81 The image crate bumped its rust-version to 1.88 somewhere around 0.25.7, while the rest of our dependency graph tops out at 1.81. Shipping 0.25.10 would have quietly made feedr require a months-old-at-best compiler for exactly zero features we use. Pin to 0.25.6 in the lockfile, same pattern as the dom_smoothie pin: the manifest stays at "0.25", the committed lockfile enforces the real version, and the manifest comment makes it clear that bumping past this is a toolchain-floor decision, not routine maintenance. For the record: the CI job that claims to test the 1.75 MSRV has been silently running stable all along, because rust-toolchain.toml pins "stable" and overrides the matrix toolchain. So nothing would have caught this. That's a pre-existing problem, and it's not getting fixed from a feature branch. * fix(feed): route cached plain_text through the clean HTML renderer The detail and dashboard views render descriptions through CleanDecorator — no [N] link markers, no footnote dump, tables flattened. But the cached plain_text, which feeds pipe-to payloads and the content-length filter, was still going through the stock html2text decorator. So what you piped to your script was not what you were reading on screen. This is not great. Move CleanDecorator and render_clean_html from ui/utils.rs into feed.rs and build plain_text with them at parse time. They live in feed.rs not because they're prettier there, but because the parse path needs them and ui already depends on feed — the reverse dependency would have been a layering violation waiting to breed. One text pipeline. What you see, search, filter, and pipe is now the same string. * fix(image): stop leaking terminal pixel data and stacking placements A batch of lifecycle bugs in the Kitty integration, all found in review, and all variations of "the protocol is keyed differently than you think". First, clear_terminal used d=a, which deletes *placements* but keeps the transmitted pixel data in the terminal's registry — and then threw away our own kitty_images map. So every view re-entry re-encoded and re-transmitted the PNG under a fresh id while the old data sat orphaned in the terminal, bounded only by the terminal's own quota. The comment claiming delete-all wiped the registry was simply wrong. Now kitty_images survives clears: re-entering a view is one cheap placement escape, no re-encode, no re-transmit. Terminal-side data is freed exactly when a URL falls out of our LRU — eviction queues the id and the next write flushes a d=I (uppercase: placements *and* data), so terminal image memory is bounded by MAX_CACHED_IMAGES instead of by hope. Second, placements are keyed by (image id, placement id), so re-using p=1 across *different* image ids stacks both images in the strip. Nothing hits that today because every image-to-image transition happens to pass through an imageless frame, but "happens to" is not an invariant — track last_placed_id and delete the old placement when the id changes. While at it: spawn fetch workers via thread::Builder so an OS thread-creation failure releases the in-flight slot instead of panicking the TUI (the fulltext workers already did this; the image path just forgot), and suppress the 10-row image strip in compact mode or on detail areas under 30 rows, where header plus strip would squeeze the article down to one visible line. A thumbnail is decoration. The article is the point. |
||
| .github | ||
| assets | ||
| examples | ||
| src | ||
| tests | ||
| .DS_Store | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| cliff.toml | ||
| demo.cast | ||
| demo.gif | ||
| LICENSE | ||
| README.md | ||
| rust-toolchain.toml | ||
Feedr - Terminal RSS Feed Reader 📰
Feedr is a feature-rich terminal-based RSS feed reader written in Rust. It provides a clean, intuitive TUI interface for managing and reading RSS feeds with elegant visuals and smooth keyboard navigation.
Demo
Features
- Dashboard View: See the latest articles across all your feeds, sorted chronologically
- Feed Management: Subscribe to and organize multiple RSS/Atom feeds
- Feed Auto-Discovery: Paste any webpage URL and Feedr will detect and offer to subscribe to its RSS/Atom feeds
- Starred Articles: Save articles for later with a dedicated starred view
- Categories: Organize feeds into custom categories with create, rename, and delete support
- Tree View: Browse feeds in a hierarchical tree grouped by category
- Advanced Filtering: Filter articles by category, age, author, read status, starred status, and content length
- Dual Themes: Switch between a dark cyberpunk theme and a light zen theme with
t - Live Search: Instantly search across all feed titles and article content
- Summary View: "What's New" screen shows articles added since your last session with per-feed stats
- Read/Unread Tracking: Persistent read state tracking across sessions
- Mark All Read: Quickly mark all visible items as read with
m - Article Preview: Toggle an inline preview pane in the dashboard view, or have it open at launch with
show_preview = true - Link Extraction: Extract and browse all links from an article with
l - Full-Text Extraction: Strip away summaries and read the actual article content inline via Mozilla Readability — manual on
Shift+F, or auto-extract on refresh per feed withfulltext = true - Inline Images: When an article has a
<media:thumbnail>(e.g. YouTube channel feeds, web-comic RSS), Feedr renders it inline in the detail view via the Kitty graphics protocol. Auto-detected on Ghostty, Kitty, and WezTerm; silently no-ops elsewhere. Images are fetched in the background — read or open in browser without waiting. - Help Overlay: Press
?for a scrollable keybinding reference overlay - OPML Import: Bulk import feeds from OPML files via
feedr --import <file.opml> - Browser Integration: Open articles in your default browser
- Mouse Support: Click to select items and scroll with the mouse wheel
- Background Refresh: Automatic feed updates with configurable intervals and smart rate limiting
- Rate Limiting: Per-domain request throttling prevents "too many requests" errors (ideal for Reddit feeds)
- Vim-Style Navigation: Use
j/kalongside arrow keys for navigation - Rich Content Display: HTML-to-text conversion with clean article formatting
- Authenticated Feeds: Support for custom HTTP headers per feed (e.g.,
Authorization: Bearer ...) for private/authenticated RSS feeds - Compact Mode: Automatic compact layout for small terminals (≤30 rows), with manual
always/neveroverride in config - CLI Config Management: Get, set, and list configuration from the command line (
feedr config), or use the interactive TUI config editor (feedr config --tui) - Configurable Keybindings: Remap any key action via the
[keybindings]section inconfig.toml - External-Command Hooks: Newsboat-style macros (
pipe-to,exec) bound to keys, plusexec_on_newnotifications fired per new item — all with shell-free argument templating - Configurable: Customize timeouts, themes, UI behavior, and default feeds via TOML config
- XDG Compliant: Follows standard directory specifications for configuration and data storage
Installation
Prerequisites
- Rust and Cargo (install from https://rustup.rs/)
Using Cargo Install (Recommended)
cargo install feedr
Arch Linux (AUR)
Feedr is available on the AUR. Install it using your preferred AUR helper:
paru -S feedr
# or
yay -S feedr
Build from Source
git clone https://github.com/bahdotsh/feedr.git
cd feedr
cargo build --release
The binary will be available at target/release/feedr.
Usage
Run the application:
feedr
OPML Import
Import feeds from an OPML file:
feedr --import feeds.opml
Configuration Management
View and modify settings from the command line:
feedr config list # List all settings with current values
feedr config get ui.theme # Get a single value
feedr config set ui.theme light # Set a value (with validation)
feedr config --tui # Open interactive TUI config editor
Available config keys use dot-notation (e.g. general.max_dashboard_items, network.http_timeout, ui.theme, ui.compact_mode). Run feedr config list to see all keys. Feed management (default_feeds) is only available through the TUI editor.
Quick Start
- When you open Feedr for the first time, press
ato add a feed - Enter a valid RSS feed URL (e.g.,
https://news.ycombinator.com/rss) - You can also press
1,2, or3to quickly add Hacker News, TechCrunch, or BBC News - Use arrow keys (or
j/k) to navigate andEnterto view items - Press
oto open the current article in your browser - Press
tto toggle between dark and light themes
Keyboard Controls
All keybindings below show their defaults. You can remap any action via the [keybindings] section in your config file — see Configurable Keybindings.
General Navigation
| Key | Action |
|---|---|
Tab |
Cycle forward through views |
Shift+Tab |
Cycle backward through views |
q |
Go back (quit from Dashboard) |
h / Esc / Backspace |
Go back one view |
Home |
Return to Dashboard |
Ctrl+Q |
Quit from any view |
r |
Refresh all feeds |
t |
Toggle dark/light theme |
/ |
Search mode |
? |
Help overlay (scrollable keybinding reference) |
Dashboard View
| Key | Action |
|---|---|
↑/↓ or k/j |
Navigate items |
g / G or End |
Jump to top / bottom |
Enter |
View selected item |
f |
Filter articles |
c |
Cycle category filter |
Ctrl+C |
Open category management |
a |
Add a new feed |
s |
Toggle starred |
Space |
Toggle read/unread |
m |
Mark all items as read |
p |
Toggle preview pane |
Shift+J / Shift+K |
Scroll preview down / up |
o |
Open link in browser |
1/2/3 |
Quick-add demo feeds (HN, TechCrunch, BBC) |
Feed List View
| Key | Action |
|---|---|
q / h / Esc |
Go to dashboard |
↑/↓ or k/j |
Navigate feeds |
Enter |
View feed items |
Space |
Expand/collapse category (tree view) |
a |
Add a new feed |
d |
Delete selected feed |
c |
Assign category to feed |
Feed Items View
| Key | Action |
|---|---|
q / h / Esc / Backspace |
Back to feeds list |
Home |
Go to dashboard |
↑/↓ or k/j |
Navigate items |
g / G or End |
Jump to top / bottom |
Enter |
View item details |
s |
Toggle starred |
Space |
Toggle read/unread |
m |
Mark all items as read |
o |
Open item in browser |
Item Detail View
| Key | Action |
|---|---|
q / h / Esc / Backspace |
Back to feed items |
↑/↓ or k/j |
Scroll content |
Ctrl+U / Ctrl+D |
Scroll content (page) |
Page Up / Page Down |
Scroll content (page) |
g |
Jump to top |
G / End |
Jump to bottom |
s |
Toggle starred |
o |
Open item in browser |
l |
Extract and show all links |
Shift+F |
Toggle/fetch full-text (Readability) |
Starred View
| Key | Action |
|---|---|
↑/↓ or k/j |
Navigate items |
Enter |
View item details |
s |
Remove from starred |
o |
Open item in browser |
Categories View
| Key | Action |
|---|---|
n |
Create new category |
e |
Rename category |
d |
Delete category |
Space |
Expand/collapse category |
Enter |
Select category |
r |
Refresh |
? |
Help |
h / Esc / q |
Back |
Filter Mode (press f on Dashboard)
| Key | Action |
|---|---|
c |
Filter by category |
t |
Filter by time/age |
a |
Filter by author |
r |
Filter by read status |
s |
Filter by starred status |
l |
Filter by content length |
x |
Clear all filters |
Mouse Support
| Action | Effect |
|---|---|
| Left click | Select item |
| Scroll up/down | Navigate items |
Configuration
Feedr supports customization through a TOML configuration file that follows XDG Base Directory specifications. You can edit the file directly, use feedr config get/set from the command line, or use feedr config --tui for an interactive editor.
Configuration File Location
- Linux/macOS:
~/.config/feedr/config.toml - Windows:
%APPDATA%\feedr\config.toml
The configuration file is automatically generated with default values on first run if it doesn't exist.
Available Settings
# Feedr Configuration File
[general]
max_dashboard_items = 100 # Maximum number of items shown on dashboard
auto_refresh_interval = 0 # Auto-refresh interval in seconds (0 = disabled)
refresh_enabled = false # Enable automatic background refresh
refresh_rate_limit_delay = 2000 # Delay in milliseconds between requests to same domain
[network]
http_timeout = 15 # HTTP request timeout in seconds
user_agent = "Mozilla/5.0 (compatible; Feedr/1.0; +https://github.com/bahdotsh/feedr)"
[ui]
tick_rate = 100 # UI update rate in milliseconds
error_display_timeout = 3000 # Error message duration in milliseconds
theme = "dark" # Theme: "dark" (cyberpunk) or "light" (zen)
compact_mode = "auto" # Compact layout: "auto", "always", or "never"
show_preview = false # Start with the dashboard preview pane open
# Optional: Define default feeds to load on first run
[[default_feeds]]
url = "https://example.com/feed.xml"
category = "News"
# Authenticated feed with custom HTTP headers
[[default_feeds]]
url = "https://private.example.com/feed.xml"
[default_feeds.headers]
Authorization = "Bearer your_token_here"
Configuration Options Explained
General Settings
- max_dashboard_items: Controls how many items are displayed on the dashboard (default: 100)
- auto_refresh_interval: Automatically refresh feeds at specified interval in seconds (0 disables auto-refresh)
- refresh_enabled: Master switch to enable/disable automatic background refresh (default: false)
- refresh_rate_limit_delay: Delay in milliseconds between requests to the same domain to prevent "too many requests" errors (default: 2000ms). This is especially useful for Reddit feeds and other rate-limited services.
Network Settings
- http_timeout: Timeout for HTTP requests when fetching feeds (useful for slow connections)
- user_agent: Custom User-Agent string for HTTP requests
UI Settings
- tick_rate: How frequently the UI updates in milliseconds (lower = more responsive, higher = less CPU usage)
- error_display_timeout: How long error messages are displayed in milliseconds
- theme: Choose between
"dark"(cyberpunk aesthetic with neon colors) or"light"(zen minimalist with organic colors). Can also be toggled at runtime witht. - compact_mode: Controls the compact layout for small terminals.
"auto"(default) enables compact mode when terminal height is ≤30 rows,"always"forces compact mode, and"never"disables it. Compact mode uses single-line items, a minimal title bar, and an abbreviated help bar to maximize screen real estate. - show_preview: When
true, the dashboard preview pane is open at launch (default:false). Thepkey still toggles it at runtime. Note that the preview pane is always hidden while compact mode is active.
Background Refresh Example
To enable automatic refresh every 5 minutes with rate limiting:
[general]
refresh_enabled = true
auto_refresh_interval = 300 # 5 minutes
refresh_rate_limit_delay = 2000 # 2 seconds between requests to same domain
Note: Rate limiting groups feeds by domain and staggers requests to prevent hitting API limits. For example, if you have multiple Reddit feeds, they will be fetched with a 2-second delay between each request to avoid getting blocked.
Default Feeds
You can define feeds to be automatically loaded on first run:
[[default_feeds]]
url = "https://news.ycombinator.com/rss"
category = "Tech"
[[default_feeds]]
url = "https://example.com/feed.xml"
category = "News"
# Optional per-feed refresh threshold (seconds). When this elapses since
# the last refresh, the next auto-refresh tick is triggered — currently
# all feeds are refreshed together (no true selective per-feed refresh).
# Requires `general.refresh_enabled = true`.
[[default_feeds]]
url = "https://example.com/fast-feed.xml"
refresh_interval = 60
Authenticated Feeds
Some RSS feeds require authentication or custom HTTP headers. You can configure per-feed headers:
[[default_feeds]]
url = "https://private.example.com/feed.xml"
[default_feeds.headers]
Authorization = "Bearer your_api_token"
[[default_feeds]]
url = "https://another-api.example.com/rss"
[default_feeds.headers]
X-API-Key = "your_api_key"
Cookie = "session=abc123"
Headers are sent with every request for that feed, including refreshes.
Full-Text Extraction
Most RSS feeds ship only short summaries. Feedr can fetch the linked article URL and run Mozilla Readability (via the dom_smoothie crate) to extract the actual article body and render it inline.
- Manual: in the article detail view, press
Shift+Fto extract the focused article. PressShift+Fagain to toggle back to the original summary, or after a failure to retry. - Auto on refresh: set
fulltext = trueon a feed and Feedr will auto-extract newly-seen items on each refresh (same "no firehose" rule asexec_on_new— the first observation of a feed seeds silently).
[[default_feeds]]
url = "https://example.com/summary-only-feed.xml"
fulltext = true
Notes:
- Extracted content is in-memory only — it is not persisted to disk. A restart re-extracts on demand.
- Per-feed auth headers are not sent to the article URL (article URLs are typically third-party hosts; forwarding
Authorizationwould leak credentials). - Pages with very short extracted bodies (likely JS-rendered or behind a wall) fail gracefully and fall back to showing the original summary.
External-Command Hooks
Feedr supports newsboat-style external commands for two workflows: macros (key-triggered chains that act on the focused article) and exec_on_new (a notification hook fired per newly-seen item after each refresh).
Commands are not run through a shell. Templates are tokenized once at config load, and %X placeholders are substituted into individual argv tokens — feed content can never break out of an argument. For pipes, redirection, or globbing, write a small shell script and invoke that.
Template Variables
Expanded in every argv token of macro and hook commands:
| Variable | Expands to |
|---|---|
%t |
Article title |
%u |
Article URL |
%a |
Author |
%d |
Formatted publish date |
%f |
Feed title |
%F |
Feed URL |
%m |
Primary media URL (<media:content>, <enclosure>, or Atom <content src>) |
%M |
MIME type of the primary media |
%i |
First <media:thumbnail> URL on the item |
%% |
Literal % |
%m/%M/%i expand to the empty string when an item has no media — they're orthogonal to %u (no fallback). For YouTube channel feeds the watch-page URL is %u; the in-feed video URL is %m. For RSS podcasts the episode page is %u; the audio file is %m.
Macros
A macro binds a key to an ordered chain of steps. Trigger with <prefix><key> (default prefix is ,). Steps are separated by ;. An optional trailing -- "description" overrides the help-overlay label.
[macros]
y = 'open-in-browser ; pipe-to "yt-dlp %u"'
w = 'pipe-to "wallabag-cli add %u" -- "Save to Wallabag"'
n = 'pipe-to "tee /tmp/out.txt" stdin=metadata'
# Media-rich feeds (YouTube channels, web comics, podcasts):
v = 'exec "mpv %u" -- "Play in mpv"' # YouTube: mpv resolves the watch URL via yt-dlp
d = 'exec "yt-dlp -o ~/Videos/%(title)s.%(ext)s %u" -- "Download with yt-dlp"'
p = 'exec "mpv %m" -- "Play media file"' # Podcasts: direct .mp3 URL is %m
i = 'exec "feh %i" -- "View thumbnail"' # Web comics / YT thumbnails
[macro_options]
prefix = "," # the macro-prefix key
pipe_default_stdin = "body" # body | title | url | metadata | none
Step kinds:
<action>— invoke a built-in action. Supported in macros:open-in-browser,toggle-star,toggle-read,mark-all-read,refresh,toggle-theme,extract-links,fetch-full-text,help.pipe-to "cmd %u" [stdin=…]— suspend the TUI, run the command, and pipe article content to its stdin.stdinis one ofbody(default),title,url,metadata, ornone.exec "cmd %u"— spawn the command detached (no stdin, no terminal takeover).
Chains halt on the first step error. Press Esc after the prefix to cancel; an unbound follow-up surfaces a "No macro bound" error. Macros are also rendered in the help overlay (?).
exec_on_new Notifications
Fire a command once per newly-seen item after each refresh. The first successful fetch of each feed seeds the seen-set silently — you do not get a firehose on initial load or first run.
[hooks]
exec_on_new = 'notify-send "New: %t" "%f"'
Children are spawned detached so the TUI never blocks on them. Crash semantics are at-most-once: feedr persists the seen-set before spawning, so a kill mid-fire loses a notification rather than re-firing on the next launch. Prefer idempotent commands (e.g. wallabag-cli add is safe; mail-me is not).
Security Notes
- The shell is never invoked, so feed content in
%t/%a/ etc. cannot escape an argument. - Do not wrap your command in
sh -c "... %t ..."— that reintroduces shell injection through item titles. Write a script file and invoke it instead. ~/$HOME/$VARare not expanded — use absolute paths.- If a macro's command template has unbalanced quotes or names an unknown action, feedr surfaces a startup warning rather than failing silently at trigger time.
Configurable Keybindings
Remap any action by adding a [keybindings] section to your config file. Each action can be bound to a single key string or an array of keys:
[keybindings]
quit = "x" # Single key
move_up = ["Up", "k", "w"] # Multiple keys
force_quit = "Ctrl+x" # Modifier keys
toggle_theme = "F5" # Function keys
Available actions:
| Action | Default | Description |
|---|---|---|
quit |
q |
Go back / quit from Dashboard |
force_quit |
Ctrl+q |
Quit from any view |
back |
h, Esc, Backspace |
Go back one view |
home |
Home |
Return to Dashboard |
toggle_theme |
t |
Switch dark/light theme |
refresh |
r |
Refresh all feeds |
help |
? |
Show help overlay |
open_search |
/ |
Enter search mode |
move_up |
Up, k |
Navigate up |
move_down |
Down, j |
Navigate down |
page_up |
PageUp, Ctrl+u |
Page up |
page_down |
PageDown, Ctrl+d |
Page down |
jump_top |
g |
Jump to top |
jump_bottom |
G, End |
Jump to bottom |
select |
Enter |
Select / open |
add_feed |
a |
Add new feed |
delete_feed |
d |
Delete selected feed |
toggle_read |
Space |
Toggle read/unread |
toggle_star |
s |
Toggle starred |
mark_all_read |
m |
Mark all items as read |
open_in_browser |
o |
Open in browser |
toggle_preview |
p |
Toggle preview pane |
open_filter |
f |
Open filter mode |
cycle_category |
c |
Cycle category filter |
open_category_management |
Ctrl+c |
Category management |
assign_category |
c |
Assign category to feed |
extract_links |
l |
Extract links from article |
fetch_full_text |
Shift+F |
Toggle/fetch full-text (Readability) |
scroll_preview_up |
Shift+K, Shift+Up |
Scroll preview up |
scroll_preview_down |
Shift+J, Shift+Down |
Scroll preview down |
toggle_expand |
Space |
Expand/collapse in tree view |
next_tab |
Tab |
Next view |
prev_tab |
Shift+Tab |
Previous view |
Supported key formats: Single characters (q, ?, /), special keys (Enter, Space, Tab, Esc, Backspace, Up, Down, Left, Right, Home, End, PageUp, PageDown, Delete, F1–F5), and modifier combos (Ctrl+q, Shift+Tab, Alt+x).
Data Storage
Feedr stores your bookmarks, categories, read/unread state, and starred articles in:
- Linux/macOS:
~/.local/share/feedr/feedr_data.json - Windows:
%LOCALAPPDATA%\feedr\feedr_data.json
Backwards Compatibility
Feedr automatically migrates data from older versions to the new XDG-compliant locations. Your existing data will be preserved and automatically moved to the correct location on first run.
Dependencies
- ratatui: Terminal UI framework
- crossterm: Terminal manipulation
- reqwest: HTTP client (with gzip/deflate/brotli support)
- feed-rs: RSS and Atom feed parsing
- html2text: HTML to text conversion
- chrono: Date and time handling
- serde: Serialization/deserialization
- clap: Command-line argument parsing
- opml: OPML import support
- toml: Configuration file parsing
- scraper: HTML parsing for feed auto-discovery
- url: URL parsing and manipulation
- dom_smoothie: Mozilla Readability port for full-text extraction
- encoding_rs: Charset detection for non-UTF-8 article pages
- shlex: Shell-style tokenization for macro/hook command templates
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request

