feat(admin): add direct multi-file media uploads - #2553
Conversation
🦋 Changeset detectedLatest commit: 44e0988 The changes in this PR will be included in the next version bump. This PR includes changesets to release 17 packages
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 |
Scope checkThis PR changes 1,801 lines across 11 files. Large PRs are harder to review and more likely to be closed without review. If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs. See CONTRIBUTING.md for contribution guidelines. |
🚀 Deploying Preview to Cloudflare 🚀Preview URL: https://feature-media-multi-upload.try.emdashcms.com (commit 44e0988)This URL reflects your latest Preview deploymentPreview Deployments by commit
|
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
docs | 44e0988 | Aug 19 2026, 03:03 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-cache | 44e0988 | Aug 19 2026, 03:05 PM |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/registry-verification
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-do | 44e0988 | Aug 19 2026, 03:04 PM |
Overlapping PRsThis PR modifies files that are also changed by other open PRs:
This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
There was a problem hiding this comment.
The multi-file upload dialog is a sensible, self-contained addition to the admin media library. The implementation is careful in most of the ways that matter: AbortSignal is threaded through the local/direct, signed-URL, and provider upload paths; object URLs are revoked in cleanup; the queue caps concurrency and guards against stale retries; and tests were added/updated for concurrency, cancellation, retry, drag overlay, and abort-signal plumbing. The one prior nit I flagged (a Copilot-referencing test comment) has been removed.
However, this is still a new user-facing admin feature, and the PR description itself continues to state that no matching maintainer-approved Discussion has been linked. That remains a process-level concern under AGENTS.md.
Code-level issues that still need resolution:
- File-type claims are inconsistent with the server. The docs Supported File Types table and the new upload dialog
acceptstring list.doc/.docx/.xls/.xlsx/.ppt/.pptx(and the docs also list.avif/.svg), butpackages/core/src/api/handlers/media-allowlist.tsonly allowsimage/png,image/jpeg,image/gif,image/webp,video/,audio/, andapplication/pdfby default. Uploads selected in the media library will be rejected withINVALID_TYPE. Either the server allowlist should be extended (with appropriate security review for SVG/office types) or the docs and dialog should be narrowed to match the actual allowed set. - Changeset bump type. A new backwards-compatible admin UI feature should be
minor, notpatch, per AGENTS.md.
I am downgrading the full-page drag overlay aria-hidden issue from the previous review to a suggestion: the overlay is pointer-only and decorative to some degree, but hiding the visible "Drop files to upload" instruction from screen readers means a screen-reader user who is dragging files gets no feedback during the operation. Removing aria-hidden (or pairing it with an aria-live announcement) would be safer.
I did not run the test suite, linter, or build; I reviewed statically. I checked the diff, full changed files, relevant server upload/allowlist code, and the existing docs for contradicting claims.
Findings
-
[needs fixing]
packages/admin/src/components/MediaUploadDialog.tsx:21The upload dialog advertises file types that the media-library server route rejects by default. The
acceptstring includes.doc,.docx,.xls,.xlsx,.ppt, and.pptx, butpackages/core/src/api/handlers/media-allowlist.tsonly allowsimage/png|jpeg|gif|webp,video/,audio/, andapplication/pdffor uploads without a field-specific allowlist (the media library never passesfieldId). Selecting any of the office-document extensions will produce anINVALID_TYPEerror after the upload starts. Either extend the server allowlist (with security review for office/SVG types) or make the client hint match what the server will actually accept.const MEDIA_UPLOAD_ACCEPT = "image/*,video/*,audio/*,.pdf"; -
[needs fixing]
docs/src/content/docs/guides/media-library.mdx:43-44The Supported File Types table claims support for formats the default server allowlist does not permit:
.avif,.svg, and all office-document extensions. Uploads of these will fail in the media library unless a field-specific allowlist happens to allow them, which is irrelevant to the library flow documented here. Update the table to match the server's default allowlist, or update the server allowlist and this table together.| Images | `.jpg`, `.jpeg`, `.png`, `.gif`, `.webp` | | Documents | `.pdf` | -
[needs fixing]
.changeset/direct-media-batches.md:2This changeset describes a new backwards-compatible admin UI feature. AGENTS.md states: "Use
patchfor bug fixes and small improvements, andminorfor new backwards-compatible features." A new multi-file upload dialog in@emdash-cms/adminshould be aminorbump."@emdash-cms/admin": minor -
[suggestion]
packages/admin/src/components/MediaLibrary.tsx:311The full-page drag overlay hides the visible "Drop files to upload" message from assistive technologies with
aria-hidden="true". Since drag-and-drop is a pointer operation, this may seem safe, but a screen-reader user who is also using a mouse will get no auditory feedback during the active drop state. Consider removingaria-hiddenfrom the overlay so the instruction is available to AT, or add a politearia-liveannouncement when a file drag becomes active.<div className="pointer-events-none fixed inset-0 z-50 bg-kumo-base/70 p-4 backdrop-blur-sm sm:p-8" >
There was a problem hiding this comment.
This is a sensible, self-contained addition to the admin media library. The new dialog handles multi-file selection, capped concurrent uploads, per-file cancellation, retry, and focus restoration, and the implementation is careful about resource cleanup and signal propagation.
Since the previous review, the three needs fixing issues have been resolved:
- Client accept string / docs file types. The new
LOCAL_MEDIA_UPLOAD_ACCEPTconstant and the docs Supported File Types table now matchpackages/core/src/api/handlers/media-allowlist.ts(explicit image types,video/*,audio/*,application/pdf). Office-document and SVG/AVIF claims have been removed. - Changeset bump type.
.changeset/direct-media-batches.mdnow correctly lists"@emdash-cms/admin": minorfor this new backwards-compatible feature. - Test comment. The Copilot-referencing comment in
MediaLibrary.test.tsxhas been removed.
One non-blocking suggestion remains (see finding). The new dialog has its own polite aria-live region, but that does not cover the full-page drag overlay.
Process note: the PR description still states that no maintainer-approved Discussion is linked, and the author explicitly lists that as a blocker to marking the draft ready. AGENTS.md requires a prior approved Discussion for a new user-facing admin feature. I am not escalating this to a code finding, but maintainers should resolve it before merge.
I did not run the test suite, linter, or build; I reviewed statically.
What does this PR do?
Adds a direct multi-file upload flow to the media library. The upload action opens a Kumo dialog with a responsive dropzone, and dragging files over the media page shows a full-page drop overlay. Files upload through a capped three-request queue with per-file previews, cancellation, failure states, and retry actions.
The existing local, same-origin streaming, signed R2/S3, deduplicated, and external-provider upload clients now accept
AbortSignalso cancellation stops in-flight work. The dialog is localized, RTL-safe, keyboard accessible, and verified in light and dark appearances down to a 320px viewport.Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change) — the full admin suite passes: 121 files, 1,502 testspnpm formathas been runmessages.pochanges are included.AI-generated code disclosure
Screenshots / test output
Verified locally in the simple demo:
Checks run:
pnpm typecheckpnpm lintpnpm formatandpnpm format:check@emdash-cms/adminVitest browser suite: 121 files, 1,502 tests passedpnpm --dir packages/admin buildpnpm --dir docs build