fix(desktop): restore scroll on the capped thinking preview

The live thinking body pins to the newest tokens and keeps max-h-40
after the turn settles so the transcript doesn't jump. overflow-hidden
let that pin work in JS but clipped the rest of the thought. overflow-auto
makes the cap a real scroller; overscroll-contain keeps the wheel from
chaining into the transcript.

Supersedes #73757.

Co-authored-by: Dan Latimer <latdani@gmail.com>
This commit is contained in:
Brooklyn Nicholson
2026-08-26 13:40:35 -05:00
committed by brooklyn!
parent 19f9d1badb
commit be7eefec8d
2 changed files with 26 additions and 2 deletions

View File

@@ -237,10 +237,12 @@ const ThinkingDisclosure: FC<{
className={cn(
// Body sits flush with the "Thinking" header — no left indent —
// and inherits the disclosure-level opacity fade defined in
// styles.css (~0.67 at rest, 1 on hover/focus).
'mt-0.5 w-full min-w-0 max-w-full overflow-hidden wrap-anywhere pb-1',
// styles.css (~0.67 at rest, 1 on hover/focus). overflow-auto so
// the max-h-40 preview is a real scroller, not a clip.
'mt-0.5 w-full min-w-0 max-w-full overflow-auto overscroll-contain wrap-anywhere pb-1',
isPreview && 'max-h-40'
)}
data-slot="aui_thinking-body"
ref={scrollRef}
>
<div ref={contentRef}>{children}</div>

View File

@@ -621,6 +621,28 @@ describe('assistant-ui streaming renderer', () => {
expect(container.textContent).not.toContain('```ts')
})
it('keeps the height-capped thinking preview scrollable after the turn settles', async () => {
const { container, settle } = renderSettlingReasoning()
const live = container.querySelector('[data-slot="aui_thinking-body"]')?.className ?? ''
expect(live).toContain('max-h-40')
expect(live).toMatch(/\boverflow-auto\b/)
expect(live).not.toMatch(/\boverflow-hidden\b/)
settle()
await waitFor(() => {
expect(within(container).getByRole('button', { name: /thought/i })).toBeTruthy()
})
const settled = container.querySelector('[data-slot="aui_thinking-body"]')?.className ?? ''
expect(settled).toContain('max-h-40')
expect(settled).toMatch(/\boverflow-auto\b/)
expect(settled).not.toMatch(/\boverflow-hidden\b/)
})
it('does not collapse a live thinking preview when the turn settles', async () => {
const { container, settle } = renderSettlingReasoning()
const toggle = within(container).getByRole('button', { name: /thinking/i })