mirror of
https://github.com/samjage/metro-warden.git
synced 2026-06-06 04:00:42 +00:00
129 lines
3.7 KiB
Python
129 lines
3.7 KiB
Python
"""
|
|
Registry Tab — displays loaded plugins and their status.
|
|
|
|
Subscribes to registry.plugin.* events to show live plugin state.
|
|
Allows loading/unloading plugins interactively.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Optional
|
|
|
|
from textual.app import ComposeResult
|
|
from textual.binding import Binding
|
|
from textual.widget import Widget
|
|
from textual.widgets import DataTable, Footer, Label, Static
|
|
|
|
|
|
class RegistryTab(Widget):
|
|
"""
|
|
Plugin registry viewer.
|
|
|
|
Shows all discovered plugins with their version, status, module path,
|
|
and load timestamp. Pressing Enter on a row toggles load/unload.
|
|
"""
|
|
|
|
DEFAULT_CSS = """
|
|
RegistryTab {
|
|
layout: vertical;
|
|
height: 1fr;
|
|
padding: 1 2;
|
|
}
|
|
RegistryTab .tab-title {
|
|
color: $primary;
|
|
text-style: bold;
|
|
height: 1;
|
|
margin-bottom: 1;
|
|
}
|
|
RegistryTab DataTable {
|
|
height: 1fr;
|
|
border: round $primary;
|
|
}
|
|
RegistryTab .hint {
|
|
height: 1;
|
|
color: $text-muted;
|
|
margin-top: 1;
|
|
}
|
|
"""
|
|
|
|
BINDINGS = [
|
|
Binding("r", "refresh_registry", "Refresh"),
|
|
Binding("enter", "toggle_plugin", "Toggle"),
|
|
]
|
|
|
|
COLUMNS = [
|
|
("Name", 14),
|
|
("Version", 9),
|
|
("Status", 11),
|
|
("Tags", 22),
|
|
("Loaded At", 22),
|
|
("Module", 32),
|
|
("Error", 30),
|
|
]
|
|
|
|
def compose(self) -> ComposeResult:
|
|
yield Static("// REGISTRY — Plugin Manifest", classes="tab-title")
|
|
yield DataTable(id="registry-table", zebra_stripes=True, cursor_type="row")
|
|
yield Static(
|
|
"Press [bold]r[/bold] to refresh | [bold]Enter[/bold] to toggle load",
|
|
classes="hint",
|
|
)
|
|
|
|
def on_mount(self) -> None:
|
|
table = self.query_one("#registry-table", DataTable)
|
|
for col_label, width in self.COLUMNS:
|
|
table.add_column(col_label, width=width)
|
|
|
|
app = self.app
|
|
if hasattr(app, "bus"):
|
|
app.bus.subscribe("registry.plugin.*", self._on_registry_event)
|
|
self._refresh_table()
|
|
|
|
def _on_registry_event(self, topic: str, data: Any) -> None:
|
|
self.call_from_thread(self._refresh_table)
|
|
|
|
def _refresh_table(self) -> None:
|
|
table = self.query_one("#registry-table", DataTable)
|
|
table.clear()
|
|
app = self.app
|
|
if not hasattr(app, "registry"):
|
|
return
|
|
for record in sorted(app.registry.all_records(), key=lambda r: r.name):
|
|
loaded_at = record.loaded_at.strftime("%Y-%m-%d %H:%M:%S") if record.loaded_at else "—"
|
|
table.add_row(
|
|
record.name,
|
|
record.version,
|
|
record.status.name,
|
|
", ".join(record.tags) or "—",
|
|
loaded_at,
|
|
record.module_path,
|
|
record.error or "—",
|
|
)
|
|
|
|
def action_refresh_registry(self) -> None:
|
|
self._refresh_table()
|
|
|
|
def action_toggle_plugin(self) -> None:
|
|
table = self.query_one("#registry-table", DataTable)
|
|
row_key = table.cursor_row
|
|
if row_key is None:
|
|
return
|
|
# Get plugin name from the focused row
|
|
try:
|
|
cell_value = table.get_cell_at((row_key, 0))
|
|
except Exception:
|
|
return
|
|
plugin_name = str(cell_value)
|
|
app = self.app
|
|
if not hasattr(app, "registry"):
|
|
return
|
|
record = app.registry.get(plugin_name)
|
|
if record is None:
|
|
return
|
|
from core.registry import PluginStatus
|
|
if record.status == PluginStatus.ACTIVE:
|
|
app.registry.unload(plugin_name)
|
|
else:
|
|
app.registry.load(plugin_name)
|
|
self._refresh_table()
|