From 2a2a88ce1f5d19c7081b407d473e532c0fbf2614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Thu, 14 May 2026 05:05:49 +0200 Subject: [PATCH] fix(studio): center vertical composition thumbnails in sidebar (#820) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(studio): add pasteboard background to preview viewport Adds bg-neutral-800 to the preview viewport so the area outside the canvas is visually distinct from the composition content — consistent with professional video editors (Premiere, DaVinci, Figma). * feat(studio): pasteboard background and canvas outline around preview - NLEPreview: viewport gets bg-neutral-700 (#404040) as the pasteboard color surrounding the canvas — distinct from the app chrome (#0a0a0a) - Player wrapper: drop bg-black so the pasteboard shows around the canvas (loading overlays still cover the area with bg-black during load) - Player: set host background to transparent via inline style (overrides :host { background: #000 } in shadow DOM), and inject a style rule into the open shadow root so .hfp-container has overflow:visible and the canvas iframe gets a thin white ring + soft drop-shadow — making the canvas boundary legible against the pasteboard * fix(studio): center vertical composition thumbnails in sidebar Portrait (and other non-16:9) compositions were pinning to the top-left of the 80x45 thumbnail slot because transform-origin was '0 0'. Compute the centering offsets from the scaled dimensions and apply them as left/top so any aspect ratio renders centred in the slot. --- .../src/components/sidebar/CompositionsTab.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/studio/src/components/sidebar/CompositionsTab.tsx b/packages/studio/src/components/sidebar/CompositionsTab.tsx index c5a5e5e27..5e1639894 100644 --- a/packages/studio/src/components/sidebar/CompositionsTab.tsx +++ b/packages/studio/src/components/sidebar/CompositionsTab.tsx @@ -8,6 +8,8 @@ interface CompositionsTabProps { } const DEFAULT_PREVIEW_STAGE = { width: 1920, height: 1080 }; +const CARD_W = 80; +const CARD_H = 45; const THUMBNAIL_SEEK_TIME_SECONDS = 3; const THUMBNAIL_PLAYBACK_SYNC_ATTEMPTS = 10; @@ -132,11 +134,13 @@ function CompCard({ const name = comp.replace(/^compositions\//, "").replace(/\.html$/, ""); const previewUrl = `/api/projects/${projectId}/preview/comp/${comp}`; const previewScale = resolveCompositionPreviewScale({ - cardWidth: 80, - cardHeight: 45, + cardWidth: CARD_W, + cardHeight: CARD_H, stageWidth: stageSize.width, stageHeight: stageSize.height, }); + const thumbnailOffsetX = (CARD_W - stageSize.width * previewScale) / 2; + const thumbnailOffsetY = (CARD_H - stageSize.height * previewScale) / 2; useEffect(() => { requestIframePlaybackSync(hovered); @@ -166,11 +170,13 @@ function CompCard({ src={previewUrl} sandbox="allow-scripts allow-same-origin" loading="lazy" - className="absolute left-0 top-0 border-none pointer-events-none" + className="absolute border-none pointer-events-none" style={{ transformOrigin: "0 0", width: stageSize.width, height: stageSize.height, + left: thumbnailOffsetX, + top: thumbnailOffsetY, transform: `scale(${previewScale})`, }} onLoad={(e) => {