feat: ✨ WebGL graphics blog post, Gerber demo on landing page, open-source license notices

- Blog: porting-kicad-graphics-to-webgl post + GAL native/WebGL comparison images and <GalCompare>
- Landing page: embed lazily-loaded Gerber demo section + footer "Live demo" link
- Licensing: /licenses page (GPLv3 + wxWindows/LGPL-v2 breakdown, ahilss credit, corresponding-source offer) and a footer build-SHA link
- Docs: CLAUDE.md + README note to bump the hardcoded footer BUILD_SHA on each release

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Viktor Vaczi 2026-06-18 14:06:37 +02:00
commit 8b438b48b2
13 changed files with 403 additions and 14 deletions

View file

@ -5,6 +5,13 @@ import EECredit from './EECredit.astro';
const year = new Date().getFullYear();
// TODO(links): add Roadmap (/roadmap) and dedicated How-it-works/Open pages when they ship.
const GITHUB_URL = 'https://github.com/emergence-engineering';
// Build provenance. Hardcoded because the site ships as static assets (R2), so
// there's no build-time git/env to read it from. BUMP THIS to the deployed
// main-repo (pcbjam) commit on each release — it pins the KiCad + wxWidgets
// submodule revisions implicitly. See /licenses for the source-code offer.
const BUILD_SHA = '695a460';
const BUILD_COMMIT_URL = `https://github.com/emergence-engineering/pcbjam/commit/${BUILD_SHA}`;
---
<footer class="site-footer">
@ -28,7 +35,7 @@ const GITHUB_URL = 'https://github.com/emergence-engineering';
<nav class="links stack" aria-label="Product">
<a href="/#how-it-works">How it works</a>
<a href="/#open">Open &amp; yours</a>
<a href="/#multiplayer">Multiplayer</a>
<a href="/#demo">Live demo</a>
<a href="/#waitlist">Join the waitlist</a>
</nav>
</div>
@ -44,12 +51,20 @@ const GITHUB_URL = 'https://github.com/emergence-engineering';
<a href="/privacy">Privacy Policy</a>
<a href="/terms">Terms of Service</a>
<a href="/cookies">Cookie Policy</a>
<a href="/licenses">Open-Source Licenses</a>
</nav>
</div>
</div>
<div class="container bottom">
<span>&copy; {year} Emergence Engineering</span>
<span
>&copy; {year} Emergence Engineering &middot; <a
class="build"
href={BUILD_COMMIT_URL}
rel="noopener"
target="_blank">build {BUILD_SHA}</a
></span
>
<span class="attrib muted">
Built on the open-source <a href="https://www.kicad.org" rel="noopener" target="_blank"
>KiCad</a
@ -124,6 +139,15 @@ const GITHUB_URL = 'https://github.com/emergence-engineering';
font-size: 0.82rem;
max-width: 42rem;
}
.build {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.82rem;
color: var(--fg-muted);
}
.build:hover {
color: var(--fg);
text-decoration: none;
}
@media (max-width: 820px) {
.cols {

View file

@ -0,0 +1,128 @@
---
/**
* Native-vs-WebGL GAL comparison strips for the WebGL-port blog post.
*
* Each scenario from the GAL regression suite (tests/gal-regression) is rendered
* twice — once by KiCad's native OpenGL GAL, once by the new WebGL GAL — and the
* two are laid side by side so the reader can eyeball parity, exactly the way the
* suite is validated. Source PNGs are the committed baselines (1600×1200);
* astro:assets <Picture> emits responsive AVIF/WebP at build time.
*/
import { Picture } from 'astro:assets';
import alphaNative from '../assets/gal/gal-alpha-blending-native.png';
import alphaWebgl from '../assets/gal/gal-alpha-blending-webgl.png';
import xfNative from '../assets/gal/gal-transform-api-native.png';
import xfWebgl from '../assets/gal/gal-transform-api-webgl.png';
const pairs = [
{
title: 'Alpha blending',
native: alphaNative,
webgl: alphaWebgl,
caption:
'Overlapping translucent fills plus a fading alpha ramp, native OpenGL and WebGL come out close.',
},
{
title: 'The Transform() API',
native: xfNative,
webgl: xfWebgl,
caption:
'A scenario the other way round: KiCad’s Transform() entry point is dead code on the desktop, so our "correct" implementation doesn\'t matter in the end.',
},
];
---
<div class="gal-compare not-prose">
{pairs.map((p) => (
<figure class="gal-pair">
<div class="gal-pair__grid">
<div class="gal-shot">
<span class="gal-shot__tag">Native OpenGL</span>
<Picture
src={p.native}
formats={['avif', 'webp']}
widths={[320, 640, 800]}
sizes="(max-width: 700px) 90vw, 340px"
alt={`${p.title} rendered by KiCad's native OpenGL GAL`}
loading="lazy"
decoding="async"
/>
</div>
<div class="gal-shot">
<span class="gal-shot__tag gal-shot__tag--webgl">WebGL</span>
<Picture
src={p.webgl}
formats={['avif', 'webp']}
widths={[320, 640, 800]}
sizes="(max-width: 700px) 90vw, 340px"
alt={`${p.title} rendered by the new WebGL GAL`}
loading="lazy"
decoding="async"
/>
</div>
</div>
<figcaption><strong>{p.title}.</strong> {p.caption}</figcaption>
</figure>
))}
</div>
<style>
.gal-compare {
display: grid;
gap: 1.75rem;
margin: 2rem 0;
}
.gal-pair {
margin: 0;
}
.gal-pair__grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.6rem;
}
.gal-shot {
position: relative;
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
background: var(--bg-soft);
}
/* astro:assets <Picture> emits the <img>; style it globally within the shot. */
.gal-shot :global(img) {
display: block;
width: 100%;
height: auto;
aspect-ratio: 4 / 3;
object-fit: contain;
}
.gal-shot__tag {
position: absolute;
top: 0.4rem;
left: 0.4rem;
z-index: 1;
font-family: var(--font-mono);
font-size: 0.66rem;
letter-spacing: 0.02em;
color: var(--fg);
background: rgba(11, 16, 32, 0.82);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 0.12rem 0.4rem;
}
.gal-shot__tag--webgl {
color: #fff;
background: var(--brand-600);
border-color: transparent;
}
.gal-pair figcaption {
margin-top: 0.6rem;
font-size: 0.85rem;
line-height: 1.5;
color: var(--fg-muted);
}
@media (max-width: 460px) {
.gal-pair__grid {
grid-template-columns: 1fr;
}
}
</style>

View file

@ -16,7 +16,7 @@
*/
const poster = "/gerber-demo/poster.png"; // captured screenshot; optional fallback art
const caption =
"KiCad's Gerber viewer (gerbview), compiled to WebAssembly, running the tiny_tapeout demo board entirely in your browser.";
"KiCad's Gerber viewer compiled to WebAssembly, running the tiny_tapeout demo board ";
---
<figure class="gerber-demo not-prose" data-state="idle">