@hyperframes/studio 0.6.90 → 0.6.92
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/assets/{index-DSLrl2tB.js → index-CDy8BuGq.js} +24 -24
- package/dist/assets/index-CmRIkCwI.js +251 -0
- package/dist/assets/index-rm9tn9nH.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +4 -4
- package/src/App.tsx +2 -0
- package/src/components/StudioPreviewArea.tsx +54 -13
- package/src/components/TimelineToolbar.tsx +52 -35
- package/src/components/editor/DomEditOverlay.tsx +79 -0
- package/src/components/editor/PropertyPanel.tsx +19 -10
- package/src/components/editor/gsapAnimatesProperty.ts +30 -0
- package/src/components/editor/manualEditingAvailability.test.ts +12 -0
- package/src/components/editor/manualEditingAvailability.ts +16 -0
- package/src/components/editor/manualEditsDom.ts +25 -5
- package/src/components/editor/manualEditsDomPatches.test.ts +1 -0
- package/src/components/editor/manualEditsDomPatches.ts +17 -1
- package/src/components/editor/manualEditsSnapshot.ts +16 -0
- package/src/components/editor/propertyPanel3dTransform.tsx +19 -4
- package/src/components/editor/useOffScreenIndicators.ts +197 -0
- package/src/components/nle/NLELayout.tsx +22 -32
- package/src/components/nle/TimelineEditorNotice.tsx +2 -25
- package/src/contexts/DomEditContext.tsx +4 -0
- package/src/hooks/gsapDragCommit.ts +119 -43
- package/src/hooks/gsapKeyframeCacheHelpers.ts +9 -4
- package/src/hooks/gsapRuntimeBridge.ts +266 -41
- package/src/hooks/gsapRuntimeReaders.ts +16 -2
- package/src/hooks/useAnimatedPropertyCommit.ts +11 -5
- package/src/hooks/useAppHotkeys.ts +48 -1
- package/src/hooks/useContextMenuDismiss.ts +29 -0
- package/src/hooks/useDomEditCommits.ts +7 -1
- package/src/hooks/useDomEditSession.ts +20 -4
- package/src/hooks/useEnableKeyframes.ts +3 -1
- package/src/hooks/useGestureCommit.ts +99 -13
- package/src/hooks/useGestureRecording.ts +18 -2
- package/src/hooks/useGsapScriptCommits.ts +24 -3
- package/src/hooks/useGsapSelectionHandlers.ts +19 -3
- package/src/hooks/useGsapTweenCache.ts +30 -10
- package/src/hooks/useRazorSplit.ts +298 -0
- package/src/hooks/useTimelineEditing.ts +15 -98
- package/src/player/components/ClipContextMenu.tsx +14 -25
- package/src/player/components/KeyframeDiamondContextMenu.tsx +16 -112
- package/src/player/components/PlayheadIndicator.tsx +43 -0
- package/src/player/components/Timeline.tsx +45 -38
- package/src/player/components/TimelineCanvas.tsx +29 -22
- package/src/player/components/TimelineClipDiamonds.tsx +3 -1
- package/src/player/components/timelineCallbacks.ts +44 -0
- package/src/player/components/timelineDragDrop.ts +2 -14
- package/src/player/components/useTimelineZoom.ts +18 -0
- package/src/player/store/playerStore.ts +20 -0
- package/src/utils/globalTimeCompiler.test.ts +2 -2
- package/src/utils/globalTimeCompiler.ts +2 -1
- package/src/utils/gsapSoftReload.test.ts +16 -0
- package/src/utils/gsapSoftReload.ts +43 -8
- package/src/utils/rdpSimplify.ts +3 -2
- package/src/utils/timelineElementSplit.test.ts +50 -0
- package/src/utils/timelineElementSplit.ts +32 -0
- package/dist/assets/index-BKuDHMYl.js +0 -146
- package/dist/assets/index-D2NkPomd.css +0 -1
package/src/utils/rdpSimplify.ts
CHANGED
|
@@ -97,7 +97,7 @@ function simplifyTimeSeries(
|
|
|
97
97
|
export function simplifyGestureSamples(
|
|
98
98
|
samples: Array<{ time: number; properties: Record<string, number> }>,
|
|
99
99
|
totalDuration: number,
|
|
100
|
-
epsilon: number,
|
|
100
|
+
epsilon: number | ((key: string) => number),
|
|
101
101
|
): Map<number, Record<string, number>> {
|
|
102
102
|
if (samples.length === 0) return new Map();
|
|
103
103
|
if (totalDuration <= 0) return new Map();
|
|
@@ -120,7 +120,8 @@ export function simplifyGestureSamples(
|
|
|
120
120
|
series.push({ time: s.time, value: s.properties[key] });
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
-
const
|
|
123
|
+
const keyEpsilon = typeof epsilon === "function" ? epsilon(key) : epsilon;
|
|
124
|
+
const simplified = simplifyTimeSeries(series, keyEpsilon);
|
|
124
125
|
for (const pt of simplified) {
|
|
125
126
|
survivingTimes.add(pt.time);
|
|
126
127
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { SPLIT_BOUNDARY_EPSILON_S, isSplitTimeWithinBounds } from "./timelineElementSplit";
|
|
3
|
+
|
|
4
|
+
describe("isSplitTimeWithinBounds", () => {
|
|
5
|
+
const start = 1;
|
|
6
|
+
const duration = 4;
|
|
7
|
+
const end = start + duration;
|
|
8
|
+
|
|
9
|
+
it("accepts the exact lower clamp boundary", () => {
|
|
10
|
+
// The timeline canvas clamps an edge click to exactly
|
|
11
|
+
// start + SPLIT_BOUNDARY_EPSILON_S, so that value must be splittable.
|
|
12
|
+
expect(isSplitTimeWithinBounds(start + SPLIT_BOUNDARY_EPSILON_S, start, duration)).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("accepts the exact upper clamp boundary", () => {
|
|
16
|
+
expect(
|
|
17
|
+
isSplitTimeWithinBounds(start + duration - SPLIT_BOUNDARY_EPSILON_S, start, duration),
|
|
18
|
+
).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("accepts an interior split time", () => {
|
|
22
|
+
expect(isSplitTimeWithinBounds(3, start, duration)).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("rejects times at or outside the clip edges", () => {
|
|
26
|
+
expect(isSplitTimeWithinBounds(start, start, duration)).toBe(false);
|
|
27
|
+
expect(isSplitTimeWithinBounds(end, start, duration)).toBe(false);
|
|
28
|
+
expect(isSplitTimeWithinBounds(start - 1, start, duration)).toBe(false);
|
|
29
|
+
expect(isSplitTimeWithinBounds(end + 1, start, duration)).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("rejects times inside the epsilon margins", () => {
|
|
33
|
+
expect(isSplitTimeWithinBounds(start + SPLIT_BOUNDARY_EPSILON_S / 2, start, duration)).toBe(
|
|
34
|
+
false,
|
|
35
|
+
);
|
|
36
|
+
expect(isSplitTimeWithinBounds(end - SPLIT_BOUNDARY_EPSILON_S / 2, start, duration)).toBe(
|
|
37
|
+
false,
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("rejects every time on a clip shorter than two epsilons", () => {
|
|
42
|
+
// Math.max(min, Math.min(max, t)) collapses to min when the clip is too
|
|
43
|
+
// short for the clamp range; that collapsed value must still be rejected.
|
|
44
|
+
const shortDuration = SPLIT_BOUNDARY_EPSILON_S;
|
|
45
|
+
expect(isSplitTimeWithinBounds(start + SPLIT_BOUNDARY_EPSILON_S, start, shortDuration)).toBe(
|
|
46
|
+
false,
|
|
47
|
+
);
|
|
48
|
+
expect(isSplitTimeWithinBounds(start + shortDuration / 2, start, shortDuration)).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { TimelineElement } from "../player/store/playerStore";
|
|
2
|
+
|
|
3
|
+
export { buildPatchTarget, readFileContent } from "../hooks/timelineEditingHelpers";
|
|
4
|
+
|
|
5
|
+
/** Minimum distance (seconds) from clip boundaries to allow a split. */
|
|
6
|
+
export const SPLIT_BOUNDARY_EPSILON_S = 0.03;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* True when splitTime leaves at least SPLIT_BOUNDARY_EPSILON_S on both sides
|
|
10
|
+
* of the cut. Inclusive at the epsilon offsets: the timeline canvas clamps
|
|
11
|
+
* edge clicks to exactly start/end ± epsilon, so the clamped value must pass.
|
|
12
|
+
*/
|
|
13
|
+
export function isSplitTimeWithinBounds(
|
|
14
|
+
splitTime: number,
|
|
15
|
+
clipStart: number,
|
|
16
|
+
clipDuration: number,
|
|
17
|
+
): boolean {
|
|
18
|
+
return (
|
|
19
|
+
splitTime >= clipStart + SPLIT_BOUNDARY_EPSILON_S &&
|
|
20
|
+
splitTime <= clipStart + clipDuration - SPLIT_BOUNDARY_EPSILON_S
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function canSplitElement(el: TimelineElement): boolean {
|
|
25
|
+
return (
|
|
26
|
+
!el.timelineLocked &&
|
|
27
|
+
el.timingSource !== "implicit" &&
|
|
28
|
+
!el.compositionSrc &&
|
|
29
|
+
!!el.duration &&
|
|
30
|
+
Number.isFinite(el.duration)
|
|
31
|
+
);
|
|
32
|
+
}
|