@hyperframes/studio 0.7.76 → 0.7.77
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/{hyperframes-player-DsRdxz7A.js → hyperframes-player-CEggaaxR.js} +1 -1
- package/dist/assets/{index-BLICKger.js → index-B1INQNmj.js} +1 -1
- package/dist/assets/{index-Du1RoLiB.js → index-B1Tjjdse.js} +1 -1
- package/dist/assets/index-Bp4jAYZG.js +428 -0
- package/dist/assets/index-CBAfprvx.css +1 -0
- package/dist/index.d.ts +64 -17
- package/dist/index.html +2 -2
- package/dist/index.js +18040 -16293
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/App.tsx +16 -17
- package/src/components/EditorShell.tsx +3 -0
- package/src/components/TimelineToolbar.test.tsx +48 -3
- package/src/components/TimelineToolbar.tsx +89 -36
- package/src/components/editor/AnimationCard.test.tsx +18 -75
- package/src/components/editor/AnimationCard.tsx +26 -2
- package/src/components/editor/GsapAddAnimationControl.tsx +68 -0
- package/src/components/editor/GsapAnimationSection.tsx +18 -157
- package/src/components/editor/KeyframeEaseList.tsx +5 -1
- package/src/components/editor/KeyframeNavigation.tsx +46 -14
- package/src/components/editor/KeyframeNavigationDiamond.test.tsx +59 -0
- package/src/components/editor/MotionPathOverlay.tsx +32 -10
- package/src/components/editor/PropertyPanel.tsx +15 -5
- package/src/components/editor/PropertyPanelFlat.tsx +48 -23
- package/src/components/editor/gsapAnimationCallbacks.test.ts +122 -0
- package/src/components/editor/gsapAnimationCallbacks.ts +108 -4
- package/src/components/editor/keyframeRetime.test.ts +25 -22
- package/src/components/editor/keyframeRetime.ts +1 -2
- package/src/components/editor/propertyPanelFlatLayoutSection.tsx +2 -2
- package/src/components/editor/propertyPanelFlatMotionSection.tsx +25 -139
- package/src/components/editor/propertyPanelFlatTextSection.test.tsx +32 -16
- package/src/components/editor/propertyPanelFlatTextSection.tsx +21 -4
- package/src/components/nle/PreviewOverlays.tsx +3 -1
- package/src/components/nle/useTimelineEditCallbacks.test.ts +30 -0
- package/src/components/nle/useTimelineEditCallbacks.test.tsx +693 -0
- package/src/components/nle/useTimelineEditCallbacks.ts +197 -57
- package/src/contexts/TimelineEditContext.tsx +1 -1
- package/src/hooks/deleteSelectedKeyframes.ts +24 -8
- package/src/hooks/gsapDragCommit.ts +2 -2
- package/src/hooks/gsapDragPositionCommit.ts +84 -5
- package/src/hooks/gsapKeyframeCacheHelpers.test.ts +176 -1
- package/src/hooks/gsapKeyframeCacheHelpers.ts +93 -52
- package/src/hooks/gsapRuntimeBridge.test.ts +100 -0
- package/src/hooks/gsapScriptCommitHelpers.ts +3 -2
- package/src/hooks/gsapScriptCommitTypes.ts +2 -0
- package/src/hooks/gsapShared.test.ts +109 -1
- package/src/hooks/gsapShared.ts +142 -17
- package/src/hooks/gsapTweenSynth.test.ts +30 -1
- package/src/hooks/gsapTweenSynth.ts +61 -14
- package/src/hooks/keyframeCacheAstLoad.ts +181 -0
- package/src/hooks/timelineEditingHelpers.test.ts +80 -0
- package/src/hooks/timelineMoveAdapter.ts +9 -2
- package/src/hooks/useDomEditWiring.ts +2 -2
- package/src/hooks/useDomSelection.ts +4 -0
- package/src/hooks/useEnableKeyframes.test.ts +122 -0
- package/src/hooks/useEnableKeyframes.ts +116 -56
- package/src/hooks/useGestureCommit.ts +2 -2
- package/src/hooks/useGsapAnimationFetchFallback.ts +1 -1
- package/src/hooks/useGsapKeyframeOps.test.tsx +92 -13
- package/src/hooks/useGsapKeyframeOps.ts +30 -13
- package/src/hooks/useGsapScriptCommits.test.tsx +21 -0
- package/src/hooks/useGsapScriptCommits.ts +2 -0
- package/src/hooks/useGsapSelectionHandlers.test.tsx +69 -3
- package/src/hooks/useGsapSelectionHandlers.ts +90 -65
- package/src/hooks/useGsapTweenCache.test.ts +16 -0
- package/src/hooks/useGsapTweenCache.ts +64 -178
- package/src/hooks/usePopulateKeyframeCacheForFile.test.tsx +68 -0
- package/src/hooks/useStudioContextValue.test.ts +93 -0
- package/src/hooks/useStudioContextValue.ts +17 -3
- package/src/hooks/useStudioTestHooks.ts +63 -0
- package/src/hooks/useTimelineEditing.test.tsx +187 -22
- package/src/hooks/useTimelineEditing.ts +43 -32
- package/src/hooks/useTimelineEditingTypes.ts +2 -0
- package/src/hooks/useTimelineGroupEditing.ts +106 -82
- package/src/player/components/KeyframeDiamondContextMenu.test.tsx +69 -0
- package/src/player/components/KeyframeDiamondContextMenu.tsx +19 -8
- package/src/player/components/LayerDisclosureRow.tsx +76 -0
- package/src/player/components/Timeline.test.ts +420 -90
- package/src/player/components/Timeline.tsx +80 -78
- package/src/player/components/TimelineCanvas.tsx +33 -21
- package/src/player/components/TimelineClip.tsx +3 -1
- package/src/player/components/TimelineClipDiamonds.test.tsx +540 -16
- package/src/player/components/TimelineClipDiamonds.tsx +320 -117
- package/src/player/components/TimelineDiamondConnectors.tsx +145 -0
- package/src/player/components/TimelineLanes.tsx +153 -79
- package/src/player/components/TimelineOverlays.tsx +4 -18
- package/src/player/components/TimelinePropertyLanes.test.tsx +450 -0
- package/src/player/components/TimelinePropertyLanes.tsx +191 -0
- package/src/player/components/TimelineRazorInteraction.tsx +60 -0
- package/src/player/components/TimelineRuler.tsx +11 -21
- package/src/player/components/TimelineTrackHeader.test.tsx +346 -0
- package/src/player/components/TimelineTrackHeader.tsx +363 -0
- package/src/player/components/TrackClipCount.tsx +17 -0
- package/src/player/components/timelineCallbacks.ts +21 -6
- package/src/player/components/timelineClipDragPreview.test.ts +50 -3
- package/src/player/components/timelineClipDragPreview.ts +33 -15
- package/src/player/components/timelineCollision.ts +4 -13
- package/src/player/components/timelineDiamondTypes.ts +120 -0
- package/src/player/components/timelineDragDrop.ts +8 -3
- package/src/player/components/timelineEditing.test.ts +19 -27
- package/src/player/components/timelineEditing.ts +7 -8
- package/src/player/components/timelineKeyframeIdentity.test.ts +49 -0
- package/src/player/components/timelineKeyframeIdentity.ts +66 -0
- package/src/player/components/timelineLayout.test.ts +162 -9
- package/src/player/components/timelineLayout.ts +198 -38
- package/src/player/components/timelineMarquee.test.ts +76 -21
- package/src/player/components/timelineMarquee.ts +17 -14
- package/src/player/components/trackHeaderLaneState.ts +121 -0
- package/src/player/components/trackHeaderLaneValues.ts +139 -0
- package/src/player/components/useAutoExpandKeyframedClips.test.tsx +75 -0
- package/src/player/components/useAutoExpandKeyframedClips.ts +50 -0
- package/src/player/components/useTimelineClipDrag.ts +4 -1
- package/src/player/components/useTimelineGeometry.ts +4 -1
- package/src/player/components/useTimelineKeyframeHandlers.test.tsx +108 -0
- package/src/player/components/useTimelineKeyframeHandlers.ts +50 -15
- package/src/player/components/useTimelinePlayhead.ts +21 -12
- package/src/player/components/useTimelineRangeSelection.ts +35 -14
- package/src/player/components/useTimelineTrackLayout.test.ts +53 -0
- package/src/player/components/useTimelineTrackLayout.ts +162 -0
- package/src/player/hooks/useExpandedTimelineElements.test.ts +43 -0
- package/src/player/hooks/useExpandedTimelineElements.ts +21 -12
- package/src/player/lib/timelineElementHelpers.ts +26 -11
- package/src/player/store/keyframeSlice.ts +122 -0
- package/src/player/store/playerStore.test.ts +25 -0
- package/src/player/store/playerStore.ts +7 -43
- package/src/telemetry/events.test.ts +9 -3
- package/src/telemetry/events.ts +16 -7
- package/src/utils/globalTimeCompiler.test.ts +10 -0
- package/src/utils/globalTimeCompiler.ts +3 -3
- package/dist/assets/index-B37rWXo4.css +0 -1
- package/dist/assets/index-BdOfFsR8.js +0 -428
- package/src/player/components/TimelineDragGhost.tsx +0 -59
- package/src/utils/keyframeSelection.test.ts +0 -45
- package/src/utils/keyframeSelection.ts +0 -29
|
@@ -299,22 +299,17 @@ describe("FlatTextFieldEditor controls", () => {
|
|
|
299
299
|
|
|
300
300
|
describe("FlatTextSection — multi-field", () => {
|
|
301
301
|
it("shows the layer list, switches the active field's rows on selection, and has no doubled heading (this component never renders its own heading — the parent FlatGroup does)", () => {
|
|
302
|
-
const host =
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
onAddTextField={vi.fn()}
|
|
314
|
-
onRemoveTextField={vi.fn()}
|
|
315
|
-
/>,
|
|
316
|
-
);
|
|
317
|
-
});
|
|
302
|
+
const { host, root } = renderInto(
|
|
303
|
+
<FlatTextSection
|
|
304
|
+
element={makeMultiFieldElement()}
|
|
305
|
+
styles={{}}
|
|
306
|
+
fontAssets={[]}
|
|
307
|
+
onSetText={vi.fn()}
|
|
308
|
+
onSetTextFieldStyle={vi.fn()}
|
|
309
|
+
onAddTextField={vi.fn()}
|
|
310
|
+
onRemoveTextField={vi.fn()}
|
|
311
|
+
/>,
|
|
312
|
+
);
|
|
318
313
|
expect(host.textContent).toContain("Headline");
|
|
319
314
|
expect(host.textContent).toContain("Subhead");
|
|
320
315
|
// Active field's editor rows are visible (Font/Weight/etc. from FlatTextFieldEditor).
|
|
@@ -408,6 +403,27 @@ describe("FlatTextSection — multi-field", () => {
|
|
|
408
403
|
act(() => root.unmount());
|
|
409
404
|
});
|
|
410
405
|
|
|
406
|
+
it("does not steal canvas focus when a multi-field element is selected", () => {
|
|
407
|
+
const focusOwner = document.createElement("button");
|
|
408
|
+
document.body.append(focusOwner);
|
|
409
|
+
focusOwner.focus();
|
|
410
|
+
|
|
411
|
+
const { root } = renderInto(
|
|
412
|
+
<FlatTextSection
|
|
413
|
+
element={makeMultiFieldElement()}
|
|
414
|
+
styles={{}}
|
|
415
|
+
fontAssets={[]}
|
|
416
|
+
onSetText={vi.fn()}
|
|
417
|
+
onSetTextFieldStyle={vi.fn()}
|
|
418
|
+
onAddTextField={vi.fn()}
|
|
419
|
+
onRemoveTextField={vi.fn()}
|
|
420
|
+
/>,
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
expect(document.activeElement).toBe(focusOwner);
|
|
424
|
+
act(() => root.unmount());
|
|
425
|
+
});
|
|
426
|
+
|
|
411
427
|
it("auto-focuses the Content textarea when a new text field is added", async () => {
|
|
412
428
|
let addResolved = false;
|
|
413
429
|
|
|
@@ -257,6 +257,12 @@ export function FlatTextSection({
|
|
|
257
257
|
const [activeFieldKey, setActiveFieldKey] = useState<string | null>(
|
|
258
258
|
element.textFields[0]?.key ?? null,
|
|
259
259
|
);
|
|
260
|
+
// Armed by the add handler so the newly added field mounts focused. State, not
|
|
261
|
+
// a ref cleared during render: Strict Mode renders twice, so the first pass
|
|
262
|
+
// would eat the marker and the second would mount the field unfocused. Nothing
|
|
263
|
+
// clears it on read either — `autoFocus` is a mount-only DOM prop and the
|
|
264
|
+
// editor is keyed on the field, so it can only fire once per added field.
|
|
265
|
+
const [autoFocusFieldKey, setAutoFocusFieldKey] = useState<string | null>(null);
|
|
260
266
|
|
|
261
267
|
useEffect(() => {
|
|
262
268
|
const nextFields = element.textFields;
|
|
@@ -271,6 +277,8 @@ export function FlatTextSection({
|
|
|
271
277
|
const activeField = textFields.find((field) => field.key === activeFieldKey) ?? textFields[0];
|
|
272
278
|
if (!activeField) return null;
|
|
273
279
|
|
|
280
|
+
const autoFocusActiveField = autoFocusFieldKey === activeField.key;
|
|
281
|
+
|
|
274
282
|
if (textFields.length > 1) {
|
|
275
283
|
return (
|
|
276
284
|
<div className="space-y-2.5">
|
|
@@ -278,10 +286,15 @@ export function FlatTextSection({
|
|
|
278
286
|
fields={textFields}
|
|
279
287
|
activeFieldKey={activeField.key}
|
|
280
288
|
styles={styles}
|
|
281
|
-
onSelect={
|
|
289
|
+
onSelect={(fieldKey) => {
|
|
290
|
+
setAutoFocusFieldKey(null);
|
|
291
|
+
setActiveFieldKey(fieldKey);
|
|
292
|
+
}}
|
|
282
293
|
onAdd={() =>
|
|
283
294
|
void Promise.resolve(onAddTextField(activeField.key)).then((nextKey) => {
|
|
284
|
-
if (nextKey)
|
|
295
|
+
if (!nextKey) return;
|
|
296
|
+
setAutoFocusFieldKey(nextKey);
|
|
297
|
+
setActiveFieldKey(nextKey);
|
|
285
298
|
})
|
|
286
299
|
}
|
|
287
300
|
onRemove={onRemoveTextField}
|
|
@@ -295,7 +308,7 @@ export function FlatTextSection({
|
|
|
295
308
|
onSetText={onSetText}
|
|
296
309
|
onSetTextFieldStyle={onSetTextFieldStyle}
|
|
297
310
|
onPreviewTextFieldStyle={onPreviewTextFieldStyle}
|
|
298
|
-
autoFocus
|
|
311
|
+
autoFocus={autoFocusActiveField}
|
|
299
312
|
/>
|
|
300
313
|
</div>
|
|
301
314
|
);
|
|
@@ -316,7 +329,11 @@ export function FlatTextSection({
|
|
|
316
329
|
type="button"
|
|
317
330
|
onClick={() => {
|
|
318
331
|
track("button", "Add text field");
|
|
319
|
-
void onAddTextField(activeField.key)
|
|
332
|
+
void Promise.resolve(onAddTextField(activeField.key)).then((nextKey) => {
|
|
333
|
+
if (!nextKey) return;
|
|
334
|
+
setAutoFocusFieldKey(nextKey);
|
|
335
|
+
setActiveFieldKey(nextKey);
|
|
336
|
+
});
|
|
320
337
|
}}
|
|
321
338
|
className="mt-0.5 flex items-center gap-[5px] text-[10px] text-panel-text-4 hover:text-panel-text-2"
|
|
322
339
|
>
|
|
@@ -27,6 +27,7 @@ import type { GestureRecordingState } from "../editor/GestureRecordControl";
|
|
|
27
27
|
import type { ReactNode } from "react";
|
|
28
28
|
|
|
29
29
|
export interface PreviewOverlaysProps {
|
|
30
|
+
shouldShowMotionPath: boolean;
|
|
30
31
|
shouldShowSelectedDomBounds: boolean;
|
|
31
32
|
blockPreview?: BlockPreviewInfo | null;
|
|
32
33
|
isGestureRecording?: boolean;
|
|
@@ -132,6 +133,7 @@ export function resolveZIndexEntries(
|
|
|
132
133
|
|
|
133
134
|
// fallow-ignore-next-line complexity
|
|
134
135
|
export function PreviewOverlays({
|
|
136
|
+
shouldShowMotionPath,
|
|
135
137
|
shouldShowSelectedDomBounds,
|
|
136
138
|
blockPreview,
|
|
137
139
|
isGestureRecording,
|
|
@@ -274,7 +276,7 @@ export function PreviewOverlays({
|
|
|
274
276
|
{STUDIO_KEYFRAMES_ENABLED && (
|
|
275
277
|
<MotionPathOverlay
|
|
276
278
|
iframeRef={previewIframeRef}
|
|
277
|
-
selection={
|
|
279
|
+
selection={shouldShowMotionPath ? domEditSelection : null}
|
|
278
280
|
compositionSize={compositionDimensions}
|
|
279
281
|
isPlaying={isPlaying}
|
|
280
282
|
/>
|
|
@@ -40,6 +40,36 @@ describe("resolveTimelineKeyframeTarget", () => {
|
|
|
40
40
|
).toBeNull();
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
+
it("uses the rendered animation identity to resolve a same-group collision", () => {
|
|
44
|
+
expect(
|
|
45
|
+
resolveTimelineKeyframeTarget(
|
|
46
|
+
50,
|
|
47
|
+
[
|
|
48
|
+
{
|
|
49
|
+
percentage: 50,
|
|
50
|
+
tweenPercentage: 25,
|
|
51
|
+
propertyGroup: "position",
|
|
52
|
+
animationId: "position-b",
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
[
|
|
56
|
+
{ id: "position-a", propertyGroup: "position" },
|
|
57
|
+
{ id: "position-b", propertyGroup: "position" },
|
|
58
|
+
],
|
|
59
|
+
),
|
|
60
|
+
).toEqual({ animId: "position-b", tweenPct: 25 });
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("rejects a rendered animation identity absent from the element", () => {
|
|
64
|
+
expect(
|
|
65
|
+
resolveTimelineKeyframeTarget(
|
|
66
|
+
50,
|
|
67
|
+
[{ percentage: 50, animationId: "stale-position" }],
|
|
68
|
+
[{ id: "position", propertyGroup: "position" }],
|
|
69
|
+
),
|
|
70
|
+
).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
43
73
|
it("keeps a keyframed and flat tween in the same property group unresolved", () => {
|
|
44
74
|
expect(
|
|
45
75
|
resolveTimelineKeyframeTarget(
|