@@ -63,29 +63,13 @@ const sink = (tr: Transaction, range: NodeRange, itemType: NodeType) => {
63
63
) ,
64
64
) ;
65
65
66
- // After sinking, lift any nested <li> children back out
67
- const from = range . start ;
68
- const $movedPos = tr . doc . resolve ( from ) ;
69
- const movedItem = $movedPos . nodeAfter ;
70
-
71
- if ( movedItem ) {
72
- movedItem . forEach ( ( child , offset ) => {
73
- if ( child . type === parent . type ) {
74
- const nestedStart = from + offset + 1 ;
75
- const nestedEnd = nestedStart + child . nodeSize ;
76
- const $liStart = tr . doc . resolve ( nestedStart + 1 ) ;
77
- const $liEnd = tr . doc . resolve ( nestedEnd - 1 ) ;
78
- const liftRange = $liStart . blockRange ( $liEnd , ( node ) => node . type === itemType ) ;
79
-
80
- if ( liftRange ) {
81
- const targetDepth = liftTarget ( liftRange ) ;
82
- if ( targetDepth !== null ) {
83
- tr . lift ( liftRange , targetDepth ) ;
84
- }
85
- }
86
- }
87
- } ) ;
88
- }
66
+ // Log the new position of the moved <li>
67
+ const oldPos = range . start ;
68
+ const newPos = tr . mapping . map ( oldPos , 1 ) ; // 1 = map as if the step was inserted after
69
+ console . log ( '[sink] moved <li> new pos:' , newPos , 'node:' , tr . doc . nodeAt ( newPos ) ?. type . name ) ;
70
+
71
+ // Lift any nested lists that ended up inside the moved <li>
72
+ liftNestedLists ( tr , itemType , parent . type , newPos ) ;
89
73
90
74
return true ;
91
75
} ;
@@ -140,6 +124,51 @@ function getListItemsToTransform(
140
124
return listItemsPoses ;
141
125
}
142
126
127
+ /**
128
+ * Lifts all nested lists (<ul>/<ol>) that are direct children of the list item at `liPos`.
129
+ *
130
+ * @param tr The working transaction
131
+ * @param itemType The node type representing a list_item
132
+ * @param listType The node type representing the surrounding list (bullet_list / ordered_list)
133
+ * @param liPos The absolute position of the moved <li> in the current transaction
134
+ */
135
+ function liftNestedLists (
136
+ tr : Transaction ,
137
+ itemType : NodeType ,
138
+ listType : NodeType ,
139
+ liPos : number ,
140
+ ) : void {
141
+ const movedItem = tr . doc . nodeAt ( liPos ) ;
142
+ if ( ! movedItem ) return ;
143
+
144
+ movedItem . forEach ( ( child , offset ) => {
145
+ // Detect nested list nodes of the same type (i.e., another bullet_list or ordered_list)
146
+ if ( child . type === listType ) {
147
+ const nestedStart = liPos + 1 + offset ; // +1 to move inside <li> content
148
+ const nestedEnd = nestedStart + child . nodeSize ;
149
+
150
+ const $nestedStart = tr . doc . resolve ( nestedStart + 1 ) ; // first node inside nested list
151
+ const $nestedEnd = tr . doc . resolve ( nestedEnd - 1 ) ; // last node inside nested list
152
+
153
+ const liftRange = $nestedStart . blockRange ( $nestedEnd , ( node ) => node . type === itemType ) ;
154
+ if ( liftRange ) {
155
+ const target = liftTarget ( liftRange ) ;
156
+ if ( target !== null ) {
157
+ console . log (
158
+ '[sink] lifting nested list' ,
159
+ 'range:' ,
160
+ liftRange . start ,
161
+ liftRange . end ,
162
+ 'target depth:' ,
163
+ target ,
164
+ ) ;
165
+ tr . lift ( liftRange , target ) ;
166
+ }
167
+ }
168
+ }
169
+ } ) ;
170
+ }
171
+
143
172
export function sinkOnlySelectedListItem ( itemType : NodeType ) : Command {
144
173
return ( { tr, selection} , dispatch ) => {
145
174
const { $from, $to, from, to} = selection ;
@@ -166,20 +195,23 @@ export function sinkOnlySelectedListItem(itemType: NodeType): Command {
166
195
const mappedStart = tr . mapping . map ( startPos ) ;
167
196
const mappedEnd = tr . mapping . map ( endPos ) ;
168
197
169
- console . log ( 'startPos: endPos' , startPos , endPos ) ;
170
- console . log ( 'mapped startPos: endPos ' , mappedStart , mappedEnd ) ;
171
-
172
198
let j = 0 ;
173
199
while ( j < tr . doc . nodeSize - 1 ) {
174
200
const node = tr . doc . nodeAt ( j ) ;
175
201
console . log ( 'node' , j , node ?. type . name ) ;
176
202
j ++ ;
177
203
}
178
204
179
- const start = startPos ;
180
- const end = endPos ;
205
+ const start = mappedStart ;
206
+ const end = mappedEnd ;
181
207
182
208
const startNode = tr . doc . nodeAt ( start ) ;
209
+ const $start = tr . doc . resolve ( start ) ;
210
+ const $end = tr . doc . resolve ( end ) ;
211
+
212
+ console . log ( '[startPos: endPos]' , startPos , endPos ) ;
213
+ console . log ( '[mapped startPos: endPos]' , mappedStart , mappedEnd ) ;
214
+ console . log ( '[$start, $end]' , $start . pos , $end . pos , 'j:' , j ) ;
183
215
console . log ( '[startNode]' , startNode ?. type , 'startNode size' , startNode ?. nodeSize ) ;
184
216
console . log (
185
217
'[start, end]' ,
@@ -189,10 +221,6 @@ export function sinkOnlySelectedListItem(itemType: NodeType): Command {
189
221
start + ( startNode ?. nodeSize ?? 0 ) ,
190
222
) ;
191
223
192
- const $start = tr . doc . resolve ( start ) ;
193
- const $end = tr . doc . resolve ( end ) ;
194
-
195
- console . log ( '[$start, $end]' , $start . pos , $end . pos , 'j:' , j ) ;
196
224
const range = $start . blockRange ( $end ) ;
197
225
198
226
if ( range ) {
0 commit comments