from __future__ import annotations from collections import deque from dataclasses import dataclass from typing import Deque, List, Sequence from ameba_control_panel import config from ameba_control_panel.utils.timeutils import timestamp_ms @dataclass class LogLine: text: str direction: str # "rx", "tx", "info" timestamp: str def as_display(self) -> str: return f"{self.timestamp} {self.text}" class LogBuffer: """Keeps a full archive plus a bounded UI tail.""" def __init__(self, max_tail: int = config.UI_LOG_TAIL_LINES) -> None: self._max_tail = max_tail self._tail: Deque[LogLine] = deque(maxlen=max_tail) self._archive: List[LogLine] = [] def append(self, text: str, direction: str) -> LogLine: line = LogLine(text=text.rstrip("\n"), direction=direction, timestamp=timestamp_ms()) self._tail.append(line) self._archive.append(line) return line def extend(self, items: Sequence[LogLine]) -> None: for line in items: self._tail.append(line) self._archive.append(line) def tail(self) -> Deque[LogLine]: return self._tail def archive(self) -> List[LogLine]: return self._archive def clear(self) -> None: self._tail.clear() self._archive.clear() def as_text(self, full: bool = False) -> str: source = self._archive if full else self._tail return "\n".join(line.as_display() for line in source)