package tui import ( "fmt" "git.mlow.ca/mlow/lmcli/pkg/api" "git.mlow.ca/mlow/lmcli/pkg/lmcli" "git.mlow.ca/mlow/lmcli/pkg/tui/model" "git.mlow.ca/mlow/lmcli/pkg/tui/shared" "git.mlow.ca/mlow/lmcli/pkg/tui/views/chat" "git.mlow.ca/mlow/lmcli/pkg/tui/views/conversations" tea "github.com/charmbracelet/bubbletea" ) type LaunchOptions struct { InitialConversation *api.Conversation InitialView shared.View } type Model struct { App *model.AppModel activeView shared.View views map[shared.View]shared.ViewModel } func initialModel(ctx *lmcli.Context, opts LaunchOptions) Model { sharedData := shared.ViewState{} app := &model.AppModel{ Ctx: ctx, Conversation: opts.InitialConversation, } 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), }, } return m } func (m Model) Init() tea.Cmd { return tea.Batch( func() tea.Msg { return shared.MsgViewChange(m.activeView) }, ) } 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 tea.Quit } return nil } func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: cmd := m.handleGlobalInput(msg) if cmd != nil { return m, cmd } case shared.MsgViewChange: m.activeView = shared.View(msg) view := m.views[m.activeView] var cmds []tea.Cmd if !view.Initialized() { cmds = append(cmds, view.Init()) } cmds = append(cmds, tea.WindowSize(), shared.ViewEnter()) 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 { return m.views[m.activeView].View() } type LaunchOption func(*LaunchOptions) func WithInitialConversation(conv *api.Conversation) LaunchOption { return func(opts *LaunchOptions) { opts.InitialConversation = conv } } func WithInitialView(view shared.View) LaunchOption { return func(opts *LaunchOptions) { opts.InitialView = view } } func Launch(ctx *lmcli.Context, options ...LaunchOption) error { opts := &LaunchOptions{ InitialView: shared.ViewChat, } for _, opt := range options { opt(opts) } program := tea.NewProgram(initialModel(ctx, *opts), tea.WithAltScreen()) if _, err := program.Run(); err != nil { return fmt.Errorf("Error running program: %v", err) } return nil }