@koda-sl/baker-cli 0.281.2-dev.1f1c09c80 → 0.281.4-dev.1f1c09c80
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/cli.js +58 -2
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -32938,6 +32938,20 @@ function endCardCta(spec) {
|
|
|
32938
32938
|
return declared.toLowerCase().replace(/[.!?]$/, "") === closing.replace(/[.!?]$/, "") ? null : declared;
|
|
32939
32939
|
}
|
|
32940
32940
|
var AD_CAST_ID = "customer";
|
|
32941
|
+
function castedAvatarOnRoster(cast, roster) {
|
|
32942
|
+
if (!cast || cast.avatar || !cast.description) return null;
|
|
32943
|
+
const words2 = new Set(
|
|
32944
|
+
cast.description.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean)
|
|
32945
|
+
);
|
|
32946
|
+
for (const person of roster) {
|
|
32947
|
+
for (const label of [person.name, person.handle]) {
|
|
32948
|
+
const token = label.trim().toLowerCase();
|
|
32949
|
+
if (!token || token.includes("-") || token.length < 3) continue;
|
|
32950
|
+
if (words2.has(token)) return person.handle;
|
|
32951
|
+
}
|
|
32952
|
+
}
|
|
32953
|
+
return null;
|
|
32954
|
+
}
|
|
32941
32955
|
function adSpecCastElements(spec, avatar) {
|
|
32942
32956
|
const description = (avatar?.subjectDescription ?? spec.cast?.description)?.trim();
|
|
32943
32957
|
if (!description) return [];
|
|
@@ -33278,6 +33292,17 @@ async function readBrandFromWorkspace(root = ".") {
|
|
|
33278
33292
|
function svgIsLiveText(svg) {
|
|
33279
33293
|
return /<text[\s>]/i.test(svg);
|
|
33280
33294
|
}
|
|
33295
|
+
async function markDirectoryIsReal(dir) {
|
|
33296
|
+
const files = await readdir8(dir).catch(() => null);
|
|
33297
|
+
if (!files) return false;
|
|
33298
|
+
const marks = files.filter((f) => /\.(svg|png|webp)$/i.test(f));
|
|
33299
|
+
if (marks.length === 0) return false;
|
|
33300
|
+
for (const f of marks.filter((f2) => /\.svg$/i.test(f2))) {
|
|
33301
|
+
const svg = await readFile15(path22.join(dir, f), "utf-8").catch(() => null);
|
|
33302
|
+
if (svg === null || svgIsLiveText(svg)) return false;
|
|
33303
|
+
}
|
|
33304
|
+
return true;
|
|
33305
|
+
}
|
|
33281
33306
|
|
|
33282
33307
|
// src/commands/canvas/composition-path.ts
|
|
33283
33308
|
import { existsSync as existsSync4 } from "fs";
|
|
@@ -33387,6 +33412,25 @@ var scaffoldAdCommand = defineCommand105({
|
|
|
33387
33412
|
process.exit(1);
|
|
33388
33413
|
return;
|
|
33389
33414
|
}
|
|
33415
|
+
if (!flagAvatar && !spec.data.cast?.avatar && spec.data.cast?.description) {
|
|
33416
|
+
const roster = await readAvatars(
|
|
33417
|
+
"/api/avatars",
|
|
33418
|
+
{ status: "ready" }
|
|
33419
|
+
).catch(() => null);
|
|
33420
|
+
const named = roster ? castedAvatarOnRoster(spec.data.cast, roster.avatars) : null;
|
|
33421
|
+
if (named) {
|
|
33422
|
+
writeJson({
|
|
33423
|
+
ok: false,
|
|
33424
|
+
error: {
|
|
33425
|
+
code: "VALIDATION_ERROR",
|
|
33426
|
+
message: `This ad describes its cast in words, but \`${named}\` is already one of this company's avatars \u2014 and the description names them. Described, they are re-invented every shot: the face drifts, their own voice is never used, and they cannot be shown speaking.`,
|
|
33427
|
+
fix: `Cast them instead: put \`"cast": { "avatar": "${named}" }\` in the spec, or pass \`--avatar ${named}\`. Keep a written description only for someone the company does not have.`
|
|
33428
|
+
}
|
|
33429
|
+
});
|
|
33430
|
+
process.exit(1);
|
|
33431
|
+
return;
|
|
33432
|
+
}
|
|
33433
|
+
}
|
|
33390
33434
|
const handle = flagAvatar || spec.data.cast?.avatar?.trim();
|
|
33391
33435
|
let avatar = null;
|
|
33392
33436
|
let avatarError = null;
|
|
@@ -33466,7 +33510,7 @@ var scaffoldAdCommand = defineCommand105({
|
|
|
33466
33510
|
error: {
|
|
33467
33511
|
code: "VALIDATION_ERROR",
|
|
33468
33512
|
message: `The brand mark at \`${resolvedLogo}\` could not be read, so there is no way to tell whether it is a real logo or a typed stand-in. An ad is not shipped on a mark nobody could open.`,
|
|
33469
|
-
fix: `Check the file is there and readable, or put the client's logo in \`${BRAND_DIR}/logos/\` \u2014
|
|
33513
|
+
fix: `Check the file is there and readable, or put the client's logo in \`${BRAND_DIR}/logos/\` \u2014 the file their designer delivered \u2014 an outlined SVG, or the PNG they gave you.`
|
|
33470
33514
|
}
|
|
33471
33515
|
});
|
|
33472
33516
|
process.exit(1);
|
|
@@ -33478,13 +33522,25 @@ var scaffoldAdCommand = defineCommand105({
|
|
|
33478
33522
|
error: {
|
|
33479
33523
|
code: "VALIDATION_ERROR",
|
|
33480
33524
|
message: `The mark at \`${resolvedLogo}\` draws its wordmark with live \`<text>\`, not outlines. That is a typed stand-in, not a logo: it renders in whatever font the machine happens to have, and the one that shipped this way came out reading "greenle\xE1" in every frame.`,
|
|
33481
|
-
fix: `Ask the client for their real logo file and put it in \`${BRAND_DIR}/logos/\` \u2014 an outlined SVG
|
|
33525
|
+
fix: `Ask the client for their real logo file and put it in \`${BRAND_DIR}/logos/\` \u2014 the one their designer delivered, as an outlined SVG or a PNG they gave you. Do not redraw it, and do not export this file to PNG: rasterising it only bakes the invented letters into pixels, where nothing downstream can tell they are wrong.`
|
|
33482
33526
|
}
|
|
33483
33527
|
});
|
|
33484
33528
|
process.exit(1);
|
|
33485
33529
|
return;
|
|
33486
33530
|
}
|
|
33487
33531
|
}
|
|
33532
|
+
if (resolvedLogo && !await markDirectoryIsReal(path24.dirname(resolvedLogo))) {
|
|
33533
|
+
writeJson({
|
|
33534
|
+
ok: false,
|
|
33535
|
+
error: {
|
|
33536
|
+
code: "VALIDATION_ERROR",
|
|
33537
|
+
message: `There is no real logo in \`${BRAND_DIR}/logos/\` \u2014 what is there is a typed stand-in, whose wordmark is drawn as live text in a font the renderer does not have. An ad built on it wears letters the machine invented, and converting it to a PNG only bakes them in.`,
|
|
33538
|
+
fix: `Ask the client for their actual logo file and put it in \`${BRAND_DIR}/logos/\` \u2014 the one their designer delivered, as an outlined SVG or a PNG they gave you. Do not redraw it and do not export the stand-in to another format.`
|
|
33539
|
+
}
|
|
33540
|
+
});
|
|
33541
|
+
process.exit(1);
|
|
33542
|
+
return;
|
|
33543
|
+
}
|
|
33488
33544
|
const blueprint = adSpecToBlueprint(spec.data);
|
|
33489
33545
|
const slug = args.slug ?? path24.basename(specPath).replace(/\.[^.]+$/, "");
|
|
33490
33546
|
const outPath = args.out ?? (args.slug ? path24.join("src/creatives", slug, `${slug}.canvas.json`) : path24.join(path24.dirname(specPath), `${slug}.canvas.json`));
|