@strapi/upload 5.51.1 → 5.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (369) hide show
  1. package/dist/admin/components/AssetCard/VideoPreview.js +1 -1
  2. package/dist/admin/components/AssetCard/VideoPreview.js.map +1 -1
  3. package/dist/admin/components/AssetCard/VideoPreview.mjs +1 -1
  4. package/dist/admin/components/AssetCard/VideoPreview.mjs.map +1 -1
  5. package/dist/admin/components/TableList/TableList.js +1 -1
  6. package/dist/admin/components/TableList/TableList.js.map +1 -1
  7. package/dist/admin/components/TableList/TableList.mjs +1 -1
  8. package/dist/admin/components/TableList/TableList.mjs.map +1 -1
  9. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js +21 -4
  10. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js.map +1 -1
  11. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs +21 -4
  12. package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs.map +1 -1
  13. package/dist/admin/future/App.js +3 -3
  14. package/dist/admin/future/App.js.map +1 -1
  15. package/dist/admin/future/App.mjs +4 -4
  16. package/dist/admin/future/App.mjs.map +1 -1
  17. package/dist/admin/future/components/BetaNotice.js +72 -0
  18. package/dist/admin/future/components/BetaNotice.js.map +1 -0
  19. package/dist/admin/future/components/BetaNotice.mjs +70 -0
  20. package/dist/admin/future/components/BetaNotice.mjs.map +1 -0
  21. package/dist/admin/future/components/Drawer.js +18 -3
  22. package/dist/admin/future/components/Drawer.js.map +1 -1
  23. package/dist/admin/future/components/Drawer.mjs +18 -3
  24. package/dist/admin/future/components/Drawer.mjs.map +1 -1
  25. package/dist/admin/future/components/UploadProgressDialog.js +200 -27
  26. package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
  27. package/dist/admin/future/components/UploadProgressDialog.mjs +203 -30
  28. package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
  29. package/dist/admin/future/hooks/useAIMetadataEnabled.js +39 -0
  30. package/dist/admin/future/hooks/useAIMetadataEnabled.js.map +1 -0
  31. package/dist/admin/future/hooks/useAIMetadataEnabled.mjs +37 -0
  32. package/dist/admin/future/hooks/useAIMetadataEnabled.mjs.map +1 -0
  33. package/dist/admin/future/hooks/useMediaLibraryPermissions.js +35 -0
  34. package/dist/admin/future/hooks/useMediaLibraryPermissions.js.map +1 -0
  35. package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs +33 -0
  36. package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs.map +1 -0
  37. package/dist/admin/future/pages/Assets/AssetsPage.js +453 -170
  38. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/AssetsPage.mjs +454 -171
  40. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js +46 -0
  42. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js.map +1 -0
  43. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs +44 -0
  44. package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs.map +1 -0
  45. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +325 -0
  46. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -0
  47. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +323 -0
  48. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -0
  49. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +110 -46
  50. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
  51. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +110 -46
  52. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
  53. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +95 -67
  54. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  55. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +95 -68
  56. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  57. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +94 -26
  58. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  59. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +96 -28
  60. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  61. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +112 -0
  62. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -0
  63. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +110 -0
  64. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -0
  65. package/dist/admin/future/pages/Assets/components/AssetsTable.js +172 -56
  66. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  67. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +175 -59
  68. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  69. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +188 -138
  70. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
  71. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +191 -141
  72. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  73. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +123 -76
  74. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -1
  75. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +126 -79
  76. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -1
  77. package/dist/admin/future/pages/Assets/components/BusyOverlay.js +36 -0
  78. package/dist/admin/future/pages/Assets/components/BusyOverlay.js.map +1 -0
  79. package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs +34 -0
  80. package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs.map +1 -0
  81. package/dist/admin/future/pages/Assets/components/DateRangeCalendar.js +193 -0
  82. package/dist/admin/future/pages/Assets/components/DateRangeCalendar.js.map +1 -0
  83. package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs +191 -0
  84. package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs.map +1 -0
  85. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js +131 -0
  86. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js.map +1 -0
  87. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs +129 -0
  88. package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs.map +1 -0
  89. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +45 -18
  90. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  91. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +46 -19
  92. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  93. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +28 -31
  94. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -1
  95. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +28 -31
  96. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -1
  97. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +10 -15
  98. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
  99. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +10 -15
  100. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
  101. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js +13 -3
  102. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js.map +1 -1
  103. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs +13 -3
  104. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs.map +1 -1
  105. package/dist/admin/future/pages/Assets/components/EmptyState.js +62 -3
  106. package/dist/admin/future/pages/Assets/components/EmptyState.js.map +1 -1
  107. package/dist/admin/future/pages/Assets/components/EmptyState.mjs +62 -4
  108. package/dist/admin/future/pages/Assets/components/EmptyState.mjs.map +1 -1
  109. package/dist/admin/future/pages/Assets/components/FilterBadges.js +417 -0
  110. package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -0
  111. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +415 -0
  112. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -0
  113. package/dist/admin/future/pages/Assets/components/FilterMenu.js +453 -0
  114. package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -0
  115. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +447 -0
  116. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -0
  117. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js +144 -0
  118. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js.map +1 -0
  119. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs +142 -0
  120. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs.map +1 -0
  121. package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.js → FolderFormDialog.js} +50 -16
  122. package/dist/admin/future/pages/Assets/components/FolderFormDialog.js.map +1 -0
  123. package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.mjs → FolderFormDialog.mjs} +52 -18
  124. package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs.map +1 -0
  125. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +125 -148
  126. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  127. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +127 -150
  128. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  129. package/dist/admin/future/pages/Assets/components/SortMenu.js +21 -32
  130. package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -1
  131. package/dist/admin/future/pages/Assets/components/SortMenu.mjs +22 -33
  132. package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -1
  133. package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js +40 -0
  134. package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js.map +1 -0
  135. package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs +38 -0
  136. package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs.map +1 -0
  137. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +21 -5
  138. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -1
  139. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +21 -5
  140. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
  141. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js +47 -0
  142. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js.map +1 -0
  143. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs +44 -0
  144. package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs.map +1 -0
  145. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js +33 -12
  146. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js.map +1 -1
  147. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs +33 -12
  148. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs.map +1 -1
  149. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +222 -45
  150. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
  151. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +223 -46
  152. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
  153. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js +75 -0
  154. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js.map +1 -0
  155. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs +73 -0
  156. package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs.map +1 -0
  157. package/dist/admin/future/pages/Assets/hooks/useListFilters.js +168 -0
  158. package/dist/admin/future/pages/Assets/hooks/useListFilters.js.map +1 -0
  159. package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs +162 -0
  160. package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs.map +1 -0
  161. package/dist/admin/future/pages/Assets/utils/buildAssetFilters.js +201 -0
  162. package/dist/admin/future/pages/Assets/utils/buildAssetFilters.js.map +1 -0
  163. package/dist/admin/future/pages/Assets/utils/buildAssetFilters.mjs +198 -0
  164. package/dist/admin/future/pages/Assets/utils/buildAssetFilters.mjs.map +1 -0
  165. package/dist/admin/future/pages/Assets/utils/listQueryKey.js +2 -2
  166. package/dist/admin/future/pages/Assets/utils/listQueryKey.js.map +1 -1
  167. package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs +2 -2
  168. package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs.map +1 -1
  169. package/dist/admin/future/services/api.js +187 -28
  170. package/dist/admin/future/services/api.js.map +1 -1
  171. package/dist/admin/future/services/api.mjs +187 -30
  172. package/dist/admin/future/services/api.mjs.map +1 -1
  173. package/dist/admin/future/services/assets.js +40 -17
  174. package/dist/admin/future/services/assets.js.map +1 -1
  175. package/dist/admin/future/services/assets.mjs +40 -17
  176. package/dist/admin/future/services/assets.mjs.map +1 -1
  177. package/dist/admin/future/services/folders.js +66 -19
  178. package/dist/admin/future/services/folders.js.map +1 -1
  179. package/dist/admin/future/services/folders.mjs +66 -20
  180. package/dist/admin/future/services/folders.mjs.map +1 -1
  181. package/dist/admin/future/services/settings.js +10 -3
  182. package/dist/admin/future/services/settings.js.map +1 -1
  183. package/dist/admin/future/services/settings.mjs +10 -3
  184. package/dist/admin/future/services/settings.mjs.map +1 -1
  185. package/dist/admin/future/store/uploadProgress.js +109 -2
  186. package/dist/admin/future/store/uploadProgress.js.map +1 -1
  187. package/dist/admin/future/store/uploadProgress.mjs +105 -3
  188. package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
  189. package/dist/admin/future/utils/buildDragSetFromSelection.js +7 -5
  190. package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -1
  191. package/dist/admin/future/utils/buildDragSetFromSelection.mjs +7 -5
  192. package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -1
  193. package/dist/admin/future/utils/formatMoveSuccessMessage.js +12 -1
  194. package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -1
  195. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +12 -1
  196. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -1
  197. package/dist/admin/future/utils/getRelationId.js +24 -0
  198. package/dist/admin/future/utils/getRelationId.js.map +1 -0
  199. package/dist/admin/future/utils/getRelationId.mjs +22 -0
  200. package/dist/admin/future/utils/getRelationId.mjs.map +1 -0
  201. package/dist/admin/future/utils/isEventFromWithin.js +14 -0
  202. package/dist/admin/future/utils/isEventFromWithin.js.map +1 -0
  203. package/dist/admin/future/utils/isEventFromWithin.mjs +12 -0
  204. package/dist/admin/future/utils/isEventFromWithin.mjs.map +1 -0
  205. package/dist/admin/future/utils/itemLocations.js +40 -0
  206. package/dist/admin/future/utils/itemLocations.js.map +1 -0
  207. package/dist/admin/future/utils/itemLocations.mjs +36 -0
  208. package/dist/admin/future/utils/itemLocations.mjs.map +1 -0
  209. package/dist/admin/future/utils/itemSource.js +36 -0
  210. package/dist/admin/future/utils/itemSource.js.map +1 -0
  211. package/dist/admin/future/utils/itemSource.mjs +32 -0
  212. package/dist/admin/future/utils/itemSource.mjs.map +1 -0
  213. package/dist/admin/future/utils/searchQueryParam.js +11 -0
  214. package/dist/admin/future/utils/searchQueryParam.js.map +1 -0
  215. package/dist/admin/future/utils/searchQueryParam.mjs +9 -0
  216. package/dist/admin/future/utils/searchQueryParam.mjs.map +1 -0
  217. package/dist/admin/hooks/useSelectionState.js +2 -4
  218. package/dist/admin/hooks/useSelectionState.js.map +1 -1
  219. package/dist/admin/hooks/useSelectionState.mjs +2 -4
  220. package/dist/admin/hooks/useSelectionState.mjs.map +1 -1
  221. package/dist/admin/index.js +11 -17
  222. package/dist/admin/index.js.map +1 -1
  223. package/dist/admin/index.mjs +12 -18
  224. package/dist/admin/index.mjs.map +1 -1
  225. package/dist/admin/shared/constants.js +43 -0
  226. package/dist/admin/shared/constants.js.map +1 -0
  227. package/dist/admin/shared/constants.mjs +38 -0
  228. package/dist/admin/shared/constants.mjs.map +1 -0
  229. package/dist/admin/src/future/App.d.ts +1 -1
  230. package/dist/admin/src/future/components/BetaNotice.d.ts +11 -0
  231. package/dist/admin/src/future/components/Drawer.d.ts +5 -1
  232. package/dist/admin/src/future/hooks/useAIMetadataEnabled.d.ts +26 -0
  233. package/dist/admin/src/future/hooks/useMediaLibraryPermissions.d.ts +21 -0
  234. package/dist/admin/src/future/pages/Assets/components/ActionsMenuContent.d.ts +33 -0
  235. package/dist/admin/src/future/pages/Assets/components/AssetActionsMenu.d.ts +24 -0
  236. package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetCropEditor.d.ts +7 -1
  237. package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +16 -0
  238. package/dist/admin/src/future/pages/Assets/components/AssetsSearchInput.d.ts +5 -0
  239. package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +21 -1
  240. package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +17 -6
  241. package/dist/admin/src/future/pages/Assets/components/BusyOverlay.d.ts +9 -0
  242. package/dist/admin/src/future/pages/Assets/components/DateRangeCalendar.d.ts +8 -0
  243. package/dist/admin/src/future/pages/Assets/components/DeleteItemsDialog.d.ts +27 -0
  244. package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +8 -1
  245. package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +18 -3
  246. package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +2 -1
  247. package/dist/admin/src/future/pages/Assets/components/DropZone/UploadDropZoneContext.d.ts +8 -1
  248. package/dist/admin/src/future/pages/Assets/components/EmptyState.d.ts +18 -1
  249. package/dist/admin/src/future/pages/Assets/components/FilterBadges.d.ts +8 -0
  250. package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +26 -0
  251. package/dist/admin/src/future/pages/Assets/components/FolderActionsMenu.d.ts +20 -0
  252. package/dist/admin/src/future/pages/Assets/components/FolderFormDialog.d.ts +16 -0
  253. package/dist/admin/src/future/pages/Assets/components/FolderTree/FolderTree.d.ts +13 -4
  254. package/dist/admin/src/future/pages/Assets/components/SortMenu.d.ts +6 -0
  255. package/dist/admin/src/future/pages/Assets/hooks/useAssetSearch.d.ts +12 -0
  256. package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +8 -1
  257. package/dist/admin/src/future/pages/Assets/hooks/useBusyAssets.d.ts +38 -0
  258. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +27 -3
  259. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteScrollSentinel.d.ts +41 -0
  260. package/dist/admin/src/future/pages/Assets/hooks/useListFilters.d.ts +69 -0
  261. package/dist/admin/src/future/pages/Assets/utils/buildAssetFilters.d.ts +46 -0
  262. package/dist/admin/src/future/pages/Assets/utils/listQueryKey.d.ts +3 -4
  263. package/dist/admin/src/future/services/api.d.ts +53 -5
  264. package/dist/admin/src/future/services/assets.d.ts +9 -1
  265. package/dist/admin/src/future/services/folders.d.ts +9 -1
  266. package/dist/admin/src/future/services/settings.d.ts +2 -2
  267. package/dist/admin/src/future/store/uploadProgress.d.ts +83 -1
  268. package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +4 -3
  269. package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +7 -1
  270. package/dist/admin/src/future/utils/getRelationId.d.ts +11 -0
  271. package/dist/admin/src/future/utils/isEventFromWithin.d.ts +11 -0
  272. package/dist/admin/src/future/utils/itemLocations.d.ts +31 -0
  273. package/dist/admin/src/future/utils/itemSource.d.ts +22 -0
  274. package/dist/admin/src/future/utils/searchQueryParam.d.ts +7 -0
  275. package/dist/admin/src/hooks/useAiAvailability.d.ts +1 -1
  276. package/dist/admin/src/hooks/useEditAsset.d.ts +4 -4
  277. package/dist/admin/src/hooks/useEditFolder.d.ts +4 -4
  278. package/dist/admin/src/hooks/useSettings.d.ts +1 -0
  279. package/dist/admin/src/hooks/useTracking.d.ts +1 -1
  280. package/dist/admin/src/hooks/useUpload.d.ts +1 -1
  281. package/dist/admin/translations/en.json.js +73 -2
  282. package/dist/admin/translations/en.json.js.map +1 -1
  283. package/dist/admin/translations/en.json.mjs +73 -2
  284. package/dist/admin/translations/en.json.mjs.map +1 -1
  285. package/dist/admin/utils/urlYupSchema.js +33 -9
  286. package/dist/admin/utils/urlYupSchema.js.map +1 -1
  287. package/dist/admin/utils/urlYupSchema.mjs +33 -9
  288. package/dist/admin/utils/urlYupSchema.mjs.map +1 -1
  289. package/dist/server/config.js +13 -5
  290. package/dist/server/config.js.map +1 -1
  291. package/dist/server/config.mjs +13 -5
  292. package/dist/server/config.mjs.map +1 -1
  293. package/dist/server/constants.js.map +1 -1
  294. package/dist/server/constants.mjs.map +1 -1
  295. package/dist/server/controllers/admin-file.js +30 -0
  296. package/dist/server/controllers/admin-file.js.map +1 -1
  297. package/dist/server/controllers/admin-file.mjs +30 -0
  298. package/dist/server/controllers/admin-file.mjs.map +1 -1
  299. package/dist/server/controllers/admin-settings.js +20 -2
  300. package/dist/server/controllers/admin-settings.js.map +1 -1
  301. package/dist/server/controllers/admin-settings.mjs +21 -3
  302. package/dist/server/controllers/admin-settings.mjs.map +1 -1
  303. package/dist/server/controllers/content-api.js +1 -2
  304. package/dist/server/controllers/content-api.js.map +1 -1
  305. package/dist/server/controllers/content-api.mjs +2 -2
  306. package/dist/server/controllers/content-api.mjs.map +1 -1
  307. package/dist/server/controllers/validation/admin/ai-metadata.js +12 -0
  308. package/dist/server/controllers/validation/admin/ai-metadata.js.map +1 -0
  309. package/dist/server/controllers/validation/admin/ai-metadata.mjs +10 -0
  310. package/dist/server/controllers/validation/admin/ai-metadata.mjs.map +1 -0
  311. package/dist/server/controllers/validation/admin/settings.js +4 -1
  312. package/dist/server/controllers/validation/admin/settings.js.map +1 -1
  313. package/dist/server/controllers/validation/admin/settings.mjs +1 -1
  314. package/dist/server/controllers/validation/admin/settings.mjs.map +1 -1
  315. package/dist/server/routes/admin.js +18 -0
  316. package/dist/server/routes/admin.js.map +1 -1
  317. package/dist/server/routes/admin.mjs +18 -0
  318. package/dist/server/routes/admin.mjs.map +1 -1
  319. package/dist/server/services/ai-metadata.js +108 -11
  320. package/dist/server/services/ai-metadata.js.map +1 -1
  321. package/dist/server/services/ai-metadata.mjs +108 -11
  322. package/dist/server/services/ai-metadata.mjs.map +1 -1
  323. package/dist/server/services/file.js +86 -13
  324. package/dist/server/services/file.js.map +1 -1
  325. package/dist/server/services/file.mjs +87 -14
  326. package/dist/server/services/file.mjs.map +1 -1
  327. package/dist/server/services/image-manipulation.js +4 -4
  328. package/dist/server/services/image-manipulation.js.map +1 -1
  329. package/dist/server/services/image-manipulation.mjs +4 -4
  330. package/dist/server/services/image-manipulation.mjs.map +1 -1
  331. package/dist/server/shared/constants.js +43 -0
  332. package/dist/server/shared/constants.js.map +1 -0
  333. package/dist/server/shared/constants.mjs +38 -0
  334. package/dist/server/shared/constants.mjs.map +1 -0
  335. package/dist/server/src/config.d.ts +2 -0
  336. package/dist/server/src/config.d.ts.map +1 -1
  337. package/dist/server/src/constants.d.ts +2 -1
  338. package/dist/server/src/constants.d.ts.map +1 -1
  339. package/dist/server/src/controllers/admin-file.d.ts +10 -0
  340. package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
  341. package/dist/server/src/controllers/admin-settings.d.ts.map +1 -1
  342. package/dist/server/src/controllers/index.d.ts +1 -0
  343. package/dist/server/src/controllers/index.d.ts.map +1 -1
  344. package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts +2 -0
  345. package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts.map +1 -0
  346. package/dist/server/src/controllers/validation/admin/settings.d.ts +1 -1
  347. package/dist/server/src/controllers/validation/admin/settings.d.ts.map +1 -1
  348. package/dist/server/src/index.d.ts +7 -1
  349. package/dist/server/src/index.d.ts.map +1 -1
  350. package/dist/server/src/routes/admin.d.ts.map +1 -1
  351. package/dist/server/src/services/ai-metadata.d.ts +19 -0
  352. package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
  353. package/dist/server/src/services/file.d.ts +12 -2
  354. package/dist/server/src/services/file.d.ts.map +1 -1
  355. package/dist/server/src/services/image-manipulation.d.ts +0 -5
  356. package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
  357. package/dist/server/src/services/index.d.ts +4 -1
  358. package/dist/server/src/services/index.d.ts.map +1 -1
  359. package/dist/server/src/types.d.ts +3 -0
  360. package/dist/server/src/types.d.ts.map +1 -1
  361. package/dist/shared/constants.d.ts +35 -0
  362. package/dist/shared/constants.d.ts.map +1 -0
  363. package/dist/shared/contracts/files.d.ts +25 -0
  364. package/dist/shared/contracts/files.d.ts.map +1 -1
  365. package/dist/shared/contracts/settings.d.ts +7 -0
  366. package/package.json +10 -10
  367. package/dist/admin/future/pages/Assets/components/CreateFolderDialog.js.map +0 -1
  368. package/dist/admin/future/pages/Assets/components/CreateFolderDialog.mjs.map +0 -1
  369. package/dist/admin/src/future/pages/Assets/components/CreateFolderDialog.d.ts +0 -8
@@ -1,48 +1,90 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
- import { useEffect, useState, useRef, useMemo, useCallback } from 'react';
2
+ import { useEffect, useMemo, useState, useRef, useCallback } from 'react';
3
3
  import * as ToggleGroup from '@radix-ui/react-toggle-group';
4
- import { usePersistentState, Layouts, useElementOnScreen } from '@strapi/admin/strapi-admin';
5
- import { Box, VisuallyHidden, Flex, SimpleMenu, MenuItem, Loader, Typography } from '@strapi/design-system';
6
- import { List, GridFour, Folder, Files, Link, ChevronDown } from '@strapi/icons';
4
+ import { usePersistentState, useElementOnScreen, Layouts, Page } from '@strapi/admin/strapi-admin';
5
+ import { Box, VisuallyHidden, Typography, SimpleMenu, MenuItem, Flex, Loader } from '@strapi/design-system';
6
+ import { Folder, Files, Link, ChevronDown, List, GridFour } from '@strapi/icons';
7
7
  import { useIntl } from 'react-intl';
8
- import { styled } from 'styled-components';
8
+ import { css, styled } from 'styled-components';
9
+ import { BetaNotice } from '../../components/BetaNotice.mjs';
10
+ import { useAIMetadataEnabled } from '../../hooks/useAIMetadataEnabled.mjs';
11
+ import { useMediaLibraryPermissions } from '../../hooks/useMediaLibraryPermissions.mjs';
9
12
  import { useUploadFilesMutation, useUploadFromUrlsMutation } from '../../services/api.mjs';
10
13
  import { useGetFolderQuery, useGetFoldersQuery } from '../../services/folders.mjs';
14
+ import { useGetUploadSettingsQuery } from '../../services/settings.mjs';
15
+ import { buildItemLocations } from '../../utils/itemLocations.mjs';
11
16
  import { getTranslationKey } from '../../utils/translations.mjs';
12
17
  import { useAssetDetailsParam, AssetDetailsDrawer } from './components/AssetDetails/AssetDetailsDrawer.mjs';
13
18
  import { AssetsGrid } from './components/AssetsGrid.mjs';
19
+ import { AssetsSearchInput } from './components/AssetsSearchInput.mjs';
14
20
  import { AssetsTable } from './components/AssetsTable.mjs';
15
21
  import { BulkActionsBar } from './components/BulkActionsBar.mjs';
16
- import { CreateFolderDialog } from './components/CreateFolderDialog.mjs';
17
22
  import { AssetsDndProvider } from './components/Dnd/AssetsDndProvider.mjs';
18
23
  import { DropZoneWithOverlay, DropFilesMessage } from './components/DropZone/UploadDropZone.mjs';
19
24
  import { UploadDropZoneProvider } from './components/DropZone/UploadDropZoneContext.mjs';
20
- import { EmptyState } from './components/EmptyState.mjs';
25
+ import { FilteredEmptyState, EmptyState } from './components/EmptyState.mjs';
26
+ import { FilterBadges } from './components/FilterBadges.mjs';
27
+ import { FilterMenu } from './components/FilterMenu.mjs';
28
+ import { FolderFormDialog } from './components/FolderFormDialog.mjs';
21
29
  import { FolderTree } from './components/FolderTree/FolderTree.mjs';
22
30
  import { ImportFromUrlDialog } from './components/ImportFromUrlDialog.mjs';
23
31
  import { SortMenu } from './components/SortMenu.mjs';
24
32
  import { localStorageKeys, viewOptions } from './constants.mjs';
33
+ import { useAssetSearch } from './hooks/useAssetSearch.mjs';
25
34
  import { AssetSelectionProvider, useAssetSelection } from './hooks/useAssetSelection.mjs';
35
+ import { BusyAssetsProvider } from './hooks/useBusyAssets.mjs';
26
36
  import { useFolderInfo } from './hooks/useFolderInfo.mjs';
27
37
  import { useFolderNavigation } from './hooks/useFolderNavigation.mjs';
28
38
  import { useInfiniteAssets } from './hooks/useInfiniteAssets.mjs';
39
+ import { useInfiniteScrollSentinel } from './hooks/useInfiniteScrollSentinel.mjs';
40
+ import { useListFilters } from './hooks/useListFilters.mjs';
29
41
  import { useListSort } from './hooks/useListSort.mjs';
42
+ import { buildAssetFilters } from './utils/buildAssetFilters.mjs';
30
43
  import { getListQueryKey } from './utils/listQueryKey.mjs';
31
44
  import { mergeMixedList } from './utils/mergeMixedList.mjs';
32
45
 
46
+ // The negative bottom margin shrinks the trigger area by a pixel so the 1px
47
+ // sentinel sitting exactly at the fold (when a page's rows happen to fill the
48
+ // viewport) reads as "not visible" and doesn't pull an extra page. It still
49
+ // triggers as soon as the sentinel scrolls a hair into view. Tune the bottom
50
+ // value if the list over- or under-fetches near the fold.
33
51
  const INTERSECTION_OPTIONS = {
34
- threshold: 0.1
52
+ threshold: 0,
53
+ rootMargin: '0px 0px -1px 0px'
35
54
  };
36
- const AssetsView = ({ view, folderId, assetsSort, foldersSort, foldersPosition, onAssetItemClick, onAddAssets })=>{
55
+ // Module-level so the header sentinel's observer isn't rebuilt every render.
56
+ const HEADER_SENTINEL_OPTIONS = {
57
+ threshold: 0
58
+ };
59
+ const ITEM_COUNT_MESSAGE = {
60
+ id: getTranslationKey('header.content.item-count'),
61
+ defaultMessage: '{count, plural, =1 {# item} other {# items}}'
62
+ };
63
+ const SEARCH_RESULTS_COUNT_MESSAGES = {
64
+ both: {
65
+ id: getTranslationKey('header.search-results.count'),
66
+ defaultMessage: '{numberFolders, plural, one {1 folder} other {# folders}} - {numberAssets, plural, one {1 asset} other {# assets}}'
67
+ },
68
+ folders: {
69
+ id: getTranslationKey('header.search-results.count.folders'),
70
+ defaultMessage: '{numberFolders, plural, one {1 folder} other {# folders}}'
71
+ },
72
+ assets: {
73
+ id: getTranslationKey('header.search-results.count.assets'),
74
+ defaultMessage: '{numberAssets, plural, =0 {0 assets} one {1 asset} other {# assets}}'
75
+ }
76
+ };
77
+ const getSearchResultsCountMessage = (numberFolders, numberAssets)=>{
78
+ if (numberFolders === 0) {
79
+ return SEARCH_RESULTS_COUNT_MESSAGES.assets;
80
+ }
81
+ if (numberAssets === 0) {
82
+ return SEARCH_RESULTS_COUNT_MESSAGES.folders;
83
+ }
84
+ return SEARCH_RESULTS_COUNT_MESSAGES.both;
85
+ };
86
+ const AssetsView = ({ view, folders, isLoadingFolders, assets, isLoadingAssets, isFetchingMore, hasNextPage, fetchNextPage, error, locations, searchQuery, assetsSort, foldersPosition, hasActiveFilters, onClearFilters, onAssetItemClick, onAddAssets, canAddAssets, onClearSearch })=>{
37
87
  const { formatMessage } = useIntl();
38
- const { assets, isLoading: isLoadingAssets, isFetchingMore, hasNextPage, fetchNextPage, error } = useInfiniteAssets({
39
- folder: folderId,
40
- sort: assetsSort
41
- });
42
- const { data: folders = [], isLoading: isLoadingFolders } = useGetFoldersQuery({
43
- parentId: folderId,
44
- sort: foldersSort
45
- });
46
88
  const isGridView = view === viewOptions.GRID;
47
89
  const isLoading = isLoadingAssets || isLoadingFolders;
48
90
  // "Folders: Mixed with files" — interleave the complete folder list into the
@@ -61,15 +103,12 @@ const AssetsView = ({ view, folderId, assetsSort, foldersSort, foldersPosition,
61
103
  assetsSort,
62
104
  hasNextPage
63
105
  ]);
64
- const loadMoreRef = useElementOnScreen(useCallback((isVisible)=>{
65
- if (isVisible && hasNextPage && !isFetchingMore) {
66
- fetchNextPage();
67
- }
68
- }, [
106
+ const loadMoreRef = useInfiniteScrollSentinel({
69
107
  hasNextPage,
70
108
  isFetchingMore,
71
- fetchNextPage
72
- ]), INTERSECTION_OPTIONS);
109
+ onLoadMore: fetchNextPage,
110
+ options: INTERSECTION_OPTIONS
111
+ });
73
112
  if (isLoading) {
74
113
  return /*#__PURE__*/ jsx(Flex, {
75
114
  justifyContent: "center",
@@ -95,8 +134,15 @@ const AssetsView = ({ view, folderId, assetsSort, foldersSort, foldersPosition,
95
134
  });
96
135
  }
97
136
  if (folders.length === 0 && assets.length === 0) {
98
- return /*#__PURE__*/ jsx(EmptyState, {
99
- onAddAssets: onAddAssets
137
+ // While searching, the search empty state wins (it names the query); a
138
+ // filter-only dead end gets the filtered variant with its Clear action.
139
+ return hasActiveFilters && !searchQuery ? /*#__PURE__*/ jsx(FilteredEmptyState, {
140
+ onClearFilters: onClearFilters
141
+ }) : /*#__PURE__*/ jsx(EmptyState, {
142
+ onAddAssets: onAddAssets,
143
+ canAddAssets: canAddAssets,
144
+ searchQuery: searchQuery,
145
+ onClearSearch: onClearSearch
100
146
  });
101
147
  }
102
148
  return /*#__PURE__*/ jsxs(Fragment, {
@@ -126,6 +172,10 @@ const AssetsView = ({ view, folderId, assetsSort, foldersSort, foldersPosition,
126
172
  defaultMessage: 'Loading more assets...'
127
173
  })
128
174
  })
175
+ }),
176
+ /*#__PURE__*/ jsx(BulkActionsBar, {
177
+ assets: assets,
178
+ locations: locations
129
179
  })
130
180
  ]
131
181
  });
@@ -185,18 +235,128 @@ const StyledToggleItem = styled(ToggleGroup.Item)`
185
235
  height: 1.6rem;
186
236
  }
187
237
  `;
188
- const HeaderWrapper = styled(Box)`
189
- [data-strapi-header] {
190
- background: ${({ theme })=>theme.colors.neutral0};
238
+ // A single sticky header — same layout scrolled or not, so the toolbar
239
+ // alignment can't reflow the way the shared sticky header did. Once the list
240
+ // scrolls under it, it shrinks (reduced vertical padding + a shadow to lift it
241
+ // off the rows), like the sticky headers elsewhere in the admin. Horizontal
242
+ // padding stays responsive (passed as props); only the vertical rhythm + shadow
243
+ // change here so they can animate.
244
+ const StickyHeader = styled(Box)`
245
+ position: sticky;
246
+ top: 0;
247
+ z-index: 2;
248
+ /* Transparent at rest (the grey page shows through); an opaque background +
249
+ shadow appear only once it sticks and content scrolls under it. */
250
+ background: transparent;
251
+ /* Horizontal padding matches the list's default spacing (Layouts.Content /
252
+ RESPONSIVE_DEFAULT_SPACING: 4 / 6 / 10) so the header lines up with the rows. */
253
+ padding: ${({ theme })=>`${theme.spaces[6]} ${theme.spaces[4]}`};
254
+ transition:
255
+ padding 0.2s ease,
256
+ background 0.2s ease,
257
+ box-shadow 0.2s ease;
191
258
 
192
- h1 {
193
- font-size: 1.8rem;
194
- }
259
+ ${({ theme })=>theme.breakpoints.medium} {
260
+ padding-left: ${({ theme })=>theme.spaces[6]};
261
+ padding-right: ${({ theme })=>theme.spaces[6]};
262
+ }
263
+ ${({ theme })=>theme.breakpoints.large} {
264
+ padding-left: ${({ theme })=>theme.spaces[10]};
265
+ padding-right: ${({ theme })=>theme.spaces[10]};
266
+ }
267
+
268
+ /* Compacting is scoped to medium and up, where the header actually sticks. On
269
+ mobile it scrolls away with the list, so shrinking it mid-scroll animated a
270
+ header the user could no longer see — the transition read as a glitch on the
271
+ way back up rather than as the header settling. */
272
+ ${({ $compact, theme })=>$compact && css`
273
+ ${theme.breakpoints.medium} {
274
+ padding-top: ${theme.spaces[3]};
275
+ padding-bottom: ${theme.spaces[3]};
276
+ padding-left: ${theme.spaces[4]};
277
+ padding-right: ${theme.spaces[4]};
278
+ background: ${theme.colors.neutral0};
279
+ box-shadow: ${theme.shadows.tableShadow};
280
+ }
281
+ ${theme.breakpoints.large} {
282
+ padding-left: ${theme.spaces[6]};
283
+ padding-right: ${theme.spaces[6]};
284
+ }
285
+ `}
286
+ `;
287
+ // New button aligns with the title, not the toolbar.
288
+ const TitleRow = styled(Flex)`
289
+ justify-content: space-between;
290
+ align-items: flex-start;
291
+ gap: ${({ theme })=>theme.spaces[4]};
292
+
293
+ h1 {
294
+ font-size: 1.8rem;
295
+ }
296
+ `;
297
+ // Two groups in source order (Filter, Search / Sort, Toggle). Mobile: stacked —
298
+ // Filter+Search on row 1, Sort+Toggle on row 2. Desktop (large+): one row,
299
+ // Filter+Search on the left, Sort+Toggle pushed right.
300
+ const Toolbar = styled(Flex)`
301
+ margin-top: ${({ theme })=>theme.spaces[5]};
302
+ flex-direction: column;
303
+ align-items: stretch;
304
+ gap: ${({ theme })=>theme.spaces[3]};
305
+ transition: margin-top 0.2s ease;
306
+
307
+ /* Tightening the gap to the title belongs to the compact header, so it is
308
+ scoped to the breakpoints that compact. On mobile the header never sticks,
309
+ and this was the last thing still shifting as the page scrolled. */
310
+ ${({ $compact, theme })=>$compact && css`
311
+ ${theme.breakpoints.medium} {
312
+ margin-top: ${theme.spaces[2]};
313
+ }
314
+ `}
315
+
316
+ ${({ theme })=>theme.breakpoints.large} {
317
+ flex-direction: row;
318
+ align-items: center;
319
+ justify-content: space-between;
320
+ }
321
+ `;
322
+ const ToolbarGroup = styled(Flex)`
323
+ align-items: center;
324
+ gap: ${({ theme })=>theme.spaces[3]};
325
+ `;
326
+ // Filter + Search. On mobile the group fills the row (search grows into it);
327
+ // on desktop it keeps its intrinsic size and sits at the left edge.
328
+ const FilterSearchGroup = styled(ToolbarGroup)``;
329
+ // Sort + Toggle. Mobile: spread across the row (Sort left, Toggle right).
330
+ // Desktop: sit together at the right edge (space-between on the toolbar leaves
331
+ // the empty space between the search and here).
332
+ const SortToggleGroup = styled(ToolbarGroup)`
333
+ justify-content: space-between;
334
+
335
+ ${({ theme })=>theme.breakpoints.large} {
336
+ justify-content: flex-end;
337
+ flex: 0 0 auto;
338
+ }
339
+ `;
340
+ // Fills the row on mobile; intrinsic width on desktop.
341
+ const SearchSlot = styled(Box)`
342
+ flex: 1;
343
+
344
+ ${({ theme })=>theme.breakpoints.large} {
345
+ flex: 0 1 auto;
346
+ }
347
+ `;
348
+ // Toggle labels are hidden below desktop — icons only (buttons keep aria-label).
349
+ const ToggleLabel = styled.span`
350
+ display: none;
351
+
352
+ ${({ theme })=>theme.breakpoints.large} {
353
+ display: inline;
195
354
  }
196
355
  `;
197
356
  const AssetsPage = ()=>{
198
357
  const { formatMessage } = useIntl();
199
358
  const { openDetails } = useAssetDetailsParam();
359
+ const { canCreate, canUpdate } = useMediaLibraryPermissions();
200
360
  const { currentFolderId, navigateToFolderId, navigateToRoot } = useFolderNavigation();
201
361
  // Deleted or missing folders (404) need a fetch — handled here, not in
202
362
  // `useFolderNavigation` (which only strips malformed ?folder= values).
@@ -214,16 +374,71 @@ const AssetsPage = ()=>{
214
374
  navigateToRoot
215
375
  ]);
216
376
  const { title, itemCount } = useFolderInfo(currentFolderId);
217
- const itemCountLabel = formatMessage({
218
- id: getTranslationKey('header.content.item-count'),
219
- defaultMessage: '{count, plural, =1 {# item} other {# items}}'
377
+ const { searchQuery, isSearching, clearSearch } = useAssetSearch();
378
+ const listSort = useListSort();
379
+ const listFilters = useListFilters();
380
+ // Resolve relative presets against "now" only when the filters change —
381
+ // keeps the query args (and RTK cache keys) stable between renders.
382
+ const builtFilters = useMemo(()=>buildAssetFilters(listFilters.filters, new Date()), // eslint-disable-next-line react-hooks/exhaustive-deps -- serialized is the value identity of filters
383
+ [
384
+ listFilters.serialized
385
+ ]);
386
+ const { assets, subscribers: assetPageSubscribers, pagination, isLoading: isLoadingAssets, isFetchingMore, hasNextPage, fetchNextPage, error: assetsError } = useInfiniteAssets({
387
+ // The real folder is passed even while searching: the service drops the folder
388
+ // filter when `search` is set, so results are still global. List filters
389
+ // compose with both modes.
390
+ folder: currentFolderId,
391
+ search: searchQuery || undefined,
392
+ sort: listSort.assetsSort,
393
+ filters: builtFilters.fileClauses,
394
+ enabled: builtFilters.showFiles
395
+ });
396
+ const { data: fetchedFolders = [], isLoading: isLoadingFolders } = useGetFoldersQuery({
397
+ parentId: currentFolderId,
398
+ search: searchQuery || undefined,
399
+ sort: listSort.foldersSort,
400
+ filters: builtFilters.folderClauses
220
401
  }, {
402
+ skip: !builtFilters.showFolders
403
+ });
404
+ // A type badge can exclude folders structurally (e.g. "Type is Picture").
405
+ const folders = useMemo(()=>builtFilters.showFolders ? fetchedFolders : [], [
406
+ builtFilters.showFolders,
407
+ fetchedFolders
408
+ ]);
409
+ // Both move affordances (drag and the bulk bar) resolve each item's parent
410
+ // from the rows on screen — while searching, results are global and the
411
+ // folder currently open says nothing about where an item lives.
412
+ const itemLocations = useMemo(()=>buildItemLocations(assets, folders), [
413
+ assets,
414
+ folders
415
+ ]);
416
+ const itemCountLabel = formatMessage(ITEM_COUNT_MESSAGE, {
221
417
  count: itemCount
222
418
  });
223
- const pageHeaderTitle = title ? `${title} (${itemCountLabel})` : formatMessage({
224
- id: 'app.loading',
225
- defaultMessage: 'Loading...'
419
+ const searchResultsTitle = formatMessage({
420
+ id: getTranslationKey('header.search-results'),
421
+ defaultMessage: 'Search results for "{query}"'
422
+ }, {
423
+ query: searchQuery
424
+ });
425
+ const numberFolders = folders.length;
426
+ const numberAssets = pagination?.total ?? 0;
427
+ const searchResultsCountLabel = formatMessage(getSearchResultsCountMessage(numberFolders, numberAssets), {
428
+ numberFolders,
429
+ numberAssets
226
430
  });
431
+ let pageHeaderTitle;
432
+ if (isSearching) {
433
+ pageHeaderTitle = `${searchResultsTitle} (${searchResultsCountLabel})`;
434
+ } else if (title) {
435
+ pageHeaderTitle = `${title} (${itemCountLabel})`;
436
+ } else {
437
+ pageHeaderTitle = formatMessage({
438
+ id: 'app.loading',
439
+ defaultMessage: 'Loading...'
440
+ });
441
+ }
227
442
  const [isCreateFolderDialogOpen, setIsCreateFolderDialogOpen] = useState(false);
228
443
  // View state
229
444
  const [view, setView] = usePersistentState(localStorageKeys.view, viewOptions.GRID);
@@ -233,9 +448,24 @@ const AssetsPage = ()=>{
233
448
  // Refs
234
449
  const fileInputRef = useRef(null);
235
450
  const uploadDropZoneRef = useRef(null);
451
+ // Shrink the sticky header once the list scrolls beneath it. A zero-height
452
+ // sentinel sits at the very top of the scroll content; when it leaves the
453
+ // viewport we're scrolled → compact.
454
+ const [isHeaderCompact, setIsHeaderCompact] = useState(false);
455
+ const onHeaderSentinelVisibility = useCallback((isVisible)=>setIsHeaderCompact(!isVisible), []);
456
+ const headerSentinelRef = useElementOnScreen(onHeaderSentinelVisibility, HEADER_SENTINEL_OPTIONS);
236
457
  // Upload handlers
237
458
  const [uploadFiles] = useUploadFilesMutation();
238
459
  const [uploadFromUrls] = useUploadFromUrlsMutation();
460
+ // `concurrentUploadRequests` echoes the app config. Missing settings (still
461
+ // loading, no permission) fall back to sequential — never faster than the
462
+ // server asked for.
463
+ const { data: settings } = useGetUploadSettingsQuery();
464
+ const concurrency = settings?.data?.concurrentUploadRequests ?? 1;
465
+ // Drives the post-upload AI metadata phase shown per row in the progress
466
+ // dialog. No mime argument: the files are not known until they are picked,
467
+ // and the server filters the batch on the same allowlist anyway.
468
+ const isAiMetadataEnabled = useAIMetadataEnabled();
239
469
  const uploadFilesToFolder = async (files, folderId)=>{
240
470
  if (files.length === 0) return;
241
471
  const formData = new FormData();
@@ -253,9 +483,11 @@ const AssetsPage = ()=>{
253
483
  try {
254
484
  await uploadFiles({
255
485
  formData,
256
- totalFiles: files.length
486
+ totalFiles: files.length,
487
+ concurrency,
488
+ generateAiMetadata: Boolean(isAiMetadataEnabled)
257
489
  }).unwrap();
258
- } catch (error) {
490
+ } catch {
259
491
  // Error is already dispatched to store from the API queryFn
260
492
  }
261
493
  };
@@ -270,188 +502,239 @@ const AssetsPage = ()=>{
270
502
  e.target.value = '';
271
503
  };
272
504
  const handleDrop = async (files)=>{
505
+ // Defence in depth: the provider is `disabled` without `assets.create`
506
+ // (so onDrop won't fire), but guard here too in case it's ever wired live.
507
+ if (!canCreate) return;
273
508
  await uploadFilesToFolder(files, currentFolderId);
274
509
  };
275
510
  const handleUrlUpload = async (urls)=>{
276
511
  try {
277
512
  await uploadFromUrls({
278
513
  urls,
279
- folderId: currentFolderId
514
+ folderId: currentFolderId,
515
+ generateAiMetadata: Boolean(isAiMetadataEnabled)
280
516
  }).unwrap();
281
- } catch (error) {
517
+ } catch {
282
518
  // Error is already dispatched to store from the API queryFn
283
519
  }
284
520
  };
285
- const listSort = useListSort();
286
521
  // The view is deliberately absent: table and grid render the same list, so
287
522
  // toggling views keeps the selection.
288
523
  const listQueryKey = getListQueryKey({
289
524
  folderId: currentFolderId,
290
- search: '',
525
+ search: searchQuery,
291
526
  // Folder position changes the render order too — selection must reset.
292
527
  sort: `${listSort.assetsSort};folders=${listSort.foldersPosition}`,
293
- filter: null
528
+ filter: listFilters.serialized || null
294
529
  });
295
530
  return /*#__PURE__*/ jsxs(Fragment, {
296
531
  children: [
297
532
  /*#__PURE__*/ jsx(UploadDropZoneProvider, {
298
533
  onDrop: handleDrop,
534
+ disabled: !canCreate,
299
535
  children: /*#__PURE__*/ jsx(AssetSelectionProvider, {
300
- children: /*#__PURE__*/ jsxs(AssetsDndProvider, {
301
- children: [
302
- /*#__PURE__*/ jsx(ClearSelectionOnChange, {
303
- listQueryKey: listQueryKey
304
- }),
305
- /*#__PURE__*/ jsx(Box, {
306
- ref: uploadDropZoneRef,
307
- children: /*#__PURE__*/ jsxs(Layouts.Root, {
308
- minHeight: "100vh",
309
- background: "neutral0",
536
+ disabled: !canUpdate,
537
+ children: /*#__PURE__*/ jsx(BusyAssetsProvider, {
538
+ children: /*#__PURE__*/ jsxs(AssetsDndProvider, {
539
+ locations: itemLocations,
540
+ children: [
541
+ /*#__PURE__*/ jsx(ClearSelectionOnChange, {
542
+ listQueryKey: listQueryKey
543
+ }),
544
+ /*#__PURE__*/ jsx(Layouts.Root, {
310
545
  sideNav: /*#__PURE__*/ jsx(FolderTree, {
311
546
  currentFolderId: currentFolderId,
547
+ showActiveFolder: !isSearching,
312
548
  onSelectFolder: navigateToFolderId
313
549
  }),
314
- children: [
315
- /*#__PURE__*/ jsx(VisuallyHidden, {
316
- children: /*#__PURE__*/ jsx("input", {
317
- type: "file",
318
- ref: fileInputRef,
319
- onChange: handleFileChange,
320
- multiple: true
321
- })
322
- }),
323
- /*#__PURE__*/ jsx(HeaderWrapper, {
324
- children: /*#__PURE__*/ jsx(Layouts.Header, {
325
- title: pageHeaderTitle,
326
- primaryAction: /*#__PURE__*/ jsxs(SimpleMenu, {
327
- popoverPlacement: "bottom-end",
328
- variant: "default",
329
- endIcon: /*#__PURE__*/ jsx(ChevronDown, {}),
330
- label: formatMessage({
331
- id: getTranslationKey('new'),
332
- defaultMessage: 'New'
333
- }),
334
- children: [
335
- /*#__PURE__*/ jsx(MenuItem, {
336
- onSelect: ()=>setIsCreateFolderDialogOpen(true),
337
- startIcon: /*#__PURE__*/ jsx(Folder, {}),
338
- children: formatMessage({
339
- id: getTranslationKey('folder.create.title'),
340
- defaultMessage: 'New folder'
341
- })
342
- }),
343
- /*#__PURE__*/ jsx(MenuItem, {
344
- onSelect: handleFileSelect,
345
- startIcon: /*#__PURE__*/ jsx(Files, {}),
346
- children: formatMessage({
347
- id: getTranslationKey('import-files'),
348
- defaultMessage: 'Import files'
349
- })
350
- }),
351
- /*#__PURE__*/ jsx(MenuItem, {
352
- onSelect: ()=>setIsUrlDialogOpen(true),
353
- startIcon: /*#__PURE__*/ jsx(Link, {}),
354
- children: formatMessage({
355
- id: getTranslationKey('import-from-url'),
356
- defaultMessage: 'Import from URL'
357
- })
358
- })
359
- ]
550
+ children: /*#__PURE__*/ jsx(Page.Main, {
551
+ children: /*#__PURE__*/ jsxs(Box, {
552
+ ref: uploadDropZoneRef,
553
+ children: [
554
+ /*#__PURE__*/ jsx(VisuallyHidden, {
555
+ children: /*#__PURE__*/ jsx("input", {
556
+ type: "file",
557
+ ref: fileInputRef,
558
+ onChange: handleFileChange,
559
+ multiple: true
560
+ })
561
+ }),
562
+ /*#__PURE__*/ jsx(Box, {
563
+ ref: headerSentinelRef,
564
+ height: 0,
565
+ "aria-hidden": true
360
566
  }),
361
- subtitle: /*#__PURE__*/ jsxs(Flex, {
362
- justifyContent: "space-between",
363
- alignItems: "center",
364
- gap: 4,
365
- width: "100%",
567
+ /*#__PURE__*/ jsxs(StickyHeader, {
568
+ $compact: isHeaderCompact,
366
569
  children: [
367
- /*#__PURE__*/ jsx(Flex, {
368
- gap: 4,
369
- alignItems: "center",
370
- children: "TODO: Filters and search"
371
- }),
372
- /*#__PURE__*/ jsxs(Flex, {
373
- gap: 4,
374
- alignItems: "stretch",
570
+ /*#__PURE__*/ jsxs(TitleRow, {
375
571
  children: [
376
- /*#__PURE__*/ jsx(SortMenu, {
377
- sort: listSort,
378
- showFoldersGroup: !isGridView
572
+ /*#__PURE__*/ jsx(Typography, {
573
+ variant: "alpha",
574
+ tag: "h1",
575
+ children: pageHeaderTitle
379
576
  }),
380
- /*#__PURE__*/ jsxs(StyledToggleGroup, {
381
- type: "single",
382
- value: isGridView ? 'grid' : 'table',
383
- onValueChange: (value)=>value && setView(value === 'grid' ? viewOptions.GRID : viewOptions.TABLE),
384
- "aria-label": formatMessage({
385
- id: getTranslationKey('view.switch.label'),
386
- defaultMessage: 'View options'
577
+ canCreate && /*#__PURE__*/ jsxs(SimpleMenu, {
578
+ popoverPlacement: "bottom-end",
579
+ variant: "default",
580
+ endIcon: /*#__PURE__*/ jsx(ChevronDown, {}),
581
+ label: formatMessage({
582
+ id: getTranslationKey('new'),
583
+ defaultMessage: 'New'
387
584
  }),
388
585
  children: [
389
- /*#__PURE__*/ jsxs(StyledToggleItem, {
390
- value: "table",
391
- "aria-label": formatMessage({
392
- id: getTranslationKey('view.table'),
393
- defaultMessage: 'Table view'
394
- }),
395
- children: [
396
- /*#__PURE__*/ jsx(List, {}),
397
- formatMessage({
398
- id: getTranslationKey('view.table'),
399
- defaultMessage: 'Table view'
400
- })
401
- ]
586
+ /*#__PURE__*/ jsx(MenuItem, {
587
+ onSelect: ()=>setIsCreateFolderDialogOpen(true),
588
+ startIcon: /*#__PURE__*/ jsx(Folder, {}),
589
+ children: formatMessage({
590
+ id: getTranslationKey('folder.create.title'),
591
+ defaultMessage: 'New folder'
592
+ })
593
+ }),
594
+ /*#__PURE__*/ jsx(MenuItem, {
595
+ onSelect: handleFileSelect,
596
+ startIcon: /*#__PURE__*/ jsx(Files, {}),
597
+ children: formatMessage({
598
+ id: getTranslationKey('import-files'),
599
+ defaultMessage: 'Import files'
600
+ })
402
601
  }),
403
- /*#__PURE__*/ jsxs(StyledToggleItem, {
404
- value: "grid",
602
+ /*#__PURE__*/ jsx(MenuItem, {
603
+ onSelect: ()=>setIsUrlDialogOpen(true),
604
+ startIcon: /*#__PURE__*/ jsx(Link, {}),
605
+ children: formatMessage({
606
+ id: getTranslationKey('import-from-url'),
607
+ defaultMessage: 'Import from URL'
608
+ })
609
+ })
610
+ ]
611
+ })
612
+ ]
613
+ }),
614
+ /*#__PURE__*/ jsxs(Toolbar, {
615
+ $compact: isHeaderCompact,
616
+ children: [
617
+ /*#__PURE__*/ jsxs(FilterSearchGroup, {
618
+ children: [
619
+ /*#__PURE__*/ jsx(Box, {
620
+ children: /*#__PURE__*/ jsx(FilterMenu, {
621
+ listFilters: listFilters
622
+ })
623
+ }),
624
+ /*#__PURE__*/ jsx(SearchSlot, {
625
+ children: /*#__PURE__*/ jsx(AssetsSearchInput, {})
626
+ })
627
+ ]
628
+ }),
629
+ /*#__PURE__*/ jsxs(SortToggleGroup, {
630
+ children: [
631
+ /*#__PURE__*/ jsx(Box, {
632
+ children: /*#__PURE__*/ jsx(SortMenu, {
633
+ sort: listSort,
634
+ showFoldersGroup: !isGridView
635
+ })
636
+ }),
637
+ /*#__PURE__*/ jsxs(StyledToggleGroup, {
638
+ type: "single",
639
+ value: isGridView ? 'grid' : 'table',
640
+ onValueChange: (value)=>value && setView(value === 'grid' ? viewOptions.GRID : viewOptions.TABLE),
405
641
  "aria-label": formatMessage({
406
- id: getTranslationKey('view.grid'),
407
- defaultMessage: 'Grid view'
642
+ id: getTranslationKey('view.switch.label'),
643
+ defaultMessage: 'View options'
408
644
  }),
409
645
  children: [
410
- /*#__PURE__*/ jsx(GridFour, {}),
411
- formatMessage({
412
- id: getTranslationKey('view.grid'),
413
- defaultMessage: 'Grid view'
646
+ /*#__PURE__*/ jsxs(StyledToggleItem, {
647
+ value: "table",
648
+ "aria-label": formatMessage({
649
+ id: getTranslationKey('view.table'),
650
+ defaultMessage: 'Table view'
651
+ }),
652
+ children: [
653
+ /*#__PURE__*/ jsx(List, {}),
654
+ /*#__PURE__*/ jsx(ToggleLabel, {
655
+ children: formatMessage({
656
+ id: getTranslationKey('view.table'),
657
+ defaultMessage: 'Table view'
658
+ })
659
+ })
660
+ ]
661
+ }),
662
+ /*#__PURE__*/ jsxs(StyledToggleItem, {
663
+ value: "grid",
664
+ "aria-label": formatMessage({
665
+ id: getTranslationKey('view.grid'),
666
+ defaultMessage: 'Grid view'
667
+ }),
668
+ children: [
669
+ /*#__PURE__*/ jsx(GridFour, {}),
670
+ /*#__PURE__*/ jsx(ToggleLabel, {
671
+ children: formatMessage({
672
+ id: getTranslationKey('view.grid'),
673
+ defaultMessage: 'Grid view'
674
+ })
675
+ })
676
+ ]
414
677
  })
415
678
  ]
416
679
  })
417
680
  ]
418
681
  })
419
682
  ]
683
+ }),
684
+ /*#__PURE__*/ jsx(FilterBadges, {
685
+ listFilters: listFilters,
686
+ compact: isHeaderCompact
687
+ })
688
+ ]
689
+ }),
690
+ /*#__PURE__*/ jsxs(Layouts.Content, {
691
+ children: [
692
+ /*#__PURE__*/ jsx(BetaNotice, {}),
693
+ assetPageSubscribers,
694
+ /*#__PURE__*/ jsxs(DropZoneWithOverlay, {
695
+ children: [
696
+ /*#__PURE__*/ jsx(DropFilesMessage, {
697
+ uploadDropZoneRef: uploadDropZoneRef,
698
+ folderName: title
699
+ }),
700
+ /*#__PURE__*/ jsx(AssetsView, {
701
+ view: view,
702
+ folders: folders,
703
+ isLoadingFolders: isLoadingFolders,
704
+ assets: assets,
705
+ isLoadingAssets: isLoadingAssets,
706
+ isFetchingMore: isFetchingMore,
707
+ hasNextPage: hasNextPage,
708
+ fetchNextPage: fetchNextPage,
709
+ error: assetsError,
710
+ locations: itemLocations,
711
+ searchQuery: searchQuery,
712
+ assetsSort: listSort.assetsSort,
713
+ foldersPosition: listSort.foldersPosition,
714
+ hasActiveFilters: listFilters.filters.length > 0,
715
+ onClearFilters: listFilters.clearFilters,
716
+ onAssetItemClick: openDetails,
717
+ onAddAssets: handleFileSelect,
718
+ canAddAssets: canCreate,
719
+ onClearSearch: clearSearch
720
+ })
721
+ ]
420
722
  })
421
723
  ]
422
724
  })
423
- })
424
- }),
425
- /*#__PURE__*/ jsx(Layouts.Content, {
426
- children: /*#__PURE__*/ jsxs(DropZoneWithOverlay, {
427
- children: [
428
- /*#__PURE__*/ jsx(DropFilesMessage, {
429
- uploadDropZoneRef: uploadDropZoneRef,
430
- folderName: title
431
- }),
432
- /*#__PURE__*/ jsx(AssetsView, {
433
- view: view,
434
- folderId: currentFolderId,
435
- assetsSort: listSort.assetsSort,
436
- foldersSort: listSort.foldersSort,
437
- foldersPosition: listSort.foldersPosition,
438
- onAssetItemClick: openDetails,
439
- onAddAssets: handleFileSelect
440
- })
441
- ]
442
- })
725
+ ]
443
726
  })
444
- ]
727
+ })
445
728
  })
446
- }),
447
- /*#__PURE__*/ jsx(BulkActionsBar, {})
448
- ]
729
+ ]
730
+ })
449
731
  })
450
732
  })
451
733
  }),
452
- /*#__PURE__*/ jsx(CreateFolderDialog, {
734
+ /*#__PURE__*/ jsx(FolderFormDialog, {
453
735
  open: isCreateFolderDialogOpen,
454
- folderName: title,
736
+ mode: "create",
737
+ parentFolderName: title,
455
738
  parentFolderId: currentFolderId,
456
739
  onClose: ()=>setIsCreateFolderDialogOpen(false)
457
740
  }),