Skip to content

text_view: Let fenced code blocks scroll inside a height cap - #3322

Merged
huacnlee merged 2 commits into
longbridge:mainfrom
jarviisha:feat/text-view-code-block-scroll
Oct 1, 2026
Merged

huacnlee merged 2 commits into
longbridge:mainfrom
jarviisha:feat/text-view-code-block-scroll

Conversation

@jarviisha

@jarviisha jarviisha commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Closes #3321

Description

A long fenced code block in a chat answer rendered with TextView::markdown inside a gpui::list pushes the rest of the answer off screen. Capping it with TextViewStyle::code_block(StyleRefinement::default().max_h(..)) could only clip it, and setting overflow.y = Scroll on the refinement didn't work properly: the block only scrolled when code_block_actions gave it an id, it had no scrollbar, and one wheel tick scrolled both the code and the parent list.

This follows the existing table pattern (overflow_x: scroll on style.table). When the code_block refinement sets overflow.y to Overflow::Scroll, CodeBlock::render wraps the block with:

  • its own ScrollHandle, kept per block with use_keyed_state (block_element_id("codeblock-scroll", span, ix)), as tables do;
  • a vertical ScrollableMask, which consumes the wheel in the capture phase so an ancestor gpui::list doesn't scroll on the same event. Once the code reaches its top or bottom edge, the mask lets the wheel through to the list, as native nested scrollers do;
  • a vertical Scrollbar.

The mask takes the block's id, as the table's mask does. The mask, the scrollbar and the code_block_actions box are siblings of the scrolled block, so the copy button stays pinned to the block's top-right corner. Scrolling doesn't depend on code_block_actions any more. Without overflow.y set to Overflow::Scroll the rendered tree is unchanged.

docs/ARCHITECTURE.md lists the code block next to the table under "Scrolling and Virtualization".

let mut code_block = StyleRefinement::default().max_h(px(320.));
code_block.overflow.y = Some(Overflow::Scroll);
TextView::markdown(id, text).style(TextViewStyle::default().code_block(code_block))

Known limit, shared with the table scroll layout: if the refinement sets a margin or a fixed width, the mask, scrollbar and actions follow the outer frame rather than the block.

Public API

No items are added, changed or removed. One existing item gains a documented behavior:

  • gpui_base::text::TextViewStyle::with_code_block(style: StyleRefinement) -> Self and gpui_component::text::TextViewStyle::code_block(style: StyleRefinement) -> Self: with overflow.y set to Overflow::Scroll and a max height on style, long code scrolls inside the block with its own scrollbar instead of growing it.

How to Test

  • cargo test -p gpui-base --lib scrolling_code_block_keeps_wheel_from_the_list: a scrollable TextView (backed by gpui::list) with a capped scrolling code block, run with and without code_block_actions. From the painted text lines it asserts that a wheel over the code scrolls the code by exactly the wheel delta while the paragraph below (the list) and the pinned actions stay put, and that a wheel below the code still scrolls the list. The test fails without this change: the list scrolls under the code block.
  • cargo test -p gpui-base --lib text:: and cargo test -p gpui-component pass.
  • Checked manually in the story gallery on Linux (Hyprland) with a temporary TextView demo: the block stops at the cap, shows a scrollbar, and the Copy button stays in the top-right corner.

This change touches gpui-base, because CodeBlock and TextView live there.

The code and this description were written with AI assistance (Claude) and reviewed by me.

Checklist

  • I have read the CONTRIBUTING document and followed the guidelines.
  • Reviewed the changes in this PR and confirmed AI generated code (If any) is accurate.
  • Passed cargo run for story tests related to the changes.
  • Tested macOS, Windows and Linux platforms performance (if the change is platform-specific)

🤖 Generated with Claude Code

Setting `overflow_y: scroll` on the `code_block` style refinement now puts
the block in a vertical scroll viewport with its own `ScrollHandle`, a
`ScrollableMask` and a `Scrollbar`, like tables do with `overflow_x: scroll`.
The mask consumes the wheel in the capture phase, so a parent `gpui::list`
no longer scrolls together with the code. The code block actions stay
pinned to the block's top-right corner.

Closes longbridge#3321

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@jarviisha
jarviisha marked this pull request as draft September 30, 2026 04:02
…nd fix docs

The scrolling code block test now also runs without `code_block_actions`,
the case that couldn't scroll at all before, and asserts that the code
itself moves, not only that the list stays put. The vertical mask takes
the block's id, as the table's mask does. The style docs and comments name
the real `overflow.y` field, and ARCHITECTURE.md lists the code block next
to the table under scrolling.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@jarviisha
jarviisha marked this pull request as ready for review September 30, 2026 04:28
@huacnlee
huacnlee merged commit 21113ca into longbridge:main Oct 1, 2026
12 checks passed
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.

TextView: allow fenced code blocks to scroll inside a height cap

2 participants