package settings import ( "strings" "git.mlow.ca/mlow/lmcli/pkg/tui/bubbles/list" "git.mlow.ca/mlow/lmcli/pkg/tui/model" "git.mlow.ca/mlow/lmcli/pkg/tui/shared" "git.mlow.ca/mlow/lmcli/pkg/tui/styles" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) type Model struct { App *model.AppModel prevView shared.View content viewport.Model modelList list.Model width int height int } type modelOpt struct { provider string model string } const ( modelListId int = iota + 1 ) func Settings(app *model.AppModel) *Model { m := &Model{ App: app, content: viewport.New(0, 0), } return m } func (m *Model) Init() tea.Cmd { m.modelList = list.NewWithGroups(m.getModelOptions()) m.modelList.ID = modelListId return nil } func (m *Model) Update(msg tea.Msg) (shared.ViewModel, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { case tea.KeyMsg: m.modelList, cmd = m.modelList.Update(msg) if cmd != nil { return m, cmd } switch msg.String() { case "esc": return m, func() tea.Msg { return shared.MsgViewChange(m.prevView) } } case shared.MsgViewEnter: m.prevView = shared.View(msg) m.modelList.Focus() m.content.SetContent(m.renderContent()) case tea.WindowSizeMsg: m.width, m.height = msg.Width, msg.Height m.content.Width = msg.Width m.content.Height = msg.Height m.content.SetContent(m.renderContent()) case list.MsgOptionSelected: switch msg.ID { case modelListId: if modelOpt, ok := msg.Option.Value.(modelOpt); ok { m.App.Model = modelOpt.model m.App.ProviderName = modelOpt.provider } return m, shared.ChangeView(m.prevView) } } m.modelList, cmd = m.modelList.Update(msg) if cmd != nil { return m, cmd } m.content.SetContent(m.renderContent()) return m, nil } func (m *Model) getModelOptions() []list.OptionGroup { modelOpts := []list.OptionGroup{} for _, p := range m.App.Ctx.Config.Providers { provider := p.Name if provider == "" { provider = p.Kind } providerLabel := p.Display if providerLabel == "" { providerLabel = strings.ToUpper(provider[:1]) + provider[1:] } group := list.OptionGroup{ Name: providerLabel, } for _, model := range p.Models { group.Options = append(group.Options, list.Option{ Label: model, Value: modelOpt{provider, model}, }) } modelOpts = append(modelOpts, group) } return modelOpts } func (m *Model) Header(width int) string { boldStyle := lipgloss.NewStyle().Bold(true) // TODO: update header depending on active settings mode (model, agent, etc) header := boldStyle.Render("Model selection") return styles.Header.Width(width).Render(header) } func (m *Model) Content(width, height int, errors string) string { // TODO: see Header() currentModel := " Active model: " + m.App.ActiveModel(lipgloss.NewStyle()) m.modelList.Width, m.modelList.Height = width, height - 2 content := "\n" + currentModel + "\n" + m.modelList.View() if errors != "" { content += errors } return content } func (m *Model) Footer(width int) string { return "" } func (m *Model) renderContent() string { return m.modelList.View() }