88 "github.com/charmbracelet/lipgloss/v2"
99 "github.com/opencode-ai/opencode/internal/app"
1010 "github.com/opencode-ai/opencode/internal/message"
11+ "github.com/opencode-ai/opencode/internal/pubsub"
1112 "github.com/opencode-ai/opencode/internal/session"
1213 "github.com/opencode-ai/opencode/internal/tui/components/chat/messages"
1314 "github.com/opencode-ai/opencode/internal/tui/components/core/list"
@@ -25,8 +26,9 @@ type messageListCmp struct {
2526 app * app.App
2627 width , height int
2728 session session.Session
28- messages []util.Model
2929 listCmp list.ListModel
30+
31+ lastUserMessageTime int64
3032}
3133
3234func NewMessagesListCmp (app * app.App ) MessageListCmp {
@@ -54,6 +56,12 @@ func (m *messageListCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
5456 return m , cmd
5557 }
5658 return m , nil
59+ case SessionClearedMsg :
60+ m .session = session.Session {}
61+ return m , m .listCmp .SetItems ([]util.Model {})
62+
63+ case pubsub.Event [message.Message ]:
64+ return m , m .handleMessageEvent (msg )
5765 default :
5866 var cmds []tea.Cmd
5967 u , cmd := m .listCmp .Update (msg )
@@ -64,19 +72,91 @@ func (m *messageListCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
6472}
6573
6674func (m * messageListCmp ) View () string {
75+ if len (m .listCmp .Items ()) == 0 {
76+ return initialScreen ()
77+ }
6778 return lipgloss .JoinVertical (lipgloss .Left , m .listCmp .View ())
6879}
6980
70- // GetSize implements MessageListCmp.
71- func (m * messageListCmp ) GetSize () (int , int ) {
72- return m .width , m .height
81+ func (m * messageListCmp ) handleChildSession (event pubsub.Event [message.Message ]) {
82+ // TODO: update the agent tool message with the changes
7383}
7484
75- // SetSize implements MessageListCmp.
76- func (m * messageListCmp ) SetSize (width int , height int ) tea.Cmd {
77- m .width = width
78- m .height = height - 1
79- return m .listCmp .SetSize (width , height - 1 )
85+ func (m * messageListCmp ) handleMessageEvent (event pubsub.Event [message.Message ]) tea.Cmd {
86+ switch event .Type {
87+ case pubsub .CreatedEvent :
88+ if event .Payload .SessionID != m .session .ID {
89+ m .handleChildSession (event )
90+ }
91+ messageExists := false
92+ // more likely to be at the end of the list
93+ items := m .listCmp .Items ()
94+ for i := len (items ) - 1 ; i >= 0 ; i -- {
95+ msg := items [i ].(messages.MessageCmp )
96+ if msg .GetMessage ().ID == event .Payload .ID {
97+ messageExists = true
98+ break
99+ }
100+ }
101+ if messageExists {
102+ return nil
103+ }
104+ switch event .Payload .Role {
105+ case message .User :
106+ return m .handleNewUserMessage (event .Payload )
107+ case message .Assistant :
108+ return m .handleNewAssistantMessage (event .Payload )
109+ }
110+ // TODO: handle tools
111+ case pubsub .UpdatedEvent :
112+ return m .handleUpdateAssistantMessage (event .Payload )
113+ }
114+ return nil
115+ }
116+
117+ func (m * messageListCmp ) handleNewUserMessage (msg message.Message ) tea.Cmd {
118+ m .lastUserMessageTime = msg .CreatedAt
119+ return m .listCmp .AppendItem (messages .NewMessageCmp (msg ))
120+ }
121+
122+ func (m * messageListCmp ) handleUpdateAssistantMessage (msg message.Message ) tea.Cmd {
123+ // Simple update the content
124+ items := m .listCmp .Items ()
125+ lastItem := items [len (items )- 1 ].(messages.MessageCmp )
126+ // TODO:handle tool calls
127+ if lastItem .GetMessage ().ID != msg .ID {
128+ return nil
129+ }
130+ // for now just updet the last message
131+ if len (msg .ToolCalls ()) == 0 || msg .Content ().Text != "" || msg .IsThinking () {
132+ m .listCmp .UpdateItem (
133+ len (items )- 1 ,
134+ messages .NewMessageCmp (
135+ msg ,
136+ messages .WithLastUserMessageTime (time .Unix (m .lastUserMessageTime , 0 )),
137+ ),
138+ )
139+ }
140+ return nil
141+ }
142+
143+ func (m * messageListCmp ) handleNewAssistantMessage (msg message.Message ) tea.Cmd {
144+ var cmds []tea.Cmd
145+ // Only add assistant messages if they don't have tool calls or there is some content
146+ if len (msg .ToolCalls ()) == 0 || msg .Content ().Text != "" || msg .IsThinking () {
147+ cmd := m .listCmp .AppendItem (
148+ messages .NewMessageCmp (
149+ msg ,
150+ messages .WithLastUserMessageTime (time .Unix (m .lastUserMessageTime , 0 )),
151+ ),
152+ )
153+ cmds = append (cmds , cmd )
154+ }
155+ for _ , tc := range msg .ToolCalls () {
156+ cmd := m .listCmp .AppendItem (messages .NewToolCallCmp (tc ))
157+ cmds = append (cmds , cmd )
158+ }
159+ return tea .Batch (cmds ... )
80160}
81161
82162func (m * messageListCmp ) SetSession (session session.Session ) tea.Cmd {
@@ -88,8 +168,8 @@ func (m *messageListCmp) SetSession(session session.Session) tea.Cmd {
88168 if err != nil {
89169 return util .ReportError (err )
90170 }
91- m . messages = make ([]util.Model , 0 )
92- lastUserMessageTime : = sessionMessages [0 ].CreatedAt
171+ uiMessages : = make ([]util.Model , 0 )
172+ m . lastUserMessageTime = sessionMessages [0 ].CreatedAt
93173 toolResultMap := make (map [string ]message.ToolResult )
94174 // first pass to get all tool results
95175 for _ , msg := range sessionMessages {
@@ -100,12 +180,18 @@ func (m *messageListCmp) SetSession(session session.Session) tea.Cmd {
100180 for _ , msg := range sessionMessages {
101181 switch msg .Role {
102182 case message .User :
103- lastUserMessageTime = msg .CreatedAt
104- m . messages = append (m . messages , messages .NewMessageCmp (msg ))
183+ m . lastUserMessageTime = msg .CreatedAt
184+ uiMessages = append (uiMessages , messages .NewMessageCmp (msg ))
105185 case message .Assistant :
106186 // Only add assistant messages if they don't have tool calls or there is some content
107187 if len (msg .ToolCalls ()) == 0 || msg .Content ().Text != "" || msg .IsThinking () {
108- m .messages = append (m .messages , messages .NewMessageCmp (msg , messages .WithLastUserMessageTime (time .Unix (lastUserMessageTime , 0 ))))
188+ uiMessages = append (
189+ uiMessages ,
190+ messages .NewMessageCmp (
191+ msg ,
192+ messages .WithLastUserMessageTime (time .Unix (m .lastUserMessageTime , 0 )),
193+ ),
194+ )
109195 }
110196 for _ , tc := range msg .ToolCalls () {
111197 options := []messages.ToolCallOption {}
@@ -115,10 +201,21 @@ func (m *messageListCmp) SetSession(session session.Session) tea.Cmd {
115201 if msg .FinishPart ().Reason == message .FinishReasonCanceled {
116202 options = append (options , messages .WithToolCallCancelled ())
117203 }
118- m . messages = append (m . messages , messages .NewToolCallCmp (tc , options ... ))
204+ uiMessages = append (uiMessages , messages .NewToolCallCmp (tc , options ... ))
119205 }
120206 }
121207 }
122- m .listCmp .SetItems (m .messages )
123- return nil
208+ return m .listCmp .SetItems (uiMessages )
209+ }
210+
211+ // GetSize implements MessageListCmp.
212+ func (m * messageListCmp ) GetSize () (int , int ) {
213+ return m .width , m .height
214+ }
215+
216+ // SetSize implements MessageListCmp.
217+ func (m * messageListCmp ) SetSize (width int , height int ) tea.Cmd {
218+ m .width = width
219+ m .height = height - 1
220+ return m .listCmp .SetSize (width , height - 1 )
124221}
0 commit comments