# -*- coding: utf-8 -*- """ Plugins-View: Tabellarische Liste aller Plugins. Zeigt Plugins in einer DataTable mit Name, Version, Beschreibung, Status. Enter/Klick auf eine Zeile oeffnet die Plugin-Detailansicht. Buttons zum Aktivieren/Deaktivieren und Neuladen. """ from __future__ import annotations from textual.app import ComposeResult from textual.containers import Horizontal from textual.widgets import Static, Button, DataTable from trixy_core.tui.views.base import MainView class PluginsView(MainView): """ F4: Plugin-Ansicht. Zeigt alle geladenen Plugins tabellarisch. Zeilen-Auswahl oeffnet Details, Buttons fuer Enable/Disable/Reload. """ TITLE = "Plugins" VIEW_ID = "plugins" def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self._plugins: list[dict] = [] self._refresh_counter: int = 0 def compose(self) -> ComposeResult: yield Static("", id="plugin-header") yield DataTable(id="plugin-table", cursor_type="row") with Horizontal(id="plugin-actions"): yield Button("Aktivieren", id="plugin-btn-enable", variant="success") yield Button("Deaktivieren", id="plugin-btn-disable", variant="warning") yield Button("Neu laden", id="plugin-btn-reload", variant="default") yield Static("", id="plugin-status") async def on_activate(self) -> None: """Laedt die Plugin-Liste.""" table = self.query_one("#plugin-table", DataTable) if not table.columns: table.add_columns("Name", "Version", "Beschreibung", "Status") await self._load_plugins() async def refresh_data(self) -> None: """Periodisches Update (alle 10 Sekunden).""" self._refresh_counter += 1 if self._refresh_counter % 5 == 0: await self._load_plugins() async def _load_plugins(self) -> None: """Laedt die Plugin-Liste vom Server.""" if not self.connection: return try: response = await self.connection.request_plugins() except Exception: self._set_status("Fehler beim Laden der Plugins", error=True) return if response is None: return self._plugins = response.plugins self._update_header() self._update_table() def _update_header(self) -> None: """Aktualisiert die Kopfzeile.""" header = self.query_one("#plugin-header", Static) total = len(self._plugins) enabled = sum(1 for p in self._plugins if p.get("enabled", False)) failed = sum(1 for p in self._plugins if p.get("failed", False)) info = f"[bold]Plugins:[/bold] {enabled}/{total} aktiv" if failed: info += f" [red]{failed} fehlgeschlagen[/red]" header.update(info) def _update_table(self) -> None: """Aktualisiert die Plugin-Tabelle.""" table = self.query_one("#plugin-table", DataTable) table.clear() sorted_plugins = sorted( self._plugins, key=lambda p: ( p.get("failed", False), not p.get("enabled", False), p.get("name", ""), ), ) for plugin_data in sorted_plugins: name = plugin_data.get("name", "Unbekannt") version = plugin_data.get("version", "") description = plugin_data.get("description", "") enabled = plugin_data.get("enabled", False) failed = plugin_data.get("failed", False) if failed: status = "[red]Fehler[/red]" elif enabled: status = "[green]Aktiv[/green]" else: status = "[dim]Inaktiv[/dim]" table.add_row(name, version, description, status, key=name) def _get_selected_plugin(self) -> dict | None: """Gibt das aktuell markierte Plugin zurueck.""" try: table = self.query_one("#plugin-table", DataTable) row_key, _ = table.coordinate_to_cell_key(table.cursor_coordinate) name = str(row_key.value) if row_key else "" if name: for p in self._plugins: if p.get("name") == name: return p except Exception: pass return None async def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None: """Enter auf einer Zeile: Oeffnet die Detail-Ansicht.""" row_key = str(event.row_key.value) if event.row_key else "" plugin_data = None for p in self._plugins: if p.get("name") == row_key: plugin_data = p break if not plugin_data: return if plugin_data.get("failed", False): self._set_status( f"Fehler: {plugin_data.get('error', 'Unbekannt')}", error=True ) return try: app = self.app if hasattr(app, "open_plugin_detail"): await app.open_plugin_detail(plugin_data) except Exception: pass async def on_button_pressed(self, event: Button.Pressed) -> None: """Verarbeitet Button-Klicks.""" btn_id = event.button.id if not self.connection: return plugin = self._get_selected_plugin() if not plugin: self._set_status("Kein Plugin ausgewaehlt", error=True) return name = plugin.get("name", "") if btn_id == "plugin-btn-enable": if plugin.get("failed", False): self._set_status("Fehlgeschlagene Plugins muessen neu geladen werden", error=True) return await self._plugin_action("enable", name) elif btn_id == "plugin-btn-disable": if plugin.get("failed", False): return await self._plugin_action("disable", name) elif btn_id == "plugin-btn-reload": await self._plugin_action("reload", name) async def _plugin_action(self, action: str, name: str) -> None: """Fuehrt eine Plugin-Aktion aus und aktualisiert die Liste.""" result = await self.connection.plugin_action(action=action, plugin_name=name) if result and result.success: self._set_status(result.message) await self._load_plugins() else: error = result.error if result else "Unbekannter Fehler" self._set_status(f"Fehler: {error}", error=True) def _set_status(self, text: str, error: bool = False) -> None: """Setzt die Statuszeile.""" status = self.query_one("#plugin-status", Static) if error: status.update(f"[red]{text}[/red]") else: status.update(f"[green]{text}[/green]")