aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-cli/src/meshbay_cli/tui/app.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-cli/src/meshbay_cli/tui/app.py')
-rw-r--r--packages/meshbay-cli/src/meshbay_cli/tui/app.py76
1 files changed, 76 insertions, 0 deletions
diff --git a/packages/meshbay-cli/src/meshbay_cli/tui/app.py b/packages/meshbay-cli/src/meshbay_cli/tui/app.py
new file mode 100644
index 0000000..dbb773c
--- /dev/null
+++ b/packages/meshbay-cli/src/meshbay_cli/tui/app.py
@@ -0,0 +1,76 @@
+"""
+`meshbay-cli` with no argument: a full-screen terminal interface.
+
+Textual, because it runs on the same asyncio loop as aiortc and httpx rather
+than beside them, and on Linux terminals and Windows' consoles alike. Signed in
+already, it opens on the two panes; otherwise on the sign-in screen first.
+
+Nothing is printed while it runs — a line on stderr would tear the screen — so
+the log goes to a file in the CLI's data directory instead.
+"""
+
+import logging
+
+from textual.app import App
+
+from ..client.account import Account, no_passphrase
+from ..client.hub import Hub
+from ..client.store import data_dir
+from ..commands.context import Context
+from ..errors import CliError
+from .screens import DeviceCode, MainScreen, SignInScreen
+
+
+class MeshBayApp(App):
+ TITLE = "MeshBay"
+
+ def __init__(self, ctx: Context):
+ super().__init__()
+ self.ctx = ctx
+ self.hub = ctx.hub
+
+ async def use_hub(self, url: str) -> None:
+ if url != self.hub.url:
+ await self.hub.aclose()
+ self.hub = Hub(url)
+ self.ctx.hub = self.hub
+ self.ctx.hub_url = url
+
+ def on_mount(self) -> None:
+ account = None
+ if self.ctx.store.peek().get("hub") == self.hub.url:
+ try:
+ account = Account.open(self.hub, None, no_passphrase, self.ctx.store)
+ except CliError as e:
+ self.notify(e.message, severity="warning", markup=False)
+ if account is not None and account.remembered:
+ self.start(account)
+ else:
+ self.push_screen(SignInScreen())
+
+ def start(self, account: Account) -> None:
+ account.on_device_code = DeviceCode(self)
+ if isinstance(self.screen, SignInScreen):
+ self.switch_screen(MainScreen(account))
+ else:
+ self.push_screen(MainScreen(account))
+
+
+def _log_to_file() -> None:
+ root = logging.getLogger()
+ for h in list(root.handlers):
+ root.removeHandler(h)
+ try:
+ directory = data_dir()
+ directory.mkdir(parents=True, exist_ok=True)
+ handler = logging.FileHandler(directory / "interactive.log", encoding="utf-8")
+ except OSError:
+ handler = logging.NullHandler()
+ handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s"))
+ root.addHandler(handler)
+
+
+async def run_interactive(ctx: Context) -> int:
+ _log_to_file()
+ await MeshBayApp(ctx).run_async()
+ return 0