feat: editor markdown-aware editing — bullets, marker cycle, indent, open-at-top #15

Merged
fiatcode merged 14 commits from feat/editor-markdown-editing into main 2026-06-17 16:32:24 +07:00
Owner

Makes weft's in-app editor behave like a native markdown editor, following the syntax-tinting/framing round (#14). Two rounds of work on one branch, both spec'd + planned + reviewed.

What changed

Bullet continuation (Enter)

  • Enter on a - bullet continues the list at the same indent; Enter on an empty bullet ends the list; non-bullet Enter inserts a normal newline. Continuation is always a plain - (never carries a marker forward).

Workflow-marker cycle (Ctrl+T)

  • Cycles the current bullet's marker plain → TODO → DONE → plain (the only markers used in the graph), no-op on non-bullets. Cursor rides with the text.

Indent / de-indent (Tab / Shift+Tab)

  • Tab indents the current line by one 2-space level, Shift+Tab de-indents (when the completion strip is closed — Tab still accepts a completion when it's open).

Fixes

  • Open the editor at the top of the buffer (was: cursor at the bottom with the viewport stuck at the top — a SetValue side effect).
  • Viewport now follows the cursor after a continuation/terminate/cycle/indent — these intercepts mutate via InsertString/replaceCurrentLine, which (unlike ta.Update) never repositioned the viewport, so a new bullet at the bottom of a page stayed off-screen until you typed. Fixed with a syncViewport() poke of the existing repositionMsg.

How it works

Decision logic is pure functions in internal/views/edit_markdown.go (bulletPrefix, isEmptyBullet, cycleMarkerLine, indentLine, dedentLine); EditorView.Update() intercepts the keys and applies results via a reusable replaceCurrentLine helper — mirroring the existing linkCompleter/acceptCompletion split. Render-layer only; internal/edit/ stays the sole disk writer.

Scope / non-goals

TODO/DONE only (not the full Logseq marker set); - bullets only (no [ ] checkboxes, ordered lists, or */+); Tab indents the current line, not a subtree; empty-bullet Enter clears the line (no multi-level outdent). Deferred: hanging-indent of wrapped bullet text in edit mode — blocked on bubbles/textarea (fixed-width prompt gutter, no per-line variable indent); logged as a weft TODO.

Testing

Pure unit tests for every helper + integration tests through Update() (continuation/terminate/cycle/indent transitions, cursor columns, open-at-top, and a viewport-reposition regression guard). Help golden regenerated. go vet ./... && go test ./... green.

Specs + plans under docs/superpowers/.

Makes weft's in-app editor *behave* like a native markdown editor, following the syntax-tinting/framing round (#14). Two rounds of work on one branch, both spec'd + planned + reviewed. ## What changed **Bullet continuation (Enter)** - Enter on a `- ` bullet continues the list at the same indent; Enter on an empty bullet ends the list; non-bullet Enter inserts a normal newline. Continuation is always a plain `- ` (never carries a marker forward). **Workflow-marker cycle (Ctrl+T)** - Cycles the current bullet's marker `plain → TODO → DONE → plain` (the only markers used in the graph), no-op on non-bullets. Cursor rides with the text. **Indent / de-indent (Tab / Shift+Tab)** - Tab indents the current line by one 2-space level, Shift+Tab de-indents (when the completion strip is closed — Tab still accepts a completion when it's open). **Fixes** - Open the editor at the **top** of the buffer (was: cursor at the bottom with the viewport stuck at the top — a `SetValue` side effect). - **Viewport now follows the cursor** after a continuation/terminate/cycle/indent — these intercepts mutate via `InsertString`/`replaceCurrentLine`, which (unlike `ta.Update`) never repositioned the viewport, so a new bullet at the bottom of a page stayed off-screen until you typed. Fixed with a `syncViewport()` poke of the existing `repositionMsg`. ## How it works Decision logic is pure functions in `internal/views/edit_markdown.go` (`bulletPrefix`, `isEmptyBullet`, `cycleMarkerLine`, `indentLine`, `dedentLine`); `EditorView.Update()` intercepts the keys and applies results via a reusable `replaceCurrentLine` helper — mirroring the existing `linkCompleter`/`acceptCompletion` split. Render-layer only; `internal/edit/` stays the sole disk writer. ## Scope / non-goals TODO/DONE only (not the full Logseq marker set); `-` bullets only (no `[ ]` checkboxes, ordered lists, or `*`/`+`); Tab indents the current line, not a subtree; empty-bullet Enter clears the line (no multi-level outdent). **Deferred:** hanging-indent of wrapped bullet text in edit mode — blocked on `bubbles/textarea` (fixed-width prompt gutter, no per-line variable indent); logged as a weft TODO. ## Testing Pure unit tests for every helper + integration tests through `Update()` (continuation/terminate/cycle/indent transitions, cursor columns, open-at-top, and a viewport-reposition regression guard). Help golden regenerated. `go vet ./... && go test ./...` green. Specs + plans under `docs/superpowers/`.
fiatcode deleted branch feat/editor-markdown-editing 2026-06-17 16:32:25 +07:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
fiatcode/weft!15
No description provided.