@vosjs/cli 0.47.0 → 0.48.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 +15 -5
- package/dist/{chunk-E42NCNWP.js → chunk-ANKECV7F.js} +634 -56
- package/dist/chunk-ANKECV7F.js.map +1 -0
- package/dist/{chunk-EFI3WUQB.js → chunk-P3X5KVA6.js} +15 -2
- package/dist/{chunk-EFI3WUQB.js.map → chunk-P3X5KVA6.js.map} +1 -1
- package/dist/cli.js +4 -4
- package/dist/index.js +2 -2
- package/dist/manifest-A4R367EM.js +8 -0
- package/dist/{run-DA5QXZ6F.js → run-CYHH56KA.js} +2 -2
- package/package.json +8 -6
- package/skills/VERSION +1 -0
- package/skills/launch-kit/SKILL.md +356 -0
- package/skills/launch-kit/references/channel-specs.md +56 -0
- package/skills/product-video/SKILL.md +263 -0
- package/skills/product-video/references/destinations.md +71 -0
- package/skills/product-video/references/sessions.md +226 -0
- package/skills/product-video/references/taste.md +115 -0
- package/skills/product-video/references/troubleshooting.md +116 -0
- package/skills/vos-authoring/SKILL.md +191 -0
- package/skills/vos-authoring/references/examples.md +239 -0
- package/skills/vos-authoring/references/schema-reference.md +468 -0
- package/skills/vos-create/SKILL.md +258 -0
- package/skills/vos-cut/SKILL.md +241 -0
- package/skills/vos-footage/SKILL.md +98 -0
- package/skills/vos-migrate/SKILL.md +108 -0
- package/skills/vos-remix/SKILL.md +136 -0
- package/skills/vos-remix/references/3d-recipe.md +37 -0
- package/skills/vos-remix/references/params-knobs.md +80 -0
- package/skills/vos-remix/references/remix-contract.md +81 -0
- package/dist/chunk-E42NCNWP.js.map +0 -1
- package/dist/manifest-UCS6OVWZ.js +0 -8
- /package/dist/{manifest-UCS6OVWZ.js.map → manifest-A4R367EM.js.map} +0 -0
- /package/dist/{run-DA5QXZ6F.js.map → run-CYHH56KA.js.map} +0 -0
|
@@ -77,11 +77,24 @@ var manifest = {
|
|
|
77
77
|
{
|
|
78
78
|
name: "login",
|
|
79
79
|
summary: "sign in via the browser (or --key); stores a content key"
|
|
80
|
-
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "setup",
|
|
83
|
+
summary: "ready this machine: the vos skills into your agent, a browser, one rules block, then doctor"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "doctor",
|
|
87
|
+
summary: "what is ready, in words: node, browser, ffmpeg, skills, a credential (never printed), the dev server"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "whoami",
|
|
91
|
+
summary: "the key\u2019s name and the account it belongs to, never the key"
|
|
92
|
+
},
|
|
93
|
+
{ name: "logout", summary: "remove ~/.config/vos/credentials" }
|
|
81
94
|
]
|
|
82
95
|
};
|
|
83
96
|
|
|
84
97
|
export {
|
|
85
98
|
manifest
|
|
86
99
|
};
|
|
87
|
-
//# sourceMappingURL=chunk-
|
|
100
|
+
//# sourceMappingURL=chunk-P3X5KVA6.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/plugin/manifest.ts"],"sourcesContent":["/**\n * The verb manifest of the take pipeline and the vos.so verbs — what\n * `vos help` lists under the engine verbs. It kept the shape a separately\n * installed plugin once handed the host (name + host range), so a script\n * reading it keeps working.\n */\nexport interface PluginVerb {\n name: string\n summary: string\n}\n\nexport interface PluginManifest {\n name: string\n /** Host versions this plugin speaks the run(argv) contract with. */\n hostRange: string\n verbs: PluginVerb[]\n}\n\nexport const manifest: PluginManifest = {\n name: '@vosjs/cli',\n hostRange: '>=0.9.0',\n verbs: [\n {\n name: 'create',\n summary: 'record + auto-plan + render, one command (--strict)',\n },\n {\n name: 'record',\n summary: 'drive actions.json into a take (screencast + cursor track)',\n },\n {\n name: 'plan',\n summary:\n 'plan zoom/cursor effects into doc.json (wand contract); --reuse re-times a previous cut onto a re-recording',\n },\n {\n name: 'render',\n summary: 'render a take directory (engine configs render in the host)',\n },\n {\n name: 'frames',\n summary:\n 'PNG stills at output times / zoom apexes / moments / exact sizes',\n },\n {\n name: 'deliver',\n summary:\n 'render a take to release destinations (CWS, Product Hunt, X, LinkedIn, OG…) + verified kit.json',\n },\n {\n name: 'digest',\n summary:\n 'see a recording before cutting it: moments (clicks, typing, scrolls, idle, scenes) + footage frames + crops, in doc units',\n },\n { name: 'open', summary: 'serve the take into the vos.so studio' },\n {\n name: 'validate',\n summary:\n 'lint actions.json, a take dir (doc.json semantics), or re-measure a kit.json against the channel specs',\n },\n {\n name: 'actions',\n summary:\n 'from-agent-browser: turn an agent-browser walk (steps.jsonl) into actions.json; what cannot follow is named',\n },\n {\n name: 'brand',\n summary:\n \"write a product's BRAND.md, witnessed: /design.md, /llms.txt, then the page (palette, faces, marks, the avoid list)\",\n },\n {\n name: 'fetch',\n summary: 'download a hosted program: config.json + vos.json tracking',\n },\n {\n name: 'push',\n summary: 'push a config.json or take to vos.so (private; versioned)',\n },\n {\n name: 'duplicate',\n summary:\n 'a private sibling of your OWN vos (someone else\\u2019s is remixed: fetch + push --remix-of)',\n },\n {\n name: 'pull',\n summary:\n 'sync what changed on vos.so since your base (--media brings a take’s footage home)',\n },\n {\n name: 'folder',\n summary:\n 'list/create/pull folders, move voses and assets into them (pull = the context package on disk)',\n },\n {\n name: 'asset',\n summary: 'rename one of your assets in place (recipes included)',\n },\n {\n name: 'recipe',\n summary:\n 'push a recipe .md into a folder, or replace one in place (prior body kept)',\n },\n {\n name: 'login',\n summary: 'sign in via the browser (or --key); stores a content key',\n },\n ],\n}\n"],"mappings":";;;AAkBO,IAAM,WAA2B;AAAA,EACtC,MAAM;AAAA,EACN,WAAW;AAAA,EACX,OAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA,EAAE,MAAM,QAAQ,SAAS,wCAAwC;AAAA,IACjE;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,
|
|
1
|
+
{"version":3,"sources":["../src/plugin/manifest.ts"],"sourcesContent":["/**\n * The verb manifest of the take pipeline and the vos.so verbs — what\n * `vos help` lists under the engine verbs. It kept the shape a separately\n * installed plugin once handed the host (name + host range), so a script\n * reading it keeps working.\n */\nexport interface PluginVerb {\n name: string\n summary: string\n}\n\nexport interface PluginManifest {\n name: string\n /** Host versions this plugin speaks the run(argv) contract with. */\n hostRange: string\n verbs: PluginVerb[]\n}\n\nexport const manifest: PluginManifest = {\n name: '@vosjs/cli',\n hostRange: '>=0.9.0',\n verbs: [\n {\n name: 'create',\n summary: 'record + auto-plan + render, one command (--strict)',\n },\n {\n name: 'record',\n summary: 'drive actions.json into a take (screencast + cursor track)',\n },\n {\n name: 'plan',\n summary:\n 'plan zoom/cursor effects into doc.json (wand contract); --reuse re-times a previous cut onto a re-recording',\n },\n {\n name: 'render',\n summary: 'render a take directory (engine configs render in the host)',\n },\n {\n name: 'frames',\n summary:\n 'PNG stills at output times / zoom apexes / moments / exact sizes',\n },\n {\n name: 'deliver',\n summary:\n 'render a take to release destinations (CWS, Product Hunt, X, LinkedIn, OG…) + verified kit.json',\n },\n {\n name: 'digest',\n summary:\n 'see a recording before cutting it: moments (clicks, typing, scrolls, idle, scenes) + footage frames + crops, in doc units',\n },\n { name: 'open', summary: 'serve the take into the vos.so studio' },\n {\n name: 'validate',\n summary:\n 'lint actions.json, a take dir (doc.json semantics), or re-measure a kit.json against the channel specs',\n },\n {\n name: 'actions',\n summary:\n 'from-agent-browser: turn an agent-browser walk (steps.jsonl) into actions.json; what cannot follow is named',\n },\n {\n name: 'brand',\n summary:\n \"write a product's BRAND.md, witnessed: /design.md, /llms.txt, then the page (palette, faces, marks, the avoid list)\",\n },\n {\n name: 'fetch',\n summary: 'download a hosted program: config.json + vos.json tracking',\n },\n {\n name: 'push',\n summary: 'push a config.json or take to vos.so (private; versioned)',\n },\n {\n name: 'duplicate',\n summary:\n 'a private sibling of your OWN vos (someone else\\u2019s is remixed: fetch + push --remix-of)',\n },\n {\n name: 'pull',\n summary:\n 'sync what changed on vos.so since your base (--media brings a take’s footage home)',\n },\n {\n name: 'folder',\n summary:\n 'list/create/pull folders, move voses and assets into them (pull = the context package on disk)',\n },\n {\n name: 'asset',\n summary: 'rename one of your assets in place (recipes included)',\n },\n {\n name: 'recipe',\n summary:\n 'push a recipe .md into a folder, or replace one in place (prior body kept)',\n },\n {\n name: 'login',\n summary: 'sign in via the browser (or --key); stores a content key',\n },\n {\n name: 'setup',\n summary:\n 'ready this machine: the vos skills into your agent, a browser, one rules block, then doctor',\n },\n {\n name: 'doctor',\n summary:\n 'what is ready, in words: node, browser, ffmpeg, skills, a credential (never printed), the dev server',\n },\n {\n name: 'whoami',\n summary: 'the key’s name and the account it belongs to, never the key',\n },\n { name: 'logout', summary: 'remove ~/.config/vos/credentials' },\n ],\n}\n"],"mappings":";;;AAkBO,IAAM,WAA2B;AAAA,EACtC,MAAM;AAAA,EACN,WAAW;AAAA,EACX,OAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA,EAAE,MAAM,QAAQ,SAAS,wCAAwC;AAAA,IACjE;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA,IACA,EAAE,MAAM,UAAU,SAAS,mCAAmC;AAAA,EAChE;AACF;","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -511,7 +511,7 @@ async function cmdCheck(argv) {
|
|
|
511
511
|
return result.ok ? EXIT_OK : EXIT_ERROR;
|
|
512
512
|
}
|
|
513
513
|
async function delegate(argv, viaAlias = false) {
|
|
514
|
-
const { run } = await import("./run-
|
|
514
|
+
const { run } = await import("./run-CYHH56KA.js");
|
|
515
515
|
if (viaAlias && argv[0]) {
|
|
516
516
|
process.stderr.write(
|
|
517
517
|
`note: "vos voila ${argv[0]}" is now "vos ${argv[0]}".
|
|
@@ -522,7 +522,7 @@ async function delegate(argv, viaAlias = false) {
|
|
|
522
522
|
}
|
|
523
523
|
async function printHelp() {
|
|
524
524
|
process.stdout.write(HELP_ENGINE);
|
|
525
|
-
const { manifest } = await import("./manifest-
|
|
525
|
+
const { manifest } = await import("./manifest-A4R367EM.js");
|
|
526
526
|
process.stdout.write("\nTake pipeline + vos.so verbs\n");
|
|
527
527
|
for (const v of manifest.verbs) {
|
|
528
528
|
if (v.name === "render") continue;
|
|
@@ -551,7 +551,7 @@ async function main() {
|
|
|
551
551
|
const engine = HELP_ENGINE.split("\n").filter(
|
|
552
552
|
(l) => l.startsWith(` vos ${cmd} `)
|
|
553
553
|
);
|
|
554
|
-
const { verbHelp } = await import("./run-
|
|
554
|
+
const { verbHelp } = await import("./run-CYHH56KA.js");
|
|
555
555
|
const take = verbHelp(cmd);
|
|
556
556
|
process.stdout.write(
|
|
557
557
|
`${engine.join("\n")}
|
|
@@ -583,7 +583,7 @@ ${take.includes("no such verb") ? "" : take}`
|
|
|
583
583
|
main().then(async (code) => {
|
|
584
584
|
if (code === EXIT_OK) {
|
|
585
585
|
const verb = process.argv[2] ?? "";
|
|
586
|
-
const { HELP } = await import("./run-
|
|
586
|
+
const { HELP } = await import("./run-CYHH56KA.js");
|
|
587
587
|
const documented = [
|
|
588
588
|
...helpFlagsFor(HELP_ENGINE, verb),
|
|
589
589
|
...helpFlagsFor(HELP, verb)
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
validateActions,
|
|
31
31
|
waitForPageDone,
|
|
32
32
|
writeSyncState
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-ANKECV7F.js";
|
|
34
34
|
import {
|
|
35
35
|
BrowserUnavailableError,
|
|
36
36
|
configDuration,
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
import "./chunk-AUPGJJTL.js";
|
|
41
41
|
import {
|
|
42
42
|
manifest
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-P3X5KVA6.js";
|
|
44
44
|
export {
|
|
45
45
|
BrowserUnavailableError,
|
|
46
46
|
RECORDING_NAME,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
run,
|
|
6
6
|
takeBrowserArgs,
|
|
7
7
|
verbHelp
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ANKECV7F.js";
|
|
9
9
|
import "./chunk-FACYY7VV.js";
|
|
10
10
|
import "./chunk-AUPGJJTL.js";
|
|
11
11
|
export {
|
|
@@ -15,4 +15,4 @@ export {
|
|
|
15
15
|
takeBrowserArgs,
|
|
16
16
|
verbHelp
|
|
17
17
|
};
|
|
18
|
-
//# sourceMappingURL=run-
|
|
18
|
+
//# sourceMappingURL=run-CYHH56KA.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vosjs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.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 and stills, deliver a release's media per destination spec, and sync with vos.so. One binary, every verb, MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
},
|
|
43
43
|
"files": [
|
|
44
44
|
"dist",
|
|
45
|
-
"schema"
|
|
45
|
+
"schema",
|
|
46
|
+
"skills"
|
|
46
47
|
],
|
|
47
48
|
"sideEffects": false,
|
|
48
49
|
"dependencies": {
|
|
@@ -51,11 +52,11 @@
|
|
|
51
52
|
"@vosjs/core": "^0.25.0",
|
|
52
53
|
"@vosjs/editor": "^1.3.1",
|
|
53
54
|
"@vosjs/elements": "^0.8.1",
|
|
54
|
-
"@vosjs/render-core": "^0.2.
|
|
55
|
+
"@vosjs/render-core": "^0.2.9",
|
|
55
56
|
"@vosjs/shared": "^0.4.1",
|
|
56
|
-
"@vosjs/studio-core": "^0.30.
|
|
57
|
-
"@vosjs/
|
|
58
|
-
"@vosjs/
|
|
57
|
+
"@vosjs/studio-core": "^0.30.3",
|
|
58
|
+
"@vosjs/tween": "^0.8.2",
|
|
59
|
+
"@vosjs/timeline": "^0.4.1"
|
|
59
60
|
},
|
|
60
61
|
"devDependencies": {
|
|
61
62
|
"@types/node": "^22",
|
|
@@ -68,6 +69,7 @@
|
|
|
68
69
|
},
|
|
69
70
|
"scripts": {
|
|
70
71
|
"build": "tsup",
|
|
72
|
+
"sync-skills": "node scripts/sync-skills.mjs",
|
|
71
73
|
"typecheck": "tsc --noEmit",
|
|
72
74
|
"lint": "eslint .",
|
|
73
75
|
"test": "vitest run",
|
package/skills/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.5.0
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: launch-kit
|
|
3
|
+
description: Ship the media with the release — one take of the real product becomes the demo video, the store listing (Chrome Web Store screenshots, tile, marquee), the Product Hunt gallery, the social cuts and the OG card, each a frame of a poster document in the brand's look, verified against per-channel specs and against the picture itself, judged beside a reference set, and pushed as a version labelled for the release. Use when asked "we're launching / shipping vN", "update the Chrome Web Store (Play, Shopify) listing", "make the launch video", "cut a changelog / what's-new clip", "PR to video", "refresh the demo for the new version", "make the Product Hunt gallery", or to produce a launch week's batch of clips in one style.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Launch kit — ship the media with the release
|
|
8
|
+
|
|
9
|
+
You are producing a RELEASE's media, not a video: one take of the shipped
|
|
10
|
+
feature becomes every asset the release needs, composed, sized and verified
|
|
11
|
+
per destination, kept as editable documents so the NEXT release is a
|
|
12
|
+
re-render, not a re-shoot. Everything is data; the preview is the render;
|
|
13
|
+
export is free at every resolution up to 4K, no watermark; the engine is MIT.
|
|
14
|
+
|
|
15
|
+
The kit is RENDERED in one verb, `vos deliver`, from documents that carry
|
|
16
|
+
the taste: every card is a frame of a POSTER document (a plain take whose
|
|
17
|
+
card sits where the poster wants it, with the words and the mark as clips),
|
|
18
|
+
every cut plays the motion `vos plan` proposed into its document (an
|
|
19
|
+
entrance, an end card, captions, a bed), the still moments come from the
|
|
20
|
+
story, and two verifiers say what is wrong in words. `deliver` composes
|
|
21
|
+
nothing. Your judgment goes into three files (`BRAND.md`, `LAUNCH.md`,
|
|
22
|
+
`actions.json`), one pick (which poster on the shelf the cards follow) and
|
|
23
|
+
one decision (which moments are the story), never into hand-cropping.
|
|
24
|
+
|
|
25
|
+
## Setup
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm i -D @vosjs/cli # the vos CLI (take pipeline included)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`ffmpeg` on PATH covers what the CLI does not emit (the PH hover-GIF) and
|
|
32
|
+
lets `vos validate --picture` read a video's first and last frame. MP4
|
|
33
|
+
renders need system Chrome (`--format mp4`).
|
|
34
|
+
|
|
35
|
+
## 1. Establish the release
|
|
36
|
+
|
|
37
|
+
Five facts before any recording:
|
|
38
|
+
|
|
39
|
+
- **What shipped** — the feature, the version string, the URL where it runs.
|
|
40
|
+
- **Which destinations** — the per-channel spec table ships as data:
|
|
41
|
+
`node_modules/@vosjs/cli/schema/channel-specs.json` (CWS, Product Hunt,
|
|
42
|
+
X, LinkedIn, GitHub, OG, YouTube; sizes, counts, byte and duration
|
|
43
|
+
ceilings, the word policy and the safe rect per destination, and each
|
|
44
|
+
image's GENRE: `screenshot` is the real page, `card` is a frame of a
|
|
45
|
+
poster document; `references/channel-specs.md` is the same data as a
|
|
46
|
+
table). **Loop over it, never hand-type dimensions.** Ask which channels
|
|
47
|
+
this release ships to; default to last release's set. The JSON carries a
|
|
48
|
+
`verified` date; if it is more than a quarter old, spot-check the channel
|
|
49
|
+
docs before shipping.
|
|
50
|
+
- **The brand** — resolve it BEFORE authoring any asset, and never default
|
|
51
|
+
to a layout's own palette. The project folder's `BRAND.md` is the brand
|
|
52
|
+
kit (frontmatter carries the colour roles `bgA bgB bgC ink accent`, the
|
|
53
|
+
face roles `fontDisplay fontBody`, `logoUrl` (a bare mark, never a tiled
|
|
54
|
+
icon), `wordmark`, and `look`, the presentation the site's own ground
|
|
55
|
+
asks for: `plate` for a paper site, `dark` for a dark one, else
|
|
56
|
+
`gradient`). Absent one, `vos brand <url>` witnesses it from the site's
|
|
57
|
+
`/design.md`, `/llms.txt` and the page itself, and writes every role with
|
|
58
|
+
its provenance; read it, correct what a page cannot say, and file it with
|
|
59
|
+
`vos recipe push BRAND.md --folder <slug>`. Place it BESIDE the take (or
|
|
60
|
+
in the take's parent folder): `plan` and `deliver` read it there with no
|
|
61
|
+
flag.
|
|
62
|
+
- **The words** — `LAUNCH.md` beside the take carries the release's roles
|
|
63
|
+
in its frontmatter, read with no flag:
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
headline: "Two to six words\nover up to three lines" # the posters and the end card
|
|
67
|
+
kicker: "PRODUCT V2.1" # absent = the wordmark plus --release
|
|
68
|
+
music: upbeat # a catalog slug or mood; none = silent
|
|
69
|
+
entrance: tilt-in # tilt-in | pull-out | rise | none
|
|
70
|
+
endCard: on # the official End card template; none switches it off; a title, vos id or doc.json names another
|
|
71
|
+
with: "Split cover, landscape@end" # optional: any template laid at an anchor (@end | @start | @step:<id> | @<seconds>)
|
|
72
|
+
captions: on # none switches the beat captions off
|
|
73
|
+
poster: poster/landscape # optional: the document every card renders from
|
|
74
|
+
poster-tile: poster/tile # optional: one class named on its own
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
A headline is the ONE line the release says; write it over its lines with
|
|
78
|
+
`\n`. `vos plan` reads the motion roles when it proposes the cut's motion
|
|
79
|
+
and `plan --style` types the words into a poster; `deliver` reads only
|
|
80
|
+
the `poster` roles, and with none it looks for `poster/<class>/doc.json`
|
|
81
|
+
beside the take.
|
|
82
|
+
- **The layout** — a poster the cards will follow, on a shelf. The official
|
|
83
|
+
`Poster families` project on vos.so holds one exemplar per aspect class
|
|
84
|
+
(landscape, square, portrait, tile) beside its `POSTER.md`, which says
|
|
85
|
+
when each class is the one; a maker's own project is the same shape.
|
|
86
|
+
`vos folder pull poster-families` lists them with their vos ids. There is
|
|
87
|
+
no layout name and no `--layout` flag: the exemplar IS the layout, copied
|
|
88
|
+
by data.
|
|
89
|
+
|
|
90
|
+
The destinations decide the VIEWPORT, before anything records: footage
|
|
91
|
+
resolution = viewport, and a 1280×720 take cannot honestly fill a 1920×1080
|
|
92
|
+
video spec. Record at 1920×1080 for a 1080p kit, 2560×1440 when a
|
|
93
|
+
destination is larger.
|
|
94
|
+
|
|
95
|
+
If the work lands in a vos.so project (folder), pull it first and read every
|
|
96
|
+
`.md` recipe in it — `LAUNCH.md` binds this loop the way `CUT.md` binds a
|
|
97
|
+
cut, and a `POSTER.md` binds the posters. Recipes override this skill's
|
|
98
|
+
defaults.
|
|
99
|
+
|
|
100
|
+
## 2. Source: one take of the real thing
|
|
101
|
+
|
|
102
|
+
The kit is made FROM the product, never from a mockup (store policy agrees:
|
|
103
|
+
misleading listing images are a removal-grade violation).
|
|
104
|
+
|
|
105
|
+
**Stage the content like a set before recording.** Half of what separates a
|
|
106
|
+
premium launch image from a screen grab is what is ON the screen. The
|
|
107
|
+
actions.json must leave the product in the state a proud screenshot would
|
|
108
|
+
show — labels typed, real-looking data, the feature mid-story — before any
|
|
109
|
+
poster or store still is cut. `deliver` drops a blank moment (a wallpaper,
|
|
110
|
+
an empty canvas) and says so, but it cannot stage the set for you.
|
|
111
|
+
|
|
112
|
+
**Write the story into `actions.json`.** Give steps an `id` (the moments
|
|
113
|
+
and the re-render loop address them by it) and a `caption` where a beat
|
|
114
|
+
deserves one (two to eight words; `plan` proposes it as a lower-third at
|
|
115
|
+
that step's moment on the cuts that take words). After a click that
|
|
116
|
+
navigates, put a `wait` for the load: the still is read at the END of that
|
|
117
|
+
wait, so the frame shows the page, never the spinner.
|
|
118
|
+
|
|
119
|
+
- **Fresh recording**: the `product-video` skill's loop (explore →
|
|
120
|
+
`actions.json` → `vos record --strict` → tune `doc.json`). Keep
|
|
121
|
+
`actions.json` in the repo — it is the next release's script.
|
|
122
|
+
- **The shipped feature is behind a login**: settle the session before
|
|
123
|
+
the script, by the `product-video` skill's ladder
|
|
124
|
+
(in full at https://vos.so/llms-full.txt, "Sessions"): mint one from the test auth the repo
|
|
125
|
+
already has, else script the form off camera, else the human signs in
|
|
126
|
+
once (`vos session open <url> --name <app>`, then `--session <app>`), else they
|
|
127
|
+
record with the extension and you cut it. Record with
|
|
128
|
+
`--storage-state <file>`. A release re-records every version, so prefer
|
|
129
|
+
the rung that needs no human: it is the one that still works next
|
|
130
|
+
release. Never type or accept a production password; keep the state file
|
|
131
|
+
out of the take and out of git; record from a demo account, because a
|
|
132
|
+
store listing showing a real customer's data is a removal-grade mistake.
|
|
133
|
+
- **Existing take**: cut it with the `vos-cut` skill. A hosted take comes
|
|
134
|
+
home with `vos fetch <vosId|watch-url> --out dir --media`.
|
|
135
|
+
- **New version of a shot product**: re-record with `vos record` into the
|
|
136
|
+
SAME take (the footage is replaced, the cut survives as `doc.prev.json`),
|
|
137
|
+
then `vos plan take --reuse` re-times the cut onto the new recording and
|
|
138
|
+
names what could not follow. Never start from scratch; every fix is an
|
|
139
|
+
edit to `doc.json`.
|
|
140
|
+
|
|
141
|
+
A motion-graphic segment rendered elsewhere is an INPUT: it drops in as a
|
|
142
|
+
media overlay clip or a backdrop in the document, never the other way round.
|
|
143
|
+
|
|
144
|
+
## 3. The posters and the motion, as documents
|
|
145
|
+
|
|
146
|
+
**The cut moves by data.** A fresh `vos plan` proposes the cut's motion into
|
|
147
|
+
`doc.json` from `LAUNCH.md`'s roles: the card's entrance, the END CARD (the
|
|
148
|
+
official `End card` template on vos.so, laid at the end: the card recedes
|
|
149
|
+
over a one-second freeze of its last frame, then the mark, the headline
|
|
150
|
+
arriving word by word, the release line and the URL settle on the ground;
|
|
151
|
+
its clips are stamped `from: endcard`), any template `with:` names at its
|
|
152
|
+
anchor, a caption per step, a music bed and a click sound on every press
|
|
153
|
+
when the take has no mic; every proposal carries a stable id (`bed`,
|
|
154
|
+
`click-<n>`, `caption-<step>`). On an existing cut `--motion` re-proposes;
|
|
155
|
+
a refresh never does, so a deleted end card stays deleted. A template is a
|
|
156
|
+
plain take on a shelf whose clips carry stable ids; a ref is a title on the
|
|
157
|
+
official shelf (`"End card"`, `"Split cover, square"`), a vos id, or a
|
|
158
|
+
document on disk. Open the take in the studio and what you see is what the
|
|
159
|
+
kit renders.
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
vos plan take --motion --release v2.1
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**A poster is a DOCUMENT, a plain take**: the card placed by `frame.inset`
|
|
166
|
+
(a negative side bleeds it off the edge), leaned by the tilt track's rest
|
|
167
|
+
pose, the words and the mark as overlay clips with stable ids
|
|
168
|
+
(`stage-title`, `stage-kicker`, `stage-brand`, `stage-mark`), and a
|
|
169
|
+
trailing `hold` on the last segment whose START is the still. Make one per
|
|
170
|
+
aspect class the release needs (landscape, square, portrait, tile), each
|
|
171
|
+
from the take's own footage:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
cp -r take poster/landscape # or vos duplicate <take vosId>, then fetch --media
|
|
175
|
+
vos plan poster/landscape --style <poster vosId|doc.json> --headline "…" --kicker "…"
|
|
176
|
+
vos frames poster/landscape --frame <rest>; vos validate poster/landscape
|
|
177
|
+
vos push poster/landscape --folder <project-slug> --label "poster, landscape"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`plan --style <poster>` copies the LAYOUT (the card's placement and
|
|
181
|
+
presentation, the stage clips with the release's words patched in from
|
|
182
|
+
`LAUNCH.md` or the flags, the rest lean, the hold) onto the take and keeps
|
|
183
|
+
the take's own aspect, chrome and cut; the brand's mark from `BRAND.md`
|
|
184
|
+
`logoUrl` is fetched into `brand/` for `stage-mark`, and what could not
|
|
185
|
+
follow is said. Narrow the segments to the moment (a zoom apex, the settled
|
|
186
|
+
response after a click) so the rest is the feature, never the cold open;
|
|
187
|
+
the product may keep PLAYING inside the card until the hold. Then look at
|
|
188
|
+
the rest frame: a poster is judged by eye before it is pushed. A human
|
|
189
|
+
opens it in the studio and drags the card, retypes the words in place,
|
|
190
|
+
moves the hold; `vos pull --media` brings that back down, and the next
|
|
191
|
+
release re-words it with the same `plan --style` from its own pushed
|
|
192
|
+
document.
|
|
193
|
+
|
|
194
|
+
## 4. Deliver
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
vos deliver take --to cws,producthunt,x,linkedin,og,github,youtube --release v2.1
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
One pass, and the verb decides what you used to decide by hand:
|
|
201
|
+
|
|
202
|
+
- **The moments.** Still times come from the STORY: every step's end plus a
|
|
203
|
+
settle (the end of the wait that follows a click), then the zoom apexes,
|
|
204
|
+
then a spread. Each candidate is read once as the real page; blanks are
|
|
205
|
+
dropped, two of one frame collapse to one, every drop said in
|
|
206
|
+
`skipped[]`. `--times step:<id>[+offset]` names one.
|
|
207
|
+
- **Screenshots** are the real page, full bleed: the store still and the
|
|
208
|
+
gallery drop the cut's zoom, tilt and chrome by default (`--composed`
|
|
209
|
+
keeps them, and the picture pass refuses it for the store).
|
|
210
|
+
- **The cards render from the posters.** Each card destination (the OG
|
|
211
|
+
card, the LinkedIn and X images, the YouTube thumbnail, the CWS tile and
|
|
212
|
+
marquee, the GitHub social preview, the PH thumbnail) renders from the
|
|
213
|
+
poster document of its aspect class beside the take
|
|
214
|
+
(`poster/<class>/doc.json`, or the document `LAUNCH.md`'s `poster` roles
|
|
215
|
+
name) at that document's rest, at the destination's exact pixels. A
|
|
216
|
+
class with no document is the take's own frame, said once in
|
|
217
|
+
`skipped[]`. Nothing is composed in memory.
|
|
218
|
+
- **The cuts play the document.** `deliver` keeps only each destination's
|
|
219
|
+
MECHANICS: a loop drops the entrance, the end card, the captions and the
|
|
220
|
+
sound; a silent channel mutes the bed; the 9:16 cut is a reframe whose
|
|
221
|
+
crop follows the camera, not a letterbox; a loop destination the take
|
|
222
|
+
outruns takes the take's first seconds up to its cap; a byte ceiling
|
|
223
|
+
becomes a bitrate budget.
|
|
224
|
+
- **The manifest.** `kit.json` beside the assets records every asset with
|
|
225
|
+
its destination, the moment it came from, and for every card the poster
|
|
226
|
+
it is a frame of (`source: "poster"`, the class, the file, the vos it
|
|
227
|
+
tracks, the shot rect and the text boxes read FROM the document), and
|
|
228
|
+
`skipped[]` with every reason.
|
|
229
|
+
|
|
230
|
+
## 5. Verify: the specs, then the picture
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
vos validate kit/kit.json --picture
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
The spec pass re-measures every asset from its bytes (px, bytes, duration,
|
|
237
|
+
count, a WebP under a `.png` name). The picture pass says what each asset
|
|
238
|
+
LOOKS like, every finding with a code, a severity, a fix hint and a box:
|
|
239
|
+
|
|
240
|
+
| code | what fires |
|
|
241
|
+
| --- | --- |
|
|
242
|
+
| `blank` | a card whose subject is under the ink floor: a wallpaper, an empty canvas |
|
|
243
|
+
| `duplicate` | cards of one frame (two is a note; three or more fails) |
|
|
244
|
+
| `subject` | a card off the 60 to 92% band, or a crop where a card was asked for |
|
|
245
|
+
| `separation` | a light card on a light ground with no shadow and no drawn edge |
|
|
246
|
+
| `halfsize` | a tile that loses its edges when the store shrinks it |
|
|
247
|
+
| `sliced` | a headline crossing the frame edge |
|
|
248
|
+
| `safe` | words outside the destination's safe rect, or words where none are wanted |
|
|
249
|
+
| `contrast` | a text box under APCA Lc 60 (headline) or 75 (body) |
|
|
250
|
+
| `firstlast` | a cut that opens or ends on nothing, or bled on all four sides |
|
|
251
|
+
|
|
252
|
+
A problem is redone by fixing the INPUT (a moment, a word, the set, the
|
|
253
|
+
poster document, a recipe line), never by hand-editing a PNG. Self-check by
|
|
254
|
+
these names before you render: a cold-open hero is `blank`, eight crops of
|
|
255
|
+
one frame is `duplicate`, white words on a paper ground is `contrast`. A
|
|
256
|
+
poster's card is judged by the shot rect the document carries, so a card
|
|
257
|
+
you dragged off the band is `subject` before anyone posts it.
|
|
258
|
+
|
|
259
|
+
## 6. Judge beside the references
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
vos judge kit/kit.json --against <MANIFEST.json>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The manifest names the maker's reference set (a private folder: id, file,
|
|
266
|
+
role, layout, facts, rule per asset; the public evals reference it by
|
|
267
|
+
role). For every still with a reference of its role the verb writes two
|
|
268
|
+
sheets (the asset left, then right) and the rubric beside them, and leaves
|
|
269
|
+
`judge.json` with a slot per pair. Judge every pair BOTH ways, with the
|
|
270
|
+
rubric's numbered rules and the three positive tests (would you post it as
|
|
271
|
+
a still; does it read at half size; name three ways it acknowledges THIS
|
|
272
|
+
product), and write `win` true, false or null (a tie) with the rule
|
|
273
|
+
numbers. Parity with the references is 50%; a kit under 40% is not ready
|
|
274
|
+
to market, said in the handoff, never shipped around.
|
|
275
|
+
|
|
276
|
+
Skip the judge for a re-render whose inputs did not change (the same take,
|
|
277
|
+
the same words); run it when a layout, a look, a headline or the set of
|
|
278
|
+
moments changed. Do not re-run `validate` or `judge` after a push unless
|
|
279
|
+
the human asks.
|
|
280
|
+
|
|
281
|
+
## 7. Push the release, file the kit
|
|
282
|
+
|
|
283
|
+
Push the source and every poster labelled for the release, and FILE the
|
|
284
|
+
kit's stills into the release's project so the human can retrospect without
|
|
285
|
+
a terminal:
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
vos push take --folder <project-slug> --label "v2.1 launch" --note "<what shipped, one line>"
|
|
289
|
+
vos push poster/landscape --folder <project-slug> --label "v2.1 poster, landscape"
|
|
290
|
+
vos asset push kit/*.png --folder <project-slug>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
End by handing the human the loop, not the files: the watch page plays the
|
|
294
|
+
latest version, the studio edits the cut AND the posters (the card is a
|
|
295
|
+
layer you drag; the words are retyped in place), and `vos pull --media`
|
|
296
|
+
brings their edits back down. Next release, start at step 2's third bullet
|
|
297
|
+
and re-word the posters with `plan --style` from their own pushed
|
|
298
|
+
documents.
|
|
299
|
+
|
|
300
|
+
**Preserve the human's changes.** They edit the take in the studio outside
|
|
301
|
+
this conversation. If `vos pull --check` or the differ's `protected` set
|
|
302
|
+
shows a change you did not make, assume it was intentional or ask; never
|
|
303
|
+
overwrite it, and never re-plan over a manual span.
|
|
304
|
+
|
|
305
|
+
## Launch week (5-12 clips, one style)
|
|
306
|
+
|
|
307
|
+
A launch week is a series: cut and sign off ONE seed clip with the human
|
|
308
|
+
first, then cut every other feature's take with
|
|
309
|
+
`vos plan <take> --style <seed doc.json|vosId>` so the batch shares its look
|
|
310
|
+
by data. Never spread before the seed is signed off.
|
|
311
|
+
|
|
312
|
+
## Notes
|
|
313
|
+
|
|
314
|
+
- `deliver` is the procedure; this skill is the judgment around it (the
|
|
315
|
+
set, the words, the layout pick, the moments, the verdicts). If you find
|
|
316
|
+
yourself cropping a PNG by hand, the fix is a document, a recipe line or
|
|
317
|
+
a check, and you should say so in the handoff.
|
|
318
|
+
- Platform specs drift. The JSON carries a `verified` date; if it is more
|
|
319
|
+
than a quarter old, spot-check the channel docs before shipping.
|
|
320
|
+
- A layout is a document on a shelf plus a recipe line, never a name the
|
|
321
|
+
document learns: the official `Templates` project holds the split cover in
|
|
322
|
+
four aspect classes and the end card today (`vos plan take --style "Split
|
|
323
|
+
cover, landscape"` carries a layout; a poster's still is where its
|
|
324
|
+
trailing freeze begins); a maker's own family is a project of their own
|
|
325
|
+
posters beside a `POSTER.md`. Applying one is `plan --style <vosId>`.
|
|
326
|
+
|
|
327
|
+
## Avoid (the traps that shipped)
|
|
328
|
+
|
|
329
|
+
- A 720p recording against a 1080p spec: the destinations pick the viewport
|
|
330
|
+
before anything records. Cost a full re-record once.
|
|
331
|
+
- A layout's own palette on a deliverable: resolve `BRAND.md` (or witness
|
|
332
|
+
the site) before any asset is authored, and put it beside the take.
|
|
333
|
+
- A cold-open hero (`blank`): the first frame is the marketing page's
|
|
334
|
+
headline with the product nowhere; `deliver` reads the steps, but a
|
|
335
|
+
script with no gestures gives it nothing to read.
|
|
336
|
+
- A poster whose rest is the cold open: narrow its segments to the moment
|
|
337
|
+
(a zoom apex) and hold there; the still is the hold's start.
|
|
338
|
+
- A card that is not a frame of a document: there is no `--poster`, no
|
|
339
|
+
`--shot-time` and no in-memory composition; a card destination with no
|
|
340
|
+
poster document renders the take's own frame and says so.
|
|
341
|
+
- Eight crops of one frame (`duplicate`): a script with one moment. Give the
|
|
342
|
+
story steps, and waits after the clicks that navigate.
|
|
343
|
+
- A store screenshot under the frame chrome or the camera zoom: the store
|
|
344
|
+
still is the real page, full bleed (deliver's default, and `--composed`
|
|
345
|
+
is refused by the picture pass).
|
|
346
|
+
- The loading plane as the hero: a click that navigates settles when the
|
|
347
|
+
wait after it ends; a click with no wait after it is read 0.4 s later,
|
|
348
|
+
mid-load.
|
|
349
|
+
- White words on a paper ground (`contrast`): the end card takes the brand's
|
|
350
|
+
ink; a kicker softened too far reads under the floor.
|
|
351
|
+
- A card that is WebP under a `.png` name: `vos still` writes WebP; convert,
|
|
352
|
+
then `vos validate` reads the bytes and says so.
|
|
353
|
+
- Padding a spec floor: a 36 s story is a skipped 60 s demo, with its reason.
|
|
354
|
+
- A hand-typed dimension: every size comes from `channel-specs.json`.
|
|
355
|
+
- The save beat on a demo instance that disables writes: say so in
|
|
356
|
+
`skipped`, never fake the click.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Channel specs — readable mirror
|
|
2
|
+
|
|
3
|
+
Source of truth: `schema/channel-specs.json` in the `@vosjs/cli` npm
|
|
4
|
+
package (loop over the JSON, don't hand-type from this table). Verified
|
|
5
|
+
against official platform docs 2026-08-04 — platform specs drift, so
|
|
6
|
+
re-verify quarterly against the channels' current docs.
|
|
7
|
+
|
|
8
|
+
## Video (4 cuts from one document)
|
|
9
|
+
|
|
10
|
+
| Cut | Spec | Destinations |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| Main demo | 16:9 1920×1080 MP4 H.264+AAC, 60–120s, captions burned in | YouTube (public) — the same upload serves the CWS promo video and the Product Hunt video (both take YouTube URLs only) |
|
|
13
|
+
| Feed cut | 16:9 or 1:1, 30–60s, ≤140s / 512MB free tier, **H.264+AAC only** (HEVC/VP9/AV1 rejected) | X native upload |
|
|
14
|
+
| Vertical cut | 9:16 1080×1920, 30–90s, critical text inside a centered ~900×1160 safe zone | YouTube Shorts + LinkedIn native vertical |
|
|
15
|
+
| README loop | 16:9, 10–20s, **≤10MB** MP4 (GitHub free-plan ceiling) | GitHub README |
|
|
16
|
+
|
|
17
|
+
## Images
|
|
18
|
+
|
|
19
|
+
| Asset | Exact spec |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| YouTube thumbnail | 1280×720, <2MB |
|
|
22
|
+
| CWS screenshots | 1–5 × **1280×800** (real UX only — misleading images are a removal-grade violation), full bleed, square corners |
|
|
23
|
+
| CWS small promo tile | 440×280 (listings without one rank lower), no text, fill the region |
|
|
24
|
+
| CWS marquee | 1400×560 (carousel eligibility) |
|
|
25
|
+
| CWS icon | 128×128 PNG, 96×96 art + 16px transparent padding |
|
|
26
|
+
| Product Hunt thumbnail | 240×240, GIF animates on hover only, first frame must stand alone, <3MB |
|
|
27
|
+
| Product Hunt gallery | 4–8 × 1270×760, first image is the hero, GIF allowed |
|
|
28
|
+
| X in-feed image | 1200×675 |
|
|
29
|
+
| LinkedIn feed image | 1200×627 (or 1080×1350 vertical) |
|
|
30
|
+
| OG card | 1200×630, <1MB, text in the center ~1080×600, explicit `twitter:card=summary_large_image` (og:image alone gets the small card) |
|
|
31
|
+
| GitHub social preview | 1280×640, <1MB, key text ≥50px from every edge |
|
|
32
|
+
|
|
33
|
+
## Composition (per image destination)
|
|
34
|
+
|
|
35
|
+
The specs carry three more facts `vos deliver` and `vos validate --picture`
|
|
36
|
+
read (loop over the JSON; this is the readable mirror). A card renders from
|
|
37
|
+
the poster document of its aspect class beside the take
|
|
38
|
+
(`poster/<class>/doc.json`, or the document `LAUNCH.md`'s `poster` roles
|
|
39
|
+
name); the class is read from the destination's pixels (wider than 1.15:1 is
|
|
40
|
+
landscape, 0.87 to 1.15 square, narrower portrait, under 700 px on the long
|
|
41
|
+
side a tile):
|
|
42
|
+
|
|
43
|
+
| Destination | Words | Safe rect (fractions) | Renders from |
|
|
44
|
+
| --- | --- | --- | --- |
|
|
45
|
+
| cws screenshot, producthunt gallery | none (the real page) | whole | the take, full bleed |
|
|
46
|
+
| cws small-promo-tile | none | whole | the tile poster |
|
|
47
|
+
| cws marquee | allowed | 5% / 8% inset | the landscape poster |
|
|
48
|
+
| og card | expected | 1080x600 centred | the landscape poster |
|
|
49
|
+
| linkedin feed-image | expected | 5% / 8% inset | the landscape poster |
|
|
50
|
+
| x feed-image | allowed | 5% / 8% inset | the landscape poster |
|
|
51
|
+
| youtube thumbnail | expected | 5% / 8% inset | the landscape poster |
|
|
52
|
+
| github social-preview | expected | 50 px from every edge | the landscape poster |
|
|
53
|
+
| producthunt thumbnail | none | 6% inset | the tile poster |
|
|
54
|
+
| shorts / vertical cut | expected | 900x1160 centred | the take, reframed |
|
|
55
|
+
| x feed-cut, youtube main-demo | allowed | 5% / 8% inset | the take, with its planned entrance and end card |
|
|
56
|
+
| github readme-loop | none | | the take, motion and sound dropped |
|