Commit Graph
11 Commits
Author SHA1 Message Date
thalladaandClaude Fable 5.1 7d12f53314 Make the origin's Cache-Control headers CDN-safe
An audit of the live site ahead of the Cloudflare move found three ways a
cache-everything rule would have gone wrong.

First, several routes named no policy at all — `/login`, `/issues.json`,
`/files/epub/*`, `/files/xtc/*`, `/robots.txt`, the error pages — so the edge
would have applied its own default TTL (Cloudflare: two hours on a 200). For
`/files/*` that means an authenticated download becoming a publicly cached one.
Those routes now say what they mean, and `security_headers` fails closed: a
response that set no `Cache-Control` gets `no-store`, so a route added later
cannot silently inherit the CDN's default. Everything cookie- or Basic-auth
gated (`/files/*`, `/opds*`) is `private, no-store` on every response, 401s and
404s included.

Second, the public pages said `max-age=300` alone. They now say
`public, max-age=300, s-maxage=86400`: five minutes for the browser, a day for
the edge, which is safe because publishing purges the edge. A request carrying
a `daily_session=` cookie still gets `private, no-store`.

Third, `/static/favicon.svg` and `/static/speculation.json` were referenced
without `?v=` while being served `immutable` for a year — editing either one
could never have reached a browser again. Both are now in the `ASSET_VERSION`
hash and referenced with the version, and `static_asset` only promises a year
when the URL actually carries `?v=`; a bare `/static/…` URL gets an hour and
revalidates against the same ETag.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va5eMEmWEnjMXBsBob5FDW
2026-09-04 21:28:59 +00:00
thallada da24e7c7c5 Merge branch 'ui-shell': nav state, ears, feeds table, font flash, faster navigation
# Conflicts:
#	src/web/static/app.css
2026-09-04 17:08:10 +00:00
thalladaandClaude Fable 5.1 5cc4ac4bd3 UI pass: nav state, ears, feeds table, font flash, faster navigation
- Issue pages highlight Archive instead of Latest unless the issue is the
  newest one (IssueView::is_latest), with a router test.
- The ears row is empty by default and says "Dashboard" on dashboard pages;
  "Morning edition" only survives on the empty-state hero.
- Run page: the Feeds card table is fixed-layout and truncates long feed
  names with the full title on hover instead of pushing the entries column
  out of the card.
- Static assets are content-hashed, so serve them immutable for a year;
  preload both Newsreader faces and use font-display: block so a cache
  revalidation never paints the fallback serif first. Persisted details
  state is restored pre-paint from theme.js.
- Cross-document view transitions (140 ms fade, masthead held still) and
  Speculation Rules (prerender reader pages, prefetch dashboard pages on
  hover) served from /static/speculation.json via the Speculation-Rules
  header, which the CSP would otherwise block inline. No client router.
- Dashboard nav gets the same accent underline as the site nav; 40 px
  targets for the theme toggle, account link and dashboard nav.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MD4VWGq6mGcd8Bg67qyx9k
2026-09-04 17:06:48 +00:00
thalladaandClaude Fable 5.1 ad2e99eeb9 UI pass: table-of-contents sidebar fixes and scroll sync
- The open hamburger panel now fills the viewport below the sticky bar
  (100dvh, measured bar offset) and locks the page behind it. Locking only
  the root element: locking body too made it its own scroll container and
  un-stuck the bar.
- Front-page scroll-spy: the current chapter marker, mobile label/counter,
  desktop "Chapter N of M" line and both progress bars follow the reader
  through "In This Issue"; passed chapters read in ink. The sidebar
  auto-scrolls to keep the current chapter in view.
- The sidebar scrollbar sits in a reserved gutter instead of over the text.
- Hamburger cross-fades to a close icon; 44px tap targets on phones.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MD4VWGq6mGcd8Bg67qyx9k
2026-09-04 17:05:39 +00:00
thalladaandClaude Fable 5.1 ca910d99e5 Web dashboard v2 step 4: table-of-contents sidebar
Signed-in issue pages (issue, article, world, behind) get a chapter list
that marks where the reader is. One `issue_toc` helper builds it for all
four handlers: the picks in issue order numbered across sections, then
World Briefing, Behind the paper and a colophon anchor, with
position/total over the navigable chapters.

At `lg` and up it is a sticky left column with its own sticky header
(issue number, "Chapter N of M", a 2px progress bar); below `lg` the same
`<nav>` collapses behind a sticky hamburger bar and drops down as a
panel that closes on link tap, Escape and outside tap. Without JS the
panel is simply visible; `theme.js` marks the document scripted before
paint so it never flashes open. Article pages advance the progress bar
with scroll position through `requestAnimationFrame`, and the bar is a
`<progress>` element because a percentage width would need an inline
style the CSP forbids.

Also fixes the phone "ears" row from the step 2 review: the row no
longer wraps at 390px, the dateline has a short form below `sm:`, and
the toggle and account link stay on one line.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NHyYupFdBiR4VfoUM7NjSM
2026-09-03 21:50:05 +00:00
thalladaandClaude Fable 5.1 29c66da865 Merge branch 'v2-frontend': Tailwind design system, theme toggle, reader pages
Resolves the step 1 / step 2 overlap in the issue and world templates:
public entries carry the summary and why lines in the new styling, and the
legacy colophon renders n/a for unknown counts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NHyYupFdBiR4VfoUM7NjSM
2026-09-03 20:56:00 +00:00
thalladaandClaude Fable 5.1 12134f3596 Web dashboard v2 step 2: Tailwind design system, theme toggle, reader pages
Replaces the hand-written stylesheet with a Tailwind CSS v4 source
(src/web/tailwind.css) whose minified build is committed as
src/web/static/app.css and still embedded with include_str!, so the binary
and the test suite never need Node. package.json pins tailwindcss and
@tailwindcss/cli to 4.3.3 and adds css, css:watch and css:check.

Colours are semantic CSS variables swapped per theme and exposed to Tailwind
through @theme inline, so templates write bg-paper/text-ink/border-rule once
and both themes work. theme.js runs synchronously in <head> to set
data-theme before paint; app.js cycles the toggle system -> light -> dark and
persists the choice. Newsreader is self-hosted and served from
web::static_asset with the same ETag/304 logic as the other assets.

Every reader template is restyled: ears row, centred masthead with its rules,
tracked section labels, the drop cap on The Brief, "Why it's here" with its
accent bar, the segmented rating control, prev/next cards, and the colophon
as a two-column definition grid. Dashboard templates are untouched (step 3).

Also fixes a .block collision with Tailwind's display utility, the SVG
`hidden` property that never toggled the theme icon, and points the dev
seed's profile_path at ./dev so previewing cannot rewrite data/profile.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NHyYupFdBiR4VfoUM7NjSM
2026-09-03 20:51:08 +00:00
thalladaandClaude Fable 5.1 f410df47c0 Web dashboard v2 step 1: public summaries, legacy issue fallback
Public issue pages and the Atom feed now carry each pick's summary and
'Why it's here' line (bodies, The Brief, World Briefing and comments stay
private). Issues stored before issues.issue_json existed rebuild their
colophon and Behind the paper from issues.report_json / the run row and
candidate_runs, and recover the World Briefing chapter from the EPUB on
disk. The dev seed mirrors that legacy shape.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NHyYupFdBiR4VfoUM7NjSM
2026-09-03 20:28:16 +00:00
thalladaandClaude Fable 5.1 eb960f1b57 Web dashboard step 7: docs, users page, polish
Users page, README and config.example updates, implementation notes, the
rollout runbook, site-layout 404/500 pages, human-readable download sizes,
dark-mode and narrow-screen polish, and a smoke test over every dashboard
route; also removes zdiff3 ancestor markers left by earlier merges.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NHyYupFdBiR4VfoUM7NjSM
2026-09-03 18:19:08 +00:00
thalladaandClaude Fable 5.1 4995a8bd5d Web dashboard step 2: full issue pages, article chapters, POST /rate
Signed-in issue page with the Brief, downloads, index and colophon; article,
World Briefing and Behind the paper pages under the login guard; the rating
widget with its fetch enhancement; the admin-only POST /rate writing
dashboard-attributed rating events; the HMAC confirmation page links to the
site.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NHyYupFdBiR4VfoUM7NjSM
2026-09-03 04:56:29 +00:00
thalladaandClaude Fable 5.1 69cd736455 Web dashboard step 1: migration, sessions, users CLI, public site
Migration 0004 (users, sessions, config_changes, profile_versions, jobs,
rating_events.user_id, runs.report_json, issues.issue_json), the issue
snapshot writer and loader, the web module skeleton with layout and static
assets, axum-login/tower-sessions over a sqlx session store, password-auth
users with a CLI, the login throttle, the origin check, security headers,
and the public issue pages, archive, Atom feed and robots.txt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NHyYupFdBiR4VfoUM7NjSM
2026-09-03 04:38:22 +00:00