diff --git a/README.md b/README.md
index 6d79886..7c37aa9 100644
--- a/README.md
+++ b/README.md
@@ -226,9 +226,14 @@ If you want the exact delta configuration I'm using - [it can be found here](htt
| i | Cycle icon style |
| o | Open file in $EDITOR |
| s | Toggle side-by-side/unified view |
+| v | Visual select mode (release mouse for native text selection / copy) |
| Tab | Switch focus between the panes |
| q | Quit |
+While mouse capture is on, your terminal can't do its native click-and-drag selection. Press v to enter visual select mode — this releases the mouse so you can select and copy lines from the diff (the footer shows `visual select`); press v again to restore mouse scroll/click. Tip: in side-by-side mode the selection spans both columns — switch to unified with s first, or use your terminal's block-select modifier (Option+drag in iTerm2 / Terminal.app, Alt+drag in many Linux terminals).
+
+Most terminals (iTerm2, Terminal.app, Alacritty, kitty, WezTerm, GNOME Terminal) also let you hold Shift while dragging to bypass mouse capture without toggling.
+
## Discord
Have questions? Join our [Discord community](https://discord.gg/SXNXp9NctV)!
diff --git a/pkg/ui/keys.go b/pkg/ui/keys.go
index 32bb9be..77183a9 100644
--- a/pkg/ui/keys.go
+++ b/pkg/ui/keys.go
@@ -24,6 +24,7 @@ type KeyMap struct {
ToggleIconStyle key.Binding
ToggleHelp key.Binding
ToggleMessage key.Binding
+ ToggleMouse key.Binding
}
var keys = &KeyMap{
@@ -111,6 +112,10 @@ var keys = &KeyMap{
key.WithKeys("m"),
key.WithHelp("m", "commit info"),
),
+ ToggleMouse: key.NewBinding(
+ key.WithKeys("v"),
+ key.WithHelp("v", "visual select"),
+ ),
}
func KeyGroups() [][]key.Binding {
@@ -133,6 +138,7 @@ func KeyGroups() [][]key.Binding {
keys.ToggleIconStyle,
}, {
keys.ToggleMessage,
+ keys.ToggleMouse,
keys.ToggleHelp,
keys.Quit,
}}
diff --git a/pkg/ui/tui.go b/pkg/ui/tui.go
index feb7bda..ea35993 100644
--- a/pkg/ui/tui.go
+++ b/pkg/ui/tui.go
@@ -95,6 +95,7 @@ type mainModel struct {
pendingCursorPath string
watchInFlight bool
repoRoot string
+ mouseDisabled bool
}
func New(input string, cfg config.Config) mainModel {
@@ -255,6 +256,10 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.search.SetWidth(m.searchWidth())
dfCmd := m.diffViewer.SetSize(m.width-sidebarWidth, h)
cmds = append(cmds, dfCmd)
+ case key.Matches(msg, keys.ToggleMouse):
+ m.mouseDisabled = !m.mouseDisabled
+ m.draggingSidebar = false
+ return m, tea.Batch(cmds...)
case key.Matches(msg, keys.ToggleIconStyle):
m.cycleIconStyle()
case key.Matches(msg, keys.ToggleDiffView):
@@ -488,7 +493,11 @@ func (m mainModel) searchUpdate(msg tea.Msg) (mainModel, []tea.Cmd) {
func (m mainModel) View() tea.View {
var view tea.View
view.AltScreen = true
- view.MouseMode = tea.MouseModeAllMotion
+ if m.mouseDisabled {
+ view.MouseMode = tea.MouseModeNone
+ } else {
+ view.MouseMode = tea.MouseModeAllMotion
+ }
view.KeyboardEnhancements.ReportEventTypes = true
// Determine colors based on active panel.
@@ -826,6 +835,12 @@ func (m mainModel) footerView() string {
usedWidth += lipgloss.Width(sep) + lipgloss.Width(watchLabel)
}
+ if m.mouseDisabled {
+ mouseLabel := base.Foreground(lipgloss.Yellow).Render("visual select")
+ parts = append(parts, sep, mouseLabel)
+ usedWidth += lipgloss.Width(sep) + lipgloss.Width(mouseLabel)
+ }
+
spacing := base.Render(strings.Repeat(" ", max(0, m.width-usedWidth)))
parts = append(parts, spacing, help)
return base.