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

Time in words: src/when.ts, and a countdown that does not move the page #39

Merged opened this

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

Merged

`bun test` is green (39 pass) and the branch is pushed.

shipped production is serving it deploy 6ecafc0

opened this

bun test is green (39 pass) and the branch is pushed.

Time is now said in one place. static/when.js holds the vocabulary — units d h m s, at most two, largest first, "just now" for the minute either side of now, "lapsed" the second a deadline arrives — as pure functions with now handed in: remaining, relative, absolute and phrase. src/when.ts is that file with types on it plus timeTag, the escaped <time class="when" data-when="…" datetime="…" title="…"> every page uses. The words live in static/ rather than src/ on purpose: the browser needs exactly the same ones, since the server renders a countdown at paint and the page re-renders it a second later, and two implementations of relative time are two implementations that drift. One file, two runtimes; src/when.ts is the only import that crosses that way, and AGENTS.md now says so.

static/live.js ticks every clock on the page from its own datetime attribute — one interval for the whole document however many clocks it has, cleared while the tab is hidden, no request of any kind — and sets the reader's local time in the hover text, the one thing the server cannot know. With JavaScript off the server's words are correct at paint and simply grow stale. static/app.css gives the slot tabular numbers and a width in ch wide enough for the longest phrase its kind can say, so a tick never reflows the row.

Beyond the issue's list I deleted two private copies of the same idea rather than leaving them beside the new one: src/page.ts had its own ago() using toLocaleDateString on the server (a locale the server cannot know), and src/tty.ts sliced an ISO string by hand. Both now call the module, so a note reads "20m ago" on the page and over ssh alike and the page's <time> actually ticks in the preview — the rendered wording changes, nothing else does. I did not add any page or route of its own: #11, #12, #14 and #16 are what this lands for. No dependency was added.

To try it: open / signed in, add a note, and watch its time — "just now" becomes "1m ago" a minute later with nothing fetched and nothing shifting; hover it for your local time; view-source: shows the UTC instant in datetime. src/when.test.ts is the table of inputs to expected strings, including 0s/59s/60s/90m/23h59m/24h/8d, a negative duration, a value that is not a time at all, and a check that nothing a caller passes is echoed into the markup.

🤖 Generated with Claude Code

Closes #20

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs