Skip to content

Commit dc19478

Browse files
committed
fix(tui): cleanup modal visuals
1 parent 3ea2daa commit dc19478

File tree

4 files changed

+95
-190
lines changed

4 files changed

+95
-190
lines changed

packages/tui/internal/components/dialog/models.go

Lines changed: 67 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
tea "github.com/charmbracelet/bubbletea/v2"
1212
"github.com/charmbracelet/lipgloss/v2"
1313
"github.com/sst/opencode/internal/app"
14+
"github.com/sst/opencode/internal/components/list"
1415
"github.com/sst/opencode/internal/components/modal"
1516
"github.com/sst/opencode/internal/layout"
1617
"github.com/sst/opencode/internal/styles"
@@ -33,35 +34,22 @@ type modelDialog struct {
3334
app *app.App
3435
availableProviders []client.ProviderInfo
3536
provider client.ProviderInfo
36-
37-
selectedIdx int
38-
width int
39-
height int
40-
scrollOffset int
41-
hScrollOffset int
42-
hScrollPossible bool
43-
44-
modal *modal.Modal
37+
width int
38+
height int
39+
hScrollOffset int
40+
hScrollPossible bool
41+
modal *modal.Modal
42+
modelList list.List[list.StringItem]
4543
}
4644

4745
type modelKeyMap struct {
48-
Up key.Binding
49-
Down key.Binding
5046
Left key.Binding
5147
Right key.Binding
5248
Enter key.Binding
5349
Escape key.Binding
5450
}
5551

5652
var modelKeys = modelKeyMap{
57-
Up: key.NewBinding(
58-
key.WithKeys("up", "k"),
59-
key.WithHelp("↑", "previous model"),
60-
),
61-
Down: key.NewBinding(
62-
key.WithKeys("down", "j"),
63-
key.WithHelp("↓", "next model"),
64-
),
6553
Left: key.NewBinding(
6654
key.WithKeys("left", "h"),
6755
key.WithHelp("←", "scroll left"),
@@ -81,42 +69,40 @@ var modelKeys = modelKeyMap{
8169
}
8270

8371
func (m *modelDialog) Init() tea.Cmd {
84-
// cfg := config.Get()
85-
// modelInfo := GetSelectedModel(cfg)
86-
// m.availableProviders = getEnabledProviders(cfg)
87-
// m.hScrollPossible = len(m.availableProviders) > 1
88-
89-
// m.provider = modelInfo.Provider
90-
// m.hScrollOffset = findProviderIndex(m.availableProviders, m.provider)
91-
92-
// m.setupModelsForProvider(m.provider)
72+
m.setupModelsForProvider(m.provider.Id)
9373
return nil
9474
}
9575

9676
func (m *modelDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
9777
switch msg := msg.(type) {
9878
case tea.KeyMsg:
9979
switch {
100-
case key.Matches(msg, modelKeys.Up):
101-
m.moveSelectionUp()
102-
case key.Matches(msg, modelKeys.Down):
103-
m.moveSelectionDown()
10480
case key.Matches(msg, modelKeys.Left):
10581
if m.hScrollPossible {
10682
m.switchProvider(-1)
10783
}
84+
return m, nil
10885
case key.Matches(msg, modelKeys.Right):
10986
if m.hScrollPossible {
11087
m.switchProvider(1)
11188
}
89+
return m, nil
11290
case key.Matches(msg, modelKeys.Enter):
91+
selectedItem, _ := m.modelList.GetSelectedItem()
11392
models := m.models()
93+
var selectedModel client.ModelInfo
94+
for _, model := range models {
95+
if model.Name == string(selectedItem) {
96+
selectedModel = model
97+
break
98+
}
99+
}
114100
return m, tea.Sequence(
115101
util.CmdHandler(modal.CloseModalMsg{}),
116102
util.CmdHandler(
117103
app.ModelSelectedMsg{
118104
Provider: m.provider,
119-
Model: models[m.selectedIdx],
105+
Model: selectedModel,
120106
}),
121107
)
122108
case key.Matches(msg, modelKeys.Escape):
@@ -127,7 +113,10 @@ func (m *modelDialog) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
127113
m.height = msg.Height
128114
}
129115

130-
return m, nil
116+
// Update the list component
117+
updatedList, cmd := m.modelList.Update(msg)
118+
m.modelList = updatedList.(list.List[list.StringItem])
119+
return m, cmd
131120
}
132121

133122
func (m *modelDialog) models() []client.ModelInfo {
@@ -137,40 +126,9 @@ func (m *modelDialog) models() []client.ModelInfo {
137126
return models
138127
}
139128

140-
// moveSelectionUp moves the selection up or wraps to bottom
141-
func (m *modelDialog) moveSelectionUp() {
142-
if m.selectedIdx > 0 {
143-
m.selectedIdx--
144-
} else {
145-
m.selectedIdx = len(m.provider.Models) - 1
146-
m.scrollOffset = max(0, len(m.provider.Models)-numVisibleModels)
147-
}
148-
149-
// Keep selection visible
150-
if m.selectedIdx < m.scrollOffset {
151-
m.scrollOffset = m.selectedIdx
152-
}
153-
}
154-
155-
// moveSelectionDown moves the selection down or wraps to top
156-
func (m *modelDialog) moveSelectionDown() {
157-
if m.selectedIdx < len(m.provider.Models)-1 {
158-
m.selectedIdx++
159-
} else {
160-
m.selectedIdx = 0
161-
m.scrollOffset = 0
162-
}
163-
164-
// Keep selection visible
165-
if m.selectedIdx >= m.scrollOffset+numVisibleModels {
166-
m.scrollOffset = m.selectedIdx - (numVisibleModels - 1)
167-
}
168-
}
169-
170129
func (m *modelDialog) switchProvider(offset int) {
171130
newOffset := m.hScrollOffset + offset
172131

173-
// Ensure we stay within bounds
174132
if newOffset < 0 {
175133
newOffset = len(m.availableProviders) - 1
176134
}
@@ -185,105 +143,46 @@ func (m *modelDialog) switchProvider(offset int) {
185143
}
186144

187145
func (m *modelDialog) View() string {
188-
t := theme.CurrentTheme()
189-
baseStyle := lipgloss.NewStyle().
190-
Background(t.BackgroundElement()).
191-
Foreground(t.Text())
192-
193-
// Render visible models
194-
endIdx := min(m.scrollOffset+numVisibleModels, len(m.provider.Models))
195-
modelItems := make([]string, 0, endIdx-m.scrollOffset)
196-
197-
models := m.models()
198-
for i := m.scrollOffset; i < endIdx; i++ {
199-
itemStyle := baseStyle.Width(maxDialogWidth)
200-
if i == m.selectedIdx {
201-
itemStyle = itemStyle.
202-
Background(t.Primary()).
203-
Foreground(t.BackgroundElement()).
204-
Bold(true)
205-
}
206-
modelItems = append(modelItems, itemStyle.Render(models[i].Name))
207-
}
208-
146+
listView := m.modelList.View()
209147
scrollIndicator := m.getScrollIndicators(maxDialogWidth)
210-
211-
content := lipgloss.JoinVertical(
212-
lipgloss.Left,
213-
baseStyle.
214-
Width(maxDialogWidth).
215-
Render(lipgloss.JoinVertical(lipgloss.Left, modelItems...)),
216-
scrollIndicator,
217-
)
218-
219-
return content
148+
return strings.Join([]string{listView, scrollIndicator}, "\n")
220149
}
221150

222151
func (m *modelDialog) getScrollIndicators(maxWidth int) string {
223152
var indicator string
224-
225-
if len(m.provider.Models) > numVisibleModels {
226-
if m.scrollOffset > 0 {
227-
indicator += "↑ "
228-
}
229-
if m.scrollOffset+numVisibleModels < len(m.provider.Models) {
230-
indicator += "↓ "
231-
}
232-
}
233-
234153
if m.hScrollPossible {
235-
indicator = "← " + indicator + "→"
154+
indicator = "← → (switch provider) "
236155
}
237-
238156
if indicator == "" {
239157
return ""
240158
}
241159

242160
t := theme.CurrentTheme()
243-
baseStyle := styles.BaseStyle()
244-
245-
return baseStyle.
246-
Foreground(t.Primary()).
161+
return styles.BaseStyle().
162+
Foreground(t.TextMuted()).
247163
Width(maxWidth).
248164
Align(lipgloss.Right).
249-
Bold(true).
250165
Render(indicator)
251166
}
252167

253-
// findProviderIndex returns the index of the provider in the list, or -1 if not found
254-
// func findProviderIndex(providers []string, provider string) int {
255-
// for i, p := range providers {
256-
// if p == provider {
257-
// return i
258-
// }
259-
// }
260-
// return -1
261-
// }
262-
263-
func (m *modelDialog) setupModelsForProvider(_ string) {
264-
m.selectedIdx = 0
265-
m.scrollOffset = 0
266-
267-
// cfg := config.Get()
268-
// agentCfg := cfg.Agents[config.AgentPrimary]
269-
// selectedModelId := agentCfg.Model
168+
func (m *modelDialog) setupModelsForProvider(providerId string) {
169+
models := m.models()
170+
modelNames := make([]string, len(models))
171+
for i, model := range models {
172+
modelNames[i] = model.Name
173+
}
270174

271-
// m.provider = provider
272-
// m.models = getModelsForProvider(provider)
175+
m.modelList = list.NewStringList(modelNames, numVisibleModels, "No models available", true)
176+
m.modelList.SetMaxWidth(maxDialogWidth)
273177

274-
// Try to select the current model if it belongs to this provider
275-
// if provider == models.SupportedModels[selectedModelId].Provider {
276-
// for i, model := range m.models {
277-
// if model.ID == selectedModelId {
278-
// m.selectedIdx = i
279-
// // Adjust scroll position to keep selected model visible
280-
// if m.selectedIdx >= numVisibleModels {
281-
// m.scrollOffset = m.selectedIdx - (numVisibleModels - 1)
282-
// }
283-
// break
284-
// }
285-
// }
286-
// }
178+
if m.app.Provider != nil && m.app.Model != nil && m.app.Provider.Id == providerId {
179+
for i, model := range models {
180+
if model.Id == m.app.Model.Id {
181+
m.modelList.SetSelectedIndex(i)
182+
break
183+
}
184+
}
185+
}
287186
}
288187

289188
func (m *modelDialog) Render(background string) string {
@@ -297,11 +196,30 @@ func (s *modelDialog) Close() tea.Cmd {
297196
func NewModelDialog(app *app.App) ModelDialog {
298197
availableProviders, _ := app.ListProviders(context.Background())
299198

300-
return &modelDialog{
199+
currentProvider := availableProviders[0]
200+
hScrollOffset := 0
201+
if app.Provider != nil {
202+
for i, provider := range availableProviders {
203+
if provider.Id == app.Provider.Id {
204+
currentProvider = provider
205+
hScrollOffset = i
206+
break
207+
}
208+
}
209+
}
210+
211+
dialog := &modelDialog{
212+
app: app,
301213
availableProviders: availableProviders,
302-
hScrollOffset: 0,
214+
hScrollOffset: hScrollOffset,
303215
hScrollPossible: len(availableProviders) > 1,
304-
provider: availableProviders[0],
305-
modal: modal.New(modal.WithTitle(fmt.Sprintf("Select %s Model", availableProviders[0].Name))),
216+
provider: currentProvider,
217+
modal: modal.New(
218+
modal.WithTitle(fmt.Sprintf("Select %s Model", currentProvider.Name)),
219+
modal.WithMaxWidth(maxDialogWidth+4),
220+
),
306221
}
222+
223+
dialog.setupModelsForProvider(currentProvider.Id)
224+
return dialog
307225
}

0 commit comments

Comments
 (0)