From 7f2adf823d28ba35ea8b1e2def1be438d0fab79f Mon Sep 17 00:00:00 2001 From: Natan Keddem Date: Sat, 4 Nov 2023 00:00:25 -0400 Subject: [PATCH] refactor terminal for new css loading scheme --- snapper/interfaces/cli.py | 35 ++++++++--------------------------- snapper/page.py | 2 ++ snapper/tabs/__init__.py | 12 +++++------- snapper/tabs/automation.py | 5 ++++- static/terminal.js | 30 +----------------------------- 5 files changed, 20 insertions(+), 64 deletions(-) diff --git a/snapper/interfaces/cli.py b/snapper/interfaces/cli.py index 9600c45..caca365 100644 --- a/snapper/interfaces/cli.py +++ b/snapper/interfaces/cli.py @@ -1,51 +1,32 @@ -from typing import Any, Callable, Dict, List, Union -from dataclasses import dataclass +from typing import Dict, List, Union import asyncio from asyncio.subprocess import Process, PIPE import contextlib import shlex from datetime import datetime -from snapper.result import Result from nicegui import app, ui -from nicegui.element import Element -from nicegui.events import GenericEventArguments, handle_event +from snapper.result import Result import logging logger = logging.getLogger(__name__) -app.add_static_files("/static", "static") -ui.add_head_html('') -ui.add_head_html('') + +def load_terminal_css(): + app.add_static_files("/static", "static") + ui.add_head_html('') class Terminal(ui.element, component="../../static/terminal.js", libraries=["../../static/xterm.js"]): # type: ignore[call-arg] def __init__( self, options: Dict, - on_init: Callable[..., Any] | None = None, ) -> None: super().__init__() self._props["options"] = options - self.is_initialized = False - if on_init: - - def handle_on_init(e: GenericEventArguments) -> None: - self.is_initialized = True - handle_event( - on_init, - GenericEventArguments(sender=self, client=self.client, args=e), - ) - - self.on("init", handle_on_init) def call_terminal_method(self, name: str, *args) -> None: self.run_method("call_api_method", name, *args) - def run_method(self, name: str, *args: Any) -> None: - if not self.is_initialized: - return - super().run_method(name, *args) - class Cli: def __init__(self, seperator: Union[bytes, None] = b"\n") -> None: @@ -108,7 +89,7 @@ class Cli: c = shlex.split(command, posix=False) try: process = await asyncio.create_subprocess_exec(*c, stdout=PIPE, stderr=PIPE) - if process.stdout is not None and process.stderr is not None: + if process is not None and process.stdout is not None and process.stderr is not None: self.stdout.clear() self.stderr.clear() self._terminate.clear() @@ -136,7 +117,7 @@ class Cli: self._busy = True try: process = await asyncio.create_subprocess_shell(command, stdout=PIPE, stderr=PIPE) - if process.stdout is not None and process.stderr is not None: + if process is not None and process.stdout is not None and process.stderr is not None: self.stdout.clear() self.stderr.clear() self._terminate.clear() diff --git a/snapper/page.py b/snapper/page.py index a2c41e6..4235ab5 100644 --- a/snapper/page.py +++ b/snapper/page.py @@ -2,6 +2,7 @@ from nicegui import ui from snapper import elements as el from snapper.drawer import Drawer from snapper.content import Content +from snapper.interfaces import cli import logging logger = logging.getLogger(__name__) @@ -28,6 +29,7 @@ def build(): """ ) + cli.load_terminal_css() ui.colors( primary=el.orange, secondary=el.orange, diff --git a/snapper/tabs/__init__.py b/snapper/tabs/__init__.py index 45f9570..68a463a 100644 --- a/snapper/tabs/__init__.py +++ b/snapper/tabs/__init__.py @@ -39,12 +39,6 @@ class Tab: cls._zfs[host] = Ssh(path="data", host=host) async def _display_result(self, result: Result) -> None: - def print_to_terminal(e): - for line in result.stdout_lines: - e.sender.call_terminal_method("write", line) - for line in result.stderr_lines: - e.sender.call_terminal_method("write", line) - with ui.dialog() as dialog, el.Card(): with el.DBody(height="[90vh]", width="[90vw]"): with el.WColumn(): @@ -65,7 +59,11 @@ class Tab: with el.Card() as card: card.tailwind.width("11/12") with el.WColumn(): - cli.Terminal(options={"rows": 20, "cols": 120, "convertEol": True}, on_init=lambda e: print_to_terminal(e)) + terminal = cli.Terminal(options={"rows": 20, "cols": 120, "convertEol": True}) + for line in result.stdout_lines: + terminal.call_terminal_method("write", line) + for line in result.stderr_lines: + terminal.call_terminal_method("write", line) with el.WRow() as row: row.tailwind.height("[40px]") el.DButton("Exit", on_click=lambda: dialog.submit("exit")) diff --git a/snapper/tabs/automation.py b/snapper/tabs/automation.py index 4894396..1a13f84 100644 --- a/snapper/tabs/automation.py +++ b/snapper/tabs/automation.py @@ -156,7 +156,9 @@ class Automation(Tab): with ui.dialog() as dialog, el.Card(): with el.DBody(height="[90vh]", width="[90vw]"): with el.WColumn(): - terminal = cli.Terminal(options={"rows": 30, "cols": 120, "convertEol": True}, on_init=lambda e: register_terminal(e)) + terminal = cli.Terminal(options={"rows": 30, "cols": 120, "convertEol": True}) + if job_data.args["data"]["name"] in job_handlers: + job_handlers[job_data.args["data"]["name"]].register_terminal(terminal) with el.WRow() as row: row.tailwind.height("[40px]") spinner = el.Spinner() @@ -164,6 +166,7 @@ class Automation(Tab): el.LgButton("Terminate", on_click=terminate) el.LgButton("Exit", on_click=lambda: dialog.submit("exit")) el.Spinner(master=spinner) + spinner.bind_visibility_from(job_handlers[job_data.args["data"]["name"]], "is_busy") await dialog if job_data.args["data"]["name"] in job_handlers: diff --git a/static/terminal.js b/static/terminal.js index 95e9fe8..89c65a0 100644 --- a/static/terminal.js +++ b/static/terminal.js @@ -1,14 +1,8 @@ export default { template: "
", - async mounted() { - await this.load_resource("static/xterm.css"); + mounted() { this.terminal = new Terminal(this.options); this.terminal.open(this.$el); - const connectInterval = setInterval(async () => { - if (window.socket.id === undefined) return; - this.$emit("init", { socket_id: window.socket.id }); - clearInterval(connectInterval); - }, 100); }, beforeDestroy() { this.terminal.dispose(); @@ -17,28 +11,6 @@ export default { this.terminal.dispose(); }, methods: { - load_resource(url) { - return new Promise((resolve, reject) => { - const dataAttribute = `data-${url.split("/").pop().replace(/\./g, "-")}`; - if (document.querySelector(`[${dataAttribute}]`)) { - resolve(); - return; - } - let element; - if (url.endsWith(".css")) { - element = document.createElement("link"); - element.setAttribute("rel", "stylesheet"); - element.setAttribute("href", url); - } else if (url.endsWith(".js")) { - element = document.createElement("script"); - element.setAttribute("src", url); - } - element.setAttribute(dataAttribute, ""); - document.head.appendChild(element); - element.onload = resolve; - element.onerror = reject; - }); - }, call_api_method(name, ...args) { this.terminal[name](...args); },