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 @@
|
|
|
1
|
+
{"version":3,"file":"timeline-preview-html.d.ts","sourceRoot":"","sources":["../../src/server/timeline-preview-html.ts"],"names":[],"mappings":"AAwBA,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,MAAM,CA4MvG"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
function escapeHtml(value) {
|
|
2
|
+
return value.replace(/[&<>"']/g, (character) => {
|
|
3
|
+
switch (character) {
|
|
4
|
+
case "&":
|
|
5
|
+
return "&";
|
|
6
|
+
case "<":
|
|
7
|
+
return "<";
|
|
8
|
+
case ">":
|
|
9
|
+
return ">";
|
|
10
|
+
case '"':
|
|
11
|
+
return """;
|
|
12
|
+
default:
|
|
13
|
+
return "'";
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
const IMPORT_MAP = JSON.stringify({
|
|
18
|
+
imports: {
|
|
19
|
+
frameyard: "/__frameyard/framework/index.js",
|
|
20
|
+
"frameyard/runtime": "/__frameyard/framework/runtime/index.js",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
export function renderTimelinePreviewHtml(timelineId, options = {}) {
|
|
24
|
+
const id = escapeHtml(timelineId);
|
|
25
|
+
if (options.embed) {
|
|
26
|
+
return `<!doctype html>
|
|
27
|
+
<html lang="en">
|
|
28
|
+
<head>
|
|
29
|
+
<meta charset="utf-8">
|
|
30
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
31
|
+
<title>Frameyard timeline ${id}</title>
|
|
32
|
+
<style>
|
|
33
|
+
html, body { margin: 0; width: 100%; height: 100%; background: transparent; overflow: hidden; }
|
|
34
|
+
#stage { position: relative; width: 100%; height: 100%; overflow: hidden; background: transparent; }
|
|
35
|
+
.timeline-layer { position: absolute; left: 50%; top: 50%; transform-origin: center; }
|
|
36
|
+
.timeline-layer iframe, .timeline-layer video, .timeline-layer img { display: block; border: 0; }
|
|
37
|
+
</style>
|
|
38
|
+
<script type="importmap" data-fy-host>${IMPORT_MAP}</script>
|
|
39
|
+
</head>
|
|
40
|
+
<body data-timeline-id="${id}" data-embed="1">
|
|
41
|
+
<div id="stage"></div>
|
|
42
|
+
<script type="module" src="/__frameyard/framework/preview/timeline-main.js"></script>
|
|
43
|
+
</body>
|
|
44
|
+
</html>`;
|
|
45
|
+
}
|
|
46
|
+
return `<!doctype html>
|
|
47
|
+
<html lang="en">
|
|
48
|
+
<head>
|
|
49
|
+
<meta charset="utf-8">
|
|
50
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
51
|
+
<title>Frameyard timeline ${id}</title>
|
|
52
|
+
<style>
|
|
53
|
+
:root {
|
|
54
|
+
color-scheme: dark;
|
|
55
|
+
--bg: #0b0d10;
|
|
56
|
+
--panel: #11151a;
|
|
57
|
+
--panel-2: #171c23;
|
|
58
|
+
--line: #252c35;
|
|
59
|
+
--text: #e8edf3;
|
|
60
|
+
--muted: #8a97a6;
|
|
61
|
+
--accent: #b8f36b;
|
|
62
|
+
--accent-ink: #101408;
|
|
63
|
+
--danger: #ff8a8a;
|
|
64
|
+
--mono: "SFMono-Regular", ui-monospace, "Cascadia Code", "Liberation Mono", monospace;
|
|
65
|
+
--sans: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
66
|
+
}
|
|
67
|
+
* { box-sizing: border-box; }
|
|
68
|
+
html, body { height: 100%; }
|
|
69
|
+
body {
|
|
70
|
+
margin: 0;
|
|
71
|
+
background: var(--bg);
|
|
72
|
+
color: var(--text);
|
|
73
|
+
font-family: var(--sans);
|
|
74
|
+
font-size: 13px;
|
|
75
|
+
overflow: hidden;
|
|
76
|
+
}
|
|
77
|
+
#app {
|
|
78
|
+
display: grid;
|
|
79
|
+
grid-template-rows: 54px minmax(0, 1fr) 210px 58px;
|
|
80
|
+
height: 100vh;
|
|
81
|
+
}
|
|
82
|
+
header {
|
|
83
|
+
display: flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: 18px;
|
|
86
|
+
padding: 0 18px;
|
|
87
|
+
border-bottom: 1px solid var(--line);
|
|
88
|
+
background: linear-gradient(180deg, #11151a, #0d1014);
|
|
89
|
+
}
|
|
90
|
+
.brand { display: flex; align-items: center; gap: 9px; font-weight: 650; }
|
|
91
|
+
.mark {
|
|
92
|
+
width: 17px; height: 17px; border-radius: 4px;
|
|
93
|
+
background: linear-gradient(135deg, var(--accent), #5fd6ff);
|
|
94
|
+
box-shadow: 0 0 18px rgba(184, 243, 107, 0.3);
|
|
95
|
+
}
|
|
96
|
+
.tag { color: var(--muted); font-family: var(--mono); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; }
|
|
97
|
+
a { color: var(--accent); text-decoration: none; }
|
|
98
|
+
a:hover { text-decoration: underline; }
|
|
99
|
+
.meta { margin-left: auto; color: var(--muted); font-family: var(--mono); font-size: 11px; }
|
|
100
|
+
#viewport {
|
|
101
|
+
min-height: 0;
|
|
102
|
+
display: grid;
|
|
103
|
+
place-items: center;
|
|
104
|
+
padding: 20px;
|
|
105
|
+
overflow: hidden;
|
|
106
|
+
background:
|
|
107
|
+
radial-gradient(circle at 50% 20%, #141b24 0, transparent 55%),
|
|
108
|
+
#080a0d;
|
|
109
|
+
}
|
|
110
|
+
#stage-wrap {
|
|
111
|
+
position: relative;
|
|
112
|
+
overflow: hidden;
|
|
113
|
+
box-shadow: 0 30px 90px rgba(0, 0, 0, 0.55);
|
|
114
|
+
background: #000;
|
|
115
|
+
}
|
|
116
|
+
#stage {
|
|
117
|
+
position: relative;
|
|
118
|
+
transform-origin: top left;
|
|
119
|
+
overflow: hidden;
|
|
120
|
+
background: #000;
|
|
121
|
+
}
|
|
122
|
+
.timeline-layer { position: absolute; left: 50%; top: 50%; transform-origin: center; }
|
|
123
|
+
.timeline-layer iframe, .timeline-layer video, .timeline-layer img { display: block; border: 0; }
|
|
124
|
+
#timeline {
|
|
125
|
+
position: relative;
|
|
126
|
+
min-height: 0;
|
|
127
|
+
border-top: 1px solid var(--line);
|
|
128
|
+
background: var(--panel);
|
|
129
|
+
overflow: auto;
|
|
130
|
+
padding: 10px 16px 16px;
|
|
131
|
+
}
|
|
132
|
+
#tracks { position: relative; display: flex; flex-direction: column; gap: 6px; }
|
|
133
|
+
.track { display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: 8px; min-height: 30px; }
|
|
134
|
+
.track-label {
|
|
135
|
+
display: flex; align-items: center; justify-content: flex-end;
|
|
136
|
+
color: var(--muted); font-family: var(--mono); font-size: 10px;
|
|
137
|
+
text-transform: uppercase; letter-spacing: 0.06em;
|
|
138
|
+
}
|
|
139
|
+
.lane {
|
|
140
|
+
position: relative; border: 1px solid var(--line); border-radius: 6px;
|
|
141
|
+
background: #0d1116; overflow: hidden;
|
|
142
|
+
}
|
|
143
|
+
.clip {
|
|
144
|
+
position: absolute; top: 3px; bottom: 3px; border-radius: 4px;
|
|
145
|
+
background: linear-gradient(180deg, #2a4058, #1d2c3d);
|
|
146
|
+
border: 1px solid #3b5672; color: #dce8f5;
|
|
147
|
+
font: 10px/1 var(--mono); padding: 6px 7px; overflow: hidden;
|
|
148
|
+
white-space: nowrap; text-overflow: ellipsis;
|
|
149
|
+
}
|
|
150
|
+
.clip.composition { background: linear-gradient(180deg, #314a33, #203120); border-color: #4d704d; }
|
|
151
|
+
.clip.audio { background: linear-gradient(180deg, #4a3b22, #302516); border-color: #6e5631; }
|
|
152
|
+
.clip.nested { background: linear-gradient(180deg, #443152, #2c2036); border-color: #63477a; }
|
|
153
|
+
#playhead {
|
|
154
|
+
position: absolute; top: 0; bottom: 0; width: 1px; background: var(--accent);
|
|
155
|
+
box-shadow: 0 0 12px rgba(184, 243, 107, 0.7); pointer-events: none;
|
|
156
|
+
}
|
|
157
|
+
#error, #diagnostics {
|
|
158
|
+
margin-top: 10px; border: 1px solid #4b3030; border-radius: 6px;
|
|
159
|
+
padding: 9px 11px; background: #1b1111; color: var(--danger);
|
|
160
|
+
font-family: var(--mono); font-size: 11px; white-space: pre-wrap;
|
|
161
|
+
}
|
|
162
|
+
#diagnostics { border-color: var(--line); background: var(--panel-2); color: var(--muted); }
|
|
163
|
+
footer {
|
|
164
|
+
display: flex; align-items: center; gap: 12px; padding: 0 18px;
|
|
165
|
+
border-top: 1px solid var(--line); background: var(--panel);
|
|
166
|
+
}
|
|
167
|
+
button {
|
|
168
|
+
border: 1px solid var(--line); background: var(--panel-2); color: var(--text);
|
|
169
|
+
border-radius: 6px; padding: 8px 12px; cursor: pointer;
|
|
170
|
+
}
|
|
171
|
+
button:hover { border-color: #3a4654; }
|
|
172
|
+
.step {
|
|
173
|
+
width: 32px; height: 32px; padding: 0; border-radius: 6px;
|
|
174
|
+
color: var(--muted); font-size: 18px; line-height: 1;
|
|
175
|
+
}
|
|
176
|
+
.jump {
|
|
177
|
+
height: 32px; padding: 0 8px; border-radius: 6px;
|
|
178
|
+
color: var(--muted); font: 10px var(--mono);
|
|
179
|
+
}
|
|
180
|
+
.step:hover:not(:disabled), .jump:hover:not(:disabled) { color: var(--text); }
|
|
181
|
+
.play {
|
|
182
|
+
width: 38px; height: 38px; border-radius: 50%; border: 0;
|
|
183
|
+
background: var(--accent); color: var(--accent-ink);
|
|
184
|
+
font-size: 15px; font-weight: 700;
|
|
185
|
+
}
|
|
186
|
+
.scrub { flex: 1; accent-color: var(--accent); }
|
|
187
|
+
.readout { min-width: 150px; text-align: right; color: var(--muted); font-family: var(--mono); font-size: 11px; }
|
|
188
|
+
.readout b { color: var(--text); font-weight: 600; }
|
|
189
|
+
.audio { display: flex; align-items: center; gap: 8px; color: var(--muted); font-family: var(--mono); font-size: 11px; }
|
|
190
|
+
.audio input[type="range"] { width: 80px; accent-color: var(--accent); }
|
|
191
|
+
.hint { color: var(--muted); font-family: var(--mono); font-size: 10px; }
|
|
192
|
+
</style>
|
|
193
|
+
<script type="importmap" data-fy-host>${IMPORT_MAP}</script>
|
|
194
|
+
</head>
|
|
195
|
+
<body data-timeline-id="${id}" data-embed="0">
|
|
196
|
+
<div id="app">
|
|
197
|
+
<header>
|
|
198
|
+
<div class="brand"><span class="mark"></span>frameyard <span class="tag">timeline</span></div>
|
|
199
|
+
<a href="/__frameyard/">← compositions</a>
|
|
200
|
+
<span id="title" class="hint"></span>
|
|
201
|
+
<div id="meta" class="meta"></div>
|
|
202
|
+
</header>
|
|
203
|
+
<main id="viewport">
|
|
204
|
+
<div id="stage-wrap"><div id="stage"></div></div>
|
|
205
|
+
</main>
|
|
206
|
+
<section id="timeline">
|
|
207
|
+
<div id="tracks"></div>
|
|
208
|
+
<div id="playhead"></div>
|
|
209
|
+
<div id="error" role="alert" hidden></div>
|
|
210
|
+
<div id="diagnostics" hidden></div>
|
|
211
|
+
</section>
|
|
212
|
+
<footer>
|
|
213
|
+
<button id="jump-back" class="jump" type="button" aria-label="Back one second" aria-keyshortcuts="Shift+ArrowLeft" title="Back one second (Shift+←)">−1s</button>
|
|
214
|
+
<button id="step-back" class="step" type="button" aria-label="Previous frame" aria-keyshortcuts="ArrowLeft" title="Previous frame (←)">‹</button>
|
|
215
|
+
<button id="play-button" class="play" type="button" aria-label="Play">▶</button>
|
|
216
|
+
<button id="step-forward" class="step" type="button" aria-label="Next frame" aria-keyshortcuts="ArrowRight" title="Next frame (→)">›</button>
|
|
217
|
+
<button id="jump-forward" class="jump" type="button" aria-label="Forward one second" aria-keyshortcuts="Shift+ArrowRight" title="Forward one second (Shift+→)">+1s</button>
|
|
218
|
+
<input id="scrub" class="scrub" type="range" min="0" max="0" value="0" step="1" aria-label="Timeline frame">
|
|
219
|
+
<div class="readout"><b id="frame">0</b> / <span id="duration">0</span> f <span id="clock">0.00s</span></div>
|
|
220
|
+
<label class="audio"><input id="mute" type="checkbox"> mute <input id="volume" type="range" min="0" max="1" step="0.01" value="0.8" aria-label="Audio volume"></label>
|
|
221
|
+
</footer>
|
|
222
|
+
</div>
|
|
223
|
+
<script type="module" src="/__frameyard/framework/preview/timeline-main.js"></script>
|
|
224
|
+
</body>
|
|
225
|
+
</html>`;
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=timeline-preview-html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeline-preview-html.js","sourceRoot":"","sources":["../../src/server/timeline-preview-html.ts"],"names":[],"mappings":"AAAA,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,SAAS,EAAE,EAAE;QAC7C,QAAQ,SAAS,EAAE,CAAC;YAClB,KAAK,GAAG;gBACN,OAAO,OAAO,CAAC;YACjB,KAAK,GAAG;gBACN,OAAO,MAAM,CAAC;YAChB,KAAK,GAAG;gBACN,OAAO,MAAM,CAAC;YAChB,KAAK,GAAG;gBACN,OAAO,QAAQ,CAAC;YAClB;gBACE,OAAO,OAAO,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;IAChC,OAAO,EAAE;QACP,SAAS,EAAE,iCAAiC;QAC5C,mBAAmB,EAAE,yCAAyC;KAC/D;CACF,CAAC,CAAC;AAEH,MAAM,UAAU,yBAAyB,CAAC,UAAkB,EAAE,UAA+B,EAAE;IAC7F,MAAM,EAAE,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IAClC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,OAAO;;;;;gCAKqB,EAAE;;;;;;;4CAOU,UAAU;;4BAE1B,EAAE;;;;QAItB,CAAC;IACP,CAAC;IAED,OAAO;;;;;gCAKuB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4CA8IU,UAAU;;4BAE1B,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA8BtB,CAAC;AACT,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ResolvedTimeline, ResolvedTimelineClip } from "./resolve.js";
|
|
2
|
+
export interface TimelineRenderLayer {
|
|
3
|
+
clip: ResolvedTimelineClip;
|
|
4
|
+
/** Source frame used at the segment's first timeline frame. */
|
|
5
|
+
sourceStart: number;
|
|
6
|
+
}
|
|
7
|
+
export interface TimelineRenderSegment {
|
|
8
|
+
index: number;
|
|
9
|
+
/** Inclusive first timeline frame. */
|
|
10
|
+
start: number;
|
|
11
|
+
/** Exclusive last timeline frame. */
|
|
12
|
+
end: number;
|
|
13
|
+
frames: number;
|
|
14
|
+
/** Visual layers from bottom to top. */
|
|
15
|
+
layers: TimelineRenderLayer[];
|
|
16
|
+
}
|
|
17
|
+
export interface TimelineRenderPlan {
|
|
18
|
+
timeline: ResolvedTimeline;
|
|
19
|
+
maxSegmentFrames: number;
|
|
20
|
+
segments: TimelineRenderSegment[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Splits a resolved timeline at visual clip boundaries and a maximum segment
|
|
24
|
+
* duration. Every segment has a fixed set of visual layers; audio is mixed
|
|
25
|
+
* globally and never appears here.
|
|
26
|
+
*/
|
|
27
|
+
export declare function planTimelineRender(timeline: ResolvedTimeline, options?: {
|
|
28
|
+
maxSegmentFrames?: number;
|
|
29
|
+
startFrame?: number;
|
|
30
|
+
endFrame?: number;
|
|
31
|
+
}): TimelineRenderPlan;
|
|
32
|
+
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/timeline/plan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAE3E,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,MAAM,EAAE,mBAAmB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,qBAAqB,EAAE,CAAC;CACnC;AAMD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClF,kBAAkB,CAiEpB"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
function isVisualClip(clip) {
|
|
2
|
+
return clip.source.kind !== "audio";
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Splits a resolved timeline at visual clip boundaries and a maximum segment
|
|
6
|
+
* duration. Every segment has a fixed set of visual layers; audio is mixed
|
|
7
|
+
* globally and never appears here.
|
|
8
|
+
*/
|
|
9
|
+
export function planTimelineRender(timeline, options = {}) {
|
|
10
|
+
const maxSegmentFrames = options.maxSegmentFrames ?? Math.max(1, Math.round(timeline.format.fps * 10));
|
|
11
|
+
if (!Number.isInteger(maxSegmentFrames) || maxSegmentFrames <= 0) {
|
|
12
|
+
throw new Error("maxSegmentFrames must be a positive integer.");
|
|
13
|
+
}
|
|
14
|
+
const startFrame = options.startFrame ?? 0;
|
|
15
|
+
const endFrame = options.endFrame ?? timeline.durationInFrames;
|
|
16
|
+
if (!Number.isInteger(startFrame) ||
|
|
17
|
+
!Number.isInteger(endFrame) ||
|
|
18
|
+
startFrame < 0 ||
|
|
19
|
+
endFrame > timeline.durationInFrames ||
|
|
20
|
+
endFrame <= startFrame) {
|
|
21
|
+
throw new Error(`Invalid timeline range ${startFrame}..${endFrame}.`);
|
|
22
|
+
}
|
|
23
|
+
const visualClips = timeline.clips.filter(isVisualClip);
|
|
24
|
+
const boundaries = new Set([startFrame, endFrame]);
|
|
25
|
+
for (const clip of visualClips) {
|
|
26
|
+
if (clip.start > startFrame && clip.start < endFrame) {
|
|
27
|
+
boundaries.add(clip.start);
|
|
28
|
+
}
|
|
29
|
+
if (clip.end > startFrame && clip.end < endFrame) {
|
|
30
|
+
boundaries.add(clip.end);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const ordered = [...boundaries].sort((left, right) => left - right);
|
|
34
|
+
const ranges = [];
|
|
35
|
+
for (let index = 1; index < ordered.length; index += 1) {
|
|
36
|
+
const start = ordered[index - 1];
|
|
37
|
+
const end = ordered[index];
|
|
38
|
+
if (end <= start) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
for (let cursor = start; cursor < end; cursor += maxSegmentFrames) {
|
|
42
|
+
ranges.push({ start: cursor, end: Math.min(end, cursor + maxSegmentFrames) });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const trackOrder = new Map(timeline.tracks.map((track, index) => [track.id, index]));
|
|
46
|
+
const segments = ranges.map((range, index) => {
|
|
47
|
+
const layers = visualClips
|
|
48
|
+
.filter((clip) => clip.start <= range.start && clip.end >= range.end)
|
|
49
|
+
.sort((left, right) => {
|
|
50
|
+
const leftOrder = trackOrder.get(left.track) ?? Number.MAX_SAFE_INTEGER;
|
|
51
|
+
const rightOrder = trackOrder.get(right.track) ?? Number.MAX_SAFE_INTEGER;
|
|
52
|
+
return leftOrder - rightOrder;
|
|
53
|
+
})
|
|
54
|
+
.map((clip) => ({
|
|
55
|
+
clip,
|
|
56
|
+
sourceStart: clip.sourceIn + (range.start - clip.start),
|
|
57
|
+
}));
|
|
58
|
+
return {
|
|
59
|
+
index,
|
|
60
|
+
start: range.start,
|
|
61
|
+
end: range.end,
|
|
62
|
+
frames: range.end - range.start,
|
|
63
|
+
layers,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
return { timeline, maxSegmentFrames, segments };
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/timeline/plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { CompositionDiagnostic, CompositionMetadata, ControllerDeclaration, ResolvedAudioClip, ResolvedTranscriptClip } from "../contracts.js";
|
|
2
|
+
import type { Project, ProjectFormat, ProjectTimeline } from "../project.js";
|
|
3
|
+
import { type TimelineClip, type TimelineTrack } from "./schema.js";
|
|
4
|
+
export interface ResolvedCompositionSource {
|
|
5
|
+
kind: "composition";
|
|
6
|
+
id: string;
|
|
7
|
+
entry: string;
|
|
8
|
+
metadata: CompositionMetadata;
|
|
9
|
+
controller: ControllerDeclaration | null;
|
|
10
|
+
durationInFrames: number;
|
|
11
|
+
audioClips: readonly ResolvedAudioClip[];
|
|
12
|
+
embeddedAudioClips: readonly ResolvedAudioClip[];
|
|
13
|
+
transcriptClips: readonly ResolvedTranscriptClip[];
|
|
14
|
+
}
|
|
15
|
+
export interface ResolvedNestedTimelineSource {
|
|
16
|
+
kind: "timeline";
|
|
17
|
+
id: string;
|
|
18
|
+
entry: string;
|
|
19
|
+
durationInFrames: number;
|
|
20
|
+
timeline: ResolvedTimeline;
|
|
21
|
+
}
|
|
22
|
+
export interface ResolvedMediaSource {
|
|
23
|
+
kind: "video" | "image" | "audio";
|
|
24
|
+
src: string;
|
|
25
|
+
absolutePath: string;
|
|
26
|
+
durationInFrames?: number;
|
|
27
|
+
width?: number;
|
|
28
|
+
height?: number;
|
|
29
|
+
hasVideo: boolean;
|
|
30
|
+
hasAudio: boolean;
|
|
31
|
+
}
|
|
32
|
+
export type ResolvedTimelineSource = ResolvedCompositionSource | ResolvedNestedTimelineSource | ResolvedMediaSource;
|
|
33
|
+
export interface ResolvedTimelineClip extends Omit<TimelineClip, "source" | "duration"> {
|
|
34
|
+
source: ResolvedTimelineSource;
|
|
35
|
+
duration: number;
|
|
36
|
+
end: number;
|
|
37
|
+
}
|
|
38
|
+
export interface ResolvedTimeline {
|
|
39
|
+
id: string;
|
|
40
|
+
entry: string;
|
|
41
|
+
name?: string;
|
|
42
|
+
background?: string;
|
|
43
|
+
format: ProjectFormat;
|
|
44
|
+
durationInFrames: number;
|
|
45
|
+
tracks: TimelineTrack[];
|
|
46
|
+
clips: ResolvedTimelineClip[];
|
|
47
|
+
}
|
|
48
|
+
export interface ResolveTimelineResult {
|
|
49
|
+
timeline: ResolvedTimeline | null;
|
|
50
|
+
diagnostics: CompositionDiagnostic[];
|
|
51
|
+
}
|
|
52
|
+
/** Resolves a project timeline, its references, media, and nested timelines. */
|
|
53
|
+
export declare function resolveProjectTimeline(options: {
|
|
54
|
+
project: Project;
|
|
55
|
+
timeline: ProjectTimeline;
|
|
56
|
+
signal?: AbortSignal;
|
|
57
|
+
/** Render-time props overrides keyed by top-level composition clip ID. */
|
|
58
|
+
propsOverrides?: Record<string, unknown>;
|
|
59
|
+
}): Promise<ResolveTimelineResult>;
|
|
60
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/timeline/resolve.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,qBAAqB,EAErB,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,sBAAsB,EACvB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAE7E,OAAO,EAAyB,KAAK,YAAY,EAAyB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAElH,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,aAAa,CAAC;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,UAAU,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACzC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACzC,kBAAkB,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACjD,eAAe,EAAE,SAAS,sBAAsB,EAAE,CAAC;CACpD;AAED,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,sBAAsB,GAAG,yBAAyB,GAAG,4BAA4B,GAAG,mBAAmB,CAAC;AAEpH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,YAAY,EAAE,QAAQ,GAAG,UAAU,CAAC;IACrF,MAAM,EAAE,sBAAsB,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,KAAK,EAAE,oBAAoB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,WAAW,EAAE,qBAAqB,EAAE,CAAC;CACtC;AAytBD,gFAAgF;AAChF,wBAAsB,sBAAsB,CAAC,OAAO,EAAE;IACpD,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,eAAe,CAAC;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAUjC"}
|