castwrightv0.3.0

DSL reference

Every key of the castwright DSL.

File extension: *.terminal.yaml. Keywords are English and lowercase. Unknown keys are errors, not warnings — a silently ignored typo in a demo file is discovered at review time, which is too late.

Every example on this page is playing: each player below was compiled from the file under its “Source”, during this site’s build.

A whole file: examples/basic.terminal.yaml

Source: examples/basic.terminal.yaml
version: 1

terminal:
  title: castwright
  cols: 80
  rows: 20

steps:
  - run: npx casoon create my-app
  - output: |
      {green}✓{/} Creating project
      {green}✓{/} Installing dependencies
      {green}✓{/} Project ready
    delay: 250
  - wait: 800
  - run: cd my-app && npm run dev
  - output: "{dim}ready in 412ms{/}\n\n  {cyan}➜{/}  Local:  http://localhost:4321/\n"
  - wait: 2000
$ npx casoon create my-app
✓ Creating project
✓ Installing dependencies
✓ Project ready
$ cd my-app && npm run dev
ready in 412ms

  ➜  Local:  http://localhost:4321/

Document

version: 1        # required, must be 1

terminal:         # required
  title: Casoon CLI
  cols: 90
  rows: 24
  theme: catppuccin-mocha
  prompt: "$ "
  cursor: block

defaults:         # optional
  speed: 45       # ms per character
  pause: 300      # ms after each step

seed: 42          # required only when a step uses jitter

steps:            # required
  - run: npm install

terminal

Key Default Notes
title — Shown in the window chrome, not inside the terminal.
cols 80 Written to the asciicast header.
rows 24 Written to the asciicast header.
theme default One of the seven bundled names; see Theming.
prompt "$ " Supports markup, so a coloured prompt is a one-liner.
cursor block block, bar or underline; a mapping adds blink: false.

defaults

Key Default Notes
speed 45 Milliseconds per character for run and type.
pause 300 Milliseconds after each step.

Steps

Exactly one primary key per step.

Key Value Meaning
run string Write the prompt, type the text, press Enter. Sugar for type + key: enter.
type string Type text at the cursor. No prompt, no Enter.
key see below Send a single key.
output string or { raw } Emit program output. Not typed.
show path Emit a file’s contents, syntax-highlighted. See below.
exec command Run a real command and record its output. See below.
wait ms Pause.
clear true Clear the screen.
prompt string Change the prompt from here on.
marker string Named chapter marker, written to the cast as an asciicast m event.

key accepts enter, tab, escape, backspace, up, down, left, right, ctrl+c, ctrl+d and ctrl+l.

type, key and wait — with seeded jitter

Source: site/src/demos/keys.terminal.yaml
version: 1
terminal: { cols: 56, rows: 7 }
seed: 7
steps:
  - type: npm ru
    jitter: 0.4
  - key: tab
  - wait: 250
  - type: "n build"
  - key: enter
  - output: |
      {green}✓{/} built in 412ms
  - wait: 1200
npm ru  n build
✓ built in 412ms

Modifiers

Key Applies to Meaning
speed run, type Milliseconds per character. 0 is instant.
jitter run, type 0–1, varies the per-character delay. Requires a top-level seed.
delay output, show Milliseconds between output lines. Default 0 — the whole block at once.
pause any Milliseconds after this step.
prompt run, type false suppresses the prompt for that step.

delay: output one line at a time

Source: site/src/demos/output-delay.terminal.yaml
version: 1
terminal: { cols: 56, rows: 9 }
steps:
  - run: npm test
  - output: |
      {green}✓{/} parser
      {green}✓{/} compiler
      {green}✓{/} player
      {dim}3 passed{/}
    delay: 400
  - wait: 1200
$ npm test
✓ parser
✓ compiler
✓ player
3 passed

Ranges

Every numeric key is range-checked at parse time, with the file, line and column of the offending value.

Key Accepts
terminal.cols, terminal.rows Whole numbers from 1 to 1000.
speed, pause, delay, wait Milliseconds from 0 to 3600000 (one hour).
jitter 0 to 1.
seed A whole number from 0 to 4294967295.

.inf and .nan are numbers as far as YAML is concerned and are rejected here.

Note: prompt means two things depending on company. Alongside run or type it is the boolean modifier above. Alone, it is the primary step that changes the prompt from there on. The presence of run/type is what decides.

show

show puts a file on screen with syntax highlighting — the output of a cat, without writing it out by hand in output:.

steps:
  - run: cat astro.config.ts
  - show: snippets/astro.config.ts   # relative to this .terminal.yaml
    lines: 1-7                       # optional: a line or a range, 1-based
    lang: ts                         # optional: default is the file extension
    theme: github-dark               # optional: any Shiki theme

Highlighting happens at build time with Shiki and is written into the cast as 24-bit colour, so nothing extra reaches the browser. The default theme is the Shiki theme matching terminal.theme (default uses dark-plus, default-light uses light-plus).

Shiki is an optional peer dependency: projects that never use show do not install it, and one that does gets a positioned error naming the fix.

pnpm add -D shiki

A missing file, a range past its end or an unknown language is reported with the file, line and column of the step. The Vite plugin registers the shown file as a dependency of the demo, so editing it recompiles the demo in dev.

A compiled cast depends on the Shiki version as well as the file: the project’s lockfile is what keeps it byte-identical from one build to the next.

For code outside a terminal, use your site’s ordinary code blocks — show is for a file that appears as part of a terminal session.

show: a file, highlighted at build time

Source: examples/show.terminal.yaml
version: 1

terminal:
  title: show
  cols: 64
  rows: 12
  theme: catppuccin-mocha

steps:
  - run: cat astro.config.ts
  - show: snippets/astro.config.ts
  - wait: 1500
$ cat astro.config.ts
import castwright from '@casoon/astro-castwright';
import { defineConfig } from 'astro/config';

// Compiles every *.terminal.yaml at build time.
export default defineConfig({
  integrations: [castwright()],
});

exec

exec runs a real command and puts what it printed into the demo, with the timing it actually had. The command is typed at the prompt like run, then executed with /bin/sh -c in a pseudo-terminal the size of the demo, so programs see a real terminal and colour their output.

steps:
  - exec: npm test
    cwd: example-app     # relative to this .terminal.yaml
    env: { CI: '1' }
    timeout: 120000      # ms before the command is killed; default 60000
    idle: 1500           # longest pause kept between two pieces of output

The command is taken literally — no colour markup, since {…} is common in shell commands. speed, prompt and pause work as they do for run.

Nothing runs unless you allow it: castwright build --allow-exec, castwright dev --allow-exec, or castwright({ allowExec: true }) in the Vite plugin. Without it a file containing exec fails to build, pointing at the step. A demo file is not a script anyone should be surprised to find executing.

Record once, replay forever. Real output depends on the machine, the time and the network, so a demo that runs exec on every build is not reproducible. castwright build --allow-exec --record runs the commands once and rewrites the file: each exec becomes a run with the command and an output: { raw } with exactly what it printed. From then on the demo builds without running anything, byte for byte the same. Comments in the file are kept.

exec:, after --record

This was an exec: step. --record ran it once and replaced it with the command and exactly what it printed.

Source: examples/recorded.terminal.yaml
version: 1

terminal:
  title: castwright validate
  cols: 110
  rows: 9
  theme: catppuccin-mocha

steps:
  # Recorded, not written: this was an exec: step, and
  # `castwright build --allow-exec --record` ran it once and replaced it
  # with the command and exactly what it printed.
  - run: castwright validate broken.terminal.yaml
  - output:
      raw: |
        broken.terminal.yaml:5:5: unknown key 'sped' in step 'run' — known keys: run, speed, jitter, pause, prompt

              sped: 30
              ^
    delay: 50
  - wait: 1500
$ castwright validate broken.terminal.yaml
broken.terminal.yaml:5:5: unknown key 'sped' in step 'run' — known keys: run, speed, jitter, pause, prompt

      sped: 30
      ^

Output ends up in a published demo, so the build warns when it contains something that looks like a token or a private key, or your home directory path — check those before publishing. It also warns when a command exits non-zero (the output is recorded anyway), and when exec runs in CI.

exec needs node-pty, an optional peer dependency with a native build:

pnpm add -D node-pty

Caution: node-pty 1.1.0 ships its macOS helper without the executable bit. If exec fails with “posix_spawnp failed”, the error names the file; chmod +x it once.

Colour and style

Real ESC bytes cannot be written comfortably in YAML, so output and prompt take inline markup. {/} closes the most recent tag, {//} resets everything, tags nest, and a literal brace is {{.

- output: |
    {red}red{/} {green}green{/} {yellow}yellow{/} {blue}blue{/}
    {bright-magenta}bright-magenta{/} {bg-blue} on blue {/}
    {#f38ba8}true colour{/} {bold}bold{/} {dim}dim{/} {underline}underline{/}
    {green}nested {bold}bold green{/} still green{/}

Tags: the eight base colours (black, red, green, yellow, blue, magenta, cyan, white) and their bright- variants, bg-<colour> for backgrounds, #rrggbb for true colour, plus bold, dim, italic and underline.

Named colours compile to palette indices, so a demo follows the theme it is played with. #rrggbb is absolute and ignores it.

Markup, compiled

Source: site/src/demos/colours.terminal.yaml
version: 1
terminal: { cols: 56, rows: 9 }
steps:
  - output: |
      {red}red{/} {green}green{/} {yellow}yellow{/} {blue}blue{/}
      {bright-magenta}bright-magenta{/} {cyan}cyan{/}
      {bg-blue} on blue {/} {#f38ba8}#f38ba8{/}
      {bold}bold{/} {dim}dim{/} {italic}italic{/} {underline}underline{/}
      {green}nested {bold}bold green{/} still green{/}
  - wait: 1500
red green yellow blue
bright-magenta cyan
 on blue  #f38ba8
bold dim italic underline
nested bold green still green

Raw ANSI

For pasting genuine program output that already contains escape sequences:

- output:
    raw: "\e[1;36mmy-cli\e[0m \e[2mv2.1.0\e[0m\r\n"

Supports \e, \n, \r, \t, \xNN, \uNNNN and \\. Markup is not processed in raw.

Validation

Enforced by castwright validate and by every code path that parses a file:

  • Exactly one primary key per step. Two is an error, not a precedence question.
  • Unknown keys are errors.
  • jitter > 0 without a top-level seed is an error.
  • Unbalanced or unknown markup tags are errors, with the offending column.
  • An unknown terminal.theme is an error, listing the known names.
  • An output line wider than cols is a warning, not an error.

Every error carries the file, the line, the column and the offending source line:

demo.terminal.yaml:4:13: unknown markup tag '{nope}'

  - output: "{nope}broken{/}"
            ^

An error, and its fix

Real castwright validate output, recorded: the position, the known keys, a caret under the typo.

Source: site/src/demos/error-fix.terminal.yaml
# Scenario: an error, and its fix. Recorded like first-demo: real commands,
# run once with `castwright build --allow-exec --record`.
version: 1
terminal:
  title: ~/my-site
  cols: 116
  rows: 20
  theme: catppuccin-mocha
steps:
  - run: castwright validate demos/deploy.terminal.yaml
  - output:
      raw: |
        demos/deploy.terminal.yaml:5:5: unknown key 'sped' in step 'run' — known keys: run, speed, jitter, pause, prompt

              sped: 30
              ^
    delay: 16
  - wait: 2500
  - run: perl -pi -e 's/sped:/speed:/' demos/deploy.terminal.yaml
  - wait: 400
  - run: castwright validate demos/deploy.terminal.yaml && echo "✓ valid"
  - output:
      raw: |
        ✓ valid
  - wait: 800
  - run: castwright build demos/deploy.terminal.yaml --format svg
  - output:
      raw: |
        dist/deploy.svg
  - wait: 2500
$ castwright validate demos/deploy.terminal.yaml
demos/deploy.terminal.yaml:5:5: unknown key 'sped' in step 'run' — known keys: run, speed, jitter, pause, prompt

      sped: 30
      ^
$ perl -pi -e 's/sped:/speed:/' demos/deploy.terminal.yaml
$ castwright validate demos/deploy.terminal.yaml && echo "✓ valid"
✓ valid
$ castwright build demos/deploy.terminal.yaml --format svg
dist/deploy.svg

Edit this page on GitHub · Docs for v0.3.0