hyperframes 0.7.28 → 0.7.29

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.
@@ -3,6 +3,7 @@ import {
3
3
  buildDomEditPatchTarget,
4
4
  buildDomEditStylePatchOperation,
5
5
  buildDomEditTextPatchOperation,
6
+ buildTextFieldChildLocator,
6
7
  collectDomEditLayerItems,
7
8
  domEditSelectionToFacts,
8
9
  findElementForSelection,
@@ -12,13 +13,14 @@ import {
12
13
  getDomLayerPatchTarget,
13
14
  isElementComputedVisible,
14
15
  isElementVisibleThroughAncestors,
16
+ isHtmlElement,
15
17
  isTextEditableSelection,
16
18
  readHfId,
17
19
  resolveAllVisualDomEditTargets,
18
20
  resolveDomEditSelection,
19
21
  serializeDomEditTextFields,
20
22
  setCompositionSourceMap
21
- } from "./chunk-AN2EWWK3.js";
23
+ } from "./chunk-JND3XUJL.js";
22
24
 
23
25
  // src/components/nle/NLELayout.tsx
24
26
  import {
@@ -769,7 +771,7 @@ function readTimelineDurationFromDocument(doc) {
769
771
  }
770
772
  return maxEnd;
771
773
  }
772
- function isHtmlElement(el) {
774
+ function isHtmlElement2(el) {
773
775
  const HtmlElementCtor = el.ownerDocument.defaultView?.HTMLElement ?? globalThis.HTMLElement;
774
776
  return typeof HtmlElementCtor !== "undefined" && el instanceof HtmlElementCtor;
775
777
  }
@@ -837,10 +839,10 @@ function getImplicitTimelineLayerLabel(el) {
837
839
  return getTimelineElementDisplayLabel({ tag: el.tagName });
838
840
  }
839
841
  function getTimelineElementSelector(el) {
840
- if (isHtmlElement(el) && el.id) return `#${CSS.escape(el.id)}`;
842
+ if (isHtmlElement2(el) && el.id) return `#${CSS.escape(el.id)}`;
841
843
  const compId = el.getAttribute("data-composition-id");
842
844
  if (compId) return `[data-composition-id="${CSS.escape(compId)}"]`;
843
- if (isHtmlElement(el)) {
845
+ if (isHtmlElement2(el)) {
844
846
  const classes = el.className.split(/\s+/).filter(Boolean);
845
847
  const firstClass = classes.find((className) => className !== "clip") ?? classes[0];
846
848
  if (firstClass) return `.${CSS.escape(firstClass)}`;
@@ -923,7 +925,7 @@ function findTimelineDomNodeForClip(doc, clip, fallbackIndex, usedNodes = /* @__
923
925
  return candidates[fallbackIndex] ?? null;
924
926
  }
925
927
  function isImplicitTimelineLayerCandidate(root, el) {
926
- if (!isHtmlElement(el)) return false;
928
+ if (!isHtmlElement2(el)) return false;
927
929
  if (isTimelineIgnoredElement(el)) return false;
928
930
  if (el.parentElement !== root) return false;
929
931
  const tagName = el.tagName.toLowerCase();
@@ -12763,24 +12765,34 @@ function Section({
12763
12765
  children: /* @__PURE__ */ jsx31("path", { d: "M2 3l3 4 3-4z" })
12764
12766
  }
12765
12767
  );
12766
- return /* @__PURE__ */ jsxs24("section", { className: "min-w-0 border-t border-panel-border", children: [
12767
- /* @__PURE__ */ jsxs24("div", { className: "flex w-full items-center gap-2 px-4 py-2.5", children: [
12768
- /* @__PURE__ */ jsxs24(
12769
- "button",
12770
- {
12771
- type: "button",
12772
- onClick: () => setCollapsed((v) => !v),
12773
- className: "flex min-w-0 flex-1 items-center justify-between gap-2 text-left",
12774
- children: [
12775
- /* @__PURE__ */ jsx31("h3", { className: "text-[12px] font-semibold text-panel-text-1", children: title }),
12776
- collapseIcon
12777
- ]
12778
- }
12779
- ),
12780
- accessory && /* @__PURE__ */ jsx31("div", { className: "flex flex-shrink-0 items-center", children: accessory })
12781
- ] }),
12782
- !collapsed && /* @__PURE__ */ jsx31("div", { className: "px-4 pb-3", children })
12783
- ] });
12768
+ return /* @__PURE__ */ jsxs24(
12769
+ "section",
12770
+ {
12771
+ className: "min-w-0 border-t border-panel-border",
12772
+ "data-panel-section": slugifyPanelSectionTitle(title),
12773
+ children: [
12774
+ /* @__PURE__ */ jsxs24("div", { className: "flex w-full items-center gap-2 px-4 py-2.5", children: [
12775
+ /* @__PURE__ */ jsxs24(
12776
+ "button",
12777
+ {
12778
+ type: "button",
12779
+ onClick: () => setCollapsed((v) => !v),
12780
+ className: "flex min-w-0 flex-1 items-center justify-between gap-2 text-left",
12781
+ children: [
12782
+ /* @__PURE__ */ jsx31("h3", { className: "text-[12px] font-semibold text-panel-text-1", children: title }),
12783
+ collapseIcon
12784
+ ]
12785
+ }
12786
+ ),
12787
+ accessory && /* @__PURE__ */ jsx31("div", { className: "flex flex-shrink-0 items-center", children: accessory })
12788
+ ] }),
12789
+ !collapsed && /* @__PURE__ */ jsx31("div", { className: "px-4 pb-3", children })
12790
+ ]
12791
+ }
12792
+ );
12793
+ }
12794
+ function slugifyPanelSectionTitle(title) {
12795
+ return title.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
12784
12796
  }
12785
12797
 
12786
12798
  // src/components/editor/propertyPanelTransformCommit.ts
@@ -19920,7 +19932,7 @@ var FileTree = memo24(function FileTree2({
19920
19932
  });
19921
19933
 
19922
19934
  // src/App.tsx
19923
- import { useState as useState92, useCallback as useCallback108, useRef as useRef90, useMemo as useMemo37, useEffect as useEffect67, useLayoutEffect as useLayoutEffect3 } from "react";
19935
+ import { useState as useState92, useCallback as useCallback109, useRef as useRef91, useMemo as useMemo37, useEffect as useEffect67, useLayoutEffect as useLayoutEffect3 } from "react";
19924
19936
 
19925
19937
  // src/components/renders/useRenderQueue.ts
19926
19938
  import { useState as useState37, useEffect as useEffect26, useCallback as useCallback32, useRef as useRef35, useMemo as useMemo18 } from "react";
@@ -21271,10 +21283,12 @@ import { useState as useState43, useCallback as useCallback38, useRef as useRef4
21271
21283
  var STUDIO_SAVE_ATTEMPT_PROPERTY = "__studioSaveAttempt";
21272
21284
  var StudioSaveHttpError = class extends Error {
21273
21285
  statusCode;
21274
- constructor(message, statusCode) {
21286
+ alreadyToasted;
21287
+ constructor(message, statusCode, options = {}) {
21275
21288
  super(message);
21276
21289
  this.name = "StudioSaveHttpError";
21277
21290
  this.statusCode = statusCode;
21291
+ this.alreadyToasted = options.alreadyToasted ?? false;
21278
21292
  }
21279
21293
  };
21280
21294
  var StudioSaveNetworkError = class extends Error {
@@ -21358,7 +21372,7 @@ function buildStudioSaveFailureProperties(input) {
21358
21372
  function trackStudioSaveFailure(input) {
21359
21373
  trackStudioEvent("save_failure", buildStudioSaveFailureProperties(input));
21360
21374
  }
21361
- async function createStudioSaveHttpError(response, fallbackMessage) {
21375
+ async function createStudioSaveHttpError(response, fallbackMessage, options = {}) {
21362
21376
  let body = "";
21363
21377
  try {
21364
21378
  body = await response.text();
@@ -21367,7 +21381,7 @@ async function createStudioSaveHttpError(response, fallbackMessage) {
21367
21381
  }
21368
21382
  const detail = body.trim().slice(0, 300);
21369
21383
  const message = detail ? `${fallbackMessage} (${response.status}): ${detail}` : `${fallbackMessage} (${response.status})`;
21370
- return new StudioSaveHttpError(message, response.status);
21384
+ return new StudioSaveHttpError(message, response.status, options);
21371
21385
  }
21372
21386
  async function retryStudioSave(operation, options = {}) {
21373
21387
  const retries = options.retries ?? 3;
@@ -22674,6 +22688,9 @@ function hasUnsafeHtmlAttributeOp(ops) {
22674
22688
  (op) => op.type === "html-attribute" && (!isAllowedHtmlAttribute(op.property) || op.value !== null && !isSafeAttributeValue(op.property, op.value))
22675
22689
  );
22676
22690
  }
22691
+ function hasChildScopedOp(ops) {
22692
+ return ops.some((op) => op.childSelector !== void 0);
22693
+ }
22677
22694
  function hasTextContentOp(ops) {
22678
22695
  return ops.some((op) => op.type === "text-content");
22679
22696
  }
@@ -22697,7 +22714,8 @@ function shouldDeclineTextCutoverForTarget(target, ops) {
22697
22714
  return tag !== null && NON_HTML_CHILD_TAGS.has(tag);
22698
22715
  }
22699
22716
  function shouldUseSdkCutover(flagEnabled, hasSession, hfId, ops) {
22700
- return flagEnabled && hasSession && !!hfId && ops.length > 0 && ops.every((o) => CUTOVER_OP_TYPES.has(o.type)) && !ops.some(mapsToReservedAttr) && !hasUnsafeHtmlAttributeOp(ops);
22717
+ return flagEnabled && hasSession && !!hfId && ops.length > 0 && ops.every((o) => CUTOVER_OP_TYPES.has(o.type)) && // SDK edit ops target only the element hfId; child-scoped patch ops need the server path.
22718
+ !hasChildScopedOp(ops) && !ops.some(mapsToReservedAttr) && !hasUnsafeHtmlAttributeOp(ops);
22701
22719
  }
22702
22720
 
22703
22721
  // src/utils/sdkCutover.ts
@@ -23382,7 +23400,7 @@ function useTimelineEditing({
23382
23400
  }
23383
23401
 
23384
23402
  // src/hooks/useDomEditSession.ts
23385
- import { useCallback as useCallback64 } from "react";
23403
+ import { useCallback as useCallback65 } from "react";
23386
23404
 
23387
23405
  // src/hooks/useAskAgentModal.ts
23388
23406
  import { useState as useState45, useCallback as useCallback42, useRef as useRef45, useEffect as useEffect29 } from "react";
@@ -23540,6 +23558,65 @@ function removePointerEventsOverride(style) {
23540
23558
  } catch {
23541
23559
  }
23542
23560
  }
23561
+ var pointerEventsInheritanceFallbackByDocument = /* @__PURE__ */ new WeakMap();
23562
+ function needsPointerEventsInheritanceFallback(doc, win) {
23563
+ const cached2 = pointerEventsInheritanceFallbackByDocument.get(doc);
23564
+ if (cached2 !== void 0) return cached2;
23565
+ const parent = doc.createElement("div");
23566
+ const child = doc.createElement("div");
23567
+ parent.style.pointerEvents = "none";
23568
+ parent.appendChild(child);
23569
+ const host = doc.body ?? doc.documentElement;
23570
+ if (!host) return false;
23571
+ host.appendChild(parent);
23572
+ const needsFallback = win.getComputedStyle(child).pointerEvents !== "none";
23573
+ parent.remove();
23574
+ pointerEventsInheritanceFallbackByDocument.set(doc, needsFallback);
23575
+ return needsFallback;
23576
+ }
23577
+ function hasOwnPointerEventsOverride(el, win) {
23578
+ const value = win.getComputedStyle(el).pointerEvents;
23579
+ return value !== "" && value !== "inherit" && value !== "unset";
23580
+ }
23581
+ function inheritsPointerEventsNoneFromAncestor(el, win) {
23582
+ let current = el.parentElement;
23583
+ while (current) {
23584
+ if (win.getComputedStyle(current).pointerEvents === "none") {
23585
+ let descendant = el;
23586
+ while (descendant && descendant !== current) {
23587
+ if (hasOwnPointerEventsOverride(descendant, win)) {
23588
+ return win.getComputedStyle(descendant).pointerEvents === "none";
23589
+ }
23590
+ descendant = descendant.parentElement;
23591
+ }
23592
+ return true;
23593
+ }
23594
+ current = current.parentElement;
23595
+ }
23596
+ return false;
23597
+ }
23598
+ function hasAuthorPointerEventsNone(el) {
23599
+ const win = el.ownerDocument.defaultView;
23600
+ if (!win) return false;
23601
+ if (win.getComputedStyle(el).pointerEvents === "none") return true;
23602
+ if (!needsPointerEventsInheritanceFallback(el.ownerDocument, win)) return false;
23603
+ return inheritsPointerEventsNoneFromAncestor(el, win);
23604
+ }
23605
+ function collectPointerEventsNoneTargets(elements) {
23606
+ const disabled = /* @__PURE__ */ new WeakSet();
23607
+ for (const entry of elements) {
23608
+ if (isHtmlElement(entry) && hasAuthorPointerEventsNone(entry)) {
23609
+ disabled.add(entry);
23610
+ }
23611
+ }
23612
+ return disabled;
23613
+ }
23614
+ function filterAuthorInteractiveTargets(elements, activeCompositionPath) {
23615
+ const pointerEventsNoneTargets = collectPointerEventsNoneTargets(elements);
23616
+ return resolveAllVisualDomEditTargets(elements, { activeCompositionPath }).filter(
23617
+ (el) => !pointerEventsNoneTargets.has(el)
23618
+ );
23619
+ }
23543
23620
  function findGroupAtPoint(doc, x, y) {
23544
23621
  let best = null;
23545
23622
  let bestArea = Infinity;
@@ -23577,20 +23654,24 @@ function getPreviewTargetFromPointer(iframe, clientX, clientY, activeComposition
23577
23654
  if (!doc || !win) return null;
23578
23655
  const localPointer = resolvePreviewLocalPointer(iframe, doc, win, clientX, clientY);
23579
23656
  if (!localPointer) return null;
23580
- const overrideStyle = forcePointerEventsAuto(doc);
23657
+ let overrideStyle = forcePointerEventsAuto(doc);
23581
23658
  try {
23582
23659
  if (typeof doc.elementsFromPoint === "function") {
23583
- const candidates = resolveAllVisualDomEditTargets(
23584
- doc.elementsFromPoint(localPointer.x, localPointer.y),
23585
- { activeCompositionPath }
23586
- );
23660
+ const elements = doc.elementsFromPoint(localPointer.x, localPointer.y);
23661
+ removePointerEventsOverride(overrideStyle);
23662
+ overrideStyle = null;
23663
+ const candidates = filterAuthorInteractiveTargets(elements, activeCompositionPath);
23587
23664
  const visualTarget = candidates.find((el) => !isFullBleedTarget(el, localPointer.viewport)) ?? null;
23588
23665
  if (visualTarget) return visualTarget;
23589
23666
  }
23667
+ removePointerEventsOverride(overrideStyle);
23668
+ overrideStyle = null;
23590
23669
  const groupHit = findGroupAtPoint(doc, localPointer.x, localPointer.y);
23591
- if (groupHit && getDomLayerPatchTarget(groupHit, activeCompositionPath)) return groupHit;
23670
+ if (groupHit && !hasAuthorPointerEventsNone(groupHit) && getDomLayerPatchTarget(groupHit, activeCompositionPath))
23671
+ return groupHit;
23592
23672
  const fallback = getEventTargetElement(doc.elementFromPoint(localPointer.x, localPointer.y));
23593
23673
  if (!fallback || !getDomLayerPatchTarget(fallback, activeCompositionPath)) return null;
23674
+ if (hasAuthorPointerEventsNone(fallback)) return null;
23594
23675
  if (!isElementComputedVisible(fallback)) return null;
23595
23676
  if (isFullBleedTarget(fallback, localPointer.viewport)) return null;
23596
23677
  return fallback;
@@ -23610,15 +23691,21 @@ function getAllPreviewTargetsFromPointer(iframe, clientX, clientY, activeComposi
23610
23691
  if (!doc || !win) return [];
23611
23692
  const localPointer = resolvePreviewLocalPointer(iframe, doc, win, clientX, clientY);
23612
23693
  if (!localPointer) return [];
23613
- const overrideStyle = forcePointerEventsAuto(doc);
23694
+ let overrideStyle = forcePointerEventsAuto(doc);
23614
23695
  try {
23615
23696
  if (typeof doc.elementsFromPoint === "function") {
23616
- return resolveAllVisualDomEditTargets(doc.elementsFromPoint(localPointer.x, localPointer.y), {
23617
- activeCompositionPath
23618
- }).filter((el) => !isFullBleedTarget(el, localPointer.viewport));
23697
+ const elements = doc.elementsFromPoint(localPointer.x, localPointer.y);
23698
+ removePointerEventsOverride(overrideStyle);
23699
+ overrideStyle = null;
23700
+ return filterAuthorInteractiveTargets(elements, activeCompositionPath).filter(
23701
+ (el) => !isFullBleedTarget(el, localPointer.viewport)
23702
+ );
23619
23703
  }
23620
23704
  const fallback = getEventTargetElement(doc.elementFromPoint(localPointer.x, localPointer.y));
23621
23705
  if (!fallback || !getDomLayerPatchTarget(fallback, activeCompositionPath)) return [];
23706
+ removePointerEventsOverride(overrideStyle);
23707
+ overrideStyle = null;
23708
+ if (hasAuthorPointerEventsNone(fallback)) return [];
23622
23709
  if (!isElementComputedVisible(fallback)) return [];
23623
23710
  if (isFullBleedTarget(fallback, localPointer.viewport)) return [];
23624
23711
  return [fallback];
@@ -23730,6 +23817,7 @@ function useDomSelection({
23730
23817
  const domEditGroupSelectionsRef = useRef46(domEditGroupSelections);
23731
23818
  const domEditHoverSelectionRef = useRef46(domEditHoverSelection);
23732
23819
  const activeGroupElementRef = useRef46(activeGroupElement);
23820
+ const compositionIdentityRef = useRef46({ activeCompPath, projectId });
23733
23821
  domEditSelectionRef.current = domEditSelection;
23734
23822
  domEditGroupSelectionsRef.current = domEditGroupSelections;
23735
23823
  domEditHoverSelectionRef.current = domEditHoverSelection;
@@ -23791,6 +23879,8 @@ function useDomSelection({
23791
23879
  }, [applyDomSelection]);
23792
23880
  const setActiveGroupElement = useCallback43(
23793
23881
  (el) => {
23882
+ if (activeGroupElementRef.current === el) return;
23883
+ activeGroupElementRef.current = el;
23794
23884
  setActiveGroupElementState(el);
23795
23885
  applyDomSelection(null, { revealPanel: false });
23796
23886
  },
@@ -23963,6 +24053,14 @@ function useDomSelection({
23963
24053
  useEffect30(() => {
23964
24054
  updateDomEditHoverSelection(null);
23965
24055
  }, [activeCompPath, projectId, previewIframe, refreshKey, updateDomEditHoverSelection]);
24056
+ useEffect30(() => {
24057
+ const previous = compositionIdentityRef.current;
24058
+ if (previous.activeCompPath === activeCompPath && previous.projectId === projectId) return;
24059
+ compositionIdentityRef.current = { activeCompPath, projectId };
24060
+ activeGroupElementRef.current = null;
24061
+ setActiveGroupElementState(null);
24062
+ applyDomSelection(null, { revealPanel: false });
24063
+ }, [activeCompPath, projectId, applyDomSelection]);
23966
24064
  useEffect30(() => {
23967
24065
  if (!domEditHoverSelection) return;
23968
24066
  const shouldClear = captionEditMode || domEditSelectionsTargetSame(domEditHoverSelection, domEditSelection) || domEditSelectionInGroup(domEditGroupSelections, domEditHoverSelection) || !domEditHoverSelection.element.isConnected;
@@ -24076,6 +24174,15 @@ function usePreviewInteraction({
24076
24174
  }) {
24077
24175
  const cycleRef = useRef47(null);
24078
24176
  const lastDownRef = useRef47(null);
24177
+ const pausePreviewPlayback = useCallback44(() => {
24178
+ const pausedTime = pauseStudioPreviewPlayback(previewIframeRef.current);
24179
+ const playerStore = usePlayerStore.getState();
24180
+ playerStore.setIsPlaying(false);
24181
+ if (pausedTime != null) {
24182
+ playerStore.setCurrentTime(pausedTime);
24183
+ liveTime.notify(pausedTime);
24184
+ }
24185
+ }, [previewIframeRef]);
24079
24186
  const handlePreviewCanvasMouseDown = useCallback44(
24080
24187
  // fallow-ignore-next-line complexity
24081
24188
  async (e, options) => {
@@ -24084,8 +24191,15 @@ function usePreviewInteraction({
24084
24191
  const lastDown = lastDownRef.current;
24085
24192
  const isDoubleClick = e.detail >= 2 || lastDown != null && downTs - lastDown.t < DOUBLE_CLICK_MS && Math.hypot(e.clientX - lastDown.x, e.clientY - lastDown.y) < DOUBLE_CLICK_RADIUS_PX;
24086
24193
  lastDownRef.current = { t: downTs, x: e.clientX, y: e.clientY };
24194
+ const wasPlaying = usePlayerStore.getState().isPlaying;
24195
+ pausePreviewPlayback();
24196
+ const resumeIfNothingSelected = () => {
24197
+ if (wasPlaying) usePlayerStore.getState().setIsPlaying(true);
24198
+ };
24087
24199
  if (isDoubleClick && !e.shiftKey) {
24088
24200
  const hit = await resolveDomSelectionFromPreviewPoint(e.clientX, e.clientY);
24201
+ const cycle = cycleRef.current;
24202
+ const hasStackCycleAtSpot = cycle !== null && cycle.candidates.length > 1 && Math.hypot(e.clientX - cycle.x, e.clientY - cycle.y) < CYCLE_RADIUS_PX && downTs - cycle.at < CYCLE_WINDOW_MS;
24089
24203
  if (hit?.element.hasAttribute("data-hf-group")) {
24090
24204
  e.preventDefault();
24091
24205
  e.stopPropagation();
@@ -24098,6 +24212,13 @@ function usePreviewInteraction({
24098
24212
  applyDomSelection(child ?? hit);
24099
24213
  return;
24100
24214
  }
24215
+ if (hit && !hasStackCycleAtSpot && !hit.element.hasAttribute("data-composition-src") && !hit.element.hasAttribute("data-composition-file")) {
24216
+ e.preventDefault();
24217
+ e.stopPropagation();
24218
+ cycleRef.current = null;
24219
+ applyDomSelection(hit);
24220
+ return;
24221
+ }
24101
24222
  }
24102
24223
  const now = Date.now();
24103
24224
  const prev = cycleRef.current;
@@ -24108,8 +24229,11 @@ function usePreviewInteraction({
24108
24229
  cycleRef.current = null;
24109
24230
  const nextSelection2 = await resolveDomSelectionFromPreviewPoint(e.clientX, e.clientY, {
24110
24231
  preferClipAncestor: options?.preferClipAncestor ?? false
24111
- });
24112
- if (!nextSelection2) return;
24232
+ }) ?? options?.hoverSelection ?? null;
24233
+ if (!nextSelection2) {
24234
+ resumeIfNothingSelected();
24235
+ return;
24236
+ }
24113
24237
  e.preventDefault();
24114
24238
  e.stopPropagation();
24115
24239
  applyDomSelection(nextSelection2, { additive: true });
@@ -24134,9 +24258,11 @@ function usePreviewInteraction({
24134
24258
  activeGroupElement: null
24135
24259
  });
24136
24260
  }
24261
+ nextSelection = nextSelection ?? options?.hoverSelection ?? null;
24137
24262
  if (!nextSelection) {
24138
24263
  cycleRef.current = null;
24139
24264
  applyDomSelection(null, { revealPanel: false });
24265
+ resumeIfNothingSelected();
24140
24266
  return;
24141
24267
  }
24142
24268
  e.preventDefault();
@@ -24153,6 +24279,7 @@ function usePreviewInteraction({
24153
24279
  captionEditMode,
24154
24280
  compositionLoading,
24155
24281
  onClickToSource,
24282
+ pausePreviewPlayback,
24156
24283
  resolveAllDomSelectionsFromPreviewPoint,
24157
24284
  resolveDomSelectionFromPreviewPoint,
24158
24285
  setActiveGroupElement
@@ -24192,14 +24319,8 @@ function usePreviewInteraction({
24192
24319
  [showToast]
24193
24320
  );
24194
24321
  const handleDomManualDragStart = useCallback44(() => {
24195
- const pausedTime = pauseStudioPreviewPlayback(previewIframeRef.current);
24196
- const playerStore = usePlayerStore.getState();
24197
- playerStore.setIsPlaying(false);
24198
- if (pausedTime != null) {
24199
- playerStore.setCurrentTime(pausedTime);
24200
- liveTime.notify(pausedTime);
24201
- }
24202
- }, [previewIframeRef]);
24322
+ pausePreviewPlayback();
24323
+ }, [pausePreviewPlayback]);
24203
24324
  return {
24204
24325
  handlePreviewCanvasMouseDown,
24205
24326
  handlePreviewCanvasPointerMove,
@@ -24210,7 +24331,7 @@ function usePreviewInteraction({
24210
24331
  }
24211
24332
 
24212
24333
  // src/hooks/useDomEditCommits.ts
24213
- import { useCallback as useCallback49, useRef as useRef49 } from "react";
24334
+ import { useCallback as useCallback50, useRef as useRef50 } from "react";
24214
24335
  import { findUnsafeDomPatchValues as findUnsafeDomPatchValues2 } from "@hyperframes/core/studio-api/finite-mutation";
24215
24336
 
24216
24337
  // src/utils/studioFontHelpers.ts
@@ -24282,6 +24403,66 @@ ${css2}
24282
24403
  ${html2}`;
24283
24404
  }
24284
24405
 
24406
+ // src/hooks/domEditPersistFailure.ts
24407
+ var DomEditPersistUnresolvableError = class extends Error {
24408
+ constructor(targetPath) {
24409
+ super(`Couldn't find this element in the source file (${targetPath})`);
24410
+ this.name = "DomEditPersistUnresolvableError";
24411
+ }
24412
+ };
24413
+ var DomEditPersistUnsafeValueError = class extends Error {
24414
+ alreadyToasted;
24415
+ constructor(message, options = {}) {
24416
+ super(message);
24417
+ this.name = "DomEditPersistUnsafeValueError";
24418
+ this.alreadyToasted = options.alreadyToasted ?? false;
24419
+ }
24420
+ };
24421
+ var DomEditPersistUnsupportedTextStructureError = class extends Error {
24422
+ constructor() {
24423
+ super("Couldn't save this text structure change");
24424
+ this.name = "DomEditPersistUnsupportedTextStructureError";
24425
+ }
24426
+ };
24427
+ function summarizeOperations(operations) {
24428
+ return operations.map((op) => `${op.type}:${op.property}`).join(", ");
24429
+ }
24430
+ function getTargetTuple(selection) {
24431
+ return {
24432
+ hfId: selection.hfId,
24433
+ id: selection.id,
24434
+ selector: selection.selector,
24435
+ selectorIndex: selection.selectorIndex,
24436
+ sourceFile: selection.sourceFile
24437
+ };
24438
+ }
24439
+ function getErrorDetail(error) {
24440
+ return error instanceof Error ? error.message : String(error);
24441
+ }
24442
+ function getSelectionLabel(selection) {
24443
+ return selection.label || selection.selector || selection.id || "this element";
24444
+ }
24445
+ function reportDomEditPersistFailure(selection, operations, error, showToast) {
24446
+ const detail = getErrorDetail(error);
24447
+ console.warn("[Studio] DOM edit persist failed", {
24448
+ target: getTargetTuple(selection),
24449
+ operations: summarizeOperations(operations),
24450
+ error: detail
24451
+ });
24452
+ const wasAlreadyToasted = (error instanceof DomEditPersistUnsafeValueError || error instanceof StudioSaveHttpError) && error.alreadyToasted;
24453
+ if (wasAlreadyToasted) {
24454
+ return;
24455
+ }
24456
+ showToast(`Couldn't save "${getSelectionLabel(selection)}": ${detail}`, "error");
24457
+ }
24458
+ function warnDomEditPersistNoOp(selection, operations) {
24459
+ console.warn("[Studio] DOM edit persist no-op", {
24460
+ target: getTargetTuple(selection),
24461
+ operations: summarizeOperations(operations),
24462
+ detail: "Server matched the target but reported no change even though the client believed the value changed."
24463
+ });
24464
+ }
24465
+
24285
24466
  // src/hooks/useDomEditPositionPatchCommit.ts
24286
24467
  import { useCallback as useCallback45 } from "react";
24287
24468
  function useDomEditPositionPatchCommit({
@@ -24319,114 +24500,154 @@ function useDomEditPositionPatchCommit({
24319
24500
  }
24320
24501
 
24321
24502
  // src/hooks/useDomEditTextCommits.ts
24503
+ import { useCallback as useCallback47, useRef as useRef49 } from "react";
24504
+
24505
+ // src/hooks/domEditTextFieldCommitOps.ts
24506
+ function hasSameKeysInSamePositions(originalFields, nextFields) {
24507
+ return originalFields.every((field, index) => nextFields[index]?.key === field.key);
24508
+ }
24509
+ function inlineStyleValue(styles, property) {
24510
+ return Object.prototype.hasOwnProperty.call(styles, property) ? styles[property] : null;
24511
+ }
24512
+ function inlineStyleProperties(originalStyles, nextStyles) {
24513
+ return Array.from(/* @__PURE__ */ new Set([...Object.keys(originalStyles), ...Object.keys(nextStyles)]));
24514
+ }
24515
+ function buildTextFieldChildOperations(originalFields, nextFields) {
24516
+ if (originalFields.length !== nextFields.length) return null;
24517
+ if (!hasSameKeysInSamePositions(originalFields, nextFields)) return null;
24518
+ if (nextFields.some((field) => field.source === "text-node")) return null;
24519
+ if (nextFields.some((field) => field.source !== "child")) return null;
24520
+ if (originalFields.some((field) => field.source !== "child")) return null;
24521
+ const originalByKey = new Map(originalFields.map((field) => [field.key, field]));
24522
+ const operations = [];
24523
+ for (const nextField of nextFields) {
24524
+ const originalField = originalByKey.get(nextField.key);
24525
+ const locator = buildTextFieldChildLocator(originalFields, nextField.key);
24526
+ if (!originalField || !locator) return null;
24527
+ if (nextField.value !== originalField.value) {
24528
+ operations.push(buildDomEditTextPatchOperation(nextField.value, locator));
24529
+ }
24530
+ for (const property of inlineStyleProperties(
24531
+ originalField.inlineStyles,
24532
+ nextField.inlineStyles
24533
+ )) {
24534
+ const originalValue = inlineStyleValue(originalField.inlineStyles, property);
24535
+ const nextValue = inlineStyleValue(nextField.inlineStyles, property);
24536
+ if (nextValue !== originalValue) {
24537
+ operations.push(buildDomEditStylePatchOperation(property, nextValue, locator));
24538
+ }
24539
+ }
24540
+ }
24541
+ return operations;
24542
+ }
24543
+
24544
+ // src/hooks/domEditCommitRunner.ts
24545
+ function bumpDomEditCommitVersion(versionRef) {
24546
+ const commitVersion = versionRef.current + 1;
24547
+ versionRef.current = commitVersion;
24548
+ return () => versionRef.current === commitVersion;
24549
+ }
24550
+ function bumpDomEditCommitMapVersion(versionMap, versionKey) {
24551
+ const commitVersion = (versionMap.get(versionKey) ?? 0) + 1;
24552
+ versionMap.set(versionKey, commitVersion);
24553
+ return () => versionMap.get(versionKey) === commitVersion;
24554
+ }
24555
+ async function runDomEditCommit(config) {
24556
+ config.capture();
24557
+ config.apply();
24558
+ try {
24559
+ await config.persist();
24560
+ } catch (error) {
24561
+ if (config.shouldRevert(error)) {
24562
+ config.revert();
24563
+ }
24564
+ config.onError(error);
24565
+ }
24566
+ if (!config.shouldResync()) return;
24567
+ await config.resync();
24568
+ }
24569
+
24570
+ // src/hooks/useDomEditAttributeCommits.ts
24322
24571
  import { useCallback as useCallback46, useRef as useRef48 } from "react";
24323
- function applyPreviewAttribute(doc, selection, activeCompPath, attr, value, options = {}) {
24324
- if (!doc) return;
24325
- const el = findElementForSelection(doc, selection, activeCompPath);
24326
- if (!el) return;
24327
- const fullAttr = options.prefixData && !attr.startsWith("data-") ? `data-${attr}` : attr;
24328
- if (value === null || value === "" || options.removeFalse && value === "false") {
24572
+ function resolveFullAttrName(attr, prefixData) {
24573
+ return prefixData && !attr.startsWith("data-") ? `data-${attr}` : attr;
24574
+ }
24575
+ function setOrRemovePreviewAttribute(el, fullAttr, value) {
24576
+ if (value === null) {
24329
24577
  el.removeAttribute(fullAttr);
24330
24578
  } else {
24331
24579
  el.setAttribute(fullAttr, value);
24332
24580
  }
24333
24581
  }
24334
- function useDomEditTextCommits({
24582
+ function findPreviewAttributeElement(doc, selection, activeCompPath) {
24583
+ if (!doc) return null;
24584
+ return findElementForSelection(doc, selection, activeCompPath);
24585
+ }
24586
+ function captureAttributeElement(doc, selection, activeCompPath, fullAttr) {
24587
+ const el = findPreviewAttributeElement(doc, selection, activeCompPath);
24588
+ if (!el) return null;
24589
+ return { element: el, previousValue: el.getAttribute(fullAttr) };
24590
+ }
24591
+ function useDomEditAttributeCommits({
24335
24592
  activeCompPath,
24336
24593
  previewIframeRef,
24594
+ showToast,
24337
24595
  domEditSelection,
24338
- applyDomSelection,
24339
24596
  refreshDomEditSelectionFromPreview,
24340
- buildDomSelectionFromTarget,
24341
- persistDomEditOperations,
24342
- resolveImportedFontAsset
24597
+ persistDomEditOperations
24343
24598
  }) {
24344
- const domTextCommitVersionRef = useRef48(0);
24345
- const handleDomStyleCommit = useCallback46(
24346
- async (property, value) => {
24347
- if (!domEditSelection) return;
24348
- if (isManualGeometryStyleProperty(property)) return;
24349
- if (!domEditSelection.capabilities.canEditStyles) return;
24350
- const importedFont = property === "font-family" ? resolveImportedFontAsset(value) : null;
24351
- const iframe = previewIframeRef.current;
24352
- const doc = iframe?.contentDocument;
24353
- if (doc) {
24354
- const el = findElementForSelection(doc, domEditSelection, activeCompPath);
24355
- if (el) {
24356
- el.style.setProperty(property, normalizeDomEditStyleValue(property, value));
24357
- if (property === "font-family") {
24358
- injectPreviewGoogleFont(doc, value);
24359
- if (importedFont) injectPreviewImportedFont(doc, importedFont);
24360
- }
24361
- if (property === "background-image" && isImageBackgroundValue(value)) {
24362
- el.style.setProperty("background-position", "center");
24363
- el.style.setProperty("background-repeat", "no-repeat");
24364
- el.style.setProperty("background-size", "contain");
24365
- }
24366
- }
24367
- }
24368
- const operations = [
24369
- buildDomEditStylePatchOperation(property, normalizeDomEditStyleValue(property, value))
24370
- ];
24371
- if (property === "background-image" && isImageBackgroundValue(value)) {
24372
- operations.push(
24373
- buildDomEditStylePatchOperation("background-position", "center"),
24374
- buildDomEditStylePatchOperation("background-repeat", "no-repeat"),
24375
- buildDomEditStylePatchOperation("background-size", "contain")
24376
- );
24377
- }
24378
- const skipRefresh = property !== "z-index";
24379
- try {
24380
- await persistDomEditOperations(domEditSelection, operations, {
24381
- label: "Edit layer style",
24382
- skipRefresh,
24383
- prepareContent: importedFont ? (html2, sourceFile) => ensureImportedFontFace(html2, importedFont, sourceFile) : void 0
24384
- });
24385
- } catch {
24386
- }
24387
- refreshDomEditSelectionFromPreview(domEditSelection);
24388
- },
24389
- [
24390
- activeCompPath,
24391
- domEditSelection,
24392
- persistDomEditOperations,
24393
- refreshDomEditSelectionFromPreview,
24394
- resolveImportedFontAsset,
24395
- previewIframeRef
24396
- ]
24397
- );
24599
+ const domAttributeCommitVersionRef = useRef48(/* @__PURE__ */ new Map());
24398
24600
  const commitDataAttribute = useCallback46(
24399
24601
  async (attr, value, options) => {
24400
24602
  if (!domEditSelection) return;
24401
24603
  const iframe = previewIframeRef.current;
24402
- applyPreviewAttribute(
24403
- iframe?.contentDocument,
24404
- domEditSelection,
24405
- activeCompPath,
24406
- attr,
24407
- value,
24408
- {
24409
- prefixData: true
24410
- }
24604
+ const fullAttr = resolveFullAttrName(attr, true);
24605
+ const commitKey = `${options.coalescePrefix}:${attr}:${getDomEditTargetKey(domEditSelection)}`;
24606
+ const isLatestCommit = bumpDomEditCommitMapVersion(
24607
+ domAttributeCommitVersionRef.current,
24608
+ commitKey
24411
24609
  );
24412
24610
  const op = { type: "attribute", property: attr, value };
24413
- try {
24414
- await persistDomEditOperations(domEditSelection, [op], {
24611
+ let editedElement = null;
24612
+ let previousValue = null;
24613
+ await runDomEditCommit({
24614
+ capture: () => {
24615
+ const captured = captureAttributeElement(
24616
+ iframe?.contentDocument,
24617
+ domEditSelection,
24618
+ activeCompPath,
24619
+ fullAttr
24620
+ );
24621
+ if (!captured) return;
24622
+ editedElement = captured.element;
24623
+ previousValue = captured.previousValue;
24624
+ },
24625
+ apply: () => {
24626
+ if (!editedElement) return;
24627
+ const nextValue = value === null || value === "" ? null : value;
24628
+ setOrRemovePreviewAttribute(editedElement, fullAttr, nextValue);
24629
+ },
24630
+ persist: () => persistDomEditOperations(domEditSelection, [op], {
24415
24631
  label: options.label,
24416
- coalesceKey: `${options.coalescePrefix}:${attr}:${getDomEditTargetKey(domEditSelection)}`,
24632
+ coalesceKey: commitKey,
24417
24633
  skipRefresh: options.skipRefresh
24418
- });
24419
- } catch {
24420
- }
24421
- if (options.refreshAfter) {
24422
- refreshDomEditSelectionFromPreview(domEditSelection);
24423
- }
24634
+ }),
24635
+ shouldRevert: () => isLatestCommit(),
24636
+ revert: () => {
24637
+ if (!editedElement) return;
24638
+ setOrRemovePreviewAttribute(editedElement, fullAttr, previousValue);
24639
+ },
24640
+ onError: (error) => reportDomEditPersistFailure(domEditSelection, [op], error, showToast),
24641
+ shouldResync: () => isLatestCommit() && !!options.refreshAfter,
24642
+ resync: () => refreshDomEditSelectionFromPreview(domEditSelection)
24643
+ });
24424
24644
  },
24425
24645
  [
24426
24646
  activeCompPath,
24427
24647
  domEditSelection,
24428
24648
  persistDomEditOperations,
24429
24649
  refreshDomEditSelectionFromPreview,
24650
+ showToast,
24430
24651
  previewIframeRef
24431
24652
  ]
24432
24653
  );
@@ -24436,7 +24657,6 @@ function useDomEditTextCommits({
24436
24657
  label: `Edit ${attr.replace(/-/g, " ")}`,
24437
24658
  coalescePrefix: "attr",
24438
24659
  skipRefresh: false,
24439
- warningMessage: "[Studio] Attribute persist failed:",
24440
24660
  refreshAfter: true
24441
24661
  });
24442
24662
  },
@@ -24447,8 +24667,7 @@ function useDomEditTextCommits({
24447
24667
  await commitDataAttribute(attr, value, {
24448
24668
  label: `Edit ${attr.replace(/^(data-)?/, "").replace(/-/g, " ")}`,
24449
24669
  coalescePrefix: "attr-live",
24450
- skipRefresh: true,
24451
- warningMessage: "[Studio] Live attribute persist failed:"
24670
+ skipRefresh: true
24452
24671
  });
24453
24672
  },
24454
24673
  [commitDataAttribute]
@@ -24457,76 +24676,242 @@ function useDomEditTextCommits({
24457
24676
  async (attr, value) => {
24458
24677
  if (!domEditSelection) return;
24459
24678
  const iframe = previewIframeRef.current;
24460
- applyPreviewAttribute(
24461
- iframe?.contentDocument,
24462
- domEditSelection,
24463
- activeCompPath,
24464
- attr,
24465
- value,
24466
- {
24467
- removeFalse: true
24468
- }
24679
+ const commitKey = `html-attr:${attr}:${getDomEditTargetKey(domEditSelection)}`;
24680
+ const isLatestCommit = bumpDomEditCommitMapVersion(
24681
+ domAttributeCommitVersionRef.current,
24682
+ commitKey
24469
24683
  );
24470
24684
  const op = { type: "html-attribute", property: attr, value };
24471
- try {
24472
- await persistDomEditOperations(domEditSelection, [op], {
24685
+ let editedElement = null;
24686
+ let previousValue = null;
24687
+ await runDomEditCommit({
24688
+ capture: () => {
24689
+ const captured = captureAttributeElement(
24690
+ iframe?.contentDocument,
24691
+ domEditSelection,
24692
+ activeCompPath,
24693
+ attr
24694
+ );
24695
+ if (!captured) return;
24696
+ editedElement = captured.element;
24697
+ previousValue = captured.previousValue;
24698
+ },
24699
+ apply: () => {
24700
+ if (!editedElement) return;
24701
+ const nextValue = value === null || value === "false" ? null : value;
24702
+ setOrRemovePreviewAttribute(editedElement, attr, nextValue);
24703
+ },
24704
+ persist: () => persistDomEditOperations(domEditSelection, [op], {
24473
24705
  label: `Edit ${attr}`,
24474
- coalesceKey: `html-attr:${attr}:${getDomEditTargetKey(domEditSelection)}`,
24706
+ coalesceKey: commitKey,
24475
24707
  skipRefresh: false
24476
- });
24477
- } catch {
24478
- }
24479
- refreshDomEditSelectionFromPreview(domEditSelection);
24708
+ }),
24709
+ shouldRevert: () => isLatestCommit(),
24710
+ revert: () => {
24711
+ if (!editedElement) return;
24712
+ setOrRemovePreviewAttribute(editedElement, attr, previousValue);
24713
+ },
24714
+ onError: (error) => reportDomEditPersistFailure(domEditSelection, [op], error, showToast),
24715
+ shouldResync: () => isLatestCommit(),
24716
+ resync: () => refreshDomEditSelectionFromPreview(domEditSelection)
24717
+ });
24718
+ },
24719
+ [
24720
+ activeCompPath,
24721
+ domEditSelection,
24722
+ persistDomEditOperations,
24723
+ refreshDomEditSelectionFromPreview,
24724
+ showToast,
24725
+ previewIframeRef
24726
+ ]
24727
+ );
24728
+ return {
24729
+ handleDomAttributeCommit,
24730
+ handleDomAttributeLiveCommit,
24731
+ handleDomHtmlAttributeCommit
24732
+ };
24733
+ }
24734
+
24735
+ // src/hooks/useDomEditTextCommits.ts
24736
+ function buildDomStyleCommitOperations(property, value, isImageBackgroundCommit) {
24737
+ const operations = [
24738
+ buildDomEditStylePatchOperation(property, normalizeDomEditStyleValue(property, value))
24739
+ ];
24740
+ if (isImageBackgroundCommit) {
24741
+ operations.push(
24742
+ buildDomEditStylePatchOperation("background-position", "center"),
24743
+ buildDomEditStylePatchOperation("background-repeat", "no-repeat"),
24744
+ buildDomEditStylePatchOperation("background-size", "contain")
24745
+ );
24746
+ }
24747
+ return operations;
24748
+ }
24749
+ function buildNextDomTextFields(textFields, value, fieldKey) {
24750
+ if (textFields.length === 0) return [];
24751
+ return textFields.map((field) => field.key === fieldKey ? { ...field, value } : field);
24752
+ }
24753
+ function planDomTextCommit(originalTextFields, nextTextFields, plainTextContent) {
24754
+ const usesSerializedTextFields = nextTextFields.length > 1 || nextTextFields.some((field) => field.source === "child");
24755
+ const nextContent = usesSerializedTextFields ? serializeDomEditTextFields(nextTextFields) : plainTextContent;
24756
+ const childOperations = usesSerializedTextFields ? buildTextFieldChildOperations(originalTextFields, nextTextFields) : null;
24757
+ const operations = childOperations ?? (usesSerializedTextFields ? [] : [buildDomEditTextPatchOperation(nextContent)]);
24758
+ return {
24759
+ usesSerializedTextFields,
24760
+ nextContent,
24761
+ childOperations,
24762
+ operations
24763
+ };
24764
+ }
24765
+ async function resyncDomTextSelectionFromPreview(doc, selection, activeCompPath, buildDomSelectionFromTarget, applyDomSelection) {
24766
+ if (!doc) return;
24767
+ const refreshed = findElementForSelection(doc, selection, activeCompPath);
24768
+ if (!refreshed) return;
24769
+ const nextSelection = await buildDomSelectionFromTarget(refreshed);
24770
+ if (!nextSelection) return;
24771
+ applyDomSelection(nextSelection, { revealPanel: false, preserveGroup: true });
24772
+ }
24773
+ function useDomEditTextCommits({
24774
+ activeCompPath,
24775
+ previewIframeRef,
24776
+ showToast,
24777
+ domEditSelection,
24778
+ applyDomSelection,
24779
+ refreshDomEditSelectionFromPreview,
24780
+ buildDomSelectionFromTarget,
24781
+ persistDomEditOperations,
24782
+ resolveImportedFontAsset
24783
+ }) {
24784
+ const domTextCommitVersionRef = useRef49(0);
24785
+ const domStyleCommitVersionRef = useRef49(/* @__PURE__ */ new Map());
24786
+ const { handleDomAttributeCommit, handleDomAttributeLiveCommit, handleDomHtmlAttributeCommit } = useDomEditAttributeCommits({
24787
+ activeCompPath,
24788
+ previewIframeRef,
24789
+ showToast,
24790
+ domEditSelection,
24791
+ refreshDomEditSelectionFromPreview,
24792
+ persistDomEditOperations
24793
+ });
24794
+ const handleDomStyleCommit = useCallback47(
24795
+ async (property, value) => {
24796
+ if (!domEditSelection) return;
24797
+ if (isManualGeometryStyleProperty(property)) return;
24798
+ if (!domEditSelection.capabilities.canEditStyles) return;
24799
+ const styleCommitKey = `${getDomEditTargetKey(domEditSelection)}:${property}`;
24800
+ const isLatestStyleCommit = bumpDomEditCommitMapVersion(
24801
+ domStyleCommitVersionRef.current,
24802
+ styleCommitKey
24803
+ );
24804
+ const importedFont = property === "font-family" ? resolveImportedFontAsset(value) : null;
24805
+ const iframe = previewIframeRef.current;
24806
+ const doc = iframe?.contentDocument;
24807
+ const normalizedValue = normalizeDomEditStyleValue(property, value);
24808
+ const isImageBackgroundCommit = property === "background-image" && isImageBackgroundValue(value);
24809
+ let editedElement = null;
24810
+ let previousInlineValue = null;
24811
+ const operations = buildDomStyleCommitOperations(property, value, isImageBackgroundCommit);
24812
+ const skipRefresh = property !== "z-index";
24813
+ await runDomEditCommit({
24814
+ capture: () => {
24815
+ if (!doc) return;
24816
+ const el = findElementForSelection(doc, domEditSelection, activeCompPath);
24817
+ if (!el) return;
24818
+ editedElement = el;
24819
+ previousInlineValue = el.style.getPropertyValue(property);
24820
+ },
24821
+ apply: () => {
24822
+ if (!editedElement) return;
24823
+ editedElement.style.setProperty(property, normalizedValue);
24824
+ if (property === "font-family" && doc) {
24825
+ injectPreviewGoogleFont(doc, value);
24826
+ if (importedFont) injectPreviewImportedFont(doc, importedFont);
24827
+ }
24828
+ if (isImageBackgroundCommit) {
24829
+ editedElement.style.setProperty("background-position", "center");
24830
+ editedElement.style.setProperty("background-repeat", "no-repeat");
24831
+ editedElement.style.setProperty("background-size", "contain");
24832
+ }
24833
+ },
24834
+ persist: () => persistDomEditOperations(domEditSelection, operations, {
24835
+ label: "Edit layer style",
24836
+ skipRefresh,
24837
+ prepareContent: importedFont ? (html2, sourceFile) => ensureImportedFontFace(html2, importedFont, sourceFile) : void 0
24838
+ }),
24839
+ shouldRevert: () => isLatestStyleCommit(),
24840
+ revert: () => {
24841
+ if (!editedElement || previousInlineValue === null) return;
24842
+ if (previousInlineValue === "") {
24843
+ editedElement.style.removeProperty(property);
24844
+ } else {
24845
+ editedElement.style.setProperty(property, previousInlineValue);
24846
+ }
24847
+ },
24848
+ onError: (error) => reportDomEditPersistFailure(domEditSelection, operations, error, showToast),
24849
+ shouldResync: isLatestStyleCommit,
24850
+ resync: () => refreshDomEditSelectionFromPreview(domEditSelection)
24851
+ });
24480
24852
  },
24481
24853
  [
24482
24854
  activeCompPath,
24483
24855
  domEditSelection,
24484
24856
  persistDomEditOperations,
24485
24857
  refreshDomEditSelectionFromPreview,
24858
+ resolveImportedFontAsset,
24859
+ showToast,
24486
24860
  previewIframeRef
24487
24861
  ]
24488
24862
  );
24489
- const handleDomTextCommit = useCallback46(
24863
+ const handleDomTextCommit = useCallback47(
24490
24864
  async (value, fieldKey) => {
24491
24865
  if (!domEditSelection) return;
24492
24866
  if (!isTextEditableSelection(domEditSelection)) return;
24493
- const commitVersion = domTextCommitVersionRef.current + 1;
24494
- domTextCommitVersionRef.current = commitVersion;
24495
- const nextTextFields = domEditSelection.textFields.length > 0 ? domEditSelection.textFields.map(
24496
- (field) => field.key === fieldKey ? { ...field, value } : field
24497
- ) : [];
24498
- const nextContent = nextTextFields.length > 1 || nextTextFields.some((field) => field.source === "child") ? serializeDomEditTextFields(nextTextFields) : value;
24867
+ const isLatestTextCommit = bumpDomEditCommitVersion(domTextCommitVersionRef);
24868
+ const nextTextFields = buildNextDomTextFields(domEditSelection.textFields, value, fieldKey);
24869
+ const textCommit = planDomTextCommit(domEditSelection.textFields, nextTextFields, value);
24499
24870
  const iframe = previewIframeRef.current;
24500
24871
  const doc = iframe?.contentDocument;
24501
- if (doc) {
24502
- const el = findElementForSelection(doc, domEditSelection, activeCompPath);
24503
- if (el) {
24504
- if (nextTextFields.length > 1 || nextTextFields.some((field) => field.source === "child")) {
24505
- el.innerHTML = nextContent;
24872
+ let editedElement = null;
24873
+ let previousInnerHtml = null;
24874
+ await runDomEditCommit({
24875
+ capture: () => {
24876
+ if (!doc) return;
24877
+ const el = findElementForSelection(doc, domEditSelection, activeCompPath);
24878
+ if (!el) return;
24879
+ editedElement = el;
24880
+ previousInnerHtml = el.innerHTML;
24881
+ },
24882
+ apply: () => {
24883
+ if (!editedElement) return;
24884
+ if (textCommit.usesSerializedTextFields) {
24885
+ editedElement.innerHTML = textCommit.nextContent;
24506
24886
  } else {
24507
- el.textContent = value;
24887
+ editedElement.textContent = value;
24508
24888
  }
24509
- }
24510
- }
24511
- await persistDomEditOperations(
24512
- domEditSelection,
24513
- [buildDomEditTextPatchOperation(nextContent)],
24514
- {
24515
- label: "Edit text",
24516
- skipRefresh: true,
24517
- shouldSave: () => domTextCommitVersionRef.current === commitVersion
24518
- }
24519
- );
24520
- if (domTextCommitVersionRef.current !== commitVersion) return;
24521
- if (doc) {
24522
- const refreshed = findElementForSelection(doc, domEditSelection, activeCompPath);
24523
- if (refreshed) {
24524
- const nextSelection = await buildDomSelectionFromTarget(refreshed);
24525
- if (nextSelection) {
24526
- applyDomSelection(nextSelection, { revealPanel: false, preserveGroup: true });
24889
+ },
24890
+ persist: async () => {
24891
+ if (textCommit.usesSerializedTextFields && textCommit.childOperations === null) {
24892
+ throw new DomEditPersistUnsupportedTextStructureError();
24527
24893
  }
24528
- }
24529
- }
24894
+ await persistDomEditOperations(domEditSelection, textCommit.operations, {
24895
+ label: "Edit text",
24896
+ skipRefresh: true,
24897
+ shouldSave: isLatestTextCommit
24898
+ });
24899
+ },
24900
+ shouldRevert: () => isLatestTextCommit(),
24901
+ revert: () => {
24902
+ if (!editedElement || previousInnerHtml === null) return;
24903
+ editedElement.innerHTML = previousInnerHtml;
24904
+ },
24905
+ onError: (error) => reportDomEditPersistFailure(domEditSelection, textCommit.operations, error, showToast),
24906
+ shouldResync: isLatestTextCommit,
24907
+ resync: () => resyncDomTextSelectionFromPreview(
24908
+ doc,
24909
+ domEditSelection,
24910
+ activeCompPath,
24911
+ buildDomSelectionFromTarget,
24912
+ applyDomSelection
24913
+ )
24914
+ });
24530
24915
  },
24531
24916
  [
24532
24917
  activeCompPath,
@@ -24534,49 +24919,74 @@ function useDomEditTextCommits({
24534
24919
  buildDomSelectionFromTarget,
24535
24920
  domEditSelection,
24536
24921
  persistDomEditOperations,
24537
- previewIframeRef
24922
+ previewIframeRef,
24923
+ showToast
24538
24924
  ]
24539
24925
  );
24540
- const commitDomTextFields = useCallback46(
24926
+ const commitDomTextFields = useCallback47(
24541
24927
  async (selection, nextTextFields, options) => {
24542
- const nextContent = nextTextFields.length > 1 || nextTextFields.some((field) => field.source === "child") ? serializeDomEditTextFields(nextTextFields) : nextTextFields[0]?.value ?? "";
24928
+ const textCommit = planDomTextCommit(
24929
+ selection.textFields,
24930
+ nextTextFields,
24931
+ nextTextFields[0]?.value ?? ""
24932
+ );
24543
24933
  const iframe = previewIframeRef.current;
24544
24934
  const doc = iframe?.contentDocument;
24545
- if (doc) {
24546
- const el = findElementForSelection(doc, selection, activeCompPath);
24547
- if (el) {
24548
- if (nextTextFields.length > 1 || nextTextFields.some((field) => field.source === "child")) {
24549
- el.innerHTML = nextContent;
24935
+ let editedElement = null;
24936
+ let previousInnerHtml = null;
24937
+ const importedFont = options?.importedFont ?? null;
24938
+ await runDomEditCommit({
24939
+ capture: () => {
24940
+ if (!doc) return;
24941
+ const el = findElementForSelection(doc, selection, activeCompPath);
24942
+ if (!el) return;
24943
+ editedElement = el;
24944
+ previousInnerHtml = el.innerHTML;
24945
+ },
24946
+ apply: () => {
24947
+ if (!editedElement) return;
24948
+ if (textCommit.usesSerializedTextFields) {
24949
+ editedElement.innerHTML = textCommit.nextContent;
24550
24950
  } else {
24551
- el.textContent = nextContent;
24951
+ editedElement.textContent = textCommit.nextContent;
24552
24952
  }
24553
- }
24554
- }
24555
- const importedFont = options?.importedFont ?? null;
24556
- await persistDomEditOperations(selection, [buildDomEditTextPatchOperation(nextContent)], {
24557
- label: "Edit text",
24558
- skipRefresh: true,
24559
- prepareContent: importedFont ? (html2, sourceFile) => ensureImportedFontFace(html2, importedFont, sourceFile) : void 0
24560
- });
24561
- if (doc) {
24562
- const refreshed = findElementForSelection(doc, selection, activeCompPath);
24563
- if (refreshed) {
24564
- const nextSelection = await buildDomSelectionFromTarget(refreshed);
24565
- if (nextSelection) {
24566
- applyDomSelection(nextSelection, { revealPanel: false, preserveGroup: true });
24953
+ },
24954
+ persist: async () => {
24955
+ if (textCommit.usesSerializedTextFields && textCommit.childOperations === null) {
24956
+ throw new DomEditPersistUnsupportedTextStructureError();
24567
24957
  }
24568
- }
24569
- }
24958
+ await persistDomEditOperations(selection, textCommit.operations, {
24959
+ label: "Edit text",
24960
+ skipRefresh: true,
24961
+ prepareContent: importedFont ? (html2, sourceFile) => ensureImportedFontFace(html2, importedFont, sourceFile) : void 0
24962
+ });
24963
+ },
24964
+ shouldRevert: () => true,
24965
+ revert: () => {
24966
+ if (!editedElement || previousInnerHtml === null) return;
24967
+ editedElement.innerHTML = previousInnerHtml;
24968
+ },
24969
+ onError: (error) => reportDomEditPersistFailure(selection, textCommit.operations, error, showToast),
24970
+ shouldResync: () => true,
24971
+ resync: () => resyncDomTextSelectionFromPreview(
24972
+ doc,
24973
+ selection,
24974
+ activeCompPath,
24975
+ buildDomSelectionFromTarget,
24976
+ applyDomSelection
24977
+ )
24978
+ });
24570
24979
  },
24571
24980
  [
24572
24981
  activeCompPath,
24573
24982
  applyDomSelection,
24574
24983
  buildDomSelectionFromTarget,
24575
24984
  persistDomEditOperations,
24576
- previewIframeRef
24985
+ previewIframeRef,
24986
+ showToast
24577
24987
  ]
24578
24988
  );
24579
- const handleDomTextFieldStyleCommit = useCallback46(
24989
+ const handleDomTextFieldStyleCommit = useCallback47(
24580
24990
  async (fieldKey, property, value) => {
24581
24991
  if (!domEditSelection) return;
24582
24992
  const field = domEditSelection.textFields.find((entry) => entry.key === fieldKey);
@@ -24617,7 +25027,7 @@ function useDomEditTextCommits({
24617
25027
  previewIframeRef
24618
25028
  ]
24619
25029
  );
24620
- const handleDomAddTextField = useCallback46(
25030
+ const handleDomAddTextField = useCallback47(
24621
25031
  async (afterFieldKey) => {
24622
25032
  if (!domEditSelection) return null;
24623
25033
  if (!domEditSelection.textFields.some((field) => field.source === "child")) return null;
@@ -24637,7 +25047,7 @@ function useDomEditTextCommits({
24637
25047
  },
24638
25048
  [commitDomTextFields, domEditSelection]
24639
25049
  );
24640
- const handleDomRemoveTextField = useCallback46(
25050
+ const handleDomRemoveTextField = useCallback47(
24641
25051
  async (fieldKey) => {
24642
25052
  if (!domEditSelection) return;
24643
25053
  const field = domEditSelection.textFields.find((entry) => entry.key === fieldKey);
@@ -24665,7 +25075,7 @@ function useDomEditTextCommits({
24665
25075
  }
24666
25076
 
24667
25077
  // src/hooks/useDomGeometryCommits.ts
24668
- import { useCallback as useCallback47 } from "react";
25078
+ import { useCallback as useCallback48 } from "react";
24669
25079
 
24670
25080
  // src/components/editor/manualEditsDomPatches.ts
24671
25081
  function collectInlineStyleOps(element, properties, ops) {
@@ -24874,7 +25284,7 @@ function useDomGeometryCommits({
24874
25284
  showToast,
24875
25285
  commitPositionPatchToHtml
24876
25286
  }) {
24877
- const handleDomPathOffsetCommit = useCallback47(
25287
+ const handleDomPathOffsetCommit = useCallback48(
24878
25288
  (selection, next) => {
24879
25289
  const gsapTargeted = isElementGsapTargeted(previewIframeRef.current, selection.element);
24880
25290
  if (gsapTargeted) {
@@ -24890,7 +25300,7 @@ function useDomGeometryCommits({
24890
25300
  },
24891
25301
  [commitPositionPatchToHtml, previewIframeRef, showToast]
24892
25302
  );
24893
- const handleDomBoxSizeCommit = useCallback47(
25303
+ const handleDomBoxSizeCommit = useCallback48(
24894
25304
  (selection, next) => {
24895
25305
  if (isElementGsapTargeted(previewIframeRef.current, selection.element)) {
24896
25306
  const error = new Error(GSAP_CSS_FALLBACK_BLOCKED_MESSAGE);
@@ -24905,7 +25315,7 @@ function useDomGeometryCommits({
24905
25315
  },
24906
25316
  [commitPositionPatchToHtml, previewIframeRef, showToast]
24907
25317
  );
24908
- const handleDomRotationCommit = useCallback47(
25318
+ const handleDomRotationCommit = useCallback48(
24909
25319
  (selection, next) => {
24910
25320
  if (isElementGsapTargeted(previewIframeRef.current, selection.element)) {
24911
25321
  const error = new Error(GSAP_CSS_FALLBACK_BLOCKED_MESSAGE);
@@ -24920,7 +25330,7 @@ function useDomGeometryCommits({
24920
25330
  },
24921
25331
  [commitPositionPatchToHtml, previewIframeRef, showToast]
24922
25332
  );
24923
- const handleDomManualEditsReset = useCallback47(
25333
+ const handleDomManualEditsReset = useCallback48(
24924
25334
  (selection) => {
24925
25335
  const element = selection.element;
24926
25336
  const clearPatches = [
@@ -24948,7 +25358,7 @@ function useDomGeometryCommits({
24948
25358
  }
24949
25359
 
24950
25360
  // src/hooks/useElementLifecycleOps.ts
24951
- import { useCallback as useCallback48 } from "react";
25361
+ import { useCallback as useCallback49 } from "react";
24952
25362
  function useElementLifecycleOps({
24953
25363
  activeCompPath,
24954
25364
  showToast,
@@ -24964,7 +25374,7 @@ function useElementLifecycleOps({
24964
25374
  commitPositionPatchToHtml,
24965
25375
  onElementDeleted
24966
25376
  }) {
24967
- const handleDomEditElementDelete = useCallback48(
25377
+ const handleDomEditElementDelete = useCallback49(
24968
25378
  // fallow-ignore-next-line complexity
24969
25379
  async (selection) => {
24970
25380
  const pid = projectIdRef.current;
@@ -25046,7 +25456,7 @@ function useElementLifecycleOps({
25046
25456
  writeProjectFile
25047
25457
  ]
25048
25458
  );
25049
- const handleDomZIndexReorderCommit = useCallback48(
25459
+ const handleDomZIndexReorderCommit = useCallback49(
25050
25460
  (entries) => {
25051
25461
  if (entries.length === 0) return;
25052
25462
  onReorderShadow?.(
@@ -25326,6 +25736,9 @@ async function assignGsapTargetAutoIdIfNeeded({
25326
25736
  function formatUnsafeFieldList(fields) {
25327
25737
  return fields.map((field) => field.path).join(", ");
25328
25738
  }
25739
+ function getErrorDetail2(error) {
25740
+ return error instanceof Error ? error.message : String(error);
25741
+ }
25329
25742
  async function readErrorResponseBody(response) {
25330
25743
  const contentType = response.headers.get("content-type") ?? "";
25331
25744
  if (!contentType.includes("application/json")) return null;
@@ -25358,7 +25771,7 @@ function useDomEditCommits({
25358
25771
  onTrySdkDelete,
25359
25772
  onReorderShadow
25360
25773
  }) {
25361
- const resolveImportedFontAsset = useCallback49(
25774
+ const resolveImportedFontAsset = useCallback50(
25362
25775
  (fontFamilyValue) => {
25363
25776
  const family = primaryFontFamilyValue(fontFamilyValue);
25364
25777
  if (!family) return null;
@@ -25378,8 +25791,8 @@ function useDomEditCommits({
25378
25791
  },
25379
25792
  [fileTree, projectId, importedFontAssetsRef]
25380
25793
  );
25381
- const reportedUnresolvableRef = useRef49(/* @__PURE__ */ new Set());
25382
- const persistDomEditOperations = useCallback49(
25794
+ const reportedUnresolvableRef = useRef50(/* @__PURE__ */ new Set());
25795
+ const persistDomEditOperations = useCallback50(
25383
25796
  // fallow-ignore-next-line complexity
25384
25797
  async (selection, operations, options) => {
25385
25798
  const pid = projectIdRef.current;
@@ -25404,7 +25817,9 @@ function useDomEditCommits({
25404
25817
  if (unsafeFields.length > 0) {
25405
25818
  const fields = formatUnsafeFieldList(unsafeFields);
25406
25819
  showToast("Couldn't save edit because it contains invalid layout values", "error");
25407
- throw new Error(`DOM patch contains unsafe values: ${fields}`);
25820
+ throw new DomEditPersistUnsafeValueError(`DOM patch contains unsafe values: ${fields}`, {
25821
+ alreadyToasted: true
25822
+ });
25408
25823
  }
25409
25824
  if (onTrySdkPersist && !options?.prepareContent && await onTrySdkPersist(selection, operations, originalContent, targetPath, {
25410
25825
  label: options?.label,
@@ -25424,7 +25839,9 @@ function useDomEditCommits({
25424
25839
  );
25425
25840
  if (!patchResponse.ok) {
25426
25841
  showToast(formatPatchRejectionMessage(await readErrorResponseBody(patchResponse)), "error");
25427
- throw await createStudioSaveHttpError(patchResponse, `Failed to patch ${targetPath}`);
25842
+ throw await createStudioSaveHttpError(patchResponse, `Failed to patch ${targetPath}`, {
25843
+ alreadyToasted: true
25844
+ });
25428
25845
  }
25429
25846
  const patchData = await patchResponse.json();
25430
25847
  if (!patchData.changed) {
@@ -25439,15 +25856,25 @@ function useDomEditCommits({
25439
25856
  composition: activeCompPath ?? void 0
25440
25857
  });
25441
25858
  }
25859
+ throw new DomEditPersistUnresolvableError(targetPath);
25442
25860
  }
25861
+ warnDomEditPersistNoOp(selection, operations);
25443
25862
  return;
25444
25863
  }
25445
25864
  const patchedContent = typeof patchData.content === "string" ? patchData.content : originalContent;
25446
25865
  let finalContent = patchedContent;
25447
25866
  if (options?.prepareContent) {
25448
- finalContent = options.prepareContent(patchedContent, targetPath);
25449
- if (finalContent !== patchedContent) {
25450
- await writeProjectFile(targetPath, finalContent);
25867
+ const preparedContent = options.prepareContent(patchedContent, targetPath);
25868
+ if (preparedContent !== patchedContent) {
25869
+ try {
25870
+ await writeProjectFile(targetPath, preparedContent);
25871
+ finalContent = preparedContent;
25872
+ } catch (error) {
25873
+ showToast(
25874
+ `Saved, but couldn't finish updating ${targetPath}: ${getErrorDetail2(error)}`,
25875
+ "error"
25876
+ );
25877
+ }
25451
25878
  }
25452
25879
  }
25453
25880
  await editHistory.recordEdit({
@@ -25491,7 +25918,8 @@ function useDomEditCommits({
25491
25918
  refreshDomEditSelectionFromPreview,
25492
25919
  buildDomSelectionFromTarget,
25493
25920
  persistDomEditOperations,
25494
- resolveImportedFontAsset
25921
+ resolveImportedFontAsset,
25922
+ showToast
25495
25923
  });
25496
25924
  const commitPositionPatchToHtml = useDomEditPositionPatchCommit({
25497
25925
  activeCompPath,
@@ -25544,7 +25972,7 @@ function useDomEditCommits({
25544
25972
  }
25545
25973
 
25546
25974
  // src/hooks/useGroupCommits.ts
25547
- import { useCallback as useCallback50 } from "react";
25975
+ import { useCallback as useCallback51 } from "react";
25548
25976
  function computeGroupGeometry(members) {
25549
25977
  const boxes = members.map((m) => ({
25550
25978
  target: buildDomEditPatchTarget(m),
@@ -25604,7 +26032,7 @@ async function commitStructuralMutation(pid, targetPath, route, body, label, dep
25604
26032
  }
25605
26033
  function useGroupCommits(params) {
25606
26034
  const { activeCompPath, showToast, projectIdRef } = params;
25607
- const groupSelection = useCallback50(
26035
+ const groupSelection = useCallback51(
25608
26036
  async (members) => {
25609
26037
  const pid = projectIdRef.current;
25610
26038
  if (!pid || members.length === 0) return null;
@@ -25633,7 +26061,7 @@ function useGroupCommits(params) {
25633
26061
  },
25634
26062
  [activeCompPath, projectIdRef, showToast, params]
25635
26063
  );
25636
- const ungroupSelection = useCallback50(
26064
+ const ungroupSelection = useCallback51(
25637
26065
  async (group) => {
25638
26066
  const pid = projectIdRef.current;
25639
26067
  if (!pid) return;
@@ -25657,7 +26085,7 @@ function useGroupCommits(params) {
25657
26085
  }
25658
26086
 
25659
26087
  // src/hooks/useGsapScriptCommits.ts
25660
- import { useCallback as useCallback56, useMemo as useMemo21, useRef as useRef51 } from "react";
26088
+ import { useCallback as useCallback57, useMemo as useMemo21, useRef as useRef52 } from "react";
25661
26089
  import { findUnsafeMutationValues } from "@hyperframes/core/studio-api/finite-mutation";
25662
26090
 
25663
26091
  // src/hooks/gsapKeyframeCacheHelpers.ts
@@ -26163,7 +26591,7 @@ function createKeyedSerializer() {
26163
26591
  }
26164
26592
 
26165
26593
  // src/hooks/useGsapAnimationOps.ts
26166
- import { useCallback as useCallback51 } from "react";
26594
+ import { useCallback as useCallback52 } from "react";
26167
26595
  function useGsapAnimationOps({
26168
26596
  projectIdRef,
26169
26597
  activeCompPath,
@@ -26173,7 +26601,7 @@ function useGsapAnimationOps({
26173
26601
  sdkSession,
26174
26602
  sdkDeps
26175
26603
  }) {
26176
- const updateGsapMeta = useCallback51(
26604
+ const updateGsapMeta = useCallback52(
26177
26605
  async (selection, animationId, updates) => {
26178
26606
  if (sdkSession && sdkDeps) {
26179
26607
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
@@ -26194,7 +26622,7 @@ function useGsapAnimationOps({
26194
26622
  },
26195
26623
  [commitMutationSafely, activeCompPath, sdkSession, sdkDeps]
26196
26624
  );
26197
- const deleteGsapAnimation = useCallback51(
26625
+ const deleteGsapAnimation = useCallback52(
26198
26626
  async (selection, animationId) => {
26199
26627
  if (sdkSession && sdkDeps) {
26200
26628
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
@@ -26215,7 +26643,7 @@ function useGsapAnimationOps({
26215
26643
  },
26216
26644
  [commitMutationSafely, activeCompPath, sdkSession, sdkDeps]
26217
26645
  );
26218
- const deleteAllForSelector = useCallback51(
26646
+ const deleteAllForSelector = useCallback52(
26219
26647
  async (selection, targetSelector) => {
26220
26648
  if (sdkSession && sdkDeps) {
26221
26649
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
@@ -26236,7 +26664,7 @@ function useGsapAnimationOps({
26236
26664
  },
26237
26665
  [commitMutation, activeCompPath, sdkSession, sdkDeps]
26238
26666
  );
26239
- const addGsapAnimation = useCallback51(
26667
+ const addGsapAnimation = useCallback52(
26240
26668
  // fallow-ignore-next-line complexity
26241
26669
  async (selection, method, _currentTime) => {
26242
26670
  const { selector, autoId } = ensureElementAddressable(selection);
@@ -26298,7 +26726,7 @@ function useGsapAnimationOps({
26298
26726
  },
26299
26727
  [activeCompPath, commitMutation, projectIdRef, showToast, sdkSession, sdkDeps]
26300
26728
  );
26301
- const addWithKeyframes = useCallback51(
26729
+ const addWithKeyframes = useCallback52(
26302
26730
  async (selection, targetSelector, position, duration, keyframes, ease, label = "Add animation with keyframes") => {
26303
26731
  if (sdkSession && sdkDeps) {
26304
26732
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
@@ -26330,7 +26758,7 @@ function useGsapAnimationOps({
26330
26758
  },
26331
26759
  [commitMutation, activeCompPath, sdkSession, sdkDeps]
26332
26760
  );
26333
- const replaceWithKeyframes = useCallback51(
26761
+ const replaceWithKeyframes = useCallback52(
26334
26762
  async (selection, animationId, targetSelector, position, duration, keyframes, ease, label = "Replace animation with keyframes") => {
26335
26763
  if (sdkSession && sdkDeps) {
26336
26764
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
@@ -26375,9 +26803,9 @@ function useGsapAnimationOps({
26375
26803
  }
26376
26804
 
26377
26805
  // src/hooks/useGsapArcPathOps.ts
26378
- import { useCallback as useCallback52 } from "react";
26806
+ import { useCallback as useCallback53 } from "react";
26379
26807
  function useGsapArcPathOps(commitMutationSafely) {
26380
- const setArcPath = useCallback52(
26808
+ const setArcPath = useCallback53(
26381
26809
  (selection, animationId, config) => {
26382
26810
  commitMutationSafely(
26383
26811
  selection,
@@ -26387,7 +26815,7 @@ function useGsapArcPathOps(commitMutationSafely) {
26387
26815
  },
26388
26816
  [commitMutationSafely]
26389
26817
  );
26390
- const updateArcSegment = useCallback52(
26818
+ const updateArcSegment = useCallback53(
26391
26819
  (selection, animationId, segmentIndex, update) => {
26392
26820
  commitMutationSafely(
26393
26821
  selection,
@@ -26397,7 +26825,7 @@ function useGsapArcPathOps(commitMutationSafely) {
26397
26825
  },
26398
26826
  [commitMutationSafely]
26399
26827
  );
26400
- const removeArcPath = useCallback52(
26828
+ const removeArcPath = useCallback53(
26401
26829
  (selection, animationId) => {
26402
26830
  commitMutationSafely(
26403
26831
  selection,
@@ -26411,7 +26839,7 @@ function useGsapArcPathOps(commitMutationSafely) {
26411
26839
  }
26412
26840
 
26413
26841
  // src/hooks/useGsapKeyframeOps.ts
26414
- import { useCallback as useCallback53 } from "react";
26842
+ import { useCallback as useCallback54 } from "react";
26415
26843
 
26416
26844
  // src/utils/optimisticUpdate.ts
26417
26845
  async function executeOptimistic(options) {
@@ -26502,7 +26930,7 @@ function useGsapKeyframeOps({
26502
26930
  sdkSession,
26503
26931
  sdkDeps
26504
26932
  }) {
26505
- const addKeyframe = useCallback53(
26933
+ const addKeyframe = useCallback54(
26506
26934
  (selection, animationId, percentage, property, value) => {
26507
26935
  const sourceFile = selection.sourceFile || activeCompPath || "index.html";
26508
26936
  const mutation = {
@@ -26563,7 +26991,7 @@ function useGsapKeyframeOps({
26563
26991
  },
26564
26992
  [activeCompPath, commitMutation, trackGsapSaveFailure, sdkSession, sdkDeps]
26565
26993
  );
26566
- const addKeyframeBatch = useCallback53(
26994
+ const addKeyframeBatch = useCallback54(
26567
26995
  async (selection, animationId, percentage, properties) => {
26568
26996
  if (sdkSession && sdkDeps) {
26569
26997
  const sourceFile = selection.sourceFile || activeCompPath || "index.html";
@@ -26586,7 +27014,7 @@ function useGsapKeyframeOps({
26586
27014
  },
26587
27015
  [commitMutation, activeCompPath, sdkSession, sdkDeps]
26588
27016
  );
26589
- const removeKeyframe = useCallback53(
27017
+ const removeKeyframe = useCallback54(
26590
27018
  (selection, animationId, percentage) => {
26591
27019
  const sourceFile = selection.sourceFile || activeCompPath || "index.html";
26592
27020
  const mutation = { type: "remove-keyframe", animationId, percentage };
@@ -26627,7 +27055,7 @@ function useGsapKeyframeOps({
26627
27055
  },
26628
27056
  [activeCompPath, commitMutation, trackGsapSaveFailure, sdkSession, sdkDeps]
26629
27057
  );
26630
- const moveKeyframe = useCallback53(
27058
+ const moveKeyframe = useCallback54(
26631
27059
  (selection, animationId, fromPercentage, toPercentage) => {
26632
27060
  const mutation = { type: "move-keyframe", animationId, fromPercentage, toPercentage };
26633
27061
  void commitMutation(selection, mutation, {
@@ -26639,7 +27067,7 @@ function useGsapKeyframeOps({
26639
27067
  },
26640
27068
  [commitMutation, trackGsapSaveFailure]
26641
27069
  );
26642
- const resizeKeyframedTween = useCallback53(
27070
+ const resizeKeyframedTween = useCallback54(
26643
27071
  (selection, animationId, position, duration, pctRemap) => {
26644
27072
  const mutation = {
26645
27073
  type: "resize-keyframed-tween",
@@ -26657,7 +27085,7 @@ function useGsapKeyframeOps({
26657
27085
  },
26658
27086
  [commitMutation, trackGsapSaveFailure]
26659
27087
  );
26660
- const convertToKeyframes = useCallback53(
27088
+ const convertToKeyframes = useCallback54(
26661
27089
  async (selection, animationId, resolvedFromValues, duration) => {
26662
27090
  if (sdkSession && sdkDeps) {
26663
27091
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
@@ -26681,7 +27109,7 @@ function useGsapKeyframeOps({
26681
27109
  },
26682
27110
  [commitMutation, activeCompPath, sdkSession, sdkDeps]
26683
27111
  );
26684
- const removeAllKeyframes = useCallback53(
27112
+ const removeAllKeyframes = useCallback54(
26685
27113
  async (selection, animationId) => {
26686
27114
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
26687
27115
  const elementId = selection.id ?? selection.selector?.match(/^#([\w-]+)/)?.[1] ?? null;
@@ -26704,7 +27132,7 @@ function useGsapKeyframeOps({
26704
27132
  },
26705
27133
  [commitMutationSafely, activeCompPath, sdkSession, sdkDeps]
26706
27134
  );
26707
- const commitKeyframeAtTime = useCallback53(
27135
+ const commitKeyframeAtTime = useCallback54(
26708
27136
  (selection, absoluteTime, animations, properties) => commitKeyframeAtTimeImpl(selection, absoluteTime, animations, properties, commitMutation),
26709
27137
  [commitMutation]
26710
27138
  );
@@ -26721,7 +27149,7 @@ function useGsapKeyframeOps({
26721
27149
  }
26722
27150
 
26723
27151
  // src/hooks/useGsapPropertyDebounce.ts
26724
- import { useCallback as useCallback54, useEffect as useEffect31, useRef as useRef50 } from "react";
27152
+ import { useCallback as useCallback55, useEffect as useEffect31, useRef as useRef51 } from "react";
26725
27153
  import { parseGsapScriptAcorn } from "@hyperframes/core/gsap-parser-acorn";
26726
27154
  var DEBOUNCE_MS = 150;
26727
27155
  function mergeTweenProperties(sdkSession, animationId, edited, kind) {
@@ -26737,11 +27165,11 @@ function mergeTweenProperties(sdkSession, animationId, edited, kind) {
26737
27165
  }
26738
27166
  }
26739
27167
  function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26740
- const pendingPropertyEditRef = useRef50(null);
26741
- const debounceTimerRef = useRef50(null);
26742
- const sdkRef = useRef50(sdk);
27168
+ const pendingPropertyEditRef = useRef51(null);
27169
+ const debounceTimerRef = useRef51(null);
27170
+ const sdkRef = useRef51(sdk);
26743
27171
  sdkRef.current = sdk;
26744
- const flushPendingPropertyEdit = useCallback54(async () => {
27172
+ const flushPendingPropertyEdit = useCallback55(async () => {
26745
27173
  const pending = pendingPropertyEditRef.current;
26746
27174
  if (!pending) return;
26747
27175
  pendingPropertyEditRef.current = null;
@@ -26774,7 +27202,7 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26774
27202
  }
26775
27203
  );
26776
27204
  }, [commitMutationSafely]);
26777
- const updateGsapProperty = useCallback54(
27205
+ const updateGsapProperty = useCallback55(
26778
27206
  (selection, animationId, property, value) => {
26779
27207
  pendingPropertyEditRef.current = { selection, animationId, property, value };
26780
27208
  if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
@@ -26790,7 +27218,7 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26790
27218
  void flushPendingPropertyEdit();
26791
27219
  };
26792
27220
  }, [flushPendingPropertyEdit]);
26793
- const addGsapProperty = useCallback54(
27221
+ const addGsapProperty = useCallback55(
26794
27222
  // fallow-ignore-next-line complexity
26795
27223
  async (selection, animationId, property) => {
26796
27224
  let defaultValue = PROPERTY_DEFAULTS[property] ?? 0;
@@ -26834,7 +27262,7 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26834
27262
  },
26835
27263
  [commitMutationSafely]
26836
27264
  );
26837
- const removeProperty = useCallback54(
27265
+ const removeProperty = useCallback55(
26838
27266
  async (selection, animationId, property, from) => {
26839
27267
  const { sdkSession, sdkDeps, activeCompPath } = sdkRef.current ?? {};
26840
27268
  if (sdkSession && sdkDeps) {
@@ -26870,11 +27298,11 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26870
27298
  },
26871
27299
  [commitMutationSafely]
26872
27300
  );
26873
- const removeGsapProperty = useCallback54(
27301
+ const removeGsapProperty = useCallback55(
26874
27302
  (selection, animationId, property) => removeProperty(selection, animationId, property, false),
26875
27303
  [removeProperty]
26876
27304
  );
26877
- const updateGsapFromProperty = useCallback54(
27305
+ const updateGsapFromProperty = useCallback55(
26878
27306
  async (selection, animationId, property, value) => {
26879
27307
  const { sdkSession, sdkDeps, activeCompPath } = sdkRef.current ?? {};
26880
27308
  if (sdkSession && sdkDeps) {
@@ -26913,7 +27341,7 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26913
27341
  },
26914
27342
  [commitMutationSafely]
26915
27343
  );
26916
- const addGsapFromProperty = useCallback54(
27344
+ const addGsapFromProperty = useCallback55(
26917
27345
  async (selection, animationId, property) => {
26918
27346
  const defaultValue = PROPERTY_DEFAULTS[property] ?? 0;
26919
27347
  const { sdkSession, sdkDeps, activeCompPath } = sdkRef.current ?? {};
@@ -26947,7 +27375,7 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26947
27375
  },
26948
27376
  [commitMutationSafely]
26949
27377
  );
26950
- const removeGsapFromProperty = useCallback54(
27378
+ const removeGsapFromProperty = useCallback55(
26951
27379
  (selection, animationId, property) => removeProperty(selection, animationId, property, true),
26952
27380
  [removeProperty]
26953
27381
  );
@@ -26962,12 +27390,12 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26962
27390
  }
26963
27391
 
26964
27392
  // src/hooks/useSafeGsapCommitMutation.ts
26965
- import { useCallback as useCallback55 } from "react";
27393
+ import { useCallback as useCallback56 } from "react";
26966
27394
  function getGsapMutationType(mutation) {
26967
27395
  return typeof mutation.type === "string" ? mutation.type : "gsap";
26968
27396
  }
26969
27397
  function useGsapSaveFailureTelemetry(activeCompPath) {
26970
- return useCallback55(
27398
+ return useCallback56(
26971
27399
  (error, selection, mutation, label) => {
26972
27400
  trackStudioSaveFailure({
26973
27401
  source: "gsap_commit",
@@ -26984,7 +27412,7 @@ function useGsapSaveFailureTelemetry(activeCompPath) {
26984
27412
  );
26985
27413
  }
26986
27414
  function useSafeGsapCommitMutation(commitMutation, trackGsapSaveFailure, showToast) {
26987
- return useCallback55(
27415
+ return useCallback56(
26988
27416
  (selection, mutation, options) => (
26989
27417
  // Return the chain so awaiting consumers (gesture commit, enable-keyframes)
26990
27418
  // run their post-actions AFTER the server save settles, not immediately.
@@ -27043,8 +27471,8 @@ function applyPreviewSync(iframe, result, options, reloadPreview) {
27043
27471
  }
27044
27472
  }
27045
27473
  function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef, editHistory, domEditSaveTimestampRef, reloadPreview, onCacheInvalidate, onFileContentChanged, showToast, sdkSession, writeProjectFile, forceReloadSdkSession }) {
27046
- const serializerRef = useRef51(createKeyedSerializer());
27047
- const runCommit = useCallback56(async (selection, mutation, options) => {
27474
+ const serializerRef = useRef52(createKeyedSerializer());
27475
+ const runCommit = useCallback57(async (selection, mutation, options) => {
27048
27476
  const pid = projectIdRef.current;
27049
27477
  if (!pid) return;
27050
27478
  const unsafeFields = findUnsafeMutationValues(mutation);
@@ -27075,7 +27503,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
27075
27503
  applyPreviewSync(previewIframeRef.current, result, options, reloadPreview);
27076
27504
  onCacheInvalidate();
27077
27505
  }, [projectIdRef, activeCompPath, previewIframeRef, editHistory, domEditSaveTimestampRef, reloadPreview, onCacheInvalidate, onFileContentChanged, showToast, forceReloadSdkSession]);
27078
- const commitMutation = useCallback56(
27506
+ const commitMutation = useCallback57(
27079
27507
  (selection, mutation, options) => {
27080
27508
  const file = selection.sourceFile || activeCompPath || "index.html";
27081
27509
  const key = options.serializeKey ?? `gsap-file:${file}`;
@@ -27085,7 +27513,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
27085
27513
  );
27086
27514
  const trackGsapSaveFailure = useGsapSaveFailureTelemetry(activeCompPath);
27087
27515
  const commitMutationSafely = useSafeGsapCommitMutation(commitMutation, trackGsapSaveFailure, showToast);
27088
- const sdkRefresh = useCallback56(
27516
+ const sdkRefresh = useCallback57(
27089
27517
  (after) => {
27090
27518
  const script = extractGsapScriptText(after);
27091
27519
  if (script) {
@@ -27097,11 +27525,11 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
27097
27525
  },
27098
27526
  [previewIframeRef, reloadPreview, onCacheInvalidate]
27099
27527
  );
27100
- const serializeByFile = useCallback56(
27528
+ const serializeByFile = useCallback57(
27101
27529
  (key, task) => serializerRef.current(key, task),
27102
27530
  []
27103
27531
  );
27104
- const readProjectFileContent = useCallback56(
27532
+ const readProjectFileContent = useCallback57(
27105
27533
  async (path) => {
27106
27534
  const pid = projectIdRef.current;
27107
27535
  if (!pid) throw new Error("No active project");
@@ -27162,7 +27590,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
27162
27590
  }
27163
27591
 
27164
27592
  // src/hooks/useGsapTweenCache.ts
27165
- import { useEffect as useEffect32, useMemo as useMemo22, useRef as useRef52, useState as useState47, useCallback as useCallback57 } from "react";
27593
+ import { useEffect as useEffect32, useMemo as useMemo22, useRef as useRef53, useState as useState47, useCallback as useCallback58 } from "react";
27166
27594
  import { isStudioHoldSet } from "@hyperframes/core/gsap-parser";
27167
27595
 
27168
27596
  // src/hooks/gsapRuntimeReaders.ts
@@ -28556,8 +28984,8 @@ function useGsapAnimationsForElement(projectId, sourceFile, target, version, ifr
28556
28984
  const [allAnimations, setAllAnimations] = useState47([]);
28557
28985
  const [multipleTimelines, setMultipleTimelines] = useState47(false);
28558
28986
  const [unsupportedTimelinePattern, setUnsupportedTimelinePattern] = useState47(false);
28559
- const lastFetchKeyRef = useRef52("");
28560
- const retryTimerRef = useRef52(null);
28987
+ const lastFetchKeyRef = useRef53("");
28988
+ const retryTimerRef = useRef53(null);
28561
28989
  const domClipChildrenKey = usePlayerStore(
28562
28990
  (s) => s.domClipChildren.map((c) => `${c.id}<${c.hostId}`).join("|")
28563
28991
  );
@@ -28735,7 +29163,7 @@ function useGsapAnimationsForElement(projectId, sourceFile, target, version, ifr
28735
29163
  }
28736
29164
  function useGsapCacheVersion() {
28737
29165
  const [version, setVersion] = useState47(0);
28738
- const bump = useCallback57(() => setVersion((v) => v + 1), []);
29166
+ const bump = useCallback58(() => setVersion((v) => v + 1), []);
28739
29167
  return { version, bump };
28740
29168
  }
28741
29169
  function usePopulateKeyframeCacheForFile(projectId, sourceFile, version, iframeRef) {
@@ -28743,9 +29171,9 @@ function usePopulateKeyframeCacheForFile(projectId, sourceFile, version, iframeR
28743
29171
  const domClipChildrenKey = usePlayerStore(
28744
29172
  (s) => s.domClipChildren.map((c) => `${c.id}<${c.hostId}`).join("|")
28745
29173
  );
28746
- const lastFetchKeyRef = useRef52("");
28747
- const runtimeScanDoneRef = useRef52("");
28748
- const astFetchDoneRef = useRef52("");
29174
+ const lastFetchKeyRef = useRef53("");
29175
+ const runtimeScanDoneRef = useRef53("");
29176
+ const astFetchDoneRef = useRef53("");
28749
29177
  useEffect32(() => {
28750
29178
  const fetchKey = `kf-cache:${projectId}:${sourceFile}:${version}:${elementCount}:${domClipChildrenKey}`;
28751
29179
  if (fetchKey === lastFetchKeyRef.current) return;
@@ -28848,10 +29276,10 @@ function usePopulateKeyframeCacheForFile(projectId, sourceFile, version, iframeR
28848
29276
  }
28849
29277
 
28850
29278
  // src/hooks/useDomEditWiring.ts
28851
- import { useCallback as useCallback61, useEffect as useEffect34, useRef as useRef55 } from "react";
29279
+ import { useCallback as useCallback62, useEffect as useEffect34, useRef as useRef56 } from "react";
28852
29280
 
28853
29281
  // src/hooks/useDomEditPreviewSync.ts
28854
- import { useEffect as useEffect33, useRef as useRef53 } from "react";
29282
+ import { useEffect as useEffect33, useRef as useRef54 } from "react";
28855
29283
  function useDomEditPreviewSync({
28856
29284
  previewIframe,
28857
29285
  activeCompPath,
@@ -28917,7 +29345,7 @@ function useDomEditPreviewSync({
28917
29345
  applyStudioManualEditsToPreviewRef,
28918
29346
  gsapCacheVersion
28919
29347
  ]);
28920
- const openSourceRef = useRef53(openSourceForSelection);
29348
+ const openSourceRef = useRef54(openSourceForSelection);
28921
29349
  openSourceRef.current = openSourceForSelection;
28922
29350
  useEffect33(
28923
29351
  // fallow-ignore-next-line complexity
@@ -28936,7 +29364,7 @@ function useDomEditPreviewSync({
28936
29364
  }
28937
29365
 
28938
29366
  // src/hooks/useGsapAnimationFetchFallback.ts
28939
- import { useCallback as useCallback58 } from "react";
29367
+ import { useCallback as useCallback59 } from "react";
28940
29368
  var COLD_PARSE_RETRIES = 5;
28941
29369
  var COLD_PARSE_DELAY_MS = 120;
28942
29370
  var FETCH_ERROR_RETRIES = 1;
@@ -28948,7 +29376,7 @@ function selectElementAnimationsOrRetry(parsed, target) {
28948
29376
  return { kind: "resolved", animations: getAnimationsForElement(parsed.animations, target) };
28949
29377
  }
28950
29378
  function useGsapAnimationFetchFallback(projectId, gsapSourceFile) {
28951
- return useCallback58(
29379
+ return useCallback59(
28952
29380
  (selection) => async () => {
28953
29381
  if (!projectId) return [];
28954
29382
  const target = { id: selection.id ?? null, selector: selection.selector ?? null };
@@ -28974,9 +29402,9 @@ function useGsapAnimationFetchFallback(projectId, gsapSourceFile) {
28974
29402
  }
28975
29403
 
28976
29404
  // src/hooks/useGsapInteractionFailureTelemetry.ts
28977
- import { useCallback as useCallback59 } from "react";
29405
+ import { useCallback as useCallback60 } from "react";
28978
29406
  function useGsapInteractionFailureTelemetry(activeCompPath, showToast) {
28979
- return useCallback59(
29407
+ return useCallback60(
28980
29408
  (error, selection, mutationType, label) => {
28981
29409
  trackStudioSaveFailure({
28982
29410
  source: "gsap_commit",
@@ -28995,7 +29423,7 @@ function useGsapInteractionFailureTelemetry(activeCompPath, showToast) {
28995
29423
  }
28996
29424
 
28997
29425
  // src/hooks/useGsapSelectionHandlers.ts
28998
- import { useCallback as useCallback60, useRef as useRef54 } from "react";
29426
+ import { useCallback as useCallback61, useRef as useRef55 } from "react";
28999
29427
  function useGsapSelectionHandlers({
29000
29428
  domEditSelection,
29001
29429
  updateGsapProperty,
@@ -29018,9 +29446,9 @@ function useGsapSelectionHandlers({
29018
29446
  handleDomManualEditsReset,
29019
29447
  selectedGsapAnimations
29020
29448
  }) {
29021
- const lastSelectionRef = useRef54(null);
29449
+ const lastSelectionRef = useRef55(null);
29022
29450
  if (domEditSelection) lastSelectionRef.current = domEditSelection;
29023
- const trackGsapHandlerFailure = useCallback60(
29451
+ const trackGsapHandlerFailure = useCallback61(
29024
29452
  (error, selection, mutationType, label) => {
29025
29453
  trackStudioSaveFailure({
29026
29454
  source: "gsap_commit",
@@ -29035,14 +29463,14 @@ function useGsapSelectionHandlers({
29035
29463
  },
29036
29464
  []
29037
29465
  );
29038
- const handleGsapUpdateProperty = useCallback60(
29466
+ const handleGsapUpdateProperty = useCallback61(
29039
29467
  (animId, prop, value) => {
29040
29468
  if (!domEditSelection) return;
29041
29469
  updateGsapProperty(domEditSelection, animId, prop, value);
29042
29470
  },
29043
29471
  [domEditSelection, updateGsapProperty]
29044
29472
  );
29045
- const handleGsapUpdateMeta = useCallback60(
29473
+ const handleGsapUpdateMeta = useCallback61(
29046
29474
  (animId, updates, selectionOverride) => {
29047
29475
  const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
29048
29476
  if (!sel) return;
@@ -29050,7 +29478,7 @@ function useGsapSelectionHandlers({
29050
29478
  },
29051
29479
  [domEditSelection, updateGsapMeta]
29052
29480
  );
29053
- const handleGsapDeleteAnimation = useCallback60(
29481
+ const handleGsapDeleteAnimation = useCallback61(
29054
29482
  (animId) => {
29055
29483
  const sel = domEditSelection ?? lastSelectionRef.current;
29056
29484
  if (!sel) return;
@@ -29058,7 +29486,7 @@ function useGsapSelectionHandlers({
29058
29486
  },
29059
29487
  [domEditSelection, deleteGsapAnimation]
29060
29488
  );
29061
- const handleGsapDeleteAllForElement = useCallback60(
29489
+ const handleGsapDeleteAllForElement = useCallback61(
29062
29490
  (targetSelector) => {
29063
29491
  const sel = domEditSelection ?? lastSelectionRef.current;
29064
29492
  if (!sel) return;
@@ -29067,7 +29495,7 @@ function useGsapSelectionHandlers({
29067
29495
  },
29068
29496
  [domEditSelection, deleteAllForSelector]
29069
29497
  );
29070
- const handleGsapAddAnimation = useCallback60(
29498
+ const handleGsapAddAnimation = useCallback61(
29071
29499
  (method) => {
29072
29500
  if (!domEditSelection) return;
29073
29501
  void addGsapAnimation(domEditSelection, method, usePlayerStore.getState().currentTime).catch(
@@ -29081,42 +29509,42 @@ function useGsapSelectionHandlers({
29081
29509
  },
29082
29510
  [domEditSelection, addGsapAnimation, handleDomManualEditsReset, trackGsapHandlerFailure]
29083
29511
  );
29084
- const handleGsapAddProperty = useCallback60(
29512
+ const handleGsapAddProperty = useCallback61(
29085
29513
  (animId, prop) => {
29086
29514
  if (!domEditSelection) return;
29087
29515
  addGsapProperty(domEditSelection, animId, prop);
29088
29516
  },
29089
29517
  [domEditSelection, addGsapProperty]
29090
29518
  );
29091
- const handleGsapRemoveProperty = useCallback60(
29519
+ const handleGsapRemoveProperty = useCallback61(
29092
29520
  (animId, prop) => {
29093
29521
  if (!domEditSelection) return;
29094
29522
  removeGsapProperty(domEditSelection, animId, prop);
29095
29523
  },
29096
29524
  [domEditSelection, removeGsapProperty]
29097
29525
  );
29098
- const handleGsapUpdateFromProperty = useCallback60(
29526
+ const handleGsapUpdateFromProperty = useCallback61(
29099
29527
  (animId, prop, value) => {
29100
29528
  if (!domEditSelection) return;
29101
29529
  updateGsapFromProperty(domEditSelection, animId, prop, value);
29102
29530
  },
29103
29531
  [domEditSelection, updateGsapFromProperty]
29104
29532
  );
29105
- const handleGsapAddFromProperty = useCallback60(
29533
+ const handleGsapAddFromProperty = useCallback61(
29106
29534
  (animId, prop) => {
29107
29535
  if (!domEditSelection) return;
29108
29536
  addGsapFromProperty(domEditSelection, animId, prop);
29109
29537
  },
29110
29538
  [domEditSelection, addGsapFromProperty]
29111
29539
  );
29112
- const handleGsapRemoveFromProperty = useCallback60(
29540
+ const handleGsapRemoveFromProperty = useCallback61(
29113
29541
  (animId, prop) => {
29114
29542
  if (!domEditSelection) return;
29115
29543
  removeGsapFromProperty(domEditSelection, animId, prop);
29116
29544
  },
29117
29545
  [domEditSelection, removeGsapFromProperty]
29118
29546
  );
29119
- const handleGsapAddKeyframe = useCallback60(
29547
+ const handleGsapAddKeyframe = useCallback61(
29120
29548
  (animId, percentage, property, value, selectionOverride) => {
29121
29549
  const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
29122
29550
  if (!sel) return;
@@ -29125,7 +29553,7 @@ function useGsapSelectionHandlers({
29125
29553
  },
29126
29554
  [domEditSelection, addKeyframe]
29127
29555
  );
29128
- const handleGsapAddKeyframeBatch = useCallback60(
29556
+ const handleGsapAddKeyframeBatch = useCallback61(
29129
29557
  (animId, percentage, properties) => {
29130
29558
  if (!domEditSelection) return Promise.resolve();
29131
29559
  return addKeyframeBatch(domEditSelection, animId, percentage, properties).catch((error) => {
@@ -29134,7 +29562,7 @@ function useGsapSelectionHandlers({
29134
29562
  },
29135
29563
  [domEditSelection, addKeyframeBatch, trackGsapHandlerFailure]
29136
29564
  );
29137
- const handleGsapRemoveKeyframe = useCallback60(
29565
+ const handleGsapRemoveKeyframe = useCallback61(
29138
29566
  (animId, percentage, selectionOverride) => {
29139
29567
  const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
29140
29568
  if (!sel) return;
@@ -29143,7 +29571,7 @@ function useGsapSelectionHandlers({
29143
29571
  },
29144
29572
  [domEditSelection, removeKeyframe]
29145
29573
  );
29146
- const handleGsapMoveKeyframeToPlayhead = useCallback60(
29574
+ const handleGsapMoveKeyframeToPlayhead = useCallback61(
29147
29575
  (animId, fromPercentage, selectionOverride) => {
29148
29576
  const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
29149
29577
  if (!sel) return;
@@ -29154,7 +29582,7 @@ function useGsapSelectionHandlers({
29154
29582
  },
29155
29583
  [domEditSelection, selectedGsapAnimations, moveKeyframe]
29156
29584
  );
29157
- const handleGsapMoveKeyframe = useCallback60(
29585
+ const handleGsapMoveKeyframe = useCallback61(
29158
29586
  (animId, fromPercentage, toPercentage, selectionOverride) => {
29159
29587
  const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
29160
29588
  if (!sel) return;
@@ -29163,7 +29591,7 @@ function useGsapSelectionHandlers({
29163
29591
  },
29164
29592
  [domEditSelection, moveKeyframe]
29165
29593
  );
29166
- const handleGsapResizeKeyframedTween = useCallback60(
29594
+ const handleGsapResizeKeyframedTween = useCallback61(
29167
29595
  (animId, position, duration, pctRemap, selectionOverride) => {
29168
29596
  const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
29169
29597
  if (!sel) return;
@@ -29172,7 +29600,7 @@ function useGsapSelectionHandlers({
29172
29600
  },
29173
29601
  [domEditSelection, resizeKeyframedTween]
29174
29602
  );
29175
- const handleGsapConvertToKeyframes = useCallback60(
29603
+ const handleGsapConvertToKeyframes = useCallback61(
29176
29604
  (animId, resolvedFromValues, duration) => {
29177
29605
  if (!domEditSelection) return Promise.resolve();
29178
29606
  return convertToKeyframes(domEditSelection, animId, resolvedFromValues, duration).catch(
@@ -29188,14 +29616,14 @@ function useGsapSelectionHandlers({
29188
29616
  },
29189
29617
  [domEditSelection, convertToKeyframes, trackGsapHandlerFailure]
29190
29618
  );
29191
- const handleGsapRemoveAllKeyframes = useCallback60(
29619
+ const handleGsapRemoveAllKeyframes = useCallback61(
29192
29620
  (animId) => {
29193
29621
  if (!domEditSelection) return;
29194
29622
  removeAllKeyframes(domEditSelection, animId);
29195
29623
  },
29196
29624
  [domEditSelection, removeAllKeyframes]
29197
29625
  );
29198
- const handleResetSelectedElementKeyframes = useCallback60(() => {
29626
+ const handleResetSelectedElementKeyframes = useCallback61(() => {
29199
29627
  if (!domEditSelection) return false;
29200
29628
  const withKeyframes = selectedGsapAnimations.find((a) => a.keyframes);
29201
29629
  if (!withKeyframes) return false;
@@ -29266,7 +29694,7 @@ function useDomEditWiring({
29266
29694
  removeAllKeyframes,
29267
29695
  handleDomManualEditsReset
29268
29696
  }) {
29269
- const onClickToSource = useCallback61(
29697
+ const onClickToSource = useCallback62(
29270
29698
  (selection) => {
29271
29699
  if (!openSourceForSelection || !selectSidebarTab) return;
29272
29700
  if (!selection.sourceFile) return;
@@ -29288,7 +29716,7 @@ function useDomEditWiring({
29288
29716
  const key = matchKey ? matchKey.key ?? matchKey.id : null;
29289
29717
  if (key && key !== selectedElementId) setSelectedElementId(key);
29290
29718
  }, [domEditSelection?.id]);
29291
- const prevRefreshKeyRef = useRef55(refreshKey);
29719
+ const prevRefreshKeyRef = useRef56(refreshKey);
29292
29720
  useEffect34(() => {
29293
29721
  if (refreshKey !== prevRefreshKeyRef.current) {
29294
29722
  prevRefreshKeyRef.current = refreshKey;
@@ -29366,10 +29794,10 @@ function useDomEditWiring({
29366
29794
  }
29367
29795
 
29368
29796
  // src/hooks/useGsapAwareEditing.ts
29369
- import { useCallback as useCallback63 } from "react";
29797
+ import { useCallback as useCallback64 } from "react";
29370
29798
 
29371
29799
  // src/hooks/useAnimatedPropertyCommit.ts
29372
- import { useCallback as useCallback62 } from "react";
29800
+ import { useCallback as useCallback63 } from "react";
29373
29801
  import { classifyPropertyGroup as classifyPropertyGroup3 } from "@hyperframes/core/gsap-parser";
29374
29802
  function pickBestAnimation(animations, selector, property) {
29375
29803
  const targetGroup = property ? classifyPropertyGroup3(property) : void 0;
@@ -29539,7 +29967,7 @@ async function commitKeyframeProps(selection, anim, props, propEntries, primaryP
29539
29967
  }
29540
29968
  function useAnimatedPropertyCommit(deps) {
29541
29969
  const { selectedGsapAnimations, gsapCommitMutation, previewIframeRef, bumpGsapCache } = deps;
29542
- const commitAnimatedProperties = useCallback62(
29970
+ const commitAnimatedProperties = useCallback63(
29543
29971
  async (selection, props) => {
29544
29972
  if (!gsapCommitMutation) return;
29545
29973
  const propEntries = Object.entries(props);
@@ -29638,7 +30066,7 @@ function useAnimatedPropertyCommit(deps) {
29638
30066
  },
29639
30067
  [selectedGsapAnimations, gsapCommitMutation, previewIframeRef, bumpGsapCache]
29640
30068
  );
29641
- const commitAnimatedProperty = useCallback62(
30069
+ const commitAnimatedProperty = useCallback63(
29642
30070
  (selection, property, value) => commitAnimatedProperties(selection, { [property]: value }),
29643
30071
  [commitAnimatedProperties]
29644
30072
  );
@@ -29661,7 +30089,7 @@ function useGsapAwareEditing({
29661
30089
  setArcPath,
29662
30090
  updateArcSegment
29663
30091
  }) {
29664
- const handleGsapAwarePathOffsetCommit = useCallback63(
30092
+ const handleGsapAwarePathOffsetCommit = useCallback64(
29665
30093
  async (selection, next, modifiers) => {
29666
30094
  if (gsapCommitMutation) {
29667
30095
  try {
@@ -29688,7 +30116,7 @@ function useGsapAwareEditing({
29688
30116
  trackGsapInteractionFailure
29689
30117
  ]
29690
30118
  );
29691
- const handleGsapAwareGroupPathOffsetCommit = useCallback63(
30119
+ const handleGsapAwareGroupPathOffsetCommit = useCallback64(
29692
30120
  async (updates) => {
29693
30121
  if (!gsapCommitMutation) return;
29694
30122
  for (const { selection, next } of updates) {
@@ -29709,7 +30137,7 @@ function useGsapAwareEditing({
29709
30137
  },
29710
30138
  [gsapCommitMutation, previewIframeRef, makeFetchFallback, trackGsapInteractionFailure]
29711
30139
  );
29712
- const handleGsapAwareBoxSizeCommit = useCallback63(
30140
+ const handleGsapAwareBoxSizeCommit = useCallback64(
29713
30141
  async (selection, next) => {
29714
30142
  if (gsapCommitMutation) {
29715
30143
  try {
@@ -29738,7 +30166,7 @@ function useGsapAwareEditing({
29738
30166
  trackGsapInteractionFailure
29739
30167
  ]
29740
30168
  );
29741
- const handleGsapAwareRotationCommit = useCallback63(
30169
+ const handleGsapAwareRotationCommit = useCallback64(
29742
30170
  async (selection, next) => {
29743
30171
  if (gsapCommitMutation) {
29744
30172
  try {
@@ -29772,21 +30200,21 @@ function useGsapAwareEditing({
29772
30200
  previewIframeRef,
29773
30201
  bumpGsapCache
29774
30202
  });
29775
- const handleSetArcPath = useCallback63(
30203
+ const handleSetArcPath = useCallback64(
29776
30204
  (animId, config) => {
29777
30205
  if (!domEditSelection) return;
29778
30206
  setArcPath(domEditSelection, animId, config);
29779
30207
  },
29780
30208
  [domEditSelection, setArcPath]
29781
30209
  );
29782
- const handleUpdateArcSegment = useCallback63(
30210
+ const handleUpdateArcSegment = useCallback64(
29783
30211
  (animId, segmentIndex, update) => {
29784
30212
  if (!domEditSelection) return;
29785
30213
  updateArcSegment(domEditSelection, animId, segmentIndex, update);
29786
30214
  },
29787
30215
  [domEditSelection, updateArcSegment]
29788
30216
  );
29789
- const noopCommit = useCallback63(async () => {
30217
+ const noopCommit = useCallback64(async () => {
29790
30218
  }, []);
29791
30219
  const trackGsapSaveFailure = useGsapSaveFailureTelemetry(null);
29792
30220
  const safeGsapCommit = useSafeGsapCommitMutation(
@@ -29794,14 +30222,14 @@ function useGsapAwareEditing({
29794
30222
  trackGsapSaveFailure,
29795
30223
  showToast
29796
30224
  );
29797
- const commitMutation = useCallback63(
30225
+ const commitMutation = useCallback64(
29798
30226
  async (mutation, options) => {
29799
30227
  if (!domEditSelection) return;
29800
30228
  await safeGsapCommit(domEditSelection, mutation, options);
29801
30229
  },
29802
30230
  [domEditSelection, safeGsapCommit]
29803
30231
  );
29804
- const handleUnroll = useCallback63(() => {
30232
+ const handleUnroll = useCallback64(() => {
29805
30233
  void commitMutation(
29806
30234
  { type: "unroll-timeline" },
29807
30235
  { label: "Unroll to literal tweens", softReload: true }
@@ -29822,7 +30250,7 @@ function useGsapAwareEditing({
29822
30250
  }
29823
30251
 
29824
30252
  // src/hooks/useStudioSelectionPublisher.ts
29825
- import { useEffect as useEffect35, useRef as useRef56 } from "react";
30253
+ import { useEffect as useEffect35, useRef as useRef57 } from "react";
29826
30254
 
29827
30255
  // src/utils/studioSelectionSnapshot.ts
29828
30256
  function round32(value) {
@@ -29912,8 +30340,8 @@ function useStudioSelectionPublisher({
29912
30340
  previewDocumentVersion,
29913
30341
  refreshDomEditSelectionFromPreview
29914
30342
  }) {
29915
- const lastSelectionRefreshKeyRef = useRef56(refreshKey);
29916
- const pendingSelectionRefreshKeyRef = useRef56(null);
30343
+ const lastSelectionRefreshKeyRef = useRef57(refreshKey);
30344
+ const pendingSelectionRefreshKeyRef = useRef57(null);
29917
30345
  useEffect35(() => {
29918
30346
  if (!projectId) return;
29919
30347
  const selection = domEditSelection?.element.isConnected ? buildStudioSelectionSnapshot({
@@ -30164,7 +30592,7 @@ function useDomEditSession({
30164
30592
  clearDomSelection,
30165
30593
  forceReloadSdkSession
30166
30594
  });
30167
- const handleGroupSelection = useCallback64(() => {
30595
+ const handleGroupSelection = useCallback65(() => {
30168
30596
  const group = domEditGroupSelectionsRef.current;
30169
30597
  const single = domEditSelectionRef.current;
30170
30598
  const members = group.length > 0 ? group : single ? [single] : [];
@@ -30175,7 +30603,7 @@ function useDomEditSession({
30175
30603
  trackStudioEvent("group", { action: "create", count: members.length });
30176
30604
  void groupSelection(members);
30177
30605
  }, [domEditGroupSelectionsRef, domEditSelectionRef, groupSelection, showToast]);
30178
- const handleUngroupSelection = useCallback64(() => {
30606
+ const handleUngroupSelection = useCallback65(() => {
30179
30607
  const sel = domEditSelectionRef.current;
30180
30608
  if (!sel?.element.hasAttribute("data-hf-group")) {
30181
30609
  showToast("Select a group to ungroup", "info");
@@ -30295,7 +30723,7 @@ function useDomEditSession({
30295
30723
  setArcPath,
30296
30724
  updateArcSegment
30297
30725
  });
30298
- const handleUpdateKeyframeEase = useCallback64(
30726
+ const handleUpdateKeyframeEase = useCallback65(
30299
30727
  (animationId, percentage, ease) => {
30300
30728
  const sel = domEditSelectionRef.current;
30301
30729
  if (!sel) return;
@@ -30313,7 +30741,7 @@ function useDomEditSession({
30313
30741
  },
30314
30742
  [gsapCommitMutation, domEditSelectionRef]
30315
30743
  );
30316
- const handleSetAllKeyframeEases = useCallback64(
30744
+ const handleSetAllKeyframeEases = useCallback65(
30317
30745
  (animationId, ease) => {
30318
30746
  const sel = domEditSelectionRef.current;
30319
30747
  if (!sel) return;
@@ -30415,7 +30843,7 @@ function useDomEditSession({
30415
30843
  }
30416
30844
 
30417
30845
  // src/hooks/useSdkSession.ts
30418
- import { useState as useState48, useEffect as useEffect36, useCallback as useCallback65 } from "react";
30846
+ import { useState as useState48, useEffect as useEffect36, useCallback as useCallback66 } from "react";
30419
30847
  import { openComposition } from "@hyperframes/sdk";
30420
30848
  async function readProjectFileOptional(projectId, path) {
30421
30849
  if (path.includes("\0") || path.includes("..")) return void 0;
@@ -30494,7 +30922,7 @@ function useSdkSession(projectId, activeCompPath, domEditSaveTimestampRef) {
30494
30922
  compRef.current?.dispose();
30495
30923
  };
30496
30924
  }, [projectId, activeCompPath, reloadToken]);
30497
- const forceReload = useCallback65(() => setReloadToken((t) => t + 1), []);
30925
+ const forceReload = useCallback66(() => setReloadToken((t) => t + 1), []);
30498
30926
  return { session, forceReload };
30499
30927
  }
30500
30928
 
@@ -30512,7 +30940,7 @@ function useSdkSelectionSync(session, domEditSelection, domEditGroupSelections)
30512
30940
  }
30513
30941
 
30514
30942
  // src/hooks/useBlockHandlers.ts
30515
- import { useCallback as useCallback66, useMemo as useMemo23, useState as useState49 } from "react";
30943
+ import { useCallback as useCallback67, useMemo as useMemo23, useState as useState49 } from "react";
30516
30944
 
30517
30945
  // src/utils/blockInstaller.ts
30518
30946
  function getMaxZIndexFromIframe(iframe) {
@@ -30680,7 +31108,7 @@ function useBlockHandlers({
30680
31108
  blockCtxDeps.showToast
30681
31109
  ]
30682
31110
  );
30683
- const handleAddBlock = useCallback66(
31111
+ const handleAddBlock = useCallback67(
30684
31112
  (blockName) => {
30685
31113
  if (!projectId) return;
30686
31114
  void (async () => {
@@ -30706,7 +31134,7 @@ function useBlockHandlers({
30706
31134
  },
30707
31135
  [projectId, blockCtx, previewIframeRef, setRightCollapsed, setRightPanelTab]
30708
31136
  );
30709
- const handleTimelineBlockDrop = useCallback66(
31137
+ const handleTimelineBlockDrop = useCallback67(
30710
31138
  (blockName, placement) => {
30711
31139
  if (!projectId) return;
30712
31140
  void addBlockToProject({
@@ -30720,7 +31148,7 @@ function useBlockHandlers({
30720
31148
  },
30721
31149
  [projectId, blockCtx, previewIframeRef]
30722
31150
  );
30723
- const handlePreviewBlockDrop = useCallback66(
31151
+ const handlePreviewBlockDrop = useCallback67(
30724
31152
  (blockName, position) => {
30725
31153
  if (!projectId) return;
30726
31154
  void addBlockToProject({
@@ -30744,7 +31172,7 @@ function useBlockHandlers({
30744
31172
  }
30745
31173
 
30746
31174
  // src/hooks/useAppHotkeys.ts
30747
- import { useCallback as useCallback67, useEffect as useEffect38, useRef as useRef57 } from "react";
31175
+ import { useCallback as useCallback68, useEffect as useEffect38, useRef as useRef58 } from "react";
30748
31176
  function iframeContentWindow(iframe) {
30749
31177
  try {
30750
31178
  return iframe?.contentWindow ?? null;
@@ -30951,9 +31379,9 @@ function useAppHotkeys({
30951
31379
  activeCompPath,
30952
31380
  forceReloadSdkSession
30953
31381
  }) {
30954
- const previewHotkeyWindowRef = useRef57(null);
30955
- const previewHistoryCleanupRef = useRef57(null);
30956
- const handleTimelineToggleHotkey = useCallback67(
31382
+ const previewHotkeyWindowRef = useRef58(null);
31383
+ const previewHistoryCleanupRef = useRef58(null);
31384
+ const handleTimelineToggleHotkey = useCallback68(
30957
31385
  (event) => {
30958
31386
  if (!shouldHandleTimelineToggleHotkey(event)) return;
30959
31387
  event.preventDefault();
@@ -30961,18 +31389,18 @@ function useAppHotkeys({
30961
31389
  },
30962
31390
  [toggleTimelineVisibility]
30963
31391
  );
30964
- const readHistoryFile = useCallback67(
31392
+ const readHistoryFile = useCallback68(
30965
31393
  (path) => path === STUDIO_MOTION_PATH ? readOptionalProjectFile(path) : readProjectFile(path),
30966
31394
  [readOptionalProjectFile, readProjectFile]
30967
31395
  );
30968
- const writeHistoryFile = useCallback67(
31396
+ const writeHistoryFile = useCallback68(
30969
31397
  async (path, content) => {
30970
31398
  domEditSaveTimestampRef.current = Date.now();
30971
31399
  await writeProjectFile(path, content);
30972
31400
  },
30973
31401
  [domEditSaveTimestampRef, writeProjectFile]
30974
31402
  );
30975
- const applyHistory = useCallback67(
31403
+ const applyHistory = useCallback68(
30976
31404
  async (direction) => {
30977
31405
  if (tryApplyBeatHistory(direction, editHistory.state, showToast)) return;
30978
31406
  await waitForPendingDomEditSaves();
@@ -31008,9 +31436,9 @@ function useAppHotkeys({
31008
31436
  forceReloadSdkSession
31009
31437
  ]
31010
31438
  );
31011
- const handleUndo = useCallback67(() => applyHistory("undo"), [applyHistory]);
31012
- const handleRedo = useCallback67(() => applyHistory("redo"), [applyHistory]);
31013
- const cbRef = useRef57(null);
31439
+ const handleUndo = useCallback68(() => applyHistory("undo"), [applyHistory]);
31440
+ const handleRedo = useCallback68(() => applyHistory("redo"), [applyHistory]);
31441
+ const cbRef = useRef58(null);
31014
31442
  cbRef.current = {
31015
31443
  toggleTimelineVisibility,
31016
31444
  handleTimelineElementDelete,
@@ -31030,7 +31458,7 @@ function useAppHotkeys({
31030
31458
  domEditSelectionRef,
31031
31459
  showToast
31032
31460
  };
31033
- const handleAppKeyDown = useCallback67((event) => {
31461
+ const handleAppKeyDown = useCallback68((event) => {
31034
31462
  const cb = cbRef.current;
31035
31463
  if (shouldHandleTimelineToggleHotkey(event)) {
31036
31464
  event.preventDefault();
@@ -31048,7 +31476,7 @@ function useAppHotkeys({
31048
31476
  window.addEventListener("keydown", handleAppKeyDown, true);
31049
31477
  return () => window.removeEventListener("keydown", handleAppKeyDown, true);
31050
31478
  }, [handleAppKeyDown]);
31051
- const syncPreviewTimelineHotkey = useCallback67(
31479
+ const syncPreviewTimelineHotkey = useCallback68(
31052
31480
  (iframe) => {
31053
31481
  const nextWindow = iframeContentWindow(iframe);
31054
31482
  if (previewHotkeyWindowRef.current === nextWindow) return;
@@ -31073,7 +31501,7 @@ function useAppHotkeys({
31073
31501
  },
31074
31502
  [handleAppKeyDown]
31075
31503
  );
31076
- const handleHistoryHotkey = useCallback67((event) => {
31504
+ const handleHistoryHotkey = useCallback68((event) => {
31077
31505
  if (!(event.metaKey || event.ctrlKey) || shouldIgnoreHistoryShortcut(event.target)) return;
31078
31506
  handleUndoRedoKey(
31079
31507
  event,
@@ -31081,7 +31509,7 @@ function useAppHotkeys({
31081
31509
  () => void cbRef.current.handleRedo()
31082
31510
  );
31083
31511
  }, []);
31084
- const syncPreviewHistoryHotkey = useCallback67(
31512
+ const syncPreviewHistoryHotkey = useCallback68(
31085
31513
  (iframe) => {
31086
31514
  previewHistoryCleanupRef.current?.();
31087
31515
  previewHistoryCleanupRef.current = null;
@@ -31120,7 +31548,7 @@ function useAppHotkeys({
31120
31548
  }
31121
31549
 
31122
31550
  // src/hooks/useClipboard.ts
31123
- import { useCallback as useCallback68, useRef as useRef58 } from "react";
31551
+ import { useCallback as useCallback69, useRef as useRef59 } from "react";
31124
31552
 
31125
31553
  // src/utils/clipboardPayload.ts
31126
31554
  function insertAsSibling(source, newHtml, selector, selectorIndex) {
@@ -31235,10 +31663,10 @@ function useClipboard({
31235
31663
  handleDomEditElementDelete,
31236
31664
  previewIframeRef
31237
31665
  }) {
31238
- const clipboardRef = useRef58(null);
31239
- const projectIdRef = useRef58(projectId);
31666
+ const clipboardRef = useRef59(null);
31667
+ const projectIdRef = useRef59(projectId);
31240
31668
  projectIdRef.current = projectId;
31241
- const handleCopy = useCallback68(() => {
31669
+ const handleCopy = useCallback69(() => {
31242
31670
  const { selectedElementId, elements } = usePlayerStore.getState();
31243
31671
  if (selectedElementId) {
31244
31672
  const element = elements.find((el) => (el.key ?? el.id) === selectedElementId);
@@ -31288,7 +31716,7 @@ function useClipboard({
31288
31716
  }
31289
31717
  return false;
31290
31718
  }, [activeCompPath, domEditSelectionRef, previewIframeRef, showToast]);
31291
- const handlePaste = useCallback68(async () => {
31719
+ const handlePaste = useCallback69(async () => {
31292
31720
  const payload = clipboardRef.current;
31293
31721
  if (!payload) {
31294
31722
  showToast("Nothing to paste.", "info");
@@ -31344,7 +31772,7 @@ function useClipboard({
31344
31772
  showToast,
31345
31773
  writeProjectFile
31346
31774
  ]);
31347
- const handleCut = useCallback68(async () => {
31775
+ const handleCut = useCallback69(async () => {
31348
31776
  const copied = handleCopy();
31349
31777
  if (!copied) return false;
31350
31778
  const { selectedElementId, elements } = usePlayerStore.getState();
@@ -31719,11 +32147,11 @@ function useCaptionDetection({
31719
32147
  }
31720
32148
 
31721
32149
  // src/hooks/useRenderClipContent.ts
31722
- import { useCallback as useCallback70 } from "react";
32150
+ import { useCallback as useCallback71 } from "react";
31723
32151
  import { createElement } from "react";
31724
32152
 
31725
32153
  // src/player/components/AudioWaveform.tsx
31726
- import { memo as memo25, useRef as useRef59, useState as useState50, useCallback as useCallback69, useEffect as useEffect40 } from "react";
32154
+ import { memo as memo25, useRef as useRef60, useState as useState50, useCallback as useCallback70, useEffect as useEffect40 } from "react";
31727
32155
  import { jsx as jsx64, jsxs as jsxs55 } from "react/jsx-runtime";
31728
32156
  var BAR_W = 2;
31729
32157
  var GAP = 1;
@@ -31771,9 +32199,9 @@ var AudioWaveform = memo25(function AudioWaveform2({
31771
32199
  trimStartFraction,
31772
32200
  trimEndFraction
31773
32201
  }) {
31774
- const containerRef = useRef59(null);
31775
- const barsRef = useRef59(null);
31776
- const roRef = useRef59(null);
32202
+ const containerRef = useRef60(null);
32203
+ const barsRef = useRef60(null);
32204
+ const roRef = useRef60(null);
31777
32205
  const cacheKey = waveformUrl ?? audioUrl;
31778
32206
  const [peaks, setPeaks] = useState50(peaksCache.get(cacheKey) ?? null);
31779
32207
  useEffect40(() => {
@@ -31800,7 +32228,7 @@ var AudioWaveform = memo25(function AudioWaveform2({
31800
32228
  cancelled = true;
31801
32229
  };
31802
32230
  }, [audioUrl, waveformUrl, cacheKey, peaks]);
31803
- const draw = useCallback69(() => {
32231
+ const draw = useCallback70(() => {
31804
32232
  const container = containerRef.current;
31805
32233
  const barsEl = barsRef.current;
31806
32234
  if (!container || !barsEl || !peaks) return;
@@ -31821,7 +32249,7 @@ var AudioWaveform = memo25(function AudioWaveform2({
31821
32249
  }
31822
32250
  barsEl.innerHTML = html2;
31823
32251
  }, [peaks, trimStartFraction, trimEndFraction]);
31824
- const setContainerRef = useCallback69(
32252
+ const setContainerRef = useCallback70(
31825
32253
  (el) => {
31826
32254
  roRef.current?.disconnect();
31827
32255
  containerRef.current = el;
@@ -31912,7 +32340,7 @@ function useRenderClipContent({
31912
32340
  activePreviewUrl,
31913
32341
  effectiveTimelineDuration
31914
32342
  }) {
31915
- return useCallback70(
32343
+ return useCallback71(
31916
32344
  // Pre-existing clip-content dispatcher; reduced by extracting renderAudioClip.
31917
32345
  // fallow-ignore-next-line complexity
31918
32346
  (el, style) => {
@@ -31977,11 +32405,11 @@ function useRenderClipContent({
31977
32405
  }
31978
32406
 
31979
32407
  // src/hooks/useConsoleErrorCapture.ts
31980
- import { useCallback as useCallback71, useEffect as useEffect41, useRef as useRef60, useState as useState51 } from "react";
32408
+ import { useCallback as useCallback72, useEffect as useEffect41, useRef as useRef61, useState as useState51 } from "react";
31981
32409
  function useConsoleErrorCapture(previewIframe) {
31982
32410
  const [consoleErrors, setConsoleErrors] = useState51(null);
31983
- const consoleErrorsRef = useRef60([]);
31984
- const resetErrors = useCallback71(() => {
32411
+ const consoleErrorsRef = useRef61([]);
32412
+ const resetErrors = useCallback72(() => {
31985
32413
  consoleErrorsRef.current = [];
31986
32414
  setConsoleErrors(null);
31987
32415
  }, []);
@@ -32050,7 +32478,7 @@ function useConsoleErrorCapture(previewIframe) {
32050
32478
  }
32051
32479
 
32052
32480
  // src/hooks/useFrameCapture.ts
32053
- import { useState as useState52, useCallback as useCallback72 } from "react";
32481
+ import { useState as useState52, useCallback as useCallback73 } from "react";
32054
32482
 
32055
32483
  // src/utils/projectRouting.ts
32056
32484
  var PROJECT_HASH_PREFIX = "#project/";
@@ -32137,10 +32565,10 @@ function useFrameCapture({
32137
32565
  setCaptureFrameTime(usePlayerStore.getState().currentTime);
32138
32566
  return liveTime.subscribe(setCaptureFrameTime);
32139
32567
  });
32140
- const refreshCaptureFrameTime = useCallback72(() => {
32568
+ const refreshCaptureFrameTime = useCallback73(() => {
32141
32569
  setCaptureFrameTime(usePlayerStore.getState().currentTime);
32142
32570
  }, []);
32143
- const handleCaptureFrameClick = useCallback72(
32571
+ const handleCaptureFrameClick = useCallback73(
32144
32572
  async (event) => {
32145
32573
  if (!projectId) return;
32146
32574
  event.preventDefault();
@@ -32210,7 +32638,7 @@ function useFrameCapture({
32210
32638
  }
32211
32639
 
32212
32640
  // src/hooks/useLintModal.ts
32213
- import { useState as useState53, useCallback as useCallback73, useEffect as useEffect42, useRef as useRef61, useMemo as useMemo24 } from "react";
32641
+ import { useState as useState53, useCallback as useCallback74, useEffect as useEffect42, useRef as useRef62, useMemo as useMemo24 } from "react";
32214
32642
  function parseFinding(f) {
32215
32643
  return {
32216
32644
  severity: f.severity === "error" ? "error" : "warning",
@@ -32224,8 +32652,8 @@ function useLintModal(projectId, refreshKey) {
32224
32652
  const [lintModal, setLintModal] = useState53(null);
32225
32653
  const [linting, setLinting] = useState53(false);
32226
32654
  const [backgroundFindings, setBackgroundFindings] = useState53([]);
32227
- const autoLintRanRef = useRef61(false);
32228
- const runLint = useCallback73(
32655
+ const autoLintRanRef = useRef62(false);
32656
+ const runLint = useCallback74(
32229
32657
  async (opts) => {
32230
32658
  if (!projectId) return;
32231
32659
  if (!opts?.background) setLinting(true);
@@ -32250,8 +32678,8 @@ function useLintModal(projectId, refreshKey) {
32250
32678
  },
32251
32679
  [projectId]
32252
32680
  );
32253
- const handleLint = useCallback73(() => runLint(), [runLint]);
32254
- const prevProjectIdRef = useRef61(projectId);
32681
+ const handleLint = useCallback74(() => runLint(), [runLint]);
32682
+ const prevProjectIdRef = useRef62(projectId);
32255
32683
  useEffect42(() => {
32256
32684
  if (projectId !== prevProjectIdRef.current) {
32257
32685
  autoLintRanRef.current = false;
@@ -32266,8 +32694,8 @@ function useLintModal(projectId, refreshKey) {
32266
32694
  const timer = setTimeout(() => void runLint({ background: true }), 1e3);
32267
32695
  return () => clearTimeout(timer);
32268
32696
  }, [projectId, refreshKey, runLint]);
32269
- const closeLintModal = useCallback73(() => setLintModal(null), []);
32270
- const groupFindings = useCallback73(
32697
+ const closeLintModal = useCallback74(() => setLintModal(null), []);
32698
+ const groupFindings = useCallback74(
32271
32699
  (keyFn) => {
32272
32700
  const map = /* @__PURE__ */ new Map();
32273
32701
  for (const f of backgroundFindings) {
@@ -32321,11 +32749,11 @@ function useCompositionDimensions() {
32321
32749
  }
32322
32750
 
32323
32751
  // src/hooks/useToast.ts
32324
- import { useState as useState55, useCallback as useCallback74, useRef as useRef62 } from "react";
32752
+ import { useState as useState55, useCallback as useCallback75, useRef as useRef63 } from "react";
32325
32753
  function useToast() {
32326
32754
  const [appToast, setAppToast] = useState55(null);
32327
- const timerRef = useRef62(null);
32328
- const showToast = useCallback74((message, tone = "error") => {
32755
+ const timerRef = useRef63(null);
32756
+ const showToast = useCallback75((message, tone = "error") => {
32329
32757
  if (timerRef.current) clearTimeout(timerRef.current);
32330
32758
  setAppToast({ message, tone });
32331
32759
  timerRef.current = setTimeout(() => setAppToast(null), 4e3);
@@ -32333,7 +32761,7 @@ function useToast() {
32333
32761
  useMountEffect(() => () => {
32334
32762
  if (timerRef.current) clearTimeout(timerRef.current);
32335
32763
  });
32336
- const dismissToast = useCallback74(() => {
32764
+ const dismissToast = useCallback75(() => {
32337
32765
  if (timerRef.current) clearTimeout(timerRef.current);
32338
32766
  setAppToast(null);
32339
32767
  }, []);
@@ -32341,7 +32769,7 @@ function useToast() {
32341
32769
  }
32342
32770
 
32343
32771
  // src/hooks/useStudioUrlState.ts
32344
- import { useCallback as useCallback75, useEffect as useEffect43, useRef as useRef63, useState as useState56 } from "react";
32772
+ import { useCallback as useCallback76, useEffect as useEffect43, useRef as useRef64, useState as useState56 } from "react";
32345
32773
 
32346
32774
  // src/utils/studioUrlState.ts
32347
32775
  var VALID_TABS = ["layers", "design", "renders"];
@@ -32465,13 +32893,13 @@ function useStudioUrlState({
32465
32893
  initialState: initialState2
32466
32894
  }) {
32467
32895
  const currentTime = usePlayerStore((s) => s.currentTime);
32468
- const hydratedSeekRef = useRef63(initialState2.currentTime == null);
32469
- const hydratedInitialTimeRef = useRef63(initialState2.currentTime == null);
32470
- const hydratedSelectionRef = useRef63(initialState2.selection == null);
32896
+ const hydratedSeekRef = useRef64(initialState2.currentTime == null);
32897
+ const hydratedInitialTimeRef = useRef64(initialState2.currentTime == null);
32898
+ const hydratedSelectionRef = useRef64(initialState2.selection == null);
32471
32899
  const [selectionHydrated, setSelectionHydrated] = useState56(initialState2.selection == null);
32472
- const pendingSelectionRef = useRef63(initialState2.selection);
32473
- const stableTimeRef = useRef63(initialState2.currentTime);
32474
- const buildUrlState = useCallback75(
32900
+ const pendingSelectionRef = useRef64(initialState2.selection);
32901
+ const stableTimeRef = useRef64(initialState2.currentTime);
32902
+ const buildUrlState = useCallback76(
32475
32903
  () => ({
32476
32904
  activeCompPath,
32477
32905
  currentTime: stableTimeRef.current,
@@ -32482,7 +32910,7 @@ function useStudioUrlState({
32482
32910
  }),
32483
32911
  [activeCompPath, domEditSelection, rightCollapsed, rightPanelTab, timelineVisible]
32484
32912
  );
32485
- const applyUrlSelection = useCallback75(
32913
+ const applyUrlSelection = useCallback76(
32486
32914
  (selection) => {
32487
32915
  if (!selection) {
32488
32916
  applyDomSelection(null, { revealPanel: false });
@@ -32598,7 +33026,7 @@ function useStudioUrlState({
32598
33026
  }
32599
33027
 
32600
33028
  // src/hooks/useStudioContextValue.ts
32601
- import { useCallback as useCallback76, useMemo as useMemo25, useRef as useRef64, useState as useState57 } from "react";
33029
+ import { useCallback as useCallback77, useMemo as useMemo25, useRef as useRef65, useState as useState57 } from "react";
32602
33030
  function buildStudioContextValue(input) {
32603
33031
  return {
32604
33032
  projectId: input.projectId,
@@ -32643,22 +33071,22 @@ function useInspectorState(rightPanelTab, rightInspectorPanes, rightCollapsed, i
32643
33071
  }
32644
33072
  function useDragOverlay(onImportFiles) {
32645
33073
  const [active, setActive] = useState57(false);
32646
- const counterRef = useRef64(0);
32647
- const onDragOver = useCallback76((e) => {
33074
+ const counterRef = useRef65(0);
33075
+ const onDragOver = useCallback77((e) => {
32648
33076
  if (!e.dataTransfer.types.includes("Files")) return;
32649
33077
  e.preventDefault();
32650
33078
  }, []);
32651
- const onDragEnter = useCallback76((e) => {
33079
+ const onDragEnter = useCallback77((e) => {
32652
33080
  if (!e.dataTransfer.types.includes("Files")) return;
32653
33081
  e.preventDefault();
32654
33082
  counterRef.current++;
32655
33083
  setActive(true);
32656
33084
  }, []);
32657
- const onDragLeave = useCallback76(() => {
33085
+ const onDragLeave = useCallback77(() => {
32658
33086
  counterRef.current--;
32659
33087
  if (counterRef.current === 0) setActive(false);
32660
33088
  }, []);
32661
- const onDrop = useCallback76(
33089
+ const onDrop = useCallback77(
32662
33090
  (e) => {
32663
33091
  counterRef.current = 0;
32664
33092
  setActive(false);
@@ -32742,7 +33170,7 @@ function PanelLayoutProvider({
32742
33170
  // src/contexts/ViewModeContext.tsx
32743
33171
  import {
32744
33172
  createContext as createContext5,
32745
- useCallback as useCallback77,
33173
+ useCallback as useCallback78,
32746
33174
  useContext as useContext5,
32747
33175
  useEffect as useEffect44,
32748
33176
  useMemo as useMemo27,
@@ -32771,7 +33199,7 @@ function useViewModeState() {
32771
33199
  window.addEventListener("popstate", onPopState);
32772
33200
  return () => window.removeEventListener("popstate", onPopState);
32773
33201
  }, []);
32774
- const setViewMode = useCallback77((mode) => {
33202
+ const setViewMode = useCallback78((mode) => {
32775
33203
  setMode(mode);
32776
33204
  writeViewModeToUrl(mode);
32777
33205
  }, []);
@@ -33134,10 +33562,10 @@ function StudioHeader({
33134
33562
  }
33135
33563
 
33136
33564
  // src/hooks/useGestureCommit.ts
33137
- import { useState as useState60, useCallback as useCallback79, useRef as useRef66, useEffect as useEffect46 } from "react";
33565
+ import { useState as useState60, useCallback as useCallback80, useRef as useRef67, useEffect as useEffect46 } from "react";
33138
33566
 
33139
33567
  // src/hooks/useGestureRecording.ts
33140
- import { useCallback as useCallback78, useEffect as useEffect45, useRef as useRef65, useState as useState59 } from "react";
33568
+ import { useCallback as useCallback79, useEffect as useEffect45, useRef as useRef66, useState as useState59 } from "react";
33141
33569
  function readBasePosition(element, iframeEl) {
33142
33570
  let baseOpacity = 1;
33143
33571
  let baseScale = 1;
@@ -33256,10 +33684,10 @@ function createRecordingRefs() {
33256
33684
  function useGestureRecording() {
33257
33685
  const [isRecording, setIsRecording] = useState59(false);
33258
33686
  const [recordingDuration, setRecordingDuration] = useState59(0);
33259
- const isRecordingRef = useRef65(false);
33260
- const refs = useRef65(createRecordingRefs());
33261
- const samplesRef = useRef65(refs.current.samples);
33262
- const trailRef = useRef65(refs.current.trail);
33687
+ const isRecordingRef = useRef66(false);
33688
+ const refs = useRef66(createRecordingRefs());
33689
+ const samplesRef = useRef66(refs.current.samples);
33690
+ const trailRef = useRef66(refs.current.trail);
33263
33691
  useEffect45(() => {
33264
33692
  const r = refs.current;
33265
33693
  return () => {
@@ -33268,7 +33696,7 @@ function useGestureRecording() {
33268
33696
  isRecordingRef.current = false;
33269
33697
  };
33270
33698
  }, []);
33271
- const startRecording = useCallback78(
33699
+ const startRecording = useCallback79(
33272
33700
  (element, iframeEl, elementEndTime) => {
33273
33701
  if (isRecordingRef.current) return;
33274
33702
  isRecordingRef.current = true;
@@ -33368,7 +33796,7 @@ function useGestureRecording() {
33368
33796
  []
33369
33797
  // No deps — uses refs only for all mutable state
33370
33798
  );
33371
- const stopRecording = useCallback78(() => {
33799
+ const stopRecording = useCallback79(() => {
33372
33800
  if (!isRecordingRef.current) return [];
33373
33801
  isRecordingRef.current = false;
33374
33802
  const r = refs.current;
@@ -33398,7 +33826,7 @@ function useGestureRecording() {
33398
33826
  setIsRecording(false);
33399
33827
  return frozen;
33400
33828
  }, []);
33401
- const clearSamples = useCallback78(() => {
33829
+ const clearSamples = useCallback79(() => {
33402
33830
  const r = refs.current;
33403
33831
  r.samples = [];
33404
33832
  r.trail = [];
@@ -33651,13 +34079,13 @@ function useGestureCommit({
33651
34079
  }) {
33652
34080
  const gestureRecording = useGestureRecording();
33653
34081
  const [gestureState, setGestureState] = useState60("idle");
33654
- const gestureStateRef = useRef66("idle");
33655
- const recordingAutoStopRef = useRef66(void 0);
33656
- const recordingStartTimeRef = useRef66(0);
33657
- const commitInFlightRef = useRef66(false);
33658
- const capturedSelectionRef = useRef66(null);
34082
+ const gestureStateRef = useRef67("idle");
34083
+ const recordingAutoStopRef = useRef67(void 0);
34084
+ const recordingStartTimeRef = useRef67(0);
34085
+ const commitInFlightRef = useRef67(false);
34086
+ const capturedSelectionRef = useRef67(null);
33659
34087
  useEffect46(() => () => clearInterval(recordingAutoStopRef.current), []);
33660
- const stopAndCommitRecording = useCallback79(async () => {
34088
+ const stopAndCommitRecording = useCallback80(async () => {
33661
34089
  clearInterval(recordingAutoStopRef.current);
33662
34090
  if (commitInFlightRef.current) {
33663
34091
  return;
@@ -33809,7 +34237,7 @@ function useGestureCommit({
33809
34237
  commitInFlightRef.current = false;
33810
34238
  }
33811
34239
  }, [gestureRecording, showToast, isGestureRecordingRef, domEditSessionRef]);
33812
- const handleToggleRecording = useCallback79(() => {
34240
+ const handleToggleRecording = useCallback80(() => {
33813
34241
  if (gestureStateRef.current === "recording") {
33814
34242
  void stopAndCommitRecording();
33815
34243
  return;
@@ -33967,20 +34395,20 @@ var GestureTrailOverlay = memo26(function GestureTrailOverlay2({
33967
34395
  });
33968
34396
 
33969
34397
  // src/components/StudioLeftSidebar.tsx
33970
- import { useCallback as useCallback85 } from "react";
34398
+ import { useCallback as useCallback86 } from "react";
33971
34399
 
33972
34400
  // src/components/sidebar/LeftSidebar.tsx
33973
34401
  import {
33974
34402
  memo as memo30,
33975
34403
  useState as useState67,
33976
- useCallback as useCallback84,
34404
+ useCallback as useCallback85,
33977
34405
  useImperativeHandle,
33978
- useRef as useRef71,
34406
+ useRef as useRef72,
33979
34407
  forwardRef as forwardRef3
33980
34408
  } from "react";
33981
34409
 
33982
34410
  // src/components/sidebar/CompositionsTab.tsx
33983
- import { memo as memo27, useCallback as useCallback80, useEffect as useEffect47, useRef as useRef67, useState as useState61 } from "react";
34411
+ import { memo as memo27, useCallback as useCallback81, useEffect as useEffect47, useRef as useRef68, useState as useState61 } from "react";
33984
34412
  import { jsx as jsx69, jsxs as jsxs58 } from "react/jsx-runtime";
33985
34413
  var DEFAULT_PREVIEW_STAGE = { width: 1920, height: 1080 };
33986
34414
  var CARD_W = 80;
@@ -34048,10 +34476,10 @@ function CompCard({
34048
34476
  }) {
34049
34477
  const [hovered, setHovered] = useState61(false);
34050
34478
  const [stageSize, setStageSize] = useState61(DEFAULT_PREVIEW_STAGE);
34051
- const iframeRef = useRef67(null);
34052
- const hoverTimer = useRef67(null);
34053
- const syncTimer = useRef67(null);
34054
- const requestIframePlaybackSync = useCallback80((shouldPlay) => {
34479
+ const iframeRef = useRef68(null);
34480
+ const hoverTimer = useRef68(null);
34481
+ const syncTimer = useRef68(null);
34482
+ const requestIframePlaybackSync = useCallback81((shouldPlay) => {
34055
34483
  if (syncTimer.current) {
34056
34484
  clearTimeout(syncTimer.current);
34057
34485
  syncTimer.current = null;
@@ -34209,7 +34637,7 @@ var CompositionsTab = memo27(function CompositionsTab2({
34209
34637
  });
34210
34638
 
34211
34639
  // src/components/sidebar/AssetsTab.tsx
34212
- import { memo as memo28, useState as useState64, useCallback as useCallback82, useRef as useRef69, useMemo as useMemo29, useEffect as useEffect50 } from "react";
34640
+ import { memo as memo28, useState as useState64, useCallback as useCallback83, useRef as useRef70, useMemo as useMemo29, useEffect as useEffect50 } from "react";
34213
34641
 
34214
34642
  // src/components/ui/VideoFrameThumbnail.tsx
34215
34643
  import { useState as useState62, useEffect as useEffect48 } from "react";
@@ -34351,7 +34779,7 @@ var CATEGORY_LABELS = {
34351
34779
  var FILTER_ORDER = ["audio", "images", "video", "fonts"];
34352
34780
 
34353
34781
  // src/components/sidebar/AudioRow.tsx
34354
- import { useState as useState63, useRef as useRef68, useEffect as useEffect49, useCallback as useCallback81 } from "react";
34782
+ import { useState as useState63, useRef as useRef69, useEffect as useEffect49, useCallback as useCallback82 } from "react";
34355
34783
  import { Fragment as Fragment20, jsx as jsx72, jsxs as jsxs60 } from "react/jsx-runtime";
34356
34784
  function AudioRow({
34357
34785
  projectId,
@@ -34366,11 +34794,11 @@ function AudioRow({
34366
34794
  const [contextMenu, setContextMenu] = useState63(null);
34367
34795
  const [playing, setPlaying] = useState63(false);
34368
34796
  const [bars, setBars] = useState63([]);
34369
- const audioRef = useRef68(null);
34370
- const actxRef = useRef68(null);
34371
- const analyserRef = useRef68(null);
34372
- const sourceRef = useRef68(null);
34373
- const animRef = useRef68(0);
34797
+ const audioRef = useRef69(null);
34798
+ const actxRef = useRef69(null);
34799
+ const analyserRef = useRef69(null);
34800
+ const sourceRef = useRef69(null);
34801
+ const animRef = useRef69(0);
34374
34802
  const name = basename(asset);
34375
34803
  const subtype = getAudioSubtype(asset);
34376
34804
  const serveUrl = `/api/projects/${projectId}/preview/${asset}`;
@@ -34409,7 +34837,7 @@ function AudioRow({
34409
34837
  }
34410
34838
  return () => cancelAnimationFrame(animRef.current);
34411
34839
  }, [playing]);
34412
- const togglePlay = useCallback81(async () => {
34840
+ const togglePlay = useCallback82(async () => {
34413
34841
  if (playing) {
34414
34842
  audioRef.current?.pause();
34415
34843
  setPlaying(false);
@@ -34646,13 +35074,13 @@ var AssetsTab = memo28(function AssetsTab2({
34646
35074
  onDelete,
34647
35075
  onRename
34648
35076
  }) {
34649
- const fileInputRef = useRef69(null);
35077
+ const fileInputRef = useRef70(null);
34650
35078
  const [dragOver, setDragOver] = useState64(false);
34651
35079
  const [copiedPath, setCopiedPath] = useState64(null);
34652
35080
  const [activeFilter, setActiveFilter] = useState64("all");
34653
35081
  const [searchQuery, setSearchQuery] = useState64("");
34654
35082
  const [manifest, setManifest] = useState64(/* @__PURE__ */ new Map());
34655
- const manifest404Ref = useRef69(/* @__PURE__ */ new Set());
35083
+ const manifest404Ref = useRef70(/* @__PURE__ */ new Set());
34656
35084
  const assetsKey = assets.join("|");
34657
35085
  useEffect50(() => {
34658
35086
  if (manifest404Ref.current.has(projectId)) return;
@@ -34681,7 +35109,7 @@ var AssetsTab = memo28(function AssetsTab2({
34681
35109
  cancelled = true;
34682
35110
  };
34683
35111
  }, [projectId, assetsKey]);
34684
- const handleDrop = useCallback82(
35112
+ const handleDrop = useCallback83(
34685
35113
  (e) => {
34686
35114
  e.preventDefault();
34687
35115
  setDragOver(false);
@@ -34689,7 +35117,7 @@ var AssetsTab = memo28(function AssetsTab2({
34689
35117
  },
34690
35118
  [onImport]
34691
35119
  );
34692
- const handleCopyPath = useCallback82(async (path) => {
35120
+ const handleCopyPath = useCallback83(async (path) => {
34693
35121
  const copied = await copyTextToClipboard(path);
34694
35122
  if (copied) {
34695
35123
  setCopiedPath(path);
@@ -34938,7 +35366,7 @@ var AssetsTab = memo28(function AssetsTab2({
34938
35366
  });
34939
35367
 
34940
35368
  // src/components/sidebar/BlocksTab.tsx
34941
- import { memo as memo29, useState as useState66, useCallback as useCallback83, useRef as useRef70, useEffect as useEffect52 } from "react";
35369
+ import { memo as memo29, useState as useState66, useCallback as useCallback84, useRef as useRef71, useEffect as useEffect52 } from "react";
34942
35370
  import { createPortal as createPortal5 } from "react-dom";
34943
35371
 
34944
35372
  // src/hooks/useBlockCatalog.ts
@@ -35232,16 +35660,16 @@ function BlockCard({
35232
35660
  }) {
35233
35661
  const [hovered, setHovered] = useState66(false);
35234
35662
  const [adding, setAdding] = useState66(false);
35235
- const hoverTimer = useRef70(null);
35663
+ const hoverTimer = useRef71(null);
35236
35664
  const colors = getCategoryColors(category);
35237
35665
  const needsWebGL = tags?.includes("html-in-canvas") || tags?.includes("webgl");
35238
- const handleEnter = useCallback83(() => {
35666
+ const handleEnter = useCallback84(() => {
35239
35667
  hoverTimer.current = setTimeout(() => {
35240
35668
  setHovered(true);
35241
35669
  onPreview?.({ videoUrl, posterUrl, title });
35242
35670
  }, 300);
35243
35671
  }, [onPreview, videoUrl, posterUrl, title]);
35244
- const handleLeave = useCallback83(() => {
35672
+ const handleLeave = useCallback84(() => {
35245
35673
  if (hoverTimer.current) {
35246
35674
  clearTimeout(hoverTimer.current);
35247
35675
  hoverTimer.current = null;
@@ -35254,7 +35682,7 @@ function BlockCard({
35254
35682
  if (hoverTimer.current) clearTimeout(hoverTimer.current);
35255
35683
  };
35256
35684
  }, []);
35257
- const handleAdd = useCallback83(
35685
+ const handleAdd = useCallback84(
35258
35686
  (e) => {
35259
35687
  e.stopPropagation();
35260
35688
  if (adding || !onAdd) return;
@@ -35265,7 +35693,7 @@ function BlockCard({
35265
35693
  [onAdd, adding]
35266
35694
  );
35267
35695
  const { activeCompPath, compositionDimensions } = useStudioShellContext();
35268
- const handleShowPrompt = useCallback83(
35696
+ const handleShowPrompt = useCallback84(
35269
35697
  (e) => {
35270
35698
  e.stopPropagation();
35271
35699
  const state = usePlayerStore.getState();
@@ -35403,11 +35831,11 @@ function PromptPreviewModal({
35403
35831
  }) {
35404
35832
  const [value, setValue] = useState66(prompt);
35405
35833
  const [copied, setCopied] = useState66(false);
35406
- const textareaRef = useRef70(null);
35834
+ const textareaRef = useRef71(null);
35407
35835
  useEffect52(() => {
35408
35836
  requestAnimationFrame(() => textareaRef.current?.focus());
35409
35837
  }, []);
35410
- const handleCopy = useCallback83(() => {
35838
+ const handleCopy = useCallback84(() => {
35411
35839
  navigator.clipboard.writeText(value);
35412
35840
  setCopied(true);
35413
35841
  setTimeout(() => setCopied(false), 1500);
@@ -35530,14 +35958,14 @@ var LeftSidebar = memo30(
35530
35958
  takeoverContent
35531
35959
  }, ref) {
35532
35960
  const [tab, setTab] = useState67(getPersistedTab);
35533
- const tabRef = useRef71(tab);
35961
+ const tabRef = useRef72(tab);
35534
35962
  tabRef.current = tab;
35535
- const selectTab = useCallback84((t) => {
35963
+ const selectTab = useCallback85((t) => {
35536
35964
  setTab(t);
35537
35965
  localStorage.setItem(STORAGE_KEY, t);
35538
35966
  trackStudioEvent("tab_switch", { panel: "left_sidebar", tab: t });
35539
35967
  }, []);
35540
- const getTab = useCallback84(() => tabRef.current, []);
35968
+ const getTab = useCallback85(() => tabRef.current, []);
35541
35969
  useImperativeHandle(ref, () => ({ selectTab, getTab }), [selectTab, getTab]);
35542
35970
  return /* @__PURE__ */ jsx75(
35543
35971
  "div",
@@ -35843,7 +36271,7 @@ function StudioLeftSidebar({
35843
36271
  handleImportFiles,
35844
36272
  handleContentChange
35845
36273
  } = useFileManagerContext();
35846
- const handleRenderComposition = useCallback85(
36274
+ const handleRenderComposition = useCallback86(
35847
36275
  async (comp) => {
35848
36276
  await waitForPendingDomEditSaves();
35849
36277
  const { format, quality, fps } = getPersistedRenderSettings();
@@ -35937,10 +36365,10 @@ function StudioLeftSidebar({
35937
36365
  }
35938
36366
 
35939
36367
  // src/components/StudioPreviewArea.tsx
35940
- import { useState as useState76, useMemo as useMemo33, useCallback as useCallback92 } from "react";
36368
+ import { useState as useState76, useMemo as useMemo33, useCallback as useCallback93 } from "react";
35941
36369
 
35942
36370
  // src/captions/components/CaptionOverlay.tsx
35943
- import { memo as memo31, useState as useState68, useCallback as useCallback86, useRef as useRef72 } from "react";
36371
+ import { memo as memo31, useState as useState68, useCallback as useCallback87, useRef as useRef73 } from "react";
35944
36372
 
35945
36373
  // src/captions/keyboard.ts
35946
36374
  var CAPTION_NUDGE_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"]);
@@ -36129,10 +36557,10 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36129
36557
  const selectSegment = useCaptionStore((s) => s.selectSegment);
36130
36558
  const clearSelection = useCaptionStore((s) => s.clearSelection);
36131
36559
  const [wordBoxes, setWordBoxes] = useState68([]);
36132
- const overlayRef = useRef72(null);
36133
- const modelRef = useRef72(model);
36560
+ const overlayRef = useRef73(null);
36561
+ const modelRef = useRef73(model);
36134
36562
  modelRef.current = model;
36135
- const interactionRef = useRef72(null);
36563
+ const interactionRef = useRef73(null);
36136
36564
  useMountEffect(() => {
36137
36565
  if (!isEditMode) return;
36138
36566
  let prevBoxes = [];
@@ -36185,14 +36613,14 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36185
36613
  window.removeEventListener("keydown", handleKeyDown);
36186
36614
  };
36187
36615
  });
36188
- const getCssScale = useCallback86(() => {
36616
+ const getCssScale = useCallback87(() => {
36189
36617
  const iframe = iframeRef.current;
36190
36618
  if (!iframe) return 1;
36191
36619
  const rect = iframe.getBoundingClientRect();
36192
36620
  const nativeW = parseFloat(iframe.style.width) || rect.width;
36193
36621
  return rect.width / nativeW;
36194
36622
  }, [iframeRef]);
36195
- const startMove = useCallback86(
36623
+ const startMove = useCallback87(
36196
36624
  (groupIndex, wordIndex, segmentId, e) => {
36197
36625
  e.stopPropagation();
36198
36626
  e.currentTarget.setPointerCapture(e.pointerId);
@@ -36216,7 +36644,7 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36216
36644
  },
36217
36645
  [iframeRef]
36218
36646
  );
36219
- const startScale = useCallback86(
36647
+ const startScale = useCallback87(
36220
36648
  (groupIndex, wordIndex, segmentId, e) => {
36221
36649
  e.stopPropagation();
36222
36650
  e.preventDefault();
@@ -36244,7 +36672,7 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36244
36672
  },
36245
36673
  [iframeRef, getCssScale]
36246
36674
  );
36247
- const startRotate = useCallback86(
36675
+ const startRotate = useCallback87(
36248
36676
  (box, e) => {
36249
36677
  e.stopPropagation();
36250
36678
  e.preventDefault();
@@ -36268,14 +36696,14 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36268
36696
  },
36269
36697
  [iframeRef]
36270
36698
  );
36271
- const getIframeWin = useCallback86(() => {
36699
+ const getIframeWin = useCallback87(() => {
36272
36700
  try {
36273
36701
  return iframeRef.current?.contentWindow ?? null;
36274
36702
  } catch {
36275
36703
  return null;
36276
36704
  }
36277
36705
  }, [iframeRef]);
36278
- const handlePointerMove = useCallback86(
36706
+ const handlePointerMove = useCallback87(
36279
36707
  (e) => {
36280
36708
  const i = interactionRef.current;
36281
36709
  if (!i) return;
@@ -36301,7 +36729,7 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36301
36729
  },
36302
36730
  [getCssScale, getIframeWin]
36303
36731
  );
36304
- const handlePointerUp = useCallback86(() => {
36732
+ const handlePointerUp = useCallback87(() => {
36305
36733
  const i = interactionRef.current;
36306
36734
  if (i) {
36307
36735
  const win = getIframeWin();
@@ -36309,7 +36737,7 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36309
36737
  interactionRef.current = null;
36310
36738
  }
36311
36739
  }, [getIframeWin]);
36312
- const handleBackgroundClick = useCallback86(
36740
+ const handleBackgroundClick = useCallback87(
36313
36741
  (e) => {
36314
36742
  if (e.target === e.currentTarget) clearSelection();
36315
36743
  },
@@ -36419,7 +36847,7 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36419
36847
  });
36420
36848
 
36421
36849
  // src/captions/components/CaptionTimeline.tsx
36422
- import { memo as memo32, useCallback as useCallback87, useRef as useRef73 } from "react";
36850
+ import { memo as memo32, useCallback as useCallback88, useRef as useRef74 } from "react";
36423
36851
  import { jsx as jsx79, jsxs as jsxs67 } from "react/jsx-runtime";
36424
36852
  var GROUP_COLORS = [
36425
36853
  "#3CE6AC",
@@ -36442,8 +36870,8 @@ var CaptionTimeline = memo32(function CaptionTimeline2({
36442
36870
  const selectSegment = useCaptionStore((s) => s.selectSegment);
36443
36871
  const updateSegmentTiming = useCaptionStore((s) => s.updateSegmentTiming);
36444
36872
  const splitGroup = useCaptionStore((s) => s.splitGroup);
36445
- const dragRef = useRef73(null);
36446
- const handleEdgePointerDown = useCallback87(
36873
+ const dragRef = useRef74(null);
36874
+ const handleEdgePointerDown = useCallback88(
36447
36875
  (e, segId, edge, originalStart, originalEnd) => {
36448
36876
  e.stopPropagation();
36449
36877
  e.preventDefault();
@@ -36452,7 +36880,7 @@ var CaptionTimeline = memo32(function CaptionTimeline2({
36452
36880
  },
36453
36881
  []
36454
36882
  );
36455
- const handlePointerMove = useCallback87(
36883
+ const handlePointerMove = useCallback88(
36456
36884
  (e) => {
36457
36885
  const drag = dragRef.current;
36458
36886
  if (!drag) return;
@@ -36469,24 +36897,24 @@ var CaptionTimeline = memo32(function CaptionTimeline2({
36469
36897
  },
36470
36898
  [pixelsPerSecond, updateSegmentTiming]
36471
36899
  );
36472
- const handlePointerUp = useCallback87(() => {
36900
+ const handlePointerUp = useCallback88(() => {
36473
36901
  dragRef.current = null;
36474
36902
  }, []);
36475
- const handleBlockClick = useCallback87(
36903
+ const handleBlockClick = useCallback88(
36476
36904
  (e, segId) => {
36477
36905
  e.stopPropagation();
36478
36906
  selectSegment(segId, e.shiftKey);
36479
36907
  },
36480
36908
  [selectSegment]
36481
36909
  );
36482
- const handleBlockDoubleClick = useCallback87(
36910
+ const handleBlockDoubleClick = useCallback88(
36483
36911
  (e, groupId, segId) => {
36484
36912
  e.stopPropagation();
36485
36913
  splitGroup(groupId, segId);
36486
36914
  },
36487
36915
  [splitGroup]
36488
36916
  );
36489
- const handleTrackClick = useCallback87(
36917
+ const handleTrackClick = useCallback88(
36490
36918
  (e) => {
36491
36919
  if (!onSeek) return;
36492
36920
  const rect = e.currentTarget.getBoundingClientRect();
@@ -36564,10 +36992,10 @@ var CaptionTimeline = memo32(function CaptionTimeline2({
36564
36992
  });
36565
36993
 
36566
36994
  // src/components/editor/DomEditOverlay.tsx
36567
- import { memo as memo35, useEffect as useEffect53, useMemo as useMemo31, useRef as useRef77, useState as useState71 } from "react";
36995
+ import { memo as memo35, useEffect as useEffect53, useMemo as useMemo31, useRef as useRef78, useState as useState71 } from "react";
36568
36996
 
36569
36997
  // src/components/editor/marqueeCommit.ts
36570
- import { useCallback as useCallback88, useRef as useRef74, useState as useState69 } from "react";
36998
+ import { useCallback as useCallback89, useRef as useRef75, useState as useState69 } from "react";
36571
36999
 
36572
37000
  // src/utils/marqueeGeometry.ts
36573
37001
  function rectsOverlap(a, b) {
@@ -36758,10 +37186,10 @@ async function runMarqueeIntersection(rect, iframe, overlayEl, activeComposition
36758
37186
  return hits;
36759
37187
  }
36760
37188
  function useMarqueeGestures(deps) {
36761
- const marqueeRef = useRef74(null);
37189
+ const marqueeRef = useRef75(null);
36762
37190
  const [marqueeRect, setMarqueeRect] = useState69(null);
36763
37191
  const [candidateRects, setCandidateRects] = useState69([]);
36764
- const commitMarquee = useCallback88(
37192
+ const commitMarquee = useCallback89(
36765
37193
  async (rect, additive) => {
36766
37194
  const iframe = deps.iframeRef.current;
36767
37195
  const overlay = deps.overlayRef.current;
@@ -36772,7 +37200,7 @@ function useMarqueeGestures(deps) {
36772
37200
  },
36773
37201
  [deps.iframeRef, deps.overlayRef, deps.onMarqueeSelectRef, deps.activeCompositionPathRef]
36774
37202
  );
36775
- const onPointerMove = useCallback88(
37203
+ const onPointerMove = useCallback89(
36776
37204
  (event) => {
36777
37205
  const m = marqueeRef.current;
36778
37206
  if (m) {
@@ -36805,7 +37233,7 @@ function useMarqueeGestures(deps) {
36805
37233
  },
36806
37234
  [deps.gestures, deps.overlayRef, deps.iframeRef, deps.activeCompositionPathRef]
36807
37235
  );
36808
- const onPointerUp = useCallback88(
37236
+ const onPointerUp = useCallback89(
36809
37237
  (event) => {
36810
37238
  const m = marqueeRef.current;
36811
37239
  if (m) {
@@ -36835,7 +37263,7 @@ function useMarqueeGestures(deps) {
36835
37263
  },
36836
37264
  [deps.gestures, commitMarquee, deps.onMarqueeSelectRef]
36837
37265
  );
36838
- const onPointerCancel = useCallback88(() => {
37266
+ const onPointerCancel = useCallback89(() => {
36839
37267
  if (marqueeRef.current) {
36840
37268
  marqueeRef.current = null;
36841
37269
  setMarqueeRect(null);
@@ -36934,7 +37362,7 @@ function hasDomEditRotationChanged(initialAngle, nextAngle) {
36934
37362
  }
36935
37363
 
36936
37364
  // src/components/editor/useDomEditOverlayRects.ts
36937
- import { useRef as useRef75, useState as useState70 } from "react";
37365
+ import { useRef as useRef76, useState as useState70 } from "react";
36938
37366
  function childRectsEqual(a, b) {
36939
37367
  if (a.length !== b.length) return false;
36940
37368
  for (let i = 0; i < a.length; i++) {
@@ -36955,13 +37383,13 @@ function useDomEditOverlayRects({
36955
37383
  const [hoverRect, setHoverRectState] = useState70(null);
36956
37384
  const [groupOverlayItems, setGroupOverlayItemsState] = useState70([]);
36957
37385
  const [childRects, setChildRectsState] = useState70([]);
36958
- const overlayRectRef = useRef75(null);
36959
- const hoverRectRef = useRef75(null);
36960
- const groupOverlayItemsRef = useRef75([]);
36961
- const resolvedElementRef = useRef75(null);
36962
- const resolvedHoverElementRef = useRef75(null);
36963
- const resolvedGroupElementRef = useRef75(/* @__PURE__ */ new Map());
36964
- const childRectsRef = useRef75([]);
37386
+ const overlayRectRef = useRef76(null);
37387
+ const hoverRectRef = useRef76(null);
37388
+ const groupOverlayItemsRef = useRef76([]);
37389
+ const resolvedElementRef = useRef76(null);
37390
+ const resolvedHoverElementRef = useRef76(null);
37391
+ const resolvedGroupElementRef = useRef76(/* @__PURE__ */ new Map());
37392
+ const childRectsRef = useRef76([]);
36965
37393
  const setOverlayRect = (next) => {
36966
37394
  if (rectsEqual(overlayRectRef.current, next)) return;
36967
37395
  overlayRectRef.current = next;
@@ -37154,7 +37582,7 @@ function OffCanvasIndicators({
37154
37582
  const selectOffCanvas = async () => {
37155
37583
  const el = elements.current.get(r.key);
37156
37584
  if (!el) return;
37157
- const { resolveDomEditSelection: resolveDomEditSelection2 } = await import("./domEditingLayers-EK7R7R4G.js");
37585
+ const { resolveDomEditSelection: resolveDomEditSelection2 } = await import("./domEditingLayers-UIQZJCOA.js");
37158
37586
  const acp = activeCompositionPathRef.current ?? "index.html";
37159
37587
  const sel = await resolveDomEditSelection2(el, {
37160
37588
  activeCompositionPath: acp,
@@ -37820,7 +38248,7 @@ function readPositiveDimension2(value) {
37820
38248
  return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
37821
38249
  }
37822
38250
  var IGNORED_TAGS = /* @__PURE__ */ new Set(["script", "style", "link", "meta", "base", "template", "br", "wbr"]);
37823
- function isHtmlElement2(node) {
38251
+ function isHtmlElement3(node) {
37824
38252
  return node.nodeType === 1;
37825
38253
  }
37826
38254
  function collectVisibleElements(root, excludeElements, maxItems) {
@@ -37828,7 +38256,7 @@ function collectVisibleElements(root, excludeElements, maxItems) {
37828
38256
  const visit = (el) => {
37829
38257
  if (result.length >= maxItems) return;
37830
38258
  for (const child of Array.from(el.children)) {
37831
- if (!isHtmlElement2(child)) continue;
38259
+ if (!isHtmlElement3(child)) continue;
37832
38260
  if (IGNORED_TAGS.has(child.tagName.toLowerCase())) continue;
37833
38261
  if (child.hasAttribute("data-composition-id")) continue;
37834
38262
  if (excludeElements.has(child)) continue;
@@ -38305,7 +38733,8 @@ function createDomEditOverlayGestureHandlers(opts) {
38305
38733
  restoreGestureOverlayRect(g);
38306
38734
  opts.suppressNextBoxClickRef.current = true;
38307
38735
  opts.onCanvasMouseDown(e, {
38308
- preferClipAncestor: false
38736
+ preferClipAncestor: false,
38737
+ hoverSelection: opts.hoverSelectionRef.current
38309
38738
  });
38310
38739
  return;
38311
38740
  }
@@ -38417,7 +38846,7 @@ function createDomEditOverlayGestureHandlers(opts) {
38417
38846
  }
38418
38847
 
38419
38848
  // src/components/editor/SnapGuideOverlay.tsx
38420
- import { memo as memo33, useRef as useRef76 } from "react";
38849
+ import { memo as memo33, useRef as useRef77 } from "react";
38421
38850
  import { jsx as jsx82, jsxs as jsxs69 } from "react/jsx-runtime";
38422
38851
  var MAX_GUIDES = 6;
38423
38852
  var MAX_SPACING_GUIDES = 4;
@@ -38429,12 +38858,12 @@ var SnapGuideOverlay = memo33(function SnapGuideOverlay2({
38429
38858
  overlayWidth,
38430
38859
  overlayHeight
38431
38860
  }) {
38432
- const guideElsRef = useRef76([]);
38433
- const spacingElsRef = useRef76([]);
38434
- const spacingLabelElsRef = useRef76([]);
38435
- const overlayWidthRef = useRef76(overlayWidth);
38861
+ const guideElsRef = useRef77([]);
38862
+ const spacingElsRef = useRef77([]);
38863
+ const spacingLabelElsRef = useRef77([]);
38864
+ const overlayWidthRef = useRef77(overlayWidth);
38436
38865
  overlayWidthRef.current = overlayWidth;
38437
- const overlayHeightRef = useRef76(overlayHeight);
38866
+ const overlayHeightRef = useRef77(overlayHeight);
38438
38867
  overlayHeightRef.current = overlayHeight;
38439
38868
  useMountEffect(() => {
38440
38869
  let frame = 0;
@@ -38616,9 +39045,9 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38616
39045
  onRotationCommit,
38617
39046
  onMarqueeSelect
38618
39047
  }) {
38619
- const overlayRef = useRef77(null);
38620
- const boxRef = useRef77(null);
38621
- const onMarqueeSelectRef = useRef77(onMarqueeSelect);
39048
+ const overlayRef = useRef78(null);
39049
+ const boxRef = useRef78(null);
39050
+ const onMarqueeSelectRef = useRef78(onMarqueeSelect);
38622
39051
  onMarqueeSelectRef.current = onMarqueeSelect;
38623
39052
  const selectionShapeStyles = (() => {
38624
39053
  const fallback = {
@@ -38642,39 +39071,39 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38642
39071
  return fallback;
38643
39072
  }
38644
39073
  })();
38645
- const gestureRef = useRef77(null);
38646
- const groupGestureRef = useRef77(null);
38647
- const blockedMoveRef = useRef77(null);
38648
- const suppressNextBoxClickRef = useRef77(false);
38649
- const suppressNextBoxMouseDownRef = useRef77(false);
38650
- const suppressNextOverlayMouseDownRef = useRef77(false);
38651
- const snapGuidesRef = useRef77(null);
38652
- const rafPausedRef = useRef77(false);
38653
- const selectionRef = useRef77(selection);
39074
+ const gestureRef = useRef78(null);
39075
+ const groupGestureRef = useRef78(null);
39076
+ const blockedMoveRef = useRef78(null);
39077
+ const suppressNextBoxClickRef = useRef78(false);
39078
+ const suppressNextBoxMouseDownRef = useRef78(false);
39079
+ const suppressNextOverlayMouseDownRef = useRef78(false);
39080
+ const snapGuidesRef = useRef78(null);
39081
+ const rafPausedRef = useRef78(false);
39082
+ const selectionRef = useRef78(selection);
38654
39083
  selectionRef.current = selection;
38655
- const activeCompositionPathRef = useRef77(activeCompositionPath);
39084
+ const activeCompositionPathRef = useRef78(activeCompositionPath);
38656
39085
  activeCompositionPathRef.current = activeCompositionPath;
38657
- const groupSelectionsRef = useRef77(groupSelections);
39086
+ const groupSelectionsRef = useRef78(groupSelections);
38658
39087
  groupSelectionsRef.current = groupSelections;
38659
- const hoverSelectionRef = useRef77(hoverSelection);
39088
+ const hoverSelectionRef = useRef78(hoverSelection);
38660
39089
  hoverSelectionRef.current = hoverSelection;
38661
- const onPathOffsetCommitRef = useRef77(onPathOffsetCommit);
39090
+ const onPathOffsetCommitRef = useRef78(onPathOffsetCommit);
38662
39091
  onPathOffsetCommitRef.current = onPathOffsetCommit;
38663
- const onGroupPathOffsetCommitRef = useRef77(onGroupPathOffsetCommit);
39092
+ const onGroupPathOffsetCommitRef = useRef78(onGroupPathOffsetCommit);
38664
39093
  onGroupPathOffsetCommitRef.current = onGroupPathOffsetCommit;
38665
- const onBoxSizeCommitRef = useRef77(onBoxSizeCommit);
39094
+ const onBoxSizeCommitRef = useRef78(onBoxSizeCommit);
38666
39095
  onBoxSizeCommitRef.current = onBoxSizeCommit;
38667
- const onRotationCommitRef = useRef77(onRotationCommit);
39096
+ const onRotationCommitRef = useRef78(onRotationCommit);
38668
39097
  onRotationCommitRef.current = onRotationCommit;
38669
- const onBlockedMoveRef = useRef77(onBlockedMove);
39098
+ const onBlockedMoveRef = useRef78(onBlockedMove);
38670
39099
  onBlockedMoveRef.current = onBlockedMove;
38671
- const onManualDragStartRef = useRef77(onManualDragStart);
39100
+ const onManualDragStartRef = useRef78(onManualDragStart);
38672
39101
  onManualDragStartRef.current = onManualDragStart;
38673
- const onCanvasPointerMoveRef = useRef77(onCanvasPointerMove);
39102
+ const onCanvasPointerMoveRef = useRef78(onCanvasPointerMove);
38674
39103
  onCanvasPointerMoveRef.current = onCanvasPointerMove;
38675
- const onCanvasPointerLeaveRef = useRef77(onCanvasPointerLeave);
39104
+ const onCanvasPointerLeaveRef = useRef78(onCanvasPointerLeave);
38676
39105
  onCanvasPointerLeaveRef.current = onCanvasPointerLeave;
38677
- const onSelectionChangeRef = useRef77(onSelectionChange);
39106
+ const onSelectionChangeRef = useRef78(onSelectionChange);
38678
39107
  onSelectionChangeRef.current = onSelectionChange;
38679
39108
  const {
38680
39109
  overlayRect,
@@ -38729,7 +39158,7 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38729
39158
  frame = requestAnimationFrame(update);
38730
39159
  return () => cancelAnimationFrame(frame);
38731
39160
  });
38732
- const offCanvasElementsRef = useRef77(/* @__PURE__ */ new Map());
39161
+ const offCanvasElementsRef = useRef78(/* @__PURE__ */ new Map());
38733
39162
  const [offCanvasRects, setOffCanvasRects] = useState71([]);
38734
39163
  useEffect53(() => {
38735
39164
  const iframe = iframeRef.current;
@@ -38766,6 +39195,7 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38766
39195
  iframeRef,
38767
39196
  boxRef,
38768
39197
  selectionRef,
39198
+ hoverSelectionRef,
38769
39199
  overlayRectRef,
38770
39200
  groupOverlayItemsRef,
38771
39201
  gestureRef,
@@ -38815,7 +39245,7 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38815
39245
  }
38816
39246
  const target = event.target;
38817
39247
  if (target?.closest('[data-dom-edit-selection-box="true"]')) return;
38818
- onCanvasMouseDown(event, { preferClipAncestor: false });
39248
+ onCanvasMouseDown(event, { hoverSelection: hoverSelectionRef.current });
38819
39249
  if (event.shiftKey) {
38820
39250
  suppressNextBoxMouseDownRef.current = true;
38821
39251
  suppressNextBoxClickRef.current = true;
@@ -38869,7 +39299,7 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38869
39299
  event.stopPropagation();
38870
39300
  return;
38871
39301
  }
38872
- onCanvasMouseDown(event, { preferClipAncestor: false });
39302
+ onCanvasMouseDown(event, { hoverSelection: hoverSelectionRef.current });
38873
39303
  };
38874
39304
  const suppressBoxMouseDown = (e) => {
38875
39305
  if (!suppressNextBoxMouseDownRef.current) return;
@@ -39074,10 +39504,10 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
39074
39504
  });
39075
39505
 
39076
39506
  // src/components/editor/MotionPathOverlay.tsx
39077
- import { memo as memo36, useEffect as useEffect55, useRef as useRef80, useState as useState73 } from "react";
39507
+ import { memo as memo36, useEffect as useEffect55, useRef as useRef81, useState as useState73 } from "react";
39078
39508
 
39079
39509
  // src/contexts/DomEditContext.tsx
39080
- import { createContext as createContext6, useCallback as useCallback89, useContext as useContext6, useMemo as useMemo32, useRef as useRef78 } from "react";
39510
+ import { createContext as createContext6, useCallback as useCallback90, useContext as useContext6, useMemo as useMemo32, useRef as useRef79 } from "react";
39081
39511
  import { jsx as jsx85 } from "react/jsx-runtime";
39082
39512
  var DomEditActionsContext = createContext6(null);
39083
39513
  var DomEditSelectionContext = createContext6(null);
@@ -39176,9 +39606,9 @@ function DomEditProvider({
39176
39606
  },
39177
39607
  children
39178
39608
  }) {
39179
- const commitMutationRef = useRef78(commitMutation);
39609
+ const commitMutationRef = useRef79(commitMutation);
39180
39610
  commitMutationRef.current = commitMutation;
39181
- const stableCommitMutation = useCallback89(
39611
+ const stableCommitMutation = useCallback90(
39182
39612
  (mutation, options) => commitMutationRef.current(mutation, options),
39183
39613
  []
39184
39614
  );
@@ -39533,7 +39963,7 @@ function commitCreatePath(targetSelector, position, x, y, commit) {
39533
39963
  }
39534
39964
 
39535
39965
  // src/components/editor/useMotionPathData.ts
39536
- import { useEffect as useEffect54, useRef as useRef79, useState as useState72 } from "react";
39966
+ import { useEffect as useEffect54, useRef as useRef80, useState as useState72 } from "react";
39537
39967
  function transformTranslate(el) {
39538
39968
  const t = el.ownerDocument?.defaultView?.getComputedStyle(el).transform;
39539
39969
  if (!t || t === "none") return { x: 0, y: 0 };
@@ -39599,7 +40029,7 @@ function hasMotionPathPlugin(iframe) {
39599
40029
  function useMotionPathData(iframeRef, selector) {
39600
40030
  const [rect, setRect] = useState72(null);
39601
40031
  const [geometry, setGeometry] = useState72(null);
39602
- const resolvedForRef = useRef79(null);
40032
+ const resolvedForRef = useRef80(null);
39603
40033
  const geometryResolved = resolvedForRef.current === selector;
39604
40034
  const [visibleInPreview, setVisibleInPreview] = useState72(true);
39605
40035
  const [home, setHome] = useState72(null);
@@ -39696,8 +40126,8 @@ var MotionPathOverlay = memo36(function MotionPathOverlay2({
39696
40126
  const armed = usePlayerStore((s) => s.motionPathArmed);
39697
40127
  const setMotionPathArmed = usePlayerStore((s) => s.setMotionPathArmed);
39698
40128
  const setMotionPathCreateAvailable = usePlayerStore((s) => s.setMotionPathCreateAvailable);
39699
- const dragRef = useRef80(null);
39700
- const parkTimerRef = useRef80(void 0);
40129
+ const dragRef = useRef81(null);
40130
+ const parkTimerRef = useRef81(void 0);
39701
40131
  const animId = editableAnimationId(selectedGsapAnimations ?? [], geometry?.kind ?? "linear");
39702
40132
  useEffect55(() => () => clearTimeout(parkTimerRef.current), [animId]);
39703
40133
  const createMode = geometryResolved && !geometry && Boolean(selection?.element) && !isPlaying;
@@ -40025,7 +40455,7 @@ var MotionPathOverlay = memo36(function MotionPathOverlay2({
40025
40455
  });
40026
40456
 
40027
40457
  // src/components/editor/SnapToolbar.tsx
40028
- import { memo as memo37, useCallback as useCallback90, useEffect as useEffect56, useRef as useRef81, useState as useState74 } from "react";
40458
+ import { memo as memo37, useCallback as useCallback91, useEffect as useEffect56, useRef as useRef82, useState as useState74 } from "react";
40029
40459
  import { MagnetStraight, GridFour, Path } from "@phosphor-icons/react";
40030
40460
  import { jsx as jsx88, jsxs as jsxs73 } from "react/jsx-runtime";
40031
40461
  var SNAP_DEFAULTS = {
@@ -40049,9 +40479,9 @@ var SnapToolbar = memo37(function SnapToolbar2({ onSnapChange }) {
40049
40479
  const motionPathCreateAvailable = usePlayerStore((s) => s.motionPathCreateAvailable);
40050
40480
  const motionPathArmed = usePlayerStore((s) => s.motionPathArmed);
40051
40481
  const setMotionPathArmed = usePlayerStore((s) => s.setMotionPathArmed);
40052
- const popoverRef = useRef81(null);
40053
- const gridButtonRef = useRef81(null);
40054
- const updatePrefs = useCallback90(
40482
+ const popoverRef = useRef82(null);
40483
+ const gridButtonRef = useRef82(null);
40484
+ const updatePrefs = useCallback91(
40055
40485
  (patch) => {
40056
40486
  setPrefs((prev) => {
40057
40487
  const next = { ...prev, ...patch };
@@ -40062,10 +40492,10 @@ var SnapToolbar = memo37(function SnapToolbar2({ onSnapChange }) {
40062
40492
  },
40063
40493
  [onSnapChange]
40064
40494
  );
40065
- const toggleSnap = useCallback90(() => {
40495
+ const toggleSnap = useCallback91(() => {
40066
40496
  updatePrefs({ snapEnabled: !prefs.snapEnabled });
40067
40497
  }, [prefs.snapEnabled, updatePrefs]);
40068
- const toggleGrid = useCallback90(() => {
40498
+ const toggleGrid = useCallback91(() => {
40069
40499
  updatePrefs({ gridVisible: !prefs.gridVisible });
40070
40500
  }, [prefs.gridVisible, updatePrefs]);
40071
40501
  useEffect56(() => {
@@ -40190,7 +40620,7 @@ var SnapToolbar = memo37(function SnapToolbar2({ onSnapChange }) {
40190
40620
  });
40191
40621
 
40192
40622
  // src/components/StudioFeedbackBar.tsx
40193
- import { memo as memo38, useState as useState75, useCallback as useCallback91, useRef as useRef82, useEffect as useEffect57 } from "react";
40623
+ import { memo as memo38, useState as useState75, useCallback as useCallback92, useRef as useRef83, useEffect as useEffect57 } from "react";
40194
40624
  import { Fragment as Fragment29, jsx as jsx89, jsxs as jsxs74 } from "react/jsx-runtime";
40195
40625
  var DEFAULT_FEEDBACK_INTERVAL = 10;
40196
40626
  var AUTO_DISMISS_MS = 2e4;
@@ -40249,8 +40679,8 @@ var StudioFeedbackBar = memo38(function StudioFeedbackBar2() {
40249
40679
  const [comment, setComment] = useState75("");
40250
40680
  const [submitted, setSubmitted] = useState75(false);
40251
40681
  const [exiting, setExiting] = useState75(false);
40252
- const inputRef = useRef82(null);
40253
- const dismissTimerRef = useRef82(null);
40682
+ const inputRef = useRef83(null);
40683
+ const dismissTimerRef = useRef83(null);
40254
40684
  useEffect57(() => {
40255
40685
  incrementSessionCount();
40256
40686
  const showTimer = setTimeout(() => {
@@ -40274,12 +40704,12 @@ var StudioFeedbackBar = memo38(function StudioFeedbackBar2() {
40274
40704
  inputRef.current.focus();
40275
40705
  }
40276
40706
  }, [rating]);
40277
- const handleDismiss = useCallback91(() => {
40707
+ const handleDismiss = useCallback92(() => {
40278
40708
  setExiting(true);
40279
40709
  markPrompted();
40280
40710
  setTimeout(() => setVisible(false), 300);
40281
40711
  }, []);
40282
- const handleSubmit = useCallback91(() => {
40712
+ const handleSubmit = useCallback92(() => {
40283
40713
  if (rating === null) return;
40284
40714
  trackStudioFeedback({
40285
40715
  rating,
@@ -40292,7 +40722,7 @@ var StudioFeedbackBar = memo38(function StudioFeedbackBar2() {
40292
40722
  setTimeout(() => setVisible(false), 300);
40293
40723
  }, 1500);
40294
40724
  }, [rating, comment]);
40295
- const handleRating = useCallback91((n) => {
40725
+ const handleRating = useCallback92((n) => {
40296
40726
  setRating(n);
40297
40727
  if (dismissTimerRef.current) {
40298
40728
  clearTimeout(dismissTimerRef.current);
@@ -40490,7 +40920,7 @@ function StudioPreviewArea({
40490
40920
  snapToGrid: p.snapToGrid ?? false
40491
40921
  };
40492
40922
  });
40493
- const resolveKeyframeTarget = useCallback92(
40923
+ const resolveKeyframeTarget = useCallback93(
40494
40924
  (pct) => {
40495
40925
  const cached2 = usePlayerStore.getState().keyframeCache.get(domEditSelection?.id ?? "");
40496
40926
  const kf = cached2?.keyframes.find((k) => Math.abs(k.percentage - pct) < 0.2);
@@ -40712,17 +41142,17 @@ function StudioPreviewArea({
40712
41142
 
40713
41143
  // src/components/StudioRightPanel.tsx
40714
41144
  import {
40715
- useCallback as useCallback102,
41145
+ useCallback as useCallback103,
40716
41146
  useMemo as useMemo34,
40717
- useRef as useRef87,
41147
+ useRef as useRef88,
40718
41148
  useState as useState85
40719
41149
  } from "react";
40720
41150
 
40721
41151
  // src/components/editor/LayersPanel.tsx
40722
- import { memo as memo39, useState as useState78, useCallback as useCallback94, useEffect as useEffect58, useRef as useRef84 } from "react";
41152
+ import { memo as memo39, useState as useState78, useCallback as useCallback95, useEffect as useEffect58, useRef as useRef85 } from "react";
40723
41153
 
40724
41154
  // src/components/editor/useLayerDrag.ts
40725
- import { useRef as useRef83, useState as useState77, useCallback as useCallback93 } from "react";
41155
+ import { useRef as useRef84, useState as useState77, useCallback as useCallback94 } from "react";
40726
41156
  var DRAG_THRESHOLD_PX2 = 4;
40727
41157
  function isLayerDraggable(layer) {
40728
41158
  if (!(layer.selector || layer.id)) return false;
@@ -40789,10 +41219,10 @@ function useLayerDrag({
40789
41219
  onReorder,
40790
41220
  onSingleSibling
40791
41221
  }) {
40792
- const dragRef = useRef83(null);
41222
+ const dragRef = useRef84(null);
40793
41223
  const [dragKey, setDragKey] = useState77(null);
40794
41224
  const [insertionLineY, setInsertionLineY] = useState77(null);
40795
- const handleRowPointerDown = useCallback93(
41225
+ const handleRowPointerDown = useCallback94(
40796
41226
  (layerIndex, e) => {
40797
41227
  if (e.button !== 0) return;
40798
41228
  const layer = visibleLayers[layerIndex];
@@ -40819,7 +41249,7 @@ function useLayerDrag({
40819
41249
  },
40820
41250
  [visibleLayers, scrollContainerRef, onSingleSibling]
40821
41251
  );
40822
- const handleContainerPointerMove = useCallback93(
41252
+ const handleContainerPointerMove = useCallback94(
40823
41253
  (e) => {
40824
41254
  const drag = dragRef.current;
40825
41255
  if (!drag) return;
@@ -40845,7 +41275,7 @@ function useLayerDrag({
40845
41275
  },
40846
41276
  [visibleLayers, scrollContainerRef]
40847
41277
  );
40848
- const handleContainerPointerUp = useCallback93(() => {
41278
+ const handleContainerPointerUp = useCallback94(() => {
40849
41279
  const drag = dragRef.current;
40850
41280
  dragRef.current = null;
40851
41281
  setDragKey(null);
@@ -40919,10 +41349,10 @@ var LayersPanel = memo39(function LayersPanel2() {
40919
41349
  } = useDomEditContext();
40920
41350
  const [layers, setLayers] = useState78([]);
40921
41351
  const [collapsed, setCollapsed] = useState78({});
40922
- const prevDocVersionRef = useRef84(0);
40923
- const scrollContainerRef = useRef84(null);
41352
+ const prevDocVersionRef = useRef85(0);
41353
+ const scrollContainerRef = useRef85(null);
40924
41354
  const isMasterView = !activeCompPath || activeCompPath === "index.html";
40925
- const collectLayers = useCallback94(() => {
41355
+ const collectLayers = useCallback95(() => {
40926
41356
  const iframe = previewIframeRef.current;
40927
41357
  if (!iframe) return;
40928
41358
  let doc = null;
@@ -40961,7 +41391,7 @@ var LayersPanel = memo39(function LayersPanel2() {
40961
41391
  return () => clearTimeout(timer);
40962
41392
  }
40963
41393
  }, [compositionLoading, collectLayers]);
40964
- const resolveSelection = useCallback94(
41394
+ const resolveSelection = useCallback95(
40965
41395
  (layer) => {
40966
41396
  let el = layer.element;
40967
41397
  if (!el.isConnected) {
@@ -40981,7 +41411,7 @@ var LayersPanel = memo39(function LayersPanel2() {
40981
41411
  },
40982
41412
  [activeCompPath, isMasterView, previewIframeRef, activeGroupElement]
40983
41413
  );
40984
- const seekToLayer = useCallback94(
41414
+ const seekToLayer = useCallback95(
40985
41415
  async (layer) => {
40986
41416
  const selection = await resolveSelection(layer);
40987
41417
  if (!selection) return;
@@ -41010,7 +41440,7 @@ var LayersPanel = memo39(function LayersPanel2() {
41010
41440
  },
41011
41441
  [currentTime, resolveSelection, timelineElements]
41012
41442
  );
41013
- const handleSelectLayer = useCallback94(
41443
+ const handleSelectLayer = useCallback95(
41014
41444
  async (layer) => {
41015
41445
  const selection = await resolveSelection(layer);
41016
41446
  if (!selection) return;
@@ -41019,7 +41449,7 @@ var LayersPanel = memo39(function LayersPanel2() {
41019
41449
  },
41020
41450
  [resolveSelection, applyDomSelection, seekToLayer]
41021
41451
  );
41022
- const handleLayerDoubleClick = useCallback94(
41452
+ const handleLayerDoubleClick = useCallback95(
41023
41453
  async (layer) => {
41024
41454
  const selection = await resolveSelection(layer);
41025
41455
  if (selection?.element.hasAttribute("data-hf-group")) {
@@ -41030,7 +41460,7 @@ var LayersPanel = memo39(function LayersPanel2() {
41030
41460
  },
41031
41461
  [resolveSelection, setActiveGroupElement, handleSelectLayer]
41032
41462
  );
41033
- const handleLayerHover = useCallback94(
41463
+ const handleLayerHover = useCallback95(
41034
41464
  async (layer) => {
41035
41465
  if (!layer) {
41036
41466
  updateDomEditHoverSelection(null);
@@ -41041,11 +41471,11 @@ var LayersPanel = memo39(function LayersPanel2() {
41041
41471
  },
41042
41472
  [resolveSelection, updateDomEditHoverSelection]
41043
41473
  );
41044
- const toggleCollapse = useCallback94((key, e) => {
41474
+ const toggleCollapse = useCallback95((key, e) => {
41045
41475
  e.stopPropagation();
41046
41476
  setCollapsed((prev) => ({ ...prev, [key]: !prev[key] }));
41047
41477
  }, []);
41048
- const handleReorder = useCallback94(
41478
+ const handleReorder = useCallback95(
41049
41479
  (event) => {
41050
41480
  const { siblingLayers, fromIndex, toIndex } = event;
41051
41481
  const reordered = [...siblingLayers];
@@ -41069,7 +41499,7 @@ var LayersPanel = memo39(function LayersPanel2() {
41069
41499
  );
41070
41500
  const selectedKey = domEditSelection ? getDomEditLayerKey(domEditSelection) : null;
41071
41501
  const visibleLayers = getVisibleLayers(layers, collapsed);
41072
- const handleSingleSibling = useCallback94(() => {
41502
+ const handleSingleSibling = useCallback95(() => {
41073
41503
  showToast("Only one layer at this level", "info");
41074
41504
  }, [showToast]);
41075
41505
  const {
@@ -41269,10 +41699,10 @@ function getVisibleLayers(layers, collapsed) {
41269
41699
  }
41270
41700
 
41271
41701
  // src/captions/components/CaptionPropertyPanel.tsx
41272
- import { memo as memo41, useCallback as useCallback96, useState as useState79 } from "react";
41702
+ import { memo as memo41, useCallback as useCallback97, useState as useState79 } from "react";
41273
41703
 
41274
41704
  // src/captions/components/CaptionAnimationPanel.tsx
41275
- import { memo as memo40, useCallback as useCallback95 } from "react";
41705
+ import { memo as memo40, useCallback as useCallback96 } from "react";
41276
41706
 
41277
41707
  // src/captions/components/shared.tsx
41278
41708
  import { jsx as jsx92, jsxs as jsxs77 } from "react/jsx-runtime";
@@ -41435,25 +41865,25 @@ var CaptionAnimationPanel = memo40(function CaptionAnimationPanel2() {
41435
41865
  }
41436
41866
  const group = resolvedGroupId ? model?.groups.get(resolvedGroupId) : void 0;
41437
41867
  const animation = group?.animation;
41438
- const handleEntranceChange = useCallback95(
41868
+ const handleEntranceChange = useCallback96(
41439
41869
  (update) => {
41440
41870
  if (resolvedGroupId) updateGroupAnimation(resolvedGroupId, "entrance", update);
41441
41871
  },
41442
41872
  [resolvedGroupId, updateGroupAnimation]
41443
41873
  );
41444
- const handleHighlightChange = useCallback95(
41874
+ const handleHighlightChange = useCallback96(
41445
41875
  (update) => {
41446
41876
  if (resolvedGroupId) updateGroupAnimation(resolvedGroupId, "highlight", update);
41447
41877
  },
41448
41878
  [resolvedGroupId, updateGroupAnimation]
41449
41879
  );
41450
- const handleExitChange = useCallback95(
41880
+ const handleExitChange = useCallback96(
41451
41881
  (update) => {
41452
41882
  if (resolvedGroupId) updateGroupAnimation(resolvedGroupId, "exit", update);
41453
41883
  },
41454
41884
  [resolvedGroupId, updateGroupAnimation]
41455
41885
  );
41456
- const handleApplyToAll = useCallback95(() => {
41886
+ const handleApplyToAll = useCallback96(() => {
41457
41887
  if (animation) applyAnimationToAll(animation);
41458
41888
  }, [animation, applyAnimationToAll]);
41459
41889
  if (!group || !resolvedGroupId || !animation) {
@@ -41531,7 +41961,7 @@ var CaptionPropertyPanel = memo41(function CaptionPropertyPanel2({
41531
41961
  ...groupStyle,
41532
41962
  ...segmentOverrides
41533
41963
  };
41534
- const applyToIframeDom = useCallback96(
41964
+ const applyToIframeDom = useCallback97(
41535
41965
  (updates) => {
41536
41966
  const iframe = iframeRef.current;
41537
41967
  if (!iframe || !model) return;
@@ -41605,7 +42035,7 @@ var CaptionPropertyPanel = memo41(function CaptionPropertyPanel2({
41605
42035
  },
41606
42036
  [iframeRef, model, selectedSegmentIds]
41607
42037
  );
41608
- const handleStyleChange = useCallback96(
42038
+ const handleStyleChange = useCallback97(
41609
42039
  (updates) => {
41610
42040
  if (selectedGroupId) {
41611
42041
  updateGroupStyle(selectedGroupId, updates);
@@ -41705,7 +42135,7 @@ var CaptionPropertyPanel = memo41(function CaptionPropertyPanel2({
41705
42135
  });
41706
42136
 
41707
42137
  // src/components/editor/BlockParamsPanel.tsx
41708
- import { memo as memo42, useState as useState80, useCallback as useCallback97 } from "react";
42138
+ import { memo as memo42, useState as useState80, useCallback as useCallback98 } from "react";
41709
42139
  import { jsx as jsx95, jsxs as jsxs80 } from "react/jsx-runtime";
41710
42140
  var BlockParamsPanel = memo42(function BlockParamsPanel2({
41711
42141
  blockTitle,
@@ -41720,7 +42150,7 @@ var BlockParamsPanel = memo42(function BlockParamsPanel2({
41720
42150
  }
41721
42151
  return initial;
41722
42152
  });
41723
- const handleChange = useCallback97((key, value) => {
42153
+ const handleChange = useCallback98((key, value) => {
41724
42154
  setValues((prev) => ({ ...prev, [key]: value }));
41725
42155
  }, []);
41726
42156
  return /* @__PURE__ */ jsxs80("div", { className: "flex flex-col h-full", children: [
@@ -41830,10 +42260,10 @@ function ParamControl({
41830
42260
  }
41831
42261
 
41832
42262
  // src/components/renders/RenderQueue.tsx
41833
- import { memo as memo44, useState as useState82, useRef as useRef85, useEffect as useEffect59 } from "react";
42263
+ import { memo as memo44, useState as useState82, useRef as useRef86, useEffect as useEffect59 } from "react";
41834
42264
 
41835
42265
  // src/components/renders/RenderQueueItem.tsx
41836
- import { memo as memo43, useCallback as useCallback98, useState as useState81 } from "react";
42266
+ import { memo as memo43, useCallback as useCallback99, useState as useState81 } from "react";
41837
42267
  import { Fragment as Fragment31, jsx as jsx96, jsxs as jsxs81 } from "react/jsx-runtime";
41838
42268
  function formatDuration(ms) {
41839
42269
  if (ms < 1e3) return `${ms}ms`;
@@ -41853,10 +42283,10 @@ var RenderQueueItem = memo43(function RenderQueueItem2({
41853
42283
  }) {
41854
42284
  const [hovered, setHovered] = useState81(false);
41855
42285
  const fileSrc = `/api/projects/${projectId}/renders/file/${job.filename}`;
41856
- const handleOpen = useCallback98(() => {
42286
+ const handleOpen = useCallback99(() => {
41857
42287
  window.open(fileSrc, "_blank");
41858
42288
  }, [fileSrc]);
41859
- const handleDownload = useCallback98(
42289
+ const handleDownload = useCallback99(
41860
42290
  (e) => {
41861
42291
  e.stopPropagation();
41862
42292
  const a = document.createElement("a");
@@ -42045,7 +42475,7 @@ var FORMAT_INFO = {
42045
42475
  };
42046
42476
  function FormatInfoTooltip({ format }) {
42047
42477
  const [open, setOpen] = useState82(false);
42048
- const timeoutRef = useRef85(void 0);
42478
+ const timeoutRef = useRef86(void 0);
42049
42479
  const show = () => {
42050
42480
  clearTimeout(timeoutRef.current);
42051
42481
  setOpen(true);
@@ -42212,7 +42642,7 @@ var RenderQueue = memo44(function RenderQueue2({
42212
42642
  isRendering,
42213
42643
  compositionDimensions
42214
42644
  }) {
42215
- const listRef = useRef85(null);
42645
+ const listRef = useRef86(null);
42216
42646
  useEffect59(() => {
42217
42647
  if (listRef.current) {
42218
42648
  listRef.current.scrollTo({ top: listRef.current.scrollHeight, behavior: "smooth" });
@@ -42295,11 +42725,11 @@ var RenderQueue = memo44(function RenderQueue2({
42295
42725
  });
42296
42726
 
42297
42727
  // src/components/panels/SlideshowPanel.tsx
42298
- import { useState as useState84, useEffect as useEffect60, useCallback as useCallback100, useRef as useRef86 } from "react";
42728
+ import { useState as useState84, useEffect as useEffect60, useCallback as useCallback101, useRef as useRef87 } from "react";
42299
42729
  import { parseSlideshowManifest } from "@hyperframes/core/slideshow";
42300
42730
 
42301
42731
  // src/components/panels/SlideshowSubPanels.tsx
42302
- import { useState as useState83, useCallback as useCallback99, useId } from "react";
42732
+ import { useState as useState83, useCallback as useCallback100, useId } from "react";
42303
42733
  import { jsx as jsx98, jsxs as jsxs83 } from "react/jsx-runtime";
42304
42734
  function SectionHeader({
42305
42735
  children,
@@ -42484,7 +42914,7 @@ function BranchTree({
42484
42914
  }) {
42485
42915
  const [newLabel, setNewLabel] = useState83("");
42486
42916
  const inputId = useId();
42487
- const handleCreate = useCallback99(() => {
42917
+ const handleCreate = useCallback100(() => {
42488
42918
  const label = newLabel.trim();
42489
42919
  if (!label) return;
42490
42920
  onCreateSequence(label);
@@ -42545,7 +42975,7 @@ function BranchItem({
42545
42975
  }) {
42546
42976
  const [editing, setEditing] = useState83(false);
42547
42977
  const [draft, setDraft] = useState83(seq.label);
42548
- const commitRename = useCallback99(() => {
42978
+ const commitRename = useCallback100(() => {
42549
42979
  const label = draft.trim();
42550
42980
  if (label && label !== seq.label) onRename(seq.id, label);
42551
42981
  setEditing(false);
@@ -42643,7 +43073,7 @@ function HotspotTool({
42643
43073
  const selectedElementId = domEditSelection?.element?.id ?? null;
42644
43074
  const selectedHfId = domEditSelection?.hfId ?? null;
42645
43075
  const elementKey = selectedElementId || selectedHfId;
42646
- const handleMakeHotspot = useCallback99(() => {
43076
+ const handleMakeHotspot = useCallback100(() => {
42647
43077
  if (!selectedSceneId || !targetSequenceId || !elementKey) return;
42648
43078
  const id = `hotspot-${elementKey}-${generateId()}`;
42649
43079
  const label = hotspotLabel.trim() || elementKey;
@@ -42932,8 +43362,8 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42932
43362
  );
42933
43363
  const currentTime = usePlayerStore((s) => s.currentTime);
42934
43364
  const { domEditSelection } = useDomEditSelectionContext();
42935
- const manifestRef = useRef86(manifest);
42936
- const notesCtrlRef = useRef86(makeSlideshowNotesController());
43365
+ const manifestRef = useRef87(manifest);
43366
+ const notesCtrlRef = useRef87(makeSlideshowNotesController());
42937
43367
  useEffect60(() => {
42938
43368
  if (!compHtml) {
42939
43369
  notesCtrlRef.current.flush();
@@ -42947,7 +43377,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42947
43377
  manifestRef.current = parsed;
42948
43378
  setSelectedSequenceId(null);
42949
43379
  }, [compHtml]);
42950
- const applyManifest = useCallback100(
43380
+ const applyManifest = useCallback101(
42951
43381
  async (next) => {
42952
43382
  const merged = notesCtrlRef.current.mergeIntoDiscrete(next);
42953
43383
  setManifest(merged);
@@ -42960,7 +43390,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42960
43390
  },
42961
43391
  [onPersist]
42962
43392
  );
42963
- const applyNotesManifest = useCallback100(
43393
+ const applyNotesManifest = useCallback101(
42964
43394
  (next) => {
42965
43395
  setManifest(next);
42966
43396
  manifestRef.current = next;
@@ -42974,7 +43404,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42974
43404
  ctrl.flush();
42975
43405
  };
42976
43406
  }, []);
42977
- const toggleSection = useCallback100((key) => {
43407
+ const toggleSection = useCallback101((key) => {
42978
43408
  setExpandedSections((prev) => {
42979
43409
  const next = new Set(prev);
42980
43410
  if (next.has(key)) {
@@ -42988,25 +43418,25 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42988
43418
  const activeSlides = selectedSequenceId ? (manifest.slideSequences ?? []).find((s) => s.id === selectedSequenceId)?.slides ?? [] : manifest.slides;
42989
43419
  const selectedSlide = activeSlides.find((s) => s.sceneId === selectedSceneId);
42990
43420
  const sequences = manifest.slideSequences ?? [];
42991
- const handleSelectBranchSlide = useCallback100((sequenceId, sceneId) => {
43421
+ const handleSelectBranchSlide = useCallback101((sequenceId, sceneId) => {
42992
43422
  setSelectedSceneId(sceneId);
42993
43423
  setSelectedSequenceId(sequenceId);
42994
43424
  }, []);
42995
- const handleToggleSlide = useCallback100(
43425
+ const handleToggleSlide = useCallback101(
42996
43426
  (sceneId) => {
42997
43427
  applyManifest(toggleMainLineSlide(manifestRef.current, sceneId)).catch(() => {
42998
43428
  });
42999
43429
  },
43000
43430
  [applyManifest]
43001
43431
  );
43002
- const handleReorder = useCallback100(
43432
+ const handleReorder = useCallback101(
43003
43433
  (sceneId, dir) => {
43004
43434
  applyManifest(reorderMainLineSlide(manifestRef.current, sceneId, dir)).catch(() => {
43005
43435
  });
43006
43436
  },
43007
43437
  [applyManifest]
43008
43438
  );
43009
- const handleSetNotes = useCallback100(
43439
+ const handleSetNotes = useCallback101(
43010
43440
  (notes) => {
43011
43441
  if (!selectedSceneId) return;
43012
43442
  applyNotesManifest(
@@ -43015,7 +43445,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43015
43445
  },
43016
43446
  [selectedSceneId, selectedSequenceId, applyNotesManifest]
43017
43447
  );
43018
- const handleMarkFragment = useCallback100(() => {
43448
+ const handleMarkFragment = useCallback101(() => {
43019
43449
  if (!selectedSceneId) return;
43020
43450
  applyManifest(
43021
43451
  addFragment(
@@ -43027,7 +43457,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43027
43457
  ).catch(() => {
43028
43458
  });
43029
43459
  }, [selectedSceneId, selectedSequenceId, currentTime, applyManifest]);
43030
- const handleRemoveFragment = useCallback100(
43460
+ const handleRemoveFragment = useCallback101(
43031
43461
  (time) => {
43032
43462
  if (!selectedSceneId) return;
43033
43463
  applyManifest(
@@ -43037,7 +43467,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43037
43467
  },
43038
43468
  [selectedSceneId, selectedSequenceId, applyManifest]
43039
43469
  );
43040
- const handleCreateSequence = useCallback100(
43470
+ const handleCreateSequence = useCallback101(
43041
43471
  (label) => {
43042
43472
  const id = `seq-${generateId()}`;
43043
43473
  applyManifest(createSequence(manifestRef.current, id, label)).catch(() => {
@@ -43045,14 +43475,14 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43045
43475
  },
43046
43476
  [applyManifest]
43047
43477
  );
43048
- const handleRenameSequence = useCallback100(
43478
+ const handleRenameSequence = useCallback101(
43049
43479
  (id, label) => {
43050
43480
  applyManifest(renameSequence(manifestRef.current, id, label)).catch(() => {
43051
43481
  });
43052
43482
  },
43053
43483
  [applyManifest]
43054
43484
  );
43055
- const handleDeleteSequence = useCallback100(
43485
+ const handleDeleteSequence = useCallback101(
43056
43486
  (id) => {
43057
43487
  const seq = (manifestRef.current.slideSequences ?? []).find((s) => s.id === id);
43058
43488
  const count = seq?.slides.length ?? 0;
@@ -43066,7 +43496,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43066
43496
  },
43067
43497
  [applyManifest]
43068
43498
  );
43069
- const handleAssign = useCallback100(
43499
+ const handleAssign = useCallback101(
43070
43500
  (sequenceId, sceneId, assign) => {
43071
43501
  applyManifest(assignToBranch(manifestRef.current, sequenceId, sceneId, assign)).catch(
43072
43502
  () => {
@@ -43075,7 +43505,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43075
43505
  },
43076
43506
  [applyManifest]
43077
43507
  );
43078
- const handleAddHotspot = useCallback100(
43508
+ const handleAddHotspot = useCallback101(
43079
43509
  (sceneId, hotspot) => {
43080
43510
  applyManifest(
43081
43511
  addHotspot(manifestRef.current, sceneId, hotspot, selectedSequenceId ?? void 0)
@@ -43084,7 +43514,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43084
43514
  },
43085
43515
  [selectedSequenceId, applyManifest]
43086
43516
  );
43087
- const handleRemoveHotspot = useCallback100(
43517
+ const handleRemoveHotspot = useCallback101(
43088
43518
  (sceneId, hotspotId) => {
43089
43519
  applyManifest(
43090
43520
  removeHotspot(manifestRef.current, sceneId, hotspotId, selectedSequenceId ?? void 0)
@@ -43188,7 +43618,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43188
43618
  }
43189
43619
 
43190
43620
  // src/hooks/useSlideshowPersist.ts
43191
- import { useCallback as useCallback101 } from "react";
43621
+ import { useCallback as useCallback102 } from "react";
43192
43622
 
43193
43623
  // src/utils/setSlideshowManifest.ts
43194
43624
  import {
@@ -43245,7 +43675,7 @@ function useSlideshowPersist({
43245
43675
  domEditSaveTimestampRef,
43246
43676
  coalesceKey
43247
43677
  }) {
43248
- return useCallback101(
43678
+ return useCallback102(
43249
43679
  async (manifest) => {
43250
43680
  if (!sdkSession) return;
43251
43681
  const path = activeCompPath ?? "index.html";
@@ -43382,8 +43812,8 @@ function StudioRightPanel({
43382
43812
  coalesceKey: activeCompPath ? `slideshow-notes:${activeCompPath}` : "slideshow-notes"
43383
43813
  });
43384
43814
  const [layersPanePercent, setLayersPanePercent] = useState85(40);
43385
- const splitContainerRef = useRef87(null);
43386
- const splitDragRef = useRef87(null);
43815
+ const splitContainerRef = useRef88(null);
43816
+ const splitDragRef = useRef88(null);
43387
43817
  const renderJobs = renderQueue.jobs;
43388
43818
  const inspectorTabActive = rightPanelTab === "design" || rightPanelTab === "layers";
43389
43819
  const slideshowScenes = useMemo34(() => {
@@ -43408,7 +43838,7 @@ function StudioRightPanel({
43408
43838
  }
43409
43839
  toggleRightInspectorPane(pane);
43410
43840
  };
43411
- const handleInspectorSplitResizeStart = useCallback102(
43841
+ const handleInspectorSplitResizeStart = useCallback103(
43412
43842
  (event) => {
43413
43843
  event.preventDefault();
43414
43844
  event.currentTarget.setPointerCapture(event.pointerId);
@@ -43421,7 +43851,7 @@ function StudioRightPanel({
43421
43851
  },
43422
43852
  [layersPanePercent]
43423
43853
  );
43424
- const handleInspectorSplitResizeMove = useCallback102((event) => {
43854
+ const handleInspectorSplitResizeMove = useCallback103((event) => {
43425
43855
  const drag = splitDragRef.current;
43426
43856
  if (!drag || drag.height <= 0) return;
43427
43857
  const deltaPercent = (event.clientY - drag.startY) / drag.height * 100;
@@ -43431,7 +43861,7 @@ function StudioRightPanel({
43431
43861
  );
43432
43862
  setLayersPanePercent(next);
43433
43863
  }, []);
43434
- const handleInspectorSplitResizeEnd = useCallback102(() => {
43864
+ const handleInspectorSplitResizeEnd = useCallback103(() => {
43435
43865
  splitDragRef.current = null;
43436
43866
  }, []);
43437
43867
  const propertyPanel = /* @__PURE__ */ jsx100(
@@ -43619,10 +44049,10 @@ function StudioRightPanel({
43619
44049
  }
43620
44050
 
43621
44051
  // src/components/TimelineToolbar.tsx
43622
- import { useRef as useRef88 } from "react";
44052
+ import { useRef as useRef89 } from "react";
43623
44053
 
43624
44054
  // src/hooks/useEnableKeyframes.ts
43625
- import { useCallback as useCallback103 } from "react";
44055
+ import { useCallback as useCallback104 } from "react";
43626
44056
  function animatedProps(anim) {
43627
44057
  if (!anim) return ["x", "y"];
43628
44058
  const own = Object.keys(anim.properties ?? {});
@@ -43832,7 +44262,7 @@ async function applyArcWaypointAtPlayhead(session, sel, arcAnim, t, iframe) {
43832
44262
  );
43833
44263
  }
43834
44264
  function useEnableKeyframes(sessionRef) {
43835
- return useCallback103(async () => {
44265
+ return useCallback104(async () => {
43836
44266
  const session = sessionRef.current;
43837
44267
  if (!session) return;
43838
44268
  const sel = session.domEditSelection;
@@ -43891,7 +44321,7 @@ function useEnableKeyframes(sessionRef) {
43891
44321
  }
43892
44322
 
43893
44323
  // src/hooks/useKeyframeKeyboard.ts
43894
- import { useEffect as useEffect61, useCallback as useCallback104 } from "react";
44324
+ import { useEffect as useEffect61, useCallback as useCallback105 } from "react";
43895
44325
  function isTextInput(el) {
43896
44326
  if (!el) return false;
43897
44327
  const tag = el.tagName;
@@ -43908,7 +44338,7 @@ function useKeyframeKeyboard({
43908
44338
  onToggleExpand,
43909
44339
  onNudgeKeyframe
43910
44340
  }) {
43911
- const handler = useCallback104(
44341
+ const handler = useCallback105(
43912
44342
  (e) => {
43913
44343
  if (!enabled) return;
43914
44344
  if (isTextInput(document.activeElement)) return;
@@ -43970,7 +44400,7 @@ function useKeyframeKeyboard({
43970
44400
  import { jsx as jsx101, jsxs as jsxs86 } from "react/jsx-runtime";
43971
44401
  function useKeyframeToggle(session) {
43972
44402
  const currentTime = usePlayerStore((s) => s.currentTime);
43973
- const sessionRef = useRef88(session);
44403
+ const sessionRef = useRef89(session);
43974
44404
  sessionRef.current = session;
43975
44405
  const onToggle = useEnableKeyframes(
43976
44406
  sessionRef
@@ -44195,13 +44625,13 @@ function TimelineToolbar({
44195
44625
  }
44196
44626
 
44197
44627
  // src/hooks/useStoryboard.ts
44198
- import { useCallback as useCallback105, useEffect as useEffect62, useState as useState86 } from "react";
44628
+ import { useCallback as useCallback106, useEffect as useEffect62, useState as useState86 } from "react";
44199
44629
  function useStoryboard(projectId) {
44200
44630
  const [data, setData] = useState86(null);
44201
44631
  const [loading, setLoading] = useState86(true);
44202
44632
  const [error, setError] = useState86(null);
44203
44633
  const [reloadKey, setReloadKey] = useState86(0);
44204
- const reload = useCallback105(() => setReloadKey((k) => k + 1), []);
44634
+ const reload = useCallback106(() => setReloadKey((k) => k + 1), []);
44205
44635
  useEffect62(() => {
44206
44636
  if (!projectId) return;
44207
44637
  let cancelled = false;
@@ -44425,7 +44855,7 @@ function StoryboardScriptPanel({ script }) {
44425
44855
  }
44426
44856
 
44427
44857
  // src/components/storyboard/StoryboardSourceEditor.tsx
44428
- import { useCallback as useCallback106, useEffect as useEffect64, useMemo as useMemo35, useRef as useRef89, useState as useState88 } from "react";
44858
+ import { useCallback as useCallback107, useEffect as useEffect64, useMemo as useMemo35, useRef as useRef90, useState as useState88 } from "react";
44429
44859
  import { marked } from "marked";
44430
44860
  import DOMPurify from "dompurify";
44431
44861
  import { jsx as jsx108, jsxs as jsxs91 } from "react/jsx-runtime";
@@ -44455,7 +44885,7 @@ function useEditableFile(path, onSaved) {
44455
44885
  cancelled = true;
44456
44886
  };
44457
44887
  }, [path, readProjectFile]);
44458
- const save = useCallback106(() => {
44888
+ const save = useCallback107(() => {
44459
44889
  if (saving) return;
44460
44890
  setSaving(true);
44461
44891
  setError(null);
@@ -44474,7 +44904,7 @@ function hardenLinks(node) {
44474
44904
  }
44475
44905
  function useMarkdownPreview(source) {
44476
44906
  const [debounced, setDebounced] = useState88(source);
44477
- const primed = useRef89(false);
44907
+ const primed = useRef90(false);
44478
44908
  useEffect64(() => {
44479
44909
  if (!primed.current && source !== "") {
44480
44910
  primed.current = true;
@@ -44580,7 +45010,7 @@ function StoryboardSourceEditor({
44580
45010
  }
44581
45011
 
44582
45012
  // src/components/storyboard/StoryboardFrameFocus.tsx
44583
- import { useCallback as useCallback107, useEffect as useEffect65, useState as useState89 } from "react";
45013
+ import { useCallback as useCallback108, useEffect as useEffect65, useState as useState89 } from "react";
44584
45014
  import { setFrameStatus, setFrameVoiceover } from "@hyperframes/core/storyboard";
44585
45015
  import { jsx as jsx109, jsxs as jsxs92 } from "react/jsx-runtime";
44586
45016
  function StoryboardFrameFocus({
@@ -44598,7 +45028,7 @@ function StoryboardFrameFocus({
44598
45028
  const [draft, setDraft] = useState89(frame.voiceover ?? "");
44599
45029
  const [busy, setBusy] = useState89(false);
44600
45030
  const [error, setError] = useState89(null);
44601
- const applyEdit = useCallback107(
45031
+ const applyEdit = useCallback108(
44602
45032
  async (edit) => {
44603
45033
  if (busy) return;
44604
45034
  setBusy(true);
@@ -44997,7 +45427,7 @@ function useServerConnection() {
44997
45427
  import { jsx as jsx113, jsxs as jsxs96 } from "react/jsx-runtime";
44998
45428
  function StudioApp() {
44999
45429
  const { projectId, resolving, waitingForServer } = useServerConnection();
45000
- const initialUrlStateRef = useRef90(readStudioUrlStateFromWindow());
45430
+ const initialUrlStateRef = useRef91(readStudioUrlStateFromWindow());
45001
45431
  const viewModeValue = useViewModeState();
45002
45432
  useEffect67(() => {
45003
45433
  if (resolving || waitingForServer) return;
@@ -45015,10 +45445,10 @@ function StudioApp() {
45015
45445
  const [refreshKey, setRefreshKey] = useState92(0);
45016
45446
  const [previewDocumentVersion, setPreviewDocumentVersion] = useState92(0);
45017
45447
  const [blockPreview, setBlockPreview] = useState92(null);
45018
- const previewIframeRef = useRef90(null);
45019
- const activeCompPathRef = useRef90(activeCompPath);
45448
+ const previewIframeRef = useRef91(null);
45449
+ const activeCompPathRef = useRef91(activeCompPath);
45020
45450
  activeCompPathRef.current = activeCompPath;
45021
- const leftSidebarRef = useRef90(null);
45451
+ const leftSidebarRef = useRef91(null);
45022
45452
  const renderQueue = useRenderQueue(projectId);
45023
45453
  const captionEditMode = useCaptionStore((s) => s.isEditMode);
45024
45454
  const captionHasSelection = useCaptionStore((s) => s.selectedSegmentIds.size > 0);
@@ -45033,8 +45463,8 @@ function StudioApp() {
45033
45463
  const maxEnd = timelineElements.length > 0 ? Math.max(...timelineElements.map((el) => el.start + el.duration)) : 0;
45034
45464
  return Math.max(timelineDuration, maxEnd);
45035
45465
  }, [timelineDuration, timelineElements]);
45036
- const refreshTimersRef = useRef90([]);
45037
- const refreshPreviewDocumentVersion = useCallback108(() => {
45466
+ const refreshTimersRef = useRef91([]);
45467
+ const refreshPreviewDocumentVersion = useCallback109(() => {
45038
45468
  for (const id of refreshTimersRef.current) clearTimeout(id);
45039
45469
  refreshTimersRef.current = [];
45040
45470
  setPreviewDocumentVersion((v) => v + 1);
@@ -45052,7 +45482,7 @@ function StudioApp() {
45052
45482
  const [timelineVisible, setTimelineVisible] = useState92(
45053
45483
  () => initialUrlStateRef.current.timelineVisible ?? readStudioUiPreferences().timelineVisible ?? true
45054
45484
  );
45055
- const toggleTimelineVisibility = useCallback108(() => {
45485
+ const toggleTimelineVisibility = useCallback109(() => {
45056
45486
  setTimelineVisible((v) => {
45057
45487
  writeStudioUiPreferences({ timelineVisible: !v });
45058
45488
  return !v;
@@ -45064,10 +45494,10 @@ function StudioApp() {
45064
45494
  rightPanelTab: initialUrlStateRef.current.rightPanelTab
45065
45495
  });
45066
45496
  const editHistory = usePersistentEditHistory({ projectId });
45067
- const domEditSaveTimestampRef = useRef90(0);
45068
- const pendingTimelineEditPathRef = useRef90(/* @__PURE__ */ new Set());
45069
- const isGestureRecordingRef = useRef90(false);
45070
- const reloadPreview = useCallback108(() => setRefreshKey((k) => k + 1), []);
45497
+ const domEditSaveTimestampRef = useRef91(0);
45498
+ const pendingTimelineEditPathRef = useRef91(/* @__PURE__ */ new Set());
45499
+ const isGestureRecordingRef = useRef91(false);
45500
+ const reloadPreview = useCallback109(() => setRefreshKey((k) => k + 1), []);
45071
45501
  const fileManager = useFileManager({
45072
45502
  projectId,
45073
45503
  showToast,
@@ -45136,18 +45566,18 @@ function StudioApp() {
45136
45566
  setRightCollapsed: panelLayout.setRightCollapsed,
45137
45567
  setRightPanelTab: panelLayout.setRightPanelTab
45138
45568
  });
45139
- const clearDomSelectionRef = useRef90(() => {
45569
+ const clearDomSelectionRef = useRef91(() => {
45140
45570
  });
45141
- const domEditSelectionBridgeRef = useRef90(null);
45142
- const handleDomEditElementDeleteRef = useRef90(
45571
+ const domEditSelectionBridgeRef = useRef91(null);
45572
+ const handleDomEditElementDeleteRef = useRef91(
45143
45573
  async () => {
45144
45574
  }
45145
45575
  );
45146
45576
  const domEditDeleteBridge = (s) => handleDomEditElementDeleteRef.current(s);
45147
- const resetKeyframesRef = useRef90(() => false);
45148
- const deleteSelectedKeyframesRef = useRef90(() => {
45577
+ const resetKeyframesRef = useRef91(() => false);
45578
+ const deleteSelectedKeyframesRef = useRef91(() => {
45149
45579
  });
45150
- const invalidateGsapCacheRef = useRef90(() => {
45580
+ const invalidateGsapCacheRef = useRef91(() => {
45151
45581
  });
45152
45582
  const { handleCopy, handlePaste, handleCut } = useClipboard({
45153
45583
  projectId,
@@ -45190,7 +45620,7 @@ function StudioApp() {
45190
45620
  forceReloadSdkSession: sdkHandle.forceReload,
45191
45621
  onToggleRecording: STUDIO_KEYFRAMES_ENABLED ? () => handleToggleRecordingRef.current() : void 0
45192
45622
  });
45193
- const sidebarTabRef = useRef90({
45623
+ const sidebarTabRef = useRef91({
45194
45624
  select: (t) => leftSidebarRef.current?.selectTab(t),
45195
45625
  get: () => leftSidebarRef.current?.getTab() ?? "compositions"
45196
45626
  });
@@ -45283,9 +45713,9 @@ function StudioApp() {
45283
45713
  resetErrors: resetConsoleErrors
45284
45714
  } = useConsoleErrorCapture(previewIframe);
45285
45715
  const dragOverlay = useDragOverlay(fileManager.handleImportFiles);
45286
- const handleToggleRecordingRef = useRef90(() => {
45716
+ const handleToggleRecordingRef = useRef91(() => {
45287
45717
  });
45288
- const domEditSessionRef = useRef90(domEditSession);
45718
+ const domEditSessionRef = useRef91(domEditSession);
45289
45719
  domEditSessionRef.current = domEditSession;
45290
45720
  const { gestureState, gestureRecording, handleToggleRecording } = useGestureCommit({
45291
45721
  domEditSessionRef,
@@ -45294,7 +45724,7 @@ function StudioApp() {
45294
45724
  isGestureRecordingRef
45295
45725
  });
45296
45726
  handleToggleRecordingRef.current = handleToggleRecording;
45297
- const canvasRectRef = useRef90(null);
45727
+ const canvasRectRef = useRef91(null);
45298
45728
  useLayoutEffect3(() => {
45299
45729
  if (gestureState !== "recording" || !previewIframe) {
45300
45730
  canvasRectRef.current = null;
@@ -45303,7 +45733,7 @@ function StudioApp() {
45303
45733
  const r = previewIframe.getBoundingClientRect();
45304
45734
  canvasRectRef.current = { left: r.left, top: r.top, width: r.width, height: r.height };
45305
45735
  }, [gestureState, previewIframe]);
45306
- const handlePreviewIframeRef = useCallback108(
45736
+ const handlePreviewIframeRef = useCallback109(
45307
45737
  (iframe) => {
45308
45738
  previewIframeRef.current = iframe;
45309
45739
  setPreviewIframe(iframe);
@@ -45314,7 +45744,7 @@ function StudioApp() {
45314
45744
  },
45315
45745
  [appHotkeys, resetConsoleErrors, refreshPreviewDocumentVersion]
45316
45746
  );
45317
- const handleSelectComposition = useCallback108(
45747
+ const handleSelectComposition = useCallback109(
45318
45748
  (comp) => {
45319
45749
  setActiveCompPath(comp.endsWith(".html") ? comp : null);
45320
45750
  fileManager.setEditingFile({ path: comp, content: null });
@@ -45520,18 +45950,18 @@ function StudioApp() {
45520
45950
  }
45521
45951
 
45522
45952
  // src/hooks/useElementPicker.ts
45523
- import { useState as useState93, useCallback as useCallback109, useRef as useRef91 } from "react";
45953
+ import { useState as useState93, useCallback as useCallback110, useRef as useRef92 } from "react";
45524
45954
  function useElementPicker(iframeRef, options) {
45525
45955
  const [isPickMode, setIsPickMode] = useState93(false);
45526
45956
  const [pickedElement, setPickedElement] = useState93(null);
45527
- const activeOverrideRef = useRef91(null);
45528
- const getActiveIframe = useCallback109(() => {
45957
+ const activeOverrideRef = useRef92(null);
45958
+ const getActiveIframe = useCallback110(() => {
45529
45959
  return activeOverrideRef.current ?? iframeRef.current;
45530
45960
  }, [iframeRef]);
45531
- const setActiveIframe = useCallback109((el) => {
45961
+ const setActiveIframe = useCallback110((el) => {
45532
45962
  activeOverrideRef.current = el;
45533
45963
  }, []);
45534
- const enablePick = useCallback109(() => {
45964
+ const enablePick = useCallback110(() => {
45535
45965
  try {
45536
45966
  getActiveIframe()?.contentWindow?.postMessage(
45537
45967
  { source: "hf-parent", type: "control", action: "enable-pick-mode" },
@@ -45541,7 +45971,7 @@ function useElementPicker(iframeRef, options) {
45541
45971
  } catch {
45542
45972
  }
45543
45973
  }, [getActiveIframe]);
45544
- const disablePick = useCallback109(() => {
45974
+ const disablePick = useCallback110(() => {
45545
45975
  try {
45546
45976
  getActiveIframe()?.contentWindow?.postMessage(
45547
45977
  { source: "hf-parent", type: "control", action: "disable-pick-mode" },
@@ -45551,7 +45981,7 @@ function useElementPicker(iframeRef, options) {
45551
45981
  }
45552
45982
  setIsPickMode(false);
45553
45983
  }, [getActiveIframe]);
45554
- const clearPick = useCallback109(() => {
45984
+ const clearPick = useCallback110(() => {
45555
45985
  setPickedElement(null);
45556
45986
  }, []);
45557
45987
  useMountEffect(() => {
@@ -45603,9 +46033,9 @@ function useElementPicker(iframeRef, options) {
45603
46033
  window.addEventListener("message", handleMessage);
45604
46034
  return () => window.removeEventListener("message", handleMessage);
45605
46035
  });
45606
- const optionsRef = useRef91(options);
46036
+ const optionsRef = useRef92(options);
45607
46037
  optionsRef.current = options;
45608
- const syncToSource = useCallback109(
46038
+ const syncToSource = useCallback110(
45609
46039
  (elementId, selector, op) => {
45610
46040
  const opts = optionsRef.current;
45611
46041
  if (!opts?.workspaceFiles || !opts.onSyncFiles || !elementId) return;
@@ -45621,7 +46051,7 @@ function useElementPicker(iframeRef, options) {
45621
46051
  },
45622
46052
  []
45623
46053
  );
45624
- const setStyle = useCallback109(
46054
+ const setStyle = useCallback110(
45625
46055
  (prop, value) => {
45626
46056
  const activeIframe = getActiveIframe();
45627
46057
  if (!pickedElement?.selector || !activeIframe) return;
@@ -45663,7 +46093,7 @@ function useElementPicker(iframeRef, options) {
45663
46093
  },
45664
46094
  [pickedElement, getActiveIframe, syncToSource]
45665
46095
  );
45666
- const setDataAttr = useCallback109(
46096
+ const setDataAttr = useCallback110(
45667
46097
  (attr, value) => {
45668
46098
  const activeIframe = getActiveIframe();
45669
46099
  if (!pickedElement?.selector || !activeIframe) return;
@@ -45691,7 +46121,7 @@ function useElementPicker(iframeRef, options) {
45691
46121
  },
45692
46122
  [pickedElement, getActiveIframe, syncToSource]
45693
46123
  );
45694
- const setTextContent = useCallback109(
46124
+ const setTextContent = useCallback110(
45695
46125
  (text) => {
45696
46126
  const activeIframe = getActiveIframe();
45697
46127
  if (!pickedElement?.selector || !activeIframe) return;
@@ -45714,7 +46144,7 @@ function useElementPicker(iframeRef, options) {
45714
46144
  },
45715
46145
  [pickedElement, getActiveIframe, syncToSource]
45716
46146
  );
45717
- const activeIframeRef = useRef91(null);
46147
+ const activeIframeRef = useRef92(null);
45718
46148
  activeIframeRef.current = getActiveIframe();
45719
46149
  return {
45720
46150
  isPickMode,