FROM python:3.13-slim-bookworm

WORKDIR /app

# Provide a reference input file at /app/frames.tza
RUN python3 - <<'PY'
from __future__ import annotations

import random
import struct
from pathlib import Path


def _pad(line: str, width: int) -> bytes:
    b = line.encode("ascii", "strict")[:width]
    return b + b" " * (width - len(b))


def _make_log_line(rng: random.Random, i: int, *, tag: str = "") -> str:
    verbs = ["scan", "parse", "index", "render", "commit", "flush", "decode", "encode"]
    objs = ["tty", "pty", "screen", "pane", "buffer", "cursor", "scroll", "region"]
    adj = ["fast", "tight", "lazy", "eager", "robust", "minimal", "lossless", "atomic"]
    token = "".join(rng.choice("0123456789abcdef") for _ in range(8))
    prefix = f"{tag} " if tag else ""
    return f"{prefix}[{i:04d}] {rng.choice(verbs)} {rng.choice(objs)} ({rng.choice(adj)}) id={token}"


def generate_frames(
    *, width: int, height: int, n_frames: int, seed: int
) -> list[bytes]:
    rng = random.Random(seed)

    header = _pad("TermZip-ANSI / Tiny-ANSI stream (dual pane)", width)
    footer = _pad("F1 help | F2 clear | Ctrl+C quit", width)

    top_fixed = 2  # header + progress
    sep_lines = 1
    footer_lines = 1
    remaining = height - top_fixed - sep_lines - footer_lines
    pane_a_h = remaining // 2
    pane_b_h = remaining - pane_a_h
    sep = _pad("-" * min(width, 60), width)

    a_lines: list[bytes] = []
    b_lines: list[bytes] = []
    for i in range(pane_a_h):
        a_lines.append(_pad(_make_log_line(rng, i % 97, tag="A"), width))
    for i in range(pane_b_h):
        b_lines.append(_pad(_make_log_line(rng, i % 89, tag="B"), width))

    bar_len = min(32, max(8, width - 40))
    spinners = "|/-\\"

    frames: list[bytes] = []
    a_next = pane_a_h
    b_next = pane_b_h

    for t in range(n_frames):
        filled = (t * 2) % (bar_len + 1)
        pct = (filled * 100) // bar_len
        spinner = spinners[(t * 3) % len(spinners)]
        bar = "#" * filled + "-" * (bar_len - filled)
        progress = _pad(
            f"t={t:04d} A={a_next:04d} B={b_next:04d} [{bar}] {spinner}",
            width,
        )

        if t != 0 and (t % 4 == 0):
            shift = 2 if (t % 29 == 0) else 1
            for _ in range(shift):
                a_lines = a_lines[1:] + [_pad(_make_log_line(rng, a_next, tag="A"), width)]
                a_next += 1

        if t != 0 and (t % 7 == 0):
            b_lines = b_lines[1:] + [_pad(_make_log_line(rng, b_next, tag="B"), width)]
            b_next += 1

        if t != 0 and (t % 41 == 0):
            b_lines[-1] = _pad(_make_log_line(rng, b_next + 5000, tag="B*"), width)

        frame = b"".join([header, progress, *a_lines, sep, *b_lines, footer])
        assert len(frame) == width * height
        frames.append(frame)

    return frames


def write_tza(path: Path, *, width: int, height: int, frames: list[bytes], max_bytes: int) -> None:
    header = b"TZA1" + struct.pack("<HHII", width, height, len(frames), max_bytes)
    path.write_bytes(header + b"".join(frames))


WIDTH = 80
HEIGHT = 24
N_FRAMES = 1200
SEED = 1337

frames = generate_frames(width=WIDTH, height=HEIGHT, n_frames=N_FRAMES, seed=SEED)

# Size budget is intentionally tight.
MAX_BYTES = 69000

write_tza(Path("/app/frames.tza"), width=WIDTH, height=HEIGHT, frames=frames, max_bytes=MAX_BYTES)
PY
