ndemo は何をしますか?
Webアプリケーションのナレーション付き画面録画デモ動画を作成します。
セットアップ
初回使用時は、コマンドを実行する前に必ずツールキットをビルドしてください。
${CLAUDE_SKILL_DIR}/dist/cli.jsが存在するか確認してください。存在しない場合は以下を実行します:
cd ${CLAUDE_SKILL_DIR} && npm install && npm run build
次に、必要に応じてPlaywrightブラウザをインストールします:
cd ${CLAUDE_SKILL_DIR} && npx playwright install chromium
セットアップを確認します:
${CLAUDE_SKILL_DIR}/ndemo doctor
コマンド
すべてのコマンドは${CLAUDE_SKILL_DIR}/ndemo経由で実行します:
| コマンド | 説明 |
|---|---|
${CLAUDE_SKILL_DIR}/ndemo open <playbook> | ブラウザデーモンを起動し、アプリに移動 |
${CLAUDE_SKILL_DIR}/ndemo close | ブラウザデーモンをシャットダウン |
${CLAUDE_SKILL_DIR}/ndemo reset | アプリのURLに戻る(初期状態) |
${CLAUDE_SKILL_DIR}/ndemo page-state | 現在のページのアクセシビリティツリーを表示 |
${CLAUDE_SKILL_DIR}/ndemo page-state --screenshot | 同じ + スクリーンショットを.ndemo/screenshot.pngに保存 |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> | すべてのセグメントを再生 |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --segment <id> | 1つのセグメントを再生(最初に巻き戻す) |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --from <id> | セグメントから最後まで再生 |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --to <id> | このセグメントで停止 |
${CLAUDE_SKILL_DIR}/ndemo play <playbook> --audio | TTSナレーション音声付きで再生 |
${CLAUDE_SKILL_DIR}/ndemo render <playbook> | 完全パイプライン: TTS → リプレイ → 合成 → mp4 |
${CLAUDE_SKILL_DIR}/ndemo render <playbook> --output <path> | 特定の出力ファイルにレンダリング |
${CLAUDE_SKILL_DIR}/ndemo doctor | 依存関係をチェック |
ワークフロー
ステップ1 — プレイブックの作成
各プレイブックは、ユーザーのプロジェクト内のdemo/ディレクトリ以下に専用のディレクトリを作成して配置します。
ディレクトリ名はプレイブック名と一致します。
demo/
my-feature/
my-feature.yaml ← プレイブック
fixtures/ ← セットアップで復元するファイル
audio/ ← 生成されたTTSファイル(自動)
video-raw/ ← 生録画(自動)
demo.mp4 ← 最終出力(自動)
プレイブックを作成する前に、デモが確実かつ再現性よく動作するためにアプリがどのような状態である必要があるかを検討してください:
- ユーザーはログインする必要がありますか? → 条件付きログインステップを追加
- デモでファイル/データが変更されますか?復元が必要ですか? → オリジナルをプレイブックの
fixtures/ディレクトリにコピーし、セットアップで復元できるようにします - デモは特定のコンテンツの存在に依存していますか? → フィクスチャで作成するか、セットアップのシェルコマンドで作成します
デモ中に変更されるファイルを、プレイブックディレクトリのfixtures/サブディレクトリにコピーします。
セットアップステップは、各実行前にそれらをコピーして戻すため、デモは常にクリーンな状態から開始されます。
ディレクトリとYAMLファイルを作成します。完全なプレイブックスキーマは以下のトップレベルセクションをサポートします:
# demo/edit-page/edit-page.yaml
app:
url: http://localhost:8080/wiki
viewport: # オプション、デフォルト 1920x1080
width: 1920
height: 1080
scale: 2 # デバイススケールファクター(デフォルト 2)
zoom: 1.25 # CSSズームレベル(デフォルト 1.25)
colorScheme: light # "light" または "dark"(デフォルト "light")
setup:
# デモ中に変更されたファイルを復元
- run: cp demo/edit-page/fixtures/page.txt data/pages/page.txt
# 以前の実行のアーティファクトをクリーンアップ
- run: rm -f data/cache/*.tmp
# 必要に応じてログイン(条件付き — すでにログインしている場合はスキップ)
- 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: # オプションのタイトルカード(最初のフレームとして表示)
title: "Editing a Wiki Page" # 目立つように表示
subtitle: "A quick tour of the editor" # オプションのサブタイトル
duration: 3000 # 表示時間(ミリ秒)(デフォルト 3000)
tts: # オプションのTTS設定
provider: openai # "openai"(デフォルト)または "elevenlabs"
voice: alloy # TTS音声名(デフォルト "alloy")
speed: 1.0 # 音声速度倍率(デフォルト 1.0)
recording: # オプションの録画設定
outputDir: "." # プレイブックからの相対出力ディレクトリ(デフォルト ".")
fps: 30 # ビデオフレームレート(デフォルト 30)
segments:
- id: intro
narration: "Welcome to our wiki. Let's edit a page."
intent: "show the wiki start page"
timing: after # "after"(デフォルト)または "parallel"
actions:
- type: wait
duration: 2000
- id: open-editor
narration: "Click the edit button to open the editor."
intent: "click the edit button"
actions: []
セグメントタイミングは、アクションがナレーションに対して実行されるタイミングを制御します:
after(デフォルト) — ナレーションが最初に再生され、次にアクションが実行されますparallel— ナレーションの再生中にアクションが実行されます
まず、すべてのセグメントをナレーションと意図(intent)で記述します。アクションは空の配列のままにします。 プレイブックのパスをndemoコマンドに渡すときは、絶対パスを使用します。
ステップ2 — ブラウザを開く
${CLAUDE_SKILL_DIR}/ndemo open /absolute/path/to/demo/my-demo/my-demo.yaml
ステップ3 — 各セグメントを作成する
アクションが空の各セグメントについて:
a) 現在のページ状態を読み取ります:
${CLAUDE_SKILL_DIR}/ndemo page-state
b) アクセシビリティツリーの出力を確認します。セグメントの意図で参照されている要素を見つけます。 ツリーから取得した要素を使用して、プレイブックYAMLにアクションを記述します。
ターゲットの記述方法 — page-stateの出力からの情報を使用します:
page-stateに[button "Settings"]と表示される場合:
target: { role: button, name: "Settings" }
page-stateに[searchbox "Search reports" value=""]と表示される場合:
target: { role: searchbox, name: "Search reports" }
その他のターゲットフィールド — 要素を最も適切に識別できるものを使用します:
target: { selector: "#my-element" } # CSSセレクター
target: { testId: "submit-btn" } # data-testid属性
target: { label: "Email" } # aria-label
target: { placeholder: "Search..." } # プレースホルダーテキスト
target: { text: "Click me" } # 表示テキストコンテンツ
少なくとも1つのターゲットフィールドが必要です。複数のフィールドを組み合わせてマッチを絞り込むことができます。
ヒント: Webアプリケーションのソースコードはプロジェクトリポジトリで入手可能です。
アクセシビリティツリーだけでは不十分な場合は、data-testid属性、クラス名、IDのためにコンポーネントのソースを確認してください。
done条件の記述方法:
done:
visible: ".settings-panel" # 要素が表示される
hidden: ".loading-spinner" # 要素が非表示になる
networkIdle: true # 保留中のリクエストがない
stable: 500 # 500ms間DOMが変更されない
url: "**/settings" # URLが変更された
text: # 要素にテキストが含まれる
selector: ".results"
has: "Q3 Revenue"
attribute: # 要素に属性がある
selector: html
name: data-theme
value: dark
timeout: 10000 # デフォルトタイムアウトをオーバーライド(ミリ秒)
ページを変更するすべてのアクションには、必ずdone条件が必要です。 ない場合、ページの準備ができる前に次のアクションが実行される可能性があります。
表示上の変更の後にはwaitアクションを追加して、視聴者が何が起こったかを確認できるようにします:
- type: wait
duration: 2000 # 2秒
c) セグメントをテストします:
${CLAUDE_SKILL_DIR}/ndemo play /absolute/path/to/demo/my-demo/my-demo.yaml --segment <id>
d) 失敗した場合、${CLAUDE_SKILL_DIR}/ndemo page-stateを実行して現在画面に何が表示されているかを確認し、アクションを調整して再試行します。
e) セグメントが機能した後、次のセグメントを作成する前に再度page-stateを読み取ります — ページが変更されています。
ステップ4 — レビュー
${CLAUDE_SKILL_DIR}/ndemo play /absolute/path/to/demo/my-demo/my-demo.yaml
ブラウザで全シーケンスを確認します。ユーザーに問題ないか尋ねます。
TTSナレーション付きでレビューするには(OPENAI_API_KEYとffplayが必要):
${CLAUDE_SKILL_DIR}/ndemo play /absolute/path/to/demo/my-demo/my-demo.yaml --audio
これにより、各セグメントのTTS音声が生成され(コンテンツハッシュによってキャッシュ)、ブラウザアクションとともに音声が再生され、アクションがナレーションの長さに一致するようにタイミングが調整されます。ナレーションテキストが変更されると、音声は自動的に再生成され、古いファイルは削除されます。
ステップ5 — 反復
ユーザーが変更を要求する場合があります。一般的なパターン:
| ユーザーの発言 | 対応 |
|---|---|
| "間違ったボタン" | page-stateを実行し、正しい要素を見つけてターゲットを更新 |
| "速すぎる"/"遅すぎる" | 待機時間を調整し、--audioでリプレイしてタイミングを確認 |
| "ナレーションを...に変更" | ナレーションフィールドを編集 |
| "Xを示すステップを追加" | 新しいセグメントを挿入し、アクションを作成 |
| "そのステップを削除" | YAMLからセグメントを削除 |
| "セグメントXからリプレイ" | ${CLAUDE_SKILL_DIR}/ndemo play --from <id> |
| "最初からやり直し" | ${CLAUDE_SKILL_DIR}/ndemo reset してから再生 |
各変更後、影響を受けるセグメントをリプレイして確認します。
ステップ6 — レンダリング
ユーザーが承認したら:
${CLAUDE_SKILL_DIR}/ndemo render /absolute/path/to/demo/my-demo/my-demo.yaml
これにより、TTSナレーション付きの最終MP4が生成されます。
セットアップステップリファレンス
セットアップステップは、デモ開始前に実行されます(open、reset、play、render時)。
シェルコマンドまたはブラウザアクションを使用できます。
シェルコマンドは、プロジェクトの作業ディレクトリ(ndemoが呼び出されたディレクトリ)に対して相対的に実行されます。
runコマンド内のすべてのパスは、プロジェクトルートからの相対パスである必要があります。
シェルコマンド — ファイル操作にはrunを使用します:
setup:
- run: cp demo/my-feature/fixtures/original.txt data/page.txt
- run: rm -f data/cache/*.tmp
- run: ./scripts/reset-db.sh
ブラウザアクション — セグメントアクションと同じ構文:
setup:
- type: click
target: { role: button, name: "Login" }
- type: type
target: { role: textbox, name: "Username" }
text: admin
条件付きステップ — ifを追加して、条件が満たされない場合にスキップします:
setup:
# すでにログインしている場合のみログイン
- type: click
target: { role: link, name: "Sign in" }
if:
hidden: ".user-menu" # ユーザーメニューが表示されている場合はスキップ
# 特定のページでのみ実行
- type: click
target: { role: button, name: "Reset" }
if:
url: "**/admin/settings" # このページでない場合はスキップ
条件フィールド:
visible: "<セレクター>"— セレクターが表示されている要素と一致する場合のみステップを実行hidden: "<セレクター>"— セレクターが表示されている要素と一致しない場合のみステップを実行url: "<パターン>"— 現在のURLが一致する場合のみステップを実行(**= 任意のパス)
アクションタイプリファレンス
| タイプ | 必須フィールド | 備考 |
|---|---|---|
| click | target | |
| type | target, text | オプションのdelay(キーストローク間のミリ秒、デフォルト 80) |
| hover | target | |
| scroll | target | 要素をビューにスクロール |
| wait | duration(ミリ秒) | 視聴者用の一時停止(デフォルト 1000ms) |
| press | key | キーボードキー、例: "Enter"、"Escape" |
| select | target, option | ドロップダウン選択 |
セグメントIDルール
- 小文字の英数字とハイフン:
open-settings、toggle-dark - プレイブック内で一意である必要があります
- オーディオ/ビデオのファイル名として使用されるため、短くしてください
トラブルシューティング
- ブラウザが応答しない:
${CLAUDE_SKILL_DIR}/ndemo closeを実行してからopenを再度実行してください。.ndemo/daemon.logでブラウザデーモンの出力を確認してください。 - 要素が見つからない:
${CLAUDE_SKILL_DIR}/ndemo page-stateを実行して、現在のページのアクセシビリティツリーを確認し、意図した要素が存在するか、異なるセレクターが必要かを確認してください。 - アクションがタイムアウトする:
done条件のタイムアウトを増やすか、条件が正しいことを確認してください。