- Detect plantuml/mermaid code fences and render as diagrams
- PlantUML: POST to localhost:8080/svg for SVG output
- Mermaid: GET to localhost:8081/img/ with base64 for JPEG output
(SVG not usable - Qt doesn't support foreignObject)
- Cache rendered diagrams by content hash
- Toggle between source code and rendered image view
- Copy button for source text or image data
- Fall back to source display on render errors
- Add ClipboardHelper for image clipboard support
- Clear diagram cache on session reset
Chat blocks:
- Enable selectByMouse and persistentSelection for text selection
- Add context menu with Copy and Select All
- Disable ListView drag during text selection (preserves wheel scroll)
- I-beam cursor over text content
Prompt input:
- Enable selectByMouse and persistentSelection
- Add context menu with Cut, Copy, Paste, Select All
- Remove ScrollView wrapper (TextArea handles overflow natively)
- I-beam cursor
- Add shouldAutoScroll property to track user intent
- Auto-scroll when near bottom (within 50px threshold)
- Disable auto-scroll when user scrolls up to read history
- Re-enable when user scrolls back to bottom
- Reset to auto-scroll on session/agent switch
- Increase timer interval to 16ms for smoother layout settling
Adds ability to hide/show the session list sidebar:
- Ctrl+B keyboard shortcut
- Toggle button (◀/▶) in the chat header
- Sidebar collapses to zero width when hidden
- Add colored dot to session rows showing tool server RPC status:
green=connected, yellow=paused+connected, gray=paused, red=broken
- Remove vestigial agent.connected from model (was never displayed)
- Session-level connected status is the correct granularity
Non-selected agents show idle (green). This matches the working
top indicator approach until we find a proper solution for tracking
all agent states reactively in QML.
- Add agentStateRevision property that increments on any state change
- QML binds to revision to trigger re-evaluation of agentState() calls
- All agent dots now show correct live state, not just active agent
- Update parentPaused on agents during incremental session updates
- Set parentPaused and parentExpanded when inserting new agents
- Update session paused property during updates
- Use explicit === true check for parentPaused
- Agent dots in tree are gray when parent session is paused
- Top state indicator is gray when viewing agent in paused session
- Top state label shows 'inactive' instead of agent state when paused
- Add createSession(name, remote) with New Session dialog
- Remove agent connectivity code (redundant with session connectivity)
- Refactor NinePConnection as formal state machine with explicit events
- Fix silent restart to not cycle through Connecting state
Removes the activeState !== 'idle' check before sending the stop
command. The server-side Interrupt() is safe to call on an idle
agent (returns false, no-op).
This fixes an intermittent issue where pressing ESC wouldn't stop
the agent because the GUI's cached activeState was stale/out-of-sync
with the server's actual state.
- onAgentStateChanged: update specific agent in model instead of version bump
- State dot: use model.state directly instead of ollie.agentState() callback
- Remove ollie.isSessionPaused() call (was O(n) iteration per agent)
- Remove unused agentStateVersion property
Before: 1 state change → re-evaluate ALL agent dots → N * ollie.agentState() calls
After: 1 state change → setProperty on 1 model item → 1 binding update
- computeConnectionColor() in QML instead of ollie.sessionConnectionColor()
(avoids O(n²) iteration through sessions list)
- Use agent.state from session/idx data directly instead of ollie.agentState()
(avoids hash lookup per agent per update)
All data now flows from session/idx through m_sessions to QML with no
callbacks into C++ during the update loop.
Instead of clearing and rebuilding the entire session tree model on
every sessionsChanged signal, diff old vs new and:
- Update existing session/agent properties in place
- Remove deleted sessions/agents
- Append new sessions/agents
This avoids O(n) model rebuilds that cause ListView to re-render all
delegates, improving responsiveness with many sessions.
- Remove console.log statements from hot paths
- Add sessionExpandedCache for O(1) expanded state lookup
- Replace O(n) parentSessionExpanded() loop with cache lookup
- Preserve expanded state across session tree rebuilds
- Add Pause/Resume toggle to session context menu
- Gray agent dots when parent session is paused
- Disable input field with 'Session paused' placeholder when paused
- Preserve focus on prompt input during tree rebuilds
- Add per-agent statewait streams to track execution state
- Agent dots show: green=idle, orange=calling, blue=thinking
- Session dots show RPC connection health (unchanged)
- Use version counter pattern to force QML binding re-evaluation
- Fetch paused status from session/{id}/paused file
- Gray dot indicator for paused sessions (green otherwise)
- Session names now show agent count: 'ollie (2)'
- Fixed rename to use baseName for sessions (without count suffix)
commitRename now checks the return value from renameSession/renameAgent.
On failure, resets displayName to the old value and shows an error dialog
explaining the rename failed (e.g., name already in use).
Session selection now auto-selects first agent:
- Clicking a session row selects its first agent (not just the session)
- Auto-select at startup now picks first agent of first session
- Fixes issue where clicking session would clear agent and show empty chat
Improved UI feedback:
- Header shows 'Session / Agent' name instead of just state
- State indicator gray when no agent selected
- Placeholder message when no agent selected: 'Select an agent to view chat'
- Chat ListView hidden when no agent (shows placeholder instead)
- Input already disabled with 'No agent' placeholder (existing behavior)
Cleanup:
- Remove dead declaration reconcileAgentConnections(const QVariantList&)
Qt5's TextEdit 'color' property can override inline styles from RichText,
breaking syntax highlighting colors. Remove the property so inline color
spans from KSyntaxHighlighting are respected.
Default text color should come from the application palette.
Change createAgent to return error string (empty=success).
Replace standardButtons with custom footer so dialog stays open
on error, showing the message and letting the user adjust values.
The dominant 9P spam came from buildSessionTree() re-listing
session/<sid>/agent on every activeSessionIdChanged and
activeAgentIdChanged signal. Tree rebuilds are now limited to
sessionsChanged and startup refresh; selection changes only
refresh the right pane transcript.
- Session row click now calls setActiveSessionId(id)
- setActiveSessionId no longer returns early for same-session clicks;
it always clears m_agentId and stops per-agent streams
- This enforces a clean 'session selected, no agent selected' state
- setActiveSessionId now selects only the session; it clears m_agentId
and never auto-selects a lone agent.
- startStateStream no longer re-resolves or falls back to another agent;
it only runs when the explicit active agent still exists.
- QML now handles activeAgentId changes explicitly and keeps the right
pane blank/disabled when no session or no agent is selected.
- ListView and its placeholder label now have activeFocusOnTab: false
and focus: false so clicking the empty sidebar doesn't steal focus
from the prompt text area
- onEventReadyRead() now parses the eventwait data directly instead of
discarding it and re-reading session/idx
- Removed the 2-second refresh timer that was stealing focus from the
prompt text area on every tick
- Call ollie.refreshSessions() from Component.onCompleted before
buildSessionTree() for reliable initial load
- Add a 2-second periodic refresh timer as fallback for missed events
- ensureRootDataLoaded() now calls refreshSessions() so the initial
session list is populated immediately, not only on events
- Added '(no sessions)' placeholder label in the session tree ListView
when no sessions exist
- getAgents(): filter out 'new' (agent creation file), return empty list
- setActiveSessionId(): skip agent init when no agents exist
- submit/interrupt/kill/streams: guard against empty m_agentId
- prompt input: disable when no agent, show 'No agent' placeholder