diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..af61ec4 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,70 @@ +name: Deploy GitHub Pages + +# Builds the fleet landing page (docs/index.html, via the shared Developer-Tools-Directory +# template) AND the local examples gallery (docs/gallery/index.html, generated from +# examples/gallery.json by scripts/build_gallery.py). Both ship in the single docs/ artifact; +# the fleet build only writes docs/index.html + docs/fonts/ + docs/assets/, so it never +# clobbers docs/gallery/. When the fleet template gains examples support, the gallery step is +# retired and the data (examples/gallery.json) migrates onto the shared template. + +on: + push: + branches: [main] + paths: + - "skills/**" + - "rules/**" + - "mcp-tools.json" + - "site.json" + - ".cursor-plugin/plugin.json" + - "assets/**" + - "examples/gallery.json" + - "docs/gallery/**" + - "scripts/build_gallery.py" + workflow_dispatch: + +permissions: + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build-and-deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Checkout site template + uses: actions/checkout@v4 + with: + repository: TMHSDigital/Developer-Tools-Directory + sparse-checkout: site-template + path: _template + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - run: pip install Jinja2 + + - name: Build fleet landing page + run: python _template/site-template/build_site.py --repo-root . --out docs + + - name: Build local examples gallery (from examples/gallery.json) + # Stdlib-only; regenerates docs/gallery/index.html so the committed page can never + # drift from gallery.json. The committed docs/gallery/assets/*.webp are untouched. + run: python scripts/build_gallery.py + + - uses: actions/configure-pages@v5 + + - uses: actions/upload-pages-artifact@v4 + with: + path: docs + + - uses: actions/deploy-pages@v5 + id: deployment diff --git a/README.md b/README.md index e9835ec..f24e155 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,8 @@ The content is consumed by AI coding agents (Cursor, Claude Code, any MCP-capabl Runnable, smoke-gated demos live in [`examples/`](examples/) — each is executed headless on both Blender 4.5 LTS and 5.1 by the `blender-smoke` workflow, so the screenshots reflect code -that actually runs. +that actually runs. Browse them in the +**[examples gallery](https://tmhsdigital.github.io/Blender-Developer-Tools/gallery/)**. diff --git a/docs/gallery/assets/gn-sdf-remesh-hero.webp b/docs/gallery/assets/gn-sdf-remesh-hero.webp new file mode 100644 index 0000000..2b23e25 Binary files /dev/null and b/docs/gallery/assets/gn-sdf-remesh-hero.webp differ diff --git a/docs/gallery/assets/swatch-grid-hero.webp b/docs/gallery/assets/swatch-grid-hero.webp new file mode 100644 index 0000000..5e01cd0 Binary files /dev/null and b/docs/gallery/assets/swatch-grid-hero.webp differ diff --git a/docs/gallery/assets/turntable-hero.webp b/docs/gallery/assets/turntable-hero.webp new file mode 100644 index 0000000..23e5745 Binary files /dev/null and b/docs/gallery/assets/turntable-hero.webp differ diff --git a/docs/gallery/index.html b/docs/gallery/index.html new file mode 100644 index 0000000..4886f73 --- /dev/null +++ b/docs/gallery/index.html @@ -0,0 +1,93 @@ + + + + + + Examples Gallery — Blender Developer Tools + + + + +
+ ← Blender Developer Tools +

Examples Gallery

+

Runnable, smoke-gated demos. Each is executed headless on Blender 4.5 LTS and 5.1 by the + blender-smoke workflow, so every render reflects code that actually runs.

+
+
+
+ + swatch-grid — Procedural Principled materials — metal and dielectric, the emission pattern, and the cross-version set_specular shim + +
+

swatch-grid

+

Procedural Principled materials — metal and dielectric, the emission pattern, and the cross-version set_specular shim.

+

witnesses EEVEE engine-id mapping: BLENDER_EEVEE on 5.x, BLENDER_EEVEE_NEXT on 4.2–4.5.

+ View example → +
+
+
+ + turntable — A slotted-actions Z-rotation turntable keyed through the cross-version channelbag path (get_channelbag_for_slot) + +
+

turntable

+

A slotted-actions Z-rotation turntable keyed through the cross-version channelbag path (get_channelbag_for_slot).

+

witnesses Slotted-actions boundary: ensure-helper channelbag on 5.x, strip.channelbag on 4.4/4.5.

+ View example → +
+
+
+ + gn-sdf-remesh — A Geometry Nodes SDF remesh (MeshToSDFGrid → GridToMesh at the SDF zero-level), with a Set Material node carrying the material through the remesh + +
+

gn-sdf-remesh

+

A Geometry Nodes SDF remesh (MeshToSDFGrid → GridToMesh at the SDF zero-level), with a Set Material node carrying the material through the remesh.

+

witnesses An SDF grid is meshed with Grid to Mesh, not Volume to Mesh; GN geometry needs Set Material or it renders untextured.

+ View example → +
+
+
+ + + diff --git a/examples/gallery.json b/examples/gallery.json new file mode 100644 index 0000000..9f03ee1 --- /dev/null +++ b/examples/gallery.json @@ -0,0 +1,30 @@ +{ + "_comment": "FORWARD-COMPATIBLE SOURCE OF TRUTH for the examples gallery. The local page at docs/gallery/index.html is GENERATED from this file by scripts/build_gallery.py -- do not hand-edit the HTML. When the fleet template (Developer-Tools-Directory: site-template/build_site.py + template.html.j2) gains examples support (see ROADMAP: 'Fleet Pages examples support'), it reads this same file and the local page is retired. That migration is a lift-and-shift, not a rewrite: keep this schema stable. Per-entry schema: {name, dir, teaches, witnessesFix, hero, preview}; hero/preview/dir are repo-root-relative.", + "repoBaseUrl": "https://github.com/TMHSDigital/Blender-Developer-Tools/tree/main", + "examples": [ + { + "name": "swatch-grid", + "dir": "examples/swatch-grid", + "teaches": "Procedural Principled materials — metal and dielectric, the emission pattern, and the cross-version set_specular shim.", + "witnessesFix": "EEVEE engine-id mapping: BLENDER_EEVEE on 5.x, BLENDER_EEVEE_NEXT on 4.2–4.5.", + "hero": "docs/gallery/assets/swatch-grid-hero.webp", + "preview": "examples/swatch-grid/preview.webp" + }, + { + "name": "turntable", + "dir": "examples/turntable", + "teaches": "A slotted-actions Z-rotation turntable keyed through the cross-version channelbag path (get_channelbag_for_slot).", + "witnessesFix": "Slotted-actions boundary: ensure-helper channelbag on 5.x, strip.channelbag on 4.4/4.5.", + "hero": "docs/gallery/assets/turntable-hero.webp", + "preview": "examples/turntable/preview.webp" + }, + { + "name": "gn-sdf-remesh", + "dir": "examples/gn-sdf-remesh", + "teaches": "A Geometry Nodes SDF remesh (MeshToSDFGrid → GridToMesh at the SDF zero-level), with a Set Material node carrying the material through the remesh.", + "witnessesFix": "An SDF grid is meshed with Grid to Mesh, not Volume to Mesh; GN geometry needs Set Material or it renders untextured.", + "hero": "docs/gallery/assets/gn-sdf-remesh-hero.webp", + "preview": "examples/gn-sdf-remesh/preview.webp" + } + ] +} diff --git a/scripts/build_gallery.py b/scripts/build_gallery.py new file mode 100644 index 0000000..7fcee23 --- /dev/null +++ b/scripts/build_gallery.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +"""Generate the standalone examples gallery page from examples/gallery.json. + +This page is a LOCAL, this-repo gallery that rides alongside the fleet-generated +docs/index.html (which build_site.py owns and overwrites). It writes ONLY to +docs/gallery/ so it never collides with the fleet build's docs/index.html, +docs/fonts/, or docs/assets/. + +examples/gallery.json is the forward-compatible source of truth: when the fleet +template gains examples support, it consumes the same data and this script and +page are retired. Run after editing gallery.json: + + python scripts/build_gallery.py + +Stdlib only (no Jinja2), so the Pages workflow can regenerate it without extra deps. +""" +import html +import json +import sys +from pathlib import Path + +REPO = Path(__file__).resolve().parent.parent +DATA = REPO / "examples" / "gallery.json" +OUT = REPO / "docs" / "gallery" / "index.html" + +CARD = """
+ + {name} — {teaches_plain} + +
+

{name}

+

{teaches}

+

witnesses {witnesses}

+ View example → +
+
""" + +PAGE = """ + + + + + Examples Gallery — Blender Developer Tools + + + + +
+ ← Blender Developer Tools +

Examples Gallery

+

Runnable, smoke-gated demos. Each is executed headless on Blender 4.5 LTS and 5.1 by the + blender-smoke workflow, so every render reflects code that actually runs.

+
+
+{cards} +
+ + + +""" + + +def strip_to_page_relative(repo_rel: str) -> str: + """docs/gallery/assets/x.webp -> assets/x.webp (relative to the gallery page).""" + prefix = "docs/gallery/" + return repo_rel[len(prefix):] if repo_rel.startswith(prefix) else repo_rel + + +def main() -> int: + data = json.loads(DATA.read_text(encoding="utf-8")) + base = data["repoBaseUrl"].rstrip("/") + examples = data["examples"] + if not examples: + print("ERROR: no examples in gallery.json", file=sys.stderr) + return 2 + + cards = [] + for ex in examples: + hero_rel = strip_to_page_relative(ex["hero"]) + if not (REPO / ex["hero"]).is_file(): + print(f"ERROR: hero image missing: {ex['hero']}", file=sys.stderr) + return 3 + cards.append(CARD.format( + href=html.escape(f"{base}/{ex['dir']}"), + hero=html.escape(hero_rel), + name=html.escape(ex["name"]), + teaches=html.escape(ex["teaches"]), + teaches_plain=html.escape(ex["teaches"].split(".")[0]), + witnesses=html.escape(ex["witnessesFix"]), + )) + + OUT.parent.mkdir(parents=True, exist_ok=True) + OUT.write_text(PAGE.format(cards="\n".join(cards)), encoding="utf-8") + print(f"Wrote {OUT} ({len(examples)} examples)") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/site.json b/site.json new file mode 100644 index 0000000..c658b37 --- /dev/null +++ b/site.json @@ -0,0 +1,14 @@ +{ + "accent": "#7c3aed", + "accentLight": "#a78bfa", + "heroGradientFrom": "#0d1117", + "heroGradientTo": "#161b22", + "links": { + "github": "https://github.com/TMHSDigital/Blender-Developer-Tools" + }, + "installSteps": [ + "Open Cursor IDE and go to Settings > Extensions", + "Search for Blender Developer Tools", + "Click Install and reload" + ] +}