Skip to content
This repository was archived by the owner on Sep 18, 2025. It is now read-only.

Commit b28c1e0

Browse files
kujtimiihoxhatermai
andcommitted
Improve diff display with colored formatting in permission dialogs
- Add colored formatting for diff display (green for additions, red for removals) - Create a dedicated formatDiff function to handle diff styling - Restructure permission dialog render function for better organization - Apply custom styling to different line types in diffs 🤖 Generated with termai Co-Authored-By: termai <noreply@termai.io>
1 parent ef15976 commit b28c1e0

1 file changed

Lines changed: 171 additions & 66 deletions

File tree

internal/tui/components/dialog/permission.go

Lines changed: 171 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,45 @@ type permissionDialogCmp struct {
6565
selectOption *huh.Select[string]
6666
}
6767

68+
// formatDiff formats a diff string with colors for additions and deletions
69+
func formatDiff(diffText string) string {
70+
lines := strings.Split(diffText, "\n")
71+
var formattedLines []string
72+
73+
// Define styles for different line types
74+
addStyle := lipgloss.NewStyle().Foreground(styles.Green)
75+
removeStyle := lipgloss.NewStyle().Foreground(styles.Red)
76+
headerStyle := lipgloss.NewStyle().Bold(true).Foreground(styles.Blue)
77+
contextStyle := lipgloss.NewStyle().Foreground(styles.SubText0)
78+
79+
// Process each line
80+
for _, line := range lines {
81+
if strings.HasPrefix(line, "+") {
82+
formattedLines = append(formattedLines, addStyle.Render(line))
83+
} else if strings.HasPrefix(line, "-") {
84+
formattedLines = append(formattedLines, removeStyle.Render(line))
85+
} else if strings.HasPrefix(line, "Changes:") || strings.HasPrefix(line, " ...") {
86+
formattedLines = append(formattedLines, headerStyle.Render(line))
87+
} else if strings.HasPrefix(line, " ") {
88+
formattedLines = append(formattedLines, contextStyle.Render(line))
89+
} else {
90+
formattedLines = append(formattedLines, line)
91+
}
92+
}
93+
94+
// Join all formatted lines
95+
content := strings.Join(formattedLines, "\n")
96+
97+
// Create a bordered box for the content
98+
contentStyle := lipgloss.NewStyle().
99+
MarginTop(1).
100+
Padding(0, 1).
101+
Border(lipgloss.RoundedBorder()).
102+
BorderForeground(styles.Flamingo)
103+
104+
return contentStyle.Render(content)
105+
}
106+
68107
func (p *permissionDialogCmp) Init() tea.Cmd {
69108
return nil
70109
}
@@ -132,93 +171,159 @@ func (p *permissionDialogCmp) render() string {
132171
lipgloss.JoinHorizontal(lipgloss.Left, keyStyle.Render("Path:"), " ", valueStyle.Render(p.permission.Path)),
133172
" ",
134173
}
174+
175+
// Create the header content first so it can be used in all cases
176+
headerContent := lipgloss.NewStyle().Padding(0, 1).Render(lipgloss.JoinVertical(lipgloss.Left, headerParts...))
177+
135178
r, _ := glamour.NewTermRenderer(
136179
glamour.WithStyles(styles.CatppuccinMarkdownStyle()),
137180
glamour.WithWordWrap(p.width-10),
138181
glamour.WithEmoji(),
139182
)
140-
content := ""
183+
184+
// Handle different tool types
141185
switch p.permission.ToolName {
142186
case tools.BashToolName:
143187
pr := p.permission.Params.(tools.BashPermissionsParams)
144188
headerParts = append(headerParts, keyStyle.Render("Command:"))
145-
content = fmt.Sprintf("```bash\n%s\n```", pr.Command)
189+
content := fmt.Sprintf("```bash\n%s\n```", pr.Command)
190+
191+
renderedContent, _ := r.Render(content)
192+
p.contentViewPort.Width = p.width - 2 - 2
193+
194+
// Calculate content height dynamically based on content
195+
contentLines := len(strings.Split(renderedContent, "\n"))
196+
// Set a reasonable min/max for the viewport height
197+
minContentHeight := 3
198+
maxContentHeight := p.height - lipgloss.Height(headerContent) - lipgloss.Height(form) - 2 - 2 - 1
199+
200+
// Add some padding to the content lines
201+
contentHeight := contentLines + 2
202+
if contentHeight < minContentHeight {
203+
contentHeight = minContentHeight
204+
}
205+
if contentHeight > maxContentHeight {
206+
contentHeight = maxContentHeight
207+
}
208+
p.contentViewPort.Height = contentHeight
209+
210+
p.contentViewPort.SetContent(renderedContent)
211+
212+
// Style the viewport
213+
var contentBorder lipgloss.Border
214+
var borderColor lipgloss.TerminalColor
215+
216+
if p.isViewportFocus {
217+
contentBorder = lipgloss.DoubleBorder()
218+
borderColor = styles.Blue
219+
} else {
220+
contentBorder = lipgloss.RoundedBorder()
221+
borderColor = styles.Flamingo
222+
}
223+
224+
contentStyle := lipgloss.NewStyle().
225+
MarginTop(1).
226+
Padding(0, 1).
227+
Border(contentBorder).
228+
BorderForeground(borderColor)
229+
230+
if p.isViewportFocus {
231+
contentStyle = contentStyle.BorderBackground(styles.Surface0)
232+
}
233+
234+
contentFinal := contentStyle.Render(p.contentViewPort.View())
235+
236+
return lipgloss.JoinVertical(
237+
lipgloss.Top,
238+
headerContent,
239+
contentFinal,
240+
form,
241+
)
242+
146243
case tools.EditToolName:
147244
pr := p.permission.Params.(tools.EditPermissionsParams)
148245
headerParts = append(headerParts, keyStyle.Render("Update"))
149-
content = fmt.Sprintf("```\n%s\n```", pr.Diff)
246+
// Recreate header content with the updated headerParts
247+
headerContent = lipgloss.NewStyle().Padding(0, 1).Render(lipgloss.JoinVertical(lipgloss.Left, headerParts...))
248+
// Format the diff with colors instead of using markdown code block
249+
formattedDiff := formatDiff(pr.Diff)
250+
return lipgloss.JoinVertical(
251+
lipgloss.Top,
252+
headerContent,
253+
formattedDiff,
254+
form,
255+
)
256+
150257
case tools.WriteToolName:
151258
pr := p.permission.Params.(tools.WritePermissionsParams)
152259
headerParts = append(headerParts, keyStyle.Render("Content"))
153-
content = fmt.Sprintf("```\n%s\n```", pr.Content)
260+
// Recreate header content with the updated headerParts
261+
headerContent = lipgloss.NewStyle().Padding(0, 1).Render(lipgloss.JoinVertical(lipgloss.Left, headerParts...))
262+
// Format the diff with colors instead of using markdown code block
263+
formattedDiff := formatDiff(pr.Content)
264+
return lipgloss.JoinVertical(
265+
lipgloss.Top,
266+
headerContent,
267+
formattedDiff,
268+
form,
269+
)
270+
154271
case tools.FetchToolName:
155272
pr := p.permission.Params.(tools.FetchPermissionsParams)
156273
headerParts = append(headerParts, keyStyle.Render("URL: "+pr.URL))
157-
default:
158-
content = p.permission.Description
159-
}
160-
161-
renderedContent, _ := r.Render(content)
162-
headerContent := lipgloss.NewStyle().Padding(0, 1).Render(lipgloss.JoinVertical(lipgloss.Left, headerParts...))
163-
p.contentViewPort.Width = p.width - 2 - 2
164-
165-
// Calculate content height dynamically based on content
166-
contentLines := len(strings.Split(renderedContent, "\n"))
167-
// Set a reasonable min/max for the viewport height
168-
minContentHeight := 3
169-
maxContentHeight := p.height - lipgloss.Height(headerContent) - lipgloss.Height(form) - 2 - 2 - 1
170-
171-
// For bash commands, adjust height based on content length
172-
if p.permission.ToolName == tools.BashToolName {
173-
// Add some padding to the content lines
174-
contentHeight := contentLines + 2
175-
if contentHeight < minContentHeight {
176-
contentHeight = minContentHeight
274+
content := p.permission.Description
275+
276+
renderedContent, _ := r.Render(content)
277+
p.contentViewPort.Width = p.width - 2 - 2
278+
p.contentViewPort.Height = p.height - lipgloss.Height(headerContent) - lipgloss.Height(form) - 2 - 2 - 1
279+
p.contentViewPort.SetContent(renderedContent)
280+
281+
// Style the viewport
282+
contentStyle := lipgloss.NewStyle().
283+
MarginTop(1).
284+
Padding(0, 1).
285+
Border(lipgloss.RoundedBorder()).
286+
BorderForeground(styles.Flamingo)
287+
288+
contentFinal := contentStyle.Render(p.contentViewPort.View())
289+
if renderedContent == "" {
290+
contentFinal = ""
177291
}
178-
if contentHeight > maxContentHeight {
179-
contentHeight = maxContentHeight
292+
293+
return lipgloss.JoinVertical(
294+
lipgloss.Top,
295+
headerContent,
296+
contentFinal,
297+
form,
298+
)
299+
300+
default:
301+
content := p.permission.Description
302+
303+
renderedContent, _ := r.Render(content)
304+
p.contentViewPort.Width = p.width - 2 - 2
305+
p.contentViewPort.Height = p.height - lipgloss.Height(headerContent) - lipgloss.Height(form) - 2 - 2 - 1
306+
p.contentViewPort.SetContent(renderedContent)
307+
308+
// Style the viewport
309+
contentStyle := lipgloss.NewStyle().
310+
MarginTop(1).
311+
Padding(0, 1).
312+
Border(lipgloss.RoundedBorder()).
313+
BorderForeground(styles.Flamingo)
314+
315+
contentFinal := contentStyle.Render(p.contentViewPort.View())
316+
if renderedContent == "" {
317+
contentFinal = ""
180318
}
181-
p.contentViewPort.Height = contentHeight
182-
} else {
183-
// For other content types, use the full available height
184-
p.contentViewPort.Height = maxContentHeight
319+
320+
return lipgloss.JoinVertical(
321+
lipgloss.Top,
322+
headerContent,
323+
contentFinal,
324+
form,
325+
)
185326
}
186-
187-
p.contentViewPort.SetContent(renderedContent)
188-
189-
// Make focus change more apparent with different border styles and colors
190-
var contentBorder lipgloss.Border
191-
var borderColor lipgloss.TerminalColor
192-
193-
if p.isViewportFocus {
194-
contentBorder = lipgloss.DoubleBorder()
195-
borderColor = styles.Blue
196-
} else {
197-
contentBorder = lipgloss.RoundedBorder()
198-
borderColor = styles.Flamingo
199-
}
200-
201-
contentStyle := lipgloss.NewStyle().
202-
MarginTop(1).
203-
Padding(0, 1).
204-
Border(contentBorder).
205-
BorderForeground(borderColor)
206-
207-
if p.isViewportFocus {
208-
contentStyle = contentStyle.BorderBackground(styles.Surface0)
209-
}
210-
211-
contentFinal := contentStyle.Render(p.contentViewPort.View())
212-
if renderedContent == "" {
213-
contentFinal = ""
214-
}
215-
216-
return lipgloss.JoinVertical(
217-
lipgloss.Top,
218-
headerContent,
219-
contentFinal,
220-
form,
221-
)
222327
}
223328

224329
func (p *permissionDialogCmp) View() string {

0 commit comments

Comments
 (0)