Skip to content

fix(mothership): stop-button transitions freeze in place#5838

Open
TheodoreSpeaks wants to merge 2 commits into
stagingfrom
improve/thinking-blob
Open

fix(mothership): stop-button transitions freeze in place#5838
TheodoreSpeaks wants to merge 2 commits into
stagingfrom
improve/thinking-blob

Conversation

@TheodoreSpeaks

Copy link
Copy Markdown
Collaborator

Summary

  • Hitting stop no longer settle-scrolls: a user stop suppresses the post-stream chase (shouldFollowSettle), so the transcript freezes where it is
  • "Stopped by user" now swaps into the thinking shimmer's slot in the same render — stopping mid-"Thinking…" reads as an in-place replacement instead of a collapse/mount shuffle
  • Fixed a 1px/frame upward creep when the sizer floor is load-bearing (fractional min-height rounded scrollHeight above the scrolled-to extent; now Math.floored)

Type of Change

  • Bug fix

Testing

Tested manually on a local dev server (stop mid-thinking, credential-request reply); vitest 110/110 in the touched tree; lint + strict boundary validation + typecheck clean

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 22, 2026 4:06am

Request Review

@cursor

cursor Bot commented Jul 22, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
UI-only scroll and layout behavior in mothership chat; no auth, data, or API changes.

Overview
Stop generation now keeps the transcript fixed: mothership-chat sets a flag when the user hits stop and passes shouldFollowSettle into useAutoScroll, which skips the post-stream bottom chase that would nudge the view when the stopped row and actions mount.

"Stopped by user" no longer renders in the main segment list; it appears in the same tail slot as the thinking shimmer (and actions), so stopping mid-"Thinking…" reads as an in-place swap instead of the shimmer disappearing while a row appears higher up.

Sizer floor during streaming uses Math.floor on the scrolled extent so fractional min-height cannot inflate scrollHeight by 1px and cause a visible 1px/frame upward creep while the floor holds.

Reviewed by Cursor Bugbot for commit fff6119. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR changes how stopped Mothership responses are rendered and how their transcript scrolling settles. The main changes are:

  • Moves the stopped indicator into the thinking-shimmer slot.
  • Suppresses post-stream following after a user stop.
  • Rounds the virtualized transcript sizer floor to an integer.

Confidence Score: 4/5

Stopping during the final row animation can still trigger the settle chase, and the new floor rounding can shift fractional scroll positions.

  • The stop marker is cleared before the combined stream-and-animation lifecycle ends.
  • The delayed teardown can therefore follow content after a user stop.
  • Flooring a load-bearing fractional extent can leave the sizer slightly shorter than the viewport requires.

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

Important Files Changed

Filename Overview
apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.tsx Moves the terminal stopped indicator into the tail slot beside message actions.
apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx Adds stop-aware settle suppression and integer sizer-floor handling, with lifecycle and fractional-layout edge cases.
apps/sim/hooks/use-auto-scroll.ts Adds a ref-backed predicate that conditionally skips the post-stream settle chase.

Reviews (1): Last reviewed commit: "fix(mothership): swap stopped row into t..." | Re-trigger Greptile

Comment on lines +312 to +314
useEffect(() => {
if (isStreamActive) stopRequestedRef.current = false
}, [isStreamActive])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Stop Marker Clears Before Teardown

When a user stops while lastRowAnimating is true, auto-scroll remains active through isStreamActive || lastRowAnimating, but this effect clears the stop marker as soon as the stream ends. The later auto-scroll teardown then sees shouldFollowSettle as true and runs the settle chase, so the transcript can move after the user pressed stop.

Context Used: Custom hook patterns and best practices (source)

Comment on lines +354 to +360
// Math.floor, not the raw float: a fractional min-height can round
// scrollHeight 1px ABOVE the scrolled-to extent, and that phantom 1px gap
// re-derives 1px higher after every chase step — a visible 1px/frame
// upward creep whenever the floor is what's holding scrollHeight.
const floor = Math.max(
0,
Math.floor(el.scrollTop + el.clientHeight - padding.top - padding.bottom)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Sizer Floor Undershoots Viewport

When scrollTop is fractional and the sizer floor is load-bearing, flooring the required extent can make scrollHeight smaller than scrollTop + clientHeight by almost one CSS pixel. The browser then clamps the scroll position, and the next layout pass derives another floor from that shifted position, which can cause a small jump or repeated drift on fractional-pixel displays.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fff6119. Configure here.

}, [isStreamActive])
const { ref: autoScrollRef } = useAutoScroll(isStreamActive || lastRowAnimating, {
shouldFollowSettle: () => !stopRequestedRef.current,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stop still chases before settle gate

Medium Severity

shouldFollowSettle only skips the post-stream kickUntil on teardown, but auto-scroll stays armed while lastRowAnimating is still true. lastRowAnimating clears in a useEffect after paint, so when the stopped row and actions mount, the live MutationObserver can still chase.kick() for at least a frame before teardown cancels it — a visible nudge after stop despite the freeze intent.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fff6119. Configure here.

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.

1 participant