Refresh docs-site from feature/lightning-talk

Bring the docs-site tree up to date with the richer version built on the
feature/lightning-talk line (guides, expanded reference, landing page) as the
base for adding freeroute/routing documentation. Docs-site only; no source
code from that branch is merged.
This commit is contained in:
Ryan Malloy 2026-07-14 22:47:59 -06:00
parent 963206d548
commit 847e38e8a3
4 changed files with 556 additions and 0 deletions

View File

@ -59,6 +59,7 @@ export default defineConfig({
{ label: "Troubleshooting", slug: "development/troubleshooting" },
],
},
{ label: "Lightning Talk", link: "/lightning-talk/" },
],
head: [
{

View File

@ -75,3 +75,9 @@ mckicad just gives them a voice.
description="Install mckicad and connect it to your first KiCad project."
href="/getting-started/installation/"
/>
<LinkCard
title="Lightning talk: teaching a language model to draw schematics"
description="Ninety seconds on why kicad-sch-api had to go, the SchDocument engine that replaced it, and the synth schematic it drew with zero ERC errors."
href="/lightning-talk/"
/>

View File

@ -0,0 +1,176 @@
---
import "../styles/lightning-talk.css";
const title = "Teaching a language model to draw schematics";
const description =
"A 90-second lightning talk on mckicad: why I replaced kicad-sch-api with an internal SchDocument engine, and what it unlocked.";
// lucide-style icon paths (stroke, viewBox 24)
const icons = {
problem: '<path d="m21.73 18-8-14a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3z"/><path d="M12 9v4"/><path d="M12 17h.01"/>',
engine: '<rect width="16" height="16" x="4" y="4" rx="2"/><rect width="6" height="6" x="9" y="9" rx="1"/><path d="M15 2v2M9 2v2M15 20v2M9 20v2M2 15h2M2 9h2M20 15h2M20 9h2"/>',
wire: '<circle cx="12" cy="4.5" r="2.5"/><circle cx="4.5" cy="19.5" r="2.5"/><circle cx="19.5" cy="19.5" r="2.5"/><path d="M10.4 6.4 6 16.5M13.6 6.4 18 16.5M7 19.5h10"/>',
check: '<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/>',
zap: '<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>',
};
const beats = [
{
n: "01",
icon: icons.problem,
tag: "The problem",
heading: "The library kept lying to me",
body: [
"mckicad is an MCP server that lets a language model design KiCad schematics on its own. For a long time it stood on <code>kicad-sch-api</code> — and that library kept returning wrong answers.",
"Global labels: dropped silently. Custom library pins: empty lists. When the layer you build on returns confident nonsense, nothing downstream can be trusted. So I did the reckless thing and ripped it out.",
],
},
{
n: "02",
icon: icons.engine,
tag: "The engine",
heading: "A parser that round-trips clean",
body: [
"The heart is <code>sexp_tree</code> — a full S-expression parser and serializer for KiCad's native format. Parse it, mutate it, write it back, byte-for-byte identical.",
"On top sits <code>SchDocument</code>: load a schematic and ask it real questions. Where is every pin. What net is this wire on. Which labels actually connect. Answers from the file itself, not a guess.",
],
},
{
n: "03",
icon: icons.wire,
tag: "Autowire",
heading: "It routes around itself",
body: [
"Place the components; autowire pulls the nets for you — with real collision detection, so wires route around each other instead of bridging into shorts.",
"It clamps stub lengths when they would overlap, offsets labels so they don't collide, and tracks power-wire stubs as obstacles. The messy part of drawing a schematic, done carefully.",
],
},
{
n: "04",
icon: icons.check,
tag: "It checks its own work",
heading: "One call, zero ambiguity",
body: [
"<code>run_schematic_erc</code> runs the full electrical rules check and reports back: zero errors — or exactly what's wrong, and where.",
"The engine grades its own homework, so the model gets a hard signal instead of a hopeful vibe.",
],
},
{
n: "05",
icon: icons.zap,
tag: "The proof",
heading: "It drew Timbre",
body: [
"It generated the phase-1 bench schematic for Timbre, a Cypress PSoC-based synthesizer. Zero ERC errors.",
"A language model, placing parts and pulling nets, for real hardware headed to a bench. That's the whole point.",
],
},
];
---
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{title} · mckicad</title>
<meta name="description" content={description} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:type" content="article" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&display=swap"
rel="stylesheet"
/>
</head>
<body class="lt">
<a class="lt-skip" href="#talk">Skip to the talk</a>
<header class="lt-hero">
<div class="lt-wrap">
<p class="lt-kicker">Lightning talk · 90 seconds</p>
<h1>{title}</h1>
<p class="lt-lede">
I ripped out the library that kept lying to me and built an engine
that tells the truth.
</p>
<div class="lt-scope" role="img" aria-label="Terminal readout: running the electrical rules check on the Timbre schematic returns zero errors and zero warnings.">
<div class="lt-scope-bar" aria-hidden="true">
<span class="lt-dot"></span><span class="lt-dot"></span
><span class="lt-dot"></span>
<span>erc · timbre-phase1.kicad_sch</span>
</div>
<div class="lt-scope-body" aria-hidden="true">
<div class="cmd">run_schematic_erc timbre-phase1.kicad_sch</div>
<div class="out">▸ 47 components · 62 nets · 128 pins</div>
<div class="out">▸ checking electrical rules…</div>
<div class="ok">✓ 0 errors · 0 warnings</div>
<div class="out">
ready<span class="lt-cursor" aria-hidden="true"></span>
</div>
</div>
</div>
<nav class="lt-actions" aria-label="Primary">
<a class="lt-btn primary" href="/getting-started/installation/">
Get started
</a>
<a class="lt-btn ghost" href="/guides/autowire/">Read the autowire guide</a>
<a
class="lt-btn ghost"
href="https://git.supported.systems/MCP/kicad-mcp"
rel="noopener">Source</a
>
</nav>
</div>
</header>
<main id="talk" class="lt-wrap">
<ol class="lt-beats">
{
beats.map((b) => (
<li class="lt-beat" data-n={b.n}>
<span class="lt-beat-tag">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true"
set:html={b.icon}
/>
{b.tag}
</span>
<h2>{b.heading}</h2>
{b.body.map((p) => (
<p set:html={p} />
))}
</li>
))
}
</ol>
</main>
<section class="lt-closer">
<div class="lt-wrap">
<div class="lt-stat">
1,900 lines
<small>of workaround code, deleted</small>
</div>
<blockquote>
“Replaced by an engine that just tells the truth.”
</blockquote>
<footer>
Also delivered as a 90-second spoken talk. ·
<a href="/">Back to mckicad</a>
</footer>
</div>
</section>
</body>
</html>

View File

@ -0,0 +1,373 @@
/* mckicad lightning talk standalone phosphor-terminal narrative page.
Palette locked to the docs hero SVG so it reads as part of the product. */
:root {
--lt-bg: #0a1410;
--lt-panel: #0f1a14;
--lt-grid: #1a3a2a;
--lt-wire: #2d8659;
--lt-dim: #5a9e78;
--lt-bright: #a8dbbe;
--lt-ink: #d6ece0;
--lt-mute: #7a9d8c;
--lt-mono: "JetBrains Mono", ui-monospace, "Cascadia Code", "Source Code Pro",
Menlo, Consolas, "DejaVu Sans Mono", monospace;
--lt-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
"Helvetica Neue", Arial, sans-serif;
--lt-maxw: 62rem;
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body.lt {
margin: 0;
background: var(--lt-bg);
color: var(--lt-ink);
font-family: var(--lt-sans);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
/* faint engineering-paper grid, echoing the hero schematic */
background-image:
linear-gradient(var(--lt-grid) 0.5px, transparent 0.5px),
linear-gradient(90deg, var(--lt-grid) 0.5px, transparent 0.5px);
background-size: 28px 28px;
background-position: center top;
}
/* faint CRT scanlines — decorative, killed for reduced-motion / it's opt-out via media */
body.lt::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
z-index: 100;
background: repeating-linear-gradient(
0deg,
rgba(0, 0, 0, 0.15) 0,
rgba(0, 0, 0, 0.15) 1px,
transparent 1px,
transparent 3px
);
mix-blend-mode: multiply;
opacity: 0.35;
}
.lt-wrap {
max-width: var(--lt-maxw);
margin: 0 auto;
padding: 0 1.25rem;
}
a {
color: var(--lt-bright);
}
/* ── skip link ─────────────────────────────── */
.lt-skip {
position: absolute;
left: -999px;
top: 0;
background: var(--lt-wire);
color: #04120c;
padding: 0.5rem 0.9rem;
border-radius: 0 0 0.4rem 0;
font-family: var(--lt-mono);
z-index: 200;
}
.lt-skip:focus {
left: 0;
}
/* ── hero ──────────────────────────────────── */
.lt-hero {
padding: clamp(3rem, 9vw, 6.5rem) 0 clamp(2rem, 6vw, 4rem);
text-align: center;
}
.lt-kicker {
font-family: var(--lt-mono);
font-size: 0.8rem;
letter-spacing: 0.28em;
text-transform: uppercase;
color: var(--lt-wire);
margin: 0 0 1.1rem;
}
.lt-hero h1 {
font-size: clamp(2.1rem, 6.5vw, 4rem);
line-height: 1.05;
letter-spacing: -0.02em;
margin: 0 auto 1.1rem;
max-width: 18ch;
color: #eafff4;
text-shadow: 0 0 24px rgba(45, 134, 89, 0.35);
}
.lt-lede {
font-size: clamp(1.05rem, 2.4vw, 1.35rem);
color: var(--lt-mute);
max-width: 46ch;
margin: 0 auto 2.25rem;
}
/* terminal "scope" panel */
.lt-scope {
max-width: 40rem;
margin: 0 auto 2rem;
text-align: left;
background: var(--lt-panel);
border: 1px solid var(--lt-grid);
border-radius: 0.7rem;
box-shadow: 0 0 0 1px rgba(45, 134, 89, 0.15),
0 24px 60px -20px rgba(0, 0, 0, 0.8);
overflow: hidden;
}
.lt-scope-bar {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.6rem 0.9rem;
border-bottom: 1px solid var(--lt-grid);
font-family: var(--lt-mono);
font-size: 0.75rem;
color: var(--lt-mute);
}
.lt-dot {
width: 0.7rem;
height: 0.7rem;
border-radius: 50%;
background: var(--lt-grid);
}
.lt-scope-bar span {
margin-left: auto;
}
.lt-scope-body {
padding: 1.1rem 1.2rem 1.3rem;
font-family: var(--lt-mono);
font-size: clamp(0.8rem, 1.9vw, 0.95rem);
line-height: 1.7;
}
.lt-scope-body .cmd {
color: var(--lt-bright);
}
.lt-scope-body .cmd::before {
content: "mckicad ";
color: var(--lt-wire);
}
.lt-scope-body .out {
color: var(--lt-dim);
}
.lt-scope-body .ok {
color: #7cf0b0;
text-shadow: 0 0 12px rgba(124, 240, 176, 0.5);
}
.lt-cursor {
display: inline-block;
width: 0.6ch;
height: 1.05em;
vertical-align: text-bottom;
background: var(--lt-bright);
margin-left: 0.15rem;
animation: lt-blink 1.1s steps(1) infinite;
}
@keyframes lt-blink {
50% {
opacity: 0;
}
}
/* action buttons */
.lt-actions {
display: flex;
gap: 0.75rem;
justify-content: center;
flex-wrap: wrap;
}
.lt-btn {
font-family: var(--lt-mono);
font-size: 0.9rem;
text-decoration: none;
padding: 0.7rem 1.25rem;
border-radius: 0.45rem;
border: 1px solid var(--lt-wire);
display: inline-flex;
align-items: center;
gap: 0.5rem;
transition: background 0.15s ease, color 0.15s ease, transform 0.15s ease;
}
.lt-btn svg {
width: 1.05em;
height: 1.05em;
}
.lt-btn.primary {
background: var(--lt-wire);
color: #04120c;
font-weight: 600;
}
.lt-btn.ghost {
color: var(--lt-bright);
background: transparent;
}
.lt-btn:hover {
transform: translateY(-1px);
}
.lt-btn.ghost:hover {
background: rgba(45, 134, 89, 0.12);
}
/* ── beats ─────────────────────────────────── */
.lt-beats {
list-style: none;
margin: 0;
padding: clamp(1rem, 4vw, 2.5rem) 0;
display: grid;
gap: clamp(1.25rem, 3vw, 2rem);
}
.lt-beat {
position: relative;
background: linear-gradient(
180deg,
rgba(15, 26, 20, 0.9),
rgba(10, 20, 16, 0.9)
);
border: 1px solid var(--lt-grid);
border-left: 3px solid var(--lt-wire);
border-radius: 0 0.7rem 0.7rem 0;
padding: clamp(1.4rem, 3.5vw, 2rem) clamp(1.3rem, 4vw, 2.4rem);
overflow: hidden;
}
/* ghosted giant numeral, SpiceBook-style */
.lt-beat::after {
content: attr(data-n);
position: absolute;
top: -0.35em;
right: 0.1em;
font-family: var(--lt-mono);
font-weight: 700;
font-size: clamp(5rem, 16vw, 9rem);
line-height: 1;
color: var(--lt-wire);
opacity: 0.07;
pointer-events: none;
}
.lt-beat-tag {
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-family: var(--lt-mono);
font-size: 0.72rem;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--lt-wire);
margin-bottom: 0.7rem;
}
.lt-beat-tag svg {
width: 1.05rem;
height: 1.05rem;
stroke: var(--lt-wire);
}
.lt-beat h2 {
font-size: clamp(1.35rem, 3.4vw, 1.9rem);
line-height: 1.15;
letter-spacing: -0.01em;
margin: 0 0 0.6rem;
color: #eafff4;
max-width: 24ch;
}
.lt-beat p {
margin: 0;
color: var(--lt-mute);
max-width: 62ch;
}
.lt-beat p + p {
margin-top: 0.7rem;
}
.lt-beat code {
font-family: var(--lt-mono);
font-size: 0.9em;
color: var(--lt-bright);
background: rgba(45, 134, 89, 0.12);
padding: 0.1rem 0.4rem;
border-radius: 0.3rem;
}
/* ── proof / closer ────────────────────────── */
.lt-closer {
text-align: center;
padding: clamp(3rem, 8vw, 5.5rem) 0 clamp(4rem, 9vw, 6rem);
border-top: 1px solid var(--lt-grid);
margin-top: clamp(1rem, 4vw, 2rem);
}
.lt-stat {
font-family: var(--lt-mono);
font-weight: 700;
font-size: clamp(3rem, 12vw, 6.5rem);
line-height: 1;
color: #eafff4;
letter-spacing: -0.03em;
text-shadow: 0 0 30px rgba(45, 134, 89, 0.4);
}
.lt-stat small {
display: block;
font-family: var(--lt-mono);
font-weight: 400;
font-size: clamp(0.85rem, 2.2vw, 1.05rem);
letter-spacing: 0.24em;
text-transform: uppercase;
color: var(--lt-wire);
margin-top: 0.6rem;
}
.lt-closer blockquote {
margin: 2rem auto 0;
max-width: 34ch;
font-size: clamp(1.15rem, 3vw, 1.6rem);
line-height: 1.35;
color: var(--lt-ink);
font-style: italic;
}
.lt-closer footer {
margin-top: 2.75rem;
font-family: var(--lt-mono);
font-size: 0.82rem;
color: var(--lt-mute);
}
.lt-closer footer a {
color: var(--lt-bright);
}
/* ── responsive niceties ───────────────────── */
@media (min-width: 46rem) {
.lt-beat {
padding-right: clamp(4rem, 14vw, 8rem);
}
}
/* ── accessibility: honour reduced motion ──── */
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
.lt-cursor {
animation: none;
}
body.lt::before {
display: none;
}
.lt-btn {
transition: none;
}
}
/* clear focus rings everywhere */
a:focus-visible,
.lt-btn:focus-visible {
outline: 2px solid var(--lt-bright);
outline-offset: 3px;
border-radius: 0.3rem;
}