Spaces:
Runtime error
Runtime error
Download backend/app/slides/render.py from firebird-technologies/NoteStack-Test: direct link, hf CLI and curl.
- Browser
- Download file 10.1 kB
-
https://huggingface.co/spaces/firebird-technologies/NoteStack-Test/resolve/main/backend/app/slides/render.py
- Command line
-
hf download hf://spaces/firebird-technologies/NoteStack-Test/backend/app/slides/render.py
-
curl -L -o render.py https://huggingface.co/spaces/firebird-technologies/NoteStack-Test/resolve/main/backend/app/slides/render.py
10.1 kB
| """A deck as HTML: one standalone page per slide (shown in the app in a sandboxed frame, scaled to fit) and one page | |
| with every slide (printed to PDF). Every word from the model is escaped; the markup is all ours.""" | |
| import math | |
| from markupsafe import escape | |
| from app.slides.content import field_limit | |
| from app.slides.decor import Decor, Shape, build, ring_path | |
| from app.slides.layout import Composed, El, Run, compose | |
| from app.slides.themes import FONTS, THEMES, H, Palette, W, theme_id | |
| FONT_LINK = ("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;700&family=Inter:wght@400;500;700" | |
| "&family=JetBrains+Mono:wght@400;700&display=swap") | |
| _FAMILY = {"display": f"'{FONTS['display']}','{FONTS['body']}',sans-serif", "body": f"'{FONTS['body']}',sans-serif", | |
| "mono": f"'{FONTS['mono']}',monospace"} | |
| def color(pal: Palette, key: str | None) -> str: | |
| if not key: | |
| return "transparent" | |
| return "#ffffff" if key == "white" else getattr(pal, key) | |
| CSS = f""" | |
| html,body{{margin:0;padding:0}} | |
| .slide{{position:relative;width:{W}px;height:{H}px;overflow:hidden;font-family:{_FAMILY['body']}; | |
| -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}} | |
| .slide svg.bg{{position:absolute;inset:0}} | |
| .e{{position:absolute;box-sizing:border-box;margin:0}} | |
| .t{{display:flex;flex-direction:column;overflow:visible;overflow-wrap:anywhere}} | |
| .t>.in{{display:block}} | |
| .t.v-middle{{justify-content:center}}.t.v-bottom{{justify-content:flex-end}} | |
| .t p{{margin:0}} | |
| .t b{{font-weight:700}} | |
| .r-heading,.r-quote,.r-closing,.r-term{{text-wrap:balance}} | |
| .dark .r-heading.l-title,.dark .r-value{{text-shadow:0 0 48px rgba(33,124,255,.45)}} | |
| .dark .r-marker{{box-shadow:0 0 36px rgba(33,124,255,.75)}} | |
| .dark .r-bar{{box-shadow:0 0 18px rgba(77,148,255,.8)}} | |
| .light .r-card,.light .r-panel{{box-shadow:0 18px 44px rgba(11,26,58,.14)}} | |
| .light .r-band{{box-shadow:0 18px 44px rgba(11,26,58,.26)}} | |
| """ | |
| def _style(**kw) -> str: | |
| return ";".join(f"{k.replace('_', '-')}:{v}" for k, v in kw.items() if v is not None) | |
| def _el(e: El, pal: Palette, layout: str, fmt: str) -> str: | |
| box = dict(left=f"{e.x:.1f}px", top=f"{e.y:.1f}px", width=f"{e.w:.1f}px", height=f"{e.h:.1f}px") | |
| if e.kind == "text": | |
| paras = [] | |
| for i, p in enumerate(e.paras): | |
| runs = "".join( | |
| f'<span{_editable(r, layout, fmt)} style="color:{color(pal, r.color)}{";font-weight:700" if r.bold else ""}">' | |
| f'{escape(r.text)}</span>' for r in p) | |
| gap = f' style="margin-top:{e.gap:.1f}px"' if i and e.gap else "" | |
| paras.append(f"<p{gap}>{runs}</p>") | |
| css = _style(**box, font_size=f"{e.size}px", line_height=str(e.lh), font_family=_FAMILY[e.font], | |
| text_align=e.align, letter_spacing=f"{e.track}em" if e.track else None, | |
| text_transform="uppercase" if e.upper else None, | |
| font_weight="700" if e.font == "display" else None) | |
| return f'<div class="e t r-{e.role} l-{layout} v-{e.valign}" style="{css}"><div class="in">{"".join(paras)}</div></div>' | |
| if e.kind == "line": | |
| horizontal = e.h == 0 | |
| css = _style(left=f"{e.x:.1f}px", top=f"{e.y - (e.stroke_w / 2 if horizontal else 0):.1f}px", | |
| width=f"{e.w if horizontal else e.stroke_w:.1f}px", height=f"{e.stroke_w if horizontal else e.h:.1f}px", | |
| background=color(pal, e.stroke), border_radius="2px") | |
| return f'<div class="e r-{e.role}" style="{css}"></div>' | |
| radius = "50%" if e.kind == "circle" else f"{e.radius}px" | |
| border = f"{e.stroke_w}px solid {color(pal, e.stroke)}" if e.stroke and e.stroke_w else None | |
| css = _style(**box, background=color(pal, e.fill), border=border, border_radius=radius, | |
| opacity=str(e.opacity) if e.opacity < 1 else None) | |
| return f'<div class="e r-{e.role}" style="{css}"></div>' | |
| def _editable(r: Run, layout: str, fmt: str) -> str: | |
| """Where a run's text lives and how long it may be, for the page's editor (none on our own text: numbers, footers).""" | |
| return f' data-f="{r.field}" data-max="{field_limit(layout, r.field, fmt)}"' if r.field else "" | |
| def _svg(d: Decor, pal: Palette, dark: bool, uid: str) -> str: | |
| defs, out = [], [] | |
| for n, s in enumerate(d.shapes): | |
| out.append(_shape(s, pal, dark, f"{uid}{n}", defs)) | |
| hx, hy = d.hot | |
| sky = (f'<radialGradient id="{uid}sky" gradientUnits="userSpaceOnUse" cx="{hx:.0f}" cy="{hy:.0f}" r="{W * 0.85:.0f}">' | |
| f'<stop offset="0" stop-color="{pal.bg2}"/><stop offset="1" stop-color="{pal.bg1}"/></radialGradient>') | |
| return (f'<svg class="bg" width="{W}" height="{H}" viewBox="0 0 {W} {H}"><defs>{sky}{"".join(defs)}</defs>' | |
| f'<rect width="{W}" height="{H}" fill="url(#{uid}sky)"/>{"".join(out)}</svg>') | |
| def _shape(s: Shape, pal: Palette, dark: bool, gid: str, defs: list[str]) -> str: | |
| c = color(pal, s.color) | |
| if s.kind == "stars": | |
| return "".join(f'<circle cx="{x:.0f}" cy="{y:.0f}" r="{r}" fill="{c}" opacity="{o}"/>' for x, y, r, o in s.points) | |
| if s.kind == "glow": | |
| defs.append(f'<radialGradient id="{gid}"><stop offset="0" stop-color="{c}" stop-opacity="{s.opacity}"/>' | |
| f'<stop offset="1" stop-color="{c}" stop-opacity="0"/></radialGradient>') | |
| return f'<circle cx="{s.cx:.0f}" cy="{s.cy:.0f}" r="{s.r:.0f}" fill="url(#{gid})"/>' | |
| if s.kind == "planet": | |
| hi, lo, rim = planet_colors(pal, dark) | |
| defs.append(f'<radialGradient id="{gid}" cx="35%" cy="28%" r="80%"><stop offset="0" stop-color="{hi}"/>' | |
| f'<stop offset="1" stop-color="{lo}"/></radialGradient>') | |
| return (f'<circle cx="{s.cx:.0f}" cy="{s.cy:.0f}" r="{s.r:.0f}" fill="url(#{gid})"/>' | |
| f'<circle cx="{s.cx:.0f}" cy="{s.cy:.0f}" r="{s.r:.0f}" fill="none" stroke="{rim}" stroke-width="3" opacity=".7"/>') | |
| if s.kind == "band": | |
| lo, hi = ring_shade(s) | |
| defs.append(f'<linearGradient id="{gid}" gradientUnits="userSpaceOnUse" x1="{lo:.0f}" y1="0" x2="{hi:.0f}" y2="0">' | |
| + "".join(f'<stop offset="{o}" stop-color="{c}" stop-opacity="{a * s.opacity:.3f}"/>' for o, a in SHADE) | |
| + "</linearGradient>") | |
| pts = ring_path(s) + ring_path(s, s.inner)[::-1] | |
| return f'<path d="M{_pts(pts)}Z" fill="url(#{gid})"/>' | |
| if s.kind == "ring" and s.arc: | |
| return (f'<path d="M{_pts(ring_path(s))}" fill="none" stroke="{c}" stroke-width="{s.width}" ' | |
| f'stroke-linecap="round" opacity="{s.opacity}"/>') | |
| if s.kind == "ring": | |
| return (f'<ellipse cx="{s.cx:.0f}" cy="{s.cy:.0f}" rx="{s.rx:.0f}" ry="{s.ry:.0f}" fill="none" stroke="{c}" ' | |
| f'stroke-width="{s.width}" opacity="{s.opacity}" transform="rotate({s.rot:.1f} {s.cx:.0f} {s.cy:.0f})"/>') | |
| if s.kind == "const": | |
| lines = "".join(f'<line x1="{s.points[a][0]:.0f}" y1="{s.points[a][1]:.0f}" x2="{s.points[b][0]:.0f}" ' | |
| f'y2="{s.points[b][1]:.0f}" stroke="{c}" stroke-width="1.6" opacity="{s.opacity * 0.5:.2f}"/>' | |
| for a, b in s.edges) | |
| dots = "".join(f'<circle cx="{x:.0f}" cy="{y:.0f}" r="{r}" fill="{c}" opacity="{s.opacity}"/>' for x, y, r, _ in s.points) | |
| return lines + dots | |
| return "" | |
| # How brightly a ringed planet's band shows across its width, left to right: lit from the upper left, as the planet is. | |
| SHADE = ((0, 1.0), (0.45, 0.8), (1, 0.3)) | |
| def ring_shade(s: Shape) -> tuple[float, float]: | |
| """The band's left and right extent on the slide, which SHADE runs across.""" | |
| half = math.hypot(s.rx * math.cos(math.radians(s.rot)), s.ry * math.sin(math.radians(s.rot))) | |
| return s.cx - half, s.cx + half | |
| def _pts(pts: list[tuple[float, float]]) -> str: | |
| return "L".join(f"{x:.1f} {y:.1f}" for x, y in pts) | |
| def planet_colors(pal: Palette, dark: bool) -> tuple[str, str, str]: | |
| """Lit side, dark side and rim of a planet.""" | |
| return ("#1d4fb3", "#020713", pal.accent) if dark else ("#5d9bff", pal.accent2, pal.accent) | |
| def slide_body(c: Composed, deck: dict, index: int, theme: str, fmt: str) -> str: | |
| t = THEMES[theme] | |
| s = c.slide | |
| d = build(theme, s["layout"], s["variant"], c.seed, index) | |
| els = "".join(_el(e, t.pal, s["layout"], fmt) for e in c.elements) | |
| cls = f"slide {'dark' if t.dark else 'light'} l-{s['layout']} v-{s['variant']}" | |
| return f'<div class="{cls}" style="background:{t.pal.bg1}">{_svg(d, t.pal, t.dark, f"s{index}g")}{els}</div>' | |
| def _page(body: str, extra_css: str = "") -> str: | |
| return (f'<!doctype html><html><head><meta charset="utf-8"><link rel="stylesheet" href="{FONT_LINK}">' | |
| f"<style>{CSS}{extra_css}</style></head><body>{body}</body></html>") | |
| def slide_html(deck: dict, index: int, theme: str, fmt: str, seed: int) -> str: | |
| """One slide as a standalone page.""" | |
| theme = theme_id(theme) | |
| return _page(slide_body(compose(deck, index, theme, fmt, seed), deck, index, theme, fmt), | |
| f"body{{background:{THEMES[theme].pal.bg1}}}") | |
| def slides_html(deck: dict, theme: str, fmt: str, seed: int, start: int, end: int) -> str: | |
| """Slides start..end-1 of the deck stacked on one page, each as it is in the deck (its number, its sky).""" | |
| theme = theme_id(theme) | |
| body = "".join(slide_body(compose(deck, i, theme, fmt, seed), deck, i, theme, fmt) for i in range(start, end)) | |
| return _page(body, f"body{{background:{THEMES[theme].pal.bg1}}}") | |
| def deck_print_html(deck: dict, theme: str, fmt: str, seed: int, script: str = "") -> str: | |
| """Every slide on one page, one per printed page.""" | |
| theme = theme_id(theme) | |
| body = "".join(slide_body(compose(deck, i, theme, fmt, seed), deck, i, theme, fmt) for i in range(len(deck["slides"]))) | |
| css = (f"@page{{size:{W}px {H}px;margin:0}}body{{background:{THEMES[theme].pal.bg1}}}" | |
| ".slide{break-after:page;page-break-after:always}.slide:last-child{break-after:auto;page-break-after:auto}" | |
| "*{-webkit-print-color-adjust:exact;print-color-adjust:exact}") | |
| return _page(body + script, css) | |