"""Faithful design preview: renders the REAL style.css + render.py output across all
UI states into a single static HTML file, so design iteration can be screenshot-verified
without launching Gradio or a GPU. Dev tool only — not part of the shipped app.
Usage: python scripts/design_preview.py -> writes design_preview_live.html
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))
import render # noqa: E402
from schema import HandoffOutput # noqa: E402
CSS = (ROOT / "style.css").read_text(encoding="utf-8")
CACHE = ROOT / "data" / "example_cache"
def load(name: str) -> HandoffOutput:
return HandoffOutput.model_validate(json.loads((CACHE / f"{name}.json").read_text(encoding="utf-8")))
def sec(label: str) -> str:
return f'
{label}
'
def build() -> str:
ahmed = load("ahmed")
arabic = load("arabic")
root_canal = load("root_canal")
# Pick a flagged case to exercise the fired safety panel / interrupt path.
flagged = next((c for c in (root_canal, ahmed, arabic) if c.red_flags), root_canal)
blocks: list[str] = [
render.header_html(),
render.footer_html(),
sec("Step rail"),
render.rail_html(),
sec("Agent dashboard — initial (Ready)"),
render.initial_agent_dashboard_html(),
sec("Agent dashboard — live auditable run"),
render.render_agent_dashboard(
"Cached", "Complete", f"{len(flagged.red_flags)} flags found", "Passed",
route_note="Cached case · model drafted narrative · sentinel ran between turns",
),
sec("Safety panel — clear"),
render.initial_safety_html(),
sec("Safety panel — fired (rule-based)"),
render.render_safety_html(flagged),
sec("Handoff card — empty / ghost state"),
render.placeholder_handoff_html(),
sec("Handoff card — loading skeleton"),
render.skeleton_handoff_html(),
sec("Handoff card — English (Ahmed, model-authored narrative)"),
render.render_handoff_html(ahmed, "English", model_authored=True),
sec("Handoff card — Bilingual (English + Arabic)"),
render.render_handoff_html(arabic, "Bilingual"),
]
# Show the hard-interrupt card if any cached case triggers it.
interrupt_case = next((c for c in (root_canal, ahmed, arabic) if render.is_hard_interrupt(c)), None)
if interrupt_case is not None:
blocks += [sec("Hard interrupt card"), render._interrupt_card(interrupt_case, "Bilingual")]
fonts = (
''
''
''
)
shell = (
"body{max-width:1180px;margin:0 auto;padding:28px 20px;background:#F5F4F0;}"
".preview-sec{margin:34px 0 12px;font-family:'IBM Plex Mono',monospace;font-size:11px;"
"letter-spacing:.14em;text-transform:uppercase;color:#8C887E;border-top:1px solid #E2DFD6;padding-top:14px;}"
)
return (
f"{fonts}"
f""
f"{''.join(blocks)}"
)
if __name__ == "__main__":
out = ROOT / "design_preview_live.html"
out.write_text(build(), encoding="utf-8")
print(f"wrote {out}")