From f52ad48937ef43679d507d7a86997291aa58101b Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Mon, 5 Jan 2026 11:51:26 -0800 Subject: [PATCH 1/3] Preserve line breaks in user chat messages --- .../message-editor/tiptap/useDraftSync.ts | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/apps/array/src/renderer/features/message-editor/tiptap/useDraftSync.ts b/apps/array/src/renderer/features/message-editor/tiptap/useDraftSync.ts index b9857a73..c3136554 100644 --- a/apps/array/src/renderer/features/message-editor/tiptap/useDraftSync.ts +++ b/apps/array/src/renderer/features/message-editor/tiptap/useDraftSync.ts @@ -9,6 +9,10 @@ function tiptapJsonToEditorContent(json: JSONContent): EditorContent { const traverse = (node: JSONContent) => { if (node.type === "text" && node.text) { segments.push({ type: "text", text: node.text }); + } else if (node.type === "hardBreak") { + // Shift+Enter creates a hard break within a paragraph + // Use two trailing spaces + newline for markdown line break (
) + segments.push({ type: "text", text: " \n" }); } else if (node.type === "mentionChip" && node.attrs) { segments.push({ type: "chip", @@ -18,6 +22,15 @@ function tiptapJsonToEditorContent(json: JSONContent): EditorContent { label: node.attrs.label, }, }); + } else if (node.type === "doc" && node.content) { + // Add double newlines between paragraphs for markdown rendering + // (single newlines in markdown become spaces, double newlines create paragraph breaks) + for (let i = 0; i < node.content.length; i++) { + if (i > 0) { + segments.push({ type: "text", text: "\n\n" }); + } + traverse(node.content[i]); + } } else if (node.content) { for (const child of node.content) { traverse(child); @@ -30,13 +43,33 @@ function tiptapJsonToEditorContent(json: JSONContent): EditorContent { } function editorContentToTiptapJson(content: EditorContent): JSONContent { - const paragraphContent: JSONContent[] = []; + const paragraphs: JSONContent[] = []; + let currentParagraphContent: JSONContent[] = []; + + const flushParagraph = () => { + paragraphs.push({ type: "paragraph", content: currentParagraphContent }); + currentParagraphContent = []; + }; for (const seg of content.segments) { if (seg.type === "text") { - paragraphContent.push({ type: "text", text: seg.text }); + const paragraphParts = seg.text.split("\n\n"); + for (let i = 0; i < paragraphParts.length; i++) { + if (i > 0) { + flushParagraph(); + } + const lineParts = paragraphParts[i].split(/ \n|\n/); + for (let j = 0; j < lineParts.length; j++) { + if (j > 0) { + currentParagraphContent.push({ type: "hardBreak" }); + } + if (lineParts[j]) { + currentParagraphContent.push({ type: "text", text: lineParts[j] }); + } + } + } } else { - paragraphContent.push({ + currentParagraphContent.push({ type: "mentionChip", attrs: { type: seg.chip.type, @@ -47,9 +80,15 @@ function editorContentToTiptapJson(content: EditorContent): JSONContent { } } + flushParagraph(); + + if (paragraphs.length === 0) { + paragraphs.push({ type: "paragraph", content: [] }); + } + return { type: "doc", - content: [{ type: "paragraph", content: paragraphContent }], + content: paragraphs, }; } From 73a0a6248014fd6b9022f5fb10a78abc341597b3 Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Mon, 5 Jan 2026 12:49:24 -0800 Subject: [PATCH 2/3] Update useDraftSync.ts --- .../src/renderer/features/message-editor/tiptap/useDraftSync.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/array/src/renderer/features/message-editor/tiptap/useDraftSync.ts b/apps/array/src/renderer/features/message-editor/tiptap/useDraftSync.ts index c3136554..615fb543 100644 --- a/apps/array/src/renderer/features/message-editor/tiptap/useDraftSync.ts +++ b/apps/array/src/renderer/features/message-editor/tiptap/useDraftSync.ts @@ -58,7 +58,7 @@ function editorContentToTiptapJson(content: EditorContent): JSONContent { if (i > 0) { flushParagraph(); } - const lineParts = paragraphParts[i].split(/ \n|\n/); + const lineParts = paragraphParts[i].split(/ {2}\n|\n/); for (let j = 0; j < lineParts.length; j++) { if (j > 0) { currentParagraphContent.push({ type: "hardBreak" }); From 028f9443a4700e707e721d8c6efef6945aff50bb Mon Sep 17 00:00:00 2001 From: Charles Vien Date: Tue, 6 Jan 2026 11:36:49 -0800 Subject: [PATCH 3/3] Update useTiptapEditor.ts --- .../renderer/features/message-editor/tiptap/useTiptapEditor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/array/src/renderer/features/message-editor/tiptap/useTiptapEditor.ts b/apps/array/src/renderer/features/message-editor/tiptap/useTiptapEditor.ts index a4be146a..3486e304 100644 --- a/apps/array/src/renderer/features/message-editor/tiptap/useTiptapEditor.ts +++ b/apps/array/src/renderer/features/message-editor/tiptap/useTiptapEditor.ts @@ -173,7 +173,7 @@ export function useTiptapEditor(options: UseTiptapEditorOptions) { const textToInsert = paths.map((p) => `"${p}"`).join(" "); - view.dispatch(view.state.tr.insertText(textToInsert + " ", pos)); + view.dispatch(view.state.tr.insertText(`${textToInsert} `, pos)); return true; }