@hyperframes/studio 0.7.5 → 0.7.7

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 (77) hide show
  1. package/dist/assets/{index-BwFzbjZQ.js → index-B2YXvFxf.js} +1 -1
  2. package/dist/assets/index-BSkUuN8g.css +1 -0
  3. package/dist/assets/index-BeRh2hMe.js +375 -0
  4. package/dist/assets/{index-C5NAfiPa.js → index-BoASKOeE.js} +1 -1
  5. package/dist/chunk-KZXYQYIU.js +876 -0
  6. package/dist/chunk-KZXYQYIU.js.map +1 -0
  7. package/dist/domEditingLayers-SSXQZHHQ.js +41 -0
  8. package/dist/domEditingLayers-SSXQZHHQ.js.map +1 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.html +2 -2
  11. package/dist/index.js +3326 -2939
  12. package/dist/index.js.map +1 -1
  13. package/package.json +5 -5
  14. package/src/captions/hooks/useCaptionSync.ts +6 -1
  15. package/src/components/StudioPreviewArea.tsx +10 -3
  16. package/src/components/StudioRightPanel.tsx +4 -0
  17. package/src/components/editor/AnimationCard.tsx +77 -250
  18. package/src/components/editor/AnimationCardParts.tsx +220 -0
  19. package/src/components/editor/BlockParamsPanel.tsx +4 -8
  20. package/src/components/editor/DomEditOverlay.tsx +132 -48
  21. package/src/components/editor/EaseCurveSection.tsx +177 -101
  22. package/src/components/editor/GsapAnimationSection.tsx +4 -0
  23. package/src/components/editor/KeyframeEaseList.tsx +127 -0
  24. package/src/components/editor/OffCanvasIndicators.tsx +111 -0
  25. package/src/components/editor/PropertyPanel.tsx +63 -56
  26. package/src/components/editor/gsapAnimationCallbacks.ts +3 -0
  27. package/src/components/editor/gsapAnimationConstants.ts +11 -32
  28. package/src/components/editor/gsapAnimationHelpers.test.ts +1 -1
  29. package/src/components/editor/manualOffsetDrag.ts +8 -0
  30. package/src/components/editor/marqueeCommit.ts +168 -0
  31. package/src/components/editor/propertyPanelHelpers.ts +7 -0
  32. package/src/components/editor/propertyPanelTimingSection.tsx +35 -2
  33. package/src/components/editor/snapTargetCollection.ts +0 -5
  34. package/src/components/editor/useMotionPathData.ts +2 -1
  35. package/src/components/panels/SlideshowPanel.tsx +0 -1
  36. package/src/components/sidebar/AssetContextMenu.tsx +97 -0
  37. package/src/components/sidebar/AssetsTab.tsx +542 -254
  38. package/src/components/sidebar/assetHelpers.ts +40 -0
  39. package/src/contexts/DomEditContext.tsx +12 -0
  40. package/src/hooks/gsapDragCommit.ts +101 -0
  41. package/src/hooks/gsapDragPositionCommit.ts +1 -2
  42. package/src/hooks/gsapRuntimeBridge.ts +28 -2
  43. package/src/hooks/gsapRuntimeKeyframes.test.ts +47 -0
  44. package/src/hooks/gsapRuntimeKeyframes.ts +13 -0
  45. package/src/hooks/gsapRuntimeReaders.ts +1 -6
  46. package/src/hooks/useDomEditCommits.ts +0 -4
  47. package/src/hooks/useDomEditPreviewSync.ts +5 -0
  48. package/src/hooks/useDomEditSession.ts +43 -0
  49. package/src/hooks/useDomEditTextCommits.ts +3 -12
  50. package/src/hooks/useDomEditWiring.ts +3 -0
  51. package/src/hooks/useDomSelection.ts +46 -0
  52. package/src/hooks/useGestureCommit.ts +26 -7
  53. package/src/hooks/useGestureRecording.ts +2 -16
  54. package/src/hooks/useGsapAnimationOps.ts +2 -2
  55. package/src/hooks/useGsapAwareEditing.ts +0 -4
  56. package/src/hooks/useGsapScriptCommits.ts +0 -12
  57. package/src/hooks/useGsapTweenCache.test.ts +45 -1
  58. package/src/hooks/useGsapTweenCache.ts +151 -42
  59. package/src/hooks/useMusicBeatAnalysis.ts +36 -21
  60. package/src/hooks/useRazorSplit.ts +0 -3
  61. package/src/player/components/Player.tsx +0 -5
  62. package/src/player/components/timelineIcons.tsx +2 -1
  63. package/src/player/hooks/useTimelinePlayer.ts +4 -14
  64. package/src/player/hooks/useTimelineSyncCallbacks.ts +2 -9
  65. package/src/player/lib/timelineIframeHelpers.ts +2 -6
  66. package/src/telemetry/client.ts +2 -0
  67. package/src/utils/gestureSmoother.test.ts +48 -0
  68. package/src/utils/gestureSmoother.ts +46 -0
  69. package/src/utils/marqueeGeometry.test.ts +123 -0
  70. package/src/utils/marqueeGeometry.ts +172 -0
  71. package/src/utils/optimisticUpdate.ts +1 -2
  72. package/src/utils/sourcePatcher.ts +0 -10
  73. package/src/utils/velocityEaseFitter.test.ts +58 -0
  74. package/src/utils/velocityEaseFitter.ts +121 -0
  75. package/dist/assets/index-D_JGXmfx.js +0 -374
  76. package/dist/assets/index-DzWIinxk.css +0 -1
  77. package/src/utils/editDebugLog.ts +0 -16
@@ -0,0 +1,121 @@
1
+ interface TimedSample {
2
+ time: number;
3
+ value: number;
4
+ }
5
+
6
+ // After Effects convention (ease named by the keyframe side it acts on):
7
+ // Easy Ease — slow at both ends (cubic-bezier 0.333,0 0.667,1)
8
+ // Easy Ease In — eases *into* the keyframe → decelerates → slow at the END
9
+ // Easy Ease Out — eases *out of* the keyframe → accelerates → slow at the START
10
+ // The control-point y values must match that polarity (a flat tangent at the
11
+ // slow side): slow-end pins CP2 at y=1, slow-start pins CP1 at y=0.
12
+ const AE_EASE = "custom(M0,0 C0.333,0 0.667,1 1,1)";
13
+ const AE_EASE_IN = "custom(M0,0 C0.333,0.333 0.667,1 1,1)";
14
+ const AE_EASE_OUT = "custom(M0,0 C0.333,0 0.667,0.667 1,1)";
15
+ const VELOCITY_THRESHOLD = 0.3;
16
+
17
+ function averageSpeed(samples: TimedSample[], from: number, to: number): number {
18
+ const seg = samples.filter((s) => s.time >= from && s.time <= to);
19
+ if (seg.length < 2) return 0;
20
+ let total = 0;
21
+ for (let i = 1; i < seg.length; i++) {
22
+ const dt = seg[i].time - seg[i - 1].time;
23
+ if (dt > 0) total += Math.abs(seg[i].value - seg[i - 1].value) / dt;
24
+ }
25
+ return total / (seg.length - 1);
26
+ }
27
+
28
+ function speedAtEdge(
29
+ samples: TimedSample[],
30
+ t: number,
31
+ window: number,
32
+ side: "start" | "end",
33
+ ): number {
34
+ const near = samples.filter((s) =>
35
+ side === "start" ? s.time >= t && s.time <= t + window : s.time >= t - window && s.time <= t,
36
+ );
37
+ if (near.length < 2) return 0;
38
+ let total = 0;
39
+ for (let i = 1; i < near.length; i++) {
40
+ const dt = near[i].time - near[i - 1].time;
41
+ if (dt > 0) total += Math.abs(near[i].value - near[i - 1].value) / dt;
42
+ }
43
+ return total / (near.length - 1);
44
+ }
45
+
46
+ export interface FittedKeyframe {
47
+ percentage: number;
48
+ properties: Record<string, number | string>;
49
+ ease?: string;
50
+ }
51
+
52
+ /**
53
+ * Analyze velocity profile of raw samples between keyframes and assign
54
+ * per-keyframe eases based on deceleration/acceleration patterns.
55
+ *
56
+ * For each segment between consecutive keyframes:
57
+ * - Constant speed → linear ("none")
58
+ * - Decelerates at end → Easy Ease In
59
+ * - Accelerates from start → Easy Ease Out
60
+ * - Both → Easy Ease (full)
61
+ */
62
+ // fallow-ignore-next-line complexity
63
+ export function fitEasesFromVelocity(
64
+ keyframes: FittedKeyframe[],
65
+ rawSamples: { time: number; properties: Record<string, number> }[],
66
+ totalDuration: number,
67
+ ): FittedKeyframe[] {
68
+ if (keyframes.length < 2 || rawSamples.length < 3) return keyframes;
69
+
70
+ const result = [...keyframes.map((kf) => ({ ...kf }))];
71
+
72
+ for (let i = 1; i < result.length; i++) {
73
+ const prevPct = result[i - 1].percentage;
74
+ const currPct = result[i].percentage;
75
+ const segStart = (prevPct / 100) * totalDuration;
76
+ const segEnd = (currPct / 100) * totalDuration;
77
+ const segDur = segEnd - segStart;
78
+ if (segDur <= 0) continue;
79
+
80
+ // Use the dominant property (largest range) for velocity analysis
81
+ const props = Object.keys(result[i].properties);
82
+ let bestProp = props[0] ?? "x";
83
+ let bestRange = 0;
84
+ for (const p of props) {
85
+ const startVal = Number(result[i - 1].properties[p] ?? 0);
86
+ const endVal = Number(result[i].properties[p] ?? 0);
87
+ const range = Math.abs(endVal - startVal);
88
+ if (range > bestRange) {
89
+ bestRange = range;
90
+ bestProp = p;
91
+ }
92
+ }
93
+
94
+ const propSamples: TimedSample[] = rawSamples
95
+ .filter((s) => s.time >= segStart && s.time <= segEnd)
96
+ .map((s) => ({ time: s.time, value: s.properties[bestProp] ?? 0 }));
97
+
98
+ if (propSamples.length < 3) continue;
99
+
100
+ const edgeWindow = segDur * 0.25;
101
+ const avgSpd = averageSpeed(propSamples, segStart, segEnd);
102
+ if (avgSpd < 1e-6) continue;
103
+
104
+ const startSpd = speedAtEdge(propSamples, segStart, edgeWindow, "start");
105
+ const endSpd = speedAtEdge(propSamples, segEnd, edgeWindow, "end");
106
+
107
+ const slowStart = startSpd / avgSpd < VELOCITY_THRESHOLD;
108
+ const slowEnd = endSpd / avgSpd < VELOCITY_THRESHOLD;
109
+
110
+ if (slowStart && slowEnd) {
111
+ result[i].ease = AE_EASE;
112
+ } else if (slowEnd) {
113
+ result[i].ease = AE_EASE_IN;
114
+ } else if (slowStart) {
115
+ result[i].ease = AE_EASE_OUT;
116
+ }
117
+ // Otherwise leave ease undefined → linear (constant speed)
118
+ }
119
+
120
+ return result;
121
+ }