@@ -9,6 +9,9 @@ import { SLASH_COMMAND_PLUGIN_KEY } from './slash-command/slash-command'
99/** Leaf nodes that have no text position, so they can only be reached as a NodeSelection. */
1010const SELECTABLE_LEAVES = new Set ( [ 'horizontalRule' , 'image' ] )
1111
12+ /** List-item node types whose empty-item Backspace must join rather than lift (see the Backspace docs). */
13+ const LIST_ITEM_TYPES = new Set ( [ 'listItem' , 'taskItem' ] )
14+
1215/**
1316 * True while a `/` or `@` suggestion menu is open. Arrow keys must reach that menu's own handler, so
1417 * the leaf-selection shortcuts below yield rather than stealing the key to select an adjacent divider.
@@ -66,12 +69,15 @@ function selectAdjacentSelectedLeaf(editor: Editor, direction: 'up' | 'down'): b
6669 * Editor-specific keyboard behavior layered on top of StarterKit's defaults:
6770 *
6871 * - **Backspace** at the start of a heading reverts it to a paragraph (ProseMirror's default joins or
69- * no-ops, stranding the heading style; a second Backspace then merges as usual). At the start of a
70- * block whose previous sibling is a divider or image, where ProseMirror's `joinBackward` can't cross
71- * the leaf and no-ops: an *empty* block is deleted (clearing the blank line between/below dividers
72- * without touching the divider itself), while a *non-empty* block selects the leaf — so a first
73- * Backspace highlights what a second deletes, the same highlight-before-delete affordance as clicking
74- * it and parity with the arrow-key leaf selection.
72+ * no-ops, stranding the heading style; a second Backspace then merges as usual). At the start of an
73+ * *empty list item* it joins into the previous block instead of ProseMirror's default lift — lifting
74+ * an empty item out of the middle of a list splits it into two lists and strands an empty paragraph,
75+ * a visible gap; `joinBackward` removes the item and keeps one list. At the start of a block whose
76+ * previous sibling is a divider or image, where ProseMirror's `joinBackward` can't cross the leaf and
77+ * no-ops: an *empty* block is deleted (clearing the blank line between/below dividers without touching
78+ * the divider itself), while a *non-empty* block selects the leaf — so a first Backspace highlights
79+ * what a second deletes, the same highlight-before-delete affordance as clicking it and parity with
80+ * the arrow-key leaf selection.
7581 * - **Mod-A** inside a code block selects only that block's contents; pressing it again (when the
7682 * block is already fully selected) falls through to the default whole-document select-all, the
7783 * same scoped behavior as a code editor.
@@ -97,6 +103,12 @@ export const RichMarkdownKeymap = Extension.create({
97103 if ( $from . parent . type . name === 'heading' ) {
98104 return editor . commands . setParagraph ( )
99105 }
106+ if (
107+ $from . parent . content . size === 0 &&
108+ LIST_ITEM_TYPES . has ( $from . node ( $from . depth - 1 ) . type . name )
109+ ) {
110+ return editor . commands . joinBackward ( )
111+ }
100112 const blockStart = $from . before ( $from . depth )
101113 const nodeBefore = doc . resolve ( blockStart ) . nodeBefore
102114 if ( ! nodeBefore || ! SELECTABLE_LEAVES . has ( nodeBefore . type . name ) ) return false
0 commit comments