@hyperframes/studio 0.8.93 → 0.8.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/assets/{hyperframes-player-CkV9KK6A.js → hyperframes-player-DJJKab4_.js} +1 -1
  2. package/dist/assets/{index-V3fzEfOD.js → index-C1dXTZvs.js} +1 -1
  3. package/dist/assets/{index-CYk0NwSv.js → index-Cc4by8r7.js} +183 -183
  4. package/dist/assets/{index-BlLFipOk.js → index-DE1dn3QB.js} +1 -1
  5. package/dist/index.d.ts +63 -46
  6. package/dist/index.html +1 -1
  7. package/dist/index.js +196 -111
  8. package/dist/index.js.map +1 -1
  9. package/package.json +8 -8
  10. package/src/components/TimelineToolbar.test.tsx +17 -0
  11. package/src/components/TimelineToolbar.tsx +3 -1
  12. package/src/components/editor/ConnectedDomEditOverlay.test.tsx +2 -0
  13. package/src/components/editor/ConnectedDomEditOverlay.tsx +3 -1
  14. package/src/components/editor/DomEditCropHandles.test.tsx +87 -1
  15. package/src/components/editor/DomEditCropHandles.tsx +64 -62
  16. package/src/components/editor/DomEditOverlay.test.ts +39 -0
  17. package/src/components/editor/DomEditOverlay.tsx +6 -1
  18. package/src/components/editor/DomEditSelectionChrome.test.tsx +114 -1
  19. package/src/components/editor/DomEditSelectionChrome.tsx +17 -10
  20. package/src/components/editor/PreviewOverlayProvider.test.tsx +19 -0
  21. package/src/components/editor/PreviewOverlayProvider.tsx +1 -1
  22. package/src/components/editor/SnapToolbar.tsx +1 -1
  23. package/src/components/editor/domEditOverlayGeometry.ts +2 -0
  24. package/src/components/editor/manualEditsSeekReapply.ts +5 -1
  25. package/src/components/nle/PreviewOverlays.tsx +3 -3
  26. package/src/hooks/gsapResizeIntercept.test.ts +40 -0
  27. package/src/hooks/gsapResizeIntercept.ts +8 -4
  28. package/src/index.ts +4 -0
  29. package/src/player/components/PlayerControls.tsx +2 -15
  30. package/src/player/components/ShortcutsPanel.test.tsx +33 -1
  31. package/src/player/components/ShortcutsPanel.tsx +26 -0
  32. package/src/player/components/Timeline.ghostLane.test.tsx +126 -0
  33. package/src/player/components/Timeline.rulerSeek.test.tsx +7 -0
  34. package/src/player/components/Timeline.test.ts +3 -3
  35. package/src/player/components/Timeline.trackPadding.test.tsx +157 -0
  36. package/src/player/components/TimelineCanvas.tsx +44 -19
  37. package/src/player/components/TimelineProvider.tsx +1 -0
  38. package/src/player/components/TimelineTypes.ts +2 -0
  39. package/src/player/components/timelineDragDrop.test.tsx +14 -12
  40. package/src/player/components/timelineLayout.test.ts +87 -11
  41. package/src/player/components/timelineLayout.ts +28 -21
  42. package/src/player/components/useTimelineProviderState.tsx +5 -1
  43. package/src/player/components/useTimelineTrackLayout.ts +11 -2
  44. package/src/player/components/useTimelineVirtualRows.test.tsx +32 -0
  45. package/src/player/components/useTimelineVirtualRows.ts +2 -2
  46. package/src/utils/gsapSoftReload.test.ts +51 -2
  47. package/src/utils/gsapSoftReload.ts +24 -10
  48. package/src/utils/gsapUndoRestore.test.ts +49 -0
  49. package/src/utils/gsapUndoRestore.ts +15 -0
package/dist/index.js CHANGED
@@ -876,6 +876,25 @@ var ShortcutsPanel = memo(function ShortcutsPanel2({
876
876
  )
877
877
  ] });
878
878
  });
879
+ function ShortcutsButton({ sections, disabled = false, onSeek }) {
880
+ const duration = usePlayerStore((s) => s.duration);
881
+ const inPoint = usePlayerStore((s) => s.inPoint);
882
+ const outPoint = usePlayerStore((s) => s.outPoint);
883
+ const { setInPoint, setOutPoint, requestSeek } = usePlayerStore.getState();
884
+ return /* @__PURE__ */ jsx(
885
+ ShortcutsPanel,
886
+ {
887
+ disabled,
888
+ duration,
889
+ inPoint,
890
+ outPoint,
891
+ setInPoint,
892
+ setOutPoint,
893
+ onSeek: onSeek ?? requestSeek,
894
+ sections
895
+ }
896
+ );
897
+ }
879
898
 
880
899
  // src/player/components/SpeedMenu.tsx
881
900
  import { useState as useState2, useCallback as useCallback3, memo as memo2 } from "react";
@@ -1152,10 +1171,6 @@ var PlayerControls = memo4(function PlayerControls2({
1152
1171
  const setAudioMuted = usePlayerStore.getState().setAudioMuted;
1153
1172
  const setAudioVolume = usePlayerStore.getState().setAudioVolume;
1154
1173
  const setLoopEnabled = usePlayerStore.getState().setLoopEnabled;
1155
- const inPoint = usePlayerStore((s) => s.inPoint);
1156
- const outPoint = usePlayerStore((s) => s.outPoint);
1157
- const setInPoint = usePlayerStore.getState().setInPoint;
1158
- const setOutPoint = usePlayerStore.getState().setOutPoint;
1159
1174
  const timeDisplayMode = usePlayerStore((s) => s.timeDisplayMode);
1160
1175
  const setTimeDisplayMode = usePlayerStore.getState().setTimeDisplayMode;
1161
1176
  const timeDisplayRef = useRef3(null);
@@ -1255,19 +1270,7 @@ var PlayerControls = memo4(function PlayerControls2({
1255
1270
  }
1256
1271
  ),
1257
1272
  onToggleFullscreen && /* @__PURE__ */ jsx4(FullscreenButton, { isFullscreen, onToggleFullscreen }),
1258
- /* @__PURE__ */ jsx4(
1259
- ShortcutsPanel,
1260
- {
1261
- disabled,
1262
- duration,
1263
- inPoint,
1264
- outPoint,
1265
- setInPoint,
1266
- setOutPoint,
1267
- onSeek,
1268
- sections: shortcutSections
1269
- }
1270
- )
1273
+ /* @__PURE__ */ jsx4(ShortcutsButton, { disabled, onSeek, sections: shortcutSections })
1271
1274
  ] })
1272
1275
  ]
1273
1276
  }
@@ -1941,8 +1944,8 @@ function getTimelineBeatEntries(beatTimes, beatStrengths, range, pinnedIndexes =
1941
1944
  function getTimelineLaneTop(laneIndex) {
1942
1945
  return TRACK_H + Math.max(0, Math.trunc(laneIndex)) * LANE_H;
1943
1946
  }
1944
- var TRACKS_TOP_PAD = 50;
1945
- var TRACKS_BOTTOM_PAD = Math.round(TRACK_H * 1.5);
1947
+ var TRACKS_TOP_PAD = 8;
1948
+ var TRACKS_BOTTOM_PAD = TRACK_H;
1946
1949
  var TRACKS_LEFT_PAD = 48;
1947
1950
  function trackHeights(tracks, expandedClipIds) {
1948
1951
  return tracks.map((clips) => {
@@ -1960,9 +1963,14 @@ function validRowHeight(height) {
1960
1963
  if (height === void 0 || !Number.isFinite(height) || height <= 0) return TRACK_H;
1961
1964
  return height;
1962
1965
  }
1966
+ function validPad(pad, fallback) {
1967
+ return pad !== void 0 && Number.isFinite(pad) ? Math.max(0, pad) : fallback;
1968
+ }
1963
1969
  var rowGeometryCache = /* @__PURE__ */ new WeakMap();
1964
1970
  var EMPTY_ROW_HEIGHTS = Object.freeze([]);
1965
- function createTimelineRowGeometry(rowKeys, rowHeights) {
1971
+ function createTimelineRowGeometry(rowKeys, rowHeights, padding = {}) {
1972
+ const topPad = validPad(padding.top, TRACKS_TOP_PAD);
1973
+ const bottomPad = validPad(padding.bottom, TRACKS_BOTTOM_PAD);
1966
1974
  const heights = Object.freeze(rowHeights.map(validRowHeight));
1967
1975
  const keys = Object.freeze(
1968
1976
  heights.map((_, row) => {
@@ -1985,7 +1993,7 @@ function createTimelineRowGeometry(rowKeys, rowHeights) {
1985
1993
  return (offsets[wholeRow] ?? 0) + (row - wholeRow) * getRowHeight(wholeRow);
1986
1994
  };
1987
1995
  const getRowFromY = (contentY) => {
1988
- const y = contentY - RULER_H - TRACKS_TOP_PAD;
1996
+ const y = contentY - RULER_H - topPad;
1989
1997
  if (heights.length === 0) return y / TRACK_H;
1990
1998
  if (y < 0) return y / getRowHeight(0);
1991
1999
  const rowsHeight = offsets[heights.length] ?? 0;
@@ -2005,10 +2013,11 @@ function createTimelineRowGeometry(rowKeys, rowHeights) {
2005
2013
  rowHeights: heights,
2006
2014
  rowOffsets: offsets,
2007
2015
  rowsHeight: offsets.at(-1) ?? 0,
2008
- canvasHeight: RULER_H + TRACKS_TOP_PAD + (offsets.at(-1) ?? 0) + TRACKS_BOTTOM_PAD,
2016
+ padding: Object.freeze({ top: topPad, bottom: bottomPad }),
2017
+ canvasHeight: RULER_H + topPad + (offsets.at(-1) ?? 0) + bottomPad,
2009
2018
  getRowIndex: (rowKey) => rowIndexByKey.get(rowKey) ?? -1,
2010
2019
  getRowHeight,
2011
- getRowTop: (row) => RULER_H + TRACKS_TOP_PAD + getRowOffset(row),
2020
+ getRowTop: (row) => RULER_H + topPad + getRowOffset(row),
2012
2021
  getRowFromY,
2013
2022
  getRowPositionFromY: (contentY) => {
2014
2023
  const rowFloat = getRowFromY(contentY);
@@ -2033,12 +2042,6 @@ function getTimelineRowGeometry(rowHeights) {
2033
2042
  function getTimelineRowHeight(row, rowHeights = EMPTY_ROW_HEIGHTS) {
2034
2043
  return validRowHeight(rowHeights[row]);
2035
2044
  }
2036
- function getTimelineRowOffset(row, rowHeights) {
2037
- return getTimelineRowGeometry(rowHeights).getRowTop(row) - RULER_H - TRACKS_TOP_PAD;
2038
- }
2039
- function getTimelineRowTop(row, rowHeights = EMPTY_ROW_HEIGHTS) {
2040
- return RULER_H + TRACKS_TOP_PAD + getTimelineRowOffset(row, rowHeights);
2041
- }
2042
2045
  function getTimelineRowFromY(contentY, rowHeights = EMPTY_ROW_HEIGHTS) {
2043
2046
  return getTimelineRowGeometry(rowHeights).getRowFromY(contentY);
2044
2047
  }
@@ -11322,7 +11325,9 @@ function applyGroupStripHeights(tracks, rowHeights, groups, expandedLaneOwnerIds
11322
11325
  return TRACK_H + groupOwnLaneCount(group) * AUTOMATION_LANE_H;
11323
11326
  });
11324
11327
  }
11325
- function useTimelineRowHeights(tracks, gsapAnimations, selectedElementId, selectedElementIds, groups) {
11328
+ function useTimelineRowHeights(tracks, gsapAnimations, selectedElementId, selectedElementIds, groups, trackPadding) {
11329
+ const padTop = trackPadding?.top;
11330
+ const padBottom = trackPadding?.bottom;
11326
11331
  const expandedClipIds = usePlayerStore((s) => s.expandedClipIds);
11327
11332
  const expandedLaneOwnerIds = usePlayerStore((s) => s.expandedLaneOwnerIds);
11328
11333
  const { laneCounts, rowGeometry } = useMemo15(() => {
@@ -11357,10 +11362,13 @@ function useTimelineRowHeights(tracks, gsapAnimations, selectedElementId, select
11357
11362
  laneCounts: laneCounts2,
11358
11363
  rowGeometry: createTimelineRowGeometry(
11359
11364
  tracks.map(([track2]) => track2),
11360
- rowHeights
11365
+ rowHeights,
11366
+ { top: padTop, bottom: padBottom }
11361
11367
  )
11362
11368
  };
11363
11369
  }, [
11370
+ padTop,
11371
+ padBottom,
11364
11372
  expandedClipIds,
11365
11373
  expandedLaneOwnerIds,
11366
11374
  gsapAnimations,
@@ -11378,7 +11386,7 @@ function useTimelineRowHeights(tracks, gsapAnimations, selectedElementId, select
11378
11386
  rowHeights: rowGeometry.rowHeights
11379
11387
  };
11380
11388
  }
11381
- function useTimelineTrackLayout(expandedElements, gsapAnimations, selectedElementId, selectedElementIds) {
11389
+ function useTimelineTrackLayout(expandedElements, gsapAnimations, selectedElementId, selectedElementIds, trackPadding) {
11382
11390
  const { tracks, trackStyles, trackOrder, groups, trackGroupOf } = useTimelineTrackDerivations(expandedElements);
11383
11391
  const trackOrderRef = useRef20(trackOrder);
11384
11392
  trackOrderRef.current = trackOrder;
@@ -11387,7 +11395,8 @@ function useTimelineTrackLayout(expandedElements, gsapAnimations, selectedElemen
11387
11395
  gsapAnimations,
11388
11396
  selectedElementId,
11389
11397
  selectedElementIds,
11390
- groups
11398
+ groups,
11399
+ trackPadding
11391
11400
  );
11392
11401
  return {
11393
11402
  tracks,
@@ -11432,8 +11441,8 @@ function useTimelineDisplayLayout(draggedClip, trackOrder, rowGeometry) {
11432
11441
  const displayTrackOrder2 = useDisplayTrackOrder(draggedClip, trackOrder);
11433
11442
  const displayRowHeights = useDisplayRowHeights(displayTrackOrder2, rowGeometry);
11434
11443
  const displayRowGeometry = useMemo15(
11435
- () => createTimelineRowGeometry(displayTrackOrder2, displayRowHeights),
11436
- [displayTrackOrder2, displayRowHeights]
11444
+ () => createTimelineRowGeometry(displayTrackOrder2, displayRowHeights, rowGeometry.padding),
11445
+ [displayTrackOrder2, displayRowHeights, rowGeometry.padding]
11437
11446
  );
11438
11447
  return {
11439
11448
  displayTrackOrder: displayTrackOrder2,
@@ -12579,7 +12588,7 @@ function useTimelineVirtualRows({
12579
12588
  getItemKey,
12580
12589
  overscan: TIMELINE_VIEWPORT_BUDGETS.rowOverscanPerSide,
12581
12590
  rangeExtractor,
12582
- scrollMargin: RULER_H + TRACKS_TOP_PAD,
12591
+ scrollMargin: RULER_H + rowGeometry.padding.top,
12583
12592
  initialRect: { width: viewport.clientWidth, height: viewport.clientHeight },
12584
12593
  initialOffset
12585
12594
  });
@@ -12797,7 +12806,8 @@ function useTimelineProviderState({
12797
12806
  theme: themeOverrides,
12798
12807
  showAudioEffects = true,
12799
12808
  showKeyframes = true,
12800
- sessionEpoch = 0,
12809
+ trackPadding,
12810
+ sessionEpoch: sessionEpochProp,
12801
12811
  previewIframeRef,
12802
12812
  onZIndexReorder
12803
12813
  } = {}) {
@@ -12839,6 +12849,7 @@ function useTimelineProviderState({
12839
12849
  const currentTime = usePlayerStore((s) => s.currentTime);
12840
12850
  const beatDragging = usePlayerStore((s) => s.beatDragging);
12841
12851
  const timelineSessionEpoch = usePlayerStore((s) => s.timelineSessionEpoch);
12852
+ const sessionEpoch = sessionEpochProp ?? timelineSessionEpoch;
12842
12853
  const setFocusedEaseSegment = usePlayerStore((s) => s.setFocusedEaseSegment);
12843
12854
  const { zoomMode, manualZoomPercent, setZoomMode, setManualZoomPercent } = useTimelineZoom();
12844
12855
  const playheadRef = useRef25(null);
@@ -12874,7 +12885,8 @@ function useTimelineProviderState({
12874
12885
  timelineElements,
12875
12886
  gsapAnimations,
12876
12887
  selectedElementId,
12877
- selectedElementIds
12888
+ selectedElementIds,
12889
+ trackPadding
12878
12890
  );
12879
12891
  const timelineElementsRef = useRef25(timelineElements);
12880
12892
  timelineElementsRef.current = timelineElements;
@@ -13153,6 +13165,7 @@ function useTimelineProviderState({
13153
13165
  marqueeRect,
13154
13166
  laneGapStrips,
13155
13167
  dropPreview: assetDrop.dropPreview,
13168
+ acceptsMediaDrop: !!(onFileDrop || onAssetDrop),
13156
13169
  theme,
13157
13170
  showAudioEffects,
13158
13171
  displayTrackOrder: displayLayout.displayTrackOrder,
@@ -19377,6 +19390,13 @@ function verifyTimelinesPopulated(win, targetKeys) {
19377
19390
  }
19378
19391
  return Object.keys(timelines).filter((k) => k !== "__proxied").length > 0;
19379
19392
  }
19393
+ function restoreAuthoredTransforms(style, authored) {
19394
+ style.transform = authored?.transform ?? "";
19395
+ if (!authored) return;
19396
+ for (const prop of ["translate", "rotate", "scale"]) {
19397
+ style.setProperty(prop, authored.getPropertyValue(prop));
19398
+ }
19399
+ }
19380
19400
  function finalizeSoftReload(win, currentTime) {
19381
19401
  win.__player?.seek?.(currentTime);
19382
19402
  win.__hfForceTimelineRebind?.();
@@ -19417,7 +19437,7 @@ function applySoftReload(iframe, scriptText, options = {}) {
19417
19437
  const currentTime = currentTimeOverride ?? win.__player?.getTime?.() ?? 0;
19418
19438
  let deferredToAsync = false;
19419
19439
  let authoredDoc;
19420
- const findAuthoredSource = (el) => {
19440
+ const findAuthoredStyle = (el) => {
19421
19441
  if (authoredDoc === void 0) {
19422
19442
  try {
19423
19443
  authoredDoc = authoredHtml ? parseSavedSource(authoredHtml) : null;
@@ -19425,13 +19445,10 @@ function applySoftReload(iframe, scriptText, options = {}) {
19425
19445
  authoredDoc = null;
19426
19446
  }
19427
19447
  }
19428
- return authoredDoc ? findAuthoredElement(authoredDoc, el) : null;
19429
- };
19430
- const readAuthoredOpacity = (el) => {
19431
- const source = findAuthoredSource(el);
19432
- if (source instanceof HTMLElement) return source.style.opacity;
19433
- return readStampedAuthoredOpacity(el);
19448
+ const source = authoredDoc ? findAuthoredElement(authoredDoc, el) : null;
19449
+ return source instanceof HTMLElement || source instanceof SVGElement ? source.style : null;
19434
19450
  };
19451
+ const readAuthoredOpacity = (el) => findAuthoredStyle(el)?.opacity ?? readStampedAuthoredOpacity(el);
19435
19452
  const doReload = () => {
19436
19453
  const timelines = win.__timelines;
19437
19454
  const allTargets = [];
@@ -19450,6 +19467,7 @@ function applySoftReload(iframe, scriptText, options = {}) {
19450
19467
  }
19451
19468
  }
19452
19469
  try {
19470
+ tl.clear?.();
19453
19471
  tl.kill?.();
19454
19472
  } catch {
19455
19473
  }
@@ -19476,7 +19494,7 @@ function applySoftReload(iframe, scriptText, options = {}) {
19476
19494
  for (const [el, css2] of saved2) {
19477
19495
  const s = el.style;
19478
19496
  s.cssText = css2;
19479
- s.removeProperty("transform");
19497
+ restoreAuthoredTransforms(s, findAuthoredStyle(el));
19480
19498
  const authored = readAuthoredOpacity(el);
19481
19499
  if (authored !== null) {
19482
19500
  applyAuthoredInlineOpacity(s, authored);
@@ -21228,6 +21246,9 @@ var TimelineCanvas = memo13(function TimelineCanvas2() {
21228
21246
  const dropTrackIndex = props.dropPreview ? displayTrackOrder2.indexOf(props.dropPreview.track) : -1;
21229
21247
  const dropRowIndex = dropTrackIndex < 0 ? displayTrackOrder2.length : dropTrackIndex;
21230
21248
  const insertLineRow = (draggedClip?.started ? draggedClip.insertRow : null) ?? props.dropPreview?.insertRow ?? null;
21249
+ const ghostLaneActive = (insertLineRow ?? -1) >= props.trackOrder.length || props.dropPreview?.insertRow == null && props.dropPreview != null && dropTrackIndex < 0;
21250
+ const showGhostLane = props.rowGeometry.padding.bottom >= TRACK_H;
21251
+ const ghostLaneRow = props.rowGeometry.getRowIndex(props.trackOrder.at(-1) ?? Number.NaN) + 1;
21231
21252
  const draggedRowHeight = getTimelineRowHeight(draggedRowIndex, props.rowHeights);
21232
21253
  const draggedClipHeight = Math.min(draggedRowHeight, TRACK_H) - CLIP_Y * 2;
21233
21254
  const beatDragging = props.beatDragging;
@@ -21239,7 +21260,13 @@ var TimelineCanvas = memo13(function TimelineCanvas2() {
21239
21260
  style: { height: props.totalH, width: props.contentOrigin + props.trackContentWidth },
21240
21261
  children: [
21241
21262
  /* @__PURE__ */ jsx55(TimelineRulerPart, {}),
21242
- /* @__PURE__ */ jsx55("div", { "aria-hidden": "true", style: { height: props.rowsVirtualized ? 0 : TRACKS_TOP_PAD } }),
21263
+ /* @__PURE__ */ jsx55(
21264
+ "div",
21265
+ {
21266
+ "aria-hidden": "true",
21267
+ style: { height: props.rowsVirtualized ? 0 : props.rowGeometry.padding.top }
21268
+ }
21269
+ ),
21243
21270
  /* @__PURE__ */ jsx55(
21244
21271
  TimelineLanes,
21245
21272
  {
@@ -21257,7 +21284,29 @@ var TimelineCanvas = memo13(function TimelineCanvas2() {
21257
21284
  onRazorSplitAll: props.onRazorSplitAll
21258
21285
  }
21259
21286
  ),
21260
- /* @__PURE__ */ jsx55("div", { "aria-hidden": "true", style: { height: props.rowsVirtualized ? 0 : TRACKS_BOTTOM_PAD } }),
21287
+ /* @__PURE__ */ jsx55(
21288
+ "div",
21289
+ {
21290
+ "aria-hidden": "true",
21291
+ style: { height: props.rowsVirtualized ? 0 : props.rowGeometry.padding.bottom }
21292
+ }
21293
+ ),
21294
+ showGhostLane && /* @__PURE__ */ jsx55(
21295
+ "div",
21296
+ {
21297
+ "aria-hidden": "true",
21298
+ "data-testid": "timeline-ghost-lane",
21299
+ "data-active": ghostLaneActive || void 0,
21300
+ className: `pointer-events-none absolute flex items-center justify-center rounded-lg border border-dashed text-[11px] transition-colors duration-150 ${ghostLaneActive ? "border-studio-accent/60 bg-studio-accent/6 text-studio-accent" : "border-neutral-700/50 text-neutral-500"}`,
21301
+ style: {
21302
+ top: props.rowGeometry.getRowTop(ghostLaneRow) + CLIP_Y,
21303
+ left: props.contentOrigin,
21304
+ width: props.trackContentWidth,
21305
+ height: TRACK_H - CLIP_Y * 2
21306
+ },
21307
+ children: props.acceptsMediaDrop && "Drop media here"
21308
+ }
21309
+ ),
21261
21310
  props.laneGapStrips.map((strip) => {
21262
21311
  const rowIndex = displayTrackOrder2.indexOf(strip.track);
21263
21312
  if (rowIndex < 0) return null;
@@ -21270,7 +21319,7 @@ var TimelineCanvas = memo13(function TimelineCanvas2() {
21270
21319
  {
21271
21320
  className: "pointer-events-none absolute",
21272
21321
  style: {
21273
- top: getTimelineRowTop(rowIndex, props.rowHeights) + CLIP_Y,
21322
+ top: props.rowGeometry.getRowTop(rowIndex) + CLIP_Y,
21274
21323
  left: props.contentOrigin + gap.start * props.pps,
21275
21324
  width: Math.max((gap.end - gap.start) * props.pps, 2),
21276
21325
  height: TRACK_H - CLIP_Y * 2,
@@ -21287,7 +21336,7 @@ var TimelineCanvas = memo13(function TimelineCanvas2() {
21287
21336
  {
21288
21337
  className: "absolute pointer-events-none",
21289
21338
  style: {
21290
- top: getTimelineRowTop(draggedRowIndex, props.rowHeights) + CLIP_Y,
21339
+ top: props.rowGeometry.getRowTop(draggedRowIndex) + CLIP_Y,
21291
21340
  left: props.contentOrigin + draggedClip.previewStart * props.pps,
21292
21341
  width: Math.max(draggedClip.element.duration * props.pps, 4),
21293
21342
  height: draggedClipHeight,
@@ -21305,7 +21354,7 @@ var TimelineCanvas = memo13(function TimelineCanvas2() {
21305
21354
  "data-testid": "timeline-drop-preview",
21306
21355
  className: "absolute pointer-events-none",
21307
21356
  style: {
21308
- top: getTimelineRowTop(dropRowIndex, props.rowHeights) + CLIP_Y,
21357
+ top: props.rowGeometry.getRowTop(dropRowIndex) + CLIP_Y,
21309
21358
  left: props.contentOrigin + props.dropPreview.start * props.pps,
21310
21359
  width: DROP_PREVIEW_SECONDS * props.pps,
21311
21360
  height: TRACK_H - CLIP_Y * 2,
@@ -21322,7 +21371,7 @@ var TimelineCanvas = memo13(function TimelineCanvas2() {
21322
21371
  "data-testid": "timeline-insert-line",
21323
21372
  className: "absolute pointer-events-none",
21324
21373
  style: {
21325
- top: getTimelineRowTop(insertLineRow, props.rowHeights) - 0.5,
21374
+ top: props.rowGeometry.getRowTop(insertLineRow) - 0.5,
21326
21375
  left: props.contentOrigin,
21327
21376
  width: props.trackContentWidth,
21328
21377
  height: 1,
@@ -28039,7 +28088,7 @@ function PreviewOverlayProvider({ iframe, children }) {
28039
28088
  toggleSafeMargins: () => usePreviewGuidesStore.getState().toggle("safeMarginsVisible")
28040
28089
  }
28041
28090
  };
28042
- return /* @__PURE__ */ jsx74(PreviewOverlayContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx74("div", { ref: overlayRef, className: "absolute inset-0", children }) });
28091
+ return /* @__PURE__ */ jsx74(PreviewOverlayContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx74("div", { ref: overlayRef, className: "pointer-events-none absolute inset-0", children }) });
28043
28092
  }
28044
28093
  function usePreviewOverlayContext() {
28045
28094
  const context = useContext8(PreviewOverlayContext);
@@ -30110,6 +30159,7 @@ function readThroughPass(memo53, key2, compute) {
30110
30159
  }
30111
30160
 
30112
30161
  // src/components/editor/domEditOverlayGeometry.ts
30162
+ var RESIZE_HANDLE_HIT_PX = 16;
30113
30163
  function isElementVisibleForOverlay(el) {
30114
30164
  return isElementVisibleThroughAncestors(el);
30115
30165
  }
@@ -31589,7 +31639,11 @@ function reapplyRotations(doc) {
31589
31639
  }
31590
31640
  }
31591
31641
  }
31592
- var STUDIO_EDIT_ATTRS = [STUDIO_PATH_OFFSET_ATTR, STUDIO_BOX_SIZE_ATTR, STUDIO_ROTATION_ATTR].flatMap((attr) => [attr, `data-${attr}`]).concat(STUDIO_MOTION_ATTR);
31642
+ var STUDIO_EDIT_ATTRS = [
31643
+ STUDIO_PATH_OFFSET_ATTR,
31644
+ STUDIO_BOX_SIZE_ATTR,
31645
+ STUDIO_ROTATION_ATTR
31646
+ ].flatMap((attr) => [attr, `data-${attr}`]).concat(STUDIO_MOTION_ATTR);
31593
31647
  var STUDIO_EDIT_SELECTORS = STUDIO_EDIT_ATTRS.map(
31594
31648
  (attr) => attr === STUDIO_MOTION_ATTR ? `[${attr}]` : `[${attr}="true"]`
31595
31649
  );
@@ -34068,30 +34122,43 @@ import { useEffect as useEffect43, useRef as useRef64, useState as useState40 }
34068
34122
  import { Fragment as Fragment26, jsx as jsx84, jsxs as jsxs57 } from "react/jsx-runtime";
34069
34123
  var EDGE_HIT_THICKNESS = 12;
34070
34124
  var EDGE_HIT_LENGTH = 32;
34071
- function edgeHandlePlacement(edge, rect) {
34072
- const cx = rect.left + rect.width / 2;
34073
- const cy = rect.top + rect.height / 2;
34074
- if (edge === "top") {
34075
- return { left: cx, top: rect.top, transform: "translate(-50%, -100%)" };
34076
- }
34077
- if (edge === "bottom") {
34078
- return { left: cx, top: rect.top + rect.height, transform: "translate(-50%, 0)" };
34079
- }
34080
- if (edge === "left") {
34081
- return { left: rect.left, top: cy, transform: "translate(-100%, -50%)" };
34125
+ var EDGE_PILL_LENGTH = 24;
34126
+ var REPOSITION_HANDLE_PX = 22;
34127
+ function edgeHandleLayout(edge, rect) {
34128
+ const vertical = edge === "left" || edge === "right";
34129
+ const length = Math.min(
34130
+ EDGE_HIT_LENGTH,
34131
+ (vertical ? rect.height : rect.width) - RESIZE_HANDLE_HIT_PX
34132
+ );
34133
+ if (length <= 0) return null;
34134
+ const pill = Math.min(EDGE_PILL_LENGTH, length);
34135
+ if (vertical) {
34136
+ return {
34137
+ hit: {
34138
+ left: edge === "left" ? rect.left - EDGE_HIT_THICKNESS : rect.left + rect.width,
34139
+ top: rect.top + (rect.height - length) / 2,
34140
+ width: EDGE_HIT_THICKNESS,
34141
+ height: length
34142
+ },
34143
+ cursor: "ew-resize",
34144
+ pill: { width: 4, height: pill }
34145
+ };
34082
34146
  }
34083
- return { left: rect.left + rect.width, top: cy, transform: "translate(0, -50%)" };
34084
- }
34085
- var EDGES = ["top", "right", "bottom", "left"];
34086
- function edgeHandleMetrics(vertical) {
34087
34147
  return {
34088
- hitWidth: vertical ? EDGE_HIT_THICKNESS : EDGE_HIT_LENGTH,
34089
- hitHeight: vertical ? EDGE_HIT_LENGTH : EDGE_HIT_THICKNESS,
34090
- cursor: vertical ? "ew-resize" : "ns-resize",
34091
- pillWidth: vertical ? 4 : 24,
34092
- pillHeight: vertical ? 24 : 4
34148
+ hit: {
34149
+ left: rect.left + (rect.width - length) / 2,
34150
+ top: edge === "top" ? rect.top - EDGE_HIT_THICKNESS : rect.top + rect.height,
34151
+ width: length,
34152
+ height: EDGE_HIT_THICKNESS
34153
+ },
34154
+ cursor: "ns-resize",
34155
+ pill: { width: pill, height: 4 }
34093
34156
  };
34094
34157
  }
34158
+ function repositionHandleSize(rect) {
34159
+ return Math.min(REPOSITION_HANDLE_PX, Math.max(rect.width, rect.height) - RESIZE_HANDLE_HIT_PX);
34160
+ }
34161
+ var EDGES = ["top", "right", "bottom", "left"];
34095
34162
  function DomEditCropHandles({
34096
34163
  selection,
34097
34164
  overlayRect,
@@ -34137,6 +34204,7 @@ function DomEditCropHandles({
34137
34204
  frame.scaleX,
34138
34205
  frame.scaleY
34139
34206
  );
34207
+ const repositionSize = repositionHandleSize(cropRect);
34140
34208
  const startCropGesture = (edge, event) => {
34141
34209
  if (!onStyleCommit) return;
34142
34210
  event.preventDefault();
@@ -34176,13 +34244,18 @@ function DomEditCropHandles({
34176
34244
  gesture.didMove = true;
34177
34245
  setState((prev) => ({ ...prev, insets: nextInsets }));
34178
34246
  };
34179
- const finishCropGesture = (event) => {
34247
+ const endCropGesture = (event) => {
34180
34248
  const gesture = gestureRef.current;
34181
- if (!gesture || gesture.pointerId !== event.pointerId) return;
34249
+ if (!gesture || gesture.pointerId !== event.pointerId) return null;
34182
34250
  event.preventDefault();
34183
34251
  event.stopPropagation();
34184
34252
  gestureRef.current = null;
34185
34253
  setDragging(false);
34254
+ return gesture;
34255
+ };
34256
+ const finishCropGesture = (event) => {
34257
+ const gesture = endCropGesture(event);
34258
+ if (!gesture) return;
34186
34259
  if (!gesture.didMove) return;
34187
34260
  const el = selection.element;
34188
34261
  const reLift = () => {
@@ -34197,12 +34270,8 @@ function DomEditCropHandles({
34197
34270
  }, reLift);
34198
34271
  };
34199
34272
  const cancelCropGesture = (event) => {
34200
- const gesture = gestureRef.current;
34201
- if (!gesture || gesture.pointerId !== event.pointerId) return;
34202
- event.preventDefault();
34203
- event.stopPropagation();
34204
- gestureRef.current = null;
34205
- setDragging(false);
34273
+ const gesture = endCropGesture(event);
34274
+ if (!gesture) return;
34206
34275
  setState((prev) => ({ ...prev, insets: gesture.startInsets }));
34207
34276
  };
34208
34277
  if (!state.croppable) return null;
@@ -34251,7 +34320,7 @@ function DomEditCropHandles({
34251
34320
  ] })
34252
34321
  }
34253
34322
  ),
34254
- hasCrop && /* @__PURE__ */ jsx84(
34323
+ hasCrop && repositionSize > 0 && /* @__PURE__ */ jsx84(
34255
34324
  "button",
34256
34325
  {
34257
34326
  type: "button",
@@ -34260,11 +34329,10 @@ function DomEditCropHandles({
34260
34329
  "data-dom-edit-crop-handle": "true",
34261
34330
  className: "pointer-events-auto absolute rounded-full border-2 border-studio-accent bg-studio-accent/30 shadow-[0_0_0_1px_rgba(0,0,0,0.4)]",
34262
34331
  style: {
34263
- left: cropRect.left + cropRect.width / 2,
34264
- top: cropRect.top + cropRect.height / 2,
34265
- width: 22,
34266
- height: 22,
34267
- transform: "translate(-50%, -50%)",
34332
+ left: cropRect.left + (cropRect.width - repositionSize) / 2,
34333
+ top: cropRect.top + (cropRect.height - repositionSize) / 2,
34334
+ width: repositionSize,
34335
+ height: repositionSize,
34268
34336
  cursor: "move",
34269
34337
  touchAction: "none"
34270
34338
  },
@@ -34275,10 +34343,9 @@ function DomEditCropHandles({
34275
34343
  }
34276
34344
  ),
34277
34345
  EDGES.map((edge) => {
34278
- const vertical = edge === "left" || edge === "right";
34279
- const place = edgeHandlePlacement(edge, cropRect);
34346
+ const layout = edgeHandleLayout(edge, cropRect);
34347
+ if (!layout) return null;
34280
34348
  const revealed = dragging || hasCrop || hotEdge === edge;
34281
- const m = edgeHandleMetrics(vertical);
34282
34349
  return /* @__PURE__ */ jsx84(
34283
34350
  "button",
34284
34351
  {
@@ -34288,12 +34355,8 @@ function DomEditCropHandles({
34288
34355
  "data-dom-edit-crop-handle": "true",
34289
34356
  className: "pointer-events-auto absolute flex items-center justify-center border-0 bg-transparent p-0",
34290
34357
  style: {
34291
- left: place.left,
34292
- top: place.top,
34293
- width: m.hitWidth,
34294
- height: m.hitHeight,
34295
- transform: place.transform,
34296
- cursor: m.cursor,
34358
+ ...layout.hit,
34359
+ cursor: layout.cursor,
34297
34360
  touchAction: "none"
34298
34361
  },
34299
34362
  onPointerEnter: () => setHotEdge(edge),
@@ -34307,8 +34370,7 @@ function DomEditCropHandles({
34307
34370
  {
34308
34371
  className: "pointer-events-none rounded-full bg-studio-accent/90 shadow-[0_0_0_1px_rgba(0,0,0,0.4)] transition-opacity duration-100",
34309
34372
  style: {
34310
- width: m.pillWidth,
34311
- height: m.pillHeight,
34373
+ ...layout.pill,
34312
34374
  opacity: revealed ? 1 : 0
34313
34375
  }
34314
34376
  }
@@ -34383,7 +34445,6 @@ var RESIZE_HANDLE_DEFS = [
34383
34445
  { handle: "sw", cursor: "nesw-resize", x: "left", y: "bottom" },
34384
34446
  { handle: "se", cursor: "nwse-resize", x: "right", y: "bottom" }
34385
34447
  ];
34386
- var RESIZE_HANDLE_HIT_PX = 16;
34387
34448
  var NO_CROP_INSET = { top: 0, right: 0, bottom: 0, left: 0 };
34388
34449
  function resizeHandleStyle(def, overlayRect, cropInset) {
34389
34450
  const half = RESIZE_HANDLE_HIT_PX / 2;
@@ -34397,6 +34458,7 @@ function DomEditGroupChrome({
34397
34458
  groupOverlayItems,
34398
34459
  groupBounds,
34399
34460
  allowCanvasMovement,
34461
+ allowBodyDrag,
34400
34462
  groupCanMove,
34401
34463
  gestures,
34402
34464
  onBoxMouseDown,
@@ -34428,10 +34490,10 @@ function DomEditGroupChrome({
34428
34490
  top: groupBounds.top,
34429
34491
  width: groupBounds.width,
34430
34492
  height: groupBounds.height,
34431
- cursor: canManipulate && groupCanMove ? "move" : "default"
34493
+ cursor: !allowBodyDrag ? void 0 : canManipulate && groupCanMove ? "move" : "default"
34432
34494
  },
34433
34495
  onPointerDown: (e) => {
34434
- if (!canManipulate || e.shiftKey) return;
34496
+ if (!canManipulate || e.shiftKey || !allowBodyDrag) return;
34435
34497
  gestures.startGroupDrag(e);
34436
34498
  },
34437
34499
  onMouseDown: onBoxMouseDown,
@@ -34444,6 +34506,7 @@ function DomEditSelectionChrome({
34444
34506
  selection,
34445
34507
  overlayRect,
34446
34508
  allowCanvasMovement,
34509
+ allowBodyDrag,
34447
34510
  cropOutlineInsetPx,
34448
34511
  boxRef,
34449
34512
  boxChromeClass,
@@ -34492,7 +34555,7 @@ function DomEditSelectionChrome({
34492
34555
  width: overlayRect.width,
34493
34556
  height: overlayRect.height,
34494
34557
  clipPath: boxClipPath,
34495
- cursor: canManipulate && selection.capabilities.canApplyManualOffset ? "move" : "default"
34558
+ cursor: !allowBodyDrag ? void 0 : canManipulate && selection.capabilities.canApplyManualOffset ? "move" : "default"
34496
34559
  },
34497
34560
  onPointerDown: (e) => {
34498
34561
  if (inlineText?.startFromPress(e)) {
@@ -34500,7 +34563,7 @@ function DomEditSelectionChrome({
34500
34563
  e.stopPropagation();
34501
34564
  return;
34502
34565
  }
34503
- if (!canManipulate || e.shiftKey) return;
34566
+ if (!allowBodyDrag || !canManipulate || e.shiftKey) return;
34504
34567
  if (selection.capabilities.canApplyManualOffset) {
34505
34568
  gestures.startGesture("drag", e);
34506
34569
  return;
@@ -35930,6 +35993,7 @@ var DomEditOverlay = memo25(function DomEditOverlay2({
35930
35993
  groupSelections = [],
35931
35994
  hoverSelection,
35932
35995
  allowCanvasMovement = true,
35996
+ allowBodyDrag = true,
35933
35997
  canvasInput = "overlay",
35934
35998
  onTextEditingChange,
35935
35999
  onSelectionBoxClick,
@@ -35950,6 +36014,7 @@ var DomEditOverlay = memo25(function DomEditOverlay2({
35950
36014
  }) {
35951
36015
  const readOnly = usePreviewReadOnly();
35952
36016
  const hostInput = canvasInput === "host";
36017
+ const bodyDrag = allowBodyDrag || !hostInput;
35953
36018
  const overlayRef = useRef68(null);
35954
36019
  const boxRef = useRef68(null);
35955
36020
  const onMarqueeSelectRef = useRef68(onMarqueeSelect);
@@ -36239,6 +36304,7 @@ var DomEditOverlay = memo25(function DomEditOverlay2({
36239
36304
  groupOverlayItems,
36240
36305
  groupBounds,
36241
36306
  allowCanvasMovement,
36307
+ allowBodyDrag: bodyDrag,
36242
36308
  groupCanMove,
36243
36309
  gestures,
36244
36310
  onBoxMouseDown: suppressBoxMouseDown,
@@ -36252,6 +36318,7 @@ var DomEditOverlay = memo25(function DomEditOverlay2({
36252
36318
  selection,
36253
36319
  overlayRect,
36254
36320
  allowCanvasMovement,
36321
+ allowBodyDrag: bodyDrag,
36255
36322
  cropOutlineInsetPx: cropOutlineInsetPx ?? void 0,
36256
36323
  boxRef,
36257
36324
  boxChromeClass,
@@ -38265,6 +38332,7 @@ function ConnectedDomEditOverlay({
38265
38332
  shouldShowSelectedDomBounds,
38266
38333
  isGestureRecording,
38267
38334
  canvasInput,
38335
+ allowBodyDrag,
38268
38336
  onTextEditingChange,
38269
38337
  onSelectionBoxClick
38270
38338
  }) {
@@ -38297,6 +38365,7 @@ function ConnectedDomEditOverlay({
38297
38365
  groupSelections: shouldShowSelectedDomBounds ? domEditGroupSelections : [],
38298
38366
  allowCanvasMovement: !isGestureRecording,
38299
38367
  canvasInput,
38368
+ allowBodyDrag,
38300
38369
  onTextEditingChange,
38301
38370
  onSelectionBoxClick,
38302
38371
  onCanvasMouseDown: handlePreviewCanvasMouseDown,
@@ -41004,7 +41073,7 @@ var SnapToolbar = memo27(function SnapToolbar2() {
41004
41073
  return /* @__PURE__ */ jsxs66(
41005
41074
  "div",
41006
41075
  {
41007
- className: "absolute top-2 right-2 z-50 flex items-center gap-1",
41076
+ className: "pointer-events-auto absolute top-2 right-2 z-50 flex items-center gap-1",
41008
41077
  onPointerDown: (e) => e.stopPropagation(),
41009
41078
  children: [
41010
41079
  motionPathCreateAvailable && /* @__PURE__ */ jsx95(
@@ -41270,7 +41339,7 @@ function PreviewOverlays({
41270
41339
  return /* @__PURE__ */ jsxs67(Fragment30, { children: [
41271
41340
  /* @__PURE__ */ jsx97(TopologyLens, { iframeRef: previewIframeRef, activeCompositionPath: activeCompPath }),
41272
41341
  /* @__PURE__ */ jsx97(CaptionOverlay, { iframeRef: previewIframeRef }),
41273
- /* @__PURE__ */ jsxs67("div", { className: "absolute top-2 left-1/2 -translate-x-1/2 z-60 flex items-center gap-2 rounded-full border border-studio-accent/40 bg-black/70 px-2.5 py-1", children: [
41342
+ /* @__PURE__ */ jsxs67("div", { className: "pointer-events-auto absolute top-2 left-1/2 -translate-x-1/2 z-60 flex items-center gap-2 rounded-full border border-studio-accent/40 bg-black/70 px-2.5 py-1", children: [
41274
41343
  /* @__PURE__ */ jsx97("span", { className: "h-1.5 w-1.5 rounded-full bg-studio-accent", "aria-hidden": "true" }),
41275
41344
  /* @__PURE__ */ jsx97("span", { className: "text-2xs text-neutral-200", children: "Editing captions" }),
41276
41345
  /* @__PURE__ */ jsx97(
@@ -41287,7 +41356,7 @@ function PreviewOverlays({
41287
41356
  "div",
41288
41357
  {
41289
41358
  role: "alert",
41290
- className: "absolute top-10 left-1/2 -translate-x-1/2 z-60 flex items-center gap-2 rounded-full border border-red-500/50 bg-red-950/90 px-2.5 py-1",
41359
+ className: "pointer-events-auto absolute top-10 left-1/2 -translate-x-1/2 z-60 flex items-center gap-2 rounded-full border border-red-500/50 bg-red-950/90 px-2.5 py-1",
41291
41360
  children: [
41292
41361
  /* @__PURE__ */ jsx97("span", { className: "text-2xs text-red-200", children: captionSyncError }),
41293
41362
  /* @__PURE__ */ jsx97(
@@ -41342,7 +41411,7 @@ function PreviewOverlays({
41342
41411
  {
41343
41412
  type: "button",
41344
41413
  onClick: enterCaptionMode,
41345
- className: "absolute top-2 left-1/2 -translate-x-1/2 z-60 rounded-full border border-neutral-700 bg-black/60 px-2.5 py-1 text-2xs text-neutral-300 transition-colors hover:border-studio-accent/50 hover:text-studio-accent focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-studio-accent",
41414
+ className: "pointer-events-auto absolute top-2 left-1/2 -translate-x-1/2 z-60 rounded-full border border-neutral-700 bg-black/60 px-2.5 py-1 text-2xs text-neutral-300 transition-colors hover:border-studio-accent/50 hover:text-studio-accent focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-studio-accent",
41346
41415
  children: "Edit captions"
41347
41416
  }
41348
41417
  )
@@ -68637,6 +68706,15 @@ function syncElementAttributes(target, source) {
68637
68706
  target.setAttribute(name, source.getAttribute(name) ?? "");
68638
68707
  }
68639
68708
  }
68709
+ function syncStaleEditMarks(doc, restored) {
68710
+ const restoredDoc = parseSavedSource(restored);
68711
+ for (const live of doc.querySelectorAll(STUDIO_EDIT_ATTRS.map((attr) => `[${attr}]`).join())) {
68712
+ const source = findAuthoredElement(restoredDoc, live);
68713
+ if (source && STUDIO_EDIT_ATTRS.some((a) => live.getAttribute(a) !== source.getAttribute(a))) {
68714
+ syncElementAttributes(live, source);
68715
+ }
68716
+ }
68717
+ }
68640
68718
  function readGsapScriptTexts(html2) {
68641
68719
  const doc = new DOMParser().parseFromString(html2, "text/html");
68642
68720
  return findGsapScriptElements(doc).map((script) => script.textContent ?? "");
@@ -68694,9 +68772,11 @@ function applyUndoRestoreToPreview(iframe, activeCompPath, files, currentTime, r
68694
68772
  const restoredScript = extractGsapScriptText(restored);
68695
68773
  const previousScript = extractGsapScriptText(previous);
68696
68774
  if (restoredScript && restoredScript !== previousScript) {
68775
+ syncStaleEditMarks(doc, restored);
68697
68776
  const result = applySoftReload(iframe, restoredScript, {
68698
68777
  onAsyncFailure: reloadPreview,
68699
- currentTimeOverride: currentTime
68778
+ currentTimeOverride: currentTime,
68779
+ authoredHtml: restored
68700
68780
  });
68701
68781
  if (result === "cannot-soft-reload") {
68702
68782
  reloadPreview();
@@ -76591,7 +76671,9 @@ async function tryGsapResizeIntercept(selection, size, animations, iframe, commi
76591
76671
  const sizeSet = resizeGroup === "size" ? anim ?? findSizeSetAnimation(workingAnimations, sel, selection.element) : findSizeSetAnimation(workingAnimations, sel, selection.element);
76592
76672
  if (resizeGroup === "size") {
76593
76673
  const animatedTween = pickClosestToPlayhead(
76594
- workingAnimations.filter((a) => !isInstantHold(a) && resolveTweenDuration(a) > 0)
76674
+ workingAnimations.filter(
76675
+ (a) => !isInstantHold(a) && resolveTweenDuration(a) > 0 && animationWritesAnyProperty(a, resizeProperties)
76676
+ )
76595
76677
  );
76596
76678
  if (animatedTween) {
76597
76679
  logResize("intercept-route", { route: "keyframed-size", tweenId: animatedTween.id });
@@ -90549,6 +90631,7 @@ function TimelineToolbar({
90549
90631
  domEditSession,
90550
90632
  onSplitElement,
90551
90633
  history: history2,
90634
+ showHistory = true,
90552
90635
  showAddBeat = true,
90553
90636
  showKeyframes = true
90554
90637
  }) {
@@ -90608,7 +90691,7 @@ function TimelineToolbar({
90608
90691
  // position (tracks right below) makes it self-evident.
90609
90692
  /* @__PURE__ */ jsx227("div", { className: "border-b border-neutral-800/60", children: /* @__PURE__ */ jsxs180("div", { className: "flex items-center justify-between px-2 py-0.5", children: [
90610
90693
  /* @__PURE__ */ jsxs180("div", { className: "flex items-center gap-0.5", children: [
90611
- /* @__PURE__ */ jsx227(TimelineHistoryButtons, { ...history2 }),
90694
+ showHistory && /* @__PURE__ */ jsx227(TimelineHistoryButtons, { ...history2 }),
90612
90695
  /* @__PURE__ */ jsx227(TimelineToolPicker, {}),
90613
90696
  /* @__PURE__ */ jsx227("div", { "aria-hidden": "true", className: "mx-1 h-4 w-px bg-neutral-800" }),
90614
90697
  /* @__PURE__ */ jsx227(Tooltip, { label: timelineSnapEnabled ? "Snapping on (N)" : "Snapping off (N)", children: /* @__PURE__ */ jsx227(
@@ -92250,12 +92333,14 @@ export {
92250
92333
  RenderQueue,
92251
92334
  SNAP_THRESHOLD_PX,
92252
92335
  Select,
92336
+ ShortcutsButton,
92253
92337
  Slider,
92254
92338
  SnapGuideOverlay,
92255
92339
  SnapToolbar,
92256
92340
  SourceEditor,
92257
92341
  StudioApp,
92258
92342
  StudioFileConflictError,
92343
+ TRACK_H,
92259
92344
  Tab,
92260
92345
  TabPanel,
92261
92346
  Tabs,