feat(git-diff): persist diff view mode and line wrap across reloads - #3271
Open
danielbachhuber wants to merge 1 commit into
Open
Conversation
Store the git diff toolbar's display mode and line overflow mode in localStorage-backed Jotai atoms so both survive a remount and reload. A stored display-mode preference now overrides the 760px responsive rule at every panel width, which replaces the expiry bookkeeping that discarded an explicit choice on resize, breakpoint crossing, and panel close. On a compact viewport the display mode is session state that defaults to unified and is never persisted, so mobile keeps an opinionated default and never overwrites the desktop preference. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.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.
Human comments
From https://discord.com/channels/1522478522332090528/1545179385194356856/1545395969603014657
What was wrong
Both git-diff toolbar preferences were component state, so they reset on remount and reload. Display mode also expired explicit choices during panel resizing, breakpoint changes, and panel closes; the 760px rule then overrode the choice.
What changed
Added device-local localStorage preferences for git-diff display mode (
bb.thread.gitDiff.displayMode) and line overflow mode (bb.thread.gitDiff.lineOverflowMode) inapps/app/src/lib/git-diff-view-preferences.ts.apps/app/src/components/secondary-panel/git-diff/useResponsiveGitDiffPanelDisplay.tsnow uses the stored display preference and exports a pure resolver;apps/app/src/components/secondary-panel/ThreadSecondaryPanel.tsxpersists the wrap toggle and passes the resize handler through directly.A stored display-mode preference overrides the 760px responsive rule at every panel width. The rule supplies the default only when no preference is stored. On a compact viewport, display mode is session-only, defaults to
unified, and is never persisted, so mobile never overwrites the desktop preference.Decisions
atomWithStoragein localStorage, notAppSettingsorPUT /settings/general: they are device-local view preferences and do not need server or CLI access.splitpreference does not carry onto a phone-width drawer and a mobile choice does not overwrite the desktop preference.useIsCompactViewport()signal (max-width: 767px) rather than a new breakpoint or panel-width proxy; tests useCompactViewportOverrideProvider.unifiedis its default, not a restriction, and an explicit compactsplitchoice is honored for the session.splitcan be cramped below that width.unified, preserving the responsive default for users who have not chosen.handleSecondaryPanelResizeStart, whose only purpose and caller supported expiry-on-resize.createNullableLocalStorageEnumStorageandcreateLocalStorageEnumStoragefromlib/browser-storage.ts; the preferences are string unions, socreateBooleanPreferenceAtomdoes not apply.bb.<area>.<name>storage-key convention and read storage on first render with{ getOnInit: true }, matching the app's otheratomWithStorageusage and avoiding a default-state flash.resolveGitDiffDisplayMode({ isCompactViewport, compactDisplayMode, displayModePreference, isWideEnoughForSplit })as a pure exported function so its combinations can be tested without rendering.Implementation and scope
useResponsiveGitDiffPanelDisplay.tsreplaces its in-memory display state and tracking refs with the compact-viewport signal, stored preference, compact session state, and anisWideEnoughForSplit: boolean | nullstate. It removes the resize-start handler, explicit-choice refs, breakpoint-crossing logic, and panel-close reset effect, all of which existed to expire the choice. The split breakpoint remainsGIT_DIFF_SPLIT_VIEW_MIN_WIDTH_PX(760px).useResponsiveGitDiffPanelDisplay.test.tsxadds 13 tests covering responsive fallback, preference precedence at every width, remount restoration, invalid storage values, compact defaults and toggles, compact non-persistence, preservation of an existing stored preference, and resolver combinations.The collapse-all chevron is intentionally not persisted. Its default is derived per diff from file count and delete status, rather than from a user choice, so persisting it would conflict with that per-diff behavior.
FilePreview.tsxandmarkdown-preview.tsxretain their independent unpersisted wrap toggles; sharing a preference is a separate product decision. There is also no settings-panel UI for these preferences.Outside compact mode, there is no UI to return display mode to an unset responsive state: choosing
unifiedon a wide panel remains a preference until storage is cleared. The compact session choice survives viewport changes within the same hook instance, which is intended but untested. Storage's subscribe implementation provides cross-tab sync, which the new tests do not exercise. Because localStorage is per origin, preferences do not carry between the packaged app and a checkout's dev app on a different port.How you verified
pnpm exec vitest run --config vitest.config.ts useResponsiveGitDiffPanelDisplay(13 tests passed)pnpm exec vitest run --config vitest.config.ts secondary-panel(37 files and 352 tests passed)pnpm exec tsc --noEmit(exited 0 with no output)pnpm exec oxlint src(exited 0; 184 warnings are the pre-existing baseline, and neither changed file appears in the output)No manual reload check was performed in a running app; the automated remount-and-restore tests cover the persistence path, and reviewers should toggle both controls and reload within a single app instance.