Diff panel Improvements: file collapse, full-width toggle and better per thread state management#2444
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
ApprovabilityVerdict: Needs human review This PR introduces new diff panel features including per-thread state management, full-width toggle, and per-file collapse functionality. These are new user-facing capabilities with new UI controls and state persistence patterns that warrant human review. You can customize Macroscope's approvability policy. Learn more. |
Move diff-panel UI state from URL search params and component-local useState into the Zustand UI store. State that is meaningful per thread is keyed by scopedThreadKey so two threads can have different diff panels open / expanded / collapsed independently. Wider state (render mode, line wrap) lives in the store globally so it survives the remount that expand/collapse causes. Per-thread (persisted to localStorage): - diff panel open/closed (replaces ?diff=1 in URL) - full-width vs split-pane (new toolbar toggle) - per-file collapse (new chevron on each file header) Global session (in store, not persisted to disk): - render mode (split/stacked) - line wrap, hydrated once from settings.diffWordWrap The per-file collapse swaps the FileDiff out for a small CollapsedFileHeader rather than hiding content via CSS — the library's Virtualizer reserves vertical space from hunk metadata, so a CSS hide leaves a tall empty box and triggers ResizeObserver thrashing during scroll. The collapsed header inlines the same change-type SVG paths the library renders inside its shadow DOM so the new/modified/deleted/renamed icon is identical between collapsed and expanded views. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
8cbe456 to
1dece16
Compare
Summary
-N +McountsScreenshots
File Collapse/Expand in diff view
Full Width Toggle Button
Full Width Diff View
Why per-file collapse swaps the component instead of hiding via CSS
The
@pierre/diffsVirtualizerreserves vertical space for each file based on hunk metadata —<div data-virtualizer-buffer>siblings of thepreget explicit pixel heights and grid columns inside thepregetmin-height: calc(N * 1lh). A CSS-only hide (e.g.[data-diff] > *:not([data-diffs-header]) { display: none }) leaves a tall empty box below collapsed files and causes ResizeObserver thrashing as the buffers and content fight to be measured (visible as scroll flicker). Rendering a small fixed-height stand-in lets the Virtualizer measure a stable height and lays the panel out cleanly.The compact header inlines the same change-type SVG paths the library uses in its sprite (the sprite lives inside each FileDiff's shadow DOM, so it can't be
<use>d from outside) so the icon next to the file path is pixel-identical to the expanded view.State scoping
Stored in
useUiStateStore:threadDiffOpenByIdthreadDiffFullWidthByIdthreadDiffFileCollapsedByIddiffRenderModediffWordWrapsettings.diffWordWrap?diff=1is removed fromDiffRouteSearch. Legacy URLs that still carry it parse cleanly and are ignored.diffTurnIdanddiffFilePathremain in the URL since they identify a specific selection within the panel and are useful for sharing.Test plan
package-lock.json(or any large file); confirm no empty space below the header and no flicker while scrollingvitest run(apps/web) andtsc --noEmitpass🤖 Generated with Claude Code
Note
Medium Risk
Moderate risk because it changes how diff-panel open/full-width/collapse state is tracked (URL → persisted Zustand store) and affects navigation/rendering behavior across threads and reloads.
Overview
Diff panel state is moved out of URL search params into
useUiStateStore.?diff=1is deprecated/ignored (still stripped for legacy URLs) and open/closed state is now persisted per-thread; the URL keeps only selection (diffTurnId,diffFilePath).Diff UX improvements: adds a per-thread full-width toggle that hides the chat pane, and adds per-file collapse/expand that swaps
FileDifffor a fixed-height compact header (with change-type icon and +/- line counts) to avoid virtualizer empty-space/resize thrash.Persistence/test updates:
uiStateStorenow sanitizes/persists the new per-thread maps plus global diff view preferences (render mode/word wrap with one-time hydration from settings), and the related route/search parsing tests are updated.Reviewed by Cursor Bugbot for commit 1dece16. Bugbot is set up for automated code reviews on this repo. Configure here.