Skip to content

Conversation

@mafredri
Copy link
Member

@mafredri mafredri commented Feb 9, 2026

The chat view had several compounding issues causing visible jank during streaming and full conversation clears on network blips.

The scroll logic used three refs, a scrollend listener, and multiple heuristics that fought each other. The scrollend listener used anonymous arrow functions, so removeEventListener never matched and cleanup never ran. This left isProgrammaticScrollRef stuck true, preventing isAtBottomRef from updating on user scroll events. The user could never scroll away to read earlier context.

transition-all duration-300 on the message container animated height changes during streaming, creating wobbly layout. The URL detection regex used a /g flag, making it stateful across split/test calls and causing links to flicker between renders.

Replaced the scroll machinery with direct scrollTop assignment gated on a single isAtBottom ref updated unconditionally on every scroll event. This runs in useLayoutEffect before paint, avoids animation conflicts, and respects user scroll position. Sending a message always scrolls to bottom and re-enables auto-scroll so the agent response stays visible.

Separately, setMessages([]) ran on every SSE reconnect, blanking the conversation on any network blip. Manual reconnect logic in onerror also fought the browser's built-in SSE reconnection. The finally block in sendMessage filtered out all draft messages, briefly removing the user's message before the server confirmed it.

Now the browser handles SSE reconnection natively, the message list is never cleared, and drafts are replaced inline when the confirmed message arrives. Failed sends clean up their draft in the error paths so ghost messages don't linger.

@mafredri mafredri force-pushed the chat-view-7r7r branch 4 times, most recently from 301c968 to 9ea76c5 Compare February 9, 2026 18:05
The chat view stuttered during streaming responses due to several
compounding issues: transition-all on the message container animated
height changes as messages grew, smooth scroll was interrupted on
every streaming chunk before the previous animation completed, the
scrollend listener used anonymous functions so cleanup never matched
and broke user scroll-away detection, and the URL regex /g flag
made it stateful across split/test calls causing link flicker.

Sending a message now always scrolls to bottom and re-enables
auto-scroll so the agent response stays visible.
Messages were reset to [] every time the EventSource reconnected,
including on transient network errors. This blanked the entire
conversation. Now we let the browser handle SSE reconnection
natively and never clear the message list.

Also removes the draft filter in sendMessage's finally block which
caused the user's message to briefly disappear before the server
confirmed it. Drafts are now replaced inline when the matching
confirmed message arrives via SSE, and cleaned up on send failure.
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