fix(chat): stop the streaming transcript floor inventing scroll space… · simstudioai/sim@1a39e29 · GitHub
Skip to content

Commit 1a39e29

Browse files
authored
fix(chat): stop the streaming transcript floor inventing scroll space (#6527)
* fix(chat): stop the streaming transcript floor inventing scroll space The sizer floor was the viewport's bottom edge (scrollTop + clientHeight), which exceeds the content height whenever the transcript is shorter than the viewport. That invents scrollable space no content occupies, and a mid-turn container shrink turns it into real scroll room the bottom-pin scrolls into. Clamp the floor to the space content has actually held this turn: the max of the virtualizer's total size and the still-applied floor. The applied-floor term keeps undrained debt across a turn boundary that interrupts the drain. * fix(chat): release the transcript floor when the chat changes The high-water mark and applied floor are per-turn refs on a component that survives a chat switch, so a tall chat's mark could size a newly opened short one for as long as the outgoing turn kept the floor engaged. Release both outright on a chat change — the switch re-lands the viewport, so there is no eased settle to preserve — while treating a pending chat adopting its id as the same conversation. Also switch the sizer-floor import to the absolute path convention.
1 parent ff64389 commit 1a39e29

3 files changed

Lines changed: 191 additions & 13 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx‎

Lines changed: 37 additions & 13 deletions
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { describe, expect, it } from 'vitest'
5+
import { nextSizerFloor } from '@/app/workspace/[workspaceId]/home/components/mothership-chat/sizer-floor'
6+
7+
/** Matches the transcript scroller's `pt-4 pb-2`. */
8+
const PADDING = { paddingTop: 16, paddingBottom: 8 }
9+
/** A viewport scrolled to the bottom of 2000px of content: extent resolves to 2000. */
10+
const PINNED = { scrollTop: 1424, clientHeight: 600, ...PADDING }
11+
12+
describe('nextSizerFloor', () => {
13+
it('follows the scrolled-to extent while it stays under the high-water mark', () => {
14+
const { floor } = nextSizerFloor({
15+
...PINNED,
16+
previousHighWater: 5000,
17+
appliedFloor: 0,
18+
contentHeight: 5000,
19+
})
20+
expect(floor).toBe(2000)
21+
})
22+
23+
it('floors fractional scroll offsets down so the sizer cannot round above the extent', () => {
24+
const { floor } = nextSizerFloor({
25+
scrollTop: 100.7,
26+
clientHeight: 600,
27+
...PADDING,
28+
previousHighWater: 5000,
29+
appliedFloor: 0,
30+
contentHeight: 5000,
31+
})
32+
expect(floor).toBe(676)
33+
})
34+
35+
it('never returns a negative floor for a container smaller than its padding', () => {
36+
const { floor } = nextSizerFloor({
37+
scrollTop: 0,
38+
clientHeight: 8,
39+
...PADDING,
40+
previousHighWater: 500,
41+
appliedFloor: 0,
42+
contentHeight: 500,
43+
})
44+
expect(floor).toBe(0)
45+
})
46+
47+
it('never exceeds the content height when the transcript is shorter than the viewport', () => {
48+
const { floor } = nextSizerFloor({
49+
scrollTop: 0,
50+
clientHeight: 600,
51+
...PADDING,
52+
previousHighWater: 0,
53+
appliedFloor: 0,
54+
contentHeight: 180,
55+
})
56+
expect(floor).toBe(180)
57+
})
58+
59+
it('holds the high-water mark when content re-measures smaller mid-turn', () => {
60+
const { floor, highWater } = nextSizerFloor({
61+
...PINNED,
62+
previousHighWater: 2000,
63+
appliedFloor: 2000,
64+
contentHeight: 1940,
65+
})
66+
expect(highWater).toBe(2000)
67+
expect(floor).toBe(2000)
68+
})
69+
70+
it('carries undrained debt across a turn boundary that interrupts the drain', () => {
71+
const { floor, highWater } = nextSizerFloor({
72+
...PINNED,
73+
previousHighWater: 0,
74+
appliedFloor: 1985,
75+
contentHeight: 1940,
76+
})
77+
expect(highWater).toBe(1985)
78+
expect(floor).toBe(1985)
79+
})
80+
81+
it('raises the high-water mark as content grows', () => {
82+
const { highWater } = nextSizerFloor({
83+
...PINNED,
84+
previousHighWater: 1200,
85+
appliedFloor: 1200,
86+
contentHeight: 1600,
87+
})
88+
expect(highWater).toBe(1600)
89+
})
90+
})
Lines changed: 64 additions & 0 deletions

0 commit comments

Comments
 (0)