Detailed changes
@@ -59,7 +59,7 @@ func New() Splash {
t := styles.CurrentTheme()
inputStyle := t.S().Base.Padding(0, 1, 0, 1)
- modelList := models.NewModelListComponent(listKeyMap, inputStyle)
+ modelList := models.NewModelListComponent(listKeyMap, inputStyle, "Find your fave")
return &splashCmp{
width: 0,
height: 0,
@@ -23,6 +23,22 @@ func Section(text string, width int) string {
return text
}
+func SectionWithInfo(text string, width int, info string) string {
+ t := styles.CurrentTheme()
+ char := "─"
+ length := lipgloss.Width(text) + 1
+ remainingWidth := width - length
+
+ if info != "" {
+ remainingWidth -= lipgloss.Width(info) + 1 // 1 for the space before info
+ }
+ lineStyle := t.S().Base.Foreground(t.Border)
+ if remainingWidth > 0 {
+ text = text + " " + lineStyle.Render(strings.Repeat(char, remainingWidth)) + " " + info
+ }
+ return text
+}
+
func Title(title string, width int) string {
t := styles.CurrentTheme()
char := "╱"
@@ -41,6 +41,7 @@ type ListModel interface {
SelectedIndex() int // Get the index of the currently selected item
SetSelected(int) tea.Cmd // Set the selected item by index and scroll to it
Filter(string) tea.Cmd // Filter items based on a search term
+ SetFilterPlaceholder(string) // Set the placeholder text for the filter input
}
// HasAnim interface identifies items that support animation.
@@ -1355,3 +1356,7 @@ func (m *model) Focus() tea.Cmd {
func (m *model) IsFocused() bool {
return m.isFocused
}
+
+func (m *model) SetFilterPlaceholder(placeholder string) {
+ m.input.Placeholder = placeholder
+}
@@ -14,11 +14,12 @@ type ItemSection interface {
util.Model
layout.Sizeable
list.SectionHeader
+ SetInfo(info string)
}
type itemSectionModel struct {
- width int
- title string
- noPadding bool // No padding for the section header
+ width int
+ title string
+ info string
}
func NewItemSection(title string) ItemSection {
@@ -40,7 +41,14 @@ func (m *itemSectionModel) View() tea.View {
title := ansi.Truncate(m.title, m.width-2, "…")
style := t.S().Base.Padding(1, 1, 0, 1)
title = t.S().Muted.Render(title)
- return tea.NewView(style.Render(core.Section(title, m.width-2)))
+ section := ""
+ if m.info != "" {
+ section = core.SectionWithInfo(title, m.width-2, m.info)
+ } else {
+ section = core.Section(title, m.width-2)
+ }
+
+ return tea.NewView(style.Render(section))
}
func (m *itemSectionModel) GetSize() (int, int) {
@@ -55,3 +63,7 @@ func (m *itemSectionModel) SetSize(width int, height int) tea.Cmd {
func (m *itemSectionModel) IsSectionHeader() bool {
return true
}
+
+func (m *itemSectionModel) SetInfo(info string) {
+ m.info = info
+}
@@ -1,6 +1,7 @@
package models
import (
+ "fmt"
"slices"
tea "github.com/charmbracelet/bubbletea/v2"
@@ -9,6 +10,7 @@ import (
"github.com/charmbracelet/crush/internal/tui/components/completions"
"github.com/charmbracelet/crush/internal/tui/components/core/list"
"github.com/charmbracelet/crush/internal/tui/components/dialogs/commands"
+ "github.com/charmbracelet/crush/internal/tui/styles"
"github.com/charmbracelet/crush/internal/tui/util"
"github.com/charmbracelet/lipgloss/v2"
)
@@ -18,11 +20,12 @@ type ModelListComponent struct {
modelType int
}
-func NewModelListComponent(keyMap list.KeyMap, inputStyle lipgloss.Style) *ModelListComponent {
+func NewModelListComponent(keyMap list.KeyMap, inputStyle lipgloss.Style, inputPlaceholder string) *ModelListComponent {
modelList := list.New(
list.WithFilterable(true),
list.WithKeyMap(keyMap),
list.WithInputStyle(inputStyle),
+ list.WithFilterPlaceholder(inputPlaceholder),
list.WithWrapNavigation(true),
)
@@ -59,6 +62,7 @@ func (m *ModelListComponent) SelectedIndex() int {
}
func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
+ t := styles.CurrentTheme()
m.modelType = modelType
providers, err := config.Providers()
@@ -77,6 +81,9 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
currentModel = cfg.Models[config.SelectedModelTypeSmall]
}
+ configuredIcon := t.S().Base.Foreground(t.Success).Render(styles.CheckIcon)
+ configured := fmt.Sprintf("%s %s", configuredIcon, t.S().Subtle.Render("Configured"))
+
// Create a map to track which providers we've already added
addedProviders := make(map[string]bool)
@@ -120,7 +127,9 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
if name == "" {
name = string(configProvider.ID)
}
- modelItems = append(modelItems, commands.NewItemSection(name))
+ section := commands.NewItemSection(name)
+ section.SetInfo(configured)
+ modelItems = append(modelItems, section)
for _, model := range configProvider.Models {
modelItems = append(modelItems, completions.NewCompletionItem(model.Name, ModelOption{
Provider: configProvider,
@@ -150,7 +159,12 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
if name == "" {
name = string(provider.ID)
}
- modelItems = append(modelItems, commands.NewItemSection(name))
+
+ section := commands.NewItemSection(name)
+ if _, ok := cfg.Providers[string(provider.ID)]; ok {
+ section.SetInfo(configured)
+ }
+ modelItems = append(modelItems, section)
for _, model := range provider.Models {
modelItems = append(modelItems, completions.NewCompletionItem(model.Name, ModelOption{
Provider: provider,
@@ -169,3 +183,7 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
func (m *ModelListComponent) GetModelType() int {
return m.modelType
}
+
+func (m *ModelListComponent) SetInputPlaceholder(placeholder string) {
+ m.list.SetFilterPlaceholder(placeholder)
+}
@@ -24,6 +24,9 @@ const (
const (
LargeModelType int = iota
SmallModelType
+
+ largeModelInputPlaceholder = "Choose a model for large, complex tasks"
+ smallModelInputPlaceholder = "Choose a model for small, simple tasks"
)
// ModelSelectedMsg is sent when a model is selected
@@ -71,7 +74,7 @@ func NewModelDialogCmp() ModelDialog {
t := styles.CurrentTheme()
inputStyle := t.S().Base.Padding(0, 1, 0, 1)
- modelList := NewModelListComponent(listKeyMap, inputStyle)
+ modelList := NewModelListComponent(listKeyMap, inputStyle, "Choose a model for large, complex tasks")
help := help.New()
help.Styles = t.S().Help
@@ -122,8 +125,10 @@ func (m *modelDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
)
case key.Matches(msg, m.keyMap.Tab):
if m.modelList.GetModelType() == LargeModelType {
+ m.modelList.SetInputPlaceholder(smallModelInputPlaceholder)
return m, m.modelList.SetModelType(SmallModelType)
} else {
+ m.modelList.SetInputPlaceholder(largeModelInputPlaceholder)
return m, m.modelList.SetModelType(LargeModelType)
}
case key.Matches(msg, m.keyMap.Close):