feat: spring-driven morph animation for askbar-to-chat transition#11
Merged
quiet-node merged 8 commits intomainfrom Apr 1, 2026
Merged
feat: spring-driven morph animation for askbar-to-chat transition#11quiet-node merged 8 commits intomainfrom
quiet-node merged 8 commits intomainfrom
Conversation
Replace the abrupt class-swap transition with a cohesive spring-physics morph: the container bounces open via Framer Motion layout animation, chat content slides down from above, and the input bar glides into position — all driven by a pronounced spring (stiffness 300, damping 20). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace Framer Motion layout transforms with explicit height animation (0 → auto) on ConversationView. Layout transforms used CSS scale which overflowed the native window boundary, causing bottom clipping during the spring overshoot. Height animation grows the chat area via real CSS height changes — no transforms, no clipping, no input bar overlap. The isMorphing ref is set synchronously during render so the spring transition is active on the exact frame where chat mode activates. After 600ms the ref flips to false, switching streaming resizes to instant (duration: 0) to prevent the input bar from lagging behind growing content. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove morph-detection refs and transition-switching logic that caused a visible height jump when the transition changed from spring to instant mid-animation. The spring (stiffness 300, damping 30) naturally handles both cases: large initial morph (0→200px) takes ~300ms for a visible effect, while streaming increments (~10-20px) settle in <50ms. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Framer Motion's height:'auto' measures once at mount and snaps when the spring finishes, causing a 50-100px jump when streaming tokens grow content during the animation. Replace with useLayoutEffect that temporarily flips to height:auto, measures natural height via getBoundingClientRect, restores the spring value (all before paint), and feeds the measurement to a useSpring. Capped at 600px so the flex chain stays intact and the scroll container can scroll when full. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two fixes for auto-scroll breaking when the conversation reaches max height: 1. Height cap: measure actual flex-available space (parent clientHeight minus sibling heights) instead of a hardcoded 600px cap. The spring now targets the exact rendered height, so the scroll container's internal layout matches the visible area — no more content hidden behind the input bar. 2. Auto-scroll: check scroll position fresh on each content change instead of relying on isUserNearBottomRef, which goes stale when spring animation triggers layout-induced scroll events. Treat "no overflow" as "at the bottom" so the growth-to-scroll transition is seamless. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The isUserNearBottomRef and handleScroll callback became dead code when the auto-scroll effect was rewritten to check scroll position fresh from the DOM. Remove the unused ref, callback, onScroll binding, and the test that only exercised the dead path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mock stubs must match framer-motion's `use`-prefixed export names; setTargetHeight in useLayoutEffect is intentional (measure before paint). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Apr 5, 2026
quiet-node
added a commit
that referenced
this pull request
Apr 10, 2026
* feat: add Apple Sheet spring morph transition for askbar-to-chat mode Replace the abrupt class-swap transition with a cohesive spring-physics morph: the container bounces open via Framer Motion layout animation, chat content slides down from above, and the input bar glides into position — all driven by a pronounced spring (stiffness 300, damping 20). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: use height animation for morph to prevent clipping and overlap Replace Framer Motion layout transforms with explicit height animation (0 → auto) on ConversationView. Layout transforms used CSS scale which overflowed the native window boundary, causing bottom clipping during the spring overshoot. Height animation grows the chat area via real CSS height changes — no transforms, no clipping, no input bar overlap. The isMorphing ref is set synchronously during render so the spring transition is active on the exact frame where chat mode activates. After 600ms the ref flips to false, switching streaming resizes to instant (duration: 0) to prevent the input bar from lagging behind growing content. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: use always-on spring for morph height animation Remove morph-detection refs and transition-switching logic that caused a visible height jump when the transition changed from spring to instant mid-animation. The spring (stiffness 300, damping 30) naturally handles both cases: large initial morph (0→200px) takes ~300ms for a visible effect, while streaming increments (~10-20px) settle in <50ms. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: replace height auto snap with spring-driven content tracking Framer Motion's height:'auto' measures once at mount and snaps when the spring finishes, causing a 50-100px jump when streaming tokens grow content during the animation. Replace with useLayoutEffect that temporarily flips to height:auto, measures natural height via getBoundingClientRect, restores the spring value (all before paint), and feeds the measurement to a useSpring. Capped at 600px so the flex chain stays intact and the scroll container can scroll when full. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: reliable auto-scroll during spring-driven height animation Two fixes for auto-scroll breaking when the conversation reaches max height: 1. Height cap: measure actual flex-available space (parent clientHeight minus sibling heights) instead of a hardcoded 600px cap. The spring now targets the exact rendered height, so the scroll container's internal layout matches the visible area — no more content hidden behind the input bar. 2. Auto-scroll: check scroll position fresh on each content change instead of relying on isUserNearBottomRef, which goes stale when spring animation triggers layout-induced scroll events. Treat "no overflow" as "at the bottom" so the growth-to-scroll transition is seamless. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: remove dead scroll-pinning ref and handler The isUserNearBottomRef and handleScroll callback became dead code when the auto-scroll effect was rewritten to check scroll position fresh from the DOM. Remove the unused ref, callback, onScroll binding, and the test that only exercised the dead path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * formated code * chore: suppress intentional eslint warnings with inline directives Mock stubs must match framer-motion's `use`-prefixed export names; setTargetHeight in useLayoutEffect is intentional (measure before paint). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> ---------
quiet-node
added a commit
that referenced
this pull request
Apr 10, 2026
* feat: add Apple Sheet spring morph transition for askbar-to-chat mode Replace the abrupt class-swap transition with a cohesive spring-physics morph: the container bounces open via Framer Motion layout animation, chat content slides down from above, and the input bar glides into position — all driven by a pronounced spring (stiffness 300, damping 20). * fix: use height animation for morph to prevent clipping and overlap Replace Framer Motion layout transforms with explicit height animation (0 → auto) on ConversationView. Layout transforms used CSS scale which overflowed the native window boundary, causing bottom clipping during the spring overshoot. Height animation grows the chat area via real CSS height changes — no transforms, no clipping, no input bar overlap. The isMorphing ref is set synchronously during render so the spring transition is active on the exact frame where chat mode activates. After 600ms the ref flips to false, switching streaming resizes to instant (duration: 0) to prevent the input bar from lagging behind growing content. * fix: use always-on spring for morph height animation Remove morph-detection refs and transition-switching logic that caused a visible height jump when the transition changed from spring to instant mid-animation. The spring (stiffness 300, damping 30) naturally handles both cases: large initial morph (0→200px) takes ~300ms for a visible effect, while streaming increments (~10-20px) settle in <50ms. * fix: replace height auto snap with spring-driven content tracking Framer Motion's height:'auto' measures once at mount and snaps when the spring finishes, causing a 50-100px jump when streaming tokens grow content during the animation. Replace with useLayoutEffect that temporarily flips to height:auto, measures natural height via getBoundingClientRect, restores the spring value (all before paint), and feeds the measurement to a useSpring. Capped at 600px so the flex chain stays intact and the scroll container can scroll when full. * fix: reliable auto-scroll during spring-driven height animation Two fixes for auto-scroll breaking when the conversation reaches max height: 1. Height cap: measure actual flex-available space (parent clientHeight minus sibling heights) instead of a hardcoded 600px cap. The spring now targets the exact rendered height, so the scroll container's internal layout matches the visible area — no more content hidden behind the input bar. 2. Auto-scroll: check scroll position fresh on each content change instead of relying on isUserNearBottomRef, which goes stale when spring animation triggers layout-induced scroll events. Treat "no overflow" as "at the bottom" so the growth-to-scroll transition is seamless. * refactor: remove dead scroll-pinning ref and handler The isUserNearBottomRef and handleScroll callback became dead code when the auto-scroll effect was rewritten to check scroll position fresh from the DOM. Remove the unused ref, callback, onScroll binding, and the test that only exercised the dead path. * formated code * chore: suppress intentional eslint warnings with inline directives Mock stubs must match framer-motion's `use`-prefixed export names; setTargetHeight in useLayoutEffect is intentional (measure before paint). ---------
quiet-node
added a commit
that referenced
this pull request
Apr 11, 2026
* feat: add Apple Sheet spring morph transition for askbar-to-chat mode Replace the abrupt class-swap transition with a cohesive spring-physics morph: the container bounces open via Framer Motion layout animation, chat content slides down from above, and the input bar glides into position — all driven by a pronounced spring (stiffness 300, damping 20). * fix: use height animation for morph to prevent clipping and overlap Replace Framer Motion layout transforms with explicit height animation (0 → auto) on ConversationView. Layout transforms used CSS scale which overflowed the native window boundary, causing bottom clipping during the spring overshoot. Height animation grows the chat area via real CSS height changes — no transforms, no clipping, no input bar overlap. The isMorphing ref is set synchronously during render so the spring transition is active on the exact frame where chat mode activates. After 600ms the ref flips to false, switching streaming resizes to instant (duration: 0) to prevent the input bar from lagging behind growing content. * fix: use always-on spring for morph height animation Remove morph-detection refs and transition-switching logic that caused a visible height jump when the transition changed from spring to instant mid-animation. The spring (stiffness 300, damping 30) naturally handles both cases: large initial morph (0→200px) takes ~300ms for a visible effect, while streaming increments (~10-20px) settle in <50ms. * fix: replace height auto snap with spring-driven content tracking Framer Motion's height:'auto' measures once at mount and snaps when the spring finishes, causing a 50-100px jump when streaming tokens grow content during the animation. Replace with useLayoutEffect that temporarily flips to height:auto, measures natural height via getBoundingClientRect, restores the spring value (all before paint), and feeds the measurement to a useSpring. Capped at 600px so the flex chain stays intact and the scroll container can scroll when full. * fix: reliable auto-scroll during spring-driven height animation Two fixes for auto-scroll breaking when the conversation reaches max height: 1. Height cap: measure actual flex-available space (parent clientHeight minus sibling heights) instead of a hardcoded 600px cap. The spring now targets the exact rendered height, so the scroll container's internal layout matches the visible area — no more content hidden behind the input bar. 2. Auto-scroll: check scroll position fresh on each content change instead of relying on isUserNearBottomRef, which goes stale when spring animation triggers layout-induced scroll events. Treat "no overflow" as "at the bottom" so the growth-to-scroll transition is seamless. * refactor: remove dead scroll-pinning ref and handler The isUserNearBottomRef and handleScroll callback became dead code when the auto-scroll effect was rewritten to check scroll position fresh from the DOM. Remove the unused ref, callback, onScroll binding, and the test that only exercised the dead path. * formated code * chore: suppress intentional eslint warnings with inline directives Mock stubs must match framer-motion's `use`-prefixed export names; setTargetHeight in useLayoutEffect is intentional (measure before paint). ---------
This was referenced Apr 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
height: 'auto'snap with a spring-driven height animation (useMotionValue+useSpring) that smoothly tracks growing content during streamingisUserNearBottomRef) andhandleScrollcallback that became unused after the auto-scroll rewriteTest plan
🤖 Generated with Claude Code