@vosjs/studio-core 0.12.0 → 0.13.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
@@ -571,7 +571,7 @@ function entranceSeconds(e) {
571
571
  function exitSeconds(e) {
572
572
  if (!e || e.kind === "none") return 0;
573
573
  const house = e.kind === "fade" ? CARD_FADE_SECONDS : END_CARD_RECEDE;
574
- return Math.max(0.1, Math.min(3, e.seconds ?? house));
574
+ return Math.max(0.1, Math.min(8, e.seconds ?? house));
575
575
  }
576
576
  function placeFreezes(rated, freezes) {
577
577
  const marks = /* @__PURE__ */ new Map();
@@ -663,8 +663,9 @@ function prependEntrance(track, head) {
663
663
  return { keyframes: sortKeyframes([...head, ...rest]) };
664
664
  }
665
665
  function exitPose(e) {
666
- return e?.kind === "fade" ? [1, 0, 0] : [0.9, -0.02, 0.22];
666
+ return e?.kind === "fade" ? [1, 0, 0] : [0.9, -0.02, 0];
667
667
  }
668
+ var RECEDE_MID = [0.91, -0.018, 0.8];
668
669
  function cardPoseTrack(enter, exit, footageEnd, outputEnd2 = footageEnd) {
669
670
  const keyframes = [];
670
671
  const s = entranceSeconds(enter);
@@ -674,16 +675,36 @@ function cardPoseTrack(enter, exit, footageEnd, outputEnd2 = footageEnd) {
674
675
  keyframes.push({ t: s, value: [1, 0, 1], ease: "power3.out" });
675
676
  }
676
677
  const x = exitSeconds(exit);
678
+ let last = [1, 0, 1];
677
679
  if (x > 0 && footageEnd > 0) {
678
- const room = outputEnd2 - footageEnd;
679
- const t0 = Math.max(s, room > 1e-6 ? footageEnd : footageEnd - x);
680
- const t1 = room > 1e-6 ? t0 + Math.min(x, Math.max(0.1, room)) : t0 + x;
680
+ const t0 = round3(Math.max(s, footageEnd - x));
681
+ const t1 = round3(Math.max(t0 + 0.05, footageEnd));
682
+ last = exitPose(exit);
681
683
  keyframes.push({ t: t0, value: [1, 0, 1], ease: "none" });
682
- keyframes.push({ t: t1, value: exitPose(exit), ease: "power2.out" });
684
+ if (exit?.kind !== "fade") {
685
+ keyframes.push({
686
+ t: round3(t0 + (t1 - t0) * 0.45),
687
+ value: [...RECEDE_MID],
688
+ ease: "power2.out"
689
+ });
690
+ keyframes.push({ t: t1, value: last, ease: "power1.in" });
691
+ } else {
692
+ keyframes.push({ t: t1, value: last, ease: "power2.out" });
693
+ }
694
+ }
695
+ if (outputEnd2 > footageEnd + 1e-6 && footageEnd > 0) {
696
+ if (!keyframes.some((k) => Math.abs(k.t - footageEnd) < 1e-9))
697
+ keyframes.push({ t: footageEnd, value: [...last], ease: "none" });
698
+ keyframes.push({
699
+ t: round3(footageEnd + 1e-3),
700
+ value: [last[0], last[1], 0],
701
+ ease: "none"
702
+ });
683
703
  }
684
704
  if (!keyframes.length) return void 0;
685
705
  return { keyframes: sortKeyframes(keyframes) };
686
706
  }
707
+ var round3 = (v) => Math.round(v * 1e3) / 1e3;
687
708
  function endCardClips(card, doc, endStart) {
688
709
  const seconds = Math.max(1, Math.min(8, card.seconds ?? END_CARD_SECONDS));
689
710
  const anim = { enter: "rise", exit: "none" };
@@ -822,18 +843,25 @@ function migrateMotion(doc) {
822
843
  const card = doc.endCard;
823
844
  if (card && segments.length) {
824
845
  const seconds = Math.max(1, Math.min(8, card.seconds ?? END_CARD_SECONDS));
846
+ const segsNow = out.segments ?? segments;
847
+ const lastSeg = segsNow.at(-1);
848
+ const freezesNow = docFreezes({ segments: segsNow, freeze: out.freeze });
825
849
  const rated = withFreezes(
826
- splitBySpeed(segments, doc.speed ?? []),
827
- docFreezes(doc)
850
+ splitBySpeed(segsNow, doc.speed ?? []),
851
+ freezesNow
828
852
  );
829
853
  const endStart = totalDuration(rated);
854
+ const taken = new Set(freezesNow.map((f) => f.id));
855
+ let n = 0;
856
+ while (taken.has(`f${n}`)) n++;
857
+ out.freeze = [
858
+ ...freezesNow,
859
+ { id: `f${n}`, at: lastSeg.out, seconds, from: END_CARD_FROM }
860
+ ].sort((a, b) => a.at - b.at);
830
861
  nextOverlays = [...nextOverlays, ...endCardClips(card, doc, endStart)];
831
862
  nextFrame.anim = {
832
863
  ...nextFrame.anim ?? {},
833
- exit: {
834
- kind: "recede",
835
- seconds: Math.min(END_CARD_RECEDE, seconds * 0.4)
836
- }
864
+ exit: { kind: "recede", seconds }
837
865
  };
838
866
  }
839
867
  delete out.endCard;
@@ -1910,14 +1938,14 @@ function resolveOverlayFx(clip, clipDuration) {
1910
1938
  const maxTotal = Math.max(dur, clipDuration * 0.9);
1911
1939
  st = Math.min(st, Math.max(0, (maxTotal - dur) / (upperN - 1)));
1912
1940
  }
1913
- const round11 = (v) => Math.round(v * 1e4) / 1e4;
1941
+ const round12 = (v) => Math.round(v * 1e4) / 1e4;
1914
1942
  return {
1915
1943
  k: spec.fx,
1916
1944
  u: unit,
1917
1945
  d: FX_DIR[spec.direction ?? "forward"] ?? 0,
1918
- st: round11(st),
1919
- dur: round11(dur),
1920
- tt: round11(st * (upperN - 1) + dur),
1946
+ st: round12(st),
1947
+ dur: round12(dur),
1948
+ tt: round12(st * (upperN - 1) + dur),
1921
1949
  units,
1922
1950
  n
1923
1951
  };
@@ -2132,7 +2160,7 @@ var CURSOR_IDLE_HOLD = 1;
2132
2160
  var CURSOR_IDLE_FADE_OUT = 0.35;
2133
2161
  var CURSOR_IDLE_FADE_IN = 0.18;
2134
2162
  var CURSOR_IDLE_EPS_FRAC = 25e-4;
2135
- var round3 = (n) => Math.round(n * 1e3) / 1e3;
2163
+ var round4 = (n) => Math.round(n * 1e3) / 1e3;
2136
2164
  function cursorIdleFade(track, o) {
2137
2165
  const moves = track.filter((e) => e.type === "move" || e.type === "down" || e.type === "up").map((e) => ({ t: e.t / 1e3, x: e.x, y: e.y }));
2138
2166
  if (moves.length === 0) return [];
@@ -2184,7 +2212,7 @@ function emit(keys, s, e) {
2184
2212
  push(keys, e + CURSOR_IDLE_FADE_IN, 1);
2185
2213
  }
2186
2214
  function push(keys, t, a) {
2187
- const rt = round3(t);
2215
+ const rt = round4(t);
2188
2216
  if (keys.length > 0 && rt <= keys[keys.length - 1].t) {
2189
2217
  keys[keys.length - 1].a = a;
2190
2218
  return;
@@ -3242,7 +3270,7 @@ function gatedRect(rect, x, y, space) {
3242
3270
  return null;
3243
3271
  const m = RECT_CONTAIN_MARGIN;
3244
3272
  const inside = x >= rect.x - m && x <= rect.x + rect.w + m && y >= rect.y - m && y <= rect.y + rect.h + m;
3245
- return inside ? [round4(rect.x), round4(rect.y), round4(rect.w), round4(rect.h)] : null;
3273
+ return inside ? [round5(rect.x), round5(rect.y), round5(rect.w), round5(rect.h)] : null;
3246
3274
  }
3247
3275
  function extractClicks(track, segments, opts) {
3248
3276
  const out = [];
@@ -3267,11 +3295,11 @@ function extractClicks(track, segments, opts) {
3267
3295
  const upSrc = st + Math.max(pressLen, 0.02);
3268
3296
  const up = keptOutputEnd(segments, st, upSrc) ?? ot + CLICK_SYNTH_RELEASE;
3269
3297
  const click = {
3270
- ot: round4(ot),
3271
- up: round4(Math.max(up, ot + 0.02)),
3272
- st: round4(st),
3273
- x: round4(e.x),
3274
- y: round4(e.y),
3298
+ ot: round5(ot),
3299
+ up: round5(Math.max(up, ot + 0.02)),
3300
+ st: round5(st),
3301
+ x: round5(e.x),
3302
+ y: round5(e.y),
3275
3303
  b: button
3276
3304
  };
3277
3305
  if (opts.rects && e.rect) {
@@ -3290,7 +3318,7 @@ function hexToRgbTriplet(hex) {
3290
3318
  const n = parseInt(h, 16);
3291
3319
  return [n >> 16, n >> 8 & 255, n & 255];
3292
3320
  }
3293
- function round4(v) {
3321
+ function round5(v) {
3294
3322
  return Math.round(v * 1e3) / 1e3;
3295
3323
  }
3296
3324
 
@@ -3353,7 +3381,7 @@ function trackEmitter() {
3353
3381
  if (tt <= prev.t + 1e-6 && sameVec(prev.value, value)) return prev.t;
3354
3382
  if (tt <= prev.t + 1e-6) tt = prev.t + 1e-3;
3355
3383
  }
3356
- keyframes.push({ t: round5(tt), value: value.map(round5), ease });
3384
+ keyframes.push({ t: round6(tt), value: value.map(round6), ease });
3357
3385
  return tt;
3358
3386
  };
3359
3387
  return { keyframes, push: push2 };
@@ -4849,10 +4877,13 @@ var ON_FRAME = `(ctx, content, dt) => {
4849
4877
  card.mesh.scale.x = cpv[0]; card.mesh.scale.y = cpv[0]
4850
4878
  card.mesh.position.y = cpv[1] * cpH
4851
4879
  if (card.mesh.material) card.mesh.material.opacity = cpv[2]
4852
- } 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))) {
4880
+ // Gone past its clip: the mesh leaves the scene, not just its paint.
4881
+ card.mesh.visible = cpv[2] > 0.001
4882
+ } 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)) {
4853
4883
  card.mesh.scale.x = 1; card.mesh.scale.y = 1
4854
4884
  card.mesh.position.y = 0
4855
4885
  if (card.mesh.material) card.mesh.material.opacity = 1
4886
+ card.mesh.visible = true
4856
4887
  }
4857
4888
  var tilted = rx * rx + ry * ry > 1e-6
4858
4889
  if (card.texture && card.texture.generateMipmaps !== tilted) {
@@ -4884,14 +4915,14 @@ function studioLayerData(layers, duration) {
4884
4915
  // needs a decoded recording, which a sync lowering can't produce.
4885
4916
  audio: (layers.audio ?? []).map((c) => ({
4886
4917
  key: c.key,
4887
- start: round5(c.start),
4888
- in: round5(c.in),
4889
- out: round5(c.out),
4890
- gain: round5(c.gain),
4918
+ start: round6(c.start),
4919
+ in: round6(c.in),
4920
+ out: round6(c.out),
4921
+ gain: round6(c.gain),
4891
4922
  loop: !!c.loop,
4892
- len: round5(clipLength(c)),
4923
+ len: round6(clipLength(c)),
4893
4924
  duck: !!c.duck,
4894
- env: clipEnvelope(c).map((p) => ({ t: round5(p.t), g: round5(p.g) }))
4925
+ env: clipEnvelope(c).map((p) => ({ t: round6(p.t), g: round6(p.g) }))
4895
4926
  })),
4896
4927
  // Object clips: numbers resolved HERE; shapes are the drafted engine spec.
4897
4928
  ...layers.objects && layers.objects.length ? {
@@ -4904,17 +4935,17 @@ function studioLayerData(layers, duration) {
4904
4935
  } : o.asset.kind === "text3d" ? resolveText3dAsset(o.asset) : { kind: "gltf", key: o.asset.key },
4905
4936
  ...o.span ? {
4906
4937
  span: {
4907
- start: round5(o.span.start),
4908
- duration: round5(o.span.duration)
4938
+ start: round6(o.span.start),
4939
+ duration: round6(o.span.duration)
4909
4940
  }
4910
4941
  } : {},
4911
- x: round5(o.transform3d.x),
4912
- y: round5(o.transform3d.y),
4913
- z: round5(o.transform3d.z),
4914
- rx: round5(o.transform3d.rx),
4915
- ry: round5(o.transform3d.ry),
4916
- rz: round5(o.transform3d.rz),
4917
- scale: round5(o.transform3d.scale || OBJECT_DEFAULT_SCALE),
4942
+ x: round6(o.transform3d.x),
4943
+ y: round6(o.transform3d.y),
4944
+ z: round6(o.transform3d.z),
4945
+ rx: round6(o.transform3d.rx),
4946
+ ry: round6(o.transform3d.ry),
4947
+ rz: round6(o.transform3d.rz),
4948
+ scale: round6(o.transform3d.scale || OBJECT_DEFAULT_SCALE),
4918
4949
  anim: idleKey(o),
4919
4950
  // Pose keyframes: clip-local [x,y,z,rx,ry,rz,scale] track,
4920
4951
  // sampled at t − span.start. Omitted when absent — parity.
@@ -4941,12 +4972,12 @@ function studioLayerData(layers, duration) {
4941
4972
  const base = {
4942
4973
  id: o.id,
4943
4974
  kind: o.kind,
4944
- start: round5(o.start),
4945
- dur: round5(Math.max(OVERLAY_MIN_DURATION, o.duration)),
4946
- x: round5(o.transform.x),
4947
- y: round5(o.transform.y),
4948
- scale: round5(o.transform.scale || 1),
4949
- rot: round5(o.transform.rotation || 0),
4975
+ start: round6(o.start),
4976
+ dur: round6(Math.max(OVERLAY_MIN_DURATION, o.duration)),
4977
+ x: round6(o.transform.x),
4978
+ y: round6(o.transform.y),
4979
+ scale: round6(o.transform.scale || 1),
4980
+ rot: round6(o.transform.rotation || 0),
4950
4981
  enter: enterKey(o),
4951
4982
  exit: exitKey(o),
4952
4983
  // Pose keyframes: a CLIP-LOCAL [x, y, scale, rot, opacity]
@@ -4967,7 +4998,7 @@ function studioLayerData(layers, duration) {
4967
4998
  return {
4968
4999
  ...base,
4969
5000
  key: o.key,
4970
- w: round5(o.width ?? OVERLAY_MEDIA_DEFAULT_WIDTH),
5001
+ w: round6(o.width ?? OVERLAY_MEDIA_DEFAULT_WIDTH),
4971
5002
  radius: o.radius ?? OVERLAY_MEDIA_DEFAULT_RADIUS,
4972
5003
  opacity: o.opacity ?? 1,
4973
5004
  loop: !!o.loop,
@@ -4977,7 +5008,7 @@ function studioLayerData(layers, duration) {
4977
5008
  ...o.shadow ? { shadow: o.shadow } : {},
4978
5009
  ...o.border && o.border.width > 0 ? {
4979
5010
  border: {
4980
- width: round5(o.border.width),
5011
+ width: round6(o.border.width),
4981
5012
  color: o.border.color
4982
5013
  }
4983
5014
  } : {}
@@ -4997,11 +5028,11 @@ function studioLayerData(layers, duration) {
4997
5028
  // Style-v2 fields bake only when non-default (byte parity for
4998
5029
  // older docs); ON_FRAME reads them unconditionally.
4999
5030
  ...st.fontStyle === "italic" ? { sty: "italic" } : {},
5000
- ...o.maxWidth ? { mw: round5(o.maxWidth) } : {},
5001
- ...st.letterSpacing ? { ls: round5(st.letterSpacing) } : {},
5002
- ...st.lineHeight !== OVERLAY_LINE_HEIGHT ? { lh: round5(st.lineHeight) } : {},
5031
+ ...o.maxWidth ? { mw: round6(o.maxWidth) } : {},
5032
+ ...st.letterSpacing ? { ls: round6(st.letterSpacing) } : {},
5033
+ ...st.lineHeight !== OVERLAY_LINE_HEIGHT ? { lh: round6(st.lineHeight) } : {},
5003
5034
  ...st.align !== "center" ? { align: st.align } : {},
5004
- ...st.stroke ? { stroke: { c: st.stroke.color, w: round5(st.stroke.width) } } : {},
5035
+ ...st.stroke ? { stroke: { c: st.stroke.color, w: round6(st.stroke.width) } } : {},
5005
5036
  // Hosted face behind an override: ON_FRAME lazy-loads it so a
5006
5037
  // live family/weight edit paints without a LOAD (SETUP only
5007
5038
  // runs on cold load).
@@ -5014,10 +5045,10 @@ function studioLayerData(layers, duration) {
5014
5045
  ...bx ? {
5015
5046
  box: {
5016
5047
  c: bx.color,
5017
- o: round5(bx.opacity),
5018
- px: round5(bx.padX),
5019
- py: round5(bx.padY),
5020
- r: round5(bx.radius)
5048
+ o: round6(bx.opacity),
5049
+ px: round6(bx.padX),
5050
+ py: round6(bx.padY),
5051
+ r: round6(bx.radius)
5021
5052
  }
5022
5053
  } : {},
5023
5054
  // Entrance animation: segmentation + timing normalized
@@ -5092,16 +5123,16 @@ function lowerToComposition(input) {
5092
5123
  // Rated segments (speed spans pre-intersected): ON_FRAME's mapTime and the
5093
5124
  // export's audio splice read the rate straight off each segment.
5094
5125
  segments: rated.map((seg) => ({
5095
- in: round5(seg.in),
5096
- out: round5(seg.out),
5097
- ...seg.rate !== void 0 && seg.rate !== 1 ? { rate: round5(seg.rate) } : {}
5126
+ in: round6(seg.in),
5127
+ out: round6(seg.out),
5128
+ ...seg.rate !== void 0 && seg.rate !== 1 ? { rate: round6(seg.rate) } : {}
5098
5129
  })),
5099
5130
  frame: doc.frame,
5100
5131
  micGain: doc.micGain ?? 1,
5101
5132
  cursor: smoothed.map((p) => ({
5102
- t: round5(p.t),
5103
- x: round5(p.x),
5104
- y: round5(p.y)
5133
+ t: round6(p.t),
5134
+ x: round6(p.x),
5135
+ y: round6(p.y)
5105
5136
  })),
5106
5137
  cursorStyle: doc.cursor,
5107
5138
  cursorSpace: { w: doc.source.meta.width, h: doc.source.meta.height },
@@ -5186,7 +5217,7 @@ function clickFxData(doc, rated) {
5186
5217
  }
5187
5218
  };
5188
5219
  }
5189
- function round5(v) {
5220
+ function round6(v) {
5190
5221
  return Math.round(v * 1e3) / 1e3;
5191
5222
  }
5192
5223
 
@@ -5276,8 +5307,8 @@ function planAutoSpeed(track, opts) {
5276
5307
  accepted.sort((a, b) => a.in - b.in);
5277
5308
  return accepted.map((c, i) => ({
5278
5309
  id: `s${i}`,
5279
- in: round6(c.in),
5280
- out: round6(c.out),
5310
+ in: round7(c.in),
5311
+ out: round7(c.out),
5281
5312
  rate: clampSpeedRate(c.rate),
5282
5313
  source: "auto"
5283
5314
  }));
@@ -5330,7 +5361,7 @@ function collectRuns(evs, gap, minLen, emit2) {
5330
5361
  }
5331
5362
  flush();
5332
5363
  }
5333
- function round6(v) {
5364
+ function round7(v) {
5334
5365
  return Math.round(v * 1e3) / 1e3;
5335
5366
  }
5336
5367
 
@@ -5507,16 +5538,16 @@ function momentsFromDoc(doc, plan, opts = {}) {
5507
5538
  return {
5508
5539
  id: `m${String(i + 1).padStart(2, "0")}`,
5509
5540
  kind: d.kind,
5510
- source: { in: round7(d.in), out: round7(d.out) },
5511
- output: output ? { in: round7(output.start), out: round7(output.end) } : null,
5512
- at: d.at === null ? null : round7(d.at),
5513
- outputAt: outAt ? round7(outAt.start) : null,
5514
- focus: d.focus ? { cx: round7(d.focus.cx), cy: round7(d.focus.cy) } : null,
5541
+ source: { in: round8(d.in), out: round8(d.out) },
5542
+ output: output ? { in: round8(output.start), out: round8(output.end) } : null,
5543
+ at: d.at === null ? null : round8(d.at),
5544
+ outputAt: outAt ? round8(outAt.start) : null,
5545
+ focus: d.focus ? { cx: round8(d.focus.cx), cy: round8(d.focus.cy) } : null,
5515
5546
  rect: d.rect ? {
5516
- x: round7(d.rect.x),
5517
- y: round7(d.rect.y),
5518
- w: round7(d.rect.w),
5519
- h: round7(d.rect.h)
5547
+ x: round8(d.rect.x),
5548
+ y: round8(d.rect.y),
5549
+ w: round8(d.rect.w),
5550
+ h: round8(d.rect.h)
5520
5551
  } : null,
5521
5552
  ...d.clicks !== void 0 ? { clicks: d.clicks } : {},
5522
5553
  ...d.pings !== void 0 ? { pings: d.pings } : {},
@@ -5536,7 +5567,7 @@ function activityOf(bins, d) {
5536
5567
  sum += bins[i];
5537
5568
  n++;
5538
5569
  }
5539
- return n ? round7(sum / n) : null;
5570
+ return n ? round8(sum / n) : null;
5540
5571
  }
5541
5572
  function saidOver(transcript, d) {
5542
5573
  if (!transcript?.length) return null;
@@ -5545,7 +5576,7 @@ function saidOver(transcript, d) {
5545
5576
  const text = transcript.filter((s) => s.start < hi && s.end > lo).map((s) => s.text.trim()).filter(Boolean).join(" ");
5546
5577
  return text || null;
5547
5578
  }
5548
- function round7(v) {
5579
+ function round8(v) {
5549
5580
  return Math.round(v * 1e3) / 1e3;
5550
5581
  }
5551
5582
 
@@ -5833,12 +5864,14 @@ function clipsFrom(doc, from) {
5833
5864
  }
5834
5865
  function dropTemplate(doc, from) {
5835
5866
  const has = clipsFrom(doc, from);
5836
- if (!has.overlays.length && !has.objects.length && !has.audio.length)
5867
+ const freezes = (doc.freeze ?? []).some((f) => f.from === from);
5868
+ if (!has.overlays.length && !has.objects.length && !has.audio.length && !freezes)
5837
5869
  return doc;
5838
5870
  const out = { ...doc };
5839
5871
  if (doc.overlays) out.overlays = doc.overlays.filter((o) => o.from !== from);
5840
5872
  if (doc.objects) out.objects = doc.objects.filter((o) => o.from !== from);
5841
5873
  out.audio = doc.audio.filter((a) => a.from !== from);
5874
+ if (doc.freeze) out.freeze = doc.freeze.filter((f) => f.from !== from);
5842
5875
  return out;
5843
5876
  }
5844
5877
  function applyTemplate(template, take, opts) {
@@ -5846,8 +5879,22 @@ function applyTemplate(template, take, opts) {
5846
5879
  const notes = [];
5847
5880
  const base = dropTemplate(take, from);
5848
5881
  const doc = structuredClone(base);
5882
+ const tail = trailingFreeze(template);
5883
+ const lastSeg = doc.segments.at(-1);
5884
+ if (tail && opts.at === "end" && lastSeg) {
5885
+ const own2 = (doc.freeze ?? []).filter(
5886
+ (f) => Math.abs(f.at - lastSeg.out) > 1e-9
5887
+ );
5888
+ const taken = new Set(own2.map((f) => f.id));
5889
+ let n = 0;
5890
+ while (taken.has(`f${n}`)) n++;
5891
+ doc.freeze = [
5892
+ ...own2,
5893
+ { id: `f${n}`, at: lastSeg.out, seconds: tail.seconds, from }
5894
+ ].sort((a, b) => a.at - b.at);
5895
+ }
5849
5896
  const templateEnd = totalDuration4(ratedSegments(template));
5850
- const takeEnd = totalDuration4(ratedSegments(take));
5897
+ const takeEnd = totalDuration4(ratedSegments(doc));
5851
5898
  const shift = shiftBy(opts.at, templateEnd, takeEnd);
5852
5899
  const own = new Map((take.overlays ?? []).map((o) => [o.id, o]));
5853
5900
  const overlays = (doc.overlays ?? []).filter(
@@ -6011,8 +6058,8 @@ function buildDigest(input) {
6011
6058
  return {
6012
6059
  digestVersion: DIGEST_VERSION,
6013
6060
  take: {
6014
- sourceDuration: round8(meta.durationMs / 1e3),
6015
- outputDuration: round8(input.outputDuration),
6061
+ sourceDuration: round9(meta.durationMs / 1e3),
6062
+ outputDuration: round9(input.outputDuration),
6016
6063
  width: meta.width,
6017
6064
  height: meta.height,
6018
6065
  captureWidth: meta.captureWidth ?? null,
@@ -6061,7 +6108,7 @@ function buildDigest(input) {
6061
6108
  function outputDurationOf(doc) {
6062
6109
  return docOutputDuration(doc);
6063
6110
  }
6064
- function round8(v) {
6111
+ function round9(v) {
6065
6112
  return Math.round(v * 1e3) / 1e3;
6066
6113
  }
6067
6114
 
@@ -6087,20 +6134,20 @@ function setSpeedInRange(spans, srcIn, srcOut, rate) {
6087
6134
  continue;
6088
6135
  }
6089
6136
  if (srcIn - s.in >= MIN_REMAINDER)
6090
- kept.push({ ...s, out: round9(srcIn), source: "manual" });
6137
+ kept.push({ ...s, out: round10(srcIn), source: "manual" });
6091
6138
  if (s.out - srcOut >= MIN_REMAINDER)
6092
6139
  kept.push({
6093
6140
  ...s,
6094
6141
  id: mintId(used),
6095
- in: round9(srcOut),
6142
+ in: round10(srcOut),
6096
6143
  source: "manual"
6097
6144
  });
6098
6145
  }
6099
6146
  if (rate != null)
6100
6147
  kept.push({
6101
6148
  id: mintId(used),
6102
- in: round9(srcIn),
6103
- out: round9(srcOut),
6149
+ in: round10(srcIn),
6150
+ out: round10(srcOut),
6104
6151
  rate: clampSpeedRate(rate),
6105
6152
  source: "manual"
6106
6153
  });
@@ -6116,9 +6163,9 @@ function removeSourceRange(segments, srcIn, srcOut) {
6116
6163
  next.push(seg);
6117
6164
  continue;
6118
6165
  }
6119
- if (srcIn - seg.in >= MIN_SEGMENT) next.push({ ...seg, out: round9(srcIn) });
6166
+ if (srcIn - seg.in >= MIN_SEGMENT) next.push({ ...seg, out: round10(srcIn) });
6120
6167
  if (seg.out - srcOut >= MIN_SEGMENT)
6121
- next.push({ ...seg, in: round9(srcOut) });
6168
+ next.push({ ...seg, in: round10(srcOut) });
6122
6169
  }
6123
6170
  return next.length ? next : [...segments];
6124
6171
  }
@@ -6136,8 +6183,8 @@ function zoomSpanForRange(zoom, srcIn, srcOut) {
6136
6183
  while (used.has(`u${n}`)) n++;
6137
6184
  return {
6138
6185
  id: `u${n}`,
6139
- in: round9(start),
6140
- out: round9(end),
6186
+ in: round10(start),
6187
+ out: round10(end),
6141
6188
  level: DEFAULT_ZOOM_LEVEL,
6142
6189
  cx: 0.5,
6143
6190
  cy: 0.5,
@@ -6151,7 +6198,7 @@ function mintId(used) {
6151
6198
  used.add(id);
6152
6199
  return id;
6153
6200
  }
6154
- function round9(v) {
6201
+ function round10(v) {
6155
6202
  return Math.round(v * 1e3) / 1e3;
6156
6203
  }
6157
6204
 
@@ -6822,8 +6869,8 @@ var videoLane = {
6822
6869
  return segments.map((s, i) => ({
6823
6870
  id: `seg-${i}`,
6824
6871
  kind: "clip",
6825
- t: round10(starts[i]),
6826
- duration: round10(
6872
+ t: round11(starts[i]),
6873
+ duration: round11(
6827
6874
  outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i))
6828
6875
  )
6829
6876
  }));
@@ -6906,7 +6953,7 @@ var videoLane = {
6906
6953
  if (local >= m.t - 1e-9 && local <= m.t + m.duration + 1e-9)
6907
6954
  return null;
6908
6955
  }
6909
- const sourceT = round10(
6956
+ const sourceT = round11(
6910
6957
  mapTime2(segmentPieces(s, speeds, freezes, leading), local)
6911
6958
  );
6912
6959
  if (sourceT - s.in < 0.05 || s.out - sourceT < 0.05) return null;
@@ -6947,7 +6994,7 @@ var videoLane = {
6947
6994
  n === 1 || !contiguous(segments, n - 1)
6948
6995
  )
6949
6996
  ] : []
6950
- ].map(round10);
6997
+ ].map(round11);
6951
6998
  }
6952
6999
  };
6953
7000
  function nextFreezeId(doc) {
@@ -6974,8 +7021,8 @@ var freezeLane = {
6974
7021
  {
6975
7022
  id: f.id,
6976
7023
  kind: "clip",
6977
- t: round10(m.t),
6978
- duration: round10(m.duration),
7024
+ t: round11(m.t),
7025
+ duration: round11(m.duration),
6979
7026
  label: `${trimZeros(f.seconds)}s`
6980
7027
  }
6981
7028
  ] : [];
@@ -6997,7 +7044,7 @@ var freezeLane = {
6997
7044
  if (g.t >= m.t - 1e-9 && g.t <= m.t + m.duration + 1e-9) return null;
6998
7045
  }
6999
7046
  if (g.t > totalDuration6(rated) + 1e-9) return null;
7000
- const srcT = round10(mapTime2(rated, Math.max(0, g.t)));
7047
+ const srcT = round11(mapTime2(rated, Math.max(0, g.t)));
7001
7048
  if (!kept(srcT) || !clear(srcT)) return null;
7002
7049
  const id = nextFreezeId(doc);
7003
7050
  return (d) => {
@@ -7033,7 +7080,7 @@ var freezeLane = {
7033
7080
  const marks = freezeOutputExtents(splitBySpeed3(segs, speeds), freezes);
7034
7081
  const mine = marks.get(f.id);
7035
7082
  const endOut = mine ? mine.t + mine.duration : null;
7036
- const srcT = round10(
7083
+ const srcT = round11(
7037
7084
  Math.min(sourceDuration, mapTime2(base, Math.max(0, g.t)))
7038
7085
  );
7039
7086
  if (!kept(srcT) || !clear(srcT, f.id)) return null;
@@ -7093,8 +7140,8 @@ var zoomLane = {
7093
7140
  {
7094
7141
  id: z.id,
7095
7142
  kind: "clip",
7096
- t: round10(ext.start),
7097
- duration: round10(ext.end - ext.start),
7143
+ t: round11(ext.start),
7144
+ duration: round11(ext.end - ext.start),
7098
7145
  label: `${z.level.toFixed(2)}\xD7`
7099
7146
  }
7100
7147
  ];
@@ -7118,8 +7165,8 @@ var zoomLane = {
7118
7165
  ...d.zoom,
7119
7166
  {
7120
7167
  id,
7121
- in: round10(srcT),
7122
- out: round10(srcT + len),
7168
+ in: round11(srcT),
7169
+ out: round11(srcT + len),
7123
7170
  level: DEFAULT_ZOOM_LEVEL,
7124
7171
  cx,
7125
7172
  cy,
@@ -7150,8 +7197,8 @@ var zoomLane = {
7150
7197
  return (d) => {
7151
7198
  const z = d.zoom.find((x) => x.id === g.id);
7152
7199
  if (!z) return;
7153
- z.in = round10(newIn);
7154
- z.out = round10(newIn + len);
7200
+ z.in = round11(newIn);
7201
+ z.out = round11(newIn + len);
7155
7202
  z.source = "manual";
7156
7203
  d.zoom.sort((a, b) => a.in - b.in);
7157
7204
  };
@@ -7180,8 +7227,8 @@ var zoomLane = {
7180
7227
  return (d) => {
7181
7228
  const z = d.zoom.find((x) => x.id === g.id);
7182
7229
  if (!z) return;
7183
- z.in = round10(next.in);
7184
- z.out = round10(next.out);
7230
+ z.in = round11(next.in);
7231
+ z.out = round11(next.out);
7185
7232
  z.source = "manual";
7186
7233
  };
7187
7234
  }
@@ -7203,8 +7250,8 @@ function cursorFocusAt(doc, srcT) {
7203
7250
  if (Math.abs(e.t - ms) < Math.abs(best.t - ms)) best = e;
7204
7251
  const clamp015 = (v) => Math.max(0, Math.min(1, v));
7205
7252
  return {
7206
- cx: round10(clamp015(best.x / width)),
7207
- cy: round10(clamp015(best.y / height))
7253
+ cx: round11(clamp015(best.x / width)),
7254
+ cy: round11(clamp015(best.y / height))
7208
7255
  };
7209
7256
  }
7210
7257
  var tiltLane = {
@@ -7218,8 +7265,8 @@ var tiltLane = {
7218
7265
  {
7219
7266
  id: z.id,
7220
7267
  kind: "clip",
7221
- t: round10(ext.start),
7222
- duration: round10(ext.end - ext.start),
7268
+ t: round11(ext.start),
7269
+ duration: round11(ext.end - ext.start),
7223
7270
  label: `${formatDeg(z.rx)}\xB0/${formatDeg(z.ry)}\xB0`
7224
7271
  }
7225
7272
  ];
@@ -7242,8 +7289,8 @@ var tiltLane = {
7242
7289
  ...d.tilt ?? [],
7243
7290
  {
7244
7291
  id,
7245
- in: round10(srcT),
7246
- out: round10(srcT + len),
7292
+ in: round11(srcT),
7293
+ out: round11(srcT + len),
7247
7294
  rx: DEFAULT_TILT_POSE.rx,
7248
7295
  ry: DEFAULT_TILT_POSE.ry,
7249
7296
  source: "manual"
@@ -7273,8 +7320,8 @@ var tiltLane = {
7273
7320
  return (d) => {
7274
7321
  const z = (d.tilt ?? []).find((x) => x.id === g.id);
7275
7322
  if (!z || !d.tilt) return;
7276
- z.in = round10(newIn);
7277
- z.out = round10(newIn + len);
7323
+ z.in = round11(newIn);
7324
+ z.out = round11(newIn + len);
7278
7325
  z.source = "manual";
7279
7326
  d.tilt.sort((a, b) => a.in - b.in);
7280
7327
  };
@@ -7303,8 +7350,8 @@ var tiltLane = {
7303
7350
  return (d) => {
7304
7351
  const z = (d.tilt ?? []).find((x) => x.id === g.id);
7305
7352
  if (!z) return;
7306
- z.in = round10(next.in);
7307
- z.out = round10(next.out);
7353
+ z.in = round11(next.in);
7354
+ z.out = round11(next.out);
7308
7355
  z.source = "manual";
7309
7356
  };
7310
7357
  }
@@ -7331,8 +7378,8 @@ var camMoveLane = {
7331
7378
  {
7332
7379
  id: z.id,
7333
7380
  kind: "clip",
7334
- t: round10(ext.start),
7335
- duration: round10(ext.end - ext.start),
7381
+ t: round11(ext.start),
7382
+ duration: round11(ext.end - ext.start),
7336
7383
  label: z.size != null ? `${Math.round(z.size * 100)}%` : "Move"
7337
7384
  }
7338
7385
  ];
@@ -7356,8 +7403,8 @@ var camMoveLane = {
7356
7403
  ...d.camMotion ?? [],
7357
7404
  {
7358
7405
  id,
7359
- in: round10(srcT),
7360
- out: round10(srcT + len),
7406
+ in: round11(srcT),
7407
+ out: round11(srcT + len),
7361
7408
  ...DEFAULT_CAM_POSE,
7362
7409
  source: "manual"
7363
7410
  }
@@ -7386,8 +7433,8 @@ var camMoveLane = {
7386
7433
  return (d) => {
7387
7434
  const z = (d.camMotion ?? []).find((x) => x.id === g.id);
7388
7435
  if (!z || !d.camMotion) return;
7389
- z.in = round10(newIn);
7390
- z.out = round10(newIn + len);
7436
+ z.in = round11(newIn);
7437
+ z.out = round11(newIn + len);
7391
7438
  z.source = "manual";
7392
7439
  d.camMotion.sort((a, b) => a.in - b.in);
7393
7440
  };
@@ -7416,8 +7463,8 @@ var camMoveLane = {
7416
7463
  return (d) => {
7417
7464
  const z = (d.camMotion ?? []).find((x) => x.id === g.id);
7418
7465
  if (!z) return;
7419
- z.in = round10(next.in);
7420
- z.out = round10(next.out);
7466
+ z.in = round11(next.in);
7467
+ z.out = round11(next.out);
7421
7468
  z.source = "manual";
7422
7469
  };
7423
7470
  }
@@ -7450,10 +7497,10 @@ var camLane = {
7450
7497
  items.push({
7451
7498
  id: `cam-${i}`,
7452
7499
  kind: "clip",
7453
- t: round10(
7500
+ t: round11(
7454
7501
  starts[i] + outputLen({ in: s.in, out: a }, speeds, freezes, leading)
7455
7502
  ),
7456
- duration: round10(
7503
+ duration: round11(
7457
7504
  outputLen({ in: a, out: b }, speeds, freezes, a === s.in && leading)
7458
7505
  )
7459
7506
  });
@@ -7482,7 +7529,7 @@ var camLane = {
7482
7529
  Math.max(first.in, last.out - span)
7483
7530
  );
7484
7531
  return (d) => {
7485
- d.cam.window = { in: round10(newIn), out: round10(newIn + span) };
7532
+ d.cam.window = { in: round11(newIn), out: round11(newIn + span) };
7486
7533
  };
7487
7534
  }
7488
7535
  const overlapping = eff.map((s, i) => ({
@@ -7495,8 +7542,8 @@ var camLane = {
7495
7542
  const next = g.edge === "start" ? { in: Math.min(sourceT, current.out - 0.05), out: current.out } : { in: current.in, out: Math.max(sourceT, current.in + 0.05) };
7496
7543
  return (d) => {
7497
7544
  d.cam.window = {
7498
- in: round10(Math.max(0, next.in)),
7499
- out: round10(Math.min(sourceDuration, next.out))
7545
+ in: round11(Math.max(0, next.in)),
7546
+ out: round11(Math.min(sourceDuration, next.out))
7500
7547
  };
7501
7548
  };
7502
7549
  },
@@ -7516,8 +7563,8 @@ var micLane = {
7516
7563
  return segments.map((s, i) => ({
7517
7564
  id: `mic-${i}`,
7518
7565
  kind: "clip",
7519
- t: round10(starts[i]),
7520
- duration: round10(
7566
+ t: round11(starts[i]),
7567
+ duration: round11(
7521
7568
  outputLen(s, speeds, freezes, i === 0 || !contiguous(segments, i))
7522
7569
  )
7523
7570
  }));
@@ -7573,8 +7620,8 @@ var speedLane = {
7573
7620
  {
7574
7621
  id: sp.id,
7575
7622
  kind: "clip",
7576
- t: round10(start),
7577
- duration: round10(end - start),
7623
+ t: round11(start),
7624
+ duration: round11(end - start),
7578
7625
  label: `${sp.rate}\xD7`
7579
7626
  }
7580
7627
  ];
@@ -7597,8 +7644,8 @@ var speedLane = {
7597
7644
  ...d.speed ?? [],
7598
7645
  {
7599
7646
  id,
7600
- in: round10(srcT),
7601
- out: round10(srcT + len),
7647
+ in: round11(srcT),
7648
+ out: round11(srcT + len),
7602
7649
  rate: DEFAULT_SPEED_RATE,
7603
7650
  source: "manual"
7604
7651
  }
@@ -7628,8 +7675,8 @@ var speedLane = {
7628
7675
  return (d) => {
7629
7676
  const s = d.speed?.find((x) => x.id === g.id);
7630
7677
  if (!s) return;
7631
- s.in = round10(newIn);
7632
- s.out = round10(newIn + len);
7678
+ s.in = round11(newIn);
7679
+ s.out = round11(newIn + len);
7633
7680
  s.source = "manual";
7634
7681
  d.speed.sort((a, b) => a.in - b.in);
7635
7682
  };
@@ -7658,8 +7705,8 @@ var speedLane = {
7658
7705
  return (d) => {
7659
7706
  const s = d.speed?.find((x) => x.id === g.id);
7660
7707
  if (!s) return;
7661
- s.in = round10(next.in);
7662
- s.out = round10(next.out);
7708
+ s.in = round11(next.in);
7709
+ s.out = round11(next.out);
7663
7710
  s.source = "manual";
7664
7711
  };
7665
7712
  }
@@ -7678,8 +7725,8 @@ var audioLane = {
7678
7725
  return doc.audio.map((c) => ({
7679
7726
  id: c.id,
7680
7727
  kind: "clip",
7681
- t: round10(c.start),
7682
- duration: round10(clipLength(c)),
7728
+ t: round11(c.start),
7729
+ duration: round11(clipLength(c)),
7683
7730
  label: c.name
7684
7731
  }));
7685
7732
  },
@@ -7690,7 +7737,7 @@ var audioLane = {
7690
7737
  const start = Math.max(0, g.t);
7691
7738
  return (d) => {
7692
7739
  const c = d.audio.find((x) => x.id === g.id);
7693
- if (c) c.start = round10(start);
7740
+ if (c) c.start = round11(start);
7694
7741
  };
7695
7742
  }
7696
7743
  if (g.type === "resize") {
@@ -7703,8 +7750,8 @@ var audioLane = {
7703
7750
  return (d) => {
7704
7751
  const c = d.audio.find((x) => x.id === g.id);
7705
7752
  if (!c) return;
7706
- c.start = round10(newStart2);
7707
- c.loopLen = round10(end - newStart2);
7753
+ c.start = round11(newStart2);
7754
+ c.loopLen = round11(end - newStart2);
7708
7755
  };
7709
7756
  }
7710
7757
  const delta = g.t - clip.start;
@@ -7713,22 +7760,22 @@ var audioLane = {
7713
7760
  return (d) => {
7714
7761
  const c = d.audio.find((x) => x.id === g.id);
7715
7762
  if (!c) return;
7716
- c.in = round10(newIn);
7717
- c.start = round10(newStart);
7763
+ c.in = round11(newIn);
7764
+ c.start = round11(newStart);
7718
7765
  };
7719
7766
  }
7720
7767
  if (clip.loop) {
7721
7768
  const newLen = Math.max(0.1, g.t - clip.start);
7722
7769
  return (d) => {
7723
7770
  const c = d.audio.find((x) => x.id === g.id);
7724
- if (c) c.loopLen = round10(newLen);
7771
+ if (c) c.loopLen = round11(newLen);
7725
7772
  };
7726
7773
  }
7727
7774
  const span = Math.max(0.05, g.t - clip.start);
7728
7775
  const newOut = Math.min(clip.in + span, clip.duration);
7729
7776
  return (d) => {
7730
7777
  const c = d.audio.find((x) => x.id === g.id);
7731
- if (c) c.out = round10(Math.max(c.in + 0.05, newOut));
7778
+ if (c) c.out = round11(Math.max(c.in + 0.05, newOut));
7732
7779
  };
7733
7780
  }
7734
7781
  if (g.type === "remove") {
@@ -7741,8 +7788,8 @@ var audioLane = {
7741
7788
  },
7742
7789
  magnets(doc) {
7743
7790
  return doc.audio.flatMap((c) => [
7744
- round10(c.start),
7745
- round10(c.start + clipLength(c))
7791
+ round11(c.start),
7792
+ round11(c.start + clipLength(c))
7746
7793
  ]);
7747
7794
  }
7748
7795
  };
@@ -7760,7 +7807,7 @@ function poseItems(clipId, start, duration, motion) {
7760
7807
  return (motion ?? []).map((p, i) => ({
7761
7808
  id: `${clipId}::k${i}`,
7762
7809
  kind: "keyframe",
7763
- t: round10(start + Math.min(Math.max(0, p.at), duration))
7810
+ t: round11(start + Math.min(Math.max(0, p.at), duration))
7764
7811
  }));
7765
7812
  }
7766
7813
  var overlaysLane = {
@@ -7771,8 +7818,8 @@ var overlaysLane = {
7771
7818
  {
7772
7819
  id: o.id,
7773
7820
  kind: "clip",
7774
- t: round10(o.start),
7775
- duration: round10(o.duration),
7821
+ t: round11(o.start),
7822
+ duration: round11(o.duration),
7776
7823
  label: o.kind === "text" ? o.text.split("\n")[0].slice(0, 24) || "Text" : o.kind === "image" ? "Image" : "Video"
7777
7824
  },
7778
7825
  // Pose diamonds render after the clips, so they sit on top.
@@ -7792,7 +7839,7 @@ var overlaysLane = {
7792
7839
  return (d) => {
7793
7840
  const o = (d.overlays ?? []).find((x) => x.id === kf.clipId);
7794
7841
  const p = o?.motion?.[kf.index];
7795
- if (p) p.at = round10(at);
7842
+ if (p) p.at = round11(at);
7796
7843
  };
7797
7844
  }
7798
7845
  if (g.type === "remove") {
@@ -7821,8 +7868,8 @@ var overlaysLane = {
7821
7868
  {
7822
7869
  id,
7823
7870
  kind: "text",
7824
- start: round10(start),
7825
- duration: round10(len),
7871
+ start: round11(start),
7872
+ duration: round11(len),
7826
7873
  text: "Title",
7827
7874
  preset: "title",
7828
7875
  // Frame fractions: centered, lower-third — at any aspect ratio.
@@ -7837,7 +7884,7 @@ var overlaysLane = {
7837
7884
  const start = Math.max(0, g.t);
7838
7885
  return (d) => {
7839
7886
  const o = (d.overlays ?? []).find((x) => x.id === g.id);
7840
- if (o) o.start = round10(start);
7887
+ if (o) o.start = round11(start);
7841
7888
  };
7842
7889
  }
7843
7890
  if (g.type === "resize") {
@@ -7847,14 +7894,14 @@ var overlaysLane = {
7847
7894
  return (d) => {
7848
7895
  const o = (d.overlays ?? []).find((x) => x.id === g.id);
7849
7896
  if (!o) return;
7850
- o.start = round10(newStart);
7851
- o.duration = round10(end - newStart);
7897
+ o.start = round11(newStart);
7898
+ o.duration = round11(end - newStart);
7852
7899
  };
7853
7900
  }
7854
7901
  const newLen = Math.max(OVERLAY_MIN_DURATION, g.t - clip.start);
7855
7902
  return (d) => {
7856
7903
  const o = (d.overlays ?? []).find((x) => x.id === g.id);
7857
- if (o) o.duration = round10(newLen);
7904
+ if (o) o.duration = round11(newLen);
7858
7905
  };
7859
7906
  }
7860
7907
  return (d) => {
@@ -7863,8 +7910,8 @@ var overlaysLane = {
7863
7910
  },
7864
7911
  magnets(doc) {
7865
7912
  return (doc.overlays ?? []).flatMap((o) => [
7866
- round10(o.start),
7867
- round10(o.start + o.duration)
7913
+ round11(o.start),
7914
+ round11(o.start + o.duration)
7868
7915
  ]);
7869
7916
  }
7870
7917
  };
@@ -7877,8 +7924,8 @@ var objectsLane = {
7877
7924
  {
7878
7925
  id: o.id,
7879
7926
  kind: "clip",
7880
- t: round10(o.span?.start ?? 0),
7881
- duration: round10(o.span?.duration ?? outDur),
7927
+ t: round11(o.span?.start ?? 0),
7928
+ duration: round11(o.span?.duration ?? outDur),
7882
7929
  label: o.asset.kind === "primitive" ? o.asset.shape : o.asset.kind === "text3d" ? o.asset.text : "model"
7883
7930
  },
7884
7931
  // Pose diamonds: clip-local `at` from the span start (0 span-less).
@@ -7906,7 +7953,7 @@ var objectsLane = {
7906
7953
  return (d) => {
7907
7954
  const o = (d.objects ?? []).find((x) => x.id === kf.clipId);
7908
7955
  const p = o?.motion?.[kf.index];
7909
- if (p) p.at = round10(at);
7956
+ if (p) p.at = round11(at);
7910
7957
  };
7911
7958
  }
7912
7959
  if (g.type === "remove") {
@@ -7927,7 +7974,7 @@ var objectsLane = {
7927
7974
  const start = Math.max(0, g.t);
7928
7975
  return (d) => {
7929
7976
  const o = (d.objects ?? []).find((x) => x.id === g.id);
7930
- if (o?.span) o.span.start = round10(start);
7977
+ if (o?.span) o.span.start = round11(start);
7931
7978
  };
7932
7979
  }
7933
7980
  if (g.type === "resize") {
@@ -7938,14 +7985,14 @@ var objectsLane = {
7938
7985
  return (d) => {
7939
7986
  const o = (d.objects ?? []).find((x) => x.id === g.id);
7940
7987
  if (!o?.span) return;
7941
- o.span.start = round10(newStart);
7942
- o.span.duration = round10(end - newStart);
7988
+ o.span.start = round11(newStart);
7989
+ o.span.duration = round11(end - newStart);
7943
7990
  };
7944
7991
  }
7945
7992
  const newLen = Math.max(OVERLAY_MIN_DURATION, g.t - clip.span.start);
7946
7993
  return (d) => {
7947
7994
  const o = (d.objects ?? []).find((x) => x.id === g.id);
7948
- if (o?.span) o.span.duration = round10(newLen);
7995
+ if (o?.span) o.span.duration = round11(newLen);
7949
7996
  };
7950
7997
  }
7951
7998
  return (d) => {
@@ -7954,7 +8001,7 @@ var objectsLane = {
7954
8001
  },
7955
8002
  magnets(doc) {
7956
8003
  return (doc.objects ?? []).flatMap(
7957
- (o) => o.span ? [round10(o.span.start), round10(o.span.start + o.span.duration)] : []
8004
+ (o) => o.span ? [round11(o.span.start), round11(o.span.start + o.span.duration)] : []
7958
8005
  );
7959
8006
  }
7960
8007
  };
@@ -7981,7 +8028,7 @@ function nextSpeedId(doc) {
7981
8028
  function segIndex(id) {
7982
8029
  return Number(id.replace("seg-", ""));
7983
8030
  }
7984
- function round10(v) {
8031
+ function round11(v) {
7985
8032
  return Math.round(v * 1e3) / 1e3;
7986
8033
  }
7987
8034