Self-hosted: Reader theme with a split archive view - #1450
Conversation
The second layout-level design on the theme manifests: the archive as a persistent rail beside the open post, both panes scrolling independently, j and k moving between entries without leaving the page. The feed route renders the split shell with a greeting pane (the rail owns the archive), article routes open with the rail alongside and deep links work unchanged. On small screens the split collapses to one pane at a time: the feed route is the rail, everything else shows the content pane. Fetching for the rail is the seam default's own, extracted into a shared useArchiveFeed hook so the two cannot drift. Cards stay the shared default so search results keep their look inside the reading pane. The template registers across the roster, the display catalog, the editor label map and the CSS registry; the guard suites cover the new file and counts.
|
Warning Review limit reached
Next review available in: 17 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (17)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Summary by QodoSelf-hosted: Add Reader theme with split archive rail layout
AI Description
Diagram
High-Level Assessment
Files changed (15)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3c5a90b13c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| author: `@${entryData.author}`, | ||
| permlink: entryData.permlink, | ||
| }} | ||
| search={{ raw: undefined }} |
There was a problem hiding this comment.
Preserve the selected filter when opening a post
When a reader opens an entry from a non-default filter such as trending, this link carries only raw; the post route's validateSearch also retains only raw, so usePostsFilterState falls back to the first configured filter and ReaderRail immediately switches to a different feed. The supposedly persistent archive and subsequent j/k navigation therefore lose the selected filter as soon as a post opens; preserve that filter through post navigation or in shell state.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in ce47b7e. Both post routes now retain a clamped optional filter search param and the rail carries a non-default filter into every post link and every j/k navigation, so the archive stays on the feed the reader was browsing. Canonical deep links stay unchanged (no filter injected when it is the default).
|
|
||
| {entries.map((entry) => { | ||
| const entryData = entry.original_entry || entry; | ||
| const active = entry.permlink === activePermlink; |
There was a problem hiding this comment.
Match the active row by its full effective post identity
When a community contains the same permlink from different authors, or an account-feed entry has an original_entry, this compares only the outer entry's permlink even though the link navigates using entryData.author and entryData.permlink. That can highlight the wrong or multiple rows, and the keyboard lookup uses the same incomplete identity; compare the route's author and permlink against the effective entry instead.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in ce47b7e. Active-row detection and the keyboard lookup now share one isOpen predicate comparing the effective entry (original_entry || entry) author AND permlink against the route params, so cross-posts highlight correctly and a permlink shared by two authors cannot mark both rows.
|
|
||
| {/* The floating composer entry point the default navigation mounts; a | ||
| theme shell must never cost owners and community members the way in. */} | ||
| <CreatePostButton /> |
There was a problem hiding this comment.
Keep the internal publisher inside the Reader shell
When resolveCreatePostTarget selects the internal composer, this button navigates to /publish, but the inspected routes/publish.tsx renders PublishPageContent directly rather than through BlogLayout. Consequently ReaderShell unmounts, its masthead and content pane disappear, and the default sidebar returns despite Reader declaring sidebar options unsupported; route the publisher through the theme shell if it is meant to be one of Reader's content-pane routes.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Acknowledged, but this is pre-existing uniform topology rather than a Reader regression: routes/publish.tsx renders the composer directly for EVERY template (the default included; only /blog and /search go through BlogLayout), so no theme's shell wraps it and no sidebar returns there. Routing the composer and edit routes through the theme Shell seam is a deliberate follow-up decision for the seam architecture, tracked with the platform carry-alongs, since wrapping a full-screen editor in a split frame has its own tradeoffs.
…full identity Post routes now retain a clamped optional filter search param and the rail carries a non-default filter into every post link and j/k move, so opening an entry no longer resets the persistent archive to the default feed. Active-row detection and the keyboard lookup compare the effective entry's author and permlink (original_entry for reblogs) against the route params, so cross-posts highlight correctly and a shared permlink from two authors cannot mark both rows.
Code Review by Qodo
1.
|
Code Review by Qodo
1. Post navigation drops filter
|
…ing routes The extracted hook's select callback types its pages as Entry[][] instead of carrying the any over from its previous home. The rail's j/k listener now only acts on the feed and open posts: the rail stays mounted but CSS-hidden on search, publish and edit, and a stray keypress there must never navigate away from a composer. Post routes stay active even where the rail is hidden on small screens, since next and previous post is exactly what those keys mean while reading.
Closes #1421
What
The second layout-level design on the theme manifest architecture: a Reader template that keeps the post archive as a persistent rail beside the open article, so moving between posts never leaves the page.
How
themes/reader/reader-shell.tsx): a compact masthead strip (title, filters, search, user menu) over a two-pane split; both panes scroll independently. On small screens the split collapses to one pane at a time: the feed route shows the rail full-width and every other route (post, search, publish) shows the content pane.reader-rail.tsx): every entry as a compact row with the open one highlighted, infinite paging, andj/kkeyboard navigation to the next and previous entry (arrow keys are left alone so they keep scrolling the article). Deep links work unchanged; the rail just marks whatever permlink the route carries.reader-home.tsx): the feed route's ArchiveList seam becomes a greeting with the keyboard hint, since the rail owns the archive. Cards stay the shared default so search results keep their look.useArchiveFeedand consumed by both BlogPostsList and the rail, so the two cannot drift (same queries, same DMCA filtering path, same paging)./v1/templatesdisplay catalog, the editor label map, the CSS registry and the guard suites (roster lockstep, appearance tokens, failure states).sidebarandlistTypeare declared unsupported so the editor hides them under this theme.Tests