@koda-sl/baker-cli 0.444.0-dev.cac1750ec → 0.445.0-dev.71ff4e44a

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 CHANGED
@@ -5833,7 +5833,7 @@ cart, a trial, a donation, a registration); who it is for is reported but never
5833
5833
  `_definition.md`, else the one its `targetAdSet` reaches (as `baker ads meta adsets --audience` reads it), else
5834
5834
  the persona and `awareness` from the Knowledge, else a cold stranger. Not understood → exit 2 with
5835
5835
  `viewer_check_failed` and a `fix` that starts with the one rewrite answering a missed offer (`Do this: make the biggest words say "<message>"`) and names what the viewer missed. After two failures of one ad in a chat turn the check no longer holds it back: the command answers `ok: true` with `understood: false` and what was missed, and `baker canvas run` renders it, printing the miss. The casting check is asked alongside the viewer. Answers are cached per copy, scene and viewer.
5836
- `baker canvas run` makes the same check on a static ad before it spends (skipped for `--fix`, an autofix round, `--use-variant`, a re-layout and resumed runs, never for an edit of the words;
5836
+ Before either, a static ad whose scene quotes words, or names a price or a share, that the copy (or `scene_text`) does not hold is refused (`scene_words_not_in_copy`): the picture would print them and a correction round would erase them. An unreadable `prompt.json` names each wrong field in a line and shows that field's shape. `baker canvas run` makes the same check on a static ad before it spends (skipped for `--fix`, an autofix round, `--use-variant`, a re-layout and resumed runs, never for an edit of the words;
5837
5837
  `--skip-viewer-check` renders without it; a model outage lets the render through with a warning). `--full` adds
5838
5838
  the viewer's whole answer and how the viewer was chosen.
5839
5839
 
@@ -6931,6 +6931,48 @@ function unwrittenProblems(spec) {
6931
6931
  }
6932
6932
  return problems;
6933
6933
  }
6934
+ var QUOTED = /"([^"]{2,})"|“([^”]{2,})”|(?<![\p{L}\p{N}])['‘]([^'’]{2,}?)['’](?![\p{L}\p{N}])/gu;
6935
+ var FIGURE = /[£$€]\s?\d[\d.,]*|\d+(?:[.,]\d+)?\s?%/g;
6936
+ var comparable = (text2) => text2.normalize("NFD").replace(new RegExp("\\p{M}", "gu"), "").toLowerCase().replace(/[^\p{L}\p{N}£$€%]+/gu, " ").trim();
6937
+ function sceneWordProblems(spec) {
6938
+ const held = [...spec.copy.map((l) => l.text), ...spec.scene_text].map(comparable);
6939
+ const holds = (text2) => {
6940
+ const want = comparable(text2);
6941
+ return want.length === 0 || held.some((h) => h.includes(want));
6942
+ };
6943
+ const scene = [spec.scene.description, spec.scene.layout ?? ""].join(" ");
6944
+ const quoted = [...scene.matchAll(QUOTED)].map((m) => (m[1] ?? m[2] ?? m[3] ?? "").trim()).filter((q) => !holds(q));
6945
+ const figures = (scene.match(FIGURE) ?? []).map((f) => f.trim()).filter((f) => !holds(f));
6946
+ const unheld = [.../* @__PURE__ */ new Set([...quoted, ...figures])];
6947
+ if (unheld.length === 0) return [];
6948
+ return [
6949
+ `The scene asks the picture to show ${unheld.map((w) => `"${w}"`).join(", ")}, which the copy does not hold: the picture would print it and the run would then spend a round erasing it. Put each one the ad needs in \`copy\` as an \`in_scene\` line with \`on\` (the bill, the phone), or describe that surface without words (blank, blurred, face-down).`
6950
+ ];
6951
+ }
6952
+ var FIELD_SHAPES = {
6953
+ copy: '"copy": [{"text": "Book your free survey", "lettering": "designed", "role": "cta"}]',
6954
+ scene: '"scene": {"description": "a family on a sunny roof terrace", "look": "photo"}',
6955
+ colors: '"colors": [{"object": "the button", "name": "deep green", "hex": "#1F5C3A"}]',
6956
+ references: '"references": [{"id": "PRODUCT_1", "role": "product", "what": "the solar panel kit"}]',
6957
+ third_party_brands: '"third_party_brands": [{"name": "Trustpilot", "role": "proof", "logo": "BADGE_1"}]',
6958
+ mechanism: '"mechanism": [{"device": "a before and after", "why_it_works": "the change is seen, not told"}]',
6959
+ viewer: '"viewer": {"who": "homeowners with high energy bills"}'
6960
+ };
6961
+ function adSpecErrorText(e) {
6962
+ if (!(e instanceof z7.ZodError)) return e instanceof Error ? e.message : String(e);
6963
+ const items = /* @__PURE__ */ new Map();
6964
+ for (const issue of e.issues) {
6965
+ const [field2, index, key] = issue.path;
6966
+ const at2 = typeof index === "number" ? `${String(field2)}[${index}]` : issue.path.map(String).join(".") || "the spec";
6967
+ const what = issue.code === "unrecognized_keys" ? `no field ${issue.keys.map((k) => `"${k}"`).join(", ")}` : key !== void 0 && typeof index === "number" ? `"${String(key)}" ${issue.message.replace(/^Invalid input: /, "")}` : issue.message.replace(/^Invalid input: /, "");
6968
+ items.set(at2, [...items.get(at2) ?? [], what]);
6969
+ }
6970
+ const fields = [...new Set(e.issues.map((i) => String(i.path[0] ?? "")))].filter((f) => FIELD_SHAPES[f]);
6971
+ return [
6972
+ ...[...items].map(([at2, whats]) => `${at2}: ${[...new Set(whats)].join("; ")}`),
6973
+ ...fields.map((f) => `Shape: ${FIELD_SHAPES[f]}`)
6974
+ ].join("\n");
6975
+ }
6934
6976
  function readAdSpec(raw) {
6935
6977
  if (isAdSpec(raw)) return AdSpecSchema.parse(raw);
6936
6978
  const advertiser = raw?.reference?.advertiser;
@@ -18488,6 +18530,8 @@ export {
18488
18530
  conceptSummary,
18489
18531
  conceptProblems,
18490
18532
  unwrittenProblems,
18533
+ sceneWordProblems,
18534
+ adSpecErrorText,
18491
18535
  readAdSpec,
18492
18536
  withRenderedLettering,
18493
18537
  conceptClassOf,
@@ -18575,4 +18619,4 @@ export {
18575
18619
  defaultRegistry,
18576
18620
  createEngineFromEnv
18577
18621
  };
18578
- //# sourceMappingURL=chunk-GY352NRN.js.map
18622
+ //# sourceMappingURL=chunk-CYZ7H4F5.js.map