"""Streaming text buffer — smooth reveal of bursty network chunks. Accumulates text from the provider and reveals it at ~60fps with a ~200ms target reveal pace, so the UI sees a smooth stream regardless of network burst patterns. All methods must be called from the GTK main thread. """ from typing import Callable from gi.repository import GLib class StreamingTextBuffer: TICK_MS = 16 # ~60fps REVEAL_TARGET_MS = 200 def __init__(self, on_reveal: Callable[[str], None]): self._pending = "" self._on_reveal = on_reveal self._timer_id: int | None = None def push(self, text: str): self._pending += text if self._timer_id is None: self._timer_id = GLib.timeout_add(self.TICK_MS, self._tick) def _tick(self) -> bool: if not self._pending: self._timer_id = None return False n = max(1, len(self._pending) * self.TICK_MS // self.REVEAL_TARGET_MS) reveal, self._pending = self._pending[:n], self._pending[n:] self._on_reveal(reveal) return True def flush(self): if self._pending: self._on_reveal(self._pending) self._pending = "" if self._timer_id is not None: GLib.source_remove(self._timer_id) self._timer_id = None def cancel(self): self._pending = "" if self._timer_id is not None: GLib.source_remove(self._timer_id) self._timer_id = None