Skip to content

Diff panel Improvements: file collapse, full-width toggle and better per thread state management#2444

Open
AfzalH wants to merge 1 commit intopingdotgg:mainfrom
AfzalH:t3code/diff-panel-state-and-toggles
Open

Diff panel Improvements: file collapse, full-width toggle and better per thread state management#2444
AfzalH wants to merge 1 commit intopingdotgg:mainfrom
AfzalH:t3code/diff-panel-state-and-toggles

Conversation

@AfzalH
Copy link
Copy Markdown

@AfzalH AfzalH commented May 1, 2026

Summary

  • Move diff-panel UI state out of URL search params / component-local state into the Zustand UI store, with per-thread scoping where it makes sense (panel open, full-width, per-file collapse) and global session scope where it doesn't (render mode, line wrap)
  • Add a full-width toggle to the diff toolbar that hides the chat column and lets the diff fill the content area; the toggle is per-thread, so one thread can be expanded while another is split
  • Add a per-file collapse chevron in each file's diff header that swaps the FileDiff out for a compact summary row showing the same change-type icon, file path, and -N +M counts

Screenshots

File Collapse/Expand in diff view

Screenshot 2026-05-01 at 15 07 55

Full Width Toggle Button

Screenshot 2026-05-01 at 15 07 16

Full Width Diff View

Screenshot 2026-05-01 at 15 07 33

Why per-file collapse swaps the component instead of hiding via CSS

The @pierre/diffs Virtualizer reserves vertical space for each file based on hunk metadata — <div data-virtualizer-buffer> siblings of the pre get explicit pixel heights and grid columns inside the pre get min-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:

Field Scope Persisted
threadDiffOpenById per-thread yes
threadDiffFullWidthById per-thread yes
threadDiffFileCollapsedById per-thread, per-file yes
diffRenderMode global session no
diffWordWrap global session, hydrated once from settings.diffWordWrap no

?diff=1 is removed from DiffRouteSearch. Legacy URLs that still carry it parse cleanly and are ignored. diffTurnId and diffFilePath remain in the URL since they identify a specific selection within the panel and are useful for sharing.

Test plan

  • Open the diff panel in thread A; confirm thread B opens with its own (closed) state
  • Toggle full-width in A; navigate to B; B is split-pane; navigate back to A; A is still full-width
  • Toggle stacked → split in A; expand/collapse the panel; selection survives the remount
  • Collapse a small file via the chevron; confirm only the header is visible, no empty space
  • Collapse package-lock.json (or any large file); confirm no empty space below the header and no flicker while scrolling
  • Confirm the change-type icon (new/modified/deleted/renamed) is visible and colored correctly in the collapsed row
  • Click the file path on a collapsed row; opens in editor (existing behavior)
  • Click the chevron; expands; click again; collapses
  • Reload the page; per-thread open/full-width/per-file-collapse preferences are restored from localStorage
  • vitest run (apps/web) and tsc --noEmit pass

🤖 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=1 is 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 FileDiff for a fixed-height compact header (with change-type icon and +/- line counts) to avoid virtualizer empty-space/resize thrash.

Persistence/test updates: uiStateStore now 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.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d547411e-ef8c-48e8-b74d-fa289fef71aa

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels May 1, 2026
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented May 1, 2026

Approvability

Verdict: 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.

@AfzalH AfzalH changed the title Diff panel: per-thread state, full-width toggle, and per-file collapse Diff panel Improvement: file collapse, full-width toggle and better per thread state management May 1, 2026
@AfzalH AfzalH changed the title Diff panel Improvement: file collapse, full-width toggle and better per thread state management Diff panel Improvements: file collapse, full-width toggle and better per thread state management May 1, 2026
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>
@AfzalH AfzalH force-pushed the t3code/diff-panel-state-and-toggles branch from 8cbe456 to 1dece16 Compare May 2, 2026 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 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