@vosjs/studio-core 0.42.4 → 0.43.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.js CHANGED
@@ -3791,7 +3791,7 @@ import {
3791
3791
  EASINGS,
3792
3792
  lerpArray as lerpArray2,
3793
3793
  sample as sample2,
3794
- segmentRate as segmentRate2,
3794
+ segmentRate as segmentRate3,
3795
3795
  sortKeyframes as sortKeyframes2,
3796
3796
  sourceToTimeline,
3797
3797
  splitBySpeed as splitBySpeed3,
@@ -4709,6 +4709,114 @@ function round42(v) {
4709
4709
  return Math.round(v * 1e5) / 1e5;
4710
4710
  }
4711
4711
 
4712
+ // src/lower/anchor.ts
4713
+ import { segmentRate } from "@vosjs/timeline";
4714
+ var ELAPSED_PLACEHOLDER = "{elapsed}";
4715
+ var EPS = 1e-9;
4716
+ function sourceMomentToOutput(rated, t, media) {
4717
+ if (!Number.isFinite(t)) return null;
4718
+ let acc = 0;
4719
+ let edge = null;
4720
+ for (const p of rated) {
4721
+ const rate = segmentRate(p);
4722
+ const len = Math.max(0, p.out - p.in) / rate;
4723
+ if (sameMedia(p.media, media)) {
4724
+ if (t >= p.in - EPS && t < p.out - EPS)
4725
+ return acc + Math.max(0, t - p.in) / rate;
4726
+ if (edge === null && Math.abs(t - p.out) <= 1e-6) edge = acc + len;
4727
+ }
4728
+ acc += len;
4729
+ }
4730
+ return edge;
4731
+ }
4732
+ var isNum = (v) => typeof v === "number" && Number.isFinite(v);
4733
+ function stepElapsedMs(meta, step) {
4734
+ const skips = Array.isArray(meta.skips) ? meta.skips : [];
4735
+ for (const raw of skips) {
4736
+ if (!raw || typeof raw !== "object") continue;
4737
+ const s = raw;
4738
+ if (!isNum(s.ms) || s.ms < 0) continue;
4739
+ const named = step.id !== void 0 && (s.id === step.id || s.step === step.id) || s.step === step.step;
4740
+ if (named) return s.ms;
4741
+ }
4742
+ const own = step.offCameraMs;
4743
+ return isNum(own) && own >= 0 ? own : null;
4744
+ }
4745
+ function elapsedFor(meta, step) {
4746
+ const own = stepElapsedMs(meta, step);
4747
+ if (own !== null) return { ms: own };
4748
+ const earlier = (meta.steps ?? []).filter((s) => s !== step && s.step < step.step).sort((a, b) => b.step - a.step);
4749
+ for (const s of earlier) {
4750
+ const ms = stepElapsedMs(meta, s);
4751
+ if (ms !== null) return { ms, from: s };
4752
+ }
4753
+ return null;
4754
+ }
4755
+ function resolveAnchor(doc, anchor, rated) {
4756
+ const source = mediaSource(doc, anchor.media);
4757
+ if (!source) return { start: null, miss: "media" };
4758
+ const meta = source.meta;
4759
+ const step = findStep(meta.steps ?? [], anchor.step);
4760
+ if (!step) return { start: null, miss: "step" };
4761
+ const elapsed = elapsedFor(meta, step);
4762
+ const extra = elapsed ? {
4763
+ elapsedMs: elapsed.ms,
4764
+ ...elapsed.from ? { elapsedFrom: elapsed.from } : {}
4765
+ } : {};
4766
+ if (step.skipped) return { start: null, step, miss: "skipped", ...extra };
4767
+ const moment = anchor.at === "end" ? step.tEnd : step.tStart;
4768
+ const out = sourceMomentToOutput(rated, moment, anchor.media);
4769
+ if (out === null) return { start: null, step, miss: "cut", ...extra };
4770
+ const offset = isNum(anchor.offset) ? anchor.offset : 0;
4771
+ return { start: round33(Math.max(0, out + offset)), step, ...extra };
4772
+ }
4773
+ var round33 = (v) => Math.round(v * 1e3) / 1e3;
4774
+ function formatElapsed(ms) {
4775
+ const s = Math.max(0, ms) / 1e3;
4776
+ const plural = (n, unit) => `${n} ${unit}${n === 1 ? "" : "s"}`;
4777
+ if (s < 9.5) return plural(Math.max(1, Math.round(s)), "second");
4778
+ if (s < 57.5) return plural(Math.round(s / 5) * 5, "second");
4779
+ const m = s / 60;
4780
+ if (m < 59.5) return plural(Math.max(1, Math.round(m)), "minute");
4781
+ const h = m / 60;
4782
+ if (h < 47.5) return plural(Math.max(1, Math.round(h)), "hour");
4783
+ return plural(Math.round(h / 24), "day");
4784
+ }
4785
+ function hasElapsed(text) {
4786
+ if (typeof text === "string") return text.includes(ELAPSED_PLACEHOLDER);
4787
+ return Array.isArray(text) && text.some(
4788
+ (r) => typeof r?.text === "string" && r.text.includes(ELAPSED_PLACEHOLDER)
4789
+ );
4790
+ }
4791
+ function fillElapsed(text, words) {
4792
+ const fill = (s) => s.split(ELAPSED_PLACEHOLDER).join(words);
4793
+ if (typeof text === "string") return fill(text);
4794
+ return text.map(
4795
+ (r) => typeof r.text === "string" && r.text.includes(ELAPSED_PLACEHOLDER) ? { ...r, text: fill(r.text) } : r
4796
+ );
4797
+ }
4798
+ function anchoredClip(clip, doc, rated) {
4799
+ if (!clip.anchor || typeof clip.anchor !== "object") return clip;
4800
+ const r = resolveAnchor(doc, clip.anchor, rated);
4801
+ let next = clip;
4802
+ if (r.start !== null && r.start !== clip.start)
4803
+ next = { ...next, start: r.start };
4804
+ if (next.kind === "text" && r.elapsedMs !== void 0 && hasElapsed(next.text)) {
4805
+ next = { ...next, text: fillElapsed(next.text, formatElapsed(r.elapsedMs)) };
4806
+ }
4807
+ return next;
4808
+ }
4809
+ function resolveOverlayAnchors(doc, rated) {
4810
+ const overlays = Array.isArray(doc.overlays) ? doc.overlays : [];
4811
+ if (!overlays.some((o) => o && o.anchor)) return doc;
4812
+ return {
4813
+ ...doc,
4814
+ overlays: overlays.map(
4815
+ (o) => o && o.anchor ? anchoredClip(o, doc, rated) : o
4816
+ )
4817
+ };
4818
+ }
4819
+
4712
4820
  // src/lower/cursorIdle.ts
4713
4821
  var CURSOR_IDLE_HOLD = 1;
4714
4822
  var CURSOR_IDLE_FADE_OUT = 0.35;
@@ -6264,7 +6372,7 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
6264
6372
  }`;
6265
6373
 
6266
6374
  // src/lower/extractClicks.ts
6267
- import { segmentRate } from "@vosjs/timeline";
6375
+ import { segmentRate as segmentRate2 } from "@vosjs/timeline";
6268
6376
  var CLICK_FX_PRE = 0.06;
6269
6377
  var CLICK_RIPPLE_DUR = 0.45;
6270
6378
  var CLICK_PULSE_DUR = 0.35;
@@ -6277,7 +6385,7 @@ function outputAt(segments, sourceT, media) {
6277
6385
  if (!segments.length) return sourceT;
6278
6386
  let acc = 0;
6279
6387
  for (const s of segments) {
6280
- const rate = segmentRate(s);
6388
+ const rate = segmentRate2(s);
6281
6389
  if (sameMedia(s.media, media) && sourceT >= s.in && sourceT <= s.out)
6282
6390
  return acc + (sourceT - s.in) / rate;
6283
6391
  acc += Math.max(0, s.out - s.in) / rate;
@@ -6288,7 +6396,7 @@ function keptOutputEnd(segments, sIn, sOut, media) {
6288
6396
  let acc = 0;
6289
6397
  let end = null;
6290
6398
  for (const p of segments) {
6291
- const rate = segmentRate(p);
6399
+ const rate = segmentRate2(p);
6292
6400
  if (sameMedia(p.media, media)) {
6293
6401
  const ovIn = Math.max(sIn, p.in);
6294
6402
  const ovOut = Math.min(sOut, p.out);
@@ -6401,7 +6509,7 @@ function spanOutputExtent(segments, sIn, sOut, media) {
6401
6509
  let start = null;
6402
6510
  let end = null;
6403
6511
  for (const p of segments) {
6404
- const rate = segmentRate2(p);
6512
+ const rate = segmentRate3(p);
6405
6513
  const len = Math.max(0, p.out - p.in) / rate;
6406
6514
  if (!sameMedia(p.media, media)) {
6407
6515
  acc += len;
@@ -6911,9 +7019,24 @@ var SETUP = `async (ctx) => {
6911
7019
  ;(ns.videoBlobs || (ns.videoBlobs = new Map())).set(src, fetched)
6912
7020
  return URL.createObjectURL(fetched)
6913
7021
  }
7022
+ // Every element handed out by this SETUP, so two players never share one.
7023
+ // A take may name one file as two media (the same shot as a teaser and in
7024
+ // full), or as a media and the footage; the by-src cache would hand both
7025
+ // ONE element, and ON_FRAME's concat rule, which pauses every media that
7026
+ // is not under the playhead, then paused the playing clip every frame under
7027
+ // the other name: play/pause churn, the element falling behind the clock,
7028
+ // and a drift-guard seek every ~0.3 s that read as a stuck picture jumping
7029
+ // forward. A second claim on the same bytes gets its OWN element, kept in
7030
+ // the cache under a slot of its own so a warm LOAD reuses it like the first.
7031
+ const claimed = new Set()
6914
7032
  const load = async (src, muted) => {
6915
- let v = cache.get(src)
6916
- if (v) return v
7033
+ let key = src
7034
+ let v = cache.get(key)
7035
+ for (let n = 2; v && claimed.has(v); n++) {
7036
+ key = src + '#' + n
7037
+ v = cache.get(key)
7038
+ }
7039
+ if (v) { claimed.add(v); return v }
6917
7040
  let url = src
6918
7041
  if (ctx.data.videoFetchMode === 'blob') {
6919
7042
  try { url = await toBlobUrl(src) }
@@ -6933,7 +7056,8 @@ var SETUP = `async (ctx) => {
6933
7056
  v.onerror = () => rej(new Error('[studio] video failed to load' + (v.error ? ' (' + v.error.code + (v.error.message ? ': ' + v.error.message : '') + ')' : '')))
6934
7057
  v.load()
6935
7058
  })
6936
- cache.set(src, v)
7059
+ cache.set(key, v)
7060
+ claimed.add(v)
6937
7061
  return v
6938
7062
  }
6939
7063
  // Shots: the "video" is a still image \u2014 drawImage accepts it directly and the
@@ -7377,7 +7501,7 @@ var ON_FRAME = `(ctx, content, dt) => {
7377
7501
  // The card under the playhead: another media's own card fields over the
7378
7502
  // take's frame (resolved at lowering), the take's frame for the primary.
7379
7503
  var cfr = amId && actM && actM.frame ? actM.frame : frame
7380
- if (r.media) for (var amK in r.media) { var amE = r.media[amK]; if (amK !== amId && amE && amE.pause && amE.paused === false) amE.pause() }
7504
+ if (r.media) for (var amK in r.media) { var amE = r.media[amK]; if (amK !== amId && amE && amE !== video && amE.pause && amE.paused === false) amE.pause() }
7381
7505
  if (amId) {
7382
7506
  if (r.video && r.video.pause && r.video.paused === false) r.video.pause()
7383
7507
  if (r.cam && r.cam.pause && r.cam.paused === false) r.cam.pause()
@@ -8835,8 +8959,9 @@ function studioLayerData(input, duration, pins) {
8835
8959
  };
8836
8960
  }
8837
8961
  function lowerToComposition(input) {
8838
- const doc = migrateText(migrateMotion(input));
8839
- const rated = ratedSegments(doc);
8962
+ const read = migrateText(migrateMotion(input));
8963
+ const rated = ratedSegments(read);
8964
+ const doc = resolveOverlayAnchors(read, rated);
8840
8965
  const transitions = docTransitions(doc);
8841
8966
  const footageEnd = durationSec(doc, rated);
8842
8967
  const duration = outputEnd(doc, footageEnd);
@@ -9050,8 +9175,9 @@ function loweredZoomSpans(doc, rated, transitions, layout, camera, zoomStyle) {
9050
9175
  });
9051
9176
  }
9052
9177
  function docZoomTrack(input) {
9053
- const doc = migrateText(migrateMotion(input));
9054
- const rated = ratedSegments(doc);
9178
+ const read = migrateText(migrateMotion(input));
9179
+ const rated = ratedSegments(read);
9180
+ const doc = resolveOverlayAnchors(read, rated);
9055
9181
  const transitions = docTransitions(doc);
9056
9182
  const layout = docCardLayout(doc);
9057
9183
  const camera = cameraModel(doc.frame);
@@ -9779,14 +9905,16 @@ import { totalDuration as totalDuration5 } from "@vosjs/timeline";
9779
9905
 
9780
9906
  // src/audioBeds.ts
9781
9907
  import { totalDuration as totalDuration4 } from "@vosjs/timeline";
9782
- var round33 = (v) => Math.round(v * 1e3) / 1e3;
9908
+ var round34 = (v) => Math.round(v * 1e3) / 1e3;
9783
9909
  function docOutputDuration(doc) {
9784
- const footage = totalDuration4(ratedSegments(doc));
9785
- return isRecordingDoc(doc) ? outputEnd(migrateMotion(doc), footage) : footage;
9910
+ const rated = ratedSegments(doc);
9911
+ const footage = totalDuration4(rated);
9912
+ if (!isRecordingDoc(doc)) return footage;
9913
+ return outputEnd(resolveOverlayAnchors(migrateMotion(doc), rated), footage);
9786
9914
  }
9787
9915
  function musicBedClip(input) {
9788
- const track = round33(input.trackDuration);
9789
- const output = round33(input.outputDuration);
9916
+ const track = round34(input.trackDuration);
9917
+ const output = round34(input.outputDuration);
9790
9918
  const clip = {
9791
9919
  id: input.id,
9792
9920
  key: input.key,
@@ -9801,7 +9929,7 @@ function musicBedClip(input) {
9801
9929
  fadeIn: 0,
9802
9930
  // A bed that just stops reads as a glitch; clamp so a very short cut
9803
9931
  // still spends most of its time at full level.
9804
- fadeOut: output > 0 ? Math.min(1.5, round33(output / 4)) : 1.5,
9932
+ fadeOut: output > 0 ? Math.min(1.5, round34(output / 4)) : 1.5,
9805
9933
  duck: input.hasMic || void 0
9806
9934
  };
9807
9935
  if (output <= 0) return clip;
@@ -9820,28 +9948,28 @@ function isMusicBed(clip, outputDuration) {
9820
9948
  return Math.abs(placed - outputDuration) <= 1;
9821
9949
  }
9822
9950
  function refillAudioBeds(doc, prevDuration, nextDuration) {
9823
- const EPS2 = 0.05;
9824
- if (Math.abs(nextDuration - prevDuration) <= EPS2 || nextDuration <= 0) {
9951
+ const EPS3 = 0.05;
9952
+ if (Math.abs(nextDuration - prevDuration) <= EPS3 || nextDuration <= 0) {
9825
9953
  return false;
9826
9954
  }
9827
9955
  let changed = false;
9828
9956
  for (const clip of doc.audio) {
9829
9957
  if (clip.start >= nextDuration) continue;
9830
9958
  const placed = clip.loop ? Math.max(clip.out - clip.in, clip.loopLen ?? clip.out - clip.in) : clip.out - clip.in;
9831
- if (Math.abs(clip.start + placed - prevDuration) > EPS2) continue;
9959
+ if (Math.abs(clip.start + placed - prevDuration) > EPS3) continue;
9832
9960
  const span = clip.out - clip.in;
9833
- const nextLen = round33(nextDuration - clip.start);
9961
+ const nextLen = round34(nextDuration - clip.start);
9834
9962
  if (clip.loop) {
9835
9963
  if (nextLen >= span) {
9836
9964
  clip.loopLen = nextLen;
9837
9965
  } else {
9838
9966
  clip.loop = void 0;
9839
9967
  clip.loopLen = void 0;
9840
- clip.out = round33(clip.in + nextLen);
9968
+ clip.out = round34(clip.in + nextLen);
9841
9969
  }
9842
9970
  } else {
9843
9971
  if (clip.in + nextLen <= clip.duration) {
9844
- clip.out = round33(clip.in + nextLen);
9972
+ clip.out = round34(clip.in + nextLen);
9845
9973
  } else {
9846
9974
  clip.out = clip.duration;
9847
9975
  clip.loop = true;
@@ -10086,7 +10214,7 @@ function applyTemplate(template, take, opts) {
10086
10214
  const skipped = [];
10087
10215
  for (const clip of template.overlays ?? []) {
10088
10216
  const next = structuredClone(clip);
10089
- next.start = Math.max(0, round34(clip.start + shift));
10217
+ next.start = Math.max(0, round35(clip.start + shift));
10090
10218
  next.from = from;
10091
10219
  const mine = own.get(clip.id);
10092
10220
  if (next.kind === "text") {
@@ -10115,7 +10243,7 @@ function applyTemplate(template, take, opts) {
10115
10243
  for (const clip of template.objects ?? []) {
10116
10244
  const next = structuredClone(clip);
10117
10245
  if (next.span)
10118
- next.span.start = Math.max(0, round34(next.span.start + shift));
10246
+ next.span.start = Math.max(0, round35(next.span.start + shift));
10119
10247
  next.from = from;
10120
10248
  objects.push(next);
10121
10249
  }
@@ -10125,7 +10253,7 @@ function applyTemplate(template, take, opts) {
10125
10253
  );
10126
10254
  for (const clip of template.audio ?? []) {
10127
10255
  const next = structuredClone(clip);
10128
- next.start = Math.max(0, round34(clip.start + shift));
10256
+ next.start = Math.max(0, round35(clip.start + shift));
10129
10257
  next.from = from;
10130
10258
  audio.push(next);
10131
10259
  }
@@ -10146,7 +10274,7 @@ function applyTemplate(template, take, opts) {
10146
10274
  }
10147
10275
  return { doc, notes };
10148
10276
  }
10149
- var round34 = (v) => Math.round(v * 1e3) / 1e3;
10277
+ var round35 = (v) => Math.round(v * 1e3) / 1e3;
10150
10278
 
10151
10279
  // src/digest/geometry.ts
10152
10280
  var CROP_MIN_FRAC = 0.25;
@@ -10329,15 +10457,15 @@ function outputRangeToSource(doc, t0, t1) {
10329
10457
  srcOut: mapTime2(rated, Math.max(0, t1))
10330
10458
  };
10331
10459
  }
10332
- var EPS = 1e-6;
10460
+ var EPS2 = 1e-6;
10333
10461
  var MIN_REMAINDER = 0.25;
10334
10462
  var MIN_SEGMENT = 0.05;
10335
10463
  function setSpeedInRange(spans, srcIn, srcOut, rate) {
10336
- if (srcOut - srcIn < EPS) return [...spans];
10464
+ if (srcOut - srcIn < EPS2) return [...spans];
10337
10465
  const kept = [];
10338
10466
  const used = new Set(spans.map((s) => s.id));
10339
10467
  for (const s of spans) {
10340
- if (s.out <= srcIn + EPS || s.in >= srcOut - EPS) {
10468
+ if (s.out <= srcIn + EPS2 || s.in >= srcOut - EPS2) {
10341
10469
  kept.push(s);
10342
10470
  continue;
10343
10471
  }
@@ -10362,12 +10490,12 @@ function setSpeedInRange(spans, srcIn, srcOut, rate) {
10362
10490
  return kept.sort((a, b) => a.in - b.in);
10363
10491
  }
10364
10492
  function removeSpeedInRange(spans, srcIn, srcOut) {
10365
- return spans.filter((s) => s.out <= srcIn + EPS || s.in >= srcOut - EPS);
10493
+ return spans.filter((s) => s.out <= srcIn + EPS2 || s.in >= srcOut - EPS2);
10366
10494
  }
10367
10495
  function removeSourceRange(segments, srcIn, srcOut) {
10368
10496
  const next = [];
10369
10497
  for (const seg of segments) {
10370
- if (seg.out <= srcIn + EPS || seg.in >= srcOut - EPS) {
10498
+ if (seg.out <= srcIn + EPS2 || seg.in >= srcOut - EPS2) {
10371
10499
  next.push(seg);
10372
10500
  continue;
10373
10501
  }
@@ -10379,11 +10507,11 @@ function removeSourceRange(segments, srcIn, srcOut) {
10379
10507
  }
10380
10508
  function zoomSpanForRange(zoom, srcIn, srcOut) {
10381
10509
  let start = srcIn;
10382
- const covering = zoom.find((z) => z.in <= start + EPS && z.out > start + EPS);
10510
+ const covering = zoom.find((z) => z.in <= start + EPS2 && z.out > start + EPS2);
10383
10511
  if (covering) start = covering.out;
10384
10512
  let end = srcOut;
10385
10513
  for (const z of zoom) {
10386
- if (z.in >= start - EPS && z.in < end) end = z.in;
10514
+ if (z.in >= start - EPS2 && z.in < end) end = z.in;
10387
10515
  }
10388
10516
  if (end - start < ZOOM_SPAN_MIN) return null;
10389
10517
  const used = new Set(zoom.map((z) => z.id));
@@ -10792,7 +10920,7 @@ function applyLook(frame, look, size, video, placement) {
10792
10920
  import {
10793
10921
  mapTime as mapTime3,
10794
10922
  removeSegment,
10795
- segmentRate as segmentRate3,
10923
+ segmentRate as segmentRate4,
10796
10924
  sourceToTimeline as sourceToTimeline2,
10797
10925
  splitBySpeed as splitBySpeed4,
10798
10926
  totalDuration as totalDuration7,
@@ -11533,7 +11661,7 @@ var micLane = {
11533
11661
  var spanDefaultLen = (sourceDuration) => Math.max(1, sourceDuration * 0.05);
11534
11662
  function rateAt(rated, srcT) {
11535
11663
  for (const p of rated)
11536
- if (srcT >= p.in - 1e-9 && srcT < p.out + 1e-9) return segmentRate3(p);
11664
+ if (srcT >= p.in - 1e-9 && srcT < p.out + 1e-9) return segmentRate4(p);
11537
11665
  return 1;
11538
11666
  }
11539
11667
  function clampToKept(kept, newIn, len) {
@@ -11563,7 +11691,7 @@ var speedLane = {
11563
11691
  let start = null;
11564
11692
  let end = 0;
11565
11693
  for (const p of rated) {
11566
- const len = Math.max(0, p.out - p.in) / segmentRate3(p);
11694
+ const len = Math.max(0, p.out - p.in) / segmentRate4(p);
11567
11695
  if (p.rate === sp.rate && p.in >= sp.in - 1e-9 && p.out <= sp.out + 1e-9) {
11568
11696
  if (start === null) start = acc;
11569
11697
  end = acc + len;
@@ -12193,6 +12321,7 @@ export {
12193
12321
  DRAG_FIT_LEVEL,
12194
12322
  DRAG_MIN_S,
12195
12323
  DRAG_MIN_TRAVEL_FRAC,
12324
+ ELAPSED_PLACEHOLDER,
12196
12325
  END_CARD_FROM,
12197
12326
  END_CARD_RECEDE,
12198
12327
  END_CARD_SECONDS,
@@ -12303,6 +12432,7 @@ export {
12303
12432
  ambientDrift,
12304
12433
  anchorKindOf,
12305
12434
  anchorSourceDuration,
12435
+ anchoredClip,
12306
12436
  animStep,
12307
12437
  applyLook,
12308
12438
  applyTemplate,
@@ -12379,6 +12509,7 @@ export {
12379
12509
  dropTemplate,
12380
12510
  duckCurve,
12381
12511
  effectiveSegments,
12512
+ elapsedFor,
12382
12513
  endCardClips,
12383
12514
  enterKey,
12384
12515
  enterOf,
@@ -12398,9 +12529,11 @@ export {
12398
12529
  focusBounds,
12399
12530
  focusForViewportCentre,
12400
12531
  followFocusEvents,
12532
+ formatElapsed,
12401
12533
  frameGeometry,
12402
12534
  freezeLane,
12403
12535
  freezeOutputExtents,
12536
+ hasElapsed,
12404
12537
  hexToRgb,
12405
12538
  hexToRgbTriplet,
12406
12539
  houseLook,
@@ -12516,7 +12649,9 @@ export {
12516
12649
  removeSourceRange,
12517
12650
  removeSpeedInRange,
12518
12651
  replaceRange,
12652
+ resolveAnchor,
12519
12653
  resolveExportSize,
12654
+ resolveOverlayAnchors,
12520
12655
  resolveOverlayBox,
12521
12656
  resolveOverlayStyle,
12522
12657
  resolvePins,
@@ -12540,8 +12675,10 @@ export {
12540
12675
  smoothCursor,
12541
12676
  snapOffset,
12542
12677
  snapRunWeight,
12678
+ sourceMomentToOutput,
12543
12679
  spanOutputExtent,
12544
12680
  speedLane,
12681
+ stepElapsedMs,
12545
12682
  studioAudioPlan,
12546
12683
  studioEntry,
12547
12684
  studioLayerData,