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:
m.setMessages(msg)
m.updateContent()
m.content.GotoBottom()
case msgResponseChunk:
chunk := string(msg)
last := len(m.messages) - 1

View File

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