Screenshot
Performance
feedreader is server-rendered HTML with vanilla JS/CSS — no framework runtime, no client hydration, no bundler payload. That keeps it genuinely tiny and fast, not just "fast for a SPA."
The home page has no render-blocking asset requests: the stylesheet and pre-paint theme bootstrap are inlined into the HTML, and the response is edge-cached per URL for five minutes. The browser-facing copy is private, no-store; the five-minute cache lives in the Worker Cache API. Versioned static assets are served with immutable browser caching; see web-static/_headers.
Lighthouse against the production deployment (mobile, simulated throttling):
Reproduce locally:
npx lighthouse https://reader.boringcode.dev --view
Features
- Multi-source feed aggregation
- Hacker News
- GitHub Trending
- Hugging Face Trending Papers
- Persistent edge storage with Cloudflare D1
- Incremental fetch model that keeps older items in the database
- Server-backed incremental loading: first page loads 12 items, first-load bootstrap/filter/search/refresh show a toast-based loading state, and
View more appends more items in place
- Source-aware card summaries
- Hacker News cards show points and comments
- GitHub cards show stars, today's stars, and forks
- GitHub repo titles are normalized to canonical
owner/repo form from the repo URL path
- Hugging Face cards show upvotes
- published/fetched dates are formatted in the browser locale while preserving the stored UTC calendar date
- Card host-row actions for optional bookmarks and a more-actions dialog with Share, Hacker News comments, and Open source
- Responsive, minimalist UI with:
- source filters
- real source icons in filters, dialog rows, and card metadata
- RSS-based app icon/favicon branding
- seven themes — Auto (follows the device), Ink, Paper, Sepia, Terminal, Glacier, and Ember — with a visual swatch picker
- inline expanding search
- reader settings dialog for theme, density, and source visibility
- iPhone landscape safe-area handling so content stays clear of the notch and Dynamic Island
- compact icon-only Install/Update and For You controls when the preview is open in phone landscape
- Configurable visible sources stored in
localStorage
- choose which source buttons are shown
- when 2+ sources are enabled,
All stays visible and aggregates over the enabled set
- when exactly 1 source is enabled, only that source button is shown
- Debounced client-side search UX backed by the server API
- Explicit empty states for no-result source filters and searches
- Connectivity indicator that shows a no-wifi icon while offline and silently refreshes the current view when the browser reconnects
- Scheduled refresh every 1 hour on wall-clock boundaries
- Manual refresh from the header re-fetches the current feed view from backend stored items only; it does not re-fetch upstream sources
- Persisted visited-link dimming for feed card titles across reload/reopen using local storage
- Optional article preview (off by default, toggled in Reader settings) that opens a reader view beside the feed on wide screens or full-screen on phones, with inline links/emphasis, image captions, tables, publication date/reading time, a Workers AI Markdown fallback for PDFs, Office documents, and HTML pages with no readable blocks, a Browser Run fallback for JavaScript-rendered pages, source-specific Hacker News comments, and an Open source button
- Saved articles with an optional per-card bookmark toggle, a
Saved filter, and a preview-panel toggle; bookmark controls are disabled by default and can be enabled in Reader settings, with saved items stored locally under feedreader.bookmarks
- PWA-ready assets and offline caching including manifest, service worker, touch icons, cached shell assets, cached
/api/items responses for previously visited views, a standalone launch veil, and skeleton loading states
- Reconnect list refresh re-fetches the current view from backend stored items only; it does not refresh upstream sources
- Cloudflare-native deployment with Workers, D1, Cron Triggers, and Workers Static Assets
Why feedreader?
feedreader is designed for people who want a small, understandable reader instead of a large feed platform.
It optimizes for:
- simple operations
- low infrastructure overhead
- straightforward data ownership
- easy extension when adding more sources
Tech stack
Runtime
- TypeScript
- Cloudflare Workers
- Web Standard APIs
node-html-parser
Frontend
- Server-rendered HTML
- Vanilla JavaScript
- Plain CSS
Storage
Deployment
- Cloudflare Workers
- Cron Triggers
- Workers Static Assets
Architecture
At a high level:
- source adapters fetch upstream content
- items are upserted into D1 by
(source, external_id)
- the Worker reads stored items ordered by article date descending
- the cron trigger refreshes hourly on wall-clock boundaries
Key properties:
- old items are retained in the database
- fetch failures do not wipe existing data
- sources without a native article date fall back to the initial fetch time (
first_seen_at) for ordering
- later refreshes preserve the original published/fetched ordering timestamps for existing items
- source fetches send a descriptive User-Agent, honor
Retry-After on 429s, and cap error details; Hugging Face reads its daily-papers JSON API instead of scraping the trending HTML page
여기까지가 README 앞부분입니다. 나머지는 원문에서 읽을 수 있습니다. README 전문 보기 ↗