11import { wrapIfNecessary } from '$lib/richText/linewrap' ;
2- import { createEditor , ParagraphNode , type LexicalEditor } from 'lexical' ;
2+ import { handleEnter } from '$lib/richText/plugins/IndentPlugin.svelte' ;
3+ import {
4+ createEditor ,
5+ TextNode ,
6+ type LexicalEditor ,
7+ COMMAND_PRIORITY_CRITICAL ,
8+ KEY_ENTER_COMMAND ,
9+ type NodeKey ,
10+ type NodeMutation
11+ } from 'lexical' ;
312import {
413 $createParagraphNode as createParagraphNode ,
514 $createTextNode as createTextNode ,
615 $getRoot as getRoot ,
716 $getNodeByKey as getNodeByKey ,
817 $getSelection as getSelection ,
918 $isRangeSelection as isRangeSelection ,
10- type TextNode
19+ $isTextNode as isTextNode ,
20+ type TextNode as LexicalTextNode
1121} from 'lexical' ;
1222import { describe , it , expect , beforeEach } from 'vitest' ;
1323
@@ -176,7 +186,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
176186 } ) ;
177187
178188 editor . update ( ( ) => {
179- const node = getNodeByKey ( textNodeKey ) as TextNode ;
189+ const node = getNodeByKey ( textNodeKey ) as LexicalTextNode ;
180190 wrapIfNecessary ( { node, maxLength : 20 } ) ;
181191 } ) ;
182192
@@ -209,7 +219,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
209219 } ) ;
210220
211221 editor . update ( ( ) => {
212- const node = getNodeByKey ( textNodeKey ) as TextNode ;
222+ const node = getNodeByKey ( textNodeKey ) as LexicalTextNode ;
213223 wrapIfNecessary ( { node, maxLength : 25 } ) ;
214224 } ) ;
215225
@@ -243,7 +253,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
243253 } ) ;
244254
245255 editor . update ( ( ) => {
246- const node = getNodeByKey ( textNodeKey ) as TextNode ;
256+ const node = getNodeByKey ( textNodeKey ) as LexicalTextNode ;
247257 wrapIfNecessary ( { node, maxLength : 20 } ) ;
248258 } ) ;
249259
@@ -282,7 +292,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
282292 root . append ( para3 ) ;
283293
284294 // Now edit the first paragraph to make it longer
285- const textNode = para1 . getFirstChild ( ) as TextNode ;
295+ const textNode = para1 . getFirstChild ( ) as LexicalTextNode ;
286296 textNode . setTextContent ( 'This is the first line that has been made much longer' ) ;
287297
288298 // Trigger rewrap
@@ -323,7 +333,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
323333 } ) ;
324334
325335 editor . update ( ( ) => {
326- const node = getNodeByKey ( textNodeKey ) as TextNode ;
336+ const node = getNodeByKey ( textNodeKey ) as LexicalTextNode ;
327337 // Simulate adding text by modifying the content
328338 node . setTextContent (
329339 'This is a very long line that has not been wrapped yet and definitely needs to be wrapped now'
@@ -370,7 +380,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
370380 root . append ( para2 ) ;
371381
372382 // Trigger wrap on first paragraph
373- const textNode = para1 . getFirstChild ( ) as TextNode ;
383+ const textNode = para1 . getFirstChild ( ) as LexicalTextNode ;
374384 wrapIfNecessary ( { node : textNode , maxLength : 20 } ) ;
375385 } ) ;
376386
@@ -401,7 +411,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
401411 root . append ( para2 ) ;
402412
403413 // Trigger wrap on first paragraph
404- const textNode = para1 . getFirstChild ( ) as TextNode ;
414+ const textNode = para1 . getFirstChild ( ) as LexicalTextNode ;
405415 wrapIfNecessary ( { node : textNode , maxLength : 20 } ) ;
406416 } ) ;
407417
@@ -435,7 +445,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
435445 root . append ( para3 ) ;
436446
437447 // Trigger wrap on first paragraph
438- const textNode = para1 . getFirstChild ( ) as TextNode ;
448+ const textNode = para1 . getFirstChild ( ) as LexicalTextNode ;
439449 wrapIfNecessary ( { node : textNode , maxLength : 20 } ) ;
440450 } ) ;
441451
@@ -466,7 +476,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
466476 } ) ;
467477
468478 editor . update ( ( ) => {
469- const node = getNodeByKey ( textNodeKey ) as TextNode ;
479+ const node = getNodeByKey ( textNodeKey ) as LexicalTextNode ;
470480 // Simulate typing " a word" in the middle, making it overflow
471481 node . setTextContent ( 'This a word is exactly at' ) ;
472482
@@ -514,7 +524,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
514524 root . append ( para3 ) ;
515525
516526 // Now edit the first paragraph to add more content
517- const textNode = para1 . getFirstChild ( ) as TextNode ;
527+ const textNode = para1 . getFirstChild ( ) as LexicalTextNode ;
518528 textNode . setTextContent ( 'First line text that has been extended' ) ;
519529
520530 // Trigger rewrap
@@ -557,7 +567,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
557567 } ) ;
558568
559569 editor . update ( ( ) => {
560- const node = getNodeByKey ( textNodeKey ) as TextNode ;
570+ const node = getNodeByKey ( textNodeKey ) as LexicalTextNode ;
561571 // Simulate typing "inserted " in the middle, making it overflow
562572 node . setTextContent ( '- First inserted second third' ) ;
563573
@@ -606,7 +616,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
606616 root . append ( para3 ) ;
607617
608618 // Now edit the first line to make it much longer
609- const textNode = para1 . getFirstChild ( ) as TextNode ;
619+ const textNode = para1 . getFirstChild ( ) as LexicalTextNode ;
610620 textNode . setTextContent ( '- Short bullet that has been made significantly longer' ) ;
611621
612622 // Trigger rewrap
@@ -664,7 +674,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
664674 root . append ( emptyPara2 ) ;
665675
666676 // Trigger wrap on the first paragraph (should not affect empty paragraphs)
667- const textNode = para1 . getFirstChild ( ) as TextNode ;
677+ const textNode = para1 . getFirstChild ( ) as LexicalTextNode ;
668678 wrapIfNecessary ( { node : textNode , maxLength : 72 } ) ;
669679 } ) ;
670680
@@ -685,6 +695,31 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
685695 } ) ;
686696
687697 it ( 'should allow adding multiple newlines at the end by typing' , ( ) => {
698+ const maxLength = 72 ;
699+
700+ // Register the IndentPlugin's Enter handler (simulating real editor behavior)
701+ editor . registerCommand ( KEY_ENTER_COMMAND , handleEnter , COMMAND_PRIORITY_CRITICAL ) ;
702+
703+ // Register the HardWrapPlugin's mutation listener (simulating the plugin being active)
704+ editor . registerMutationListener ( TextNode , ( nodes : Map < NodeKey , NodeMutation > ) => {
705+ editor . update (
706+ ( ) => {
707+ for ( const [ key , type ] of nodes . entries ( ) ) {
708+ if ( type !== 'updated' ) continue ;
709+
710+ const node = getNodeByKey ( key ) ;
711+ if ( ! node || ! isTextNode ( node ) ) continue ;
712+
713+ wrapIfNecessary ( { node, maxLength } ) ;
714+ }
715+ } ,
716+ {
717+ tag : 'history-merge'
718+ }
719+ ) ;
720+ } ) ;
721+
722+ // Set up initial state
688723 editor . update ( ( ) => {
689724 const root = getRoot ( ) ;
690725 const paragraph = createParagraphNode ( ) ;
@@ -695,38 +730,22 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
695730 textNode . select ( 9 , 9 ) ;
696731 } ) ;
697732
698- // Add an empty paragraph at the end
699- editor . update ( ( ) => {
700- const root = getRoot ( ) ;
701- const emptyPara = createParagraphNode ( ) ;
702- emptyPara . append ( createTextNode ( '' ) ) ;
703- root . append ( emptyPara ) ;
704- } ) ;
705-
706- // Add another empty paragraph at the end
733+ // Simulate pressing Enter (like a user would)
707734 editor . update ( ( ) => {
708- const root = getRoot ( ) ;
709- const emptyPara = createParagraphNode ( ) ;
710- emptyPara . append ( createTextNode ( '' ) ) ;
711- root . append ( emptyPara ) ;
735+ editor . dispatchCommand ( KEY_ENTER_COMMAND , null ) ;
712736 } ) ;
713737
714- // Now trigger wrapping on an empty paragraph (simulating typing in the empty area)
738+ // Press Enter again
715739 editor . update ( ( ) => {
716- const root = getRoot ( ) ;
717- const children = root . getChildren ( ) ;
718- const lastPara = children [ children . length - 1 ] as ParagraphNode ;
719- const textNode = lastPara . getFirstChild ( ) as TextNode ;
720- if ( textNode ) {
721- wrapIfNecessary ( { node : textNode , maxLength : 72 } ) ;
722- }
740+ editor . dispatchCommand ( KEY_ENTER_COMMAND , null ) ;
723741 } ) ;
724742
743+ // Verify the result
725744 editor . read ( ( ) => {
726745 const root = getRoot ( ) ;
727746 const children = root . getChildren ( ) ;
728747
729- // Should still have all paragraphs
748+ // Should have 3 paragraphs total
730749 expect ( children . length ) . toBe ( 3 ) ;
731750
732751 // First paragraph should have text
@@ -756,7 +775,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
756775 root . append ( emptyPara2 ) ;
757776
758777 // Now type something in the last empty paragraph
759- const lastTextNode = emptyPara2 . getFirstChild ( ) as TextNode ;
778+ const lastTextNode = emptyPara2 . getFirstChild ( ) as LexicalTextNode ;
760779 lastTextNode . setTextContent ( 'x' ) ;
761780
762781 // Trigger wrapping on the last paragraph (this simulates what happens when typing)
@@ -795,7 +814,7 @@ describe('HardWrapPlugin with multi-paragraph structure', () => {
795814 root . append ( emptyPara2 ) ;
796815
797816 // Now add more text to the first paragraph
798- const firstTextNode = para1 . getFirstChild ( ) as TextNode ;
817+ const firstTextNode = para1 . getFirstChild ( ) as LexicalTextNode ;
799818 firstTextNode . setTextContent ( 'Some text with more content added' ) ;
800819
801820 // Trigger wrapping on the first paragraph (this simulates what happens when typing)
0 commit comments