sessions: fix PR status icon flicker on session list updates#319219
Draft
roblourens wants to merge 2 commits into
Draft
sessions: fix PR status icon flicker on session list updates#319219roblourens wants to merge 2 commits into
roblourens wants to merge 2 commits into
Conversation
The GitHub PR status icons in the Agents window session list flickered whenever the list updated. The renderer cross-fades icons when the icon selector changes, and the icon was being transiently reset to the read/unread fallback dot on every recompute. Root cause: the `gitHubInfo` derived in both session providers acquired the ref-counted PR model via `reader.store`, which the observable framework disposes *before* each recompute. Releasing the reference dropped the refcount to 0, disposing the cached model; the freshly re-acquired model starts with `pullRequest === undefined`, so `livePR` momentarily read `undefined` and the icon fell back to the dot, triggering a cross-fade. In the copilot-chat provider this recompute fired on every unrelated `update()` because `_baseGitHubInfo` was reset with a fresh object each time. Fix: - Acquire the PR model reference via `reader.delayedStore` (disposed *after* recompute) so the cached, ref-counted model survives the recompute and `livePR` never blips to `undefined`. - Add `equalsFn: gitHubInfoEqual` to the `gitHubInfo` derived (both providers) and to `_baseGitHubInfo` so structurally-unchanged GitHub info no longer recomputes/re-notifies on unrelated list updates. - Widen `derivedOpts`'s computeFn reader type to `IReaderWithStore` so callers can use `store`/`delayedStore`, matching `derived`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes GitHub PR status icon flicker in the Agents window session list by preventing transient fallback-icon states during observable recomputation, and by reducing unnecessary recomputation/notifications when GitHub info hasn’t structurally changed.
Changes:
- Keep ref-counted PR model references alive across derived recomputations by using
reader.delayedStore(avoids briefpullRequest === undefinedstates that trigger cross-fade flicker). - Add structural equality (
equalsFn: gitHubInfoEqual) to GitHub info observables/deriveds to avoid churn on unrelated session updates. - Update
derivedOpts’scomputeFnreader type toIReaderWithStoreso callers can safely usestore/delayedStore.
Show a summary per file
| File | Description |
|---|---|
| src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsProvider.ts | Uses delayedStore for PR model refs and adds gitHubInfoEqual-based equality to prevent flicker and recompute churn. |
| src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts | Applies the same delayedStore ref-holding pattern and gitHubInfoEqual equality for PR icon stability. |
| src/vs/base/common/observableInternal/observables/derived.ts | Types derivedOpts compute reader as IReaderWithStore to enable store/delayedStore usage. |
Copilot's findings
- Files reviewed: 3/3 changed files
- Comments generated: 0
The agent host session adapter resolves its PR number asynchronously, so per-session polling never starts (it reads gitHubInfo.pullRequest at session-add time, when it is still undefined). The shared, ref-counted PR model is therefore only kept alive by the session list rows. When a session goes inactive, the active-session refresh stops updating its model, and the list re-splice that updates selection briefly unobserves the row's gitHubInfo derived, releasing the last reference. The model is disposed and re-acquired in an unpopulated state, so livePR reads undefined and the icon downgraded to the fallback dot -- it disappeared when clicking away from a session. Latch the last known icon keyed by the full PR identity (owner/repo/number) and reuse it whenever livePR is transiently undefined for the same PR. This also prevents the original flicker on list updates, while still updating promptly when the live model reports a new state. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Fixes #319221
Problem
When the session list in the Agents window updates, the GitHub PR status icons flicker. The list renderer cross-fades icons whenever the icon's CSS selector changes, and on each update the PR icon was being transiently reset to the read/unread fallback dot, producing a visible flicker.
A follow-up issue was also found: the PR icon could disappear entirely (downgrade to the fallback dot) when clicking away from an agent host session.
Root cause
Flicker
The
gitHubInfoderivedin both session providers acquired the ref-counted PR model viareader.store. The observable framework disposesreader.storebefore each recompute. Because the PR model is ref-counted (ReferenceCollection), releasing the reference dropped the refcount to 0 and disposed the cached model. The freshly re-acquired model starts withpullRequest === undefined, solivePRmomentarily readundefined, the icon fell back to the dot, and the renderer cross-faded back and forth. In the copilot-chat provider this recompute fired on every unrelatedupdate()(title/status/time/etc.), because_baseGitHubInfowas reset with a fresh object each time.Disappearing icon (agent host)
The agent host adapter resolves its PR number asynchronously, so the per-session polling contribution never starts for it (it reads
gitHubInfo.pullRequestat session-add time, when it is still undefined). The shared PR model is therefore only kept alive by the session list rows. When a session goes inactive, the active-session refresh stops updating its model, and the list re-splice that updates selection briefly unobserves the row'sgitHubInfoderived, releasing the last reference. The model is disposed and re-acquired in an unpopulated state, solivePRreadsundefinedand the icon downgrades to the fallback it disappeared when clicking away. Unlike copilot-chat (which has a static fallback icon from session metadata), the agent host adapter had no fallback.dotFix
reader.delayedStore(disposed after recompute) instead ofreader.storeto hold the PR model reference, so the cached, ref-counted model survives recomputations (the documenteddelayedStore+ ref-counting pattern).equalsFn: gitHubInfoEqualto thegitHubInfoderived (both providers) and to_baseGitHubInfo, so structurally-unchanged GitHub info no longer recomputes/re-notifies on unrelated list updates.derivedOpts's computeFn reader type toIReaderWithStoreso callers can usestore/delayedStore, matchingderived(which already exposes them).Files
src/vs/sessions/contrib/providers/copilotChatSessions/browser/copilotChatSessionsProvider.tssrc/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.tssrc/vs/base/common/observableInternal/observables/derived.tsValidation
npm run compile-check-ts-nativepasses cleanNote: the Electron unit suite could not be run in this worktree (the Electron test runner fails at startup with an unrelated
app.setPatherror). Manual verification on a running dev build is recommended.(Written by Copilot)