Add a permanent "Latest" lane beside "Highlights"

Restructure the nav around two permanent lanes, then the reader's chosen ones:
"Highlights" (the curated daily brief — formerly "Today") and "Latest" (the
freshest accepted stories, newest-first). Now that the gate is tight, a
chronological "incoming" feed is safe to expose.

* feed(): new sort="latest" (pure recency) alongside the default best-first
  rank; /api/feed exposes sort=ranked|latest (validated). Still accepted-only
  and boundary-respecting either way.
* lanes.py: two pinned lanes (Highlights + Latest) instead of one.
* Home: "Latest" view + "Load more" pagination for every feed view (offset-
  paged, de-duped). Mobile bottom bar gains a Latest tab.
* LanePicker shows both pinned lanes; nav rail renders them first.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-06 15:56:48 -04:00
parent d87347b032
commit c25e14ed6a
8 changed files with 143 additions and 37 deletions
+8 -4
View File
@@ -1,14 +1,18 @@
<script>
// Mobile-only primary navigation. Today = the brief, Browse = mood/topic
// discovery, You = account + personal controls (shows the user's avatar in).
// Mobile-only primary navigation. Highlights = the brief, Latest = the
// chronological feed, Browse = mood/topic discovery, You = account.
import Avatar from './Avatar.svelte';
let { active = 'today', onToday, onBrowse, onYou, user = null } = $props();
let { active = 'today', onToday, onLatest, onBrowse, onYou, user = null } = $props();
</script>
<nav class="bottomnav" aria-label="Primary">
<button class:active={active === 'today'} onclick={onToday}>
<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="4.2" fill="none" stroke="currentColor" stroke-width="1.8" /><path d="M12 3v2M12 19v2M3 12h2M19 12h2M5.6 5.6l1.4 1.4M17 17l1.4 1.4M18.4 5.6L17 7M7 17l-1.4 1.4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" /></svg>
<span>Today</span>
<span>Highlights</span>
</button>
<button class:active={active === 'latest'} onclick={onLatest}>
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h10" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" /></svg>
<span>Latest</span>
</button>
<button class:active={active === 'browse'} onclick={onBrowse}>
<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="1.8" /><path d="M15.5 8.5l-2 5-5 2 2-5 5-2z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" /></svg>