commit 4dab3c8015ff5c1ed2219a86a636fd25a6727021 Author: Stu Leak Date: Mon Nov 3 10:06:20 2025 -0500 v0.1.0 — initial Telefact broadcaster base (frame + renderer) diff --git a/main.py b/main.py new file mode 100644 index 0000000..0d179e0 --- /dev/null +++ b/main.py @@ -0,0 +1,32 @@ +""" +Telefact — minimal runner +- Opens a 4:3 800×600 window +- Renders header (blue band) and footer (red band) +- No formatter yet; that’s next. +""" + +import tkinter as tk +from src.telefact_renderer import TelefactRenderer +from src.core.telefact_frame import TelefactFrame + +def main(): + root = tk.Tk() + root.title("Telefact — Broadcaster Prototype") + + renderer = TelefactRenderer(root, width=800, height=600, show_grid=False) + frame = TelefactFrame() + + # put a couple test glyphs so you can confirm alignment quickly + for i, ch in enumerate("TELEFACT"): + frame.set_cell(2 + i, 2, ch, "yellow") + for i, ch in enumerate("BROADCASTER BASE"): + frame.set_cell(2 + i, 4, ch, "white") + + renderer.render(frame) + + root.bind("", lambda e: root.quit()) + root.bind("q", lambda e: root.quit()) + root.mainloop() + +if __name__ == "__main__": + main() diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/__pycache__/__init__.cpython-313.pyc b/src/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..f786811 Binary files /dev/null and b/src/__pycache__/__init__.cpython-313.pyc differ diff --git a/src/__pycache__/telefact_renderer.cpython-313.pyc b/src/__pycache__/telefact_renderer.cpython-313.pyc new file mode 100644 index 0000000..f839bc3 Binary files /dev/null and b/src/__pycache__/telefact_renderer.cpython-313.pyc differ diff --git a/src/core/__init__.py b/src/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/core/__pycache__/__init__.cpython-313.pyc b/src/core/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..b994a54 Binary files /dev/null and b/src/core/__pycache__/__init__.cpython-313.pyc differ diff --git a/src/core/__pycache__/telefact_frame.cpython-313.pyc b/src/core/__pycache__/telefact_frame.cpython-313.pyc new file mode 100644 index 0000000..558292a Binary files /dev/null and b/src/core/__pycache__/telefact_frame.cpython-313.pyc differ diff --git a/src/core/telefact_body.py b/src/core/telefact_body.py new file mode 100644 index 0000000..e69de29 diff --git a/src/core/telefact_footer.py b/src/core/telefact_footer.py new file mode 100644 index 0000000..e69de29 diff --git a/src/core/telefact_formatter.py b/src/core/telefact_formatter.py new file mode 100644 index 0000000..e69de29 diff --git a/src/core/telefact_frame.py b/src/core/telefact_frame.py new file mode 100644 index 0000000..7821f15 --- /dev/null +++ b/src/core/telefact_frame.py @@ -0,0 +1,42 @@ +""" +Telefact Frame (model) +- Defines a 40×24 grid and logical regions. +- No rendering here—pure data model. +""" + +class TelefactFrame: + def __init__(self, cols: int = 40, rows: int = 24): + self.cols = cols + self.rows = rows + + self.header_rows = 1 + self.footer_rows = 1 + self.body_rows = self.rows - (self.header_rows + self.footer_rows) + + # grid[row][col] -> (char, color_name) + self.grid = [[(" ", "white") for _ in range(self.cols)] for _ in range(self.rows)] + + # basic cell ops + def set_cell(self, col: int, row: int, char: str, color: str = "white"): + if 0 <= col < self.cols and 0 <= row < self.rows and char: + self.grid[row][col] = (char[0], color) + + def get_cell(self, col: int, row: int): + if 0 <= col < self.cols and 0 <= row < self.rows: + return self.grid[row][col] + return (" ", "white") + + def clear(self, char: str = " ", color: str = "white"): + for r in range(self.rows): + for c in range(self.cols): + self.grid[r][c] = (char, color) + + # regions + def header_region(self): + return range(0, self.header_rows) + + def body_region(self): + return range(self.header_rows, self.header_rows + self.body_rows) + + def footer_region(self): + return range(self.rows - self.footer_rows, self.rows) diff --git a/src/core/telefact_header.py b/src/core/telefact_header.py new file mode 100644 index 0000000..e69de29 diff --git a/src/telefact_renderer.py b/src/telefact_renderer.py new file mode 100644 index 0000000..54ff0f5 --- /dev/null +++ b/src/telefact_renderer.py @@ -0,0 +1,97 @@ +""" +Telefact Renderer (Tkinter) +- Renders a TelefactFrame to a Canvas. +- Pure Python (stdlib only). +""" + +import tkinter as tk +from typing import Dict +from src.core.telefact_frame import TelefactFrame + +PALETTE: Dict[str, str] = { + "black": "#000000", + "blue": "#0000FF", + "red": "#FF0000", + "magenta":"#FF00FF", + "green": "#00FF00", + "cyan": "#00FFFF", + "yellow": "#FFFF00", + "white": "#FFFFFF", +} + +class TelefactRenderer: + def __init__(self, root: tk.Tk, width: int = 800, height: int = 600, show_grid: bool = False): + self.root = root + self.width = width + self.height = height + self.show_grid = show_grid + + # 40×24 grid for Telefact pages + self.cols, self.rows = 40, 24 + self.cell_w = max(1, width // self.cols) + self.cell_h = max(1, height // self.rows) + self.w = self.cell_w * self.cols + self.h = self.cell_h * self.rows + + self.canvas = tk.Canvas( + root, width=self.w, height=self.h, + highlightthickness=0, bg=PALETTE["black"] + ) + self.canvas.pack() + + # simple monospace font; will swap for Teletext font later + self.font = ("Courier New", max(12, self.cell_h - 9), "bold") + + # ----- low-level drawing helpers ----- + def _gx(self, col: int) -> int: + return col * self.cell_w + + def _gy(self, row: int) -> int: + return row * self.cell_h + + def _fill_row(self, row: int, color: str) -> None: + self.canvas.create_rectangle( + self._gx(0), self._gy(row), self._gx(self.cols), self._gy(row + 1), + fill=PALETTE.get(color, color), width=0 + ) + + def _draw_char(self, col: int, row: int, char: str, color: str) -> None: + x = self._gx(col) + 2 + y = self._gy(row) + self.cell_h // 2 + self.canvas.create_text( + x, y, anchor="w", text=char, + font=self.font, fill=PALETTE.get(color, color) + ) + + def _draw_grid(self) -> None: + for c in range(self.cols + 1): + x = self._gx(c) + self.canvas.create_line(x, 0, x, self.h, fill="#202020") + for r in range(self.rows + 1): + y = self._gy(r) + self.canvas.create_line(0, y, self.w, y, fill="#202020") + + # ----- public API ----- + def render(self, frame: TelefactFrame) -> None: + """Paint the whole frame to the canvas.""" + self.canvas.delete("all") + + # background + self.canvas.create_rectangle(0, 0, self.w, self.h, + fill=PALETTE["black"], width=0) + + # header/footer colour bands + for r in frame.header_region(): + self._fill_row(r, "blue") + for r in frame.footer_region(): + self._fill_row(r, "red") + + # draw text cells + for row in range(frame.rows): + for col in range(frame.cols): + ch, fg = frame.grid[row][col] + if ch.strip(): + self._draw_char(col, row, ch, fg) + + if self.show_grid: + self._draw_grid()