Bulk Management & a Library View
Confirmed by live testing in the editor, not just by reading the plugin’s code: the library itself works, assets keep their alt text and other metadata, and the picker correctly reuses that metadata when an asset is picked again. What’s still missing is any way to see the whole library at once, independent of a specific field’s picker. That’s a real gap, and it’s the one worth scoping as a feature request rather than folding into the smaller notes on Why the Picker Doesn’t Show.
This is a P1 platform gap, not a Brown-Forman-specific one. Filed as its own page because it needs its own scope, not a bullet on another page.
Confirmed: no way to mass-manage media today
Every entry point into the library goes through one specific component’s one specific field. That means:
- No bulk view. No way to see every asset uploaded to the site in one place, outside the context of picking an image for a particular field on a particular page.
- No bulk edit. No way to update alt text, caption, credit, or byline on multiple assets at once, or to audit which assets are missing required metadata.
- No bulk delete or cleanup. Combined with the confirmed soft-delete-only behavior and the confirmed “find everywhere this image is used” tooling not existing, there’s no practical way to find and clean up unused or duplicate assets across a growing library.
None of this is a bug in any single picker. It’s a consequence of the library having exactly one UI surface (see Is there a standalone Media Library screen?): a modal that opens from a field, scoped to “pick one asset for this field,” never “manage the whole collection.”
Feature to scope: a Library tab in the editor’s left rail
Confirmed buildable, not hypothetical. The left rail’s tab system is
@puckeditor/core’s own public Plugin API, already used twice in this exact
codebase. This isn’t a new UI paradigm to invent, it’s the same, proven mechanism.
Puck’s Plugin type (confirmed directly from @puckeditor/core’s own type
definitions) is:
type Plugin = {
name?: string;
label?: string;
icon?: ReactNode;
render?: () => ReactElement;
overrides?: Partial<Overrides>;
fieldTransforms?: FieldTransforms;
};A plugin with label, icon, and render gets its own tab in the left rail.
Confirmed live: Pantheon’s own Data sources panel (the one documented on
Data Sources & Queries) is built exactly this way:
function createRemoteDatasourceExplorerPlugin(options) {
return {
name: "datasource-explorer",
label: "Data sources",
icon: <Icon iconName="server" />,
render: () => <RemoteDatasourceExplorerPanel initialPath={options.editorPath} />,
};
}A “Media Library” tab is the identical pattern: a plugin object with its own
label, icon, and render, added to the same additionalPlugins array this
project’s editor-client.tsx already passes to useP1Editor (alongside the media
plugin and, when enabled, the AI chat plugin). No new platform capability from
Pantheon required to add the tab itself; this is ours to build today.
Confirmed: the data access question is answered, and it changes the recommendation
Traced directly through @pantheon-systems/p1-media’s compiled source
(src/components/media-library.tsx, the code behind the picker’s “Choose from
Library” modal). Two things this settles:
The listing endpoint is real, and it’s not picker-internal state. The picker calls a genuine REST endpoint:
GET {workerUrl}/media?siteId={siteId}&limit=500&search={query}
Authorization: Bearer {token}returning an array of:
{
assetId: string;
versionId: string;
url: string;
filename: string;
contentType?: string;
size?: number;
width?: number;
height?: number;
metadata?: { alt?: string; caption?: string; credit?: string; byline?: string };
createdAt?: string;
}Same endpoint, same auth pattern, callable from anywhere, not locked inside the picker component.
The full-featured library UI already exists. It’s just not exported. The
picker’s internal MediaLibrary component is a complete implementation: search,
metadata editing (PATCH /media/{assetId}), delete (DELETE /media/{assetId}),
drag-and-drop upload with progress (presign/upload/finalize). Everything this page
originally asked for as a feature request already exists in the installed package.
It’s a private component: p1-media’s public exports are
createMediaPlugin, createMediaFigureBlock, buildImageUrl, getMediaProps, MediaImage, MediaFigure, makeMediaValue, isMediaValue, DEFAULT_MEDIA_PATTERNS, MediaLibrary
isn’t in that list.
This changes the recommendation. Reimplementing search/edit/delete/upload
ourselves would mean maintaining a second copy of functionality Pantheon has
already built and tested. The highest-leverage move is asking Pantheon to export
MediaLibrary (and whatever internal config context it needs) directly, not
building a parallel one.
Draft: a browse-only v1, ready today
A real, typechecked, working draft exists at
components/puck/media-library-plugin.tsx in the main site repository. Not yet
wired into the live editor, it’s a draft for review.
Built as a real Puck Plugin ({ name, label, icon, render }, the same shape
covered above), registered the same way createMediaPlugin already is in
editor-client.tsx. It calls the same GET /media endpoint directly (since
MediaLibrary itself isn’t importable) and covers:
- A searchable grid of every asset on the site, thumbnails generated via the
public
buildImageUrl()helper. - A visible flag on any asset missing alt text, plus a running count (“3 of 40 shown assets are missing alt text”), directly answering the audit need from Requirements: WebP Delivery & Alt Text.
- A detail view per asset: full metadata (alt/caption/credit/byline), dimensions, file size, asset ID, and a copy-URL action.
Deliberately browse-only, no edit/delete/upload. Duplicating those flows here would
be exactly the “second copy of functionality Pantheon already built” problem noted
above. If the Pantheon export doesn’t happen, extending this draft with PATCH/
DELETE calls to the same endpoints is a scoped, well-understood next step (the
request/response shapes are documented above); it’s just not worth building until
it’s clear the export request won’t land.
Open questions for this page now live on Outstanding Questions, tracked centrally across all pages rather than repeated per page.