From 307ff4912bac1095ebf382d70241f19409b2f8b8 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Sun, 14 Jun 2026 16:30:47 +0300 Subject: add: templating --- _shared/build.py | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++ _shared/footer.html | 12 +++++++ _shared/style.css | 22 +++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 _shared/build.py create mode 100644 _shared/footer.html create mode 100644 _shared/style.css (limited to '_shared') diff --git a/_shared/build.py b/_shared/build.py new file mode 100644 index 0000000..935bf5c --- /dev/null +++ b/_shared/build.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +"""Build sites from body.html + meta into index.html using shared templates.""" +import json +import os +import sys + +FAVICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1IDUiPjxyZWN0IHg9IjEiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiLz48cmVjdCB4PSIyIiB5PSIxIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIi8+PHJlY3QgeD0iMCIgeT0iMiIgd2lkdGg9IjEiIGhlaWdodD0iMSIvPjxyZWN0IHg9IjEiIHk9IjIiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiLz48cmVjdCB4PSIyIiB5PSIyIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIi8+PC9zdmc+" + + +def render(title, breadcrumb, style, extra_css, body, footer): + css = style + ("\n" + extra_css if extra_css.strip() else "") + return f""" + + + + + +{title} + + + +
+

gumx / {breadcrumb}

+
+
+{body} +
+{footer} + + +""" + + +def build_demo_body(demos_file): + demos = json.load(open(demos_file)) + parts = [] + for d in demos: + url = d.get("url", "#") + title = d.get("title", d.get("name", "")) + desc = d.get("description", "") + src = d.get("source", "") + src_link = f' / source' if src else "" + parts.append(f'

{title}{src_link}

\n

{desc}

') + return "\n".join(parts) + + +def build(sites_dir): + shared = os.path.join(sites_dir, "_shared") + style = open(os.path.join(shared, "style.css")).read() + footer = open(os.path.join(shared, "footer.html")).read() + + for site in sorted(os.listdir(sites_dir)): + if site.startswith("_") or site == "fonts" or site == "hooks": + continue + site_dir = os.path.join(sites_dir, site) + if not os.path.isdir(site_dir): + continue + + body_file = os.path.join(site_dir, "body.html") + demos_file = os.path.join(site_dir, "demos.json") + + if site == "demo.gumx.cc" and os.path.exists(demos_file): + body = build_demo_body(demos_file) + elif os.path.exists(body_file): + body = open(body_file).read() + else: + continue + + title = site + breadcrumb = site + meta_file = os.path.join(site_dir, "meta") + if os.path.exists(meta_file): + for line in open(meta_file): + k, _, v = line.strip().partition("=") + if k == "TITLE": + title = v.strip('"') + elif k == "BREADCRUMB": + breadcrumb = v.strip('"') + + extra_css = "" + extra_file = os.path.join(site_dir, "extra.css") + if os.path.exists(extra_file): + extra_css = open(extra_file).read() + + out = render(title, breadcrumb, style, extra_css, body, footer) + with open(os.path.join(site_dir, "index.html"), "w") as f: + f.write(out) + print(f"built: {site}") + + +if __name__ == "__main__": + build(sys.argv[1] if len(sys.argv) > 1 else ".") diff --git a/_shared/footer.html b/_shared/footer.html new file mode 100644 index 0000000..3aedfc6 --- /dev/null +++ b/_shared/footer.html @@ -0,0 +1,12 @@ + diff --git a/_shared/style.css b/_shared/style.css new file mode 100644 index 0000000..0f7e7cf --- /dev/null +++ b/_shared/style.css @@ -0,0 +1,22 @@ +@font-face { font-family: "Kawkab Mono"; src: url(/fonts/KawkabMono-Regular.woff2); font-weight: normal; } +@font-face { font-family: "Kawkab Mono"; src: url(/fonts/KawkabMono-Bold.woff2); font-weight: bold; } +* { unicode-bidi: plaintext; box-sizing: border-box; } +html { color: black; background-color: white; } +body { font-family: "Kawkab Mono"; font-size: 16px; line-height: 1.4; margin: 0; padding: 4rem 0; min-height: 100%; overflow-wrap: break-word; } +main, header, footer { max-width: 800px; margin-inline: auto; padding: 0 2rem; } +h1, header, footer { text-align: center; } +main { text-align: justify; } +p, h2, h3, h4 { margin: 1em 0 0 0; } +ol { margin: 0.5em 0 0 0; } +table { margin: auto; border-collapse: collapse; } +th, td { border: 1px solid; padding: 0.3em 0.8em; } +pre { margin: 1em 0; } +pre code { border: thin solid; padding: 1em; display: block; text-align: start; overflow-x: scroll; } +code { font-size: 85%; } +hr { border: none; border-top: thin solid; margin: 1.25rem 0; } +header { margin-bottom: 1em; } +footer { margin-top: 3em; } +a { color: inherit; } +@media (max-width: 600px) { body { font-size: 0.9em; } h1 { font-size: 1.8em; } } +@media (max-width: 400px) { body { font-size: 0.8em; } h1 { font-size: 1.6em; } } +@media (prefers-color-scheme: dark) { html { filter: invert(1); } img { filter: invert(1); } } -- cgit v1.2.3