hyperframes 0.8.87 → 0.8.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/{add-U6IYWNEI.js → add-JDX7VHMM.js} +5 -5
  2. package/dist/{asset-IVVTJRJ3.js → asset-ZQU3ZAHZ.js} +2 -2
  3. package/dist/{auth-B52RVHDK.js → auth-ZI5X6SXG.js} +1 -1
  4. package/dist/{autoUpdate-JE4IONTI.js → autoUpdate-BDREI4XN.js} +2 -2
  5. package/dist/{backgroundChecks-CGYRZGOR.js → backgroundChecks-52MY7SIC.js} +3 -3
  6. package/dist/backgroundChecksWorker.js +3 -3
  7. package/dist/{beats-E5V4HEFQ.js → beats-77Z6UPMB.js} +4 -4
  8. package/dist/{benchmark-5OYJMUXY.js → benchmark-HR242B6Q.js} +5 -5
  9. package/dist/{browser-ZCYC2U3H.js → browser-CLQNZB5J.js} +3 -3
  10. package/dist/{catalog-Q4L4P7VV.js → catalog-OAJVYTXP.js} +5 -5
  11. package/dist/{check-HM46YAG7.js → check-AJB25CBI.js} +9 -9
  12. package/dist/{checkBrowser-GMHXULQC.js → checkBrowser-RTZFBO62.js} +7 -7
  13. package/dist/{chunk-UUMXWC2H.js → chunk-BEZ5LKUM.js} +2 -2
  14. package/dist/{chunk-BQ4N7ECC.js → chunk-C22UJWNH.js} +1 -1
  15. package/dist/{chunk-NETMPMSJ.js → chunk-CZXT2DRB.js} +2 -2
  16. package/dist/{chunk-V3RCZR7L.js → chunk-EAJNP7C7.js} +1 -1
  17. package/dist/{chunk-VP53XFPD.js → chunk-ELQAAO4F.js} +1 -1
  18. package/dist/{chunk-KGEVE5JJ.js → chunk-J4ZDN5N7.js} +2 -2
  19. package/dist/{chunk-GMEL3UDG.js → chunk-LTCGOUKZ.js} +2 -2
  20. package/dist/{chunk-4MLISXWH.js → chunk-PKK6QWAQ.js} +1 -1
  21. package/dist/{chunk-C4LRCQ7O.js → chunk-RLKPZQKI.js} +2 -2
  22. package/dist/{chunk-V5643PWM.js → chunk-ROFOQAPQ.js} +2 -2
  23. package/dist/{chunk-O24S2D45.js → chunk-S2F6QM6J.js} +1 -1
  24. package/dist/{chunk-RXHJRTDQ.js → chunk-SAYNFMCN.js} +1 -1
  25. package/dist/{chunk-4GPB7TYV.js → chunk-TQNLHVZB.js} +2 -2
  26. package/dist/{chunk-S4NITUIE.js → chunk-XMDQQAIB.js} +1 -1
  27. package/dist/{chunk-OAT3NROY.js → chunk-Z4LBLZ3I.js} +2 -2
  28. package/dist/{clean-LG7EU4Z6.js → clean-YNSDX62K.js} +3 -3
  29. package/dist/cli.js +44 -44
  30. package/dist/{cloud-DQSKLPJH.js → cloud-O6C6D2CC.js} +4 -4
  31. package/dist/{compare-QOEPDS3E.js → compare-32ZYX7EU.js} +4 -4
  32. package/dist/{component-ZVGX6SCH.js → component-OX6ODG5G.js} +3 -3
  33. package/dist/{compositions-JVJO7CKJ.js → compositions-IAWH2EMO.js} +5 -5
  34. package/dist/{delete-573HEO7D.js → delete-4AGDWC2T.js} +2 -2
  35. package/dist/{doctor-MB6A7TN7.js → doctor-AH7IX5Z6.js} +3 -3
  36. package/dist/{events-Z6DV7LT7.js → events-6M6FDQZZ.js} +3 -3
  37. package/dist/{events-7F6C7O2R.js → events-CJRCDPWV.js} +2 -2
  38. package/dist/{feedback-ELSUXCWM.js → feedback-C6DJ6XKA.js} +4 -4
  39. package/dist/{figma-4NL4EZCF.js → figma-L5RHNGY7.js} +3 -3
  40. package/dist/fontLocalizeCli.js +1 -1
  41. package/dist/{get-4W2IN7H3.js → get-TBBCYF5B.js} +2 -2
  42. package/dist/{grade-compare-744LENMN.js → grade-compare-FNPPD6UE.js} +4 -4
  43. package/dist/{help-VET2A2JX.js → help-L4V4PV7C.js} +1 -1
  44. package/dist/{history-BWJH4567.js → history-EEWE52BC.js} +5 -5
  45. package/dist/hyperframes-player.global.js +1 -1
  46. package/dist/{info-TZZU5K44.js → info-VQYR7VW6.js} +5 -5
  47. package/dist/{init-5QPBGFAP.js → init-L5BK5HGZ.js} +6 -6
  48. package/dist/{inspect-ZKM46DT2.js → inspect-4DSMPUHN.js} +6 -6
  49. package/dist/{keyframes-L4PTFVRB.js → keyframes-AXHMLOSU.js} +5 -5
  50. package/dist/{layout-RCQKNYDP.js → layout-ZV23GQU6.js} +6 -6
  51. package/dist/{lint-FDUB6APN.js → lint-HZIRYUSA.js} +6 -6
  52. package/dist/{list-D6NIQOEY.js → list-DLBDHWZ2.js} +2 -2
  53. package/dist/{login-M6AWHM6U.js → login-3PLQRQEP.js} +4 -4
  54. package/dist/{media-treatment-MRNN2DOS.js → media-treatment-7TZ7L7NU.js} +5 -5
  55. package/dist/{normalize-audio-WB76C7OQ.js → normalize-audio-SX64JNLW.js} +4 -4
  56. package/dist/{play-N5FZSNGF.js → play-VX2LSHXR.js} +4 -4
  57. package/dist/{present-N24VMIWP.js → present-F2DPEKZU.js} +4 -4
  58. package/dist/{preview-RO3INI3V.js → preview-ARITZNNL.js} +5 -5
  59. package/dist/{render-FQ7VV2TU.js → render-2VWTXUCT.js} +5 -5
  60. package/dist/{render-FULEBKUN.js → render-FT4NTFKZ.js} +5 -5
  61. package/dist/skills/media-use/audio/references/bgm.md +2 -0
  62. package/dist/skills/media-use/audio/references/sfx.md +2 -3
  63. package/dist/skills/media-use/audio/references/tts.md +1 -1
  64. package/dist/skills/media-use/audio/scripts/audio.mjs +5 -2
  65. package/dist/skills/media-use/audio/scripts/audio.test.mjs +61 -8
  66. package/dist/skills/media-use/audio/scripts/gemini-pipeline.test.mjs +14 -3
  67. package/dist/skills/media-use/audio/scripts/lib/bgm.mjs +8 -4
  68. package/dist/skills/media-use/audio/scripts/lib/bgm.test.mjs +45 -1
  69. package/dist/skills/media-use/audio/scripts/lib/media-record.mjs +43 -2
  70. package/dist/skills/media-use/audio/scripts/lib/media-record.test.mjs +54 -2
  71. package/dist/skills/media-use/audio/scripts/lib/sfx.mjs +36 -17
  72. package/dist/skills/media-use/audio/scripts/lib/sfx.test.mjs +53 -1
  73. package/dist/skills/media-use/scripts/lib/manifest.mjs +3 -0
  74. package/dist/skills/media-use/scripts/resolve.mjs +4 -4
  75. package/dist/{skills-JPRPPT3N.js → skills-KDD7E5CB.js} +5 -5
  76. package/dist/{skillsUpdateCheck-PPLBLSII.js → skillsUpdateCheck-DXGEK4AH.js} +3 -3
  77. package/dist/{snapshot-HBS2KVJV.js → snapshot-BSOD5KKR.js} +4 -4
  78. package/dist/studio/assets/{hyperframes-player-CtNVImMi.js → hyperframes-player-CVoS7kv8.js} +1 -1
  79. package/dist/studio/assets/{index-DZKcDAtf.js → index-BM8PwYby.js} +187 -187
  80. package/dist/studio/assets/{index-DJ-Ol7YS.js → index-D35-L_yn.js} +1 -1
  81. package/dist/studio/assets/{index-B_MF86RE.js → index-DBub9qQY.js} +1 -1
  82. package/dist/studio/{chunk-7PLTC6SV.js → chunk-KU4F7QZ3.js} +9 -1
  83. package/dist/studio/chunk-KU4F7QZ3.js.map +1 -0
  84. package/dist/studio/index.d.ts +42 -5
  85. package/dist/studio/index.html +1 -1
  86. package/dist/studio/index.js +269 -94
  87. package/dist/studio/index.js.map +1 -1
  88. package/dist/studio/player.js +1 -1
  89. package/dist/{studioServer-HIKBLJCQ.js → studioServer-2IK5LLRR.js} +4 -4
  90. package/dist/{telemetry-ZJ6VMRWN.js → telemetry-6P5WHWYI.js} +3 -3
  91. package/dist/{timeline-NMCFP4IO.js → timeline-QZ4Q2LED.js} +5 -5
  92. package/dist/{tokens-ILHCMG2C.js → tokens-VXZYKHFV.js} +2 -2
  93. package/dist/{transcribe-TC7XR5IS.js → transcribe-RQ6RHU2V.js} +3 -3
  94. package/dist/{updateCheck-D7C3KAJT.js → updateCheck-RRWEIZY7.js} +2 -2
  95. package/dist/{upgrade-ZGAFWJLS.js → upgrade-LI5TVSQQ.js} +2 -2
  96. package/dist/{validate-T243OVX2.js → validate-I56RAOC6.js} +6 -6
  97. package/package.json +1 -1
  98. package/dist/studio/chunk-7PLTC6SV.js.map +0 -1
@@ -66,7 +66,7 @@ import {
66
66
  whenPreviewBooted,
67
67
  wrapTimeline,
68
68
  writeStudioUiPreferences
69
- } from "./chunk-7PLTC6SV.js";
69
+ } from "./chunk-KU4F7QZ3.js";
70
70
  import {
71
71
  Button,
72
72
  ContextMenu,
@@ -600,6 +600,8 @@ var DEFAULT_SHORTCUT_SECTIONS = [
600
600
  { key: "\u2318X", label: "Cut element" },
601
601
  { key: hintKey(STUDIO_PLAIN_KEYS.split), label: "Split clip at playhead" },
602
602
  { key: "\u21E7Click", label: "Razor tool: split all tracks" },
603
+ { key: "[", label: "Select clips starting before the playhead" },
604
+ { key: "]", label: "Select clips starting at or after the playhead" },
603
605
  { key: "\u2318G", label: "Group elements" },
604
606
  { key: "\u2318\u21E7G", label: "Ungroup" },
605
607
  { key: "Del", label: "Delete selected element (no keyframe selected)" }
@@ -10657,6 +10659,17 @@ function useTimelineLabelColumn(animationsByElement, elements) {
10657
10659
  return { labelMode, contentOrigin };
10658
10660
  }
10659
10661
 
10662
+ // src/player/components/useTimelineKeyframeData.ts
10663
+ var HIDDEN = {
10664
+ gsapAnimations: /* @__PURE__ */ new Map(),
10665
+ keyframeCache: /* @__PURE__ */ new Map()
10666
+ };
10667
+ function useTimelineKeyframeData(showKeyframes) {
10668
+ const gsapAnimations = usePlayerStore((s) => s.gsapAnimations);
10669
+ const keyframeCache = usePlayerStore((s) => s.keyframeCache);
10670
+ return showKeyframes ? { gsapAnimations, keyframeCache } : HIDDEN;
10671
+ }
10672
+
10660
10673
  // src/player/components/useTimelineScrollViewport.ts
10661
10674
  import { useCallback as useCallback14, useEffect as useEffect17, useRef as useRef19, useState as useState10 } from "react";
10662
10675
 
@@ -12743,6 +12756,7 @@ function useTimelineProviderState({
12743
12756
  canPasteClip,
12744
12757
  theme: themeOverrides,
12745
12758
  showAudioEffects = true,
12759
+ showKeyframes = true,
12746
12760
  sessionEpoch = 0,
12747
12761
  previewIframeRef,
12748
12762
  onZIndexReorder
@@ -12779,8 +12793,9 @@ function useTimelineProviderState({
12779
12793
  const selectedElementId = usePlayerStore((s) => s.selectedElementId);
12780
12794
  const selectedElementIds = usePlayerStore((s) => s.selectedElementIds);
12781
12795
  const focusedEaseSegment = usePlayerStore((s) => s.focusedEaseSegment);
12782
- const gsapAnimations = usePlayerStore((s) => s.gsapAnimations);
12783
- const { labelMode, contentOrigin } = useTimelineLabelColumn(gsapAnimations, timelineElements);
12796
+ const { gsapAnimations, keyframeCache } = useTimelineKeyframeData(showKeyframes);
12797
+ const namedAnimations = usePlayerStore((s) => s.gsapAnimations);
12798
+ const { labelMode, contentOrigin } = useTimelineLabelColumn(namedAnimations, timelineElements);
12784
12799
  const setSelectedElementId = usePlayerStore((s) => s.setSelectedElementId);
12785
12800
  const currentTime = usePlayerStore((s) => s.currentTime);
12786
12801
  const beatDragging = usePlayerStore((s) => s.beatDragging);
@@ -12805,7 +12820,6 @@ function useTimelineProviderState({
12805
12820
  () => getEffectiveTimelineDuration(duration, timelineElements),
12806
12821
  [duration, timelineElements]
12807
12822
  );
12808
- const keyframeCache = usePlayerStore((s) => s.keyframeCache);
12809
12823
  useAutoExpandKeyframedClips(gsapAnimations);
12810
12824
  const {
12811
12825
  tracks,
@@ -77132,8 +77146,15 @@ function useGsapAwareEditing({
77132
77146
  );
77133
77147
  const handleGsapAwareBoxSizeCommit = useCallback135(
77134
77148
  async (selection, next, offset, restore = () => void 0) => {
77135
- const ownedAnimations = getGsapAnimationsForSelection(selection);
77136
- const targetAnimations = Array.isArray(ownedAnimations) ? ownedAnimations : await ownedAnimations;
77149
+ let targetAnimations;
77150
+ try {
77151
+ const ownedAnimations = getGsapAnimationsForSelection(selection);
77152
+ targetAnimations = Array.isArray(ownedAnimations) ? ownedAnimations : await ownedAnimations;
77153
+ } catch (error) {
77154
+ restore();
77155
+ trackGsapInteractionFailure(error, selection, "resize", "Resize animated layer");
77156
+ throw error;
77157
+ }
77137
77158
  const scaleRoute = targetAnimations.some((anim) => anim.propertyGroup === "scale");
77138
77159
  const selector = selectorFromSelection(selection);
77139
77160
  const hasLivePositionTween = selector ? hasNonHoldTweenForElement(
@@ -79389,6 +79410,13 @@ function dispatchPlainKey(event, key2, cb) {
79389
79410
  usePlayerStore.getState().setActiveTool("select");
79390
79411
  return;
79391
79412
  }
79413
+ if ((key2 === "[" || key2 === "]") && !event.shiftKey && !event.altKey) {
79414
+ event.preventDefault();
79415
+ const store = usePlayerStore.getState();
79416
+ if (key2 === "[") store.selectLeftward();
79417
+ else store.selectRightward();
79418
+ return;
79419
+ }
79392
79420
  if (key2 === "a" && !event.shiftKey && !event.altKey) {
79393
79421
  const { activeTool, setActiveTool } = usePlayerStore.getState();
79394
79422
  if (activeTool === "razor") {
@@ -90149,8 +90177,82 @@ function TimelineHistoryButtons(props) {
90149
90177
  ] });
90150
90178
  }
90151
90179
 
90180
+ // src/components/TimelineToolPicker.tsx
90181
+ import { ArrowLineLeft, ArrowLineRight, CaretDown } from "@phosphor-icons/react";
90182
+ import { jsx as jsx225, jsxs as jsxs179 } from "react/jsx-runtime";
90183
+ function SelectIcon() {
90184
+ return /* @__PURE__ */ jsx225("svg", { width: "16", height: "16", viewBox: "0 0 12 12", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx225("path", { d: "M2 0.5L10 6L6.5 6.5L8.5 11L6.5 11.5L4.5 7L2 9Z" }) });
90185
+ }
90186
+ function TimelineToolPicker() {
90187
+ const activeTool = usePlayerStore((s) => s.activeTool);
90188
+ const store = () => usePlayerStore.getState();
90189
+ const rows = [
90190
+ {
90191
+ label: "Select",
90192
+ shortcut: "V",
90193
+ icon: /* @__PURE__ */ jsx225(SelectIcon, {}),
90194
+ active: activeTool === "select",
90195
+ run: () => store().setActiveTool("select")
90196
+ },
90197
+ {
90198
+ label: "Split",
90199
+ shortcut: "B",
90200
+ icon: /* @__PURE__ */ jsx225(Scissors, { size: 16 }),
90201
+ active: activeTool === "razor",
90202
+ run: () => store().setActiveTool("razor")
90203
+ },
90204
+ {
90205
+ label: "Select leftward",
90206
+ shortcut: "[",
90207
+ icon: /* @__PURE__ */ jsx225(ArrowLineLeft, { size: 16, "aria-hidden": "true" }),
90208
+ active: false,
90209
+ run: () => store().selectLeftward()
90210
+ },
90211
+ {
90212
+ label: "Select rightward",
90213
+ shortcut: "]",
90214
+ icon: /* @__PURE__ */ jsx225(ArrowLineRight, { size: 16, "aria-hidden": "true" }),
90215
+ active: false,
90216
+ run: () => store().selectRightward()
90217
+ }
90218
+ ];
90219
+ const current = rows.find((row) => row.active) ?? rows[0];
90220
+ return /* @__PURE__ */ jsx225(
90221
+ Menu,
90222
+ {
90223
+ "aria-label": "Timeline tools",
90224
+ trigger: /* @__PURE__ */ jsxs179(
90225
+ "button",
90226
+ {
90227
+ type: "button",
90228
+ "aria-label": `Timeline tool: ${current.label}`,
90229
+ className: cn(flatIdle, "gap-0.5"),
90230
+ children: [
90231
+ current.icon,
90232
+ /* @__PURE__ */ jsx225(CaretDown, { size: 10, weight: "bold", "aria-hidden": "true" })
90233
+ ]
90234
+ }
90235
+ ),
90236
+ children: rows.map((row) => /* @__PURE__ */ jsx225(
90237
+ MenuItem,
90238
+ {
90239
+ shortcut: row.shortcut,
90240
+ onClick: row.run,
90241
+ "data-active": row.active || void 0,
90242
+ className: row.active ? "bg-neutral-800 text-white" : void 0,
90243
+ children: /* @__PURE__ */ jsxs179("span", { className: "flex items-center gap-2", children: [
90244
+ row.icon,
90245
+ row.label
90246
+ ] })
90247
+ },
90248
+ row.label
90249
+ ))
90250
+ }
90251
+ );
90252
+ }
90253
+
90152
90254
  // src/components/TimelineToolbar.tsx
90153
- import { Fragment as Fragment63, jsx as jsx225, jsxs as jsxs179 } from "react/jsx-runtime";
90255
+ import { Fragment as Fragment63, jsx as jsx226, jsxs as jsxs180 } from "react/jsx-runtime";
90154
90256
  var NO_KEYFRAME_TOGGLE = {
90155
90257
  state: "none",
90156
90258
  isMotionPath: false,
@@ -90212,8 +90314,6 @@ function TimelineToolbar({
90212
90314
  showAddBeat = true,
90213
90315
  showKeyframes = true
90214
90316
  }) {
90215
- const activeTool = usePlayerStore((s) => s.activeTool);
90216
- const setActiveTool = usePlayerStore((s) => s.setActiveTool);
90217
90317
  const timelineSnapEnabled = usePlayerStore((s) => s.timelineSnapEnabled);
90218
90318
  const setTimelineSnapEnabled = usePlayerStore((s) => s.setTimelineSnapEnabled);
90219
90319
  const rippleEditEnabled = usePlayerStore((s) => s.rippleEditEnabled);
@@ -90268,33 +90368,12 @@ function TimelineToolbar({
90268
90368
  return (
90269
90369
  // The "TIMELINE" label is dropped for CapCut-like density — the pane's
90270
90370
  // position (tracks right below) makes it self-evident.
90271
- /* @__PURE__ */ jsx225("div", { className: "border-b border-neutral-800/60", children: /* @__PURE__ */ jsxs179("div", { className: "flex items-center justify-between px-2 py-0.5", children: [
90272
- /* @__PURE__ */ jsxs179("div", { className: "flex items-center gap-0.5", children: [
90273
- /* @__PURE__ */ jsx225(TimelineHistoryButtons, { ...history2 }),
90274
- /* @__PURE__ */ jsx225(Tooltip, { label: "Selection tool (V)", children: /* @__PURE__ */ jsx225(
90275
- "button",
90276
- {
90277
- type: "button",
90278
- onClick: () => setActiveTool("select"),
90279
- "aria-label": "Selection tool",
90280
- "aria-pressed": activeTool === "select",
90281
- className: activeTool === "select" ? flatActive : flatIdle,
90282
- children: /* @__PURE__ */ jsx225("svg", { width: "16", height: "16", viewBox: "0 0 12 12", fill: "currentColor", children: /* @__PURE__ */ jsx225("path", { d: "M2 0.5L10 6L6.5 6.5L8.5 11L6.5 11.5L4.5 7L2 9Z" }) })
90283
- }
90284
- ) }),
90285
- /* @__PURE__ */ jsx225(Tooltip, { label: "Razor tool (B) \u2014 Shift+click splits all tracks", children: /* @__PURE__ */ jsx225(
90286
- "button",
90287
- {
90288
- type: "button",
90289
- onClick: () => setActiveTool("razor"),
90290
- "aria-label": "Razor tool",
90291
- "aria-pressed": activeTool === "razor",
90292
- className: activeTool === "razor" ? flatActive : flatIdle,
90293
- children: /* @__PURE__ */ jsx225(Scissors, { size: 16 })
90294
- }
90295
- ) }),
90296
- /* @__PURE__ */ jsx225("div", { "aria-hidden": "true", className: "mx-1 h-4 w-px bg-neutral-800" }),
90297
- /* @__PURE__ */ jsx225(Tooltip, { label: timelineSnapEnabled ? "Snapping on (N)" : "Snapping off (N)", children: /* @__PURE__ */ jsx225(
90371
+ /* @__PURE__ */ jsx226("div", { className: "border-b border-neutral-800/60", children: /* @__PURE__ */ jsxs180("div", { className: "flex items-center justify-between px-2 py-0.5", children: [
90372
+ /* @__PURE__ */ jsxs180("div", { className: "flex items-center gap-0.5", children: [
90373
+ /* @__PURE__ */ jsx226(TimelineHistoryButtons, { ...history2 }),
90374
+ /* @__PURE__ */ jsx226(TimelineToolPicker, {}),
90375
+ /* @__PURE__ */ jsx226("div", { "aria-hidden": "true", className: "mx-1 h-4 w-px bg-neutral-800" }),
90376
+ /* @__PURE__ */ jsx226(Tooltip, { label: timelineSnapEnabled ? "Snapping on (N)" : "Snapping off (N)", children: /* @__PURE__ */ jsx226(
90298
90377
  "button",
90299
90378
  {
90300
90379
  type: "button",
@@ -90302,14 +90381,14 @@ function TimelineToolbar({
90302
90381
  "aria-label": "Toggle timeline snapping",
90303
90382
  "aria-pressed": timelineSnapEnabled,
90304
90383
  className: timelineSnapEnabled ? flatActive : flatIdle,
90305
- children: /* @__PURE__ */ jsx225(Magnet, { size: 16, weight: "bold", "aria-hidden": "true" })
90384
+ children: /* @__PURE__ */ jsx226(Magnet, { size: 16, weight: "bold", "aria-hidden": "true" })
90306
90385
  }
90307
90386
  ) }),
90308
- /* @__PURE__ */ jsx225(
90387
+ /* @__PURE__ */ jsx226(
90309
90388
  Tooltip,
90310
90389
  {
90311
90390
  label: rippleEditEnabled ? "Ripple on \u2014 keeps the main track gapless" : "Ripple off \u2014 deleting a main-track clip leaves a gap",
90312
- children: /* @__PURE__ */ jsx225(
90391
+ children: /* @__PURE__ */ jsx226(
90313
90392
  "button",
90314
90393
  {
90315
90394
  type: "button",
@@ -90317,12 +90396,12 @@ function TimelineToolbar({
90317
90396
  "aria-label": "Toggle ripple edit",
90318
90397
  "aria-pressed": rippleEditEnabled,
90319
90398
  className: rippleEditEnabled ? flatActive : flatIdle,
90320
- children: /* @__PURE__ */ jsx225(RippleEditIcon, { size: 16 })
90399
+ children: /* @__PURE__ */ jsx226(RippleEditIcon, { size: 16 })
90321
90400
  }
90322
90401
  )
90323
90402
  }
90324
90403
  ),
90325
- projectHasAudio && /* @__PURE__ */ jsx225(Tooltip, { label: audioMetersVisible ? "Hide audio meters" : "Show audio meters", children: /* @__PURE__ */ jsx225(
90404
+ projectHasAudio && /* @__PURE__ */ jsx226(Tooltip, { label: audioMetersVisible ? "Hide audio meters" : "Show audio meters", children: /* @__PURE__ */ jsx226(
90326
90405
  "button",
90327
90406
  {
90328
90407
  type: "button",
@@ -90330,15 +90409,15 @@ function TimelineToolbar({
90330
90409
  "aria-label": "Toggle audio meters",
90331
90410
  "aria-pressed": audioMetersVisible,
90332
90411
  className: audioMetersVisible ? flatActive : flatIdle,
90333
- children: /* @__PURE__ */ jsx225(AudioMetersIcon, { size: 16 })
90412
+ children: /* @__PURE__ */ jsx226(AudioMetersIcon, { size: 16 })
90334
90413
  }
90335
90414
  ) }),
90336
- showKeyframes && /* @__PURE__ */ jsxs179(Fragment63, { children: [
90337
- /* @__PURE__ */ jsx225(
90415
+ showKeyframes && /* @__PURE__ */ jsxs180(Fragment63, { children: [
90416
+ /* @__PURE__ */ jsx226(
90338
90417
  Tooltip,
90339
90418
  {
90340
90419
  label: keyframePathEndpoint ? "Motion path endpoints cannot be removed" : !onToggleKeyframe ? "Select an animated element to add keyframes" : keyframeIsMotionPath ? keyframeWillExtend ? "Extend motion path to playhead (K)" : keyframeState === "active" ? "Remove waypoint from motion path (K)" : "Add waypoint to motion path (K)" : keyframeState === "active" ? "Remove keyframe at playhead (K)" : keyframeState === "inactive" ? keyframeWillExtend ? "Add keyframe at playhead, extends animation (K)" : "Add keyframe at playhead (K)" : "Add keyframe (K)",
90341
- children: /* @__PURE__ */ jsx225(
90420
+ children: /* @__PURE__ */ jsx226(
90342
90421
  "button",
90343
90422
  {
90344
90423
  type: "button",
@@ -90346,7 +90425,7 @@ function TimelineToolbar({
90346
90425
  onClick: onToggleKeyframe,
90347
90426
  "aria-label": keyframePathEndpoint ? "Motion path endpoint" : keyframeIsMotionPath ? keyframeState === "active" ? "Remove motion path waypoint" : keyframeWillExtend ? "Extend motion path to playhead" : "Add motion path waypoint" : keyframeState === "active" ? "Remove keyframe at playhead" : "Add keyframe at playhead",
90348
90427
  className: !onToggleKeyframe ? flatDisabled : `${flatBtn} active:scale-[0.98] hover:bg-white/6 ${keyframeState === "active" ? "text-studio-accent" : keyframeState === "inactive" ? "text-neutral-400 hover:text-studio-accent" : "text-neutral-600 hover:text-neutral-400"}`,
90349
- children: /* @__PURE__ */ jsx225("svg", { width: "16", height: "16", viewBox: "0 0 10 10", fill: "currentColor", children: keyframeState === "active" ? /* @__PURE__ */ jsx225("path", { d: "M5 0.5L9.5 5L5 9.5L0.5 5Z" }) : /* @__PURE__ */ jsx225(
90428
+ children: /* @__PURE__ */ jsx226("svg", { width: "16", height: "16", viewBox: "0 0 10 10", fill: "currentColor", children: keyframeState === "active" ? /* @__PURE__ */ jsx226("path", { d: "M5 0.5L9.5 5L5 9.5L0.5 5Z" }) : /* @__PURE__ */ jsx226(
90350
90429
  "path",
90351
90430
  {
90352
90431
  d: "M5 1.2L8.8 5L5 8.8L1.2 5Z",
@@ -90359,11 +90438,11 @@ function TimelineToolbar({
90359
90438
  )
90360
90439
  }
90361
90440
  ),
90362
- /* @__PURE__ */ jsx225(
90441
+ /* @__PURE__ */ jsx226(
90363
90442
  Tooltip,
90364
90443
  {
90365
90444
  label: autoKeyframeEnabled ? "Auto-record manual edits as keyframes (click to turn off)" : "Manual edits will not be recorded as keyframes (click to turn on)",
90366
- children: /* @__PURE__ */ jsx225(
90445
+ children: /* @__PURE__ */ jsx226(
90367
90446
  "button",
90368
90447
  {
90369
90448
  type: "button",
@@ -90371,9 +90450,9 @@ function TimelineToolbar({
90371
90450
  "aria-label": "Auto-record manual edits as keyframes",
90372
90451
  "aria-pressed": autoKeyframeEnabled,
90373
90452
  className: `${flatBtn} active:scale-[0.98] hover:bg-white/6 ${autoKeyframeEnabled ? "text-red-400 hover:text-red-300" : "text-neutral-600 hover:text-neutral-400"}`,
90374
- children: /* @__PURE__ */ jsxs179("svg", { width: "16", height: "16", viewBox: "0 0 10 10", fill: "none", children: [
90375
- /* @__PURE__ */ jsx225("path", { d: "M5 0.7L9.3 5L5 9.3L0.7 5Z", stroke: "currentColor", strokeWidth: "1" }),
90376
- /* @__PURE__ */ jsx225(
90453
+ children: /* @__PURE__ */ jsxs180("svg", { width: "16", height: "16", viewBox: "0 0 10 10", fill: "none", children: [
90454
+ /* @__PURE__ */ jsx226("path", { d: "M5 0.7L9.3 5L5 9.3L0.7 5Z", stroke: "currentColor", strokeWidth: "1" }),
90455
+ /* @__PURE__ */ jsx226(
90377
90456
  "circle",
90378
90457
  {
90379
90458
  cx: "5",
@@ -90394,11 +90473,11 @@ function TimelineToolbar({
90394
90473
  const el = selectedElementId ? elements.find((e) => (e.key ?? e.id) === selectedElementId) : null;
90395
90474
  const splittable = el != null && canSplitElement(el);
90396
90475
  const canSplit = splittable && currentTime > el.start && currentTime < el.start + el.duration;
90397
- return /* @__PURE__ */ jsx225(
90476
+ return /* @__PURE__ */ jsx226(
90398
90477
  Tooltip,
90399
90478
  {
90400
90479
  label: canSplit ? "Split at playhead (S)" : splittable ? "Move the playhead inside the clip to split" : "Select a clip to split",
90401
- children: /* @__PURE__ */ jsx225(
90480
+ children: /* @__PURE__ */ jsx226(
90402
90481
  "button",
90403
90482
  {
90404
90483
  type: "button",
@@ -90408,7 +90487,7 @@ function TimelineToolbar({
90408
90487
  if (canSplit && el) onSplitElement(el, currentTime);
90409
90488
  },
90410
90489
  className: canSplit ? flatIdle : flatDisabled,
90411
- children: /* @__PURE__ */ jsxs179(
90490
+ children: /* @__PURE__ */ jsxs180(
90412
90491
  "svg",
90413
90492
  {
90414
90493
  width: "16",
@@ -90421,8 +90500,8 @@ function TimelineToolbar({
90421
90500
  strokeLinejoin: "round",
90422
90501
  "aria-hidden": "true",
90423
90502
  children: [
90424
- /* @__PURE__ */ jsx225("path", { d: "M5 3 L7 3 L7 13 L5 13" }),
90425
- /* @__PURE__ */ jsx225("path", { d: "M11 3 L9 3 L9 13 L11 13" })
90503
+ /* @__PURE__ */ jsx226("path", { d: "M5 3 L7 3 L7 13 L5 13" }),
90504
+ /* @__PURE__ */ jsx226("path", { d: "M11 3 L9 3 L9 13 L11 13" })
90426
90505
  ]
90427
90506
  }
90428
90507
  )
@@ -90434,11 +90513,11 @@ function TimelineToolbar({
90434
90513
  (() => {
90435
90514
  if (!showAddBeat) return null;
90436
90515
  const canAdd = beatAnalysisReady && canAddBeatAt(currentTime);
90437
- return /* @__PURE__ */ jsx225(
90516
+ return /* @__PURE__ */ jsx226(
90438
90517
  Tooltip,
90439
90518
  {
90440
90519
  label: !beatAnalysisReady ? "Add a music track with beat analysis to place beats" : canAdd ? "Add beat at playhead" : "A beat already exists at the playhead",
90441
- children: /* @__PURE__ */ jsx225(
90520
+ children: /* @__PURE__ */ jsx226(
90442
90521
  "button",
90443
90522
  {
90444
90523
  type: "button",
@@ -90448,7 +90527,7 @@ function TimelineToolbar({
90448
90527
  if (canAdd) addBeatAtCompositionTime(currentTime);
90449
90528
  },
90450
90529
  className: canAdd ? `${flatBtn} text-neutral-400 hover:bg-white/6 hover:text-[#22c55e] active:scale-[0.98]` : flatDisabled,
90451
- children: /* @__PURE__ */ jsx225("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx225(
90530
+ children: /* @__PURE__ */ jsx226("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx226(
90452
90531
  "path",
90453
90532
  {
90454
90533
  d: "M21 10C21 12.2091 16.9706 14 12 14M21 10C21 7.79086 16.9706 6 12 6C7.02944 6 3 7.79086 3 10M21 10V16C21 18.2091 16.9706 20 12 20M12 14C7.02944 14 3 12.2091 3 10M12 14V20M3 10V16C3 18.2091 7.02944 20 12 20M7 19.3264V13.3264M17 19.3264V13.3264M12 10L20 4",
@@ -90464,12 +90543,12 @@ function TimelineToolbar({
90464
90543
  );
90465
90544
  })()
90466
90545
  ] }),
90467
- /* @__PURE__ */ jsxs179("div", { className: "flex items-center gap-0.5", children: [
90468
- /* @__PURE__ */ jsx225(
90546
+ /* @__PURE__ */ jsxs180("div", { className: "flex items-center gap-0.5", children: [
90547
+ /* @__PURE__ */ jsx226(
90469
90548
  Tooltip,
90470
90549
  {
90471
90550
  label: thumbnailsVisible ? "Hide thumbnails \u2014 labels only" : "Show thumbnails \u2014 posters stay visible; richer previews appear on interaction",
90472
- children: /* @__PURE__ */ jsx225(
90551
+ children: /* @__PURE__ */ jsx226(
90473
90552
  "button",
90474
90553
  {
90475
90554
  type: "button",
@@ -90477,12 +90556,12 @@ function TimelineToolbar({
90477
90556
  "aria-pressed": thumbnailsVisible,
90478
90557
  onClick: () => setThumbnailMode(thumbnailsVisible ? "hidden" : "adaptive"),
90479
90558
  className: `h-7 px-2 rounded-md text-[11px] font-medium transition-colors ${thumbnailsVisible ? "bg-studio-accent/10 text-studio-accent" : "text-neutral-400 hover:bg-white/6 hover:text-neutral-200"}`,
90480
- children: /* @__PURE__ */ jsx225(Image3, { size: 16, "aria-hidden": "true" })
90559
+ children: /* @__PURE__ */ jsx226(Image3, { size: 16, "aria-hidden": "true" })
90481
90560
  }
90482
90561
  )
90483
90562
  }
90484
90563
  ),
90485
- /* @__PURE__ */ jsx225(Tooltip, { label: "Fit timeline to width", children: /* @__PURE__ */ jsx225(
90564
+ /* @__PURE__ */ jsx226(Tooltip, { label: "Fit timeline to width", children: /* @__PURE__ */ jsx226(
90486
90565
  "button",
90487
90566
  {
90488
90567
  type: "button",
@@ -90490,10 +90569,10 @@ function TimelineToolbar({
90490
90569
  "aria-label": "Fit timeline to width",
90491
90570
  "aria-pressed": zoomMode === "fit",
90492
90571
  className: `h-7 px-2 rounded-md text-[11px] font-medium transition-colors ${zoomMode === "fit" ? "bg-studio-accent/10 text-studio-accent" : "text-neutral-400 hover:bg-white/6 hover:text-neutral-200"}`,
90493
- children: /* @__PURE__ */ jsx225(ArrowsOutLineHorizontal, { size: 16, "aria-hidden": "true" })
90572
+ children: /* @__PURE__ */ jsx226(ArrowsOutLineHorizontal, { size: 16, "aria-hidden": "true" })
90494
90573
  }
90495
90574
  ) }),
90496
- /* @__PURE__ */ jsx225(Tooltip, { label: "Zoom out", children: /* @__PURE__ */ jsx225(
90575
+ /* @__PURE__ */ jsx226(Tooltip, { label: "Zoom out", children: /* @__PURE__ */ jsx226(
90497
90576
  "button",
90498
90577
  {
90499
90578
  type: "button",
@@ -90505,10 +90584,10 @@ function TimelineToolbar({
90505
90584
  );
90506
90585
  },
90507
90586
  className: flatIdle,
90508
- children: /* @__PURE__ */ jsx225(MagnifyingGlassMinus, { size: 16, "aria-hidden": "true" })
90587
+ children: /* @__PURE__ */ jsx226(MagnifyingGlassMinus, { size: 16, "aria-hidden": "true" })
90509
90588
  }
90510
90589
  ) }),
90511
- /* @__PURE__ */ jsx225(
90590
+ /* @__PURE__ */ jsx226(
90512
90591
  "input",
90513
90592
  {
90514
90593
  type: "range",
@@ -90526,7 +90605,7 @@ function TimelineToolbar({
90526
90605
  className: "mx-1 h-6 w-[96px] cursor-pointer appearance-none bg-transparent [&::-webkit-slider-runnable-track]:h-[2px] [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-neutral-700 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-[10px] [&::-webkit-slider-thumb]:h-[10px] [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:-mt-1 [&::-webkit-slider-thumb]:shadow-[0_0_0_2px_#0a0a0a,0_1px_3px_rgba(0,0,0,0.5)] [&::-webkit-slider-thumb]:cursor-grab [&::-webkit-slider-thumb:active]:cursor-grabbing"
90527
90606
  }
90528
90607
  ),
90529
- /* @__PURE__ */ jsx225(Tooltip, { label: "Zoom in", children: /* @__PURE__ */ jsx225(
90608
+ /* @__PURE__ */ jsx226(Tooltip, { label: "Zoom in", children: /* @__PURE__ */ jsx226(
90530
90609
  "button",
90531
90610
  {
90532
90611
  type: "button",
@@ -90538,10 +90617,10 @@ function TimelineToolbar({
90538
90617
  );
90539
90618
  },
90540
90619
  className: flatIdle,
90541
- children: /* @__PURE__ */ jsx225(MagnifyingGlassPlus, { size: 16, "aria-hidden": "true" })
90620
+ children: /* @__PURE__ */ jsx226(MagnifyingGlassPlus, { size: 16, "aria-hidden": "true" })
90542
90621
  }
90543
90622
  ) }),
90544
- /* @__PURE__ */ jsx225(
90623
+ /* @__PURE__ */ jsx226(
90545
90624
  "span",
90546
90625
  {
90547
90626
  className: "ml-1 w-[38px] text-right font-mono text-[11px] tabular-nums text-neutral-500 select-none",
@@ -90555,18 +90634,18 @@ function TimelineToolbar({
90555
90634
  }
90556
90635
 
90557
90636
  // src/components/StudioSplash.tsx
90558
- import { jsx as jsx226, jsxs as jsxs180 } from "react/jsx-runtime";
90637
+ import { jsx as jsx227, jsxs as jsxs181 } from "react/jsx-runtime";
90559
90638
  function StudioSplash({ waiting }) {
90560
- return /* @__PURE__ */ jsx226("div", { className: "h-full w-full bg-neutral-950 flex items-center justify-center", children: waiting ? /* @__PURE__ */ jsxs180("div", { className: "flex flex-col items-center gap-3 text-center px-6", role: "status", children: [
90561
- /* @__PURE__ */ jsx226("div", { className: "w-4 h-4 rounded-full border-2 border-neutral-700 border-t-neutral-500 animate-spin motion-reduce:animate-none" }),
90562
- /* @__PURE__ */ jsxs180("p", { className: "text-xs text-neutral-600", children: [
90639
+ return /* @__PURE__ */ jsx227("div", { className: "h-full w-full bg-neutral-950 flex items-center justify-center", children: waiting ? /* @__PURE__ */ jsxs181("div", { className: "flex flex-col items-center gap-3 text-center px-6", role: "status", children: [
90640
+ /* @__PURE__ */ jsx227("div", { className: "w-4 h-4 rounded-full border-2 border-neutral-700 border-t-neutral-500 animate-spin motion-reduce:animate-none" }),
90641
+ /* @__PURE__ */ jsxs181("p", { className: "text-xs text-neutral-600", children: [
90563
90642
  "Waiting for preview server\u2026 run",
90564
90643
  " ",
90565
- /* @__PURE__ */ jsx226("code", { className: "text-neutral-500 font-mono", children: "npm run dev" })
90644
+ /* @__PURE__ */ jsx227("code", { className: "text-neutral-500 font-mono", children: "npm run dev" })
90566
90645
  ] })
90567
- ] }) : /* @__PURE__ */ jsxs180("div", { className: "flex flex-col items-center gap-3 text-center px-6", role: "status", children: [
90568
- /* @__PURE__ */ jsx226("div", { className: "w-4 h-4 rounded-full bg-studio-accent animate-pulse motion-reduce:animate-none" }),
90569
- /* @__PURE__ */ jsx226("p", { className: "text-xs text-neutral-600", children: "Connecting to project\u2026" })
90646
+ ] }) : /* @__PURE__ */ jsxs181("div", { className: "flex flex-col items-center gap-3 text-center px-6", role: "status", children: [
90647
+ /* @__PURE__ */ jsx227("div", { className: "w-4 h-4 rounded-full bg-studio-accent animate-pulse motion-reduce:animate-none" }),
90648
+ /* @__PURE__ */ jsx227("p", { className: "text-xs text-neutral-600", children: "Connecting to project\u2026" })
90570
90649
  ] }) });
90571
90650
  }
90572
90651
 
@@ -90833,7 +90912,7 @@ function useActiveComposition({
90833
90912
  }
90834
90913
 
90835
90914
  // src/App.tsx
90836
- import { Fragment as Fragment64, jsx as jsx227, jsxs as jsxs181 } from "react/jsx-runtime";
90915
+ import { Fragment as Fragment64, jsx as jsx228, jsxs as jsxs182 } from "react/jsx-runtime";
90837
90916
  var getTimelineSelectionSet = () => usePlayerStore.getState().selectedElementIds;
90838
90917
  function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
90839
90918
  const { projectId, resolving, waitingForServer } = useServerConnection();
@@ -91197,7 +91276,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
91197
91276
  refreshPreviewDocumentVersion
91198
91277
  });
91199
91278
  const timelineToolbar = useMemo79(
91200
- () => /* @__PURE__ */ jsx227(
91279
+ () => /* @__PURE__ */ jsx228(
91201
91280
  TimelineToolbar,
91202
91281
  {
91203
91282
  domEditSession,
@@ -91207,15 +91286,15 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
91207
91286
  [domEditSession, timelineEditing.handleTimelineElementSplit]
91208
91287
  );
91209
91288
  if (resolving || waitingForServer || !projectId)
91210
- return /* @__PURE__ */ jsx227(StudioSplash, { waiting: waitingForServer });
91211
- return /* @__PURE__ */ jsx227(StudioShellProvider, { value: studioCtxValue, children: /* @__PURE__ */ jsx227(StudioPlaybackProvider, { value: studioCtxValue, children: /* @__PURE__ */ jsx227(PanelLayoutProvider, { value: layout, children: /* @__PURE__ */ jsx227(FileManagerProvider, { value: fileManager, children: /* @__PURE__ */ jsx227(DomEditProvider, { value: domEditSession, children: /* @__PURE__ */ jsxs181(
91289
+ return /* @__PURE__ */ jsx228(StudioSplash, { waiting: waitingForServer });
91290
+ return /* @__PURE__ */ jsx228(StudioShellProvider, { value: studioCtxValue, children: /* @__PURE__ */ jsx228(StudioPlaybackProvider, { value: studioCtxValue, children: /* @__PURE__ */ jsx228(PanelLayoutProvider, { value: layout, children: /* @__PURE__ */ jsx228(FileManagerProvider, { value: fileManager, children: /* @__PURE__ */ jsx228(DomEditProvider, { value: domEditSession, children: /* @__PURE__ */ jsxs182(
91212
91291
  "div",
91213
91292
  {
91214
91293
  className: "flex flex-col h-full w-full bg-neutral-950 relative",
91215
91294
  onDragOver: fileDrop.onDragOver,
91216
91295
  onDrop: fileDrop.onDrop,
91217
91296
  children: [
91218
- /* @__PURE__ */ jsx227(
91297
+ /* @__PURE__ */ jsx228(
91219
91298
  StudioHeader,
91220
91299
  {
91221
91300
  captureFrameHref: frameCapture.captureFrameHref,
@@ -91233,23 +91312,23 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
91233
91312
  }
91234
91313
  }
91235
91314
  ),
91236
- previewPersistence.domEditSaveQueuePaused && !externalFileChanges.blocked && /* @__PURE__ */ jsx227(
91315
+ previewPersistence.domEditSaveQueuePaused && !externalFileChanges.blocked && /* @__PURE__ */ jsx228(
91237
91316
  SaveQueuePausedBanner,
91238
91317
  {
91239
91318
  message: previewPersistence.domEditSaveQueuePaused,
91240
91319
  onRetry: previewPersistence.resetDomEditSaveQueueBreaker
91241
91320
  }
91242
91321
  ),
91243
- /* @__PURE__ */ jsx227(ExternalFileConflictBanner, { coordinator: externalFileChanges }),
91244
- sdkHandle.unreachableProject && /* @__PURE__ */ jsx227(ProjectUnreachableBanner, { projectId: sdkHandle.unreachableProject }),
91245
- sdkHandle.compositionMissing && activeCompPath && /* @__PURE__ */ jsx227(CompositionMissingBanner, { path: activeCompPath }),
91246
- /* @__PURE__ */ jsx227(
91322
+ /* @__PURE__ */ jsx228(ExternalFileConflictBanner, { coordinator: externalFileChanges }),
91323
+ sdkHandle.unreachableProject && /* @__PURE__ */ jsx228(ProjectUnreachableBanner, { projectId: sdkHandle.unreachableProject }),
91324
+ sdkHandle.compositionMissing && activeCompPath && /* @__PURE__ */ jsx228(CompositionMissingBanner, { path: activeCompPath }),
91325
+ /* @__PURE__ */ jsx228(
91247
91326
  EditorShell,
91248
91327
  {
91249
91328
  readOnlyPreview,
91250
91329
  readOnlyPreviewReason,
91251
- panels: /* @__PURE__ */ jsxs181(Fragment64, { children: [
91252
- /* @__PURE__ */ jsx227(
91330
+ panels: /* @__PURE__ */ jsxs182(Fragment64, { children: [
91331
+ /* @__PURE__ */ jsx228(
91253
91332
  StudioLeftPanels,
91254
91333
  {
91255
91334
  onSelectComposition: handleSelectComposition,
@@ -91264,7 +91343,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
91264
91343
  onAddCompositionToTimeline: handleAddCompositionAtPlayhead
91265
91344
  }
91266
91345
  ),
91267
- /* @__PURE__ */ jsx227(
91346
+ /* @__PURE__ */ jsx228(
91268
91347
  StudioRightPanels,
91269
91348
  {
91270
91349
  activeBlockParams,
@@ -91319,7 +91398,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
91319
91398
  recordingState: gestureState,
91320
91399
  onToggleRecording: handleToggleRecording,
91321
91400
  blockPreview,
91322
- gestureOverlay: gestureState === "recording" && previewIframe ? /* @__PURE__ */ jsx227(
91401
+ gestureOverlay: gestureState === "recording" && previewIframe ? /* @__PURE__ */ jsx228(
91323
91402
  GestureTrailOverlay,
91324
91403
  {
91325
91404
  samples: gestureRecording.samplesRef.current,
@@ -91332,7 +91411,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
91332
91411
  ) : void 0
91333
91412
  }
91334
91413
  ),
91335
- /* @__PURE__ */ jsx227(
91414
+ /* @__PURE__ */ jsx228(
91336
91415
  StudioOverlays,
91337
91416
  {
91338
91417
  projectId,
@@ -91760,6 +91839,101 @@ function useDomStyleCommit({
91760
91839
  return { commitStyle, waitForPendingSaves: queue2.waitForIdle };
91761
91840
  }
91762
91841
 
91842
+ // src/hooks/useDomGeometryCommit.ts
91843
+ import { useCallback as useCallback185, useMemo as useMemo81, useRef as useRef183 } from "react";
91844
+ var noop2 = () => {
91845
+ };
91846
+ var NO_SELECTED_ANIMATIONS = [];
91847
+ var noDomBoxSizeRoute = () => Promise.reject(new Error("Resize has no DOM route here"));
91848
+ function useDomGeometryCommit({
91849
+ projectId,
91850
+ iframeRef,
91851
+ writeProjectFile,
91852
+ recordEdit,
91853
+ activeCompPath = null,
91854
+ showToast = noop2,
91855
+ reloadPreview
91856
+ }) {
91857
+ const projectIdRef = useRef183(projectId);
91858
+ projectIdRef.current = projectId;
91859
+ const pending2 = useRef183(/* @__PURE__ */ new Set()).current;
91860
+ const editHistory = useMemo81(() => ({ recordEdit }), [recordEdit]);
91861
+ const reload = useCallback185(
91862
+ () => reloadPreview ? reloadPreview() : iframeRef.current?.contentWindow?.location.reload(),
91863
+ [reloadPreview, iframeRef]
91864
+ );
91865
+ const { bump: bumpGsapCache } = useGsapCacheVersion();
91866
+ const gsap3 = useGsapScriptCommits({
91867
+ projectIdRef,
91868
+ activeCompPath,
91869
+ previewIframeRef: iframeRef,
91870
+ editHistory,
91871
+ reloadPreview: reload,
91872
+ onCacheInvalidate: bumpGsapCache,
91873
+ showToast,
91874
+ writeProjectFile
91875
+ });
91876
+ const makeFetchFallback = useGsapAnimationFetchFallback(projectId);
91877
+ const trackGsapInteractionFailure = useGsapInteractionFailureTelemetry(activeCompPath, showToast);
91878
+ const {
91879
+ handleGsapAwarePathOffsetCommit,
91880
+ handleGsapAwareGroupPathOffsetCommit,
91881
+ handleGsapAwareBoxSizeCommit,
91882
+ handleGsapAwareRotationCommit
91883
+ } = useGsapAwareEditing({
91884
+ // The host owns selection, so every gesture reads its element's animations from the server.
91885
+ domEditSelection: null,
91886
+ selectedGsapAnimations: NO_SELECTED_ANIMATIONS,
91887
+ gsapCommitMutation: gsap3.commitMutation,
91888
+ previewIframeRef: iframeRef,
91889
+ showToast,
91890
+ bumpGsapCache,
91891
+ makeFetchFallback,
91892
+ trackGsapInteractionFailure,
91893
+ handleDomBoxSizeCommit: noDomBoxSizeRoute,
91894
+ addGsapAnimation: gsap3.addGsapAnimation,
91895
+ convertToKeyframes: gsap3.convertToKeyframes,
91896
+ setArcPath: gsap3.setArcPath,
91897
+ updateArcSegment: gsap3.updateArcSegment
91898
+ });
91899
+ const saved2 = useCallback185(
91900
+ async (commit, restore = noop2) => {
91901
+ const refusal2 = !projectIdRef.current ? "No project is open" : !isPreviewBooted(projectIdRef.current) ? "The preview for this project has not loaded yet" : null;
91902
+ if (refusal2) {
91903
+ restore();
91904
+ showToast(refusal2, "error");
91905
+ throw new Error(refusal2);
91906
+ }
91907
+ const run = commit();
91908
+ pending2.add(run);
91909
+ try {
91910
+ await run;
91911
+ } finally {
91912
+ pending2.delete(run);
91913
+ }
91914
+ return { ok: true };
91915
+ },
91916
+ [pending2, showToast]
91917
+ );
91918
+ return useMemo81(
91919
+ () => ({
91920
+ commitPathOffset: (selection, next, modifiers) => saved2(() => handleGsapAwarePathOffsetCommit(selection, next, modifiers)),
91921
+ commitGroupPathOffset: (updates) => saved2(() => handleGsapAwareGroupPathOffsetCommit(updates)),
91922
+ commitBoxSize: (selection, next, offset, restore) => saved2(() => handleGsapAwareBoxSizeCommit(selection, next, offset, restore), restore),
91923
+ commitRotation: (selection, next) => saved2(() => handleGsapAwareRotationCommit(selection, next)),
91924
+ waitForPendingSaves: () => Promise.allSettled([...pending2]).then(noop2)
91925
+ }),
91926
+ [
91927
+ saved2,
91928
+ pending2,
91929
+ handleGsapAwarePathOffsetCommit,
91930
+ handleGsapAwareGroupPathOffsetCommit,
91931
+ handleGsapAwareBoxSizeCommit,
91932
+ handleGsapAwareRotationCommit
91933
+ ]
91934
+ );
91935
+ }
91936
+
91763
91937
  // src/index.ts
91764
91938
  import {
91765
91939
  playSeamTransitionLoop
@@ -91869,6 +92043,7 @@ export {
91869
92043
  useCompositionStack,
91870
92044
  useDomEditSelectionContext,
91871
92045
  useDomEditSession,
92046
+ useDomGeometryCommit,
91872
92047
  useDomStyleCommit,
91873
92048
  useEditHistoryActions,
91874
92049
  useElementPicker,