on main help sign in
feat: add keybinding to permissions dialog to navigate the diffview
7677483Andrey Nering
internal/tui/components/dialogs/permissions/keys.go+41−6
@@ -11,7 +11,12 @@ type KeyMap struct {
11 Select,
11 Select,
12 Allow,
12 Allow,
13 AllowSession,
13 AllowSession,
14+ Deny,
15+ ToggleDiffMode,
16+ ScrollDown,
17+ ScrollUp key.Binding
18+ ScrollLeft,
14− Deny key.Binding
19+ ScrollRight key.Binding
15}
20}
16
21
17func DefaultKeyMap() KeyMap {
22func DefaultKeyMap() KeyMap {
@@ -41,9 +46,29 @@ func DefaultKeyMap() KeyMap {
41 key.WithHelp("d", "deny"),
46 key.WithHelp("d", "deny"),
42 ),
47 ),
43 Select: key.NewBinding(
48 Select: key.NewBinding(
44− key.WithKeys("enter", "tab", "ctrl+y"),
49+ key.WithKeys("enter", "ctrl+y"),
45 key.WithHelp("enter", "confirm"),
50 key.WithHelp("enter", "confirm"),
46 ),
51 ),
52+ ToggleDiffMode: key.NewBinding(
53+ key.WithKeys("t"),
54+ key.WithHelp("t", "toggle diff mode"),
55+ ),
56+ ScrollDown: key.NewBinding(
57+ key.WithKeys("shift+down", "J"),
58+ key.WithHelp("shift+↓", "scroll down"),
59+ ),
60+ ScrollUp: key.NewBinding(
61+ key.WithKeys("shift+up", "K"),
62+ key.WithHelp("shift+↑", "scroll up"),
63+ ),
64+ ScrollLeft: key.NewBinding(
65+ key.WithKeys("shift+left", "H"),
66+ key.WithHelp("shift+←", "scroll left"),
67+ ),
68+ ScrollRight: key.NewBinding(
69+ key.WithKeys("shift+right", "L"),
70+ key.WithHelp("shift+→", "scroll right"),
71+ ),
47 }
72 }
48}
73}
49
74
@@ -57,6 +82,11 @@ func (k KeyMap) KeyBindings() []key.Binding {
57 k.Allow,
82 k.Allow,
58 k.AllowSession,
83 k.AllowSession,
59 k.Deny,
84 k.Deny,
85+ k.ToggleDiffMode,
86+ k.ScrollDown,
87+ k.ScrollUp,
88+ k.ScrollLeft,
89+ k.ScrollRight,
60 }
90 }
61}
91}
62
92
@@ -74,9 +104,14 @@ func (k KeyMap) FullHelp() [][]key.Binding {
74// ShortHelp implements help.KeyMap.
104// ShortHelp implements help.KeyMap.
75func (k KeyMap) ShortHelp() []key.Binding {
105func (k KeyMap) ShortHelp() []key.Binding {
76 return []key.Binding{
106 return []key.Binding{
77− k.Allow,
107+ k.ToggleDiffMode,
78− k.AllowSession,
108+ key.NewBinding(
79− k.Deny,
109+ key.WithKeys("shift+left", "shift+down", "shift+up", "shift+right"),
80− k.Select,
110+ key.WithHelp("shift+←↓↑→", "scroll"),
111+ ),
112+ key.NewBinding(
113+ key.WithKeys("shift+h", "shift+j", "shift+k", "shift+l"),
114+ key.WithHelp("shift+hjkl", "scroll"),
115+ ),
81 }
116 }
82}
117}
internal/tui/components/dialogs/permissions/permissions.go+37−4
@@ -4,6 +4,7 @@ import (
4 "fmt"
4 "fmt"
5 "strings"
5 "strings"
6
6
7+ "github.com/charmbracelet/bubbles/v2/help"
7 "github.com/charmbracelet/bubbles/v2/key"
8 "github.com/charmbracelet/bubbles/v2/key"
8 "github.com/charmbracelet/bubbles/v2/viewport"
9 "github.com/charmbracelet/bubbles/v2/viewport"
9 tea "github.com/charmbracelet/bubbletea/v2"
10 tea "github.com/charmbracelet/bubbletea/v2"
@@ -50,6 +51,11 @@ type permissionDialogCmp struct {
50 contentViewPort viewport.Model
51 contentViewPort viewport.Model
51 selectedOption int // 0: Allow, 1: Allow for session, 2: Deny
52 selectedOption int // 0: Allow, 1: Allow for session, 2: Deny
52
53
54+ // Diff view state
55+ diffSplitMode bool // true for split, false for unified
56+ diffXOffset int // horizontal scroll offset
57+ diffYOffset int // vertical scroll offset
58+
53 keyMap KeyMap
59 keyMap KeyMap
54}
60}
55
61
@@ -101,6 +107,21 @@ func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
101 util.CmdHandler(dialogs.CloseDialogMsg{}),
107 util.CmdHandler(dialogs.CloseDialogMsg{}),
102 util.CmdHandler(PermissionResponseMsg{Action: PermissionDeny, Permission: p.permission}),
108 util.CmdHandler(PermissionResponseMsg{Action: PermissionDeny, Permission: p.permission}),
103 )
109 )
110+ case key.Matches(msg, p.keyMap.ToggleDiffMode):
111+ p.diffSplitMode = !p.diffSplitMode
112+ return p, nil
113+ case key.Matches(msg, p.keyMap.ScrollDown):
114+ p.diffYOffset += 1
115+ return p, nil
116+ case key.Matches(msg, p.keyMap.ScrollUp):
117+ p.diffYOffset = max(0, p.diffYOffset-1)
118+ return p, nil
119+ case key.Matches(msg, p.keyMap.ScrollLeft):
120+ p.diffXOffset = max(0, p.diffXOffset-5)
121+ return p, nil
122+ case key.Matches(msg, p.keyMap.ScrollRight):
123+ p.diffXOffset += 5
124+ return p, nil
104 default:
125 default:
105 // Pass other keys to viewport
126 // Pass other keys to viewport
106 viewPort, cmd := p.contentViewPort.Update(msg)
127 viewPort, cmd := p.contentViewPort.Update(msg)
@@ -269,8 +290,15 @@ func (p *permissionDialogCmp) renderEditContent() string {
269 formatter := core.DiffFormatter().
290 formatter := core.DiffFormatter().
270 Before(fsext.PrettyPath(pr.FilePath), pr.OldContent).
291 Before(fsext.PrettyPath(pr.FilePath), pr.OldContent).
271 After(fsext.PrettyPath(pr.FilePath), pr.NewContent).
292 After(fsext.PrettyPath(pr.FilePath), pr.NewContent).
293+ Height(p.contentViewPort.Height()).
272 Width(p.contentViewPort.Width()).
294 Width(p.contentViewPort.Width()).
273− Split()
295+ XOffset(p.diffXOffset).
296+ YOffset(p.diffYOffset)
297+ if p.diffSplitMode {
298+ formatter = formatter.Split()
299+ } else {
300+ formatter = formatter.Unified()
301+ }
274
302
275 diff := formatter.String()
303 diff := formatter.String()
276 contentHeight := min(p.height-9, lipgloss.Height(diff))
304 contentHeight := min(p.height-9, lipgloss.Height(diff))
@@ -367,11 +395,13 @@ func (p *permissionDialogCmp) render() string {
367
395
368 // Render content based on tool type
396 // Render content based on tool type
369 var contentFinal string
397 var contentFinal string
398+ var contentHelp string
370 switch p.permission.ToolName {
399 switch p.permission.ToolName {
371 case tools.BashToolName:
400 case tools.BashToolName:
372 contentFinal = p.renderBashContent()
401 contentFinal = p.renderBashContent()
373 case tools.EditToolName:
402 case tools.EditToolName:
374 contentFinal = p.renderEditContent()
403 contentFinal = p.renderEditContent()
404+ contentHelp = help.New().View(p.keyMap)
375 case tools.WriteToolName:
405 case tools.WriteToolName:
376 contentFinal = p.renderWriteContent()
406 contentFinal = p.renderWriteContent()
377 case tools.FetchToolName:
407 case tools.FetchToolName:
@@ -381,8 +411,7 @@ func (p *permissionDialogCmp) render() string {
381 }
411 }
382 // Calculate content height dynamically based on window size
412 // Calculate content height dynamically based on window size
383
413
384− content := lipgloss.JoinVertical(
414+ strs := []string{
385− lipgloss.Top,
386 title,
415 title,
387 "",
416 "",
388 headerContent,
417 headerContent,
@@ -390,7 +419,11 @@ func (p *permissionDialogCmp) render() string {
390 "",
419 "",
391 buttons,
420 buttons,
392 "",
421 "",
393− )
422+ }
423+ if contentHelp != "" {
424+ strs = append(strs, "", contentHelp)
425+ }
426+ content := lipgloss.JoinVertical(lipgloss.Top, strs...)
394
427
395 return baseStyle.
428 return baseStyle.
396 Padding(0, 1).
429 Padding(0, 1).