1package dialog
2
3import (
4 "charm.land/catwalk/pkg/catwalk"
5 "charm.land/lipgloss/v2"
6 "github.com/charmbracelet/crush/internal/config"
7 "github.com/charmbracelet/crush/internal/ui/common"
8 "github.com/charmbracelet/crush/internal/ui/styles"
9 "github.com/charmbracelet/x/ansi"
10 "github.com/sahilm/fuzzy"
11)
12
13// ModelGroup represents a group of model items.
14type ModelGroup struct {
15 Title string
16 Items []*ModelItem
17 configured bool
18 t *styles.Styles
19}
20
21// NewModelGroup creates a new ModelGroup.
22func NewModelGroup(t *styles.Styles, title string, configured bool, items ...*ModelItem) ModelGroup {
23 return ModelGroup{
24 Title: title,
25 Items: items,
26 configured: configured,
27 t: t,
28 }
29}
30
31// AppendItems appends [ModelItem]s to the group.
32func (m *ModelGroup) AppendItems(items ...*ModelItem) {
33 m.Items = append(m.Items, items...)
34}
35
36// Render implements [list.Item].
37func (m *ModelGroup) Render(width int) string {
38 var configured string
39 if m.configured {
40 configuredIcon := m.t.ToolCallSuccess.Render()
41 configuredText := m.t.Subtle.Render("Configured")
42 configured = configuredIcon + " " + configuredText
43 }
44
45 title := " " + m.Title + " "
46 title = ansi.Truncate(title, max(0, width-lipgloss.Width(configured)-1), "…")
47
48 return common.Section(m.t, title, width, configured)
49}
50
51// ModelItem represents a list item for a model type.
52type ModelItem struct {
53 prov catwalk.Provider
54 model catwalk.Model
55 modelType ModelType
56
57 cache map[int]string
58 t *styles.Styles
59 m fuzzy.Match
60 focused bool
61 showProvider bool
62}
63
64// SelectedModel returns this model item as a [config.SelectedModel] instance.
65func (m *ModelItem) SelectedModel() config.SelectedModel {
66 return config.SelectedModel{
67 Model: m.model.ID,
68 Provider: string(m.prov.ID),
69 ReasoningEffort: m.model.DefaultReasoningEffort,
70 MaxTokens: m.model.DefaultMaxTokens,
71 }
72}
73
74// SelectedModelType returns the type of model represented by this item.
75func (m *ModelItem) SelectedModelType() config.SelectedModelType {
76 return m.modelType.Config()
77}
78
79var _ ListItem = &ModelItem{}
80
81// NewModelItem creates a new ModelItem.
82func NewModelItem(t *styles.Styles, prov catwalk.Provider, model catwalk.Model, typ ModelType, showProvider bool) *ModelItem {
83 return &ModelItem{
84 prov: prov,
85 model: model,
86 modelType: typ,
87 t: t,
88 cache: make(map[int]string),
89 showProvider: showProvider,
90 }
91}
92
93// Filter implements ListItem.
94func (m *ModelItem) Filter() string {
95 return m.model.Name
96}
97
98// ID implements ListItem.
99func (m *ModelItem) ID() string {
100 return modelKey(string(m.prov.ID), m.model.ID)
101}
102
103// Render implements ListItem.
104func (m *ModelItem) Render(width int) string {
105 var providerInfo string
106 if m.showProvider {
107 providerInfo = string(m.prov.Name)
108 }
109 styles := ListItemStyles{
110 ItemBlurred: m.t.Dialog.NormalItem,
111 ItemFocused: m.t.Dialog.SelectedItem,
112 InfoTextBlurred: m.t.Base,
113 InfoTextFocused: m.t.Base,
114 }
115 return renderItem(styles, m.model.Name, providerInfo, m.focused, width, m.cache, &m.m)
116}
117
118// SetFocused implements ListItem.
119func (m *ModelItem) SetFocused(focused bool) {
120 if m.focused != focused {
121 m.cache = nil
122 }
123 m.focused = focused
124}
125
126// SetMatch implements ListItem.
127func (m *ModelItem) SetMatch(fm fuzzy.Match) {
128 m.cache = nil
129 m.m = fm
130}