From b2f2c9ac37e05455c282bf4106a5288d3a1478b3 Mon Sep 17 00:00:00 2001 From: adamdotdevin <2363879+adamdottv@users.noreply.github.com> Date: Tue, 12 Aug 2025 07:52:09 -0500 Subject: [PATCH] fix: use real cursor instead of virtual cursor --- .../tui/internal/components/chat/editor.go | 6 +++ packages/tui/internal/tui/tui.go | 48 ++++++++++++++----- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/packages/tui/internal/components/chat/editor.go b/packages/tui/internal/components/chat/editor.go index 509de624..980d5262 100644 --- a/packages/tui/internal/components/chat/editor.go +++ b/packages/tui/internal/components/chat/editor.go @@ -31,6 +31,7 @@ type EditorComponent interface { tea.Model tea.ViewModel Content() string + Cursor() *tea.Cursor Lines() int Value() string Length() int @@ -407,6 +408,10 @@ func (m *editorComponent) Content() string { return content } +func (m *editorComponent) Cursor() *tea.Cursor { + return m.textarea.Cursor() +} + func (m *editorComponent) View() string { width := m.width if m.app.Session.ID == "" { @@ -694,6 +699,7 @@ func NewEditorComponent(app *app.App) EditorComponent { ta.Prompt = " " ta.ShowLineNumbers = false ta.CharLimit = -1 + ta.VirtualCursor = false ta = updateTextareaStyles(ta) m := &editorComponent{ diff --git a/packages/tui/internal/tui/tui.go b/packages/tui/internal/tui/tui.go index 6a56e201..7895adea 100644 --- a/packages/tui/internal/tui/tui.go +++ b/packages/tui/internal/tui/tui.go @@ -59,6 +59,8 @@ const interruptDebounceTimeout = 1 * time.Second const exitDebounceTimeout = 1 * time.Second type Model struct { + tea.Model + tea.CursorModel width, height int app *app.App modal layout.Modal @@ -748,15 +750,17 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return a, tea.Batch(cmds...) } -func (a Model) View() string { +func (a Model) View() (string, *tea.Cursor) { t := theme.CurrentTheme() var mainLayout string + var editorX int + var editorY int if a.app.Session.ID == "" { - mainLayout = a.home() + mainLayout, editorX, editorY = a.home() } else { - mainLayout = a.chat() + mainLayout, editorX, editorY = a.chat() } mainLayout = styles.NewStyle(). Background(t.Background()). @@ -780,7 +784,12 @@ func (a Model) View() string { if theme.CurrentThemeUsesAnsiColors() { mainLayout = util.ConvertRGBToAnsi16Colors(mainLayout) } - return mainLayout + "\n" + a.status.View() + + cursor := a.editor.Cursor() + cursor.Position.X += editorX + cursor.Position.Y += editorY + + return mainLayout + "\n" + a.status.View(), cursor } func (a Model) Cleanup() { @@ -807,7 +816,7 @@ func (a Model) openFile(filepath string) (tea.Model, tea.Cmd) { return a, cmd } -func (a Model) home() string { +func (a Model) home() (string, int, int) { t := theme.CurrentTheme() effectiveWidth := a.width - 4 baseStyle := styles.NewStyle().Background(t.Background()) @@ -899,14 +908,21 @@ func (a Model) home() string { styles.WhitespaceStyle(t.Background()), ) - editorX := (effectiveWidth - editorWidth) / 2 + editorX := max(0, (effectiveWidth-editorWidth)/2) editorY := (a.height / 2) + (mainHeight / 2) - 2 if editorLines > 1 { + content := a.editor.Content() + editorHeight := lipgloss.Height(content) + + if editorY+editorHeight > a.height { + difference := (editorY + editorHeight) - a.height + editorY -= difference + } mainLayout = layout.PlaceOverlay( editorX, editorY, - a.editor.Content(), + content, mainLayout, ) } @@ -924,10 +940,10 @@ func (a Model) home() string { ) } - return mainLayout + return mainLayout, editorX + 5, editorY + 2 } -func (a Model) chat() string { +func (a Model) chat() (string, int, int) { effectiveWidth := a.width - 4 t := theme.CurrentTheme() editorView := a.editor.View() @@ -944,14 +960,20 @@ func (a Model) chat() string { ) mainLayout := messagesView + "\n" + editorView - editorX := (effectiveWidth - editorWidth) / 2 + editorX := max(0, (effectiveWidth-editorWidth)/2) + editorY := a.height - editorHeight if lines > 1 { - editorY := a.height - editorHeight + content := a.editor.Content() + editorHeight := lipgloss.Height(content) + if editorY+editorHeight > a.height { + difference := (editorY + editorHeight) - a.height + editorY -= difference + } mainLayout = layout.PlaceOverlay( editorX, editorY, - a.editor.Content(), + content, mainLayout, ) } @@ -970,7 +992,7 @@ func (a Model) chat() string { ) } - return mainLayout + return mainLayout, editorX + 5, editorY + 2 } func (a Model) executeCommand(command commands.Command) (tea.Model, tea.Cmd) {