@@ -4,6 +4,7 @@ import (
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
55+ diffSplitMode bool // true for split, false for unified
56+ diffXOffset int // horizontal scroll offset
57+ diffYOffset int // vertical scroll offset
53 keyMap KeyMap
59 keyMap KeyMap
@@ -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}),
110+ case key.Matches(msg, p.keyMap.ToggleDiffMode):
111+ p.diffSplitMode = !p.diffSplitMode
113+ case key.Matches(msg, p.keyMap.ScrollDown):
116+ case key.Matches(msg, p.keyMap.ScrollUp):
117+ p.diffYOffset = max(0, p.diffYOffset-1)
119+ case key.Matches(msg, p.keyMap.ScrollLeft):
120+ p.diffXOffset = max(0, p.diffXOffset-5)
122+ case key.Matches(msg, p.keyMap.ScrollRight):
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()
300+ formatter = formatter.Unified()
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 {
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 {
382 // Calculate content height dynamically based on window size
412 // Calculate content height dynamically based on window size
384− content := lipgloss.JoinVertical(
414+ strs := []string{
388 headerContent,
417 headerContent,
@@ -390,7 +419,11 @@ func (p *permissionDialogCmp) render() string {
423+ if contentHelp != "" {
424+ strs = append(strs, "", contentHelp)
426+ content := lipgloss.JoinVertical(lipgloss.Top, strs...)
395 return baseStyle.
428 return baseStyle.
396 Padding(0, 1).
429 Padding(0, 1).