Skip to content

Commit 329e237

Browse files
EtienneLescotclaude
andcommitted
fix(ai-edition): make preview canvas actually contain-fit the selected aspect ratio
The aspect-ratio setting was already wired to the document, but the previewFrame's CSS (`aspect-ratio` + `width: 100%` + `max-height: 100%`) only clamps height when the box overflows -- it never shrinks width back down to match, so portrait ratios like 9:16 silently kept the frame full-width instead of resizing. Compute the contain-fit box in JS instead, measuring the frame's parent and applying explicit pixel width/height. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent d49b2e4 commit 329e237

1 file changed

Lines changed: 40 additions & 1 deletion

File tree

‎src/components/ai-edition/PreviewCanvas.tsx‎

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import {
4343
type WebcamCompositeLayout,
4444
} from "@/lib/compositeLayout";
4545
import { getCssClipPath } from "@/lib/webcamMaskShapes";
46+
import { getAspectRatioValue } from "@/utils/aspectRatioUtils";
4647
import { AnnotationLayer } from "./AnnotationLayer";
4748
import styles from "./NewEditorShell.module.css";
4849
import { type VideoSource, VirtualPreview } from "./VirtualPreview";
@@ -88,6 +89,28 @@ export function PreviewCanvas(props: PreviewCanvasProps) {
8889
const frameRef = useRef<HTMLDivElement | null>(null);
8990
const webcamSlotRef = useRef<HTMLDivElement | null>(null);
9091
const [canvasSize, setCanvasSize] = useState({ width: 1280, height: 720 });
92+
// ponytail: contain-fit the frame within its wrapper ourselves. CSS
93+
// `aspect-ratio` + `width: 100%` + `max-height: 100%` only clamps height —
94+
// it never shrinks width back down to match, so portrait ratios (9:16 etc)
95+
// silently overflowed/stretched instead of fitting. Measuring the parent
96+
// (not the frame, which we're about to size) lets us compute an explicit
97+
// pixel box that actually respects the ratio on both axes.
98+
const [containerSize, setContainerSize] = useState({ width: 1280, height: 720 });
99+
100+
useEffect(() => {
101+
const el = frameRef.current?.parentElement;
102+
if (!el) return;
103+
const update = () =>
104+
setContainerSize({
105+
width: el.clientWidth || 1280,
106+
height: el.clientHeight || 720,
107+
});
108+
update();
109+
if (typeof ResizeObserver === "undefined") return;
110+
const observer = new ResizeObserver(update);
111+
observer.observe(el);
112+
return () => observer.disconnect();
113+
}, []);
91114

92115
useEffect(() => {
93116
const el = frameRef.current;
@@ -104,6 +127,18 @@ export function PreviewCanvas(props: PreviewCanvasProps) {
104127
return () => observer.disconnect();
105128
}, []);
106129

130+
const frameSize = useMemo(() => {
131+
const ratio = getAspectRatioValue(settings.aspectRatio);
132+
const { width: containerWidth, height: containerHeight } = containerSize;
133+
if (containerWidth <= 0 || containerHeight <= 0) return { width: containerWidth, height: containerHeight };
134+
if (containerWidth / containerHeight > ratio) {
135+
const height = containerHeight;
136+
return { width: Math.round(height * ratio), height: Math.round(height) };
137+
}
138+
const width = containerWidth;
139+
return { width: Math.round(width), height: Math.round(width / ratio) };
140+
}, [containerSize, settings.aspectRatio]);
141+
107142
const layout = useMemo(() => {
108143
const preset = settings.webcamLayoutPreset as WebcamLayoutPreset;
109144
const mask = settings.webcamMaskShape as WebcamMaskShape;
@@ -204,7 +239,11 @@ export function PreviewCanvas(props: PreviewCanvasProps) {
204239
: null;
205240

206241
return (
207-
<div ref={frameRef} className={styles.previewFrame} style={frameStyle}>
242+
<div
243+
ref={frameRef}
244+
className={styles.previewFrame}
245+
style={{ ...frameStyle, width: frameSize.width, height: frameSize.height }}
246+
>
208247
<div className={styles.bgBlur} style={blurStyle} aria-hidden />
209248
{layout?.screenRect ? (
210249
<div className={styles.screenStage} style={screenStyle}>

0 commit comments

Comments
 (0)