From b841f556d44d0afebe61d209ae0f81e641c8dd13 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Mon, 3 Aug 2026 00:24:52 +0200 Subject: [PATCH] gui: preserve newlines in plaintext blocks --- gui/chatblockmodel.cpp | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/gui/chatblockmodel.cpp b/gui/chatblockmodel.cpp index d14f910..08406e3 100644 --- a/gui/chatblockmodel.cpp +++ b/gui/chatblockmodel.cpp @@ -201,9 +201,13 @@ QString ChatBlockModel::renderMarkdown(const QString &markdown) const auto it = fenceRe.globalMatch(markdown); while (it.hasNext()) { const auto match = it.next(); - QTextDocument prose; - prose.setMarkdown(markdown.mid(cursor, match.capturedStart() - cursor)); - result += prose.toHtml(); + // Prose before this fence: HTML-escape and wrap in pre-wrap div to + // preserve newlines and whitespace. QTextDocument::setMarkdown would + // collapse single newlines (standard Markdown behavior). + const QString prose = markdown.mid(cursor, match.capturedStart() - cursor); + if (!prose.isEmpty()) + result += QStringLiteral("
") + + prose.toHtmlEscaped() + QStringLiteral("
"); result += highlightCode(match.captured(2), match.captured(1)); cursor = match.capturedEnd(); } @@ -215,16 +219,18 @@ QString ChatBlockModel::renderMarkdown(const QString &markdown) const R"(^```([A-Za-z0-9_+.-]*)[^\n]*\n([\s\S]*)$)", QRegularExpression::MultilineOption); const auto tail = markdown.mid(cursor); - const auto open = openFenceRe.match(tail); - if (open.hasMatch()) { - QTextDocument prose; - prose.setMarkdown(tail.left(open.capturedStart())); - result += prose.toHtml(); - result += highlightCode(open.captured(2), open.captured(1)); - } else { - QTextDocument prose; - prose.setMarkdown(tail); - result += prose.toHtml(); + if (!tail.isEmpty()) { + const auto open = openFenceRe.match(tail); + if (open.hasMatch()) { + const QString prose = tail.left(open.capturedStart()); + if (!prose.isEmpty()) + result += QStringLiteral("
") + + prose.toHtmlEscaped() + QStringLiteral("
"); + result += highlightCode(open.captured(2), open.captured(1)); + } else { + result += QStringLiteral("
") + + tail.toHtmlEscaped() + QStringLiteral("
"); + } } return result; }