@hyperframes/studio 0.8.119 → 0.8.120

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 (29) hide show
  1. package/dist/assets/{hyperframes-player-7Icl2uVN.js → hyperframes-player-i4gqkhjz.js} +1 -1
  2. package/dist/assets/{index-Dc8FEIEU.js → index-C7LHDM18.js} +1 -1
  3. package/dist/assets/{index-DOO2cYhl.js → index-Ce3Lr-Gt.js} +174 -174
  4. package/dist/assets/{index-7YcUqkoh.js → index-uWOhRqxe.js} +1 -1
  5. package/dist/index.d.ts +5 -1
  6. package/dist/index.html +1 -1
  7. package/dist/index.js +72 -87
  8. package/dist/index.js.map +1 -1
  9. package/package.json +8 -8
  10. package/src/components/EditorShell.tsx +6 -0
  11. package/src/components/nle/TimelinePane.tsx +3 -0
  12. package/src/hooks/fetchStubTestUtils.ts +3 -5
  13. package/src/hooks/gsapMutationClient.ts +17 -8
  14. package/src/hooks/gsapScriptCommitHelpers.ts +2 -64
  15. package/src/hooks/newTweenTarget.test.ts +3 -0
  16. package/src/hooks/useDomEditCommitsHelpers.test.ts +116 -0
  17. package/src/hooks/useDomEditCommitsHelpers.ts +44 -0
  18. package/src/hooks/useGsapAnimationOps.test.tsx +181 -1
  19. package/src/hooks/useGsapAnimationOps.ts +13 -12
  20. package/src/hooks/useGsapScriptCommits.test.tsx +19 -0
  21. package/src/hooks/useGsapScriptCommits.ts +6 -32
  22. package/src/player/components/ClipContextMenu.test.tsx +31 -0
  23. package/src/player/components/ClipContextMenu.tsx +4 -1
  24. package/src/player/components/TimelineOverlays.test.ts +18 -0
  25. package/src/player/components/TimelineOverlays.tsx +1 -0
  26. package/src/player/components/TimelineProvider.tsx +1 -0
  27. package/src/player/components/TimelineTypes.ts +1 -0
  28. package/src/player/components/studioShortcuts.ts +3 -1
  29. package/src/player/components/useTimelineProviderState.tsx +2 -0
@@ -1 +1 @@
1
- import{g as P}from"./index-DOO2cYhl.js";function j(c,d){for(var s=0;s<d.length;s++){const a=d[s];if(typeof a!="string"&&!Array.isArray(a)){for(const i in a)if(i!=="default"&&!(i in c)){const l=Object.getOwnPropertyDescriptor(a,i);l&&Object.defineProperty(c,i,l.get?l:{enumerable:!0,get:()=>a[i]})}}}return Object.freeze(Object.defineProperty(c,Symbol.toStringTag,{value:"Module"}))}var v={},w;function k(){if(w)return v;w=1,Object.defineProperty(v,"__esModule",{value:!0}),v.default=d;var c=window.OfflineAudioContext||window.webkitOfflineAudioContext;function d(e){var r=a(e);return r.start(0),[i,y,O(e.sampleRate),s].reduce(function(t,o){return o(t)},r.buffer.getChannelData(0))}function s(e){return e.sort(function(r,t){return t.count-r.count}).splice(0,5)[0].tempo}function a(e){var r=e.length,t=e.numberOfChannels,o=e.sampleRate,n=new c(t,r,o),u=n.createBufferSource();u.buffer=e;var f=n.createBiquadFilter();return f.type="lowpass",u.connect(f),f.connect(n.destination),u}function i(e){for(var r=[],t=.9,o=.3,n=15;r.length<n&&t>=o;)r=l(e,t),t-=.05;if(r.length<n)throw new Error("Could not find enough samples for a reliable detection.");return r}function l(e,r){for(var t=[],o=0,n=e.length;o<n;o+=1)e[o]>r&&(t.push(o),o+=1e4);return t}function y(e){var r=[];return e.forEach(function(t,o){for(var n=function(x){var g=e[o+x]-t,_=r.some(function(h){if(h.interval===g)return h.count+=1});_||r.push({interval:g,count:1})},u=0;u<10;u+=1)n(u)}),r}function O(e){return function(r){var t=[];return r.forEach(function(o){if(o.interval!==0){for(var n=60/(o.interval/e);n<90;)n*=2;for(;n>180;)n/=2;n=Math.round(n);var u=t.some(function(f){if(f.tempo===n)return f.count+=o.count});u||t.push({tempo:n,count:o.count})}}),t}}return v}var p,b;function q(){return b||(b=1,p=k().default),p}var m=q();const A=P(m),D=j({__proto__:null,default:A},[m]);export{D as i};
1
+ import{g as P}from"./index-Ce3Lr-Gt.js";function j(c,d){for(var s=0;s<d.length;s++){const a=d[s];if(typeof a!="string"&&!Array.isArray(a)){for(const i in a)if(i!=="default"&&!(i in c)){const l=Object.getOwnPropertyDescriptor(a,i);l&&Object.defineProperty(c,i,l.get?l:{enumerable:!0,get:()=>a[i]})}}}return Object.freeze(Object.defineProperty(c,Symbol.toStringTag,{value:"Module"}))}var v={},w;function k(){if(w)return v;w=1,Object.defineProperty(v,"__esModule",{value:!0}),v.default=d;var c=window.OfflineAudioContext||window.webkitOfflineAudioContext;function d(e){var r=a(e);return r.start(0),[i,y,O(e.sampleRate),s].reduce(function(t,o){return o(t)},r.buffer.getChannelData(0))}function s(e){return e.sort(function(r,t){return t.count-r.count}).splice(0,5)[0].tempo}function a(e){var r=e.length,t=e.numberOfChannels,o=e.sampleRate,n=new c(t,r,o),u=n.createBufferSource();u.buffer=e;var f=n.createBiquadFilter();return f.type="lowpass",u.connect(f),f.connect(n.destination),u}function i(e){for(var r=[],t=.9,o=.3,n=15;r.length<n&&t>=o;)r=l(e,t),t-=.05;if(r.length<n)throw new Error("Could not find enough samples for a reliable detection.");return r}function l(e,r){for(var t=[],o=0,n=e.length;o<n;o+=1)e[o]>r&&(t.push(o),o+=1e4);return t}function y(e){var r=[];return e.forEach(function(t,o){for(var n=function(x){var g=e[o+x]-t,_=r.some(function(h){if(h.interval===g)return h.count+=1});_||r.push({interval:g,count:1})},u=0;u<10;u+=1)n(u)}),r}function O(e){return function(r){var t=[];return r.forEach(function(o){if(o.interval!==0){for(var n=60/(o.interval/e);n<90;)n*=2;for(;n>180;)n/=2;n=Math.round(n);var u=t.some(function(f){if(f.tempo===n)return f.count+=o.count});u||t.push({tempo:n,count:o.count})}}),t}}return v}var p,b;function q(){return b||(b=1,p=k().default),p}var m=q();const A=P(m),D=j({__proto__:null,default:A},[m]);export{D as i};
package/dist/index.d.ts CHANGED
@@ -1308,6 +1308,7 @@ interface TimelineProps extends TimelineDropCallbacks, TimelineEditOverrides, Ti
1308
1308
  onDuplicateClip?: () => Promise<boolean>;
1309
1309
  canPasteClip?: () => boolean;
1310
1310
  clipMenuItems?: (element: TimelineElement) => readonly TimelineClipMenuItem[];
1311
+ splitShortcut?: string;
1311
1312
  theme?: Partial<TimelineTheme>;
1312
1313
  showAudioEffects?: boolean;
1313
1314
  showKeyframes?: boolean;
@@ -1689,6 +1690,7 @@ interface TimelineOverlaysState {
1689
1690
  onDuplicateClip?: () => Promise<boolean>;
1690
1691
  canPasteClip?: () => boolean;
1691
1692
  clipMenuItems?: TimelineProps["clipMenuItems"];
1693
+ splitShortcut?: TimelineProps["splitShortcut"];
1692
1694
  gapContextMenu: TrackGapContextMenuState | null;
1693
1695
  onDismissGapContextMenu: () => void;
1694
1696
  onCloseTrackGap: () => void;
@@ -2018,6 +2020,7 @@ interface EditorShellProps extends TimelineEditCallbackDeps {
2018
2020
  onDuplicateClip: () => Promise<boolean>;
2019
2021
  canPasteClip: () => boolean;
2020
2022
  clipMenuItems?: TimelineProps["clipMenuItems"];
2023
+ splitShortcut?: TimelineProps["splitShortcut"];
2021
2024
  setCompIdToSrc: (map: Map<string, string>) => void;
2022
2025
  setCompositionLoading: (loading: boolean) => void;
2023
2026
  shouldShowMotionPath: boolean;
@@ -3023,8 +3026,9 @@ interface TimelinePaneProps {
3023
3026
  onDuplicateClip?: () => Promise<boolean>;
3024
3027
  canPasteClip?: () => boolean;
3025
3028
  clipMenuItems?: TimelineProps["clipMenuItems"];
3029
+ splitShortcut?: TimelineProps["splitShortcut"];
3026
3030
  }
3027
- declare function TimelinePane({ timelineToolbar, timelineFooter, timelineOverlay, renderClipContent, onFileDrop, onDeleteElement, onAssetDrop, onBlockDrop, onCompositionDrop, onBlockedEditAttempt, onSelectTimelineElement, onRangeSelect, onCopyClip, onPasteClip, onDuplicateClip, canPasteClip, clipMenuItems, }: TimelinePaneProps): react_jsx_runtime.JSX.Element;
3031
+ declare function TimelinePane({ timelineToolbar, timelineFooter, timelineOverlay, renderClipContent, onFileDrop, onDeleteElement, onAssetDrop, onBlockDrop, onCompositionDrop, onBlockedEditAttempt, onSelectTimelineElement, onRangeSelect, onCopyClip, onPasteClip, onDuplicateClip, canPasteClip, clipMenuItems, splitShortcut, }: TimelinePaneProps): react_jsx_runtime.JSX.Element;
3028
3032
 
3029
3033
  declare function TimelineEditProvider({ value, children, }: {
3030
3034
  value: TimelineEditCallbacks;
package/dist/index.html CHANGED
@@ -14,7 +14,7 @@
14
14
  document.documentElement.dataset.theme = "paper";
15
15
  }
16
16
  </script>
17
- <script type="module" crossorigin src="/assets/index-DOO2cYhl.js"></script>
17
+ <script type="module" crossorigin src="/assets/index-Ce3Lr-Gt.js"></script>
18
18
  <link rel="stylesheet" crossorigin href="/assets/index-t1LH6H5H.css">
19
19
  </head>
20
20
  <body>
package/dist/index.js CHANGED
@@ -626,6 +626,7 @@ var STUDIO_PLAIN_KEYS = {
626
626
  audioGain: "g"
627
627
  };
628
628
  var hintKey = (key2) => key2.toUpperCase();
629
+ var SPLIT_SHORTCUT_HINT = hintKey(STUDIO_PLAIN_KEYS.split);
629
630
  var DEFAULT_SHORTCUT_SECTIONS = [
630
631
  {
631
632
  title: "Playback",
@@ -657,7 +658,7 @@ var DEFAULT_SHORTCUT_SECTIONS = [
657
658
  { key: "\u2318C", label: "Copy element" },
658
659
  { key: "\u2318V", label: "Paste element" },
659
660
  { key: "\u2318X", label: "Cut element" },
660
- { key: hintKey(STUDIO_PLAIN_KEYS.split), label: "Split clip at playhead" },
661
+ { key: SPLIT_SHORTCUT_HINT, label: "Split clip at playhead" },
661
662
  { key: hintKey(STUDIO_PLAIN_KEYS.audioGain), label: "Audio Gain for clips with sound" },
662
663
  { key: "\u21E7Click", label: "Razor tool: split all tracks" },
663
664
  { key: "[", label: "Select clips starting before the playhead" },
@@ -14054,6 +14055,7 @@ function useTimelineProviderState({
14054
14055
  onDuplicateClip,
14055
14056
  canPasteClip,
14056
14057
  clipMenuItems,
14058
+ splitShortcut,
14057
14059
  theme: themeOverrides,
14058
14060
  showAudioEffects = true,
14059
14061
  showKeyframes = true,
@@ -14359,6 +14361,7 @@ function useTimelineProviderState({
14359
14361
  onDuplicateClip,
14360
14362
  canPasteClip,
14361
14363
  clipMenuItems,
14364
+ splitShortcut,
14362
14365
  onSelectElement,
14363
14366
  selectedElementId,
14364
14367
  setRangeSelectionRef,
@@ -17402,17 +17405,20 @@ function readMutationError(value, fallback) {
17402
17405
  if (isRecord2(value) && typeof value.error === "string") return value.error;
17403
17406
  return fallback;
17404
17407
  }
17408
+ function requestGsapMutation(projectId, route, filePath, body) {
17409
+ return studioApiFetch(
17410
+ `/api/projects/${encodeURIComponent(projectId)}/${route}/${encodeURIComponent(filePath)}`,
17411
+ {
17412
+ method: "POST",
17413
+ headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
17414
+ body: JSON.stringify(body)
17415
+ }
17416
+ );
17417
+ }
17405
17418
  async function postGsapMutation(projectId, filePath, mutation, fallback) {
17406
17419
  let response;
17407
17420
  try {
17408
- response = await studioApiFetch(
17409
- `/api/projects/${encodeURIComponent(projectId)}/gsap-mutations/${encodeURIComponent(filePath)}`,
17410
- {
17411
- method: "POST",
17412
- headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
17413
- body: JSON.stringify(mutation)
17414
- }
17415
- );
17421
+ response = await requestGsapMutation(projectId, "gsap-mutations", filePath, mutation);
17416
17422
  } catch (error) {
17417
17423
  throw new GsapPreviewConvergenceError(`${fallback}: mutation outcome unknown`, {
17418
17424
  cause: error
@@ -26250,7 +26256,8 @@ var ClipContextMenu = memo17(function ClipContextMenu2({
26250
26256
  onPaste,
26251
26257
  onDuplicate,
26252
26258
  canPaste,
26253
- hostItems = []
26259
+ hostItems = [],
26260
+ splitShortcut = SPLIT_SHORTCUT_HINT
26254
26261
  }) {
26255
26262
  const menuRef = useContextMenuDismiss(onClose);
26256
26263
  useMenuKeyboardNav(menuRef);
@@ -26294,7 +26301,7 @@ var ClipContextMenu = memo17(function ClipContextMenu2({
26294
26301
  },
26295
26302
  children: [
26296
26303
  /* @__PURE__ */ jsx66("span", { children: splitLabel }),
26297
- /* @__PURE__ */ jsx66("span", { className: "text-neutral-500 text-[10px] ml-3", children: "S" })
26304
+ /* @__PURE__ */ jsx66("span", { className: "text-neutral-500 text-[10px] ml-3", children: splitShortcut })
26298
26305
  ]
26299
26306
  }
26300
26307
  ),
@@ -26669,7 +26676,8 @@ function TimelineClipMenuOverlay() {
26669
26676
  onPaste: overlay.onPasteClip,
26670
26677
  onDuplicate: overlay.onDuplicateClip,
26671
26678
  canPaste: overlay.canPasteClip?.() ?? false,
26672
- hostItems
26679
+ hostItems,
26680
+ splitShortcut: overlay.splitShortcut
26673
26681
  }
26674
26682
  );
26675
26683
  }
@@ -33751,7 +33759,8 @@ function TimelinePane({
33751
33759
  onPasteClip,
33752
33760
  onDuplicateClip,
33753
33761
  canPasteClip,
33754
- clipMenuItems
33762
+ clipMenuItems,
33763
+ splitShortcut
33755
33764
  }) {
33756
33765
  const {
33757
33766
  seek,
@@ -33807,7 +33816,8 @@ function TimelinePane({
33807
33816
  onPasteClip,
33808
33817
  onDuplicateClip,
33809
33818
  canPasteClip,
33810
- clipMenuItems
33819
+ clipMenuItems,
33820
+ splitShortcut
33811
33821
  }
33812
33822
  ) }),
33813
33823
  /* @__PURE__ */ jsx86(AudioMeterStrip, {})
@@ -50179,6 +50189,7 @@ var EditorShell = memo33(function EditorShell2({
50179
50189
  onDuplicateClip,
50180
50190
  canPasteClip,
50181
50191
  clipMenuItems,
50192
+ splitShortcut,
50182
50193
  setCompIdToSrc,
50183
50194
  setCompositionLoading,
50184
50195
  shouldShowMotionPath,
@@ -50270,6 +50281,7 @@ var EditorShell = memo33(function EditorShell2({
50270
50281
  onDuplicateClip,
50271
50282
  canPasteClip,
50272
50283
  clipMenuItems,
50284
+ splitShortcut,
50273
50285
  previewOverlay: /* @__PURE__ */ jsx112(
50274
50286
  PreviewOverlays,
50275
50287
  {
@@ -50305,7 +50317,8 @@ function EditorShellBody({
50305
50317
  onPasteClip,
50306
50318
  onDuplicateClip,
50307
50319
  canPasteClip,
50308
- clipMenuItems
50320
+ clipMenuItems,
50321
+ splitShortcut
50309
50322
  }) {
50310
50323
  const { compositionStack, updateCompositionStack, containerRef } = useNLEContext();
50311
50324
  const seekCaptionTime = useCallback74((time) => {
@@ -50352,6 +50365,7 @@ function EditorShellBody({
50352
50365
  onDuplicateClip,
50353
50366
  canPasteClip,
50354
50367
  clipMenuItems,
50368
+ splitShortcut,
50355
50369
  onSelectTimelineElement,
50356
50370
  timelineFooter: captionEditMode ? /* @__PURE__ */ jsxs82("div", { className: "border-t border-neutral-800/30 shrink-0", style: { height: 60 }, children: [
50357
50371
  /* @__PURE__ */ jsx112("div", { className: "flex items-center gap-1.5 px-2 py-0.5", children: /* @__PURE__ */ jsx112("span", { className: "text-[9px] font-medium text-neutral-500 uppercase tracking-wider", children: "Captions" }) }),
@@ -78034,7 +78048,6 @@ import { useCallback as useCallback127, useRef as useRef144 } from "react";
78034
78048
  import { findUnsafeDomPatchValues as findUnsafeDomPatchValues2 } from "@hyperframes/core/studio-api/finite-mutation";
78035
78049
 
78036
78050
  // src/hooks/gsapScriptCommitHelpers.ts
78037
- import { findUnsafeDomPatchValues } from "@hyperframes/core/studio-api/finite-mutation";
78038
78051
  function ensureElementAddressable(selection) {
78039
78052
  if (selection.id) return { selector: idSelector(selection.id) };
78040
78053
  const el = selection.element;
@@ -78049,7 +78062,6 @@ function ensureElementAddressable(selection) {
78049
78062
  n += 1;
78050
78063
  id = `${tag}-${n}`;
78051
78064
  }
78052
- el.setAttribute("id", id);
78053
78065
  return { selector: idSelector(id), autoId: id };
78054
78066
  }
78055
78067
  var GsapMutationHttpError = class extends Error {
@@ -78092,49 +78104,9 @@ function formatGsapMutationRejectionToast(error) {
78092
78104
  }
78093
78105
  return `Couldn't save animation: ${error.message}`;
78094
78106
  }
78095
- async function assignGsapTargetAutoIdIfNeeded({
78096
- projectId,
78097
- targetPath,
78098
- selection,
78099
- autoId,
78100
- showToast
78101
- }) {
78102
- const patchBody = {
78103
- target: {
78104
- id: selection.id,
78105
- hfId: selection.hfId,
78106
- selector: selection.selector,
78107
- selectorIndex: selection.selectorIndex
78108
- },
78109
- operations: [{ type: "html-attribute", property: "id", value: autoId }]
78110
- };
78111
- const unsafePatchFields = findUnsafeDomPatchValues(patchBody);
78112
- if (unsafePatchFields.length > 0) {
78113
- showToast?.("Couldn't assign element id because the patch contains invalid values", "error");
78114
- return false;
78115
- }
78116
- const res = await studioApiFetch(
78117
- `/api/projects/${encodeURIComponent(projectId)}/file-mutations/patch-element/${encodeURIComponent(targetPath)}`,
78118
- {
78119
- method: "POST",
78120
- headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
78121
- body: JSON.stringify(patchBody)
78122
- }
78123
- );
78124
- if (!res.ok) {
78125
- showToast?.(
78126
- formatGsapMutationRejectionToast(
78127
- new GsapMutationHttpError(res.status, await readJsonResponseBody(res))
78128
- ),
78129
- "error"
78130
- );
78131
- return false;
78132
- }
78133
- const data = await res.json();
78134
- return data.changed === true;
78135
- }
78136
78107
 
78137
78108
  // src/hooks/useDomEditCommitsHelpers.ts
78109
+ import { findUnsafeDomPatchValues } from "@hyperframes/core/studio-api/finite-mutation";
78138
78110
  function formatUnsafeFieldList(fields) {
78139
78111
  return fields.map((field) => field.path).join(", ");
78140
78112
  }
@@ -78238,6 +78210,36 @@ async function postPatchElement(projectId, targetPath, body, showToast) {
78238
78210
  }
78239
78211
  return await response.json();
78240
78212
  }
78213
+ async function assignGsapTargetAutoIdIfNeeded({
78214
+ projectId,
78215
+ targetPath,
78216
+ selection,
78217
+ autoId,
78218
+ showToast
78219
+ }) {
78220
+ const patchBody = {
78221
+ target: {
78222
+ id: selection.id,
78223
+ hfId: selection.hfId,
78224
+ selector: selection.selector,
78225
+ selectorIndex: selection.selectorIndex
78226
+ },
78227
+ operations: [{ type: "ensure-id", property: "id", value: autoId }]
78228
+ };
78229
+ if (findUnsafeDomPatchValues(patchBody).length > 0) {
78230
+ showToast("Couldn't assign element id because the patch contains invalid values", "error");
78231
+ return null;
78232
+ }
78233
+ try {
78234
+ const { elementId: elementId2 } = await postPatchElement(projectId, targetPath, patchBody, showToast);
78235
+ if (elementId2) return elementId2;
78236
+ showToast(`Couldn't assign element id: element not found in ${targetPath}`, "error");
78237
+ return null;
78238
+ } catch (error) {
78239
+ if (error instanceof StudioSaveHttpError && error.alreadyToasted) return null;
78240
+ throw error;
78241
+ }
78242
+ }
78241
78243
  async function writePreparedContent(targetPath, patchedContent, prepare, writeProjectFile, showToast) {
78242
78244
  const preparedContent = prepare(patchedContent, targetPath);
78243
78245
  if (preparedContent === patchedContent) return { content: patchedContent, failed: false };
@@ -80135,11 +80137,13 @@ function useGsapAnimationOps({
80135
80137
  const addGsapAnimation = useCallback134(
80136
80138
  // fallow-ignore-next-line complexity
80137
80139
  async (selection, method, _currentTime) => {
80138
- const { selector, autoId } = ensureElementAddressable(selection);
80140
+ const address = ensureElementAddressable(selection);
80141
+ const { autoId } = address;
80142
+ let selector = address.selector;
80139
80143
  if (autoId) {
80140
80144
  const pid = projectIdRef.current;
80141
- const targetPath = selection.sourceFile || activeCompPath || "index.html";
80142
80145
  if (!pid) return;
80146
+ const targetPath = selection.sourceFile || activeCompPath || "index.html";
80143
80147
  const assign = () => assignGsapTargetAutoIdIfNeeded({
80144
80148
  projectId: pid,
80145
80149
  targetPath,
@@ -80147,8 +80151,10 @@ function useGsapAnimationOps({
80147
80151
  autoId,
80148
80152
  showToast
80149
80153
  });
80150
- const assigned = await (writeProjectFile ? serializeStudioFileMutation(writeProjectFile, targetPath, assign) : assign());
80151
- if (!assigned) return;
80154
+ const savedId = await (writeProjectFile ? serializeStudioFileMutation(writeProjectFile, targetPath, assign) : assign());
80155
+ if (!savedId) return;
80156
+ selection.element.setAttribute("id", savedId);
80157
+ selector = idSelector(savedId);
80152
80158
  }
80153
80159
  const elStart = Number.parseFloat(selection.dataAttributes?.start ?? "0") || 0;
80154
80160
  const elDuration = Number.parseFloat(selection.dataAttributes?.duration ?? "1") || 1;
@@ -80975,29 +80981,8 @@ function useSafeGsapCommitMutation(commitMutation, trackGsapSaveFailure, showToa
80975
80981
  }
80976
80982
 
80977
80983
  // src/hooks/useGsapScriptCommits.ts
80978
- async function mutateGsapScript(projectId, sourceFile, mutation) {
80979
- const res = await studioApiFetch(
80980
- `/api/projects/${encodeURIComponent(projectId)}/gsap-mutations/${encodeURIComponent(sourceFile)}`,
80981
- {
80982
- method: "POST",
80983
- headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
80984
- body: JSON.stringify(mutation)
80985
- }
80986
- );
80987
- if (!res.ok) throw new GsapMutationHttpError(res.status, await readJsonResponseBody(res));
80988
- const result = await res.json();
80989
- if (!result.ok) throw new Error(`Failed to update GSAP in ${sourceFile}`);
80990
- return result;
80991
- }
80992
- async function mutateGsapScriptBatch(projectId, sourceFile, mutations) {
80993
- const res = await studioApiFetch(
80994
- `/api/projects/${encodeURIComponent(projectId)}/gsap-mutations-batch/${encodeURIComponent(sourceFile)}`,
80995
- {
80996
- method: "POST",
80997
- headers: { "Content-Type": "application/json", ...studioWriteHeaders() },
80998
- body: JSON.stringify({ mutations })
80999
- }
81000
- );
80984
+ async function mutateGsapScript(projectId, route, sourceFile, body) {
80985
+ const res = await requestGsapMutation(projectId, route, sourceFile, body);
81001
80986
  if (!res.ok) throw new GsapMutationHttpError(res.status, await readJsonResponseBody(res));
81002
80987
  const result = await res.json();
81003
80988
  if (!result.ok) throw new Error(`Failed to update GSAP in ${sourceFile}`);
@@ -81195,7 +81180,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
81195
81180
  [mutation],
81196
81181
  options,
81197
81182
  showToast,
81198
- () => mutateGsapScript(pid, targetPath, mutation)
81183
+ () => mutateGsapScript(pid, "gsap-mutations", targetPath, mutation)
81199
81184
  );
81200
81185
  if (!result) return;
81201
81186
  trackKeyframeCommit([mutation], result, options);
@@ -81211,7 +81196,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
81211
81196
  mutations,
81212
81197
  options,
81213
81198
  showToast,
81214
- () => mutateGsapScriptBatch(pid, targetPath, mutations)
81199
+ () => mutateGsapScript(pid, "gsap-mutations-batch", targetPath, { mutations })
81215
81200
  );
81216
81201
  if (!result) return;
81217
81202
  trackKeyframeCommit(mutations, result, options, calls.map((call) => call.options));