hyperframes 0.8.80 → 0.8.81

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.
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
6
6
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
7
  <title>HyperFrames Studio</title>
8
- <script type="module" crossorigin src="/assets/index-CsG24Jub.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-hNVM9Pd2.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-BWJqxeaH.css">
10
10
  </head>
11
11
  <body>
@@ -68183,7 +68183,7 @@ var FileTree = memo38(function FileTree2({
68183
68183
  });
68184
68184
 
68185
68185
  // src/App.tsx
68186
- import { useState as useState155, useCallback as useCallback182, useRef as useRef186, useMemo as useMemo77, useLayoutEffect as useLayoutEffect11 } from "react";
68186
+ import { useState as useState155, useCallback as useCallback183, useRef as useRef186, useMemo as useMemo77, useLayoutEffect as useLayoutEffect11 } from "react";
68187
68187
 
68188
68188
  // src/hooks/useRightPanelIntents.ts
68189
68189
  import { useCallback as useCallback90, useEffect as useEffect101, useRef as useRef121 } from "react";
@@ -82945,7 +82945,9 @@ function useLintModal(projectId, refreshKey) {
82945
82945
  );
82946
82946
  const findingsByElement = useMemo69(() => groupFindings((f) => f.elementId), [groupFindings]);
82947
82947
  const findingsByFile = useMemo69(() => groupFindings((f) => f.file), [groupFindings]);
82948
- const hasLintError = (lintModal ?? backgroundFindings).some((f) => f.severity === "error");
82948
+ const badgeFindings = lintModal ?? backgroundFindings;
82949
+ const lintFindingCount = badgeFindings.length;
82950
+ const hasLintError = badgeFindings.some((f) => f.severity === "error");
82949
82951
  useEffect125(() => {
82950
82952
  usePlayerStore.getState().setLintFindingsByElement(findingsByElement);
82951
82953
  }, [findingsByElement]);
@@ -82957,6 +82959,7 @@ function useLintModal(projectId, refreshKey) {
82957
82959
  backgroundFindings,
82958
82960
  findingsByElement,
82959
82961
  findingsByFile,
82962
+ lintFindingCount,
82960
82963
  hasLintError
82961
82964
  };
82962
82965
  }
@@ -86724,6 +86727,7 @@ function CompositionsPanel(props) {
86724
86727
 
86725
86728
  // src/components/sidebar/SidebarLintButton.tsx
86726
86729
  import { jsx as jsx216, jsxs as jsxs171 } from "react/jsx-runtime";
86730
+ var ERROR_PULSES_BEFORE_IDLE = 3;
86727
86731
  function SidebarLintButton({
86728
86732
  onLint,
86729
86733
  linting,
@@ -86758,12 +86762,14 @@ function SidebarLintButton({
86758
86762
  "span",
86759
86763
  {
86760
86764
  "data-lint-badge": hasError ? "error" : "warning",
86765
+ style: hasError ? { animationIterationCount: ERROR_PULSES_BEFORE_IDLE } : void 0,
86761
86766
  className: hasError ? "ml-1 min-w-[16px] rounded-full bg-panel-danger/25 px-1 text-[9px] font-bold text-panel-danger animate-pulse motion-reduce:animate-none" : "ml-1 min-w-[16px] rounded-full bg-amber-500/20 px-1 text-[9px] font-bold text-amber-400",
86762
86767
  children: [
86763
86768
  findingCount,
86764
86769
  /* @__PURE__ */ jsx216("span", { className: "sr-only", children: hasError ? " lint findings, including errors" : " lint findings, warnings only" })
86765
86770
  ]
86766
- }
86771
+ },
86772
+ findingCount
86767
86773
  )
86768
86774
  ]
86769
86775
  }
@@ -86941,7 +86947,7 @@ function CodeBody({
86941
86947
  }
86942
86948
 
86943
86949
  // src/components/StudioRightPanels.tsx
86944
- import { useCallback as useCallback177 } from "react";
86950
+ import { useCallback as useCallback178 } from "react";
86945
86951
 
86946
86952
  // src/components/editor/LayersPanel.tsx
86947
86953
  import { memo as memo44, useState as useState140, useCallback as useCallback163, useEffect as useEffect138, useRef as useRef176 } from "react";
@@ -90175,11 +90181,11 @@ async function readGroup(path, readProjectFile) {
90175
90181
  return null;
90176
90182
  }
90177
90183
  }
90178
- function useProjectCompositionVariables(fileTree, excludePath, readProjectFile, refreshKey) {
90184
+ function useProjectCompositionVariables(compositionPaths, excludePath, readProjectFile, refreshKey) {
90179
90185
  const [groups, setGroups] = useState149([]);
90180
90186
  useEffect144(() => {
90181
90187
  let cancelled = false;
90182
- const htmlFiles = fileTree.filter((p) => p.endsWith(".html") && p !== excludePath);
90188
+ const htmlFiles = compositionPaths.filter((p) => p !== excludePath);
90183
90189
  void (async () => {
90184
90190
  const out = [];
90185
90191
  for (const path of htmlFiles) {
@@ -90191,7 +90197,7 @@ function useProjectCompositionVariables(fileTree, excludePath, readProjectFile,
90191
90197
  return () => {
90192
90198
  cancelled = true;
90193
90199
  };
90194
- }, [fileTree, excludePath, readProjectFile, refreshKey]);
90200
+ }, [compositionPaths, excludePath, readProjectFile, refreshKey]);
90195
90201
  return groups;
90196
90202
  }
90197
90203
  function useEditVariablesInFile(deps) {
@@ -90551,7 +90557,7 @@ function CompositionSection({
90551
90557
  ] });
90552
90558
  }
90553
90559
  function VariablesOtherCompositions({
90554
- fileTree,
90560
+ compositionPaths,
90555
90561
  excludePath,
90556
90562
  refreshKey,
90557
90563
  readProjectFile,
@@ -90561,7 +90567,7 @@ function VariablesOtherCompositions({
90561
90567
  }) {
90562
90568
  const [selfRefresh, setSelfRefresh] = useState151(0);
90563
90569
  const groups = useProjectCompositionVariables(
90564
- fileTree,
90570
+ compositionPaths,
90565
90571
  excludePath,
90566
90572
  readProjectFile,
90567
90573
  `${refreshKey}:${selfRefresh}`
@@ -90777,7 +90783,7 @@ var VariablesPanel = memo52(function VariablesPanel2({
90777
90783
  }) {
90778
90784
  const { activeCompPath, showToast } = useStudioShellContext();
90779
90785
  const { refreshKey } = useStudioPlaybackContext();
90780
- const { readProjectFile, writeProjectFile, fileTree, compositions } = useFileManagerContext();
90786
+ const { readProjectFile, writeProjectFile, compositions } = useFileManagerContext();
90781
90787
  const { domEditSelection } = useDomEditContext();
90782
90788
  const effectiveCompPath = activeCompPath ?? resolveMasterCompositionPath(compositions);
90783
90789
  const previewValues = usePreviewVariablesStore((s) => s.values);
@@ -91024,8 +91030,8 @@ var VariablesPanel = memo52(function VariablesPanel2({
91024
91030
  /* @__PURE__ */ jsx232(
91025
91031
  VariablesOtherCompositions,
91026
91032
  {
91027
- fileTree,
91028
- excludePath: activeCompPath ?? "index.html",
91033
+ compositionPaths: compositions,
91034
+ excludePath: effectiveCompPath,
91029
91035
  refreshKey: `${refreshKey}:${revision}`,
91030
91036
  readProjectFile,
91031
91037
  writeProjectFile,
@@ -91196,46 +91202,8 @@ function DesignPanelPromoteProvider({
91196
91202
  );
91197
91203
  }
91198
91204
 
91199
- // src/components/editor/colorGradingScopePatch.ts
91200
- var MEDIA_TAG_RE = /<\s*(video|img)\b(?:[^>"']|"[^"]*"|'[^']*')*>/gi;
91201
- var COLOR_GRADING_ATTR_RE = /\sdata-color-grading=(?:"[^"]*"|'[^']*')/i;
91202
- var IGNORED_HTML_RANGE_RE = /<!--[\s\S]*?-->|<(script|style)\b[\s\S]*?<\/\1\s*>/gi;
91203
- function collectIgnoredRanges(html2) {
91204
- const ranges = [];
91205
- let match;
91206
- IGNORED_HTML_RANGE_RE.lastIndex = 0;
91207
- while ((match = IGNORED_HTML_RANGE_RE.exec(html2)) !== null) {
91208
- ranges.push({ start: match.index, end: match.index + match[0].length });
91209
- }
91210
- return ranges;
91211
- }
91212
- function isInsideRange(offset, ranges) {
91213
- return ranges.some((range) => offset >= range.start && offset < range.end);
91214
- }
91215
- function escapeHtmlAttribute2(value) {
91216
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
91217
- }
91218
- function patchMediaTag(tag, value) {
91219
- if (value === null || value === "") {
91220
- return tag.replace(COLOR_GRADING_ATTR_RE, "");
91221
- }
91222
- const nextAttr = ` data-color-grading="${escapeHtmlAttribute2(value)}"`;
91223
- if (COLOR_GRADING_ATTR_RE.test(tag)) {
91224
- return tag.replace(COLOR_GRADING_ATTR_RE, nextAttr);
91225
- }
91226
- return tag.replace(/\s*\/?>$/, (end) => `${nextAttr}${end}`);
91227
- }
91228
- function patchMediaColorGradingInHtml(html2, value) {
91229
- let count = 0;
91230
- const ignoredRanges = collectIgnoredRanges(html2);
91231
- const patched = html2.replace(MEDIA_TAG_RE, (tag, _tagName, offset) => {
91232
- if (isInsideRange(offset, ignoredRanges)) return tag;
91233
- const next = patchMediaTag(tag, value);
91234
- if (next !== tag) count += 1;
91235
- return next;
91236
- });
91237
- return { html: patched, count };
91238
- }
91205
+ // src/hooks/useRemoveBackground.ts
91206
+ import { useCallback as useCallback176, useEffect as useEffect146, useRef as useRef182 } from "react";
91239
91207
 
91240
91208
  // src/components/studioMediaJobs.ts
91241
91209
  var MEDIA_JOB_RECONNECT_TIMEOUT_MS = 15e3;
@@ -91339,13 +91307,104 @@ function waitForMediaJob(jobId, onProgress, signal) {
91339
91307
  });
91340
91308
  }
91341
91309
 
91310
+ // src/hooks/useRemoveBackground.ts
91311
+ function useRemoveBackground(projectId, refreshFileTree, showToast) {
91312
+ const abortRef = useRef182(null);
91313
+ useEffect146(
91314
+ () => () => {
91315
+ abortRef.current?.abort();
91316
+ },
91317
+ []
91318
+ );
91319
+ return useCallback176(
91320
+ // fallow-ignore-next-line complexity
91321
+ async (inputPath, options) => {
91322
+ const response = await fetch(
91323
+ `/api/projects/${encodeURIComponent(projectId)}/media/remove-background`,
91324
+ {
91325
+ method: "POST",
91326
+ headers: { "Content-Type": "application/json" },
91327
+ body: JSON.stringify({
91328
+ inputPath,
91329
+ createBackgroundPlate: options.createBackgroundPlate === true,
91330
+ quality: options.quality ?? "balanced"
91331
+ })
91332
+ }
91333
+ );
91334
+ const data = await response.json().catch(() => ({}));
91335
+ if (!response.ok || !data.jobId) {
91336
+ throw new Error(data.error || `Background removal failed (${response.status})`);
91337
+ }
91338
+ showToast("Removing background...", "info");
91339
+ abortRef.current?.abort();
91340
+ const controller = new AbortController();
91341
+ abortRef.current = controller;
91342
+ try {
91343
+ const result = await waitForMediaJob(data.jobId, options.onProgress, controller.signal);
91344
+ await refreshFileTree();
91345
+ showToast(`Created transparent asset: ${result.outputPath.split("/").pop()}`, "info");
91346
+ return result;
91347
+ } finally {
91348
+ if (abortRef.current === controller) {
91349
+ abortRef.current = null;
91350
+ }
91351
+ }
91352
+ },
91353
+ [projectId, refreshFileTree, showToast]
91354
+ );
91355
+ }
91356
+
91357
+ // src/hooks/useApplyColorGradingScope.ts
91358
+ import { useCallback as useCallback177 } from "react";
91359
+
91360
+ // src/components/editor/colorGradingScopePatch.ts
91361
+ var MEDIA_TAG_RE = /<\s*(video|img)\b(?:[^>"']|"[^"]*"|'[^']*')*>/gi;
91362
+ var COLOR_GRADING_ATTR_RE = /\sdata-color-grading=(?:"[^"]*"|'[^']*')/i;
91363
+ var IGNORED_HTML_RANGE_RE = /<!--[\s\S]*?-->|<(script|style)\b[\s\S]*?<\/\1\s*>/gi;
91364
+ function collectIgnoredRanges(html2) {
91365
+ const ranges = [];
91366
+ let match;
91367
+ IGNORED_HTML_RANGE_RE.lastIndex = 0;
91368
+ while ((match = IGNORED_HTML_RANGE_RE.exec(html2)) !== null) {
91369
+ ranges.push({ start: match.index, end: match.index + match[0].length });
91370
+ }
91371
+ return ranges;
91372
+ }
91373
+ function isInsideRange(offset, ranges) {
91374
+ return ranges.some((range) => offset >= range.start && offset < range.end);
91375
+ }
91376
+ function escapeHtmlAttribute2(value) {
91377
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
91378
+ }
91379
+ function patchMediaTag(tag, value) {
91380
+ if (value === null || value === "") {
91381
+ return tag.replace(COLOR_GRADING_ATTR_RE, "");
91382
+ }
91383
+ const nextAttr = ` data-color-grading="${escapeHtmlAttribute2(value)}"`;
91384
+ if (COLOR_GRADING_ATTR_RE.test(tag)) {
91385
+ return tag.replace(COLOR_GRADING_ATTR_RE, nextAttr);
91386
+ }
91387
+ return tag.replace(/\s*\/?>$/, (end) => `${nextAttr}${end}`);
91388
+ }
91389
+ function patchMediaColorGradingInHtml(html2, value) {
91390
+ let count = 0;
91391
+ const ignoredRanges = collectIgnoredRanges(html2);
91392
+ const patched = html2.replace(MEDIA_TAG_RE, (tag, _tagName, offset) => {
91393
+ if (isInsideRange(offset, ignoredRanges)) return tag;
91394
+ const next = patchMediaTag(tag, value);
91395
+ if (next !== tag) count += 1;
91396
+ return next;
91397
+ });
91398
+ return { html: patched, count };
91399
+ }
91400
+
91342
91401
  // src/components/studioColorGradingScope.ts
91343
91402
  var EMPTY_COLOR_GRADING_SCOPE_RESULT = {
91344
91403
  changedFiles: 0,
91345
91404
  changedElements: 0
91346
91405
  };
91347
- function colorGradingScopePaths(scope, selectedSourceFile, fileTree) {
91348
- return scope === "source-file" ? [selectedSourceFile] : fileTree.filter((path) => /\.html?$/i.test(path));
91406
+ function colorGradingScopePaths(scope, selectedSourceFile, compositionPaths) {
91407
+ return scope === "source-file" ? [selectedSourceFile] : compositionPaths;
91349
91408
  }
91350
91409
  async function patchColorGradingScopeFiles(paths, value, readProjectFile) {
91351
91410
  const snapshots = await Promise.all(
@@ -91369,7 +91428,7 @@ async function applyColorGradingScopeUpdate({
91369
91428
  scope,
91370
91429
  value,
91371
91430
  selectedSourceFile,
91372
- fileTree,
91431
+ compositionPaths,
91373
91432
  projectId,
91374
91433
  waitForPendingDomEditSaves,
91375
91434
  readProjectFile,
@@ -91387,7 +91446,7 @@ async function applyColorGradingScopeUpdate({
91387
91446
  return EMPTY_COLOR_GRADING_SCOPE_RESULT;
91388
91447
  }
91389
91448
  const { files, changedElements } = await patchColorGradingScopeFiles(
91390
- colorGradingScopePaths(scope, selectedSourceFile, fileTree),
91449
+ colorGradingScopePaths(scope, selectedSourceFile, compositionPaths),
91391
91450
  value,
91392
91451
  readProjectFile
91393
91452
  );
@@ -91411,51 +91470,43 @@ async function applyColorGradingScopeUpdate({
91411
91470
  return { changedFiles: changedPaths.length, changedElements };
91412
91471
  }
91413
91472
 
91414
- // src/hooks/useRemoveBackground.ts
91415
- import { useCallback as useCallback176, useEffect as useEffect146, useRef as useRef182 } from "react";
91416
- function useRemoveBackground(projectId, refreshFileTree, showToast) {
91417
- const abortRef = useRef182(null);
91418
- useEffect146(
91419
- () => () => {
91420
- abortRef.current?.abort();
91421
- },
91422
- []
91423
- );
91424
- return useCallback176(
91425
- // fallow-ignore-next-line complexity
91426
- async (inputPath, options) => {
91427
- const response = await fetch(
91428
- `/api/projects/${encodeURIComponent(projectId)}/media/remove-background`,
91429
- {
91430
- method: "POST",
91431
- headers: { "Content-Type": "application/json" },
91432
- body: JSON.stringify({
91433
- inputPath,
91434
- createBackgroundPlate: options.createBackgroundPlate === true,
91435
- quality: options.quality ?? "balanced"
91436
- })
91437
- }
91473
+ // src/hooks/useApplyColorGradingScope.ts
91474
+ function useApplyColorGradingScope(recordEdit, reloadPreview) {
91475
+ const { projectId, activeCompPath, showToast, waitForPendingDomEditSaves } = useStudioShellContext();
91476
+ const { domEditSelection } = useDomEditContext();
91477
+ const { readProjectFile, writeProjectFile, compositions } = useFileManagerContext();
91478
+ return useCallback177(
91479
+ async (scope, value) => applyColorGradingScopeUpdate({
91480
+ scope,
91481
+ value,
91482
+ selectedSourceFile: domEditSelection?.sourceFile || activeCompPath || "index.html",
91483
+ compositionPaths: compositions,
91484
+ projectId,
91485
+ waitForPendingDomEditSaves,
91486
+ readProjectFile,
91487
+ writeProjectFile,
91488
+ recordEdit,
91489
+ reloadPreview,
91490
+ showToast
91491
+ }).catch((error) => {
91492
+ showToast(
91493
+ `Couldn't apply color grading: ${error instanceof Error ? error.message : String(error)}`,
91494
+ "error"
91438
91495
  );
91439
- const data = await response.json().catch(() => ({}));
91440
- if (!response.ok || !data.jobId) {
91441
- throw new Error(data.error || `Background removal failed (${response.status})`);
91442
- }
91443
- showToast("Removing background...", "info");
91444
- abortRef.current?.abort();
91445
- const controller = new AbortController();
91446
- abortRef.current = controller;
91447
- try {
91448
- const result = await waitForMediaJob(data.jobId, options.onProgress, controller.signal);
91449
- await refreshFileTree();
91450
- showToast(`Created transparent asset: ${result.outputPath.split("/").pop()}`, "info");
91451
- return result;
91452
- } finally {
91453
- if (abortRef.current === controller) {
91454
- abortRef.current = null;
91455
- }
91456
- }
91457
- },
91458
- [projectId, refreshFileTree, showToast]
91496
+ return EMPTY_COLOR_GRADING_SCOPE_RESULT;
91497
+ }),
91498
+ [
91499
+ activeCompPath,
91500
+ compositions,
91501
+ domEditSelection?.sourceFile,
91502
+ projectId,
91503
+ readProjectFile,
91504
+ recordEdit,
91505
+ reloadPreview,
91506
+ showToast,
91507
+ waitForPendingDomEditSaves,
91508
+ writeProjectFile
91509
+ ]
91459
91510
  );
91460
91511
  }
91461
91512
 
@@ -91477,14 +91528,7 @@ function StudioRightPanels({
91477
91528
  onAutoGroupCarveSources,
91478
91529
  onAddMediaOverlay
91479
91530
  }) {
91480
- const {
91481
- previewIframeRef,
91482
- projectId,
91483
- activeCompPath,
91484
- showToast,
91485
- waitForPendingDomEditSaves,
91486
- renderQueue
91487
- } = useStudioShellContext();
91531
+ const { previewIframeRef, projectId, activeCompPath, showToast, renderQueue } = useStudioShellContext();
91488
91532
  const { captionEditMode, refreshKey } = useStudioPlaybackContext();
91489
91533
  const {
91490
91534
  domEditSelection,
@@ -91540,7 +91584,6 @@ function StudioRightPanels({
91540
91584
  refreshFileTree,
91541
91585
  readProjectFile,
91542
91586
  writeProjectFile,
91543
- fileTree,
91544
91587
  editingFile
91545
91588
  } = useFileManagerContext();
91546
91589
  const onPersistSlideshow = useSlideshowPersist({
@@ -91576,41 +91619,9 @@ function StudioRightPanels({
91576
91619
  onDismiss: onDismissBlockParams
91577
91620
  });
91578
91621
  useCaptionDesignFocus(captionEditMode);
91579
- const handleApplyColorGradingScope = useCallback177(
91580
- async (scope, value) => applyColorGradingScopeUpdate({
91581
- scope,
91582
- value,
91583
- selectedSourceFile: domEditSelection?.sourceFile || activeCompPath || "index.html",
91584
- fileTree,
91585
- projectId,
91586
- waitForPendingDomEditSaves,
91587
- readProjectFile,
91588
- writeProjectFile,
91589
- recordEdit,
91590
- reloadPreview,
91591
- showToast
91592
- }).catch((error) => {
91593
- showToast(
91594
- `Couldn't apply color grading: ${error instanceof Error ? error.message : String(error)}`,
91595
- "error"
91596
- );
91597
- return EMPTY_COLOR_GRADING_SCOPE_RESULT;
91598
- }),
91599
- [
91600
- activeCompPath,
91601
- domEditSelection?.sourceFile,
91602
- fileTree,
91603
- projectId,
91604
- readProjectFile,
91605
- recordEdit,
91606
- reloadPreview,
91607
- showToast,
91608
- waitForPendingDomEditSaves,
91609
- writeProjectFile
91610
- ]
91611
- );
91622
+ const handleApplyColorGradingScope = useApplyColorGradingScope(recordEdit, reloadPreview);
91612
91623
  const handleRemoveBackground = useRemoveBackground(projectId, refreshFileTree, showToast);
91613
- const setAttributeWhileDragging = useCallback177(
91624
+ const setAttributeWhileDragging = useCallback178(
91614
91625
  (attr, value) => handleDomAttributeLiveCommit(attr, value, void 0, { previewOnly: true }),
91615
91626
  [handleDomAttributeLiveCommit]
91616
91627
  );
@@ -91755,7 +91766,7 @@ import { useEffect as useEffect148, useRef as useRef183 } from "react";
91755
91766
  import { Image as Image3, Magnet, MagnifyingGlassMinus, MagnifyingGlassPlus } from "@phosphor-icons/react";
91756
91767
 
91757
91768
  // src/hooks/useEnableKeyframes.ts
91758
- import { useCallback as useCallback178 } from "react";
91769
+ import { useCallback as useCallback179 } from "react";
91759
91770
  var enableKeyframesTransactionCounter = 0;
91760
91771
  function animatedProps(anim) {
91761
91772
  if (!anim) return ["x", "y"];
@@ -92008,7 +92019,7 @@ async function applyArcKeyframeAtPlayhead(session, sel, arcAnim, t, iframe) {
92008
92019
  );
92009
92020
  }
92010
92021
  function useEnableKeyframes(sessionRef) {
92011
- return useCallback178(async () => {
92022
+ return useCallback179(async () => {
92012
92023
  const session = sessionRef.current;
92013
92024
  if (!session) return;
92014
92025
  const sel = session.domEditSelection;
@@ -92090,7 +92101,7 @@ function useEnableKeyframes(sessionRef) {
92090
92101
  }
92091
92102
 
92092
92103
  // src/hooks/useKeyframeKeyboard.ts
92093
- import { useEffect as useEffect147, useCallback as useCallback179 } from "react";
92104
+ import { useEffect as useEffect147, useCallback as useCallback180 } from "react";
92094
92105
  function isTextInput2(el) {
92095
92106
  if (!el) return false;
92096
92107
  const tag = el.tagName;
@@ -92107,7 +92118,7 @@ function useKeyframeKeyboard({
92107
92118
  onToggleExpand,
92108
92119
  onNudgeKeyframe
92109
92120
  }) {
92110
- const handler = useCallback179(
92121
+ const handler = useCallback180(
92111
92122
  (e) => {
92112
92123
  if (!enabled) return;
92113
92124
  if (isTextInput2(document.activeElement)) return;
@@ -92775,12 +92786,12 @@ function useStudioSessionStart(projectId, resolving, waitingForServer) {
92775
92786
  }
92776
92787
 
92777
92788
  // src/hooks/useTimelineAddAtPlayhead.ts
92778
- import { useCallback as useCallback180 } from "react";
92789
+ import { useCallback as useCallback181 } from "react";
92779
92790
  function useTimelineAddAtPlayhead(addAsset, addComposition) {
92780
92791
  const placement = () => ({ start: usePlayerStore.getState().currentTime, track: 0 });
92781
92792
  return {
92782
- addAssetAtPlayhead: useCallback180((path) => addAsset(path, placement()), [addAsset]),
92783
- addCompositionAtPlayhead: useCallback180(
92793
+ addAssetAtPlayhead: useCallback181((path) => addAsset(path, placement()), [addAsset]),
92794
+ addCompositionAtPlayhead: useCallback181(
92784
92795
  (path) => addComposition(path, placement()),
92785
92796
  [addComposition]
92786
92797
  )
@@ -92836,14 +92847,14 @@ function useAutoOpenRootComposition({
92836
92847
  }
92837
92848
 
92838
92849
  // src/hooks/useCompositionContentLoader.ts
92839
- import { useCallback as useCallback181 } from "react";
92850
+ import { useCallback as useCallback182 } from "react";
92840
92851
  function useCompositionContentLoader({
92841
92852
  projectId,
92842
92853
  setEditingFile,
92843
92854
  setActiveCompPath,
92844
92855
  showToast
92845
92856
  }) {
92846
- return useCallback181(
92857
+ return useCallback182(
92847
92858
  (comp) => {
92848
92859
  if (!projectId) return;
92849
92860
  setActiveCompPath(comp.endsWith(".html") ? comp : null);
@@ -92961,7 +92972,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
92961
92972
  const handleDomZIndexReorderCommitRef = useRef186(null);
92962
92973
  const pendingTimelineEditPathRef = useRef186(/* @__PURE__ */ new Set());
92963
92974
  const isGestureRecordingRef = useRef186(false);
92964
- const reloadPreview = useCallback182(() => setRefreshKey((k) => k + 1), []);
92975
+ const reloadPreview = useCallback183(() => setRefreshKey((k) => k + 1), []);
92965
92976
  const fileManager = useFileManager({
92966
92977
  projectId,
92967
92978
  showToast,
@@ -93012,7 +93023,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
93012
93023
  });
93013
93024
  const invalidateGsapCacheRef = useRef186(() => {
93014
93025
  });
93015
- const invalidateGsapCache = useCallback182(() => invalidateGsapCacheRef.current(), []);
93026
+ const invalidateGsapCache = useCallback183(() => invalidateGsapCacheRef.current(), []);
93016
93027
  const timelineEditing = useTimelineEditing({
93017
93028
  projectId,
93018
93029
  activeCompPath,
@@ -93032,7 +93043,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
93032
93043
  invalidateGsapCache,
93033
93044
  handleDomZIndexReorderCommitRef
93034
93045
  });
93035
- const handleTimelineElementsMove = useCallback182(
93046
+ const handleTimelineElementsMove = useCallback183(
93036
93047
  async (edits, coalesceKey, operation = "timing", coalesceMs) => {
93037
93048
  const deps = { handleTimelineGroupMove: timelineEditing.handleTimelineGroupMove };
93038
93049
  await persistTimelineMoveEditsAtomically(edits, coalesceKey, operation, deps, coalesceMs);
@@ -93070,7 +93081,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
93070
93081
  setRightCollapsed: panelLayout.setRightCollapsed,
93071
93082
  setRightPanelTab: panelLayout.setRightPanelTab
93072
93083
  });
93073
- const dismissBlockParams = useCallback182(() => setActiveBlockParams(null), [setActiveBlockParams]);
93084
+ const dismissBlockParams = useCallback183(() => setActiveBlockParams(null), [setActiveBlockParams]);
93074
93085
  const setRightPanelTab = useDismissingTabSetter(panelLayout.setRightPanelTab, dismissBlockParams);
93075
93086
  const layout = useMemo77(
93076
93087
  () => ({ ...panelLayout, setRightPanelTab }),
@@ -93197,7 +93208,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
93197
93208
  effectiveTimelineDuration
93198
93209
  });
93199
93210
  const compositionDimensions = useCompositionDimensions(previewIframeRef);
93200
- const { lintModal, linting, handleLint, closeLintModal, findingsByFile, hasLintError } = useLintModal(projectId, refreshKey);
93211
+ const lint = useLintModal(projectId, refreshKey);
93201
93212
  const frameCapture = useFrameCapture({
93202
93213
  projectId,
93203
93214
  activeCompPath,
@@ -93230,7 +93241,7 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
93230
93241
  }
93231
93242
  canvasRectRef.current = previewIframe.getBoundingClientRect();
93232
93243
  }, [gestureState, previewIframe]);
93233
- const handlePreviewIframeRef = useCallback182(
93244
+ const handlePreviewIframeRef = useCallback183(
93234
93245
  (iframe) => {
93235
93246
  previewIframeRef.current = iframe;
93236
93247
  usePreviewIframeStore.getState().setIframe(iframe);
@@ -93349,11 +93360,11 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
93349
93360
  onSelectComposition: handleSelectComposition,
93350
93361
  onAddBlock: handleAddBlock,
93351
93362
  onPreviewBlock: setBlockPreview,
93352
- onLint: handleLint,
93353
- linting,
93354
- lintFindingCount: lintModal?.length ?? findingsByFile.size,
93355
- lintFindingsByFile: findingsByFile,
93356
- lintHasError: hasLintError,
93363
+ onLint: lint.handleLint,
93364
+ linting: lint.linting,
93365
+ lintFindingCount: lint.lintFindingCount,
93366
+ lintFindingsByFile: lint.findingsByFile,
93367
+ lintHasError: lint.hasLintError,
93357
93368
  onAddAssetToTimeline: handleAddAssetAtPlayhead,
93358
93369
  onAddCompositionToTimeline: handleAddCompositionAtPlayhead
93359
93370
  }
@@ -93431,8 +93442,8 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
93431
93442
  {
93432
93443
  projectId,
93433
93444
  projectDir: fileManager.projectDir,
93434
- lintModal,
93435
- closeLintModal,
93445
+ lintModal: lint.lintModal,
93446
+ closeLintModal: lint.closeLintModal,
93436
93447
  consoleErrors,
93437
93448
  clearConsoleErrors: () => setConsoleErrors(null),
93438
93449
  domEditSession,
@@ -93447,32 +93458,32 @@ function StudioApp({ readOnlyPreview = false, readOnlyPreviewReason } = {}) {
93447
93458
  }
93448
93459
 
93449
93460
  // src/hooks/useElementPicker.ts
93450
- import { useState as useState156, useCallback as useCallback183, useRef as useRef187 } from "react";
93461
+ import { useState as useState156, useCallback as useCallback184, useRef as useRef187 } from "react";
93451
93462
  function useElementPicker(iframeRef, options) {
93452
93463
  const [isPickMode, setIsPickMode] = useState156(false);
93453
93464
  const [pickedElement, setPickedElement] = useState156(null);
93454
93465
  const activeOverrideRef = useRef187(null);
93455
- const getActiveIframe = useCallback183(() => {
93466
+ const getActiveIframe = useCallback184(() => {
93456
93467
  return activeOverrideRef.current ?? iframeRef.current;
93457
93468
  }, [iframeRef]);
93458
- const setActiveIframe = useCallback183((el) => {
93469
+ const setActiveIframe = useCallback184((el) => {
93459
93470
  activeOverrideRef.current = el;
93460
93471
  }, []);
93461
- const enablePick = useCallback183(() => {
93472
+ const enablePick = useCallback184(() => {
93462
93473
  try {
93463
93474
  postRuntimeControlMessage(getActiveIframe()?.contentWindow, "enable-pick-mode");
93464
93475
  setIsPickMode(true);
93465
93476
  } catch {
93466
93477
  }
93467
93478
  }, [getActiveIframe]);
93468
- const disablePick = useCallback183(() => {
93479
+ const disablePick = useCallback184(() => {
93469
93480
  try {
93470
93481
  postRuntimeControlMessage(getActiveIframe()?.contentWindow, "disable-pick-mode");
93471
93482
  } catch {
93472
93483
  }
93473
93484
  setIsPickMode(false);
93474
93485
  }, [getActiveIframe]);
93475
- const clearPick = useCallback183(() => {
93486
+ const clearPick = useCallback184(() => {
93476
93487
  setPickedElement(null);
93477
93488
  }, []);
93478
93489
  useMountEffect(() => {
@@ -93502,7 +93513,7 @@ function useElementPicker(iframeRef, options) {
93502
93513
  optionsRef.current = options;
93503
93514
  const pendingWritesRef = useRef187(/* @__PURE__ */ new Map());
93504
93515
  settlePendingWrites(pendingWritesRef.current, options?.workspaceFiles);
93505
- const syncToSource = useCallback183(
93516
+ const syncToSource = useCallback184(
93506
93517
  (picked, live, iframe, op) => {
93507
93518
  const opts = optionsRef.current;
93508
93519
  if (!opts?.workspaceFiles || !opts.onSyncFiles) return;
@@ -93516,7 +93527,7 @@ function useElementPicker(iframeRef, options) {
93516
93527
  },
93517
93528
  []
93518
93529
  );
93519
- const setStyle = useCallback183(
93530
+ const setStyle = useCallback184(
93520
93531
  (prop, value) => {
93521
93532
  const activeIframe = getActiveIframe();
93522
93533
  if (!pickedElement?.selector || !activeIframe) return;
@@ -93542,7 +93553,7 @@ function useElementPicker(iframeRef, options) {
93542
93553
  },
93543
93554
  [pickedElement, getActiveIframe, syncToSource]
93544
93555
  );
93545
- const setDataAttr = useCallback183(
93556
+ const setDataAttr = useCallback184(
93546
93557
  (attr, value) => {
93547
93558
  const activeIframe = getActiveIframe();
93548
93559
  if (!pickedElement?.selector || !activeIframe) return;
@@ -93568,7 +93579,7 @@ function useElementPicker(iframeRef, options) {
93568
93579
  },
93569
93580
  [pickedElement, getActiveIframe, syncToSource]
93570
93581
  );
93571
- const setTextContent = useCallback183(
93582
+ const setTextContent = useCallback184(
93572
93583
  (text) => {
93573
93584
  const activeIframe = getActiveIframe();
93574
93585
  if (!pickedElement?.selector || !activeIframe) return;