Skip to content

fix(plugins): support media pickers in repeaters - #4440

Open
khoinguyenpham04 wants to merge 1 commit into
mainfrom
noah/repeater-media-picker
Open

khoinguyenpham04 wants to merge 1 commit into
mainfrom
noah/repeater-media-picker

Conversation

@khoinguyenpham04

Copy link
Copy Markdown
Collaborator

What does this PR do?

Native Portable Text plugin forms already render media pickers inside repeater rows, but the public types and Block Kit validation reject their declarations. This aligns the supported types and validation with that existing editor behavior. Selected images keep their existing URL-string format.

The shared plugin media picker now follows the compact image-card layout used by the content editor. It keeps the whole image visible, exposes Replace and Remove without hover, and safely encodes local storage keys. Tests cover declaration validation and selecting, saving, reopening, reordering, replacing, and removing images with their captions.

Related issue: not applicable. This follows a specific Maintainer request.

Type of change

  • Bug fix
  • Feature (requires an accepted feature plan or RFC)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Your connection to this change

  • I use EmDash and ran into the problem this PR addresses
  • I maintain a tool, dependency, or service that EmDash uses, and this change concerns that integration
  • A Maintainer approved this specific change before I opened the PR

An EmDash Maintainer requested this compatibility fix.

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs.
  • I have added and reviewed the user-facing changeset (if this PR changes a published package)
  • New features link to their merged design PR.
  • I have included screenshots below if this PR changes the UI

This corrects the declaration contract for existing behavior, so no new feature design is included. Required-field validation remains separate.

AI assistance disclosure

  • This PR includes AI-generated code or text. Model/tool: Codex (GPT-5 and GPT-6) and GPT-5.6 Sol.

Screenshots / test output

Before After
Plugin repeater image shown as a large preview with hover-only actions Plugin repeater image shown as a compact card with visible Replace and Remove actions

A disposable local site was checked in English and Arabic, including a real post save and reload.

  • Root build, package typecheck, formatting, quick lint, and type-aware lint passed. Type-aware lint reported zero diagnostics.
  • All 171 Block Kit tests and 90 targeted Chromium editor and media-picker tests passed. The public builder regression was also explicitly typechecked.
  • The new validator regression failed before the fix with Repeater sub-field type 'media_picker' is not allowed; the typed builder failed with TS2322. Both pass after the fix.
  • The docs build passed.

@changeset-bot

changeset-bot Bot commented Oct 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 24ea7a0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@emdash-cms/blocks Patch
@emdash-cms/admin Patch
emdash Patch
@emdash-cms/plugin-test Patch
@emdash-cms/plugin-embeds Patch
@emdash-cms/cloudflare Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/auth Patch
create-emdash Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
@emdash-cms/auth-atproto Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
docs 24ea7a0 Oct 27 2026, 05:17 PM

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach is sound. Widening the RepeaterSubField type and REPEATER_SUB_FIELD_TYPES in @emdash-cms/blocks lets repeaters declare media_picker sub-fields, which the Portable Text editor already renders. Nested sub-fields still go through validateElement, so the existing media_picker checks still apply. Both reviewers approved and found nothing that needs fixing. The one suggestion is a wording fix in the skill reference, because media_picker is also used in sandboxed field widgets. Review was static; neither reviewer ran tests, builds or lint.


Reviewed by anthropic/claude-opus-5-5 and openai/gpt-6.1-sol.

## Repeater and media picker elements

`repeater` and `media_picker` are admin-authoring elements, not ordinary fields in a sandboxed admin-page `form`.
`repeater` and `media_picker` are authoring elements for native plugin Portable Text block forms. They are not ordinary fields in a sandboxed admin-page `form`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] This line now describes repeater and media_picker as authoring elements for native plugin Portable Text block forms. That is accurate for repeater, but media_picker is also rendered in sandboxed plugin field widgets (BlockKitFieldWidget.tsx has a case "media_picker" that uses the same BlockKitMediaPickerField). Agents following this reference could conclude that a field widget can't use media_picker. Consider naming both surfaces, for example: "repeater is an authoring element for native plugin Portable Text block forms; media_picker works there and in plugin field widgets. Neither is an ordinary field in a sandboxed admin-page form."

@pkg-pr-new

pkg-pr-new Bot commented Oct 27, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/admin@4440

@emdash-cms/auth

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/auth@4440

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/auth-atproto@4440

@emdash-cms/blocks

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/blocks@4440

@emdash-cms/cloudflare

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/cloudflare@4440

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/contentful-to-portable-text@4440

emdash

npm i https://pkg.pr.new/emdash-cms/emdash@4440

create-emdash

npm i https://pkg.pr.new/emdash-cms/emdash/create-emdash@4440

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/gutenberg-to-portable-text@4440

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-cli@4440

@emdash-cms/plugin-test

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-test@4440

@emdash-cms/plugin-types

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-types@4440

@emdash-cms/registry-client

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-client@4440

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-lexicons@4440

@emdash-cms/registry-loader

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-loader@4440

@emdash-cms/registry-moderation

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-moderation@4440

@emdash-cms/registry-verification

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/registry-verification@4440

upgrade-emdash

npm i https://pkg.pr.new/emdash-cms/emdash/upgrade-emdash@4440

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/sandbox-workerd@4440

@emdash-cms/x402

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/x402@4440

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-ai-moderation@4440

@emdash-cms/plugin-ai-search

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-ai-search@4440

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-atproto@4440

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-audit-log@4440

@emdash-cms/plugin-color

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-color@4440

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-embeds@4440

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-field-kit@4440

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-forms@4440

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/emdash-cms/emdash/@emdash-cms/plugin-webhook-notifier@4440

commit: 24ea7a0

@cloudflare-workers-and-pages

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://noah-repeater-media-picker.try.emdashcms.com, https://noah-repeater-media-picker-emdash-playground.emdash-cms.workers.dev (commit 24ea7a0)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://bd008407.try.emdashcms.com, https://bd008407-emdash-playground.emdash-cms.workers.dev 24ea7a0 2026-10-27T17:17:54.965Z Visit the dashboard ↗

@danielmlr danielmlr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Noah!

I found a few small items, worth a look:

  1. The skill reference probably wants to name both places media_picker works, as emdashbot suggests on line 409. That line says it's only for Portable Text block forms, while the same file lists it for plugin field widgets further down.
  2. Field widgets use the same component, so they get the new card too. It'd be great to have a before and after screenshot of a plugin field widget with a media picker as well.
  3. The changeset could say the new card applies to plugin field widgets too. At the moment it mentions only repeaters.
  4. I think the card title wants to be Selected image for local media, like in ImageFieldRenderer. Uploaded files are stored under a generated ID, so for almost every image the title reads like 01JC8….jpg.
  5. The "maintains a minimum height so the container does not collapse" test can probably go. The minimum height is gone, and the test now repeats the two broken-image tests above it.

@emdashbot emdashbot Bot added review/needs-review No maintainer or bot review yet and removed review/needs-review No maintainer or bot review yet labels Oct 28, 2026
@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-review No maintainer or bot review yet labels Oct 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants