@vosjs/studio-core 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +170 -7
- package/dist/index.js +782 -131
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -98,6 +98,8 @@ function clipLength(clip) {
|
|
|
98
98
|
return clip.loop ? Math.max(span, clip.loopLen ?? span) : span;
|
|
99
99
|
}
|
|
100
100
|
var MOTION_EASE = "power2.inOut";
|
|
101
|
+
var LAYER_CARD_SHADOW = 0.32;
|
|
102
|
+
var MEDIA_KEY_PREFIX = "media:";
|
|
101
103
|
var OVERLAY_SCALE_MAX = 8;
|
|
102
104
|
var OVERLAY_MEDIA_DEFAULT_WIDTH = 0.35;
|
|
103
105
|
var OVERLAY_MEDIA_DEFAULT_RADIUS = 12;
|
|
@@ -454,9 +456,35 @@ var CARD_ENTER_KINDS = [
|
|
|
454
456
|
"fade",
|
|
455
457
|
"rise",
|
|
456
458
|
"tilt-in",
|
|
457
|
-
"pull-out"
|
|
459
|
+
"pull-out",
|
|
460
|
+
"slide"
|
|
458
461
|
];
|
|
459
462
|
var CARD_EXIT_KINDS = ["none", "fade", "recede"];
|
|
463
|
+
var SEGMENT_ENTER_KINDS = [
|
|
464
|
+
"none",
|
|
465
|
+
"slide",
|
|
466
|
+
"fade",
|
|
467
|
+
"scale"
|
|
468
|
+
];
|
|
469
|
+
var SEGMENT_EXIT_KINDS = [
|
|
470
|
+
"none",
|
|
471
|
+
"slide",
|
|
472
|
+
"fade",
|
|
473
|
+
"scale"
|
|
474
|
+
];
|
|
475
|
+
var TRANSITION_SECONDS = 0.6;
|
|
476
|
+
var TRANSITION_SECONDS_MIN = 0.15;
|
|
477
|
+
var TRANSITION_SECONDS_MAX = 2;
|
|
478
|
+
function transitionSeconds(step) {
|
|
479
|
+
if (!step || step.kind === "none") return 0;
|
|
480
|
+
return Math.max(
|
|
481
|
+
TRANSITION_SECONDS_MIN,
|
|
482
|
+
Math.min(TRANSITION_SECONDS_MAX, step.seconds ?? TRANSITION_SECONDS)
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
function slideSide(step, which) {
|
|
486
|
+
return step?.side ?? (which === "enter" ? "right" : "left");
|
|
487
|
+
}
|
|
460
488
|
var TEXT_ENTER_KINDS = [
|
|
461
489
|
"none",
|
|
462
490
|
"fade",
|
|
@@ -582,6 +610,15 @@ function cardFields(frame) {
|
|
|
582
610
|
}
|
|
583
611
|
return out;
|
|
584
612
|
}
|
|
613
|
+
var mediaRef = (id) => MEDIA_KEY_PREFIX + id;
|
|
614
|
+
function mediaRefId(key) {
|
|
615
|
+
if (typeof key !== "string" || !key.startsWith(MEDIA_KEY_PREFIX)) return null;
|
|
616
|
+
return key.slice(MEDIA_KEY_PREFIX.length);
|
|
617
|
+
}
|
|
618
|
+
function layerMedia(doc, key) {
|
|
619
|
+
const id = mediaRefId(key);
|
|
620
|
+
return id === null ? null : mediaSource(doc, id);
|
|
621
|
+
}
|
|
585
622
|
function nextMediaId(doc) {
|
|
586
623
|
const taken = new Set((doc.media ?? []).map((m) => m.id));
|
|
587
624
|
let n = 1;
|
|
@@ -786,7 +823,7 @@ function cardPoseTrack(enter, exit, footageEnd, outputEnd2 = footageEnd) {
|
|
|
786
823
|
const keyframes = [];
|
|
787
824
|
const s = entranceSeconds(enter);
|
|
788
825
|
if (s) {
|
|
789
|
-
const from = enter?.kind === "rise" ? [0.96, 0.08, 0.7] : enter?.kind === "pull-out" || enter?.kind === "fade" ? enter.kind === "fade" ? [1, 0, 0] : [1, 0, 1] : [0.94, 0.05, 0.7];
|
|
826
|
+
const from = enter?.kind === "rise" ? [0.96, 0.08, 0.7] : enter?.kind === "pull-out" || enter?.kind === "fade" || enter?.kind === "slide" ? enter.kind === "fade" ? [1, 0, 0] : [1, 0, 1] : [0.94, 0.05, 0.7];
|
|
790
827
|
keyframes.push({ t: 0, value: from, ease: "none" });
|
|
791
828
|
keyframes.push({ t: s, value: [1, 0, 1], ease: "power3.out" });
|
|
792
829
|
}
|
|
@@ -1687,8 +1724,8 @@ import {
|
|
|
1687
1724
|
segmentRate as segmentRate2,
|
|
1688
1725
|
sortKeyframes as sortKeyframes2,
|
|
1689
1726
|
sourceToTimeline,
|
|
1690
|
-
splitBySpeed as
|
|
1691
|
-
totalDuration as
|
|
1727
|
+
splitBySpeed as splitBySpeed3,
|
|
1728
|
+
totalDuration as totalDuration3
|
|
1692
1729
|
} from "@vosjs/timeline";
|
|
1693
1730
|
import { timelineRuntimeCode as timelineRuntimeCode2 } from "@vosjs/timeline/bundle";
|
|
1694
1731
|
|
|
@@ -2177,6 +2214,142 @@ function resolveText3dAsset(asset) {
|
|
|
2177
2214
|
};
|
|
2178
2215
|
}
|
|
2179
2216
|
|
|
2217
|
+
// src/lower/segmentStarts.ts
|
|
2218
|
+
import { splitBySpeed as splitBySpeed2, totalDuration as totalDuration2 } from "@vosjs/timeline";
|
|
2219
|
+
function effectiveSegments(doc) {
|
|
2220
|
+
if (isRecordingDoc(doc) && doc.segments.length) return doc.segments;
|
|
2221
|
+
return [{ in: 0, out: anchorSourceDuration(doc) }];
|
|
2222
|
+
}
|
|
2223
|
+
var contiguous = (segments, i) => i > 0 && sameMedia(segments[i - 1].media, segments[i].media) && Math.abs(segments[i - 1].out - segments[i].in) < 1e-9;
|
|
2224
|
+
var segmentPieces = (seg, speeds, freezes, leading) => {
|
|
2225
|
+
const own = freezes.filter(
|
|
2226
|
+
(f) => sameMedia(f.media, seg.media) && f.at <= seg.out + 1e-9 && (leading ? f.at >= seg.in - 1e-9 : f.at > seg.in + 1e-9)
|
|
2227
|
+
);
|
|
2228
|
+
return withFreezes(
|
|
2229
|
+
splitBySpeed2(
|
|
2230
|
+
[seg],
|
|
2231
|
+
speeds.filter((sp) => sameMedia(sp.media, seg.media))
|
|
2232
|
+
),
|
|
2233
|
+
own
|
|
2234
|
+
);
|
|
2235
|
+
};
|
|
2236
|
+
var outputLen = (seg, speeds, freezes = [], leading = true) => totalDuration2(segmentPieces(seg, speeds, freezes, leading));
|
|
2237
|
+
var segmentStarts = (segments, speeds, freezes = []) => {
|
|
2238
|
+
const starts = [];
|
|
2239
|
+
let acc = 0;
|
|
2240
|
+
segments.forEach((s, i) => {
|
|
2241
|
+
starts.push(acc);
|
|
2242
|
+
acc += outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i));
|
|
2243
|
+
});
|
|
2244
|
+
return starts;
|
|
2245
|
+
};
|
|
2246
|
+
function segmentOutputExtents(doc) {
|
|
2247
|
+
const segments = effectiveSegments(doc);
|
|
2248
|
+
const speeds = doc.speed ?? [];
|
|
2249
|
+
const freezes = isRecordingDoc(doc) ? docFreezes(doc) : [];
|
|
2250
|
+
const starts = segmentStarts(segments, speeds, freezes);
|
|
2251
|
+
return segments.map((s, i) => ({
|
|
2252
|
+
start: starts[i],
|
|
2253
|
+
end: starts[i] + outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i))
|
|
2254
|
+
}));
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
// src/lower/transitions.ts
|
|
2258
|
+
var TRANSITION_TRAVEL = 1.04;
|
|
2259
|
+
var TRANSITION_SCALE_STEP = 0.08;
|
|
2260
|
+
var GHOST_Z_LIFT = 4e-3;
|
|
2261
|
+
var GHOST_RENDER_ORDER = 1.1;
|
|
2262
|
+
var LAST_FRAME_BACK = 2e-3;
|
|
2263
|
+
var round32 = (v) => Math.round(v * 1e3) / 1e3;
|
|
2264
|
+
function move(step, which, cap) {
|
|
2265
|
+
const secs = transitionSeconds(step);
|
|
2266
|
+
if (!secs || !step) return null;
|
|
2267
|
+
const kind = step.kind;
|
|
2268
|
+
if (kind !== "slide" && kind !== "fade" && kind !== "scale") return null;
|
|
2269
|
+
const d = Math.min(secs, cap);
|
|
2270
|
+
if (d < TRANSITION_SECONDS_MIN) return null;
|
|
2271
|
+
return { kind, side: slideSide(step, which), d: round32(d) };
|
|
2272
|
+
}
|
|
2273
|
+
function docTransitions(doc) {
|
|
2274
|
+
const out = [];
|
|
2275
|
+
const segments = doc.segments;
|
|
2276
|
+
const extents = segmentOutputExtents(doc);
|
|
2277
|
+
const open = enterOf(doc.frame.anim);
|
|
2278
|
+
if (open?.kind === "slide" && extents.length) {
|
|
2279
|
+
const first = extents[0];
|
|
2280
|
+
const m = move(open, "enter", Math.max(0, first.end - first.start) / 2);
|
|
2281
|
+
if (m) out.push({ t: 0, d: m.d, in: m });
|
|
2282
|
+
}
|
|
2283
|
+
for (let i = 0; i + 1 < segments.length; i++) {
|
|
2284
|
+
const a = segments[i];
|
|
2285
|
+
const b = segments[i + 1];
|
|
2286
|
+
const la = extents[i].end - extents[i].start;
|
|
2287
|
+
const lb = extents[i + 1].end - extents[i + 1].start;
|
|
2288
|
+
const cap = Math.min(la, lb) / 2;
|
|
2289
|
+
const exit = move(exitOf(a.anim), "exit", cap);
|
|
2290
|
+
const enter = move(enterOf(b.anim), "enter", cap);
|
|
2291
|
+
if (!exit && !enter) continue;
|
|
2292
|
+
const t = round32(extents[i + 1].start);
|
|
2293
|
+
const rec = {
|
|
2294
|
+
t,
|
|
2295
|
+
d: Math.max(exit?.d ?? 0, enter?.d ?? 0)
|
|
2296
|
+
};
|
|
2297
|
+
if (enter) rec.in = enter;
|
|
2298
|
+
if (exit)
|
|
2299
|
+
rec.out = {
|
|
2300
|
+
...exit,
|
|
2301
|
+
media: a.media ?? "",
|
|
2302
|
+
at: round32(Math.max(a.in, a.out - LAST_FRAME_BACK))
|
|
2303
|
+
};
|
|
2304
|
+
out.push(rec);
|
|
2305
|
+
}
|
|
2306
|
+
return out;
|
|
2307
|
+
}
|
|
2308
|
+
var REST_LEAD = 0.1;
|
|
2309
|
+
function restSpansThroughTransitions(spans, rated, transitions, minLen) {
|
|
2310
|
+
const windows = transitions.filter((tr) => tr.t > 0 && tr.d > 0);
|
|
2311
|
+
if (!windows.length) return [...spans];
|
|
2312
|
+
const out = [];
|
|
2313
|
+
for (const span of spans) {
|
|
2314
|
+
const start = outputStartOf(rated, span.in, span.out, span.media);
|
|
2315
|
+
const win = start === null ? void 0 : windows.find((tr) => start >= tr.t - REST_LEAD && start < tr.t + tr.d);
|
|
2316
|
+
if (start === null || !win) {
|
|
2317
|
+
out.push(span);
|
|
2318
|
+
continue;
|
|
2319
|
+
}
|
|
2320
|
+
const after = win.t + win.d;
|
|
2321
|
+
if (mediaAtOutput(rated, after) !== (span.media ?? "")) continue;
|
|
2322
|
+
const head = sourceAt(rated, after);
|
|
2323
|
+
if (head === null || head > span.out - minLen) continue;
|
|
2324
|
+
out.push({ ...span, in: round32(head) });
|
|
2325
|
+
}
|
|
2326
|
+
return out;
|
|
2327
|
+
}
|
|
2328
|
+
function outputStartOf(rated, sIn, sOut, media) {
|
|
2329
|
+
let acc = 0;
|
|
2330
|
+
for (const p of rated) {
|
|
2331
|
+
const rate = p.rate ?? 1;
|
|
2332
|
+
const len = Math.max(0, p.out - p.in) / rate;
|
|
2333
|
+
if (sameMedia(p.media, media)) {
|
|
2334
|
+
const ovIn = Math.max(sIn, p.in);
|
|
2335
|
+
const ovOut = Math.min(sOut, p.out);
|
|
2336
|
+
if (ovOut > ovIn) return acc + (ovIn - p.in) / rate;
|
|
2337
|
+
}
|
|
2338
|
+
acc += len;
|
|
2339
|
+
}
|
|
2340
|
+
return null;
|
|
2341
|
+
}
|
|
2342
|
+
function sourceAt(rated, t) {
|
|
2343
|
+
let acc = 0;
|
|
2344
|
+
for (const p of rated) {
|
|
2345
|
+
const rate = p.rate ?? 1;
|
|
2346
|
+
const len = Math.max(0, p.out - p.in) / rate;
|
|
2347
|
+
if (t < acc + len) return p.in + (t - acc) * rate;
|
|
2348
|
+
acc += len;
|
|
2349
|
+
}
|
|
2350
|
+
return null;
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2180
2353
|
// src/lower/audioEnvelope.ts
|
|
2181
2354
|
function clipEnvelope(clip) {
|
|
2182
2355
|
const span = clipLength(clip);
|
|
@@ -2698,6 +2871,8 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
2698
2871
|
// over its whole life, so it must repaint every visible frame \u2014 the
|
|
2699
2872
|
// signature alone would freeze it between edits (the olVisSig trap).
|
|
2700
2873
|
if (ol0.track) olAnim = true
|
|
2874
|
+
// A layer CARD is painted on its own plane every frame it is visible.
|
|
2875
|
+
if (ol0.card) olAnim = true
|
|
2701
2876
|
// Video overlays advance every frame; images redraw until decoded.
|
|
2702
2877
|
if (ol0.kind === 'video') olAnim = true
|
|
2703
2878
|
else if (ol0.kind === 'image') {
|
|
@@ -2707,6 +2882,76 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
2707
2882
|
}
|
|
2708
2883
|
var ovSig = W + 'x' + H + '|' + olVisSig
|
|
2709
2884
|
var ovDirty = ov.sig !== ovSig || ov.active || olAnim
|
|
2885
|
+
// The layer cards visible this frame, with their pose, for the card
|
|
2886
|
+
// planes below (the overlay loop collects them instead of painting).
|
|
2887
|
+
var lcList = []
|
|
2888
|
+
// Acquire a media overlay's element through the shared cache and sync it
|
|
2889
|
+
// to CLIP-LOCAL time (pure f(t); muted always \u2014 soundtracks belong to
|
|
2890
|
+
// doc.audio). Null until it has a frame to draw. One seam for the flat
|
|
2891
|
+
// picture and the layer card.
|
|
2892
|
+
function olAcquire(ol, olT) {
|
|
2893
|
+
var olEl = ns.videoCache ? ns.videoCache.get(ol.key) : null
|
|
2894
|
+
if (!olEl && ns.videoCache) {
|
|
2895
|
+
if (ol.kind === 'image') {
|
|
2896
|
+
olEl = new Image()
|
|
2897
|
+
olEl.crossOrigin = 'anonymous'
|
|
2898
|
+
olEl.src = ol.key
|
|
2899
|
+
} else {
|
|
2900
|
+
olEl = document.createElement('video')
|
|
2901
|
+
olEl.crossOrigin = 'anonymous'
|
|
2902
|
+
olEl.muted = true
|
|
2903
|
+
olEl.playsInline = true
|
|
2904
|
+
olEl.preload = 'auto'
|
|
2905
|
+
olEl.src = ol.key
|
|
2906
|
+
olEl.load()
|
|
2907
|
+
}
|
|
2908
|
+
ns.videoCache.set(ol.key, olEl)
|
|
2909
|
+
}
|
|
2910
|
+
if (!olEl) return null
|
|
2911
|
+
var olIsImg = ol.kind === 'image'
|
|
2912
|
+
if (!olIsImg && olEl.play) {
|
|
2913
|
+
var olDur = olEl.duration || 0
|
|
2914
|
+
var olMT = olT
|
|
2915
|
+
if (ol.loop && olDur > 0) olMT = olT % olDur
|
|
2916
|
+
else if (olDur > 0) olMT = Math.min(olT, olDur - 0.001)
|
|
2917
|
+
try {
|
|
2918
|
+
if (playing) {
|
|
2919
|
+
if (olEl.playbackRate !== 1) olEl.playbackRate = 1
|
|
2920
|
+
var olDrift = Math.abs(olEl.currentTime - olMT)
|
|
2921
|
+
if (olDur > 0 && !olEl.seeking && olDrift > 0.3 && (!ol.loop || olDur - olDrift > 0.3)) olEl.currentTime = olMT
|
|
2922
|
+
if (!ol.loop && olDur > 0 && olT >= olDur) { if (!olEl.paused) olEl.pause() }
|
|
2923
|
+
else if (olEl.paused) { var olP = olEl.play(); if (olP && olP.catch) olP.catch(function () {}) }
|
|
2924
|
+
} else {
|
|
2925
|
+
if (!olEl.paused) olEl.pause()
|
|
2926
|
+
var olTarget = Math.min(olMT, olEl.duration || olMT)
|
|
2927
|
+
// Coalesce scrub seeks (the backgroundMedia pattern): re-assigning
|
|
2928
|
+
// currentTime aborts the in-flight seek, so a per-frame scrub keeps
|
|
2929
|
+
// a remote source seeking forever. Defer until 'seeked' lands.
|
|
2930
|
+
if (olEl.readyState >= 1 && !olEl.seeking && Math.abs(olEl.currentTime - olTarget) > 0.02) {
|
|
2931
|
+
if (ns.pendingDecodes) {
|
|
2932
|
+
var olDp = new Promise(function (resolve) {
|
|
2933
|
+
var olDone = function () { olEl.removeEventListener('seeked', olDone); resolve() }
|
|
2934
|
+
olEl.addEventListener('seeked', olDone)
|
|
2935
|
+
setTimeout(olDone, 250)
|
|
2936
|
+
})
|
|
2937
|
+
ns.pendingDecodes.add(olDp)
|
|
2938
|
+
olDp.finally(function () { ns.pendingDecodes.delete(olDp) })
|
|
2939
|
+
}
|
|
2940
|
+
olEl.currentTime = olTarget
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
} catch (e) {}
|
|
2944
|
+
}
|
|
2945
|
+
// Video readiness is STICKY through seeks (the cam-bubble pattern):
|
|
2946
|
+
// readyState dips below HAVE_CURRENT_DATA while a scrub seek is in
|
|
2947
|
+
// flight, and this layer repaints every frame a video clip is visible \u2014
|
|
2948
|
+
// gating each frame on it would blink the clip out for the whole drag.
|
|
2949
|
+
// After the first decoded frame, keep drawing: Chrome paints the
|
|
2950
|
+
// element's retained frame mid-seek.
|
|
2951
|
+
if (!olIsImg && olEl.readyState >= 2) olEl.__vosHasFrame = true
|
|
2952
|
+
var olReady = olIsImg ? !!(olEl.complete && olEl.naturalWidth) : !!(olEl.readyState >= 2 || olEl.__vosHasFrame)
|
|
2953
|
+
return olReady ? olEl : null
|
|
2954
|
+
}
|
|
2710
2955
|
if (ovDirty) {
|
|
2711
2956
|
ovC.clearRect(0, 0, W, H)
|
|
2712
2957
|
// --- text overlays (compositor v2): OUTPUT-anchored clips, styles resolved
|
|
@@ -2764,71 +3009,19 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
2764
3009
|
if (olPose) olA *= Math.max(0, Math.min(1, olPose[4]))
|
|
2765
3010
|
if (olA <= 0.004) continue
|
|
2766
3011
|
if (ol.kind === 'image' || ol.kind === 'video') {
|
|
3012
|
+
// A layer CARD is painted by the card painter on its own plane, below:
|
|
3013
|
+
// collect it with its pose and skip the flat picture.
|
|
3014
|
+
if (ol.card) {
|
|
3015
|
+
lcList.push({ ol: ol, t: olT, a: olA * (ol.opacity == null ? 1 : ol.opacity), x: olMX, y: olMY, s: olMS, r: olMR, yof: olYof })
|
|
3016
|
+
continue
|
|
3017
|
+
}
|
|
2767
3018
|
// Media overlay: lazy-acquire through the shared cache (SET_DATA-added
|
|
2768
3019
|
// clips load without a LOAD \u2014 the backgroundMedia pattern), sync video
|
|
2769
3020
|
// to CLIP-LOCAL time (pure f(t)), draw a rounded media card centered on
|
|
2770
3021
|
// the fraction anchor. Muted always \u2014 soundtracks belong to doc.audio.
|
|
2771
|
-
var olEl =
|
|
2772
|
-
if (!olEl && ns.videoCache) {
|
|
2773
|
-
if (ol.kind === 'image') {
|
|
2774
|
-
olEl = new Image()
|
|
2775
|
-
olEl.crossOrigin = 'anonymous'
|
|
2776
|
-
olEl.src = ol.key
|
|
2777
|
-
} else {
|
|
2778
|
-
olEl = document.createElement('video')
|
|
2779
|
-
olEl.crossOrigin = 'anonymous'
|
|
2780
|
-
olEl.muted = true
|
|
2781
|
-
olEl.playsInline = true
|
|
2782
|
-
olEl.preload = 'auto'
|
|
2783
|
-
olEl.src = ol.key
|
|
2784
|
-
olEl.load()
|
|
2785
|
-
}
|
|
2786
|
-
ns.videoCache.set(ol.key, olEl)
|
|
2787
|
-
}
|
|
3022
|
+
var olEl = olAcquire(ol, olT)
|
|
2788
3023
|
if (!olEl) continue
|
|
2789
3024
|
var olIsImg = ol.kind === 'image'
|
|
2790
|
-
if (!olIsImg && olEl.play) {
|
|
2791
|
-
var olDur = olEl.duration || 0
|
|
2792
|
-
var olMT = olT
|
|
2793
|
-
if (ol.loop && olDur > 0) olMT = olT % olDur
|
|
2794
|
-
else if (olDur > 0) olMT = Math.min(olT, olDur - 0.001)
|
|
2795
|
-
try {
|
|
2796
|
-
if (playing) {
|
|
2797
|
-
if (olEl.playbackRate !== 1) olEl.playbackRate = 1
|
|
2798
|
-
var olDrift = Math.abs(olEl.currentTime - olMT)
|
|
2799
|
-
if (olDur > 0 && !olEl.seeking && olDrift > 0.3 && (!ol.loop || olDur - olDrift > 0.3)) olEl.currentTime = olMT
|
|
2800
|
-
if (!ol.loop && olDur > 0 && olT >= olDur) { if (!olEl.paused) olEl.pause() }
|
|
2801
|
-
else if (olEl.paused) { var olP = olEl.play(); if (olP && olP.catch) olP.catch(function () {}) }
|
|
2802
|
-
} else {
|
|
2803
|
-
if (!olEl.paused) olEl.pause()
|
|
2804
|
-
var olTarget = Math.min(olMT, olEl.duration || olMT)
|
|
2805
|
-
// Coalesce scrub seeks (the backgroundMedia pattern): re-assigning
|
|
2806
|
-
// currentTime aborts the in-flight seek, so a per-frame scrub keeps
|
|
2807
|
-
// a remote source seeking forever. Defer until 'seeked' lands.
|
|
2808
|
-
if (olEl.readyState >= 1 && !olEl.seeking && Math.abs(olEl.currentTime - olTarget) > 0.02) {
|
|
2809
|
-
if (ns.pendingDecodes) {
|
|
2810
|
-
var olDp = new Promise(function (resolve) {
|
|
2811
|
-
var olDone = function () { olEl.removeEventListener('seeked', olDone); resolve() }
|
|
2812
|
-
olEl.addEventListener('seeked', olDone)
|
|
2813
|
-
setTimeout(olDone, 250)
|
|
2814
|
-
})
|
|
2815
|
-
ns.pendingDecodes.add(olDp)
|
|
2816
|
-
olDp.finally(function () { ns.pendingDecodes.delete(olDp) })
|
|
2817
|
-
}
|
|
2818
|
-
olEl.currentTime = olTarget
|
|
2819
|
-
}
|
|
2820
|
-
}
|
|
2821
|
-
} catch (e) {}
|
|
2822
|
-
}
|
|
2823
|
-
// Video readiness is STICKY through seeks (the cam-bubble pattern):
|
|
2824
|
-
// readyState dips below HAVE_CURRENT_DATA while a scrub seek is in
|
|
2825
|
-
// flight, and this layer repaints every frame a video clip is visible \u2014
|
|
2826
|
-
// gating each frame on it would blink the clip out for the whole drag.
|
|
2827
|
-
// After the first decoded frame, keep drawing: Chrome paints the
|
|
2828
|
-
// element's retained frame mid-seek.
|
|
2829
|
-
if (!olIsImg && olEl.readyState >= 2) olEl.__vosHasFrame = true
|
|
2830
|
-
var olReady = olIsImg ? !!(olEl.complete && olEl.naturalWidth) : !!(olEl.readyState >= 2 || olEl.__vosHasFrame)
|
|
2831
|
-
if (!olReady) continue
|
|
2832
3025
|
var olNW = (olIsImg ? olEl.naturalWidth : olEl.videoWidth) || 16
|
|
2833
3026
|
var olNH = (olIsImg ? olEl.naturalHeight : olEl.videoHeight) || 9
|
|
2834
3027
|
var olDW = ol.w * W * olMS
|
|
@@ -3340,6 +3533,194 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
|
|
|
3340
3533
|
}
|
|
3341
3534
|
obM.scale.setScalar(obPS * obRefH * (obE.norm || 1))
|
|
3342
3535
|
}
|
|
3536
|
+
// --- layer cards (many cards): a media overlay that names a frame is
|
|
3537
|
+
// drawn by the card painter on its OWN plane in the prop group
|
|
3538
|
+
// (renderOrder 1.25: above the primary card, below the props): the
|
|
3539
|
+
// layered shadow (the primary's table and strengths), the media clipped
|
|
3540
|
+
// to its corners, the browser bar after it, the border, and for a
|
|
3541
|
+
// document media its click rings and cursor dot inside the card, at
|
|
3542
|
+
// clip-local time. Chrome scales WITH the card (a smaller window has
|
|
3543
|
+
// smaller chrome), the way the primary's scales with cf. The plane is
|
|
3544
|
+
// sized to the canvas at the card depth, placed at the clip's anchor,
|
|
3545
|
+
// leaned by the card's own rx/ry (the tilt convention) and turned by the
|
|
3546
|
+
// clip's rotation. Locals lc*.
|
|
3547
|
+
var lcC = obC.cards || (obC.cards = new Map())
|
|
3548
|
+
var lcSeen = {}
|
|
3549
|
+
var lcK = obRefH / H
|
|
3550
|
+
for (var lci = 0; lci < lcList.length; lci++) {
|
|
3551
|
+
var lcI = lcList[lci], lcO = lcI.ol, lcCard = lcO.card
|
|
3552
|
+
var lcEl = olAcquire(lcO, lcI.t)
|
|
3553
|
+
if (!lcEl) continue
|
|
3554
|
+
lcSeen[lcO.id] = true
|
|
3555
|
+
var lcE = lcC.get(lcO.id)
|
|
3556
|
+
if (!lcE) {
|
|
3557
|
+
var lcCv = document.createElement('canvas')
|
|
3558
|
+
var lcTx = new THREE3.CanvasTexture(lcCv)
|
|
3559
|
+
if (THREE3.SRGBColorSpace) lcTx.colorSpace = THREE3.SRGBColorSpace
|
|
3560
|
+
if (THREE3.LinearFilter) { lcTx.minFilter = THREE3.LinearFilter; lcTx.magFilter = THREE3.LinearFilter }
|
|
3561
|
+
lcTx.generateMipmaps = false
|
|
3562
|
+
var lcM = new THREE3.Mesh(new THREE3.PlaneGeometry(1, 1), new THREE3.MeshBasicMaterial({ map: lcTx, transparent: true, depthTest: false, depthWrite: false }))
|
|
3563
|
+
lcM.renderOrder = 1.25
|
|
3564
|
+
lcM.frustumCulled = false
|
|
3565
|
+
obC.group.add(lcM)
|
|
3566
|
+
lcE = { mesh: lcM, canvas: lcCv, c2d: lcCv.getContext('2d'), texture: lcTx }
|
|
3567
|
+
lcC.set(lcO.id, lcE)
|
|
3568
|
+
}
|
|
3569
|
+
var lcIsImg = lcO.kind === 'image'
|
|
3570
|
+
var lcCrop = lcO.crop || null
|
|
3571
|
+
var lcNW = lcCrop ? lcCrop.w : ((lcIsImg ? lcEl.naturalWidth : lcEl.videoWidth) || 16)
|
|
3572
|
+
var lcNH = lcCrop ? lcCrop.h : ((lcIsImg ? lcEl.naturalHeight : lcEl.videoHeight) || 9)
|
|
3573
|
+
var lcW = Math.max(2, lcO.w * W * lcI.s)
|
|
3574
|
+
var lcS = s * (lcW / W)
|
|
3575
|
+
var lcBar = lcCard.bar || {}
|
|
3576
|
+
var lcBarH = lcBar.kind && lcBar.kind !== 'none' ? (lcBar.height || 44) * lcS : 0
|
|
3577
|
+
var lcMH = lcW * (lcNH / lcNW)
|
|
3578
|
+
var lcH = lcMH + lcBarH
|
|
3579
|
+
var lcRad = Math.min((lcO.radius || 0) * lcS, lcH / 2)
|
|
3580
|
+
var lcMg = Math.ceil(170 * lcS)
|
|
3581
|
+
var lcCW = Math.ceil(lcW + 2 * lcMg), lcCH = Math.ceil(lcH + 2 * lcMg)
|
|
3582
|
+
var lcCv2 = lcE.canvas, lcX = lcE.c2d
|
|
3583
|
+
if (lcCv2.width !== lcCW || lcCv2.height !== lcCH) { lcCv2.width = lcCW; lcCv2.height = lcCH }
|
|
3584
|
+
lcX.clearRect(0, 0, lcCW, lcCH)
|
|
3585
|
+
var lcX0 = lcMg, lcY0 = lcMg
|
|
3586
|
+
var lcSh = lcCard.shadow == null ? ${LAYER_CARD_SHADOW} : lcCard.shadow
|
|
3587
|
+
var lcRgb = '0,0,0'
|
|
3588
|
+
var lcHex = lcCard.shadowColor
|
|
3589
|
+
if (typeof lcHex === 'string' && /^#[0-9a-fA-F]{6}$/.test(lcHex)) lcRgb = parseInt(lcHex.slice(1, 3), 16) + ',' + parseInt(lcHex.slice(3, 5), 16) + ',' + parseInt(lcHex.slice(5, 7), 16)
|
|
3590
|
+
// the shadows are cast by a body drawn off the canvas and brought back
|
|
3591
|
+
// by shadowOffsetX (the primary's seam rule)
|
|
3592
|
+
var lcD = lcCW * 2 + lcCH * 2
|
|
3593
|
+
if (lcSh > 0) {
|
|
3594
|
+
var lcL = [[2, 1, 0.1], [12, 4, 0.1], [40, 16, 0.14], [120, 44, 0.3]]
|
|
3595
|
+
for (var lcj = 0; lcj < lcL.length; lcj++) {
|
|
3596
|
+
lcX.save()
|
|
3597
|
+
lcX.shadowColor = 'rgba(' + lcRgb + ',' + +(lcSh * lcL[lcj][2]).toFixed(3) + ')'
|
|
3598
|
+
lcX.shadowBlur = lcL[lcj][0] * lcS; lcX.shadowOffsetX = lcD; lcX.shadowOffsetY = lcL[lcj][1] * lcS
|
|
3599
|
+
lcX.fillStyle = '#000'
|
|
3600
|
+
rr(lcX0 - lcD, lcY0, lcW, lcH, lcRad, lcX); lcX.fill()
|
|
3601
|
+
lcX.restore()
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
if (lcCard.shadowContact > 0) {
|
|
3605
|
+
lcX.save()
|
|
3606
|
+
lcX.shadowColor = 'rgba(' + lcRgb + ',' + lcCard.shadowContact + ')'
|
|
3607
|
+
lcX.shadowBlur = 10 * lcS; lcX.shadowOffsetX = lcD; lcX.shadowOffsetY = 3 * lcS
|
|
3608
|
+
lcX.fillStyle = '#000'
|
|
3609
|
+
rr(lcX0 - lcD, lcY0, lcW, lcH, lcRad, lcX); lcX.fill()
|
|
3610
|
+
lcX.restore()
|
|
3611
|
+
}
|
|
3612
|
+
lcX.save()
|
|
3613
|
+
rr(lcX0, lcY0, lcW, lcH, lcRad, lcX); lcX.clip()
|
|
3614
|
+
var lcMY = lcY0 + lcBarH
|
|
3615
|
+
try {
|
|
3616
|
+
if (lcCrop) lcX.drawImage(lcEl, lcCrop.x, lcCrop.y, lcCrop.w, lcCrop.h, lcX0, lcMY, lcW, lcMH)
|
|
3617
|
+
else lcX.drawImage(lcEl, lcX0, lcMY, lcW, lcMH)
|
|
3618
|
+
} catch (e) {}
|
|
3619
|
+
if (lcBarH > 0) {
|
|
3620
|
+
var lcDark = lcBar.kind.indexOf('dark') >= 0, lcMin = lcBar.kind === 'minimal'
|
|
3621
|
+
var lcThm = (lcMin && lcBar.theme) || null
|
|
3622
|
+
lcX.fillStyle = lcMin ? (lcThm ? lcThm.bar : '#141417') : lcDark ? '#2a2a2e' : '#e9e9eb'
|
|
3623
|
+
lcX.fillRect(lcX0, lcY0, lcW, lcBarH)
|
|
3624
|
+
lcX.fillStyle = lcDark || (lcMin && !(lcThm && lcThm.light)) ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.08)'
|
|
3625
|
+
lcX.fillRect(lcX0, lcY0 + lcBarH - lcS, lcW, lcS)
|
|
3626
|
+
var lcMid = lcY0 + lcBarH / 2
|
|
3627
|
+
if (lcBar.showControls !== false && !lcMin) {
|
|
3628
|
+
if (lcBar.kind.indexOf('mac') === 0) {
|
|
3629
|
+
var lcLights = ['#ff5f57', '#febc2e', '#28c840']
|
|
3630
|
+
for (var lcl = 0; lcl < 3; lcl++) { lcX.fillStyle = lcLights[lcl]; lcX.beginPath(); lcX.arc(lcX0 + (20 + lcl * 20) * lcS, lcMid, 6 * lcS, 0, Math.PI * 2); lcX.fill() }
|
|
3631
|
+
} else {
|
|
3632
|
+
lcX.strokeStyle = lcDark ? 'rgba(255,255,255,0.75)' : 'rgba(0,0,0,0.6)'
|
|
3633
|
+
lcX.lineWidth = 1.5 * lcS
|
|
3634
|
+
var lcG = 4.5 * lcS, lcGx = lcX0 + lcW - 22 * lcS
|
|
3635
|
+
lcX.beginPath(); lcX.moveTo(lcGx - lcG, lcMid - lcG); lcX.lineTo(lcGx + lcG, lcMid + lcG); lcX.moveTo(lcGx + lcG, lcMid - lcG); lcX.lineTo(lcGx - lcG, lcMid + lcG); lcX.stroke()
|
|
3636
|
+
lcX.strokeRect(lcGx - 28 * lcS - lcG, lcMid - lcG, lcG * 2, lcG * 2)
|
|
3637
|
+
lcX.beginPath(); lcX.moveTo(lcGx - 56 * lcS - lcG, lcMid); lcX.lineTo(lcGx - 56 * lcS + lcG, lcMid); lcX.stroke()
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
if (lcBar.showUrl !== false && lcBar.url) {
|
|
3641
|
+
var lcPW = Math.min(lcW * 0.5, Math.max(200 * lcS, lcW * 0.34)), lcPH = lcBarH - 16 * lcS
|
|
3642
|
+
var lcPx = lcX0 + (lcW - lcPW) / 2, lcPy = lcY0 + 8 * lcS
|
|
3643
|
+
lcX.fillStyle = lcMin ? (lcThm ? lcThm.pill : '#26262b') : lcDark ? '#1d1d20' : '#ffffff'
|
|
3644
|
+
rr(lcPx, lcPy, lcPW, lcPH, lcPH / 2, lcX); lcX.fill()
|
|
3645
|
+
lcX.fillStyle = lcMin ? (lcThm ? lcThm.text : '#9a9aa1') : lcDark ? '#a1a1a6' : '#5f5f64'
|
|
3646
|
+
lcX.font = 13 * lcS + 'px -apple-system, system-ui, sans-serif'
|
|
3647
|
+
lcX.textAlign = 'center'; lcX.textBaseline = 'middle'
|
|
3648
|
+
var lcLabel = String(lcBar.url), lcMaxW = lcPW - 28 * lcS
|
|
3649
|
+
if (lcX.measureText(lcLabel).width > lcMaxW) {
|
|
3650
|
+
while (lcLabel.length > 1 && lcX.measureText(lcLabel + '\u2026').width > lcMaxW) lcLabel = lcLabel.slice(0, -1)
|
|
3651
|
+
lcLabel += '\u2026'
|
|
3652
|
+
}
|
|
3653
|
+
lcX.fillText(lcLabel, lcPx + lcPW / 2, lcPy + lcPH / 2)
|
|
3654
|
+
lcX.textAlign = 'start'; lcX.textBaseline = 'alphabetic'
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
// the media's facts: click rings and the cursor dot, video-anchored,
|
|
3658
|
+
// at clip-local time
|
|
3659
|
+
var lcCu = lcO.cur
|
|
3660
|
+
if (lcCu && lcCu.pts && lcCu.pts.length) {
|
|
3661
|
+
var lcSp = lcCu.space || { w: lcNW, h: lcNH }
|
|
3662
|
+
var lcSize = (lcCu.size || 24) * lcS * 0.5
|
|
3663
|
+
var lcT = lcI.t
|
|
3664
|
+
var lcCks = lcCu.clicks || []
|
|
3665
|
+
for (var lcc = 0; lcc < lcCks.length; lcc++) {
|
|
3666
|
+
var lcCk = lcCks[lcc]
|
|
3667
|
+
var lcAge = lcT - lcCk.t
|
|
3668
|
+
if (lcAge < -0.08 || lcAge > 0.6) continue
|
|
3669
|
+
var lcU = Math.max(0, Math.min(1, (lcAge + 0.08) / 0.68))
|
|
3670
|
+
var lcFade = 1 - Math.pow(1 - lcU, 3)
|
|
3671
|
+
lcX.save()
|
|
3672
|
+
lcX.strokeStyle = 'rgba(255,255,255,' + (0.9 * (1 - lcU)) + ')'
|
|
3673
|
+
lcX.lineWidth = Math.max(1, 3 * lcS * (1 - lcU * 0.6))
|
|
3674
|
+
lcX.beginPath(); lcX.arc(lcX0 + (lcCk.x / (lcSp.w || lcNW)) * lcW, lcMY + (lcCk.y / (lcSp.h || lcNH)) * lcMH, lcSize * 0.8 + lcFade * 44 * lcS, 0, Math.PI * 2); lcX.stroke()
|
|
3675
|
+
lcX.restore()
|
|
3676
|
+
}
|
|
3677
|
+
var lcPts = lcCu.pts, lcPX = lcPts[0].x, lcPY = lcPts[0].y
|
|
3678
|
+
for (var lcp = 0; lcp < lcPts.length; lcp++) { if (lcPts[lcp].t <= lcT) { lcPX = lcPts[lcp].x; lcPY = lcPts[lcp].y } }
|
|
3679
|
+
lcX.save()
|
|
3680
|
+
lcX.fillStyle = 'rgba(255,255,255,0.95)'; lcX.strokeStyle = 'rgba(0,0,0,0.4)'; lcX.lineWidth = 2 * lcS
|
|
3681
|
+
lcX.beginPath(); lcX.arc(lcX0 + (lcPX / (lcSp.w || lcNW)) * lcW, lcMY + (lcPY / (lcSp.h || lcNH)) * lcMH, lcSize, 0, Math.PI * 2); lcX.fill(); lcX.stroke()
|
|
3682
|
+
lcX.restore()
|
|
3683
|
+
}
|
|
3684
|
+
lcX.restore()
|
|
3685
|
+
if (lcO.border && lcO.border.width > 0) {
|
|
3686
|
+
var lcBW = lcO.border.width * lcS
|
|
3687
|
+
lcX.save(); lcX.strokeStyle = lcO.border.color || '#fff'; lcX.lineWidth = lcBW
|
|
3688
|
+
rr(lcX0 - lcBW / 2, lcY0 - lcBW / 2, lcW + lcBW, lcH + lcBW, lcRad > 0 ? lcRad + lcBW / 2 : 0, lcX); lcX.stroke()
|
|
3689
|
+
lcX.restore()
|
|
3690
|
+
}
|
|
3691
|
+
lcE.texture.needsUpdate = true
|
|
3692
|
+
var lcM2 = lcE.mesh
|
|
3693
|
+
lcM2.visible = true
|
|
3694
|
+
if (lcM2.material) lcM2.material.opacity = lcI.a
|
|
3695
|
+
var lcLean = lcCard.lean || [0, 0]
|
|
3696
|
+
var lcRx = (lcLean[0] || 0) * Math.PI / 180, lcRy = (lcLean[1] || 0) * Math.PI / 180, lcRz = -(lcI.r || 0) * Math.PI / 180
|
|
3697
|
+
var lcPlW = lcCW * lcK, lcPlH = lcCH * lcK
|
|
3698
|
+
var lcWX = (lcI.x - 0.5) * obRefH * obAspect
|
|
3699
|
+
var lcWY = -(lcI.y - 0.5) * obRefH - lcI.yof * lcK
|
|
3700
|
+
if (obOrtho) {
|
|
3701
|
+
lcM2.position.set((obOCX + (lcI.x - 0.5) * obOW) / obAX, obOCY - (lcI.y - 0.5) * obOH, -obOD)
|
|
3702
|
+
obB.e.set(lcRx, lcRy, lcRz); lcM2.quaternion.setFromEuler(obB.e)
|
|
3703
|
+
} else if (obB) {
|
|
3704
|
+
lcM2.position.copy(obCam.position).addScaledVector(obB.f, Math.abs(${CARD_Z}) - 0.01).addScaledVector(obB.r, lcWX).addScaledVector(obB.u, lcWY)
|
|
3705
|
+
obB.e.set(lcRx, lcRy, lcRz); lcM2.quaternion.copy(obCam.quaternion).multiply(obB.q.setFromEuler(obB.e))
|
|
3706
|
+
} else {
|
|
3707
|
+
lcM2.position.set(lcWX, lcWY, ${CARD_Z} + 0.01)
|
|
3708
|
+
lcM2.rotation.set(lcRx, lcRy, lcRz)
|
|
3709
|
+
}
|
|
3710
|
+
lcM2.scale.set(lcPlW, lcPlH, 1)
|
|
3711
|
+
}
|
|
3712
|
+
// A card off screen rests; one gone from the data is disposed.
|
|
3713
|
+
lcC.forEach(function (lcE2, lcId) {
|
|
3714
|
+
if (lcSeen[lcId]) return
|
|
3715
|
+
var lcStill = false
|
|
3716
|
+
for (var lcq = 0; lcq < ols.length; lcq++) if (ols[lcq].id === lcId && ols[lcq].card) lcStill = true
|
|
3717
|
+
if (lcStill) { lcE2.mesh.visible = false; return }
|
|
3718
|
+
obC.group.remove(lcE2.mesh)
|
|
3719
|
+
if (lcE2.mesh.geometry && lcE2.mesh.geometry.dispose) lcE2.mesh.geometry.dispose()
|
|
3720
|
+
if (lcE2.mesh.material && lcE2.mesh.material.dispose) lcE2.mesh.material.dispose()
|
|
3721
|
+
if (lcE2.texture && lcE2.texture.dispose) lcE2.texture.dispose()
|
|
3722
|
+
lcC.delete(lcId)
|
|
3723
|
+
})
|
|
3343
3724
|
// Dispose props no longer in the data (live removal).
|
|
3344
3725
|
obC.pool.forEach(function (obE2, obId) {
|
|
3345
3726
|
if (!obSeen[obId]) {
|
|
@@ -3479,7 +3860,7 @@ function ratedSegments(doc) {
|
|
|
3479
3860
|
const freezes = isRecordingDoc(doc) ? docFreezes(doc) : [];
|
|
3480
3861
|
const speeds = doc.speed ?? [];
|
|
3481
3862
|
const pieces = segs.flatMap(
|
|
3482
|
-
(seg) =>
|
|
3863
|
+
(seg) => splitBySpeed3(
|
|
3483
3864
|
[seg],
|
|
3484
3865
|
speeds.filter(
|
|
3485
3866
|
(sp) => sameMedia(sp.media, seg.media)
|
|
@@ -3489,7 +3870,7 @@ function ratedSegments(doc) {
|
|
|
3489
3870
|
return withFreezes(pieces, freezes);
|
|
3490
3871
|
}
|
|
3491
3872
|
function durationSec(doc, rated) {
|
|
3492
|
-
const trimmed =
|
|
3873
|
+
const trimmed = totalDuration3(rated);
|
|
3493
3874
|
return trimmed > 0 ? trimmed : doc.source.meta.durationMs / 1e3;
|
|
3494
3875
|
}
|
|
3495
3876
|
function spanOutputExtent(segments, sIn, sOut, media) {
|
|
@@ -3817,6 +4198,52 @@ var FRAME_STEP_SRC = `
|
|
|
3817
4198
|
}
|
|
3818
4199
|
bgEl.currentTime = bgTarget
|
|
3819
4200
|
}
|
|
4201
|
+
}
|
|
4202
|
+
// A GHOST element: a second element on the same bytes, held on ONE frame
|
|
4203
|
+
// (a transition's outgoing snapshot), so one recording can be both cards
|
|
4204
|
+
// at a boundary. Cached by source. Its readiness rides pendingDecodes,
|
|
4205
|
+
// so an export's first paint inside a window waits for it, and a frame
|
|
4206
|
+
// asked before it could seek is sought the moment it can.
|
|
4207
|
+
function ghostEl(el) {
|
|
4208
|
+
var gc = ns.ghostCache || (ns.ghostCache = new Map())
|
|
4209
|
+
var src = el.currentSrc || el.src
|
|
4210
|
+
var g = gc.get(src)
|
|
4211
|
+
if (g) return g
|
|
4212
|
+
g = document.createElement('video')
|
|
4213
|
+
g.crossOrigin = 'anonymous'
|
|
4214
|
+
g.muted = true
|
|
4215
|
+
g.playsInline = true
|
|
4216
|
+
g.preload = 'auto'
|
|
4217
|
+
var gp = new Promise(function (resolve) {
|
|
4218
|
+
var fired = false
|
|
4219
|
+
var done = function () {
|
|
4220
|
+
if (fired) return
|
|
4221
|
+
fired = true
|
|
4222
|
+
g.removeEventListener('loadeddata', done)
|
|
4223
|
+
if (g.__voilaWant != null && g.readyState >= 1) {
|
|
4224
|
+
var want = g.__voilaWant
|
|
4225
|
+
g.__voilaWant = null
|
|
4226
|
+
var seeked = function () { g.removeEventListener('seeked', seeked); resolve() }
|
|
4227
|
+
g.addEventListener('seeked', seeked)
|
|
4228
|
+
setTimeout(seeked, 250)
|
|
4229
|
+
g.currentTime = Math.min(want, g.duration || want)
|
|
4230
|
+
} else resolve()
|
|
4231
|
+
}
|
|
4232
|
+
g.addEventListener('loadeddata', done)
|
|
4233
|
+
setTimeout(done, 2000)
|
|
4234
|
+
})
|
|
4235
|
+
if (ns.pendingDecodes) {
|
|
4236
|
+
ns.pendingDecodes.add(gp)
|
|
4237
|
+
gp.finally(function () { ns.pendingDecodes.delete(gp) })
|
|
4238
|
+
}
|
|
4239
|
+
g.src = src
|
|
4240
|
+
g.load()
|
|
4241
|
+
gc.set(src, g)
|
|
4242
|
+
return g
|
|
4243
|
+
}
|
|
4244
|
+
function stepGhost(g, at) {
|
|
4245
|
+
if (g.readyState >= 1) { g.__voilaWant = null; stepPaused(g, at); return }
|
|
4246
|
+
g.__voilaWant = at
|
|
3820
4247
|
}`;
|
|
3821
4248
|
var SETUP = `async (ctx) => {
|
|
3822
4249
|
;${timelineRuntimeCode2}
|
|
@@ -4083,6 +4510,15 @@ var SETUP = `async (ctx) => {
|
|
|
4083
4510
|
if (cam) stepPaused(cam, srcT)
|
|
4084
4511
|
if (mic) stepPaused(mic, srcT)
|
|
4085
4512
|
}
|
|
4513
|
+
// A boundary's outgoing snapshot: the ghost element on the clip's last
|
|
4514
|
+
// frame, stepped like the footage, and warmed a second ahead.
|
|
4515
|
+
const trs = d.transitions || []
|
|
4516
|
+
for (let i = 0; i < trs.length; i++) {
|
|
4517
|
+
const tr = trs[i]
|
|
4518
|
+
if (!tr.out || t < tr.t - 1 || t >= tr.t + tr.d) continue
|
|
4519
|
+
const live = tr.out.media ? mediaEls[tr.out.media] : video
|
|
4520
|
+
if (live && live.play) stepGhost(ghostEl(live), tr.out.at)
|
|
4521
|
+
}
|
|
4086
4522
|
const bg2 = d.frame && d.frame.backgroundMedia
|
|
4087
4523
|
if (bg2 && bg2.key && bg2.kind !== 'image') {
|
|
4088
4524
|
const bgEl2 = cache.get(bg2.key)
|
|
@@ -4360,6 +4796,87 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
4360
4796
|
if (video.play) syncVid(video) // stills (HTMLImageElement) have nothing to sync
|
|
4361
4797
|
if (r.cam && !amId) syncVid(r.cam)
|
|
4362
4798
|
if (r.mic && !amId) syncVid(r.mic)
|
|
4799
|
+
|
|
4800
|
+
// --- transitions at a boundary (data.transitions, output seconds): the
|
|
4801
|
+
// record whose window holds t, else the next one within a second (its
|
|
4802
|
+
// snapshot warms ahead, so playback never shows a blank ghost). Locals
|
|
4803
|
+
// are tr-prefixed (one var scope).
|
|
4804
|
+
var trs = d.transitions || [], trA = null, trW = null
|
|
4805
|
+
for (var trI = 0; trI < trs.length; trI++) {
|
|
4806
|
+
var trR = trs[trI]
|
|
4807
|
+
if (t >= trR.t && t < trR.t + trR.d) { trA = trR; break }
|
|
4808
|
+
if (trR.out && trR.t > t && trR.t <= t + 1) { trW = trR; break }
|
|
4809
|
+
}
|
|
4810
|
+
function trEase(p) { p = p < 0 ? 0 : p > 1 ? 1 : p; var q = 1 - p; return 1 - q * q * q }
|
|
4811
|
+
function trSide(side) { return side === 'left' ? [-1, 0] : side === 'right' ? [1, 0] : side === 'up' ? [0, 1] : [0, -1] }
|
|
4812
|
+
// A record's outgoing media: its live element, and the GHOST held on the
|
|
4813
|
+
// clip's last frame (a second element on the same bytes, so one
|
|
4814
|
+
// recording can be both cards: a page change inside a single take). A
|
|
4815
|
+
// still needs no ghost.
|
|
4816
|
+
function trLiveEl(o) { return o.media ? (r.media ? r.media[o.media] : null) : r.video }
|
|
4817
|
+
function trGhostEl(o) {
|
|
4818
|
+
var el = trLiveEl(o)
|
|
4819
|
+
if (!el) return null
|
|
4820
|
+
if (!el.play) return el
|
|
4821
|
+
var g = ghostEl(el)
|
|
4822
|
+
stepGhost(g, o.at)
|
|
4823
|
+
return g
|
|
4824
|
+
}
|
|
4825
|
+
if (trW) trGhostEl(trW.out)
|
|
4826
|
+
var trIn = trA && trA.in ? trA.in : null, trOut = trA && trA.out ? trA.out : null
|
|
4827
|
+
var trGEl = trOut ? trGhostEl(trOut) : null
|
|
4828
|
+
if (!trGEl) trOut = null
|
|
4829
|
+
// The live card's enter and the ghost's exit as plane-fraction offsets
|
|
4830
|
+
// (x in plane widths, y in plane heights; +y up), a scale and an opacity.
|
|
4831
|
+
var trOx = 0, trOy = 0, trSc = 1, trOp = 1
|
|
4832
|
+
if (trIn) {
|
|
4833
|
+
var trP = 1 - trEase((t - trA.t) / trIn.d)
|
|
4834
|
+
if (trIn.kind === 'slide') { var trV = trSide(trIn.side); trOx = trV[0] * trP * ${TRANSITION_TRAVEL}; trOy = trV[1] * trP * ${TRANSITION_TRAVEL} }
|
|
4835
|
+
else if (trIn.kind === 'fade') trOp = 1 - trP
|
|
4836
|
+
else { trSc = 1 - ${TRANSITION_SCALE_STEP} * trP; trOp = 1 - trP }
|
|
4837
|
+
}
|
|
4838
|
+
var trGox = 0, trGoy = 0, trGsc = 1, trGop = 1
|
|
4839
|
+
if (trOut) {
|
|
4840
|
+
var trQ = trEase((t - trA.t) / trOut.d)
|
|
4841
|
+
if (trOut.kind === 'slide') { var trV2 = trSide(trOut.side); trGox = trV2[0] * trQ * ${TRANSITION_TRAVEL}; trGoy = trV2[1] * trQ * ${TRANSITION_TRAVEL} }
|
|
4842
|
+
else if (trOut.kind === 'fade') trGop = 1 - trQ
|
|
4843
|
+
else { trGsc = 1 - ${TRANSITION_SCALE_STEP} * trQ; trGop = 1 - trQ }
|
|
4844
|
+
}
|
|
4845
|
+
var trGM = null
|
|
4846
|
+
if (trOut && trOut.media && d.media) for (var trJ = 0; trJ < d.media.length; trJ++) if (d.media[trJ].id === trOut.media) trGM = d.media[trJ]
|
|
4847
|
+
var trGFr = trOut && trOut.media && trGM && trGM.frame ? trGM.frame : frame
|
|
4848
|
+
// The ghost layer: made on first need (most documents never cross a
|
|
4849
|
+
// boundary), sized with the card layer, above it and below the stack's
|
|
4850
|
+
// card planes. The flat stub paints the ghost on the card's own canvas.
|
|
4851
|
+
var gh = null
|
|
4852
|
+
if (trOut) {
|
|
4853
|
+
if (!r.ghost && ctx.THREE && ctx.scene && card.mesh) {
|
|
4854
|
+
var gT = ctx.THREE
|
|
4855
|
+
var gCv = document.createElement('canvas')
|
|
4856
|
+
gCv.width = cW; gCv.height = cH
|
|
4857
|
+
var gTex = new gT.CanvasTexture(gCv)
|
|
4858
|
+
gTex.colorSpace = gT.SRGBColorSpace
|
|
4859
|
+
gTex.minFilter = gT.LinearFilter; gTex.magFilter = gT.LinearFilter; gTex.generateMipmaps = false
|
|
4860
|
+
var gPh = 2 * ${Math.abs(CARD_Z)} * Math.tan(${CARD_FOV} * Math.PI / 180 / 2)
|
|
4861
|
+
var gMesh = new gT.Mesh(new gT.PlaneGeometry(gPh * (W / H) * (cW / W), gPh * (cH / H)), new gT.MeshBasicMaterial({ map: gTex, transparent: true, depthTest: false, depthWrite: false }))
|
|
4862
|
+
gMesh.position.set(0, 0, ${CARD_Z + GHOST_Z_LIFT})
|
|
4863
|
+
gMesh.frustumCulled = false
|
|
4864
|
+
gMesh.renderOrder = ${GHOST_RENDER_ORDER}
|
|
4865
|
+
ctx.scene.add(gMesh)
|
|
4866
|
+
r.ghost = { canvas: gCv, c2d: gCv.getContext('2d'), texture: gTex, mesh: gMesh, w: cW, h: cH }
|
|
4867
|
+
}
|
|
4868
|
+
gh = r.ghost || { c2d: c, canvas: cv }
|
|
4869
|
+
if (r.ghost && (r.ghost.w !== cW || r.ghost.h !== cH)) {
|
|
4870
|
+
r.ghost.canvas.width = cW; r.ghost.canvas.height = cH; r.ghost.w = cW; r.ghost.h = cH
|
|
4871
|
+
if (r.ghost.texture.dispose) r.ghost.texture.dispose()
|
|
4872
|
+
if (r.ghost.mesh.geometry && r.ghost.mesh.geometry.dispose) r.ghost.mesh.geometry.dispose()
|
|
4873
|
+
var gPh2 = 2 * ${Math.abs(CARD_Z)} * Math.tan(${CARD_FOV} * Math.PI / 180 / 2)
|
|
4874
|
+
r.ghost.mesh.geometry = new ctx.THREE.PlaneGeometry(gPh2 * (W / H) * (cW / W), gPh2 * (cH / H))
|
|
4875
|
+
}
|
|
4876
|
+
}
|
|
4877
|
+
// The two cursor points a boundary lerps between (frame space), set by
|
|
4878
|
+
// the passes below: the ghost's last, the live card's current.
|
|
4879
|
+
var trQa = null, trQb = null, trQs = 0, trQw = 0
|
|
4363
4880
|
// Gain routing (live via SET_DATA). With a mic sidecar (AT split) the
|
|
4364
4881
|
// recording <video> carries SYSTEM audio \u2014 its volume is the system fader \u2014
|
|
4365
4882
|
// and the sidecar element is the voice (micGain). Legacy takes have one
|
|
@@ -4556,6 +5073,19 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
4556
5073
|
if (bg.texture) bg.texture.needsUpdate = true
|
|
4557
5074
|
}
|
|
4558
5075
|
|
|
5076
|
+
// The card paints in PASSES: the ghost first (the outgoing clip's last
|
|
5077
|
+
// frame on its own canvas, while a boundary is crossing), then the live
|
|
5078
|
+
// card. A pass swaps the canvas, the element, the media, the source
|
|
5079
|
+
// moment and the card fields it reads; the ghost reads the camera as it
|
|
5080
|
+
// stood at the boundary and paints no clicks and no dot. The live pass
|
|
5081
|
+
// runs last, so everything after the loop reads the live card.
|
|
5082
|
+
var lvVideo = video, lvActM = actM, lvAmId = amId, lvSrcT = srcT, lvCfr = cfr, lvC = c, lvCv = cv
|
|
5083
|
+
var trPasses = trOut ? [true, false] : [false]
|
|
5084
|
+
for (var tpI = 0; tpI < trPasses.length; tpI++) {
|
|
5085
|
+
var tpG = trPasses[tpI]
|
|
5086
|
+
var tpT = t
|
|
5087
|
+
if (tpG) { c = gh.c2d; cv = gh.canvas; video = trGEl; actM = trGM; amId = trOut.media; srcT = trOut.at; cfr = trGFr; tpT = trA.t - 0.001 }
|
|
5088
|
+
else { c = lvC; cv = lvCv; video = lvVideo; actM = lvActM; amId = lvAmId; srcT = lvSrcT; cfr = lvCfr }
|
|
4559
5089
|
// The CARD layer canvas starts transparent each frame \u2014 the padding around
|
|
4560
5090
|
// the contain-fit card shows the background layer through the plane's alpha.
|
|
4561
5091
|
// Under overscan the canvas is larger than the frame and every frame-space
|
|
@@ -4612,7 +5142,7 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
4612
5142
|
var lvl = 1, zx = 0.5, zy = 0.5
|
|
4613
5143
|
var zt = d.zoomTrack
|
|
4614
5144
|
if (zt && zt.keyframes && zt.keyframes.length) {
|
|
4615
|
-
var z = TL.sample(zt,
|
|
5145
|
+
var z = TL.sample(zt, tpT, TL.lerpArray)
|
|
4616
5146
|
lvl = z[0]; zx = z[1]; zy = z[2]
|
|
4617
5147
|
}
|
|
4618
5148
|
var barH = bar.kind && bar.kind !== 'none' ? (bar.height || 44) * s2 : 0
|
|
@@ -4815,7 +5345,7 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
4815
5345
|
var cks = d.clicks || []
|
|
4816
5346
|
var ckF = d.clickFx || {}
|
|
4817
5347
|
var ckPress = 1
|
|
4818
|
-
if (cks.length) {
|
|
5348
|
+
if (cks.length && !tpG) {
|
|
4819
5349
|
var ckK = ckF.k || 1
|
|
4820
5350
|
var ckPre = ${CLICK_FX_PRE}
|
|
4821
5351
|
var ckRD = ${CLICK_RIPPLE_DUR} * (ckF.dur || 1)
|
|
@@ -4974,7 +5504,15 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
4974
5504
|
}
|
|
4975
5505
|
}
|
|
4976
5506
|
}
|
|
4977
|
-
|
|
5507
|
+
// Across a boundary the dot is ONE, drawn in frame space on the
|
|
5508
|
+
// overlay layer (the lerp after the passes), never on a card that is
|
|
5509
|
+
// moving: each pass leaves its point, mapped out of the zoom.
|
|
5510
|
+
if (trOut) {
|
|
5511
|
+
var tqX = ax, tqY = ay
|
|
5512
|
+
if (lvl > 1.001 && !d.zoomSuppressed) { tqX = fx + (ax - fx) * lvl; tqY = fy + (ay - fy) * lvl }
|
|
5513
|
+
if (tpG) trQa = [tqX, tqY]
|
|
5514
|
+
else { trQb = [tqX, tqY]; trQs = curSize * ckPress; trQw = s2 }
|
|
5515
|
+
} else if (cuA > 0.01) {
|
|
4978
5516
|
c.save()
|
|
4979
5517
|
c.fillStyle = 'rgba(255,255,255,' + (0.95 * cuA) + ')'
|
|
4980
5518
|
c.strokeStyle = 'rgba(0,0,0,' + (0.4 * cuA) + ')'
|
|
@@ -4985,6 +5523,8 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
4985
5523
|
}
|
|
4986
5524
|
if (fitCover) c.restore()
|
|
4987
5525
|
c.restore()
|
|
5526
|
+
if (tpG && gh.texture) gh.texture.needsUpdate = true
|
|
5527
|
+
}
|
|
4988
5528
|
|
|
4989
5529
|
// --- OVERLAY layer (screen-space plane, never tilts): the cam bubble, the
|
|
4990
5530
|
// recording's own footage. Text/image/video overlay CLIPS are the studio
|
|
@@ -5002,7 +5542,7 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
5002
5542
|
if (camV && camV.readyState >= 2) r.camHasFrame = true
|
|
5003
5543
|
var camActiveNow = !!(camV && camOn && camS.visible !== false && r.camHasFrame)
|
|
5004
5544
|
var ovSig = W + 'x' + H
|
|
5005
|
-
var ovDirty = ov.sig !== ovSig || camActiveNow || ov.active
|
|
5545
|
+
var ovDirty = ov.sig !== ovSig || camActiveNow || ov.active || !!trOut
|
|
5006
5546
|
if (ovDirty) {
|
|
5007
5547
|
ovC.clearRect(0, 0, W, H)
|
|
5008
5548
|
// webcam bubble \u2014 pinned to the frame corner regardless of card tilt/zoom.
|
|
@@ -5052,8 +5592,26 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
5052
5592
|
ovC.restore()
|
|
5053
5593
|
}
|
|
5054
5594
|
}
|
|
5595
|
+
// The boundary dot: the outgoing card's last cursor point and the
|
|
5596
|
+
// incoming's current one, each carried by its card's move, one dot on
|
|
5597
|
+
// the eased lerp between them (the idle fade never fires across a page
|
|
5598
|
+
// change). It lands on the incoming's own dot as the window closes.
|
|
5599
|
+
if (trOut && (trQa || trQb) && !(d.cursorStyle && d.cursorStyle.visible === false)) {
|
|
5600
|
+
var tqA = trQa || trQb, tqB = trQb || trQa
|
|
5601
|
+
var tqP = trEase((t - trA.t) / trA.d)
|
|
5602
|
+
var tqAx = (tqA[0] - W / 2) * trGsc + W / 2 + trGox * W, tqAy = (tqA[1] - H / 2) * trGsc + H / 2 - trGoy * H
|
|
5603
|
+
var tqBx = (tqB[0] - W / 2) * trSc + W / 2 + trOx * W, tqBy = (tqB[1] - H / 2) * trSc + H / 2 - trOy * H
|
|
5604
|
+
var tqX2 = tqAx + (tqBx - tqAx) * tqP, tqY2 = tqAy + (tqBy - tqAy) * tqP
|
|
5605
|
+
var tqR = trQs || (((d.cursorStyle && d.cursorStyle.size) || 24) * s * 0.5)
|
|
5606
|
+
ovC.save()
|
|
5607
|
+
ovC.fillStyle = 'rgba(255,255,255,0.95)'
|
|
5608
|
+
ovC.strokeStyle = 'rgba(0,0,0,0.4)'
|
|
5609
|
+
ovC.lineWidth = 2 * (trQw || s)
|
|
5610
|
+
ovC.beginPath(); ovC.arc(tqX2, tqY2, tqR, 0, Math.PI * 2); ovC.fill(); ovC.stroke()
|
|
5611
|
+
ovC.restore()
|
|
5612
|
+
}
|
|
5055
5613
|
ov.sig = ovSig
|
|
5056
|
-
ov.active = camActiveNow
|
|
5614
|
+
ov.active = camActiveNow || !!trOut
|
|
5057
5615
|
if (ov.texture) ov.texture.needsUpdate = true
|
|
5058
5616
|
}
|
|
5059
5617
|
|
|
@@ -5082,15 +5640,22 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
5082
5640
|
// which the overscan grows).
|
|
5083
5641
|
var cpk = d.cardPoseTrack
|
|
5084
5642
|
var cpH = 2 * ${Math.abs(CARD_Z)} * Math.tan(${CARD_FOV} * Math.PI / 180 / 2)
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5643
|
+
var cpv = null
|
|
5644
|
+
if (cpk && cpk.keyframes && cpk.keyframes.length) cpv = TL.sample(cpk, t, TL.lerpArray)
|
|
5645
|
+
if ((cpv || trIn) && card.mesh.scale && card.mesh.position) {
|
|
5646
|
+
// The pose track, then a boundary's enter on top: a slide is an
|
|
5647
|
+
// offset in plane widths and heights, a fade an opacity, a scale both.
|
|
5648
|
+
var cpS = (cpv ? cpv[0] : 1) * trSc
|
|
5649
|
+
var cpO = (cpv ? cpv[2] : 1) * trOp
|
|
5650
|
+
card.mesh.scale.x = cpS; card.mesh.scale.y = cpS
|
|
5651
|
+
card.mesh.position.x = trOx * cpH * (W / H)
|
|
5652
|
+
card.mesh.position.y = ((cpv ? cpv[1] : 0) + trOy) * cpH
|
|
5653
|
+
if (card.mesh.material) card.mesh.material.opacity = cpO
|
|
5090
5654
|
// Gone past its clip: the mesh leaves the scene, not just its paint.
|
|
5091
|
-
card.mesh.visible =
|
|
5092
|
-
} else if (card.mesh.scale && card.mesh.position && ((card.mesh.scale.x !== undefined && card.mesh.scale.x !== 1) || (card.mesh.position.y !== undefined && card.mesh.position.y !== 0) || card.mesh.visible === false)) {
|
|
5655
|
+
card.mesh.visible = cpO > 0.001
|
|
5656
|
+
} else if (card.mesh.scale && card.mesh.position && ((card.mesh.scale.x !== undefined && card.mesh.scale.x !== 1) || (card.mesh.position.y !== undefined && card.mesh.position.y !== 0) || (card.mesh.position.x !== undefined && card.mesh.position.x !== 0) || card.mesh.visible === false)) {
|
|
5093
5657
|
card.mesh.scale.x = 1; card.mesh.scale.y = 1
|
|
5658
|
+
card.mesh.position.x = 0
|
|
5094
5659
|
card.mesh.position.y = 0
|
|
5095
5660
|
if (card.mesh.material) card.mesh.material.opacity = 1
|
|
5096
5661
|
card.mesh.visible = true
|
|
@@ -5107,6 +5672,29 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
5107
5672
|
}
|
|
5108
5673
|
}
|
|
5109
5674
|
|
|
5675
|
+
// The ghost plane: posed as the card stood at the boundary (its lean, its
|
|
5676
|
+
// pose), then moved by its exit; hidden while no boundary is crossing.
|
|
5677
|
+
if (r.ghost && r.ghost.mesh) {
|
|
5678
|
+
var gm = r.ghost.mesh
|
|
5679
|
+
if (trOut && gh === r.ghost) {
|
|
5680
|
+
var gtT = trA.t - 0.001
|
|
5681
|
+
var grx = 0, gry = 0
|
|
5682
|
+
var gtk = d.tiltTrack
|
|
5683
|
+
if (gtk && gtk.keyframes && gtk.keyframes.length && !d.tiltSuppressed) { var gtv = TL.sample(gtk, gtT, TL.lerpArray); grx = gtv[0] * Math.PI / 180; gry = gtv[1] * Math.PI / 180 }
|
|
5684
|
+
gm.rotation.x = grx; gm.rotation.y = gry
|
|
5685
|
+
var gpv = [1, 0, 1]
|
|
5686
|
+
var gpk = d.cardPoseTrack
|
|
5687
|
+
if (gpk && gpk.keyframes && gpk.keyframes.length) gpv = TL.sample(gpk, gtT, TL.lerpArray)
|
|
5688
|
+
var gpH = 2 * ${Math.abs(CARD_Z)} * Math.tan(${CARD_FOV} * Math.PI / 180 / 2)
|
|
5689
|
+
var gS = gpv[0] * trGsc, gO = gpv[2] * trGop
|
|
5690
|
+
gm.scale.x = gS; gm.scale.y = gS
|
|
5691
|
+
gm.position.x = trGox * gpH * (W / H)
|
|
5692
|
+
gm.position.y = (gpv[1] + trGoy) * gpH
|
|
5693
|
+
if (gm.material) gm.material.opacity = gO
|
|
5694
|
+
gm.visible = gO > 0.001
|
|
5695
|
+
} else if (gm.visible !== false) gm.visible = false
|
|
5696
|
+
}
|
|
5697
|
+
|
|
5110
5698
|
// The card layer redraws every frame (dynamic content); bg/overlay uploads are
|
|
5111
5699
|
// gated inside their blocks (dirty-tracking above).
|
|
5112
5700
|
if (card.texture) card.texture.needsUpdate = true
|
|
@@ -5118,6 +5706,7 @@ var ON_FRAME = `(ctx, content, dt) => {
|
|
|
5118
5706
|
}
|
|
5119
5707
|
}`;
|
|
5120
5708
|
function studioLayerData(layers, duration) {
|
|
5709
|
+
const rec = "source" in layers ? layers : null;
|
|
5121
5710
|
return {
|
|
5122
5711
|
// Music/SFX clips with their gain envelopes baked (shared truth for the
|
|
5123
5712
|
// preview scheduler and the export's offline mix). `duckEnv` (the mic-derived
|
|
@@ -5205,9 +5794,60 @@ function studioLayerData(layers, duration) {
|
|
|
5205
5794
|
})()
|
|
5206
5795
|
};
|
|
5207
5796
|
if (o.kind !== "text") {
|
|
5797
|
+
const lm = rec ? layerMedia(rec, o.key) : null;
|
|
5798
|
+
const lmImage = lm ? lm.sourceKind === "image" : o.kind === "image";
|
|
5799
|
+
const lmCard = o.frame ? {
|
|
5800
|
+
bar: {
|
|
5801
|
+
kind: o.frame.browserBar?.kind ?? "none",
|
|
5802
|
+
url: o.frame.browserBar?.url ?? (lm ? pageDisplayUrl(lm.meta.pageUrl) : ""),
|
|
5803
|
+
showUrl: o.frame.browserBar?.showUrl ?? true,
|
|
5804
|
+
showControls: o.frame.browserBar?.showControls ?? true,
|
|
5805
|
+
height: o.frame.browserBar?.height ?? 44,
|
|
5806
|
+
...o.frame.browserBar?.theme ? { theme: o.frame.browserBar.theme } : {}
|
|
5807
|
+
},
|
|
5808
|
+
lean: [
|
|
5809
|
+
round6(o.frame.lean?.rx ?? 0),
|
|
5810
|
+
round6(o.frame.lean?.ry ?? 0)
|
|
5811
|
+
],
|
|
5812
|
+
shadow: o.frame.shadow ?? LAYER_CARD_SHADOW,
|
|
5813
|
+
shadowContact: o.frame.shadowContact ?? 0,
|
|
5814
|
+
...o.frame.shadowColor ? { shadowColor: o.frame.shadowColor } : {}
|
|
5815
|
+
} : null;
|
|
5816
|
+
const lmFacts = lm && o.frame && o.frame.cursor !== false && lm.cursor.length ? (() => {
|
|
5817
|
+
const lfx = rec.cursor.clickFx;
|
|
5818
|
+
const sm = smoothCursor(lm.cursor, {
|
|
5819
|
+
factor: rec.cursor.smoothing,
|
|
5820
|
+
clickSnap: lfx.style !== "none" || lfx.press
|
|
5821
|
+
});
|
|
5822
|
+
const clicks = [];
|
|
5823
|
+
for (const ev of lm.cursor) {
|
|
5824
|
+
if (ev.type === "down")
|
|
5825
|
+
clicks.push({
|
|
5826
|
+
t: round6(ev.t / 1e3),
|
|
5827
|
+
x: round6(ev.x),
|
|
5828
|
+
y: round6(ev.y)
|
|
5829
|
+
});
|
|
5830
|
+
}
|
|
5831
|
+
return {
|
|
5832
|
+
cur: {
|
|
5833
|
+
pts: sm.map((p) => ({
|
|
5834
|
+
t: round6(p.t),
|
|
5835
|
+
x: round6(p.x),
|
|
5836
|
+
y: round6(p.y)
|
|
5837
|
+
})),
|
|
5838
|
+
space: { w: lm.meta.width, h: lm.meta.height },
|
|
5839
|
+
size: rec.cursor.size,
|
|
5840
|
+
clicks
|
|
5841
|
+
}
|
|
5842
|
+
};
|
|
5843
|
+
})() : {};
|
|
5208
5844
|
return {
|
|
5209
5845
|
...base,
|
|
5210
|
-
|
|
5846
|
+
...lm ? { kind: lmImage ? "image" : "video" } : {},
|
|
5847
|
+
key: lm ? lm.videoKey : o.key,
|
|
5848
|
+
...lm && lm.crop ? { crop: lm.crop } : {},
|
|
5849
|
+
...lmCard ? { card: lmCard } : {},
|
|
5850
|
+
...lmFacts,
|
|
5211
5851
|
w: round6(o.width ?? OVERLAY_MEDIA_DEFAULT_WIDTH),
|
|
5212
5852
|
radius: o.radius ?? OVERLAY_MEDIA_DEFAULT_RADIUS,
|
|
5213
5853
|
opacity: o.opacity ?? 1,
|
|
@@ -5279,6 +5919,7 @@ function studioLayerData(layers, duration) {
|
|
|
5279
5919
|
function lowerToComposition(input) {
|
|
5280
5920
|
const doc = migrateMotion(input);
|
|
5281
5921
|
const rated = ratedSegments(doc);
|
|
5922
|
+
const transitions = docTransitions(doc);
|
|
5282
5923
|
const footageEnd = durationSec(doc, rated);
|
|
5283
5924
|
const duration = outputEnd(doc, footageEnd);
|
|
5284
5925
|
const fx = doc.cursor.clickFx;
|
|
@@ -5293,7 +5934,12 @@ function lowerToComposition(input) {
|
|
|
5293
5934
|
const layout = docCardLayout(doc);
|
|
5294
5935
|
const meta = doc.source.meta;
|
|
5295
5936
|
const zoomStyle = resolveZoomStyle(doc.zoomStyle, doc.zoomParams);
|
|
5296
|
-
const zoomSpans =
|
|
5937
|
+
const zoomSpans = restSpansThroughTransitions(
|
|
5938
|
+
doc.zoom,
|
|
5939
|
+
rated,
|
|
5940
|
+
transitions,
|
|
5941
|
+
ZOOM_SPAN_MIN
|
|
5942
|
+
).map((z) => {
|
|
5297
5943
|
if (z.focusMode === "auto") {
|
|
5298
5944
|
const f = followFocusEvents(
|
|
5299
5945
|
z,
|
|
@@ -5380,6 +6026,9 @@ function lowerToComposition(input) {
|
|
|
5380
6026
|
})
|
|
5381
6027
|
} : {},
|
|
5382
6028
|
...cursorFade.length ? { cursorFade } : {},
|
|
6029
|
+
// The boundaries that move: ON_FRAME reads them by output time and
|
|
6030
|
+
// needs no document. Absent when none moves.
|
|
6031
|
+
...transitions.length ? { transitions } : {},
|
|
5383
6032
|
// Click effects: OUTPUT-anchored click records + resolved styling (named
|
|
5384
6033
|
// intensities/colors become numbers HERE — ON_FRAME reads no registry).
|
|
5385
6034
|
...clickFxData(doc, rated),
|
|
@@ -5397,7 +6046,13 @@ function lowerToComposition(input) {
|
|
|
5397
6046
|
// producer of this track, never a second pose.
|
|
5398
6047
|
...(() => {
|
|
5399
6048
|
const head = entranceTiltKeyframes(cardEnter(doc.frame));
|
|
5400
|
-
const
|
|
6049
|
+
const tilts = doc.tilt ? restSpansThroughTransitions(
|
|
6050
|
+
doc.tilt,
|
|
6051
|
+
rated,
|
|
6052
|
+
transitions,
|
|
6053
|
+
TILT_SPAN_MIN
|
|
6054
|
+
) : [];
|
|
6055
|
+
const spans = tilts.length ? tiltTrackFromDoc(tilts, rated, zoomStyle.tilt) : void 0;
|
|
5401
6056
|
const track = prependEntrance(spans, head);
|
|
5402
6057
|
return track ? { tiltTrack: track } : {};
|
|
5403
6058
|
})(),
|
|
@@ -5874,18 +6529,18 @@ function zoomCoversRect(span, rect, layout, tol = 0.02) {
|
|
|
5874
6529
|
}
|
|
5875
6530
|
|
|
5876
6531
|
// src/digest/style.ts
|
|
5877
|
-
import { totalDuration as
|
|
6532
|
+
import { totalDuration as totalDuration5 } from "@vosjs/timeline";
|
|
5878
6533
|
|
|
5879
6534
|
// src/audioBeds.ts
|
|
5880
|
-
import { totalDuration as
|
|
5881
|
-
var
|
|
6535
|
+
import { totalDuration as totalDuration4 } from "@vosjs/timeline";
|
|
6536
|
+
var round33 = (v) => Math.round(v * 1e3) / 1e3;
|
|
5882
6537
|
function docOutputDuration(doc) {
|
|
5883
|
-
const footage =
|
|
6538
|
+
const footage = totalDuration4(ratedSegments(doc));
|
|
5884
6539
|
return isRecordingDoc(doc) ? outputEnd(migrateMotion(doc), footage) : footage;
|
|
5885
6540
|
}
|
|
5886
6541
|
function musicBedClip(input) {
|
|
5887
|
-
const track =
|
|
5888
|
-
const output =
|
|
6542
|
+
const track = round33(input.trackDuration);
|
|
6543
|
+
const output = round33(input.outputDuration);
|
|
5889
6544
|
const clip = {
|
|
5890
6545
|
id: input.id,
|
|
5891
6546
|
key: input.key,
|
|
@@ -5900,7 +6555,7 @@ function musicBedClip(input) {
|
|
|
5900
6555
|
fadeIn: 0,
|
|
5901
6556
|
// A bed that just stops reads as a glitch; clamp so a very short cut
|
|
5902
6557
|
// still spends most of its time at full level.
|
|
5903
|
-
fadeOut: output > 0 ? Math.min(1.5,
|
|
6558
|
+
fadeOut: output > 0 ? Math.min(1.5, round33(output / 4)) : 1.5,
|
|
5904
6559
|
duck: input.hasMic || void 0
|
|
5905
6560
|
};
|
|
5906
6561
|
if (output <= 0) return clip;
|
|
@@ -5929,18 +6584,18 @@ function refillAudioBeds(doc, prevDuration, nextDuration) {
|
|
|
5929
6584
|
const placed = clip.loop ? Math.max(clip.out - clip.in, clip.loopLen ?? clip.out - clip.in) : clip.out - clip.in;
|
|
5930
6585
|
if (Math.abs(clip.start + placed - prevDuration) > EPS2) continue;
|
|
5931
6586
|
const span = clip.out - clip.in;
|
|
5932
|
-
const nextLen =
|
|
6587
|
+
const nextLen = round33(nextDuration - clip.start);
|
|
5933
6588
|
if (clip.loop) {
|
|
5934
6589
|
if (nextLen >= span) {
|
|
5935
6590
|
clip.loopLen = nextLen;
|
|
5936
6591
|
} else {
|
|
5937
6592
|
clip.loop = void 0;
|
|
5938
6593
|
clip.loopLen = void 0;
|
|
5939
|
-
clip.out =
|
|
6594
|
+
clip.out = round33(clip.in + nextLen);
|
|
5940
6595
|
}
|
|
5941
6596
|
} else {
|
|
5942
6597
|
if (clip.in + nextLen <= clip.duration) {
|
|
5943
|
-
clip.out =
|
|
6598
|
+
clip.out = round33(clip.in + nextLen);
|
|
5944
6599
|
} else {
|
|
5945
6600
|
clip.out = clip.duration;
|
|
5946
6601
|
clip.loop = true;
|
|
@@ -6161,8 +6816,8 @@ function applyTemplate(template, take, opts) {
|
|
|
6161
6816
|
}
|
|
6162
6817
|
].sort((a, b) => a.at - b.at);
|
|
6163
6818
|
}
|
|
6164
|
-
const templateEnd =
|
|
6165
|
-
const takeEnd =
|
|
6819
|
+
const templateEnd = totalDuration5(ratedSegments(template));
|
|
6820
|
+
const takeEnd = totalDuration5(ratedSegments(doc));
|
|
6166
6821
|
const shift = shiftBy(opts.at, templateEnd, takeEnd);
|
|
6167
6822
|
const own = new Map((take.overlays ?? []).map((o) => [o.id, o]));
|
|
6168
6823
|
const overlays = (doc.overlays ?? []).filter(
|
|
@@ -6171,7 +6826,7 @@ function applyTemplate(template, take, opts) {
|
|
|
6171
6826
|
const skipped = [];
|
|
6172
6827
|
for (const clip of template.overlays ?? []) {
|
|
6173
6828
|
const next = structuredClone(clip);
|
|
6174
|
-
next.start = Math.max(0,
|
|
6829
|
+
next.start = Math.max(0, round34(clip.start + shift));
|
|
6175
6830
|
next.from = from;
|
|
6176
6831
|
const mine = own.get(clip.id);
|
|
6177
6832
|
if (next.kind === "text") {
|
|
@@ -6199,7 +6854,7 @@ function applyTemplate(template, take, opts) {
|
|
|
6199
6854
|
for (const clip of template.objects ?? []) {
|
|
6200
6855
|
const next = structuredClone(clip);
|
|
6201
6856
|
if (next.span)
|
|
6202
|
-
next.span.start = Math.max(0,
|
|
6857
|
+
next.span.start = Math.max(0, round34(next.span.start + shift));
|
|
6203
6858
|
next.from = from;
|
|
6204
6859
|
objects.push(next);
|
|
6205
6860
|
}
|
|
@@ -6209,7 +6864,7 @@ function applyTemplate(template, take, opts) {
|
|
|
6209
6864
|
);
|
|
6210
6865
|
for (const clip of template.audio) {
|
|
6211
6866
|
const next = structuredClone(clip);
|
|
6212
|
-
next.start = Math.max(0,
|
|
6867
|
+
next.start = Math.max(0, round34(clip.start + shift));
|
|
6213
6868
|
next.from = from;
|
|
6214
6869
|
audio.push(next);
|
|
6215
6870
|
}
|
|
@@ -6230,7 +6885,7 @@ function applyTemplate(template, take, opts) {
|
|
|
6230
6885
|
}
|
|
6231
6886
|
return { doc, notes };
|
|
6232
6887
|
}
|
|
6233
|
-
var
|
|
6888
|
+
var round34 = (v) => Math.round(v * 1e3) / 1e3;
|
|
6234
6889
|
|
|
6235
6890
|
// src/digest/geometry.ts
|
|
6236
6891
|
var CROP_MIN_FRAC = 0.25;
|
|
@@ -6472,7 +7127,7 @@ function round10(v) {
|
|
|
6472
7127
|
|
|
6473
7128
|
// src/lower/lowerStudioDoc.ts
|
|
6474
7129
|
import { applyTimelineEdits } from "@vosjs/shared/timelineEdits";
|
|
6475
|
-
import { totalDuration as
|
|
7130
|
+
import { totalDuration as totalDuration6 } from "@vosjs/timeline";
|
|
6476
7131
|
function lowerStudioDoc(doc, opts = {}) {
|
|
6477
7132
|
return isRecordingDoc(doc) ? lowerToComposition(doc) : lowerProgramDoc(doc, opts);
|
|
6478
7133
|
}
|
|
@@ -6503,7 +7158,7 @@ function lowerProgramDoc(doc, opts = {}) {
|
|
|
6503
7158
|
) : doc.program.config;
|
|
6504
7159
|
const own = programDuration(doc);
|
|
6505
7160
|
const rated = ratedSegments(doc);
|
|
6506
|
-
const duration = own > 0 ?
|
|
7161
|
+
const duration = own > 0 ? totalDuration6(rated) : 0;
|
|
6507
7162
|
const entryData = studioLayerData(doc, duration);
|
|
6508
7163
|
const baseData = anchor.data && typeof anchor.data === "object" ? anchor.data : {};
|
|
6509
7164
|
const retimed = !!doc.speed?.length && own > 0;
|
|
@@ -7101,31 +7756,10 @@ import {
|
|
|
7101
7756
|
removeSegment,
|
|
7102
7757
|
segmentRate as segmentRate3,
|
|
7103
7758
|
sourceToTimeline as sourceToTimeline2,
|
|
7104
|
-
splitBySpeed as
|
|
7105
|
-
totalDuration as
|
|
7759
|
+
splitBySpeed as splitBySpeed4,
|
|
7760
|
+
totalDuration as totalDuration7,
|
|
7106
7761
|
trimSegment
|
|
7107
7762
|
} from "@vosjs/timeline";
|
|
7108
|
-
function effectiveSegments(doc) {
|
|
7109
|
-
if (isRecordingDoc(doc) && doc.segments.length) return doc.segments;
|
|
7110
|
-
return [{ in: 0, out: anchorSourceDuration(doc) }];
|
|
7111
|
-
}
|
|
7112
|
-
var segmentPieces = (seg, speeds, freezes, leading) => {
|
|
7113
|
-
const own = freezes.filter(
|
|
7114
|
-
(f) => f.at <= seg.out + 1e-9 && (leading ? f.at >= seg.in - 1e-9 : f.at > seg.in + 1e-9)
|
|
7115
|
-
);
|
|
7116
|
-
return withFreezes(splitBySpeed3([seg], speeds), own);
|
|
7117
|
-
};
|
|
7118
|
-
var outputLen = (seg, speeds, freezes = [], leading = true) => totalDuration6(segmentPieces(seg, speeds, freezes, leading));
|
|
7119
|
-
var segmentStarts = (segments, speeds, freezes = []) => {
|
|
7120
|
-
const starts = [];
|
|
7121
|
-
let acc = 0;
|
|
7122
|
-
segments.forEach((s, i) => {
|
|
7123
|
-
starts.push(acc);
|
|
7124
|
-
acc += outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i));
|
|
7125
|
-
});
|
|
7126
|
-
return starts;
|
|
7127
|
-
};
|
|
7128
|
-
var contiguous = (segments, i) => i > 0 && Math.abs(segments[i - 1].out - segments[i].in) < 1e-9;
|
|
7129
7763
|
var videoLane = {
|
|
7130
7764
|
id: "video",
|
|
7131
7765
|
label: "Video",
|
|
@@ -7177,7 +7811,7 @@ var videoLane = {
|
|
|
7177
7811
|
if (index < 0 || index >= segments.length) return null;
|
|
7178
7812
|
const seg = segments[index];
|
|
7179
7813
|
const starts = segmentStarts(segments, speeds, freezes);
|
|
7180
|
-
const fullMap =
|
|
7814
|
+
const fullMap = splitBySpeed4([{ in: 0, out: sourceDuration }], speeds);
|
|
7181
7815
|
const edgeSrc = g.edge === "start" ? seg.in : seg.out;
|
|
7182
7816
|
const edgeOutNow = g.edge === "start" ? starts[index] : starts[index] + outputLen(
|
|
7183
7817
|
seg,
|
|
@@ -7213,7 +7847,7 @@ var videoLane = {
|
|
|
7213
7847
|
const leading = index === 0 || !contiguous(segments, index);
|
|
7214
7848
|
const local = g.t - starts[index];
|
|
7215
7849
|
for (const m of freezeOutputExtents(
|
|
7216
|
-
|
|
7850
|
+
splitBySpeed4([s], speeds),
|
|
7217
7851
|
freezes.filter(
|
|
7218
7852
|
(f) => f.at <= s.out + 1e-9 && (leading ? f.at >= s.in - 1e-9 : f.at > s.in + 1e-9)
|
|
7219
7853
|
)
|
|
@@ -7280,7 +7914,7 @@ var freezeLane = {
|
|
|
7280
7914
|
if (!freezes.length) return [];
|
|
7281
7915
|
const segs = effectiveSegments(doc);
|
|
7282
7916
|
const marks = freezeOutputExtents(
|
|
7283
|
-
|
|
7917
|
+
splitBySpeed4(segs, doc.speed ?? []),
|
|
7284
7918
|
freezes
|
|
7285
7919
|
);
|
|
7286
7920
|
return freezes.flatMap((f) => {
|
|
@@ -7306,12 +7940,12 @@ var freezeLane = {
|
|
|
7306
7940
|
if (g.type === "create") {
|
|
7307
7941
|
const rated = ratedSegments(doc);
|
|
7308
7942
|
for (const m of freezeOutputExtents(
|
|
7309
|
-
|
|
7943
|
+
splitBySpeed4(segs, speeds),
|
|
7310
7944
|
freezes
|
|
7311
7945
|
).values()) {
|
|
7312
7946
|
if (g.t >= m.t - 1e-9 && g.t <= m.t + m.duration + 1e-9) return null;
|
|
7313
7947
|
}
|
|
7314
|
-
if (g.t >
|
|
7948
|
+
if (g.t > totalDuration7(rated) + 1e-9) return null;
|
|
7315
7949
|
const srcT = round11(mapTime2(rated, Math.max(0, g.t)));
|
|
7316
7950
|
if (!kept(srcT) || !clear(srcT)) return null;
|
|
7317
7951
|
const id = nextFreezeId(doc);
|
|
@@ -7347,11 +7981,11 @@ var freezeLane = {
|
|
|
7347
7981
|
return mine;
|
|
7348
7982
|
};
|
|
7349
7983
|
const base = withFreezes(
|
|
7350
|
-
|
|
7984
|
+
splitBySpeed4(segs, speeds),
|
|
7351
7985
|
freezes.filter((x) => x.id !== f.id)
|
|
7352
7986
|
);
|
|
7353
7987
|
if (g.type === "move" || g.type === "resize" && g.edge === "start") {
|
|
7354
|
-
const marks = freezeOutputExtents(
|
|
7988
|
+
const marks = freezeOutputExtents(splitBySpeed4(segs, speeds), freezes);
|
|
7355
7989
|
const mine = marks.get(f.id);
|
|
7356
7990
|
const endOut = mine ? mine.t + mine.duration : null;
|
|
7357
7991
|
const srcT = round11(
|
|
@@ -7364,7 +7998,7 @@ var freezeLane = {
|
|
|
7364
7998
|
x.at = srcT;
|
|
7365
7999
|
if (g.type === "resize" && endOut !== null) {
|
|
7366
8000
|
const after = freezeOutputExtents(
|
|
7367
|
-
|
|
8001
|
+
splitBySpeed4(effectiveSegments(d), d.speed ?? []),
|
|
7368
8002
|
docFreezes({
|
|
7369
8003
|
...d,
|
|
7370
8004
|
freeze: (d.freeze ?? []).map(
|
|
@@ -7378,7 +8012,7 @@ var freezeLane = {
|
|
|
7378
8012
|
};
|
|
7379
8013
|
}
|
|
7380
8014
|
if (g.type === "resize") {
|
|
7381
|
-
const marks = freezeOutputExtents(
|
|
8015
|
+
const marks = freezeOutputExtents(splitBySpeed4(segs, speeds), freezes);
|
|
7382
8016
|
const mine = marks.get(f.id);
|
|
7383
8017
|
if (!mine) return null;
|
|
7384
8018
|
const seconds = clampFreezeSeconds(g.t - mine.t);
|
|
@@ -7939,7 +8573,7 @@ var speedLane = {
|
|
|
7939
8573
|
const sp = spans.find((s) => s.id === g.id);
|
|
7940
8574
|
if (!sp) return null;
|
|
7941
8575
|
const others = spans.filter((o) => o.id !== g.id);
|
|
7942
|
-
const base =
|
|
8576
|
+
const base = splitBySpeed4(effectiveSegments(doc), others);
|
|
7943
8577
|
if (g.type === "move") {
|
|
7944
8578
|
const len = sp.out - sp.in;
|
|
7945
8579
|
let newIn = clampToKept(
|
|
@@ -8522,11 +9156,13 @@ export {
|
|
|
8522
9156
|
FREEZE_SECONDS_MIN,
|
|
8523
9157
|
HOUSE_GRADIENT,
|
|
8524
9158
|
IDLE_KINDS,
|
|
9159
|
+
LAYER_CARD_SHADOW,
|
|
8525
9160
|
LAYOUT_FRAME_FIELDS,
|
|
8526
9161
|
LOOK_KINDS,
|
|
8527
9162
|
MEDIA_ENTER_KINDS,
|
|
8528
9163
|
MEDIA_EXIT_KINDS,
|
|
8529
9164
|
MEDIA_FRAME_KEYS,
|
|
9165
|
+
MEDIA_KEY_PREFIX,
|
|
8530
9166
|
MINIMAL_BAR_THEMES,
|
|
8531
9167
|
MOTION_DELTA,
|
|
8532
9168
|
MOTION_EASE,
|
|
@@ -8549,6 +9185,8 @@ export {
|
|
|
8549
9185
|
REST_TILT_ID,
|
|
8550
9186
|
SCENE_MOTION,
|
|
8551
9187
|
SCENE_QUIET,
|
|
9188
|
+
SEGMENT_ENTER_KINDS,
|
|
9189
|
+
SEGMENT_EXIT_KINDS,
|
|
8552
9190
|
SPEED_RATE_MAX,
|
|
8553
9191
|
SPEED_RATE_MIN,
|
|
8554
9192
|
SPEED_SPAN_MIN,
|
|
@@ -8574,6 +9212,9 @@ export {
|
|
|
8574
9212
|
TILT_RAMP_OUT,
|
|
8575
9213
|
TILT_SPAN_MIN,
|
|
8576
9214
|
TILT_UI_DEG_MAX,
|
|
9215
|
+
TRANSITION_SECONDS,
|
|
9216
|
+
TRANSITION_SECONDS_MAX,
|
|
9217
|
+
TRANSITION_SECONDS_MIN,
|
|
8577
9218
|
TRANSITION_SPEED_MULT,
|
|
8578
9219
|
WINDOW_FOCUS_MIN,
|
|
8579
9220
|
ZOOM_CHAIN_GAP,
|
|
@@ -8639,6 +9280,7 @@ export {
|
|
|
8639
9280
|
docStillTime,
|
|
8640
9281
|
docToCropSpace,
|
|
8641
9282
|
docToFullSpace,
|
|
9283
|
+
docTransitions,
|
|
8642
9284
|
dropTemplate,
|
|
8643
9285
|
duckCurve,
|
|
8644
9286
|
effectiveSegments,
|
|
@@ -8673,6 +9315,7 @@ export {
|
|
|
8673
9315
|
isStageClip,
|
|
8674
9316
|
isUnitStep,
|
|
8675
9317
|
lastLayerEnd,
|
|
9318
|
+
layerMedia,
|
|
8676
9319
|
layoutOf,
|
|
8677
9320
|
levelForFocusFraction,
|
|
8678
9321
|
lookFromBrand,
|
|
@@ -8684,6 +9327,8 @@ export {
|
|
|
8684
9327
|
mediaDuration,
|
|
8685
9328
|
mediaFrame,
|
|
8686
9329
|
mediaKey,
|
|
9330
|
+
mediaRef,
|
|
9331
|
+
mediaRefId,
|
|
8687
9332
|
mediaSource,
|
|
8688
9333
|
micLane,
|
|
8689
9334
|
migrateHostedDoc,
|
|
@@ -8735,10 +9380,15 @@ export {
|
|
|
8735
9380
|
resolveText3dAsset,
|
|
8736
9381
|
resolveZoomStyle,
|
|
8737
9382
|
restLeanOf,
|
|
9383
|
+
restSpansThroughTransitions,
|
|
8738
9384
|
sameMedia,
|
|
8739
9385
|
sceneChanges,
|
|
8740
9386
|
scrollRuns,
|
|
9387
|
+
segmentOutputExtents,
|
|
9388
|
+
outputLen as segmentOutputLen,
|
|
9389
|
+
segmentStarts,
|
|
8741
9390
|
setSpeedInRange,
|
|
9391
|
+
slideSide,
|
|
8742
9392
|
smoothCursor,
|
|
8743
9393
|
spanOutputExtent,
|
|
8744
9394
|
speedLane,
|
|
@@ -8748,6 +9398,7 @@ export {
|
|
|
8748
9398
|
tiltLane,
|
|
8749
9399
|
tiltTrackFromDoc,
|
|
8750
9400
|
transitionMult,
|
|
9401
|
+
transitionSeconds,
|
|
8751
9402
|
videoLane,
|
|
8752
9403
|
voiceKey,
|
|
8753
9404
|
withBackdrop,
|