@koda-sl/baker-cli 0.394.0-dev.d28cef496 → 0.394.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.
Files changed (29) hide show
  1. package/README.md +256 -96
  2. package/canvas/static-ad-composition/index.html +253 -0
  3. package/canvas/static-ad-composition/meta.json +53 -0
  4. package/dist/{chunk-AOVFDDVW.js → chunk-53ATKV76.js} +13 -2
  5. package/dist/{chunk-AOVFDDVW.js.map → chunk-53ATKV76.js.map} +1 -1
  6. package/dist/{chunk-OKE2QJSJ.js → chunk-CEVZG46B.js} +3 -3
  7. package/dist/{chunk-NEZ3NAJV.js → chunk-F56XWRCY.js} +4 -4
  8. package/dist/{chunk-QIQ57EIA.js → chunk-S35KWGIQ.js} +2 -2
  9. package/dist/{chunk-FEJQJ64L.js → chunk-VTLHR3BB.js} +3997 -2418
  10. package/dist/chunk-VTLHR3BB.js.map +1 -0
  11. package/dist/{chunk-T2UNK5BW.js → chunk-ZPQ7L33H.js} +3 -3
  12. package/dist/cli.js +12369 -9286
  13. package/dist/cli.js.map +1 -1
  14. package/dist/{client-EQOSDYJL.js → client-ST4W3WRE.js} +4 -4
  15. package/dist/engine/index.d.ts +178 -117
  16. package/dist/engine/index.js +3 -3
  17. package/dist/{env-ANKM47IJ.js → env-HQWFIYZF.js} +2 -2
  18. package/dist/{output-NR4EJQDV.js → output-BTGDLTVL.js} +5 -5
  19. package/dist/{shared-SLCDVV45.js → shared-KLQWQWPJ.js} +6 -6
  20. package/package.json +2 -1
  21. package/dist/chunk-FEJQJ64L.js.map +0 -1
  22. /package/dist/{chunk-OKE2QJSJ.js.map → chunk-CEVZG46B.js.map} +0 -0
  23. /package/dist/{chunk-NEZ3NAJV.js.map → chunk-F56XWRCY.js.map} +0 -0
  24. /package/dist/{chunk-QIQ57EIA.js.map → chunk-S35KWGIQ.js.map} +0 -0
  25. /package/dist/{chunk-T2UNK5BW.js.map → chunk-ZPQ7L33H.js.map} +0 -0
  26. /package/dist/{client-EQOSDYJL.js.map → client-ST4W3WRE.js.map} +0 -0
  27. /package/dist/{env-ANKM47IJ.js.map → env-HQWFIYZF.js.map} +0 -0
  28. /package/dist/{output-NR4EJQDV.js.map → output-BTGDLTVL.js.map} +0 -0
  29. /package/dist/{shared-SLCDVV45.js.map → shared-KLQWQWPJ.js.map} +0 -0
@@ -0,0 +1,253 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=__WIDTH__, height=__HEIGHT__" />
6
+ <script src="./gsap.min.js"></script>
7
+ <style>
8
+ /* The ad's own faces, staged beside this file by the render (a brand file or a web font
9
+ file). One range per face, so a single-weight file is used as it is instead of being
10
+ faked bolder. The browser fetches a face only when text uses it, and text uses it
11
+ only when a face was staged (see FRAME.fonts below). */
12
+ @font-face { font-family: 'AdHeadline'; src: url('./headline.font'); font-weight: 100 900; font-display: block; }
13
+ @font-face { font-family: 'AdBody'; src: url('./body.font'); font-weight: 100 900; font-display: block; }
14
+ * { margin: 0; padding: 0; box-sizing: border-box; }
15
+ html, body { width: __WIDTH__px; height: __HEIGHT__px; overflow: hidden; background: #000; }
16
+ #root { position: absolute; inset: 0; overflow: hidden; }
17
+ .plate { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; }
18
+ .scrim { position: absolute; left: 0; right: 0; pointer-events: none; display: none; }
19
+ .group { position: absolute; display: flex; flex-direction: column; gap: 0; }
20
+ .block { font-family: 'AdBody', 'Helvetica Neue', Arial, sans-serif; overflow-wrap: break-word; text-wrap: pretty; }
21
+ .block.headline, .block.subheadline, .block.cta { font-family: 'AdHeadline', 'AdBody', 'Helvetica Neue', Arial, sans-serif; }
22
+ .block.headline { text-wrap: balance; letter-spacing: -0.01em; }
23
+ .fill { display: inline-block; line-height: 1.1; padding: 0.5em 1.2em; }
24
+ .fill.highlight { padding: 0.12em 0.4em; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
25
+ .fill.sticker { transform: rotate(-3deg); }
26
+ .logo { display: block; width: auto; object-fit: contain; }
27
+ </style>
28
+ </head>
29
+ <body>
30
+ <div id="root" data-composition-id="main" data-start="0" data-duration="1" data-width="__WIDTH__" data-height="__HEIGHT__">
31
+ <div class="scrim" id="scrim-top"></div>
32
+ <div class="scrim" id="scrim-bottom"></div>
33
+ </div>
34
+
35
+ <script>
36
+ (function () {
37
+ var AD = {{ad}};
38
+ var FRAME = {{frame}};
39
+ var W = __WIDTH__;
40
+ var H = __HEIGHT__;
41
+ var root = document.getElementById("root");
42
+ var safe = FRAME.safe || { top: 0.05, bottom: 0.05, left: 0.06, right: 0.06 };
43
+ var box = { x: safe.left * W, y: safe.top * H, w: W * (1 - safe.left - safe.right), h: H * (1 - safe.top - safe.bottom) };
44
+ // One unit per percent of the shorter side, so type reads the same size on every format.
45
+ var U = Math.min(W, H) / 100;
46
+ // A text-only ad has no picture (`plate: null`): the copy sits on the brand's ground.
47
+ var hasPlate = FRAME.plate !== null;
48
+ var ground = AD.background || { color: "#111111", ink: "#ffffff" };
49
+
50
+ var SIZE = { x_large: 8.6, large: 6.6, medium: 4.4, small: 3.0 };
51
+ var WEIGHT = { bold: 800, semibold: 600, regular: 400 };
52
+ var CASE = { upper: "uppercase", title: "capitalize", sentence: "none" };
53
+ var MAX_SHARE = { top: 0.44, middle: 0.3, bottom: 0.36 };
54
+ var RADIUS = { pill: "999px", rounded: "0.35em", square: "0" };
55
+
56
+ // With no face staged, the text never names the ad's faces, so the browser never asks
57
+ // for a file that is not there (a 404 the pre-render check refuses).
58
+ var FALLBACK_FACE = "'Helvetica Neue', Arial, sans-serif";
59
+ var facesStaged = FRAME.fonts !== false;
60
+
61
+ if (hasPlate) {
62
+ var plate = document.createElement("img");
63
+ plate.className = "plate";
64
+ plate.id = "plate";
65
+ plate.src = "./plate.png";
66
+ plate.alt = "";
67
+ root.insertBefore(plate, root.firstChild);
68
+ } else {
69
+ root.style.background = ground.color;
70
+ }
71
+
72
+ function luma(hex) {
73
+ var m = /^#?([0-9a-f]{6})$/i.exec(hex || "");
74
+ if (!m) return null;
75
+ var v = parseInt(m[1], 16);
76
+ return 0.2126 * ((v >> 16) & 255) + 0.7152 * ((v >> 8) & 255) + 0.0722 * (v & 255);
77
+ }
78
+
79
+ function alignSelf(align) {
80
+ return align === "left" ? "flex-start" : align === "right" ? "flex-end" : "center";
81
+ }
82
+
83
+ // Every line is drawn in its group: the layout region the reference put it in (placed
84
+ // at that region's box on this format), or a band when the layout gave no position.
85
+ var groups = {};
86
+ var bands = { top: null, middle: null, bottom: null };
87
+ (AD.groups || []).forEach(function (g) {
88
+ groups[g.id] = g;
89
+ });
90
+
91
+ function bandEl(name) {
92
+ if (bands[name]) return bands[name];
93
+ var el = document.createElement("div");
94
+ el.className = "group band";
95
+ el.dataset.band = name;
96
+ el.style.left = box.x + "px";
97
+ el.style.width = box.w + "px";
98
+ el.style.gap = 1.6 * U + "px";
99
+ if (name === "top") el.style.top = box.y + "px";
100
+ if (name === "bottom") el.style.bottom = H - (box.y + box.h) + "px";
101
+ if (name === "middle") {
102
+ el.style.top = box.y + box.h / 2 + "px";
103
+ el.style.transform = "translateY(-50%)";
104
+ }
105
+ el.dataset.max = String(box.h * MAX_SHARE[name]);
106
+ root.appendChild(el);
107
+ bands[name] = el;
108
+ return el;
109
+ }
110
+
111
+ var placed = {};
112
+ function containerFor(groupId, band) {
113
+ var g = groups[groupId];
114
+ var at = g && g.at && g.at[FRAME.format];
115
+ if (!at) return bandEl((g && g.band) || band || "top");
116
+ if (placed[groupId]) return placed[groupId];
117
+ var el = document.createElement("div");
118
+ el.className = "group placed";
119
+ el.style.left = (at.x[0] / 100) * W + "px";
120
+ el.style.width = ((at.x[1] - at.x[0]) / 100) * W + "px";
121
+ el.style.top = (at.y[0] / 100) * H + "px";
122
+ el.style.minHeight = ((at.y[1] - at.y[0]) / 100) * H + "px";
123
+ el.style.justifyContent = "center";
124
+ el.style.gap = 1.2 * U + "px";
125
+ // Copy rewritten for our brand runs longer than the source's: allow some growth
126
+ // past the region before the type shrinks.
127
+ el.dataset.max = String(((at.y[1] - at.y[0]) / 100) * H * 1.35);
128
+ root.appendChild(el);
129
+ placed[groupId] = el;
130
+ return el;
131
+ }
132
+
133
+ function addLogo(logo) {
134
+ var img = document.createElement("img");
135
+ img.className = "logo";
136
+ img.src = "./logo.png";
137
+ img.alt = "";
138
+ img.style.height = 5.4 * U + "px";
139
+ img.style.maxWidth = box.w * 0.42 + "px";
140
+ img.style.alignSelf = alignSelf(logo.align);
141
+ img.onerror = function () {
142
+ img.remove();
143
+ };
144
+ var el = containerFor(logo.group, logo.band);
145
+ if (el.dataset.band === "bottom") el.appendChild(img);
146
+ else el.insertBefore(img, el.firstChild);
147
+ }
148
+
149
+ function addBlock(b) {
150
+ var el = document.createElement("div");
151
+ el.className = "block " + b.role;
152
+ if (!facesStaged) el.style.fontFamily = FALLBACK_FACE;
153
+ var size = b.role === "fine_print" ? 2.4 : SIZE[b.size] || SIZE.medium;
154
+ el.dataset.base = String(size);
155
+ el.style.fontSize = size * U + "px";
156
+ el.style.fontWeight = String(WEIGHT[b.weight] || (b.role === "cta" ? 700 : 400));
157
+ el.style.lineHeight = b.role === "headline" ? "1.04" : "1.22";
158
+ el.style.textTransform = CASE[b.case] || "none";
159
+ el.style.textAlign = b.align || "center";
160
+ el.style.alignSelf = alignSelf(b.align);
161
+ var t = b.treatment || { kind: b.role === "cta" && !AD.groups ? "button" : "plain" };
162
+ var ink = b.color || (hasPlate ? "#ffffff" : ground.ink);
163
+ var text = b.text;
164
+ if (t.kind === "arrow" && !/[→›»]\s*$/.test(text)) text = text + " →";
165
+ if (t.kind === "button" || t.kind === "sticker" || t.kind === "highlight") {
166
+ var fill = document.createElement("span");
167
+ fill.className = "fill " + t.kind;
168
+ fill.textContent = text;
169
+ fill.style.background = t.background || (AD.cta && AD.cta.background) || "#111111";
170
+ fill.style.color = t.color || (AD.cta && AD.cta.color) || "#ffffff";
171
+ fill.style.borderRadius = RADIUS[t.shape] || RADIUS.rounded;
172
+ el.appendChild(fill);
173
+ } else {
174
+ el.textContent = text;
175
+ el.style.color = ink;
176
+ if (t.kind === "underline") {
177
+ el.style.textDecoration = "underline";
178
+ el.style.textDecorationThickness = "0.08em";
179
+ el.style.textUnderlineOffset = "0.18em";
180
+ }
181
+ var dark = (luma(ink) || 255) < 110;
182
+ el.dataset.dark = dark ? "1" : "0";
183
+ if (hasPlate) el.style.textShadow = dark ? "0 1px 10px rgba(255,255,255,0.35)" : "0 2px 14px rgba(0,0,0,0.45)";
184
+ }
185
+ containerFor(b.group, b.band).appendChild(el);
186
+ }
187
+
188
+ var drawCopy = FRAME.draw_copy !== false;
189
+ (drawCopy ? AD.copy || [] : []).forEach(addBlock);
190
+ if (AD.logo) addLogo(AD.logo);
191
+
192
+ function containers() {
193
+ return Array.prototype.slice.call(root.querySelectorAll(".group"));
194
+ }
195
+
196
+ // Shrink a group's type until it fits its room and no line runs past its edge. The
197
+ // copy is whatever prompt.json says today, so its length is not known when the
198
+ // layout is written.
199
+ function fit(el) {
200
+ var max = Number(el.dataset.max);
201
+ var width = el.getBoundingClientRect().width;
202
+ var scale = 1;
203
+ for (var i = 0; i < 40; i++) {
204
+ var tooTall = el.scrollHeight > max;
205
+ var tooWide = Array.prototype.some.call(el.children, function (c) {
206
+ return c.scrollWidth > width + 1;
207
+ });
208
+ if ((!tooTall && !tooWide) || scale < 0.45) break;
209
+ scale *= 0.94;
210
+ Array.prototype.forEach.call(el.querySelectorAll(".block"), function (c) {
211
+ c.style.fontSize = Number(c.dataset.base) * U * scale + "px";
212
+ });
213
+ }
214
+ }
215
+
216
+ // A soft scrim behind a band of light type over the picture (or a light one behind
217
+ // dark type), so copy with no position of its own reads on any plate. Copy placed
218
+ // by the layout sits on the calm area the plate kept for it.
219
+ function scrim(name) {
220
+ var el = bands[name];
221
+ if (!hasPlate || !el) return;
222
+ var s = document.getElementById("scrim-" + name);
223
+ var firstText = el.querySelector(".block[data-dark]");
224
+ if (!firstText) return;
225
+ var tint = firstText.dataset.dark === "1" ? "255,255,255" : "0,0,0";
226
+ var rect = el.getBoundingClientRect();
227
+ var reach = name === "top" ? rect.bottom + 6 * U : H - rect.top + 6 * U;
228
+ s.style.display = "block";
229
+ s.style.height = Math.min(H * 0.6, reach) + "px";
230
+ if (name === "top") {
231
+ s.style.top = "0";
232
+ s.style.background = "linear-gradient(to bottom, rgba(" + tint + ",0.42), rgba(" + tint + ",0))";
233
+ } else {
234
+ s.style.bottom = "0";
235
+ s.style.background = "linear-gradient(to top, rgba(" + tint + ",0.42), rgba(" + tint + ",0))";
236
+ }
237
+ }
238
+
239
+ var tl = gsap.timeline({ paused: true });
240
+ tl.to({}, { duration: 1 });
241
+ window.__timelines = window.__timelines || {};
242
+ window.__timelines.main = tl;
243
+
244
+ var ready = document.fonts && document.fonts.ready ? document.fonts.ready : Promise.resolve();
245
+ ready.then(function () {
246
+ containers().forEach(fit);
247
+ ["top", "bottom"].forEach(scrim);
248
+ root.setAttribute("data-ready", "1");
249
+ });
250
+ })();
251
+ </script>
252
+ </body>
253
+ </html>
@@ -0,0 +1,53 @@
1
+ {
2
+ "id": "static-ad",
3
+ "title": "Static ad: the picture (or the brand colours) with what the concept holds drawn on top",
4
+ "description": "Written by `baker canvas scaffold-static-ad`, one copy per delivered format. Draws only what prompt.compose.json (derived from prompt.json) holds: each line of copy where the source layout placed it, set the way the reference set it (plain, underlined, with an arrow, as a button or sticker), in the staged fonts, and the logo file only when the concept shows one. The plate is the image model's clean picture; a text-only ad has none and sits on the brand colour. Width and height are this format's delivered size.",
5
+ "width": 1080,
6
+ "height": 1350,
7
+ "fps": 1,
8
+ "default_duration": 1,
9
+ "inputs": {
10
+ "plate": {
11
+ "kind": "image",
12
+ "required": false,
13
+ "staged_as": "plate.png",
14
+ "description": "The picture, with no overlay text and no logo. Absent for a text-only ad."
15
+ },
16
+ "logo": {
17
+ "kind": "image",
18
+ "required": false,
19
+ "staged_as": "logo.png",
20
+ "description": "The brand logo file, wired only when the concept shows a logo."
21
+ },
22
+ "font_headline": {
23
+ "kind": "font",
24
+ "required": false,
25
+ "staged_as": "headline.font",
26
+ "description": "The brand's headline face."
27
+ },
28
+ "font_body": {
29
+ "kind": "font",
30
+ "required": false,
31
+ "staged_as": "body.font",
32
+ "description": "The brand's body face."
33
+ },
34
+ "compose": {
35
+ "kind": "json",
36
+ "required": true,
37
+ "staged_as": "compose.json",
38
+ "description": "prompt.compose.json, also passed inline as the `ad` param."
39
+ }
40
+ },
41
+ "params": {
42
+ "ad": {
43
+ "kind": "json",
44
+ "required": true,
45
+ "description": "The copy blocks, logo placement and CTA colours (prompt.compose.json)."
46
+ },
47
+ "frame": {
48
+ "kind": "json",
49
+ "required": true,
50
+ "description": "This format: its safe area, whether copy is drawn, the picture it is cut from (plate: null for none), and whether fonts are staged."
51
+ }
52
+ }
53
+ }
@@ -947,6 +947,12 @@ function round2(value) {
947
947
  function asRecord(value) {
948
948
  return value && typeof value === "object" ? value : {};
949
949
  }
950
+ function bakerUses(value) {
951
+ const uses = asRecord(value);
952
+ const companies = typeof uses.companies === "number" ? uses.companies : 0;
953
+ if (companies === 0) return {};
954
+ return { baker_uses: companies, ...uses.ours === true ? { already_adapted: true } : {} };
955
+ }
950
956
  function compactWinningAd(record) {
951
957
  const dna = asRecord(record.dna);
952
958
  return {
@@ -958,7 +964,11 @@ function compactWinningAd(record) {
958
964
  summary: String(dna.creative_concept ?? ""),
959
965
  advertiser_id: String(record.advertiser_id ?? ""),
960
966
  ad_id: String(record.ad_id ?? ""),
961
- media_url: String(record.media_url ?? "")
967
+ media_url: String(record.media_url ?? ""),
968
+ ...typeof dna.visual === "string" && dna.visual ? { visual: dna.visual } : {},
969
+ ...typeof dna.container === "string" && dna.container ? { container: dna.container } : {},
970
+ ...bakerUses(record.baker_uses),
971
+ ...record.source === "followed" || record.source === "library" ? { source: record.source } : {}
962
972
  };
963
973
  }
964
974
  function fullWinningAd(record) {
@@ -975,6 +985,7 @@ function fullWinningAd(record) {
975
985
  awareness_stage: dna.awareness_stage ?? null,
976
986
  target_persona: dna.target_persona ?? null,
977
987
  hook_archetype: dna.hook_archetype ?? null,
988
+ mechanism: dna.mechanism ?? null,
978
989
  industry: dna.industry ?? null
979
990
  };
980
991
  }
@@ -1299,4 +1310,4 @@ export {
1299
1310
  captureVimeoCookie,
1300
1311
  childEnvWith
1301
1312
  };
1302
- //# sourceMappingURL=chunk-AOVFDDVW.js.map
1313
+ //# sourceMappingURL=chunk-53ATKV76.js.map