@hyperframes/studio 0.8.112 → 0.8.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/assets/{hyperframes-player-CBpg41JM.js → hyperframes-player-aXNJKiIg.js} +1 -1
  2. package/dist/assets/index-BYnsJnJV.css +1 -0
  3. package/dist/assets/{index-BFitquwi.js → index-Bn2RJNTJ.js} +1 -1
  4. package/dist/assets/{index-CZfrSyU9.js → index-C9N-DGjC.js} +1 -1
  5. package/dist/assets/{index-BKy_birQ.js → index-Dz7doz9l.js} +199 -199
  6. package/dist/index.d.ts +3 -3
  7. package/dist/index.html +2 -2
  8. package/dist/index.js +1605 -1536
  9. package/dist/index.js.map +1 -1
  10. package/dist/styles.css +1 -1
  11. package/package.json +9 -8
  12. package/src/App.tsx +82 -84
  13. package/src/components/EditorShell.tsx +3 -3
  14. package/src/components/StudioHeader.tsx +4 -4
  15. package/src/components/StudioLeftPanels.tsx +3 -3
  16. package/src/components/StudioOverlays.tsx +4 -4
  17. package/src/components/StudioRightPanels.tsx +14 -9
  18. package/src/components/StudioToast.tsx +1 -1
  19. package/src/components/TimelineToolbar.tsx +3 -3
  20. package/src/components/editor/InlineTextToolbar.tsx +11 -27
  21. package/src/components/editor/colorBrowserCanvas.test.ts +116 -0
  22. package/src/components/editor/colorValue.test.ts +16 -0
  23. package/src/components/editor/colorValue.ts +100 -24
  24. package/src/components/editor/cropResize.test.ts +11 -0
  25. package/src/components/editor/cropResize.ts +2 -3
  26. package/src/components/editor/gradientValue.ts +28 -36
  27. package/src/components/editor/gsapAnimatesProperty.ts +0 -54
  28. package/src/components/editor/inlineTextStyleRange.test.ts +31 -1
  29. package/src/components/editor/inlineTextStyleRange.ts +5 -2
  30. package/src/components/editor/inlineTextStyleRead.ts +9 -0
  31. package/src/components/editor/manualEditsDom.ts +2 -2
  32. package/src/components/editor/manualEditsSeekReapply.ts +2 -2
  33. package/src/components/editor/propertyPanelColor.tsx +1 -1
  34. package/src/components/editor/reapplyPositionEditsAfterSeek.test.ts +25 -0
  35. package/src/hooks/gsapRuntimeKeyframes.ts +1 -1
  36. package/src/hooks/gsapRuntimeReaders.test.ts +48 -0
  37. package/src/hooks/gsapRuntimeReaders.ts +3 -90
  38. package/src/hooks/useAppHotkeys.ts +11 -5
  39. package/src/hooks/useFrameCapture.ts +15 -8
  40. package/src/hooks/useGestureCommit.ts +4 -1
  41. package/src/hooks/useGestureTrailOverlay.tsx +33 -0
  42. package/src/hooks/usePanelLayout.ts +5 -1
  43. package/src/hooks/useShellSlices.ts +53 -0
  44. package/src/hooks/useStableHandlers.test.tsx +77 -0
  45. package/src/hooks/useStableHandlers.ts +44 -0
  46. package/src/hooks/useStudioSdkSessions.ts +2 -1
  47. package/src/hooks/useStudioSidePanels.test.tsx +44 -0
  48. package/src/hooks/useStudioSidePanels.tsx +23 -0
  49. package/src/hooks/useTimelineAddAtPlayhead.ts +16 -8
  50. package/src/hooks/useTimelineEditing.ts +33 -27
  51. package/src/utils/frameCapture.ts +3 -1
  52. package/src/utils/gsapUndoRestore.browser.test.ts +4 -2
  53. package/dist/assets/index-DbfKVTId.css +0 -1
@@ -1,5 +1,6 @@
1
1
  // fallow-ignore-file complexity
2
2
  import { useCallback, useRef } from "react";
3
+ import { useStableHandlers } from "./useStableHandlers";
3
4
  import { usePlayerStore, type TimelineElement } from "../player";
4
5
  import { toAuthoredStart, toCompositionTime } from "../player/store/timelineElement";
5
6
  import { useRazorSplit } from "./useRazorSplit";
@@ -508,13 +509,41 @@ export function useTimelineEditing({
508
509
  return [...flatMembers, ...domMembers];
509
510
  };
510
511
 
512
+ const audioGroupAttribute = {
513
+ ...setAudioGroupAttribute,
514
+ // Same two-array member lookup syncStoredGroupAttribute mirrors into
515
+ // (timelineAudioGroupVolume.ts): a sub-composition's group members have
516
+ // no flat twin, only a domClipChildren entry, so both are checked.
517
+ setQuiet: track(
518
+ guard(audioGroupMembers, setAudioGroupAttribute.setQuiet, (reason, groupId, attr) => {
519
+ setAudioGroupAttribute.revertLive(groupId, attr);
520
+ return refused(reason);
521
+ }),
522
+ ),
523
+ };
524
+ const stableAudioGroupAttribute = useStableHandlers(audioGroupAttribute, projectId);
525
+ const elementFxAttribute = {
526
+ ...setElementFxAttribute,
527
+ setMany: track(guard((edits) => edits.map((edit) => edit.element), setElementsAttribute)),
528
+ setQuiet: track(
529
+ guard(
530
+ (element) => [element],
531
+ setElementFxAttribute.setQuiet,
532
+ (reason, element, attr) => {
533
+ setElementFxAttribute.revertLive(element, attr);
534
+ return refused(reason);
535
+ },
536
+ ),
537
+ ),
538
+ };
539
+ const stableElementFxAttribute = useStableHandlers(elementFxAttribute, projectId);
511
540
  // Every write-handler is tracked here, the one place all hand edits
512
541
  // converge, so undo never races a write; canEdit gates the same point.
513
542
  // Coverage boundary: see the PR body, not every kind resolves an element.
514
543
  const trackedRazorSplit = track(
515
544
  guard((element) => withLinkPartners([element]), handleRazorSplit),
516
545
  );
517
- return {
546
+ const editing = {
518
547
  handleTimelineElementMove: track(guard((element) => [element], handleTimelineElementMove)),
519
548
  handleTimelineElementResize: track(guard((element) => [element], handleTimelineElementResize)),
520
549
  handleToggleTrackHidden: track(
@@ -530,32 +559,8 @@ export function useTimelineEditing({
530
559
  }, handleToggleElementHidden),
531
560
  ),
532
561
  handleAutoGroupCarveSources: track(handleAutoGroupCarveSources),
533
- setAudioGroupAttribute: {
534
- ...setAudioGroupAttribute,
535
- // Same two-array member lookup syncStoredGroupAttribute mirrors into
536
- // (timelineAudioGroupVolume.ts): a sub-composition's group members have
537
- // no flat twin, only a domClipChildren entry, so both are checked.
538
- setQuiet: track(
539
- guard(audioGroupMembers, setAudioGroupAttribute.setQuiet, (reason, groupId, attr) => {
540
- setAudioGroupAttribute.revertLive(groupId, attr);
541
- return refused(reason);
542
- }),
543
- ),
544
- },
545
- setElementFxAttribute: {
546
- ...setElementFxAttribute,
547
- setMany: track(guard((edits) => edits.map((edit) => edit.element), setElementsAttribute)),
548
- setQuiet: track(
549
- guard(
550
- (element) => [element],
551
- setElementFxAttribute.setQuiet,
552
- (reason, element, attr) => {
553
- setElementFxAttribute.revertLive(element, attr);
554
- return refused(reason);
555
- },
556
- ),
557
- ),
558
- },
562
+ setAudioGroupAttribute: stableAudioGroupAttribute,
563
+ setElementFxAttribute: stableElementFxAttribute,
559
564
  handleTimelineElementDelete: track(
560
565
  guard((element) => withLinkPartners([element]), linkEditing.handleLinkedElementDelete),
561
566
  ),
@@ -591,4 +596,5 @@ export function useTimelineEditing({
591
596
  setAudioGroupAttribute.restoreLive(restore);
592
597
  },
593
598
  };
599
+ return useStableHandlers(editing, projectId);
594
600
  }
@@ -5,6 +5,7 @@ export interface FrameCaptureRequest {
5
5
  compositionPath: string | null;
6
6
  currentTime: number;
7
7
  origin?: string;
8
+ version?: number;
8
9
  }
9
10
 
10
11
  function normalizeCompositionPath(compositionPath: string | null): string {
@@ -16,6 +17,7 @@ export function buildFrameCaptureUrl({
16
17
  compositionPath,
17
18
  currentTime,
18
19
  origin = window.location.origin,
20
+ version = Date.now(),
19
21
  }: FrameCaptureRequest): string {
20
22
  const compPath = normalizeCompositionPath(compositionPath);
21
23
  const url = new URL(
@@ -25,7 +27,7 @@ export function buildFrameCaptureUrl({
25
27
  url.searchParams.set("t", Math.max(0, currentTime).toFixed(3));
26
28
  url.searchParams.set("format", "png");
27
29
  url.searchParams.set("output", "source");
28
- url.searchParams.set("v", String(Date.now()));
30
+ url.searchParams.set("v", String(version));
29
31
  return url.toString();
30
32
  }
31
33
 
@@ -6,12 +6,14 @@ import { join } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import puppeteer, { type Browser } from "puppeteer-core";
8
8
  import { build } from "vite";
9
- import { afterAll, beforeAll, expect, it } from "vitest";
9
+ import { afterAll, beforeAll, expect, it, vi } from "vitest";
10
10
  import { findSystemChrome } from "../../vite.browser";
11
11
  import { writeFixture } from "../../tests/e2e/edit-accuracy/grid.mjs";
12
12
 
13
13
  const require = createRequire(import.meta.url);
14
14
  const CROP = "inset(0px 40px 0px 0px)";
15
+ // Real Chrome on a loaded Windows runner exceeds Vitest's 5s test and 10s hook defaults.
16
+ vi.setConfig({ testTimeout: 60_000, hookTimeout: 60_000 });
15
17
  let browser: Browser;
16
18
  let undoBundle: string;
17
19
 
@@ -37,7 +39,7 @@ beforeAll(async () => {
37
39
  });
38
40
  undoBundle = (Array.isArray(out) ? out[0]! : (out as { output: [{ code: string }] })).output[0]
39
41
  .code;
40
- }, 60_000);
42
+ });
41
43
 
42
44
  afterAll(() => browser?.close());
43
45