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:
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 —
|
||||
|
||||
Reference in New Issue
Block a user