Files
gniza4linux/tui/widgets/operation_log.py
shuki 75fd25e559 Show spinner only for backup and restore operations
Other OperationLog dialogs (crontab show, remote test, retention) no
longer display the spinner since they complete quickly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 04:49:22 +02:00

87 lines
2.7 KiB
Python

import asyncio
from rich.spinner import Spinner as RichSpinner
from rich.text import Text
from textual.app import ComposeResult
from textual.screen import ModalScreen
from textual.widgets import RichLog, Button, Static
from textual.containers import Vertical, Horizontal
from textual.timer import Timer
class SpinnerWidget(Static):
"""Animated spinner using Rich's Spinner renderable."""
def __init__(self, style: str = "dots", **kwargs):
super().__init__("", **kwargs)
self._spinner = RichSpinner(style, text=" Running...")
self._timer: Timer | None = None
def on_mount(self) -> None:
self._timer = self.set_interval(1 / 12, self._tick)
def _tick(self) -> None:
self.update(self._spinner)
def stop(self) -> None:
if self._timer:
self._timer.stop()
self.update("")
class OperationLog(ModalScreen[None]):
BINDINGS = [("escape", "close", "Close")]
def __init__(self, title: str = "Operation Output", show_spinner: bool = True):
super().__init__()
self._title = title
self._show_spinner = show_spinner
self._mounted_event = asyncio.Event()
self._buffer: list[str] = []
def compose(self) -> ComposeResult:
with Vertical(id="op-log"):
yield Static(self._title, id="ol-title")
yield RichLog(id="ol-log", wrap=True, highlight=True, markup=True)
with Horizontal(id="ol-footer"):
yield Button("Close", variant="primary", id="ol-close")
if self._show_spinner:
yield SpinnerWidget("arrow3", id="ol-spinner")
def on_mount(self) -> None:
# Flush any buffered writes
log = self.query_one("#ol-log", RichLog)
for text in self._buffer:
self._write_to_log(log, text)
self._buffer.clear()
self._mounted_event.set()
def on_button_pressed(self, event: Button.Pressed) -> None:
self.dismiss(None)
def action_close(self) -> None:
self.dismiss(None)
def _write_to_log(self, log: RichLog, text: str) -> None:
if "[" in text and "[/" in text:
log.write(Text.from_markup(text))
else:
log.write(text)
def finish(self) -> None:
try:
self.query_one("#ol-spinner", SpinnerWidget).stop()
except Exception:
pass
def write(self, text: str) -> None:
if not self._mounted_event.is_set():
self._buffer.append(text)
return
try:
log = self.query_one("#ol-log", RichLog)
self._write_to_log(log, text)
except Exception:
self._buffer.append(text)