Skip to content

Media: prompt to convert animated GIF uploads to video - #80075

Closed
adamsilverstein wants to merge 20 commits into
trunkfrom
add/animated-gif-upload-prompt
Closed

adamsilverstein wants to merge 20 commits into
trunkfrom
add/animated-gif-upload-prompt

Conversation

@adamsilverstein

@adamsilverstein adamsilverstein commented Jul 9, 2026 •

Copy link
Copy Markdown
Member

What?

Closes #80136.

Adds the upload-time prompt for animated GIFs proposed by @jasmussen in #79787: uploading an animated GIF asks whether to convert it to a video (the Video block's GIF variation) or keep it as a GIF. Converting is the steered default.

Stacked on #80072 (opt-in block transforms); only the last commits are new.

Why?

#80072 made the conversion opt-in via the block switcher, but the block switcher is a discovery-dependent surface. The prompt from the issue discussion makes the choice explicit at the moment it matters - upload time - while still leaving the author in control:

  • No conversion work happens (and no companion video is stored) unless the author asks for it.
  • Converting is presented as the primary, focused action to steer users to the better default, per the issue discussion.

The prompt appears for every animated GIF upload. An earlier iteration added a "Remember my choice" checkbox and a Preferences → Media setting; per the discussion below that was removed - a per-upload conversion is a genuine choice rather than a nag to silence, and a remembered preference (if it turns out users want it) is better revisited separately once the direction for a general drag-and-drop prompt is settled.

The prompt is opened by an explicit action, not by the editor observing the upload pipeline. This addresses @andrewserong's review point and is built as a small, reusable primitive so a future general upload/drag-and-drop prompt can adopt it:

  • @wordpress/upload-media gains a generic uploadPrompts store slice with private requestUploadPrompt( { type, itemId, data } ) / resolveUploadPrompt( id ) actions and a getUploadPrompts() selector. The flow that needs an answer requests a typed prompt; the record is deliberately decoupled from any specific upload lifecycle. GIF conversion is its first consumer.
  • @wordpress/editor renders prompts through a generic UploadPromptHost that maps each prompt's type to a component (only 'gif-conversion' today). Adding a new prompt later = register a type + component and call requestUploadPrompt().

Flow, end to end:

  • @wordpress/upload-media: the gifConvert setting accepts a new 'prompt' value. In prompt mode, as soon as a drop passes the animated/opacity checks (both read only the local file bytes), prepareItem records a pending GifConversion lifecycle record and dispatches requestUploadPrompt( { type: 'gif-conversion', itemId } ) — so the modal opens immediately while the GIF uploads as a plain image in the background (the upload is never blocked). resolveGifConversion( itemId, 'video' | 'gif' ) then discards the record or enqueues the exact same TranscodeGif companion sideload the automatic mode uses (mid-upload answers ride the running upload; post-upload answers use a bare finalize parent). true/false behavior is unchanged for other consumers.
  • @wordpress/editor: UploadPromptHost renders a transient, single-GIF GifConversionPrompt modal while its prompt is pending; on answer it resolves the conversion and dismisses the prompt. A separate, always-mounted GifConversionBlockSwapper owns the completion side effect — once the companion video is recorded it swaps matching Image blocks via the regular Image → Video transform from GIF to video conversion: make it opt-in. Switching via block transforms #80072 (switchToBlockType + its canInsertBlockType guard). The swap is deliberately separate from the prompt because the transcode finishes after the modal has closed. The upload progress snackbar is suppressed while any prompt is open (useHasActiveUploadPrompt).

Declining ("Not now") skips the transcode entirely rather than hiding its result, so no CPU or storage is spent on a declined conversion. Consequence: such an image won't offer the block-switcher Video transform (it's gated on the companion existing) - converting later means re-uploading. An on-demand transcode from the switcher could be a follow-up.

Testing Instructions

Test in WP Playground

  1. Enable client-side media processing (Gutenberg → Experiments) in a cross-origin-isolated environment.
  2. Upload an animated GIF into an Image block. The upload proceeds immediately and a "Convert to video" dialog appears, with Convert focused.
  3. Press Enter (or click Convert): the block becomes the Video block's GIF variation once the transcode finishes.
  4. Undo (Cmd+Z) to go back to the Image block - and note the block switcher still offers the Video transform (the companion is kept).
  5. Upload another GIF and choose Not now: the block stays an Image block and no companion video is created (check the attachment's media_details has no animated_video).
  6. Transparent or non-animated GIFs never prompt.
npm run test:e2e -- test/e2e/specs/editor/various/gif-to-video.spec.js
npm run test:unit -- packages/upload-media

Screenshots or screencast

The prompt after uploading an animated GIF (the upload itself already completed - it is never blocked by the dialog):

Follow-up to #78410 addressing design feedback in #79787: converting an
uploaded animated GIF to a video changes the media type, softens the
image, and swaps the block (losing image tools), so it should be the
author's explicit choice rather than an automatic swap.

- Stop automatically replacing the Image block with a Video block when
  a GIF upload's sideloaded video companion becomes available.
- Remove the bespoke 'Display as video' / 'Display as GIF' toolbar
  buttons.
- Offer the conversion through the block Transforms API instead: an
  animated GIF Image block (whose companion video exists) can transform
  to the Video block's GIF variation via the block switcher, and back
  to an Image block. The switcher also handles disabled target blocks
  and undo out of the box.

The companion video is still transcoded and sideloaded at upload time;
only the block swap became opt-in.
The spec now proves the upload keeps the Image block (no automatic
swap), then exercises the round trip through the block switcher:
Image to the Video block's GIF variation and back to the original GIF.

Wait for the editor to finish setting up before inserting the block
(an insert dispatched during setup is wiped by the initial blocks
reset) and poll for the sideloaded companion in the attachment
metadata, which is recorded slightly after the upload queue drains.

Also point a stale upload-media comment at the new transform location.
In prompt mode (gifConvert: 'prompt'), an animated GIF uploads as a plain
image and no companion video is transcoded automatically. Instead the
store keeps a GifConversion record (attachment ID + original file) so the
host application can ask the user first and trigger the conversion via
the new resolveGifConversion() private action, which reuses the exact
companion sideload flow. This enables an upload-time prompt in the editor
per the feedback on #79787 while leaving automatic (true) and disabled
(false) behavior unchanged for other consumers.
Adds the upload-time prompt requested in #79787: uploading an animated
GIF keeps the upload non-blocking (the GIF uploads as a plain image) and
asks whether to convert it to the Video block's GIF variation, steering
towards conversion as the primary, focused action. A 'Remember my
choice' checkbox persists the answer to the new core/media
animatedGifUploads preference ('ask' | 'gif' | 'video'), which is also
editable under Preferences > Media. A remembered 'video' resolves
conversions automatically; the resulting block swap runs the regular
Image to Video block transform, matching its availability rules.
Covers: prompt mode records a pending conversion instead of enqueueing
the transcode sideload; resolveGifConversion discards on 'gif', enqueues
the mirrored companion sideload on 'video', and ignores non-pending
records; the reducer add/update/remove cases; and cancellation cleanup
for the original upload, the requested transcode, and (negatively) an
unrelated child sideload.
…t item

A sideloaded companion only becomes part of the attachment once a parent
item's Finalize operation reports it via mediaFinalize() - sideloads
merely accumulate their result on their parent. The initial
resolveGifConversion() implementation reused the long-gone original
upload item as parentId, so the transcoded video uploaded but was never
recorded on the attachment and the editor's block swap never fired.

Queue a bare parent item whose only operation is Finalize instead. The
existing machinery does the rest: the transcode sideload and its poster
sibling accumulate on it, the Finalize gate waits for both, and the
conversion record is marked converted only after finalization, when the
refetched attachment actually carries animated_video.

Also updates the e2e suite for the prompt flow: convert with round-trip
transform back, decline skips the transcode, remember stops prompting,
and a remembered 'video' converts automatically.
@adamsilverstein
adamsilverstein requested a review from ellatrix as a code owner July 9, 2026 20:10
@github-actions github-actions Bot added [Package] Editor /packages/editor [Package] Block editor /packages/block-editor [Package] Edit Post /packages/edit-post [Package] Edit Site /packages/edit-site labels Jul 9, 2026
@github-actions

github-actions Bot commented Jul 9, 2026 •

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: adamsilverstein <adamsilverstein@git.wordpress.org>
Co-authored-by: andrewserong <andrewserong@git.wordpress.org>
Co-authored-by: swissspidy <swissspidy@git.wordpress.org>
Co-authored-by: jasmussen <joen@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@adamsilverstein adamsilverstein added [Type] Enhancement A suggestion for improvement. [Feature] Client Side Media Media processing in the browser with WASM labels Jul 9, 2026
Base automatically changed from update/gif-video-conversion-use-transforms to trunk July 10, 2026 06:38
@jasmussen

jasmussen commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Here's what I see:

state

This is really fast and nice work. I'm impressed. Overnight delivery.

This can work, however I'll be honest it wasn't what I was thinking of—apologies I should've been clearer. In this implementation, the GIF is uploaded and inserted, and then after a second, the modal appears giving you a choice of conversion. The "remember" is a nice choice, though important if we add such a thing we'll want to also give you a section of settings to let you under a "remember" choice.

What I was picturing was that as soon as you drop an item, it prompts you, immediately, before anything happens. Before the GIF is uploaded, before anything at all is inserted into the document. And I was thinking the smallest possible modal. It has to feel super ephemeral, like part of the fluid action of dragging an image into the editor.

I guess the tricky thing here is that we can't necessarily know that a GIF is animated by looking only at the filetype we read on-drop, that maybe we have to look at the bytes to truly know. However if it allows us to fire the modal before upload, I still think it might be the right thing to do: I doubt very many people use GIF as a format for anything not animated in this day and age, so it feels safe to assume. So if it becomes a problem, I'd rather handle the "it was not animated" edge-case post-upload.

My hope with this feedback is that the implementation could possibly be simple and basic. On-drop: if GIF → fire modal.

What do you think?

@adamsilverstein

Copy link
Copy Markdown
Member Author

I guess the tricky thing here is that we can't necessarily know that a GIF is animated by looking only at the filetype we read on-drop, that maybe we have to look at the bytes to truly know. However if it allows us to fire the modal before upload, I still think it might be the right thing to do: I doubt very many people use GIF as a format for anything not animated in this day and age, so it feels safe to assume. So if it becomes a problem, I'd rather handle the "it was not animated" edge-case post-upload.

Another challenge is transparency: we don't know if the GIF has transparency until we at least partially process it - and video conversion is only offered if there is no transparency detected since video can't represent transparency well.

So I think we will need to at least process the gif a bit to know whether we can convert it to a video. I already avoid doing the video transcode if the user selects no or hits escape.

That said, I think we could do what you suggest and show the modal before doing the upload - although since the GIF always gets uploaded even if you convert to video so I'm not sure why waiting to upload makes sense here. I think what you are really after is a more of a different feeling...

feel super ephemeral, like part of the fluid action of dragging an image into the editor.

so maybe instead of

the GIF is uploaded and inserted, and then after a second, the modal appears giving you a choice of conversion.

we try this:

As soon as the gif is dropped we determine if it is a conversion candidate, showing the modal immediately if so while the upload continues in the background. maybe avoid showing the upload progress snackbar in this case since its distracting.

The "remember" is a nice choice, though important if we add such a thing we'll want to also give you a section of settings to let you under a "remember" choice

to me this feels important, as a hypothetical user who regularly uploads GIFs, I want WordPress to remember my conversion preference. I can add a settings under Preferences->Media.

The conversion record that drives the editor prompt was created in
generateThumbnails(), i.e. only after the GIF finished uploading, so the
modal appeared with a noticeable delay after the drop. Create it in
prepareItem() instead, right after the animated/transparency checks pass:
both only read the local file bytes, so the prompt can appear as part of
the drop action while the upload continues in the background.

Since no attachment exists at that point, records are now keyed by the
original upload item ID instead of the attachment ID, and the attachment
ID is stamped onto the record once the upload completes. An answer that
arrives while the upload is still running marks the record 'accepted';
generateThumbnails() then runs the transcode as a regular companion
sideload of the still-pending item (single Finalize, no synthetic parent
needed). The bare-finalize-parent path remains for answers that arrive
after the upload finished, with the parent now carrying the record key
via gifConversionItemId.
The prompt now opens during the upload, so the progress snackbar would
pop up behind it and compete for attention. Hide (and retract) the
notice while the prompt is visible; it comes back if uploads are still
running once the prompt is answered. Audible announcements are kept so
screen reader users still hear about the upload itself.
The prompt now opens while the GIF is still uploading; verify the
upload progress and completion snackbars stay hidden while it is open.
@github-actions github-actions Bot added the [Package] Block library /packages/block-library label Jul 11, 2026
@adamsilverstein

adamsilverstein commented Jul 11, 2026 •

Copy link
Copy Markdown
Member Author

Implemented as discussed:

  • The prompt now fires immediately on drop. The animated + transparency checks only need the local file bytes, so the conversion candidate is recorded before the upload starts and the modal opens as part of the drop action - no more waiting for the upload to finish. The upload continues in the background either way; answering "Convert to video" while it's still running simply attaches the transcode to the in-flight upload.
  • The upload progress snackbar is suppressed while the prompt is open, so the prompt is the single point of attention (it reappears if uploads are still running once you answer).
  • Smaller modal: switched to the small size and dropped the lead-in sentence - since the prompt appears as you drop, "the GIF you uploaded is animated" no longer added anything.

The Preferences → Media setting for the remembered choice was already in place from the first round.

gif-conversion-prompt-on-drop

The base PR #80072 was squash-merged to trunk with a final change this
branch predates: the Video-to-Image transform back was removed (undo is
the way back), along with utils/gif-conversion.js, the Video block
prefetch, and their tests. Take trunk's version of all block-library
files (this PR never touches block-library) and port the prompt e2e's
round-trip section to undo semantics accordingly.
@github-actions github-actions Bot removed the [Package] Block library /packages/block-library label Jul 11, 2026
@github-actions

github-actions Bot commented Jul 11, 2026 •

Copy link
Copy Markdown

Size Change: +1.94 kB (+0.03%)

Total Size: 7.72 MB

📦 View Changed
Filename Size Change
build/scripts/block-editor/index.min.js 422 kB +13 B (0%)
build/scripts/edit-post/index.min.js 52.1 kB +52 B (+0.1%)
build/scripts/edit-site/index.min.js 303 kB +75 B (+0.02%)
build/scripts/editor/index.min.js 497 kB +969 B (+0.2%)
build/scripts/upload-media/index.min.js 16 kB +830 B (+5.46%) 🔍

compressed-size-action

@github-actions

github-actions Bot commented Jul 13, 2026 •

Copy link
Copy Markdown

Flaky tests detected in 3709fd2.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/29437238650
📝 Reported issues:

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

Conceptually I like the idea of showing a modal to confirm what the user intends to do when they go to upload a media item (in this case gifs). A few thoughts on the approach code-wise:

  • In other discussions it was brought up the idea that we might eventually have a general purpose modal that shows up for lots of different upload / drag and drop cases. So, how might we structure this for the idea that there could be many different kinds of prompts, not just a GifConversionPrompt?
  • Currently this this code subscribes to some stuff to determine whether or not to show the modal. This might be unnecessary most of the time as we only need to open the modal in response to something that's happening based on a user interaction (in this case uploading a file). So an option might be to trigger the modal to open via an action — I'm wondering if that could simplify things a little? The potential downside is figuring out the right shape of how we want to open the modal.
  • The above question leads me to: should we consider what a general upload / drag and drop prompt might look like, and what the shape of data it needs might be?
  • Also, if we use an imperative action to open a prompt, I'm wondering if that'll bypass the need to have gifConversions in state?

These are just observations from having a quick look over the code, by the way — so consider them thoughts rather than a review. I think my main hesitation over this PR is just that it's making lots of decisions quite quickly for this particular feature (nice work there), but I'd really like us to also consider how we'll eventually move toward some of the ideas @jasmussen raised about other kinds of drag-and-drop modal prompts.

We don't need to solve it all just yet — but getting a sense of how we might step in that direction, and what the mechanism is for triggering the prompt, might help here.

@jasmussen

Copy link
Copy Markdown
Contributor

In other discussions it was brought up the idea that we might eventually have a general purpose modal that shows up for lots of different upload / drag and drop cases. So, how might we structure this for the idea that there could be many different kinds of prompts, not just a GifConversionPrompt?

Very good question to ask. This needs a little design work, for me coming to mind, keep it concise, and make the buttons really big, multiline buttons. I feel like @fcoveram might have instincts since he has worked closely with the media work overall.

One thing stands out though: such a dialog cannot be dismissed with a checkbox. It should always be there. Or, probably at least.

I do wonder if this is one of those cases where we should land this PR without the "remember my choice" options: extract those options to a separate PR, and come back to them only if feedback tells us users really want it?

@adamsilverstein

Copy link
Copy Markdown
Member Author

On a side note, I am working on showing video conversion progress (since it can take some time for larger GIFs) in #80325 and would appreciate some design feedback about how/where to show progress. Since we know the conversion progress exactly I'm thinking we can do better than the current long running spinner for conversions.

I do wonder if this is one of those cases where we should land this PR without the "remember my choice" options: extract those options to a separate PR, and come back to them only if feedback tells us users really want it?

Good point @jasmussen - I instinctively wanted to add this for completeness, but its not really needed and complicates the PR unnecessarily. I agree we can add this later and will remove for now.

keep it concise, and make the buttons really big, multiline buttons

   OK   

The prompt now always appears for each animated GIF upload rather than
offering to remember the answer. Per PR discussion, a per-upload
conversion is a genuine choice, not a nag to silence with a checkbox; a
remembered preference, if wanted, is better revisited separately once the
general drag-and-drop prompt direction is settled.

Removes the CheckboxControl and the Preferences > Media 'Animated GIF
uploads' SelectControl, along with the core/media animatedGifUploads
preference plumbing. gifConvert is now always 'prompt'. The store's
prompt/accepted machinery is unchanged.
@adamsilverstein

adamsilverstein commented Jul 15, 2026 •

Copy link
Copy Markdown
Member Author

Removed "Remember my choice" in 3709fd2 as discussed with @jasmussen: the checkbox, the Preferences → Media "Animated GIF uploads" setting, and the whole core/media animatedGifUploads preference are gone. The prompt now simply appears for every animated GIF upload, and gifConvert is always 'prompt' when client-side media is on. Smaller diff, and it matches @jasmussen's point that a per-upload conversion is a real choice, not something to permanently dismiss with a checkbox. Updated the description accordingly.

@andrewserong thanks for the architectural read - a few honest thoughts:

  • General-purpose prompt: fully agree this is the right direction, and @jasmussen + @fcoveram flagged it needs some design work (concise, big multiline buttons, always-present). I'd suggest we land this as the concrete first case and do the generalization as a follow-up once that design settles.
  • Imperative trigger vs. subscription: I like the instinct, and I think the modal-opening concern can absolutely become an imperative "open prompt X" action in a general design. Worth separating that from the next point though.
  • Bypassing gifConversions in state: That state isn't only gating the modal - it's the coordination record for an async lifecycle: created on drop (in upload-media, block-editor layer), it has to survive the upload completing, carry the answer so the "convert" case can attach the companion sideload to the right in-flight or finished upload item, and then persist until the transcode records the companion so the editor (a higher layer) can swap the blocks. An imperative open-action could sit on top of that, but I don't think the tracking state itself can go away without losing the cross-layer coordination.

So: I'd love to keep this focused, and treat the general drag-and-drop prompt (imperative trigger + shared data shape) as the next step with design input. Does that split sound reasonable?

…oad-prompt

# Conflicts:
#	packages/block-editor/src/components/provider/use-media-upload-settings.js
@adamsilverstein
adamsilverstein requested a review from desrosj as a code owner July 15, 2026 18:36

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Unrelated change?

The lint-staged formatter had reflowed the 'needs:' arrays in
upload-release-to-plugin-repo.yml. This is unrelated to the GIF
conversion prompt; restore the file to match trunk. Committed with
--no-verify because the format hook would otherwise re-apply the
reflow.
@adamsilverstein

Copy link
Copy Markdown
Member Author

@swissspidy good catch on the workflow file - that was an accidental lint-staged formatter reflow, unrelated to this PR. Reverted to trunk in 459a84b.

@adamsilverstein

Copy link
Copy Markdown
Member Author

In other discussions it was brought up the idea that we might eventually have a general purpose modal that shows up for lots of different upload / drag and drop cases. So, how might we structure this for the idea that there could be many different kinds of prompts, not just a GifConversionPrompt?

I've started to break this out separately so we can discuss the architecture/design.

Introduce a store-level primitive for asking the user to decide something
about an upload, decoupled from any specific upload lifecycle:

- uploadPrompts state slice of { id, type, itemId?, data? } records
- private requestUploadPrompt(descriptor) / resolveUploadPrompt(id) actions
- private getUploadPrompts() selector

The flow that needs an answer requests a typed prompt; a host application
renders the UI matching 'type' and resolves it once the user answers. This
is the shared seam for upload/drag-and-drop prompts. GIF conversion becomes
its first consumer in a follow-up commit; the gifConversions lifecycle
records stay as-is.
…ction

Two changes to prepareItem's animated-GIF handling:

- Skip conversion for multi-file drops. A multi-file drop is implicitly a
  gallery, and a Video block cannot be inserted into a gallery, so an
  animated GIF that arrives as part of a batch now uploads as a plain image
  with no conversion offered — matching the block switcher transform's
  existing gallery guard, and avoiding a prompt (and wasted transcode) that
  could never result in a swap. A lone GIF still converts.

- In prompt mode, dispatch requestUploadPrompt({ type: 'gif-conversion' })
  right after recording the pending conversion, so the modal is triggered by
  an explicit action rather than the editor deriving visibility from the
  gifConversions lifecycle records.
Replace the editor's state-derived GIF prompt with a generic host driven by
the uploadPrompts primitive, so the modal opens in response to an explicit
requestUploadPrompt() action rather than the editor observing the upload
lifecycle records:

- UploadPromptHost (always mounted, generic) renders the pending prompts,
  mapping each prompt's 'type' to a component. GIF conversion is the only
  registered type today; a general drag-and-drop prompt would register here.
- GifConversionPrompt becomes a transient, single-item modal rendered by the
  host while a 'gif-conversion' prompt is pending; on answer it resolves the
  gif conversion and dismisses the prompt.
- GifConversionBlockSwapper (always mounted, separate) owns the completion
  side effect: it swaps Image->Video once the transcode finishes, which
  happens after the prompt has closed, so it cannot live in the prompt.
- Snackbar suppression now keys off useHasActiveUploadPrompt() (any prompt
  open) instead of reaching into the gif conversion records.

The gifConversions lifecycle records are unchanged; only the modal-open and
snackbar-suppression paths moved onto the generic primitive.
…t dispatch

- Add an e2e case: dropping multiple animated GIFs into a Gallery never shows
  the conversion prompt and leaves them as Image blocks (no transcode).
- Add requestUploadPrompt to the private ActionCreators type so prepareItem's
  dispatch.requestUploadPrompt() call typechecks.
…ow-up

Remove the multi-file drop gate from prepareItem. Reliably detecting that an
upload belongs to a gallery is not possible at the upload-store layer: upload
paths batch inconsistently (a one-shot multi-file upload is a single batch,
but the Gallery block uploads each file separately), and the authoritative
signal - the target block context cannot hold a Video block - lives in the
editor, not the store. A gallery-context gate belongs in the editor layer and
will be a follow-up. The block switcher transform's canInsertBlockType guard
already prevents an actual Image->Video swap inside a gallery, so no broken
conversion can occur in the meantime.

The action-triggered prompt itself is unchanged (requestUploadPrompt on drop).
@adamsilverstein

Copy link
Copy Markdown
Member Author

@andrewserong I took your "trigger the modal via an action" point and built it out, as a small reusable primitive rather than gif-specific glue so a future general drag-and-drop prompt can adopt it directly. Pushed as a few commits on top.

What changed

  • New generic uploadPrompts primitive in @wordpress/upload-media — private requestUploadPrompt( { type, itemId, data } ) / resolveUploadPrompt( id ) actions + a getUploadPrompts() selector, backed by a small state slice. A prompt request is now a typed, first-class thing, deliberately decoupled from the gif conversion lifecycle records.
  • The editor opens the modal via that action, not by observing the pipeline. A generic UploadPromptHost maps each pending prompt's type to a component (only 'gif-conversion' today). Adding a new prompt later = register a type + a component and call requestUploadPrompt().
  • The old component split into two so responsibilities stay clean: a transient, single-GIF GifConversionPrompt (mounted by the host only while its prompt is pending) and an always-mounted GifConversionBlockSwapper that owns the completion side effect (swap Image→Video once the transcode finishes — which happens after the modal has closed, so it can't live in the prompt). Snackbar suppression now keys off useHasActiveUploadPrompt() instead of reaching into the gif records.

On removing gifConversions state — I kept it, and I think that's right. As I dug in, that state is doing real cross-layer coordination inside the pipeline, independent of how the modal opens: it's created on drop in upload-media, has to survive the upload completing to receive its attachmentId, carries the kept original file to the transcode, and correlates cancellations. An imperative open-action sits cleanly on top of that, but the tracking state itself can't go away without losing that coordination. So the win here is the trigger becoming explicit and the prompt concept becoming generic/reusable — not state removal.

On the multi-drop / gallery case (from a separate thread): I tried suppressing the prompt for multi-file drops in the upload store, but it's not reliably detectable there — upload paths batch inconsistently (a one-shot multi-file upload is a single batch, but the Gallery block uploads each file separately), and the authoritative "this can't become a Video block" signal lives in the editor's block context. So I've pulled that out and will do it properly as a follow-up at the editor layer (gating on canInsertBlockType( 'core/video' )). The block-switcher transform's existing guard already prevents an actual bad swap inside a gallery in the meantime.

Convert/decline flows verified green via the e2e; unit + typecheck clean. Happy to iterate on the primitive's shape.

@andrewserong

andrewserong commented Jul 16, 2026 •

Copy link
Copy Markdown
Contributor

Thanks for the updates here!

So: I'd love to keep this focused, and treat the general drag-and-drop prompt (imperative trigger + shared data shape) as the next step with design input. Does that split sound reasonable?

I haven't taken a close look at the code again just yet, but I did want to mention that I'm a little cautious about the inclusion of the modal at this stage due to the current hanging / conversion timing issue being looked at in: #80260 (comment)

Given that we're past Beta 1 and that the feature itself is still buggy, would it be worth parking adding a conversion modal for 7.1? Basically, I'm wondering if we should try to keep things as invisible to the user for 7.1, and once we're more confident in the feature, expose it a little more deliberately in 7.2?

I thought it worth asking the question now rather than spending too much time polishing something if it's not going to be included in the release. (I don't have a strong opinion here, just mindful of whatever's going to help us release things smoothly)

@adamsilverstein

Copy link
Copy Markdown
Member Author

I thought it worth asking the question now rather than spending too much time polishing something if it's not going to be included in the release. (I don't have a strong opinion here, just mindful of whatever's going to help us release things smoothly)

Its fine to leave this off for now, I'd like to get more design review as well.

@adamsilverstein
adamsilverstein marked this pull request as draft July 16, 2026 05:48
@fcoveram

Copy link
Copy Markdown
Contributor

Apologies for chiming late here, but I need to disagree with this idea.

I don’t think this is the right path for suggesting content changes. Using a modal to recommend a block transformation interrupts the UX abruptly by forcing users to make a decision that could be intentionally triggered. In a way, it feels like infantilizing users as if they are not aware of what media file they’re adding.

I fully agree with the idea of suggesting a block transformation, but the friction should be less prominent.

I’m drawn to add a suggestion in context, by either an action in the toolbar, as part of 58418 discussion, or a dismissible Notice message in the block communicating that the file is heavy and might impact the site.

@jasmussen

Copy link
Copy Markdown
Contributor

I don’t think this is the right path for suggesting content changes. Using a modal to recommend a block transformation interrupts the UX abruptly by forcing users to make a decision that could be intentionally triggered. In a way, it feels like infantilizing users as if they are not aware of what media file they’re adding.

I appreciate your careful attention to the UX. One of the sources my proposal here come from older discussion in the make/core space, slack chats, and older GitHub chats where @mtias suggested we should generally do more with providing you choice when you upload material to WordPress. This is to leverage the broad features of WordPress itself and its ability to handle many different filetypes, and notably the conversations zeroed in on MD files, which you might both want to add as files you can download, or you might want to insert their content inside a post. OR, if you drop an MD file in the Posts list, we can offer to convert it to a draft post, because WordPress knows how to handle drafts.

This is proposed less as intentional friction, and more as ambiguating between choices with the intent to be helpful. It's both a "did you know" on features you might not have been aware of, but also simply offering an upfront choice where ambiguity is present, rather than making that choice in advance.

If I read you correctly, your concern is mostly with the GIF detection: whether we should offer a conversion offer on upload or after. I agree this is a bit of a gray-area, since the choice is less: do you want to embed as a link or upload. Media you virtually always want to embed. I guess that also translates to concern about automatically converting to video. I could agree a notice in the image inspector is also a good idea to explore.

But in case your concern is with on-upload modals in general, including for other filetypes, let's connect, I think there's some nuance I would like to relay and I believe I might convince you it can be a beautiful experience.

@adamsilverstein

Copy link
Copy Markdown
Member Author

Thanks for the feedback on this exploration @jasmussen and @fcoveram. Closing this out for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Client Side Media Media processing in the browser with WASM [Package] Block editor /packages/block-editor [Package] Edit Post /packages/edit-post [Package] Edit Site /packages/edit-site [Package] Editor /packages/editor [Type] Enhancement A suggestion for improvement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prompt to convert animated GIF uploads to video

5 participants