Two things. `?media=` is Media's own folder and now goes when you leave it. It survived every navigation, so `?section=home&media=GALLERY/CALAVERA+1` was an ordinary URL — a parameter naming a folder in a section that is not open, carried into every link and shared or bookmarked that way. And the intro is a tiptap editor rather than a textarea of raw HTML. HTML in and HTML out, because that is already what the site's database holds and renders — introducing a second format would buy a migration and a round-tripping bug class for nothing. Declared as the PLUGIN's npmDependencies, not added to the platform's package.json. That lockfile is frozen and shared, and npm-deps.ts exists for exactly this: everything lands in plugins/node_modules/, so a plugin's editor cannot move a version the platform depends on. The toolbar is only what the site's stylesheet can render. Anything more would produce copy that looks right while editing and wrong when published, which the person only discovers after saying it was done. The content effect compares against getHTML() before writing back, and that comparison is the whole thing: while somebody types the two already agree, so nothing is pushed in and the caret stays put. Without it this is the classic controlled-contentEditable bug where the cursor jumps to the end on every keystroke. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
84 lines
3.7 KiB
TypeScript
84 lines
3.7 KiB
TypeScript
import type { PluginManifest } from '@@/plugins/manifest';
|
|
|
|
/**
|
|
* Fofos — the fofos website's admin, inside Officer.
|
|
*
|
|
* ── What it replaces ──
|
|
*
|
|
* A server-rendered admin on its own subdomain with its own argon2 passwords, its own session cookie
|
|
* and its own `/admin/setup` bootstrap. Every part of that duplicated something Officer already does,
|
|
* and none of it could be shared: giving somebody access to the machine still meant giving them
|
|
* access to the admin, separately, by hand. That is the whole reason this is a plugin.
|
|
*
|
|
* ── It holds no content ──
|
|
*
|
|
* fofos owns its own database and its own media store, and this reads and writes them over HTTP. The
|
|
* only thing in Officer's database is the connection — a URL and a token.
|
|
*
|
|
* That is a deliberate property rather than a convenience. **Deleting this plugin cannot lose a
|
|
* photograph or a line of the client's copy**, with the "delete its data" checkbox ticked or not,
|
|
* because none of it was ever here. The alternative — a `db/schema.ts` carrying fofos's own tables —
|
|
* would have put every one of them under an installer that is allowed to drop them.
|
|
*
|
|
* ── The permission is grantable; the credential is not ──
|
|
*
|
|
* Reading and editing the site is exactly what a member might be brought in to do. The TOKEN is a
|
|
* different matter: it is unrestricted write authority over everything the public sees, so `/_config`
|
|
* is `ownerOnly` — the split that field exists for, and the same shape proxy-manager and Jellyfin use
|
|
* for the same reason.
|
|
*
|
|
* ── No `screens` ──
|
|
*
|
|
* One screen at the mount prefix, labelled `label`. The nine sections are `?section=` on that one
|
|
* screen rather than nine routes: they are panels of one workspace, and `screens` is for a plugin
|
|
* contributing genuinely separate destinations.
|
|
*/
|
|
export const manifest: PluginManifest = {
|
|
publisher: 'officerdev',
|
|
version: '2026.08.29.26',
|
|
platform: '>=1.0.0',
|
|
|
|
label: 'Fofos',
|
|
summary: "The fofos website's content — pages, galleries, drops and collabs",
|
|
/** one website's admin, and useless without that website. */
|
|
tags: ['Personal'],
|
|
icon: 'PenLine',
|
|
color: '#c2410c',
|
|
|
|
// Nothing to declare. It talks HTTP to fofos and stores one row; there is no host binary anywhere
|
|
// on the path, and fofos itself is a separate deployment rather than something this installs.
|
|
|
|
/*
|
|
The rich text editor for the site's copy.
|
|
|
|
Declared here rather than added to the platform's package.json, which is frozen and shared: these
|
|
are installed into `plugins/node_modules/` alongside every other plugin's, so nothing about this
|
|
plugin's editor can move a version the platform depends on.
|
|
|
|
`@tiptap/extension-text-align` was already in the platform's dependencies with nothing importing
|
|
it — a leftover from an earlier attempt. It is asked for here too, so this plugin's copy is
|
|
resolved by the same rule as the rest rather than by whatever happens to be up the tree.
|
|
*/
|
|
npmDependencies: {
|
|
'@tiptap/react': '^3.15.3',
|
|
'@tiptap/starter-kit': '^3.15.3',
|
|
'@tiptap/pm': '^3.15.3',
|
|
'@tiptap/extension-text-align': '^3.15.3',
|
|
'@tiptap/extension-link': '^3.15.3',
|
|
},
|
|
|
|
permissions: [
|
|
{
|
|
key: 'fofos-admin',
|
|
label: 'Fofos',
|
|
description: 'Edit the fofos website — pages, galleries, drops, collabs and the messages people send',
|
|
|
|
// Where the fofos URL and token are set, and the probe that checks them. Prefix-matched, so
|
|
// `/_config/test` is covered too — deliberately, because that request carries the token in its
|
|
// BODY. A member who could call it would have an oracle pointed at a credential that can
|
|
// rewrite the whole public site.
|
|
ownerOnly: ['/_config'],
|
|
},
|
|
],
|
|
};
|