Commit Graph
3 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 6f8dc83984 the preview iframe was rendering Officer inside Officer
Two mistakes, and the second would have bitten immediately after the first was fixed.

The iframe pointed at /fofos-admin/preview/layout. But /fofos-admin is a FRONTEND route — the
single-page app owns it, and anything under it no router claims is answered with the app's own
index.html. So the iframe was served Officer. The plugin's API is mounted at /api/fofos-admin, which
every other call already knew: useClient defaults its base to /api, and thumbnail.ts spells the full
path out for <img> tags that cannot use it.

And an iframe cannot send an Authorization header. Neither can the stylesheet, the fonts or the images
the page goes on to request — those are all Officer requests now, so all of them need ?token=, the
same reason every <img> in this plugin carries one. The token comes off the iframe's own query string
and is propagated into every URL the rewrite touches, with the separator decided per URL because many
already carry the site's ?v= stamp.

It is stripped before the request reaches the site. The token is Officer's and the site has never
heard of it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 02:01:18 +01:00
pastilhasandClaude Opus 5 b56c8375c3 the preview is the real page, proxied
The panel drew its own header and footer in React. It looked plausible and it was wrong — the site's
header has a logo hanging past a rule and a nav in the content column — and worse, it was a second
implementation of the layout, so it would have gone on being wrong differently every time either side
moved.

So the admin embeds the site's own `/layout` route instead: the real shell with no page in it. A
designer changing the header in code changes what the admin shows, with no admin change at all. That
is the bridge.

Proxied through Officer rather than linked, because everything this plugin knows about where the site
lives is loopback — 127.0.0.1:<devPort> and nothing else. There is no public hostname stored anywhere
and adding one would be a second fact about the same thing, kept in step by hand. The request goes the
way every other request here goes, server-side, and the browser talks only to Officer.

Same origin, which is also what keeps the door open: the day the page builder wants to draw handles on
top of the real page, it can. A cross-origin iframe never could.

The rewrite is narrow and its limits are the interesting part. The site writes root-relative URLs and
those resolve against Officer's root inside the iframe; a <base> tag does not help, because it moves
relative URLs and leaves root-relative ones exactly where they were wrong. So href, src, srcset and
CSS url() are moved under the prefix — and `//` is excluded deliberately, because that names another
host and prefixing one would turn it into a path on this server.

Only HTML is rewritten. Stylesheets, fonts and images are returned as they came: rewriting bytes we do
not understand is how a proxy corrupts a font.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 01:55:45 +01:00
pastilhasandClaude Opus 5 09b914c430 Layout replaces one section per page
The admin was a section each for Home, About Us, Collections, Gallery, Collabs, Drops and Services,
because the site's pages were a constant in site-map.ts and a developer was the only one who could add
one. A page builder is going to replace that, so the nav stops enumerating pages.

What is left is the two things a page is NOT: Layout — the shell every page renders inside — and Pages,
listed but not built. Layout is first because it is first in the work: building pages before the frame
they sit in means building them twice.

The panel is a sketch of the page with the controls under it. Menu across the top, a body, the footer
at the bottom, in the chosen colours and typefaces — the same principle the header editor already
follows, where the admin renders the structure as the page will show it. It is honest about being a
sketch: drawing the logo and the rules would be a second implementation of the layout, and the two
would drift the first time either changed.

Fonts are a mapping, not free text. styles.css already declares four ROLES — body, sub, title,
display — and every template asks for a role rather than a typeface, so choosing a font is choosing
which of the four faces the build subsets plays each part. The list travels with the payload rather
than being copied here, because a picker offering a face the build never subsets renders as a fallback
nobody chose.

The per-page panels are NOT deleted. AboutPanel, HomePanel and RecordsPanel are still in the tree and
still work; they are unreachable, which is a line in sections.ts rather than a rewrite. The page
builder is not finished, and removing the only way to edit the live site's content before its
replacement exists would leave a gap with nothing in it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 01:38:05 +01:00