From a79dc2b1afcd747b243148c53b9f75015c6a10ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Mon, 31 Aug 2026 04:28:51 +0100 Subject: [PATCH] the page builder gets Edit/View and the three widths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The same bar Layout has, from the same opt-in: the section says it has a preview and the controls appear. Pages — the list — still says nothing, because there is nothing there to view at tablet width. VIEW paints nothing at all. Not dimmed chrome or hidden buttons: no injection, so the frame holds exactly the document the site served. That is what View has to mean here — the outlines, the labels and the drop zone are the editor's, and a preview that keeps any of them is not showing you the page. Done by putting `mode` in the callback's dependencies rather than by branching on it somewhere else. Switching hands SitePreview a new function; its effect runs the previous paint's cleanup and then does nothing. The chrome goes without a reload and without a second code path that could disagree with the first. Co-Authored-By: Claude Opus 5 (1M context) --- web/sections/PageBuilderPanel.tsx | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/web/sections/PageBuilderPanel.tsx b/web/sections/PageBuilderPanel.tsx index a90b8b6..d4601aa 100644 --- a/web/sections/PageBuilderPanel.tsx +++ b/web/sections/PageBuilderPanel.tsx @@ -3,6 +3,7 @@ import { useSearchParams } from 'react-router'; import { useClient } from 'hooks/useClient'; import { readableError } from '../errors'; import { SitePreview, useDevPort } from '../SitePreview'; +import { usePreviewable, useViewMode } from '../ViewMode'; import { paintCanvas, type CanvasSection } from '../canvas'; import { AddSectionDialog, type SectionLayout } from './AddSectionDialog'; import { usePages, type Page } from '../usePages'; @@ -33,6 +34,11 @@ export const PageBuilderPanel = () => { const devPort = useDevPort(); const { pages, announce } = usePages(); + // This section has a preview, so the bar offers Edit/View and the three widths — the same opt-in + // Layout makes. Pages, the list, does not: there is nothing there to view at tablet width. + usePreviewable(); + const { mode } = useViewMode(); + const [error, setError] = useState(null); const [sections, setSections] = useState([]); @@ -62,14 +68,29 @@ export const PageBuilderPanel = () => { Stable, because SitePreview holds it and re-runs it on every load. A new function each render would be a new injection each render. */ + /* + In VIEW, nothing is painted at all. + + Not the chrome dimmed or the buttons hidden — no injection, so the frame holds exactly the document + the site served. That is what View has to mean here: outlines, labels and a drop zone are the + editor's, and a preview that keeps any of them is not showing you the page. + + The check is INSIDE the callback and `mode` is a dependency, so switching to View hands SitePreview + a new function: its effect runs the previous paint's cleanup and then paints nothing. That is what + takes the chrome away, with no reload and no second code path. + */ const onDocument = useCallback( - (frameDocument: Document) => - paintCanvas(frameDocument, { + (frameDocument: Document) => { + if (mode === 'view') return; + return paintCanvas(frameDocument, { sections, onAddSection: () => handlersRef.current.add(), onDeleteSection: (section) => handlersRef.current.remove(section.id), - }), - [sections], + }); + }, + // `mode` is in here so switching to View gives SitePreview a NEW function, which runs the previous + // paint's cleanup and then paints nothing. That is what removes the chrome without a reload. + [sections, mode], ); /*