feat(web): add a setting to disable history swipe navigation#28370
Problem
On macOS desktop, a Logitech MX Master's horizontal thumb wheel can navigate thread history accidentally, including when horizontal scrolling reaches the edge of wide content. Electron 44's input events carry no trackpad-vs-wheel provenance, so the existing gesture cannot currently be restricted to trackpads.
Change
Add “Swipe to go back and forward” in desktop Settings > General > Navigation, with Settings search, a per-setting reset, and restore-defaults support. The client preference defaults to enabled. Turning it off unmounts the gesture handler and indicator immediately, including during a swipe; turning it on works without reloading. Mouse back/forward buttons and keyboard navigation retain their existing behavior.
Scope and approval
CONTRIBUTING.md states: “A focused configuration option for an established capability may also be eligible without prior feature approval.” This option controls the existing desktop history-swipe capability. The default is unchanged, and the scope adds no workflow or broader navigation behavior. This follows the configuration route; it does not claim explicit maintainer approval or guaranteed acceptance.
The change is limited to the client setting contract, desktop renderer Settings/search/reset integration, its mount gate, and focused tests. Hosted/local web and native mobile do not expose the desktop gesture. The gesture is not documented in user guides, so no documentation change is needed.
Verification
- Failing first on origin/main at 15fe2832727893cbdd2214383f9f971474849695, with only regression tests added: both disabled swipe directions still invoked history navigation once; three schema cases failed because the field was absent (5 failures, 28 existing tests passed).
- With the change: 59 tests passed across settings contracts, AppSidebarLayout browser behavior, the existing swipe helper, and Settings search. Covers disabled back/forward navigation, removal of the indicator during an active swipe, and re-enabling without a reload.
- Command:
vp test run packages/contracts/src/settings.test.ts apps/web/src/components/AppSidebarLayout.browser.test.tsx apps/web/src/components/HistorySwipeNavigation.test.ts apps/web/src/components/settings/settingsSearch.test.ts. vp run check:changed: passed (exit 0): format, lint, both knip passes, typecheck of 35 affected packages/dependents, 64 tests across six files, and four changed-source React Compiler cases.- Upstream HistorySwipeNavigation.tsx was unchanged since 305a90dce4; this change only adds an indicator test ID there. PR #28091 addresses shadow-root scrollers, a separate case.
- Manual check in a desktop build carrying this change (recording below).
Evidence
Settings > General > Navigation, enabled and disabled:
Recording: 0–10 s the setting is on and horizontal swipes move between threads; at 12 s it is turned off; 15–24 s the same swipes leave the thread in place.
https://github.com/user-attachments/assets/aaa41028-ccc5-413c-a834-d8a64bb37132
Model: gpt-6.1-sol via Codex CLI, orchestrated by Claude Code.
Checks
Added 1 commit
- feat(web): add a setting to disable history swipe navigationdb2a71b
Approvability
Verdict: Approved at db2a71b
Macroscope's review found this PR approvable — Adds a desktop setting to disable swipe history navigation while keeping it enabled by default. The change is reversible and removes gesture listeners when disabled.
You can add or adjust custom eligibility rules. Learn more.
No actionable comments were generated in the recent review.
Walkthrough
-
The change adds a client setting for desktop history swipe navigation. It defaults to enabled, appears in Electron General settings, and controls whether
AppSidebarLayoutrenders the navigation component.Changes: History Swipe Navigation
Layer / File(s) Summary Client setting contract
packages/contracts/src/settings.ts,packages/contracts/src/settings.test.tsClientSettingsSchemadefaultshistorySwipeNavigationtotrue.ClientSettingsPatchaccepts an optional boolean. Tests cover omitted and explicit values.Settings controls and search
apps/web/src/components/settings/SettingsPanels.tsx,apps/web/src/components/settings/settingsSearch.ts,apps/web/src/components/settings/settingsSearch.test.tsThe Electron General settings panel exposes the setting and a reset action. Restore tracking and restoring defaults include it. Desktop settings search indexes the setting. Sidebar navigation gating
apps/web/src/components/AppSidebarLayout.tsx,apps/web/src/components/HistorySwipeNavigation.tsx,apps/web/src/components/AppSidebarLayout.browser.test.tsxAppSidebarLayoutrendersHistorySwipeNavigationonly in Electron when the setting is enabled. Browser tests cover disabled and enabled behavior in both directions. The indicator has a test ID.Estimated code review effort: 2 (Simple) | ~12 minutes
Change: Feature
Possibly related issues
- pingdotgg/t3code issue 32955 — The issue reports horizontal mouse-wheel input triggering desktop thread-history swipes and identifies an opt-out setting as a possible fix. This change adds that setting and gates the swipe component on its value.
Possibly related PRs
- pingdotgg/t3code#27430 — Adds the desktop history-swipe gesture that this change gates with a setting.
- pingdotgg/t3code#31728 — Adds the same setting and related settings, restore, search, and contract support.
- pingdotgg/t3code#29766 — Changes when
HistorySwipeNavigationinvokes history navigation; this change gates that handler and tests disabled behavior.
Suggested reviewers:
juliusmarminge
Merge Risk: External image Minimal · up to 18909
-
The swipe setting appears ready to merge after normal checks; no actionable issue is identified.
Pre-merge checks: Passed 4
-
Check name Status Explanation Title check Passed The title clearly summarizes the main change: adding a setting to disable history swipe navigation. Description check Passed The description includes the required Problem, Change, Scope and approval, and Verification sections. It explains the issue and behavior, defines the scope and approval rationale, reports focused test… Linked Issues check Passed Check skipped because no linked issues were found for this pull request. Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request. Full details: Description check
-
Explanation
The description includes the required Problem, Change, Scope and approval, and Verification sections. It explains the issue and behavior, defines the scope and approval rationale, reports focused test results, and provides screenshots and a recording for the UI change.
-
Finishing Touches
-
Generate unit tests (beta)
-
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
External image Recent review info
-
External image Run configuration
-
- Source mode: Auto
- Repository: pingdotgg/t3code
- Branch: main
- File: .coderabbit.config.ts
- Commit: 934c7f8
- Layers: Repository TS config
- Review profile: CHILL
- Plan: Advanced
- Run ID:
c3ba3d87-e96e-48ad-b6b4-0c5df54f9868
External image Commits
-
Reviewing files that changed from the base of the PR and between db2a71b12f7d5a389f60aa0fc05f1ff56c72de54 and 18909a592b54869b13ed9300431dd9e607aff5dd.
External image Files selected for processing: 7
-
apps/web/src/components/AppSidebarLayout.tsxapps/web/src/components/HistorySwipeNavigation.tsxapps/web/src/components/settings/SettingsPanels.tsxapps/web/src/components/settings/settingsSearch.test.tsapps/web/src/components/settings/settingsSearch.tspackages/contracts/src/settings.test.tspackages/contracts/src/settings.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
External image Recent review info
-
External image Run configuration
-
- Source mode: Auto
- Repository: pingdotgg/t3code
- Branch: main
- File: .coderabbit.config.ts
- Commit: 934c7f8
- Layers: Repository TS config
- Review profile: CHILL
- Plan: Advanced
- Run ID:
c3ba3d87-e96e-48ad-b6b4-0c5df54f9868
External image Commits
-
Reviewing files that changed from the base of the PR and between db2a71b12f7d5a389f60aa0fc05f1ff56c72de54 and 18909a592b54869b13ed9300431dd9e607aff5dd.
External image Files selected for processing: 7
-
apps/web/src/components/AppSidebarLayout.tsxapps/web/src/components/HistorySwipeNavigation.tsxapps/web/src/components/settings/SettingsPanels.tsxapps/web/src/components/settings/settingsSearch.test.tsapps/web/src/components/settings/settingsSearch.tspackages/contracts/src/settings.test.tspackages/contracts/src/settings.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
-
Note
Written by claude-opus-5-5 on behalf of Maria
Takeover update
merged current main into the branch at 18909a592b (merge commit, no rebase). one conflict in AppSidebarLayout.tsx: main added a showSidebar prop, the pr gates HistorySwipeNavigation on the setting. both kept. no other source changes; no required changes or actionable bot findings.
- blacksmith:
vp run check:changedpassed (format, lint, knip, typecheck of 37 packages, 7 test files, react compiler).AppSidebarLayout.browser.test.tsx,settingsSearch.test.tsand contractssettings.test.tspass. - the electron client was not re-driven after the merge. the evidence below is from
db2a71b.
Earlier verification (gpt-6.1-sol)
Verified in a real dev environment
verified as-is at db2a71b12f7d5a389f60aa0fc05f1ff56c72de54 on this linux vm. no source changes.
- reproduced on
github/mainat8b74a69019e3first: horizontal wheel input navigates between threads, with no setting to disable it. - used real dev servers, chrome through repository playwright, and the actual electron 44 desktop client built from each revision. created a real git project and two threads through the app. codex
gpt-6.1-solcompleted both main turns and another turn through the pr's web client. no mocks or seeded conversations. - in desktop settings, the control defaults on. switching off blocks both directions and removes the indicator. re-enabling restores both directions without reload. disabling during a swipe removes its indicator. the disabled preference survives reload.
- keyboard and toolbar back/forward still work while swipes are disabled. settings search for "horizontal wheel", per-setting reset, and restore device defaults work. chrome correctly hides the desktop-only control.
- blacksmith: desktop build and
vp run check:changed 15fe2832727893cbdd2214383f9f971474849695passed, including 35-package typechecking, 64 tests across six files, and four react compiler cases. all 31 reported ci checks pass.
after disabling, the real-client checks reported:
| 1 | disabled back swipe keeps beta true |
| 2 | disabled swipe has no indicator true |
| 3 | keyboard back still works while disabled true |
| 4 | disabled forward swipe keeps alpha true |
| 5 | keyboard forward still works while disabled true |
| 6 | toolbar back still works while disabled true |
| 7 | toolbar forward still works while disabled true |
| 8 | re-enabled back swipe works without reload true |
| 9 | re-enabled forward swipe works without reload true |
before, main settings with matching project data and viewport:
main general settings without a swipe control
pr, enabled:
pr general settings with swipe navigation enabled
pr, disabled:
pr general settings with swipe navigation disabled
main: horizontal wheel input moves beta to alpha and back.
main desktop wheel navigation between real codex threads
pr: disabled swipes leave the thread unchanged; keyboard and toolbar navigation still work; enabling restores swipe navigation. these animations contain the full recordings. the original mp4s were inspected and played through in t3, including seeking.
pr disabling swipes, preserving other navigation, and re-enabling
Not verified
macos hardware input from a physical trackpad or logitech mx master, mouse thumb buttons, windows desktop, native ios/android, and relay/tunnel connections were not exercised on this linux vm. the wheel checks used trusted chromium input through the real electron renderer and gesture ipc, so they do not prove macos hardware event provenance.
Note
🤖 Claude Opus 5.5 responding on behalf of Ilias
I'm a huge +1 on this PR. I use an Apple Magic Trackpad, and I couldn't understand what was happening: while I was scrolling, my threads kept suddenly switching from one to another.
need this urgently
I keep running into issues using shift+scroll to horizontally scroll diffs but end up over scrolling and moving forward/back in navigation history. It's incredibly obnoxious. Would this proposed setting fix that since over scrolls are considered swipes?
+1. Same on macOS desktop with an MX Master 3S: horizontally scrolling a wide Markdown table with the thumb wheel, once the table hits its edge the swipe indicator appears and the app jumps to another thread. It is easy to miss and confusing. It also happens with the trackpad in the same situation, so a setting to turn the gesture off would help both cases.
Same issue on Zorin OS 18.1 (Ubuntu-based), Wayland, T3 Code 0.1.7, with an MX Master 4. Horizontal thumb-wheel scrolling switches threads. Would appreciate this toggle.
🤖 Update on behalf of Mateusz (PR author): how this PR relates to the swipe fixes opened since.
- #32963 (fixes #32955) makes two-finger swipe macOS-only, because on Windows and Linux Electron cannot tell a tilt wheel from a trackpad. Touchpad users there lose the gesture; macOS keeps it unchanged.
- macOS, which #32963 leaves unchanged, has the same blind spot: Electron gives no trackpad-vs-wheel provenance there either. A horizontal mouse wheel (the MX Master thumb wheel this PR was opened for, on macOS) still drives the gesture.
- #28475 stops scrolls at the edge of a horizontal scroller from navigating. It does not cover a horizontal wheel over areas that do not scroll sideways, which is the case here.
This switch is the per-user escape hatch the platform check cannot provide: mouse users on macOS can turn the gesture off without taking it away from trackpad users. If #32963 lands first, I'll rebase and show the switch only where the gesture exists. #31728 proposes the same switch; happy to close this one in favour of whichever you prefer.
No code change in this update.
Added 1 commit
- Merge remote-tracking branch 'github/main' into feat/history-swipe-setting18909a5
macos hardware input from a physical trackpad or logitech mx master, mouse thumb buttons, windows desktop, native ios/android, and relay/tunnel connections were not exercised on this linux vm.
Adding the hardware side that the verification above could not cover (it ran on a Linux VM with synthetic wheel input).
I have used this change daily since 31 October in desktop builds on a MacBook Pro on macOS 26.6 with a Logitech MX Master and the built-in trackpad:
- Switch on (the default): a two-finger trackpad swipe goes back and forward as on main. The MX Master's thumb wheel triggers the same navigation, which is the misfire that made me open this PR.
- Switch off: neither the thumb wheel nor a trackpad swipe changes the thread. Keyboard and toolbar back/forward still work.
- The choice has survived every restart and app update since.
On #32963: it leaves macOS unchanged, so this case stays open there. If it lands first, I'll rebase this PR and show the switch only where the gesture exists.
Really agree with adding this as a toggleable feature. I think the suggested changes here just make the wording a little simpler and fit in with the rest of the settings a little better. Would love to see this merged!
| … | … | @@ -573,6 +573,9 @@ export function useSettingsRestore(onRestored?: () => void) { | |
| 574 | 574 | : []), | |
| 575 | 575 | ...(settings.confirmQuit !== DEFAULT_UNIFIED_SETTINGS.confirmQuit ? ["Quit shortcut"] : []), | |
| 576 | + | ...(settings.historySwipeNavigation !== DEFAULT_UNIFIED_SETTINGS.historySwipeNavigation | |
| 577 | + | ? ["Swipe to go back and forward"] | |
| 1 | ? ["Swipe to navigate"] |
| … | … | @@ -882,6 +886,37 @@ export function GeneralSettingsPanel() { | |
| 890 | + | <SettingsSection id="navigation" title="Navigation"> | |
| 891 | + | <SettingsRow | |
| 892 | + | {...searchableSetting("history-swipe-navigation")} | |
| 893 | + | description="Two-finger horizontal swipes move through your thread history. Turn off if a mouse with a horizontal wheel switches threads by accident." | |
| 1 | description="Swipe horizontally with two fingers on a trackpad to go back or forward." |
| … | … | @@ -818,6 +818,13 @@ export const SETTINGS_SEARCH_ITEMS = [ | |
| 822 | + | id: "history-swipe-navigation", | |
| 823 | + | title: "Swipe to go back and forward", | |
| 824 | + | to: "/settings/general", | |
| 825 | + | searchTerms: ["two-finger horizontal trackpad gesture mouse wheel thread history navigation"], | |
| 1 | searchTerms: ["two-finger horizontal trackpad gesture mouse wheel back forward thread history navigation"], |
| … | … | @@ -818,6 +818,13 @@ export const SETTINGS_SEARCH_ITEMS = [ | |
| 820 | 820 | }, | |
| 821 | + | { | |
| 822 | + | id: "history-swipe-navigation", | |
| 823 | + | title: "Swipe to go back and forward", | |
| 1 | title: "Swipe to navigate", |
Tested locally at 18909a592b on macOS, with a trackpad and an MX Master 3.
- Switch on: two-finger swipes and the thumb wheel navigate as before.
- Switch off: neither trackpad swipes nor the MX Master thumb wheel navigate anymore.
- ⌘[ / ⌘] and the back/forward buttons still work with it off.
- Turning it back on takes effect right away, no reload needed.
+1 to getting this merged -- accidental navigation happens to me multiple time per day.
Note
Written by Claude Opus 5.5 on behalf of Nikhil
@maria-rcks @juliusmarminge thanks for keeping this one and closing my duplicate (#34481). +1 to merge: it is clean against main, and kelchm and chomamateusz have confirmed it on macOS with a trackpad and an MX Master.
Two small refinements from #34481 that Julius mentioned. Neither needs to block this PR; I can open them as a follow-up after it merges, or you can take them here:
- Since #32963 the gesture runs only on macOS, so on Windows and Linux desktop the switch does nothing. Show the row only when
isElectron && isMacPlatform(navigator.platform), and mark its search itemmacOnly: true. - One line in
docs/user/keybindings.md, afternavigation.back/navigation.forward: "In the desktop app on macOS, a two-finger trackpad swipe does the same. Turn it off in Settings."