Problem
When changing the type of a list item via the palette (e.g., BulletList → NumberedList or Checklist), only the active block changes. This breaks list consistency — you end up with a mixed list where one item is a different type than its siblings/parents/children, which is almost never what the user wants.
Context
- Block types:
internal/block/block.go:19 (BulletList, NumberedList, Checklist), with IsListItem() at line 73.
- Palette type conversion is driven through
internal/editor/palette.go (openForBlock, defaultPaletteItems) and applied in internal/editor/editor.go where palette selection mutates m.blocks[m.active].Type.
- Indent is tracked per-block via
Block.Indent (see nested lists work from 2026-04-06).
Approach
When the palette selects a new list type and the active block IsListItem():
- Walk outward from
m.active to find the full contiguous list group — siblings at any indent level, plus children (deeper indent) and parents (shallower indent) that are still list items.
- Stop the walk at the first non-list-item block in either direction.
- Apply the new list type to every block in that group, preserving each block's
Indent and Content (and Checked where applicable — dropping check state when converting to non-Checklist).
Wrap in a single pushUndo() so the whole cascade is one undo step.
Tasks
Acceptance criteria
Problem
When changing the type of a list item via the palette (e.g.,
BulletList→NumberedListorChecklist), only the active block changes. This breaks list consistency — you end up with a mixed list where one item is a different type than its siblings/parents/children, which is almost never what the user wants.Context
internal/block/block.go:19(BulletList,NumberedList,Checklist), withIsListItem()at line 73.internal/editor/palette.go(openForBlock,defaultPaletteItems) and applied ininternal/editor/editor.gowhere palette selection mutatesm.blocks[m.active].Type.Block.Indent(see nested lists work from 2026-04-06).Approach
When the palette selects a new list type and the active block
IsListItem():m.activeto find the full contiguous list group — siblings at any indent level, plus children (deeper indent) and parents (shallower indent) that are still list items.IndentandContent(andCheckedwhere applicable — dropping check state when converting to non-Checklist).Wrap in a single
pushUndo()so the whole cascade is one undo step.Tasks
listGroupRange(idx int) (start, end int)) that returns the span of the contiguous list group containingidx.m.blocks[m.active].IsListItem(), apply the new type across[start, end].internal/editor/palette_test.gocovering flat lists, nested lists, and mixed-indent groups.Acceptance criteria
Indent) and parents (shallowerIndent) in the same group convert together.