tui: removed confirm before send, dynamic footer

footer now rendered based on model data, instead of being set to a fixed
string
This commit is contained in:
Matt Low 2024-03-12 17:10:40 +00:00
parent 758f74aba5
commit d8c8262890

View File

@ -30,13 +30,12 @@ type model struct {
err error err error
// ui state // ui state
isWaiting bool isWaiting bool
confirmPrompt bool status string // a general status message
// ui elements // ui elements
content viewport.Model content viewport.Model
input textarea.Model input textarea.Model
footer string
} }
type message struct { type message struct {
@ -103,7 +102,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.messages[i].Content += chunk m.messages[i].Content += chunk
default: default:
m.messages = append(m.messages, models.Message{ m.messages = append(m.messages, models.Message{
Role: models.MessageRoleAssistant, Role: models.MessageRoleAssistant,
Content: chunk, Content: chunk,
}) })
} }
@ -112,7 +111,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmd = waitForChunk(m.replyChan) // wait for the next chunk cmd = waitForChunk(m.replyChan) // wait for the next chunk
case msgResponseEnd: case msgResponseEnd:
m.isWaiting = false m.isWaiting = false
m.footer = "Press Ctrl+S to send, Ctrl+C or 'q' to quit" m.status = "Press ctrl+s to send"
} }
if cmd != nil { if cmd != nil {
@ -147,9 +146,6 @@ func initialModel(ctx *lmcli.Context, convShortname string) model {
convShortname: convShortname, convShortname: convShortname,
replyChan: make(chan string), replyChan: make(chan string),
isWaiting: false,
confirmPrompt: false,
} }
m.content = viewport.New(0, 0) m.content = viewport.New(0, 0)
@ -163,7 +159,8 @@ func initialModel(ctx *lmcli.Context, convShortname string) model {
m.updateContent() m.updateContent()
m.footer = "Press Ctrl+S to send, Ctrl+C or 'q' to quit" m.isWaiting = false
m.status = "Press ctrl+s to send"
return m return m
} }
@ -173,38 +170,25 @@ func (m *model) handleKeyMsg(msg tea.KeyMsg) tea.Cmd {
case "ctrl+c", "q": case "ctrl+c", "q":
return tea.Quit return tea.Quit
case "ctrl+s": case "ctrl+s":
if !m.isWaiting && !m.confirmPrompt { userInput := strings.TrimSpace(m.input.Value())
m.confirmPrompt = true if strings.TrimSpace(userInput) == "" {
m.footer = "Press 'y' to confirm sending the message, 'n' to cancel"
return nil
}
case "y":
if m.confirmPrompt {
userInput := m.input.Value()
m.input.SetValue("")
m.messages = append(m.messages, models.Message{
Role: models.MessageRoleUser,
Content: userInput,
})
m.updateContent()
m.content.GotoBottom()
m.isWaiting = true
m.confirmPrompt = false
m.footer = "Waiting for response... (Press 's' to stop)"
return m.promptLLM()
}
case "n":
if m.confirmPrompt {
m.confirmPrompt = false
m.footer = "Press Ctrl+S to send, Ctrl+C or 'q' to quit"
return nil return nil
} }
m.input.SetValue("")
m.messages = append(m.messages, models.Message{
Role: models.MessageRoleUser,
Content: userInput,
})
m.updateContent()
m.content.GotoBottom()
m.isWaiting = true
m.status = "Waiting for response... (Press 's' to stop)"
return m.promptLLM()
case "s": case "s":
if m.isWaiting { // TOOD: stop response
m.isWaiting = false break
m.footer = "Response generation stopped. Press Ctrl+S to send, Ctrl+C or 'q' to quit"
return nil
}
} }
return nil return nil
@ -284,7 +268,7 @@ func (m *model) updateContent() {
style = assistantStyle style = assistantStyle
} }
sb.WriteString(fmt.Sprintf("%s:\n\n", style.Render(string(message.Role)))) sb.WriteString(fmt.Sprintf("%s:\n\n", style.Render(string(message.Role.FriendlyRole()))))
highlighted, _ := m.ctx.Chroma.HighlightS(message.Content) highlighted, _ := m.ctx.Chroma.HighlightS(message.Content)
sb.WriteString(contentStyle.Width(m.content.Width - 5).Render(highlighted)) sb.WriteString(contentStyle.Width(m.content.Width - 5).Render(highlighted))
@ -306,10 +290,20 @@ func (m model) inputView() string {
} }
func (m model) footerView() string { func (m model) footerView() string {
return footerStyle. left := m.status
Width(m.content.Width). right := fmt.Sprintf("Model: %s", *m.ctx.Config.Defaults.Model)
Align(lipgloss.Center).
Render(m.footer) totalWidth := lipgloss.Width(left + right)
var padding string
if m.content.Width-totalWidth > 0 {
padding = strings.Repeat(" ", m.content.Width-totalWidth)
} else {
padding = ""
}
footer := lipgloss.JoinHorizontal(lipgloss.Center, left, padding, right)
return footerStyle.Width(m.content.Width).Render(footer)
} }
func Launch(ctx *lmcli.Context, convShortname string) error { func Launch(ctx *lmcli.Context, convShortname string) error {