@@ -4,6 +4,7 @@ import (
44 "fmt"
55 "strings"
66
7+ "github.com/charmbracelet/bubbles/v2/help"
78 "github.com/charmbracelet/bubbles/v2/key"
89 "github.com/charmbracelet/bubbles/v2/viewport"
910 tea "github.com/charmbracelet/bubbletea/v2"
@@ -50,6 +51,11 @@ type permissionDialogCmp struct {
5051 contentViewPort viewport.Model
5152 selectedOption int // 0: Allow, 1: Allow for session, 2: Deny
5253
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+
5359 keyMap KeyMap
5460}
5561
@@ -101,6 +107,21 @@ func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
101107 util.CmdHandler(dialogs.CloseDialogMsg{}),
102108 util.CmdHandler(PermissionResponseMsg{Action: PermissionDeny, Permission: p.permission}),
103109 )
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
104125 default:
105126 // Pass other keys to viewport
106127 viewPort, cmd := p.contentViewPort.Update(msg)
@@ -269,8 +290,15 @@ func (p *permissionDialogCmp) renderEditContent() string {
269290 formatter := core.DiffFormatter().
270291 Before(fsext.PrettyPath(pr.FilePath), pr.OldContent).
271292 After(fsext.PrettyPath(pr.FilePath), pr.NewContent).
293+ Height(p.contentViewPort.Height()).
272294 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+ }
274302
275303 diff := formatter.String()
276304 contentHeight := min(p.height-9, lipgloss.Height(diff))
@@ -367,11 +395,13 @@ func (p *permissionDialogCmp) render() string {
367395
368396 // Render content based on tool type
369397 var contentFinal string
398+ var contentHelp string
370399 switch p.permission.ToolName {
371400 case tools.BashToolName:
372401 contentFinal = p.renderBashContent()
373402 case tools.EditToolName:
374403 contentFinal = p.renderEditContent()
404+ contentHelp = help.New().View(p.keyMap)
375405 case tools.WriteToolName:
376406 contentFinal = p.renderWriteContent()
377407 case tools.FetchToolName:
@@ -381,8 +411,7 @@ func (p *permissionDialogCmp) render() string {
381411 }
382412 // Calculate content height dynamically based on window size
383413
384− content := lipgloss.JoinVertical(
385− lipgloss.Top,
414+ strs := []string{
386415 title,
387416 "",
388417 headerContent,
@@ -390,7 +419,11 @@ func (p *permissionDialogCmp) render() string {
390419 "",
391420 buttons,
392421 "",
393− )
422+ }
423+ if contentHelp != "" {
424+ strs = append(strs, "", contentHelp)
425+ }
426+ content := lipgloss.JoinVertical(lipgloss.Top, strs...)
394427
395428 return baseStyle.
396429 Padding(0, 1).