@strapi/upload 5.51.0 → 5.51.2

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 (346) 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/components/Drawer.js +3 -1
  14. package/dist/admin/future/components/Drawer.js.map +1 -1
  15. package/dist/admin/future/components/Drawer.mjs +3 -1
  16. package/dist/admin/future/components/Drawer.mjs.map +1 -1
  17. package/dist/admin/future/components/UploadProgressDialog.js +200 -27
  18. package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
  19. package/dist/admin/future/components/UploadProgressDialog.mjs +203 -30
  20. package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
  21. package/dist/admin/future/hooks/useMediaLibraryPermissions.js +35 -0
  22. package/dist/admin/future/hooks/useMediaLibraryPermissions.js.map +1 -0
  23. package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs +33 -0
  24. package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs.map +1 -0
  25. package/dist/admin/future/pages/Assets/AssetsPage.js +344 -150
  26. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  27. package/dist/admin/future/pages/Assets/AssetsPage.mjs +343 -149
  28. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  29. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +75 -32
  30. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
  31. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +75 -32
  32. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
  33. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +46 -19
  34. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  35. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +47 -20
  36. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  37. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +161 -19
  38. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +162 -20
  40. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +100 -0
  42. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -0
  43. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +98 -0
  44. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -0
  45. package/dist/admin/future/pages/Assets/components/AssetsTable.js +218 -37
  46. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  47. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +219 -38
  48. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  49. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +370 -0
  50. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -0
  51. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +368 -0
  52. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -0
  53. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +209 -0
  54. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -0
  55. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +207 -0
  56. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -0
  57. package/dist/admin/future/pages/Assets/components/DateRangeCalendar.js +193 -0
  58. package/dist/admin/future/pages/Assets/components/DateRangeCalendar.js.map +1 -0
  59. package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs +191 -0
  60. package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs.map +1 -0
  61. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +129 -37
  62. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  63. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +131 -39
  64. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  65. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js +120 -13
  66. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js.map +1 -1
  67. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs +120 -13
  68. package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs.map +1 -1
  69. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +71 -0
  70. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -0
  71. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +69 -0
  72. package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -0
  73. package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.js +34 -0
  74. package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.js.map +1 -0
  75. package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.mjs +32 -0
  76. package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.mjs.map +1 -0
  77. package/dist/admin/future/pages/Assets/components/Dnd/dndIds.js +15 -0
  78. package/dist/admin/future/pages/Assets/components/Dnd/dndIds.js.map +1 -1
  79. package/dist/admin/future/pages/Assets/components/Dnd/dndIds.mjs +13 -1
  80. package/dist/admin/future/pages/Assets/components/Dnd/dndIds.mjs.map +1 -1
  81. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +19 -25
  82. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
  83. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +19 -25
  84. package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
  85. package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.js +35 -0
  86. package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.js.map +1 -0
  87. package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.mjs +33 -0
  88. package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.mjs.map +1 -0
  89. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js +13 -3
  90. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js.map +1 -1
  91. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs +13 -3
  92. package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs.map +1 -1
  93. package/dist/admin/future/pages/Assets/components/EmptyState.js +62 -3
  94. package/dist/admin/future/pages/Assets/components/EmptyState.js.map +1 -1
  95. package/dist/admin/future/pages/Assets/components/EmptyState.mjs +62 -4
  96. package/dist/admin/future/pages/Assets/components/EmptyState.mjs.map +1 -1
  97. package/dist/admin/future/pages/Assets/components/FilterBadges.js +408 -0
  98. package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -0
  99. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +406 -0
  100. package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -0
  101. package/dist/admin/future/pages/Assets/components/FilterMenu.js +273 -0
  102. package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -0
  103. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +267 -0
  104. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -0
  105. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +195 -40
  106. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  107. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +198 -43
  108. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  109. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js +33 -0
  110. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js.map +1 -0
  111. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs +31 -0
  112. package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs.map +1 -0
  113. package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.js +22 -0
  114. package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.js.map +1 -0
  115. package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.mjs +19 -0
  116. package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.mjs.map +1 -0
  117. package/dist/admin/future/pages/Assets/components/SortMenu.js +154 -0
  118. package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -0
  119. package/dist/admin/future/pages/Assets/components/SortMenu.mjs +152 -0
  120. package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -0
  121. package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js +40 -0
  122. package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js.map +1 -0
  123. package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs +38 -0
  124. package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs.map +1 -0
  125. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +75 -0
  126. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -0
  127. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +71 -0
  128. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -0
  129. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js +33 -12
  130. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js.map +1 -1
  131. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs +33 -12
  132. package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs.map +1 -1
  133. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +40 -4
  134. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
  135. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +40 -4
  136. package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
  137. package/dist/admin/future/pages/Assets/hooks/useListFilters.js +168 -0
  138. package/dist/admin/future/pages/Assets/hooks/useListFilters.js.map +1 -0
  139. package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs +162 -0
  140. package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs.map +1 -0
  141. package/dist/admin/future/pages/Assets/hooks/useListSort.js +124 -0
  142. package/dist/admin/future/pages/Assets/hooks/useListSort.js.map +1 -0
  143. package/dist/admin/future/pages/Assets/hooks/useListSort.mjs +120 -0
  144. package/dist/admin/future/pages/Assets/hooks/useListSort.mjs.map +1 -0
  145. package/dist/admin/future/pages/Assets/utils/buildAssetFilters.js +201 -0
  146. package/dist/admin/future/pages/Assets/utils/buildAssetFilters.js.map +1 -0
  147. package/dist/admin/future/pages/Assets/utils/buildAssetFilters.mjs +198 -0
  148. package/dist/admin/future/pages/Assets/utils/buildAssetFilters.mjs.map +1 -0
  149. package/dist/admin/future/pages/Assets/utils/listQueryKey.js +29 -0
  150. package/dist/admin/future/pages/Assets/utils/listQueryKey.js.map +1 -0
  151. package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs +27 -0
  152. package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs.map +1 -0
  153. package/dist/admin/future/pages/Assets/utils/mergeMixedList.js +84 -0
  154. package/dist/admin/future/pages/Assets/utils/mergeMixedList.js.map +1 -0
  155. package/dist/admin/future/pages/Assets/utils/mergeMixedList.mjs +81 -0
  156. package/dist/admin/future/pages/Assets/utils/mergeMixedList.mjs.map +1 -0
  157. package/dist/admin/future/pages/Assets/utils/selection.js +104 -0
  158. package/dist/admin/future/pages/Assets/utils/selection.js.map +1 -0
  159. package/dist/admin/future/pages/Assets/utils/selection.mjs +94 -0
  160. package/dist/admin/future/pages/Assets/utils/selection.mjs.map +1 -0
  161. package/dist/admin/future/services/api.js +163 -28
  162. package/dist/admin/future/services/api.js.map +1 -1
  163. package/dist/admin/future/services/api.mjs +163 -30
  164. package/dist/admin/future/services/api.mjs.map +1 -1
  165. package/dist/admin/future/services/assets.js +62 -18
  166. package/dist/admin/future/services/assets.js.map +1 -1
  167. package/dist/admin/future/services/assets.mjs +62 -19
  168. package/dist/admin/future/services/assets.mjs.map +1 -1
  169. package/dist/admin/future/services/folders.js +31 -19
  170. package/dist/admin/future/services/folders.js.map +1 -1
  171. package/dist/admin/future/services/folders.mjs +31 -19
  172. package/dist/admin/future/services/folders.mjs.map +1 -1
  173. package/dist/admin/future/services/settings.js +10 -3
  174. package/dist/admin/future/services/settings.js.map +1 -1
  175. package/dist/admin/future/services/settings.mjs +10 -3
  176. package/dist/admin/future/services/settings.mjs.map +1 -1
  177. package/dist/admin/future/store/uploadProgress.js +109 -2
  178. package/dist/admin/future/store/uploadProgress.js.map +1 -1
  179. package/dist/admin/future/store/uploadProgress.mjs +105 -3
  180. package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
  181. package/dist/admin/future/utils/buildDragSetFromSelection.js +33 -0
  182. package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -0
  183. package/dist/admin/future/utils/buildDragSetFromSelection.mjs +31 -0
  184. package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -0
  185. package/dist/admin/future/utils/canDropItemOnFolder.js +4 -0
  186. package/dist/admin/future/utils/canDropItemOnFolder.js.map +1 -1
  187. package/dist/admin/future/utils/canDropItemOnFolder.mjs +4 -0
  188. package/dist/admin/future/utils/canDropItemOnFolder.mjs.map +1 -1
  189. package/dist/admin/future/utils/flattenFolderStructure.js +28 -0
  190. package/dist/admin/future/utils/flattenFolderStructure.js.map +1 -0
  191. package/dist/admin/future/utils/flattenFolderStructure.mjs +26 -0
  192. package/dist/admin/future/utils/flattenFolderStructure.mjs.map +1 -0
  193. package/dist/admin/future/utils/formatMoveSuccessMessage.js +19 -0
  194. package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -0
  195. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +17 -0
  196. package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -0
  197. package/dist/admin/future/utils/getFolderLabel.js +31 -0
  198. package/dist/admin/future/utils/getFolderLabel.js.map +1 -0
  199. package/dist/admin/future/utils/getFolderLabel.mjs +29 -0
  200. package/dist/admin/future/utils/getFolderLabel.mjs.map +1 -0
  201. package/dist/admin/future/utils/searchQueryParam.js +11 -0
  202. package/dist/admin/future/utils/searchQueryParam.js.map +1 -0
  203. package/dist/admin/future/utils/searchQueryParam.mjs +9 -0
  204. package/dist/admin/future/utils/searchQueryParam.mjs.map +1 -0
  205. package/dist/admin/hooks/useSelectionState.js +2 -4
  206. package/dist/admin/hooks/useSelectionState.js.map +1 -1
  207. package/dist/admin/hooks/useSelectionState.mjs +2 -4
  208. package/dist/admin/hooks/useSelectionState.mjs.map +1 -1
  209. package/dist/admin/shared/constants.js +43 -0
  210. package/dist/admin/shared/constants.js.map +1 -0
  211. package/dist/admin/shared/constants.mjs +38 -0
  212. package/dist/admin/shared/constants.mjs.map +1 -0
  213. package/dist/admin/src/future/hooks/useMediaLibraryPermissions.d.ts +21 -0
  214. package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetCropEditor.d.ts +7 -1
  215. package/dist/admin/src/future/pages/Assets/components/AssetsSearchInput.d.ts +5 -0
  216. package/dist/admin/src/future/pages/Assets/components/AssetsTable.d.ts +7 -1
  217. package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +14 -0
  218. package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +13 -0
  219. package/dist/admin/src/future/pages/Assets/components/DateRangeCalendar.d.ts +8 -0
  220. package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +2 -0
  221. package/dist/admin/src/future/pages/Assets/components/Dnd/DragOverlayChip.d.ts +2 -2
  222. package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +17 -0
  223. package/dist/admin/src/future/pages/Assets/components/Dnd/computeValidDropTargets.d.ts +7 -0
  224. package/dist/admin/src/future/pages/Assets/components/Dnd/dndIds.d.ts +3 -0
  225. package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +3 -1
  226. package/dist/admin/src/future/pages/Assets/components/Dnd/useFolderTreeDroppable.d.ts +19 -0
  227. package/dist/admin/src/future/pages/Assets/components/DropZone/UploadDropZoneContext.d.ts +8 -1
  228. package/dist/admin/src/future/pages/Assets/components/EmptyState.d.ts +18 -1
  229. package/dist/admin/src/future/pages/Assets/components/FilterBadges.d.ts +6 -0
  230. package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +22 -0
  231. package/dist/admin/src/future/pages/Assets/components/FolderTree/FolderTree.d.ts +13 -4
  232. package/dist/admin/src/future/pages/Assets/components/FolderTree/filterFolderTree.d.ts +16 -0
  233. package/dist/admin/src/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.d.ts +6 -0
  234. package/dist/admin/src/future/pages/Assets/components/SortMenu.d.ts +23 -0
  235. package/dist/admin/src/future/pages/Assets/hooks/useAssetSearch.d.ts +12 -0
  236. package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +46 -0
  237. package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +13 -3
  238. package/dist/admin/src/future/pages/Assets/hooks/useListFilters.d.ts +69 -0
  239. package/dist/admin/src/future/pages/Assets/hooks/useListSort.d.ts +47 -0
  240. package/dist/admin/src/future/pages/Assets/utils/buildAssetFilters.d.ts +46 -0
  241. package/dist/admin/src/future/pages/Assets/utils/listQueryKey.d.ts +29 -0
  242. package/dist/admin/src/future/pages/Assets/utils/mergeMixedList.d.ts +40 -0
  243. package/dist/admin/src/future/pages/Assets/utils/selection.d.ts +50 -0
  244. package/dist/admin/src/future/services/api.d.ts +53 -5
  245. package/dist/admin/src/future/services/assets.d.ts +13 -2
  246. package/dist/admin/src/future/services/folders.d.ts +7 -1
  247. package/dist/admin/src/future/services/settings.d.ts +2 -2
  248. package/dist/admin/src/future/store/uploadProgress.d.ts +83 -1
  249. package/dist/admin/src/future/types/dnd.d.ts +5 -0
  250. package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +11 -0
  251. package/dist/admin/src/future/utils/canDropItemOnFolder.d.ts +1 -1
  252. package/dist/admin/src/future/utils/flattenFolderStructure.d.ts +18 -0
  253. package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +14 -0
  254. package/dist/admin/src/future/utils/getFolderLabel.d.ts +11 -0
  255. package/dist/admin/src/future/utils/searchQueryParam.d.ts +7 -0
  256. package/dist/admin/src/hooks/useAiAvailability.d.ts +1 -1
  257. package/dist/admin/src/hooks/useEditAsset.d.ts +4 -4
  258. package/dist/admin/src/hooks/useEditFolder.d.ts +4 -4
  259. package/dist/admin/src/hooks/useSettings.d.ts +1 -0
  260. package/dist/admin/src/hooks/useTracking.d.ts +1 -1
  261. package/dist/admin/src/hooks/useUpload.d.ts +1 -1
  262. package/dist/admin/translations/en.json.js +85 -2
  263. package/dist/admin/translations/en.json.js.map +1 -1
  264. package/dist/admin/translations/en.json.mjs +85 -2
  265. package/dist/admin/translations/en.json.mjs.map +1 -1
  266. package/dist/admin/utils/urlYupSchema.js +33 -9
  267. package/dist/admin/utils/urlYupSchema.js.map +1 -1
  268. package/dist/admin/utils/urlYupSchema.mjs +33 -9
  269. package/dist/admin/utils/urlYupSchema.mjs.map +1 -1
  270. package/dist/server/config.js +13 -5
  271. package/dist/server/config.js.map +1 -1
  272. package/dist/server/config.mjs +13 -5
  273. package/dist/server/config.mjs.map +1 -1
  274. package/dist/server/constants.js.map +1 -1
  275. package/dist/server/constants.mjs.map +1 -1
  276. package/dist/server/controllers/admin-file.js +30 -0
  277. package/dist/server/controllers/admin-file.js.map +1 -1
  278. package/dist/server/controllers/admin-file.mjs +30 -0
  279. package/dist/server/controllers/admin-file.mjs.map +1 -1
  280. package/dist/server/controllers/admin-settings.js +20 -2
  281. package/dist/server/controllers/admin-settings.js.map +1 -1
  282. package/dist/server/controllers/admin-settings.mjs +21 -3
  283. package/dist/server/controllers/admin-settings.mjs.map +1 -1
  284. package/dist/server/controllers/content-api.js +1 -2
  285. package/dist/server/controllers/content-api.js.map +1 -1
  286. package/dist/server/controllers/content-api.mjs +2 -2
  287. package/dist/server/controllers/content-api.mjs.map +1 -1
  288. package/dist/server/controllers/validation/admin/ai-metadata.js +12 -0
  289. package/dist/server/controllers/validation/admin/ai-metadata.js.map +1 -0
  290. package/dist/server/controllers/validation/admin/ai-metadata.mjs +10 -0
  291. package/dist/server/controllers/validation/admin/ai-metadata.mjs.map +1 -0
  292. package/dist/server/controllers/validation/admin/settings.js +4 -1
  293. package/dist/server/controllers/validation/admin/settings.js.map +1 -1
  294. package/dist/server/controllers/validation/admin/settings.mjs +1 -1
  295. package/dist/server/controllers/validation/admin/settings.mjs.map +1 -1
  296. package/dist/server/routes/admin.js +18 -0
  297. package/dist/server/routes/admin.js.map +1 -1
  298. package/dist/server/routes/admin.mjs +18 -0
  299. package/dist/server/routes/admin.mjs.map +1 -1
  300. package/dist/server/services/ai-metadata.js +108 -11
  301. package/dist/server/services/ai-metadata.js.map +1 -1
  302. package/dist/server/services/ai-metadata.mjs +108 -11
  303. package/dist/server/services/ai-metadata.mjs.map +1 -1
  304. package/dist/server/services/file.js +86 -13
  305. package/dist/server/services/file.js.map +1 -1
  306. package/dist/server/services/file.mjs +87 -14
  307. package/dist/server/services/file.mjs.map +1 -1
  308. package/dist/server/services/image-manipulation.js +4 -4
  309. package/dist/server/services/image-manipulation.js.map +1 -1
  310. package/dist/server/services/image-manipulation.mjs +4 -4
  311. package/dist/server/services/image-manipulation.mjs.map +1 -1
  312. package/dist/server/shared/constants.js +43 -0
  313. package/dist/server/shared/constants.js.map +1 -0
  314. package/dist/server/shared/constants.mjs +38 -0
  315. package/dist/server/shared/constants.mjs.map +1 -0
  316. package/dist/server/src/config.d.ts +2 -0
  317. package/dist/server/src/config.d.ts.map +1 -1
  318. package/dist/server/src/constants.d.ts +2 -1
  319. package/dist/server/src/constants.d.ts.map +1 -1
  320. package/dist/server/src/controllers/admin-file.d.ts +10 -0
  321. package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
  322. package/dist/server/src/controllers/admin-settings.d.ts.map +1 -1
  323. package/dist/server/src/controllers/index.d.ts +1 -0
  324. package/dist/server/src/controllers/index.d.ts.map +1 -1
  325. package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts +2 -0
  326. package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts.map +1 -0
  327. package/dist/server/src/controllers/validation/admin/settings.d.ts +1 -1
  328. package/dist/server/src/controllers/validation/admin/settings.d.ts.map +1 -1
  329. package/dist/server/src/index.d.ts +7 -1
  330. package/dist/server/src/index.d.ts.map +1 -1
  331. package/dist/server/src/routes/admin.d.ts.map +1 -1
  332. package/dist/server/src/services/ai-metadata.d.ts +19 -0
  333. package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
  334. package/dist/server/src/services/file.d.ts +12 -2
  335. package/dist/server/src/services/file.d.ts.map +1 -1
  336. package/dist/server/src/services/index.d.ts +4 -1
  337. package/dist/server/src/services/index.d.ts.map +1 -1
  338. package/dist/server/src/types.d.ts +3 -0
  339. package/dist/server/src/types.d.ts.map +1 -1
  340. package/dist/shared/constants.d.ts +35 -0
  341. package/dist/shared/constants.d.ts.map +1 -0
  342. package/dist/shared/contracts/files.d.ts +25 -0
  343. package/dist/shared/contracts/files.d.ts.map +1 -1
  344. package/dist/shared/contracts/folders.d.ts +1 -1
  345. package/dist/shared/contracts/settings.d.ts +7 -0
  346. package/package.json +8 -8
@@ -0,0 +1,81 @@
1
+ const getComparableValue = (record, field)=>{
2
+ switch(field){
3
+ case 'createdAt':
4
+ case 'updatedAt':
5
+ return record[field] ? new Date(record[field]).getTime() : 0;
6
+ case 'size':
7
+ // Folders have no size — they compare as 0 (start of ascending, end of descending).
8
+ return record.size ?? 0;
9
+ case 'name':
10
+ default:
11
+ return (record.name ?? '').toLowerCase();
12
+ }
13
+ };
14
+ /**
15
+ * Builds a comparator from comma-separated sort rules (`updatedAt:DESC,name:ASC`).
16
+ * Rules apply in order; the first non-equal field decides. Client-side
17
+ * approximation of the server ordering — good enough to slot folders between
18
+ * files that were sorted server-side by the same rules.
19
+ */ const buildComparator = (sort)=>{
20
+ const rules = sort.split(',').map((rule)=>rule.trim()).filter(Boolean).map((rule)=>{
21
+ const [field, direction] = rule.split(':');
22
+ return {
23
+ field,
24
+ desc: direction?.toUpperCase() === 'DESC'
25
+ };
26
+ });
27
+ return (a, b)=>{
28
+ for (const { field, desc } of rules){
29
+ const va = getComparableValue(a, field);
30
+ const vb = getComparableValue(b, field);
31
+ let cmp;
32
+ if (typeof va === 'string' || typeof vb === 'string') {
33
+ cmp = String(va) < String(vb) ? -1 : String(va) > String(vb) ? 1 : 0;
34
+ } else {
35
+ cmp = va - vb;
36
+ }
37
+ if (cmp !== 0) {
38
+ return desc ? -cmp : cmp;
39
+ }
40
+ }
41
+ return 0;
42
+ };
43
+ };
44
+ /**
45
+ * Interleaves the complete folder list into the (possibly partially loaded)
46
+ * asset stream according to the active sort. Ties order folders first.
47
+ */ const mergeMixedList = ({ folders, assets, sort, hasNextPage })=>{
48
+ const compare = buildComparator(sort);
49
+ const sortedFolders = [
50
+ ...folders
51
+ ].sort(compare);
52
+ const lastAsset = assets[assets.length - 1];
53
+ const placeableFolders = !hasNextPage || !lastAsset ? hasNextPage ? [] // nothing loaded yet — wait for the first page
54
+ : sortedFolders : sortedFolders.filter((folder)=>compare(folder, lastAsset) <= 0);
55
+ const items = [];
56
+ let folderIndex = 0;
57
+ for (const asset of assets){
58
+ while(folderIndex < placeableFolders.length && compare(placeableFolders[folderIndex], asset) <= 0){
59
+ items.push({
60
+ kind: 'folder',
61
+ folder: placeableFolders[folderIndex]
62
+ });
63
+ folderIndex += 1;
64
+ }
65
+ items.push({
66
+ kind: 'asset',
67
+ asset
68
+ });
69
+ }
70
+ while(folderIndex < placeableFolders.length){
71
+ items.push({
72
+ kind: 'folder',
73
+ folder: placeableFolders[folderIndex]
74
+ });
75
+ folderIndex += 1;
76
+ }
77
+ return items;
78
+ };
79
+
80
+ export { buildComparator, mergeMixedList };
81
+ //# sourceMappingURL=mergeMixedList.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mergeMixedList.mjs","sources":["../../../../../../admin/src/future/pages/Assets/utils/mergeMixedList.ts"],"sourcesContent":["import type { File } from '../../../../../../shared/contracts/files';\nimport type { Folder } from '../../../../../../shared/contracts/folders';\n\nexport type MixedItem = { kind: 'folder'; folder: Folder } | { kind: 'asset'; asset: File };\n\ntype SortableRecord = {\n name?: string | null;\n createdAt?: string | null;\n updatedAt?: string | null;\n size?: number | null;\n};\n\nconst getComparableValue = (record: SortableRecord, field: string): number | string => {\n switch (field) {\n case 'createdAt':\n case 'updatedAt':\n return record[field] ? new Date(record[field]!).getTime() : 0;\n case 'size':\n // Folders have no size — they compare as 0 (start of ascending, end of descending).\n return record.size ?? 0;\n case 'name':\n default:\n return (record.name ?? '').toLowerCase();\n }\n};\n\n/**\n * Builds a comparator from comma-separated sort rules (`updatedAt:DESC,name:ASC`).\n * Rules apply in order; the first non-equal field decides. Client-side\n * approximation of the server ordering — good enough to slot folders between\n * files that were sorted server-side by the same rules.\n */\nexport const buildComparator = (sort: string) => {\n const rules = sort\n .split(',')\n .map((rule) => rule.trim())\n .filter(Boolean)\n .map((rule) => {\n const [field, direction] = rule.split(':');\n return { field, desc: direction?.toUpperCase() === 'DESC' };\n });\n\n return (a: SortableRecord, b: SortableRecord): number => {\n for (const { field, desc } of rules) {\n const va = getComparableValue(a, field);\n const vb = getComparableValue(b, field);\n\n let cmp: number;\n if (typeof va === 'string' || typeof vb === 'string') {\n cmp = String(va) < String(vb) ? -1 : String(va) > String(vb) ? 1 : 0;\n } else {\n cmp = va - vb;\n }\n\n if (cmp !== 0) {\n return desc ? -cmp : cmp;\n }\n }\n\n return 0;\n };\n};\n\ninterface MergeMixedListParams {\n folders: Folder[];\n assets: File[];\n /** Combined sort rules used for the assets query. */\n sort: string;\n /**\n * More asset pages exist. A folder may only be placed once we know no\n * not-yet-loaded asset sorts before it — i.e. it compares ≤ the last loaded\n * asset. Later pages release the remaining folders progressively.\n */\n hasNextPage: boolean;\n}\n\n/**\n * Interleaves the complete folder list into the (possibly partially loaded)\n * asset stream according to the active sort. Ties order folders first.\n */\nexport const mergeMixedList = ({\n folders,\n assets,\n sort,\n hasNextPage,\n}: MergeMixedListParams): MixedItem[] => {\n const compare = buildComparator(sort);\n\n const sortedFolders = [...folders].sort(compare);\n\n const lastAsset = assets[assets.length - 1];\n const placeableFolders =\n !hasNextPage || !lastAsset\n ? hasNextPage\n ? [] // nothing loaded yet — wait for the first page\n : sortedFolders\n : sortedFolders.filter((folder) => compare(folder, lastAsset) <= 0);\n\n const items: MixedItem[] = [];\n let folderIndex = 0;\n\n for (const asset of assets) {\n while (\n folderIndex < placeableFolders.length &&\n compare(placeableFolders[folderIndex], asset) <= 0\n ) {\n items.push({ kind: 'folder', folder: placeableFolders[folderIndex] });\n folderIndex += 1;\n }\n items.push({ kind: 'asset', asset });\n }\n\n while (folderIndex < placeableFolders.length) {\n items.push({ kind: 'folder', folder: placeableFolders[folderIndex] });\n folderIndex += 1;\n }\n\n return items;\n};\n"],"names":["getComparableValue","record","field","Date","getTime","size","name","toLowerCase","buildComparator","sort","rules","split","map","rule","trim","filter","Boolean","direction","desc","toUpperCase","a","b","va","vb","cmp","String","mergeMixedList","folders","assets","hasNextPage","compare","sortedFolders","lastAsset","length","placeableFolders","folder","items","folderIndex","asset","push","kind"],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Pure, view-agnostic selection logic for the future Media Library multi-select.
5
+ *
6
+ * Selection covers both assets and folders. Since their numeric ids live in
7
+ * different tables (and can collide), items are tracked as namespaced keys
8
+ * (`asset:1` / `folder:1`). Views build their rendered order as a key array so
9
+ * range / select-all work across the folder and asset sections seamlessly.
10
+ */ const assetKey = (id)=>`asset:${id}`;
11
+ const folderKey = (id)=>`folder:${id}`;
12
+ /** Extracts the numeric ids of one kind from a set of item keys. */ const getIdsOfKind = (keys, kind)=>{
13
+ const ids = new Set();
14
+ keys.forEach((key)=>{
15
+ const [keyKind, id] = key.split(':');
16
+ if (keyKind === kind) {
17
+ ids.add(Number(id));
18
+ }
19
+ });
20
+ return ids;
21
+ };
22
+ const createEmptySelection = ()=>({
23
+ selectedKeys: new Set(),
24
+ anchorKey: null
25
+ });
26
+ /**
27
+ * Additive toggle — adds the key if absent, removes it otherwise, without
28
+ * touching the rest of the selection. Used by the item checkbox and Cmd/Ctrl+click.
29
+ * The toggled key becomes the new anchor.
30
+ */ const toggleSelection = (state, key)=>{
31
+ const selectedKeys = new Set(state.selectedKeys);
32
+ if (selectedKeys.has(key)) {
33
+ selectedKeys.delete(key);
34
+ } else {
35
+ selectedKeys.add(key);
36
+ }
37
+ return {
38
+ selectedKeys,
39
+ anchorKey: key
40
+ };
41
+ };
42
+ /**
43
+ * Shift+click — selects the contiguous range between the current anchor and the
44
+ * target in render order, replacing the current selection. The anchor stays put.
45
+ *
46
+ * Falls back to a single selection when there is no usable anchor (e.g. first
47
+ * click was a Shift+click, or the anchor is no longer in the list).
48
+ */ const selectRange = (state, orderedKeys, targetKey)=>{
49
+ const targetIndex = orderedKeys.indexOf(targetKey);
50
+ if (targetIndex === -1) {
51
+ return state;
52
+ }
53
+ const anchorIndex = state.anchorKey === null ? -1 : orderedKeys.indexOf(state.anchorKey);
54
+ if (anchorIndex === -1) {
55
+ return {
56
+ selectedKeys: new Set([
57
+ targetKey
58
+ ]),
59
+ anchorKey: targetKey
60
+ };
61
+ }
62
+ const start = Math.min(anchorIndex, targetIndex);
63
+ const end = Math.max(anchorIndex, targetIndex);
64
+ return {
65
+ selectedKeys: new Set(orderedKeys.slice(start, end + 1)),
66
+ anchorKey: state.anchorKey
67
+ };
68
+ };
69
+ /**
70
+ * Select-all — selects every key in the provided ordered array. Used by the header
71
+ * checkbox when not everything is already selected.
72
+ */ const selectAll = (orderedKeys)=>({
73
+ selectedKeys: new Set(orderedKeys),
74
+ anchorKey: orderedKeys.length > 0 ? orderedKeys[orderedKeys.length - 1] : null
75
+ });
76
+ const clearSelection = ()=>createEmptySelection();
77
+ /**
78
+ * Derives the header checkbox state: everything selected, some (indeterminate),
79
+ * or none. Only considers the currently-rendered items (orderedKeys).
80
+ */ const getSelectAllState = (selectedKeys, orderedKeys)=>{
81
+ if (orderedKeys.length === 0) {
82
+ return {
83
+ allSelected: false,
84
+ isIndeterminate: false
85
+ };
86
+ }
87
+ const selectedCount = orderedKeys.reduce((count, key)=>selectedKeys.has(key) ? count + 1 : count, 0);
88
+ const allSelected = selectedCount === orderedKeys.length;
89
+ return {
90
+ allSelected,
91
+ isIndeterminate: selectedCount > 0 && !allSelected
92
+ };
93
+ };
94
+
95
+ exports.assetKey = assetKey;
96
+ exports.clearSelection = clearSelection;
97
+ exports.createEmptySelection = createEmptySelection;
98
+ exports.folderKey = folderKey;
99
+ exports.getIdsOfKind = getIdsOfKind;
100
+ exports.getSelectAllState = getSelectAllState;
101
+ exports.selectAll = selectAll;
102
+ exports.selectRange = selectRange;
103
+ exports.toggleSelection = toggleSelection;
104
+ //# sourceMappingURL=selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.js","sources":["../../../../../../admin/src/future/pages/Assets/utils/selection.ts"],"sourcesContent":["/**\n * Pure, view-agnostic selection logic for the future Media Library multi-select.\n *\n * Selection covers both assets and folders. Since their numeric ids live in\n * different tables (and can collide), items are tracked as namespaced keys\n * (`asset:1` / `folder:1`). Views build their rendered order as a key array so\n * range / select-all work across the folder and asset sections seamlessly.\n */\n\nexport type SelectableKind = 'asset' | 'folder';\n\nexport type ItemKey = `${SelectableKind}:${number}`;\n\nexport const assetKey = (id: number): ItemKey => `asset:${id}`;\n\nexport const folderKey = (id: number): ItemKey => `folder:${id}`;\n\n/** Extracts the numeric ids of one kind from a set of item keys. */\nexport const getIdsOfKind = (keys: Set<ItemKey>, kind: SelectableKind): Set<number> => {\n const ids = new Set<number>();\n\n keys.forEach((key) => {\n const [keyKind, id] = key.split(':');\n if (keyKind === kind) {\n ids.add(Number(id));\n }\n });\n\n return ids;\n};\n\nexport interface SelectionState {\n /** Currently selected item keys (assets and folders). */\n selectedKeys: Set<ItemKey>;\n /** Anchor used as the start of a Shift+click range. */\n anchorKey: ItemKey | null;\n}\n\nexport const createEmptySelection = (): SelectionState => ({\n selectedKeys: new Set<ItemKey>(),\n anchorKey: null,\n});\n\n/**\n * Additive toggle — adds the key if absent, removes it otherwise, without\n * touching the rest of the selection. Used by the item checkbox and Cmd/Ctrl+click.\n * The toggled key becomes the new anchor.\n */\nexport const toggleSelection = (state: SelectionState, key: ItemKey): SelectionState => {\n const selectedKeys = new Set(state.selectedKeys);\n\n if (selectedKeys.has(key)) {\n selectedKeys.delete(key);\n } else {\n selectedKeys.add(key);\n }\n\n return { selectedKeys, anchorKey: key };\n};\n\n/**\n * Shift+click — selects the contiguous range between the current anchor and the\n * target in render order, replacing the current selection. The anchor stays put.\n *\n * Falls back to a single selection when there is no usable anchor (e.g. first\n * click was a Shift+click, or the anchor is no longer in the list).\n */\nexport const selectRange = (\n state: SelectionState,\n orderedKeys: ItemKey[],\n targetKey: ItemKey\n): SelectionState => {\n const targetIndex = orderedKeys.indexOf(targetKey);\n\n if (targetIndex === -1) {\n return state;\n }\n\n const anchorIndex = state.anchorKey === null ? -1 : orderedKeys.indexOf(state.anchorKey);\n\n if (anchorIndex === -1) {\n return { selectedKeys: new Set<ItemKey>([targetKey]), anchorKey: targetKey };\n }\n\n const start = Math.min(anchorIndex, targetIndex);\n const end = Math.max(anchorIndex, targetIndex);\n\n return {\n selectedKeys: new Set(orderedKeys.slice(start, end + 1)),\n anchorKey: state.anchorKey,\n };\n};\n\n/**\n * Select-all — selects every key in the provided ordered array. Used by the header\n * checkbox when not everything is already selected.\n */\nexport const selectAll = (orderedKeys: ItemKey[]): SelectionState => ({\n selectedKeys: new Set(orderedKeys),\n anchorKey: orderedKeys.length > 0 ? orderedKeys[orderedKeys.length - 1] : null,\n});\n\nexport const clearSelection = (): SelectionState => createEmptySelection();\n\nexport interface SelectAllState {\n allSelected: boolean;\n isIndeterminate: boolean;\n}\n\n/**\n * Derives the header checkbox state: everything selected, some (indeterminate),\n * or none. Only considers the currently-rendered items (orderedKeys).\n */\nexport const getSelectAllState = (\n selectedKeys: Set<ItemKey>,\n orderedKeys: ItemKey[]\n): SelectAllState => {\n if (orderedKeys.length === 0) {\n return { allSelected: false, isIndeterminate: false };\n }\n\n const selectedCount = orderedKeys.reduce(\n (count, key) => (selectedKeys.has(key) ? count + 1 : count),\n 0\n );\n const allSelected = selectedCount === orderedKeys.length;\n\n return {\n allSelected,\n isIndeterminate: selectedCount > 0 && !allSelected,\n };\n};\n"],"names":["assetKey","id","folderKey","getIdsOfKind","keys","kind","ids","Set","forEach","key","keyKind","split","add","Number","createEmptySelection","selectedKeys","anchorKey","toggleSelection","state","has","delete","selectRange","orderedKeys","targetKey","targetIndex","indexOf","anchorIndex","start","Math","min","end","max","slice","selectAll","length","clearSelection","getSelectAllState","allSelected","isIndeterminate","selectedCount","reduce","count"],"mappings":";;AAAA;;;;;;;UAaaA,QAAAA,GAAW,CAACC,KAAwB,CAAC,MAAM,EAAEA,EAAAA,CAAAA;AAEnD,MAAMC,YAAY,CAACD,EAAAA,GAAwB,CAAC,OAAO,EAAEA;AAE5D,qEACO,MAAME,YAAAA,GAAe,CAACC,IAAAA,EAAoBC,IAAAA,GAAAA;AAC/C,IAAA,MAAMC,MAAM,IAAIC,GAAAA,EAAAA;IAEhBH,IAAAA,CAAKI,OAAO,CAAC,CAACC,GAAAA,GAAAA;AACZ,QAAA,MAAM,CAACC,OAAAA,EAAST,EAAAA,CAAG,GAAGQ,GAAAA,CAAIE,KAAK,CAAC,GAAA,CAAA;AAChC,QAAA,IAAID,YAAYL,IAAAA,EAAM;YACpBC,GAAAA,CAAIM,GAAG,CAACC,MAAAA,CAAOZ,EAAAA,CAAAA,CAAAA;AACjB,QAAA;AACF,IAAA,CAAA,CAAA;IAEA,OAAOK,GAAAA;AACT;AASO,MAAMQ,oBAAAA,GAAuB,KAAuB;AACzDC,QAAAA,YAAAA,EAAc,IAAIR,GAAAA,EAAAA;QAClBS,SAAAA,EAAW;AACb,KAAA;AAEA;;;;AAIC,IACM,MAAMC,eAAAA,GAAkB,CAACC,KAAAA,EAAuBT,GAAAA,GAAAA;AACrD,IAAA,MAAMM,YAAAA,GAAe,IAAIR,GAAAA,CAAIW,KAAAA,CAAMH,YAAY,CAAA;IAE/C,IAAIA,YAAAA,CAAaI,GAAG,CAACV,GAAAA,CAAAA,EAAM;AACzBM,QAAAA,YAAAA,CAAaK,MAAM,CAACX,GAAAA,CAAAA;IACtB,CAAA,MAAO;AACLM,QAAAA,YAAAA,CAAaH,GAAG,CAACH,GAAAA,CAAAA;AACnB,IAAA;IAEA,OAAO;AAAEM,QAAAA,YAAAA;QAAcC,SAAAA,EAAWP;AAAI,KAAA;AACxC;AAEA;;;;;;AAMC,IACM,MAAMY,WAAAA,GAAc,CACzBH,OACAI,WAAAA,EACAC,SAAAA,GAAAA;IAEA,MAAMC,WAAAA,GAAcF,WAAAA,CAAYG,OAAO,CAACF,SAAAA,CAAAA;IAExC,IAAIC,WAAAA,KAAgB,EAAC,EAAG;QACtB,OAAON,KAAAA;AACT,IAAA;IAEA,MAAMQ,WAAAA,GAAcR,KAAAA,CAAMF,SAAS,KAAK,IAAA,GAAO,EAAC,GAAIM,WAAAA,CAAYG,OAAO,CAACP,KAAAA,CAAMF,SAAS,CAAA;IAEvF,IAAIU,WAAAA,KAAgB,EAAC,EAAG;QACtB,OAAO;AAAEX,YAAAA,YAAAA,EAAc,IAAIR,GAAAA,CAAa;AAACgB,gBAAAA;AAAU,aAAA,CAAA;YAAGP,SAAAA,EAAWO;AAAU,SAAA;AAC7E,IAAA;AAEA,IAAA,MAAMI,KAAAA,GAAQC,IAAAA,CAAKC,GAAG,CAACH,WAAAA,EAAaF,WAAAA,CAAAA;AACpC,IAAA,MAAMM,GAAAA,GAAMF,IAAAA,CAAKG,GAAG,CAACL,WAAAA,EAAaF,WAAAA,CAAAA;IAElC,OAAO;AACLT,QAAAA,YAAAA,EAAc,IAAIR,GAAAA,CAAIe,WAAAA,CAAYU,KAAK,CAACL,OAAOG,GAAAA,GAAM,CAAA,CAAA,CAAA;AACrDd,QAAAA,SAAAA,EAAWE,MAAMF;AACnB,KAAA;AACF;AAEA;;;AAGC,IACM,MAAMiB,SAAAA,GAAY,CAACX,eAA4C;AACpEP,QAAAA,YAAAA,EAAc,IAAIR,GAAAA,CAAIe,WAAAA,CAAAA;QACtBN,SAAAA,EAAWM,WAAAA,CAAYY,MAAM,GAAG,CAAA,GAAIZ,WAAW,CAACA,WAAAA,CAAYY,MAAM,GAAG,CAAA,CAAE,GAAG;AAC5E,KAAA;AAEO,MAAMC,cAAAA,GAAiB,IAAsBrB,oBAAAA;AAOpD;;;AAGC,IACM,MAAMsB,iBAAAA,GAAoB,CAC/BrB,YAAAA,EACAO,WAAAA,GAAAA;IAEA,IAAIA,WAAAA,CAAYY,MAAM,KAAK,CAAA,EAAG;QAC5B,OAAO;YAAEG,WAAAA,EAAa,KAAA;YAAOC,eAAAA,EAAiB;AAAM,SAAA;AACtD,IAAA;AAEA,IAAA,MAAMC,aAAAA,GAAgBjB,WAAAA,CAAYkB,MAAM,CACtC,CAACC,KAAAA,EAAOhC,GAAAA,GAASM,YAAAA,CAAaI,GAAG,CAACV,GAAAA,CAAAA,GAAOgC,KAAAA,GAAQ,IAAIA,KAAAA,EACrD,CAAA,CAAA;IAEF,MAAMJ,WAAAA,GAAcE,aAAAA,KAAkBjB,WAAAA,CAAYY,MAAM;IAExD,OAAO;AACLG,QAAAA,WAAAA;QACAC,eAAAA,EAAiBC,aAAAA,GAAgB,KAAK,CAACF;AACzC,KAAA;AACF;;;;;;;;;;;;"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Pure, view-agnostic selection logic for the future Media Library multi-select.
3
+ *
4
+ * Selection covers both assets and folders. Since their numeric ids live in
5
+ * different tables (and can collide), items are tracked as namespaced keys
6
+ * (`asset:1` / `folder:1`). Views build their rendered order as a key array so
7
+ * range / select-all work across the folder and asset sections seamlessly.
8
+ */ const assetKey = (id)=>`asset:${id}`;
9
+ const folderKey = (id)=>`folder:${id}`;
10
+ /** Extracts the numeric ids of one kind from a set of item keys. */ const getIdsOfKind = (keys, kind)=>{
11
+ const ids = new Set();
12
+ keys.forEach((key)=>{
13
+ const [keyKind, id] = key.split(':');
14
+ if (keyKind === kind) {
15
+ ids.add(Number(id));
16
+ }
17
+ });
18
+ return ids;
19
+ };
20
+ const createEmptySelection = ()=>({
21
+ selectedKeys: new Set(),
22
+ anchorKey: null
23
+ });
24
+ /**
25
+ * Additive toggle — adds the key if absent, removes it otherwise, without
26
+ * touching the rest of the selection. Used by the item checkbox and Cmd/Ctrl+click.
27
+ * The toggled key becomes the new anchor.
28
+ */ const toggleSelection = (state, key)=>{
29
+ const selectedKeys = new Set(state.selectedKeys);
30
+ if (selectedKeys.has(key)) {
31
+ selectedKeys.delete(key);
32
+ } else {
33
+ selectedKeys.add(key);
34
+ }
35
+ return {
36
+ selectedKeys,
37
+ anchorKey: key
38
+ };
39
+ };
40
+ /**
41
+ * Shift+click — selects the contiguous range between the current anchor and the
42
+ * target in render order, replacing the current selection. The anchor stays put.
43
+ *
44
+ * Falls back to a single selection when there is no usable anchor (e.g. first
45
+ * click was a Shift+click, or the anchor is no longer in the list).
46
+ */ const selectRange = (state, orderedKeys, targetKey)=>{
47
+ const targetIndex = orderedKeys.indexOf(targetKey);
48
+ if (targetIndex === -1) {
49
+ return state;
50
+ }
51
+ const anchorIndex = state.anchorKey === null ? -1 : orderedKeys.indexOf(state.anchorKey);
52
+ if (anchorIndex === -1) {
53
+ return {
54
+ selectedKeys: new Set([
55
+ targetKey
56
+ ]),
57
+ anchorKey: targetKey
58
+ };
59
+ }
60
+ const start = Math.min(anchorIndex, targetIndex);
61
+ const end = Math.max(anchorIndex, targetIndex);
62
+ return {
63
+ selectedKeys: new Set(orderedKeys.slice(start, end + 1)),
64
+ anchorKey: state.anchorKey
65
+ };
66
+ };
67
+ /**
68
+ * Select-all — selects every key in the provided ordered array. Used by the header
69
+ * checkbox when not everything is already selected.
70
+ */ const selectAll = (orderedKeys)=>({
71
+ selectedKeys: new Set(orderedKeys),
72
+ anchorKey: orderedKeys.length > 0 ? orderedKeys[orderedKeys.length - 1] : null
73
+ });
74
+ const clearSelection = ()=>createEmptySelection();
75
+ /**
76
+ * Derives the header checkbox state: everything selected, some (indeterminate),
77
+ * or none. Only considers the currently-rendered items (orderedKeys).
78
+ */ const getSelectAllState = (selectedKeys, orderedKeys)=>{
79
+ if (orderedKeys.length === 0) {
80
+ return {
81
+ allSelected: false,
82
+ isIndeterminate: false
83
+ };
84
+ }
85
+ const selectedCount = orderedKeys.reduce((count, key)=>selectedKeys.has(key) ? count + 1 : count, 0);
86
+ const allSelected = selectedCount === orderedKeys.length;
87
+ return {
88
+ allSelected,
89
+ isIndeterminate: selectedCount > 0 && !allSelected
90
+ };
91
+ };
92
+
93
+ export { assetKey, clearSelection, createEmptySelection, folderKey, getIdsOfKind, getSelectAllState, selectAll, selectRange, toggleSelection };
94
+ //# sourceMappingURL=selection.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.mjs","sources":["../../../../../../admin/src/future/pages/Assets/utils/selection.ts"],"sourcesContent":["/**\n * Pure, view-agnostic selection logic for the future Media Library multi-select.\n *\n * Selection covers both assets and folders. Since their numeric ids live in\n * different tables (and can collide), items are tracked as namespaced keys\n * (`asset:1` / `folder:1`). Views build their rendered order as a key array so\n * range / select-all work across the folder and asset sections seamlessly.\n */\n\nexport type SelectableKind = 'asset' | 'folder';\n\nexport type ItemKey = `${SelectableKind}:${number}`;\n\nexport const assetKey = (id: number): ItemKey => `asset:${id}`;\n\nexport const folderKey = (id: number): ItemKey => `folder:${id}`;\n\n/** Extracts the numeric ids of one kind from a set of item keys. */\nexport const getIdsOfKind = (keys: Set<ItemKey>, kind: SelectableKind): Set<number> => {\n const ids = new Set<number>();\n\n keys.forEach((key) => {\n const [keyKind, id] = key.split(':');\n if (keyKind === kind) {\n ids.add(Number(id));\n }\n });\n\n return ids;\n};\n\nexport interface SelectionState {\n /** Currently selected item keys (assets and folders). */\n selectedKeys: Set<ItemKey>;\n /** Anchor used as the start of a Shift+click range. */\n anchorKey: ItemKey | null;\n}\n\nexport const createEmptySelection = (): SelectionState => ({\n selectedKeys: new Set<ItemKey>(),\n anchorKey: null,\n});\n\n/**\n * Additive toggle — adds the key if absent, removes it otherwise, without\n * touching the rest of the selection. Used by the item checkbox and Cmd/Ctrl+click.\n * The toggled key becomes the new anchor.\n */\nexport const toggleSelection = (state: SelectionState, key: ItemKey): SelectionState => {\n const selectedKeys = new Set(state.selectedKeys);\n\n if (selectedKeys.has(key)) {\n selectedKeys.delete(key);\n } else {\n selectedKeys.add(key);\n }\n\n return { selectedKeys, anchorKey: key };\n};\n\n/**\n * Shift+click — selects the contiguous range between the current anchor and the\n * target in render order, replacing the current selection. The anchor stays put.\n *\n * Falls back to a single selection when there is no usable anchor (e.g. first\n * click was a Shift+click, or the anchor is no longer in the list).\n */\nexport const selectRange = (\n state: SelectionState,\n orderedKeys: ItemKey[],\n targetKey: ItemKey\n): SelectionState => {\n const targetIndex = orderedKeys.indexOf(targetKey);\n\n if (targetIndex === -1) {\n return state;\n }\n\n const anchorIndex = state.anchorKey === null ? -1 : orderedKeys.indexOf(state.anchorKey);\n\n if (anchorIndex === -1) {\n return { selectedKeys: new Set<ItemKey>([targetKey]), anchorKey: targetKey };\n }\n\n const start = Math.min(anchorIndex, targetIndex);\n const end = Math.max(anchorIndex, targetIndex);\n\n return {\n selectedKeys: new Set(orderedKeys.slice(start, end + 1)),\n anchorKey: state.anchorKey,\n };\n};\n\n/**\n * Select-all — selects every key in the provided ordered array. Used by the header\n * checkbox when not everything is already selected.\n */\nexport const selectAll = (orderedKeys: ItemKey[]): SelectionState => ({\n selectedKeys: new Set(orderedKeys),\n anchorKey: orderedKeys.length > 0 ? orderedKeys[orderedKeys.length - 1] : null,\n});\n\nexport const clearSelection = (): SelectionState => createEmptySelection();\n\nexport interface SelectAllState {\n allSelected: boolean;\n isIndeterminate: boolean;\n}\n\n/**\n * Derives the header checkbox state: everything selected, some (indeterminate),\n * or none. Only considers the currently-rendered items (orderedKeys).\n */\nexport const getSelectAllState = (\n selectedKeys: Set<ItemKey>,\n orderedKeys: ItemKey[]\n): SelectAllState => {\n if (orderedKeys.length === 0) {\n return { allSelected: false, isIndeterminate: false };\n }\n\n const selectedCount = orderedKeys.reduce(\n (count, key) => (selectedKeys.has(key) ? count + 1 : count),\n 0\n );\n const allSelected = selectedCount === orderedKeys.length;\n\n return {\n allSelected,\n isIndeterminate: selectedCount > 0 && !allSelected,\n };\n};\n"],"names":["assetKey","id","folderKey","getIdsOfKind","keys","kind","ids","Set","forEach","key","keyKind","split","add","Number","createEmptySelection","selectedKeys","anchorKey","toggleSelection","state","has","delete","selectRange","orderedKeys","targetKey","targetIndex","indexOf","anchorIndex","start","Math","min","end","max","slice","selectAll","length","clearSelection","getSelectAllState","allSelected","isIndeterminate","selectedCount","reduce","count"],"mappings":"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"}
@@ -7,10 +7,14 @@ var files = require('../utils/files.js');
7
7
  var uploadFileViaXHR = require('./uploadFileViaXHR.js');
8
8
 
9
9
  /**
10
- * Stores the original upload entries for a batch (keyed by uploadId) to enable retry.
10
+ * Stores the original upload batch (keyed by uploadId) to enable retry.
11
11
  */ const uploadRegistry = new Map();
12
- const registerUploadEntries = (uploadId, entries)=>{
13
- uploadRegistry.set(uploadId, entries);
12
+ const registerUploadEntries = (uploadId, entries, generateAiMetadata, concurrency)=>{
13
+ uploadRegistry.set(uploadId, {
14
+ entries,
15
+ generateAiMetadata,
16
+ concurrency
17
+ });
14
18
  };
15
19
  const getUploadEntries = (uploadId)=>{
16
20
  return uploadRegistry.get(uploadId);
@@ -39,6 +43,54 @@ const unregisterAbortController = (uploadId)=>{
39
43
  unregisterAbortController(uploadId);
40
44
  }
41
45
  };
46
+ /** Maps a server per-file outcome onto the row's terminal metadata status. */ const METADATA_STATUS_BY_RESULT = {
47
+ success: 'generated',
48
+ skipped: 'skipped',
49
+ error: 'failed'
50
+ };
51
+ /**
52
+ * Kicks off AI metadata generation for a freshly uploaded file.
53
+ *
54
+ * Deliberately fire-and-forget — the caller must NOT await it:
55
+ * - generation for file N overlaps the upload of file N+1;
56
+ * - a generation failure can never affect the upload result (the upload already
57
+ * succeeded), which is why the promise is fully swallowed here.
58
+ *
59
+ * Every uploaded file is sent, including non-images: the server already classifies
60
+ * them (`ai-metadata.generateForFiles` returns `skipped` for anything whose mime
61
+ * isn't `image/*`) and duplicating that rule here would let the two drift apart.
62
+ * The upshot is that a non-image now reports "Metadata generation skipped" rather
63
+ * than silently showing no metadata state at all.
64
+ */ const maybeGenerateMetadata = ({ file, index, uploadId, enabled, dispatch })=>{
65
+ if (!enabled || typeof file.id !== 'number') {
66
+ return;
67
+ }
68
+ dispatch(uploadProgress.setFileMetadataGenerating({
69
+ index,
70
+ uploadId
71
+ }));
72
+ // `uploadApi` is declared below but only read at call time, long after the module
73
+ // has finished evaluating.
74
+ const promise = dispatch(uploadApi.endpoints.generateAiMetadata.initiate({
75
+ fileIds: [
76
+ file.id
77
+ ]
78
+ }));
79
+ promise.unwrap().then((results)=>{
80
+ const result = results?.find((r)=>r.id === file.id) ?? results?.[0];
81
+ dispatch(uploadProgress.setFileMetadataResult({
82
+ index,
83
+ uploadId,
84
+ status: result ? METADATA_STATUS_BY_RESULT[result.status] : 'failed'
85
+ }));
86
+ }).catch(()=>{
87
+ dispatch(uploadProgress.setFileMetadataResult({
88
+ index,
89
+ uploadId,
90
+ status: 'failed'
91
+ }));
92
+ });
93
+ };
42
94
  /**
43
95
  * Uploads the given entry indices one at a time through the single-file endpoint.
44
96
  *
@@ -46,16 +98,19 @@ const unregisterAbortController = (uploadId)=>{
46
98
  * per-frame batcher to `setFileProgress`, awaits the XHR, then dispatches
47
99
  * "complete" or "error". Each file is wrapped in its own try/catch so one failure
48
100
  * does not stop the batch. An abort stops the loop without starting further files.
49
- */ const runSequentialUpload = async ({ entries, indices, token, uploadId, abortController, dispatch })=>{
101
+ */ const runUploadPool = async ({ entries, indices, token, uploadId, abortController, dispatch, concurrency = 1, generateAiMetadata })=>{
50
102
  const url = `${window.strapi.backendURL}/upload/unstable/upload-file`;
51
103
  const uploaded = [];
52
- for (const index of indices){
53
- if (abortController.signal.aborted) {
54
- break;
55
- }
104
+ // Shared queue: each worker shifts the next index when it frees up, so N
105
+ // files are in flight at any moment (not N-sized waves). With concurrency 1
106
+ // this degenerates to the original strictly sequential loop.
107
+ const queue = [
108
+ ...indices
109
+ ];
110
+ const uploadOne = async (index)=>{
56
111
  const entry = entries[index];
57
112
  if (!entry) {
58
- continue;
113
+ return;
59
114
  }
60
115
  const fileName = entry.fileInfo?.name ?? entry.file.name;
61
116
  dispatch(uploadProgress.setFileUploading({
@@ -81,10 +136,20 @@ const unregisterAbortController = (uploadId)=>{
81
136
  index,
82
137
  file
83
138
  }));
139
+ // Not awaited: overlaps with the next file's upload and can't fail the batch.
140
+ maybeGenerateMetadata({
141
+ file,
142
+ index,
143
+ uploadId,
144
+ enabled: generateAiMetadata,
145
+ dispatch
146
+ });
84
147
  } catch (err) {
85
148
  batcher.cancel();
86
149
  if (err instanceof uploadFileViaXHR.UploadAbortedError) {
87
- break;
150
+ // Batch was cancelled — the worker loop checks the signal and stops.
151
+ // cancelUpload (dispatched from the dialog) marks the remaining rows.
152
+ return;
88
153
  }
89
154
  const message = err instanceof Error ? err.message : 'Upload failed';
90
155
  dispatch(uploadProgress.setFileError({
@@ -93,7 +158,28 @@ const unregisterAbortController = (uploadId)=>{
93
158
  message
94
159
  }));
95
160
  }
96
- }
161
+ };
162
+ // Guard non-finite/invalid values (NaN, Infinity, <1): `Math.floor(NaN)`
163
+ // would propagate through Math.max to `Array.from({ length: NaN })` = zero
164
+ // workers, silently uploading nothing. Anything invalid falls back to 1.
165
+ const requested = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;
166
+ const workerCount = Math.min(Math.max(1, requested), queue.length);
167
+ const worker = async ()=>{
168
+ while(queue.length > 0){
169
+ if (abortController.signal.aborted) {
170
+ break;
171
+ }
172
+ const index = queue.shift();
173
+ if (index === undefined) {
174
+ break;
175
+ }
176
+ // eslint-disable-next-line no-await-in-loop
177
+ await uploadOne(index);
178
+ }
179
+ };
180
+ await Promise.all(Array.from({
181
+ length: workerCount
182
+ }, ()=>worker()));
97
183
  unregisterAbortController(uploadId);
98
184
  return {
99
185
  data: uploaded
@@ -154,7 +240,7 @@ const unregisterAbortController = (uploadId)=>{
154
240
  /**
155
241
  * Processes an SSE stream from the URL upload endpoint.
156
242
  * Dispatches Redux actions for each file event and returns the final result.
157
- */ const processSSEStream = async ({ response, dispatch })=>{
243
+ */ const processSSEStream = async ({ response, dispatch, uploadId, generateAiMetadata })=>{
158
244
  const reader = response.body.getReader();
159
245
  const decoder = new TextDecoder();
160
246
  let streamResult = null;
@@ -206,6 +292,14 @@ const unregisterAbortController = (uploadId)=>{
206
292
  index,
207
293
  file: payload.file
208
294
  }));
295
+ // Same fire-and-forget semantics as the file flow.
296
+ maybeGenerateMetadata({
297
+ file: payload.file,
298
+ index,
299
+ uploadId,
300
+ enabled: generateAiMetadata,
301
+ dispatch
302
+ });
209
303
  break;
210
304
  }
211
305
  case 'file:error':
@@ -242,10 +336,12 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
242
336
  }).injectEndpoints({
243
337
  endpoints: (builder)=>({
244
338
  /**
245
- * Upload files sequentially, one request per file, to `/upload/unstable/upload-file`.
246
- * Real per-file byte progress comes from `XHR.upload.onprogress`.
339
+ * Upload files to `/upload/unstable/upload-file`, one request per file,
340
+ * through a worker pool of `concurrency` parallel requests (default 1 =
341
+ * sequential). Real per-file byte progress comes from
342
+ * `XHR.upload.onprogress`.
247
343
  */ uploadFiles: builder.mutation({
248
- queryFn: async ({ formData, totalFiles }, { dispatch, getState })=>{
344
+ queryFn: async ({ formData, totalFiles, concurrency = 1, generateAiMetadata }, { dispatch, getState })=>{
249
345
  const token = getState().admin_app?.token;
250
346
  // Extract the original files and their per-file fileInfo from the combined FormData.
251
347
  const files = formData.getAll('files');
@@ -271,17 +367,19 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
271
367
  // Get the uploadId from state after dispatching
272
368
  const uploadId = getState().uploadProgress.uploadId;
273
369
  // Store original entries for retry functionality
274
- registerUploadEntries(uploadId, entries);
370
+ registerUploadEntries(uploadId, entries, generateAiMetadata, concurrency);
275
371
  // One AbortController per batch
276
372
  const abortController = new AbortController();
277
373
  registerAbortController(uploadId, abortController);
278
- return runSequentialUpload({
374
+ return runUploadPool({
279
375
  entries,
280
376
  indices: entries.map((_, index)=>index),
281
377
  token,
282
378
  uploadId,
283
379
  abortController,
284
- dispatch
380
+ dispatch,
381
+ concurrency,
382
+ generateAiMetadata
285
383
  });
286
384
  },
287
385
  invalidatesTags: [
@@ -329,9 +427,9 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
329
427
  /**
330
428
  * Retry uploading cancelled files.
331
429
  * Maps cancelled rows back to their original entries and re-runs only those
332
- * through the same sequential loop with a fresh AbortController.
430
+ * through the same upload pool with a fresh AbortController.
333
431
  */ retryCancelledFiles: builder.mutation({
334
- queryFn: async (_, { dispatch, getState })=>{
432
+ queryFn: async (_arg, { dispatch, getState })=>{
335
433
  const { uploadId, files: stateFiles } = getState().uploadProgress;
336
434
  const token = getState().admin_app?.token;
337
435
  const cancelledIndices = stateFiles.filter((f)=>f.status === 'cancelled').map((f)=>f.index);
@@ -343,8 +441,8 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
343
441
  }
344
442
  };
345
443
  }
346
- const entries = getUploadEntries(uploadId);
347
- if (!entries) {
444
+ const batch = getUploadEntries(uploadId);
445
+ if (!batch) {
348
446
  return {
349
447
  error: {
350
448
  name: 'UnknownError',
@@ -357,13 +455,16 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
357
455
  // Fresh AbortController for the retry run
358
456
  const abortController = new AbortController();
359
457
  registerAbortController(uploadId, abortController);
360
- return runSequentialUpload({
361
- entries,
458
+ return runUploadPool({
459
+ entries: batch.entries,
362
460
  indices: cancelledIndices,
363
461
  token,
364
462
  uploadId,
365
463
  abortController,
366
- dispatch
464
+ dispatch,
465
+ // Replay the retried rows with the flags the batch was started with.
466
+ concurrency: batch.concurrency,
467
+ generateAiMetadata: batch.generateAiMetadata
367
468
  });
368
469
  },
369
470
  invalidatesTags: [
@@ -377,7 +478,7 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
377
478
  * Upload files from URLs.
378
479
  * Sends URLs to the server which fetches and uploads them (SSE flow, unchanged).
379
480
  */ uploadFromUrls: builder.mutation({
380
- queryFn: async ({ urls, folderId }, { dispatch, getState })=>{
481
+ queryFn: async ({ urls, folderId, generateAiMetadata }, { dispatch, getState })=>{
381
482
  const token = getState().admin_app?.token;
382
483
  // Extract filenames from URLs for the progress dialog
383
484
  const fileNames = urls.map((url)=>files.getFilenameFromUrl(url));
@@ -426,7 +527,9 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
426
527
  // Process SSE stream from server
427
528
  const streamResult = await processSSEStream({
428
529
  response,
429
- dispatch
530
+ dispatch,
531
+ uploadId,
532
+ generateAiMetadata
430
533
  });
431
534
  unregisterAbortController(uploadId);
432
535
  if (streamResult && streamResult.data.length > 0) {
@@ -468,13 +571,45 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
468
571
  id: 'LIST'
469
572
  }
470
573
  ]
574
+ }),
575
+ /**
576
+ * Generate AI metadata (alt text + caption) for the given assets.
577
+ * Synchronous: resolves once every file has been processed and reports the
578
+ * outcome per file, so non-images and individual failures don't fail the
579
+ * whole batch. Existing alt text and captions are never overwritten.
580
+ *
581
+ * Lives here rather than in `assets.ts` because the upload flows dispatch it
582
+ * directly via `initiate()` after each file completes, and `assets.ts` already
583
+ * imports from this module — defining it there would be a circular import.
584
+ * Re-exported from `assets.ts` for the components that consume the hook.
585
+ */ generateAiMetadata: builder.mutation({
586
+ query: ({ fileIds })=>({
587
+ url: '/upload/unstable/generate-ai-metadata',
588
+ method: 'POST',
589
+ data: {
590
+ fileIds
591
+ }
592
+ }),
593
+ transformResponse: (response)=>response.data,
594
+ invalidatesTags: (_result, _error, { fileIds })=>[
595
+ ...fileIds.map((id)=>({
596
+ type: 'Asset',
597
+ id
598
+ })),
599
+ {
600
+ type: 'Asset',
601
+ id: 'LIST'
602
+ }
603
+ ]
471
604
  })
472
605
  })
473
606
  });
474
- const { useUploadFilesMutation, useUploadFileSilentlyMutation, useRetryCancelledFilesMutation, useUploadFromUrlsMutation } = uploadApi;
607
+ const { useUploadFilesMutation, useUploadFileSilentlyMutation, useRetryCancelledFilesMutation, useUploadFromUrlsMutation, useGenerateAiMetadataMutation } = uploadApi;
475
608
 
476
609
  exports.abortUpload = abortUpload;
610
+ exports.maybeGenerateMetadata = maybeGenerateMetadata;
477
611
  exports.uploadApi = uploadApi;
612
+ exports.useGenerateAiMetadataMutation = useGenerateAiMetadataMutation;
478
613
  exports.useRetryCancelledFilesMutation = useRetryCancelledFilesMutation;
479
614
  exports.useUploadFileSilentlyMutation = useUploadFileSilentlyMutation;
480
615
  exports.useUploadFilesMutation = useUploadFilesMutation;