Changelog

What's changed on this site recently. Full commit log on GitHub.

  1. 🎧 badges now render on tag + category pages

    The narration indicator was only threaded through on the homepage and /posts listing. Tag + category pages showed the cards but no badges. Extracted the batch narration lookup into a shared helper so all five listing surfaces render badges consistently.

    audioui
  2. Fixed site search returning 401 for anonymous visitors

    EmDash's built-in search endpoint requires content:read scope, so typing in the nav search box silently returned zero results for everyone not logged in. Shipped /api/search.json as a public proxy with scored results; intercepted the client-side fetch so the existing UI keeps working. Real-browser verified via Playwright.

    searchfix
  3. Cmd+K keyboard shortcut for search

    Standard everywhere — now here too. ⌘K (or Ctrl+K on Linux/Windows) focuses the nav search from anywhere on the page; Esc blurs back out. Visible ⌘K badge inside the input disappears while typing.

    uikeyboard
  4. Reading progress + back-to-top on post pages

    Thin accent-colored strip at the top of the viewport fills as you scroll through an article. Circular back-to-top button fades in past 700px scroll. Both driven by a single rAF-throttled handler.

    ui
  5. Podcast listing: duration preview, 500KB lighter page

    Episode rows now show approximate duration (6:45, 3:43, etc.) in the meta before clicking play — computed from MP3 byte size. Pair change: preload='none' on every episode <audio> since we no longer need metadata to render the duration. Saves ~500KB per page load.

    audioperformance
  6. Manual dark/light theme toggle

    Sun/moon button in the nav flips the theme and persists preference for a year. Returning visitors get their theme on first paint — no flash.

    uidark-mode
  7. First successful scheduled backup in weeks

    Cron-driven D1 backups had been silently failing with HTTP 522 because the Worker's scheduled() handler tried to fetch its own public hostname. Refactored to call the shared runBackup module directly; all 19 tables now dump to R2 every hour. 5 regression tests lock in the shape.

    infrafix
  8. Public operational status page

    /status now shows cron health, content counts, and every discovery URL in one place. Admins get a green/yellow/red dot right in the nav via /api/cron-health.json.

    infraui
  9. WCAG AA across light + dark mode

    Ran axe-core against 9 pages in both color schemes. Went from 19 serious violations to 0. Gruvbox palette darkened where needed to clear 4.5:1 contrast on both bg and surface colors.

    a11y
  10. 6-script real-browser verify pipeline

    pnpm run verify now drives Playwright + axe-core + link crawler + feed validator. Every deploy gates on vitest (90 tests). Full pipeline in scripts/smoke*.mjs.

    testing
  11. Blog → Podcast

    Every AI-narrated post is now a podcast episode. /podcast.xml is iTunes-compliant (with Podcast 2.0 transcripts). /podcast landing page lets humans subscribe via Apple / Overcast / Pocket Casts. /rss.xml and /atom.xml also carry audio enclosures.

    audiodistribution
  12. Browser SpeechSynthesis fallback on silent posts

    Every published post now has a playable audio option. If the MiniMax narration isn't ready yet, a 'Read aloud' button uses the browser's Web Speech API instead — clear UX rather than silent omission.

    audioa11y
  13. Agent discovery surfaces

    Shipped /openapi.json (with x-x402-payment extension), exposed the built-in EmDash MCP server, added Organization + OfferCatalog + Person + PodcastSeries + ItemList JSON-LD, and linked it all from /docs/agents. Every paid surface is discoverable programmatically.

    agents
  14. Three-layer LCP optimization

    Hero image now preloads in the <head>, loads eagerly with fetchpriority=high, while below-fold grid cards stay lazy. Hover-prefetch on desktop internal links drops click-to-paint latency to near zero.

    performance
  15. PWA manifest + iOS meta

    Site is now installable as an app on iOS, Android, and Chrome desktop. Home-screen shortcuts jump straight to Posts, Podcast, or Skills.

    mobile