@@ -63,6 +63,7 @@ class PluginManagementScreen(BaseScreen):
6363 Binding ("e" , "toggle_plugin" , "Enable/Disable" ),
6464 Binding ("c" , "configure_plugin" , "Configure" ),
6565 Binding ("i" , "install_plugin" , "Install" ),
66+ Binding ("r" , "remove_plugin_from_project" , "Remove from Project" ),
6667 Binding ("u" , "uninstall_plugin" , "Uninstall" ),
6768 Binding ("U" , "update_plugin" , "Update" ),
6869 ]
@@ -168,6 +169,7 @@ def __init__(self, config):
168169 show_back = True
169170 )
170171 self .selected_plugin = None
172+ self .selected_missing_plugin = None
171173 self .installed_plugins = []
172174
173175 def compose_content (self ) -> ComposeResult :
@@ -243,8 +245,12 @@ def _load_plugins(self) -> None:
243245 plugin_list .highlighted = 0
244246 first = all_plugins [0 ]
245247 if first .startswith ("missing:" ):
246- self ._show_plugin_missing (first .removeprefix ("missing:" ))
248+ plugin_name = first .removeprefix ("missing:" )
249+ self .selected_plugin = None
250+ self .selected_missing_plugin = plugin_name
251+ self ._show_plugin_missing (plugin_name )
247252 else :
253+ self .selected_missing_plugin = None
248254 self .selected_plugin = first
249255 self ._show_plugin_details (first )
250256
@@ -254,9 +260,12 @@ def on_option_list_option_selected(self, event: OptionList.OptionSelected) -> No
254260 return
255261
256262 if event .option .id .startswith ("missing:" ):
257- self ._show_plugin_missing (event .option .id .removeprefix ("missing:" ))
263+ self .selected_plugin = None
264+ self .selected_missing_plugin = event .option .id .removeprefix ("missing:" )
265+ self ._show_plugin_missing (self .selected_missing_plugin )
258266 return
259267
268+ self .selected_missing_plugin = None
260269 self .selected_plugin = event .option .id
261270 self ._show_plugin_details (self .selected_plugin )
262271
@@ -266,9 +275,12 @@ def on_option_list_option_highlighted(self, event: OptionList.OptionHighlighted)
266275 return
267276
268277 if event .option .id .startswith ("missing:" ):
269- self ._show_plugin_missing (event .option .id .removeprefix ("missing:" ))
278+ self .selected_plugin = None
279+ self .selected_missing_plugin = event .option .id .removeprefix ("missing:" )
280+ self ._show_plugin_missing (self .selected_missing_plugin )
270281 return
271282
283+ self .selected_missing_plugin = None
272284 self .selected_plugin = event .option .id
273285 self ._show_plugin_details (self .selected_plugin )
274286
@@ -295,9 +307,11 @@ def _show_plugin_missing(self, plugin_name: str) -> None:
295307 ))
296308 details .mount (Text ("" ))
297309 details .mount (DimText ("Press i to install it from a community plugin URL." ))
310+ details .mount (DimText ("Press r to remove it from this project's config." ))
298311 details .mount (Text ("" ))
299312 details .mount (Horizontal (
300313 Button ("Install Plugin" , variant = "primary" , id = "install-plugin-button-details" ),
314+ Button ("Remove from Project" , variant = "error" , id = "remove-plugin-button-details" ),
301315 classes = "button-container"
302316 ))
303317
@@ -420,6 +434,8 @@ def on_button_pressed(self, event: Button.Pressed) -> None:
420434 self .action_configure_plugin ()
421435 elif event .button .id in ("install-plugin-button" , "install-plugin-button-details" ):
422436 self .action_install_plugin ()
437+ elif event .button .id == "remove-plugin-button-details" :
438+ self .action_remove_plugin_from_project ()
423439 elif event .button .id == "update-button" :
424440 self .action_update_plugin ()
425441 elif event .button .id == "uninstall-button" :
@@ -504,6 +520,23 @@ def on_install_done(result):
504520
505521 self .app .push_screen (InstallPluginScreen (self .config ), on_install_done )
506522
523+ def action_remove_plugin_from_project (self ) -> None :
524+ """Remove the selected missing plugin from the current project's config."""
525+ plugin_name = self .selected_missing_plugin
526+ if not plugin_name :
527+ self .app .notify ("Please select a missing plugin" , severity = "warning" )
528+ return
529+
530+ try :
531+ self ._remove_plugin_from_project_config (plugin_name )
532+ self .selected_missing_plugin = None
533+ self .config .load ()
534+ self ._load_plugins ()
535+ self .app .notify (f"Plugin '{ plugin_name } ' removed from this project." , severity = "information" )
536+ except Exception as e :
537+ logger .exception ("plugin_remove_from_project_failed" , plugin = plugin_name )
538+ self .app .notify (f"Failed to remove plugin from project: { e } " , severity = "error" )
539+
507540 def action_update_plugin (self ) -> None :
508541 """Check for and apply an update to the selected stable community plugin."""
509542 if not self .selected_plugin :
@@ -645,3 +678,21 @@ def _clear_project_plugin_source(self, plugin_name: str) -> None:
645678
646679 with open (project_cfg_path , "wb" ) as f :
647680 tomli_w .dump (project_cfg_dict , f )
681+
682+ def _remove_plugin_from_project_config (self , plugin_name : str ) -> None :
683+ """Remove a plugin block from the current project's config."""
684+ project_cfg_path = self .config .project_config_path
685+ if not project_cfg_path or not project_cfg_path .exists ():
686+ raise FileNotFoundError ("No project configuration found" )
687+
688+ with open (project_cfg_path , "rb" ) as f :
689+ project_cfg_dict = tomli .load (f )
690+
691+ plugins_table = project_cfg_dict .get ("plugins" )
692+ if not plugins_table or plugin_name not in plugins_table :
693+ raise KeyError (f"Plugin '{ plugin_name } ' is not configured in this project" )
694+
695+ plugins_table .pop (plugin_name , None )
696+
697+ with open (project_cfg_path , "wb" ) as f :
698+ tomli_w .dump (project_cfg_dict , f )
0 commit comments