LinkGameDialog (user request or auditor link), EditionsCard on the
details page, LinkQueuePage and AuditLogPage for auditors with paging,
hideDuplicates filter and editions badge in the library, audit menu
entries, de/en texts.
F16 F17 F18 F19 F20 F21: import polling backs off and pauses in a hidden tab, one date format module, fake timers in the import tests, error codes instead of raw messages, one safe storage wrapper, dead code.
The token is no longer stored; a tbp_session flag remembers the
login. apiFetch renews the session once on a 401 and sends the
request again. Parallel requests and other tabs share one refresh,
because Auth revokes the session when a refresh token is used twice.
Only a 401 from the refresh ends the session; a 502 or no network
shows the normal error.
All authenticated calls now use apiFetch, and only utils/auth.js
touches the stored token. The cookie login can then change these two
files instead of every page.
Every 401 from Auth or Core now goes through useSessionExpired: the login page
says the session expired and returns to the last page after login. App no
longer removes a token that only the browser clock calls expired, so a backend
401 is the one signal. A 403 on the profile now shows an error instead.
Add shadcn/ui (Radix + Tailwind 4) components, zinc/violet theme tokens and self-hosted Inter. New header with user menu, Steam cover art, skeletons and toasts. Replace @mdi/font with lucide-react. Fix: top-owned list on the landing page only showed with top-played entries; numbers use the app language.
Remove the placeholder tiles and the settings tile, fix the mobile header width. iconUrl/formatPlaytime move to utils/games.js (599 min now shows 10h, not 10.0h), and the sync button logic to the useForceSync hook shared with the library.