diff --git a/gui/chatblockmodel.cpp b/gui/chatblockmodel.cpp index 126abb4..e9d9e61 100644 --- a/gui/chatblockmodel.cpp +++ b/gui/chatblockmodel.cpp @@ -131,29 +131,33 @@ static constexpr int LOAD_MORE_COUNT = 30; void ChatBlockModel::reparseIncremental() { - // Only re-parse from where the last block started — O(tail) not O(full text) + // Fast path: just update the last block's content from the raw text tail. + // Only do a full reparse if a new block header appeared. QString tail = m_rawText.mid(m_lastBlockTextOffset); - QList tailBlocks = parseBlocks(tail); - // Replace the last block (which may have grown) and append any new blocks - if (!m_allBlocks.isEmpty()) - m_allBlocks.removeLast(); - m_allBlocks.append(tailBlocks); + // Check if a new block header appeared in the NEW portion (after old last-block content) + int oldLastBlockLen = m_allBlocks.isEmpty() ? 0 : m_allBlocks.last().content.size(); + // The header itself is at m_lastBlockTextOffset, content starts after it + newline + // Simple heuristic: if the tail contains another header pattern, do full reparse + static const QRegularExpression headerRe( + R"(^\[(user|assistant|reasoning|call|tool|error|info)(?::([^\]]*))\])", + QRegularExpression::MultilineOption); + auto it = headerRe.globalMatch(tail); + int headerCount = 0; + while (it.hasNext()) { it.next(); headerCount++; } - // Update offset: last block starts at the last header in the tail - if (tailBlocks.size() > 1) { - // Multiple blocks parsed from tail — advance offset to where the last one starts - // Find the last header boundary in m_rawText - static const QRegularExpression headerRe( - R"(^\[(user|assistant|reasoning|call|tool|error|info)(?::([^\]]*))\])", - QRegularExpression::MultilineOption); - auto it = headerRe.globalMatch(m_rawText); - int lastPos = 0; - while (it.hasNext()) { - auto match = it.next(); - lastPos = match.capturedStart(); + if (headerCount > 1) { + // New block boundary appeared — fall back to full reparse + reparse(); + return; + } + + // Common case: just the last block grew. Update its content directly. + if (!m_allBlocks.isEmpty()) { + QList tailBlocks = parseBlocks(tail); + if (!tailBlocks.isEmpty()) { + m_allBlocks.last() = tailBlocks.last(); } - m_lastBlockTextOffset = lastPos; } updateVisibleWindow();