@@ -152,10 +152,11 @@ func (m setupModel) handleKey(msg tea.KeyMsg) (setupModel, tea.Cmd) {
152152
153153 switch m .step {
154154 case stepProvider :
155- return m .handleArrowSelect (key , len (providers ), & m .providerIdx , func () (setupModel , tea.Cmd ) {
155+ newIdx , entered := arrowSelect (key , len (providers ), m .providerIdx )
156+ m .providerIdx = newIdx
157+ if entered {
156158 p := providers [m .providerIdx ]
157159 if p .baseURL == "" {
158- // Custom URL
159160 m .step = stepCustomURL
160161 m .input .SetValue ("" )
161162 m .input .Placeholder = "https://your-api.example.com/v1"
@@ -169,7 +170,8 @@ func (m setupModel) handleKey(msg tea.KeyMsg) (setupModel, tea.Cmd) {
169170 m .input .EchoMode = textinput .EchoPassword
170171 m .input .Focus ()
171172 return m , textinput .Blink
172- })
173+ }
174+ return m , nil
173175
174176 case stepCustomURL :
175177 if key == "enter" {
@@ -259,13 +261,17 @@ func (m setupModel) handleKey(msg tea.KeyMsg) (setupModel, tea.Cmd) {
259261 m .input , cmd = m .input .Update (msg )
260262 return m , cmd
261263 }
262- return m .handleArrowSelect (key , len (m .models ), & m .modelIdx , func () (setupModel , tea.Cmd ) {
264+ newIdx , entered := arrowSelect (key , len (m .models ), m .modelIdx )
265+ m .modelIdx = newIdx
266+ if entered {
263267 m .step = stepGlueChoice
264- return m , nil
265- })
268+ }
269+ return m , nil
266270
267271 case stepGlueChoice :
268- return m .handleArrowSelect (key , len (glueChoices ), & m .glueChoice , func () (setupModel , tea.Cmd ) {
272+ newIdx , entered := arrowSelect (key , len (glueChoices ), m .glueChoice )
273+ m .glueChoice = newIdx
274+ if entered {
269275 switch m .glueChoice {
270276 case 0 : // Same provider
271277 m .glueBaseURL = m .baseURL
@@ -280,11 +286,13 @@ func (m setupModel) handleKey(msg tea.KeyMsg) (setupModel, tea.Cmd) {
280286 case 2 : // Skip
281287 return m , m .saveAndFinish ()
282288 }
283- return m , nil
284- })
289+ }
290+ return m , nil
285291
286292 case stepGlueProvider :
287- return m .handleArrowSelect (key , len (providers ), & m .glueProviderIdx , func () (setupModel , tea.Cmd ) {
293+ newIdx , entered := arrowSelect (key , len (providers ), m .glueProviderIdx )
294+ m .glueProviderIdx = newIdx
295+ if entered {
288296 p := providers [m .glueProviderIdx ]
289297 if p .baseURL == "" {
290298 m .step = stepGlueCustomURL
@@ -300,7 +308,8 @@ func (m setupModel) handleKey(msg tea.KeyMsg) (setupModel, tea.Cmd) {
300308 m .input .EchoMode = textinput .EchoPassword
301309 m .input .Focus ()
302310 return m , textinput .Blink
303- })
311+ }
312+ return m , nil
304313
305314 case stepGlueCustomURL :
306315 if key == "enter" {
@@ -375,10 +384,11 @@ func (m setupModel) handleKey(msg tea.KeyMsg) (setupModel, tea.Cmd) {
375384 m .input , cmd = m .input .Update (msg )
376385 return m , cmd
377386 }
378- return m .handleArrowSelect (key , len (m .glueModels ), & m .glueFastIdx , func () (setupModel , tea.Cmd ) {
387+ newIdx , entered := arrowSelect (key , len (m .glueModels ), m .glueFastIdx )
388+ m .glueFastIdx = newIdx
389+ if entered {
379390 m .step = stepGlueSmartModel
380391 m .glueSmartIdx = 0
381- // Pre-select a smarter model if possible
382392 for i , model := range m .glueModels {
383393 lower := strings .ToLower (model )
384394 if strings .Contains (lower , "70b" ) || strings .Contains (lower , "4o" ) ||
@@ -387,8 +397,8 @@ func (m setupModel) handleKey(msg tea.KeyMsg) (setupModel, tea.Cmd) {
387397 break
388398 }
389399 }
390- return m , nil
391- })
400+ }
401+ return m , nil
392402
393403 case stepGlueSmartModel :
394404 if m .manualEntry {
@@ -397,12 +407,9 @@ func (m setupModel) handleKey(msg tea.KeyMsg) (setupModel, tea.Cmd) {
397407 if val == "" {
398408 return m , nil
399409 }
400- // Save: glueFast was already set, now set glueSmart
401410 m .glueSmartIdx = 0
402- // We need to store the manual smart model separately
403411 m .input .Blur ()
404412 m .manualEntry = false
405- // Build saved config with manual entries
406413 sc := m .buildSavedConfig ()
407414 sc .GlueSmartModel = val
408415 return m , func () tea.Msg { return setupDoneMsg {config : sc } }
@@ -411,36 +418,35 @@ func (m setupModel) handleKey(msg tea.KeyMsg) (setupModel, tea.Cmd) {
411418 m .input , cmd = m .input .Update (msg )
412419 return m , cmd
413420 }
414- return m .handleArrowSelect (key , len (m .glueModels ), & m .glueSmartIdx , func () (setupModel , tea.Cmd ) {
421+ newIdx , entered := arrowSelect (key , len (m .glueModels ), m .glueSmartIdx )
422+ m .glueSmartIdx = newIdx
423+ if entered {
415424 return m , m .saveAndFinish ()
416- })
425+ }
426+ return m , nil
417427 }
418428
419429 return m , nil
420430}
421431
422- func (m setupModel ) handleArrowSelect (key string , count int , idx * int , onEnter func () (setupModel , tea.Cmd )) (setupModel , tea.Cmd ) {
432+ // arrowSelect handles arrow/vim key navigation. Returns the new index and
433+ // whether Enter was pressed. This is a free function (not a method) to avoid
434+ // the Go value-receiver copy bug where pointer modifications to a caller's
435+ // struct field are lost when the method returns its own stale copy of m.
436+ func arrowSelect (key string , count int , idx int ) (int , bool ) {
423437 switch key {
424- case "up" , "k" :
425- if * idx > 0 {
426- * idx --
427- }
428- case "down" , "j" :
429- if * idx < count - 1 {
430- * idx ++
438+ case "up" , "k" , "left" , "h" :
439+ if idx > 0 {
440+ idx --
431441 }
432- case "left" , "h" :
433- if * idx > 0 {
434- * idx --
435- }
436- case "right" , "l" :
437- if * idx < count - 1 {
438- * idx ++
442+ case "down" , "j" , "right" , "l" :
443+ if idx < count - 1 {
444+ idx ++
439445 }
440446 case "enter" :
441- return onEnter ()
447+ return idx , true
442448 }
443- return m , nil
449+ return idx , false
444450}
445451
446452func (m setupModel ) handleModelsFetched (msg modelsFetchedMsg ) (setupModel , tea.Cmd ) {
0 commit comments