From a3826f2ce896b0e40c0f4cb2013c277a5364d9b4 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:14:55 +0200 Subject: [PATCH] =?UTF-8?q?gui:=20fix=20freeze=20on=20agent=20switch=20?= =?UTF-8?q?=E2=80=94=20bulk=20load=20chat=20blocks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit appendContent was calling rebuildBlockHtml (full syntax highlighting) on every single line during initial log load. For a 1000-line chat, that's O(n²) highlighting passes. Add beginBulkLoad()/endBulkLoad() — skips per-line rebuilds during bulk, then rebuilds all blocks once at the end. QML load sites now wrap appendData(log) with bulk mode. --- gui/chatblockmodel.cpp | 25 ++++++++++++++++++++++--- gui/chatblockmodel.h | 3 +++ gui/main.qml | 6 ++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/gui/chatblockmodel.cpp b/gui/chatblockmodel.cpp index 357bcc1..5f457e4 100644 --- a/gui/chatblockmodel.cpp +++ b/gui/chatblockmodel.cpp @@ -480,10 +480,29 @@ void ChatBlockModel::appendContent(const QString &line) last.content += '\n'; last.content += line; - rebuildBlockHtml(m_blocks.size() - 1); + if (!m_bulkLoading) { + rebuildBlockHtml(m_blocks.size() - 1); + QModelIndex idx = index(m_blocks.size() - 1); + emit dataChanged(idx, idx, {ContentRole, RenderedHtmlRole, SegmentsRole}); + } +} - QModelIndex idx = index(m_blocks.size() - 1); - emit dataChanged(idx, idx, {ContentRole, RenderedHtmlRole, SegmentsRole}); +void ChatBlockModel::beginBulkLoad() +{ + m_bulkLoading = true; +} + +void ChatBlockModel::endBulkLoad() +{ + m_bulkLoading = false; + // Rebuild HTML for all blocks once. + for (int i = 0; i < m_blocks.size(); ++i) { + rebuildBlockHtml(i); + } + if (!m_blocks.isEmpty()) { + emit dataChanged(index(0), index(m_blocks.size() - 1), + {ContentRole, RenderedHtmlRole, SegmentsRole}); + } } void ChatBlockModel::processLine(const QString &line) diff --git a/gui/chatblockmodel.h b/gui/chatblockmodel.h index cfb97ec..b316406 100644 --- a/gui/chatblockmodel.h +++ b/gui/chatblockmodel.h @@ -78,6 +78,8 @@ public: QHash roleNames() const override; Q_INVOKABLE void appendData(const QString &text); + Q_INVOKABLE void beginBulkLoad(); + Q_INVOKABLE void endBulkLoad(); Q_INVOKABLE void reset(); Q_INVOKABLE void trimFront(int keepCount); Q_INVOKABLE void clearDiagramCache(); @@ -110,6 +112,7 @@ private: QVariantList segmentsToVariant(const QList &segments) const; State m_state = State::BlockStart; + bool m_bulkLoading = false; KSyntaxHighlighting::Repository *m_repo = nullptr; QNetworkAccessManager *m_netManager = nullptr; diff --git a/gui/main.qml b/gui/main.qml index 74b457a..9935cea 100644 --- a/gui/main.qml +++ b/gui/main.qml @@ -463,8 +463,10 @@ ApplicationWindow { if (ollie.activeAgentId) { var log = ollie.readLogForSession(ollie.activeSessionId, ollie.activeAgentId) if (log) { + chatModel.beginBulkLoad() chatModel.appendData(log) chatModel.trimFront(20) + chatModel.endBulkLoad() } } ollie.refreshSessions() @@ -479,8 +481,10 @@ ApplicationWindow { if (ollie.activeSessionId && ollie.activeAgentId) { var log = ollie.readLogForSession(ollie.activeSessionId, ollie.activeAgentId) if (log) { + chatModel.beginBulkLoad() chatModel.appendData(log) chatModel.trimFront(20) + chatModel.endBulkLoad() } } } @@ -490,8 +494,10 @@ ApplicationWindow { if (ollie.activeSessionId && ollie.activeAgentId) { var log = ollie.readLogForSession(ollie.activeSessionId, ollie.activeAgentId) if (log) { + chatModel.beginBulkLoad() chatModel.appendData(log) chatModel.trimFront(20) + chatModel.endBulkLoad() } } }