Simulator
The simulator (simulator/main.tsx) renders the entire wall — controller + every screen — inside one browser window using iframes.
Auto-fit wall
Section titled “Auto-fit wall”The wall is auto-fit to the viewport, preserving aspect ratio:
const wallAspect = settings.wall.width / settings.wall.heightconst availAspect = availW / availHconst wallPx = availAspect > wallAspect ? { w: availH * wallAspect, h: availH } // viewport wider — fit by height : { w: availW, h: availW / wallAspect } // taller — fit by width
const ppcm = wallPx.w / settings.wall.widthppcm (pixels per centimetre) drives every iframe’s size: screen.size.width * ppcm.
Minimum browser size
Section titled “Minimum browser size”settings.simulator.minWidth / minHeight gate the simulator. Below that, a notice is shown with current vs required dimensions.
Iframes
Section titled “Iframes”Each iframe is wrapped in a DraggableScreen component that:
- Positions itself absolutely on the wall using
top: ${pos.top}%,left: ${pos.left}%. - Sizes itself in pixels:
width = cm × ppcm. - In edit mode, overlays a transparent div on top of the iframe to capture pointer events for dragging.
Edit mode
Section titled “Edit mode”Toggle with the Edit button. While unlocked:
- Iframes get a dashed blue outline.
- Pointer over iframe →
cursor-grab. Drag →cursor-grabbing. - A small live-position badge shows
top% · left%above the dragged iframe. - Pointer up commits the new position to
setSettings(...).
Drag math:
const dxPct = (dx / wallPx.w) * 100const dyPct = (dy / wallPx.h) * 100Positions are clamped to [0, 100] so iframes can’t drift off the wall.
Settings panel
Section titled “Settings panel”Click Settings in the floating controls to open the slide-in panel. See Settings for the data model.