Skip to content

Commit f1788e4

Browse files
authored
feat: Add remove missing plugin from project functionality (#201)
1 parent 775e29f commit f1788e4

File tree

1 file changed

+54
-3
lines changed

1 file changed

+54
-3
lines changed

titan_cli/ui/tui/screens/plugin_management.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)