diff options
| author | user <user@clank> | 2026-08-30 16:28:57 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-30 16:28:57 +0200 |
| commit | a5ce3d56f9ba17acf073b9ca051e658bfd262cae (patch) | |
| tree | 5f005f89425b1be572019a066053256a6185274c | |
| parent | web: the logo cycles hyper, and casts a glow to match (diff) | |
| download | slosh-a5ce3d56f9ba17acf073b9ca051e658bfd262cae.tar.gz slosh-a5ce3d56f9ba17acf073b9ca051e658bfd262cae.tar.xz slosh-a5ce3d56f9ba17acf073b9ca051e658bfd262cae.zip | |
web: the demo machine, bootable in the front page
A try section between the tour and the install box: a modest poster
with one button, and the machine only when the button is pushed -- the
front page keeps costing two files, which is the decision the README
recorded when the demo got its own page, still being honoured.
The click grows the stage to most of a viewport *before* the iframe
goes in (the guest reads its geometry from the room exactly once, at
boot), then glides the scroll to land the machine centred. An iframe,
not the demo's scripts inlined: the emulator's emscripten glue owns
its globals by name, and isolation is what keeps this page and that
blob out of each other's namespaces. ?embed=1 makes the demo page
drop its own header and footer inside the frame; standalone it is
unchanged, and phones navigate to it rather than embedding -- wasm
under a soft keyboard in an iframe helps nobody.
The floating logo learns the tour's edges properly while at it: the
try section arriving closes the show (measured with a -45% rootMargin,
because the tour's exit margin lets the section ride high while the
last step is still centred), and a section scrolled past counts as
arrived, not gone.
| -rw-r--r-- | web/demo/web/demo.css | 10 | ||||
| -rw-r--r-- | web/demo/web/demo.js | 6 | ||||
| -rw-r--r-- | web/index.html | 72 | ||||
| -rw-r--r-- | web/site.css | 49 |
4 files changed, 134 insertions, 3 deletions
diff --git a/web/demo/web/demo.css b/web/demo/web/demo.css index 3b7f608..f2c02c7 100644 --- a/web/demo/web/demo.css +++ b/web/demo/web/demo.css @@ -182,3 +182,13 @@ footer p { margin: 0; max-width: 52rem; } #keys dl { grid-template-columns: 1fr; gap: 0 0; } #keys dt { text-align: left; margin-top: 0.5rem; } } + +/* ---- embedded (?embed=1) --------------------------------------------- */ +/* Inside the front page's iframe the stage is the whole story: no header + to introduce it, no footer to credit it (the front page does both), and + the stage plus the status bar fill the frame exactly. */ + +body.embed { padding: 0.6rem 0.6rem 0.4rem; max-width: none; } +body.embed header, +body.embed footer { display: none; } +body.embed #stage { height: calc(100vh - 3.4rem); min-height: 240px; } diff --git a/web/demo/web/demo.js b/web/demo/web/demo.js index ad8b20a..cc1df44 100644 --- a/web/demo/web/demo.js +++ b/web/demo/web/demo.js @@ -84,6 +84,12 @@ function update_downloading(flag) { var mem_size = (params["mem"] | 0) || 128; var cmdline = params["cmdline"] || ""; + /* ?embed=1: the same page inside the front page's iframe. The chrome + around the stage belongs to the standalone page; embedded, the stage + is the whole story (demo.css hides the rest and fills the frame). */ + if (params["embed"]) + document.body.classList.add("embed"); + var stage = document.getElementById("stage"); var wrap = document.getElementById("term_wrap"); var status_el = document.getElementById("status"); diff --git a/web/index.html b/web/index.html index db43867..c967655 100644 --- a/web/index.html +++ b/web/index.html @@ -42,7 +42,7 @@ <p class="hero-links"> <a class="btn" href="#install">install</a> - <a class="btn ghost" href="demo/">try it in the browser</a> + <a class="btn ghost" href="#try">try it in the browser</a> <a class="btn ghost" href="docs/">browse docs</a> </p> </header> @@ -168,6 +168,28 @@ <!-- ================================================================ --> +<section id="try"> + <h2>try it</h2> + + <p>A riscv64 Linux with slosh as the thing you land in, booted right here. + Nothing is installed, nothing leaves the tab, and the machine is throwaway + — reload for a fresh one. About 11 MiB streams in while it boots. + <a href="demo/">Prefer a whole tab?</a></p> + + <!-- The facade: this section costs a poster until the button is pushed. + The click swaps in an iframe on demo/ -- an iframe because the + emulator's emscripten glue owns its globals by name, and isolation is + what keeps this page's scripts and that blob out of each other's + namespaces. Phones navigate to the page instead (the script at the + bottom): wasm under a soft keyboard in an iframe helps nobody. --> + <div class="try-stage" id="try_stage"> + <button class="boot" type="button" id="boot_btn">boot the machine</button> + <p class="aside">riscv64 · TinyEMU, compiled to wasm · yours until you reload</p> + </div> +</section> + +<!-- ================================================================ --> + <section id="install"> <h2>install</h2> @@ -288,13 +310,33 @@ for (const box of document.querySelectorAll(".cmd[data-copy]")) { }); } -// The little logo in the corner: on while the big one is off screen. +// The little logo in the corner: on while the tour is what you are looking +// at. The big logo scrolling away opens the show; the try section arriving +// closes it -- the dead space the little one floats in is the tour's, and +// over the demo box or the install tabs it is just in the way. The tour +// itself is the wrong closing signal: its negative bottom margin leaves its +// border box under the sections that follow. The rootMargin keeps the last +// tour step from tripping it (the try section already rides high in the viewport there, pulled up by the tour's exit margin), +// and a section already scrolled past counts as arrived, not as gone. const bigLogo = document.querySelector(".hero .logo"); const littleLogo = document.querySelector(".logo-float"); +const tryEl = document.getElementById("try"); if (bigLogo && littleLogo && "IntersectionObserver" in window) { + const seen = { heroGone: false, pastTour: false }; + const apply = () => + littleLogo.classList.toggle("show", seen.heroGone && !seen.pastTour); new IntersectionObserver(([e]) => { - littleLogo.classList.toggle("show", !e.isIntersecting); + seen.heroGone = !e.isIntersecting; + apply(); }).observe(bigLogo); + if (tryEl) + new IntersectionObserver( + ([e]) => { + seen.pastTour = e.isIntersecting || e.boundingClientRect.top < 0; + apply(); + }, + { rootMargin: "0px 0px -45% 0px" } + ).observe(tryEl); } // ...and vertically centred in the air between the viewport top and the @@ -321,6 +363,30 @@ if (stagePin && littleLogo) { for (const el of stagePin.querySelectorAll("[data-sloshcast]")) ro.observe(el); } +// The demo, booted on request: the iframe exists only after the click, so +// the front page keeps costing two files until someone asks for a machine. +// Small screens go to the page itself instead of embedding it. +const bootBtn = document.getElementById("boot_btn"); +if (bootBtn) + bootBtn.addEventListener("click", () => { + if (matchMedia("(max-width: 54rem)").matches) { + location.href = "demo/"; + return; + } + const stage = document.getElementById("try_stage"); + stage.classList.add("booted"); // full height first: the guest reads the + const frame = document.createElement("iframe"); // room once, at boot + frame.src = "demo/?embed=1"; + frame.className = "try-frame"; + frame.title = "slosh, running in a riscv64 machine in this page"; + stage.replaceChildren(frame); + // The stage just grew from a strip to most of a viewport, which shoves + // the layout around under the reader; follow it, and land the machine + // centred. Smooth for free -- the page scrolls smooth globally. + stage.scrollIntoView({ block: "center" }); + frame.addEventListener("load", () => frame.contentWindow.focus()); + }); + // The install box opens on the platform reading it; the tabs are CSS. const ua = navigator.userAgent; const os = /Windows/i.test(ua) ? "windows" : /Mac/i.test(ua) ? "macos" : "linux"; diff --git a/web/site.css b/web/site.css index bf196af..5dd2162 100644 --- a/web/site.css +++ b/web/site.css @@ -207,6 +207,55 @@ section > p:last-child { margin-bottom: 0; } .aside { color: var(--dim); font-size: 0.95rem; } +/* ---- the embedded demo ------------------------------------------------ */ +/* A poster until the button is pushed, then the machine, same box. The + * frame is the section's whole width and most of a viewport tall: the demo + * page sizes its grid to the space it wakes up in, once, at boot. */ + +.try-stage { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.7rem; + /* The tour's breakout: the machine deserves the same width the pinned + * terminal got, not the prose column. */ + width: var(--tour-w); + margin-left: calc((100% - var(--tour-w)) / 2); + /* A poster until booted: the machine gets most of a viewport, the offer + * of one gets a modest strip. The height moves before the iframe goes in, + * so the guest reads its one-time geometry from the final room. */ + padding: 3.5rem 1rem; + border: 1px solid var(--code-bg); + border-radius: 6px; + background: var(--panel); +} +.try-stage.booted { + padding: 0; + height: 74vh; + min-height: 360px; +} + +.boot { + padding: 0.6rem 1.6rem; + border: 0; + background: var(--pink); + color: var(--panel); + font-family: var(--mono); + font-size: 15px; + cursor: pointer; +} +.boot:hover { background: #fff; } + +.try-frame { + display: block; + width: 100%; + height: 100%; + border: 0; + border-radius: 5px; /* inside the stage's own border */ + background: var(--bg); +} + /* ---- the install box ------------------------------------------------- */ /* Pick a platform, read only its instructions. Hidden radios, labels drawn * like a slosh tab strip, one panel each; the switching is CSS. */ |
