Skip to content

feat(media): search, filters and confirmation in the replace image dialog - #2223

Open
trostum wants to merge 7 commits into
sanity-io:mainfrom
trostum:feat/media-replace-dialog-search
Open

trostum wants to merge 7 commits into
sanity-io:mainfrom
trostum:feat/media-replace-dialog-search

Conversation

@trostum

@trostum trostum commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Description

The Replace action in sanity-plugin-media opens a "Choose a replacement image" dialog that lists every asset newest-first, 100 at a time, with no way to search. In a dataset with ~10,000 images that makes it close to impossible to pick an older image, which is exactly what editors need when cleaning up duplicates by pointing references at an image that already exists. Recently uploaded images are easy to find; older ones are not.

This PR gives the dialog its own search and adds a confirmation step:

  • Independent search. The dialog has a search input plus tag and folder filters, with its own state and queries. It never reads or changes the browser's search, facets, folder scope or loaded assets. Previously it cleared the browser state on open and tried to restore it on close.
  • Images only, from the server. The query only matches sanity.imageAsset (excluding the asset being replaced and drafts) and still honours excludeTags and showMediaLibraryAssets. Previously files were fetched and filtered out client-side, paging until an image turned up. Search text, tag and folder are passed as GROQ params rather than interpolated.
  • Confirmation before replacing. Clicking an image no longer rewrites references immediately. A confirmation step shows the current and replacement image side by side, with the number of documents that reference the current image. Back returns to the picker with the search intact. Replace image is disabled while a replace of the same asset is already running.
  • Clear empty and error states: "No images match your search" (with a button to clear filters), "There are no replacement images", and a load error with Try again.

Cleanup

  • The replace-asset mode in CardAsset and the source prop on AssetGridVirtualized are no longer used and have been removed, along with their tests.

Screenshots

What to review

  • useReplacementAssets pages through results with local state. Results for previous filters stay visible while new ones load, to avoid flashing an empty grid, and a page that fails to load can be retried.
  • Matching reuses the browser's text search field list (textSearchFieldList from constructFilter), so with plugin locales set, alt text, title, description and credit line also match under each field["localeId"], as in fix(media): search localized alt text, title, description and credit line #2061. The folder filter matches the exact folder, like the browser.

Testing

  • New unit tests for the query builder (constructReplacementQuery), the picker (ReplaceAssetsOverview) and the dialog flow (DialogAllAssets). All 265 tests in sanity-plugin-media pass.
  • Ran the query through groq-js against a fixture dataset to check paging with [$start...$end], search with quotes and non-ASCII text, the tag and folder filters, and that files, drafts, excluded tags and Media Library assets are left out.
  • Tested manually in a production studio (Sanity 6.18, ~10k images) by installing a packed build of this branch: searching, filtering, confirming, going back, and checking that the browser's own search is unchanged after closing the dialog.

Notes for release

Adds search, tag and folder filters to the "Choose a replacement image" dialog, and asks for confirmation before replacing an image.

Screenshots

Skjermbilde 2026-10-17 kl  14 45 39
Skjermbilde 2026-10-17 kl  14 43 57

Prepared with AI assistance (Claude Code); per AGENTS.md this should carry the ✨ ai label, which I can't apply from a fork.


Note

Medium Risk
The replace action still bulk-patches every document referencing the image; the new confirmation step reduces accidental writes, but mistaken confirmation remains impactful.

Overview
The Choose a replacement image dialog no longer piggybacks on the media browser’s Redux search, folder scope, or asset grid. It now runs dedicated GROQ (constructReplacementQuery + useReplacementAssets) for image-only candidates, with debounced search plus optional tag and folder filters, paging, retry, and empty/error states—without mutating the browser’s filters or loaded assets.

Selection is two-step: picking a thumbnail opens Confirm replacement (ReplaceAssetConfirm) with current vs replacement previews and a reference count; Replace image then dispatches the existing updateImageReferences flow (blocked while the original is already updating). Back returns to the picker with filter/search state preserved in the dialog.

The old instant replace on grid click path is removed (CardAsset replace-asset / source on AssetGridVirtualized), replaced by a simpler ReplacementAssetGrid for the dialog only.

Reviewed by Cursor Bugbot for commit b419fc9. Bugbot is set up for automated code reviews on this repo. Configure here.

…alog

The "Choose a replacement image" dialog listed every asset newest-first with
no way to search, which made it impractical to pick an older image in large
datasets (e.g. when cleaning up duplicates among ~10k images).

The dialog now runs its own search, independent of the browser's search,
facets and folder scope:

- Search input plus tag and folder filters
- Only image assets are queried, honouring `excludeTags` and
  `showMediaLibraryAssets`; user input is passed as GROQ params
- A confirmation step shows the current and replacement image side by side,
  with the number of documents that will be updated, before any references
  are rewritten

The old save/restore of the browser's search state and the `replace-asset`
mode in `CardAsset` are removed.
@changeset-bot

changeset-bot Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b419fc9

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

This PR includes changesets to release 1 package
Name Type
sanity-plugin-media Minor

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

@vercel

vercel Bot commented Oct 17, 2026

Copy link
Copy Markdown

@trostum is attempting to deploy a commit to the Sanity Sandbox Team on Vercel.

A member of the Team first needs to authorize it.

@trostum
trostum marked this pull request as ready for review October 17, 2026 13:04
@trostum
trostum requested a review from a team as a code owner October 17, 2026 13:04
@trostum
trostum requested review from pedrobonamin and removed request for a team October 17, 2026 13:04

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Returning to earlier filters (load more, search, clear) reused the stale
page index stored for those filters, so the first page of results was
never fetched again. Reset the stored page request whenever the filters
change instead of only deriving page 0.
@trostum

trostum commented Oct 19, 2026

Copy link
Copy Markdown
Contributor Author

Would love your input on this one also @pedrobonamin :)
Many of our clients have requested such a feature, so would be great if it could be added

@pedrobonamin
pedrobonamin requested review from a team and jmswrnr and removed request for a team October 20, 2026 09:17
@pedrobonamin

pedrobonamin commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Would love your input on this one also @pedrobonamin :) Many of our clients have requested such a feature, so would be great if it could be added

Hi @trostum
Adding @sanity-io/media-library as reviewer for this PR, they are the owners of this plugin now, thank you for doing this changes.

@trostum

trostum commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

I see you are the owner of these files now @jmswrnr
Any initial thoughts on the PR?
It's a highly requested feature from our clients using the media-browser today, so i would like to contribute any way i can :)

@bjoerge
bjoerge requested a review from Jamiewarb October 27, 2026 12:15
@Jamiewarb Jamiewarb added the ✨ ai PR authored by an AI agent (🤖 bot is for CI automation/Renovate) label Oct 27, 2026
@Jamiewarb
Jamiewarb requested a review from a team as a code owner October 27, 2026 13:58

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

@Jamiewarb

Copy link
Copy Markdown
Contributor

Thanks this looks great. Cursor's flagged one potential issue, could you take a look and see if it needs addressing or if it can be dismissed as false positive, please? Thanks!

@trostum

trostum commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Good catch, it was valid. The replace dialog used to reuse AssetGridVirtualized and got the fix from #2231 that way, but this PR gave it its own grid, so it lost it. Added overflowAnchor: 'none' and a test in 716ce2a.

The earlier paging comment is already fixed in a3419d2.

@trostum

trostum commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

Thanks this looks great. Cursor's flagged one potential issue, could you take a look and see if it needs addressing or if it can be dismissed as false positive, please? Thanks!

I tested and found no issue, but just to be safe here and in sync with the fix in #2231, i added overflowAnchor: 'none' and a test

@vercel

vercel Bot commented Oct 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
plugins-e2e-test-studio Ignored Ignored Oct 30, 2026 7:38pm UTC

Request Review

@Jamiewarb
Jamiewarb enabled auto-merge October 30, 2026 19:39
Jamiewarb
Jamiewarb previously approved these changes Oct 30, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5acde94. Configure here.

Reuse the browser's locale-aware text search field list, so title, alt text,
description and credit line match under each configured locale as well as the
bare field.
auto-merge was automatically disabled October 30, 2026 19:48

Head branch was pushed to by a user without write access

@pedrobonamin
pedrobonamin removed their request for review November 2, 2026 11:30

This branch has not been deployed

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

Labels

✨ ai PR authored by an AI agent (🤖 bot is for CI automation/Renovate)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants