From 93c9bd995efbe7029a380cd57c11abe9d48e7f30 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Thu, 16 Jul 2026 07:07:51 +0200 Subject: [PATCH] move call/tool formatting entirely to C++ via formattedContent role QML scope issues made JS-based formatting unreliable. New approach: - FormattedContentRole in the model computes pretty-printed HTML in C++ - Call blocks: always prettyJson() - Tool blocks: detect JSON/diff and format, otherwise escaped HTML - QML just binds text: block.formattedContent || "" --- gui/chatblockmodel.cpp | 20 ++++++++++++++++++++ gui/chatblockmodel.h | 1 + gui/main.qml | 14 ++------------ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/gui/chatblockmodel.cpp b/gui/chatblockmodel.cpp index f71dd4a..2908651 100644 --- a/gui/chatblockmodel.cpp +++ b/gui/chatblockmodel.cpp @@ -61,6 +61,25 @@ QVariant ChatBlockModel::data(const QModelIndex &index, int role) const block.cachedContent = block.content; } return block.cachedSubBlocks; + case FormattedContentRole: { + // For call blocks: always pretty-print JSON + // For tool blocks: detect JSON/diff, format accordingly + // For others: just escape HTML + QString content = block.content; + if (content.isEmpty()) return QString(); + if (block.type == ChatBlock::Call) { + return prettyJson(content); + } + QString trimmed = content.trimmed(); + if (trimmed.startsWith('{') || trimmed.startsWith('[')) { + return prettyJson(content); + } + if (trimmed.startsWith(QLatin1String("---")) || trimmed.startsWith(QLatin1String("diff "))) { + return highlightedHtml(content, QStringLiteral("Diff")); + } + // Plain text: escape and add line breaks + return content.toHtmlEscaped().replace('\n', QLatin1String("
")); + } } return {}; } @@ -72,6 +91,7 @@ QHash ChatBlockModel::roleNames() const {NameRole, "blockName"}, {ContentRole, "blockContent"}, {SubBlocksRole, "subBlocks"}, + {FormattedContentRole, "formattedContent"}, }; } diff --git a/gui/chatblockmodel.h b/gui/chatblockmodel.h index 5bbb823..f5d660a 100644 --- a/gui/chatblockmodel.h +++ b/gui/chatblockmodel.h @@ -48,6 +48,7 @@ public: NameRole, ContentRole, SubBlocksRole, // QVariantList of sub-block maps + FormattedContentRole, // pre-formatted HTML for call/tool expanded view }; explicit ChatBlockModel(QObject *parent = nullptr); diff --git a/gui/main.qml b/gui/main.qml index 4d6e426..e74c8cc 100644 --- a/gui/main.qml +++ b/gui/main.qml @@ -624,7 +624,7 @@ ApplicationWindow { Text { id: callText Layout.fillWidth: true - text: visible ? chatModel.prettyJson(String(block.blockContent || "")) : "" + text: block.formattedContent || "" textFormat: Text.RichText wrapMode: Text.Wrap font.family: "monospace" @@ -653,17 +653,7 @@ ApplicationWindow { Text { id: toolText Layout.fillWidth: true - text: { - if (!visible) return "" - let content = String(block.blockContent || "") - if (!content) return "" - let trimmed = content.trim() - if (trimmed.charAt(0) === '{' || trimmed.charAt(0) === '[') - return chatModel.prettyJson(content) - if (trimmed.indexOf("---") === 0 || trimmed.indexOf("diff ") === 0) - return chatModel.highlightedHtml(content, "diff") - return content.replace(/&/g, '&').replace(//g, '>').replace(/\n/g, '
') - } + text: block.formattedContent || "" textFormat: Text.RichText wrapMode: Text.Wrap font.family: "monospace"