96 lines
2.9 KiB
Python
96 lines
2.9 KiB
Python
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()
|