Skip to content

feat(admin): add direct multi-file media uploads - #2553

Merged
khoinguyenpham04 merged 14 commits into
mainfrom
feature/media-multi-upload
Aug 19, 2026
Merged

feat(admin): add direct multi-file media uploads#2553
khoinguyenpham04 merged 14 commits into
mainfrom
feature/media-multi-upload

Conversation

@khoinguyenpham04

@khoinguyenpham04 khoinguyenpham04 commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

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 AbortSignal so 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

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change) — the full admin suite passes: 121 files, 1,502 tests
  • 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). No messages.po changes are included.
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion — pending maintainer-approved Discussion

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: OpenAI Codex GPT-5.6

Screenshots / test output

Verified locally in the simple demo:

  • Empty upload dialog from the header and empty-state actions
  • Full-page drag overlay
  • Successful three-file concurrent upload and media-list refresh
  • Dark and light appearances
  • 320px responsive layout
  • Arabic/RTL mirroring
  • Keyboard focus order and focus restoration

Checks run:

  • pnpm typecheck
  • pnpm lint
  • pnpm format and pnpm format:check
  • Full @emdash-cms/admin Vitest browser suite: 121 files, 1,502 tests passed
  • pnpm --dir packages/admin build
  • pnpm --dir docs build
  • Adversarial second-opinion review against the merge base: clean, no findings or remaining nits

@changeset-bot

changeset-bot Bot commented Aug 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 44e0988

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

This PR includes changesets to release 17 packages
Name Type
@emdash-cms/admin Minor
emdash Minor
@emdash-cms/cloudflare Minor
@emdash-cms/sandbox-workerd Patch
@emdash-cms/plugin-mcp-smoke Major
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/auth Minor
@emdash-cms/blocks Minor
@emdash-cms/gutenberg-to-portable-text Minor
@emdash-cms/x402 Minor
create-emdash Minor
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds 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

@github-actions

Copy link
Copy Markdown
Contributor

Scope check

This 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.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 19, 2026

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://feature-media-multi-upload.try.emdashcms.com (commit 44e0988)

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://0a3c9649.try.emdashcms.com 44e0988 2026-08-19T15:04:14.449Z Visit the dashboard ↗
  • Build: Success ✅

View logs ↗
bc246a7 2026-08-19T12:59:53.755Z Visit the dashboard ↗
  • Build: Success ✅

View logs ↗
c3033a5 2026-08-19T12:50:26.293Z Visit the dashboard ↗
  • Build: Success ✅

View logs ↗
355940c 2026-08-19T12:45:12.128Z Visit the dashboard ↗
  • Build: Success ✅

View logs ↗
74fbd01 2026-08-19T11:05:33.091Z Visit the dashboard ↗
  • Build: Success ✅

View logs ↗
cf97673 2026-08-19T10:21:58.149Z Visit the dashboard ↗
  • Build: Success ✅

View logs ↗
788ff6b 2026-08-19T09:39:05.120Z Visit the dashboard ↗
  • Build: In progress 🔵

View logs ↗
2845347 2026-08-19T09:32:11.816Z View logs ↗
  • Build: Success ✅

View logs ↗
3b1c2d4 2026-08-19T07:49:06.938Z Visit the dashboard ↗

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 19, 2026

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 44e0988 Aug 19 2026, 03:03 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 19, 2026

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
emdash-demo-cache 44e0988 Aug 19 2026, 03:05 PM

@pkg-pr-new

pkg-pr-new Bot commented Aug 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

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

@emdash-cms/auth

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

@emdash-cms/auth-atproto

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

@emdash-cms/blocks

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

@emdash-cms/cloudflare

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

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

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

emdash

npm i https://pkg.pr.new/emdash@2553

create-emdash

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

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

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

@emdash-cms/plugin-cli

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

@emdash-cms/plugin-types

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

@emdash-cms/registry-client

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

@emdash-cms/registry-lexicons

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

@emdash-cms/registry-verification

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

@emdash-cms/sandbox-workerd

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

@emdash-cms/x402

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

@emdash-cms/plugin-ai-moderation

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

@emdash-cms/plugin-atproto

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

@emdash-cms/plugin-audit-log

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

@emdash-cms/plugin-color

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

@emdash-cms/plugin-embeds

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

@emdash-cms/plugin-field-kit

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

@emdash-cms/plugin-forms

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

@emdash-cms/plugin-webhook-notifier

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

commit: 44e0988

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 19, 2026

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
emdash-demo-do 44e0988 Aug 19 2026, 03:04 PM

@khoinguyenpham04
khoinguyenpham04 marked this pull request as ready for review August 19, 2026 09:33
@github-actions github-actions Bot added review/needs-review No maintainer or bot review yet cla: signed labels Aug 19, 2026
@khoinguyenpham04 khoinguyenpham04 added the bot:review Trigger an emdashbot code review on this PR label Aug 19, 2026
emdashbot[bot]

This comment was marked as outdated.

@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Aug 19, 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 Aug 19, 2026
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/awaiting-author Reviewed; waiting on the author to respond labels Aug 19, 2026
@khoinguyenpham04 khoinguyenpham04 added the bot:review Trigger an emdashbot code review on this PR label Aug 19, 2026
@github-actions github-actions Bot removed the review/awaiting-author Reviewed; waiting on the author to respond label Aug 19, 2026
@ascorbic ascorbic added the bot:review Trigger an emdashbot code review on this PR label Aug 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Aug 19, 2026
@khoinguyenpham04 khoinguyenpham04 added the bot:review Trigger an emdashbot code review on this PR label Aug 19, 2026

@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 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 accept string list .doc/.docx/.xls/.xlsx/.ppt/.pptx (and the docs also list .avif/.svg), but packages/core/src/api/handlers/media-allowlist.ts only allows image/png, image/jpeg, image/gif, image/webp, video/, audio/, and application/pdf by default. Uploads selected in the media library will be rejected with INVALID_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, not patch, 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:21

    The upload dialog advertises file types that the media-library server route rejects by default. The accept string includes .doc, .docx, .xls, .xlsx, .ppt, and .pptx, but packages/core/src/api/handlers/media-allowlist.ts only allows image/png|jpeg|gif|webp, video/, audio/, and application/pdf for uploads without a field-specific allowlist (the media library never passes fieldId). Selecting any of the office-document extensions will produce an INVALID_TYPE error 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-44

    The 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:2

    This changeset describes a new backwards-compatible admin UI feature. AGENTS.md states: "Use patch for bug fixes and small improvements, and minor for new backwards-compatible features." A new multi-file upload dialog in @emdash-cms/admin should be a minor bump.

    "@emdash-cms/admin": minor
    
  • [suggestion] packages/admin/src/components/MediaLibrary.tsx:311

    The 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 removing aria-hidden from the overlay so the instruction is available to AT, or add a polite aria-live announcement 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"
    				>
    

@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-rereview Author pushed changes since the last review labels Aug 19, 2026
@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Aug 19, 2026
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/awaiting-author Reviewed; waiting on the author to respond labels Aug 19, 2026
@khoinguyenpham04 khoinguyenpham04 added the bot:review Trigger an emdashbot code review on this PR label Aug 19, 2026

@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.

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_ACCEPT constant and the docs Supported File Types table now match packages/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.md now correctly lists "@emdash-cms/admin": minor for this new backwards-compatible feature.
  • Test comment. The Copilot-referencing comment in MediaLibrary.test.tsx has 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.

Comment thread packages/admin/src/components/MediaLibrary.tsx
@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Aug 19, 2026
@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-rereview Author pushed changes since the last review labels Aug 19, 2026
@github-actions github-actions Bot added review/approved Approved; no new commits since and removed review/awaiting-author Reviewed; waiting on the author to respond labels Aug 19, 2026
@khoinguyenpham04
khoinguyenpham04 merged commit ffaadc4 into main Aug 19, 2026
58 checks passed
@khoinguyenpham04
khoinguyenpham04 deleted the feature/media-multi-upload branch August 19, 2026 16:17
@emdashbot emdashbot Bot mentioned this pull request Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants