@@ -44,22 +44,27 @@ export interface SmoothBottomChaseHandle {
4444 *
4545 * Self-interrupting: chase writes only ever move the offset down, and content
4646 * growth leaves it where the last write put it — so an offset that moved UP
47- * since the last write can only be a user scrolling away, and the loop parks
48- * instead of fighting them. `shouldContinue` layers any caller-owned stickiness
49- * on top (checked every frame).
47+ * since the last write, MORE than the bottom itself moved up, can only be a
48+ * user scrolling away, and the loop parks instead of fighting them. (A
49+ * content-shrink clamp moves the offset and the bottom together — e.g. the
50+ * transcript's floor drain — and must not read as a user scroll.)
51+ * `shouldContinue` layers any caller-owned stickiness on top (checked every
52+ * frame).
5053 */
5154export function createSmoothBottomChase (
5255 target : SmoothBottomChaseTarget ,
5356 shouldContinue : ( ) => boolean = ( ) => true
5457) : SmoothBottomChaseHandle {
5558 let raf : number | null = null
5659 let lastTop : number | null = null
60+ let lastBottomTop : number | null = null
5761 let deadline = 0
5862
5963 const park = ( ) => {
6064 if ( raf !== null ) cancelAnimationFrame ( raf )
6165 raf = null
6266 lastTop = null
67+ lastBottomTop = null
6368 deadline = 0
6469 }
6570
@@ -74,11 +79,17 @@ export function createSmoothBottomChase(
7479 return
7580 }
7681 const top = target . getTop ( )
77- if ( lastTop !== null && top < lastTop - 1 ) {
82+ const bottomTop = target . getBottomTop ( )
83+ if (
84+ lastTop !== null &&
85+ lastBottomTop !== null &&
86+ lastTop - top > lastBottomTop - bottomTop + 1
87+ ) {
7888 park ( )
7989 return
8090 }
81- const gap = target . getBottomTop ( ) - top
91+ lastBottomTop = bottomTop
92+ const gap = bottomTop - top
8293 if ( gap <= CHASE_REST_GAP ) {
8394 // Within a kickUntil deadline, idle at rest instead of parking so
8495 // trigger-less growth inside the window is still chased.
@@ -107,6 +118,7 @@ export function createSmoothBottomChase(
107118 const start = ( ) => {
108119 if ( raf !== null ) return
109120 lastTop = target . getTop ( )
121+ lastBottomTop = target . getBottomTop ( )
110122 raf = requestAnimationFrame ( step )
111123 }
112124
0 commit comments