@@ -135,23 +135,52 @@ def _pick_one(prompt_text: str, choices: list[str]) -> str:
135135 return choices [_pick_one_idx (prompt_text , choices )]
136136
137137
138+ def _make_menu (
139+ choices : list [str ],
140+ title : str ,
141+ * ,
142+ multi_select : bool = False ,
143+ type_to_search : bool = True ,
144+ ):
145+ from simple_term_menu import TerminalMenu
146+
147+ kwargs : dict = {
148+ "menu_cursor" : "❯ " ,
149+ "menu_cursor_style" : ("fg_cyan" , "bold" ),
150+ "menu_highlight_style" : ("fg_cyan" , "bold" ),
151+ "search_highlight_style" : ("fg_yellow" , "bold" ),
152+ }
153+ if multi_select :
154+ kwargs .update (
155+ multi_select = True ,
156+ multi_select_select_on_accept = False ,
157+ multi_select_keys = ("tab" ,),
158+ )
159+ if type_to_search :
160+ kwargs ["search_key" ] = None
161+ return TerminalMenu (choices , title = title , ** kwargs )
162+
163+
164+ def _show_menu (menu , choices : list [str ], fallback_title : str , ** kw ):
165+ """Show a menu, falling back to /-triggered search if the library crashes."""
166+ try :
167+ return menu .show ()
168+ except (ValueError , OSError ):
169+ # simple_term_menu can crash when search text exceeds terminal width;
170+ # recover by retrying without live search
171+ fallback = _make_menu (choices , fallback_title , type_to_search = False , ** kw )
172+ return fallback .show ()
173+
174+
138175def _pick_one_idx (prompt_text : str , choices : list [str ]) -> int :
139176 """Arrow-key single selection with type-to-search, returns the chosen index."""
140177 if not sys .stdin .isatty ():
141178 error ("Interactive selection requires a terminal. Provide explicit flags instead." )
142179 raise typer .Exit (1 )
143- from simple_term_menu import TerminalMenu
144-
145- menu = TerminalMenu (
146- choices ,
147- title = f"\n { prompt_text } \n ↑/↓ navigate · type to search · enter confirm" ,
148- menu_cursor = "❯ " ,
149- menu_cursor_style = ("fg_cyan" , "bold" ),
150- menu_highlight_style = ("fg_cyan" , "bold" ),
151- search_highlight_style = ("fg_yellow" , "bold" ),
152- search_key = None ,
153- )
154- idx = menu .show ()
180+ title = f"\n { prompt_text } \n ↑/↓ navigate · type to search · enter confirm"
181+ fallback_title = f"\n { prompt_text } \n ↑/↓ navigate · / to search · enter confirm"
182+ menu = _make_menu (choices , title )
183+ idx = _show_menu (menu , choices , fallback_title )
155184 if idx is None :
156185 raise typer .Abort ()
157186 return idx
@@ -162,22 +191,11 @@ def _pick_many(prompt_text: str, choices: list[str]) -> list[str]:
162191 if not sys .stdin .isatty ():
163192 error ("Interactive selection requires a terminal. Provide explicit flags instead." )
164193 raise typer .Exit (1 )
165- from simple_term_menu import TerminalMenu
166-
167194 display = ["(all)" , * choices ]
168- menu = TerminalMenu (
169- display ,
170- title = f"\n { prompt_text } \n ↑/↓ navigate · tab select · type to search · enter confirm" ,
171- multi_select = True ,
172- multi_select_select_on_accept = False ,
173- multi_select_keys = ("tab" ,),
174- menu_cursor = "❯ " ,
175- menu_cursor_style = ("fg_cyan" , "bold" ),
176- menu_highlight_style = ("fg_cyan" , "bold" ),
177- search_highlight_style = ("fg_yellow" , "bold" ),
178- search_key = None ,
179- )
180- result = menu .show ()
195+ title = f"\n { prompt_text } \n ↑/↓ navigate · tab select · type to search · enter confirm"
196+ fallback_title = f"\n { prompt_text } \n ↑/↓ navigate · tab select · / to search · enter confirm"
197+ menu = _make_menu (display , title , multi_select = True )
198+ result = _show_menu (menu , display , fallback_title , multi_select = True )
181199 if result is None :
182200 raise typer .Abort ()
183201 selected = menu .chosen_menu_entries
0 commit comments