@hyperframes/studio 0.8.190 → 0.8.191

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.
package/dist/index.js CHANGED
@@ -48,7 +48,7 @@ import {
48
48
  usePreviewVariablesStore,
49
49
  whenPreviewBooted,
50
50
  writeStudioUiPreferences
51
- } from "./chunk-563JPIL4.js";
51
+ } from "./chunk-IWO4UENE.js";
52
52
  import {
53
53
  Button,
54
54
  ContextMenu,
@@ -36337,11 +36337,11 @@ function ownNodeTransform(node, ops, getStyle) {
36337
36337
  const spin = individualRotateDegrees(style.rotate);
36338
36338
  return spin === 0 ? own : ops.compose(ops.fromRotate(spin), own);
36339
36339
  }
36340
- function composeElementTransform(element, ops, getStyle, memo63) {
36340
+ function composeElementTransform(element, ops, getStyle, memo64) {
36341
36341
  const pending2 = [];
36342
36342
  let above;
36343
36343
  for (let node = element; node; node = node.parentElement) {
36344
- above = memo63?.get(node);
36344
+ above = memo64?.get(node);
36345
36345
  if (above !== void 0) break;
36346
36346
  pending2.push(node);
36347
36347
  if (node.hasAttribute(COMPOSITION_ROOT_ATTR)) break;
@@ -36353,7 +36353,7 @@ function composeElementTransform(element, ops, getStyle, memo63) {
36353
36353
  const own = ownNodeTransform(node, ops, getStyle);
36354
36354
  acc = own === null ? null : ops.compose(acc, own);
36355
36355
  }
36356
- memo63?.set(node, acc);
36356
+ memo64?.set(node, acc);
36357
36357
  }
36358
36358
  return acc;
36359
36359
  }
@@ -36636,11 +36636,11 @@ function createOverlayMeasurePass() {
36636
36636
  sourceBoundaryRect: /* @__PURE__ */ new Map()
36637
36637
  };
36638
36638
  }
36639
- function readThroughPass(memo63, key2, compute) {
36640
- const answered = memo63.get(key2);
36639
+ function readThroughPass(memo64, key2, compute) {
36640
+ const answered = memo64.get(key2);
36641
36641
  if (answered !== void 0) return answered;
36642
36642
  const value = compute();
36643
- memo63.set(key2, value);
36643
+ memo64.set(key2, value);
36644
36644
  return value;
36645
36645
  }
36646
36646
 
@@ -51878,6 +51878,7 @@ function DockStripActions({
51878
51878
  // src/components/dock/DockTab.tsx
51879
51879
  import { useCallback as useCallback72, useSyncExternalStore as useSyncExternalStore9 } from "react";
51880
51880
  import {
51881
+ Aperture,
51881
51882
  BracketsCurly,
51882
51883
  ChartBarHorizontal,
51883
51884
  Code,
@@ -51903,7 +51904,8 @@ var TAB_ICONS = {
51903
51904
  layers: Stack2,
51904
51905
  renders: FilmSlate,
51905
51906
  variables: BracketsCurly,
51906
- slideshow: Presentation
51907
+ slideshow: Presentation,
51908
+ motionBlur: Aperture
51907
51909
  };
51908
51910
  function DockTab({ api }) {
51909
51911
  const subscribe2 = useCallback72(
@@ -52041,7 +52043,7 @@ var MIN_SIDE_W = 200;
52041
52043
  var MIN_SIDE_W_FLOOR = 120;
52042
52044
  var DEFAULT_TIMELINE_H = 360;
52043
52045
  var DEFAULT_LEFT = ["compositions", "assets", "code", "catalog"];
52044
- var DEFAULT_RIGHT = ["design", "layers", "renders", "variables"];
52046
+ var DEFAULT_RIGHT = ["design", "layers", "renders", "variables", "motionBlur"];
52045
52047
  function defaultSideWidths(viewportWidth) {
52046
52048
  const floor = sideMinimumWidth(viewportWidth);
52047
52049
  const left = Math.max(floor, Math.min(384, Math.round(viewportWidth * 0.257)));
@@ -72917,7 +72919,7 @@ var FileTree = memo43(function FileTree2({
72917
72919
  });
72918
72920
 
72919
72921
  // src/App.tsx
72920
- import { useState as useState164, useCallback as useCallback194, useRef as useRef189, useMemo as useMemo89 } from "react";
72922
+ import { useState as useState165, useCallback as useCallback195, useRef as useRef190, useMemo as useMemo90 } from "react";
72921
72923
 
72922
72924
  // src/hooks/useStableHandlers.ts
72923
72925
  import { useRef as useRef117 } from "react";
@@ -88887,19 +88889,26 @@ var DEFAULT_ANIMATION_SET = {
88887
88889
 
88888
88890
  // src/captions/parser.ts
88889
88891
  function buildCaptionModel(transcript, options) {
88890
- const { width, height, duration, wordsPerGroup = 5 } = options;
88892
+ const { wordsPerGroup = 5 } = options;
88893
+ const wordGroups = [];
88894
+ for (let offset = 0; offset < transcript.length; offset += wordsPerGroup) {
88895
+ wordGroups.push(transcript.slice(offset, offset + wordsPerGroup));
88896
+ }
88897
+ return buildGroupedCaptionModel(wordGroups, options);
88898
+ }
88899
+ function buildGroupedCaptionModel(wordGroups, { width, height, duration }) {
88891
88900
  const segments = /* @__PURE__ */ new Map();
88892
88901
  const groups = /* @__PURE__ */ new Map();
88893
88902
  const groupOrder = [];
88894
- for (let groupIdx = 0; groupIdx < transcript.length; groupIdx += wordsPerGroup) {
88895
- const chunk2 = transcript.slice(groupIdx, groupIdx + wordsPerGroup);
88896
- const groupId = `group-${groupIdx / wordsPerGroup}`;
88903
+ for (const chunk2 of wordGroups) {
88904
+ const groupId = `group-${groups.size}`;
88897
88905
  const segmentIds = [];
88898
88906
  chunk2.forEach((word, wordIdx) => {
88899
- const segmentId = `segment-${groupIdx + wordIdx}`;
88907
+ const segmentIndex = segments.size;
88908
+ const segmentId = `segment-${segmentIndex}`;
88900
88909
  const segment = {
88901
88910
  id: segmentId,
88902
- wordId: word.id ?? `w${groupIdx + wordIdx}`,
88911
+ wordId: word.id ?? `w${segmentIndex}`,
88903
88912
  text: word.text,
88904
88913
  start: word.start,
88905
88914
  end: word.end,
@@ -88938,6 +88947,20 @@ function buildCaptionModel(transcript, options) {
88938
88947
  }
88939
88948
  };
88940
88949
  }
88950
+ var GROUP_SELECTOR = ".caption-group, .caption-line, .caption-block";
88951
+ var WORD_SELECTOR = ".word, .caption-word";
88952
+ function readTranscriptGroups(transcript, groupEls) {
88953
+ const wordGroups = [];
88954
+ let offset = 0;
88955
+ for (const group of groupEls) {
88956
+ if (group.parentElement?.closest(GROUP_SELECTOR)) continue;
88957
+ const size = group.querySelectorAll(WORD_SELECTOR).length;
88958
+ if (size === 0) continue;
88959
+ wordGroups.push(transcript.slice(offset, offset + size));
88960
+ offset += size;
88961
+ }
88962
+ return offset === transcript.length ? wordGroups : null;
88963
+ }
88941
88964
  function extractTranscript(source) {
88942
88965
  const varPattern = /(?:const|let|var)\s+(?:TRANSCRIPT|script)\s*=\s*(\[[\s\S]*?\]);/;
88943
88966
  const match = source.match(varPattern);
@@ -88956,21 +88979,15 @@ function parseCaptionComposition(iframeDoc, iframeWin, source, compositionWidth,
88956
88979
  if (transcript.length === 0) {
88957
88980
  return null;
88958
88981
  }
88959
- const groupEls = iframeDoc.querySelectorAll(".caption-group, .caption-line, .caption-block");
88960
- const wordEls = iframeDoc.querySelectorAll(".word, .caption-word");
88961
- let wordsPerGroup = 5;
88962
- if (groupEls.length > 0 && wordEls.length > 0) {
88963
- wordsPerGroup = Math.round(wordEls.length / groupEls.length);
88964
- if (wordsPerGroup < 1) {
88965
- wordsPerGroup = 1;
88966
- }
88967
- }
88968
- const model = buildCaptionModel(transcript, {
88982
+ const groupEls = iframeDoc.querySelectorAll(GROUP_SELECTOR);
88983
+ const wordEls = iframeDoc.querySelectorAll(WORD_SELECTOR);
88984
+ const wordGroups = readTranscriptGroups(transcript, groupEls);
88985
+ const options = {
88969
88986
  width: compositionWidth,
88970
88987
  height: compositionHeight,
88971
- duration: compositionDuration,
88972
- wordsPerGroup
88973
- });
88988
+ duration: compositionDuration
88989
+ };
88990
+ const model = wordGroups ? buildGroupedCaptionModel(wordGroups, options) : buildCaptionModel(transcript, options);
88974
88991
  const firstWordEl = wordEls.item(0);
88975
88992
  const firstGroupEl = groupEls.item(0);
88976
88993
  const styleSourceEl = firstWordEl ?? firstGroupEl;
@@ -89517,7 +89534,14 @@ function useToast() {
89517
89534
  import { useCallback as useCallback160, useEffect as useEffect132, useRef as useRef166, useState as useState132 } from "react";
89518
89535
 
89519
89536
  // src/utils/studioUrlState.ts
89520
- var VALID_TABS = ["layers", "design", "renders", "slideshow", "variables"];
89537
+ var VALID_TABS = [
89538
+ "layers",
89539
+ "design",
89540
+ "renders",
89541
+ "slideshow",
89542
+ "variables",
89543
+ "motionBlur"
89544
+ ];
89521
89545
  function resolveMasterCompositionPath(compositions) {
89522
89546
  if (compositions.includes("index.html")) return "index.html";
89523
89547
  return compositions.find((p) => p.endsWith(".html")) ?? null;
@@ -91778,7 +91802,7 @@ function useGestureTrailOverlay({
91778
91802
  }
91779
91803
 
91780
91804
  // src/hooks/useStudioSidePanels.tsx
91781
- import { useMemo as useMemo88 } from "react";
91805
+ import { useMemo as useMemo89 } from "react";
91782
91806
 
91783
91807
  // src/components/StudioLeftPanels.tsx
91784
91808
  import { memo as memo51, useCallback as useCallback172 } from "react";
@@ -94413,7 +94437,7 @@ function CodeBody({
94413
94437
  }
94414
94438
 
94415
94439
  // src/components/StudioRightPanels.tsx
94416
- import { memo as memo61, useCallback as useCallback189 } from "react";
94440
+ import { memo as memo62, useCallback as useCallback190 } from "react";
94417
94441
 
94418
94442
  // src/components/editor/LayersPanel.tsx
94419
94443
  import { memo as memo52, useState as useState148, useCallback as useCallback174, useEffect as useEffect146, useRef as useRef179 } from "react";
@@ -98574,8 +98598,175 @@ var VariablesPanel = memo60(function VariablesPanel2({
98574
98598
  ] });
98575
98599
  });
98576
98600
 
98601
+ // src/components/panels/MotionBlurPanel.tsx
98602
+ import { memo as memo61, useCallback as useCallback185, useEffect as useEffect154, useMemo as useMemo87, useReducer as useReducer2, useRef as useRef185, useState as useState161 } from "react";
98603
+
98604
+ // src/components/panels/motionBlurPanelModel.ts
98605
+ import {
98606
+ MOTION_BLUR_ATTR,
98607
+ MOTION_BLUR_LIMITS,
98608
+ motionBlurAttributeEdits,
98609
+ readMotionBlurSettings
98610
+ } from "@hyperframes/core/motion-blur-settings";
98611
+ function findCompositionRoot(roots) {
98612
+ const root = roots.find((el) => "data-composition-id" in el.attributes) ?? roots[0];
98613
+ return root ? { id: root.id, attributes: root.attributes } : null;
98614
+ }
98615
+ var settingsOf = (attributes) => readMotionBlurSettings((name) => attributes[name] ?? null);
98616
+ function changedEdits(attributes, next) {
98617
+ return Object.entries(motionBlurAttributeEdits(next)).filter(([name, value]) => (attributes[name] ?? null) !== value).map(([name, value]) => ({ name, value }));
98618
+ }
98619
+ function withEdits(attributes, edits) {
98620
+ const next = { ...attributes };
98621
+ for (const { name, value } of edits) {
98622
+ if (value === null) delete next[name];
98623
+ else next[name] = value;
98624
+ }
98625
+ return next;
98626
+ }
98627
+ function readPreviewRootAttributes(iframe) {
98628
+ try {
98629
+ const root = iframe?.contentDocument?.querySelector("[data-composition-id]");
98630
+ if (!root) return null;
98631
+ const found = {};
98632
+ for (const name of Object.values(MOTION_BLUR_ATTR)) {
98633
+ const value = root.getAttribute(name);
98634
+ if (value !== null) found[name] = value;
98635
+ }
98636
+ return found;
98637
+ } catch {
98638
+ return null;
98639
+ }
98640
+ }
98641
+ var sameAttributes = (a, b) => a === b || a !== null && b !== null && Object.keys({ ...a, ...b }).every((name) => a[name] === b[name]);
98642
+ var BLUR_LEVELS = [
98643
+ { level: "off", label: "Off" },
98644
+ { level: "subtle", label: "Subtle" },
98645
+ { level: "strong", label: "Strong" }
98646
+ ];
98647
+ var LEVEL_SETTINGS = {
98648
+ subtle: { shutterDeg: MOTION_BLUR_LIMITS.shutterDeg.default, samples: null },
98649
+ strong: {
98650
+ shutterDeg: MOTION_BLUR_LIMITS.shutterDeg.max,
98651
+ samples: MOTION_BLUR_LIMITS.samples.max
98652
+ }
98653
+ };
98654
+ var STRONG_ABOVE = (LEVEL_SETTINGS.subtle.shutterDeg + LEVEL_SETTINGS.strong.shutterDeg) / 2;
98655
+ function levelOf(settings) {
98656
+ if (!settings.enabled) return "off";
98657
+ return settings.shutterDeg > STRONG_ABOVE ? "strong" : "subtle";
98658
+ }
98659
+ function patchForLevel(level) {
98660
+ return level === "off" ? { enabled: false } : { enabled: true, ...LEVEL_SETTINGS[level] };
98661
+ }
98662
+
98663
+ // src/components/panels/MotionBlurPanel.tsx
98664
+ import { jsx as jsx244, jsxs as jsxs193 } from "react/jsx-runtime";
98665
+ var POLL_MS2 = 500;
98666
+ var SETTLE_MS = 1500;
98667
+ var NothingToChange = class extends Error {
98668
+ };
98669
+ function useLivePreviewAttributes(previewIframeRef, refreshKey) {
98670
+ const [live, setLive] = useState161(null);
98671
+ const holdUntil = useRef185(0);
98672
+ useEffect154(() => {
98673
+ const read2 = () => {
98674
+ const found = readPreviewRootAttributes(previewIframeRef.current);
98675
+ if (found && Date.now() >= holdUntil.current) {
98676
+ setLive((current) => sameAttributes(current, found) ? current : found);
98677
+ }
98678
+ };
98679
+ read2();
98680
+ const timer = setInterval(read2, POLL_MS2);
98681
+ return () => clearInterval(timer);
98682
+ }, [previewIframeRef, refreshKey]);
98683
+ const hold = useCallback185((next) => {
98684
+ holdUntil.current = Date.now() + SETTLE_MS;
98685
+ setLive(next);
98686
+ }, []);
98687
+ return [live, hold];
98688
+ }
98689
+ var MotionBlurPanel = memo61(function MotionBlurPanel2({
98690
+ sdkSession,
98691
+ publishSdkSession,
98692
+ reloadPreview,
98693
+ recordEdit
98694
+ }) {
98695
+ const { activeCompPath, previewIframeRef, showToast } = useStudioShellContext();
98696
+ const { refreshKey } = useStudioPlaybackContext();
98697
+ const { readProjectFile, writeProjectFile, compositions } = useFileManagerContext();
98698
+ const [, rerender] = useReducer2((n) => n + 1, 0);
98699
+ const [live, hold] = useLivePreviewAttributes(previewIframeRef, refreshKey);
98700
+ useEffect154(() => sdkSession?.on("change", rerender), [sdkSession]);
98701
+ const root = sdkSession ? findCompositionRoot(sdkSession.getRootElements()) : null;
98702
+ const attributes = live ?? root?.attributes ?? null;
98703
+ const settings = useMemo87(() => attributes ? settingsOf(attributes) : null, [attributes]);
98704
+ const isMain = activeCompPath == null || activeCompPath === resolveMasterCompositionPath(compositions);
98705
+ const persist2 = useVariablesPersist({
98706
+ sdkSession,
98707
+ activeCompPath,
98708
+ readProjectFile,
98709
+ writeProjectFile,
98710
+ recordEdit,
98711
+ reloadPreview,
98712
+ publishSdkSession
98713
+ });
98714
+ const apply = useCallback185(
98715
+ async (patch) => {
98716
+ if (!attributes) return;
98717
+ let applied = [];
98718
+ try {
98719
+ await persist2("Edit motion blur", (session) => {
98720
+ const target = findCompositionRoot(session.getRootElements());
98721
+ if (!target) return;
98722
+ const next = { ...settingsOf(target.attributes), ...patch };
98723
+ applied = changedEdits(target.attributes, next);
98724
+ if (applied.length === 0) throw new NothingToChange();
98725
+ for (const e of applied) {
98726
+ session.dispatch({
98727
+ type: "setAttribute",
98728
+ target: target.id,
98729
+ name: e.name,
98730
+ value: e.value
98731
+ });
98732
+ }
98733
+ });
98734
+ hold((current2) => withEdits(current2 ?? attributes, applied));
98735
+ rerender();
98736
+ } catch (err) {
98737
+ if (err instanceof NothingToChange) return;
98738
+ showToast(err instanceof Error ? err.message : String(err), "error");
98739
+ }
98740
+ },
98741
+ [attributes, persist2, hold, showToast]
98742
+ );
98743
+ if (!settings) {
98744
+ return /* @__PURE__ */ jsx244("p", { className: "p-3 text-[10px] leading-relaxed text-neutral-500", children: "Open a composition to set up motion blur." });
98745
+ }
98746
+ const locked = !isMain;
98747
+ const current = locked ? null : levelOf(settings);
98748
+ return /* @__PURE__ */ jsxs193("div", { className: "flex h-full flex-col gap-3 overflow-y-auto p-3 text-[11px] text-neutral-300", children: [
98749
+ /* @__PURE__ */ jsx244("span", { className: "text-[12px] font-medium text-neutral-100", children: "Motion blur" }),
98750
+ /* @__PURE__ */ jsx244("div", { role: "radiogroup", "aria-label": "Motion blur", className: "flex gap-1.5", children: BLUR_LEVELS.map(({ level, label }) => /* @__PURE__ */ jsx244(
98751
+ Button,
98752
+ {
98753
+ role: "radio",
98754
+ "aria-checked": current === level,
98755
+ size: "sm",
98756
+ variant: current === level ? "secondary" : "ghost",
98757
+ className: "flex-1",
98758
+ disabled: locked,
98759
+ onClick: () => void apply(patchForLevel(level)),
98760
+ children: label
98761
+ },
98762
+ level
98763
+ )) }),
98764
+ locked ? /* @__PURE__ */ jsx244("p", { className: "text-[10px] leading-relaxed text-amber-400/90", children: "Motion blur is read from the main composition. Open it to change this setting." }) : null
98765
+ ] });
98766
+ });
98767
+
98577
98768
  // src/hooks/useSlideshowPersist.ts
98578
- import { useCallback as useCallback185 } from "react";
98769
+ import { useCallback as useCallback186 } from "react";
98579
98770
 
98580
98771
  // src/utils/setSlideshowManifest.ts
98581
98772
  import {
@@ -98632,7 +98823,7 @@ function useSlideshowPersist({
98632
98823
  publishSdkSession,
98633
98824
  coalesceKey
98634
98825
  }) {
98635
- return useCallback185(
98826
+ return useCallback186(
98636
98827
  async (manifest) => {
98637
98828
  if (!sdkSession) return;
98638
98829
  const path = activeCompPath ?? "index.html";
@@ -98665,15 +98856,15 @@ function useSlideshowPersist({
98665
98856
  }
98666
98857
 
98667
98858
  // src/hooks/useSlideshowTabState.ts
98668
- import { useMemo as useMemo87 } from "react";
98859
+ import { useMemo as useMemo88 } from "react";
98669
98860
  import { SLIDESHOW_ISLAND_TYPE as SLIDESHOW_ISLAND_TYPE2, slideshowIslandRegex as slideshowIslandRegex2 } from "@hyperframes/core/slideshow";
98670
98861
  function useSlideshowTabState(params) {
98671
98862
  const { editingFileContent, previewIframeRef, refreshKey, slideshowVisible } = params;
98672
- const isSlideshowComposition = useMemo87(() => {
98863
+ const isSlideshowComposition = useMemo88(() => {
98673
98864
  if (!editingFileContent || !editingFileContent.includes(SLIDESHOW_ISLAND_TYPE2)) return false;
98674
98865
  return slideshowIslandRegex2("i").test(editingFileContent);
98675
98866
  }, [editingFileContent]);
98676
- const slideshowScenes = useMemo87(() => {
98867
+ const slideshowScenes = useMemo88(() => {
98677
98868
  try {
98678
98869
  const win = previewIframeRef.current?.contentWindow;
98679
98870
  return (win?.__clipManifest?.scenes ?? []).map((s) => ({
@@ -98690,8 +98881,8 @@ function useSlideshowTabState(params) {
98690
98881
  }
98691
98882
 
98692
98883
  // src/components/DesignPanelPromoteProvider.tsx
98693
- import { useCallback as useCallback186 } from "react";
98694
- import { jsx as jsx244 } from "react/jsx-runtime";
98884
+ import { useCallback as useCallback187 } from "react";
98885
+ import { jsx as jsx245 } from "react/jsx-runtime";
98695
98886
  function DesignPanelPromoteProvider({
98696
98887
  selection,
98697
98888
  projectId,
@@ -98709,7 +98900,7 @@ function DesignPanelPromoteProvider({
98709
98900
  publishSdkSession: handle.publish,
98710
98901
  activeCompPath: targetPath
98711
98902
  });
98712
- const persist2 = useCallback186(
98903
+ const persist2 = useCallback187(
98713
98904
  async (label, mutate) => {
98714
98905
  const committed = await rawPersist(label, mutate);
98715
98906
  if (committed) forceReloadSharedSdkSession?.();
@@ -98717,11 +98908,11 @@ function DesignPanelPromoteProvider({
98717
98908
  },
98718
98909
  [rawPersist, forceReloadSharedSdkSession]
98719
98910
  );
98720
- const handlePersistError = useCallback186(
98911
+ const handlePersistError = useCallback187(
98721
98912
  (error) => showToast(getStudioSaveErrorMessage(error), "error"),
98722
98913
  [showToast]
98723
98914
  );
98724
- return /* @__PURE__ */ jsx244(
98915
+ return /* @__PURE__ */ jsx245(
98725
98916
  VariablePromoteProvider,
98726
98917
  {
98727
98918
  session: handle.session,
@@ -98734,7 +98925,7 @@ function DesignPanelPromoteProvider({
98734
98925
  }
98735
98926
 
98736
98927
  // src/hooks/useRemoveBackground.ts
98737
- import { useCallback as useCallback187, useEffect as useEffect154, useRef as useRef185 } from "react";
98928
+ import { useCallback as useCallback188, useEffect as useEffect155, useRef as useRef186 } from "react";
98738
98929
 
98739
98930
  // src/components/studioMediaJobs.ts
98740
98931
  var MEDIA_JOB_RECONNECT_TIMEOUT_MS = 15e3;
@@ -98840,14 +99031,14 @@ function waitForMediaJob(jobId, onProgress, signal) {
98840
99031
 
98841
99032
  // src/hooks/useRemoveBackground.ts
98842
99033
  function useRemoveBackground(projectId, refreshFileTree, showToast) {
98843
- const abortRef = useRef185(null);
98844
- useEffect154(
99034
+ const abortRef = useRef186(null);
99035
+ useEffect155(
98845
99036
  () => () => {
98846
99037
  abortRef.current?.abort();
98847
99038
  },
98848
99039
  []
98849
99040
  );
98850
- return useCallback187(
99041
+ return useCallback188(
98851
99042
  // fallow-ignore-next-line complexity
98852
99043
  async (inputPath, options) => {
98853
99044
  const response = await studioApiFetch(
@@ -98886,7 +99077,7 @@ function useRemoveBackground(projectId, refreshFileTree, showToast) {
98886
99077
  }
98887
99078
 
98888
99079
  // src/hooks/useApplyColorGradingScope.ts
98889
- import { useCallback as useCallback188 } from "react";
99080
+ import { useCallback as useCallback189 } from "react";
98890
99081
 
98891
99082
  // src/components/editor/colorGradingScopePatch.ts
98892
99083
  var MEDIA_TAG_RE = /<\s*(video|img)\b(?:[^>"']|"[^"]*"|'[^']*')*>/gi;
@@ -98990,7 +99181,7 @@ function useApplyColorGradingScope(recordEdit, reloadPreview) {
98990
99181
  const { projectId, activeCompPath, showToast, waitForPendingDomEditSaves } = useStudioShellContext();
98991
99182
  const { domEditSelection } = useDomEditContext();
98992
99183
  const { readProjectFile, writeProjectFile, compositions } = useFileManagerContext();
98993
- return useCallback188(
99184
+ return useCallback189(
98994
99185
  async (scope, value) => applyColorGradingScopeUpdate({
98995
99186
  scope,
98996
99187
  value,
@@ -99026,9 +99217,9 @@ function useApplyColorGradingScope(recordEdit, reloadPreview) {
99026
99217
  }
99027
99218
 
99028
99219
  // src/components/StudioRightPanels.tsx
99029
- import { Fragment as Fragment66, jsx as jsx245, jsxs as jsxs193 } from "react/jsx-runtime";
99220
+ import { Fragment as Fragment66, jsx as jsx246, jsxs as jsxs194 } from "react/jsx-runtime";
99030
99221
  var seekToTime = (t2) => usePlayerStore.getState().requestSeek(t2);
99031
- var StudioRightPanels = memo61(function StudioRightPanels2({
99222
+ var StudioRightPanels = memo62(function StudioRightPanels2({
99032
99223
  activeBlockParams,
99033
99224
  onCloseBlockParams,
99034
99225
  onDismissBlockParams,
@@ -99138,11 +99329,11 @@ var StudioRightPanels = memo61(function StudioRightPanels2({
99138
99329
  useCaptionDesignFocus(captionEditMode);
99139
99330
  const handleApplyColorGradingScope = useApplyColorGradingScope(recordEdit, reloadPreview);
99140
99331
  const handleRemoveBackground = useRemoveBackground(projectId, refreshFileTree, showToast);
99141
- const setAttributeWhileDragging = useCallback189(
99332
+ const setAttributeWhileDragging = useCallback190(
99142
99333
  (attr, value) => handleDomAttributeLiveCommit(attr, value, void 0, { previewOnly: true }),
99143
99334
  [handleDomAttributeLiveCommit]
99144
99335
  );
99145
- const handleHideAllSelected = useCallback189(() => {
99336
+ const handleHideAllSelected = useCallback190(() => {
99146
99337
  if (!canHideSelections(domEditGroupSelections)) {
99147
99338
  showToast("Audio can't be hidden. Use the group's own controls", "info");
99148
99339
  return;
@@ -99151,11 +99342,11 @@ var StudioRightPanels = memo61(function StudioRightPanels2({
99151
99342
  const keys = timelineKeysForSelections(domEditGroupSelections, elements, activeCompPath);
99152
99343
  if (keys.length > 0) void onToggleElementHidden?.(keys, true);
99153
99344
  }, [domEditGroupSelections, showToast, activeCompPath, onToggleElementHidden]);
99154
- const convertToKeyframes = useCallback189(
99345
+ const convertToKeyframes = useCallback190(
99155
99346
  (animId, duration) => handleGsapConvertToKeyframes(animId, void 0, duration),
99156
99347
  [handleGsapConvertToKeyframes]
99157
99348
  );
99158
- const propertyPanel = /* @__PURE__ */ jsx245(
99349
+ const propertyPanel = /* @__PURE__ */ jsx246(
99159
99350
  DesignPanelPromoteProvider,
99160
99351
  {
99161
99352
  selection: domEditGroupSelections.length > 1 ? null : domEditSelection,
@@ -99167,7 +99358,7 @@ var StudioRightPanels = memo61(function StudioRightPanels2({
99167
99358
  recordEdit,
99168
99359
  reloadPreview,
99169
99360
  forceReloadSharedSdkSession: forceReloadSdkSession,
99170
- children: /* @__PURE__ */ jsx245(
99361
+ children: /* @__PURE__ */ jsx246(
99171
99362
  PropertyPanel,
99172
99363
  {
99173
99364
  projectId,
@@ -99238,9 +99429,9 @@ var StudioRightPanels = memo61(function StudioRightPanels2({
99238
99429
  );
99239
99430
  let designBody = propertyPanel;
99240
99431
  if (captionEditMode) {
99241
- designBody = /* @__PURE__ */ jsx245(CaptionPropertyPanel, { iframeRef: previewIframeRef });
99432
+ designBody = /* @__PURE__ */ jsx246(CaptionPropertyPanel, { iframeRef: previewIframeRef });
99242
99433
  } else if (activeBlockParams) {
99243
- designBody = /* @__PURE__ */ jsx245(
99434
+ designBody = /* @__PURE__ */ jsx246(
99244
99435
  BlockParamsPanel,
99245
99436
  {
99246
99437
  blockName: activeBlockParams.blockName,
@@ -99252,18 +99443,18 @@ var StudioRightPanels = memo61(function StudioRightPanels2({
99252
99443
  }
99253
99444
  );
99254
99445
  }
99255
- return /* @__PURE__ */ jsxs193(Fragment66, { children: [
99256
- /* @__PURE__ */ jsx245(Dock.Panel, { id: "design", children: designBody }),
99257
- /* @__PURE__ */ jsx245(Dock.Panel, { id: "layers", children: /* @__PURE__ */ jsx245(LayersPanel, {}) }),
99258
- /* @__PURE__ */ jsx245(
99446
+ return /* @__PURE__ */ jsxs194(Fragment66, { children: [
99447
+ /* @__PURE__ */ jsx246(Dock.Panel, { id: "design", children: designBody }),
99448
+ /* @__PURE__ */ jsx246(Dock.Panel, { id: "layers", children: /* @__PURE__ */ jsx246(LayersPanel, {}) }),
99449
+ /* @__PURE__ */ jsx246(
99259
99450
  Dock.Panel,
99260
99451
  {
99261
99452
  id: "renders",
99262
99453
  title: renderJobs.length > 0 ? `Renders (${renderJobs.length})` : void 0,
99263
- children: /* @__PURE__ */ jsx245(RenderQueuePanel, {})
99454
+ children: /* @__PURE__ */ jsx246(RenderQueuePanel, {})
99264
99455
  }
99265
99456
  ),
99266
- /* @__PURE__ */ jsx245(Dock.Panel, { id: "variables", children: /* @__PURE__ */ jsx245(
99457
+ /* @__PURE__ */ jsx246(Dock.Panel, { id: "variables", children: /* @__PURE__ */ jsx246(
99267
99458
  VariablesPanel,
99268
99459
  {
99269
99460
  sdkSession,
@@ -99272,7 +99463,16 @@ var StudioRightPanels = memo61(function StudioRightPanels2({
99272
99463
  recordEdit
99273
99464
  }
99274
99465
  ) }),
99275
- /* @__PURE__ */ jsx245(Dock.Panel, { id: "slideshow", children: /* @__PURE__ */ jsx245(
99466
+ /* @__PURE__ */ jsx246(Dock.Panel, { id: "motionBlur", children: /* @__PURE__ */ jsx246(
99467
+ MotionBlurPanel,
99468
+ {
99469
+ sdkSession,
99470
+ publishSdkSession,
99471
+ reloadPreview,
99472
+ recordEdit
99473
+ }
99474
+ ) }),
99475
+ /* @__PURE__ */ jsx246(Dock.Panel, { id: "slideshow", children: /* @__PURE__ */ jsx246(
99276
99476
  SlideshowPanel,
99277
99477
  {
99278
99478
  scenes: slideshowScenes,
@@ -99284,21 +99484,21 @@ var StudioRightPanels = memo61(function StudioRightPanels2({
99284
99484
  });
99285
99485
 
99286
99486
  // src/hooks/useStudioSidePanels.tsx
99287
- import { Fragment as Fragment67, jsx as jsx246, jsxs as jsxs194 } from "react/jsx-runtime";
99487
+ import { Fragment as Fragment67, jsx as jsx247, jsxs as jsxs195 } from "react/jsx-runtime";
99288
99488
  function useStudioSidePanels(left, right, projectId) {
99289
99489
  const stableLeft = useStableHandlers(left, projectId);
99290
99490
  const stableRight = useStableHandlers(right, projectId);
99291
- return useMemo88(
99292
- () => /* @__PURE__ */ jsxs194(Fragment67, { children: [
99293
- /* @__PURE__ */ jsx246(StudioLeftPanels, { ...stableLeft }),
99294
- /* @__PURE__ */ jsx246(StudioRightPanels, { ...stableRight })
99491
+ return useMemo89(
99492
+ () => /* @__PURE__ */ jsxs195(Fragment67, { children: [
99493
+ /* @__PURE__ */ jsx247(StudioLeftPanels, { ...stableLeft }),
99494
+ /* @__PURE__ */ jsx247(StudioRightPanels, { ...stableRight })
99295
99495
  ] }),
99296
99496
  [stableLeft, stableRight]
99297
99497
  );
99298
99498
  }
99299
99499
 
99300
99500
  // src/components/TimelineToolbar.tsx
99301
- import { memo as memo62, useEffect as useEffect156, useRef as useRef186 } from "react";
99501
+ import { memo as memo63, useEffect as useEffect157, useRef as useRef187 } from "react";
99302
99502
  import {
99303
99503
  ArrowsOutLineHorizontal,
99304
99504
  Image as Image3,
@@ -99308,9 +99508,9 @@ import {
99308
99508
  } from "@phosphor-icons/react";
99309
99509
 
99310
99510
  // src/hooks/useKeyframeKeyboard.ts
99311
- import { useEffect as useEffect155, useCallback as useCallback190 } from "react";
99511
+ import { useEffect as useEffect156, useCallback as useCallback191 } from "react";
99312
99512
  function useKeyframeKeyboard({ enabled, onAddKeyframe }) {
99313
- const handler = useCallback190(
99513
+ const handler = useCallback191(
99314
99514
  (e) => {
99315
99515
  if (!enabled || !onAddKeyframe) return;
99316
99516
  if (isTypingTarget(document.activeElement)) return;
@@ -99322,7 +99522,7 @@ function useKeyframeKeyboard({ enabled, onAddKeyframe }) {
99322
99522
  },
99323
99523
  [enabled, onAddKeyframe]
99324
99524
  );
99325
- useEffect155(() => {
99525
+ useEffect156(() => {
99326
99526
  if (!enabled) return;
99327
99527
  window.addEventListener("keydown", handler, { capture: true });
99328
99528
  return () => window.removeEventListener("keydown", handler, { capture: true });
@@ -99330,9 +99530,9 @@ function useKeyframeKeyboard({ enabled, onAddKeyframe }) {
99330
99530
  }
99331
99531
 
99332
99532
  // src/components/icons/AudioMetersIcon.tsx
99333
- import { jsx as jsx247 } from "react/jsx-runtime";
99533
+ import { jsx as jsx248 } from "react/jsx-runtime";
99334
99534
  function AudioMetersIcon({ size = 16 }) {
99335
- return /* @__PURE__ */ jsx247(
99535
+ return /* @__PURE__ */ jsx248(
99336
99536
  "svg",
99337
99537
  {
99338
99538
  width: size,
@@ -99343,15 +99543,15 @@ function AudioMetersIcon({ size = 16 }) {
99343
99543
  strokeWidth: 1.5,
99344
99544
  strokeLinecap: "round",
99345
99545
  "aria-hidden": "true",
99346
- children: /* @__PURE__ */ jsx247("path", { d: "M2.5 13h2m-2-3h2M7 13h2m-2-3h2m-2-3h2m-2-3h2M11.5 13h2m-2-3h2m-2-3h2" })
99546
+ children: /* @__PURE__ */ jsx248("path", { d: "M2.5 13h2m-2-3h2M7 13h2m-2-3h2m-2-3h2m-2-3h2M11.5 13h2m-2-3h2m-2-3h2" })
99347
99547
  }
99348
99548
  );
99349
99549
  }
99350
99550
 
99351
99551
  // src/components/icons/RippleEditIcon.tsx
99352
- import { jsx as jsx248, jsxs as jsxs195 } from "react/jsx-runtime";
99552
+ import { jsx as jsx249, jsxs as jsxs196 } from "react/jsx-runtime";
99353
99553
  function RippleEditIcon({ size = 16 }) {
99354
- return /* @__PURE__ */ jsxs195(
99554
+ return /* @__PURE__ */ jsxs196(
99355
99555
  "svg",
99356
99556
  {
99357
99557
  width: size,
@@ -99364,20 +99564,20 @@ function RippleEditIcon({ size = 16 }) {
99364
99564
  strokeLinejoin: "round",
99365
99565
  "aria-hidden": "true",
99366
99566
  children: [
99367
- /* @__PURE__ */ jsx248("rect", { x: "1.5", y: "8", width: "4", height: "5", rx: "0.5" }),
99368
- /* @__PURE__ */ jsx248("rect", { x: "10.5", y: "8", width: "4", height: "5", rx: "0.5" }),
99369
- /* @__PURE__ */ jsx248("path", { d: "M13 4H6m2-2L6 4l2 2" })
99567
+ /* @__PURE__ */ jsx249("rect", { x: "1.5", y: "8", width: "4", height: "5", rx: "0.5" }),
99568
+ /* @__PURE__ */ jsx249("rect", { x: "10.5", y: "8", width: "4", height: "5", rx: "0.5" }),
99569
+ /* @__PURE__ */ jsx249("path", { d: "M13 4H6m2-2L6 4l2 2" })
99370
99570
  ]
99371
99571
  }
99372
99572
  );
99373
99573
  }
99374
99574
 
99375
99575
  // src/components/TimelineHistoryButtons.tsx
99376
- import { Fragment as Fragment68, jsx as jsx249, jsxs as jsxs196 } from "react/jsx-runtime";
99576
+ import { Fragment as Fragment68, jsx as jsx250, jsxs as jsxs197 } from "react/jsx-runtime";
99377
99577
  function HistoryButton({ action, can, label, onClick }) {
99378
99578
  const Icon = action === "undo" ? RotateCcw : RotateCw;
99379
99579
  const enabled = Boolean(onClick) && can;
99380
- return /* @__PURE__ */ jsx249(Tooltip, { label: historyTooltipLabel(action, label), children: /* @__PURE__ */ jsx249(
99580
+ return /* @__PURE__ */ jsx250(Tooltip, { label: historyTooltipLabel(action, label), children: /* @__PURE__ */ jsx250(
99381
99581
  "button",
99382
99582
  {
99383
99583
  type: "button",
@@ -99388,7 +99588,7 @@ function HistoryButton({ action, can, label, onClick }) {
99388
99588
  trackStudioEvent("toolbar_action", { action });
99389
99589
  void onClick?.();
99390
99590
  },
99391
- children: /* @__PURE__ */ jsx249(Icon, { size: 16 })
99591
+ children: /* @__PURE__ */ jsx250(Icon, { size: 16 })
99392
99592
  }
99393
99593
  ) });
99394
99594
  }
@@ -99396,8 +99596,8 @@ function TimelineHistoryButtons(props) {
99396
99596
  const shell = useStudioShellContextOptional();
99397
99597
  const onUndo = props.onUndo ?? shell?.handleUndo;
99398
99598
  const onRedo = props.onRedo ?? shell?.handleRedo;
99399
- return /* @__PURE__ */ jsxs196(Fragment68, { children: [
99400
- /* @__PURE__ */ jsx249(
99599
+ return /* @__PURE__ */ jsxs197(Fragment68, { children: [
99600
+ /* @__PURE__ */ jsx250(
99401
99601
  HistoryButton,
99402
99602
  {
99403
99603
  action: "undo",
@@ -99406,7 +99606,7 @@ function TimelineHistoryButtons(props) {
99406
99606
  onClick: onUndo
99407
99607
  }
99408
99608
  ),
99409
- /* @__PURE__ */ jsx249(
99609
+ /* @__PURE__ */ jsx250(
99410
99610
  HistoryButton,
99411
99611
  {
99412
99612
  action: "redo",
@@ -99420,9 +99620,9 @@ function TimelineHistoryButtons(props) {
99420
99620
 
99421
99621
  // src/components/TimelineToolPicker.tsx
99422
99622
  import { ArrowLineLeft, ArrowLineRight, CaretDown } from "@phosphor-icons/react";
99423
- import { jsx as jsx250, jsxs as jsxs197 } from "react/jsx-runtime";
99623
+ import { jsx as jsx251, jsxs as jsxs198 } from "react/jsx-runtime";
99424
99624
  function SelectIcon() {
99425
- return /* @__PURE__ */ jsx250("svg", { width: "16", height: "16", viewBox: "0 0 12 12", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx250("path", { d: "M2 0.5L10 6L6.5 6.5L8.5 11L6.5 11.5L4.5 7L2 9Z" }) });
99625
+ return /* @__PURE__ */ jsx251("svg", { width: "16", height: "16", viewBox: "0 0 12 12", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx251("path", { d: "M2 0.5L10 6L6.5 6.5L8.5 11L6.5 11.5L4.5 7L2 9Z" }) });
99426
99626
  }
99427
99627
  function TimelineToolPicker({ showSelectAroundPlayhead = true }) {
99428
99628
  const activeTool = usePlayerStore((s) => s.activeTool);
@@ -99431,14 +99631,14 @@ function TimelineToolPicker({ showSelectAroundPlayhead = true }) {
99431
99631
  {
99432
99632
  label: "Select",
99433
99633
  shortcut: "V",
99434
- icon: /* @__PURE__ */ jsx250(SelectIcon, {}),
99634
+ icon: /* @__PURE__ */ jsx251(SelectIcon, {}),
99435
99635
  active: activeTool === "select",
99436
99636
  run: () => store().setActiveTool("select")
99437
99637
  },
99438
99638
  {
99439
99639
  label: "Split",
99440
99640
  shortcut: "B",
99441
- icon: /* @__PURE__ */ jsx250(Scissors, { size: 16 }),
99641
+ icon: /* @__PURE__ */ jsx251(Scissors, { size: 16 }),
99442
99642
  active: activeTool === "razor",
99443
99643
  run: () => store().setActiveTool("razor")
99444
99644
  }
@@ -99448,25 +99648,25 @@ function TimelineToolPicker({ showSelectAroundPlayhead = true }) {
99448
99648
  {
99449
99649
  label: "Select leftward",
99450
99650
  shortcut: "[",
99451
- icon: /* @__PURE__ */ jsx250(ArrowLineLeft, { size: 16, "aria-hidden": "true" }),
99651
+ icon: /* @__PURE__ */ jsx251(ArrowLineLeft, { size: 16, "aria-hidden": "true" }),
99452
99652
  active: false,
99453
99653
  run: () => store().selectLeftward()
99454
99654
  },
99455
99655
  {
99456
99656
  label: "Select rightward",
99457
99657
  shortcut: "]",
99458
- icon: /* @__PURE__ */ jsx250(ArrowLineRight, { size: 16, "aria-hidden": "true" }),
99658
+ icon: /* @__PURE__ */ jsx251(ArrowLineRight, { size: 16, "aria-hidden": "true" }),
99459
99659
  active: false,
99460
99660
  run: () => store().selectRightward()
99461
99661
  }
99462
99662
  );
99463
99663
  }
99464
99664
  const current = rows.find((row) => row.active) ?? rows[0];
99465
- return /* @__PURE__ */ jsx250(
99665
+ return /* @__PURE__ */ jsx251(
99466
99666
  Menu,
99467
99667
  {
99468
99668
  "aria-label": "Timeline tools",
99469
- trigger: /* @__PURE__ */ jsxs197(
99669
+ trigger: /* @__PURE__ */ jsxs198(
99470
99670
  "button",
99471
99671
  {
99472
99672
  type: "button",
@@ -99474,18 +99674,18 @@ function TimelineToolPicker({ showSelectAroundPlayhead = true }) {
99474
99674
  className: cn(flatIdle, "gap-0.5"),
99475
99675
  children: [
99476
99676
  current.icon,
99477
- /* @__PURE__ */ jsx250(CaretDown, { size: 10, weight: "bold", "aria-hidden": "true" })
99677
+ /* @__PURE__ */ jsx251(CaretDown, { size: 10, weight: "bold", "aria-hidden": "true" })
99478
99678
  ]
99479
99679
  }
99480
99680
  ),
99481
- children: rows.map((row) => /* @__PURE__ */ jsx250(
99681
+ children: rows.map((row) => /* @__PURE__ */ jsx251(
99482
99682
  MenuItem,
99483
99683
  {
99484
99684
  shortcut: row.shortcut,
99485
99685
  onClick: row.run,
99486
99686
  "data-active": row.active || void 0,
99487
99687
  className: row.active ? "bg-press text-text-0" : void 0,
99488
- children: /* @__PURE__ */ jsxs197("span", { className: "flex items-center gap-2", children: [
99688
+ children: /* @__PURE__ */ jsxs198("span", { className: "flex items-center gap-2", children: [
99489
99689
  row.icon,
99490
99690
  row.label
99491
99691
  ] })
@@ -99497,17 +99697,17 @@ function TimelineToolPicker({ showSelectAroundPlayhead = true }) {
99497
99697
  }
99498
99698
 
99499
99699
  // src/components/LinkedSelectionToggle.tsx
99500
- import { useCallback as useCallback191, useState as useState161 } from "react";
99700
+ import { useCallback as useCallback192, useState as useState162 } from "react";
99501
99701
  import { createPortal as createPortal15 } from "react-dom";
99502
99702
  import { LinkSimple } from "@phosphor-icons/react";
99503
- import { Fragment as Fragment69, jsx as jsx251, jsxs as jsxs198 } from "react/jsx-runtime";
99703
+ import { Fragment as Fragment69, jsx as jsx252, jsxs as jsxs199 } from "react/jsx-runtime";
99504
99704
  function SyncIndicatorMenu({ x, y, onClose }) {
99505
99705
  const menuRef = useContextMenuDismiss(onClose);
99506
99706
  useMenuKeyboardNav(menuRef);
99507
99707
  const visible = useLinkedClipPreferences((s) => s.syncIndicatorsVisible);
99508
99708
  const setVisible = useLinkedClipPreferences((s) => s.setSyncIndicatorsVisible);
99509
99709
  return createPortal15(
99510
- /* @__PURE__ */ jsx251(
99710
+ /* @__PURE__ */ jsx252(
99511
99711
  "div",
99512
99712
  {
99513
99713
  ref: menuRef,
@@ -99515,7 +99715,7 @@ function SyncIndicatorMenu({ x, y, onClose }) {
99515
99715
  "aria-label": "Linked clips",
99516
99716
  className: `${menuClasses.panel} fixed z-200 min-w-[200px]`,
99517
99717
  style: { left: x, top: y },
99518
- children: /* @__PURE__ */ jsxs198(
99718
+ children: /* @__PURE__ */ jsxs199(
99519
99719
  "button",
99520
99720
  {
99521
99721
  type: "button",
@@ -99527,7 +99727,7 @@ function SyncIndicatorMenu({ x, y, onClose }) {
99527
99727
  onClose();
99528
99728
  },
99529
99729
  children: [
99530
- /* @__PURE__ */ jsx251("span", { className: "w-3 text-studio-accent", children: visible ? "\u2713" : "" }),
99730
+ /* @__PURE__ */ jsx252("span", { className: "w-3 text-studio-accent", children: visible ? "\u2713" : "" }),
99531
99731
  "Show out-of-sync indicators"
99532
99732
  ]
99533
99733
  }
@@ -99540,14 +99740,14 @@ function SyncIndicatorMenu({ x, y, onClose }) {
99540
99740
  function LinkedSelectionToggle() {
99541
99741
  const linkedSelection = useLinkedClipPreferences((s) => s.linkedSelection);
99542
99742
  const setLinkedSelection = useLinkedClipPreferences((s) => s.setLinkedSelection);
99543
- const [menuAt, setMenuAt] = useState161(null);
99544
- const closeMenu = useCallback191(() => setMenuAt(null), []);
99545
- return /* @__PURE__ */ jsxs198(Fragment69, { children: [
99546
- /* @__PURE__ */ jsx251(
99743
+ const [menuAt, setMenuAt] = useState162(null);
99744
+ const closeMenu = useCallback192(() => setMenuAt(null), []);
99745
+ return /* @__PURE__ */ jsxs199(Fragment69, { children: [
99746
+ /* @__PURE__ */ jsx252(
99547
99747
  Tooltip,
99548
99748
  {
99549
99749
  label: linkedSelection ? "Linked Selection on: a click selects both halves (\u2325-click for one)" : "Linked Selection off: a click selects one clip",
99550
- children: /* @__PURE__ */ jsx251(
99750
+ children: /* @__PURE__ */ jsx252(
99551
99751
  "button",
99552
99752
  {
99553
99753
  type: "button",
@@ -99559,17 +99759,17 @@ function LinkedSelectionToggle() {
99559
99759
  "aria-label": "Linked Selection",
99560
99760
  "aria-pressed": linkedSelection,
99561
99761
  className: linkedSelection ? flatActive : flatIdle,
99562
- children: /* @__PURE__ */ jsx251(LinkSimple, { size: 16, weight: "bold", "aria-hidden": "true" })
99762
+ children: /* @__PURE__ */ jsx252(LinkSimple, { size: 16, weight: "bold", "aria-hidden": "true" })
99563
99763
  }
99564
99764
  )
99565
99765
  }
99566
99766
  ),
99567
- menuAt && /* @__PURE__ */ jsx251(SyncIndicatorMenu, { x: menuAt.x, y: menuAt.y, onClose: closeMenu })
99767
+ menuAt && /* @__PURE__ */ jsx252(SyncIndicatorMenu, { x: menuAt.x, y: menuAt.y, onClose: closeMenu })
99568
99768
  ] });
99569
99769
  }
99570
99770
 
99571
99771
  // src/components/TimelineToolbar.tsx
99572
- import { Fragment as Fragment70, jsx as jsx252, jsxs as jsxs199 } from "react/jsx-runtime";
99772
+ import { Fragment as Fragment70, jsx as jsx253, jsxs as jsxs200 } from "react/jsx-runtime";
99573
99773
  var NO_KEYFRAME_TOGGLE = {
99574
99774
  state: "none",
99575
99775
  isMotionPath: false,
@@ -99613,7 +99813,7 @@ function useKeyframeToggle(session) {
99613
99813
  const currentTime = usePlayerStore((s) => s.currentTime);
99614
99814
  const selectedElementId = usePlayerStore((s) => s.selectedElementId);
99615
99815
  const elements = usePlayerStore((s) => s.elements);
99616
- const sessionRef = useRef186(session);
99816
+ const sessionRef = useRef187(session);
99617
99817
  sessionRef.current = session;
99618
99818
  const onToggle = useEnableKeyframes(
99619
99819
  sessionRef
@@ -99626,7 +99826,7 @@ function useKeyframeToggle(session) {
99626
99826
  onToggle: session?.domEditSelection && !toggleState.pathEndpoint ? onToggle : void 0
99627
99827
  };
99628
99828
  }
99629
- var TimelineToolbar = memo62(function TimelineToolbar2({
99829
+ var TimelineToolbar = memo63(function TimelineToolbar2({
99630
99830
  domEditSession,
99631
99831
  onSplitElement,
99632
99832
  splitShortcut = SPLIT_SHORTCUT_HINT,
@@ -99668,10 +99868,10 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99668
99868
  enabled: showKeyframes && Boolean(onToggleKeyframe),
99669
99869
  onAddKeyframe: onToggleKeyframe
99670
99870
  });
99671
- useEffect156(() => {
99871
+ useEffect157(() => {
99672
99872
  if (!showKeyframes) setAutoKeyframeEnabled(false);
99673
99873
  }, [showKeyframes, setAutoKeyframeEnabled]);
99674
- useEffect156(() => {
99874
+ useEffect157(() => {
99675
99875
  const onKeyDown = (e) => {
99676
99876
  if (e.key !== "n" && e.key !== "N") return;
99677
99877
  if (e.metaKey || e.ctrlKey || e.altKey) return;
@@ -99682,13 +99882,13 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99682
99882
  window.addEventListener("keydown", onKeyDown);
99683
99883
  return () => window.removeEventListener("keydown", onKeyDown);
99684
99884
  }, []);
99685
- return /* @__PURE__ */ jsx252("div", { className: "border-b border-neutral-800/60", children: /* @__PURE__ */ jsxs199("div", { className: "flex items-center justify-between px-2 py-0.5", children: [
99686
- /* @__PURE__ */ jsxs199("div", { className: "flex items-center gap-0.5", children: [
99687
- showHistory && /* @__PURE__ */ jsx252(TimelineHistoryButtons, { ...history2 }),
99688
- /* @__PURE__ */ jsx252(TimelineToolPicker, { showSelectAroundPlayhead }),
99689
- /* @__PURE__ */ jsx252("div", { "aria-hidden": "true", className: "mx-1 h-4 w-px bg-neutral-800" }),
99690
- /* @__PURE__ */ jsx252(LinkedSelectionToggle, {}),
99691
- /* @__PURE__ */ jsx252(Tooltip, { label: timelineSnapEnabled ? "Snapping on (N)" : "Snapping off (N)", children: /* @__PURE__ */ jsx252(
99885
+ return /* @__PURE__ */ jsx253("div", { className: "border-b border-neutral-800/60", children: /* @__PURE__ */ jsxs200("div", { className: "flex items-center justify-between px-2 py-0.5", children: [
99886
+ /* @__PURE__ */ jsxs200("div", { className: "flex items-center gap-0.5", children: [
99887
+ showHistory && /* @__PURE__ */ jsx253(TimelineHistoryButtons, { ...history2 }),
99888
+ /* @__PURE__ */ jsx253(TimelineToolPicker, { showSelectAroundPlayhead }),
99889
+ /* @__PURE__ */ jsx253("div", { "aria-hidden": "true", className: "mx-1 h-4 w-px bg-neutral-800" }),
99890
+ /* @__PURE__ */ jsx253(LinkedSelectionToggle, {}),
99891
+ /* @__PURE__ */ jsx253(Tooltip, { label: timelineSnapEnabled ? "Snapping on (N)" : "Snapping off (N)", children: /* @__PURE__ */ jsx253(
99692
99892
  "button",
99693
99893
  {
99694
99894
  type: "button",
@@ -99696,14 +99896,14 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99696
99896
  "aria-label": "Toggle timeline snapping",
99697
99897
  "aria-pressed": timelineSnapEnabled,
99698
99898
  className: timelineSnapEnabled ? flatActive : flatIdle,
99699
- children: /* @__PURE__ */ jsx252(Magnet, { size: 16, weight: "bold", "aria-hidden": "true" })
99899
+ children: /* @__PURE__ */ jsx253(Magnet, { size: 16, weight: "bold", "aria-hidden": "true" })
99700
99900
  }
99701
99901
  ) }),
99702
- /* @__PURE__ */ jsx252(
99902
+ /* @__PURE__ */ jsx253(
99703
99903
  Tooltip,
99704
99904
  {
99705
99905
  label: rippleEditEnabled ? "Ripple on: keeps the main track gapless" : "Ripple off: deleting a main-track clip leaves a gap",
99706
- children: /* @__PURE__ */ jsx252(
99906
+ children: /* @__PURE__ */ jsx253(
99707
99907
  "button",
99708
99908
  {
99709
99909
  type: "button",
@@ -99711,12 +99911,12 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99711
99911
  "aria-label": "Toggle ripple edit",
99712
99912
  "aria-pressed": rippleEditEnabled,
99713
99913
  className: rippleEditEnabled ? flatActive : flatIdle,
99714
- children: /* @__PURE__ */ jsx252(RippleEditIcon, { size: 16 })
99914
+ children: /* @__PURE__ */ jsx253(RippleEditIcon, { size: 16 })
99715
99915
  }
99716
99916
  )
99717
99917
  }
99718
99918
  ),
99719
- projectHasAudio && /* @__PURE__ */ jsx252(Tooltip, { label: audioMetersVisible ? "Hide audio meters" : "Show audio meters", children: /* @__PURE__ */ jsx252(
99919
+ projectHasAudio && /* @__PURE__ */ jsx253(Tooltip, { label: audioMetersVisible ? "Hide audio meters" : "Show audio meters", children: /* @__PURE__ */ jsx253(
99720
99920
  "button",
99721
99921
  {
99722
99922
  type: "button",
@@ -99724,15 +99924,15 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99724
99924
  "aria-label": "Toggle audio meters",
99725
99925
  "aria-pressed": audioMetersVisible,
99726
99926
  className: audioMetersVisible ? flatActive : flatIdle,
99727
- children: /* @__PURE__ */ jsx252(AudioMetersIcon, { size: 16 })
99927
+ children: /* @__PURE__ */ jsx253(AudioMetersIcon, { size: 16 })
99728
99928
  }
99729
99929
  ) }),
99730
- showKeyframes && /* @__PURE__ */ jsxs199(Fragment70, { children: [
99731
- /* @__PURE__ */ jsx252(
99930
+ showKeyframes && /* @__PURE__ */ jsxs200(Fragment70, { children: [
99931
+ /* @__PURE__ */ jsx253(
99732
99932
  Tooltip,
99733
99933
  {
99734
99934
  label: keyframePathEndpoint ? "Motion path endpoints cannot be removed" : !onToggleKeyframe ? "Select an animated element to add keyframes" : keyframeIsMotionPath ? keyframeWillExtend ? "Extend motion path to playhead (K)" : keyframeState === "active" ? "Remove waypoint from motion path (K)" : "Add waypoint to motion path (K)" : keyframeState === "active" ? "Remove keyframe at playhead (K)" : keyframeState === "inactive" ? keyframeWillExtend ? "Add keyframe at playhead, extends animation (K)" : "Add keyframe at playhead (K)" : "Add keyframe (K)",
99735
- children: /* @__PURE__ */ jsx252(
99935
+ children: /* @__PURE__ */ jsx253(
99736
99936
  "button",
99737
99937
  {
99738
99938
  type: "button",
@@ -99740,7 +99940,7 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99740
99940
  onClick: onToggleKeyframe,
99741
99941
  "aria-label": keyframePathEndpoint ? "Motion path endpoint" : keyframeIsMotionPath ? keyframeState === "active" ? "Remove motion path waypoint" : keyframeWillExtend ? "Extend motion path to playhead" : "Add motion path waypoint" : keyframeState === "active" ? "Remove keyframe at playhead" : "Add keyframe at playhead",
99742
99942
  className: !onToggleKeyframe ? flatDisabled : `${flatBtn} active:scale-[0.98] hover:bg-hover ${keyframeState === "active" ? "text-accent-ink" : keyframeState === "inactive" ? "text-neutral-400 hover:text-accent-ink" : "text-neutral-600 hover:text-neutral-400"}`,
99743
- children: /* @__PURE__ */ jsx252("svg", { width: "16", height: "16", viewBox: "0 0 10 10", fill: "currentColor", children: keyframeState === "active" ? /* @__PURE__ */ jsx252("path", { d: "M5 0.5L9.5 5L5 9.5L0.5 5Z" }) : /* @__PURE__ */ jsx252(
99943
+ children: /* @__PURE__ */ jsx253("svg", { width: "16", height: "16", viewBox: "0 0 10 10", fill: "currentColor", children: keyframeState === "active" ? /* @__PURE__ */ jsx253("path", { d: "M5 0.5L9.5 5L5 9.5L0.5 5Z" }) : /* @__PURE__ */ jsx253(
99744
99944
  "path",
99745
99945
  {
99746
99946
  d: "M5 1.2L8.8 5L5 8.8L1.2 5Z",
@@ -99753,11 +99953,11 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99753
99953
  )
99754
99954
  }
99755
99955
  ),
99756
- /* @__PURE__ */ jsx252(
99956
+ /* @__PURE__ */ jsx253(
99757
99957
  Tooltip,
99758
99958
  {
99759
99959
  label: autoKeyframeEnabled ? "Auto-record manual edits as keyframes (click to turn off)" : "Manual edits will not be recorded as keyframes (click to turn on)",
99760
- children: /* @__PURE__ */ jsx252(
99960
+ children: /* @__PURE__ */ jsx253(
99761
99961
  "button",
99762
99962
  {
99763
99963
  type: "button",
@@ -99765,9 +99965,9 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99765
99965
  "aria-label": "Auto-record manual edits as keyframes",
99766
99966
  "aria-pressed": autoKeyframeEnabled,
99767
99967
  className: `${flatBtn} active:scale-[0.98] hover:bg-hover ${autoKeyframeEnabled ? "text-danger-ink" : "text-neutral-600 hover:text-neutral-400"}`,
99768
- children: /* @__PURE__ */ jsxs199("svg", { width: "16", height: "16", viewBox: "0 0 10 10", fill: "none", children: [
99769
- /* @__PURE__ */ jsx252("path", { d: "M5 0.7L9.3 5L5 9.3L0.7 5Z", stroke: "currentColor", strokeWidth: "1" }),
99770
- /* @__PURE__ */ jsx252(
99968
+ children: /* @__PURE__ */ jsxs200("svg", { width: "16", height: "16", viewBox: "0 0 10 10", fill: "none", children: [
99969
+ /* @__PURE__ */ jsx253("path", { d: "M5 0.7L9.3 5L5 9.3L0.7 5Z", stroke: "currentColor", strokeWidth: "1" }),
99970
+ /* @__PURE__ */ jsx253(
99771
99971
  "circle",
99772
99972
  {
99773
99973
  cx: "5",
@@ -99788,11 +99988,11 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99788
99988
  const el = selectedElementId ? elements.find((e) => (e.key ?? e.id) === selectedElementId) : null;
99789
99989
  const splittable = el != null && canSplitElement(el);
99790
99990
  const canSplit = splittable && currentTime > el.start && currentTime < el.start + el.duration;
99791
- return /* @__PURE__ */ jsx252(
99991
+ return /* @__PURE__ */ jsx253(
99792
99992
  Tooltip,
99793
99993
  {
99794
99994
  label: canSplit ? `Split at playhead (${splitShortcut})` : splittable ? "Move the playhead inside the clip to split" : "Select a clip to split",
99795
- children: /* @__PURE__ */ jsx252(
99995
+ children: /* @__PURE__ */ jsx253(
99796
99996
  "button",
99797
99997
  {
99798
99998
  type: "button",
@@ -99802,7 +100002,7 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99802
100002
  if (canSplit && el) onSplitElement(el, currentTime);
99803
100003
  },
99804
100004
  className: canSplit ? flatIdle : flatDisabled,
99805
- children: /* @__PURE__ */ jsxs199(
100005
+ children: /* @__PURE__ */ jsxs200(
99806
100006
  "svg",
99807
100007
  {
99808
100008
  width: "16",
@@ -99815,8 +100015,8 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99815
100015
  strokeLinejoin: "round",
99816
100016
  "aria-hidden": "true",
99817
100017
  children: [
99818
- /* @__PURE__ */ jsx252("path", { d: "M5 3 L7 3 L7 13 L5 13" }),
99819
- /* @__PURE__ */ jsx252("path", { d: "M11 3 L9 3 L9 13 L11 13" })
100018
+ /* @__PURE__ */ jsx253("path", { d: "M5 3 L7 3 L7 13 L5 13" }),
100019
+ /* @__PURE__ */ jsx253("path", { d: "M11 3 L9 3 L9 13 L11 13" })
99820
100020
  ]
99821
100021
  }
99822
100022
  )
@@ -99828,11 +100028,11 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99828
100028
  (() => {
99829
100029
  if (!showAddBeat) return null;
99830
100030
  const canAdd = beatAnalysisReady && canAddBeatAt(currentTime);
99831
- return /* @__PURE__ */ jsx252(
100031
+ return /* @__PURE__ */ jsx253(
99832
100032
  Tooltip,
99833
100033
  {
99834
100034
  label: !beatAnalysisReady ? "Add a music track with beat analysis to place beats" : canAdd ? "Add beat at playhead" : "A beat already exists at the playhead",
99835
- children: /* @__PURE__ */ jsx252(
100035
+ children: /* @__PURE__ */ jsx253(
99836
100036
  "button",
99837
100037
  {
99838
100038
  type: "button",
@@ -99842,7 +100042,7 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99842
100042
  if (canAdd) addBeatAtCompositionTime(currentTime);
99843
100043
  },
99844
100044
  className: canAdd ? `${flatBtn} text-text-2 hover:bg-hover hover:text-accent-ink active:scale-[0.98]` : flatDisabled,
99845
- children: /* @__PURE__ */ jsx252("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx252(
100045
+ children: /* @__PURE__ */ jsx253("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx253(
99846
100046
  "path",
99847
100047
  {
99848
100048
  d: "M21 10C21 12.2091 16.9706 14 12 14M21 10C21 7.79086 16.9706 6 12 6C7.02944 6 3 7.79086 3 10M21 10V16C21 18.2091 16.9706 20 12 20M12 14C7.02944 14 3 12.2091 3 10M12 14V20M3 10V16C3 18.2091 7.02944 20 12 20M7 19.3264V13.3264M17 19.3264V13.3264M12 10L20 4",
@@ -99858,13 +100058,13 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99858
100058
  );
99859
100059
  })()
99860
100060
  ] }),
99861
- /* @__PURE__ */ jsxs199("div", { className: "flex items-center gap-0.5", children: [
100061
+ /* @__PURE__ */ jsxs200("div", { className: "flex items-center gap-0.5", children: [
99862
100062
  rightActions,
99863
- /* @__PURE__ */ jsx252(
100063
+ /* @__PURE__ */ jsx253(
99864
100064
  Tooltip,
99865
100065
  {
99866
100066
  label: thumbnailsVisible ? "Hide thumbnails: labels only" : "Show thumbnails: posters stay visible; richer previews appear on interaction",
99867
- children: /* @__PURE__ */ jsx252(
100067
+ children: /* @__PURE__ */ jsx253(
99868
100068
  "button",
99869
100069
  {
99870
100070
  type: "button",
@@ -99872,12 +100072,12 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99872
100072
  "aria-pressed": thumbnailsVisible,
99873
100073
  onClick: () => setThumbnailMode(thumbnailsVisible ? "hidden" : "adaptive"),
99874
100074
  className: `h-7 px-2 rounded-md text-[11px] font-medium transition-colors ${thumbnailsVisible ? "bg-studio-accent/10 text-accent-ink" : "text-neutral-400 hover:bg-hover hover:text-neutral-200"}`,
99875
- children: /* @__PURE__ */ jsx252(Image3, { size: 16, "aria-hidden": "true" })
100075
+ children: /* @__PURE__ */ jsx253(Image3, { size: 16, "aria-hidden": "true" })
99876
100076
  }
99877
100077
  )
99878
100078
  }
99879
100079
  ),
99880
- /* @__PURE__ */ jsx252(Tooltip, { label: "Fit timeline to width", children: /* @__PURE__ */ jsx252(
100080
+ /* @__PURE__ */ jsx253(Tooltip, { label: "Fit timeline to width", children: /* @__PURE__ */ jsx253(
99881
100081
  "button",
99882
100082
  {
99883
100083
  type: "button",
@@ -99885,20 +100085,20 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99885
100085
  "aria-label": "Fit timeline to width",
99886
100086
  "aria-pressed": zoomMode === "fit",
99887
100087
  className: `h-7 px-2 rounded-md text-[11px] font-medium transition-colors ${zoomMode === "fit" ? "bg-studio-accent/10 text-accent-ink" : "text-neutral-400 hover:bg-hover hover:text-neutral-200"}`,
99888
- children: /* @__PURE__ */ jsx252(ArrowsOutLineHorizontal, { size: 16, "aria-hidden": "true" })
100088
+ children: /* @__PURE__ */ jsx253(ArrowsOutLineHorizontal, { size: 16, "aria-hidden": "true" })
99889
100089
  }
99890
100090
  ) }),
99891
- /* @__PURE__ */ jsx252(Tooltip, { label: "Zoom out", children: /* @__PURE__ */ jsx252(
100091
+ /* @__PURE__ */ jsx253(Tooltip, { label: "Zoom out", children: /* @__PURE__ */ jsx253(
99892
100092
  "button",
99893
100093
  {
99894
100094
  type: "button",
99895
100095
  "aria-label": "Zoom out",
99896
100096
  onClick: () => zoomTimelineStep("out"),
99897
100097
  className: flatIdle,
99898
- children: /* @__PURE__ */ jsx252(MagnifyingGlassMinus, { size: 16, "aria-hidden": "true" })
100098
+ children: /* @__PURE__ */ jsx253(MagnifyingGlassMinus, { size: 16, "aria-hidden": "true" })
99899
100099
  }
99900
100100
  ) }),
99901
- /* @__PURE__ */ jsx252(
100101
+ /* @__PURE__ */ jsx253(
99902
100102
  "input",
99903
100103
  {
99904
100104
  type: "range",
@@ -99913,17 +100113,17 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99913
100113
  className: "mx-1 h-6 w-[96px] cursor-pointer appearance-none bg-transparent [&::-webkit-slider-runnable-track]:h-[2px] [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:bg-neutral-700 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-[10px] [&::-webkit-slider-thumb]:h-[10px] [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-text-0 [&::-webkit-slider-thumb]:-mt-1 [&::-webkit-slider-thumb]:shadow-[0_0_0_2px_#0a0a0a,0_1px_3px_rgba(0,0,0,0.5)] [&::-webkit-slider-thumb]:cursor-grab [&::-webkit-slider-thumb:active]:cursor-grabbing"
99914
100114
  }
99915
100115
  ),
99916
- /* @__PURE__ */ jsx252(Tooltip, { label: "Zoom in", children: /* @__PURE__ */ jsx252(
100116
+ /* @__PURE__ */ jsx253(Tooltip, { label: "Zoom in", children: /* @__PURE__ */ jsx253(
99917
100117
  "button",
99918
100118
  {
99919
100119
  type: "button",
99920
100120
  "aria-label": "Zoom in",
99921
100121
  onClick: () => zoomTimelineStep("in"),
99922
100122
  className: flatIdle,
99923
- children: /* @__PURE__ */ jsx252(MagnifyingGlassPlus, { size: 16, "aria-hidden": "true" })
100123
+ children: /* @__PURE__ */ jsx253(MagnifyingGlassPlus, { size: 16, "aria-hidden": "true" })
99924
100124
  }
99925
100125
  ) }),
99926
- /* @__PURE__ */ jsx252(
100126
+ /* @__PURE__ */ jsx253(
99927
100127
  "span",
99928
100128
  {
99929
100129
  className: "ml-1 w-[38px] text-right font-mono text-[11px] tabular-nums text-neutral-500 select-none",
@@ -99936,23 +100136,23 @@ var TimelineToolbar = memo62(function TimelineToolbar2({
99936
100136
  });
99937
100137
 
99938
100138
  // src/components/StudioSplash.tsx
99939
- import { jsx as jsx253, jsxs as jsxs200 } from "react/jsx-runtime";
100139
+ import { jsx as jsx254, jsxs as jsxs201 } from "react/jsx-runtime";
99940
100140
  function StudioSplash({ waiting }) {
99941
- return /* @__PURE__ */ jsx253("div", { className: "h-full w-full bg-neutral-950 flex items-center justify-center", children: waiting ? /* @__PURE__ */ jsxs200("div", { className: "flex flex-col items-center gap-3 text-center px-6", role: "status", children: [
99942
- /* @__PURE__ */ jsx253("div", { className: "w-4 h-4 rounded-full border-2 border-neutral-700 border-t-neutral-500 animate-spin motion-reduce:animate-none" }),
99943
- /* @__PURE__ */ jsxs200("p", { className: "text-xs text-neutral-600", children: [
100141
+ return /* @__PURE__ */ jsx254("div", { className: "h-full w-full bg-neutral-950 flex items-center justify-center", children: waiting ? /* @__PURE__ */ jsxs201("div", { className: "flex flex-col items-center gap-3 text-center px-6", role: "status", children: [
100142
+ /* @__PURE__ */ jsx254("div", { className: "w-4 h-4 rounded-full border-2 border-neutral-700 border-t-neutral-500 animate-spin motion-reduce:animate-none" }),
100143
+ /* @__PURE__ */ jsxs201("p", { className: "text-xs text-neutral-600", children: [
99944
100144
  "Waiting for preview server\u2026 run",
99945
100145
  " ",
99946
- /* @__PURE__ */ jsx253("code", { className: "text-neutral-500 font-mono", children: "npm run dev" })
100146
+ /* @__PURE__ */ jsx254("code", { className: "text-neutral-500 font-mono", children: "npm run dev" })
99947
100147
  ] })
99948
- ] }) : /* @__PURE__ */ jsxs200("div", { className: "flex flex-col items-center gap-3 text-center px-6", role: "status", children: [
99949
- /* @__PURE__ */ jsx253("div", { className: "w-4 h-4 rounded-full bg-studio-accent animate-pulse motion-reduce:animate-none" }),
99950
- /* @__PURE__ */ jsx253("p", { className: "text-xs text-neutral-600", children: "Connecting to project\u2026" })
100148
+ ] }) : /* @__PURE__ */ jsxs201("div", { className: "flex flex-col items-center gap-3 text-center px-6", role: "status", children: [
100149
+ /* @__PURE__ */ jsx254("div", { className: "w-4 h-4 rounded-full bg-studio-accent animate-pulse motion-reduce:animate-none" }),
100150
+ /* @__PURE__ */ jsx254("p", { className: "text-xs text-neutral-600", children: "Connecting to project\u2026" })
99951
100151
  ] }) });
99952
100152
  }
99953
100153
 
99954
100154
  // src/hooks/useServerConnection.ts
99955
- import { useEffect as useEffect157, useState as useState162 } from "react";
100155
+ import { useEffect as useEffect158, useState as useState163 } from "react";
99956
100156
  async function resolveHashProject(id) {
99957
100157
  try {
99958
100158
  const res = await studioApiFetch(`/api/projects/${encodeURIComponent(id)}`);
@@ -99982,9 +100182,9 @@ function reportHashVerdict(stage, verdict) {
99982
100182
  });
99983
100183
  }
99984
100184
  function useServerConnection() {
99985
- const [projectId, setProjectId] = useState162(null);
99986
- const [resolving, setResolving] = useState162(true);
99987
- const [waitingForServer, setWaitingForServer] = useState162(false);
100185
+ const [projectId, setProjectId] = useState163(null);
100186
+ const [resolving, setResolving] = useState163(true);
100187
+ const [waitingForServer, setWaitingForServer] = useState163(false);
99988
100188
  useMountEffect(() => {
99989
100189
  const hashProjectId = parseProjectIdFromHash(window.location.hash);
99990
100190
  let cancelled = false;
@@ -100026,7 +100226,7 @@ function useServerConnection() {
100026
100226
  if (retryTimer !== null) clearTimeout(retryTimer);
100027
100227
  };
100028
100228
  });
100029
- useEffect157(() => {
100229
+ useEffect158(() => {
100030
100230
  const onHashChange = () => {
100031
100231
  const next = parseProjectIdFromHash(window.location.hash);
100032
100232
  if (!next || next === projectId) return;
@@ -100052,9 +100252,9 @@ function useServerConnection() {
100052
100252
  }
100053
100253
 
100054
100254
  // src/hooks/useStudioSessionStart.ts
100055
- import { useEffect as useEffect158 } from "react";
100255
+ import { useEffect as useEffect159 } from "react";
100056
100256
  function useStudioSessionStart(projectId, resolving, waitingForServer) {
100057
- useEffect158(() => {
100257
+ useEffect159(() => {
100058
100258
  if (resolving || waitingForServer || hasFiredSessionStart()) return;
100059
100259
  markSessionStartFired();
100060
100260
  trackStudioSessionStart({ has_project: projectId != null });
@@ -100062,13 +100262,13 @@ function useStudioSessionStart(projectId, resolving, waitingForServer) {
100062
100262
  }
100063
100263
 
100064
100264
  // src/hooks/useTimelineAddAtPlayhead.ts
100065
- import { useCallback as useCallback192 } from "react";
100265
+ import { useCallback as useCallback193 } from "react";
100066
100266
  function useTimelineAddAtPlayhead(addAsset, addComposition, projectId) {
100067
100267
  const placement = () => ({ start: usePlayerStore.getState().currentTime, track: 0 });
100068
100268
  return useStableHandlers(
100069
100269
  {
100070
- addAssetAtPlayhead: useCallback192((path) => addAsset(path, placement()), [addAsset]),
100071
- addCompositionAtPlayhead: useCallback192(
100270
+ addAssetAtPlayhead: useCallback193((path) => addAsset(path, placement()), [addAsset]),
100271
+ addCompositionAtPlayhead: useCallback193(
100072
100272
  (path) => addComposition(path, placement()),
100073
100273
  [addComposition]
100074
100274
  )
@@ -100078,10 +100278,10 @@ function useTimelineAddAtPlayhead(addAsset, addComposition, projectId) {
100078
100278
  }
100079
100279
 
100080
100280
  // src/hooks/useActiveComposition.ts
100081
- import { useState as useState163 } from "react";
100281
+ import { useState as useState164 } from "react";
100082
100282
 
100083
100283
  // src/hooks/useHydrateActiveCompPathFromUrl.ts
100084
- import { useEffect as useEffect159 } from "react";
100284
+ import { useEffect as useEffect160 } from "react";
100085
100285
  function useHydrateActiveCompPathFromUrl({
100086
100286
  hydrated,
100087
100287
  fileTreeLoaded,
@@ -100090,7 +100290,7 @@ function useHydrateActiveCompPathFromUrl({
100090
100290
  setActiveCompPath,
100091
100291
  setHydrated
100092
100292
  }) {
100093
- useEffect159(() => {
100293
+ useEffect160(() => {
100094
100294
  if (hydrated) return;
100095
100295
  if (!fileTreeLoaded) return;
100096
100296
  const nextCompPath = normalizeStudioCompositionPath(
@@ -100103,7 +100303,7 @@ function useHydrateActiveCompPathFromUrl({
100103
100303
  }
100104
100304
 
100105
100305
  // src/hooks/useAutoOpenRootComposition.ts
100106
- import { useEffect as useEffect160, useRef as useRef187 } from "react";
100306
+ import { useEffect as useEffect161, useRef as useRef188 } from "react";
100107
100307
  function useAutoOpenRootComposition({
100108
100308
  projectId,
100109
100309
  activeCompPath,
@@ -100111,8 +100311,8 @@ function useAutoOpenRootComposition({
100111
100311
  masterCompPath,
100112
100312
  onSelectComposition
100113
100313
  }) {
100114
- const settledForProjectRef = useRef187(void 0);
100115
- useEffect160(() => {
100314
+ const settledForProjectRef = useRef188(void 0);
100315
+ useEffect161(() => {
100116
100316
  if (settledForProjectRef.current === projectId) return;
100117
100317
  if (!activeCompPathHydrated) return;
100118
100318
  if (activeCompPath !== null) {
@@ -100126,14 +100326,14 @@ function useAutoOpenRootComposition({
100126
100326
  }
100127
100327
 
100128
100328
  // src/hooks/useCompositionContentLoader.ts
100129
- import { useCallback as useCallback193 } from "react";
100329
+ import { useCallback as useCallback194 } from "react";
100130
100330
  function useCompositionContentLoader({
100131
100331
  projectId,
100132
100332
  setEditingFile,
100133
100333
  setActiveCompPath,
100134
100334
  showToast
100135
100335
  }) {
100136
- return useCallback193(
100336
+ return useCallback194(
100137
100337
  (comp) => {
100138
100338
  if (!projectId) return;
100139
100339
  setActiveCompPath(comp.endsWith(".html") ? comp : null);
@@ -100153,14 +100353,14 @@ function useCompositionContentLoader({
100153
100353
  }
100154
100354
 
100155
100355
  // src/hooks/useResetSelectionOnProjectSwitch.ts
100156
- import { useRef as useRef188 } from "react";
100356
+ import { useRef as useRef189 } from "react";
100157
100357
  function useResetSelectionOnProjectSwitch({
100158
100358
  projectId,
100159
100359
  initialUrlStateRef,
100160
100360
  setActiveCompPath,
100161
100361
  setActiveCompPathHydrated
100162
100362
  }) {
100163
- const previousProjectIdRef = useRef188(projectId);
100363
+ const previousProjectIdRef = useRef189(projectId);
100164
100364
  if (previousProjectIdRef.current !== projectId) {
100165
100365
  const isSwitch = previousProjectIdRef.current !== null;
100166
100366
  previousProjectIdRef.current = projectId;
@@ -100182,8 +100382,8 @@ function useActiveComposition({
100182
100382
  setEditingFile,
100183
100383
  showToast
100184
100384
  }) {
100185
- const [activeCompPath, setActiveCompPath] = useState163(null);
100186
- const [activeCompPathHydrated, setActiveCompPathHydrated] = useState163(
100385
+ const [activeCompPath, setActiveCompPath] = useState164(null);
100386
+ const [activeCompPathHydrated, setActiveCompPathHydrated] = useState164(
100187
100387
  () => isHydratedFromUrlState(initialUrlStateRef.current)
100188
100388
  );
100189
100389
  useResetSelectionOnProjectSwitch({
@@ -100217,19 +100417,19 @@ function useActiveComposition({
100217
100417
  }
100218
100418
 
100219
100419
  // src/App.tsx
100220
- import { jsx as jsx254, jsxs as jsxs201 } from "react/jsx-runtime";
100420
+ import { jsx as jsx255, jsxs as jsxs202 } from "react/jsx-runtime";
100221
100421
  var getTimelineSelectionSet = () => usePlayerStore.getState().selectedElementIds;
100222
100422
  function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100223
100423
  const { projectId, resolving, waitingForServer } = useServerConnection();
100224
- const initialUrlStateRef = useRef189(readStudioUrlStateFromWindow());
100424
+ const initialUrlStateRef = useRef190(readStudioUrlStateFromWindow());
100225
100425
  useStudioSessionStart(projectId, resolving, waitingForServer);
100226
- const [compIdToSrc, setCompIdToSrc] = useState164(/* @__PURE__ */ new Map());
100426
+ const [compIdToSrc, setCompIdToSrc] = useState165(/* @__PURE__ */ new Map());
100227
100427
  const previewIframe = useOwnPreviewIframe();
100228
- const [compositionLoading, setCompositionLoading] = useState164(true);
100229
- const [refreshKey, setRefreshKeyState] = useState164(0);
100428
+ const [compositionLoading, setCompositionLoading] = useState165(true);
100429
+ const [refreshKey, setRefreshKeyState] = useState165(0);
100230
100430
  const [previewDocumentVersion, refreshPreviewDocumentVersion] = usePreviewDocumentVersion();
100231
- const [blockPreview, setBlockPreview] = useState164(null);
100232
- const previewIframeRef = useRef189(null);
100431
+ const [blockPreview, setBlockPreview] = useState165(null);
100432
+ const previewIframeRef = useRef190(null);
100233
100433
  const captionEditMode = useCaptionStore((s) => s.isEditMode);
100234
100434
  const captionHasSelection = useCaptionStore((s) => s.selectedSegmentIds.size > 0);
100235
100435
  const captionSync = useCaptionSync(projectId);
@@ -100248,14 +100448,14 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100248
100448
  rightPanelTab: initialUrlStateRef.current.rightPanelTab
100249
100449
  });
100250
100450
  const editHistory = useStableHandlers(usePersistentEditHistory({ projectId }), projectId);
100251
- const handleDomZIndexReorderCommitRef = useRef189(null);
100252
- const pendingTimelineEditPathRef = useRef189(/* @__PURE__ */ new Set());
100253
- const isGestureRecordingRef = useRef189(false);
100254
- const setRefreshKey = useCallback194((next) => {
100451
+ const handleDomZIndexReorderCommitRef = useRef190(null);
100452
+ const pendingTimelineEditPathRef = useRef190(/* @__PURE__ */ new Set());
100453
+ const isGestureRecordingRef = useRef190(false);
100454
+ const setRefreshKey = useCallback195((next) => {
100255
100455
  requestPreviewReload();
100256
100456
  setRefreshKeyState(next);
100257
100457
  }, []);
100258
- const reloadPreview = useCallback194(() => setRefreshKey((k) => k + 1), [setRefreshKey]);
100458
+ const reloadPreview = useCallback195(() => setRefreshKey((k) => k + 1), [setRefreshKey]);
100259
100459
  const fileManagerResult = useFileManager({
100260
100460
  projectId,
100261
100461
  showToast,
@@ -100263,7 +100463,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100263
100463
  setRefreshKey
100264
100464
  });
100265
100465
  const fileManager = useStableHandlers(fileManagerResult, projectId);
100266
- const masterCompPath = useMemo89(
100466
+ const masterCompPath = useMemo90(
100267
100467
  () => resolveMasterCompositionPath(fileManager.compositions),
100268
100468
  [fileManager.compositions]
100269
100469
  );
@@ -100284,7 +100484,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100284
100484
  fileManager.fileTreeLoaded,
100285
100485
  fileManager.refreshFileTree
100286
100486
  );
100287
- const activeCompPathRef = useRef189(activeCompPath);
100487
+ const activeCompPathRef = useRef190(activeCompPath);
100288
100488
  activeCompPathRef.current = activeCompPath;
100289
100489
  const renderQueue = useStableHandlers(useRenderQueue(projectId, activeCompPathRef), projectId);
100290
100490
  const previewPersistenceResult = usePreviewPersistence({
@@ -100307,9 +100507,9 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100307
100507
  reloadPreview,
100308
100508
  onOutsideChange: editHistory.noteOutsideChange
100309
100509
  });
100310
- const invalidateGsapCacheRef = useRef189(() => {
100510
+ const invalidateGsapCacheRef = useRef190(() => {
100311
100511
  });
100312
- const invalidateGsapCache = useCallback194(() => invalidateGsapCacheRef.current(), []);
100512
+ const invalidateGsapCache = useCallback195(() => invalidateGsapCacheRef.current(), []);
100313
100513
  const timelineEditing = useTimelineEditing({
100314
100514
  projectId,
100315
100515
  activeCompPath,
@@ -100365,20 +100565,20 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100365
100565
  handleAddMediaOverlay,
100366
100566
  handlePreviewBlockDrop
100367
100567
  } = useStableHandlers(blockHandlersResult, projectId);
100368
- const dismissBlockParams = useCallback194(() => setActiveBlockParams(null), [setActiveBlockParams]);
100568
+ const dismissBlockParams = useCallback195(() => setActiveBlockParams(null), [setActiveBlockParams]);
100369
100569
  const setRightPanelTab = useDismissingTabSetter(panelLayout.setRightPanelTab, dismissBlockParams);
100370
- const layout = useMemo89(
100570
+ const layout = useMemo90(
100371
100571
  () => ({ ...panelLayout, setRightPanelTab }),
100372
100572
  [panelLayout, setRightPanelTab]
100373
100573
  );
100374
- const clearDomSelectionRef = useRef189(() => {
100574
+ const clearDomSelectionRef = useRef190(() => {
100375
100575
  });
100376
- const domEditSelectionBridgeRef = useRef189(null);
100377
- const handleDomEditElementDeleteRef = useRef189(async () => {
100576
+ const domEditSelectionBridgeRef = useRef190(null);
100577
+ const handleDomEditElementDeleteRef = useRef190(async () => {
100378
100578
  });
100379
100579
  const domEditDeleteBridge = (s, o) => handleDomEditElementDeleteRef.current(s, o);
100380
- const resetKeyframesRef = useRef189(() => false);
100381
- const deleteSelectedKeyframesRef = useRef189(() => {
100580
+ const resetKeyframesRef = useRef190(() => false);
100581
+ const deleteSelectedKeyframesRef = useRef190(() => {
100382
100582
  });
100383
100583
  const { handleCopy: handleCopy2, handlePaste: handlePaste2, handleCut, handleDuplicate, canPaste } = useClipboard({
100384
100584
  projectId,
@@ -100508,9 +100708,9 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100508
100708
  const frameCapture = useStableHandlers(frameCaptureResult, projectId);
100509
100709
  const { consoleErrors, setConsoleErrors } = useConsoleErrorCapture(previewIframe);
100510
100710
  const fileDrop = useGlobalFileDrop(timelineEditing.handleTimelineFileDrop);
100511
- const handleToggleRecordingRef = useRef189(() => {
100711
+ const handleToggleRecordingRef = useRef190(() => {
100512
100712
  });
100513
- const domEditSessionRef = useRef189(domEditSession);
100713
+ const domEditSessionRef = useRef190(domEditSession);
100514
100714
  domEditSessionRef.current = domEditSession;
100515
100715
  const { gestureState, gestureRecording, handleToggleRecording } = useGestureCommit({
100516
100716
  projectId,
@@ -100526,7 +100726,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100526
100726
  previewIframe,
100527
100727
  compositionDimensions
100528
100728
  );
100529
- const handlePreviewIframeRef = useCallback194(
100729
+ const handlePreviewIframeRef = useCallback195(
100530
100730
  (iframe) => {
100531
100731
  previewIframeRef.current = iframe;
100532
100732
  usePreviewIframeStore.getState().setIframe(iframe);
@@ -100587,8 +100787,8 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100587
100787
  handlePreviewIframeRef,
100588
100788
  refreshPreviewDocumentVersion
100589
100789
  });
100590
- const timelineToolbar = useMemo89(
100591
- () => /* @__PURE__ */ jsx254(
100790
+ const timelineToolbar = useMemo90(
100791
+ () => /* @__PURE__ */ jsx255(
100592
100792
  TimelineToolbar,
100593
100793
  {
100594
100794
  domEditSession: toolbarSession,
@@ -100597,10 +100797,10 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100597
100797
  ),
100598
100798
  [toolbarSession, timelineEditing.handleTimelineElementSplit]
100599
100799
  );
100600
- const handleExport = useCallback194(() => {
100800
+ const handleExport = useCallback195(() => {
100601
100801
  void previewPersistence.waitForPendingDomEditSaves().then(() => renderQueue.startRender(void 0));
100602
100802
  }, [previewPersistence, renderQueue]);
100603
- const clearConsoleErrors = useCallback194(() => setConsoleErrors(null), [setConsoleErrors]);
100803
+ const clearConsoleErrors = useCallback195(() => setConsoleErrors(null), [setConsoleErrors]);
100604
100804
  const panels = useStudioSidePanels(
100605
100805
  {
100606
100806
  onSelectComposition: handleSelectComposition,
@@ -100636,15 +100836,15 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100636
100836
  projectId
100637
100837
  );
100638
100838
  if (resolving || waitingForServer || !projectId)
100639
- return /* @__PURE__ */ jsx254(StudioSplash, { waiting: waitingForServer });
100640
- return /* @__PURE__ */ jsx254(StudioShellProvider, { value: studioCtxValue, children: /* @__PURE__ */ jsx254(StudioPlaybackProvider, { value: studioCtxValue, children: /* @__PURE__ */ jsx254(PanelLayoutProvider, { value: layout, children: /* @__PURE__ */ jsx254(FileManagerProvider, { value: fileManager, children: /* @__PURE__ */ jsx254(DomEditProvider, { value: domEditSession, children: /* @__PURE__ */ jsxs201(
100839
+ return /* @__PURE__ */ jsx255(StudioSplash, { waiting: waitingForServer });
100840
+ return /* @__PURE__ */ jsx255(StudioShellProvider, { value: studioCtxValue, children: /* @__PURE__ */ jsx255(StudioPlaybackProvider, { value: studioCtxValue, children: /* @__PURE__ */ jsx255(PanelLayoutProvider, { value: layout, children: /* @__PURE__ */ jsx255(FileManagerProvider, { value: fileManager, children: /* @__PURE__ */ jsx255(DomEditProvider, { value: domEditSession, children: /* @__PURE__ */ jsxs202(
100641
100841
  "div",
100642
100842
  {
100643
100843
  className: "flex flex-col h-full w-full bg-neutral-950 relative",
100644
100844
  onDragOver: fileDrop.onDragOver,
100645
100845
  onDrop: fileDrop.onDrop,
100646
100846
  children: [
100647
- /* @__PURE__ */ jsx254(
100847
+ /* @__PURE__ */ jsx255(
100648
100848
  StudioHeader,
100649
100849
  {
100650
100850
  captureFrameHref: frameCapture.captureFrameHref,
@@ -100657,17 +100857,17 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100657
100857
  onExport: handleExport
100658
100858
  }
100659
100859
  ),
100660
- previewPersistence.domEditSaveQueuePaused && !externalFileChanges.blocked && /* @__PURE__ */ jsx254(
100860
+ previewPersistence.domEditSaveQueuePaused && !externalFileChanges.blocked && /* @__PURE__ */ jsx255(
100661
100861
  SaveQueuePausedBanner,
100662
100862
  {
100663
100863
  message: previewPersistence.domEditSaveQueuePaused,
100664
100864
  onRetry: previewPersistence.resetDomEditSaveQueueBreaker
100665
100865
  }
100666
100866
  ),
100667
- /* @__PURE__ */ jsx254(ExternalFileConflictBanner, { coordinator: externalFileChanges }),
100668
- sdkHandle.unreachableProject && /* @__PURE__ */ jsx254(ProjectUnreachableBanner, { projectId: sdkHandle.unreachableProject }),
100669
- sdkHandle.compositionMissing && activeCompPath && /* @__PURE__ */ jsx254(CompositionMissingBanner, { path: activeCompPath }),
100670
- /* @__PURE__ */ jsx254(
100867
+ /* @__PURE__ */ jsx255(ExternalFileConflictBanner, { coordinator: externalFileChanges }),
100868
+ sdkHandle.unreachableProject && /* @__PURE__ */ jsx255(ProjectUnreachableBanner, { projectId: sdkHandle.unreachableProject }),
100869
+ sdkHandle.compositionMissing && activeCompPath && /* @__PURE__ */ jsx255(CompositionMissingBanner, { path: activeCompPath }),
100870
+ /* @__PURE__ */ jsx255(
100671
100871
  EditorShell,
100672
100872
  {
100673
100873
  readOnlyPreview,
@@ -100711,7 +100911,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100711
100911
  gestureOverlay
100712
100912
  }
100713
100913
  ),
100714
- /* @__PURE__ */ jsx254(
100914
+ /* @__PURE__ */ jsx255(
100715
100915
  StudioOverlays,
100716
100916
  {
100717
100917
  projectId,
@@ -100732,35 +100932,35 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
100732
100932
  }
100733
100933
 
100734
100934
  // src/hooks/useElementPicker.ts
100735
- import { useState as useState165, useCallback as useCallback195, useRef as useRef190 } from "react";
100935
+ import { useState as useState166, useCallback as useCallback196, useRef as useRef191 } from "react";
100736
100936
  function useElementPicker(iframeRef, options) {
100737
- const [isPickMode, setIsPickMode] = useState165(false);
100738
- const [pickedElement, setPickedElement] = useState165(null);
100739
- const activeOverrideRef = useRef190(null);
100740
- const getActiveIframe = useCallback195(() => {
100937
+ const [isPickMode, setIsPickMode] = useState166(false);
100938
+ const [pickedElement, setPickedElement] = useState166(null);
100939
+ const activeOverrideRef = useRef191(null);
100940
+ const getActiveIframe = useCallback196(() => {
100741
100941
  return activeOverrideRef.current ?? iframeRef.current;
100742
100942
  }, [iframeRef]);
100743
- const setActiveIframe = useCallback195((el) => {
100943
+ const setActiveIframe = useCallback196((el) => {
100744
100944
  activeOverrideRef.current = el;
100745
100945
  }, []);
100746
- const enablePick = useCallback195(() => {
100946
+ const enablePick = useCallback196(() => {
100747
100947
  try {
100748
100948
  postRuntimeControlMessage(getActiveIframe()?.contentWindow, "enable-pick-mode");
100749
100949
  setIsPickMode(true);
100750
100950
  } catch {
100751
100951
  }
100752
100952
  }, [getActiveIframe]);
100753
- const disablePick = useCallback195(() => {
100953
+ const disablePick = useCallback196(() => {
100754
100954
  try {
100755
100955
  postRuntimeControlMessage(getActiveIframe()?.contentWindow, "disable-pick-mode");
100756
100956
  } catch {
100757
100957
  }
100758
100958
  setIsPickMode(false);
100759
100959
  }, [getActiveIframe]);
100760
- const clearPick = useCallback195(() => {
100960
+ const clearPick = useCallback196(() => {
100761
100961
  setPickedElement(null);
100762
100962
  }, []);
100763
- const windowsThatWerePrimary = useRef190(/* @__PURE__ */ new WeakSet());
100963
+ const windowsThatWerePrimary = useRef191(/* @__PURE__ */ new WeakSet());
100764
100964
  useMountEffect(() => {
100765
100965
  const handleMessage = (e) => {
100766
100966
  const data = e.data;
@@ -100787,11 +100987,11 @@ function useElementPicker(iframeRef, options) {
100787
100987
  window.addEventListener("message", handleMessage);
100788
100988
  return () => window.removeEventListener("message", handleMessage);
100789
100989
  });
100790
- const optionsRef = useRef190(options);
100990
+ const optionsRef = useRef191(options);
100791
100991
  optionsRef.current = options;
100792
- const pendingWritesRef = useRef190(/* @__PURE__ */ new Map());
100992
+ const pendingWritesRef = useRef191(/* @__PURE__ */ new Map());
100793
100993
  settlePendingWrites(pendingWritesRef.current, options?.workspaceFiles);
100794
- const syncToSource = useCallback195(
100994
+ const syncToSource = useCallback196(
100795
100995
  (picked, live, iframe, op) => {
100796
100996
  const opts = optionsRef.current;
100797
100997
  if (!opts?.workspaceFiles || !opts.onSyncFiles) return;
@@ -100805,7 +101005,7 @@ function useElementPicker(iframeRef, options) {
100805
101005
  },
100806
101006
  []
100807
101007
  );
100808
- const setStyle = useCallback195(
101008
+ const setStyle = useCallback196(
100809
101009
  (prop, value) => {
100810
101010
  const activeIframe = getActiveIframe();
100811
101011
  if (!pickedElement?.selector || !activeIframe) return;
@@ -100831,7 +101031,7 @@ function useElementPicker(iframeRef, options) {
100831
101031
  },
100832
101032
  [pickedElement, getActiveIframe, syncToSource]
100833
101033
  );
100834
- const setDataAttr = useCallback195(
101034
+ const setDataAttr = useCallback196(
100835
101035
  (attr, value) => {
100836
101036
  const activeIframe = getActiveIframe();
100837
101037
  if (!pickedElement?.selector || !activeIframe) return;
@@ -100857,7 +101057,7 @@ function useElementPicker(iframeRef, options) {
100857
101057
  },
100858
101058
  [pickedElement, getActiveIframe, syncToSource]
100859
101059
  );
100860
- const setTextContent = useCallback195(
101060
+ const setTextContent = useCallback196(
100861
101061
  (text) => {
100862
101062
  const activeIframe = getActiveIframe();
100863
101063
  if (!pickedElement?.selector || !activeIframe) return;
@@ -100878,7 +101078,7 @@ function useElementPicker(iframeRef, options) {
100878
101078
  },
100879
101079
  [pickedElement, getActiveIframe, syncToSource]
100880
101080
  );
100881
- const activeIframeRef = useRef190(null);
101081
+ const activeIframeRef = useRef191(null);
100882
101082
  activeIframeRef.current = getActiveIframe();
100883
101083
  return {
100884
101084
  isPickMode,
@@ -101096,7 +101296,7 @@ function findElementBlock(html2, elementId2) {
101096
101296
  }
101097
101297
 
101098
101298
  // src/hooks/useDomStyleCommit.ts
101099
- import { useCallback as useCallback196, useMemo as useMemo90, useRef as useRef191, useState as useState166 } from "react";
101299
+ import { useCallback as useCallback197, useMemo as useMemo91, useRef as useRef192, useState as useState167 } from "react";
101100
101300
  var noop = () => {
101101
101301
  };
101102
101302
  function useDomStyleCommit({
@@ -101107,12 +101307,12 @@ function useDomStyleCommit({
101107
101307
  activeCompPath = null,
101108
101308
  showToast = noop
101109
101309
  }) {
101110
- const projectIdRef = useRef191(projectId);
101310
+ const projectIdRef = useRef192(projectId);
101111
101311
  projectIdRef.current = projectId;
101112
- const [queue2] = useState166(createDomEditSaveQueue);
101312
+ const [queue2] = useState167(createDomEditSaveQueue);
101113
101313
  useMountEffect(() => () => queue2.destroy());
101114
- const versions = useRef191(/* @__PURE__ */ new Map()).current;
101115
- const editHistory = useMemo90(() => ({ recordEdit }), [recordEdit]);
101314
+ const versions = useRef192(/* @__PURE__ */ new Map()).current;
101315
+ const editHistory = useMemo91(() => ({ recordEdit }), [recordEdit]);
101116
101316
  const persistDomEditOperations = useDomEditPersist({
101117
101317
  activeCompPath,
101118
101318
  previewIframeRef: iframeRef,
@@ -101123,7 +101323,7 @@ function useDomStyleCommit({
101123
101323
  projectIdRef,
101124
101324
  reloadPreview: noop
101125
101325
  });
101126
- const commitStyle = useCallback196(
101326
+ const commitStyle = useCallback197(
101127
101327
  (selection, styles) => {
101128
101328
  queue2.reset();
101129
101329
  return projectIdRef.current ? commitDomStyles(
@@ -101144,7 +101344,7 @@ function useDomStyleCommit({
101144
101344
  }
101145
101345
 
101146
101346
  // src/hooks/useDomGeometryCommit.ts
101147
- import { useCallback as useCallback197, useMemo as useMemo91, useRef as useRef192, useState as useState167 } from "react";
101347
+ import { useCallback as useCallback198, useMemo as useMemo92, useRef as useRef193, useState as useState168 } from "react";
101148
101348
  var noop2 = () => {
101149
101349
  };
101150
101350
  var NO_SELECTED_ANIMATIONS = [];
@@ -101157,10 +101357,10 @@ function useDomGeometryCommit({
101157
101357
  showToast = noop2,
101158
101358
  reloadPreview
101159
101359
  }) {
101160
- const projectIdRef = useRef192(projectId);
101360
+ const projectIdRef = useRef193(projectId);
101161
101361
  projectIdRef.current = projectId;
101162
- const pending2 = useRef192(/* @__PURE__ */ new Set()).current;
101163
- const editHistory = useMemo91(() => ({ recordEdit }), [recordEdit]);
101362
+ const pending2 = useRef193(/* @__PURE__ */ new Set()).current;
101363
+ const editHistory = useMemo92(() => ({ recordEdit }), [recordEdit]);
101164
101364
  const { bump: bumpGsapCache } = useGsapCacheVersion();
101165
101365
  const gsap3 = useGsapScriptCommits({
101166
101366
  projectIdRef,
@@ -101172,7 +101372,7 @@ function useDomGeometryCommit({
101172
101372
  showToast,
101173
101373
  writeProjectFile
101174
101374
  });
101175
- const [queue2] = useState167(createDomEditSaveQueue);
101375
+ const [queue2] = useState168(createDomEditSaveQueue);
101176
101376
  useMountEffect(() => () => queue2.destroy());
101177
101377
  const persistDomEditOperations = useDomEditPersist({
101178
101378
  activeCompPath,
@@ -101189,7 +101389,7 @@ function useDomGeometryCommit({
101189
101389
  persistDomEditOperations,
101190
101390
  showToast
101191
101391
  });
101192
- const commitWithFreshQueue = useCallback197(
101392
+ const commitWithFreshQueue = useCallback198(
101193
101393
  (...args) => {
101194
101394
  queue2.reset();
101195
101395
  return commitPositionPatchToHtml(...args);
@@ -101228,7 +101428,7 @@ function useDomGeometryCommit({
101228
101428
  setArcPath: gsap3.setArcPath,
101229
101429
  updateArcSegment: gsap3.updateArcSegment
101230
101430
  });
101231
- const saved2 = useCallback197(
101431
+ const saved2 = useCallback198(
101232
101432
  async (commit2, restore2 = noop2) => {
101233
101433
  const refusal2 = !projectIdRef.current ? "No project is open" : !isPreviewBooted(projectIdRef.current) ? "The preview for this project has not loaded yet" : null;
101234
101434
  if (refusal2) {
@@ -101246,7 +101446,7 @@ function useDomGeometryCommit({
101246
101446
  },
101247
101447
  [pending2, showToast]
101248
101448
  );
101249
- return useMemo91(
101449
+ return useMemo92(
101250
101450
  () => ({
101251
101451
  commitPathOffset: (selection, next, modifiers) => saved2(() => handleGsapAwarePathOffsetCommit(selection, next, modifiers)),
101252
101452
  commitGroupPathOffset: (updates) => saved2(() => handleGsapAwareGroupPathOffsetCommit(updates)),