diff --git a/web/DetailPanel.tsx b/web/DetailPanel.tsx index 3cdb711..ab76815 100644 --- a/web/DetailPanel.tsx +++ b/web/DetailPanel.tsx @@ -31,7 +31,7 @@ import { useReady } from './useReady'; * ── Not ready means Administration, whatever the URL says ── * * The gate is `useReady`, shared with the nav so the two cannot disagree. Enforced HERE and not only - * there, because the nav can only decline to offer a link — `?section=gallery` typed into the address + * there, because the nav can only decline to offer a link — `?section=carousels` typed into the address * bar, or a bookmark from before the connection was forgotten, arrives without going through it. A * nav that hides a door is a courtesy; this is the lock. */ diff --git a/web/PublishButton.tsx b/web/PublishButton.tsx index 9fd10d3..5816010 100644 --- a/web/PublishButton.tsx +++ b/web/PublishButton.tsx @@ -11,7 +11,7 @@ import { useReady } from './useReady'; * * ── Why they are in the header and not only in Administration ── * - * Publishing is the last step of every edit — write something on Home, choose a gallery, add a drop, + * Publishing is the last step of every edit — write something on Home, choose a carousel, add a drop, * then make it public. It lived in one card at the bottom of one section, so finishing a change meant * leaving the thing you were working on to go and find the button. A person who does not know the * panel simply does not find it, and concludes their edit went live when it did not. diff --git a/web/canvas.ts b/web/canvas.ts index 8db014d..0854180 100644 --- a/web/canvas.ts +++ b/web/canvas.ts @@ -60,7 +60,7 @@ export type CanvasComponent = { /* One optional field per kind that the panel reads back. Not `Record`: every reader would then need a cast, and the point of naming them is that the compiler knows `free-text` holds - `html` and `gallery` holds `carouselId`. + `html` and `carousel` holds `carouselId`. */ content?: { html?: string; carouselId?: number | null; url?: string; ratio?: number; crop?: string }; padding?: Record; diff --git a/web/file-actions.ts b/web/file-actions.ts index 056f0b4..820b5c6 100644 --- a/web/file-actions.ts +++ b/web/file-actions.ts @@ -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. */ diff --git a/web/sections.ts b/web/sections.ts index 2c9287e..e3bdea8 100644 --- a/web/sections.ts +++ b/web/sections.ts @@ -123,12 +123,12 @@ export const SECTIONS: Section[] = [ Carousels — an entity of their own, not a list hanging off a page. Above Media and pinned with it, because the two are the same kind of thing: the raw material the - site is built from, held on this machine. Media is the files; a gallery is a set of them somebody + site is built from, held on this machine. Media is the files; a carousel is a set of them somebody has chosen and put in an order, which is a decision worth keeping once rather than remaking on every page that wants it. `requires: 'connection'`, unlike Media beside it, and the difference is real rather than a - copy-paste. Media is a file browser over a folder on this disk and needs nothing else. A gallery + copy-paste. Media is a file browser over a folder on this disk and needs nothing else. A carousel is a ROW in the site's database — reading or writing one goes through the site's admin API, so this section is useless without the development server answering, and opening it there would be a screen where every action fails. diff --git a/web/sections/AddComponentDialog.tsx b/web/sections/AddComponentDialog.tsx index ef72289..356bb0f 100644 --- a/web/sections/AddComponentDialog.tsx +++ b/web/sections/AddComponentDialog.tsx @@ -63,7 +63,7 @@ type Choice = { * * Named rather than omitted, and the ones named are things the site already HAS — media, video and * carousels all exist as concepts here. That is the difference between saying what is coming and - * inventing a roadmap. Gallery stopped being one of them on 2026-08-31: the set, its ordering and the + * inventing a roadmap. The carousel stopped being one of them on 2026-08-31: the set, its ordering and * carousel that draws it were all already built, so the block is a reference to one. */ export const CHOICES: Choice[] = [ @@ -73,7 +73,7 @@ export const CHOICES: Choice[] = [ Image and Video were two rows — one of them greyed out as `soon` — and the distinction was never the editor's to make: the path names a real file and what that file IS is a fact about it. So the - picker asks for a file and the site works out the rest, which is also what a gallery item has + picker asks for a file and the site works out the rest, which is also what a carousel item has always done. */ { kind: 'media', label: 'Media', hint: 'A photograph or a video, from the media store', Icon: Image }, @@ -104,11 +104,11 @@ export const CHOICES: Choice[] = [ "it" for that reason. What differs between the sections is a path and a few spec labels, and both travel with the subject. */ - { kind: 'subject-cover', label: 'Its lead photograph', hint: 'The first in its gallery, large', Icon: Image }, + { kind: 'subject-cover', label: 'Its lead photograph', hint: 'The first in its carousel, large', Icon: Image }, { kind: 'subject-title', label: 'Its name', hint: 'What this page is about', Icon: Heading }, { kind: 'subject-description', label: 'Its description', hint: 'What was written about it', Icon: Type }, { kind: 'subject-specs', label: 'Its specs', hint: 'Measurement, material, colours, and the rest', Icon: Ruler }, - { kind: 'subject-carousel', label: 'Its photographs', hint: 'The gallery it points at', Icon: Images }, + { kind: 'subject-carousel', label: 'Its photographs', hint: 'The carousel it points at', Icon: Images }, ]; export const AddComponentDialog = ({ diff --git a/web/sections/BlockSettings.tsx b/web/sections/BlockSettings.tsx index 97e0fd9..52e9f27 100644 --- a/web/sections/BlockSettings.tsx +++ b/web/sections/BlockSettings.tsx @@ -12,7 +12,7 @@ import { useDevice } from '../DeviceToggle'; * * ── Why it is not inside the free text ── * - * Typing a heading into the text works for one kind of block and no others. A gallery, a video and a + * Typing a heading into the text works for one kind of block and no others. A carousel, a video and a * partner row all want to announce themselves and have nowhere to put the words — so the heading is a * property of the BLOCK rather than of its contents, and every kind gets one. * diff --git a/web/sections/CarouselItems.tsx b/web/sections/CarouselItems.tsx index 8aeda89..e73f7f1 100644 --- a/web/sections/CarouselItems.tsx +++ b/web/sections/CarouselItems.tsx @@ -8,15 +8,15 @@ import { readableError } from '../errors'; import { thumbUrl } from '../thumbnail'; /** - * One gallery's contents: what is in it, in what order, and what each picture is called. + * One carousel's contents: what is in it, in what order, and what each picture is called. * * ── Why it is here and not a link to Carousels ── * - * A gallery block on a page IS a gallery, and being sent to another section to reorder it means + * A carousel block on a page IS a carousel, and being sent to another section to reorder it means * losing the page you were arranging it for. The set is a thing of its own — that is why it has its * own section — but editing it is something you do while looking at where it appears. * - * ── It reads the gallery itself ── + * ── It reads the carousel itself ── * * `GET /carousels/:id`, rather than being handed one. The block knows an id and nothing else, and a * component that fetches what it needs can be dropped anywhere something knows an id. @@ -29,21 +29,21 @@ import { thumbUrl } from '../thumbnail'; */ type Item = { id: number; url: string; alt: string | null }; -type Gallery = { id: number; name: string; slug: string; items: Item[] }; +type Carousel = { id: number; name: string; slug: string; items: Item[] }; export const CarouselItems = ({ carouselId, onChanged }: { carouselId: number; onChanged?: () => void }) => { const client = useClient(); const clientRef = useRef(client); clientRef.current = client; - const [gallery, setCarousel] = useState(null); + const [carousel, setCarousel] = useState(null); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const [picking, setPicking] = useState(false); const load = useCallback(async () => { try { - setCarousel((await clientRef.current.get<{ data: Gallery }>(`/fofos-admin/carousels/${carouselId}`)).data); + setCarousel((await clientRef.current.get<{ data: Carousel }>(`/fofos-admin/carousels/${carouselId}`)).data); } catch (failure) { setError(readableError(failure)); } @@ -60,7 +60,7 @@ export const CarouselItems = ({ carouselId, onChanged }: { carouselId: number; o try { await call(); await load(); - // The page draws this gallery, so it is now showing the old one. + // The page draws this carousel, so it is now showing the old one. onChanged?.(); } catch (failure) { setError(readableError(failure)); @@ -83,19 +83,19 @@ export const CarouselItems = ({ carouselId, onChanged }: { carouselId: number; o const orderedRef = useRef(null); const moveOver = (event: React.DragEvent, overId: number) => { - if (dragged === null || !gallery) return; + if (dragged === null || !carousel) return; // Without this the drop is refused and the browser animates the row back to where it started. event.preventDefault(); if (dragged === overId) return; - const next = [...gallery.items]; + const next = [...carousel.items]; const from = next.findIndex((one) => one.id === dragged); const to = next.findIndex((one) => one.id === overId); if (from < 0 || to < 0) return; next.splice(to, 0, next.splice(from, 1)[0]!); orderedRef.current = next; - setCarousel({ ...gallery, items: next }); + setCarousel({ ...carousel, items: next }); }; const commitOrder = () => { @@ -110,19 +110,19 @@ export const CarouselItems = ({ carouselId, onChanged }: { carouselId: number; o ); }; - if (!gallery) return

{error ?? 'Loading…'}

; + if (!carousel) return

{error ?? 'Loading…'}

; return (
- {gallery.name} + {carousel.name} - {gallery.items.length} item{gallery.items.length === 1 ? '' : 's'} + {carousel.items.length} item{carousel.items.length === 1 ? '' : 's'}
    - {gallery.items.map((item) => ( + {carousel.items.map((item) => (
  • clientRef.current.post(`/fofos-admin/carousels/${carouselId}/items`, { urls: [url] })); }} - title="Add to this gallery" + title="Add to this carousel" />
); diff --git a/web/sections/CarouselsPanel.tsx b/web/sections/CarouselsPanel.tsx index 367b26a..62333e3 100644 --- a/web/sections/CarouselsPanel.tsx +++ b/web/sections/CarouselsPanel.tsx @@ -11,7 +11,7 @@ import { CarouselItems } from './CarouselItems'; /** * Carousels — named, ordered sets of media, reusable anywhere on the site. * - * ── Why a gallery is not a list on a page ── + * ── Why a carousel is not a list on a page ── * * The home page had one, as rows hanging off its own content. That works for exactly one. The moment a * second page wants the same photographs in the same order, a list-per-page means choosing them again @@ -19,26 +19,26 @@ import { CarouselItems } from './CarouselItems'; * * ── The server owns the shape ── * - * Every write answers with the whole gallery and this never patches its own state from what it sent. + * Every write answers with the whole carousel and this never patches its own state from what it sent. * That matters most here: reordering renumbers everything, adding appends after whatever was there, * and deleting closes a gap. A client keeping its own copy drifts on the first of those with no way to * notice. */ type Item = { id: number; url: string; alt: string | null; sort_order: number }; -type Gallery = { id: number; name: string; slug: string; items: Item[] }; +type Carousel = { id: number; name: string; slug: string; items: Item[] }; export const CarouselsPanel = () => { const client = useClient(); const clientRef = useRef(client); clientRef.current = client; - const [carousels, setCarousels] = useState(null); + const [carousels, setCarousels] = useState(null); const [openId, setOpenId] = useState(null); const [name, setName] = useState(''); /** Paths handed over by the context menu, waiting for a name. */ const [pending, setPending] = useState([]); - /** The gallery being renamed, and the name being typed into it. Null when nobody is renaming. */ + /** The carousel being renamed, and the name being typed into it. Null when nobody is renaming. */ const [renaming, setRenaming] = useState<{ id: number; name: string } | null>(null); const [busy, setBusy] = useState(null); const [error, setError] = useState(null); @@ -46,7 +46,7 @@ export const CarouselsPanel = () => { const load = useCallback(async () => { try { - const response = await clientRef.current.get<{ data: Gallery[] }>('/fofos-admin/carousels'); + const response = await clientRef.current.get<{ data: Carousel[] }>('/fofos-admin/carousels'); setCarousels(response.data); } catch (failure) { setError(readableError(failure)); @@ -59,7 +59,7 @@ export const CarouselsPanel = () => { }, [load]); /* - The selected gallery's contents, handed to the editor column. + The selected carousel's contents, handed to the editor column. Announced rather than rendered, because that panel is a SIBLING — see useEditorSlot. `onChanged` reloads the list here, so the item count beside the name is never one behind what is in it. @@ -77,16 +77,16 @@ export const CarouselsPanel = () => { ); }, [openId, load]); - // Cleared on unmount, or the panel goes on offering a gallery over somebody else's screen. + // Cleared on unmount, or the panel goes on offering a carousel over somebody else's screen. useEffect(() => () => setEditorRef.current(null), []); /* - Arriving from "Create Gallery" in the file browser. + Arriving from "Carousel from folder" in the file browser. The action puts the chosen paths in the URL and sends you here rather than opening its own dialog: - naming a gallery, seeing the slug it gets and watching the items land belongs on the screen that + naming a carousel, seeing the slug it gets and watching the items land belongs on the screen that owns carousels. Consumed once and removed from the URL — a `?from=` left behind would recreate the - gallery on every refresh, which somebody discovers as four copies. + carousel on every refresh, which somebody discovers as four copies. */ const arriving = params.get('from'); useEffect(() => { @@ -124,13 +124,13 @@ export const CarouselsPanel = () => { /* Renaming changes the NAME and leaves the slug exactly where it is. - Everything that points at a gallery points at its id — `collections.carousel_id`, and a gallery + Everything that points at a carousel points at its id — `collections.carousel_id`, and a carousel block's `content.carouselId`. Nothing renders through the slug, so a rename cannot break a reference today. It is left alone anyway, because a slug is an ADDRESS: it can end up in a link somebody sent, and the day something does resolve by it, a label having quietly moved it would be a very hard bug to find. - So the two are separable and this only writes one of them. A gallery whose slug is genuinely wrong + So the two are separable and this only writes one of them. A carousel whose slug is genuinely wrong is a different act with different consequences, and it should look like one. */ const rename = () => @@ -143,7 +143,7 @@ export const CarouselsPanel = () => { const create = () => send('create', async () => { // `urls` in the same request, so creating from a selection is one action — splitting it would - // leave an empty gallery behind whenever the second call failed. + // leave an empty carousel behind whenever the second call failed. await clientRef.current.post('/fofos-admin/carousels', { name: name.trim(), urls: pending }); setName(''); setPending([]); @@ -170,7 +170,7 @@ export const CarouselsPanel = () => { autoFocus={pending.length > 0} value={name} onChange={(event) => setName(event.target.value)} - placeholder="Name a new gallery" + placeholder="Name a new carousel" className="min-w-0 flex-1 text-xs" onKeyDown={(event) => { if (event.key === 'Enter' && name.trim()) void create(); @@ -192,23 +192,23 @@ export const CarouselsPanel = () => { {/* The list, and only the list. - The contents of a gallery — its pictures, their order, their captions — are announced to the - editor column and drawn by `CarouselItems`, which is the same component a gallery BLOCK on a + The contents of a carousel — its pictures, their order, their captions — are announced to the + editor column and drawn by `CarouselItems`, which is the same component a carousel BLOCK on a page uses. Two copies of that list existed for a day and this is the one that went. So this panel is what a master list should be: which carousels there are, making one, deleting one. What is inside the selected one is a different question, answered beside it. */} - {carousels.map((gallery) => { - const open = openId === gallery.id; + {carousels.map((carousel) => { + const open = openId === carousel.id; return (
- {renaming?.id === gallery.id ? ( + {renaming?.id === carousel.id ? (
@@ -216,7 +216,7 @@ export const CarouselsPanel = () => { autoFocus value={renaming.name} className="h-7 text-sm" - onChange={(event) => setRenaming({ id: gallery.id, name: event.target.value })} + onChange={(event) => setRenaming({ id: carousel.id, name: event.target.value })} onKeyDown={(event) => { if (event.key === 'Enter' && renaming.name.trim()) void rename(); if (event.key === 'Escape') setRenaming(null); @@ -225,7 +225,7 @@ export const CarouselsPanel = () => { {/* Said while renaming, not afterwards: somebody about to change a name is exactly who wonders whether the address moves with it. */} - {gallery.slug} · the address does not change + {carousel.slug} · the address does not change
)} - {renaming?.id !== gallery.id && ( + {renaming?.id !== carousel.id && (
diff --git a/web/sections/PageBuilderPanel.tsx b/web/sections/PageBuilderPanel.tsx index 59bcfd6..003f2ba 100644 --- a/web/sections/PageBuilderPanel.tsx +++ b/web/sections/PageBuilderPanel.tsx @@ -162,7 +162,7 @@ export const PageBuilderPanel = () => { const [choosing, setChoosing] = useState<{ sectionId: number; column: number } | null>(null); /** The component being written, and where a new one would go. Null when the dialog is shut. */ /* - Which gallery a block shows, while it is being chosen. + Which carousel a block shows, while it is being chosen. The same shape as `picking` and for the same reason: choosing a set is one act with one outcome, so there is nothing to draft and nothing to cancel out of afterwards. `carouselId` rides along so @@ -274,7 +274,7 @@ export const PageBuilderPanel = () => { The effect below cannot do that job: bumping `rendered` runs it immediately, while `frameDoc` still points at the outgoing document — so it scrolled the page that was about to be replaced, and the - new one arrived at the top. Every write from the editor did that: reorder a gallery, lose your + new one arrived at the top. Every write from the editor did that: reorder a carousel, lose your place. */ const partRef = useRef(part); @@ -474,7 +474,7 @@ export const PageBuilderPanel = () => { with one outcome, so there is nothing to draft and nothing to cancel out of afterwards. Photograph or video is not asked. The file already knows, and the site reads it off the extension - the way a gallery item always has — a kind stored here would be a second answer able to disagree. + the way a carousel item always has — a kind stored here would be a second answer able to disagree. */ const saveVideo = async (url: string) => { if (!page || !picking) return; @@ -494,7 +494,7 @@ export const PageBuilderPanel = () => { }; /* - A gallery block is a REFERENCE, saved as soon as the set is chosen — see CarouselPicker. + A carousel block is a REFERENCE, saved as soon as the set is chosen — see CarouselPicker. Identical in shape to saveVideo because it is the same interaction: pick one thing, store one value. The two are left as two functions rather than one generic saver, because what they store @@ -589,7 +589,7 @@ export const PageBuilderPanel = () => { return; } - if (component.kind === 'gallery') { + if (component.kind === 'carousel') { const held = owner.components.find((one) => one.id === component.id)?.content?.carouselId; setChoosingCarousel({ id: component.id, @@ -793,7 +793,7 @@ export const PageBuilderPanel = () => { */} {block.kind === 'partners' && setRendered((n) => n + 1)} />} - {block.kind === 'gallery' && ( + {block.kind === 'carousel' && (
{/* - The gallery's own contents, here rather than a link to the Carousels section. + The carousel's own contents, here rather than a link to the Carousels section. - A gallery block on a page IS a gallery, and being sent elsewhere to reorder it means + A carousel block on a page IS a carousel, and being sent elsewhere to reorder it means losing the page you were arranging it for. `onChanged` reloads the frame, because the page draws this set and has just been given a different one. */} diff --git a/web/sections/PageStructure.tsx b/web/sections/PageStructure.tsx index 465d886..f75bad5 100644 --- a/web/sections/PageStructure.tsx +++ b/web/sections/PageStructure.tsx @@ -22,7 +22,7 @@ import { BandBlocks } from './BandBlocks'; * ── The gesture is the one used everywhere else ── * * Grip on the left, the row is the drop target, the list rearranges as the pointer crosses it, one - * write at the end carrying every id. Pages, gallery items and partners all do this; a fourth way to + * write at the end carrying every id. Pages, carousel items and partners all do this; a fourth way to * reorder something would be a fourth thing to learn. */ export const PageStructure = ({ diff --git a/web/sections/SubjectFields.tsx b/web/sections/SubjectFields.tsx index f792708..00e29c9 100644 --- a/web/sections/SubjectFields.tsx +++ b/web/sections/SubjectFields.tsx @@ -13,7 +13,7 @@ import type { SubjectRecord, SubjectSection } from './subjects'; * The fields below the description come from the section — see subjects.ts. Everything above them is * shared because it is the same in both: what it is called, and where it lives. * - * The gallery is not here. It is chosen beside the photographs it puts on screen, in the panel, where + * The carousel is not here. It is chosen beside the photographs it puts on screen, in the panel, where * the consequence of choosing it is visible — a dropdown in a column of text fields would be the one * control whose effect you cannot see. * diff --git a/web/sections/SubjectPanel.tsx b/web/sections/SubjectPanel.tsx index a243676..f3d764e 100644 --- a/web/sections/SubjectPanel.tsx +++ b/web/sections/SubjectPanel.tsx @@ -25,7 +25,7 @@ import type { SubjectPhoto, SubjectRecord, SubjectSection } from './subjects'; * * ── The grid is READ-ONLY ── * - * The photographs belong to the gallery, and so does their order — including which one leads, which + * The photographs belong to the carousel, and so does their order — including which one leads, which * is simply the first. Making them look pressable here would promise a control that lives in * Carousels. */ @@ -85,7 +85,7 @@ export const SubjectPanel = ({ section }: { section: Su ); }, [records, selectedId, setParams, section.param]); - // The gallery's photographs, re-read when the selection moves. Null while nothing is chosen, so the + // The carousel's photographs, re-read when the selection moves. Null while nothing is chosen, so the // empty state and "still loading" are not the same thing. useEffect(() => { if (!selectedId || !selected) { @@ -230,10 +230,10 @@ export const SubjectPanel = ({ section }: { section: Su
- {/* Deleting removes the ROW, never the photographs — they belong to a gallery, which + {/* Deleting removes the ROW, never the photographs — they belong to a carousel, which belongs to Carousels. Said on the button so nobody has to find out. */}