4.4 KiB
Edit Book Actions — design spec
Problem
The index page shows books grouped by status but provides no way to change a book's status, add reading notes, or record ratings. Users must edit the Org file manually. We need lightweight, context-sensitive edit actions accessible from the agenda view.
Interaction model
Click a book row on the index page → the footer (minibuffer) shows action links tailored to that book's current status. Clicking an action either performs it immediately (status transitions) or shows a lightweight prompt page (add note, mark read with date).
No JavaScript. All actions are plain <a href> links or simple HTML forms.
Actions per status
WANT
- Mark reading — immediate: sets TODO to READING
- Add note — prompt page: date + textarea, appends a note
READING
- Mark read — prompt page: date picker (default today) Rating is deferred (see out of scope).
- Abandon — immediate: sets TODO to ABANDONED
- Add note — prompt page: date + textarea
READ
- Read again — immediate: sets TODO to READING
- Add note — prompt page: date + textarea
ABANDONED
- Try again — immediate: sets TODO to READING
Implementation
New Org write functions (spine.el)
spine-set-status (id status &optional date)
Finds the book by :ID: property, changes its TODO keyword to status, and records
the state transition in the LOGBOOK drawer (using org-todo). If date is provided
(for READ status), sets a :READ_DATE: property.
spine-add-note (id text &optional date)
Finds the book by :ID:, appends - [YYYY-MM-DD] text to the headline body. Date
defaults to today if not provided. Follows the same pattern as spine-add-book:
find-file-noselect, modify, save, kill-buffer.
spine-set-rating (id rating)
Sets the :RATING: property on the book.
Handler: httpd/edit
Single handler dispatching on action query param:
action=status— callsspine-set-statuswithidandvalue(the target TODO state). Redirects to/index?id=<id>.action=note— GET shows a prompt page with date + textarea for the book. POST processes: callsspine-add-notewithid,text, and optionaldate. Redirects to/index?id=<id>.action=rating— callsspine-set-ratingwithidandvalue. Redirects to/index?id=<id>.- Unknown action or missing
id— redirect to/indexsilently.
The prompt page for notes is a minimal HTML page (no Mustache template needed — small
enough to inline): shows the book title as context, a date input (default today), a
textarea, and a submit button. POST data includes id, action, date, text.
Model: spine-index-model minibuffer extension
When a book is selected (selected-id matches), the model gains a minibuffer field:
("minibuffer" .
(ht ("actions"
(list
(ht ("label" "Mark reading") ("href" "/edit?id=X&action=status&value=READING"))
(ht ("label" "Add note") ("href" "/edit?id=X&action=note"))))))
Actions are computed from the selected book's :status property. The exact set
follows the table in "Actions per status" above.
Template: index.mustache minibuffer section
The existing minibuffer footer placeholder is replaced with a dynamic version:
{{#minibuffer}}
<footer class="minibuffer">
<span class="muted">M-x</span>
<span style="display:flex;gap:.5rem;flex-wrap:wrap">
{{#actions}}
<a href="{{href}}" class="pill {{command}}">{{label}}</a>
{{/actions}}
</span>
</footer>
{{/minibuffer}}
Styled as pill links consistent with the existing pill classes. M-x prefix keeps
the Emacs feel.
Testing
New test file test/spine-edit-test.el:
spine-set-statuschanges TODO state correctly for each transitionspine-set-statusrecords LOGBOOK entryspine-add-noteappends a note with the correct formatspine-add-notedefaults to today when no date providedspine-set-ratingsets the rating property- Model includes
minibufferfield when book is selected - Model has correct action set per status
Out of scope
- Editing book properties (author, format, ISBN) — add-book covers creation; editing those can come later if needed
- Deleting books — add-book is additive; no delete flow yet
- Rating prompt on the "Mark read" page — deferred; rating can be set separately
- Inline editing in the detail section — the minibuffer actions are the primary surface for now