#!/usr/bin/env python3 """Renderiza el reactor a PNG sin arrancar Newelle. Extrae el metodo _draw_reactor REAL de call.py (no una copia) y lo ejecuta con un self falso y niveles de audio simulados. Sirve para iterar el diseño sin pagar el build de flatpak cada vez. python3 preview_reactor.py [salida.png] """ import ast import math import sys import textwrap from pathlib import Path import cairo CALL_PY = Path(__file__).parent / "newelle" / "src" / "ui" / "widgets" / "call.py" SIZE = 240 def load_draw_func(): """Saca _draw_reactor del fuente y lo compila aislado.""" tree = ast.parse(CALL_PY.read_text()) for node in ast.walk(tree): if isinstance(node, ast.FunctionDef) and node.name == "_draw_reactor": src = textwrap.dedent(ast.get_source_segment(CALL_PY.read_text(), node)) ns = {"math": math} exec(compile(src, "call.py", "exec"), ns) return ns["_draw_reactor"] raise SystemExit("no encuentro _draw_reactor en call.py") class FakePanel: """El minimo que _draw_reactor toca de self.""" def __init__(self, levels, phase, user=False, assistant=False): self.reactor_levels = levels self.reactor_phase = phase self.user_speaking = user self.assistant_speaking = assistant def frame(draw, panel, path): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, SIZE, SIZE) cr = cairo.Context(surface) # fondo del modo llamada, para juzgar el contraste real cr.set_source_rgb(0.04, 0.09, 0.13) cr.paint() draw(panel, None, cr, SIZE, SIZE) surface.write_to_png(str(path)) def main(): draw = load_draw_func() out = Path(sys.argv[1] if len(sys.argv) > 1 else "reactor.png") escenas = [ ("reposo", FakePanel([0.04] * 12, 0.0)), ("escuchando", FakePanel( [0.25, 0.55, 0.85, 0.60, 0.35, 0.70, 0.95, 0.45, 0.30, 0.65, 0.50, 0.20], 1.1, user=True)), ("respondiendo", FakePanel( [0.40, 0.30, 0.55, 0.45, 0.60, 0.35, 0.50, 0.40, 0.55, 0.30, 0.45, 0.35], 2.4, assistant=True)), ] # tira horizontal con las tres escenas strip = cairo.ImageSurface(cairo.FORMAT_ARGB32, SIZE * 3, SIZE) scr = cairo.Context(strip) scr.set_source_rgb(0.04, 0.09, 0.13) scr.paint() for i, (nombre, panel) in enumerate(escenas): tmp = Path(f"/tmp/_reactor_{nombre}.png") frame(draw, panel, tmp) img = cairo.ImageSurface.create_from_png(str(tmp)) scr.set_source_surface(img, i * SIZE, 0) scr.paint() tmp.unlink(missing_ok=True) strip.write_to_png(str(out)) print(f"escrito {out} ({SIZE * 3}x{SIZE}) — reposo | escuchando | respondiendo") if __name__ == "__main__": main()