diff --git a/internal/tui/components/chat/messages/renderer.go b/internal/tui/components/chat/messages/renderer.go index 87eb2c8476655fe7d11fc8c787e73b32d4584de4..ecd095798633e62e004a165c82b5d1816797c175 100644 --- a/internal/tui/components/chat/messages/renderer.go +++ b/internal/tui/components/chat/messages/renderer.go @@ -3,6 +3,7 @@ package messages import ( "encoding/json" "fmt" + "strconv" "strings" "time" @@ -656,6 +657,7 @@ func joinHeaderBody(header, body string) string { func renderPlainContent(v *toolCallCmp, content string) string { t := styles.CurrentTheme() content = strings.TrimSpace(content) + content = escapeContent(t, content) lines := strings.Split(content, "\n") width := v.textWidth() - 2 // -2 for left padding @@ -694,6 +696,7 @@ func pad(v any, width int) string { func renderCodeContent(v *toolCallCmp, path, content string, offset int) string { t := styles.CurrentTheme() + content = escapeContent(t, content) truncated := truncateHeight(content, responseContextHeight) highlighted, _ := highlight.SyntaxHighlight(truncated, path, t.BgBase) @@ -766,3 +769,41 @@ func prettifyToolName(name string) string { return name } } + +// escapeContent escapes ANSI escape sequences and control characters in the +// content and styles it for display in the terminal. +func escapeContent(t *styles.Theme, content string) string { + lines := strings.Split(content, "\n") + for i, line := range lines { + lines[i] = escapeLine(t, line) + } + + content = strings.Join(lines, "\n") + return content +} + +// escapeLine escapes ANSI escape sequences and control characters and styles +// them for display in the terminal. +func escapeLine(t *styles.Theme, text string) string { + var ( + sb strings.Builder + state byte + seq string + n int + w int + ) + faint := ansi.NewStyle().Faint().ForegroundColor(t.FgMuted) + for len(text) > 0 { + seq, w, n, state = ansi.DecodeSequence(text, state, nil) + if w > 0 { + sb.WriteString(seq) + } else { + quote := strconv.Quote(seq) + quote = strings.TrimPrefix(quote, "\"") + quote = strings.TrimSuffix(quote, "\"") + sb.WriteString(faint.Styled(quote)) + } + text = text[n:] + } + return sb.String() +}