- OllieDBusClient: detachProcess, listDetached, signalDetached,
getDetachedOutput, dismissDetached methods + D-Bus signal handlers
- ChatPane.qml: Detach button (visible during calling: state) +
badge showing detached process count (click opens Process Manager)
- main.qml + main-kf5.qml: Process Manager window with process list,
live output tail, Term/Kill/Dismiss buttons per process
Full split-pane implementation:
- Ctrl+\ splits focused pane (BSP tree layout, alternating h/v)
- Ctrl+W closes focused pane (collapses parent split)
- Sloppy focus: pane focuses when mouse stops moving (100ms)
- Drag header to another pane to swap sessions
- Multi-line TextArea prompt with emoji shortcode completion
- Each pane has own ChatBlockModel and D-Bus signal filtering
- Auto-relayout on window resize
Major refactor: extracted chat UI into ChatPane.qml (self-contained
with own ChatBlockModel, D-Bus signal filtering, prompt input).
Main content area is now a SplitView with dynamic pane creation.
Auto-layout: splits horizontal if window wider than tall.
Sidebar click updates the focused pane session.
New OllieDBusClient methods: getChat, getConfig, getState (per-session).
New signal: chatAppended (emitted for all sessions, not just active).
Circular node layout showing all sessions with peer relationship edges.
Nodes are color-coded by state (green=idle, blue=thinking, orange=calling),
pulse when active. Click a node to select that session.
Toggle between list and graph view in sidebar toolbar.
Adds peerList() to OllieDBusClient.
Code fences with /path:start-end on the fence line get parsed.
Apply replaces that line range in the target file.
Fallback: insert at cursor or replace selection.
agent-copilot.md instructs model to produce ranged fences.
Context menu: right-click selection shows Fix/Refactor/Add tests/Document
actions that submit the selection with file+line context to the agent.
Apply button: code blocks in assistant messages get a clickable Apply
button that inserts the code into the target file (detected from
preceding prose) or the active document.
Inline annotations: file:line references in agent output are highlighted
with MovingRanges in the active editor view.
setHtml() resets the document layout. sb->setValue(sb->maximum()) was
reading a stale maximum before relayout completed. Use moveCursor(End)
+ ensureCursorVisible() which forces layout and scrolls reliably.
Also: fix session list state updates destroying delegates (rename fix)
already included in previous commit.
- Add configurable close-to-tray (Settings > System Tray)
- QSystemTrayIcon with show/hide toggle and quit action
- closeToTray property persisted in theme.conf
- Window hides instead of quitting when enabled
- Fix session rename being interrupted by state changes
- onStateChanged no longer calls refreshSessions()
- State updates in-place via sessionStateUpdated signal
- Delegates track state locally, preserving rename TextField
- Replace Qt.callLater scroll debounce with Timer (16ms) to coalesce
rapid contentHeight changes and eliminate scroll flickering
- Add execute button (play icon) on code blocks for scripted languages
(bash, python, perl, ruby, etc.) — launches in terminal
- Fix assistant bubble width: always use 95% of available width
- Add minimum width (280px) to code fence blocks
- Move emoji popup into prompt RowLayout so it appears near input
- Disable wheel scroll inertia (direct contentY) while keeping flick
inertia intact
- Apply all fixes to both KF5 and KF6 QML files
- Tool blocks: expanded via sub-blocks (same as assistant) so embedded
diffs and code fences render properly with coloring
- Call blocks: always JSON, use formattedContent (pretty-print + highlight)
- Prose sub-blocks use Text.MarkdownText for native backtick/bold/link support
Tool results often contain markdown with code fences and diffs.
Using formattedContent treated the whole block as raw text.
Now tool blocks expand into parsed sub-blocks (prose, code, diff)
just like assistant blocks, so embedded diffs get colored.
HTML RichText collapses multiple spaces, destroying JSON indentation.
Wrap all highlighted output in <pre> tags and use real newlines instead
of <br>. pre-wrap allows wrapping at container width.
QSyntaxHighlighter applies colors to the layout only (not accessible
via QTextBlock iteration). Replace with a custom AbstractHighlighter
subclass (HtmlBuilder) that builds HTML directly in applyFormat().
This actually produces colored output.
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 || ""
The onVisibleChanged approach failed because block.blockContent could
not resolve through the Loader scope. Now using direct binding:
- call blocks: text bound to chatModel.prettyJson(block.blockContent)
- tool blocks: detect JSON/diff, format accordingly
- prettyJson() uses QJsonDocument for proper indentation + KSyntaxHighlighting
- Only evaluated when visible (lazy)
ListView.positionViewAtEnd() causes freezes by forcing instantiation
of all delegates to measure total height. Flickable+Column avoids this
- contentHeight is computed directly from Column.implicitHeight
- Scroll to bottom is just contentY = contentHeight - height
- No virtualization needed with only 20 blocks
- Eliminates all positionViewAtEnd and _scrolling guard complexity
- Code blocks show plain text immediately, highlight via Timer after
150ms (prevents blocking the render thread during initial load)
- Reduced initial blocks from 40 to 20
- Detect append vs full reset in setRawText for better session switch
- 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