diff --git a/src/workspaces/officerdev/src/apps/Plugins/PluginDetail.tsx b/src/workspaces/officerdev/src/apps/Plugins/PluginDetail.tsx index 78cddd4e..9d765935 100644 --- a/src/workspaces/officerdev/src/apps/Plugins/PluginDetail.tsx +++ b/src/workspaces/officerdev/src/apps/Plugins/PluginDetail.tsx @@ -295,24 +295,32 @@ const Readme = ({ appName }: { appName: string }) => { * plugin. A modal that says "Are you sure?" tests whether you can click twice, not whether you know what * you are about to lose. * - * Offered whether or not the plugin is installed, because the state that needs it most is the one where - * a broken install has already been uninstalled and the directory is still sitting there hiding the - * plugin from the marketplace. + * ── Only once it is uninstalled ── + * + * This used to be offered in every state, including while the plugin was running. Two clicks — one of + * them a confirmation nobody reads the second time — separated a working plugin from its tables being + * dropped. Now deleting is the SECOND of two verbs: uninstall stops it and forgets it, and only then can + * it be deleted. That is also the order the buttons appear in, so the screen teaches the sequence. + * + * Nothing is lost by the gate. The state this exists for — a directory sitting on disk keeping the plugin + * out of the marketplace — is by definition an uninstalled one. */ const Purge = ({ plugin, busy, onPurge }: { plugin: PluginItem; busy: boolean; onPurge: () => void }) => { const [confirming, setConfirming] = useState(false); + // Installed means uninstall first. The button is absent rather than disabled: a disabled control invites + // a hunt for what would enable it, and the answer here is a different button three inches away. + if (plugin.installed) return null; + if (!confirming) { return (
- + +

+ Removes its tables, its data and plugins/{plugin.appName} — and puts it back in the marketplace. +

); } @@ -482,7 +490,7 @@ export const PluginDetail = () => {

{plugin.installed ? 'Disabling unmounts its routes and stops its sidecar. Uninstalling also forgets the install — neither deletes anything the plugin stored.' - : 'Not installed, but the code is still on this machine — which is why the marketplace does not offer it.'} + : 'Not installed, but the code is still on this machine — which is why the marketplace does not offer it. Installing again reuses this directory as it stands; deleting it below gets you a fresh clone.'}

run('purge', plugin.appName)} /> diff --git a/src/workspaces/officerdev/src/apps/Plugins/PluginsList.tsx b/src/workspaces/officerdev/src/apps/Plugins/PluginsList.tsx index 7e892f39..c8124ae3 100644 --- a/src/workspaces/officerdev/src/apps/Plugins/PluginsList.tsx +++ b/src/workspaces/officerdev/src/apps/Plugins/PluginsList.tsx @@ -34,7 +34,11 @@ export const PluginsList = () => { that: a count would imply a check that has not happened. */} {plugins.length ? (
- Installed + {/* "On this machine", not "Installed" — the list is every plugin DIRECTORY, and an uninstalled + one stays in it. Under the old heading such a row read "Installed · not installed", which is + how a real uninstall got mistaken for a failed one: the plugin never moved, so nothing on + screen said the directory was still there deciding what a reinstall would run. */} + On this machine