- Model shows last 40 blocks initially, positioned at end
- Scrolling near top triggers loadEarlier() which prepends 30 blocks
- Scroll position maintained after prepend via contentY adjustment
- All blocks still parsed (for searching later) but only tail rendered
- Reduced initial render cost significantly
With 300KB+ chat logs producing hundreds of blocks, instantiating all
delegates was causing unresponsiveness. Cap the model at the last 80
blocks and set cacheBuffer: 200 to limit off-screen rendering.
- Cache parseSubBlocks per block (only reparse when content changes)
- Cache highlightedHtml results (code+language key)
- Prevents expensive KSyntaxHighlighting and regex on every render
- Also fixes scroll loop with _scrolling guard
- ChatBlockModel::reparse() now does incremental diffs instead of
beginResetModel which destroys all delegates and resets scroll
- ListView scroll uses _scrolling guard to prevent infinite loop
from positionViewAtEnd triggering contentHeightChanged
Replace flat TextArea chat view with structured ListView:
- ChatBlockModel parses raw chat text into typed blocks (user,
assistant, reasoning, call, tool, error, info)
- Sub-blocks parsed from markdown: prose, fenced code, diffs
- Code blocks rendered with KSyntaxHighlighting (auto light/dark theme)
- Diff blocks with red/green line coloring
- Copy button on code and diff blocks
- File paths linkified and clickable (opens with xdg-open)
- Reasoning/call/tool blocks collapsible (click to expand)
- User blocks highlighted with subtle accent background
- Error blocks in red
- Scroll pinning preserved (auto-scroll when at bottom)