@pixerate/editor 0.86.0 → 0.87.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.
@@ -1,4 +1,4 @@
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;
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;
2
2
 
3
3
  var _chunkWAJQKWQUcjs = require('../chunk-WAJQKWQU.cjs');
4
4
 
@@ -2124,6 +2124,379 @@ var VideoAudioPeaksCache = (_class2 = class {
2124
2124
  }
2125
2125
  }, _class2);
2126
2126
 
2127
+ // src/video-editor/scopes.ts
2128
+ var VIDEO_SCOPE_KINDS = ["histogram", "waveform", "vectorscope"];
2129
+ var videoLuma = (r, g, b) => 0.2126 * r + 0.7152 * g + 0.0722 * b;
2130
+ function videoChroma(r, g, b) {
2131
+ const y = videoLuma(r, g, b);
2132
+ return [(b - y) / 1.8556, (r - y) / 1.5748];
2133
+ }
2134
+ function eachPixel(pixels, maxPixels, visit) {
2135
+ const { width, height, data } = pixels;
2136
+ if (!(width > 0 && height > 0) || data.length < width * height * 4) throw new Error(`The pixels are not ${width} \xD7 ${height} RGBA: there are ${data.length} values, and ${width * height * 4} are needed.`);
2137
+ const step = Math.max(1, Math.sqrt(width * height / Math.max(1, _nullishCoalesce(maxPixels, () => ( 262144)))));
2138
+ let count = 0;
2139
+ for (let fy = step / 2; fy < height; fy += step) {
2140
+ const y = Math.floor(fy);
2141
+ for (let fx = step / 2; fx < width; fx += step) {
2142
+ const x = Math.floor(fx);
2143
+ const i = (y * width + x) * 4;
2144
+ visit(data[i], data[i + 1], data[i + 2], x, y);
2145
+ count++;
2146
+ }
2147
+ }
2148
+ return count;
2149
+ }
2150
+ var binOf = (v, bins) => Math.min(bins - 1, Math.floor((v + 1e-6) / 256 * bins));
2151
+ function videoHistogram(pixels, options = {}) {
2152
+ const bins = Math.max(1, Math.min(256, Math.round(_nullishCoalesce(options.bins, () => ( 256)))));
2153
+ const out = { bins, r: Array(bins).fill(0), g: Array(bins).fill(0), b: Array(bins).fill(0), luma: Array(bins).fill(0), total: 0 };
2154
+ out.total = eachPixel(pixels, options.maxPixels, (r, g, b) => {
2155
+ out.r[binOf(r, bins)]++;
2156
+ out.g[binOf(g, bins)]++;
2157
+ out.b[binOf(b, bins)]++;
2158
+ out.luma[binOf(videoLuma(r, g, b), bins)]++;
2159
+ });
2160
+ return out;
2161
+ }
2162
+ function videoLumaWaveform(pixels, options = {}) {
2163
+ const columns = Math.max(1, Math.min(pixels.width, Math.round(_nullishCoalesce(options.columns, () => ( Math.min(pixels.width, 256))))));
2164
+ const levels = Math.max(2, Math.min(256, Math.round(_nullishCoalesce(options.levels, () => ( 128)))));
2165
+ const data = new Uint32Array(columns * levels);
2166
+ let peak = 0;
2167
+ eachPixel(pixels, options.maxPixels, (r, g, b, x) => {
2168
+ const c = Math.min(columns - 1, Math.floor(x / pixels.width * columns));
2169
+ const i = c * levels + binOf(videoLuma(r, g, b), levels);
2170
+ if (++data[i] > peak) peak = data[i];
2171
+ });
2172
+ return { columns, levels, data, peak };
2173
+ }
2174
+ var videoVectorscopePoint = (cb, cr, size) => [(cb + 0.5) * (size - 1), (0.5 - cr) * (size - 1)];
2175
+ function videoVectorscope(pixels, options = {}) {
2176
+ const size = Math.max(16, Math.min(512, Math.round(_nullishCoalesce(options.size, () => ( 128)))));
2177
+ const data = new Uint32Array(size * size);
2178
+ let peak = 0;
2179
+ eachPixel(pixels, options.maxPixels, (r, g, b) => {
2180
+ const [cb, cr] = videoChroma(r / 255, g / 255, b / 255);
2181
+ const [x, y] = videoVectorscopePoint(cb, cr, size);
2182
+ const i = Math.round(y) * size + Math.round(x);
2183
+ if (++data[i] > peak) peak = data[i];
2184
+ });
2185
+ return { size, data, peak };
2186
+ }
2187
+ var round3 = (v) => Math.round(v * 1e3) / 1e3;
2188
+ var hex2 = (v) => Math.round(Math.min(255, Math.max(0, v))).toString(16).padStart(2, "0");
2189
+ function videoExposureStats(pixels, options = {}) {
2190
+ const low = (_nullishCoalesce(options.shadowLevel, () => ( 0.02))) * 255;
2191
+ const high = (_nullishCoalesce(options.highlightLevel, () => ( 0.98))) * 255;
2192
+ const lumaBins = new Uint32Array(256);
2193
+ let sumY = 0;
2194
+ let sumR = 0;
2195
+ let sumG = 0;
2196
+ let sumB = 0;
2197
+ let sumSat = 0;
2198
+ let maxSat = 0;
2199
+ let shadows = 0;
2200
+ let highlights = 0;
2201
+ let cr = 0;
2202
+ let cg = 0;
2203
+ let cbl = 0;
2204
+ const total = eachPixel(pixels, options.maxPixels, (r, g, b) => {
2205
+ const y = videoLuma(r, g, b);
2206
+ lumaBins[Math.min(255, Math.round(y))]++;
2207
+ sumY += y;
2208
+ sumR += r;
2209
+ sumG += g;
2210
+ sumB += b;
2211
+ const [u, v] = videoChroma(r / 255, g / 255, b / 255);
2212
+ const sat = Math.min(1, Math.hypot(u, v) / 0.5);
2213
+ sumSat += sat;
2214
+ if (sat > maxSat) maxSat = sat;
2215
+ if (y <= low) shadows++;
2216
+ if (y >= high) highlights++;
2217
+ if (r >= high) cr++;
2218
+ if (g >= high) cg++;
2219
+ if (b >= high) cbl++;
2220
+ });
2221
+ const percentile = (p) => {
2222
+ const target = p * total;
2223
+ let seen = 0;
2224
+ for (let i = 0; i < 256; i++) {
2225
+ seen += lumaBins[i];
2226
+ if (seen >= target && seen > 0) return i / 255;
2227
+ }
2228
+ return 1;
2229
+ };
2230
+ let min = 0;
2231
+ while (min < 255 && !lumaBins[min]) min++;
2232
+ let max = 255;
2233
+ while (max > 0 && !lumaBins[max]) max--;
2234
+ return {
2235
+ pixels: total,
2236
+ luma: { mean: round3(sumY / total / 255), median: round3(percentile(0.5)), min: round3(min / 255), max: round3(max / 255), p1: round3(percentile(0.01)), p99: round3(percentile(0.99)) },
2237
+ clipped: { shadows: round3(shadows / total), highlights: round3(highlights / total), r: round3(cr / total), g: round3(cg / total), b: round3(cbl / total) },
2238
+ saturation: { mean: round3(sumSat / total), max: round3(maxSat) },
2239
+ average: `#${hex2(sumR / total)}${hex2(sumG / total)}${hex2(sumB / total)}`
2240
+ };
2241
+ }
2242
+ function videoExposureNotes(stats) {
2243
+ const notes = [];
2244
+ const pct = (v2) => `${Math.round(v2 * 1e3) / 10}%`;
2245
+ if (stats.clipped.shadows >= 0.05) notes.push(`${pct(stats.clipped.shadows)} of the picture is crushed to black; raise exposure or lower contrast.`);
2246
+ if (stats.clipped.highlights >= 0.02) notes.push(`${pct(stats.clipped.highlights)} of the picture is blown to white; lower exposure.`);
2247
+ else {
2248
+ const channels = ["r", "g", "b"].filter((k) => stats.clipped[k] >= 0.05);
2249
+ if (channels.length) notes.push(`The ${channels.map((k) => ({ r: "red", g: "green", b: "blue" })[k]).join(" and ")} channel${channels.length > 1 ? "s clip" : " clips"} in ${pct(Math.max(...channels.map((k) => stats.clipped[k])))} of the picture; lower exposure or saturation.`);
2250
+ }
2251
+ if (stats.luma.mean < 0.2 && stats.clipped.shadows < 0.05) notes.push(`The picture is dark (mean luma ${stats.luma.mean}); raise exposure if it is not meant to be.`);
2252
+ if (stats.luma.mean > 0.75 && stats.clipped.highlights < 0.02) notes.push(`The picture is bright (mean luma ${stats.luma.mean}); lower exposure if it is not meant to be.`);
2253
+ if (stats.luma.p99 - stats.luma.p1 < 0.3 && stats.pixels > 1) notes.push(`The picture is flat (most of it lies between luma ${stats.luma.p1} and ${stats.luma.p99}); raise contrast.`);
2254
+ const [r, g, b] = [1, 3, 5].map((i) => parseInt(stats.average.slice(i, i + 2), 16) / 255);
2255
+ const [u, v] = videoChroma(r, g, b);
2256
+ if (Math.hypot(u, v) > 0.04 && stats.saturation.mean < 0.35) {
2257
+ const warm = v - u;
2258
+ notes.push(`The picture has a ${Math.abs(warm) > Math.abs(v + u) * 0.5 ? warm > 0 ? "warm (orange)" : "cool (blue)" : v > 0 ? "magenta" : "green"} cast (average ${stats.average}); adjust temperature and tint.`);
2259
+ }
2260
+ return notes;
2261
+ }
2262
+ var videoVectorscopeAngle = (cb, cr) => (Math.atan2(cr, cb) * 180 / Math.PI % 360 + 360) % 360;
2263
+ function summarizeVideoScopes(pixels, options = {}) {
2264
+ const stats = videoExposureStats(pixels, options);
2265
+ const bins = Math.max(2, Math.min(64, Math.round(_nullishCoalesce(options.bins, () => ( 16)))));
2266
+ const h = videoHistogram(pixels, { ...options, bins });
2267
+ const share = (a) => a.map((v) => round3(v / h.total));
2268
+ const columns = Math.max(1, Math.min(32, Math.round(_nullishCoalesce(options.columns, () => ( 8)))));
2269
+ const w = videoLumaWaveform(pixels, { ...options, columns, levels: 256 });
2270
+ const waveform = Array.from({ length: w.columns }, (_, c) => {
2271
+ const counts = w.data.subarray(c * 256, c * 256 + 256);
2272
+ const n2 = counts.reduce((a, b) => a + b, 0);
2273
+ let lo = 0;
2274
+ while (lo < 255 && !counts[lo]) lo++;
2275
+ let hi = 255;
2276
+ while (hi > 0 && !counts[hi]) hi--;
2277
+ let seen = 0;
2278
+ let median = 0;
2279
+ for (let i = 0; i < 256; i++) {
2280
+ seen += counts[i];
2281
+ if (seen >= n2 / 2 && n2) {
2282
+ median = i;
2283
+ break;
2284
+ }
2285
+ }
2286
+ return { start: round3(c / w.columns), end: round3((c + 1) / w.columns), min: round3(lo / 255), median: round3(median / 255), max: round3(hi / 255) };
2287
+ }).filter((_, c) => w.data.subarray(c * 256, c * 256 + 256).some((v) => v > 0));
2288
+ const chroma = [];
2289
+ eachPixel(pixels, Math.min(_nullishCoalesce(options.maxPixels, () => ( 65536)), 65536), (r, g, b) => {
2290
+ const [u, v] = videoChroma(r / 255, g / 255, b / 255);
2291
+ chroma.push([u, v, Math.hypot(u, v)]);
2292
+ });
2293
+ chroma.sort((a, b) => b[2] - a[2]);
2294
+ const top = chroma.slice(0, Math.max(1, Math.ceil(chroma.length / 10)));
2295
+ const mu = top.reduce((s, c) => s + c[0], 0) / top.length;
2296
+ const mv = top.reduce((s, c) => s + c[1], 0) / top.length;
2297
+ const strength = Math.min(1, top.reduce((s, c) => s + c[2], 0) / top.length / 0.5);
2298
+ const coherence = Math.hypot(mu, mv) / Math.max(1e-9, top.reduce((s, c) => s + c[2], 0) / top.length);
2299
+ return {
2300
+ stats,
2301
+ notes: videoExposureNotes(stats),
2302
+ histogram: { bins, luma: share(h.luma), r: share(h.r), g: share(h.g), b: share(h.b) },
2303
+ waveform,
2304
+ vectorscope: { hue: strength < 0.02 ? null : Math.round(videoVectorscopeAngle(mu, mv)), strength: round3(strength), spread: round3(1 - coherence) }
2305
+ };
2306
+ }
2307
+ var VIDEO_VECTORSCOPE_TARGETS = [
2308
+ ["R", [0.75, 0, 0]],
2309
+ ["Mg", [0.75, 0, 0.75]],
2310
+ ["B", [0, 0, 0.75]],
2311
+ ["Cy", [0, 0.75, 0.75]],
2312
+ ["G", [0, 0.75, 0]],
2313
+ ["Yl", [0.75, 0.75, 0]]
2314
+ ].map(([name, rgb]) => {
2315
+ const [cb, cr] = videoChroma(rgb[0], rgb[1], rgb[2]);
2316
+ return { name, rgb: [...rgb], cb, cr };
2317
+ });
2318
+ function canvas(width, height) {
2319
+ const data = new Uint8ClampedArray(width * height * 4);
2320
+ for (let i = 0; i < data.length; i += 4) {
2321
+ data[i] = 12;
2322
+ data[i + 1] = 14;
2323
+ data[i + 2] = 18;
2324
+ data[i + 3] = 255;
2325
+ }
2326
+ return { width, height, data };
2327
+ }
2328
+ function plot(img, x, y, rgb, alpha = 1, add = false) {
2329
+ const xi = Math.round(x);
2330
+ const yi = Math.round(y);
2331
+ if (xi < 0 || yi < 0 || xi >= img.width || yi >= img.height) return;
2332
+ const i = (yi * img.width + xi) * 4;
2333
+ for (let k = 0; k < 3; k++) img.data[i + k] = add ? img.data[i + k] + rgb[k] * alpha : img.data[i + k] * (1 - alpha) + rgb[k] * alpha;
2334
+ }
2335
+ var GRID = [70, 76, 88];
2336
+ var glow = (count, peak) => count ? 0.25 + 0.75 * (Math.log1p(count) / Math.log1p(Math.max(1, peak))) : 0;
2337
+ function videoHistogramImage(h, width = 256, height = 128) {
2338
+ const img = canvas(width, height);
2339
+ for (const q of [0.25, 0.5, 0.75]) for (let y = 0; y < height; y++) plot(img, q * (width - 1), y, GRID);
2340
+ const inner = (a) => a.length > 2 ? a.slice(1, -1) : a;
2341
+ const peak = Math.max(1, ...inner(h.r), ...inner(h.g), ...inner(h.b), ...inner(h.luma));
2342
+ const height01 = (v) => Math.min(1, Math.sqrt(v / peak));
2343
+ for (let x = 0; x < width; x++) {
2344
+ const bin = Math.min(h.bins - 1, Math.floor(x / width * h.bins));
2345
+ const channels = [
2346
+ [h.r, [200, 40, 40]],
2347
+ [h.g, [40, 180, 40]],
2348
+ [h.b, [50, 80, 220]]
2349
+ ];
2350
+ for (const [a, rgb] of channels) {
2351
+ const top = height - height01(a[bin]) * height;
2352
+ for (let y = Math.ceil(top); y < height; y++) plot(img, x, y, rgb, 0.8, true);
2353
+ }
2354
+ plot(img, x, height - 1 - height01(h.luma[bin]) * (height - 1), [255, 255, 255]);
2355
+ }
2356
+ return img;
2357
+ }
2358
+ function videoWaveformImage(w, width = 256, height = 128) {
2359
+ const img = canvas(width, height);
2360
+ for (const q of [0, 0.25, 0.5, 0.75, 1]) for (let x = 0; x < width; x++) plot(img, x, (1 - q) * (height - 1), GRID);
2361
+ for (let x = 0; x < width; x++) {
2362
+ const c = Math.min(w.columns - 1, Math.floor(x / width * w.columns));
2363
+ for (let y = 0; y < height; y++) {
2364
+ const level = Math.min(w.levels - 1, Math.floor((height - 1 - y) / height * w.levels));
2365
+ const a = glow(w.data[c * w.levels + level], w.peak);
2366
+ if (a) plot(img, x, y, [120, 255, 140], a);
2367
+ }
2368
+ }
2369
+ return img;
2370
+ }
2371
+ function videoVectorscopeImage(v, size = v.size) {
2372
+ const img = canvas(size, size);
2373
+ const c = (size - 1) / 2;
2374
+ for (let a = 0; a < 720; a++) {
2375
+ const t = a / 720 * 2 * Math.PI;
2376
+ plot(img, c + Math.cos(t) * c, c - Math.sin(t) * c, GRID);
2377
+ }
2378
+ const skin = 123 * Math.PI / 180;
2379
+ for (let r = 0; r < c; r++) plot(img, c + Math.cos(skin) * r, c - Math.sin(skin) * r, [140, 110, 90]);
2380
+ for (const t of VIDEO_VECTORSCOPE_TARGETS) {
2381
+ const [x, y] = videoVectorscopePoint(t.cb, t.cr, size);
2382
+ const rgb = t.rgb.map((k) => 80 + k * 200);
2383
+ for (let k = -3; k <= 3; k++) {
2384
+ plot(img, x + k, y - 3, rgb);
2385
+ plot(img, x + k, y + 3, rgb);
2386
+ plot(img, x - 3, y + k, rgb);
2387
+ plot(img, x + 3, y + k, rgb);
2388
+ }
2389
+ }
2390
+ for (let y = 0; y < size; y++)
2391
+ for (let x = 0; x < size; x++) {
2392
+ const sy = Math.min(v.size - 1, Math.floor(y / size * v.size));
2393
+ const sx = Math.min(v.size - 1, Math.floor(x / size * v.size));
2394
+ const a = glow(v.data[sy * v.size + sx], v.peak);
2395
+ if (!a) continue;
2396
+ const cb = sx / (v.size - 1) - 0.5;
2397
+ const cr = 0.5 - sy / (v.size - 1);
2398
+ const r = Math.min(1, Math.max(0, 0.6 + 1.5748 * cr));
2399
+ const g = Math.min(1, Math.max(0, 0.6 - 0.1873 * cb - 0.4681 * cr));
2400
+ const b = Math.min(1, Math.max(0, 0.6 + 1.8556 * cb));
2401
+ plot(img, x, y, [r * 255, g * 255, b * 255], a);
2402
+ }
2403
+ return img;
2404
+ }
2405
+ function videoScopeImage(kind, pixels, width = 256, height = 128, options = {}) {
2406
+ if (kind === "histogram") return videoHistogramImage(videoHistogram(pixels, { ...options, bins: Math.min(256, width) }), width, height);
2407
+ if (kind === "waveform") return videoWaveformImage(videoLumaWaveform(pixels, { ...options, columns: Math.min(pixels.width, width), levels: Math.min(256, height) }), width, height);
2408
+ const size = Math.min(width, height);
2409
+ return videoVectorscopeImage(videoVectorscope(pixels, { ...options, size }), size);
2410
+ }
2411
+ var VideoPreviewPixels = (_class3 = class {constructor() { _class3.prototype.__init6.call(this);_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this); }
2412
+ __init6() {this.reader = null}
2413
+ __init7() {this.drawnAt = null}
2414
+ __init8() {this.listeners = /* @__PURE__ */ new Set()}
2415
+ /** Gives the canvas to read (the latest preview wins). Returns a function that takes it away again. */
2416
+ attach(reader) {
2417
+ this.reader = reader;
2418
+ this.drawnAt = null;
2419
+ return () => {
2420
+ if (this.reader !== reader) return;
2421
+ this.reader = null;
2422
+ this.drawnAt = null;
2423
+ this.emit();
2424
+ };
2425
+ }
2426
+ /** Whether a preview is attached. */
2427
+ get attached() {
2428
+ return this.reader !== null;
2429
+ }
2430
+ /** The preview drew the frame at `time`; `complete` is false while a picture was missing or stale. */
2431
+ drawn(time, complete) {
2432
+ this.drawnAt = { time, complete };
2433
+ this.emit();
2434
+ }
2435
+ /** The last frame drawn, with its pixels; `null` without a preview. Throws when the canvas cannot be read. */
2436
+ read() {
2437
+ if (!this.reader) return null;
2438
+ try {
2439
+ return this.reader();
2440
+ } catch (e) {
2441
+ const reason = e instanceof Error ? e.message : String(e);
2442
+ throw new Error(`The preview's pixels cannot be read (${reason}). Media from another origin must be served with CORS headers (Access-Control-Allow-Origin) for its pixels to be measured.`);
2443
+ }
2444
+ }
2445
+ /** Called after each frame the preview draws. */
2446
+ subscribe(listener) {
2447
+ this.listeners.add(listener);
2448
+ return () => {
2449
+ this.listeners.delete(listener);
2450
+ };
2451
+ }
2452
+ /**
2453
+ * The frame at `time` once it is complete: now, if the last frame drawn was it, else the next
2454
+ * complete drawing of it. Fails after `timeout` ms (default 5000).
2455
+ */
2456
+ next(time, timeout = 5e3) {
2457
+ const matches = () => this.drawnAt && this.drawnAt.complete && Math.abs(this.drawnAt.time - time) < 0.5;
2458
+ return new Promise((resolve, reject) => {
2459
+ const done = (fn) => {
2460
+ clearTimeout(timer);
2461
+ off();
2462
+ fn();
2463
+ };
2464
+ const check = () => {
2465
+ if (!this.reader) return done(() => reject(new Error("The preview was closed before it drew the frame.")));
2466
+ if (!matches()) return;
2467
+ try {
2468
+ const snap = this.read();
2469
+ done(() => snap ? resolve(snap) : reject(new Error("The preview has no pixels to read.")));
2470
+ } catch (e) {
2471
+ done(() => reject(e));
2472
+ }
2473
+ };
2474
+ const off = this.subscribe(check);
2475
+ const timer = setTimeout(() => done(() => reject(new Error(`The preview did not finish drawing ${Math.round(time)} ms within ${timeout} ms; its media may still be loading or may have failed to load. Try again, or check the asset sources.`))), timeout);
2476
+ check();
2477
+ });
2478
+ }
2479
+ emit() {
2480
+ for (const l of [...this.listeners]) l();
2481
+ }
2482
+ }, _class3);
2483
+ function readVideoScopesPanel(feed, kinds = VIDEO_SCOPE_KINDS, options = {}) {
2484
+ let snap;
2485
+ try {
2486
+ snap = feed.read();
2487
+ } catch (e) {
2488
+ return { ok: false, message: e instanceof Error ? e.message : String(e) };
2489
+ }
2490
+ if (!snap) return { ok: false, message: feed.attached ? "The preview has not drawn a frame yet." : "Show the preview to see its scopes." };
2491
+ const width = _nullishCoalesce(options.width, () => ( 256));
2492
+ const height = _nullishCoalesce(options.height, () => ( 128));
2493
+ const opts = { maxPixels: _nullishCoalesce(options.maxPixels, () => ( 65536)) };
2494
+ const images = {};
2495
+ for (const kind of kinds) images[kind] = videoScopeImage(kind, snap.pixels, width, height, opts);
2496
+ const stats = videoExposureStats(snap.pixels, opts);
2497
+ return { ok: true, time: snap.time, complete: snap.complete, images, stats, notes: videoExposureNotes(stats) };
2498
+ }
2499
+
2127
2500
  // src/video-editor/transcript.ts
2128
2501
  var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
2129
2502
  function cueTime(s) {
@@ -2845,26 +3218,40 @@ function buildFfmpeg(project, options, measure) {
2845
3218
  }
2846
3219
 
2847
3220
  // src/video-editor/preview.ts
2848
- var VideoPreviewEngine = (_class3 = class {
2849
- constructor(source, canvas, options = {}) {;_class3.prototype.__init6.call(this);_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);
3221
+ var VideoPreviewEngine = (_class4 = class {
3222
+ constructor(source, canvas2, 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);
2850
3223
  this.source = source;
2851
3224
  this.options = options;
2852
- this.canvas = canvas;
2853
- this.ctx = options.context !== void 0 ? options.context : _nullishCoalesce(_optionalChain([canvas, 'access', _73 => _73.getContext, 'optionalCall', _74 => _74("2d")]), () => ( null));
3225
+ this.canvas = canvas2;
3226
+ this.ctx = options.context !== void 0 ? options.context : _nullishCoalesce(_optionalChain([canvas2, 'access', _73 => _73.getContext, 'optionalCall', _74 => _74("2d")]), () => ( null));
2854
3227
  this.unsubscribe = source.subscribe(() => this.update());
3228
+ const pixels = options.pixels !== void 0 ? options.pixels : source.previewPixels;
3229
+ this.detachPixels = pixels ? pixels.attach(() => this.readPixels()) : () => {
3230
+ };
3231
+ this.pixelsTarget = _nullishCoalesce(pixels, () => ( null));
2855
3232
  this.update();
2856
3233
  }
2857
3234
 
2858
3235
 
2859
3236
 
2860
3237
 
2861
- __init6() {this.media = /* @__PURE__ */ new Map()}
2862
- __init7() {this.images = /* @__PURE__ */ new Map()}
3238
+ __init9() {this.media = /* @__PURE__ */ new Map()}
3239
+ __init10() {this.images = /* @__PURE__ */ new Map()}
3240
+
3241
+ __init11() {this.scheduled = false}
3242
+ __init12() {this.destroyed = false}
3243
+ __init13() {this.filterHost = null}
3244
+ __init14() {this.filters = /* @__PURE__ */ new Set()}
3245
+
2863
3246
 
2864
- __init8() {this.scheduled = false}
2865
- __init9() {this.destroyed = false}
2866
- __init10() {this.filterHost = null}
2867
- __init11() {this.filters = /* @__PURE__ */ new Set()}
3247
+ __init15() {this.drawn = null}
3248
+ __init16() {this.complete = true}
3249
+ /** The canvas's pixels and the frame they show, or `null` when its context cannot be read. */
3250
+ readPixels() {
3251
+ const ctx = this.ctx;
3252
+ if (!_optionalChain([ctx, 'optionalAccess', _75 => _75.getImageData]) || !this.drawn) return null;
3253
+ return { ...this.drawn, pixels: ctx.getImageData(0, 0, this.canvas.width, this.canvas.height) };
3254
+ }
2868
3255
  createMedia(kind) {
2869
3256
  if (this.options.createMedia) return this.options.createMedia(kind);
2870
3257
  const el = document.createElement(kind);
@@ -2900,8 +3287,8 @@ var VideoPreviewEngine = (_class3 = class {
2900
3287
  entry.media.pause();
2901
3288
  entry.media.removeEventListener("seeked", entry.onReady);
2902
3289
  entry.media.removeEventListener("loadeddata", entry.onReady);
2903
- _optionalChain([entry, 'access', _75 => _75.media, 'access', _76 => _76.removeAttribute, 'optionalCall', _77 => _77("src")]);
2904
- _optionalChain([entry, 'access', _78 => _78.media, 'access', _79 => _79.load, 'optionalCall', _80 => _80()]);
3290
+ _optionalChain([entry, 'access', _76 => _76.media, 'access', _77 => _77.removeAttribute, 'optionalCall', _78 => _78("src")]);
3291
+ _optionalChain([entry, 'access', _79 => _79.media, 'access', _80 => _80.load, 'optionalCall', _81 => _81()]);
2905
3292
  this.media.delete(entry.clip);
2906
3293
  }
2907
3294
  /** Brings the media elements in step with the editor and draws the frame. */
@@ -2935,7 +3322,7 @@ var VideoPreviewEngine = (_class3 = class {
2935
3322
  for (const layer of frame.layers) {
2936
3323
  if (layer.type !== "media" || layer.assetKind !== "video" || !layer.src) continue;
2937
3324
  const sound = sounding.get(layer.clip);
2938
- sync(layer.clip, layer.src, "video", _nullishCoalesce(layer.sourceTime, () => ( 0)), _nullishCoalesce(_optionalChain([sound, 'optionalAccess', _81 => _81.speed]), () => ( 1)), sound ? sound.gain : null);
3325
+ sync(layer.clip, layer.src, "video", _nullishCoalesce(layer.sourceTime, () => ( 0)), _nullishCoalesce(_optionalChain([sound, 'optionalAccess', _82 => _82.speed]), () => ( 1)), sound ? sound.gain : null);
2939
3326
  sounding.delete(layer.clip);
2940
3327
  }
2941
3328
  for (const a of sounding.values()) sync(a.clip, a.src, "audio", a.sourceTime, a.speed, a.gain);
@@ -2950,13 +3337,15 @@ var VideoPreviewEngine = (_class3 = class {
2950
3337
  if (!this.destroyed) this.draw(videoFrameAt(this.source.getState().project, this.source.getState().time));
2951
3338
  });
2952
3339
  }
2953
- __init12() {this.sourceFor = (layer) => {
3340
+ __init17() {this.sourceFor = (layer) => {
2954
3341
  if (layer.assetKind === "image" && layer.src) {
2955
3342
  const img = this.image(layer.src);
2956
3343
  return img.complete ? img : null;
2957
3344
  }
2958
3345
  const entry = this.media.get(layer.clip);
2959
- return entry && entry.media.readyState >= 2 ? entry.media : null;
3346
+ if (!entry || entry.media.readyState < 2) return null;
3347
+ if (entry.media.seeking) this.complete = false;
3348
+ return entry.media;
2960
3349
  }}
2961
3350
  draw(frame) {
2962
3351
  if (!this.ctx) return;
@@ -2967,10 +3356,18 @@ var VideoPreviewEngine = (_class3 = class {
2967
3356
  const h = Math.max(1, Math.round(frame.height * resolution));
2968
3357
  if (this.canvas.width !== w) this.canvas.width = w;
2969
3358
  if (this.canvas.height !== h) this.canvas.height = h;
2970
- drawVideoFrame(this.ctx, frame, this.sourceFor, resolution, { defineSvgFilter: this.defineSvgFilter });
2971
- _optionalChain([this, 'access', _82 => _82.options, 'access', _83 => _83.onFrame, 'optionalCall', _84 => _84(frame)]);
3359
+ this.complete = true;
3360
+ const sourceFor = (layer) => {
3361
+ const source = this.sourceFor(layer);
3362
+ if (!source) this.complete = false;
3363
+ return source;
3364
+ };
3365
+ drawVideoFrame(this.ctx, frame, sourceFor, resolution, { defineSvgFilter: this.defineSvgFilter });
3366
+ this.drawn = { time: frame.time, complete: this.complete };
3367
+ _optionalChain([this, 'access', _83 => _83.options, 'access', _84 => _84.onFrame, 'optionalCall', _85 => _85(frame)]);
3368
+ _optionalChain([this, 'access', _86 => _86.pixelsTarget, 'optionalAccess', _87 => _87.drawn, 'call', _88 => _88(frame.time, this.complete)]);
2972
3369
  }
2973
- __init13() {this.defineSvgFilter = (id, markup) => {
3370
+ __init18() {this.defineSvgFilter = (id, markup) => {
2974
3371
  if (this.options.defineSvgFilter === null) return;
2975
3372
  if (this.options.defineSvgFilter) return this.options.defineSvgFilter(id, markup);
2976
3373
  if (this.filters.has(id) || typeof document === "undefined") return;
@@ -2989,33 +3386,36 @@ var VideoPreviewEngine = (_class3 = class {
2989
3386
  /** Stops following the editor and releases every media element. */
2990
3387
  destroy() {
2991
3388
  this.destroyed = true;
2992
- _optionalChain([this, 'access', _85 => _85.filterHost, 'optionalAccess', _86 => _86.remove, 'call', _87 => _87()]);
3389
+ this.detachPixels();
3390
+ _optionalChain([this, 'access', _89 => _89.filterHost, 'optionalAccess', _90 => _90.remove, 'call', _91 => _91()]);
2993
3391
  this.filterHost = null;
2994
3392
  this.filters.clear();
2995
3393
  this.unsubscribe();
2996
3394
  for (const entry of [...this.media.values()]) this.release(entry);
2997
3395
  this.images.clear();
2998
3396
  }
2999
- }, _class3);
3397
+ }, _class4);
3000
3398
 
3001
3399
  // src/video-editor/controller.ts
3002
- var VideoEditorController = (_class4 = class {
3400
+ var VideoEditorController = (_class5 = class {
3003
3401
 
3004
3402
 
3005
3403
  /** Sound levels per asset, loaded on demand for waveforms and agents. */
3006
3404
 
3405
+ /** The pixels the preview last drew, for scopes and `get_video_scopes` (a `VideoPreviewEngine` fills it). */
3406
+ __init19() {this.previewPixels = new VideoPreviewPixels()}
3007
3407
 
3008
3408
 
3009
- __init14() {this.selection = []}
3409
+ __init20() {this.selection = []}
3010
3410
 
3011
- __init15() {this.error = null}
3012
- __init16() {this.revision = 0}
3013
- __init17() {this.applying = false}
3411
+ __init21() {this.error = null}
3412
+ __init22() {this.revision = 0}
3413
+ __init23() {this.applying = false}
3014
3414
 
3015
- __init18() {this.listeners = /* @__PURE__ */ new Set()}
3016
- __init19() {this.unsubscribers = []}
3415
+ __init24() {this.listeners = /* @__PURE__ */ new Set()}
3416
+ __init25() {this.unsubscribers = []}
3017
3417
 
3018
- constructor(options = {}) {;_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);_class4.prototype.__init21.call(this);
3418
+ constructor(options = {}) {;_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);_class5.prototype.__init25.call(this);_class5.prototype.__init26.call(this);_class5.prototype.__init27.call(this);
3019
3419
  this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
3020
3420
  this.audioPeaks = options.audioPeaks instanceof VideoAudioPeaksCache ? options.audioPeaks : new VideoAudioPeaksCache(options.audioPeaks);
3021
3421
  this.project = options.project ? parseVideoProject(options.project) : createVideoProject();
@@ -3040,8 +3440,8 @@ var VideoEditorController = (_class4 = class {
3040
3440
  this.player.pause();
3041
3441
  this.unsubscribers.splice(0).forEach((u) => u());
3042
3442
  }
3043
- __init20() {this.getState = () => this.state}
3044
- __init21() {this.subscribe = (listener) => {
3443
+ __init26() {this.getState = () => this.state}
3444
+ __init27() {this.subscribe = (listener) => {
3045
3445
  this.connect();
3046
3446
  this.listeners.add(listener);
3047
3447
  return () => {
@@ -3097,7 +3497,7 @@ var VideoEditorController = (_class4 = class {
3097
3497
  this.applying = false;
3098
3498
  }
3099
3499
  this.notify();
3100
- _optionalChain([this, 'access', _88 => _88.onChange, 'optionalCall', _89 => _89(project)]);
3500
+ _optionalChain([this, 'access', _92 => _92.onChange, 'optionalCall', _93 => _93(project)]);
3101
3501
  }
3102
3502
  commit(label, next) {
3103
3503
  const before = this.project;
@@ -3161,7 +3561,7 @@ var VideoEditorController = (_class4 = class {
3161
3561
  }
3162
3562
  /** Selects clips (unknown ids are dropped). `add` toggles them into the current selection. */
3163
3563
  select(clips, options = {}) {
3164
- const known = _nullishCoalesce(_optionalChain([clips, 'optionalAccess', _90 => _90.filter, 'call', _91 => _91((id) => findVideoClip(this.project, id))]), () => ( []));
3564
+ const known = _nullishCoalesce(_optionalChain([clips, 'optionalAccess', _94 => _94.filter, 'call', _95 => _95((id) => findVideoClip(this.project, id))]), () => ( []));
3165
3565
  let next;
3166
3566
  if (options.add) {
3167
3567
  next = [...this.selection];
@@ -3242,7 +3642,7 @@ var VideoEditorController = (_class4 = class {
3242
3642
  toEdl(options = {}) {
3243
3643
  return toVideoEdl(this.project, options);
3244
3644
  }
3245
- }, _class4);
3645
+ }, _class5);
3246
3646
 
3247
3647
  // src/video-editor/tools.ts
3248
3648
  var round = (ms) => Math.round(ms * 10) / 10;
@@ -3455,14 +3855,14 @@ function createVideoEditorToolProvider(controller, options = {}) {
3455
3855
  return null;
3456
3856
  };
3457
3857
  const placed = (o) => ({
3458
- ..._optionalChain([o, 'access', _92 => _92.created, 'optionalAccess', _93 => _93.length]) ? { created: o.created } : {},
3459
- ..._optionalChain([o, 'access', _94 => _94.trimmed, 'optionalAccess', _95 => _95.length]) ? { trimmed: o.trimmed } : {},
3460
- ..._optionalChain([o, 'access', _96 => _96.removed, 'optionalAccess', _97 => _97.length]) ? { removed: o.removed } : {},
3858
+ ..._optionalChain([o, 'access', _96 => _96.created, 'optionalAccess', _97 => _97.length]) ? { created: o.created } : {},
3859
+ ..._optionalChain([o, 'access', _98 => _98.trimmed, 'optionalAccess', _99 => _99.length]) ? { trimmed: o.trimmed } : {},
3860
+ ..._optionalChain([o, 'access', _100 => _100.removed, 'optionalAccess', _101 => _101.length]) ? { removed: o.removed } : {},
3461
3861
  ...o.rippled ? { rippled: o.rippled } : {}
3462
3862
  });
3463
3863
  const keysOut = (p, clip, property) => {
3464
3864
  const c = clipOut(p, clip);
3465
- const value = VIDEO_TRANSFORM_PROPERTIES.includes(property) ? _optionalChain([c, 'optionalAccess', _98 => _98.transform, 'optionalAccess', _99 => _99[property]]) : _optionalChain([c, 'optionalAccess', _100 => _100[property]]);
3865
+ const value = VIDEO_TRANSFORM_PROPERTIES.includes(property) ? _optionalChain([c, 'optionalAccess', _102 => _102.transform, 'optionalAccess', _103 => _103[property]]) : _optionalChain([c, 'optionalAccess', _104 => _104[property]]);
3466
3866
  return typeof value === "object" ? { keys: value.keys } : { keys: [], value: _nullishCoalesce(value, () => ( null)) };
3467
3867
  };
3468
3868
  const getProject = {
@@ -3760,7 +4160,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
3760
4160
  const at = findVideoClip(p, id);
3761
4161
  if (!at) throw new Error(`There is no clip "${id}"; get_video_project lists the clips.`);
3762
4162
  const { clip } = at;
3763
- if (at.track.kind !== "video" || clip.type === "media" && _optionalChain([p, 'access', _101 => _101.assets, 'access', _102 => _102.find, 'call', _103 => _103((a) => a.id === clip.asset), 'optionalAccess', _104 => _104.kind]) === "audio")
4163
+ if (at.track.kind !== "video" || clip.type === "media" && _optionalChain([p, 'access', _105 => _105.assets, 'access', _106 => _106.find, 'call', _107 => _107((a) => a.id === clip.asset), 'optionalAccess', _108 => _108.kind]) === "audio")
3764
4164
  throw new Error(`Clip "${id}" is sound only; grades change pictures. Pass clips on video tracks.`);
3765
4165
  }
3766
4166
  };
@@ -3781,7 +4181,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
3781
4181
  gradeTargets(clips);
3782
4182
  const grade = videoGradePreset(preset, _nullishCoalesce(amount, () => ( 1)));
3783
4183
  const edits = clips.map((clip) => grade ? { op: "update_clip", clip, set: { grade } } : { op: "update_clip", clip, remove: ["grade"] });
3784
- return apply(edits, `Apply ${preset} look`, (r) => ({ clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([findVideoClip, 'call', _105 => _105(r.project, id), 'optionalAccess', _106 => _106.clip, 'access', _107 => _107.grade]), () => ( null)) })) }));
4184
+ return apply(edits, `Apply ${preset} look`, (r) => ({ clips: clips.map((id) => ({ id, grade: _nullishCoalesce(_optionalChain([findVideoClip, 'call', _109 => _109(r.project, id), 'optionalAccess', _110 => _110.clip, 'access', _111 => _111.grade]), () => ( null)) })) }));
3785
4185
  }
3786
4186
  };
3787
4187
  const copyGrade = {
@@ -3848,7 +4248,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
3848
4248
  handler: ({ track, from, to, limit }) => {
3849
4249
  const all = timelineWords(track).filter((w) => w.end > (_nullishCoalesce(from, () => ( 0))) && w.start < (_nullishCoalesce(to, () => ( Infinity))));
3850
4250
  const words = all.slice(0, _nullishCoalesce(limit, () => ( 1e3)));
3851
- return { track: _nullishCoalesce(_optionalChain([words, 'access', _108 => _108[0], 'optionalAccess', _109 => _109.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 };
4251
+ return { track: _nullishCoalesce(_optionalChain([words, 'access', _112 => _112[0], 'optionalAccess', _113 => _113.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 };
3852
4252
  }
3853
4253
  };
3854
4254
  const findText = {
@@ -3910,7 +4310,7 @@ function createVideoEditorToolProvider(controller, options = {}) {
3910
4310
  if (!at) throw new Error(`There is no clip "${id}"; get_video_project lists the clips.`);
3911
4311
  const { clip } = at;
3912
4312
  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.`);
3913
- if (_optionalChain([project, 'call', _110 => _110(), 'access', _111 => _111.assets, 'access', _112 => _112.find, 'call', _113 => _113((a) => a.id === clip.asset), 'optionalAccess', _114 => _114.kind]) === "image") throw new Error(`Clip "${id}" is a still, so it has no motion to change the speed of.`);
4313
+ if (_optionalChain([project, 'call', _114 => _114(), 'access', _115 => _115.assets, 'access', _116 => _116.find, 'call', _117 => _117((a) => a.id === clip.asset), 'optionalAccess', _118 => _118.kind]) === "image") throw new Error(`Clip "${id}" is a still, so it has no motion to change the speed of.`);
3914
4314
  return clip;
3915
4315
  };
3916
4316
  const rampOut = (r, id) => {
@@ -4199,7 +4599,46 @@ function createVideoEditorToolProvider(controller, options = {}) {
4199
4599
  return { asset: r.asset.id, from: r.from, to: r.to, silences, total: round(silences.reduce((n2, x) => n2 + x.end - x.start, 0)) };
4200
4600
  }
4201
4601
  };
4202
- const reads = [getProject, getFrame, getTranscript, findText, findFillers, findPauses, findGaps, checkProject, renderFrame, exportFfmpeg, exportLoudnessMeasure, exportEdl, audioLevels, findSilences, seek];
4602
+ const pixelsAt = async (time) => {
4603
+ if (options.framePixels) {
4604
+ const px = await options.framePixels(time);
4605
+ if (!px) throw new Error(`The host could not render the frame at ${Math.round(time)} ms; use render_video_frame to look at it instead.`);
4606
+ return px;
4607
+ }
4608
+ const feed = controller.previewPixels;
4609
+ if (!feed.attached) throw new Error("No preview is showing this project, so there are no pixels to measure. The host must show a <VideoPreview> for this editor (or pass framePixels to the tool provider); until then use get_video_frame and render_video_frame.");
4610
+ const state = controller.getState();
4611
+ if (Math.abs(time - state.time) < 0.5) return (await feed.next(state.time, options.framePixelsTimeout)).pixels;
4612
+ const { time: before, playing } = state;
4613
+ if (playing) controller.pause();
4614
+ controller.seek(time);
4615
+ try {
4616
+ return (await feed.next(controller.getState().time, options.framePixelsTimeout)).pixels;
4617
+ } finally {
4618
+ controller.seek(before);
4619
+ if (playing) controller.play();
4620
+ }
4621
+ };
4622
+ const getScopes = {
4623
+ name: "get_video_scopes",
4624
+ title: "Measure a frame with scopes",
4625
+ description: "Measures the rendered picture at a moment as colour scopes would, without changing the project: luma (0 = black, 1 = white) mean, median, min, max and 1st / 99th percentiles; the share of pixels crushed to black or blown to white (and per channel); saturation; the average colour (a cast shows as a tint); a coarse luma and RGB histogram; the luma spread of vertical strips left to right (a waveform); the main hue and strength on a vectorscope; and notes on what to fix. Use it before and after grading (update_video_clip grade, apply_video_grade_preset) to check exposure and colour. Needs the pixels the host draws: fails with a reason when no preview is showing.",
4626
+ inputSchema: object({
4627
+ time: MS("Time from the start to measure (default: the playhead). Another time is shown briefly and the playhead put back"),
4628
+ bins: { type: "integer", minimum: 2, maximum: 64, description: "Histogram steps, darkest first (default 16)." },
4629
+ columns: { type: "integer", minimum: 1, maximum: 32, description: "Waveform strips across the picture, left to right (default 8)." }
4630
+ }),
4631
+ annotations: { title: "Measure a frame with scopes", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
4632
+ handler: async ({ time, bins, columns }) => {
4633
+ const p = project();
4634
+ const end = videoProjectDuration(p);
4635
+ const t = _nullishCoalesce(time, () => ( controller.getState().time));
4636
+ if (time !== void 0 && time > end) throw new Error(`Time ${time} ms is past the end of the project (${end} ms).`);
4637
+ const pixels = await pixelsAt(t);
4638
+ return { time: t, timecode: formatVideoTimecode(t, p.fps), width: pixels.width, height: pixels.height, ...summarizeVideoScopes(pixels, { bins, columns }) };
4639
+ }
4640
+ };
4641
+ const reads = [getProject, getFrame, getTranscript, findText, findFillers, findPauses, findGaps, checkProject, renderFrame, getScopes, exportFfmpeg, exportLoudnessMeasure, exportEdl, audioLevels, findSilences, seek];
4203
4642
  const writes = [setSettings, addAsset, updateAsset, removeAsset, addTrack, updateTrack, removeTrack, moveTrack, addClip, moveClip, trimClip, splitClip, deleteClips, updateClip, setTransition, setSpeedRamp, addFreeze, removeRamp, setTranscript, removeRanges, applyGradePreset, copyGrade, setKeyframes, removeKeyframes, slipClip, rollEdit, slideClip, liftRange, extractRange, splitAt, closeGaps, addMarker, removeMarker, editVideo, undo, redo];
4204
4643
  return {
4205
4644
  id: _nullishCoalesce(options.id, () => ( "video")),
@@ -4286,7 +4725,7 @@ function videoClipLabel(clip, asset) {
4286
4725
  if (clip.name) return clip.name;
4287
4726
  if (clip.type === "text") return clip.text.split("\n")[0];
4288
4727
  if (clip.type === "color") return clip.color;
4289
- const base = _nullishCoalesce(_nullishCoalesce(_optionalChain([asset, 'optionalAccess', _115 => _115.name]), () => ( _optionalChain([asset, 'optionalAccess', _116 => _116.src, 'access', _117 => _117.replace, 'call', _118 => _118(/^.*[\\/]/, "")]))), () => ( clip.asset));
4728
+ const base = _nullishCoalesce(_nullishCoalesce(_optionalChain([asset, 'optionalAccess', _119 => _119.name]), () => ( _optionalChain([asset, 'optionalAccess', _120 => _120.src, 'access', _121 => _121.replace, 'call', _122 => _122(/^.*[\\/]/, "")]))), () => ( clip.asset));
4290
4729
  const speed = videoClipSpeed(clip);
4291
4730
  return speed !== 1 ? `${base} (${Math.round(speed * 100)}%)` : base;
4292
4731
  }
@@ -4330,7 +4769,7 @@ function videoAssetDropEdit(project, assetId, options = {}) {
4330
4769
  const asset = videoAssetMap(project).get(assetId);
4331
4770
  if (!asset) return null;
4332
4771
  const kind = asset.kind === "audio" ? "audio" : "video";
4333
- const track = _nullishCoalesce(options.track, () => ( _optionalChain([project, 'access', _119 => _119.tracks, 'access', _120 => _120.find, 'call', _121 => _121((t) => t.kind === kind && !t.locked), 'optionalAccess', _122 => _122.id])));
4772
+ const track = _nullishCoalesce(options.track, () => ( _optionalChain([project, 'access', _123 => _123.tracks, 'access', _124 => _124.find, 'call', _125 => _125((t) => t.kind === kind && !t.locked), 'optionalAccess', _126 => _126.id])));
4334
4773
  if (!track) return null;
4335
4774
  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 } : {} };
4336
4775
  }
@@ -4379,14 +4818,14 @@ var KEY_LABELS = {
4379
4818
  scale: { label: "Scale", step: 0.05 },
4380
4819
  rotation: { label: "Rotation", step: 1 }
4381
4820
  };
4382
- var animatableOf = (clip, property) => VIDEO_TRANSFORM_PROPERTIES.includes(property) ? _optionalChain([clip, 'access', _123 => _123.transform, 'optionalAccess', _124 => _124[property]]) : property === "volume" ? clip.type === "media" ? clip.volume : void 0 : clip.opacity;
4821
+ var animatableOf = (clip, property) => VIDEO_TRANSFORM_PROPERTIES.includes(property) ? _optionalChain([clip, 'access', _127 => _127.transform, 'optionalAccess', _128 => _128[property]]) : property === "volume" ? clip.type === "media" ? clip.volume : void 0 : clip.opacity;
4383
4822
  var playheadInClip = (clip, time) => Math.round(Math.min(clip.duration, Math.max(0, time - clip.start)));
4384
4823
  function videoKeyframeRows(project, clipId, time) {
4385
4824
  const at = findVideoClip(project, clipId);
4386
4825
  if (!at) return [];
4387
4826
  const { clip } = at;
4388
4827
  const asset = clip.type === "media" ? videoAssetMap(project).get(clip.asset) : void 0;
4389
- const visual = !(_optionalChain([asset, 'optionalAccess', _125 => _125.kind]) === "audio" || at.track.kind === "audio");
4828
+ const visual = !(_optionalChain([asset, 'optionalAccess', _129 => _129.kind]) === "audio" || at.track.kind === "audio");
4390
4829
  const audible = clip.type === "media" && videoAssetHasSound(asset);
4391
4830
  const properties = [...visual ? ["opacity"] : [], ...audible ? ["volume"] : [], ...visual ? VIDEO_TRANSFORM_PROPERTIES : []];
4392
4831
  const clipTime = playheadInClip(clip, time);
@@ -4610,5 +5049,23 @@ function videoKeyframeToggleEdit(project, clipId, property, time) {
4610
5049
 
4611
5050
 
4612
5051
 
4613
- 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_PEAK_BLOCK = VIDEO_PEAK_BLOCK; exports.VIDEO_PLACEMENT_MODES = VIDEO_PLACEMENT_MODES; 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_WIPE_DIRECTIONS = VIDEO_WIPE_DIRECTIONS; exports.VideoAudioPeaksCache = VideoAudioPeaksCache; exports.VideoEditorController = VideoEditorController; exports.VideoPreviewEngine = VideoPreviewEngine; exports.allVideoClipIds = allVideoClipIds; exports.applyVideoEdits = applyVideoEdits; exports.applyVideoGrade = applyVideoGrade; exports.buildVideoAudioPeaks = buildVideoAudioPeaks; exports.checkVideoProject = checkVideoProject; exports.checkVideoTimeRemap = checkVideoTimeRemap; exports.createVideoEditorToolProvider = createVideoEditorToolProvider; exports.createVideoProject = createVideoProject; exports.decodeVideoAudioPeaks = decodeVideoAudioPeaks; exports.describeVideoProject = describeVideoProject; exports.drawVideoFrame = drawVideoFrame; exports.ffmpegFilterValue = ffmpegFilterValue; exports.findVideoAudioSilences = findVideoAudioSilences; exports.findVideoClip = findVideoClip; exports.findVideoFillers = findVideoFillers; exports.findVideoGaps = findVideoGaps; 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.normalizeVideoWord = normalizeVideoWord; exports.parseVideoAnimatable = parseVideoAnimatable; exports.parseVideoAsset = parseVideoAsset; exports.parseVideoClip = parseVideoClip; exports.parseVideoClipAnimation = parseVideoClipAnimation; exports.parseVideoGrade = parseVideoGrade; exports.parseVideoKeyframes = parseVideoKeyframes; exports.parseVideoLoudnessMeasurement = parseVideoLoudnessMeasurement; exports.parseVideoMarker = parseVideoMarker; exports.parseVideoProject = parseVideoProject; 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.probeVideoMedia = probeVideoMedia; exports.removeVideoKey = removeVideoKey; exports.retimeVideoAnimatable = retimeVideoAnimatable; exports.setVideoKey = setVideoKey; exports.snapToVideoFrame = snapToVideoFrame; exports.snapVideoTime = snapVideoTime; exports.sortVideoKeys = sortVideoKeys; exports.sortedVideoClips = sortedVideoClips; exports.splitVideoAnimatable = splitVideoAnimatable; exports.toVideoEdl = toVideoEdl; exports.toVideoFfmpeg = toVideoFfmpeg; exports.toVideoLoudnessMeasure = toVideoLoudnessMeasure; exports.videoAssetDropEdit = videoAssetDropEdit; exports.videoAssetHasSound = videoAssetHasSound; exports.videoAssetKindOf = videoAssetKindOf; exports.videoAssetMap = videoAssetMap; exports.videoAudioLevels = videoAudioLevels; exports.videoAudioPeakRange = videoAudioPeakRange; exports.videoAudioPeakSpans = videoAudioPeakSpans; exports.videoAudioPeaksDuration = videoAudioPeaksDuration; 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.videoFadeCurveGain = videoFadeCurveGain; exports.videoFrameAt = videoFrameAt; exports.videoFrameDuration = videoFrameDuration; exports.videoFrameToSvg = videoFrameToSvg; exports.videoFrames = videoFrames; exports.videoFreezeFrameEdit = videoFreezeFrameEdit; exports.videoGradeCssFilter = videoGradeCssFilter; exports.videoGradeFfmpegFilters = videoGradeFfmpegFilters; exports.videoGradeGainsFilterId = videoGradeGainsFilterId; exports.videoGradePreset = videoGradePreset; exports.videoGradeStepMatrix = videoGradeStepMatrix; exports.videoGradeStepSvgValues = videoGradeStepSvgValues; exports.videoGradeSteps = videoGradeSteps; exports.videoGradeSvgDefs = videoGradeSvgDefs; exports.videoGradeSvgFilter = videoGradeSvgFilter; 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.videoOverlap = videoOverlap; exports.videoPeakDb = videoPeakDb; exports.videoProjectDuration = videoProjectDuration; exports.videoProjectSvg = videoProjectSvg; exports.videoRemoveRangesEdits = videoRemoveRangesEdits; exports.videoRulerTicks = videoRulerTicks; exports.videoSlowMotionRamp = videoSlowMotionRamp; exports.videoSnapPoints = videoSnapPoints; 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.videoWaveformPath = videoWaveformPath; exports.videoWipeRect = videoWipeRect; exports.videoWordAt = videoWordAt; exports.videoWordsRange = videoWordsRange;
5052
+
5053
+
5054
+
5055
+
5056
+
5057
+
5058
+
5059
+
5060
+
5061
+
5062
+
5063
+
5064
+
5065
+
5066
+
5067
+
5068
+
5069
+
5070
+ 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_PEAK_BLOCK = VIDEO_PEAK_BLOCK; exports.VIDEO_PLACEMENT_MODES = VIDEO_PLACEMENT_MODES; 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.VideoPreviewEngine = VideoPreviewEngine; exports.VideoPreviewPixels = VideoPreviewPixels; exports.allVideoClipIds = allVideoClipIds; exports.applyVideoEdits = applyVideoEdits; exports.applyVideoGrade = applyVideoGrade; exports.buildVideoAudioPeaks = buildVideoAudioPeaks; exports.checkVideoProject = checkVideoProject; exports.checkVideoTimeRemap = checkVideoTimeRemap; exports.createVideoEditorToolProvider = createVideoEditorToolProvider; exports.createVideoProject = createVideoProject; exports.decodeVideoAudioPeaks = decodeVideoAudioPeaks; exports.describeVideoProject = describeVideoProject; exports.drawVideoFrame = drawVideoFrame; exports.ffmpegFilterValue = ffmpegFilterValue; exports.findVideoAudioSilences = findVideoAudioSilences; exports.findVideoClip = findVideoClip; exports.findVideoFillers = findVideoFillers; exports.findVideoGaps = findVideoGaps; 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.normalizeVideoWord = normalizeVideoWord; exports.parseVideoAnimatable = parseVideoAnimatable; exports.parseVideoAsset = parseVideoAsset; exports.parseVideoClip = parseVideoClip; exports.parseVideoClipAnimation = parseVideoClipAnimation; exports.parseVideoGrade = parseVideoGrade; exports.parseVideoKeyframes = parseVideoKeyframes; exports.parseVideoLoudnessMeasurement = parseVideoLoudnessMeasurement; exports.parseVideoMarker = parseVideoMarker; exports.parseVideoProject = parseVideoProject; 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.probeVideoMedia = probeVideoMedia; exports.readVideoScopesPanel = readVideoScopesPanel; exports.removeVideoKey = removeVideoKey; 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.videoAudioLevels = videoAudioLevels; exports.videoAudioPeakRange = videoAudioPeakRange; exports.videoAudioPeakSpans = videoAudioPeakSpans; exports.videoAudioPeaksDuration = videoAudioPeaksDuration; 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.videoFrameDuration = videoFrameDuration; exports.videoFrameToSvg = videoFrameToSvg; exports.videoFrames = videoFrames; exports.videoFreezeFrameEdit = videoFreezeFrameEdit; exports.videoGradeCssFilter = videoGradeCssFilter; exports.videoGradeFfmpegFilters = videoGradeFfmpegFilters; exports.videoGradeGainsFilterId = videoGradeGainsFilterId; 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.videoOverlap = videoOverlap; exports.videoPeakDb = videoPeakDb; exports.videoProjectDuration = videoProjectDuration; exports.videoProjectSvg = videoProjectSvg; exports.videoRemoveRangesEdits = videoRemoveRangesEdits; exports.videoRulerTicks = videoRulerTicks; exports.videoScopeImage = videoScopeImage; exports.videoSlowMotionRamp = videoSlowMotionRamp; exports.videoSnapPoints = videoSnapPoints; 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;
4614
5071
  //# sourceMappingURL=index.cjs.map