@pixerate/editor 0.115.0 → 0.116.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 (117) hide show
  1. package/THIRD_PARTY_NOTICES.md +3 -0
  2. package/dist/animation-editor/index.cjs +54 -54
  3. package/dist/animation-editor/index.d.cts +2 -2
  4. package/dist/animation-editor/index.d.ts +2 -2
  5. package/dist/animation-editor/index.js +2 -2
  6. package/dist/{chunk-DFQEX3AM.cjs → chunk-2UT4GXYA.cjs} +133 -8
  7. package/dist/chunk-2UT4GXYA.cjs.map +1 -0
  8. package/dist/{chunk-VXR7XWMF.js → chunk-7LNZAWSJ.js} +437 -41
  9. package/dist/chunk-7LNZAWSJ.js.map +1 -0
  10. package/dist/{chunk-LKCYANMW.js → chunk-ARQ7PQ6Q.js} +129 -4
  11. package/dist/chunk-ARQ7PQ6Q.js.map +1 -0
  12. package/dist/chunk-COAMRKVI.cjs +159 -0
  13. package/dist/chunk-COAMRKVI.cjs.map +1 -0
  14. package/dist/{chunk-C6C5PDKU.js → chunk-D53KSJUV.js} +3 -322
  15. package/dist/chunk-D53KSJUV.js.map +1 -0
  16. package/dist/chunk-D53VQGEJ.js +3262 -0
  17. package/dist/chunk-D53VQGEJ.js.map +1 -0
  18. package/dist/chunk-EJVDBITD.cjs +501 -0
  19. package/dist/chunk-EJVDBITD.cjs.map +1 -0
  20. package/dist/{chunk-LPLCWZTM.cjs → chunk-FODKPD44.cjs} +555 -159
  21. package/dist/chunk-FODKPD44.cjs.map +1 -0
  22. package/dist/chunk-HOCPSFGA.cjs +3262 -0
  23. package/dist/chunk-HOCPSFGA.cjs.map +1 -0
  24. package/dist/chunk-IPFRWILU.js +1908 -0
  25. package/dist/chunk-IPFRWILU.js.map +1 -0
  26. package/dist/{chunk-FV5AXTSF.cjs → chunk-JSSBHMMU.cjs} +36 -188
  27. package/dist/chunk-JSSBHMMU.cjs.map +1 -0
  28. package/dist/chunk-LF4WOZXZ.js +159 -0
  29. package/dist/chunk-LF4WOZXZ.js.map +1 -0
  30. package/dist/{chunk-26PGXHY7.cjs → chunk-M7O4BJRH.cjs} +36 -36
  31. package/dist/{chunk-26PGXHY7.cjs.map → chunk-M7O4BJRH.cjs.map} +1 -1
  32. package/dist/chunk-N3467VD6.js +501 -0
  33. package/dist/chunk-N3467VD6.js.map +1 -0
  34. package/dist/{chunk-ST7E3NEE.cjs → chunk-PAOWHJ5O.cjs} +18 -337
  35. package/dist/chunk-PAOWHJ5O.cjs.map +1 -0
  36. package/dist/{chunk-XICA4PVO.js → chunk-SEBYNBHW.js} +10 -10
  37. package/dist/chunk-SMJGE7HM.cjs +1908 -0
  38. package/dist/chunk-SMJGE7HM.cjs.map +1 -0
  39. package/dist/{chunk-MMVHBKMM.js → chunk-T2P5TIZ6.js} +2 -2
  40. package/dist/{chunk-FGW5OWJ4.js → chunk-VJUML33I.js} +4 -156
  41. package/dist/chunk-VJUML33I.js.map +1 -0
  42. package/dist/{chunk-I7FQBWMQ.cjs → chunk-ZYZGEILO.cjs} +239 -239
  43. package/dist/{chunk-I7FQBWMQ.cjs.map → chunk-ZYZGEILO.cjs.map} +1 -1
  44. package/dist/export/index.cjs +4 -2
  45. package/dist/export/index.cjs.map +1 -1
  46. package/dist/export/index.js +5 -3
  47. package/dist/{export-BJE8T0YA.d.cts → export-BXxSGBCq.d.cts} +2 -2
  48. package/dist/{export-Con3UKkm.d.ts → export-Dm44YUvS.d.ts} +2 -2
  49. package/dist/export-Dv0-ZVrV.d.cts +501 -0
  50. package/dist/export-Dv0-ZVrV.d.ts +501 -0
  51. package/dist/{retouch-Do6tBs2H.d.ts → flow-BMtHAmg4.d.ts} +561 -2
  52. package/dist/{retouch-Bxkuc0KH.d.cts → flow-COCB42TP.d.cts} +561 -2
  53. package/dist/image-editor/index.cjs +957 -795
  54. package/dist/image-editor/index.cjs.map +1 -1
  55. package/dist/image-editor/index.d.cts +6 -6
  56. package/dist/image-editor/index.d.ts +6 -6
  57. package/dist/image-editor/index.js +180 -18
  58. package/dist/image-editor/index.js.map +1 -1
  59. package/dist/index.cjs +4 -2
  60. package/dist/index.cjs.map +1 -1
  61. package/dist/index.js +5 -3
  62. package/dist/index.js.map +1 -1
  63. package/dist/kernel-wasm-WYCGNZR4.js +10 -0
  64. package/dist/kernel-wasm-WYCGNZR4.js.map +1 -0
  65. package/dist/kernel-wasm-YPVAEMVN.cjs +10 -0
  66. package/dist/kernel-wasm-YPVAEMVN.cjs.map +1 -0
  67. package/dist/paint/index.cjs +165 -3
  68. package/dist/paint/index.cjs.map +1 -1
  69. package/dist/paint/index.d.cts +225 -3
  70. package/dist/paint/index.d.ts +225 -3
  71. package/dist/paint/index.js +166 -4
  72. package/dist/paint-editor/index.cjs +4690 -845
  73. package/dist/paint-editor/index.cjs.map +1 -1
  74. package/dist/paint-editor/index.d.cts +1525 -277
  75. package/dist/paint-editor/index.d.ts +1525 -277
  76. package/dist/paint-editor/index.js +4560 -715
  77. package/dist/paint-editor/index.js.map +1 -1
  78. package/dist/raster/index.cjs +4 -3
  79. package/dist/raster/index.cjs.map +1 -1
  80. package/dist/raster/index.js +10 -9
  81. package/dist/{svg-BnRj0q8i.d.cts → svg-CVdFc80-.d.cts} +2 -2
  82. package/dist/{svg-CmmBFfGT.d.ts → svg-DlEuhwoX.d.ts} +2 -2
  83. package/dist/{types-1SUQKDAw.d.ts → types-CDLGhAZp.d.ts} +40 -2
  84. package/dist/{types-yINF17Ta.d.cts → types-Tfly4Aya.d.cts} +40 -2
  85. package/dist/vector/index.cjs +1897 -975
  86. package/dist/vector/index.cjs.map +1 -1
  87. package/dist/vector/index.d.cts +292 -32
  88. package/dist/vector/index.d.ts +292 -32
  89. package/dist/vector/index.js +928 -6
  90. package/dist/vector/index.js.map +1 -1
  91. package/dist/video-editor/index.cjs +502 -2165
  92. package/dist/video-editor/index.cjs.map +1 -1
  93. package/dist/video-editor/index.d.cts +3 -501
  94. package/dist/video-editor/index.d.ts +3 -501
  95. package/dist/video-editor/index.js +151 -1814
  96. package/dist/video-editor/index.js.map +1 -1
  97. package/package.json +1 -1
  98. package/skills/pixerate-image-editor/references/photoshop-files.md +7 -3
  99. package/skills/pixerate-vector/SKILL.md +3 -0
  100. package/dist/chunk-C6C5PDKU.js.map +0 -1
  101. package/dist/chunk-DFQEX3AM.cjs.map +0 -1
  102. package/dist/chunk-FGW5OWJ4.js.map +0 -1
  103. package/dist/chunk-FV5AXTSF.cjs.map +0 -1
  104. package/dist/chunk-GX332W2C.js +0 -181
  105. package/dist/chunk-GX332W2C.js.map +0 -1
  106. package/dist/chunk-LKCYANMW.js.map +0 -1
  107. package/dist/chunk-LPLCWZTM.cjs.map +0 -1
  108. package/dist/chunk-MUUXNRU4.cjs +0 -181
  109. package/dist/chunk-MUUXNRU4.cjs.map +0 -1
  110. package/dist/chunk-ST7E3NEE.cjs.map +0 -1
  111. package/dist/chunk-TCKIXI46.cjs +0 -1136
  112. package/dist/chunk-TCKIXI46.cjs.map +0 -1
  113. package/dist/chunk-UBIXVXPR.js +0 -1136
  114. package/dist/chunk-UBIXVXPR.js.map +0 -1
  115. package/dist/chunk-VXR7XWMF.js.map +0 -1
  116. /package/dist/{chunk-XICA4PVO.js.map → chunk-SEBYNBHW.js.map} +0 -0
  117. /package/dist/{chunk-MMVHBKMM.js.map → chunk-T2P5TIZ6.js.map} +0 -0
@@ -1,4 +1,126 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6;
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+
118
+
119
+
120
+
121
+
122
+ var _chunkSMJGE7HMcjs = require('../chunk-SMJGE7HM.cjs');
123
+
2
124
 
3
125
  var _chunkWAJQKWQUcjs = require('../chunk-WAJQKWQU.cjs');
4
126
 
@@ -11,708 +133,6 @@ require('../chunk-2FHLHZAL.cjs');
11
133
  require('../chunk-MR3Z4OQI.cjs');
12
134
  require('../chunk-A6PHCAGX.cjs');
13
135
 
14
- // src/video-editor/types.ts
15
- var VIDEO_FITS = ["contain", "cover", "fill", "none"];
16
- var VIDEO_TRANSITION_TYPES = ["crossfade", "dip", "wipe", "push", "slide", "iris"];
17
- var VIDEO_WIPE_DIRECTIONS = ["left", "right", "up", "down"];
18
- var videoTransitionAudioCurve = (t) => _nullishCoalesce(t.audioCurve, () => ( (t.type === "dip" ? "linear" : "equal-power")));
19
- var VIDEO_BLEND_MODES = ["normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "add"];
20
- var VIDEO_CLIP_ANIMATION_EFFECTS = ["fade", "rise", "drop", "slide-left", "slide-right", "zoom", "pop"];
21
- var VIDEO_FADE_CURVES = ["linear", "equal-power", "s-curve", "exponential", "logarithmic"];
22
- var VIDEO_PLACEMENT_MODES = ["refuse", "overwrite", "insert"];
23
- var VIDEO_CLIP_TYPES = ["media", "text", "color", "sequence"];
24
-
25
- // src/video-editor/keyframes.ts
26
- var VIDEO_EASE_NAMES = ["linear", "hold", "ease-in", "ease-out", "ease-in-out"];
27
- var VIDEO_KEYFRAME_PROPERTIES = ["opacity", "volume", "x", "y", "scale", "rotation"];
28
- var VIDEO_TRANSFORM_PROPERTIES = ["x", "y", "scale", "rotation"];
29
- var VIDEO_KEYFRAME_LIMITS = {
30
- opacity: { default: 1, min: 0, max: 1 },
31
- volume: { default: 1, min: 0, max: 4 },
32
- x: { default: 0 },
33
- y: { default: 0 },
34
- scale: { default: 1, min: 0 },
35
- rotation: { default: 0 }
36
- };
37
- var NAMED = {
38
- "ease-in": [0.42, 0, 1, 1],
39
- "ease-out": [0, 0, 0.58, 1],
40
- "ease-in-out": [0.42, 0, 0.58, 1]
41
- };
42
- var isVideoKeyframes = (v) => typeof v === "object" && v !== null && Array.isArray(v.keys);
43
- function videoEaseBezier(ease) {
44
- if (ease === void 0 || ease === "linear" || ease === "hold") return null;
45
- return Array.isArray(ease) ? ease : NAMED[ease];
46
- }
47
- var bez = (a, b, s) => 3 * a * (1 - s) * (1 - s) * s + 3 * b * (1 - s) * s * s + s * s * s;
48
- var bezSlope = (a, b, s) => 3 * a * (1 - s) * (1 - s) + 6 * (b - a) * (1 - s) * s + 3 * (1 - b) * s * s;
49
- function bezierParam(x1, x2, x) {
50
- let s = x;
51
- for (let i = 0; i < 8; i++) {
52
- const err = bez(x1, x2, s) - x;
53
- if (Math.abs(err) < 1e-7) return s;
54
- const d = bezSlope(x1, x2, s);
55
- if (Math.abs(d) < 1e-6) break;
56
- s -= err / d;
57
- if (s < 0 || s > 1) break;
58
- }
59
- let lo = 0;
60
- let hi = 1;
61
- s = x;
62
- for (let i = 0; i < 60; i++) {
63
- const v = bez(x1, x2, s);
64
- if (Math.abs(v - x) < 1e-9) break;
65
- if (v < x) lo = s;
66
- else hi = s;
67
- s = (lo + hi) / 2;
68
- }
69
- return s;
70
- }
71
- function videoEaseProgress(ease, p) {
72
- if (p <= 0) return 0;
73
- if (p >= 1) return ease === "hold" ? 0 : 1;
74
- if (ease === "hold") return 0;
75
- const b = videoEaseBezier(ease);
76
- if (!b) return p;
77
- return bez(b[1], b[3], bezierParam(b[0], b[2], p));
78
- }
79
- function keyBefore(keys, t) {
80
- let lo = 0;
81
- let hi = keys.length - 1;
82
- if (t < keys[0].t) return -1;
83
- while (lo < hi) {
84
- const mid = lo + hi + 1 >> 1;
85
- if (keys[mid].t <= t) lo = mid;
86
- else hi = mid - 1;
87
- }
88
- return lo;
89
- }
90
- function videoValueAt(prop, clipTime, fallback = 0) {
91
- if (prop === void 0) return fallback;
92
- if (typeof prop === "number") return prop;
93
- const { keys } = prop;
94
- if (!keys.length) return fallback;
95
- const i = keyBefore(keys, clipTime);
96
- if (i < 0) return keys[0].v;
97
- if (i >= keys.length - 1) return keys[keys.length - 1].v;
98
- const a = keys[i];
99
- const b = keys[i + 1];
100
- if (a.ease === "hold") return a.v;
101
- return a.v + (b.v - a.v) * videoEaseProgress(a.ease, (clipTime - a.t) / (b.t - a.t));
102
- }
103
- var videoKeysOf = (prop) => isVideoKeyframes(prop) ? prop.keys : [];
104
- function retimeVideoAnimatable(prop, dt, k = 1) {
105
- if (!isVideoKeyframes(prop)) return prop;
106
- return { keys: prop.keys.map((key) => ({ ...key, t: key.t * k + dt })) };
107
- }
108
- function splitEase(ease, p) {
109
- if (ease === "hold") return ["hold", "hold"];
110
- const b = videoEaseBezier(ease);
111
- if (!b) return [ease, ease];
112
- const s = bezierParam(b[0], b[2], p);
113
- const lerp = (u, v, k) => [u[0] + (v[0] - u[0]) * k, u[1] + (v[1] - u[1]) * k];
114
- const P0 = [0, 0];
115
- const P1 = [b[0], b[1]];
116
- const P2 = [b[2], b[3]];
117
- const P3 = [1, 1];
118
- const A = lerp(P0, P1, s);
119
- const B = lerp(P1, P2, s);
120
- const C = lerp(P2, P3, s);
121
- const AB = lerp(A, B, s);
122
- const BC = lerp(B, C, s);
123
- const M = lerp(AB, BC, s);
124
- const r = (v) => Math.round(v * 1e6) / 1e6;
125
- const left = Math.abs(M[1]) < 1e-9 ? "linear" : [r(A[0] / M[0]), r(A[1] / M[1]), r(AB[0] / M[0]), r(AB[1] / M[1])];
126
- const dx = 1 - M[0];
127
- const dy = 1 - M[1];
128
- const right = Math.abs(dy) < 1e-9 ? "linear" : [r((BC[0] - M[0]) / dx), r((BC[1] - M[1]) / dy), r((C[0] - M[0]) / dx), r((C[1] - M[1]) / dy)];
129
- return [left, right];
130
- }
131
- function splitVideoAnimatable(prop, at) {
132
- if (!isVideoKeyframes(prop)) return [prop, prop];
133
- const keys = prop.keys;
134
- const eps = 1e-6;
135
- const v = videoValueAt(prop, at);
136
- const i = keyBefore(keys, at);
137
- const exact = i >= 0 && Math.abs(keys[i].t - at) < eps;
138
- let leftEase;
139
- let rightEase;
140
- if (exact) rightEase = keys[i].ease;
141
- else if (i >= 0 && i < keys.length - 1) {
142
- const a = keys[i];
143
- [leftEase, rightEase] = splitEase(a.ease, (at - a.t) / (keys[i + 1].t - a.t));
144
- }
145
- const before = keys.slice(0, exact ? i : i + 1).map((k) => ({ ...k }));
146
- if (before.length && leftEase !== void 0) before[before.length - 1] = withEase(before[before.length - 1], leftEase);
147
- const left = [...before, { t: at, v }];
148
- const right = [withEase({ t: 0, v }, rightEase), ...keys.slice(i + 1).map((k) => ({ ...k, t: k.t - at }))];
149
- return [{ keys: left }, { keys: right }];
150
- }
151
- var withEase = (key, ease) => {
152
- const { ease: _old, ...rest } = key;
153
- return ease === void 0 || ease === "linear" ? rest : { ...rest, ease };
154
- };
155
- function sortVideoKeys(keys) {
156
- const sorted = [...keys].sort((a, b) => a.t - b.t);
157
- for (let i = 1; i < sorted.length; i++) if (Math.abs(sorted[i].t - sorted[i - 1].t) < 1e-6) throw new Error(`Two keys are at ${sorted[i].t} ms; give each key its own time.`);
158
- return sorted;
159
- }
160
- function setVideoKey(prop, key, fallback) {
161
- const keys = isVideoKeyframes(prop) ? prop.keys.filter((k) => Math.abs(k.t - key.t) >= 1e-6) : [];
162
- if (!isVideoKeyframes(prop)) {
163
- const v = _nullishCoalesce(prop, () => ( fallback));
164
- if (key.t !== 0) keys.push({ t: 0, v });
165
- }
166
- return { keys: sortVideoKeys([...keys, key]) };
167
- }
168
- function removeVideoKey(prop, t) {
169
- const keys = prop.keys.filter((k) => Math.abs(k.t - t) >= 1e-6);
170
- return keys.length ? { keys } : prop.keys[0].v;
171
- }
172
- var fmt = (v) => {
173
- const r = Math.round(v * 1e4) / 1e4;
174
- return Object.is(r, -0) ? "0" : String(r);
175
- };
176
- var signed = (v) => fmt(v).startsWith("-") ? fmt(v) : `+${fmt(v)}`;
177
- function videoKeyframeBreakpoints(prop, step) {
178
- const { keys } = prop;
179
- const points = [];
180
- const holds = /* @__PURE__ */ new Set();
181
- for (let i = 0; i < keys.length; i++) {
182
- const a = keys[i];
183
- points.push([a.t, a.v]);
184
- const b = keys[i + 1];
185
- if (!b) break;
186
- if (a.ease === "hold") {
187
- holds.add(points.length - 1);
188
- continue;
189
- }
190
- if (!videoEaseBezier(a.ease)) continue;
191
- const n2 = Math.max(1, Math.ceil((b.t - a.t) / step));
192
- for (let k = 1; k < n2; k++) {
193
- const t = a.t + (b.t - a.t) * k / n2;
194
- points.push([t, videoValueAt(prop, t)]);
195
- }
196
- }
197
- return { points, holds };
198
- }
199
- function videoKeyframeFfmpegExpr(prop, time, fallback, step) {
200
- if (!isVideoKeyframes(prop)) return fmt(_nullishCoalesce(prop, () => ( fallback)));
201
- const { points, holds } = videoKeyframeBreakpoints(prop, step);
202
- if (points.length === 1 || points.every(([, v]) => v === points[0][1])) return fmt(points[0][1]);
203
- let expr = fmt(points[points.length - 1][1]);
204
- for (let i = points.length - 2; i >= 0; i--) {
205
- const [t0, v0] = points[i];
206
- const [t1, v1] = points[i + 1];
207
- const seg = holds.has(i) || v0 === v1 ? fmt(v0) : `${fmt(v0)}${signed((v1 - v0) / (t1 - t0))}*(${time}${signed(-t0)})`;
208
- expr = `if(lt(${time},${fmt(t1)}),${seg},${expr})`;
209
- }
210
- return `if(lt(${time},${fmt(points[0][0])}),${fmt(points[0][1])},${expr})`;
211
- }
212
-
213
- // src/video-editor/lut.ts
214
- var VIDEO_LUT_MAX_SIZE = 129;
215
- function parseVideoCubeLut(text) {
216
- let size = 0;
217
- let size1 = 0;
218
- let title;
219
- const domainMin = [0, 0, 0];
220
- const domainMax = [1, 1, 1];
221
- let data = null;
222
- let rows = 0;
223
- const lines = text.split(/\r\n|\r|\n/);
224
- const fail2 = (line, message) => {
225
- throw new Error(`Line ${line + 1} of the .cube file: ${message}.`);
226
- };
227
- const numbers = (parts, line) => parts.map((p) => {
228
- const v = Number(p);
229
- if (!Number.isFinite(v)) fail2(line, `"${p}" is not a number`);
230
- return v;
231
- });
232
- for (let i = 0; i < lines.length; i++) {
233
- const line = lines[i].replace(/#.*/, "").trim();
234
- if (!line) continue;
235
- const parts = line.split(/\s+/);
236
- const key = parts[0];
237
- if (/^[A-Za-z]/.test(key)) {
238
- if (rows) fail2(i, `"${key}" comes after the table; keywords go before it`);
239
- if (key === "TITLE") title = line.slice(5).trim().replace(/^"|"$/g, "");
240
- else if (key === "LUT_3D_SIZE") {
241
- size = Number(parts[1]);
242
- if (!Number.isInteger(size) || size < 2 || size > VIDEO_LUT_MAX_SIZE) fail2(i, `LUT_3D_SIZE must be a whole number from 2 to ${VIDEO_LUT_MAX_SIZE}, not "${_nullishCoalesce(parts[1], () => ( ""))}"`);
243
- } else if (key === "LUT_1D_SIZE") size1 = Number(parts[1]) || 1;
244
- else if (key === "DOMAIN_MIN" || key === "DOMAIN_MAX") {
245
- const v2 = numbers(parts.slice(1), i);
246
- if (v2.length !== 3) fail2(i, `${key} needs 3 numbers`);
247
- (key === "DOMAIN_MIN" ? domainMin : domainMax).splice(0, 3, ...v2);
248
- } else if (key === "LUT_3D_INPUT_RANGE") {
249
- const v2 = numbers(parts.slice(1), i);
250
- if (v2.length !== 2 || !(v2[1] > v2[0])) fail2(i, "LUT_3D_INPUT_RANGE needs a minimum and a larger maximum");
251
- domainMin.fill(v2[0]);
252
- domainMax.fill(v2[1]);
253
- }
254
- continue;
255
- }
256
- if (!size) fail2(i, size1 ? "this is a 1D LUT (LUT_1D_SIZE); only 3D LUTs (LUT_3D_SIZE) are supported" : "the table starts before LUT_3D_SIZE");
257
- data ??= new Float32Array(size * size * size * 3);
258
- const v = numbers(parts, i);
259
- if (v.length !== 3) fail2(i, `a table row needs 3 numbers, not ${v.length}`);
260
- if (rows >= size ** 3) fail2(i, `the table has more than the ${size ** 3} rows LUT_3D_SIZE ${size} needs`);
261
- data.set(v, rows * 3);
262
- rows++;
263
- }
264
- if (!size) throw new Error(size1 ? "The .cube file is a 1D LUT (LUT_1D_SIZE); only 3D LUTs (LUT_3D_SIZE) are supported." : "The .cube file has no LUT_3D_SIZE line, so it is not a 3D LUT.");
265
- if (rows !== size ** 3) throw new Error(`The .cube file has ${rows} table rows; LUT_3D_SIZE ${size} needs ${size ** 3}.`);
266
- for (let c = 0; c < 3; c++) if (!(domainMax[c] > domainMin[c])) throw new Error("The .cube file's DOMAIN_MAX must be larger than its DOMAIN_MIN on every channel.");
267
- return { ...title ? { title } : {}, size, domainMin, domainMax, data };
268
- }
269
- function videoLutFromFunction(size, fn, title) {
270
- const n2 = size - 1;
271
- const data = new Float32Array(size ** 3 * 3);
272
- let i = 0;
273
- for (let b = 0; b < size; b++)
274
- for (let g = 0; g < size; g++)
275
- for (let r = 0; r < size; r++) {
276
- data.set(fn([r / n2, g / n2, b / n2]), i);
277
- i += 3;
278
- }
279
- return { ...title ? { title } : {}, size, domainMin: [0, 0, 0], domainMax: [1, 1, 1], data };
280
- }
281
- function videoLutToCube(lut) {
282
- const f = (v) => (Math.round(v * 1e6) / 1e6).toString();
283
- const head = [...lut.title ? [`TITLE "${lut.title.replace(/"/g, "'")}"`] : [], `LUT_3D_SIZE ${lut.size}`];
284
- if (lut.domainMin.some((v) => v !== 0) || lut.domainMax.some((v) => v !== 1)) head.push(`DOMAIN_MIN ${lut.domainMin.map(f).join(" ")}`, `DOMAIN_MAX ${lut.domainMax.map(f).join(" ")}`);
285
- const rows = [];
286
- for (let i = 0; i < lut.data.length; i += 3) rows.push(`${f(lut.data[i])} ${f(lut.data[i + 1])} ${f(lut.data[i + 2])}`);
287
- return `${head.join("\n")}
288
- ${rows.join("\n")}
289
- `;
290
- }
291
- var clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
292
- function applyVideoLut(lut, rgb, out = [0, 0, 0]) {
293
- const { size, data, domainMin, domainMax } = lut;
294
- const n2 = size - 1;
295
- const x = clamp01((rgb[0] - domainMin[0]) / (domainMax[0] - domainMin[0])) * n2;
296
- const y = clamp01((rgb[1] - domainMin[1]) / (domainMax[1] - domainMin[1])) * n2;
297
- const z = clamp01((rgb[2] - domainMin[2]) / (domainMax[2] - domainMin[2])) * n2;
298
- const r0 = Math.min(n2 - 1, Math.floor(x));
299
- const g0 = Math.min(n2 - 1, Math.floor(y));
300
- const b0 = Math.min(n2 - 1, Math.floor(z));
301
- const fr = x - r0;
302
- const fg = y - g0;
303
- const fb = z - b0;
304
- const sr = 3;
305
- const sg = 3 * size;
306
- const sb = 3 * size * size;
307
- const o = r0 * sr + g0 * sg + b0 * sb;
308
- let a, c, w0, w1, w2, w3;
309
- if (fr > fg) {
310
- if (fg > fb) [a, c, w0, w1, w2, w3] = [sr, sr + sg, 1 - fr, fr - fg, fg - fb, fb];
311
- else if (fr > fb) [a, c, w0, w1, w2, w3] = [sr, sr + sb, 1 - fr, fr - fb, fb - fg, fg];
312
- else [a, c, w0, w1, w2, w3] = [sb, sb + sr, 1 - fb, fb - fr, fr - fg, fg];
313
- } else if (fb > fg) [a, c, w0, w1, w2, w3] = [sb, sb + sg, 1 - fb, fb - fg, fg - fr, fr];
314
- else if (fb > fr) [a, c, w0, w1, w2, w3] = [sg, sg + sb, 1 - fg, fg - fb, fb - fr, fr];
315
- else [a, c, w0, w1, w2, w3] = [sg, sg + sr, 1 - fg, fg - fr, fr - fb, fb];
316
- const d = sr + sg + sb;
317
- for (let k = 0; k < 3; k++) out[k] = clamp01(w0 * data[o + k] + w1 * data[o + a + k] + w2 * data[o + c + k] + w3 * data[o + d + k]);
318
- return out;
319
- }
320
- function applyVideoLutToPixels(lut, data, strength = 1) {
321
- const s = clamp01(strength);
322
- if (!s) return;
323
- const rgb = [0, 0, 0];
324
- const out = [0, 0, 0];
325
- for (let i = 0; i < data.length; i += 4) {
326
- if (!data[i + 3]) continue;
327
- rgb[0] = data[i] / 255;
328
- rgb[1] = data[i + 1] / 255;
329
- rgb[2] = data[i + 2] / 255;
330
- applyVideoLut(lut, rgb, out);
331
- for (let k = 0; k < 3; k++) data[i + k] = Math.round((rgb[k] + (out[k] - rgb[k]) * s) * 255);
332
- }
333
- }
334
- var approximations = /* @__PURE__ */ new WeakMap();
335
- function videoLutSvgApproximation(lut) {
336
- const cached = approximations.get(lut);
337
- if (cached) return cached;
338
- const points = 17;
339
- const tables = [[], [], []];
340
- for (let i = 0; i < points; i++) {
341
- const v = i / (points - 1);
342
- const out = applyVideoLut(lut, [v, v, v]);
343
- for (let k = 0; k < 3; k++) tables[k].push(out[k]);
344
- }
345
- const curve = (k, v) => {
346
- const x = clamp01(v) * (points - 1);
347
- const i = Math.min(points - 2, Math.floor(x));
348
- return tables[k][i] + (tables[k][i + 1] - tables[k][i]) * (x - i);
349
- };
350
- const ata = Array.from({ length: 4 }, () => [0, 0, 0, 0]);
351
- const atb = Array.from({ length: 3 }, () => [0, 0, 0, 0]);
352
- const grid = 9;
353
- for (let b = 0; b < grid; b++)
354
- for (let g = 0; g < grid; g++)
355
- for (let r = 0; r < grid; r++) {
356
- const x = [r / (grid - 1), g / (grid - 1), b / (grid - 1)];
357
- const z = [curve(0, x[0]), curve(1, x[1]), curve(2, x[2]), 1];
358
- const y = applyVideoLut(lut, x);
359
- for (let i = 0; i < 4; i++) {
360
- for (let j = 0; j < 4; j++) ata[i][j] += z[i] * z[j];
361
- for (let k = 0; k < 3; k++) atb[k][i] += z[i] * y[k];
362
- }
363
- }
364
- const matrix = atb.map((rhs) => solve4(ata, rhs));
365
- const result = { tables, matrix };
366
- approximations.set(lut, result);
367
- return result;
368
- }
369
- function solve4(a, b) {
370
- const m = a.map((row, i) => [...row, b[i]]);
371
- for (let col = 0; col < 4; col++) {
372
- let pivot = col;
373
- for (let r = col + 1; r < 4; r++) if (Math.abs(m[r][col]) > Math.abs(m[pivot][col])) pivot = r;
374
- if (Math.abs(m[pivot][col]) < 1e-12) continue;
375
- [m[col], m[pivot]] = [m[pivot], m[col]];
376
- for (let r = 0; r < 4; r++) {
377
- if (r === col) continue;
378
- const f = m[r][col] / m[col][col];
379
- for (let c = col; c < 5; c++) m[r][c] -= f * m[col][c];
380
- }
381
- }
382
- return m.map((row, i) => Math.abs(row[i]) < 1e-12 ? 0 : row[4] / row[i]);
383
- }
384
- var VideoLutCache = (_class = class {
385
- constructor(loader = async (src) => {
386
- const res = await fetch(src);
387
- if (!res.ok) throw new Error(`${res.status} ${res.statusText}`.trim());
388
- return res.text();
389
- }) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);
390
- this.loader = loader;
391
- }
392
-
393
- __init() {this.ready = /* @__PURE__ */ new Map()}
394
- __init2() {this.pending = /* @__PURE__ */ new Map()}
395
- __init3() {this.failed = /* @__PURE__ */ new Map()}
396
- __init4() {this.listeners = /* @__PURE__ */ new Set()}
397
- /** The LUT if loaded, without starting to load it. */
398
- peek(src) {
399
- return this.ready.get(src);
400
- }
401
- /** The LUT if loaded; otherwise `undefined`, and loading starts (once) unless it failed. */
402
- get(src) {
403
- const lut = this.ready.get(src);
404
- if (!lut && !this.failed.has(src)) void this.load(src).catch(() => {
405
- });
406
- return lut;
407
- }
408
- /** Why loading a LUT failed, if it did. */
409
- error(src) {
410
- return this.failed.get(src);
411
- }
412
- /** The LUT, loading and reading it if needed. Rejects with a readable error. */
413
- load(src) {
414
- const ready = this.ready.get(src);
415
- if (ready) return Promise.resolve(ready);
416
- const failed = this.failed.get(src);
417
- if (failed) return Promise.reject(new Error(failed));
418
- let pending = this.pending.get(src);
419
- if (!pending) {
420
- pending = Promise.resolve().then(() => this.loader(src)).then((v) => typeof v === "string" ? parseVideoCubeLut(v) : v).then(
421
- (lut) => {
422
- this.pending.delete(src);
423
- this.ready.set(src, lut);
424
- this.emit();
425
- return lut;
426
- },
427
- (e) => {
428
- this.pending.delete(src);
429
- const message = `The LUT "${src}" could not be read: ${e instanceof Error ? e.message : String(e)}`;
430
- this.failed.set(src, message);
431
- this.emit();
432
- throw new Error(message);
433
- }
434
- );
435
- this.pending.set(src, pending);
436
- }
437
- return pending;
438
- }
439
- /** Uses a LUT the host already has (`.cube` text or a parsed LUT), under `src`. */
440
- set(src, lut) {
441
- const parsed = typeof lut === "string" ? parseVideoCubeLut(lut) : lut;
442
- this.ready.set(src, parsed);
443
- this.failed.delete(src);
444
- this.emit();
445
- return parsed;
446
- }
447
- /** Forgets a failure so the next `get` tries again. */
448
- retry(src) {
449
- if (this.failed.delete(src)) this.emit();
450
- }
451
- subscribe(fn) {
452
- this.listeners.add(fn);
453
- return () => {
454
- this.listeners.delete(fn);
455
- };
456
- }
457
- emit() {
458
- for (const l of [...this.listeners]) l();
459
- }
460
- }, _class);
461
-
462
- // src/video-editor/grade.ts
463
- var VIDEO_GRADE_CONTROLS = [
464
- { key: "exposure", label: "Exposure", min: -3, max: 3, step: 0.1, description: "Exposure in stops: +1 doubles every channel, -1 halves it (default 0)." },
465
- { key: "contrast", label: "Contrast", min: -1, max: 1, step: 0.05, description: "Contrast around mid grey, -1 (flat grey) to 1 (twice as steep); default 0." },
466
- { key: "saturation", label: "Saturation", min: -1, max: 1, step: 0.05, description: "Saturation, -1 (black and white) to 1 (twice as saturated); default 0." },
467
- { key: "hue", label: "Hue", min: -180, max: 180, step: 1, description: "Hue rotation in degrees (default 0)." },
468
- { key: "temperature", label: "Temperature", min: -1, max: 1, step: 0.05, description: "White balance, -1 (cool, bluer) to 1 (warm, more orange); default 0." },
469
- { key: "tint", label: "Tint", min: -1, max: 1, step: 0.05, description: "White balance, -1 (greener) to 1 (more magenta); default 0." }
470
- ];
471
- var VIDEO_GRADE_PRESETS = {
472
- warm: { temperature: 0.4, saturation: 0.1 },
473
- cool: { temperature: -0.4, tint: -0.05 },
474
- punchy: { contrast: 0.25, saturation: 0.3 },
475
- faded: { exposure: 0.15, contrast: -0.3, saturation: -0.3 },
476
- "black-and-white": { saturation: -1, contrast: 0.15 },
477
- "bleach-bypass": { saturation: -0.6, contrast: 0.4 },
478
- "day-for-night": { exposure: -1.5, saturation: -0.4, temperature: -0.6 }
479
- };
480
- var VIDEO_GRADE_PRESET_NAMES = Object.keys(VIDEO_GRADE_PRESETS);
481
- var clampTo = (c, v) => Math.min(c.max, Math.max(c.min, v));
482
- function normalizeVideoGradeLut(lut) {
483
- if (!lut || typeof lut.src !== "string" || !lut.src) return void 0;
484
- const strength = typeof lut.strength === "number" && Number.isFinite(lut.strength) ? Math.round(Math.min(1, Math.max(0, lut.strength)) * 1e3) / 1e3 : 1;
485
- if (!strength) return void 0;
486
- return { src: lut.src, ...strength !== 1 ? { strength } : {}, ...lut.name ? { name: lut.name } : {} };
487
- }
488
- function normalizeVideoGrade(grade) {
489
- if (!grade) return void 0;
490
- const out = {};
491
- for (const c of VIDEO_GRADE_CONTROLS) {
492
- const v = grade[c.key];
493
- if (typeof v === "number" && Number.isFinite(v) && v !== 0) out[c.key] = Math.round(clampTo(c, v) * 1e3) / 1e3;
494
- }
495
- const lut = normalizeVideoGradeLut(grade.lut);
496
- if (lut) out.lut = lut;
497
- return Object.keys(out).length ? out : void 0;
498
- }
499
- function videoGradePreset(name, amount = 1) {
500
- const preset = VIDEO_GRADE_PRESETS[name];
501
- if (!preset) throw new Error(`There is no grade preset "${name}"; the presets are ${VIDEO_GRADE_PRESET_NAMES.join(", ")}.`);
502
- const k = Math.min(1, Math.max(0, amount));
503
- return normalizeVideoGrade(Object.fromEntries(Object.entries(preset).map(([key, v]) => [key, v * k])));
504
- }
505
- function videoGradeLook(current, name, amount = 1) {
506
- return normalizeVideoGrade({ ...videoGradePreset(name, amount), ..._optionalChain([current, 'optionalAccess', _2 => _2.lut]) ? { lut: current.lut } : {} });
507
- }
508
- function videoGradeSteps(grade) {
509
- const g = normalizeVideoGrade(grade);
510
- if (!g) return [];
511
- const steps = [];
512
- if (g.exposure) steps.push({ kind: "brightness", amount: 2 ** g.exposure });
513
- if (g.contrast) steps.push({ kind: "contrast", amount: 1 + g.contrast });
514
- if (g.saturation) steps.push({ kind: "saturate", amount: 1 + g.saturation });
515
- if (g.hue) steps.push({ kind: "hue-rotate", degrees: g.hue });
516
- if (g.temperature || g.tint) {
517
- const t = _nullishCoalesce(g.temperature, () => ( 0));
518
- steps.push({ kind: "gains", r: 1 + 0.25 * t, g: 1 - 0.25 * (_nullishCoalesce(g.tint, () => ( 0))), b: 1 - 0.25 * t });
519
- }
520
- return steps;
521
- }
522
- function videoGradeStepMatrix(step) {
523
- switch (step.kind) {
524
- case "brightness":
525
- return [
526
- [step.amount, 0, 0, 0],
527
- [0, step.amount, 0, 0],
528
- [0, 0, step.amount, 0]
529
- ];
530
- case "contrast": {
531
- const c = step.amount;
532
- const o = 0.5 - 0.5 * c;
533
- return [
534
- [c, 0, 0, o],
535
- [0, c, 0, o],
536
- [0, 0, c, o]
537
- ];
538
- }
539
- case "gains":
540
- return [
541
- [step.r, 0, 0, 0],
542
- [0, step.g, 0, 0],
543
- [0, 0, step.b, 0]
544
- ];
545
- case "saturate": {
546
- const s = step.amount;
547
- return [
548
- [0.213 + 0.787 * s, 0.715 - 0.715 * s, 0.072 - 0.072 * s, 0],
549
- [0.213 - 0.213 * s, 0.715 + 0.285 * s, 0.072 - 0.072 * s, 0],
550
- [0.213 - 0.213 * s, 0.715 - 0.715 * s, 0.072 + 0.928 * s, 0]
551
- ];
552
- }
553
- case "hue-rotate": {
554
- const a = step.degrees * Math.PI / 180;
555
- const cos = Math.cos(a);
556
- const sin = Math.sin(a);
557
- return [
558
- [0.213 + cos * 0.787 - sin * 0.213, 0.715 - cos * 0.715 - sin * 0.715, 0.072 - cos * 0.072 + sin * 0.928, 0],
559
- [0.213 - cos * 0.213 + sin * 0.143, 0.715 + cos * 0.285 + sin * 0.14, 0.072 - cos * 0.072 - sin * 0.283, 0],
560
- [0.213 - cos * 0.213 - sin * 0.787, 0.715 - cos * 0.715 + sin * 0.715, 0.072 + cos * 0.928 + sin * 0.072, 0]
561
- ];
562
- }
563
- }
564
- }
565
- function applyVideoGrade(grade, rgb, lut) {
566
- let v = rgb.map((x) => x / 255);
567
- for (const step of videoGradeSteps(grade)) {
568
- const m = videoGradeStepMatrix(step);
569
- v = m.map((row) => Math.min(1, Math.max(0, row[0] * v[0] + row[1] * v[1] + row[2] * v[2] + row[3])));
570
- }
571
- const setting = normalizeVideoGradeLut(_optionalChain([grade, 'optionalAccess', _3 => _3.lut]));
572
- if (setting && lut) {
573
- const out = applyVideoLut(lut, v);
574
- const s = _nullishCoalesce(setting.strength, () => ( 1));
575
- v = v.map((x, k) => x + (out[k] - x) * s);
576
- }
577
- return v.map((x) => Math.round(x * 255));
578
- }
579
- var fmt2 = (v) => String(Math.round(v * 1e5) / 1e5);
580
- function videoGradeStepSvgValues(step) {
581
- return videoGradeStepMatrix(step).map((row) => [row[0], row[1], row[2], 0, row[3]].map(fmt2).join(" ")).concat("0 0 0 1 0").join(" ");
582
- }
583
- function videoLutSvgPrimitives(lut, strength = 1) {
584
- const { tables, matrix } = videoLutSvgApproximation(lut);
585
- const table = (k) => tables[k].map(fmt2).join(" ");
586
- const values = matrix.map((row) => [row[0], row[1], row[2], 0, row[3]].map(fmt2).join(" ")).concat("0 0 0 1 0").join(" ");
587
- const s = Math.min(1, Math.max(0, strength));
588
- return `<feComponentTransfer result="lutIn"><feFuncA type="identity"/></feComponentTransfer><feComponentTransfer><feFuncR type="table" tableValues="${table(0)}"/><feFuncG type="table" tableValues="${table(1)}"/><feFuncB type="table" tableValues="${table(2)}"/></feComponentTransfer><feColorMatrix type="matrix" values="${values}"/>` + (s < 1 ? `<feComposite in2="lutIn" operator="arithmetic" k1="0" k2="${fmt2(s)}" k3="${fmt2(1 - s)}" k4="0"/>` : "");
589
- }
590
- function videoGradeSvgFilter(id, grade, lut) {
591
- const steps = videoGradeSteps(grade);
592
- const setting = normalizeVideoGradeLut(_optionalChain([grade, 'optionalAccess', _4 => _4.lut]));
593
- return `<filter id="${id}" color-interpolation-filters="sRGB" x="0" y="0" width="100%" height="100%">${steps.map((s) => `<feColorMatrix type="matrix" values="${videoGradeStepSvgValues(s)}"/>`).join("")}${setting && lut ? videoLutSvgPrimitives(lut, setting.strength) : ""}</filter>`;
594
- }
595
- var videoGradeGainsFilterId = (step) => `pixerate-wb-${[step.r, step.g, step.b].map((v) => Math.round(v * 1e3)).join("-")}`;
596
- function videoGradeCssFilter(grade) {
597
- return videoGradeSteps(grade).map((s) => s.kind === "brightness" ? `brightness(${fmt2(s.amount)})` : s.kind === "contrast" ? `contrast(${fmt2(s.amount)})` : s.kind === "saturate" ? `saturate(${fmt2(s.amount)})` : s.kind === "hue-rotate" ? `hue-rotate(${fmt2(s.degrees)}deg)` : `url(#${videoGradeGainsFilterId(s)})`).join(" ");
598
- }
599
- function videoGradeSvgDefs(grade) {
600
- return videoGradeSteps(grade).filter((s) => s.kind === "gains").map((s) => {
601
- const id = videoGradeGainsFilterId(s);
602
- return { id, markup: `<filter id="${id}" color-interpolation-filters="sRGB"><feColorMatrix type="matrix" values="${videoGradeStepSvgValues(s)}"/></filter>` };
603
- });
604
- }
605
- function videoGradeFfmpegFilters(grade) {
606
- return videoGradeSteps(grade).map((step) => {
607
- const m = videoGradeStepMatrix(step);
608
- if (step.kind === "brightness" || step.kind === "contrast") {
609
- const lut = (i) => `clip(val*${fmt2(m[i][i])}+${fmt2(m[i][3] * 255)},0,255)`;
610
- return `lutrgb=r='${lut(0)}':g='${lut(1)}':b='${lut(2)}'`;
611
- }
612
- const k = ["r", "g", "b"];
613
- return `colorchannelmixer=${m.flatMap((row, i) => row.slice(0, 3).map((v, j) => `${k[i]}${k[j]}=${fmt2(v)}`)).join(":")}`;
614
- });
615
- }
616
-
617
- // src/video-editor/remap.ts
618
- function videoClipSourceTime(clip, clipTime) {
619
- const base = _nullishCoalesce(clip.in, () => ( 0));
620
- return clip.timeRemap ? base + videoValueAt(clip.timeRemap, clipTime, 0) : base + clipTime * (_nullishCoalesce(clip.speed, () => ( 1)));
621
- }
622
- function videoClipSpeedAt(clip, clipTime) {
623
- if (clip.type !== "media") return 1;
624
- if (!clip.timeRemap) return _nullishCoalesce(clip.speed, () => ( 1));
625
- const t = Math.min(Math.max(clipTime, 0), Math.max(0, clip.duration - 1));
626
- return Math.max(0, videoClipSourceTime(clip, t + 1) - videoClipSourceTime(clip, t));
627
- }
628
- function videoSpeedRampKeys(segments) {
629
- if (!segments.length) throw new Error("Give at least one stretch { duration, speed }.");
630
- const keys = [];
631
- let t = 0;
632
- let v = 0;
633
- segments.forEach((s, i) => {
634
- if (!(s.duration > 0)) throw new Error(`Stretch ${i + 1} needs a duration above 0 ms.`);
635
- for (const [name, x] of [["speed", s.speed], ["toSpeed", s.toSpeed]])
636
- if (x !== void 0 && !(x >= 0 && x <= 20)) throw new Error(`Stretch ${i + 1}'s ${name} must be from 0 (a freeze) to 20; got ${x}.`);
637
- const a = s.speed;
638
- const b = _nullishCoalesce(s.toSpeed, () => ( s.speed));
639
- const avg = (a + b) / 2;
640
- let ease = "linear";
641
- if (a !== b && avg > 0) {
642
- ease = [1 / 3, 2 * a / (3 * (a + b)), 2 / 3, 1 - 2 * b / (3 * (a + b))].map((n2) => Math.round(n2 * 1e6) / 1e6);
643
- }
644
- keys.push({ t, v, ...ease === "linear" ? {} : { ease } });
645
- t += s.duration;
646
- v += avg * s.duration;
647
- });
648
- keys.push({ t, v });
649
- return { keys: mergeLinear(keys), duration: t, source: v };
650
- }
651
- function mergeLinear(keys) {
652
- const out = [keys[0]];
653
- for (let i = 1; i < keys.length; i++) {
654
- const prev = out[out.length - 1];
655
- const k = keys[i];
656
- const before = out.length > 1 ? out[out.length - 2] : null;
657
- if (before && !before.ease && !prev.ease) {
658
- const s1 = (prev.v - before.v) / (prev.t - before.t);
659
- const s2 = (k.v - prev.v) / (k.t - prev.t);
660
- if (Math.abs(s1 - s2) < 1e-9) {
661
- out[out.length - 1] = k;
662
- continue;
663
- }
664
- }
665
- out.push(k);
666
- }
667
- return out;
668
- }
669
- function checkVideoTimeRemap(remap, path = "timeRemap") {
670
- const { keys } = remap;
671
- if (!keys.length) throw new Error(`${path} needs at least one key.`);
672
- keys.forEach((k, i) => {
673
- if (k.v < 0) throw new Error(`${path}.keys[${i}].v must be at least 0 (ms after the in point).`);
674
- const next = keys[i + 1];
675
- if (!next) return;
676
- if (next.v < k.v - 1e-6) throw new Error(`${path}.keys[${i + 1}] goes back in the source (${next.v} ms after ${k.v} ms); a ramp only plays forwards, so make each value at least the one before.`);
677
- if (!videoEaseBezier(k.ease)) return;
678
- let prev = k.v;
679
- for (let n2 = 1; n2 <= 32; n2++) {
680
- const v = videoValueAt(remap, k.t + (next.t - k.t) * n2 / 32);
681
- if (v < prev - 1e-3) throw new Error(`${path}.keys[${i}].ease overshoots, which would play backwards; use a curve that only moves forwards (or linear).`);
682
- prev = v;
683
- }
684
- });
685
- }
686
- function videoTimeRemapSpan(remap) {
687
- return { from: remap.keys[0].v, to: remap.keys[remap.keys.length - 1].v };
688
- }
689
- function videoTimeRemapBreakpoints(remap, step) {
690
- const { points, holds } = videoKeyframeBreakpoints(remap, step);
691
- const out = [];
692
- points.forEach(([t, v], i) => {
693
- out.push({ t, v });
694
- if (holds.has(i) && points[i + 1]) out.push({ t: points[i + 1][0], v });
695
- });
696
- return out;
697
- }
698
- function videoSpeedRampEdit(clip, segments, ripple = true) {
699
- const ramp = videoSpeedRampKeys(segments);
700
- const offset = videoClipSourceTime(clip, 0) - (_nullishCoalesce(clip.in, () => ( 0)));
701
- return { op: "set_time_remap", clip: clip.id, keys: ramp.keys.map((k) => ({ ...k, v: k.v + offset })), duration: ramp.duration, ripple };
702
- }
703
- function videoFreezeFrameEdit(clip, at, duration, ripple = true) {
704
- if (at < 0 || at > clip.duration) throw new Error(`${Math.round(at)} ms is outside clip "${clip.id}" (0\u2013${clip.duration} ms of clip time).`);
705
- const remap = _nullishCoalesce(clip.timeRemap, () => ( { keys: [{ t: 0, v: 0 }, { t: clip.duration, v: clip.duration * (_nullishCoalesce(clip.speed, () => ( 1))) }] }));
706
- const [left, right] = splitVideoAnimatable(remap, at);
707
- const keys = [...left.keys.slice(0, -1), { ...left.keys[left.keys.length - 1], ease: "linear" }, ...right.keys.map((k) => ({ ...k, t: k.t + at + duration }))];
708
- return { op: "set_time_remap", clip: clip.id, keys, duration: clip.duration + duration, ripple };
709
- }
710
- var videoSlowMotionRamp = (duration) => [
711
- { duration: Math.max(1, duration / 2), speed: 1 },
712
- { duration: 500, speed: 1, toSpeed: 0.3 },
713
- { duration: 1500, speed: 0.3 }
714
- ];
715
-
716
136
  // src/video-editor/parse.ts
717
137
  var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
718
138
  var fail = (path, what) => {
@@ -745,11 +165,11 @@ var compact = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !=
745
165
  function parseVideoEase(v, path) {
746
166
  if (v === void 0) return void 0;
747
167
  if (Array.isArray(v)) {
748
- if (v.length !== 4 || !v.every((n2) => typeof n2 === "number" && Number.isFinite(n2))) fail(path, "must be four numbers [x1, y1, x2, y2] (CSS cubic-bezier)");
168
+ if (v.length !== 4 || !v.every((n) => typeof n === "number" && Number.isFinite(n))) fail(path, "must be four numbers [x1, y1, x2, y2] (CSS cubic-bezier)");
749
169
  if (v[0] < 0 || v[0] > 1 || v[2] < 0 || v[2] > 1) fail(path, "must have x1 and x2 between 0 and 1");
750
170
  return [v[0], v[1], v[2], v[3]];
751
171
  }
752
- return oneOf(v, VIDEO_EASE_NAMES, path);
172
+ return oneOf(v, _chunkSMJGE7HMcjs.VIDEO_EASE_NAMES, path);
753
173
  }
754
174
  function parseVideoKeyframes(v, path, { min, max } = {}) {
755
175
  if (!isObject(v)) return fail(path, "must be a number or keyframes { keys: [{ t, v, ease }] }");
@@ -787,7 +207,7 @@ function parseVideoTimeRemap(v, speed, path) {
787
207
  if (typeof remap === "number" || remap === void 0) return fail(path, "must be keyframes { keys: [{ t, v, ease }] } (v: ms of source after the in point)");
788
208
  if (speed !== void 0 && speed !== 1) fail(path, 'cannot be used with a speed; a ramp sets the speed itself (remove "speed")');
789
209
  try {
790
- checkVideoTimeRemap(remap, path);
210
+ _chunkSMJGE7HMcjs.checkVideoTimeRemap.call(void 0, remap, path);
791
211
  } catch (e) {
792
212
  throw new Error(e.message.replace(/\.?$/, "."));
793
213
  }
@@ -797,17 +217,17 @@ function parseVideoClipAnimation(v, path) {
797
217
  if (v === void 0) return void 0;
798
218
  if (!isObject(v)) return fail(path, "must be an object { effect, duration }");
799
219
  return compact({
800
- effect: oneOf(v.effect, VIDEO_CLIP_ANIMATION_EFFECTS, `${path}.effect`, false),
220
+ effect: oneOf(v.effect, _chunkSMJGE7HMcjs.VIDEO_CLIP_ANIMATION_EFFECTS, `${path}.effect`, false),
801
221
  duration: num(v.duration, `${path}.duration`, { optional: true, positive: true })
802
222
  });
803
223
  }
804
224
  function parseVideoGrade(v, path) {
805
225
  if (v === void 0) return void 0;
806
226
  if (!isObject(v)) return fail(path, "must be an object such as { exposure, contrast, saturation }");
807
- const known = /* @__PURE__ */ new Set([...VIDEO_GRADE_CONTROLS.map((c) => c.key), "lut"]);
227
+ const known = /* @__PURE__ */ new Set([..._chunkSMJGE7HMcjs.VIDEO_GRADE_CONTROLS.map((c) => c.key), "lut"]);
808
228
  const unknown = Object.keys(v).find((k) => !known.has(k));
809
229
  if (unknown) fail(`${path}.${unknown}`, `is not a grade setting; use ${[...known].join(", ")}`);
810
- return compact({ ...Object.fromEntries(VIDEO_GRADE_CONTROLS.map((c) => [c.key, num(v[c.key], `${path}.${c.key}`, { optional: true, min: c.min, max: c.max })])), lut: parseVideoGradeLut(v.lut, `${path}.lut`) });
230
+ return compact({ ...Object.fromEntries(_chunkSMJGE7HMcjs.VIDEO_GRADE_CONTROLS.map((c) => [c.key, num(v[c.key], `${path}.${c.key}`, { optional: true, min: c.min, max: c.max })])), lut: parseVideoGradeLut(v.lut, `${path}.lut`) });
811
231
  }
812
232
  function parseVideoGradeLut(v, path) {
813
233
  if (v === void 0) return void 0;
@@ -820,11 +240,11 @@ function parseVideoTransition(v, path) {
820
240
  if (v === void 0) return void 0;
821
241
  if (!isObject(v)) return fail(path, "must be an object { type, duration }");
822
242
  return compact({
823
- type: oneOf(v.type, VIDEO_TRANSITION_TYPES, `${path}.type`, false),
243
+ type: oneOf(v.type, _chunkSMJGE7HMcjs.VIDEO_TRANSITION_TYPES, `${path}.type`, false),
824
244
  duration: num(v.duration, `${path}.duration`, { positive: true }),
825
245
  color: str(v.color, `${path}.color`, true),
826
- direction: oneOf(v.direction, VIDEO_WIPE_DIRECTIONS, `${path}.direction`),
827
- audioCurve: oneOf(v.audioCurve, VIDEO_FADE_CURVES, `${path}.audioCurve`)
246
+ direction: oneOf(v.direction, _chunkSMJGE7HMcjs.VIDEO_WIPE_DIRECTIONS, `${path}.direction`),
247
+ audioCurve: oneOf(v.audioCurve, _chunkSMJGE7HMcjs.VIDEO_FADE_CURVES, `${path}.audioCurve`)
828
248
  });
829
249
  }
830
250
  function parseVideoTranscriptWords(v, path) {
@@ -881,7 +301,7 @@ function parseVideoAssetProxies(v, path) {
881
301
  }
882
302
  function parseVideoClip(v, path = "clip") {
883
303
  if (!isObject(v)) return fail(path, "must be an object");
884
- const type = oneOf(_nullishCoalesce(v.type, () => ( "media")), VIDEO_CLIP_TYPES, `${path}.type`, false);
304
+ const type = oneOf(_nullishCoalesce(v.type, () => ( "media")), _chunkSMJGE7HMcjs.VIDEO_CLIP_TYPES, `${path}.type`, false);
885
305
  const base = {
886
306
  id: str(v.id, `${path}.id`),
887
307
  name: str(v.name, `${path}.name`, true),
@@ -889,8 +309,8 @@ function parseVideoClip(v, path = "clip") {
889
309
  duration: num(v.duration, `${path}.duration`, { positive: true }),
890
310
  opacity: parseVideoAnimatable(v.opacity, `${path}.opacity`, { optional: true, min: 0, max: 1 }),
891
311
  transform: parseVideoTransform(v.transform, `${path}.transform`),
892
- fit: oneOf(v.fit, VIDEO_FITS, `${path}.fit`),
893
- blend: oneOf(v.blend, VIDEO_BLEND_MODES, `${path}.blend`),
312
+ fit: oneOf(v.fit, _chunkSMJGE7HMcjs.VIDEO_FITS, `${path}.fit`),
313
+ blend: oneOf(v.blend, _chunkSMJGE7HMcjs.VIDEO_BLEND_MODES, `${path}.blend`),
894
314
  grade: parseVideoGrade(v.grade, `${path}.grade`),
895
315
  animateIn: parseVideoClipAnimation(v.animateIn, `${path}.animateIn`),
896
316
  animateOut: parseVideoClipAnimation(v.animateOut, `${path}.animateOut`),
@@ -908,8 +328,8 @@ function parseVideoClip(v, path = "clip") {
908
328
  volume: parseVideoAnimatable(v.volume, `${path}.volume`, { optional: true, min: 0, max: 4 }),
909
329
  muted: bool(v.muted, `${path}.muted`),
910
330
  timeRemap: parseVideoTimeRemap(v.timeRemap, v.speed, `${path}.timeRemap`),
911
- fadeInCurve: oneOf(v.fadeInCurve, VIDEO_FADE_CURVES, `${path}.fadeInCurve`),
912
- fadeOutCurve: oneOf(v.fadeOutCurve, VIDEO_FADE_CURVES, `${path}.fadeOutCurve`)
331
+ fadeInCurve: oneOf(v.fadeInCurve, _chunkSMJGE7HMcjs.VIDEO_FADE_CURVES, `${path}.fadeInCurve`),
332
+ fadeOutCurve: oneOf(v.fadeOutCurve, _chunkSMJGE7HMcjs.VIDEO_FADE_CURVES, `${path}.fadeOutCurve`)
913
333
  });
914
334
  if (type === "sequence") {
915
335
  for (const key of ["speed", "timeRemap"])
@@ -921,8 +341,8 @@ function parseVideoClip(v, path = "clip") {
921
341
  in: num(v.in, `${path}.in`, { optional: true, min: 0 }),
922
342
  volume: parseVideoAnimatable(v.volume, `${path}.volume`, { optional: true, min: 0, max: 4 }),
923
343
  muted: bool(v.muted, `${path}.muted`),
924
- fadeInCurve: oneOf(v.fadeInCurve, VIDEO_FADE_CURVES, `${path}.fadeInCurve`),
925
- fadeOutCurve: oneOf(v.fadeOutCurve, VIDEO_FADE_CURVES, `${path}.fadeOutCurve`)
344
+ fadeInCurve: oneOf(v.fadeInCurve, _chunkSMJGE7HMcjs.VIDEO_FADE_CURVES, `${path}.fadeInCurve`),
345
+ fadeOutCurve: oneOf(v.fadeOutCurve, _chunkSMJGE7HMcjs.VIDEO_FADE_CURVES, `${path}.fadeOutCurve`)
926
346
  });
927
347
  }
928
348
  if (type === "text")
@@ -1026,264 +446,12 @@ function createVideoProject(options = {}) {
1026
446
  });
1027
447
  }
1028
448
 
1029
- // src/video-editor/timeline.ts
1030
- var VIDEO_TIME_EPSILON = 0.5;
1031
- var videoClipEnd = (clip) => clip.start + clip.duration;
1032
- var videoTrackSyncLocked = (track) => track.syncLocked !== false;
1033
- var videoClipSpeed = (clip) => clip.type === "media" ? _nullishCoalesce(clip.speed, () => ( 1)) : 1;
1034
- function videoClipSourceRange(clip) {
1035
- if (clip.type === "sequence") return { in: _nullishCoalesce(clip.in, () => ( 0)), out: (_nullishCoalesce(clip.in, () => ( 0))) + clip.duration };
1036
- if (clip.type !== "media") return null;
1037
- const start = _nullishCoalesce(clip.in, () => ( 0));
1038
- if (_optionalChain([clip, 'access', _5 => _5.timeRemap, 'optionalAccess', _6 => _6.keys, 'access', _7 => _7.length])) return { in: start + clip.timeRemap.keys[0].v, out: start + clip.timeRemap.keys[clip.timeRemap.keys.length - 1].v };
1039
- return { in: start, out: start + clip.duration * videoClipSpeed(clip) };
1040
- }
1041
- var videoFrameDuration = (fps) => 1e3 / fps;
1042
- function snapToVideoFrame(time, fps) {
1043
- const frame = Math.round(time * fps / 1e3);
1044
- return Math.round(frame * 1e3 / fps * 1e3) / 1e3;
1045
- }
1046
- var videoFrames = (time, fps) => Math.round(time * fps / 1e3);
1047
- function isVideoDropFrameRate(fps) {
1048
- const nominal = Math.round(fps);
1049
- return (nominal === 30 || nominal === 60) && Math.abs(fps - nominal * 1e3 / 1001) < 5e-3;
1050
- }
1051
- var exactVideoRate = (fps) => {
1052
- const nominal = Math.round(fps);
1053
- return nominal !== fps && Math.abs(fps - nominal * 1e3 / 1001) < 5e-3 ? nominal * 1e3 / 1001 : fps;
1054
- };
1055
- var videoTimecodeFrames = (time, fps) => Math.max(0, Math.round(time * exactVideoRate(fps) / 1e3));
1056
- function formatVideoTimecode(time, fps, dropFrame = isVideoDropFrameRate(fps)) {
1057
- return formatVideoTimecodeFrames(videoTimecodeFrames(time, fps), fps, dropFrame);
1058
- }
1059
- function formatVideoTimecodeFrames(frames, fps, dropFrame = isVideoDropFrameRate(fps)) {
1060
- const base = Math.round(fps);
1061
- let total = Math.max(0, Math.round(frames));
1062
- const drop = dropFrame && isVideoDropFrameRate(fps);
1063
- if (drop) {
1064
- const skip = base / 15;
1065
- const perMinute = base * 60 - skip;
1066
- const perTen = perMinute * 10 + skip;
1067
- const tens = Math.floor(total / perTen);
1068
- const rest = total % perTen;
1069
- total += skip * 9 * tens + (rest > skip ? skip * Math.floor((rest - skip) / perMinute) : 0);
1070
- }
1071
- const ff = total % base;
1072
- const secs = Math.floor(total / base);
1073
- const pad = (n2) => String(n2).padStart(2, "0");
1074
- return `${pad(Math.floor(secs / 3600))}:${pad(Math.floor(secs / 60) % 60)}:${pad(secs % 60)}${drop ? ";" : ":"}${pad(ff)}`;
1075
- }
1076
- var videoHourFrames = (fps, dropFrame = isVideoDropFrameRate(fps)) => {
1077
- const base = Math.round(fps);
1078
- return dropFrame && isVideoDropFrameRate(fps) ? base * 3600 - base / 15 * 54 : base * 3600;
1079
- };
1080
- var formatVideoSeconds = (time) => `${(time / 1e3).toFixed(2)} s`;
1081
- var videoAssetHasSound = (asset) => !!asset && (asset.kind === "audio" || asset.kind === "video" && asset.hasAudio !== false);
1082
- function videoAssetMap(project) {
1083
- return new Map(project.assets.map((a) => [a.id, a]));
1084
- }
1085
- function videoSequenceMap(project) {
1086
- return new Map((_nullishCoalesce(project.sequences, () => ( []))).map((q) => [q.id, q]));
1087
- }
1088
- function videoSequenceDuration(sequence) {
1089
- let end = 0;
1090
- for (const track of sequence.tracks) for (const clip of track.clips) end = Math.max(end, videoClipEnd(clip));
1091
- return end;
1092
- }
1093
- var videoSequenceChildren = (sequence) => [...new Set(sequence.tracks.flatMap((t) => t.clips.flatMap((c) => c.type === "sequence" ? [c.sequence] : [])))];
1094
- function findVideoSequenceCycle(project, id) {
1095
- const sequences = videoSequenceMap(project);
1096
- const walk = (at, path) => {
1097
- const seq = sequences.get(at);
1098
- if (!seq) return null;
1099
- for (const child of videoSequenceChildren(seq)) {
1100
- if (child === id) return [...path, child];
1101
- if (path.includes(child)) continue;
1102
- const found = walk(child, [...path, child]);
1103
- if (found) return found;
1104
- }
1105
- return null;
1106
- };
1107
- return walk(id, [id]);
1108
- }
1109
- function allVideoTracks(project) {
1110
- return [...project.tracks.map((track) => ({ track })), ...(_nullishCoalesce(project.sequences, () => ( []))).flatMap((q) => q.tracks.map((track) => ({ sequence: q.id, track })))];
1111
- }
1112
- function sortedVideoClips(track) {
1113
- return [...track.clips].sort((a, b) => a.start - b.start || a.duration - b.duration);
1114
- }
1115
- function findVideoClip(project, id) {
1116
- for (let trackIndex = 0; trackIndex < project.tracks.length; trackIndex++) {
1117
- const track = project.tracks[trackIndex];
1118
- const index = track.clips.findIndex((c) => c.id === id);
1119
- if (index >= 0) return { track, trackIndex, clip: track.clips[index], index };
1120
- }
1121
- return null;
1122
- }
1123
- function allVideoClipIds(project) {
1124
- return allVideoTracks(project).flatMap(({ track }) => track.clips.map((c) => c.id));
1125
- }
1126
- function findVideoSequenceClip(project, id) {
1127
- for (const sequence of _nullishCoalesce(project.sequences, () => ( [])))
1128
- for (const track of sequence.tracks) {
1129
- const clip = track.clips.find((c) => c.id === id);
1130
- if (clip) return { sequence, track, clip };
1131
- }
1132
- return null;
1133
- }
1134
- function videoProjectDuration(project) {
1135
- let end = 0;
1136
- for (const track of project.tracks) for (const clip of track.clips) end = Math.max(end, videoClipEnd(clip));
1137
- return end;
1138
- }
1139
- function videoClipBefore(track, clip) {
1140
- let best = null;
1141
- for (const c of track.clips) if (c !== clip && Math.abs(videoClipEnd(c) - clip.start) <= VIDEO_TIME_EPSILON) best = c;
1142
- return best;
1143
- }
1144
- function videoClipAfter(track, clip) {
1145
- for (const c of track.clips) if (c !== clip && Math.abs(c.start - videoClipEnd(clip)) <= VIDEO_TIME_EPSILON) return c;
1146
- return null;
1147
- }
1148
- function videoClipHandles(clip, assets, sequences) {
1149
- if (clip.type === "sequence") {
1150
- const seq = _optionalChain([sequences, 'optionalAccess', _8 => _8.get, 'call', _9 => _9(clip.sequence)]);
1151
- const range2 = videoClipSourceRange(clip);
1152
- return { before: range2.in, after: seq ? Math.max(0, videoSequenceDuration(seq) - range2.out) : Infinity };
1153
- }
1154
- if (clip.type !== "media") return { before: Infinity, after: Infinity };
1155
- const asset = assets.get(clip.asset);
1156
- const speed = videoClipSpeed(clip);
1157
- const range = videoClipSourceRange(clip);
1158
- const before = range.in / speed;
1159
- if (!asset || asset.kind === "image" || asset.duration === void 0) return { before: _optionalChain([asset, 'optionalAccess', _10 => _10.kind]) === "image" ? Infinity : before, after: Infinity };
1160
- return { before, after: Math.max(0, (asset.duration - range.out) / speed) };
1161
- }
1162
- function videoClipOverhang(track, clip) {
1163
- const lead = clip.transition && clip.transition.type !== "dip" && videoClipBefore(track, clip) ? clip.transition.duration / 2 : 0;
1164
- const next = videoClipAfter(track, clip);
1165
- const tail = _optionalChain([next, 'optionalAccess', _11 => _11.transition]) && next.transition.type !== "dip" ? next.transition.duration / 2 : 0;
1166
- return { lead, tail };
1167
- }
1168
- function videoSnapPoints(project, options = {}) {
1169
- const exclude = new Set(_nullishCoalesce(options.exclude, () => ( [])));
1170
- const points = /* @__PURE__ */ new Set([0]);
1171
- for (const track of project.tracks)
1172
- for (const clip of track.clips) {
1173
- if (exclude.has(clip.id)) continue;
1174
- points.add(clip.start);
1175
- points.add(videoClipEnd(clip));
1176
- }
1177
- for (const marker of _nullishCoalesce(project.markers, () => ( []))) points.add(marker.time);
1178
- if (options.playhead !== void 0) points.add(options.playhead);
1179
- return [...points].sort((a, b) => a - b);
1180
- }
1181
- function snapVideoTime(time, points, threshold) {
1182
- let best = null;
1183
- for (const p of points) if (Math.abs(p - time) <= threshold && (best === null || Math.abs(p - time) < Math.abs(best - time))) best = p;
1184
- return best === null ? { time, snapped: null } : { time: best, snapped: best };
1185
- }
1186
- function findVideoGaps(project, trackId) {
1187
- const gaps = [];
1188
- for (const track of project.tracks) {
1189
- if (trackId !== void 0 && track.id !== trackId) continue;
1190
- let at = 0;
1191
- for (const clip of sortedVideoClips(track)) {
1192
- if (clip.start - at > VIDEO_TIME_EPSILON) gaps.push({ track: track.id, start: at, end: clip.start });
1193
- at = Math.max(at, videoClipEnd(clip));
1194
- }
1195
- }
1196
- return gaps;
1197
- }
1198
- function videoOverlap(track, start, end, ignore = /* @__PURE__ */ new Set()) {
1199
- for (const c of sortedVideoClips(track)) {
1200
- if (ignore.has(c.id)) continue;
1201
- if (c.start < end - VIDEO_TIME_EPSILON && videoClipEnd(c) > start + VIDEO_TIME_EPSILON) return c;
1202
- }
1203
- return null;
1204
- }
1205
- function nextFreeVideoSlot(track, from, duration, ignore = /* @__PURE__ */ new Set()) {
1206
- let at = from;
1207
- for (const c of sortedVideoClips(track)) {
1208
- if (ignore.has(c.id)) continue;
1209
- if (videoClipEnd(c) <= at + VIDEO_TIME_EPSILON) continue;
1210
- if (c.start >= at + duration - VIDEO_TIME_EPSILON) break;
1211
- at = videoClipEnd(c);
1212
- }
1213
- return at;
1214
- }
1215
- function videoClipsAt(project, time) {
1216
- const out = [];
1217
- for (const track of project.tracks) for (const clip of track.clips) if (time >= clip.start && time < videoClipEnd(clip)) out.push({ track: track.id, clip });
1218
- return out;
1219
- }
1220
-
1221
- // src/video-editor/clip-animation.ts
1222
- var VIDEO_CLIP_ANIMATION_DURATION = 500;
1223
- var EFFECTS = {
1224
- fade: { easeIn: "linear", easeOut: "linear" },
1225
- rise: { y: 0.06, easeIn: "ease-out", easeOut: "ease-in" },
1226
- drop: { y: -0.06, easeIn: "ease-out", easeOut: "ease-in" },
1227
- "slide-left": { x: 0.15, easeIn: "ease-out", easeOut: "ease-in" },
1228
- "slide-right": { x: -0.15, easeIn: "ease-out", easeOut: "ease-in" },
1229
- zoom: { scale: 0.6, easeIn: "ease-out", easeOut: "ease-in" },
1230
- // Overshoots past full size, then settles (a "back" curve).
1231
- pop: { scale: 0.3, easeIn: [0.34, 1.56, 0.64, 1], easeOut: [0.36, 0, 0.66, -0.56] }
1232
- };
1233
- var PROPS = ["opacity", "x", "y", "scale"];
1234
- function videoClipAnimationProperties(animation) {
1235
- const spec = EFFECTS[animation.effect];
1236
- return PROPS.filter((p) => p === "opacity" || spec[p] !== void 0);
1237
- }
1238
- function videoClipAnimationSpans(clip) {
1239
- const a = clip.animateIn ? _nullishCoalesce(clip.animateIn.duration, () => ( VIDEO_CLIP_ANIMATION_DURATION)) : 0;
1240
- const b = clip.animateOut ? _nullishCoalesce(clip.animateOut.duration, () => ( VIDEO_CLIP_ANIMATION_DURATION)) : 0;
1241
- const k = a + b > clip.duration ? clip.duration / (a + b) : 1;
1242
- return { in: a * k, out: b * k };
1243
- }
1244
- function videoClipAnimated(clip, project) {
1245
- if (!clip.animateIn && !clip.animateOut) return clip;
1246
- const spans = videoClipAnimationSpans(clip);
1247
- const out = { ...clip, transform: { ...clip.transform } };
1248
- for (const prop of PROPS) {
1249
- const current = prop === "opacity" ? clip.opacity : _optionalChain([clip, 'access', _12 => _12.transform, 'optionalAccess', _13 => _13[prop]]);
1250
- if (isVideoKeyframes(current)) continue;
1251
- const base = _nullishCoalesce(current, () => ( (prop === "opacity" || prop === "scale" ? 1 : 0)));
1252
- const away = (spec) => {
1253
- if (prop === "opacity") return 0;
1254
- const v = spec[prop];
1255
- if (v === void 0) return void 0;
1256
- return prop === "scale" ? base * v : base + v * (prop === "x" ? project.width : project.height);
1257
- };
1258
- const keys = [];
1259
- if (clip.animateIn) {
1260
- const spec = EFFECTS[clip.animateIn.effect];
1261
- const from = away(spec);
1262
- if (from !== void 0) keys.push({ t: 0, v: from, ease: spec.easeIn }, { t: spans.in, v: base });
1263
- }
1264
- if (clip.animateOut) {
1265
- const spec = EFFECTS[clip.animateOut.effect];
1266
- const to = away(spec);
1267
- if (to !== void 0) {
1268
- const start = clip.duration - spans.out;
1269
- if (!keys.length || keys[keys.length - 1].t < start) keys.push({ t: start, v: base, ease: spec.easeOut });
1270
- else keys[keys.length - 1] = { ...keys[keys.length - 1], ease: spec.easeOut };
1271
- keys.push({ t: clip.duration, v: to });
1272
- }
1273
- }
1274
- if (!keys.length) continue;
1275
- if (prop === "opacity") out.opacity = { keys };
1276
- else out.transform[prop] = { keys };
1277
- }
1278
- return out;
1279
- }
1280
-
1281
449
  // src/video-editor/check.ts
1282
450
  function videoClipKeyframes(clip) {
1283
451
  const out = [
1284
- { property: "opacity", keys: videoKeysOf(clip.opacity) },
1285
- { property: "volume", keys: clip.type === "media" || clip.type === "sequence" ? videoKeysOf(clip.volume) : [] },
1286
- ...VIDEO_TRANSFORM_PROPERTIES.map((p) => ({ property: p, keys: videoKeysOf(_optionalChain([clip, 'access', _14 => _14.transform, 'optionalAccess', _15 => _15[p]])) }))
452
+ { property: "opacity", keys: _chunkSMJGE7HMcjs.videoKeysOf.call(void 0, clip.opacity) },
453
+ { property: "volume", keys: clip.type === "media" || clip.type === "sequence" ? _chunkSMJGE7HMcjs.videoKeysOf.call(void 0, clip.volume) : [] },
454
+ ..._chunkSMJGE7HMcjs.VIDEO_TRANSFORM_PROPERTIES.map((p) => ({ property: p, keys: _chunkSMJGE7HMcjs.videoKeysOf.call(void 0, _optionalChain([clip, 'access', _2 => _2.transform, 'optionalAccess', _3 => _3[p]])) }))
1287
455
  ];
1288
456
  return out.filter((o) => o.keys.length);
1289
457
  }
@@ -1310,8 +478,8 @@ var VIDEO_ISSUE_CODES = [
1310
478
  var videoIssueKey = (i) => [i.code, _nullishCoalesce(i.sequence, () => ( "")), _nullishCoalesce(i.track, () => ( "")), _nullishCoalesce(i.clip, () => ( "")), _nullishCoalesce(i.asset, () => ( "")), _nullishCoalesce(i.marker, () => ( ""))].join("|");
1311
479
  function checkVideoProject(project) {
1312
480
  const issues = [];
1313
- const assets = videoAssetMap(project);
1314
- const sequences = videoSequenceMap(project);
481
+ const assets = _chunkSMJGE7HMcjs.videoAssetMap.call(void 0, project);
482
+ const sequences = _chunkSMJGE7HMcjs.videoSequenceMap.call(void 0, project);
1315
483
  const used = /* @__PURE__ */ new Set();
1316
484
  const usedSequences = /* @__PURE__ */ new Set();
1317
485
  const cycles = /* @__PURE__ */ new Map();
@@ -1319,7 +487,7 @@ function checkVideoProject(project) {
1319
487
  if ((asset.kind === "video" || asset.kind === "audio") && asset.duration === void 0)
1320
488
  issues.push({ code: "unknown-duration", severity: "warning", asset: asset.id, message: `Asset "${asset.id}" has no duration, so clips of it cannot be checked against its length; set "duration" in ms.` });
1321
489
  const checkTrack = (track, sequence) => {
1322
- const clips = sortedVideoClips(track);
490
+ const clips = _chunkSMJGE7HMcjs.sortedVideoClips.call(void 0, track);
1323
491
  const where = sequence ? ` in sequence "${sequence}"` : "";
1324
492
  for (let i = 0; i < clips.length; i++) {
1325
493
  const clip = clips[i];
@@ -1329,13 +497,13 @@ function checkVideoProject(project) {
1329
497
  const seq = sequences.get(clip.sequence);
1330
498
  if (!seq) issues.push({ code: "missing-sequence", severity: "error", ...at, message: `Compound clip "${clip.id}" plays sequence "${clip.sequence}", which is not in the project; add the sequence or delete the clip.` });
1331
499
  else {
1332
- if (!cycles.has(seq.id)) cycles.set(seq.id, findVideoSequenceCycle(project, seq.id));
500
+ if (!cycles.has(seq.id)) cycles.set(seq.id, _chunkSMJGE7HMcjs.findVideoSequenceCycle.call(void 0, project, seq.id));
1333
501
  const cycle = cycles.get(seq.id);
1334
502
  if (cycle) issues.push({ code: "sequence-cycle", severity: "error", ...at, message: `Compound clip "${clip.id}" plays sequence "${seq.id}", which contains itself (${cycle.join(" \u2192 ")}), so it is left out of the picture and sound; remove the compound clip that closes the loop.` });
1335
- const length = videoSequenceDuration(seq);
1336
- const range = videoClipSourceRange(clip);
1337
- if (range.out > length + VIDEO_TIME_EPSILON)
1338
- issues.push({ code: "source-overrun", severity: "error", ...at, message: `Compound clip "${clip.id}" plays to ${formatVideoSeconds(range.out)} of sequence "${seq.id}", which is only ${formatVideoSeconds(length)} long; shorten the clip or move its in point.` });
503
+ const length = _chunkSMJGE7HMcjs.videoSequenceDuration.call(void 0, seq);
504
+ const range = _chunkSMJGE7HMcjs.videoClipSourceRange.call(void 0, clip);
505
+ if (range.out > length + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON)
506
+ issues.push({ code: "source-overrun", severity: "error", ...at, message: `Compound clip "${clip.id}" plays to ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, range.out)} of sequence "${seq.id}", which is only ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, length)} long; shorten the clip or move its in point.` });
1339
507
  }
1340
508
  } else if (clip.type === "media") {
1341
509
  used.add(clip.asset);
@@ -1344,28 +512,28 @@ function checkVideoProject(project) {
1344
512
  else {
1345
513
  if (track.kind === "audio" && asset.kind === "image") issues.push({ code: "wrong-track", severity: "error", ...at, message: `Clip "${clip.id}" is an image on audio track "${track.id}"; move it to a video track.` });
1346
514
  if (track.kind === "video" && asset.kind === "audio") issues.push({ code: "wrong-track", severity: "error", ...at, message: `Clip "${clip.id}" is audio on video track "${track.id}"; move it to an audio track.` });
1347
- const range = videoClipSourceRange(clip);
1348
- if (asset.duration !== void 0 && range.out > asset.duration + VIDEO_TIME_EPSILON)
1349
- issues.push({ code: "source-overrun", severity: "error", ...at, message: `Clip "${clip.id}" plays to ${formatVideoSeconds(range.out)} of "${asset.id}", which is only ${formatVideoSeconds(asset.duration)} long; shorten the clip or move its in point.` });
515
+ const range = _chunkSMJGE7HMcjs.videoClipSourceRange.call(void 0, clip);
516
+ if (asset.duration !== void 0 && range.out > asset.duration + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON)
517
+ issues.push({ code: "source-overrun", severity: "error", ...at, message: `Clip "${clip.id}" plays to ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, range.out)} of "${asset.id}", which is only ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, asset.duration)} long; shorten the clip or move its in point.` });
1350
518
  }
1351
519
  } else if (track.kind === "audio") issues.push({ code: "wrong-track", severity: "error", ...at, message: `Clip "${clip.id}" is a ${clip.type} clip on audio track "${track.id}"; move it to a video track.` });
1352
520
  const next = clips[i + 1];
1353
- if (next && next.start < videoClipEnd(clip) - VIDEO_TIME_EPSILON)
1354
- issues.push({ code: "overlap", severity: "error", ...at, message: `Clips "${clip.id}" and "${next.id}" overlap on track "${track.id}"${where} from ${formatVideoSeconds(next.start)}; move one of them.` });
1355
- if ((_nullishCoalesce(clip.fadeIn, () => ( 0))) + (_nullishCoalesce(clip.fadeOut, () => ( 0))) > clip.duration + VIDEO_TIME_EPSILON)
521
+ if (next && next.start < _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, clip) - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON)
522
+ issues.push({ code: "overlap", severity: "error", ...at, message: `Clips "${clip.id}" and "${next.id}" overlap on track "${track.id}"${where} from ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, next.start)}; move one of them.` });
523
+ if ((_nullishCoalesce(clip.fadeIn, () => ( 0))) + (_nullishCoalesce(clip.fadeOut, () => ( 0))) > clip.duration + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON)
1356
524
  issues.push({ code: "fades-too-long", severity: "warning", ...at, message: `Clip "${clip.id}"'s fades (${_nullishCoalesce(clip.fadeIn, () => ( 0))} + ${_nullishCoalesce(clip.fadeOut, () => ( 0))} ms) are longer than the clip (${clip.duration} ms); shorten them.` });
1357
- const anim = (clip.animateIn ? _nullishCoalesce(clip.animateIn.duration, () => ( VIDEO_CLIP_ANIMATION_DURATION)) : 0) + (clip.animateOut ? _nullishCoalesce(clip.animateOut.duration, () => ( VIDEO_CLIP_ANIMATION_DURATION)) : 0);
1358
- if (anim > clip.duration + VIDEO_TIME_EPSILON)
525
+ const anim = (clip.animateIn ? _nullishCoalesce(clip.animateIn.duration, () => ( _chunkSMJGE7HMcjs.VIDEO_CLIP_ANIMATION_DURATION)) : 0) + (clip.animateOut ? _nullishCoalesce(clip.animateOut.duration, () => ( _chunkSMJGE7HMcjs.VIDEO_CLIP_ANIMATION_DURATION)) : 0);
526
+ if (anim > clip.duration + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON)
1359
527
  issues.push({ code: "animations-too-long", severity: "warning", ...at, message: `Clip "${clip.id}"'s entrance and exit (${anim} ms together) are longer than the clip (${clip.duration} ms), so both are shortened; give them shorter durations.` });
1360
528
  for (const which of ["animateIn", "animateOut"]) {
1361
529
  const a = clip[which];
1362
530
  if (!a) continue;
1363
- const keyed = videoClipAnimationProperties(a).filter((p) => videoKeysOf(p === "opacity" ? clip.opacity : _optionalChain([clip, 'access', _16 => _16.transform, 'optionalAccess', _17 => _17[p]])).length);
531
+ const keyed = _chunkSMJGE7HMcjs.videoClipAnimationProperties.call(void 0, a).filter((p) => _chunkSMJGE7HMcjs.videoKeysOf.call(void 0, p === "opacity" ? clip.opacity : _optionalChain([clip, 'access', _4 => _4.transform, 'optionalAccess', _5 => _5[p]])).length);
1364
532
  if (keyed.length)
1365
533
  issues.push({ code: "animation-keyed", severity: "note", ...at, message: `Clip "${clip.id}"'s ${a.effect} ${which === "animateIn" ? "entrance" : "exit"} leaves ${keyed.join(", ")} alone, because the clip keys ${keyed.length > 1 ? "them" : "it"} itself; remove those keyframes or the animation.` });
1366
534
  }
1367
535
  for (const { property, keys } of videoClipKeyframes(clip)) {
1368
- const outside = keys.filter((k) => k.t < -VIDEO_TIME_EPSILON || k.t > clip.duration + VIDEO_TIME_EPSILON);
536
+ const outside = keys.filter((k) => k.t < -_chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON || k.t > clip.duration + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON);
1369
537
  if (outside.length)
1370
538
  issues.push({
1371
539
  code: "keyframes-outside-clip",
@@ -1376,20 +544,20 @@ function checkVideoProject(project) {
1376
544
  }
1377
545
  if (clip.transition) {
1378
546
  const d = clip.transition.duration;
1379
- const before = videoClipBefore(track, clip);
547
+ const before = _chunkSMJGE7HMcjs.videoClipBefore.call(void 0, track, clip);
1380
548
  if (!before) {
1381
549
  issues.push({ code: "transition-without-cut", severity: "warning", ...at, message: `Clip "${clip.id}" has a transition but no clip ends where it starts on track "${track.id}", so it plays as nothing; butt a clip up against it or remove the transition.` });
1382
550
  continue;
1383
551
  }
1384
- if (d > clip.duration + VIDEO_TIME_EPSILON || d > before.duration + VIDEO_TIME_EPSILON) {
552
+ if (d > clip.duration + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON || d > before.duration + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) {
1385
553
  issues.push({ code: "transition-too-long", severity: "error", ...at, message: `The ${d} ms transition into "${clip.id}" is longer than "${clip.id}" (${clip.duration} ms) or "${before.id}" (${before.duration} ms); make it at most ${Math.min(clip.duration, before.duration)} ms.` });
1386
554
  continue;
1387
555
  }
1388
556
  if (clip.transition.type === "dip") continue;
1389
- const outgoing = videoClipHandles(before, assets, sequences).after;
1390
- const incoming = videoClipHandles(clip, assets, sequences).before;
557
+ const outgoing = _chunkSMJGE7HMcjs.videoClipHandles.call(void 0, before, assets, sequences).after;
558
+ const incoming = _chunkSMJGE7HMcjs.videoClipHandles.call(void 0, clip, assets, sequences).before;
1391
559
  const half = d / 2;
1392
- if (outgoing < half - VIDEO_TIME_EPSILON || incoming < half - VIDEO_TIME_EPSILON) {
560
+ if (outgoing < half - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON || incoming < half - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) {
1393
561
  const fits = 2 * Math.min(outgoing, incoming);
1394
562
  issues.push({
1395
563
  code: "transition-handles",
@@ -1406,9 +574,9 @@ function checkVideoProject(project) {
1406
574
  for (const sequence of _nullishCoalesce(project.sequences, () => ( [])))
1407
575
  if (!usedSequences.has(sequence.id)) issues.push({ code: "unused-sequence", severity: "note", sequence: sequence.id, message: `Sequence "${sequence.id}" is not played by any compound clip.` });
1408
576
  for (const asset of project.assets) if (!used.has(asset.id)) issues.push({ code: "unused-asset", severity: "note", asset: asset.id, message: `Asset "${asset.id}" is not used by any clip.` });
1409
- const end = videoProjectDuration(project);
577
+ const end = _chunkSMJGE7HMcjs.videoProjectDuration.call(void 0, project);
1410
578
  for (const marker of _nullishCoalesce(project.markers, () => ( [])))
1411
- if (marker.time > end + VIDEO_TIME_EPSILON) issues.push({ code: "marker-past-end", severity: "note", marker: marker.id, message: `Marker "${marker.id}" at ${formatVideoSeconds(marker.time)} is past the end (${formatVideoSeconds(end)}).` });
579
+ if (marker.time > end + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) issues.push({ code: "marker-past-end", severity: "note", marker: marker.id, message: `Marker "${marker.id}" at ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, marker.time)} is past the end (${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, end)}).` });
1412
580
  if (!project.tracks.some((t) => t.clips.length)) issues.push({ code: "empty", severity: "note", message: "The project has no clips yet; add assets and then clips." });
1413
581
  const rank = { error: 0, warning: 1, note: 2 };
1414
582
  return issues.sort((a, b) => rank[a.severity] - rank[b.severity]);
@@ -1455,14 +623,14 @@ var refuse = (message) => {
1455
623
  };
1456
624
  function freshId(prefix, taken) {
1457
625
  const used = new Set(taken);
1458
- for (let n2 = 1; ; n2++) if (!used.has(`${prefix}${n2}`)) return `${prefix}${n2}`;
626
+ for (let n = 1; ; n++) if (!used.has(`${prefix}${n}`)) return `${prefix}${n}`;
1459
627
  }
1460
628
  var slug = (s) => s.replace(/^.*[\\/]/, "").replace(/\.[a-z0-9]+(\?.*)?$/i, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 32);
1461
629
  function mergeSpans(spans) {
1462
630
  const out = [];
1463
631
  for (const [from, to] of [...spans].sort((a, b) => a[0] - b[0])) {
1464
632
  const last = out[out.length - 1];
1465
- if (last && from <= last[1] + VIDEO_TIME_EPSILON) last[1] = Math.max(last[1], to);
633
+ if (last && from <= last[1] + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) last[1] = Math.max(last[1], to);
1466
634
  else out.push([from, to]);
1467
635
  }
1468
636
  return out;
@@ -1476,31 +644,31 @@ function clipAnimatables(clip) {
1476
644
  // A speed ramp is keys in clip time too, so trims and splits keep it on the same frames of source.
1477
645
  { get: () => c.timeRemap, set: (v) => c.timeRemap = v }
1478
646
  ];
1479
- for (const p of VIDEO_TRANSFORM_PROPERTIES) out.push({ get: () => _optionalChain([c, 'access', _18 => _18.transform, 'optionalAccess', _19 => _19[p]]), set: (v) => c.transform && (c.transform[p] = v) });
647
+ for (const p of _chunkSMJGE7HMcjs.VIDEO_TRANSFORM_PROPERTIES) out.push({ get: () => _optionalChain([c, 'access', _6 => _6.transform, 'optionalAccess', _7 => _7[p]]), set: (v) => c.transform && (c.transform[p] = v) });
1480
648
  return out;
1481
649
  }
1482
650
  function retimeClipKeys(clip, fn) {
1483
651
  for (const a of clipAnimatables(clip)) {
1484
652
  const v = a.get();
1485
- if (isVideoKeyframes(v)) a.set(fn(v));
653
+ if (_chunkSMJGE7HMcjs.isVideoKeyframes.call(void 0, v)) a.set(fn(v));
1486
654
  }
1487
655
  }
1488
656
  var OPTIONAL_CLIP_FIELDS = ["name", "opacity", "transform", "fit", "blend", "grade", "animateIn", "animateOut", "fadeIn", "fadeOut", "fadeInCurve", "fadeOutCurve", "transition", "in", "speed", "timeRemap", "volume", "muted", "color", "fontSize", "fontFamily", "fontWeight", "background"];
1489
657
  var TIME_FIELDS = { slip_clip: ["delta"], roll_edit: ["delta"], slide_clip: ["delta"], lift_range: ["from", "to"], extract_range: ["from", "to"], split_at: ["at"] };
1490
- var Editor = (_class2 = class {
1491
- constructor(p) {;_class2.prototype.__init5.call(this);
658
+ var Editor = (_class = class {
659
+ constructor(p) {;_class.prototype.__init.call(this);
1492
660
  this.p = p;
1493
661
  }
1494
662
 
1495
- __init5() {this.outcomes = []}
663
+ __init() {this.outcomes = []}
1496
664
  track(id) {
1497
665
  const t = this.p.tracks.find((x) => x.id === id);
1498
666
  if (!t) refuse(`There is no track ${_chunkWAJQKWQUcjs.namedWithSuggestion.call(void 0, id, this.p.tracks.map((x) => x.id))}; the tracks are ${this.p.tracks.map((x) => `"${x.id}" (${x.kind})`).join(", ") || "none"}.`);
1499
667
  return t;
1500
668
  }
1501
669
  clip(id) {
1502
- const at = findVideoClip(this.p, id);
1503
- const nested = at ? null : findVideoSequenceClip(this.p, id);
670
+ const at = _chunkSMJGE7HMcjs.findVideoClip.call(void 0, this.p, id);
671
+ const nested = at ? null : _chunkSMJGE7HMcjs.findVideoSequenceClip.call(void 0, this.p, id);
1504
672
  if (nested) refuse(`Clip "${id}" is inside sequence "${nested.sequence.id}", and clips inside sequences cannot be edited yet; edit the compound clip that plays the sequence instead (list_video_sequences shows which).`);
1505
673
  if (!at) {
1506
674
  const ids = this.p.tracks.flatMap((t) => t.clips.map((c) => c.id));
@@ -1517,10 +685,10 @@ var Editor = (_class2 = class {
1517
685
  return a;
1518
686
  }
1519
687
  fits(track, start, duration, ignore, what) {
1520
- const hit = videoOverlap(track, start, start + duration, ignore);
688
+ const hit = _chunkSMJGE7HMcjs.videoOverlap.call(void 0, track, start, start + duration, ignore);
1521
689
  if (hit) {
1522
- const free = nextFreeVideoSlot(track, start, duration, ignore);
1523
- refuse(`${what} at ${formatVideoSeconds(start)}\u2013${formatVideoSeconds(start + duration)} would overlap clip "${hit.id}" (${formatVideoSeconds(hit.start)}\u2013${formatVideoSeconds(videoClipEnd(hit))}) on track "${track.id}"; the next free place is ${formatVideoSeconds(free)}, or use mode "insert" to push later clips along or "overwrite" to replace what is there.`);
690
+ const free = _chunkSMJGE7HMcjs.nextFreeVideoSlot.call(void 0, track, start, duration, ignore);
691
+ refuse(`${what} at ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, start)}\u2013${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, start + duration)} would overlap clip "${hit.id}" (${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, hit.start)}\u2013${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, hit))}) on track "${track.id}"; the next free place is ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, free)}, or use mode "insert" to push later clips along or "overwrite" to replace what is there.`);
1524
692
  }
1525
693
  }
1526
694
  kindFits(track, clip) {
@@ -1531,19 +699,19 @@ var Editor = (_class2 = class {
1531
699
  return;
1532
700
  }
1533
701
  if (track.kind === "audio" && (clip.type !== "media" || asset.kind === "image")) refuse(`Track "${track.id}" is an audio track; ${clip.type === "media" ? "images" : `${clip.type} clips`} go on a video track.`);
1534
- if (track.kind === "video" && _optionalChain([asset, 'optionalAccess', _20 => _20.kind]) === "audio") refuse(`Track "${track.id}" is a video track; audio asset "${asset.id}" goes on an audio track.`);
702
+ if (track.kind === "video" && _optionalChain([asset, 'optionalAccess', _8 => _8.kind]) === "audio") refuse(`Track "${track.id}" is a video track; audio asset "${asset.id}" goes on an audio track.`);
1535
703
  }
1536
704
  /** Moves every clip on `track` starting at or after `from` by `delta`. */
1537
705
  shift(track, from, delta, except = /* @__PURE__ */ new Set()) {
1538
706
  if (!delta) return;
1539
- for (const c of track.clips) if (!except.has(c.id) && c.start >= from - VIDEO_TIME_EPSILON) c.start = Math.max(0, c.start + delta);
707
+ for (const c of track.clips) if (!except.has(c.id) && c.start >= from - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) c.start = Math.max(0, c.start + delta);
1540
708
  track.clips.sort((a, b) => a.start - b.start);
1541
709
  }
1542
710
  get frame() {
1543
711
  return 1e3 / this.p.fps;
1544
712
  }
1545
713
  ids() {
1546
- return allVideoClipIds(this.p);
714
+ return _chunkSMJGE7HMcjs.allVideoClipIds.call(void 0, this.p);
1547
715
  }
1548
716
  /**
1549
717
  * The tracks a ripple on `sources` moves, top to bottom: the sources, and every unlocked
@@ -1551,8 +719,8 @@ var Editor = (_class2 = class {
1551
719
  */
1552
720
  rippleTracks(sources) {
1553
721
  const set = new Set(sources);
1554
- if ([...set].some(videoTrackSyncLocked)) {
1555
- for (const t of this.p.tracks) if (videoTrackSyncLocked(t) && !t.locked) set.add(t);
722
+ if ([...set].some(_chunkSMJGE7HMcjs.videoTrackSyncLocked)) {
723
+ for (const t of this.p.tracks) if (_chunkSMJGE7HMcjs.videoTrackSyncLocked.call(void 0, t) && !t.locked) set.add(t);
1556
724
  }
1557
725
  return this.p.tracks.filter((t) => set.has(t));
1558
726
  }
@@ -1569,7 +737,7 @@ var Editor = (_class2 = class {
1569
737
  return open;
1570
738
  }
1571
739
  range(from, to) {
1572
- if (!(to - from > VIDEO_TIME_EPSILON)) refuse(`"to" (${formatVideoSeconds(to)}) must be after "from" (${formatVideoSeconds(from)}).`);
740
+ if (!(to - from > _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON)) refuse(`"to" (${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, to)}) must be after "from" (${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, from)}).`);
1573
741
  }
1574
742
  /** Moves a media or compound clip's in point by `delta` of timeline time (stills, titles and colours have none). */
1575
743
  moveIn(clip, delta) {
@@ -1578,18 +746,18 @@ var Editor = (_class2 = class {
1578
746
  if (clip.in < 1e-9) delete clip.in;
1579
747
  return;
1580
748
  }
1581
- if (clip.type !== "media" || _optionalChain([videoAssetMap, 'call', _21 => _21(this.p), 'access', _22 => _22.get, 'call', _23 => _23(clip.asset), 'optionalAccess', _24 => _24.kind]) === "image") return;
1582
- clip.in = Math.max(0, (_nullishCoalesce(clip.in, () => ( 0))) + delta * videoClipSpeed(clip));
749
+ if (clip.type !== "media" || _optionalChain([_chunkSMJGE7HMcjs.videoAssetMap.call(void 0, this.p), 'access', _9 => _9.get, 'call', _10 => _10(clip.asset), 'optionalAccess', _11 => _11.kind]) === "image") return;
750
+ clip.in = Math.max(0, (_nullishCoalesce(clip.in, () => ( 0))) + delta * _chunkSMJGE7HMcjs.videoClipSpeed.call(void 0, clip));
1583
751
  if (clip.in < 1e-9) delete clip.in;
1584
752
  }
1585
753
  /** Moves a clip's head `delta` later in time (start, in point and keys); the caller sets start and duration. */
1586
754
  trimHead(clip, delta) {
1587
755
  if (!isRamped(clip)) this.moveIn(clip, delta);
1588
- retimeClipKeys(clip, (v) => retimeVideoAnimatable(v, -delta));
756
+ retimeClipKeys(clip, (v) => _chunkSMJGE7HMcjs.retimeVideoAnimatable.call(void 0, v, -delta));
1589
757
  }
1590
758
  /** Cuts `clip` at `at` (inside it): the left part keeps the id, the right part is returned. */
1591
759
  cut(track, clip, at, id) {
1592
- const end = videoClipEnd(clip);
760
+ const end = _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, clip);
1593
761
  const right = { ...structuredClone(clip), id: _nullishCoalesce(id, () => ( freshId(`${clip.id}-`, this.ids()))), start: at, duration: end - at };
1594
762
  delete right.transition;
1595
763
  delete right.fadeIn;
@@ -1597,9 +765,9 @@ var Editor = (_class2 = class {
1597
765
  delete right.animateIn;
1598
766
  const leftValues = clipAnimatables(clip);
1599
767
  clipAnimatables(right).forEach((r, i) => {
1600
- const [l, rr] = splitVideoAnimatable(leftValues[i].get(), at - clip.start);
1601
- if (isVideoKeyframes(l)) leftValues[i].set(l);
1602
- if (isVideoKeyframes(rr)) r.set(rr);
768
+ const [l, rr] = _chunkSMJGE7HMcjs.splitVideoAnimatable.call(void 0, leftValues[i].get(), at - clip.start);
769
+ if (_chunkSMJGE7HMcjs.isVideoKeyframes.call(void 0, l)) leftValues[i].set(l);
770
+ if (_chunkSMJGE7HMcjs.isVideoKeyframes.call(void 0, rr)) r.set(rr);
1603
771
  });
1604
772
  if (!isRamped(right)) this.moveIn(right, at - clip.start);
1605
773
  clip.duration = at - clip.start;
@@ -1613,7 +781,7 @@ var Editor = (_class2 = class {
1613
781
  /** Cuts every clip on `tracks` that runs across `at`; returns the new right-hand parts. */
1614
782
  cutAcross(tracks, at) {
1615
783
  const made = [];
1616
- for (const t of tracks) for (const c of [...t.clips]) if (c.start < at - VIDEO_TIME_EPSILON && videoClipEnd(c) > at + VIDEO_TIME_EPSILON) made.push(this.cut(t, c, at));
784
+ for (const t of tracks) for (const c of [...t.clips]) if (c.start < at - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON && _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, c) > at + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) made.push(this.cut(t, c, at));
1617
785
  return made;
1618
786
  }
1619
787
  /**
@@ -1631,10 +799,10 @@ var Editor = (_class2 = class {
1631
799
  */
1632
800
  close(tracks, from, to) {
1633
801
  for (const t of tracks) {
1634
- const hit = videoOverlap(t, from, to);
802
+ const hit = _chunkSMJGE7HMcjs.videoOverlap.call(void 0, t, from, to);
1635
803
  if (hit)
1636
804
  refuse(
1637
- `Rippling out ${formatVideoSeconds(from)}\u2013${formatVideoSeconds(to)} would pull track "${t.id}" out of sync: clip "${hit.id}" (${formatVideoSeconds(hit.start)}\u2013${formatVideoSeconds(videoClipEnd(hit))}) is in that stretch. Extract the range across "${t.id}" too (extract_range with its id in "tracks"), turn its sync lock off (update_track with syncLocked false) or lock it, or lift instead of rippling.`
805
+ `Rippling out ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, from)}\u2013${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, to)} would pull track "${t.id}" out of sync: clip "${hit.id}" (${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, hit.start)}\u2013${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, hit))}) is in that stretch. Extract the range across "${t.id}" too (extract_range with its id in "tracks"), turn its sync lock off (update_track with syncLocked false) or lock it, or lift instead of rippling.`
1638
806
  );
1639
807
  }
1640
808
  for (const t of tracks) this.shift(t, to, from - to);
@@ -1646,10 +814,10 @@ var Editor = (_class2 = class {
1646
814
  clear(track, from, to, report) {
1647
815
  for (const c of [...track.clips]) {
1648
816
  const start = c.start;
1649
- const end = videoClipEnd(c);
1650
- if (end <= from + VIDEO_TIME_EPSILON || start >= to - VIDEO_TIME_EPSILON) continue;
1651
- const coversHead = start >= from - VIDEO_TIME_EPSILON;
1652
- const coversTail = end <= to + VIDEO_TIME_EPSILON;
817
+ const end = _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, c);
818
+ if (end <= from + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON || start >= to - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) continue;
819
+ const coversHead = start >= from - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON;
820
+ const coversTail = end <= to + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON;
1653
821
  if (coversHead && coversTail) {
1654
822
  track.clips = track.clips.filter((x) => x !== c);
1655
823
  report.removed.push(c.id);
@@ -1672,8 +840,8 @@ var Editor = (_class2 = class {
1672
840
  /** Clamps a slip, roll or slide to `[lo, hi]`, refusing when it cannot move that way at all. */
1673
841
  clamp(delta, lo, hi, why) {
1674
842
  const applied = Math.min(Math.max(delta, Math.min(0, lo)), Math.max(0, hi));
1675
- if (Math.abs(delta) > VIDEO_TIME_EPSILON && Math.abs(applied) <= VIDEO_TIME_EPSILON) refuse(why());
1676
- return Math.abs(applied) <= VIDEO_TIME_EPSILON ? 0 : applied;
843
+ if (Math.abs(delta) > _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON && Math.abs(applied) <= _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) refuse(why());
844
+ return Math.abs(applied) <= _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON ? 0 : applied;
1677
845
  }
1678
846
  /** Places a clip on a track by `mode`, returning what it cut, trimmed, removed and rippled. */
1679
847
  place(track, clip, mode, outcome) {
@@ -1683,14 +851,14 @@ var Editor = (_class2 = class {
1683
851
  outcome.rippled = tracks.map((t) => t.id);
1684
852
  } else if (mode === "overwrite") {
1685
853
  const report = { removed: [], trimmed: [], created: [] };
1686
- this.clear(track, clip.start, videoClipEnd(clip), report);
854
+ this.clear(track, clip.start, _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, clip), report);
1687
855
  Object.assign(outcome, report);
1688
856
  } else this.fits(track, clip.start, clip.duration, /* @__PURE__ */ new Set([clip.id]), `Clip "${clip.id}"`);
1689
857
  track.clips.push(clip);
1690
858
  track.clips.sort((a, b) => a.start - b.start);
1691
859
  }
1692
860
  placement(mode, ripple) {
1693
- if (mode !== void 0 && !VIDEO_PLACEMENT_MODES.includes(mode)) refuse(`"mode" must be ${VIDEO_PLACEMENT_MODES.map((m) => `"${m}"`).join(", ")}.`);
861
+ if (mode !== void 0 && !_chunkSMJGE7HMcjs.VIDEO_PLACEMENT_MODES.includes(mode)) refuse(`"mode" must be ${_chunkSMJGE7HMcjs.VIDEO_PLACEMENT_MODES.map((m) => `"${m}"`).join(", ")}.`);
1694
862
  if (ripple && mode !== void 0 && mode !== "insert") refuse(`"ripple" means mode "insert", so it cannot go with mode "${mode}"; pass one or the other.`);
1695
863
  return _nullishCoalesce(mode, () => ( (ripple ? "insert" : "refuse")));
1696
864
  }
@@ -1727,7 +895,7 @@ var Editor = (_class2 = class {
1727
895
  }
1728
896
  case "remove_asset": {
1729
897
  const asset = this.asset(e.asset);
1730
- const users = allVideoTracks(this.p).flatMap(({ track: t }) => t.clips.filter((c) => c.type === "media" && c.asset === asset.id).map((c) => c.id));
898
+ const users = _chunkSMJGE7HMcjs.allVideoTracks.call(void 0, this.p).flatMap(({ track: t }) => t.clips.filter((c) => c.type === "media" && c.asset === asset.id).map((c) => c.id));
1731
899
  if (users.length) refuse(`Asset "${asset.id}" is used by clip${users.length > 1 ? "s" : ""} ${users.map((u) => `"${u}"`).join(", ")}; delete ${users.length > 1 ? "them" : "it"} first.`);
1732
900
  this.p.assets = this.p.assets.filter((a) => a !== asset);
1733
901
  return this.outcomes.push({ op: e.op, asset: asset.id });
@@ -1736,8 +904,8 @@ var Editor = (_class2 = class {
1736
904
  const ids = this.p.tracks.map((t) => t.id);
1737
905
  if (e.track.id && ids.includes(e.track.id)) refuse(`There is already a track "${e.track.id}".`);
1738
906
  const id = _nullishCoalesce(e.track.id, () => ( freshId(e.track.kind === "video" ? "v" : "a", ids)));
1739
- const n2 = this.p.tracks.filter((t) => t.kind === e.track.kind).length + 1;
1740
- const track = { name: `${e.track.kind === "video" ? "Video" : "Audio"} ${n2}`, ...e.track, id, clips: [] };
907
+ const n = this.p.tracks.filter((t) => t.kind === e.track.kind).length + 1;
908
+ const track = { name: `${e.track.kind === "video" ? "Video" : "Audio"} ${n}`, ...e.track, id, clips: [] };
1741
909
  const at = _nullishCoalesce(e.at, () => ( (e.track.kind === "video" ? 0 : this.p.tracks.length)));
1742
910
  if (at > this.p.tracks.length) refuse(`"at" is ${at} but there are only ${this.p.tracks.length} tracks; use 0\u2013${this.p.tracks.length}.`);
1743
911
  this.p.tracks.splice(at, 0, track);
@@ -1780,12 +948,12 @@ var Editor = (_class2 = class {
1780
948
  else draft.duration = (asset.duration - (_nullishCoalesce(draft.in, () => ( 0)))) / (_nullishCoalesce(draft.speed, () => ( 1)));
1781
949
  if (!(draft.duration > 0)) refuse(`"in" (${draft.in} ms) is at or past the end of "${asset.id}" (${asset.duration} ms).`);
1782
950
  } else if (draft.type === "sequence") {
1783
- const length = videoSequenceDuration(videoSequenceMap(this.p).get(draft.sequence)) - (_nullishCoalesce(draft.in, () => ( 0)));
951
+ const length = _chunkSMJGE7HMcjs.videoSequenceDuration.call(void 0, _chunkSMJGE7HMcjs.videoSequenceMap.call(void 0, this.p).get(draft.sequence)) - (_nullishCoalesce(draft.in, () => ( 0)));
1784
952
  if (!(length > 0)) refuse(`"in" (${_nullishCoalesce(draft.in, () => ( 0))} ms) is at or past the end of sequence "${draft.sequence}".`);
1785
953
  draft.duration = length;
1786
954
  } else draft.duration = VIDEO_DEFAULT_TITLE_DURATION;
1787
955
  }
1788
- const end = sortedVideoClips(track).reduce((m, c) => Math.max(m, videoClipEnd(c)), 0);
956
+ const end = _chunkSMJGE7HMcjs.sortedVideoClips.call(void 0, track).reduce((m, c) => Math.max(m, _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, c)), 0);
1789
957
  draft.start ??= end;
1790
958
  const clip = parseVideoClip(draft, "clip");
1791
959
  const outcome = { op: e.op, clip: clip.id, track: track.id };
@@ -1810,20 +978,20 @@ var Editor = (_class2 = class {
1810
978
  case "trim_clip": {
1811
979
  const { track, clip } = this.clip(e.clip);
1812
980
  this.unlocked(track);
1813
- const speed = videoClipSpeed(clip);
1814
- const end = videoClipEnd(clip);
981
+ const speed = _chunkSMJGE7HMcjs.videoClipSpeed.call(void 0, clip);
982
+ const end = _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, clip);
1815
983
  const minimum = 1e3 / this.p.fps;
1816
- const assets = videoAssetMap(this.p);
984
+ const assets = _chunkSMJGE7HMcjs.videoAssetMap.call(void 0, this.p);
1817
985
  if (e.edge === "start") {
1818
986
  const delta = e.to - clip.start;
1819
- if (clip.duration - delta < minimum - VIDEO_TIME_EPSILON) refuse(`Trimming the start to ${formatVideoSeconds(e.to)} would leave "${clip.id}" shorter than a frame; it ends at ${formatVideoSeconds(end)}.`);
987
+ if (clip.duration - delta < minimum - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) refuse(`Trimming the start to ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, e.to)} would leave "${clip.id}" shorter than a frame; it ends at ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, end)}.`);
1820
988
  if (clip.type === "media" && !isRamped(clip) || clip.type === "sequence") {
1821
989
  const sourceIn = (_nullishCoalesce(clip.in, () => ( 0))) + delta * speed;
1822
- if (sourceIn < -VIDEO_TIME_EPSILON) refuse(`"${clip.id}" starts ${Math.round((_nullishCoalesce(clip.in, () => ( 0))) / speed)} ms into its source, so its start can move at most that far earlier (to ${formatVideoSeconds(clip.start - (_nullishCoalesce(clip.in, () => ( 0))) / speed)}).`);
990
+ if (sourceIn < -_chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) refuse(`"${clip.id}" starts ${Math.round((_nullishCoalesce(clip.in, () => ( 0))) / speed)} ms into its source, so its start can move at most that far earlier (to ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, clip.start - (_nullishCoalesce(clip.in, () => ( 0))) / speed)}).`);
1823
991
  clip.in = Math.max(0, sourceIn);
1824
992
  if (clip.in === 0) delete clip.in;
1825
993
  }
1826
- retimeClipKeys(clip, (v) => retimeVideoAnimatable(v, -delta));
994
+ retimeClipKeys(clip, (v) => _chunkSMJGE7HMcjs.retimeVideoAnimatable.call(void 0, v, -delta));
1827
995
  if (e.ripple) {
1828
996
  const tracks = this.rippleTracks([track]);
1829
997
  if (delta > 0) {
@@ -1844,17 +1012,17 @@ var Editor = (_class2 = class {
1844
1012
  }
1845
1013
  } else {
1846
1014
  const duration = e.to - clip.start;
1847
- if (duration < minimum - VIDEO_TIME_EPSILON) refuse(`Trimming the end to ${formatVideoSeconds(e.to)} would leave "${clip.id}" shorter than a frame; it starts at ${formatVideoSeconds(clip.start)}.`);
1015
+ if (duration < minimum - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) refuse(`Trimming the end to ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, e.to)} would leave "${clip.id}" shorter than a frame; it starts at ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, clip.start)}.`);
1848
1016
  if (clip.type === "media" && !isRamped(clip)) {
1849
1017
  const asset = assets.get(clip.asset);
1850
1018
  const out = (_nullishCoalesce(clip.in, () => ( 0))) + duration * speed;
1851
- if (_optionalChain([asset, 'optionalAccess', _25 => _25.duration]) !== void 0 && out > asset.duration + VIDEO_TIME_EPSILON)
1852
- refuse(`"${clip.id}" can end at most at ${formatVideoSeconds(clip.start + (asset.duration - (_nullishCoalesce(clip.in, () => ( 0)))) / speed)}, where "${asset.id}" runs out.`);
1019
+ if (_optionalChain([asset, 'optionalAccess', _12 => _12.duration]) !== void 0 && out > asset.duration + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON)
1020
+ refuse(`"${clip.id}" can end at most at ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, clip.start + (asset.duration - (_nullishCoalesce(clip.in, () => ( 0)))) / speed)}, where "${asset.id}" runs out.`);
1853
1021
  }
1854
1022
  if (clip.type === "sequence") {
1855
- const seq = videoSequenceMap(this.p).get(clip.sequence);
1856
- const length = seq ? videoSequenceDuration(seq) : Infinity;
1857
- if ((_nullishCoalesce(clip.in, () => ( 0))) + duration > length + VIDEO_TIME_EPSILON) refuse(`"${clip.id}" can end at most at ${formatVideoSeconds(clip.start + length - (_nullishCoalesce(clip.in, () => ( 0))))}, where sequence "${clip.sequence}" ends.`);
1023
+ const seq = _chunkSMJGE7HMcjs.videoSequenceMap.call(void 0, this.p).get(clip.sequence);
1024
+ const length = seq ? _chunkSMJGE7HMcjs.videoSequenceDuration.call(void 0, seq) : Infinity;
1025
+ if ((_nullishCoalesce(clip.in, () => ( 0))) + duration > length + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) refuse(`"${clip.id}" can end at most at ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, clip.start + length - (_nullishCoalesce(clip.in, () => ( 0))))}, where sequence "${clip.sequence}" ends.`);
1858
1026
  }
1859
1027
  if (e.ripple) {
1860
1028
  const tracks = this.rippleTracks([track]);
@@ -1876,9 +1044,9 @@ var Editor = (_class2 = class {
1876
1044
  case "split_clip": {
1877
1045
  const { track, clip } = this.clip(e.clip);
1878
1046
  this.unlocked(track);
1879
- const end = videoClipEnd(clip);
1047
+ const end = _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, clip);
1880
1048
  const minimum = 1e3 / this.p.fps;
1881
- if (e.at < clip.start + minimum - VIDEO_TIME_EPSILON || e.at > end - minimum + VIDEO_TIME_EPSILON) refuse(`${formatVideoSeconds(e.at)} is not inside "${clip.id}" (${formatVideoSeconds(clip.start)}\u2013${formatVideoSeconds(end)}), at least a frame from either end.`);
1049
+ if (e.at < clip.start + minimum - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON || e.at > end - minimum + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) refuse(`${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, e.at)} is not inside "${clip.id}" (${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, clip.start)}\u2013${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, end)}), at least a frame from either end.`);
1882
1050
  if (e.id !== void 0 && this.ids().includes(e.id)) refuse(`There is already a clip "${e.id}".`);
1883
1051
  const right = this.cut(track, clip, e.at, e.id);
1884
1052
  return this.outcomes.push({ op: e.op, clips: [clip.id, right.id], clip: right.id, track: track.id });
@@ -1889,12 +1057,12 @@ var Editor = (_class2 = class {
1889
1057
  for (const track of new Set(found.map((f) => f.track))) track.clips = track.clips.filter((c) => !found.some((f) => f.clip === c));
1890
1058
  if (!e.ripple) return this.outcomes.push({ op: e.op, clips: found.map((f) => f.clip.id) });
1891
1059
  const rippled = /* @__PURE__ */ new Set();
1892
- const groups = [...new Set(found.map((f) => f.track))].filter((t) => !videoTrackSyncLocked(t)).map((t) => [t]);
1893
- if (found.some((f) => videoTrackSyncLocked(f.track))) groups.push(found.filter((f) => videoTrackSyncLocked(f.track)).map((f) => f.track));
1060
+ const groups = [...new Set(found.map((f) => f.track))].filter((t) => !_chunkSMJGE7HMcjs.videoTrackSyncLocked.call(void 0, t)).map((t) => [t]);
1061
+ if (found.some((f) => _chunkSMJGE7HMcjs.videoTrackSyncLocked.call(void 0, f.track))) groups.push(found.filter((f) => _chunkSMJGE7HMcjs.videoTrackSyncLocked.call(void 0, f.track)).map((f) => f.track));
1894
1062
  for (const sources of groups) {
1895
1063
  const tracks = this.rippleTracks(sources);
1896
1064
  tracks.forEach((t) => rippled.add(t));
1897
- const spans = mergeSpans(found.filter((f) => sources.includes(f.track)).map((f) => [f.clip.start, videoClipEnd(f.clip)]));
1065
+ const spans = mergeSpans(found.filter((f) => sources.includes(f.track)).map((f) => [f.clip.start, _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, f.clip)]));
1898
1066
  for (const [from, to] of spans.reverse()) this.close(tracks, from, to);
1899
1067
  }
1900
1068
  return this.outcomes.push({ op: e.op, clips: found.map((f) => f.clip.id), rippled: this.p.tracks.filter((t) => rippled.has(t)).map((t) => t.id) });
@@ -1910,15 +1078,15 @@ var Editor = (_class2 = class {
1910
1078
  if ("timeRemap" in set) refuse("Set a speed ramp with set_time_remap, which checks it against the media and moves later clips if asked.");
1911
1079
  let stretch = 1;
1912
1080
  if (at.clip.type === "media" && !("duration" in set)) {
1913
- const was = videoClipSpeed(at.clip);
1914
- const now = "speed" in set ? set.speed : _optionalChain([e, 'access', _26 => _26.remove, 'optionalAccess', _27 => _27.includes, 'call', _28 => _28("speed")]) ? 1 : was;
1081
+ const was = _chunkSMJGE7HMcjs.videoClipSpeed.call(void 0, at.clip);
1082
+ const now = "speed" in set ? set.speed : _optionalChain([e, 'access', _13 => _13.remove, 'optionalAccess', _14 => _14.includes, 'call', _15 => _15("speed")]) ? 1 : was;
1915
1083
  if (now !== was) {
1916
1084
  set.duration = at.clip.duration * was / now;
1917
1085
  stretch = was / now;
1918
1086
  }
1919
1087
  }
1920
1088
  const next = { ...structuredClone(at.clip), ...set };
1921
- if (stretch !== 1) retimeClipKeys(next, (v) => retimeVideoAnimatable(v, 0, stretch));
1089
+ if (stretch !== 1) retimeClipKeys(next, (v) => _chunkSMJGE7HMcjs.retimeVideoAnimatable.call(void 0, v, 0, stretch));
1922
1090
  for (const k of _nullishCoalesce(e.remove, () => ( []))) delete next[k];
1923
1091
  const clip = parseVideoClip(next, "set");
1924
1092
  if (clip.type === "media" && clip.asset !== at.clip.asset) this.kindFits(at.track, clip);
@@ -1935,13 +1103,13 @@ var Editor = (_class2 = class {
1935
1103
  const asset = this.asset(clip.asset);
1936
1104
  if (asset.kind === "image") refuse(`Clip "${clip.id}" is a still ("${asset.id}"), so it has no motion to ramp.`);
1937
1105
  const media = clip;
1938
- const end = videoClipEnd(media);
1106
+ const end = _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, media);
1939
1107
  const duration = _nullishCoalesce(e.duration, () => ( media.duration));
1940
- if (!(duration >= 1e3 / this.p.fps - VIDEO_TIME_EPSILON)) refuse(`"duration" must be at least a frame (${Math.ceil(1e3 / this.p.fps)} ms).`);
1108
+ if (!(duration >= 1e3 / this.p.fps - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON)) refuse(`"duration" must be at least a frame (${Math.ceil(1e3 / this.p.fps)} ms).`);
1941
1109
  if (e.keys === null) {
1942
1110
  if (!media.timeRemap) refuse(`Clip "${clip.id}" has no speed ramp to remove.`);
1943
1111
  const from = (_nullishCoalesce(media.in, () => ( 0))) + media.timeRemap.keys[0].v;
1944
- if (asset.duration !== void 0 && from + duration > asset.duration + VIDEO_TIME_EPSILON)
1112
+ if (asset.duration !== void 0 && from + duration > asset.duration + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON)
1945
1113
  refuse(`At normal speed from ${Math.round(from)} ms, "${clip.id}" would run past the end of "${asset.id}" (${asset.duration} ms); give a duration of at most ${Math.floor(asset.duration - from)} ms.`);
1946
1114
  delete media.timeRemap;
1947
1115
  media.in = from;
@@ -1949,17 +1117,17 @@ var Editor = (_class2 = class {
1949
1117
  } else {
1950
1118
  if (!Array.isArray(e.keys) || !e.keys.length) refuse('"keys" must be a list of at least one { t, v, ease } (or null to remove the ramp).');
1951
1119
  const remap = parseVideoTimeRemap({ keys: e.keys }, void 0, "keys");
1952
- remap.keys = sortVideoKeys(remap.keys);
1953
- checkVideoTimeRemap(remap, "keys");
1954
- const span = videoTimeRemapSpan(remap);
1955
- if (asset.duration !== void 0 && (_nullishCoalesce(media.in, () => ( 0))) + span.to > asset.duration + VIDEO_TIME_EPSILON)
1120
+ remap.keys = _chunkSMJGE7HMcjs.sortVideoKeys.call(void 0, remap.keys);
1121
+ _chunkSMJGE7HMcjs.checkVideoTimeRemap.call(void 0, remap, "keys");
1122
+ const span = _chunkSMJGE7HMcjs.videoTimeRemapSpan.call(void 0, remap);
1123
+ if (asset.duration !== void 0 && (_nullishCoalesce(media.in, () => ( 0))) + span.to > asset.duration + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON)
1956
1124
  refuse(`The ramp plays ${Math.round(span.to)} ms of source from the in point (${_nullishCoalesce(media.in, () => ( 0))} ms), past the end of "${asset.id}" (${asset.duration} ms); it can use at most ${Math.floor(asset.duration - (_nullishCoalesce(media.in, () => ( 0))))} ms.`);
1957
1125
  media.timeRemap = remap;
1958
1126
  delete media.speed;
1959
1127
  }
1960
1128
  const newEnd = media.start + duration;
1961
1129
  const outcome = { op: e.op, clip: clip.id, track: track.id };
1962
- if (Math.abs(newEnd - end) > VIDEO_TIME_EPSILON) {
1130
+ if (Math.abs(newEnd - end) > _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) {
1963
1131
  if (e.ripple) {
1964
1132
  const tracks = this.rippleTracks([track]);
1965
1133
  if (newEnd < end) {
@@ -1991,8 +1159,8 @@ var Editor = (_class2 = class {
1991
1159
  if (clip.type !== "media" && clip.type !== "sequence") refuse(`Clip "${clip.id}" is a ${clip.type === "text" ? "title" : "colour"}, so it has no source to slip; only video, audio and compound clips slip.`);
1992
1160
  const source = clip.type === "sequence" ? `sequence "${clip.sequence}"` : `"${this.asset(clip.asset).id}"`;
1993
1161
  if (clip.type === "media" && this.asset(clip.asset).kind === "image") refuse(`Clip "${clip.id}" is a still ("${clip.asset}"), so it has no source to slip; only video and audio clips slip.`);
1994
- const handles = videoClipHandles(clip, videoAssetMap(this.p), videoSequenceMap(this.p));
1995
- const { lead, tail } = videoClipOverhang(track, clip);
1162
+ const handles = _chunkSMJGE7HMcjs.videoClipHandles.call(void 0, clip, _chunkSMJGE7HMcjs.videoAssetMap.call(void 0, this.p), _chunkSMJGE7HMcjs.videoSequenceMap.call(void 0, this.p));
1163
+ const { lead, tail } = _chunkSMJGE7HMcjs.videoClipOverhang.call(void 0, track, clip);
1996
1164
  const delta = this.clamp(
1997
1165
  e.delta,
1998
1166
  lead - handles.before,
@@ -2010,13 +1178,13 @@ var Editor = (_class2 = class {
2010
1178
  this.unlocked(track);
2011
1179
  const L = left.clip;
2012
1180
  const R = right.clip;
2013
- if (Math.abs(videoClipEnd(L) - R.start) > VIDEO_TIME_EPSILON) {
2014
- if (Math.abs(videoClipEnd(R) - L.start) <= VIDEO_TIME_EPSILON) refuse(`"${R.id}" comes before "${L.id}"; pass left "${R.id}" and right "${L.id}".`);
2015
- refuse(`"${L.id}" ends at ${formatVideoSeconds(videoClipEnd(L))} but "${R.id}" starts at ${formatVideoSeconds(R.start)}; a roll needs two clips that meet at a cut (use trim_clip to move one edge).`);
1181
+ if (Math.abs(_chunkSMJGE7HMcjs.videoClipEnd.call(void 0, L) - R.start) > _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) {
1182
+ if (Math.abs(_chunkSMJGE7HMcjs.videoClipEnd.call(void 0, R) - L.start) <= _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) refuse(`"${R.id}" comes before "${L.id}"; pass left "${R.id}" and right "${L.id}".`);
1183
+ refuse(`"${L.id}" ends at ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, L))} but "${R.id}" starts at ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, R.start)}; a roll needs two clips that meet at a cut (use trim_clip to move one edge).`);
2016
1184
  }
2017
- const assets = videoAssetMap(this.p);
2018
- const lo = Math.max(-(L.duration - this.frame), -(videoClipHandles(R, assets, videoSequenceMap(this.p)).before - videoClipOverhang(track, R).lead));
2019
- const hi = Math.min(R.duration - this.frame, videoClipHandles(L, assets, videoSequenceMap(this.p)).after - videoClipOverhang(track, L).tail);
1185
+ const assets = _chunkSMJGE7HMcjs.videoAssetMap.call(void 0, this.p);
1186
+ const lo = Math.max(-(L.duration - this.frame), -(_chunkSMJGE7HMcjs.videoClipHandles.call(void 0, R, assets, _chunkSMJGE7HMcjs.videoSequenceMap.call(void 0, this.p)).before - _chunkSMJGE7HMcjs.videoClipOverhang.call(void 0, track, R).lead));
1187
+ const hi = Math.min(R.duration - this.frame, _chunkSMJGE7HMcjs.videoClipHandles.call(void 0, L, assets, _chunkSMJGE7HMcjs.videoSequenceMap.call(void 0, this.p)).after - _chunkSMJGE7HMcjs.videoClipOverhang.call(void 0, track, L).tail);
2020
1188
  const delta = this.clamp(
2021
1189
  e.delta,
2022
1190
  lo,
@@ -2032,18 +1200,18 @@ var Editor = (_class2 = class {
2032
1200
  case "slide_clip": {
2033
1201
  const { track, clip } = this.clip(e.clip);
2034
1202
  this.unlocked(track);
2035
- const before = videoClipBefore(track, clip);
2036
- const after = videoClipAfter(track, clip);
1203
+ const before = _chunkSMJGE7HMcjs.videoClipBefore.call(void 0, track, clip);
1204
+ const after = _chunkSMJGE7HMcjs.videoClipAfter.call(void 0, track, clip);
2037
1205
  if (!before && !after) refuse(`"${clip.id}" has no clip right before or after it on track "${track.id}", so there is nothing to slide against; use move_clip.`);
2038
- const assets = videoAssetMap(this.p);
2039
- const end = videoClipEnd(clip);
1206
+ const assets = _chunkSMJGE7HMcjs.videoAssetMap.call(void 0, this.p);
1207
+ const end = _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, clip);
2040
1208
  const others = track.clips.filter((c) => c !== clip);
2041
1209
  const room = {
2042
- earlier: before ? before.duration - this.frame : clip.start - others.filter((c) => videoClipEnd(c) <= clip.start + VIDEO_TIME_EPSILON).reduce((m, c) => Math.max(m, videoClipEnd(c)), 0),
2043
- later: after ? after.duration - this.frame : others.filter((c) => c.start >= end - VIDEO_TIME_EPSILON).reduce((m, c) => Math.min(m, c.start - end), Infinity)
1210
+ earlier: before ? before.duration - this.frame : clip.start - others.filter((c) => _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, c) <= clip.start + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON).reduce((m, c) => Math.max(m, _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, c)), 0),
1211
+ later: after ? after.duration - this.frame : others.filter((c) => c.start >= end - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON).reduce((m, c) => Math.min(m, c.start - end), Infinity)
2044
1212
  };
2045
- const lo = -Math.min(room.earlier, after ? videoClipHandles(after, assets, videoSequenceMap(this.p)).before - videoClipOverhang(track, after).lead : Infinity);
2046
- const hi = Math.min(room.later, before ? videoClipHandles(before, assets, videoSequenceMap(this.p)).after - videoClipOverhang(track, before).tail : Infinity);
1213
+ const lo = -Math.min(room.earlier, after ? _chunkSMJGE7HMcjs.videoClipHandles.call(void 0, after, assets, _chunkSMJGE7HMcjs.videoSequenceMap.call(void 0, this.p)).before - _chunkSMJGE7HMcjs.videoClipOverhang.call(void 0, track, after).lead : Infinity);
1214
+ const hi = Math.min(room.later, before ? _chunkSMJGE7HMcjs.videoClipHandles.call(void 0, before, assets, _chunkSMJGE7HMcjs.videoSequenceMap.call(void 0, this.p)).after - _chunkSMJGE7HMcjs.videoClipOverhang.call(void 0, track, before).tail : Infinity);
2047
1215
  const delta = this.clamp(
2048
1216
  e.delta,
2049
1217
  lo,
@@ -2058,7 +1226,7 @@ var Editor = (_class2 = class {
2058
1226
  this.trimHead(after, delta);
2059
1227
  }
2060
1228
  track.clips.sort((a, b) => a.start - b.start);
2061
- return this.outcomes.push({ op: e.op, clip: clip.id, clips: [_optionalChain([before, 'optionalAccess', _29 => _29.id]), clip.id, _optionalChain([after, 'optionalAccess', _30 => _30.id])].filter((x) => !!x), track: track.id, delta });
1229
+ return this.outcomes.push({ op: e.op, clip: clip.id, clips: [_optionalChain([before, 'optionalAccess', _16 => _16.id]), clip.id, _optionalChain([after, 'optionalAccess', _17 => _17.id])].filter((x) => !!x), track: track.id, delta });
2062
1230
  }
2063
1231
  case "lift_range":
2064
1232
  case "extract_range": {
@@ -2076,29 +1244,29 @@ var Editor = (_class2 = class {
2076
1244
  }
2077
1245
  case "split_at": {
2078
1246
  const tracks = this.rangeTracks(e.tracks);
2079
- const lefts = tracks.flatMap((t) => t.clips.filter((c) => c.start < e.at - VIDEO_TIME_EPSILON && videoClipEnd(c) > e.at + VIDEO_TIME_EPSILON).map((c) => c.id));
2080
- if (!lefts.length) refuse(`No clip runs across ${formatVideoSeconds(e.at)} on ${e.tracks ? `track${tracks.length > 1 ? "s" : ""} ${tracks.map((t) => `"${t.id}"`).join(", ")}` : "the unlocked tracks"}, so there is nothing to split there.`);
1247
+ const lefts = tracks.flatMap((t) => t.clips.filter((c) => c.start < e.at - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON && _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, c) > e.at + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON).map((c) => c.id));
1248
+ if (!lefts.length) refuse(`No clip runs across ${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, e.at)} on ${e.tracks ? `track${tracks.length > 1 ? "s" : ""} ${tracks.map((t) => `"${t.id}"`).join(", ")}` : "the unlocked tracks"}, so there is nothing to split there.`);
2081
1249
  const created = this.cutAcross(tracks, e.at).map((c) => c.id);
2082
1250
  return this.outcomes.push({ op: e.op, clips: lefts, created });
2083
1251
  }
2084
1252
  case "set_keyframes": {
2085
1253
  const { track, clip } = this.clip(e.clip);
2086
1254
  this.unlocked(track);
2087
- if (!VIDEO_KEYFRAME_PROPERTIES.includes(e.property)) refuse(`"${e.property}" cannot be keyframed; use one of ${VIDEO_KEYFRAME_PROPERTIES.join(", ")}.`);
1255
+ if (!_chunkSMJGE7HMcjs.VIDEO_KEYFRAME_PROPERTIES.includes(e.property)) refuse(`"${e.property}" cannot be keyframed; use one of ${_chunkSMJGE7HMcjs.VIDEO_KEYFRAME_PROPERTIES.join(", ")}.`);
2088
1256
  if (e.property === "volume" && clip.type !== "media" && clip.type !== "sequence") refuse(`Clip "${clip.id}" is a ${clip.type} clip and has no volume; volume keys go on media and compound clips.`);
2089
- const limits = VIDEO_KEYFRAME_LIMITS[e.property];
2090
- const transform = VIDEO_TRANSFORM_PROPERTIES.includes(e.property);
1257
+ const limits = _chunkSMJGE7HMcjs.VIDEO_KEYFRAME_LIMITS[e.property];
1258
+ const transform = _chunkSMJGE7HMcjs.VIDEO_TRANSFORM_PROPERTIES.includes(e.property);
2091
1259
  const c = clip;
2092
- const current = transform ? _optionalChain([c, 'access', _31 => _31.transform, 'optionalAccess', _32 => _32[e.property]]) : c[e.property];
1260
+ const current = transform ? _optionalChain([c, 'access', _18 => _18.transform, 'optionalAccess', _19 => _19[e.property]]) : c[e.property];
2093
1261
  let value;
2094
1262
  if (e.keys === null) {
2095
- const v = videoValueAt(current, 0, limits.default);
2096
- value = isVideoKeyframes(current) ? v === limits.default ? void 0 : v : current;
1263
+ const v = _chunkSMJGE7HMcjs.videoValueAt.call(void 0, current, 0, limits.default);
1264
+ value = _chunkSMJGE7HMcjs.isVideoKeyframes.call(void 0, current) ? v === limits.default ? void 0 : v : current;
2097
1265
  } else {
2098
1266
  if (!Array.isArray(e.keys) || !e.keys.length) refuse('"keys" must list at least one key { t, v }; pass null to remove the keyframes.');
2099
1267
  let sorted = [];
2100
1268
  try {
2101
- sorted = sortVideoKeys(e.keys);
1269
+ sorted = _chunkSMJGE7HMcjs.sortVideoKeys.call(void 0, e.keys);
2102
1270
  } catch (error) {
2103
1271
  refuse(error.message);
2104
1272
  }
@@ -2117,12 +1285,12 @@ var Editor = (_class2 = class {
2117
1285
  this.unlocked(track);
2118
1286
  let at = 0;
2119
1287
  const moved = [];
2120
- for (const clip of sortedVideoClips(track)) {
2121
- if (clip.start >= (_nullishCoalesce(e.from, () => ( 0))) - VIDEO_TIME_EPSILON && clip.start > at + VIDEO_TIME_EPSILON) {
1288
+ for (const clip of _chunkSMJGE7HMcjs.sortedVideoClips.call(void 0, track)) {
1289
+ if (clip.start >= (_nullishCoalesce(e.from, () => ( 0))) - _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON && clip.start > at + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) {
2122
1290
  clip.start = Math.max(at, _nullishCoalesce(e.from, () => ( 0)));
2123
1291
  moved.push(clip.id);
2124
1292
  }
2125
- at = Math.max(at, videoClipEnd(clip));
1293
+ at = Math.max(at, _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, clip));
2126
1294
  }
2127
1295
  return this.outcomes.push({ op: e.op, track: track.id, clips: moved });
2128
1296
  }
@@ -2160,7 +1328,7 @@ var Editor = (_class2 = class {
2160
1328
  refuse(`Unknown op ${_chunkWAJQKWQUcjs.namedWithSuggestion.call(void 0, String(e.op), VIDEO_EDIT_OPS)}; the ops are ${VIDEO_EDIT_OPS.join(", ")}.`);
2161
1329
  }
2162
1330
  }
2163
- }, _class2);
1331
+ }, _class);
2164
1332
  function applyVideoEdits(project, edits) {
2165
1333
  const list = Array.isArray(edits) ? edits : [edits];
2166
1334
  if (!list.length) return { ok: true, project, issues: checkVideoProject(project), outcomes: [] };
@@ -2171,7 +1339,7 @@ function applyVideoEdits(project, edits) {
2171
1339
  editor.run(edit);
2172
1340
  } catch (error) {
2173
1341
  const message = error.message;
2174
- return { ok: false, error: `${list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _33 => _33.op])}): ` : ""}${message}${/Nothing was changed\.$/.test(message) ? "" : " Nothing was changed."}` };
1342
+ return { ok: false, error: `${list.length > 1 ? `op ${i + 1} (${_optionalChain([edit, 'optionalAccess', _20 => _20.op])}): ` : ""}${message}${/Nothing was changed\.$/.test(message) ? "" : " Nothing was changed."}` };
2175
1343
  }
2176
1344
  }
2177
1345
  let next;
@@ -2188,169 +1356,11 @@ function applyVideoEdits(project, edits) {
2188
1356
  return { ok: true, project: next, issues, outcomes: editor.outcomes };
2189
1357
  }
2190
1358
 
2191
- // src/video-editor/evaluate.ts
2192
- function videoTransformAt(transform, clipTime = 0) {
2193
- return { x: videoValueAt(_optionalChain([transform, 'optionalAccess', _34 => _34.x]), clipTime, 0), y: videoValueAt(_optionalChain([transform, 'optionalAccess', _35 => _35.y]), clipTime, 0), scale: videoValueAt(_optionalChain([transform, 'optionalAccess', _36 => _36.scale]), clipTime, 1), rotation: videoValueAt(_optionalChain([transform, 'optionalAccess', _37 => _37.rotation]), clipTime, 0) };
2194
- }
2195
- function placeVideoRect(frame, size, fit = "contain", transform = {}, clipTime = 0) {
2196
- let w = size.width;
2197
- let h = size.height;
2198
- if (fit === "fill") {
2199
- w = frame.width;
2200
- h = frame.height;
2201
- } else if (fit !== "none") {
2202
- const k = fit === "cover" ? Math.max(frame.width / w, frame.height / h) : Math.min(frame.width / w, frame.height / h);
2203
- w *= k;
2204
- h *= k;
2205
- }
2206
- const t = videoTransformAt(transform, clipTime);
2207
- w *= t.scale;
2208
- h *= t.scale;
2209
- const cx = frame.width / 2 + t.x;
2210
- const cy = frame.height / 2 + t.y;
2211
- return { x: cx - w / 2, y: cy - h / 2, width: w, height: h };
2212
- }
2213
- function videoTextStyle(clip) {
2214
- return { text: clip.text, color: _nullishCoalesce(clip.color, () => ( "#ffffff")), fontSize: _nullishCoalesce(clip.fontSize, () => ( 64)), fontFamily: _nullishCoalesce(clip.fontFamily, () => ( "sans-serif")), fontWeight: _nullishCoalesce(clip.fontWeight, () => ( 700)), ...clip.background ? { background: clip.background } : {} };
2215
- }
2216
- function videoTitleFrame(project, clip, clipTime = 0) {
2217
- const layer = { clip: clip.id, track: "", type: "text", opacity: 1, rect: placeVideoRect(project, project, "fill", clip.transform, clipTime), rotation: videoTransformAt(clip.transform, clipTime).rotation, text: videoTextStyle(clip) };
2218
- return { time: clip.start, width: project.width, height: project.height, background: "rgba(0,0,0,0)", layers: [layer], audio: [] };
2219
- }
2220
- var clamp012 = (v) => Math.min(1, Math.max(0, v));
2221
- var videoTransitionEase = (progress) => (1 - Math.cos(Math.PI * clamp012(progress))) / 2;
2222
- function videoFadeCurveGain(curve, x) {
2223
- const t = clamp012(x);
2224
- switch (curve) {
2225
- case "equal-power":
2226
- return Math.sin(t * Math.PI / 2);
2227
- case "s-curve":
2228
- return (1 - Math.cos(t * Math.PI)) / 2;
2229
- case "exponential":
2230
- return t * t * t;
2231
- case "logarithmic":
2232
- return 1 - (1 - t) * (1 - t);
2233
- default:
2234
- return t;
2235
- }
2236
- }
2237
- function fadeGain(clip, time, sound = false) {
2238
- let g = 1;
2239
- const shape = sound && (clip.type === "media" || clip.type === "sequence") ? clip : void 0;
2240
- if (clip.fadeIn) g *= videoFadeCurveGain(_optionalChain([shape, 'optionalAccess', _38 => _38.fadeInCurve]), (time - clip.start) / clip.fadeIn);
2241
- if (clip.fadeOut) g *= videoFadeCurveGain(_optionalChain([shape, 'optionalAccess', _39 => _39.fadeOutCurve]), (videoClipEnd(clip) - time) / clip.fadeOut);
2242
- return g;
2243
- }
2244
- function videoClipSoundGain(clip, clipTime) {
2245
- const volume = clip.type === "media" || clip.type === "sequence" ? videoValueAt(clip.volume, clipTime, 1) : 1;
2246
- return volume * fadeGain(clip, clip.start + Math.min(Math.max(clipTime, 0), clip.duration), true);
2247
- }
2248
- function activeOnTrack(track, time) {
2249
- const out = [];
2250
- for (const clip of sortedVideoClips(track)) {
2251
- const before = clip.transition ? videoClipBefore(track, clip) : null;
2252
- const lead = before ? clip.transition.duration / 2 : 0;
2253
- const next = videoClipAfter(track, clip);
2254
- const tail = _optionalChain([next, 'optionalAccess', _40 => _40.transition]) ? next.transition.duration / 2 : 0;
2255
- if (time < clip.start - lead || time >= videoClipEnd(clip) + tail) continue;
2256
- const a = { clip };
2257
- if (lead && time < clip.start + lead) a.incoming = { progress: clamp012((time - (clip.start - lead)) / (2 * lead)), type: clip.transition.type, transition: clip.transition };
2258
- if (tail && time >= videoClipEnd(clip) - tail) a.outgoing = { progress: clamp012((time - (videoClipEnd(clip) - tail)) / (2 * tail)), type: next.transition.type, next };
2259
- out.push(a);
2260
- }
2261
- return out;
2262
- }
2263
- var sourceTimeAt = (clip, time) => clip.type === "media" ? videoClipSourceTime(clip, time - clip.start) : 0;
2264
- function videoFrameAt(project, time) {
2265
- const { layers, audio } = tracksAt(project, project.tracks, time, { assets: videoAssetMap(project), sequences: videoSequenceMap(project), within: [], prefix: "" });
2266
- return { time, width: project.width, height: project.height, background: _nullishCoalesce(project.background, () => ( "#000000")), layers, audio };
2267
- }
2268
- function tracksAt(project, tracks, time, scope) {
2269
- const { assets, prefix } = scope;
2270
- const layers = [];
2271
- const audio = [];
2272
- for (let ti = tracks.length - 1; ti >= 0; ti--) {
2273
- const track = tracks[ti];
2274
- for (const { clip, incoming, outgoing } of activeOnTrack(track, time)) {
2275
- if (_optionalChain([incoming, 'optionalAccess', _41 => _41.type]) === "dip" && incoming.progress < 0.5) continue;
2276
- if (_optionalChain([outgoing, 'optionalAccess', _42 => _42.type]) === "dip" && outgoing.progress >= 0.5) continue;
2277
- const inside = Math.min(Math.max(time, clip.start), videoClipEnd(clip));
2278
- const fade = fadeGain(clip, inside);
2279
- const level = (t, transition, into) => videoFadeCurveGain(videoTransitionAudioCurve(transition), t.type === "dip" ? Math.abs(1 - 2 * t.progress) : into ? t.progress : 1 - t.progress);
2280
- const mix = (incoming ? level(incoming, incoming.transition, true) : 1) * (outgoing ? level(outgoing, outgoing.next.transition, false) : 1);
2281
- const asset = clip.type === "media" ? assets.get(clip.asset) : void 0;
2282
- const sourceTime = clip.type === "sequence" ? (_nullishCoalesce(clip.in, () => ( 0))) + time - clip.start : sourceTimeAt(clip, time);
2283
- const clipTime = time - clip.start;
2284
- let nested = null;
2285
- if (clip.type === "sequence") {
2286
- const seq = scope.sequences.get(clip.sequence);
2287
- if (!seq || scope.within.includes(seq.id)) continue;
2288
- nested = tracksAt(project, seq.tracks, sourceTime, { ...scope, within: [...scope.within, seq.id], prefix: `${prefix}${clip.id}/` });
2289
- }
2290
- const audible = videoAssetHasSound(asset);
2291
- if (audible && clip.type === "media" && !clip.muted && !track.muted) {
2292
- const gain = videoValueAt(clip.volume, clipTime, 1) * (_nullishCoalesce(track.volume, () => ( 1))) * fadeGain(clip, inside, true) * mix;
2293
- audio.push({ clip: prefix + clip.id, track: track.id, asset: asset.id, src: asset.src, sourceTime, gain, speed: videoClipSpeedAt(clip, clipTime) });
2294
- }
2295
- if (nested && clip.type === "sequence" && !clip.muted && !track.muted) {
2296
- const gain = videoValueAt(clip.volume, clipTime, 1) * (_nullishCoalesce(track.volume, () => ( 1))) * fadeGain(clip, inside, true) * mix;
2297
- for (const a of nested.audio) audio.push({ ...a, gain: a.gain * gain });
2298
- }
2299
- if (track.kind !== "video" || track.hidden) continue;
2300
- if (clip.type === "media" && (!asset || asset.kind === "audio")) continue;
2301
- if (nested && !nested.layers.length) continue;
2302
- const shown = videoClipAnimated(clip, project);
2303
- let opacity = videoValueAt(shown.opacity, clipTime, 1) * fade;
2304
- let wipe;
2305
- let dip = null;
2306
- let iris;
2307
- let shift = 0;
2308
- let shiftDirection = "right";
2309
- if (incoming) {
2310
- if (incoming.type === "crossfade") opacity *= incoming.progress;
2311
- else if (incoming.type === "wipe") wipe = { direction: _nullishCoalesce(incoming.transition.direction, () => ( "right")), progress: incoming.progress };
2312
- else if (incoming.type === "iris") iris = { progress: incoming.progress };
2313
- else if (incoming.type === "push" || incoming.type === "slide") {
2314
- shiftDirection = _nullishCoalesce(incoming.transition.direction, () => ( "right"));
2315
- shift = videoTransitionEase(incoming.progress) - 1;
2316
- } else dip = { color: _nullishCoalesce(incoming.transition.color, () => ( "#000000")), opacity: (1 - incoming.progress) * 2 };
2317
- }
2318
- if (_optionalChain([outgoing, 'optionalAccess', _43 => _43.type]) === "dip") dip = { color: _nullishCoalesce(outgoing.next.transition.color, () => ( "#000000")), opacity: outgoing.progress * 2 };
2319
- if (_optionalChain([outgoing, 'optionalAccess', _44 => _44.type]) === "push") {
2320
- shiftDirection = _nullishCoalesce(outgoing.next.transition.direction, () => ( "right"));
2321
- shift = videoTransitionEase(outgoing.progress);
2322
- }
2323
- const size = clip.type === "media" ? { width: _nullishCoalesce(asset.width, () => ( project.width)), height: _nullishCoalesce(asset.height, () => ( project.height)) } : { width: project.width, height: project.height };
2324
- const fit = _nullishCoalesce(clip.fit, () => ( (clip.type === "media" || clip.type === "sequence" ? "contain" : "fill")));
2325
- const placed = placeVideoRect(project, size, clip.type === "text" ? "fill" : fit, shown.transform, clipTime);
2326
- const across = shiftDirection === "left" || shiftDirection === "right";
2327
- const sign = shiftDirection === "left" || shiftDirection === "up" ? -1 : 1;
2328
- const rect = shift ? { ...placed, ...across ? { x: placed.x + sign * shift * project.width } : { y: placed.y + sign * shift * project.height } } : placed;
2329
- const layer = { clip: prefix + clip.id, track: track.id, type: clip.type, opacity: clamp012(opacity), rect, rotation: videoValueAt(_optionalChain([shown, 'access', _45 => _45.transform, 'optionalAccess', _46 => _46.rotation]), clipTime, 0) };
2330
- if (wipe) layer.wipe = wipe;
2331
- if (iris) layer.iris = iris;
2332
- if (clip.blend && clip.blend !== "normal") layer.blend = clip.blend;
2333
- const grade = normalizeVideoGrade(clip.grade);
2334
- if (grade) layer.grade = grade;
2335
- if (clip.type === "media") Object.assign(layer, { asset: asset.id, assetKind: asset.kind, src: asset.src, sourceTime });
2336
- if (clip.type === "sequence") Object.assign(layer, { sequence: clip.sequence, sourceTime, layers: nested.layers });
2337
- if (clip.type === "text") layer.text = videoTextStyle(clip);
2338
- if (clip.type === "color") layer.color = clip.color;
2339
- if (layer.opacity > 0) layers.push(layer);
2340
- if (dip && dip.opacity > 0) layers.push({ clip: `transition:${prefix}${incoming ? clip.id : outgoing.next.id}`, track: track.id, type: "color", color: dip.color, opacity: clamp012(dip.opacity), rect: { x: 0, y: 0, width: project.width, height: project.height }, rotation: 0 });
2341
- }
2342
- }
2343
- return { layers, audio };
2344
- }
2345
- function videoFrameLeafLayers(layers) {
2346
- return layers.flatMap((l) => l.layers ? videoFrameLeafLayers(l.layers) : [l]);
2347
- }
2348
-
2349
1359
  // src/video-editor/audio.ts
2350
1360
  var VIDEO_PEAK_BLOCK = 32;
2351
1361
  function buildVideoAudioPeaks(channels, sampleRate, blockSize = VIDEO_PEAK_BLOCK) {
2352
1362
  if (!(sampleRate > 0)) throw new Error("The sample rate must be above 0.");
2353
- const length = channels.reduce((n2, c) => Math.max(n2, c.length), 0);
1363
+ const length = channels.reduce((n, c) => Math.max(n, c.length), 0);
2354
1364
  const blocks = Math.max(1, Math.ceil(length / blockSize));
2355
1365
  const level0 = new Float32Array(blocks * 2);
2356
1366
  for (let b = 0; b < blocks; b++) {
@@ -2371,12 +1381,12 @@ function buildVideoAudioPeaks(channels, sampleRate, blockSize = VIDEO_PEAK_BLOCK
2371
1381
  const levels = [level0];
2372
1382
  while (levels[levels.length - 1].length > 2) {
2373
1383
  const below = levels[levels.length - 1];
2374
- const n2 = below.length / 2;
2375
- const up = new Float32Array(Math.ceil(n2 / 4) * 2);
1384
+ const n = below.length / 2;
1385
+ const up = new Float32Array(Math.ceil(n / 4) * 2);
2376
1386
  for (let j = 0; j < up.length / 2; j++) {
2377
1387
  let lo = Infinity;
2378
1388
  let hi = -Infinity;
2379
- for (let i = 4 * j; i < Math.min(n2, 4 * j + 4); i++) {
1389
+ for (let i = 4 * j; i < Math.min(n, 4 * j + 4); i++) {
2380
1390
  lo = Math.min(lo, below[2 * i]);
2381
1391
  hi = Math.max(hi, below[2 * i + 1]);
2382
1392
  }
@@ -2417,10 +1427,10 @@ function videoAudioPeakRange(peaks, from, to) {
2417
1427
  return min === Infinity ? { min: 0, max: 0 } : { min, max };
2418
1428
  }
2419
1429
  function videoAudioPeakSpans(peaks, from, to, columns) {
2420
- const n2 = Math.max(0, Math.floor(columns));
2421
- const out = new Float32Array(n2 * 2);
2422
- const step = (to - from) / Math.max(1, n2);
2423
- for (let c = 0; c < n2; c++) {
1430
+ const n = Math.max(0, Math.floor(columns));
1431
+ const out = new Float32Array(n * 2);
1432
+ const step = (to - from) / Math.max(1, n);
1433
+ for (let c = 0; c < n; c++) {
2424
1434
  const { min, max } = videoAudioPeakRange(peaks, from + c * step, from + (c + 1) * step);
2425
1435
  out[2 * c] = min;
2426
1436
  out[2 * c + 1] = max;
@@ -2428,30 +1438,30 @@ function videoAudioPeakSpans(peaks, from, to, columns) {
2428
1438
  return out;
2429
1439
  }
2430
1440
  function videoClipWaveform(clip, peaks, columns) {
2431
- const n2 = Math.max(0, Math.floor(columns));
2432
- const out = new Float32Array(n2 * 2);
1441
+ const n = Math.max(0, Math.floor(columns));
1442
+ const out = new Float32Array(n * 2);
2433
1443
  const speed = _nullishCoalesce(clip.speed, () => ( 1));
2434
1444
  const start = _nullishCoalesce(clip.in, () => ( 0));
2435
- const step = clip.duration / Math.max(1, n2);
2436
- for (let c = 0; c < n2; c++) {
1445
+ const step = clip.duration / Math.max(1, n);
1446
+ for (let c = 0; c < n; c++) {
2437
1447
  const { min, max } = videoAudioPeakRange(peaks, start + c * step * speed, start + (c + 1) * step * speed);
2438
- const gain = videoClipSoundGain(clip, (c + 0.5) * step);
1448
+ const gain = _chunkSMJGE7HMcjs.videoClipSoundGain.call(void 0, clip, (c + 0.5) * step);
2439
1449
  out[2 * c] = min * gain;
2440
1450
  out[2 * c + 1] = max * gain;
2441
1451
  }
2442
1452
  return out;
2443
1453
  }
2444
1454
  function videoWaveformPath(spans, width, height) {
2445
- const n2 = spans.length / 2;
2446
- if (!n2 || width <= 0 || height <= 0) return "";
1455
+ const n = spans.length / 2;
1456
+ if (!n || width <= 0 || height <= 0) return "";
2447
1457
  const mid = height / 2;
2448
1458
  const y = (v) => (mid - Math.max(-1, Math.min(1, v)) * mid).toFixed(1);
2449
- const x = (i) => ((i + 0.5) * (width / n2)).toFixed(1);
1459
+ const x = (i) => ((i + 0.5) * (width / n)).toFixed(1);
2450
1460
  const floor = 0.5 / mid;
2451
1461
  const top = [];
2452
1462
  const bottom = [];
2453
- for (let i = 0; i < n2; i++) top.push(`${i ? "L" : "M"}${x(i)} ${y(Math.max(spans[2 * i + 1], floor))}`);
2454
- for (let i = n2 - 1; i >= 0; i--) bottom.push(`L${x(i)} ${y(Math.min(spans[2 * i], -floor))}`);
1463
+ for (let i = 0; i < n; i++) top.push(`${i ? "L" : "M"}${x(i)} ${y(Math.max(spans[2 * i + 1], floor))}`);
1464
+ for (let i = n - 1; i >= 0; i--) bottom.push(`L${x(i)} ${y(Math.min(spans[2 * i], -floor))}`);
2455
1465
  return `${top.join("")}${bottom.join("")}Z`;
2456
1466
  }
2457
1467
  function videoClipWaveformPath(clip, peaks, width, height) {
@@ -2497,15 +1507,15 @@ async function decodeVideoAudioPeaks(src, options = {}) {
2497
1507
  }
2498
1508
  return buildVideoAudioPeaks(Array.from({ length: buffer.numberOfChannels }, (_, i) => buffer.getChannelData(i)), buffer.sampleRate);
2499
1509
  }
2500
- var VideoAudioPeaksCache = (_class3 = class {
2501
- constructor(loader = (asset) => decodeVideoAudioPeaks(asset.src)) {;_class3.prototype.__init6.call(this);_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);
1510
+ var VideoAudioPeaksCache = (_class2 = class {
1511
+ constructor(loader = (asset) => decodeVideoAudioPeaks(asset.src)) {;_class2.prototype.__init2.call(this);_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);
2502
1512
  this.loader = loader;
2503
1513
  }
2504
1514
 
2505
- __init6() {this.ready = /* @__PURE__ */ new Map()}
2506
- __init7() {this.pending = /* @__PURE__ */ new Map()}
2507
- __init8() {this.failed = /* @__PURE__ */ new Map()}
2508
- __init9() {this.listeners = /* @__PURE__ */ new Set()}
1515
+ __init2() {this.ready = /* @__PURE__ */ new Map()}
1516
+ __init3() {this.pending = /* @__PURE__ */ new Map()}
1517
+ __init4() {this.failed = /* @__PURE__ */ new Map()}
1518
+ __init5() {this.listeners = /* @__PURE__ */ new Set()}
2509
1519
  /** The peaks if ready, without starting to load them. */
2510
1520
  peek(asset) {
2511
1521
  return this.ready.get(asset.src);
@@ -2561,7 +1571,7 @@ var VideoAudioPeaksCache = (_class3 = class {
2561
1571
  emit() {
2562
1572
  for (const fn of this.listeners) fn();
2563
1573
  }
2564
- }, _class3);
1574
+ }, _class2);
2565
1575
 
2566
1576
  // src/video-editor/scopes.ts
2567
1577
  var VIDEO_SCOPE_KINDS = ["histogram", "waveform", "vectorscope"];
@@ -2708,7 +1718,7 @@ function summarizeVideoScopes(pixels, options = {}) {
2708
1718
  const w = videoLumaWaveform(pixels, { ...options, columns, levels: 256 });
2709
1719
  const waveform = Array.from({ length: w.columns }, (_, c) => {
2710
1720
  const counts = w.data.subarray(c * 256, c * 256 + 256);
2711
- const n2 = counts.reduce((a, b) => a + b, 0);
1721
+ const n = counts.reduce((a, b) => a + b, 0);
2712
1722
  let lo = 0;
2713
1723
  while (lo < 255 && !counts[lo]) lo++;
2714
1724
  let hi = 255;
@@ -2717,7 +1727,7 @@ function summarizeVideoScopes(pixels, options = {}) {
2717
1727
  let median = 0;
2718
1728
  for (let i = 0; i < 256; i++) {
2719
1729
  seen += counts[i];
2720
- if (seen >= n2 / 2 && n2) {
1730
+ if (seen >= n / 2 && n) {
2721
1731
  median = i;
2722
1732
  break;
2723
1733
  }
@@ -2847,10 +1857,10 @@ function videoScopeImage(kind, pixels, width = 256, height = 128, options = {})
2847
1857
  const size = Math.min(width, height);
2848
1858
  return videoVectorscopeImage(videoVectorscope(pixels, { ...options, size }), size);
2849
1859
  }
2850
- var VideoPreviewPixels = (_class4 = class {constructor() { _class4.prototype.__init10.call(this);_class4.prototype.__init11.call(this);_class4.prototype.__init12.call(this); }
2851
- __init10() {this.reader = null}
2852
- __init11() {this.drawnAt = null}
2853
- __init12() {this.listeners = /* @__PURE__ */ new Set()}
1860
+ var VideoPreviewPixels = (_class3 = class {constructor() { _class3.prototype.__init6.call(this);_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this); }
1861
+ __init6() {this.reader = null}
1862
+ __init7() {this.drawnAt = null}
1863
+ __init8() {this.listeners = /* @__PURE__ */ new Set()}
2854
1864
  /** Gives the canvas to read (the latest preview wins). Returns a function that takes it away again. */
2855
1865
  attach(reader) {
2856
1866
  this.reader = reader;
@@ -2918,7 +1928,7 @@ var VideoPreviewPixels = (_class4 = class {constructor() { _class4.prototype.__i
2918
1928
  emit() {
2919
1929
  for (const l of [...this.listeners]) l();
2920
1930
  }
2921
- }, _class4);
1931
+ }, _class3);
2922
1932
  function readVideoScopesPanel(feed, kinds = VIDEO_SCOPE_KINDS, options = {}) {
2923
1933
  let snap;
2924
1934
  try {
@@ -3000,24 +2010,24 @@ function videoTranscriptTrack(project, track) {
3000
2010
  if (!project.tracks.some((t) => t.id === track)) throw new Error(`There is no track "${track}"; the tracks are ${project.tracks.map((t) => `"${t.id}"`).join(", ")}.`);
3001
2011
  return track;
3002
2012
  }
3003
- const assets = videoAssetMap(project);
3004
- const transcribed = (t) => t.clips.some((c) => c.type === "media" && _optionalChain([assets, 'access', _47 => _47.get, 'call', _48 => _48(c.asset), 'optionalAccess', _49 => _49.transcript, 'optionalAccess', _50 => _50.length]));
3005
- return _nullishCoalesce(_optionalChain([(_nullishCoalesce(project.tracks.find((t) => t.kind === "audio" && transcribed(t)), () => ( project.tracks.find(transcribed)))), 'optionalAccess', _51 => _51.id]), () => ( null));
2013
+ const assets = _chunkSMJGE7HMcjs.videoAssetMap.call(void 0, project);
2014
+ const transcribed = (t) => t.clips.some((c) => c.type === "media" && _optionalChain([assets, 'access', _21 => _21.get, 'call', _22 => _22(c.asset), 'optionalAccess', _23 => _23.transcript, 'optionalAccess', _24 => _24.length]));
2015
+ return _nullishCoalesce(_optionalChain([(_nullishCoalesce(project.tracks.find((t) => t.kind === "audio" && transcribed(t)), () => ( project.tracks.find(transcribed)))), 'optionalAccess', _25 => _25.id]), () => ( null));
3006
2016
  }
3007
2017
  function videoTimelineTranscript(project, options = {}) {
3008
2018
  const id = videoTranscriptTrack(project, options.track);
3009
2019
  if (!id) return [];
3010
2020
  const track = project.tracks.find((t) => t.id === id);
3011
- const assets = videoAssetMap(project);
2021
+ const assets = _chunkSMJGE7HMcjs.videoAssetMap.call(void 0, project);
3012
2022
  const out = [];
3013
- for (const clip of sortedVideoClips(track)) {
2023
+ for (const clip of _chunkSMJGE7HMcjs.sortedVideoClips.call(void 0, track)) {
3014
2024
  if (clip.type !== "media") continue;
3015
- const words = _optionalChain([assets, 'access', _52 => _52.get, 'call', _53 => _53(clip.asset), 'optionalAccess', _54 => _54.transcript]);
3016
- if (!_optionalChain([words, 'optionalAccess', _55 => _55.length])) continue;
3017
- const speed = videoClipSpeed(clip);
2025
+ const words = _optionalChain([assets, 'access', _26 => _26.get, 'call', _27 => _27(clip.asset), 'optionalAccess', _28 => _28.transcript]);
2026
+ if (!_optionalChain([words, 'optionalAccess', _29 => _29.length])) continue;
2027
+ const speed = _chunkSMJGE7HMcjs.videoClipSpeed.call(void 0, clip);
3018
2028
  const from = _nullishCoalesce(clip.in, () => ( 0));
3019
2029
  const to = from + clip.duration * speed;
3020
- const toTimeline = (s) => Math.min(videoClipEnd(clip), Math.max(clip.start, clip.start + (s - from) / speed));
2030
+ const toTimeline = (s) => Math.min(_chunkSMJGE7HMcjs.videoClipEnd.call(void 0, clip), Math.max(clip.start, clip.start + (s - from) / speed));
3021
2031
  words.forEach((w, index) => {
3022
2032
  const mid = (w.start + w.end) / 2;
3023
2033
  if (mid < from || mid >= to) return;
@@ -3027,7 +2037,7 @@ function videoTimelineTranscript(project, options = {}) {
3027
2037
  return out;
3028
2038
  }
3029
2039
  var normalizeVideoWord = (word) => word.toLowerCase().replace(/^[^\p{L}\p{N}']+|[^\p{L}\p{N}']+$/gu, "");
3030
- var rangeOf = (words, i, n2) => ({ from: words[i].start, to: words[i + n2 - 1].end, text: words.slice(i, i + n2).map((w) => w.word).join(" ") });
2040
+ var rangeOf = (words, i, n) => ({ from: words[i].start, to: words[i + n - 1].end, text: words.slice(i, i + n).map((w) => w.word).join(" ") });
3031
2041
  function findVideoTranscriptText(words, query) {
3032
2042
  const q = query.split(/\s+/).map(normalizeVideoWord).filter(Boolean);
3033
2043
  if (!q.length) throw new Error("Search for at least one word.");
@@ -3062,19 +2072,19 @@ function findVideoTranscriptPauses(words, options = {}) {
3062
2072
  let to = b.start - keep;
3063
2073
  if (options.fps) {
3064
2074
  const f = 1e3 / options.fps;
3065
- from = snapToVideoFrame(Math.ceil(from / f - 1e-9) * f, options.fps);
3066
- to = snapToVideoFrame(Math.floor(to / f + 1e-9) * f, options.fps);
2075
+ from = _chunkSMJGE7HMcjs.snapToVideoFrame.call(void 0, Math.ceil(from / f - 1e-9) * f, options.fps);
2076
+ to = _chunkSMJGE7HMcjs.snapToVideoFrame.call(void 0, Math.floor(to / f + 1e-9) * f, options.fps);
3067
2077
  }
3068
- if (to - from > VIDEO_TIME_EPSILON) out.push({ from, to, text: `(pause after "${a.word}")` });
2078
+ if (to - from > _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) out.push({ from, to, text: `(pause after "${a.word}")` });
3069
2079
  }
3070
2080
  return out;
3071
2081
  }
3072
2082
  function mergeVideoRanges(ranges) {
3073
- const sorted = ranges.filter((r) => r.to - r.from > VIDEO_TIME_EPSILON).map((r) => ({ from: r.from, to: r.to })).sort((a, b) => a.from - b.from);
2083
+ const sorted = ranges.filter((r) => r.to - r.from > _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON).map((r) => ({ from: r.from, to: r.to })).sort((a, b) => a.from - b.from);
3074
2084
  const out = [];
3075
2085
  for (const r of sorted) {
3076
2086
  const last = out[out.length - 1];
3077
- if (last && r.from <= last.to + VIDEO_TIME_EPSILON) last.to = Math.max(last.to, r.to);
2087
+ if (last && r.from <= last.to + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON) last.to = Math.max(last.to, r.to);
3078
2088
  else out.push(r);
3079
2089
  }
3080
2090
  return out;
@@ -3101,688 +2111,15 @@ function videoWordAt(words, time) {
3101
2111
  return -1;
3102
2112
  }
3103
2113
 
3104
- // src/video-editor/render.ts
3105
- var esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
3106
- var n = (v) => String(Math.round(v * 100) / 100);
3107
- var n4 = (v) => String(Math.round(v * 1e4) / 1e4);
3108
- var videoIrisRadius = (frame, progress) => progress * Math.hypot(frame.width / 2, frame.height / 2);
3109
- function videoWipeRect(rect, wipe) {
3110
- const { direction, progress } = wipe;
3111
- const w = rect.width * progress;
3112
- const h = rect.height * progress;
3113
- if (direction === "right") return { ...rect, width: w };
3114
- if (direction === "left") return { ...rect, x: rect.x + rect.width - w, width: w };
3115
- if (direction === "down") return { ...rect, height: h };
3116
- return { ...rect, y: rect.y + rect.height - h, height: h };
3117
- }
3118
- function videoFrameToSvg(frame, options = {}, assets = []) {
3119
- const defs = [];
3120
- const posters = new Map(assets.map((a) => [a.id, a.poster]));
3121
- const draw = (layers, prefix) => layers.map((layer, index) => {
3122
- const i = `${prefix}${index}`;
3123
- const { rect } = layer;
3124
- const attrs = [];
3125
- const style = [];
3126
- if (layer.opacity < 1) attrs.push(`opacity="${n(layer.opacity)}"`);
3127
- if (layer.blend) style.push(`mix-blend-mode:${layer.blend === "add" ? "plus-lighter" : layer.blend}`);
3128
- if (layer.layers) style.push("isolation:isolate");
3129
- if (style.length) attrs.push(`style="${style.join(";")}"`);
3130
- if (layer.grade) {
3131
- defs.push(videoGradeSvgFilter(`g${i}`, layer.grade, layer.grade.lut ? _optionalChain([options, 'access', _56 => _56.lutFor, 'optionalCall', _57 => _57(layer.grade.lut.src)]) : null));
3132
- attrs.push(`filter="url(#g${i})"`);
3133
- }
3134
- if (layer.rotation) attrs.push(`transform="rotate(${n(layer.rotation)} ${n(rect.x + rect.width / 2)} ${n(rect.y + rect.height / 2)})"`);
3135
- if (layer.wipe) {
3136
- const r = videoWipeRect(rect, layer.wipe);
3137
- defs.push(`<clipPath id="w${i}"><rect x="${n(r.x)}" y="${n(r.y)}" width="${n(r.width)}" height="${n(r.height)}"/></clipPath>`);
3138
- attrs.push(`clip-path="url(#w${i})"`);
3139
- }
3140
- const box = `x="${n(rect.x)}" y="${n(rect.y)}" width="${n(rect.width)}" height="${n(rect.height)}"`;
3141
- let inner = "";
3142
- if (layer.layers) {
3143
- inner = `<g transform="translate(${n(rect.x)} ${n(rect.y)}) scale(${n4(rect.width / frame.width)} ${n4(rect.height / frame.height)})">${draw(layer.layers, `${i}-`)}</g>`;
3144
- } else if (layer.type === "color") inner = `<rect ${box} fill="${esc(_nullishCoalesce(layer.color, () => ( "#000000")))}"/>`;
3145
- else if (layer.type === "text" && layer.text) {
3146
- const t = layer.text;
3147
- const lines = t.text.split("\n");
3148
- const lh = t.fontSize * 1.2;
3149
- const cx = rect.x + rect.width / 2;
3150
- const cy = rect.y + rect.height / 2;
3151
- const top = cy - lh * (lines.length - 1) / 2;
3152
- if (t.background) {
3153
- const w = Math.max(...lines.map((l) => l.length)) * t.fontSize * 0.6 + t.fontSize;
3154
- const h = lh * lines.length + t.fontSize * 0.4;
3155
- inner += `<rect x="${n(cx - w / 2)}" y="${n(cy - h / 2)}" width="${n(w)}" height="${n(h)}" fill="${esc(t.background)}"/>`;
3156
- }
3157
- inner += `<text x="${n(cx)}" text-anchor="middle" dominant-baseline="central" font-family="${esc(t.fontFamily)}" font-size="${n(t.fontSize)}" font-weight="${t.fontWeight}" fill="${esc(t.color)}">${lines.map((l, k) => `<tspan x="${n(cx)}" y="${n(top + k * lh)}">${esc(l)}</tspan>`).join("")}</text>`;
3158
- } else {
3159
- const href = options.imageFor ? options.imageFor(layer, frame) : layer.assetKind === "image" ? _nullishCoalesce(layer.src, () => ( null)) : _nullishCoalesce(posters.get(_nullishCoalesce(layer.asset, () => ( ""))), () => ( null));
3160
- if (href) inner = `<image href="${esc(href)}" ${box} preserveAspectRatio="none"/>`;
3161
- else {
3162
- const label = `${_nullishCoalesce(layer.asset, () => ( layer.clip))} \xB7 ${formatVideoTimecode(_nullishCoalesce(layer.sourceTime, () => ( 0)), _nullishCoalesce(options.fps, () => ( 30)))}`;
3163
- const size = Math.max(10, Math.min(rect.height / 8, rect.width / Math.max(8, label.length * 0.6)));
3164
- inner = `<rect ${box} fill="#334155" stroke="#94a3b8" stroke-width="2"/><text x="${n(rect.x + rect.width / 2)}" y="${n(rect.y + rect.height / 2)}" text-anchor="middle" dominant-baseline="central" font-family="sans-serif" font-size="${n(size)}" fill="#e2e8f0">${esc(label)}</text>`;
3165
- }
3166
- }
3167
- let item = attrs.length ? `<g ${attrs.join(" ")}>${inner}</g>` : inner;
3168
- if (layer.iris) {
3169
- defs.push(`<clipPath id="i${i}"><circle cx="${n(frame.width / 2)}" cy="${n(frame.height / 2)}" r="${n(videoIrisRadius(frame, layer.iris.progress))}"/></clipPath>`);
3170
- item = `<g clip-path="url(#i${i})">${item}</g>`;
3171
- }
3172
- return item;
3173
- }).join("");
3174
- const body = draw(frame.layers, "");
3175
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${n(frame.width)} ${n(frame.height)}" width="${n(frame.width)}" height="${n(frame.height)}">${defs.length ? `<defs>${defs.join("")}</defs>` : ""}<rect width="100%" height="100%" fill="${esc(frame.background)}"/>${body}</svg>`;
3176
- }
3177
- function videoProjectSvg(project, time, options = {}) {
3178
- return videoFrameToSvg(videoFrameAt(project, time), { fps: project.fps, ...options }, project.assets);
3179
- }
3180
- function videoContactSheetSvg(project, times, options = {}) {
3181
- const columns = _nullishCoalesce(options.columns, () => ( Math.ceil(Math.sqrt(times.length))));
3182
- const w = _nullishCoalesce(options.width, () => ( 320));
3183
- const h = w * project.height / project.width;
3184
- const label = 18;
3185
- const gap = 8;
3186
- const rows = Math.ceil(times.length / columns);
3187
- const W = columns * w + (columns + 1) * gap;
3188
- const H = rows * (h + label) + (rows + 1) * gap;
3189
- const cells = times.map((t, i) => {
3190
- const x = gap + i % columns * (w + gap);
3191
- const y = gap + Math.floor(i / columns) * (h + label + gap);
3192
- const svg = videoProjectSvg(project, t, options).replace(/^<svg xmlns="http:\/\/www.w3.org\/2000\/svg"/, `<svg x="${n(x)}" y="${n(y)}" width="${n(w)}" height="${n(h)}"`).replace(/ width="[\d.]+" height="[\d.]+">/, ">");
3193
- return `${svg}<text x="${n(x)}" y="${n(y + h + 14)}" font-family="sans-serif" font-size="12" fill="#0f172a">${(t / 1e3).toFixed(2)} s \xB7 ${formatVideoTimecode(t, project.fps)}</text>`;
3194
- });
3195
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${n(W)} ${n(H)}" width="${n(W)}" height="${n(H)}"><rect width="100%" height="100%" fill="#f8fafc"/>${cells.join("")}</svg>`;
3196
- }
3197
- function paintVideoLayer(ctx, frame, layer, sourceFor, options) {
3198
- const { rect } = layer;
3199
- if (layer.iris) {
3200
- ctx.beginPath();
3201
- ctx.arc(frame.width / 2, frame.height / 2, videoIrisRadius(frame, layer.iris.progress), 0, 2 * Math.PI);
3202
- ctx.clip();
3203
- }
3204
- if (layer.grade) {
3205
- const defs = videoGradeSvgDefs(layer.grade);
3206
- if (options.defineSvgFilter) for (const d of defs) options.defineSvgFilter(d.id, d.markup);
3207
- const css = videoGradeCssFilter(layer.grade);
3208
- ctx.filter = options.defineSvgFilter ? css || "none" : css.replace(/\s*url\([^)]*\)/g, "").trim() || "none";
3209
- }
3210
- if (layer.rotation) {
3211
- ctx.translate(rect.x + rect.width / 2, rect.y + rect.height / 2);
3212
- ctx.rotate(layer.rotation * Math.PI / 180);
3213
- ctx.translate(-(rect.x + rect.width / 2), -(rect.y + rect.height / 2));
3214
- }
3215
- if (layer.wipe) {
3216
- const r = videoWipeRect(rect, layer.wipe);
3217
- ctx.beginPath();
3218
- ctx.rect(r.x, r.y, r.width, r.height);
3219
- ctx.clip();
3220
- }
3221
- if (layer.type === "color") {
3222
- ctx.fillStyle = _nullishCoalesce(layer.color, () => ( "#000000"));
3223
- ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
3224
- } else if (layer.type === "text" && layer.text) {
3225
- const t = layer.text;
3226
- const lines = t.text.split("\n");
3227
- const lh = t.fontSize * 1.2;
3228
- const cx = rect.x + rect.width / 2;
3229
- const top = rect.y + rect.height / 2 - lh * (lines.length - 1) / 2;
3230
- ctx.font = `${t.fontWeight} ${t.fontSize}px ${t.fontFamily}`;
3231
- ctx.textAlign = "center";
3232
- ctx.textBaseline = "middle";
3233
- if (t.background) {
3234
- const w = Math.max(...lines.map((l) => ctx.measureText(l).width)) + t.fontSize;
3235
- const h = lh * lines.length + t.fontSize * 0.4;
3236
- ctx.fillStyle = t.background;
3237
- ctx.fillRect(cx - w / 2, rect.y + rect.height / 2 - h / 2, w, h);
3238
- }
3239
- ctx.fillStyle = t.color;
3240
- lines.forEach((l, k) => ctx.fillText(l, cx, top + k * lh));
3241
- } else {
3242
- const source = sourceFor(layer);
3243
- if (source) ctx.drawImage(source, rect.x, rect.y, rect.width, rect.height);
3244
- else {
3245
- ctx.fillStyle = "#334155";
3246
- ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
3247
- ctx.strokeStyle = "#94a3b8";
3248
- ctx.lineWidth = 2;
3249
- ctx.strokeRect(rect.x, rect.y, rect.width, rect.height);
3250
- }
3251
- }
3252
- }
3253
- function drawVideoFrame(ctx, frame, sourceFor, scale = 1, options = {}) {
3254
- ctx.save();
3255
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
3256
- ctx.globalAlpha = 1;
3257
- ctx.fillStyle = frame.background;
3258
- ctx.fillRect(0, 0, frame.width, frame.height);
3259
- drawVideoLayers(ctx, frame, frame.layers, sourceFor, scale, options, 0);
3260
- ctx.restore();
3261
- }
3262
- function drawVideoLayers(ctx, frame, layers, sourceFor, scale, options, depth) {
3263
- const cw = Math.max(1, Math.round(frame.width * scale));
3264
- const ch = Math.max(1, Math.round(frame.height * scale));
3265
- for (const layer of layers) {
3266
- let source = sourceFor;
3267
- if (layer.layers) {
3268
- const group = _optionalChain([options, 'access', _58 => _58.scratch, 'optionalCall', _59 => _59(cw, ch, 2 * depth)]);
3269
- if (!group) {
3270
- drawVideoLayers(ctx, frame, layer.layers, sourceFor, scale, options, depth + 1);
3271
- continue;
3272
- }
3273
- const g = group.ctx;
3274
- g.save();
3275
- g.setTransform(1, 0, 0, 1, 0, 0);
3276
- g.clearRect(0, 0, cw, ch);
3277
- g.setTransform(scale, 0, 0, scale, 0, 0);
3278
- g.globalAlpha = 1;
3279
- g.globalCompositeOperation = "source-over";
3280
- drawVideoLayers(g, frame, layer.layers, sourceFor, scale, options, depth + 1);
3281
- g.restore();
3282
- source = (l) => l === layer ? group.canvas : sourceFor(l);
3283
- }
3284
- const setting = _optionalChain([layer, 'access', _60 => _60.grade, 'optionalAccess', _61 => _61.lut]);
3285
- const lut = setting ? _optionalChain([options, 'access', _62 => _62.lutFor, 'optionalCall', _63 => _63(setting.src)]) : null;
3286
- const scratch = lut ? _optionalChain([options, 'access', _64 => _64.scratch, 'optionalCall', _65 => _65(cw, ch, 2 * depth + 1)]) : null;
3287
- ctx.save();
3288
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
3289
- if (lut && scratch) {
3290
- const s = scratch.ctx;
3291
- s.save();
3292
- s.setTransform(1, 0, 0, 1, 0, 0);
3293
- s.clearRect(0, 0, cw, ch);
3294
- s.setTransform(scale, 0, 0, scale, 0, 0);
3295
- s.globalAlpha = 1;
3296
- s.globalCompositeOperation = "source-over";
3297
- paintVideoLayer(s, frame, layer, source, options);
3298
- s.restore();
3299
- const img = s.getImageData(0, 0, cw, ch);
3300
- applyVideoLutToPixels(lut, img.data, _nullishCoalesce(setting.strength, () => ( 1)));
3301
- s.putImageData(img, 0, 0);
3302
- ctx.setTransform(1, 0, 0, 1, 0, 0);
3303
- ctx.globalAlpha = layer.opacity;
3304
- if (layer.blend && layer.blend !== "normal") ctx.globalCompositeOperation = layer.blend === "add" ? "lighter" : layer.blend;
3305
- ctx.drawImage(scratch.canvas, 0, 0, cw, ch);
3306
- } else {
3307
- ctx.globalAlpha = layer.opacity;
3308
- if (layer.blend && layer.blend !== "normal") ctx.globalCompositeOperation = layer.blend === "add" ? "lighter" : layer.blend;
3309
- paintVideoLayer(ctx, frame, layer, source, options);
3310
- }
3311
- ctx.restore();
3312
- }
3313
- }
3314
-
3315
- // src/video-editor/export.ts
3316
- var reelName = (asset) => (_nullishCoalesce(_nullishCoalesce(_optionalChain([asset, 'optionalAccess', _66 => _66.name]), () => ( _optionalChain([asset, 'optionalAccess', _67 => _67.id]))), () => ( "AX"))).replace(/\.[a-z0-9]+$/i, "").replace(/[^A-Za-z0-9]/g, "").toUpperCase().slice(0, 8) || "AX";
3317
- function toVideoEdl(project, options = {}) {
3318
- const track = options.track !== void 0 ? project.tracks.find((t) => t.id === options.track) : [...project.tracks].reverse().find((t) => t.kind === "video");
3319
- if (!track) throw new Error(options.track !== void 0 ? `There is no track "${options.track}".` : "The project has no video track.");
3320
- const fps = project.fps;
3321
- const dropFrame = (_nullishCoalesce(options.dropFrame, () => ( true))) && isVideoDropFrameRate(fps);
3322
- const tc = (ms) => formatVideoTimecode(ms, fps, dropFrame);
3323
- const rec0 = options.recordStart !== void 0 ? videoTimecodeFrames(options.recordStart, fps) : videoHourFrames(fps, dropFrame);
3324
- const rtc = (ms) => formatVideoTimecodeFrames(rec0 + videoTimecodeFrames(ms, fps), fps, dropFrame);
3325
- const assets = videoAssetMap(project);
3326
- const channel = track.kind === "video" ? "V" : "A";
3327
- const lines = [`TITLE: ${(_nullishCoalesce(_nullishCoalesce(options.title, () => ( project.name)), () => ( "Untitled"))).toUpperCase()}`, `FCM: ${dropFrame ? "DROP FRAME" : "NON-DROP FRAME"}`, ""];
3328
- let event = 0;
3329
- const num2 = () => String(++event).padStart(3, "0");
3330
- const sourceAt = (clip, time) => clip.type === "media" ? (_nullishCoalesce(clip.in, () => ( 0))) + (time - clip.start) * videoClipSpeed(clip) : time - clip.start;
3331
- const row = (n2, reel, kind, srcIn, srcOut, recIn, recOut) => `${n2} ${reel.padEnd(8)} ${channel.padEnd(5)} ${kind.padEnd(8)} ${tc(srcIn)} ${tc(srcOut)} ${rtc(recIn)} ${rtc(recOut)}`;
3332
- for (const clip of sortedVideoClips(track)) {
3333
- const asset = clip.type === "media" ? assets.get(clip.asset) : void 0;
3334
- const reel = clip.type === "media" ? reelName(asset) : "BL";
3335
- const end = videoClipEnd(clip);
3336
- const before = clip.transition ? videoClipBefore(track, clip) : null;
3337
- const half = before && clip.transition.type !== "dip" ? clip.transition.duration / 2 : 0;
3338
- const next = sortedVideoClips(track).find((c) => c !== clip && Math.abs(c.start - end) < 0.5 && c.transition && c.transition.type !== "dip");
3339
- const recOut = next ? end - next.transition.duration / 2 : end;
3340
- const recIn = clip.start - half;
3341
- const n2 = num2();
3342
- if (before) {
3343
- const t = clip.transition;
3344
- const frames = String(Math.round(t.duration * fps / 1e3)).padStart(3, "0");
3345
- const outReel = before.type === "media" ? reelName(assets.get(before.asset)) : "BL";
3346
- const at = sourceAt(before, recIn);
3347
- lines.push(row(n2, outReel, "C", at, at, recIn, recIn));
3348
- lines.push(row(n2, reel, `${t.type === "crossfade" || t.type === "dip" ? "D" : "W001"} ${frames}`, sourceAt(clip, recIn), sourceAt(clip, recOut), recIn, recOut));
3349
- } else lines.push(row(n2, reel, "C", sourceAt(clip, recIn), sourceAt(clip, recOut), recIn, recOut));
3350
- if (clip.type === "media" && videoClipSpeed(clip) !== 1) lines.push(`M2 ${reel.padEnd(8)} ${(videoClipSpeed(clip) * fps).toFixed(1).padStart(6)} ${tc(sourceAt(clip, recIn))}`);
3351
- if (clip.type === "media") lines.push(`* FROM CLIP NAME: ${_nullishCoalesce(_nullishCoalesce(_optionalChain([asset, 'optionalAccess', _68 => _68.name]), () => ( _optionalChain([asset, 'optionalAccess', _69 => _69.src]))), () => ( clip.asset))}`);
3352
- if (clip.type === "text") lines.push(`* TITLE: ${clip.text.replace(/\n/g, " ")}`);
3353
- if (clip.type === "color") lines.push(`* COLOR: ${clip.color}`);
3354
- if (clip.type === "sequence") lines.push(`* COMPOUND CLIP: ${_nullishCoalesce(clip.name, () => ( clip.sequence))} (sequence ${clip.sequence} from ${tc(_nullishCoalesce(clip.in, () => ( 0)))}, not flattened)`);
3355
- lines.push("");
3356
- }
3357
- return lines.join("\n");
3358
- }
3359
- function loudnormFilter(target = {}, measure = false) {
3360
- const I = _nullishCoalesce(target.integrated, () => ( -14));
3361
- const TP = _nullishCoalesce(target.truePeak, () => ( -1));
3362
- const LRA = _nullishCoalesce(target.range, () => ( 11));
3363
- if (!(I >= -70 && I <= -5)) throw new Error(`The loudness target must be between -70 and -5 LUFS (got ${I}); -14 suits streaming, -23 broadcast.`);
3364
- if (!(TP >= -9 && TP <= 0)) throw new Error(`The true peak ceiling must be between -9 and 0 dBTP (got ${TP}); -1 is usual.`);
3365
- if (!(LRA >= 1 && LRA <= 50)) throw new Error(`The loudness range must be between 1 and 50 LU (got ${LRA}).`);
3366
- const parts = [`loudnorm=I=${I}:TP=${TP}:LRA=${LRA}`];
3367
- const m = target.measured;
3368
- if (m && !measure) {
3369
- for (const [k, v] of Object.entries(m)) if (!Number.isFinite(v)) throw new Error(`The measured ${k} is not a number (${v}); measure again, or leave "measured" out for a one-pass normalisation.`);
3370
- parts.push(`measured_I=${m.integrated}:measured_TP=${m.truePeak}:measured_LRA=${m.range}:measured_thresh=${m.threshold}:offset=${m.offset}:linear=true`);
3371
- }
3372
- if (measure) parts.push("print_format=json");
3373
- return `${parts.join(":")},aresample=48000`;
3374
- }
3375
- function parseVideoLoudnessMeasurement(output) {
3376
- const blocks = output.match(/\{[^{}]*"input_i"[^{}]*\}/g);
3377
- if (!blocks) throw new Error("No loudness measurement found: pass the stderr of the command from toVideoLoudnessMeasure.");
3378
- const json = JSON.parse(blocks[blocks.length - 1]);
3379
- const n2 = (k) => Number(json[k]);
3380
- const m = { integrated: n2("input_i"), truePeak: n2("input_tp"), range: n2("input_lra"), threshold: n2("input_thresh"), offset: n2("target_offset") };
3381
- if (!Number.isFinite(m.integrated)) throw new Error("The mix is silent, so its loudness cannot be normalised.");
3382
- return m;
3383
- }
3384
- var sec = (ms) => String(Math.round(ms) / 1e3);
3385
- var fmtMs = (v) => String(Math.round(v * 100) / 100);
3386
- var FFMPEG_BLEND = {
3387
- multiply: "multiply",
3388
- screen: "screen",
3389
- overlay: "overlay",
3390
- darken: "darken",
3391
- lighten: "lighten",
3392
- "color-dodge": "dodge",
3393
- "color-burn": "burn",
3394
- "hard-light": "hardlight",
3395
- "soft-light": "softlight",
3396
- difference: "difference",
3397
- exclusion: "exclusion",
3398
- add: "addition"
3399
- };
3400
- var AFADE_CURVES = { linear: "tri", "equal-power": "qsin", "s-curve": "hsin", exponential: "cub", logarithmic: "ipar" };
3401
- var afadeCurve = (curve) => curve && curve !== "linear" ? `:curve=${AFADE_CURVES[curve]}` : "";
3402
- var even = (v) => Math.max(2, 2 * Math.round(v / 2));
3403
- var ffmpegFilterValue = (s) => s.replace(/[\\':%]/g, (c) => `\\${c}`).replace(/[\\'[\],;]/g, (c) => `\\${c}`);
3404
- var color = (c) => {
3405
- const hex = /^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i.exec(c);
3406
- if (!hex) return c.replace(/[^A-Za-z0-9@.]/g, "");
3407
- const h = hex[1].length === 3 ? [...hex[1]].map((x) => x + x).join("") : hex[1];
3408
- return `0x${h.slice(0, 6)}${h.length === 8 ? `@${(parseInt(h.slice(6), 16) / 255).toFixed(3)}` : ""}`;
3409
- };
3410
- var videoShellQuote = (s) => /^[\w@%+=:,./-]+$/.test(s) ? s : `'${s.replace(/'/g, `'\\''`)}'`;
3411
- function atempo(speed) {
3412
- const out = [];
3413
- let s = speed;
3414
- while (s > 2) {
3415
- out.push("atempo=2");
3416
- s /= 2;
3417
- }
3418
- while (s < 0.5) {
3419
- out.push("atempo=0.5");
3420
- s /= 0.5;
3421
- }
3422
- if (Math.abs(s - 1) > 1e-6) out.push(`atempo=${Math.round(s * 1e6) / 1e6}`);
3423
- return out;
3424
- }
3425
- function toVideoFfmpeg(project, options = {}) {
3426
- return buildFfmpeg(project, options, false);
3427
- }
3428
- function toVideoLoudnessMeasure(project, options = {}) {
3429
- return buildFfmpeg(project, { ...options, loudness: { ...options.loudness, measured: void 0 } }, true);
3430
- }
3431
- function buildFfmpeg(project, options, measure) {
3432
- const notes = [];
3433
- const assets = videoAssetMap(project);
3434
- const sequences = videoSequenceMap(project);
3435
- const D = videoProjectDuration(project);
3436
- if (D <= 0) throw new Error("The project has no clips, so there is nothing to render.");
3437
- const W = even(project.width);
3438
- const H = even(project.height);
3439
- const fps = project.fps;
3440
- const inputs = [];
3441
- const graph = measure ? [] : [`color=c=${color(_nullishCoalesce(project.background, () => ( "#000000")))}:s=${W}x${H}:r=${fps}:d=${sec(D)},format=yuva420p[base]`];
3442
- let usesDrawtext = false;
3443
- const animated = /* @__PURE__ */ new Set();
3444
- const ramped = /* @__PURE__ */ new Set();
3445
- const rampedPicture = (remap, lead, end) => {
3446
- const pts = videoTimeRemapBreakpoints(remap, 2e3 / fps);
3447
- const first = pts[0];
3448
- const last = pts[pts.length - 1];
3449
- const s = `(T*1000+${fmtMs(first.v)})`;
3450
- let e = fmtMs(last.t - first.t);
3451
- for (let i = pts.length - 2; i >= 0; i--) {
3452
- const a = pts[i];
3453
- const b = pts[i + 1];
3454
- if (b.v - a.v < 1e-6) continue;
3455
- e = `if(lt(${s},${fmtMs(b.v)}),${fmtMs(a.t - first.t)}+(${s}-${fmtMs(a.v)})*${fmtMs((b.t - a.t) / (b.v - a.v))},${e})`;
3456
- }
3457
- const before = lead + first.t;
3458
- const after = Math.max(0, end - last.t);
3459
- return ["setpts=PTS-STARTPTS", `setpts='(${e})/1000/TB'`, `fps=${fps}`, ...before > 0.5 || after > 0.5 ? [`tpad=start_mode=clone:start_duration=${sec(before)}:stop_mode=clone:stop_duration=${sec(after + 1e3 / fps)}`] : [`tpad=stop_mode=clone:stop_duration=${sec(1e3 / fps)}`], `trim=duration=${sec(lead + end)}`];
3460
- };
3461
- const rampedSound = (remap, input2, output, lead, end) => {
3462
- const pts = videoTimeRemapBreakpoints(remap, Math.max(80, 2e3 / fps));
3463
- const first = pts[0];
3464
- const last = pts[pts.length - 1];
3465
- const n2 = ak;
3466
- const fmtA = "aformat=sample_fmts=fltp:sample_rates=48000:channel_layouts=stereo";
3467
- const silence = (ms) => `anullsrc=r=48000:cl=stereo,atrim=duration=${sec(ms)},${fmtA}`;
3468
- const pieces = [];
3469
- const out = [];
3470
- const parts = [];
3471
- if (lead + first.t > 0.5) parts.push(silence(lead + first.t));
3472
- const played = [];
3473
- for (let i = 0; i + 1 < pts.length; i++) {
3474
- const dt = pts[i + 1].t - pts[i].t;
3475
- if (dt < 0.5) continue;
3476
- const speed = (pts[i + 1].v - pts[i].v) / dt;
3477
- if (speed < 0.1) parts.push(silence(dt));
3478
- else {
3479
- played.push({ from: pts[i].v - first.v, to: pts[i + 1].v - first.v, speed });
3480
- parts.push(`#${played.length - 1}`);
3481
- }
3482
- }
3483
- if (end - last.t > 0.5) parts.push(silence(end - last.t));
3484
- if (played.length) out.push(`[${input2}]asplit=${played.length}${played.map((_, k2) => `[r${n2}s${k2}]`).join("")}`);
3485
- parts.forEach((p, k2) => {
3486
- const label = `[r${n2}p${k2}]`;
3487
- if (p.startsWith("#")) {
3488
- const piece = played[Number(p.slice(1))];
3489
- out.push(`[r${n2}s${p.slice(1)}]atrim=start=${sec(piece.from)}:end=${sec(piece.to)},asetpts=PTS-STARTPTS,${atempo(piece.speed).join(",") || "anull"},${fmtA}${label}`);
3490
- } else out.push(`${p}${label}`);
3491
- pieces.push(label);
3492
- });
3493
- out.push(`${pieces.join("")}concat=n=${pieces.length}:v=0:a=1${output}`);
3494
- return out;
3495
- };
3496
- const blended = /* @__PURE__ */ new Set();
3497
- let k = 0;
3498
- const step = 2e3 / fps;
3499
- const expr = (prop, clipTime, fallback, what) => {
3500
- if (isVideoKeyframes(prop)) animated.add(what);
3501
- return videoKeyframeFfmpegExpr(prop, clipTime, fallback, step);
3502
- };
3503
- const input = (clip, asset, lead, tail) => {
3504
- const speed = videoClipSpeed(clip);
3505
- const src = options.resolve ? options.resolve(asset) : asset.src;
3506
- const span = clip.duration + lead + tail;
3507
- if (asset.kind === "image") inputs.push(["-loop", "1", "-framerate", String(fps), "-t", sec(span), "-i", src]);
3508
- else if (clip.timeRemap) {
3509
- const { from, to } = videoTimeRemapSpan(clip.timeRemap);
3510
- inputs.push(["-ss", sec((_nullishCoalesce(clip.in, () => ( 0))) + from), "-t", sec(to - from + 1e3 / fps), "-i", src]);
3511
- } else inputs.push(["-ss", sec((_nullishCoalesce(clip.in, () => ( 0))) - lead * speed), "-t", sec(span * speed), "-i", src]);
3512
- return inputs.length - 1;
3513
- };
3514
- let ak = 0;
3515
- let q = 0;
3516
- const compose = (tracks, base, length, scope) => {
3517
- let cur2 = base;
3518
- const sounds = [];
3519
- for (const track of [...tracks].reverse()) {
3520
- for (const raw of sortedVideoClips(track)) {
3521
- const clip = videoClipAnimated(raw, project);
3522
- const { lead, tail } = videoClipOverhang(track, clip);
3523
- const t0 = clip.start - lead;
3524
- const span = clip.duration + lead + tail;
3525
- const end = videoClipEnd(clip);
3526
- const asset = clip.type === "media" ? assets.get(clip.asset) : void 0;
3527
- if (clip.type === "media" && !asset) {
3528
- notes.push(`Clip "${clip.id}" was left out: its asset "${clip.asset}" is missing.`);
3529
- continue;
3530
- }
3531
- const shows = scope.picture && track.kind === "video" && !track.hidden;
3532
- let compound = null;
3533
- if (clip.type === "sequence") {
3534
- const seq = sequences.get(clip.sequence);
3535
- if (!seq || scope.within.includes(seq.id)) {
3536
- notes.push(`Compound clip "${clip.id}" was left out: its sequence "${clip.sequence}" ${seq ? "contains itself" : "is missing"}.`);
3537
- continue;
3538
- }
3539
- const from = (_nullishCoalesce(clip.in, () => ( 0))) - lead;
3540
- if (from < -0.5) notes.push(`Compound clip "${clip.id}"'s transition needs ${Math.round(-from)} ms of sequence "${seq.id}" before its start, which is not there; it shows nothing then.`);
3541
- const length2 = Math.max(videoSequenceDuration(seq), from + span);
3542
- const hears = scope.sound && !clip.muted && !track.muted;
3543
- const n2 = q++;
3544
- const nestedBase = shows ? `nb${n2}` : null;
3545
- if (nestedBase) graph.push(`color=c=black@0:s=${W}x${H}:r=${fps}:d=${sec(length2)},format=yuva420p[${nestedBase}]`);
3546
- const sub = compose(seq.tracks, nestedBase, length2, { within: [...scope.within, seq.id], picture: shows, sound: hears });
3547
- compound = { picture: null, sound: null };
3548
- if (sub.video) {
3549
- graph.push(`[${sub.video}]trim=start=${sec(Math.max(0, from))}:duration=${sec(span)},setpts=PTS-STARTPTS[sq${n2}]`);
3550
- compound.picture = `[sq${n2}]`;
3551
- }
3552
- if (sub.audio.length) {
3553
- graph.push(`${sub.audio.join("")}amix=inputs=${sub.audio.length}:normalize=0:duration=longest,apad,atrim=start=${sec(Math.max(0, from))}:end=${sec(from + span)}[sm${n2}]`);
3554
- compound.sound = `[sm${n2}]`;
3555
- }
3556
- }
3557
- let index;
3558
- if (shows && asset && asset.kind !== "audio") index = input(clip, asset, lead, tail);
3559
- const audible = clip.type === "sequence" ? !!_optionalChain([compound, 'optionalAccess', _70 => _70.sound]) : videoAssetHasSound(asset);
3560
- if (scope.sound && audible && (clip.type === "media" || clip.type === "sequence") && !clip.muted && !track.muted) {
3561
- const trackGain = _nullishCoalesce(track.volume, () => ( 1));
3562
- const volume = isVideoKeyframes(clip.volume) ? `volume='${trackGain}*(${expr(clip.volume, `(t*1000${lead ? `-${Math.round(lead)}` : ""})`, 1, "volume")})':eval=frame` : `volume=${(_nullishCoalesce(clip.volume, () => ( 1))) * trackGain}`;
3563
- const ramp = clip.type === "media" ? clip.timeRemap : void 0;
3564
- const ai = compound ? -1 : _nullishCoalesce(index, () => ( input(clip, asset, lead, tail)));
3565
- let source2 = _nullishCoalesce(_optionalChain([compound, 'optionalAccess', _71 => _71.sound]), () => ( `[${ai}:a]`));
3566
- if (ramp) {
3567
- source2 = `[r${ak}]`;
3568
- graph.push(...rampedSound(ramp, `${ai}:a`, source2, lead, clip.duration + tail));
3569
- ramped.add("sound");
3570
- }
3571
- const chain2 = [...ramp ? [] : ["asetpts=PTS-STARTPTS", ...atempo(videoClipSpeed(clip))], volume];
3572
- const before2 = clip.transition ? videoClipBefore(track, clip) : null;
3573
- const next2 = sortedVideoClips(track).find((c) => c !== clip && Math.abs(c.start - end) < 0.5 && c.transition);
3574
- const inCurve = clip.transition ? afadeCurve(videoTransitionAudioCurve(clip.transition)) : "";
3575
- const outCurve = next2 ? afadeCurve(videoTransitionAudioCurve(next2.transition)) : "";
3576
- if (lead) chain2.push(`afade=t=in:st=0:d=${sec(2 * lead)}${inCurve}`);
3577
- if (tail) chain2.push(`afade=t=out:st=${sec(span - 2 * tail)}:d=${sec(2 * tail)}${outCurve}`);
3578
- if (before2 && clip.transition.type === "dip") chain2.push(`afade=t=in:st=0:d=${sec(clip.transition.duration / 2)}${inCurve}`);
3579
- if (_optionalChain([next2, 'optionalAccess', _72 => _72.transition, 'access', _73 => _73.type]) === "dip") chain2.push(`afade=t=out:st=${sec(span - next2.transition.duration / 2)}:d=${sec(next2.transition.duration / 2)}${outCurve}`);
3580
- if (clip.fadeIn) chain2.push(`afade=t=in:st=${sec(lead)}:d=${sec(clip.fadeIn)}${afadeCurve(clip.fadeInCurve)}`);
3581
- if (clip.fadeOut) chain2.push(`afade=t=out:st=${sec(lead + clip.duration - clip.fadeOut)}:d=${sec(clip.fadeOut)}${afadeCurve(clip.fadeOutCurve)}`);
3582
- chain2.push(`adelay=${Math.round(t0)}:all=1`);
3583
- graph.push(`${source2}${chain2.join(",")}[a${ak}]`);
3584
- sounds.push(`[a${ak++}]`);
3585
- }
3586
- if (!shows || compound && !compound.picture) continue;
3587
- const enable = `enable='between(t,${sec(t0)},${sec(t0 + span)})'`;
3588
- const next = `v${k++}`;
3589
- const onTimeline = `(t*1000-${Math.round(clip.start)})`;
3590
- const onChain = (v) => `(${v}*1000${lead ? `-${Math.round(lead)}` : ""})`;
3591
- const tf = clip.transform;
3592
- const moving = VIDEO_TRANSFORM_PROPERTIES.some((p) => isVideoKeyframes(_optionalChain([tf, 'optionalAccess', _74 => _74[p]])));
3593
- if (clip.type === "text" && options.titleImage) {
3594
- inputs.push(["-loop", "1", "-framerate", String(fps), "-t", sec(span), "-i", options.titleImage(moving ? { ...clip, transform: void 0 } : clip, { width: project.width, height: project.height })]);
3595
- index = inputs.length - 1;
3596
- } else if (clip.type === "text") {
3597
- usesDrawtext = true;
3598
- if (scope.within.length) notes.push(`Title "${clip.id}" is inside a compound clip and drawn with drawtext, which leaves the sequence's transparent areas transparent, so it shows only over pictures in the sequence; pass titleImage to draw it anywhere.`);
3599
- const op = isVideoKeyframes(clip.opacity) ? `clip(${expr(clip.opacity, onTimeline, 1, "opacity")},0,1)` : String(_nullishCoalesce(clip.opacity, () => ( 1)));
3600
- const alpha = [op, clip.fadeIn ? `min(1,(t-${sec(clip.start)})/${sec(clip.fadeIn)})` : "", clip.fadeOut ? `min(1,(${sec(end)}-t)/${sec(clip.fadeOut)})` : ""].filter(Boolean).join("*");
3601
- const coord = (axis) => {
3602
- const v = _optionalChain([tf, 'optionalAccess', _75 => _75[axis]]);
3603
- const base2 = `(${axis === "x" ? "w-text_w" : "h-text_h"})/2`;
3604
- return isVideoKeyframes(v) ? `${axis}='${base2}+(${expr(v, onTimeline, 0, "position")})'` : `${axis}=${base2}+${_nullishCoalesce(v, () => ( 0))}`;
3605
- };
3606
- const fontSize = _nullishCoalesce(clip.fontSize, () => ( 64));
3607
- const opts = [
3608
- `text=${ffmpegFilterValue(clip.text)}`,
3609
- options.fontFile ? `fontfile=${ffmpegFilterValue(options.fontFile)}` : `font=${ffmpegFilterValue(_nullishCoalesce(clip.fontFamily, () => ( "sans-serif")))}`,
3610
- isVideoKeyframes(_optionalChain([tf, 'optionalAccess', _76 => _76.scale])) ? `fontsize='${fontSize}*(${expr(tf.scale, onTimeline, 1, "scale")})'` : `fontsize=${Math.round(fontSize * (_nullishCoalesce(_optionalChain([tf, 'optionalAccess', _77 => _77.scale]), () => ( 1))))}`,
3611
- `fontcolor=${color(_nullishCoalesce(clip.color, () => ( "#ffffff")))}`,
3612
- coord("x"),
3613
- coord("y"),
3614
- `alpha='${alpha}'`,
3615
- ...clip.background ? ["box=1", `boxcolor=${color(clip.background)}`, `boxborderw=${Math.round((_nullishCoalesce(clip.fontSize, () => ( 64))) / 2)}`] : [],
3616
- `enable='between(t,${sec(clip.start)},${sec(end)})'`
3617
- ];
3618
- if (_optionalChain([tf, 'optionalAccess', _78 => _78.rotation])) notes.push(`Title "${clip.id}" is drawn without its rotation (drawtext cannot rotate).`);
3619
- if (videoGradeFfmpegFilters(clip.grade).length || normalizeVideoGradeLut(_optionalChain([clip, 'access', _79 => _79.grade, 'optionalAccess', _80 => _80.lut]))) notes.push(`Title "${clip.id}" is drawn without its grade (drawtext draws straight onto the picture); pass titleImage to grade titles.`);
3620
- if (clip.blend && clip.blend !== "normal") notes.push(`Title "${clip.id}" is drawn without its blend mode (drawtext draws straight onto the picture); pass titleImage to blend titles.`);
3621
- graph.push(`[${cur2}]drawtext=${opts.join(":")}[${next}]`);
3622
- cur2 = next;
3623
- continue;
3624
- }
3625
- const size = asset ? { width: _nullishCoalesce(asset.width, () => ( project.width)), height: _nullishCoalesce(asset.height, () => ( project.height)) } : { width: project.width, height: project.height };
3626
- const placed = clip.type === "text" && !moving ? void 0 : tf;
3627
- const still = Object.fromEntries(Object.entries(_nullishCoalesce(placed, () => ( {}))).filter(([, v]) => !isVideoKeyframes(v)));
3628
- const rect = placeVideoRect(project, size, clip.type === "text" ? "fill" : _nullishCoalesce(clip.fit, () => ( (clip.type === "media" || clip.type === "sequence" ? "contain" : "fill"))), still);
3629
- const chain = [];
3630
- let source;
3631
- if (clip.type === "color") source = `color=c=${color(clip.color)}:s=${even(rect.width)}x${even(rect.height)}:r=${fps}:d=${sec(span)}`;
3632
- else if (_optionalChain([compound, 'optionalAccess', _81 => _81.picture])) {
3633
- source = compound.picture;
3634
- chain.push(`scale=${even(rect.width)}:${even(rect.height)}`);
3635
- } else {
3636
- source = `[${index}:v]`;
3637
- if (clip.type === "media" && clip.timeRemap) chain.push(...rampedPicture(clip.timeRemap, lead, clip.duration + tail), `scale=${even(rect.width)}:${even(rect.height)}`);
3638
- else chain.push(`setpts=${videoClipSpeed(clip) !== 1 ? `(PTS-STARTPTS)/${videoClipSpeed(clip)}` : "PTS-STARTPTS"}`, `fps=${fps}`, `scale=${even(rect.width)}:${even(rect.height)}`);
3639
- }
3640
- const grade = videoGradeFfmpegFilters(clip.grade);
3641
- if (grade.length) chain.push("format=rgba", ...grade);
3642
- const lut = normalizeVideoGradeLut(_optionalChain([clip, 'access', _82 => _82.grade, 'optionalAccess', _83 => _83.lut]));
3643
- if (lut) {
3644
- chain.push("format=gbrap");
3645
- const path = options.resolveLut ? options.resolveLut(lut.src) : lut.src;
3646
- if (/^(blob|data|https?):/i.test(path)) notes.push(`ffmpeg's lut3d reads only local files, and clip "${clip.id}"'s LUT is "${path.slice(0, 80)}"; save it and pass resolveLut to give its path.`);
3647
- const file = ffmpegFilterValue(path);
3648
- const into2 = `q${next}`;
3649
- graph.push(`${source.startsWith("[") ? source : `${source},`}${chain.join(",")}[${into2}]`);
3650
- const lut3d = `lut3d=file=${file}:interp=tetrahedral`;
3651
- if ((_nullishCoalesce(lut.strength, () => ( 1))) >= 1) graph.push(`[${into2}]${lut3d}[r${next}]`);
3652
- else graph.push(`[${into2}]split[o${next}][p${next}]`, `[p${next}]${lut3d}[s${next}]`, `[s${next}][o${next}]blend=all_mode=normal:all_opacity=${lut.strength}[r${next}]`);
3653
- source = `[r${next}]`;
3654
- chain.length = 0;
3655
- }
3656
- chain.push("format=yuva420p");
3657
- if (isVideoKeyframes(clip.opacity)) chain.push(`geq=lum='lum(X,Y)':cb='cb(X,Y)':cr='cr(X,Y)':a='alpha(X,Y)*clip(${expr(clip.opacity, onChain("T"), 1, "opacity")},0,1)'`);
3658
- else if (clip.opacity !== void 0 && clip.opacity < 1) chain.push(`colorchannelmixer=aa=${clip.opacity}`);
3659
- if (clip.fadeIn) chain.push(`fade=t=in:st=${sec(lead)}:d=${sec(clip.fadeIn)}:alpha=1`);
3660
- if (clip.fadeOut) chain.push(`fade=t=out:st=${sec(lead + clip.duration - clip.fadeOut)}:d=${sec(clip.fadeOut)}:alpha=1`);
3661
- if (lead && clip.transition.type === "crossfade") chain.push(`fade=t=in:st=0:d=${sec(2 * lead)}:alpha=1`);
3662
- if (lead && clip.transition.type === "wipe") {
3663
- const d = sec(2 * lead);
3664
- const p = `min(1,T/${d})`;
3665
- const dir = _nullishCoalesce(clip.transition.direction, () => ( "right"));
3666
- const test = dir === "right" ? `lt(X,W*${p})` : dir === "left" ? `gt(X,W*(1-${p}))` : dir === "down" ? `lt(Y,H*${p})` : `gt(Y,H*(1-${p}))`;
3667
- chain.push(`geq=lum='lum(X,Y)':cb='cb(X,Y)':cr='cr(X,Y)':a='if(${test},alpha(X,Y),0)'`);
3668
- }
3669
- if (lead && clip.transition.type === "iris") {
3670
- const r = videoIrisRadius({ width: W, height: H }, 1);
3671
- chain.push(`geq=lum='lum(X,Y)':cb='cb(X,Y)':cr='cr(X,Y)':a='if(lte(hypot(X+${Math.round(rect.x)}-${W / 2},Y+${Math.round(rect.y)}-${H / 2}),${Math.round(r)}*min(1,T/${sec(2 * lead)})),alpha(X,Y),0)'`);
3672
- }
3673
- const rotation = _optionalChain([placed, 'optionalAccess', _84 => _84.rotation]);
3674
- if (isVideoKeyframes(rotation)) chain.push(`rotate=a='(${expr(rotation, onChain("t"), 0, "rotation")})*PI/180':c=black@0:ow='hypot(iw,ih)':oh=ow`);
3675
- else if (rotation) chain.push(`rotate=${rotation}*PI/180:c=black@0:ow=rotw(${rotation}*PI/180):oh=roth(${rotation}*PI/180)`);
3676
- if (isVideoKeyframes(_optionalChain([placed, 'optionalAccess', _85 => _85.scale]))) {
3677
- const s = expr(placed.scale, onChain("t"), 1, "scale");
3678
- chain.push(`scale=w='max(2,trunc(iw*(${s})/2)*2)':h='max(2,trunc(ih*(${s})/2)*2)':eval=frame`);
3679
- }
3680
- chain.push(`setpts=PTS+${sec(t0)}/TB`);
3681
- const cx = rect.x + rect.width / 2;
3682
- const cy = rect.y + rect.height / 2;
3683
- const shifts = { x: [], y: [] };
3684
- const shiftBy = (direction, from, d, entering) => {
3685
- const dir = _nullishCoalesce(direction, () => ( "right"));
3686
- const axis = dir === "left" || dir === "right" ? "x" : "y";
3687
- const size2 = axis === "x" ? W : H;
3688
- const sign = dir === "left" || dir === "up" ? -1 : 1;
3689
- const ease = `(1-cos(PI*clip((t-${sec(from)})/${sec(d)},0,1)))/2`;
3690
- shifts[axis].push(`${sign * size2}*(${ease}${entering ? "-1" : ""})`);
3691
- };
3692
- const into = lead ? clip.transition.type : void 0;
3693
- if (into === "push" || into === "slide") shiftBy(clip.transition.direction, clip.start - lead, 2 * lead, true);
3694
- const after = tail ? sortedVideoClips(track).find((c) => c !== clip && Math.abs(c.start - end) < 0.5 && c.transition) : void 0;
3695
- if (_optionalChain([after, 'optionalAccess', _86 => _86.transition, 'access', _87 => _87.type]) === "push") shiftBy(after.transition.direction, end - tail, 2 * tail, false);
3696
- const at = (axis, c) => {
3697
- const v = _optionalChain([placed, 'optionalAccess', _88 => _88[axis]]);
3698
- const extra = shifts[axis].map((s) => `+${s}`).join("");
3699
- return isVideoKeyframes(v) ? `${axis}='${Math.round(axis === "x" ? project.width / 2 : project.height / 2)}+(${expr(v, onTimeline, 0, "position")})-${axis === "x" ? "w" : "h"}/2${extra}'` : `${axis}='${Math.round(c)}-${axis === "x" ? "w" : "h"}/2${extra}'`;
3700
- };
3701
- graph.push(`${source.startsWith("[") ? source : `${source},`}${chain.join(",")}[l${next}]`);
3702
- if (clip.blend && clip.blend !== "normal") {
3703
- graph.push(`color=c=black@0:s=${W}x${H}:r=${fps}:d=${sec(length)},format=yuva420p[t${next}]`);
3704
- const clear = scope.within.length > 0;
3705
- graph.push(`[t${next}][l${next}]overlay=${at("x", cx)}:${at("y", cy)}:eof_action=pass:${enable},format=gbrap,split${clear ? `=3` : ""}[f${next}][g${next}]${clear ? `[h${next}]` : ""}`);
3706
- graph.push(`[${cur2}]format=gbrap,split${clear ? `=3` : ""}[c${next}][d${next}]${clear ? `[e${next}]` : ""}`);
3707
- const layerFirst = clip.blend === "color-dodge" || clip.blend === "color-burn";
3708
- graph.push(`${layerFirst ? `[f${next}][c${next}]` : `[c${next}][f${next}]`}blend=all_mode=${FFMPEG_BLEND[clip.blend]}[b${next}]`);
3709
- graph.push(`[g${next}]alphaextract[m${next}]`);
3710
- if (clear) {
3711
- graph.push(`[e${next}]alphaextract[ea${next}]`);
3712
- graph.push(`[b${next}][ea${next}]alphamerge[bb${next}]`);
3713
- graph.push(`[h${next}]format=gbrp[ho${next}]`);
3714
- graph.push(`[ho${next}][bb${next}]overlay=format=gbrp[bm${next}]`);
3715
- graph.push(`[bm${next}][m${next}]alphamerge[x${next}]`);
3716
- } else graph.push(`[b${next}][m${next}]alphamerge[x${next}]`);
3717
- graph.push(`[d${next}][x${next}]overlay=format=gbrp,format=yuva420p[${next}]`);
3718
- blended.add(clip.blend);
3719
- } else graph.push(`[${cur2}][l${next}]overlay=${at("x", cx)}:${at("y", cy)}:eof_action=pass:${enable}[${next}]`);
3720
- cur2 = next;
3721
- const before = clip.transition ? videoClipBefore(track, clip) : null;
3722
- if (before && clip.transition.type === "dip") {
3723
- const d = clip.transition.duration;
3724
- const dip = `d${k}`;
3725
- graph.push(`color=c=${color(_nullishCoalesce(clip.transition.color, () => ( "#000000")))}:s=${W}x${H}:r=${fps}:d=${sec(d)},format=yuva420p,fade=t=in:st=0:d=${sec(d / 2)}:alpha=1,fade=t=out:st=${sec(d / 2)}:d=${sec(d / 2)}:alpha=1,setpts=PTS+${sec(clip.start - d / 2)}/TB[l${dip}]`);
3726
- graph.push(`[${cur2}][l${dip}]overlay=eof_action=pass:enable='between(t,${sec(clip.start - d / 2)},${sec(clip.start + d / 2)})'[${dip}]`);
3727
- cur2 = dip;
3728
- }
3729
- }
3730
- }
3731
- return { video: cur2, audio: sounds };
3732
- };
3733
- const top = compose(project.tracks, measure ? null : "base", D, { within: [], picture: !measure, sound: true });
3734
- const audio = top.audio;
3735
- const cur = top.video;
3736
- if (!measure) graph.push(`[${cur}]format=yuv420p[vout]`);
3737
- if (measure && !audio.length) throw new Error("Nothing on the timeline makes a sound, so there is no loudness to measure.");
3738
- const loudness = options.loudness || measure ? `,${loudnormFilter(options.loudness, measure)}` : "";
3739
- if (audio.length) graph.push(`${audio.join("")}amix=inputs=${audio.length}:normalize=0:duration=longest,apad,atrim=0:${sec(D)}${loudness}[aout]`);
3740
- if (options.loudness && !audio.length) notes.push("Nothing makes a sound, so there was no loudness to normalise.");
3741
- else if (options.loudness && !options.loudness.measured)
3742
- notes.push("Loudness is normalised in one pass, which adjusts the level as it goes; for a fixed gain that keeps the dynamics, run toVideoLoudnessMeasure first and pass its result as loudness.measured.");
3743
- if (allVideoTracks(project).some(({ track: t }) => t.clips.some((c) => c.type === "media" && _optionalChain([assets, 'access', _89 => _89.get, 'call', _90 => _90(c.asset), 'optionalAccess', _91 => _91.kind]) === "video" && _optionalChain([assets, 'access', _92 => _92.get, 'call', _93 => _93(c.asset), 'optionalAccess', _94 => _94.hasAudio]) === void 0)))
3744
- notes.push('Video assets are assumed to have sound; set "hasAudio": false on any that do not, or ffmpeg will stop at a missing audio stream.');
3745
- if (animated.size)
3746
- notes.push(`Keyframed ${[...animated].sort().join(", ")} ${animated.size > 1 ? "are" : "is"} rendered as straight lines between keys, with eased segments sampled every two frames${animated.has("scale") ? "; animated scale needs ffmpeg 5 or later (scale with eval=frame)" : ""}.`);
3747
- if (blended.has("soft-light")) notes.push("ffmpeg's soft-light blend uses a slightly different formula from the browser's, so the render can differ a little from the preview.");
3748
- if (ramped.has("sound")) notes.push("Sound through a speed ramp plays each stretch at its own speed, so a gliding change is heard in small steps (about every 80 ms), and freezes are silent.");
3749
- if (usesDrawtext) notes.push("Titles use drawtext, which needs an ffmpeg built with libfreetype (Homebrew's is not: check `ffmpeg -filters | grep drawtext`); pass titleImage to render them as images instead.");
3750
- const filterComplex = graph.join(";");
3751
- if (measure) {
3752
- const args2 = ["-hide_banner", "-nostats", ...inputs.flat(), "-filter_complex", filterComplex, "-map", "[aout]", "-f", "null", "-"];
3753
- return { args: args2, command: ["ffmpeg", ...args2].map(videoShellQuote).join(" "), filterComplex, duration: D, notes };
3754
- }
3755
- const args = [
3756
- "-y",
3757
- ...inputs.flat(),
3758
- "-filter_complex",
3759
- filterComplex,
3760
- "-map",
3761
- "[vout]",
3762
- ...audio.length ? ["-map", "[aout]", "-c:a", _nullishCoalesce(options.audioCodec, () => ( "aac"))] : ["-an"],
3763
- "-c:v",
3764
- _nullishCoalesce(options.videoCodec, () => ( "libx264")),
3765
- ...(_nullishCoalesce(options.videoCodec, () => ( "libx264"))) === "libx264" ? ["-crf", String(_nullishCoalesce(options.crf, () => ( 18))), "-preset", "medium"] : [],
3766
- "-pix_fmt",
3767
- "yuv420p",
3768
- "-r",
3769
- String(fps),
3770
- "-t",
3771
- sec(D),
3772
- _nullishCoalesce(options.output, () => ( "output.mp4"))
3773
- ];
3774
- return { args, command: ["ffmpeg", ...args].map(videoShellQuote).join(" "), filterComplex, duration: D, notes };
3775
- }
3776
-
3777
2114
  // src/video-editor/proxy.ts
3778
2115
  var VIDEO_PROXY_SCALES = [0.5, 0.25];
3779
2116
  function videoAssetProxy(asset, scale) {
3780
- if (!_optionalChain([asset, 'optionalAccess', _95 => _95.proxies, 'optionalAccess', _96 => _96.length]) || !(scale > 0) || scale >= 1) return null;
2117
+ if (!_optionalChain([asset, 'optionalAccess', _30 => _30.proxies, 'optionalAccess', _31 => _31.length]) || !(scale > 0) || scale >= 1) return null;
3781
2118
  let best = null;
3782
2119
  for (const p of asset.proxies) if (p.scale >= scale - 1e-9 && p.scale < 1 && (!best || p.scale < best.scale)) best = p;
3783
2120
  return best;
3784
2121
  }
3785
- var videoPreviewSrc = (asset, scale, fallback = _nullishCoalesce(_optionalChain([asset, 'optionalAccess', _97 => _97.src]), () => ( ""))) => _nullishCoalesce(_optionalChain([videoAssetProxy, 'call', _98 => _98(asset, scale), 'optionalAccess', _99 => _99.src]), () => ( fallback));
2122
+ var videoPreviewSrc = (asset, scale, fallback = _nullishCoalesce(_optionalChain([asset, 'optionalAccess', _32 => _32.src]), () => ( ""))) => _nullishCoalesce(_optionalChain([videoAssetProxy, 'call', _33 => _33(asset, scale), 'optionalAccess', _34 => _34.src]), () => ( fallback));
3786
2123
  function resolveVideoPreviewScale(scale, shown) {
3787
2124
  if (scale === void 0 || scale === "auto") return Math.min(1, Math.max(0, shown));
3788
2125
  if (scale === "full") return 1;
@@ -3831,7 +2168,7 @@ function videoProxyFfmpeg(asset, options = {}) {
3831
2168
  output
3832
2169
  ];
3833
2170
  const proxy = { scale, src: output, ...asset.width && asset.height ? { width: Math.trunc(asset.width * scale / 2) * 2, height: Math.trunc(asset.height * scale / 2) * 2 } : {} };
3834
- return { asset: asset.id, args, command: ["ffmpeg", ...args].map(videoShellQuote).join(" "), proxy };
2171
+ return { asset: asset.id, args, command: ["ffmpeg", ...args].map(_chunkSMJGE7HMcjs.videoShellQuote).join(" "), proxy };
3835
2172
  }
3836
2173
  function videoProxyPlan(project, options = {}) {
3837
2174
  const scale = _nullishCoalesce(options.scale, () => ( 0.5));
@@ -3841,7 +2178,7 @@ function videoProxyPlan(project, options = {}) {
3841
2178
  if (!a) throw new Error(`There is no asset "${id}"; the assets are ${project.assets.map((x) => `"${x.id}"`).join(", ") || "none"}.`);
3842
2179
  if (a.kind !== "video") throw new Error(`Asset "${id}" is ${a.kind === "image" ? "an image" : "audio"}; only video assets have proxies.`);
3843
2180
  }
3844
- return project.assets.filter((a) => a.kind === "video" && (!wanted || wanted.has(a.id)) && !_optionalChain([a, 'access', _100 => _100.proxies, 'optionalAccess', _101 => _101.some, 'call', _102 => _102((p) => Math.abs(p.scale - scale) < 1e-9)])).map((a) => videoProxyFfmpeg(a, { ...options, scale }));
2181
+ return project.assets.filter((a) => a.kind === "video" && (!wanted || wanted.has(a.id)) && !_optionalChain([a, 'access', _35 => _35.proxies, 'optionalAccess', _36 => _36.some, 'call', _37 => _37((p) => Math.abs(p.scale - scale) < 1e-9)])).map((a) => videoProxyFfmpeg(a, { ...options, scale }));
3845
2182
  }
3846
2183
  var VIDEO_PREVIEW_SCALE_OPTIONS = [
3847
2184
  { value: "auto", label: "Auto", scale: "auto" },
@@ -3849,7 +2186,7 @@ var VIDEO_PREVIEW_SCALE_OPTIONS = [
3849
2186
  ...VIDEO_PROXY_SCALES.map((s) => ({ value: String(s), label: s === 0.5 ? "\xBD" : s === 0.25 ? "\xBC" : `${s * 100}%`, scale: s }))
3850
2187
  ];
3851
2188
  var parseVideoPreviewScale = (value) => value === "full" ? "full" : Number(value) > 0 && Number(value) <= 1 ? Number(value) : "auto";
3852
- var videoProjectHasProxies = (project) => project.assets.some((a) => !!_optionalChain([a, 'access', _103 => _103.proxies, 'optionalAccess', _104 => _104.length]));
2189
+ var videoProjectHasProxies = (project) => project.assets.some((a) => !!_optionalChain([a, 'access', _38 => _38.proxies, 'optionalAccess', _39 => _39.length]));
3853
2190
 
3854
2191
  // src/video-editor/frame-cache.ts
3855
2192
  var videoSourceFrame = (sourceTime, fps) => Math.max(0, Math.floor(sourceTime * fps / 1e3 + 1e-6));
@@ -3857,7 +2194,7 @@ var videoSourceFrameTime = (frame, fps) => (frame + 0.5) * 1e3 / fps;
3857
2194
  var videoFrameCacheKey = (src, frame) => `${frame}@${src}`;
3858
2195
  function videoFrameRequestsAt(project, time, srcFor = (l) => l.src) {
3859
2196
  const out = [];
3860
- for (const layer of videoFrameLeafLayers(videoFrameAt(project, time).layers)) {
2197
+ for (const layer of _chunkSMJGE7HMcjs.videoFrameLeafLayers.call(void 0, _chunkSMJGE7HMcjs.videoFrameAt.call(void 0, project, time).layers)) {
3861
2198
  if (layer.type !== "media" || layer.assetKind !== "video") continue;
3862
2199
  const src = srcFor(layer);
3863
2200
  if (!src) continue;
@@ -3884,7 +2221,7 @@ function planVideoPrefetch(project, time, options = {}) {
3884
2221
  return out;
3885
2222
  }
3886
2223
  function videoCacheEvictions(entries, options) {
3887
- let total = entries.reduce((n2, e) => n2 + e.bytes, 0);
2224
+ let total = entries.reduce((n, e) => n + e.bytes, 0);
3888
2225
  if (total <= options.maxBytes) return [];
3889
2226
  const dir = _nullishCoalesce(options.direction, () => ( 1));
3890
2227
  const behind = _nullishCoalesce(options.behind, () => ( 2));
@@ -3893,7 +2230,7 @@ function videoCacheEvictions(entries, options) {
3893
2230
  return d >= 0 ? d : -d * behind;
3894
2231
  };
3895
2232
  const out = [];
3896
- for (const e of entries.filter((x) => !_optionalChain([options, 'access', _105 => _105.protect, 'optionalAccess', _106 => _106.has, 'call', _107 => _107(x.key)])).sort((a, b) => cost(b) - cost(a))) {
2233
+ for (const e of entries.filter((x) => !_optionalChain([options, 'access', _40 => _40.protect, 'optionalAccess', _41 => _41.has, 'call', _42 => _42(x.key)])).sort((a, b) => cost(b) - cost(a))) {
3897
2234
  if (total <= options.maxBytes) break;
3898
2235
  out.push(e.key);
3899
2236
  total -= e.bytes;
@@ -3905,26 +2242,26 @@ var defaultCapture = async (source) => {
3905
2242
  const image = await createImageBitmap(source);
3906
2243
  return { image, width: image.width, height: image.height };
3907
2244
  };
3908
- var VideoFrameCache = (_class5 = class {
3909
- constructor(options = {}) {;_class5.prototype.__init13.call(this);_class5.prototype.__init14.call(this);_class5.prototype.__init15.call(this);_class5.prototype.__init16.call(this);_class5.prototype.__init17.call(this);_class5.prototype.__init18.call(this);_class5.prototype.__init19.call(this);_class5.prototype.__init20.call(this);_class5.prototype.__init21.call(this);_class5.prototype.__init22.call(this);_class5.prototype.__init23.call(this);_class5.prototype.__init24.call(this);
2245
+ var VideoFrameCache = (_class4 = class {
2246
+ constructor(options = {}) {;_class4.prototype.__init9.call(this);_class4.prototype.__init10.call(this);_class4.prototype.__init11.call(this);_class4.prototype.__init12.call(this);_class4.prototype.__init13.call(this);_class4.prototype.__init14.call(this);_class4.prototype.__init15.call(this);_class4.prototype.__init16.call(this);_class4.prototype.__init17.call(this);_class4.prototype.__init18.call(this);_class4.prototype.__init19.call(this);_class4.prototype.__init20.call(this);
3910
2247
  this.options = options;
3911
2248
  this.maxBytes = _nullishCoalesce(options.maxBytes, () => ( 512 * 1024 * 1024));
3912
2249
  this.prefetch = _nullishCoalesce(options.prefetch, () => ( 2e3));
3913
2250
  this.decode = options.decode !== void 0 ? options.decode : typeof document !== "undefined" ? createVideoElementDecoder() : null;
3914
2251
  }
3915
2252
 
3916
- __init13() {this.entries = /* @__PURE__ */ new Map()}
3917
- __init14() {this.inFlight = /* @__PURE__ */ new Set()}
3918
- __init15() {this.queue = []}
3919
- __init16() {this.protect = /* @__PURE__ */ new Set()}
3920
- __init17() {this.listeners = /* @__PURE__ */ new Set()}
3921
- __init18() {this.playhead = 0}
3922
- __init19() {this.direction = 1}
3923
- __init20() {this.hits = 0}
3924
- __init21() {this.misses = 0}
3925
- __init22() {this.bytes = 0}
3926
- __init23() {this.generation = 0}
3927
- __init24() {this.destroyed = false}
2253
+ __init9() {this.entries = /* @__PURE__ */ new Map()}
2254
+ __init10() {this.inFlight = /* @__PURE__ */ new Set()}
2255
+ __init11() {this.queue = []}
2256
+ __init12() {this.protect = /* @__PURE__ */ new Set()}
2257
+ __init13() {this.listeners = /* @__PURE__ */ new Set()}
2258
+ __init14() {this.playhead = 0}
2259
+ __init15() {this.direction = 1}
2260
+ __init16() {this.hits = 0}
2261
+ __init17() {this.misses = 0}
2262
+ __init18() {this.bytes = 0}
2263
+ __init19() {this.generation = 0}
2264
+ __init20() {this.destroyed = false}
3928
2265
 
3929
2266
 
3930
2267
 
@@ -3934,7 +2271,7 @@ var VideoFrameCache = (_class5 = class {
3934
2271
  const entry = this.entries.get(videoFrameCacheKey(src, videoSourceFrame(sourceTime, fps)));
3935
2272
  if (entry) this.hits++;
3936
2273
  else this.misses++;
3937
- return _optionalChain([entry, 'optionalAccess', _108 => _108.frame, 'access', _109 => _109.image]);
2274
+ return _optionalChain([entry, 'optionalAccess', _43 => _43.frame, 'access', _44 => _44.image]);
3938
2275
  }
3939
2276
  /** Whether a frame is cached, without counting it. */
3940
2277
  has(src, sourceTime, fps) {
@@ -3942,7 +2279,7 @@ var VideoFrameCache = (_class5 = class {
3942
2279
  }
3943
2280
  /** Keeps a decoded frame (replacing any with the same key), then evicts to the budget. */
3944
2281
  put(request, frame) {
3945
- if (this.destroyed) return void _optionalChain([frame, 'access', _110 => _110.image, 'access', _111 => _111.close, 'optionalCall', _112 => _112()]);
2282
+ if (this.destroyed) return void _optionalChain([frame, 'access', _45 => _45.image, 'access', _46 => _46.close, 'optionalCall', _47 => _47()]);
3946
2283
  const old = this.entries.get(request.key);
3947
2284
  if (old) this.drop(old);
3948
2285
  const bytes = Math.max(1, frame.width * frame.height * 4);
@@ -4041,7 +2378,7 @@ var VideoFrameCache = (_class5 = class {
4041
2378
  (frame) => {
4042
2379
  this.inFlight.delete(r.key);
4043
2380
  if (generation === this.generation || this.bytes + frame.width * frame.height * 4 <= this.maxBytes) this.put(r, frame);
4044
- else _optionalChain([frame, 'access', _113 => _113.image, 'access', _114 => _114.close, 'optionalCall', _115 => _115()]);
2381
+ else _optionalChain([frame, 'access', _48 => _48.image, 'access', _49 => _49.close, 'optionalCall', _50 => _50()]);
4045
2382
  },
4046
2383
  () => this.inFlight.delete(r.key)
4047
2384
  ).finally(() => this.pump());
@@ -4054,12 +2391,12 @@ var VideoFrameCache = (_class5 = class {
4054
2391
  drop(e) {
4055
2392
  this.entries.delete(e.key);
4056
2393
  this.bytes -= e.bytes;
4057
- _optionalChain([e, 'access', _116 => _116.frame, 'access', _117 => _117.image, 'access', _118 => _118.close, 'optionalCall', _119 => _119()]);
2394
+ _optionalChain([e, 'access', _51 => _51.frame, 'access', _52 => _52.image, 'access', _53 => _53.close, 'optionalCall', _54 => _54()]);
4058
2395
  }
4059
2396
  emit() {
4060
2397
  for (const l of [...this.listeners]) l();
4061
2398
  }
4062
- }, _class5);
2399
+ }, _class4);
4063
2400
  function createVideoElementDecoder(options = {}) {
4064
2401
  const videos = /* @__PURE__ */ new Map();
4065
2402
  const make = _nullishCoalesce(options.createVideo, () => ( (() => {
@@ -4101,12 +2438,12 @@ function createVideoElementDecoder(options = {}) {
4101
2438
  }
4102
2439
 
4103
2440
  // src/video-editor/preview.ts
4104
- var VideoPreviewEngine = (_class6 = class {
4105
- constructor(source, canvas2, options = {}) {;_class6.prototype.__init25.call(this);_class6.prototype.__init26.call(this);_class6.prototype.__init27.call(this);_class6.prototype.__init28.call(this);_class6.prototype.__init29.call(this);_class6.prototype.__init30.call(this);_class6.prototype.__init31.call(this);_class6.prototype.__init32.call(this);_class6.prototype.__init33.call(this);_class6.prototype.__init34.call(this);_class6.prototype.__init35.call(this);_class6.prototype.__init36.call(this);_class6.prototype.__init37.call(this);_class6.prototype.__init38.call(this);_class6.prototype.__init39.call(this);_class6.prototype.__init40.call(this);
2441
+ var VideoPreviewEngine = (_class5 = class {
2442
+ constructor(source, canvas2, options = {}) {;_class5.prototype.__init21.call(this);_class5.prototype.__init22.call(this);_class5.prototype.__init23.call(this);_class5.prototype.__init24.call(this);_class5.prototype.__init25.call(this);_class5.prototype.__init26.call(this);_class5.prototype.__init27.call(this);_class5.prototype.__init28.call(this);_class5.prototype.__init29.call(this);_class5.prototype.__init30.call(this);_class5.prototype.__init31.call(this);_class5.prototype.__init32.call(this);_class5.prototype.__init33.call(this);_class5.prototype.__init34.call(this);_class5.prototype.__init35.call(this);_class5.prototype.__init36.call(this);
4106
2443
  this.source = source;
4107
2444
  this.options = options;
4108
2445
  this.canvas = canvas2;
4109
- this.ctx = options.context !== void 0 ? options.context : _nullishCoalesce(_optionalChain([canvas2, 'access', _124 => _124.getContext, 'optionalCall', _125 => _125("2d")]), () => ( null));
2446
+ this.ctx = options.context !== void 0 ? options.context : _nullishCoalesce(_optionalChain([canvas2, 'access', _59 => _59.getContext, 'optionalCall', _60 => _60("2d")]), () => ( null));
4110
2447
  this.unsubscribe = source.subscribe(() => this.update());
4111
2448
  this.lutCache = options.luts !== void 0 ? options.luts : _nullishCoalesce(source.luts, () => ( null));
4112
2449
  this.unsubscribeLuts = this.lutCache ? this.lutCache.subscribe(() => this.redraw()) : () => {
@@ -4124,29 +2461,29 @@ var VideoPreviewEngine = (_class6 = class {
4124
2461
 
4125
2462
 
4126
2463
 
4127
- __init25() {this.media = /* @__PURE__ */ new Map()}
4128
- __init26() {this.images = /* @__PURE__ */ new Map()}
2464
+ __init21() {this.media = /* @__PURE__ */ new Map()}
2465
+ __init22() {this.images = /* @__PURE__ */ new Map()}
4129
2466
 
4130
- __init27() {this.scheduled = false}
4131
- __init28() {this.destroyed = false}
4132
- __init29() {this.filterHost = null}
4133
- __init30() {this.filters = /* @__PURE__ */ new Set()}
2467
+ __init23() {this.scheduled = false}
2468
+ __init24() {this.destroyed = false}
2469
+ __init25() {this.filterHost = null}
2470
+ __init26() {this.filters = /* @__PURE__ */ new Set()}
4134
2471
 
4135
2472
 
4136
- __init31() {this.drawn = null}
4137
- __init32() {this.complete = true}
2473
+ __init27() {this.drawn = null}
2474
+ __init28() {this.complete = true}
4138
2475
 
4139
2476
 
4140
2477
 
4141
- __init33() {this.srcFor = (layer) => layer.src}
4142
- __init34() {this.playing = false}
4143
- __init35() {this.fps = 30}
2478
+ __init29() {this.srcFor = (layer) => layer.src}
2479
+ __init30() {this.playing = false}
2480
+ __init31() {this.fps = 30}
4144
2481
 
4145
- __init36() {this.scratchCanvases = /* @__PURE__ */ new Map()}
2482
+ __init32() {this.scratchCanvases = /* @__PURE__ */ new Map()}
4146
2483
  /** The canvas's pixels and the frame they show, or `null` when its context cannot be read. */
4147
2484
  readPixels() {
4148
2485
  const ctx = this.ctx;
4149
- if (!_optionalChain([ctx, 'optionalAccess', _126 => _126.getImageData]) || !this.drawn) return null;
2486
+ if (!_optionalChain([ctx, 'optionalAccess', _61 => _61.getImageData]) || !this.drawn) return null;
4150
2487
  return { ...this.drawn, pixels: ctx.getImageData(0, 0, this.canvas.width, this.canvas.height) };
4151
2488
  }
4152
2489
  createMedia(kind) {
@@ -4184,8 +2521,8 @@ var VideoPreviewEngine = (_class6 = class {
4184
2521
  entry.media.pause();
4185
2522
  entry.media.removeEventListener("seeked", entry.onReady);
4186
2523
  entry.media.removeEventListener("loadeddata", entry.onReady);
4187
- _optionalChain([entry, 'access', _127 => _127.media, 'access', _128 => _128.removeAttribute, 'optionalCall', _129 => _129("src")]);
4188
- _optionalChain([entry, 'access', _130 => _130.media, 'access', _131 => _131.load, 'optionalCall', _132 => _132()]);
2524
+ _optionalChain([entry, 'access', _62 => _62.media, 'access', _63 => _63.removeAttribute, 'optionalCall', _64 => _64("src")]);
2525
+ _optionalChain([entry, 'access', _65 => _65.media, 'access', _66 => _66.load, 'optionalCall', _67 => _67()]);
4189
2526
  this.media.delete(entry.clip);
4190
2527
  }
4191
2528
  /** Brings the media elements in step with the editor and draws the frame. */
@@ -4193,13 +2530,13 @@ var VideoPreviewEngine = (_class6 = class {
4193
2530
  if (this.destroyed) return;
4194
2531
  const state = this.source.getState();
4195
2532
  const { project, time, playing } = state;
4196
- const frame = videoFrameAt(project, time);
2533
+ const frame = _chunkSMJGE7HMcjs.videoFrameAt.call(void 0, project, time);
4197
2534
  const scale = resolveVideoPreviewScale(_nullishCoalesce(this.options.previewScale, () => ( state.previewScale)), this.resolutionFor(project.width));
4198
2535
  const assets = new Map(project.assets.map((a) => [a.id, a]));
4199
2536
  this.srcFor = (layer) => videoPreviewSrc(assets.get(_nullishCoalesce(layer.asset, () => ( ""))), scale, layer.src);
4200
2537
  this.playing = playing;
4201
2538
  this.fps = project.fps;
4202
- _optionalChain([this, 'access', _133 => _133.frameCache, 'optionalAccess', _134 => _134.update, 'call', _135 => _135(project, time, { playing, srcFor: this.srcFor })]);
2539
+ _optionalChain([this, 'access', _68 => _68.frameCache, 'optionalAccess', _69 => _69.update, 'call', _70 => _70(project, time, { playing, srcFor: this.srcFor })]);
4203
2540
  const tolerance = (_nullishCoalesce(this.options.tolerance, () => ( 200))) / 1e3;
4204
2541
  const active = /* @__PURE__ */ new Set();
4205
2542
  const sync = (clip, src, kind, sourceTime, speed, gain) => {
@@ -4223,10 +2560,10 @@ var VideoPreviewEngine = (_class6 = class {
4223
2560
  }
4224
2561
  };
4225
2562
  const sounding = new Map(frame.audio.map((a) => [a.clip, a]));
4226
- for (const layer of videoFrameLeafLayers(frame.layers)) {
2563
+ for (const layer of _chunkSMJGE7HMcjs.videoFrameLeafLayers.call(void 0, frame.layers)) {
4227
2564
  if (layer.type !== "media" || layer.assetKind !== "video" || !layer.src) continue;
4228
2565
  const sound = sounding.get(layer.clip);
4229
- sync(layer.clip, this.srcFor(layer), "video", _nullishCoalesce(layer.sourceTime, () => ( 0)), _nullishCoalesce(_optionalChain([sound, 'optionalAccess', _136 => _136.speed]), () => ( 1)), sound ? sound.gain : null);
2566
+ sync(layer.clip, this.srcFor(layer), "video", _nullishCoalesce(layer.sourceTime, () => ( 0)), _nullishCoalesce(_optionalChain([sound, 'optionalAccess', _71 => _71.speed]), () => ( 1)), sound ? sound.gain : null);
4230
2567
  sounding.delete(layer.clip);
4231
2568
  }
4232
2569
  for (const a of sounding.values()) sync(a.clip, a.src, "audio", a.sourceTime, a.speed, a.gain);
@@ -4238,10 +2575,10 @@ var VideoPreviewEngine = (_class6 = class {
4238
2575
  this.scheduled = true;
4239
2576
  queueMicrotask(() => {
4240
2577
  this.scheduled = false;
4241
- if (!this.destroyed) this.draw(videoFrameAt(this.source.getState().project, this.source.getState().time));
2578
+ if (!this.destroyed) this.draw(_chunkSMJGE7HMcjs.videoFrameAt.call(void 0, this.source.getState().project, this.source.getState().time));
4242
2579
  });
4243
2580
  }
4244
- __init37() {this.sourceFor = (layer) => {
2581
+ __init33() {this.sourceFor = (layer) => {
4245
2582
  if (layer.assetKind === "image" && layer.src) {
4246
2583
  const img = this.image(layer.src);
4247
2584
  return img.complete ? img : null;
@@ -4249,7 +2586,7 @@ var VideoPreviewEngine = (_class6 = class {
4249
2586
  const entry = this.media.get(layer.clip);
4250
2587
  const ready = !!entry && entry.media.readyState >= 2;
4251
2588
  const cache = this.frameCache;
4252
- const src = _nullishCoalesce(_optionalChain([entry, 'optionalAccess', _137 => _137.src]), () => ( this.srcFor(layer)));
2589
+ const src = _nullishCoalesce(_optionalChain([entry, 'optionalAccess', _72 => _72.src]), () => ( this.srcFor(layer)));
4253
2590
  const sourceTime = _nullishCoalesce(layer.sourceTime, () => ( 0));
4254
2591
  if (cache && src && (!this.playing || !ready || entry.media.seeking)) {
4255
2592
  const hit = cache.get(src, sourceTime, this.fps);
@@ -4280,18 +2617,18 @@ var VideoPreviewEngine = (_class6 = class {
4280
2617
  if (!source) this.complete = false;
4281
2618
  return source;
4282
2619
  };
4283
- drawVideoFrame(this.ctx, frame, sourceFor, resolution, { defineSvgFilter: this.defineSvgFilter, lutFor: this.lutFor, scratch: this.scratch });
2620
+ _chunkSMJGE7HMcjs.drawVideoFrame.call(void 0, this.ctx, frame, sourceFor, resolution, { defineSvgFilter: this.defineSvgFilter, lutFor: this.lutFor, scratch: this.scratch });
4284
2621
  this.drawn = { time: frame.time, complete: this.complete };
4285
- _optionalChain([this, 'access', _138 => _138.options, 'access', _139 => _139.onFrame, 'optionalCall', _140 => _140(frame)]);
4286
- _optionalChain([this, 'access', _141 => _141.pixelsTarget, 'optionalAccess', _142 => _142.drawn, 'call', _143 => _143(frame.time, this.complete)]);
2622
+ _optionalChain([this, 'access', _73 => _73.options, 'access', _74 => _74.onFrame, 'optionalCall', _75 => _75(frame)]);
2623
+ _optionalChain([this, 'access', _76 => _76.pixelsTarget, 'optionalAccess', _77 => _77.drawn, 'call', _78 => _78(frame.time, this.complete)]);
4287
2624
  }
4288
2625
  /** A grade's LUT once loaded; while it loads (or if it fails) the frame is drawn without it and counts as incomplete. */
4289
- __init38() {this.lutFor = (src) => {
4290
- const lut = _optionalChain([this, 'access', _144 => _144.lutCache, 'optionalAccess', _145 => _145.get, 'call', _146 => _146(src)]);
2626
+ __init34() {this.lutFor = (src) => {
2627
+ const lut = _optionalChain([this, 'access', _79 => _79.lutCache, 'optionalAccess', _80 => _80.get, 'call', _81 => _81(src)]);
4291
2628
  if (!lut) this.complete = false;
4292
2629
  return lut;
4293
2630
  }}
4294
- __init39() {this.scratch = (width, height, slot) => {
2631
+ __init35() {this.scratch = (width, height, slot) => {
4295
2632
  if (!this.scratchCanvases.has(slot)) {
4296
2633
  let made = null;
4297
2634
  if (this.options.createScratch) made = this.options.createScratch(width, height, slot);
@@ -4309,7 +2646,7 @@ var VideoPreviewEngine = (_class6 = class {
4309
2646
  if (c.height !== height) c.height = height;
4310
2647
  return s;
4311
2648
  }}
4312
- __init40() {this.defineSvgFilter = (id, markup) => {
2649
+ __init36() {this.defineSvgFilter = (id, markup) => {
4313
2650
  if (this.options.defineSvgFilter === null) return;
4314
2651
  if (this.options.defineSvgFilter) return this.options.defineSvgFilter(id, markup);
4315
2652
  if (this.filters.has(id) || typeof document === "undefined") return;
@@ -4331,50 +2668,50 @@ var VideoPreviewEngine = (_class6 = class {
4331
2668
  this.detachPixels();
4332
2669
  this.unsubscribeLuts();
4333
2670
  this.unsubscribeFrames();
4334
- _optionalChain([this, 'access', _147 => _147.filterHost, 'optionalAccess', _148 => _148.remove, 'call', _149 => _149()]);
2671
+ _optionalChain([this, 'access', _82 => _82.filterHost, 'optionalAccess', _83 => _83.remove, 'call', _84 => _84()]);
4335
2672
  this.filterHost = null;
4336
2673
  this.filters.clear();
4337
2674
  this.unsubscribe();
4338
2675
  for (const entry of [...this.media.values()]) this.release(entry);
4339
2676
  this.images.clear();
4340
2677
  }
4341
- }, _class6);
2678
+ }, _class5);
4342
2679
 
4343
2680
  // src/video-editor/controller.ts
4344
- var VideoEditorController = (_class7 = class {
2681
+ var VideoEditorController = (_class6 = class {
4345
2682
 
4346
2683
 
4347
2684
  /** Sound levels per asset, loaded on demand for waveforms and agents. */
4348
2685
 
4349
2686
  /** The pixels the preview last drew, for scopes and `get_video_scopes` (a `VideoPreviewEngine` fills it). */
4350
- __init41() {this.previewPixels = new VideoPreviewPixels()}
2687
+ __init37() {this.previewPixels = new VideoPreviewPixels()}
4351
2688
  /** Grades' LUTs, loaded on demand for the preview, SVG frames and agents. */
4352
2689
 
4353
2690
  /** The host's frame cache, if it gave one (the preview fills and reads it). */
4354
2691
 
4355
2692
 
4356
2693
 
4357
- __init42() {this.selection = []}
2694
+ __init38() {this.selection = []}
4358
2695
 
4359
2696
 
4360
- __init43() {this.error = null}
4361
- __init44() {this.revision = 0}
4362
- __init45() {this.applying = false}
2697
+ __init39() {this.error = null}
2698
+ __init40() {this.revision = 0}
2699
+ __init41() {this.applying = false}
4363
2700
 
4364
- __init46() {this.listeners = /* @__PURE__ */ new Set()}
4365
- __init47() {this.unsubscribers = []}
2701
+ __init42() {this.listeners = /* @__PURE__ */ new Set()}
2702
+ __init43() {this.unsubscribers = []}
4366
2703
 
4367
- constructor(options = {}) {;_class7.prototype.__init41.call(this);_class7.prototype.__init42.call(this);_class7.prototype.__init43.call(this);_class7.prototype.__init44.call(this);_class7.prototype.__init45.call(this);_class7.prototype.__init46.call(this);_class7.prototype.__init47.call(this);_class7.prototype.__init48.call(this);_class7.prototype.__init49.call(this);
2704
+ constructor(options = {}) {;_class6.prototype.__init37.call(this);_class6.prototype.__init38.call(this);_class6.prototype.__init39.call(this);_class6.prototype.__init40.call(this);_class6.prototype.__init41.call(this);_class6.prototype.__init42.call(this);_class6.prototype.__init43.call(this);_class6.prototype.__init44.call(this);_class6.prototype.__init45.call(this);
4368
2705
  this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
4369
2706
  this.audioPeaks = options.audioPeaks instanceof VideoAudioPeaksCache ? options.audioPeaks : new VideoAudioPeaksCache(options.audioPeaks);
4370
- this.luts = options.luts instanceof VideoLutCache ? options.luts : new VideoLutCache(options.luts);
2707
+ this.luts = options.luts instanceof _chunkSMJGE7HMcjs.VideoLutCache ? options.luts : new (0, _chunkSMJGE7HMcjs.VideoLutCache)(options.luts);
4371
2708
  this.frameCache = _nullishCoalesce(options.frameCache, () => ( null));
4372
2709
  this.project = options.project ? parseVideoProject(options.project) : createVideoProject();
4373
2710
  this.issues = checkVideoProject(this.project);
4374
2711
  this.readOnly = _nullishCoalesce(options.readOnly, () => ( false));
4375
2712
  this.previewScale = checkPreviewScale(_nullishCoalesce(options.previewScale, () => ( "auto")));
4376
2713
  this.onChange = options.onChange;
4377
- this.player = new (0, _chunk477UVMNRcjs.AnimationPlayer)({ clock: options.clock, duration: () => videoProjectDuration(this.project), loop: "none" });
2714
+ this.player = new (0, _chunk477UVMNRcjs.AnimationPlayer)({ clock: options.clock, duration: () => _chunkSMJGE7HMcjs.videoProjectDuration.call(void 0, this.project), loop: "none" });
4378
2715
  this.state = this.computeState();
4379
2716
  this.connect();
4380
2717
  }
@@ -4392,8 +2729,8 @@ var VideoEditorController = (_class7 = class {
4392
2729
  this.player.pause();
4393
2730
  this.unsubscribers.splice(0).forEach((u) => u());
4394
2731
  }
4395
- __init48() {this.getState = () => this.state}
4396
- __init49() {this.subscribe = (listener) => {
2732
+ __init44() {this.getState = () => this.state}
2733
+ __init45() {this.subscribe = (listener) => {
4397
2734
  this.connect();
4398
2735
  this.listeners.add(listener);
4399
2736
  return () => {
@@ -4409,12 +2746,12 @@ var VideoEditorController = (_class7 = class {
4409
2746
  const h = this.history.getState();
4410
2747
  return {
4411
2748
  project: this.project,
4412
- duration: videoProjectDuration(this.project),
2749
+ duration: _chunkSMJGE7HMcjs.videoProjectDuration.call(void 0, this.project),
4413
2750
  time: p.time,
4414
2751
  playing: p.playing,
4415
2752
  loop: p.loop,
4416
2753
  selection: this.selection,
4417
- current: videoClipsAt(this.project, p.time).map((c) => c.clip.id),
2754
+ current: _chunkSMJGE7HMcjs.videoClipsAt.call(void 0, this.project, p.time).map((c) => c.clip.id),
4418
2755
  issues: this.issues,
4419
2756
  readOnly: this.readOnly,
4420
2757
  error: this.error,
@@ -4445,12 +2782,12 @@ var VideoEditorController = (_class7 = class {
4445
2782
  this.issues = checkVideoProject(project);
4446
2783
  const ids = new Set(project.tracks.flatMap((t) => t.clips.map((c) => c.id)));
4447
2784
  if (this.selection.some((id) => !ids.has(id))) this.selection = this.selection.filter((id) => ids.has(id));
4448
- this.player.setDuration(() => videoProjectDuration(this.project));
2785
+ this.player.setDuration(() => _chunkSMJGE7HMcjs.videoProjectDuration.call(void 0, this.project));
4449
2786
  } finally {
4450
2787
  this.applying = false;
4451
2788
  }
4452
2789
  this.notify();
4453
- _optionalChain([this, 'access', _150 => _150.onChange, 'optionalCall', _151 => _151(project)]);
2790
+ _optionalChain([this, 'access', _85 => _85.onChange, 'optionalCall', _86 => _86(project)]);
4454
2791
  }
4455
2792
  commit(label, next) {
4456
2793
  const before = this.project;
@@ -4525,7 +2862,7 @@ var VideoEditorController = (_class7 = class {
4525
2862
  }
4526
2863
  /** Selects clips (unknown ids are dropped). `add` toggles them into the current selection. */
4527
2864
  select(clips, options = {}) {
4528
- const known = _nullishCoalesce(_optionalChain([clips, 'optionalAccess', _152 => _152.filter, 'call', _153 => _153((id) => findVideoClip(this.project, id))]), () => ( []));
2865
+ const known = _nullishCoalesce(_optionalChain([clips, 'optionalAccess', _87 => _87.filter, 'call', _88 => _88((id) => _chunkSMJGE7HMcjs.findVideoClip.call(void 0, this.project, id))]), () => ( []));
4529
2866
  let next;
4530
2867
  if (options.add) {
4531
2868
  next = [...this.selection];
@@ -4552,7 +2889,7 @@ var VideoEditorController = (_class7 = class {
4552
2889
  step(frames) {
4553
2890
  this.player.pause();
4554
2891
  const fps = this.project.fps;
4555
- this.player.seek(Math.max(0, snapToVideoFrame(this.player.currentTime, fps) + frames * 1e3 / fps));
2892
+ this.player.seek(Math.max(0, _chunkSMJGE7HMcjs.snapToVideoFrame.call(void 0, this.player.currentTime, fps) + frames * 1e3 / fps));
4556
2893
  }
4557
2894
  setLoop(loop) {
4558
2895
  this.player.setLoop(loop);
@@ -4569,14 +2906,14 @@ var VideoEditorController = (_class7 = class {
4569
2906
  */
4570
2907
  snap(time, options = {}) {
4571
2908
  const threshold = _nullishCoalesce(options.threshold, () => ( 8 * 1e3 / this.project.fps));
4572
- const points = videoSnapPoints(this.project, { exclude: options.exclude, playhead: options.playhead === false ? void 0 : this.player.currentTime });
4573
- const hit = snapVideoTime(time, points, threshold);
4574
- return hit.snapped !== null ? hit : { time: Math.max(0, snapToVideoFrame(time, this.project.fps)), snapped: null };
2909
+ const points = _chunkSMJGE7HMcjs.videoSnapPoints.call(void 0, this.project, { exclude: options.exclude, playhead: options.playhead === false ? void 0 : this.player.currentTime });
2910
+ const hit = _chunkSMJGE7HMcjs.snapVideoTime.call(void 0, time, points, threshold);
2911
+ return hit.snapped !== null ? hit : { time: Math.max(0, _chunkSMJGE7HMcjs.snapToVideoFrame.call(void 0, time, this.project.fps)), snapped: null };
4575
2912
  }
4576
2913
  /** Splits the selected clips (or every clip) under the playhead there, as one undo step. */
4577
2914
  splitAtPlayhead() {
4578
- const t = snapToVideoFrame(this.player.currentTime, this.project.fps);
4579
- const under = videoClipsAt(this.project, t).filter(({ clip }) => t > clip.start && t < videoClipEnd(clip));
2915
+ const t = _chunkSMJGE7HMcjs.snapToVideoFrame.call(void 0, this.player.currentTime, this.project.fps);
2916
+ const under = _chunkSMJGE7HMcjs.videoClipsAt.call(void 0, this.project, t).filter(({ clip }) => t > clip.start && t < _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, clip));
4580
2917
  const chosen = this.selection.length ? under.filter(({ clip }) => this.selection.includes(clip.id)) : under;
4581
2918
  if (!chosen.length) return this.refuse("No clip is under the playhead to split.");
4582
2919
  return this.apply(chosen.map(({ clip }) => ({ op: "split_clip", clip: clip.id, at: t })), "Split");
@@ -4588,25 +2925,25 @@ var VideoEditorController = (_class7 = class {
4588
2925
  }
4589
2926
  /** What plays at `time` (default: the playhead). */
4590
2927
  frame(time = this.player.currentTime) {
4591
- return videoFrameAt(this.project, time);
2928
+ return _chunkSMJGE7HMcjs.videoFrameAt.call(void 0, this.project, time);
4592
2929
  }
4593
2930
  /** The frame at `time` (default: the playhead) as SVG. */
4594
2931
  toSvg(time = this.player.currentTime, options = {}) {
4595
- return videoProjectSvg(this.project, time, { lutFor: (src) => this.luts.peek(src), ...options });
2932
+ return _chunkSMJGE7HMcjs.videoProjectSvg.call(void 0, this.project, time, { lutFor: (src) => this.luts.peek(src), ...options });
4596
2933
  }
4597
2934
  /** The project as an ffmpeg command. */
4598
2935
  toFfmpeg(options = {}) {
4599
- return toVideoFfmpeg(this.project, options);
2936
+ return _chunkSMJGE7HMcjs.toVideoFfmpeg.call(void 0, this.project, options);
4600
2937
  }
4601
2938
  /** The ffmpeg command that measures the mix's loudness (see `toVideoLoudnessMeasure`). */
4602
2939
  toLoudnessMeasure(options = {}) {
4603
- return toVideoLoudnessMeasure(this.project, options);
2940
+ return _chunkSMJGE7HMcjs.toVideoLoudnessMeasure.call(void 0, this.project, options);
4604
2941
  }
4605
2942
  /** One track as a CMX 3600 EDL. */
4606
2943
  toEdl(options = {}) {
4607
- return toVideoEdl(this.project, options);
2944
+ return _chunkSMJGE7HMcjs.toVideoEdl.call(void 0, this.project, options);
4608
2945
  }
4609
- }, _class7);
2946
+ }, _class6);
4610
2947
  function checkPreviewScale(scale) {
4611
2948
  if (scale === "auto" || scale === "full") return scale;
4612
2949
  if (typeof scale !== "number" || !(scale > 0 && scale <= 1)) throw new Error(`A preview scale is "auto", "full" or a fraction above 0 up to 1 (0.5 is half size), not ${JSON.stringify(scale)}.`);
@@ -4623,12 +2960,12 @@ function videoRulerTicks(duration, pxPerSecond, fps) {
4623
2960
  const ticks = [];
4624
2961
  for (let t = 0; t <= duration + 1e-6; t += step) {
4625
2962
  const isMajor = Math.abs(t / major - Math.round(t / major)) < 1e-9;
4626
- ticks.push({ time: t, x: t * pxPerMs, major: isMajor, ...isMajor ? { label: formatVideoTimecode(t, fps).replace(/^00:/, "") } : {} });
2963
+ ticks.push({ time: t, x: t * pxPerMs, major: isMajor, ...isMajor ? { label: _chunkSMJGE7HMcjs.formatVideoTimecode.call(void 0, t, fps).replace(/^00:/, "") } : {} });
4627
2964
  }
4628
2965
  return ticks;
4629
2966
  }
4630
2967
  function videoClipShowsWaveform(clip, asset, mode = "audio") {
4631
- if (clip.type !== "media" || mode === "none" || !videoAssetHasSound(asset)) return false;
2968
+ if (clip.type !== "media" || mode === "none" || !_chunkSMJGE7HMcjs.videoAssetHasSound.call(void 0, asset)) return false;
4632
2969
  return mode === "all" || asset.kind === "audio";
4633
2970
  }
4634
2971
  var VIDEO_CLIP_ANIMATION_LABELS = {
@@ -4682,13 +3019,13 @@ function videoClipLabel(clip, asset, sequence) {
4682
3019
  if (clip.name) return clip.name;
4683
3020
  if (clip.type === "text") return clip.text.split("\n")[0];
4684
3021
  if (clip.type === "color") return clip.color;
4685
- if (clip.type === "sequence") return `Compound: ${_nullishCoalesce(_optionalChain([sequence, 'optionalAccess', _154 => _154.name]), () => ( clip.sequence))}`;
4686
- const base = _nullishCoalesce(_nullishCoalesce(_optionalChain([asset, 'optionalAccess', _155 => _155.name]), () => ( _optionalChain([asset, 'optionalAccess', _156 => _156.src, 'access', _157 => _157.replace, 'call', _158 => _158(/^.*[\\/]/, "")]))), () => ( clip.asset));
4687
- const speed = videoClipSpeed(clip);
3022
+ if (clip.type === "sequence") return `Compound: ${_nullishCoalesce(_optionalChain([sequence, 'optionalAccess', _89 => _89.name]), () => ( clip.sequence))}`;
3023
+ const base = _nullishCoalesce(_nullishCoalesce(_optionalChain([asset, 'optionalAccess', _90 => _90.name]), () => ( _optionalChain([asset, 'optionalAccess', _91 => _91.src, 'access', _92 => _92.replace, 'call', _93 => _93(/^.*[\\/]/, "")]))), () => ( clip.asset));
3024
+ const speed = _chunkSMJGE7HMcjs.videoClipSpeed.call(void 0, clip);
4688
3025
  return speed !== 1 ? `${base} (${Math.round(speed * 100)}%)` : base;
4689
3026
  }
4690
3027
  function planVideoClipDrag(project, input) {
4691
- const at = findVideoClip(project, input.clip);
3028
+ const at = _chunkSMJGE7HMcjs.findVideoClip.call(void 0, project, input.clip);
4692
3029
  if (!at) return null;
4693
3030
  const { clip, track } = at;
4694
3031
  const snap = _nullishCoalesce(input.snap, () => ( ((t) => t)));
@@ -4705,17 +3042,17 @@ function planVideoClipDrag(project, input) {
4705
3042
  }
4706
3043
  const minimum = 1e3 / project.fps;
4707
3044
  if (input.mode === "trim-start") {
4708
- const to2 = Math.min(snap(Math.max(0, clip.start + input.delta)), videoClipEnd(clip) - minimum);
3045
+ const to2 = Math.min(snap(Math.max(0, clip.start + input.delta)), _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, clip) - minimum);
4709
3046
  const changed2 = Math.abs(to2 - clip.start) > 1e-6;
4710
- const plan = input.ripple ? { start: clip.start, duration: clip.duration - (to2 - clip.start) } : { start: to2, duration: videoClipEnd(clip) - to2 };
3047
+ const plan = input.ripple ? { start: clip.start, duration: clip.duration - (to2 - clip.start) } : { start: to2, duration: _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, clip) - to2 };
4711
3048
  return { ...plan, track: track.id, edit: changed2 ? { op: "trim_clip", clip: clip.id, edge: "start", to: to2, ...input.ripple ? { ripple: true } : {} } : null };
4712
3049
  }
4713
- const to = Math.max(snap(videoClipEnd(clip) + input.delta), clip.start + minimum);
4714
- const changed = Math.abs(to - videoClipEnd(clip)) > 1e-6;
3050
+ const to = Math.max(snap(_chunkSMJGE7HMcjs.videoClipEnd.call(void 0, clip) + input.delta), clip.start + minimum);
3051
+ const changed = Math.abs(to - _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, clip)) > 1e-6;
4715
3052
  return { start: clip.start, duration: to - clip.start, track: track.id, edit: changed ? { op: "trim_clip", clip: clip.id, edge: "end", to, ...input.ripple ? { ripple: true } : {} } : null };
4716
3053
  }
4717
3054
  function videoDropTracks(project, clipId) {
4718
- const at = findVideoClip(project, clipId);
3055
+ const at = _chunkSMJGE7HMcjs.findVideoClip.call(void 0, project, clipId);
4719
3056
  if (!at) return [];
4720
3057
  return project.tracks.filter((t) => t.kind === at.track.kind && !t.locked).map((t) => t.id);
4721
3058
  }
@@ -4724,10 +3061,10 @@ function videoDragLabel(edit) {
4724
3061
  return edit.op === "trim_clip" && edit.ripple ? "Ripple trim" : "Trim clip";
4725
3062
  }
4726
3063
  function videoAssetDropEdit(project, assetId, options = {}) {
4727
- const asset = videoAssetMap(project).get(assetId);
3064
+ const asset = _chunkSMJGE7HMcjs.videoAssetMap.call(void 0, project).get(assetId);
4728
3065
  if (!asset) return null;
4729
3066
  const kind = asset.kind === "audio" ? "audio" : "video";
4730
- const track = _nullishCoalesce(options.track, () => ( _optionalChain([project, 'access', _159 => _159.tracks, 'access', _160 => _160.find, 'call', _161 => _161((t) => t.kind === kind && !t.locked), 'optionalAccess', _162 => _162.id])));
3067
+ const track = _nullishCoalesce(options.track, () => ( _optionalChain([project, 'access', _94 => _94.tracks, 'access', _95 => _95.find, 'call', _96 => _96((t) => t.kind === kind && !t.locked), 'optionalAccess', _97 => _97.id])));
4731
3068
  if (!track) return null;
4732
3069
  return { op: "add_clip", track, clip: { type: "media", asset: asset.id, ...options.start !== void 0 ? { start: options.start } : {} }, ...options.ripple ? { ripple: true } : {}, ...options.mode && options.mode !== "refuse" ? { mode: options.mode } : {} };
4733
3070
  }
@@ -4776,25 +3113,25 @@ var KEY_LABELS = {
4776
3113
  scale: { label: "Scale", step: 0.05 },
4777
3114
  rotation: { label: "Rotation", step: 1 }
4778
3115
  };
4779
- var animatableOf = (clip, property) => VIDEO_TRANSFORM_PROPERTIES.includes(property) ? _optionalChain([clip, 'access', _163 => _163.transform, 'optionalAccess', _164 => _164[property]]) : property === "volume" ? clip.type === "media" || clip.type === "sequence" ? clip.volume : void 0 : clip.opacity;
3116
+ var animatableOf = (clip, property) => _chunkSMJGE7HMcjs.VIDEO_TRANSFORM_PROPERTIES.includes(property) ? _optionalChain([clip, 'access', _98 => _98.transform, 'optionalAccess', _99 => _99[property]]) : property === "volume" ? clip.type === "media" || clip.type === "sequence" ? clip.volume : void 0 : clip.opacity;
4780
3117
  var playheadInClip = (clip, time) => Math.round(Math.min(clip.duration, Math.max(0, time - clip.start)));
4781
3118
  function videoKeyframeRows(project, clipId, time) {
4782
- const at = findVideoClip(project, clipId);
3119
+ const at = _chunkSMJGE7HMcjs.findVideoClip.call(void 0, project, clipId);
4783
3120
  if (!at) return [];
4784
3121
  const { clip } = at;
4785
- const asset = clip.type === "media" ? videoAssetMap(project).get(clip.asset) : void 0;
4786
- const visual = !(_optionalChain([asset, 'optionalAccess', _165 => _165.kind]) === "audio" || at.track.kind === "audio");
4787
- const audible = clip.type === "media" && videoAssetHasSound(asset) || clip.type === "sequence";
4788
- const properties = [...visual ? ["opacity"] : [], ...audible ? ["volume"] : [], ...visual ? VIDEO_TRANSFORM_PROPERTIES : []];
3122
+ const asset = clip.type === "media" ? _chunkSMJGE7HMcjs.videoAssetMap.call(void 0, project).get(clip.asset) : void 0;
3123
+ const visual = !(_optionalChain([asset, 'optionalAccess', _100 => _100.kind]) === "audio" || at.track.kind === "audio");
3124
+ const audible = clip.type === "media" && _chunkSMJGE7HMcjs.videoAssetHasSound.call(void 0, asset) || clip.type === "sequence";
3125
+ const properties = [...visual ? ["opacity"] : [], ...audible ? ["volume"] : [], ...visual ? _chunkSMJGE7HMcjs.VIDEO_TRANSFORM_PROPERTIES : []];
4789
3126
  const clipTime = playheadInClip(clip, time);
4790
3127
  return properties.map((property) => {
4791
3128
  const prop = animatableOf(clip, property);
4792
- const keys = isVideoKeyframes(prop) ? prop.keys : [];
4793
- const limits = VIDEO_KEYFRAME_LIMITS[property];
3129
+ const keys = _chunkSMJGE7HMcjs.isVideoKeyframes.call(void 0, prop) ? prop.keys : [];
3130
+ const limits = _chunkSMJGE7HMcjs.VIDEO_KEYFRAME_LIMITS[property];
4794
3131
  return {
4795
3132
  property,
4796
3133
  ...KEY_LABELS[property],
4797
- value: Math.round(videoValueAt(prop, clipTime, limits.default) * 1e3) / 1e3,
3134
+ value: Math.round(_chunkSMJGE7HMcjs.videoValueAt.call(void 0, prop, clipTime, limits.default) * 1e3) / 1e3,
4798
3135
  keyed: keys.length > 0,
4799
3136
  keys: keys.length,
4800
3137
  onKey: keys.some((k) => Math.abs(k.t - clipTime) < 0.5),
@@ -4805,44 +3142,44 @@ function videoKeyframeRows(project, clipId, time) {
4805
3142
  });
4806
3143
  }
4807
3144
  function plainValueEdit(clip, property, value) {
4808
- if (!VIDEO_TRANSFORM_PROPERTIES.includes(property))
3145
+ if (!_chunkSMJGE7HMcjs.VIDEO_TRANSFORM_PROPERTIES.includes(property))
4809
3146
  return value === void 0 ? { op: "update_clip", clip: clip.id, remove: [property] } : { op: "update_clip", clip: clip.id, set: { [property]: value } };
4810
3147
  const transform = { ...clip.transform, [property]: value };
4811
3148
  if (value === void 0) delete transform[property];
4812
3149
  return Object.keys(transform).length ? { op: "update_clip", clip: clip.id, set: { transform } } : { op: "update_clip", clip: clip.id, remove: ["transform"] };
4813
3150
  }
4814
3151
  function videoKeyframeValueEdit(project, clipId, property, time, value) {
4815
- const at = findVideoClip(project, clipId);
3152
+ const at = _chunkSMJGE7HMcjs.findVideoClip.call(void 0, project, clipId);
4816
3153
  if (!at) return null;
4817
3154
  const prop = animatableOf(at.clip, property);
4818
- if (!isVideoKeyframes(prop)) return plainValueEdit(at.clip, property, value);
3155
+ if (!_chunkSMJGE7HMcjs.isVideoKeyframes.call(void 0, prop)) return plainValueEdit(at.clip, property, value);
4819
3156
  if (value === void 0) return null;
4820
- return { op: "set_keyframes", clip: clipId, property, keys: setVideoKey(prop, { t: playheadInClip(at.clip, time), v: value }, VIDEO_KEYFRAME_LIMITS[property].default).keys };
3157
+ return { op: "set_keyframes", clip: clipId, property, keys: _chunkSMJGE7HMcjs.setVideoKey.call(void 0, prop, { t: playheadInClip(at.clip, time), v: value }, _chunkSMJGE7HMcjs.VIDEO_KEYFRAME_LIMITS[property].default).keys };
4821
3158
  }
4822
3159
  function videoKeyframeToggleEdit(project, clipId, property, time) {
4823
- const at = findVideoClip(project, clipId);
3160
+ const at = _chunkSMJGE7HMcjs.findVideoClip.call(void 0, project, clipId);
4824
3161
  if (!at) return null;
4825
3162
  const prop = animatableOf(at.clip, property);
4826
3163
  const t = playheadInClip(at.clip, time);
4827
- const fallback = VIDEO_KEYFRAME_LIMITS[property].default;
4828
- if (isVideoKeyframes(prop)) {
3164
+ const fallback = _chunkSMJGE7HMcjs.VIDEO_KEYFRAME_LIMITS[property].default;
3165
+ if (_chunkSMJGE7HMcjs.isVideoKeyframes.call(void 0, prop)) {
4829
3166
  const key = prop.keys.find((k) => Math.abs(k.t - t) < 0.5);
4830
3167
  if (key) {
4831
- const rest = removeVideoKey(prop, key.t);
4832
- return isVideoKeyframes(rest) ? { op: "set_keyframes", clip: clipId, property, keys: rest.keys } : plainValueEdit(at.clip, property, rest === fallback ? void 0 : rest);
3168
+ const rest = _chunkSMJGE7HMcjs.removeVideoKey.call(void 0, prop, key.t);
3169
+ return _chunkSMJGE7HMcjs.isVideoKeyframes.call(void 0, rest) ? { op: "set_keyframes", clip: clipId, property, keys: rest.keys } : plainValueEdit(at.clip, property, rest === fallback ? void 0 : rest);
4833
3170
  }
4834
3171
  }
4835
- const v = videoValueAt(prop, t, fallback);
4836
- return { op: "set_keyframes", clip: clipId, property, keys: isVideoKeyframes(prop) ? setVideoKey(prop, { t, v }, fallback).keys : [{ t, v }] };
3172
+ const v = _chunkSMJGE7HMcjs.videoValueAt.call(void 0, prop, t, fallback);
3173
+ return { op: "set_keyframes", clip: clipId, property, keys: _chunkSMJGE7HMcjs.isVideoKeyframes.call(void 0, prop) ? _chunkSMJGE7HMcjs.setVideoKey.call(void 0, prop, { t, v }, fallback).keys : [{ t, v }] };
4837
3174
  }
4838
3175
  function videoLutEdits(project, clips, lut) {
4839
3176
  const edits = [];
4840
3177
  for (const id of clips) {
4841
- const at = findVideoClip(project, id);
3178
+ const at = _chunkSMJGE7HMcjs.findVideoClip.call(void 0, project, id);
4842
3179
  if (!at) continue;
4843
3180
  const { lut: current, ...rest } = _nullishCoalesce(at.clip.grade, () => ( {}));
4844
3181
  if (!lut && !current) continue;
4845
- const grade = normalizeVideoGrade(lut ? { ...rest, lut } : rest);
3182
+ const grade = _chunkSMJGE7HMcjs.normalizeVideoGrade.call(void 0, lut ? { ...rest, lut } : rest);
4846
3183
  edits.push(grade ? { op: "update_clip", clip: id, set: { grade } } : { op: "update_clip", clip: id, remove: ["grade"] });
4847
3184
  }
4848
3185
  return edits;
@@ -4872,7 +3209,7 @@ var RIPPLE = {
4872
3209
  };
4873
3210
  var MODE = {
4874
3211
  type: "string",
4875
- enum: [...VIDEO_PLACEMENT_MODES],
3212
+ enum: [..._chunkSMJGE7HMcjs.VIDEO_PLACEMENT_MODES],
4876
3213
  description: 'What to do with clips already where it lands: "refuse" (default) fails with the next free place; "overwrite" cuts, trims or removes what is underneath on that track; "insert" cuts everything across the start and pushes later material along by its length on this track and every sync-locked track.'
4877
3214
  };
4878
3215
  var DELTA = (what) => ({ type: "number", description: `${what}, in milliseconds of timeline time: positive is later, negative earlier. It is clamped to what the media and neighbours allow; the result says how far it went.` });
@@ -4889,11 +3226,11 @@ var TRANSFORM = object(
4889
3226
  );
4890
3227
  var TRANSITION = object(
4891
3228
  {
4892
- type: { type: "string", enum: [...VIDEO_TRANSITION_TYPES], description: "crossfade (this clip fades up over the one before), dip (down into a colour and up out of it), wipe (an edge travels across revealing this clip), push (this clip slides in and pushes the one before out), slide (this clip slides in over the one before) or iris (a circle opens from the centre)." },
3229
+ type: { type: "string", enum: [..._chunkSMJGE7HMcjs.VIDEO_TRANSITION_TYPES], description: "crossfade (this clip fades up over the one before), dip (down into a colour and up out of it), wipe (an edge travels across revealing this clip), push (this clip slides in and pushes the one before out), slide (this clip slides in over the one before) or iris (a circle opens from the centre)." },
4893
3230
  duration: { type: "number", exclusiveMinimum: 0, description: "Length in ms, centred on the cut. Every type but dip needs half of it in media past the cut on both sides (handles); a dip needs none." },
4894
3231
  color: COLOR("For dip: the colour passed through (default black)"),
4895
- direction: { type: "string", enum: [...VIDEO_WIPE_DIRECTIONS], description: "For wipe, push and slide: the way the edge or picture travels (default right, coming in from the left)." },
4896
- audioCurve: { type: "string", enum: [...VIDEO_FADE_CURVES], description: "Shape of the sound's fades through it: default equal-power for every type but dip (steady loudness while the two sounds overlap), linear for dip; or s-curve, exponential, logarithmic." }
3232
+ direction: { type: "string", enum: [..._chunkSMJGE7HMcjs.VIDEO_WIPE_DIRECTIONS], description: "For wipe, push and slide: the way the edge or picture travels (default right, coming in from the left)." },
3233
+ audioCurve: { type: "string", enum: [..._chunkSMJGE7HMcjs.VIDEO_FADE_CURVES], description: "Shape of the sound's fades through it: default equal-power for every type but dip (steady loudness while the two sounds overlap), linear for dip; or s-curve, exponential, logarithmic." }
4897
3234
  },
4898
3235
  ["type", "duration"],
4899
3236
  "A transition from the clip that ends where this one starts on the same track."
@@ -4902,7 +3239,7 @@ var LUT_SRC = { type: "string", minLength: 1, description: 'A .cube 3D LUT file
4902
3239
  var LUT_STRENGTH = { type: "number", minimum: 0, maximum: 1, description: "How much of the LUT's change to apply, 0 (none) to 1 (full, the default); 0.3-0.7 for a subtler look." };
4903
3240
  var GRADE = object(
4904
3241
  {
4905
- ...Object.fromEntries(VIDEO_GRADE_CONTROLS.map((c) => [c.key, { type: "number", minimum: c.min, maximum: c.max, description: c.description }])),
3242
+ ...Object.fromEntries(_chunkSMJGE7HMcjs.VIDEO_GRADE_CONTROLS.map((c) => [c.key, { type: "number", minimum: c.min, maximum: c.max, description: c.description }])),
4906
3243
  lut: object({ src: LUT_SRC, strength: LUT_STRENGTH, name: { type: "string", minLength: 1, description: "A label for the look." } }, ["src"], "A 3D LUT applied after the other settings (set_video_lut checks the file first).")
4907
3244
  },
4908
3245
  [],
@@ -4910,7 +3247,7 @@ var GRADE = object(
4910
3247
  );
4911
3248
  var ANIMATION = (when) => object(
4912
3249
  {
4913
- effect: { type: "string", enum: [...VIDEO_CLIP_ANIMATION_EFFECTS], description: "fade; rise (up into place) or drop (down into place); slide-left / slide-right (moving that way); zoom (grows into place); pop (grows past its size and settles). Each also fades; an exit plays the movement backwards." },
3250
+ effect: { type: "string", enum: [..._chunkSMJGE7HMcjs.VIDEO_CLIP_ANIMATION_EFFECTS], description: "fade; rise (up into place) or drop (down into place); slide-left / slide-right (moving that way); zoom (grows into place); pop (grows past its size and settles). Each also fades; an exit plays the movement backwards." },
4914
3251
  duration: { type: "number", exclusiveMinimum: 0, description: "Length in ms (default 500)." }
4915
3252
  },
4916
3253
  ["effect"],
@@ -4920,11 +3257,11 @@ var CLIP_COMMON = {
4920
3257
  name: { type: "string", description: "A label for the clip." },
4921
3258
  opacity: { type: "number", minimum: 0, maximum: 1, description: "Opacity 0\u20131 (default 1). A number replaces any keyframes; set_video_keyframes animates it." },
4922
3259
  transform: TRANSFORM,
4923
- fit: { type: "string", enum: [...VIDEO_FITS], description: "How the picture fits the frame before the transform, as CSS object-fit (default contain for media, fill for colours)." },
3260
+ fit: { type: "string", enum: [..._chunkSMJGE7HMcjs.VIDEO_FITS], description: "How the picture fits the frame before the transform, as CSS object-fit (default contain for media, fill for colours)." },
4924
3261
  grade: GRADE,
4925
3262
  animateIn: ANIMATION("How the picture enters, from the clip's start (on top of any fade in). Titles usually rise, pop or fade in."),
4926
3263
  animateOut: ANIMATION("How the picture leaves, ending at the clip's end."),
4927
- blend: { type: "string", enum: [...VIDEO_BLEND_MODES], description: "How the picture mixes with what is beneath it, as CSS mix-blend-mode (default normal): multiply darkens (shadows, textures), screen and add lighten (light leaks, flares, glows), overlay and soft-light add contrast, difference and exclusion invert." },
3264
+ blend: { type: "string", enum: [..._chunkSMJGE7HMcjs.VIDEO_BLEND_MODES], description: "How the picture mixes with what is beneath it, as CSS mix-blend-mode (default normal): multiply darkens (shadows, textures), screen and add lighten (light leaks, flares, glows), overlay and soft-light add contrast, difference and exclusion invert." },
4928
3265
  fadeIn: MS("Fade up from nothing (picture and sound) over this long"),
4929
3266
  fadeOut: MS("Fade down to nothing over this long")
4930
3267
  };
@@ -4933,8 +3270,8 @@ var MEDIA_FIELDS = {
4933
3270
  speed: { type: "number", minimum: 0.05, maximum: 20, description: "Playback speed (default 1). Changing it keeps the stretch of source, so the clip gets shorter or longer." },
4934
3271
  volume: { type: "number", minimum: 0, maximum: 4, description: "Gain (1 = unchanged, default 1). A number replaces any keyframes; set_video_keyframes animates it." },
4935
3272
  muted: { type: "boolean", description: "Silence this clip (default false)." },
4936
- fadeInCurve: { type: "string", enum: [...VIDEO_FADE_CURVES], description: "Shape of the sound's fade in (default linear; the picture always fades linearly): linear, equal-power (steady loudness, best for crossfades), s-curve (gentle at both ends), exponential (slow start, natural from silence) or logarithmic (fast start)." },
4937
- fadeOutCurve: { type: "string", enum: [...VIDEO_FADE_CURVES], description: "Shape of the sound's fade out (default linear), the same choices as fadeInCurve; exponential sounds natural fading to silence." }
3273
+ fadeInCurve: { type: "string", enum: [..._chunkSMJGE7HMcjs.VIDEO_FADE_CURVES], description: "Shape of the sound's fade in (default linear; the picture always fades linearly): linear, equal-power (steady loudness, best for crossfades), s-curve (gentle at both ends), exponential (slow start, natural from silence) or logarithmic (fast start)." },
3274
+ fadeOutCurve: { type: "string", enum: [..._chunkSMJGE7HMcjs.VIDEO_FADE_CURVES], description: "Shape of the sound's fade out (default linear), the same choices as fadeInCurve; exponential sounds natural fading to silence." }
4938
3275
  };
4939
3276
  var TEXT_FIELDS = {
4940
3277
  text: { type: "string", minLength: 1, description: `The title's text ("\\n" breaks lines).` },
@@ -4983,7 +3320,7 @@ var ASSET_FIELDS = {
4983
3320
  };
4984
3321
  var KEY_PROPERTY = {
4985
3322
  type: "string",
4986
- enum: [...VIDEO_KEYFRAME_PROPERTIES],
3323
+ enum: [..._chunkSMJGE7HMcjs.VIDEO_KEYFRAME_PROPERTIES],
4987
3324
  description: "Which property: opacity (0\u20131), volume (media only, 0\u20134 gain), x / y (pixels from the frame's centre, right / down), scale (1 = fitted size) or rotation (degrees clockwise)."
4988
3325
  };
4989
3326
  var KEYS = {
@@ -4998,7 +3335,7 @@ var KEYS = {
4998
3335
  ease: {
4999
3336
  description: "How the value moves from this key to the next (default linear): linear, hold (stays, then jumps at the next key), ease-in, ease-out, ease-in-out, or CSS cubic-bezier points [x1, y1, x2, y2] with x1 and x2 in 0\u20131.",
5000
3337
  anyOf: [
5001
- { type: "string", enum: [...VIDEO_EASE_NAMES] },
3338
+ { type: "string", enum: [..._chunkSMJGE7HMcjs.VIDEO_EASE_NAMES] },
5002
3339
  { type: "array", items: { type: "number" }, minItems: 4, maxItems: 4 }
5003
3340
  ]
5004
3341
  }
@@ -5027,19 +3364,19 @@ var describeVideoTracks = (tracks) => tracks.map((t, index) => ({
5027
3364
  ...t.locked ? { locked: true } : {},
5028
3365
  ...t.syncLocked === false ? { syncLocked: false } : {},
5029
3366
  ...t.volume !== void 0 ? { volume: t.volume } : {},
5030
- clips: sortedVideoClips(t).map((c) => ({ ...c, end: videoClipEnd(c), ...c.type === "media" || c.type === "sequence" ? { source: videoClipSourceRange(c) } : {} }))
3367
+ clips: _chunkSMJGE7HMcjs.sortedVideoClips.call(void 0, t).map((c) => ({ ...c, end: _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, c), ...c.type === "media" || c.type === "sequence" ? { source: _chunkSMJGE7HMcjs.videoClipSourceRange.call(void 0, c) } : {} }))
5031
3368
  }));
5032
- var videoSequenceUsers = (project, id) => allVideoTracks(project).flatMap(
5033
- ({ sequence, track }) => track.clips.flatMap((c) => c.type === "sequence" && c.sequence === id ? [{ clip: c.id, ...sequence ? { inSequence: sequence } : {}, track: track.id, start: c.start, end: videoClipEnd(c), source: videoClipSourceRange(c) }] : [])
3369
+ var videoSequenceUsers = (project, id) => _chunkSMJGE7HMcjs.allVideoTracks.call(void 0, project).flatMap(
3370
+ ({ sequence, track }) => track.clips.flatMap((c) => c.type === "sequence" && c.sequence === id ? [{ clip: c.id, ...sequence ? { inSequence: sequence } : {}, track: track.id, start: c.start, end: _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, c), source: _chunkSMJGE7HMcjs.videoClipSourceRange.call(void 0, c) }] : [])
5034
3371
  );
5035
3372
  function describeVideoSequence(project, sequence, tracks = true) {
5036
- const cycle = findVideoSequenceCycle(project, sequence.id);
3373
+ const cycle = _chunkSMJGE7HMcjs.findVideoSequenceCycle.call(void 0, project, sequence.id);
5037
3374
  return {
5038
3375
  id: sequence.id,
5039
3376
  ...sequence.name ? { name: sequence.name } : {},
5040
- duration: videoSequenceDuration(sequence),
5041
- clipCount: sequence.tracks.reduce((n2, t) => n2 + t.clips.length, 0),
5042
- nests: videoSequenceChildren(sequence),
3377
+ duration: _chunkSMJGE7HMcjs.videoSequenceDuration.call(void 0, sequence),
3378
+ clipCount: sequence.tracks.reduce((n, t) => n + t.clips.length, 0),
3379
+ nests: _chunkSMJGE7HMcjs.videoSequenceChildren.call(void 0, sequence),
5043
3380
  usedBy: videoSequenceUsers(project, sequence.id),
5044
3381
  ...cycle ? { cycle } : {},
5045
3382
  ...tracks ? { tracks: describeVideoTracks(sequence.tracks) } : { trackCount: sequence.tracks.length }
@@ -5051,11 +3388,11 @@ function describeVideoProject(project) {
5051
3388
  width: project.width,
5052
3389
  height: project.height,
5053
3390
  fps: project.fps,
5054
- duration: videoProjectDuration(project),
3391
+ duration: _chunkSMJGE7HMcjs.videoProjectDuration.call(void 0, project),
5055
3392
  ...project.background ? { background: project.background } : {},
5056
3393
  assets: project.assets.map(videoAssetOut),
5057
3394
  tracks: describeVideoTracks(project.tracks),
5058
- ..._optionalChain([project, 'access', _166 => _166.sequences, 'optionalAccess', _167 => _167.length]) ? { sequences: project.sequences.map((q) => describeVideoSequence(project, q, false)) } : {},
3395
+ ..._optionalChain([project, 'access', _101 => _101.sequences, 'optionalAccess', _102 => _102.length]) ? { sequences: project.sequences.map((q) => describeVideoSequence(project, q, false)) } : {},
5059
3396
  markers: _nullishCoalesce(project.markers, () => ( []))
5060
3397
  };
5061
3398
  }
@@ -5068,7 +3405,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5068
3405
  const project = () => controller.getProject();
5069
3406
  const outcome = (result, extra = {}) => {
5070
3407
  if (!result.ok) throw new Error(result.error);
5071
- return { ...extra, duration: videoProjectDuration(result.project), issues: issueSummary(result.issues) };
3408
+ return { ...extra, duration: _chunkSMJGE7HMcjs.videoProjectDuration.call(void 0, result.project), issues: issueSummary(result.issues) };
5072
3409
  };
5073
3410
  const apply = (edits, label, extra) => {
5074
3411
  const result = controller.apply(edits, label);
@@ -5077,19 +3414,19 @@ function createVideoEditorToolProvider(controller, options = {}) {
5077
3414
  const clipOut = (p, id) => {
5078
3415
  for (const t of p.tracks) {
5079
3416
  const c = t.clips.find((x) => x.id === id);
5080
- if (c) return { track: t.id, ...c, end: videoClipEnd(c), ...c.type === "media" || c.type === "sequence" ? { source: videoClipSourceRange(c) } : {} };
3417
+ if (c) return { track: t.id, ...c, end: _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, c), ...c.type === "media" || c.type === "sequence" ? { source: _chunkSMJGE7HMcjs.videoClipSourceRange.call(void 0, c) } : {} };
5081
3418
  }
5082
3419
  return null;
5083
3420
  };
5084
3421
  const placed = (o) => ({
5085
- ..._optionalChain([o, 'access', _168 => _168.created, 'optionalAccess', _169 => _169.length]) ? { created: o.created } : {},
5086
- ..._optionalChain([o, 'access', _170 => _170.trimmed, 'optionalAccess', _171 => _171.length]) ? { trimmed: o.trimmed } : {},
5087
- ..._optionalChain([o, 'access', _172 => _172.removed, 'optionalAccess', _173 => _173.length]) ? { removed: o.removed } : {},
3422
+ ..._optionalChain([o, 'access', _103 => _103.created, 'optionalAccess', _104 => _104.length]) ? { created: o.created } : {},
3423
+ ..._optionalChain([o, 'access', _105 => _105.trimmed, 'optionalAccess', _106 => _106.length]) ? { trimmed: o.trimmed } : {},
3424
+ ..._optionalChain([o, 'access', _107 => _107.removed, 'optionalAccess', _108 => _108.length]) ? { removed: o.removed } : {},
5088
3425
  ...o.rippled ? { rippled: o.rippled } : {}
5089
3426
  });
5090
3427
  const keysOut = (p, clip, property) => {
5091
3428
  const c = clipOut(p, clip);
5092
- const value = VIDEO_TRANSFORM_PROPERTIES.includes(property) ? _optionalChain([c, 'optionalAccess', _174 => _174.transform, 'optionalAccess', _175 => _175[property]]) : _optionalChain([c, 'optionalAccess', _176 => _176[property]]);
3429
+ const value = _chunkSMJGE7HMcjs.VIDEO_TRANSFORM_PROPERTIES.includes(property) ? _optionalChain([c, 'optionalAccess', _109 => _109.transform, 'optionalAccess', _110 => _110[property]]) : _optionalChain([c, 'optionalAccess', _111 => _111[property]]);
5093
3430
  return typeof value === "object" ? { keys: value.keys } : { keys: [], value: _nullishCoalesce(value, () => ( null)) };
5094
3431
  };
5095
3432
  const getProject = {
@@ -5111,8 +3448,8 @@ function createVideoEditorToolProvider(controller, options = {}) {
5111
3448
  inputSchema: object({ time: MS("Time from the start (default: the playhead)") }),
5112
3449
  annotations: { title: "What plays at a moment", ...READ },
5113
3450
  handler: ({ time }) => {
5114
- const frame = videoFrameAt(project(), _nullishCoalesce(time, () => ( controller.getState().time)));
5115
- return { ...frame, timecode: formatVideoTimecode(frame.time, project().fps) };
3451
+ const frame = _chunkSMJGE7HMcjs.videoFrameAt.call(void 0, project(), _nullishCoalesce(time, () => ( controller.getState().time)));
3452
+ return { ...frame, timecode: _chunkSMJGE7HMcjs.formatVideoTimecode.call(void 0, frame.time, project().fps) };
5116
3453
  }
5117
3454
  };
5118
3455
  const listSequences = {
@@ -5140,7 +3477,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5140
3477
  annotations: { title: "Find gaps", ...READ },
5141
3478
  handler: ({ track }) => {
5142
3479
  if (track !== void 0 && !project().tracks.some((t) => t.id === track)) throw new Error(`There is no track "${track}"; the tracks are ${project().tracks.map((t) => `"${t.id}"`).join(", ")}.`);
5143
- return { gaps: findVideoGaps(project(), track) };
3480
+ return { gaps: _chunkSMJGE7HMcjs.findVideoGaps.call(void 0, project(), track) };
5144
3481
  }
5145
3482
  };
5146
3483
  const checkProject = {
@@ -5167,10 +3504,10 @@ function createVideoEditorToolProvider(controller, options = {}) {
5167
3504
  annotations: { title: "Render a frame", ...READ },
5168
3505
  handler: async ({ time, times, columns, format = "svg" }) => {
5169
3506
  const p = project();
5170
- const lutSrcs = [...new Set(p.tracks.flatMap((t) => t.clips.map((c) => _optionalChain([c, 'access', _177 => _177.grade, 'optionalAccess', _178 => _178.lut, 'optionalAccess', _179 => _179.src])).filter((x) => !!x)))];
3507
+ const lutSrcs = [...new Set(p.tracks.flatMap((t) => t.clips.map((c) => _optionalChain([c, 'access', _112 => _112.grade, 'optionalAccess', _113 => _113.lut, 'optionalAccess', _114 => _114.src])).filter((x) => !!x)))];
5171
3508
  const missing = (await Promise.all(lutSrcs.map((src) => controller.luts.load(src).then(() => null, (e) => e.message)))).filter((m) => !!m);
5172
3509
  const svgOptions = { lutFor: (src) => controller.luts.peek(src), ...columns ? { columns } : {} };
5173
- const svg = times ? videoContactSheetSvg(p, times, svgOptions) : videoProjectSvg(p, _nullishCoalesce(time, () => ( controller.getState().time)), svgOptions);
3510
+ const svg = times ? _chunkSMJGE7HMcjs.videoContactSheetSvg.call(void 0, p, times, svgOptions) : _chunkSMJGE7HMcjs.videoProjectSvg.call(void 0, p, _nullishCoalesce(time, () => ( controller.getState().time)), svgOptions);
5174
3511
  const notes = [...missing.map((m) => `${m}; that LUT is left out of the picture.`), ...lutSrcs.length > missing.length ? ["LUTs are drawn approximately in SVG (curves and a colour matrix); the preview and export apply them exactly."] : []];
5175
3512
  if (format === "png") {
5176
3513
  if (!options.rasterize) throw new Error('This host cannot make PNG images; call render_video_frame with format "svg".');
@@ -5260,7 +3597,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5260
3597
  if (play === true) controller.play();
5261
3598
  if (play === false) controller.pause();
5262
3599
  const state = controller.getState();
5263
- return { time: state.time, timecode: formatVideoTimecode(state.time, p.fps), playing: state.playing, clips: state.current };
3600
+ return { time: state.time, timecode: _chunkSMJGE7HMcjs.formatVideoTimecode.call(void 0, state.time, p.fps), playing: state.playing, clips: state.current };
5264
3601
  }
5265
3602
  };
5266
3603
  const setSettings = {
@@ -5405,10 +3742,10 @@ function createVideoEditorToolProvider(controller, options = {}) {
5405
3742
  const gradeTargets = (ids) => {
5406
3743
  const p = project();
5407
3744
  for (const id of ids) {
5408
- const at = findVideoClip(p, id);
3745
+ const at = _chunkSMJGE7HMcjs.findVideoClip.call(void 0, p, id);
5409
3746
  if (!at) throw new Error(`There is no clip "${id}"; get_video_project lists the clips.`);
5410
3747
  const { clip } = at;
5411
- if (at.track.kind !== "video" || clip.type === "media" && _optionalChain([p, 'access', _180 => _180.assets, 'access', _181 => _181.find, 'call', _182 => _182((a) => a.id === clip.asset), 'optionalAccess', _183 => _183.kind]) === "audio")
3748
+ if (at.track.kind !== "video" || clip.type === "media" && _optionalChain([p, 'access', _115 => _115.assets, 'access', _116 => _116.find, 'call', _117 => _117((a) => a.id === clip.asset), 'optionalAccess', _118 => _118.kind]) === "audio")
5412
3749
  throw new Error(`Clip "${id}" is sound only; grades change pictures. Pass clips on video tracks.`);
5413
3750
  }
5414
3751
  };
@@ -5419,7 +3756,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5419
3756
  inputSchema: object(
5420
3757
  {
5421
3758
  clips: { type: "array", items: CLIP_ID, minItems: 1, description: "Clips on video tracks to grade." },
5422
- preset: { type: "string", enum: [...VIDEO_GRADE_PRESET_NAMES], description: "The look." },
3759
+ preset: { type: "string", enum: [..._chunkSMJGE7HMcjs.VIDEO_GRADE_PRESET_NAMES], description: "The look." },
5423
3760
  amount: { type: "number", minimum: 0, maximum: 1, description: "Strength, 0 (none) to 1 (full, the default); 0.5 is a subtler version." }
5424
3761
  },
5425
3762
  ["clips", "preset"]
@@ -5428,10 +3765,10 @@ function createVideoEditorToolProvider(controller, options = {}) {
5428
3765
  handler: ({ clips, preset, amount }) => {
5429
3766
  gradeTargets(clips);
5430
3767
  const edits = clips.map((clip) => {
5431
- const grade = videoGradeLook(findVideoClip(project(), clip).clip.grade, preset, _nullishCoalesce(amount, () => ( 1)));
3768
+ const grade = _chunkSMJGE7HMcjs.videoGradeLook.call(void 0, _chunkSMJGE7HMcjs.findVideoClip.call(void 0, project(), clip).clip.grade, preset, _nullishCoalesce(amount, () => ( 1)));
5432
3769
  return grade ? { op: "update_clip", clip, set: { grade } } : { op: "update_clip", clip, remove: ["grade"] };
5433
3770
  });
5434
- return apply(edits, `Apply ${preset} look`, (r) => ({ clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([findVideoClip, 'call', _184 => _184(r.project, id), 'optionalAccess', _185 => _185.clip, 'access', _186 => _186.grade]), () => ( null)) })) }));
3771
+ return apply(edits, `Apply ${preset} look`, (r) => ({ clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([_chunkSMJGE7HMcjs.findVideoClip.call(void 0, r.project, id), 'optionalAccess', _119 => _119.clip, 'access', _120 => _120.grade]), () => ( null)) })) }));
5435
3772
  }
5436
3773
  };
5437
3774
  const copyGrade = {
@@ -5442,7 +3779,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5442
3779
  annotations: { title: "Copy a colour grade", ...WRITE_IDEMPOTENT },
5443
3780
  handler: ({ from, to }) => {
5444
3781
  gradeTargets([from, ...to]);
5445
- const grade = findVideoClip(project(), from).clip.grade;
3782
+ const grade = _chunkSMJGE7HMcjs.findVideoClip.call(void 0, project(), from).clip.grade;
5446
3783
  const edits = to.map((clip) => grade ? { op: "update_clip", clip, set: { grade: { ...grade } } } : { op: "update_clip", clip, remove: ["grade"] });
5447
3784
  return apply(edits, "Copy grade", () => ({ grade: _nullishCoalesce(grade, () => ( null)), clips: to }));
5448
3785
  }
@@ -5467,7 +3804,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5467
3804
  const lut = await controller.luts.load(src);
5468
3805
  const label = _nullishCoalesce(name, () => ( lut.title));
5469
3806
  const setting = { src, ...strength !== void 0 && strength !== 1 ? { strength } : {}, ...label ? { name: label } : {} };
5470
- return apply(videoLutEdits(project(), clips, setting), "Apply LUT", (r) => ({ lut: { src, ...lut.title ? { title: lut.title } : {}, size: lut.size }, clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([findVideoClip, 'call', _187 => _187(r.project, id), 'optionalAccess', _188 => _188.clip, 'access', _189 => _189.grade]), () => ( null)) })) }));
3807
+ return apply(videoLutEdits(project(), clips, setting), "Apply LUT", (r) => ({ lut: { src, ...lut.title ? { title: lut.title } : {}, size: lut.size }, clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([_chunkSMJGE7HMcjs.findVideoClip.call(void 0, r.project, id), 'optionalAccess', _121 => _121.clip, 'access', _122 => _122.grade]), () => ( null)) })) }));
5471
3808
  }
5472
3809
  };
5473
3810
  const removeLut = {
@@ -5479,9 +3816,9 @@ function createVideoEditorToolProvider(controller, options = {}) {
5479
3816
  handler: ({ clips }) => {
5480
3817
  gradeTargets(clips);
5481
3818
  const p = project();
5482
- const withLut = clips.filter((id) => _optionalChain([findVideoClip, 'call', _190 => _190(p, id), 'access', _191 => _191.clip, 'access', _192 => _192.grade, 'optionalAccess', _193 => _193.lut]));
3819
+ const withLut = clips.filter((id) => _optionalChain([_chunkSMJGE7HMcjs.findVideoClip.call(void 0, p, id), 'access', _123 => _123.clip, 'access', _124 => _124.grade, 'optionalAccess', _125 => _125.lut]));
5483
3820
  if (!withLut.length) throw new Error(`None of ${clips.map((c) => `"${c}"`).join(", ")} has a LUT; get_video_project shows each clip's grade.`);
5484
- return apply(videoLutEdits(p, withLut, null), "Remove LUT", (r) => ({ clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([findVideoClip, 'call', _194 => _194(r.project, id), 'optionalAccess', _195 => _195.clip, 'access', _196 => _196.grade]), () => ( null)) })) }));
3821
+ return apply(videoLutEdits(p, withLut, null), "Remove LUT", (r) => ({ clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([_chunkSMJGE7HMcjs.findVideoClip.call(void 0, r.project, id), 'optionalAccess', _126 => _126.clip, 'access', _127 => _127.grade]), () => ( null)) })) }));
5485
3822
  }
5486
3823
  };
5487
3824
  const PROXY_SCALE = { type: "number", exclusiveMinimum: 0, exclusiveMaximum: 1, description: "Proxy size as a fraction of the original: 0.5 (half width and height, the default) or 0.25 for very large footage." };
@@ -5539,7 +3876,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5539
3876
  controller.setPreviewScale(scale);
5540
3877
  const s = controller.getState().previewScale;
5541
3878
  const fixed = s === "full" ? 1 : typeof s === "number" ? s : null;
5542
- const plays = fixed === null ? void 0 : project().assets.filter((a) => a.kind === "video").map((a) => ({ asset: a.id, src: _nullishCoalesce(_optionalChain([videoAssetProxy, 'call', _197 => _197(a, fixed), 'optionalAccess', _198 => _198.src]), () => ( a.src)), proxy: !!videoAssetProxy(a, fixed) }));
3879
+ const plays = fixed === null ? void 0 : project().assets.filter((a) => a.kind === "video").map((a) => ({ asset: a.id, src: _nullishCoalesce(_optionalChain([videoAssetProxy, 'call', _128 => _128(a, fixed), 'optionalAccess', _129 => _129.src]), () => ( a.src)), proxy: !!videoAssetProxy(a, fixed) }));
5543
3880
  return { previewScale: s, ...plays ? { plays } : { note: "auto picks the smallest proxy at least as large as the preview canvas shows, so it depends on the preview's size." } };
5544
3881
  }
5545
3882
  };
@@ -5590,7 +3927,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5590
3927
  approximate: parsed.approximate,
5591
3928
  first: list[0],
5592
3929
  last: list[list.length - 1],
5593
- ...a.duration !== void 0 && list[list.length - 1].end > a.duration + VIDEO_TIME_EPSILON ? { warning: `The transcript runs to ${list[list.length - 1].end} ms, past the asset's ${a.duration} ms: is it for this asset?` } : {}
3930
+ ...a.duration !== void 0 && list[list.length - 1].end > a.duration + _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON ? { warning: `The transcript runs to ${list[list.length - 1].end} ms, past the asset's ${a.duration} ms: is it for this asset?` } : {}
5594
3931
  }));
5595
3932
  }
5596
3933
  };
@@ -5608,7 +3945,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5608
3945
  handler: ({ track, from, to, limit }) => {
5609
3946
  const all = timelineWords(track).filter((w) => w.end > (_nullishCoalesce(from, () => ( 0))) && w.start < (_nullishCoalesce(to, () => ( Infinity))));
5610
3947
  const words = all.slice(0, _nullishCoalesce(limit, () => ( 1e3)));
5611
- return { track: _nullishCoalesce(_optionalChain([words, 'access', _199 => _199[0], 'optionalAccess', _200 => _200.track]), () => ( videoTranscriptTrack(project(), track))), text: words.map((w) => w.word).join(" "), words: words.map((w) => ({ word: w.word, start: round(w.start), end: round(w.end), clip: w.clip })), more: all.length > words.length };
3948
+ return { track: _nullishCoalesce(_optionalChain([words, 'access', _130 => _130[0], 'optionalAccess', _131 => _131.track]), () => ( videoTranscriptTrack(project(), track))), text: words.map((w) => w.word).join(" "), words: words.map((w) => ({ word: w.word, start: round(w.start), end: round(w.end), clip: w.clip })), more: all.length > words.length };
5612
3949
  }
5613
3950
  };
5614
3951
  const findText = {
@@ -5627,7 +3964,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5627
3964
  annotations: { title: "Find filler words", ...READ },
5628
3965
  handler: ({ track, fillers }) => {
5629
3966
  const found = findVideoFillers(timelineWords(track), fillers);
5630
- return { fillers: found.map(RANGE_OUT), total: round(found.reduce((n2, r) => n2 + r.to - r.from, 0)) };
3967
+ return { fillers: found.map(RANGE_OUT), total: round(found.reduce((n, r) => n + r.to - r.from, 0)) };
5631
3968
  }
5632
3969
  };
5633
3970
  const findPauses = {
@@ -5642,7 +3979,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5642
3979
  annotations: { title: "Find pauses between words", ...READ },
5643
3980
  handler: ({ track, min, keep }) => {
5644
3981
  const found = findVideoTranscriptPauses(timelineWords(track), { min, keep, fps: project().fps });
5645
- return { pauses: found.map(RANGE_OUT), total: round(found.reduce((n2, r) => n2 + r.to - r.from, 0)) };
3982
+ return { pauses: found.map(RANGE_OUT), total: round(found.reduce((n, r) => n + r.to - r.from, 0)) };
5646
3983
  }
5647
3984
  };
5648
3985
  const removeRanges = {
@@ -5661,22 +3998,22 @@ function createVideoEditorToolProvider(controller, options = {}) {
5661
3998
  const bad = ranges.findIndex((r) => !(r.to > r.from));
5662
3999
  if (bad >= 0) throw new Error(`Range ${bad + 1} is empty or backwards (${ranges[bad].from}\u2013${ranges[bad].to} ms); "to" must be after "from".`);
5663
4000
  const merged = mergeVideoRanges(ranges);
5664
- const before = videoProjectDuration(project());
5665
- return apply(videoRemoveRangesEdits(merged, tracks), merged.length > 1 ? `Cut ${merged.length} stretches` : "Cut stretch", (r) => ({ removed: merged.length, time: round(before - videoProjectDuration(r.project)), duration: videoProjectDuration(r.project) }));
4001
+ const before = _chunkSMJGE7HMcjs.videoProjectDuration.call(void 0, project());
4002
+ return apply(videoRemoveRangesEdits(merged, tracks), merged.length > 1 ? `Cut ${merged.length} stretches` : "Cut stretch", (r) => ({ removed: merged.length, time: round(before - _chunkSMJGE7HMcjs.videoProjectDuration.call(void 0, r.project)), duration: _chunkSMJGE7HMcjs.videoProjectDuration.call(void 0, r.project) }));
5666
4003
  }
5667
4004
  };
5668
4005
  const rampTarget = (id) => {
5669
- const at = findVideoClip(project(), id);
4006
+ const at = _chunkSMJGE7HMcjs.findVideoClip.call(void 0, project(), id);
5670
4007
  if (!at) throw new Error(`There is no clip "${id}"; get_video_project lists the clips.`);
5671
4008
  const { clip } = at;
5672
4009
  if (clip.type === "sequence") throw new Error(`Clip "${id}" is a compound clip, which plays its sequence at normal speed; change the speed of the clips inside the sequence instead.`);
5673
4010
  if (clip.type !== "media") throw new Error(`Clip "${id}" is a ${clip.type === "text" ? "title" : "colour"}, so it has no source to change the speed of; use a video or audio clip.`);
5674
- if (_optionalChain([project, 'call', _201 => _201(), 'access', _202 => _202.assets, 'access', _203 => _203.find, 'call', _204 => _204((a) => a.id === clip.asset), 'optionalAccess', _205 => _205.kind]) === "image") throw new Error(`Clip "${id}" is a still, so it has no motion to change the speed of.`);
4011
+ if (_optionalChain([project, 'call', _132 => _132(), 'access', _133 => _133.assets, 'access', _134 => _134.find, 'call', _135 => _135((a) => a.id === clip.asset), 'optionalAccess', _136 => _136.kind]) === "image") throw new Error(`Clip "${id}" is a still, so it has no motion to change the speed of.`);
5675
4012
  return clip;
5676
4013
  };
5677
4014
  const rampOut = (r, id) => {
5678
4015
  const outcome2 = r.outcomes[r.outcomes.length - 1];
5679
- return { clip: clipOut(r.project, id), ...outcome2.rippled ? { rippled: outcome2.rippled } : {}, duration: videoProjectDuration(r.project) };
4016
+ return { clip: clipOut(r.project, id), ...outcome2.rippled ? { rippled: outcome2.rippled } : {}, duration: _chunkSMJGE7HMcjs.videoProjectDuration.call(void 0, r.project) };
5680
4017
  };
5681
4018
  const RIPPLE_RAMP = { type: "boolean", description: "Move later material on every sync-locked track by the change in length (default true); false refuses if the clip would overlap the next one." };
5682
4019
  const setSpeedRamp = {
@@ -5707,8 +4044,8 @@ function createVideoEditorToolProvider(controller, options = {}) {
5707
4044
  ),
5708
4045
  annotations: { title: "Ramp the speed", ...WRITE_IDEMPOTENT },
5709
4046
  handler: ({ clip, segments, ripple }) => {
5710
- const edit = videoSpeedRampEdit(rampTarget(clip), segments, _nullishCoalesce(ripple, () => ( true)));
5711
- return apply(edit, "Speed ramp", (r) => ({ ...rampOut(r, clip), source: Math.round(videoSpeedRampKeys(segments).source) }));
4047
+ const edit = _chunkSMJGE7HMcjs.videoSpeedRampEdit.call(void 0, rampTarget(clip), segments, _nullishCoalesce(ripple, () => ( true)));
4048
+ return apply(edit, "Speed ramp", (r) => ({ ...rampOut(r, clip), source: Math.round(_chunkSMJGE7HMcjs.videoSpeedRampKeys.call(void 0, segments).source) }));
5712
4049
  }
5713
4050
  };
5714
4051
  const addFreeze = {
@@ -5728,8 +4065,8 @@ function createVideoEditorToolProvider(controller, options = {}) {
5728
4065
  handler: ({ clip, at, duration, ripple }) => {
5729
4066
  const c = rampTarget(clip);
5730
4067
  const t = at - c.start;
5731
- if (t < 0 || t > c.duration) throw new Error(`${formatVideoSeconds(at)} is outside clip "${clip}" (${formatVideoSeconds(c.start)}\u2013${formatVideoSeconds(videoClipEnd(c))}); pick a time inside it.`);
5732
- return apply(videoFreezeFrameEdit(c, t, duration, _nullishCoalesce(ripple, () => ( true))), "Freeze frame", (r) => rampOut(r, clip));
4068
+ if (t < 0 || t > c.duration) throw new Error(`${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, at)} is outside clip "${clip}" (${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, c.start)}\u2013${_chunkSMJGE7HMcjs.formatVideoSeconds.call(void 0, _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, c))}); pick a time inside it.`);
4069
+ return apply(_chunkSMJGE7HMcjs.videoFreezeFrameEdit.call(void 0, c, t, duration, _nullishCoalesce(ripple, () => ( true))), "Freeze frame", (r) => rampOut(r, clip));
5733
4070
  }
5734
4071
  };
5735
4072
  const removeRamp = {
@@ -5902,7 +4239,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5902
4239
  let from;
5903
4240
  let to;
5904
4241
  if (args.clip !== void 0) {
5905
- const found = findVideoClip(p, args.clip);
4242
+ const found = _chunkSMJGE7HMcjs.findVideoClip.call(void 0, p, args.clip);
5906
4243
  if (!found) throw new Error(`There is no clip "${args.clip}"; get_video_project lists the clips.`);
5907
4244
  const c = found.clip;
5908
4245
  if (c.type === "sequence") throw new Error(`Clip "${c.id}" is a compound clip; its sound is the mix of its sequence "${c.sequence}", so read the levels of the media in it (list_video_sequences lists its clips and assets).`);
@@ -5912,7 +4249,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5912
4249
  toSource = (t) => (_nullishCoalesce(c.in, () => ( 0))) + (t - c.start) * speed;
5913
4250
  toOut = (s) => c.start + (s - (_nullishCoalesce(c.in, () => ( 0)))) / speed;
5914
4251
  from = Math.max(c.start, _nullishCoalesce(args.from, () => ( c.start)));
5915
- to = Math.min(videoClipEnd(c), _nullishCoalesce(args.to, () => ( videoClipEnd(c))));
4252
+ to = Math.min(_chunkSMJGE7HMcjs.videoClipEnd.call(void 0, c), _nullishCoalesce(args.to, () => ( _chunkSMJGE7HMcjs.videoClipEnd.call(void 0, c))));
5916
4253
  } else {
5917
4254
  asset = p.assets.find((a) => a.id === args.asset);
5918
4255
  if (!asset) throw new Error(`There is no asset "${args.asset}"; the assets are ${p.assets.map((a) => `"${a.id}"`).join(", ") || "none"}.`);
@@ -5958,7 +4295,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
5958
4295
  const r = soundRange(args);
5959
4296
  const peaks = await controller.audioPeaks.load(r.asset);
5960
4297
  const silences = findVideoAudioSilences(peaks, r.toSource(r.from), r.toSource(r.to), { thresholdDb: args.thresholdDb, minDuration: args.minDuration }).map((x) => ({ start: round(r.toOut(x.start)), end: round(r.toOut(x.end)) }));
5961
- return { asset: r.asset.id, from: r.from, to: r.to, silences, total: round(silences.reduce((n2, x) => n2 + x.end - x.start, 0)) };
4298
+ return { asset: r.asset.id, from: r.from, to: r.to, silences, total: round(silences.reduce((n, x) => n + x.end - x.start, 0)) };
5962
4299
  }
5963
4300
  };
5964
4301
  const pixelsAt = async (time) => {
@@ -5993,11 +4330,11 @@ function createVideoEditorToolProvider(controller, options = {}) {
5993
4330
  annotations: { title: "Measure a frame with scopes", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
5994
4331
  handler: async ({ time, bins, columns }) => {
5995
4332
  const p = project();
5996
- const end = videoProjectDuration(p);
4333
+ const end = _chunkSMJGE7HMcjs.videoProjectDuration.call(void 0, p);
5997
4334
  const t = _nullishCoalesce(time, () => ( controller.getState().time));
5998
4335
  if (time !== void 0 && time > end) throw new Error(`Time ${time} ms is past the end of the project (${end} ms).`);
5999
4336
  const pixels = await pixelsAt(t);
6000
- return { time: t, timecode: formatVideoTimecode(t, p.fps), width: pixels.width, height: pixels.height, ...summarizeVideoScopes(pixels, { bins, columns }) };
4337
+ return { time: t, timecode: _chunkSMJGE7HMcjs.formatVideoTimecode.call(void 0, t, p.fps), width: pixels.width, height: pixels.height, ...summarizeVideoScopes(pixels, { bins, columns }) };
6001
4338
  }
6002
4339
  };
6003
4340
  const reads = [getProject, listSequences, getFrame, getTranscript, findText, findFillers, findPauses, findGaps, checkProject, renderFrame, getScopes, previewCache, planProxies, exportFfmpeg, exportLoudnessMeasure, exportEdl, audioLevels, findSilences, seek, setPreviewScale];
@@ -6250,5 +4587,5 @@ function createVideoEditorToolProvider(controller, options = {}) {
6250
4587
 
6251
4588
 
6252
4589
 
6253
- exports.VIDEO_BLEND_MODES = VIDEO_BLEND_MODES; exports.VIDEO_BLEND_MODE_LABELS = VIDEO_BLEND_MODE_LABELS; exports.VIDEO_CLIP_ANIMATION_DURATION = VIDEO_CLIP_ANIMATION_DURATION; exports.VIDEO_CLIP_ANIMATION_EFFECTS = VIDEO_CLIP_ANIMATION_EFFECTS; exports.VIDEO_CLIP_ANIMATION_LABELS = VIDEO_CLIP_ANIMATION_LABELS; exports.VIDEO_CLIP_TYPES = VIDEO_CLIP_TYPES; exports.VIDEO_DEFAULT_FILLERS = VIDEO_DEFAULT_FILLERS; exports.VIDEO_DEFAULT_STILL_DURATION = VIDEO_DEFAULT_STILL_DURATION; exports.VIDEO_DEFAULT_TITLE_DURATION = VIDEO_DEFAULT_TITLE_DURATION; exports.VIDEO_EASE_NAMES = VIDEO_EASE_NAMES; exports.VIDEO_EDIT_OPS = VIDEO_EDIT_OPS; exports.VIDEO_FADE_CURVES = VIDEO_FADE_CURVES; exports.VIDEO_FADE_CURVE_LABELS = VIDEO_FADE_CURVE_LABELS; exports.VIDEO_FITS = VIDEO_FITS; exports.VIDEO_GRADE_CONTROLS = VIDEO_GRADE_CONTROLS; exports.VIDEO_GRADE_PRESETS = VIDEO_GRADE_PRESETS; exports.VIDEO_GRADE_PRESET_NAMES = VIDEO_GRADE_PRESET_NAMES; exports.VIDEO_ISSUE_CODES = VIDEO_ISSUE_CODES; exports.VIDEO_KEYFRAME_LIMITS = VIDEO_KEYFRAME_LIMITS; exports.VIDEO_KEYFRAME_PROPERTIES = VIDEO_KEYFRAME_PROPERTIES; exports.VIDEO_LUT_MAX_SIZE = VIDEO_LUT_MAX_SIZE; exports.VIDEO_PEAK_BLOCK = VIDEO_PEAK_BLOCK; exports.VIDEO_PLACEMENT_MODES = VIDEO_PLACEMENT_MODES; exports.VIDEO_PREVIEW_SCALE_OPTIONS = VIDEO_PREVIEW_SCALE_OPTIONS; exports.VIDEO_PROXY_SCALES = VIDEO_PROXY_SCALES; exports.VIDEO_SCOPE_KINDS = VIDEO_SCOPE_KINDS; exports.VIDEO_TIME_EPSILON = VIDEO_TIME_EPSILON; exports.VIDEO_TRANSFORM_PROPERTIES = VIDEO_TRANSFORM_PROPERTIES; exports.VIDEO_TRANSITION_LABELS = VIDEO_TRANSITION_LABELS; exports.VIDEO_TRANSITION_TYPES = VIDEO_TRANSITION_TYPES; exports.VIDEO_VECTORSCOPE_TARGETS = VIDEO_VECTORSCOPE_TARGETS; exports.VIDEO_WIPE_DIRECTIONS = VIDEO_WIPE_DIRECTIONS; exports.VideoAudioPeaksCache = VideoAudioPeaksCache; exports.VideoEditorController = VideoEditorController; exports.VideoFrameCache = VideoFrameCache; exports.VideoLutCache = VideoLutCache; exports.VideoPreviewEngine = VideoPreviewEngine; exports.VideoPreviewPixels = VideoPreviewPixels; exports.allVideoClipIds = allVideoClipIds; exports.allVideoTracks = allVideoTracks; exports.applyVideoEdits = applyVideoEdits; exports.applyVideoGrade = applyVideoGrade; exports.applyVideoLut = applyVideoLut; exports.applyVideoLutToPixels = applyVideoLutToPixels; exports.buildVideoAudioPeaks = buildVideoAudioPeaks; exports.checkVideoProject = checkVideoProject; exports.checkVideoTimeRemap = checkVideoTimeRemap; exports.createVideoEditorToolProvider = createVideoEditorToolProvider; exports.createVideoElementDecoder = createVideoElementDecoder; exports.createVideoProject = createVideoProject; exports.decodeVideoAudioPeaks = decodeVideoAudioPeaks; exports.describeVideoProject = describeVideoProject; exports.describeVideoSequence = describeVideoSequence; exports.drawVideoFrame = drawVideoFrame; exports.ffmpegFilterValue = ffmpegFilterValue; exports.findVideoAudioSilences = findVideoAudioSilences; exports.findVideoClip = findVideoClip; exports.findVideoFillers = findVideoFillers; exports.findVideoGaps = findVideoGaps; exports.findVideoSequenceClip = findVideoSequenceClip; exports.findVideoSequenceCycle = findVideoSequenceCycle; exports.findVideoTranscriptPauses = findVideoTranscriptPauses; exports.findVideoTranscriptText = findVideoTranscriptText; exports.formatVideoSeconds = formatVideoSeconds; exports.formatVideoTimecode = formatVideoTimecode; exports.formatVideoTimecodeFrames = formatVideoTimecodeFrames; exports.isVideoDropFrameRate = isVideoDropFrameRate; exports.isVideoKeyframes = isVideoKeyframes; exports.mergeVideoRanges = mergeVideoRanges; exports.nextFreeVideoSlot = nextFreeVideoSlot; exports.normalizeVideoGrade = normalizeVideoGrade; exports.normalizeVideoGradeLut = normalizeVideoGradeLut; exports.normalizeVideoWord = normalizeVideoWord; exports.parseVideoAnimatable = parseVideoAnimatable; exports.parseVideoAsset = parseVideoAsset; exports.parseVideoAssetProxies = parseVideoAssetProxies; exports.parseVideoClip = parseVideoClip; exports.parseVideoClipAnimation = parseVideoClipAnimation; exports.parseVideoCubeLut = parseVideoCubeLut; exports.parseVideoGrade = parseVideoGrade; exports.parseVideoGradeLut = parseVideoGradeLut; exports.parseVideoKeyframes = parseVideoKeyframes; exports.parseVideoLoudnessMeasurement = parseVideoLoudnessMeasurement; exports.parseVideoMarker = parseVideoMarker; exports.parseVideoPreviewScale = parseVideoPreviewScale; exports.parseVideoProject = parseVideoProject; exports.parseVideoSequence = parseVideoSequence; exports.parseVideoTimeRemap = parseVideoTimeRemap; exports.parseVideoTrack = parseVideoTrack; exports.parseVideoTranscript = parseVideoTranscript; exports.parseVideoTranscriptWords = parseVideoTranscriptWords; exports.parseVideoTransform = parseVideoTransform; exports.parseVideoTransition = parseVideoTransition; exports.placeVideoRect = placeVideoRect; exports.planVideoClipDrag = planVideoClipDrag; exports.planVideoPrefetch = planVideoPrefetch; exports.probeVideoMedia = probeVideoMedia; exports.readVideoScopesPanel = readVideoScopesPanel; exports.removeVideoKey = removeVideoKey; exports.resolveVideoPreviewScale = resolveVideoPreviewScale; exports.retimeVideoAnimatable = retimeVideoAnimatable; exports.setVideoKey = setVideoKey; exports.snapToVideoFrame = snapToVideoFrame; exports.snapVideoTime = snapVideoTime; exports.sortVideoKeys = sortVideoKeys; exports.sortedVideoClips = sortedVideoClips; exports.splitVideoAnimatable = splitVideoAnimatable; exports.summarizeVideoScopes = summarizeVideoScopes; exports.toVideoEdl = toVideoEdl; exports.toVideoFfmpeg = toVideoFfmpeg; exports.toVideoLoudnessMeasure = toVideoLoudnessMeasure; exports.videoAssetDropEdit = videoAssetDropEdit; exports.videoAssetHasSound = videoAssetHasSound; exports.videoAssetKindOf = videoAssetKindOf; exports.videoAssetMap = videoAssetMap; exports.videoAssetProxy = videoAssetProxy; exports.videoAudioLevels = videoAudioLevels; exports.videoAudioPeakRange = videoAudioPeakRange; exports.videoAudioPeakSpans = videoAudioPeakSpans; exports.videoAudioPeaksDuration = videoAudioPeaksDuration; exports.videoCacheEvictions = videoCacheEvictions; exports.videoChroma = videoChroma; exports.videoClipAfter = videoClipAfter; exports.videoClipAnimated = videoClipAnimated; exports.videoClipAnimationProperties = videoClipAnimationProperties; exports.videoClipAnimationSpans = videoClipAnimationSpans; exports.videoClipBefore = videoClipBefore; exports.videoClipEnd = videoClipEnd; exports.videoClipHandles = videoClipHandles; exports.videoClipKeyframes = videoClipKeyframes; exports.videoClipLabel = videoClipLabel; exports.videoClipOverhang = videoClipOverhang; exports.videoClipShowsWaveform = videoClipShowsWaveform; exports.videoClipSoundGain = videoClipSoundGain; exports.videoClipSourceRange = videoClipSourceRange; exports.videoClipSourceTime = videoClipSourceTime; exports.videoClipSpeed = videoClipSpeed; exports.videoClipSpeedAt = videoClipSpeedAt; exports.videoClipTint = videoClipTint; exports.videoClipWaveform = videoClipWaveform; exports.videoClipWaveformPath = videoClipWaveformPath; exports.videoClipsAt = videoClipsAt; exports.videoContactSheetSvg = videoContactSheetSvg; exports.videoDragLabel = videoDragLabel; exports.videoDropTracks = videoDropTracks; exports.videoEaseBezier = videoEaseBezier; exports.videoEaseProgress = videoEaseProgress; exports.videoExposureNotes = videoExposureNotes; exports.videoExposureStats = videoExposureStats; exports.videoFadeCurveGain = videoFadeCurveGain; exports.videoFrameAt = videoFrameAt; exports.videoFrameCacheKey = videoFrameCacheKey; exports.videoFrameDuration = videoFrameDuration; exports.videoFrameLeafLayers = videoFrameLeafLayers; exports.videoFrameRequestsAt = videoFrameRequestsAt; exports.videoFrameToSvg = videoFrameToSvg; exports.videoFrames = videoFrames; exports.videoFreezeFrameEdit = videoFreezeFrameEdit; exports.videoGradeCssFilter = videoGradeCssFilter; exports.videoGradeFfmpegFilters = videoGradeFfmpegFilters; exports.videoGradeGainsFilterId = videoGradeGainsFilterId; exports.videoGradeLook = videoGradeLook; exports.videoGradePreset = videoGradePreset; exports.videoGradeStepMatrix = videoGradeStepMatrix; exports.videoGradeStepSvgValues = videoGradeStepSvgValues; exports.videoGradeSteps = videoGradeSteps; exports.videoGradeSvgDefs = videoGradeSvgDefs; exports.videoGradeSvgFilter = videoGradeSvgFilter; exports.videoHistogram = videoHistogram; exports.videoHistogramImage = videoHistogramImage; exports.videoHourFrames = videoHourFrames; exports.videoIrisRadius = videoIrisRadius; exports.videoIssueKey = videoIssueKey; exports.videoKeyframeBreakpoints = videoKeyframeBreakpoints; exports.videoKeyframeFfmpegExpr = videoKeyframeFfmpegExpr; exports.videoKeyframeRows = videoKeyframeRows; exports.videoKeyframeToggleEdit = videoKeyframeToggleEdit; exports.videoKeyframeValueEdit = videoKeyframeValueEdit; exports.videoKeysOf = videoKeysOf; exports.videoLuma = videoLuma; exports.videoLumaWaveform = videoLumaWaveform; exports.videoLutEdits = videoLutEdits; exports.videoLutFromFunction = videoLutFromFunction; exports.videoLutSvgApproximation = videoLutSvgApproximation; exports.videoLutSvgPrimitives = videoLutSvgPrimitives; exports.videoLutToCube = videoLutToCube; exports.videoOverlap = videoOverlap; exports.videoPeakDb = videoPeakDb; exports.videoPreviewSrc = videoPreviewSrc; exports.videoProjectDuration = videoProjectDuration; exports.videoProjectHasProxies = videoProjectHasProxies; exports.videoProjectSvg = videoProjectSvg; exports.videoProxyFfmpeg = videoProxyFfmpeg; exports.videoProxyName = videoProxyName; exports.videoProxyPlan = videoProxyPlan; exports.videoRemoveRangesEdits = videoRemoveRangesEdits; exports.videoRulerTicks = videoRulerTicks; exports.videoScopeImage = videoScopeImage; exports.videoSequenceChildren = videoSequenceChildren; exports.videoSequenceDuration = videoSequenceDuration; exports.videoSequenceMap = videoSequenceMap; exports.videoShellQuote = videoShellQuote; exports.videoSlowMotionRamp = videoSlowMotionRamp; exports.videoSnapPoints = videoSnapPoints; exports.videoSourceFrame = videoSourceFrame; exports.videoSourceFrameTime = videoSourceFrameTime; exports.videoSpeedRampEdit = videoSpeedRampEdit; exports.videoSpeedRampKeys = videoSpeedRampKeys; exports.videoTextStyle = videoTextStyle; exports.videoTimeRemapBreakpoints = videoTimeRemapBreakpoints; exports.videoTimeRemapSpan = videoTimeRemapSpan; exports.videoTimecodeFrames = videoTimecodeFrames; exports.videoTimelineTranscript = videoTimelineTranscript; exports.videoTitleFrame = videoTitleFrame; exports.videoTrackSyncLocked = videoTrackSyncLocked; exports.videoTranscriptEdit = videoTranscriptEdit; exports.videoTranscriptTrack = videoTranscriptTrack; exports.videoTransformAt = videoTransformAt; exports.videoTransitionAudioCurve = videoTransitionAudioCurve; exports.videoTransitionEase = videoTransitionEase; exports.videoTransitionHasDirection = videoTransitionHasDirection; exports.videoValueAt = videoValueAt; exports.videoVectorscope = videoVectorscope; exports.videoVectorscopeAngle = videoVectorscopeAngle; exports.videoVectorscopeImage = videoVectorscopeImage; exports.videoVectorscopePoint = videoVectorscopePoint; exports.videoWaveformImage = videoWaveformImage; exports.videoWaveformPath = videoWaveformPath; exports.videoWipeRect = videoWipeRect; exports.videoWordAt = videoWordAt; exports.videoWordsRange = videoWordsRange;
4590
+ exports.VIDEO_BLEND_MODES = _chunkSMJGE7HMcjs.VIDEO_BLEND_MODES; exports.VIDEO_BLEND_MODE_LABELS = VIDEO_BLEND_MODE_LABELS; exports.VIDEO_CLIP_ANIMATION_DURATION = _chunkSMJGE7HMcjs.VIDEO_CLIP_ANIMATION_DURATION; exports.VIDEO_CLIP_ANIMATION_EFFECTS = _chunkSMJGE7HMcjs.VIDEO_CLIP_ANIMATION_EFFECTS; exports.VIDEO_CLIP_ANIMATION_LABELS = VIDEO_CLIP_ANIMATION_LABELS; exports.VIDEO_CLIP_TYPES = _chunkSMJGE7HMcjs.VIDEO_CLIP_TYPES; exports.VIDEO_DEFAULT_FILLERS = VIDEO_DEFAULT_FILLERS; exports.VIDEO_DEFAULT_STILL_DURATION = VIDEO_DEFAULT_STILL_DURATION; exports.VIDEO_DEFAULT_TITLE_DURATION = VIDEO_DEFAULT_TITLE_DURATION; exports.VIDEO_EASE_NAMES = _chunkSMJGE7HMcjs.VIDEO_EASE_NAMES; exports.VIDEO_EDIT_OPS = VIDEO_EDIT_OPS; exports.VIDEO_FADE_CURVES = _chunkSMJGE7HMcjs.VIDEO_FADE_CURVES; exports.VIDEO_FADE_CURVE_LABELS = VIDEO_FADE_CURVE_LABELS; exports.VIDEO_FITS = _chunkSMJGE7HMcjs.VIDEO_FITS; exports.VIDEO_GRADE_CONTROLS = _chunkSMJGE7HMcjs.VIDEO_GRADE_CONTROLS; exports.VIDEO_GRADE_PRESETS = _chunkSMJGE7HMcjs.VIDEO_GRADE_PRESETS; exports.VIDEO_GRADE_PRESET_NAMES = _chunkSMJGE7HMcjs.VIDEO_GRADE_PRESET_NAMES; exports.VIDEO_ISSUE_CODES = VIDEO_ISSUE_CODES; exports.VIDEO_KEYFRAME_LIMITS = _chunkSMJGE7HMcjs.VIDEO_KEYFRAME_LIMITS; exports.VIDEO_KEYFRAME_PROPERTIES = _chunkSMJGE7HMcjs.VIDEO_KEYFRAME_PROPERTIES; exports.VIDEO_LUT_MAX_SIZE = _chunkSMJGE7HMcjs.VIDEO_LUT_MAX_SIZE; exports.VIDEO_PEAK_BLOCK = VIDEO_PEAK_BLOCK; exports.VIDEO_PLACEMENT_MODES = _chunkSMJGE7HMcjs.VIDEO_PLACEMENT_MODES; exports.VIDEO_PREVIEW_SCALE_OPTIONS = VIDEO_PREVIEW_SCALE_OPTIONS; exports.VIDEO_PROXY_SCALES = VIDEO_PROXY_SCALES; exports.VIDEO_SCOPE_KINDS = VIDEO_SCOPE_KINDS; exports.VIDEO_TIME_EPSILON = _chunkSMJGE7HMcjs.VIDEO_TIME_EPSILON; exports.VIDEO_TRANSFORM_PROPERTIES = _chunkSMJGE7HMcjs.VIDEO_TRANSFORM_PROPERTIES; exports.VIDEO_TRANSITION_LABELS = VIDEO_TRANSITION_LABELS; exports.VIDEO_TRANSITION_TYPES = _chunkSMJGE7HMcjs.VIDEO_TRANSITION_TYPES; exports.VIDEO_VECTORSCOPE_TARGETS = VIDEO_VECTORSCOPE_TARGETS; exports.VIDEO_WIPE_DIRECTIONS = _chunkSMJGE7HMcjs.VIDEO_WIPE_DIRECTIONS; exports.VideoAudioPeaksCache = VideoAudioPeaksCache; exports.VideoEditorController = VideoEditorController; exports.VideoFrameCache = VideoFrameCache; exports.VideoLutCache = _chunkSMJGE7HMcjs.VideoLutCache; exports.VideoPreviewEngine = VideoPreviewEngine; exports.VideoPreviewPixels = VideoPreviewPixels; exports.allVideoClipIds = _chunkSMJGE7HMcjs.allVideoClipIds; exports.allVideoTracks = _chunkSMJGE7HMcjs.allVideoTracks; exports.applyVideoEdits = applyVideoEdits; exports.applyVideoGrade = _chunkSMJGE7HMcjs.applyVideoGrade; exports.applyVideoLut = _chunkSMJGE7HMcjs.applyVideoLut; exports.applyVideoLutToPixels = _chunkSMJGE7HMcjs.applyVideoLutToPixels; exports.buildVideoAudioPeaks = buildVideoAudioPeaks; exports.checkVideoProject = checkVideoProject; exports.checkVideoTimeRemap = _chunkSMJGE7HMcjs.checkVideoTimeRemap; exports.createVideoEditorToolProvider = createVideoEditorToolProvider; exports.createVideoElementDecoder = createVideoElementDecoder; exports.createVideoProject = createVideoProject; exports.decodeVideoAudioPeaks = decodeVideoAudioPeaks; exports.describeVideoProject = describeVideoProject; exports.describeVideoSequence = describeVideoSequence; exports.drawVideoFrame = _chunkSMJGE7HMcjs.drawVideoFrame; exports.ffmpegFilterValue = _chunkSMJGE7HMcjs.ffmpegFilterValue; exports.findVideoAudioSilences = findVideoAudioSilences; exports.findVideoClip = _chunkSMJGE7HMcjs.findVideoClip; exports.findVideoFillers = findVideoFillers; exports.findVideoGaps = _chunkSMJGE7HMcjs.findVideoGaps; exports.findVideoSequenceClip = _chunkSMJGE7HMcjs.findVideoSequenceClip; exports.findVideoSequenceCycle = _chunkSMJGE7HMcjs.findVideoSequenceCycle; exports.findVideoTranscriptPauses = findVideoTranscriptPauses; exports.findVideoTranscriptText = findVideoTranscriptText; exports.formatVideoSeconds = _chunkSMJGE7HMcjs.formatVideoSeconds; exports.formatVideoTimecode = _chunkSMJGE7HMcjs.formatVideoTimecode; exports.formatVideoTimecodeFrames = _chunkSMJGE7HMcjs.formatVideoTimecodeFrames; exports.isVideoDropFrameRate = _chunkSMJGE7HMcjs.isVideoDropFrameRate; exports.isVideoKeyframes = _chunkSMJGE7HMcjs.isVideoKeyframes; exports.mergeVideoRanges = mergeVideoRanges; exports.nextFreeVideoSlot = _chunkSMJGE7HMcjs.nextFreeVideoSlot; exports.normalizeVideoGrade = _chunkSMJGE7HMcjs.normalizeVideoGrade; exports.normalizeVideoGradeLut = _chunkSMJGE7HMcjs.normalizeVideoGradeLut; exports.normalizeVideoWord = normalizeVideoWord; exports.parseVideoAnimatable = parseVideoAnimatable; exports.parseVideoAsset = parseVideoAsset; exports.parseVideoAssetProxies = parseVideoAssetProxies; exports.parseVideoClip = parseVideoClip; exports.parseVideoClipAnimation = parseVideoClipAnimation; exports.parseVideoCubeLut = _chunkSMJGE7HMcjs.parseVideoCubeLut; exports.parseVideoGrade = parseVideoGrade; exports.parseVideoGradeLut = parseVideoGradeLut; exports.parseVideoKeyframes = parseVideoKeyframes; exports.parseVideoLoudnessMeasurement = _chunkSMJGE7HMcjs.parseVideoLoudnessMeasurement; exports.parseVideoMarker = parseVideoMarker; exports.parseVideoPreviewScale = parseVideoPreviewScale; exports.parseVideoProject = parseVideoProject; exports.parseVideoSequence = parseVideoSequence; exports.parseVideoTimeRemap = parseVideoTimeRemap; exports.parseVideoTrack = parseVideoTrack; exports.parseVideoTranscript = parseVideoTranscript; exports.parseVideoTranscriptWords = parseVideoTranscriptWords; exports.parseVideoTransform = parseVideoTransform; exports.parseVideoTransition = parseVideoTransition; exports.placeVideoRect = _chunkSMJGE7HMcjs.placeVideoRect; exports.planVideoClipDrag = planVideoClipDrag; exports.planVideoPrefetch = planVideoPrefetch; exports.probeVideoMedia = probeVideoMedia; exports.readVideoScopesPanel = readVideoScopesPanel; exports.removeVideoKey = _chunkSMJGE7HMcjs.removeVideoKey; exports.resolveVideoPreviewScale = resolveVideoPreviewScale; exports.retimeVideoAnimatable = _chunkSMJGE7HMcjs.retimeVideoAnimatable; exports.setVideoKey = _chunkSMJGE7HMcjs.setVideoKey; exports.snapToVideoFrame = _chunkSMJGE7HMcjs.snapToVideoFrame; exports.snapVideoTime = _chunkSMJGE7HMcjs.snapVideoTime; exports.sortVideoKeys = _chunkSMJGE7HMcjs.sortVideoKeys; exports.sortedVideoClips = _chunkSMJGE7HMcjs.sortedVideoClips; exports.splitVideoAnimatable = _chunkSMJGE7HMcjs.splitVideoAnimatable; exports.summarizeVideoScopes = summarizeVideoScopes; exports.toVideoEdl = _chunkSMJGE7HMcjs.toVideoEdl; exports.toVideoFfmpeg = _chunkSMJGE7HMcjs.toVideoFfmpeg; exports.toVideoLoudnessMeasure = _chunkSMJGE7HMcjs.toVideoLoudnessMeasure; exports.videoAssetDropEdit = videoAssetDropEdit; exports.videoAssetHasSound = _chunkSMJGE7HMcjs.videoAssetHasSound; exports.videoAssetKindOf = videoAssetKindOf; exports.videoAssetMap = _chunkSMJGE7HMcjs.videoAssetMap; exports.videoAssetProxy = videoAssetProxy; exports.videoAudioLevels = videoAudioLevels; exports.videoAudioPeakRange = videoAudioPeakRange; exports.videoAudioPeakSpans = videoAudioPeakSpans; exports.videoAudioPeaksDuration = videoAudioPeaksDuration; exports.videoCacheEvictions = videoCacheEvictions; exports.videoChroma = videoChroma; exports.videoClipAfter = _chunkSMJGE7HMcjs.videoClipAfter; exports.videoClipAnimated = _chunkSMJGE7HMcjs.videoClipAnimated; exports.videoClipAnimationProperties = _chunkSMJGE7HMcjs.videoClipAnimationProperties; exports.videoClipAnimationSpans = _chunkSMJGE7HMcjs.videoClipAnimationSpans; exports.videoClipBefore = _chunkSMJGE7HMcjs.videoClipBefore; exports.videoClipEnd = _chunkSMJGE7HMcjs.videoClipEnd; exports.videoClipHandles = _chunkSMJGE7HMcjs.videoClipHandles; exports.videoClipKeyframes = videoClipKeyframes; exports.videoClipLabel = videoClipLabel; exports.videoClipOverhang = _chunkSMJGE7HMcjs.videoClipOverhang; exports.videoClipShowsWaveform = videoClipShowsWaveform; exports.videoClipSoundGain = _chunkSMJGE7HMcjs.videoClipSoundGain; exports.videoClipSourceRange = _chunkSMJGE7HMcjs.videoClipSourceRange; exports.videoClipSourceTime = _chunkSMJGE7HMcjs.videoClipSourceTime; exports.videoClipSpeed = _chunkSMJGE7HMcjs.videoClipSpeed; exports.videoClipSpeedAt = _chunkSMJGE7HMcjs.videoClipSpeedAt; exports.videoClipTint = videoClipTint; exports.videoClipWaveform = videoClipWaveform; exports.videoClipWaveformPath = videoClipWaveformPath; exports.videoClipsAt = _chunkSMJGE7HMcjs.videoClipsAt; exports.videoContactSheetSvg = _chunkSMJGE7HMcjs.videoContactSheetSvg; exports.videoDragLabel = videoDragLabel; exports.videoDropTracks = videoDropTracks; exports.videoEaseBezier = _chunkSMJGE7HMcjs.videoEaseBezier; exports.videoEaseProgress = _chunkSMJGE7HMcjs.videoEaseProgress; exports.videoExposureNotes = videoExposureNotes; exports.videoExposureStats = videoExposureStats; exports.videoFadeCurveGain = _chunkSMJGE7HMcjs.videoFadeCurveGain; exports.videoFrameAt = _chunkSMJGE7HMcjs.videoFrameAt; exports.videoFrameCacheKey = videoFrameCacheKey; exports.videoFrameDuration = _chunkSMJGE7HMcjs.videoFrameDuration; exports.videoFrameLeafLayers = _chunkSMJGE7HMcjs.videoFrameLeafLayers; exports.videoFrameRequestsAt = videoFrameRequestsAt; exports.videoFrameToSvg = _chunkSMJGE7HMcjs.videoFrameToSvg; exports.videoFrames = _chunkSMJGE7HMcjs.videoFrames; exports.videoFreezeFrameEdit = _chunkSMJGE7HMcjs.videoFreezeFrameEdit; exports.videoGradeCssFilter = _chunkSMJGE7HMcjs.videoGradeCssFilter; exports.videoGradeFfmpegFilters = _chunkSMJGE7HMcjs.videoGradeFfmpegFilters; exports.videoGradeGainsFilterId = _chunkSMJGE7HMcjs.videoGradeGainsFilterId; exports.videoGradeLook = _chunkSMJGE7HMcjs.videoGradeLook; exports.videoGradePreset = _chunkSMJGE7HMcjs.videoGradePreset; exports.videoGradeStepMatrix = _chunkSMJGE7HMcjs.videoGradeStepMatrix; exports.videoGradeStepSvgValues = _chunkSMJGE7HMcjs.videoGradeStepSvgValues; exports.videoGradeSteps = _chunkSMJGE7HMcjs.videoGradeSteps; exports.videoGradeSvgDefs = _chunkSMJGE7HMcjs.videoGradeSvgDefs; exports.videoGradeSvgFilter = _chunkSMJGE7HMcjs.videoGradeSvgFilter; exports.videoHistogram = videoHistogram; exports.videoHistogramImage = videoHistogramImage; exports.videoHourFrames = _chunkSMJGE7HMcjs.videoHourFrames; exports.videoIrisRadius = _chunkSMJGE7HMcjs.videoIrisRadius; exports.videoIssueKey = videoIssueKey; exports.videoKeyframeBreakpoints = _chunkSMJGE7HMcjs.videoKeyframeBreakpoints; exports.videoKeyframeFfmpegExpr = _chunkSMJGE7HMcjs.videoKeyframeFfmpegExpr; exports.videoKeyframeRows = videoKeyframeRows; exports.videoKeyframeToggleEdit = videoKeyframeToggleEdit; exports.videoKeyframeValueEdit = videoKeyframeValueEdit; exports.videoKeysOf = _chunkSMJGE7HMcjs.videoKeysOf; exports.videoLuma = videoLuma; exports.videoLumaWaveform = videoLumaWaveform; exports.videoLutEdits = videoLutEdits; exports.videoLutFromFunction = _chunkSMJGE7HMcjs.videoLutFromFunction; exports.videoLutSvgApproximation = _chunkSMJGE7HMcjs.videoLutSvgApproximation; exports.videoLutSvgPrimitives = _chunkSMJGE7HMcjs.videoLutSvgPrimitives; exports.videoLutToCube = _chunkSMJGE7HMcjs.videoLutToCube; exports.videoOverlap = _chunkSMJGE7HMcjs.videoOverlap; exports.videoPeakDb = videoPeakDb; exports.videoPreviewSrc = videoPreviewSrc; exports.videoProjectDuration = _chunkSMJGE7HMcjs.videoProjectDuration; exports.videoProjectHasProxies = videoProjectHasProxies; exports.videoProjectSvg = _chunkSMJGE7HMcjs.videoProjectSvg; exports.videoProxyFfmpeg = videoProxyFfmpeg; exports.videoProxyName = videoProxyName; exports.videoProxyPlan = videoProxyPlan; exports.videoRemoveRangesEdits = videoRemoveRangesEdits; exports.videoRulerTicks = videoRulerTicks; exports.videoScopeImage = videoScopeImage; exports.videoSequenceChildren = _chunkSMJGE7HMcjs.videoSequenceChildren; exports.videoSequenceDuration = _chunkSMJGE7HMcjs.videoSequenceDuration; exports.videoSequenceMap = _chunkSMJGE7HMcjs.videoSequenceMap; exports.videoShellQuote = _chunkSMJGE7HMcjs.videoShellQuote; exports.videoSlowMotionRamp = _chunkSMJGE7HMcjs.videoSlowMotionRamp; exports.videoSnapPoints = _chunkSMJGE7HMcjs.videoSnapPoints; exports.videoSourceFrame = videoSourceFrame; exports.videoSourceFrameTime = videoSourceFrameTime; exports.videoSpeedRampEdit = _chunkSMJGE7HMcjs.videoSpeedRampEdit; exports.videoSpeedRampKeys = _chunkSMJGE7HMcjs.videoSpeedRampKeys; exports.videoTextStyle = _chunkSMJGE7HMcjs.videoTextStyle; exports.videoTimeRemapBreakpoints = _chunkSMJGE7HMcjs.videoTimeRemapBreakpoints; exports.videoTimeRemapSpan = _chunkSMJGE7HMcjs.videoTimeRemapSpan; exports.videoTimecodeFrames = _chunkSMJGE7HMcjs.videoTimecodeFrames; exports.videoTimelineTranscript = videoTimelineTranscript; exports.videoTitleFrame = _chunkSMJGE7HMcjs.videoTitleFrame; exports.videoTrackSyncLocked = _chunkSMJGE7HMcjs.videoTrackSyncLocked; exports.videoTranscriptEdit = videoTranscriptEdit; exports.videoTranscriptTrack = videoTranscriptTrack; exports.videoTransformAt = _chunkSMJGE7HMcjs.videoTransformAt; exports.videoTransitionAudioCurve = _chunkSMJGE7HMcjs.videoTransitionAudioCurve; exports.videoTransitionEase = _chunkSMJGE7HMcjs.videoTransitionEase; exports.videoTransitionHasDirection = videoTransitionHasDirection; exports.videoValueAt = _chunkSMJGE7HMcjs.videoValueAt; exports.videoVectorscope = videoVectorscope; exports.videoVectorscopeAngle = videoVectorscopeAngle; exports.videoVectorscopeImage = videoVectorscopeImage; exports.videoVectorscopePoint = videoVectorscopePoint; exports.videoWaveformImage = videoWaveformImage; exports.videoWaveformPath = videoWaveformPath; exports.videoWipeRect = _chunkSMJGE7HMcjs.videoWipeRect; exports.videoWordAt = videoWordAt; exports.videoWordsRange = videoWordsRange;
6254
4591
  //# sourceMappingURL=index.cjs.map