tui: basic conversation selection and navigation
This commit is contained in:
parent
0e68e22efa
commit
7463b7502c
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user