RDLTR

Notes from every release

This release focuses on UI polish and list interaction improvements. The privacy toggle moved into the menu, sorting options became proper links, link metadata layout was reorganized, and SSE updates now reconnect and preserve selection state without page reloads. Several visual consistency fixes landed across links, underlines, and icons, plus a username length limit and internal tooling updates.

New

  • Stats link added to homepage empty stateThe homepage empty state now includes a link to the stats page, giving new users a quicker path to that section. 8a4f5d6
  • Fine print copy added to the premium pageThe premium page now includes additional explanatory copy in the fine print section. 9b97fb0

Improved

  • Privacy toggle moved into the menu with a keyboard shortcutThe privacy lock toggle is now accessible from the menu (P shortcut) rather than inline. The lock icon is only shown when a list is public, and toggling privacy now reloads the page to reflect the new state. aa060bd 9bcd1b6 51b3338 af509a4
  • Sorting options are now clickable links on desktop and mobileSort controls were refactored into proper anchor links using shared components, making them work consistently across device sizes and easier to maintain. 7228801
  • Link metadata right-aligned with username shown before dateLink metadata in list views is now right-aligned and the display order swapped to show username before date. The first item is also auto-selected when SSE updates arrive. b0cd076
  • Consistent link underline style and hover transitions across the appAll links and the logout menu button now share the same underline thickness and a smooth hover transition, replacing inconsistent per-page styles. c964665 d02c0ee e5945f1 51e1b7e
  • Navigation icons replaced with Lucide user iconsThe triangle indicators in the navigation bar have been replaced with Lucide user icons for a cleaner look. a512da1
  • Usernames are now limited to 15 charactersRegistration enforces a maximum username length of 15 characters. Existing usernames are unaffected, but any new sign-up attempt with a longer name will be rejected. 811ab91

Fixed

  • SSE reconnects without a page reload and preserves chevron selectionThe live-update connection now reconnects in the background when it drops, and the selected item's chevron stays in place when items are prepended or removed via SSE swaps. 6f1f8bd 98571d4

This release adds a stats page and richer empty states with reading streaks, favicons, and a confetti celebration for owners; improves the premium upgrade flow with a privateIntent option so paying users can go private in one step; switches Twitter metadata extraction to the vxtwitter API for better title coverage; adds fuzzy duplicate detection for saved links; and ships a comprehensive Playwright E2E test suite covering registration, keyboard shortcuts, payment flows, and session handling.

New

  • Stats page and per-user reading statsA new /~/stats page is now linked from the help page. Per-user stats now track streak, today's reads, median daily reads, pace (average days from save to read), top domains with favicons, and a global aggregate view across all users. e2ddb64 0784bf3 f9ce5fc 4d55a40
  • Richer empty states for owners, visitors, and the homepageThe empty-list view for owners now shows reading stats, a streak counter, favicon thumbnails of top domains, and a confetti celebration. Visitors, the homepage, and the 404 page each get distinct prose empty states. A nudge message appears until stats unlock at a threshold read count. e2ddb64 0784bf3 2d8c4e9 cabb60a
  • Premium upgrade flow supports going private in one paymentA new privateIntent option threads through the Stripe checkout and webhook so that clicking the lock button as a non-premium user on a short username triggers payment and enables private mode in a single step. The owner of an unpaid short username now sees a pay button; visitors see a normal profile rather than a payment-required message. d3384a9 cabb60a

Improved

  • Save script falls back to a random Lobsters story when no URL is givenRunning the save script without a URL argument now fetches a random story from Lobsters and saves it, making it easier to seed a local database for development. f9ce5fc

Fixed

  • Twitter/X metadata now uses the vxtwitter APILink titles for tweets and X articles are fetched via the vxtwitter API, fixing cases where URL-only tweets showed a t.co URL as the title instead of the article headline. 113e7dc
  • Fuzzy duplicate detection catches near-identical link titlesThe duplicate check now uses 80% prefix similarity instead of exact title matching, so variants like titles with '[video]' or '| Hacker News' suffixes, or cases where the new title is a prefix of an existing one, are correctly identified as duplicates. 8912fac
  • Empty state visibility fixed when reading the last articleThe CSS :has() selector was not re-evaluating after HTMX removed items via out-of-band swaps. An explicit toggleEmptyState() call now shows or hides the empty state element whenever the list changes. 2120954