| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- # -*- 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]")
|