@hyperframes/studio 0.8.71 → 0.8.72
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-DuN39PN6.js → hyperframes-player-jTC1EDAI.js} +1 -1
- package/dist/assets/{index-CUcdruHw.js → index-BqUGSWqN.js} +164 -164
- package/dist/assets/{index-BoQsbDgf.js → index-DpVD1igA.js} +1 -1
- package/dist/assets/{index-BmCEvqsr.js → index-HuYqM2Fi.js} +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.html +1 -1
- package/dist/index.js +81 -65
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/components/nle/NLEPreview.test.ts +19 -1
- package/src/components/nle/NLEPreview.tsx +22 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/studio",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.72",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -52,11 +52,11 @@
|
|
|
52
52
|
"dockview-react": "8.3.1",
|
|
53
53
|
"gsap": "^3.13.0",
|
|
54
54
|
"mediabunny": "^1.45.3",
|
|
55
|
-
"@hyperframes/core": "0.8.
|
|
56
|
-
"@hyperframes/parsers": "0.8.
|
|
57
|
-
"@hyperframes/player": "0.8.
|
|
58
|
-
"@hyperframes/studio-server": "0.8.
|
|
59
|
-
"@hyperframes/sdk": "0.8.
|
|
55
|
+
"@hyperframes/core": "0.8.72",
|
|
56
|
+
"@hyperframes/parsers": "0.8.72",
|
|
57
|
+
"@hyperframes/player": "0.8.72",
|
|
58
|
+
"@hyperframes/studio-server": "0.8.72",
|
|
59
|
+
"@hyperframes/sdk": "0.8.72"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@tailwindcss/vite": "^4.3.3",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"vite": "^6.4.2",
|
|
73
73
|
"vitest": "^4.1.11",
|
|
74
74
|
"zustand": "^5.0.0",
|
|
75
|
-
"@hyperframes/producer": "0.8.
|
|
75
|
+
"@hyperframes/producer": "0.8.72"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
78
|
"react": "19",
|
|
@@ -80,6 +80,10 @@ function renderPreview(
|
|
|
80
80
|
previewSlots: Array<{ gen: number; role: "live" | "shadow"; url?: string }> = [
|
|
81
81
|
{ gen: 0, role: "live" },
|
|
82
82
|
],
|
|
83
|
+
{
|
|
84
|
+
box = { width: 800, height: 600 },
|
|
85
|
+
fillBox,
|
|
86
|
+
}: { box?: { width: number; height: number }; fillBox?: boolean } = {},
|
|
83
87
|
) {
|
|
84
88
|
resizeCallbacks = [];
|
|
85
89
|
const host = document.createElement("div");
|
|
@@ -99,6 +103,7 @@ function renderPreview(
|
|
|
99
103
|
onShadowError: () => {},
|
|
100
104
|
setShadowIframeNode: () => {},
|
|
101
105
|
resetPreviewSlots: () => {},
|
|
106
|
+
fillBox,
|
|
102
107
|
}),
|
|
103
108
|
);
|
|
104
109
|
});
|
|
@@ -108,7 +113,7 @@ function renderPreview(
|
|
|
108
113
|
expect(viewport).toBeTruthy();
|
|
109
114
|
expect(stage).toBeTruthy();
|
|
110
115
|
|
|
111
|
-
setRect(viewport,
|
|
116
|
+
setRect(viewport, box);
|
|
112
117
|
act(() => {
|
|
113
118
|
for (const fire of resizeCallbacks) fire();
|
|
114
119
|
});
|
|
@@ -251,6 +256,19 @@ describe("NLEPreview", () => {
|
|
|
251
256
|
view.cleanup();
|
|
252
257
|
});
|
|
253
258
|
|
|
259
|
+
it("insets the picture by default and fills a same-shape box when fillBox is on", () => {
|
|
260
|
+
const box = { width: 640, height: 360 };
|
|
261
|
+
const inset = renderPreview(undefined, { box });
|
|
262
|
+
expect([inset.stage.style.width, inset.stage.style.height]).toEqual(["611.5556px", "344px"]);
|
|
263
|
+
expect(parseFloat(inset.stage.parentElement!.style.inset)).toBe(8);
|
|
264
|
+
inset.cleanup();
|
|
265
|
+
|
|
266
|
+
const filled = renderPreview(undefined, { box, fillBox: true });
|
|
267
|
+
expect([filled.stage.style.width, filled.stage.style.height]).toEqual(["640px", "360px"]);
|
|
268
|
+
expect(parseFloat(filled.stage.parentElement!.style.inset)).toBe(0);
|
|
269
|
+
filled.cleanup();
|
|
270
|
+
});
|
|
271
|
+
|
|
254
272
|
it("clips a shadow reload so its own loading overlay cannot paint over the live frame", () => {
|
|
255
273
|
const view = renderPreview([
|
|
256
274
|
{ gen: 0, role: "live" },
|
|
@@ -36,6 +36,8 @@ interface NLEPreviewProps {
|
|
|
36
36
|
onStageRef?: (ref: React.RefObject<HTMLDivElement | null>) => void;
|
|
37
37
|
/** Reports the authored composition size measured from the loaded preview. */
|
|
38
38
|
onCompositionSizeChange?: (size: PreviewCompositionSize | null) => void;
|
|
39
|
+
/** Draws the picture edge to edge in this box, without Studio's inset band. */
|
|
40
|
+
fillBox?: boolean;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
export function getPreviewPlayerKey({
|
|
@@ -50,7 +52,7 @@ export function getPreviewPlayerKey({
|
|
|
50
52
|
|
|
51
53
|
const ZOOM_HUD_TIMEOUT_MS = 1200;
|
|
52
54
|
const ZOOM_SETTLE_MS = 200;
|
|
53
|
-
const PREVIEW_STAGE_INSET_PX =
|
|
55
|
+
const PREVIEW_STAGE_INSET_PX = 8;
|
|
54
56
|
|
|
55
57
|
// clip-path as well as visibility: the player's loading overlay sets its own
|
|
56
58
|
// visibility:visible and would otherwise paint over the live frame.
|
|
@@ -87,9 +89,10 @@ export function resolvePreviewStageSize(
|
|
|
87
89
|
compositionSize: PreviewCompositionSize | null,
|
|
88
90
|
portrait: boolean | undefined,
|
|
89
91
|
gutterPx = 0,
|
|
92
|
+
insetPx = PREVIEW_STAGE_INSET_PX,
|
|
90
93
|
): { width: number; height: number } {
|
|
91
|
-
const availableWidth = Math.max(0, viewportWidth -
|
|
92
|
-
const availableHeight = Math.max(0, viewportHeight -
|
|
94
|
+
const availableWidth = Math.max(0, viewportWidth - 2 * (insetPx + gutterPx));
|
|
95
|
+
const availableHeight = Math.max(0, viewportHeight - 2 * (insetPx + gutterPx));
|
|
93
96
|
const aspectRatio =
|
|
94
97
|
compositionSize && compositionSize.width > 0 && compositionSize.height > 0
|
|
95
98
|
? compositionSize.width / compositionSize.height
|
|
@@ -130,6 +133,7 @@ export const NLEPreview = memo(function NLEPreview({
|
|
|
130
133
|
suppressLoadingOverlay,
|
|
131
134
|
onStageRef,
|
|
132
135
|
onCompositionSizeChange,
|
|
136
|
+
fillBox,
|
|
133
137
|
}: NLEPreviewProps) {
|
|
134
138
|
const activeKey = getPreviewPlayerKey({ projectId, directUrl });
|
|
135
139
|
const viewportRef = useRef<HTMLDivElement>(null);
|
|
@@ -151,6 +155,7 @@ export const NLEPreview = memo(function NLEPreview({
|
|
|
151
155
|
const reportPreviewFirstFrame = usePreviewFirstFrameTelemetry(previewSlots);
|
|
152
156
|
const [compositionSize, setCompositionSize] = useState<PreviewCompositionSize | null>(null);
|
|
153
157
|
const gutterPx = usePreviewGuidesStore((s) => (s.rulerVisible ? RULER_GUTTER_PX : 0));
|
|
158
|
+
const insetPx = fillBox ? 0 : PREVIEW_STAGE_INSET_PX;
|
|
154
159
|
const [stageSize, setStageSize] = useState(() => resolvePreviewStageSize(0, 0, null, portrait));
|
|
155
160
|
|
|
156
161
|
const zoomRef = useRef<PreviewZoomState>(loadInitialZoom());
|
|
@@ -181,7 +186,14 @@ export const NLEPreview = memo(function NLEPreview({
|
|
|
181
186
|
const updateStageSize = () => {
|
|
182
187
|
const rect = viewport.getBoundingClientRect();
|
|
183
188
|
setStageSize(
|
|
184
|
-
resolvePreviewStageSize(
|
|
189
|
+
resolvePreviewStageSize(
|
|
190
|
+
rect.width,
|
|
191
|
+
rect.height,
|
|
192
|
+
compositionSize,
|
|
193
|
+
portrait,
|
|
194
|
+
gutterPx,
|
|
195
|
+
insetPx,
|
|
196
|
+
),
|
|
185
197
|
);
|
|
186
198
|
};
|
|
187
199
|
|
|
@@ -189,7 +201,7 @@ export const NLEPreview = memo(function NLEPreview({
|
|
|
189
201
|
const observer = new ResizeObserver(updateStageSize);
|
|
190
202
|
observer.observe(viewport);
|
|
191
203
|
return () => observer.disconnect();
|
|
192
|
-
}, [compositionSize, portrait, gutterPx]);
|
|
204
|
+
}, [compositionSize, portrait, gutterPx, insetPx]);
|
|
193
205
|
|
|
194
206
|
const onCompositionSizeChangeRef = useRef(onCompositionSizeChange);
|
|
195
207
|
onCompositionSizeChangeRef.current = onCompositionSizeChange;
|
|
@@ -467,11 +479,14 @@ export const NLEPreview = memo(function NLEPreview({
|
|
|
467
479
|
<div className="flex flex-col h-full min-h-0">
|
|
468
480
|
<div
|
|
469
481
|
ref={viewportRef}
|
|
470
|
-
className="relative flex-1 flex items-center justify-center
|
|
482
|
+
className="relative flex-1 flex items-center justify-center overflow-hidden min-h-0 outline-hidden focus:ring-1 focus:ring-studio-accent/40 bg-[var(--studio-preview-bg,var(--color-neutral-950))]"
|
|
471
483
|
tabIndex={0}
|
|
472
484
|
aria-label="Composition preview"
|
|
473
485
|
>
|
|
474
|
-
<div
|
|
486
|
+
<div
|
|
487
|
+
className="absolute flex items-center justify-center pointer-events-none"
|
|
488
|
+
style={{ inset: insetPx }}
|
|
489
|
+
>
|
|
475
490
|
<div
|
|
476
491
|
ref={stageRef}
|
|
477
492
|
className="relative shrink-0 pointer-events-auto"
|