@hyperframes/studio 0.8.93 → 0.8.94

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 (39) hide show
  1. package/dist/assets/{hyperframes-player-CkV9KK6A.js → hyperframes-player-Bkm-3G56.js} +1 -1
  2. package/dist/assets/{index-BlLFipOk.js → index-BUn5H_np.js} +1 -1
  3. package/dist/assets/{index-CYk0NwSv.js → index-BupvpoiM.js} +175 -175
  4. package/dist/assets/{index-V3fzEfOD.js → index-n2ZRiO44.js} +1 -1
  5. package/dist/index.d.ts +62 -46
  6. package/dist/index.html +1 -1
  7. package/dist/index.js +142 -98
  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/nle/PreviewOverlays.tsx +3 -3
  25. package/src/hooks/gsapResizeIntercept.test.ts +40 -0
  26. package/src/hooks/gsapResizeIntercept.ts +8 -4
  27. package/src/index.ts +4 -0
  28. package/src/player/components/PlayerControls.tsx +2 -15
  29. package/src/player/components/ShortcutsPanel.test.tsx +33 -1
  30. package/src/player/components/ShortcutsPanel.tsx +26 -0
  31. package/src/player/components/Timeline.trackPadding.test.tsx +157 -0
  32. package/src/player/components/TimelineCanvas.tsx +16 -19
  33. package/src/player/components/TimelineTypes.ts +2 -0
  34. package/src/player/components/timelineLayout.test.ts +47 -0
  35. package/src/player/components/timelineLayout.ts +22 -15
  36. package/src/player/components/useTimelineProviderState.tsx +2 -0
  37. package/src/player/components/useTimelineTrackLayout.ts +11 -2
  38. package/src/player/components/useTimelineVirtualRows.test.tsx +32 -0
  39. package/src/player/components/useTimelineVirtualRows.ts +2 -2
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
  }
@@ -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,6 +12806,7 @@ function useTimelineProviderState({
12797
12806
  theme: themeOverrides,
12798
12807
  showAudioEffects = true,
12799
12808
  showKeyframes = true,
12809
+ trackPadding,
12800
12810
  sessionEpoch = 0,
12801
12811
  previewIframeRef,
12802
12812
  onZIndexReorder
@@ -12874,7 +12884,8 @@ function useTimelineProviderState({
12874
12884
  timelineElements,
12875
12885
  gsapAnimations,
12876
12886
  selectedElementId,
12877
- selectedElementIds
12887
+ selectedElementIds,
12888
+ trackPadding
12878
12889
  );
12879
12890
  const timelineElementsRef = useRef25(timelineElements);
12880
12891
  timelineElementsRef.current = timelineElements;
@@ -21239,7 +21250,13 @@ var TimelineCanvas = memo13(function TimelineCanvas2() {
21239
21250
  style: { height: props.totalH, width: props.contentOrigin + props.trackContentWidth },
21240
21251
  children: [
21241
21252
  /* @__PURE__ */ jsx55(TimelineRulerPart, {}),
21242
- /* @__PURE__ */ jsx55("div", { "aria-hidden": "true", style: { height: props.rowsVirtualized ? 0 : TRACKS_TOP_PAD } }),
21253
+ /* @__PURE__ */ jsx55(
21254
+ "div",
21255
+ {
21256
+ "aria-hidden": "true",
21257
+ style: { height: props.rowsVirtualized ? 0 : props.rowGeometry.padding.top }
21258
+ }
21259
+ ),
21243
21260
  /* @__PURE__ */ jsx55(
21244
21261
  TimelineLanes,
21245
21262
  {
@@ -21257,7 +21274,13 @@ var TimelineCanvas = memo13(function TimelineCanvas2() {
21257
21274
  onRazorSplitAll: props.onRazorSplitAll
21258
21275
  }
21259
21276
  ),
21260
- /* @__PURE__ */ jsx55("div", { "aria-hidden": "true", style: { height: props.rowsVirtualized ? 0 : TRACKS_BOTTOM_PAD } }),
21277
+ /* @__PURE__ */ jsx55(
21278
+ "div",
21279
+ {
21280
+ "aria-hidden": "true",
21281
+ style: { height: props.rowsVirtualized ? 0 : props.rowGeometry.padding.bottom }
21282
+ }
21283
+ ),
21261
21284
  props.laneGapStrips.map((strip) => {
21262
21285
  const rowIndex = displayTrackOrder2.indexOf(strip.track);
21263
21286
  if (rowIndex < 0) return null;
@@ -21270,7 +21293,7 @@ var TimelineCanvas = memo13(function TimelineCanvas2() {
21270
21293
  {
21271
21294
  className: "pointer-events-none absolute",
21272
21295
  style: {
21273
- top: getTimelineRowTop(rowIndex, props.rowHeights) + CLIP_Y,
21296
+ top: props.rowGeometry.getRowTop(rowIndex) + CLIP_Y,
21274
21297
  left: props.contentOrigin + gap.start * props.pps,
21275
21298
  width: Math.max((gap.end - gap.start) * props.pps, 2),
21276
21299
  height: TRACK_H - CLIP_Y * 2,
@@ -21287,7 +21310,7 @@ var TimelineCanvas = memo13(function TimelineCanvas2() {
21287
21310
  {
21288
21311
  className: "absolute pointer-events-none",
21289
21312
  style: {
21290
- top: getTimelineRowTop(draggedRowIndex, props.rowHeights) + CLIP_Y,
21313
+ top: props.rowGeometry.getRowTop(draggedRowIndex) + CLIP_Y,
21291
21314
  left: props.contentOrigin + draggedClip.previewStart * props.pps,
21292
21315
  width: Math.max(draggedClip.element.duration * props.pps, 4),
21293
21316
  height: draggedClipHeight,
@@ -21305,7 +21328,7 @@ var TimelineCanvas = memo13(function TimelineCanvas2() {
21305
21328
  "data-testid": "timeline-drop-preview",
21306
21329
  className: "absolute pointer-events-none",
21307
21330
  style: {
21308
- top: getTimelineRowTop(dropRowIndex, props.rowHeights) + CLIP_Y,
21331
+ top: props.rowGeometry.getRowTop(dropRowIndex) + CLIP_Y,
21309
21332
  left: props.contentOrigin + props.dropPreview.start * props.pps,
21310
21333
  width: DROP_PREVIEW_SECONDS * props.pps,
21311
21334
  height: TRACK_H - CLIP_Y * 2,
@@ -21322,7 +21345,7 @@ var TimelineCanvas = memo13(function TimelineCanvas2() {
21322
21345
  "data-testid": "timeline-insert-line",
21323
21346
  className: "absolute pointer-events-none",
21324
21347
  style: {
21325
- top: getTimelineRowTop(insertLineRow, props.rowHeights) - 0.5,
21348
+ top: props.rowGeometry.getRowTop(insertLineRow) - 0.5,
21326
21349
  left: props.contentOrigin,
21327
21350
  width: props.trackContentWidth,
21328
21351
  height: 1,
@@ -28039,7 +28062,7 @@ function PreviewOverlayProvider({ iframe, children }) {
28039
28062
  toggleSafeMargins: () => usePreviewGuidesStore.getState().toggle("safeMarginsVisible")
28040
28063
  }
28041
28064
  };
28042
- return /* @__PURE__ */ jsx74(PreviewOverlayContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx74("div", { ref: overlayRef, className: "absolute inset-0", children }) });
28065
+ return /* @__PURE__ */ jsx74(PreviewOverlayContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx74("div", { ref: overlayRef, className: "pointer-events-none absolute inset-0", children }) });
28043
28066
  }
28044
28067
  function usePreviewOverlayContext() {
28045
28068
  const context = useContext8(PreviewOverlayContext);
@@ -30110,6 +30133,7 @@ function readThroughPass(memo53, key2, compute) {
30110
30133
  }
30111
30134
 
30112
30135
  // src/components/editor/domEditOverlayGeometry.ts
30136
+ var RESIZE_HANDLE_HIT_PX = 16;
30113
30137
  function isElementVisibleForOverlay(el) {
30114
30138
  return isElementVisibleThroughAncestors(el);
30115
30139
  }
@@ -34068,30 +34092,43 @@ import { useEffect as useEffect43, useRef as useRef64, useState as useState40 }
34068
34092
  import { Fragment as Fragment26, jsx as jsx84, jsxs as jsxs57 } from "react/jsx-runtime";
34069
34093
  var EDGE_HIT_THICKNESS = 12;
34070
34094
  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%)" };
34095
+ var EDGE_PILL_LENGTH = 24;
34096
+ var REPOSITION_HANDLE_PX = 22;
34097
+ function edgeHandleLayout(edge, rect) {
34098
+ const vertical = edge === "left" || edge === "right";
34099
+ const length = Math.min(
34100
+ EDGE_HIT_LENGTH,
34101
+ (vertical ? rect.height : rect.width) - RESIZE_HANDLE_HIT_PX
34102
+ );
34103
+ if (length <= 0) return null;
34104
+ const pill = Math.min(EDGE_PILL_LENGTH, length);
34105
+ if (vertical) {
34106
+ return {
34107
+ hit: {
34108
+ left: edge === "left" ? rect.left - EDGE_HIT_THICKNESS : rect.left + rect.width,
34109
+ top: rect.top + (rect.height - length) / 2,
34110
+ width: EDGE_HIT_THICKNESS,
34111
+ height: length
34112
+ },
34113
+ cursor: "ew-resize",
34114
+ pill: { width: 4, height: pill }
34115
+ };
34082
34116
  }
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
34117
  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
34118
+ hit: {
34119
+ left: rect.left + (rect.width - length) / 2,
34120
+ top: edge === "top" ? rect.top - EDGE_HIT_THICKNESS : rect.top + rect.height,
34121
+ width: length,
34122
+ height: EDGE_HIT_THICKNESS
34123
+ },
34124
+ cursor: "ns-resize",
34125
+ pill: { width: pill, height: 4 }
34093
34126
  };
34094
34127
  }
34128
+ function repositionHandleSize(rect) {
34129
+ return Math.min(REPOSITION_HANDLE_PX, Math.max(rect.width, rect.height) - RESIZE_HANDLE_HIT_PX);
34130
+ }
34131
+ var EDGES = ["top", "right", "bottom", "left"];
34095
34132
  function DomEditCropHandles({
34096
34133
  selection,
34097
34134
  overlayRect,
@@ -34137,6 +34174,7 @@ function DomEditCropHandles({
34137
34174
  frame.scaleX,
34138
34175
  frame.scaleY
34139
34176
  );
34177
+ const repositionSize = repositionHandleSize(cropRect);
34140
34178
  const startCropGesture = (edge, event) => {
34141
34179
  if (!onStyleCommit) return;
34142
34180
  event.preventDefault();
@@ -34176,13 +34214,18 @@ function DomEditCropHandles({
34176
34214
  gesture.didMove = true;
34177
34215
  setState((prev) => ({ ...prev, insets: nextInsets }));
34178
34216
  };
34179
- const finishCropGesture = (event) => {
34217
+ const endCropGesture = (event) => {
34180
34218
  const gesture = gestureRef.current;
34181
- if (!gesture || gesture.pointerId !== event.pointerId) return;
34219
+ if (!gesture || gesture.pointerId !== event.pointerId) return null;
34182
34220
  event.preventDefault();
34183
34221
  event.stopPropagation();
34184
34222
  gestureRef.current = null;
34185
34223
  setDragging(false);
34224
+ return gesture;
34225
+ };
34226
+ const finishCropGesture = (event) => {
34227
+ const gesture = endCropGesture(event);
34228
+ if (!gesture) return;
34186
34229
  if (!gesture.didMove) return;
34187
34230
  const el = selection.element;
34188
34231
  const reLift = () => {
@@ -34197,12 +34240,8 @@ function DomEditCropHandles({
34197
34240
  }, reLift);
34198
34241
  };
34199
34242
  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);
34243
+ const gesture = endCropGesture(event);
34244
+ if (!gesture) return;
34206
34245
  setState((prev) => ({ ...prev, insets: gesture.startInsets }));
34207
34246
  };
34208
34247
  if (!state.croppable) return null;
@@ -34251,7 +34290,7 @@ function DomEditCropHandles({
34251
34290
  ] })
34252
34291
  }
34253
34292
  ),
34254
- hasCrop && /* @__PURE__ */ jsx84(
34293
+ hasCrop && repositionSize > 0 && /* @__PURE__ */ jsx84(
34255
34294
  "button",
34256
34295
  {
34257
34296
  type: "button",
@@ -34260,11 +34299,10 @@ function DomEditCropHandles({
34260
34299
  "data-dom-edit-crop-handle": "true",
34261
34300
  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
34301
  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%)",
34302
+ left: cropRect.left + (cropRect.width - repositionSize) / 2,
34303
+ top: cropRect.top + (cropRect.height - repositionSize) / 2,
34304
+ width: repositionSize,
34305
+ height: repositionSize,
34268
34306
  cursor: "move",
34269
34307
  touchAction: "none"
34270
34308
  },
@@ -34275,10 +34313,9 @@ function DomEditCropHandles({
34275
34313
  }
34276
34314
  ),
34277
34315
  EDGES.map((edge) => {
34278
- const vertical = edge === "left" || edge === "right";
34279
- const place = edgeHandlePlacement(edge, cropRect);
34316
+ const layout = edgeHandleLayout(edge, cropRect);
34317
+ if (!layout) return null;
34280
34318
  const revealed = dragging || hasCrop || hotEdge === edge;
34281
- const m = edgeHandleMetrics(vertical);
34282
34319
  return /* @__PURE__ */ jsx84(
34283
34320
  "button",
34284
34321
  {
@@ -34288,12 +34325,8 @@ function DomEditCropHandles({
34288
34325
  "data-dom-edit-crop-handle": "true",
34289
34326
  className: "pointer-events-auto absolute flex items-center justify-center border-0 bg-transparent p-0",
34290
34327
  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,
34328
+ ...layout.hit,
34329
+ cursor: layout.cursor,
34297
34330
  touchAction: "none"
34298
34331
  },
34299
34332
  onPointerEnter: () => setHotEdge(edge),
@@ -34307,8 +34340,7 @@ function DomEditCropHandles({
34307
34340
  {
34308
34341
  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
34342
  style: {
34310
- width: m.pillWidth,
34311
- height: m.pillHeight,
34343
+ ...layout.pill,
34312
34344
  opacity: revealed ? 1 : 0
34313
34345
  }
34314
34346
  }
@@ -34383,7 +34415,6 @@ var RESIZE_HANDLE_DEFS = [
34383
34415
  { handle: "sw", cursor: "nesw-resize", x: "left", y: "bottom" },
34384
34416
  { handle: "se", cursor: "nwse-resize", x: "right", y: "bottom" }
34385
34417
  ];
34386
- var RESIZE_HANDLE_HIT_PX = 16;
34387
34418
  var NO_CROP_INSET = { top: 0, right: 0, bottom: 0, left: 0 };
34388
34419
  function resizeHandleStyle(def, overlayRect, cropInset) {
34389
34420
  const half = RESIZE_HANDLE_HIT_PX / 2;
@@ -34397,6 +34428,7 @@ function DomEditGroupChrome({
34397
34428
  groupOverlayItems,
34398
34429
  groupBounds,
34399
34430
  allowCanvasMovement,
34431
+ allowBodyDrag,
34400
34432
  groupCanMove,
34401
34433
  gestures,
34402
34434
  onBoxMouseDown,
@@ -34428,10 +34460,10 @@ function DomEditGroupChrome({
34428
34460
  top: groupBounds.top,
34429
34461
  width: groupBounds.width,
34430
34462
  height: groupBounds.height,
34431
- cursor: canManipulate && groupCanMove ? "move" : "default"
34463
+ cursor: !allowBodyDrag ? void 0 : canManipulate && groupCanMove ? "move" : "default"
34432
34464
  },
34433
34465
  onPointerDown: (e) => {
34434
- if (!canManipulate || e.shiftKey) return;
34466
+ if (!canManipulate || e.shiftKey || !allowBodyDrag) return;
34435
34467
  gestures.startGroupDrag(e);
34436
34468
  },
34437
34469
  onMouseDown: onBoxMouseDown,
@@ -34444,6 +34476,7 @@ function DomEditSelectionChrome({
34444
34476
  selection,
34445
34477
  overlayRect,
34446
34478
  allowCanvasMovement,
34479
+ allowBodyDrag,
34447
34480
  cropOutlineInsetPx,
34448
34481
  boxRef,
34449
34482
  boxChromeClass,
@@ -34492,7 +34525,7 @@ function DomEditSelectionChrome({
34492
34525
  width: overlayRect.width,
34493
34526
  height: overlayRect.height,
34494
34527
  clipPath: boxClipPath,
34495
- cursor: canManipulate && selection.capabilities.canApplyManualOffset ? "move" : "default"
34528
+ cursor: !allowBodyDrag ? void 0 : canManipulate && selection.capabilities.canApplyManualOffset ? "move" : "default"
34496
34529
  },
34497
34530
  onPointerDown: (e) => {
34498
34531
  if (inlineText?.startFromPress(e)) {
@@ -34500,7 +34533,7 @@ function DomEditSelectionChrome({
34500
34533
  e.stopPropagation();
34501
34534
  return;
34502
34535
  }
34503
- if (!canManipulate || e.shiftKey) return;
34536
+ if (!allowBodyDrag || !canManipulate || e.shiftKey) return;
34504
34537
  if (selection.capabilities.canApplyManualOffset) {
34505
34538
  gestures.startGesture("drag", e);
34506
34539
  return;
@@ -35930,6 +35963,7 @@ var DomEditOverlay = memo25(function DomEditOverlay2({
35930
35963
  groupSelections = [],
35931
35964
  hoverSelection,
35932
35965
  allowCanvasMovement = true,
35966
+ allowBodyDrag = true,
35933
35967
  canvasInput = "overlay",
35934
35968
  onTextEditingChange,
35935
35969
  onSelectionBoxClick,
@@ -35950,6 +35984,7 @@ var DomEditOverlay = memo25(function DomEditOverlay2({
35950
35984
  }) {
35951
35985
  const readOnly = usePreviewReadOnly();
35952
35986
  const hostInput = canvasInput === "host";
35987
+ const bodyDrag = allowBodyDrag || !hostInput;
35953
35988
  const overlayRef = useRef68(null);
35954
35989
  const boxRef = useRef68(null);
35955
35990
  const onMarqueeSelectRef = useRef68(onMarqueeSelect);
@@ -36239,6 +36274,7 @@ var DomEditOverlay = memo25(function DomEditOverlay2({
36239
36274
  groupOverlayItems,
36240
36275
  groupBounds,
36241
36276
  allowCanvasMovement,
36277
+ allowBodyDrag: bodyDrag,
36242
36278
  groupCanMove,
36243
36279
  gestures,
36244
36280
  onBoxMouseDown: suppressBoxMouseDown,
@@ -36252,6 +36288,7 @@ var DomEditOverlay = memo25(function DomEditOverlay2({
36252
36288
  selection,
36253
36289
  overlayRect,
36254
36290
  allowCanvasMovement,
36291
+ allowBodyDrag: bodyDrag,
36255
36292
  cropOutlineInsetPx: cropOutlineInsetPx ?? void 0,
36256
36293
  boxRef,
36257
36294
  boxChromeClass,
@@ -38265,6 +38302,7 @@ function ConnectedDomEditOverlay({
38265
38302
  shouldShowSelectedDomBounds,
38266
38303
  isGestureRecording,
38267
38304
  canvasInput,
38305
+ allowBodyDrag,
38268
38306
  onTextEditingChange,
38269
38307
  onSelectionBoxClick
38270
38308
  }) {
@@ -38297,6 +38335,7 @@ function ConnectedDomEditOverlay({
38297
38335
  groupSelections: shouldShowSelectedDomBounds ? domEditGroupSelections : [],
38298
38336
  allowCanvasMovement: !isGestureRecording,
38299
38337
  canvasInput,
38338
+ allowBodyDrag,
38300
38339
  onTextEditingChange,
38301
38340
  onSelectionBoxClick,
38302
38341
  onCanvasMouseDown: handlePreviewCanvasMouseDown,
@@ -41004,7 +41043,7 @@ var SnapToolbar = memo27(function SnapToolbar2() {
41004
41043
  return /* @__PURE__ */ jsxs66(
41005
41044
  "div",
41006
41045
  {
41007
- className: "absolute top-2 right-2 z-50 flex items-center gap-1",
41046
+ className: "pointer-events-auto absolute top-2 right-2 z-50 flex items-center gap-1",
41008
41047
  onPointerDown: (e) => e.stopPropagation(),
41009
41048
  children: [
41010
41049
  motionPathCreateAvailable && /* @__PURE__ */ jsx95(
@@ -41270,7 +41309,7 @@ function PreviewOverlays({
41270
41309
  return /* @__PURE__ */ jsxs67(Fragment30, { children: [
41271
41310
  /* @__PURE__ */ jsx97(TopologyLens, { iframeRef: previewIframeRef, activeCompositionPath: activeCompPath }),
41272
41311
  /* @__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: [
41312
+ /* @__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
41313
  /* @__PURE__ */ jsx97("span", { className: "h-1.5 w-1.5 rounded-full bg-studio-accent", "aria-hidden": "true" }),
41275
41314
  /* @__PURE__ */ jsx97("span", { className: "text-2xs text-neutral-200", children: "Editing captions" }),
41276
41315
  /* @__PURE__ */ jsx97(
@@ -41287,7 +41326,7 @@ function PreviewOverlays({
41287
41326
  "div",
41288
41327
  {
41289
41328
  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",
41329
+ 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
41330
  children: [
41292
41331
  /* @__PURE__ */ jsx97("span", { className: "text-2xs text-red-200", children: captionSyncError }),
41293
41332
  /* @__PURE__ */ jsx97(
@@ -41342,7 +41381,7 @@ function PreviewOverlays({
41342
41381
  {
41343
41382
  type: "button",
41344
41383
  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",
41384
+ 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
41385
  children: "Edit captions"
41347
41386
  }
41348
41387
  )
@@ -76591,7 +76630,9 @@ async function tryGsapResizeIntercept(selection, size, animations, iframe, commi
76591
76630
  const sizeSet = resizeGroup === "size" ? anim ?? findSizeSetAnimation(workingAnimations, sel, selection.element) : findSizeSetAnimation(workingAnimations, sel, selection.element);
76592
76631
  if (resizeGroup === "size") {
76593
76632
  const animatedTween = pickClosestToPlayhead(
76594
- workingAnimations.filter((a) => !isInstantHold(a) && resolveTweenDuration(a) > 0)
76633
+ workingAnimations.filter(
76634
+ (a) => !isInstantHold(a) && resolveTweenDuration(a) > 0 && animationWritesAnyProperty(a, resizeProperties)
76635
+ )
76595
76636
  );
76596
76637
  if (animatedTween) {
76597
76638
  logResize("intercept-route", { route: "keyframed-size", tweenId: animatedTween.id });
@@ -90549,6 +90590,7 @@ function TimelineToolbar({
90549
90590
  domEditSession,
90550
90591
  onSplitElement,
90551
90592
  history: history2,
90593
+ showHistory = true,
90552
90594
  showAddBeat = true,
90553
90595
  showKeyframes = true
90554
90596
  }) {
@@ -90608,7 +90650,7 @@ function TimelineToolbar({
90608
90650
  // position (tracks right below) makes it self-evident.
90609
90651
  /* @__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
90652
  /* @__PURE__ */ jsxs180("div", { className: "flex items-center gap-0.5", children: [
90611
- /* @__PURE__ */ jsx227(TimelineHistoryButtons, { ...history2 }),
90653
+ showHistory && /* @__PURE__ */ jsx227(TimelineHistoryButtons, { ...history2 }),
90612
90654
  /* @__PURE__ */ jsx227(TimelineToolPicker, {}),
90613
90655
  /* @__PURE__ */ jsx227("div", { "aria-hidden": "true", className: "mx-1 h-4 w-px bg-neutral-800" }),
90614
90656
  /* @__PURE__ */ jsx227(Tooltip, { label: timelineSnapEnabled ? "Snapping on (N)" : "Snapping off (N)", children: /* @__PURE__ */ jsx227(
@@ -92250,12 +92292,14 @@ export {
92250
92292
  RenderQueue,
92251
92293
  SNAP_THRESHOLD_PX,
92252
92294
  Select,
92295
+ ShortcutsButton,
92253
92296
  Slider,
92254
92297
  SnapGuideOverlay,
92255
92298
  SnapToolbar,
92256
92299
  SourceEditor,
92257
92300
  StudioApp,
92258
92301
  StudioFileConflictError,
92302
+ TRACK_H,
92259
92303
  Tab,
92260
92304
  TabPanel,
92261
92305
  Tabs,