fbf449f9e0
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).
73 lines
2.0 KiB
Plaintext
73 lines
2.0 KiB
Plaintext
---
|
|
/**
|
|
* 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>
|