df1a7aebc9
- 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
134 lines
4.3 KiB
Markdown
134 lines
4.3 KiB
Markdown
# 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
|
|
|
|
```org
|
|
#+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-mode` compatibility,
|
|
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 from `spine-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 `:shelf` keys.
|
|
- `spine-books-first-book-has-all-fields`: adds `:shelf` assertion.
|
|
- `spine-books-missing-properties-are-nil`: adds `:shelf` check.
|
|
- `spine-index-model-*`: rewritten — tests shelf grouping, shelf
|
|
filtering, empty-shelf inclusion. Status-based tests removed.
|
|
- `spine-add-book-*`: tests require `:shelf` arg. 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-shelves` tests: parses shelf names, includes empty shelves.
|