@@ -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
4745type 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
5652var 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
8371func (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
9676func (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
133122func (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-
170129func (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
187145func (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
222151func (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
289188func (m * modelDialog ) Render (background string ) string {
@@ -297,11 +196,30 @@ func (s *modelDialog) Close() tea.Cmd {
297196func 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