# -*- coding: utf-8 -*- """ InstallerApp — Haupteinstieg der Trixy Installer TUI. Gestartet via: python app.py --mode server --version 1.0.0 --source-archive pfad.tar.gz """ from __future__ import annotations import argparse import sys from pathlib import Path from textual.app import App, ComposeResult from textual.binding import Binding from .config import InstallerConfig from .menu_context import MenuBinding, MenuContext from .widgets.header import InstallerHeader from .widgets.footer import InstallerFooter from .views.base import InstallerView from .views.system_view import SystemView from .views.network_view import NetworkView from .views.plugins_view import PluginsView from .views.requirements_view import RequirementsView from .views.agreement_view import AgreementView from .views.install_view import InstallView class InstallerApp(App): """Trixy Installer TUI-Anwendung.""" CSS_PATH = "css/installer.tcss" TITLE = "Trixy Installer" BINDINGS = [ Binding("f1", "fkey('f1')", "F1", show=False), Binding("f2", "fkey('f2')", "F2", show=False), Binding("f3", "fkey('f3')", "F3", show=False), Binding("f4", "fkey('f4')", "F4", show=False), Binding("f5", "fkey('f5')", "F5", show=False), Binding("f6", "fkey('f6')", "F6", show=False), Binding("q", "quit", "Beenden", show=False), ] def __init__(self, config: InstallerConfig, existing: "ExistingInstallation | None" = None) -> None: super().__init__() self._config = config self._existing = existing self._views: list[InstallerView] = [] self._view_map: dict[str, InstallerView] = {} self._active_view: InstallerView | None = None self._menu = MenuContext() self._fkey_map: dict[str, str] = {} def compose(self) -> ComposeResult: yield InstallerHeader( version=self._config.version, mode=self._config.mode, id="header", ) # Views erstellen — nur fuer den aktuellen Modus mode = self._config.mode all_views: list[InstallerView] = [ SystemView(self._config, id="view-system"), NetworkView(self._config, id="view-network"), PluginsView(self._config, id="view-plugins"), RequirementsView(self._config, id="view-requirements"), AgreementView(self._config, id="view-agreement"), InstallView(self._config, existing=self._existing, id="view-install"), ] # Tab-Definitionen: (f-key, view) tab_defs = [ ("f1", all_views[0]), # System ("f2", all_views[1]), # Netzwerk ("f3", all_views[2]), # Plugins ("f4", all_views[3]), # Pakete ("f5", all_views[4]), # Vereinbarung ("f6", all_views[5]), # Installation ] for fkey, view in tab_defs: if mode in view.MODES: self._views.append(view) self._view_map[view.VIEW_ID] = view self._fkey_map[fkey] = view.VIEW_ID self._menu.add(MenuBinding( key=fkey, label=view.TITLE, view_id=view.VIEW_ID, )) view.display = False yield view yield InstallerFooter(self._menu, id="footer") async def on_mount(self) -> None: """Aktiviert den ersten View nach dem Mount.""" if self._views: await self._activate_view(self._views[0].VIEW_ID) async def action_fkey(self, key: str) -> None: """Verarbeitet F-Tasten-Druecke.""" view_id = self._fkey_map.get(key) if view_id and view_id in self._view_map: await self._activate_view(view_id) async def _activate_view(self, view_id: str) -> None: """Aktiviert einen View und deaktiviert den aktuellen.""" new_view = self._view_map.get(view_id) if not new_view or new_view is self._active_view: return # Alten View deaktivieren if self._active_view: await self._active_view.on_deactivate() self._active_view.display = False # Neuen View aktivieren new_view.display = True self._active_view = new_view await new_view.on_activate() # Header aktualisieren header = self.query_one("#header", InstallerHeader) header.subtitle = new_view.TITLE def main() -> None: """CLI-Einstiegspunkt.""" parser = argparse.ArgumentParser(description="Trixy Installer") parser.add_argument( "--mode", choices=["server", "client", "standalone", "config"], required=True, help="Installationsmodus", ) parser.add_argument("--version", default="1.0.0", help="Trixy-Version") parser.add_argument("--source-archive", default="", help="Pfad zum Quellarchiv (.tar.gz)") parser.add_argument("--install-path", default="", help="Installationspfad (ueberschreibt Default)") args = parser.parse_args() config = InstallerConfig( mode=args.mode, version=args.version, source_archive=args.source_archive, ) if args.install_path: config.install_path = args.install_path # Bestehende Installation erkennen from .detect import detect_installation, apply_existing_to_config existing = detect_installation(config.install_path, config.mode, config.version) if existing.found: apply_existing_to_config(existing, config) app = InstallerApp(config, existing=existing) app.run() if __name__ == "__main__": main()