diff --git a/src/textual_web/cli.py b/src/textual_web/cli.py index 915ac8c..c0fe9ac 100644 --- a/src/textual_web/cli.py +++ b/src/textual_web/cli.py @@ -92,6 +92,7 @@ def print_disclaimer() -> None: default=0, help="Exit textual-web when no apps have been launched in WAIT seconds", ) +@click.option("-w", "--web-interface", is_flag=True, help="Enable web interface") @click.option("-s", "--signup", is_flag=True, help="Create a textual-web account.") @click.option("--welcome", is_flag=True, help="Launch an example app.") @click.option("--merlin", is_flag=True, help="Launch Merlin game.") @@ -102,6 +103,7 @@ def app( dev: bool, terminal: bool, exit_on_idle: int, + web_interface: bool, api_key: str, signup: bool, welcome: bool, @@ -185,6 +187,7 @@ def app( api_key=api_key or None, devtools=dev, exit_on_idle=exit_on_idle, + web_interface=web_interface, ) for app_command in run: diff --git a/src/textual_web/exit_poller.py b/src/textual_web/exit_poller.py index 1228f68..57cde07 100644 --- a/src/textual_web/exit_poller.py +++ b/src/textual_web/exit_poller.py @@ -5,7 +5,7 @@ import logging from time import monotonic from typing import TYPE_CHECKING -EXIT_POLL_RATE = 15 +EXIT_POLL_RATE = 5 log = logging.getLogger("textual-web") diff --git a/src/textual_web/ganglion_client.py b/src/textual_web/ganglion_client.py index 513a461..be3009e 100644 --- a/src/textual_web/ganglion_client.py +++ b/src/textual_web/ganglion_client.py @@ -31,6 +31,8 @@ from .retry import Retry from .session import SessionConnector from .session_manager import SessionManager from .types import Meta, RouteKey, SessionID +from .web import run_web_interface + if TYPE_CHECKING: from .config import Config @@ -78,10 +80,12 @@ class GanglionClient(Handlers): api_key: str | None, devtools: bool = False, exit_on_idle: int = 0, + web_interface: bool = False, ) -> None: self.environment = environment self.websocket_url = environment.url self.exit_on_idle = exit_on_idle + self.web_interface = web_interface abs_path = Path(config_path).absolute() path = abs_path if abs_path.is_dir() else abs_path.parent @@ -165,6 +169,7 @@ class GanglionClient(Handlers): async def run(self) -> None: """Run the connection loop.""" + try: self._exit_poller.start() await self._run() @@ -201,7 +206,15 @@ class GanglionClient(Handlers): self._poller.set_loop(loop) self._poller.start() - self._task = asyncio.create_task(self.connect()) + if self.web_interface: + app = await run_web_interface() + try: + self._task = asyncio.create_task(self.connect()) + finally: + await app.shutdown() + else: + self._task = asyncio.create_task(self.connect()) + await self._task def force_exit(self) -> None: diff --git a/src/textual_web/web.py b/src/textual_web/web.py new file mode 100644 index 0000000..53bab24 --- /dev/null +++ b/src/textual_web/web.py @@ -0,0 +1,30 @@ +""" +An optional web interface to control textual-web + +Note: Currently just a stub. + +""" + +import logging + +from aiohttp import web + + +log = logging.getLogger("textual-web") + + +async def run_web_interface() -> web.Application: + """Run the web interface.""" + + async def health_check(request) -> web.Response: + return web.Response(text="Hello, world") + + app = web.Application() + app.add_routes([web.get("/health-check/", health_check)]) + + runner = web.AppRunner(app) + await runner.setup() + site = web.TCPSite(runner, "0.0.0.0", 8080) + await site.start() + log.info("Web interface started on port 8080") + return app