fix: eliminate streaming jitter during upward window growth - #10
Merged
Conversation
When the overlay spawns near the bottom screen edge and grows upward, per-token window resizing caused visible jitter because macOS can't atomically reposition + resize in sync with the webview render cycle. Fix: pre-expand the window to max chat height in a single IPC call when streaming starts, skip per-token resizes during streaming, and flip the outer container to justify-end so content pins to the bottom and grows upward naturally within the already-sized frame. The ResizeObserver resumes normal duty once streaming ends to shrink-to-fit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lash Move the pre-expansion from streaming start to overlay open. The first ResizeObserver event now immediately expands the window to max chat height — this fires during Framer Motion's entrance fade-in (opacity 0→1), making the expansion invisible. By the time the user interacts with the input bar, the window is already at full size. Simplifies the implementation: removes useLayoutEffect, isGeneratingRef, containerNodeRef, and the container freeze/transition animation. Replaces them with a single isPreExpandedRef flag that gates the ResizeObserver. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Don't clear isAnchoredUpward in requestHideOverlay — flipping from justify-end to justify-start during the Framer Motion exit animation caused content to jump from bottom to top of the max-height window. The state is already reset in replayEntranceAnimation on next open. 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
* fix: eliminate streaming jitter during upward window growth When the overlay spawns near the bottom screen edge and grows upward, per-token window resizing caused visible jitter because macOS can't atomically reposition + resize in sync with the webview render cycle. Fix: pre-expand the window to max chat height in a single IPC call when streaming starts, skip per-token resizes during streaming, and flip the outer container to justify-end so content pins to the bottom and grows upward naturally within the already-sized frame. The ResizeObserver resumes normal duty once streaming ends to shrink-to-fit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: pre-expand anchored window at overlay open to eliminate submit flash Move the pre-expansion from streaming start to overlay open. The first ResizeObserver event now immediately expands the window to max chat height — this fires during Framer Motion's entrance fade-in (opacity 0→1), making the expansion invisible. By the time the user interacts with the input bar, the window is already at full size. Simplifies the implementation: removes useLayoutEffect, isGeneratingRef, containerNodeRef, and the container freeze/transition animation. Replaces them with a single isPreExpandedRef flag that gates the ResizeObserver. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: keep justify-end during exit animation to prevent dismiss flash Don't clear isAnchoredUpward in requestHideOverlay — flipping from justify-end to justify-start during the Framer Motion exit animation caused content to jump from bottom to top of the max-height window. The state is already reset in replayEntranceAnimation on next open. 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
* fix: eliminate streaming jitter during upward window growth When the overlay spawns near the bottom screen edge and grows upward, per-token window resizing caused visible jitter because macOS can't atomically reposition + resize in sync with the webview render cycle. Fix: pre-expand the window to max chat height in a single IPC call when streaming starts, skip per-token resizes during streaming, and flip the outer container to justify-end so content pins to the bottom and grows upward naturally within the already-sized frame. The ResizeObserver resumes normal duty once streaming ends to shrink-to-fit. * fix: pre-expand anchored window at overlay open to eliminate submit flash Move the pre-expansion from streaming start to overlay open. The first ResizeObserver event now immediately expands the window to max chat height — this fires during Framer Motion's entrance fade-in (opacity 0→1), making the expansion invisible. By the time the user interacts with the input bar, the window is already at full size. Simplifies the implementation: removes useLayoutEffect, isGeneratingRef, containerNodeRef, and the container freeze/transition animation. Replaces them with a single isPreExpandedRef flag that gates the ResizeObserver. * fix: keep justify-end during exit animation to prevent dismiss flash Don't clear isAnchoredUpward in requestHideOverlay — flipping from justify-end to justify-start during the Framer Motion exit animation caused content to jump from bottom to top of the max-height window. The state is already reset in replayEntranceAnimation on next open. ---------
quiet-node
added a commit
that referenced
this pull request
Apr 11, 2026
* fix: eliminate streaming jitter during upward window growth When the overlay spawns near the bottom screen edge and grows upward, per-token window resizing caused visible jitter because macOS can't atomically reposition + resize in sync with the webview render cycle. Fix: pre-expand the window to max chat height in a single IPC call when streaming starts, skip per-token resizes during streaming, and flip the outer container to justify-end so content pins to the bottom and grows upward naturally within the already-sized frame. The ResizeObserver resumes normal duty once streaming ends to shrink-to-fit. * fix: pre-expand anchored window at overlay open to eliminate submit flash Move the pre-expansion from streaming start to overlay open. The first ResizeObserver event now immediately expands the window to max chat height — this fires during Framer Motion's entrance fade-in (opacity 0→1), making the expansion invisible. By the time the user interacts with the input bar, the window is already at full size. Simplifies the implementation: removes useLayoutEffect, isGeneratingRef, containerNodeRef, and the container freeze/transition animation. Replaces them with a single isPreExpandedRef flag that gates the ResizeObserver. * fix: keep justify-end during exit animation to prevent dismiss flash Don't clear isAnchoredUpward in requestHideOverlay — flipping from justify-end to justify-start during the Framer Motion exit animation caused content to jump from bottom to top of the max-height window. The state is already reset in replayEntranceAnimation on next open. ---------
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
justify-end— when the window spawns near the bottom edge (upward growth), the outer container flips tojustify-endso the morphing container pins to the bottom of the pre-expanded window and content grows upward naturally.isAnchoredUpwardpersists through the hide transition so content stays bottom-pinned while fading out, preventing a dismiss flash.Test plan
🤖 Generated with Claude Code