hyperframes 0.7.18 → 0.7.19

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.
@@ -21,9 +21,9 @@ import {
21
21
 
22
22
  // src/components/nle/NLELayout.tsx
23
23
  import {
24
- useState as useState17,
24
+ useState as useState18,
25
25
  useCallback as useCallback20,
26
- useRef as useRef20,
26
+ useRef as useRef21,
27
27
  useEffect as useEffect14,
28
28
  useSyncExternalStore,
29
29
  memo as memo15
@@ -572,6 +572,8 @@ Player.displayName = "Player";
572
572
 
573
573
  // src/player/components/PlayerControls.tsx
574
574
  import { useRef as useRef5, useCallback as useCallback4, useEffect as useEffect5, memo as memo3 } from "react";
575
+ import gsap from "gsap";
576
+ import { MorphSVGPlugin } from "gsap/MorphSVGPlugin";
575
577
 
576
578
  // src/player/lib/time.ts
577
579
  var STUDIO_PREVIEW_FPS = 30;
@@ -2068,14 +2070,28 @@ function useSeekBarDrag(refs, onSeek, disabled, duration) {
2068
2070
  // src/player/components/PlayerControls.tsx
2069
2071
  import { useState as useState5 } from "react";
2070
2072
  import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2071
- function PlayIcon() {
2072
- return /* @__PURE__ */ jsx7("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "#FAFAFA", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("polygon", { points: "6,3 20,12 6,21" }) });
2073
- }
2074
- function PauseIcon() {
2075
- return /* @__PURE__ */ jsxs7("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "#FAFAFA", "aria-hidden": "true", children: [
2076
- /* @__PURE__ */ jsx7("rect", { x: "6", y: "4", width: "4", height: "16", rx: "1" }),
2077
- /* @__PURE__ */ jsx7("rect", { x: "14", y: "4", width: "4", height: "16", rx: "1" })
2078
- ] });
2073
+ gsap.registerPlugin(MorphSVGPlugin);
2074
+ var PLAY_BLADE_D = "M87.5129 57.5141L56.9696 73.5433C52.8371 75.7098 48.7046 73.2553 49.6688 69.2104L58.9483 30.1391C59.9125 26.0942 65.2097 23.6397 68.3154 25.8062L91.2447 41.8354C96.4668 45.4796 94.4631 53.8699 87.5129 57.5141Z";
2075
+ var PAUSE_BARS_D = "M56 28H67V71H56Z M73 28H84V71H73Z";
2076
+ function PlayPauseMorphIcon({ playing }) {
2077
+ const pathRef = useRef5(null);
2078
+ const isFirstRun = useRef5(true);
2079
+ useEffect5(() => {
2080
+ const el = pathRef.current;
2081
+ if (!el) return;
2082
+ const target = playing ? PAUSE_BARS_D : PLAY_BLADE_D;
2083
+ const reduceMotion = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
2084
+ if (isFirstRun.current || reduceMotion) {
2085
+ isFirstRun.current = false;
2086
+ gsap.set(el, { morphSVG: target });
2087
+ return;
2088
+ }
2089
+ const tween = gsap.to(el, { duration: 0.28, ease: "power2.inOut", morphSVG: target });
2090
+ return () => {
2091
+ tween.kill();
2092
+ };
2093
+ }, [playing]);
2094
+ return /* @__PURE__ */ jsx7("span", { className: "relative inline-flex h-3 w-3 items-center justify-center", "aria-hidden": "true", children: /* @__PURE__ */ jsx7("svg", { width: "12", height: "12", viewBox: "46 21 54 56", fill: "#FAFAFA", children: /* @__PURE__ */ jsx7("path", { ref: pathRef, d: playing ? PAUSE_BARS_D : PLAY_BLADE_D }) }) });
2079
2095
  }
2080
2096
  var MuteButton = memo3(function MuteButton2({
2081
2097
  audioMuted,
@@ -2402,7 +2418,7 @@ var PlayerControls = memo3(function PlayerControls2({
2402
2418
  disabled: controlsDisabled,
2403
2419
  className: "flex-shrink-0 w-8 h-8 flex items-center justify-center rounded-lg disabled:opacity-30 disabled:pointer-events-none transition-colors",
2404
2420
  style: { background: "rgba(255,255,255,0.06)" },
2405
- children: isPlaying ? /* @__PURE__ */ jsx7(PauseIcon, {}) : /* @__PURE__ */ jsx7(PlayIcon, {})
2421
+ children: /* @__PURE__ */ jsx7(PlayPauseMorphIcon, { playing: isPlaying })
2406
2422
  }
2407
2423
  ) }),
2408
2424
  /* @__PURE__ */ jsx7(
@@ -2481,7 +2497,7 @@ var PlayerControls = memo3(function PlayerControls2({
2481
2497
  });
2482
2498
 
2483
2499
  // src/player/components/Timeline.tsx
2484
- import { useRef as useRef13, useMemo as useMemo8, useCallback as useCallback11, useState as useState11, useEffect as useEffect9, memo as memo11 } from "react";
2500
+ import { useRef as useRef14, useMemo as useMemo8, useCallback as useCallback11, useState as useState12, useEffect as useEffect9, memo as memo11 } from "react";
2485
2501
 
2486
2502
  // src/hooks/useMusicBeatAnalysis.ts
2487
2503
  import { useEffect as useEffect6, useMemo as useMemo2, useRef as useRef6 } from "react";
@@ -4783,7 +4799,40 @@ var TimelineClip = memo5(function TimelineClip2({
4783
4799
  });
4784
4800
 
4785
4801
  // src/player/components/TimelineClipDiamonds.tsx
4786
- import { memo as memo6 } from "react";
4802
+ import { memo as memo6, useRef as useRef11, useState as useState10 } from "react";
4803
+
4804
+ // src/components/editor/keyframeDrag.ts
4805
+ var KEYFRAME_DRAG_THRESHOLD_PX = 4;
4806
+ var NOOP_EPSILON_PCT = 0.1;
4807
+ var NEIGHBOR_EPSILON_PCT = 0.5;
4808
+ var clamp2 = (n, lo, hi) => Math.max(lo, Math.min(hi, n));
4809
+ function clampToNeighbors(clipPct, sortedClipPcts, draggedIndex) {
4810
+ const left = draggedIndex > 0 ? (sortedClipPcts[draggedIndex - 1] ?? 0) + NEIGHBOR_EPSILON_PCT : 0;
4811
+ const right = draggedIndex < sortedClipPcts.length - 1 ? (sortedClipPcts[draggedIndex + 1] ?? 100) - NEIGHBOR_EPSILON_PCT : 100;
4812
+ if (left > right) return (left + right) / 2;
4813
+ return clamp2(clipPct, left, right);
4814
+ }
4815
+ function resolveKeyframeDrag(opts) {
4816
+ const dx = opts.pointerUpX - opts.pointerDownX;
4817
+ if (Math.abs(dx) < KEYFRAME_DRAG_THRESHOLD_PX || opts.clipWidthPx <= 0) {
4818
+ return { kind: "click" };
4819
+ }
4820
+ const rawClipPct = opts.draggedClipPct + dx / opts.clipWidthPx * 100;
4821
+ const toClipPct = clampToNeighbors(rawClipPct, opts.sortedClipPcts, opts.draggedIndex);
4822
+ if (Math.abs(toClipPct - opts.draggedClipPct) < NOOP_EPSILON_PCT) return { kind: "noop" };
4823
+ return { kind: "move", toClipPct };
4824
+ }
4825
+ function previewClipPct(opts) {
4826
+ if (opts.clipWidthPx <= 0) return opts.draggedClipPct;
4827
+ const dx = opts.pointerMoveX - opts.pointerDownX;
4828
+ return clampToNeighbors(
4829
+ opts.draggedClipPct + dx / opts.clipWidthPx * 100,
4830
+ opts.sortedClipPcts,
4831
+ opts.draggedIndex
4832
+ );
4833
+ }
4834
+
4835
+ // src/player/components/TimelineClipDiamonds.tsx
4787
4836
  import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
4788
4837
  var DIAMOND_RATIO = 0.8;
4789
4838
  var KF_MIN_PCT = -5;
@@ -4800,23 +4849,20 @@ var TimelineClipDiamonds = memo6(function TimelineClipDiamonds2({
4800
4849
  selectedKeyframes,
4801
4850
  onClickKeyframe,
4802
4851
  onShiftClickKeyframe,
4803
- onContextMenuKeyframe
4852
+ onContextMenuKeyframe,
4853
+ onMoveKeyframe
4804
4854
  }) {
4855
+ const dragRef = useRef11(null);
4856
+ const [preview, setPreview] = useState10(null);
4805
4857
  if (clipWidthPx < 20) return null;
4806
4858
  const diamondSize = Math.round(clipHeightPx * (beatsActive ? 0.45 : DIAMOND_RATIO));
4807
4859
  const half = diamondSize / 2;
4808
4860
  const centerY = beatsActive ? BEAT_BAND_H + (clipHeightPx - BEAT_BAND_H) / 2 : clipHeightPx / 2;
4809
4861
  const sorted = keyframesData.keyframes.filter((kf) => kf.percentage >= KF_MIN_PCT && kf.percentage <= KF_MAX_PCT).sort((a, b) => a.percentage - b.percentage);
4862
+ const sortedClipPcts = sorted.map((k) => k.percentage);
4810
4863
  const baseColor = isSelected ? accentColor : "#a3a3a3";
4811
4864
  const baseOpacity = isSelected ? 0.4 : 0.25;
4812
- const handleClick = (e, pct) => {
4813
- e.stopPropagation();
4814
- if (e.shiftKey) {
4815
- onShiftClickKeyframe?.(elementId, pct);
4816
- } else {
4817
- onClickKeyframe?.(pct);
4818
- }
4819
- };
4865
+ const canDrag = isSelected && !!onMoveKeyframe;
4820
4866
  return /* @__PURE__ */ jsxs11("div", { className: "absolute inset-0", style: { zIndex: 3, pointerEvents: "none" }, children: [
4821
4867
  sorted.map((kf, i) => {
4822
4868
  if (i === 0) return null;
@@ -4843,12 +4889,72 @@ var TimelineClipDiamonds = memo6(function TimelineClipDiamonds2({
4843
4889
  );
4844
4890
  }),
4845
4891
  sorted.map((kf, i) => {
4846
- const leftPx = kf.percentage / 100 * clipWidthPx - half;
4847
4892
  const kfKey = `${elementId}:${kf.percentage}`;
4893
+ const renderPct = preview?.kfKey === kfKey ? preview.clipPct : kf.percentage;
4894
+ const leftPx = renderPct / 100 * clipWidthPx - half;
4848
4895
  const isKfSelected = selectedKeyframes.has(kfKey);
4849
4896
  const atPlayhead = isSelected && Math.abs(kf.percentage - currentPercentage) < 0.5;
4850
4897
  const isHighlighted = isKfSelected || atPlayhead;
4851
4898
  const color = isHighlighted ? accentColor : "#a3a3a3";
4899
+ const onPointerDown = (e) => {
4900
+ if (e.button !== 0) return;
4901
+ e.stopPropagation();
4902
+ if (canDrag) {
4903
+ e.currentTarget.setPointerCapture?.(e.pointerId);
4904
+ dragRef.current = {
4905
+ kfKey,
4906
+ startX: e.clientX,
4907
+ fromClipPct: kf.percentage,
4908
+ moved: false
4909
+ };
4910
+ }
4911
+ };
4912
+ const onPointerMove = (e) => {
4913
+ const d = dragRef.current;
4914
+ if (!d || d.kfKey !== kfKey) return;
4915
+ if (!d.moved && Math.abs(e.clientX - d.startX) >= KEYFRAME_DRAG_THRESHOLD_PX) {
4916
+ d.moved = true;
4917
+ }
4918
+ if (d.moved) {
4919
+ setPreview({
4920
+ kfKey,
4921
+ clipPct: previewClipPct({
4922
+ pointerDownX: d.startX,
4923
+ pointerMoveX: e.clientX,
4924
+ clipWidthPx,
4925
+ draggedClipPct: d.fromClipPct,
4926
+ draggedIndex: i,
4927
+ sortedClipPcts
4928
+ })
4929
+ });
4930
+ }
4931
+ };
4932
+ const onPointerUp = (e) => {
4933
+ const d = dragRef.current;
4934
+ if (!d || d.kfKey !== kfKey) {
4935
+ if (e.shiftKey) onShiftClickKeyframe?.(elementId, kf.percentage);
4936
+ else onClickKeyframe?.(kf.percentage);
4937
+ return;
4938
+ }
4939
+ e.stopPropagation();
4940
+ dragRef.current = null;
4941
+ setPreview(null);
4942
+ e.currentTarget.releasePointerCapture?.(e.pointerId);
4943
+ const res = resolveKeyframeDrag({
4944
+ pointerDownX: d.startX,
4945
+ pointerUpX: e.clientX,
4946
+ clipWidthPx,
4947
+ draggedClipPct: d.fromClipPct,
4948
+ draggedIndex: i,
4949
+ sortedClipPcts
4950
+ });
4951
+ if (res.kind === "click") {
4952
+ if (e.shiftKey) onShiftClickKeyframe?.(elementId, kf.percentage);
4953
+ else onClickKeyframe?.(kf.percentage);
4954
+ } else if (res.kind === "move" && res.toClipPct != null) {
4955
+ onMoveKeyframe?.(elementId, d.fromClipPct, res.toClipPct);
4956
+ }
4957
+ };
4852
4958
  return /* @__PURE__ */ jsx14(
4853
4959
  "button",
4854
4960
  {
@@ -4864,10 +4970,13 @@ var TimelineClipDiamonds = memo6(function TimelineClipDiamonds2({
4864
4970
  pointerEvents: "auto",
4865
4971
  background: "none",
4866
4972
  border: "none",
4867
- cursor: "pointer",
4868
- padding: 0
4973
+ cursor: canDrag ? "ew-resize" : "pointer",
4974
+ padding: 0,
4975
+ touchAction: "none"
4869
4976
  },
4870
- onClick: (e) => handleClick(e, kf.percentage),
4977
+ onPointerDown,
4978
+ onPointerMove,
4979
+ onPointerUp,
4871
4980
  onContextMenu: (e) => {
4872
4981
  e.preventDefault();
4873
4982
  e.stopPropagation();
@@ -5691,6 +5800,8 @@ function TimelineEditProvider({
5691
5800
  value.onDeleteKeyframe,
5692
5801
  value.onDeleteAllKeyframes,
5693
5802
  value.onChangeKeyframeEase,
5803
+ value.onMoveKeyframeToPlayhead,
5804
+ value.onMoveKeyframe,
5694
5805
  value.onToggleKeyframeAtPlayhead
5695
5806
  ]
5696
5807
  );
@@ -5762,6 +5873,7 @@ var TimelineCanvas = memo8(function TimelineCanvas2({
5762
5873
  onClickKeyframe,
5763
5874
  onShiftClickKeyframe,
5764
5875
  onContextMenuKeyframe,
5876
+ onMoveKeyframe,
5765
5877
  onContextMenuClip,
5766
5878
  beatAnalysis
5767
5879
  }) {
@@ -6024,7 +6136,8 @@ var TimelineCanvas = memo8(function TimelineCanvas2({
6024
6136
  selectedKeyframes,
6025
6137
  onClickKeyframe: (pct) => onClickKeyframe?.(previewElement, pct),
6026
6138
  onShiftClickKeyframe,
6027
- onContextMenuKeyframe
6139
+ onContextMenuKeyframe,
6140
+ onMoveKeyframe
6028
6141
  }
6029
6142
  )
6030
6143
  ]
@@ -6115,9 +6228,9 @@ import { memo as memo9 } from "react";
6115
6228
  import { createPortal as createPortal2 } from "react-dom";
6116
6229
 
6117
6230
  // src/hooks/useContextMenuDismiss.ts
6118
- import { useCallback as useCallback9, useEffect as useEffect8, useRef as useRef11 } from "react";
6231
+ import { useCallback as useCallback9, useEffect as useEffect8, useRef as useRef12 } from "react";
6119
6232
  function useContextMenuDismiss(onClose) {
6120
- const menuRef = useRef11(null);
6233
+ const menuRef = useRef12(null);
6121
6234
  const dismiss = useCallback9(
6122
6235
  (e) => {
6123
6236
  if (e instanceof KeyboardEvent && e.key !== "Escape") return;
@@ -6143,11 +6256,12 @@ var KeyframeDiamondContextMenu = memo9(function KeyframeDiamondContextMenu2({
6143
6256
  state,
6144
6257
  onClose,
6145
6258
  onDelete,
6146
- onDeleteAll
6259
+ onDeleteAll,
6260
+ onMoveToPlayhead
6147
6261
  }) {
6148
6262
  const menuRef = useContextMenuDismiss(onClose);
6149
6263
  const menuWidth = 200;
6150
- const menuHeight = 70;
6264
+ const menuHeight = onMoveToPlayhead ? 100 : 70;
6151
6265
  const overflowY = state.y + menuHeight - window.innerHeight;
6152
6266
  const adjustedX = state.x + menuWidth > window.innerWidth ? state.x - menuWidth : state.x;
6153
6267
  const adjustedY = overflowY > 0 ? state.y - overflowY - 8 : state.y;
@@ -6159,13 +6273,25 @@ var KeyframeDiamondContextMenu = memo9(function KeyframeDiamondContextMenu2({
6159
6273
  className: "fixed z-50 bg-neutral-900 border border-neutral-700 rounded-md shadow-lg py-1 min-w-[180px]",
6160
6274
  style: { left: adjustedX, top: adjustedY },
6161
6275
  children: [
6276
+ onMoveToPlayhead && /* @__PURE__ */ jsx19(
6277
+ "button",
6278
+ {
6279
+ type: "button",
6280
+ className: "w-full flex items-center gap-2 px-3 py-1.5 text-xs text-neutral-200 hover:bg-neutral-800 cursor-pointer text-left",
6281
+ onClick: () => {
6282
+ onMoveToPlayhead(state.elementId, state.percentage);
6283
+ onClose();
6284
+ },
6285
+ children: "Move to Playhead"
6286
+ }
6287
+ ),
6162
6288
  /* @__PURE__ */ jsx19(
6163
6289
  "button",
6164
6290
  {
6165
6291
  type: "button",
6166
6292
  className: "w-full flex items-center gap-2 px-3 py-1.5 text-xs text-red-400 hover:bg-neutral-800 cursor-pointer text-left",
6167
6293
  onClick: () => {
6168
- onDelete(state.elementId, state.tweenPercentage ?? state.percentage);
6294
+ onDelete(state.elementId, state.percentage);
6169
6295
  onClose();
6170
6296
  },
6171
6297
  children: "Delete Keyframe"
@@ -6191,7 +6317,7 @@ var KeyframeDiamondContextMenu = memo9(function KeyframeDiamondContextMenu2({
6191
6317
  });
6192
6318
 
6193
6319
  // src/player/components/useTimelineClipDrag.ts
6194
- import { useRef as useRef12, useState as useState10, useCallback as useCallback10, useMemo as useMemo6 } from "react";
6320
+ import { useRef as useRef13, useState as useState11, useCallback as useCallback10, useMemo as useMemo6 } from "react";
6195
6321
  var BEAT_SNAP_PX = 8;
6196
6322
  var EMPTY_BEAT_TIMES = [];
6197
6323
  function snapToNearestBeat(time, beatTimes, thresholdSecs) {
@@ -6263,22 +6389,22 @@ function useTimelineClipDrag({
6263
6389
  musicPlaybackStart,
6264
6390
  musicDuration
6265
6391
  ]);
6266
- const beatTimesRef = useRef12([]);
6392
+ const beatTimesRef = useRef13([]);
6267
6393
  beatTimesRef.current = adjustedBeatTimes;
6268
- const [draggedClip, setDraggedClip] = useState10(null);
6269
- const draggedClipRef = useRef12(null);
6394
+ const [draggedClip, setDraggedClip] = useState11(null);
6395
+ const draggedClipRef = useRef13(null);
6270
6396
  draggedClipRef.current = draggedClip;
6271
- const [resizingClip, setResizingClip] = useState10(null);
6272
- const resizingClipRef = useRef12(null);
6397
+ const [resizingClip, setResizingClip] = useState11(null);
6398
+ const resizingClipRef = useRef13(null);
6273
6399
  resizingClipRef.current = resizingClip;
6274
- const blockedClipRef = useRef12(null);
6275
- const suppressClickRef = useRef12(false);
6276
- const onMoveElementRef = useRef12(onMoveElement);
6400
+ const blockedClipRef = useRef13(null);
6401
+ const suppressClickRef = useRef13(false);
6402
+ const onMoveElementRef = useRef13(onMoveElement);
6277
6403
  onMoveElementRef.current = onMoveElement;
6278
- const onResizeElementRef = useRef12(onResizeElement);
6404
+ const onResizeElementRef = useRef13(onResizeElement);
6279
6405
  onResizeElementRef.current = onResizeElement;
6280
- const clipDragScrollRaf = useRef12(0);
6281
- const clipDragPointerRef = useRef12(null);
6406
+ const clipDragScrollRaf = useRef13(0);
6407
+ const clipDragPointerRef = useRef13(null);
6282
6408
  const updateDraggedClipPreview = useCallback10(
6283
6409
  (drag, clientX, clientY) => {
6284
6410
  const scroll = scrollRef.current;
@@ -6368,11 +6494,11 @@ function useTimelineClipDrag({
6368
6494
  },
6369
6495
  [scrollRef, stepClipDragAutoScroll]
6370
6496
  );
6371
- const updateDraggedClipPreviewRef = useRef12(updateDraggedClipPreview);
6497
+ const updateDraggedClipPreviewRef = useRef13(updateDraggedClipPreview);
6372
6498
  updateDraggedClipPreviewRef.current = updateDraggedClipPreview;
6373
- const syncClipDragAutoScrollRef = useRef12(syncClipDragAutoScroll);
6499
+ const syncClipDragAutoScrollRef = useRef13(syncClipDragAutoScroll);
6374
6500
  syncClipDragAutoScrollRef.current = syncClipDragAutoScroll;
6375
- const stopClipDragAutoScrollRef = useRef12(stopClipDragAutoScroll);
6501
+ const stopClipDragAutoScrollRef = useRef13(stopClipDragAutoScroll);
6376
6502
  stopClipDragAutoScrollRef.current = stopClipDragAutoScroll;
6377
6503
  useMountEffect(() => {
6378
6504
  const clearSuppressedClick = () => {
@@ -6679,7 +6805,9 @@ var Timeline = memo11(function Timeline2({
6679
6805
  onRazorSplitAll,
6680
6806
  onDeleteKeyframe,
6681
6807
  onDeleteAllKeyframes,
6682
- onChangeKeyframeEase
6808
+ onChangeKeyframeEase,
6809
+ onMoveKeyframeToPlayhead,
6810
+ onMoveKeyframe
6683
6811
  } = useResolvedTimelineEditCallbacks({
6684
6812
  onMoveElement: onMoveElementOverride,
6685
6813
  onResizeElement: onResizeElementOverride,
@@ -6703,14 +6831,14 @@ var Timeline = memo11(function Timeline2({
6703
6831
  const setSelectedElementId = usePlayerStore((s) => s.setSelectedElementId);
6704
6832
  const currentTime = usePlayerStore((s) => s.currentTime);
6705
6833
  const { zoomMode, manualZoomPercent, setZoomMode, setManualZoomPercent } = useTimelineZoom();
6706
- const playheadRef = useRef13(null);
6707
- const containerRef = useRef13(null);
6708
- const scrollRef = useRef13(null);
6834
+ const playheadRef = useRef14(null);
6835
+ const containerRef = useRef14(null);
6836
+ const scrollRef = useRef14(null);
6709
6837
  const activeTool = usePlayerStore((s) => s.activeTool);
6710
- const [hoveredClip, setHoveredClip] = useState11(null);
6711
- const isDragging = useRef13(false);
6712
- const [shiftHeld, setShiftHeld] = useState11(false);
6713
- const [razorGuideX, setRazorGuideX] = useState11(null);
6838
+ const [hoveredClip, setHoveredClip] = useState12(null);
6839
+ const isDragging = useRef14(false);
6840
+ const [shiftHeld, setShiftHeld] = useState12(false);
6841
+ const [razorGuideX, setRazorGuideX] = useState12(null);
6714
6842
  useMountEffect(() => {
6715
6843
  const down = (e) => e.key === "Shift" && setShiftHeld(true);
6716
6844
  const up = (e) => e.key === "Shift" && setShiftHeld(false);
@@ -6724,13 +6852,13 @@ var Timeline = memo11(function Timeline2({
6724
6852
  window.removeEventListener("blur", blur);
6725
6853
  };
6726
6854
  });
6727
- const [showPopover, setShowPopover] = useState11(false);
6728
- const [showShortcutHint, setShowShortcutHint] = useState11(true);
6729
- const [kfContextMenu, setKfContextMenu] = useState11(null);
6730
- const [clipContextMenu, setClipContextMenu] = useState11(null);
6731
- const [viewportWidth, setViewportWidth] = useState11(0);
6732
- const roRef = useRef13(null);
6733
- const shortcutHintRafRef = useRef13(0);
6855
+ const [showPopover, setShowPopover] = useState12(false);
6856
+ const [showShortcutHint, setShowShortcutHint] = useState12(true);
6857
+ const [kfContextMenu, setKfContextMenu] = useState12(null);
6858
+ const [clipContextMenu, setClipContextMenu] = useState12(null);
6859
+ const [viewportWidth, setViewportWidth] = useState12(0);
6860
+ const roRef = useRef14(null);
6861
+ const shortcutHintRafRef = useRef14(0);
6734
6862
  const syncShortcutHintVisibility = useCallback11(() => {
6735
6863
  const scroll = scrollRef.current;
6736
6864
  setShowShortcutHint(
@@ -6790,12 +6918,12 @@ var Timeline = memo11(function Timeline2({
6790
6918
  return map;
6791
6919
  }, [tracks]);
6792
6920
  const trackOrder = useMemo8(() => tracks.map(([trackNum]) => trackNum), [tracks]);
6793
- const trackOrderRef = useRef13(trackOrder);
6921
+ const trackOrderRef = useRef14(trackOrder);
6794
6922
  trackOrderRef.current = trackOrder;
6795
- const ppsRef = useRef13(100);
6796
- const durationRef = useRef13(effectiveDuration);
6923
+ const ppsRef = useRef14(100);
6924
+ const durationRef = useRef14(effectiveDuration);
6797
6925
  durationRef.current = effectiveDuration;
6798
- const setRangeSelectionRef = useRef13(null);
6926
+ const setRangeSelectionRef = useRef14(null);
6799
6927
  const {
6800
6928
  draggedClip,
6801
6929
  setDraggedClip,
@@ -6828,17 +6956,17 @@ var Timeline = memo11(function Timeline2({
6828
6956
  () => expandedElements.find((element) => (element.key ?? element.id) === selectedElementId) ?? null,
6829
6957
  [expandedElements, selectedElementId]
6830
6958
  );
6831
- const selectedElementRef = useRef13(selectedElement);
6959
+ const selectedElementRef = useRef14(selectedElement);
6832
6960
  selectedElementRef.current = selectedElement;
6833
6961
  const fitPps = viewportWidth > GUTTER && effectiveDuration > 0 ? (viewportWidth - GUTTER - 2) / effectiveDuration : 100;
6834
6962
  const pps = getTimelinePixelsPerSecond(fitPps, zoomMode, manualZoomPercent);
6835
6963
  ppsRef.current = pps;
6836
6964
  const trackContentWidth = Math.max(0, effectiveDuration * pps);
6837
- const zoomModeRef = useRef13(zoomMode);
6965
+ const zoomModeRef = useRef14(zoomMode);
6838
6966
  zoomModeRef.current = zoomMode;
6839
- const manualZoomPercentRef = useRef13(manualZoomPercent);
6967
+ const manualZoomPercentRef = useRef14(manualZoomPercent);
6840
6968
  manualZoomPercentRef.current = manualZoomPercent;
6841
- const fitPpsRef = useRef13(fitPps);
6969
+ const fitPpsRef = useRef14(fitPps);
6842
6970
  fitPpsRef.current = fitPps;
6843
6971
  const { seekFromX, autoScrollDuringDrag, dragScrollRaf } = useTimelinePlayhead({
6844
6972
  playheadRef,
@@ -6881,7 +7009,7 @@ var Timeline = memo11(function Timeline2({
6881
7009
  setShowPopover
6882
7010
  });
6883
7011
  setRangeSelectionRef.current = setRangeSelection;
6884
- const prevSelectedRef = useRef13(selectedElementRef.current);
7012
+ const prevSelectedRef = useRef14(selectedElementRef.current);
6885
7013
  useEffect9(() => {
6886
7014
  const prev = prevSelectedRef.current;
6887
7015
  const curr = selectedElementRef.current;
@@ -7033,6 +7161,7 @@ var Timeline = memo11(function Timeline2({
7033
7161
  onShiftClickKeyframe: (elId, pct) => {
7034
7162
  toggleSelectedKeyframe(`${elId}:${pct}`);
7035
7163
  },
7164
+ onMoveKeyframe,
7036
7165
  onContextMenuKeyframe: (e, elId, pct) => {
7037
7166
  const el = expandedElements.find((x) => (x.key ?? x.id) === elId);
7038
7167
  if (el) {
@@ -7113,6 +7242,7 @@ var Timeline = memo11(function Timeline2({
7113
7242
  onDelete: (elId, pct) => onDeleteKeyframe?.(elId, pct),
7114
7243
  onDeleteAll: (elId) => onDeleteAllKeyframes?.(elId),
7115
7244
  onChangeEase: (elId, pct, ease) => onChangeKeyframeEase?.(elId, pct, ease),
7245
+ onMoveToPlayhead: onMoveKeyframeToPlayhead ? (elId, pct) => onMoveKeyframeToPlayhead(elId, pct) : void 0,
7116
7246
  onCopyProperties: (elId, pct) => {
7117
7247
  const kfData = keyframeCache.get(elId);
7118
7248
  const kf = kfData?.keyframes.find((k) => k.percentage === pct);
@@ -7140,7 +7270,7 @@ var Timeline = memo11(function Timeline2({
7140
7270
  });
7141
7271
 
7142
7272
  // src/player/components/VideoThumbnail.tsx
7143
- import { memo as memo12, useRef as useRef14, useState as useState12, useCallback as useCallback12, useEffect as useEffect10 } from "react";
7273
+ import { memo as memo12, useRef as useRef15, useState as useState13, useCallback as useCallback12, useEffect as useEffect10 } from "react";
7144
7274
  import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
7145
7275
  var CLIP_HEIGHT = 66;
7146
7276
  var MAX_UNIQUE_FRAMES = 6;
@@ -7150,13 +7280,13 @@ var VideoThumbnail = memo12(function VideoThumbnail2({
7150
7280
  labelColor,
7151
7281
  duration = 5
7152
7282
  }) {
7153
- const [containerWidth, setContainerWidth] = useState12(0);
7154
- const [visible, setVisible] = useState12(false);
7155
- const [frames, setFrames] = useState12([]);
7156
- const [aspect, setAspect] = useState12(16 / 9);
7157
- const ioRef = useRef14(null);
7158
- const roRef = useRef14(null);
7159
- const extractingRef = useRef14(false);
7283
+ const [containerWidth, setContainerWidth] = useState13(0);
7284
+ const [visible, setVisible] = useState13(false);
7285
+ const [frames, setFrames] = useState13([]);
7286
+ const [aspect, setAspect] = useState13(16 / 9);
7287
+ const ioRef = useRef15(null);
7288
+ const roRef = useRef15(null);
7289
+ const extractingRef = useRef15(false);
7160
7290
  const setContainerRef = useCallback12((el) => {
7161
7291
  ioRef.current?.disconnect();
7162
7292
  roRef.current?.disconnect();
@@ -7297,7 +7427,7 @@ var VideoThumbnail = memo12(function VideoThumbnail2({
7297
7427
  });
7298
7428
 
7299
7429
  // src/player/components/CompositionThumbnail.tsx
7300
- import { memo as memo13, useCallback as useCallback13, useState as useState13, useRef as useRef15 } from "react";
7430
+ import { memo as memo13, useCallback as useCallback13, useState as useState14, useRef as useRef16 } from "react";
7301
7431
  import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
7302
7432
  var CLIP_HEIGHT2 = 66;
7303
7433
  var THUMBNAIL_URL_VERSION = "v3";
@@ -7331,10 +7461,10 @@ var CompositionThumbnail = memo13(function CompositionThumbnail2({
7331
7461
  seekTime = 2,
7332
7462
  duration = 5
7333
7463
  }) {
7334
- const [containerWidth, setContainerWidth] = useState13(0);
7335
- const [loaded, setLoaded] = useState13(false);
7336
- const [aspect, setAspect] = useState13(16 / 9);
7337
- const roRef = useRef15(null);
7464
+ const [containerWidth, setContainerWidth] = useState14(0);
7465
+ const [loaded, setLoaded] = useState14(false);
7466
+ const [aspect, setAspect] = useState14(16 / 9);
7467
+ const roRef = useRef16(null);
7338
7468
  const setContainerRef = useCallback13((el) => {
7339
7469
  roRef.current?.disconnect();
7340
7470
  if (!el) return;
@@ -7417,10 +7547,10 @@ var CompositionThumbnail = memo13(function CompositionThumbnail2({
7417
7547
  });
7418
7548
 
7419
7549
  // src/player/hooks/useTimelinePlayer.ts
7420
- import { useRef as useRef17, useCallback as useCallback16, useEffect as useEffect11 } from "react";
7550
+ import { useRef as useRef18, useCallback as useCallback16, useEffect as useEffect11 } from "react";
7421
7551
 
7422
7552
  // src/player/hooks/usePlaybackKeyboard.ts
7423
- import { useRef as useRef16, useCallback as useCallback14 } from "react";
7553
+ import { useRef as useRef17, useCallback as useCallback14 } from "react";
7424
7554
 
7425
7555
  // src/captions/store.ts
7426
7556
  import { create as create2 } from "zustand";
@@ -7647,10 +7777,10 @@ function usePlaybackKeyboard({
7647
7777
  pause,
7648
7778
  seek
7649
7779
  }) {
7650
- const pressedKeysRef = useRef16(/* @__PURE__ */ new Set());
7651
- const playbackKeyDownRef = useRef16(() => {
7780
+ const pressedKeysRef = useRef17(/* @__PURE__ */ new Set());
7781
+ const playbackKeyDownRef = useRef17(() => {
7652
7782
  });
7653
- const playbackKeyUpRef = useRef16(() => {
7783
+ const playbackKeyUpRef = useRef17(() => {
7654
7784
  });
7655
7785
  const stepFrames = useCallback14(
7656
7786
  (deltaFrames) => {
@@ -8180,18 +8310,18 @@ function timelineElementsChanged(prev, next) {
8180
8310
  });
8181
8311
  }
8182
8312
  function useTimelinePlayer() {
8183
- const iframeRef = useRef17(null);
8184
- const rafRef = useRef17(0);
8185
- const probeIntervalRef = useRef17(void 0);
8186
- const pendingSeekRef = useRef17(null);
8187
- const isRefreshingRef = useRef17(false);
8188
- const reverseRafRef = useRef17(0);
8189
- const shuttleDirectionRef = useRef17(null);
8190
- const shuttleSpeedIndexRef = useRef17(0);
8191
- const iframeShortcutCleanupRef = useRef17(null);
8192
- const lastTimelineMessageRef = useRef17(0);
8193
- const staticSeekAdapterRef = useRef17(null);
8194
- const staticSeekWarnedRef = useRef17(false);
8313
+ const iframeRef = useRef18(null);
8314
+ const rafRef = useRef18(0);
8315
+ const probeIntervalRef = useRef18(void 0);
8316
+ const pendingSeekRef = useRef18(null);
8317
+ const isRefreshingRef = useRef18(false);
8318
+ const reverseRafRef = useRef18(0);
8319
+ const shuttleDirectionRef = useRef18(null);
8320
+ const shuttleSpeedIndexRef = useRef18(0);
8321
+ const iframeShortcutCleanupRef = useRef18(null);
8322
+ const lastTimelineMessageRef = useRef18(0);
8323
+ const staticSeekAdapterRef = useRef18(null);
8324
+ const staticSeekWarnedRef = useRef18(false);
8195
8325
  const { setIsPlaying, setCurrentTime, setDuration, setTimelineReady, setElements } = usePlayerStore.getState();
8196
8326
  const syncTimelineElements = useCallback16(
8197
8327
  (elements, nextDuration) => {
@@ -8543,7 +8673,7 @@ function useTimelinePlayer() {
8543
8673
  url.searchParams.set("_t", String(Date.now()));
8544
8674
  iframe.src = url.toString();
8545
8675
  }, [saveSeekPosition]);
8546
- const getAdapterRef = useRef17(getAdapter);
8676
+ const getAdapterRef = useRef18(getAdapter);
8547
8677
  getAdapterRef.current = getAdapter;
8548
8678
  useMountEffect(() => {
8549
8679
  const handleWindowKeyDown = (e) => playbackKeyDownRef.current(e);
@@ -8648,7 +8778,7 @@ function useTimelinePlayer() {
8648
8778
  }
8649
8779
 
8650
8780
  // src/components/nle/NLEPreview.tsx
8651
- import { memo as memo14, useCallback as useCallback17, useEffect as useEffect12, useRef as useRef18, useState as useState14 } from "react";
8781
+ import { memo as memo14, useCallback as useCallback17, useEffect as useEffect12, useRef as useRef19, useState as useState15 } from "react";
8652
8782
 
8653
8783
  // src/components/nle/previewZoom.ts
8654
8784
  var MIN_PREVIEW_ZOOM_PERCENT = 25;
@@ -8814,21 +8944,21 @@ var NLEPreview = memo14(function NLEPreview2({
8814
8944
  onStageRef
8815
8945
  }) {
8816
8946
  const activeKey = getPreviewPlayerKey({ projectId, directUrl });
8817
- const viewportRef = useRef18(null);
8818
- const stageRef = useRef18(null);
8819
- const previewIframeRef = useRef18(null);
8947
+ const viewportRef = useRef19(null);
8948
+ const stageRef = useRef19(null);
8949
+ const previewIframeRef = useRef19(null);
8820
8950
  useEffect12(() => {
8821
8951
  onStageRef?.(stageRef);
8822
8952
  }, [onStageRef]);
8823
- const [compositionSize, setCompositionSize] = useState14(null);
8824
- const [stageSize, setStageSize] = useState14(() => resolvePreviewStageSize(0, 0, null, portrait));
8825
- const zoomRef = useRef18(loadInitialZoom());
8826
- const [settledZoom, setSettledZoom] = useState14(() => zoomRef.current);
8827
- const hudRef = useRef18(null);
8828
- const hudTimerRef = useRef18(null);
8829
- const settleTimerRef = useRef18(null);
8830
- const zoomingRef = useRef18(false);
8831
- const dragRef = useRef18(null);
8953
+ const [compositionSize, setCompositionSize] = useState15(null);
8954
+ const [stageSize, setStageSize] = useState15(() => resolvePreviewStageSize(0, 0, null, portrait));
8955
+ const zoomRef = useRef19(loadInitialZoom());
8956
+ const [settledZoom, setSettledZoom] = useState15(() => zoomRef.current);
8957
+ const hudRef = useRef19(null);
8958
+ const hudTimerRef = useRef19(null);
8959
+ const settleTimerRef = useRef19(null);
8960
+ const zoomingRef = useRef19(false);
8961
+ const dragRef = useRef19(null);
8832
8962
  useEffect12(() => {
8833
8963
  return () => {
8834
8964
  if (settleTimerRef.current) clearTimeout(settleTimerRef.current);
@@ -8860,7 +8990,7 @@ var NLEPreview = memo14(function NLEPreview2({
8860
8990
  },
8861
8991
  [iframeRef]
8862
8992
  );
8863
- const stageSizeRef = useRef18(stageSize);
8993
+ const stageSizeRef = useRef19(stageSize);
8864
8994
  stageSizeRef.current = stageSize;
8865
8995
  const writeTransform2 = useCallback17((state) => {
8866
8996
  const stage = stageRef.current;
@@ -9181,7 +9311,7 @@ function CompositionBreadcrumb({ stack, onNavigate }) {
9181
9311
  }
9182
9312
 
9183
9313
  // src/components/nle/usePreviewBlockDrop.ts
9184
- import { useCallback as useCallback18, useState as useState15 } from "react";
9314
+ import { useCallback as useCallback18, useState as useState16 } from "react";
9185
9315
  function parseBlockPayload(raw) {
9186
9316
  try {
9187
9317
  const parsed = JSON.parse(raw);
@@ -9214,7 +9344,7 @@ function usePreviewBlockDrop({
9214
9344
  stageRef,
9215
9345
  onBlockDrop
9216
9346
  }) {
9217
- const [isDragOver, setIsDragOver] = useState15(false);
9347
+ const [isDragOver, setIsDragOver] = useState16(false);
9218
9348
  const handleDragOver = useCallback18(
9219
9349
  (e) => {
9220
9350
  if (!onBlockDrop) return;
@@ -9253,16 +9383,16 @@ function usePreviewBlockDrop({
9253
9383
  }
9254
9384
 
9255
9385
  // src/components/nle/useCompositionStack.ts
9256
- import { useState as useState16, useCallback as useCallback19, useRef as useRef19, useEffect as useEffect13 } from "react";
9386
+ import { useState as useState17, useCallback as useCallback19, useRef as useRef20, useEffect as useEffect13 } from "react";
9257
9387
  function useCompositionStack({
9258
9388
  projectId,
9259
9389
  activeCompositionPath,
9260
9390
  onCompositionChange
9261
9391
  }) {
9262
- const [compositionStack, setCompositionStack] = useState16([
9392
+ const [compositionStack, setCompositionStack] = useState17([
9263
9393
  { id: "master", label: "Master", previewUrl: `/api/projects/${projectId}/preview` }
9264
9394
  ]);
9265
- const onCompositionChangeRef = useRef19(onCompositionChange);
9395
+ const onCompositionChangeRef = useRef20(onCompositionChange);
9266
9396
  onCompositionChangeRef.current = onCompositionChange;
9267
9397
  const updateCompositionStack = useCallback19((action) => {
9268
9398
  setCompositionStack((prev) => {
@@ -9272,9 +9402,9 @@ function useCompositionStack({
9272
9402
  return next;
9273
9403
  });
9274
9404
  }, []);
9275
- const masterSeekRef = useRef19(0);
9276
- const [compIdToSrc, setCompIdToSrc] = useState16(/* @__PURE__ */ new Map());
9277
- const compIdToSrcRef = useRef19(compIdToSrc);
9405
+ const masterSeekRef = useRef20(0);
9406
+ const [compIdToSrc, setCompIdToSrc] = useState17(/* @__PURE__ */ new Map());
9407
+ const compIdToSrcRef = useRef20(compIdToSrc);
9278
9408
  compIdToSrcRef.current = compIdToSrc;
9279
9409
  const handleNavigateComposition = useCallback19(
9280
9410
  (index) => {
@@ -9706,6 +9836,13 @@ function applySoftReload(iframe, scriptText, onAsyncFailure) {
9706
9836
  delete timelines[key];
9707
9837
  }
9708
9838
  }
9839
+ const seenTargets = new Set(allTargets);
9840
+ for (const el of doc.querySelectorAll("[style*='transform']")) {
9841
+ if (el.style.transform && "_gsap" in el && !seenTargets.has(el)) {
9842
+ seenTargets.add(el);
9843
+ allTargets.push(el);
9844
+ }
9845
+ }
9709
9846
  if (allTargets.length > 0 && win.gsap?.set) {
9710
9847
  const saved = [];
9711
9848
  for (const el of allTargets) {
@@ -9829,7 +9966,7 @@ var NLELayout = memo15(function NLELayout2({
9829
9966
  useEffect14(() => {
9830
9967
  usePlayerStore.getState().reset();
9831
9968
  }, [projectId]);
9832
- const stageRefForDrop = useRef20(null);
9969
+ const stageRefForDrop = useRef21(null);
9833
9970
  const handleStageRef = useCallback20((ref) => {
9834
9971
  stageRefForDrop.current = ref.current;
9835
9972
  }, []);
@@ -9843,7 +9980,7 @@ var NLELayout = memo15(function NLELayout2({
9843
9980
  stageRef: stageRefForDrop,
9844
9981
  onBlockDrop: onPreviewBlockDrop
9845
9982
  });
9846
- const prevRefreshKeyRef = useRef20(refreshKey);
9983
+ const prevRefreshKeyRef = useRef21(refreshKey);
9847
9984
  useEffect14(() => {
9848
9985
  if (refreshKey === prevRefreshKeyRef.current) return;
9849
9986
  prevRefreshKeyRef.current = refreshKey;
@@ -9946,7 +10083,7 @@ var NLELayout = memo15(function NLELayout2({
9946
10083
  },
9947
10084
  [onSplitElement, toLocalElement]
9948
10085
  );
9949
- const compIdToSrcRef = useRef20(compIdToSrc);
10086
+ const compIdToSrcRef = useRef21(compIdToSrc);
9950
10087
  compIdToSrcRef.current = compIdToSrc;
9951
10088
  useMountEffect(() => {
9952
10089
  fetch(`/api/projects/${projectId}/files/index.html`).then((r) => r.json()).then((data) => {
@@ -9995,9 +10132,9 @@ var NLELayout = memo15(function NLELayout2({
9995
10132
  patching = false;
9996
10133
  });
9997
10134
  }, [compIdToSrc]);
9998
- const [timelineH, setTimelineH] = useState17(DEFAULT_TIMELINE_H);
9999
- const hasLoadedOnceRef = useRef20(false);
10000
- const [compositionLoading, setCompositionLoadingRaw] = useState17(true);
10135
+ const [timelineH, setTimelineH] = useState18(DEFAULT_TIMELINE_H);
10136
+ const hasLoadedOnceRef = useRef21(false);
10137
+ const [compositionLoading, setCompositionLoadingRaw] = useState18(true);
10001
10138
  const setCompositionLoading = useCallback20((loading) => {
10002
10139
  if (!loading) hasLoadedOnceRef.current = true;
10003
10140
  if (loading && hasLoadedOnceRef.current) return;
@@ -10009,8 +10146,8 @@ var NLELayout = memo15(function NLELayout2({
10009
10146
  }, [compositionLoading, onCompositionLoadingChangeParent]);
10010
10147
  const fullscreenElement = useSyncExternalStore(subscribeFullscreen, getFullscreenElement);
10011
10148
  const isTimelineVisible = timelineVisible ?? true;
10012
- const isDragging = useRef20(false);
10013
- const containerRef = useRef20(null);
10149
+ const isDragging = useRef21(false);
10150
+ const containerRef = useRef21(null);
10014
10151
  const isFullscreen = fullscreenElement === containerRef.current && fullscreenElement != null;
10015
10152
  const toggleFullscreen = useCallback20(() => {
10016
10153
  if (!containerRef.current) return;
@@ -10022,7 +10159,7 @@ var NLELayout = memo15(function NLELayout2({
10022
10159
  }, []);
10023
10160
  const currentLevel = compositionStack[compositionStack.length - 1];
10024
10161
  const directUrl = compositionStack.length > 1 ? currentLevel.previewUrl : void 0;
10025
- const onIframeRefStable = useRef20(onIframeRef);
10162
+ const onIframeRefStable = useRef21(onIframeRef);
10026
10163
  onIframeRefStable.current = onIframeRef;
10027
10164
  useEffect14(() => {
10028
10165
  onIframeRefStable.current?.(iframeRef.current);
@@ -10239,7 +10376,7 @@ var NLELayout = memo15(function NLELayout2({
10239
10376
  });
10240
10377
 
10241
10378
  // src/components/editor/SourceEditor.tsx
10242
- import { useRef as useRef21, useCallback as useCallback21, useEffect as useEffect15, memo as memo16 } from "react";
10379
+ import { useRef as useRef22, useCallback as useCallback21, useEffect as useEffect15, memo as memo16 } from "react";
10243
10380
  import {
10244
10381
  EditorView,
10245
10382
  keymap,
@@ -10297,11 +10434,11 @@ var SourceEditor = memo16(function SourceEditor2({
10297
10434
  readOnly = false,
10298
10435
  revealOffset
10299
10436
  }) {
10300
- const editorRef = useRef21(null);
10301
- const containerRef = useRef21(null);
10302
- const onChangeRef = useRef21(onChange);
10437
+ const editorRef = useRef22(null);
10438
+ const containerRef = useRef22(null);
10439
+ const onChangeRef = useRef22(onChange);
10303
10440
  onChangeRef.current = onChange;
10304
- const contentRef = useRef21(content);
10441
+ const contentRef = useRef22(content);
10305
10442
  contentRef.current = content;
10306
10443
  const mountEditor = useCallback21(
10307
10444
  (node) => {
@@ -10369,7 +10506,7 @@ var SourceEditor = memo16(function SourceEditor2({
10369
10506
  });
10370
10507
 
10371
10508
  // src/components/editor/PropertyPanel.tsx
10372
- import { memo as memo22, useEffect as useEffect24, useMemo as useMemo15, useRef as useRef31, useState as useState33 } from "react";
10509
+ import { memo as memo22, useEffect as useEffect24, useMemo as useMemo15, useRef as useRef32, useState as useState34 } from "react";
10373
10510
 
10374
10511
  // src/icons/SystemIcons.tsx
10375
10512
  import {
@@ -10769,7 +10906,7 @@ function readCurrentTime(win, fallback) {
10769
10906
  function applyStudioMotionFromDom(document2, currentTime) {
10770
10907
  const win = document2.defaultView;
10771
10908
  if (!win) return 0;
10772
- const gsap = win.gsap;
10909
+ const gsap2 = win.gsap;
10773
10910
  win.__timelines = win.__timelines ?? {};
10774
10911
  win.__timelines[STUDIO_MOTION_TIMELINE_ID]?.kill?.();
10775
10912
  delete win.__timelines[STUDIO_MOTION_TIMELINE_ID];
@@ -10783,8 +10920,8 @@ function applyStudioMotionFromDom(document2, currentTime) {
10783
10920
  motionElements.push({ element: el, motion: motionData });
10784
10921
  }
10785
10922
  }
10786
- if (!gsap?.timeline || motionElements.length === 0) return 0;
10787
- const timeline = gsap.timeline({
10923
+ if (!gsap2?.timeline || motionElements.length === 0) return 0;
10924
+ const timeline = gsap2.timeline({
10788
10925
  paused: true,
10789
10926
  defaults: { overwrite: "auto" }
10790
10927
  });
@@ -12256,14 +12393,14 @@ function readGsapRuntimeValuesForPanel(gsapAnimId, gsapAnimations, element, prev
12256
12393
  const selector = element.id ? `#${element.id}` : element.selector;
12257
12394
  if (!selector) return null;
12258
12395
  try {
12259
- const gsap = iframe.contentWindow.gsap;
12260
- if (!gsap?.getProperty) return null;
12396
+ const gsap2 = iframe.contentWindow.gsap;
12397
+ if (!gsap2?.getProperty) return null;
12261
12398
  const el = iframe.contentDocument?.querySelector(selector);
12262
12399
  if (!el) return null;
12263
12400
  const propKeys = collectPanelPropKeys(gsapAnimations);
12264
12401
  const result = {};
12265
12402
  for (const prop of propKeys) {
12266
- const v = Number(gsap.getProperty(el, prop));
12403
+ const v = Number(gsap2.getProperty(el, prop));
12267
12404
  if (Number.isFinite(v)) result[prop] = roundToCenti(v);
12268
12405
  }
12269
12406
  return Object.keys(result).length > 0 ? result : null;
@@ -12298,7 +12435,7 @@ function readGsapBorderRadiusForPanel(gsapRuntimeValues, gsapAnimations, element
12298
12435
  }
12299
12436
 
12300
12437
  // src/components/editor/propertyPanelPrimitives.tsx
12301
- import { useCallback as useCallback22, useEffect as useEffect16, useRef as useRef22, useState as useState18 } from "react";
12438
+ import { useCallback as useCallback22, useEffect as useEffect16, useRef as useRef23, useState as useState19 } from "react";
12302
12439
  import { jsx as jsx31, jsxs as jsxs24 } from "react/jsx-runtime";
12303
12440
  function CommitField({
12304
12441
  value,
@@ -12306,11 +12443,11 @@ function CommitField({
12306
12443
  liveCommit,
12307
12444
  onCommit
12308
12445
  }) {
12309
- const [draft, setDraft] = useState18(value);
12310
- const commitTimerRef = useRef22(null);
12311
- const valueRef = useRef22(value);
12312
- const draftRef = useRef22(draft);
12313
- const inputRef = useRef22(null);
12446
+ const [draft, setDraft] = useState19(value);
12447
+ const commitTimerRef = useRef23(null);
12448
+ const valueRef = useRef23(value);
12449
+ const draftRef = useRef23(draft);
12450
+ const inputRef = useRef23(null);
12314
12451
  valueRef.current = value;
12315
12452
  draftRef.current = draft;
12316
12453
  useEffect16(() => {
@@ -12349,7 +12486,7 @@ function CommitField({
12349
12486
  if (nextDraft !== valueRef.current) onCommit(nextDraft);
12350
12487
  }, 120);
12351
12488
  };
12352
- const scheduleCommitRef = useRef22(scheduleCommit);
12489
+ const scheduleCommitRef = useRef23(scheduleCommit);
12353
12490
  scheduleCommitRef.current = scheduleCommit;
12354
12491
  return /* @__PURE__ */ jsx31(
12355
12492
  "input",
@@ -12390,7 +12527,7 @@ function MetricField({
12390
12527
  tooltip,
12391
12528
  onCommit
12392
12529
  }) {
12393
- const scrubRef = useRef22(null);
12530
+ const scrubRef = useRef23(null);
12394
12531
  const handleScrubPointerDown = useCallback22(
12395
12532
  (e) => {
12396
12533
  if (disabled || !scrub) return;
@@ -12454,9 +12591,9 @@ function SliderControl({
12454
12591
  disabled,
12455
12592
  onCommit
12456
12593
  }) {
12457
- const [draft, setDraft] = useState18(value);
12458
- const commitTimerRef = useRef22(null);
12459
- const valueRef = useRef22(value);
12594
+ const [draft, setDraft] = useState19(value);
12595
+ const commitTimerRef = useRef23(null);
12596
+ const valueRef = useRef23(value);
12460
12597
  valueRef.current = value;
12461
12598
  useEffect16(() => {
12462
12599
  setDraft(value);
@@ -12555,7 +12692,7 @@ function Section({
12555
12692
  accessory,
12556
12693
  defaultCollapsed = false
12557
12694
  }) {
12558
- const [collapsed, setCollapsed] = useState18(defaultCollapsed);
12695
+ const [collapsed, setCollapsed] = useState19(defaultCollapsed);
12559
12696
  const collapseIcon = collapsed ? /* @__PURE__ */ jsx31(
12560
12697
  "svg",
12561
12698
  {
@@ -12684,7 +12821,7 @@ function createTransformCommitHandlers({
12684
12821
  import { classifyPropertyGroup } from "@hyperframes/core/gsap-parser";
12685
12822
 
12686
12823
  // src/components/editor/propertyPanelMediaSection.tsx
12687
- import { useState as useState19 } from "react";
12824
+ import { useState as useState20 } from "react";
12688
12825
  import { Fragment as Fragment10, jsx as jsx32, jsxs as jsxs25 } from "react/jsx-runtime";
12689
12826
  var MEDIA_TAGS = /* @__PURE__ */ new Set(["video", "audio"]);
12690
12827
  function isMediaElement(element) {
@@ -12714,7 +12851,7 @@ function MediaSection({
12714
12851
  const sourceDuration = Number.parseFloat(element.dataAttributes["source-duration"] ?? "") || el.duration || 0;
12715
12852
  const mediaStartMax = Math.max(30, Math.ceil(sourceDuration || mediaStart + 10));
12716
12853
  const srcAttr = el.getAttribute("src") ?? "";
12717
- const [copied, setCopied] = useState19(false);
12854
+ const [copied, setCopied] = useState20(false);
12718
12855
  const absoluteSrc = projectDir && srcAttr && !srcAttr.startsWith("http") ? `${projectDir}/${srcAttr}` : srcAttr;
12719
12856
  return /* @__PURE__ */ jsx32(
12720
12857
  Section,
@@ -12903,8 +13040,8 @@ import {
12903
13040
  useCallback as useCallback24,
12904
13041
  useEffect as useEffect18,
12905
13042
  useMemo as useMemo11,
12906
- useRef as useRef24,
12907
- useState as useState21
13043
+ useRef as useRef25,
13044
+ useState as useState22
12908
13045
  } from "react";
12909
13046
  import {
12910
13047
  HF_COLOR_GRADING_ATTR,
@@ -12915,7 +13052,7 @@ import {
12915
13052
  } from "@hyperframes/core/color-grading";
12916
13053
 
12917
13054
  // src/components/editor/propertyPanelColorGradingControls.tsx
12918
- import { useCallback as useCallback23, useEffect as useEffect17, useMemo as useMemo10, useRef as useRef23, useState as useState20 } from "react";
13055
+ import { useCallback as useCallback23, useEffect as useEffect17, useMemo as useMemo10, useRef as useRef24, useState as useState21 } from "react";
12919
13056
  import {
12920
13057
  HF_COLOR_GRADING_PRESETS,
12921
13058
  normalizeHfColorGrading
@@ -12974,10 +13111,10 @@ function ColorGradingSliderControl({
12974
13111
  onCommit,
12975
13112
  onReset
12976
13113
  }) {
12977
- const [draftState, setDraftState] = useState20(null);
12978
- const [inputDraft, setInputDraft] = useState20(null);
12979
- const commitTimerRef = useRef23(null);
12980
- const valueRef = useRef23(value);
13114
+ const [draftState, setDraftState] = useState21(null);
13115
+ const [inputDraft, setInputDraft] = useState21(null);
13116
+ const commitTimerRef = useRef24(null);
13117
+ const valueRef = useRef24(value);
12981
13118
  valueRef.current = value;
12982
13119
  useEffect17(
12983
13120
  () => () => {
@@ -13175,7 +13312,7 @@ function ColorGradingControls({
13175
13312
  onImportAssets,
13176
13313
  onCommitColorGrading
13177
13314
  }) {
13178
- const lutInputRef = useRef23(null);
13315
+ const lutInputRef = useRef24(null);
13179
13316
  const lutAssets = useMemo10(
13180
13317
  () => assets.filter((asset) => LUT_EXT.test(asset)).sort((a, b) => a.localeCompare(b)),
13181
13318
  [assets]
@@ -13453,16 +13590,16 @@ function ColorGradingSection({
13453
13590
  onImportAssets,
13454
13591
  onSetAttributeLive
13455
13592
  }) {
13456
- const [grading, setGrading] = useState21(() => readColorGradingFromElement(element));
13457
- const [compareEnabled, setCompareEnabled] = useState21(false);
13458
- const [runtimeStatus, setRuntimeStatus] = useState21(() => ({
13593
+ const [grading, setGrading] = useState22(() => readColorGradingFromElement(element));
13594
+ const [compareEnabled, setCompareEnabled] = useState22(false);
13595
+ const [runtimeStatus, setRuntimeStatus] = useState22(() => ({
13459
13596
  state: "pending",
13460
13597
  message: "Waiting for runtime"
13461
13598
  }));
13462
- const persistTimerRef = useRef24(null);
13463
- const pendingPersistValueRef = useRef24(void 0);
13464
- const onSetAttributeLiveRef = useRef24(onSetAttributeLive);
13465
- const compareEnabledRef = useRef24(compareEnabled);
13599
+ const persistTimerRef = useRef25(null);
13600
+ const pendingPersistValueRef = useRef25(void 0);
13601
+ const onSetAttributeLiveRef = useRef25(onSetAttributeLive);
13602
+ const compareEnabledRef = useRef25(compareEnabled);
13466
13603
  onSetAttributeLiveRef.current = onSetAttributeLive;
13467
13604
  compareEnabledRef.current = compareEnabled;
13468
13605
  const target = useMemo11(
@@ -13616,7 +13753,7 @@ function ColorGradingSection({
13616
13753
  }
13617
13754
 
13618
13755
  // src/components/editor/propertyPanelSections.tsx
13619
- import { useEffect as useEffect22, useRef as useRef28, useState as useState27 } from "react";
13756
+ import { useEffect as useEffect22, useRef as useRef29, useState as useState28 } from "react";
13620
13757
 
13621
13758
  // src/components/editor/domEditingAgentPrompt.ts
13622
13759
  function formatBoundingBox(bounds) {
@@ -13696,11 +13833,11 @@ function buildAgentContextPreview(selection, activeCompPath) {
13696
13833
  }
13697
13834
 
13698
13835
  // src/components/editor/propertyPanelColor.tsx
13699
- import { useCallback as useCallback25, useEffect as useEffect19, useLayoutEffect as useLayoutEffect2, useRef as useRef25, useState as useState22 } from "react";
13836
+ import { useCallback as useCallback25, useEffect as useEffect19, useLayoutEffect as useLayoutEffect2, useRef as useRef26, useState as useState23 } from "react";
13700
13837
  import { createPortal as createPortal4 } from "react-dom";
13701
13838
 
13702
13839
  // src/components/editor/floatingPanel.ts
13703
- function clamp2(value, min, max) {
13840
+ function clamp3(value, min, max) {
13704
13841
  return Math.max(min, Math.min(max, value));
13705
13842
  }
13706
13843
  function resolveFloatingPanelPosition(anchor, viewport, panel, options) {
@@ -13708,7 +13845,7 @@ function resolveFloatingPanelPosition(anchor, viewport, panel, options) {
13708
13845
  const margin = options?.margin ?? 12;
13709
13846
  const maxLeft = Math.max(margin, viewport.width - panel.width - margin);
13710
13847
  const preferredLeft = anchor.left + anchor.width / 2 - panel.width / 2;
13711
- const left = clamp2(preferredLeft, margin, maxLeft);
13848
+ const left = clamp3(preferredLeft, margin, maxLeft);
13712
13849
  const belowTop = anchor.bottom + offset;
13713
13850
  const aboveTop = anchor.top - panel.height - offset;
13714
13851
  const fitsBelow = belowTop + panel.height <= viewport.height - margin;
@@ -13716,13 +13853,13 @@ function resolveFloatingPanelPosition(anchor, viewport, panel, options) {
13716
13853
  if (fitsBelow || !fitsAbove) {
13717
13854
  return {
13718
13855
  left,
13719
- top: clamp2(belowTop, margin, Math.max(margin, viewport.height - panel.height - margin)),
13856
+ top: clamp3(belowTop, margin, Math.max(margin, viewport.height - panel.height - margin)),
13720
13857
  placement: "bottom"
13721
13858
  };
13722
13859
  }
13723
13860
  return {
13724
13861
  left,
13725
- top: clamp2(aboveTop, margin, Math.max(margin, viewport.height - panel.height - margin)),
13862
+ top: clamp3(aboveTop, margin, Math.max(margin, viewport.height - panel.height - margin)),
13726
13863
  placement: "top"
13727
13864
  };
13728
13865
  }
@@ -13742,7 +13879,7 @@ function ColorSlider({
13742
13879
  disabled,
13743
13880
  onCommit
13744
13881
  }) {
13745
- const trackRef = useRef25(null);
13882
+ const trackRef = useRef26(null);
13746
13883
  const percent = (value - min) / (max - min) * 100;
13747
13884
  const commitFromClientX = (clientX) => {
13748
13885
  const rect = trackRef.current?.getBoundingClientRect();
@@ -13817,12 +13954,12 @@ function ColorField({
13817
13954
  disabled,
13818
13955
  onCommit
13819
13956
  }) {
13820
- const buttonRef = useRef25(null);
13821
- const panelRef = useRef25(null);
13822
- const [open, setOpen] = useState22(false);
13823
- const [panelPosition, setPanelPosition] = useState22(null);
13824
- const [draftColor, setDraftColor] = useState22(() => colorFromCss(value));
13825
- const [hexDraft, setHexDraft] = useState22(() => toHexColor(colorFromCss(value)).toUpperCase());
13957
+ const buttonRef = useRef26(null);
13958
+ const panelRef = useRef26(null);
13959
+ const [open, setOpen] = useState23(false);
13960
+ const [panelPosition, setPanelPosition] = useState23(null);
13961
+ const [draftColor, setDraftColor] = useState23(() => colorFromCss(value));
13962
+ const [hexDraft, setHexDraft] = useState23(() => toHexColor(colorFromCss(value)).toUpperCase());
13826
13963
  const hsv = rgbToHsv(draftColor);
13827
13964
  const hueColor = formatCssColor({
13828
13965
  ...hsvToRgb({ hue: hsv.hue, saturation: 1, value: 1 }),
@@ -14084,7 +14221,7 @@ function ColorField({
14084
14221
  }
14085
14222
 
14086
14223
  // src/components/editor/propertyPanelFont.tsx
14087
- import { useEffect as useEffect20, useMemo as useMemo12, useRef as useRef26, useState as useState23 } from "react";
14224
+ import { useEffect as useEffect20, useMemo as useMemo12, useRef as useRef27, useState as useState24 } from "react";
14088
14225
 
14089
14226
  // src/components/editor/fontAssets.ts
14090
14227
  var FONT_EXT_RE = /\.(eot|otf|ttc|ttf|woff2?)$/i;
@@ -14196,18 +14333,18 @@ function FontFamilyField({
14196
14333
  onCommit
14197
14334
  }) {
14198
14335
  const currentFamily = primaryFontFamily(value);
14199
- const containerRef = useRef26(null);
14200
- const inputRef = useRef26(null);
14201
- const fontInputRef = useRef26(null);
14202
- const [open, setOpen] = useState23(false);
14203
- const [query, setQuery] = useState23("");
14204
- const [localFonts, setLocalFonts] = useState23([]);
14205
- const [localFontData, setLocalFontData] = useState23([]);
14206
- const [googleFonts, setGoogleFonts] = useState23(() => [...POPULAR_GOOGLE_FONT_FAMILIES]);
14207
- const [loadingLocalFonts, setLoadingLocalFonts] = useState23(false);
14208
- const [loadingGoogleFonts, setLoadingGoogleFonts] = useState23(false);
14209
- const [importingFonts, setImportingFonts] = useState23(false);
14210
- const [fontNotice, setFontNotice] = useState23(null);
14336
+ const containerRef = useRef27(null);
14337
+ const inputRef = useRef27(null);
14338
+ const fontInputRef = useRef27(null);
14339
+ const [open, setOpen] = useState24(false);
14340
+ const [query, setQuery] = useState24("");
14341
+ const [localFonts, setLocalFonts] = useState24([]);
14342
+ const [localFontData, setLocalFontData] = useState24([]);
14343
+ const [googleFonts, setGoogleFonts] = useState24(() => [...POPULAR_GOOGLE_FONT_FAMILIES]);
14344
+ const [loadingLocalFonts, setLoadingLocalFonts] = useState24(false);
14345
+ const [loadingGoogleFonts, setLoadingGoogleFonts] = useState24(false);
14346
+ const [importingFonts, setImportingFonts] = useState24(false);
14347
+ const [fontNotice, setFontNotice] = useState24(null);
14211
14348
  const canQueryLocalFonts = typeof window !== "undefined" && typeof window.queryLocalFonts === "function";
14212
14349
  useEffect20(() => {
14213
14350
  if (!open) return;
@@ -14496,7 +14633,7 @@ function FontFamilyField({
14496
14633
  }
14497
14634
 
14498
14635
  // src/components/editor/propertyPanelStyleSections.tsx
14499
- import { useEffect as useEffect21, useState as useState26 } from "react";
14636
+ import { useEffect as useEffect21, useState as useState27 } from "react";
14500
14637
 
14501
14638
  // src/components/editor/gradientValue.ts
14502
14639
  var RADIAL_SIZE_KEYWORDS = [
@@ -14557,7 +14694,7 @@ function hasCssWord(value, word) {
14557
14694
  function parsePercentToken(value, fallback) {
14558
14695
  if (!value?.endsWith("%")) return fallback;
14559
14696
  const parsed = parseCssNumber(value.slice(0, -1));
14560
- return parsed == null ? fallback : clamp3(parsed, 0, 100);
14697
+ return parsed == null ? fallback : clamp4(parsed, 0, 100);
14561
14698
  }
14562
14699
  function parseAngleToken(value) {
14563
14700
  const trimmed = value?.trim().toLowerCase();
@@ -14579,13 +14716,13 @@ function trailingPercentStart(value) {
14579
14716
  if (start === 0 || !isWhitespace(withoutUnit[start - 1])) return null;
14580
14717
  return start;
14581
14718
  }
14582
- function clamp3(value, min, max) {
14719
+ function clamp4(value, min, max) {
14583
14720
  return Math.min(max, Math.max(min, value));
14584
14721
  }
14585
14722
  var round = roundToCenti;
14586
14723
  function parsePercent(value, fallback) {
14587
14724
  const parsed = parseCssNumber(value);
14588
- return parsed == null ? fallback : clamp3(parsed, 0, 100);
14725
+ return parsed == null ? fallback : clamp4(parsed, 0, 100);
14589
14726
  }
14590
14727
  function parseColorStop(raw) {
14591
14728
  const trimmed = raw.trim();
@@ -14616,7 +14753,7 @@ function normalizeStops(stops) {
14616
14753
  }));
14617
14754
  return result.map((stop) => ({
14618
14755
  color: stop.color,
14619
- position: round(clamp3(stop.position, 0, 100))
14756
+ position: round(clamp4(stop.position, 0, 100))
14620
14757
  }));
14621
14758
  }
14622
14759
  function splitGradientArgs(value) {
@@ -14779,7 +14916,7 @@ function formatHex(channel) {
14779
14916
  return channel.toString(16).padStart(2, "0");
14780
14917
  }
14781
14918
  function interpolateGradientStopColor(model, position) {
14782
- const clampedPosition = clamp3(position, 0, 100);
14919
+ const clampedPosition = clamp4(position, 0, 100);
14783
14920
  const sortedStops = [...model.stops].sort((a, b) => a.position - b.position);
14784
14921
  const exact = sortedStops.find((stop) => Math.abs(stop.position - clampedPosition) < 1e-3);
14785
14922
  if (exact) return exact.color;
@@ -14803,7 +14940,7 @@ function interpolateGradientStopColor(model, position) {
14803
14940
  return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
14804
14941
  }
14805
14942
  function insertGradientStop(model, position) {
14806
- const clampedPosition = round(clamp3(position, 0, 100));
14943
+ const clampedPosition = round(clamp4(position, 0, 100));
14807
14944
  const color = interpolateGradientStopColor(model, clampedPosition);
14808
14945
  const nextStops = [...model.stops, { color, position: clampedPosition }].sort(
14809
14946
  (a, b) => a.position - b.position
@@ -14840,7 +14977,7 @@ function parseColorString(value) {
14840
14977
  }
14841
14978
 
14842
14979
  // src/components/editor/propertyPanelFill.tsx
14843
- import { useMemo as useMemo13, useRef as useRef27, useState as useState24 } from "react";
14980
+ import { useMemo as useMemo13, useRef as useRef28, useState as useState25 } from "react";
14844
14981
  import { jsx as jsx37, jsxs as jsxs30 } from "react/jsx-runtime";
14845
14982
  function normalizeProjectPath(value) {
14846
14983
  const trimmed = value.trim();
@@ -14881,8 +15018,8 @@ function ImageFillField({
14881
15018
  onCommit,
14882
15019
  onImportAssets
14883
15020
  }) {
14884
- const fileInputRef = useRef27(null);
14885
- const [uploading, setUploading] = useState24(false);
15021
+ const fileInputRef = useRef28(null);
15022
+ const [uploading, setUploading] = useState25(false);
14886
15023
  const imageAssets = useMemo13(() => assets.filter((a) => IMAGE_EXT.test(a)), [assets]);
14887
15024
  const selectedAsset = useMemo13(
14888
15025
  () => resolveSelectedAsset(value, sourceFile, imageAssets),
@@ -14981,7 +15118,7 @@ function GradientField({
14981
15118
  disabled,
14982
15119
  onCommit
14983
15120
  }) {
14984
- const previewRef = useRef27(null);
15121
+ const previewRef = useRef28(null);
14985
15122
  const parsed = parseGradient(value) ?? buildDefaultGradientModel(fallbackColor);
14986
15123
  const commit = (next) => onCommit(serializeGradient(next));
14987
15124
  const patch = (partial) => commit({ ...parsed, ...partial });
@@ -15210,7 +15347,7 @@ function GradientField({
15210
15347
  }
15211
15348
 
15212
15349
  // src/components/editor/BorderRadiusEditor.tsx
15213
- import { useCallback as useCallback26, useState as useState25 } from "react";
15350
+ import { useCallback as useCallback26, useState as useState26 } from "react";
15214
15351
  import { jsx as jsx38, jsxs as jsxs31 } from "react/jsx-runtime";
15215
15352
  var PREVIEW_W = 72;
15216
15353
  var PREVIEW_H = 52;
@@ -15231,7 +15368,7 @@ function BorderRadiusEditor({
15231
15368
  onCommit
15232
15369
  }) {
15233
15370
  const uniform = tl === tr && tr === br && br === bl;
15234
- const [linked, setLinked] = useState25(uniform);
15371
+ const [linked, setLinked] = useState26(uniform);
15235
15372
  const maxVal = Math.max(tl, tr, br, bl, 1);
15236
15373
  const sTL = scaleRadius(tl, maxVal);
15237
15374
  const sTR = scaleRadius(tr, maxVal);
@@ -15451,7 +15588,7 @@ function StyleSections({
15451
15588
  const backgroundImage = styles["background-image"] ?? "none";
15452
15589
  const hasTextControls = isTextEditableSelection(element);
15453
15590
  const fillMode = backgroundImage && backgroundImage !== "none" ? backgroundImage.includes("gradient") ? "Gradient" : "Image" : "Solid";
15454
- const [preferredFillMode, setPreferredFillMode] = useState26(fillMode);
15591
+ const [preferredFillMode, setPreferredFillMode] = useState27(fillMode);
15455
15592
  const imageUrl = extractBackgroundImageUrl(backgroundImage);
15456
15593
  useEffect21(() => {
15457
15594
  setPreferredFillMode(fillMode);
@@ -15838,11 +15975,11 @@ function TextAreaField({
15838
15975
  autoFocus,
15839
15976
  onCommit
15840
15977
  }) {
15841
- const [draft, setDraft] = useState27(value);
15842
- const textareaRef = useRef28(null);
15843
- const commitTimerRef = useRef28(null);
15844
- const focusedRef = useRef28(false);
15845
- const valueRef = useRef28(value);
15978
+ const [draft, setDraft] = useState28(value);
15979
+ const textareaRef = useRef29(null);
15980
+ const commitTimerRef = useRef29(null);
15981
+ const focusedRef = useRef29(false);
15982
+ const valueRef = useRef29(value);
15846
15983
  valueRef.current = value;
15847
15984
  useEffect22(() => {
15848
15985
  if (focusedRef.current) return;
@@ -16092,7 +16229,7 @@ function TextSection({
16092
16229
  onRemoveTextField
16093
16230
  }) {
16094
16231
  const hasTextControls = isTextEditableSelection(element);
16095
- const [activeTextFieldKey, setActiveTextFieldKey] = useState27(
16232
+ const [activeTextFieldKey, setActiveTextFieldKey] = useState28(
16096
16233
  element.textFields[0]?.key ?? null
16097
16234
  );
16098
16235
  useEffect22(() => {
@@ -16185,7 +16322,7 @@ function TextSection({
16185
16322
  }
16186
16323
 
16187
16324
  // src/components/editor/GsapAnimationSection.tsx
16188
- import { memo as memo19, useState as useState30 } from "react";
16325
+ import { memo as memo19, useState as useState31 } from "react";
16189
16326
 
16190
16327
  // src/components/editor/gsapAnimationConstants.ts
16191
16328
  var METHOD_LABELS = {
@@ -16350,7 +16487,7 @@ var ADD_METHOD_LABELS = {
16350
16487
  };
16351
16488
 
16352
16489
  // src/components/editor/AnimationCard.tsx
16353
- import { memo as memo18, useCallback as useCallback29, useMemo as useMemo14, useState as useState29 } from "react";
16490
+ import { memo as memo18, useCallback as useCallback29, useMemo as useMemo14, useState as useState30 } from "react";
16354
16491
  import { SUPPORTED_EASES, SUPPORTED_PROPS } from "@hyperframes/core/gsap-constants";
16355
16492
 
16356
16493
  // src/components/editor/gsapAnimationHelpers.ts
@@ -16388,7 +16525,7 @@ function buildTweenSummary(animation) {
16388
16525
  }
16389
16526
 
16390
16527
  // src/components/editor/EaseCurveSection.tsx
16391
- import { useCallback as useCallback27, useRef as useRef29, useState as useState28 } from "react";
16528
+ import { useCallback as useCallback27, useRef as useRef30, useState as useState29 } from "react";
16392
16529
  import { Fragment as Fragment12, jsx as jsx41, jsxs as jsxs34 } from "react/jsx-runtime";
16393
16530
  var PRESET_GRID_EASES = [
16394
16531
  "none",
@@ -16479,12 +16616,12 @@ function EaseCurveSection({
16479
16616
  const curveFromPreset = EASE_CURVES[ease];
16480
16617
  const customPoints = isCustom ? parseCustomEaseFromString(ease) : null;
16481
16618
  const curve = isCustom && customPoints ? [customPoints.x1, customPoints.y1, customPoints.x2, customPoints.y2] : curveFromPreset ?? null;
16482
- const [draft, setDraft] = useState28(null);
16483
- const [progress, setProgress] = useState28(null);
16484
- const [hover, setHover] = useState28(null);
16485
- const draggingRef = useRef29(null);
16486
- const svgRef = useRef29(null);
16487
- const rafRef = useRef29(0);
16619
+ const [draft, setDraft] = useState29(null);
16620
+ const [progress, setProgress] = useState29(null);
16621
+ const [hover, setHover] = useState29(null);
16622
+ const draggingRef = useRef30(null);
16623
+ const svgRef = useRef30(null);
16624
+ const rafRef = useRef30(0);
16488
16625
  const play = useCallback27(() => {
16489
16626
  const start = performance.now();
16490
16627
  const dur = 1100;
@@ -17168,10 +17305,10 @@ var AnimationCard = memo18(function AnimationCard2({
17168
17305
  onSetAllKeyframeEases,
17169
17306
  onUnroll
17170
17307
  }) {
17171
- const [expanded, setExpanded] = useState29(defaultExpanded);
17172
- const [addingProp, setAddingProp] = useState29(false);
17173
- const [addingFromProp, setAddingFromProp] = useState29(false);
17174
- const [expandedKfPct, setExpandedKfPct] = useState29(null);
17308
+ const [expanded, setExpanded] = useState30(defaultExpanded);
17309
+ const [addingProp, setAddingProp] = useState30(false);
17310
+ const [addingFromProp, setAddingFromProp] = useState30(false);
17311
+ const [expandedKfPct, setExpandedKfPct] = useState30(null);
17175
17312
  const usedProps = useMemo14(
17176
17313
  () => new Set(Object.keys(animation.properties)),
17177
17314
  [animation.properties]
@@ -17228,7 +17365,7 @@ var AnimationCard = memo18(function AnimationCard2({
17228
17365
  },
17229
17366
  [animation.id, onUpdateMeta]
17230
17367
  );
17231
- const [copied, setCopied] = useState29(false);
17368
+ const [copied, setCopied] = useState30(false);
17232
17369
  const methodLabel = METHOD_LABELS[animation.method] ?? animation.method;
17233
17370
  const easeName = (animation.keyframes ? animation.keyframes.easeEach : void 0) ?? animation.ease ?? "none";
17234
17371
  const easeLabel = easeName.startsWith("custom(") ? "Custom curve" : EASE_LABELS[easeName] ?? easeName;
@@ -17505,7 +17642,7 @@ var GsapAnimationSection = memo19(function GsapAnimationSection2({
17505
17642
  onSetAllKeyframeEases,
17506
17643
  onUnroll
17507
17644
  }) {
17508
- const [addMenuOpen, setAddMenuOpen] = useState30(false);
17645
+ const [addMenuOpen, setAddMenuOpen] = useState31(false);
17509
17646
  return /* @__PURE__ */ jsxs40(Section, { title: "Animation", icon: /* @__PURE__ */ jsx47(Film, { size: 15 }), children: [
17510
17647
  multipleTimelines && /* @__PURE__ */ jsx47("p", { className: "mb-2 rounded-lg bg-amber-500/10 px-3 py-2 text-[11px] leading-relaxed text-amber-400", children: "This file has multiple GSAP timelines. Animation editing is disabled to prevent data loss \u2014 consolidate into a single timeline to enable editing." }),
17511
17648
  unsupportedTimelinePattern && /* @__PURE__ */ jsx47("p", { className: "mb-2 rounded-lg bg-amber-500/10 px-3 py-2 text-[11px] leading-relaxed text-amber-400", children: `This timeline uses a computed key (window.__timelines[variable]) the editor can't resolve statically. Use a string-literal key (window.__timelines["id"]) or a variable declaration (const tl = gsap.timeline()) to enable editing.` }),
@@ -17572,7 +17709,7 @@ var GsapAnimationSection = memo19(function GsapAnimationSection2({
17572
17709
  });
17573
17710
 
17574
17711
  // src/components/editor/propertyPanel3dTransform.tsx
17575
- import { useState as useState32 } from "react";
17712
+ import { useState as useState33 } from "react";
17576
17713
 
17577
17714
  // src/components/editor/KeyframeNavigation.tsx
17578
17715
  import { memo as memo21 } from "react";
@@ -17753,7 +17890,7 @@ var KeyframeNavigation = memo21(function KeyframeNavigation2({
17753
17890
  });
17754
17891
 
17755
17892
  // src/components/editor/Transform3DCube.tsx
17756
- import { useEffect as useEffect23, useRef as useRef30, useState as useState31 } from "react";
17893
+ import { useEffect as useEffect23, useRef as useRef31, useState as useState32 } from "react";
17757
17894
 
17758
17895
  // src/components/editor/transform3dProjection.ts
17759
17896
  var DEG = Math.PI / 180;
@@ -17867,14 +18004,14 @@ function Transform3DCube({
17867
18004
  onKeyframe,
17868
18005
  keyframed
17869
18006
  }) {
17870
- const [draft, setDraft] = useState31(null);
17871
- const [depthDraft, setDepthDraft] = useState31(null);
17872
- const dragRef = useRef30(null);
18007
+ const [draft, setDraft] = useState32(null);
18008
+ const [depthDraft, setDepthDraft] = useState32(null);
18009
+ const dragRef = useRef31(null);
17873
18010
  const shown = draft ?? pose;
17874
18011
  const shownZ = depthDraft ?? z;
17875
- const svgRef = useRef30(null);
18012
+ const svgRef = useRef31(null);
17876
18013
  const lens = perspective > 0 ? perspective : defaultPerspective;
17877
- const depthRef = useRef30({ z, onDepthDraft, onDepthCommit, lens });
18014
+ const depthRef = useRef31({ z, onDepthDraft, onDepthCommit, lens });
17878
18015
  depthRef.current = { z, onDepthDraft, onDepthCommit, lens };
17879
18016
  useEffect23(() => {
17880
18017
  const el = svgRef.current;
@@ -18244,7 +18381,7 @@ function PropertyPanel3dTransform({
18244
18381
  onConvertToKeyframes,
18245
18382
  onLivePreviewProps
18246
18383
  }) {
18247
- const [collapsed, setCollapsed] = useState32(false);
18384
+ const [collapsed, setCollapsed] = useState33(false);
18248
18385
  const ctx2 = {
18249
18386
  element,
18250
18387
  gsapRuntimeValues,
@@ -18510,12 +18647,12 @@ var PropertyPanel = memo22(function PropertyPanel2({
18510
18647
  }) {
18511
18648
  const styles = element?.computedStyles ?? EMPTY_STYLES;
18512
18649
  const { showToast } = useStudioShellContext();
18513
- const [clipboardCopied, setClipboardCopied] = useState33(false);
18514
- const clipboardTimerRef = useRef31(void 0);
18650
+ const [clipboardCopied, setClipboardCopied] = useState34(false);
18651
+ const clipboardTimerRef = useRef32(void 0);
18515
18652
  const storeTime = usePlayerStore((s) => s.currentTime);
18516
18653
  const isPlaying = usePlayerStore((s) => s.isPlaying);
18517
- const liveTimeRef = useRef31(storeTime);
18518
- const [, forceRender] = useState33(0);
18654
+ const liveTimeRef = useRef32(storeTime);
18655
+ const [, forceRender] = useState34(0);
18519
18656
  useEffect24(() => {
18520
18657
  if (!isPlaying) return;
18521
18658
  let timerId = 0;
@@ -18934,11 +19071,11 @@ var PropertyPanel = memo22(function PropertyPanel2({
18934
19071
  });
18935
19072
 
18936
19073
  // src/components/editor/FileTree.tsx
18937
- import { memo as memo24, useState as useState35, useCallback as useCallback31, useMemo as useMemo17, useRef as useRef33 } from "react";
19074
+ import { memo as memo24, useState as useState36, useCallback as useCallback31, useMemo as useMemo17, useRef as useRef34 } from "react";
18938
19075
  import { Plus as Plus2, FolderSimplePlus as FolderSimplePlus2 } from "@phosphor-icons/react";
18939
19076
 
18940
19077
  // src/components/editor/FileTreeNodes.tsx
18941
- import { memo as memo23, useState as useState34, useCallback as useCallback30, useMemo as useMemo16, useRef as useRef32, useEffect as useEffect25 } from "react";
19078
+ import { memo as memo23, useState as useState35, useCallback as useCallback30, useMemo as useMemo16, useRef as useRef33, useEffect as useEffect25 } from "react";
18942
19079
  import {
18943
19080
  PencilSimple,
18944
19081
  Copy,
@@ -19053,7 +19190,7 @@ function ContextMenu({
19053
19190
  onDuplicate,
19054
19191
  onDelete
19055
19192
  }) {
19056
- const menuRef = useRef32(null);
19193
+ const menuRef = useRef33(null);
19057
19194
  useEffect25(() => {
19058
19195
  const handleClickOutside = (e) => {
19059
19196
  if (menuRef.current && !menuRef.current.contains(e.target)) {
@@ -19182,9 +19319,9 @@ function InlineInput({
19182
19319
  onCommit,
19183
19320
  onCancel
19184
19321
  }) {
19185
- const inputRef = useRef32(null);
19186
- const committedRef = useRef32(false);
19187
- const [value, setValue] = useState34(defaultValue);
19322
+ const inputRef = useRef33(null);
19323
+ const committedRef = useRef33(false);
19324
+ const [value, setValue] = useState35(defaultValue);
19188
19325
  useEffect25(() => {
19189
19326
  const el = inputRef.current;
19190
19327
  if (!el) return;
@@ -19246,7 +19383,7 @@ function DeleteConfirm({
19246
19383
  onConfirm,
19247
19384
  onCancel
19248
19385
  }) {
19249
- const ref = useRef32(null);
19386
+ const ref = useRef33(null);
19250
19387
  useEffect25(() => {
19251
19388
  const handleEscape = (e) => {
19252
19389
  if (e.key === "Escape") onCancel();
@@ -19364,7 +19501,7 @@ var TreeFolder = memo23(function TreeFolder2({
19364
19501
  dragOverFolder,
19365
19502
  lintFindingsByFile
19366
19503
  }) {
19367
- const [isOpen, setIsOpen] = useState34(defaultOpen);
19504
+ const [isOpen, setIsOpen] = useState35(defaultOpen);
19368
19505
  const toggle = useCallback30(() => setIsOpen((v) => !v), []);
19369
19506
  const children = useMemo16(() => sortChildren(node.children), [node.children]);
19370
19507
  const Chevron = isOpen ? ChevronDown : ChevronRight;
@@ -19499,11 +19636,11 @@ var FileTree = memo24(function FileTree2({
19499
19636
  }) {
19500
19637
  const tree = useMemo17(() => buildTree(files), [files]);
19501
19638
  const children = useMemo17(() => sortChildren(tree.children), [tree]);
19502
- const [contextMenu, setContextMenu] = useState35(null);
19503
- const [inlineInput, setInlineInput] = useState35(null);
19504
- const [deleteTarget, setDeleteTarget] = useState35(null);
19505
- const [dragOverFolder, setDragOverFolder] = useState35(null);
19506
- const dragSourceRef = useRef33(null);
19639
+ const [contextMenu, setContextMenu] = useState36(null);
19640
+ const [inlineInput, setInlineInput] = useState36(null);
19641
+ const [deleteTarget, setDeleteTarget] = useState36(null);
19642
+ const [dragOverFolder, setDragOverFolder] = useState36(null);
19643
+ const dragSourceRef = useRef34(null);
19507
19644
  const hasFileOps = !!(onCreateFile || onCreateFolder || onDeleteFile || onRenameFile || onDuplicateFile);
19508
19645
  const handleContextMenu = useCallback31(
19509
19646
  (e, path, isFolder) => {
@@ -19739,14 +19876,14 @@ var FileTree = memo24(function FileTree2({
19739
19876
  });
19740
19877
 
19741
19878
  // src/App.tsx
19742
- import { useState as useState91, useCallback as useCallback106, useRef as useRef89, useMemo as useMemo37, useEffect as useEffect66, useLayoutEffect as useLayoutEffect3 } from "react";
19879
+ import { useState as useState92, useCallback as useCallback108, useRef as useRef90, useMemo as useMemo37, useEffect as useEffect67, useLayoutEffect as useLayoutEffect3 } from "react";
19743
19880
 
19744
19881
  // src/components/renders/useRenderQueue.ts
19745
- import { useState as useState36, useEffect as useEffect26, useCallback as useCallback32, useRef as useRef34, useMemo as useMemo18 } from "react";
19882
+ import { useState as useState37, useEffect as useEffect26, useCallback as useCallback32, useRef as useRef35, useMemo as useMemo18 } from "react";
19746
19883
  function useRenderQueue(projectId) {
19747
- const [jobs, setJobs] = useState36([]);
19748
- const eventSourceRef = useRef34(null);
19749
- const activeJobRef = useRef34(null);
19884
+ const [jobs, setJobs] = useState37([]);
19885
+ const eventSourceRef = useRef35(null);
19886
+ const activeJobRef = useRef35(null);
19750
19887
  const loadRenders = useCallback32(async () => {
19751
19888
  if (!projectId) return;
19752
19889
  try {
@@ -19916,7 +20053,7 @@ function useRenderQueue(projectId) {
19916
20053
  }
19917
20054
 
19918
20055
  // src/components/LintModal.tsx
19919
- import { useState as useState37 } from "react";
20056
+ import { useState as useState38 } from "react";
19920
20057
  import { XIcon, WarningIcon, CheckCircleIcon, CaretRightIcon } from "@phosphor-icons/react";
19921
20058
  import { jsx as jsx58, jsxs as jsxs49 } from "react/jsx-runtime";
19922
20059
  function LintModal({
@@ -19927,7 +20064,7 @@ function LintModal({
19927
20064
  const errors = findings.filter((f) => f.severity === "error");
19928
20065
  const warnings = findings.filter((f) => f.severity === "warning");
19929
20066
  const hasIssues = findings.length > 0;
19930
- const [copied, setCopied] = useState37(false);
20067
+ const [copied, setCopied] = useState38(false);
19931
20068
  const handleCopyToAgent = async () => {
19932
20069
  const lines = findings.map((f) => {
19933
20070
  let line = `[${f.severity}] ${f.message}`;
@@ -20036,7 +20173,7 @@ ${lines.join("\n\n")}`;
20036
20173
  }
20037
20174
 
20038
20175
  // src/components/AskAgentModal.tsx
20039
- import { useState as useState38, useRef as useRef35 } from "react";
20176
+ import { useState as useState39, useRef as useRef36 } from "react";
20040
20177
 
20041
20178
  // src/utils/studioHelpers.ts
20042
20179
  function getTimelineElementLabel(element) {
@@ -20218,8 +20355,8 @@ function AskAgentModal({
20218
20355
  onSubmit,
20219
20356
  onClose
20220
20357
  }) {
20221
- const [value, setValue] = useState38("");
20222
- const inputRef = useRef35(null);
20358
+ const [value, setValue] = useState39("");
20359
+ const inputRef = useRef36(null);
20223
20360
  const modalPositionStyle = getAgentModalPositionStyle(anchorPoint);
20224
20361
  useMountEffect(() => {
20225
20362
  requestAnimationFrame(() => inputRef.current?.focus());
@@ -20458,7 +20595,7 @@ function SaveQueuePausedBanner({ message, onDismiss }) {
20458
20595
  }
20459
20596
 
20460
20597
  // src/captions/hooks/useCaptionSync.ts
20461
- import { useCallback as useCallback33, useRef as useRef36 } from "react";
20598
+ import { useCallback as useCallback33, useRef as useRef37 } from "react";
20462
20599
  function buildOverrides(model) {
20463
20600
  const entries = [];
20464
20601
  let globalWordIndex = 0;
@@ -20489,10 +20626,10 @@ function buildOverrides(model) {
20489
20626
  return entries;
20490
20627
  }
20491
20628
  function useCaptionSync(projectId) {
20492
- const projectIdRef = useRef36(projectId);
20629
+ const projectIdRef = useRef37(projectId);
20493
20630
  projectIdRef.current = projectId;
20494
- const debounceRef = useRef36(null);
20495
- const suppressSaveRef = useRef36(false);
20631
+ const debounceRef = useRef37(null);
20632
+ const suppressSaveRef = useRef37(false);
20496
20633
  const save = useCallback33(() => {
20497
20634
  const state = useCaptionStore.getState();
20498
20635
  if (!state.model || !state.sourceFilePath || !state.isEditMode) return;
@@ -20581,7 +20718,7 @@ function useCaptionSync(projectId) {
20581
20718
  }
20582
20719
 
20583
20720
  // src/hooks/usePersistentEditHistory.ts
20584
- import { useCallback as useCallback34, useEffect as useEffect27, useMemo as useMemo19, useRef as useRef37, useState as useState39 } from "react";
20721
+ import { useCallback as useCallback34, useEffect as useEffect27, useMemo as useMemo19, useRef as useRef38, useState as useState40 } from "react";
20585
20722
 
20586
20723
  // src/utils/editHistory.ts
20587
20724
  var DEFAULT_MAX_ENTRIES = 100;
@@ -20931,10 +21068,10 @@ function usePersistentEditHistory(options) {
20931
21068
  [options.storage]
20932
21069
  );
20933
21070
  const now = options.now ?? Date.now;
20934
- const [state, setState] = useState39(() => createEmptyEditHistory());
20935
- const [loaded, setLoaded] = useState39(false);
21071
+ const [state, setState] = useState40(() => createEmptyEditHistory());
21072
+ const [loaded, setLoaded] = useState40(false);
20936
21073
  const projectId = options.projectId;
20937
- const storeRef = useRef37(null);
21074
+ const storeRef = useRef38(null);
20938
21075
  useEffect27(() => {
20939
21076
  let cancelled = false;
20940
21077
  const emptyState = createEmptyEditHistory();
@@ -20991,25 +21128,25 @@ function usePersistentEditHistory(options) {
20991
21128
  }
20992
21129
 
20993
21130
  // src/hooks/usePanelLayout.ts
20994
- import { useState as useState40, useCallback as useCallback35, useRef as useRef38 } from "react";
21131
+ import { useState as useState41, useCallback as useCallback35, useRef as useRef39 } from "react";
20995
21132
  function getInitialRightInspectorPanes(tab) {
20996
21133
  if (tab === "layers") return { layers: true, design: false };
20997
21134
  return { layers: false, design: true };
20998
21135
  }
20999
21136
  function usePanelLayout(initialState2) {
21000
- const [leftWidth, setLeftWidth] = useState40(240);
21001
- const [rightWidth, setRightWidth] = useState40(400);
21002
- const [leftCollapsed, setLeftCollapsed] = useState40(
21137
+ const [leftWidth, setLeftWidth] = useState41(240);
21138
+ const [rightWidth, setRightWidth] = useState41(400);
21139
+ const [leftCollapsed, setLeftCollapsed] = useState41(
21003
21140
  () => readStudioUiPreferences().leftCollapsed ?? false
21004
21141
  );
21005
- const [rightCollapsed, setRightCollapsed] = useState40(initialState2?.rightCollapsed ?? true);
21006
- const [rightPanelTab, setRightPanelTab] = useState40(
21142
+ const [rightCollapsed, setRightCollapsed] = useState41(initialState2?.rightCollapsed ?? true);
21143
+ const [rightPanelTab, setRightPanelTab] = useState41(
21007
21144
  initialState2?.rightPanelTab ?? "renders"
21008
21145
  );
21009
- const [rightInspectorPanes, setRightInspectorPanes] = useState40(
21146
+ const [rightInspectorPanes, setRightInspectorPanes] = useState41(
21010
21147
  () => getInitialRightInspectorPanes(initialState2?.rightPanelTab)
21011
21148
  );
21012
- const panelDragRef = useRef38(null);
21149
+ const panelDragRef = useRef39(null);
21013
21150
  const toggleLeftSidebar = useCallback35(() => {
21014
21151
  setLeftCollapsed((collapsed) => {
21015
21152
  writeStudioUiPreferences({ leftCollapsed: !collapsed });
@@ -21084,7 +21221,7 @@ function usePanelLayout(initialState2) {
21084
21221
  }
21085
21222
 
21086
21223
  // src/hooks/useFileManager.ts
21087
- import { useState as useState42, useCallback as useCallback38, useRef as useRef40 } from "react";
21224
+ import { useState as useState43, useCallback as useCallback38, useRef as useRef41 } from "react";
21088
21225
 
21089
21226
  // src/utils/studioSaveDiagnostics.ts
21090
21227
  var STUDIO_SAVE_ATTEMPT_PROPERTY = "__studioSaveAttempt";
@@ -21227,12 +21364,12 @@ async function retryStudioSave(operation, options = {}) {
21227
21364
  }
21228
21365
 
21229
21366
  // src/hooks/useFileTree.ts
21230
- import { useState as useState41, useCallback as useCallback36, useEffect as useEffect28, useMemo as useMemo20 } from "react";
21367
+ import { useState as useState42, useCallback as useCallback36, useEffect as useEffect28, useMemo as useMemo20 } from "react";
21231
21368
  function useFileTree({ projectId, projectIdRef }) {
21232
- const [projectDir, setProjectDir] = useState41(null);
21233
- const [fileTree, setFileTree] = useState41([]);
21234
- const [compositionPaths, setCompositionPaths] = useState41([]);
21235
- const [fileTreeLoaded, setFileTreeLoaded] = useState41(false);
21369
+ const [projectDir, setProjectDir] = useState42(null);
21370
+ const [fileTree, setFileTree] = useState42([]);
21371
+ const [compositionPaths, setCompositionPaths] = useState42([]);
21372
+ const [fileTreeLoaded, setFileTreeLoaded] = useState42(false);
21236
21373
  useEffect28(() => {
21237
21374
  if (!projectId) {
21238
21375
  setFileTreeLoaded(false);
@@ -21286,7 +21423,7 @@ function useFileTree({ projectId, projectIdRef }) {
21286
21423
  }
21287
21424
 
21288
21425
  // src/hooks/useEditorSave.ts
21289
- import { useCallback as useCallback37, useRef as useRef39 } from "react";
21426
+ import { useCallback as useCallback37, useRef as useRef40 } from "react";
21290
21427
  function useEditorSave({
21291
21428
  editingPathRef,
21292
21429
  projectIdRef,
@@ -21296,8 +21433,8 @@ function useEditorSave({
21296
21433
  domEditSaveTimestampRef,
21297
21434
  setRefreshKey
21298
21435
  }) {
21299
- const saveRafRef = useRef39(null);
21300
- const refreshRafRef = useRef39(null);
21436
+ const saveRafRef = useRef40(null);
21437
+ const refreshRafRef = useRef40(null);
21301
21438
  const handleContentChange = useCallback37(
21302
21439
  (content) => {
21303
21440
  const pid = projectIdRef.current;
@@ -21351,13 +21488,13 @@ function useFileManager({
21351
21488
  domEditSaveTimestampRef,
21352
21489
  setRefreshKey
21353
21490
  }) {
21354
- const [editingFile, setEditingFile] = useState42(null);
21355
- const [revealSourceOffset, setRevealSourceOffset] = useState42(null);
21356
- const editingPathRef = useRef40(editingFile?.path);
21491
+ const [editingFile, setEditingFile] = useState43(null);
21492
+ const [revealSourceOffset, setRevealSourceOffset] = useState43(null);
21493
+ const editingPathRef = useRef41(editingFile?.path);
21357
21494
  editingPathRef.current = editingFile?.path;
21358
- const projectIdRef = useRef40(projectId);
21495
+ const projectIdRef = useRef41(projectId);
21359
21496
  projectIdRef.current = projectId;
21360
- const importedFontAssetsRef = useRef40([]);
21497
+ const importedFontAssetsRef = useRef41([]);
21361
21498
  const {
21362
21499
  projectDir,
21363
21500
  fileTree,
@@ -21423,8 +21560,8 @@ function useFileManager({
21423
21560
  domEditSaveTimestampRef,
21424
21561
  setRefreshKey
21425
21562
  });
21426
- const revealRequestIdRef = useRef40(0);
21427
- const revealAbortRef = useRef40(null);
21563
+ const revealRequestIdRef = useRef41(0);
21564
+ const revealAbortRef = useRef41(null);
21428
21565
  const handleFileSelect = useCallback38((path) => {
21429
21566
  const pid = projectIdRef.current;
21430
21567
  if (!pid) return;
@@ -21683,7 +21820,7 @@ function useFileManager({
21683
21820
  }
21684
21821
 
21685
21822
  // src/hooks/usePreviewPersistence.ts
21686
- import { useCallback as useCallback39, useRef as useRef41, useState as useState43 } from "react";
21823
+ import { useCallback as useCallback39, useRef as useRef42, useState as useState44 } from "react";
21687
21824
 
21688
21825
  // src/utils/domEditSaveQueue.ts
21689
21826
  var DEFAULT_FAILURE_THRESHOLD = 5;
@@ -21791,12 +21928,12 @@ function usePreviewPersistence({
21791
21928
  }) {
21792
21929
  void _recordEdit;
21793
21930
  void _activeCompPathRef;
21794
- const [domEditSaveQueuePaused, setDomEditSaveQueuePaused] = useState43(null);
21795
- const domTextCommitVersionRef = useRef41(0);
21796
- const showToastRef = useRef41(showToast);
21931
+ const [domEditSaveQueuePaused, setDomEditSaveQueuePaused] = useState44(null);
21932
+ const domTextCommitVersionRef = useRef42(0);
21933
+ const showToastRef = useRef42(showToast);
21797
21934
  showToastRef.current = showToast;
21798
- const domEditSaveQueueRef = useRef41(null);
21799
- const applyStudioManualEditsToPreviewRef = useRef41(async () => {
21935
+ const domEditSaveQueueRef = useRef42(null);
21936
+ const applyStudioManualEditsToPreviewRef = useRef42(async () => {
21800
21937
  });
21801
21938
  if (!domEditSaveQueueRef.current) {
21802
21939
  domEditSaveQueueRef.current = createDomEditSaveQueue({
@@ -21816,7 +21953,7 @@ function usePreviewPersistence({
21816
21953
  }
21817
21954
  });
21818
21955
  }
21819
- const projectIdRef = useRef41(projectId);
21956
+ const projectIdRef = useRef42(projectId);
21820
21957
  projectIdRef.current = projectId;
21821
21958
  const queueDomEditSave = useCallback39((save) => {
21822
21959
  return domEditSaveQueueRef.current?.enqueue(save) ?? save();
@@ -21897,10 +22034,10 @@ function usePreviewPersistence({
21897
22034
  }
21898
22035
 
21899
22036
  // src/hooks/useTimelineEditing.ts
21900
- import { useCallback as useCallback41, useRef as useRef43 } from "react";
22037
+ import { useCallback as useCallback41, useRef as useRef44 } from "react";
21901
22038
 
21902
22039
  // src/hooks/useRazorSplit.ts
21903
- import { useCallback as useCallback40, useRef as useRef42 } from "react";
22040
+ import { useCallback as useCallback40, useRef as useRef43 } from "react";
21904
22041
  function generateSplitId(existingIds, baseId) {
21905
22042
  let newId = `${baseId}-split`;
21906
22043
  let suffix = 2;
@@ -22007,7 +22144,7 @@ function useRazorSplit({
22007
22144
  reloadPreview,
22008
22145
  isRecordingRef
22009
22146
  }) {
22010
- const projectIdRef = useRef42(projectId);
22147
+ const projectIdRef = useRef43(projectId);
22011
22148
  projectIdRef.current = projectId;
22012
22149
  const handleRazorSplit = useCallback40(
22013
22150
  // fallow-ignore-next-line complexity
@@ -22732,10 +22869,10 @@ function useTimelineEditing({
22732
22869
  sdkSession,
22733
22870
  forceReloadSdkSession
22734
22871
  }) {
22735
- const projectIdRef = useRef43(projectId);
22872
+ const projectIdRef = useRef44(projectId);
22736
22873
  projectIdRef.current = projectId;
22737
- const editQueueRef = useRef43(Promise.resolve());
22738
- const lastBlockedTimelineToastAtRef = useRef43(0);
22874
+ const editQueueRef = useRef44(Promise.resolve());
22875
+ const lastBlockedTimelineToastAtRef = useRef44(0);
22739
22876
  const enqueueEdit = useCallback41(
22740
22877
  (element, label, buildPatches, coalesceKey) => {
22741
22878
  if (isRecordingRef?.current) {
@@ -23139,7 +23276,7 @@ function useTimelineEditing({
23139
23276
  import { useCallback as useCallback64 } from "react";
23140
23277
 
23141
23278
  // src/hooks/useAskAgentModal.ts
23142
- import { useState as useState44, useCallback as useCallback42, useRef as useRef44, useEffect as useEffect29 } from "react";
23279
+ import { useState as useState45, useCallback as useCallback42, useRef as useRef45, useEffect as useEffect29 } from "react";
23143
23280
  function useAskAgentModal({
23144
23281
  activeCompPath,
23145
23282
  projectDir,
@@ -23148,14 +23285,14 @@ function useAskAgentModal({
23148
23285
  domEditSelectionRef,
23149
23286
  domEditSelection
23150
23287
  }) {
23151
- const [agentPromptTagSnippet, setAgentPromptTagSnippet] = useState44();
23152
- const [agentPromptSelectionContext, setAgentPromptSelectionContext] = useState44();
23153
- const [agentModalAnchorPoint, setAgentModalAnchorPoint] = useState44(
23288
+ const [agentPromptTagSnippet, setAgentPromptTagSnippet] = useState45();
23289
+ const [agentPromptSelectionContext, setAgentPromptSelectionContext] = useState45();
23290
+ const [agentModalAnchorPoint, setAgentModalAnchorPoint] = useState45(
23154
23291
  null
23155
23292
  );
23156
- const [copiedAgentPrompt, setCopiedAgentPrompt] = useState44(false);
23157
- const [agentModalOpen, setAgentModalOpen] = useState44(false);
23158
- const copiedAgentTimerRef = useRef44(null);
23293
+ const [copiedAgentPrompt, setCopiedAgentPrompt] = useState45(false);
23294
+ const [agentModalOpen, setAgentModalOpen] = useState45(false);
23295
+ const copiedAgentTimerRef = useRef45(null);
23159
23296
  const preloadAgentPromptSnippet = useCallback42(
23160
23297
  async (selection) => {
23161
23298
  const pid = projectIdRef.current;
@@ -23250,7 +23387,7 @@ function useAskAgentModal({
23250
23387
  }
23251
23388
 
23252
23389
  // src/hooks/useDomSelection.ts
23253
- import { useState as useState45, useCallback as useCallback43, useRef as useRef45, useEffect as useEffect30 } from "react";
23390
+ import { useState as useState46, useCallback as useCallback43, useRef as useRef46, useEffect as useEffect30 } from "react";
23254
23391
 
23255
23392
  // src/utils/studioPreviewHelpers.ts
23256
23393
  var FULL_BLEED_RATIO = 0.95;
@@ -23476,14 +23613,14 @@ function useDomSelection({
23476
23613
  refreshKey,
23477
23614
  rightPanelTab
23478
23615
  }) {
23479
- const [domEditSelection, setDomEditSelection] = useState45(null);
23480
- const [domEditGroupSelections, setDomEditGroupSelections] = useState45([]);
23481
- const [domEditHoverSelection, setDomEditHoverSelection] = useState45(null);
23482
- const [activeGroupElement, setActiveGroupElementState] = useState45(null);
23483
- const domEditSelectionRef = useRef45(domEditSelection);
23484
- const domEditGroupSelectionsRef = useRef45(domEditGroupSelections);
23485
- const domEditHoverSelectionRef = useRef45(domEditHoverSelection);
23486
- const activeGroupElementRef = useRef45(activeGroupElement);
23616
+ const [domEditSelection, setDomEditSelection] = useState46(null);
23617
+ const [domEditGroupSelections, setDomEditGroupSelections] = useState46([]);
23618
+ const [domEditHoverSelection, setDomEditHoverSelection] = useState46(null);
23619
+ const [activeGroupElement, setActiveGroupElementState] = useState46(null);
23620
+ const domEditSelectionRef = useRef46(domEditSelection);
23621
+ const domEditGroupSelectionsRef = useRef46(domEditGroupSelections);
23622
+ const domEditHoverSelectionRef = useRef46(domEditHoverSelection);
23623
+ const activeGroupElementRef = useRef46(activeGroupElement);
23487
23624
  domEditSelectionRef.current = domEditSelection;
23488
23625
  domEditGroupSelectionsRef.current = domEditGroupSelections;
23489
23626
  domEditHoverSelectionRef.current = domEditHoverSelection;
@@ -23811,7 +23948,7 @@ function useDomSelection({
23811
23948
  }
23812
23949
 
23813
23950
  // src/hooks/usePreviewInteraction.ts
23814
- import { useCallback as useCallback44, useRef as useRef46 } from "react";
23951
+ import { useCallback as useCallback44, useRef as useRef47 } from "react";
23815
23952
  var CYCLE_RADIUS_PX = 6;
23816
23953
  var CYCLE_WINDOW_MS = 600;
23817
23954
  var DOUBLE_CLICK_MS = 400;
@@ -23828,8 +23965,8 @@ function usePreviewInteraction({
23828
23965
  setActiveGroupElement,
23829
23966
  onClickToSource
23830
23967
  }) {
23831
- const cycleRef = useRef46(null);
23832
- const lastDownRef = useRef46(null);
23968
+ const cycleRef = useRef47(null);
23969
+ const lastDownRef = useRef47(null);
23833
23970
  const handlePreviewCanvasMouseDown = useCallback44(
23834
23971
  // fallow-ignore-next-line complexity
23835
23972
  async (e, options) => {
@@ -23964,7 +24101,7 @@ function usePreviewInteraction({
23964
24101
  }
23965
24102
 
23966
24103
  // src/hooks/useDomEditCommits.ts
23967
- import { useCallback as useCallback49, useRef as useRef48 } from "react";
24104
+ import { useCallback as useCallback49, useRef as useRef49 } from "react";
23968
24105
  import { findUnsafeDomPatchValues as findUnsafeDomPatchValues2 } from "@hyperframes/core/studio-api/finite-mutation";
23969
24106
 
23970
24107
  // src/utils/studioFontHelpers.ts
@@ -24073,7 +24210,7 @@ function useDomEditPositionPatchCommit({
24073
24210
  }
24074
24211
 
24075
24212
  // src/hooks/useDomEditTextCommits.ts
24076
- import { useCallback as useCallback46, useRef as useRef47 } from "react";
24213
+ import { useCallback as useCallback46, useRef as useRef48 } from "react";
24077
24214
  function applyPreviewAttribute(doc, selection, activeCompPath, attr, value, options = {}) {
24078
24215
  if (!doc) return;
24079
24216
  const el = findElementForSelection(doc, selection, activeCompPath);
@@ -24095,7 +24232,7 @@ function useDomEditTextCommits({
24095
24232
  persistDomEditOperations,
24096
24233
  resolveImportedFontAsset
24097
24234
  }) {
24098
- const domTextCommitVersionRef = useRef47(0);
24235
+ const domTextCommitVersionRef = useRef48(0);
24099
24236
  const handleDomStyleCommit = useCallback46(
24100
24237
  async (property, value) => {
24101
24238
  if (!domEditSelection) return;
@@ -24921,8 +25058,8 @@ function computeElementPercentage(currentTime, selection, animation) {
24921
25058
  function getIframeGsap(iframe) {
24922
25059
  if (!iframe?.contentWindow) return null;
24923
25060
  try {
24924
- const gsap = iframe.contentWindow.gsap;
24925
- return gsap?.getProperty ? gsap : null;
25061
+ const gsap2 = iframe.contentWindow.gsap;
25062
+ return gsap2?.getProperty ? gsap2 : null;
24926
25063
  } catch {
24927
25064
  return null;
24928
25065
  }
@@ -25132,7 +25269,7 @@ function useDomEditCommits({
25132
25269
  },
25133
25270
  [fileTree, projectId, importedFontAssetsRef]
25134
25271
  );
25135
- const reportedUnresolvableRef = useRef48(/* @__PURE__ */ new Set());
25272
+ const reportedUnresolvableRef = useRef49(/* @__PURE__ */ new Set());
25136
25273
  const persistDomEditOperations = useCallback49(
25137
25274
  // fallow-ignore-next-line complexity
25138
25275
  async (selection, operations, options) => {
@@ -25411,7 +25548,7 @@ function useGroupCommits(params) {
25411
25548
  }
25412
25549
 
25413
25550
  // src/hooks/useGsapScriptCommits.ts
25414
- import { useCallback as useCallback56, useMemo as useMemo21, useRef as useRef50 } from "react";
25551
+ import { useCallback as useCallback56, useMemo as useMemo21, useRef as useRef51 } from "react";
25415
25552
  import { findUnsafeMutationValues } from "@hyperframes/core/studio-api/finite-mutation";
25416
25553
 
25417
25554
  // src/hooks/gsapKeyframeCacheHelpers.ts
@@ -26381,6 +26518,36 @@ function useGsapKeyframeOps({
26381
26518
  },
26382
26519
  [activeCompPath, commitMutation, trackGsapSaveFailure, sdkSession, sdkDeps]
26383
26520
  );
26521
+ const moveKeyframe = useCallback53(
26522
+ (selection, animationId, fromPercentage, toPercentage) => {
26523
+ const mutation = { type: "move-keyframe", animationId, fromPercentage, toPercentage };
26524
+ void commitMutation(selection, mutation, {
26525
+ label: `Move keyframe to ${toPercentage}%`,
26526
+ softReload: true
26527
+ }).catch((error) => {
26528
+ trackGsapSaveFailure(error, selection, mutation, `Move keyframe to ${toPercentage}%`);
26529
+ });
26530
+ },
26531
+ [commitMutation, trackGsapSaveFailure]
26532
+ );
26533
+ const resizeKeyframedTween = useCallback53(
26534
+ (selection, animationId, position, duration, pctRemap) => {
26535
+ const mutation = {
26536
+ type: "resize-keyframed-tween",
26537
+ animationId,
26538
+ position,
26539
+ duration,
26540
+ pctRemap
26541
+ };
26542
+ void commitMutation(selection, mutation, {
26543
+ label: "Retime keyframe (resize tween)",
26544
+ softReload: true
26545
+ }).catch((error) => {
26546
+ trackGsapSaveFailure(error, selection, mutation, "Retime keyframe (resize tween)");
26547
+ });
26548
+ },
26549
+ [commitMutation, trackGsapSaveFailure]
26550
+ );
26384
26551
  const convertToKeyframes = useCallback53(
26385
26552
  async (selection, animationId, resolvedFromValues, duration) => {
26386
26553
  if (sdkSession && sdkDeps) {
@@ -26407,8 +26574,10 @@ function useGsapKeyframeOps({
26407
26574
  );
26408
26575
  const removeAllKeyframes = useCallback53(
26409
26576
  async (selection, animationId) => {
26577
+ const targetPath = selection.sourceFile || activeCompPath || "index.html";
26578
+ const elementId = selection.id ?? selection.selector?.match(/^#([\w-]+)/)?.[1] ?? null;
26579
+ if (elementId) clearKeyframeCacheForElement(targetPath, elementId);
26410
26580
  if (sdkSession && sdkDeps) {
26411
- const targetPath = selection.sourceFile || activeCompPath || "index.html";
26412
26581
  const handled = await sdkGsapRemoveAllKeyframesPersist(
26413
26582
  targetPath,
26414
26583
  animationId,
@@ -26434,6 +26603,8 @@ function useGsapKeyframeOps({
26434
26603
  addKeyframe,
26435
26604
  addKeyframeBatch,
26436
26605
  removeKeyframe,
26606
+ moveKeyframe,
26607
+ resizeKeyframedTween,
26437
26608
  convertToKeyframes,
26438
26609
  removeAllKeyframes,
26439
26610
  commitKeyframeAtTime
@@ -26441,7 +26612,7 @@ function useGsapKeyframeOps({
26441
26612
  }
26442
26613
 
26443
26614
  // src/hooks/useGsapPropertyDebounce.ts
26444
- import { useCallback as useCallback54, useEffect as useEffect31, useRef as useRef49 } from "react";
26615
+ import { useCallback as useCallback54, useEffect as useEffect31, useRef as useRef50 } from "react";
26445
26616
  import { parseGsapScriptAcorn } from "@hyperframes/core/gsap-parser-acorn";
26446
26617
  var DEBOUNCE_MS = 150;
26447
26618
  function mergeTweenProperties(sdkSession, animationId, edited, kind) {
@@ -26457,9 +26628,9 @@ function mergeTweenProperties(sdkSession, animationId, edited, kind) {
26457
26628
  }
26458
26629
  }
26459
26630
  function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26460
- const pendingPropertyEditRef = useRef49(null);
26461
- const debounceTimerRef = useRef49(null);
26462
- const sdkRef = useRef49(sdk);
26631
+ const pendingPropertyEditRef = useRef50(null);
26632
+ const debounceTimerRef = useRef50(null);
26633
+ const sdkRef = useRef50(sdk);
26463
26634
  sdkRef.current = sdk;
26464
26635
  const flushPendingPropertyEdit = useCallback54(async () => {
26465
26636
  const pending = pendingPropertyEditRef.current;
@@ -26762,7 +26933,7 @@ function applyPreviewSync(iframe, result, options, reloadPreview) {
26762
26933
  }
26763
26934
  }
26764
26935
  function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef, editHistory, domEditSaveTimestampRef, reloadPreview, onCacheInvalidate, onFileContentChanged, showToast, sdkSession, writeProjectFile, forceReloadSdkSession }) {
26765
- const serializerRef = useRef50(createKeyedSerializer());
26936
+ const serializerRef = useRef51(createKeyedSerializer());
26766
26937
  const runCommit = useCallback56(async (selection, mutation, options) => {
26767
26938
  const pid = projectIdRef.current;
26768
26939
  if (!pid) return;
@@ -26881,19 +27052,19 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
26881
27052
  }
26882
27053
 
26883
27054
  // src/hooks/useGsapTweenCache.ts
26884
- import { useEffect as useEffect32, useMemo as useMemo22, useRef as useRef51, useState as useState46, useCallback as useCallback57 } from "react";
27055
+ import { useEffect as useEffect32, useMemo as useMemo22, useRef as useRef52, useState as useState47, useCallback as useCallback57 } from "react";
26885
27056
  import { isStudioHoldSet } from "@hyperframes/core/gsap-parser";
26886
27057
 
26887
27058
  // src/hooks/gsapRuntimeReaders.ts
26888
27059
  import { classifyPropertyGroup as classifyPropertyGroup2 } from "@hyperframes/core/gsap-parser";
26889
27060
  function readGsapProperty(iframe, selector, prop) {
26890
27061
  if (!selector) return null;
26891
- const gsap = getIframeGsap(iframe);
26892
- if (!gsap) return null;
27062
+ const gsap2 = getIframeGsap(iframe);
27063
+ if (!gsap2) return null;
26893
27064
  const el = queryIframeElement(iframe, selector);
26894
27065
  if (!el) return null;
26895
27066
  try {
26896
- const val = Number(gsap.getProperty(el, prop));
27067
+ const val = Number(gsap2.getProperty(el, prop));
26897
27068
  if (!Number.isFinite(val)) return null;
26898
27069
  return POSITION_PROPS.has(prop) ? Math.round(val) : roundTo3(val);
26899
27070
  } catch {
@@ -26924,8 +27095,8 @@ var GSAP_CONFIG_KEYS = /* @__PURE__ */ new Set([
26924
27095
  function readAllAnimatedProperties(iframe, selector, anim, group) {
26925
27096
  const result = {};
26926
27097
  if (!iframe) return result;
26927
- const gsap = getIframeGsap(iframe);
26928
- if (!gsap) return result;
27098
+ const gsap2 = getIframeGsap(iframe);
27099
+ if (!gsap2) return result;
26929
27100
  const el = queryIframeElement(iframe, selector);
26930
27101
  if (!el) return result;
26931
27102
  let doc = null;
@@ -26949,7 +27120,7 @@ function readAllAnimatedProperties(iframe, selector, anim, group) {
26949
27120
  }
26950
27121
  }
26951
27122
  for (const prop of propKeys) {
26952
- const val = Number(gsap.getProperty(el, prop));
27123
+ const val = Number(gsap2.getProperty(el, prop));
26953
27124
  if (Number.isFinite(val)) {
26954
27125
  result[prop] = POSITION_PROPS.has(prop) ? Math.round(val) : roundTo3(val);
26955
27126
  }
@@ -27005,7 +27176,7 @@ function readAllAnimatedProperties(iframe, selector, anim, group) {
27005
27176
  for (const [prop, defaultVal] of Object.entries(UNIVERSAL_BASELINE)) {
27006
27177
  if (prop in result) continue;
27007
27178
  if (!allTweenedProps.has(prop)) continue;
27008
- const val = Number(gsap.getProperty(el, prop));
27179
+ const val = Number(gsap2.getProperty(el, prop));
27009
27180
  if (Number.isFinite(val) && Math.round(val * 1e3) !== Math.round(defaultVal * 1e3)) {
27010
27181
  result[prop] = roundTo3(val);
27011
27182
  }
@@ -27035,7 +27206,7 @@ function readAllAnimatedProperties(iframe, selector, anim, group) {
27035
27206
  for (const prop of COMPUTED_BASELINE) {
27036
27207
  if (prop in result) continue;
27037
27208
  if (otherTweenProps.has(prop)) continue;
27038
- const gsapVal = Number(gsap.getProperty(el, prop));
27209
+ const gsapVal = Number(gsap2.getProperty(el, prop));
27039
27210
  if (!Number.isFinite(gsapVal)) continue;
27040
27211
  let cssVal = NaN;
27041
27212
  if (computedStyle) {
@@ -27134,6 +27305,13 @@ function findPositionSetAnimation(animations, selector) {
27134
27305
  (a) => a.method === "set" && a.targetSelector === selector && ("x" in a.properties || "y" in a.properties)
27135
27306
  ) ?? null;
27136
27307
  }
27308
+ function findExistingPositionWrite(animations, selector) {
27309
+ const set = findPositionSetAnimation(animations, selector);
27310
+ if (set) return set;
27311
+ return animations.find(
27312
+ (a) => a.targetSelector === selector && a.propertyGroup === "position" && !a.keyframes && (a.duration ?? 0) === 0
27313
+ ) ?? null;
27314
+ }
27137
27315
  async function commitStaticGsapPosition(selection, studioOffset, gsapPos, selector, existingSet, callbacks) {
27138
27316
  const { newX, newY } = computeDraggedGsapPosition(selection.element, studioOffset, gsapPos);
27139
27317
  if (existingSet) {
@@ -27434,8 +27612,8 @@ async function commitKeyframedPosition(selection, anim, properties, callbacks, b
27434
27612
  function resolveDragRuntime(iframe, selector) {
27435
27613
  if (!iframe || !selector) return null;
27436
27614
  const win = iframe.contentWindow;
27437
- const gsap = win?.gsap;
27438
- if (typeof gsap?.getProperty !== "function" || typeof gsap.set !== "function") return null;
27615
+ const gsap2 = win?.gsap;
27616
+ if (typeof gsap2?.getProperty !== "function" || typeof gsap2.set !== "function") return null;
27439
27617
  let el = null;
27440
27618
  try {
27441
27619
  el = iframe.contentDocument?.querySelector(selector) ?? null;
@@ -27447,7 +27625,7 @@ function resolveDragRuntime(iframe, selector) {
27447
27625
  const mainTl = timelines ? Object.values(timelines)[0] : void 0;
27448
27626
  if (typeof mainTl?.seek !== "function") return null;
27449
27627
  return {
27450
- gsapLib: gsap,
27628
+ gsapLib: gsap2,
27451
27629
  el,
27452
27630
  mainTl
27453
27631
  };
@@ -27692,14 +27870,14 @@ async function commitGsapPositionFromDrag(selection, anim, studioOffset, gsapPos
27692
27870
  }
27693
27871
  }
27694
27872
 
27695
- // src/hooks/gsapRuntimeBridge.ts
27873
+ // src/hooks/gsapPositionDetection.ts
27696
27874
  function readGsapPositionFromIframe(iframe, elementSelector) {
27697
- const gsap = getIframeGsap(iframe);
27698
- if (!gsap) return null;
27875
+ const gsap2 = getIframeGsap(iframe);
27876
+ if (!gsap2) return null;
27699
27877
  const element = queryIframeElement(iframe, elementSelector);
27700
27878
  if (!element) return null;
27701
- const x = Number(gsap.getProperty(element, "x")) || 0;
27702
- const y = Number(gsap.getProperty(element, "y")) || 0;
27879
+ const x = Number(gsap2.getProperty(element, "x")) || 0;
27880
+ const y = Number(gsap2.getProperty(element, "y")) || 0;
27703
27881
  return { x, y };
27704
27882
  }
27705
27883
  function animHasPosition(anim) {
@@ -27748,6 +27926,8 @@ function pickClosestToPlayhead(anims) {
27748
27926
  return best;
27749
27927
  });
27750
27928
  }
27929
+
27930
+ // src/hooks/gsapRuntimeBridge.ts
27751
27931
  async function resolveGroupTween(group, animations, selection, commitMutation, fetchFallbackAnimations) {
27752
27932
  const groupAnims = animations.filter((a) => a.propertyGroup === group);
27753
27933
  const groupAnim = pickClosestToPlayhead(groupAnims);
@@ -27788,17 +27968,38 @@ async function tryGsapDragIntercept(selection, offset, animations, iframe, commi
27788
27968
  if (!selector) {
27789
27969
  return false;
27790
27970
  }
27971
+ let workingAnimations = animations;
27972
+ const isPosWrite = (a) => a.targetSelector === selector && a.propertyGroup === "position";
27973
+ if (animations.filter(isPosWrite).length > 1 && fetchFallbackAnimations) {
27974
+ const fresh = await fetchFallbackAnimations();
27975
+ const dupes = fresh.filter(isPosWrite);
27976
+ if (dupes.length > 1) {
27977
+ const keeper = dupes.find((a) => a.keyframes) ?? dupes.find((a) => (a.duration ?? 0) > 0) ?? dupes[0];
27978
+ await commitMutation(
27979
+ selection,
27980
+ {
27981
+ type: "consolidate-position-writes",
27982
+ targetSelector: selector,
27983
+ keepAnimationId: keeper.id
27984
+ },
27985
+ { label: "Consolidate position writes", skipReload: true }
27986
+ );
27987
+ workingAnimations = await fetchFallbackAnimations();
27988
+ } else {
27989
+ workingAnimations = fresh;
27990
+ }
27991
+ }
27791
27992
  const resolved = await resolveGroupTween(
27792
27993
  "position",
27793
- animations,
27994
+ workingAnimations,
27794
27995
  selection,
27795
27996
  commitMutation,
27796
27997
  fetchFallbackAnimations
27797
27998
  );
27798
27999
  let posAnim = resolved?.anim ?? null;
27799
- let resolvedAnimations = resolved?.animations ?? animations;
28000
+ let resolvedAnimations = resolved?.animations ?? workingAnimations;
27800
28001
  if (!posAnim) {
27801
- posAnim = findGsapPositionAnimation(animations, selector);
28002
+ posAnim = findGsapPositionAnimation(workingAnimations, selector);
27802
28003
  if (!posAnim && fetchFallbackAnimations) {
27803
28004
  const fresh = await fetchFallbackAnimations();
27804
28005
  resolvedAnimations = fresh;
@@ -27809,7 +28010,7 @@ async function tryGsapDragIntercept(selection, offset, animations, iframe, commi
27809
28010
  const hasNonHold = hasNonHoldTweenForElement(iframe, selector);
27810
28011
  const hasKeyframedPosTween = !!posAnim?.keyframes;
27811
28012
  if (!hasNonHold && !hasKeyframedPosTween) {
27812
- const existingSet = posAnim && posAnim.method === "set" && posAnim.targetSelector === selector ? posAnim : findPositionSetAnimation(resolvedAnimations, selector);
28013
+ const existingSet = posAnim && posAnim.method === "set" && posAnim.targetSelector === selector ? posAnim : findExistingPositionWrite(resolvedAnimations, selector);
27813
28014
  await commitStaticGsapPosition(selection, offset, gsapPos, selector, existingSet, {
27814
28015
  commitMutation,
27815
28016
  fetchAnimations: fetchFallbackAnimations
@@ -28167,11 +28368,11 @@ function resolveClipTimingBasis(elementId, sourceFile, elements, domClipChildren
28167
28368
  return { elStart: host?.start ?? 0, elDuration: host?.duration ?? 1 };
28168
28369
  }
28169
28370
  function useGsapAnimationsForElement(projectId, sourceFile, target, version, iframeRef) {
28170
- const [allAnimations, setAllAnimations] = useState46([]);
28171
- const [multipleTimelines, setMultipleTimelines] = useState46(false);
28172
- const [unsupportedTimelinePattern, setUnsupportedTimelinePattern] = useState46(false);
28173
- const lastFetchKeyRef = useRef51("");
28174
- const retryTimerRef = useRef51(null);
28371
+ const [allAnimations, setAllAnimations] = useState47([]);
28372
+ const [multipleTimelines, setMultipleTimelines] = useState47(false);
28373
+ const [unsupportedTimelinePattern, setUnsupportedTimelinePattern] = useState47(false);
28374
+ const lastFetchKeyRef = useRef52("");
28375
+ const retryTimerRef = useRef52(null);
28175
28376
  const domClipChildrenKey = usePlayerStore(
28176
28377
  (s) => s.domClipChildren.map((c) => `${c.id}<${c.hostId}`).join("|")
28177
28378
  );
@@ -28308,7 +28509,7 @@ function useGsapAnimationsForElement(projectId, sourceFile, target, version, ifr
28308
28509
  let ease;
28309
28510
  let easeEach;
28310
28511
  for (const anim of animations) {
28311
- if (anim.method === "set" && Object.keys(anim.properties).every((k) => k === "x" || k === "y"))
28512
+ if (!anim.keyframes && Object.keys(anim.properties).length > 0 && Object.keys(anim.properties).every((k) => k === "x" || k === "y") && (anim.method === "set" || (anim.duration ?? 0) === 0))
28312
28513
  continue;
28313
28514
  const kf = anim.keyframes ?? synthesizeFlatTweenKeyframes(anim);
28314
28515
  if (!kf) continue;
@@ -28348,7 +28549,7 @@ function useGsapAnimationsForElement(projectId, sourceFile, target, version, ifr
28348
28549
  return { animations, multipleTimelines, unsupportedTimelinePattern };
28349
28550
  }
28350
28551
  function useGsapCacheVersion() {
28351
- const [version, setVersion] = useState46(0);
28552
+ const [version, setVersion] = useState47(0);
28352
28553
  const bump = useCallback57(() => setVersion((v) => v + 1), []);
28353
28554
  return { version, bump };
28354
28555
  }
@@ -28357,9 +28558,9 @@ function usePopulateKeyframeCacheForFile(projectId, sourceFile, version, iframeR
28357
28558
  const domClipChildrenKey = usePlayerStore(
28358
28559
  (s) => s.domClipChildren.map((c) => `${c.id}<${c.hostId}`).join("|")
28359
28560
  );
28360
- const lastFetchKeyRef = useRef51("");
28361
- const runtimeScanDoneRef = useRef51("");
28362
- const astFetchDoneRef = useRef51("");
28561
+ const lastFetchKeyRef = useRef52("");
28562
+ const runtimeScanDoneRef = useRef52("");
28563
+ const astFetchDoneRef = useRef52("");
28363
28564
  useEffect32(() => {
28364
28565
  const fetchKey = `kf-cache:${projectId}:${sourceFile}:${version}:${elementCount}:${domClipChildrenKey}`;
28365
28566
  if (fetchKey === lastFetchKeyRef.current) return;
@@ -28377,9 +28578,9 @@ function usePopulateKeyframeCacheForFile(projectId, sourceFile, version, iframeR
28377
28578
  const mergedByElement = /* @__PURE__ */ new Map();
28378
28579
  for (const anim of parsed.animations) {
28379
28580
  if (anim.hasUnresolvedKeyframes) continue;
28380
- if (anim.method === "set") {
28581
+ if (!anim.keyframes && (anim.method === "set" || (anim.duration ?? 0) === 0)) {
28381
28582
  const propKeys = Object.keys(anim.properties).filter((k) => k !== "immediateRender");
28382
- if (propKeys.every((k) => k === "x" || k === "y")) {
28583
+ if (propKeys.length > 0 && propKeys.every((k) => k === "x" || k === "y")) {
28383
28584
  continue;
28384
28585
  }
28385
28586
  }
@@ -28462,10 +28663,10 @@ function usePopulateKeyframeCacheForFile(projectId, sourceFile, version, iframeR
28462
28663
  }
28463
28664
 
28464
28665
  // src/hooks/useDomEditWiring.ts
28465
- import { useCallback as useCallback61, useEffect as useEffect34, useRef as useRef54 } from "react";
28666
+ import { useCallback as useCallback61, useEffect as useEffect34, useRef as useRef55 } from "react";
28466
28667
 
28467
28668
  // src/hooks/useDomEditPreviewSync.ts
28468
- import { useEffect as useEffect33, useRef as useRef52 } from "react";
28669
+ import { useEffect as useEffect33, useRef as useRef53 } from "react";
28469
28670
  function useDomEditPreviewSync({
28470
28671
  previewIframe,
28471
28672
  activeCompPath,
@@ -28531,7 +28732,7 @@ function useDomEditPreviewSync({
28531
28732
  applyStudioManualEditsToPreviewRef,
28532
28733
  gsapCacheVersion
28533
28734
  ]);
28534
- const openSourceRef = useRef52(openSourceForSelection);
28735
+ const openSourceRef = useRef53(openSourceForSelection);
28535
28736
  openSourceRef.current = openSourceForSelection;
28536
28737
  useEffect33(
28537
28738
  // fallow-ignore-next-line complexity
@@ -28609,7 +28810,7 @@ function useGsapInteractionFailureTelemetry(activeCompPath, showToast) {
28609
28810
  }
28610
28811
 
28611
28812
  // src/hooks/useGsapSelectionHandlers.ts
28612
- import { useCallback as useCallback60, useRef as useRef53 } from "react";
28813
+ import { useCallback as useCallback60, useRef as useRef54 } from "react";
28613
28814
  function useGsapSelectionHandlers({
28614
28815
  domEditSelection,
28615
28816
  updateGsapProperty,
@@ -28625,12 +28826,14 @@ function useGsapSelectionHandlers({
28625
28826
  addKeyframe,
28626
28827
  addKeyframeBatch,
28627
28828
  removeKeyframe,
28829
+ moveKeyframe,
28830
+ resizeKeyframedTween,
28628
28831
  convertToKeyframes,
28629
28832
  removeAllKeyframes,
28630
28833
  handleDomManualEditsReset,
28631
28834
  selectedGsapAnimations
28632
28835
  }) {
28633
- const lastSelectionRef = useRef53(null);
28836
+ const lastSelectionRef = useRef54(null);
28634
28837
  if (domEditSelection) lastSelectionRef.current = domEditSelection;
28635
28838
  const trackGsapHandlerFailure = useCallback60(
28636
28839
  (error, selection, mutationType, label) => {
@@ -28755,6 +28958,35 @@ function useGsapSelectionHandlers({
28755
28958
  },
28756
28959
  [domEditSelection, removeKeyframe]
28757
28960
  );
28961
+ const handleGsapMoveKeyframeToPlayhead = useCallback60(
28962
+ (animId, fromPercentage, selectionOverride) => {
28963
+ const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
28964
+ if (!sel) return;
28965
+ const anim = selectedGsapAnimations.find((a) => a.id === animId);
28966
+ const toPercentage = computeCurrentPercentage(sel, anim);
28967
+ trackStudioEvent("keyframe", { action: "move_to_playhead" });
28968
+ moveKeyframe(sel, animId, fromPercentage, toPercentage);
28969
+ },
28970
+ [domEditSelection, selectedGsapAnimations, moveKeyframe]
28971
+ );
28972
+ const handleGsapMoveKeyframe = useCallback60(
28973
+ (animId, fromPercentage, toPercentage, selectionOverride) => {
28974
+ const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
28975
+ if (!sel) return;
28976
+ trackStudioEvent("keyframe", { action: "retime" });
28977
+ moveKeyframe(sel, animId, fromPercentage, toPercentage);
28978
+ },
28979
+ [domEditSelection, moveKeyframe]
28980
+ );
28981
+ const handleGsapResizeKeyframedTween = useCallback60(
28982
+ (animId, position, duration, pctRemap, selectionOverride) => {
28983
+ const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
28984
+ if (!sel) return;
28985
+ trackStudioEvent("keyframe", { action: "retime_resize" });
28986
+ resizeKeyframedTween(sel, animId, position, duration, pctRemap);
28987
+ },
28988
+ [domEditSelection, resizeKeyframedTween]
28989
+ );
28758
28990
  const handleGsapConvertToKeyframes = useCallback60(
28759
28991
  (animId, resolvedFromValues, duration) => {
28760
28992
  if (!domEditSelection) return Promise.resolve();
@@ -28799,6 +29031,9 @@ function useGsapSelectionHandlers({
28799
29031
  handleGsapAddKeyframe,
28800
29032
  handleGsapAddKeyframeBatch,
28801
29033
  handleGsapRemoveKeyframe,
29034
+ handleGsapMoveKeyframeToPlayhead,
29035
+ handleGsapMoveKeyframe,
29036
+ handleGsapResizeKeyframedTween,
28802
29037
  handleGsapConvertToKeyframes,
28803
29038
  handleGsapRemoveAllKeyframes,
28804
29039
  handleResetSelectedElementKeyframes
@@ -28840,6 +29075,8 @@ function useDomEditWiring({
28840
29075
  addKeyframe,
28841
29076
  addKeyframeBatch,
28842
29077
  removeKeyframe,
29078
+ moveKeyframe,
29079
+ resizeKeyframedTween,
28843
29080
  convertToKeyframes,
28844
29081
  removeAllKeyframes,
28845
29082
  handleDomManualEditsReset
@@ -28866,7 +29103,7 @@ function useDomEditWiring({
28866
29103
  const key = matchKey ? matchKey.key ?? matchKey.id : null;
28867
29104
  if (key && key !== selectedElementId) setSelectedElementId(key);
28868
29105
  }, [domEditSelection?.id]);
28869
- const prevRefreshKeyRef = useRef54(refreshKey);
29106
+ const prevRefreshKeyRef = useRef55(refreshKey);
28870
29107
  useEffect34(() => {
28871
29108
  if (refreshKey !== prevRefreshKeyRef.current) {
28872
29109
  prevRefreshKeyRef.current = refreshKey;
@@ -28910,6 +29147,8 @@ function useDomEditWiring({
28910
29147
  addKeyframe,
28911
29148
  addKeyframeBatch,
28912
29149
  removeKeyframe,
29150
+ moveKeyframe,
29151
+ resizeKeyframedTween,
28913
29152
  convertToKeyframes,
28914
29153
  removeAllKeyframes,
28915
29154
  handleDomManualEditsReset,
@@ -29379,7 +29618,7 @@ function useGsapAwareEditing({
29379
29618
  }
29380
29619
 
29381
29620
  // src/hooks/useStudioSelectionPublisher.ts
29382
- import { useEffect as useEffect35, useRef as useRef55 } from "react";
29621
+ import { useEffect as useEffect35, useRef as useRef56 } from "react";
29383
29622
 
29384
29623
  // src/utils/studioSelectionSnapshot.ts
29385
29624
  function round32(value) {
@@ -29469,8 +29708,8 @@ function useStudioSelectionPublisher({
29469
29708
  previewDocumentVersion,
29470
29709
  refreshDomEditSelectionFromPreview
29471
29710
  }) {
29472
- const lastSelectionRefreshKeyRef = useRef55(refreshKey);
29473
- const pendingSelectionRefreshKeyRef = useRef55(null);
29711
+ const lastSelectionRefreshKeyRef = useRef56(refreshKey);
29712
+ const pendingSelectionRefreshKeyRef = useRef56(null);
29474
29713
  useEffect35(() => {
29475
29714
  if (!projectId) return;
29476
29715
  const selection = domEditSelection?.element.isConnected ? buildStudioSelectionSnapshot({
@@ -29623,6 +29862,8 @@ function useDomEditSession({
29623
29862
  addKeyframe,
29624
29863
  addKeyframeBatch,
29625
29864
  removeKeyframe,
29865
+ moveKeyframe,
29866
+ resizeKeyframedTween,
29626
29867
  convertToKeyframes,
29627
29868
  removeAllKeyframes,
29628
29869
  setArcPath,
@@ -29758,6 +29999,9 @@ function useDomEditSession({
29758
29999
  handleGsapAddKeyframe,
29759
30000
  handleGsapAddKeyframeBatch,
29760
30001
  handleGsapRemoveKeyframe,
30002
+ handleGsapMoveKeyframeToPlayhead,
30003
+ handleGsapMoveKeyframe,
30004
+ handleGsapResizeKeyframedTween,
29761
30005
  handleGsapConvertToKeyframes,
29762
30006
  handleGsapRemoveAllKeyframes,
29763
30007
  handleResetSelectedElementKeyframes
@@ -29795,6 +30039,8 @@ function useDomEditSession({
29795
30039
  addKeyframe,
29796
30040
  addKeyframeBatch,
29797
30041
  removeKeyframe,
30042
+ moveKeyframe,
30043
+ resizeKeyframedTween,
29798
30044
  convertToKeyframes,
29799
30045
  removeAllKeyframes,
29800
30046
  handleDomManualEditsReset
@@ -29943,6 +30189,9 @@ function useDomEditSession({
29943
30189
  handleGsapAddKeyframe,
29944
30190
  handleGsapAddKeyframeBatch,
29945
30191
  handleGsapRemoveKeyframe,
30192
+ handleGsapMoveKeyframeToPlayhead,
30193
+ handleGsapMoveKeyframe,
30194
+ handleGsapResizeKeyframedTween,
29946
30195
  handleGsapConvertToKeyframes,
29947
30196
  handleGsapRemoveAllKeyframes,
29948
30197
  handleResetSelectedElementKeyframes,
@@ -29960,7 +30209,7 @@ function useDomEditSession({
29960
30209
  }
29961
30210
 
29962
30211
  // src/hooks/useSdkSession.ts
29963
- import { useState as useState47, useEffect as useEffect36, useCallback as useCallback65 } from "react";
30212
+ import { useState as useState48, useEffect as useEffect36, useCallback as useCallback65 } from "react";
29964
30213
  import { openComposition } from "@hyperframes/sdk";
29965
30214
  async function readProjectFileOptional(projectId, path) {
29966
30215
  if (path.includes("\0") || path.includes("..")) return void 0;
@@ -29988,8 +30237,8 @@ function shouldReloadOnFileChange(activeCompPath, content, withinSuppressWindow)
29988
30237
  return !withinSuppressWindow;
29989
30238
  }
29990
30239
  function useSdkSession(projectId, activeCompPath, domEditSaveTimestampRef) {
29991
- const [session, setSession] = useState47(null);
29992
- const [reloadToken, setReloadToken] = useState47(0);
30240
+ const [session, setSession] = useState48(null);
30241
+ const [reloadToken, setReloadToken] = useState48(0);
29993
30242
  useEffect36(() => {
29994
30243
  if (!activeCompPath) return;
29995
30244
  const compPath = activeCompPath;
@@ -30057,7 +30306,7 @@ function useSdkSelectionSync(session, domEditSelection, domEditGroupSelections)
30057
30306
  }
30058
30307
 
30059
30308
  // src/hooks/useBlockHandlers.ts
30060
- import { useCallback as useCallback66, useMemo as useMemo23, useState as useState48 } from "react";
30309
+ import { useCallback as useCallback66, useMemo as useMemo23, useState as useState49 } from "react";
30061
30310
 
30062
30311
  // src/utils/blockInstaller.ts
30063
30312
  function getMaxZIndexFromIframe(iframe) {
@@ -30201,7 +30450,7 @@ function useBlockHandlers({
30201
30450
  setRightCollapsed,
30202
30451
  setRightPanelTab
30203
30452
  }) {
30204
- const [activeBlockParams, setActiveBlockParams] = useState48(null);
30453
+ const [activeBlockParams, setActiveBlockParams] = useState49(null);
30205
30454
  const blockCtx = useMemo23(
30206
30455
  () => ({
30207
30456
  activeCompPath: blockCtxDeps.activeCompPath,
@@ -30289,7 +30538,7 @@ function useBlockHandlers({
30289
30538
  }
30290
30539
 
30291
30540
  // src/hooks/useAppHotkeys.ts
30292
- import { useCallback as useCallback67, useEffect as useEffect38, useRef as useRef56 } from "react";
30541
+ import { useCallback as useCallback67, useEffect as useEffect38, useRef as useRef57 } from "react";
30293
30542
  function iframeContentWindow(iframe) {
30294
30543
  try {
30295
30544
  return iframe?.contentWindow ?? null;
@@ -30497,8 +30746,8 @@ function useAppHotkeys({
30497
30746
  activeCompPath,
30498
30747
  forceReloadSdkSession
30499
30748
  }) {
30500
- const previewHotkeyWindowRef = useRef56(null);
30501
- const previewHistoryCleanupRef = useRef56(null);
30749
+ const previewHotkeyWindowRef = useRef57(null);
30750
+ const previewHistoryCleanupRef = useRef57(null);
30502
30751
  const handleTimelineToggleHotkey = useCallback67(
30503
30752
  (event) => {
30504
30753
  if (!shouldHandleTimelineToggleHotkey(event)) return;
@@ -30556,7 +30805,7 @@ function useAppHotkeys({
30556
30805
  );
30557
30806
  const handleUndo = useCallback67(() => applyHistory("undo"), [applyHistory]);
30558
30807
  const handleRedo = useCallback67(() => applyHistory("redo"), [applyHistory]);
30559
- const cbRef = useRef56(null);
30808
+ const cbRef = useRef57(null);
30560
30809
  cbRef.current = {
30561
30810
  toggleTimelineVisibility,
30562
30811
  handleTimelineElementDelete,
@@ -30666,7 +30915,7 @@ function useAppHotkeys({
30666
30915
  }
30667
30916
 
30668
30917
  // src/hooks/useClipboard.ts
30669
- import { useCallback as useCallback68, useRef as useRef57 } from "react";
30918
+ import { useCallback as useCallback68, useRef as useRef58 } from "react";
30670
30919
 
30671
30920
  // src/utils/clipboardPayload.ts
30672
30921
  function insertAsSibling(source, newHtml, selector, selectorIndex) {
@@ -30781,8 +31030,8 @@ function useClipboard({
30781
31030
  handleDomEditElementDelete,
30782
31031
  previewIframeRef
30783
31032
  }) {
30784
- const clipboardRef = useRef57(null);
30785
- const projectIdRef = useRef57(projectId);
31033
+ const clipboardRef = useRef58(null);
31034
+ const projectIdRef = useRef58(projectId);
30786
31035
  projectIdRef.current = projectId;
30787
31036
  const handleCopy = useCallback68(() => {
30788
31037
  const { selectedElementId, elements } = usePlayerStore.getState();
@@ -31269,7 +31518,7 @@ import { useCallback as useCallback70 } from "react";
31269
31518
  import { createElement } from "react";
31270
31519
 
31271
31520
  // src/player/components/AudioWaveform.tsx
31272
- import { memo as memo25, useRef as useRef58, useState as useState49, useCallback as useCallback69, useEffect as useEffect40 } from "react";
31521
+ import { memo as memo25, useRef as useRef59, useState as useState50, useCallback as useCallback69, useEffect as useEffect40 } from "react";
31273
31522
  import { jsx as jsx64, jsxs as jsxs55 } from "react/jsx-runtime";
31274
31523
  var BAR_W = 2;
31275
31524
  var GAP = 1;
@@ -31317,11 +31566,11 @@ var AudioWaveform = memo25(function AudioWaveform2({
31317
31566
  trimStartFraction,
31318
31567
  trimEndFraction
31319
31568
  }) {
31320
- const containerRef = useRef58(null);
31321
- const barsRef = useRef58(null);
31322
- const roRef = useRef58(null);
31569
+ const containerRef = useRef59(null);
31570
+ const barsRef = useRef59(null);
31571
+ const roRef = useRef59(null);
31323
31572
  const cacheKey = waveformUrl ?? audioUrl;
31324
- const [peaks, setPeaks] = useState49(peaksCache.get(cacheKey) ?? null);
31573
+ const [peaks, setPeaks] = useState50(peaksCache.get(cacheKey) ?? null);
31325
31574
  useEffect40(() => {
31326
31575
  if (peaks || !cacheKey) return;
31327
31576
  let cancelled = false;
@@ -31523,10 +31772,10 @@ function useRenderClipContent({
31523
31772
  }
31524
31773
 
31525
31774
  // src/hooks/useConsoleErrorCapture.ts
31526
- import { useCallback as useCallback71, useEffect as useEffect41, useRef as useRef59, useState as useState50 } from "react";
31775
+ import { useCallback as useCallback71, useEffect as useEffect41, useRef as useRef60, useState as useState51 } from "react";
31527
31776
  function useConsoleErrorCapture(previewIframe) {
31528
- const [consoleErrors, setConsoleErrors] = useState50(null);
31529
- const consoleErrorsRef = useRef59([]);
31777
+ const [consoleErrors, setConsoleErrors] = useState51(null);
31778
+ const consoleErrorsRef = useRef60([]);
31530
31779
  const resetErrors = useCallback71(() => {
31531
31780
  consoleErrorsRef.current = [];
31532
31781
  setConsoleErrors(null);
@@ -31596,7 +31845,7 @@ function useConsoleErrorCapture(previewIframe) {
31596
31845
  }
31597
31846
 
31598
31847
  // src/hooks/useFrameCapture.ts
31599
- import { useState as useState51, useCallback as useCallback72 } from "react";
31848
+ import { useState as useState52, useCallback as useCallback72 } from "react";
31600
31849
 
31601
31850
  // src/utils/projectRouting.ts
31602
31851
  var PROJECT_HASH_PREFIX = "#project/";
@@ -31678,7 +31927,7 @@ function useFrameCapture({
31678
31927
  showToast,
31679
31928
  waitForPendingDomEditSaves
31680
31929
  }) {
31681
- const [captureFrameTime, setCaptureFrameTime] = useState51(0);
31930
+ const [captureFrameTime, setCaptureFrameTime] = useState52(0);
31682
31931
  useMountEffect(() => {
31683
31932
  setCaptureFrameTime(usePlayerStore.getState().currentTime);
31684
31933
  return liveTime.subscribe(setCaptureFrameTime);
@@ -31756,7 +32005,7 @@ function useFrameCapture({
31756
32005
  }
31757
32006
 
31758
32007
  // src/hooks/useLintModal.ts
31759
- import { useState as useState52, useCallback as useCallback73, useEffect as useEffect42, useRef as useRef60, useMemo as useMemo24 } from "react";
32008
+ import { useState as useState53, useCallback as useCallback73, useEffect as useEffect42, useRef as useRef61, useMemo as useMemo24 } from "react";
31760
32009
  function parseFinding(f) {
31761
32010
  return {
31762
32011
  severity: f.severity === "error" ? "error" : "warning",
@@ -31767,10 +32016,10 @@ function parseFinding(f) {
31767
32016
  };
31768
32017
  }
31769
32018
  function useLintModal(projectId, refreshKey) {
31770
- const [lintModal, setLintModal] = useState52(null);
31771
- const [linting, setLinting] = useState52(false);
31772
- const [backgroundFindings, setBackgroundFindings] = useState52([]);
31773
- const autoLintRanRef = useRef60(false);
32019
+ const [lintModal, setLintModal] = useState53(null);
32020
+ const [linting, setLinting] = useState53(false);
32021
+ const [backgroundFindings, setBackgroundFindings] = useState53([]);
32022
+ const autoLintRanRef = useRef61(false);
31774
32023
  const runLint = useCallback73(
31775
32024
  async (opts) => {
31776
32025
  if (!projectId) return;
@@ -31797,7 +32046,7 @@ function useLintModal(projectId, refreshKey) {
31797
32046
  [projectId]
31798
32047
  );
31799
32048
  const handleLint = useCallback73(() => runLint(), [runLint]);
31800
- const prevProjectIdRef = useRef60(projectId);
32049
+ const prevProjectIdRef = useRef61(projectId);
31801
32050
  useEffect42(() => {
31802
32051
  if (projectId !== prevProjectIdRef.current) {
31803
32052
  autoLintRanRef.current = false;
@@ -31845,9 +32094,9 @@ function useLintModal(projectId, refreshKey) {
31845
32094
  }
31846
32095
 
31847
32096
  // src/hooks/useCompositionDimensions.ts
31848
- import { useState as useState53 } from "react";
32097
+ import { useState as useState54 } from "react";
31849
32098
  function useCompositionDimensions() {
31850
- const [compositionDimensions, setCompositionDimensions] = useState53(
32099
+ const [compositionDimensions, setCompositionDimensions] = useState54(
31851
32100
  null
31852
32101
  );
31853
32102
  useMountEffect(() => {
@@ -31867,10 +32116,10 @@ function useCompositionDimensions() {
31867
32116
  }
31868
32117
 
31869
32118
  // src/hooks/useToast.ts
31870
- import { useState as useState54, useCallback as useCallback74, useRef as useRef61 } from "react";
32119
+ import { useState as useState55, useCallback as useCallback74, useRef as useRef62 } from "react";
31871
32120
  function useToast() {
31872
- const [appToast, setAppToast] = useState54(null);
31873
- const timerRef = useRef61(null);
32121
+ const [appToast, setAppToast] = useState55(null);
32122
+ const timerRef = useRef62(null);
31874
32123
  const showToast = useCallback74((message, tone = "error") => {
31875
32124
  if (timerRef.current) clearTimeout(timerRef.current);
31876
32125
  setAppToast({ message, tone });
@@ -31887,7 +32136,7 @@ function useToast() {
31887
32136
  }
31888
32137
 
31889
32138
  // src/hooks/useStudioUrlState.ts
31890
- import { useCallback as useCallback75, useEffect as useEffect43, useRef as useRef62, useState as useState55 } from "react";
32139
+ import { useCallback as useCallback75, useEffect as useEffect43, useRef as useRef63, useState as useState56 } from "react";
31891
32140
 
31892
32141
  // src/utils/studioUrlState.ts
31893
32142
  var VALID_TABS = ["layers", "design", "renders"];
@@ -32011,12 +32260,12 @@ function useStudioUrlState({
32011
32260
  initialState: initialState2
32012
32261
  }) {
32013
32262
  const currentTime = usePlayerStore((s) => s.currentTime);
32014
- const hydratedSeekRef = useRef62(initialState2.currentTime == null);
32015
- const hydratedInitialTimeRef = useRef62(initialState2.currentTime == null);
32016
- const hydratedSelectionRef = useRef62(initialState2.selection == null);
32017
- const [selectionHydrated, setSelectionHydrated] = useState55(initialState2.selection == null);
32018
- const pendingSelectionRef = useRef62(initialState2.selection);
32019
- const stableTimeRef = useRef62(initialState2.currentTime);
32263
+ const hydratedSeekRef = useRef63(initialState2.currentTime == null);
32264
+ const hydratedInitialTimeRef = useRef63(initialState2.currentTime == null);
32265
+ const hydratedSelectionRef = useRef63(initialState2.selection == null);
32266
+ const [selectionHydrated, setSelectionHydrated] = useState56(initialState2.selection == null);
32267
+ const pendingSelectionRef = useRef63(initialState2.selection);
32268
+ const stableTimeRef = useRef63(initialState2.currentTime);
32020
32269
  const buildUrlState = useCallback75(
32021
32270
  () => ({
32022
32271
  activeCompPath,
@@ -32144,7 +32393,7 @@ function useStudioUrlState({
32144
32393
  }
32145
32394
 
32146
32395
  // src/hooks/useStudioContextValue.ts
32147
- import { useCallback as useCallback76, useMemo as useMemo25, useRef as useRef63, useState as useState56 } from "react";
32396
+ import { useCallback as useCallback76, useMemo as useMemo25, useRef as useRef64, useState as useState57 } from "react";
32148
32397
  function buildStudioContextValue(input) {
32149
32398
  return {
32150
32399
  projectId: input.projectId,
@@ -32188,8 +32437,8 @@ function useInspectorState(rightPanelTab, rightInspectorPanes, rightCollapsed, i
32188
32437
  }, [rightPanelTab, rightInspectorPanes, rightCollapsed, isPlaying, isGestureRecording]);
32189
32438
  }
32190
32439
  function useDragOverlay(onImportFiles) {
32191
- const [active, setActive] = useState56(false);
32192
- const counterRef = useRef63(0);
32440
+ const [active, setActive] = useState57(false);
32441
+ const counterRef = useRef64(0);
32193
32442
  const onDragOver = useCallback76((e) => {
32194
32443
  if (!e.dataTransfer.types.includes("Files")) return;
32195
32444
  e.preventDefault();
@@ -32292,7 +32541,7 @@ import {
32292
32541
  useContext as useContext5,
32293
32542
  useEffect as useEffect44,
32294
32543
  useMemo as useMemo27,
32295
- useState as useState57
32544
+ useState as useState58
32296
32545
  } from "react";
32297
32546
  import { jsx as jsx66 } from "react/jsx-runtime";
32298
32547
  var VIEW_QUERY_PARAM = "view";
@@ -32311,7 +32560,7 @@ function writeViewModeToUrl(mode) {
32311
32560
  window.history.replaceState(window.history.state, "", url);
32312
32561
  }
32313
32562
  function useViewModeState(enabled) {
32314
- const [viewMode, setMode] = useState57(
32563
+ const [viewMode, setMode] = useState58(
32315
32564
  () => enabled ? readViewModeFromUrl() : "timeline"
32316
32565
  );
32317
32566
  useEffect44(() => {
@@ -32688,22 +32937,22 @@ function StudioHeader({
32688
32937
  }
32689
32938
 
32690
32939
  // src/hooks/useGestureCommit.ts
32691
- import { useState as useState59, useCallback as useCallback79, useRef as useRef65, useEffect as useEffect46 } from "react";
32940
+ import { useState as useState60, useCallback as useCallback79, useRef as useRef66, useEffect as useEffect46 } from "react";
32692
32941
 
32693
32942
  // src/hooks/useGestureRecording.ts
32694
- import { useCallback as useCallback78, useEffect as useEffect45, useRef as useRef64, useState as useState58 } from "react";
32943
+ import { useCallback as useCallback78, useEffect as useEffect45, useRef as useRef65, useState as useState59 } from "react";
32695
32944
  function readBasePosition(element, iframeEl) {
32696
32945
  let baseOpacity = 1;
32697
32946
  let baseScale = 1;
32698
32947
  let baseX = 0;
32699
32948
  let baseY = 0;
32700
32949
  try {
32701
- const gsap = iframeEl.contentWindow.gsap;
32702
- if (gsap?.getProperty) {
32703
- baseOpacity = Number(gsap.getProperty(element, "opacity")) || 1;
32704
- baseScale = Number(gsap.getProperty(element, "scaleX")) || 1;
32705
- baseX = Number(gsap.getProperty(element, "x")) || 0;
32706
- baseY = Number(gsap.getProperty(element, "y")) || 0;
32950
+ const gsap2 = iframeEl.contentWindow.gsap;
32951
+ if (gsap2?.getProperty) {
32952
+ baseOpacity = Number(gsap2.getProperty(element, "opacity")) || 1;
32953
+ baseScale = Number(gsap2.getProperty(element, "scaleX")) || 1;
32954
+ baseX = Number(gsap2.getProperty(element, "x")) || 0;
32955
+ baseY = Number(gsap2.getProperty(element, "y")) || 0;
32707
32956
  }
32708
32957
  } catch {
32709
32958
  }
@@ -32808,12 +33057,12 @@ function createRecordingRefs() {
32808
33057
  };
32809
33058
  }
32810
33059
  function useGestureRecording() {
32811
- const [isRecording, setIsRecording] = useState58(false);
32812
- const [recordingDuration, setRecordingDuration] = useState58(0);
32813
- const isRecordingRef = useRef64(false);
32814
- const refs = useRef64(createRecordingRefs());
32815
- const samplesRef = useRef64(refs.current.samples);
32816
- const trailRef = useRef64(refs.current.trail);
33060
+ const [isRecording, setIsRecording] = useState59(false);
33061
+ const [recordingDuration, setRecordingDuration] = useState59(0);
33062
+ const isRecordingRef = useRef65(false);
33063
+ const refs = useRef65(createRecordingRefs());
33064
+ const samplesRef = useRef65(refs.current.samples);
33065
+ const trailRef = useRef65(refs.current.trail);
32817
33066
  useEffect45(() => {
32818
33067
  const r = refs.current;
32819
33068
  return () => {
@@ -33204,12 +33453,12 @@ function useGestureCommit({
33204
33453
  isGestureRecordingRef
33205
33454
  }) {
33206
33455
  const gestureRecording = useGestureRecording();
33207
- const [gestureState, setGestureState] = useState59("idle");
33208
- const gestureStateRef = useRef65("idle");
33209
- const recordingAutoStopRef = useRef65(void 0);
33210
- const recordingStartTimeRef = useRef65(0);
33211
- const commitInFlightRef = useRef65(false);
33212
- const capturedSelectionRef = useRef65(null);
33456
+ const [gestureState, setGestureState] = useState60("idle");
33457
+ const gestureStateRef = useRef66("idle");
33458
+ const recordingAutoStopRef = useRef66(void 0);
33459
+ const recordingStartTimeRef = useRef66(0);
33460
+ const commitInFlightRef = useRef66(false);
33461
+ const capturedSelectionRef = useRef66(null);
33213
33462
  useEffect46(() => () => clearInterval(recordingAutoStopRef.current), []);
33214
33463
  const stopAndCommitRecording = useCallback79(async () => {
33215
33464
  clearInterval(recordingAutoStopRef.current);
@@ -33526,15 +33775,15 @@ import { useCallback as useCallback85 } from "react";
33526
33775
  // src/components/sidebar/LeftSidebar.tsx
33527
33776
  import {
33528
33777
  memo as memo30,
33529
- useState as useState66,
33778
+ useState as useState67,
33530
33779
  useCallback as useCallback84,
33531
33780
  useImperativeHandle,
33532
- useRef as useRef70,
33781
+ useRef as useRef71,
33533
33782
  forwardRef as forwardRef3
33534
33783
  } from "react";
33535
33784
 
33536
33785
  // src/components/sidebar/CompositionsTab.tsx
33537
- import { memo as memo27, useCallback as useCallback80, useEffect as useEffect47, useRef as useRef66, useState as useState60 } from "react";
33786
+ import { memo as memo27, useCallback as useCallback80, useEffect as useEffect47, useRef as useRef67, useState as useState61 } from "react";
33538
33787
  import { jsx as jsx69, jsxs as jsxs58 } from "react/jsx-runtime";
33539
33788
  var DEFAULT_PREVIEW_STAGE = { width: 1920, height: 1080 };
33540
33789
  var CARD_W = 80;
@@ -33600,11 +33849,11 @@ function CompCard({
33600
33849
  isRendering,
33601
33850
  lintInfo
33602
33851
  }) {
33603
- const [hovered, setHovered] = useState60(false);
33604
- const [stageSize, setStageSize] = useState60(DEFAULT_PREVIEW_STAGE);
33605
- const iframeRef = useRef66(null);
33606
- const hoverTimer = useRef66(null);
33607
- const syncTimer = useRef66(null);
33852
+ const [hovered, setHovered] = useState61(false);
33853
+ const [stageSize, setStageSize] = useState61(DEFAULT_PREVIEW_STAGE);
33854
+ const iframeRef = useRef67(null);
33855
+ const hoverTimer = useRef67(null);
33856
+ const syncTimer = useRef67(null);
33608
33857
  const requestIframePlaybackSync = useCallback80((shouldPlay) => {
33609
33858
  if (syncTimer.current) {
33610
33859
  clearTimeout(syncTimer.current);
@@ -33763,13 +34012,13 @@ var CompositionsTab = memo27(function CompositionsTab2({
33763
34012
  });
33764
34013
 
33765
34014
  // src/components/sidebar/AssetsTab.tsx
33766
- import { memo as memo28, useState as useState63, useCallback as useCallback82, useRef as useRef68, useMemo as useMemo29, useEffect as useEffect50 } from "react";
34015
+ import { memo as memo28, useState as useState64, useCallback as useCallback82, useRef as useRef69, useMemo as useMemo29, useEffect as useEffect50 } from "react";
33767
34016
 
33768
34017
  // src/components/ui/VideoFrameThumbnail.tsx
33769
- import { useState as useState61, useEffect as useEffect48 } from "react";
34018
+ import { useState as useState62, useEffect as useEffect48 } from "react";
33770
34019
  import { jsx as jsx70 } from "react/jsx-runtime";
33771
34020
  function VideoFrameThumbnail({ src }) {
33772
- const [frame, setFrame] = useState61(null);
34021
+ const [frame, setFrame] = useState62(null);
33773
34022
  useEffect48(() => {
33774
34023
  const video = document.createElement("video");
33775
34024
  video.crossOrigin = "anonymous";
@@ -33905,7 +34154,7 @@ var CATEGORY_LABELS = {
33905
34154
  var FILTER_ORDER = ["audio", "images", "video", "fonts"];
33906
34155
 
33907
34156
  // src/components/sidebar/AudioRow.tsx
33908
- import { useState as useState62, useRef as useRef67, useEffect as useEffect49, useCallback as useCallback81 } from "react";
34157
+ import { useState as useState63, useRef as useRef68, useEffect as useEffect49, useCallback as useCallback81 } from "react";
33909
34158
  import { Fragment as Fragment20, jsx as jsx72, jsxs as jsxs60 } from "react/jsx-runtime";
33910
34159
  function AudioRow({
33911
34160
  projectId,
@@ -33917,14 +34166,14 @@ function AudioRow({
33917
34166
  onDelete,
33918
34167
  onRename
33919
34168
  }) {
33920
- const [contextMenu, setContextMenu] = useState62(null);
33921
- const [playing, setPlaying] = useState62(false);
33922
- const [bars, setBars] = useState62([]);
33923
- const audioRef = useRef67(null);
33924
- const actxRef = useRef67(null);
33925
- const analyserRef = useRef67(null);
33926
- const sourceRef = useRef67(null);
33927
- const animRef = useRef67(0);
34169
+ const [contextMenu, setContextMenu] = useState63(null);
34170
+ const [playing, setPlaying] = useState63(false);
34171
+ const [bars, setBars] = useState63([]);
34172
+ const audioRef = useRef68(null);
34173
+ const actxRef = useRef68(null);
34174
+ const analyserRef = useRef68(null);
34175
+ const sourceRef = useRef68(null);
34176
+ const animRef = useRef68(0);
33928
34177
  const name = basename(asset);
33929
34178
  const subtype = getAudioSubtype(asset);
33930
34179
  const serveUrl = `/api/projects/${projectId}/preview/${asset}`;
@@ -34082,8 +34331,8 @@ function ImageCard({
34082
34331
  onRename,
34083
34332
  size
34084
34333
  }) {
34085
- const [contextMenu, setContextMenu] = useState63(null);
34086
- const [hovered, setHovered] = useState63(false);
34334
+ const [contextMenu, setContextMenu] = useState64(null);
34335
+ const [hovered, setHovered] = useState64(false);
34087
34336
  const name = basename(asset);
34088
34337
  const extension = ext(asset);
34089
34338
  const serveUrl = `/api/projects/${projectId}/preview/${asset}`;
@@ -34200,13 +34449,13 @@ var AssetsTab = memo28(function AssetsTab2({
34200
34449
  onDelete,
34201
34450
  onRename
34202
34451
  }) {
34203
- const fileInputRef = useRef68(null);
34204
- const [dragOver, setDragOver] = useState63(false);
34205
- const [copiedPath, setCopiedPath] = useState63(null);
34206
- const [activeFilter, setActiveFilter] = useState63("all");
34207
- const [searchQuery, setSearchQuery] = useState63("");
34208
- const [manifest, setManifest] = useState63(/* @__PURE__ */ new Map());
34209
- const manifest404Ref = useRef68(/* @__PURE__ */ new Set());
34452
+ const fileInputRef = useRef69(null);
34453
+ const [dragOver, setDragOver] = useState64(false);
34454
+ const [copiedPath, setCopiedPath] = useState64(null);
34455
+ const [activeFilter, setActiveFilter] = useState64("all");
34456
+ const [searchQuery, setSearchQuery] = useState64("");
34457
+ const [manifest, setManifest] = useState64(/* @__PURE__ */ new Map());
34458
+ const manifest404Ref = useRef69(/* @__PURE__ */ new Set());
34210
34459
  const assetsKey = assets.join("|");
34211
34460
  useEffect50(() => {
34212
34461
  if (manifest404Ref.current.has(projectId)) return;
@@ -34492,11 +34741,11 @@ var AssetsTab = memo28(function AssetsTab2({
34492
34741
  });
34493
34742
 
34494
34743
  // src/components/sidebar/BlocksTab.tsx
34495
- import { memo as memo29, useState as useState65, useCallback as useCallback83, useRef as useRef69, useEffect as useEffect52 } from "react";
34744
+ import { memo as memo29, useState as useState66, useCallback as useCallback83, useRef as useRef70, useEffect as useEffect52 } from "react";
34496
34745
  import { createPortal as createPortal5 } from "react-dom";
34497
34746
 
34498
34747
  // src/hooks/useBlockCatalog.ts
34499
- import { useState as useState64, useEffect as useEffect51, useMemo as useMemo30 } from "react";
34748
+ import { useState as useState65, useEffect as useEffect51, useMemo as useMemo30 } from "react";
34500
34749
 
34501
34750
  // src/utils/blockCategories.ts
34502
34751
  import {
@@ -34520,11 +34769,11 @@ function getCategoryColors(category) {
34520
34769
 
34521
34770
  // src/hooks/useBlockCatalog.ts
34522
34771
  function useBlockCatalog() {
34523
- const [blocks, setBlocks] = useState64([]);
34524
- const [loading, setLoading] = useState64(true);
34525
- const [error, setError] = useState64(null);
34526
- const [search, setSearch] = useState64("");
34527
- const [category, setCategory] = useState64(null);
34772
+ const [blocks, setBlocks] = useState65([]);
34773
+ const [loading, setLoading] = useState65(true);
34774
+ const [error, setError] = useState65(null);
34775
+ const [search, setSearch] = useState65("");
34776
+ const [category, setCategory] = useState65(null);
34528
34777
  useEffect51(() => {
34529
34778
  let cancelled = false;
34530
34779
  (async () => {
@@ -34579,7 +34828,7 @@ function useBlockCatalog() {
34579
34828
  import { jsx as jsx74, jsxs as jsxs62 } from "react/jsx-runtime";
34580
34829
  var BlocksTab = memo29(function BlocksTab2({ onAddBlock, onPreviewBlock }) {
34581
34830
  const { loading, error, search, setSearch, category, setCategory, filteredBlocks } = useBlockCatalog();
34582
- const [promptModal, setPromptModal] = useState65(null);
34831
+ const [promptModal, setPromptModal] = useState66(null);
34583
34832
  if (loading) {
34584
34833
  return /* @__PURE__ */ jsx74("div", { className: "flex flex-1 items-center justify-center text-neutral-600 text-xs", children: "Loading blocks\u2026" });
34585
34834
  }
@@ -34784,9 +35033,9 @@ function BlockCard({
34784
35033
  onShowPrompt,
34785
35034
  onPreview
34786
35035
  }) {
34787
- const [hovered, setHovered] = useState65(false);
34788
- const [adding, setAdding] = useState65(false);
34789
- const hoverTimer = useRef69(null);
35036
+ const [hovered, setHovered] = useState66(false);
35037
+ const [adding, setAdding] = useState66(false);
35038
+ const hoverTimer = useRef70(null);
34790
35039
  const colors = getCategoryColors(category);
34791
35040
  const needsWebGL = tags?.includes("html-in-canvas") || tags?.includes("webgl");
34792
35041
  const handleEnter = useCallback83(() => {
@@ -34955,9 +35204,9 @@ function PromptPreviewModal({
34955
35204
  prompt,
34956
35205
  onClose
34957
35206
  }) {
34958
- const [value, setValue] = useState65(prompt);
34959
- const [copied, setCopied] = useState65(false);
34960
- const textareaRef = useRef69(null);
35207
+ const [value, setValue] = useState66(prompt);
35208
+ const [copied, setCopied] = useState66(false);
35209
+ const textareaRef = useRef70(null);
34961
35210
  useEffect52(() => {
34962
35211
  requestAnimationFrame(() => textareaRef.current?.focus());
34963
35212
  }, []);
@@ -35083,8 +35332,8 @@ var LeftSidebar = memo30(
35083
35332
  onPreviewBlock,
35084
35333
  takeoverContent
35085
35334
  }, ref) {
35086
- const [tab, setTab] = useState66(getPersistedTab);
35087
- const tabRef = useRef70(tab);
35335
+ const [tab, setTab] = useState67(getPersistedTab);
35336
+ const tabRef = useRef71(tab);
35088
35337
  tabRef.current = tab;
35089
35338
  const selectTab = useCallback84((t) => {
35090
35339
  setTab(t);
@@ -35491,10 +35740,10 @@ function StudioLeftSidebar({
35491
35740
  }
35492
35741
 
35493
35742
  // src/components/StudioPreviewArea.tsx
35494
- import { useState as useState75, useMemo as useMemo33 } from "react";
35743
+ import { useState as useState76, useMemo as useMemo33, useCallback as useCallback92 } from "react";
35495
35744
 
35496
35745
  // src/captions/components/CaptionOverlay.tsx
35497
- import { memo as memo31, useState as useState67, useCallback as useCallback86, useRef as useRef71 } from "react";
35746
+ import { memo as memo31, useState as useState68, useCallback as useCallback86, useRef as useRef72 } from "react";
35498
35747
 
35499
35748
  // src/captions/keyboard.ts
35500
35749
  var CAPTION_NUDGE_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"]);
@@ -35611,13 +35860,13 @@ function getWordEl(iframe, groupIndex, wordIndex) {
35611
35860
  return wordEls[wordIndex] ?? null;
35612
35861
  }
35613
35862
  function readGsapTransform(el, iframeWin) {
35614
- const gsap = iframeWin.gsap;
35615
- if (gsap && gsap.getProperty) {
35863
+ const gsap2 = iframeWin.gsap;
35864
+ if (gsap2 && gsap2.getProperty) {
35616
35865
  return {
35617
- x: gsap.getProperty(el, "x") || 0,
35618
- y: gsap.getProperty(el, "y") || 0,
35619
- scale: gsap.getProperty(el, "scale") || 1,
35620
- rotation: gsap.getProperty(el, "rotation") || 0
35866
+ x: gsap2.getProperty(el, "x") || 0,
35867
+ y: gsap2.getProperty(el, "y") || 0,
35868
+ scale: gsap2.getProperty(el, "scale") || 1,
35869
+ rotation: gsap2.getProperty(el, "rotation") || 0
35621
35870
  };
35622
35871
  }
35623
35872
  const t = el.style.transform || "";
@@ -35645,9 +35894,9 @@ function getOrCreateWrapper(el) {
35645
35894
  }
35646
35895
  function writeTransform(el, iframeWin, x, y, scale, rotation) {
35647
35896
  const wrapper = getOrCreateWrapper(el);
35648
- const gsap = iframeWin.gsap;
35649
- if (gsap && gsap.set) {
35650
- gsap.set(wrapper, { x, y, scale, rotation });
35897
+ const gsap2 = iframeWin.gsap;
35898
+ if (gsap2 && gsap2.set) {
35899
+ gsap2.set(wrapper, { x, y, scale, rotation });
35651
35900
  } else {
35652
35901
  wrapper.style.transform = `translate(${x.toFixed(1)}px, ${y.toFixed(1)}px) rotate(${rotation.toFixed(1)}deg) scale(${scale.toFixed(3)})`;
35653
35902
  }
@@ -35682,11 +35931,11 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
35682
35931
  const selectedSegmentIds = useCaptionStore((s) => s.selectedSegmentIds);
35683
35932
  const selectSegment = useCaptionStore((s) => s.selectSegment);
35684
35933
  const clearSelection = useCaptionStore((s) => s.clearSelection);
35685
- const [wordBoxes, setWordBoxes] = useState67([]);
35686
- const overlayRef = useRef71(null);
35687
- const modelRef = useRef71(model);
35934
+ const [wordBoxes, setWordBoxes] = useState68([]);
35935
+ const overlayRef = useRef72(null);
35936
+ const modelRef = useRef72(model);
35688
35937
  modelRef.current = model;
35689
- const interactionRef = useRef71(null);
35938
+ const interactionRef = useRef72(null);
35690
35939
  useMountEffect(() => {
35691
35940
  if (!isEditMode) return;
35692
35941
  let prevBoxes = [];
@@ -35973,7 +36222,7 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
35973
36222
  });
35974
36223
 
35975
36224
  // src/captions/components/CaptionTimeline.tsx
35976
- import { memo as memo32, useCallback as useCallback87, useRef as useRef72 } from "react";
36225
+ import { memo as memo32, useCallback as useCallback87, useRef as useRef73 } from "react";
35977
36226
  import { jsx as jsx79, jsxs as jsxs67 } from "react/jsx-runtime";
35978
36227
  var GROUP_COLORS = [
35979
36228
  "#3CE6AC",
@@ -35996,7 +36245,7 @@ var CaptionTimeline = memo32(function CaptionTimeline2({
35996
36245
  const selectSegment = useCaptionStore((s) => s.selectSegment);
35997
36246
  const updateSegmentTiming = useCaptionStore((s) => s.updateSegmentTiming);
35998
36247
  const splitGroup = useCaptionStore((s) => s.splitGroup);
35999
- const dragRef = useRef72(null);
36248
+ const dragRef = useRef73(null);
36000
36249
  const handleEdgePointerDown = useCallback87(
36001
36250
  (e, segId, edge, originalStart, originalEnd) => {
36002
36251
  e.stopPropagation();
@@ -36118,10 +36367,10 @@ var CaptionTimeline = memo32(function CaptionTimeline2({
36118
36367
  });
36119
36368
 
36120
36369
  // src/components/editor/DomEditOverlay.tsx
36121
- import { memo as memo35, useEffect as useEffect53, useMemo as useMemo31, useRef as useRef76, useState as useState70 } from "react";
36370
+ import { memo as memo35, useEffect as useEffect53, useMemo as useMemo31, useRef as useRef77, useState as useState71 } from "react";
36122
36371
 
36123
36372
  // src/components/editor/marqueeCommit.ts
36124
- import { useCallback as useCallback88, useRef as useRef73, useState as useState68 } from "react";
36373
+ import { useCallback as useCallback88, useRef as useRef74, useState as useState69 } from "react";
36125
36374
 
36126
36375
  // src/utils/marqueeGeometry.ts
36127
36376
  function rectsOverlap(a, b) {
@@ -36312,9 +36561,9 @@ async function runMarqueeIntersection(rect, iframe, overlayEl, activeComposition
36312
36561
  return hits;
36313
36562
  }
36314
36563
  function useMarqueeGestures(deps) {
36315
- const marqueeRef = useRef73(null);
36316
- const [marqueeRect, setMarqueeRect] = useState68(null);
36317
- const [candidateRects, setCandidateRects] = useState68([]);
36564
+ const marqueeRef = useRef74(null);
36565
+ const [marqueeRect, setMarqueeRect] = useState69(null);
36566
+ const [candidateRects, setCandidateRects] = useState69([]);
36318
36567
  const commitMarquee = useCallback88(
36319
36568
  async (rect, additive) => {
36320
36569
  const iframe = deps.iframeRef.current;
@@ -36488,7 +36737,7 @@ function hasDomEditRotationChanged(initialAngle, nextAngle) {
36488
36737
  }
36489
36738
 
36490
36739
  // src/components/editor/useDomEditOverlayRects.ts
36491
- import { useRef as useRef74, useState as useState69 } from "react";
36740
+ import { useRef as useRef75, useState as useState70 } from "react";
36492
36741
  function childRectsEqual(a, b) {
36493
36742
  if (a.length !== b.length) return false;
36494
36743
  for (let i = 0; i < a.length; i++) {
@@ -36505,17 +36754,17 @@ function useDomEditOverlayRects({
36505
36754
  hoverSelectionRef,
36506
36755
  rafPausedRef
36507
36756
  }) {
36508
- const [overlayRect, setOverlayRectState] = useState69(null);
36509
- const [hoverRect, setHoverRectState] = useState69(null);
36510
- const [groupOverlayItems, setGroupOverlayItemsState] = useState69([]);
36511
- const [childRects, setChildRectsState] = useState69([]);
36512
- const overlayRectRef = useRef74(null);
36513
- const hoverRectRef = useRef74(null);
36514
- const groupOverlayItemsRef = useRef74([]);
36515
- const resolvedElementRef = useRef74(null);
36516
- const resolvedHoverElementRef = useRef74(null);
36517
- const resolvedGroupElementRef = useRef74(/* @__PURE__ */ new Map());
36518
- const childRectsRef = useRef74([]);
36757
+ const [overlayRect, setOverlayRectState] = useState70(null);
36758
+ const [hoverRect, setHoverRectState] = useState70(null);
36759
+ const [groupOverlayItems, setGroupOverlayItemsState] = useState70([]);
36760
+ const [childRects, setChildRectsState] = useState70([]);
36761
+ const overlayRectRef = useRef75(null);
36762
+ const hoverRectRef = useRef75(null);
36763
+ const groupOverlayItemsRef = useRef75([]);
36764
+ const resolvedElementRef = useRef75(null);
36765
+ const resolvedHoverElementRef = useRef75(null);
36766
+ const resolvedGroupElementRef = useRef75(/* @__PURE__ */ new Map());
36767
+ const childRectsRef = useRef75([]);
36519
36768
  const setOverlayRect = (next) => {
36520
36769
  if (rectsEqual(overlayRectRef.current, next)) return;
36521
36770
  overlayRectRef.current = next;
@@ -36746,27 +36995,27 @@ function OffCanvasIndicators({
36746
36995
  // src/components/editor/manualOffsetDrag.ts
36747
36996
  function getOffsetDragGsap(element) {
36748
36997
  const win = element.ownerDocument.defaultView;
36749
- const gsap = win?.gsap;
36750
- return gsap?.set && gsap.getProperty ? gsap : null;
36998
+ const gsap2 = win?.gsap;
36999
+ return gsap2?.set && gsap2.getProperty ? gsap2 : null;
36751
37000
  }
36752
37001
  function applyOffsetDragDraftViaGsap(element, offset, baseGsap) {
36753
- const gsap = getOffsetDragGsap(element);
36754
- if (!gsap) return false;
37002
+ const gsap2 = getOffsetDragGsap(element);
37003
+ if (!gsap2) return false;
36755
37004
  element.style.setProperty("translate", "none");
36756
37005
  const { newX, newY } = computeDraggedGsapPosition(element, offset, baseGsap);
36757
- gsap.set(element, { x: newX, y: newY });
37006
+ gsap2.set(element, { x: newX, y: newY });
36758
37007
  return true;
36759
37008
  }
36760
37009
  function applyRotationDraftViaGsap(element, angle) {
36761
- const gsap = getOffsetDragGsap(element);
36762
- if (!gsap) return false;
37010
+ const gsap2 = getOffsetDragGsap(element);
37011
+ if (!gsap2) return false;
36763
37012
  element.style.setProperty("rotate", "none");
36764
- gsap.set(element, { rotation: angle });
37013
+ gsap2.set(element, { rotation: angle });
36765
37014
  return true;
36766
37015
  }
36767
37016
  function readGsapRotation(element) {
36768
- const gsap = getOffsetDragGsap(element);
36769
- return gsap ? Number(gsap.getProperty(element, "rotation")) || 0 : 0;
37017
+ const gsap2 = getOffsetDragGsap(element);
37018
+ return gsap2 ? Number(gsap2.getProperty(element, "rotation")) || 0 : 0;
36770
37019
  }
36771
37020
  var DEFAULT_OFFSET_PROBE_PX = 100;
36772
37021
  var MIN_PROBE_VECTOR_LENGTH_PX = 0.01;
@@ -37971,7 +38220,7 @@ function createDomEditOverlayGestureHandlers(opts) {
37971
38220
  }
37972
38221
 
37973
38222
  // src/components/editor/SnapGuideOverlay.tsx
37974
- import { memo as memo33, useRef as useRef75 } from "react";
38223
+ import { memo as memo33, useRef as useRef76 } from "react";
37975
38224
  import { jsx as jsx82, jsxs as jsxs69 } from "react/jsx-runtime";
37976
38225
  var MAX_GUIDES = 6;
37977
38226
  var MAX_SPACING_GUIDES = 4;
@@ -37983,12 +38232,12 @@ var SnapGuideOverlay = memo33(function SnapGuideOverlay2({
37983
38232
  overlayWidth,
37984
38233
  overlayHeight
37985
38234
  }) {
37986
- const guideElsRef = useRef75([]);
37987
- const spacingElsRef = useRef75([]);
37988
- const spacingLabelElsRef = useRef75([]);
37989
- const overlayWidthRef = useRef75(overlayWidth);
38235
+ const guideElsRef = useRef76([]);
38236
+ const spacingElsRef = useRef76([]);
38237
+ const spacingLabelElsRef = useRef76([]);
38238
+ const overlayWidthRef = useRef76(overlayWidth);
37990
38239
  overlayWidthRef.current = overlayWidth;
37991
- const overlayHeightRef = useRef75(overlayHeight);
38240
+ const overlayHeightRef = useRef76(overlayHeight);
37992
38241
  overlayHeightRef.current = overlayHeight;
37993
38242
  useMountEffect(() => {
37994
38243
  let frame = 0;
@@ -38170,9 +38419,9 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38170
38419
  onRotationCommit,
38171
38420
  onMarqueeSelect
38172
38421
  }) {
38173
- const overlayRef = useRef76(null);
38174
- const boxRef = useRef76(null);
38175
- const onMarqueeSelectRef = useRef76(onMarqueeSelect);
38422
+ const overlayRef = useRef77(null);
38423
+ const boxRef = useRef77(null);
38424
+ const onMarqueeSelectRef = useRef77(onMarqueeSelect);
38176
38425
  onMarqueeSelectRef.current = onMarqueeSelect;
38177
38426
  const selectionShapeStyles = (() => {
38178
38427
  const fallback = {
@@ -38196,39 +38445,39 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38196
38445
  return fallback;
38197
38446
  }
38198
38447
  })();
38199
- const gestureRef = useRef76(null);
38200
- const groupGestureRef = useRef76(null);
38201
- const blockedMoveRef = useRef76(null);
38202
- const suppressNextBoxClickRef = useRef76(false);
38203
- const suppressNextBoxMouseDownRef = useRef76(false);
38204
- const suppressNextOverlayMouseDownRef = useRef76(false);
38205
- const snapGuidesRef = useRef76(null);
38206
- const rafPausedRef = useRef76(false);
38207
- const selectionRef = useRef76(selection);
38448
+ const gestureRef = useRef77(null);
38449
+ const groupGestureRef = useRef77(null);
38450
+ const blockedMoveRef = useRef77(null);
38451
+ const suppressNextBoxClickRef = useRef77(false);
38452
+ const suppressNextBoxMouseDownRef = useRef77(false);
38453
+ const suppressNextOverlayMouseDownRef = useRef77(false);
38454
+ const snapGuidesRef = useRef77(null);
38455
+ const rafPausedRef = useRef77(false);
38456
+ const selectionRef = useRef77(selection);
38208
38457
  selectionRef.current = selection;
38209
- const activeCompositionPathRef = useRef76(activeCompositionPath);
38458
+ const activeCompositionPathRef = useRef77(activeCompositionPath);
38210
38459
  activeCompositionPathRef.current = activeCompositionPath;
38211
- const groupSelectionsRef = useRef76(groupSelections);
38460
+ const groupSelectionsRef = useRef77(groupSelections);
38212
38461
  groupSelectionsRef.current = groupSelections;
38213
- const hoverSelectionRef = useRef76(hoverSelection);
38462
+ const hoverSelectionRef = useRef77(hoverSelection);
38214
38463
  hoverSelectionRef.current = hoverSelection;
38215
- const onPathOffsetCommitRef = useRef76(onPathOffsetCommit);
38464
+ const onPathOffsetCommitRef = useRef77(onPathOffsetCommit);
38216
38465
  onPathOffsetCommitRef.current = onPathOffsetCommit;
38217
- const onGroupPathOffsetCommitRef = useRef76(onGroupPathOffsetCommit);
38466
+ const onGroupPathOffsetCommitRef = useRef77(onGroupPathOffsetCommit);
38218
38467
  onGroupPathOffsetCommitRef.current = onGroupPathOffsetCommit;
38219
- const onBoxSizeCommitRef = useRef76(onBoxSizeCommit);
38468
+ const onBoxSizeCommitRef = useRef77(onBoxSizeCommit);
38220
38469
  onBoxSizeCommitRef.current = onBoxSizeCommit;
38221
- const onRotationCommitRef = useRef76(onRotationCommit);
38470
+ const onRotationCommitRef = useRef77(onRotationCommit);
38222
38471
  onRotationCommitRef.current = onRotationCommit;
38223
- const onBlockedMoveRef = useRef76(onBlockedMove);
38472
+ const onBlockedMoveRef = useRef77(onBlockedMove);
38224
38473
  onBlockedMoveRef.current = onBlockedMove;
38225
- const onManualDragStartRef = useRef76(onManualDragStart);
38474
+ const onManualDragStartRef = useRef77(onManualDragStart);
38226
38475
  onManualDragStartRef.current = onManualDragStart;
38227
- const onCanvasPointerMoveRef = useRef76(onCanvasPointerMove);
38476
+ const onCanvasPointerMoveRef = useRef77(onCanvasPointerMove);
38228
38477
  onCanvasPointerMoveRef.current = onCanvasPointerMove;
38229
- const onCanvasPointerLeaveRef = useRef76(onCanvasPointerLeave);
38478
+ const onCanvasPointerLeaveRef = useRef77(onCanvasPointerLeave);
38230
38479
  onCanvasPointerLeaveRef.current = onCanvasPointerLeave;
38231
- const onSelectionChangeRef = useRef76(onSelectionChange);
38480
+ const onSelectionChangeRef = useRef77(onSelectionChange);
38232
38481
  onSelectionChangeRef.current = onSelectionChange;
38233
38482
  const {
38234
38483
  overlayRect,
@@ -38248,7 +38497,7 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38248
38497
  hoverSelectionRef,
38249
38498
  rafPausedRef
38250
38499
  });
38251
- const [compRect, setCompRect] = useState70({
38500
+ const [compRect, setCompRect] = useState71({
38252
38501
  left: 0,
38253
38502
  top: 0,
38254
38503
  width: 0,
@@ -38283,8 +38532,8 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38283
38532
  frame = requestAnimationFrame(update);
38284
38533
  return () => cancelAnimationFrame(frame);
38285
38534
  });
38286
- const offCanvasElementsRef = useRef76(/* @__PURE__ */ new Map());
38287
- const [offCanvasRects, setOffCanvasRects] = useState70([]);
38535
+ const offCanvasElementsRef = useRef77(/* @__PURE__ */ new Map());
38536
+ const [offCanvasRects, setOffCanvasRects] = useState71([]);
38288
38537
  useEffect53(() => {
38289
38538
  const iframe = iframeRef.current;
38290
38539
  const overlay = overlayRef.current;
@@ -38628,10 +38877,10 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38628
38877
  });
38629
38878
 
38630
38879
  // src/components/editor/MotionPathOverlay.tsx
38631
- import { memo as memo36, useEffect as useEffect55, useRef as useRef79, useState as useState72 } from "react";
38880
+ import { memo as memo36, useEffect as useEffect55, useRef as useRef80, useState as useState73 } from "react";
38632
38881
 
38633
38882
  // src/contexts/DomEditContext.tsx
38634
- import { createContext as createContext6, useCallback as useCallback89, useContext as useContext6, useMemo as useMemo32, useRef as useRef77 } from "react";
38883
+ import { createContext as createContext6, useCallback as useCallback89, useContext as useContext6, useMemo as useMemo32, useRef as useRef78 } from "react";
38635
38884
  import { jsx as jsx85 } from "react/jsx-runtime";
38636
38885
  var DomEditActionsContext = createContext6(null);
38637
38886
  var DomEditSelectionContext = createContext6(null);
@@ -38710,6 +38959,9 @@ function DomEditProvider({
38710
38959
  handleGsapAddKeyframe,
38711
38960
  handleGsapAddKeyframeBatch,
38712
38961
  handleGsapRemoveKeyframe,
38962
+ handleGsapMoveKeyframeToPlayhead,
38963
+ handleGsapMoveKeyframe,
38964
+ handleGsapResizeKeyframedTween,
38713
38965
  handleGsapConvertToKeyframes,
38714
38966
  handleGsapRemoveAllKeyframes,
38715
38967
  handleResetSelectedElementKeyframes,
@@ -38727,7 +38979,7 @@ function DomEditProvider({
38727
38979
  },
38728
38980
  children
38729
38981
  }) {
38730
- const commitMutationRef = useRef77(commitMutation);
38982
+ const commitMutationRef = useRef78(commitMutation);
38731
38983
  commitMutationRef.current = commitMutation;
38732
38984
  const stableCommitMutation = useCallback89(
38733
38985
  (mutation, options) => commitMutationRef.current(mutation, options),
@@ -38783,6 +39035,9 @@ function DomEditProvider({
38783
39035
  handleGsapAddKeyframe,
38784
39036
  handleGsapAddKeyframeBatch,
38785
39037
  handleGsapRemoveKeyframe,
39038
+ handleGsapMoveKeyframeToPlayhead,
39039
+ handleGsapMoveKeyframe,
39040
+ handleGsapResizeKeyframedTween,
38786
39041
  handleGsapConvertToKeyframes,
38787
39042
  handleGsapRemoveAllKeyframes,
38788
39043
  handleResetSelectedElementKeyframes,
@@ -38847,6 +39102,9 @@ function DomEditProvider({
38847
39102
  handleGsapAddKeyframe,
38848
39103
  handleGsapAddKeyframeBatch,
38849
39104
  handleGsapRemoveKeyframe,
39105
+ handleGsapMoveKeyframeToPlayhead,
39106
+ handleGsapMoveKeyframe,
39107
+ handleGsapResizeKeyframedTween,
38850
39108
  handleGsapConvertToKeyframes,
38851
39109
  handleGsapRemoveAllKeyframes,
38852
39110
  handleResetSelectedElementKeyframes,
@@ -39078,7 +39336,7 @@ function commitCreatePath(targetSelector, position, x, y, commit) {
39078
39336
  }
39079
39337
 
39080
39338
  // src/components/editor/useMotionPathData.ts
39081
- import { useEffect as useEffect54, useRef as useRef78, useState as useState71 } from "react";
39339
+ import { useEffect as useEffect54, useRef as useRef79, useState as useState72 } from "react";
39082
39340
  function transformTranslate(el) {
39083
39341
  const t = el.ownerDocument?.defaultView?.getComputedStyle(el).transform;
39084
39342
  if (!t || t === "none") return { x: 0, y: 0 };
@@ -39142,13 +39400,13 @@ function hasMotionPathPlugin(iframe) {
39142
39400
  }
39143
39401
  }
39144
39402
  function useMotionPathData(iframeRef, selector) {
39145
- const [rect, setRect] = useState71(null);
39146
- const [geometry, setGeometry] = useState71(null);
39147
- const resolvedForRef = useRef78(null);
39403
+ const [rect, setRect] = useState72(null);
39404
+ const [geometry, setGeometry] = useState72(null);
39405
+ const resolvedForRef = useRef79(null);
39148
39406
  const geometryResolved = resolvedForRef.current === selector;
39149
- const [visibleInPreview, setVisibleInPreview] = useState71(true);
39150
- const [home, setHome] = useState71(null);
39151
- const [pScale, setPScale] = useState71(1);
39407
+ const [visibleInPreview, setVisibleInPreview] = useState72(true);
39408
+ const [home, setHome] = useState72(null);
39409
+ const [pScale, setPScale] = useState72(1);
39152
39410
  useEffect54(() => {
39153
39411
  if (!selector) {
39154
39412
  setRect(null);
@@ -39226,22 +39484,23 @@ var MotionPathOverlay = memo36(function MotionPathOverlay2({
39226
39484
  commitMutation,
39227
39485
  selectedGsapAnimations,
39228
39486
  handleGsapRemoveKeyframe,
39229
- handleGsapDeleteAllForElement
39487
+ handleGsapRemoveAllKeyframes,
39488
+ handleGsapMoveKeyframeToPlayhead
39230
39489
  } = useDomEditContext();
39231
39490
  const { rect, geometry, geometryResolved, visibleInPreview, home, pScale } = useMotionPathData(
39232
39491
  iframeRef,
39233
39492
  selectorFor(selection)
39234
39493
  );
39235
- const [draft, setDraft] = useState72(null);
39236
- const [ghost, setGhost] = useState72(null);
39237
- const [hoverNode, setHoverNode] = useState72(null);
39238
- const [kfMenu, setKfMenu] = useState72(null);
39494
+ const [draft, setDraft] = useState73(null);
39495
+ const [ghost, setGhost] = useState73(null);
39496
+ const [hoverNode, setHoverNode] = useState73(null);
39497
+ const [kfMenu, setKfMenu] = useState73(null);
39239
39498
  const activeKeyframePct = usePlayerStore((s) => s.activeKeyframePct);
39240
39499
  const armed = usePlayerStore((s) => s.motionPathArmed);
39241
39500
  const setMotionPathArmed = usePlayerStore((s) => s.setMotionPathArmed);
39242
39501
  const setMotionPathCreateAvailable = usePlayerStore((s) => s.setMotionPathCreateAvailable);
39243
- const dragRef = useRef79(null);
39244
- const parkTimerRef = useRef79(void 0);
39502
+ const dragRef = useRef80(null);
39503
+ const parkTimerRef = useRef80(void 0);
39245
39504
  const animId = editableAnimationId(selectedGsapAnimations ?? [], geometry?.kind ?? "linear");
39246
39505
  useEffect55(() => () => clearTimeout(parkTimerRef.current), [animId]);
39247
39506
  const createMode = geometryResolved && !geometry && Boolean(selection?.element) && !isPlaying;
@@ -39549,14 +39808,15 @@ var MotionPathOverlay = memo36(function MotionPathOverlay2({
39549
39808
  state: kfMenu,
39550
39809
  onClose: () => setKfMenu(null),
39551
39810
  onDelete: (_elId, pct) => animId && handleGsapRemoveKeyframe(animId, pct),
39552
- onDeleteAll: (elId) => handleGsapDeleteAllForElement(`#${elId}`)
39811
+ onDeleteAll: () => animId && handleGsapRemoveAllKeyframes(animId),
39812
+ onMoveToPlayhead: (_elId, pct) => animId && handleGsapMoveKeyframeToPlayhead(animId, pct)
39553
39813
  }
39554
39814
  )
39555
39815
  ] });
39556
39816
  });
39557
39817
 
39558
39818
  // src/components/editor/SnapToolbar.tsx
39559
- import { memo as memo37, useCallback as useCallback90, useEffect as useEffect56, useRef as useRef80, useState as useState73 } from "react";
39819
+ import { memo as memo37, useCallback as useCallback90, useEffect as useEffect56, useRef as useRef81, useState as useState74 } from "react";
39560
39820
  import { MagnetStraight, GridFour, Path } from "@phosphor-icons/react";
39561
39821
  import { jsx as jsx88, jsxs as jsxs73 } from "react/jsx-runtime";
39562
39822
  var SNAP_DEFAULTS = {
@@ -39575,13 +39835,13 @@ function readSnapPrefs() {
39575
39835
  };
39576
39836
  }
39577
39837
  var SnapToolbar = memo37(function SnapToolbar2({ onSnapChange }) {
39578
- const [prefs, setPrefs] = useState73(readSnapPrefs);
39579
- const [gridPopoverOpen, setGridPopoverOpen] = useState73(false);
39838
+ const [prefs, setPrefs] = useState74(readSnapPrefs);
39839
+ const [gridPopoverOpen, setGridPopoverOpen] = useState74(false);
39580
39840
  const motionPathCreateAvailable = usePlayerStore((s) => s.motionPathCreateAvailable);
39581
39841
  const motionPathArmed = usePlayerStore((s) => s.motionPathArmed);
39582
39842
  const setMotionPathArmed = usePlayerStore((s) => s.setMotionPathArmed);
39583
- const popoverRef = useRef80(null);
39584
- const gridButtonRef = useRef80(null);
39843
+ const popoverRef = useRef81(null);
39844
+ const gridButtonRef = useRef81(null);
39585
39845
  const updatePrefs = useCallback90(
39586
39846
  (patch) => {
39587
39847
  setPrefs((prev) => {
@@ -39720,7 +39980,7 @@ var SnapToolbar = memo37(function SnapToolbar2({ onSnapChange }) {
39720
39980
  });
39721
39981
 
39722
39982
  // src/components/StudioFeedbackBar.tsx
39723
- import { memo as memo38, useState as useState74, useCallback as useCallback91, useRef as useRef81, useEffect as useEffect57 } from "react";
39983
+ import { memo as memo38, useState as useState75, useCallback as useCallback91, useRef as useRef82, useEffect as useEffect57 } from "react";
39724
39984
  import { Fragment as Fragment29, jsx as jsx89, jsxs as jsxs74 } from "react/jsx-runtime";
39725
39985
  var DEFAULT_FEEDBACK_INTERVAL = 10;
39726
39986
  var AUTO_DISMISS_MS = 2e4;
@@ -39774,13 +40034,13 @@ function markPrompted() {
39774
40034
  }
39775
40035
  }
39776
40036
  var StudioFeedbackBar = memo38(function StudioFeedbackBar2() {
39777
- const [visible, setVisible] = useState74(false);
39778
- const [rating, setRating] = useState74(null);
39779
- const [comment, setComment] = useState74("");
39780
- const [submitted, setSubmitted] = useState74(false);
39781
- const [exiting, setExiting] = useState74(false);
39782
- const inputRef = useRef81(null);
39783
- const dismissTimerRef = useRef81(null);
40037
+ const [visible, setVisible] = useState75(false);
40038
+ const [rating, setRating] = useState75(null);
40039
+ const [comment, setComment] = useState75("");
40040
+ const [submitted, setSubmitted] = useState75(false);
40041
+ const [exiting, setExiting] = useState75(false);
40042
+ const inputRef = useRef82(null);
40043
+ const dismissTimerRef = useRef82(null);
39784
40044
  useEffect57(() => {
39785
40045
  incrementSessionCount();
39786
40046
  const showTimer = setTimeout(() => {
@@ -39900,6 +40160,46 @@ var StudioFeedbackBar = memo38(function StudioFeedbackBar2() {
39900
40160
  );
39901
40161
  });
39902
40162
 
40163
+ // src/components/editor/keyframeRetime.ts
40164
+ var NOOP_EPSILON_PCT2 = 0.1;
40165
+ var EPSILON_TIME = 1e-4;
40166
+ var round33 = (n) => Math.round(n * 1e3) / 1e3;
40167
+ var round1 = (n) => Math.round(n * 10) / 10;
40168
+ var clamp5 = (n, lo, hi) => Math.max(lo, Math.min(hi, n));
40169
+ function resolveKeyframeRetime(opts) {
40170
+ const { keyframes, draggedTweenPct, tweenStart, tweenDuration, dropAbsTime } = opts;
40171
+ if (tweenDuration <= 0) return { kind: "noop" };
40172
+ const tweenEnd = tweenStart + tweenDuration;
40173
+ if (dropAbsTime >= tweenStart - EPSILON_TIME && dropAbsTime <= tweenEnd + EPSILON_TIME) {
40174
+ const toTweenPct = clamp5((dropAbsTime - tweenStart) / tweenDuration * 100, 0, 100);
40175
+ if (Math.abs(toTweenPct - draggedTweenPct) < NOOP_EPSILON_PCT2) return { kind: "noop" };
40176
+ return { kind: "move", toTweenPct };
40177
+ }
40178
+ if (keyframes.length === 0) return { kind: "noop" };
40179
+ const newStart = Math.min(dropAbsTime, tweenStart);
40180
+ const newEnd = Math.max(dropAbsTime, tweenEnd);
40181
+ const newDuration = Math.max(0.01, newEnd - newStart);
40182
+ let draggedIdx = 0;
40183
+ let best = Infinity;
40184
+ keyframes.forEach((kf, i) => {
40185
+ const d = Math.abs(kf.percentage - draggedTweenPct);
40186
+ if (d < best) {
40187
+ best = d;
40188
+ draggedIdx = i;
40189
+ }
40190
+ });
40191
+ const pctRemap = keyframes.map((kf, i) => {
40192
+ const absTime = i === draggedIdx ? dropAbsTime : tweenStart + kf.percentage / 100 * tweenDuration;
40193
+ return { from: kf.percentage, to: round1((absTime - newStart) / newDuration * 100) };
40194
+ });
40195
+ return {
40196
+ kind: "resize",
40197
+ position: round33(newStart),
40198
+ duration: round33(newDuration),
40199
+ pctRemap
40200
+ };
40201
+ }
40202
+
39903
40203
  // src/components/StudioPreviewArea.tsx
39904
40204
  import { Fragment as Fragment30, jsx as jsx90, jsxs as jsxs75 } from "react/jsx-runtime";
39905
40205
  function StudioPreviewArea({
@@ -39961,14 +40261,17 @@ function StudioPreviewArea({
39961
40261
  handleDomBoxSizeCommit,
39962
40262
  handleDomRotationCommit,
39963
40263
  handleGsapRemoveKeyframe,
40264
+ handleGsapMoveKeyframeToPlayhead,
40265
+ handleGsapMoveKeyframe,
40266
+ handleGsapResizeKeyframedTween,
39964
40267
  handleGsapUpdateMeta,
39965
40268
  handleGsapAddKeyframe,
39966
40269
  handleGsapConvertToKeyframes,
39967
- handleGsapDeleteAllForElement,
40270
+ handleGsapRemoveAllKeyframes,
39968
40271
  buildDomSelectionForTimelineElement,
39969
40272
  applyMarqueeSelection
39970
40273
  } = useDomEditActionsContext();
39971
- const [snapPrefs, setSnapPrefs] = useState75(() => {
40274
+ const [snapPrefs, setSnapPrefs] = useState76(() => {
39972
40275
  const p = readStudioUiPreferences();
39973
40276
  return {
39974
40277
  snapEnabled: p.snapEnabled ?? true,
@@ -39977,6 +40280,16 @@ function StudioPreviewArea({
39977
40280
  snapToGrid: p.snapToGrid ?? false
39978
40281
  };
39979
40282
  });
40283
+ const resolveKeyframeTarget = useCallback92(
40284
+ (pct) => {
40285
+ const cached2 = usePlayerStore.getState().keyframeCache.get(domEditSelection?.id ?? "");
40286
+ const kf = cached2?.keyframes.find((k) => Math.abs(k.percentage - pct) < 0.2);
40287
+ const group = kf?.propertyGroup;
40288
+ const anim = (group ? selectedGsapAnimations.find((a) => a.propertyGroup === group) : void 0) ?? selectedGsapAnimations.find((a) => a.keyframes);
40289
+ return anim ? { animId: anim.id, tweenPct: kf?.tweenPercentage ?? pct } : null;
40290
+ },
40291
+ [domEditSelection?.id, selectedGsapAnimations]
40292
+ );
39980
40293
  const timelineEditCallbacks = useMemo33(
39981
40294
  () => ({
39982
40295
  onMoveElement: handleTimelineElementMove,
@@ -39985,19 +40298,61 @@ function StudioPreviewArea({
39985
40298
  onSplitElement: handleTimelineElementSplit,
39986
40299
  onRazorSplit: handleRazorSplit,
39987
40300
  onRazorSplitAll: handleRazorSplitAll,
39988
- onDeleteAllKeyframes: (elId) => {
39989
- const rawId = elId.includes("#") ? elId.split("#").pop() ?? elId : elId;
39990
- handleGsapDeleteAllForElement(`#${rawId}`);
40301
+ onDeleteAllKeyframes: () => {
40302
+ const anim = selectedGsapAnimations.find((a) => a.keyframes);
40303
+ if (!anim) return;
40304
+ handleGsapRemoveAllKeyframes(anim.id);
39991
40305
  },
39992
- // fallow-ignore-next-line complexity
39993
40306
  onDeleteKeyframe: (_elId, pct) => {
39994
- const cacheKey = domEditSelection?.id ?? "";
39995
- const cached2 = usePlayerStore.getState().keyframeCache.get(cacheKey);
39996
- const kf = cached2?.keyframes.find((k) => Math.abs(k.percentage - pct) < 0.2);
39997
- const group = kf?.propertyGroup;
39998
- const anim = (group ? selectedGsapAnimations.find((a) => a.propertyGroup === group) : void 0) ?? selectedGsapAnimations.find((a) => a.keyframes);
39999
- if (!anim) return;
40000
- handleGsapRemoveKeyframe(anim.id, kf?.tweenPercentage ?? pct);
40307
+ const target = resolveKeyframeTarget(pct);
40308
+ if (target) handleGsapRemoveKeyframe(target.animId, target.tweenPct);
40309
+ },
40310
+ // Retime the keyframe to the playhead, preserving its value + ease.
40311
+ onMoveKeyframeToPlayhead: (_elId, pct) => {
40312
+ const target = resolveKeyframeTarget(pct);
40313
+ if (target) handleGsapMoveKeyframeToPlayhead(target.animId, target.tweenPct);
40314
+ },
40315
+ // Drag-to-retime. The diamond reports clip-%s; resolveKeyframeTarget gives
40316
+ // the dragged keyframe's anim + tween-%. We convert the clip-% drop to an
40317
+ // absolute time (via the clip's timing basis) and let resolveKeyframeRetime
40318
+ // decide: a drop inside the tween window is a plain move (re-key tween-%); a
40319
+ // drop past the boundary (last keyframe past the end, first before the start)
40320
+ // resizes the tween — position/duration grow so the dragged keyframe lands at
40321
+ // the drop while every other keyframe keeps its absolute time (value+ease too).
40322
+ onMoveKeyframe: (_elId, fromClipPct, toClipPct) => {
40323
+ const target = resolveKeyframeTarget(fromClipPct);
40324
+ const sel = domEditSelection;
40325
+ if (!target || !sel) return;
40326
+ const anim = selectedGsapAnimations.find((a) => a.id === target.animId);
40327
+ const tweenStart = anim ? resolveTweenStart(anim) : null;
40328
+ if (!anim || tweenStart === null) return;
40329
+ const tweenDuration = anim.duration ?? resolveTweenDuration(anim);
40330
+ const sourceFile = sel.sourceFile || activeCompPath || "index.html";
40331
+ const { elements, domClipChildren } = usePlayerStore.getState();
40332
+ const { elStart, elDuration } = resolveClipTimingBasis(
40333
+ sel.id ?? "",
40334
+ sourceFile,
40335
+ elements,
40336
+ domClipChildren
40337
+ );
40338
+ const dropAbsTime = elStart + toClipPct / 100 * elDuration;
40339
+ const decision = resolveKeyframeRetime({
40340
+ keyframes: anim.keyframes?.keyframes ?? [],
40341
+ draggedTweenPct: target.tweenPct,
40342
+ tweenStart,
40343
+ tweenDuration,
40344
+ dropAbsTime
40345
+ });
40346
+ if (decision.kind === "move" && decision.toTweenPct != null) {
40347
+ handleGsapMoveKeyframe(target.animId, target.tweenPct, decision.toTweenPct);
40348
+ } else if (decision.kind === "resize" && decision.pctRemap && decision.position != null && decision.duration != null) {
40349
+ handleGsapResizeKeyframedTween(
40350
+ target.animId,
40351
+ decision.position,
40352
+ decision.duration,
40353
+ decision.pctRemap
40354
+ );
40355
+ }
40001
40356
  },
40002
40357
  onChangeKeyframeEase: (_elId, _pct, ease) => {
40003
40358
  for (const anim of selectedGsapAnimations) {
@@ -40030,16 +40385,20 @@ function StudioPreviewArea({
40030
40385
  handleTimelineElementSplit,
40031
40386
  handleRazorSplit,
40032
40387
  handleRazorSplitAll,
40033
- handleGsapDeleteAllForElement,
40034
- domEditSelection?.id,
40388
+ handleGsapRemoveAllKeyframes,
40389
+ resolveKeyframeTarget,
40035
40390
  selectedGsapAnimations,
40036
40391
  handleGsapRemoveKeyframe,
40392
+ handleGsapMoveKeyframeToPlayhead,
40393
+ handleGsapMoveKeyframe,
40394
+ handleGsapResizeKeyframedTween,
40037
40395
  handleGsapUpdateMeta,
40038
40396
  handleGsapAddKeyframe,
40039
40397
  handleGsapConvertToKeyframes,
40040
40398
  buildDomSelectionForTimelineElement,
40041
40399
  projectId,
40042
- activeCompPath
40400
+ activeCompPath,
40401
+ domEditSelection
40043
40402
  ]
40044
40403
  );
40045
40404
  return /* @__PURE__ */ jsxs75("div", { className: "flex-1 flex flex-col relative min-w-0", children: [
@@ -40143,17 +40502,17 @@ function StudioPreviewArea({
40143
40502
 
40144
40503
  // src/components/StudioRightPanel.tsx
40145
40504
  import {
40146
- useCallback as useCallback101,
40505
+ useCallback as useCallback102,
40147
40506
  useMemo as useMemo34,
40148
- useRef as useRef86,
40149
- useState as useState84
40507
+ useRef as useRef87,
40508
+ useState as useState85
40150
40509
  } from "react";
40151
40510
 
40152
40511
  // src/components/editor/LayersPanel.tsx
40153
- import { memo as memo39, useState as useState77, useCallback as useCallback93, useEffect as useEffect58, useRef as useRef83 } from "react";
40512
+ import { memo as memo39, useState as useState78, useCallback as useCallback94, useEffect as useEffect58, useRef as useRef84 } from "react";
40154
40513
 
40155
40514
  // src/components/editor/useLayerDrag.ts
40156
- import { useRef as useRef82, useState as useState76, useCallback as useCallback92 } from "react";
40515
+ import { useRef as useRef83, useState as useState77, useCallback as useCallback93 } from "react";
40157
40516
  var DRAG_THRESHOLD_PX2 = 4;
40158
40517
  function isLayerDraggable(layer) {
40159
40518
  if (!(layer.selector || layer.id)) return false;
@@ -40220,10 +40579,10 @@ function useLayerDrag({
40220
40579
  onReorder,
40221
40580
  onSingleSibling
40222
40581
  }) {
40223
- const dragRef = useRef82(null);
40224
- const [dragKey, setDragKey] = useState76(null);
40225
- const [insertionLineY, setInsertionLineY] = useState76(null);
40226
- const handleRowPointerDown = useCallback92(
40582
+ const dragRef = useRef83(null);
40583
+ const [dragKey, setDragKey] = useState77(null);
40584
+ const [insertionLineY, setInsertionLineY] = useState77(null);
40585
+ const handleRowPointerDown = useCallback93(
40227
40586
  (layerIndex, e) => {
40228
40587
  if (e.button !== 0) return;
40229
40588
  const layer = visibleLayers[layerIndex];
@@ -40250,7 +40609,7 @@ function useLayerDrag({
40250
40609
  },
40251
40610
  [visibleLayers, scrollContainerRef, onSingleSibling]
40252
40611
  );
40253
- const handleContainerPointerMove = useCallback92(
40612
+ const handleContainerPointerMove = useCallback93(
40254
40613
  (e) => {
40255
40614
  const drag = dragRef.current;
40256
40615
  if (!drag) return;
@@ -40276,7 +40635,7 @@ function useLayerDrag({
40276
40635
  },
40277
40636
  [visibleLayers, scrollContainerRef]
40278
40637
  );
40279
- const handleContainerPointerUp = useCallback92(() => {
40638
+ const handleContainerPointerUp = useCallback93(() => {
40280
40639
  const drag = dragRef.current;
40281
40640
  dragRef.current = null;
40282
40641
  setDragKey(null);
@@ -40348,12 +40707,12 @@ var LayersPanel = memo39(function LayersPanel2() {
40348
40707
  handleDomZIndexReorderCommit,
40349
40708
  setActiveGroupElement
40350
40709
  } = useDomEditContext();
40351
- const [layers, setLayers] = useState77([]);
40352
- const [collapsed, setCollapsed] = useState77({});
40353
- const prevDocVersionRef = useRef83(0);
40354
- const scrollContainerRef = useRef83(null);
40710
+ const [layers, setLayers] = useState78([]);
40711
+ const [collapsed, setCollapsed] = useState78({});
40712
+ const prevDocVersionRef = useRef84(0);
40713
+ const scrollContainerRef = useRef84(null);
40355
40714
  const isMasterView = !activeCompPath || activeCompPath === "index.html";
40356
- const collectLayers = useCallback93(() => {
40715
+ const collectLayers = useCallback94(() => {
40357
40716
  const iframe = previewIframeRef.current;
40358
40717
  if (!iframe) return;
40359
40718
  let doc = null;
@@ -40392,7 +40751,7 @@ var LayersPanel = memo39(function LayersPanel2() {
40392
40751
  return () => clearTimeout(timer);
40393
40752
  }
40394
40753
  }, [compositionLoading, collectLayers]);
40395
- const resolveSelection = useCallback93(
40754
+ const resolveSelection = useCallback94(
40396
40755
  (layer) => {
40397
40756
  let el = layer.element;
40398
40757
  if (!el.isConnected) {
@@ -40412,7 +40771,7 @@ var LayersPanel = memo39(function LayersPanel2() {
40412
40771
  },
40413
40772
  [activeCompPath, isMasterView, previewIframeRef, activeGroupElement]
40414
40773
  );
40415
- const seekToLayer = useCallback93(
40774
+ const seekToLayer = useCallback94(
40416
40775
  async (layer) => {
40417
40776
  const selection = await resolveSelection(layer);
40418
40777
  if (!selection) return;
@@ -40441,7 +40800,7 @@ var LayersPanel = memo39(function LayersPanel2() {
40441
40800
  },
40442
40801
  [currentTime, resolveSelection, timelineElements]
40443
40802
  );
40444
- const handleSelectLayer = useCallback93(
40803
+ const handleSelectLayer = useCallback94(
40445
40804
  async (layer) => {
40446
40805
  const selection = await resolveSelection(layer);
40447
40806
  if (!selection) return;
@@ -40450,7 +40809,7 @@ var LayersPanel = memo39(function LayersPanel2() {
40450
40809
  },
40451
40810
  [resolveSelection, applyDomSelection, seekToLayer]
40452
40811
  );
40453
- const handleLayerDoubleClick = useCallback93(
40812
+ const handleLayerDoubleClick = useCallback94(
40454
40813
  async (layer) => {
40455
40814
  const selection = await resolveSelection(layer);
40456
40815
  if (selection?.element.hasAttribute("data-hf-group")) {
@@ -40461,7 +40820,7 @@ var LayersPanel = memo39(function LayersPanel2() {
40461
40820
  },
40462
40821
  [resolveSelection, setActiveGroupElement, handleSelectLayer]
40463
40822
  );
40464
- const handleLayerHover = useCallback93(
40823
+ const handleLayerHover = useCallback94(
40465
40824
  async (layer) => {
40466
40825
  if (!layer) {
40467
40826
  updateDomEditHoverSelection(null);
@@ -40472,11 +40831,11 @@ var LayersPanel = memo39(function LayersPanel2() {
40472
40831
  },
40473
40832
  [resolveSelection, updateDomEditHoverSelection]
40474
40833
  );
40475
- const toggleCollapse = useCallback93((key, e) => {
40834
+ const toggleCollapse = useCallback94((key, e) => {
40476
40835
  e.stopPropagation();
40477
40836
  setCollapsed((prev) => ({ ...prev, [key]: !prev[key] }));
40478
40837
  }, []);
40479
- const handleReorder = useCallback93(
40838
+ const handleReorder = useCallback94(
40480
40839
  (event) => {
40481
40840
  const { siblingLayers, fromIndex, toIndex } = event;
40482
40841
  const reordered = [...siblingLayers];
@@ -40500,7 +40859,7 @@ var LayersPanel = memo39(function LayersPanel2() {
40500
40859
  );
40501
40860
  const selectedKey = domEditSelection ? getDomEditLayerKey(domEditSelection) : null;
40502
40861
  const visibleLayers = getVisibleLayers(layers, collapsed);
40503
- const handleSingleSibling = useCallback93(() => {
40862
+ const handleSingleSibling = useCallback94(() => {
40504
40863
  showToast("Only one layer at this level", "info");
40505
40864
  }, [showToast]);
40506
40865
  const {
@@ -40700,10 +41059,10 @@ function getVisibleLayers(layers, collapsed) {
40700
41059
  }
40701
41060
 
40702
41061
  // src/captions/components/CaptionPropertyPanel.tsx
40703
- import { memo as memo41, useCallback as useCallback95, useState as useState78 } from "react";
41062
+ import { memo as memo41, useCallback as useCallback96, useState as useState79 } from "react";
40704
41063
 
40705
41064
  // src/captions/components/CaptionAnimationPanel.tsx
40706
- import { memo as memo40, useCallback as useCallback94 } from "react";
41065
+ import { memo as memo40, useCallback as useCallback95 } from "react";
40707
41066
 
40708
41067
  // src/captions/components/shared.tsx
40709
41068
  import { jsx as jsx92, jsxs as jsxs77 } from "react/jsx-runtime";
@@ -40866,25 +41225,25 @@ var CaptionAnimationPanel = memo40(function CaptionAnimationPanel2() {
40866
41225
  }
40867
41226
  const group = resolvedGroupId ? model?.groups.get(resolvedGroupId) : void 0;
40868
41227
  const animation = group?.animation;
40869
- const handleEntranceChange = useCallback94(
41228
+ const handleEntranceChange = useCallback95(
40870
41229
  (update) => {
40871
41230
  if (resolvedGroupId) updateGroupAnimation(resolvedGroupId, "entrance", update);
40872
41231
  },
40873
41232
  [resolvedGroupId, updateGroupAnimation]
40874
41233
  );
40875
- const handleHighlightChange = useCallback94(
41234
+ const handleHighlightChange = useCallback95(
40876
41235
  (update) => {
40877
41236
  if (resolvedGroupId) updateGroupAnimation(resolvedGroupId, "highlight", update);
40878
41237
  },
40879
41238
  [resolvedGroupId, updateGroupAnimation]
40880
41239
  );
40881
- const handleExitChange = useCallback94(
41240
+ const handleExitChange = useCallback95(
40882
41241
  (update) => {
40883
41242
  if (resolvedGroupId) updateGroupAnimation(resolvedGroupId, "exit", update);
40884
41243
  },
40885
41244
  [resolvedGroupId, updateGroupAnimation]
40886
41245
  );
40887
- const handleApplyToAll = useCallback94(() => {
41246
+ const handleApplyToAll = useCallback95(() => {
40888
41247
  if (animation) applyAnimationToAll(animation);
40889
41248
  }, [animation, applyAnimationToAll]);
40890
41249
  if (!group || !resolvedGroupId || !animation) {
@@ -40943,7 +41302,7 @@ var CaptionPropertyPanel = memo41(function CaptionPropertyPanel2({
40943
41302
  const selectedGroupId = useCaptionStore((s) => s.selectedGroupId);
40944
41303
  const updateSelectedStyle = useCaptionStore((s) => s.updateSelectedStyle);
40945
41304
  const updateGroupStyle = useCaptionStore((s) => s.updateGroupStyle);
40946
- const [activeTab, setActiveTab] = useState78("style");
41305
+ const [activeTab, setActiveTab] = useState79("style");
40947
41306
  const firstSegmentId = selectedSegmentIds.size > 0 ? [...selectedSegmentIds][0] : void 0;
40948
41307
  const firstSegment = model?.segments.get(firstSegmentId ?? "");
40949
41308
  let ownerGroupId = null;
@@ -40962,7 +41321,7 @@ var CaptionPropertyPanel = memo41(function CaptionPropertyPanel2({
40962
41321
  ...groupStyle,
40963
41322
  ...segmentOverrides
40964
41323
  };
40965
- const applyToIframeDom = useCallback95(
41324
+ const applyToIframeDom = useCallback96(
40966
41325
  (updates) => {
40967
41326
  const iframe = iframeRef.current;
40968
41327
  if (!iframe || !model) return;
@@ -41036,7 +41395,7 @@ var CaptionPropertyPanel = memo41(function CaptionPropertyPanel2({
41036
41395
  },
41037
41396
  [iframeRef, model, selectedSegmentIds]
41038
41397
  );
41039
- const handleStyleChange = useCallback95(
41398
+ const handleStyleChange = useCallback96(
41040
41399
  (updates) => {
41041
41400
  if (selectedGroupId) {
41042
41401
  updateGroupStyle(selectedGroupId, updates);
@@ -41136,7 +41495,7 @@ var CaptionPropertyPanel = memo41(function CaptionPropertyPanel2({
41136
41495
  });
41137
41496
 
41138
41497
  // src/components/editor/BlockParamsPanel.tsx
41139
- import { memo as memo42, useState as useState79, useCallback as useCallback96 } from "react";
41498
+ import { memo as memo42, useState as useState80, useCallback as useCallback97 } from "react";
41140
41499
  import { jsx as jsx95, jsxs as jsxs80 } from "react/jsx-runtime";
41141
41500
  var BlockParamsPanel = memo42(function BlockParamsPanel2({
41142
41501
  blockTitle,
@@ -41144,14 +41503,14 @@ var BlockParamsPanel = memo42(function BlockParamsPanel2({
41144
41503
  compositionPath: _compositionPath,
41145
41504
  onClose
41146
41505
  }) {
41147
- const [values, setValues] = useState79(() => {
41506
+ const [values, setValues] = useState80(() => {
41148
41507
  const initial = {};
41149
41508
  for (const p of params) {
41150
41509
  initial[p.key] = p.default;
41151
41510
  }
41152
41511
  return initial;
41153
41512
  });
41154
- const handleChange = useCallback96((key, value) => {
41513
+ const handleChange = useCallback97((key, value) => {
41155
41514
  setValues((prev) => ({ ...prev, [key]: value }));
41156
41515
  }, []);
41157
41516
  return /* @__PURE__ */ jsxs80("div", { className: "flex flex-col h-full", children: [
@@ -41261,10 +41620,10 @@ function ParamControl({
41261
41620
  }
41262
41621
 
41263
41622
  // src/components/renders/RenderQueue.tsx
41264
- import { memo as memo44, useState as useState81, useRef as useRef84, useEffect as useEffect59 } from "react";
41623
+ import { memo as memo44, useState as useState82, useRef as useRef85, useEffect as useEffect59 } from "react";
41265
41624
 
41266
41625
  // src/components/renders/RenderQueueItem.tsx
41267
- import { memo as memo43, useCallback as useCallback97, useState as useState80 } from "react";
41626
+ import { memo as memo43, useCallback as useCallback98, useState as useState81 } from "react";
41268
41627
  import { Fragment as Fragment31, jsx as jsx96, jsxs as jsxs81 } from "react/jsx-runtime";
41269
41628
  function formatDuration(ms) {
41270
41629
  if (ms < 1e3) return `${ms}ms`;
@@ -41282,12 +41641,12 @@ var RenderQueueItem = memo43(function RenderQueueItem2({
41282
41641
  projectId,
41283
41642
  onDelete
41284
41643
  }) {
41285
- const [hovered, setHovered] = useState80(false);
41644
+ const [hovered, setHovered] = useState81(false);
41286
41645
  const fileSrc = `/api/projects/${projectId}/renders/file/${job.filename}`;
41287
- const handleOpen = useCallback97(() => {
41646
+ const handleOpen = useCallback98(() => {
41288
41647
  window.open(fileSrc, "_blank");
41289
41648
  }, [fileSrc]);
41290
- const handleDownload = useCallback97(
41649
+ const handleDownload = useCallback98(
41291
41650
  (e) => {
41292
41651
  e.stopPropagation();
41293
41652
  const a = document.createElement("a");
@@ -41475,8 +41834,8 @@ var FORMAT_INFO = {
41475
41834
  }
41476
41835
  };
41477
41836
  function FormatInfoTooltip({ format }) {
41478
- const [open, setOpen] = useState81(false);
41479
- const timeoutRef = useRef84(void 0);
41837
+ const [open, setOpen] = useState82(false);
41838
+ const timeoutRef = useRef85(void 0);
41480
41839
  const show = () => {
41481
41840
  clearTimeout(timeoutRef.current);
41482
41841
  setOpen(true);
@@ -41528,10 +41887,10 @@ function FormatExportButton({
41528
41887
  compositionDimensions
41529
41888
  }) {
41530
41889
  const persisted = getPersistedRenderSettings();
41531
- const [format, setFormat] = useState81(persisted.format);
41532
- const [quality, setQuality] = useState81(persisted.quality);
41533
- const [resolution, setResolution] = useState81("auto");
41534
- const [fps, setFps] = useState81(persisted.fps);
41890
+ const [format, setFormat] = useState82(persisted.format);
41891
+ const [quality, setQuality] = useState82(persisted.quality);
41892
+ const [resolution, setResolution] = useState82("auto");
41893
+ const [fps, setFps] = useState82(persisted.fps);
41535
41894
  const showQuality = format !== "mov";
41536
41895
  const selectCls = "h-7 w-full px-2 text-[11px] bg-panel-input rounded-md text-panel-text-1 outline-none cursor-pointer disabled:opacity-50 hover:bg-panel-hover transition-colors";
41537
41896
  return /* @__PURE__ */ jsxs82("div", { className: "flex flex-col gap-3", children: [
@@ -41643,7 +42002,7 @@ var RenderQueue = memo44(function RenderQueue2({
41643
42002
  isRendering,
41644
42003
  compositionDimensions
41645
42004
  }) {
41646
- const listRef = useRef84(null);
42005
+ const listRef = useRef85(null);
41647
42006
  useEffect59(() => {
41648
42007
  if (listRef.current) {
41649
42008
  listRef.current.scrollTo({ top: listRef.current.scrollHeight, behavior: "smooth" });
@@ -41726,11 +42085,11 @@ var RenderQueue = memo44(function RenderQueue2({
41726
42085
  });
41727
42086
 
41728
42087
  // src/components/panels/SlideshowPanel.tsx
41729
- import { useState as useState83, useEffect as useEffect60, useCallback as useCallback99, useRef as useRef85 } from "react";
42088
+ import { useState as useState84, useEffect as useEffect60, useCallback as useCallback100, useRef as useRef86 } from "react";
41730
42089
  import { parseSlideshowManifest } from "@hyperframes/core/slideshow";
41731
42090
 
41732
42091
  // src/components/panels/SlideshowSubPanels.tsx
41733
- import { useState as useState82, useCallback as useCallback98, useId } from "react";
42092
+ import { useState as useState83, useCallback as useCallback99, useId } from "react";
41734
42093
  import { jsx as jsx98, jsxs as jsxs83 } from "react/jsx-runtime";
41735
42094
  function SectionHeader({
41736
42095
  children,
@@ -41913,9 +42272,9 @@ function BranchTree({
41913
42272
  selectedSequenceId,
41914
42273
  onSelectBranchSlide
41915
42274
  }) {
41916
- const [newLabel, setNewLabel] = useState82("");
42275
+ const [newLabel, setNewLabel] = useState83("");
41917
42276
  const inputId = useId();
41918
- const handleCreate = useCallback98(() => {
42277
+ const handleCreate = useCallback99(() => {
41919
42278
  const label = newLabel.trim();
41920
42279
  if (!label) return;
41921
42280
  onCreateSequence(label);
@@ -41974,9 +42333,9 @@ function BranchItem({
41974
42333
  selectedSequenceId,
41975
42334
  onSelectBranchSlide
41976
42335
  }) {
41977
- const [editing, setEditing] = useState82(false);
41978
- const [draft, setDraft] = useState82(seq.label);
41979
- const commitRename = useCallback98(() => {
42336
+ const [editing, setEditing] = useState83(false);
42337
+ const [draft, setDraft] = useState83(seq.label);
42338
+ const commitRename = useCallback99(() => {
41980
42339
  const label = draft.trim();
41981
42340
  if (label && label !== seq.label) onRename(seq.id, label);
41982
42341
  setEditing(false);
@@ -42068,13 +42427,13 @@ function HotspotTool({
42068
42427
  onAddHotspot,
42069
42428
  onRemoveHotspot
42070
42429
  }) {
42071
- const [targetSequenceId, setTargetSequenceId] = useState82("");
42072
- const [hotspotLabel, setHotspotLabel] = useState82("");
42430
+ const [targetSequenceId, setTargetSequenceId] = useState83("");
42431
+ const [hotspotLabel, setHotspotLabel] = useState83("");
42073
42432
  const hotspots = slide?.hotspots ?? [];
42074
42433
  const selectedElementId = domEditSelection?.element?.id ?? null;
42075
42434
  const selectedHfId = domEditSelection?.hfId ?? null;
42076
42435
  const elementKey = selectedElementId || selectedHfId;
42077
- const handleMakeHotspot = useCallback98(() => {
42436
+ const handleMakeHotspot = useCallback99(() => {
42078
42437
  if (!selectedSceneId || !targetSequenceId || !elementKey) return;
42079
42438
  const id = `hotspot-${elementKey}-${generateId()}`;
42080
42439
  const label = hotspotLabel.trim() || elementKey;
@@ -42352,19 +42711,19 @@ function makeSlideshowNotesController() {
42352
42711
  function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42353
42712
  const { editingFile } = useFileManagerContext();
42354
42713
  const compHtml = editingFile?.content ?? null;
42355
- const [manifest, setManifest] = useState83(() => {
42714
+ const [manifest, setManifest] = useState84(() => {
42356
42715
  if (!compHtml) return { slides: [] };
42357
42716
  return safeParseManifest(compHtml);
42358
42717
  });
42359
- const [selectedSceneId, setSelectedSceneId] = useState83(null);
42360
- const [selectedSequenceId, setSelectedSequenceId] = useState83(null);
42361
- const [expandedSections, setExpandedSections] = useState83(
42718
+ const [selectedSceneId, setSelectedSceneId] = useState84(null);
42719
+ const [selectedSequenceId, setSelectedSequenceId] = useState84(null);
42720
+ const [expandedSections, setExpandedSections] = useState84(
42362
42721
  () => /* @__PURE__ */ new Set(["slides", "inspector"])
42363
42722
  );
42364
42723
  const currentTime = usePlayerStore((s) => s.currentTime);
42365
42724
  const { domEditSelection } = useDomEditSelectionContext();
42366
- const manifestRef = useRef85(manifest);
42367
- const notesCtrlRef = useRef85(makeSlideshowNotesController());
42725
+ const manifestRef = useRef86(manifest);
42726
+ const notesCtrlRef = useRef86(makeSlideshowNotesController());
42368
42727
  useEffect60(() => {
42369
42728
  if (!compHtml) {
42370
42729
  notesCtrlRef.current.flush();
@@ -42378,7 +42737,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42378
42737
  manifestRef.current = parsed;
42379
42738
  setSelectedSequenceId(null);
42380
42739
  }, [compHtml]);
42381
- const applyManifest = useCallback99(
42740
+ const applyManifest = useCallback100(
42382
42741
  async (next) => {
42383
42742
  const merged = notesCtrlRef.current.mergeIntoDiscrete(next);
42384
42743
  setManifest(merged);
@@ -42391,7 +42750,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42391
42750
  },
42392
42751
  [onPersist]
42393
42752
  );
42394
- const applyNotesManifest = useCallback99(
42753
+ const applyNotesManifest = useCallback100(
42395
42754
  (next) => {
42396
42755
  setManifest(next);
42397
42756
  manifestRef.current = next;
@@ -42405,7 +42764,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42405
42764
  ctrl.flush();
42406
42765
  };
42407
42766
  }, []);
42408
- const toggleSection = useCallback99((key) => {
42767
+ const toggleSection = useCallback100((key) => {
42409
42768
  setExpandedSections((prev) => {
42410
42769
  const next = new Set(prev);
42411
42770
  if (next.has(key)) {
@@ -42419,25 +42778,25 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42419
42778
  const activeSlides = selectedSequenceId ? (manifest.slideSequences ?? []).find((s) => s.id === selectedSequenceId)?.slides ?? [] : manifest.slides;
42420
42779
  const selectedSlide = activeSlides.find((s) => s.sceneId === selectedSceneId);
42421
42780
  const sequences = manifest.slideSequences ?? [];
42422
- const handleSelectBranchSlide = useCallback99((sequenceId, sceneId) => {
42781
+ const handleSelectBranchSlide = useCallback100((sequenceId, sceneId) => {
42423
42782
  setSelectedSceneId(sceneId);
42424
42783
  setSelectedSequenceId(sequenceId);
42425
42784
  }, []);
42426
- const handleToggleSlide = useCallback99(
42785
+ const handleToggleSlide = useCallback100(
42427
42786
  (sceneId) => {
42428
42787
  applyManifest(toggleMainLineSlide(manifestRef.current, sceneId)).catch(() => {
42429
42788
  });
42430
42789
  },
42431
42790
  [applyManifest]
42432
42791
  );
42433
- const handleReorder = useCallback99(
42792
+ const handleReorder = useCallback100(
42434
42793
  (sceneId, dir) => {
42435
42794
  applyManifest(reorderMainLineSlide(manifestRef.current, sceneId, dir)).catch(() => {
42436
42795
  });
42437
42796
  },
42438
42797
  [applyManifest]
42439
42798
  );
42440
- const handleSetNotes = useCallback99(
42799
+ const handleSetNotes = useCallback100(
42441
42800
  (notes) => {
42442
42801
  if (!selectedSceneId) return;
42443
42802
  applyNotesManifest(
@@ -42446,7 +42805,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42446
42805
  },
42447
42806
  [selectedSceneId, selectedSequenceId, applyNotesManifest]
42448
42807
  );
42449
- const handleMarkFragment = useCallback99(() => {
42808
+ const handleMarkFragment = useCallback100(() => {
42450
42809
  if (!selectedSceneId) return;
42451
42810
  applyManifest(
42452
42811
  addFragment(
@@ -42458,7 +42817,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42458
42817
  ).catch(() => {
42459
42818
  });
42460
42819
  }, [selectedSceneId, selectedSequenceId, currentTime, applyManifest]);
42461
- const handleRemoveFragment = useCallback99(
42820
+ const handleRemoveFragment = useCallback100(
42462
42821
  (time) => {
42463
42822
  if (!selectedSceneId) return;
42464
42823
  applyManifest(
@@ -42468,7 +42827,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42468
42827
  },
42469
42828
  [selectedSceneId, selectedSequenceId, applyManifest]
42470
42829
  );
42471
- const handleCreateSequence = useCallback99(
42830
+ const handleCreateSequence = useCallback100(
42472
42831
  (label) => {
42473
42832
  const id = `seq-${generateId()}`;
42474
42833
  applyManifest(createSequence(manifestRef.current, id, label)).catch(() => {
@@ -42476,14 +42835,14 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42476
42835
  },
42477
42836
  [applyManifest]
42478
42837
  );
42479
- const handleRenameSequence = useCallback99(
42838
+ const handleRenameSequence = useCallback100(
42480
42839
  (id, label) => {
42481
42840
  applyManifest(renameSequence(manifestRef.current, id, label)).catch(() => {
42482
42841
  });
42483
42842
  },
42484
42843
  [applyManifest]
42485
42844
  );
42486
- const handleDeleteSequence = useCallback99(
42845
+ const handleDeleteSequence = useCallback100(
42487
42846
  (id) => {
42488
42847
  const seq = (manifestRef.current.slideSequences ?? []).find((s) => s.id === id);
42489
42848
  const count = seq?.slides.length ?? 0;
@@ -42497,7 +42856,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42497
42856
  },
42498
42857
  [applyManifest]
42499
42858
  );
42500
- const handleAssign = useCallback99(
42859
+ const handleAssign = useCallback100(
42501
42860
  (sequenceId, sceneId, assign) => {
42502
42861
  applyManifest(assignToBranch(manifestRef.current, sequenceId, sceneId, assign)).catch(
42503
42862
  () => {
@@ -42506,7 +42865,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42506
42865
  },
42507
42866
  [applyManifest]
42508
42867
  );
42509
- const handleAddHotspot = useCallback99(
42868
+ const handleAddHotspot = useCallback100(
42510
42869
  (sceneId, hotspot) => {
42511
42870
  applyManifest(
42512
42871
  addHotspot(manifestRef.current, sceneId, hotspot, selectedSequenceId ?? void 0)
@@ -42515,7 +42874,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42515
42874
  },
42516
42875
  [selectedSequenceId, applyManifest]
42517
42876
  );
42518
- const handleRemoveHotspot = useCallback99(
42877
+ const handleRemoveHotspot = useCallback100(
42519
42878
  (sceneId, hotspotId) => {
42520
42879
  applyManifest(
42521
42880
  removeHotspot(manifestRef.current, sceneId, hotspotId, selectedSequenceId ?? void 0)
@@ -42619,7 +42978,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42619
42978
  }
42620
42979
 
42621
42980
  // src/hooks/useSlideshowPersist.ts
42622
- import { useCallback as useCallback100 } from "react";
42981
+ import { useCallback as useCallback101 } from "react";
42623
42982
 
42624
42983
  // src/utils/setSlideshowManifest.ts
42625
42984
  import {
@@ -42676,7 +43035,7 @@ function useSlideshowPersist({
42676
43035
  domEditSaveTimestampRef,
42677
43036
  coalesceKey
42678
43037
  }) {
42679
- return useCallback100(
43038
+ return useCallback101(
42680
43039
  async (manifest) => {
42681
43040
  if (!sdkSession) return;
42682
43041
  const path = activeCompPath ?? "index.html";
@@ -42812,9 +43171,9 @@ function StudioRightPanel({
42812
43171
  domEditSaveTimestampRef,
42813
43172
  coalesceKey: activeCompPath ? `slideshow-notes:${activeCompPath}` : "slideshow-notes"
42814
43173
  });
42815
- const [layersPanePercent, setLayersPanePercent] = useState84(40);
42816
- const splitContainerRef = useRef86(null);
42817
- const splitDragRef = useRef86(null);
43174
+ const [layersPanePercent, setLayersPanePercent] = useState85(40);
43175
+ const splitContainerRef = useRef87(null);
43176
+ const splitDragRef = useRef87(null);
42818
43177
  const renderJobs = renderQueue.jobs;
42819
43178
  const inspectorTabActive = rightPanelTab === "design" || rightPanelTab === "layers";
42820
43179
  const slideshowScenes = useMemo34(() => {
@@ -42839,7 +43198,7 @@ function StudioRightPanel({
42839
43198
  }
42840
43199
  toggleRightInspectorPane(pane);
42841
43200
  };
42842
- const handleInspectorSplitResizeStart = useCallback101(
43201
+ const handleInspectorSplitResizeStart = useCallback102(
42843
43202
  (event) => {
42844
43203
  event.preventDefault();
42845
43204
  event.currentTarget.setPointerCapture(event.pointerId);
@@ -42852,7 +43211,7 @@ function StudioRightPanel({
42852
43211
  },
42853
43212
  [layersPanePercent]
42854
43213
  );
42855
- const handleInspectorSplitResizeMove = useCallback101((event) => {
43214
+ const handleInspectorSplitResizeMove = useCallback102((event) => {
42856
43215
  const drag = splitDragRef.current;
42857
43216
  if (!drag || drag.height <= 0) return;
42858
43217
  const deltaPercent = (event.clientY - drag.startY) / drag.height * 100;
@@ -42862,7 +43221,7 @@ function StudioRightPanel({
42862
43221
  );
42863
43222
  setLayersPanePercent(next);
42864
43223
  }, []);
42865
- const handleInspectorSplitResizeEnd = useCallback101(() => {
43224
+ const handleInspectorSplitResizeEnd = useCallback102(() => {
42866
43225
  splitDragRef.current = null;
42867
43226
  }, []);
42868
43227
  const propertyPanel = /* @__PURE__ */ jsx100(
@@ -43050,10 +43409,10 @@ function StudioRightPanel({
43050
43409
  }
43051
43410
 
43052
43411
  // src/components/TimelineToolbar.tsx
43053
- import { useRef as useRef87 } from "react";
43412
+ import { useRef as useRef88 } from "react";
43054
43413
 
43055
43414
  // src/hooks/useEnableKeyframes.ts
43056
- import { useCallback as useCallback102 } from "react";
43415
+ import { useCallback as useCallback103 } from "react";
43057
43416
  function animatedProps(anim) {
43058
43417
  if (!anim) return ["x", "y"];
43059
43418
  const own = Object.keys(anim.properties ?? {});
@@ -43094,17 +43453,17 @@ function buildExtendedKeyframes(anim, currentTime, position) {
43094
43453
  function readElementPosition(iframe, sel, anim) {
43095
43454
  const result = {};
43096
43455
  if (!iframe?.contentWindow) return result;
43097
- let gsap;
43456
+ let gsap2;
43098
43457
  try {
43099
- gsap = iframe.contentWindow.gsap;
43458
+ gsap2 = iframe.contentWindow.gsap;
43100
43459
  } catch {
43101
43460
  return result;
43102
43461
  }
43103
43462
  const element = sel.element;
43104
- if (!element?.isConnected || !gsap?.getProperty) return result;
43463
+ if (!element?.isConnected || !gsap2?.getProperty) return result;
43105
43464
  const props = animatedProps(anim);
43106
43465
  for (const prop of props) {
43107
- const val = Number(gsap.getProperty(element, prop));
43466
+ const val = Number(gsap2.getProperty(element, prop));
43108
43467
  if (!Number.isFinite(val)) continue;
43109
43468
  result[prop] = POSITION_PROPS.has(prop) ? Math.round(val) : roundTo3(val);
43110
43469
  }
@@ -43263,7 +43622,7 @@ async function applyArcWaypointAtPlayhead(session, sel, arcAnim, t, iframe) {
43263
43622
  );
43264
43623
  }
43265
43624
  function useEnableKeyframes(sessionRef) {
43266
- return useCallback102(async () => {
43625
+ return useCallback103(async () => {
43267
43626
  const session = sessionRef.current;
43268
43627
  if (!session) return;
43269
43628
  const sel = session.domEditSelection;
@@ -43321,11 +43680,87 @@ function useEnableKeyframes(sessionRef) {
43321
43680
  }, [sessionRef]);
43322
43681
  }
43323
43682
 
43683
+ // src/hooks/useKeyframeKeyboard.ts
43684
+ import { useEffect as useEffect61, useCallback as useCallback104 } from "react";
43685
+ function isTextInput(el) {
43686
+ if (!el) return false;
43687
+ const tag = el.tagName;
43688
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
43689
+ return el.isContentEditable === true;
43690
+ }
43691
+ function useKeyframeKeyboard({
43692
+ enabled,
43693
+ onAddKeyframe,
43694
+ onDeleteKeyframe,
43695
+ onPrevKeyframe,
43696
+ onNextKeyframe,
43697
+ onToggleHold,
43698
+ onToggleExpand,
43699
+ onNudgeKeyframe
43700
+ }) {
43701
+ const handler = useCallback104(
43702
+ (e) => {
43703
+ if (!enabled) return;
43704
+ if (isTextInput(document.activeElement)) return;
43705
+ if (e.metaKey || e.ctrlKey) return;
43706
+ const hasSelectedKeyframes = usePlayerStore.getState().selectedKeyframes.size > 0;
43707
+ const consume = (run) => {
43708
+ e.preventDefault();
43709
+ e.stopImmediatePropagation();
43710
+ run();
43711
+ };
43712
+ switch (e.key.toLowerCase()) {
43713
+ case "k":
43714
+ if (onAddKeyframe) consume(onAddKeyframe);
43715
+ break;
43716
+ case "delete":
43717
+ case "backspace":
43718
+ if (onDeleteKeyframe && hasSelectedKeyframes) consume(onDeleteKeyframe);
43719
+ break;
43720
+ case "j": {
43721
+ const nav = e.shiftKey ? onNextKeyframe : onPrevKeyframe;
43722
+ if (nav) consume(nav);
43723
+ break;
43724
+ }
43725
+ case "h":
43726
+ if (onToggleHold && hasSelectedKeyframes) consume(onToggleHold);
43727
+ break;
43728
+ case "u":
43729
+ if (onToggleExpand) consume(onToggleExpand);
43730
+ break;
43731
+ case "arrowleft":
43732
+ if (onNudgeKeyframe && hasSelectedKeyframes && !e.altKey)
43733
+ consume(() => onNudgeKeyframe(-1, e.shiftKey));
43734
+ break;
43735
+ case "arrowright":
43736
+ if (onNudgeKeyframe && hasSelectedKeyframes && !e.altKey)
43737
+ consume(() => onNudgeKeyframe(1, e.shiftKey));
43738
+ break;
43739
+ }
43740
+ },
43741
+ [
43742
+ enabled,
43743
+ onAddKeyframe,
43744
+ onDeleteKeyframe,
43745
+ onPrevKeyframe,
43746
+ onNextKeyframe,
43747
+ onToggleHold,
43748
+ onToggleExpand,
43749
+ onNudgeKeyframe
43750
+ ]
43751
+ );
43752
+ useEffect61(() => {
43753
+ if (!enabled) return;
43754
+ window.addEventListener("keydown", handler, { capture: true });
43755
+ return () => window.removeEventListener("keydown", handler, { capture: true });
43756
+ }, [enabled, handler]);
43757
+ }
43758
+
43324
43759
  // src/components/TimelineToolbar.tsx
43325
43760
  import { Fragment as Fragment34, jsx as jsx101, jsxs as jsxs86 } from "react/jsx-runtime";
43326
43761
  function useKeyframeToggle(session) {
43327
43762
  const currentTime = usePlayerStore((s) => s.currentTime);
43328
- const sessionRef = useRef87(session);
43763
+ const sessionRef = useRef88(session);
43329
43764
  sessionRef.current = session;
43330
43765
  const onToggle = useEnableKeyframes(
43331
43766
  sessionRef
@@ -43363,6 +43798,10 @@ function TimelineToolbar({
43363
43798
  willExtend: keyframeWillExtend,
43364
43799
  onToggle: onToggleKeyframe
43365
43800
  } = useKeyframeToggle(domEditSession);
43801
+ useKeyframeKeyboard({
43802
+ enabled: STUDIO_KEYFRAMES_ENABLED && Boolean(onToggleKeyframe),
43803
+ onAddKeyframe: onToggleKeyframe
43804
+ });
43366
43805
  return /* @__PURE__ */ jsx101("div", { className: "border-b border-neutral-800/40 bg-neutral-950/96", children: /* @__PURE__ */ jsxs86("div", { className: "flex items-center justify-between px-3 py-2", children: [
43367
43806
  /* @__PURE__ */ jsxs86("div", { className: "flex items-center gap-3", children: [
43368
43807
  /* @__PURE__ */ jsx101("div", { className: "text-[10px] font-medium uppercase tracking-[0.16em] text-neutral-500", children: "Timeline" }),
@@ -43515,14 +43954,14 @@ function TimelineToolbar({
43515
43954
  }
43516
43955
 
43517
43956
  // src/hooks/useStoryboard.ts
43518
- import { useCallback as useCallback103, useEffect as useEffect61, useState as useState85 } from "react";
43957
+ import { useCallback as useCallback105, useEffect as useEffect62, useState as useState86 } from "react";
43519
43958
  function useStoryboard(projectId) {
43520
- const [data, setData] = useState85(null);
43521
- const [loading, setLoading] = useState85(true);
43522
- const [error, setError] = useState85(null);
43523
- const [reloadKey, setReloadKey] = useState85(0);
43524
- const reload = useCallback103(() => setReloadKey((k) => k + 1), []);
43525
- useEffect61(() => {
43959
+ const [data, setData] = useState86(null);
43960
+ const [loading, setLoading] = useState86(true);
43961
+ const [error, setError] = useState86(null);
43962
+ const [reloadKey, setReloadKey] = useState86(0);
43963
+ const reload = useCallback105(() => setReloadKey((k) => k + 1), []);
43964
+ useEffect62(() => {
43526
43965
  if (!projectId) return;
43527
43966
  let cancelled = false;
43528
43967
  setLoading(true);
@@ -43545,7 +43984,7 @@ function useStoryboard(projectId) {
43545
43984
  }
43546
43985
 
43547
43986
  // src/components/storyboard/StoryboardLoaded.tsx
43548
- import { useMemo as useMemo36, useState as useState89 } from "react";
43987
+ import { useMemo as useMemo36, useState as useState90 } from "react";
43549
43988
 
43550
43989
  // src/components/storyboard/StoryboardDirection.tsx
43551
43990
  import { jsx as jsx102, jsxs as jsxs87 } from "react/jsx-runtime";
@@ -43568,11 +44007,11 @@ function StoryboardDirection({ globals, frameCount }) {
43568
44007
  }
43569
44008
 
43570
44009
  // src/components/storyboard/FramePoster.tsx
43571
- import { useEffect as useEffect62, useState as useState86 } from "react";
44010
+ import { useEffect as useEffect63, useState as useState87 } from "react";
43572
44011
  import { jsx as jsx103 } from "react/jsx-runtime";
43573
44012
  function FramePoster({ projectId, src, seconds, title, fit = "cover" }) {
43574
- const [failed2, setFailed] = useState86(false);
43575
- useEffect62(() => setFailed(false), [src, seconds]);
44013
+ const [failed2, setFailed] = useState87(false);
44014
+ useEffect63(() => setFailed(false), [src, seconds]);
43576
44015
  if (failed2) {
43577
44016
  return /* @__PURE__ */ jsx103("div", { className: "flex h-full w-full items-center justify-center text-[11px] text-neutral-600", children: "Preview unavailable" });
43578
44017
  }
@@ -43745,19 +44184,19 @@ function StoryboardScriptPanel({ script }) {
43745
44184
  }
43746
44185
 
43747
44186
  // src/components/storyboard/StoryboardSourceEditor.tsx
43748
- import { useCallback as useCallback104, useEffect as useEffect63, useMemo as useMemo35, useRef as useRef88, useState as useState87 } from "react";
44187
+ import { useCallback as useCallback106, useEffect as useEffect64, useMemo as useMemo35, useRef as useRef89, useState as useState88 } from "react";
43749
44188
  import { marked } from "marked";
43750
44189
  import DOMPurify from "dompurify";
43751
44190
  import { jsx as jsx108, jsxs as jsxs91 } from "react/jsx-runtime";
43752
44191
  var DISCARD_PROMPT = "Discard unsaved markdown changes?";
43753
44192
  function useEditableFile(path, onSaved) {
43754
44193
  const { readProjectFile, writeProjectFile } = useFileManagerContext();
43755
- const [content, setContent] = useState87("");
43756
- const [saved, setSaved] = useState87("");
43757
- const [loading, setLoading] = useState87(true);
43758
- const [saving, setSaving] = useState87(false);
43759
- const [error, setError] = useState87(null);
43760
- useEffect63(() => {
44194
+ const [content, setContent] = useState88("");
44195
+ const [saved, setSaved] = useState88("");
44196
+ const [loading, setLoading] = useState88(true);
44197
+ const [saving, setSaving] = useState88(false);
44198
+ const [error, setError] = useState88(null);
44199
+ useEffect64(() => {
43761
44200
  if (!path) return;
43762
44201
  let cancelled = false;
43763
44202
  setLoading(true);
@@ -43775,7 +44214,7 @@ function useEditableFile(path, onSaved) {
43775
44214
  cancelled = true;
43776
44215
  };
43777
44216
  }, [path, readProjectFile]);
43778
- const save = useCallback104(() => {
44217
+ const save = useCallback106(() => {
43779
44218
  if (saving) return;
43780
44219
  setSaving(true);
43781
44220
  setError(null);
@@ -43793,9 +44232,9 @@ function hardenLinks(node) {
43793
44232
  }
43794
44233
  }
43795
44234
  function useMarkdownPreview(source) {
43796
- const [debounced, setDebounced] = useState87(source);
43797
- const primed = useRef88(false);
43798
- useEffect63(() => {
44235
+ const [debounced, setDebounced] = useState88(source);
44236
+ const primed = useRef89(false);
44237
+ useEffect64(() => {
43799
44238
  if (!primed.current && source !== "") {
43800
44239
  primed.current = true;
43801
44240
  setDebounced(source);
@@ -43823,12 +44262,12 @@ function StoryboardSourceEditor({
43823
44262
  onSaved,
43824
44263
  onDirtyChange
43825
44264
  }) {
43826
- const [selected, setSelected] = useState87(files[0]?.path ?? "");
44265
+ const [selected, setSelected] = useState88(files[0]?.path ?? "");
43827
44266
  const activePath = files.some((f) => f.path === selected) ? selected : files[0]?.path ?? "";
43828
44267
  const file = useEditableFile(activePath, onSaved);
43829
44268
  const previewHtml = useMarkdownPreview(file.content);
43830
- useEffect63(() => onDirtyChange?.(file.dirty), [onDirtyChange, file.dirty]);
43831
- useEffect63(() => {
44269
+ useEffect64(() => onDirtyChange?.(file.dirty), [onDirtyChange, file.dirty]);
44270
+ useEffect64(() => {
43832
44271
  if (!file.dirty) return;
43833
44272
  const onBeforeUnload = (event) => {
43834
44273
  event.preventDefault();
@@ -43900,7 +44339,7 @@ function StoryboardSourceEditor({
43900
44339
  }
43901
44340
 
43902
44341
  // src/components/storyboard/StoryboardFrameFocus.tsx
43903
- import { useCallback as useCallback105, useEffect as useEffect64, useState as useState88 } from "react";
44342
+ import { useCallback as useCallback107, useEffect as useEffect65, useState as useState89 } from "react";
43904
44343
  import { setFrameStatus, setFrameVoiceover } from "@hyperframes/core/storyboard";
43905
44344
  import { jsx as jsx109, jsxs as jsxs92 } from "react/jsx-runtime";
43906
44345
  function StoryboardFrameFocus({
@@ -43915,10 +44354,10 @@ function StoryboardFrameFocus({
43915
44354
  }) {
43916
44355
  const { readProjectFile, writeProjectFile } = useFileManagerContext();
43917
44356
  const { setViewMode } = useViewMode();
43918
- const [draft, setDraft] = useState88(frame.voiceover ?? "");
43919
- const [busy, setBusy] = useState88(false);
43920
- const [error, setError] = useState88(null);
43921
- const applyEdit = useCallback105(
44357
+ const [draft, setDraft] = useState89(frame.voiceover ?? "");
44358
+ const [busy, setBusy] = useState89(false);
44359
+ const [error, setError] = useState89(null);
44360
+ const applyEdit = useCallback107(
43922
44361
  async (edit) => {
43923
44362
  if (busy) return;
43924
44363
  setBusy(true);
@@ -43945,7 +44384,7 @@ function StoryboardFrameFocus({
43945
44384
  const handleNavigate = (delta) => {
43946
44385
  if (confirmLeave()) onNavigate(delta);
43947
44386
  };
43948
- useEffect64(() => {
44387
+ useEffect65(() => {
43949
44388
  const onKey = (e) => {
43950
44389
  const el = document.activeElement;
43951
44390
  if (el instanceof HTMLTextAreaElement || el instanceof HTMLInputElement) return;
@@ -44124,9 +44563,9 @@ function StoryboardLoaded({
44124
44563
  reload,
44125
44564
  onSelectComposition
44126
44565
  }) {
44127
- const [subView, setSubView] = useState89("board");
44128
- const [sourceDirty, setSourceDirty] = useState89(false);
44129
- const [focusedIndex, setFocusedIndex] = useState89(null);
44566
+ const [subView, setSubView] = useState90("board");
44567
+ const [sourceDirty, setSourceDirty] = useState90(false);
44568
+ const [focusedIndex, setFocusedIndex] = useState90(null);
44130
44569
  const sourceFiles = useMemo36(() => {
44131
44570
  const files = [{ path: data.path, label: data.path }];
44132
44571
  if (data.script?.exists) files.push({ path: data.script.path, label: data.script.path });
@@ -44261,11 +44700,11 @@ function StudioSplash({ waiting }) {
44261
44700
  }
44262
44701
 
44263
44702
  // src/hooks/useServerConnection.ts
44264
- import { useEffect as useEffect65, useState as useState90 } from "react";
44703
+ import { useEffect as useEffect66, useState as useState91 } from "react";
44265
44704
  function useServerConnection() {
44266
- const [projectId, setProjectId] = useState90(null);
44267
- const [resolving, setResolving] = useState90(true);
44268
- const [waitingForServer, setWaitingForServer] = useState90(false);
44705
+ const [projectId, setProjectId] = useState91(null);
44706
+ const [resolving, setResolving] = useState91(true);
44707
+ const [waitingForServer, setWaitingForServer] = useState91(false);
44269
44708
  useMountEffect(() => {
44270
44709
  const hashProjectId = parseProjectIdFromHash(window.location.hash);
44271
44710
  let cancelled = false;
@@ -44302,7 +44741,7 @@ function useServerConnection() {
44302
44741
  if (retryTimer !== null) clearTimeout(retryTimer);
44303
44742
  };
44304
44743
  });
44305
- useEffect65(() => {
44744
+ useEffect66(() => {
44306
44745
  const onHashChange = () => {
44307
44746
  const next = parseProjectIdFromHash(window.location.hash);
44308
44747
  if (next && next !== projectId) setProjectId(next);
@@ -44317,28 +44756,28 @@ function useServerConnection() {
44317
44756
  import { jsx as jsx113, jsxs as jsxs96 } from "react/jsx-runtime";
44318
44757
  function StudioApp() {
44319
44758
  const { projectId, resolving, waitingForServer } = useServerConnection();
44320
- const initialUrlStateRef = useRef89(readStudioUrlStateFromWindow());
44759
+ const initialUrlStateRef = useRef90(readStudioUrlStateFromWindow());
44321
44760
  const viewModeValue = useViewModeState(STUDIO_STORYBOARD_ENABLED);
44322
- useEffect66(() => {
44761
+ useEffect67(() => {
44323
44762
  if (resolving || waitingForServer) return;
44324
44763
  if (hasFiredSessionStart()) return;
44325
44764
  markSessionStartFired();
44326
44765
  trackStudioSessionStart({ has_project: projectId != null });
44327
44766
  }, [projectId, resolving, waitingForServer]);
44328
- const [activeCompPath, setActiveCompPath] = useState91(null);
44329
- const [activeCompPathHydrated, setActiveCompPathHydrated] = useState91(
44767
+ const [activeCompPath, setActiveCompPath] = useState92(null);
44768
+ const [activeCompPathHydrated, setActiveCompPathHydrated] = useState92(
44330
44769
  () => initialUrlStateRef.current.activeCompPath == null
44331
44770
  );
44332
- const [compIdToSrc, setCompIdToSrc] = useState91(/* @__PURE__ */ new Map());
44333
- const [previewIframe, setPreviewIframe] = useState91(null);
44334
- const [compositionLoading, setCompositionLoading] = useState91(true);
44335
- const [refreshKey, setRefreshKey] = useState91(0);
44336
- const [previewDocumentVersion, setPreviewDocumentVersion] = useState91(0);
44337
- const [blockPreview, setBlockPreview] = useState91(null);
44338
- const previewIframeRef = useRef89(null);
44339
- const activeCompPathRef = useRef89(activeCompPath);
44771
+ const [compIdToSrc, setCompIdToSrc] = useState92(/* @__PURE__ */ new Map());
44772
+ const [previewIframe, setPreviewIframe] = useState92(null);
44773
+ const [compositionLoading, setCompositionLoading] = useState92(true);
44774
+ const [refreshKey, setRefreshKey] = useState92(0);
44775
+ const [previewDocumentVersion, setPreviewDocumentVersion] = useState92(0);
44776
+ const [blockPreview, setBlockPreview] = useState92(null);
44777
+ const previewIframeRef = useRef90(null);
44778
+ const activeCompPathRef = useRef90(activeCompPath);
44340
44779
  activeCompPathRef.current = activeCompPath;
44341
- const leftSidebarRef = useRef89(null);
44780
+ const leftSidebarRef = useRef90(null);
44342
44781
  const renderQueue = useRenderQueue(projectId);
44343
44782
  const captionEditMode = useCaptionStore((s) => s.isEditMode);
44344
44783
  const captionHasSelection = useCaptionStore((s) => s.selectedSegmentIds.size > 0);
@@ -44353,8 +44792,8 @@ function StudioApp() {
44353
44792
  const maxEnd = timelineElements.length > 0 ? Math.max(...timelineElements.map((el) => el.start + el.duration)) : 0;
44354
44793
  return Math.max(timelineDuration, maxEnd);
44355
44794
  }, [timelineDuration, timelineElements]);
44356
- const refreshTimersRef = useRef89([]);
44357
- const refreshPreviewDocumentVersion = useCallback106(() => {
44795
+ const refreshTimersRef = useRef90([]);
44796
+ const refreshPreviewDocumentVersion = useCallback108(() => {
44358
44797
  for (const id of refreshTimersRef.current) clearTimeout(id);
44359
44798
  refreshTimersRef.current = [];
44360
44799
  setPreviewDocumentVersion((v) => v + 1);
@@ -44363,16 +44802,16 @@ function StudioApp() {
44363
44802
  window.setTimeout(() => setPreviewDocumentVersion((v) => v + 1), 300)
44364
44803
  );
44365
44804
  }, []);
44366
- useEffect66(
44805
+ useEffect67(
44367
44806
  () => () => {
44368
44807
  for (const id of refreshTimersRef.current) clearTimeout(id);
44369
44808
  },
44370
44809
  []
44371
44810
  );
44372
- const [timelineVisible, setTimelineVisible] = useState91(
44811
+ const [timelineVisible, setTimelineVisible] = useState92(
44373
44812
  () => initialUrlStateRef.current.timelineVisible ?? readStudioUiPreferences().timelineVisible ?? true
44374
44813
  );
44375
- const toggleTimelineVisibility = useCallback106(() => {
44814
+ const toggleTimelineVisibility = useCallback108(() => {
44376
44815
  setTimelineVisible((v) => {
44377
44816
  writeStudioUiPreferences({ timelineVisible: !v });
44378
44817
  return !v;
@@ -44384,10 +44823,10 @@ function StudioApp() {
44384
44823
  rightPanelTab: initialUrlStateRef.current.rightPanelTab
44385
44824
  });
44386
44825
  const editHistory = usePersistentEditHistory({ projectId });
44387
- const domEditSaveTimestampRef = useRef89(0);
44388
- const pendingTimelineEditPathRef = useRef89(/* @__PURE__ */ new Set());
44389
- const isGestureRecordingRef = useRef89(false);
44390
- const reloadPreview = useCallback106(() => setRefreshKey((k) => k + 1), []);
44826
+ const domEditSaveTimestampRef = useRef90(0);
44827
+ const pendingTimelineEditPathRef = useRef90(/* @__PURE__ */ new Set());
44828
+ const isGestureRecordingRef = useRef90(false);
44829
+ const reloadPreview = useCallback108(() => setRefreshKey((k) => k + 1), []);
44391
44830
  const fileManager = useFileManager({
44392
44831
  projectId,
44393
44832
  showToast,
@@ -44396,7 +44835,7 @@ function StudioApp() {
44396
44835
  setRefreshKey
44397
44836
  });
44398
44837
  const sdkHandle = useSdkSession(projectId, activeCompPath, domEditSaveTimestampRef);
44399
- useEffect66(() => {
44838
+ useEffect67(() => {
44400
44839
  if (activeCompPathHydrated) return;
44401
44840
  if (!fileManager.fileTreeLoaded) return;
44402
44841
  const nextCompPath = normalizeStudioCompositionPath(
@@ -44456,18 +44895,18 @@ function StudioApp() {
44456
44895
  setRightCollapsed: panelLayout.setRightCollapsed,
44457
44896
  setRightPanelTab: panelLayout.setRightPanelTab
44458
44897
  });
44459
- const clearDomSelectionRef = useRef89(() => {
44898
+ const clearDomSelectionRef = useRef90(() => {
44460
44899
  });
44461
- const domEditSelectionBridgeRef = useRef89(null);
44462
- const handleDomEditElementDeleteRef = useRef89(
44900
+ const domEditSelectionBridgeRef = useRef90(null);
44901
+ const handleDomEditElementDeleteRef = useRef90(
44463
44902
  async () => {
44464
44903
  }
44465
44904
  );
44466
44905
  const domEditDeleteBridge = (s) => handleDomEditElementDeleteRef.current(s);
44467
- const resetKeyframesRef = useRef89(() => false);
44468
- const deleteSelectedKeyframesRef = useRef89(() => {
44906
+ const resetKeyframesRef = useRef90(() => false);
44907
+ const deleteSelectedKeyframesRef = useRef90(() => {
44469
44908
  });
44470
- const invalidateGsapCacheRef = useRef89(() => {
44909
+ const invalidateGsapCacheRef = useRef90(() => {
44471
44910
  });
44472
44911
  const { handleCopy, handlePaste, handleCut } = useClipboard({
44473
44912
  projectId,
@@ -44510,7 +44949,7 @@ function StudioApp() {
44510
44949
  forceReloadSdkSession: sdkHandle.forceReload,
44511
44950
  onToggleRecording: STUDIO_KEYFRAMES_ENABLED ? () => handleToggleRecordingRef.current() : void 0
44512
44951
  });
44513
- const sidebarTabRef = useRef89({
44952
+ const sidebarTabRef = useRef90({
44514
44953
  select: (t) => leftSidebarRef.current?.selectTab(t),
44515
44954
  get: () => leftSidebarRef.current?.getTab() ?? "compositions"
44516
44955
  });
@@ -44603,9 +45042,9 @@ function StudioApp() {
44603
45042
  resetErrors: resetConsoleErrors
44604
45043
  } = useConsoleErrorCapture(previewIframe);
44605
45044
  const dragOverlay = useDragOverlay(fileManager.handleImportFiles);
44606
- const handleToggleRecordingRef = useRef89(() => {
45045
+ const handleToggleRecordingRef = useRef90(() => {
44607
45046
  });
44608
- const domEditSessionRef = useRef89(domEditSession);
45047
+ const domEditSessionRef = useRef90(domEditSession);
44609
45048
  domEditSessionRef.current = domEditSession;
44610
45049
  const { gestureState, gestureRecording, handleToggleRecording } = useGestureCommit({
44611
45050
  domEditSessionRef,
@@ -44614,7 +45053,7 @@ function StudioApp() {
44614
45053
  isGestureRecordingRef
44615
45054
  });
44616
45055
  handleToggleRecordingRef.current = handleToggleRecording;
44617
- const canvasRectRef = useRef89(null);
45056
+ const canvasRectRef = useRef90(null);
44618
45057
  useLayoutEffect3(() => {
44619
45058
  if (gestureState !== "recording" || !previewIframe) {
44620
45059
  canvasRectRef.current = null;
@@ -44623,7 +45062,7 @@ function StudioApp() {
44623
45062
  const r = previewIframe.getBoundingClientRect();
44624
45063
  canvasRectRef.current = { left: r.left, top: r.top, width: r.width, height: r.height };
44625
45064
  }, [gestureState, previewIframe]);
44626
- const handlePreviewIframeRef = useCallback106(
45065
+ const handlePreviewIframeRef = useCallback108(
44627
45066
  (iframe) => {
44628
45067
  previewIframeRef.current = iframe;
44629
45068
  setPreviewIframe(iframe);
@@ -44634,7 +45073,7 @@ function StudioApp() {
44634
45073
  },
44635
45074
  [appHotkeys, resetConsoleErrors, refreshPreviewDocumentVersion]
44636
45075
  );
44637
- const handleSelectComposition = useCallback106(
45076
+ const handleSelectComposition = useCallback108(
44638
45077
  (comp) => {
44639
45078
  setActiveCompPath(comp.endsWith(".html") ? comp : null);
44640
45079
  fileManager.setEditingFile({ path: comp, content: null });
@@ -44840,18 +45279,18 @@ function StudioApp() {
44840
45279
  }
44841
45280
 
44842
45281
  // src/hooks/useElementPicker.ts
44843
- import { useState as useState92, useCallback as useCallback107, useRef as useRef90 } from "react";
45282
+ import { useState as useState93, useCallback as useCallback109, useRef as useRef91 } from "react";
44844
45283
  function useElementPicker(iframeRef, options) {
44845
- const [isPickMode, setIsPickMode] = useState92(false);
44846
- const [pickedElement, setPickedElement] = useState92(null);
44847
- const activeOverrideRef = useRef90(null);
44848
- const getActiveIframe = useCallback107(() => {
45284
+ const [isPickMode, setIsPickMode] = useState93(false);
45285
+ const [pickedElement, setPickedElement] = useState93(null);
45286
+ const activeOverrideRef = useRef91(null);
45287
+ const getActiveIframe = useCallback109(() => {
44849
45288
  return activeOverrideRef.current ?? iframeRef.current;
44850
45289
  }, [iframeRef]);
44851
- const setActiveIframe = useCallback107((el) => {
45290
+ const setActiveIframe = useCallback109((el) => {
44852
45291
  activeOverrideRef.current = el;
44853
45292
  }, []);
44854
- const enablePick = useCallback107(() => {
45293
+ const enablePick = useCallback109(() => {
44855
45294
  try {
44856
45295
  getActiveIframe()?.contentWindow?.postMessage(
44857
45296
  { source: "hf-parent", type: "control", action: "enable-pick-mode" },
@@ -44861,7 +45300,7 @@ function useElementPicker(iframeRef, options) {
44861
45300
  } catch {
44862
45301
  }
44863
45302
  }, [getActiveIframe]);
44864
- const disablePick = useCallback107(() => {
45303
+ const disablePick = useCallback109(() => {
44865
45304
  try {
44866
45305
  getActiveIframe()?.contentWindow?.postMessage(
44867
45306
  { source: "hf-parent", type: "control", action: "disable-pick-mode" },
@@ -44871,7 +45310,7 @@ function useElementPicker(iframeRef, options) {
44871
45310
  }
44872
45311
  setIsPickMode(false);
44873
45312
  }, [getActiveIframe]);
44874
- const clearPick = useCallback107(() => {
45313
+ const clearPick = useCallback109(() => {
44875
45314
  setPickedElement(null);
44876
45315
  }, []);
44877
45316
  useMountEffect(() => {
@@ -44923,9 +45362,9 @@ function useElementPicker(iframeRef, options) {
44923
45362
  window.addEventListener("message", handleMessage);
44924
45363
  return () => window.removeEventListener("message", handleMessage);
44925
45364
  });
44926
- const optionsRef = useRef90(options);
45365
+ const optionsRef = useRef91(options);
44927
45366
  optionsRef.current = options;
44928
- const syncToSource = useCallback107(
45367
+ const syncToSource = useCallback109(
44929
45368
  (elementId, selector, op) => {
44930
45369
  const opts = optionsRef.current;
44931
45370
  if (!opts?.workspaceFiles || !opts.onSyncFiles || !elementId) return;
@@ -44941,7 +45380,7 @@ function useElementPicker(iframeRef, options) {
44941
45380
  },
44942
45381
  []
44943
45382
  );
44944
- const setStyle = useCallback107(
45383
+ const setStyle = useCallback109(
44945
45384
  (prop, value) => {
44946
45385
  const activeIframe = getActiveIframe();
44947
45386
  if (!pickedElement?.selector || !activeIframe) return;
@@ -44983,7 +45422,7 @@ function useElementPicker(iframeRef, options) {
44983
45422
  },
44984
45423
  [pickedElement, getActiveIframe, syncToSource]
44985
45424
  );
44986
- const setDataAttr = useCallback107(
45425
+ const setDataAttr = useCallback109(
44987
45426
  (attr, value) => {
44988
45427
  const activeIframe = getActiveIframe();
44989
45428
  if (!pickedElement?.selector || !activeIframe) return;
@@ -45011,7 +45450,7 @@ function useElementPicker(iframeRef, options) {
45011
45450
  },
45012
45451
  [pickedElement, getActiveIframe, syncToSource]
45013
45452
  );
45014
- const setTextContent = useCallback107(
45453
+ const setTextContent = useCallback109(
45015
45454
  (text) => {
45016
45455
  const activeIframe = getActiveIframe();
45017
45456
  if (!pickedElement?.selector || !activeIframe) return;
@@ -45034,7 +45473,7 @@ function useElementPicker(iframeRef, options) {
45034
45473
  },
45035
45474
  [pickedElement, getActiveIframe, syncToSource]
45036
45475
  );
45037
- const activeIframeRef = useRef90(null);
45476
+ const activeIframeRef = useRef91(null);
45038
45477
  activeIframeRef.current = getActiveIframe();
45039
45478
  return {
45040
45479
  isPickMode,