Skip to content

Commit f5463f0

Browse files
committed
Add /prototyping/ with layout demos and no-cache headers
- public/_headers gains a /prototyping/* rule with Cache-Control: no-cache, must-revalidate so review pages always show the latest deploy. Generated by fingerprint_assets.py. - public/marginalia-gestalt.html and operators-polish-comparison.html move into public/prototyping/. - scripts/build_marginalia.py writes to the new location. - src/marginalia.py adds two new figures, iterator-unroll and scope-rings, used by the journey spread prototype. - scripts/build_prototypes.py is a new builder producing seven prototypes plus an index, all under /prototyping/: index.html listing of all prototypes layout-margin.html floats figure into the implicit outer margin (the planned approach) layout-inline-above.html figure block above prose layout-inline-between.html figure between prose and code layout-third-column.html figure as a third lp-cell column (squeezes prose+code) layout-summary-bottom.html figures collected after playground (the spec's mobile fallback) journey-spread.html Streams journey with per-section figure-icons next to each heading Each prototype renders the real lesson content (mutability or the Streams journey) so the layout strategies can be compared on representative material. https://claude.ai/code/session_01MazwoRWAihW6dwso3fMCHE
1 parent 68b3eb7 commit f5463f0

15 files changed

Lines changed: 1036 additions & 3 deletions

public/_headers

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@
99

1010
/favicon.svg
1111
Cache-Control: public, max-age=31536000, immutable
12+
13+
/prototyping/*
14+
Cache-Control: no-cache, must-revalidate

public/prototyping/index.html

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Visual explainer prototypes · Prototype</title>
6+
<link rel="stylesheet" href="/site.css">
7+
<style>
8+
.prototype-banner {
9+
margin: 0 0 var(--space-4);
10+
padding: var(--space-2) var(--space-3);
11+
background: rgba(0, 0, 0, 0.04);
12+
border: 1px dashed var(--hairline);
13+
border-radius: .5rem;
14+
color: var(--muted);
15+
font-size: .85rem;
16+
}
17+
.prototype-banner strong { color: var(--text); font-weight: 600; }
18+
.prototype-banner a { color: inherit; }
19+
20+
.prototype-list { list-style: none; padding: 0; margin: var(--space-4) 0 0; }
21+
.prototype-list li { padding: var(--space-3) 0; border-bottom: 1px dashed var(--hairline-soft); }
22+
.prototype-list li:first-child { border-top: 1px dashed var(--hairline-soft); }
23+
.prototype-list strong { font-weight: 600; font-size: 1.05rem; }
24+
.prototype-list .meta { margin: .25rem 0 0; max-width: 60ch; }
25+
26+
</style>
27+
</head>
28+
<body>
29+
<div class="prototype-banner"><strong>Prototype</strong> · All prototypes · <a href="/prototyping/">all prototypes</a></div>
30+
31+
<article class="example-shell">
32+
<section class="example-intro">
33+
<p class="eyebrow">Prototypes · cache: no-cache, must-revalidate</p>
34+
<h1>Visual explainer prototypes</h1>
35+
<p class="meta">Layout sketches for embedding marginalia in real example pages. Each prototype renders the same lesson with a different figure-placement strategy so the alternatives can be compared.</p>
36+
</section>
37+
<ul class="prototype-list"><li><a class="text-link" href="/prototyping/marginalia-gestalt.html"><strong>Marginalia gestalt</strong></a><p class="meta">Every journey and example as a card, drawn from the shared grammar. Generated by build_marginalia.py; pure design review.</p></li><li><a class="text-link" href="/prototyping/operators-polish-comparison.html"><strong>Operators alignment polish</strong></a><p class="meta">Side-by-side before/after for the tree-edge alignment fix; demonstrates Canvas.connect().</p></li><li><a class="text-link" href="/prototyping/layout-margin.html"><strong>Layout · margin (planned)</strong></a><p class="meta">Figure floats in the implicit outer margin via position: absolute. Today&#x27;s centered layout is bit-for-bit unchanged. Mutability lesson with the aliasing-mutation figure on cell 0.</p></li><li><a class="text-link" href="/prototyping/layout-inline-above.html"><strong>Layout · inline above prose</strong></a><p class="meta">Figure renders as a block above the cell prose, full prose-column width. Universal across viewports. Eye flow: see picture, read prose, read code.</p></li><li><a class="text-link" href="/prototyping/layout-inline-between.html"><strong>Layout · inline between prose and code</strong></a><p class="meta">Figure renders between the prose paragraph and the code block. Eye flow: read intuition, see picture, read code.</p></li><li><a class="text-link" href="/prototyping/layout-third-column.html"><strong>Layout · third lp-cell column</strong></a><p class="meta">lp-cell becomes a 3-column grid: prose | code | figure. Compromise: works on narrower screens but squeezes prose+code.</p></li><li><a class="text-link" href="/prototyping/layout-summary-bottom.html"><strong>Layout · summary at end</strong></a><p class="meta">All figures collected in a Diagrams section after the runnable example. The current spec&#x27;s mobile fallback. Universal.</p></li><li><a class="text-link" href="/prototyping/journey-spread.html"><strong>Journey spread</strong></a><p class="meta">Streams journey with per-section figure-icons next to each section heading. Visual table of contents.</p></li></ul>
38+
</article>
39+
40+
</body>
41+
</html>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Journey · Streams · Prototype</title>
6+
<link rel="stylesheet" href="/site.css">
7+
<style>
8+
.prototype-banner {
9+
margin: 0 0 var(--space-4);
10+
padding: var(--space-2) var(--space-3);
11+
background: rgba(0, 0, 0, 0.04);
12+
border: 1px dashed var(--hairline);
13+
border-radius: .5rem;
14+
color: var(--muted);
15+
font-size: .85rem;
16+
}
17+
.prototype-banner strong { color: var(--text); font-weight: 600; }
18+
.prototype-banner a { color: inherit; }
19+
20+
.journey-section { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
21+
@media (min-width: 900px) {
22+
.journey-section { grid-template-columns: minmax(0, 1.4fr) minmax(220px, 280px); align-items: start; }
23+
}
24+
.journey-figure { margin: 0; padding: 0; }
25+
.journey-figure svg { width: 100%; height: auto; display: block; }
26+
.journey-figure figcaption { margin-top: var(--space-2); color: var(--muted); font-size: .85rem; font-style: italic; }
27+
28+
</style>
29+
</head>
30+
<body>
31+
<div class="prototype-banner"><strong>Prototype</strong> · Streams journey with per-section figure-icons next to each section heading. Each figure summarises the section's conceptual shift. · <a href="/prototyping/">all prototypes</a></div>
32+
33+
<article class="example-shell journey-page">
34+
<div class="example-top"><a class="text-link" href="/">← Home</a></div>
35+
<section class="example-intro">
36+
<p class="eyebrow">Journey</p>
37+
<h1>Streams</h1>
38+
<p class="meta">This journey shows how Python code chooses paths, repeats work, stops early, and eventually treats iteration as streams of values.</p>
39+
</section>
40+
<section class="journey-section"><div><h2>Make decisions explicitly.</h2><p class="meta">Learners should first understand ordinary branching before reaching for compact expression forms.</p><ul class="journey-list"><li><a class="text-link journey-item-title" href="/examples/conditionals">Conditionals</a><p class="meta">choose between branches with clear predicates</p></li><li><a class="text-link journey-item-title" href="/examples/assignment-expressions">Assignment Expressions</a><p class="meta">name an intermediate value inside a condition when it improves clarity</p></li><li><a class="text-link journey-item-title" href="/examples/match-statements">Match Statements</a><p class="meta">dispatch on the shape of data rather than only on boolean tests</p></li><li><a class="text-link journey-item-title" href="/examples/advanced-match-patterns">Advanced Match Patterns</a><p class="meta">combine destructuring, alternatives, and guards in pattern matching</p></li><li><p class="journey-gap-label">Gap · Guard clause style</p><p class="meta">show how early returns reduce nested conditional code</p></li></ul></div><figure class="journey-figure"><svg viewBox="0 0 220 175" xmlns="http://www.w3.org/2000/svg"><text x="0" y="12" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="8" fill="#6e7888" text-anchor="start" letter-spacing="0.5">BEFORE</text><rect x="0" y="18" width="60" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="30.0" y="34.0" font-family="'Iowan Old Style', Charter, Georgia, serif" font-size="11" fill="#2b3441" text-anchor="middle" font-style="italic">first</text><rect x="0" y="48" width="60" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="30.0" y="64.0" font-family="'Iowan Old Style', Charter, Georgia, serif" font-size="11" fill="#2b3441" text-anchor="middle" font-style="italic">second</text><line x1="60" y1="30" x2="80.03839178306819" y2="42.331318020349656" stroke="#2b3441" stroke-width="1.0"/><polygon points="86,46 78.57091899120806,44.71596130712238 81.50586457492832,39.946674733576934" fill="#2b3441"/><line x1="60" y1="60" x2="79.83670230054477" y2="49.31869876124512" stroke="#2b3441" stroke-width="1.0"/><polygon points="86,46 81.16418180504282,51.784017841027215 78.50922279604673,46.85337968146303" fill="#2b3441"/><rect x="88" y="32" width="88" height="28" fill="rgba(0,0,0,0.06)" stroke="#2b3441" stroke-width="1.0"/><text x="132.0" y="50.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">["python"]</text><text x="0" y="100" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="8" fill="#6e7888" text-anchor="start" letter-spacing="0.5">AFTER APPEND</text><rect x="0" y="108" width="60" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="30.0" y="124.0" font-family="'Iowan Old Style', Charter, Georgia, serif" font-size="11" fill="#2b3441" text-anchor="middle" font-style="italic">first</text><rect x="0" y="138" width="60" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="30.0" y="154.0" font-family="'Iowan Old Style', Charter, Georgia, serif" font-size="11" fill="#2b3441" text-anchor="middle" font-style="italic">second</text><line x1="60" y1="120" x2="80.03839178306819" y2="132.33131802034967" stroke="#2b3441" stroke-width="1.0"/><polygon points="86,136 78.57091899120806,134.7159613071224 81.50586457492832,129.94667473357694" fill="#2b3441"/><line x1="60" y1="150" x2="79.83670230054477" y2="139.31869876124512" stroke="#2b3441" stroke-width="1.0"/><polygon points="86,136 81.16418180504282,141.7840178410272 78.50922279604673,136.85337968146303" fill="#2b3441"/><rect x="88" y="122" width="130" height="28" fill="rgba(0,0,0,0.06)" stroke="#2b3441" stroke-width="1.0"/><text x="153.0" y="140.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">["python","workers"]</text></svg><figcaption>Branches map values to outcomes.</figcaption></figure></section><section class="journey-section"><div><h2>Choose the right loop shape.</h2><p class="meta">This section distinguishes counting, consuming, open-ended repetition, and loop completion logic.</p><ul class="journey-list"><li><a class="text-link journey-item-title" href="/examples/for-loops">For Loops</a><p class="meta">consume values from an iterable</p></li><li><a class="text-link journey-item-title" href="/examples/break-and-continue">Break and Continue</a><p class="meta">interrupt or skip loop work intentionally</p></li><li><a class="text-link journey-item-title" href="/examples/loop-else">Loop Else</a><p class="meta">attach completion logic to loops that did not break</p></li><li><a class="text-link journey-item-title" href="/examples/while-loops">While Loops</a><p class="meta">repeat while a condition must be rechecked</p></li><li><p class="journey-gap-label">Gap · Sentinel iteration</p><p class="meta">show `iter(callable, sentinel)` for repeated reads until a marker appears</p></li></ul></div><figure class="journey-figure"><svg viewBox="0 0 220 130" xmlns="http://www.w3.org/2000/svg"><rect x="20" y="8" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="32.0" y="24.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">a</text><rect x="44" y="8" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="56.0" y="24.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">b</text><rect x="68" y="8" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="80.0" y="24.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">c</text><rect x="92" y="8" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="104.0" y="24.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">d</text><polygon points="32,7 28,1 36,1" fill="#c85a4a"/><text x="124" y="24" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="9" fill="#6e7888" text-anchor="start">next()</text><rect x="20" y="38" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="32.0" y="54.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">a</text><rect x="44" y="38" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="56.0" y="54.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">b</text><rect x="68" y="38" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="80.0" y="54.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">c</text><rect x="92" y="38" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="104.0" y="54.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">d</text><polygon points="56,37 52,31 60,31" fill="#c85a4a"/><text x="124" y="54" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="9" fill="#6e7888" text-anchor="start">next()</text><rect x="20" y="68" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="32.0" y="84.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">a</text><rect x="44" y="68" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="56.0" y="84.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">b</text><rect x="68" y="68" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="80.0" y="84.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">c</text><rect x="92" y="68" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="104.0" y="84.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">d</text><polygon points="80,67 76,61 84,61" fill="#c85a4a"/><text x="124" y="84" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="9" fill="#6e7888" text-anchor="start">next()</text><rect x="20" y="98" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="32.0" y="114.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">a</text><rect x="44" y="98" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="56.0" y="114.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">b</text><rect x="68" y="98" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="80.0" y="114.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">c</text><rect x="92" y="98" width="24" height="24" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="104.0" y="114.0" font-family="'JetBrains Mono', 'IBM Plex Mono', Menlo, monospace" font-size="10" fill="#2b3441" text-anchor="middle">d</text><polygon points="104,97 100,91 108,91" fill="#c85a4a"/><text x="124" y="114" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="9" fill="#6e7888" text-anchor="start">next() — last</text></svg><figcaption>Each next() advances one cell along a sequence.</figcaption></figure></section><section class="journey-section"><div><h2>Recognize iteration as a protocol.</h2><p class="meta">The important mental shift is that loops consume value streams rather than special-casing lists.</p><ul class="journey-list"><li><a class="text-link journey-item-title" href="/examples/iterating-over-iterables">Iterating over Iterables</a><p class="meta">separate value producers from value consumers</p></li><li><a class="text-link journey-item-title" href="/examples/iterators">Iterators</a><p class="meta">use `iter()` and `next()` to expose the protocol behind `for`</p></li><li><a class="text-link journey-item-title" href="/examples/generators">Generators</a><p class="meta">write functions that produce values lazily</p></li><li><a class="text-link journey-item-title" href="/examples/generator-expressions">Generator Expressions</a><p class="meta">create lazy one-pass streams with expression syntax</p></li><li><a class="text-link journey-item-title" href="/examples/itertools">Itertools</a><p class="meta">compose iterator streams without materializing every value</p></li></ul></div><figure class="journey-figure"><svg viewBox="0 0 216 116" xmlns="http://www.w3.org/2000/svg"><rect x="8" y="6" width="200" height="100" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="14" y="3" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="8" fill="#6e7888" text-anchor="start" letter-spacing="0.5">B · BUILT-IN</text><rect x="28" y="22" width="160" height="76" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="34" y="19" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="8" fill="#6e7888" text-anchor="start" letter-spacing="0.5">G · GLOBAL</text><rect x="48" y="38" width="120" height="52" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="54" y="35" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="8" fill="#6e7888" text-anchor="start" letter-spacing="0.5">E · ENCLOSING</text><rect x="68" y="54" width="80" height="28" fill="none" stroke="#2b3441" stroke-width="1.0"/><text x="74" y="51" font-family="-apple-system, 'Source Sans Pro', sans-serif" font-size="8" fill="#6e7888" text-anchor="start" letter-spacing="0.5">L · LOCAL</text><circle cx="108" cy="68" r="2.5" fill="#c85a4a"/><line x1="108" y1="78" x2="108" y2="100" stroke="#2b3441" stroke-width="0.6" stroke-dasharray="2 2"/></svg><figcaption>Lookups follow nested scopes; iteration follows nested protocols.</figcaption></figure></section>
41+
</article>
42+
43+
</body>
44+
</html>

0 commit comments

Comments
 (0)