Click "⤤ edit" on a section body to replace the rendered segments with a
multi-line editor seeded from the raw body text. Save writes the body
back; the server reparses on commit. Cancel/Escape discards; Ctrl+Enter
saves.
Model:
- DocumentModel::bodyText(row) returns the raw /body for seeding the
editor; setBody(row, text) writes /<doc>/<id>/body and reload()s, since
a body edit can change the section's substructure.
Body editor (OutlineView):
- The body block now shows while editing even when empty, so an empty
body can be opened. The segment Repeater is gated off (empty model)
while editing and a TextArea in a ScrollView takes its place, with a
toolbar (Insert timestamp…, Cancel, Save).
Timestamp-at-cursor insertion:
- DatePickerDialog gains an "insert" mode: openInsert() shows an
Active <>/Inactive [] selector and, on Insert, emits timestampAccepted
with the built timestamp instead of writing a planning field. The
shared picker inserts it at the body editor's cursor (via the view's
insertTarget).
The server's set_section_body already writes only the region after the
headline/planning/properties, so planning lines and property drawers are
preserved across a body edit.
Verified over 9P: body round-trips (seed read, multi-line write with an
inserted <...> timestamp and a checkbox item, read back), the section and
its siblings stay intact, and a section with SCHEDULED + a property
drawer keeps both after a body edit. GUI builds clean. VISION v0.4
updated: in-place editing and keyword/priority/tag/timestamp manipulation
done.
First direct-manipulation edit (v0.4): clicking a headline's keyword pill
cycles its TODO state, each change a single 9P write. Left-click / wheel-up
advance (TODO -> NEXT -> ... -> DONE -> none -> TODO); right-click / wheel-down
step back.
Per-document keyword set (server):
- keywords() previously advertised a hardcoded "TODO NEXT WAITING | DONE
CANCELLED", but the parser only recognizes NEXT/WAITING/CANCELLED when the
document declares them via #+TODO:. Writing an unrecognized keyword merged it
into the title and changed the section's slug id.
- keywords() now returns the parser default "TODO | DONE"; new keywords_for(doc)
returns a document's declared #+TODO: sequence or that default. Exposed as a
per-document /<doc>/keywords file.
GUI:
- DocumentModel::setKeyword writes /<doc>/<id>/keyword and refreshes the row;
cycleKeyword reads the per-document /<doc>/keywords so it only cycles to
keywords the parser recognizes (keeping the section id stable), with a
trailing empty state to clear the keyword.
- The keyword pill gains a hover highlight and a MouseArea for click/wheel
cycling.
Verified: server tests (66 passed, +3: default keyword set, declared sequence,
id stable across set+clear). Live 9P run cycling both a plain document
(TODO|DONE) and one with a #+TODO: sequence confirms the section id stays
stable through a full cycle and every transition writes correctly; empty-write
clearing is covered by a unit test (the ork CLI can't pass an empty arg, but
the GUI's p9 client sends a real empty write). GUI builds clean.
Also refresh VISION.md milestones: v0.3 done; checkbox toggling, code
execution, and keyword cycling ticked under v0.4.
The vision predated the 9P pivot and the GUI direction. Correct it:
- Architecture: clients (GUI, CLI, Emacs/vim, agents) speak 9P to
ork-server; drop the C FFI layer entirely.
- Replace "keyboard-first, mouse optional" with "the document is the
control surface": actions live on objects, mouse and keyboard equal,
no menu trees or button walls.
- Add an explicit Editing Model: structured render, per-section
raw-text editing with reparse-on-commit, and direct-manipulation
structural edits that each map to one 9P operation.
- Refresh milestones (v0.1 parser/CLI and v0.2 9P server/client done;
GUI is next) and the rationale (add the "Why 9P" section).