feat(website): asciinema cast pipeline + landing quickstart demo

Settle the cast toolchain (STYLE.md #9) and build the demo pipeline end to
end. Driver: autocast, chosen by spike — its !Interactive feeds keys to the
running TUI and captures the redraw, the right model for a full-screen
crossterm app. asciinema-automation was rejected (assumes shell echo/\n Enter;
produced a garbled cast against the TUI).

- add asciinema-player; Cast.astro (player island) + Demo.astro (the WASM-swap
  seam, ADR-website-001 §3)
- casts-src/: human-readable command-lists (casts.mjs) + generate.mjs, exposed
  as `pnpm casts`; expands steps to autocast YAML and records to public/casts/.
  Command-lists are the durable source; .cast files are regenerable (final
  re-record sweep due once the app is locked).
- quickstart.cast (create -> add columns -> insert -> show data) embedded on
  the landing page above the feature cards.

Verified: pnpm build clean (25 pages); player + cast bundled and served;
landing HTML references the cast. Visual playback check pending (no headless
browser here — verify via dev server over the tunnel).
This commit is contained in:
claude@clouddev1
2026-06-10 12:59:50 +00:00
parent c0cc92a741
commit fbf449f9e0
9 changed files with 658 additions and 8 deletions
+72
View File
@@ -0,0 +1,72 @@
---
/**
* Cast.astro — embeds an asciinema `.cast` recording via asciinema-player
* (ADR-website-001 §2). Client-side island: the player script is bundled and
* initialises every `.asciinema-cast` element on the page from its data
* attributes, so multiple casts on one page work without per-instance scripts.
*
* `.cast` files live in `public/casts/` and are generated from the sources in
* `casts-src/` (run `pnpm casts`). Recordings are regenerable artifacts — the
* source command-lists are the durable thing (re-run, re-record as the app
* evolves).
*/
import 'asciinema-player/dist/bundle/asciinema-player.css';
interface Props {
/** Path to the cast, e.g. `/casts/quickstart.cast`. */
src: string;
autoPlay?: boolean;
loop?: boolean;
/** Skip idle gaps longer than this many seconds (keeps demos snappy). */
idleTimeLimit?: number;
/** Poster frame shown before play, e.g. `npt:0:2` (the frame at 2s). */
poster?: string;
cols?: number;
rows?: number;
theme?: string;
}
const {
src,
autoPlay = false,
loop = false,
idleTimeLimit = 2,
poster = 'npt:0:2',
cols,
rows,
theme = 'asciinema',
} = Astro.props;
// Serialise options for the client script; nulls are dropped there.
const opts = JSON.stringify({
autoPlay,
loop,
idleTimeLimit,
poster,
cols,
rows,
theme,
fit: 'width',
controls: true,
});
---
<div class="asciinema-cast" data-src={src} data-opts={opts}></div>
<script>
import * as AsciinemaPlayer from 'asciinema-player';
function initCasts() {
document.querySelectorAll<HTMLElement>('.asciinema-cast').forEach((el) => {
if (el.dataset.initialised) return;
el.dataset.initialised = '1';
const opts = JSON.parse(el.dataset.opts || '{}');
Object.keys(opts).forEach((k) => opts[k] == null && delete opts[k]);
AsciinemaPlayer.create(el.dataset.src!, el, opts);
});
}
initCasts();
// Re-init after Starlight/Astro view-transition navigations.
document.addEventListener('astro:page-load', initCasts);
</script>
+35
View File
@@ -0,0 +1,35 @@
---
/**
* Demo.astro — the stable demo seam (ADR-website-001 §3). Call sites use this,
* not Cast directly, so a future in-page WASM playground island can replace
* the asciinema player behind the same `{ src, title, autoplay }` contract
* with no change to the pages that embed it.
*/
import Cast from './Cast.astro';
interface Props {
/** Path to the cast, e.g. `/casts/quickstart.cast`. */
src: string;
/** Optional caption shown above the demo. */
title?: string;
autoplay?: boolean;
}
const { src, title, autoplay = false } = Astro.props;
---
<figure class="demo not-content">
{title && <figcaption class="demo-caption">{title}</figcaption>}
<Cast src={src} autoPlay={autoplay} />
</figure>
<style>
.demo {
margin: 1.5rem 0;
}
.demo-caption {
font-size: var(--sl-text-sm);
color: var(--sl-color-gray-3);
margin-block-end: 0.5rem;
}
</style>
+3
View File
@@ -14,6 +14,7 @@ hero:
---
import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
import Demo from '../../components/Demo.astro';
RDBMS Playground is a cross-platform terminal app that gives you a safe
sandbox for exploring relational database concepts: tables, columns,
@@ -21,6 +22,8 @@ primary and foreign keys, relationships, indexes, queries, and query
plans. It is built for learning — from your first table to writing raw
SQL.
<Demo src="/casts/quickstart.cast" title="From a fresh table to your first query, in simple mode." />
<CardGrid>
<Card title="Two ways to work" icon="seti:db">
Start in **simple mode** — a friendly, keyword-based command language —