1010from menu_kit .core .database import Database , ItemType , MenuItem
1111from menu_kit .core .display_mode import DisplayMode , DisplayModeManager
1212from menu_kit .menu .base import GUI_BACKENDS , get_backend
13+ from menu_kit .plugins .base import ActionResult
1314from menu_kit .plugins .loader import PluginLoader
1415
1516if TYPE_CHECKING :
@@ -221,34 +222,33 @@ def _run_menu(self) -> int:
221222
222223 result = self .backend .show (items , prompt = "menu-kit" )
223224
224- # Exit only when cancelled from main menu
225+ # ESC at main menu level exits the app
225226 if result .cancelled or result .selected is None :
226227 return EXIT_CANCELLED
227228
228229 item = result .selected
229230
230231 # Handle submenu entry selection
231232 if item .id .startswith ("_submenu:" ):
232- from menu_kit .plugins .base import MenuCancelled
233-
234233 plugin_name = item .id [9 :] # Remove "_submenu:" prefix
235- try :
236- if self ._show_plugin_submenu (plugin_name , display_manager ):
237- return EXIT_SUCCESS # Plugin was executed, exit
238- except MenuCancelled :
239- return EXIT_CANCELLED # ESC pressed, exit
234+ submenu_result = self ._show_plugin_submenu (plugin_name , display_manager )
235+ if submenu_result == ActionResult .CLOSE :
236+ return EXIT_SUCCESS
237+ # BACK or None: continue loop (show main menu again)
240238 continue
241239
242240 # Record usage
243241 if self .config .frequency_tracking :
244242 self .database .record_use (item .id )
245243
246- # Execute plugin and exit (launcher behavior)
244+ # Execute plugin
247245 if item .plugin :
248246 action = ""
249247 if ":" in item .id :
250248 _ , action = item .id .split (":" , 1 )
251- self .loader .run_plugin (item .plugin , action )
249+ action_result = self .loader .run_plugin (item .plugin , action )
250+ if action_result == ActionResult .BACK :
251+ continue # Show main menu again
252252 return EXIT_SUCCESS
253253
254254 def _build_main_menu (self , display_manager : DisplayModeManager ) -> list [MenuItem ]:
@@ -287,7 +287,9 @@ def _build_main_menu(self, display_manager: DisplayModeManager) -> list[MenuItem
287287 result .append (
288288 MenuItem (
289289 id = item .id ,
290- title = display_manager .format_inline_title (item .plugin , item .title ),
290+ title = display_manager .format_inline_title (
291+ item .plugin , item .title
292+ ),
291293 item_type = item .item_type ,
292294 path = item .path ,
293295 plugin = item .plugin ,
@@ -341,18 +343,15 @@ def _sort_menu_items(self, items: list[MenuItem], sort: str) -> list[MenuItem]:
341343 else :
342344 return items
343345
344- def _show_plugin_submenu (self , plugin_name : str , display_manager : DisplayModeManager ) -> bool :
346+ def _show_plugin_submenu (
347+ self , plugin_name : str , display_manager : DisplayModeManager
348+ ) -> ActionResult :
345349 """Show items for a plugin in submenu mode.
346350
347351 Returns:
348- True if a plugin was executed (caller should exit),
349- False if back button selected (caller should continue).
350-
351- Raises:
352- MenuCancelled: If user presses ESC (should exit entire menu).
352+ ActionResult.CLOSE if a plugin action completed (caller should exit).
353+ ActionResult.BACK if user navigated back (caller should continue).
353354 """
354- from menu_kit .plugins .base import MenuCancelled
355-
356355 assert self .database is not None
357356 assert self .backend is not None
358357 assert self .config is not None
@@ -365,35 +364,38 @@ def _show_plugin_submenu(self, plugin_name: str, display_manager: DisplayModeMan
365364 items = self .database .get_items (plugin = plugin_name , order_by_frequency = True )
366365
367366 if not items :
368- return False
367+ return ActionResult . BACK
369368
370369 # Add back button
371370 items .append (MenuItem (id = "_back" , title = "Back" , item_type = ItemType .ACTION ))
372371
373372 result = self .backend .show (items , prompt = prompt )
374373
375- # ESC pressed - propagate up to exit entire menu
376- if result .cancelled :
377- raise MenuCancelled ()
378-
379- if result .selected is None or result .selected .id == "_back" :
380- return False
374+ # ESC or Back: go back to main menu
375+ if (
376+ result .cancelled
377+ or result .selected is None
378+ or result .selected .id == "_back"
379+ ):
380+ return ActionResult .BACK
381381
382382 item = result .selected
383383
384384 # Record usage
385385 if self .config .frequency_tracking :
386386 self .database .record_use (item .id )
387387
388- # Execute and exit
388+ # Execute plugin action
389389 if item .plugin :
390390 action = ""
391391 if ":" in item .id :
392392 _ , action = item .id .split (":" , 1 )
393- self .loader .run_plugin (item .plugin , action )
394- return True # Signal to exit menu
393+ action_result = self .loader .run_plugin (item .plugin , action )
394+ if action_result == ActionResult .BACK :
395+ continue # Show submenu again
396+ return ActionResult .CLOSE
395397
396- return False
398+ return ActionResult . BACK
397399
398400 def _format_item (self , item : MenuItem , prefix : str ) -> str :
399401 """Format an item for display."""
0 commit comments