|
| 1 | +#!/usr/bin/env python3 |
| 2 | +"""Inject PySDR branding into a built JupyterLite site. |
| 3 | +
|
| 4 | +Run AFTER `jupyter lite build`. JupyterLab 4 / Notebook 7 render the top-left |
| 5 | +logo from JavaScript and offer no user-CSS hook, so we inline a small stylesheet |
| 6 | +(pysdr-brand.css) into every generated app page. It hides the built-in Jupyter |
| 7 | +mark (#jp-NotebookLogo for the Notebook apps, #jp-MainLogo for Lab) and paints |
| 8 | +the PySDR logo from /_static/logo.svg instead. |
| 9 | +
|
| 10 | +Usage: python jupyterlite/inject_branding.py [output_dir] |
| 11 | + (output_dir defaults to _build/jupyterlite) |
| 12 | +
|
| 13 | +Exits non-zero if it patches nothing, so CI fails loudly if a JupyterLite |
| 14 | +upgrade changes the build layout out from under us. |
| 15 | +""" |
| 16 | +from __future__ import annotations |
| 17 | + |
| 18 | +import sys |
| 19 | +from pathlib import Path |
| 20 | + |
| 21 | +HERE = Path(__file__).resolve().parent |
| 22 | +MARKER = "pysdr-branding" |
| 23 | + |
| 24 | + |
| 25 | +def main() -> int: |
| 26 | + out = Path(sys.argv[1] if len(sys.argv) > 1 else "_build/jupyterlite") |
| 27 | + if not out.is_dir(): |
| 28 | + print(f"[inject_branding] output dir not found: {out}", file=sys.stderr) |
| 29 | + return 1 |
| 30 | + |
| 31 | + css = (HERE / "pysdr-brand.css").read_text(encoding="utf-8") |
| 32 | + block = f'<style data-{MARKER}="1">\n{css}\n</style>' |
| 33 | + |
| 34 | + patched = 0 |
| 35 | + for html in out.rglob("*.html"): |
| 36 | + text = html.read_text(encoding="utf-8") |
| 37 | + if MARKER in text or "</head>" not in text: |
| 38 | + continue |
| 39 | + html.write_text(text.replace("</head>", block + "\n</head>", 1), encoding="utf-8") |
| 40 | + patched += 1 |
| 41 | + |
| 42 | + print(f"[inject_branding] patched {patched} page(s) under {out}") |
| 43 | + return 0 if patched else 1 |
| 44 | + |
| 45 | + |
| 46 | +if __name__ == "__main__": |
| 47 | + raise SystemExit(main()) |
0 commit comments