tui: basic conversation selection and navigation

This commit is contained in:
Matt Low 2024-04-01 22:44:29 +00:00
parent 0e68e22efa
commit 7463b7502c
3 changed files with 51 additions and 16 deletions

View File

@ -255,6 +255,7 @@ func (m chatModel) Update(msg tea.Msg) (chatModel, tea.Cmd) {
case msgMessagesLoaded: case msgMessagesLoaded:
m.setMessages(msg) m.setMessages(msg)
m.updateContent() m.updateContent()
m.content.GotoBottom()
case msgResponseChunk: case msgResponseChunk:
chunk := string(msg) chunk := string(msg)
last := len(m.messages) - 1 last := len(m.messages) - 1

View File

@ -21,14 +21,17 @@ type loadedConversation struct {
type ( type (
// sent when conversation list is loaded // sent when conversation list is loaded
msgConversationsLoaded ([]loadedConversation) msgConversationsLoaded ([]loadedConversation)
// sent when each coversation's 'last reply' is loaded // sent when a conversation is selected
msgConversationSelected models.Conversation
) )
type conversationsModel struct { type conversationsModel struct {
basemodel basemodel
conversations []loadedConversation conversations []loadedConversation
content viewport.Model cursor int // index of the currently selected message message
content viewport.Model
} }
func newConversationsModel(tui *model) conversationsModel { func newConversationsModel(tui *model) conversationsModel {
@ -48,10 +51,23 @@ func newConversationsModel(tui *model) conversationsModel {
func (m *conversationsModel) handleInput(msg tea.KeyMsg) (bool, tea.Cmd) { func (m *conversationsModel) handleInput(msg tea.KeyMsg) (bool, tea.Cmd) {
switch msg.String() { switch msg.String() {
case "enter": case "enter":
// how to notify chats model if len(m.conversations) > 0 && m.cursor < len(m.conversations) {
return true, func() tea.Msg { return true, func() tea.Msg {
return msgChangeState(stateChat) return msgConversationSelected(m.conversations[m.cursor].conv)
}
} }
case "j", "down":
if m.cursor < len(m.conversations)-1 {
m.cursor++
m.content.SetContent(m.renderConversationList())
}
return true, nil
case "k", "up":
if m.cursor > 0 {
m.cursor--
m.content.SetContent(m.renderConversationList())
}
return true, nil
case "n": case "n":
// new conversation // new conversation
case "d": case "d":
@ -80,8 +96,10 @@ func (m conversationsModel) Update(msg tea.Msg) (conversationsModel, tea.Cmd) {
switch msg := msg.(type) { switch msg := msg.(type) {
case msgChangeState: case msgChangeState:
cmds = append(cmds, m.loadConversations()) cmds = append(cmds, m.loadConversations())
m.content.SetContent(m.renderConversationList())
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
m.handleResize(msg.Width, msg.Height) m.handleResize(msg.Width, msg.Height)
m.content.SetContent(m.renderConversationList())
case msgConversationsLoaded: case msgConversationsLoaded:
m.conversations = msg m.conversations = msg
m.content.SetContent(m.renderConversationList()) m.content.SetContent(m.renderConversationList())
@ -175,17 +193,17 @@ func (m *conversationsModel) renderConversationList() string {
PaddingLeft(1). PaddingLeft(1).
Bold(true) Bold(true)
conversationHeadingStyle := lipgloss.NewStyle(). itemStyle := lipgloss.NewStyle().
MarginBottom(1). MarginBottom(1)
PaddingLeft(2)
ageStyle := lipgloss.NewStyle().Faint(true) ageStyle := lipgloss.NewStyle().Faint(true).SetString()
titleStyle := lipgloss.NewStyle().Bold(true) titleStyle := lipgloss.NewStyle().Bold(true)
untitledStyle := titleStyle.Copy().Italic(true).Faint(true).SetString("(untitled)") untitledStyle := lipgloss.NewStyle().Faint(true).Italic(true)
selectedStyle := lipgloss.NewStyle().Faint(true).Foreground(lipgloss.Color("6"))
var currentCategory string var currentCategory string
sb := &strings.Builder{} sb := &strings.Builder{}
for _, c := range m.conversations { for i, c := range m.conversations {
lastReplyAge := now.Sub(c.lastReply.CreatedAt) lastReplyAge := now.Sub(c.lastReply.CreatedAt)
var category string var category string
@ -196,21 +214,32 @@ func (m *conversationsModel) renderConversationList() string {
} }
} }
// print the category
if category != currentCategory { if category != currentCategory {
currentCategory = category currentCategory = category
fmt.Fprintf(sb, "%s\n", categoryStyle.Render(currentCategory)) fmt.Fprintf(sb, "%s\n", categoryStyle.Render(currentCategory))
} }
tstyle := titleStyle tStyle := titleStyle.Copy()
padding := " "
if c.conv.Title == "" { if c.conv.Title == "" {
tstyle = untitledStyle tStyle = tStyle.Inherit(untitledStyle).SetString("(untitled)")
} }
if i == m.cursor {
tStyle = tStyle.Inherit(selectedStyle)
}
title := tStyle.Width(m.width - 3).PaddingLeft(2).Render(c.conv.Title)
if i == m.cursor {
title = ">" + title[1:]
}
heading := fmt.Sprintf( heading := fmt.Sprintf(
"%s\n%s", "%s\n%s",
tstyle.Render(c.conv.Title), title,
ageStyle.Render(util.HumanTimeElapsedSince(lastReplyAge)), padding + ageStyle.Render(util.HumanTimeElapsedSince(lastReplyAge)),
) )
sb.WriteString(conversationHeadingStyle.Render(heading)) sb.WriteString(itemStyle.Render(heading))
sb.WriteRune('\n') sb.WriteRune('\n')
} }
return sb.String() return sb.String()

View File

@ -132,6 +132,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.conversations.handleResize(m.width, m.height) m.conversations.handleResize(m.width, m.height)
} }
m.state = state(msg) m.state = state(msg)
case msgConversationSelected:
m.opts.convShortname = msg.ShortName.String
cmds = append(cmds, func() tea.Msg {
return msgChangeState(stateChat)
})
case tea.WindowSizeMsg: case tea.WindowSizeMsg:
m.width, m.height = msg.Width, msg.Height m.width, m.height = msg.Width, msg.Height
case msgError: case msgError: