The family-lens hardening: library_visibility was only enforced where
file bytes flowed (download/reader/OPDS) — metadata surfaces leaked
hidden libraries to any authenticated account, and registration was
open by default with no admin-side user creation.
- Series are grouped PER LIBRARY now (same name in two libraries =
two entries: manga adaptations / LN splits no longer merge);
/api/series aggregates only the requester's VISIBLE libraries and
/api/series/books REQUIRES a visible library_id (breaking contract;
web + app clients updated in the same change).
- /api/media-items, item detail, and the reader content endpoints
(chapters/panels/outline/THUMBNAILS — page images leaked unguarded
next to a gated GetPage) all enforce the visible set; resolveLibrary
no longer honors a hand-typed library_id outside the visible set.
- Annotation/note/bookmark mutations (update/delete/tombstone, sync
LWW paths incl. conflict resolution) carry ownership predicates:
another account's bare row id is a 404, not a silent delete.
- Admin user creation: POST /api/auth/users (AdminMiddleware) + a
Create User card in the admin Users UI — accounts stay creatable
with self-service registration OFF, which is now the schema default
(fresh installs; the first-admin bootstrap exception is unchanged).
- Tests: series contract re-pinned (per-library, visibility 403s),
new ownership + admin-create integration suites, OPDS all=1 test
now passes an explicit page size (shared-DB growth made the fresh
books fall off page 1), device-cap helper switched to the admin
endpoint.
One boolean (Security > Public Registration) switches the whole public
signup lifecycle: front page + login links, the logged-out sidebar's
Create-an-account (via a package-level templates hook so shared page
templates keep their signatures), GET /register -> 302 /login, and an
early 403 on POST /api/auth/register. First-user exception preserved:
zero admins keeps every route and link reachable for bootstrapping —
same 'users exist' reasoning as the setup gate. Admin user creation is
unaffected by design.
Live E2E verified: flag off hides all surfaces and blocks the POST
(403), flag on restores them (201); the admin UI renders the row
automatically.
Regenerates all *_templ.go files after the templ generate pass (version
stamp updates across the board; functional changes only in
admin_archived_templ.go and admin_library_templ.go, which were committed
with their features). web/static/style.css and web/static/htmx.min.js are
refreshed outputs of npm run build (tailwind pass over the current
templates; htmx copied from the current node_modules version).
Bring the tighten-ui brand treatment into new-ui:
- Replace the emoji book (📚) in the sidebar header with the book-open
icon rendered in the theme accent color, matching the tighten-ui
header brand (templates/header.templ).
- Add web/static/favicon.svg (book-open glyph, tokyo-night accent
#7aa2f7 stroke) and reference it from the <head> of all 27 page
templates, so the favicon is present on login/setup/error pages too.
- Regenerate templ output for all affected templates.
Upgrade from templ v0.3.1001 to v0.3.1020. Generated code changes include
JoinStringErrs -> ResolveAttributeValue and removal of manual EscapeString
calls (now handled internally by ResolveAttributeValue).
- bookshelf.templ: Fix form field name from "library" to "library_id"
to match the handler's QueryParam("library_id"). Add "All Books"
as the default option in the library filter dropdown. The bookshelf
uses its own inline filter, NOT the universal library switcher.
- collections.templ: Remove @LibrarySwitcher from the collections list
page — collections are not library-specific, so the switcher was
misleading. Fix data-id interpolation bug where {collection.ID} was
rendered as literal text instead of being interpolated.
- series.templ: Replace inline library selector with the universal
@LibrarySwitcher component. SeriesCard links no longer include
library_id since series detail always shows all books.
Reverts generated Go template files from templ v0.3.1020 back to
v0.3.1001 output. Changes include filename path prefix adjustments
(admin_library.templ → templates/admin_library.templ) and attribute
handling differences (ResolveAttributeValue → JoinStringErrs + EscapeString).
templ v0.3.1020 generates different code than v0.3.1001 (uses
ResolveAttributeValue for attribute handling). Regenerated all 33
_templ.go files to match the new runtime.
Create a new /series/detail?name=X&library_id=Y SSR page that shows
all books in a specific series, replacing the broken approach of
linking to /bookshelf?series_filter=X (the bookshelf SSR handler
ignores all filter query params).
The series detail page features:
- Back link to /series browse page
- Library selector dropdown (full page navigation on change)
- Series name header with book count badge
- Book grid using the shared BookCard template
- Empty state for series with no books
Update all links to point to the new page:
- Series cards on /series browse page
- Series badge on book detail page
- JS-rendered cards in series.ts switchLibrary
Add seriesDetailPage Alpine component for the detail page's
library switcher (simple navigation, no AJAX needed).
Create templates/series.templ with:
- Library selector dropdown (sticky, same pattern as dashboard)
- Loading spinner overlay for AJAX library switching
- Series grid with stacked-cascade multi-cover cards
- Empty state when no series found
- Pagination with Previous/Next links
- SeriesCard sub-template linking to filtered bookshelf view
Add 'Series' nav link in header between 'All Books' and 'Collections'.
Make series badge on book detail page clickable, linking to
/bookshelf?series_filter=<name>&sort=series.
Add 'Continue Series' option to restore system collection modal.