Fixes to message/conversation handling in tui chat view
This set of changes fixes root/child message cycling and ensures all database operations happen within a `tea.Cmd`
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -22,13 +21,13 @@ func (m *Model) HandleResize(width, height int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) waitForReply() tea.Cmd {
|
||||
func (m *Model) waitForResponse() tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
return msgAssistantReply(<-m.replyChan)
|
||||
return msgResponse(<-m.replyChan)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Model) waitForChunk() tea.Cmd {
|
||||
func (m *Model) waitForResponseChunk() tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
return msgResponseChunk(<-m.replyChunkChan)
|
||||
}
|
||||
@@ -37,46 +36,59 @@ func (m *Model) waitForChunk() tea.Cmd {
|
||||
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
||||
var cmds []tea.Cmd
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
m.HandleResize(msg.Width, msg.Height)
|
||||
case shared.MsgViewEnter:
|
||||
// wake up spinners and cursors
|
||||
cmds = append(cmds, cursor.Blink, m.spinner.Tick)
|
||||
|
||||
if m.State.Values.ConvShortname != "" && m.conversation.ShortName.String != m.State.Values.ConvShortname {
|
||||
if m.State.Values.ConvShortname != "" {
|
||||
// (re)load conversation contents
|
||||
cmds = append(cmds, m.loadConversation(m.State.Values.ConvShortname))
|
||||
|
||||
if m.conversation.ShortName.String != m.State.Values.ConvShortname {
|
||||
// clear existing messages if we're loading a new conversation
|
||||
m.messages = []models.Message{}
|
||||
m.selectedMessage = 0
|
||||
}
|
||||
}
|
||||
|
||||
m.rebuildMessageCache()
|
||||
m.updateContent()
|
||||
case tea.WindowSizeMsg:
|
||||
m.HandleResize(msg.Width, msg.Height)
|
||||
case tuiutil.MsgTempfileEditorClosed:
|
||||
contents := string(msg)
|
||||
switch m.editorTarget {
|
||||
case input:
|
||||
m.input.SetValue(contents)
|
||||
case selectedMessage:
|
||||
m.setMessageContents(m.selectedMessage, contents)
|
||||
if m.persistence && m.messages[m.selectedMessage].ID > 0 {
|
||||
// update persisted message
|
||||
err := m.State.Ctx.Store.UpdateMessage(&m.messages[m.selectedMessage])
|
||||
if err != nil {
|
||||
cmds = append(cmds, shared.WrapError(fmt.Errorf("Could not save edited message: %v", err)))
|
||||
toEdit := m.messages[m.selectedMessage]
|
||||
if toEdit.Content != contents {
|
||||
toEdit.Content = contents
|
||||
m.setMessage(m.selectedMessage, toEdit)
|
||||
if m.persistence && toEdit.ID > 0 {
|
||||
// create clone of message with its new contents
|
||||
cmds = append(cmds, m.cloneMessage(toEdit, true))
|
||||
}
|
||||
}
|
||||
m.updateContent()
|
||||
}
|
||||
case msgConversationLoaded:
|
||||
m.conversation = (*models.Conversation)(msg)
|
||||
m.rootMessages, _ = m.State.Ctx.Store.RootMessages(m.conversation.ID)
|
||||
cmds = append(cmds, m.loadMessages(m.conversation))
|
||||
m.conversation = msg.conversation
|
||||
m.rootMessages = msg.rootMessages
|
||||
m.selectedMessage = -1
|
||||
if len(m.rootMessages) > 0 {
|
||||
cmds = append(cmds, m.loadConversationMessages())
|
||||
}
|
||||
case msgMessagesLoaded:
|
||||
m.selectedMessage = len(msg) - 1
|
||||
m.messages = msg
|
||||
if m.selectedMessage == -1 {
|
||||
m.selectedMessage = len(msg) - 1
|
||||
} else {
|
||||
m.selectedMessage = min(m.selectedMessage, len(m.messages))
|
||||
}
|
||||
m.rebuildMessageCache()
|
||||
m.updateContent()
|
||||
m.content.GotoBottom()
|
||||
case msgResponseChunk:
|
||||
cmds = append(cmds, m.waitForChunk()) // wait for the next chunk
|
||||
cmds = append(cmds, m.waitForResponseChunk()) // wait for the next chunk
|
||||
|
||||
chunk := string(msg)
|
||||
if chunk == "" {
|
||||
@@ -102,8 +114,8 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
||||
|
||||
m.tokenCount++
|
||||
m.elapsed = time.Now().Sub(m.startTime)
|
||||
case msgAssistantReply:
|
||||
cmds = append(cmds, m.waitForReply()) // wait for the next reply
|
||||
case msgResponse:
|
||||
cmds = append(cmds, m.waitForResponse()) // wait for the next response
|
||||
|
||||
reply := models.Message(msg)
|
||||
reply.Content = strings.TrimSpace(reply.Content)
|
||||
@@ -121,10 +133,7 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
if m.persistence {
|
||||
err := m.persistConversation()
|
||||
if err != nil {
|
||||
cmds = append(cmds, shared.WrapError(err))
|
||||
}
|
||||
cmds = append(cmds, m.persistConversation())
|
||||
}
|
||||
|
||||
if m.conversation.Title == "" {
|
||||
@@ -146,20 +155,30 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
||||
m.status = "Press ctrl+s to send"
|
||||
m.State.Err = error(msg)
|
||||
m.updateContent()
|
||||
case msgConversationTitleChanged:
|
||||
case msgConversationTitleGenerated:
|
||||
title := string(msg)
|
||||
m.conversation.Title = title
|
||||
if m.persistence {
|
||||
err := m.State.Ctx.Store.UpdateConversation(m.conversation)
|
||||
if err != nil {
|
||||
cmds = append(cmds, shared.WrapError(err))
|
||||
}
|
||||
cmds = append(cmds, m.updateConversationTitle(m.conversation))
|
||||
}
|
||||
case cursor.BlinkMsg:
|
||||
if m.waitingForReply {
|
||||
// ensure we show updated "wait for response" cursor blink state
|
||||
// ensure we show the updated "wait for response" cursor blink state
|
||||
m.updateContent()
|
||||
}
|
||||
case msgConversationPersisted:
|
||||
m.conversation = msg.conversation
|
||||
m.messages = msg.messages
|
||||
m.rebuildMessageCache()
|
||||
m.updateContent()
|
||||
case msgMessageCloned:
|
||||
if msg.Parent == nil {
|
||||
m.conversation = &msg.Conversation
|
||||
m.rootMessages = append(m.rootMessages, *msg)
|
||||
}
|
||||
cmds = append(cmds, m.loadConversationMessages())
|
||||
case msgSelectedRootCycled, msgSelectedReplyCycled, msgMessageUpdated:
|
||||
cmds = append(cmds, m.loadConversationMessages())
|
||||
}
|
||||
|
||||
var cmd tea.Cmd
|
||||
@@ -218,12 +237,12 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
||||
// move cursor up until content reaches the bottom of the viewport
|
||||
m.input.CursorUp()
|
||||
}
|
||||
m.input, cmd = m.input.Update(nil)
|
||||
m.input, _ = m.input.Update(nil)
|
||||
for i := 0; i < dist; i++ {
|
||||
// move cursor back down to its previous position
|
||||
m.input.CursorDown()
|
||||
}
|
||||
m.input, cmd = m.input.Update(nil)
|
||||
m.input, _ = m.input.Update(nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user