Library curation + navigation: delete, upload, site header, kobo removal fix #7
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "curation-impl"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implements the full curation spec from
docs/superpowers/specs/2026-04-11-curation-and-nav-design.md: delete books from the web UI, upload books through a header action, and replace the corner dropdown with a thin top-bar nav. Plus an incidental but necessary Kobo bug fix that the delete flow would otherwise trip.Twenty small commits, one per plan task (with two gofmt fixups along the way), closed out through Tasks 1–18 of
docs/superpowers/plans/2026-04-11-curation-and-nav.md. Fullgo test ./...green across all nine packages. Final full-branch code review completed against the spec — one Important finding (EXDEV safety inlibrary.Archive/Restore) was addressed inline in the last commit.What you get
delete bookbutton. First click arms it in stamp red with a "really?" prefix; second click within 5s commits. No modal. Soft-delete: the row stays (so Kobo removal tracking can still find the entitlement UUID), the files move to<BOOKS_DATA_DIR>/archived-books/<hash>.<ext>, the book auto-unshelves in the same DB transaction.+ uploadaction in the site header opens a hidden file picker, POSTs the.epubto/upload, runs it through the same importer pipeline as the inbox watcher, and lands on the new book's detail page. Uploading a file whose hash matches a previously-archived book restores the original row from the archive instead of creating a duplicate — the book's ID, UUID, and all metadata survive the round-trip. 50 MB cap enforced beforeParseMultipartFormis called.shelf · library · erratanav with an active-state stamp underline, plus the upload action on the right. Active page is driven by the existingPageData.Sectionfield. The← libraryback link on the book detail page went away with it — the library tab in the header now serves that role.buildRemovedEntitlementused to synthesize a fake UUID viauuidFromInt(bookID). In the pre-delete world this never mattered — nothing ever routed a book through theremovedbranch after we'd previously delivered a real UUID. The moment delete-of-a-shelved-book ships, every removal envelope would carry a UUID Nickel never saw, and the device would silently fail to forget the book. Fixed by a newstore.GetBookByIDIncludingDeletedmethod that survives theWHERE deleted_at IS NULLfilter, plus changingbuildRemovedEntitlement(book)to readbook.UUIDviaentitlementID.uuidFromIntdeleted as dead code. Regression test pinned.?flash=<url-encoded>query parameter on the redirect target.html/templateauto-escapes on render ("in the test assertions proves it), the flash clears on the next navigation when the URL no longer carries the parameter, and an explicit test asserts the XSS-safe rendering path. Single-user tool, minimal surface area, no new crypto.What's not in this PR (deliberate)
book_authors/device_removals/reading_state/kobo_sync_session_books— those rows stay put and the Kobo delta still handles them correctly.File structure
Plan-compliant: new handlers (
delete.go,upload.go) are their own files ininternal/web/, test files (delete_test.go,upload_test.go,flash_test.go,roundtrip_test.go) similarly split. No monster-file growth.Test plan
go test ./...— all 9 packages passgo build ./...— cleangofmt -l— clean on every touched file (two fixup commits for drift along the way)superpowers:code-reviewersubagent) — one Important finding addressed inline in the last commitbookscontainer, trigger a sync, walk through delete + upload + archive-restore end-to-end. The existing Assassins Quest row should survive the schema migration untouched.Intended to be squash-merged. The 20 commits are a breadcrumb trail, not a curated history.
🤖 Generated with Claude Code