fix(ui): correctly position cursor when attachments are present (#2190)

Ayman Bagabas created

This change fixes a UI issue where the cursor is off by one line when
attachments are present in the editor.

Change summary

internal/ui/model/ui.go | 34 +++++++++++++---------------------
1 file changed, 13 insertions(+), 21 deletions(-)

Detailed changes

internal/ui/model/ui.go 🔗

@@ -1928,12 +1928,8 @@ func (m *UI) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
 
 		if m.textarea.Focused() {
 			cur := m.textarea.Cursor()
-			cur.X++ // Adjust for app margins
-			cur.Y += m.layout.editor.Min.Y
-			// Offset for attachment row if present.
-			if len(m.attachments.List()) > 0 {
-				cur.Y++
-			}
+			cur.X++                            // Adjust for app margins
+			cur.Y += m.layout.editor.Min.Y + 1 // Offset for attachments row
 			return cur
 		}
 	}
@@ -2223,7 +2219,10 @@ func (m *UI) updateSize() {
 
 	m.chat.SetSize(m.layout.main.Dx(), m.layout.main.Dy())
 	m.textarea.SetWidth(m.layout.editor.Dx())
-	m.textarea.SetHeight(m.layout.editor.Dy())
+	// TODO: Abstract the textarea and attachments into a single editor
+	// component so we don't have to manually account for the attachments
+	// height here.
+	m.textarea.SetHeight(m.layout.editor.Dy() - 2) // Account for top margin/attachments and bottom margin
 	m.renderPills()
 
 	// Handle different app states
@@ -2375,14 +2374,6 @@ func (m *UI) generateLayout(w, h int) uiLayout {
 		}
 	}
 
-	if !uiLayout.editor.Empty() {
-		// Add editor margins 1 top and bottom
-		if len(m.attachments.List()) == 0 {
-			uiLayout.editor.Min.Y += 1
-		}
-		uiLayout.editor.Max.Y -= 1
-	}
-
 	return uiLayout
 }
 
@@ -2686,14 +2677,15 @@ func (m *UI) randomizePlaceholders() {
 
 // renderEditorView renders the editor view with attachments if any.
 func (m *UI) renderEditorView(width int) string {
-	if len(m.attachments.List()) == 0 {
-		return m.textarea.View()
+	var attachmentsView string
+	if len(m.attachments.List()) > 0 {
+		attachmentsView = m.attachments.Render(width)
 	}
-	return lipgloss.JoinVertical(
-		lipgloss.Top,
-		m.attachments.Render(width),
+	return strings.Join([]string{
+		attachmentsView,
 		m.textarea.View(),
-	)
+		"", // margin at bottom of editor
+	}, "\n")
 }
 
 // cacheSidebarLogo renders and caches the sidebar logo at the specified width.