Skip to content

fix: eliminate streaming jitter during upward window growth - #10

Merged
quiet-node merged 3 commits into
mainfrom
fix/streaming-jitter-transform-buffering
Apr 1, 2026
Merged

fix: eliminate streaming jitter during upward window growth#10
quiet-node merged 3 commits into
mainfrom
fix/streaming-jitter-transform-buffering

Conversation

@quiet-node

Copy link
Copy Markdown
Owner

Summary

  • Pre-expand anchored windows to max height at overlay open — the first ResizeObserver event immediately expands the window to max chat height during the Framer Motion entrance fade-in, making the expansion invisible. All subsequent observer events are skipped so content grows inside the fixed window with zero per-token resizing.
  • Bottom-pin content with justify-end — when the window spawns near the bottom edge (upward growth), the outer container flips to justify-end so the morphing container pins to the bottom of the pre-expanded window and content grows upward naturally.
  • Preserve layout during exit animationisAnchoredUpward persists through the hide transition so content stays bottom-pinned while fading out, preventing a dismiss flash.

Test plan

  • 138 tests passing, 100% coverage maintained
  • Typecheck clean, lint clean
  • Manual: spawn overlay near bottom edge → submit message → verify no jitter during streaming
  • Manual: spawn overlay near bottom edge → dismiss with Cmd+Cmd → verify no flash
  • Manual: spawn overlay at top/center → verify downward growth still works normally
  • Manual: drag window after anchored spawn → verify it switches to downward growth

🤖 Generated with Claude Code

quiet-node and others added 3 commits March 31, 2026 22:06
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>
@quiet-node
quiet-node merged commit 0213b34 into main Apr 1, 2026
3 checks passed
@quiet-node
quiet-node deleted the fix/streaming-jitter-transform-buffering branch April 1, 2026 03:52
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.

---------
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