Public: anyone can read this; the crew can change it.

The page is a timeline: server-rendered, filtered by link, live as it happens #34

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`/` now renders the newest 50 events server-side, grouped under a heading per day. Each row's source and kind double as filter chips: clicking one sets `?source=&kind=` in the URL, clicking the active one clears it — plain links, working with JavaScript off. An invalid filter shows the action's own message above the list rather than a blank page, and a filtered/reload round-trips through the URL (…

shipped production is serving it deploy 460b31c

opened this

/ now renders the newest 50 events server-side, grouped under a heading per day. Each row's source and kind double as filter chips: clicking one sets ?source=&kind= in the URL, clicking the active one clears it — plain links, working with JavaScript off. An invalid filter shows the action's own message above the list rather than a blank page, and a filtered/reload round-trips through the URL (tested).

static/live.js no longer refetches the whole page on a live event; it builds the new row straight from the SSE payload with textContent (never innerHTML, since that data came from another app), skips a duplicate id, and only inserts it when it matches the URL's current filter, creating a new day heading if needed.

Added a small GET /docs/:file route (src/http.ts, src/routes.ts) so the empty state — four headers and one curl, matching client/relay.ts — can link docs/adopting.md for real. CSP and the security floor are untouched; bun test is green (61 tests, including new coverage for escaping, the filter round-trip, and the empty-state doc link).

What I chose not to do: give every inline per-row source/kind chip the full 44px tap target — that would wreck the list's density on a phone, so only the toolbar's standalone active-filter chips and "clear all" get the full 44px, while inline ones stay compact but still comfortably tappable (32px, padded).

To try it: open /, post a couple of events from two different sources/kinds via POST /api/events (see the empty state or README), click a badge to filter, reload to see it persist, then post another event and watch it appear at the top live without a reload.

🤖 Generated with Claude Code

Closes #9

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs