hyperframes 0.7.28 → 0.7.30

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();
@@ -1600,27 +1602,28 @@ async function flushEvents() {
1600
1602
  clearTimeout(timeout);
1601
1603
  }
1602
1604
  }
1605
+ function flushViaBeacon() {
1606
+ if (flushTimer) {
1607
+ clearInterval(flushTimer);
1608
+ flushTimer = null;
1609
+ }
1610
+ if (queue.length === 0) return;
1611
+ const batch = queue.map((e) => ({
1612
+ event: e.event,
1613
+ properties: { ...e.properties, $ip: null },
1614
+ distinct_id: getDistinctId(),
1615
+ timestamp: e.timestamp
1616
+ }));
1617
+ queue = [];
1618
+ const body = JSON.stringify({ api_key: POSTHOG_API_KEY, batch });
1619
+ try {
1620
+ navigator.sendBeacon(`${POSTHOG_HOST}/batch/`, body);
1621
+ } catch {
1622
+ }
1623
+ }
1603
1624
  if (typeof window !== "undefined") {
1604
1625
  window.addEventListener("visibilitychange", () => {
1605
- if (document.visibilityState === "hidden") {
1606
- if (flushTimer) {
1607
- clearInterval(flushTimer);
1608
- flushTimer = null;
1609
- }
1610
- if (queue.length === 0) return;
1611
- const batch = queue.map((e) => ({
1612
- event: e.event,
1613
- properties: { ...e.properties, $ip: null },
1614
- distinct_id: getDistinctId(),
1615
- timestamp: e.timestamp
1616
- }));
1617
- queue = [];
1618
- const body = JSON.stringify({ api_key: POSTHOG_API_KEY, batch });
1619
- try {
1620
- navigator.sendBeacon(`${POSTHOG_HOST}/batch/`, body);
1621
- } catch {
1622
- }
1623
- }
1626
+ if (document.visibilityState === "hidden") flushViaBeacon();
1624
1627
  });
1625
1628
  }
1626
1629
 
@@ -12763,24 +12766,34 @@ function Section({
12763
12766
  children: /* @__PURE__ */ jsx31("path", { d: "M2 3l3 4 3-4z" })
12764
12767
  }
12765
12768
  );
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
- ] });
12769
+ return /* @__PURE__ */ jsxs24(
12770
+ "section",
12771
+ {
12772
+ className: "min-w-0 border-t border-panel-border",
12773
+ "data-panel-section": slugifyPanelSectionTitle(title),
12774
+ children: [
12775
+ /* @__PURE__ */ jsxs24("div", { className: "flex w-full items-center gap-2 px-4 py-2.5", children: [
12776
+ /* @__PURE__ */ jsxs24(
12777
+ "button",
12778
+ {
12779
+ type: "button",
12780
+ onClick: () => setCollapsed((v) => !v),
12781
+ className: "flex min-w-0 flex-1 items-center justify-between gap-2 text-left",
12782
+ children: [
12783
+ /* @__PURE__ */ jsx31("h3", { className: "text-[12px] font-semibold text-panel-text-1", children: title }),
12784
+ collapseIcon
12785
+ ]
12786
+ }
12787
+ ),
12788
+ accessory && /* @__PURE__ */ jsx31("div", { className: "flex flex-shrink-0 items-center", children: accessory })
12789
+ ] }),
12790
+ !collapsed && /* @__PURE__ */ jsx31("div", { className: "px-4 pb-3", children })
12791
+ ]
12792
+ }
12793
+ );
12794
+ }
12795
+ function slugifyPanelSectionTitle(title) {
12796
+ return title.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
12784
12797
  }
12785
12798
 
12786
12799
  // src/components/editor/propertyPanelTransformCommit.ts
@@ -19920,7 +19933,7 @@ var FileTree = memo24(function FileTree2({
19920
19933
  });
19921
19934
 
19922
19935
  // 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";
19936
+ import { useState as useState92, useCallback as useCallback109, useRef as useRef91, useMemo as useMemo37, useEffect as useEffect67, useLayoutEffect as useLayoutEffect3 } from "react";
19924
19937
 
19925
19938
  // src/components/renders/useRenderQueue.ts
19926
19939
  import { useState as useState37, useEffect as useEffect26, useCallback as useCallback32, useRef as useRef35, useMemo as useMemo18 } from "react";
@@ -21271,10 +21284,12 @@ import { useState as useState43, useCallback as useCallback38, useRef as useRef4
21271
21284
  var STUDIO_SAVE_ATTEMPT_PROPERTY = "__studioSaveAttempt";
21272
21285
  var StudioSaveHttpError = class extends Error {
21273
21286
  statusCode;
21274
- constructor(message, statusCode) {
21287
+ alreadyToasted;
21288
+ constructor(message, statusCode, options = {}) {
21275
21289
  super(message);
21276
21290
  this.name = "StudioSaveHttpError";
21277
21291
  this.statusCode = statusCode;
21292
+ this.alreadyToasted = options.alreadyToasted ?? false;
21278
21293
  }
21279
21294
  };
21280
21295
  var StudioSaveNetworkError = class extends Error {
@@ -21358,7 +21373,7 @@ function buildStudioSaveFailureProperties(input) {
21358
21373
  function trackStudioSaveFailure(input) {
21359
21374
  trackStudioEvent("save_failure", buildStudioSaveFailureProperties(input));
21360
21375
  }
21361
- async function createStudioSaveHttpError(response, fallbackMessage) {
21376
+ async function createStudioSaveHttpError(response, fallbackMessage, options = {}) {
21362
21377
  let body = "";
21363
21378
  try {
21364
21379
  body = await response.text();
@@ -21367,7 +21382,7 @@ async function createStudioSaveHttpError(response, fallbackMessage) {
21367
21382
  }
21368
21383
  const detail = body.trim().slice(0, 300);
21369
21384
  const message = detail ? `${fallbackMessage} (${response.status}): ${detail}` : `${fallbackMessage} (${response.status})`;
21370
- return new StudioSaveHttpError(message, response.status);
21385
+ return new StudioSaveHttpError(message, response.status, options);
21371
21386
  }
21372
21387
  async function retryStudioSave(operation, options = {}) {
21373
21388
  const retries = options.retries ?? 3;
@@ -22453,6 +22468,42 @@ function sdkResolverShadowCheck(session, hfId, ops, sourceContent) {
22453
22468
  if (inverse.length > 0) session.applyPatches(inverse);
22454
22469
  }
22455
22470
  }
22471
+ var attemptCounts = {};
22472
+ function recordAttempt(opLabel) {
22473
+ attemptCounts[opLabel] = (attemptCounts[opLabel] ?? 0) + 1;
22474
+ ensureAttemptFlushScheduled();
22475
+ }
22476
+ function flushAttemptCounts() {
22477
+ const keys = Object.keys(attemptCounts);
22478
+ if (keys.length === 0) return null;
22479
+ const snapshot = {};
22480
+ for (const key of keys) {
22481
+ snapshot[key] = attemptCounts[key];
22482
+ delete attemptCounts[key];
22483
+ }
22484
+ return snapshot;
22485
+ }
22486
+ var ATTEMPT_FLUSH_INTERVAL_MS = 5 * 6e4;
22487
+ var attemptFlushTimer = null;
22488
+ var attemptVisibilityHandler = null;
22489
+ function flushAndEmitAttempts() {
22490
+ const counts = flushAttemptCounts();
22491
+ if (counts === null) return;
22492
+ trackStudioEvent("sdk_resolver_shadow_attempt", { counts: JSON.stringify(counts) });
22493
+ }
22494
+ function ensureAttemptFlushScheduled() {
22495
+ if (!attemptFlushTimer) {
22496
+ attemptFlushTimer = setInterval(flushAndEmitAttempts, ATTEMPT_FLUSH_INTERVAL_MS);
22497
+ }
22498
+ if (!attemptVisibilityHandler && typeof document !== "undefined") {
22499
+ attemptVisibilityHandler = () => {
22500
+ if (document.visibilityState !== "hidden") return;
22501
+ flushAndEmitAttempts();
22502
+ flushViaBeacon();
22503
+ };
22504
+ document.addEventListener("visibilitychange", attemptVisibilityHandler);
22505
+ }
22506
+ }
22456
22507
  function redactValue(value) {
22457
22508
  if (value == null) return value;
22458
22509
  return `[redacted len=${value.length}]`;
@@ -22468,6 +22519,7 @@ function runResolverShadow(session, hfId, ops, sourceContent) {
22468
22519
  if (!STUDIO_SDK_RESOLVER_SHADOW_ENABLED) return;
22469
22520
  if (!hfId) return;
22470
22521
  try {
22522
+ recordAttempt("dom-edit");
22471
22523
  const mismatches = sdkResolverShadowCheck(session, hfId, ops, sourceContent);
22472
22524
  if (mismatches.length === 0) return;
22473
22525
  const isElementNotFound = mismatches.some((m) => m.kind === "element_not_found");
@@ -22497,6 +22549,7 @@ async function recordResolverParity(session, hfId, opLabel, readSource) {
22497
22549
  if (!STUDIO_SDK_RESOLVER_SHADOW_ENABLED) return;
22498
22550
  if (!session || !hfId) return;
22499
22551
  try {
22552
+ recordAttempt(opLabel);
22500
22553
  if (resolveSnapshot(session, hfId)) return;
22501
22554
  const sessionElementCount = session.getElements().length;
22502
22555
  let source;
@@ -22536,6 +22589,7 @@ function recordAnimationResolverParity(session, animationId, opLabel) {
22536
22589
  if (!STUDIO_SDK_RESOLVER_SHADOW_ENABLED) return;
22537
22590
  if (!session || !animationId) return;
22538
22591
  try {
22592
+ recordAttempt(opLabel);
22539
22593
  const elements = session.getElements();
22540
22594
  const resolves = elements.some((el) => el.animationIds.includes(animationId));
22541
22595
  if (resolves) return;
@@ -22674,6 +22728,9 @@ function hasUnsafeHtmlAttributeOp(ops) {
22674
22728
  (op) => op.type === "html-attribute" && (!isAllowedHtmlAttribute(op.property) || op.value !== null && !isSafeAttributeValue(op.property, op.value))
22675
22729
  );
22676
22730
  }
22731
+ function hasChildScopedOp(ops) {
22732
+ return ops.some((op) => op.childSelector !== void 0);
22733
+ }
22677
22734
  function hasTextContentOp(ops) {
22678
22735
  return ops.some((op) => op.type === "text-content");
22679
22736
  }
@@ -22697,7 +22754,8 @@ function shouldDeclineTextCutoverForTarget(target, ops) {
22697
22754
  return tag !== null && NON_HTML_CHILD_TAGS.has(tag);
22698
22755
  }
22699
22756
  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);
22757
+ 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.
22758
+ !hasChildScopedOp(ops) && !ops.some(mapsToReservedAttr) && !hasUnsafeHtmlAttributeOp(ops);
22701
22759
  }
22702
22760
 
22703
22761
  // src/utils/sdkCutover.ts
@@ -23382,7 +23440,7 @@ function useTimelineEditing({
23382
23440
  }
23383
23441
 
23384
23442
  // src/hooks/useDomEditSession.ts
23385
- import { useCallback as useCallback64 } from "react";
23443
+ import { useCallback as useCallback65 } from "react";
23386
23444
 
23387
23445
  // src/hooks/useAskAgentModal.ts
23388
23446
  import { useState as useState45, useCallback as useCallback42, useRef as useRef45, useEffect as useEffect29 } from "react";
@@ -23540,6 +23598,65 @@ function removePointerEventsOverride(style) {
23540
23598
  } catch {
23541
23599
  }
23542
23600
  }
23601
+ var pointerEventsInheritanceFallbackByDocument = /* @__PURE__ */ new WeakMap();
23602
+ function needsPointerEventsInheritanceFallback(doc, win) {
23603
+ const cached2 = pointerEventsInheritanceFallbackByDocument.get(doc);
23604
+ if (cached2 !== void 0) return cached2;
23605
+ const parent = doc.createElement("div");
23606
+ const child = doc.createElement("div");
23607
+ parent.style.pointerEvents = "none";
23608
+ parent.appendChild(child);
23609
+ const host = doc.body ?? doc.documentElement;
23610
+ if (!host) return false;
23611
+ host.appendChild(parent);
23612
+ const needsFallback = win.getComputedStyle(child).pointerEvents !== "none";
23613
+ parent.remove();
23614
+ pointerEventsInheritanceFallbackByDocument.set(doc, needsFallback);
23615
+ return needsFallback;
23616
+ }
23617
+ function hasOwnPointerEventsOverride(el, win) {
23618
+ const value = win.getComputedStyle(el).pointerEvents;
23619
+ return value !== "" && value !== "inherit" && value !== "unset";
23620
+ }
23621
+ function inheritsPointerEventsNoneFromAncestor(el, win) {
23622
+ let current = el.parentElement;
23623
+ while (current) {
23624
+ if (win.getComputedStyle(current).pointerEvents === "none") {
23625
+ let descendant = el;
23626
+ while (descendant && descendant !== current) {
23627
+ if (hasOwnPointerEventsOverride(descendant, win)) {
23628
+ return win.getComputedStyle(descendant).pointerEvents === "none";
23629
+ }
23630
+ descendant = descendant.parentElement;
23631
+ }
23632
+ return true;
23633
+ }
23634
+ current = current.parentElement;
23635
+ }
23636
+ return false;
23637
+ }
23638
+ function hasAuthorPointerEventsNone(el) {
23639
+ const win = el.ownerDocument.defaultView;
23640
+ if (!win) return false;
23641
+ if (win.getComputedStyle(el).pointerEvents === "none") return true;
23642
+ if (!needsPointerEventsInheritanceFallback(el.ownerDocument, win)) return false;
23643
+ return inheritsPointerEventsNoneFromAncestor(el, win);
23644
+ }
23645
+ function collectPointerEventsNoneTargets(elements) {
23646
+ const disabled = /* @__PURE__ */ new WeakSet();
23647
+ for (const entry of elements) {
23648
+ if (isHtmlElement(entry) && hasAuthorPointerEventsNone(entry)) {
23649
+ disabled.add(entry);
23650
+ }
23651
+ }
23652
+ return disabled;
23653
+ }
23654
+ function filterAuthorInteractiveTargets(elements, activeCompositionPath) {
23655
+ const pointerEventsNoneTargets = collectPointerEventsNoneTargets(elements);
23656
+ return resolveAllVisualDomEditTargets(elements, { activeCompositionPath }).filter(
23657
+ (el) => !pointerEventsNoneTargets.has(el)
23658
+ );
23659
+ }
23543
23660
  function findGroupAtPoint(doc, x, y) {
23544
23661
  let best = null;
23545
23662
  let bestArea = Infinity;
@@ -23577,20 +23694,24 @@ function getPreviewTargetFromPointer(iframe, clientX, clientY, activeComposition
23577
23694
  if (!doc || !win) return null;
23578
23695
  const localPointer = resolvePreviewLocalPointer(iframe, doc, win, clientX, clientY);
23579
23696
  if (!localPointer) return null;
23580
- const overrideStyle = forcePointerEventsAuto(doc);
23697
+ let overrideStyle = forcePointerEventsAuto(doc);
23581
23698
  try {
23582
23699
  if (typeof doc.elementsFromPoint === "function") {
23583
- const candidates = resolveAllVisualDomEditTargets(
23584
- doc.elementsFromPoint(localPointer.x, localPointer.y),
23585
- { activeCompositionPath }
23586
- );
23700
+ const elements = doc.elementsFromPoint(localPointer.x, localPointer.y);
23701
+ removePointerEventsOverride(overrideStyle);
23702
+ overrideStyle = null;
23703
+ const candidates = filterAuthorInteractiveTargets(elements, activeCompositionPath);
23587
23704
  const visualTarget = candidates.find((el) => !isFullBleedTarget(el, localPointer.viewport)) ?? null;
23588
23705
  if (visualTarget) return visualTarget;
23589
23706
  }
23707
+ removePointerEventsOverride(overrideStyle);
23708
+ overrideStyle = null;
23590
23709
  const groupHit = findGroupAtPoint(doc, localPointer.x, localPointer.y);
23591
- if (groupHit && getDomLayerPatchTarget(groupHit, activeCompositionPath)) return groupHit;
23710
+ if (groupHit && !hasAuthorPointerEventsNone(groupHit) && getDomLayerPatchTarget(groupHit, activeCompositionPath))
23711
+ return groupHit;
23592
23712
  const fallback = getEventTargetElement(doc.elementFromPoint(localPointer.x, localPointer.y));
23593
23713
  if (!fallback || !getDomLayerPatchTarget(fallback, activeCompositionPath)) return null;
23714
+ if (hasAuthorPointerEventsNone(fallback)) return null;
23594
23715
  if (!isElementComputedVisible(fallback)) return null;
23595
23716
  if (isFullBleedTarget(fallback, localPointer.viewport)) return null;
23596
23717
  return fallback;
@@ -23610,15 +23731,21 @@ function getAllPreviewTargetsFromPointer(iframe, clientX, clientY, activeComposi
23610
23731
  if (!doc || !win) return [];
23611
23732
  const localPointer = resolvePreviewLocalPointer(iframe, doc, win, clientX, clientY);
23612
23733
  if (!localPointer) return [];
23613
- const overrideStyle = forcePointerEventsAuto(doc);
23734
+ let overrideStyle = forcePointerEventsAuto(doc);
23614
23735
  try {
23615
23736
  if (typeof doc.elementsFromPoint === "function") {
23616
- return resolveAllVisualDomEditTargets(doc.elementsFromPoint(localPointer.x, localPointer.y), {
23617
- activeCompositionPath
23618
- }).filter((el) => !isFullBleedTarget(el, localPointer.viewport));
23737
+ const elements = doc.elementsFromPoint(localPointer.x, localPointer.y);
23738
+ removePointerEventsOverride(overrideStyle);
23739
+ overrideStyle = null;
23740
+ return filterAuthorInteractiveTargets(elements, activeCompositionPath).filter(
23741
+ (el) => !isFullBleedTarget(el, localPointer.viewport)
23742
+ );
23619
23743
  }
23620
23744
  const fallback = getEventTargetElement(doc.elementFromPoint(localPointer.x, localPointer.y));
23621
23745
  if (!fallback || !getDomLayerPatchTarget(fallback, activeCompositionPath)) return [];
23746
+ removePointerEventsOverride(overrideStyle);
23747
+ overrideStyle = null;
23748
+ if (hasAuthorPointerEventsNone(fallback)) return [];
23622
23749
  if (!isElementComputedVisible(fallback)) return [];
23623
23750
  if (isFullBleedTarget(fallback, localPointer.viewport)) return [];
23624
23751
  return [fallback];
@@ -23730,6 +23857,7 @@ function useDomSelection({
23730
23857
  const domEditGroupSelectionsRef = useRef46(domEditGroupSelections);
23731
23858
  const domEditHoverSelectionRef = useRef46(domEditHoverSelection);
23732
23859
  const activeGroupElementRef = useRef46(activeGroupElement);
23860
+ const compositionIdentityRef = useRef46({ activeCompPath, projectId });
23733
23861
  domEditSelectionRef.current = domEditSelection;
23734
23862
  domEditGroupSelectionsRef.current = domEditGroupSelections;
23735
23863
  domEditHoverSelectionRef.current = domEditHoverSelection;
@@ -23791,6 +23919,8 @@ function useDomSelection({
23791
23919
  }, [applyDomSelection]);
23792
23920
  const setActiveGroupElement = useCallback43(
23793
23921
  (el) => {
23922
+ if (activeGroupElementRef.current === el) return;
23923
+ activeGroupElementRef.current = el;
23794
23924
  setActiveGroupElementState(el);
23795
23925
  applyDomSelection(null, { revealPanel: false });
23796
23926
  },
@@ -23963,6 +24093,14 @@ function useDomSelection({
23963
24093
  useEffect30(() => {
23964
24094
  updateDomEditHoverSelection(null);
23965
24095
  }, [activeCompPath, projectId, previewIframe, refreshKey, updateDomEditHoverSelection]);
24096
+ useEffect30(() => {
24097
+ const previous = compositionIdentityRef.current;
24098
+ if (previous.activeCompPath === activeCompPath && previous.projectId === projectId) return;
24099
+ compositionIdentityRef.current = { activeCompPath, projectId };
24100
+ activeGroupElementRef.current = null;
24101
+ setActiveGroupElementState(null);
24102
+ applyDomSelection(null, { revealPanel: false });
24103
+ }, [activeCompPath, projectId, applyDomSelection]);
23966
24104
  useEffect30(() => {
23967
24105
  if (!domEditHoverSelection) return;
23968
24106
  const shouldClear = captionEditMode || domEditSelectionsTargetSame(domEditHoverSelection, domEditSelection) || domEditSelectionInGroup(domEditGroupSelections, domEditHoverSelection) || !domEditHoverSelection.element.isConnected;
@@ -24076,6 +24214,15 @@ function usePreviewInteraction({
24076
24214
  }) {
24077
24215
  const cycleRef = useRef47(null);
24078
24216
  const lastDownRef = useRef47(null);
24217
+ const pausePreviewPlayback = useCallback44(() => {
24218
+ const pausedTime = pauseStudioPreviewPlayback(previewIframeRef.current);
24219
+ const playerStore = usePlayerStore.getState();
24220
+ playerStore.setIsPlaying(false);
24221
+ if (pausedTime != null) {
24222
+ playerStore.setCurrentTime(pausedTime);
24223
+ liveTime.notify(pausedTime);
24224
+ }
24225
+ }, [previewIframeRef]);
24079
24226
  const handlePreviewCanvasMouseDown = useCallback44(
24080
24227
  // fallow-ignore-next-line complexity
24081
24228
  async (e, options) => {
@@ -24084,8 +24231,15 @@ function usePreviewInteraction({
24084
24231
  const lastDown = lastDownRef.current;
24085
24232
  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
24233
  lastDownRef.current = { t: downTs, x: e.clientX, y: e.clientY };
24234
+ const wasPlaying = usePlayerStore.getState().isPlaying;
24235
+ pausePreviewPlayback();
24236
+ const resumeIfNothingSelected = () => {
24237
+ if (wasPlaying) usePlayerStore.getState().setIsPlaying(true);
24238
+ };
24087
24239
  if (isDoubleClick && !e.shiftKey) {
24088
24240
  const hit = await resolveDomSelectionFromPreviewPoint(e.clientX, e.clientY);
24241
+ const cycle = cycleRef.current;
24242
+ 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
24243
  if (hit?.element.hasAttribute("data-hf-group")) {
24090
24244
  e.preventDefault();
24091
24245
  e.stopPropagation();
@@ -24098,6 +24252,13 @@ function usePreviewInteraction({
24098
24252
  applyDomSelection(child ?? hit);
24099
24253
  return;
24100
24254
  }
24255
+ if (hit && !hasStackCycleAtSpot && !hit.element.hasAttribute("data-composition-src") && !hit.element.hasAttribute("data-composition-file")) {
24256
+ e.preventDefault();
24257
+ e.stopPropagation();
24258
+ cycleRef.current = null;
24259
+ applyDomSelection(hit);
24260
+ return;
24261
+ }
24101
24262
  }
24102
24263
  const now = Date.now();
24103
24264
  const prev = cycleRef.current;
@@ -24108,8 +24269,11 @@ function usePreviewInteraction({
24108
24269
  cycleRef.current = null;
24109
24270
  const nextSelection2 = await resolveDomSelectionFromPreviewPoint(e.clientX, e.clientY, {
24110
24271
  preferClipAncestor: options?.preferClipAncestor ?? false
24111
- });
24112
- if (!nextSelection2) return;
24272
+ }) ?? options?.hoverSelection ?? null;
24273
+ if (!nextSelection2) {
24274
+ resumeIfNothingSelected();
24275
+ return;
24276
+ }
24113
24277
  e.preventDefault();
24114
24278
  e.stopPropagation();
24115
24279
  applyDomSelection(nextSelection2, { additive: true });
@@ -24134,9 +24298,11 @@ function usePreviewInteraction({
24134
24298
  activeGroupElement: null
24135
24299
  });
24136
24300
  }
24301
+ nextSelection = nextSelection ?? options?.hoverSelection ?? null;
24137
24302
  if (!nextSelection) {
24138
24303
  cycleRef.current = null;
24139
24304
  applyDomSelection(null, { revealPanel: false });
24305
+ resumeIfNothingSelected();
24140
24306
  return;
24141
24307
  }
24142
24308
  e.preventDefault();
@@ -24153,6 +24319,7 @@ function usePreviewInteraction({
24153
24319
  captionEditMode,
24154
24320
  compositionLoading,
24155
24321
  onClickToSource,
24322
+ pausePreviewPlayback,
24156
24323
  resolveAllDomSelectionsFromPreviewPoint,
24157
24324
  resolveDomSelectionFromPreviewPoint,
24158
24325
  setActiveGroupElement
@@ -24192,14 +24359,8 @@ function usePreviewInteraction({
24192
24359
  [showToast]
24193
24360
  );
24194
24361
  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]);
24362
+ pausePreviewPlayback();
24363
+ }, [pausePreviewPlayback]);
24203
24364
  return {
24204
24365
  handlePreviewCanvasMouseDown,
24205
24366
  handlePreviewCanvasPointerMove,
@@ -24210,7 +24371,7 @@ function usePreviewInteraction({
24210
24371
  }
24211
24372
 
24212
24373
  // src/hooks/useDomEditCommits.ts
24213
- import { useCallback as useCallback49, useRef as useRef49 } from "react";
24374
+ import { useCallback as useCallback50, useRef as useRef50 } from "react";
24214
24375
  import { findUnsafeDomPatchValues as findUnsafeDomPatchValues2 } from "@hyperframes/core/studio-api/finite-mutation";
24215
24376
 
24216
24377
  // src/utils/studioFontHelpers.ts
@@ -24282,6 +24443,66 @@ ${css2}
24282
24443
  ${html2}`;
24283
24444
  }
24284
24445
 
24446
+ // src/hooks/domEditPersistFailure.ts
24447
+ var DomEditPersistUnresolvableError = class extends Error {
24448
+ constructor(targetPath) {
24449
+ super(`Couldn't find this element in the source file (${targetPath})`);
24450
+ this.name = "DomEditPersistUnresolvableError";
24451
+ }
24452
+ };
24453
+ var DomEditPersistUnsafeValueError = class extends Error {
24454
+ alreadyToasted;
24455
+ constructor(message, options = {}) {
24456
+ super(message);
24457
+ this.name = "DomEditPersistUnsafeValueError";
24458
+ this.alreadyToasted = options.alreadyToasted ?? false;
24459
+ }
24460
+ };
24461
+ var DomEditPersistUnsupportedTextStructureError = class extends Error {
24462
+ constructor() {
24463
+ super("Couldn't save this text structure change");
24464
+ this.name = "DomEditPersistUnsupportedTextStructureError";
24465
+ }
24466
+ };
24467
+ function summarizeOperations(operations) {
24468
+ return operations.map((op) => `${op.type}:${op.property}`).join(", ");
24469
+ }
24470
+ function getTargetTuple(selection) {
24471
+ return {
24472
+ hfId: selection.hfId,
24473
+ id: selection.id,
24474
+ selector: selection.selector,
24475
+ selectorIndex: selection.selectorIndex,
24476
+ sourceFile: selection.sourceFile
24477
+ };
24478
+ }
24479
+ function getErrorDetail(error) {
24480
+ return error instanceof Error ? error.message : String(error);
24481
+ }
24482
+ function getSelectionLabel(selection) {
24483
+ return selection.label || selection.selector || selection.id || "this element";
24484
+ }
24485
+ function reportDomEditPersistFailure(selection, operations, error, showToast) {
24486
+ const detail = getErrorDetail(error);
24487
+ console.warn("[Studio] DOM edit persist failed", {
24488
+ target: getTargetTuple(selection),
24489
+ operations: summarizeOperations(operations),
24490
+ error: detail
24491
+ });
24492
+ const wasAlreadyToasted = (error instanceof DomEditPersistUnsafeValueError || error instanceof StudioSaveHttpError) && error.alreadyToasted;
24493
+ if (wasAlreadyToasted) {
24494
+ return;
24495
+ }
24496
+ showToast(`Couldn't save "${getSelectionLabel(selection)}": ${detail}`, "error");
24497
+ }
24498
+ function warnDomEditPersistNoOp(selection, operations) {
24499
+ console.warn("[Studio] DOM edit persist no-op", {
24500
+ target: getTargetTuple(selection),
24501
+ operations: summarizeOperations(operations),
24502
+ detail: "Server matched the target but reported no change even though the client believed the value changed."
24503
+ });
24504
+ }
24505
+
24285
24506
  // src/hooks/useDomEditPositionPatchCommit.ts
24286
24507
  import { useCallback as useCallback45 } from "react";
24287
24508
  function useDomEditPositionPatchCommit({
@@ -24319,114 +24540,154 @@ function useDomEditPositionPatchCommit({
24319
24540
  }
24320
24541
 
24321
24542
  // src/hooks/useDomEditTextCommits.ts
24543
+ import { useCallback as useCallback47, useRef as useRef49 } from "react";
24544
+
24545
+ // src/hooks/domEditTextFieldCommitOps.ts
24546
+ function hasSameKeysInSamePositions(originalFields, nextFields) {
24547
+ return originalFields.every((field, index) => nextFields[index]?.key === field.key);
24548
+ }
24549
+ function inlineStyleValue(styles, property) {
24550
+ return Object.prototype.hasOwnProperty.call(styles, property) ? styles[property] : null;
24551
+ }
24552
+ function inlineStyleProperties(originalStyles, nextStyles) {
24553
+ return Array.from(/* @__PURE__ */ new Set([...Object.keys(originalStyles), ...Object.keys(nextStyles)]));
24554
+ }
24555
+ function buildTextFieldChildOperations(originalFields, nextFields) {
24556
+ if (originalFields.length !== nextFields.length) return null;
24557
+ if (!hasSameKeysInSamePositions(originalFields, nextFields)) return null;
24558
+ if (nextFields.some((field) => field.source === "text-node")) return null;
24559
+ if (nextFields.some((field) => field.source !== "child")) return null;
24560
+ if (originalFields.some((field) => field.source !== "child")) return null;
24561
+ const originalByKey = new Map(originalFields.map((field) => [field.key, field]));
24562
+ const operations = [];
24563
+ for (const nextField of nextFields) {
24564
+ const originalField = originalByKey.get(nextField.key);
24565
+ const locator = buildTextFieldChildLocator(originalFields, nextField.key);
24566
+ if (!originalField || !locator) return null;
24567
+ if (nextField.value !== originalField.value) {
24568
+ operations.push(buildDomEditTextPatchOperation(nextField.value, locator));
24569
+ }
24570
+ for (const property of inlineStyleProperties(
24571
+ originalField.inlineStyles,
24572
+ nextField.inlineStyles
24573
+ )) {
24574
+ const originalValue = inlineStyleValue(originalField.inlineStyles, property);
24575
+ const nextValue = inlineStyleValue(nextField.inlineStyles, property);
24576
+ if (nextValue !== originalValue) {
24577
+ operations.push(buildDomEditStylePatchOperation(property, nextValue, locator));
24578
+ }
24579
+ }
24580
+ }
24581
+ return operations;
24582
+ }
24583
+
24584
+ // src/hooks/domEditCommitRunner.ts
24585
+ function bumpDomEditCommitVersion(versionRef) {
24586
+ const commitVersion = versionRef.current + 1;
24587
+ versionRef.current = commitVersion;
24588
+ return () => versionRef.current === commitVersion;
24589
+ }
24590
+ function bumpDomEditCommitMapVersion(versionMap, versionKey) {
24591
+ const commitVersion = (versionMap.get(versionKey) ?? 0) + 1;
24592
+ versionMap.set(versionKey, commitVersion);
24593
+ return () => versionMap.get(versionKey) === commitVersion;
24594
+ }
24595
+ async function runDomEditCommit(config) {
24596
+ config.capture();
24597
+ config.apply();
24598
+ try {
24599
+ await config.persist();
24600
+ } catch (error) {
24601
+ if (config.shouldRevert(error)) {
24602
+ config.revert();
24603
+ }
24604
+ config.onError(error);
24605
+ }
24606
+ if (!config.shouldResync()) return;
24607
+ await config.resync();
24608
+ }
24609
+
24610
+ // src/hooks/useDomEditAttributeCommits.ts
24322
24611
  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") {
24612
+ function resolveFullAttrName(attr, prefixData) {
24613
+ return prefixData && !attr.startsWith("data-") ? `data-${attr}` : attr;
24614
+ }
24615
+ function setOrRemovePreviewAttribute(el, fullAttr, value) {
24616
+ if (value === null) {
24329
24617
  el.removeAttribute(fullAttr);
24330
24618
  } else {
24331
24619
  el.setAttribute(fullAttr, value);
24332
24620
  }
24333
24621
  }
24334
- function useDomEditTextCommits({
24622
+ function findPreviewAttributeElement(doc, selection, activeCompPath) {
24623
+ if (!doc) return null;
24624
+ return findElementForSelection(doc, selection, activeCompPath);
24625
+ }
24626
+ function captureAttributeElement(doc, selection, activeCompPath, fullAttr) {
24627
+ const el = findPreviewAttributeElement(doc, selection, activeCompPath);
24628
+ if (!el) return null;
24629
+ return { element: el, previousValue: el.getAttribute(fullAttr) };
24630
+ }
24631
+ function useDomEditAttributeCommits({
24335
24632
  activeCompPath,
24336
24633
  previewIframeRef,
24634
+ showToast,
24337
24635
  domEditSelection,
24338
- applyDomSelection,
24339
24636
  refreshDomEditSelectionFromPreview,
24340
- buildDomSelectionFromTarget,
24341
- persistDomEditOperations,
24342
- resolveImportedFontAsset
24637
+ persistDomEditOperations
24343
24638
  }) {
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
- );
24639
+ const domAttributeCommitVersionRef = useRef48(/* @__PURE__ */ new Map());
24398
24640
  const commitDataAttribute = useCallback46(
24399
24641
  async (attr, value, options) => {
24400
24642
  if (!domEditSelection) return;
24401
24643
  const iframe = previewIframeRef.current;
24402
- applyPreviewAttribute(
24403
- iframe?.contentDocument,
24404
- domEditSelection,
24405
- activeCompPath,
24406
- attr,
24407
- value,
24408
- {
24409
- prefixData: true
24410
- }
24644
+ const fullAttr = resolveFullAttrName(attr, true);
24645
+ const commitKey = `${options.coalescePrefix}:${attr}:${getDomEditTargetKey(domEditSelection)}`;
24646
+ const isLatestCommit = bumpDomEditCommitMapVersion(
24647
+ domAttributeCommitVersionRef.current,
24648
+ commitKey
24411
24649
  );
24412
24650
  const op = { type: "attribute", property: attr, value };
24413
- try {
24414
- await persistDomEditOperations(domEditSelection, [op], {
24651
+ let editedElement = null;
24652
+ let previousValue = null;
24653
+ await runDomEditCommit({
24654
+ capture: () => {
24655
+ const captured = captureAttributeElement(
24656
+ iframe?.contentDocument,
24657
+ domEditSelection,
24658
+ activeCompPath,
24659
+ fullAttr
24660
+ );
24661
+ if (!captured) return;
24662
+ editedElement = captured.element;
24663
+ previousValue = captured.previousValue;
24664
+ },
24665
+ apply: () => {
24666
+ if (!editedElement) return;
24667
+ const nextValue = value === null || value === "" ? null : value;
24668
+ setOrRemovePreviewAttribute(editedElement, fullAttr, nextValue);
24669
+ },
24670
+ persist: () => persistDomEditOperations(domEditSelection, [op], {
24415
24671
  label: options.label,
24416
- coalesceKey: `${options.coalescePrefix}:${attr}:${getDomEditTargetKey(domEditSelection)}`,
24672
+ coalesceKey: commitKey,
24417
24673
  skipRefresh: options.skipRefresh
24418
- });
24419
- } catch {
24420
- }
24421
- if (options.refreshAfter) {
24422
- refreshDomEditSelectionFromPreview(domEditSelection);
24423
- }
24674
+ }),
24675
+ shouldRevert: () => isLatestCommit(),
24676
+ revert: () => {
24677
+ if (!editedElement) return;
24678
+ setOrRemovePreviewAttribute(editedElement, fullAttr, previousValue);
24679
+ },
24680
+ onError: (error) => reportDomEditPersistFailure(domEditSelection, [op], error, showToast),
24681
+ shouldResync: () => isLatestCommit() && !!options.refreshAfter,
24682
+ resync: () => refreshDomEditSelectionFromPreview(domEditSelection)
24683
+ });
24424
24684
  },
24425
24685
  [
24426
24686
  activeCompPath,
24427
24687
  domEditSelection,
24428
24688
  persistDomEditOperations,
24429
24689
  refreshDomEditSelectionFromPreview,
24690
+ showToast,
24430
24691
  previewIframeRef
24431
24692
  ]
24432
24693
  );
@@ -24436,7 +24697,6 @@ function useDomEditTextCommits({
24436
24697
  label: `Edit ${attr.replace(/-/g, " ")}`,
24437
24698
  coalescePrefix: "attr",
24438
24699
  skipRefresh: false,
24439
- warningMessage: "[Studio] Attribute persist failed:",
24440
24700
  refreshAfter: true
24441
24701
  });
24442
24702
  },
@@ -24447,8 +24707,7 @@ function useDomEditTextCommits({
24447
24707
  await commitDataAttribute(attr, value, {
24448
24708
  label: `Edit ${attr.replace(/^(data-)?/, "").replace(/-/g, " ")}`,
24449
24709
  coalescePrefix: "attr-live",
24450
- skipRefresh: true,
24451
- warningMessage: "[Studio] Live attribute persist failed:"
24710
+ skipRefresh: true
24452
24711
  });
24453
24712
  },
24454
24713
  [commitDataAttribute]
@@ -24457,76 +24716,242 @@ function useDomEditTextCommits({
24457
24716
  async (attr, value) => {
24458
24717
  if (!domEditSelection) return;
24459
24718
  const iframe = previewIframeRef.current;
24460
- applyPreviewAttribute(
24461
- iframe?.contentDocument,
24462
- domEditSelection,
24463
- activeCompPath,
24464
- attr,
24465
- value,
24466
- {
24467
- removeFalse: true
24468
- }
24719
+ const commitKey = `html-attr:${attr}:${getDomEditTargetKey(domEditSelection)}`;
24720
+ const isLatestCommit = bumpDomEditCommitMapVersion(
24721
+ domAttributeCommitVersionRef.current,
24722
+ commitKey
24469
24723
  );
24470
24724
  const op = { type: "html-attribute", property: attr, value };
24471
- try {
24472
- await persistDomEditOperations(domEditSelection, [op], {
24725
+ let editedElement = null;
24726
+ let previousValue = null;
24727
+ await runDomEditCommit({
24728
+ capture: () => {
24729
+ const captured = captureAttributeElement(
24730
+ iframe?.contentDocument,
24731
+ domEditSelection,
24732
+ activeCompPath,
24733
+ attr
24734
+ );
24735
+ if (!captured) return;
24736
+ editedElement = captured.element;
24737
+ previousValue = captured.previousValue;
24738
+ },
24739
+ apply: () => {
24740
+ if (!editedElement) return;
24741
+ const nextValue = value === null || value === "false" ? null : value;
24742
+ setOrRemovePreviewAttribute(editedElement, attr, nextValue);
24743
+ },
24744
+ persist: () => persistDomEditOperations(domEditSelection, [op], {
24473
24745
  label: `Edit ${attr}`,
24474
- coalesceKey: `html-attr:${attr}:${getDomEditTargetKey(domEditSelection)}`,
24746
+ coalesceKey: commitKey,
24475
24747
  skipRefresh: false
24476
- });
24477
- } catch {
24478
- }
24479
- refreshDomEditSelectionFromPreview(domEditSelection);
24748
+ }),
24749
+ shouldRevert: () => isLatestCommit(),
24750
+ revert: () => {
24751
+ if (!editedElement) return;
24752
+ setOrRemovePreviewAttribute(editedElement, attr, previousValue);
24753
+ },
24754
+ onError: (error) => reportDomEditPersistFailure(domEditSelection, [op], error, showToast),
24755
+ shouldResync: () => isLatestCommit(),
24756
+ resync: () => refreshDomEditSelectionFromPreview(domEditSelection)
24757
+ });
24758
+ },
24759
+ [
24760
+ activeCompPath,
24761
+ domEditSelection,
24762
+ persistDomEditOperations,
24763
+ refreshDomEditSelectionFromPreview,
24764
+ showToast,
24765
+ previewIframeRef
24766
+ ]
24767
+ );
24768
+ return {
24769
+ handleDomAttributeCommit,
24770
+ handleDomAttributeLiveCommit,
24771
+ handleDomHtmlAttributeCommit
24772
+ };
24773
+ }
24774
+
24775
+ // src/hooks/useDomEditTextCommits.ts
24776
+ function buildDomStyleCommitOperations(property, value, isImageBackgroundCommit) {
24777
+ const operations = [
24778
+ buildDomEditStylePatchOperation(property, normalizeDomEditStyleValue(property, value))
24779
+ ];
24780
+ if (isImageBackgroundCommit) {
24781
+ operations.push(
24782
+ buildDomEditStylePatchOperation("background-position", "center"),
24783
+ buildDomEditStylePatchOperation("background-repeat", "no-repeat"),
24784
+ buildDomEditStylePatchOperation("background-size", "contain")
24785
+ );
24786
+ }
24787
+ return operations;
24788
+ }
24789
+ function buildNextDomTextFields(textFields, value, fieldKey) {
24790
+ if (textFields.length === 0) return [];
24791
+ return textFields.map((field) => field.key === fieldKey ? { ...field, value } : field);
24792
+ }
24793
+ function planDomTextCommit(originalTextFields, nextTextFields, plainTextContent) {
24794
+ const usesSerializedTextFields = nextTextFields.length > 1 || nextTextFields.some((field) => field.source === "child");
24795
+ const nextContent = usesSerializedTextFields ? serializeDomEditTextFields(nextTextFields) : plainTextContent;
24796
+ const childOperations = usesSerializedTextFields ? buildTextFieldChildOperations(originalTextFields, nextTextFields) : null;
24797
+ const operations = childOperations ?? (usesSerializedTextFields ? [] : [buildDomEditTextPatchOperation(nextContent)]);
24798
+ return {
24799
+ usesSerializedTextFields,
24800
+ nextContent,
24801
+ childOperations,
24802
+ operations
24803
+ };
24804
+ }
24805
+ async function resyncDomTextSelectionFromPreview(doc, selection, activeCompPath, buildDomSelectionFromTarget, applyDomSelection) {
24806
+ if (!doc) return;
24807
+ const refreshed = findElementForSelection(doc, selection, activeCompPath);
24808
+ if (!refreshed) return;
24809
+ const nextSelection = await buildDomSelectionFromTarget(refreshed);
24810
+ if (!nextSelection) return;
24811
+ applyDomSelection(nextSelection, { revealPanel: false, preserveGroup: true });
24812
+ }
24813
+ function useDomEditTextCommits({
24814
+ activeCompPath,
24815
+ previewIframeRef,
24816
+ showToast,
24817
+ domEditSelection,
24818
+ applyDomSelection,
24819
+ refreshDomEditSelectionFromPreview,
24820
+ buildDomSelectionFromTarget,
24821
+ persistDomEditOperations,
24822
+ resolveImportedFontAsset
24823
+ }) {
24824
+ const domTextCommitVersionRef = useRef49(0);
24825
+ const domStyleCommitVersionRef = useRef49(/* @__PURE__ */ new Map());
24826
+ const { handleDomAttributeCommit, handleDomAttributeLiveCommit, handleDomHtmlAttributeCommit } = useDomEditAttributeCommits({
24827
+ activeCompPath,
24828
+ previewIframeRef,
24829
+ showToast,
24830
+ domEditSelection,
24831
+ refreshDomEditSelectionFromPreview,
24832
+ persistDomEditOperations
24833
+ });
24834
+ const handleDomStyleCommit = useCallback47(
24835
+ async (property, value) => {
24836
+ if (!domEditSelection) return;
24837
+ if (isManualGeometryStyleProperty(property)) return;
24838
+ if (!domEditSelection.capabilities.canEditStyles) return;
24839
+ const styleCommitKey = `${getDomEditTargetKey(domEditSelection)}:${property}`;
24840
+ const isLatestStyleCommit = bumpDomEditCommitMapVersion(
24841
+ domStyleCommitVersionRef.current,
24842
+ styleCommitKey
24843
+ );
24844
+ const importedFont = property === "font-family" ? resolveImportedFontAsset(value) : null;
24845
+ const iframe = previewIframeRef.current;
24846
+ const doc = iframe?.contentDocument;
24847
+ const normalizedValue = normalizeDomEditStyleValue(property, value);
24848
+ const isImageBackgroundCommit = property === "background-image" && isImageBackgroundValue(value);
24849
+ let editedElement = null;
24850
+ let previousInlineValue = null;
24851
+ const operations = buildDomStyleCommitOperations(property, value, isImageBackgroundCommit);
24852
+ const skipRefresh = property !== "z-index";
24853
+ await runDomEditCommit({
24854
+ capture: () => {
24855
+ if (!doc) return;
24856
+ const el = findElementForSelection(doc, domEditSelection, activeCompPath);
24857
+ if (!el) return;
24858
+ editedElement = el;
24859
+ previousInlineValue = el.style.getPropertyValue(property);
24860
+ },
24861
+ apply: () => {
24862
+ if (!editedElement) return;
24863
+ editedElement.style.setProperty(property, normalizedValue);
24864
+ if (property === "font-family" && doc) {
24865
+ injectPreviewGoogleFont(doc, value);
24866
+ if (importedFont) injectPreviewImportedFont(doc, importedFont);
24867
+ }
24868
+ if (isImageBackgroundCommit) {
24869
+ editedElement.style.setProperty("background-position", "center");
24870
+ editedElement.style.setProperty("background-repeat", "no-repeat");
24871
+ editedElement.style.setProperty("background-size", "contain");
24872
+ }
24873
+ },
24874
+ persist: () => persistDomEditOperations(domEditSelection, operations, {
24875
+ label: "Edit layer style",
24876
+ skipRefresh,
24877
+ prepareContent: importedFont ? (html2, sourceFile) => ensureImportedFontFace(html2, importedFont, sourceFile) : void 0
24878
+ }),
24879
+ shouldRevert: () => isLatestStyleCommit(),
24880
+ revert: () => {
24881
+ if (!editedElement || previousInlineValue === null) return;
24882
+ if (previousInlineValue === "") {
24883
+ editedElement.style.removeProperty(property);
24884
+ } else {
24885
+ editedElement.style.setProperty(property, previousInlineValue);
24886
+ }
24887
+ },
24888
+ onError: (error) => reportDomEditPersistFailure(domEditSelection, operations, error, showToast),
24889
+ shouldResync: isLatestStyleCommit,
24890
+ resync: () => refreshDomEditSelectionFromPreview(domEditSelection)
24891
+ });
24480
24892
  },
24481
24893
  [
24482
24894
  activeCompPath,
24483
24895
  domEditSelection,
24484
24896
  persistDomEditOperations,
24485
24897
  refreshDomEditSelectionFromPreview,
24898
+ resolveImportedFontAsset,
24899
+ showToast,
24486
24900
  previewIframeRef
24487
24901
  ]
24488
24902
  );
24489
- const handleDomTextCommit = useCallback46(
24903
+ const handleDomTextCommit = useCallback47(
24490
24904
  async (value, fieldKey) => {
24491
24905
  if (!domEditSelection) return;
24492
24906
  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;
24907
+ const isLatestTextCommit = bumpDomEditCommitVersion(domTextCommitVersionRef);
24908
+ const nextTextFields = buildNextDomTextFields(domEditSelection.textFields, value, fieldKey);
24909
+ const textCommit = planDomTextCommit(domEditSelection.textFields, nextTextFields, value);
24499
24910
  const iframe = previewIframeRef.current;
24500
24911
  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;
24912
+ let editedElement = null;
24913
+ let previousInnerHtml = null;
24914
+ await runDomEditCommit({
24915
+ capture: () => {
24916
+ if (!doc) return;
24917
+ const el = findElementForSelection(doc, domEditSelection, activeCompPath);
24918
+ if (!el) return;
24919
+ editedElement = el;
24920
+ previousInnerHtml = el.innerHTML;
24921
+ },
24922
+ apply: () => {
24923
+ if (!editedElement) return;
24924
+ if (textCommit.usesSerializedTextFields) {
24925
+ editedElement.innerHTML = textCommit.nextContent;
24506
24926
  } else {
24507
- el.textContent = value;
24927
+ editedElement.textContent = value;
24508
24928
  }
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 });
24929
+ },
24930
+ persist: async () => {
24931
+ if (textCommit.usesSerializedTextFields && textCommit.childOperations === null) {
24932
+ throw new DomEditPersistUnsupportedTextStructureError();
24527
24933
  }
24528
- }
24529
- }
24934
+ await persistDomEditOperations(domEditSelection, textCommit.operations, {
24935
+ label: "Edit text",
24936
+ skipRefresh: true,
24937
+ shouldSave: isLatestTextCommit
24938
+ });
24939
+ },
24940
+ shouldRevert: () => isLatestTextCommit(),
24941
+ revert: () => {
24942
+ if (!editedElement || previousInnerHtml === null) return;
24943
+ editedElement.innerHTML = previousInnerHtml;
24944
+ },
24945
+ onError: (error) => reportDomEditPersistFailure(domEditSelection, textCommit.operations, error, showToast),
24946
+ shouldResync: isLatestTextCommit,
24947
+ resync: () => resyncDomTextSelectionFromPreview(
24948
+ doc,
24949
+ domEditSelection,
24950
+ activeCompPath,
24951
+ buildDomSelectionFromTarget,
24952
+ applyDomSelection
24953
+ )
24954
+ });
24530
24955
  },
24531
24956
  [
24532
24957
  activeCompPath,
@@ -24534,49 +24959,74 @@ function useDomEditTextCommits({
24534
24959
  buildDomSelectionFromTarget,
24535
24960
  domEditSelection,
24536
24961
  persistDomEditOperations,
24537
- previewIframeRef
24962
+ previewIframeRef,
24963
+ showToast
24538
24964
  ]
24539
24965
  );
24540
- const commitDomTextFields = useCallback46(
24966
+ const commitDomTextFields = useCallback47(
24541
24967
  async (selection, nextTextFields, options) => {
24542
- const nextContent = nextTextFields.length > 1 || nextTextFields.some((field) => field.source === "child") ? serializeDomEditTextFields(nextTextFields) : nextTextFields[0]?.value ?? "";
24968
+ const textCommit = planDomTextCommit(
24969
+ selection.textFields,
24970
+ nextTextFields,
24971
+ nextTextFields[0]?.value ?? ""
24972
+ );
24543
24973
  const iframe = previewIframeRef.current;
24544
24974
  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;
24975
+ let editedElement = null;
24976
+ let previousInnerHtml = null;
24977
+ const importedFont = options?.importedFont ?? null;
24978
+ await runDomEditCommit({
24979
+ capture: () => {
24980
+ if (!doc) return;
24981
+ const el = findElementForSelection(doc, selection, activeCompPath);
24982
+ if (!el) return;
24983
+ editedElement = el;
24984
+ previousInnerHtml = el.innerHTML;
24985
+ },
24986
+ apply: () => {
24987
+ if (!editedElement) return;
24988
+ if (textCommit.usesSerializedTextFields) {
24989
+ editedElement.innerHTML = textCommit.nextContent;
24550
24990
  } else {
24551
- el.textContent = nextContent;
24991
+ editedElement.textContent = textCommit.nextContent;
24552
24992
  }
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 });
24993
+ },
24994
+ persist: async () => {
24995
+ if (textCommit.usesSerializedTextFields && textCommit.childOperations === null) {
24996
+ throw new DomEditPersistUnsupportedTextStructureError();
24567
24997
  }
24568
- }
24569
- }
24998
+ await persistDomEditOperations(selection, textCommit.operations, {
24999
+ label: "Edit text",
25000
+ skipRefresh: true,
25001
+ prepareContent: importedFont ? (html2, sourceFile) => ensureImportedFontFace(html2, importedFont, sourceFile) : void 0
25002
+ });
25003
+ },
25004
+ shouldRevert: () => true,
25005
+ revert: () => {
25006
+ if (!editedElement || previousInnerHtml === null) return;
25007
+ editedElement.innerHTML = previousInnerHtml;
25008
+ },
25009
+ onError: (error) => reportDomEditPersistFailure(selection, textCommit.operations, error, showToast),
25010
+ shouldResync: () => true,
25011
+ resync: () => resyncDomTextSelectionFromPreview(
25012
+ doc,
25013
+ selection,
25014
+ activeCompPath,
25015
+ buildDomSelectionFromTarget,
25016
+ applyDomSelection
25017
+ )
25018
+ });
24570
25019
  },
24571
25020
  [
24572
25021
  activeCompPath,
24573
25022
  applyDomSelection,
24574
25023
  buildDomSelectionFromTarget,
24575
25024
  persistDomEditOperations,
24576
- previewIframeRef
25025
+ previewIframeRef,
25026
+ showToast
24577
25027
  ]
24578
25028
  );
24579
- const handleDomTextFieldStyleCommit = useCallback46(
25029
+ const handleDomTextFieldStyleCommit = useCallback47(
24580
25030
  async (fieldKey, property, value) => {
24581
25031
  if (!domEditSelection) return;
24582
25032
  const field = domEditSelection.textFields.find((entry) => entry.key === fieldKey);
@@ -24617,7 +25067,7 @@ function useDomEditTextCommits({
24617
25067
  previewIframeRef
24618
25068
  ]
24619
25069
  );
24620
- const handleDomAddTextField = useCallback46(
25070
+ const handleDomAddTextField = useCallback47(
24621
25071
  async (afterFieldKey) => {
24622
25072
  if (!domEditSelection) return null;
24623
25073
  if (!domEditSelection.textFields.some((field) => field.source === "child")) return null;
@@ -24637,7 +25087,7 @@ function useDomEditTextCommits({
24637
25087
  },
24638
25088
  [commitDomTextFields, domEditSelection]
24639
25089
  );
24640
- const handleDomRemoveTextField = useCallback46(
25090
+ const handleDomRemoveTextField = useCallback47(
24641
25091
  async (fieldKey) => {
24642
25092
  if (!domEditSelection) return;
24643
25093
  const field = domEditSelection.textFields.find((entry) => entry.key === fieldKey);
@@ -24665,7 +25115,7 @@ function useDomEditTextCommits({
24665
25115
  }
24666
25116
 
24667
25117
  // src/hooks/useDomGeometryCommits.ts
24668
- import { useCallback as useCallback47 } from "react";
25118
+ import { useCallback as useCallback48 } from "react";
24669
25119
 
24670
25120
  // src/components/editor/manualEditsDomPatches.ts
24671
25121
  function collectInlineStyleOps(element, properties, ops) {
@@ -24874,7 +25324,7 @@ function useDomGeometryCommits({
24874
25324
  showToast,
24875
25325
  commitPositionPatchToHtml
24876
25326
  }) {
24877
- const handleDomPathOffsetCommit = useCallback47(
25327
+ const handleDomPathOffsetCommit = useCallback48(
24878
25328
  (selection, next) => {
24879
25329
  const gsapTargeted = isElementGsapTargeted(previewIframeRef.current, selection.element);
24880
25330
  if (gsapTargeted) {
@@ -24890,7 +25340,7 @@ function useDomGeometryCommits({
24890
25340
  },
24891
25341
  [commitPositionPatchToHtml, previewIframeRef, showToast]
24892
25342
  );
24893
- const handleDomBoxSizeCommit = useCallback47(
25343
+ const handleDomBoxSizeCommit = useCallback48(
24894
25344
  (selection, next) => {
24895
25345
  if (isElementGsapTargeted(previewIframeRef.current, selection.element)) {
24896
25346
  const error = new Error(GSAP_CSS_FALLBACK_BLOCKED_MESSAGE);
@@ -24905,7 +25355,7 @@ function useDomGeometryCommits({
24905
25355
  },
24906
25356
  [commitPositionPatchToHtml, previewIframeRef, showToast]
24907
25357
  );
24908
- const handleDomRotationCommit = useCallback47(
25358
+ const handleDomRotationCommit = useCallback48(
24909
25359
  (selection, next) => {
24910
25360
  if (isElementGsapTargeted(previewIframeRef.current, selection.element)) {
24911
25361
  const error = new Error(GSAP_CSS_FALLBACK_BLOCKED_MESSAGE);
@@ -24920,7 +25370,7 @@ function useDomGeometryCommits({
24920
25370
  },
24921
25371
  [commitPositionPatchToHtml, previewIframeRef, showToast]
24922
25372
  );
24923
- const handleDomManualEditsReset = useCallback47(
25373
+ const handleDomManualEditsReset = useCallback48(
24924
25374
  (selection) => {
24925
25375
  const element = selection.element;
24926
25376
  const clearPatches = [
@@ -24948,7 +25398,7 @@ function useDomGeometryCommits({
24948
25398
  }
24949
25399
 
24950
25400
  // src/hooks/useElementLifecycleOps.ts
24951
- import { useCallback as useCallback48 } from "react";
25401
+ import { useCallback as useCallback49 } from "react";
24952
25402
  function useElementLifecycleOps({
24953
25403
  activeCompPath,
24954
25404
  showToast,
@@ -24964,7 +25414,7 @@ function useElementLifecycleOps({
24964
25414
  commitPositionPatchToHtml,
24965
25415
  onElementDeleted
24966
25416
  }) {
24967
- const handleDomEditElementDelete = useCallback48(
25417
+ const handleDomEditElementDelete = useCallback49(
24968
25418
  // fallow-ignore-next-line complexity
24969
25419
  async (selection) => {
24970
25420
  const pid = projectIdRef.current;
@@ -25046,7 +25496,7 @@ function useElementLifecycleOps({
25046
25496
  writeProjectFile
25047
25497
  ]
25048
25498
  );
25049
- const handleDomZIndexReorderCommit = useCallback48(
25499
+ const handleDomZIndexReorderCommit = useCallback49(
25050
25500
  (entries) => {
25051
25501
  if (entries.length === 0) return;
25052
25502
  onReorderShadow?.(
@@ -25326,6 +25776,9 @@ async function assignGsapTargetAutoIdIfNeeded({
25326
25776
  function formatUnsafeFieldList(fields) {
25327
25777
  return fields.map((field) => field.path).join(", ");
25328
25778
  }
25779
+ function getErrorDetail2(error) {
25780
+ return error instanceof Error ? error.message : String(error);
25781
+ }
25329
25782
  async function readErrorResponseBody(response) {
25330
25783
  const contentType = response.headers.get("content-type") ?? "";
25331
25784
  if (!contentType.includes("application/json")) return null;
@@ -25358,7 +25811,7 @@ function useDomEditCommits({
25358
25811
  onTrySdkDelete,
25359
25812
  onReorderShadow
25360
25813
  }) {
25361
- const resolveImportedFontAsset = useCallback49(
25814
+ const resolveImportedFontAsset = useCallback50(
25362
25815
  (fontFamilyValue) => {
25363
25816
  const family = primaryFontFamilyValue(fontFamilyValue);
25364
25817
  if (!family) return null;
@@ -25378,8 +25831,8 @@ function useDomEditCommits({
25378
25831
  },
25379
25832
  [fileTree, projectId, importedFontAssetsRef]
25380
25833
  );
25381
- const reportedUnresolvableRef = useRef49(/* @__PURE__ */ new Set());
25382
- const persistDomEditOperations = useCallback49(
25834
+ const reportedUnresolvableRef = useRef50(/* @__PURE__ */ new Set());
25835
+ const persistDomEditOperations = useCallback50(
25383
25836
  // fallow-ignore-next-line complexity
25384
25837
  async (selection, operations, options) => {
25385
25838
  const pid = projectIdRef.current;
@@ -25404,7 +25857,9 @@ function useDomEditCommits({
25404
25857
  if (unsafeFields.length > 0) {
25405
25858
  const fields = formatUnsafeFieldList(unsafeFields);
25406
25859
  showToast("Couldn't save edit because it contains invalid layout values", "error");
25407
- throw new Error(`DOM patch contains unsafe values: ${fields}`);
25860
+ throw new DomEditPersistUnsafeValueError(`DOM patch contains unsafe values: ${fields}`, {
25861
+ alreadyToasted: true
25862
+ });
25408
25863
  }
25409
25864
  if (onTrySdkPersist && !options?.prepareContent && await onTrySdkPersist(selection, operations, originalContent, targetPath, {
25410
25865
  label: options?.label,
@@ -25424,7 +25879,9 @@ function useDomEditCommits({
25424
25879
  );
25425
25880
  if (!patchResponse.ok) {
25426
25881
  showToast(formatPatchRejectionMessage(await readErrorResponseBody(patchResponse)), "error");
25427
- throw await createStudioSaveHttpError(patchResponse, `Failed to patch ${targetPath}`);
25882
+ throw await createStudioSaveHttpError(patchResponse, `Failed to patch ${targetPath}`, {
25883
+ alreadyToasted: true
25884
+ });
25428
25885
  }
25429
25886
  const patchData = await patchResponse.json();
25430
25887
  if (!patchData.changed) {
@@ -25439,15 +25896,25 @@ function useDomEditCommits({
25439
25896
  composition: activeCompPath ?? void 0
25440
25897
  });
25441
25898
  }
25899
+ throw new DomEditPersistUnresolvableError(targetPath);
25442
25900
  }
25901
+ warnDomEditPersistNoOp(selection, operations);
25443
25902
  return;
25444
25903
  }
25445
25904
  const patchedContent = typeof patchData.content === "string" ? patchData.content : originalContent;
25446
25905
  let finalContent = patchedContent;
25447
25906
  if (options?.prepareContent) {
25448
- finalContent = options.prepareContent(patchedContent, targetPath);
25449
- if (finalContent !== patchedContent) {
25450
- await writeProjectFile(targetPath, finalContent);
25907
+ const preparedContent = options.prepareContent(patchedContent, targetPath);
25908
+ if (preparedContent !== patchedContent) {
25909
+ try {
25910
+ await writeProjectFile(targetPath, preparedContent);
25911
+ finalContent = preparedContent;
25912
+ } catch (error) {
25913
+ showToast(
25914
+ `Saved, but couldn't finish updating ${targetPath}: ${getErrorDetail2(error)}`,
25915
+ "error"
25916
+ );
25917
+ }
25451
25918
  }
25452
25919
  }
25453
25920
  await editHistory.recordEdit({
@@ -25491,7 +25958,8 @@ function useDomEditCommits({
25491
25958
  refreshDomEditSelectionFromPreview,
25492
25959
  buildDomSelectionFromTarget,
25493
25960
  persistDomEditOperations,
25494
- resolveImportedFontAsset
25961
+ resolveImportedFontAsset,
25962
+ showToast
25495
25963
  });
25496
25964
  const commitPositionPatchToHtml = useDomEditPositionPatchCommit({
25497
25965
  activeCompPath,
@@ -25544,7 +26012,7 @@ function useDomEditCommits({
25544
26012
  }
25545
26013
 
25546
26014
  // src/hooks/useGroupCommits.ts
25547
- import { useCallback as useCallback50 } from "react";
26015
+ import { useCallback as useCallback51 } from "react";
25548
26016
  function computeGroupGeometry(members) {
25549
26017
  const boxes = members.map((m) => ({
25550
26018
  target: buildDomEditPatchTarget(m),
@@ -25604,7 +26072,7 @@ async function commitStructuralMutation(pid, targetPath, route, body, label, dep
25604
26072
  }
25605
26073
  function useGroupCommits(params) {
25606
26074
  const { activeCompPath, showToast, projectIdRef } = params;
25607
- const groupSelection = useCallback50(
26075
+ const groupSelection = useCallback51(
25608
26076
  async (members) => {
25609
26077
  const pid = projectIdRef.current;
25610
26078
  if (!pid || members.length === 0) return null;
@@ -25633,7 +26101,7 @@ function useGroupCommits(params) {
25633
26101
  },
25634
26102
  [activeCompPath, projectIdRef, showToast, params]
25635
26103
  );
25636
- const ungroupSelection = useCallback50(
26104
+ const ungroupSelection = useCallback51(
25637
26105
  async (group) => {
25638
26106
  const pid = projectIdRef.current;
25639
26107
  if (!pid) return;
@@ -25657,7 +26125,7 @@ function useGroupCommits(params) {
25657
26125
  }
25658
26126
 
25659
26127
  // src/hooks/useGsapScriptCommits.ts
25660
- import { useCallback as useCallback56, useMemo as useMemo21, useRef as useRef51 } from "react";
26128
+ import { useCallback as useCallback57, useMemo as useMemo21, useRef as useRef52 } from "react";
25661
26129
  import { findUnsafeMutationValues } from "@hyperframes/core/studio-api/finite-mutation";
25662
26130
 
25663
26131
  // src/hooks/gsapKeyframeCacheHelpers.ts
@@ -26163,7 +26631,7 @@ function createKeyedSerializer() {
26163
26631
  }
26164
26632
 
26165
26633
  // src/hooks/useGsapAnimationOps.ts
26166
- import { useCallback as useCallback51 } from "react";
26634
+ import { useCallback as useCallback52 } from "react";
26167
26635
  function useGsapAnimationOps({
26168
26636
  projectIdRef,
26169
26637
  activeCompPath,
@@ -26173,7 +26641,7 @@ function useGsapAnimationOps({
26173
26641
  sdkSession,
26174
26642
  sdkDeps
26175
26643
  }) {
26176
- const updateGsapMeta = useCallback51(
26644
+ const updateGsapMeta = useCallback52(
26177
26645
  async (selection, animationId, updates) => {
26178
26646
  if (sdkSession && sdkDeps) {
26179
26647
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
@@ -26194,7 +26662,7 @@ function useGsapAnimationOps({
26194
26662
  },
26195
26663
  [commitMutationSafely, activeCompPath, sdkSession, sdkDeps]
26196
26664
  );
26197
- const deleteGsapAnimation = useCallback51(
26665
+ const deleteGsapAnimation = useCallback52(
26198
26666
  async (selection, animationId) => {
26199
26667
  if (sdkSession && sdkDeps) {
26200
26668
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
@@ -26215,7 +26683,7 @@ function useGsapAnimationOps({
26215
26683
  },
26216
26684
  [commitMutationSafely, activeCompPath, sdkSession, sdkDeps]
26217
26685
  );
26218
- const deleteAllForSelector = useCallback51(
26686
+ const deleteAllForSelector = useCallback52(
26219
26687
  async (selection, targetSelector) => {
26220
26688
  if (sdkSession && sdkDeps) {
26221
26689
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
@@ -26236,7 +26704,7 @@ function useGsapAnimationOps({
26236
26704
  },
26237
26705
  [commitMutation, activeCompPath, sdkSession, sdkDeps]
26238
26706
  );
26239
- const addGsapAnimation = useCallback51(
26707
+ const addGsapAnimation = useCallback52(
26240
26708
  // fallow-ignore-next-line complexity
26241
26709
  async (selection, method, _currentTime) => {
26242
26710
  const { selector, autoId } = ensureElementAddressable(selection);
@@ -26298,7 +26766,7 @@ function useGsapAnimationOps({
26298
26766
  },
26299
26767
  [activeCompPath, commitMutation, projectIdRef, showToast, sdkSession, sdkDeps]
26300
26768
  );
26301
- const addWithKeyframes = useCallback51(
26769
+ const addWithKeyframes = useCallback52(
26302
26770
  async (selection, targetSelector, position, duration, keyframes, ease, label = "Add animation with keyframes") => {
26303
26771
  if (sdkSession && sdkDeps) {
26304
26772
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
@@ -26330,7 +26798,7 @@ function useGsapAnimationOps({
26330
26798
  },
26331
26799
  [commitMutation, activeCompPath, sdkSession, sdkDeps]
26332
26800
  );
26333
- const replaceWithKeyframes = useCallback51(
26801
+ const replaceWithKeyframes = useCallback52(
26334
26802
  async (selection, animationId, targetSelector, position, duration, keyframes, ease, label = "Replace animation with keyframes") => {
26335
26803
  if (sdkSession && sdkDeps) {
26336
26804
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
@@ -26375,9 +26843,9 @@ function useGsapAnimationOps({
26375
26843
  }
26376
26844
 
26377
26845
  // src/hooks/useGsapArcPathOps.ts
26378
- import { useCallback as useCallback52 } from "react";
26846
+ import { useCallback as useCallback53 } from "react";
26379
26847
  function useGsapArcPathOps(commitMutationSafely) {
26380
- const setArcPath = useCallback52(
26848
+ const setArcPath = useCallback53(
26381
26849
  (selection, animationId, config) => {
26382
26850
  commitMutationSafely(
26383
26851
  selection,
@@ -26387,7 +26855,7 @@ function useGsapArcPathOps(commitMutationSafely) {
26387
26855
  },
26388
26856
  [commitMutationSafely]
26389
26857
  );
26390
- const updateArcSegment = useCallback52(
26858
+ const updateArcSegment = useCallback53(
26391
26859
  (selection, animationId, segmentIndex, update) => {
26392
26860
  commitMutationSafely(
26393
26861
  selection,
@@ -26397,7 +26865,7 @@ function useGsapArcPathOps(commitMutationSafely) {
26397
26865
  },
26398
26866
  [commitMutationSafely]
26399
26867
  );
26400
- const removeArcPath = useCallback52(
26868
+ const removeArcPath = useCallback53(
26401
26869
  (selection, animationId) => {
26402
26870
  commitMutationSafely(
26403
26871
  selection,
@@ -26411,7 +26879,7 @@ function useGsapArcPathOps(commitMutationSafely) {
26411
26879
  }
26412
26880
 
26413
26881
  // src/hooks/useGsapKeyframeOps.ts
26414
- import { useCallback as useCallback53 } from "react";
26882
+ import { useCallback as useCallback54 } from "react";
26415
26883
 
26416
26884
  // src/utils/optimisticUpdate.ts
26417
26885
  async function executeOptimistic(options) {
@@ -26502,7 +26970,7 @@ function useGsapKeyframeOps({
26502
26970
  sdkSession,
26503
26971
  sdkDeps
26504
26972
  }) {
26505
- const addKeyframe = useCallback53(
26973
+ const addKeyframe = useCallback54(
26506
26974
  (selection, animationId, percentage, property, value) => {
26507
26975
  const sourceFile = selection.sourceFile || activeCompPath || "index.html";
26508
26976
  const mutation = {
@@ -26563,7 +27031,7 @@ function useGsapKeyframeOps({
26563
27031
  },
26564
27032
  [activeCompPath, commitMutation, trackGsapSaveFailure, sdkSession, sdkDeps]
26565
27033
  );
26566
- const addKeyframeBatch = useCallback53(
27034
+ const addKeyframeBatch = useCallback54(
26567
27035
  async (selection, animationId, percentage, properties) => {
26568
27036
  if (sdkSession && sdkDeps) {
26569
27037
  const sourceFile = selection.sourceFile || activeCompPath || "index.html";
@@ -26586,7 +27054,7 @@ function useGsapKeyframeOps({
26586
27054
  },
26587
27055
  [commitMutation, activeCompPath, sdkSession, sdkDeps]
26588
27056
  );
26589
- const removeKeyframe = useCallback53(
27057
+ const removeKeyframe = useCallback54(
26590
27058
  (selection, animationId, percentage) => {
26591
27059
  const sourceFile = selection.sourceFile || activeCompPath || "index.html";
26592
27060
  const mutation = { type: "remove-keyframe", animationId, percentage };
@@ -26627,7 +27095,7 @@ function useGsapKeyframeOps({
26627
27095
  },
26628
27096
  [activeCompPath, commitMutation, trackGsapSaveFailure, sdkSession, sdkDeps]
26629
27097
  );
26630
- const moveKeyframe = useCallback53(
27098
+ const moveKeyframe = useCallback54(
26631
27099
  (selection, animationId, fromPercentage, toPercentage) => {
26632
27100
  const mutation = { type: "move-keyframe", animationId, fromPercentage, toPercentage };
26633
27101
  void commitMutation(selection, mutation, {
@@ -26639,7 +27107,7 @@ function useGsapKeyframeOps({
26639
27107
  },
26640
27108
  [commitMutation, trackGsapSaveFailure]
26641
27109
  );
26642
- const resizeKeyframedTween = useCallback53(
27110
+ const resizeKeyframedTween = useCallback54(
26643
27111
  (selection, animationId, position, duration, pctRemap) => {
26644
27112
  const mutation = {
26645
27113
  type: "resize-keyframed-tween",
@@ -26657,7 +27125,7 @@ function useGsapKeyframeOps({
26657
27125
  },
26658
27126
  [commitMutation, trackGsapSaveFailure]
26659
27127
  );
26660
- const convertToKeyframes = useCallback53(
27128
+ const convertToKeyframes = useCallback54(
26661
27129
  async (selection, animationId, resolvedFromValues, duration) => {
26662
27130
  if (sdkSession && sdkDeps) {
26663
27131
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
@@ -26681,7 +27149,7 @@ function useGsapKeyframeOps({
26681
27149
  },
26682
27150
  [commitMutation, activeCompPath, sdkSession, sdkDeps]
26683
27151
  );
26684
- const removeAllKeyframes = useCallback53(
27152
+ const removeAllKeyframes = useCallback54(
26685
27153
  async (selection, animationId) => {
26686
27154
  const targetPath = selection.sourceFile || activeCompPath || "index.html";
26687
27155
  const elementId = selection.id ?? selection.selector?.match(/^#([\w-]+)/)?.[1] ?? null;
@@ -26704,7 +27172,7 @@ function useGsapKeyframeOps({
26704
27172
  },
26705
27173
  [commitMutationSafely, activeCompPath, sdkSession, sdkDeps]
26706
27174
  );
26707
- const commitKeyframeAtTime = useCallback53(
27175
+ const commitKeyframeAtTime = useCallback54(
26708
27176
  (selection, absoluteTime, animations, properties) => commitKeyframeAtTimeImpl(selection, absoluteTime, animations, properties, commitMutation),
26709
27177
  [commitMutation]
26710
27178
  );
@@ -26721,7 +27189,7 @@ function useGsapKeyframeOps({
26721
27189
  }
26722
27190
 
26723
27191
  // src/hooks/useGsapPropertyDebounce.ts
26724
- import { useCallback as useCallback54, useEffect as useEffect31, useRef as useRef50 } from "react";
27192
+ import { useCallback as useCallback55, useEffect as useEffect31, useRef as useRef51 } from "react";
26725
27193
  import { parseGsapScriptAcorn } from "@hyperframes/core/gsap-parser-acorn";
26726
27194
  var DEBOUNCE_MS = 150;
26727
27195
  function mergeTweenProperties(sdkSession, animationId, edited, kind) {
@@ -26737,11 +27205,11 @@ function mergeTweenProperties(sdkSession, animationId, edited, kind) {
26737
27205
  }
26738
27206
  }
26739
27207
  function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26740
- const pendingPropertyEditRef = useRef50(null);
26741
- const debounceTimerRef = useRef50(null);
26742
- const sdkRef = useRef50(sdk);
27208
+ const pendingPropertyEditRef = useRef51(null);
27209
+ const debounceTimerRef = useRef51(null);
27210
+ const sdkRef = useRef51(sdk);
26743
27211
  sdkRef.current = sdk;
26744
- const flushPendingPropertyEdit = useCallback54(async () => {
27212
+ const flushPendingPropertyEdit = useCallback55(async () => {
26745
27213
  const pending = pendingPropertyEditRef.current;
26746
27214
  if (!pending) return;
26747
27215
  pendingPropertyEditRef.current = null;
@@ -26774,7 +27242,7 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26774
27242
  }
26775
27243
  );
26776
27244
  }, [commitMutationSafely]);
26777
- const updateGsapProperty = useCallback54(
27245
+ const updateGsapProperty = useCallback55(
26778
27246
  (selection, animationId, property, value) => {
26779
27247
  pendingPropertyEditRef.current = { selection, animationId, property, value };
26780
27248
  if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
@@ -26790,7 +27258,7 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26790
27258
  void flushPendingPropertyEdit();
26791
27259
  };
26792
27260
  }, [flushPendingPropertyEdit]);
26793
- const addGsapProperty = useCallback54(
27261
+ const addGsapProperty = useCallback55(
26794
27262
  // fallow-ignore-next-line complexity
26795
27263
  async (selection, animationId, property) => {
26796
27264
  let defaultValue = PROPERTY_DEFAULTS[property] ?? 0;
@@ -26834,7 +27302,7 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26834
27302
  },
26835
27303
  [commitMutationSafely]
26836
27304
  );
26837
- const removeProperty = useCallback54(
27305
+ const removeProperty = useCallback55(
26838
27306
  async (selection, animationId, property, from) => {
26839
27307
  const { sdkSession, sdkDeps, activeCompPath } = sdkRef.current ?? {};
26840
27308
  if (sdkSession && sdkDeps) {
@@ -26870,11 +27338,11 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26870
27338
  },
26871
27339
  [commitMutationSafely]
26872
27340
  );
26873
- const removeGsapProperty = useCallback54(
27341
+ const removeGsapProperty = useCallback55(
26874
27342
  (selection, animationId, property) => removeProperty(selection, animationId, property, false),
26875
27343
  [removeProperty]
26876
27344
  );
26877
- const updateGsapFromProperty = useCallback54(
27345
+ const updateGsapFromProperty = useCallback55(
26878
27346
  async (selection, animationId, property, value) => {
26879
27347
  const { sdkSession, sdkDeps, activeCompPath } = sdkRef.current ?? {};
26880
27348
  if (sdkSession && sdkDeps) {
@@ -26913,7 +27381,7 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26913
27381
  },
26914
27382
  [commitMutationSafely]
26915
27383
  );
26916
- const addGsapFromProperty = useCallback54(
27384
+ const addGsapFromProperty = useCallback55(
26917
27385
  async (selection, animationId, property) => {
26918
27386
  const defaultValue = PROPERTY_DEFAULTS[property] ?? 0;
26919
27387
  const { sdkSession, sdkDeps, activeCompPath } = sdkRef.current ?? {};
@@ -26947,7 +27415,7 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26947
27415
  },
26948
27416
  [commitMutationSafely]
26949
27417
  );
26950
- const removeGsapFromProperty = useCallback54(
27418
+ const removeGsapFromProperty = useCallback55(
26951
27419
  (selection, animationId, property) => removeProperty(selection, animationId, property, true),
26952
27420
  [removeProperty]
26953
27421
  );
@@ -26962,12 +27430,12 @@ function useGsapPropertyDebounce(commitMutationSafely, sdk) {
26962
27430
  }
26963
27431
 
26964
27432
  // src/hooks/useSafeGsapCommitMutation.ts
26965
- import { useCallback as useCallback55 } from "react";
27433
+ import { useCallback as useCallback56 } from "react";
26966
27434
  function getGsapMutationType(mutation) {
26967
27435
  return typeof mutation.type === "string" ? mutation.type : "gsap";
26968
27436
  }
26969
27437
  function useGsapSaveFailureTelemetry(activeCompPath) {
26970
- return useCallback55(
27438
+ return useCallback56(
26971
27439
  (error, selection, mutation, label) => {
26972
27440
  trackStudioSaveFailure({
26973
27441
  source: "gsap_commit",
@@ -26984,7 +27452,7 @@ function useGsapSaveFailureTelemetry(activeCompPath) {
26984
27452
  );
26985
27453
  }
26986
27454
  function useSafeGsapCommitMutation(commitMutation, trackGsapSaveFailure, showToast) {
26987
- return useCallback55(
27455
+ return useCallback56(
26988
27456
  (selection, mutation, options) => (
26989
27457
  // Return the chain so awaiting consumers (gesture commit, enable-keyframes)
26990
27458
  // run their post-actions AFTER the server save settles, not immediately.
@@ -27043,8 +27511,8 @@ function applyPreviewSync(iframe, result, options, reloadPreview) {
27043
27511
  }
27044
27512
  }
27045
27513
  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) => {
27514
+ const serializerRef = useRef52(createKeyedSerializer());
27515
+ const runCommit = useCallback57(async (selection, mutation, options) => {
27048
27516
  const pid = projectIdRef.current;
27049
27517
  if (!pid) return;
27050
27518
  const unsafeFields = findUnsafeMutationValues(mutation);
@@ -27075,7 +27543,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
27075
27543
  applyPreviewSync(previewIframeRef.current, result, options, reloadPreview);
27076
27544
  onCacheInvalidate();
27077
27545
  }, [projectIdRef, activeCompPath, previewIframeRef, editHistory, domEditSaveTimestampRef, reloadPreview, onCacheInvalidate, onFileContentChanged, showToast, forceReloadSdkSession]);
27078
- const commitMutation = useCallback56(
27546
+ const commitMutation = useCallback57(
27079
27547
  (selection, mutation, options) => {
27080
27548
  const file = selection.sourceFile || activeCompPath || "index.html";
27081
27549
  const key = options.serializeKey ?? `gsap-file:${file}`;
@@ -27085,7 +27553,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
27085
27553
  );
27086
27554
  const trackGsapSaveFailure = useGsapSaveFailureTelemetry(activeCompPath);
27087
27555
  const commitMutationSafely = useSafeGsapCommitMutation(commitMutation, trackGsapSaveFailure, showToast);
27088
- const sdkRefresh = useCallback56(
27556
+ const sdkRefresh = useCallback57(
27089
27557
  (after) => {
27090
27558
  const script = extractGsapScriptText(after);
27091
27559
  if (script) {
@@ -27097,11 +27565,11 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
27097
27565
  },
27098
27566
  [previewIframeRef, reloadPreview, onCacheInvalidate]
27099
27567
  );
27100
- const serializeByFile = useCallback56(
27568
+ const serializeByFile = useCallback57(
27101
27569
  (key, task) => serializerRef.current(key, task),
27102
27570
  []
27103
27571
  );
27104
- const readProjectFileContent = useCallback56(
27572
+ const readProjectFileContent = useCallback57(
27105
27573
  async (path) => {
27106
27574
  const pid = projectIdRef.current;
27107
27575
  if (!pid) throw new Error("No active project");
@@ -27162,7 +27630,7 @@ function useGsapScriptCommits({ projectIdRef, activeCompPath, previewIframeRef,
27162
27630
  }
27163
27631
 
27164
27632
  // src/hooks/useGsapTweenCache.ts
27165
- import { useEffect as useEffect32, useMemo as useMemo22, useRef as useRef52, useState as useState47, useCallback as useCallback57 } from "react";
27633
+ import { useEffect as useEffect32, useMemo as useMemo22, useRef as useRef53, useState as useState47, useCallback as useCallback58 } from "react";
27166
27634
  import { isStudioHoldSet } from "@hyperframes/core/gsap-parser";
27167
27635
 
27168
27636
  // src/hooks/gsapRuntimeReaders.ts
@@ -28556,8 +29024,8 @@ function useGsapAnimationsForElement(projectId, sourceFile, target, version, ifr
28556
29024
  const [allAnimations, setAllAnimations] = useState47([]);
28557
29025
  const [multipleTimelines, setMultipleTimelines] = useState47(false);
28558
29026
  const [unsupportedTimelinePattern, setUnsupportedTimelinePattern] = useState47(false);
28559
- const lastFetchKeyRef = useRef52("");
28560
- const retryTimerRef = useRef52(null);
29027
+ const lastFetchKeyRef = useRef53("");
29028
+ const retryTimerRef = useRef53(null);
28561
29029
  const domClipChildrenKey = usePlayerStore(
28562
29030
  (s) => s.domClipChildren.map((c) => `${c.id}<${c.hostId}`).join("|")
28563
29031
  );
@@ -28735,7 +29203,7 @@ function useGsapAnimationsForElement(projectId, sourceFile, target, version, ifr
28735
29203
  }
28736
29204
  function useGsapCacheVersion() {
28737
29205
  const [version, setVersion] = useState47(0);
28738
- const bump = useCallback57(() => setVersion((v) => v + 1), []);
29206
+ const bump = useCallback58(() => setVersion((v) => v + 1), []);
28739
29207
  return { version, bump };
28740
29208
  }
28741
29209
  function usePopulateKeyframeCacheForFile(projectId, sourceFile, version, iframeRef) {
@@ -28743,9 +29211,9 @@ function usePopulateKeyframeCacheForFile(projectId, sourceFile, version, iframeR
28743
29211
  const domClipChildrenKey = usePlayerStore(
28744
29212
  (s) => s.domClipChildren.map((c) => `${c.id}<${c.hostId}`).join("|")
28745
29213
  );
28746
- const lastFetchKeyRef = useRef52("");
28747
- const runtimeScanDoneRef = useRef52("");
28748
- const astFetchDoneRef = useRef52("");
29214
+ const lastFetchKeyRef = useRef53("");
29215
+ const runtimeScanDoneRef = useRef53("");
29216
+ const astFetchDoneRef = useRef53("");
28749
29217
  useEffect32(() => {
28750
29218
  const fetchKey = `kf-cache:${projectId}:${sourceFile}:${version}:${elementCount}:${domClipChildrenKey}`;
28751
29219
  if (fetchKey === lastFetchKeyRef.current) return;
@@ -28848,10 +29316,10 @@ function usePopulateKeyframeCacheForFile(projectId, sourceFile, version, iframeR
28848
29316
  }
28849
29317
 
28850
29318
  // src/hooks/useDomEditWiring.ts
28851
- import { useCallback as useCallback61, useEffect as useEffect34, useRef as useRef55 } from "react";
29319
+ import { useCallback as useCallback62, useEffect as useEffect34, useRef as useRef56 } from "react";
28852
29320
 
28853
29321
  // src/hooks/useDomEditPreviewSync.ts
28854
- import { useEffect as useEffect33, useRef as useRef53 } from "react";
29322
+ import { useEffect as useEffect33, useRef as useRef54 } from "react";
28855
29323
  function useDomEditPreviewSync({
28856
29324
  previewIframe,
28857
29325
  activeCompPath,
@@ -28917,7 +29385,7 @@ function useDomEditPreviewSync({
28917
29385
  applyStudioManualEditsToPreviewRef,
28918
29386
  gsapCacheVersion
28919
29387
  ]);
28920
- const openSourceRef = useRef53(openSourceForSelection);
29388
+ const openSourceRef = useRef54(openSourceForSelection);
28921
29389
  openSourceRef.current = openSourceForSelection;
28922
29390
  useEffect33(
28923
29391
  // fallow-ignore-next-line complexity
@@ -28936,7 +29404,7 @@ function useDomEditPreviewSync({
28936
29404
  }
28937
29405
 
28938
29406
  // src/hooks/useGsapAnimationFetchFallback.ts
28939
- import { useCallback as useCallback58 } from "react";
29407
+ import { useCallback as useCallback59 } from "react";
28940
29408
  var COLD_PARSE_RETRIES = 5;
28941
29409
  var COLD_PARSE_DELAY_MS = 120;
28942
29410
  var FETCH_ERROR_RETRIES = 1;
@@ -28948,7 +29416,7 @@ function selectElementAnimationsOrRetry(parsed, target) {
28948
29416
  return { kind: "resolved", animations: getAnimationsForElement(parsed.animations, target) };
28949
29417
  }
28950
29418
  function useGsapAnimationFetchFallback(projectId, gsapSourceFile) {
28951
- return useCallback58(
29419
+ return useCallback59(
28952
29420
  (selection) => async () => {
28953
29421
  if (!projectId) return [];
28954
29422
  const target = { id: selection.id ?? null, selector: selection.selector ?? null };
@@ -28974,9 +29442,9 @@ function useGsapAnimationFetchFallback(projectId, gsapSourceFile) {
28974
29442
  }
28975
29443
 
28976
29444
  // src/hooks/useGsapInteractionFailureTelemetry.ts
28977
- import { useCallback as useCallback59 } from "react";
29445
+ import { useCallback as useCallback60 } from "react";
28978
29446
  function useGsapInteractionFailureTelemetry(activeCompPath, showToast) {
28979
- return useCallback59(
29447
+ return useCallback60(
28980
29448
  (error, selection, mutationType, label) => {
28981
29449
  trackStudioSaveFailure({
28982
29450
  source: "gsap_commit",
@@ -28995,7 +29463,7 @@ function useGsapInteractionFailureTelemetry(activeCompPath, showToast) {
28995
29463
  }
28996
29464
 
28997
29465
  // src/hooks/useGsapSelectionHandlers.ts
28998
- import { useCallback as useCallback60, useRef as useRef54 } from "react";
29466
+ import { useCallback as useCallback61, useRef as useRef55 } from "react";
28999
29467
  function useGsapSelectionHandlers({
29000
29468
  domEditSelection,
29001
29469
  updateGsapProperty,
@@ -29018,9 +29486,9 @@ function useGsapSelectionHandlers({
29018
29486
  handleDomManualEditsReset,
29019
29487
  selectedGsapAnimations
29020
29488
  }) {
29021
- const lastSelectionRef = useRef54(null);
29489
+ const lastSelectionRef = useRef55(null);
29022
29490
  if (domEditSelection) lastSelectionRef.current = domEditSelection;
29023
- const trackGsapHandlerFailure = useCallback60(
29491
+ const trackGsapHandlerFailure = useCallback61(
29024
29492
  (error, selection, mutationType, label) => {
29025
29493
  trackStudioSaveFailure({
29026
29494
  source: "gsap_commit",
@@ -29035,14 +29503,14 @@ function useGsapSelectionHandlers({
29035
29503
  },
29036
29504
  []
29037
29505
  );
29038
- const handleGsapUpdateProperty = useCallback60(
29506
+ const handleGsapUpdateProperty = useCallback61(
29039
29507
  (animId, prop, value) => {
29040
29508
  if (!domEditSelection) return;
29041
29509
  updateGsapProperty(domEditSelection, animId, prop, value);
29042
29510
  },
29043
29511
  [domEditSelection, updateGsapProperty]
29044
29512
  );
29045
- const handleGsapUpdateMeta = useCallback60(
29513
+ const handleGsapUpdateMeta = useCallback61(
29046
29514
  (animId, updates, selectionOverride) => {
29047
29515
  const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
29048
29516
  if (!sel) return;
@@ -29050,7 +29518,7 @@ function useGsapSelectionHandlers({
29050
29518
  },
29051
29519
  [domEditSelection, updateGsapMeta]
29052
29520
  );
29053
- const handleGsapDeleteAnimation = useCallback60(
29521
+ const handleGsapDeleteAnimation = useCallback61(
29054
29522
  (animId) => {
29055
29523
  const sel = domEditSelection ?? lastSelectionRef.current;
29056
29524
  if (!sel) return;
@@ -29058,7 +29526,7 @@ function useGsapSelectionHandlers({
29058
29526
  },
29059
29527
  [domEditSelection, deleteGsapAnimation]
29060
29528
  );
29061
- const handleGsapDeleteAllForElement = useCallback60(
29529
+ const handleGsapDeleteAllForElement = useCallback61(
29062
29530
  (targetSelector) => {
29063
29531
  const sel = domEditSelection ?? lastSelectionRef.current;
29064
29532
  if (!sel) return;
@@ -29067,7 +29535,7 @@ function useGsapSelectionHandlers({
29067
29535
  },
29068
29536
  [domEditSelection, deleteAllForSelector]
29069
29537
  );
29070
- const handleGsapAddAnimation = useCallback60(
29538
+ const handleGsapAddAnimation = useCallback61(
29071
29539
  (method) => {
29072
29540
  if (!domEditSelection) return;
29073
29541
  void addGsapAnimation(domEditSelection, method, usePlayerStore.getState().currentTime).catch(
@@ -29081,42 +29549,42 @@ function useGsapSelectionHandlers({
29081
29549
  },
29082
29550
  [domEditSelection, addGsapAnimation, handleDomManualEditsReset, trackGsapHandlerFailure]
29083
29551
  );
29084
- const handleGsapAddProperty = useCallback60(
29552
+ const handleGsapAddProperty = useCallback61(
29085
29553
  (animId, prop) => {
29086
29554
  if (!domEditSelection) return;
29087
29555
  addGsapProperty(domEditSelection, animId, prop);
29088
29556
  },
29089
29557
  [domEditSelection, addGsapProperty]
29090
29558
  );
29091
- const handleGsapRemoveProperty = useCallback60(
29559
+ const handleGsapRemoveProperty = useCallback61(
29092
29560
  (animId, prop) => {
29093
29561
  if (!domEditSelection) return;
29094
29562
  removeGsapProperty(domEditSelection, animId, prop);
29095
29563
  },
29096
29564
  [domEditSelection, removeGsapProperty]
29097
29565
  );
29098
- const handleGsapUpdateFromProperty = useCallback60(
29566
+ const handleGsapUpdateFromProperty = useCallback61(
29099
29567
  (animId, prop, value) => {
29100
29568
  if (!domEditSelection) return;
29101
29569
  updateGsapFromProperty(domEditSelection, animId, prop, value);
29102
29570
  },
29103
29571
  [domEditSelection, updateGsapFromProperty]
29104
29572
  );
29105
- const handleGsapAddFromProperty = useCallback60(
29573
+ const handleGsapAddFromProperty = useCallback61(
29106
29574
  (animId, prop) => {
29107
29575
  if (!domEditSelection) return;
29108
29576
  addGsapFromProperty(domEditSelection, animId, prop);
29109
29577
  },
29110
29578
  [domEditSelection, addGsapFromProperty]
29111
29579
  );
29112
- const handleGsapRemoveFromProperty = useCallback60(
29580
+ const handleGsapRemoveFromProperty = useCallback61(
29113
29581
  (animId, prop) => {
29114
29582
  if (!domEditSelection) return;
29115
29583
  removeGsapFromProperty(domEditSelection, animId, prop);
29116
29584
  },
29117
29585
  [domEditSelection, removeGsapFromProperty]
29118
29586
  );
29119
- const handleGsapAddKeyframe = useCallback60(
29587
+ const handleGsapAddKeyframe = useCallback61(
29120
29588
  (animId, percentage, property, value, selectionOverride) => {
29121
29589
  const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
29122
29590
  if (!sel) return;
@@ -29125,7 +29593,7 @@ function useGsapSelectionHandlers({
29125
29593
  },
29126
29594
  [domEditSelection, addKeyframe]
29127
29595
  );
29128
- const handleGsapAddKeyframeBatch = useCallback60(
29596
+ const handleGsapAddKeyframeBatch = useCallback61(
29129
29597
  (animId, percentage, properties) => {
29130
29598
  if (!domEditSelection) return Promise.resolve();
29131
29599
  return addKeyframeBatch(domEditSelection, animId, percentage, properties).catch((error) => {
@@ -29134,7 +29602,7 @@ function useGsapSelectionHandlers({
29134
29602
  },
29135
29603
  [domEditSelection, addKeyframeBatch, trackGsapHandlerFailure]
29136
29604
  );
29137
- const handleGsapRemoveKeyframe = useCallback60(
29605
+ const handleGsapRemoveKeyframe = useCallback61(
29138
29606
  (animId, percentage, selectionOverride) => {
29139
29607
  const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
29140
29608
  if (!sel) return;
@@ -29143,7 +29611,7 @@ function useGsapSelectionHandlers({
29143
29611
  },
29144
29612
  [domEditSelection, removeKeyframe]
29145
29613
  );
29146
- const handleGsapMoveKeyframeToPlayhead = useCallback60(
29614
+ const handleGsapMoveKeyframeToPlayhead = useCallback61(
29147
29615
  (animId, fromPercentage, selectionOverride) => {
29148
29616
  const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
29149
29617
  if (!sel) return;
@@ -29154,7 +29622,7 @@ function useGsapSelectionHandlers({
29154
29622
  },
29155
29623
  [domEditSelection, selectedGsapAnimations, moveKeyframe]
29156
29624
  );
29157
- const handleGsapMoveKeyframe = useCallback60(
29625
+ const handleGsapMoveKeyframe = useCallback61(
29158
29626
  (animId, fromPercentage, toPercentage, selectionOverride) => {
29159
29627
  const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
29160
29628
  if (!sel) return;
@@ -29163,7 +29631,7 @@ function useGsapSelectionHandlers({
29163
29631
  },
29164
29632
  [domEditSelection, moveKeyframe]
29165
29633
  );
29166
- const handleGsapResizeKeyframedTween = useCallback60(
29634
+ const handleGsapResizeKeyframedTween = useCallback61(
29167
29635
  (animId, position, duration, pctRemap, selectionOverride) => {
29168
29636
  const sel = selectionOverride ?? domEditSelection ?? lastSelectionRef.current;
29169
29637
  if (!sel) return;
@@ -29172,7 +29640,7 @@ function useGsapSelectionHandlers({
29172
29640
  },
29173
29641
  [domEditSelection, resizeKeyframedTween]
29174
29642
  );
29175
- const handleGsapConvertToKeyframes = useCallback60(
29643
+ const handleGsapConvertToKeyframes = useCallback61(
29176
29644
  (animId, resolvedFromValues, duration) => {
29177
29645
  if (!domEditSelection) return Promise.resolve();
29178
29646
  return convertToKeyframes(domEditSelection, animId, resolvedFromValues, duration).catch(
@@ -29188,14 +29656,14 @@ function useGsapSelectionHandlers({
29188
29656
  },
29189
29657
  [domEditSelection, convertToKeyframes, trackGsapHandlerFailure]
29190
29658
  );
29191
- const handleGsapRemoveAllKeyframes = useCallback60(
29659
+ const handleGsapRemoveAllKeyframes = useCallback61(
29192
29660
  (animId) => {
29193
29661
  if (!domEditSelection) return;
29194
29662
  removeAllKeyframes(domEditSelection, animId);
29195
29663
  },
29196
29664
  [domEditSelection, removeAllKeyframes]
29197
29665
  );
29198
- const handleResetSelectedElementKeyframes = useCallback60(() => {
29666
+ const handleResetSelectedElementKeyframes = useCallback61(() => {
29199
29667
  if (!domEditSelection) return false;
29200
29668
  const withKeyframes = selectedGsapAnimations.find((a) => a.keyframes);
29201
29669
  if (!withKeyframes) return false;
@@ -29266,7 +29734,7 @@ function useDomEditWiring({
29266
29734
  removeAllKeyframes,
29267
29735
  handleDomManualEditsReset
29268
29736
  }) {
29269
- const onClickToSource = useCallback61(
29737
+ const onClickToSource = useCallback62(
29270
29738
  (selection) => {
29271
29739
  if (!openSourceForSelection || !selectSidebarTab) return;
29272
29740
  if (!selection.sourceFile) return;
@@ -29288,7 +29756,7 @@ function useDomEditWiring({
29288
29756
  const key = matchKey ? matchKey.key ?? matchKey.id : null;
29289
29757
  if (key && key !== selectedElementId) setSelectedElementId(key);
29290
29758
  }, [domEditSelection?.id]);
29291
- const prevRefreshKeyRef = useRef55(refreshKey);
29759
+ const prevRefreshKeyRef = useRef56(refreshKey);
29292
29760
  useEffect34(() => {
29293
29761
  if (refreshKey !== prevRefreshKeyRef.current) {
29294
29762
  prevRefreshKeyRef.current = refreshKey;
@@ -29366,10 +29834,10 @@ function useDomEditWiring({
29366
29834
  }
29367
29835
 
29368
29836
  // src/hooks/useGsapAwareEditing.ts
29369
- import { useCallback as useCallback63 } from "react";
29837
+ import { useCallback as useCallback64 } from "react";
29370
29838
 
29371
29839
  // src/hooks/useAnimatedPropertyCommit.ts
29372
- import { useCallback as useCallback62 } from "react";
29840
+ import { useCallback as useCallback63 } from "react";
29373
29841
  import { classifyPropertyGroup as classifyPropertyGroup3 } from "@hyperframes/core/gsap-parser";
29374
29842
  function pickBestAnimation(animations, selector, property) {
29375
29843
  const targetGroup = property ? classifyPropertyGroup3(property) : void 0;
@@ -29539,7 +30007,7 @@ async function commitKeyframeProps(selection, anim, props, propEntries, primaryP
29539
30007
  }
29540
30008
  function useAnimatedPropertyCommit(deps) {
29541
30009
  const { selectedGsapAnimations, gsapCommitMutation, previewIframeRef, bumpGsapCache } = deps;
29542
- const commitAnimatedProperties = useCallback62(
30010
+ const commitAnimatedProperties = useCallback63(
29543
30011
  async (selection, props) => {
29544
30012
  if (!gsapCommitMutation) return;
29545
30013
  const propEntries = Object.entries(props);
@@ -29638,7 +30106,7 @@ function useAnimatedPropertyCommit(deps) {
29638
30106
  },
29639
30107
  [selectedGsapAnimations, gsapCommitMutation, previewIframeRef, bumpGsapCache]
29640
30108
  );
29641
- const commitAnimatedProperty = useCallback62(
30109
+ const commitAnimatedProperty = useCallback63(
29642
30110
  (selection, property, value) => commitAnimatedProperties(selection, { [property]: value }),
29643
30111
  [commitAnimatedProperties]
29644
30112
  );
@@ -29661,7 +30129,7 @@ function useGsapAwareEditing({
29661
30129
  setArcPath,
29662
30130
  updateArcSegment
29663
30131
  }) {
29664
- const handleGsapAwarePathOffsetCommit = useCallback63(
30132
+ const handleGsapAwarePathOffsetCommit = useCallback64(
29665
30133
  async (selection, next, modifiers) => {
29666
30134
  if (gsapCommitMutation) {
29667
30135
  try {
@@ -29688,7 +30156,7 @@ function useGsapAwareEditing({
29688
30156
  trackGsapInteractionFailure
29689
30157
  ]
29690
30158
  );
29691
- const handleGsapAwareGroupPathOffsetCommit = useCallback63(
30159
+ const handleGsapAwareGroupPathOffsetCommit = useCallback64(
29692
30160
  async (updates) => {
29693
30161
  if (!gsapCommitMutation) return;
29694
30162
  for (const { selection, next } of updates) {
@@ -29709,7 +30177,7 @@ function useGsapAwareEditing({
29709
30177
  },
29710
30178
  [gsapCommitMutation, previewIframeRef, makeFetchFallback, trackGsapInteractionFailure]
29711
30179
  );
29712
- const handleGsapAwareBoxSizeCommit = useCallback63(
30180
+ const handleGsapAwareBoxSizeCommit = useCallback64(
29713
30181
  async (selection, next) => {
29714
30182
  if (gsapCommitMutation) {
29715
30183
  try {
@@ -29738,7 +30206,7 @@ function useGsapAwareEditing({
29738
30206
  trackGsapInteractionFailure
29739
30207
  ]
29740
30208
  );
29741
- const handleGsapAwareRotationCommit = useCallback63(
30209
+ const handleGsapAwareRotationCommit = useCallback64(
29742
30210
  async (selection, next) => {
29743
30211
  if (gsapCommitMutation) {
29744
30212
  try {
@@ -29772,21 +30240,21 @@ function useGsapAwareEditing({
29772
30240
  previewIframeRef,
29773
30241
  bumpGsapCache
29774
30242
  });
29775
- const handleSetArcPath = useCallback63(
30243
+ const handleSetArcPath = useCallback64(
29776
30244
  (animId, config) => {
29777
30245
  if (!domEditSelection) return;
29778
30246
  setArcPath(domEditSelection, animId, config);
29779
30247
  },
29780
30248
  [domEditSelection, setArcPath]
29781
30249
  );
29782
- const handleUpdateArcSegment = useCallback63(
30250
+ const handleUpdateArcSegment = useCallback64(
29783
30251
  (animId, segmentIndex, update) => {
29784
30252
  if (!domEditSelection) return;
29785
30253
  updateArcSegment(domEditSelection, animId, segmentIndex, update);
29786
30254
  },
29787
30255
  [domEditSelection, updateArcSegment]
29788
30256
  );
29789
- const noopCommit = useCallback63(async () => {
30257
+ const noopCommit = useCallback64(async () => {
29790
30258
  }, []);
29791
30259
  const trackGsapSaveFailure = useGsapSaveFailureTelemetry(null);
29792
30260
  const safeGsapCommit = useSafeGsapCommitMutation(
@@ -29794,14 +30262,14 @@ function useGsapAwareEditing({
29794
30262
  trackGsapSaveFailure,
29795
30263
  showToast
29796
30264
  );
29797
- const commitMutation = useCallback63(
30265
+ const commitMutation = useCallback64(
29798
30266
  async (mutation, options) => {
29799
30267
  if (!domEditSelection) return;
29800
30268
  await safeGsapCommit(domEditSelection, mutation, options);
29801
30269
  },
29802
30270
  [domEditSelection, safeGsapCommit]
29803
30271
  );
29804
- const handleUnroll = useCallback63(() => {
30272
+ const handleUnroll = useCallback64(() => {
29805
30273
  void commitMutation(
29806
30274
  { type: "unroll-timeline" },
29807
30275
  { label: "Unroll to literal tweens", softReload: true }
@@ -29822,7 +30290,7 @@ function useGsapAwareEditing({
29822
30290
  }
29823
30291
 
29824
30292
  // src/hooks/useStudioSelectionPublisher.ts
29825
- import { useEffect as useEffect35, useRef as useRef56 } from "react";
30293
+ import { useEffect as useEffect35, useRef as useRef57 } from "react";
29826
30294
 
29827
30295
  // src/utils/studioSelectionSnapshot.ts
29828
30296
  function round32(value) {
@@ -29912,8 +30380,8 @@ function useStudioSelectionPublisher({
29912
30380
  previewDocumentVersion,
29913
30381
  refreshDomEditSelectionFromPreview
29914
30382
  }) {
29915
- const lastSelectionRefreshKeyRef = useRef56(refreshKey);
29916
- const pendingSelectionRefreshKeyRef = useRef56(null);
30383
+ const lastSelectionRefreshKeyRef = useRef57(refreshKey);
30384
+ const pendingSelectionRefreshKeyRef = useRef57(null);
29917
30385
  useEffect35(() => {
29918
30386
  if (!projectId) return;
29919
30387
  const selection = domEditSelection?.element.isConnected ? buildStudioSelectionSnapshot({
@@ -30164,7 +30632,7 @@ function useDomEditSession({
30164
30632
  clearDomSelection,
30165
30633
  forceReloadSdkSession
30166
30634
  });
30167
- const handleGroupSelection = useCallback64(() => {
30635
+ const handleGroupSelection = useCallback65(() => {
30168
30636
  const group = domEditGroupSelectionsRef.current;
30169
30637
  const single = domEditSelectionRef.current;
30170
30638
  const members = group.length > 0 ? group : single ? [single] : [];
@@ -30175,7 +30643,7 @@ function useDomEditSession({
30175
30643
  trackStudioEvent("group", { action: "create", count: members.length });
30176
30644
  void groupSelection(members);
30177
30645
  }, [domEditGroupSelectionsRef, domEditSelectionRef, groupSelection, showToast]);
30178
- const handleUngroupSelection = useCallback64(() => {
30646
+ const handleUngroupSelection = useCallback65(() => {
30179
30647
  const sel = domEditSelectionRef.current;
30180
30648
  if (!sel?.element.hasAttribute("data-hf-group")) {
30181
30649
  showToast("Select a group to ungroup", "info");
@@ -30295,7 +30763,7 @@ function useDomEditSession({
30295
30763
  setArcPath,
30296
30764
  updateArcSegment
30297
30765
  });
30298
- const handleUpdateKeyframeEase = useCallback64(
30766
+ const handleUpdateKeyframeEase = useCallback65(
30299
30767
  (animationId, percentage, ease) => {
30300
30768
  const sel = domEditSelectionRef.current;
30301
30769
  if (!sel) return;
@@ -30313,7 +30781,7 @@ function useDomEditSession({
30313
30781
  },
30314
30782
  [gsapCommitMutation, domEditSelectionRef]
30315
30783
  );
30316
- const handleSetAllKeyframeEases = useCallback64(
30784
+ const handleSetAllKeyframeEases = useCallback65(
30317
30785
  (animationId, ease) => {
30318
30786
  const sel = domEditSelectionRef.current;
30319
30787
  if (!sel) return;
@@ -30415,7 +30883,7 @@ function useDomEditSession({
30415
30883
  }
30416
30884
 
30417
30885
  // src/hooks/useSdkSession.ts
30418
- import { useState as useState48, useEffect as useEffect36, useCallback as useCallback65 } from "react";
30886
+ import { useState as useState48, useEffect as useEffect36, useCallback as useCallback66 } from "react";
30419
30887
  import { openComposition } from "@hyperframes/sdk";
30420
30888
  async function readProjectFileOptional(projectId, path) {
30421
30889
  if (path.includes("\0") || path.includes("..")) return void 0;
@@ -30494,7 +30962,7 @@ function useSdkSession(projectId, activeCompPath, domEditSaveTimestampRef) {
30494
30962
  compRef.current?.dispose();
30495
30963
  };
30496
30964
  }, [projectId, activeCompPath, reloadToken]);
30497
- const forceReload = useCallback65(() => setReloadToken((t) => t + 1), []);
30965
+ const forceReload = useCallback66(() => setReloadToken((t) => t + 1), []);
30498
30966
  return { session, forceReload };
30499
30967
  }
30500
30968
 
@@ -30512,7 +30980,7 @@ function useSdkSelectionSync(session, domEditSelection, domEditGroupSelections)
30512
30980
  }
30513
30981
 
30514
30982
  // src/hooks/useBlockHandlers.ts
30515
- import { useCallback as useCallback66, useMemo as useMemo23, useState as useState49 } from "react";
30983
+ import { useCallback as useCallback67, useMemo as useMemo23, useState as useState49 } from "react";
30516
30984
 
30517
30985
  // src/utils/blockInstaller.ts
30518
30986
  function getMaxZIndexFromIframe(iframe) {
@@ -30680,7 +31148,7 @@ function useBlockHandlers({
30680
31148
  blockCtxDeps.showToast
30681
31149
  ]
30682
31150
  );
30683
- const handleAddBlock = useCallback66(
31151
+ const handleAddBlock = useCallback67(
30684
31152
  (blockName) => {
30685
31153
  if (!projectId) return;
30686
31154
  void (async () => {
@@ -30706,7 +31174,7 @@ function useBlockHandlers({
30706
31174
  },
30707
31175
  [projectId, blockCtx, previewIframeRef, setRightCollapsed, setRightPanelTab]
30708
31176
  );
30709
- const handleTimelineBlockDrop = useCallback66(
31177
+ const handleTimelineBlockDrop = useCallback67(
30710
31178
  (blockName, placement) => {
30711
31179
  if (!projectId) return;
30712
31180
  void addBlockToProject({
@@ -30720,7 +31188,7 @@ function useBlockHandlers({
30720
31188
  },
30721
31189
  [projectId, blockCtx, previewIframeRef]
30722
31190
  );
30723
- const handlePreviewBlockDrop = useCallback66(
31191
+ const handlePreviewBlockDrop = useCallback67(
30724
31192
  (blockName, position) => {
30725
31193
  if (!projectId) return;
30726
31194
  void addBlockToProject({
@@ -30744,7 +31212,7 @@ function useBlockHandlers({
30744
31212
  }
30745
31213
 
30746
31214
  // src/hooks/useAppHotkeys.ts
30747
- import { useCallback as useCallback67, useEffect as useEffect38, useRef as useRef57 } from "react";
31215
+ import { useCallback as useCallback68, useEffect as useEffect38, useRef as useRef58 } from "react";
30748
31216
  function iframeContentWindow(iframe) {
30749
31217
  try {
30750
31218
  return iframe?.contentWindow ?? null;
@@ -30951,9 +31419,9 @@ function useAppHotkeys({
30951
31419
  activeCompPath,
30952
31420
  forceReloadSdkSession
30953
31421
  }) {
30954
- const previewHotkeyWindowRef = useRef57(null);
30955
- const previewHistoryCleanupRef = useRef57(null);
30956
- const handleTimelineToggleHotkey = useCallback67(
31422
+ const previewHotkeyWindowRef = useRef58(null);
31423
+ const previewHistoryCleanupRef = useRef58(null);
31424
+ const handleTimelineToggleHotkey = useCallback68(
30957
31425
  (event) => {
30958
31426
  if (!shouldHandleTimelineToggleHotkey(event)) return;
30959
31427
  event.preventDefault();
@@ -30961,18 +31429,18 @@ function useAppHotkeys({
30961
31429
  },
30962
31430
  [toggleTimelineVisibility]
30963
31431
  );
30964
- const readHistoryFile = useCallback67(
31432
+ const readHistoryFile = useCallback68(
30965
31433
  (path) => path === STUDIO_MOTION_PATH ? readOptionalProjectFile(path) : readProjectFile(path),
30966
31434
  [readOptionalProjectFile, readProjectFile]
30967
31435
  );
30968
- const writeHistoryFile = useCallback67(
31436
+ const writeHistoryFile = useCallback68(
30969
31437
  async (path, content) => {
30970
31438
  domEditSaveTimestampRef.current = Date.now();
30971
31439
  await writeProjectFile(path, content);
30972
31440
  },
30973
31441
  [domEditSaveTimestampRef, writeProjectFile]
30974
31442
  );
30975
- const applyHistory = useCallback67(
31443
+ const applyHistory = useCallback68(
30976
31444
  async (direction) => {
30977
31445
  if (tryApplyBeatHistory(direction, editHistory.state, showToast)) return;
30978
31446
  await waitForPendingDomEditSaves();
@@ -31008,9 +31476,9 @@ function useAppHotkeys({
31008
31476
  forceReloadSdkSession
31009
31477
  ]
31010
31478
  );
31011
- const handleUndo = useCallback67(() => applyHistory("undo"), [applyHistory]);
31012
- const handleRedo = useCallback67(() => applyHistory("redo"), [applyHistory]);
31013
- const cbRef = useRef57(null);
31479
+ const handleUndo = useCallback68(() => applyHistory("undo"), [applyHistory]);
31480
+ const handleRedo = useCallback68(() => applyHistory("redo"), [applyHistory]);
31481
+ const cbRef = useRef58(null);
31014
31482
  cbRef.current = {
31015
31483
  toggleTimelineVisibility,
31016
31484
  handleTimelineElementDelete,
@@ -31030,7 +31498,7 @@ function useAppHotkeys({
31030
31498
  domEditSelectionRef,
31031
31499
  showToast
31032
31500
  };
31033
- const handleAppKeyDown = useCallback67((event) => {
31501
+ const handleAppKeyDown = useCallback68((event) => {
31034
31502
  const cb = cbRef.current;
31035
31503
  if (shouldHandleTimelineToggleHotkey(event)) {
31036
31504
  event.preventDefault();
@@ -31048,7 +31516,7 @@ function useAppHotkeys({
31048
31516
  window.addEventListener("keydown", handleAppKeyDown, true);
31049
31517
  return () => window.removeEventListener("keydown", handleAppKeyDown, true);
31050
31518
  }, [handleAppKeyDown]);
31051
- const syncPreviewTimelineHotkey = useCallback67(
31519
+ const syncPreviewTimelineHotkey = useCallback68(
31052
31520
  (iframe) => {
31053
31521
  const nextWindow = iframeContentWindow(iframe);
31054
31522
  if (previewHotkeyWindowRef.current === nextWindow) return;
@@ -31073,7 +31541,7 @@ function useAppHotkeys({
31073
31541
  },
31074
31542
  [handleAppKeyDown]
31075
31543
  );
31076
- const handleHistoryHotkey = useCallback67((event) => {
31544
+ const handleHistoryHotkey = useCallback68((event) => {
31077
31545
  if (!(event.metaKey || event.ctrlKey) || shouldIgnoreHistoryShortcut(event.target)) return;
31078
31546
  handleUndoRedoKey(
31079
31547
  event,
@@ -31081,7 +31549,7 @@ function useAppHotkeys({
31081
31549
  () => void cbRef.current.handleRedo()
31082
31550
  );
31083
31551
  }, []);
31084
- const syncPreviewHistoryHotkey = useCallback67(
31552
+ const syncPreviewHistoryHotkey = useCallback68(
31085
31553
  (iframe) => {
31086
31554
  previewHistoryCleanupRef.current?.();
31087
31555
  previewHistoryCleanupRef.current = null;
@@ -31120,7 +31588,7 @@ function useAppHotkeys({
31120
31588
  }
31121
31589
 
31122
31590
  // src/hooks/useClipboard.ts
31123
- import { useCallback as useCallback68, useRef as useRef58 } from "react";
31591
+ import { useCallback as useCallback69, useRef as useRef59 } from "react";
31124
31592
 
31125
31593
  // src/utils/clipboardPayload.ts
31126
31594
  function insertAsSibling(source, newHtml, selector, selectorIndex) {
@@ -31235,10 +31703,10 @@ function useClipboard({
31235
31703
  handleDomEditElementDelete,
31236
31704
  previewIframeRef
31237
31705
  }) {
31238
- const clipboardRef = useRef58(null);
31239
- const projectIdRef = useRef58(projectId);
31706
+ const clipboardRef = useRef59(null);
31707
+ const projectIdRef = useRef59(projectId);
31240
31708
  projectIdRef.current = projectId;
31241
- const handleCopy = useCallback68(() => {
31709
+ const handleCopy = useCallback69(() => {
31242
31710
  const { selectedElementId, elements } = usePlayerStore.getState();
31243
31711
  if (selectedElementId) {
31244
31712
  const element = elements.find((el) => (el.key ?? el.id) === selectedElementId);
@@ -31288,7 +31756,7 @@ function useClipboard({
31288
31756
  }
31289
31757
  return false;
31290
31758
  }, [activeCompPath, domEditSelectionRef, previewIframeRef, showToast]);
31291
- const handlePaste = useCallback68(async () => {
31759
+ const handlePaste = useCallback69(async () => {
31292
31760
  const payload = clipboardRef.current;
31293
31761
  if (!payload) {
31294
31762
  showToast("Nothing to paste.", "info");
@@ -31344,7 +31812,7 @@ function useClipboard({
31344
31812
  showToast,
31345
31813
  writeProjectFile
31346
31814
  ]);
31347
- const handleCut = useCallback68(async () => {
31815
+ const handleCut = useCallback69(async () => {
31348
31816
  const copied = handleCopy();
31349
31817
  if (!copied) return false;
31350
31818
  const { selectedElementId, elements } = usePlayerStore.getState();
@@ -31719,11 +32187,11 @@ function useCaptionDetection({
31719
32187
  }
31720
32188
 
31721
32189
  // src/hooks/useRenderClipContent.ts
31722
- import { useCallback as useCallback70 } from "react";
32190
+ import { useCallback as useCallback71 } from "react";
31723
32191
  import { createElement } from "react";
31724
32192
 
31725
32193
  // src/player/components/AudioWaveform.tsx
31726
- import { memo as memo25, useRef as useRef59, useState as useState50, useCallback as useCallback69, useEffect as useEffect40 } from "react";
32194
+ import { memo as memo25, useRef as useRef60, useState as useState50, useCallback as useCallback70, useEffect as useEffect40 } from "react";
31727
32195
  import { jsx as jsx64, jsxs as jsxs55 } from "react/jsx-runtime";
31728
32196
  var BAR_W = 2;
31729
32197
  var GAP = 1;
@@ -31771,9 +32239,9 @@ var AudioWaveform = memo25(function AudioWaveform2({
31771
32239
  trimStartFraction,
31772
32240
  trimEndFraction
31773
32241
  }) {
31774
- const containerRef = useRef59(null);
31775
- const barsRef = useRef59(null);
31776
- const roRef = useRef59(null);
32242
+ const containerRef = useRef60(null);
32243
+ const barsRef = useRef60(null);
32244
+ const roRef = useRef60(null);
31777
32245
  const cacheKey = waveformUrl ?? audioUrl;
31778
32246
  const [peaks, setPeaks] = useState50(peaksCache.get(cacheKey) ?? null);
31779
32247
  useEffect40(() => {
@@ -31800,7 +32268,7 @@ var AudioWaveform = memo25(function AudioWaveform2({
31800
32268
  cancelled = true;
31801
32269
  };
31802
32270
  }, [audioUrl, waveformUrl, cacheKey, peaks]);
31803
- const draw = useCallback69(() => {
32271
+ const draw = useCallback70(() => {
31804
32272
  const container = containerRef.current;
31805
32273
  const barsEl = barsRef.current;
31806
32274
  if (!container || !barsEl || !peaks) return;
@@ -31821,7 +32289,7 @@ var AudioWaveform = memo25(function AudioWaveform2({
31821
32289
  }
31822
32290
  barsEl.innerHTML = html2;
31823
32291
  }, [peaks, trimStartFraction, trimEndFraction]);
31824
- const setContainerRef = useCallback69(
32292
+ const setContainerRef = useCallback70(
31825
32293
  (el) => {
31826
32294
  roRef.current?.disconnect();
31827
32295
  containerRef.current = el;
@@ -31912,7 +32380,7 @@ function useRenderClipContent({
31912
32380
  activePreviewUrl,
31913
32381
  effectiveTimelineDuration
31914
32382
  }) {
31915
- return useCallback70(
32383
+ return useCallback71(
31916
32384
  // Pre-existing clip-content dispatcher; reduced by extracting renderAudioClip.
31917
32385
  // fallow-ignore-next-line complexity
31918
32386
  (el, style) => {
@@ -31977,11 +32445,11 @@ function useRenderClipContent({
31977
32445
  }
31978
32446
 
31979
32447
  // src/hooks/useConsoleErrorCapture.ts
31980
- import { useCallback as useCallback71, useEffect as useEffect41, useRef as useRef60, useState as useState51 } from "react";
32448
+ import { useCallback as useCallback72, useEffect as useEffect41, useRef as useRef61, useState as useState51 } from "react";
31981
32449
  function useConsoleErrorCapture(previewIframe) {
31982
32450
  const [consoleErrors, setConsoleErrors] = useState51(null);
31983
- const consoleErrorsRef = useRef60([]);
31984
- const resetErrors = useCallback71(() => {
32451
+ const consoleErrorsRef = useRef61([]);
32452
+ const resetErrors = useCallback72(() => {
31985
32453
  consoleErrorsRef.current = [];
31986
32454
  setConsoleErrors(null);
31987
32455
  }, []);
@@ -32050,7 +32518,7 @@ function useConsoleErrorCapture(previewIframe) {
32050
32518
  }
32051
32519
 
32052
32520
  // src/hooks/useFrameCapture.ts
32053
- import { useState as useState52, useCallback as useCallback72 } from "react";
32521
+ import { useState as useState52, useCallback as useCallback73 } from "react";
32054
32522
 
32055
32523
  // src/utils/projectRouting.ts
32056
32524
  var PROJECT_HASH_PREFIX = "#project/";
@@ -32137,10 +32605,10 @@ function useFrameCapture({
32137
32605
  setCaptureFrameTime(usePlayerStore.getState().currentTime);
32138
32606
  return liveTime.subscribe(setCaptureFrameTime);
32139
32607
  });
32140
- const refreshCaptureFrameTime = useCallback72(() => {
32608
+ const refreshCaptureFrameTime = useCallback73(() => {
32141
32609
  setCaptureFrameTime(usePlayerStore.getState().currentTime);
32142
32610
  }, []);
32143
- const handleCaptureFrameClick = useCallback72(
32611
+ const handleCaptureFrameClick = useCallback73(
32144
32612
  async (event) => {
32145
32613
  if (!projectId) return;
32146
32614
  event.preventDefault();
@@ -32210,7 +32678,7 @@ function useFrameCapture({
32210
32678
  }
32211
32679
 
32212
32680
  // src/hooks/useLintModal.ts
32213
- import { useState as useState53, useCallback as useCallback73, useEffect as useEffect42, useRef as useRef61, useMemo as useMemo24 } from "react";
32681
+ import { useState as useState53, useCallback as useCallback74, useEffect as useEffect42, useRef as useRef62, useMemo as useMemo24 } from "react";
32214
32682
  function parseFinding(f) {
32215
32683
  return {
32216
32684
  severity: f.severity === "error" ? "error" : "warning",
@@ -32224,8 +32692,8 @@ function useLintModal(projectId, refreshKey) {
32224
32692
  const [lintModal, setLintModal] = useState53(null);
32225
32693
  const [linting, setLinting] = useState53(false);
32226
32694
  const [backgroundFindings, setBackgroundFindings] = useState53([]);
32227
- const autoLintRanRef = useRef61(false);
32228
- const runLint = useCallback73(
32695
+ const autoLintRanRef = useRef62(false);
32696
+ const runLint = useCallback74(
32229
32697
  async (opts) => {
32230
32698
  if (!projectId) return;
32231
32699
  if (!opts?.background) setLinting(true);
@@ -32250,8 +32718,8 @@ function useLintModal(projectId, refreshKey) {
32250
32718
  },
32251
32719
  [projectId]
32252
32720
  );
32253
- const handleLint = useCallback73(() => runLint(), [runLint]);
32254
- const prevProjectIdRef = useRef61(projectId);
32721
+ const handleLint = useCallback74(() => runLint(), [runLint]);
32722
+ const prevProjectIdRef = useRef62(projectId);
32255
32723
  useEffect42(() => {
32256
32724
  if (projectId !== prevProjectIdRef.current) {
32257
32725
  autoLintRanRef.current = false;
@@ -32266,8 +32734,8 @@ function useLintModal(projectId, refreshKey) {
32266
32734
  const timer = setTimeout(() => void runLint({ background: true }), 1e3);
32267
32735
  return () => clearTimeout(timer);
32268
32736
  }, [projectId, refreshKey, runLint]);
32269
- const closeLintModal = useCallback73(() => setLintModal(null), []);
32270
- const groupFindings = useCallback73(
32737
+ const closeLintModal = useCallback74(() => setLintModal(null), []);
32738
+ const groupFindings = useCallback74(
32271
32739
  (keyFn) => {
32272
32740
  const map = /* @__PURE__ */ new Map();
32273
32741
  for (const f of backgroundFindings) {
@@ -32321,11 +32789,11 @@ function useCompositionDimensions() {
32321
32789
  }
32322
32790
 
32323
32791
  // src/hooks/useToast.ts
32324
- import { useState as useState55, useCallback as useCallback74, useRef as useRef62 } from "react";
32792
+ import { useState as useState55, useCallback as useCallback75, useRef as useRef63 } from "react";
32325
32793
  function useToast() {
32326
32794
  const [appToast, setAppToast] = useState55(null);
32327
- const timerRef = useRef62(null);
32328
- const showToast = useCallback74((message, tone = "error") => {
32795
+ const timerRef = useRef63(null);
32796
+ const showToast = useCallback75((message, tone = "error") => {
32329
32797
  if (timerRef.current) clearTimeout(timerRef.current);
32330
32798
  setAppToast({ message, tone });
32331
32799
  timerRef.current = setTimeout(() => setAppToast(null), 4e3);
@@ -32333,7 +32801,7 @@ function useToast() {
32333
32801
  useMountEffect(() => () => {
32334
32802
  if (timerRef.current) clearTimeout(timerRef.current);
32335
32803
  });
32336
- const dismissToast = useCallback74(() => {
32804
+ const dismissToast = useCallback75(() => {
32337
32805
  if (timerRef.current) clearTimeout(timerRef.current);
32338
32806
  setAppToast(null);
32339
32807
  }, []);
@@ -32341,7 +32809,7 @@ function useToast() {
32341
32809
  }
32342
32810
 
32343
32811
  // src/hooks/useStudioUrlState.ts
32344
- import { useCallback as useCallback75, useEffect as useEffect43, useRef as useRef63, useState as useState56 } from "react";
32812
+ import { useCallback as useCallback76, useEffect as useEffect43, useRef as useRef64, useState as useState56 } from "react";
32345
32813
 
32346
32814
  // src/utils/studioUrlState.ts
32347
32815
  var VALID_TABS = ["layers", "design", "renders"];
@@ -32465,13 +32933,13 @@ function useStudioUrlState({
32465
32933
  initialState: initialState2
32466
32934
  }) {
32467
32935
  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);
32936
+ const hydratedSeekRef = useRef64(initialState2.currentTime == null);
32937
+ const hydratedInitialTimeRef = useRef64(initialState2.currentTime == null);
32938
+ const hydratedSelectionRef = useRef64(initialState2.selection == null);
32471
32939
  const [selectionHydrated, setSelectionHydrated] = useState56(initialState2.selection == null);
32472
- const pendingSelectionRef = useRef63(initialState2.selection);
32473
- const stableTimeRef = useRef63(initialState2.currentTime);
32474
- const buildUrlState = useCallback75(
32940
+ const pendingSelectionRef = useRef64(initialState2.selection);
32941
+ const stableTimeRef = useRef64(initialState2.currentTime);
32942
+ const buildUrlState = useCallback76(
32475
32943
  () => ({
32476
32944
  activeCompPath,
32477
32945
  currentTime: stableTimeRef.current,
@@ -32482,7 +32950,7 @@ function useStudioUrlState({
32482
32950
  }),
32483
32951
  [activeCompPath, domEditSelection, rightCollapsed, rightPanelTab, timelineVisible]
32484
32952
  );
32485
- const applyUrlSelection = useCallback75(
32953
+ const applyUrlSelection = useCallback76(
32486
32954
  (selection) => {
32487
32955
  if (!selection) {
32488
32956
  applyDomSelection(null, { revealPanel: false });
@@ -32598,7 +33066,7 @@ function useStudioUrlState({
32598
33066
  }
32599
33067
 
32600
33068
  // src/hooks/useStudioContextValue.ts
32601
- import { useCallback as useCallback76, useMemo as useMemo25, useRef as useRef64, useState as useState57 } from "react";
33069
+ import { useCallback as useCallback77, useMemo as useMemo25, useRef as useRef65, useState as useState57 } from "react";
32602
33070
  function buildStudioContextValue(input) {
32603
33071
  return {
32604
33072
  projectId: input.projectId,
@@ -32643,22 +33111,22 @@ function useInspectorState(rightPanelTab, rightInspectorPanes, rightCollapsed, i
32643
33111
  }
32644
33112
  function useDragOverlay(onImportFiles) {
32645
33113
  const [active, setActive] = useState57(false);
32646
- const counterRef = useRef64(0);
32647
- const onDragOver = useCallback76((e) => {
33114
+ const counterRef = useRef65(0);
33115
+ const onDragOver = useCallback77((e) => {
32648
33116
  if (!e.dataTransfer.types.includes("Files")) return;
32649
33117
  e.preventDefault();
32650
33118
  }, []);
32651
- const onDragEnter = useCallback76((e) => {
33119
+ const onDragEnter = useCallback77((e) => {
32652
33120
  if (!e.dataTransfer.types.includes("Files")) return;
32653
33121
  e.preventDefault();
32654
33122
  counterRef.current++;
32655
33123
  setActive(true);
32656
33124
  }, []);
32657
- const onDragLeave = useCallback76(() => {
33125
+ const onDragLeave = useCallback77(() => {
32658
33126
  counterRef.current--;
32659
33127
  if (counterRef.current === 0) setActive(false);
32660
33128
  }, []);
32661
- const onDrop = useCallback76(
33129
+ const onDrop = useCallback77(
32662
33130
  (e) => {
32663
33131
  counterRef.current = 0;
32664
33132
  setActive(false);
@@ -32742,7 +33210,7 @@ function PanelLayoutProvider({
32742
33210
  // src/contexts/ViewModeContext.tsx
32743
33211
  import {
32744
33212
  createContext as createContext5,
32745
- useCallback as useCallback77,
33213
+ useCallback as useCallback78,
32746
33214
  useContext as useContext5,
32747
33215
  useEffect as useEffect44,
32748
33216
  useMemo as useMemo27,
@@ -32771,7 +33239,7 @@ function useViewModeState() {
32771
33239
  window.addEventListener("popstate", onPopState);
32772
33240
  return () => window.removeEventListener("popstate", onPopState);
32773
33241
  }, []);
32774
- const setViewMode = useCallback77((mode) => {
33242
+ const setViewMode = useCallback78((mode) => {
32775
33243
  setMode(mode);
32776
33244
  writeViewModeToUrl(mode);
32777
33245
  }, []);
@@ -33134,10 +33602,10 @@ function StudioHeader({
33134
33602
  }
33135
33603
 
33136
33604
  // src/hooks/useGestureCommit.ts
33137
- import { useState as useState60, useCallback as useCallback79, useRef as useRef66, useEffect as useEffect46 } from "react";
33605
+ import { useState as useState60, useCallback as useCallback80, useRef as useRef67, useEffect as useEffect46 } from "react";
33138
33606
 
33139
33607
  // src/hooks/useGestureRecording.ts
33140
- import { useCallback as useCallback78, useEffect as useEffect45, useRef as useRef65, useState as useState59 } from "react";
33608
+ import { useCallback as useCallback79, useEffect as useEffect45, useRef as useRef66, useState as useState59 } from "react";
33141
33609
  function readBasePosition(element, iframeEl) {
33142
33610
  let baseOpacity = 1;
33143
33611
  let baseScale = 1;
@@ -33256,10 +33724,10 @@ function createRecordingRefs() {
33256
33724
  function useGestureRecording() {
33257
33725
  const [isRecording, setIsRecording] = useState59(false);
33258
33726
  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);
33727
+ const isRecordingRef = useRef66(false);
33728
+ const refs = useRef66(createRecordingRefs());
33729
+ const samplesRef = useRef66(refs.current.samples);
33730
+ const trailRef = useRef66(refs.current.trail);
33263
33731
  useEffect45(() => {
33264
33732
  const r = refs.current;
33265
33733
  return () => {
@@ -33268,7 +33736,7 @@ function useGestureRecording() {
33268
33736
  isRecordingRef.current = false;
33269
33737
  };
33270
33738
  }, []);
33271
- const startRecording = useCallback78(
33739
+ const startRecording = useCallback79(
33272
33740
  (element, iframeEl, elementEndTime) => {
33273
33741
  if (isRecordingRef.current) return;
33274
33742
  isRecordingRef.current = true;
@@ -33368,7 +33836,7 @@ function useGestureRecording() {
33368
33836
  []
33369
33837
  // No deps — uses refs only for all mutable state
33370
33838
  );
33371
- const stopRecording = useCallback78(() => {
33839
+ const stopRecording = useCallback79(() => {
33372
33840
  if (!isRecordingRef.current) return [];
33373
33841
  isRecordingRef.current = false;
33374
33842
  const r = refs.current;
@@ -33398,7 +33866,7 @@ function useGestureRecording() {
33398
33866
  setIsRecording(false);
33399
33867
  return frozen;
33400
33868
  }, []);
33401
- const clearSamples = useCallback78(() => {
33869
+ const clearSamples = useCallback79(() => {
33402
33870
  const r = refs.current;
33403
33871
  r.samples = [];
33404
33872
  r.trail = [];
@@ -33651,13 +34119,13 @@ function useGestureCommit({
33651
34119
  }) {
33652
34120
  const gestureRecording = useGestureRecording();
33653
34121
  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);
34122
+ const gestureStateRef = useRef67("idle");
34123
+ const recordingAutoStopRef = useRef67(void 0);
34124
+ const recordingStartTimeRef = useRef67(0);
34125
+ const commitInFlightRef = useRef67(false);
34126
+ const capturedSelectionRef = useRef67(null);
33659
34127
  useEffect46(() => () => clearInterval(recordingAutoStopRef.current), []);
33660
- const stopAndCommitRecording = useCallback79(async () => {
34128
+ const stopAndCommitRecording = useCallback80(async () => {
33661
34129
  clearInterval(recordingAutoStopRef.current);
33662
34130
  if (commitInFlightRef.current) {
33663
34131
  return;
@@ -33809,7 +34277,7 @@ function useGestureCommit({
33809
34277
  commitInFlightRef.current = false;
33810
34278
  }
33811
34279
  }, [gestureRecording, showToast, isGestureRecordingRef, domEditSessionRef]);
33812
- const handleToggleRecording = useCallback79(() => {
34280
+ const handleToggleRecording = useCallback80(() => {
33813
34281
  if (gestureStateRef.current === "recording") {
33814
34282
  void stopAndCommitRecording();
33815
34283
  return;
@@ -33967,20 +34435,20 @@ var GestureTrailOverlay = memo26(function GestureTrailOverlay2({
33967
34435
  });
33968
34436
 
33969
34437
  // src/components/StudioLeftSidebar.tsx
33970
- import { useCallback as useCallback85 } from "react";
34438
+ import { useCallback as useCallback86 } from "react";
33971
34439
 
33972
34440
  // src/components/sidebar/LeftSidebar.tsx
33973
34441
  import {
33974
34442
  memo as memo30,
33975
34443
  useState as useState67,
33976
- useCallback as useCallback84,
34444
+ useCallback as useCallback85,
33977
34445
  useImperativeHandle,
33978
- useRef as useRef71,
34446
+ useRef as useRef72,
33979
34447
  forwardRef as forwardRef3
33980
34448
  } from "react";
33981
34449
 
33982
34450
  // src/components/sidebar/CompositionsTab.tsx
33983
- import { memo as memo27, useCallback as useCallback80, useEffect as useEffect47, useRef as useRef67, useState as useState61 } from "react";
34451
+ import { memo as memo27, useCallback as useCallback81, useEffect as useEffect47, useRef as useRef68, useState as useState61 } from "react";
33984
34452
  import { jsx as jsx69, jsxs as jsxs58 } from "react/jsx-runtime";
33985
34453
  var DEFAULT_PREVIEW_STAGE = { width: 1920, height: 1080 };
33986
34454
  var CARD_W = 80;
@@ -34048,10 +34516,10 @@ function CompCard({
34048
34516
  }) {
34049
34517
  const [hovered, setHovered] = useState61(false);
34050
34518
  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) => {
34519
+ const iframeRef = useRef68(null);
34520
+ const hoverTimer = useRef68(null);
34521
+ const syncTimer = useRef68(null);
34522
+ const requestIframePlaybackSync = useCallback81((shouldPlay) => {
34055
34523
  if (syncTimer.current) {
34056
34524
  clearTimeout(syncTimer.current);
34057
34525
  syncTimer.current = null;
@@ -34209,7 +34677,7 @@ var CompositionsTab = memo27(function CompositionsTab2({
34209
34677
  });
34210
34678
 
34211
34679
  // 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";
34680
+ import { memo as memo28, useState as useState64, useCallback as useCallback83, useRef as useRef70, useMemo as useMemo29, useEffect as useEffect50 } from "react";
34213
34681
 
34214
34682
  // src/components/ui/VideoFrameThumbnail.tsx
34215
34683
  import { useState as useState62, useEffect as useEffect48 } from "react";
@@ -34351,7 +34819,7 @@ var CATEGORY_LABELS = {
34351
34819
  var FILTER_ORDER = ["audio", "images", "video", "fonts"];
34352
34820
 
34353
34821
  // src/components/sidebar/AudioRow.tsx
34354
- import { useState as useState63, useRef as useRef68, useEffect as useEffect49, useCallback as useCallback81 } from "react";
34822
+ import { useState as useState63, useRef as useRef69, useEffect as useEffect49, useCallback as useCallback82 } from "react";
34355
34823
  import { Fragment as Fragment20, jsx as jsx72, jsxs as jsxs60 } from "react/jsx-runtime";
34356
34824
  function AudioRow({
34357
34825
  projectId,
@@ -34366,11 +34834,11 @@ function AudioRow({
34366
34834
  const [contextMenu, setContextMenu] = useState63(null);
34367
34835
  const [playing, setPlaying] = useState63(false);
34368
34836
  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);
34837
+ const audioRef = useRef69(null);
34838
+ const actxRef = useRef69(null);
34839
+ const analyserRef = useRef69(null);
34840
+ const sourceRef = useRef69(null);
34841
+ const animRef = useRef69(0);
34374
34842
  const name = basename(asset);
34375
34843
  const subtype = getAudioSubtype(asset);
34376
34844
  const serveUrl = `/api/projects/${projectId}/preview/${asset}`;
@@ -34409,7 +34877,7 @@ function AudioRow({
34409
34877
  }
34410
34878
  return () => cancelAnimationFrame(animRef.current);
34411
34879
  }, [playing]);
34412
- const togglePlay = useCallback81(async () => {
34880
+ const togglePlay = useCallback82(async () => {
34413
34881
  if (playing) {
34414
34882
  audioRef.current?.pause();
34415
34883
  setPlaying(false);
@@ -34646,13 +35114,13 @@ var AssetsTab = memo28(function AssetsTab2({
34646
35114
  onDelete,
34647
35115
  onRename
34648
35116
  }) {
34649
- const fileInputRef = useRef69(null);
35117
+ const fileInputRef = useRef70(null);
34650
35118
  const [dragOver, setDragOver] = useState64(false);
34651
35119
  const [copiedPath, setCopiedPath] = useState64(null);
34652
35120
  const [activeFilter, setActiveFilter] = useState64("all");
34653
35121
  const [searchQuery, setSearchQuery] = useState64("");
34654
35122
  const [manifest, setManifest] = useState64(/* @__PURE__ */ new Map());
34655
- const manifest404Ref = useRef69(/* @__PURE__ */ new Set());
35123
+ const manifest404Ref = useRef70(/* @__PURE__ */ new Set());
34656
35124
  const assetsKey = assets.join("|");
34657
35125
  useEffect50(() => {
34658
35126
  if (manifest404Ref.current.has(projectId)) return;
@@ -34681,7 +35149,7 @@ var AssetsTab = memo28(function AssetsTab2({
34681
35149
  cancelled = true;
34682
35150
  };
34683
35151
  }, [projectId, assetsKey]);
34684
- const handleDrop = useCallback82(
35152
+ const handleDrop = useCallback83(
34685
35153
  (e) => {
34686
35154
  e.preventDefault();
34687
35155
  setDragOver(false);
@@ -34689,7 +35157,7 @@ var AssetsTab = memo28(function AssetsTab2({
34689
35157
  },
34690
35158
  [onImport]
34691
35159
  );
34692
- const handleCopyPath = useCallback82(async (path) => {
35160
+ const handleCopyPath = useCallback83(async (path) => {
34693
35161
  const copied = await copyTextToClipboard(path);
34694
35162
  if (copied) {
34695
35163
  setCopiedPath(path);
@@ -34938,7 +35406,7 @@ var AssetsTab = memo28(function AssetsTab2({
34938
35406
  });
34939
35407
 
34940
35408
  // src/components/sidebar/BlocksTab.tsx
34941
- import { memo as memo29, useState as useState66, useCallback as useCallback83, useRef as useRef70, useEffect as useEffect52 } from "react";
35409
+ import { memo as memo29, useState as useState66, useCallback as useCallback84, useRef as useRef71, useEffect as useEffect52 } from "react";
34942
35410
  import { createPortal as createPortal5 } from "react-dom";
34943
35411
 
34944
35412
  // src/hooks/useBlockCatalog.ts
@@ -35232,16 +35700,16 @@ function BlockCard({
35232
35700
  }) {
35233
35701
  const [hovered, setHovered] = useState66(false);
35234
35702
  const [adding, setAdding] = useState66(false);
35235
- const hoverTimer = useRef70(null);
35703
+ const hoverTimer = useRef71(null);
35236
35704
  const colors = getCategoryColors(category);
35237
35705
  const needsWebGL = tags?.includes("html-in-canvas") || tags?.includes("webgl");
35238
- const handleEnter = useCallback83(() => {
35706
+ const handleEnter = useCallback84(() => {
35239
35707
  hoverTimer.current = setTimeout(() => {
35240
35708
  setHovered(true);
35241
35709
  onPreview?.({ videoUrl, posterUrl, title });
35242
35710
  }, 300);
35243
35711
  }, [onPreview, videoUrl, posterUrl, title]);
35244
- const handleLeave = useCallback83(() => {
35712
+ const handleLeave = useCallback84(() => {
35245
35713
  if (hoverTimer.current) {
35246
35714
  clearTimeout(hoverTimer.current);
35247
35715
  hoverTimer.current = null;
@@ -35254,7 +35722,7 @@ function BlockCard({
35254
35722
  if (hoverTimer.current) clearTimeout(hoverTimer.current);
35255
35723
  };
35256
35724
  }, []);
35257
- const handleAdd = useCallback83(
35725
+ const handleAdd = useCallback84(
35258
35726
  (e) => {
35259
35727
  e.stopPropagation();
35260
35728
  if (adding || !onAdd) return;
@@ -35265,7 +35733,7 @@ function BlockCard({
35265
35733
  [onAdd, adding]
35266
35734
  );
35267
35735
  const { activeCompPath, compositionDimensions } = useStudioShellContext();
35268
- const handleShowPrompt = useCallback83(
35736
+ const handleShowPrompt = useCallback84(
35269
35737
  (e) => {
35270
35738
  e.stopPropagation();
35271
35739
  const state = usePlayerStore.getState();
@@ -35403,11 +35871,11 @@ function PromptPreviewModal({
35403
35871
  }) {
35404
35872
  const [value, setValue] = useState66(prompt);
35405
35873
  const [copied, setCopied] = useState66(false);
35406
- const textareaRef = useRef70(null);
35874
+ const textareaRef = useRef71(null);
35407
35875
  useEffect52(() => {
35408
35876
  requestAnimationFrame(() => textareaRef.current?.focus());
35409
35877
  }, []);
35410
- const handleCopy = useCallback83(() => {
35878
+ const handleCopy = useCallback84(() => {
35411
35879
  navigator.clipboard.writeText(value);
35412
35880
  setCopied(true);
35413
35881
  setTimeout(() => setCopied(false), 1500);
@@ -35530,14 +35998,14 @@ var LeftSidebar = memo30(
35530
35998
  takeoverContent
35531
35999
  }, ref) {
35532
36000
  const [tab, setTab] = useState67(getPersistedTab);
35533
- const tabRef = useRef71(tab);
36001
+ const tabRef = useRef72(tab);
35534
36002
  tabRef.current = tab;
35535
- const selectTab = useCallback84((t) => {
36003
+ const selectTab = useCallback85((t) => {
35536
36004
  setTab(t);
35537
36005
  localStorage.setItem(STORAGE_KEY, t);
35538
36006
  trackStudioEvent("tab_switch", { panel: "left_sidebar", tab: t });
35539
36007
  }, []);
35540
- const getTab = useCallback84(() => tabRef.current, []);
36008
+ const getTab = useCallback85(() => tabRef.current, []);
35541
36009
  useImperativeHandle(ref, () => ({ selectTab, getTab }), [selectTab, getTab]);
35542
36010
  return /* @__PURE__ */ jsx75(
35543
36011
  "div",
@@ -35843,7 +36311,7 @@ function StudioLeftSidebar({
35843
36311
  handleImportFiles,
35844
36312
  handleContentChange
35845
36313
  } = useFileManagerContext();
35846
- const handleRenderComposition = useCallback85(
36314
+ const handleRenderComposition = useCallback86(
35847
36315
  async (comp) => {
35848
36316
  await waitForPendingDomEditSaves();
35849
36317
  const { format, quality, fps } = getPersistedRenderSettings();
@@ -35937,10 +36405,10 @@ function StudioLeftSidebar({
35937
36405
  }
35938
36406
 
35939
36407
  // src/components/StudioPreviewArea.tsx
35940
- import { useState as useState76, useMemo as useMemo33, useCallback as useCallback92 } from "react";
36408
+ import { useState as useState76, useMemo as useMemo33, useCallback as useCallback93 } from "react";
35941
36409
 
35942
36410
  // src/captions/components/CaptionOverlay.tsx
35943
- import { memo as memo31, useState as useState68, useCallback as useCallback86, useRef as useRef72 } from "react";
36411
+ import { memo as memo31, useState as useState68, useCallback as useCallback87, useRef as useRef73 } from "react";
35944
36412
 
35945
36413
  // src/captions/keyboard.ts
35946
36414
  var CAPTION_NUDGE_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"]);
@@ -36129,10 +36597,10 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36129
36597
  const selectSegment = useCaptionStore((s) => s.selectSegment);
36130
36598
  const clearSelection = useCaptionStore((s) => s.clearSelection);
36131
36599
  const [wordBoxes, setWordBoxes] = useState68([]);
36132
- const overlayRef = useRef72(null);
36133
- const modelRef = useRef72(model);
36600
+ const overlayRef = useRef73(null);
36601
+ const modelRef = useRef73(model);
36134
36602
  modelRef.current = model;
36135
- const interactionRef = useRef72(null);
36603
+ const interactionRef = useRef73(null);
36136
36604
  useMountEffect(() => {
36137
36605
  if (!isEditMode) return;
36138
36606
  let prevBoxes = [];
@@ -36185,14 +36653,14 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36185
36653
  window.removeEventListener("keydown", handleKeyDown);
36186
36654
  };
36187
36655
  });
36188
- const getCssScale = useCallback86(() => {
36656
+ const getCssScale = useCallback87(() => {
36189
36657
  const iframe = iframeRef.current;
36190
36658
  if (!iframe) return 1;
36191
36659
  const rect = iframe.getBoundingClientRect();
36192
36660
  const nativeW = parseFloat(iframe.style.width) || rect.width;
36193
36661
  return rect.width / nativeW;
36194
36662
  }, [iframeRef]);
36195
- const startMove = useCallback86(
36663
+ const startMove = useCallback87(
36196
36664
  (groupIndex, wordIndex, segmentId, e) => {
36197
36665
  e.stopPropagation();
36198
36666
  e.currentTarget.setPointerCapture(e.pointerId);
@@ -36216,7 +36684,7 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36216
36684
  },
36217
36685
  [iframeRef]
36218
36686
  );
36219
- const startScale = useCallback86(
36687
+ const startScale = useCallback87(
36220
36688
  (groupIndex, wordIndex, segmentId, e) => {
36221
36689
  e.stopPropagation();
36222
36690
  e.preventDefault();
@@ -36244,7 +36712,7 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36244
36712
  },
36245
36713
  [iframeRef, getCssScale]
36246
36714
  );
36247
- const startRotate = useCallback86(
36715
+ const startRotate = useCallback87(
36248
36716
  (box, e) => {
36249
36717
  e.stopPropagation();
36250
36718
  e.preventDefault();
@@ -36268,14 +36736,14 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36268
36736
  },
36269
36737
  [iframeRef]
36270
36738
  );
36271
- const getIframeWin = useCallback86(() => {
36739
+ const getIframeWin = useCallback87(() => {
36272
36740
  try {
36273
36741
  return iframeRef.current?.contentWindow ?? null;
36274
36742
  } catch {
36275
36743
  return null;
36276
36744
  }
36277
36745
  }, [iframeRef]);
36278
- const handlePointerMove = useCallback86(
36746
+ const handlePointerMove = useCallback87(
36279
36747
  (e) => {
36280
36748
  const i = interactionRef.current;
36281
36749
  if (!i) return;
@@ -36301,7 +36769,7 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36301
36769
  },
36302
36770
  [getCssScale, getIframeWin]
36303
36771
  );
36304
- const handlePointerUp = useCallback86(() => {
36772
+ const handlePointerUp = useCallback87(() => {
36305
36773
  const i = interactionRef.current;
36306
36774
  if (i) {
36307
36775
  const win = getIframeWin();
@@ -36309,7 +36777,7 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36309
36777
  interactionRef.current = null;
36310
36778
  }
36311
36779
  }, [getIframeWin]);
36312
- const handleBackgroundClick = useCallback86(
36780
+ const handleBackgroundClick = useCallback87(
36313
36781
  (e) => {
36314
36782
  if (e.target === e.currentTarget) clearSelection();
36315
36783
  },
@@ -36419,7 +36887,7 @@ var CaptionOverlay = memo31(function CaptionOverlay2({ iframeRef }) {
36419
36887
  });
36420
36888
 
36421
36889
  // src/captions/components/CaptionTimeline.tsx
36422
- import { memo as memo32, useCallback as useCallback87, useRef as useRef73 } from "react";
36890
+ import { memo as memo32, useCallback as useCallback88, useRef as useRef74 } from "react";
36423
36891
  import { jsx as jsx79, jsxs as jsxs67 } from "react/jsx-runtime";
36424
36892
  var GROUP_COLORS = [
36425
36893
  "#3CE6AC",
@@ -36442,8 +36910,8 @@ var CaptionTimeline = memo32(function CaptionTimeline2({
36442
36910
  const selectSegment = useCaptionStore((s) => s.selectSegment);
36443
36911
  const updateSegmentTiming = useCaptionStore((s) => s.updateSegmentTiming);
36444
36912
  const splitGroup = useCaptionStore((s) => s.splitGroup);
36445
- const dragRef = useRef73(null);
36446
- const handleEdgePointerDown = useCallback87(
36913
+ const dragRef = useRef74(null);
36914
+ const handleEdgePointerDown = useCallback88(
36447
36915
  (e, segId, edge, originalStart, originalEnd) => {
36448
36916
  e.stopPropagation();
36449
36917
  e.preventDefault();
@@ -36452,7 +36920,7 @@ var CaptionTimeline = memo32(function CaptionTimeline2({
36452
36920
  },
36453
36921
  []
36454
36922
  );
36455
- const handlePointerMove = useCallback87(
36923
+ const handlePointerMove = useCallback88(
36456
36924
  (e) => {
36457
36925
  const drag = dragRef.current;
36458
36926
  if (!drag) return;
@@ -36469,24 +36937,24 @@ var CaptionTimeline = memo32(function CaptionTimeline2({
36469
36937
  },
36470
36938
  [pixelsPerSecond, updateSegmentTiming]
36471
36939
  );
36472
- const handlePointerUp = useCallback87(() => {
36940
+ const handlePointerUp = useCallback88(() => {
36473
36941
  dragRef.current = null;
36474
36942
  }, []);
36475
- const handleBlockClick = useCallback87(
36943
+ const handleBlockClick = useCallback88(
36476
36944
  (e, segId) => {
36477
36945
  e.stopPropagation();
36478
36946
  selectSegment(segId, e.shiftKey);
36479
36947
  },
36480
36948
  [selectSegment]
36481
36949
  );
36482
- const handleBlockDoubleClick = useCallback87(
36950
+ const handleBlockDoubleClick = useCallback88(
36483
36951
  (e, groupId, segId) => {
36484
36952
  e.stopPropagation();
36485
36953
  splitGroup(groupId, segId);
36486
36954
  },
36487
36955
  [splitGroup]
36488
36956
  );
36489
- const handleTrackClick = useCallback87(
36957
+ const handleTrackClick = useCallback88(
36490
36958
  (e) => {
36491
36959
  if (!onSeek) return;
36492
36960
  const rect = e.currentTarget.getBoundingClientRect();
@@ -36564,10 +37032,10 @@ var CaptionTimeline = memo32(function CaptionTimeline2({
36564
37032
  });
36565
37033
 
36566
37034
  // src/components/editor/DomEditOverlay.tsx
36567
- import { memo as memo35, useEffect as useEffect53, useMemo as useMemo31, useRef as useRef77, useState as useState71 } from "react";
37035
+ import { memo as memo35, useEffect as useEffect53, useMemo as useMemo31, useRef as useRef78, useState as useState71 } from "react";
36568
37036
 
36569
37037
  // src/components/editor/marqueeCommit.ts
36570
- import { useCallback as useCallback88, useRef as useRef74, useState as useState69 } from "react";
37038
+ import { useCallback as useCallback89, useRef as useRef75, useState as useState69 } from "react";
36571
37039
 
36572
37040
  // src/utils/marqueeGeometry.ts
36573
37041
  function rectsOverlap(a, b) {
@@ -36758,10 +37226,10 @@ async function runMarqueeIntersection(rect, iframe, overlayEl, activeComposition
36758
37226
  return hits;
36759
37227
  }
36760
37228
  function useMarqueeGestures(deps) {
36761
- const marqueeRef = useRef74(null);
37229
+ const marqueeRef = useRef75(null);
36762
37230
  const [marqueeRect, setMarqueeRect] = useState69(null);
36763
37231
  const [candidateRects, setCandidateRects] = useState69([]);
36764
- const commitMarquee = useCallback88(
37232
+ const commitMarquee = useCallback89(
36765
37233
  async (rect, additive) => {
36766
37234
  const iframe = deps.iframeRef.current;
36767
37235
  const overlay = deps.overlayRef.current;
@@ -36772,7 +37240,7 @@ function useMarqueeGestures(deps) {
36772
37240
  },
36773
37241
  [deps.iframeRef, deps.overlayRef, deps.onMarqueeSelectRef, deps.activeCompositionPathRef]
36774
37242
  );
36775
- const onPointerMove = useCallback88(
37243
+ const onPointerMove = useCallback89(
36776
37244
  (event) => {
36777
37245
  const m = marqueeRef.current;
36778
37246
  if (m) {
@@ -36805,7 +37273,7 @@ function useMarqueeGestures(deps) {
36805
37273
  },
36806
37274
  [deps.gestures, deps.overlayRef, deps.iframeRef, deps.activeCompositionPathRef]
36807
37275
  );
36808
- const onPointerUp = useCallback88(
37276
+ const onPointerUp = useCallback89(
36809
37277
  (event) => {
36810
37278
  const m = marqueeRef.current;
36811
37279
  if (m) {
@@ -36835,7 +37303,7 @@ function useMarqueeGestures(deps) {
36835
37303
  },
36836
37304
  [deps.gestures, commitMarquee, deps.onMarqueeSelectRef]
36837
37305
  );
36838
- const onPointerCancel = useCallback88(() => {
37306
+ const onPointerCancel = useCallback89(() => {
36839
37307
  if (marqueeRef.current) {
36840
37308
  marqueeRef.current = null;
36841
37309
  setMarqueeRect(null);
@@ -36934,7 +37402,7 @@ function hasDomEditRotationChanged(initialAngle, nextAngle) {
36934
37402
  }
36935
37403
 
36936
37404
  // src/components/editor/useDomEditOverlayRects.ts
36937
- import { useRef as useRef75, useState as useState70 } from "react";
37405
+ import { useRef as useRef76, useState as useState70 } from "react";
36938
37406
  function childRectsEqual(a, b) {
36939
37407
  if (a.length !== b.length) return false;
36940
37408
  for (let i = 0; i < a.length; i++) {
@@ -36955,13 +37423,13 @@ function useDomEditOverlayRects({
36955
37423
  const [hoverRect, setHoverRectState] = useState70(null);
36956
37424
  const [groupOverlayItems, setGroupOverlayItemsState] = useState70([]);
36957
37425
  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([]);
37426
+ const overlayRectRef = useRef76(null);
37427
+ const hoverRectRef = useRef76(null);
37428
+ const groupOverlayItemsRef = useRef76([]);
37429
+ const resolvedElementRef = useRef76(null);
37430
+ const resolvedHoverElementRef = useRef76(null);
37431
+ const resolvedGroupElementRef = useRef76(/* @__PURE__ */ new Map());
37432
+ const childRectsRef = useRef76([]);
36965
37433
  const setOverlayRect = (next) => {
36966
37434
  if (rectsEqual(overlayRectRef.current, next)) return;
36967
37435
  overlayRectRef.current = next;
@@ -37154,7 +37622,7 @@ function OffCanvasIndicators({
37154
37622
  const selectOffCanvas = async () => {
37155
37623
  const el = elements.current.get(r.key);
37156
37624
  if (!el) return;
37157
- const { resolveDomEditSelection: resolveDomEditSelection2 } = await import("./domEditingLayers-EK7R7R4G.js");
37625
+ const { resolveDomEditSelection: resolveDomEditSelection2 } = await import("./domEditingLayers-UIQZJCOA.js");
37158
37626
  const acp = activeCompositionPathRef.current ?? "index.html";
37159
37627
  const sel = await resolveDomEditSelection2(el, {
37160
37628
  activeCompositionPath: acp,
@@ -37820,7 +38288,7 @@ function readPositiveDimension2(value) {
37820
38288
  return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
37821
38289
  }
37822
38290
  var IGNORED_TAGS = /* @__PURE__ */ new Set(["script", "style", "link", "meta", "base", "template", "br", "wbr"]);
37823
- function isHtmlElement2(node) {
38291
+ function isHtmlElement3(node) {
37824
38292
  return node.nodeType === 1;
37825
38293
  }
37826
38294
  function collectVisibleElements(root, excludeElements, maxItems) {
@@ -37828,7 +38296,7 @@ function collectVisibleElements(root, excludeElements, maxItems) {
37828
38296
  const visit = (el) => {
37829
38297
  if (result.length >= maxItems) return;
37830
38298
  for (const child of Array.from(el.children)) {
37831
- if (!isHtmlElement2(child)) continue;
38299
+ if (!isHtmlElement3(child)) continue;
37832
38300
  if (IGNORED_TAGS.has(child.tagName.toLowerCase())) continue;
37833
38301
  if (child.hasAttribute("data-composition-id")) continue;
37834
38302
  if (excludeElements.has(child)) continue;
@@ -38305,7 +38773,8 @@ function createDomEditOverlayGestureHandlers(opts) {
38305
38773
  restoreGestureOverlayRect(g);
38306
38774
  opts.suppressNextBoxClickRef.current = true;
38307
38775
  opts.onCanvasMouseDown(e, {
38308
- preferClipAncestor: false
38776
+ preferClipAncestor: false,
38777
+ hoverSelection: opts.hoverSelectionRef.current
38309
38778
  });
38310
38779
  return;
38311
38780
  }
@@ -38417,7 +38886,7 @@ function createDomEditOverlayGestureHandlers(opts) {
38417
38886
  }
38418
38887
 
38419
38888
  // src/components/editor/SnapGuideOverlay.tsx
38420
- import { memo as memo33, useRef as useRef76 } from "react";
38889
+ import { memo as memo33, useRef as useRef77 } from "react";
38421
38890
  import { jsx as jsx82, jsxs as jsxs69 } from "react/jsx-runtime";
38422
38891
  var MAX_GUIDES = 6;
38423
38892
  var MAX_SPACING_GUIDES = 4;
@@ -38429,12 +38898,12 @@ var SnapGuideOverlay = memo33(function SnapGuideOverlay2({
38429
38898
  overlayWidth,
38430
38899
  overlayHeight
38431
38900
  }) {
38432
- const guideElsRef = useRef76([]);
38433
- const spacingElsRef = useRef76([]);
38434
- const spacingLabelElsRef = useRef76([]);
38435
- const overlayWidthRef = useRef76(overlayWidth);
38901
+ const guideElsRef = useRef77([]);
38902
+ const spacingElsRef = useRef77([]);
38903
+ const spacingLabelElsRef = useRef77([]);
38904
+ const overlayWidthRef = useRef77(overlayWidth);
38436
38905
  overlayWidthRef.current = overlayWidth;
38437
- const overlayHeightRef = useRef76(overlayHeight);
38906
+ const overlayHeightRef = useRef77(overlayHeight);
38438
38907
  overlayHeightRef.current = overlayHeight;
38439
38908
  useMountEffect(() => {
38440
38909
  let frame = 0;
@@ -38616,9 +39085,9 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38616
39085
  onRotationCommit,
38617
39086
  onMarqueeSelect
38618
39087
  }) {
38619
- const overlayRef = useRef77(null);
38620
- const boxRef = useRef77(null);
38621
- const onMarqueeSelectRef = useRef77(onMarqueeSelect);
39088
+ const overlayRef = useRef78(null);
39089
+ const boxRef = useRef78(null);
39090
+ const onMarqueeSelectRef = useRef78(onMarqueeSelect);
38622
39091
  onMarqueeSelectRef.current = onMarqueeSelect;
38623
39092
  const selectionShapeStyles = (() => {
38624
39093
  const fallback = {
@@ -38642,39 +39111,39 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38642
39111
  return fallback;
38643
39112
  }
38644
39113
  })();
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);
39114
+ const gestureRef = useRef78(null);
39115
+ const groupGestureRef = useRef78(null);
39116
+ const blockedMoveRef = useRef78(null);
39117
+ const suppressNextBoxClickRef = useRef78(false);
39118
+ const suppressNextBoxMouseDownRef = useRef78(false);
39119
+ const suppressNextOverlayMouseDownRef = useRef78(false);
39120
+ const snapGuidesRef = useRef78(null);
39121
+ const rafPausedRef = useRef78(false);
39122
+ const selectionRef = useRef78(selection);
38654
39123
  selectionRef.current = selection;
38655
- const activeCompositionPathRef = useRef77(activeCompositionPath);
39124
+ const activeCompositionPathRef = useRef78(activeCompositionPath);
38656
39125
  activeCompositionPathRef.current = activeCompositionPath;
38657
- const groupSelectionsRef = useRef77(groupSelections);
39126
+ const groupSelectionsRef = useRef78(groupSelections);
38658
39127
  groupSelectionsRef.current = groupSelections;
38659
- const hoverSelectionRef = useRef77(hoverSelection);
39128
+ const hoverSelectionRef = useRef78(hoverSelection);
38660
39129
  hoverSelectionRef.current = hoverSelection;
38661
- const onPathOffsetCommitRef = useRef77(onPathOffsetCommit);
39130
+ const onPathOffsetCommitRef = useRef78(onPathOffsetCommit);
38662
39131
  onPathOffsetCommitRef.current = onPathOffsetCommit;
38663
- const onGroupPathOffsetCommitRef = useRef77(onGroupPathOffsetCommit);
39132
+ const onGroupPathOffsetCommitRef = useRef78(onGroupPathOffsetCommit);
38664
39133
  onGroupPathOffsetCommitRef.current = onGroupPathOffsetCommit;
38665
- const onBoxSizeCommitRef = useRef77(onBoxSizeCommit);
39134
+ const onBoxSizeCommitRef = useRef78(onBoxSizeCommit);
38666
39135
  onBoxSizeCommitRef.current = onBoxSizeCommit;
38667
- const onRotationCommitRef = useRef77(onRotationCommit);
39136
+ const onRotationCommitRef = useRef78(onRotationCommit);
38668
39137
  onRotationCommitRef.current = onRotationCommit;
38669
- const onBlockedMoveRef = useRef77(onBlockedMove);
39138
+ const onBlockedMoveRef = useRef78(onBlockedMove);
38670
39139
  onBlockedMoveRef.current = onBlockedMove;
38671
- const onManualDragStartRef = useRef77(onManualDragStart);
39140
+ const onManualDragStartRef = useRef78(onManualDragStart);
38672
39141
  onManualDragStartRef.current = onManualDragStart;
38673
- const onCanvasPointerMoveRef = useRef77(onCanvasPointerMove);
39142
+ const onCanvasPointerMoveRef = useRef78(onCanvasPointerMove);
38674
39143
  onCanvasPointerMoveRef.current = onCanvasPointerMove;
38675
- const onCanvasPointerLeaveRef = useRef77(onCanvasPointerLeave);
39144
+ const onCanvasPointerLeaveRef = useRef78(onCanvasPointerLeave);
38676
39145
  onCanvasPointerLeaveRef.current = onCanvasPointerLeave;
38677
- const onSelectionChangeRef = useRef77(onSelectionChange);
39146
+ const onSelectionChangeRef = useRef78(onSelectionChange);
38678
39147
  onSelectionChangeRef.current = onSelectionChange;
38679
39148
  const {
38680
39149
  overlayRect,
@@ -38729,7 +39198,7 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38729
39198
  frame = requestAnimationFrame(update);
38730
39199
  return () => cancelAnimationFrame(frame);
38731
39200
  });
38732
- const offCanvasElementsRef = useRef77(/* @__PURE__ */ new Map());
39201
+ const offCanvasElementsRef = useRef78(/* @__PURE__ */ new Map());
38733
39202
  const [offCanvasRects, setOffCanvasRects] = useState71([]);
38734
39203
  useEffect53(() => {
38735
39204
  const iframe = iframeRef.current;
@@ -38766,6 +39235,7 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38766
39235
  iframeRef,
38767
39236
  boxRef,
38768
39237
  selectionRef,
39238
+ hoverSelectionRef,
38769
39239
  overlayRectRef,
38770
39240
  groupOverlayItemsRef,
38771
39241
  gestureRef,
@@ -38815,7 +39285,7 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38815
39285
  }
38816
39286
  const target = event.target;
38817
39287
  if (target?.closest('[data-dom-edit-selection-box="true"]')) return;
38818
- onCanvasMouseDown(event, { preferClipAncestor: false });
39288
+ onCanvasMouseDown(event, { hoverSelection: hoverSelectionRef.current });
38819
39289
  if (event.shiftKey) {
38820
39290
  suppressNextBoxMouseDownRef.current = true;
38821
39291
  suppressNextBoxClickRef.current = true;
@@ -38869,7 +39339,7 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
38869
39339
  event.stopPropagation();
38870
39340
  return;
38871
39341
  }
38872
- onCanvasMouseDown(event, { preferClipAncestor: false });
39342
+ onCanvasMouseDown(event, { hoverSelection: hoverSelectionRef.current });
38873
39343
  };
38874
39344
  const suppressBoxMouseDown = (e) => {
38875
39345
  if (!suppressNextBoxMouseDownRef.current) return;
@@ -39074,10 +39544,10 @@ var DomEditOverlay = memo35(function DomEditOverlay2({
39074
39544
  });
39075
39545
 
39076
39546
  // src/components/editor/MotionPathOverlay.tsx
39077
- import { memo as memo36, useEffect as useEffect55, useRef as useRef80, useState as useState73 } from "react";
39547
+ import { memo as memo36, useEffect as useEffect55, useRef as useRef81, useState as useState73 } from "react";
39078
39548
 
39079
39549
  // src/contexts/DomEditContext.tsx
39080
- import { createContext as createContext6, useCallback as useCallback89, useContext as useContext6, useMemo as useMemo32, useRef as useRef78 } from "react";
39550
+ import { createContext as createContext6, useCallback as useCallback90, useContext as useContext6, useMemo as useMemo32, useRef as useRef79 } from "react";
39081
39551
  import { jsx as jsx85 } from "react/jsx-runtime";
39082
39552
  var DomEditActionsContext = createContext6(null);
39083
39553
  var DomEditSelectionContext = createContext6(null);
@@ -39176,9 +39646,9 @@ function DomEditProvider({
39176
39646
  },
39177
39647
  children
39178
39648
  }) {
39179
- const commitMutationRef = useRef78(commitMutation);
39649
+ const commitMutationRef = useRef79(commitMutation);
39180
39650
  commitMutationRef.current = commitMutation;
39181
- const stableCommitMutation = useCallback89(
39651
+ const stableCommitMutation = useCallback90(
39182
39652
  (mutation, options) => commitMutationRef.current(mutation, options),
39183
39653
  []
39184
39654
  );
@@ -39533,7 +40003,7 @@ function commitCreatePath(targetSelector, position, x, y, commit) {
39533
40003
  }
39534
40004
 
39535
40005
  // src/components/editor/useMotionPathData.ts
39536
- import { useEffect as useEffect54, useRef as useRef79, useState as useState72 } from "react";
40006
+ import { useEffect as useEffect54, useRef as useRef80, useState as useState72 } from "react";
39537
40007
  function transformTranslate(el) {
39538
40008
  const t = el.ownerDocument?.defaultView?.getComputedStyle(el).transform;
39539
40009
  if (!t || t === "none") return { x: 0, y: 0 };
@@ -39599,7 +40069,7 @@ function hasMotionPathPlugin(iframe) {
39599
40069
  function useMotionPathData(iframeRef, selector) {
39600
40070
  const [rect, setRect] = useState72(null);
39601
40071
  const [geometry, setGeometry] = useState72(null);
39602
- const resolvedForRef = useRef79(null);
40072
+ const resolvedForRef = useRef80(null);
39603
40073
  const geometryResolved = resolvedForRef.current === selector;
39604
40074
  const [visibleInPreview, setVisibleInPreview] = useState72(true);
39605
40075
  const [home, setHome] = useState72(null);
@@ -39696,8 +40166,8 @@ var MotionPathOverlay = memo36(function MotionPathOverlay2({
39696
40166
  const armed = usePlayerStore((s) => s.motionPathArmed);
39697
40167
  const setMotionPathArmed = usePlayerStore((s) => s.setMotionPathArmed);
39698
40168
  const setMotionPathCreateAvailable = usePlayerStore((s) => s.setMotionPathCreateAvailable);
39699
- const dragRef = useRef80(null);
39700
- const parkTimerRef = useRef80(void 0);
40169
+ const dragRef = useRef81(null);
40170
+ const parkTimerRef = useRef81(void 0);
39701
40171
  const animId = editableAnimationId(selectedGsapAnimations ?? [], geometry?.kind ?? "linear");
39702
40172
  useEffect55(() => () => clearTimeout(parkTimerRef.current), [animId]);
39703
40173
  const createMode = geometryResolved && !geometry && Boolean(selection?.element) && !isPlaying;
@@ -40025,7 +40495,7 @@ var MotionPathOverlay = memo36(function MotionPathOverlay2({
40025
40495
  });
40026
40496
 
40027
40497
  // src/components/editor/SnapToolbar.tsx
40028
- import { memo as memo37, useCallback as useCallback90, useEffect as useEffect56, useRef as useRef81, useState as useState74 } from "react";
40498
+ import { memo as memo37, useCallback as useCallback91, useEffect as useEffect56, useRef as useRef82, useState as useState74 } from "react";
40029
40499
  import { MagnetStraight, GridFour, Path } from "@phosphor-icons/react";
40030
40500
  import { jsx as jsx88, jsxs as jsxs73 } from "react/jsx-runtime";
40031
40501
  var SNAP_DEFAULTS = {
@@ -40049,9 +40519,9 @@ var SnapToolbar = memo37(function SnapToolbar2({ onSnapChange }) {
40049
40519
  const motionPathCreateAvailable = usePlayerStore((s) => s.motionPathCreateAvailable);
40050
40520
  const motionPathArmed = usePlayerStore((s) => s.motionPathArmed);
40051
40521
  const setMotionPathArmed = usePlayerStore((s) => s.setMotionPathArmed);
40052
- const popoverRef = useRef81(null);
40053
- const gridButtonRef = useRef81(null);
40054
- const updatePrefs = useCallback90(
40522
+ const popoverRef = useRef82(null);
40523
+ const gridButtonRef = useRef82(null);
40524
+ const updatePrefs = useCallback91(
40055
40525
  (patch) => {
40056
40526
  setPrefs((prev) => {
40057
40527
  const next = { ...prev, ...patch };
@@ -40062,10 +40532,10 @@ var SnapToolbar = memo37(function SnapToolbar2({ onSnapChange }) {
40062
40532
  },
40063
40533
  [onSnapChange]
40064
40534
  );
40065
- const toggleSnap = useCallback90(() => {
40535
+ const toggleSnap = useCallback91(() => {
40066
40536
  updatePrefs({ snapEnabled: !prefs.snapEnabled });
40067
40537
  }, [prefs.snapEnabled, updatePrefs]);
40068
- const toggleGrid = useCallback90(() => {
40538
+ const toggleGrid = useCallback91(() => {
40069
40539
  updatePrefs({ gridVisible: !prefs.gridVisible });
40070
40540
  }, [prefs.gridVisible, updatePrefs]);
40071
40541
  useEffect56(() => {
@@ -40190,7 +40660,7 @@ var SnapToolbar = memo37(function SnapToolbar2({ onSnapChange }) {
40190
40660
  });
40191
40661
 
40192
40662
  // src/components/StudioFeedbackBar.tsx
40193
- import { memo as memo38, useState as useState75, useCallback as useCallback91, useRef as useRef82, useEffect as useEffect57 } from "react";
40663
+ import { memo as memo38, useState as useState75, useCallback as useCallback92, useRef as useRef83, useEffect as useEffect57 } from "react";
40194
40664
  import { Fragment as Fragment29, jsx as jsx89, jsxs as jsxs74 } from "react/jsx-runtime";
40195
40665
  var DEFAULT_FEEDBACK_INTERVAL = 10;
40196
40666
  var AUTO_DISMISS_MS = 2e4;
@@ -40249,8 +40719,8 @@ var StudioFeedbackBar = memo38(function StudioFeedbackBar2() {
40249
40719
  const [comment, setComment] = useState75("");
40250
40720
  const [submitted, setSubmitted] = useState75(false);
40251
40721
  const [exiting, setExiting] = useState75(false);
40252
- const inputRef = useRef82(null);
40253
- const dismissTimerRef = useRef82(null);
40722
+ const inputRef = useRef83(null);
40723
+ const dismissTimerRef = useRef83(null);
40254
40724
  useEffect57(() => {
40255
40725
  incrementSessionCount();
40256
40726
  const showTimer = setTimeout(() => {
@@ -40274,12 +40744,12 @@ var StudioFeedbackBar = memo38(function StudioFeedbackBar2() {
40274
40744
  inputRef.current.focus();
40275
40745
  }
40276
40746
  }, [rating]);
40277
- const handleDismiss = useCallback91(() => {
40747
+ const handleDismiss = useCallback92(() => {
40278
40748
  setExiting(true);
40279
40749
  markPrompted();
40280
40750
  setTimeout(() => setVisible(false), 300);
40281
40751
  }, []);
40282
- const handleSubmit = useCallback91(() => {
40752
+ const handleSubmit = useCallback92(() => {
40283
40753
  if (rating === null) return;
40284
40754
  trackStudioFeedback({
40285
40755
  rating,
@@ -40292,7 +40762,7 @@ var StudioFeedbackBar = memo38(function StudioFeedbackBar2() {
40292
40762
  setTimeout(() => setVisible(false), 300);
40293
40763
  }, 1500);
40294
40764
  }, [rating, comment]);
40295
- const handleRating = useCallback91((n) => {
40765
+ const handleRating = useCallback92((n) => {
40296
40766
  setRating(n);
40297
40767
  if (dismissTimerRef.current) {
40298
40768
  clearTimeout(dismissTimerRef.current);
@@ -40490,7 +40960,7 @@ function StudioPreviewArea({
40490
40960
  snapToGrid: p.snapToGrid ?? false
40491
40961
  };
40492
40962
  });
40493
- const resolveKeyframeTarget = useCallback92(
40963
+ const resolveKeyframeTarget = useCallback93(
40494
40964
  (pct) => {
40495
40965
  const cached2 = usePlayerStore.getState().keyframeCache.get(domEditSelection?.id ?? "");
40496
40966
  const kf = cached2?.keyframes.find((k) => Math.abs(k.percentage - pct) < 0.2);
@@ -40712,17 +41182,17 @@ function StudioPreviewArea({
40712
41182
 
40713
41183
  // src/components/StudioRightPanel.tsx
40714
41184
  import {
40715
- useCallback as useCallback102,
41185
+ useCallback as useCallback103,
40716
41186
  useMemo as useMemo34,
40717
- useRef as useRef87,
41187
+ useRef as useRef88,
40718
41188
  useState as useState85
40719
41189
  } from "react";
40720
41190
 
40721
41191
  // src/components/editor/LayersPanel.tsx
40722
- import { memo as memo39, useState as useState78, useCallback as useCallback94, useEffect as useEffect58, useRef as useRef84 } from "react";
41192
+ import { memo as memo39, useState as useState78, useCallback as useCallback95, useEffect as useEffect58, useRef as useRef85 } from "react";
40723
41193
 
40724
41194
  // src/components/editor/useLayerDrag.ts
40725
- import { useRef as useRef83, useState as useState77, useCallback as useCallback93 } from "react";
41195
+ import { useRef as useRef84, useState as useState77, useCallback as useCallback94 } from "react";
40726
41196
  var DRAG_THRESHOLD_PX2 = 4;
40727
41197
  function isLayerDraggable(layer) {
40728
41198
  if (!(layer.selector || layer.id)) return false;
@@ -40789,10 +41259,10 @@ function useLayerDrag({
40789
41259
  onReorder,
40790
41260
  onSingleSibling
40791
41261
  }) {
40792
- const dragRef = useRef83(null);
41262
+ const dragRef = useRef84(null);
40793
41263
  const [dragKey, setDragKey] = useState77(null);
40794
41264
  const [insertionLineY, setInsertionLineY] = useState77(null);
40795
- const handleRowPointerDown = useCallback93(
41265
+ const handleRowPointerDown = useCallback94(
40796
41266
  (layerIndex, e) => {
40797
41267
  if (e.button !== 0) return;
40798
41268
  const layer = visibleLayers[layerIndex];
@@ -40819,7 +41289,7 @@ function useLayerDrag({
40819
41289
  },
40820
41290
  [visibleLayers, scrollContainerRef, onSingleSibling]
40821
41291
  );
40822
- const handleContainerPointerMove = useCallback93(
41292
+ const handleContainerPointerMove = useCallback94(
40823
41293
  (e) => {
40824
41294
  const drag = dragRef.current;
40825
41295
  if (!drag) return;
@@ -40845,7 +41315,7 @@ function useLayerDrag({
40845
41315
  },
40846
41316
  [visibleLayers, scrollContainerRef]
40847
41317
  );
40848
- const handleContainerPointerUp = useCallback93(() => {
41318
+ const handleContainerPointerUp = useCallback94(() => {
40849
41319
  const drag = dragRef.current;
40850
41320
  dragRef.current = null;
40851
41321
  setDragKey(null);
@@ -40919,10 +41389,10 @@ var LayersPanel = memo39(function LayersPanel2() {
40919
41389
  } = useDomEditContext();
40920
41390
  const [layers, setLayers] = useState78([]);
40921
41391
  const [collapsed, setCollapsed] = useState78({});
40922
- const prevDocVersionRef = useRef84(0);
40923
- const scrollContainerRef = useRef84(null);
41392
+ const prevDocVersionRef = useRef85(0);
41393
+ const scrollContainerRef = useRef85(null);
40924
41394
  const isMasterView = !activeCompPath || activeCompPath === "index.html";
40925
- const collectLayers = useCallback94(() => {
41395
+ const collectLayers = useCallback95(() => {
40926
41396
  const iframe = previewIframeRef.current;
40927
41397
  if (!iframe) return;
40928
41398
  let doc = null;
@@ -40961,7 +41431,7 @@ var LayersPanel = memo39(function LayersPanel2() {
40961
41431
  return () => clearTimeout(timer);
40962
41432
  }
40963
41433
  }, [compositionLoading, collectLayers]);
40964
- const resolveSelection = useCallback94(
41434
+ const resolveSelection = useCallback95(
40965
41435
  (layer) => {
40966
41436
  let el = layer.element;
40967
41437
  if (!el.isConnected) {
@@ -40981,7 +41451,7 @@ var LayersPanel = memo39(function LayersPanel2() {
40981
41451
  },
40982
41452
  [activeCompPath, isMasterView, previewIframeRef, activeGroupElement]
40983
41453
  );
40984
- const seekToLayer = useCallback94(
41454
+ const seekToLayer = useCallback95(
40985
41455
  async (layer) => {
40986
41456
  const selection = await resolveSelection(layer);
40987
41457
  if (!selection) return;
@@ -41010,7 +41480,7 @@ var LayersPanel = memo39(function LayersPanel2() {
41010
41480
  },
41011
41481
  [currentTime, resolveSelection, timelineElements]
41012
41482
  );
41013
- const handleSelectLayer = useCallback94(
41483
+ const handleSelectLayer = useCallback95(
41014
41484
  async (layer) => {
41015
41485
  const selection = await resolveSelection(layer);
41016
41486
  if (!selection) return;
@@ -41019,7 +41489,7 @@ var LayersPanel = memo39(function LayersPanel2() {
41019
41489
  },
41020
41490
  [resolveSelection, applyDomSelection, seekToLayer]
41021
41491
  );
41022
- const handleLayerDoubleClick = useCallback94(
41492
+ const handleLayerDoubleClick = useCallback95(
41023
41493
  async (layer) => {
41024
41494
  const selection = await resolveSelection(layer);
41025
41495
  if (selection?.element.hasAttribute("data-hf-group")) {
@@ -41030,7 +41500,7 @@ var LayersPanel = memo39(function LayersPanel2() {
41030
41500
  },
41031
41501
  [resolveSelection, setActiveGroupElement, handleSelectLayer]
41032
41502
  );
41033
- const handleLayerHover = useCallback94(
41503
+ const handleLayerHover = useCallback95(
41034
41504
  async (layer) => {
41035
41505
  if (!layer) {
41036
41506
  updateDomEditHoverSelection(null);
@@ -41041,11 +41511,11 @@ var LayersPanel = memo39(function LayersPanel2() {
41041
41511
  },
41042
41512
  [resolveSelection, updateDomEditHoverSelection]
41043
41513
  );
41044
- const toggleCollapse = useCallback94((key, e) => {
41514
+ const toggleCollapse = useCallback95((key, e) => {
41045
41515
  e.stopPropagation();
41046
41516
  setCollapsed((prev) => ({ ...prev, [key]: !prev[key] }));
41047
41517
  }, []);
41048
- const handleReorder = useCallback94(
41518
+ const handleReorder = useCallback95(
41049
41519
  (event) => {
41050
41520
  const { siblingLayers, fromIndex, toIndex } = event;
41051
41521
  const reordered = [...siblingLayers];
@@ -41069,7 +41539,7 @@ var LayersPanel = memo39(function LayersPanel2() {
41069
41539
  );
41070
41540
  const selectedKey = domEditSelection ? getDomEditLayerKey(domEditSelection) : null;
41071
41541
  const visibleLayers = getVisibleLayers(layers, collapsed);
41072
- const handleSingleSibling = useCallback94(() => {
41542
+ const handleSingleSibling = useCallback95(() => {
41073
41543
  showToast("Only one layer at this level", "info");
41074
41544
  }, [showToast]);
41075
41545
  const {
@@ -41269,10 +41739,10 @@ function getVisibleLayers(layers, collapsed) {
41269
41739
  }
41270
41740
 
41271
41741
  // src/captions/components/CaptionPropertyPanel.tsx
41272
- import { memo as memo41, useCallback as useCallback96, useState as useState79 } from "react";
41742
+ import { memo as memo41, useCallback as useCallback97, useState as useState79 } from "react";
41273
41743
 
41274
41744
  // src/captions/components/CaptionAnimationPanel.tsx
41275
- import { memo as memo40, useCallback as useCallback95 } from "react";
41745
+ import { memo as memo40, useCallback as useCallback96 } from "react";
41276
41746
 
41277
41747
  // src/captions/components/shared.tsx
41278
41748
  import { jsx as jsx92, jsxs as jsxs77 } from "react/jsx-runtime";
@@ -41435,25 +41905,25 @@ var CaptionAnimationPanel = memo40(function CaptionAnimationPanel2() {
41435
41905
  }
41436
41906
  const group = resolvedGroupId ? model?.groups.get(resolvedGroupId) : void 0;
41437
41907
  const animation = group?.animation;
41438
- const handleEntranceChange = useCallback95(
41908
+ const handleEntranceChange = useCallback96(
41439
41909
  (update) => {
41440
41910
  if (resolvedGroupId) updateGroupAnimation(resolvedGroupId, "entrance", update);
41441
41911
  },
41442
41912
  [resolvedGroupId, updateGroupAnimation]
41443
41913
  );
41444
- const handleHighlightChange = useCallback95(
41914
+ const handleHighlightChange = useCallback96(
41445
41915
  (update) => {
41446
41916
  if (resolvedGroupId) updateGroupAnimation(resolvedGroupId, "highlight", update);
41447
41917
  },
41448
41918
  [resolvedGroupId, updateGroupAnimation]
41449
41919
  );
41450
- const handleExitChange = useCallback95(
41920
+ const handleExitChange = useCallback96(
41451
41921
  (update) => {
41452
41922
  if (resolvedGroupId) updateGroupAnimation(resolvedGroupId, "exit", update);
41453
41923
  },
41454
41924
  [resolvedGroupId, updateGroupAnimation]
41455
41925
  );
41456
- const handleApplyToAll = useCallback95(() => {
41926
+ const handleApplyToAll = useCallback96(() => {
41457
41927
  if (animation) applyAnimationToAll(animation);
41458
41928
  }, [animation, applyAnimationToAll]);
41459
41929
  if (!group || !resolvedGroupId || !animation) {
@@ -41531,7 +42001,7 @@ var CaptionPropertyPanel = memo41(function CaptionPropertyPanel2({
41531
42001
  ...groupStyle,
41532
42002
  ...segmentOverrides
41533
42003
  };
41534
- const applyToIframeDom = useCallback96(
42004
+ const applyToIframeDom = useCallback97(
41535
42005
  (updates) => {
41536
42006
  const iframe = iframeRef.current;
41537
42007
  if (!iframe || !model) return;
@@ -41605,7 +42075,7 @@ var CaptionPropertyPanel = memo41(function CaptionPropertyPanel2({
41605
42075
  },
41606
42076
  [iframeRef, model, selectedSegmentIds]
41607
42077
  );
41608
- const handleStyleChange = useCallback96(
42078
+ const handleStyleChange = useCallback97(
41609
42079
  (updates) => {
41610
42080
  if (selectedGroupId) {
41611
42081
  updateGroupStyle(selectedGroupId, updates);
@@ -41705,7 +42175,7 @@ var CaptionPropertyPanel = memo41(function CaptionPropertyPanel2({
41705
42175
  });
41706
42176
 
41707
42177
  // src/components/editor/BlockParamsPanel.tsx
41708
- import { memo as memo42, useState as useState80, useCallback as useCallback97 } from "react";
42178
+ import { memo as memo42, useState as useState80, useCallback as useCallback98 } from "react";
41709
42179
  import { jsx as jsx95, jsxs as jsxs80 } from "react/jsx-runtime";
41710
42180
  var BlockParamsPanel = memo42(function BlockParamsPanel2({
41711
42181
  blockTitle,
@@ -41720,7 +42190,7 @@ var BlockParamsPanel = memo42(function BlockParamsPanel2({
41720
42190
  }
41721
42191
  return initial;
41722
42192
  });
41723
- const handleChange = useCallback97((key, value) => {
42193
+ const handleChange = useCallback98((key, value) => {
41724
42194
  setValues((prev) => ({ ...prev, [key]: value }));
41725
42195
  }, []);
41726
42196
  return /* @__PURE__ */ jsxs80("div", { className: "flex flex-col h-full", children: [
@@ -41830,10 +42300,10 @@ function ParamControl({
41830
42300
  }
41831
42301
 
41832
42302
  // src/components/renders/RenderQueue.tsx
41833
- import { memo as memo44, useState as useState82, useRef as useRef85, useEffect as useEffect59 } from "react";
42303
+ import { memo as memo44, useState as useState82, useRef as useRef86, useEffect as useEffect59 } from "react";
41834
42304
 
41835
42305
  // src/components/renders/RenderQueueItem.tsx
41836
- import { memo as memo43, useCallback as useCallback98, useState as useState81 } from "react";
42306
+ import { memo as memo43, useCallback as useCallback99, useState as useState81 } from "react";
41837
42307
  import { Fragment as Fragment31, jsx as jsx96, jsxs as jsxs81 } from "react/jsx-runtime";
41838
42308
  function formatDuration(ms) {
41839
42309
  if (ms < 1e3) return `${ms}ms`;
@@ -41853,10 +42323,10 @@ var RenderQueueItem = memo43(function RenderQueueItem2({
41853
42323
  }) {
41854
42324
  const [hovered, setHovered] = useState81(false);
41855
42325
  const fileSrc = `/api/projects/${projectId}/renders/file/${job.filename}`;
41856
- const handleOpen = useCallback98(() => {
42326
+ const handleOpen = useCallback99(() => {
41857
42327
  window.open(fileSrc, "_blank");
41858
42328
  }, [fileSrc]);
41859
- const handleDownload = useCallback98(
42329
+ const handleDownload = useCallback99(
41860
42330
  (e) => {
41861
42331
  e.stopPropagation();
41862
42332
  const a = document.createElement("a");
@@ -42045,7 +42515,7 @@ var FORMAT_INFO = {
42045
42515
  };
42046
42516
  function FormatInfoTooltip({ format }) {
42047
42517
  const [open, setOpen] = useState82(false);
42048
- const timeoutRef = useRef85(void 0);
42518
+ const timeoutRef = useRef86(void 0);
42049
42519
  const show = () => {
42050
42520
  clearTimeout(timeoutRef.current);
42051
42521
  setOpen(true);
@@ -42212,7 +42682,7 @@ var RenderQueue = memo44(function RenderQueue2({
42212
42682
  isRendering,
42213
42683
  compositionDimensions
42214
42684
  }) {
42215
- const listRef = useRef85(null);
42685
+ const listRef = useRef86(null);
42216
42686
  useEffect59(() => {
42217
42687
  if (listRef.current) {
42218
42688
  listRef.current.scrollTo({ top: listRef.current.scrollHeight, behavior: "smooth" });
@@ -42295,11 +42765,11 @@ var RenderQueue = memo44(function RenderQueue2({
42295
42765
  });
42296
42766
 
42297
42767
  // src/components/panels/SlideshowPanel.tsx
42298
- import { useState as useState84, useEffect as useEffect60, useCallback as useCallback100, useRef as useRef86 } from "react";
42768
+ import { useState as useState84, useEffect as useEffect60, useCallback as useCallback101, useRef as useRef87 } from "react";
42299
42769
  import { parseSlideshowManifest } from "@hyperframes/core/slideshow";
42300
42770
 
42301
42771
  // src/components/panels/SlideshowSubPanels.tsx
42302
- import { useState as useState83, useCallback as useCallback99, useId } from "react";
42772
+ import { useState as useState83, useCallback as useCallback100, useId } from "react";
42303
42773
  import { jsx as jsx98, jsxs as jsxs83 } from "react/jsx-runtime";
42304
42774
  function SectionHeader({
42305
42775
  children,
@@ -42484,7 +42954,7 @@ function BranchTree({
42484
42954
  }) {
42485
42955
  const [newLabel, setNewLabel] = useState83("");
42486
42956
  const inputId = useId();
42487
- const handleCreate = useCallback99(() => {
42957
+ const handleCreate = useCallback100(() => {
42488
42958
  const label = newLabel.trim();
42489
42959
  if (!label) return;
42490
42960
  onCreateSequence(label);
@@ -42545,7 +43015,7 @@ function BranchItem({
42545
43015
  }) {
42546
43016
  const [editing, setEditing] = useState83(false);
42547
43017
  const [draft, setDraft] = useState83(seq.label);
42548
- const commitRename = useCallback99(() => {
43018
+ const commitRename = useCallback100(() => {
42549
43019
  const label = draft.trim();
42550
43020
  if (label && label !== seq.label) onRename(seq.id, label);
42551
43021
  setEditing(false);
@@ -42643,7 +43113,7 @@ function HotspotTool({
42643
43113
  const selectedElementId = domEditSelection?.element?.id ?? null;
42644
43114
  const selectedHfId = domEditSelection?.hfId ?? null;
42645
43115
  const elementKey = selectedElementId || selectedHfId;
42646
- const handleMakeHotspot = useCallback99(() => {
43116
+ const handleMakeHotspot = useCallback100(() => {
42647
43117
  if (!selectedSceneId || !targetSequenceId || !elementKey) return;
42648
43118
  const id = `hotspot-${elementKey}-${generateId()}`;
42649
43119
  const label = hotspotLabel.trim() || elementKey;
@@ -42932,8 +43402,8 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42932
43402
  );
42933
43403
  const currentTime = usePlayerStore((s) => s.currentTime);
42934
43404
  const { domEditSelection } = useDomEditSelectionContext();
42935
- const manifestRef = useRef86(manifest);
42936
- const notesCtrlRef = useRef86(makeSlideshowNotesController());
43405
+ const manifestRef = useRef87(manifest);
43406
+ const notesCtrlRef = useRef87(makeSlideshowNotesController());
42937
43407
  useEffect60(() => {
42938
43408
  if (!compHtml) {
42939
43409
  notesCtrlRef.current.flush();
@@ -42947,7 +43417,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42947
43417
  manifestRef.current = parsed;
42948
43418
  setSelectedSequenceId(null);
42949
43419
  }, [compHtml]);
42950
- const applyManifest = useCallback100(
43420
+ const applyManifest = useCallback101(
42951
43421
  async (next) => {
42952
43422
  const merged = notesCtrlRef.current.mergeIntoDiscrete(next);
42953
43423
  setManifest(merged);
@@ -42960,7 +43430,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42960
43430
  },
42961
43431
  [onPersist]
42962
43432
  );
42963
- const applyNotesManifest = useCallback100(
43433
+ const applyNotesManifest = useCallback101(
42964
43434
  (next) => {
42965
43435
  setManifest(next);
42966
43436
  manifestRef.current = next;
@@ -42974,7 +43444,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42974
43444
  ctrl.flush();
42975
43445
  };
42976
43446
  }, []);
42977
- const toggleSection = useCallback100((key) => {
43447
+ const toggleSection = useCallback101((key) => {
42978
43448
  setExpandedSections((prev) => {
42979
43449
  const next = new Set(prev);
42980
43450
  if (next.has(key)) {
@@ -42988,25 +43458,25 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
42988
43458
  const activeSlides = selectedSequenceId ? (manifest.slideSequences ?? []).find((s) => s.id === selectedSequenceId)?.slides ?? [] : manifest.slides;
42989
43459
  const selectedSlide = activeSlides.find((s) => s.sceneId === selectedSceneId);
42990
43460
  const sequences = manifest.slideSequences ?? [];
42991
- const handleSelectBranchSlide = useCallback100((sequenceId, sceneId) => {
43461
+ const handleSelectBranchSlide = useCallback101((sequenceId, sceneId) => {
42992
43462
  setSelectedSceneId(sceneId);
42993
43463
  setSelectedSequenceId(sequenceId);
42994
43464
  }, []);
42995
- const handleToggleSlide = useCallback100(
43465
+ const handleToggleSlide = useCallback101(
42996
43466
  (sceneId) => {
42997
43467
  applyManifest(toggleMainLineSlide(manifestRef.current, sceneId)).catch(() => {
42998
43468
  });
42999
43469
  },
43000
43470
  [applyManifest]
43001
43471
  );
43002
- const handleReorder = useCallback100(
43472
+ const handleReorder = useCallback101(
43003
43473
  (sceneId, dir) => {
43004
43474
  applyManifest(reorderMainLineSlide(manifestRef.current, sceneId, dir)).catch(() => {
43005
43475
  });
43006
43476
  },
43007
43477
  [applyManifest]
43008
43478
  );
43009
- const handleSetNotes = useCallback100(
43479
+ const handleSetNotes = useCallback101(
43010
43480
  (notes) => {
43011
43481
  if (!selectedSceneId) return;
43012
43482
  applyNotesManifest(
@@ -43015,7 +43485,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43015
43485
  },
43016
43486
  [selectedSceneId, selectedSequenceId, applyNotesManifest]
43017
43487
  );
43018
- const handleMarkFragment = useCallback100(() => {
43488
+ const handleMarkFragment = useCallback101(() => {
43019
43489
  if (!selectedSceneId) return;
43020
43490
  applyManifest(
43021
43491
  addFragment(
@@ -43027,7 +43497,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43027
43497
  ).catch(() => {
43028
43498
  });
43029
43499
  }, [selectedSceneId, selectedSequenceId, currentTime, applyManifest]);
43030
- const handleRemoveFragment = useCallback100(
43500
+ const handleRemoveFragment = useCallback101(
43031
43501
  (time) => {
43032
43502
  if (!selectedSceneId) return;
43033
43503
  applyManifest(
@@ -43037,7 +43507,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43037
43507
  },
43038
43508
  [selectedSceneId, selectedSequenceId, applyManifest]
43039
43509
  );
43040
- const handleCreateSequence = useCallback100(
43510
+ const handleCreateSequence = useCallback101(
43041
43511
  (label) => {
43042
43512
  const id = `seq-${generateId()}`;
43043
43513
  applyManifest(createSequence(manifestRef.current, id, label)).catch(() => {
@@ -43045,14 +43515,14 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43045
43515
  },
43046
43516
  [applyManifest]
43047
43517
  );
43048
- const handleRenameSequence = useCallback100(
43518
+ const handleRenameSequence = useCallback101(
43049
43519
  (id, label) => {
43050
43520
  applyManifest(renameSequence(manifestRef.current, id, label)).catch(() => {
43051
43521
  });
43052
43522
  },
43053
43523
  [applyManifest]
43054
43524
  );
43055
- const handleDeleteSequence = useCallback100(
43525
+ const handleDeleteSequence = useCallback101(
43056
43526
  (id) => {
43057
43527
  const seq = (manifestRef.current.slideSequences ?? []).find((s) => s.id === id);
43058
43528
  const count = seq?.slides.length ?? 0;
@@ -43066,7 +43536,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43066
43536
  },
43067
43537
  [applyManifest]
43068
43538
  );
43069
- const handleAssign = useCallback100(
43539
+ const handleAssign = useCallback101(
43070
43540
  (sequenceId, sceneId, assign) => {
43071
43541
  applyManifest(assignToBranch(manifestRef.current, sequenceId, sceneId, assign)).catch(
43072
43542
  () => {
@@ -43075,7 +43545,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43075
43545
  },
43076
43546
  [applyManifest]
43077
43547
  );
43078
- const handleAddHotspot = useCallback100(
43548
+ const handleAddHotspot = useCallback101(
43079
43549
  (sceneId, hotspot) => {
43080
43550
  applyManifest(
43081
43551
  addHotspot(manifestRef.current, sceneId, hotspot, selectedSequenceId ?? void 0)
@@ -43084,7 +43554,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43084
43554
  },
43085
43555
  [selectedSequenceId, applyManifest]
43086
43556
  );
43087
- const handleRemoveHotspot = useCallback100(
43557
+ const handleRemoveHotspot = useCallback101(
43088
43558
  (sceneId, hotspotId) => {
43089
43559
  applyManifest(
43090
43560
  removeHotspot(manifestRef.current, sceneId, hotspotId, selectedSequenceId ?? void 0)
@@ -43188,7 +43658,7 @@ function SlideshowPanel({ scenes, onPersist, onPersistNotes }) {
43188
43658
  }
43189
43659
 
43190
43660
  // src/hooks/useSlideshowPersist.ts
43191
- import { useCallback as useCallback101 } from "react";
43661
+ import { useCallback as useCallback102 } from "react";
43192
43662
 
43193
43663
  // src/utils/setSlideshowManifest.ts
43194
43664
  import {
@@ -43245,7 +43715,7 @@ function useSlideshowPersist({
43245
43715
  domEditSaveTimestampRef,
43246
43716
  coalesceKey
43247
43717
  }) {
43248
- return useCallback101(
43718
+ return useCallback102(
43249
43719
  async (manifest) => {
43250
43720
  if (!sdkSession) return;
43251
43721
  const path = activeCompPath ?? "index.html";
@@ -43382,8 +43852,8 @@ function StudioRightPanel({
43382
43852
  coalesceKey: activeCompPath ? `slideshow-notes:${activeCompPath}` : "slideshow-notes"
43383
43853
  });
43384
43854
  const [layersPanePercent, setLayersPanePercent] = useState85(40);
43385
- const splitContainerRef = useRef87(null);
43386
- const splitDragRef = useRef87(null);
43855
+ const splitContainerRef = useRef88(null);
43856
+ const splitDragRef = useRef88(null);
43387
43857
  const renderJobs = renderQueue.jobs;
43388
43858
  const inspectorTabActive = rightPanelTab === "design" || rightPanelTab === "layers";
43389
43859
  const slideshowScenes = useMemo34(() => {
@@ -43408,7 +43878,7 @@ function StudioRightPanel({
43408
43878
  }
43409
43879
  toggleRightInspectorPane(pane);
43410
43880
  };
43411
- const handleInspectorSplitResizeStart = useCallback102(
43881
+ const handleInspectorSplitResizeStart = useCallback103(
43412
43882
  (event) => {
43413
43883
  event.preventDefault();
43414
43884
  event.currentTarget.setPointerCapture(event.pointerId);
@@ -43421,7 +43891,7 @@ function StudioRightPanel({
43421
43891
  },
43422
43892
  [layersPanePercent]
43423
43893
  );
43424
- const handleInspectorSplitResizeMove = useCallback102((event) => {
43894
+ const handleInspectorSplitResizeMove = useCallback103((event) => {
43425
43895
  const drag = splitDragRef.current;
43426
43896
  if (!drag || drag.height <= 0) return;
43427
43897
  const deltaPercent = (event.clientY - drag.startY) / drag.height * 100;
@@ -43431,7 +43901,7 @@ function StudioRightPanel({
43431
43901
  );
43432
43902
  setLayersPanePercent(next);
43433
43903
  }, []);
43434
- const handleInspectorSplitResizeEnd = useCallback102(() => {
43904
+ const handleInspectorSplitResizeEnd = useCallback103(() => {
43435
43905
  splitDragRef.current = null;
43436
43906
  }, []);
43437
43907
  const propertyPanel = /* @__PURE__ */ jsx100(
@@ -43619,10 +44089,10 @@ function StudioRightPanel({
43619
44089
  }
43620
44090
 
43621
44091
  // src/components/TimelineToolbar.tsx
43622
- import { useRef as useRef88 } from "react";
44092
+ import { useRef as useRef89 } from "react";
43623
44093
 
43624
44094
  // src/hooks/useEnableKeyframes.ts
43625
- import { useCallback as useCallback103 } from "react";
44095
+ import { useCallback as useCallback104 } from "react";
43626
44096
  function animatedProps(anim) {
43627
44097
  if (!anim) return ["x", "y"];
43628
44098
  const own = Object.keys(anim.properties ?? {});
@@ -43832,7 +44302,7 @@ async function applyArcWaypointAtPlayhead(session, sel, arcAnim, t, iframe) {
43832
44302
  );
43833
44303
  }
43834
44304
  function useEnableKeyframes(sessionRef) {
43835
- return useCallback103(async () => {
44305
+ return useCallback104(async () => {
43836
44306
  const session = sessionRef.current;
43837
44307
  if (!session) return;
43838
44308
  const sel = session.domEditSelection;
@@ -43891,7 +44361,7 @@ function useEnableKeyframes(sessionRef) {
43891
44361
  }
43892
44362
 
43893
44363
  // src/hooks/useKeyframeKeyboard.ts
43894
- import { useEffect as useEffect61, useCallback as useCallback104 } from "react";
44364
+ import { useEffect as useEffect61, useCallback as useCallback105 } from "react";
43895
44365
  function isTextInput(el) {
43896
44366
  if (!el) return false;
43897
44367
  const tag = el.tagName;
@@ -43908,7 +44378,7 @@ function useKeyframeKeyboard({
43908
44378
  onToggleExpand,
43909
44379
  onNudgeKeyframe
43910
44380
  }) {
43911
- const handler = useCallback104(
44381
+ const handler = useCallback105(
43912
44382
  (e) => {
43913
44383
  if (!enabled) return;
43914
44384
  if (isTextInput(document.activeElement)) return;
@@ -43970,7 +44440,7 @@ function useKeyframeKeyboard({
43970
44440
  import { jsx as jsx101, jsxs as jsxs86 } from "react/jsx-runtime";
43971
44441
  function useKeyframeToggle(session) {
43972
44442
  const currentTime = usePlayerStore((s) => s.currentTime);
43973
- const sessionRef = useRef88(session);
44443
+ const sessionRef = useRef89(session);
43974
44444
  sessionRef.current = session;
43975
44445
  const onToggle = useEnableKeyframes(
43976
44446
  sessionRef
@@ -44195,13 +44665,13 @@ function TimelineToolbar({
44195
44665
  }
44196
44666
 
44197
44667
  // src/hooks/useStoryboard.ts
44198
- import { useCallback as useCallback105, useEffect as useEffect62, useState as useState86 } from "react";
44668
+ import { useCallback as useCallback106, useEffect as useEffect62, useState as useState86 } from "react";
44199
44669
  function useStoryboard(projectId) {
44200
44670
  const [data, setData] = useState86(null);
44201
44671
  const [loading, setLoading] = useState86(true);
44202
44672
  const [error, setError] = useState86(null);
44203
44673
  const [reloadKey, setReloadKey] = useState86(0);
44204
- const reload = useCallback105(() => setReloadKey((k) => k + 1), []);
44674
+ const reload = useCallback106(() => setReloadKey((k) => k + 1), []);
44205
44675
  useEffect62(() => {
44206
44676
  if (!projectId) return;
44207
44677
  let cancelled = false;
@@ -44425,7 +44895,7 @@ function StoryboardScriptPanel({ script }) {
44425
44895
  }
44426
44896
 
44427
44897
  // src/components/storyboard/StoryboardSourceEditor.tsx
44428
- import { useCallback as useCallback106, useEffect as useEffect64, useMemo as useMemo35, useRef as useRef89, useState as useState88 } from "react";
44898
+ import { useCallback as useCallback107, useEffect as useEffect64, useMemo as useMemo35, useRef as useRef90, useState as useState88 } from "react";
44429
44899
  import { marked } from "marked";
44430
44900
  import DOMPurify from "dompurify";
44431
44901
  import { jsx as jsx108, jsxs as jsxs91 } from "react/jsx-runtime";
@@ -44455,7 +44925,7 @@ function useEditableFile(path, onSaved) {
44455
44925
  cancelled = true;
44456
44926
  };
44457
44927
  }, [path, readProjectFile]);
44458
- const save = useCallback106(() => {
44928
+ const save = useCallback107(() => {
44459
44929
  if (saving) return;
44460
44930
  setSaving(true);
44461
44931
  setError(null);
@@ -44474,7 +44944,7 @@ function hardenLinks(node) {
44474
44944
  }
44475
44945
  function useMarkdownPreview(source) {
44476
44946
  const [debounced, setDebounced] = useState88(source);
44477
- const primed = useRef89(false);
44947
+ const primed = useRef90(false);
44478
44948
  useEffect64(() => {
44479
44949
  if (!primed.current && source !== "") {
44480
44950
  primed.current = true;
@@ -44580,7 +45050,7 @@ function StoryboardSourceEditor({
44580
45050
  }
44581
45051
 
44582
45052
  // src/components/storyboard/StoryboardFrameFocus.tsx
44583
- import { useCallback as useCallback107, useEffect as useEffect65, useState as useState89 } from "react";
45053
+ import { useCallback as useCallback108, useEffect as useEffect65, useState as useState89 } from "react";
44584
45054
  import { setFrameStatus, setFrameVoiceover } from "@hyperframes/core/storyboard";
44585
45055
  import { jsx as jsx109, jsxs as jsxs92 } from "react/jsx-runtime";
44586
45056
  function StoryboardFrameFocus({
@@ -44598,7 +45068,7 @@ function StoryboardFrameFocus({
44598
45068
  const [draft, setDraft] = useState89(frame.voiceover ?? "");
44599
45069
  const [busy, setBusy] = useState89(false);
44600
45070
  const [error, setError] = useState89(null);
44601
- const applyEdit = useCallback107(
45071
+ const applyEdit = useCallback108(
44602
45072
  async (edit) => {
44603
45073
  if (busy) return;
44604
45074
  setBusy(true);
@@ -44997,7 +45467,7 @@ function useServerConnection() {
44997
45467
  import { jsx as jsx113, jsxs as jsxs96 } from "react/jsx-runtime";
44998
45468
  function StudioApp() {
44999
45469
  const { projectId, resolving, waitingForServer } = useServerConnection();
45000
- const initialUrlStateRef = useRef90(readStudioUrlStateFromWindow());
45470
+ const initialUrlStateRef = useRef91(readStudioUrlStateFromWindow());
45001
45471
  const viewModeValue = useViewModeState();
45002
45472
  useEffect67(() => {
45003
45473
  if (resolving || waitingForServer) return;
@@ -45015,10 +45485,10 @@ function StudioApp() {
45015
45485
  const [refreshKey, setRefreshKey] = useState92(0);
45016
45486
  const [previewDocumentVersion, setPreviewDocumentVersion] = useState92(0);
45017
45487
  const [blockPreview, setBlockPreview] = useState92(null);
45018
- const previewIframeRef = useRef90(null);
45019
- const activeCompPathRef = useRef90(activeCompPath);
45488
+ const previewIframeRef = useRef91(null);
45489
+ const activeCompPathRef = useRef91(activeCompPath);
45020
45490
  activeCompPathRef.current = activeCompPath;
45021
- const leftSidebarRef = useRef90(null);
45491
+ const leftSidebarRef = useRef91(null);
45022
45492
  const renderQueue = useRenderQueue(projectId);
45023
45493
  const captionEditMode = useCaptionStore((s) => s.isEditMode);
45024
45494
  const captionHasSelection = useCaptionStore((s) => s.selectedSegmentIds.size > 0);
@@ -45033,8 +45503,8 @@ function StudioApp() {
45033
45503
  const maxEnd = timelineElements.length > 0 ? Math.max(...timelineElements.map((el) => el.start + el.duration)) : 0;
45034
45504
  return Math.max(timelineDuration, maxEnd);
45035
45505
  }, [timelineDuration, timelineElements]);
45036
- const refreshTimersRef = useRef90([]);
45037
- const refreshPreviewDocumentVersion = useCallback108(() => {
45506
+ const refreshTimersRef = useRef91([]);
45507
+ const refreshPreviewDocumentVersion = useCallback109(() => {
45038
45508
  for (const id of refreshTimersRef.current) clearTimeout(id);
45039
45509
  refreshTimersRef.current = [];
45040
45510
  setPreviewDocumentVersion((v) => v + 1);
@@ -45052,7 +45522,7 @@ function StudioApp() {
45052
45522
  const [timelineVisible, setTimelineVisible] = useState92(
45053
45523
  () => initialUrlStateRef.current.timelineVisible ?? readStudioUiPreferences().timelineVisible ?? true
45054
45524
  );
45055
- const toggleTimelineVisibility = useCallback108(() => {
45525
+ const toggleTimelineVisibility = useCallback109(() => {
45056
45526
  setTimelineVisible((v) => {
45057
45527
  writeStudioUiPreferences({ timelineVisible: !v });
45058
45528
  return !v;
@@ -45064,10 +45534,10 @@ function StudioApp() {
45064
45534
  rightPanelTab: initialUrlStateRef.current.rightPanelTab
45065
45535
  });
45066
45536
  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), []);
45537
+ const domEditSaveTimestampRef = useRef91(0);
45538
+ const pendingTimelineEditPathRef = useRef91(/* @__PURE__ */ new Set());
45539
+ const isGestureRecordingRef = useRef91(false);
45540
+ const reloadPreview = useCallback109(() => setRefreshKey((k) => k + 1), []);
45071
45541
  const fileManager = useFileManager({
45072
45542
  projectId,
45073
45543
  showToast,
@@ -45136,18 +45606,18 @@ function StudioApp() {
45136
45606
  setRightCollapsed: panelLayout.setRightCollapsed,
45137
45607
  setRightPanelTab: panelLayout.setRightPanelTab
45138
45608
  });
45139
- const clearDomSelectionRef = useRef90(() => {
45609
+ const clearDomSelectionRef = useRef91(() => {
45140
45610
  });
45141
- const domEditSelectionBridgeRef = useRef90(null);
45142
- const handleDomEditElementDeleteRef = useRef90(
45611
+ const domEditSelectionBridgeRef = useRef91(null);
45612
+ const handleDomEditElementDeleteRef = useRef91(
45143
45613
  async () => {
45144
45614
  }
45145
45615
  );
45146
45616
  const domEditDeleteBridge = (s) => handleDomEditElementDeleteRef.current(s);
45147
- const resetKeyframesRef = useRef90(() => false);
45148
- const deleteSelectedKeyframesRef = useRef90(() => {
45617
+ const resetKeyframesRef = useRef91(() => false);
45618
+ const deleteSelectedKeyframesRef = useRef91(() => {
45149
45619
  });
45150
- const invalidateGsapCacheRef = useRef90(() => {
45620
+ const invalidateGsapCacheRef = useRef91(() => {
45151
45621
  });
45152
45622
  const { handleCopy, handlePaste, handleCut } = useClipboard({
45153
45623
  projectId,
@@ -45190,7 +45660,7 @@ function StudioApp() {
45190
45660
  forceReloadSdkSession: sdkHandle.forceReload,
45191
45661
  onToggleRecording: STUDIO_KEYFRAMES_ENABLED ? () => handleToggleRecordingRef.current() : void 0
45192
45662
  });
45193
- const sidebarTabRef = useRef90({
45663
+ const sidebarTabRef = useRef91({
45194
45664
  select: (t) => leftSidebarRef.current?.selectTab(t),
45195
45665
  get: () => leftSidebarRef.current?.getTab() ?? "compositions"
45196
45666
  });
@@ -45283,9 +45753,9 @@ function StudioApp() {
45283
45753
  resetErrors: resetConsoleErrors
45284
45754
  } = useConsoleErrorCapture(previewIframe);
45285
45755
  const dragOverlay = useDragOverlay(fileManager.handleImportFiles);
45286
- const handleToggleRecordingRef = useRef90(() => {
45756
+ const handleToggleRecordingRef = useRef91(() => {
45287
45757
  });
45288
- const domEditSessionRef = useRef90(domEditSession);
45758
+ const domEditSessionRef = useRef91(domEditSession);
45289
45759
  domEditSessionRef.current = domEditSession;
45290
45760
  const { gestureState, gestureRecording, handleToggleRecording } = useGestureCommit({
45291
45761
  domEditSessionRef,
@@ -45294,7 +45764,7 @@ function StudioApp() {
45294
45764
  isGestureRecordingRef
45295
45765
  });
45296
45766
  handleToggleRecordingRef.current = handleToggleRecording;
45297
- const canvasRectRef = useRef90(null);
45767
+ const canvasRectRef = useRef91(null);
45298
45768
  useLayoutEffect3(() => {
45299
45769
  if (gestureState !== "recording" || !previewIframe) {
45300
45770
  canvasRectRef.current = null;
@@ -45303,7 +45773,7 @@ function StudioApp() {
45303
45773
  const r = previewIframe.getBoundingClientRect();
45304
45774
  canvasRectRef.current = { left: r.left, top: r.top, width: r.width, height: r.height };
45305
45775
  }, [gestureState, previewIframe]);
45306
- const handlePreviewIframeRef = useCallback108(
45776
+ const handlePreviewIframeRef = useCallback109(
45307
45777
  (iframe) => {
45308
45778
  previewIframeRef.current = iframe;
45309
45779
  setPreviewIframe(iframe);
@@ -45314,7 +45784,7 @@ function StudioApp() {
45314
45784
  },
45315
45785
  [appHotkeys, resetConsoleErrors, refreshPreviewDocumentVersion]
45316
45786
  );
45317
- const handleSelectComposition = useCallback108(
45787
+ const handleSelectComposition = useCallback109(
45318
45788
  (comp) => {
45319
45789
  setActiveCompPath(comp.endsWith(".html") ? comp : null);
45320
45790
  fileManager.setEditingFile({ path: comp, content: null });
@@ -45520,18 +45990,18 @@ function StudioApp() {
45520
45990
  }
45521
45991
 
45522
45992
  // src/hooks/useElementPicker.ts
45523
- import { useState as useState93, useCallback as useCallback109, useRef as useRef91 } from "react";
45993
+ import { useState as useState93, useCallback as useCallback110, useRef as useRef92 } from "react";
45524
45994
  function useElementPicker(iframeRef, options) {
45525
45995
  const [isPickMode, setIsPickMode] = useState93(false);
45526
45996
  const [pickedElement, setPickedElement] = useState93(null);
45527
- const activeOverrideRef = useRef91(null);
45528
- const getActiveIframe = useCallback109(() => {
45997
+ const activeOverrideRef = useRef92(null);
45998
+ const getActiveIframe = useCallback110(() => {
45529
45999
  return activeOverrideRef.current ?? iframeRef.current;
45530
46000
  }, [iframeRef]);
45531
- const setActiveIframe = useCallback109((el) => {
46001
+ const setActiveIframe = useCallback110((el) => {
45532
46002
  activeOverrideRef.current = el;
45533
46003
  }, []);
45534
- const enablePick = useCallback109(() => {
46004
+ const enablePick = useCallback110(() => {
45535
46005
  try {
45536
46006
  getActiveIframe()?.contentWindow?.postMessage(
45537
46007
  { source: "hf-parent", type: "control", action: "enable-pick-mode" },
@@ -45541,7 +46011,7 @@ function useElementPicker(iframeRef, options) {
45541
46011
  } catch {
45542
46012
  }
45543
46013
  }, [getActiveIframe]);
45544
- const disablePick = useCallback109(() => {
46014
+ const disablePick = useCallback110(() => {
45545
46015
  try {
45546
46016
  getActiveIframe()?.contentWindow?.postMessage(
45547
46017
  { source: "hf-parent", type: "control", action: "disable-pick-mode" },
@@ -45551,7 +46021,7 @@ function useElementPicker(iframeRef, options) {
45551
46021
  }
45552
46022
  setIsPickMode(false);
45553
46023
  }, [getActiveIframe]);
45554
- const clearPick = useCallback109(() => {
46024
+ const clearPick = useCallback110(() => {
45555
46025
  setPickedElement(null);
45556
46026
  }, []);
45557
46027
  useMountEffect(() => {
@@ -45603,9 +46073,9 @@ function useElementPicker(iframeRef, options) {
45603
46073
  window.addEventListener("message", handleMessage);
45604
46074
  return () => window.removeEventListener("message", handleMessage);
45605
46075
  });
45606
- const optionsRef = useRef91(options);
46076
+ const optionsRef = useRef92(options);
45607
46077
  optionsRef.current = options;
45608
- const syncToSource = useCallback109(
46078
+ const syncToSource = useCallback110(
45609
46079
  (elementId, selector, op) => {
45610
46080
  const opts = optionsRef.current;
45611
46081
  if (!opts?.workspaceFiles || !opts.onSyncFiles || !elementId) return;
@@ -45621,7 +46091,7 @@ function useElementPicker(iframeRef, options) {
45621
46091
  },
45622
46092
  []
45623
46093
  );
45624
- const setStyle = useCallback109(
46094
+ const setStyle = useCallback110(
45625
46095
  (prop, value) => {
45626
46096
  const activeIframe = getActiveIframe();
45627
46097
  if (!pickedElement?.selector || !activeIframe) return;
@@ -45663,7 +46133,7 @@ function useElementPicker(iframeRef, options) {
45663
46133
  },
45664
46134
  [pickedElement, getActiveIframe, syncToSource]
45665
46135
  );
45666
- const setDataAttr = useCallback109(
46136
+ const setDataAttr = useCallback110(
45667
46137
  (attr, value) => {
45668
46138
  const activeIframe = getActiveIframe();
45669
46139
  if (!pickedElement?.selector || !activeIframe) return;
@@ -45691,7 +46161,7 @@ function useElementPicker(iframeRef, options) {
45691
46161
  },
45692
46162
  [pickedElement, getActiveIframe, syncToSource]
45693
46163
  );
45694
- const setTextContent = useCallback109(
46164
+ const setTextContent = useCallback110(
45695
46165
  (text) => {
45696
46166
  const activeIframe = getActiveIframe();
45697
46167
  if (!pickedElement?.selector || !activeIframe) return;
@@ -45714,7 +46184,7 @@ function useElementPicker(iframeRef, options) {
45714
46184
  },
45715
46185
  [pickedElement, getActiveIframe, syncToSource]
45716
46186
  );
45717
- const activeIframeRef = useRef91(null);
46187
+ const activeIframeRef = useRef92(null);
45718
46188
  activeIframeRef.current = getActiveIframe();
45719
46189
  return {
45720
46190
  isPickMode,