Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions assets/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,33 @@ pre.code b {
max-width: 68ch;
}

.walk {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 0.75rem;
margin: 1.5rem 0 0;
}

.walk .summary {
margin: 0;
}

[data-step] {
scroll-margin-top: 2rem;
transition: color 0.2s;
}

[data-step].here {
color: var(--accent);
}

@media (prefers-reduced-motion: reduce) {
[data-step] {
transition: none;
}
}

.next {
display: grid;
grid-template-columns: repeat(3, 1fr);
Expand Down
66 changes: 66 additions & 0 deletions assets/walk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// The thirty seconds a recording would have shown, without the recording.
//
// A video of this page would go stale the moment the compiler said anything
// differently, and this page's whole claim is that it prints what the compiler
// prints. So the walkthrough moves through the page that is already here
// rather than a copy of it filmed in the past.
//
// No sound to turn off, and nothing that only works if you can see colour: the
// caption says what to look at and the section is scrolled to.

const BUTTON = document.getElementById("walk");
const CAPTION = document.getElementById("caption");
const STEPS = Array.from(document.querySelectorAll("[data-step]"));

const STILL = matchMedia("(prefers-reduced-motion: reduce)");
const PAUSE = 4500;

let at = -1;
let timer = null;

function show(index) {
at = index;
const step = STEPS[at];
for (const other of STEPS) other.classList.toggle("here", other === step);
CAPTION.textContent = `${at + 1} of ${STEPS.length}. ${step.dataset.step}`;
step.scrollIntoView({
block: "center",
behavior: STILL.matches ? "auto" : "smooth",
});
}

function stop() {
clearTimeout(timer);
timer = null;
BUTTON.textContent = at + 1 < STEPS.length ? "Keep going" : "Start again";
}

function next() {
if (at + 1 >= STEPS.length) {
stop();
return;
}
show(at + 1);
// Someone who wants to read a step should be able to. Auto-advance is off
// entirely when the reader has asked for less movement.
if (!STILL.matches) timer = setTimeout(next, PAUSE);
else stop();
}

BUTTON.addEventListener("click", () => {
if (timer) {
stop();
return;
}
if (at + 1 >= STEPS.length) at = -1;
BUTTON.textContent = "Pause";
next();
});

// Anything that means "I am reading this myself" ends the tour rather than
// fighting it.
for (const event of ["wheel", "touchstart", "keydown"]) {
addEventListener(event, () => {
if (timer) stop();
}, { passive: true });
}
20 changes: 13 additions & 7 deletions one-clause/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ <h1>One clause, two worlds.</h1>
wrong, and why.
-->

<h2>Two functions</h2>
<div class="walk">
<button id="walk" type="button">Walk me through it</button>
<span class="summary" id="caption" aria-live="polite"></span>
</div>

<h2 data-step="Two functions. One line apart: the second admits to saving as well as reading.">Two functions</h2>
<p>
Identical, except for one line. They are
<code>demo/read_only.deed</code> and
Expand Down Expand Up @@ -82,7 +87,7 @@ <h3>read_write</h3>
</div>
</section>

<h2>Two worlds</h2>
<h2 data-step="Compiled, that one line is one more entry in the import section. Nobody wrote it.">Two worlds</h2>
<p>
Compiled to WebAssembly components, the import section of each module
<em>is</em> its world. It lists what the module asks a host for, and
Expand All @@ -108,7 +113,7 @@ <h3>read_write.wasm imports</h3>
code is the manifest.
</p>

<h2>What the host does with it</h2>
<h2 data-step="A host without `Io.save` refuses the second module, before any of it runs.">What the host does with it</h2>
<p>
A host that offers <code>Io.read</code> and not <code>Io.save</code>
runs the first module and refuses the second. The refusal is not a
Expand All @@ -134,7 +139,7 @@ <h2>What the host does with it</h2>
<code>process</code> executes.
</p>

<h2>None of this is checked by eye</h2>
<h2 data-step="All of it is pinned by tests that run on every commit, including the listing above.">None of this is checked by eye</h2>
<p>
The import sections, the host's refusal and the listing printed
further up this page are all pinned in the compiler's repository, and
Expand All @@ -156,7 +161,7 @@ <h2>None of this is checked by eye</h2>
import section with nothing to notice the prose no longer matching.
</p>

<h2>Try them</h2>
<h2 data-step="Now take the first one and add `Io.save` to it, and watch the compiler answer.">Try them</h2>
<p>
Both programs open in the playground, running the same compiler this
page is describing. Add <code>Io.save</code> to the first one's
Expand All @@ -166,14 +171,14 @@ <h2>Try them</h2>
<nav class="next" aria-label="Open in the playground">
<a
class="card"
href="../play/#0.2.1/zTZFBi9wwDIXv_hXv1i5Mk3soC0t7mcsW2oUei-MoY4MjBUveNJT-9-JktuzRsvS9p6e-xzcmhOyrEhLDQ9ONvdVCSAqLhCnNMxXiQBjJNiKGbYIgyypMbNq5vnd9j5eYFHPlYEkYhfyk8JhTpg4vkd6R1e8K-u2D5R1b9IZkCJ5hUkMcGqwpt1Hd1Wi5YJZyIBPfOjyLxcQ3UFbq8EWWNWWaYIKfND6p0jLm_dIYDbXIVDN9UKRllWJQOh0GYfOJFcIEWan4ozwXWQ75KGrDfTdgIpqGJDhc3KtPRw8S60rBWlzekJMaRpqlEEplbkZbrHzLLWK1Ug_9DlfDRCFNpA22RbJIpW2hdV3zDvIh3k13b1qHxFrktY3hKl2zg7EaWKxhrtKpf6UjzlIZ1q7y_1jYkkWphkJqJZ1GnDsjwkSL9A34Szjvzs3cpAKpfjxuMeBrKhewX2jADyuJbw_49IjvpDXb57Nyuf88OqAqqQPwZvTi_rx_ntQT-OD-un8"
href="../play/#0.2.2/zTZFBi9wwDIXv_hXv1i5Mk3soC0t7mcsW2oUei-MoY4MjBUveNJT-9-JktuzRsvS9p6e-xzcmhOyrEhLDQ9ONvdVCSAqLhCnNMxXiQBjJNiKGbYIgyypMbNq5vnd9j5eYFHPlYEkYhfyk8JhTpg4vkd6R1e8K-u2D5R1b9IZkCJ5hUkMcGqwpt1Hd1Wi5YJZyIBPfOjyLxcQ3UFbq8EWWNWWaYIKfND6p0jLm_dIYDbXIVDN9UKRllWJQOh0GYfOJFcIEWan4ozwXWQ75KGrDfTdgIpqGJDhc3KtPRw8S60rBWlzekJMaRpqlEEplbkZbrHzLLWK1Ug_9DlfDRCFNpA22RbJIpW2hdV3zDvIh3k13b1qHxFrktY3hKl2zg7EaWKxhrtKpf6UjzlIZ1q7y_1jYkkWphkJqJZ1GnDsjwkSL9A34Szjvzs3cpAKpfjxuMeBrKhewX2jADyuJbw_49IjvpDXb57Nyuf88OqAqqQPwZvTi_rx_ntQT-OD-un8"
>
<strong>read_only.deed</strong>
<span>The one a host without `Io.save` will run.</span>
</a>
<a
class="card"
href="../play/#0.2.1/zhVFBjtswDLz7FXNrArj2PWgDLNpLTwXaBXpcKBIdC2uThkhvNijy90Jy7F20h-okaciZ4bBt8Z0JfnCzEiLDQeOZnc2JEBXWE0LsOkrEnnAiuxAx7CLwMk7CxKZN1bZV2-Kxj4puZm9RGIlcUDh0cSA4DrikaKSIhpPzz00RpldL7p289ZSZNg8HfJNG3Qs1-CLjFAcKMMEvOj2o0ngarnXxOEqYB_qgiOMkyaBUTGQulgu8sLnIWozLRMllVNElGUt_L2qH-xhAIAqHKCgz_PWVzdwLH0obrHeGKclLDKQQHq7ZdG6FEi0Z5q7VW84iUTdrwaJmqi1MnKiTREgzc-Rz2QefhxyOWprLVA0et5ERyA8uUYADExW360DwjlkMI5HVRXZDluUU51sG_0Z39-L4Ci-hmNKmqjblUdpM9FQ2W1Ud5xg8qe7y0vWArzHVYDfSAT8tRT7v8fGIH6TzYJ-Wn_qOHCsgR1IBWPOr10eOr65-l-fozPdrxSK0aOyxFORDKe0u_XWPz8ftXm-oPO-MXq2gG13WeE9XY6l5I_0_8Z38qcCryht8W6636lb9AQ"
href="../play/#0.2.2/zhVFBjtswDLz7FXNrArj2PWgDLNpLTwXaBXpcKBIdC2uThkhvNijy90Jy7F20h-okaciZ4bBt8Z0JfnCzEiLDQeOZnc2JEBXWE0LsOkrEnnAiuxAx7CLwMk7CxKZN1bZV2-Kxj4puZm9RGIlcUDh0cSA4DrikaKSIhpPzz00RpldL7p289ZSZNg8HfJNG3Qs1-CLjFAcKMMEvOj2o0ngarnXxOEqYB_qgiOMkyaBUTGQulgu8sLnIWozLRMllVNElGUt_L2qH-xhAIAqHKCgz_PWVzdwLH0obrHeGKclLDKQQHq7ZdG6FEi0Z5q7VW84iUTdrwaJmqi1MnKiTREgzc-Rz2QefhxyOWprLVA0et5ERyA8uUYADExW360DwjlkMI5HVRXZDluUU51sG_0Z39-L4Ci-hmNKmqjblUdpM9FQ2W1Ud5xg8qe7y0vWArzHVYDfSAT8tRT7v8fGIH6TzYJ-Wn_qOHCsgR1IBWPOr10eOr65-l-fozPdrxSK0aOyxFORDKe0u_XWPz8ftXm-oPO-MXq2gG13WeE9XY6l5I_0_8Z38qcCryht8W6636lb9AQ"
>
<strong>read_write.deed</strong>
<span>The one it will not.</span>
Expand All @@ -188,5 +193,6 @@ <h2>Try them</h2>
</p>
</footer>
</div>
<script src="../assets/walk.js"></script>
</body>
</html>
12 changes: 12 additions & 0 deletions tools/check.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,18 @@ if (index.tag !== tag) {
complain("examples/index.json", `says ${index.tag} and the page pins ${tag}`);
}

// A share link carries the version it was written against, and the playground
// says so when it disagrees. A link written into a page here should not be the
// thing triggering that.
for (const path of files.filter((f) => f.endsWith(".html"))) {
const html = await readFile(path, "utf8");
for (const [, version] of html.matchAll(/play\/#(\d+\.\d+\.\d+)\//g)) {
if (`v${version}` !== tag) {
complain(relative(root, path), `links a program written against ${version}, and the pin is ${tag}`);
}
}
}

if (problems.length) {
for (const problem of problems) console.error(problem);
console.error(`\n${problems.length} problem${problems.length === 1 ? "" : "s"}.`);
Expand Down