Commit Graph

59 Commits

Author SHA1 Message Date
Levi Neely b6a7d93d0e gui: fix prose-after-fence overwriting finalized code segment
In incrementalAppend(), when a prose line follows a closed code fence,
the code checked only whether the last segment was type Text (which code
fences also are) and then tried to append. Since the finalized fence HTML
ends in </pre> not </div>, it fell into the 'shouldn't happen' branch and
overwrote the segment with escapeForHtml(proseLines) — destroying the
rendered tool output.

Tool blocks are always shaped:
  ```
  <output>
  ```
  <blank>
  <blank>

so the trailing blank lines triggered this on every tool call, leaving
only <div>\n</div> in renderedHtml.

Fix: gate extension on the segment actually being an in-progress prose
segment (trailing </div>). Otherwise start a new prose segment.

Verified: tool blocks now render 'hi\nexit: 0', file_glob results, and
syntax-highlighted diffs correctly.
2026-08-09 12:14:04 +02:00
Levi Neely 59e1992f37 gui: emit dataChanged on block close for segment updates
When a complete block arrives in a single streaming chunk, the QML
Repeater may not pick up intermediate segment updates because the
event loop hasn't run. Emit an explicit dataChanged with SegmentsRole
when [[[end]]] closes a block, ensuring the delegate shows the final
rendered content.
2026-08-09 11:57:49 +02:00
Levi Neely 93eba4442d gui: incremental rendering for chat blocks (O(n) → O(1) per line)
Previously, every streamed line triggered a full rebuild of the
block's HTML: reparsing all content, re-running syntax highlighting
on every code fence from the start. For a block with n lines this
was O(n) per line = O(n²) total.

Now appendContent() uses incrementalAppend() which:
- Prose: escapes only the new line and appends to the segment HTML
- Code fences: saves KSyntaxHighlighting::State between lines and
  only highlights the new line (O(1) per line)
- Fence close: finalizes segment, requests diagrams as before
- Partial line display: uses cached state, no full re-highlight

Full rebuildBlockHtml() is retained only for diagram reply callbacks
where the entire segment needs reconstruction.

Also adds BlockRenderState to ChatBlock for tracking incremental
parse position, highlight state, and accumulated HTML.
2026-08-09 00:12:22 +02:00
Levi Neely 66aecc8315 Revert "gui: fix freeze on agent switch — bulk load chat blocks"
This reverts commit a3826f2ce8.
2026-08-08 23:15:48 +02:00
Levi Neely a3826f2ce8 gui: fix freeze on agent switch — bulk load chat blocks
appendContent was calling rebuildBlockHtml (full syntax highlighting)
on every single line during initial log load. For a 1000-line chat,
that's O(n²) highlighting passes.

Add beginBulkLoad()/endBulkLoad() — skips per-line rebuilds during
bulk, then rebuilds all blocks once at the end. QML load sites now
wrap appendData(log) with bulk mode.
2026-08-08 23:14:55 +02:00
Levi Neely 0ac03a9931 Fix chat rendering and improve scroll behavior
- Emit SegmentsRole in dataChanged signals so QML updates properly
- Add onContentHeightChanged to scroll when content loads after session switch
- Add floating scroll-to-bottom button that appears when scrolled up
- Wrap chat ScrollView in Item to allow button overlay
2026-08-07 15:15:05 +02:00
Levi Neely 07232b94df Add PlantUML and Mermaid diagram rendering
- 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
2026-08-07 14:59:20 +02:00
Levi Neely 1ca5563fdb gui: stream partial content to UI before newlines
appendData() now emits dataChanged with partial line buffer content
so the UI updates as tokens arrive, not just after complete lines.
This fixes the laggy streaming where text would appear in chunks.
2026-08-07 10:11:14 +02:00
Levi Neely 28de16dbf1 fix(gui): rewrite chat block rendering for proper syntax highlighting
Reworked ChatBlockModel to fix syntax highlighting issues:

- Replace regex-based renderMarkdown() with line-by-line fence parsing
- Code fences detected by ```lang (open) and ``` alone (close)
- Prose wrapped in <div> with explicit palette text color
- Code blocks syntax highlighted with KSyntaxHighlighting

HtmlHighlighter improvements:
- Add defaultColor for unhighlighted text spans
- Track offset to emit gaps between highlighted tokens
- Add endLine() to flush trailing unhighlighted content

Remove TextEdit color property that was overriding inline styles in Qt5.
2026-08-03 16:16:43 +02:00
Levi Neely 34d4a97b3d gui: complete KF5 build with full feature parity
- Add chatblockmodel.cpp and thememanager.cpp to KF5 GUI target
- Link KF5::ConfigCore and KF5::SyntaxHighlighting to GUI
- Strip Qt6-only grouped palette syntax from QML at configure time
  (ThemeManager::applyPalette handles palette via QGuiApplication)
- Fix QStringList brace-init ambiguity in Qt5
- Fix QList::remove(pos,count) not available in Qt5 (use erase)
2026-08-03 09:03:12 +02:00
Levi Neely 7cb809fad2 revert all system-user-interruption changes 2026-08-03 00:41:49 +02:00
Levi Neely 64883cf9b8 Revert "render system-user-interruption as styled yellow box in prose"
This reverts commit 4e455e2dd3.
2026-08-03 00:39:34 +02:00
Levi Neely 4e455e2dd3 render system-user-interruption as styled yellow box in prose 2026-08-03 00:38:01 +02:00
Levi Neely e08b69c152 fix build: replace-with-lambda requires Qt 6.5+, use manual global-match loop 2026-08-03 00:32:26 +02:00
Levi Neely 1fdae741c3 render system-user-interruption as a styled yellow box with warning icon 2026-08-03 00:31:08 +02:00
Levi Neely cf4313165b fix fence close regex: only match backtick on its own line 2026-08-03 00:27:25 +02:00
Levi Neely b841f556d4 gui: preserve newlines in plaintext blocks 2026-08-03 00:24:52 +02:00
Levi Neely b50a2e01b2 fix source block rendering 2026-08-02 23:48:00 +02:00
Levi Neely c983e2bb6a simplify rendering 2026-08-02 22:43:11 +02:00
Levi Neely 753467db5b fix: preserve pre-fence text and post-fence content in rendered blocks
When a fence () closes inside a block, prepend any content
that came before the fence to renderedHtml so both text and the
highlighted fence show up. When content is appended after the fence,
keep renderedHtml in sync by appending the escaped text. This prevents
the fence from 'eating' surrounding content.
2026-08-02 11:13:22 +02:00
Levi Neely 67b33f95d2 refactor: StreamFsm lifecycle + chatblock model FSM fix
- Introduce StreamFsm class wrapping QProcess lifecycle with
  Oneshot/Looping/Guarded restart policies
- Replace raw QProcess management in Ollie9pClient with StreamFsm
- Fix chatblockmodel FSM to properly handle [[[end]]] and fence
  transitions with a combined State enum (Outside/InBlock/InFence)
- Add Q_INVOKABLE to setActiveSessionId for direct QML access
- Add streamfsm.cpp to CMakeLists.txt for both KF5 and KF6 builds
2026-08-02 11:11:05 +02:00
Levi Neely 8617aa5293 gui: render user-interrupt with yellow background and warning icon
- Add Interrupt block type to ChatBlock enum
- Detect <system-user-interruption> and </system-user-interruption>
  tags as block openers/closers in the parser
- Render interrupt blocks with bright yellow header
  (#3d2e00 dark, #fff8e1 light)
- Show ⚠ User Interrupt header label
2026-08-01 17:11:52 +02:00
Levi Neely 998efc15c5 chat log: switch to [[[type]]]/[[[end]]] block format
Replace the single-bracket INI-like format with triple-bracket blocks
with explicit [[[end]]] terminators. The parser tracks m_inBlock to
ignore content between blocks, making the format robust against
accidental header-like content in tool output or LLM responses.

- Go sender: FormatEvent + NewEventHandler + replayMessagesToLog
- C++ parser: new regex for [[[type]]], new [[[end]]] handler, m_inBlock gate
- QML: update hardcoded [info] string
2026-08-01 16:48:57 +02:00
Levi Neely 3a33c4e42a add retry block support: Retry type, header color, expanded default 2026-08-01 12:45:33 +02:00
Levi Neely 7d75ba8524 gui: preserve original indentation in fenced code/diff blocks
- C++: Use <pre style="white-space:pre-wrap;"> instead of <div> with <br>
  so raw newlines are preserved through to the HTML renderer
- QML: Use TextEdit (preserves whitespace) instead of Label (collapses it)
  for rendering blockRenderedHtml
2026-08-01 12:00:14 +02:00
Levi Neely 24edd5941a gui: syntax-highlight fenced code/diff blocks at stream end
- Add fence state machine (NoFence → InFence) to ChatBlockModel
- On closing ```, render via KSyntaxHighlighting or inline diff colorizer
- Store rendered HTML in ChatBlock.renderedHtml, emit dataChanged on RenderedHtmlRole
- QML shows RichText Label when renderedHtml is populated, falls back to PlainText
- Add KF6::SyntaxHighlighting dependency for ollie-gui target
2026-08-01 11:36:43 +02:00
Levi Neely df41aad5cb trim chat to last 30 blocks on initial load and session switch 2026-08-01 10:19:22 +02:00
Levi Neely 857a919b64 streaming INI parser: ChatBlockModel with per-block decorations 2026-08-01 09:37:48 +02:00
Levi Neely e7b3291826 cleanup: delete all dead D-Bus code and unused UI components
Removed:
- plasmoid/ (entire directory — QML plugin, D-Bus client, metadata)
- tray/ (entire directory — KStatusNotifierItem, D-Bus watcher)
- gui/chatblockmodel.* (replaced by plain text streaming)
- gui/thememanager.* (unused — Basic style handles palette)
- gui/ChatPane.qml, SessionGraph.qml (complex delegate system, gone)
- gui/emojis.js (emoji shortcode completion, can re-add later)
- gui/main-kf5.qml (duplicate of main.qml)

What remains in gui/:
- main.cpp, main.qml — 9P-based minimal GUI
- ollie9pclient.cpp/h — QProcess-based 9P transport
- qml.qrc, ollie.svg, desktop file
2026-07-31 20:44:22 +02:00
Levi Neely f4161fcabc gui: replace D-Bus with native 9P streaming
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).
2026-07-31 20:06:05 +02:00
Levi Neely 5bdab3c616 remove emoji reaction feature (KDE GUI) 2026-07-31 09:09:30 +02:00
Levi Neely c76a144de2 diagrams: re-enable mermaid server via /img/ (raster) endpoint
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.
2026-07-29 16:40:18 +02:00
Levi Neely a598d0c5cc diagrams: render mermaid as PNG instead of SVG
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).
2026-07-29 16:38:34 +02:00
Levi Neely 3b27bccdda diagrams: force light theme for mermaid rendering
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.
2026-07-29 16:25:36 +02:00
Levi Neely 29c74b2837 feat(gui): render PlantUML, Mermaid, and draw.io diagrams as images
- 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
2026-07-29 15:23:29 +02:00
Levi Neely 1cce42f2d6 fix: setReactions updates blocks in-place with dataChanged signal 2026-07-27 19:50:53 +02:00
Levi Neely d5f0770b0a structured reactions via getReactions/setReactions, remove broken chat-text parsing 2026-07-27 19:47:21 +02:00
Levi Neely 6e02ea4ca3 fix: update assistant block reaction badge on incremental parse 2026-07-27 19:33:15 +02:00
Levi Neely 042acf347e fix: parse reaction markers from [user] block content 2026-07-27 19:30:10 +02:00
Levi Neely cf1f0f75d9 pin reaction badge on assistant blocks, hide reaction buttons after reacting 2026-07-27 19:27:20 +02:00
Levi Neely e2592d821f propagate response IDs through chat models to reactTo 2026-07-27 19:12:12 +02:00
Levi Neely dc18436bae gui: Ctrl+F filter bar to search chat messages
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.
2026-07-27 09:37:26 +02:00
Levi Neely b9e02a972a gui: inline video embeds in chat
Detect video URLs (YouTube, Vimeo, Dailymotion, Twitch, direct media)
in chat responses. Direct .mp4/.webm/.ogv files play inline via
QtMultimedia Video. Other video sites show a card with play button
that opens in the system browser.

Adds Qt6::Multimedia dependency.
2026-07-18 18:46:35 +02:00
Levi Neely 7703d06430 gui: revert incremental parsing, use full reparse always
Incremental parsing caused phantom blocks and disappearing content.
The 150ms coalescing on the daemon side is sufficient for perf.
updateVisibleWindow() extraction kept for clean separation.
2026-07-18 18:12:10 +02:00
Levi Neely c46f38fc93 gui: fix incremental parser producing phantom tool_call blocks
When the tail contains only one header (common case during streaming),
just update the last block content directly. Fall back to full reparse
only when a genuinely new block boundary appears.
2026-07-18 18:04:42 +02:00
Levi Neely 7ccce95445 gui: incremental chat parsing during streaming
reparseIncremental() only re-parses from the last block boundary
instead of the entire chat text. Fixes UI lag and typing blockage
during long streaming responses.

Also extracted updateVisibleWindow() for reuse.
2026-07-18 17:36:36 +02:00
Levi Neely 9e0e5cb0f4 Improve KDE GUI file and command handling 2026-07-16 19:42:21 +02:00
Levi Neely 1d646f7fab gui: fix flickering, add code exec button, fix bubble width, improve scroll
- 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
2026-07-16 13:20:08 +02:00
Levi Neely 2929bb6b04 kde: visual enhancements, emoji completion, scroll/layout fixes
ollie-gui:
- Structured chat blocks (user/assistant bubbles, tool/call with left borders)
- Tool/call blocks always visible (not collapsible), whitespace preserved
- User interrupt blocks (amber warning style)
- Copy buttons on bubbles (32x32, hover-visible)
- Emoji completion with popup selector + quick emoticons
- Backend/model display in header bar
- Session state updates on switch
- Scroll flicker fix (debounced, guarded)
- Null guards on delegate block properties
- Binding loop fixes

Kate plugin:
- Diff viewer integration via Kate DiffWidget (libkateprivate)
- Diff mode config (unified/side-by-side/raw)
- Emoji processing in prompt input (quick emoticons + :shortcode:)
- Increased chat font size
- Scroll position preservation

Dbus client:
- activeModel/activeBackend properties from GetConfig
2026-07-16 11:19:22 +02:00
Levi Neely cc24400ba7 fix indentation: wrap highlighted HTML in <pre> with white-space:pre-wrap
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.
2026-07-16 07:20:55 +02:00