Skip to content
This repository was archived by the owner on Sep 18, 2025. It is now read-only.

Commit f154400

Browse files
committed
wip dialogs
1 parent a23e1a3 commit f154400

40 files changed

Lines changed: 1926 additions & 1094 deletions

cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"flagWords":[],"version":"0.2","words":["opencode","charmbracelet","lipgloss","bubbletea"],"language":"en"}

go.mod

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ require (
1111
github.com/aymanbagabas/go-udiff v0.2.0
1212
github.com/bmatcuk/doublestar/v4 v4.8.1
1313
github.com/catppuccin/go v0.3.0
14-
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1
15-
github.com/charmbracelet/bubbletea/v2 v2.0.0-beta1
16-
github.com/charmbracelet/glamour/v2 v2.0.0-20250513163904-eeeced3bb3c6
17-
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1.0.20250513162854-28902d027c40
18-
github.com/charmbracelet/x/ansi v0.9.2
14+
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250526131538-b3f0c9e42318
15+
github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3.0.20250526132317-434f93986a5c
16+
github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe
17+
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1.0.20250523195325-2d1af06b557c
18+
github.com/charmbracelet/x/ansi v0.9.3-0.20250516160309-24eee56f89fa
1919
github.com/fsnotify/fsnotify v1.8.0
2020
github.com/go-logfmt/logfmt v0.6.0
2121
github.com/google/uuid v1.6.0
@@ -57,12 +57,12 @@ require (
5757
github.com/aws/smithy-go v1.20.3 // indirect
5858
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
5959
github.com/aymerick/douceur v0.2.0 // indirect
60-
github.com/charmbracelet/colorprofile v0.3.0 // indirect
61-
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250326144200-0875329e71da // indirect
60+
github.com/charmbracelet/colorprofile v0.3.1 // indirect
61+
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa // indirect
6262
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
63-
github.com/charmbracelet/x/input v0.3.4 // indirect
63+
github.com/charmbracelet/x/input v0.3.5-0.20250509021451-13796e822d86 // indirect
6464
github.com/charmbracelet/x/term v0.2.1 // indirect
65-
github.com/charmbracelet/x/windows v0.2.0 // indirect
65+
github.com/charmbracelet/x/windows v0.2.1 // indirect
6666
github.com/davecgh/go-spew v1.1.1 // indirect
6767
github.com/disintegration/imaging v1.6.2
6868
github.com/dlclark/regexp2 v1.11.4 // indirect

go.sum

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -68,30 +68,30 @@ github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR
6868
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
6969
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
7070
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
71-
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1 h1:swACzss0FjnyPz1enfX56GKkLiuKg5FlyVmOLIlU2kE=
72-
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
73-
github.com/charmbracelet/bubbletea/v2 v2.0.0-beta1 h1:yaxFt97mvofGY7bYZn8U/aSVoamXGE3O4AEvWhshUDI=
74-
github.com/charmbracelet/bubbletea/v2 v2.0.0-beta1/go.mod h1:qbcZLI5z8R49v9xBdU5V5Dh5D2uccx8wSwBqxQyErqc=
75-
github.com/charmbracelet/colorprofile v0.3.0 h1:KtLh9uuu1RCt+Hml4s6Hz+kB1PfV3wi++1h5ia65yKQ=
76-
github.com/charmbracelet/colorprofile v0.3.0/go.mod h1:oHJ340RS2nmG1zRGPmhJKJ/jf4FPNNk0P39/wBPA1G0=
77-
github.com/charmbracelet/glamour/v2 v2.0.0-20250513163904-eeeced3bb3c6 h1:AKhOV8dSRU3KpqMgpGME9JU7ouumB2S6hMmD6PRJeTc=
78-
github.com/charmbracelet/glamour/v2 v2.0.0-20250513163904-eeeced3bb3c6/go.mod h1:7xBAUTCSADx9mHG0uBf4NDoVpYxMzIQ2j/NMLGdFsFM=
79-
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1.0.20250513162854-28902d027c40 h1:SxOUomYAVo5zh+6WCH1bGshlAnSKP0ZeovI0FHAl9kg=
80-
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1.0.20250513162854-28902d027c40/go.mod h1:tRlx/Hu0lo/j9viunCN2H+Ze6JrmdjQlXUQvvArgaOc=
81-
github.com/charmbracelet/x/ansi v0.9.2 h1:92AGsQmNTRMzuzHEYfCdjQeUzTrgE1vfO5/7fEVoXdY=
82-
github.com/charmbracelet/x/ansi v0.9.2/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
83-
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250326144200-0875329e71da h1:8MGKD5WBtuzfXglq0CnyzVSwGojv57X+H46OL9OUyRA=
84-
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250326144200-0875329e71da/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
71+
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250526131538-b3f0c9e42318 h1:f8Q0ybZGxT+St1JfPM7yoz/XFpbmtodcIehaom/9XT8=
72+
github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250526131538-b3f0c9e42318/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
73+
github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3.0.20250526132317-434f93986a5c h1:EoW1x1K2EDKYw1D7raqZqWKnwk21IZVpYqLHQVhz1ZU=
74+
github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.3.0.20250526132317-434f93986a5c/go.mod h1:sXuGtrlVJo43r1fVGBM06E7PPb16oBl8rDRr6YgQOck=
75+
github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40=
76+
github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0=
77+
github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe h1:i6ce4CcAlPpTj2ER69m1DBeLZ3RRcHnKExuwhKa3GfY=
78+
github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe/go.mod h1:p3Q+aN4eQKeM5jhrmXPMgPrlKbmc59rWSnMsSA3udhk=
79+
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1.0.20250523195325-2d1af06b557c h1:177KMz8zHRlEZJsWzafbKYh6OdjgvTspoH+UjaxgIXY=
80+
github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1.0.20250523195325-2d1af06b557c/go.mod h1:EJWvaCrhOhNGVZMvcjc0yVryl4qqpMs8tz0r9WyEkdQ=
81+
github.com/charmbracelet/x/ansi v0.9.3-0.20250516160309-24eee56f89fa h1:JU05TLAB6nOEL46bxHDV/+e8umBX32ODsGbVkc7o7bk=
82+
github.com/charmbracelet/x/ansi v0.9.3-0.20250516160309-24eee56f89fa/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
83+
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa h1:lphz0Z3rsiOtMYiz8axkT24i9yFiueDhJbzyNUADmME=
84+
github.com/charmbracelet/x/cellbuf v0.0.14-0.20250516160309-24eee56f89fa/go.mod h1:xBlh2Yi3DL3zy/2n15kITpg0YZardf/aa/hgUaIM6Rk=
8585
github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a h1:FsHEJ52OC4VuTzU8t+n5frMjLvpYWEznSr/u8tnkCYw=
8686
github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
8787
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI=
8888
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU=
89-
github.com/charmbracelet/x/input v0.3.4 h1:Mujmnv/4DaitU0p+kIsrlfZl/UlmeLKw1wAP3e1fMN0=
90-
github.com/charmbracelet/x/input v0.3.4/go.mod h1:JI8RcvdZWQIhn09VzeK3hdp4lTz7+yhiEdpEQtZN+2c=
89+
github.com/charmbracelet/x/input v0.3.5-0.20250509021451-13796e822d86 h1:BxAEmOBIDajkgao3EsbBxKQCYvgYPGdT62WASLvtf4Y=
90+
github.com/charmbracelet/x/input v0.3.5-0.20250509021451-13796e822d86/go.mod h1:62Rp/6EtTxoeJDSdtpA3tJp3y3ZRpsiekBSje+K8htA=
9191
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
9292
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
93-
github.com/charmbracelet/x/windows v0.2.0 h1:ilXA1GJjTNkgOm94CLPeSz7rar54jtFatdmoiONPuEw=
94-
github.com/charmbracelet/x/windows v0.2.0/go.mod h1:ZibNFR49ZFqCXgP76sYanisxRyC+EYrBE7TTknD8s1s=
93+
github.com/charmbracelet/x/windows v0.2.1 h1:3x7vnbpQrjpuq/4L+I4gNsG5htYoCiA5oe9hLjAij5I=
94+
github.com/charmbracelet/x/windows v0.2.1/go.mod h1:ptZp16h40gDYqs5TSawSVW+yiLB13j4kSMA0lSCHL0M=
9595
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
9696
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
9797
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=

internal/tui/components/anim/anim.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ func (a *anim) updateChars(chars *[]cyclingChar) {
239239
}
240240

241241
// View renders the animation.
242-
func (a anim) View() string {
242+
func (a anim) View() tea.View {
243243
t := theme.CurrentTheme()
244244
var b strings.Builder
245245

@@ -259,10 +259,10 @@ func (a anim) View() string {
259259
textStyle.Render(string(c.currentValue)),
260260
)
261261
}
262-
return b.String() + textStyle.Render(a.ellipsis.View())
262+
return tea.NewView(b.String() + textStyle.Render(a.ellipsis.View()))
263263
}
264264

265-
return b.String()
265+
return tea.NewView(b.String())
266266
}
267267

268268
func makeGradientRamp(length int) []color.Color {

internal/tui/components/chat/editor.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
type editorCmp struct {
2727
width int
2828
height int
29+
x, y int
2930
app *app.App
3031
session session.Session
3132
textarea textarea.Model
@@ -116,7 +117,7 @@ func (m *editorCmp) openEditor() tea.Cmd {
116117
}
117118

118119
func (m *editorCmp) Init() tea.Cmd {
119-
return textarea.Blink
120+
return nil
120121
}
121122

122123
func (m *editorCmp) send() tea.Cmd {
@@ -212,7 +213,7 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
212213
return m, cmd
213214
}
214215

215-
func (m *editorCmp) View() string {
216+
func (m *editorCmp) View() tea.View {
216217
t := theme.CurrentTheme()
217218

218219
// Style the prompt with theme colors
@@ -221,16 +222,23 @@ func (m *editorCmp) View() string {
221222
Bold(true).
222223
Foreground(t.Primary())
223224

225+
cursor := m.textarea.Cursor()
226+
cursor.X = m.textarea.Cursor().X + m.x + 2
227+
cursor.Y = m.textarea.Cursor().Y + m.y + 1
224228
if len(m.attachments) == 0 {
225-
return lipgloss.JoinHorizontal(lipgloss.Top, style.Render(">"), m.textarea.View())
229+
view := tea.NewView(lipgloss.JoinHorizontal(lipgloss.Top, style.Render(">"), m.textarea.View()))
230+
view.SetCursor(cursor)
231+
return view
226232
}
227233
m.textarea.SetHeight(m.height - 1)
228-
return lipgloss.JoinVertical(lipgloss.Top,
234+
view := tea.NewView(lipgloss.JoinVertical(lipgloss.Top,
229235
m.attachmentsContent(),
230236
lipgloss.JoinHorizontal(lipgloss.Top, style.Render(">"),
231237
m.textarea.View(),
232238
),
233-
)
239+
))
240+
view.SetCursor(cursor)
241+
return view
234242
}
235243

236244
func (m *editorCmp) SetSize(width, height int) tea.Cmd {
@@ -275,6 +283,12 @@ func (m *editorCmp) BindingKeys() []key.Binding {
275283
return bindings
276284
}
277285

286+
func (m *editorCmp) SetPosition(x, y int) tea.Cmd {
287+
m.x = x
288+
m.y = y
289+
return nil
290+
}
291+
278292
func CreateTextArea(existing *textarea.Model) textarea.Model {
279293
t := theme.CurrentTheme()
280294
bgColor := t.Background()
@@ -297,11 +311,12 @@ func CreateTextArea(existing *textarea.Model) textarea.Model {
297311

298312
s.Focused = f
299313
s.Blurred = b
300-
ta.Styles = s
314+
ta.SetStyles(s)
301315

302316
ta.Prompt = " "
303317
ta.ShowLineNumbers = false
304318
ta.CharLimit = -1
319+
ta.SetVirtualCursor(false)
305320

306321
if existing != nil {
307322
ta.SetValue(existing.Value())

internal/tui/components/chat/list.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,11 @@ func (m *messageListCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
104104
}
105105

106106
// View renders the message list or an initial screen if empty.
107-
func (m *messageListCmp) View() string {
107+
func (m *messageListCmp) View() tea.View {
108108
if len(m.listCmp.Items()) == 0 {
109-
return initialScreen()
109+
return tea.NewView(initialScreen())
110110
}
111-
return lipgloss.JoinVertical(lipgloss.Left, m.listCmp.View())
111+
return tea.NewView(lipgloss.JoinVertical(lipgloss.Left, m.listCmp.View().String()))
112112
}
113113

114114
// handleChildSession handles messages from child sessions (agent tools).

internal/tui/components/chat/messages/messages.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,20 +94,20 @@ func (m *messageCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
9494

9595
// View renders the message component based on its current state.
9696
// Returns different views for spinning, user, and assistant messages.
97-
func (m *messageCmp) View() string {
97+
func (m *messageCmp) View() tea.View {
9898
if m.spinning {
99-
return m.style().PaddingLeft(1).Render(m.anim.View())
99+
return tea.NewView(m.style().PaddingLeft(1).Render(m.anim.View().String()))
100100
}
101101
if m.message.ID != "" {
102102
// this is a user or assistant message
103103
switch m.message.Role {
104104
case message.User:
105-
return m.renderUserMessage()
105+
return tea.NewView(m.renderUserMessage())
106106
default:
107-
return m.renderAssistantMessage()
107+
return tea.NewView(m.renderAssistantMessage())
108108
}
109109
}
110-
return "Unknown Message"
110+
return tea.NewView(m.style().Render("No message content"))
111111
}
112112

113113
// GetMessage returns the underlying message data

internal/tui/components/chat/messages/renderer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ func (tr agentRenderer) Render(v *toolCallCmp) string {
507507
}
508508
if v.result.ToolCallID == "" {
509509
v.spinning = true
510-
parts = append(parts, v.anim.View())
510+
parts = append(parts, v.anim.View().String())
511511
} else {
512512
v.spinning = false
513513
}

internal/tui/components/chat/messages/tool.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,22 +134,22 @@ func (m *toolCallCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
134134

135135
// View renders the tool call component based on its current state.
136136
// Shows either a pending animation or the tool-specific rendered result.
137-
func (m *toolCallCmp) View() string {
137+
func (m *toolCallCmp) View() tea.View {
138138
box := m.style()
139139

140140
if !m.call.Finished && !m.cancelled {
141141
if m.isNested {
142-
return box.Render(m.renderPending())
142+
return tea.NewView(box.Render(m.renderPending()))
143143
}
144-
return box.PaddingLeft(1).Render(m.renderPending())
144+
return tea.NewView(box.PaddingLeft(1).Render(m.renderPending()))
145145
}
146146

147147
r := registry.lookup(m.call.Name)
148148

149149
if m.isNested {
150-
return box.Render(r.Render(m))
150+
return tea.NewView(box.Render(r.Render(m)))
151151
}
152-
return box.PaddingLeft(1).Render(r.Render(m))
152+
return tea.NewView(box.PaddingLeft(1).Render(r.Render(m)))
153153
}
154154

155155
// State management methods

internal/tui/components/chat/sidebar.go

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -82,26 +82,28 @@ func (m *sidebarCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
8282
return m, nil
8383
}
8484

85-
func (m *sidebarCmp) View() string {
85+
func (m *sidebarCmp) View() tea.View {
8686
baseStyle := styles.BaseStyle()
8787

88-
return baseStyle.
89-
Width(m.width).
90-
PaddingLeft(4).
91-
PaddingRight(2).
92-
Height(m.height - 1).
93-
Render(
94-
lipgloss.JoinVertical(
95-
lipgloss.Top,
96-
header(),
97-
" ",
98-
m.sessionSection(),
99-
" ",
100-
lspsConfigured(),
101-
" ",
102-
m.modifiedFiles(),
88+
return tea.NewView(
89+
baseStyle.
90+
Width(m.width).
91+
PaddingLeft(4).
92+
PaddingRight(2).
93+
Height(m.height - 1).
94+
Render(
95+
lipgloss.JoinVertical(
96+
lipgloss.Top,
97+
header(),
98+
" ",
99+
m.sessionSection(),
100+
" ",
101+
lspsConfigured(),
102+
" ",
103+
m.modifiedFiles(),
104+
),
103105
),
104-
)
106+
)
105107
}
106108

107109
func (m *sidebarCmp) sessionSection() string {

0 commit comments

Comments
 (0)