@hyperframes/studio 0.6.59 → 0.6.60
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-B5EnhVCT.css +1 -0
- package/dist/assets/index-DG5-N9Mj.js +139 -0
- package/dist/index.html +2 -2
- package/package.json +4 -4
- package/src/App.tsx +63 -127
- package/src/components/AskAgentModal.tsx +14 -2
- package/src/components/StudioRightPanel.tsx +1 -0
- package/src/components/editor/AnimationCard.tsx +60 -53
- package/src/components/editor/EaseCurveSection.tsx +5 -2
- package/src/components/editor/PropertyPanel.tsx +3 -1
- package/src/components/editor/domEditingAgentPrompt.ts +14 -0
- package/src/components/editor/domEditingElement.ts +34 -15
- package/src/components/editor/gsapAnimationConstants.ts +3 -1
- package/src/components/editor/gsapAnimationHelpers.test.ts +67 -0
- package/src/components/editor/gsapAnimationHelpers.ts +36 -0
- package/src/components/editor/propertyPanelPrimitives.tsx +1 -1
- package/src/components/nle/NLELayout.tsx +2 -1
- package/src/hooks/useDomEditSession.ts +18 -10
- package/src/hooks/useDomSelection.ts +35 -1
- package/src/hooks/useFileManager.ts +4 -5
- package/src/hooks/useGsapScriptCommits.ts +3 -0
- package/src/hooks/usePreviewInteraction.ts +67 -3
- package/src/hooks/useStudioContextValue.ts +138 -0
- package/src/utils/studioPreviewHelpers.ts +38 -0
- package/dist/assets/index-B1XH-ptc.js +0 -138
- package/dist/assets/index-DH9QNjuX.css +0 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { useCallback, useMemo, useRef, useState, type DragEvent } from "react";
|
|
2
|
+
import {
|
|
3
|
+
STUDIO_INSPECTOR_PANELS_ENABLED,
|
|
4
|
+
STUDIO_MOTION_PANEL_ENABLED,
|
|
5
|
+
} from "../components/editor/manualEditingAvailability";
|
|
6
|
+
import { readStudioMotionFromElement } from "../components/editor/studioMotion";
|
|
7
|
+
import type { StudioContextValue } from "../contexts/StudioContext";
|
|
8
|
+
import type { DomEditSelection } from "../components/editor/domEditing";
|
|
9
|
+
|
|
10
|
+
interface StudioContextInput {
|
|
11
|
+
projectId: string;
|
|
12
|
+
activeCompPath: string | null;
|
|
13
|
+
setActiveCompPath: (path: string | null) => void;
|
|
14
|
+
showToast: (message: string, tone?: "error" | "info") => void;
|
|
15
|
+
previewIframeRef: React.MutableRefObject<HTMLIFrameElement | null>;
|
|
16
|
+
captionEditMode: boolean;
|
|
17
|
+
compositionLoading: boolean;
|
|
18
|
+
refreshKey: number;
|
|
19
|
+
setRefreshKey: React.Dispatch<React.SetStateAction<number>>;
|
|
20
|
+
currentTime: number;
|
|
21
|
+
timelineElements: StudioContextValue["timelineElements"];
|
|
22
|
+
isPlaying: boolean;
|
|
23
|
+
editHistory: { canUndo: boolean; canRedo: boolean; undoLabel: string; redoLabel: string };
|
|
24
|
+
handleUndo: StudioContextValue["handleUndo"];
|
|
25
|
+
handleRedo: StudioContextValue["handleRedo"];
|
|
26
|
+
renderQueue: {
|
|
27
|
+
jobs: unknown[];
|
|
28
|
+
isRendering: boolean;
|
|
29
|
+
deleteRender: (id: string) => void;
|
|
30
|
+
clearCompleted: () => void;
|
|
31
|
+
startRender: (options: unknown) => Promise<void>;
|
|
32
|
+
};
|
|
33
|
+
compositionDimensions: { width: number; height: number } | null;
|
|
34
|
+
waitForPendingDomEditSaves: () => Promise<void>;
|
|
35
|
+
handlePreviewIframeRef: (iframe: HTMLIFrameElement | null) => void;
|
|
36
|
+
refreshPreviewDocumentVersion: () => void;
|
|
37
|
+
timelineVisible: boolean;
|
|
38
|
+
toggleTimelineVisibility: () => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// fallow-ignore-next-line complexity
|
|
42
|
+
export function buildStudioContextValue(input: StudioContextInput): StudioContextValue {
|
|
43
|
+
return {
|
|
44
|
+
projectId: input.projectId,
|
|
45
|
+
activeCompPath: input.activeCompPath,
|
|
46
|
+
setActiveCompPath: input.setActiveCompPath,
|
|
47
|
+
showToast: input.showToast,
|
|
48
|
+
previewIframeRef: input.previewIframeRef,
|
|
49
|
+
captionEditMode: input.captionEditMode,
|
|
50
|
+
compositionLoading: input.compositionLoading,
|
|
51
|
+
refreshKey: input.refreshKey,
|
|
52
|
+
setRefreshKey: input.setRefreshKey,
|
|
53
|
+
currentTime: input.currentTime,
|
|
54
|
+
timelineElements: input.timelineElements,
|
|
55
|
+
isPlaying: input.isPlaying,
|
|
56
|
+
editHistory: input.editHistory,
|
|
57
|
+
handleUndo: input.handleUndo,
|
|
58
|
+
handleRedo: input.handleRedo,
|
|
59
|
+
renderQueue: input.renderQueue,
|
|
60
|
+
compositionDimensions: input.compositionDimensions,
|
|
61
|
+
waitForPendingDomEditSaves: input.waitForPendingDomEditSaves,
|
|
62
|
+
handlePreviewIframeRef: input.handlePreviewIframeRef,
|
|
63
|
+
refreshPreviewDocumentVersion: input.refreshPreviewDocumentVersion,
|
|
64
|
+
timelineVisible: input.timelineVisible,
|
|
65
|
+
toggleTimelineVisibility: input.toggleTimelineVisibility,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface InspectorState {
|
|
70
|
+
selectedStudioMotion: ReturnType<typeof readStudioMotionFromElement> | null;
|
|
71
|
+
layersPanelActive: boolean;
|
|
72
|
+
designPanelActive: boolean;
|
|
73
|
+
motionPanelActive: boolean;
|
|
74
|
+
inspectorPanelActive: boolean;
|
|
75
|
+
inspectorButtonActive: boolean;
|
|
76
|
+
shouldShowSelectedDomBounds: boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function useInspectorState(
|
|
80
|
+
rightPanelTab: string,
|
|
81
|
+
rightCollapsed: boolean,
|
|
82
|
+
isPlaying: boolean,
|
|
83
|
+
domEditSelection: DomEditSelection | null,
|
|
84
|
+
): InspectorState {
|
|
85
|
+
// fallow-ignore-next-line complexity
|
|
86
|
+
return useMemo(() => {
|
|
87
|
+
const selectedStudioMotion =
|
|
88
|
+
STUDIO_INSPECTOR_PANELS_ENABLED && domEditSelection
|
|
89
|
+
? readStudioMotionFromElement(domEditSelection.element)
|
|
90
|
+
: null;
|
|
91
|
+
const layersPanelActive = STUDIO_INSPECTOR_PANELS_ENABLED && rightPanelTab === "layers";
|
|
92
|
+
const designPanelActive = STUDIO_INSPECTOR_PANELS_ENABLED && rightPanelTab === "design";
|
|
93
|
+
const motionPanelActive =
|
|
94
|
+
STUDIO_INSPECTOR_PANELS_ENABLED && STUDIO_MOTION_PANEL_ENABLED && rightPanelTab === "motion";
|
|
95
|
+
const inspectorPanelActive = layersPanelActive || designPanelActive || motionPanelActive;
|
|
96
|
+
return {
|
|
97
|
+
selectedStudioMotion,
|
|
98
|
+
layersPanelActive,
|
|
99
|
+
designPanelActive,
|
|
100
|
+
motionPanelActive,
|
|
101
|
+
inspectorPanelActive,
|
|
102
|
+
inspectorButtonActive:
|
|
103
|
+
STUDIO_INSPECTOR_PANELS_ENABLED && !rightCollapsed && inspectorPanelActive,
|
|
104
|
+
shouldShowSelectedDomBounds: inspectorPanelActive && !rightCollapsed && !isPlaying,
|
|
105
|
+
};
|
|
106
|
+
}, [rightPanelTab, rightCollapsed, isPlaying, domEditSelection]);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// fallow-ignore-next-line complexity
|
|
110
|
+
export function useDragOverlay(onImportFiles: (files: FileList) => void) {
|
|
111
|
+
const [active, setActive] = useState(false);
|
|
112
|
+
const counterRef = useRef(0);
|
|
113
|
+
const onDragOver = useCallback((e: DragEvent) => {
|
|
114
|
+
if (!e.dataTransfer.types.includes("Files")) return;
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
}, []);
|
|
117
|
+
const onDragEnter = useCallback((e: DragEvent) => {
|
|
118
|
+
if (!e.dataTransfer.types.includes("Files")) return;
|
|
119
|
+
e.preventDefault();
|
|
120
|
+
counterRef.current++;
|
|
121
|
+
setActive(true);
|
|
122
|
+
}, []);
|
|
123
|
+
const onDragLeave = useCallback(() => {
|
|
124
|
+
counterRef.current--;
|
|
125
|
+
if (counterRef.current === 0) setActive(false);
|
|
126
|
+
}, []);
|
|
127
|
+
const onDrop = useCallback(
|
|
128
|
+
(e: DragEvent) => {
|
|
129
|
+
counterRef.current = 0;
|
|
130
|
+
setActive(false);
|
|
131
|
+
if (e.defaultPrevented) return;
|
|
132
|
+
e.preventDefault();
|
|
133
|
+
if (e.dataTransfer.files.length) onImportFiles(e.dataTransfer.files);
|
|
134
|
+
},
|
|
135
|
+
[onImportFiles],
|
|
136
|
+
);
|
|
137
|
+
return { active, onDragOver, onDragEnter, onDragLeave, onDrop };
|
|
138
|
+
}
|
|
@@ -3,6 +3,7 @@ import { resolveVisualDomEditSelectionTarget } from "../components/editor/domEdi
|
|
|
3
3
|
import {
|
|
4
4
|
getDomLayerPatchTarget,
|
|
5
5
|
isElementComputedVisible,
|
|
6
|
+
resolveAllVisualDomEditTargets,
|
|
6
7
|
} from "../components/editor/domEditingElement";
|
|
7
8
|
import { getEventTargetElement } from "./studioHelpers";
|
|
8
9
|
|
|
@@ -58,6 +59,7 @@ function removePointerEventsOverride(style: HTMLStyleElement | null): void {
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
// fallow-ignore-next-line complexity
|
|
61
63
|
export function getPreviewTargetFromPointer(
|
|
62
64
|
iframe: HTMLIFrameElement,
|
|
63
65
|
clientX: number,
|
|
@@ -98,6 +100,42 @@ export function getPreviewTargetFromPointer(
|
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
102
|
|
|
103
|
+
/** Returns all independently-selectable elements at the pointer (topmost first). */
|
|
104
|
+
export function getAllPreviewTargetsFromPointer(
|
|
105
|
+
iframe: HTMLIFrameElement,
|
|
106
|
+
clientX: number,
|
|
107
|
+
clientY: number,
|
|
108
|
+
activeCompositionPath: string | null,
|
|
109
|
+
): HTMLElement[] {
|
|
110
|
+
let doc: Document | null = null;
|
|
111
|
+
let win: Window | null = null;
|
|
112
|
+
try {
|
|
113
|
+
doc = iframe.contentDocument;
|
|
114
|
+
win = iframe.contentWindow;
|
|
115
|
+
} catch {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
if (!doc || !win) return [];
|
|
119
|
+
|
|
120
|
+
const localPointer = resolvePreviewLocalPointer(iframe, doc, win, clientX, clientY);
|
|
121
|
+
if (!localPointer) return [];
|
|
122
|
+
|
|
123
|
+
const overrideStyle = forcePointerEventsAuto(doc);
|
|
124
|
+
try {
|
|
125
|
+
if (typeof doc.elementsFromPoint === "function") {
|
|
126
|
+
return resolveAllVisualDomEditTargets(doc.elementsFromPoint(localPointer.x, localPointer.y), {
|
|
127
|
+
activeCompositionPath,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
const fallback = getEventTargetElement(doc.elementFromPoint(localPointer.x, localPointer.y));
|
|
131
|
+
if (!fallback || !getDomLayerPatchTarget(fallback, activeCompositionPath)) return [];
|
|
132
|
+
if (!isElementComputedVisible(fallback)) return [];
|
|
133
|
+
return [fallback];
|
|
134
|
+
} finally {
|
|
135
|
+
removePointerEventsOverride(overrideStyle);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
101
139
|
function objectLike(value: unknown): object | null {
|
|
102
140
|
return value && (typeof value === "object" || typeof value === "function") ? value : null;
|
|
103
141
|
}
|