@vibeframe/cli 0.104.2 → 0.105.1
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/dist/index.js +314 -51
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -17106,6 +17106,53 @@ function pickReferenceImageUrl(input) {
|
|
|
17106
17106
|
return input;
|
|
17107
17107
|
return void 0;
|
|
17108
17108
|
}
|
|
17109
|
+
function isFalFileInput(value) {
|
|
17110
|
+
return value.startsWith("http://") || value.startsWith("https://") || value.startsWith("data:");
|
|
17111
|
+
}
|
|
17112
|
+
async function normaliseReferences(input, client) {
|
|
17113
|
+
if (!input)
|
|
17114
|
+
return [];
|
|
17115
|
+
const references = [];
|
|
17116
|
+
for (const ref of input) {
|
|
17117
|
+
if (!isFalFileInput(ref.url))
|
|
17118
|
+
continue;
|
|
17119
|
+
references.push({
|
|
17120
|
+
...ref,
|
|
17121
|
+
url: ref.url.startsWith("data:") ? await uploadDataUri(client, ref.url) : ref.url
|
|
17122
|
+
});
|
|
17123
|
+
}
|
|
17124
|
+
return references;
|
|
17125
|
+
}
|
|
17126
|
+
async function uploadDataUri(client, dataUri) {
|
|
17127
|
+
const blob = dataUriToBlob(dataUri);
|
|
17128
|
+
return client.storage.upload(blob, {
|
|
17129
|
+
lifecycle: { expiresIn: "1h" }
|
|
17130
|
+
});
|
|
17131
|
+
}
|
|
17132
|
+
function dataUriToBlob(dataUri) {
|
|
17133
|
+
const match = /^data:([^;,]+)?(;base64)?,(.*)$/s.exec(dataUri);
|
|
17134
|
+
if (!match)
|
|
17135
|
+
throw new Error("Invalid data URI reference.");
|
|
17136
|
+
const mimeType = match[1] || "application/octet-stream";
|
|
17137
|
+
const isBase64 = Boolean(match[2]);
|
|
17138
|
+
const payload = match[3] ?? "";
|
|
17139
|
+
const buffer = isBase64 ? Buffer.from(payload, "base64") : Buffer.from(decodeURIComponent(payload), "utf-8");
|
|
17140
|
+
return new Blob([new Uint8Array(buffer)], { type: mimeType });
|
|
17141
|
+
}
|
|
17142
|
+
function groupReferences(references) {
|
|
17143
|
+
const image_urls = [];
|
|
17144
|
+
const video_urls = [];
|
|
17145
|
+
const audio_urls = [];
|
|
17146
|
+
for (const ref of references) {
|
|
17147
|
+
if (ref.kind === "image")
|
|
17148
|
+
image_urls.push(ref.url);
|
|
17149
|
+
if (ref.kind === "video")
|
|
17150
|
+
video_urls.push(ref.url);
|
|
17151
|
+
if (ref.kind === "audio")
|
|
17152
|
+
audio_urls.push(ref.url);
|
|
17153
|
+
}
|
|
17154
|
+
return { image_urls, video_urls, audio_urls };
|
|
17155
|
+
}
|
|
17109
17156
|
function normaliseAspect(value) {
|
|
17110
17157
|
if (!value)
|
|
17111
17158
|
return "auto";
|
|
@@ -17128,7 +17175,7 @@ function normaliseDuration(value) {
|
|
|
17128
17175
|
return "auto";
|
|
17129
17176
|
return Math.max(4, Math.min(15, Math.round(value)));
|
|
17130
17177
|
}
|
|
17131
|
-
var import_client, ENDPOINT_TEXT_TO_VIDEO, ENDPOINT_IMAGE_TO_VIDEO, DEFAULT_VARIANT, VALID_RESOLUTIONS, VALID_ASPECTS, FalProvider, falProvider;
|
|
17178
|
+
var import_client, ENDPOINT_TEXT_TO_VIDEO, ENDPOINT_IMAGE_TO_VIDEO, ENDPOINT_REFERENCE_TO_VIDEO, DEFAULT_VARIANT, VALID_RESOLUTIONS, VALID_ASPECTS, FalProvider, falProvider;
|
|
17132
17179
|
var init_FalProvider = __esm({
|
|
17133
17180
|
"../ai-providers/dist/fal/FalProvider.js"() {
|
|
17134
17181
|
"use strict";
|
|
@@ -17141,6 +17188,10 @@ var init_FalProvider = __esm({
|
|
|
17141
17188
|
"seedance-2.0": "bytedance/seedance-2.0/image-to-video",
|
|
17142
17189
|
"seedance-2.0-fast": "bytedance/seedance-2.0/fast/image-to-video"
|
|
17143
17190
|
};
|
|
17191
|
+
ENDPOINT_REFERENCE_TO_VIDEO = {
|
|
17192
|
+
"seedance-2.0": "bytedance/seedance-2.0/reference-to-video",
|
|
17193
|
+
"seedance-2.0-fast": "bytedance/seedance-2.0/fast/reference-to-video"
|
|
17194
|
+
};
|
|
17144
17195
|
DEFAULT_VARIANT = "seedance-2.0";
|
|
17145
17196
|
VALID_RESOLUTIONS = ["480p", "720p", "1080p"];
|
|
17146
17197
|
VALID_ASPECTS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "auto"];
|
|
@@ -17149,7 +17200,7 @@ var init_FalProvider = __esm({
|
|
|
17149
17200
|
this.id = "seedance";
|
|
17150
17201
|
this.name = "fal.ai (Seedance 2.0)";
|
|
17151
17202
|
this.description = "fal.ai hosting ByteDance Seedance 2.0 \u2014 Artificial Analysis #2 on both text-to-video and image-to-video leaderboards";
|
|
17152
|
-
this.capabilities = ["text-to-video", "image-to-video"];
|
|
17203
|
+
this.capabilities = ["text-to-video", "image-to-video", "reference-to-video"];
|
|
17153
17204
|
this.iconUrl = "/icons/fal.svg";
|
|
17154
17205
|
this.isAvailable = true;
|
|
17155
17206
|
}
|
|
@@ -17188,9 +17239,11 @@ var init_FalProvider = __esm({
|
|
|
17188
17239
|
error: `Unknown Seedance variant: ${variant}. Valid: ${Object.keys(ENDPOINT_TEXT_TO_VIDEO).join(", ")}.`
|
|
17189
17240
|
};
|
|
17190
17241
|
}
|
|
17191
|
-
const
|
|
17242
|
+
const references = await normaliseReferences(options?.references, this.client);
|
|
17243
|
+
const hasReferences = references.length > 0;
|
|
17244
|
+
const referenceImage = hasReferences ? void 0 : pickReferenceImageUrl(options?.referenceImage);
|
|
17192
17245
|
const isImageToVideo = !!referenceImage;
|
|
17193
|
-
const endpointId = isImageToVideo ? ENDPOINT_IMAGE_TO_VIDEO[variant] : ENDPOINT_TEXT_TO_VIDEO[variant];
|
|
17246
|
+
const endpointId = hasReferences ? ENDPOINT_REFERENCE_TO_VIDEO[variant] : isImageToVideo ? ENDPOINT_IMAGE_TO_VIDEO[variant] : ENDPOINT_TEXT_TO_VIDEO[variant];
|
|
17194
17247
|
const aspect = normaliseAspect(options?.aspectRatio);
|
|
17195
17248
|
const resolution = normaliseResolution(options?.resolution);
|
|
17196
17249
|
const duration = normaliseDuration(options?.duration);
|
|
@@ -17200,13 +17253,26 @@ var init_FalProvider = __esm({
|
|
|
17200
17253
|
resolution,
|
|
17201
17254
|
duration
|
|
17202
17255
|
};
|
|
17203
|
-
if (
|
|
17256
|
+
if (hasReferences) {
|
|
17257
|
+
const grouped = groupReferences(references);
|
|
17258
|
+
if (grouped.image_urls.length > 0)
|
|
17259
|
+
input.image_urls = grouped.image_urls;
|
|
17260
|
+
if (grouped.video_urls.length > 0)
|
|
17261
|
+
input.video_urls = grouped.video_urls;
|
|
17262
|
+
if (grouped.audio_urls.length > 0)
|
|
17263
|
+
input.audio_urls = grouped.audio_urls;
|
|
17264
|
+
} else if (referenceImage) {
|
|
17204
17265
|
input.image_url = referenceImage;
|
|
17266
|
+
}
|
|
17205
17267
|
if (options?.negativePrompt)
|
|
17206
17268
|
input.negative_prompt = options.negativePrompt;
|
|
17207
17269
|
if (typeof options?.seed === "number")
|
|
17208
17270
|
input.seed = options.seed;
|
|
17209
|
-
if (options?.
|
|
17271
|
+
if (typeof options?.generateAudio === "boolean")
|
|
17272
|
+
input.generate_audio = options.generateAudio;
|
|
17273
|
+
if (options?.endUserId)
|
|
17274
|
+
input.end_user_id = options.endUserId;
|
|
17275
|
+
if (!hasReferences && options?.lastFrame)
|
|
17210
17276
|
input.end_image_url = options.lastFrame;
|
|
17211
17277
|
try {
|
|
17212
17278
|
const out = await this.client.subscribe(endpointId, { input, logs: false });
|
|
@@ -17253,14 +17319,15 @@ var init_fal = __esm({
|
|
|
17253
17319
|
// Review this alias at the 1.0 cut.
|
|
17254
17320
|
aliases: ["fal"],
|
|
17255
17321
|
models: ["seedance-2.0", "seedance-2.0-fast"],
|
|
17256
|
-
capabilities: ["text-to-video", "image-to-video", "native-audio"],
|
|
17322
|
+
capabilities: ["text-to-video", "image-to-video", "reference-to-video", "native-audio"],
|
|
17257
17323
|
apiKey: "fal",
|
|
17258
17324
|
kinds: ["video"],
|
|
17259
17325
|
resolverPriority: { video: 1 },
|
|
17260
17326
|
commandsUnlocked: [
|
|
17261
17327
|
"generate video -p seedance (Seedance 2.0 via fal.ai \u2014 default since v0.57)",
|
|
17262
17328
|
"generate video -p seedance --seedance-model fast (lower-latency variant)",
|
|
17263
|
-
"generate video -p seedance -i <image> (image-to-video)"
|
|
17329
|
+
"generate video -p seedance -i <image> (image-to-video)",
|
|
17330
|
+
"generate video -p seedance --ref-images <images...> (reference-to-video)"
|
|
17264
17331
|
]
|
|
17265
17332
|
});
|
|
17266
17333
|
}
|
|
@@ -26478,7 +26545,7 @@ YAML cues that drive narration, backdrop generation, and timing.
|
|
|
26478
26545
|
|
|
26479
26546
|
\`\`\`yaml
|
|
26480
26547
|
narration: "Introduce the promise in one crisp sentence."
|
|
26481
|
-
backdrop: "
|
|
26548
|
+
backdrop: "Topic-aligned editorial background plate, abstract visual system, no readable text, no logos, no consumer products, clean negative space for HTML overlays"
|
|
26482
26549
|
duration: 4
|
|
26483
26550
|
\`\`\`
|
|
26484
26551
|
|
|
@@ -26489,7 +26556,7 @@ screen and one spoken breath.
|
|
|
26489
26556
|
|
|
26490
26557
|
\`\`\`yaml
|
|
26491
26558
|
narration: "Show the mechanism or proof point that makes the promise believable."
|
|
26492
|
-
backdrop: "
|
|
26559
|
+
backdrop: "Topic-aligned analytical background plate, abstract dashboard structure, no readable text, no product photos, no shoes, no unrelated objects"
|
|
26493
26560
|
duration: 4
|
|
26494
26561
|
\`\`\`
|
|
26495
26562
|
|
|
@@ -26500,7 +26567,7 @@ before/after.
|
|
|
26500
26567
|
|
|
26501
26568
|
\`\`\`yaml
|
|
26502
26569
|
narration: "Close with the action the viewer should remember."
|
|
26503
|
-
backdrop: "Resolved
|
|
26570
|
+
backdrop: "Resolved editorial background plate, confident final composition, clean negative space, no readable text, no logos, no unrelated products"
|
|
26504
26571
|
duration: 4
|
|
26505
26572
|
\`\`\`
|
|
26506
26573
|
|
|
@@ -26553,6 +26620,33 @@ consult this file \u2014 run the generate command directly.
|
|
|
26553
26620
|
Browse named styles: \`vibe scene list-styles\`. Re-seed from one with
|
|
26554
26621
|
\`vibe scene init . --visual-style "Swiss Pulse"\` (idempotent).
|
|
26555
26622
|
|
|
26623
|
+
## Brief and local media
|
|
26624
|
+
|
|
26625
|
+
\`brief.md\` is raw intent, not a strict schema. It may contain messy notes,
|
|
26626
|
+
pasted research, links, product requirements, or a one-line idea. \`vibe init
|
|
26627
|
+
--from brief.md\` uses it only to seed \`STORYBOARD.md\` and \`DESIGN.md\`;
|
|
26628
|
+
after init, those two files are the working source of truth.
|
|
26629
|
+
|
|
26630
|
+
Use \`media/\` for user-provided source files: product photos, screenshots,
|
|
26631
|
+
logos, B-roll, recorded narration, or reference clips. Keep those inputs
|
|
26632
|
+
inside this project so build references stay project-relative. Do not put user
|
|
26633
|
+
media in \`references/\`; that directory is reserved for local composition
|
|
26634
|
+
rules installed by VibeFrame.
|
|
26635
|
+
|
|
26636
|
+
When a beat should reuse a local file, reference it from \`STORYBOARD.md\`
|
|
26637
|
+
with a project-relative path:
|
|
26638
|
+
|
|
26639
|
+
\`\`\`yaml
|
|
26640
|
+
backdrop: "media/product-shot.png" # existing still image
|
|
26641
|
+
video: "media/broll.mp4" # existing video/B-roll
|
|
26642
|
+
narration: "media/voice.wav" # existing recorded narration
|
|
26643
|
+
asset: "media/logo.png" # generic local asset reference
|
|
26644
|
+
\`\`\`
|
|
26645
|
+
|
|
26646
|
+
Use text cues when you want VibeFrame to generate an asset. Use path cues
|
|
26647
|
+
when you want VibeFrame to reuse a local file. Avoid absolute paths or parent
|
|
26648
|
+
directory references; copy files into \`media/\` first.
|
|
26649
|
+
|
|
26556
26650
|
## Provider keys and project scope
|
|
26557
26651
|
|
|
26558
26652
|
Use VibeFrame CLI generation for project assets:
|
|
@@ -26600,9 +26694,11 @@ the framework-level minimum, not the cinematic craft layer.
|
|
|
26600
26694
|
|
|
26601
26695
|
- \`DESIGN.md\` \u2014 visual identity contract (palette, type, motion, transitions)
|
|
26602
26696
|
- \`STORYBOARD.md\` \u2014 per-beat narration/backdrop/duration cues for \`vibe build\`
|
|
26697
|
+
- \`media/\` \u2014 user-provided source files (photos, logos, clips, voice recordings)
|
|
26603
26698
|
- \`index.html\` \u2014 root composition (timeline)
|
|
26604
26699
|
- \`compositions/scene-*.html\` \u2014 per-scene HTML authored by you or the agent
|
|
26605
|
-
- \`assets/\` \u2014
|
|
26700
|
+
- \`assets/\` \u2014 generated/canonical build media (narration audio, images, video)
|
|
26701
|
+
- \`references/\` \u2014 composition rule docs installed by VibeFrame, not user media
|
|
26606
26702
|
- \`transcript.json\` \u2014 Whisper word-level transcript (if narration exists)
|
|
26607
26703
|
- \`hyperframes.json\` \u2014 HF registry config (speak to both toolchains)
|
|
26608
26704
|
- \`vibe.config.json\` \u2014 canonical VibeFrame config (providers, budget)
|
|
@@ -26633,7 +26729,11 @@ npx hyperframes render
|
|
|
26633
26729
|
\`\`\`
|
|
26634
26730
|
4. Videos use \`muted\` with a separate \`<audio>\` element for the audio track.
|
|
26635
26731
|
5. Sub-compositions use \`data-composition-src="compositions/file.html"\`.
|
|
26636
|
-
6.
|
|
26732
|
+
6. For render-stable text, do not apply continuous \`scale\`, \`x\`, \`y\`, or
|
|
26733
|
+
\`filter\` tweens to \`.scene-content\` or any ancestor containing live text.
|
|
26734
|
+
Animate background/media layers instead; text/cards should enter briefly and
|
|
26735
|
+
then hold still at their final CSS positions.
|
|
26736
|
+
7. Only deterministic logic \u2014 no \`Date.now()\`, \`Math.random()\`, or network fetches.
|
|
26637
26737
|
|
|
26638
26738
|
## Linting \u2014 run after changes
|
|
26639
26739
|
|
|
@@ -26746,7 +26846,11 @@ async function scaffoldSceneProject(opts) {
|
|
|
26746
26846
|
if (await pathExists(metaPath)) {
|
|
26747
26847
|
skipped2.push(metaPath);
|
|
26748
26848
|
} else {
|
|
26749
|
-
await writeFile4(
|
|
26849
|
+
await writeFile4(
|
|
26850
|
+
metaPath,
|
|
26851
|
+
JSON.stringify(buildHyperframesMeta(name, now), null, 2) + "\n",
|
|
26852
|
+
"utf-8"
|
|
26853
|
+
);
|
|
26750
26854
|
created.push(metaPath);
|
|
26751
26855
|
}
|
|
26752
26856
|
const rootPath = resolve7(dir, "index.html");
|
|
@@ -26761,11 +26865,7 @@ async function scaffoldSceneProject(opts) {
|
|
|
26761
26865
|
if (await pathExists(vibeConfigJsonPath)) {
|
|
26762
26866
|
skipped2.push(vibeConfigJsonPath);
|
|
26763
26867
|
} else {
|
|
26764
|
-
await writeFile4(
|
|
26765
|
-
vibeConfigJsonPath,
|
|
26766
|
-
projectConfigJson({ name, aspect }),
|
|
26767
|
-
"utf-8"
|
|
26768
|
-
);
|
|
26868
|
+
await writeFile4(vibeConfigJsonPath, projectConfigJson({ name, aspect }), "utf-8");
|
|
26769
26869
|
created.push(vibeConfigJsonPath);
|
|
26770
26870
|
}
|
|
26771
26871
|
const vibePath = resolve7(dir, "vibe.project.yaml");
|
|
@@ -26804,11 +26904,7 @@ ${existing}`, "utf-8");
|
|
|
26804
26904
|
if (await pathExists(designPath)) {
|
|
26805
26905
|
skipped2.push(designPath);
|
|
26806
26906
|
} else {
|
|
26807
|
-
await writeFile4(
|
|
26808
|
-
designPath,
|
|
26809
|
-
buildDesignMd({ name, style: opts.visualStyle }),
|
|
26810
|
-
"utf-8"
|
|
26811
|
-
);
|
|
26907
|
+
await writeFile4(designPath, buildDesignMd({ name, style: opts.visualStyle }), "utf-8");
|
|
26812
26908
|
created.push(designPath);
|
|
26813
26909
|
}
|
|
26814
26910
|
const storyboardPath2 = resolve7(dir, "STORYBOARD.md");
|
|
@@ -29061,13 +29157,18 @@ Requirements (non-negotiable):
|
|
|
29061
29157
|
producer's seek lands past the timeline's natural end and visibility state
|
|
29062
29158
|
goes stale \u2014 the hold phase renders BLACK. Anchor the timeline to the full
|
|
29063
29159
|
beat duration via either:
|
|
29064
|
-
1. A subtle idle motion spanning 0\u2192duration on a
|
|
29065
|
-
\`tl.fromTo(".
|
|
29160
|
+
1. A subtle idle motion spanning 0\u2192duration on a background/media layer,
|
|
29161
|
+
e.g. \`tl.fromTo(".backdrop", { scale: 1.0 }, { scale: 1.015, duration: <beat>, ease: "none" }, 0);\`
|
|
29066
29162
|
(Ken-Burns, breathing opacity, gradient drift \u2014 should be barely
|
|
29067
29163
|
perceptible so it doesn't compete with entry/exit beats).
|
|
29068
29164
|
2. OR an explicit \`tl.set(target, { ...natural state... }, <beat - 0.001>)\`
|
|
29069
29165
|
anchor at the end.
|
|
29070
29166
|
This is the #2 source of "text disappears mid-beat" bugs after \`.clip\` sizing.
|
|
29167
|
+
- Do not apply continuous \`scale\`, \`x\`, \`y\`, \`filter\`, or other transform
|
|
29168
|
+
tweens to \`.scene-content\` or any ancestor that contains live text/cards.
|
|
29169
|
+
Animate the backdrop/media plane instead; let text enter briefly, then hold
|
|
29170
|
+
still at its final CSS position. Continuous transforms on text ancestors can
|
|
29171
|
+
create subpixel shimmer in screenshot-captured renders.
|
|
29071
29172
|
- Timed children inside the composition have \`class="clip"\` plus
|
|
29072
29173
|
\`data-start\`, \`data-duration\`, \`data-track-index\`.
|
|
29073
29174
|
- If \`assets/backdrop-${ctx.beat.id}.png\` exists, use that local file as the
|
|
@@ -29137,7 +29238,9 @@ Reference shape (verbatim \u2014 match this skeleton exactly, no DOCTYPE / html
|
|
|
29137
29238
|
const tl = gsap.timeline({ paused: true });
|
|
29138
29239
|
// Idle motion spanning full beat duration \u2014 required to keep timeline
|
|
29139
29240
|
// length aligned with data-duration (otherwise hold phase goes black).
|
|
29140
|
-
|
|
29241
|
+
// Keep continuous motion on the background/media layer so live text does
|
|
29242
|
+
// not shimmer from subpixel resampling.
|
|
29243
|
+
tl.fromTo(".backdrop", { scale: 1.0 }, { scale: 1.015, duration: <sec>, ease: "none" }, 0);
|
|
29141
29244
|
// entry tweens
|
|
29142
29245
|
window.__timelines["${compositionId}"] = tl;
|
|
29143
29246
|
</script>
|
|
@@ -33809,6 +33912,9 @@ async function executeVideoGenerate(options) {
|
|
|
33809
33912
|
prompt: prompt3,
|
|
33810
33913
|
provider = "kling",
|
|
33811
33914
|
image,
|
|
33915
|
+
refImages,
|
|
33916
|
+
refVideos,
|
|
33917
|
+
refAudio,
|
|
33812
33918
|
duration = 5,
|
|
33813
33919
|
ratio = "16:9",
|
|
33814
33920
|
seed,
|
|
@@ -33817,6 +33923,7 @@ async function executeVideoGenerate(options) {
|
|
|
33817
33923
|
resolution,
|
|
33818
33924
|
veoModel = "3.1-fast",
|
|
33819
33925
|
seedanceModel = "quality",
|
|
33926
|
+
generateAudio,
|
|
33820
33927
|
output,
|
|
33821
33928
|
wait = true,
|
|
33822
33929
|
apiKey
|
|
@@ -33855,8 +33962,13 @@ async function executeVideoGenerate(options) {
|
|
|
33855
33962
|
if (provider === "seedance" || provider === "fal") {
|
|
33856
33963
|
const fal = new FalProvider();
|
|
33857
33964
|
await fal.initialize({ apiKey: key });
|
|
33965
|
+
const references = await prepareSeedanceReferences({
|
|
33966
|
+
refImages,
|
|
33967
|
+
refVideos,
|
|
33968
|
+
refAudio
|
|
33969
|
+
});
|
|
33858
33970
|
let falImage = referenceImage;
|
|
33859
|
-
if (falImage && falImage.startsWith("data:")) {
|
|
33971
|
+
if (falImage && falImage.startsWith("data:") && references.length === 0) {
|
|
33860
33972
|
const uploadHost = await resolveUploadHost();
|
|
33861
33973
|
const upload = await uploadHost.uploadImage(referenceImageBuffer, {
|
|
33862
33974
|
filename: image,
|
|
@@ -33867,11 +33979,14 @@ async function executeVideoGenerate(options) {
|
|
|
33867
33979
|
const model = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
|
|
33868
33980
|
const result = await fal.generateVideo(prompt3, {
|
|
33869
33981
|
prompt: prompt3,
|
|
33870
|
-
referenceImage: falImage,
|
|
33982
|
+
referenceImage: references.length > 0 ? void 0 : falImage,
|
|
33983
|
+
references: references.length > 0 ? references : void 0,
|
|
33871
33984
|
duration,
|
|
33872
33985
|
aspectRatio: ratio,
|
|
33873
33986
|
negativePrompt: negative,
|
|
33874
|
-
model
|
|
33987
|
+
model,
|
|
33988
|
+
resolution,
|
|
33989
|
+
generateAudio
|
|
33875
33990
|
});
|
|
33876
33991
|
if (result.status === "failed")
|
|
33877
33992
|
return { success: false, error: result.error || "Seedance generation failed" };
|
|
@@ -34048,6 +34163,54 @@ async function executeVideoGenerate(options) {
|
|
|
34048
34163
|
};
|
|
34049
34164
|
}
|
|
34050
34165
|
}
|
|
34166
|
+
async function prepareSeedanceReferences(opts) {
|
|
34167
|
+
const references = [];
|
|
34168
|
+
for (const sourcePath of opts.refImages ?? []) {
|
|
34169
|
+
references.push({
|
|
34170
|
+
kind: "image",
|
|
34171
|
+
url: await fileInputToUrlOrDataUri(sourcePath, "image/png"),
|
|
34172
|
+
sourcePath
|
|
34173
|
+
});
|
|
34174
|
+
}
|
|
34175
|
+
for (const sourcePath of opts.refVideos ?? []) {
|
|
34176
|
+
references.push({
|
|
34177
|
+
kind: "video",
|
|
34178
|
+
url: await fileInputToUrlOrDataUri(sourcePath, "video/mp4"),
|
|
34179
|
+
sourcePath
|
|
34180
|
+
});
|
|
34181
|
+
}
|
|
34182
|
+
for (const sourcePath of opts.refAudio ?? []) {
|
|
34183
|
+
references.push({
|
|
34184
|
+
kind: "audio",
|
|
34185
|
+
url: await fileInputToUrlOrDataUri(sourcePath, "audio/mpeg"),
|
|
34186
|
+
sourcePath
|
|
34187
|
+
});
|
|
34188
|
+
}
|
|
34189
|
+
return references;
|
|
34190
|
+
}
|
|
34191
|
+
async function fileInputToUrlOrDataUri(input, fallbackMimeType) {
|
|
34192
|
+
if (input.startsWith("http://") || input.startsWith("https://") || input.startsWith("data:")) {
|
|
34193
|
+
return input;
|
|
34194
|
+
}
|
|
34195
|
+
const absPath = resolve23(process.cwd(), input);
|
|
34196
|
+
const buffer = await readFile15(absPath);
|
|
34197
|
+
return `data:${mimeTypeForPath(input, fallbackMimeType)};base64,${buffer.toString("base64")}`;
|
|
34198
|
+
}
|
|
34199
|
+
function mimeTypeForPath(path2, fallback2) {
|
|
34200
|
+
const ext = path2.toLowerCase().split(".").pop();
|
|
34201
|
+
const mimeTypes2 = {
|
|
34202
|
+
jpg: "image/jpeg",
|
|
34203
|
+
jpeg: "image/jpeg",
|
|
34204
|
+
png: "image/png",
|
|
34205
|
+
gif: "image/gif",
|
|
34206
|
+
webp: "image/webp",
|
|
34207
|
+
mp4: "video/mp4",
|
|
34208
|
+
mov: "video/quicktime",
|
|
34209
|
+
mp3: "audio/mpeg",
|
|
34210
|
+
wav: "audio/wav"
|
|
34211
|
+
};
|
|
34212
|
+
return mimeTypes2[ext || ""] || fallback2;
|
|
34213
|
+
}
|
|
34051
34214
|
async function executeVideoStatus(options) {
|
|
34052
34215
|
const {
|
|
34053
34216
|
taskId,
|
|
@@ -36301,10 +36464,10 @@ var init_provider_resolver = __esm({
|
|
|
36301
36464
|
|
|
36302
36465
|
// src/commands/_shared/openai-image.ts
|
|
36303
36466
|
function resolveOpenAIImageModel(modelAlias) {
|
|
36304
|
-
const
|
|
36467
|
+
const isGptImage15 = modelAlias === "1.5" || modelAlias === "gpt-image-1.5";
|
|
36305
36468
|
return {
|
|
36306
|
-
openaiModel:
|
|
36307
|
-
modelLabel:
|
|
36469
|
+
openaiModel: isGptImage15 ? "gpt-image-1.5" : "gpt-image-2",
|
|
36470
|
+
modelLabel: isGptImage15 ? "GPT Image 1.5" : "GPT Image 2"
|
|
36308
36471
|
};
|
|
36309
36472
|
}
|
|
36310
36473
|
async function executeOpenAIImageGenerate(prompt3, options, ctx) {
|
|
@@ -36341,7 +36504,7 @@ function registerImageCommand(parent) {
|
|
|
36341
36504
|
"1:1"
|
|
36342
36505
|
).option("--quality <quality>", "Quality: standard, hd (openai only)", "standard").option("--style <style>", "Style: vivid, natural (openai only)", "vivid").option("--count <n>", "Number of images to generate", "1").option(
|
|
36343
36506
|
"-m, --model <model>",
|
|
36344
|
-
"Model. Gemini: flash, 3.1-flash, latest, pro. OpenAI:
|
|
36507
|
+
"Model. Gemini: flash, 3.1-flash, latest, pro. OpenAI: 2 (default), 1.5"
|
|
36345
36508
|
).option("--dry-run", "Preview parameters without executing").addHelpText(
|
|
36346
36509
|
"after",
|
|
36347
36510
|
`
|
|
@@ -37775,8 +37938,8 @@ function registerVideoCommand(parent) {
|
|
|
37775
37938
|
"quality"
|
|
37776
37939
|
).option("--negative <prompt>", "Negative prompt - what to avoid (Kling/Veo)").option("--resolution <res>", "Video resolution: 720p, 1080p, 4k (Veo only)").option("--last-frame <path>", "Last frame image for frame interpolation (Veo only)").option(
|
|
37777
37940
|
"--ref-images <paths...>",
|
|
37778
|
-
"Reference images for
|
|
37779
|
-
).option("--person <mode>", "Person generation: allow_all, allow_adult (Veo only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast (default: 3.1-fast)", "3.1-fast").option(
|
|
37941
|
+
"Reference images for Seedance reference-to-video or Veo character consistency"
|
|
37942
|
+
).option("--ref-videos <paths...>", "Reference videos for Seedance reference-to-video").option("--ref-audio <paths...>", "Reference audio for Seedance reference-to-video").option("--no-generate-audio", "Disable native audio when the provider supports it").option("--person <mode>", "Person generation: allow_all, allow_adult (Veo only)").option("--veo-model <model>", "Veo model: 3.0, 3.1, 3.1-fast (default: 3.1-fast)", "3.1-fast").option(
|
|
37780
37943
|
"--runway-model <model>",
|
|
37781
37944
|
"Runway model: gen4.5 (default, text+image-to-video), gen4_turbo (image-to-video only)",
|
|
37782
37945
|
"gen4.5"
|
|
@@ -37919,7 +38082,11 @@ Examples:
|
|
|
37919
38082
|
negative: options.negative,
|
|
37920
38083
|
resolution: options.resolution,
|
|
37921
38084
|
veoModel: options.veoModel,
|
|
37922
|
-
seedanceModel: options.seedanceModel
|
|
38085
|
+
seedanceModel: options.seedanceModel,
|
|
38086
|
+
refImages: options.refImages,
|
|
38087
|
+
refVideos: options.refVideos,
|
|
38088
|
+
refAudio: options.refAudio,
|
|
38089
|
+
generateAudio: options.generateAudio
|
|
37923
38090
|
}
|
|
37924
38091
|
}
|
|
37925
38092
|
});
|
|
@@ -38206,8 +38373,13 @@ Examples:
|
|
|
38206
38373
|
} else if (provider === "seedance") {
|
|
38207
38374
|
const fal = new FalProvider();
|
|
38208
38375
|
await fal.initialize({ apiKey });
|
|
38376
|
+
const seedanceReferences = await prepareSeedanceReferences2({
|
|
38377
|
+
refImages: options.refImages,
|
|
38378
|
+
refVideos: options.refVideos,
|
|
38379
|
+
refAudio: options.refAudio
|
|
38380
|
+
});
|
|
38209
38381
|
let falImage = referenceImage;
|
|
38210
|
-
if (falImage && falImage.startsWith("data:")) {
|
|
38382
|
+
if (falImage && falImage.startsWith("data:") && seedanceReferences.length === 0) {
|
|
38211
38383
|
try {
|
|
38212
38384
|
const uploadHost = await resolveUploadHost();
|
|
38213
38385
|
spinner2.text = `Uploading image via ${uploadHost.provider} for Seedance...`;
|
|
@@ -38230,11 +38402,14 @@ Examples:
|
|
|
38230
38402
|
const falModel = seedanceModel === "fast" || seedanceModel === "seedance-2.0-fast" ? "seedance-2.0-fast" : "seedance-2.0";
|
|
38231
38403
|
result = await fal.generateVideo(prompt3, {
|
|
38232
38404
|
prompt: prompt3,
|
|
38233
|
-
referenceImage: falImage,
|
|
38405
|
+
referenceImage: seedanceReferences.length > 0 ? void 0 : falImage,
|
|
38406
|
+
references: seedanceReferences.length > 0 ? seedanceReferences : void 0,
|
|
38234
38407
|
duration: options.duration ? parseInt(options.duration) : void 0,
|
|
38235
38408
|
aspectRatio: options.ratio,
|
|
38236
38409
|
negativePrompt: options.negative,
|
|
38237
|
-
model: falModel
|
|
38410
|
+
model: falModel,
|
|
38411
|
+
resolution: options.resolution,
|
|
38412
|
+
generateAudio: options.generateAudio
|
|
38238
38413
|
});
|
|
38239
38414
|
finalResult = result;
|
|
38240
38415
|
}
|
|
@@ -38289,6 +38464,54 @@ Examples:
|
|
|
38289
38464
|
}
|
|
38290
38465
|
});
|
|
38291
38466
|
}
|
|
38467
|
+
async function prepareSeedanceReferences2(opts) {
|
|
38468
|
+
const references = [];
|
|
38469
|
+
for (const sourcePath of opts.refImages ?? []) {
|
|
38470
|
+
references.push({
|
|
38471
|
+
kind: "image",
|
|
38472
|
+
url: await fileInputToUrlOrDataUri2(sourcePath, "image/png"),
|
|
38473
|
+
sourcePath
|
|
38474
|
+
});
|
|
38475
|
+
}
|
|
38476
|
+
for (const sourcePath of opts.refVideos ?? []) {
|
|
38477
|
+
references.push({
|
|
38478
|
+
kind: "video",
|
|
38479
|
+
url: await fileInputToUrlOrDataUri2(sourcePath, "video/mp4"),
|
|
38480
|
+
sourcePath
|
|
38481
|
+
});
|
|
38482
|
+
}
|
|
38483
|
+
for (const sourcePath of opts.refAudio ?? []) {
|
|
38484
|
+
references.push({
|
|
38485
|
+
kind: "audio",
|
|
38486
|
+
url: await fileInputToUrlOrDataUri2(sourcePath, "audio/mpeg"),
|
|
38487
|
+
sourcePath
|
|
38488
|
+
});
|
|
38489
|
+
}
|
|
38490
|
+
return references;
|
|
38491
|
+
}
|
|
38492
|
+
async function fileInputToUrlOrDataUri2(input, fallbackMimeType) {
|
|
38493
|
+
if (input.startsWith("http://") || input.startsWith("https://") || input.startsWith("data:")) {
|
|
38494
|
+
return input;
|
|
38495
|
+
}
|
|
38496
|
+
const absPath = resolve31(process.cwd(), input);
|
|
38497
|
+
const buffer = await readFile18(absPath);
|
|
38498
|
+
return `data:${mimeTypeForPath2(input, fallbackMimeType)};base64,${buffer.toString("base64")}`;
|
|
38499
|
+
}
|
|
38500
|
+
function mimeTypeForPath2(path2, fallback2) {
|
|
38501
|
+
const ext = path2.toLowerCase().split(".").pop();
|
|
38502
|
+
const mimeTypes2 = {
|
|
38503
|
+
jpg: "image/jpeg",
|
|
38504
|
+
jpeg: "image/jpeg",
|
|
38505
|
+
png: "image/png",
|
|
38506
|
+
gif: "image/gif",
|
|
38507
|
+
webp: "image/webp",
|
|
38508
|
+
mp4: "video/mp4",
|
|
38509
|
+
mov: "video/quicktime",
|
|
38510
|
+
mp3: "audio/mpeg",
|
|
38511
|
+
wav: "audio/wav"
|
|
38512
|
+
};
|
|
38513
|
+
return mimeTypes2[ext || ""] || fallback2;
|
|
38514
|
+
}
|
|
38292
38515
|
async function recordVideoNoWaitJob(opts) {
|
|
38293
38516
|
return createAndWriteJobRecord({
|
|
38294
38517
|
jobType: "generate-video",
|
|
@@ -58370,6 +58593,32 @@ var init_build_cache = __esm({
|
|
|
58370
58593
|
}
|
|
58371
58594
|
});
|
|
58372
58595
|
|
|
58596
|
+
// src/commands/_shared/build-backdrop-prompt.ts
|
|
58597
|
+
function augmentBackdropPrompt(cue) {
|
|
58598
|
+
const trimmed = cue.trim();
|
|
58599
|
+
const lower = trimmed.toLowerCase();
|
|
58600
|
+
const requestsTextOrMarks = /\b(text|typography|title|headline|label|caption|logo|logos|wordmark|brand mark|brand marks)\b/.test(lower);
|
|
58601
|
+
const forbidsTextOrMarks = /\b(no|without|avoid)\s+(readable\s+)?(text|typography|titles?|headlines?|labels?|captions?|brand\s+logos?|logos?|wordmarks?|brand\s+marks?)\b/.test(
|
|
58602
|
+
lower
|
|
58603
|
+
);
|
|
58604
|
+
const allowsTextOrMarks = requestsTextOrMarks && !forbidsTextOrMarks;
|
|
58605
|
+
const overlayContract = allowsTextOrMarks ? "The image is a video background or end-card plate; do not add any text, logos, charts, or UI beyond what the scene cue explicitly requests." : "The image is a background only; HTML overlays will provide all final text, charts, logos, and UI labels.";
|
|
58606
|
+
const textRule = allowsTextOrMarks ? "If text, logos, or brand marks are explicitly requested, keep them minimal, legible, and do not invent extras." : "No readable text, labels, UI copy, logos, brand marks, watermarks, or invented typography.";
|
|
58607
|
+
return [
|
|
58608
|
+
"Create a 16:9 video background plate for a HyperFrames scene.",
|
|
58609
|
+
overlayContract,
|
|
58610
|
+
`Scene cue: ${trimmed}`,
|
|
58611
|
+
textRule,
|
|
58612
|
+
"Avoid unrelated consumer product photography, shoes, packaging, food, people, celebrity faces, advertisements, and random objects unless explicitly requested by the scene cue.",
|
|
58613
|
+
"Leave generous negative space for overlay text and cards. Keep the result topic-aligned, editorial, cinematic, and non-distracting."
|
|
58614
|
+
].join(" ");
|
|
58615
|
+
}
|
|
58616
|
+
var init_build_backdrop_prompt = __esm({
|
|
58617
|
+
"src/commands/_shared/build-backdrop-prompt.ts"() {
|
|
58618
|
+
"use strict";
|
|
58619
|
+
}
|
|
58620
|
+
});
|
|
58621
|
+
|
|
58373
58622
|
// src/commands/_shared/build-plan.ts
|
|
58374
58623
|
import { existsSync as existsSync44 } from "node:fs";
|
|
58375
58624
|
import { readFile as readFile40 } from "node:fs/promises";
|
|
@@ -58463,6 +58712,7 @@ async function createBuildPlan(opts) {
|
|
|
58463
58712
|
const voice = stringOrUndefined3(cue.voice) ?? resolved.voice;
|
|
58464
58713
|
const narrationText = stringOrUndefined3(cue.narration);
|
|
58465
58714
|
const backdropPrompt = stringOrUndefined3(cue.backdrop);
|
|
58715
|
+
const augmentedBackdropPrompt = backdropPrompt ? augmentBackdropPrompt(backdropPrompt) : null;
|
|
58466
58716
|
const videoPrompt = stringOrUndefined3(cue.video);
|
|
58467
58717
|
const musicPrompt = stringOrUndefined3(cue.music);
|
|
58468
58718
|
const genericReference = resolveGenericAssetReference(projectDir, cue.asset);
|
|
@@ -58482,9 +58732,9 @@ async function createBuildPlan(opts) {
|
|
|
58482
58732
|
voice,
|
|
58483
58733
|
ext: resolved.narration.resolved === "elevenlabs" ? "mp3" : "wav"
|
|
58484
58734
|
}) : null;
|
|
58485
|
-
const backdropCache =
|
|
58735
|
+
const backdropCache = augmentedBackdropPrompt && !backdropReference ? backdropCacheDescriptor({
|
|
58486
58736
|
beatId: beat.id,
|
|
58487
|
-
cue:
|
|
58737
|
+
cue: augmentedBackdropPrompt,
|
|
58488
58738
|
provider: resolved.image.resolved,
|
|
58489
58739
|
quality: imageQuality,
|
|
58490
58740
|
size: imageSize2,
|
|
@@ -59014,6 +59264,7 @@ var init_build_plan = __esm({
|
|
|
59014
59264
|
init_build_asset_reference();
|
|
59015
59265
|
init_build_cache();
|
|
59016
59266
|
init_build_asset_metadata();
|
|
59267
|
+
init_build_backdrop_prompt();
|
|
59017
59268
|
init_composer_resolve();
|
|
59018
59269
|
init_storyboard_parse();
|
|
59019
59270
|
init_project_config();
|
|
@@ -60428,8 +60679,9 @@ async function dispatchNarration(beat, ctx) {
|
|
|
60428
60679
|
async function dispatchBackdrop(beat, ctx) {
|
|
60429
60680
|
const reference = assetReferenceForBeat(ctx.projectDir, "backdrop", beat);
|
|
60430
60681
|
if (reference) return referencePrimitiveOutcome("backdrop", beat, ctx, reference);
|
|
60431
|
-
const
|
|
60432
|
-
if (!
|
|
60682
|
+
const cue = stringOrUndefined4(beat.cues?.backdrop);
|
|
60683
|
+
if (!cue) return { status: "no-cue" };
|
|
60684
|
+
const prompt3 = augmentBackdropPrompt(cue);
|
|
60433
60685
|
const rel = `assets/backdrop-${beat.id}.png`;
|
|
60434
60686
|
const abs = join22(ctx.projectDir, rel);
|
|
60435
60687
|
const size = ctx.imageSize ?? "1536x1024";
|
|
@@ -61407,6 +61659,7 @@ var init_scene_build = __esm({
|
|
|
61407
61659
|
init_root_sync();
|
|
61408
61660
|
init_build_cache();
|
|
61409
61661
|
init_build_asset_metadata();
|
|
61662
|
+
init_build_backdrop_prompt();
|
|
61410
61663
|
init_ai_video();
|
|
61411
61664
|
init_music();
|
|
61412
61665
|
init_status_jobs();
|
|
@@ -68156,6 +68409,9 @@ var init_generate2 = __esm({
|
|
|
68156
68409
|
"Video provider (default: seedance when FAL_API_KEY is configured, otherwise first configured provider)"
|
|
68157
68410
|
),
|
|
68158
68411
|
image: external_exports.string().optional().describe("Reference image path for image-to-video"),
|
|
68412
|
+
refImages: external_exports.array(external_exports.string()).optional().describe("Reference images for Seedance reference-to-video"),
|
|
68413
|
+
refVideos: external_exports.array(external_exports.string()).optional().describe("Reference videos for Seedance reference-to-video"),
|
|
68414
|
+
refAudio: external_exports.array(external_exports.string()).optional().describe("Reference audio files for Seedance reference-to-video"),
|
|
68159
68415
|
duration: external_exports.number().optional().describe("Duration in seconds (default: 5; Seedance accepts 4-15)"),
|
|
68160
68416
|
ratio: external_exports.string().optional().describe("Aspect ratio: 16:9, 9:16, 1:1 (default: 16:9)"),
|
|
68161
68417
|
mode: external_exports.string().optional().describe("Kling mode: std or pro"),
|
|
@@ -68164,6 +68420,7 @@ var init_generate2 = __esm({
|
|
|
68164
68420
|
veoModel: external_exports.string().optional().describe("Veo model: 3.0, 3.1, 3.1-fast"),
|
|
68165
68421
|
runwayModel: external_exports.string().optional().describe("Runway model: gen4.5, gen4_turbo"),
|
|
68166
68422
|
seedanceModel: external_exports.string().optional().describe("Seedance variant: quality or fast (fal.ai only)"),
|
|
68423
|
+
generateAudio: external_exports.boolean().optional().describe("Generate native synchronized audio when supported"),
|
|
68167
68424
|
output: external_exports.string().optional().describe("Output file path (downloads video)"),
|
|
68168
68425
|
wait: external_exports.boolean().optional().describe("Wait for completion (default: true)")
|
|
68169
68426
|
}),
|
|
@@ -71397,7 +71654,7 @@ init_source();
|
|
|
71397
71654
|
// package.json
|
|
71398
71655
|
var package_default = {
|
|
71399
71656
|
name: "@vibeframe/cli",
|
|
71400
|
-
version: "0.
|
|
71657
|
+
version: "0.105.1",
|
|
71401
71658
|
description: "VibeFrame CLI - natural language video editing from the terminal",
|
|
71402
71659
|
private: false,
|
|
71403
71660
|
license: "MIT",
|
|
@@ -79966,7 +80223,7 @@ function fourBeatDraft(opts) {
|
|
|
79966
80223
|
title: scenic ? "First light" : "Hook",
|
|
79967
80224
|
duration: opts.durations[0],
|
|
79968
80225
|
narration: scenic ? `The first light reaches ${opts.product} before the day begins.` : `${opts.product} starts with one clear promise.`,
|
|
79969
|
-
backdrop: scenic ? `misty mountain ridge at sunrise, golden light, fog layers, cinematic ${opts.theme}` : `
|
|
80226
|
+
backdrop: scenic ? `misty mountain ridge at sunrise, golden light, fog layers, cinematic ${opts.theme}` : `topic-aligned editorial background plate for ${opts.product}, ${opts.theme}, no readable text, no logos, no unrelated consumer products, clean negative space for HTML overlays`,
|
|
79970
80227
|
motion: scenic ? "quiet title reveal, soft parallax, slow camera drift" : "large readable headline, restrained camera push, immediate value reveal",
|
|
79971
80228
|
body: scenic ? "Show the mountain identity immediately. Keep text minimal." : "Make the value obvious in the first beat. Avoid setup that delays the payoff."
|
|
79972
80229
|
},
|
|
@@ -79975,7 +80232,7 @@ function fourBeatDraft(opts) {
|
|
|
79975
80232
|
title: scenic ? "The path appears" : "Proof",
|
|
79976
80233
|
duration: opts.durations[1],
|
|
79977
80234
|
narration: scenic ? "A clear path appears when every step is visible." : "The proof appears when each step is visible.",
|
|
79978
|
-
backdrop: scenic ? "ridge trail emerging through fog, warm sunrise edge light, high contrast" : `
|
|
80235
|
+
backdrop: scenic ? "ridge trail emerging through fog, warm sunrise edge light, high contrast" : `topic-aligned proof background plate for ${opts.product}, abstract workflow structure, no readable text, no product photos, no shoes, no unrelated objects`,
|
|
79979
80236
|
motion: scenic ? "thin line traces the path, small labels fade in" : "interface panels or proof points slide into place with precise transitions",
|
|
79980
80237
|
body: "Connect the visual proof to the promise in one focused moment."
|
|
79981
80238
|
},
|
|
@@ -79993,7 +80250,7 @@ function fourBeatDraft(opts) {
|
|
|
79993
80250
|
title: scenic ? "Day one" : "Close",
|
|
79994
80251
|
duration: opts.durations[3],
|
|
79995
80252
|
narration: scenic ? `${opts.product}. Day one begins here.` : `${opts.product} is ready to show.`,
|
|
79996
|
-
backdrop: scenic ? "wide mountain ridge at peak golden hour, valley filled with warm light" : `resolved
|
|
80253
|
+
backdrop: scenic ? "wide mountain ridge at peak golden hour, valley filled with warm light" : `resolved editorial background plate for ${opts.product}, clean negative space, confident lockup, no readable text, no logos, no unrelated products`,
|
|
79997
80254
|
motion: "clean final title, slow settling motion, gentle fade to the final frame",
|
|
79998
80255
|
body: "End with a quiet branded title and no new idea."
|
|
79999
80256
|
}
|
|
@@ -80006,7 +80263,7 @@ function threeBeatDraft(opts) {
|
|
|
80006
80263
|
title: "Hook",
|
|
80007
80264
|
duration: opts.durations[0],
|
|
80008
80265
|
narration: `${opts.product} starts with one clear promise.`,
|
|
80009
|
-
backdrop: `
|
|
80266
|
+
backdrop: `topic-aligned editorial background plate for ${opts.product}, ${opts.theme}, no readable text, no logos, no unrelated consumer products, clean negative space for HTML overlays`,
|
|
80010
80267
|
motion: "large readable headline, restrained camera push, immediate value reveal",
|
|
80011
80268
|
body: "Make the value obvious in one beat."
|
|
80012
80269
|
},
|
|
@@ -80015,7 +80272,7 @@ function threeBeatDraft(opts) {
|
|
|
80015
80272
|
title: "Proof",
|
|
80016
80273
|
duration: opts.durations[1],
|
|
80017
80274
|
narration: "The proof appears when each step is visible.",
|
|
80018
|
-
backdrop: `
|
|
80275
|
+
backdrop: `topic-aligned proof background plate for ${opts.product}, abstract workflow structure, no readable text, no product photos, no shoes, no unrelated objects`,
|
|
80019
80276
|
motion: "layered panels, highlighted cause and effect, precise transitions",
|
|
80020
80277
|
body: "Show the mechanism, metric, or before/after that makes the promise believable."
|
|
80021
80278
|
},
|
|
@@ -80024,7 +80281,7 @@ function threeBeatDraft(opts) {
|
|
|
80024
80281
|
title: "Close",
|
|
80025
80282
|
duration: opts.durations[2],
|
|
80026
80283
|
narration: `${opts.product} is ready to show.`,
|
|
80027
|
-
backdrop: `resolved
|
|
80284
|
+
backdrop: `resolved editorial background plate for ${opts.product}, clean negative space, confident lockup, no readable text, no logos, no unrelated products`,
|
|
80028
80285
|
motion: "clean final title, subtle parallax, gentle fade to the final frame",
|
|
80029
80286
|
body: "End on the product name, offer, command, or memorable final line."
|
|
80030
80287
|
}
|
|
@@ -80257,6 +80514,12 @@ principles, type system, and visual-identity hard-gate. The same skill
|
|
|
80257
80514
|
governs \`vibe scene lint\` so your authored HTML and the linter stay in
|
|
80258
80515
|
agreement.
|
|
80259
80516
|
|
|
80517
|
+
For render-stable text, do not apply continuous \`scale\`, \`x\`, \`y\`,
|
|
80518
|
+
\`filter\`, or other transform tweens to \`.scene-content\` or any ancestor
|
|
80519
|
+
containing live text/cards. Put ambient zoom/parallax on background or media
|
|
80520
|
+
layers only; text should enter briefly and then hold still at its final CSS
|
|
80521
|
+
position.
|
|
80522
|
+
|
|
80260
80523
|
To retro-install on a project scaffolded before this command existed, run
|
|
80261
80524
|
\`vibe scene install-skill [--host all]\`.
|
|
80262
80525
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibeframe/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.105.1",
|
|
4
4
|
"description": "VibeFrame CLI - natural language video editing from the terminal",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -183,8 +183,8 @@
|
|
|
183
183
|
"tsx": "^4.21.0",
|
|
184
184
|
"typescript": "^5.3.3",
|
|
185
185
|
"vitest": "^1.2.2",
|
|
186
|
-
"@vibeframe/ai-providers": "0.
|
|
187
|
-
"@vibeframe/core": "0.
|
|
186
|
+
"@vibeframe/ai-providers": "0.105.1",
|
|
187
|
+
"@vibeframe/core": "0.105.1"
|
|
188
188
|
},
|
|
189
189
|
"engines": {
|
|
190
190
|
"node": ">=20"
|