TUI view management and input handling cleanup
This commit is contained in:
114
pkg/tui/tui.go
114
pkg/tui/tui.go
@@ -1,11 +1,5 @@
|
||||
package tui
|
||||
|
||||
// The terminal UI for lmcli, launched from the `lmcli chat` command
|
||||
// TODO:
|
||||
// - change model
|
||||
// - rename conversation
|
||||
// - set system prompt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@@ -24,117 +18,81 @@ type LaunchOptions struct {
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
App *model.AppModel
|
||||
view shared.View
|
||||
App *model.AppModel
|
||||
|
||||
// views
|
||||
chat chat.Model
|
||||
conversations conversations.Model
|
||||
activeView shared.View
|
||||
views map[shared.View]shared.ViewModel
|
||||
}
|
||||
|
||||
func initialModel(ctx *lmcli.Context, opts LaunchOptions) Model {
|
||||
m := Model{
|
||||
App: &model.AppModel{
|
||||
Ctx: ctx,
|
||||
Conversation: opts.InitialConversation,
|
||||
},
|
||||
view: opts.InitialView,
|
||||
sharedData := shared.ViewState{}
|
||||
|
||||
app := &model.AppModel{
|
||||
Ctx: ctx,
|
||||
Conversation: opts.InitialConversation,
|
||||
}
|
||||
|
||||
sharedData := shared.Shared{}
|
||||
m := Model{
|
||||
App: app,
|
||||
activeView: opts.InitialView,
|
||||
views: map[shared.View]shared.ViewModel{
|
||||
shared.ViewChat: chat.Chat(app, sharedData),
|
||||
shared.ViewConversations: conversations.Conversations(app, sharedData),
|
||||
},
|
||||
}
|
||||
|
||||
m.chat = chat.Chat(m.App, sharedData)
|
||||
m.conversations = conversations.Conversations(m.App, sharedData)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m Model) Init() tea.Cmd {
|
||||
return tea.Batch(
|
||||
func() tea.Msg {
|
||||
return shared.MsgViewChange(m.view)
|
||||
return shared.MsgViewChange(m.activeView)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (m *Model) handleGlobalInput(msg tea.KeyMsg) (bool, tea.Cmd) {
|
||||
var cmds []tea.Cmd
|
||||
switch m.view {
|
||||
case shared.StateChat:
|
||||
handled, cmd := m.chat.HandleInput(msg)
|
||||
cmds = append(cmds, cmd)
|
||||
if handled {
|
||||
m.chat, cmd = m.chat.Update(nil)
|
||||
cmds = append(cmds, cmd)
|
||||
return true, tea.Batch(cmds...)
|
||||
}
|
||||
case shared.StateConversations:
|
||||
handled, cmd := m.conversations.HandleInput(msg)
|
||||
cmds = append(cmds, cmd)
|
||||
if handled {
|
||||
m.conversations, cmd = m.conversations.Update(nil)
|
||||
cmds = append(cmds, cmd)
|
||||
return true, tea.Batch(cmds...)
|
||||
}
|
||||
func (m *Model) handleGlobalInput(msg tea.KeyMsg) tea.Cmd {
|
||||
view, cmd := m.views[m.activeView].Update(msg)
|
||||
m.views[m.activeView] = view
|
||||
if cmd != nil {
|
||||
return cmd
|
||||
}
|
||||
|
||||
switch msg.String() {
|
||||
case "ctrl+c", "ctrl+q":
|
||||
return true, tea.Quit
|
||||
return tea.Quit
|
||||
}
|
||||
return false, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmds []tea.Cmd
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
handled, cmd := m.handleGlobalInput(msg)
|
||||
if handled {
|
||||
cmd := m.handleGlobalInput(msg)
|
||||
if cmd != nil {
|
||||
return m, cmd
|
||||
}
|
||||
case shared.MsgViewChange:
|
||||
m.view = shared.View(msg)
|
||||
m.activeView = shared.View(msg)
|
||||
view := m.views[m.activeView]
|
||||
|
||||
var cmds []tea.Cmd
|
||||
switch m.view {
|
||||
case shared.StateConversations:
|
||||
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
|
||||
}
|
||||
if !view.Initialized() {
|
||||
cmds = append(cmds, view.Init())
|
||||
}
|
||||
cmds = append(cmds, tea.WindowSize(), shared.ViewEnter())
|
||||
|
||||
return m, tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
var cmd tea.Cmd
|
||||
switch m.view {
|
||||
case shared.StateConversations:
|
||||
m.conversations, cmd = m.conversations.Update(msg)
|
||||
case shared.StateChat:
|
||||
m.chat, cmd = m.chat.Update(msg)
|
||||
}
|
||||
if cmd != nil {
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
|
||||
return m, tea.Batch(cmds...)
|
||||
view, cmd := m.views[m.activeView].Update(msg)
|
||||
m.views[m.activeView] = view
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m Model) View() string {
|
||||
switch m.view {
|
||||
case shared.StateConversations:
|
||||
return m.conversations.View()
|
||||
case shared.StateChat:
|
||||
return m.chat.View()
|
||||
}
|
||||
return ""
|
||||
return m.views[m.activeView].View()
|
||||
}
|
||||
|
||||
type LaunchOption func(*LaunchOptions)
|
||||
@@ -153,7 +111,7 @@ func WithInitialView(view shared.View) LaunchOption {
|
||||
|
||||
func Launch(ctx *lmcli.Context, options ...LaunchOption) error {
|
||||
opts := &LaunchOptions{
|
||||
InitialView: shared.StateChat,
|
||||
InitialView: shared.ViewChat,
|
||||
}
|
||||
for _, opt := range options {
|
||||
opt(opts)
|
||||
|
||||
Reference in New Issue
Block a user