Skip to content

Commit f7a94da

Browse files
fix(mothership): clamp-aware chase interrupt so the floor drain can't park the settle follow
1 parent 4bb8fbd commit f7a94da

1 file changed

Lines changed: 17 additions & 5 deletions

File tree

apps/sim/lib/core/utils/smooth-bottom-chase.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
5154
export 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

Comments
 (0)