Commit Graph
4 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 944d3d9c5b a Media section, browsing the site's originals in place
a new section above Administration. it embeds THE PLATFORM'S OWN file browser rather
than drawing a file list — upload, delete, rename, move, thumbnails, the context
menu and every task hanging off it already exist and are maintained in one place. a
plugin's own would be a second to keep in step, arriving without any of that.

`basePath` IS A JAIL, not a starting folder: `useFileBrowserApp` resets currentPath
whenever it leaves, so pointing it at `<root>/media/originals` means the breadcrumb
stops there and `derived/` is not reachable by navigating. that is what was asked
for — people work in originals, derived is made for them.

IT IS A GUARDRAIL AND NOT A PERMISSION BOUNDARY, and the panel says so in its own
docstring because the difference will matter to whoever reads this next. the
file-browser API is unchanged and still addresses the whole home: anyone with file
access reaches derived/ through the Files screen. this prevents an accident, not a
determined person. confining it properly means scoping that API per user, which is a
platform change and a much larger one.

the panel also says, once, that derived/ exists and is made from these — otherwise
somebody who knows about it would think it had been lost.

`rootRelative` is new on the settings API because only the SERVER knows what home
is. the file browser addresses everything relative to a root, and a browser deriving
that prefix from the absolute path would be guessing. null when the root sits
outside home — a legitimate setting the browser cannot address, which the panel
explains rather than rendering an empty folder.

NOTHING HERE IS FOFOS-SPECIFIC, which matters now that this plugin is the template
for other site admins: the root is a setting, media/originals is the layout this
plugin creates, and both are read from the API. another site's plugin changes the
label.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 08:39:53 +01:00
pastilhasandClaude Opus 5 5e96274ebd Administration sits at the foot of the panel, not the end of the list
it was last in SECTIONS, so it rendered as the tenth row and then stopped —
directly under Messages, with empty panel beneath it. that is the bottom of the
LIST. what it wanted is the bottom of the PANEL, the way Update-all sits in the
plugins nav.

`mt-auto` on a second group, divided by the same border the rows already use. it
holds whether there are ten sections or two, and it does not need a spacer element
to push it down.

the split is a `pinned` flag on the section rather than the nav testing for the
key. which sections are the client's site and which are this plugin's own
machinery is a fact about the section, and the nav should be reading it, not
knowing it. one array still — `sectionFor` and `isSectionKey` have to resolve
every key, and a second exported list is a second place to forget one.

the row body came out into `renderRow` so both groups draw the same row. no
change to what a row looks like.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 00:20:39 +01:00
pastilhasandClaude Opus 5 7eec2ec51d Administration is a section, not an overlay
the connection form — where fofos is, and the token — was reached through
`?config=1` and a link in the detail header, because it was not one of the nine
sections. it is now the tenth, called Administration, last in the nav.

that removes the overlay and the second route to it. the two could disagree: the
form covered whatever section was selected underneath, so the nav highlighted one
thing while the panel showed another, and an unconfigured plugin rendered the form
with no header at all — which reads as the selected section being broken rather
than as one setup step not done yet. unconfigured now goes to Administration and
the header says so.

`site` and `administration` are deliberately separate and the file says why. Site
settings is the CLIENT's — partners, social links, contact details, all of it
public copy. Administration is Officer's end: how this plugin reaches fofos, and
whatever else it will need to know. nothing in it ever appears on the site, and
the people who touch the two are not the same people.

`path: null`, alone among the sections: it reads nothing from fofos, which is why
it keeps working when fofos is unreachable and the others do not.

the header's settings link is replaced by the section's own hint, so the row still
says what it is once you are in it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 00:18:13 +01:00
PastilhasandClaude Opus 5 6b87956e44 Everything needed to be a plugin
manifest, one table, a router and two panels. Discovery is by convention, so
this is the point at which the directory stops being a directory and becomes
something Officer can install.

The manifest holds only what a listing cannot say. One permission, grantable,
with ownerOnly on /_config — reading and editing the site is exactly what a
member might be brought in for, but the token is unrestricted write authority
over everything the public sees, so handing it over is the owner's.

db/schema.ts is ONE table and should stay that way. fofos owns its own Postgres
and its own media store; this holds a URL and a token. That is what makes the
strongest thing about this plugin true: no operation on it can destroy the
client's work, checkbox ticked or not, because none of it was ever here. A cache
would be the thin end of that.

The router mostly forwards. fofos's /api/admin already answers in the shape a
panel wants, so re-wrapping it would be a second place for the contract to drift.
Two things in it are load-bearing rather than style:

  registerProxiedPrefix('/api/fofos-admin/media'). bodyParser calls parseBody on
  multipart across the whole protected router, and Hono then caches the parsed
  FormData — so asking for the bytes afterwards re-serialises them under a NEW
  boundary while the ORIGINAL content-type header is what gets forwarded. Header
  and body disagree and every upload fails with "Unexpected end of form".
  Narrow to the one route: everything else is JSON that is fine to parse.

  The catch-all is LAST in the file. Hono runs matching handlers in registration
  order, so /* above /_config would swallow it and the settings form would
  forward itself to fofos, which has no such endpoint — a 404 from a plugin that
  is working perfectly.

Panels are a nav and a detail pane that never speak to each other: the nav writes
?section=, the detail pane reads it. Real <Link>s rather than the buttons
proxy-manager uses — AGENTS.md says nav items are links, and the file documenting
that pattern says not to copy the older one. <Link> and not <NavLink> because
active state here is a query parameter and NavLink compares paths, which would
light every row at once.

Which component renders which section is a MAP, not a conditional on `built`.
The first version read `built ? <MessagesPanel/> : <Planned/>`, which is correct
only while exactly one section exists — the second one finished would have
rendered Messages under its own name.

Messages is the only section built, chosen because it is one read-only route: it
proves the path from an Officer panel through this proxy to fofos and back with
nothing else able to be wrong at the same time. The other eight are in the nav,
dimmed and labelled, saying what they will do and that the old admin still edits
them. A nav that hides what is unfinished gives no sense of what the thing is for.

Typechecked against the platform — and the typechecker was verified to actually
cover plugins/ by planting an error and watching it fail, because a clean run
that does not include your files is worse than no run.

NOT yet: installed, or run against a live fofos. Nothing here has been through
the permission gate or a real request.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 23:05:34 +01:00