on main help sign in
refactor: move ansi escape function to the `ansiext` package
63efed6Andrey Nering
internal/ansiext/ansi.gonew file+24
@@ -0,0 +1,24 @@
1+package ansiext
2+
3+import (
4+ "strings"
5+
6+ "github.com/charmbracelet/x/ansi"
7+)
8+
9+// Escape replaces control characters with their Unicode Control Picture
10+// representations to ensure they are displayed correctly in the UI.
11+func Escape(content string) string {
12+ var sb strings.Builder
13+ for _, r := range content {
14+ switch {
15+ case r >= 0 && r <= 0x1f: // Control characters 0x00-0x1F
16+ sb.WriteRune('\u2400' + r)
17+ case r == ansi.DEL:
18+ sb.WriteRune('\u2421')
19+ default:
20+ sb.WriteRune(r)
21+ }
22+ }
23+ return sb.String()
24+}
internal/tui/components/chat/messages/renderer.go+3−19
@@ -6,6 +6,7 @@ import (
6 "strings"
6 "strings"
7 "time"
7 "time"
8
8
9+ "github.com/charmbracelet/crush/internal/ansiext"
9 "github.com/charmbracelet/crush/internal/fsext"
10 "github.com/charmbracelet/crush/internal/fsext"
10 "github.com/charmbracelet/crush/internal/llm/agent"
11 "github.com/charmbracelet/crush/internal/llm/agent"
11 "github.com/charmbracelet/crush/internal/llm/tools"
12 "github.com/charmbracelet/crush/internal/llm/tools"
@@ -702,7 +703,7 @@ func renderPlainContent(v *toolCallCmp, content string) string {
702 if i >= responseContextHeight {
703 if i >= responseContextHeight {
703 break
704 break
704 }
705 }
705− ln = escapeContent(ln)
706+ ln = ansiext.Escape(ln)
706 ln = " " + ln // left padding
707 ln = " " + ln // left padding
707 if len(ln) > width {
708 if len(ln) > width {
708 ln = v.fit(ln, width)
709 ln = v.fit(ln, width)
@@ -740,7 +741,7 @@ func renderCodeContent(v *toolCallCmp, path, content string, offset int) string
740
741
741 lines := strings.Split(truncated, "\n")
742 lines := strings.Split(truncated, "\n")
742 for i, ln := range lines {
743 for i, ln := range lines {
743− lines[i] = escapeContent(ln)
744+ lines[i] = ansiext.Escape(ln)
744 }
745 }
745
746
746 highlighted, _ := highlight.SyntaxHighlight(strings.Join(lines, "\n"), path, t.BgBase)
747 highlighted, _ := highlight.SyntaxHighlight(strings.Join(lines, "\n"), path, t.BgBase)
@@ -816,20 +817,3 @@ func prettifyToolName(name string) string {
816 return name
817 return name
817 }
818 }
818}
819}
819−
820−// escapeContent replaces control characters with their Unicode Control Picture
821−// representations to ensure they are displayed correctly in the UI.
822−func escapeContent(content string) string {
823− var sb strings.Builder
824− for _, r := range content {
825− switch {
826− case r >= 0 && r <= 0x1f: // Control characters 0x00-0x1F
827− sb.WriteRune('\u2400' + r)
828− case r == ansi.DEL:
829− sb.WriteRune('\u2421')
830− default:
831− sb.WriteRune(r)
832− }
833− }
834− return sb.String()
835−}