SSkilltecabyclaudinhocode
Enviar skill
← Voltar para o catálogo

ndemo

Pesquisa e Web

A claude skill to create a narrated demo video of a web application

5estrelas
Ver no GitHub ↗Autor: splitbrain

ndemo

Create narrated screen-recording demo videos of web applications.

Setup

On first use, you MUST build the toolkit before running any command. Check if ${CLAUDE_SKILL_DIR}/dist/cli.js exists. If not, run:

cd ${CLAUDE_SKILL_DIR} && npm install && npm run build

Then install the Playwright browser if needed:

cd ${CLAUDE_SKILL_DIR} && npx playwright install chromium

Verify the setup:

${CLAUDE_SKILL_DIR}/ndemo doctor

Commands

All commands are run via ${CLAUDE_SKILL_DIR}/ndemo:

CommandWhat it does
${CLAUDE_SKILL_DIR}/ndemo open <playbook>Launch browser daemon, navigate to app
${CLAUDE_SKILL_DIR}/ndemo closeShut down browser daemon
${CLAUDE_SKILL_DIR}/ndemo resetNavigate back to app URL (fresh state)
${CLAUDE_SKILL_DIR}/ndemo page-statePrint current page accessibility tree
${CLAUDE_SKILL_DIR}/ndemo page-state --screenshotSame + save screenshot to .ndemo/screenshot.png
${CLAUDE_SKILL_DIR}/ndemo play <playbook>Play all segments
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --segment <id>Play one segment (rewinds first)
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --from <id>Play from segment to end
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --to <id>Stop after this segment
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --audioPlay with TTS narration audio
${CLAUDE_SKILL_DIR}/ndemo render <playbook>Full pipeline: TTS → replay → merge → mp4
${CLAUDE_SKILL_DIR}/ndemo render <playbook> --output <path>Render to a specific output file
${CLAUDE_SKILL_DIR}/ndemo doctorCheck dependencies

Workflow

Step 1 — Create the playbook

Each playbook lives in its own directory under demo/ in the user's project. The directory name matches the playbook name.

demo/
  my-feature/
    my-feature.yaml    ← playbook
    fixtures/          ← files to restore during setup
    audio/             ← generated TTS files (auto)
    video-raw/         ← raw recording (auto)
    demo.mp4           ← final output (auto)

Before writing the playbook, think about what state the app needs to be in for the demo to work reliably and repeatably:

  • Does the user need to be logged in? → add conditional login steps
  • Does the demo modify files/data that need restoring? → copy originals into the playbook's fixtures/ directory so setup can restore them
  • Does the demo depend on specific content existing? → create it in fixtures or via setup shell commands

Copy any files that will be modified during the demo into the fixtures/ subdirectory of the playbook directory. Setup steps will copy them back before each run so the demo always starts from a clean state.

Create the directory and YAML file. The full playbook schema supports these top-level sections:

# demo/edit-page/edit-page.yaml
app:
  url: http://localhost:8080/wiki
  viewport:                          # optional, default 1920x1080
    width: 1920
    height: 1080
  scale: 2                           # device scale factor (default 2)
  zoom: 1.25                         # CSS zoom level (default 1.25)
  colorScheme: light                 # "light" or "dark" (default "light")
  setup:
    # Restore files modified during the demo
    - run: cp demo/edit-page/fixtures/page.txt data/pages/page.txt
    # Clean up artifacts from previous runs
    - run: rm -f data/cache/*.tmp
    # Login if needed (conditional — skipped if already logged in)
    - type: click
      target: { role: link, name: "Login" }
      if:
        hidden: ".user-info"
    - type: type
      target: { role: textbox, name: "Username" }
      text: admin
      if:
        visible: ".login-form"
    - type: type
      target: { role: textbox, name: "Password" }
      text: password
      if:
        visible: ".login-form"
    - type: click
      target: { role: button, name: "Sign in" }
      if:
        visible: ".login-form"
      done:
        visible: ".user-info"

titleCard:                               # optional title card (shown as first frame)
  title: "Editing a Wiki Page"           # displayed prominently
  subtitle: "A quick tour of the editor" # optional subtitle
  duration: 3000                         # milliseconds to hold (default 3000)

tts:                                   # optional TTS configuration
  provider: openai                     # "openai" (default) or "elevenlabs"
  voice: alloy                         # TTS voice name (default "alloy")
  speed: 1.0                           # speech speed multiplier (default 1.0)

recording:                             # optional recording settings
  outputDir: "."                       # output directory relative to playbook (default ".")
  fps: 30                              # video frame rate (default 30)

segments:
  - id: intro
    narration: "Welcome to our wiki. Let's edit a page."
    intent: "show the wiki start page"
    timing: after                      # "after" (default) or "parallel"
    actions:
      - type: wait
        duration: 2000

  - id: open-editor
    narration: "Click the edit button to open the editor."
    intent: "click the edit button"
    actions: []

Segment timing controls when actions run relative to narration:

  • after (default) — narration plays first, then actions execute
  • parallel — actions execute while narration plays

Write all segments with narration and intent first. Leave actions as empty arrays. Use absolute paths when passing playbook paths to ndemo commands.

Step 2 — Open the browser

${CLAUDE_SKILL_DIR}/ndemo open /absolute/path/to/demo/my-demo/my-demo.yaml

Step 3 — Author each segment

For each segment with empty actions:

a) Read the current page state:

${CLAUDE_SKILL_DIR}/ndemo page-state

b) Look at the accessibility tree output. Find the elements referenced in the segment's intent. Write actions into the playbook YAML using elements from the tree.

How to write targets — use info from page-state output:

If page-state shows [button "Settings"]:

target: { role: button, name: "Settings" }

If page-state shows [searchbox "Search reports" value=""]:

target: { role: searchbox, name: "Search reports" }

Other target fields — use whichever best identifies the element:

target: { selector: "#my-element" }        # CSS selector
target: { testId: "submit-btn" }           # data-testid attribute
target: { label: "Email" }                 # aria-label
target: { placeholder: "Search..." }       # placeholder text
target: { text: "Click me" }              # visible text content

At least one target field is required. Multiple fields can be combined to narrow the match.

Tip: The web app's source code is available in the project repo. Look at the component source for data-testid attributes, class names, or IDs when the accessibility tree isn't sufficient.

How to write done conditions:

done:
  visible: ".settings-panel"        # element appears
  hidden: ".loading-spinner"        # element disappears
  networkIdle: true                 # no pending requests
  stable: 500                       # DOM unchanged for 500ms
  url: "**/settings"                # URL changed
  text:                             # element contains text
    selector: ".results"
    has: "Q3 Revenue"
  attribute:                        # element has attribute
    selector: html
    name: data-theme
    value: dark
  timeout: 10000                    # override default timeout (ms)

Every action that changes the page MUST have a done condition. Without one, the next action may execute before the page is ready.

Add wait actions after visible changes so the viewer can see what happened:

- type: wait
  duration: 2000    # 2 seconds

c) Test the segment:

${CLAUDE_SKILL_DIR}/ndemo play /absolute/path/to/demo/my-demo/my-demo.yaml --se

Como adicionar

/plugin marketplace add splitbrain/ndemo

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Comentários · Nenhum comentário

Entre para comentar. Entrar

  • Ainda não há comentários. Seja o primeiro.