one device control, read from the URL

Spacing's settings are per width, so the section has to say which width it is editing — and the answer
must be the one the preview is already showing, not a second selector able to disagree with it.

It reads `?device=` rather than the ViewMode context, and that is not a preference. The editor is a
SIBLING panel: an element built inside the detail panel resolves context where it is RENDERED, so a
toggle handed over would look up a tree with no provider in it and quietly show the default forever.

The URL is the shared state, which means there is nothing to share. Three components read one
parameter, so they cannot disagree, and the preview follows without being subscribed to anything — it
reads the same value. More editor sections can take the control by rendering it.

The bar's own toggle is now the same component. It had its own copy of the options and the handler,
which is exactly how two controls for one thing start to differ.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-01 03:53:02 +01:00
co-authored by Claude Opus 5
parent 53d6a0324d
commit 852c53dd09
3 changed files with 99 additions and 29 deletions
+16
View File
@@ -8,6 +8,7 @@ import { useSaveTarget } from '../SaveTarget';
import { usePreviewable, useViewMode } from '../ViewMode';
import { applyGutter, paintMeasure } from '../canvas';
import { SitePreview, useDevPort } from '../SitePreview';
import { DeviceToggle } from '../DeviceToggle';
import { useEditorSlot } from '../useEditorSlot';
import { type LayoutBand } from './PaddingTab';
import { GutterTab, type Gutter } from './GutterTab';
@@ -359,6 +360,21 @@ export const LayoutPanel = () => {
<AccordionItem value="padding" className="rounded-lg border border-border px-4">
<AccordionTrigger className="text-xs">Spacing</AccordionTrigger>
<AccordionContent>
{/*
The view this section's settings are FOR, at the top of it.
Spacing is per width, so the section has to say which width it is editing — and the answer
is the same one the preview is showing, not a second selector that could disagree with it.
The control reads `?device=`, so pressing it here moves the preview, the bar's toggle and
this section together because all three are reading one value.
At the top rather than beside the fields: it scopes everything below it, and a control that
scopes a group belongs above the group.
*/}
<div className="mb-3 flex items-center gap-2">
<span className="text-xs text-muted-foreground">Editing</span>
<DeviceToggle size="section" />
</div>
{/*
The site's gutter, not a per-band padding.