@hyperframes/studio 0.8.56 → 0.8.57
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-c23wnIfc.js +464 -0
- package/dist/assets/{index-ZmBtVrEm.css → index-DKJpHYYd.css} +1 -1
- package/dist/assets/{index-DFlILHJe.js → index-DgkAzx3n.js} +1 -1
- package/dist/assets/{index-CxR7F2YQ.js → index-IDNr3X1H.js} +1 -1
- package/dist/assets/{index-BlkJ_Axe.js → index-gbOCDCuZ.js} +194 -194
- package/dist/index.d.ts +95 -9
- package/dist/index.html +2 -2
- package/dist/index.js +15573 -14835
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +7 -7
- package/src/App.tsx +4 -0
- package/src/components/EditorShell.tsx +3 -1
- package/src/components/ProjectUnreachableBanner.test.tsx +113 -0
- package/src/components/ProjectUnreachableBanner.tsx +89 -0
- package/src/components/editor/DomEditOverlay.tsx +0 -15
- package/src/components/editor/GridOverlay.tsx +12 -23
- package/src/components/editor/MotionPathOverlay.test.tsx +66 -0
- package/src/components/editor/MotionPathOverlay.tsx +3 -1
- package/src/components/editor/PreviewGuides.test.tsx +6 -1
- package/src/components/editor/PreviewGuides.tsx +8 -10
- package/src/components/editor/PreviewOverlayProvider.test.tsx +90 -0
- package/src/components/editor/PreviewOverlayProvider.tsx +86 -0
- package/src/components/editor/SnapToolbar.test.tsx +25 -12
- package/src/components/editor/SnapToolbar.tsx +30 -59
- package/src/components/editor/propertyPanelFlatMediaSection.test.tsx +2 -2
- package/src/components/editor/propertyPanelFlatMediaSection.tsx +46 -12
- package/src/components/editor/usePreviewCompositionRect.test.tsx +19 -13
- package/src/components/editor/usePreviewCompositionRect.ts +12 -12
- package/src/components/editor/useVolumeAutomation.test.tsx +68 -0
- package/src/components/editor/useVolumeAutomation.ts +57 -16
- package/src/components/nle/NLEContext.tsx +40 -4
- package/src/components/nle/NLEPreview.test.ts +66 -1
- package/src/components/nle/NLEPreview.tsx +88 -44
- package/src/components/nle/PreviewOverlays.tsx +4 -16
- package/src/components/nle/PreviewPane.tsx +17 -2
- package/src/components/nle/previewReadOnly.test.tsx +1 -0
- package/src/hooks/useCompositionDimensions.test.tsx +38 -0
- package/src/hooks/useCompositionDimensions.ts +17 -1
- package/src/hooks/useDomEditAttributeCommits.test.ts +23 -0
- package/src/hooks/useDomEditAttributeCommits.ts +14 -3
- package/src/hooks/useFileTree.test.tsx +56 -0
- package/src/hooks/useFileTree.ts +8 -1
- package/src/hooks/useSdkSession.lifecycle.test.tsx +80 -0
- package/src/hooks/useSdkSession.ts +46 -10
- package/src/hooks/useServerConnection.hashProject.test.tsx +303 -0
- package/src/hooks/useServerConnection.ts +81 -8
- package/src/index.ts +12 -0
- package/src/player/components/Player.test.ts +141 -13
- package/src/player/components/Player.tsx +58 -2
- package/src/player/components/ShortcutsPanel.test.tsx +4 -10
- package/src/player/components/Timeline.tsx +23 -572
- package/src/player/components/TimelineCanvas.tsx +9 -30
- package/src/player/components/TimelineOverlays.test.ts +67 -32
- package/src/player/components/TimelineOverlays.tsx +46 -107
- package/src/player/components/TimelineProvider.tsx +191 -0
- package/src/player/components/testRootHarness.ts +18 -0
- package/src/player/components/timelineProviderStateBuilders.test.ts +109 -0
- package/src/player/components/timelineProviderStateBuilders.ts +83 -0
- package/src/player/components/useTimelineClipContextMenu.ts +1 -1
- package/src/player/components/useTimelineOverlaysState.ts +100 -0
- package/src/player/components/useTimelineProviderState.tsx +583 -0
- package/src/player/hooks/playbackAdapterResolution.ts +98 -0
- package/src/player/hooks/timelinePlayerTestHarness.ts +120 -0
- package/src/player/hooks/usePreviewFirstFrameTelemetry.ts +51 -0
- package/src/player/hooks/useShadowPreviewReload.ts +183 -0
- package/src/player/hooks/useTimelinePlayer.seek.test.ts +8 -88
- package/src/player/hooks/useTimelinePlayer.shadowReload.test.ts +457 -0
- package/src/player/hooks/useTimelinePlayer.ts +65 -99
- package/src/player/hooks/useTimelineSyncCallbacks.ts +159 -88
- package/src/telemetry/events.test.ts +18 -0
- package/src/telemetry/events.ts +10 -0
- package/src/telemetry/system.test.ts +49 -0
- package/src/telemetry/system.ts +6 -0
- package/src/telemetry/tabId.test.ts +43 -0
- package/src/telemetry/tabId.ts +44 -0
- package/src/utils/previewCompositionSize.ts +26 -0
- package/src/utils/reloadDebug.ts +2 -4
- package/src/utils/sourcePatcher.ts +1 -1
- package/src/utils/studioTelemetry.test.ts +14 -0
- package/src/utils/studioTelemetry.ts +4 -0
- package/dist/assets/hyperframes-player-k8LzMtXX.js +0 -464
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TimelineMeta,
|
|
3
|
+
TimelineContainerProps,
|
|
4
|
+
TimelineViewportProps,
|
|
5
|
+
} from "./TimelineProvider";
|
|
6
|
+
import type { ResizingClipState } from "./useTimelineClipDrag";
|
|
7
|
+
import type { TimelineLaneBaseProps } from "./timelineLaneProps";
|
|
8
|
+
|
|
9
|
+
export function resolveResizingElementIds(
|
|
10
|
+
resizingClip: ResizingClipState | null,
|
|
11
|
+
): readonly string[] | undefined {
|
|
12
|
+
if (resizingClip?.groupPreview) return resizingClip.groupPreview.map((change) => change.key);
|
|
13
|
+
if (resizingClip) return [resizingClip.element.key ?? resizingClip.element.id];
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function resolveRenderClipContent(
|
|
18
|
+
rowVirtualizationActive: boolean,
|
|
19
|
+
isScrolling: boolean,
|
|
20
|
+
renderClipContent: TimelineLaneBaseProps["renderClipContent"],
|
|
21
|
+
): TimelineLaneBaseProps["renderClipContent"] {
|
|
22
|
+
return rowVirtualizationActive && isScrolling ? undefined : renderClipContent;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function shouldIgnoreTimelinePointerDown(target: EventTarget | null): boolean {
|
|
26
|
+
return target instanceof Element && target.closest("button, input, select, a") !== null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type ContainerInputs = Omit<TimelineContainerProps, "className"> & {
|
|
30
|
+
isDragOver: boolean;
|
|
31
|
+
activeTool: string;
|
|
32
|
+
shiftHeld: boolean;
|
|
33
|
+
accentClass: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type ViewportInputs = Omit<
|
|
37
|
+
TimelineViewportProps,
|
|
38
|
+
"className" | "data-timeline-scroll-viewport" | "data-timeline-auto-scroll-left-inset"
|
|
39
|
+
> & {
|
|
40
|
+
labelMode: boolean;
|
|
41
|
+
zoomMode: "fit" | "manual";
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export interface TimelineMetaBuilderInputs {
|
|
45
|
+
emptyState: TimelineMeta["emptyState"];
|
|
46
|
+
container: ContainerInputs;
|
|
47
|
+
viewport: ViewportInputs;
|
|
48
|
+
elementCount: number;
|
|
49
|
+
labelColumnWidth: number;
|
|
50
|
+
razorGuide: TimelineMeta["razorGuide"];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function buildTimelineMeta({
|
|
54
|
+
emptyState,
|
|
55
|
+
container,
|
|
56
|
+
viewport,
|
|
57
|
+
elementCount,
|
|
58
|
+
labelColumnWidth,
|
|
59
|
+
razorGuide,
|
|
60
|
+
}: TimelineMetaBuilderInputs): TimelineMeta {
|
|
61
|
+
const { labelMode, zoomMode, ...viewportProps } = viewport;
|
|
62
|
+
const containerClassName = `relative border-t select-none h-full overflow-hidden ${container.isDragOver ? container.accentClass : ""} ${container.activeTool === "razor" ? "cursor-crosshair" : container.shiftHeld ? "cursor-crosshair" : "cursor-default"}`;
|
|
63
|
+
const viewportClassName = `${zoomMode === "fit" ? "overflow-x-hidden" : "overflow-x-auto"} overflow-y-auto h-full outline-hidden`;
|
|
64
|
+
return {
|
|
65
|
+
emptyState,
|
|
66
|
+
containerProps: {
|
|
67
|
+
ref: container.ref,
|
|
68
|
+
"aria-label": container["aria-label"],
|
|
69
|
+
"data-timeline-element-count": elementCount,
|
|
70
|
+
className: containerClassName,
|
|
71
|
+
onMouseMove: container.onMouseMove,
|
|
72
|
+
onMouseLeave: container.onMouseLeave,
|
|
73
|
+
style: container.style,
|
|
74
|
+
},
|
|
75
|
+
viewportProps: {
|
|
76
|
+
...viewportProps,
|
|
77
|
+
"data-timeline-scroll-viewport": true,
|
|
78
|
+
"data-timeline-auto-scroll-left-inset": labelMode ? labelColumnWidth : 0,
|
|
79
|
+
className: viewportClassName,
|
|
80
|
+
},
|
|
81
|
+
razorGuide,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
2
|
import { usePlayerStore, type TimelineElement } from "../store/playerStore";
|
|
3
|
-
import type { ClipContextMenuState } from "./
|
|
3
|
+
import type { ClipContextMenuState } from "./TimelineProvider";
|
|
4
4
|
|
|
5
5
|
/** A clicked member of an active multi-selection keeps the whole group
|
|
6
6
|
* selected, so the context menu it opens acts on all of it. */
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { TimelineElement } from "../store/playerStore";
|
|
2
|
+
import type { TimelineOverlaysState } from "./TimelineProvider";
|
|
3
|
+
import type { TimelineTimeRange } from "../store/rangeSelectionSlice";
|
|
4
|
+
import { useClipContextMenu } from "./useTimelineClipContextMenu";
|
|
5
|
+
import { useTimelineRangeSelection } from "./useTimelineRangeSelection";
|
|
6
|
+
import { usePublishRangeSelection } from "./usePublishRangeSelection";
|
|
7
|
+
import { useTimelineSelectionLifecycle } from "./useTimelineSelectionLifecycle";
|
|
8
|
+
import { useTrackGapMenu, type TrackGapHighlight } from "./useTrackGapMenu";
|
|
9
|
+
|
|
10
|
+
type OverlayStateInputs = Omit<
|
|
11
|
+
TimelineOverlaysState,
|
|
12
|
+
| "elements"
|
|
13
|
+
| "elementsRef"
|
|
14
|
+
| "gapContextMenu"
|
|
15
|
+
| "onDismissGapContextMenu"
|
|
16
|
+
| "onCloseTrackGap"
|
|
17
|
+
| "onCloseAllTrackGaps"
|
|
18
|
+
| "onHoverGapAction"
|
|
19
|
+
| "rangeSelection"
|
|
20
|
+
| "setRangeSelection"
|
|
21
|
+
> & {
|
|
22
|
+
elements: TimelineElement[];
|
|
23
|
+
elementsRef: { current: TimelineElement[] };
|
|
24
|
+
rangeSelection: Parameters<typeof useTimelineRangeSelection>[0] & {
|
|
25
|
+
onRangeSelect?: (range: TimelineTimeRange | null) => void;
|
|
26
|
+
};
|
|
27
|
+
gapMenu: Parameters<typeof useTrackGapMenu>[0];
|
|
28
|
+
onSelectElement: ((element: TimelineElement | null) => void) | undefined;
|
|
29
|
+
selectedElementId: string | null;
|
|
30
|
+
setRangeSelectionRef: { current: ((selection: null) => void) | null };
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type TimelineOverlaysStateResult = {
|
|
34
|
+
overlays: TimelineOverlaysState;
|
|
35
|
+
shiftClickClipRef: ReturnType<typeof useTimelineRangeSelection>["shiftClickClipRef"];
|
|
36
|
+
marqueeRect: ReturnType<typeof useTimelineRangeSelection>["marqueeRect"];
|
|
37
|
+
isScrubbing: ReturnType<typeof useTimelineRangeSelection>["isScrubbing"];
|
|
38
|
+
handlePointerDown: ReturnType<typeof useTimelineRangeSelection>["handlePointerDown"];
|
|
39
|
+
handlePointerMove: ReturnType<typeof useTimelineRangeSelection>["handlePointerMove"];
|
|
40
|
+
handlePointerUp: ReturnType<typeof useTimelineRangeSelection>["handlePointerUp"];
|
|
41
|
+
handlePointerCancel: ReturnType<typeof useTimelineRangeSelection>["handlePointerCancel"];
|
|
42
|
+
gapHighlight: TrackGapHighlight | null;
|
|
43
|
+
openGapMenu: ReturnType<typeof useTrackGapMenu>["openGapMenu"];
|
|
44
|
+
onContextMenuClip: ReturnType<typeof useClipContextMenu>;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export function useTimelineOverlaysState({
|
|
48
|
+
gapMenu,
|
|
49
|
+
rangeSelection: rangeSelectionInputs,
|
|
50
|
+
onSelectElement,
|
|
51
|
+
selectedElementId,
|
|
52
|
+
setRangeSelectionRef,
|
|
53
|
+
...state
|
|
54
|
+
}: OverlayStateInputs): TimelineOverlaysStateResult {
|
|
55
|
+
const {
|
|
56
|
+
rangeSelection,
|
|
57
|
+
setRangeSelection,
|
|
58
|
+
shiftClickClipRef,
|
|
59
|
+
marqueeRect,
|
|
60
|
+
isScrubbing,
|
|
61
|
+
handlePointerDown,
|
|
62
|
+
handlePointerMove,
|
|
63
|
+
handlePointerUp,
|
|
64
|
+
handlePointerCancel,
|
|
65
|
+
} = useTimelineRangeSelection(rangeSelectionInputs);
|
|
66
|
+
usePublishRangeSelection(rangeSelection, rangeSelectionInputs.onRangeSelect);
|
|
67
|
+
setRangeSelectionRef.current = () => setRangeSelection(null); // oxlint-disable-line react/refs -- stable ref consumed by useTimelineClipDrag
|
|
68
|
+
useTimelineSelectionLifecycle(state.elements, selectedElementId, state.setShowPopover, () =>
|
|
69
|
+
setRangeSelection(null),
|
|
70
|
+
);
|
|
71
|
+
const gap = useTrackGapMenu(gapMenu);
|
|
72
|
+
const onContextMenuClip = useClipContextMenu(
|
|
73
|
+
onSelectElement,
|
|
74
|
+
gap.dismissGapMenu,
|
|
75
|
+
state.setClipContextMenu,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
overlays: {
|
|
80
|
+
...state,
|
|
81
|
+
rangeSelection,
|
|
82
|
+
setRangeSelection,
|
|
83
|
+
gapContextMenu: gap.gapMenuModel,
|
|
84
|
+
onDismissGapContextMenu: gap.dismissGapMenu,
|
|
85
|
+
onCloseTrackGap: gap.closeTrackGap,
|
|
86
|
+
onCloseAllTrackGaps: gap.closeAllTrackGaps,
|
|
87
|
+
onHoverGapAction: gap.setHoveredGapAction,
|
|
88
|
+
},
|
|
89
|
+
shiftClickClipRef,
|
|
90
|
+
marqueeRect,
|
|
91
|
+
isScrubbing,
|
|
92
|
+
handlePointerDown,
|
|
93
|
+
handlePointerMove,
|
|
94
|
+
handlePointerUp,
|
|
95
|
+
handlePointerCancel,
|
|
96
|
+
gapHighlight: gap.gapHighlight,
|
|
97
|
+
openGapMenu: gap.openGapMenu,
|
|
98
|
+
onContextMenuClip,
|
|
99
|
+
};
|
|
100
|
+
}
|