From 96a6f6e6918e722f5fcd595439f82bc986b90707 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 26 Aug 2026 07:53:13 -0400 Subject: [PATCH] web: the editor draws the checklist too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2992's other half. The browser was the last surface still showing `- [ ] ` as markup: cards rendered and ticked checkboxes, the editor did not. Same shape as Android, deliberately. notes/blocks.ts mirrors EditorBlock.kt — splitBlocks, joinBlocks, afterEnter, withoutIndex, plusTask — because the two editors should behave alike and the cheapest way to keep them that way is for the code to read alike. `body` becomes a computed over the blocks, so every save, baseline check and draft still reads the one markdown string they always did. markdown.ts now exports parseTaskLine and renderTaskLine, and parseMarkdown uses the former. The read view and the editor's block split had been matching the same grammar through two separate copies of one regex; now they agree by construction. Two places the web can do better than Compose, and does: * Backspace at the start of an empty item removes it. A browser sends a real keydown for Backspace; an Android soft keyboard sends an IME delete that never surfaces as one, which is why that surface only has Enter-on-empty. * Prose fields size to their text — rows="1" plus a scrollHeight fit, which beats guessing a row count that is wrong the moment a line wraps. KNOWN, and the same on both surfaces: typing `- [ ] ` by hand into a prose block leaves it prose until the note is reopened. Blocks are split when the editor loads, not re-derived per keystroke — re-splitting mid-type would move the caret. The toolbar button is the intended path. Converting on blur would fix it and is worth doing to BOTH editors at once rather than letting them drift. --- frontend/src/components/NoteEditor.vue | 227 +++++++++++++++++-------- frontend/src/notes/blocks.ts | 110 ++++++++++++ frontend/src/notes/markdown.ts | 31 +++- 3 files changed, 291 insertions(+), 77 deletions(-) create mode 100644 frontend/src/notes/blocks.ts diff --git a/frontend/src/components/NoteEditor.vue b/frontend/src/components/NoteEditor.vue index c41f7ec..f8cc462 100644 --- a/frontend/src/components/NoteEditor.vue +++ b/frontend/src/components/NoteEditor.vue @@ -10,6 +10,14 @@ import { takeMorphOrigin } from "../composables/useEditorMorph"; import { prefersReducedMotion } from "../composables/useReducedMotion"; import type { Note, NoteLabel, NoteRevision } from "../stores/notes"; import { LABEL_CHIP_CLASSES, type NoteColor } from "../notes/colors"; +import { + afterEnter, + type EditorBlock, + joinBlocks, + plusTask, + splitBlocks, + withoutIndex, +} from "../notes/blocks"; // One modal editor for BOTH composing and editing — a single surface (the board's // "Take a note…" bar just opens this in compose mode). `note` = the note being edited, @@ -24,7 +32,14 @@ const emit = defineEmits<{ (e: "close"): void; (e: "navigate", id: string): void const notes = useNotesStore(); const noteId = ref(props.note?.id ?? null); -const body = ref(props.note?.body ?? props.initialBody); +// BLOCKS rather than one string, because a checklist item is drawn as a real checkbox +// and a widget cannot live inside a