Commit Graph
4 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 82862ed340 use the platform's dev proxy instead of a worse copy of it
The preview went through a proxy written into this plugin. The platform already had one —
/api/dev-proxy/:port/* — and it is better in exactly the ways the copy was worst.

Authentication. An iframe navigation cannot carry an Authorization header, and the copy answered that
by putting a token on the URL and on every asset the page then requested. The platform's answer is a
short-lived cookie obtained by an ordinary authenticated request, and its own comments say why: a
token on an iframe URL is a guess about which key the browser stored it under. It had been tried
there and rejected before this plugin repeated it.

Coverage. The copy rewrote URLs with a regex over the markup, which catches only what is written into
the document. The platform injects a script that patches fetch, XMLHttpRequest and WebSocket, so URLs
the page BUILDS at runtime are moved as well — which is most of them in a modern dev server. It also
normalises `..`, forwards HMR, and has nineteen tests behind it.

So the plugin's proxy is gone, previewUrl with it, and the panel does what the platform's Preview
panel does: ask for the session, then mount the frame at the dev port.

No sandbox attribute, for the reason that panel gives. Without allow-same-origin a frame has an opaque
origin and no storage; with allow-same-origin and allow-scripts it can reach up and remove its own
sandbox attribute. Claiming a protection that does not hold is worse than not claiming it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 02:07:18 +01:00
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 84336b8af5 a framing slot renders the original, not a rung
The preview asked `derived/` for a 640-wide rung and drew it about 1900 pixels wide — stretched three
times over, which reads as a poor photograph rather than as the wrong file. Raising the rung only moves
the problem: the box is most of a panel wide and can be zoomed on top of that, so any rung chosen in
advance is chosen wrong, and the widest one available is whatever the ladder happens to hold.

The original has every pixel there is. It is also, often, a HEIC that no browser can decode — which is
what av-club is for, and the platform already renders those for its own file browser through exactly
this path.

So there is a `/original/*` route: it resolves a path inside `originals/`, forwards to av-club's
`/v1/render` with a width, and streams back something a browser can draw. It decodes nothing itself.
That is the standing rule and the reason there is no image library in this plugin.

Not cached on disk, like the platform's own /render and for the same reason: a full-size render is
large, looked at once, and keyed on a width the panel chose. Caching it would fill a disk to make the
second look at one photograph faster.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 00:02:24 +01:00
pastilhasandClaude Opus 5 9d3cd7702f a media slot is the picture, at the shape the page will show it
It was a read-only text box holding a path and a Browse button beside it. A path is not what anybody is
choosing — somebody picking the header photograph is choosing a PHOTOGRAPH, and reading
`ABOUT US/HEADER.jpg` to find out whether you picked the right one is doing the computer's job for it.

MediaField is the slot: empty, it is a dashed box at the page's own aspect ratio that you click or drop
onto; filled, it is the picture, with Change and Remove appearing over it on hover. The panel also stops
changing height as things are chosen, so a page with three slots starts to look like the page rather
than like a form.

── The aspect ratio is a prop and has to be ──

The header is 21:9, the product video 16:9, a gallery slot square. The component cannot know that and
must not guess: the SITE decides what shape a slot is, and a component that guessed would be a second
opinion to keep in step with the stylesheet. Both callers name the ratio their partial draws.

── Dropping ──

It accepts a drag from Officer's own file browser, which carries paths under
`application/x-officer-files` — the same payload that moves files between windows. So dragging a
photograph out of Media onto a slot is one gesture and no upload.

It does not accept a file from the desktop. That is an upload into originals/, a different operation
with its own failure modes, and the Media section already does it properly.

── Also ──

`thumbUrl` was copied into GalleriesPanel and RecordsPanel and was about to be copied a third time. It
is a module now: three copies of a URL shape is three places to be wrong about the rung when the ladder
changes, which it did four times in a day.

A file that has gone since it was chosen falls back to the empty state rather than a browser's
broken-image glyph, and says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 23:43:12 +01:00