From e81cfdba331d455b258f7f62c2e7340906f35c61 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 10 Jul 2026 17:00:44 +0200 Subject: [PATCH] Add a keybinding for toggling mouse capture at runtime (%) Some terminals provide a modifier key that bypasses mouse capture so that text can be selected with the mouse and copied to the clipboard (option in iTerm, shift in Ghostty), but others don't (e.g. the terminals built into VS Code and Zed). In those, selecting text requires setting gui.mouseEvents to false in the config file and back again afterwards, which is inconvenient. Add a global keybinding (% by default) that toggles mouse capture on the fly. Like the whitespace toggle, it flips the in-memory config value, so the toggled state lasts until the config file is changed and reloaded. The key needs to be free in every panel, and % is; as a plain rune it also arrives reliably in every terminal on all platforms, and it can't fire while typing in a text prompt, since editors consume plain runes. An alt-based binding would have been more mnemonic, but doesn't reach lazygit in the built-in terminals of VS Code and Zed on macOS (the very terminals that need this feature) unless the option key is configured to act as meta; and a ctrl-based binding would burn one of the few remaining free ctrl keys, which are better saved for future features that get used more frequently. Co-Authored-By: Claude Fable 5 --- docs-master/Config.md | 3 +++ docs-master/keybindings/Keybindings_en.md | 1 + docs-master/keybindings/Keybindings_ja.md | 1 + docs-master/keybindings/Keybindings_ko.md | 1 + docs-master/keybindings/Keybindings_nl.md | 1 + docs-master/keybindings/Keybindings_pl.md | 1 + docs-master/keybindings/Keybindings_pt.md | 1 + docs-master/keybindings/Keybindings_ru.md | 1 + docs-master/keybindings/Keybindings_zh-CN.md | 1 + docs-master/keybindings/Keybindings_zh-TW.md | 1 + pkg/config/user_config.go | 3 +++ pkg/gui/controllers/global_controller.go | 20 ++++++++++++++++++ pkg/i18n/english.go | 8 +++++++ .../tests/misc/toggle_mouse_capture.go | 21 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + schema-master/config.json | 16 +++++++++++++- 16 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 pkg/integration/tests/misc/toggle_mouse_capture.go diff --git a/docs-master/Config.md b/docs-master/Config.md index 1d101be1863..8af7e84e8d7 100644 --- a/docs-master/Config.md +++ b/docs-master/Config.md @@ -73,6 +73,8 @@ gui: # If true, capture mouse events. # When mouse events are captured, it's a little harder to select text: e.g. # requiring you to hold the option key when on macOS. + # Mouse capture can also be toggled at runtime with the 'toggleMouseCapture' + # keybinding ('%' by default). mouseEvents: true # If true, do not show a warning when amending a commit. @@ -725,6 +727,7 @@ keybinding: submitEditorText: extrasMenu: '@' toggleWhitespaceInDiffView: + toggleMouseCapture: '%' increaseContextInDiffView: '}' decreaseContextInDiffView: '{' increaseRenameSimilarityThreshold: ) diff --git a/docs-master/keybindings/Keybindings_en.md b/docs-master/keybindings/Keybindings_en.md index 4aa202740d0..6afc8115277 100644 --- a/docs-master/keybindings/Keybindings_en.md +++ b/docs-master/keybindings/Keybindings_en.md @@ -31,6 +31,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` q, `` | Quit | | | `` `` | Suspend the application | | | `` `` | Toggle whitespace | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | +| `` % `` | Toggle mouse capture | Toggle whether lazygit captures mouse events. When mouse capture is off, the terminal handles the mouse, so you can select text with it; this is useful in terminals that don't have a modifier key to bypass the capture.

The default can be changed in the config file with the key 'gui.mouseEvents'. | | `` `` | Edit config file | Open file in external editor. | | `` z `` | Undo | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. | | `` Z `` | Redo | The reflog will be used to determine what git command to run to redo the last git command. This does not include changes to the working tree; only commits are taken into consideration. | diff --git a/docs-master/keybindings/Keybindings_ja.md b/docs-master/keybindings/Keybindings_ja.md index 5b9c798a1e8..d34d11cd2a0 100644 --- a/docs-master/keybindings/Keybindings_ja.md +++ b/docs-master/keybindings/Keybindings_ja.md @@ -31,6 +31,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` q, `` | 終了 | | | `` `` | Suspend the application | | | `` `` | 空白表示の切り替え | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | +| `` % `` | Toggle mouse capture | Toggle whether lazygit captures mouse events. When mouse capture is off, the terminal handles the mouse, so you can select text with it; this is useful in terminals that don't have a modifier key to bypass the capture.

The default can be changed in the config file with the key 'gui.mouseEvents'. | | `` `` | 設定ファイルを編集 | 外部エディタでファイルを開きます。 | | `` z `` | 元に戻す | 最後のgitコマンドを元に戻すために実行するgitコマンドを決定するためにreflogが使用されます。これにはワーキングツリーへの変更は含まれません。コミットのみが考慮されます。 | | `` Z `` | やり直す | 最後のgitコマンドをやり直すために実行するgitコマンドを決定するためにreflogが使用されます。これにはワーキングツリーへの変更は含まれません。コミットのみが考慮されます。 | diff --git a/docs-master/keybindings/Keybindings_ko.md b/docs-master/keybindings/Keybindings_ko.md index d1eb3afb1cb..81214eb558f 100644 --- a/docs-master/keybindings/Keybindings_ko.md +++ b/docs-master/keybindings/Keybindings_ko.md @@ -31,6 +31,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` q, `` | 종료 | | | `` `` | Suspend the application | | | `` `` | 공백문자를 Diff 뷰에서 표시 여부 전환 | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | +| `` % `` | Toggle mouse capture | Toggle whether lazygit captures mouse events. When mouse capture is off, the terminal handles the mouse, so you can select text with it; this is useful in terminals that don't have a modifier key to bypass the capture.

The default can be changed in the config file with the key 'gui.mouseEvents'. | | `` `` | 설정 파일 수정 | Open file in external editor. | | `` z `` | 되돌리기 (reflog) (실험적) | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. | | `` Z `` | 다시 실행 (reflog) (실험적) | The reflog will be used to determine what git command to run to redo the last git command. This does not include changes to the working tree; only commits are taken into consideration. | diff --git a/docs-master/keybindings/Keybindings_nl.md b/docs-master/keybindings/Keybindings_nl.md index 0eb61729b5e..1464409ad52 100644 --- a/docs-master/keybindings/Keybindings_nl.md +++ b/docs-master/keybindings/Keybindings_nl.md @@ -31,6 +31,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` q, `` | Afsluiten | | | `` `` | Pauzeer de applicatie | | | `` `` | Toggle whitespace | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | +| `` % `` | Toggle mouse capture | Toggle whether lazygit captures mouse events. When mouse capture is off, the terminal handles the mouse, so you can select text with it; this is useful in terminals that don't have a modifier key to bypass the capture.

The default can be changed in the config file with the key 'gui.mouseEvents'. | | `` `` | Verander config bestand | Open bestand in externe editor. | | `` z `` | Ongedaan maken (via reflog) (experimenteel) | The reflog will be used to determine what git command to run to undo the last git command. This does not include changes to the working tree; only commits are taken into consideration. | | `` Z `` | Redo (via reflog) (experimenteel) | The reflog will be used to determine what git command to run to redo the last git command. This does not include changes to the working tree; only commits are taken into consideration. | diff --git a/docs-master/keybindings/Keybindings_pl.md b/docs-master/keybindings/Keybindings_pl.md index 719a7e6c4fe..8af254f35df 100644 --- a/docs-master/keybindings/Keybindings_pl.md +++ b/docs-master/keybindings/Keybindings_pl.md @@ -31,6 +31,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` q, `` | Wyjdź | | | `` `` | Suspend the application | | | `` `` | Przełącz białe znaki | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | +| `` % `` | Toggle mouse capture | Toggle whether lazygit captures mouse events. When mouse capture is off, the terminal handles the mouse, so you can select text with it; this is useful in terminals that don't have a modifier key to bypass the capture.

The default can be changed in the config file with the key 'gui.mouseEvents'. | | `` `` | Edytuj plik konfiguracyjny | Otwórz plik w zewnętrznym edytorze. | | `` z `` | Cofnij | Dziennik reflog zostanie użyty do określenia, jakie polecenie git należy uruchomić, aby cofnąć ostatnie polecenie git. Nie obejmuje to zmian w drzewie roboczym; brane są pod uwagę tylko commity. | | `` Z `` | Ponów | Dziennik reflog zostanie użyty do określenia, jakie polecenie git należy uruchomić, aby ponowić ostatnie polecenie git. Nie obejmuje to zmian w drzewie roboczym; brane są pod uwagę tylko commity. | diff --git a/docs-master/keybindings/Keybindings_pt.md b/docs-master/keybindings/Keybindings_pt.md index 5fcba268804..baa8851f1a0 100644 --- a/docs-master/keybindings/Keybindings_pt.md +++ b/docs-master/keybindings/Keybindings_pt.md @@ -31,6 +31,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` q, `` | Sair | | | `` `` | Suspender a aplicação | | | `` `` | Toggle whitespace | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | +| `` % `` | Toggle mouse capture | Toggle whether lazygit captures mouse events. When mouse capture is off, the terminal handles the mouse, so you can select text with it; this is useful in terminals that don't have a modifier key to bypass the capture.

The default can be changed in the config file with the key 'gui.mouseEvents'. | | `` `` | Editar arquivo de configuração | Abrir arquivo no editor externo. | | `` z `` | Desfazer | O reflog será usado para determinar qual comando git para executar para desfazer o último comando git. Isto não inclui mudanças na árvore de trabalho; apenas compromissos são tidos em consideração. | | `` Z `` | Refazer | O reflog será usado para determinar qual comando git para executar para refazer o último comando git. Isto não inclui mudanças na árvore de trabalho; apenas compromissos são tidos em consideração. | diff --git a/docs-master/keybindings/Keybindings_ru.md b/docs-master/keybindings/Keybindings_ru.md index 6831354480b..47b46fc74c5 100644 --- a/docs-master/keybindings/Keybindings_ru.md +++ b/docs-master/keybindings/Keybindings_ru.md @@ -31,6 +31,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` q, `` | Выйти | | | `` `` | Suspend the application | | | `` `` | Переключить отображение изменении пробелов в просмотрщике сравнении | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | +| `` % `` | Toggle mouse capture | Toggle whether lazygit captures mouse events. When mouse capture is off, the terminal handles the mouse, so you can select text with it; this is useful in terminals that don't have a modifier key to bypass the capture.

The default can be changed in the config file with the key 'gui.mouseEvents'. | | `` `` | Редактировать файл конфигурации | Open file in external editor. | | `` z `` | Отменить (через reflog) (экспериментальный) | Журнал ссылок (reflog) будет использоваться для определения того, какую команду git запустить, чтобы отменить последнюю команду git. Сюда не входят изменения в рабочем дереве; учитываются только коммиты. | | `` Z `` | Повторить (через reflog) (экспериментальный) | Журнал ссылок (reflog) будет использоваться для определения того, какую команду git нужно запустить, чтобы повторить последнюю команду git. Сюда не входят изменения в рабочем дереве; учитываются только коммиты. | diff --git a/docs-master/keybindings/Keybindings_zh-CN.md b/docs-master/keybindings/Keybindings_zh-CN.md index b4f1134ecfe..9a213862268 100644 --- a/docs-master/keybindings/Keybindings_zh-CN.md +++ b/docs-master/keybindings/Keybindings_zh-CN.md @@ -31,6 +31,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` q, `` | 退出 | | | `` `` | 挂起应用程序 | | | `` `` | 切换是否在差异视图中显示空白字符差异 | 切换是否在差异视图中显示空白字符更改。

默认值可在配置文件中通过键 'git.ignoreWhitespaceInDiffView' 更改。 | +| `` % `` | Toggle mouse capture | Toggle whether lazygit captures mouse events. When mouse capture is off, the terminal handles the mouse, so you can select text with it; this is useful in terminals that don't have a modifier key to bypass the capture.

The default can be changed in the config file with the key 'gui.mouseEvents'. | | `` `` | 编辑配置文件 | 使用外部编辑器打开文件 | | `` z `` | 撤销 | Reflog将用于确定运行哪个git命令来撤消最后一个git命令。这并不包括对工作树的更改,只考虑提交。 | | `` Z `` | 重做 | Reflog将用于确定运行哪个git命令来重做上一个git命令。这并不包括对工作树的更改,只考虑提交。 | diff --git a/docs-master/keybindings/Keybindings_zh-TW.md b/docs-master/keybindings/Keybindings_zh-TW.md index b50e25d354e..bb1b4580369 100644 --- a/docs-master/keybindings/Keybindings_zh-TW.md +++ b/docs-master/keybindings/Keybindings_zh-TW.md @@ -31,6 +31,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` q, `` | 結束 | | | `` `` | Suspend the application | | | `` `` | 切換是否在差異檢視中顯示空格變更 | Toggle whether or not whitespace changes are shown in the diff view.

The default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'. | +| `` % `` | Toggle mouse capture | Toggle whether lazygit captures mouse events. When mouse capture is off, the terminal handles the mouse, so you can select text with it; this is useful in terminals that don't have a modifier key to bypass the capture.

The default can be changed in the config file with the key 'gui.mouseEvents'. | | `` `` | 編輯設定檔案 | 使用外部編輯器開啟 | | `` z `` | 復原 | 將使用 reflog 確任 git 指令以復原。這不包括工作區更改;只考慮提交。 | | `` Z `` | 取消復原 | 將使用 reflog 確任 git 指令以重作。這不包括工作區更改;只考慮提交。 | diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 30ce0377d5a..a53375fb2b2 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -91,6 +91,7 @@ type GuiConfig struct { TabWidth int `yaml:"tabWidth" jsonschema:"minimum=1"` // If true, capture mouse events. // When mouse events are captured, it's a little harder to select text: e.g. requiring you to hold the option key when on macOS. + // Mouse capture can also be toggled at runtime with the 'toggleMouseCapture' keybinding ('%' by default). MouseEvents bool `yaml:"mouseEvents"` // If true, do not show a warning when amending a commit. SkipAmendWarning bool `yaml:"skipAmendWarning"` @@ -548,6 +549,7 @@ type KeybindingUniversalConfig struct { SubmitEditorText Keybinding `yaml:"submitEditorText"` ExtrasMenu Keybinding `yaml:"extrasMenu"` ToggleWhitespaceInDiffView Keybinding `yaml:"toggleWhitespaceInDiffView"` + ToggleMouseCapture Keybinding `yaml:"toggleMouseCapture"` IncreaseContextInDiffView Keybinding `yaml:"increaseContextInDiffView"` DecreaseContextInDiffView Keybinding `yaml:"decreaseContextInDiffView"` IncreaseRenameSimilarityThreshold Keybinding `yaml:"increaseRenameSimilarityThreshold"` @@ -1066,6 +1068,7 @@ func GetDefaultConfigForPlatform(platform string) *UserConfig { SubmitEditorText: Keybinding{""}, ExtrasMenu: Keybinding{"@"}, ToggleWhitespaceInDiffView: Keybinding{""}, + ToggleMouseCapture: Keybinding{"%"}, IncreaseContextInDiffView: Keybinding{"}"}, DecreaseContextInDiffView: Keybinding{"{"}, IncreaseRenameSimilarityThreshold: Keybinding{")"}, diff --git a/pkg/gui/controllers/global_controller.go b/pkg/gui/controllers/global_controller.go index 8b9871294fe..3573fbea3cb 100644 --- a/pkg/gui/controllers/global_controller.go +++ b/pkg/gui/controllers/global_controller.go @@ -136,6 +136,12 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type Description: self.c.Tr.ToggleWhitespaceInDiffView, Tooltip: self.c.Tr.ToggleWhitespaceInDiffViewTooltip, }, + { + Keys: opts.GetKeys(opts.Config.Universal.ToggleMouseCapture), + Handler: self.toggleMouseCapture, + Description: self.c.Tr.ToggleMouseCapture, + Tooltip: self.c.Tr.ToggleMouseCaptureTooltip, + }, { Keys: opts.GetKeys(opts.Config.Universal.EditConfig), Handler: self.editConfig, @@ -273,6 +279,20 @@ func (self *GlobalController) toggleWhitespace() error { return (&ToggleWhitespaceAction{c: self.c}).Call() } +func (self *GlobalController) toggleMouseCapture() error { + userConfig := self.c.UserConfig() + userConfig.Gui.MouseEvents = !userConfig.Gui.MouseEvents + self.c.GocuiGui().Mouse = userConfig.Gui.MouseEvents + + if userConfig.Gui.MouseEvents { + self.c.Toast(self.c.Tr.MouseCaptureEnabled) + } else { + self.c.Toast(self.c.Tr.MouseCaptureDisabled) + } + + return nil +} + func (self *GlobalController) editConfig() error { return (&EditConfigAction{c: self.c}).Call() } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 69ea7012f27..2514be7de25 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -779,6 +779,10 @@ type TranslationSet struct { ToggleWhitespaceInDiffViewTooltip string IgnoreWhitespaceDiffViewSubTitle string IgnoreWhitespaceNotSupportedHere string + ToggleMouseCapture string + ToggleMouseCaptureTooltip string + MouseCaptureEnabled string + MouseCaptureDisabled string IncreaseContextInDiffView string IncreaseContextInDiffViewTooltip string DecreaseContextInDiffView string @@ -1931,6 +1935,10 @@ func EnglishTranslationSet() *TranslationSet { ToggleWhitespaceInDiffViewTooltip: "Toggle whether or not whitespace changes are shown in the diff view.\n\nThe default can be changed in the config file with the key 'git.ignoreWhitespaceInDiffView'.", IgnoreWhitespaceDiffViewSubTitle: "(ignoring whitespace)", IgnoreWhitespaceNotSupportedHere: "Ignoring whitespace is not supported in this view", + ToggleMouseCapture: "Toggle mouse capture", + ToggleMouseCaptureTooltip: "Toggle whether lazygit captures mouse events. When mouse capture is off, the terminal handles the mouse, so you can select text with it; this is useful in terminals that don't have a modifier key to bypass the capture.\n\nThe default can be changed in the config file with the key 'gui.mouseEvents'.", + MouseCaptureEnabled: "Mouse capture enabled", + MouseCaptureDisabled: "Mouse capture disabled", IncreaseContextInDiffView: "Increase diff context size", IncreaseContextInDiffViewTooltip: "Increase the amount of the context shown around changes in the diff view.\n\nThe default can be changed in the config file with the key 'git.diffContextSize'.", DecreaseContextInDiffView: "Decrease diff context size", diff --git a/pkg/integration/tests/misc/toggle_mouse_capture.go b/pkg/integration/tests/misc/toggle_mouse_capture.go new file mode 100644 index 00000000000..c8bdb49bec4 --- /dev/null +++ b/pkg/integration/tests/misc/toggle_mouse_capture.go @@ -0,0 +1,21 @@ +package misc + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var ToggleMouseCapture = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Toggle mouse capture off and back on with the global keybinding", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) {}, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.GlobalPress(keys.Universal.ToggleMouseCapture) + t.ExpectToast(Equals("Mouse capture disabled")) + + t.GlobalPress(keys.Universal.ToggleMouseCapture) + t.ExpectToast(Equals("Mouse capture enabled")) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index abf13073ed0..25a44183c3a 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -347,6 +347,7 @@ var tests = []*components.IntegrationTest{ misc.DirenvUnloadsOnBlockedEnvrc, misc.InitialOpen, misc.RecentReposOnLaunch, + misc.ToggleMouseCapture, patch_building.Apply, patch_building.ApplyInReverse, patch_building.ApplyInReverseWithConflict, diff --git a/schema-master/config.json b/schema-master/config.json index 82dbebb0b7f..85709e672fa 100644 --- a/schema-master/config.json +++ b/schema-master/config.json @@ -540,7 +540,7 @@ }, "mouseEvents": { "type": "boolean", - "description": "If true, capture mouse events.\nWhen mouse events are captured, it's a little harder to select text: e.g. requiring you to hold the option key when on macOS.", + "description": "If true, capture mouse events.\nWhen mouse events are captured, it's a little harder to select text: e.g. requiring you to hold the option key when on macOS.\nMouse capture can also be toggled at runtime with the 'toggleMouseCapture' keybinding ('%' by default).", "default": true }, "skipAmendWarning": { @@ -3335,6 +3335,20 @@ ], "default": "\u003cctrl+w\u003e" }, + "toggleMouseCapture": { + "oneOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "default": "%" + }, "increaseContextInDiffView": { "oneOf": [ {