219 files, 2.0 MB, untracked in souveraine/docs and existing nowhere else. The volume is at 100% with no snapshots.
4.1 KiB
| task_id | title | status | assignee | priority | phase |
|---|---|---|---|---|---|
| ui-bubble-overflow-001 | Fix Message Bubble Overflow — Content Breaking Out of Bubble Borders | pending | medium | 3.0 |
Task: Fix Message Bubble Overflow
Objective
When agent messages contain long lines (code blocks, URLs, or wide text), the rendered markdown lines can exceed the bubble's computed width, breaking the box-drawing characters (│ │ border) and spilling content into the chat margin.
Current State
The bubble rendering in src/ui/chat.rs has a tension between two approaches:
-
bubble()(plain text, line 1044) — Useswrap_words()to wrap body text tomax_inner, then sets bubble width based on the widest wrapped line. This works correctly because wrapped lines are guaranteed to fit. -
bubble_rendered()(markdown, line 1107) — Takes pre-renderedVec<Line<'static>>frommarkdown::render()and places them inside the bubble. The bubble width is set towidest_line_width.min(max_inner). But: ifwidest_line_width > max_inner, the lines are clipped tomax_innerwhile the bubble border usesmax_inner + 4— so the content overflows past the│ │borders.
The root cause is in bubble_rendered() at line 1121:
let widest = body_lines.iter().map(|l| l.width()).max().unwrap_or(0).max(...);
let inner = widest.min(max_inner); // clips widest to max_inner
let outer = inner + 4;
When widest > max_inner, inner = max_inner but the rendered body_lines still contain lines wider than max_inner. The │ border is drawn at inner + 4 while content extends past it.
Additional Cases
- Surfacing bubbles (line 987): These also use
bubble()which handles wrapping, but surfacing content could still overflow if it contains code. - Tool cards (line 1209): These use
bubble_rendered()with the same overflow risk. - User bubbles (line 959): Use
bubble()with word wrapping — less risk but long URLs without spaces can still break out.
Implementation Options
Option A: Constrain markdown rendering width
Pass max_inner to markdown::render() so it wraps lines before they reach the bubble. The markdown renderer at src/ui/markdown.rs:74 currently has no width parameter — it splits on \n only. Adding a max_width parameter lets it wrap inline code and long text.
Pros: Fixes the root cause. All callers benefit.
Cons: Requires changes to markdown::render() API. More work.
Option B: Post-hoc wrapping in bubble_rendered
After rendering markdown, re-wrap any lines that exceed max_inner using the same wrap_words() logic from bubble(). Insert the re-wrapped lines and skip originals that exceed width.
Pros: Minimal changes, contained in bubble_rendered().
Cons: Code-style markers (bold, code spans) on long lines would need wrapping aware of Ratatui spans.
Option C: Auto-expand bubble width
Don't cap inner at max_inner. Let the bubble grow to the widest rendered line. Center-align or use scroll when the terminal is too narrow. This is how desktop chat apps work — the bubble grows, overflow is handled by viewport scroll.
Pros: No wrapping needed. Content is never truncated. Cons: Very long lines (1000+ char) would make the bubble span the entire terminal width. Feels wrong for a TUI.
Recommendation: Option A — constrain at the markdown renderer. Cleanest architecture, fixes all callers.
Investigation Needed
- Look at
src/ui/markdown.rs:74— understand the current renderer API and where amax_widthparam would fit. - Look at the four callers of
bubble_rendered()— which are most affected by overflow? - Check if the same issue exists in
bubble()for user messages with long URLs.
Files to Modify
| File | Change |
|---|---|
src/ui/markdown.rs |
Add max_width parameter to render(), implement word-wrapping for code blocks and long lines |
src/ui/chat.rs |
Pass max_inner to markdown::render() calls in bubble_rendered() and tool card rendering |
src/ui/chat.rs — bubble_rendered() |
Lines 1121-1123: remove the widest.min(max_inner) truncation since wrapping is now upstream |