@@ -165,13 +165,17 @@ def _build(self) -> None:
165165 self .grid_columnconfigure (0 , weight = 1 )
166166 self .grid_rowconfigure (4 , weight = 1 )
167167
168- header = ctk .CTkFrame (self , fg_color = COLORS ["bg" ], height = 120 )
168+ header = ctk .CTkFrame (self , fg_color = COLORS ["bg" ], height = 160 )
169169 header .grid (row = 0 , column = 0 , sticky = "ew" , padx = 0 , pady = 0 )
170170 header .grid_columnconfigure (0 , weight = 1 )
171171 header .grid_propagate (False )
172172
173- _CTkLabelTitle (header , text = "Setup" ).grid (row = 0 , column = 0 , padx = (40 , 40 ), pady = (30 , 2 ), sticky = "w" )
174- _CTkLabelSubtitle (header , text = "Configure your OpenCode Telegram Bot" ).grid (row = 1 , column = 0 , padx = (40 , 40 ), pady = (0 , 10 ), sticky = "w" )
173+ _CTkLabelTitle (header , text = "Setup" ).grid (row = 0 , column = 0 , padx = (40 , 40 ), pady = (20 , 2 ), sticky = "w" )
174+ _CTkLabelSubtitle (header , text = "Configure your OpenCode Telegram Bot" ).grid (row = 1 , column = 0 , padx = (40 , 40 ), pady = (0 , 6 ), sticky = "w" )
175+ intro_text = ctk .CTkTextbox (header , height = 48 , corner_radius = 0 , border_width = 0 , fg_color = COLORS ["bg" ], text_color = COLORS ["text_secondary" ], font = ctk .CTkFont (size = 13 ))
176+ intro_text .grid (row = 2 , column = 0 , padx = (40 , 40 ), pady = (0 , 8 ), sticky = "ew" )
177+ intro_text .insert ("1.0" , "Connect your Telegram bot to OpenCode AI. Get your bot token from @BotFather and user ID from @userinfobot. Then enter your OpenCode server details below to link them together." )
178+ intro_text .configure (state = "disabled" )
175179
176180 content = ctk .CTkScrollableFrame (self , fg_color = COLORS ["bg" ])
177181 content .grid (row = 1 , column = 0 , sticky = "nsew" , padx = 40 , pady = 10 )
@@ -262,6 +266,8 @@ def __init__(self, master: Any, settings: Settings) -> None:
262266 self ._bot_app = None
263267 self ._bot_handler = None
264268 self ._alive = True
269+ self .selected_model = ""
270+ self ._available_models : list [str ] = []
265271
266272 self ._build ()
267273 self ._start_status_poll ()
@@ -308,7 +314,9 @@ def _build(self) -> None:
308314
309315 status_bar = ctk .CTkFrame (self , fg_color = COLORS ["bg_secondary" ], corner_radius = 16 , height = 56 )
310316 status_bar .grid (row = 1 , column = 0 , columnspan = 2 , padx = 40 , pady = (0 , 16 ), sticky = "ew" )
311- status_bar .grid_columnconfigure ((0 , 1 , 2 ), weight = 1 )
317+ status_bar .grid_columnconfigure (0 , weight = 1 )
318+ status_bar .grid_columnconfigure (1 , weight = 1 )
319+ status_bar .grid_columnconfigure (2 , weight = 1 )
312320 status_bar .grid_propagate (False )
313321
314322 self .server_label = _CTkLabelSmall (status_bar , text = "Server: Checking..." , anchor = "w" )
@@ -317,8 +325,32 @@ def _build(self) -> None:
317325 self .bot_label = _CTkLabelSmall (status_bar , text = "Bot: Stopped" , anchor = "w" )
318326 self .bot_label .grid (row = 0 , column = 1 , padx = 10 , pady = 16 , sticky = "w" )
319327
320- self .model_label = _CTkLabelSmall (status_bar , text = f"Model: { self .settings .opencode_model_id } " , anchor = "w" )
321- self .model_label .grid (row = 0 , column = 2 , padx = (10 , 20 ), pady = 16 , sticky = "w" )
328+ model_bar = ctk .CTkFrame (status_bar , fg_color = COLORS ["bg_secondary" ])
329+ model_bar .grid (row = 0 , column = 2 , padx = (10 , 20 ), pady = 16 , sticky = "w" )
330+ model_bar .grid_columnconfigure (0 , weight = 0 )
331+
332+ self .model_label = _CTkLabelSmall (model_bar , text = f"Model: { self .settings .opencode_model_id } " , anchor = "w" )
333+ self .model_label .grid (row = 0 , column = 0 , padx = (0 , 8 ), sticky = "w" )
334+
335+ self .model_var = ctk .StringVar (value = "Loading..." )
336+ self .model_dropdown = ctk .CTkOptionMenu (
337+ model_bar ,
338+ variable = self .model_var ,
339+ values = ["Loading..." ],
340+ command = self ._on_model_selected ,
341+ width = 180 ,
342+ height = 32 ,
343+ corner_radius = 8 ,
344+ fg_color = COLORS ["bg" ],
345+ button_color = COLORS ["border" ],
346+ button_hover_color = COLORS ["accent" ],
347+ text_color = COLORS ["text_primary" ],
348+ dropdown_fg_color = COLORS ["bg" ],
349+ dropdown_text_color = COLORS ["text_primary" ],
350+ dropdown_hover_color = COLORS ["bg_secondary" ],
351+ font = ctk .CTkFont (size = 12 ),
352+ )
353+ self .model_dropdown .grid (row = 0 , column = 1 , sticky = "w" )
322354
323355 ctrl_frame = ctk .CTkFrame (self , fg_color = COLORS ["bg" ], height = 60 )
324356 ctrl_frame .grid (row = 2 , column = 0 , columnspan = 2 , padx = 40 , pady = (0 , 16 ), sticky = "ew" )
@@ -352,7 +384,7 @@ def _build(self) -> None:
352384 self .sessions_text = ctk .CTkTextbox (left_panel , height = 150 , corner_radius = 12 , border_width = 1 , border_color = COLORS ["border" ], fg_color = COLORS ["bg_secondary" ], text_color = COLORS ["text_primary" ], font = ctk .CTkFont (size = 13 ))
353385 self .sessions_text .grid (row = 1 , column = 0 , sticky = "ew" , pady = (0 , 16 ))
354386
355- _CTkLabel (left_panel , text = "Models" , font = ctk .CTkFont (size = 18 , weight = "bold" ), anchor = "w" ).grid (row = 2 , column = 0 , pady = (0 , 8 ), sticky = "w" )
387+ _CTkLabel (left_panel , text = "Available Models" , font = ctk .CTkFont (size = 18 , weight = "bold" ), anchor = "w" ).grid (row = 2 , column = 0 , pady = (0 , 8 ), sticky = "w" )
356388 self .models_frame = ctk .CTkScrollableFrame (left_panel , fg_color = COLORS ["bg_secondary" ], corner_radius = 12 , height = 250 )
357389 self .models_frame .grid (row = 3 , column = 0 , sticky = "nsew" )
358390
@@ -586,7 +618,13 @@ def fetch():
586618 try :
587619 loop = asyncio .new_event_loop ()
588620 asyncio .set_event_loop (loop )
589- providers = loop .run_until_complete (self .client .get_config_providers ())
621+ try :
622+ providers = loop .run_until_complete (self .client .get_config_providers ())
623+ except Exception :
624+ try :
625+ providers = loop .run_until_complete (self .client .get_providers ())
626+ except Exception :
627+ providers = {}
590628 loop .close ()
591629 self ._safe_after (0 , self ._render_models , providers )
592630 except Exception as exc :
@@ -603,14 +641,18 @@ def _render_models(self, providers_data: dict[str, Any]) -> None:
603641 if not isinstance (providers , list ):
604642 providers = []
605643
644+ model_choices = []
645+
606646 for p in providers :
607647 pid = p .get ("id" , p .get ("name" , "unknown" ))
608648 models = p .get ("models" , [])
609649 if not isinstance (models , list ):
610650 models = []
611651 _CTkLabel (self .models_frame , text = pid , font = ctk .CTkFont (weight = "bold" , size = 13 ), text_color = COLORS ["text_primary" ]).pack (anchor = "w" , padx = 12 , pady = (12 , 4 ))
612- for m in models [: 8 ] :
652+ for m in models :
613653 mid = m .get ("id" , m .get ("name" , "" ))
654+ full = f"{ pid } /{ mid } "
655+ model_choices .append (full )
614656 btn = ctk .CTkButton (
615657 self .models_frame ,
616658 text = mid ,
@@ -627,12 +669,33 @@ def _render_models(self, providers_data: dict[str, Any]) -> None:
627669 )
628670 btn .pack (anchor = "w" , padx = 12 , pady = 2 )
629671
630- if not providers :
672+ if not model_choices :
631673 _CTkLabelSubtitle (self .models_frame , text = "No models found. Configure providers in OpenCode." ).pack (pady = 20 )
674+ model_choices = ["No models available" ]
675+
676+ self ._available_models = model_choices
677+ self .model_dropdown .configure (values = model_choices )
678+
679+ if self .selected_model and self .selected_model in model_choices :
680+ self .model_var .set (self .selected_model )
681+ elif model_choices and model_choices [0 ] != "No models available" :
682+ self .model_var .set (model_choices [0 ])
683+ self .selected_model = model_choices [0 ]
684+ else :
685+ self .model_var .set ("No models available" )
686+
687+ def _on_model_selected (self , choice : str ) -> None :
688+ if choice and choice != "No models available" and choice != "Loading..." :
689+ self .selected_model = choice
690+ self .model_label .configure (text = f"Model: { choice } " )
691+ self ._append_log (f"Model switched to: { choice } " )
632692
633693 def _switch_model (self , provider : str , model : str ) -> None :
634- self .model_label .configure (text = f"Model: { provider } /{ model } " )
635- self ._append_log (f"Model selected: { provider } /{ model } " )
694+ full = f"{ provider } /{ model } "
695+ self .selected_model = full
696+ self .model_label .configure (text = f"Model: { full } " )
697+ self .model_var .set (full )
698+ self ._append_log (f"Model selected: { full } " )
636699
637700
638701class App (ctk .CTk ):
0 commit comments