More monior TUI refactor/cleanup
`tui/tui.go` is no longer responsible for passing window resize updates to all views, instead we request a new window size message to be sent at the same time we enter the view, allowing the view to catch and handle it. Add `Initialized` to `tui/shared/View` model, now we only call `Init` on a view before entering it for the first time, rather than calling `Init` on all views when the application starts. Renames file, small cleanups
This commit is contained in:
parent
7c0bfefc65
commit
24b5cdbbf6
@ -16,7 +16,6 @@ type LoadedConversation struct {
|
|||||||
LastReply api.Message
|
LastReply api.Message
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppModel represents the application data model
|
|
||||||
type AppModel struct {
|
type AppModel struct {
|
||||||
Ctx *lmcli.Context
|
Ctx *lmcli.Context
|
||||||
Conversations []LoadedConversation
|
Conversations []LoadedConversation
|
||||||
@ -193,7 +192,6 @@ func (a *AppModel) PromptLLM(messages []api.Message, chatReplyChunks chan api.Ch
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function
|
|
||||||
func cycleSelectedMessage(selected *api.Message, choices []api.Message, dir MessageCycleDirection) (*api.Message, error) {
|
func cycleSelectedMessage(selected *api.Message, choices []api.Message, dir MessageCycleDirection) (*api.Message, error) {
|
||||||
currentIndex := -1
|
currentIndex := -1
|
||||||
for i, reply := range choices {
|
for i, reply := range choices {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Shared struct {
|
type Shared struct {
|
||||||
|
Initialized bool
|
||||||
Width int
|
Width int
|
||||||
Height int
|
Height int
|
||||||
Err error
|
Err error
|
||||||
@ -29,6 +30,12 @@ type (
|
|||||||
MsgError error
|
MsgError error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func ViewEnter() tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
return MsgViewEnter{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WrapError(err error) tea.Cmd {
|
func WrapError(err error) tea.Cmd {
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
return MsgError(err)
|
return MsgError(err)
|
||||||
|
@ -24,12 +24,12 @@ type LaunchOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
App *model.AppModel
|
App *model.AppModel
|
||||||
view shared.View
|
view shared.View
|
||||||
|
|
||||||
|
// views
|
||||||
chat chat.Model
|
chat chat.Model
|
||||||
conversations conversations.Model
|
conversations conversations.Model
|
||||||
Width int
|
|
||||||
Height int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initialModel(ctx *lmcli.Context, opts LaunchOptions) Model {
|
func initialModel(ctx *lmcli.Context, opts LaunchOptions) Model {
|
||||||
@ -50,8 +50,6 @@ func initialModel(ctx *lmcli.Context, opts LaunchOptions) Model {
|
|||||||
|
|
||||||
func (m Model) Init() tea.Cmd {
|
func (m Model) Init() tea.Cmd {
|
||||||
return tea.Batch(
|
return tea.Batch(
|
||||||
m.conversations.Init(),
|
|
||||||
m.chat.Init(),
|
|
||||||
func() tea.Msg {
|
func() tea.Msg {
|
||||||
return shared.MsgViewChange(m.view)
|
return shared.MsgViewChange(m.view)
|
||||||
},
|
},
|
||||||
@ -96,15 +94,23 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
case shared.MsgViewChange:
|
case shared.MsgViewChange:
|
||||||
m.view = shared.View(msg)
|
m.view = shared.View(msg)
|
||||||
|
|
||||||
|
var cmds []tea.Cmd
|
||||||
switch m.view {
|
switch m.view {
|
||||||
case shared.StateChat:
|
|
||||||
m.chat.HandleResize(m.Width, m.Height)
|
|
||||||
case shared.StateConversations:
|
case shared.StateConversations:
|
||||||
m.conversations.HandleResize(m.Width, m.Height)
|
if !m.conversations.Initialized {
|
||||||
|
cmds = append(cmds, m.conversations.Init())
|
||||||
|
m.conversations.Initialized = true
|
||||||
|
}
|
||||||
|
case shared.StateChat:
|
||||||
|
if !m.chat.Initialized {
|
||||||
|
cmds = append(cmds, m.chat.Init())
|
||||||
|
m.chat.Initialized = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return m, func() tea.Msg { return shared.MsgViewEnter(struct{}{}) }
|
cmds = append(cmds, tea.WindowSize(), shared.ViewEnter())
|
||||||
case tea.WindowSizeMsg:
|
|
||||||
m.Width, m.Height = msg.Width, msg.Height
|
return m, tea.Batch(cmds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
|
@ -9,39 +9,9 @@ import (
|
|||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *Model) setMessage(i int, msg api.Message) {
|
func (m *Model) waitForResponseChunk() tea.Cmd {
|
||||||
if i >= len(m.App.Messages) {
|
return func() tea.Msg {
|
||||||
panic("i out of range")
|
return msgChatResponseChunk(<-m.chatReplyChunks)
|
||||||
}
|
|
||||||
m.App.Messages[i] = msg
|
|
||||||
m.messageCache[i] = m.renderMessage(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Model) addMessage(msg api.Message) {
|
|
||||||
m.App.Messages = append(m.App.Messages, msg)
|
|
||||||
m.messageCache = append(m.messageCache, m.renderMessage(len(m.App.Messages)-1))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Model) setMessageContents(i int, content string) {
|
|
||||||
if i >= len(m.App.Messages) {
|
|
||||||
panic("i out of range")
|
|
||||||
}
|
|
||||||
m.App.Messages[i].Content = content
|
|
||||||
m.messageCache[i] = m.renderMessage(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Model) rebuildMessageCache() {
|
|
||||||
m.messageCache = make([]string, len(m.App.Messages))
|
|
||||||
for i := range m.App.Messages {
|
|
||||||
m.messageCache[i] = m.renderMessage(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Model) updateContent() {
|
|
||||||
atBottom := m.content.AtBottom()
|
|
||||||
m.content.SetContent(m.conversationMessagesView())
|
|
||||||
if atBottom {
|
|
||||||
m.content.GotoBottom()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +67,7 @@ func (m *Model) updateMessageContent(message *api.Message) tea.Cmd {
|
|||||||
|
|
||||||
func (m *Model) cycleSelectedRoot(conv *api.Conversation, dir model.MessageCycleDirection) tea.Cmd {
|
func (m *Model) cycleSelectedRoot(conv *api.Conversation, dir model.MessageCycleDirection) tea.Cmd {
|
||||||
if len(m.App.RootMessages) < 2 {
|
if len(m.App.RootMessages) < 2 {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,11 +124,9 @@ func (m *Model) promptLLM() tea.Cmd {
|
|||||||
|
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
resp, err := m.App.PromptLLM(m.App.Messages, m.chatReplyChunks, m.stopSignal)
|
resp, err := m.App.PromptLLM(m.App.Messages, m.chatReplyChunks, m.stopSignal)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return msgChatResponseError(err)
|
return msgChatResponseError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return msgChatResponse(resp)
|
return msgChatResponse(resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,19 +11,39 @@ import (
|
|||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *Model) HandleResize(width, height int) {
|
func (m *Model) setMessage(i int, msg api.Message) {
|
||||||
m.Width, m.Height = width, height
|
if i >= len(m.App.Messages) {
|
||||||
m.content.Width = width
|
panic("i out of range")
|
||||||
m.input.SetWidth(width - m.input.FocusedStyle.Base.GetHorizontalFrameSize())
|
}
|
||||||
if len(m.App.Messages) > 0 {
|
m.App.Messages[i] = msg
|
||||||
m.rebuildMessageCache()
|
m.messageCache[i] = m.renderMessage(i)
|
||||||
m.updateContent()
|
}
|
||||||
|
|
||||||
|
func (m *Model) addMessage(msg api.Message) {
|
||||||
|
m.App.Messages = append(m.App.Messages, msg)
|
||||||
|
m.messageCache = append(m.messageCache, m.renderMessage(len(m.App.Messages)-1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Model) setMessageContents(i int, content string) {
|
||||||
|
if i >= len(m.App.Messages) {
|
||||||
|
panic("i out of range")
|
||||||
|
}
|
||||||
|
m.App.Messages[i].Content = content
|
||||||
|
m.messageCache[i] = m.renderMessage(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Model) rebuildMessageCache() {
|
||||||
|
m.messageCache = make([]string, len(m.App.Messages))
|
||||||
|
for i := range m.App.Messages {
|
||||||
|
m.messageCache[i] = m.renderMessage(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) waitForResponseChunk() tea.Cmd {
|
func (m *Model) updateContent() {
|
||||||
return func() tea.Msg {
|
atBottom := m.content.AtBottom()
|
||||||
return msgChatResponseChunk(<-m.chatReplyChunks)
|
m.content.SetContent(m.conversationMessagesView())
|
||||||
|
if atBottom {
|
||||||
|
m.content.GotoBottom()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +51,13 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
|||||||
var cmds []tea.Cmd
|
var cmds []tea.Cmd
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
m.HandleResize(msg.Width, msg.Height)
|
m.Width, m.Height = msg.Width, msg.Height
|
||||||
|
m.content.Width = msg.Width
|
||||||
|
m.input.SetWidth(msg.Width - m.input.FocusedStyle.Base.GetHorizontalFrameSize())
|
||||||
|
if len(m.App.Messages) > 0 {
|
||||||
|
m.rebuildMessageCache()
|
||||||
|
m.updateContent()
|
||||||
|
}
|
||||||
case shared.MsgViewEnter:
|
case shared.MsgViewEnter:
|
||||||
// wake up spinners and cursors
|
// wake up spinners and cursors
|
||||||
cmds = append(cmds, cursor.Blink, m.spinner.Tick)
|
cmds = append(cmds, cursor.Blink, m.spinner.Tick)
|
||||||
|
@ -127,11 +127,6 @@ func (m Model) Init() tea.Cmd {
|
|||||||
return m.loadConversations()
|
return m.loadConversations()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Model) HandleResize(width, height int) {
|
|
||||||
m.Width, m.Height = width, height
|
|
||||||
m.content.Width = width
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
||||||
var cmds []tea.Cmd
|
var cmds []tea.Cmd
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
@ -139,7 +134,8 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
|
|||||||
cmds = append(cmds, m.loadConversations())
|
cmds = append(cmds, m.loadConversations())
|
||||||
m.content.SetContent(m.renderConversationList())
|
m.content.SetContent(m.renderConversationList())
|
||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
m.HandleResize(msg.Width, msg.Height)
|
m.Width, m.Height = msg.Width, msg.Height
|
||||||
|
m.content.Width = msg.Width
|
||||||
m.content.SetContent(m.renderConversationList())
|
m.content.SetContent(m.renderConversationList())
|
||||||
case msgConversationsLoaded:
|
case msgConversationsLoaded:
|
||||||
m.App.Conversations = msg
|
m.App.Conversations = msg
|
||||||
|
Loading…
Reference in New Issue
Block a user