@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/studio",
3
- "version": "0.8.71",
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.71",
56
- "@hyperframes/parsers": "0.8.71",
57
- "@hyperframes/player": "0.8.71",
58
- "@hyperframes/studio-server": "0.8.71",
59
- "@hyperframes/sdk": "0.8.71"
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.71"
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, { width: 800, height: 600 });
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 = 16;
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 - PREVIEW_STAGE_INSET_PX - 2 * gutterPx);
92
- const availableHeight = Math.max(0, viewportHeight - PREVIEW_STAGE_INSET_PX - 2 * gutterPx);
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(rect.width, rect.height, compositionSize, portrait, gutterPx),
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 p-2 overflow-hidden min-h-0 outline-hidden focus:ring-1 focus:ring-studio-accent/40 bg-[var(--studio-preview-bg,var(--color-neutral-950))]"
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 className="absolute inset-2 flex items-center justify-center pointer-events-none">
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"