on main help sign in
chore: show configured indicator
99d4b01Kujtim Hoxha
internal/tui/components/chat/splash/splash.go+1−1
@@ -59,7 +59,7 @@ func New() Splash {
59
59
60 t := styles.CurrentTheme()
60 t := styles.CurrentTheme()
61 inputStyle := t.S().Base.Padding(0, 1, 0, 1)
61 inputStyle := t.S().Base.Padding(0, 1, 0, 1)
62− modelList := models.NewModelListComponent(listKeyMap, inputStyle)
62+ modelList := models.NewModelListComponent(listKeyMap, inputStyle, "Find your fave")
63 return &splashCmp{
63 return &splashCmp{
64 width: 0,
64 width: 0,
65 height: 0,
65 height: 0,
internal/tui/components/core/helpers.go+16
@@ -23,6 +23,22 @@ func Section(text string, width int) string {
23 return text
23 return text
24}
24}
25
25
26+func SectionWithInfo(text string, width int, info string) string {
27+ t := styles.CurrentTheme()
28+ char := "─"
29+ length := lipgloss.Width(text) + 1
30+ remainingWidth := width - length
31+
32+ if info != "" {
33+ remainingWidth -= lipgloss.Width(info) + 1 // 1 for the space before info
34+ }
35+ lineStyle := t.S().Base.Foreground(t.Border)
36+ if remainingWidth > 0 {
37+ text = text + " " + lineStyle.Render(strings.Repeat(char, remainingWidth)) + " " + info
38+ }
39+ return text
40+}
41+
26func Title(title string, width int) string {
42func Title(title string, width int) string {
27 t := styles.CurrentTheme()
43 t := styles.CurrentTheme()
28 char := "╱"
44 char := "╱"
internal/tui/components/core/list/list.go+5
@@ -41,6 +41,7 @@ type ListModel interface {
41 SelectedIndex() int // Get the index of the currently selected item
41 SelectedIndex() int // Get the index of the currently selected item
42 SetSelected(int) tea.Cmd // Set the selected item by index and scroll to it
42 SetSelected(int) tea.Cmd // Set the selected item by index and scroll to it
43 Filter(string) tea.Cmd // Filter items based on a search term
43 Filter(string) tea.Cmd // Filter items based on a search term
44+ SetFilterPlaceholder(string) // Set the placeholder text for the filter input
44}
45}
45
46
46// HasAnim interface identifies items that support animation.
47// HasAnim interface identifies items that support animation.
@@ -1355,3 +1356,7 @@ func (m *model) Focus() tea.Cmd {
1355func (m *model) IsFocused() bool {
1356func (m *model) IsFocused() bool {
1356 return m.isFocused
1357 return m.isFocused
1357}
1358}
1359+
1360+func (m *model) SetFilterPlaceholder(placeholder string) {
1361+ m.input.Placeholder = placeholder
1362+}
internal/tui/components/dialogs/commands/item.go+16−4
@@ -14,11 +14,12 @@ type ItemSection interface {
14 util.Model
14 util.Model
15 layout.Sizeable
15 layout.Sizeable
16 list.SectionHeader
16 list.SectionHeader
17+ SetInfo(info string)
17}
18}
18type itemSectionModel struct {
19type itemSectionModel struct {
19− width int
20+ width int
20− title string
21+ title string
21− noPadding bool // No padding for the section header
22+ info string
22}
23}
23
24
24func NewItemSection(title string) ItemSection {
25func NewItemSection(title string) ItemSection {
@@ -40,7 +41,14 @@ func (m *itemSectionModel) View() tea.View {
40 title := ansi.Truncate(m.title, m.width-2, "…")
41 title := ansi.Truncate(m.title, m.width-2, "…")
41 style := t.S().Base.Padding(1, 1, 0, 1)
42 style := t.S().Base.Padding(1, 1, 0, 1)
42 title = t.S().Muted.Render(title)
43 title = t.S().Muted.Render(title)
44+ section := ""
45+ if m.info != "" {
46+ section = core.SectionWithInfo(title, m.width-2, m.info)
47+ } else {
48+ section = core.Section(title, m.width-2)
49+ }
50+
43− return tea.NewView(style.Render(core.Section(title, m.width-2)))
51+ return tea.NewView(style.Render(section))
44}
52}
45
53
46func (m *itemSectionModel) GetSize() (int, int) {
54func (m *itemSectionModel) GetSize() (int, int) {
@@ -55,3 +63,7 @@ func (m *itemSectionModel) SetSize(width int, height int) tea.Cmd {
55func (m *itemSectionModel) IsSectionHeader() bool {
63func (m *itemSectionModel) IsSectionHeader() bool {
56 return true
64 return true
57}
65}
66+
67+func (m *itemSectionModel) SetInfo(info string) {
68+ m.info = info
69+}
internal/tui/components/dialogs/models/list.go+21−3
@@ -1,6 +1,7 @@
1package models
1package models
2
2
3import (
3import (
4+ "fmt"
4 "slices"
5 "slices"
5
6
6 tea "github.com/charmbracelet/bubbletea/v2"
7 tea "github.com/charmbracelet/bubbletea/v2"
@@ -9,6 +10,7 @@ import (
9 "github.com/charmbracelet/crush/internal/tui/components/completions"
10 "github.com/charmbracelet/crush/internal/tui/components/completions"
10 "github.com/charmbracelet/crush/internal/tui/components/core/list"
11 "github.com/charmbracelet/crush/internal/tui/components/core/list"
11 "github.com/charmbracelet/crush/internal/tui/components/dialogs/commands"
12 "github.com/charmbracelet/crush/internal/tui/components/dialogs/commands"
13+ "github.com/charmbracelet/crush/internal/tui/styles"
12 "github.com/charmbracelet/crush/internal/tui/util"
14 "github.com/charmbracelet/crush/internal/tui/util"
13 "github.com/charmbracelet/lipgloss/v2"
15 "github.com/charmbracelet/lipgloss/v2"
14)
16)
@@ -18,11 +20,12 @@ type ModelListComponent struct {
18 modelType int
20 modelType int
19}
21}
20
22
21−func NewModelListComponent(keyMap list.KeyMap, inputStyle lipgloss.Style) *ModelListComponent {
23+func NewModelListComponent(keyMap list.KeyMap, inputStyle lipgloss.Style, inputPlaceholder string) *ModelListComponent {
22 modelList := list.New(
24 modelList := list.New(
23 list.WithFilterable(true),
25 list.WithFilterable(true),
24 list.WithKeyMap(keyMap),
26 list.WithKeyMap(keyMap),
25 list.WithInputStyle(inputStyle),
27 list.WithInputStyle(inputStyle),
28+ list.WithFilterPlaceholder(inputPlaceholder),
26 list.WithWrapNavigation(true),
29 list.WithWrapNavigation(true),
27 )
30 )
28
31
@@ -59,6 +62,7 @@ func (m *ModelListComponent) SelectedIndex() int {
59}
62}
60
63
61func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
64func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
65+ t := styles.CurrentTheme()
62 m.modelType = modelType
66 m.modelType = modelType
63
67
64 providers, err := config.Providers()
68 providers, err := config.Providers()
@@ -77,6 +81,9 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
77 currentModel = cfg.Models[config.SelectedModelTypeSmall]
81 currentModel = cfg.Models[config.SelectedModelTypeSmall]
78 }
82 }
79
83
84+ configuredIcon := t.S().Base.Foreground(t.Success).Render(styles.CheckIcon)
85+ configured := fmt.Sprintf("%s %s", configuredIcon, t.S().Subtle.Render("Configured"))
86+
80 // Create a map to track which providers we've already added
87 // Create a map to track which providers we've already added
81 addedProviders := make(map[string]bool)
88 addedProviders := make(map[string]bool)
82
89
@@ -120,7 +127,9 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
120 if name == "" {
127 if name == "" {
121 name = string(configProvider.ID)
128 name = string(configProvider.ID)
122 }
129 }
123− modelItems = append(modelItems, commands.NewItemSection(name))
130+ section := commands.NewItemSection(name)
131+ section.SetInfo(configured)
132+ modelItems = append(modelItems, section)
124 for _, model := range configProvider.Models {
133 for _, model := range configProvider.Models {
125 modelItems = append(modelItems, completions.NewCompletionItem(model.Name, ModelOption{
134 modelItems = append(modelItems, completions.NewCompletionItem(model.Name, ModelOption{
126 Provider: configProvider,
135 Provider: configProvider,
@@ -150,7 +159,12 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
150 if name == "" {
159 if name == "" {
151 name = string(provider.ID)
160 name = string(provider.ID)
152 }
161 }
162+
153− modelItems = append(modelItems, commands.NewItemSection(name))
163+ section := commands.NewItemSection(name)
164+ if _, ok := cfg.Providers[string(provider.ID)]; ok {
165+ section.SetInfo(configured)
166+ }
167+ modelItems = append(modelItems, section)
154 for _, model := range provider.Models {
168 for _, model := range provider.Models {
155 modelItems = append(modelItems, completions.NewCompletionItem(model.Name, ModelOption{
169 modelItems = append(modelItems, completions.NewCompletionItem(model.Name, ModelOption{
156 Provider: provider,
170 Provider: provider,
@@ -169,3 +183,7 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
169func (m *ModelListComponent) GetModelType() int {
183func (m *ModelListComponent) GetModelType() int {
170 return m.modelType
184 return m.modelType
171}
185}
186+
187+func (m *ModelListComponent) SetInputPlaceholder(placeholder string) {
188+ m.list.SetFilterPlaceholder(placeholder)
189+}
internal/tui/components/dialogs/models/models.go+6−1
@@ -24,6 +24,9 @@ const (
24const (
24const (
25 LargeModelType int = iota
25 LargeModelType int = iota
26 SmallModelType
26 SmallModelType
27+
28+ largeModelInputPlaceholder = "Choose a model for large, complex tasks"
29+ smallModelInputPlaceholder = "Choose a model for small, simple tasks"
27)
30)
28
31
29// ModelSelectedMsg is sent when a model is selected
32// ModelSelectedMsg is sent when a model is selected
@@ -71,7 +74,7 @@ func NewModelDialogCmp() ModelDialog {
71
74
72 t := styles.CurrentTheme()
75 t := styles.CurrentTheme()
73 inputStyle := t.S().Base.Padding(0, 1, 0, 1)
76 inputStyle := t.S().Base.Padding(0, 1, 0, 1)
74− modelList := NewModelListComponent(listKeyMap, inputStyle)
77+ modelList := NewModelListComponent(listKeyMap, inputStyle, "Choose a model for large, complex tasks")
75 help := help.New()
78 help := help.New()
76 help.Styles = t.S().Help
79 help.Styles = t.S().Help
77
80
@@ -122,8 +125,10 @@ func (m *modelDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
122 )
125 )
123 case key.Matches(msg, m.keyMap.Tab):
126 case key.Matches(msg, m.keyMap.Tab):
124 if m.modelList.GetModelType() == LargeModelType {
127 if m.modelList.GetModelType() == LargeModelType {
128+ m.modelList.SetInputPlaceholder(smallModelInputPlaceholder)
125 return m, m.modelList.SetModelType(SmallModelType)
129 return m, m.modelList.SetModelType(SmallModelType)
126 } else {
130 } else {
131+ m.modelList.SetInputPlaceholder(largeModelInputPlaceholder)
127 return m, m.modelList.SetModelType(LargeModelType)
132 return m, m.modelList.SetModelType(LargeModelType)
128 }
133 }
129 case key.Matches(msg, m.keyMap.Close):
134 case key.Matches(msg, m.keyMap.Close):