@vosjs/cli 0.78.0 → 0.79.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -12
- package/dist/{assetManifest-S6H5F2VE.js → assetManifest-U25FJER6.js} +3 -3
- package/dist/{chunk-ZSJEISKL.js → chunk-4ZUHEXUX.js} +9 -4
- package/dist/chunk-4ZUHEXUX.js.map +1 -0
- package/dist/{chunk-B4U4URE3.js → chunk-J3KHIZCH.js} +466 -412
- package/dist/chunk-J3KHIZCH.js.map +1 -0
- package/dist/{chunk-RUDDBHQ7.js → chunk-OWYOJB7K.js} +5 -1
- package/dist/chunk-OWYOJB7K.js.map +1 -0
- package/dist/{chunk-INRUYQ6J.js → chunk-TWAP4BKR.js} +2299 -1412
- package/dist/chunk-TWAP4BKR.js.map +1 -0
- package/dist/{chunk-JYCW4EOA.js → chunk-Z6TLHRFQ.js} +2 -2
- package/dist/cli.js +9 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +13 -4
- package/dist/index.js +5 -5
- package/dist/manifest-VTOIEUHJ.js +8 -0
- package/dist/{run-TIFM3ZHI.js → run-UKWKAGDK.js} +5 -5
- package/dist/{shotList-SCA234OS.js → shotList-EE3S5LVF.js} +34 -7
- package/dist/shotList-EE3S5LVF.js.map +1 -0
- package/package.json +3 -3
- package/schema/actions.schema.json +298 -13
- package/schema/doc.schema.json +37 -1
- package/skills/VERSION +1 -1
- package/skills/launch-kit/SKILL.md +6 -1
- package/skills/product-video/SKILL.md +7 -0
- package/skills/vos-authoring/SKILL.md +8 -1
- package/skills/vos-create/SKILL.md +5 -0
- package/skills/vos-cut/SKILL.md +8 -0
- package/skills/vos-footage/SKILL.md +4 -0
- package/skills/vos-migrate/SKILL.md +4 -0
- package/dist/chunk-B4U4URE3.js.map +0 -1
- package/dist/chunk-INRUYQ6J.js.map +0 -1
- package/dist/chunk-RUDDBHQ7.js.map +0 -1
- package/dist/chunk-ZSJEISKL.js.map +0 -1
- package/dist/manifest-6DJSPKVH.js +0 -8
- package/dist/shotList-SCA234OS.js.map +0 -1
- /package/dist/{assetManifest-S6H5F2VE.js.map → assetManifest-U25FJER6.js.map} +0 -0
- /package/dist/{chunk-JYCW4EOA.js.map → chunk-Z6TLHRFQ.js.map} +0 -0
- /package/dist/{manifest-6DJSPKVH.js.map → manifest-VTOIEUHJ.js.map} +0 -0
- /package/dist/{run-TIFM3ZHI.js.map → run-UKWKAGDK.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin/shotList.ts"],"sourcesContent":["/**\n * The shot list: an `actions.json` said in words, for a person who will\n * record it by hand. The last rung of the session ladder is \"the human\n * records, the agent cuts\", and an agent that falls to it should hand over\n * more than an apology: the flow it worked out, as numbered beats with the\n * pace it wanted, so its knowledge survives the hop to a human. The take\n * comes back through the ordinary handoff and the agent cuts it.\n *\n * Pure: an ActionsFile in, lines out. Selectors are said as what they name\n * where the script says (an id, a caption, a text= locator), and as the\n * selector itself where it does not: a person reading `#filter-late` can\n * find it; a person reading nothing cannot.\n */\nimport type { ActionStep, ActionsFile } from './actions'\nimport { askedMs } from './pace'\nimport { pressSequence, splitChord } from '@vosjs/render-core/record'\n\n/** A key or a chord as a person reads it: `Mod` is ⌘ on a Mac, Ctrl elsewhere. */\nexport function sayChord(chord: string): string {\n const { mods, key } = splitChord(chord)\n const word = (k: string) =>\n k === 'Mod' || k === 'ControlOrMeta'\n ? '⌘/Ctrl'\n : k === 'Control'\n ? 'Ctrl'\n : k === 'Meta'\n ? '⌘'\n : k === 'Alt'\n ? 'Alt/Option'\n : k === ' '\n ? 'Space'\n : k\n return [...mods, key].map(word).join('+')\n}\n\n/** A viewport point as a person finds it: where it is, and the script's numbers. */\nfunction sayPoint(p: { x?: number; y?: number }): string {\n const x = p.x ?? 0\n const y = p.y ?? 0\n return `the spot at ${Math.round(x)}, ${Math.round(y)} (${y < 360 ? 'upper' : 'lower'} ${x < 640 ? 'left' : 'right'})`\n}\n\n/** A list in words: `a`, `a then b`, `a, b then c`. */\nconst thenList = (xs: string[]): string =>\n xs.length <= 1\n ? (xs[0] ?? '')\n : `${xs.slice(0, -1).join(', ')} then ${xs[xs.length - 1]}`\n\nexport interface ShotBeat {\n n: number\n /** what to do, in words */\n line: string\n /** how long to hold after, in seconds, when the script asks for a hold */\n holdS?: number\n}\n\nexport interface ShotList {\n url?: string\n viewport?: { width: number; height: number }\n beats: ShotBeat[]\n /** the take's asked length, from the script's own pacing */\n aboutS: number\n setup: number\n}\n\n/** A selector as a person reads it: the text it names, or the selector itself. */\nexport function sayTarget(selector: string): string {\n const text = /^text=(['\"]?)(.+?)\\1$/.exec(selector)\n if (text) return `\"${text[2]}\"`\n const has = /:has-text\\((['\"])(.+?)\\1\\)/.exec(selector)\n if (has) return `\"${has[2]}\"`\n const role = /getByRole\\(['\"](\\w+)['\"],\\s*\\{\\s*name:\\s*(['\"])(.+?)\\2/.exec(\n selector,\n )\n if (role) return `the ${role[1]} \"${role[3]}\"`\n const label = /\\[aria-label=(['\"])(.+?)\\1\\]/.exec(selector)\n if (label) return `\"${label[2]}\"`\n const ph = /\\[placeholder=(['\"])(.+?)\\1\\]/.exec(selector)\n if (ph) return `the \"${ph[2]}\" field`\n return `\\`${selector}\\``\n}\n\nconst s1 = (ms: number) => `${Math.round(ms / 100) / 10}`.replace(/\\.0$/, '')\n\n/** A step's id, said as a name when it reads as one (`late`, `new-order`, `openBilling`). */\nexport function sayId(id: string | undefined): string | null {\n if (!id || /^\\d+$/.test(id) || id.length < 3) return null\n const words = id\n .replace(/([a-z])([A-Z])/g, '$1 $2')\n .replace(/[-_]+/g, ' ')\n .toLowerCase()\n .trim()\n return words.length >= 3 ? words : null\n}\n\n/** What a step points at, for a person: the words the selector names, else the id's, else the selector. */\nfunction sayStepTarget(step: { selector: string; id?: string }): string {\n const bySelector = sayTarget(step.selector)\n if (!bySelector.startsWith('`')) return bySelector\n const byId = sayId(step.id)\n return byId ? `${byId} (${bySelector})` : bySelector\n}\n\n/** A page condition, for a person: what they should see before going on. */\nfunction sayCondition(c: {\n selector?: string\n text?: string\n gone?: string\n enabled?: boolean\n id?: string\n}): string {\n if (c.text !== undefined) return `\"${c.text}\" shows on the page`\n if (c.gone !== undefined) return `${sayTarget(c.gone)} is gone`\n const target = sayStepTarget({ selector: c.selector ?? '', id: c.id })\n if (c.enabled === true) return `${target} is enabled`\n if (c.enabled === false) return `${target} is disabled`\n return `${target} is showing`\n}\n\nfunction beatOf(step: ActionStep): string | null {\n const line = gestureBeat(step)\n // A step that spends something real is marked where a person reads it.\n return line && step.guard\n ? `${line} (this spends something real: do it only if you mean to)`\n : line\n}\n\nfunction gestureBeat(step: ActionStep): string | null {\n const cap = step.caption\n const named = cap ? `${cap}: ` : ''\n switch (step.do) {\n case 'expect':\n return `${named}Check that ${sayCondition(step)}`\n case 'await':\n return `${named}Wait until ${sayCondition({ ...step.until, id: step.id })} (off camera: pause the recording meanwhile)`\n case 'download':\n return step.selector !== undefined\n ? `${named}Download the file ${sayStepTarget({ selector: step.selector, id: step.id })} gives${step.as ? `, as ${step.as}` : ''}`\n : `${named}Download what ${sayTarget(step.src ?? '')} plays${step.as ? `, as ${step.as}` : ''}`\n case 'wait':\n return null\n case 'hover':\n return `${named}Rest the pointer on ${step.selector !== undefined ? sayStepTarget({ selector: step.selector, id: step.id }) : sayPoint(step)}`\n case 'click': {\n const verb =\n step.count === 3\n ? 'Triple-click'\n : step.count === 2\n ? 'Double-click'\n : 'Click'\n const button =\n step.button === 'right'\n ? ' with the right button'\n : step.button === 'middle'\n ? ' with the middle button'\n : ''\n const target =\n step.selector !== undefined\n ? sayStepTarget({ selector: step.selector, id: step.id })\n : sayPoint(step)\n const holding = step.modifiers?.length\n ? `Hold ${step.modifiers.map(sayChord).join('+')} and ${verb.toLowerCase()}`\n : verb\n return `${named}${holding} ${target}${button}`\n }\n case 'type':\n return `${named}${step.replace ? 'Replace what is in' : 'Type'}${step.replace ? ` ${sayStepTarget(step)} with \"${step.text}\"` : ` \"${step.text}\" into ${sayStepTarget(step)}`}${step.at === 'end' ? ', at the end' : step.at === 'start' ? ', at the start' : ''}`\n case 'press':\n return `${named}Press ${thenList(pressSequence(step).map(sayChord))}${step.selector ? ` in ${sayStepTarget(step as { selector: string; id?: string })}` : ''}`\n case 'paste': {\n const what =\n step.text !== undefined\n ? `\"${step.text.length > 60 ? `${step.text.slice(0, 57)}...` : step.text}\"`\n : `the file ${step.file}`\n return `${named}Copy ${what}, then paste it${step.selector ? ` into ${sayStepTarget({ selector: step.selector, id: step.id })}` : ''} (⌘/Ctrl+V)`\n }\n case 'scroll':\n return `${named}Scroll ${step.dy < 0 ? 'up' : 'down'} ${step.dy < 0 ? 'a little' : 'a little'}`\n case 'move':\n return `${named}Move the pointer to the ${step.y < 360 ? 'upper' : 'lower'} ${step.x < 640 ? 'left' : 'right'}`\n case 'drag': {\n const holding = step.modifiers?.length\n ? `Hold ${step.modifiers.map(sayChord).join('+')} and `\n : ''\n const verb = holding ? 'drag' : 'Drag'\n if (step.path !== undefined)\n return `${named}${holding}${holding ? 'draw' : 'Draw'} the stroke ${typeof step.path === 'string' ? `in ${step.path}` : `of ${step.path.length} points`}, in one press`\n return `${named}${holding}${verb} ${step.selector ? sayStepTarget(step as { selector: string; id?: string }) : 'from the start point'} across`\n }\n }\n}\n\nexport function shotList(actions: ActionsFile): ShotList {\n const beats: ShotBeat[] = []\n let pendingHold = 0\n let total = 0\n for (const step of actions.steps) {\n total += askedMs(step)\n if (step.do === 'wait') {\n // A wait after a beat is that beat's hold; a leading wait is the\n // settle before the first, which a person does without being told.\n if (beats.length) {\n const last = beats[beats.length - 1]\n last.holdS = (last.holdS ?? 0) + step.ms / 1000\n } else pendingHold += step.ms\n continue\n }\n const line = beatOf(step)\n if (!line) continue\n const beat: ShotBeat = { n: beats.length + 1, line }\n if (step.do === 'hover') beat.holdS = askedMs(step) / 1000\n beats.push(beat)\n }\n void pendingHold\n return {\n url: actions.url,\n viewport: actions.viewport,\n beats,\n // gestures the recorder adds around each ask: about a second a beat\n aboutS: Math.round((total + beats.length * 1000) / 1000),\n setup: actions.setup?.length ?? 0,\n }\n}\n\n/** The list as the person reads it. */\nexport function formatShotList(list: ShotList): string {\n const out: string[] = []\n out.push(\n `Record this with the vosso extension in your own signed-in browser, about ${list.aboutS} seconds${list.viewport ? `, window about ${list.viewport.width}×${list.viewport.height}` : ''}.`,\n )\n if (list.url)\n out.push(`Start on ${list.url}, signed in, with the page settled.`)\n if (list.setup)\n out.push(\n `Before you press record: the script's ${list.setup} setup step${list.setup === 1 ? '' : 's'} (sign in, dismiss banners) are yours to do first; they are not part of the take.`,\n )\n out.push('')\n for (const b of list.beats) {\n const hold =\n b.holdS && b.holdS >= 0.4 ? ` Hold ${s1(b.holdS * 1000)} s.` : ''\n out.push(`${b.n}. ${b.line}.${hold}`)\n }\n out.push('')\n out.push(\n 'Move at a talking pace, let each screen settle before the next beat, and end on the finished state. Press the icon again to stop; the take opens in the studio and saves to your shelf.',\n )\n return out.join('\\n')\n}\n"],"mappings":";;;;;;AAeA,SAAS,eAAe,kBAAkB;AAGnC,SAAS,SAAS,OAAuB;AAC9C,QAAM,EAAE,MAAM,IAAI,IAAI,WAAW,KAAK;AACtC,QAAM,OAAO,CAAC,MACZ,MAAM,SAAS,MAAM,kBACjB,gBACA,MAAM,YACJ,SACA,MAAM,SACJ,WACA,MAAM,QACJ,eACA,MAAM,MACJ,UACA;AACd,SAAO,CAAC,GAAG,MAAM,GAAG,EAAE,IAAI,IAAI,EAAE,KAAK,GAAG;AAC1C;AAGA,SAAS,SAAS,GAAuC;AACvD,QAAM,IAAI,EAAE,KAAK;AACjB,QAAM,IAAI,EAAE,KAAK;AACjB,SAAO,eAAe,KAAK,MAAM,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,KAAK,IAAI,MAAM,UAAU,OAAO,IAAI,IAAI,MAAM,SAAS,OAAO;AACrH;AAGA,IAAM,WAAW,CAAC,OAChB,GAAG,UAAU,IACR,GAAG,CAAC,KAAK,KACV,GAAG,GAAG,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC,SAAS,GAAG,GAAG,SAAS,CAAC,CAAC;AAoBtD,SAAS,UAAU,UAA0B;AAClD,QAAM,OAAO,wBAAwB,KAAK,QAAQ;AAClD,MAAI,KAAM,QAAO,IAAI,KAAK,CAAC,CAAC;AAC5B,QAAM,MAAM,6BAA6B,KAAK,QAAQ;AACtD,MAAI,IAAK,QAAO,IAAI,IAAI,CAAC,CAAC;AAC1B,QAAM,OAAO,yDAAyD;AAAA,IACpE;AAAA,EACF;AACA,MAAI,KAAM,QAAO,OAAO,KAAK,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;AAC3C,QAAM,QAAQ,+BAA+B,KAAK,QAAQ;AAC1D,MAAI,MAAO,QAAO,IAAI,MAAM,CAAC,CAAC;AAC9B,QAAM,KAAK,gCAAgC,KAAK,QAAQ;AACxD,MAAI,GAAI,QAAO,QAAQ,GAAG,CAAC,CAAC;AAC5B,SAAO,KAAK,QAAQ;AACtB;AAEA,IAAM,KAAK,CAAC,OAAe,GAAG,KAAK,MAAM,KAAK,GAAG,IAAI,EAAE,GAAG,QAAQ,QAAQ,EAAE;AAGrE,SAAS,MAAM,IAAuC;AAC3D,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAE,KAAK,GAAG,SAAS,EAAG,QAAO;AACrD,QAAM,QAAQ,GACX,QAAQ,mBAAmB,OAAO,EAClC,QAAQ,UAAU,GAAG,EACrB,YAAY,EACZ,KAAK;AACR,SAAO,MAAM,UAAU,IAAI,QAAQ;AACrC;AAGA,SAAS,cAAc,MAAiD;AACtE,QAAM,aAAa,UAAU,KAAK,QAAQ;AAC1C,MAAI,CAAC,WAAW,WAAW,GAAG,EAAG,QAAO;AACxC,QAAM,OAAO,MAAM,KAAK,EAAE;AAC1B,SAAO,OAAO,GAAG,IAAI,KAAK,UAAU,MAAM;AAC5C;AAGA,SAAS,aAAa,GAMX;AACT,MAAI,EAAE,SAAS,OAAW,QAAO,IAAI,EAAE,IAAI;AAC3C,MAAI,EAAE,SAAS,OAAW,QAAO,GAAG,UAAU,EAAE,IAAI,CAAC;AACrD,QAAM,SAAS,cAAc,EAAE,UAAU,EAAE,YAAY,IAAI,IAAI,EAAE,GAAG,CAAC;AACrE,MAAI,EAAE,YAAY,KAAM,QAAO,GAAG,MAAM;AACxC,MAAI,EAAE,YAAY,MAAO,QAAO,GAAG,MAAM;AACzC,SAAO,GAAG,MAAM;AAClB;AAEA,SAAS,OAAO,MAAiC;AAC/C,QAAM,OAAO,YAAY,IAAI;AAE7B,SAAO,QAAQ,KAAK,QAChB,GAAG,IAAI,6DACP;AACN;AAEA,SAAS,YAAY,MAAiC;AACpD,QAAM,MAAM,KAAK;AACjB,QAAM,QAAQ,MAAM,GAAG,GAAG,OAAO;AACjC,UAAQ,KAAK,IAAI;AAAA,IACf,KAAK;AACH,aAAO,GAAG,KAAK,cAAc,aAAa,IAAI,CAAC;AAAA,IACjD,KAAK;AACH,aAAO,GAAG,KAAK,cAAc,aAAa,EAAE,GAAG,KAAK,OAAO,IAAI,KAAK,GAAG,CAAC,CAAC;AAAA,IAC3E,KAAK;AACH,aAAO,KAAK,aAAa,SACrB,GAAG,KAAK,qBAAqB,cAAc,EAAE,UAAU,KAAK,UAAU,IAAI,KAAK,GAAG,CAAC,CAAC,SAAS,KAAK,KAAK,QAAQ,KAAK,EAAE,KAAK,EAAE,KAC7H,GAAG,KAAK,iBAAiB,UAAU,KAAK,OAAO,EAAE,CAAC,SAAS,KAAK,KAAK,QAAQ,KAAK,EAAE,KAAK,EAAE;AAAA,IACjG,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,GAAG,KAAK,uBAAuB,KAAK,aAAa,SAAY,cAAc,EAAE,UAAU,KAAK,UAAU,IAAI,KAAK,GAAG,CAAC,IAAI,SAAS,IAAI,CAAC;AAAA,IAC9I,KAAK,SAAS;AACZ,YAAM,OACJ,KAAK,UAAU,IACX,iBACA,KAAK,UAAU,IACb,iBACA;AACR,YAAM,SACJ,KAAK,WAAW,UACZ,2BACA,KAAK,WAAW,WACd,4BACA;AACR,YAAM,SACJ,KAAK,aAAa,SACd,cAAc,EAAE,UAAU,KAAK,UAAU,IAAI,KAAK,GAAG,CAAC,IACtD,SAAS,IAAI;AACnB,YAAM,UAAU,KAAK,WAAW,SAC5B,QAAQ,KAAK,UAAU,IAAI,QAAQ,EAAE,KAAK,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC,KACxE;AACJ,aAAO,GAAG,KAAK,GAAG,OAAO,IAAI,MAAM,GAAG,MAAM;AAAA,IAC9C;AAAA,IACA,KAAK;AACH,aAAO,GAAG,KAAK,GAAG,KAAK,UAAU,uBAAuB,MAAM,GAAG,KAAK,UAAU,IAAI,cAAc,IAAI,CAAC,UAAU,KAAK,IAAI,MAAM,KAAK,KAAK,IAAI,UAAU,cAAc,IAAI,CAAC,EAAE,GAAG,KAAK,OAAO,QAAQ,iBAAiB,KAAK,OAAO,UAAU,mBAAmB,EAAE;AAAA,IAClQ,KAAK;AACH,aAAO,GAAG,KAAK,SAAS,SAAS,cAAc,IAAI,EAAE,IAAI,QAAQ,CAAC,CAAC,GAAG,KAAK,WAAW,OAAO,cAAc,IAAyC,CAAC,KAAK,EAAE;AAAA,IAC9J,KAAK,SAAS;AACZ,YAAM,OACJ,KAAK,SAAS,SACV,IAAI,KAAK,KAAK,SAAS,KAAK,GAAG,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC,QAAQ,KAAK,IAAI,MACtE,YAAY,KAAK,IAAI;AAC3B,aAAO,GAAG,KAAK,QAAQ,IAAI,kBAAkB,KAAK,WAAW,SAAS,cAAc,EAAE,UAAU,KAAK,UAAU,IAAI,KAAK,GAAG,CAAC,CAAC,KAAK,EAAE;AAAA,IACtI;AAAA,IACA,KAAK;AACH,aAAO,GAAG,KAAK,UAAU,KAAK,KAAK,IAAI,OAAO,MAAM,IAAI,KAAK,KAAK,IAAI,aAAa,UAAU;AAAA,IAC/F,KAAK;AACH,aAAO,GAAG,KAAK,2BAA2B,KAAK,IAAI,MAAM,UAAU,OAAO,IAAI,KAAK,IAAI,MAAM,SAAS,OAAO;AAAA,IAC/G,KAAK,QAAQ;AACX,YAAM,UAAU,KAAK,WAAW,SAC5B,QAAQ,KAAK,UAAU,IAAI,QAAQ,EAAE,KAAK,GAAG,CAAC,UAC9C;AACJ,YAAM,OAAO,UAAU,SAAS;AAChC,UAAI,KAAK,SAAS;AAChB,eAAO,GAAG,KAAK,GAAG,OAAO,GAAG,UAAU,SAAS,MAAM,eAAe,OAAO,KAAK,SAAS,WAAW,MAAM,KAAK,IAAI,KAAK,MAAM,KAAK,KAAK,MAAM,SAAS;AACzJ,aAAO,GAAG,KAAK,GAAG,OAAO,GAAG,IAAI,IAAI,KAAK,WAAW,cAAc,IAAyC,IAAI,sBAAsB;AAAA,IACvI;AAAA,EACF;AACF;AAEO,SAAS,SAAS,SAAgC;AACvD,QAAM,QAAoB,CAAC;AAC3B,MAAI,cAAc;AAClB,MAAI,QAAQ;AACZ,aAAW,QAAQ,QAAQ,OAAO;AAChC,aAAS,QAAQ,IAAI;AACrB,QAAI,KAAK,OAAO,QAAQ;AAGtB,UAAI,MAAM,QAAQ;AAChB,cAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,aAAK,SAAS,KAAK,SAAS,KAAK,KAAK,KAAK;AAAA,MAC7C,MAAO,gBAAe,KAAK;AAC3B;AAAA,IACF;AACA,UAAM,OAAO,OAAO,IAAI;AACxB,QAAI,CAAC,KAAM;AACX,UAAM,OAAiB,EAAE,GAAG,MAAM,SAAS,GAAG,KAAK;AACnD,QAAI,KAAK,OAAO,QAAS,MAAK,QAAQ,QAAQ,IAAI,IAAI;AACtD,UAAM,KAAK,IAAI;AAAA,EACjB;AACA,OAAK;AACL,SAAO;AAAA,IACL,KAAK,QAAQ;AAAA,IACb,UAAU,QAAQ;AAAA,IAClB;AAAA;AAAA,IAEA,QAAQ,KAAK,OAAO,QAAQ,MAAM,SAAS,OAAQ,GAAI;AAAA,IACvD,OAAO,QAAQ,OAAO,UAAU;AAAA,EAClC;AACF;AAGO,SAAS,eAAe,MAAwB;AACrD,QAAM,MAAgB,CAAC;AACvB,MAAI;AAAA,IACF,6EAA6E,KAAK,MAAM,WAAW,KAAK,WAAW,kBAAkB,KAAK,SAAS,KAAK,OAAI,KAAK,SAAS,MAAM,KAAK,EAAE;AAAA,EACzL;AACA,MAAI,KAAK;AACP,QAAI,KAAK,YAAY,KAAK,GAAG,qCAAqC;AACpE,MAAI,KAAK;AACP,QAAI;AAAA,MACF,yCAAyC,KAAK,KAAK,cAAc,KAAK,UAAU,IAAI,KAAK,GAAG;AAAA,IAC9F;AACF,MAAI,KAAK,EAAE;AACX,aAAW,KAAK,KAAK,OAAO;AAC1B,UAAM,OACJ,EAAE,SAAS,EAAE,SAAS,MAAM,SAAS,GAAG,EAAE,QAAQ,GAAI,CAAC,QAAQ;AACjE,QAAI,KAAK,GAAG,EAAE,CAAC,KAAK,EAAE,IAAI,IAAI,IAAI,EAAE;AAAA,EACtC;AACA,MAAI,KAAK,EAAE;AACX,MAAI;AAAA,IACF;AAAA,EACF;AACA,SAAO,IAAI,KAAK,IAAI;AACtB;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vosjs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.79.0",
|
|
4
4
|
"description": "The vos CLI: record the real product from a scripted browser flow, auto-zoom from the cursor track, cut as data in doc.json, render deterministic video, deliver a release's video cuts per destination spec, and sync with vos.so. One binary, every verb, MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"playwright": "^1.49.0",
|
|
54
54
|
"@vosjs/core": "^0.31.0",
|
|
55
55
|
"@vosjs/editor": "^1.6.0",
|
|
56
|
+
"@vosjs/render-core": "^0.8.0",
|
|
57
|
+
"@vosjs/studio-core": "^0.44.0",
|
|
56
58
|
"@vosjs/elements": "^0.12.1",
|
|
57
|
-
"@vosjs/render-core": "^0.7.0",
|
|
58
59
|
"@vosjs/shared": "^0.7.0",
|
|
59
|
-
"@vosjs/studio-core": "^0.43.0",
|
|
60
60
|
"@vosjs/timeline": "^0.5.0",
|
|
61
61
|
"@vosjs/tween": "^0.8.3"
|
|
62
62
|
},
|
|
@@ -80,6 +80,12 @@
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
|
+
"ready": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"enum": ["dom", "networkidle"],
|
|
86
|
+
"default": "dom",
|
|
87
|
+
"description": "When the first page counts as ready for step 1. dom: after load, once the page has made no DOM change for 800 ms, at most 8 s. networkidle: once the network has gone quiet, at most 45 s, which a page holding a socket open or an analytics beacon never does. The same mode opens a setup goto, the page after a setup, and an await's reload. `--ready` overrides. The done event's `ready` says how the first load settled ({ mode, ms, capped }); `navTimeout` is true only when networkidle was asked for and never came."
|
|
88
|
+
},
|
|
83
89
|
"mask": {
|
|
84
90
|
"type": "array",
|
|
85
91
|
"description": "Hidden BEFORE the first frame is captured and kept hidden across navigations and re-renders: what a signed-in account shows that must not ship. The real value is never in a frame, never in the recording, never pushed. `vos record` reports what it still sees afterwards (meta.exposures), and a mask whose selector reached nothing fails --strict.",
|
|
@@ -104,6 +110,11 @@
|
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
112
|
},
|
|
113
|
+
"settle": {
|
|
114
|
+
"type": "boolean",
|
|
115
|
+
"default": false,
|
|
116
|
+
"description": "every gesture step waits on the app after it (a step's own settle overrides): a canvas that falls behind under load finishes the last stroke before the next one starts"
|
|
117
|
+
},
|
|
107
118
|
"steps": {
|
|
108
119
|
"type": "array",
|
|
109
120
|
"minItems": 1,
|
|
@@ -140,14 +151,24 @@
|
|
|
140
151
|
},
|
|
141
152
|
{
|
|
142
153
|
"type": "object",
|
|
143
|
-
"required": ["do"
|
|
154
|
+
"required": ["do"],
|
|
144
155
|
"additionalProperties": false,
|
|
145
156
|
"properties": {
|
|
146
157
|
"do": {
|
|
147
158
|
"const": "hover"
|
|
148
159
|
},
|
|
149
160
|
"selector": {
|
|
150
|
-
"type": "string"
|
|
161
|
+
"type": "string",
|
|
162
|
+
"description": "the element whose centre the pointer goes to; or x and y instead"
|
|
163
|
+
},
|
|
164
|
+
"x": {
|
|
165
|
+
"type": "number",
|
|
166
|
+
"minimum": 0,
|
|
167
|
+
"description": "with y: a viewport point in CSS px, for a spot no selector names (a place on a canvas); takes no selector"
|
|
168
|
+
},
|
|
169
|
+
"y": {
|
|
170
|
+
"type": "number",
|
|
171
|
+
"minimum": 0
|
|
151
172
|
},
|
|
152
173
|
"ms": {
|
|
153
174
|
"type": "number",
|
|
@@ -155,6 +176,10 @@
|
|
|
155
176
|
"default": 700,
|
|
156
177
|
"description": "Dwell — parked cursor becomes a zoom signal."
|
|
157
178
|
},
|
|
179
|
+
"settle": {
|
|
180
|
+
"type": "boolean",
|
|
181
|
+
"description": "after the gesture, wait until the page has made no DOM change and painted no new frame for 300 ms (at most 4 s), so an app that fell behind catches up before the next step; meta.steps[].settleMs says how long it waited. Absent: the script's top-level settle"
|
|
182
|
+
},
|
|
158
183
|
"id": {
|
|
159
184
|
"type": "string",
|
|
160
185
|
"minLength": 1,
|
|
@@ -170,11 +195,33 @@
|
|
|
170
195
|
"minLength": 1,
|
|
171
196
|
"description": "a selector whose words the recorder keeps once the step has settled (meta.steps[].text, trimmed, at most 2 KB): the take's own payload, which a layer quotes instead of a replay; on a click, what the click opened"
|
|
172
197
|
}
|
|
173
|
-
}
|
|
198
|
+
},
|
|
199
|
+
"oneOf": [
|
|
200
|
+
{
|
|
201
|
+
"required": ["selector"],
|
|
202
|
+
"not": {
|
|
203
|
+
"anyOf": [
|
|
204
|
+
{
|
|
205
|
+
"required": ["x"]
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"required": ["y"]
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"required": ["x", "y"],
|
|
215
|
+
"not": {
|
|
216
|
+
"required": ["selector"]
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
],
|
|
220
|
+
"description": "Rest the pointer on an element's centre (selector) or on a viewport point (x, y); ms is the dwell, a zoom signal."
|
|
174
221
|
},
|
|
175
222
|
{
|
|
176
223
|
"type": "object",
|
|
177
|
-
"required": ["do"
|
|
224
|
+
"required": ["do"],
|
|
178
225
|
"additionalProperties": false,
|
|
179
226
|
"properties": {
|
|
180
227
|
"do": {
|
|
@@ -186,7 +233,42 @@
|
|
|
186
233
|
"description": "the read after the press, ms (default 150), held from the page's last visual change: the recorder watches the screencast and pays the settle itself (a change within 400 ms says the page is answering, 250 ms of quiet says it settled, 1.2 s bounds a page that never stops), so the same ms reads the same on every run. Size it as a reading beat, about 1000 after a navigation and 600 after a control, never padded for a slow page."
|
|
187
234
|
},
|
|
188
235
|
"selector": {
|
|
189
|
-
"type": "string"
|
|
236
|
+
"type": "string",
|
|
237
|
+
"description": "the element whose centre the pointer goes to; or x and y instead"
|
|
238
|
+
},
|
|
239
|
+
"x": {
|
|
240
|
+
"type": "number",
|
|
241
|
+
"minimum": 0,
|
|
242
|
+
"description": "with y: a viewport point in CSS px, for a spot no selector names (a place on a canvas); takes no selector"
|
|
243
|
+
},
|
|
244
|
+
"y": {
|
|
245
|
+
"type": "number",
|
|
246
|
+
"minimum": 0
|
|
247
|
+
},
|
|
248
|
+
"count": {
|
|
249
|
+
"type": "integer",
|
|
250
|
+
"enum": [1, 2, 3],
|
|
251
|
+
"default": 1,
|
|
252
|
+
"description": "2 is a real double click, 3 a triple click: that many presses inside the platform's double-click interval, each carrying its click count; every press is a down and an up on the cursor track"
|
|
253
|
+
},
|
|
254
|
+
"button": {
|
|
255
|
+
"type": "string",
|
|
256
|
+
"enum": ["left", "right", "middle"],
|
|
257
|
+
"default": "left",
|
|
258
|
+
"description": "the button pressed: right opens a context menu"
|
|
259
|
+
},
|
|
260
|
+
"modifiers": {
|
|
261
|
+
"type": "array",
|
|
262
|
+
"uniqueItems": true,
|
|
263
|
+
"items": {
|
|
264
|
+
"type": "string",
|
|
265
|
+
"enum": ["Alt", "Shift", "Meta", "Control", "Mod"]
|
|
266
|
+
},
|
|
267
|
+
"description": "held for the press (a Shift-click adds to the selection) and released after it, whatever happens; Mod is Meta on macOS, Control elsewhere"
|
|
268
|
+
},
|
|
269
|
+
"settle": {
|
|
270
|
+
"type": "boolean",
|
|
271
|
+
"description": "after the gesture, wait until the page has made no DOM change and painted no new frame for 300 ms (at most 4 s), so an app that fell behind catches up before the next step; meta.steps[].settleMs says how long it waited. Absent: the script's top-level settle"
|
|
190
272
|
},
|
|
191
273
|
"id": {
|
|
192
274
|
"type": "string",
|
|
@@ -208,7 +290,28 @@
|
|
|
208
290
|
"enum": ["spend"],
|
|
209
291
|
"description": "the step costs something real (credits, a purchase, a message sent): `vos record --dry-run` hovers it and never presses it, and `vos record` presses it only with `--confirm <id>`, refusing before any capture otherwise. Needs an id. A hosted take refuses a guarded step"
|
|
210
292
|
}
|
|
211
|
-
}
|
|
293
|
+
},
|
|
294
|
+
"oneOf": [
|
|
295
|
+
{
|
|
296
|
+
"required": ["selector"],
|
|
297
|
+
"not": {
|
|
298
|
+
"anyOf": [
|
|
299
|
+
{
|
|
300
|
+
"required": ["x"]
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"required": ["y"]
|
|
304
|
+
}
|
|
305
|
+
]
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"required": ["x", "y"],
|
|
310
|
+
"not": {
|
|
311
|
+
"required": ["selector"]
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
]
|
|
212
315
|
},
|
|
213
316
|
{
|
|
214
317
|
"type": "object",
|
|
@@ -240,6 +343,10 @@
|
|
|
240
343
|
"default": true,
|
|
241
344
|
"description": "The recorder clicks the field before typing, and that click is what opens the typing zoom on it. false types into the field as it is already focused: for a keystroke that finishes earlier typing (a submitting Enter) rather than starting it, where a second click rings a click effect on empty space beside the text."
|
|
242
345
|
},
|
|
346
|
+
"settle": {
|
|
347
|
+
"type": "boolean",
|
|
348
|
+
"description": "after the gesture, wait until the page has made no DOM change and painted no new frame for 300 ms (at most 4 s), so an app that fell behind catches up before the next step; meta.steps[].settleMs says how long it waited. Absent: the script's top-level settle"
|
|
349
|
+
},
|
|
243
350
|
"id": {
|
|
244
351
|
"type": "string",
|
|
245
352
|
"minLength": 1,
|
|
@@ -274,9 +381,9 @@
|
|
|
274
381
|
},
|
|
275
382
|
{
|
|
276
383
|
"type": "object",
|
|
277
|
-
"required": ["do"
|
|
384
|
+
"required": ["do"],
|
|
278
385
|
"additionalProperties": false,
|
|
279
|
-
"description": "
|
|
386
|
+
"description": "A key, a chord or a sequence, each recorded as a typing ping where the pointer is (or on selector when given, with no click), never as the key itself. key names one control key (Escape closes what a click opened, Enter submits what was typed); keys names a chord (\"Shift+D\", \"Mod+Z\", Mod being Meta on macOS and Control elsewhere), a plain key (\"r\", a tool's shortcut) or a sequence ([\"v\", \"Escape\"]), 150 ms apart. Words are typed with type.",
|
|
280
387
|
"properties": {
|
|
281
388
|
"do": {
|
|
282
389
|
"const": "press"
|
|
@@ -300,6 +407,24 @@
|
|
|
300
407
|
"Space"
|
|
301
408
|
]
|
|
302
409
|
},
|
|
410
|
+
"keys": {
|
|
411
|
+
"oneOf": [
|
|
412
|
+
{
|
|
413
|
+
"type": "string",
|
|
414
|
+
"minLength": 1
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
"type": "array",
|
|
418
|
+
"minItems": 1,
|
|
419
|
+
"maxItems": 50,
|
|
420
|
+
"items": {
|
|
421
|
+
"type": "string",
|
|
422
|
+
"minLength": 1
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
],
|
|
426
|
+
"description": "a key name or chord (modifiers Shift, Control, Alt, Meta, Mod, ControlOrMeta joined with + before the key; a key is a single character, a named key like Escape or ArrowLeft, F1 to F24, or a code like KeyA or Digit1), or a list of them pressed in order"
|
|
427
|
+
},
|
|
303
428
|
"selector": {
|
|
304
429
|
"type": "string",
|
|
305
430
|
"description": "the element the key is about: the ping and the step's rect are its, with no click"
|
|
@@ -309,6 +434,10 @@
|
|
|
309
434
|
"minimum": 0,
|
|
310
435
|
"description": "the read after the key, ms (default 150), held from the page's last visual change like a click's"
|
|
311
436
|
},
|
|
437
|
+
"settle": {
|
|
438
|
+
"type": "boolean",
|
|
439
|
+
"description": "after the gesture, wait until the page has made no DOM change and painted no new frame for 300 ms (at most 4 s), so an app that fell behind catches up before the next step; meta.steps[].settleMs says how long it waited. Absent: the script's top-level settle"
|
|
440
|
+
},
|
|
312
441
|
"id": {
|
|
313
442
|
"type": "string",
|
|
314
443
|
"minLength": 1,
|
|
@@ -329,7 +458,15 @@
|
|
|
329
458
|
"enum": ["spend"],
|
|
330
459
|
"description": "the step costs something real (credits, a purchase, a message sent): `vos record --dry-run` hovers it and never presses it, and `vos record` presses it only with `--confirm <id>`, refusing before any capture otherwise. Needs an id. A hosted take refuses a guarded step"
|
|
331
460
|
}
|
|
332
|
-
}
|
|
461
|
+
},
|
|
462
|
+
"oneOf": [
|
|
463
|
+
{
|
|
464
|
+
"required": ["key"]
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
"required": ["keys"]
|
|
468
|
+
}
|
|
469
|
+
]
|
|
333
470
|
},
|
|
334
471
|
{
|
|
335
472
|
"type": "object",
|
|
@@ -347,6 +484,10 @@
|
|
|
347
484
|
"dy": {
|
|
348
485
|
"type": "number"
|
|
349
486
|
},
|
|
487
|
+
"settle": {
|
|
488
|
+
"type": "boolean",
|
|
489
|
+
"description": "after the gesture, wait until the page has made no DOM change and painted no new frame for 300 ms (at most 4 s), so an app that fell behind catches up before the next step; meta.steps[].settleMs says how long it waited. Absent: the script's top-level settle"
|
|
490
|
+
},
|
|
350
491
|
"id": {
|
|
351
492
|
"type": "string",
|
|
352
493
|
"minLength": 1,
|
|
@@ -380,6 +521,10 @@
|
|
|
380
521
|
"type": "number",
|
|
381
522
|
"minimum": 0
|
|
382
523
|
},
|
|
524
|
+
"settle": {
|
|
525
|
+
"type": "boolean",
|
|
526
|
+
"description": "after the gesture, wait until the page has made no DOM change and painted no new frame for 300 ms (at most 4 s), so an app that fell behind catches up before the next step; meta.steps[].settleMs says how long it waited. Absent: the script's top-level settle"
|
|
527
|
+
},
|
|
383
528
|
"id": {
|
|
384
529
|
"type": "string",
|
|
385
530
|
"minLength": 1,
|
|
@@ -399,9 +544,9 @@
|
|
|
399
544
|
},
|
|
400
545
|
{
|
|
401
546
|
"type": "object",
|
|
402
|
-
"required": ["do"
|
|
547
|
+
"required": ["do"],
|
|
403
548
|
"additionalProperties": false,
|
|
404
|
-
"description": "Press-move-release: drag an element, slide a range input, move a clip. Start = selector center when given, else (x, y); end = (tx, ty), eased over ms.",
|
|
549
|
+
"description": "Press-move-release: drag an element, slide a range input, move a clip. Start = selector center when given, else (x, y); end = (tx, ty), eased over ms. With path, the drag DRAWS: the press at the path's first point, a move at every later point in order (never dropped, so a page that answers slowly makes the stroke late, reported as meta.steps[].overrunMs, never a different stroke), the release at its last. modifiers are held from the press to the release.",
|
|
405
550
|
"properties": {
|
|
406
551
|
"do": {
|
|
407
552
|
"const": "drag"
|
|
@@ -425,10 +570,42 @@
|
|
|
425
570
|
"type": "number",
|
|
426
571
|
"minimum": 0
|
|
427
572
|
},
|
|
573
|
+
"path": {
|
|
574
|
+
"oneOf": [
|
|
575
|
+
{
|
|
576
|
+
"type": "array",
|
|
577
|
+
"minItems": 2,
|
|
578
|
+
"maxItems": 20000,
|
|
579
|
+
"items": {
|
|
580
|
+
"$ref": "#/$defs/pathPoint"
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
"type": "string",
|
|
585
|
+
"pattern": "^(?!\\.)(?!.*\\.\\.)[^/\\\\]+$",
|
|
586
|
+
"description": "a JSON file beside the actions file (a plain name) holding the points, or { \"points\": [...] }"
|
|
587
|
+
}
|
|
588
|
+
],
|
|
589
|
+
"description": "viewport points to draw through, replayed by their own times (tMs from the first point) or, untimed, evenly over ms. A point that carries pressure draws the whole path with a pen (pointer events with that pressure); without, the mouse draws it. A path takes no selector, x, y, tx or ty"
|
|
590
|
+
},
|
|
591
|
+
"modifiers": {
|
|
592
|
+
"type": "array",
|
|
593
|
+
"uniqueItems": true,
|
|
594
|
+
"items": {
|
|
595
|
+
"type": "string",
|
|
596
|
+
"enum": ["Alt", "Shift", "Meta", "Control", "Mod"]
|
|
597
|
+
},
|
|
598
|
+
"description": "held from the press to the release (Alt-drag to duplicate, Shift to constrain); Mod is Meta on macOS, Control elsewhere"
|
|
599
|
+
},
|
|
428
600
|
"ms": {
|
|
429
601
|
"type": "number",
|
|
430
602
|
"minimum": 0,
|
|
431
|
-
"default": 700
|
|
603
|
+
"default": 700,
|
|
604
|
+
"description": "the travel of a straight drag, or of an untimed path (default 700)"
|
|
605
|
+
},
|
|
606
|
+
"settle": {
|
|
607
|
+
"type": "boolean",
|
|
608
|
+
"description": "after the gesture, wait until the page has made no DOM change and painted no new frame for 300 ms (at most 4 s), so an app that fell behind catches up before the next step; meta.steps[].settleMs says how long it waited. Absent: the script's top-level settle"
|
|
432
609
|
},
|
|
433
610
|
"id": {
|
|
434
611
|
"type": "string",
|
|
@@ -445,7 +622,18 @@
|
|
|
445
622
|
"minLength": 1,
|
|
446
623
|
"description": "a selector whose words the recorder keeps once the step has settled (meta.steps[].text, trimmed, at most 2 KB): the take's own payload, which a layer quotes instead of a replay; on a click, what the click opened"
|
|
447
624
|
}
|
|
448
|
-
}
|
|
625
|
+
},
|
|
626
|
+
"oneOf": [
|
|
627
|
+
{
|
|
628
|
+
"required": ["tx", "ty"],
|
|
629
|
+
"not": {
|
|
630
|
+
"required": ["path"]
|
|
631
|
+
}
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
"required": ["path"]
|
|
635
|
+
}
|
|
636
|
+
]
|
|
449
637
|
},
|
|
450
638
|
{
|
|
451
639
|
"type": "object",
|
|
@@ -623,9 +811,106 @@
|
|
|
623
811
|
"description": "a selector whose words the recorder keeps once the step has settled (meta.steps[].text, trimmed, at most 2 KB): the take's own payload, which a layer quotes instead of a replay; on a click, what the click opened"
|
|
624
812
|
}
|
|
625
813
|
}
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
"type": "object",
|
|
817
|
+
"required": ["do"],
|
|
818
|
+
"additionalProperties": false,
|
|
819
|
+
"description": "Put words or a file on the clipboard and paste it the way a person's ⌘V does: the browser's own clipboard and a trusted paste chord, so the page gets a trusted keydown and paste event and navigator.clipboard.read() sees the item; when the browser refuses the item, a paste event dispatched in the page (untrusted, said in the log). Into selector (clicked first) or where the focus is. Recorded as a typing ping, never its contents. A hosted take refuses a paste.",
|
|
820
|
+
"oneOf": [
|
|
821
|
+
{
|
|
822
|
+
"required": ["text"]
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
"required": ["file"]
|
|
826
|
+
}
|
|
827
|
+
],
|
|
828
|
+
"properties": {
|
|
829
|
+
"do": {
|
|
830
|
+
"const": "paste"
|
|
831
|
+
},
|
|
832
|
+
"selector": {
|
|
833
|
+
"type": "string",
|
|
834
|
+
"minLength": 1,
|
|
835
|
+
"description": "the element to paste into, clicked first"
|
|
836
|
+
},
|
|
837
|
+
"text": {
|
|
838
|
+
"type": "string",
|
|
839
|
+
"description": "the words to paste"
|
|
840
|
+
},
|
|
841
|
+
"file": {
|
|
842
|
+
"type": "string",
|
|
843
|
+
"pattern": "^(?!\\.)(?!.*\\.\\.)[^/\\\\]+\\.(png|jpe?g|webp|gif|svg|txt|md|html?|json|csv)$",
|
|
844
|
+
"description": "a file beside the actions file, by a plain name, at most 10 MB: an image (a picture other than PNG is pasted as PNG), an SVG (also as its markup), or text"
|
|
845
|
+
},
|
|
846
|
+
"ms": {
|
|
847
|
+
"type": "number",
|
|
848
|
+
"minimum": 0,
|
|
849
|
+
"description": "the read after the paste, ms (default 150), held from the page's last visual change like a click's"
|
|
850
|
+
},
|
|
851
|
+
"settle": {
|
|
852
|
+
"type": "boolean",
|
|
853
|
+
"description": "after the gesture, wait until the page has made no DOM change and painted no new frame for 300 ms (at most 4 s), so an app that fell behind catches up before the next step; meta.steps[].settleMs says how long it waited. Absent: the script's top-level settle"
|
|
854
|
+
},
|
|
855
|
+
"id": {
|
|
856
|
+
"type": "string",
|
|
857
|
+
"minLength": 1,
|
|
858
|
+
"description": "Optional stable identity: doc.json anchors name a step by this id (else by index), so a step survives moving or gaining neighbours across script edits. Unique across steps when present."
|
|
859
|
+
},
|
|
860
|
+
"caption": {
|
|
861
|
+
"type": "string",
|
|
862
|
+
"maxLength": 80,
|
|
863
|
+
"description": "a beat's caption: vos deliver lands it as a lower-third at this step's moment on cuts that take words (feed cuts, the demo), never on a loop"
|
|
864
|
+
},
|
|
865
|
+
"read": {
|
|
866
|
+
"type": "string",
|
|
867
|
+
"minLength": 1,
|
|
868
|
+
"description": "a selector whose words the recorder keeps once the step has settled (meta.steps[].text, trimmed, at most 2 KB): the take's own payload, which a layer quotes instead of a replay; on a click, what the click opened"
|
|
869
|
+
}
|
|
870
|
+
}
|
|
626
871
|
}
|
|
627
872
|
]
|
|
628
873
|
}
|
|
629
874
|
}
|
|
875
|
+
},
|
|
876
|
+
"$defs": {
|
|
877
|
+
"pathPoint": {
|
|
878
|
+
"oneOf": [
|
|
879
|
+
{
|
|
880
|
+
"type": "array",
|
|
881
|
+
"minItems": 2,
|
|
882
|
+
"maxItems": 4,
|
|
883
|
+
"items": {
|
|
884
|
+
"type": "number"
|
|
885
|
+
},
|
|
886
|
+
"description": "[x, y], [x, y, tMs] or [x, y, tMs, pressure]: every point timed or none"
|
|
887
|
+
},
|
|
888
|
+
{
|
|
889
|
+
"type": "object",
|
|
890
|
+
"required": ["x", "y"],
|
|
891
|
+
"additionalProperties": false,
|
|
892
|
+
"properties": {
|
|
893
|
+
"x": {
|
|
894
|
+
"type": "number",
|
|
895
|
+
"minimum": 0
|
|
896
|
+
},
|
|
897
|
+
"y": {
|
|
898
|
+
"type": "number",
|
|
899
|
+
"minimum": 0
|
|
900
|
+
},
|
|
901
|
+
"t": {
|
|
902
|
+
"type": "number",
|
|
903
|
+
"minimum": 0,
|
|
904
|
+
"description": "ms"
|
|
905
|
+
},
|
|
906
|
+
"pressure": {
|
|
907
|
+
"type": "number",
|
|
908
|
+
"minimum": 0,
|
|
909
|
+
"maximum": 1
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
]
|
|
914
|
+
}
|
|
630
915
|
}
|
|
631
916
|
}
|
package/schema/doc.schema.json
CHANGED
|
@@ -1589,7 +1589,38 @@
|
|
|
1589
1589
|
"gain": {
|
|
1590
1590
|
"type": "number",
|
|
1591
1591
|
"minimum": 0,
|
|
1592
|
-
"maximum": 1
|
|
1592
|
+
"maximum": 1,
|
|
1593
|
+
"description": "linear gain; with a measured `normalize`, a trim on the target"
|
|
1594
|
+
},
|
|
1595
|
+
"normalize": {
|
|
1596
|
+
"type": "number",
|
|
1597
|
+
"minimum": -30,
|
|
1598
|
+
"maximum": -6,
|
|
1599
|
+
"description": "target integrated loudness, LUFS (e.g. -16). Applies only while `loudness.src` is this clip's `key`: the clip plays at 10^((normalize - loudness.lufs) / 20) × gain, which may exceed 1, capped so the file's sample peak stays under -1 dBFS and never lifted more than +24 dB. Unmeasured or measured for another key, it is ignored (vos validate warns: measure it with vos audio measure)"
|
|
1600
|
+
},
|
|
1601
|
+
"loudness": {
|
|
1602
|
+
"type": "object",
|
|
1603
|
+
"description": "what the file measures (BS.1770 integrated, K-weighted, gated, over the whole file), written by vos audio measure, vos score and vos join, never by hand",
|
|
1604
|
+
"required": ["lufs", "peakDb", "at", "src"],
|
|
1605
|
+
"properties": {
|
|
1606
|
+
"lufs": {
|
|
1607
|
+
"type": "number",
|
|
1608
|
+
"description": "integrated loudness, LUFS"
|
|
1609
|
+
},
|
|
1610
|
+
"peakDb": {
|
|
1611
|
+
"type": ["number", "null"],
|
|
1612
|
+
"description": "sample peak, dBFS (not oversampled)"
|
|
1613
|
+
},
|
|
1614
|
+
"at": {
|
|
1615
|
+
"type": "string",
|
|
1616
|
+
"description": "when it was measured (ISO date)"
|
|
1617
|
+
},
|
|
1618
|
+
"src": {
|
|
1619
|
+
"type": "string",
|
|
1620
|
+
"description": "the key it was measured for"
|
|
1621
|
+
}
|
|
1622
|
+
},
|
|
1623
|
+
"additionalProperties": false
|
|
1593
1624
|
},
|
|
1594
1625
|
"fadeIn": {
|
|
1595
1626
|
"type": "number",
|
|
@@ -1609,6 +1640,11 @@
|
|
|
1609
1640
|
"type": "boolean",
|
|
1610
1641
|
"description": "duck under the mic while speech is detected"
|
|
1611
1642
|
},
|
|
1643
|
+
"source": {
|
|
1644
|
+
"type": "string",
|
|
1645
|
+
"enum": ["page"],
|
|
1646
|
+
"description": "page: sound the recorded page itself played (a video or audio element heard during the take, meta.media), placed by the plan at the output time it was heard and keyed to the file fetched into page-sound/. A re-plan replaces only these; absent = a clip someone added"
|
|
1647
|
+
},
|
|
1612
1648
|
"media": {
|
|
1613
1649
|
"type": "string",
|
|
1614
1650
|
"description": "the media this belongs to: a `media[].id`; absent = the primary, `source`"
|
package/skills/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.25.0
|
|
@@ -180,7 +180,10 @@ not re-run `validate` after a push unless the human asks.
|
|
|
180
180
|
|
|
181
181
|
## 6. Push the release
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
Set `still` in `doc.json` to the payoff first (OUTPUT seconds): the
|
|
184
|
+
result the release is about, never the opening. Without one the cover is
|
|
185
|
+
derived near the start, usually a zoom on a navigation control under the
|
|
186
|
+
first caption. Then push the take labelled for the release:
|
|
184
187
|
|
|
185
188
|
```
|
|
186
189
|
vos push take --folder <project-slug> --label "v2.1 launch" --note "<what shipped, one line>" \
|
|
@@ -224,6 +227,8 @@ by data. Never spread before the seed is signed off.
|
|
|
224
227
|
anything records. Cost a full re-record once.
|
|
225
228
|
- A template's own palette on a deliverable: resolve `BRAND.md` (or witness
|
|
226
229
|
the site) before cutting, and put it beside the take.
|
|
230
|
+
- A push with no `still`: the release's watch page and share card open on
|
|
231
|
+
a frame near the start, not on what shipped.
|
|
227
232
|
- A cold open (`firstlast`): the first frame is the marketing page's
|
|
228
233
|
headline with the product nowhere; trim to the story or let the entrance
|
|
229
234
|
carry it.
|
|
@@ -201,6 +201,11 @@ Per-channel dimensions and byte budgets: `references/destinations.md`.
|
|
|
201
201
|
serves the take into the studio — your doc.json edits arrive intact and
|
|
202
202
|
every zoom span is draggable.
|
|
203
203
|
|
|
204
|
+
6c. **Pushing it to vos.so** (`vos push take`)? Set `still` in `doc.json`
|
|
205
|
+
(OUTPUT seconds) to the payoff first, the result the video is about,
|
|
206
|
+
never the opening: without one the cover is derived near the start,
|
|
207
|
+
usually a zoom on a navigation control under the first caption.
|
|
208
|
+
|
|
204
209
|
7. **Package for the destination**: `references/destinations.md`.
|
|
205
210
|
|
|
206
211
|
## Launch kit (one take → every cut of a release)
|
|
@@ -251,6 +256,8 @@ when the ask is a release, not one video.
|
|
|
251
256
|
frame the text, and open the span after the click.
|
|
252
257
|
- Routing the cursor through a hover-triggered menu between beats.
|
|
253
258
|
- A first frame or a last frame that cannot stand alone as a poster.
|
|
259
|
+
- A push with no `still`: the shelf, the watch page and the share card
|
|
260
|
+
show a frame near the start instead of the result.
|
|
254
261
|
- A frozen opening: a static landing page records as freeze-then-bang;
|
|
255
262
|
trim it or speed it, the story opens near the money shot.
|
|
256
263
|
- A sign-in in `steps`: every step there is in the footage and a typed
|
|
@@ -57,10 +57,15 @@ the first line of `~/.config/vos/credentials`. With one, push:
|
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
vos push my-config.json --title "…" --desc "one line" \
|
|
60
|
-
--prompt "<the person's prompt, as they wrote it>" --wait
|
|
60
|
+
--prompt "<the person's prompt, as they wrote it>" --still <t> --wait
|
|
61
61
|
# --wait stays until the cover has rendered, then prints the links
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
**Set the cover at the payoff.** `--still <t>` (OUTPUT seconds) picks the
|
|
65
|
+
frame the shelf, the thumbnail and the share card show: the result the
|
|
66
|
+
film is about, never the opening. Without it the cover is an early frame,
|
|
67
|
+
before anything has arrived; later pushes keep the one you set.
|
|
68
|
+
|
|
64
69
|
**Show the prompt that made it.** Pass the person's prompt with `--prompt`
|
|
65
70
|
on the push that creates the vos: the watch page shows it by default, so
|
|
66
71
|
whoever watches sees what was asked. Use their words as they wrote them,
|
|
@@ -155,6 +160,8 @@ examples of every major type: `references/examples.md`.
|
|
|
155
160
|
7. **NO `Date.now()` / `Math.random()`** in frame paths — determinism is the
|
|
156
161
|
contract; derive everything from the timeline's time and `ctx.data`
|
|
157
162
|
8. **NO `onFrame`** for new configs — put all per-frame logic in `onUpdate`
|
|
163
|
+
9. **NO push without `--still`** at the payoff — the default cover is an
|
|
164
|
+
early frame, before the film has arrived
|
|
158
165
|
|
|
159
166
|
## Params and Looks (make it remixable)
|
|
160
167
|
|
|
@@ -171,6 +171,11 @@ vos push <file>.json --title "…" --desc "one line" --tags a,b \
|
|
|
171
171
|
|
|
172
172
|
`--prompt` shows the person's prompt on the watch page by default: their words as they wrote them, never yours, with no file path, key or email they did not mean to publish. Say it is shown, and that they can take it off from the watch page.
|
|
173
173
|
|
|
174
|
+
Add `--still <t>` (OUTPUT seconds) on the first push, at the payoff: the
|
|
175
|
+
frame the design is about, never the opening. Without it the cover is an
|
|
176
|
+
early frame, before the composition has arrived; every later push keeps
|
|
177
|
+
the one you set.
|
|
178
|
+
|
|
174
179
|
Pass `--label` and `--note` on EVERY push, the first one included: a
|
|
175
180
|
create stamps them on v1, and the history is the conversation the human
|
|
176
181
|
reads. The push creates a PRIVATE vos on the key owner's shelf, filed
|