@koda-sl/baker-cli 0.128.0-dev.70bf43ce4 → 0.131.0-dev.597b50181
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 +255 -288
- package/canvas/tiktok-captions-composition/index.html +1 -1
- package/canvas/video-overlay-composition/index.html +78 -6
- package/canvas/video-overlay-composition/meta.json +30 -2
- package/dist/{chunk-43KBQLP5.js → chunk-4QK7JC6O.js} +1243 -461
- package/dist/chunk-4QK7JC6O.js.map +1 -0
- package/dist/cli.js +8796 -6171
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.d.ts +62 -2
- package/dist/engine/index.js +2 -2
- package/package.json +4 -2
- package/dist/chunk-43KBQLP5.js.map +0 -1
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
|
|
113
113
|
group.forEach((word, wi) => {
|
|
114
114
|
const span = document.getElementById('w_' + gi + '_' + wi);
|
|
115
|
-
tl.set(span, { className: '
|
|
115
|
+
tl.set(span, { className: 'active-word' }, word.start);
|
|
116
116
|
});
|
|
117
117
|
|
|
118
118
|
tl.fromTo(el,
|
|
@@ -81,6 +81,27 @@
|
|
|
81
81
|
after another (e.g. coverage chips appearing as each is spoken), not as a clump. */
|
|
82
82
|
.chips { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; max-width: 92%; }
|
|
83
83
|
|
|
84
|
+
/* BUILT-IN CAPTION TRACK (renders only when a transcript.json is wired by the
|
|
85
|
+
scaffold — the merged overlay+captions pass). Restyle freely; the karaoke
|
|
86
|
+
grouping/sync is driven by the script below off the word-level transcript. */
|
|
87
|
+
.caption-container {
|
|
88
|
+
position: absolute; bottom: 320px; left: 0; right: 0;
|
|
89
|
+
display: flex; align-items: center; justify-content: center;
|
|
90
|
+
padding: 0 60px; pointer-events: none;
|
|
91
|
+
}
|
|
92
|
+
.caption-group {
|
|
93
|
+
position: absolute;
|
|
94
|
+
font-family: 'BrandFont', 'Arial Black', 'Helvetica Neue', sans-serif;
|
|
95
|
+
font-size: {{font_size}}px; font-weight: 900; color: {{text_color}};
|
|
96
|
+
text-align: center; text-transform: uppercase; line-height: 1.2;
|
|
97
|
+
text-shadow:
|
|
98
|
+
2px 2px 0 rgba(0,0,0,0.9), -2px -2px 0 rgba(0,0,0,0.9),
|
|
99
|
+
2px -2px 0 rgba(0,0,0,0.9), -2px 2px 0 rgba(0,0,0,0.9),
|
|
100
|
+
0 4px 8px rgba(0,0,0,0.6);
|
|
101
|
+
visibility: hidden;
|
|
102
|
+
}
|
|
103
|
+
.caption-group .active-word { color: {{highlight_color}}; }
|
|
104
|
+
|
|
84
105
|
/* 9-grid position helpers (absolute). Tweak the insets or add your own. */
|
|
85
106
|
.pos-top-left { top: 90px; left: 56px; text-align: left; }
|
|
86
107
|
.pos-top-center { top: 90px; left: 50%; transform: translateX(-50%); }
|
|
@@ -116,17 +137,20 @@
|
|
|
116
137
|
data-start, data-duration and data-track-index. See
|
|
117
138
|
references/hyperframes/blueprints-and-transitions.md. -->
|
|
118
139
|
|
|
119
|
-
<!-- CAPTION TRACK
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
scaffold already wires a default caption track as a separate composition;
|
|
140
|
+
<!-- CAPTION TRACK: built in below (#captions renders the word-synced karaoke
|
|
141
|
+
track whenever the scaffold wires a transcript.json — same render pass).
|
|
142
|
+
To escalate the register (caption-kinetic-slam, caption-editorial-emphasis,
|
|
143
|
+
caption-neon-glow) pull a block: `npx hyperframes add <id>` and restyle;
|
|
124
144
|
see references/hyperframes/captions-and-audio.md for the sync contract. -->
|
|
125
145
|
</div>
|
|
146
|
+
<!-- Captions live OUTSIDE #overlay-root on purpose: the generic overlay runtime
|
|
147
|
+
animates every [data-start] element under #overlay-root, and the caption
|
|
148
|
+
groups carry their own karaoke timing — nesting them would double-animate. -->
|
|
149
|
+
<div class="caption-container" id="captions" data-layout-allow-occlusion></div>
|
|
126
150
|
</div>
|
|
127
151
|
|
|
128
152
|
<script>
|
|
129
|
-
(() => {
|
|
153
|
+
(async () => {
|
|
130
154
|
const DURATION = parseFloat('{{duration}}');
|
|
131
155
|
const tl = gsap.timeline({ paused: true });
|
|
132
156
|
const els = Array.from(document.querySelectorAll('#overlay-root [data-start]'));
|
|
@@ -163,6 +187,54 @@
|
|
|
163
187
|
if (tx) el.style.transform = tx;
|
|
164
188
|
}
|
|
165
189
|
|
|
190
|
+
// BUILT-IN CAPTION TRACK — active only when the scaffold staged a transcript
|
|
191
|
+
// (the merged overlay+captions pass). No transcript.json ⇒ captions skip
|
|
192
|
+
// silently and this composition is a pure overlay layer.
|
|
193
|
+
const transcript = await fetch('./transcript.json')
|
|
194
|
+
.then((r) => (r.ok ? r.json() : null))
|
|
195
|
+
.catch(() => null);
|
|
196
|
+
if (Array.isArray(transcript) && transcript.length > 0) {
|
|
197
|
+
const WORDS_PER_GROUP = parseInt('{{words_per_group}}', 10) || 3;
|
|
198
|
+
const container = document.getElementById('captions');
|
|
199
|
+
const groups = [];
|
|
200
|
+
for (let i = 0; i < transcript.length; i += WORDS_PER_GROUP) {
|
|
201
|
+
groups.push(transcript.slice(i, i + WORDS_PER_GROUP));
|
|
202
|
+
}
|
|
203
|
+
groups.forEach((group, gi) => {
|
|
204
|
+
const el = document.createElement('div');
|
|
205
|
+
// `clip` marks this as a hyperframes-timed element (this same GSAP
|
|
206
|
+
// timeline drives its show/hide); without it the pre-render gate flags
|
|
207
|
+
// every group as visible for the whole composition.
|
|
208
|
+
el.className = 'caption-group clip';
|
|
209
|
+
el.id = 'cg_' + gi;
|
|
210
|
+
el.setAttribute('data-start', String(group[0].start));
|
|
211
|
+
el.setAttribute('data-duration', String((group[group.length - 1].end - group[0].start) || 0.5));
|
|
212
|
+
group.forEach((word, wi) => {
|
|
213
|
+
const span = document.createElement('span');
|
|
214
|
+
span.id = 'w_' + gi + '_' + wi;
|
|
215
|
+
span.textContent = word.text + ' ';
|
|
216
|
+
el.appendChild(span);
|
|
217
|
+
});
|
|
218
|
+
container.appendChild(el);
|
|
219
|
+
|
|
220
|
+
const groupStart = group[0].start;
|
|
221
|
+
const groupEnd = group[group.length - 1].end;
|
|
222
|
+
gsap.set(el, { visibility: 'hidden' });
|
|
223
|
+
tl.set(el, { visibility: 'visible' }, groupStart);
|
|
224
|
+
group.forEach((word, wi) => {
|
|
225
|
+
const span = document.getElementById('w_' + gi + '_' + wi);
|
|
226
|
+
tl.set(span, { className: 'active-word' }, word.start);
|
|
227
|
+
});
|
|
228
|
+
tl.fromTo(
|
|
229
|
+
el,
|
|
230
|
+
{ scale: 0.8, opacity: 0 },
|
|
231
|
+
{ scale: 1, opacity: 1, duration: 0.1, ease: 'back.out(1.5)' },
|
|
232
|
+
groupStart
|
|
233
|
+
);
|
|
234
|
+
tl.set(el, { visibility: 'hidden' }, groupEnd + 0.05);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
166
238
|
tl.to({}, { duration: 0.01 }, Math.max(0.01, DURATION - 0.01));
|
|
167
239
|
window.__timelines = window.__timelines || {};
|
|
168
240
|
window.__timelines.main = tl;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "video-overlay",
|
|
3
3
|
"title": "Overlay layer: agent-authored HTML/CSS composited over a video",
|
|
4
|
-
"description": "A paint-it-yourself overlay layer, NOT a prop renderer. The scaffold seeds #overlay-root in index.html with the reference ad's overlays as real, editable HTML elements (text + a position class + data-start/data-dur timing); a tiny generic runtime only shows/hides each element at its timestamp (with an optional data-anim entrance). Every styling decision
|
|
4
|
+
"description": "A paint-it-yourself overlay layer, NOT a prop renderer. The scaffold seeds #overlay-root in index.html with the reference ad's overlays as real, editable HTML elements (text + a position class + data-start/data-dur timing); a tiny generic runtime only shows/hides each element at its timestamp (with an optional data-anim entrance). Every styling decision \u2014 bars, tickers, colors, fonts, a real logo <img> dropped into this dir \u2014 lives in the HTML/CSS you edit, not in props. Drop brand-bold.otf / brand-regular.otf for on-brand type. Fixed 1080x1920; copy + edit width/height for other ratios. When speech is present the scaffold also wires the optional `transcript` input, and this composition renders the word-synced caption track in the SAME pass (one headless render, one less encode) \u2014 captions skip silently when no transcript is wired.",
|
|
5
5
|
"width": 1080,
|
|
6
6
|
"height": 1920,
|
|
7
7
|
"fps": 30,
|
|
@@ -12,7 +12,35 @@
|
|
|
12
12
|
"required": true,
|
|
13
13
|
"staged_as": "background.mp4",
|
|
14
14
|
"description": "The base video to composite overlays onto (e.g. the concatenated reproduction clips)."
|
|
15
|
+
},
|
|
16
|
+
"transcript": {
|
|
17
|
+
"kind": "json",
|
|
18
|
+
"required": false,
|
|
19
|
+
"staged_as": "transcript.json",
|
|
20
|
+
"description": "Optional word-level transcript JSON from video_transcribe: [{text, start, end}, ...]. When wired, the built-in TikTok-style caption track renders in this same pass."
|
|
15
21
|
}
|
|
16
22
|
},
|
|
17
|
-
"params": {
|
|
23
|
+
"params": {
|
|
24
|
+
"words_per_group": {
|
|
25
|
+
"kind": "integer",
|
|
26
|
+
"min": 1,
|
|
27
|
+
"max": 8,
|
|
28
|
+
"default": 3,
|
|
29
|
+
"description": "How many caption words to show at once."
|
|
30
|
+
},
|
|
31
|
+
"font_size": {
|
|
32
|
+
"kind": "integer",
|
|
33
|
+
"min": 24,
|
|
34
|
+
"max": 200,
|
|
35
|
+
"default": 72
|
|
36
|
+
},
|
|
37
|
+
"text_color": {
|
|
38
|
+
"kind": "color",
|
|
39
|
+
"default": "#ffffff"
|
|
40
|
+
},
|
|
41
|
+
"highlight_color": {
|
|
42
|
+
"kind": "color",
|
|
43
|
+
"default": "#FFD700"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
18
46
|
}
|