@hyperframes/studio 0.5.5 → 0.6.0-alpha.1
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-Cd8vYWxP.js +198 -0
- package/dist/assets/index-D04_ZoMm.js +107 -0
- package/dist/assets/index-UWFaHilT.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +4 -4
- package/src/App.tsx +2621 -170
- package/src/components/LintModal.tsx +3 -4
- package/src/components/editor/DomEditOverlay.test.ts +241 -0
- package/src/components/editor/DomEditOverlay.tsx +1300 -0
- package/src/components/editor/MotionPanel.tsx +651 -0
- package/src/components/editor/PropertyPanel.test.ts +67 -0
- package/src/components/editor/PropertyPanel.tsx +2891 -207
- package/src/components/editor/TimelineLayerPanel.test.ts +42 -0
- package/src/components/editor/TimelineLayerPanel.tsx +113 -0
- package/src/components/editor/colorValue.test.ts +82 -0
- package/src/components/editor/colorValue.ts +175 -0
- package/src/components/editor/domEditing.test.ts +872 -0
- package/src/components/editor/domEditing.ts +993 -0
- package/src/components/editor/floatingPanel.test.ts +34 -0
- package/src/components/editor/floatingPanel.ts +54 -0
- package/src/components/editor/fontAssets.ts +32 -0
- package/src/components/editor/fontCatalog.ts +126 -0
- package/src/components/editor/gradientValue.test.ts +89 -0
- package/src/components/editor/gradientValue.ts +445 -0
- package/src/components/editor/manualEditingAvailability.test.ts +120 -0
- package/src/components/editor/manualEditingAvailability.ts +60 -0
- package/src/components/editor/manualEdits.test.ts +945 -0
- package/src/components/editor/manualEdits.ts +1397 -0
- package/src/components/editor/manualOffsetDrag.test.ts +140 -0
- package/src/components/editor/manualOffsetDrag.ts +307 -0
- package/src/components/editor/studioMotion.test.ts +355 -0
- package/src/components/editor/studioMotion.ts +632 -0
- package/src/components/nle/NLELayout.tsx +27 -4
- package/src/components/nle/NLEPreview.tsx +50 -5
- package/src/components/renders/RenderQueue.tsx +13 -62
- package/src/components/renders/useRenderQueue.ts +6 -30
- package/src/components/sidebar/AssetsTab.tsx +3 -4
- package/src/components/sidebar/CompositionsTab.test.ts +16 -1
- package/src/components/sidebar/CompositionsTab.tsx +117 -45
- package/src/components/sidebar/LeftSidebar.tsx +140 -125
- package/src/hooks/usePersistentEditHistory.test.ts +256 -0
- package/src/hooks/usePersistentEditHistory.ts +337 -0
- package/src/icons/SystemIcons.tsx +2 -0
- package/src/player/components/CompositionThumbnail.test.ts +19 -0
- package/src/player/components/CompositionThumbnail.tsx +50 -13
- package/src/player/components/EditModal.tsx +5 -20
- package/src/player/components/Player.tsx +18 -2
- package/src/player/components/Timeline.test.ts +20 -0
- package/src/player/components/Timeline.tsx +103 -21
- package/src/player/components/TimelineClip.test.ts +92 -0
- package/src/player/components/TimelineClip.tsx +241 -7
- package/src/player/components/timelineEditing.test.ts +16 -3
- package/src/player/components/timelineEditing.ts +10 -3
- package/src/player/hooks/useTimelinePlayer.test.ts +148 -19
- package/src/player/hooks/useTimelinePlayer.ts +287 -16
- package/src/player/store/playerStore.ts +2 -0
- package/src/utils/clipboard.test.ts +89 -0
- package/src/utils/clipboard.ts +57 -0
- package/src/utils/editHistory.test.ts +244 -0
- package/src/utils/editHistory.ts +218 -0
- package/src/utils/editHistoryStorage.test.ts +37 -0
- package/src/utils/editHistoryStorage.ts +99 -0
- package/src/utils/mediaTypes.ts +1 -1
- package/src/utils/sourcePatcher.test.ts +128 -1
- package/src/utils/sourcePatcher.ts +130 -18
- package/src/utils/studioFileHistory.test.ts +156 -0
- package/src/utils/studioFileHistory.ts +61 -0
- package/src/utils/timelineAssetDrop.test.ts +31 -11
- package/src/utils/timelineAssetDrop.ts +22 -2
- package/src/utils/timelineInspector.test.ts +79 -0
- package/src/utils/timelineInspector.ts +116 -0
- package/dist/assets/hyperframes-player-CEnWY28J.js +0 -417
- package/dist/assets/index-04Mp2wOn.css +0 -1
- package/dist/assets/index-960mgQMI.js +0 -93
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import { XIcon, WarningIcon, CheckCircleIcon, CaretRightIcon } from "@phosphor-icons/react";
|
|
3
|
+
import { copyTextToClipboard } from "../utils/clipboard";
|
|
3
4
|
|
|
4
5
|
export interface LintFinding {
|
|
5
6
|
severity: "error" | "warning";
|
|
@@ -30,12 +31,10 @@ export function LintModal({
|
|
|
30
31
|
return line;
|
|
31
32
|
});
|
|
32
33
|
const text = `Fix these HyperFrames lint issues for project "${projectId}":\n\nProject path: ${window.location.href}\n\n${lines.join("\n\n")}`;
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
const copiedText = await copyTextToClipboard(text);
|
|
35
|
+
if (copiedText) {
|
|
35
36
|
setCopied(true);
|
|
36
37
|
setTimeout(() => setCopied(false), 2000);
|
|
37
|
-
} catch {
|
|
38
|
-
// ignore
|
|
39
38
|
}
|
|
40
39
|
};
|
|
41
40
|
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { Window } from "happy-dom";
|
|
3
|
+
import {
|
|
4
|
+
filterNestedDomEditGroupItems,
|
|
5
|
+
focusDomEditOverlayElement,
|
|
6
|
+
hasDomEditRotationChanged,
|
|
7
|
+
resolveDomEditCoordinateScale,
|
|
8
|
+
resolveDomEditGroupOverlayRect,
|
|
9
|
+
resolveDomEditResizeGesture,
|
|
10
|
+
resolveDomEditRotationGesture,
|
|
11
|
+
} from "./DomEditOverlay";
|
|
12
|
+
|
|
13
|
+
describe("focusDomEditOverlayElement", () => {
|
|
14
|
+
it("focuses the canvas overlay without scrolling", () => {
|
|
15
|
+
const calls: Array<FocusOptions | undefined> = [];
|
|
16
|
+
focusDomEditOverlayElement({
|
|
17
|
+
focus: (options?: FocusOptions) => calls.push(options),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
expect(calls).toEqual([{ preventScroll: true }]);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("resolveDomEditCoordinateScale", () => {
|
|
25
|
+
it("uses the top-level preview scale when no source boundary dimensions are available", () => {
|
|
26
|
+
expect(
|
|
27
|
+
resolveDomEditCoordinateScale({
|
|
28
|
+
rootScaleX: 0.5,
|
|
29
|
+
rootScaleY: 0.5,
|
|
30
|
+
}),
|
|
31
|
+
).toEqual({
|
|
32
|
+
scaleX: 0.5,
|
|
33
|
+
scaleY: 0.5,
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("converts source-local pixels through a scaled nested composition host", () => {
|
|
38
|
+
expect(
|
|
39
|
+
resolveDomEditCoordinateScale({
|
|
40
|
+
rootScaleX: 0.5,
|
|
41
|
+
rootScaleY: 0.5,
|
|
42
|
+
sourceRectWidth: 960,
|
|
43
|
+
sourceRectHeight: 540,
|
|
44
|
+
sourceWidth: 1920,
|
|
45
|
+
sourceHeight: 1080,
|
|
46
|
+
}),
|
|
47
|
+
).toEqual({
|
|
48
|
+
scaleX: 0.25,
|
|
49
|
+
scaleY: 0.25,
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("resolveDomEditGroupOverlayRect", () => {
|
|
55
|
+
it("returns a bounding box that contains every selected element", () => {
|
|
56
|
+
expect(
|
|
57
|
+
resolveDomEditGroupOverlayRect([
|
|
58
|
+
{ left: 40, top: 30, width: 80, height: 50, editScaleX: 1, editScaleY: 1 },
|
|
59
|
+
{ left: 150, top: 10, width: 30, height: 120, editScaleX: 0.5, editScaleY: 0.5 },
|
|
60
|
+
{ left: 20, top: 90, width: 50, height: 20, editScaleX: 2, editScaleY: 2 },
|
|
61
|
+
]),
|
|
62
|
+
).toEqual({
|
|
63
|
+
left: 20,
|
|
64
|
+
top: 10,
|
|
65
|
+
width: 160,
|
|
66
|
+
height: 120,
|
|
67
|
+
editScaleX: 1,
|
|
68
|
+
editScaleY: 1,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("returns null for an empty group", () => {
|
|
73
|
+
expect(resolveDomEditGroupOverlayRect([])).toBeNull();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("filterNestedDomEditGroupItems", () => {
|
|
78
|
+
it("keeps top-level selected elements so descendants are not moved twice", () => {
|
|
79
|
+
const window = new Window();
|
|
80
|
+
const parent = window.document.createElement("div");
|
|
81
|
+
const child = window.document.createElement("div");
|
|
82
|
+
const sibling = window.document.createElement("div");
|
|
83
|
+
parent.append(child);
|
|
84
|
+
|
|
85
|
+
expect(
|
|
86
|
+
filterNestedDomEditGroupItems([
|
|
87
|
+
{ key: "parent", element: parent },
|
|
88
|
+
{ key: "child", element: child },
|
|
89
|
+
{ key: "sibling", element: sibling },
|
|
90
|
+
]).map((item) => item.key),
|
|
91
|
+
).toEqual(["parent", "sibling"]);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("resolveDomEditResizeGesture", () => {
|
|
96
|
+
it("resizes width and height independently by default", () => {
|
|
97
|
+
expect(
|
|
98
|
+
resolveDomEditResizeGesture({
|
|
99
|
+
originWidth: 240,
|
|
100
|
+
originHeight: 120,
|
|
101
|
+
actualWidth: 240,
|
|
102
|
+
actualHeight: 120,
|
|
103
|
+
scaleX: 1,
|
|
104
|
+
scaleY: 1,
|
|
105
|
+
dx: 30,
|
|
106
|
+
dy: 12,
|
|
107
|
+
uniform: false,
|
|
108
|
+
}),
|
|
109
|
+
).toEqual({
|
|
110
|
+
overlayWidth: 270,
|
|
111
|
+
overlayHeight: 132,
|
|
112
|
+
width: 270,
|
|
113
|
+
height: 132,
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("snaps width and height to the same value when Shift is held", () => {
|
|
118
|
+
expect(
|
|
119
|
+
resolveDomEditResizeGesture({
|
|
120
|
+
originWidth: 240,
|
|
121
|
+
originHeight: 120,
|
|
122
|
+
actualWidth: 240,
|
|
123
|
+
actualHeight: 120,
|
|
124
|
+
scaleX: 1,
|
|
125
|
+
scaleY: 1,
|
|
126
|
+
dx: 30,
|
|
127
|
+
dy: 12,
|
|
128
|
+
uniform: true,
|
|
129
|
+
}),
|
|
130
|
+
).toEqual({
|
|
131
|
+
overlayWidth: 270,
|
|
132
|
+
overlayHeight: 270,
|
|
133
|
+
width: 270,
|
|
134
|
+
height: 270,
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("uses the dominant pointer delta for uniform shrink", () => {
|
|
139
|
+
expect(
|
|
140
|
+
resolveDomEditResizeGesture({
|
|
141
|
+
originWidth: 300,
|
|
142
|
+
originHeight: 180,
|
|
143
|
+
actualWidth: 300,
|
|
144
|
+
actualHeight: 180,
|
|
145
|
+
scaleX: 1,
|
|
146
|
+
scaleY: 1,
|
|
147
|
+
dx: 8,
|
|
148
|
+
dy: -40,
|
|
149
|
+
uniform: true,
|
|
150
|
+
}),
|
|
151
|
+
).toMatchObject({
|
|
152
|
+
width: 260,
|
|
153
|
+
height: 260,
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("writes source-local dimensions when the edited source is scaled down in master view", () => {
|
|
158
|
+
expect(
|
|
159
|
+
resolveDomEditResizeGesture({
|
|
160
|
+
originWidth: 100,
|
|
161
|
+
originHeight: 50,
|
|
162
|
+
actualWidth: 400,
|
|
163
|
+
actualHeight: 200,
|
|
164
|
+
scaleX: 0.25,
|
|
165
|
+
scaleY: 0.25,
|
|
166
|
+
dx: 25,
|
|
167
|
+
dy: 10,
|
|
168
|
+
uniform: false,
|
|
169
|
+
}),
|
|
170
|
+
).toEqual({
|
|
171
|
+
overlayWidth: 125,
|
|
172
|
+
overlayHeight: 60,
|
|
173
|
+
width: 500,
|
|
174
|
+
height: 240,
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe("resolveDomEditRotationGesture", () => {
|
|
180
|
+
it("rotates by the pointer angle around the element center", () => {
|
|
181
|
+
expect(
|
|
182
|
+
resolveDomEditRotationGesture({
|
|
183
|
+
centerX: 0,
|
|
184
|
+
centerY: 0,
|
|
185
|
+
startX: 0,
|
|
186
|
+
startY: -10,
|
|
187
|
+
currentX: 10,
|
|
188
|
+
currentY: 0,
|
|
189
|
+
actualAngle: 5,
|
|
190
|
+
snap: false,
|
|
191
|
+
}),
|
|
192
|
+
).toEqual({ angle: 95 });
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("uses the shortest delta across the 180 degree boundary", () => {
|
|
196
|
+
expect(
|
|
197
|
+
resolveDomEditRotationGesture({
|
|
198
|
+
centerX: 0,
|
|
199
|
+
centerY: 0,
|
|
200
|
+
startX: -10,
|
|
201
|
+
startY: 1.76,
|
|
202
|
+
currentX: -10,
|
|
203
|
+
currentY: -1.76,
|
|
204
|
+
actualAngle: 0,
|
|
205
|
+
snap: false,
|
|
206
|
+
}).angle,
|
|
207
|
+
).toBeCloseTo(20, 1);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("snaps to 15 degree increments when requested", () => {
|
|
211
|
+
expect(
|
|
212
|
+
resolveDomEditRotationGesture({
|
|
213
|
+
centerX: 0,
|
|
214
|
+
centerY: 0,
|
|
215
|
+
startX: 10,
|
|
216
|
+
startY: 0,
|
|
217
|
+
currentX: 10,
|
|
218
|
+
currentY: 3.25,
|
|
219
|
+
actualAngle: 0,
|
|
220
|
+
snap: true,
|
|
221
|
+
}),
|
|
222
|
+
).toEqual({ angle: 15 });
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("allows small pointer movements when the rounded angle changes", () => {
|
|
226
|
+
const nextRotation = resolveDomEditRotationGesture({
|
|
227
|
+
centerX: 0,
|
|
228
|
+
centerY: 0,
|
|
229
|
+
startX: 0,
|
|
230
|
+
startY: -40,
|
|
231
|
+
currentX: 1,
|
|
232
|
+
currentY: -40,
|
|
233
|
+
actualAngle: 0,
|
|
234
|
+
snap: false,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
expect(nextRotation.angle).toBe(1.4);
|
|
238
|
+
expect(hasDomEditRotationChanged(0, nextRotation.angle)).toBe(true);
|
|
239
|
+
expect(hasDomEditRotationChanged(0, 0)).toBe(false);
|
|
240
|
+
});
|
|
241
|
+
});
|