package chat import ( "fmt" "strings" "git.mlow.ca/mlow/lmcli/pkg/api" "git.mlow.ca/mlow/lmcli/pkg/tui/model" "git.mlow.ca/mlow/lmcli/pkg/tui/shared" tuiutil "git.mlow.ca/mlow/lmcli/pkg/tui/util" tea "github.com/charmbracelet/bubbletea" ) func (m *Model) handleInput(msg tea.KeyMsg) tea.Cmd { switch m.focus { case focusInput: cmd := m.handleInputKey(msg) if cmd != nil { return cmd } case focusMessages: cmd := m.handleMessagesKey(msg) if cmd != nil { return cmd } } switch msg.String() { case "esc": if m.state == pendingResponse { m.stopSignal <- struct{}{} return shared.KeyHandled(msg) } return func() tea.Msg { return shared.MsgViewChange(shared.ViewConversations) } case "ctrl+c": if m.state == pendingResponse { m.stopSignal <- struct{}{} return shared.KeyHandled(msg) } case "ctrl+p": m.persistence = !m.persistence return shared.KeyHandled(msg) case "ctrl+t": m.showToolResults = !m.showToolResults m.rebuildMessageCache() m.updateContent() return shared.KeyHandled(msg) case "ctrl+w": m.wrap = !m.wrap m.rebuildMessageCache() m.updateContent() return shared.KeyHandled(msg) case "ctrl+n": m.App.ClearConversation() m.rebuildMessageCache() m.updateContent() return shared.KeyHandled(msg) } return nil } // handleMessagesKey handles input when the messages pane is focused func (m *Model) handleMessagesKey(msg tea.KeyMsg) tea.Cmd { switch msg.String() { case "tab", "enter": m.focus = focusInput m.updateContent() m.input.Focus() return shared.KeyHandled(msg) case "e": if m.selectedMessage < len(m.App.Messages) { m.editorTarget = selectedMessage return tuiutil.OpenTempfileEditor( "message.*.md", m.App.Messages[m.selectedMessage].Content, "# Edit the message below\n", ) } return nil case "ctrl+k": if m.selectedMessage > 0 && len(m.App.Messages) == len(m.messageOffsets) { m.selectedMessage-- m.updateContent() offset := m.messageOffsets[m.selectedMessage] tuiutil.ScrollIntoView(&m.content, offset, m.content.Height/2) } return shared.KeyHandled(msg) case "ctrl+j": if m.selectedMessage < len(m.App.Messages)-1 && len(m.App.Messages) == len(m.messageOffsets) { m.selectedMessage++ m.updateContent() offset := m.messageOffsets[m.selectedMessage] tuiutil.ScrollIntoView(&m.content, offset, m.content.Height/2) } return shared.KeyHandled(msg) case "ctrl+h", "ctrl+l": dir := model.CyclePrev if msg.String() == "ctrl+l" { dir = model.CycleNext } var cmd tea.Cmd if m.selectedMessage == 0 { cmd = m.cycleSelectedRoot(m.App.Conversation, dir) } else if m.selectedMessage > 0 { cmd = m.cycleSelectedReply(&m.App.Messages[m.selectedMessage-1], dir) } return cmd case "ctrl+r": // resubmit the conversation with all messages up until and including the selected message if m.state == idle && m.selectedMessage < len(m.App.Messages) { m.App.Messages = m.App.Messages[:m.selectedMessage+1] m.messageCache = m.messageCache[:m.selectedMessage+1] cmd := m.promptLLM() m.updateContent() m.content.GotoBottom() return cmd } } return nil } // handleInputKey handles input when the input textarea is focused func (m *Model) handleInputKey(msg tea.KeyMsg) tea.Cmd { switch msg.String() { case "esc": m.focus = focusMessages if len(m.App.Messages) > 0 { if m.selectedMessage < 0 || m.selectedMessage >= len(m.App.Messages) { m.selectedMessage = len(m.App.Messages) - 1 } offset := m.messageOffsets[m.selectedMessage] tuiutil.ScrollIntoView(&m.content, offset, m.content.Height/2) } m.updateContent() m.input.Blur() return shared.KeyHandled(msg) case "ctrl+s": // TODO: call a "handleSend" function which returns a tea.Cmd if m.state != idle { return nil } input := strings.TrimSpace(m.input.Value()) if input == "" { return shared.KeyHandled(msg) } if len(m.App.Messages) > 0 && m.App.Messages[len(m.App.Messages)-1].Role == api.MessageRoleUser { return shared.WrapError(fmt.Errorf("Can't reply to a user message")) } m.addMessage(api.Message{ Role: api.MessageRoleUser, Content: input, }) m.input.SetValue("") var cmds []tea.Cmd if m.persistence { cmds = append(cmds, m.persistConversation()) } cmds = append(cmds, m.promptLLM()) m.updateContent() m.content.GotoBottom() return tea.Batch(cmds...) case "ctrl+e": cmd := tuiutil.OpenTempfileEditor("message.*.md", m.input.Value(), "# Edit your input below\n") m.editorTarget = input return cmd } return nil }