from __future__ import annotations import curses from typing import Optional from .colors import UIColors class HeaderView: """Top panel with live metrics.""" def __init__(self, height: int, width: int, y: int, x: int) -> None: self.win = curses.newwin(height, width, y, x) def render( self, iteration: int, digits: int, elapsed: float, rate: float, last_digit: Optional[str], computing: bool, ) -> None: self.win.erase() style = UIColors.style(UIColors.HEADER, bold=True) metric_style = UIColors.style(UIColors.METRIC, bold=True) status_style = UIColors.style(UIColors.STATUS) lines = [ " π Stream Dashboard ", f" Iteration: {iteration:,}", f" Digits: {digits:,}", f" Elapsed: {elapsed:8.2f}s • Rate: {rate:8.2f} digits/s", ] status = " Generating…" if computing else " Idle" digit_line = f" Last digit: {last_digit if last_digit is not None else '—'}" try: self.win.addstr(0, 0, lines[0], style) self.win.addstr(1, 0, lines[1], metric_style) self.win.addstr(2, 0, lines[2], metric_style) self.win.addstr(3, 0, lines[3], metric_style) self.win.addstr(4, 0, digit_line, metric_style) self.win.addstr(5, 0, status, status_style) width = max(1, self.win.getmaxyx()[1] - 1) self.win.addstr(6, 0, "─" * width, UIColors.style(UIColors.SEPARATOR)) except curses.error: pass self.win.noutrefresh() class DigitsView: """Scrollable window for the π digits.""" def __init__(self, height: int, width: int, y: int, x: int) -> None: self.win = curses.newwin(height, width, y, x) def render(self, digits_text: str, scroll: int) -> int: self.win.erase() max_y, max_x = self.win.getmaxyx() if max_y <= 0 or max_x <= 0: self.win.noutrefresh() return 0 cols = max(1, max_x - 1) chunks = [digits_text[i : i + cols] for i in range(0, len(digits_text), cols)] or ["0"] max_scroll = max(0, len(chunks) - max_y) scroll = max(0, min(scroll, max_scroll)) style = UIColors.style(UIColors.NUMBER) for idx, chunk in enumerate(chunks[scroll : scroll + max_y]): try: self.win.addstr(idx, 0, chunk, style) except curses.error: break self.win.noutrefresh() return scroll class HelpView: """Bottom row with key hints.""" def __init__(self, y: int, x: int, width: int) -> None: self.win = curses.newwin(1, width, y, x) def render(self) -> None: message = " ↑↓ scroll • q quit" try: self.win.addstr(0, 0, message, UIColors.style(UIColors.STATUS, dim=True)) except curses.error: pass self.win.noutrefresh()