Skip to content

fix: prevent sidebar selection paint flicker#881

Merged
Astro-Han merged 7 commits into
devfrom
codex/i879-sidebar-flicker
May 24, 2026
Merged

fix: prevent sidebar selection paint flicker#881
Astro-Han merged 7 commits into
devfrom
codex/i879-sidebar-flicker

Conversation

@Astro-Han

@Astro-Han Astro-Han commented May 24, 2026

Copy link
Copy Markdown
Owner

Summary

  • Enforced a single session-row paint priority: switching > selected > interaction (hover/focus/menu) > idle.
  • Suppressed hover paint while data-switch-paint is present so the target row cannot stack selected + hover paint before the router-applied active class arrives.
  • Moved the right-side status/action opacity rules into sidebar.css so background paint and status paint follow the same contract.
  • Reworked SortableJS row drag styling so chosenClass no longer dims rows during ordinary clicks; only the actual fallback drag preview gets drag-specific styling.
  • Added focused E2E regressions for both the R2 switch-paint cascade bug and the R3 press-before-drag dimming bug.

Why

Fixes #879.

The first follow-up still flickered in the user's R2 recording. Frame-by-frame sampling showed the clicked target row briefly painted darker and then settled lighter after selection. The target row had already been marked for switch paint, but the later hover selector still enabled the animated hover layer on top of the instant selected layer.

The concrete R2 cascade bug was:

  • [data-switch-paint]::after { opacity: 0 } tried to suppress hover paint during a switch;
  • the later :hover interaction selector set the hover layer back to opacity: 1 for a not-yet-active target row;
  • the user saw that as a dark-to-stable flash after click.

The user's R3 recording showed a second, separate flash. Frame-by-frame analysis showed the row content and right-side action dimming together for a frame on mouse down. A minimal SortableJS probe confirmed that chosenClass is applied on ordinary mousedown, before a drag is confirmed. Our .pw-drag-chosen { opacity: 0.35 } therefore dimmed normal clicks.

This change keeps normal hover/focus/menu animation, makes switching a higher-priority paint state, and separates SortableJS states by meaning:

  • chosenClass: pointer is down, but a drag may not happen; no dimming.
  • ghostClass: in-list placeholder during actual drag.
  • dragClass / fallback clone: actual moving drag preview.

Related Issue

Fixes #879

Human Review Status

Pending

Review Focus

Please focus on the session row paint contract in sidebar.css and the SortableJS state styling:

  • switching > selected > interaction > idle should stay centralized and non-stacking.
  • Normal hover/focus/menu-open animation should still work when no switch is in progress.
  • Active rows should not gain an extra hover background layer.
  • Status/action opacity should match the same switch/interaction priority.
  • Ordinary click/press on a draggable sidebar row should not dim the row.
  • Real drag, pinned reorder, menu, and leading-slot sidebar states should remain visually intact.

NewSessionItem is intentionally not included because the confirmed repro path is switching between existing session rows, and it does not share the right-side status/action crossfade involved in this issue.

Risk Notes

Low risk. The change is scoped to pawwork-session-row paint, SortableJS row state styling, and a temporary click-switch marker. Routing, session persistence, and drag data flow are unchanged.

Skipped conditional checklist items:

  • Platform/packaging impact: not applicable; no platform, packaging, updater, signing, shell, path, or permission surface changed.
  • Docs/release/dependency/local file surfaces: not applicable; no docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes are included in the PR.

How To Verify

Dependency setup: bun install --frozen-lockfile completed in the new worktree.
TDD RED (R2): bun run test:e2e sidebar/sidebar-session-links.spec.ts --grep "switch paint suppresses hover" failed before the fix with Expected "0", Received "1" for the target row hover layer opacity.
TDD RED (R3): bun run test:e2e sidebar/sidebar-session-links.spec.ts --grep "press does not dim" failed before the fix with Expected "1", Received "0.35" for the drag wrapper opacity.
Focused GREEN (R3): bun run test:e2e sidebar/sidebar-session-links.spec.ts --grep "press does not dim" passed after removing dimming from SortableJS chosenClass.
Sidebar links suite: bun run test:e2e sidebar/sidebar-session-links.spec.ts passed, 6 tests.
Drag/reorder coverage: bun run test:e2e sidebar/sidebar-drag-pointer.spec.ts sidebar/sidebar-pinned-reorder.spec.ts passed, 4 tests.
Typecheck: bun run typecheck passed.
Visual snap: bun run snap sidebar passed and the generated grid was reviewed locally.
Visual snap: bun run snap sidebar-pinned passed and the generated grid was reviewed locally.
Diff check: git diff --check passed.
Manual Electron verification: after merging origin/dev into this branch, @Astro-Han tested the sidebar session switching flow in Electron from the PR worktree and confirmed the flicker is gone.

Note: an earlier attempt to run two Playwright commands in parallel caused ERR_CONNECTION_REFUSED from dev-server port contention in sidebar-session-links.spec.ts; rerunning that suite alone passed.

Screenshots or Recordings

Visible UI was checked with local snap grids:

  • docs/design/preview/screenshots/sidebar.png
  • docs/design/preview/screenshots/sidebar-pinned.png

Manual Electron verification was also completed by @Astro-Han on the PR worktree after origin/dev was merged into the branch.

Checklist

How to use this checklist:

  • Tick a box by replacing [ ] with [x]. Do not edit, add, or remove items.
  • The bot-applied label items can only be honestly ticked AFTER the PR is opened and the labeler / priority-triage bots have run — return to the PR description and tick them then.
  • Most items are required. The few that are conditional are explicitly marked (conditional); for those, leave unticked if they truly do not apply and explain why in Risk Notes. All other items must be ticked before requesting human review.
  • Type label — this PR carries exactly one of bug, enhancement, task, documentation. Type labels are author-added; the labeler bot does NOT assign them. Add the label in the GitHub UI, then tick this.
  • Routing labels — this PR carries at least one of app, ui, platform, harness, ci. The labeler bot assigns these on PR open based on changed paths. Confirm the bot's choice (or override if wrong), then tick this.
  • Priority label — this PR carries exactly one of P0, P1, P2, P3. The priority-triage bot suggests one on PR open. Confirm or override, then tick this.
  • Human Review Status above is set to Pending, Approved by @<reviewer>, or Not required: <reason> (default is Pending; "not required" is restricted to bot-authored low-risk PRs).
  • I linked the related issue, or stated in Summary why there is no issue.
  • I described the review focus and any meaningful risks.
  • I replaced the example block in How To Verify with the real verification steps and the key result for each.
  • I did not introduce unrelated refactors, dependencies, generated files, or file changes beyond the stated scope.
  • (conditional) I manually checked visible UI or copy changes when needed, with screenshots or recordings. Leave unticked only if no visible UI or copy changed.
  • (conditional) I considered macOS and Windows impact for platform, packaging, updater, signing, paths, shell, or permissions changes. Leave unticked only if no platform/packaging surface was touched.
  • (conditional) I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes when relevant. Leave unticked only if none of those surfaces was touched.
  • I reviewed the final diff for unrelated changes and suspicious dependency changes.
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English.

@Astro-Han Astro-Han added bug Something isn't working P3 Low priority ui Design system and user interface labels May 24, 2026
@github-actions github-actions Bot added app Application behavior and product flows P2 Medium priority and removed P3 Low priority labels May 24, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested priority: P2 (includes user-path files (packages/app/src/pages/layout/sidebar-items.tsx, packages/app/src/pages/layout/sidebar.css)).

P1/P0 are reserved for maintainer confirmation. Please relabel manually if this is a release blocker, security issue, data-loss risk, or updater/runtime failure.

@coderabbitai

coderabbitai Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@Astro-Han, we couldn't start this review because you've used your available PR reviews for now.

Your plan currently allows 1 review/hour. Refill in 59 minutes and 57 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 057417cd-b1ac-4744-b67f-eacaac51a229

📥 Commits

Reviewing files that changed from the base of the PR and between dcc8b56 and 24fe294.

📒 Files selected for processing (4)
  • packages/app/e2e/sidebar/sidebar-session-links.spec.ts
  • packages/app/src/pages/layout/pawwork-sidebar.tsx
  • packages/app/src/pages/layout/sidebar-items.tsx
  • packages/app/src/pages/layout/sidebar.css
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/i879-sidebar-flicker

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the sidebar session row styling by moving highlight logic from Tailwind classes to CSS pseudo-elements, allowing for instant active state updates while preserving smooth hover transitions. A new E2E test was added to verify these visual changes. The review feedback suggests replacing page.waitForTimeout() in the test with a more deterministic approach, such as waiting for a requestAnimationFrame, to prevent potential flakiness.

Comment thread packages/app/e2e/sidebar/sidebar-session-links.spec.ts Outdated
@github-actions

github-actions Bot commented May 24, 2026

Copy link
Copy Markdown

Perf delta summary

Comparator: pass

Profile / Scenario interaction median interaction worst long task max tbt frame gap p95 frame gap max jank count cls status
default / homepage-cold 24 -> 24 (0) 48 -> 32 (-16) 87 -> 64 (-23) 37 -> 14 (-23) 16.8 -> 16.8 (0) 133.2 -> 116.7 (-16.5) 4 -> 3 (-1) 0 -> 0 (0) pass
default / long-session-input-lag 48 -> 48 (0) 48 -> 48 (0) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.7 (-0.1) 16.8 -> 16.7 (-0.1) 0 -> 0 (0) 0 -> 0 (0) pass
default / session-streaming-long 48 -> 48 (0) 64 -> 64 (0) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.8 (0) 33.4 -> 33.3 (-0.1) 0 -> 0 (0) 0 -> 0 (0) pass
default / tool-call-expand 40 -> 40 (0) 40 -> 40 (0) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.7 (-0.1) 16.8 -> 16.7 (-0.1) 0 -> 0 (0) 0 -> 0 (0) pass
default / tool-default-open-heavy-bash 16 -> 16 (0) 24 -> 16 (-8) 83 -> 89 (+6) 48 -> 55 (+7) 50 -> 66.6 (+16.6) 166.6 -> 116.7 (-49.9) 3 -> 4 (+1) 0.004 -> 0.004 (0) pass
default / terminal-side-panel-open 56 -> 56 (0) 72 -> 56 (-16) 0 -> 0 (0) 0 -> 0 (0) 33.3 -> 33.4 (+0.1) 33.3 -> 33.4 (+0.1) 0 -> 0 (0) 0 -> 0 (0) pass
default / session-scroll-reading 16 -> 16 (0) 32 -> 16 (-16) 0 -> 0 (0) 0 -> 0 (0) 33.3 -> 16.8 (-16.5) 33.3 -> 16.8 (-16.5) 0 -> 0 (0) 0 -> 0 (0) pass

@Astro-Han Astro-Han merged commit 9978e1e into dev May 24, 2026
27 checks passed
@Astro-Han Astro-Han deleted the codex/i879-sidebar-flicker branch May 24, 2026 13:13
Astro-Han added a commit that referenced this pull request May 24, 2026
Bump the desktop Electron package version to v2026.5.25 for the release train.

Included release-readiness checks:
- Local app and desktop Electron typechecks passed.
- Focused UI component tests for grouped tool-call summaries passed.
- PR CI passed, including app/ui/opencode/desktop units, typecheck, CodeQL, desktop smoke, and e2e-artifacts.
- Computer Use manual E2E covered new session creation, an actual execute-command tool call, right-panel tabs and collapse, #880 sidebar/right-panel toggle affordances, and pinned session drag reordering.

This release includes the current dev branch work through #880 and the later fixes #881 and #883.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app Application behavior and product flows bug Something isn't working P2 Medium priority ui Design system and user interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Sidebar session selection flickers between old and new rows

1 participant