Skip to content

fix(ui): stop code blocks from widening chat bubbles and make their scrollbar usable - #1189

Merged
catatafishen merged 1 commit into
catatafishen:masterfrom
BambinoSK:fix/native-chat-code-block-overflow
Oct 17, 2026
Merged

catatafishen merged 1 commit into
catatafishen:masterfrom
BambinoSK:fix/native-chat-code-block-overflow

Conversation

@BambinoSK

@BambinoSK BambinoSK commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1180

Problem

When a chat bubble contains a fenced code block with one long line, the whole bubble body (prose paragraphs included) is laid out at the code width instead of the bubble width. Every prose line then clips mid-word at the right edge of the bubble. Issue #1180 reports this for the thinking bubble; the same mechanism affects message bubbles.

Root cause: ScrollableCodeView (the view behind <pre> blocks) overrides getPreferredSpan(X_AXIS) to report 0 so the pane does not inflate to the code width, but it did not override getMinimumSpan. BoxView.layoutMinorAxis never allocates a child less than its minimum span (spans = max(childMin, targetSpan)), and the view's inline children are deliberately uncapped so code lines stay intact for horizontal scrolling. The longest code line's minimum therefore propagated up through the whole box hierarchy, and the body laid out at the code width no matter how narrow the bubble was.

What this PR does

  • ScrollableCodeView.getMinimumSpan(X_AXIS) now reports 0, mirroring the existing preferred-span override. The document body lays out at the bubble width again; code blocks keep their natural line width and remain horizontally scrollable, by design.
  • The per-block overlay scrollbar is now actually usable. On master it was painted decoration with no mouse handling:
    • the strip is draggable (press, drag, release); a press on the track jumps the thumb immediately
    • strip events are intercepted before the caret listeners, so dragging never selects text
    • scrolling invalidates the pane's paint cache; previously the cache key did not include the scroll position, so every repaint blitted the pre-scroll frame and scrolling looked dead
    • scroll is baked into BoxView.getOffset geometry instead of a paint-time translation, so hit-testing (viewToModel), the caret and selection painting agree with the painted pixels; selecting in a scrolled block no longer hits wrong characters
    • Shift+wheel over an overflowing block scrolls the block; a plain wheel is always forwarded so the chat keeps scrolling vertically
    • a text selection dragged past the right edge of a scrolled block auto-scrolls the block with the selection
    • a widening resize re-clamps or clears the scroll so no stale offset survives; Shift+wheel only owns blocks that actually overflow, and an in-progress strip drag is dropped when the document re-renders

Notes for reviewers

  • The min-span override is the one-line fix for the width bug; the rest of the commit makes the scrollbar machinery that already shipped on master (clip + 6px overlay strip) functional. Soft-wrapping code lines was considered and rejected as a bigger behavioral change: it would break line integrity for diff blocks and long commands that users expect to scroll.
  • The thinking-bubble test reproduces the Native chat panel: thinking bubble text is laid out wider than the panel and clips every line at the right edge #1180 geometry end to end in a real NativeChatPanel: prose streamed in chunks like session/update, laid out headlessly, laid-out content measured against the pane width. With the fix reverted it fails (prose at 633px in a 175px pane); with the fix it passes.
  • The overflow test paints the laid-out view tree onto a canvas wider than the pane with no clip, so overflow shows up as painted pixels rather than only as view-tree numbers. A 12px pixel tolerance covers glyph rasterization fringes from lines that wrap flush against the edge; the guarded regression paints 300+ full columns.

Verification

  • New tests, all green:
    • NativeMarkdownOverflowTest (8): overflow matrix - plain prose, long inline code, indented code, list-item inline code, table code cell, blockquote code, fenced block with a long line (complete and streamed parse)
    • NativeMarkdownCodeScrollInteractionTest (8): strip drag to max without selecting text, track-click jump, press outside strip passes through, plain wheel passes through to the chat, Shift+wheel consumed, selection drag auto-scrolls, widening resize clears a stale scroll, Shift+wheel over a non-overflowing block passes through
    • NativeChatThinkingBubbleWidthTest (2): prose-only and prose + code block in the thinking bubble stay inside the pane
  • Existing NativeMarkdownWrappingTest (10) still green.
  • RED check: disabling only the getMinimumSpan override makes the fenced and thinking-bubble tests fail, the prose-only tests stay green (prose always wrapped; only the code-block channel regresses).

How to verify

  1. Build the plugin, open a chat panel, narrow it to about a quarter of the tool window.
  2. Ask the agent something that produces a fenced code block with one long line (for example a git log --graph --pretty=format:... command) with prose around it.
  3. On master the prose lines clip mid-word at the bubble edge; with this PR the prose wraps at the bubble width and the block keeps its long line.
  4. Over the code block: Shift+wheel scrolls it horizontally, plain wheel scrolls the chat, the strip at the bottom of the block drags and jumps on click, selecting text in the block and dragging past the right edge scrolls the block along.

…crollbar usable

A fenced code block with one long line laid the whole document body,
prose included, out at the code width, so every prose line clipped at
the bubble edge (catatafishen#1180). ScrollableCodeView hides its preferred X span
but not its minimum, and BoxView.layoutMinorAxis never allocates a
child below its minimum, so the uncapped code-line minimum propagated
up through the whole box hierarchy.

- ScrollableCodeView.getMinimumSpan(X_AXIS) = 0, mirroring the
  preferred-span override that already exists
- scrollbar strip is now interactive: thumb drag, track-click jump,
  events intercepted before the caret so no text gets selected
- scrolling invalidates the paint cache (stale-frame blit made every
  scroll visually dead) and is baked into getOffset geometry so
  selection hit-testing matches the painted pixels
- Shift+wheel scrolls an overflowing block, plain wheel always passes
  through to the chat; a widening resize re-clamps or clears the
  scroll so no stale offset survives
- a selection dragged past the block edge auto-scrolls the block
- an in-progress strip drag is dropped when the document re-renders

Tests: overflow matrix incl. the streaming path and the real
thinking-bubble hierarchy from catatafishen#1180, interaction coverage for the
scrollbar, resize and non-overflowing wheel cases.
@github-actions

Copy link
Copy Markdown

⚠️ ClusterFuzzLite — Maintainer Security Review Required

A maintainer needs to approve the ClusterFuzzLite PR fuzzing workflow run for this fork PR.

Before approving, please review the code changes from BambinoSK/agentbridge@9852b563 to confirm there is no code that could:

  • Exfiltrate the CFLITE_STORAGE_REPO_TOKEN CI secret
  • Abuse CI infrastructure or the fuzz corpus storage repository

The fuzz targets from the fork branch will be compiled and executed with access to repository secrets. Rubber-stamping this approval without reviewing the code is a security risk.

This comment is posted automatically whenever ClusterFuzzLite is triggered on a fork PR.

@codecov

codecov Bot commented Oct 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 59.13043% with 47 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
.../catatafishen/agentbridge/ui/NativeMarkdownPane.kt 59.13% 25 Missing and 22 partials ⚠️

📢 Thoughts on this report? Let us know!

@catatafishen
catatafishen merged commit 3ac17fb into catatafishen:master Oct 17, 2026
19 of 20 checks passed

This branch is waiting to be deployed

1 waiting deployment
fuzzing-gate — 9852b563 Waiting Oct 17, 2026 by BambinoSK via Maintainer approval gate (fork PR) #1640
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Native chat panel: thinking bubble text is laid out wider than the panel and clips every line at the right edge

2 participants