- Add spine--format-defs defconst shared between index and book models - Add spine-book-model with status extraction, format selector, notes, recommendation - Add templates/book.mustache (Pico CSS, journal-style layout) - Add httpd/book handler serving the detail page - Link book titles in index view to /book?id=X - Add spine-book-model-test.el with 13 tests
4.3 KiB
Shelves data model
Replace the TODO-state-based book model with shelf-based organization. Shelves are top-level Org headings; books are nested underneath them.
Motivation
Books-as-top-level-headings with TODO states (WANT/READING/READ/ABANDONED) made the file's structure reflect lifecycle status rather than organizing concepts. The user wants arbitrary grouping ("shelves") as the primary organizing axis. TODO states are removed — logbook entries (already present) record when reading happened.
Org data model
#+TITLE: Spine
* Fiction
** Piranesi
:PROPERTIES:
:AUTHOR: Susanna Clarke
:FORMAT: ebook
:REC_BY: Alex
:REC_NOTE: Le Guin at her most human
:RATING:
:ADDED: [2026-04-01]
:LAST_MODIFIED: [2026-04-01]
:ID: 5e8d-pir
:END:
::LOGBOOK:
- State "READ" from "WANT" [2026-01-10]
::END:
- [2025-11-22] The pronoun game is a genuinely fresh take on identity.
* Non-Fiction
** The Checklist Manifesto
...
- Shelves are level-1 headlines (
*) - Books are level-2 headlines (
**) - Books keep their TODO keyword syntax for
org-modecompatibility, but the app ignores it entirely - The
#+TODO:line is removed — no TODO states - Empty shelves (a level-1 headline with no children) are valid and appear in the UI
- LOGBOOK drawers remain for historical state-change records but are not surfaced by the UI
Key functions
spine-shelves (new)
Returns an ordered list of shelf names (strings), one per level-1 headline that is not a COMMENT or archive target. Empty shelves are included.
spine-books (modified)
Walks level-1 headlines as shelves. For each shelf, walks level-2 children
as books. Returns flat plist list; each book plist gains a :shelf key
with the parent headline text. No status/TODO filtering.
Places that previously checked Todo state (e.g. spine-set-status) are
removed — the only remaining mutable fields are notes, rating, format.
spine-index-model (modified)
Groups books by :shelf instead of :status. Accepts an optional
shelf filter (instead of read/want/all) to show one shelf.
Includes shelf nav items for all shelves (even empty ones, from
spine-shelves).
Removes:
- Summary groups (READ/ABANDONED links)
- Concise view truncation
- Status-related book model fields (status_class, status_label)
spine-add-book (modified)
Takes required :shelf argument. Inserts the new book heading under
the matching level-1 shelf headline instead of at file end. If the
shelf doesn't exist, signals an error (shelves are created manually).
Removes:
org-todo "WANT"call#+TODO:file initialization- Category field logic (replaced by shelf)
Functions removed
spine-set-status— no status to change. Logbook records history.- All HTTP endpoints and UI actions that invoke it.
UI changes
Index view (index.mustache)
- Titlebar:
N books · M shelves - Filter bar: replaced with shelf nav — links for each shelf name plus "All" (default). Active shelf highlighted.
- Groups: one section per shelf. Each shelf shows its books in the existing row format, minus the status pill.
- Empty shelves: shown as a group with the shelf name and "(empty)" subtext. No expandable books.
- Minibuffer (when a book is selected): simplified to two actions — Add note, Set rating. Same for every book.
Add form (add.mustache)
- "Category" free-text field replaced with "Shelf"
<select>dropdown populated fromspine-shelves. - Required. No datalist — only existing shelves.
- All other fields unchanged.
Sample file
sample-books.org is restructured to the new shelf layout with
shelves like "Fiction", "Non-Fiction", "Science Fiction".
Test changes
spine-books-parses-sample: loads restructured sample, asserts flat book list with:shelfkeys.spine-books-first-book-has-all-fields: adds:shelfassertion.spine-books-missing-properties-are-nil: adds:shelfcheck.spine-index-model-*: rewritten — tests shelf grouping, shelf filtering, empty-shelf inclusion. Status-based tests removed.spine-add-book-*: tests require:shelfarg. Removes status check. Tests that books land under the correct shelf heading.spine-edit-*: keeps note/rating tests. Removes status-change tests. Updates model-assertion tests for new minibuffer shape.- New
spine-shelvestests: parses shelf names, includes empty shelves.