@@ -9,6 +9,10 @@ function tiptapJsonToEditorContent(json: JSONContent): EditorContent {
99 const traverse = ( node : JSONContent ) => {
1010 if ( node . type === "text" && node . text ) {
1111 segments . push ( { type : "text" , text : node . text } ) ;
12+ } else if ( node . type === "hardBreak" ) {
13+ // Shift+Enter creates a hard break within a paragraph
14+ // Use two trailing spaces + newline for markdown line break (<br>)
15+ segments . push ( { type : "text" , text : " \n" } ) ;
1216 } else if ( node . type === "mentionChip" && node . attrs ) {
1317 segments . push ( {
1418 type : "chip" ,
@@ -18,6 +22,15 @@ function tiptapJsonToEditorContent(json: JSONContent): EditorContent {
1822 label : node . attrs . label ,
1923 } ,
2024 } ) ;
25+ } else if ( node . type === "doc" && node . content ) {
26+ // Add double newlines between paragraphs for markdown rendering
27+ // (single newlines in markdown become spaces, double newlines create paragraph breaks)
28+ for ( let i = 0 ; i < node . content . length ; i ++ ) {
29+ if ( i > 0 ) {
30+ segments . push ( { type : "text" , text : "\n\n" } ) ;
31+ }
32+ traverse ( node . content [ i ] ) ;
33+ }
2134 } else if ( node . content ) {
2235 for ( const child of node . content ) {
2336 traverse ( child ) ;
@@ -30,13 +43,33 @@ function tiptapJsonToEditorContent(json: JSONContent): EditorContent {
3043}
3144
3245function editorContentToTiptapJson ( content : EditorContent ) : JSONContent {
33- const paragraphContent : JSONContent [ ] = [ ] ;
46+ const paragraphs : JSONContent [ ] = [ ] ;
47+ let currentParagraphContent : JSONContent [ ] = [ ] ;
48+
49+ const flushParagraph = ( ) => {
50+ paragraphs . push ( { type : "paragraph" , content : currentParagraphContent } ) ;
51+ currentParagraphContent = [ ] ;
52+ } ;
3453
3554 for ( const seg of content . segments ) {
3655 if ( seg . type === "text" ) {
37- paragraphContent . push ( { type : "text" , text : seg . text } ) ;
56+ const paragraphParts = seg . text . split ( "\n\n" ) ;
57+ for ( let i = 0 ; i < paragraphParts . length ; i ++ ) {
58+ if ( i > 0 ) {
59+ flushParagraph ( ) ;
60+ }
61+ const lineParts = paragraphParts [ i ] . split ( / { 2 } \n | \n / ) ;
62+ for ( let j = 0 ; j < lineParts . length ; j ++ ) {
63+ if ( j > 0 ) {
64+ currentParagraphContent . push ( { type : "hardBreak" } ) ;
65+ }
66+ if ( lineParts [ j ] ) {
67+ currentParagraphContent . push ( { type : "text" , text : lineParts [ j ] } ) ;
68+ }
69+ }
70+ }
3871 } else {
39- paragraphContent . push ( {
72+ currentParagraphContent . push ( {
4073 type : "mentionChip" ,
4174 attrs : {
4275 type : seg . chip . type ,
@@ -47,9 +80,15 @@ function editorContentToTiptapJson(content: EditorContent): JSONContent {
4780 }
4881 }
4982
83+ flushParagraph ( ) ;
84+
85+ if ( paragraphs . length === 0 ) {
86+ paragraphs . push ( { type : "paragraph" , content : [ ] } ) ;
87+ }
88+
5089 return {
5190 type : "doc" ,
52- content : [ { type : "paragraph" , content : paragraphContent } ] ,
91+ content : paragraphs ,
5392 } ;
5493}
5594
0 commit comments