#!/usr/bin/env python3 """ cover.py — Generate cover.html from tokens.json. Usage: python3 cover.py --tokens tokens.json --out cover.html Reads tokens.json["cover_pattern"] and renders the matching HTML cover. Cover fonts are loaded live via Google Fonts @import (no local caching). Exit codes: 0 success, 1 bad args/missing file, 3 render error """ import argparse import json import sys # ── Google Fonts loader ──────────────────────────────────────────────────────── def _gfonts_import(t: dict) -> str: """Return a CSS @import for the document's Google Fonts, if available.""" url = t.get("gfonts_import", "") if url: return f"@import url('{url}');" return "" # ── Shared CSS head (required by all patterns) ───────────────────────────────── def _base_css(t: dict) -> str: """Critical reset + shared variables. Never remove these rules.""" return f""" {_gfonts_import(t)} * {{ margin: 0; padding: 0; box-sizing: border-box; }} html, body {{ width: 794px; height: 1123px; overflow: hidden; background: {t['cover_bg']}; font-family: '{t['font_body']}', 'Helvetica Neue', Helvetica, Arial, sans-serif; }} .page {{ position: relative; width: 794px; height: 1123px; background: {t['cover_bg']}; overflow: hidden; }} """ # ── Dot-grid SVG helper ───────────────────────────────────────────────────────── def _dot_grid(x0, y0, cols, rows, *, gap, r, color, opacity) -> str: """Render a dot-grid as an absolutely positioned SVG element.""" dots = [] for row in range(rows): for col in range(cols): cx = x0 + col * gap cy = y0 + row * gap dots.append(f'') return ( f'' + "".join(dots) + "" ) # ── Cross-hatch SVG helper ────────────────────────────────────────────────────── def _cross_hatch(color, opacity, spacing=32, stroke_w=0.5) -> str: lines = [] for i in range(-20, 60): x = i * spacing lines.append(f'') return ( f'' + "".join(lines) + "" ) # ── Pattern 1: Full-bleed block ──────────────────────────────────────────────── def _pattern_fullbleed(t: dict) -> str: dot_grid = _dot_grid( x0=500, y0=40, cols=10, rows=20, gap=24, r=1.8, color=t["accent"], opacity=0.12 ) subtitle_block = "" if t.get("subtitle"): subtitle_block = f"""
{t['subtitle']}
""" return f"""
{dot_grid}
{t.get('doc_type','Document').upper()}  ·  {t.get('date','')}
{t['title']}
{subtitle_block}
""" # ── Pattern 2: Split panel ───────────────────────────────────────────────────── def _pattern_split(t: dict) -> str: dot_grid = _dot_grid( x0=360, y0=120, cols=10, rows=18, gap=22, r=2, color="#CCCCCC", opacity=0.25 ) return f"""
{t['title']}
{'
' + t['subtitle'] + '
' if t.get('subtitle') else ''}
{t.get('author','')}
{t.get('date','')}
{dot_grid}
{t.get('doc_type','').upper()}
""" # ── Pattern 3: Typographic ───────────────────────────────────────────────────── def _pattern_typographic(t: dict) -> str: words = t['title'].split() first = words[0] if words else "" rest = " ".join(words[1:]) if len(words) > 1 else "" return f"""
{first}
{'
' + rest + '
' if rest else ''}
{t.get('author','')}
{t.get('date','')}
{'
' + t['subtitle'] + '
' if t.get('subtitle') else ''}
""" # ── Pattern 4: Dark atmospheric ──────────────────────────────────────────────── def _pattern_atmospheric(t: dict) -> str: dot_grid = _dot_grid( x0=60, y0=60, cols=16, rows=22, gap=20, r=1.5, color=t["accent"], opacity=0.08 ) return f"""
{dot_grid}
{t.get('doc_type','').upper()}  ·  {t.get('date','')}
{t['title']}
{'
' + t['subtitle'] + '
' if t.get('subtitle') else ''}
""" # ── Pattern 5: Minimal — thick left bar, generous whitespace ─────────────────── def _pattern_minimal(t: dict) -> str: """ Ultra-restrained: white background, 8px left accent bar, oversized light-weight title, nothing else but a hairline rule and minimal metadata. The bar is the only color on the page — everything else is black on white. """ # Pick text color for page (minimal uses page_bg which is near-white) text_dark = t.get("dark", "#111111") muted = t.get("muted", "#999999") accent = t["accent"] subtitle_block = "" if t.get("subtitle"): subtitle_block = f'
{t["subtitle"]}
' return f"""
{t.get('doc_type','').upper()}
{t['title']}
{subtitle_block}
{t.get('author','')}{(' · ' + t.get('date','')) if t.get('date') else ''}
""" # ── Pattern 6: Stripe — bold horizontal bands ────────────────────────────────── def _pattern_stripe(t: dict) -> str: """ Page divided into three bold horizontal bands: - Top band (accent, ~18%): document type label - Middle band (dark, ~52%): large title in white - Bottom band (page bg, ~30%): author / date / subtitle Hard geometry, no gradients, no textures. Newspaper / brand poster aesthetic. """ top_h = 200 # accent band mid_h = 580 # dark band bot_y = top_h + mid_h # 780 accent = t["accent"] dark = t.get("cover_bg", "#1A1A2E") light = t.get("page_bg", "#FAFAF8") text_l = t.get("text_light", "#FFFFFF") muted = t.get("muted", "#888888") subtitle_block = "" if t.get("subtitle"): subtitle_block = f'
{t["subtitle"]}
' return f"""
{t.get('doc_type','').upper()}
{t['title']}
{t.get('author','')}
{t.get('date','')}
{subtitle_block}
""" # ── Pattern 7: Diagonal — angled color split ─────────────────────────────────── def _pattern_diagonal(t: dict) -> str: """ SVG polygon cuts the page diagonally: upper-left in dark cover color, lower-right in light page bg. Title sits on the dark area, metadata on light. One angled edge — no gradients, no curves. """ dark_bg = t.get("cover_bg", "#1B2A4A") light_bg = t.get("page_bg", "#FAFCFF") accent = t["accent"] text_l = t.get("text_light", "#F8FAFF") text_d = t.get("dark", "#0F1A2E") muted = t.get("muted", "#7A8A99") # Polygon: full upper-left to ~60% down on right side # Points: top-left, top-right, (794, 620), (0, 820) poly = "0,0 794,0 794,620 0,820" subtitle_block = "" if t.get("subtitle"): subtitle_block = f'
{t["subtitle"]}
' return f"""
{t.get('doc_type','').upper()}  ·  {t.get('date','')}
{t['title']}
{t.get('author','')}
{subtitle_block}
""" # ── Pattern 8: Frame — elegant inset border ──────────────────────────────────── def _pattern_frame(t: dict) -> str: """ Classic formal layout: outer thin border line inset ~28px from page edges, inner accent strip at top and bottom inside the frame. Title centered in the frame space, classical serif typography. Used for: academic papers, formal reports, legal docs, annual reports. """ bg = t.get("cover_bg", "#FAF8F3") accent = t["accent"] dark = t.get("dark", "#2A1A0A") muted = t.get("muted", "#9A8A78") pad = 28 # frame inset from page edge inner_w = 794 - 2 * pad inner_h = 1123 - 2 * pad subtitle_block = "" if t.get("subtitle"): subtitle_block = f'
{t["subtitle"]}
' return f"""
{t.get('doc_type','').upper()}
{t['title']}
{subtitle_block}
{t.get('author','')}{(' · ' + t.get('date','')) if t.get('date') else ''}
""" # ── Pattern 9: Editorial — oversized ghost letter + bold type ────────────────── def _pattern_editorial(t: dict) -> str: """ Magazine / editorial feel: - Oversized first-letter of title as a ghost background element (8–12% opacity) - Bold category label at top in accent - Title in very large condensed weight, flush-left - Thin full-width rule separating title from metadata - Author / date bottom-left, page type bottom-right Designed for editorial reports, annual reviews, magazine-format content. """ bg = t.get("cover_bg", "#FFFFFF") accent = t["accent"] dark = t.get("dark", "#0A0A0A") muted = t.get("muted", "#777777") text_l = t.get("text_light", "#FFFFFF") # Ghost letter — first character of title ghost = t['title'][0].upper() if t['title'] else "A" subtitle_block = "" if t.get("subtitle"): subtitle_block = f'
{t["subtitle"]}
' # Determine if background is dark (use light text) or light (use dark text) is_dark_bg = ( bg.startswith("#0") or bg.startswith("#1") or bg.startswith("#2") ) title_color = text_l if is_dark_bg else dark # noqa: F841 body_color = text_l if is_dark_bg else dark return f"""
{ghost}
{t.get('doc_type','').upper()}
{t['title']}
{subtitle_block}
""" # ── Pattern 10: Magazine — elegant centered with optional hero image ──────────── def _pattern_magazine(t: dict) -> str: """ Upscale centered layout: company name + accent rule at top, large serif title, decorative rule, italic subtitle, optional hero image, abstract block, author. Used for: annual reports, strategic documents, formal publications. """ bg = t.get("cover_bg", "#F2F0EC") accent = t["accent"] dark = t.get("dark", "#0D1A2B") muted = t.get("muted", "#888888") org = t.get("doc_type", "").upper() img_url = t.get("cover_image", "") subtitle_block = "" if t.get("subtitle"): subtitle_block = f'
{t["subtitle"]}
' image_block = "" if img_url: image_block = f"""
""" abstract_block = "" if t.get("abstract"): abstract_block = f"""
Abstract: {t['abstract']}
""" return f"""
{org}
{t['title']}
{subtitle_block} {image_block} {abstract_block} {'
' if (t.get('abstract') or img_url) else '
'}
{t.get('author','')}
""" # ── Pattern 11: Darkroom — dark magazine variant ──────────────────────────────── def _pattern_darkroom(t: dict) -> str: """ Dark-background centered layout. Same structure as magazine but inverted: deep navy page, white/silver text, accent rules in lighter tone. Used for: premium reports, tech annual reviews, dark-themed documents. """ bg = t.get("cover_bg", "#151C27") accent = t["accent"] text_l = t.get("text_light", "#F0EDE6") muted = t.get("muted", "#8A9AB0") org = t.get("doc_type", "").upper() img_url = t.get("cover_image", "") subtitle_block = "" if t.get("subtitle"): subtitle_block = f'
{t["subtitle"]}
' image_block = "" if img_url: image_block = f"""
""" abstract_block = "" if t.get("abstract"): abstract_block = f"""
Abstract: {t['abstract']}
""" return f"""
{org}
{t['title']}
{subtitle_block} {image_block} {abstract_block} {'
' if (t.get('abstract') or img_url) else '
'}
{t.get('author','')}
""" # ── Pattern 12: Terminal — cyber/hacker aesthetic ─────────────────────────────── def _pattern_terminal(t: dict) -> str: """ Dark terminal/IDE aesthetic: grid overlay, monospace font, neon accent, corner brackets around the title block, status bar at bottom. Used for: tech reports, developer docs, security audits, system documentation. """ bg = t.get("cover_bg", "#0D1117") accent = t["accent"] text_l = t.get("text_light", "#E6EDF3") muted = t.get("muted", "#48897C") dark = t.get("dark", "#010409") org = t.get("doc_type", "DOCUMENT").upper() date_s = t.get("date", "") author = t.get("author", "") subtitle_line = "" if t.get("subtitle"): subtitle_line = f'
> {t["subtitle"]}
' abstract_block = "" if t.get("abstract"): abstract_block = f"""
{t['abstract']}
""" # grid overlay: horizontal + vertical lines h_lines = "".join( f'' for y in range(0, 1124, 48) ) v_lines = "".join( f'' for x in range(0, 795, 48) ) grid_svg = ( f'' + h_lines + v_lines + "" ) return f"""
{grid_svg}
SYSTEM_REPORT // {date_s}
{t['title']}
{subtitle_line}
{abstract_block}
AUTHOR_ID
{author}
{org}
Ln 1, Col 1
UTF-8
GENERATED_BY_COVERGENIUS
""" # ── Pattern 13: Poster — bold sidebar + oversized type ───────────────────────── def _pattern_poster(t: dict) -> str: """ Bold minimalist poster: thick vertical sidebar on the left, oversized all-caps title, typewriter-style metadata. Optional thumbnail on the right side. Used for: portfolios, creative reports, journalism, photography books. """ bg = t.get("cover_bg", "#FFFFFF") accent = t["accent"] # typically black or strong dark dark = t.get("dark", "#0A0A0A") muted = t.get("muted", "#888888") text_l = t.get("text_light", "#FFFFFF") img_url = t.get("cover_image", "") sidebar_w = 52 subtitle_block = "" if t.get("subtitle"): subtitle_block = f'
{t["subtitle"]}
' image_block = "" if img_url: image_block = f""" """ meta_lines = [] if t.get("author"): meta_lines.append(f'
{t["author"]}
') if t.get("subtitle"): meta_lines.append(f'
{t["subtitle"]}
') if t.get("date"): meta_lines.append(f'
{t["date"]}
') meta_block = "\n".join(meta_lines) return f"""
{t['title']}
{subtitle_block}
{meta_block}
{image_block}
""" # ── Dispatch ─────────────────────────────────────────────────────────────────── PATTERNS = { "fullbleed": _pattern_fullbleed, "split": _pattern_split, "typographic": _pattern_typographic, "atmospheric": _pattern_atmospheric, "minimal": _pattern_minimal, "stripe": _pattern_stripe, "diagonal": _pattern_diagonal, "frame": _pattern_frame, "editorial": _pattern_editorial, "magazine": _pattern_magazine, "darkroom": _pattern_darkroom, "terminal": _pattern_terminal, "poster": _pattern_poster, } def render(tokens: dict) -> str: """Dispatch to the cover pattern function and return the HTML string.""" pattern = tokens.get("cover_pattern", "fullbleed") fn = PATTERNS.get(pattern, _pattern_fullbleed) return fn(tokens) # ── CLI ─────────────────────────────────────────────────────────────────────── def main(): """CLI entry point.""" parser = argparse.ArgumentParser(description="Render cover HTML from tokens.json") parser.add_argument("--tokens", default="tokens.json") parser.add_argument("--out", default="cover.html") parser.add_argument("--subtitle", default="", help="Optional subtitle override") args = parser.parse_args() try: with open(args.tokens, encoding="utf-8") as f: tokens = json.load(f) except FileNotFoundError: print(json.dumps({"status": "error", "error": f"tokens file not found: {args.tokens}"}), file=sys.stderr) sys.exit(1) except json.JSONDecodeError as e: print(json.dumps({"status": "error", "error": f"invalid JSON: {e}"}), file=sys.stderr) sys.exit(1) if args.subtitle: tokens["subtitle"] = args.subtitle html = render(tokens) try: with open(args.out, "w", encoding="utf-8") as f: f.write(html) except OSError as e: print(json.dumps({"status": "error", "error": str(e)}), file=sys.stderr) sys.exit(3) print(json.dumps({ "status": "ok", "out": args.out, "pattern": tokens.get("cover_pattern"), })) if __name__ == "__main__": main()