@strapi/upload 5.51.1 → 5.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/components/AssetCard/VideoPreview.js +1 -1
- package/dist/admin/components/AssetCard/VideoPreview.js.map +1 -1
- package/dist/admin/components/AssetCard/VideoPreview.mjs +1 -1
- package/dist/admin/components/AssetCard/VideoPreview.mjs.map +1 -1
- package/dist/admin/components/TableList/TableList.js +1 -1
- package/dist/admin/components/TableList/TableList.js.map +1 -1
- package/dist/admin/components/TableList/TableList.mjs +1 -1
- package/dist/admin/components/TableList/TableList.mjs.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js +21 -4
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.js.map +1 -1
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs +21 -4
- package/dist/admin/components/UploadAssetDialog/AddAssetStep/FromUrlForm.mjs.map +1 -1
- package/dist/admin/future/App.js +3 -3
- package/dist/admin/future/App.js.map +1 -1
- package/dist/admin/future/App.mjs +4 -4
- package/dist/admin/future/App.mjs.map +1 -1
- package/dist/admin/future/components/BetaNotice.js +72 -0
- package/dist/admin/future/components/BetaNotice.js.map +1 -0
- package/dist/admin/future/components/BetaNotice.mjs +70 -0
- package/dist/admin/future/components/BetaNotice.mjs.map +1 -0
- package/dist/admin/future/components/Drawer.js +18 -3
- package/dist/admin/future/components/Drawer.js.map +1 -1
- package/dist/admin/future/components/Drawer.mjs +18 -3
- package/dist/admin/future/components/Drawer.mjs.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.js +200 -27
- package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
- package/dist/admin/future/components/UploadProgressDialog.mjs +203 -30
- package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
- package/dist/admin/future/hooks/useAIMetadataEnabled.js +39 -0
- package/dist/admin/future/hooks/useAIMetadataEnabled.js.map +1 -0
- package/dist/admin/future/hooks/useAIMetadataEnabled.mjs +37 -0
- package/dist/admin/future/hooks/useAIMetadataEnabled.mjs.map +1 -0
- package/dist/admin/future/hooks/useMediaLibraryPermissions.js +35 -0
- package/dist/admin/future/hooks/useMediaLibraryPermissions.js.map +1 -0
- package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs +33 -0
- package/dist/admin/future/hooks/useMediaLibraryPermissions.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/AssetsPage.js +453 -170
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +454 -171
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js +46 -0
- package/dist/admin/future/pages/Assets/components/ActionsMenuContent.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs +44 -0
- package/dist/admin/future/pages/Assets/components/ActionsMenuContent.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +325 -0
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +323 -0
- package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +110 -46
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +110 -46
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +95 -67
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +95 -68
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +94 -26
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +96 -28
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +112 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +110 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +172 -56
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +175 -59
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +188 -138
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +191 -141
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +123 -76
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +126 -79
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BusyOverlay.js +36 -0
- package/dist/admin/future/pages/Assets/components/BusyOverlay.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs +34 -0
- package/dist/admin/future/pages/Assets/components/BusyOverlay.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.js +193 -0
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs +191 -0
- package/dist/admin/future/pages/Assets/components/DateRangeCalendar.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js +131 -0
- package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs +129 -0
- package/dist/admin/future/pages/Assets/components/DeleteItemsDialog.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +45 -18
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +46 -19
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +28 -31
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +28 -31
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +10 -15
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +10 -15
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js +13 -3
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs +13 -3
- package/dist/admin/future/pages/Assets/components/DropZone/UploadDropZoneContext.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/EmptyState.js +62 -3
- package/dist/admin/future/pages/Assets/components/EmptyState.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/EmptyState.mjs +62 -4
- package/dist/admin/future/pages/Assets/components/EmptyState.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FilterBadges.js +417 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +415 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.js +453 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +447 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js +144 -0
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs +142 -0
- package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.js → FolderFormDialog.js} +50 -16
- package/dist/admin/future/pages/Assets/components/FolderFormDialog.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/{CreateFolderDialog.mjs → FolderFormDialog.mjs} +52 -18
- package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +125 -148
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +127 -150
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/SortMenu.js +21 -32
- package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs +22 -33
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js +40 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs +38 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSearch.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +21 -5
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +21 -5
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js +47 -0
- package/dist/admin/future/pages/Assets/hooks/useBusyAssets.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs +44 -0
- package/dist/admin/future/pages/Assets/hooks/useBusyAssets.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js +33 -12
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs +33 -12
- package/dist/admin/future/pages/Assets/hooks/useFolderNavigation.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js +222 -45
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +223 -46
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js +75 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs +73 -0
- package/dist/admin/future/pages/Assets/hooks/useInfiniteScrollSentinel.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListFilters.js +168 -0
- package/dist/admin/future/pages/Assets/hooks/useListFilters.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs +162 -0
- package/dist/admin/future/pages/Assets/hooks/useListFilters.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.js +201 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.mjs +198 -0
- package/dist/admin/future/pages/Assets/utils/buildAssetFilters.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.js +2 -2
- package/dist/admin/future/pages/Assets/utils/listQueryKey.js.map +1 -1
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs +2 -2
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs.map +1 -1
- package/dist/admin/future/services/api.js +187 -28
- package/dist/admin/future/services/api.js.map +1 -1
- package/dist/admin/future/services/api.mjs +187 -30
- package/dist/admin/future/services/api.mjs.map +1 -1
- package/dist/admin/future/services/assets.js +40 -17
- package/dist/admin/future/services/assets.js.map +1 -1
- package/dist/admin/future/services/assets.mjs +40 -17
- package/dist/admin/future/services/assets.mjs.map +1 -1
- package/dist/admin/future/services/folders.js +66 -19
- package/dist/admin/future/services/folders.js.map +1 -1
- package/dist/admin/future/services/folders.mjs +66 -20
- package/dist/admin/future/services/folders.mjs.map +1 -1
- package/dist/admin/future/services/settings.js +10 -3
- package/dist/admin/future/services/settings.js.map +1 -1
- package/dist/admin/future/services/settings.mjs +10 -3
- package/dist/admin/future/services/settings.mjs.map +1 -1
- package/dist/admin/future/store/uploadProgress.js +109 -2
- package/dist/admin/future/store/uploadProgress.js.map +1 -1
- package/dist/admin/future/store/uploadProgress.mjs +105 -3
- package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
- package/dist/admin/future/utils/buildDragSetFromSelection.js +7 -5
- package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -1
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs +7 -5
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -1
- package/dist/admin/future/utils/formatMoveSuccessMessage.js +12 -1
- package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -1
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +12 -1
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -1
- package/dist/admin/future/utils/getRelationId.js +24 -0
- package/dist/admin/future/utils/getRelationId.js.map +1 -0
- package/dist/admin/future/utils/getRelationId.mjs +22 -0
- package/dist/admin/future/utils/getRelationId.mjs.map +1 -0
- package/dist/admin/future/utils/isEventFromWithin.js +14 -0
- package/dist/admin/future/utils/isEventFromWithin.js.map +1 -0
- package/dist/admin/future/utils/isEventFromWithin.mjs +12 -0
- package/dist/admin/future/utils/isEventFromWithin.mjs.map +1 -0
- package/dist/admin/future/utils/itemLocations.js +40 -0
- package/dist/admin/future/utils/itemLocations.js.map +1 -0
- package/dist/admin/future/utils/itemLocations.mjs +36 -0
- package/dist/admin/future/utils/itemLocations.mjs.map +1 -0
- package/dist/admin/future/utils/itemSource.js +36 -0
- package/dist/admin/future/utils/itemSource.js.map +1 -0
- package/dist/admin/future/utils/itemSource.mjs +32 -0
- package/dist/admin/future/utils/itemSource.mjs.map +1 -0
- package/dist/admin/future/utils/searchQueryParam.js +11 -0
- package/dist/admin/future/utils/searchQueryParam.js.map +1 -0
- package/dist/admin/future/utils/searchQueryParam.mjs +9 -0
- package/dist/admin/future/utils/searchQueryParam.mjs.map +1 -0
- package/dist/admin/hooks/useSelectionState.js +2 -4
- package/dist/admin/hooks/useSelectionState.js.map +1 -1
- package/dist/admin/hooks/useSelectionState.mjs +2 -4
- package/dist/admin/hooks/useSelectionState.mjs.map +1 -1
- package/dist/admin/index.js +11 -17
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +12 -18
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/shared/constants.js +43 -0
- package/dist/admin/shared/constants.js.map +1 -0
- package/dist/admin/shared/constants.mjs +38 -0
- package/dist/admin/shared/constants.mjs.map +1 -0
- package/dist/admin/src/future/App.d.ts +1 -1
- package/dist/admin/src/future/components/BetaNotice.d.ts +11 -0
- package/dist/admin/src/future/components/Drawer.d.ts +5 -1
- package/dist/admin/src/future/hooks/useAIMetadataEnabled.d.ts +26 -0
- package/dist/admin/src/future/hooks/useMediaLibraryPermissions.d.ts +21 -0
- package/dist/admin/src/future/pages/Assets/components/ActionsMenuContent.d.ts +33 -0
- package/dist/admin/src/future/pages/Assets/components/AssetActionsMenu.d.ts +24 -0
- package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetCropEditor.d.ts +7 -1
- package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +16 -0
- package/dist/admin/src/future/pages/Assets/components/AssetsSearchInput.d.ts +5 -0
- package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +21 -1
- package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +17 -6
- package/dist/admin/src/future/pages/Assets/components/BusyOverlay.d.ts +9 -0
- package/dist/admin/src/future/pages/Assets/components/DateRangeCalendar.d.ts +8 -0
- package/dist/admin/src/future/pages/Assets/components/DeleteItemsDialog.d.ts +27 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +18 -3
- package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +2 -1
- package/dist/admin/src/future/pages/Assets/components/DropZone/UploadDropZoneContext.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/components/EmptyState.d.ts +18 -1
- package/dist/admin/src/future/pages/Assets/components/FilterBadges.d.ts +8 -0
- package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +26 -0
- package/dist/admin/src/future/pages/Assets/components/FolderActionsMenu.d.ts +20 -0
- package/dist/admin/src/future/pages/Assets/components/FolderFormDialog.d.ts +16 -0
- package/dist/admin/src/future/pages/Assets/components/FolderTree/FolderTree.d.ts +13 -4
- package/dist/admin/src/future/pages/Assets/components/SortMenu.d.ts +6 -0
- package/dist/admin/src/future/pages/Assets/hooks/useAssetSearch.d.ts +12 -0
- package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +8 -1
- package/dist/admin/src/future/pages/Assets/hooks/useBusyAssets.d.ts +38 -0
- package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +27 -3
- package/dist/admin/src/future/pages/Assets/hooks/useInfiniteScrollSentinel.d.ts +41 -0
- package/dist/admin/src/future/pages/Assets/hooks/useListFilters.d.ts +69 -0
- package/dist/admin/src/future/pages/Assets/utils/buildAssetFilters.d.ts +46 -0
- package/dist/admin/src/future/pages/Assets/utils/listQueryKey.d.ts +3 -4
- package/dist/admin/src/future/services/api.d.ts +53 -5
- package/dist/admin/src/future/services/assets.d.ts +9 -1
- package/dist/admin/src/future/services/folders.d.ts +9 -1
- package/dist/admin/src/future/services/settings.d.ts +2 -2
- package/dist/admin/src/future/store/uploadProgress.d.ts +83 -1
- package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +4 -3
- package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +7 -1
- package/dist/admin/src/future/utils/getRelationId.d.ts +11 -0
- package/dist/admin/src/future/utils/isEventFromWithin.d.ts +11 -0
- package/dist/admin/src/future/utils/itemLocations.d.ts +31 -0
- package/dist/admin/src/future/utils/itemSource.d.ts +22 -0
- package/dist/admin/src/future/utils/searchQueryParam.d.ts +7 -0
- package/dist/admin/src/hooks/useAiAvailability.d.ts +1 -1
- package/dist/admin/src/hooks/useEditAsset.d.ts +4 -4
- package/dist/admin/src/hooks/useEditFolder.d.ts +4 -4
- package/dist/admin/src/hooks/useSettings.d.ts +1 -0
- package/dist/admin/src/hooks/useTracking.d.ts +1 -1
- package/dist/admin/src/hooks/useUpload.d.ts +1 -1
- package/dist/admin/translations/en.json.js +73 -2
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +73 -2
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/urlYupSchema.js +33 -9
- package/dist/admin/utils/urlYupSchema.js.map +1 -1
- package/dist/admin/utils/urlYupSchema.mjs +33 -9
- package/dist/admin/utils/urlYupSchema.mjs.map +1 -1
- package/dist/server/config.js +13 -5
- package/dist/server/config.js.map +1 -1
- package/dist/server/config.mjs +13 -5
- package/dist/server/config.mjs.map +1 -1
- package/dist/server/constants.js.map +1 -1
- package/dist/server/constants.mjs.map +1 -1
- package/dist/server/controllers/admin-file.js +30 -0
- package/dist/server/controllers/admin-file.js.map +1 -1
- package/dist/server/controllers/admin-file.mjs +30 -0
- package/dist/server/controllers/admin-file.mjs.map +1 -1
- package/dist/server/controllers/admin-settings.js +20 -2
- package/dist/server/controllers/admin-settings.js.map +1 -1
- package/dist/server/controllers/admin-settings.mjs +21 -3
- package/dist/server/controllers/admin-settings.mjs.map +1 -1
- package/dist/server/controllers/content-api.js +1 -2
- package/dist/server/controllers/content-api.js.map +1 -1
- package/dist/server/controllers/content-api.mjs +2 -2
- package/dist/server/controllers/content-api.mjs.map +1 -1
- package/dist/server/controllers/validation/admin/ai-metadata.js +12 -0
- package/dist/server/controllers/validation/admin/ai-metadata.js.map +1 -0
- package/dist/server/controllers/validation/admin/ai-metadata.mjs +10 -0
- package/dist/server/controllers/validation/admin/ai-metadata.mjs.map +1 -0
- package/dist/server/controllers/validation/admin/settings.js +4 -1
- package/dist/server/controllers/validation/admin/settings.js.map +1 -1
- package/dist/server/controllers/validation/admin/settings.mjs +1 -1
- package/dist/server/controllers/validation/admin/settings.mjs.map +1 -1
- package/dist/server/routes/admin.js +18 -0
- package/dist/server/routes/admin.js.map +1 -1
- package/dist/server/routes/admin.mjs +18 -0
- package/dist/server/routes/admin.mjs.map +1 -1
- package/dist/server/services/ai-metadata.js +108 -11
- package/dist/server/services/ai-metadata.js.map +1 -1
- package/dist/server/services/ai-metadata.mjs +108 -11
- package/dist/server/services/ai-metadata.mjs.map +1 -1
- package/dist/server/services/file.js +86 -13
- package/dist/server/services/file.js.map +1 -1
- package/dist/server/services/file.mjs +87 -14
- package/dist/server/services/file.mjs.map +1 -1
- package/dist/server/services/image-manipulation.js +4 -4
- package/dist/server/services/image-manipulation.js.map +1 -1
- package/dist/server/services/image-manipulation.mjs +4 -4
- package/dist/server/services/image-manipulation.mjs.map +1 -1
- package/dist/server/shared/constants.js +43 -0
- package/dist/server/shared/constants.js.map +1 -0
- package/dist/server/shared/constants.mjs +38 -0
- package/dist/server/shared/constants.mjs.map +1 -0
- package/dist/server/src/config.d.ts +2 -0
- package/dist/server/src/config.d.ts.map +1 -1
- package/dist/server/src/constants.d.ts +2 -1
- package/dist/server/src/constants.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-file.d.ts +10 -0
- package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-settings.d.ts.map +1 -1
- package/dist/server/src/controllers/index.d.ts +1 -0
- package/dist/server/src/controllers/index.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts +2 -0
- package/dist/server/src/controllers/validation/admin/ai-metadata.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/admin/settings.d.ts +1 -1
- package/dist/server/src/controllers/validation/admin/settings.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +7 -1
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/routes/admin.d.ts.map +1 -1
- package/dist/server/src/services/ai-metadata.d.ts +19 -0
- package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
- package/dist/server/src/services/file.d.ts +12 -2
- package/dist/server/src/services/file.d.ts.map +1 -1
- package/dist/server/src/services/image-manipulation.d.ts +0 -5
- package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +4 -1
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/types.d.ts +3 -0
- package/dist/server/src/types.d.ts.map +1 -1
- package/dist/shared/constants.d.ts +35 -0
- package/dist/shared/constants.d.ts.map +1 -0
- package/dist/shared/contracts/files.d.ts +25 -0
- package/dist/shared/contracts/files.d.ts.map +1 -1
- package/dist/shared/contracts/settings.d.ts +7 -0
- package/package.json +10 -10
- package/dist/admin/future/pages/Assets/components/CreateFolderDialog.js.map +0 -1
- package/dist/admin/future/pages/Assets/components/CreateFolderDialog.mjs.map +0 -1
- package/dist/admin/src/future/pages/Assets/components/CreateFolderDialog.d.ts +0 -8
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
const MIME_BY_VALUE = {
|
|
2
|
+
picture: 'image',
|
|
3
|
+
audio: 'audio',
|
|
4
|
+
video: 'video'
|
|
5
|
+
};
|
|
6
|
+
const ALL_MIMES = Object.values(MIME_BY_VALUE);
|
|
7
|
+
const PRESET_DELTAS = {
|
|
8
|
+
'1day': {
|
|
9
|
+
days: 1
|
|
10
|
+
},
|
|
11
|
+
'3days': {
|
|
12
|
+
days: 3
|
|
13
|
+
},
|
|
14
|
+
'1week': {
|
|
15
|
+
days: 7
|
|
16
|
+
},
|
|
17
|
+
'1month': {
|
|
18
|
+
months: 1
|
|
19
|
+
},
|
|
20
|
+
'3months': {
|
|
21
|
+
months: 3
|
|
22
|
+
},
|
|
23
|
+
'6months': {
|
|
24
|
+
months: 6
|
|
25
|
+
},
|
|
26
|
+
'1year': {
|
|
27
|
+
years: 1
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const subtractPreset = (now, preset)=>{
|
|
31
|
+
const { days = 0, months = 0, years = 0 } = PRESET_DELTAS[preset];
|
|
32
|
+
const result = new Date(now.getTime());
|
|
33
|
+
if (years || months) {
|
|
34
|
+
// JS date setters overflow at month ends (Mar 31 − 1 month → Feb 31 → Mar 3),
|
|
35
|
+
// so move to the 1st first and clamp the day to the target month's length.
|
|
36
|
+
const dayOfMonth = result.getDate();
|
|
37
|
+
result.setDate(1);
|
|
38
|
+
result.setFullYear(result.getFullYear() - years);
|
|
39
|
+
result.setMonth(result.getMonth() - months);
|
|
40
|
+
const daysInTargetMonth = new Date(result.getFullYear(), result.getMonth() + 1, 0).getDate();
|
|
41
|
+
result.setDate(Math.min(dayOfMonth, daysInTargetMonth));
|
|
42
|
+
}
|
|
43
|
+
result.setDate(result.getDate() - days);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
const startOfDay = (date)=>{
|
|
47
|
+
const result = new Date(date.getTime());
|
|
48
|
+
result.setHours(0, 0, 0, 0);
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
const endOfDay = (date)=>{
|
|
52
|
+
const result = new Date(date.getTime());
|
|
53
|
+
result.setHours(23, 59, 59, 999);
|
|
54
|
+
return result;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Parses `YYYY-MM-DD` as a local calendar date — NOT `new Date(value)`, which
|
|
58
|
+
* treats date-only strings as UTC midnight and shifts the day in every
|
|
59
|
+
* timezone west of UTC. Shared by the query builder, the badge labels and the
|
|
60
|
+
* range calendar so all three agree on which day a range endpoint means.
|
|
61
|
+
*/ const parseCalendarDate = (value)=>{
|
|
62
|
+
const [year, month, day] = value.split('-').map(Number);
|
|
63
|
+
return new Date(year, month - 1, day);
|
|
64
|
+
};
|
|
65
|
+
const buildDateClause = (filter, now)=>{
|
|
66
|
+
const { field } = filter;
|
|
67
|
+
if (filter.mode === 'preset') {
|
|
68
|
+
const pivot = subtractPreset(now, filter.preset);
|
|
69
|
+
switch(filter.condition){
|
|
70
|
+
case 'withinLast':
|
|
71
|
+
return {
|
|
72
|
+
[field]: {
|
|
73
|
+
$gte: pivot.toISOString()
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
case 'notWithinLast':
|
|
77
|
+
return {
|
|
78
|
+
[field]: {
|
|
79
|
+
$lt: pivot.toISOString()
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
case 'isExactly':
|
|
83
|
+
return {
|
|
84
|
+
[field]: {
|
|
85
|
+
$gte: startOfDay(pivot).toISOString(),
|
|
86
|
+
$lte: endOfDay(pivot).toISOString()
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const from = startOfDay(parseCalendarDate(filter.from)).toISOString();
|
|
92
|
+
const to = endOfDay(parseCalendarDate(filter.to)).toISOString();
|
|
93
|
+
if (filter.condition === 'is') {
|
|
94
|
+
return {
|
|
95
|
+
[field]: {
|
|
96
|
+
$gte: from,
|
|
97
|
+
$lte: to
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
$or: [
|
|
103
|
+
{
|
|
104
|
+
[field]: {
|
|
105
|
+
$lt: from
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
[field]: {
|
|
110
|
+
$gt: to
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
const buildMimeClause = (filter)=>{
|
|
117
|
+
const fileValues = filter.values.filter((value)=>value !== 'folder');
|
|
118
|
+
if (fileValues.length === 0) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const branches = fileValues.map((value)=>{
|
|
122
|
+
if (value === 'document') {
|
|
123
|
+
// Catch-all: none of the known mime families.
|
|
124
|
+
return {
|
|
125
|
+
$and: ALL_MIMES.map((mime)=>({
|
|
126
|
+
mime: {
|
|
127
|
+
$notContains: mime
|
|
128
|
+
}
|
|
129
|
+
}))
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
mime: {
|
|
134
|
+
$contains: MIME_BY_VALUE[value]
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
if (filter.condition === 'is') {
|
|
139
|
+
return branches.length === 1 ? branches[0] : {
|
|
140
|
+
$or: branches
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// "is not" = matches none of the selected values: negate each branch, AND them.
|
|
144
|
+
const negated = fileValues.map((value)=>{
|
|
145
|
+
if (value === 'document') {
|
|
146
|
+
// Not a document = is one of the known families.
|
|
147
|
+
return {
|
|
148
|
+
$or: ALL_MIMES.map((mime)=>({
|
|
149
|
+
mime: {
|
|
150
|
+
$contains: mime
|
|
151
|
+
}
|
|
152
|
+
}))
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
mime: {
|
|
157
|
+
$notContains: MIME_BY_VALUE[value]
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
return negated.length === 1 ? negated[0] : {
|
|
162
|
+
$and: negated
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
const buildAssetFilters = (filters, now)=>{
|
|
166
|
+
const fileClauses = [];
|
|
167
|
+
const folderClauses = [];
|
|
168
|
+
let showFolders = true;
|
|
169
|
+
let showFiles = true;
|
|
170
|
+
for (const filter of filters){
|
|
171
|
+
if (filter.kind === 'date') {
|
|
172
|
+
const clause = buildDateClause(filter, now);
|
|
173
|
+
fileClauses.push(clause);
|
|
174
|
+
folderClauses.push(clause);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const includesFolder = filter.values.includes('folder');
|
|
178
|
+
if (filter.condition === 'is' ? !includesFolder : includesFolder) {
|
|
179
|
+
showFolders = false;
|
|
180
|
+
}
|
|
181
|
+
const mimeClause = buildMimeClause(filter);
|
|
182
|
+
if (mimeClause) {
|
|
183
|
+
fileClauses.push(mimeClause);
|
|
184
|
+
} else if (filter.condition === 'is') {
|
|
185
|
+
// `is [folder]` only — no file can match the badge.
|
|
186
|
+
showFiles = false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
fileClauses,
|
|
191
|
+
folderClauses,
|
|
192
|
+
showFolders,
|
|
193
|
+
showFiles
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
export { buildAssetFilters, parseCalendarDate };
|
|
198
|
+
//# sourceMappingURL=buildAssetFilters.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildAssetFilters.mjs","sources":["../../../../../../admin/src/future/pages/Assets/utils/buildAssetFilters.ts"],"sourcesContent":["import {\n type DateFilter,\n type DatePreset,\n type ListFilter,\n type TypeFilter,\n type TypeValue,\n} from '../hooks/useListFilters';\n\n/**\n * Translates the URL filter model into Strapi `filters[$and]` entries for the\n * files and folders queries, and decides the structural visibility of both\n * sections.\n *\n * Relative presets resolve against `now` at build time — the URL stays\n * relative (\"within the last week\"), only the outgoing query is absolute.\n *\n * Semantics:\n * - Every badge is one `$and` entry (badges combine with AND, per ticket).\n * - Type: `folder` is not a mime — it acts structurally on the folders\n * section. The remaining values map to mime clauses; `document` is the\n * legacy catch-all (mime contains none of image/audio/video — audio\n * included in the exclusion, fixing the legacy quirk).\n * - Dates apply to files and folders alike (folders have both timestamps).\n * - \"is exactly <preset>\" means the calendar day of `now - delta`.\n * - Ranges are inclusive on both ends (start-of-day .. end-of-day).\n */\n\ntype QueryClause = Record<string, unknown>;\n\nconst MIME_BY_VALUE: Record<Exclude<TypeValue, 'folder' | 'document'>, string> = {\n picture: 'image',\n audio: 'audio',\n video: 'video',\n};\n\nconst ALL_MIMES = Object.values(MIME_BY_VALUE);\n\nconst PRESET_DELTAS: Record<DatePreset, { days?: number; months?: number; years?: number }> = {\n '1day': { days: 1 },\n '3days': { days: 3 },\n '1week': { days: 7 },\n '1month': { months: 1 },\n '3months': { months: 3 },\n '6months': { months: 6 },\n '1year': { years: 1 },\n};\n\nconst subtractPreset = (now: Date, preset: DatePreset): Date => {\n const { days = 0, months = 0, years = 0 } = PRESET_DELTAS[preset];\n const result = new Date(now.getTime());\n\n if (years || months) {\n // JS date setters overflow at month ends (Mar 31 − 1 month → Feb 31 → Mar 3),\n // so move to the 1st first and clamp the day to the target month's length.\n const dayOfMonth = result.getDate();\n result.setDate(1);\n result.setFullYear(result.getFullYear() - years);\n result.setMonth(result.getMonth() - months);\n\n const daysInTargetMonth = new Date(result.getFullYear(), result.getMonth() + 1, 0).getDate();\n result.setDate(Math.min(dayOfMonth, daysInTargetMonth));\n }\n\n result.setDate(result.getDate() - days);\n\n return result;\n};\n\nconst startOfDay = (date: Date): Date => {\n const result = new Date(date.getTime());\n result.setHours(0, 0, 0, 0);\n return result;\n};\n\nconst endOfDay = (date: Date): Date => {\n const result = new Date(date.getTime());\n result.setHours(23, 59, 59, 999);\n return result;\n};\n\n/**\n * Parses `YYYY-MM-DD` as a local calendar date — NOT `new Date(value)`, which\n * treats date-only strings as UTC midnight and shifts the day in every\n * timezone west of UTC. Shared by the query builder, the badge labels and the\n * range calendar so all three agree on which day a range endpoint means.\n */\nexport const parseCalendarDate = (value: string): Date => {\n const [year, month, day] = value.split('-').map(Number);\n return new Date(year, month - 1, day);\n};\n\nconst buildDateClause = (filter: DateFilter, now: Date): QueryClause => {\n const { field } = filter;\n\n if (filter.mode === 'preset') {\n const pivot = subtractPreset(now, filter.preset);\n\n switch (filter.condition) {\n case 'withinLast':\n return { [field]: { $gte: pivot.toISOString() } };\n case 'notWithinLast':\n return { [field]: { $lt: pivot.toISOString() } };\n case 'isExactly':\n return {\n [field]: {\n $gte: startOfDay(pivot).toISOString(),\n $lte: endOfDay(pivot).toISOString(),\n },\n };\n }\n }\n\n const from = startOfDay(parseCalendarDate(filter.from)).toISOString();\n const to = endOfDay(parseCalendarDate(filter.to)).toISOString();\n\n if (filter.condition === 'is') {\n return { [field]: { $gte: from, $lte: to } };\n }\n\n return { $or: [{ [field]: { $lt: from } }, { [field]: { $gt: to } }] };\n};\n\nconst buildMimeClause = (filter: TypeFilter): QueryClause | null => {\n const fileValues = filter.values.filter((value): value is TypeValue => value !== 'folder');\n\n if (fileValues.length === 0) {\n return null;\n }\n\n const branches: QueryClause[] = fileValues.map((value) => {\n if (value === 'document') {\n // Catch-all: none of the known mime families.\n return { $and: ALL_MIMES.map((mime) => ({ mime: { $notContains: mime } })) };\n }\n return { mime: { $contains: MIME_BY_VALUE[value as keyof typeof MIME_BY_VALUE] } };\n });\n\n if (filter.condition === 'is') {\n return branches.length === 1 ? branches[0] : { $or: branches };\n }\n\n // \"is not\" = matches none of the selected values: negate each branch, AND them.\n const negated: QueryClause[] = fileValues.map((value) => {\n if (value === 'document') {\n // Not a document = is one of the known families.\n return { $or: ALL_MIMES.map((mime) => ({ mime: { $contains: mime } })) };\n }\n return { mime: { $notContains: MIME_BY_VALUE[value as keyof typeof MIME_BY_VALUE] } };\n });\n\n return negated.length === 1 ? negated[0] : { $and: negated };\n};\n\nexport interface BuiltFilters {\n /** Extra `$and` entries for the files query. */\n fileClauses: QueryClause[];\n /** Extra `$and` entries for the folders query (dates only). */\n folderClauses: QueryClause[];\n /**\n * Whether the folders section can show at all. A type badge decides it by\n * evaluating \"folder\" like any other value: `is` shows folders only when\n * folder is selected; `is not` hides them only when folder is selected.\n */\n showFolders: boolean;\n /**\n * Whether the files list can show at all — false when an `is` type badge\n * selects only \"folder\" (no file can match).\n */\n showFiles: boolean;\n}\n\nexport const buildAssetFilters = (filters: ListFilter[], now: Date): BuiltFilters => {\n const fileClauses: QueryClause[] = [];\n const folderClauses: QueryClause[] = [];\n let showFolders = true;\n let showFiles = true;\n\n for (const filter of filters) {\n if (filter.kind === 'date') {\n const clause = buildDateClause(filter, now);\n fileClauses.push(clause);\n folderClauses.push(clause);\n continue;\n }\n\n const includesFolder = filter.values.includes('folder');\n if (filter.condition === 'is' ? !includesFolder : includesFolder) {\n showFolders = false;\n }\n\n const mimeClause = buildMimeClause(filter);\n if (mimeClause) {\n fileClauses.push(mimeClause);\n } else if (filter.condition === 'is') {\n // `is [folder]` only — no file can match the badge.\n showFiles = false;\n }\n }\n\n return { fileClauses, folderClauses, showFolders, showFiles };\n};\n"],"names":["MIME_BY_VALUE","picture","audio","video","ALL_MIMES","Object","values","PRESET_DELTAS","days","months","years","subtractPreset","now","preset","result","Date","getTime","dayOfMonth","getDate","setDate","setFullYear","getFullYear","setMonth","getMonth","daysInTargetMonth","Math","min","startOfDay","date","setHours","endOfDay","parseCalendarDate","value","year","month","day","split","map","Number","buildDateClause","filter","field","mode","pivot","condition","$gte","toISOString","$lt","$lte","from","to","$or","$gt","buildMimeClause","fileValues","length","branches","$and","mime","$notContains","$contains","negated","buildAssetFilters","filters","fileClauses","folderClauses","showFolders","showFiles","kind","clause","push","includesFolder","includes","mimeClause"],"mappings":"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"}
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* same list, so the selection survives the toggle.
|
|
12
12
|
* - Infinite scroll (load more) does NOT change any segment → selection persists.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Filter uses a stable default until its header control is wired; that follow-up
|
|
15
|
+
* only passes a real value — no selection logic changes needed.
|
|
16
16
|
*/ /**
|
|
17
17
|
* Builds a stable fingerprint for the current asset list query.
|
|
18
18
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listQueryKey.js","sources":["../../../../../../admin/src/future/pages/Assets/utils/listQueryKey.ts"],"sourcesContent":["/**\n * Fingerprint for \"which asset list the user is looking at\".\n *\n * Selection clears when this key changes (see ClearSelectionOnChange in AssetsPage).\n *\n * Hybrid rule\n * - Folder, search, sort, filter changes → new key → selection clears.\n * - Switching table/grid view does NOT change the key — both views render the\n * same list, so the selection survives the toggle.\n * - Infinite scroll (load more) does NOT change any segment → selection persists.\n *\n *
|
|
1
|
+
{"version":3,"file":"listQueryKey.js","sources":["../../../../../../admin/src/future/pages/Assets/utils/listQueryKey.ts"],"sourcesContent":["/**\n * Fingerprint for \"which asset list the user is looking at\".\n *\n * Selection clears when this key changes (see ClearSelectionOnChange in AssetsPage).\n *\n * Hybrid rule\n * - Folder, search, sort, filter changes → new key → selection clears.\n * - Switching table/grid view does NOT change the key — both views render the\n * same list, so the selection survives the toggle.\n * - Infinite scroll (load more) does NOT change any segment → selection persists.\n *\n * Filter uses a stable default until its header control is wired; that follow-up\n * only passes a real value — no selection logic changes needed.\n */\nexport interface ListQueryKeyInput {\n folderId: number | null;\n search: string;\n /** Composite of asset sort and folder position — both change the render order. */\n sort: string | null;\n /** Null until filter UI ships. */\n filter: string | null;\n}\n\n/**\n * Builds a stable fingerprint for the current asset list query.\n *\n * Selection clears when this key changes. Infinite scroll does not change any\n * segment, so selection persists across load-more.\n */\nexport const getListQueryKey = ({ folderId, search, sort, filter }: ListQueryKeyInput): string =>\n JSON.stringify({ folderId, search, sort, filter });\n"],"names":["getListQueryKey","folderId","search","sort","filter","JSON","stringify"],"mappings":";;AAAA;;;;;;;;;;;;;;;;;;AA4BC,IACM,MAAMA,eAAAA,GAAkB,CAAC,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,IAAI,EAAEC,MAAM,EAAqB,GACnFC,IAAAA,CAAKC,SAAS,CAAC;AAAEL,QAAAA,QAAAA;AAAUC,QAAAA,MAAAA;AAAQC,QAAAA,IAAAA;AAAMC,QAAAA;KAAO;;;;"}
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* same list, so the selection survives the toggle.
|
|
10
10
|
* - Infinite scroll (load more) does NOT change any segment → selection persists.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Filter uses a stable default until its header control is wired; that follow-up
|
|
13
|
+
* only passes a real value — no selection logic changes needed.
|
|
14
14
|
*/ /**
|
|
15
15
|
* Builds a stable fingerprint for the current asset list query.
|
|
16
16
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listQueryKey.mjs","sources":["../../../../../../admin/src/future/pages/Assets/utils/listQueryKey.ts"],"sourcesContent":["/**\n * Fingerprint for \"which asset list the user is looking at\".\n *\n * Selection clears when this key changes (see ClearSelectionOnChange in AssetsPage).\n *\n * Hybrid rule\n * - Folder, search, sort, filter changes → new key → selection clears.\n * - Switching table/grid view does NOT change the key — both views render the\n * same list, so the selection survives the toggle.\n * - Infinite scroll (load more) does NOT change any segment → selection persists.\n *\n *
|
|
1
|
+
{"version":3,"file":"listQueryKey.mjs","sources":["../../../../../../admin/src/future/pages/Assets/utils/listQueryKey.ts"],"sourcesContent":["/**\n * Fingerprint for \"which asset list the user is looking at\".\n *\n * Selection clears when this key changes (see ClearSelectionOnChange in AssetsPage).\n *\n * Hybrid rule\n * - Folder, search, sort, filter changes → new key → selection clears.\n * - Switching table/grid view does NOT change the key — both views render the\n * same list, so the selection survives the toggle.\n * - Infinite scroll (load more) does NOT change any segment → selection persists.\n *\n * Filter uses a stable default until its header control is wired; that follow-up\n * only passes a real value — no selection logic changes needed.\n */\nexport interface ListQueryKeyInput {\n folderId: number | null;\n search: string;\n /** Composite of asset sort and folder position — both change the render order. */\n sort: string | null;\n /** Null until filter UI ships. */\n filter: string | null;\n}\n\n/**\n * Builds a stable fingerprint for the current asset list query.\n *\n * Selection clears when this key changes. Infinite scroll does not change any\n * segment, so selection persists across load-more.\n */\nexport const getListQueryKey = ({ folderId, search, sort, filter }: ListQueryKeyInput): string =>\n JSON.stringify({ folderId, search, sort, filter });\n"],"names":["getListQueryKey","folderId","search","sort","filter","JSON","stringify"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;AA4BC,IACM,MAAMA,eAAAA,GAAkB,CAAC,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,IAAI,EAAEC,MAAM,EAAqB,GACnFC,IAAAA,CAAKC,SAAS,CAAC;AAAEL,QAAAA,QAAAA;AAAUC,QAAAA,MAAAA;AAAQC,QAAAA,IAAAA;AAAMC,QAAAA;KAAO;;;;"}
|
|
@@ -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
|
|
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,
|
|
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
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
246
|
-
*
|
|
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,23 +367,31 @@ 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
|
|
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
|
},
|
|
385
|
+
// `Folder, LIST` refreshes the folder header count, which changes when
|
|
386
|
+
// files are added to it.
|
|
287
387
|
invalidatesTags: [
|
|
288
388
|
{
|
|
289
389
|
type: 'Asset',
|
|
290
390
|
id: 'LIST'
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
type: 'Folder',
|
|
394
|
+
id: 'LIST'
|
|
291
395
|
}
|
|
292
396
|
]
|
|
293
397
|
}),
|
|
@@ -319,19 +423,25 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
|
|
|
319
423
|
};
|
|
320
424
|
}
|
|
321
425
|
},
|
|
426
|
+
// `Folder, LIST` refreshes the folder header count, which changes when
|
|
427
|
+
// files are added to it.
|
|
322
428
|
invalidatesTags: [
|
|
323
429
|
{
|
|
324
430
|
type: 'Asset',
|
|
325
431
|
id: 'LIST'
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
type: 'Folder',
|
|
435
|
+
id: 'LIST'
|
|
326
436
|
}
|
|
327
437
|
]
|
|
328
438
|
}),
|
|
329
439
|
/**
|
|
330
440
|
* Retry uploading cancelled files.
|
|
331
441
|
* Maps cancelled rows back to their original entries and re-runs only those
|
|
332
|
-
* through the same
|
|
442
|
+
* through the same upload pool with a fresh AbortController.
|
|
333
443
|
*/ retryCancelledFiles: builder.mutation({
|
|
334
|
-
queryFn: async (
|
|
444
|
+
queryFn: async (_arg, { dispatch, getState })=>{
|
|
335
445
|
const { uploadId, files: stateFiles } = getState().uploadProgress;
|
|
336
446
|
const token = getState().admin_app?.token;
|
|
337
447
|
const cancelledIndices = stateFiles.filter((f)=>f.status === 'cancelled').map((f)=>f.index);
|
|
@@ -343,8 +453,8 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
|
|
|
343
453
|
}
|
|
344
454
|
};
|
|
345
455
|
}
|
|
346
|
-
const
|
|
347
|
-
if (!
|
|
456
|
+
const batch = getUploadEntries(uploadId);
|
|
457
|
+
if (!batch) {
|
|
348
458
|
return {
|
|
349
459
|
error: {
|
|
350
460
|
name: 'UnknownError',
|
|
@@ -357,19 +467,28 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
|
|
|
357
467
|
// Fresh AbortController for the retry run
|
|
358
468
|
const abortController = new AbortController();
|
|
359
469
|
registerAbortController(uploadId, abortController);
|
|
360
|
-
return
|
|
361
|
-
entries,
|
|
470
|
+
return runUploadPool({
|
|
471
|
+
entries: batch.entries,
|
|
362
472
|
indices: cancelledIndices,
|
|
363
473
|
token,
|
|
364
474
|
uploadId,
|
|
365
475
|
abortController,
|
|
366
|
-
dispatch
|
|
476
|
+
dispatch,
|
|
477
|
+
// Replay the retried rows with the flags the batch was started with.
|
|
478
|
+
concurrency: batch.concurrency,
|
|
479
|
+
generateAiMetadata: batch.generateAiMetadata
|
|
367
480
|
});
|
|
368
481
|
},
|
|
482
|
+
// `Folder, LIST` refreshes the folder header count, which changes when
|
|
483
|
+
// files are added to it.
|
|
369
484
|
invalidatesTags: [
|
|
370
485
|
{
|
|
371
486
|
type: 'Asset',
|
|
372
487
|
id: 'LIST'
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
type: 'Folder',
|
|
491
|
+
id: 'LIST'
|
|
373
492
|
}
|
|
374
493
|
]
|
|
375
494
|
}),
|
|
@@ -377,7 +496,7 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
|
|
|
377
496
|
* Upload files from URLs.
|
|
378
497
|
* Sends URLs to the server which fetches and uploads them (SSE flow, unchanged).
|
|
379
498
|
*/ uploadFromUrls: builder.mutation({
|
|
380
|
-
queryFn: async ({ urls, folderId }, { dispatch, getState })=>{
|
|
499
|
+
queryFn: async ({ urls, folderId, generateAiMetadata }, { dispatch, getState })=>{
|
|
381
500
|
const token = getState().admin_app?.token;
|
|
382
501
|
// Extract filenames from URLs for the progress dialog
|
|
383
502
|
const fileNames = urls.map((url)=>files.getFilenameFromUrl(url));
|
|
@@ -426,7 +545,9 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
|
|
|
426
545
|
// Process SSE stream from server
|
|
427
546
|
const streamResult = await processSSEStream({
|
|
428
547
|
response,
|
|
429
|
-
dispatch
|
|
548
|
+
dispatch,
|
|
549
|
+
uploadId,
|
|
550
|
+
generateAiMetadata
|
|
430
551
|
});
|
|
431
552
|
unregisterAbortController(uploadId);
|
|
432
553
|
if (streamResult && streamResult.data.length > 0) {
|
|
@@ -462,19 +583,57 @@ const uploadApi = strapiAdmin.adminApi.enhanceEndpoints({
|
|
|
462
583
|
};
|
|
463
584
|
}
|
|
464
585
|
},
|
|
586
|
+
// `Folder, LIST` refreshes the folder header count, which changes when
|
|
587
|
+
// files are added to it.
|
|
465
588
|
invalidatesTags: [
|
|
466
589
|
{
|
|
467
590
|
type: 'Asset',
|
|
468
591
|
id: 'LIST'
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
type: 'Folder',
|
|
595
|
+
id: 'LIST'
|
|
469
596
|
}
|
|
470
597
|
]
|
|
598
|
+
}),
|
|
599
|
+
/**
|
|
600
|
+
* Generate AI metadata (alt text + caption) for the given assets.
|
|
601
|
+
* Synchronous: resolves once every file has been processed and reports the
|
|
602
|
+
* outcome per file, so non-images and individual failures don't fail the
|
|
603
|
+
* whole batch. Existing alt text and captions are never overwritten.
|
|
604
|
+
*
|
|
605
|
+
* Lives here rather than in `assets.ts` because the upload flows dispatch it
|
|
606
|
+
* directly via `initiate()` after each file completes, and `assets.ts` already
|
|
607
|
+
* imports from this module — defining it there would be a circular import.
|
|
608
|
+
* Re-exported from `assets.ts` for the components that consume the hook.
|
|
609
|
+
*/ generateAiMetadata: builder.mutation({
|
|
610
|
+
query: ({ fileIds })=>({
|
|
611
|
+
url: '/upload/unstable/generate-ai-metadata',
|
|
612
|
+
method: 'POST',
|
|
613
|
+
data: {
|
|
614
|
+
fileIds
|
|
615
|
+
}
|
|
616
|
+
}),
|
|
617
|
+
transformResponse: (response)=>response.data,
|
|
618
|
+
invalidatesTags: (_result, _error, { fileIds })=>[
|
|
619
|
+
...fileIds.map((id)=>({
|
|
620
|
+
type: 'Asset',
|
|
621
|
+
id
|
|
622
|
+
})),
|
|
623
|
+
{
|
|
624
|
+
type: 'Asset',
|
|
625
|
+
id: 'LIST'
|
|
626
|
+
}
|
|
627
|
+
]
|
|
471
628
|
})
|
|
472
629
|
})
|
|
473
630
|
});
|
|
474
|
-
const { useUploadFilesMutation, useUploadFileSilentlyMutation, useRetryCancelledFilesMutation, useUploadFromUrlsMutation } = uploadApi;
|
|
631
|
+
const { useUploadFilesMutation, useUploadFileSilentlyMutation, useRetryCancelledFilesMutation, useUploadFromUrlsMutation, useGenerateAiMetadataMutation } = uploadApi;
|
|
475
632
|
|
|
476
633
|
exports.abortUpload = abortUpload;
|
|
634
|
+
exports.maybeGenerateMetadata = maybeGenerateMetadata;
|
|
477
635
|
exports.uploadApi = uploadApi;
|
|
636
|
+
exports.useGenerateAiMetadataMutation = useGenerateAiMetadataMutation;
|
|
478
637
|
exports.useRetryCancelledFilesMutation = useRetryCancelledFilesMutation;
|
|
479
638
|
exports.useUploadFileSilentlyMutation = useUploadFileSilentlyMutation;
|
|
480
639
|
exports.useUploadFilesMutation = useUploadFilesMutation;
|