@strapi/upload 5.55.1 → 5.57.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 (304) hide show
  1. package/dist/admin/hooks/useAIMetadataAvailability.js +14 -0
  2. package/dist/admin/hooks/useAIMetadataAvailability.js.map +1 -0
  3. package/dist/admin/hooks/useAIMetadataAvailability.mjs +12 -0
  4. package/dist/admin/hooks/useAIMetadataAvailability.mjs.map +1 -0
  5. package/dist/admin/hooks/useAIMetadataEnabled.js +7 -5
  6. package/dist/admin/hooks/useAIMetadataEnabled.js.map +1 -1
  7. package/dist/admin/hooks/useAIMetadataEnabled.mjs +7 -5
  8. package/dist/admin/hooks/useAIMetadataEnabled.mjs.map +1 -1
  9. package/dist/admin/hooks/useTracking.js +4 -10
  10. package/dist/admin/hooks/useTracking.js.map +1 -1
  11. package/dist/admin/hooks/useTracking.mjs +5 -10
  12. package/dist/admin/hooks/useTracking.mjs.map +1 -1
  13. package/dist/admin/legacy/hooks/useAIMetadataAvailability.js +14 -0
  14. package/dist/admin/legacy/hooks/useAIMetadataAvailability.js.map +1 -0
  15. package/dist/admin/legacy/hooks/useAIMetadataAvailability.mjs +12 -0
  16. package/dist/admin/legacy/hooks/useAIMetadataAvailability.mjs.map +1 -0
  17. package/dist/admin/legacy/hooks/useAIMetadataEnabled.js +23 -0
  18. package/dist/admin/legacy/hooks/useAIMetadataEnabled.js.map +1 -0
  19. package/dist/admin/legacy/hooks/useAIMetadataEnabled.mjs +21 -0
  20. package/dist/admin/legacy/hooks/useAIMetadataEnabled.mjs.map +1 -0
  21. package/dist/admin/legacy/hooks/useTracking.js +5 -3
  22. package/dist/admin/legacy/hooks/useTracking.js.map +1 -1
  23. package/dist/admin/legacy/hooks/useTracking.mjs +5 -3
  24. package/dist/admin/legacy/hooks/useTracking.mjs.map +1 -1
  25. package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.js +2 -1
  26. package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.js.map +1 -1
  27. package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.mjs +2 -1
  28. package/dist/admin/legacy/pages/App/ConfigureTheView/ConfigureTheView.mjs.map +1 -1
  29. package/dist/admin/legacy/pages/App/MediaLibrary.js +6 -5
  30. package/dist/admin/legacy/pages/App/MediaLibrary.js.map +1 -1
  31. package/dist/admin/legacy/pages/App/MediaLibrary.mjs +6 -5
  32. package/dist/admin/legacy/pages/App/MediaLibrary.mjs.map +1 -1
  33. package/dist/admin/pages/Assets/AssetsPage.js +24 -12
  34. package/dist/admin/pages/Assets/AssetsPage.js.map +1 -1
  35. package/dist/admin/pages/Assets/AssetsPage.mjs +25 -13
  36. package/dist/admin/pages/Assets/AssetsPage.mjs.map +1 -1
  37. package/dist/admin/pages/Assets/components/AssetActions.js +326 -0
  38. package/dist/admin/pages/Assets/components/AssetActions.js.map +1 -0
  39. package/dist/admin/pages/Assets/components/AssetActions.mjs +324 -0
  40. package/dist/admin/pages/Assets/components/AssetActions.mjs.map +1 -0
  41. package/dist/admin/pages/Assets/components/AssetActionsMenu.js +28 -309
  42. package/dist/admin/pages/Assets/components/AssetActionsMenu.js.map +1 -1
  43. package/dist/admin/pages/Assets/components/AssetActionsMenu.mjs +31 -312
  44. package/dist/admin/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
  45. package/dist/admin/pages/Assets/components/AssetContextMenu.js +79 -0
  46. package/dist/admin/pages/Assets/components/AssetContextMenu.js.map +1 -0
  47. package/dist/admin/pages/Assets/components/AssetContextMenu.mjs +77 -0
  48. package/dist/admin/pages/Assets/components/AssetContextMenu.mjs.map +1 -0
  49. package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.js +87 -5
  50. package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
  51. package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +89 -7
  52. package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
  53. package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +3 -3
  54. package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  55. package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +3 -3
  56. package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  57. package/dist/admin/pages/Assets/components/AssetDetails/useCropImg.js +62 -5
  58. package/dist/admin/pages/Assets/components/AssetDetails/useCropImg.js.map +1 -1
  59. package/dist/admin/pages/Assets/components/AssetDetails/useCropImg.mjs +60 -6
  60. package/dist/admin/pages/Assets/components/AssetDetails/useCropImg.mjs.map +1 -1
  61. package/dist/admin/pages/Assets/components/AssetsGrid.js +27 -4
  62. package/dist/admin/pages/Assets/components/AssetsGrid.js.map +1 -1
  63. package/dist/admin/pages/Assets/components/AssetsGrid.mjs +27 -4
  64. package/dist/admin/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  65. package/dist/admin/pages/Assets/components/AssetsTable.js +27 -4
  66. package/dist/admin/pages/Assets/components/AssetsTable.js.map +1 -1
  67. package/dist/admin/pages/Assets/components/AssetsTable.mjs +27 -4
  68. package/dist/admin/pages/Assets/components/AssetsTable.mjs.map +1 -1
  69. package/dist/admin/pages/Assets/components/CursorAnchoredMenu.js +77 -0
  70. package/dist/admin/pages/Assets/components/CursorAnchoredMenu.js.map +1 -0
  71. package/dist/admin/pages/Assets/components/CursorAnchoredMenu.mjs +75 -0
  72. package/dist/admin/pages/Assets/components/CursorAnchoredMenu.mjs.map +1 -0
  73. package/dist/admin/pages/Assets/components/DeleteItemsDialog.js +4 -2
  74. package/dist/admin/pages/Assets/components/DeleteItemsDialog.js.map +1 -1
  75. package/dist/admin/pages/Assets/components/DeleteItemsDialog.mjs +4 -2
  76. package/dist/admin/pages/Assets/components/DeleteItemsDialog.mjs.map +1 -1
  77. package/dist/admin/pages/Assets/components/FolderActions.js +139 -0
  78. package/dist/admin/pages/Assets/components/FolderActions.js.map +1 -0
  79. package/dist/admin/pages/Assets/components/FolderActions.mjs +137 -0
  80. package/dist/admin/pages/Assets/components/FolderActions.mjs.map +1 -0
  81. package/dist/admin/pages/Assets/components/FolderActionsMenu.js +25 -119
  82. package/dist/admin/pages/Assets/components/FolderActionsMenu.js.map +1 -1
  83. package/dist/admin/pages/Assets/components/FolderActionsMenu.mjs +27 -121
  84. package/dist/admin/pages/Assets/components/FolderActionsMenu.mjs.map +1 -1
  85. package/dist/admin/pages/Assets/components/FolderContextMenu.js +67 -0
  86. package/dist/admin/pages/Assets/components/FolderContextMenu.js.map +1 -0
  87. package/dist/admin/pages/Assets/components/FolderContextMenu.mjs +65 -0
  88. package/dist/admin/pages/Assets/components/FolderContextMenu.mjs.map +1 -0
  89. package/dist/admin/pages/Assets/components/FolderTree/FolderTree.js +139 -45
  90. package/dist/admin/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  91. package/dist/admin/pages/Assets/components/FolderTree/FolderTree.mjs +140 -47
  92. package/dist/admin/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  93. package/dist/admin/pages/Assets/components/FolderTree/FolderTreeRowMenu.js +115 -0
  94. package/dist/admin/pages/Assets/components/FolderTree/FolderTreeRowMenu.js.map +1 -0
  95. package/dist/admin/pages/Assets/components/FolderTree/FolderTreeRowMenu.mjs +113 -0
  96. package/dist/admin/pages/Assets/components/FolderTree/FolderTreeRowMenu.mjs.map +1 -0
  97. package/dist/admin/pages/Assets/components/ItemContextMenu.js +125 -0
  98. package/dist/admin/pages/Assets/components/ItemContextMenu.js.map +1 -0
  99. package/dist/admin/pages/Assets/components/ItemContextMenu.mjs +122 -0
  100. package/dist/admin/pages/Assets/components/ItemContextMenu.mjs.map +1 -0
  101. package/dist/admin/pages/Assets/components/MainAreaContextMenu.js +88 -63
  102. package/dist/admin/pages/Assets/components/MainAreaContextMenu.js.map +1 -1
  103. package/dist/admin/pages/Assets/components/MainAreaContextMenu.mjs +88 -63
  104. package/dist/admin/pages/Assets/components/MainAreaContextMenu.mjs.map +1 -1
  105. package/dist/admin/pages/Assets/components/SelectionActions.js +111 -0
  106. package/dist/admin/pages/Assets/components/SelectionActions.js.map +1 -0
  107. package/dist/admin/pages/Assets/components/SelectionActions.mjs +109 -0
  108. package/dist/admin/pages/Assets/components/SelectionActions.mjs.map +1 -0
  109. package/dist/admin/pages/Assets/components/SelectionContextMenu.js +82 -0
  110. package/dist/admin/pages/Assets/components/SelectionContextMenu.js.map +1 -0
  111. package/dist/admin/pages/Assets/components/SelectionContextMenu.mjs +80 -0
  112. package/dist/admin/pages/Assets/components/SelectionContextMenu.mjs.map +1 -0
  113. package/dist/admin/pages/Assets/hooks/useAssetSelection.js +10 -0
  114. package/dist/admin/pages/Assets/hooks/useAssetSelection.js.map +1 -1
  115. package/dist/admin/pages/Assets/hooks/useAssetSelection.mjs +10 -0
  116. package/dist/admin/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
  117. package/dist/admin/pages/SettingsPage/SettingsPage.js +10 -3
  118. package/dist/admin/pages/SettingsPage/SettingsPage.js.map +1 -1
  119. package/dist/admin/pages/SettingsPage/SettingsPage.mjs +10 -3
  120. package/dist/admin/pages/SettingsPage/SettingsPage.mjs.map +1 -1
  121. package/dist/admin/services/api.js.map +1 -1
  122. package/dist/admin/services/api.mjs.map +1 -1
  123. package/dist/admin/src/hooks/useAIMetadataAvailability.d.ts +5 -0
  124. package/dist/admin/src/hooks/useAIMetadataEnabled.d.ts +5 -3
  125. package/dist/admin/src/hooks/useTracking.d.ts +0 -7
  126. package/dist/admin/src/legacy/components/EditAssetDialog/PreviewBox/PreviewComponents.d.ts +2 -2
  127. package/dist/admin/src/legacy/hooks/useAIMetadataAvailability.d.ts +5 -0
  128. package/dist/admin/src/legacy/hooks/useAIMetadataEnabled.d.ts +12 -0
  129. package/dist/admin/src/legacy/hooks/useSettings.d.ts +1 -0
  130. package/dist/admin/src/pages/Assets/components/ActionsMenuContent.d.ts +2 -2
  131. package/dist/admin/src/pages/Assets/components/AssetActions.d.ts +70 -0
  132. package/dist/admin/src/pages/Assets/components/AssetActionsMenu.d.ts +5 -14
  133. package/dist/admin/src/pages/Assets/components/AssetContextMenu.d.ts +27 -0
  134. package/dist/admin/src/pages/Assets/components/AssetDetails/useCropImg.d.ts +16 -0
  135. package/dist/admin/src/pages/Assets/components/CursorAnchoredMenu.d.ts +32 -0
  136. package/dist/admin/src/pages/Assets/components/FolderActions.d.ts +48 -0
  137. package/dist/admin/src/pages/Assets/components/FolderActionsMenu.d.ts +4 -8
  138. package/dist/admin/src/pages/Assets/components/FolderContextMenu.d.ts +21 -0
  139. package/dist/admin/src/pages/Assets/components/FolderTree/FolderTree.d.ts +5 -1
  140. package/dist/admin/src/pages/Assets/components/FolderTree/FolderTreeRowMenu.d.ts +25 -0
  141. package/dist/admin/src/pages/Assets/components/ItemContextMenu.d.ts +53 -0
  142. package/dist/admin/src/pages/Assets/components/MainAreaContextMenu.d.ts +15 -1
  143. package/dist/admin/src/pages/Assets/components/SelectionActions.d.ts +40 -0
  144. package/dist/admin/src/pages/Assets/components/SelectionContextMenu.d.ts +25 -0
  145. package/dist/admin/src/pages/Assets/hooks/useAssetSelection.d.ts +5 -0
  146. package/dist/admin/src/utils/mediaLibraryVersion.d.ts +18 -0
  147. package/dist/admin/translations/en.json.js +9 -3
  148. package/dist/admin/translations/en.json.js.map +1 -1
  149. package/dist/admin/translations/en.json.mjs +9 -3
  150. package/dist/admin/translations/en.json.mjs.map +1 -1
  151. package/dist/admin/utils/mediaLibraryVersion.js +24 -0
  152. package/dist/admin/utils/mediaLibraryVersion.js.map +1 -0
  153. package/dist/admin/utils/mediaLibraryVersion.mjs +20 -0
  154. package/dist/admin/utils/mediaLibraryVersion.mjs.map +1 -0
  155. package/dist/server/bootstrap.js +7 -0
  156. package/dist/server/bootstrap.js.map +1 -1
  157. package/dist/server/bootstrap.mjs +7 -0
  158. package/dist/server/bootstrap.mjs.map +1 -1
  159. package/dist/server/controllers/admin-file.js +2 -2
  160. package/dist/server/controllers/admin-file.js.map +1 -1
  161. package/dist/server/controllers/admin-file.mjs +2 -2
  162. package/dist/server/controllers/admin-file.mjs.map +1 -1
  163. package/dist/server/controllers/admin-folder-file.js +51 -4
  164. package/dist/server/controllers/admin-folder-file.js.map +1 -1
  165. package/dist/server/controllers/admin-folder-file.mjs +52 -5
  166. package/dist/server/controllers/admin-folder-file.mjs.map +1 -1
  167. package/dist/server/controllers/admin-folder.js +5 -5
  168. package/dist/server/controllers/admin-folder.js.map +1 -1
  169. package/dist/server/controllers/admin-folder.mjs +5 -5
  170. package/dist/server/controllers/admin-folder.mjs.map +1 -1
  171. package/dist/server/controllers/admin-settings.js +2 -1
  172. package/dist/server/controllers/admin-settings.js.map +1 -1
  173. package/dist/server/controllers/admin-settings.mjs +2 -1
  174. package/dist/server/controllers/admin-settings.mjs.map +1 -1
  175. package/dist/server/controllers/content-api.js +30 -7
  176. package/dist/server/controllers/content-api.js.map +1 -1
  177. package/dist/server/controllers/content-api.mjs +30 -7
  178. package/dist/server/controllers/content-api.mjs.map +1 -1
  179. package/dist/server/controllers/validation/admin/folder-file.js +4 -4
  180. package/dist/server/controllers/validation/admin/folder-file.js.map +1 -1
  181. package/dist/server/controllers/validation/admin/folder-file.mjs +2 -2
  182. package/dist/server/controllers/validation/admin/folder-file.mjs.map +1 -1
  183. package/dist/server/controllers/validation/admin/folder.js +3 -4
  184. package/dist/server/controllers/validation/admin/folder.js.map +1 -1
  185. package/dist/server/controllers/validation/admin/folder.mjs +3 -4
  186. package/dist/server/controllers/validation/admin/folder.mjs.map +1 -1
  187. package/dist/server/controllers/validation/admin/upload.js +1 -2
  188. package/dist/server/controllers/validation/admin/upload.js.map +1 -1
  189. package/dist/server/controllers/validation/admin/upload.mjs +1 -2
  190. package/dist/server/controllers/validation/admin/upload.mjs.map +1 -1
  191. package/dist/server/controllers/validation/admin/utils.js +1 -2
  192. package/dist/server/controllers/validation/admin/utils.js.map +1 -1
  193. package/dist/server/controllers/validation/admin/utils.mjs +1 -2
  194. package/dist/server/controllers/validation/admin/utils.mjs.map +1 -1
  195. package/dist/server/errors.js +12 -0
  196. package/dist/server/errors.js.map +1 -0
  197. package/dist/server/errors.mjs +10 -0
  198. package/dist/server/errors.mjs.map +1 -0
  199. package/dist/server/migrations/unsign-richtext-and-blocks-urls.js +2 -2
  200. package/dist/server/migrations/unsign-richtext-and-blocks-urls.js.map +1 -1
  201. package/dist/server/migrations/unsign-richtext-and-blocks-urls.mjs +1 -1
  202. package/dist/server/migrations/unsign-richtext-and-blocks-urls.mjs.map +1 -1
  203. package/dist/server/routes/content-api.js +3 -3
  204. package/dist/server/routes/content-api.js.map +1 -1
  205. package/dist/server/routes/content-api.mjs +3 -3
  206. package/dist/server/routes/content-api.mjs.map +1 -1
  207. package/dist/server/routes/validation/upload.js +8 -0
  208. package/dist/server/routes/validation/upload.js.map +1 -1
  209. package/dist/server/routes/validation/upload.mjs +8 -0
  210. package/dist/server/routes/validation/upload.mjs.map +1 -1
  211. package/dist/server/services/ai-metadata-provider.js +83 -0
  212. package/dist/server/services/ai-metadata-provider.js.map +1 -0
  213. package/dist/server/services/ai-metadata-provider.mjs +61 -0
  214. package/dist/server/services/ai-metadata-provider.mjs.map +1 -0
  215. package/dist/server/services/ai-metadata-strapi-managed.js +48 -0
  216. package/dist/server/services/ai-metadata-strapi-managed.js.map +1 -0
  217. package/dist/server/services/ai-metadata-strapi-managed.mjs +46 -0
  218. package/dist/server/services/ai-metadata-strapi-managed.mjs.map +1 -0
  219. package/dist/server/services/ai-metadata.js +5 -58
  220. package/dist/server/services/ai-metadata.js.map +1 -1
  221. package/dist/server/services/ai-metadata.mjs +6 -39
  222. package/dist/server/services/ai-metadata.mjs.map +1 -1
  223. package/dist/server/services/api-upload-folder.js +3 -3
  224. package/dist/server/services/api-upload-folder.js.map +1 -1
  225. package/dist/server/services/api-upload-folder.mjs +3 -3
  226. package/dist/server/services/api-upload-folder.mjs.map +1 -1
  227. package/dist/server/services/extensions/utils.js +19 -15
  228. package/dist/server/services/extensions/utils.js.map +1 -1
  229. package/dist/server/services/extensions/utils.mjs +19 -15
  230. package/dist/server/services/extensions/utils.mjs.map +1 -1
  231. package/dist/server/services/file.js +2 -2
  232. package/dist/server/services/file.js.map +1 -1
  233. package/dist/server/services/file.mjs +1 -1
  234. package/dist/server/services/file.mjs.map +1 -1
  235. package/dist/server/services/folder.js +9 -6
  236. package/dist/server/services/folder.js.map +1 -1
  237. package/dist/server/services/folder.mjs +9 -6
  238. package/dist/server/services/folder.mjs.map +1 -1
  239. package/dist/server/services/index.js +3 -1
  240. package/dist/server/services/index.js.map +1 -1
  241. package/dist/server/services/index.mjs +3 -1
  242. package/dist/server/services/index.mjs.map +1 -1
  243. package/dist/server/services/metrics.js +1 -1
  244. package/dist/server/services/metrics.js.map +1 -1
  245. package/dist/server/services/metrics.mjs +1 -1
  246. package/dist/server/services/metrics.mjs.map +1 -1
  247. package/dist/server/services/provider.js +3 -4
  248. package/dist/server/services/provider.js.map +1 -1
  249. package/dist/server/services/provider.mjs +3 -4
  250. package/dist/server/services/provider.mjs.map +1 -1
  251. package/dist/server/services/upload.js +5 -6
  252. package/dist/server/services/upload.js.map +1 -1
  253. package/dist/server/services/upload.mjs +4 -5
  254. package/dist/server/services/upload.mjs.map +1 -1
  255. package/dist/server/services/weekly-metrics.js +2 -2
  256. package/dist/server/services/weekly-metrics.js.map +1 -1
  257. package/dist/server/services/weekly-metrics.mjs +2 -2
  258. package/dist/server/services/weekly-metrics.mjs.map +1 -1
  259. package/dist/server/src/bootstrap.d.ts.map +1 -1
  260. package/dist/server/src/controllers/admin-folder-file.d.ts.map +1 -1
  261. package/dist/server/src/controllers/admin-folder.d.ts.map +1 -1
  262. package/dist/server/src/controllers/admin-settings.d.ts.map +1 -1
  263. package/dist/server/src/controllers/content-api.d.ts.map +1 -1
  264. package/dist/server/src/controllers/validation/admin/folder.d.ts.map +1 -1
  265. package/dist/server/src/controllers/validation/admin/upload.d.ts.map +1 -1
  266. package/dist/server/src/controllers/validation/admin/utils.d.ts.map +1 -1
  267. package/dist/server/src/errors.d.ts +6 -0
  268. package/dist/server/src/errors.d.ts.map +1 -0
  269. package/dist/server/src/index.d.ts +6 -1
  270. package/dist/server/src/index.d.ts.map +1 -1
  271. package/dist/server/src/mcp/ambient-instance.d.ts +3 -1
  272. package/dist/server/src/mcp/ambient-instance.d.ts.map +1 -1
  273. package/dist/server/src/routes/validation/upload.d.ts +4 -0
  274. package/dist/server/src/routes/validation/upload.d.ts.map +1 -1
  275. package/dist/server/src/services/ai-metadata-provider.d.ts +26 -0
  276. package/dist/server/src/services/ai-metadata-provider.d.ts.map +1 -0
  277. package/dist/server/src/services/ai-metadata-strapi-managed.d.ts +7 -0
  278. package/dist/server/src/services/ai-metadata-strapi-managed.d.ts.map +1 -0
  279. package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
  280. package/dist/server/src/services/extensions/utils.d.ts +33 -1
  281. package/dist/server/src/services/extensions/utils.d.ts.map +1 -1
  282. package/dist/server/src/services/folder.d.ts +5 -2
  283. package/dist/server/src/services/folder.d.ts.map +1 -1
  284. package/dist/server/src/services/index.d.ts +6 -1
  285. package/dist/server/src/services/index.d.ts.map +1 -1
  286. package/dist/server/src/services/provider.d.ts.map +1 -1
  287. package/dist/server/src/services/upload.d.ts.map +1 -1
  288. package/dist/server/src/utils/images.d.ts +2 -2
  289. package/dist/server/src/utils/images.d.ts.map +1 -1
  290. package/dist/server/src/utils/index.d.ts +2 -0
  291. package/dist/server/src/utils/index.d.ts.map +1 -1
  292. package/dist/server/utils/images.js +6 -7
  293. package/dist/server/utils/images.js.map +1 -1
  294. package/dist/server/utils/images.mjs +6 -7
  295. package/dist/server/utils/images.mjs.map +1 -1
  296. package/dist/server/utils/index.js.map +1 -1
  297. package/dist/server/utils/index.mjs.map +1 -1
  298. package/dist/shared/contracts/settings.d.ts +6 -0
  299. package/package.json +11 -11
  300. package/dist/admin/legacy/hooks/useAiAvailability.js +0 -22
  301. package/dist/admin/legacy/hooks/useAiAvailability.js.map +0 -1
  302. package/dist/admin/legacy/hooks/useAiAvailability.mjs +0 -20
  303. package/dist/admin/legacy/hooks/useAiAvailability.mjs.map +0 -1
  304. package/dist/admin/src/legacy/hooks/useAiAvailability.d.ts +0 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssetActions.js","sources":["../../../../../admin/src/pages/Assets/components/AssetActions.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\n\nimport { useClipboard, useNotification } from '@strapi/admin/strapi-admin';\nimport { Button, Dialog, Flex, Menu, Typography, VisuallyHidden } from '@strapi/design-system';\nimport { ArrowRight, ArrowsCounterClockwise, Download, Link, Trash } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\n\nimport { useAIMetadataEnabled } from '../../../hooks/useAIMetadataEnabled';\nimport { useApiErrorMessage } from '../../../hooks/useApiErrorMessage';\nimport { useMediaLibraryPermissions } from '../../../hooks/useMediaLibraryPermissions';\nimport { useReplaceAssetMutation } from '../../../services/assets';\nimport { downloadFile } from '../../../utils/downloadFile';\nimport { prefixFileUrlWithBackendUrl } from '../../../utils/files';\nimport { getTranslationKey } from '../../../utils/translations';\nimport { useAssetSelection } from '../hooks/useAssetSelection';\nimport { useBusyAssetsOptional } from '../hooks/useBusyAssets';\nimport { assetKey } from '../utils/selection';\n\nimport { BulkMoveDialog } from './BulkMoveDialog';\nimport { DeleteItemsDialog } from './DeleteItemsDialog';\n\nimport type { File } from '../../../../../shared/contracts/files';\nimport type { DragFileData } from '../../../types/dnd';\n\ninterface AssetActionsRender {\n /** The `Menu.Item`s, for whichever trigger is rendering them. */\n items: ReactNode;\n /**\n * The replace file input and the three dialogs. Rendered outside `Menu.Root`\n * by every caller: Radix unmounts menu content on close, and a dialog opened\n * from a menu item has to outlive the menu that opened it.\n */\n dialogs: ReactNode;\n /**\n * False once RBAC has settled and every action is gated away. Callers render\n * no trigger at all in that case — an empty popup is worse than none.\n */\n hasActions: boolean;\n /**\n * True while anything the actions own is still in play — a dialog, or the\n * native file picker. A cursor-anchored caller is mounted only as long as it\n * has something on screen, so it needs this to know it must stay around\n * after the menu closes.\n */\n isBusy: boolean;\n}\n\n/**\n * The asset actions themselves — items, dialogs and handlers — with no opinion\n * about how the menu is opened. A render-prop rather than a hook because it\n * produces JSX, and the repo reserves `use*.tsx` for tests.\n *\n * Both entry points (the row's `•••` trigger and the right-click menu anchored\n * at the cursor) render what this returns, so neither can drift from the other:\n * adding an action or changing a permission gate happens here, once.\n *\n * Replace / copy link / download duplicate what the details drawer offers as\n * icon buttons, so the same action is one click away without opening the asset\n * first. They are re-implemented rather than lifted out of the drawer: the\n * drawer versions render as `IconButton`s and report through its in-drawer toast\n * slot, neither of which fits a menu item on a row.\n *\n * A successful move or delete deselects this one asset and leaves the rest of\n * the selection intact: the mutations only invalidate RTK tags, so without it an\n * `asset:<id>` key for a file that has been deleted — or has moved out of this\n * list — would linger.\n */\n/** The subset of the RBAC answer these actions are gated on. */\nexport interface AssetActionPermissions {\n isLoading: boolean;\n canUpdate: boolean;\n canDownload: boolean;\n canCopyLink: boolean;\n}\n\ninterface AssetActionsProps {\n asset: File;\n dragData: DragFileData;\n /**\n * Already-resolved permissions, for a caller that mounts on demand.\n *\n * `useRBAC` starts every instance at `isLoading: true` with each flag false,\n * so a menu mounted by a right-click would render empty for a tick — an open\n * popup with nothing in it. A caller that has been mounted all along (the\n * context menu provider) has the answer already and passes it here. Omitted\n * by the `•••` trigger, which is mounted with its row and has no such gap.\n */\n permissions?: AssetActionPermissions;\n /** Receives the items and dialogs to place around whichever trigger it owns. */\n children: (actions: AssetActionsRender) => ReactNode;\n}\n\nexport const AssetActions = ({ asset, dragData, permissions, children }: AssetActionsProps) => {\n const { formatMessage } = useIntl();\n const getErrorMessage = useApiErrorMessage();\n const { copy } = useClipboard();\n const { toggleNotification } = useNotification();\n const { deselect } = useAssetSelection();\n // Absent in the asset picker and in unit tests: the replace still runs, it\n // just renders no row-level overlay.\n const markBusy = useBusyAssetsOptional()?.markBusy ?? (() => () => {});\n // Always called — hooks can't be conditional — but ignored when the caller\n // supplied an answer it resolved earlier.\n const ownPermissions = useMediaLibraryPermissions();\n const {\n canUpdate,\n canDownload,\n canCopyLink,\n isLoading: isLoadingPermissions,\n } = permissions ?? ownPermissions;\n const [replaceAsset, { isLoading: isReplacing }] = useReplaceAssetMutation();\n const aiEnabled = useAIMetadataEnabled({ mime: asset.mime });\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [isReplaceOpen, setIsReplaceOpen] = useState(false);\n const [isMoveOpen, setIsMoveOpen] = useState(false);\n const [isDeleteOpen, setIsDeleteOpen] = useState(false);\n const [isDownloading, setIsDownloading] = useState(false);\n // The native picker is not a React thing: nothing re-renders while it is up.\n // A cursor-anchored caller is mounted only while it has something on screen,\n // and without this the input it owns would be unmounted the moment the\n // confirm dialog closes — so the file would come back to nothing.\n const [isAwaitingFile, setIsAwaitingFile] = useState(false);\n\n // Stable identity: the move dialog memoizes its destination walk on it.\n const moveItems = useMemo(() => [dragData], [dragData]);\n\n // Confirm first, then open the native picker, so the user only commits to\n // replacing after acknowledging the warning (same order as the drawer).\n // Dismissing the picker fires `cancel`, not `change`, so without this the\n // caller would stay mounted with nothing on screen. Attached natively: the\n // React version here has no `onCancel` on the input types.\n useEffect(() => {\n const input = fileInputRef.current;\n\n if (!input) {\n return undefined;\n }\n\n const handleCancel = () => setIsAwaitingFile(false);\n\n input.addEventListener('cancel', handleCancel);\n\n return () => input.removeEventListener('cancel', handleCancel);\n }, []);\n\n const handleReplaceContinue = () => {\n setIsReplaceOpen(false);\n setIsAwaitingFile(true);\n fileInputRef.current?.click();\n };\n\n const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n // Reset the native input so the same file can be picked again later.\n event.target.value = '';\n setIsAwaitingFile(false);\n\n if (!file) {\n return;\n }\n\n // The menu has closed by the time the picker returns, so `isReplacing` has\n // nothing left to disable and the row would otherwise sit inert for the\n // whole upload. Marking the asset busy puts the same overlay the drawer\n // uses on the row/card, cleared when the mutation settles either way.\n const releaseBusy = markBusy(\n asset.id,\n formatMessage({\n id: getTranslationKey('asset-details.replace.loading'),\n defaultMessage: 'Replacing the file…',\n })\n );\n\n let res;\n try {\n res = await replaceAsset({ id: asset.id, file, fileInfo: { name: asset.name } });\n } finally {\n releaseBusy();\n }\n\n if ('error' in res) {\n toggleNotification({\n type: 'danger',\n message: getErrorMessage(\n res.error,\n formatMessage({\n id: getTranslationKey('asset-details.replace.error'),\n defaultMessage: 'Failed to replace the file.',\n })\n ),\n });\n return;\n }\n\n toggleNotification({\n type: 'success',\n message: formatMessage({\n id: getTranslationKey('asset-details.replace.success'),\n defaultMessage: 'File replaced.',\n }),\n });\n };\n\n const handleCopyLink = async () => {\n const url = prefixFileUrlWithBackendUrl(asset.url);\n\n if (!url) {\n return;\n }\n\n const didCopy = await copy(url);\n\n toggleNotification({\n type: didCopy ? 'success' : 'danger',\n message: formatMessage(\n didCopy\n ? {\n id: getTranslationKey('asset-details.copy-link.success'),\n defaultMessage: 'Link copied.',\n }\n : {\n id: getTranslationKey('asset-details.copy-link.error'),\n defaultMessage: 'Failed to copy the link.',\n }\n ),\n });\n };\n\n const handleDownload = async () => {\n const url = prefixFileUrlWithBackendUrl(asset.url);\n\n if (!url) {\n return;\n }\n\n setIsDownloading(true);\n\n try {\n await downloadFile(url, asset.name);\n } catch {\n toggleNotification({\n type: 'danger',\n message: formatMessage({\n id: getTranslationKey('asset-details.download.error'),\n defaultMessage: 'Failed to download the file.',\n }),\n });\n } finally {\n setIsDownloading(false);\n }\n };\n\n // Two permission-gated groups: replace/copy-link/download above, move/delete\n // below. The separator earns its place only when both survive the gate —\n // `canUpdate` alone puts Replace in the top group, which is not enough.\n const hasTopGroup = canUpdate || canCopyLink || canDownload;\n const hasBottomGroup = canUpdate;\n const showSeparator = (canCopyLink || canDownload) && hasBottomGroup;\n\n const items = (\n <>\n {canUpdate && (\n <Menu.Item\n startIcon={<ArrowsCounterClockwise />}\n disabled={isReplacing}\n onSelect={() => setIsReplaceOpen(true)}\n >\n {formatMessage({\n id: getTranslationKey('list.assets.actions.replace'),\n defaultMessage: 'Replace media',\n })}\n </Menu.Item>\n )}\n {canCopyLink && (\n <Menu.Item startIcon={<Link />} onSelect={handleCopyLink}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.copy-link'),\n defaultMessage: 'Copy link to media',\n })}\n </Menu.Item>\n )}\n {canDownload && (\n <Menu.Item startIcon={<Download />} disabled={isDownloading} onSelect={handleDownload}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.download'),\n defaultMessage: 'Download media',\n })}\n </Menu.Item>\n )}\n {showSeparator && <Menu.Separator />}\n {canUpdate && (\n <>\n <Menu.Item startIcon={<ArrowRight />} onSelect={() => setIsMoveOpen(true)}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.move'),\n defaultMessage: 'Move to folder',\n })}\n </Menu.Item>\n <Menu.Item startIcon={<Trash />} variant=\"danger\" onSelect={() => setIsDeleteOpen(true)}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.delete'),\n defaultMessage: 'Delete',\n })}\n </Menu.Item>\n </>\n )}\n </>\n );\n\n const dialogs = (\n <>\n <VisuallyHidden>\n <input\n ref={fileInputRef}\n type=\"file\"\n // Replacing swaps the bytes of an existing asset, so the picker offers\n // only its own type. Without this the picker accepted anything and a\n // JPG could come back as PNG bytes still served under a `.jpg` url,\n // since replace preserves hash and ext by design.\n accept={asset.mime ?? ''}\n multiple={false}\n onChange={handleFileChange}\n aria-hidden\n tabIndex={-1}\n />\n </VisuallyHidden>\n <Dialog.Root open={isReplaceOpen} onOpenChange={setIsReplaceOpen}>\n <Dialog.Content>\n <Dialog.Header>\n {formatMessage({\n id: getTranslationKey('asset-details.replace.title'),\n defaultMessage: 'Replace this media file?',\n })}\n </Dialog.Header>\n <Dialog.Body textAlign=\"center\">\n <Flex direction=\"column\" textAlign=\"center\">\n <Typography variant=\"omega\">\n {formatMessage({\n id: getTranslationKey('asset-details.replace.description'),\n defaultMessage: 'Current content will be permanently replaced.',\n })}\n </Typography>\n {aiEnabled ? (\n <Typography variant=\"omega\">\n {formatMessage({\n id: getTranslationKey('asset-details.replace.description.ai'),\n defaultMessage: 'AI will generate new metadata after upload.',\n })}\n </Typography>\n ) : null}\n </Flex>\n </Dialog.Body>\n <Dialog.Footer>\n <Dialog.Cancel>\n <Button variant=\"tertiary\" fullWidth>\n {formatMessage({ id: 'app.components.Button.cancel', defaultMessage: 'Cancel' })}\n </Button>\n </Dialog.Cancel>\n <Dialog.Action>\n <Button variant=\"secondary\" onClick={handleReplaceContinue} fullWidth>\n {formatMessage({\n id: getTranslationKey('asset-details.replace.continue'),\n defaultMessage: 'Continue',\n })}\n </Button>\n </Dialog.Action>\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog.Root>\n {isMoveOpen && (\n <BulkMoveDialog\n open\n onClose={() => setIsMoveOpen(false)}\n items={moveItems}\n onSuccess={() => deselect(assetKey(asset.id))}\n />\n )}\n {/* Both dialogs live inside the row, so a background refetch that drops the\n row would take an open dialog with it. Nothing invalidates until the\n mutation resolves, and the dialog closes in the same tick, so the\n flows themselves can't trigger it. */}\n {isDeleteOpen && (\n <DeleteItemsDialog\n open\n onClose={() => setIsDeleteOpen(false)}\n target={{ fileIds: [asset.id], folderIds: [] }}\n onSuccess={() => deselect(assetKey(asset.id))}\n />\n )}\n </>\n );\n\n return (\n <>\n {children({\n items,\n dialogs,\n // Every flag is `false` until the RBAC check settles, so wait for it —\n // otherwise the trigger unmounts and remounts on first paint for everyone.\n hasActions: isLoadingPermissions || hasTopGroup || hasBottomGroup,\n isBusy: isReplaceOpen || isMoveOpen || isDeleteOpen || isAwaitingFile,\n })}\n </>\n );\n};\n"],"names":["AssetActions","asset","dragData","permissions","children","formatMessage","useIntl","getErrorMessage","useApiErrorMessage","copy","useClipboard","toggleNotification","useNotification","deselect","useAssetSelection","markBusy","useBusyAssetsOptional","ownPermissions","useMediaLibraryPermissions","canUpdate","canDownload","canCopyLink","isLoading","isLoadingPermissions","replaceAsset","isReplacing","useReplaceAssetMutation","aiEnabled","useAIMetadataEnabled","mime","fileInputRef","useRef","isReplaceOpen","setIsReplaceOpen","useState","isMoveOpen","setIsMoveOpen","isDeleteOpen","setIsDeleteOpen","isDownloading","setIsDownloading","isAwaitingFile","setIsAwaitingFile","moveItems","useMemo","useEffect","input","current","undefined","handleCancel","addEventListener","removeEventListener","handleReplaceContinue","click","handleFileChange","event","file","target","files","value","releaseBusy","id","getTranslationKey","defaultMessage","res","fileInfo","name","type","message","error","handleCopyLink","url","prefixFileUrlWithBackendUrl","didCopy","handleDownload","downloadFile","hasTopGroup","hasBottomGroup","showSeparator","items","_jsxs","_Fragment","_jsx","Menu","Item","startIcon","ArrowsCounterClockwise","disabled","onSelect","Link","Download","Separator","ArrowRight","Trash","variant","dialogs","VisuallyHidden","ref","accept","multiple","onChange","aria-hidden","tabIndex","Dialog","Root","open","onOpenChange","Content","Header","Body","textAlign","Flex","direction","Typography","Footer","Cancel","Button","fullWidth","Action","onClick","BulkMoveDialog","onClose","onSuccess","assetKey","DeleteItemsDialog","fileIds","folderIds","hasActions","isBusy"],"mappings":"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"}
@@ -0,0 +1,324 @@
1
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
+ import { useRef, useState, useMemo, useEffect } from 'react';
3
+ import { useClipboard, useNotification } from '@strapi/admin/strapi-admin';
4
+ import { Menu, VisuallyHidden, Dialog, Flex, Typography, Button } from '@strapi/design-system';
5
+ import { ArrowsCounterClockwise, Link, Download, ArrowRight, Trash } from '@strapi/icons';
6
+ import { useIntl } from 'react-intl';
7
+ import { useAIMetadataEnabled } from '../../../hooks/useAIMetadataEnabled.mjs';
8
+ import { useApiErrorMessage } from '../../../hooks/useApiErrorMessage.mjs';
9
+ import { useMediaLibraryPermissions } from '../../../hooks/useMediaLibraryPermissions.mjs';
10
+ import { useReplaceAssetMutation } from '../../../services/assets.mjs';
11
+ import { downloadFile } from '../../../utils/downloadFile.mjs';
12
+ import { prefixFileUrlWithBackendUrl } from '../../../utils/files.mjs';
13
+ import { getTranslationKey } from '../../../utils/translations.mjs';
14
+ import { useAssetSelection } from '../hooks/useAssetSelection.mjs';
15
+ import { useBusyAssetsOptional } from '../hooks/useBusyAssets.mjs';
16
+ import { assetKey } from '../utils/selection.mjs';
17
+ import { BulkMoveDialog } from './BulkMoveDialog.mjs';
18
+ import { DeleteItemsDialog } from './DeleteItemsDialog.mjs';
19
+
20
+ const AssetActions = ({ asset, dragData, permissions, children })=>{
21
+ const { formatMessage } = useIntl();
22
+ const getErrorMessage = useApiErrorMessage();
23
+ const { copy } = useClipboard();
24
+ const { toggleNotification } = useNotification();
25
+ const { deselect } = useAssetSelection();
26
+ // Absent in the asset picker and in unit tests: the replace still runs, it
27
+ // just renders no row-level overlay.
28
+ const markBusy = useBusyAssetsOptional()?.markBusy ?? (()=>()=>{});
29
+ // Always called — hooks can't be conditional — but ignored when the caller
30
+ // supplied an answer it resolved earlier.
31
+ const ownPermissions = useMediaLibraryPermissions();
32
+ const { canUpdate, canDownload, canCopyLink, isLoading: isLoadingPermissions } = permissions ?? ownPermissions;
33
+ const [replaceAsset, { isLoading: isReplacing }] = useReplaceAssetMutation();
34
+ const aiEnabled = useAIMetadataEnabled({
35
+ mime: asset.mime
36
+ });
37
+ const fileInputRef = useRef(null);
38
+ const [isReplaceOpen, setIsReplaceOpen] = useState(false);
39
+ const [isMoveOpen, setIsMoveOpen] = useState(false);
40
+ const [isDeleteOpen, setIsDeleteOpen] = useState(false);
41
+ const [isDownloading, setIsDownloading] = useState(false);
42
+ // The native picker is not a React thing: nothing re-renders while it is up.
43
+ // A cursor-anchored caller is mounted only while it has something on screen,
44
+ // and without this the input it owns would be unmounted the moment the
45
+ // confirm dialog closes — so the file would come back to nothing.
46
+ const [isAwaitingFile, setIsAwaitingFile] = useState(false);
47
+ // Stable identity: the move dialog memoizes its destination walk on it.
48
+ const moveItems = useMemo(()=>[
49
+ dragData
50
+ ], [
51
+ dragData
52
+ ]);
53
+ // Confirm first, then open the native picker, so the user only commits to
54
+ // replacing after acknowledging the warning (same order as the drawer).
55
+ // Dismissing the picker fires `cancel`, not `change`, so without this the
56
+ // caller would stay mounted with nothing on screen. Attached natively: the
57
+ // React version here has no `onCancel` on the input types.
58
+ useEffect(()=>{
59
+ const input = fileInputRef.current;
60
+ if (!input) {
61
+ return undefined;
62
+ }
63
+ const handleCancel = ()=>setIsAwaitingFile(false);
64
+ input.addEventListener('cancel', handleCancel);
65
+ return ()=>input.removeEventListener('cancel', handleCancel);
66
+ }, []);
67
+ const handleReplaceContinue = ()=>{
68
+ setIsReplaceOpen(false);
69
+ setIsAwaitingFile(true);
70
+ fileInputRef.current?.click();
71
+ };
72
+ const handleFileChange = async (event)=>{
73
+ const file = event.target.files?.[0];
74
+ // Reset the native input so the same file can be picked again later.
75
+ event.target.value = '';
76
+ setIsAwaitingFile(false);
77
+ if (!file) {
78
+ return;
79
+ }
80
+ // The menu has closed by the time the picker returns, so `isReplacing` has
81
+ // nothing left to disable and the row would otherwise sit inert for the
82
+ // whole upload. Marking the asset busy puts the same overlay the drawer
83
+ // uses on the row/card, cleared when the mutation settles either way.
84
+ const releaseBusy = markBusy(asset.id, formatMessage({
85
+ id: getTranslationKey('asset-details.replace.loading'),
86
+ defaultMessage: 'Replacing the file…'
87
+ }));
88
+ let res;
89
+ try {
90
+ res = await replaceAsset({
91
+ id: asset.id,
92
+ file,
93
+ fileInfo: {
94
+ name: asset.name
95
+ }
96
+ });
97
+ } finally{
98
+ releaseBusy();
99
+ }
100
+ if ('error' in res) {
101
+ toggleNotification({
102
+ type: 'danger',
103
+ message: getErrorMessage(res.error, formatMessage({
104
+ id: getTranslationKey('asset-details.replace.error'),
105
+ defaultMessage: 'Failed to replace the file.'
106
+ }))
107
+ });
108
+ return;
109
+ }
110
+ toggleNotification({
111
+ type: 'success',
112
+ message: formatMessage({
113
+ id: getTranslationKey('asset-details.replace.success'),
114
+ defaultMessage: 'File replaced.'
115
+ })
116
+ });
117
+ };
118
+ const handleCopyLink = async ()=>{
119
+ const url = prefixFileUrlWithBackendUrl(asset.url);
120
+ if (!url) {
121
+ return;
122
+ }
123
+ const didCopy = await copy(url);
124
+ toggleNotification({
125
+ type: didCopy ? 'success' : 'danger',
126
+ message: formatMessage(didCopy ? {
127
+ id: getTranslationKey('asset-details.copy-link.success'),
128
+ defaultMessage: 'Link copied.'
129
+ } : {
130
+ id: getTranslationKey('asset-details.copy-link.error'),
131
+ defaultMessage: 'Failed to copy the link.'
132
+ })
133
+ });
134
+ };
135
+ const handleDownload = async ()=>{
136
+ const url = prefixFileUrlWithBackendUrl(asset.url);
137
+ if (!url) {
138
+ return;
139
+ }
140
+ setIsDownloading(true);
141
+ try {
142
+ await downloadFile(url, asset.name);
143
+ } catch {
144
+ toggleNotification({
145
+ type: 'danger',
146
+ message: formatMessage({
147
+ id: getTranslationKey('asset-details.download.error'),
148
+ defaultMessage: 'Failed to download the file.'
149
+ })
150
+ });
151
+ } finally{
152
+ setIsDownloading(false);
153
+ }
154
+ };
155
+ // Two permission-gated groups: replace/copy-link/download above, move/delete
156
+ // below. The separator earns its place only when both survive the gate —
157
+ // `canUpdate` alone puts Replace in the top group, which is not enough.
158
+ const hasTopGroup = canUpdate || canCopyLink || canDownload;
159
+ const hasBottomGroup = canUpdate;
160
+ const showSeparator = (canCopyLink || canDownload) && hasBottomGroup;
161
+ const items = /*#__PURE__*/ jsxs(Fragment, {
162
+ children: [
163
+ canUpdate && /*#__PURE__*/ jsx(Menu.Item, {
164
+ startIcon: /*#__PURE__*/ jsx(ArrowsCounterClockwise, {}),
165
+ disabled: isReplacing,
166
+ onSelect: ()=>setIsReplaceOpen(true),
167
+ children: formatMessage({
168
+ id: getTranslationKey('list.assets.actions.replace'),
169
+ defaultMessage: 'Replace media'
170
+ })
171
+ }),
172
+ canCopyLink && /*#__PURE__*/ jsx(Menu.Item, {
173
+ startIcon: /*#__PURE__*/ jsx(Link, {}),
174
+ onSelect: handleCopyLink,
175
+ children: formatMessage({
176
+ id: getTranslationKey('list.assets.actions.copy-link'),
177
+ defaultMessage: 'Copy link to media'
178
+ })
179
+ }),
180
+ canDownload && /*#__PURE__*/ jsx(Menu.Item, {
181
+ startIcon: /*#__PURE__*/ jsx(Download, {}),
182
+ disabled: isDownloading,
183
+ onSelect: handleDownload,
184
+ children: formatMessage({
185
+ id: getTranslationKey('list.assets.actions.download'),
186
+ defaultMessage: 'Download media'
187
+ })
188
+ }),
189
+ showSeparator && /*#__PURE__*/ jsx(Menu.Separator, {}),
190
+ canUpdate && /*#__PURE__*/ jsxs(Fragment, {
191
+ children: [
192
+ /*#__PURE__*/ jsx(Menu.Item, {
193
+ startIcon: /*#__PURE__*/ jsx(ArrowRight, {}),
194
+ onSelect: ()=>setIsMoveOpen(true),
195
+ children: formatMessage({
196
+ id: getTranslationKey('list.assets.actions.move'),
197
+ defaultMessage: 'Move to folder'
198
+ })
199
+ }),
200
+ /*#__PURE__*/ jsx(Menu.Item, {
201
+ startIcon: /*#__PURE__*/ jsx(Trash, {}),
202
+ variant: "danger",
203
+ onSelect: ()=>setIsDeleteOpen(true),
204
+ children: formatMessage({
205
+ id: getTranslationKey('list.assets.actions.delete'),
206
+ defaultMessage: 'Delete'
207
+ })
208
+ })
209
+ ]
210
+ })
211
+ ]
212
+ });
213
+ const dialogs = /*#__PURE__*/ jsxs(Fragment, {
214
+ children: [
215
+ /*#__PURE__*/ jsx(VisuallyHidden, {
216
+ children: /*#__PURE__*/ jsx("input", {
217
+ ref: fileInputRef,
218
+ type: "file",
219
+ // Replacing swaps the bytes of an existing asset, so the picker offers
220
+ // only its own type. Without this the picker accepted anything and a
221
+ // JPG could come back as PNG bytes still served under a `.jpg` url,
222
+ // since replace preserves hash and ext by design.
223
+ accept: asset.mime ?? '',
224
+ multiple: false,
225
+ onChange: handleFileChange,
226
+ "aria-hidden": true,
227
+ tabIndex: -1
228
+ })
229
+ }),
230
+ /*#__PURE__*/ jsx(Dialog.Root, {
231
+ open: isReplaceOpen,
232
+ onOpenChange: setIsReplaceOpen,
233
+ children: /*#__PURE__*/ jsxs(Dialog.Content, {
234
+ children: [
235
+ /*#__PURE__*/ jsx(Dialog.Header, {
236
+ children: formatMessage({
237
+ id: getTranslationKey('asset-details.replace.title'),
238
+ defaultMessage: 'Replace this media file?'
239
+ })
240
+ }),
241
+ /*#__PURE__*/ jsx(Dialog.Body, {
242
+ textAlign: "center",
243
+ children: /*#__PURE__*/ jsxs(Flex, {
244
+ direction: "column",
245
+ textAlign: "center",
246
+ children: [
247
+ /*#__PURE__*/ jsx(Typography, {
248
+ variant: "omega",
249
+ children: formatMessage({
250
+ id: getTranslationKey('asset-details.replace.description'),
251
+ defaultMessage: 'Current content will be permanently replaced.'
252
+ })
253
+ }),
254
+ aiEnabled ? /*#__PURE__*/ jsx(Typography, {
255
+ variant: "omega",
256
+ children: formatMessage({
257
+ id: getTranslationKey('asset-details.replace.description.ai'),
258
+ defaultMessage: 'AI will generate new metadata after upload.'
259
+ })
260
+ }) : null
261
+ ]
262
+ })
263
+ }),
264
+ /*#__PURE__*/ jsxs(Dialog.Footer, {
265
+ children: [
266
+ /*#__PURE__*/ jsx(Dialog.Cancel, {
267
+ children: /*#__PURE__*/ jsx(Button, {
268
+ variant: "tertiary",
269
+ fullWidth: true,
270
+ children: formatMessage({
271
+ id: 'app.components.Button.cancel',
272
+ defaultMessage: 'Cancel'
273
+ })
274
+ })
275
+ }),
276
+ /*#__PURE__*/ jsx(Dialog.Action, {
277
+ children: /*#__PURE__*/ jsx(Button, {
278
+ variant: "secondary",
279
+ onClick: handleReplaceContinue,
280
+ fullWidth: true,
281
+ children: formatMessage({
282
+ id: getTranslationKey('asset-details.replace.continue'),
283
+ defaultMessage: 'Continue'
284
+ })
285
+ })
286
+ })
287
+ ]
288
+ })
289
+ ]
290
+ })
291
+ }),
292
+ isMoveOpen && /*#__PURE__*/ jsx(BulkMoveDialog, {
293
+ open: true,
294
+ onClose: ()=>setIsMoveOpen(false),
295
+ items: moveItems,
296
+ onSuccess: ()=>deselect(assetKey(asset.id))
297
+ }),
298
+ isDeleteOpen && /*#__PURE__*/ jsx(DeleteItemsDialog, {
299
+ open: true,
300
+ onClose: ()=>setIsDeleteOpen(false),
301
+ target: {
302
+ fileIds: [
303
+ asset.id
304
+ ],
305
+ folderIds: []
306
+ },
307
+ onSuccess: ()=>deselect(assetKey(asset.id))
308
+ })
309
+ ]
310
+ });
311
+ return /*#__PURE__*/ jsx(Fragment, {
312
+ children: children({
313
+ items,
314
+ dialogs,
315
+ // Every flag is `false` until the RBAC check settles, so wait for it —
316
+ // otherwise the trigger unmounts and remounts on first paint for everyone.
317
+ hasActions: isLoadingPermissions || hasTopGroup || hasBottomGroup,
318
+ isBusy: isReplaceOpen || isMoveOpen || isDeleteOpen || isAwaitingFile
319
+ })
320
+ });
321
+ };
322
+
323
+ export { AssetActions };
324
+ //# sourceMappingURL=AssetActions.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssetActions.mjs","sources":["../../../../../admin/src/pages/Assets/components/AssetActions.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\n\nimport { useClipboard, useNotification } from '@strapi/admin/strapi-admin';\nimport { Button, Dialog, Flex, Menu, Typography, VisuallyHidden } from '@strapi/design-system';\nimport { ArrowRight, ArrowsCounterClockwise, Download, Link, Trash } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\n\nimport { useAIMetadataEnabled } from '../../../hooks/useAIMetadataEnabled';\nimport { useApiErrorMessage } from '../../../hooks/useApiErrorMessage';\nimport { useMediaLibraryPermissions } from '../../../hooks/useMediaLibraryPermissions';\nimport { useReplaceAssetMutation } from '../../../services/assets';\nimport { downloadFile } from '../../../utils/downloadFile';\nimport { prefixFileUrlWithBackendUrl } from '../../../utils/files';\nimport { getTranslationKey } from '../../../utils/translations';\nimport { useAssetSelection } from '../hooks/useAssetSelection';\nimport { useBusyAssetsOptional } from '../hooks/useBusyAssets';\nimport { assetKey } from '../utils/selection';\n\nimport { BulkMoveDialog } from './BulkMoveDialog';\nimport { DeleteItemsDialog } from './DeleteItemsDialog';\n\nimport type { File } from '../../../../../shared/contracts/files';\nimport type { DragFileData } from '../../../types/dnd';\n\ninterface AssetActionsRender {\n /** The `Menu.Item`s, for whichever trigger is rendering them. */\n items: ReactNode;\n /**\n * The replace file input and the three dialogs. Rendered outside `Menu.Root`\n * by every caller: Radix unmounts menu content on close, and a dialog opened\n * from a menu item has to outlive the menu that opened it.\n */\n dialogs: ReactNode;\n /**\n * False once RBAC has settled and every action is gated away. Callers render\n * no trigger at all in that case — an empty popup is worse than none.\n */\n hasActions: boolean;\n /**\n * True while anything the actions own is still in play — a dialog, or the\n * native file picker. A cursor-anchored caller is mounted only as long as it\n * has something on screen, so it needs this to know it must stay around\n * after the menu closes.\n */\n isBusy: boolean;\n}\n\n/**\n * The asset actions themselves — items, dialogs and handlers — with no opinion\n * about how the menu is opened. A render-prop rather than a hook because it\n * produces JSX, and the repo reserves `use*.tsx` for tests.\n *\n * Both entry points (the row's `•••` trigger and the right-click menu anchored\n * at the cursor) render what this returns, so neither can drift from the other:\n * adding an action or changing a permission gate happens here, once.\n *\n * Replace / copy link / download duplicate what the details drawer offers as\n * icon buttons, so the same action is one click away without opening the asset\n * first. They are re-implemented rather than lifted out of the drawer: the\n * drawer versions render as `IconButton`s and report through its in-drawer toast\n * slot, neither of which fits a menu item on a row.\n *\n * A successful move or delete deselects this one asset and leaves the rest of\n * the selection intact: the mutations only invalidate RTK tags, so without it an\n * `asset:<id>` key for a file that has been deleted — or has moved out of this\n * list — would linger.\n */\n/** The subset of the RBAC answer these actions are gated on. */\nexport interface AssetActionPermissions {\n isLoading: boolean;\n canUpdate: boolean;\n canDownload: boolean;\n canCopyLink: boolean;\n}\n\ninterface AssetActionsProps {\n asset: File;\n dragData: DragFileData;\n /**\n * Already-resolved permissions, for a caller that mounts on demand.\n *\n * `useRBAC` starts every instance at `isLoading: true` with each flag false,\n * so a menu mounted by a right-click would render empty for a tick — an open\n * popup with nothing in it. A caller that has been mounted all along (the\n * context menu provider) has the answer already and passes it here. Omitted\n * by the `•••` trigger, which is mounted with its row and has no such gap.\n */\n permissions?: AssetActionPermissions;\n /** Receives the items and dialogs to place around whichever trigger it owns. */\n children: (actions: AssetActionsRender) => ReactNode;\n}\n\nexport const AssetActions = ({ asset, dragData, permissions, children }: AssetActionsProps) => {\n const { formatMessage } = useIntl();\n const getErrorMessage = useApiErrorMessage();\n const { copy } = useClipboard();\n const { toggleNotification } = useNotification();\n const { deselect } = useAssetSelection();\n // Absent in the asset picker and in unit tests: the replace still runs, it\n // just renders no row-level overlay.\n const markBusy = useBusyAssetsOptional()?.markBusy ?? (() => () => {});\n // Always called — hooks can't be conditional — but ignored when the caller\n // supplied an answer it resolved earlier.\n const ownPermissions = useMediaLibraryPermissions();\n const {\n canUpdate,\n canDownload,\n canCopyLink,\n isLoading: isLoadingPermissions,\n } = permissions ?? ownPermissions;\n const [replaceAsset, { isLoading: isReplacing }] = useReplaceAssetMutation();\n const aiEnabled = useAIMetadataEnabled({ mime: asset.mime });\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [isReplaceOpen, setIsReplaceOpen] = useState(false);\n const [isMoveOpen, setIsMoveOpen] = useState(false);\n const [isDeleteOpen, setIsDeleteOpen] = useState(false);\n const [isDownloading, setIsDownloading] = useState(false);\n // The native picker is not a React thing: nothing re-renders while it is up.\n // A cursor-anchored caller is mounted only while it has something on screen,\n // and without this the input it owns would be unmounted the moment the\n // confirm dialog closes — so the file would come back to nothing.\n const [isAwaitingFile, setIsAwaitingFile] = useState(false);\n\n // Stable identity: the move dialog memoizes its destination walk on it.\n const moveItems = useMemo(() => [dragData], [dragData]);\n\n // Confirm first, then open the native picker, so the user only commits to\n // replacing after acknowledging the warning (same order as the drawer).\n // Dismissing the picker fires `cancel`, not `change`, so without this the\n // caller would stay mounted with nothing on screen. Attached natively: the\n // React version here has no `onCancel` on the input types.\n useEffect(() => {\n const input = fileInputRef.current;\n\n if (!input) {\n return undefined;\n }\n\n const handleCancel = () => setIsAwaitingFile(false);\n\n input.addEventListener('cancel', handleCancel);\n\n return () => input.removeEventListener('cancel', handleCancel);\n }, []);\n\n const handleReplaceContinue = () => {\n setIsReplaceOpen(false);\n setIsAwaitingFile(true);\n fileInputRef.current?.click();\n };\n\n const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0];\n // Reset the native input so the same file can be picked again later.\n event.target.value = '';\n setIsAwaitingFile(false);\n\n if (!file) {\n return;\n }\n\n // The menu has closed by the time the picker returns, so `isReplacing` has\n // nothing left to disable and the row would otherwise sit inert for the\n // whole upload. Marking the asset busy puts the same overlay the drawer\n // uses on the row/card, cleared when the mutation settles either way.\n const releaseBusy = markBusy(\n asset.id,\n formatMessage({\n id: getTranslationKey('asset-details.replace.loading'),\n defaultMessage: 'Replacing the file…',\n })\n );\n\n let res;\n try {\n res = await replaceAsset({ id: asset.id, file, fileInfo: { name: asset.name } });\n } finally {\n releaseBusy();\n }\n\n if ('error' in res) {\n toggleNotification({\n type: 'danger',\n message: getErrorMessage(\n res.error,\n formatMessage({\n id: getTranslationKey('asset-details.replace.error'),\n defaultMessage: 'Failed to replace the file.',\n })\n ),\n });\n return;\n }\n\n toggleNotification({\n type: 'success',\n message: formatMessage({\n id: getTranslationKey('asset-details.replace.success'),\n defaultMessage: 'File replaced.',\n }),\n });\n };\n\n const handleCopyLink = async () => {\n const url = prefixFileUrlWithBackendUrl(asset.url);\n\n if (!url) {\n return;\n }\n\n const didCopy = await copy(url);\n\n toggleNotification({\n type: didCopy ? 'success' : 'danger',\n message: formatMessage(\n didCopy\n ? {\n id: getTranslationKey('asset-details.copy-link.success'),\n defaultMessage: 'Link copied.',\n }\n : {\n id: getTranslationKey('asset-details.copy-link.error'),\n defaultMessage: 'Failed to copy the link.',\n }\n ),\n });\n };\n\n const handleDownload = async () => {\n const url = prefixFileUrlWithBackendUrl(asset.url);\n\n if (!url) {\n return;\n }\n\n setIsDownloading(true);\n\n try {\n await downloadFile(url, asset.name);\n } catch {\n toggleNotification({\n type: 'danger',\n message: formatMessage({\n id: getTranslationKey('asset-details.download.error'),\n defaultMessage: 'Failed to download the file.',\n }),\n });\n } finally {\n setIsDownloading(false);\n }\n };\n\n // Two permission-gated groups: replace/copy-link/download above, move/delete\n // below. The separator earns its place only when both survive the gate —\n // `canUpdate` alone puts Replace in the top group, which is not enough.\n const hasTopGroup = canUpdate || canCopyLink || canDownload;\n const hasBottomGroup = canUpdate;\n const showSeparator = (canCopyLink || canDownload) && hasBottomGroup;\n\n const items = (\n <>\n {canUpdate && (\n <Menu.Item\n startIcon={<ArrowsCounterClockwise />}\n disabled={isReplacing}\n onSelect={() => setIsReplaceOpen(true)}\n >\n {formatMessage({\n id: getTranslationKey('list.assets.actions.replace'),\n defaultMessage: 'Replace media',\n })}\n </Menu.Item>\n )}\n {canCopyLink && (\n <Menu.Item startIcon={<Link />} onSelect={handleCopyLink}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.copy-link'),\n defaultMessage: 'Copy link to media',\n })}\n </Menu.Item>\n )}\n {canDownload && (\n <Menu.Item startIcon={<Download />} disabled={isDownloading} onSelect={handleDownload}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.download'),\n defaultMessage: 'Download media',\n })}\n </Menu.Item>\n )}\n {showSeparator && <Menu.Separator />}\n {canUpdate && (\n <>\n <Menu.Item startIcon={<ArrowRight />} onSelect={() => setIsMoveOpen(true)}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.move'),\n defaultMessage: 'Move to folder',\n })}\n </Menu.Item>\n <Menu.Item startIcon={<Trash />} variant=\"danger\" onSelect={() => setIsDeleteOpen(true)}>\n {formatMessage({\n id: getTranslationKey('list.assets.actions.delete'),\n defaultMessage: 'Delete',\n })}\n </Menu.Item>\n </>\n )}\n </>\n );\n\n const dialogs = (\n <>\n <VisuallyHidden>\n <input\n ref={fileInputRef}\n type=\"file\"\n // Replacing swaps the bytes of an existing asset, so the picker offers\n // only its own type. Without this the picker accepted anything and a\n // JPG could come back as PNG bytes still served under a `.jpg` url,\n // since replace preserves hash and ext by design.\n accept={asset.mime ?? ''}\n multiple={false}\n onChange={handleFileChange}\n aria-hidden\n tabIndex={-1}\n />\n </VisuallyHidden>\n <Dialog.Root open={isReplaceOpen} onOpenChange={setIsReplaceOpen}>\n <Dialog.Content>\n <Dialog.Header>\n {formatMessage({\n id: getTranslationKey('asset-details.replace.title'),\n defaultMessage: 'Replace this media file?',\n })}\n </Dialog.Header>\n <Dialog.Body textAlign=\"center\">\n <Flex direction=\"column\" textAlign=\"center\">\n <Typography variant=\"omega\">\n {formatMessage({\n id: getTranslationKey('asset-details.replace.description'),\n defaultMessage: 'Current content will be permanently replaced.',\n })}\n </Typography>\n {aiEnabled ? (\n <Typography variant=\"omega\">\n {formatMessage({\n id: getTranslationKey('asset-details.replace.description.ai'),\n defaultMessage: 'AI will generate new metadata after upload.',\n })}\n </Typography>\n ) : null}\n </Flex>\n </Dialog.Body>\n <Dialog.Footer>\n <Dialog.Cancel>\n <Button variant=\"tertiary\" fullWidth>\n {formatMessage({ id: 'app.components.Button.cancel', defaultMessage: 'Cancel' })}\n </Button>\n </Dialog.Cancel>\n <Dialog.Action>\n <Button variant=\"secondary\" onClick={handleReplaceContinue} fullWidth>\n {formatMessage({\n id: getTranslationKey('asset-details.replace.continue'),\n defaultMessage: 'Continue',\n })}\n </Button>\n </Dialog.Action>\n </Dialog.Footer>\n </Dialog.Content>\n </Dialog.Root>\n {isMoveOpen && (\n <BulkMoveDialog\n open\n onClose={() => setIsMoveOpen(false)}\n items={moveItems}\n onSuccess={() => deselect(assetKey(asset.id))}\n />\n )}\n {/* Both dialogs live inside the row, so a background refetch that drops the\n row would take an open dialog with it. Nothing invalidates until the\n mutation resolves, and the dialog closes in the same tick, so the\n flows themselves can't trigger it. */}\n {isDeleteOpen && (\n <DeleteItemsDialog\n open\n onClose={() => setIsDeleteOpen(false)}\n target={{ fileIds: [asset.id], folderIds: [] }}\n onSuccess={() => deselect(assetKey(asset.id))}\n />\n )}\n </>\n );\n\n return (\n <>\n {children({\n items,\n dialogs,\n // Every flag is `false` until the RBAC check settles, so wait for it —\n // otherwise the trigger unmounts and remounts on first paint for everyone.\n hasActions: isLoadingPermissions || hasTopGroup || hasBottomGroup,\n isBusy: isReplaceOpen || isMoveOpen || isDeleteOpen || isAwaitingFile,\n })}\n </>\n );\n};\n"],"names":["AssetActions","asset","dragData","permissions","children","formatMessage","useIntl","getErrorMessage","useApiErrorMessage","copy","useClipboard","toggleNotification","useNotification","deselect","useAssetSelection","markBusy","useBusyAssetsOptional","ownPermissions","useMediaLibraryPermissions","canUpdate","canDownload","canCopyLink","isLoading","isLoadingPermissions","replaceAsset","isReplacing","useReplaceAssetMutation","aiEnabled","useAIMetadataEnabled","mime","fileInputRef","useRef","isReplaceOpen","setIsReplaceOpen","useState","isMoveOpen","setIsMoveOpen","isDeleteOpen","setIsDeleteOpen","isDownloading","setIsDownloading","isAwaitingFile","setIsAwaitingFile","moveItems","useMemo","useEffect","input","current","undefined","handleCancel","addEventListener","removeEventListener","handleReplaceContinue","click","handleFileChange","event","file","target","files","value","releaseBusy","id","getTranslationKey","defaultMessage","res","fileInfo","name","type","message","error","handleCopyLink","url","prefixFileUrlWithBackendUrl","didCopy","handleDownload","downloadFile","hasTopGroup","hasBottomGroup","showSeparator","items","_jsxs","_Fragment","_jsx","Menu","Item","startIcon","ArrowsCounterClockwise","disabled","onSelect","Link","Download","Separator","ArrowRight","Trash","variant","dialogs","VisuallyHidden","ref","accept","multiple","onChange","aria-hidden","tabIndex","Dialog","Root","open","onOpenChange","Content","Header","Body","textAlign","Flex","direction","Typography","Footer","Cancel","Button","fullWidth","Action","onClick","BulkMoveDialog","onClose","onSuccess","assetKey","DeleteItemsDialog","fileIds","folderIds","hasActions","isBusy"],"mappings":"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"}