Desktop is the panel's width, and the guides are the only marking

Two things, both asked for after looking at it.

The tint came out. Shading the margins was meant to say "the page does not use this", and against a
white page it read as a blue background — a colour the site does not have, on the screen that exists to
show the site's colours. The two hairlines say the same thing without claiming anything.

And Desktop is fluid. `width: null` means the frame takes whatever the panel gives it, which is the
honest answer for a device that has no width: a desktop is whatever window somebody opened. 1440 was a
guess that then had to be centred and scrolled inside the panel — a page rendered at one width and
looked at inside another, with dark room either side of it.

Tablet and mobile stay pinned, because 834 and 390 are claims about real devices and pinning them is
the only way the preview can answer "what does this look like on a phone".

Fluid, the frame IS a desktop window: the site's media queries answer the panel's real width, and the
measure guides mark where the content stops in the pixels you are actually looking at.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-01 03:24:23 +01:00
co-authored by Claude Opus 5
parent d185382d84
commit cee2f5c8e2
3 changed files with 32 additions and 39 deletions
+17 -6
View File
@@ -102,7 +102,13 @@ export const SitePreview = ({
clientRef.current = client;
const { device } = useViewMode();
const { width } = DEVICES[device];
/*
`null` means fluid — Desktop takes the panel's width. See DEVICES.
Everything below reads `frame`: the real number once the panel has been measured. Until then it is
the panel's own width, which is what a fluid frame is anyway.
*/
const { width: deviceWidth } = DEVICES[device];
const [ready, setReady] = useState(false);
const [failed, setFailed] = useState<string | null>(null);
@@ -123,6 +129,9 @@ export const SitePreview = ({
const box = useRef<HTMLDivElement | null>(null);
const [available, setAvailable] = useState({ width: 0, height: 0 });
// The width the frame is actually rendering at, for the caption and the centring maths.
const frameWidth = deviceWidth ?? Math.round(available.width);
useEffect(() => {
const element = box.current;
if (!element) return;
@@ -250,8 +259,8 @@ export const SitePreview = ({
useEffect(() => {
const element = scroller.current;
if (!element) return;
element.scrollLeft = Math.max(0, (width - element.clientWidth) / 2);
}, [width, available.width, loaded]);
element.scrollLeft = Math.max(0, (frameWidth - element.clientWidth) / 2);
}, [frameWidth, available.width, loaded]);
return (
<section className="flex min-h-0 flex-1 flex-col gap-2">
@@ -273,7 +282,9 @@ export const SitePreview = ({
the platforms where scrollbars are always classic.
*/
className="mx-auto overflow-auto rounded-lg border border-border bg-white [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
style={{ width: Math.min(width, available.width || width), height: shownHeight }}
// Fluid fills the panel; a fixed device is capped by it so a 1440 frame in a 900 panel
// scrolls rather than overflowing the workspace.
style={{ width: deviceWidth === null ? '100%' : Math.min(frameWidth, available.width || frameWidth), height: shownHeight }}
>
{message ? (
<p className="p-6 text-center text-xs text-muted-foreground">{message}</p>
@@ -299,7 +310,7 @@ export const SitePreview = ({
title={`The site at /${path}`}
className="border-0"
// The device's real width, and the room's real height. No transform: see the note above.
style={{ width, height: shownHeight }}
style={{ width: deviceWidth === null ? '100%' : frameWidth, height: shownHeight }}
/>
)}
</div>
@@ -308,7 +319,7 @@ export const SitePreview = ({
{/* Always, now that editing is the only mode. It says which server this is and at what width,
which the device buttons show but the SERVER half of it — dev, not production — they do not. */}
<p className="shrink-0 text-[10px] text-muted-foreground">
The development server, live at {width}px. Save to see a change here.
The development server, live at {frameWidth}px. Save to see a change here.
</p>
</section>
);