Embedding AC pieces: framing #
The host owns the preview rectangle. AC fills that rectangle. A 4:3 preview means a width-to-height ratio; it does not imply a palette, bezel, pixel density, or filter.
Set the viewport in the host #
<div class="piece-preview">
<iframe
title="Piece preview"
src="https://aesthetic.computer/notepat?nogap=true&nolabel=true&autoreload=true"
allow="autoplay"
></iframe>
</div>
.piece-preview {
position: relative;
width: 100%;
aspect-ratio: 4 / 3;
overflow: hidden;
}
.piece-preview > iframe {
position: absolute;
inset: 0;
display: block;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
border: 0;
}
The wrapper establishes a height before the iframe fills it. display: block
avoids the baseline space below inline iframes. Remove host padding and borders
when the preview should touch its container edges. In a native app, apply the
same ratio to the WebView's layout bounds.
An iframe is a separate viewport: style its element in the host, and configure
the AC document with its URL. Host CSS cannot reach into a cross-origin frame.
object-fit on an iframe does not resize the child canvas. Piece code should
compose against screen.width and screen.height, not the surrounding app.
Configure the runtime #
| Parameter | Effect | Use |
|---|---|---|
nogap=true |
Sets runtime gap to zero; canvas wrapper fills its viewport and loses rounded corners/checkerboard background. | Edge-to-edge previews. |
nolabel=true |
Suppresses the runtime corner label/HUD. | Host supplies its own controls. |
autoreload=true |
Takes available piece updates automatically instead of showing the update badge. | Live authoring previews. |
noauth=true |
Skips the embedded runtime's authentication startup. | Draft previews whose host handles account access; omit when the embedded piece needs authentication. |
preview=true |
Selects the piece's preview() lifecycle behavior when present. |
Intentional preview rendering, not an arbitrary preview identifier. |
icon=true |
Selects icon rendering behavior. | Intentional icon generation. |
density=… |
Changes the runtime's rendering density. | Deliberate resolution tuning, independently of aspect ratio. |
These flags are read by presence in several runtime paths. Remove a flag to
disable it; do not rely on nogap=false or nolabel=false.
The ordinary embedded authoring baseline is
?nogap=true&nolabel=true&autoreload=true. A blank draft runtime can start at
/wipe with noauth=true added. Keep query parameters before any #fragment.
allow="autoplay" permits autoplay within the iframe policy; it does not
guarantee audio without a browser-required user gesture.
Implementation: boot parameters, BIOS framing, runtime CSS, and piece lifecycle/label handling.
Keep framing through source changes #
Changing source should not require navigating the iframe again. The native
Aesel bridge waits for runtime readiness, then injects a dropped:piece message
from inside the WebView:
window.acSEND({
type: "dropped:piece",
content: {
name: "aesel-preview",
source,
search: "nogap=true&nolabel=true&autoreload=true",
isKidLisp: false,
},
});
This is an internal runtime call, not a generic cross-origin iframe messaging
API. A browser host on another origin needs an explicit bridge; it cannot call
frame.contentWindow.acSEND directly.
The URL configures initial framing. The message's search preserves the piece
layer's display flags during replacement. The worker remembers label suppression
for its lifetime, and BIOS preserves view parameters when rewriting browser
history. Carry intentional preview or icon flags too, but do not add them as
tracking tags. A message being accepted is not proof of a successful paint;
authoring bridges should wait for rendering evidence and report runtime errors.
Existing integrations:
- Aesel URL construction, native injection and rendering evidence.
- Shared “Try” page:
constructs embedded URLs with
nogap,nolabel, andnoauth. - Aesel phone sessions: published-piece preview URLs.
- Whistlegraph host, native source bridge.
Diagnose an unwanted gap #
Measure three rectangles separately: the host wrapper, iframe viewport, and
AC's #aesthetic-computer wrapper. In nogap mode, the latter should start at
0,0 and fill the child viewport. Check screenshots after first paint, a source
replacement, a resize/rotation, and a density change.
| Symptom | Inspect |
|---|---|
| Space outside the iframe | Host margin, padding, border, inline baseline, or a fixed height competing with aspect-ratio. |
| Checkerboard or rounded corners inside it | body.nogap and the effective BIOS gap. |
| Label returns after an edit | Initial URL plus source message search; worker label persistence. |
| Correct frame, padded drawing | Piece composition and its own screen/reframe calls. |
| Tiny seam on one edge | Wrapper/canvas CSS dimensions and fractional scaling; zero-gap BIOS uses 100% dimensions. |
BIOS must resolve an omitted frame() gap from lastGap/startGap before
toggling body.nogap. Otherwise a density change calling frame() can remove
the class while the effective gap remains zero. A useful regression sequence
is ?nogap=true, followed by Cmd/Ctrl+Plus or an ac-density-change event:
the class and edge-to-edge bounds must survive both.
window.acFORCE_NOGAP = true is an internal host hook used by packed bundles
that have no normal query string. BIOS honors it on every reframe, including
explicit nonzero gap requests. It is not a URL parameter, and an ordinary web
embed should use nogap=true rather than depend on script injection.
Iterate on Whistlegraph layout without restarting #
Whistlegraph lives in apple/whistlegraph (formerly apple/walkieware). The installed bundle identifier, Keychain service, and WebKit storage origin retain their legacy identifiers so updates preserve sign-in and artwork. Local storage is copied once to whistlegraph-* keys; saved source and history remain intact. Existing ww… piece codes, API routes, module imports, and the ww.mjs CLI remain compatibility aliases. New codes use wg…; the primary CLI is slab/bin/whistlegraph.mjs.
The Ware picker defaults to Aesthetic.Computer Piece and also offers Roblox Room. Each has separate saved versions. The brain can queue a ware switch; explicit requests such as “switch to Roblox” also work locally. The room editor and private service setup are described in roblox/rooms/README.md.
For a running Whistlegraph app with the live-layout bridge, send a CSS file through its existing owner-authenticated thread socket:
node slab/bin/whistlegraph.mjs layout wwRuboh /tmp/whistlegraph-layout.css
The stylesheet replaces the previous live override and persists locally across launches. An empty file clears it. This updates the surrounding app shell, not the cross-origin AC document. It does not reload the WebView, regenerate the piece, or add a piece version. The device must be online and idle; the command returns an acknowledgement after applying the override. CSS is limited to 100 KB. Native Swift changes still require an app build/install; this is a CSS iteration path, not Swift code hot reload.
Whistlegraph's native shell keeps its Workspace WebView at one stable SwiftUI
position. WhistlegraphScreen.swift owns the preview card, trailing caption,
version feed, and talk dock. engine.mjs remains the authority for versions
and generation; it sends typed, throttled display snapshots and accepts native
checkout/new-piece/stop commands. The engine document is the dedicated
Resources/Web/shell.html, not an Aesel prototype. Early provisional previews
are distinct from committed versions, so streaming remains visible before a
version is saved.
For live native layout tuning, the same CSS transport accepts bounded tokens:
:root {
--ww-spacing: 12;
--ww-page-inset: 20;
--ww-history-size: 22;
--ww-talk-height: 98;
--ww-title-size: 30;
}
The bridge reads these as numeric values and Swift clamps them to supported ranges before updating observable layout state. This changes native layout without recreating WebKit. Other CSS declarations affect only the web host; they do not style SwiftUI controls. An empty override restores the defaults.