11// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22// SPDX-License-Identifier: Apache-2.0
33
4+ #![ warn( missing_debug_implementations) ]
5+
46use std:: collections:: HashMap ;
57use std:: time:: { Duration , Instant } ;
68
@@ -368,11 +370,13 @@ pub struct CreateProviderForm {
368370 pub credentials : Vec < ( String , String ) > ,
369371 /// Which credential row is focused.
370372 pub cred_cursor : usize ,
371- /// TODO: inline doc for config, possibilities of using IndexMap
373+ /// Provider config key-value pairs (e.g. `ANTHROPIC_BASE_URL`).
372374 pub config : IndexMap < String , String > ,
373- /// Which config row is focused .
375+ /// Which existing config entry is selected (for deletion) .
374376 pub config_cursor : usize ,
377+ /// Config key being entered.
375378 pub config_key_input : String ,
379+ /// Config value being entered.
376380 pub config_value_input : String ,
377381 /// For generic / types with no known env vars: custom env var name.
378382 pub generic_env_name : String ,
@@ -508,9 +512,22 @@ pub struct UpdateProviderForm {
508512 pub provider_type : String ,
509513 pub credential_key : String ,
510514 pub new_value : String ,
515+ pub config : IndexMap < String , String > ,
516+ pub config_key_input : String ,
517+ pub config_value_input : String ,
518+ pub config_cursor : usize ,
519+ pub focus : UpdateProviderField ,
511520 pub status : Option < String > ,
512521}
513522
523+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
524+ pub enum UpdateProviderField {
525+ CredentialValue ,
526+ ConfigKey ,
527+ ConfigValue ,
528+ Submit ,
529+ }
530+
514531// ---------------------------------------------------------------------------
515532// App state
516533// ---------------------------------------------------------------------------
@@ -2358,9 +2375,8 @@ impl App {
23582375 . cloned ( )
23592376 . unwrap_or_default ( ) ;
23602377 form. config . shift_remove ( & key_to_remove) ;
2361- form. config_cursor = form
2362- . config_cursor
2363- . min ( form. config . len ( ) . saturating_sub ( 1 ) ) ;
2378+ form. config_cursor =
2379+ form. config_cursor . min ( form. config . len ( ) . saturating_sub ( 1 ) ) ;
23642380 }
23652381 }
23662382 _ => {
@@ -2503,6 +2519,17 @@ impl App {
25032519 . get ( self . provider_selected )
25042520 . cloned ( )
25052521 . unwrap_or_default ( ) ;
2522+ let existing_config = self
2523+ . provider_entries
2524+ . get ( self . provider_selected )
2525+ . map ( |e| {
2526+ e. provider
2527+ . config
2528+ . iter ( )
2529+ . map ( |( k, v) | ( k. clone ( ) , v. clone ( ) ) )
2530+ . collect :: < IndexMap < _ , _ > > ( )
2531+ } )
2532+ . unwrap_or_default ( ) ;
25062533
25072534 // If we don't know the credential key, derive from registry.
25082535 let key = if cred_key. is_empty ( ) {
@@ -2520,6 +2547,11 @@ impl App {
25202547 provider_type : ptype,
25212548 credential_key : key,
25222549 new_value : String :: new ( ) ,
2550+ config : existing_config,
2551+ config_key_input : String :: new ( ) ,
2552+ config_value_input : String :: new ( ) ,
2553+ config_cursor : 0 ,
2554+ focus : UpdateProviderField :: CredentialValue ,
25232555 status : None ,
25242556 } ) ;
25252557 }
@@ -2533,18 +2565,100 @@ impl App {
25332565 KeyCode :: Esc => {
25342566 self . update_provider_form = None ;
25352567 }
2536- KeyCode :: Enter => {
2537- if form. new_value . is_empty ( ) {
2538- form. status = Some ( "Value is required." . to_string ( ) ) ;
2539- return ;
2568+ KeyCode :: Tab => match form. focus {
2569+ UpdateProviderField :: CredentialValue => {
2570+ form. focus = UpdateProviderField :: ConfigKey ;
25402571 }
2541- self . pending_provider_update = true ;
2542- }
2543- KeyCode :: Char ( c) => form. new_value . push ( c) ,
2544- KeyCode :: Backspace => {
2545- form. new_value . pop ( ) ;
2546- }
2547- _ => { }
2572+ UpdateProviderField :: ConfigKey => {
2573+ form. focus = UpdateProviderField :: ConfigValue ;
2574+ }
2575+ UpdateProviderField :: ConfigValue => {
2576+ if !form. config_key_input . is_empty ( ) && !form. config_value_input . is_empty ( ) {
2577+ form. config . insert (
2578+ std:: mem:: take ( & mut form. config_key_input ) ,
2579+ std:: mem:: take ( & mut form. config_value_input ) ,
2580+ ) ;
2581+ form. focus = UpdateProviderField :: ConfigKey ;
2582+ } else if form. config_key_input . is_empty ( ) && form. config_value_input . is_empty ( )
2583+ {
2584+ form. focus = UpdateProviderField :: Submit ;
2585+ } else {
2586+ form. focus = UpdateProviderField :: ConfigKey ;
2587+ }
2588+ }
2589+ UpdateProviderField :: Submit => {
2590+ form. focus = UpdateProviderField :: CredentialValue ;
2591+ }
2592+ } ,
2593+ KeyCode :: BackTab => match form. focus {
2594+ UpdateProviderField :: CredentialValue => {
2595+ form. focus = UpdateProviderField :: Submit ;
2596+ }
2597+ UpdateProviderField :: ConfigKey => {
2598+ form. focus = UpdateProviderField :: CredentialValue ;
2599+ }
2600+ UpdateProviderField :: ConfigValue => {
2601+ form. focus = UpdateProviderField :: ConfigKey ;
2602+ }
2603+ UpdateProviderField :: Submit => {
2604+ form. focus = UpdateProviderField :: ConfigValue ;
2605+ }
2606+ } ,
2607+ _ => match form. focus {
2608+ UpdateProviderField :: CredentialValue => {
2609+ Self :: handle_text_input ( & mut form. new_value , key) ;
2610+ }
2611+ UpdateProviderField :: ConfigKey => match key. code {
2612+ KeyCode :: Enter => {
2613+ if !form. config_key_input . is_empty ( ) && !form. config_value_input . is_empty ( )
2614+ {
2615+ form. config . insert (
2616+ std:: mem:: take ( & mut form. config_key_input ) ,
2617+ std:: mem:: take ( & mut form. config_value_input ) ,
2618+ ) ;
2619+ }
2620+ }
2621+ KeyCode :: Char ( 'd' ) if key. modifiers . contains ( KeyModifiers :: CONTROL ) => {
2622+ if !form. config . is_empty ( ) {
2623+ let key_to_remove = form
2624+ . config
2625+ . keys ( )
2626+ . nth ( form. config_cursor )
2627+ . cloned ( )
2628+ . unwrap_or_default ( ) ;
2629+ form. config . shift_remove ( & key_to_remove) ;
2630+ form. config_cursor =
2631+ form. config_cursor . min ( form. config . len ( ) . saturating_sub ( 1 ) ) ;
2632+ }
2633+ }
2634+ _ => {
2635+ Self :: handle_text_input ( & mut form. config_key_input , key) ;
2636+ }
2637+ } ,
2638+ UpdateProviderField :: ConfigValue => match key. code {
2639+ KeyCode :: Enter => {
2640+ if !form. config_key_input . is_empty ( ) && !form. config_value_input . is_empty ( )
2641+ {
2642+ form. config . insert (
2643+ std:: mem:: take ( & mut form. config_key_input ) ,
2644+ std:: mem:: take ( & mut form. config_value_input ) ,
2645+ ) ;
2646+ }
2647+ }
2648+ _ => {
2649+ Self :: handle_text_input ( & mut form. config_value_input , key) ;
2650+ }
2651+ } ,
2652+ UpdateProviderField :: Submit => {
2653+ if key. code == KeyCode :: Enter {
2654+ if form. new_value . is_empty ( ) {
2655+ form. status = Some ( "Value is required." . to_string ( ) ) ;
2656+ return ;
2657+ }
2658+ self . pending_provider_update = true ;
2659+ }
2660+ }
2661+ } ,
25482662 }
25492663 }
25502664
0 commit comments