frameyard 0.5.0 → 0.6.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.
- package/CHANGELOG.md +55 -0
- package/GLOSSARY.md +152 -24
- package/README.md +94 -29
- package/dist/cli.js +111 -14
- package/dist/cli.js.map +1 -1
- package/dist/contracts.d.ts +12 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/preview/main.js +89 -11
- package/dist/preview/main.js.map +1 -1
- package/dist/preview/timeline-data.d.ts +85 -0
- package/dist/preview/timeline-data.d.ts.map +1 -0
- package/dist/preview/timeline-data.js +2 -0
- package/dist/preview/timeline-data.js.map +1 -0
- package/dist/preview/timeline-host.d.ts +19 -0
- package/dist/preview/timeline-host.d.ts.map +1 -0
- package/dist/preview/timeline-host.js +297 -0
- package/dist/preview/timeline-host.js.map +1 -0
- package/dist/preview/timeline-main.d.ts +17 -0
- package/dist/preview/timeline-main.d.ts.map +1 -0
- package/dist/preview/timeline-main.js +364 -0
- package/dist/preview/timeline-main.js.map +1 -0
- package/dist/project.d.ts +16 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +81 -26
- package/dist/project.js.map +1 -1
- package/dist/renderer/browser.d.ts +2 -0
- package/dist/renderer/browser.d.ts.map +1 -1
- package/dist/renderer/browser.js +6 -1
- package/dist/renderer/browser.js.map +1 -1
- package/dist/renderer/ffmpeg.d.ts +61 -0
- package/dist/renderer/ffmpeg.d.ts.map +1 -1
- package/dist/renderer/ffmpeg.js +198 -0
- package/dist/renderer/ffmpeg.js.map +1 -1
- package/dist/renderer/timeline.d.ts +109 -0
- package/dist/renderer/timeline.d.ts.map +1 -0
- package/dist/renderer/timeline.js +984 -0
- package/dist/renderer/timeline.js.map +1 -0
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +89 -24
- package/dist/scaffold.js.map +1 -1
- package/dist/server/preview-html.d.ts.map +1 -1
- package/dist/server/preview-html.js +15 -1
- package/dist/server/preview-html.js.map +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +39 -1
- package/dist/server/server.js.map +1 -1
- package/dist/server/timeline-preview-data.d.ts +4 -0
- package/dist/server/timeline-preview-data.d.ts.map +1 -0
- package/dist/server/timeline-preview-data.js +97 -0
- package/dist/server/timeline-preview-data.js.map +1 -0
- package/dist/server/timeline-preview-html.d.ts +4 -0
- package/dist/server/timeline-preview-html.d.ts.map +1 -0
- package/dist/server/timeline-preview-html.js +227 -0
- package/dist/server/timeline-preview-html.js.map +1 -0
- package/dist/timeline/plan.d.ts +32 -0
- package/dist/timeline/plan.d.ts.map +1 -0
- package/dist/timeline/plan.js +68 -0
- package/dist/timeline/plan.js.map +1 -0
- package/dist/timeline/resolve.d.ts +60 -0
- package/dist/timeline/resolve.d.ts.map +1 -0
- package/dist/timeline/resolve.js +523 -0
- package/dist/timeline/resolve.js.map +1 -0
- package/dist/timeline/schema.d.ts +71 -0
- package/dist/timeline/schema.d.ts.map +1 -0
- package/dist/timeline/schema.js +425 -0
- package/dist/timeline/schema.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/adr/0002-make-frame-and-props-the-only-animation-state.md +2 -0
- package/docs/adr/0005-keep-parallel-rendering-opt-in-and-frame-ordered.md +2 -0
- package/docs/adr/0007-make-html-documents-the-composition-unit.md +1 -1
- package/docs/adr/0009-composite-production-frames-in-software.md +1 -1
- package/docs/adr/0010-make-timelines-the-render-unit.md +24 -0
- package/docs/adr/0011-compose-timeline-frames-in-a-live-browser-host.md +22 -0
- package/docs/adr/0012-render-timelines-with-segmented-lossless-clip-intermediates.md +31 -0
- package/docs/v0.1-design.md +135 -86
- package/examples/demo/README.md +10 -3
- package/examples/demo/timelines/demo.timeline.json +12 -0
- package/examples/demo/video.config.js +4 -0
- package/examples/video/README.md +50 -6
- package/examples/video/compositions/video-overlay.html +350 -0
- package/examples/video/props/video-timeline.json +10 -0
- package/examples/video/timelines/video-timeline-mp4.timeline.json +44 -0
- package/examples/video/timelines/video-timeline.timeline.json +44 -0
- package/examples/video/video.config.js +6 -0
- package/package.json +1 -1
- package/skills/frameyard/SKILL.md +17 -13
- package/skills/frameyard/references/timelines.md +141 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<script type="application/json" data-fy="props">
|
|
6
|
+
{
|
|
7
|
+
"kicker": "Frameyard / Timeline Overlay 001",
|
|
8
|
+
"title": "Timeline",
|
|
9
|
+
"titleAccent": "Overlay",
|
|
10
|
+
"subtitle": "The clip lives on a timeline track. This HTML layer composites over it with alpha.",
|
|
11
|
+
"detail": "Detail / second pass",
|
|
12
|
+
"format": "30 FPS",
|
|
13
|
+
"accent": "#ffc37a"
|
|
14
|
+
}
|
|
15
|
+
</script>
|
|
16
|
+
<style>
|
|
17
|
+
@keyframes fade-in {
|
|
18
|
+
from {
|
|
19
|
+
opacity: 0;
|
|
20
|
+
}
|
|
21
|
+
to {
|
|
22
|
+
opacity: 1;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
@keyframes rise {
|
|
26
|
+
from {
|
|
27
|
+
opacity: 0;
|
|
28
|
+
transform: translateY(42px);
|
|
29
|
+
}
|
|
30
|
+
to {
|
|
31
|
+
opacity: 1;
|
|
32
|
+
transform: translateY(0);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
@keyframes draw {
|
|
36
|
+
from {
|
|
37
|
+
opacity: 0;
|
|
38
|
+
transform: scaleX(0);
|
|
39
|
+
}
|
|
40
|
+
to {
|
|
41
|
+
opacity: 1;
|
|
42
|
+
transform: scaleX(1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
@keyframes detail-cycle {
|
|
46
|
+
0% {
|
|
47
|
+
opacity: 0;
|
|
48
|
+
}
|
|
49
|
+
6% {
|
|
50
|
+
opacity: 1;
|
|
51
|
+
}
|
|
52
|
+
94% {
|
|
53
|
+
opacity: 1;
|
|
54
|
+
}
|
|
55
|
+
100% {
|
|
56
|
+
opacity: 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
@keyframes outro-fade {
|
|
60
|
+
from {
|
|
61
|
+
opacity: 0;
|
|
62
|
+
}
|
|
63
|
+
to {
|
|
64
|
+
opacity: 1;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
html,
|
|
68
|
+
body,
|
|
69
|
+
fy-composition {
|
|
70
|
+
background: transparent;
|
|
71
|
+
}
|
|
72
|
+
#scene {
|
|
73
|
+
position: relative;
|
|
74
|
+
width: 100%;
|
|
75
|
+
height: 100%;
|
|
76
|
+
overflow: hidden;
|
|
77
|
+
isolation: isolate;
|
|
78
|
+
color: #eef4f1;
|
|
79
|
+
font-family: Inter, "Helvetica Neue", ui-sans-serif, system-ui, sans-serif;
|
|
80
|
+
}
|
|
81
|
+
#scrim {
|
|
82
|
+
position: absolute;
|
|
83
|
+
inset: 0;
|
|
84
|
+
background: linear-gradient(
|
|
85
|
+
90deg,
|
|
86
|
+
rgba(4, 8, 11, 0.94) 0%,
|
|
87
|
+
rgba(4, 8, 11, 0.82) 22%,
|
|
88
|
+
rgba(4, 8, 11, 0.35) 46%,
|
|
89
|
+
rgba(4, 8, 11, 0) 66%
|
|
90
|
+
);
|
|
91
|
+
pointer-events: none;
|
|
92
|
+
}
|
|
93
|
+
#footer-scrim {
|
|
94
|
+
position: absolute;
|
|
95
|
+
left: 0;
|
|
96
|
+
right: 0;
|
|
97
|
+
bottom: 0;
|
|
98
|
+
height: 150px;
|
|
99
|
+
background: linear-gradient(0deg, rgba(4, 8, 11, 0.88) 0%, rgba(4, 8, 11, 0) 100%);
|
|
100
|
+
pointer-events: none;
|
|
101
|
+
}
|
|
102
|
+
#frame-border {
|
|
103
|
+
position: absolute;
|
|
104
|
+
inset: 36px;
|
|
105
|
+
border: 1px solid rgba(238, 244, 241, 0.16);
|
|
106
|
+
pointer-events: none;
|
|
107
|
+
}
|
|
108
|
+
#header {
|
|
109
|
+
position: absolute;
|
|
110
|
+
top: 80px;
|
|
111
|
+
left: 148px;
|
|
112
|
+
right: 148px;
|
|
113
|
+
display: flex;
|
|
114
|
+
justify-content: space-between;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 32px;
|
|
117
|
+
animation: fade-in 1s linear both;
|
|
118
|
+
}
|
|
119
|
+
#kicker {
|
|
120
|
+
display: flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
gap: 15px;
|
|
123
|
+
font:
|
|
124
|
+
600 14px/1 ui-monospace,
|
|
125
|
+
monospace;
|
|
126
|
+
letter-spacing: 0.24em;
|
|
127
|
+
text-transform: uppercase;
|
|
128
|
+
color: var(--fy-prop-accent);
|
|
129
|
+
}
|
|
130
|
+
#kicker::before {
|
|
131
|
+
content: "";
|
|
132
|
+
width: 8px;
|
|
133
|
+
height: 8px;
|
|
134
|
+
background: var(--fy-prop-accent);
|
|
135
|
+
box-shadow: 0 0 16px var(--fy-prop-accent);
|
|
136
|
+
}
|
|
137
|
+
#format {
|
|
138
|
+
font:
|
|
139
|
+
500 12px/1 ui-monospace,
|
|
140
|
+
monospace;
|
|
141
|
+
letter-spacing: 0.16em;
|
|
142
|
+
text-transform: uppercase;
|
|
143
|
+
color: #b9c6c8;
|
|
144
|
+
text-shadow: 0 2px 18px rgba(0, 0, 0, 0.65);
|
|
145
|
+
}
|
|
146
|
+
#copy {
|
|
147
|
+
position: absolute;
|
|
148
|
+
top: 278px;
|
|
149
|
+
left: 148px;
|
|
150
|
+
width: 860px;
|
|
151
|
+
}
|
|
152
|
+
#title {
|
|
153
|
+
margin: 0;
|
|
154
|
+
font-size: 174px;
|
|
155
|
+
line-height: 0.84;
|
|
156
|
+
letter-spacing: -0.064em;
|
|
157
|
+
font-weight: 660;
|
|
158
|
+
}
|
|
159
|
+
#title span,
|
|
160
|
+
#title em {
|
|
161
|
+
display: block;
|
|
162
|
+
animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
|
|
163
|
+
}
|
|
164
|
+
#title em {
|
|
165
|
+
margin-top: 0.06em;
|
|
166
|
+
font-family: "Iowan Old Style", "Palatino Linotype", Georgia, ui-serif, serif;
|
|
167
|
+
font-style: italic;
|
|
168
|
+
font-weight: 400;
|
|
169
|
+
letter-spacing: -0.035em;
|
|
170
|
+
color: var(--fy-prop-accent);
|
|
171
|
+
}
|
|
172
|
+
#subtitle {
|
|
173
|
+
width: 720px;
|
|
174
|
+
margin: 38px 0 0;
|
|
175
|
+
font-size: 29px;
|
|
176
|
+
line-height: 1.5;
|
|
177
|
+
font-weight: 420;
|
|
178
|
+
color: #b3c1c4;
|
|
179
|
+
animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
|
|
180
|
+
}
|
|
181
|
+
#detail {
|
|
182
|
+
position: absolute;
|
|
183
|
+
left: 1332px;
|
|
184
|
+
top: 646px;
|
|
185
|
+
width: 440px;
|
|
186
|
+
animation: detail-cycle 1s linear both;
|
|
187
|
+
}
|
|
188
|
+
#detail-bar {
|
|
189
|
+
display: flex;
|
|
190
|
+
justify-content: space-between;
|
|
191
|
+
align-items: center;
|
|
192
|
+
gap: 16px;
|
|
193
|
+
height: 22px;
|
|
194
|
+
margin-bottom: 10px;
|
|
195
|
+
padding: 0 10px;
|
|
196
|
+
background: rgba(4, 8, 11, 0.55);
|
|
197
|
+
font:
|
|
198
|
+
600 11px/1 ui-monospace,
|
|
199
|
+
monospace;
|
|
200
|
+
letter-spacing: 0.18em;
|
|
201
|
+
text-transform: uppercase;
|
|
202
|
+
}
|
|
203
|
+
#detail-bar > span:first-child {
|
|
204
|
+
color: var(--fy-prop-accent);
|
|
205
|
+
}
|
|
206
|
+
#detail-time {
|
|
207
|
+
color: #c0cdcf;
|
|
208
|
+
font-weight: 500;
|
|
209
|
+
}
|
|
210
|
+
#detail-window {
|
|
211
|
+
position: relative;
|
|
212
|
+
width: 440px;
|
|
213
|
+
height: 248px;
|
|
214
|
+
box-sizing: border-box;
|
|
215
|
+
overflow: hidden;
|
|
216
|
+
border: 1px solid rgba(255, 195, 122, 0.75);
|
|
217
|
+
background: transparent;
|
|
218
|
+
box-shadow: 0 24px 90px rgba(0, 0, 0, 0.45);
|
|
219
|
+
}
|
|
220
|
+
#detail-tint {
|
|
221
|
+
position: absolute;
|
|
222
|
+
inset: 0;
|
|
223
|
+
background: linear-gradient(0deg, rgba(4, 8, 11, 0.32) 0%, rgba(4, 8, 11, 0) 45%);
|
|
224
|
+
pointer-events: none;
|
|
225
|
+
}
|
|
226
|
+
#footer {
|
|
227
|
+
position: absolute;
|
|
228
|
+
left: 148px;
|
|
229
|
+
right: 148px;
|
|
230
|
+
bottom: 70px;
|
|
231
|
+
contain: layout paint style;
|
|
232
|
+
animation: fade-in 1s linear both;
|
|
233
|
+
}
|
|
234
|
+
#rule {
|
|
235
|
+
position: relative;
|
|
236
|
+
height: 1px;
|
|
237
|
+
background: rgba(238, 244, 241, 0.24);
|
|
238
|
+
transform-origin: left center;
|
|
239
|
+
animation: draw 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
|
|
240
|
+
}
|
|
241
|
+
#progress {
|
|
242
|
+
position: absolute;
|
|
243
|
+
inset: 0 auto 0 0;
|
|
244
|
+
width: calc(var(--fy-progress) * 100%);
|
|
245
|
+
background: var(--fy-prop-accent);
|
|
246
|
+
box-shadow: 0 0 14px var(--fy-prop-accent);
|
|
247
|
+
}
|
|
248
|
+
#footer-row {
|
|
249
|
+
display: flex;
|
|
250
|
+
justify-content: space-between;
|
|
251
|
+
align-items: baseline;
|
|
252
|
+
gap: 24px;
|
|
253
|
+
margin-top: 18px;
|
|
254
|
+
font:
|
|
255
|
+
500 12px/1 ui-monospace,
|
|
256
|
+
monospace;
|
|
257
|
+
letter-spacing: 0.14em;
|
|
258
|
+
color: #a4b1b3;
|
|
259
|
+
text-transform: uppercase;
|
|
260
|
+
text-shadow: 0 2px 14px rgba(0, 0, 0, 0.7);
|
|
261
|
+
}
|
|
262
|
+
#footer-row strong {
|
|
263
|
+
color: #edf5f2;
|
|
264
|
+
font-weight: 600;
|
|
265
|
+
}
|
|
266
|
+
#footer-row .accent {
|
|
267
|
+
color: var(--fy-prop-accent);
|
|
268
|
+
}
|
|
269
|
+
#frame-counter {
|
|
270
|
+
display: inline-block;
|
|
271
|
+
min-width: 3ch;
|
|
272
|
+
text-align: right;
|
|
273
|
+
}
|
|
274
|
+
#frame-counter::after {
|
|
275
|
+
counter-reset: fy-frame var(--fy-frame);
|
|
276
|
+
content: counter(fy-frame);
|
|
277
|
+
}
|
|
278
|
+
#outro-fade {
|
|
279
|
+
position: absolute;
|
|
280
|
+
inset: 0;
|
|
281
|
+
background: #04080b;
|
|
282
|
+
pointer-events: none;
|
|
283
|
+
opacity: 0;
|
|
284
|
+
animation: outro-fade 1s linear both;
|
|
285
|
+
}
|
|
286
|
+
</style>
|
|
287
|
+
</head>
|
|
288
|
+
<body>
|
|
289
|
+
<fy-composition width="1920" height="1080" fps="30" duration-in-frames="360">
|
|
290
|
+
<div id="scene">
|
|
291
|
+
<div id="scrim"></div>
|
|
292
|
+
<div id="footer-scrim"></div>
|
|
293
|
+
<div id="frame-border"></div>
|
|
294
|
+
|
|
295
|
+
<header id="header" data-fy-anim-start="8" data-fy-anim-duration="28">
|
|
296
|
+
<div id="kicker" data-fy-text="kicker"></div>
|
|
297
|
+
<div id="format">Timeline video / <span data-fy-text="format"></span></div>
|
|
298
|
+
</header>
|
|
299
|
+
|
|
300
|
+
<div id="copy">
|
|
301
|
+
<h1 id="title">
|
|
302
|
+
<span
|
|
303
|
+
data-fy-text="title"
|
|
304
|
+
data-fy-anim-start="14"
|
|
305
|
+
data-fy-anim-duration="44"
|
|
306
|
+
data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
|
|
307
|
+
></span>
|
|
308
|
+
<em
|
|
309
|
+
data-fy-text="titleAccent"
|
|
310
|
+
data-fy-anim-start="26"
|
|
311
|
+
data-fy-anim-duration="44"
|
|
312
|
+
data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
|
|
313
|
+
></em>
|
|
314
|
+
</h1>
|
|
315
|
+
<p
|
|
316
|
+
id="subtitle"
|
|
317
|
+
data-fy-text="subtitle"
|
|
318
|
+
data-fy-anim-start="40"
|
|
319
|
+
data-fy-anim-duration="40"
|
|
320
|
+
data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
|
|
321
|
+
></p>
|
|
322
|
+
</div>
|
|
323
|
+
|
|
324
|
+
<div id="detail" data-fy-anim-start="112" data-fy-anim-duration="170">
|
|
325
|
+
<div id="detail-bar">
|
|
326
|
+
<span data-fy-text="detail"></span>
|
|
327
|
+
<span id="detail-time">SRC 5.8–10.8 S</span>
|
|
328
|
+
</div>
|
|
329
|
+
<div id="detail-window">
|
|
330
|
+
<div id="detail-tint"></div>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
<div id="footer" data-fy-anim-start="46" data-fy-anim-duration="30">
|
|
335
|
+
<div id="rule" data-fy-anim-start="52" data-fy-anim-duration="50">
|
|
336
|
+
<div id="progress"></div>
|
|
337
|
+
</div>
|
|
338
|
+
<div id="footer-row">
|
|
339
|
+
<span>Hero / v1 · SRC <strong>0.0–12.0 S</strong></span>
|
|
340
|
+
<span>Detail / v2 · SRC <strong>5.8–10.8 S</strong></span>
|
|
341
|
+
<span>Frame <strong id="frame-counter"></strong> / 359</span>
|
|
342
|
+
<span class="accent">HTML overlay / v3 · 30 FPS</span>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
|
|
346
|
+
<div id="outro-fade" data-fy-anim-start="329" data-fy-anim-duration="30"></div>
|
|
347
|
+
</div>
|
|
348
|
+
</fy-composition>
|
|
349
|
+
</body>
|
|
350
|
+
</html>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"name": "Video on a track, HTML overlay on top (MP4)",
|
|
4
|
+
"tracks": [
|
|
5
|
+
{ "id": "v1", "kind": "video", "name": "Hero video" },
|
|
6
|
+
{ "id": "v2", "kind": "video", "name": "Detail video" },
|
|
7
|
+
{ "id": "v3", "kind": "video", "name": "HTML overlay" }
|
|
8
|
+
],
|
|
9
|
+
"clips": [
|
|
10
|
+
{
|
|
11
|
+
"id": "hero",
|
|
12
|
+
"track": "v1",
|
|
13
|
+
"source": { "kind": "video", "src": "assets/clip.mp4" },
|
|
14
|
+
"start": 0,
|
|
15
|
+
"audio": { "gain": 0.85 }
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "detail",
|
|
19
|
+
"track": "v2",
|
|
20
|
+
"source": { "kind": "video", "src": "assets/clip.mp4" },
|
|
21
|
+
"start": 120,
|
|
22
|
+
"sourceIn": 174,
|
|
23
|
+
"duration": 150,
|
|
24
|
+
"transform": {
|
|
25
|
+
"fit": "cover",
|
|
26
|
+
"width": 440,
|
|
27
|
+
"height": 248,
|
|
28
|
+
"x": 592,
|
|
29
|
+
"y": 262,
|
|
30
|
+
"rotation": 0,
|
|
31
|
+
"opacity": 1
|
|
32
|
+
},
|
|
33
|
+
"audio": { "muted": true }
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "overlay",
|
|
37
|
+
"track": "v3",
|
|
38
|
+
"source": { "kind": "composition", "id": "video-overlay" },
|
|
39
|
+
"start": 0,
|
|
40
|
+
"duration": 360,
|
|
41
|
+
"props": { "format": "H.264 + AAC / 30 FPS" }
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"name": "Video on a track, HTML overlay on top",
|
|
4
|
+
"tracks": [
|
|
5
|
+
{ "id": "v1", "kind": "video", "name": "Hero video" },
|
|
6
|
+
{ "id": "v2", "kind": "video", "name": "Detail video" },
|
|
7
|
+
{ "id": "v3", "kind": "video", "name": "HTML overlay" }
|
|
8
|
+
],
|
|
9
|
+
"clips": [
|
|
10
|
+
{
|
|
11
|
+
"id": "hero",
|
|
12
|
+
"track": "v1",
|
|
13
|
+
"source": { "kind": "video", "src": "assets/clip.webm" },
|
|
14
|
+
"start": 0,
|
|
15
|
+
"audio": { "gain": 0.85 }
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "detail",
|
|
19
|
+
"track": "v2",
|
|
20
|
+
"source": { "kind": "video", "src": "assets/clip.webm" },
|
|
21
|
+
"start": 120,
|
|
22
|
+
"sourceIn": 174,
|
|
23
|
+
"duration": 150,
|
|
24
|
+
"transform": {
|
|
25
|
+
"fit": "cover",
|
|
26
|
+
"width": 440,
|
|
27
|
+
"height": 248,
|
|
28
|
+
"x": 592,
|
|
29
|
+
"y": 262,
|
|
30
|
+
"rotation": 0,
|
|
31
|
+
"opacity": 1
|
|
32
|
+
},
|
|
33
|
+
"audio": { "muted": true }
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "overlay",
|
|
37
|
+
"track": "v3",
|
|
38
|
+
"source": { "kind": "composition", "id": "video-overlay" },
|
|
39
|
+
"start": 0,
|
|
40
|
+
"duration": 360,
|
|
41
|
+
"props": { "format": "VP9 + Opus / 30 FPS" }
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export default {
|
|
2
|
+
format: { width: 1920, height: 1080, fps: 30 },
|
|
2
3
|
compositions: {
|
|
3
4
|
video: "./compositions/video.html",
|
|
4
5
|
"video-mp4": "./compositions/video-mp4.html",
|
|
6
|
+
"video-overlay": "./compositions/video-overlay.html",
|
|
7
|
+
},
|
|
8
|
+
timelines: {
|
|
9
|
+
"video-timeline": "./timelines/video-timeline.timeline.json",
|
|
10
|
+
"video-timeline-mp4": "./timelines/video-timeline-mp4.timeline.json",
|
|
5
11
|
},
|
|
6
12
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: frameyard
|
|
3
|
-
description: Create, validate, inspect, and render deterministic Frameyard videos authored with HTML
|
|
4
|
-
frameyard-api-version: 0.
|
|
3
|
+
description: Create, validate, inspect, and render deterministic Frameyard videos authored with HTML compositions and JSON timelines. Use when a user asks to build or operate a Frameyard project, produce parameterized video, assemble clips into a timeline, add a local TTS or transcription asset through Frameyard's wrappers, or diagnose a Frameyard render. Do not use for general-purpose media generation outside a Frameyard project.
|
|
4
|
+
frameyard-api-version: 0.6.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Frameyard
|
|
@@ -11,19 +11,20 @@ Use Frameyard's public project format and CLI. Do not introduce an agent-only ru
|
|
|
11
11
|
## First decisions
|
|
12
12
|
|
|
13
13
|
1. Find the project: look for `video.config.js` from the working directory upward, or run `npx frameyard init <directory>` to scaffold one.
|
|
14
|
-
2. Read the
|
|
15
|
-
3. Read the composition HTML, its optional controller, and the props
|
|
14
|
+
2. Read the `format`, `compositions`, and `timelines` maps in `video.config.js`. Entries are explicit `.html` and `.json` documents; there is no directory scanning.
|
|
15
|
+
3. Read the timeline JSON, the referenced composition HTML, its optional controller, and the props files before changing behavior.
|
|
16
16
|
4. If the task needs speech or transcription, confirm that inference is requested or explicitly approved before producing assets.
|
|
17
17
|
|
|
18
18
|
## Workflow
|
|
19
19
|
|
|
20
|
-
1. **Author**
|
|
21
|
-
2. **
|
|
22
|
-
3. **
|
|
23
|
-
4. **
|
|
24
|
-
5. **
|
|
25
|
-
6. **
|
|
26
|
-
7. **
|
|
20
|
+
1. **Author compositions** as full HTML documents with one `<fy-composition>` root. Put metadata, default props, markup, CSS animation, bindings, audio, and transcripts in the document. Use an optional controller for Canvas 2D, WebGL, karaoke, or per-frame computation CSS cannot express. Read [deterministic authoring](references/deterministic-authoring.md), [bindings](references/bindings.md), and [controllers](references/controllers.md).
|
|
21
|
+
2. **Assemble the timeline** in JSON: project format, video/audio tracks, composition, media, and nested-timeline clips, source ranges, static transforms, and audio. Read [timelines](references/timelines.md).
|
|
22
|
+
3. **Provide props** as JSON. Composition props merge over defaults; timeline render props are a map keyed by composition clip ID. Validate domain-specific requirements with a controller's `validateProps()`.
|
|
23
|
+
4. **Produce optional assets** with `npx frameyard tts` and `npx frameyard transcribe`, never with the upstream executables. Read [audio assets](references/audio-assets.md). Do not overwrite or regenerate existing generated assets without permission.
|
|
24
|
+
5. **Validate** with `npx frameyard check <id> --json`, where `<id>` is a timeline or composition. Static validation runs before Chromium; then every frame is evaluated. Read the structured `diagnostics` array. Read [diagnostics](references/diagnostics.md) and [common mistakes](references/common-mistakes.md).
|
|
25
|
+
6. **Inspect** representative frames with `npx frameyard still <id> --frame <n> --out <file.png> --json`. View the PNGs when the host supports image viewing. If visual inspection is impossible, say so explicitly.
|
|
26
|
+
7. **Render** with `npx frameyard render <id> [--out <file.mp4>] [--props <file>] --json`. Composition renders accept `--concurrency <n>`; timeline renders accept `--workers <n>` and `--scratch <dir>`. The default output is `out/<id>.mp4` in the project root.
|
|
27
|
+
8. **Report** the project path, timeline and composition entries, controller, props, generated assets, provenance manifest, validation results, diagnostics, still paths and inspection status, final video path, and any failures.
|
|
27
28
|
|
|
28
29
|
Never claim success when `check` fails, a still is missing, a render exits nonzero, or visual inspection was skipped. A partial or unpublished render is not a successful result.
|
|
29
30
|
|
|
@@ -33,13 +34,15 @@ Never claim success when `check` fails, a still is missing, a render exits nonze
|
|
|
33
34
|
- Keep the current output files unless the user explicitly approves replacement. Use `--force` only with that approval.
|
|
34
35
|
- Use `--project <dir>` when not running from the project root.
|
|
35
36
|
- `render` writes to `out/<id>.mp4` in the project root unless `--out` is passed. Explicit `--out` paths are relative to the current working directory.
|
|
36
|
-
- Use `--concurrency <n>` only
|
|
37
|
+
- Use `--concurrency <n>` only for composition renders; the default is `1` and the maximum is `16`. Use `--workers <n>` for timeline renders; the default is `8`. Timeline renders also accept `--scratch <dir>` for FFV1 intermediates.
|
|
38
|
+
- For a timeline, `--props` is a JSON object keyed by top-level composition clip ID. For a composition, it is the props object.
|
|
37
39
|
- Use `npx frameyard doctor --json` before reporting an environment problem. It inspects without repairing or downloading.
|
|
38
40
|
- Read [JSON results](references/json-results.md) for result and error shapes, and [failure recovery](references/failure-recovery.md) for actionable error codes.
|
|
39
41
|
|
|
40
42
|
## Authoring rules
|
|
41
43
|
|
|
42
44
|
- A composition is a full HTML document with one `<fy-composition width height fps duration-in-frames>` root and a `data-fy="props"` default-props block.
|
|
45
|
+
- A timeline is a versioned JSON document with explicit tracks and clips. Sources are composition IDs, nested timeline IDs, or local video/image/audio paths. Timeline frames, sourceIn, and duration are integer frames. No remote URLs.
|
|
43
46
|
- Bindings are explicit: `data-fy-text`, `data-fy-attr-*`, `data-fy-class-*`, `data-fy-if`, and `data-fy-repeat` on `<template>`. Primitive top-level props are also `--fy-prop-<name>` CSS variables.
|
|
44
47
|
- CSS `@keyframes` and the Web Animations API are seeked from the frame clock. `data-fy-anim-start`, `data-fy-anim-duration`, and `data-fy-anim-easing` schedule one animation per element in frames.
|
|
45
48
|
- Frame-state variables on `:root`: `--fy-frame`, `--fy-time`, `--fy-progress`, `--fy-fps`, `--fy-duration`, and `--fy-duration-frames`.
|
|
@@ -50,7 +53,7 @@ Never claim success when `check` fails, a still is missing, a render exits nonze
|
|
|
50
53
|
|
|
51
54
|
## Determinism guardrails
|
|
52
55
|
|
|
53
|
-
- A frame may depend only on its integer `frame`, immutable `props`, and assets loaded before frame production.
|
|
56
|
+
- A composition frame may depend only on its integer `frame`, immutable `props`, and assets loaded before frame production. A timeline frame derives from its active clips and the same composition rules.
|
|
54
57
|
- Use `random("stable:key")` from `frameyard/runtime`; never use `Math.random()`.
|
|
55
58
|
- Never use `Date.now()`, `performance.now()`, timers, independent `requestAnimationFrame` loops, or CSS transitions as animation inputs.
|
|
56
59
|
- Finish remote data, fonts, and image loading during the loading phase. Network requests are blocked during frame capture.
|
|
@@ -80,6 +83,7 @@ When the CLI version differs from `frameyard-api-version`, pin it with `npx fram
|
|
|
80
83
|
|
|
81
84
|
## References
|
|
82
85
|
|
|
86
|
+
- [Timelines](references/timelines.md)
|
|
83
87
|
- [Deterministic HTML authoring](references/deterministic-authoring.md)
|
|
84
88
|
- [Bindings](references/bindings.md)
|
|
85
89
|
- [Controllers](references/controllers.md)
|