Drag a section by its ⠿ grip to restructure the outline. Vertical pointer
position chooses the insertion gap; horizontal position (indent) chooses
the depth. A live indicator shows the resolved target — an insertion bar
indented to the resolved level plus a ghost label ("L3 under \"B\"") —
computed by the same function that commits, so the preview is truthful.
Resolving the ambiguity (is this a new L2, an L3 sibling, or an L4
child?): at a gap between row A (level La, above) and row B (level Lb,
below), the legal depth range is [max(1, Lb), La + 1]. The pointer's
indent is clamped into that range; the parent is then computed by walking
A's ancestor chain to the row at level-1 (never guessed), and the
before-sibling is the first row after the gap at that level under that
parent. The dragged subtree is excluded, so it can't drop into itself.
Server (refile insert-before):
- refile_section gains a `before` parameter: insert the subtree
immediately before that sibling instead of appending at the end of the
parent. The ctl verb becomes
`refile <doc> <id> <to-doc> [parent-id] [before-id]`, and a literal "-"
in the parent slot means top level (so a before-id can be given while
refiling to the top).
Model:
- moveSection/promoteSection/demoteSection/refileSection drive the move,
promote, demote, and refile ctl verbs and reload on success.
- resolveDrop(draggedRow, gapAboveRow, desiredLevel) implements the
clamp + ancestor-walk and returns {valid, level, parentId, parentTitle,
beforeId}. sectionIdAt(row) exposes the row's id.
GUI:
- A drag grip per headline with a manual-drag MouseArea mapping the
pointer to ListView content coords and calling resolveDrop live; the
drop indicator overlay renders from the result; release commits via
refileSection.
Verified: server tests (75 passed, +3: refile before-sibling same-doc,
before top-level, and the ctl "-" top-level placeholder). The drop
resolution was checked with a standalone harness over the ambiguous
C|E-gap cases (indent 1/2/3/4 → top-before-E / L2-under-A / L3-under-B /
L4-under-C, clamping, and self-drop exclusion). Live 9P confirmed
`refile d f d b c` (reparent + relevel + insert-before) and
`refile d f d - a` (promote to top, insert-before). GUI builds clean.