fix: use canvas to render UI view

Ayman Bagabas created

Change summary

internal/ui/model/ui.go | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)

Detailed changes

internal/ui/model/ui.go 🔗

@@ -23,6 +23,7 @@ import (
 	"github.com/charmbracelet/crush/internal/version"
 	uv "github.com/charmbracelet/ultraviolet"
 	"github.com/charmbracelet/ultraviolet/screen"
+	"github.com/charmbracelet/x/ansi"
 )
 
 // uiFocusState represents the current focus state of the UI.
@@ -224,7 +225,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 }
 
 // Draw implements [tea.Layer] and draws the UI model.
-func (m *UI) Draw(scr tea.Screen, area tea.Rectangle) {
+func (m *UI) Draw(scr uv.Screen, area uv.Rectangle) {
 	layout := generateLayout(m, area.Dx(), area.Dy())
 
 	// Clear the screen first
@@ -334,7 +335,21 @@ func (m *UI) View() tea.View {
 		v.Cursor = cur
 	}
 
-	v.Content = m
+	// TODO: Switch to lipgloss.Canvas when available
+	canvas := uv.NewScreenBuffer(m.width, m.height)
+	canvas.Method = ansi.GraphemeWidth
+
+	m.Draw(canvas, canvas.Bounds())
+
+	content := strings.ReplaceAll(canvas.Render(), "\r\n", "\n") // normalize newlines
+	contentLines := strings.Split(content, "\n")
+	for i, line := range contentLines {
+		// Trim trailing spaces for concise rendering
+		contentLines[i] = strings.TrimRight(line, " ")
+	}
+
+	content = strings.Join(contentLines, "\n")
+	v.Content = content
 	if m.sendProgressBar && m.com.App != nil && m.com.App.AgentCoordinator != nil && m.com.App.AgentCoordinator.IsBusy() {
 		// HACK: use a random percentage to prevent ghostty from hiding it
 		// after a timeout.