Produktvideo (und die dazugehörigen Assets), Ende zu Ende
Du produzierst ein versandfertiges Produkt-Asset – ein poliertes Video, einen Satz
exakt dimensionierter Standbilder oder beides – aus einer Live-Webseite mit der
vos CLI. Alles ist Daten: Du schreibst ein Ablaufskript, die CLI zeichnet auf und plant,
du optimierst JSON, du renderst neu. Nie neu aufnehmen, um Tempo oder Zooms zu korrigieren – editiere
doc.json und rendere erneut. Qualitätsbarriere: references/taste.md – befolge
ihre Qualitätsschleife und beurteile Standbilder multimodal.
Einrichtung
npm i -D @vosjs/cli
Ein Befehl, ein Paket: @vosjs/cli ist die Open-Source (MIT) vos
Binärdatei mit den Engine-Verben, der hier verwendeten Take-Pipeline (record / plan /
frames / render bei Bildschirmaufnahmen) und den vos.so Plattform-Verben. (Bis
0.9 wurde die Take-Pipeline separat als @vosso/vos-plugin ausgeliefert; ein Projekt,
das sie noch listet, kann sie entfernen.) Voraussetzungen:
- Ein Chromium: Das System-Chrome wird automatisch gefunden; andernfalls
npx playwright install chromiumoderVOS_BROWSER_PATHauf eines setzen. Exit-Code 3 bedeutet, dass kein Browser gefunden wurde. - Netzwerk zur Renderzeit: Die Renderseite lädt three/mediabunny von esm.sh. Die Aufnahme benötigt ebenfalls Zugriff auf die Ziel-URL. Vollständig offline Sandboxes können nicht rendern – sag das stattdessen, anstatt nichts auszuliefern.
Konventionen: Logs → stderr, Ergebnisse → stdout; --json streamt NDJSON, das mit
{"event":"done",…} endet; Exit-Codes 0 ok / 1 Fehler / 2 Verwendung oder strikter
Fehler / 3 kein Browser / 4 der Rekorder traf auf eine Anmeldeseite anstatt der Seite
(eine fehlende oder abgelaufene SESSION, niemals ein Skriptfehler: references/sessions.md).
vos <verb> --help druckt die Flags dieses Verbs (@vosjs/cli 0.41.1 und später).
Schritt 0 – wähle das Ziel (es entscheidet alles)
| Ziel | Viewport | Ausgabe | Extras |
|---|---|---|---|
| Landing-Page-Clip (Held/Section-Einbettung) | 2560×1440 (Footage-nativ 2K) | webm → VP9-Neu-Encoding + Poster | stumme Schleife; siehe references/destinations.md |
| Launch-Video (PH, Social, Store-Promo) | 2560×1440 oder 1280×720 | mp4 (--format mp4, benötigt System-Chrome) | Musikbett via doc.audio |
| Launch-Kit-Standbilder (Store-Screenshots, Tiles, OG) | auf das Asset skaliert | frames --frame <t> --size WxH PNGs vom selben Take | ein Take → jedes Asset |
| Schnelldemo (Issue, PR, Chat) | 1280×720 | webm, Standardeinstellungen | Geschwindigkeit vor Hochglanz; Entwürfe sind NUR hier akzeptabel |
Dimensions- und Byte-Budgets pro Kanal: references/destinations.md.
Die Kernschleife (jedes Ziel)
-
Erkunde die Zielseite mit deinen eigenen Werkzeugen (HTML holen / Playwright). Identifiziere die 3–6 Momente, die EINE Geschichte erzählen. Sammle STABILE Selektoren (
a[href='…'], IDs, Rollen – keine nth-child-Ketten). Bühne den Content wie ein Set: Das Skript muss das Produkt in dem Zustand hinterlassen, den ein stolzer Screenshot zeigen würde – eingegebene Labels, echt aussehende Daten, die Funktion mitten in der Geschichte. Eine leere Leinwand zeichnet schnell auf und demonstriert nichts, und keine nachgelagerte Komposition rettet das.Hinter einem Login? Kläre die Sitzung vor dem Skript. Ein Rekorder ohne Sitzung zeichnet die Wand auf (die Anmeldeseite oder die öffentliche Seite, die die Site einem Fremden zeigt), und das einzige Symptom ist ein übersprungener Selektor. Gehe die Leiter in
references/sessions.mdvon oben nach unten und halte auf der ersten Sprosse an, die hält: keine Wand (ein Demo-Modus, ein lokaler Server ohne Authentifizierung) → MINTE eine Sitzung aus der Test-Authentifizierung, die das Projekt bereits hat (playwright/.auth, einauth.setup.ts, ein Seed-Skript: schau nach, bevor du jemanden fragst) → melde dich off-camera mitsetupan inactions.json(das Passwort von{ "env": "NAME" }, niemals als Literal) → der Mensch meldet sich einmal an (vos session open <url> --name <app>, dann--session <app>bei der Aufnahme) → der Mensch zeichnet mit der Erweiterung von der Shot-Liste aufvos actions script actions.jsongibt dies aus, und du schneidest es. Jede Sprosse endet insetup, einer Zustandsdatei für--storage-stateoder einer benannten Sitzung für--session. Gib niemals ein Produktionspasswort ein oder akzeptiere es, behalte die Zustandsdatei außerhalb des Take-Verzeichnisses und außerhalb von Git, und zeichne von einem Demo-Konto auf: Was das Konto zeigt, wird im Video ausgeliefert.Hast du die Funktion bereits mit agent-browser verifiziert? Behalte diesen Durchlauf und überspringe das zweite Skript. agent-browsers
--jsonErgebnis sagt nicht, was ausgeführt wurde (scrollantwortet mit{scrolled:true}), also umschließe jeden Aufruf, sodass der Befehl neben seinem Ergebnis reitet, dann konvertiere:ab() { agent-browser "$@" --json | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const r=JSON.parse(s);process.stdout.write(JSON.stringify({command:process.argv.slice(1),...r})+"\n")})' -- "$@" >> steps.jsonl; } ab open https://target.example; ab snapshot -i -u; ab click @e27 ab wait 800; ab snapshot -i # die Seite hat sich geändert: Ref-Nummern wurden neu vergeben ab fill @e2 query; ab press Enter vos actions from-agent-browser steps.jsonl --out actions.json(ein gesamter Pfad, der als ein
agent-browser batch … --json > steps.jsonlausgeführt wird, hat bereits diese Form). Das Log iststeps.jsonlim Verzeichnis, in dem du dich befindest, also gehe von einem Verzeichnis aus. Referenzen lösen sich durch das letztesnapshot -ivor ihnen auf, und sie NEU-NUMMERIEREN nach einer Navigation und erneut innerhalb eines Dialogs: Mache einen neuen Snapshot nach allem, was die Seite ändert, und lies die Referenzen, bevor du eine benennst (-ugibt Links ihren href). Klicke das Steuerelement, drücke nicht seine Tastenkombination: ⌘K öffnet den Dialog für einen Menschen, aber ein Tastendruck ist der eine Schritt, den der Rekorder nicht wiederholen kann. Was auch immer er nicht verfolgen kann (eine Tastenkombination, ein Drag, ein zweitesopen) wird in der Ausgabe GENANNT, niemals weggelassen: lies die Notizen, schreibe diese Schritte von Hand, dann zeichne auf. -
Schreibe
actions.json:{ "url": "https://target.example", "viewport": { "width": 1280, "height": 720 }, "steps": [ { "do": "wait", "ms": 800 }, { "do": "hover", "selector": "a[href='/pricing']", "ms": 700 }, { "do": "click", "selector": "#cta" }, { "do": "wait", "ms": 1500 }, { "do": "scroll", "dy": 400 }, { "do": "move", "x": 640, "y": 320 }, { "do": "wait", "ms": 900 } ] }Verben:
waithoverclicktypescrollmovedrag(type={do:'type', selector, text, delayMs?, ms?, focus?}: es klickt das Feld, dann tippt estext;focus:falsetippt in das, was bereits fokussiert ist, für ein abschließendes Enter) (drag = echte Bearbeitungen:{do:'drag', selector|x,y, tx, ty, ms}– einen Bereichsschieberegler bewegen, ein Canvas-Element ziehen, einen Timeline-Clip verschieben). Das Tempo IST der Zoomplan:wait ≥700msöffnen; über Wichtigem hover 700–900ms (Verweildauern werden Zooms); diemseines Klicks ist LESEZEIT, gemessen ab der letzten Änderung der Seite (@vosjs/cli0.47 und später; der Rekorder bezahlt die Beruhigung selbst), also dimensioniere es als einen Takt: etwa 1000ms nach einer Navigation, 600ms nach einem Steuerelement, niemals aufgefüllt für eine langsame Seite; ende beruhigt. Leite den Cursor von Hover-ausgelösten Menüs weg (taste.md, Flussregeln). Überprüfe mitvos validate actions.json.Dann PROBE es (
@vosjs/cli0.39 und später):vos record --actions actions.json --out take --dry-run. Jeder Schritt läuft gegen die echte Seite, in der Reihenfolge, weil ein späterer Selektor normalerweise erst nach einem früheren Klick existiert, aber es wird nichts erfasst und nichts geschrieben: Ein verpasster Selektor wird in Sekunden genannt (exit 2) anstatt nach einem Echtzeit-Take und dessen Encoding, und ein Take, der bereits in--outist, behält sein Footage und seinen Schnitt. Es druckt das Rechteck jedes Schritts in Capture-Px, was eine angepinnte Ebene liest. Probe erneut nach jeder Skriptbearbeitung, und zeichne nur ein Skript auf, das besteht. Ein eingeloggtes Produkt probt auf die gleiche Weise:--storage-stateund--browser-arg=gelten ebenfalls dafür, und die Probe endet damit, aufzulisten, was der Frame ENTHÜLLT (eine Adresse, einen Schlüssel, eine Karte). Verstecke diese mitmask, bevor du aufzeichnest:references/sessions.md. -
Aufnehmen:
vos record --actions actions.json --out take --strict --json--strictimmer: übersprungener Selektor / Netzwerk-Idle-Timeout → exit 2 mitskipped[]im done-Event. Ein Überspringen bedeutet der Fluss ist defekt – behebe ihn, liefere niemals drum herum aus. Der Take encodiert und plant automatisch. (vos create --actions actions.json out.webm --strictist das Einmal-Aufnahme+Rendern Verb – in Ordnung für einen schnellen ersten Durchlauf, aber DIESE Fähigkeitsschleife überprüft Frames vor dem Rendern, also bevorzuge hier die separaten Verben.) -
Optimiere
doc.json(JSON Schema wird im@vosjs/clinpm Paket ausgeliefert:schema/doc.schema.json):zoom:[{in, out, level, cx, cy, source}], QUELL-Sekunden; Stufen 1.4–2.8;cx/cyNORMALISIERT [0..1] (0.5,0.5 = Mitte) – NICHT Pixel; setze"source": "manual"auf Berührungsbereiche (überlebt Neuplanung).segments(Trimms) ·speed(rate0.1–16) ·frame.*·cursor.tilt:[{in, out, rx, ry, source}], QUELL-Sekunden – die 3D-Karte neigt sich zur Pose, solange sie aktiv ist, kehrt dazwischen in die Ruheposition zurück. GRAD (±5..18 liest sich hochwertig): +rx = die Oberkante näher, +ry = die linke Kante näher (Neigung zu einem Fokus auf der rechten Seite = negativesry). Bereiche ≥ 0.8s; paare mit Zoom-Momenten (gleicher in/out verkettet die Bewegungen), ein Posenwechsel pro ~5s Takt."source": "manual"auf Bereiche, die du berührst;tiltStyle: "subtle"|"medium"|"strong"protokolliert den automatischen Zauberstab.frame.backgroundMedia: eine Videoschleife / ein Bild hinter der Karte –{"kind":"video","key":"/bg.webm","duration":10,"dim":0.2}.key= eine Datei, die in das Take-Verzeichnis gelegt wurde ("/bg.webm") oder eine Media-URL; Video benötigtduration(AUSGABE-verankerte Modulo-Schleife);dim0..1 Scheinwerfer. Atmosphäre, kein Motiv – dimme es hinter dichter UI.audio: AUSGABE-verankerte Clips;keykann eine Datei sein, die in das Take-Verzeichnis gelegt wurde ("/music.mp3"); Lautstärke/Überblendungen/Schleife. Auf Voll-Rendern gemuxt (Opus/AAC);--rangebleibt stumm; erzwingt single-flight.- export:
{"resolution": "720p|1080p|2k|4k", "fps": 30}– niemals über dem Footage (Validierung warnt). Dannvos validate take --json– Lints müssen bestehen.
-
Schau, bevor du renderst (die taste.md-Qualitätsschleife):
vos frames take --at-zooms --times 0,25%,50%,75%,100% --json→ bewerte jedes Standbild gegen taste.md, Zoom-Apexes am strengsten.- Iteriere: editiere doc.json →
vos render take check.webm --range a..b --draft(Sekunden, halbe Auflösung – versende niemals Entwürfe) → rahme die geänderte Region neu ein. - Probierst du eine Präsentation aus? Verwende ein Flag, kein Scratch-Skript.
render/framesakzeptiert doc-Override –--set <pfad>=<wert>(wiederholbar; JSON-oder-String),--frame <macos|windows|minimal|none>(rendern),--background <url>– die das doc im Speicher patchen (doc.json unberührt) und lint-beschränkt sind. Alsovos frames take --frame 2.0 --set frame.browserBar.kind=mac-light --set tilt[0].rx=8zeigt eine gerahmte, geneigte Karte in der Vorschau, ohne die Datei zu berühren.
-
Endgültiges Rendern:
vos render take out.webm --json(oder--format mp4). Rahme den Endframe neu ein (frames --at-zooms) gegen taste.md, bevor du ihn für fertig erklärst. Renderings sind deterministisch – nur deine Änderungen verändern die Ausgabe.
6b. Menschliche Überprüfungsrunde (wenn die Anfrage eine beinhaltet): vos open take
serviert den Take im Studio – deine doc.json-Bearbeitungen kommen intakt an und