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.
Add timestamp editing to the headline: a 📅 button and clickable
scheduled/deadline labels open a date picker that writes the planning
field. One 9P write per change; the id is unaffected.
GUI:
- New DatePickerDialog.qml: a MonthGrid calendar with month/year
navigation, a Scheduled/Deadline selector, optional time (HH:MM) and
repeater (+1w / ++1m / .+2d) fields, and Set/Clear/Cancel. It parses an
existing <...>/[...] value to prefill, and builds the inner timestamp
"YYYY-MM-DD Day [HH:MM] [repeater]" for the server to wrap.
- OutlineView: a shared DatePickerDialog instance; the calendar button
(prefills from scheduled, else deadline, else today) and clicking a
timestamp label open it on the right field.
- DocumentModel::setScheduled/setDeadline write /<doc>/<id>/{scheduled,
deadline} and refresh the row.
Server fix (set_planning_field):
- The old logic rebuilt the planning line by splitting on whitespace,
which breaks timestamps that contain spaces (day name, time, repeater):
replacing a date-only SCHEDULED with a time-bearing one left a stray
trailing token ("... 14:30> Sun>") and could duplicate the line, so the
field then failed to parse. Rewrite it to parse the planning line into
(keyword, bracketed-timestamp) pairs via new parse_planning_line, update
or remove the target field, and re-emit in canonical order
(SCHEDULED, DEADLINE, CLOSED) via emit_planning_line. CLOSED now wraps
as inactive [...]; SCHEDULED/DEADLINE as active <...>.
Verified: server tests (72 passed, +4: time/repeater round-trip, two
coexisting fields without duplication, planning-line parse, and an exact
repro of the live scenario). Live 9P run: date-only -> time -> time+
repeater all round-trip, and SCHEDULED+DEADLINE share one clean planning
line. GUI builds clean (DatePickerDialog compiles).