@@ -655,6 +655,7 @@ func joinHeaderBody(header, body string) string {
655655
656656func renderPlainContent(v *toolCallCmp, content string) string {
657657 t := styles.CurrentTheme()
658+ content = strings.ReplaceAll(content, "\r\n", "\n") // Normalize line endings
658659 content = strings.TrimSpace(content)
659660 lines := strings.Split(content, "\n")
660661
@@ -664,6 +665,7 @@ func renderPlainContent(v *toolCallCmp, content string) string {
664665 if i >= responseContextHeight {
665666 break
666667 }
668+ ln = escapeContent(ln)
667669 ln = " " + ln // left padding
668670 if len(ln) > width {
669671 ln = v.fit(ln, width)
@@ -680,6 +682,7 @@ func renderPlainContent(v *toolCallCmp, content string) string {
680682 Width(width).
681683 Render(fmt.Sprintf("… (%d lines)", len(lines)-responseContextHeight)))
682684 }
685+
683686 return strings.Join(out, "\n")
684687}
685688
@@ -694,10 +697,17 @@ func pad(v any, width int) string {
694697
695698func renderCodeContent(v *toolCallCmp, path, content string, offset int) string {
696699 t := styles.CurrentTheme()
700+ content = strings.ReplaceAll(content, "\r\n", "\n") // Normalize line endings
701+ content = strings.ReplaceAll(content, "\t", " ") // Replace tabs with spaces
697702 truncated := truncateHeight(content, responseContextHeight)
698703
699− highlighted, _ := highlight.SyntaxHighlight(truncated, path, t.BgBase)
700− lines := strings.Split(highlighted, "\n")
704+ lines := strings.Split(truncated, "\n")
705+ for i, ln := range lines {
706+ lines[i] = escapeContent(ln)
707+ }
708+
709+ highlighted, _ := highlight.SyntaxHighlight(strings.Join(lines, "\n"), path, t.BgBase)
710+ lines = strings.Split(highlighted, "\n")
701711
702712 if len(strings.Split(content, "\n")) > responseContextHeight {
703713 lines = append(lines, t.S().Muted.
@@ -721,6 +731,7 @@ func renderCodeContent(v *toolCallCmp, path, content string, offset int) string
721731 PaddingLeft(1).
722732 Render(v.fit(ln, w-1)))
723733 }
734+
724735 return lipgloss.JoinVertical(lipgloss.Left, lines...)
725736}
726737
@@ -766,3 +777,20 @@ func prettifyToolName(name string) string {
766777 return name
767778 }
768779}
780+
781+// escapeContent replaces control characters with their Unicode Control Picture
782+// representations to ensure they are displayed correctly in the UI.
783+func escapeContent(content string) string {
784+ var sb strings.Builder
785+ for _, r := range content {
786+ switch {
787+ case r >= 0 && r <= 0x1f: // Control characters 0x00-0x1F
788+ sb.WriteRune('\u2400' + r)
789+ case r == ansi.DEL:
790+ sb.WriteRune('\u2421')
791+ default:
792+ sb.WriteRune(r)
793+ }
794+ }
795+ return sb.String()
796+}