Kill the entire D-Bus rendering pipeline. The GUI now uses plan9port's
9p command as a subprocess for all server communication:
- Chat streaming: persistent 'read chat' process, readyReadStandardOutput
delivers tokens directly to QML (no polling, no signals, no offsets)
- State: 'read statewait' process, restarts on each state change
- Session list: 'read session/idx' one-shot
- Submit/stop/kill: 'write prompt/ctl' one-shot
The QML is stripped to fundamentals: session list + monospace streaming
text (last 8KB tail) + input field. No ChatBlockModel, no ThemeManager,
no OllieDBusClient, no syntax highlighting, no markdown rendering.
This eliminates the multi-second GUI freezes caused by:
- QTextDocument relayout on 200KB+ accumulated text
- loadEarlier cascade creating hundreds of delegates
- KSyntaxHighlighting::Repository blocking init
- Offset mismatch triggering synchronous D-Bus calls
Features will be re-added incrementally on this proven foundation.
D-Bus retained only for plasmoid/tray/KRunner (desktop integration).
QTextDocument::setHtml was called while the document was still
tearing down from clear(). Defer refreshSessions to the next
event loop iteration with QTimer::singleShot(0, ...).
Allow specifying a remote target (e.g. user@host) when creating a
session from the GUI. Empty means local execution. The value is passed
through to the D-Bus CreateSession method's remote parameter.
Reactions were removed from the core runtime. Remove the dead QML code:
- 3x setReactions/getReactions dbus calls
- blockReaction property on delegate loader
- Reaction badge label and reaction buttons row
MovingRanges are owned by the document. When the document is destroyed,
it deletes its ranges. If clearAnnotations is called after the document
is gone (e.g., view changed), qDeleteAll double-frees. Track the owning
document with QPointer and only delete ranges if it's still alive.
When the new-session dialog opens, the backend combo defaults to index 0
but onCurrentTextChanged doesn't fire for the initial value. Explicitly
call listModelsByBackend for the default backend so the model dropdown
is populated immediately.
Child MouseArea inside Flickable interferes with drag detection even
when only accepting RightButton. Move it to a sibling overlay with
propagateComposedEvents so left-drag reaches the Flickable natively.
Remove manual pan logic. Let Flickable handle drag-to-pan natively.
MouseArea only accepts RightButton (close) and wheel (zoom), so it
no longer steals left-click events from the Flickable.
Left-click drag was unreliable for panning. Use Meta (Super/Win key)
+ left-click-drag instead. Flickable interactive mode disabled;
panning is handled manually via MouseArea.
The mermaid container has been replaced with a proper mermaid.ink
server. Use /img/ endpoint (returns JPEG) instead of /svg/ since
Qt's SVG renderer can't handle CSS class-based styles. Falls back
to mmdc CLI with PNG output if server is unavailable.
Qt's SVG renderer doesn't support CSS class-based styling that mermaid
generates (<style> blocks with class selectors). This caused text and
connectors to be invisible — they had no fill/stroke applied.
Fix: output PNG from mmdc for mermaid diagrams. Qt renders PNG perfectly.
PlantUML and Draw.io continue using SVG (they use inline styles).
Text and connectors were invisible because mmdc rendered with a dark
theme (white text/edges) on the white diagram background. Explicitly
pass -t default to force dark text on light background.
- Detect mermaid/plantuml/drawio/mxgraph code fences as 'diagram' sub-blocks
- Render via local servers (PlantUML :8080, Mermaid :8081, draw.io :8082)
- Fall back to CLI tools (plantuml, mmdc, drawio) if servers unavailable
- Cache rendered SVGs in ~/.cache/*/diagrams/ keyed by content hash
- Show rendered image with white background (dark theme friendly)
- Collapsible source toggle (▶/▼) below rendered diagram
- Middle-click to invalidate cache and re-render
- Left-click opens fullscreen pannable/zoomable viewer
- Fullscreen: scroll-wheel zoom, drag to pan, Esc/right-click/dbl-click to close
- Both kf5 and kf6 builds
Connect the new ConfigChanged signal from the session manager to trigger
refreshSessions() and fetchConfig(), so the sidebar updates immediately
when the user runs /agent to switch agents.
In Qt5 Repeater delegates, 'property var block: model' doesn't
propagate dataChanged updates for individual roles. Add a reactive
'blockReaction' string property bound to model.reaction directly,
which updates when the model emits dataChanged.
Same fixes as kf5:
- Remove hover-based sloppy focus that stole focus from buttons
- Add Ctrl+L to focus prompt
- Add emoji reaction buttons on assistant message hover
Reaction buttons (👍👎🚀💩🤔) appear on hover over assistant chat
bubbles. Clicking one calls the React D-Bus method which records
the feedback in session history without triggering a model turn.
Also adds react() to OllieDBusClient.
The hover-based sloppy focus timer was stealing focus from buttons
via paneRoot.focus=true every 100ms of mouse movement, making
buttons very hard to click.
Replace with a click-to-focus MouseArea that only updates the
focusedPaneIndex on actual clicks, with mouse.accepted=false
so clicks propagate to the target widget normally.
The sloppy focus timer was calling panePrompt.forceActiveFocus()
on mouse movement, stealing focus from button clicks. Remove the
auto-focus; add Ctrl+L shortcut to focus the prompt input instead.
Adds a filterText property to ChatBlockModel that filters all
parsed blocks by case-insensitive substring match on content
or tool/call name. Toggle the filter bar with Ctrl+F; Escape
or the close button clears the filter and hides the bar.
Shows match count while filtering.
Optional name field in the dialog. If provided, the session is
renamed immediately after creation via RenameSession D-Bus call.
Left blank, the auto-generated ID is used as before.
Use palette.mid at 60% opacity when active (scrolling), fully
transparent when idle, palette.mid on hover, palette.highlight
when pressed. 150ms color animation for smooth transitions.
- Add ScrollBar.vertical to the chat Flickable (both KF5/KF6)
- Remove interactive:false so native flick gestures work
- Trigger paneModel.loadEarlier() when scrolled near top
- Preserve scroll position when prepending older blocks
- Reduce column width by 12px for scrollbar clearance
- Expose GuiActivator as "activator" context property
- Add Ctrl+N shortcut to open a new window (both KF5 and KF6)
- Add per-session draft text persistence to main-kf5.qml
(KF6 ChatPane.qml already had this from previous commit)
When switching between sessions, the draft text in the prompt
is saved and restored. Each pane maintains a _drafts map keyed
by session ID, so switching away and back preserves whatever
was typed.
Running ollie-gui a second time now opens a new window in the
existing process (via NewWindow D-Bus method) instead of just
raising the existing window. The single-instance guard remains.
Each window gets its own ChatBlockModel via QML instantiation
(ChatPane.qml), so windows can display independent sessions.
Only render the last 40 blocks eagerly. Earlier messages are
lazy-loaded in chunks of 20 as the user scrolls up. This fixes
the progressive slowdown that occurred during long sessions where
the full HTML re-render on every ChatUpdated signal became O(n)
on the entire conversation history.
Register an action with the gitplusplus plugin (kate-worktree) that
appears in the Git++ tree view context menu. When triggered, it reads
the computed diff from the gitplusplus lastDiff property and submits
it to the active ollie session for review.
Also adds a "Review Diff" entry to the standard KTextEditor context
menu when the document has Diff highlighting mode (fallback path).
Replace simple folder picker with a full dialog. Adds listBackends()
and listModels() to OllieDBusClient. createSession() now accepts
backend and model parameters.