feat(website): refine casts — trim shell, autoplay+loop landing, cap size
Address cast review feedback: - Trim every cast to the in-app region (generate.mjs): the recording now starts with the app already running and ends on the last in-app frame — drops the `$ rdbms-playground` launch and the return-to-shell frame (the latter was the stray cursor-under-$ artifact). Opt out per cast with `keepShell: true` for demos that document the CLI launch. - Landing quickstart cast: autoPlay + loop, with a 2.5s hold on the final frame so it pauses before restarting. - Cap the demo at max-width 46rem and centre it, so the player (fit:'width') no longer scales its font up to the full splash column. Casts re-recorded via `pnpm casts`. Build clean (25 pages). Tab-keypress visibility deferred to an in-app overlay primitive (filed as issue #22 — also serves the planned guided-lesson system); the cast notes Tab in its caption for now.
This commit is contained in:
@@ -45,6 +45,7 @@ export const casts = [
|
||||
width: 90,
|
||||
height: 26,
|
||||
typeSpeed: '45ms',
|
||||
holdEnd: 2.5, // landing cast loops — pause on the final frame before restart
|
||||
steps: [
|
||||
{ wait: 1100 },
|
||||
{ type: 'create table authors with pk', after: 1000 },
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* recording shows a clean `$ rdbms-playground` launch line.
|
||||
*/
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
||||
import { mkdirSync, writeFileSync, readFileSync, rmSync } from 'node:fs';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import os from 'node:os';
|
||||
@@ -67,6 +67,45 @@ function keysFor(steps) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim a recorded cast to just the in-app portion and (optionally) hold the
|
||||
* final frame. The app enters the alternate screen (`?1049h`) right after
|
||||
* launch and leaves it (`?1049l`) on quit, so keeping the events between those
|
||||
* drops the shell prompt, the `$ rdbms-playground` launch line, and the
|
||||
* return-to-shell frame — the cast starts with the app already running and
|
||||
* ends on the last in-app frame (no stray cursor-under-prompt frame).
|
||||
* `holdEnd` re-emits the final frame after a pause so a looping cast lingers
|
||||
* before restarting.
|
||||
*/
|
||||
function trimCast(outPath, { holdEnd = 1.5 } = {}) {
|
||||
const lines = readFileSync(outPath, 'utf8').split('\n').filter(Boolean);
|
||||
const header = JSON.parse(lines[0]);
|
||||
const events = lines.slice(1).map((l) => JSON.parse(l));
|
||||
const isOut = (e) => Array.isArray(e) && e[1] === 'o' && typeof e[2] === 'string';
|
||||
|
||||
const start = events.findIndex((e) => isOut(e) && e[2].includes('?1049h'));
|
||||
let end = -1;
|
||||
for (let i = events.length - 1; i >= 0; i--) {
|
||||
if (isOut(events[i]) && events[i][2].includes('?1049l')) {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const kept = events.slice(start < 0 ? 0 : start, end < 0 ? events.length : end);
|
||||
if (kept.length === 0) return; // nothing matched — leave the cast untouched
|
||||
const t0 = kept[0][0];
|
||||
const rebased = kept.map((e) => [Number((e[0] - t0).toFixed(6)), e[1], e[2]]);
|
||||
if (holdEnd > 0) {
|
||||
const lastT = rebased[rebased.length - 1][0];
|
||||
rebased.push([Number((lastT + holdEnd).toFixed(6)), 'o', '']); // hold final frame
|
||||
}
|
||||
delete header.duration; // let the player recompute from events
|
||||
writeFileSync(
|
||||
outPath,
|
||||
[JSON.stringify(header), ...rebased.map((e) => JSON.stringify(e))].join('\n') + '\n'
|
||||
);
|
||||
}
|
||||
|
||||
function yamlFor(cast) {
|
||||
const keys = keysFor(cast.steps)
|
||||
.map((k) => ` - ${k}`)
|
||||
@@ -113,6 +152,10 @@ for (const cast of selected) {
|
||||
console.error(`✗ ${cast.name} failed (autocast exit ${res.status})`);
|
||||
failures += 1;
|
||||
} else {
|
||||
// Trim the shell launch/quit so the cast starts with the app already
|
||||
// running (the default; opt out with `keepShell: true` for casts that
|
||||
// deliberately document the CLI launch).
|
||||
if (!cast.keepShell) trimCast(outPath, { holdEnd: cast.holdEnd ?? 1.5 });
|
||||
console.log(`✓ ${cast.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user