Skip to content

fix(web): preserve Agents panel scroll position - #9637

Open
f4llenz wants to merge 3 commits into
pingdotgg:mainfrom
f4llenz:fix/preserve-agents-panel-scroll
Open

fix(web): preserve Agents panel scroll position#9637
f4llenz wants to merge 3 commits into
pingdotgg:mainfrom
f4llenz:fix/preserve-agents-panel-scroll

Conversation

@f4llenz

@f4llenz f4llenz commented Sep 4, 2026

Copy link
Copy Markdown

What Changed

When the Agents panel remounted after switching threads, changing right-panel tabs, hiding the panel, or moving between inline and sheet layouts, it returned to the top.

Remember each thread's Agents viewport offset in renderer memory, keyed by its environment-qualified thread key. Restore the offset before paint when the viewport remounts, and give each thread a distinct AgentsPanel lifetime so view state cannot leak between threads. ScrollArea now exposes its real scrolling viewport through an optional ref while preserving its existing root ref.

Why

Agent rosters can grow long, and navigating away from a thread should not lose the user's place. Renderer-only memory preserves that context across panel remounts without adding persisted client state, server state, or wire changes. Scroll capture uses a passive listener and does not trigger React renders. If a remounted layout has a smaller scroll range, its clamped position does not overwrite the deeper saved offset.

UI Changes

This changes interaction behavior without changing the panel's visual design.

CleanShot.2026-09-04.at.05.33.04.mp4

Validation

Nine focused tests cover per-thread and per-environment isolation, tab and visibility remounts, inline/sheet remounts, delayed roster appearance, viewport ref behavior, and scroll capture without rerenders. Web typecheck, targeted lint, formatting, and git diff --check pass. The behavior was also tested manually in an isolated local web client using cloned data; no live user state was changed.

Web and desktop are covered by the shared web surface. Mobile has no Agents panel.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes (not applicable: behavior-only change)
  • I included a video for animation/interaction changes

Created with GPT-5.6 Sol in Codex.

Note

Preserve Agents panel scroll position across thread switches and remounts

  • AgentsPanel now stores per-thread vertical scroll offsets in a module-level map keyed by threadKey, restoring the saved scrollTop when the roster viewport mounts and capturing scroll events without React state updates
  • ScrollArea gains an optional viewportRef prop so callers can attach a ref to the scrolling viewport element
  • ChatView passes activeThreadKey to AgentsPanel via the new threadKey prop and uses it as the React key, causing a remount on thread change
  • Adds a lightweight in-memory DOM test double and a test suite covering scroll isolation per thread/environment, remount transitions, delayed roster rendering, clamped-viewport offsets, and zero rerender on scroll capture
  • Risk: AgentsPanel now requires a non-null threadKey prop; callers omitting it will get no scroll persistence

Macroscope summarized 72f4d0f.


Note

Low Risk
UI-only scroll persistence in renderer memory; ScrollArea adds an optional prop without changing default behavior.

Overview
Fixes the Agents right panel jumping back to the top when you switch threads, change tabs, hide the panel, or remount the layout.

AgentsPanel now keeps scroll offsets in an in-memory map keyed by threadKey. On mount it restores scrollTop via useLayoutEffect, updates the map with a passive scroll listener (no React state), and avoids overwriting a deeper saved offset when a remounted viewport is temporarily shorter. ChatView keys the panel by activeThreadKey and passes that as threadKey.

ScrollArea gains an optional viewportRef so the panel can target the real scrolling element; root ref behavior is unchanged.

Adds a lightweight reactTestDom helper plus tests for per-thread/environment isolation, remount scenarios, viewport ref wiring, and scroll capture without extra renders.

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

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Sep 4, 2026
Comment thread apps/web/src/components/AgentsPanel.tsx Outdated
import { ScrollArea } from "~/components/ui/scroll-area";
import { Button } from "~/components/ui/button";

const agentsScrollTopByThreadKey = new Map<string, number>();

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.

🟡 Medium components/AgentsPanel.tsx:33

agentsScrollTopByThreadKey grows for the lifetime of the renderer, so creating and deleting many agent threads retains every obsolete thread key and causes unbounded memory growth. Add deletion when a thread is removed (or otherwise bound/evict this cache) so entries do not accumulate indefinitely.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/AgentsPanel.tsx around line 33:

`agentsScrollTopByThreadKey` grows for the lifetime of the renderer, so creating and deleting many agent threads retains every obsolete thread key and causes unbounded memory growth. Add deletion when a thread is removed (or otherwise bound/evict this cache) so entries do not accumulate indefinitely.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Keeping this as designed. The cache is renderer-session-only and stores one number per thread whose Agents panel was visited; it resets on reload. Wiring thread deletion or adding eviction would expand the feature and could discard scroll state the user expects to recover.

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.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

@f4llenz
f4llenz marked this pull request as ready for review September 4, 2026 09:50

@cursor cursor Bot left a comment

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.

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.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 48b7360. Configure here.

Comment thread apps/web/src/components/AgentsPanel.tsx
@macroscopeapp

macroscopeapp Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Would Approve

Macroscope's review found this PR approvable — This is a focused UI bug fix that preserves Agents-panel scroll offsets with small, thread-scoped runtime changes and comprehensive focused tests; the shared ScrollArea change is optional and leaves existing callers unchanged. An unresolved Medium finding about unbounded renderer-session cache growth remains an independent correctness risk.

Not approved because:

  • 1 blocking correctness issue found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

@f4llenz
f4llenz force-pushed the fix/preserve-agents-panel-scroll branch 3 times, most recently from 59ac619 to a5a25fa Compare September 5, 2026 18:43
@cursor

cursor Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@f4llenz
f4llenz force-pushed the fix/preserve-agents-panel-scroll branch from a5a25fa to d633b9e Compare September 5, 2026 18:44
@cursor

cursor Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@f4llenz
f4llenz force-pushed the fix/preserve-agents-panel-scroll branch 2 times, most recently from ad86810 to e079fee Compare September 5, 2026 19:02
Remember each thread's Agents viewport offset for the lifetime of the
renderer so switching threads, tabs, visibility, or panel layouts no longer
returns users to the top. Keep thread lifetimes isolated by the existing
environment-qualified thread key and avoid React updates while scrolling.

Verification: 8 focused tests, web typecheck, targeted lint and formatting.
Keep the saved Agents offset when a remounted layout has a smaller scroll
range and the browser clamps the viewport. This lets the original layout
restore its deeper position instead of inheriting the temporary maximum.

Verification: 9 focused tests, web typecheck, targeted lint and formatting.
Treat viewport positions within one pixel of the calculated scroll maximum as
clamped so browser rounding cannot replace a deeper saved Agents offset.

Verification: 9 focused tests, web typecheck, targeted lint and formatting.
@f4llenz
f4llenz force-pushed the fix/preserve-agents-panel-scroll branch from e079fee to 72f4d0f Compare September 5, 2026 19:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant