agentshot: Screenshots und Demovideos
Das Tool befindet sich unter /path/to/agentshot. Führen Sie es aus mit:
node /path/to/agentshot/bin/agentshot.mjs <Befehl> ...
Im Folgenden steht agentshot für diesen Befehl. Jeder Befehl gibt eine JSON-Zeile aus ("ok": true|false). Lange Render-Vorgänge geben zusätzlich Fortschritt auf stderr aus.
Führen Sie agentshot help für die Befehlsliste aus.
Arbeitsablauf
- Lesen Sie zuerst die Regeln des Produkts:
agentshot style <Produkt>gibtstyle.json(Standard-Look) undguidelines.md(was der Benutzer mag) aus. Folgen Sie ihnen. Wenn das Produkt neu ist, führen Sieagentshot style <Produkt> --initaus. - Erfassen (eines auswählen):
- Web-App oder Expo-App → schreiben Sie eine Szenario-JSON mit
"url", führen Sieagentshot capture scenario.jsonaus. - iOS-App auf einem Mac → ein Szenario mit
"simulator"(die echte native App im iOS Simulator, siehe "iOS Simulator"); ein schnelles Standbild →agentshot sim snap --name <Aufnahme> --clean-status-bar. - Desktop-Programm → ein Szenario mit
"window": Sie steuern die echte Maus/Tastatur, während es aufzeichnet (siehe "Desktop-App"). Nur ein Standbild →agentshot snap --window "<Titel>" --name <Aufnahme>. Der Benutzer führt die Aktionen aus →agentshot record start/stop. - Der Benutzer hat bereits eine Datei (iPhone-Screenshot / Bildschirmaufnahme) →
agentshot import <Datei> --name <Aufnahme>.
- Web-App oder Expo-App → schreiben Sie eine Szenario-JSON mit
- Inspizieren:
agentshot inspect <Aufnahme>listet Aufnahmen, Element-Boxen (Bildschirmpunkte), Klicks, Dauer und, bei Videos,moves(Zeigerbewegung),changes(Seitenwechsel: große visuelle Sprünge),quiet(nichts bewegt sich: Schnitt oder Beschleunigung) und die Szenario-timelineauf. Planen Sie Schnitte und Zooms anhand dieser Zahlen und bestätigen Sie mitagentshot frames <Aufnahme> --from a --to b(ein Kontaktbogen mit Zeitstempeln; lesen Sie das ausgegebene PNG). Betrachten Sie das Aufnahme-PNG mit dem Lese-Tool, bevor Sie es gestalten. - Schreiben Sie ein Rezept JSON und führen Sie
agentshot render recipe.json --note "<was geändert wurde>"aus. - Überprüfen Sie das Ergebnis selbst: Lesen Sie das PNG; bei MP4:
agentshot frames <Produkt>/<Asset>(gesamtes Video) undagentshot frames <Produkt>/<Asset> --from 3 --to 4 --every 0.033 --crop x,y,w,h(Bild für Bild um einen Schnitt oder Klick herum). Korrigieren und erneut rendern (eine neue Version wird hinzugefügt). Wenn der Benutzer ein Rezept selbst durchgehen oder anpassen möchte, führen Sieagentshot preview recipe.jsonim Hintergrund aus: Es öffnet eine lokale Seite mit der Szene und einem Scrubber und aktualisiert live, wenn das Rezept (oder diestyle.jsondes Produkts) bearbeitet wird. Es gibt eine JSON-Zeile mit derurlaus und bleibt aktiv. - Melden Sie den Ausgabepfad (
results[].output).--copy-to <Verz>kopiert es auch in das Projekt des Benutzers. - Lernen: Wenn der Benutzer etwas korrigiert oder lobt, fügen Sie eine datierte Zeile an die
guidelines.mddes Produkts an (und aktualisieren Sie die Standardwerte instyle.json, wenn es sich um einen Standard-Look handelt). Lesen Sie es beim nächsten Mal.
Halten Sie Szenarien und Rezepte im Projekt des Benutzers (z. B. docs/shots/), damit sie nach UI-Änderungen erneut ausgeführt werden können.
Szenario (Web-App erfassen)
Kommentare in den JSON-Beispielen unten sind nur Erklärungen; echte Dateien müssen reines JSON sein.
{
"name": "ellie-today-drag", // Aufnahme-Name, eindeutig; mit dem Produktnamen präfixieren
"url": "http://localhost:8081", // oder ein Dateipfad
"device": "iphone", // iphone | iphone-max | ipad | desktop | laptop
"safeArea": true, // Expo/RN Web: Chrome meldet iPhone Safe-Area-Insets, die App layoutet
// wie auf einem Gerät ("pad" = Padding erzwingen für Apps, die Insets ignorieren)
"css": "[class*='navigationMenuRoot'] { display: none }", // Web-only Chrome ausblenden (Expo Router Tabs erscheinen im Web als Top-Menü)
"waitFor": "today-list", // auf dieses Element warten, bevor begonnen wird
"track": { "row": "text=Plan a birthday" }, // zusätzliche benannte Element-Boxen aufzeichnen
"steps": [
{ "shot": "main" }, // Standbild in voller Auflösung (für Screenshots)
{ "wait": 0.6 },
{ "tap": "add-button" }, // auf ein Ziel klicken/tippen (bewegt sich zuerst dorthin)
{ "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" }
]
}
Ziele: Ein bloßer Name passt auf data-shot="..." oder data-testid="..." (React Native testID wird
data-testid im Web, fügen Sie also testIDs zu den Elementen hinzu, auf die Sie zeigen möchten), oder auf einen beliebigen Playwright-Selektor
(text=Save, role=button[name="Save"], #id, .class), oder [x, y] in CSS-Px.
Alle Elemente mit data-shot/data-testid werden mit ihren Boxen aufgezeichnet, zusammen mit track-Einträgen.
Timing: Standardmäßig ("timing": "frame") wird die Seitenuhr angehalten und genau ein Frame nach dem anderen
vorgerückt (60 fps) mit einem Screenshot in voller Auflösung pro Frame, sodass App-Animationen in jeder Auflösung
flüssig sind; die Aufnahme dauert einige Male länger als das Szenario. Verwenden Sie "timing": "real" nur, wenn
die App unter einer kontrollierten Uhr kaputt geht (ein Schritt wird dann mit Timeout abgebrochen). agentshot capture meldet Klickzeiten
(clicks) zur Planung von cuts/zoom und Konsolenfehler/-warnungen (console). Entwickler-Overlays (Expo LogBox
Toast, Next.js-Indikator) werden ausgeblendet; "devOverlays": true behält sie.
Bevorzugen Sie ein durchgehendes cuts-Segment gegenüber mehreren harten Schnitten: Sprünge zwischen Segmenten wirken ruckelig.
Ziele in einer App ohne testIDs finden: Öffnen Sie die Seite mit Playwright und listen Sie Schaltflächen/Eingaben
(role, aria-label, Text) auf, verwenden Sie dann text=..., role=button[name=/^My daughter/] oder
[aria-label='Your prayer'], und benennen Sie Boxen, die Sie durch track hervorheben/lupe möchten.
Das Tippen in ein Feld, das von einer klebrigen Fußzeile bedeckt ist, funktioniert trotzdem (das Feld erhält Fokus).
Expo: Starten Sie zuerst den Web-Build (npx expo start --web, normalerweise http://localhost:8081). Nur-native
Module funktionieren möglicherweise nicht im Web; wenn ein Bildschirm kaputt geht, bitten Sie den Benutzer um einen iPhone-Screenshot/eine Aufnahme und importieren Sie sie (import).
Desktop-App (Szenario mit "window")
agentshot windows listet Fenster auf (Titel, Prozess, Größe). Das Fenster wird nach Titel, dann nach Prozessnamen zugeordnet,
wiederhergestellt, wenn es minimiert ist, und in den Vordergrund gebracht. Koordinaten sind Fenster-Pixel: Betrachten Sie zuerst ein Standbild
(agentshot snap --window "X" --name x-probe, lesen Sie das PNG) und benennen Sie die Boxen, die Sie in track verwenden.
{
"name": "notes-tour",
"window": "Notes", // Titelteil oder Prozessname
"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], // wo der Zeiger beginnt
"steps": [
{ "shot": "home" }, // Standbild = der aufgezeichnete Frame zu diesem Zeitpunkt
{ "wait": 1 },
{ "click": "nav-recent", "duration": 0.9 }, // dorthin gleiten (abgerundet, leichter Bogen), sofort klicken
{ "waitStill": 0.6, "region": "content" }, // warten, bis sich der Bildschirm ändert, dann bis zur Beruhigung
{ "shot": "recent" },
{ "wheel": 4, "at": [760, 460] }, // 4 Stufen nach unten scrollen (negativ = nach oben)
{ "click": "nav-search" }, { "press": "ctrl+a" },
{ "type": "Weekly plan", "cps": 12 }, // beliebiger Text, einschließlich Nicht-lateinischer Schriften
{ "press": "Enter" }, // Tasten: 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" } // Markierung in der Timeline
]
}
-
Das Ergebnis hat
timeline(Start/Ende jedes Schritts in Videosekunden,at= Klickmoment) undshots(Standbilder aus dem Video geschnitten). Rezepte verwendentrack-Namen als Ziele ("blur"auf"profile"usw.). -
waitStillwartet zuerst (bis zuchangeTimeout, 3 s), bis sich die Region von ihrem vorherigen Zustand unterscheidet (change, mittlere Pixeldifferenz, Standard 6;"change": falseum nur zu warten, bis sie sich beruhigt), dann bis sich für die angegebene Zeit nichts geändert hat (threshold0.6). Verwenden Sie eineregionohne animierte Teile (Player, Live-Hintergründe). -
Klicken Sie direkt nach der Ankunft: Ein Cursor, der ~0,5 s über einem Bedienelement ruht, lässt Windows-Tooltips ins Video einblenden.
-
Felder, die alten Text behalten:
press ctrl+avortype. -
Der Treiber startet vor der Aufnahme (er benötigt einige Sekunden) und bewegt den echten Cursor: Verwenden Sie die Maschine in der Zwischenzeit nicht. Desktop-Aufnahmen sind 1x, daher sieht eine Lupe weich aus; bevorzugen Sie
lift. -
Standbilder zu jedem Zeitpunkt einer Aufnahme:
agentshot snap <Aufnahme> --at 12.4 --shot name. -
Kleine Änderungen (ein Popup schließt sich) erreichen möglicherweise nicht den
change-Wert über das gesamte Fenster: Geben Sieregionum sie herum an. -
Tastenkürzel:
modist Strg unter Windows und Cmd unter macOS ("press": "mod+a"), sodass ein Szenario auf beiden läuft.
Manuelle Aufnahme (der Benutzer führt die Aktionen aus; der Cursor wird protokolliert und vom Renderer gezeichnet):
agentshot record --name app-filter --window "Figma" --duration 12 # zeichnet 12 s auf, dann Stopp
agentshot record start --name app-filter --window "Figma" # oder start ...
agentshot record stop # ... und später stoppen
macOS
Gleiche Befehle und Szenarien wie unter Windows; recorder/mac.swift wird bei der ersten Verwendung kompiliert (benötigt die Xcode
Command Line Tools und macOS 14+). Führen Sie zuerst agentshot doctor aus: Die App, die den Agenten ausführt (Terminal, iTerm,
Claude, VS Code) benötigt Bildschirmaufnahme und Bedienungshilfen in den Systemeinstellungen > Datenschutz & Sicherheit
(agentshot doctor --request öffnet die Aufforderungen; starten Sie die App nach der Gewährung neu). Ohne Bildschirmaufnahme sind
Fenstertitel nur App-Namen und die Aufnahme schlägt fehl; ohne Bedienungshilfen kann der Treiber nicht klicken oder tippen.
- Koordinaten (
track, Schritte, Rezeptziele) sind Punkte; Standbilder und Videos haben die Pixel- Skalierung des Displays (2 auf Retina,inspectzeigtscale), sodass Abhebungen und Zooms scharf bleiben. - ScreenCaptureKit zeichnet das Fenster selbst auf: Es kann teilweise verdeckt sein, darf aber nicht minimiert sein.
node scripts/selftest-mac.mjsüberprüft alles auf TextEdit (und dem Simulator, falls einer gestartet ist) und gibt aus, was fehlgeschlagen ist. Sync-Prüfung:agentshot record ... --show-cursorbrennt den echten Cursor ein; vergleichen Sie ihn mit dem protokollierten Zeiger (agentshot framesvsinspect) wie bei jeder Aufnahme.
iOS Simulator (macOS)
Die native App anstelle ihres Web-Builds: echte Schriftarten, native Module, iOS-Animationen. Starten Sie einen Simulator
(open -a Simulator), führen Sie die App darin aus (npx expo run:ios oder ein Dev-Build), dann:
agentshot sim devices // gestartete Simulatoren
agentshot sim snap --name app-home --clean-status-bar // verlustfreies Standbild, 9:41 / voller Akku
agentshot sim record start --name app-flow // jemand anderes steuert ...
agentshot sim record stop
Ein Szenario steuert es mit idb (brew install facebook/fb/idb-companion, pipx install fb-idb); Koordinaten
sind Gerätepunkte (Lesen Sie ein sim snap und teilen Sie seine Pixel durch scale):
{
"name": "app-onboarding",
"simulator": "booted", // oder eine UDID oder ein Teil des Gerätenamens
"statusBar": "clean", // 9:41 während der Aufnahme, danach gelöscht
"appearance": "light", // oder "dark"
"openUrl": "exp://127.0.0.1:8081/--/today", // Deep Link vor der Aufzeichnung (oder "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" }, // in das fokussierte Feld
{ "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" }
]
}
Standbilder sind verlustfreie Simulator-Screenshots; Tippen und Wischen werden zum Touch-Zeiger und zu Klicks, daher verwenden
Rezepte "pointer": "touch" und "device": "iphone-black" wie bei Browser-Aufnahmen. Ohne idb funktionieren nur
wait/waitStill/agentshot/openUrl/launch-Schritte.
Rezept (Stil und Rendern)
{
"name": "today-drag", // Asset-Name in der Bibliothek
"product": "ellie", // Bibliotheksordner; style.json-Standardwerte werden angewendet
"capture": "ellie-today-drag",
"shot": "main", // für ein PNG-Standbild setzen; für ein MP4-Video weglassen
"size": "16:9", // 16:9 16:10 4:3 3:2 1:1 4:5 9:16, oder Breite/Höhe
"background": "lavender", // lavender peach sage sky rose mint neutral midnight ink white,
// jede CSS-Farbe / jeder Verlauf oder ein Bild unter public/ (backgrounds/x.jpg)
"backgroundBlur": 0.02, // für Fotohintergründe, Anteil der kurzen Seite
"device": "iphone-black", // iphone-black iphone-natural iphone-white browser window none
"url": "app.ellie.so/today", // Browserleistentext; "title": Fenstertitel für "window"
"padding": 0.08, "tilt": 12, // Geräterand; 3D-Neigung in Grad
"position": [0.69, 0.5], // Gerätemittelpunkt auf der Leinwand, lässt Platz für einen Titel daneben
"accent": "#6d5ce8", // Farbe von Schritten, Konturen, Beschriftungen, Klickwellen
"font": "Gilda Display", "labelFont": "Inter", // Titel / Lift-Beschriftungen: Inter Manrope Poppins Nunito Fraunces Lora
// "Playfair Display" "DM Serif Display" "Gilda Display" "Cormorant Garamond" Caveat
"lift": { "target": "task-row", "label": "Ziehen Sie es auf Ihren Tag", "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": [
{ "type": "arrow", "from": { "anchor": [0.5, 0.5] }, "to": { "target": "add-button" }, "tail": 12, "head": 18 },
{ "type": "step", "target": "a-b", "label": "Stufe 3", "at": 2.0 },
{ "type": "blur", "target": "task-birthday", "radius": 8, "exclude": [16, 20, 220, 32] },
{ "type": "cut", "at": 1.2 },
{ "type": "cursor", "shape": "hand" },
{ "type": "caption", "text": "Los geht's", "align": "right" },
{ "type": "zoom", "target": "row-0", "start": 3.6, "end": 5.1, "scale": 2.5 },
{ "type": "highlight", "target": "title-input", "style": "glow", "glowColor": "#ffffff" }
],
"title": [
{ "width": 0.28, "height": 0.16, "text": "Heute", "fontSize": 42, "align": "right", "vAlign": "bottom", "x": 0.45, "y": 0.28 },
{ "lines": "Ziehen, um zu planen", "x": 0.45, "y": 0.53 }
],
"cuts": [
{ "segment": [0, 1.2], "speed": 1.2 },
{ "segment": [1.4, 3.8], "speed": 2.0, "blend": "fade", "blendDuration": 0.3 },
{ "segment": [4.0, 8.0], "clip": [4.4, 7.6] }
],
"loop": 8.2, // Endpunkt der Schleife (in Sekunden), wenn das Video am Ende die Szene wiederholt
"titleHold": 2.0, // Titel nach dem letzten Frame halten
"progress": false // Fortschrittsbalken deaktivieren (für Vorschaubilder)
}