on main help sign in
fix(ui): use ansi.truncate everywhere
9c61c7dKieran Klukas
internal/agent/agent.go+2−1
@@ -46,6 +46,7 @@ import (
46 "github.com/charmbracelet/crush/internal/session"
46 "github.com/charmbracelet/crush/internal/session"
47 "github.com/charmbracelet/crush/internal/stringext"
47 "github.com/charmbracelet/crush/internal/stringext"
48 "github.com/charmbracelet/crush/internal/version"
48 "github.com/charmbracelet/crush/internal/version"
49+ "github.com/charmbracelet/x/ansi"
49 "github.com/charmbracelet/x/exp/charmtone"
50 "github.com/charmbracelet/x/exp/charmtone"
50)
51)
51
52
@@ -1762,7 +1763,7 @@ func (a *sessionAgent) GenerateTitle(ctx context.Context, sessionID string, user
1762 fallback := strings.ReplaceAll(userPrompt, "\n", " ")
1763 fallback := strings.ReplaceAll(userPrompt, "\n", " ")
1763 fallback = strings.TrimSpace(fallback)
1764 fallback = strings.TrimSpace(fallback)
1764 if len(fallback) > 50 {
1765 if len(fallback) > 50 {
1765− fallback = fallback[:50]
1766+ fallback = ansi.Truncate(fallback, 50, "…")
1766 }
1767 }
1767 title = cmp.Or(fallback, DefaultSessionName)
1768 title = cmp.Or(fallback, DefaultSessionName)
1768 }
1769 }
internal/agent/tools/grep.go+3−2
@@ -23,6 +23,7 @@ import (
23 "github.com/charmbracelet/crush/internal/config"
23 "github.com/charmbracelet/crush/internal/config"
24 "github.com/charmbracelet/crush/internal/csync"
24 "github.com/charmbracelet/crush/internal/csync"
25 "github.com/charmbracelet/crush/internal/fsext"
25 "github.com/charmbracelet/crush/internal/fsext"
26+ "github.com/charmbracelet/x/ansi"
26)
27)
27
28
28// regexCache provides thread-safe caching of compiled regex patterns
29// regexCache provides thread-safe caching of compiled regex patterns
@@ -161,8 +162,8 @@ func NewGrepTool(workingDir string, config config.ToolGrep) fantasy.AgentTool {
161 }
162 }
162 if match.lineNum > 0 {
163 if match.lineNum > 0 {
163 lineText := match.lineText
164 lineText := match.lineText
164− if len(lineText) > maxGrepContentWidth {
165+ if ansi.StringWidth(lineText) > maxGrepContentWidth {
165− lineText = lineText[:maxGrepContentWidth] + "..."
166+ lineText = ansi.Truncate(lineText, maxGrepContentWidth, "...")
166 }
167 }
167 if match.charNum > 0 {
168 if match.charNum > 0 {
168 fmt.Fprintf(&output, " Line %d, Char %d: %s\n", match.lineNum, match.charNum, lineText)
169 fmt.Fprintf(&output, " Line %d, Char %d: %s\n", match.lineNum, match.charNum, lineText)
internal/agent/tools/view.go+3−1
@@ -335,7 +335,9 @@ func readTextFile(filePath string, offset, limit, maxContentSize int) (string, b
335 lineText = strings.TrimSuffix(lineText, "\n")
335 lineText = strings.TrimSuffix(lineText, "\n")
336 lineText = strings.TrimSuffix(lineText, "\r")
336 lineText = strings.TrimSuffix(lineText, "\r")
337 if len(lineText) > MaxLineLength {
337 if len(lineText) > MaxLineLength {
338+ // Truncate at a rune boundary to avoid splitting
339+ // multi-byte characters.
338− lineText = lineText[:MaxLineLength] + "..."
340+ lineText = strings.ToValidUTF8(lineText[:MaxLineLength], "") + "..."
339 }
341 }
340 projectedSize := contentSize + len(lineText)
342 projectedSize := contentSize + len(lineText)
341 if len(lines) > 0 {
343 if len(lines) > 0 {