feat(media): add folder management to the media browser - #1121
Conversation
Port of sanity-io/sanity-plugin-media#281 by @bobbygeo. Adds reference-based folder support: a media.folder document type (name + weak parent ref) and a single weak opt.media.folder reference on each asset (mirroring tags). Includes a folder sidebar with tree and breadcrumb navigation, create/rename/move/recursive-delete flows, a bulk "Move to folder" dialog, and multi-select insert when used as an asset source for array fields. The default Home view lists unfiled assets.
🦋 Changeset detectedLatest commit: 56c4ad7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
There was a problem hiding this comment.
Pull request overview
Ports and integrates folder management into sanity-plugin-media, adding a reference-based folder data model (media.folder) and associated UI/Redux flows so users can create, browse, rename, move, and recursively delete folders, as well as move assets between them and support multi-select insert when used as an asset source.
Changes:
- Add
media.folderschema +opt.media.folderweak reference on assets, and update GROQ filtering to scope assets to the current folder/home. - Introduce full folder UI (sidebar tree, breadcrumbs, folder cards/rows, create/rename/move dialogs, bulk “Move to folder”) plus state management (Redux slice + epics + notifications).
- Enable multi-select insertion behavior when used as an asset source for array/multiple fields.
Reviewed changes
Copilot reviewed 42 out of 42 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| plugins/sanity-plugin-media/src/utils/constructFilter.ts | Adds currentFolderId-aware filtering to scope assets by folder vs home/unfiled. |
| plugins/sanity-plugin-media/src/utils/constructFilter.test.ts | Updates snapshot expectation to include the new home/unfiled folder filter. |
| plugins/sanity-plugin-media/src/types/index.ts | Extends types for folders, folder dialogs, folder card items, and upload folder targeting. |
| plugins/sanity-plugin-media/src/schemas/folder.ts | Introduces media.folder document schema with weak parent reference and preview. |
| plugins/sanity-plugin-media/src/plugin.tsx | Registers the new folder schema in the plugin’s schema types. |
| plugins/sanity-plugin-media/src/modules/uploads/index.ts | Persists uploaded assets into the current folder via a post-upload patch; passes folder to filters. |
| plugins/sanity-plugin-media/src/modules/selectors.ts | Includes current folder’s child folders in the combined virtualized items list. |
| plugins/sanity-plugin-media/src/modules/selectors.test.ts | Updates selector test fixture state to include the new folders reducer slice. |
| plugins/sanity-plugin-media/src/modules/notifications/index.ts | Adds folder create/delete/rename notifications and includes folder errors in generic error handling. |
| plugins/sanity-plugin-media/src/modules/index.ts | Wires the new folders reducer + epics and folder-related notifications into the root module. |
| plugins/sanity-plugin-media/src/modules/folders/index.ts | Implements folders Redux slice, selectors, and epics for fetch/create/rename/move/delete + refresh behavior. |
| plugins/sanity-plugin-media/src/modules/dialog/index.ts | Adds folder dialog types and confirm-delete-folder flow; clears dialogs on folder/asset changes. |
| plugins/sanity-plugin-media/src/modules/dialog/actions.ts | Adds dialog actions for folder create/move/rename dialogs. |
| plugins/sanity-plugin-media/src/modules/assets/index.ts | Adds bulk asset “set folder” transaction flow + local state updates + refresh behavior. |
| plugins/sanity-plugin-media/src/formSchema/index.ts | Adds folderFormSchema for folder create/rename dialogs. |
| plugins/sanity-plugin-media/src/contexts/AssetSourceDispatchContext.tsx | Adds isMultiSelect flag to control selection behavior in asset-source mode. |
| plugins/sanity-plugin-media/src/constants.ts | Adds folder-related constants and order options (including a folder sort option). |
| plugins/sanity-plugin-media/src/config/searchFacets.ts | Adds a new “Folder” search facet configuration. |
| plugins/sanity-plugin-media/src/config/orders.ts | Adds order labels for folder ordering. |
| plugins/sanity-plugin-media/src/components/TableRowFolder/index.tsx | Adds a table row renderer for folder items in the table view. |
| plugins/sanity-plugin-media/src/components/TableRowAsset/index.tsx | Adjusts click/context behavior for single vs multi-select asset-source mode. |
| plugins/sanity-plugin-media/src/components/ReduxProvider/index.tsx | Extends the initial Redux state with the new folders slice defaults. |
| plugins/sanity-plugin-media/src/components/PickedBar/index.tsx | Adds “Move to folder” action (tool mode) and “Insert selected” action (multi-select asset-source mode). |
| plugins/sanity-plugin-media/src/components/Items/index.tsx | Auto-hides folders/tags panels on small breakpoints and includes folders panel state. |
| plugins/sanity-plugin-media/src/components/FormBuilderTool/index.tsx | Detects multi-select asset-source usage and passes isMultiSelect through to the browser. |
| plugins/sanity-plugin-media/src/components/FolderView/index.tsx | Implements the folders sidebar tree UI with counts and folder actions. |
| plugins/sanity-plugin-media/src/components/FolderPanel/index.tsx | Adds the collapsible folders panel wrapper and scroll container. |
| plugins/sanity-plugin-media/src/components/FolderBreadcrumbs/index.tsx | Adds mobile breadcrumb navigation for the current folder path. |
| plugins/sanity-plugin-media/src/components/Dialogs/index.tsx | Registers the new folder dialogs in the dialog renderer. |
| plugins/sanity-plugin-media/src/components/DialogFolderRename/index.tsx | Adds the folder rename dialog using zod + react-hook-form. |
| plugins/sanity-plugin-media/src/components/DialogFolderMove/index.tsx | Adds bulk move dialog with folder tree picker. |
| plugins/sanity-plugin-media/src/components/DialogFolderCreate/index.tsx | Adds folder creation dialog (optionally under a parent folder). |
| plugins/sanity-plugin-media/src/components/Controls/index.tsx | Adds “New folder” controls and a folders panel toggle. |
| plugins/sanity-plugin-media/src/components/CardFolder/index.tsx | Adds a grid card renderer for folder items in the grid view. |
| plugins/sanity-plugin-media/src/components/CardAsset/index.tsx | Adjusts click/context behavior for single vs multi-select asset-source mode. |
| plugins/sanity-plugin-media/src/components/Browser/useBrowserInit.ts | Fetches folders on init and listens for folder document changes to refresh. |
| plugins/sanity-plugin-media/src/components/Browser/index.tsx | Adds folders panel + breadcrumbs, and passes multi-select info into context. |
| plugins/sanity-plugin-media/src/components/AssetTableVirtualized/index.tsx | Supports rendering folder rows alongside assets/uploads in table view. |
| plugins/sanity-plugin-media/src/components/AssetGridVirtualized/index.tsx | Supports rendering folder cards alongside assets/uploads in grid view. |
| plugins/sanity-plugin-media/src/tests/fixtures/rootState.ts | Extends test root state fixture with folders slice defaults. |
| plugins/sanity-plugin-media/README.md | Documents folder behavior and querying opt.media.folder in GROQ; updates feature list. |
| .changeset/media-folder-management.md | Adds a minor changeset with author: credit for the original contributor. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
We need to include the changes from this branch as well https://github.com/sanity-io/sanity-plugin-media/compare/add-folder-support-review, it fixes the comments and suggestions I added to the last changes in the PR |
Resolve 4 conflicts in the media plugin (all simple - compatible intents): - contexts/AssetSourceDispatchContext: keep main's useMemo wrapper and add the isMultiSelect field from this branch. - modules/assets: keep the folder patch helper; drop the unused duplicate assetsRemoveTagsEpic that main removed. - modules/uploads: keep the added 'from' rxjs import; drop the unused Selector import that main removed. - types: keep the folderFormSchema import; drop the unused SUPPORTED_ASSET_TYPES import that main removed. Adapt folder code to main's stricter tooling (noUncheckedIndexedAccess + React Compiler lint): local-capture indexed lookups, bracket-notation form data access, react/react-compiler suppressions for the dialog Footer and the auto-expand effects, and add the missing folders effect dependency.
Ports the add-folder-support-review changes by @pedrobonamin (his review feedback on the original PR): - Treat "All assets" as the default browser view; only filter by folder when one is opened (no folder filter at the root). - Rebuild the folder sidebar and the move dialog on @sanity/ui Tree/TreeItem. - Move the Folders toggle next to the view switcher and drop the redundant "New folder" button next to the filters. - Show and edit an asset's folder (change / remove) from the asset details dialog. - Make folder deletion non-recursive: clear the folder ref on contained assets and promote child folders one level up. Remove the unused folder-move epic and the moving state. - Drop the folder sort order and restrict the folder search facet to empty / notEmpty.
…ent-c619' into cursor/port-media-folder-management-c619
Resolve conflicts with mediaIndex panel hiding, functional ReduxProvider preloaded folders state, and typed selectCombinedItems folder cards. Also address remaining Copilot review notes: count assets per folder in GROQ, guard ontouchstart behind window, and target folder._ref in the empty/notEmpty facet. Co-authored-by: Cody Olsen <stipsan@users.noreply.github.com>
@sanity/icons v5 types FolderIcon/AddIcon/EditIcon/TrashIcon as never from the root entry; import from dedicated subpaths like the rest of the plugin after the main merge.
✅ E2E Tests🟢 24 passed • view full report • view run Studio: https://plugins-e2e-test-studio-oxqp6kz6c.sanity.dev Datasets: |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d7a5a7c. Configure here.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 46 out of 46 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
plugins/sanity-plugin-media/src/components/CardFolder/index.tsx:76
FolderCarddefines a hoverborder-colorin styled-components, but the inlinestyle={{border: '1px solid transparent'}}wins in the cascade (inline styles override stylesheet rules). As a result, the hover border color change won’t apply.
radius={2}
style={{
background: getSchemeColor(scheme, 'bg'),
border: '1px solid transparent',
}}
plugins/sanity-plugin-media/src/modules/folders/index.ts:621
selectCurrentFolderChildrenrecomputes each child folder’stotalCountby walking its entire subtree in a loop for every child. For large folder trees this becomes O(N×M) work per selector run and can cause noticeable UI jank when the asset list re-renders.
plugins/sanity-plugin-media/src/components/TableRowFolder/index.tsx:28- The
ContainerGridhover background won’t take effect because the row setsstyle={{background: ...}}inline later. Inlinebackgroundoverrides the&:hover { background: ... }rule, so the hover styling is effectively dead code.
@media (hover: hover) and (pointer: fine) {
&:hover {
background: ${theme.sanity.color.card.enabled.bg};
}
}
plugins/sanity-plugin-media/src/components/CardFolder/index.tsx:5
- This introduces new
styled-componentsstyling (and a number of inline style objects) for newly added UI. In this monorepo, new plugin styling should prefer vanilla-extract (*.css.ts) to avoid runtime CSS-in-JS cost and to keep styling consistent with the repo’s direction.
This issue also appears on line 72 of the same file.
import {Box, Card, Flex, Stack, Text} from '@sanity/ui'
import {useDispatch} from 'react-redux'
import {useColorSchemeValue} from 'sanity'
import {css, styled} from 'styled-components'
…dialog When deleting (or losing) the currently viewed folder, clear and reload assets so the grid matches All assets. On small screens, open folders in a dialog like tags instead of a panel that was immediately forced closed.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 47 out of 47 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
plugins/sanity-plugin-media/src/components/FormBuilderTool/index.tsx:17
selectionTypeis read via anas unknown ascast, which bypasses type-safety and makes it easy to accidentally depend on a non-existent prop. A runtime-safe'selectionType' in propscheck avoids theunknowncast while keeping behavior the same when the prop is absent.
const selectionType = (props as unknown as {selectionType?: string})?.selectionType
const schemaJsonType = (props?.schemaType as {jsonType?: string} | undefined)?.jsonType
const isMultiSelect = selectionType === 'multiple' || schemaJsonType === 'array'
plugins/sanity-plugin-media/src/modules/notifications/index.ts:170
- Folder fetch failures won't produce an error notification because
foldersActions.fetchErrorisn't included in thenotificationsGenericErrorEpicofType list. This makes folder-related connectivity/auth errors easy to miss.
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @sanity/sanity-plugin-async-list@3.0.0 ### Major Changes - [#1087](#1087) [`f4e7fcc`](f4e7fcc) Thanks [@stipsan](https://github.com/stipsan)! - Refactor the input into a real React component and fix the `async-list-undefined` namespace/id - **Breaking:** `AsyncList` is now a regular React component that takes a single `props` argument (the standard Sanity input props plus an `options` field), instead of being called as `AsyncList(props, options)`. For the `components.input` slot, use the new `createAsyncListInput(options)` factory: `input: createAsyncListInput({loader})`. This makes the input safe under the Rules of Hooks and lets the React Compiler optimize it. - **Breaking:** the secrets namespace and DOM `id` are no longer derived as `async-list-${schemaType}` (which became the literal `async-list-undefined` for component usage). The DOM `id` now uses Sanity's stable per-field id, and the secrets namespace falls back to `async-list` (instead of `async-list-undefined`) when no `schemaType`/`secrets.namespace` is available. When using the component with `secrets`, set an explicit `secrets.namespace`; a dev warning is logged if it is missing. - Fix: the debounced search handler is stable and is cancelled on unmount, so it no longer drops queued calls or updates state on an unmounted tree. - Fix: the value-change handler no longer depends on the whole `props` object, avoiding unnecessary `Autocomplete` re-renders. - Fix: loader results are validated to ensure each option's `value` is a string. ### Patch Changes - Updated dependencies [[`c61bb44`](c61bb44)]: - @sanity/studio-secrets@4.0.15 ## @sanity/personalization-plugin@3.1.0 ### Minor Changes - [#1226](#1226) [`1143bd6`](1143bd6) Thanks [@jjburbridge](https://github.com/jjburbridge)! - Add `fieldLevelPersonalization` export for segment-based field personalization. Segments can be defined statically, fetched from an external API, or stored in your Sanity dataset. `FieldPluginConfig` is renamed to `ExperimentFieldPluginConfig` (a deprecated type alias is kept for compatibility). ### Patch Changes - [#1208](#1208) [`51317b2`](51317b2) Thanks [@stipsan](https://github.com/stipsan)! - Address review feedback from the migration into the monorepo: - Fix LaunchDarkly experiment pagination so `offset` advances between pages, preventing duplicate results and a possible infinite loop - Correct misspelled plugin names (`personalistaion` → `personalization`) and stop the LaunchDarkly entry point from identifying itself as the GrowthBook plugin - Fix an operator-precedence bug in the experiment field preview that could set the title to the entire field object - Walk reference preview paths with optional chaining to avoid throwing on missing intermediate fields - Move the experiment item activation patch out of render and into an effect - Remove unused `Select` props - Fix the GrowthBook (`project` config key) and LaunchDarkly (`fieldLevelExperiments` usage) docs and drop the stale standalone-repo Studio version / tooling references - Updated dependencies [[`c61bb44`](c61bb44)]: - @sanity/studio-secrets@4.0.15 ## sanity-plugin-cloudinary@2.1.0 ### Minor Changes - [#1233](#1233) [`36f3fe2`](36f3fe2) Thanks [@ChrisLaRocque](https://github.com/ChrisLaRocque)! - Add performant preview images Asset previews and diffs now build an optimized, scaled-down preview URL with `@cloudinary/url-gen` (a 400px-wide transformation) when a cloud name is configured, instead of loading the full-size original. This keeps the Studio fast when previewing large Cloudinary assets, and falls back to the stored asset URL when no cloud name is available. - [#1227](#1227) [`3b02635`](3b02635) Thanks [@pgurley](https://github.com/pgurley), [@atlvis](https://github.com/atlvis)! - Add `cloudinaryReferencePlugin` for storing Cloudinary assets as reusable document references - New `cloudinaryReferencePlugin` registers the schema types needed to reference Cloudinary assets as documents - New `cloudinaryAssetDocument` type stores a Cloudinary asset as a standalone document - New `cloudinaryAssetReference` type references those asset documents, with a custom input for selecting and managing assets through the Cloudinary Media Library - `openMediaSelector` now supports a `showHandler` callback and a `folder` option, so the select button can show a loading state and scope the library to a folder - Fixed the internal name of `cloudinaryAssetSourcePlugin` (`cloudinart-asset-source` → `cloudinary-asset-source`) ### Patch Changes - [#1236](#1236) [`7ec89b6`](7ec89b6) Thanks [@stipsan](https://github.com/stipsan)! - - Fix the `cloudinaryAssetSourcePlugin` name, which was mistakenly registered as `cloudinart-asset-source` - Wait for the Cloudinary Media Library script to finish loading before opening it, avoiding runtime errors when several inputs mount at once - Fix a user-facing typo in the asset source loading message ("Media Libary" → "Media Library") - Remove an invalid `src`-less `<track>` element from the video preview - Correct the README usage examples (`defineConfg` → `defineConfig`) and drop stale standalone-repo "Develop & test" / "Release new version" instructions - Updated dependencies [[`c61bb44`](c61bb44)]: - @sanity/studio-secrets@4.0.15 ## sanity-plugin-media@6.1.0 ### Minor Changes - [#1119](#1119) [`ab39674`](ab39674) Thanks [@lud-hu](https://github.com/lud-hu)! - Add an "Edit Media" asset source so media details (alt text, title, tags, etc.) can be edited directly from an image or file field on a document, without opening the full Media tool. - [#1117](#1117) [`6f4316d`](6f4316d) Thanks [@nickeforsberg](https://github.com/nickeforsberg)! - Add an `excludeTags` option that hides assets referencing the listed `media.tag` slugs (`name.current` values). Excluded assets are omitted from the Media browser grid and asset-picker queries, and the matching tags are hidden from the tag sidebar and tag search facet. The asset edit dialog still lists all tags so you can assign or remove them on an open asset. - [#1121](#1121) [`30fa7e2`](30fa7e2) Thanks [@bobbygeo](https://github.com/bobbygeo), [@pedrobonamin](https://github.com/pedrobonamin)! - Add folder management to the media browser - Organise assets into nested folders, backed by a new `media.folder` document type (a `name` plus a weak `parent` reference) and a single weak `opt.media.folder` reference on each asset (mirroring how tags work). Renaming a folder is a one-field document write regardless of how many assets it contains. - Browse folders in a dedicated sidebar tree, with breadcrumb navigation, create / rename / delete flows, a bulk "Move to folder" dialog, and per-asset folder controls in the asset details dialog. The default "All assets" view lists every asset; opening a folder filters the list to that folder. - Deleting a folder removes only the folder document — its assets stay in the library with their folder assignment cleared, and nested folders move up one level. - Support multi-select insert when the media plugin is used as an asset source for array (multiple) fields. - [#1115](#1115) [`e7ec6e6`](e7ec6e6) Thanks [@am0wa](https://github.com/am0wa)! - Extract `image` (`sanity.imageExifTags`) metadata on asset upload and include it in asset queries. The asset edit dialog now pre-fills the Description field from the image's `ImageDescription` EXIF tag as a fallback for image assets. - [#1118](#1118) [`186e29c`](186e29c) Thanks [@Polleke007](https://github.com/Polleke007), [@joepvandepol](https://github.com/joepvandepol)! - Add the ability to replace a selected asset with another one and update all of its references across documents When a single asset is selected, a new **Replace** action opens an overview where you can pick a replacement asset. Every document that references the original asset (including deeply nested image fields) is re-pointed to the chosen asset. Ported from sanity-io/sanity-plugin-media#236. ### Patch Changes - [#1108](#1108) [`1dc3a7b`](1dc3a7b) Thanks [@oxygensmith](https://github.com/oxygensmith)! - Add `crossOrigin="anonymous"` to the image thumbnail component so thumbnails load in Firefox. Firefox's Opaque Response Blocking would otherwise block Sanity CDN asset responses (which send `Vary: Origin`) when requested without an `Origin` header. ## @sanity/plugin-kit@10.0.3 ### Patch Changes - [#1698](#1698) [`d3dedd3`](d3dedd3) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency oxlint to ^1.76.0 - [#1779](#1779) [`5555795`](5555795) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency oxfmt to ^0.61.0 - [#1799](#1799) [`93858fc`](93858fc) Thanks [@stipsan](https://github.com/stipsan)! - Prefer function components via `react/prefer-function-component` (with `allowErrorBoundary`) instead of banning `Component`/`PureComponent` imports ## @sanity/cross-dataset-duplicator@2.0.13 ### Patch Changes - [#1228](#1228) [`71d77d5`](71d77d5) Thanks [@rohanvachheta](https://github.com/rohanvachheta)! - Handle reference errors during duplication by fetching missing referenced documents (including transitive refs, respecting `filter`), re-uploading any recovered assets, and retrying the transaction, with a one-by-one commit fallback - Updated dependencies [[`c61bb44`](c61bb44)]: - @sanity/studio-secrets@4.0.15 ## @sanity/embeddings-index-ui@4.0.13 ### Patch Changes - [#1792](#1792) [`c61bb44`](c61bb44) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency react-rx to ^4.2.5 ## @sanity/form-toolkit@3.0.13 ### Patch Changes - [#1234](#1234) [`1dd36c8`](1dd36c8) Thanks [@stipsan](https://github.com/stipsan)! - Address post-migration review feedback: - `FormRenderer` now renders interactive (uncontrolled) inputs when no `getFieldState` is provided, so the documented native HTML form usage works out of the box - Uncontrolled text and textarea fields honor `options.defaultValue` (controlled fields keep form-library state only, so UI and submit values stay in sync) - `FormRenderer` falls back to `field.name` for the React key when a field has no `_key` - The shared HubSpot/Mailchimp request handler defaults to the Next.js-compatible handler instead of throwing when no framework environment variable is detected, and short-circuits CORS preflight (`OPTIONS`) requests - Corrected the `formSchema`, `formiumInput`, and `mailchimpInput` usage examples (import paths and required options), README typos, and example/dev-workflow references - Updated dependencies [[`f4e7fcc`](f4e7fcc)]: - @sanity/sanity-plugin-async-list@3.0.0 ## @sanity/language-filter@5.0.14 ### Patch Changes - [#1792](#1792) [`c61bb44`](c61bb44) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency react-rx to ^4.2.5 ## @sanity/studio-secrets@4.0.15 ### Patch Changes - [#1792](#1792) [`c61bb44`](c61bb44) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency react-rx to ^4.2.5 ## sanity-plugin-hotspot-array@5.0.12 ### Patch Changes - [#1789](#1789) [`9628df4`](9628df4) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency motion to ^12.43.0 ## sanity-plugin-iframe-pane@5.0.30 ### Patch Changes - [#1789](#1789) [`9628df4`](9628df4) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency motion to ^12.43.0 ## sanity-plugin-internationalized-array@5.1.25 ### Patch Changes - [#1765](#1765) [`97937c4`](97937c4) Thanks [@pedrobonamin](https://github.com/pedrobonamin)! - Add a stable `data-testid` on the document-level add-translations panel for e2e coverage ## sanity-plugin-mux-input@5.0.8 ### Patch Changes - [#1792](#1792) [`c61bb44`](c61bb44) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency react-rx to ^4.2.5 ## sanity-plugin-studio-smartling@5.0.14 ### Patch Changes - [#1216](#1216) [`d4dd45b`](d4dd45b) Thanks [@stipsan](https://github.com/stipsan)! - Harden the Smartling adapter and fix docs: - Send the secret credentials JSON verbatim during authentication instead of double-encoding it, which could break auth with proxies that forward the request body as-is - Throw a clear error (surfacing Smartling's message) when authentication does not return an access token, instead of a cryptic `TypeError` - Avoid throwing when no existing job is found or when a translation/progress response is missing its expected payload - Guard the progress calculation against a zero total word count so empty documents no longer report `NaN`/`Infinity` - Fix the `additionalDeserializers` option name in the advanced configuration docs (was `additonalDeserializers`) and update the README development/release instructions for the monorepo ## sanity-plugin-workflow@3.0.39 ### Patch Changes - [#1789](#1789) [`9628df4`](9628df4) Thanks [@squiggler-app](https://github.com/apps/squiggler-app)! - fix(deps): update dependency motion to ^12.43.0 ## @sanity/document-internationalization@6.2.28 ## @sanity/sfcc@1.0.25 <!-- CURSOR_SUMMARY --> --- > [!NOTE] > <sup>[Cursor Bugbot](https://cursor.com/bugbot) is generating a summary for commit 9246b02. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>

Summary
Ports folder management from sanity-io/sanity-plugin-media#281 into the monorepo, then incorporates Pedro’s follow-up from
add-folder-support-review.Authors
Behavior
media.folderdocuments (name+ weakparent) and weakopt.media.folderon assetsopt.media.folder._refBabysit follow-ups
main(useMediaIndex, functionalReduxProvider)windowguard for touch, facet_ref, icons v5 subpath importsTest plan
pnpm lintpnpm --filter sanity-plugin-media buildpnpm --filter sanity-plugin-media exec vitest run(157 tests)