finish the carousel rename in the panels

Two of these were live: PageBuilderPanel still compared kind === 'gallery' in
two places, so a carousel block's editor and its Choose button were both dead
branches. The type checker could not see them — both compare against string.

The rest is copy somebody reads: "Change gallery", "Name a new gallery",
"Reading the gallery", "Add to this gallery", Home's Gallery heading, and the
"Create Gallery" file action, now "Carousel from folder".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-04 18:06:06 +01:00
co-authored by Claude Opus 5
parent 15042ccc76
commit 6e33e89e2c
15 changed files with 104 additions and 104 deletions
+8 -8
View File
@@ -8,12 +8,12 @@ import type { FileAction } from 'officerdev';
* ── Why an action and not a task ──
*
* A task runs a script as the owner and reports through the job system, which is right for "convert
* these" and wrong for this. Creating a gallery is a name to type and a screen to land on — expressing
* these" and wrong for this. Creating a carousel is a name to type and a screen to land on — expressing
* it as a job would produce something to watch whose only output is somewhere to go next.
*
* ── It only offers itself inside `originals/` ──
*
* A gallery item is a path relative to that folder, so the action is meaningless anywhere else — and a
* A carousel item is a path relative to that folder, so the action is meaningless anywhere else — and a
* menu entry that appears everywhere and works in one place is worse than one that appears where it
* works. The check is on the path rather than on which panel is open, because the same browser is
* mounted in several places and only the path says where it is pointed.
@@ -21,7 +21,7 @@ import type { FileAction } from 'officerdev';
const ORIGINALS = '/media/originals';
/** Everything under `originals/`, relative to it — which is what a gallery item stores. */
/** Everything under `originals/`, relative to it — which is what a carousel item stores. */
const relativeTo = (path: string): string | null => {
const at = path.indexOf(ORIGINALS);
return at === -1 ? null : path.slice(at + ORIGINALS.length).replace(/^\/+/, '');
@@ -48,12 +48,12 @@ export const fileActions: FileAction[] = [
/*
A folder is expanded into the files inside it.
What somebody right-clicked is the directory; what a gallery holds is pictures. Without this the
gallery gets one item — the folder — which is what shipped first and is obviously wrong the
What somebody right-clicked is the directory; what a carousel holds is pictures. Without this the
carousel gets one item — the folder — which is what shipped first and is obviously wrong the
moment you look at it.
Expanded by the SERVER, because that side already knows which extensions are media and how to
walk the tree: it is the same `listOriginals` the sync uses, so a gallery cannot end up holding
walk the tree: it is the same `listOriginals` the sync uses, so a carousel cannot end up holding
a file the pipeline would never convert.
*/
const chosen = entries.map((entry) => relativeTo(entry.path)).filter((path): path is string => Boolean(path));
@@ -71,14 +71,14 @@ export const fileActions: FileAction[] = [
.get<{ files?: string[] }>(`/fofos-admin/_config/media/files?path=${encodeURIComponent(chosen[0] ?? '')}`)
.catch(() => null);
// Falling back to the folder itself would create the wrong gallery quietly. Nothing is better.
// Falling back to the folder itself would create the wrong carousel quietly. Nothing is better.
if (!body?.files?.length) return;
paths = body.files;
}
/*
Handed over in the URL, and the Carousels panel does the asking.
The action has no dialog of its own on purpose: naming a gallery, seeing the slug it will get
The action has no dialog of its own on purpose: naming a carousel, seeing the slug it will get
and watching the items land belongs on the screen that owns carousels, not in a modal over a
file browser that would then have to explain where the result went.
*/