UI: FreshRSS-style keyboard shortcuts #5

Closed
opened 2026-09-10 03:37:43 +00:00 by agent-pi · 4 comments
Collaborator

Problem

The reader UI is mouse-only. As a FreshRSS user I have keyboard-driven reading muscle memory — j/k to move between articles, r to mark read, space to open the original — and reaching for the mouse for every article is slow.

Goal

Keyboard shortcuts across the reader UI that mirror FreshRSS defaults wherever a FreshRSS action has a yunjin equivalent, so switching between the two readers feels natural.

## Problem The reader UI is mouse-only. As a FreshRSS user I have keyboard-driven reading muscle memory — `j`/`k` to move between articles, `r` to mark read, `space` to open the original — and reaching for the mouse for every article is slow. ## Goal Keyboard shortcuts across the reader UI that mirror FreshRSS defaults wherever a FreshRSS action has a yunjin equivalent, so switching between the two readers feels natural.
Author
Collaborator

Findings

  • Zero keyboard handling exists today. The web UI has no JavaScript at all — server-rendered templates plus a single stylesheet (src/yunjin/web/static/style.css).
  • FreshRSS default shortcuts, from config-user.default.php on the edge branch of the FreshRSS repo (source):
Key FreshRSS action Yunjin equivalent
j / k next / previous entry next / previous article (or aggregate)
h next unread entry next unread article
n / p skip to next / previous (no mark-read) same
home / end first / last entry first / last article
r toggle read toggle read (endpoint exists: read/unread tracking)
space open on original website open article link
c collapse entry collapse/expand aggregate
m load more load more, if pagination applies
f1 help shortcut help overlay
escape close menus close help/menus
q actualize (refresh feeds) trigger feed refresh
v toggle media show/hide article images
t toggle sidebar show/hide nav
f, l, s, a, u favorite, labels, share, focus search, user filter no yunjin equivalent (no favorites/labels/sharing/search) — skip
1/2/3 normal/global/reading view yunjin's views are pages (Reader/Feeds/Settings), not view modes — either skip or map 1→Reader, 2→Feeds, 3→Settings; decide during implementation
  • FreshRSS makes shortcuts user-configurable (Configuration → Shortcuts; see configureController.php). For yunjin v1, hardcoded FreshRSS defaults are enough — configurability would be a separate feature if ever wanted.

Plan of attack

  1. One small static shortcuts.js loaded in base.html — progressive enhancement: the site must remain fully usable with JavaScript disabled (this is the sanctioned, minimal exception to the no-JS rule, and the reason this feature needs any JS at all).
  2. Global keydown handler that ignores keystrokes while focus is in input/textarea/select elements.
  3. A visible "current article" focus indicator in the reader; j/k move it, r toggles read via the existing read/unread route (fetch POST), space opens the original in a new tab.
  4. Aggregate pages: c collapses/expands the current aggregate.
  5. f1 (and ?) opens a help overlay listing all shortcuts; escape closes it.
  6. Manual verification checklist in the PR (no JS test infrastructure in this project; server-side behaviour unchanged).
## Findings - **Zero keyboard handling exists today.** The web UI has no JavaScript at all — server-rendered templates plus a single stylesheet (`src/yunjin/web/static/style.css`). - FreshRSS default shortcuts, from `config-user.default.php` on the `edge` branch of the FreshRSS repo ([source](https://github.com/FreshRSS/FreshRSS/blob/edge/config-user.default.php)): | Key | FreshRSS action | Yunjin equivalent | |-----|-----------------|-------------------| | `j` / `k` | next / previous entry | next / previous article (or aggregate) | | `h` | next unread entry | next unread article | | `n` / `p` | skip to next / previous (no mark-read) | same | | `home` / `end` | first / last entry | first / last article | | `r` | toggle read | toggle read (endpoint exists: read/unread tracking) | | `space` | open on original website | open article link | | `c` | collapse entry | collapse/expand aggregate | | `m` | load more | load more, if pagination applies | | `f1` | help | shortcut help overlay | | `escape` | close menus | close help/menus | | `q` | actualize (refresh feeds) | trigger feed refresh | | `v` | toggle media | show/hide article images | | `t` | toggle sidebar | show/hide nav | | `f`, `l`, `s`, `a`, `u` | favorite, labels, share, focus search, user filter | **no yunjin equivalent** (no favorites/labels/sharing/search) — skip | | `1`/`2`/`3` | normal/global/reading view | yunjin's views are pages (Reader/Feeds/Settings), not view modes — either skip or map `1`→Reader, `2`→Feeds, `3`→Settings; decide during implementation | - FreshRSS makes shortcuts user-configurable (Configuration → Shortcuts; see `configureController.php`). For yunjin v1, hardcoded FreshRSS defaults are enough — configurability would be a separate feature if ever wanted. ## Plan of attack 1. One small static `shortcuts.js` loaded in `base.html` — progressive enhancement: the site must remain fully usable with JavaScript disabled (this is the sanctioned, minimal exception to the no-JS rule, and the reason this feature needs any JS at all). 2. Global `keydown` handler that ignores keystrokes while focus is in input/textarea/select elements. 3. A visible "current article" focus indicator in the reader; `j`/`k` move it, `r` toggles read via the existing read/unread route (fetch POST), `space` opens the original in a new tab. 4. Aggregate pages: `c` collapses/expands the current aggregate. 5. `f1` (and `?`) opens a help overlay listing all shortcuts; `escape` closes it. 6. Manual verification checklist in the PR (no JS test infrastructure in this project; server-side behaviour unchanged).
Author
Collaborator

Dependency note

The r (toggle read) and h (next unread) shortcut mappings here assume read/unread state is visible and filterable in the reader. #18 (mark groups / articles as read, hide read items) is filed for exactly that — implementing #18 first (or alongside) will save rework on the shortcut endpoints.

## Dependency note The `r` (toggle read) and `h` (next unread) shortcut mappings here assume read/unread state is visible and filterable in the reader. #18 (mark groups / articles as read, hide read items) is filed for exactly that — implementing #18 first (or alongside) will save rework on the shortcut endpoints.
Author
Collaborator

Design decision (Marvin8 request, implemented in the same PR)

New setting "Mark groups as read when viewed" (default off) on the settings page — this is what makes the h next-unread shortcut a proper FreshRSS-style workflow together with the hide-read toggle.

Semantics: with the setting on, opening a group marks all its articles read — but the page renders with the read states captured before marking, so what you are reading does not change under you. The group simply counts as read everywhere else: it drops off the reader index when read groups are hidden, and h skips past it. Stored as mark_read_on_view in the settings store; toggle at Settings → Reading.

## Design decision (Marvin8 request, implemented in the same PR) New setting **"Mark groups as read when viewed"** (default off) on the settings page — this is what makes the `h` next-unread shortcut a proper FreshRSS-style workflow together with the hide-read toggle. Semantics: with the setting on, opening a group marks all its articles read — but the page renders with the read states captured *before* marking, so what you are reading does not change under you. The group simply counts as read everywhere else: it drops off the reader index when read groups are hidden, and `h` skips past it. Stored as `mark_read_on_view` in the settings store; toggle at Settings → Reading.
Author
Collaborator

Resolved by PR #28 (merged): FreshRSS-style keyboard shortcuts (j/k/n/p, h next-unread article, g open-next-unread-group, r toggle read at article or group level, space, o/enter, c, 1/2/3, f1/? help) plus the mark-groups-read-when-viewed setting (Settings → Reading) that makes the h/g unread workflow end-to-end.

Resolved by PR #28 (merged): FreshRSS-style keyboard shortcuts (j/k/n/p, h next-unread article, g open-next-unread-group, r toggle read at article or group level, space, o/enter, c, 1/2/3, f1/? help) plus the mark-groups-read-when-viewed setting (Settings → Reading) that makes the h/g unread workflow end-to-end.
Sign in to join this conversation.
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
marvin8/yunjin#5
No description provided.