@@ -1,8 +1,6 @@
1package models
1package models
6 "github.com/charmbracelet/bubbles/v2/help"
4 "github.com/charmbracelet/bubbles/v2/help"
7 "github.com/charmbracelet/bubbles/v2/key"
5 "github.com/charmbracelet/bubbles/v2/key"
8 tea "github.com/charmbracelet/bubbletea/v2"
6 tea "github.com/charmbracelet/bubbletea/v2"
@@ -12,7 +10,6 @@ import (
12 "github.com/charmbracelet/crush/internal/tui/components/core"
10 "github.com/charmbracelet/crush/internal/tui/components/core"
13 "github.com/charmbracelet/crush/internal/tui/components/core/list"
11 "github.com/charmbracelet/crush/internal/tui/components/core/list"
14 "github.com/charmbracelet/crush/internal/tui/components/dialogs"
12 "github.com/charmbracelet/crush/internal/tui/components/dialogs"
15− "github.com/charmbracelet/crush/internal/tui/components/dialogs/commands"
16 "github.com/charmbracelet/crush/internal/tui/styles"
13 "github.com/charmbracelet/crush/internal/tui/styles"
17 "github.com/charmbracelet/crush/internal/tui/util"
14 "github.com/charmbracelet/crush/internal/tui/util"
18 "github.com/charmbracelet/lipgloss/v2"
15 "github.com/charmbracelet/lipgloss/v2"
@@ -53,10 +50,9 @@ type modelDialogCmp struct {
53 wWidth int
50 wWidth int
54 wHeight int
51 wHeight int
56− modelList list.ListModel
53+ modelList *ModelListComponent
57 keyMap KeyMap
54 keyMap KeyMap
58 help help.Model
55 help help.Model
62func NewModelDialogCmp() ModelDialog {
58func NewModelDialogCmp() ModelDialog {
@@ -75,12 +71,7 @@ func NewModelDialogCmp() ModelDialog {
76 t := styles.CurrentTheme()
72 t := styles.CurrentTheme()
77 inputStyle := t.S().Base.Padding(0, 1, 0, 1)
73 inputStyle := t.S().Base.Padding(0, 1, 0, 1)
78− modelList := list.New(
74+ modelList := NewModelListComponent(listKeyMap, inputStyle)
79− list.WithFilterable(true),
80− list.WithKeyMap(listKeyMap),
81− list.WithInputStyle(inputStyle),
82− list.WithWrapNavigation(true),
84 help := help.New()
75 help := help.New()
85 help.Styles = t.S().Help
76 help.Styles = t.S().Help
@@ -89,12 +80,10 @@ func NewModelDialogCmp() ModelDialog {
89 width: defaultWidth,
80 width: defaultWidth,
90 keyMap: DefaultKeyMap(),
81 keyMap: DefaultKeyMap(),
91 help: help,
82 help: help,
92− modelType: LargeModelType,
96func (m *modelDialogCmp) Init() tea.Cmd {
86func (m *modelDialogCmp) Init() tea.Cmd {
97− m.SetModelType(m.modelType)
98 return m.modelList.Init()
87 return m.modelList.Init()
@@ -103,7 +92,6 @@ func (m *modelDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
103 case tea.WindowSizeMsg:
92 case tea.WindowSizeMsg:
104 m.wWidth = msg.Width
93 m.wWidth = msg.Width
105 m.wHeight = msg.Height
94 m.wHeight = msg.Height
106− m.SetModelType(m.modelType)
107 return m, m.modelList.SetSize(m.listWidth(), m.listHeight())
95 return m, m.modelList.SetSize(m.listWidth(), m.listHeight())
108 case tea.KeyPressMsg:
96 case tea.KeyPressMsg:
@@ -116,8 +104,8 @@ func (m *modelDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
116 selectedItem := items[selectedItemInx].(completions.CompletionItem).Value().(ModelOption)
104 selectedItem := items[selectedItemInx].(completions.CompletionItem).Value().(ModelOption)
118 var modelType config.SelectedModelType
106 var modelType config.SelectedModelType
119− if m.modelType == LargeModelType {
107+ if m.modelList.GetModelType() == LargeModelType {
120− modelType = config.SelectedModelTypeLarge
108+ modelType = config.LargeModel
122 modelType = config.SelectedModelTypeSmall
110 modelType = config.SelectedModelTypeSmall
@@ -133,16 +121,16 @@ func (m *modelDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
135 case key.Matches(msg, m.keyMap.Tab):
123 case key.Matches(msg, m.keyMap.Tab):
136− if m.modelType == LargeModelType {
124+ if m.modelList.GetModelType() == LargeModelType {
137− return m, m.SetModelType(SmallModelType)
125+ return m, m.modelList.SetModelType(SmallModelType)
139− return m, m.SetModelType(LargeModelType)
127+ return m, m.modelList.SetModelType(LargeModelType)
141 case key.Matches(msg, m.keyMap.Close):
129 case key.Matches(msg, m.keyMap.Close):
142 return m, util.CmdHandler(dialogs.CloseDialogMsg{})
130 return m, util.CmdHandler(dialogs.CloseDialogMsg{})
144 u, cmd := m.modelList.Update(msg)
132 u, cmd := m.modelList.Update(msg)
145− m.modelList = u.(list.ListModel)
133+ m.modelList = u
146 return m, cmd
134 return m, cmd
@@ -181,7 +169,8 @@ func (m *modelDialogCmp) listWidth() int {
183func (m *modelDialogCmp) listHeight() int {
171func (m *modelDialogCmp) listHeight() int {
184− listHeigh := len(m.modelList.Items()) + 2 + 4 // height based on items + 2 for the input + 4 for the sections
172+ items := m.modelList.Items()
173+ listHeigh := len(items) + 2 + 4
185 return min(listHeigh, m.wHeight/2)
174 return min(listHeigh, m.wHeight/2)
@@ -209,115 +198,8 @@ func (m *modelDialogCmp) modelTypeRadio() string {
209 choices := []string{"Large Task", "Small Task"}
198 choices := []string{"Large Task", "Small Task"}
210 iconSelected := "◉"
199 iconSelected := "◉"
211 iconUnselected := "○"
200 iconUnselected := "○"
212− if m.modelType == LargeModelType {
201+ if m.modelList.GetModelType() == LargeModelType {
213 return t.S().Base.Foreground(t.FgHalfMuted).Render(iconSelected + " " + choices[0] + " " + iconUnselected + " " + choices[1])
202 return t.S().Base.Foreground(t.FgHalfMuted).Render(iconSelected + " " + choices[0] + " " + iconUnselected + " " + choices[1])
215 return t.S().Base.Foreground(t.FgHalfMuted).Render(iconUnselected + " " + choices[0] + " " + iconSelected + " " + choices[1])
204 return t.S().Base.Foreground(t.FgHalfMuted).Render(iconUnselected + " " + choices[0] + " " + iconSelected + " " + choices[1])
218−func (m *modelDialogCmp) SetModelType(modelType int) tea.Cmd {
219− m.modelType = modelType
221− providers, err := config.Providers()
223− return util.ReportError(err)
226− modelItems := []util.Model{}
230− var currentModel config.SelectedModel
231− if m.modelType == LargeModelType {
232− currentModel = cfg.Models[config.SelectedModelTypeLarge]
234− currentModel = cfg.Models[config.SelectedModelTypeSmall]
237− // Create a map to track which providers we've already added
238− addedProviders := make(map[string]bool)
240− // First, add any configured providers that are not in the known providers list
241− // These should appear at the top of the list
242− knownProviders := provider.KnownProviders()
243− for providerID, providerConfig := range cfg.Providers {
244− if providerConfig.Disable {
248− // Check if this provider is not in the known providers list
249− if !slices.Contains(knownProviders, provider.InferenceProvider(providerID)) {
250− // Convert config provider to provider.Provider format
251− configProvider := provider.Provider{
252− Name: string(providerID), // Use provider ID as name for unknown providers
253− ID: provider.InferenceProvider(providerID),
254− Models: make([]provider.Model, len(providerConfig.Models)),
258− for i, model := range providerConfig.Models {
259− configProvider.Models[i] = provider.Model{
262− CostPer1MIn: model.CostPer1MIn,
263− CostPer1MOut: model.CostPer1MOut,
264− CostPer1MInCached: model.CostPer1MInCached,
265− CostPer1MOutCached: model.CostPer1MOutCached,
266− ContextWindow: model.ContextWindow,
267− DefaultMaxTokens: model.DefaultMaxTokens,
268− CanReason: model.CanReason,
269− HasReasoningEffort: model.HasReasoningEffort,
270− DefaultReasoningEffort: model.DefaultReasoningEffort,
271− SupportsImages: model.SupportsImages,
275− // Add this unknown provider to the list
276− name := configProvider.Name
278− name = string(configProvider.ID)
280− modelItems = append(modelItems, commands.NewItemSection(name))
281− for _, model := range configProvider.Models {
282− modelItems = append(modelItems, completions.NewCompletionItem(model.Name, ModelOption{
283− Provider: configProvider,
286− if model.ID == currentModel.Model && string(configProvider.ID) == currentModel.Provider {
287− selectIndex = len(modelItems) - 1 // Set the selected index to the current model
290− addedProviders[providerID] = true
294− // Then add the known providers from the predefined list
295− for _, provider := range providers {
296− // Skip if we already added this provider as an unknown provider
297− if addedProviders[string(provider.ID)] {
301− // Check if this provider is configured and not disabled
302− if providerConfig, exists := cfg.Providers[string(provider.ID)]; exists && providerConfig.Disable {
306− name := provider.Name
308− name = string(provider.ID)
310− modelItems = append(modelItems, commands.NewItemSection(name))
311− for _, model := range provider.Models {
312− modelItems = append(modelItems, completions.NewCompletionItem(model.Name, ModelOption{
316− if model.ID == currentModel.Model && string(provider.ID) == currentModel.Provider {
317− selectIndex = len(modelItems) - 1 // Set the selected index to the current model
322− return tea.Sequence(m.modelList.SetItems(modelItems), m.modelList.SetSelected(selectIndex))