agentshot: スクリーンショットとデモ動画
ツールは /path/to/agentshot にあります。次のように実行します:
node /path/to/agentshot/bin/agentshot.mjs <command> ...
以下では、agentshot はそのコマンドを意味します。すべてのコマンドは JSON の1行 ("ok": true|false) を出力します。長時間のレンダリングでは、stderr に進捗も出力されます。
コマンド一覧については agentshot help を実行してください。
ワークフロー
- 最初に製品のルールを読む:
agentshot style <product>はstyle.json(デフォルトの見た目) とguidelines.md(ユーザーの好み) を出力します。それらに従ってください。製品が新しい場合は、agentshot style <product> --initを実行します。 - キャプチャ (いずれかを選択):
- Web アプリまたは Expo アプリ →
"url"を含むシナリオ JSON を書き、agentshot capture scenario.jsonを実行します。 - Mac 上の iOS アプリ →
"simulator"を含むシナリオ (iOS シミュレーター上の実際のネイティブアプリ、"iOS Simulator" 参照); クイック静止画 →agentshot sim snap --name <capture> --clean-status-bar。 - デスクトッププログラム →
"window"を含むシナリオ: 記録中に実際のマウス/キーボードを操作します ("Desktop app" 参照)。単なる静止画 →agentshot snap --window "<title>" --name <capture>。 ユーザーが操作を実行 →agentshot record start/stop。 - ユーザーがすでにファイルを持っている場合 (iPhone スクリーンショット / 画面収録) →
agentshot import <file> --name <capture>。
- Web アプリまたは Expo アプリ →
- 検査する:
agentshot inspect <capture>は、ショット、要素ボックス (画面の点)、クリック、持続時間、そして動画についてはmoves(ポインターの移動)、changes(ページ切り替え: 大きな視覚的ジャンプ)、quiet(何も動かない: カットまたは速度変更) およびシナリオのtimelineをリストします。これらの数値からカットとズームを計画し、agentshot frames <capture> --from a --to b(タイムスタンプ付きのコンタクトシート; それが出力する PNG を読んでください) で確認します。スタイリングの前に、Read ツールでショットの PNG を確認します。 - レシピ JSON を書き、
agentshot render recipe.json --note "<変更内容>"を実行します。 - 結果を自分で確認する: PNG を読む; MP4 の場合は
agentshot frames <product>/<asset>(動画全体) とagentshot frames <product>/<asset> --from 3 --to 4 --every 0.033 --crop x,y,w,h(カットまたはクリックの前後のフレームごと)。修正して再レンダリングします (新しいバージョンが追加されます)。 ユーザーがレシピを自分でスクラブまたは微調整したい場合は、agentshot preview recipe.jsonをバックグラウンドで実行します: シーンとスクラバーを備えたローカルページが開き、レシピ (または製品のstyle.json) が編集されるとライブで更新されます。urlを含む JSON 行を1つ出力し、サーバーを維持します。 - 出力パスを報告する (
results[].output)。--copy-to <dir>はそれをユーザーのプロジェクトにコピーします。 - 学習する: ユーザーが何かを修正または賞賛した場合、製品の
guidelines.mdに行を追加します (日付付きで) (そして、デフォルトの見た目である場合はstyle.jsonのデフォルトを更新します)。次回読んでください。
シナリオとレシピは、UI 変更後に再実行できるように、ユーザーのプロジェクト (例: docs/shots/) に保存してください。
シナリオ (Web アプリのキャプチャ)
以下の JSON 例のコメントは説明のみです。実際のファイルはプレーンな JSON でなければなりません。
{
"name": "ellie-today-drag", // キャプチャ名、一意; 製品名をプレフィックスとして付ける
"url": "http://localhost:8081", // またはファイルパス
"device": "iphone", // iphone | iphone-max | ipad | desktop | laptop
"safeArea": true, // Expo/RN web: Chrome が iPhone のセーフエリアインセットを報告し、アプリはデバイス上のようにレイアウトされます
// ("pad" = インセットを無視するアプリにパディングを強制)
"css": "[class*='navigationMenuRoot'] { display: none }", // Web 専用のクロムを非表示にする (Expo Router タブは Web 上でトップメニューとしてレンダリングされる)
"waitFor": "today-list", // 開始前にこの要素を待つ
"track": { "row": "text=Plan a birthday" }, // 記録する追加の名前付き要素ボックス
"steps": [
{ "shot": "main" }, // フル解像度の静止画 (スクリーンショット用)
{ "wait": 0.6 },
{ "tap": "add-button" }, // ターゲットをクリック/タップ (最初にそこに移動)
{ "type": "Buy milk", "into": "title-input" },
{ "press": "Enter" },
{ "drag": "task-birthday", "to": "slot-11am", "duration": 1.4 },
{ "scroll": 400, "duration": 0.8 },
{ "move": [200, 300], "duration": 0.6 },
{ "goto": "/settings" },
{ "waitFor": "settings-title" },
{ "shot": "after" }
]
}
ターゲット: ベアネームは data-shot="..." または data-testid="..." (React Native の testID は Web 上で data-testid になります。したがって、指したい要素に testID を追加してください)、または任意の Playwright セレクター (text=Save、role=button[name="Save"]、#id、.class)、または CSS px での [x, y] に一致します。
data-shot/data-testid を持つすべての要素は、そのボックスと共に track エントリとして記録されます。
タイミング: デフォルト ("timing": "frame") では、ページクロックは一時停止され、1フレームずつ正確に進められます (60 fps)。各フレームでフル解像度のスクリーンショットが撮られるため、アプリのアニメーションはどの解像度でもスムーズです。キャプチャにはシナリオよりも数倍の時間がかかります。"timing": "real" は、アプリが制御されたクロックの下で壊れる場合 (ステップがタイムアウトする場合) にのみ使用してください。agentshot capture は、クリック時間 (clicks) を報告して cuts/zoom を計画し、コンソールエラーと警告 (console) も報告します。開発時オーバーレイ (Expo LogBox トースト、Next.js インジケーター) は非表示になります。"devOverlays": true で維持されます。
1つの連続した cuts セグメントを、複数のハードカットよりも優先してください: セグメント間のジャンプはぎくしゃくして見えます。
testID がないアプリでターゲットを見つける: Playwright でページを開き、ボタン/入力をリストし (role、aria-label、text)、text=...、role=button[name=/^My daughter/] または [aria-label='Your prayer'] を使用し、track を通じてリフト/ルーペしたいボックスに名前を付けます。スティッキーフッターで覆われているフィールドへの入力も機能します (フィールドがフォーカスを取得します)。
Expo: 最初に Web ビルドを開始します (npx expo start --web、通常は http://localhost:8081)。ネイティブのみのモジュールは Web 上で動作しない場合があります。画面が壊れる場合は、ユーザーに iPhone のスクリーンショット/録画を依頼し、import してください。
デスクトップアプリ ("window" を使用したシナリオ)
agentshot windows はウィンドウをリストします (タイトル、プロセス、サイズ)。ウィンドウはタイトル、次にプロセス名で照合され、最小化されている場合は復元され、前面に表示されます。座標はウィンドウピクセルです: 最初に静止画を確認し (agentshot snap --window "X" --name x-probe、PNG を読む)、track で使用するボックスに名前を付けます。
{
"name": "notes-tour",
"window": "Notes", // タイトル部分またはプロセス名
"track": { "nav-recent": [20, 200, 190, 40], "nav-search": [20, 96, 190, 40], "content": [232, 40, 1030, 650], "profile": [16, 718, 150, 62] },
"cursor": [1000, 330], // ポインターの開始位置
"steps": [
{ "shot": "home" }, // 静止画 = この時点で記録されたフレーム
{ "wait": 1 },
{ "click": "nav-recent", "duration": 0.9 }, // そこにグライド (イージング、わずかな弧)、すぐにクリック
{ "waitStill": 0.6, "region": "content" }, // 画面が変化するのを待ち、次に落ち着くのを待つ
{ "shot": "recent" },
{ "wheel": 4, "at": [760, 460] }, // 下に4ノッチスクロール (負の値は上)
{ "click": "nav-search" }, { "press": "ctrl+a" },
{ "type": "Weekly plan", "cps": 12 }, // 任意のテキスト、非ラテン文字も含む
{ "press": "Enter" }, // キー: enter esc tab space backspace delete arrows home end f1..f12, ctrl/alt/shift/win+x
{ "drag": [100, 300], "to": [400, 300], "duration": 1.2 },
{ "click": [640, 400], "button": "right" }, { "click": [640, 400], "double": true },
{ "note": "beat 2" } // タイムラインのマーカー
]
}
- 結果には
timeline(各ステップの開始/終了を動画の秒数で、at= クリックの瞬間) とshots(動画から切り出された静止画) が含まれます。レシピはtrack名をターゲットとして使用します (例:"profile"上の"blur")。 waitStillは最初に (最大changeTimeout、3秒) 領域が以前と異なるのを待ち (change、平均ピクセル差、デフォルト 6;"change": falseで落ち着くのだけを待つ)、次に指定された秒数 (しきい値 0.6) 何も変化しなくなるのを待ちます。アニメーション部分 (プレーヤー、ライブ背景) のないregionを使用してください。- 到着後すぐにクリック: カーソルがコントロール上に約 0.5 秒間置かれていると、Windows のツールチップが動画に表示されます。
- 古いテキストを保持するフィールド:
typeの前にpress ctrl+aを実行します。 - ドライバーは録画の前に起動し (数秒必要)、実際のカーソルを移動します: その間はマシンを使用しないでください。デスクトップキャプチャは 1x であるため、ルーペはソフトに見えます;
liftを優先してください。 - 録画中の任意の時点での静止画:
agentshot snap <capture> --at 12.4 --shot name。 - 小さな変更 (ポップアップが閉じるなど) はウィンドウ全体で
changeに達しない場合があります: その周りにregionを指定します。 - ショートカット:
modは Windows では Ctrl、macOS では Cmd です ("press": "mod+a")。したがって、1つのシナリオが両方で実行されます。
手動録画 (ユーザーが操作を実行; カーソルはレンダラーによってログに記録され描画されます):
agentshot record --name app-filter --window "Figma" --duration 12 # 12秒録画し、停止
agentshot record start --name app-filter --window "Figma" # または start ...
agentshot record stop # ... 後で停止
macOS
Windows と同じコマンドとシナリオ; recorder/mac.swift は初回使用時にコンパイルされます (Xcode コマンドラインツールと macOS 14+ が必要)。最初に agentshot doctor を実行してください: エージェントを実行するアプリ (Terminal、iTerm、Claude、VS Code) には、システム設定 > プライバシーとセキュリティで 画面収録 と アクセシビリティ が必要です (agentshot doctor --request でプロンプトを開きます; 許可付与後にそのアプリを再起動してください)。画面収録がないとウィンドウタイトルはアプリ名のみになり、録画は失敗します。アクセシビリティがないと、ドライバーはクリックやタイピングができません。
- 座標 (
track、ステップ、レシピのターゲット) は ポイント です。静止画と動画はディスプレイのピクセルスケール (Retina では 2、inspectでscaleが表示されます) であるため、リフトとズームは鮮明に保たれます。 - ScreenCaptureKit はウィンドウ自体を録画します: 部分的に覆われていてもかまいませんが、最小化されてはいけません。
node scripts/selftest-mac.mjsは TextEdit (およびブートされている場合はシミュレーター) ですべてをチェックし、失敗したものを出力します。同期チェック:agentshot record ... --show-cursorは実際のカーソルを焼き付けます; それをログに記録されたポインターと比較します (agentshot framesvsinspect)。これは任意の録画と同様です。
iOS シミュレーター (macOS)
Web ビルドではなくネイティブアプリ: 実際のフォント、ネイティブモジュール、iOS アニメーション。シミュレーターを起動し (open -a Simulator)、その中でアプリを実行し (npx expo run:ios または開発ビルド)、その後:
agentshot sim devices # 起動中のシミュレーター
agentshot sim snap --name app-home --clean-status-bar # ロスレス静止画、9:41 / フルバッテリー
agentshot sim record start --name app-flow # 他の誰かが操作...
agentshot sim record stop
シナリオは idb でドライブします (brew install facebook/fb/idb-companion、pipx install fb-idb); 座標はデバイスポイントです (sim snap を読み、そのピクセルを scale で割ります):
{
"name": "app-onboarding",
"simulator": "booted", // または UDID、またはデバイス名の一部
"statusBar": "clean", // キャプチャ中は 9:41、キャプチャ後にクリア
"appearance": "light", // または "dark"
"openUrl": "exp://127.0.0.1:8081/--/today", // 録画前のディープリンク (または "launch": "<bundle id>")
"track": { "add": [330, 760, 48, 48], "title": [24, 120, 340, 44] },
"steps": [
{ "shot": "today" },
{ "tap": "add" },
{ "waitStill": 0.5 },
{ "type": "Buy milk" }, // フォーカスされたフィールドに入力
{ "press": "enter" }, // enter esc backspace tab space arrows home lock
{ "swipe": [200, 600], "to": [200, 250], "duration": 0.4 },
{ "openUrl": "exp://127.0.0.1:8081/--/settings" },
{ "waitStill": 0.6 },
{ "shot": "settings" }
]
}
静止画はロスレスのシミュレータースクリーンショットです。タップとスワイプはタッチポインターとクリックになります。そのため、レシピではブラウザキャプチャと同様に "pointer": "touch" と "device": "iphone-black" を使用します。idb がない場合は、wait/waitStill/agentshot/openUrl/launch ステップのみが機能します。
レシピ (スタイルとレンダリング)
{
"name": "today-drag", // ライブラリ内のアセット名
"product": "ellie", // ライブラリフォルダー; style.json のデフォルトが適用される
"capture": "ellie-today-drag",
"shot": "main", // PNG 静止画の場合は設定; MP4 動画の場合は省略
"size": "16:9", // 16:9 16:10 4:3 3:2 1:1 4:5 9:16、または width/height
"background": "lavender", // lavender peach sage sky rose mint neutral midnight ink white、
// 任意の CSS 色/グラデーション、または public/ の下の画像 (backgrounds/x.jpg)
"backgroundBlur": 0.02, // 写真背景の場合、短辺の割合
"device": "iphone-black", // iphone-black iphone-natural iphone-white browser window none
"url": "app.ellie.so/today", // ブラウザバーのテキスト; "title": "window" のウィンドウタイトル
"padding": 0.08, "tilt": 12, // デバイスの余白; 3D 傾斜角度 (度)
"position": [0.69, 0.5], // キャンバス上のデバイス中心、横にタイトルを配置するスペースを残す
"accent": "#6d5ce8", // ステップ、アウトライン、ラベル、クリック波紋の色
"font": "Gilda Display", "labelFont": "Inter", // タイトル / リフトラベル: Inter Manrope Poppins Nunito Fraunces Lora Artifika
// "Playfair Display" "DM Serif Display" "Gilda Display" "Cormorant Garamond" Caveat
"lift": { "target": "task-row", "label": "一日にドラッグ", "start": 3.9, "pad": 10, "dim": 0.1 },
"loupe": { "target": "label-chip", "anchor": [0.2, 0.5], "zoom": 2.3, "size": 0.12, "offset": [-0.15, 0.12] },
"annotations": [
{ "target": "task-birthday", "color": "#ff7700", "radius": 2, "label": "1", "offset": [0, -0.03] }
],
"steps": [
{ "start": 3.0, "count": ["1", "2", "3"], "prefix": "ステップ", "offset": [0, -0.07], "align": "center" },
{ "start": 3.0, "label": "重要なアクション", "position": [0.02, 0.02], "accent": false }
],
"arrows": [
{ "from": "row", "to": "add-button", "color": "#e74c3c", "tip": "arrow", "style": "round", "start": 4.2, "end": 4.5 }
],
"blur": [
{ "target": "profile", "start": 0, "end": 1, "dim": 0.7, "radius": 12 },
{ "region": [100, 100, 0.5, 0.3], "start": 2, "dim": 0.7, "radius": 12 }
],
"cuts": [
{ "from": 0, "to": 4.5, "caption": "アクションを設定" },
{ "from": 5.5 }
],
"zoom": [
{ "from": 0, "to": 0.3, "center": "row" },
{ "from": 4.2, "to": 4.5, "center": "add-button" }
],
"cursor": { "shape": "pointer", "from": 0.2, "end": 5.5, "glow": 0.08, "trail": 0.06 },
"sections": [
{ "title": "タスクの追加", "start": 0, "subtitle": "イベントごと" },
{ "title": "タイムラインの表示", "start": 4.5 }
],
"watermark": [
{ "image": "logos/ellie.png", "position": [0.9, 0.06], "opacity": 0.5 }
],
"overlay": "説明: {caption}", // 動画上のテキスト/レイヤー。キャプション、位置、その他の値については help
"watch": ["../src/App.tsx"], // これらのファイルを監視し、変更時に render を再実行
"duration": 5.5, // 出力の長さ (秒)。指定しない場合、cuts の範囲まで
"fps": 12, // 1 ~ 60。出力フレームレート。低いほどファイルサイズが小さくなります。12 が推奨
"quality": 0.88, // MP4/WebM の品質: 0–1 (1 = ロスレス)。0.88 が適切なトレードオフ
"codec": "h264", // h264 h265 vp9 hevc (macOS は h264 で十分)
"output": "final-demo.mp4" // 出力ファイル名、デフォルトは <product>/<name>/v<version>.<ext>
}
すべての "start" と "end" (秒数、またはセグメントがない場合は "from" / "to") は、cuts が適用される前の、キャプチャのタイムラインに対する値です。レシピはレンダリング後に上書きされます: duration、cuts、zoom セグメントは inspect タイミングに合わせて調整されます。
zoom はキーフレームでイージングされます。from と to の両方に center を設定します。"easing": "ease-in-out" (linear、ease-in、ease-out)。
"device": "window" の場合、背景は "background" で、外側のグラデーションは生成されません (tilt、shadow、shine は無視されます)。"device": "none" はキャンバス全体をキャプチャのサイズに合わせます。
overlay 文字列は {caption}、{from}、{to}、{duration}、{product} を補間します。省略すると、組み込みの foot/caption バーが表示されます。"overlay": false で非表示にします。
cut セッションの最初には事前にコンパイルされたテキストが配置されます。cuts 要素は "fade": 0.15 のトランジションになります。
カスタムCSS (スタイルのオーバーライド)
製品の style.json はコンポーネントの外観を制御します:
{
"background": "mesh-gradient-2.png",
"backgroundBlur": 0.1,
"tilt": 8,
"device": "iphone-natural",
"accent": "#1a2b3c",
"font": "Inter",
"labelFont": "Caveat",
"padding": 0.06,
"css": ".agentshot-title { font-weight: 600; }\n.agentshot-lift { border-color: #1a2b3c; }"
}
style.json の css は agentshot-render のスタイル付き HTML に挿入されます。これは、.vis-none で標準コンポーネントの CSS をオーバーライドすることで、特定の製品のすべてのアセットで要素を非表示にするためにも使用できます。
コマンドリファレンス
agentshot help に基づく:
| コマンド | 引数 | 用途 |
|---|---|---|
capture | scenario.json | シナリオを録画する |
style | <product> [--init] | スタイルを読み取る/初期化する |
inspect | <capture> | 要素ボックス、タイミング、クリックを表示する |
frames | <capture>/<asset> [--from a --to b --every s --crop x,y,w,h] | コンタクトシート (タイムスタンプ付きの PNG グリッドまたは個別フレーム) |
render | recipe.json [--note msg] | アセットをレンダリングする |
preview | recipe.json | ライブプレビューを提供する |
snap | --window <title> --name <capture> | デスクトップの静止画 |
record | [start/stop] --name <capture> --window <title> [--duration s] | 手動デスクトップ録画 |
sim | devices snap record start/stop | iOS シミュレーターのキャプチャ |
import | <file> --name <capture> [--product p] | ユーザーのスクリーンショット/録画をインポートする |
windows | アクティブなウィンドウをリストする | |
doctor | [--request] | macOS のアクセス許可を確認する |
help | [command] | ヘルプテキストを表示する |
ライブラリ構造
<project>/
agentshot/
<product>/ # 製品フォルダー
style.json # デフォルトのスタイル
guidelines.md # ユーザーの好みの履歴
<asset>/ # アセットバージョン
v1/ # バージョンフォルダー
recipe.json # このバージョンのレシピ
capture.json # キャプチャメタデータ
output.png / .mp4 # 最終出力
frames/ # コンタクトシート (必要に応じて)
v2/
...
captures/
<capture>/ # 生の録画
scenario.json # 元のシナリオ
recording.mov / .png # 生の画面キャプチャ (デスクトップ) または webm (web)
inspect.json # 検査データ
errors.json # コンソールエラー
シナリオのベストプラクティス
- シナリオを製品名でプレフィックスして、キャプチャの名前の競合を避けます。
data-shotまたはdata-testidを使用して要素に注釈を付け、track の必要性を減らします。- ウェイト: 0.3 ~ 0.6 秒でほとんどのトランジションをカバーできます。
waitStillはページの準備ができたことを確認します。 - デスクトップの場合: キャプチャする前にアプリを最大化します。ウインドウタイトルが一意であることを確認します。
inspect出力で過剰なquiet時間がないか確認します: カットするか、速度を上げます。- レシピはキャプチャ後に編集でき、再レンダリングして新しいバージョンを作成できます。
トラブルシューティング
- キャプチャが失敗する (Web):
urlが実行中であることを確認します。Chrome DevTools プロトコルをブロックするファイアウォールがないか確認します。 - デスクトップクリックが機能しない:
agentshot doctor(macOS) を実行します。アプリにアクセシビリティ権限があることを確認します。 - iOS シミュレーターが接続しない: idb がインストールされているか確認します (
idb list-targets)。シミュレーターが起動していて、アプリがインストールされていることを確認します。 - 間違った要素をターゲットにしている:
agentshot framesを実行して、実際に何がキャプチャされているかを確認します。trackを使用して目的のボックスを正確に特定します。 - macOS の許可の問題:
agentshot doctor --requestを実行し、プロンプトに従います。ターミナル/IDE を再起動します。 - すべての幅/高さ: 値の解釈については
agentshot helpを実行します。これらのドキュメントは version 2 です。