@strapi/upload 5.51.0 → 5.51.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/components/Drawer.js +3 -1
- package/dist/admin/future/components/Drawer.js.map +1 -1
- package/dist/admin/future/components/Drawer.mjs +3 -1
- 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/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 +344 -150
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +343 -149
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js +75 -32
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +75 -32
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetCropEditor.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +46 -19
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +47 -20
- package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +161 -19
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +162 -20
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +100 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +98 -0
- package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +218 -37
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +219 -38
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +370 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +368 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +209 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +207 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.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/Dnd/AssetsDndProvider.js +129 -37
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +131 -39
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js +120 -13
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs +120 -13
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +71 -0
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +69 -0
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.js +34 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.mjs +32 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.js +15 -0
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.mjs +13 -1
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +19 -25
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +19 -25
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.js +35 -0
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.mjs +33 -0
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.mjs.map +1 -0
- 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 +408 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs +406 -0
- package/dist/admin/future/pages/Assets/components/FilterBadges.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.js +273 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +267 -0
- package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +195 -40
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +198 -43
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js +33 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs +31 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/filterFolderTree.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.js +22 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.mjs +19 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.js +154 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs +152 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -0
- 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 +75 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +71 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.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 +40 -4
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.js.map +1 -1
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs +40 -4
- package/dist/admin/future/pages/Assets/hooks/useInfiniteAssets.mjs.map +1 -1
- 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/hooks/useListSort.js +124 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.mjs +120 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.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 +29 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs +27 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.js +84 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.mjs +81 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/selection.js +104 -0
- package/dist/admin/future/pages/Assets/utils/selection.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/selection.mjs +94 -0
- package/dist/admin/future/pages/Assets/utils/selection.mjs.map +1 -0
- package/dist/admin/future/services/api.js +163 -28
- package/dist/admin/future/services/api.js.map +1 -1
- package/dist/admin/future/services/api.mjs +163 -30
- package/dist/admin/future/services/api.mjs.map +1 -1
- package/dist/admin/future/services/assets.js +62 -18
- package/dist/admin/future/services/assets.js.map +1 -1
- package/dist/admin/future/services/assets.mjs +62 -19
- package/dist/admin/future/services/assets.mjs.map +1 -1
- package/dist/admin/future/services/folders.js +31 -19
- package/dist/admin/future/services/folders.js.map +1 -1
- package/dist/admin/future/services/folders.mjs +31 -19
- 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 +33 -0
- package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -0
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs +31 -0
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -0
- package/dist/admin/future/utils/canDropItemOnFolder.js +4 -0
- package/dist/admin/future/utils/canDropItemOnFolder.js.map +1 -1
- package/dist/admin/future/utils/canDropItemOnFolder.mjs +4 -0
- package/dist/admin/future/utils/canDropItemOnFolder.mjs.map +1 -1
- package/dist/admin/future/utils/flattenFolderStructure.js +28 -0
- package/dist/admin/future/utils/flattenFolderStructure.js.map +1 -0
- package/dist/admin/future/utils/flattenFolderStructure.mjs +26 -0
- package/dist/admin/future/utils/flattenFolderStructure.mjs.map +1 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.js +19 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +17 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -0
- package/dist/admin/future/utils/getFolderLabel.js +31 -0
- package/dist/admin/future/utils/getFolderLabel.js.map +1 -0
- package/dist/admin/future/utils/getFolderLabel.mjs +29 -0
- package/dist/admin/future/utils/getFolderLabel.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/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/hooks/useMediaLibraryPermissions.d.ts +21 -0
- package/dist/admin/src/future/pages/Assets/components/AssetDetails/AssetCropEditor.d.ts +7 -1
- package/dist/admin/src/future/pages/Assets/components/AssetsSearchInput.d.ts +5 -0
- package/dist/admin/src/future/pages/Assets/components/AssetsTable.d.ts +7 -1
- package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +14 -0
- package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +13 -0
- package/dist/admin/src/future/pages/Assets/components/DateRangeCalendar.d.ts +8 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +2 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/DragOverlayChip.d.ts +2 -2
- package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +17 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/computeValidDropTargets.d.ts +7 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/dndIds.d.ts +3 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +3 -1
- package/dist/admin/src/future/pages/Assets/components/Dnd/useFolderTreeDroppable.d.ts +19 -0
- 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 +6 -0
- package/dist/admin/src/future/pages/Assets/components/FilterMenu.d.ts +22 -0
- package/dist/admin/src/future/pages/Assets/components/FolderTree/FolderTree.d.ts +13 -4
- package/dist/admin/src/future/pages/Assets/components/FolderTree/filterFolderTree.d.ts +16 -0
- package/dist/admin/src/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.d.ts +6 -0
- package/dist/admin/src/future/pages/Assets/components/SortMenu.d.ts +23 -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 +46 -0
- package/dist/admin/src/future/pages/Assets/hooks/useInfiniteAssets.d.ts +13 -3
- package/dist/admin/src/future/pages/Assets/hooks/useListFilters.d.ts +69 -0
- package/dist/admin/src/future/pages/Assets/hooks/useListSort.d.ts +47 -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 +29 -0
- package/dist/admin/src/future/pages/Assets/utils/mergeMixedList.d.ts +40 -0
- package/dist/admin/src/future/pages/Assets/utils/selection.d.ts +50 -0
- package/dist/admin/src/future/services/api.d.ts +53 -5
- package/dist/admin/src/future/services/assets.d.ts +13 -2
- package/dist/admin/src/future/services/folders.d.ts +7 -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/types/dnd.d.ts +5 -0
- package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +11 -0
- package/dist/admin/src/future/utils/canDropItemOnFolder.d.ts +1 -1
- package/dist/admin/src/future/utils/flattenFolderStructure.d.ts +18 -0
- package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +14 -0
- package/dist/admin/src/future/utils/getFolderLabel.d.ts +11 -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 +85 -2
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +85 -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/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/folders.d.ts +1 -1
- package/dist/shared/contracts/settings.d.ts +7 -0
- package/package.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploadProgress.mjs","sources":["../../../../admin/src/future/store/uploadProgress.ts"],"sourcesContent":["import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit';\n\nimport type { File } from '../../../../shared/contracts/files';\n\nexport interface FileUploadError {\n name: string;\n message: string;\n}\n\nexport type FileProgressStatus = 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n\nexport interface FileProgress {\n name: string;\n index: number;\n status: FileProgressStatus;\n size: number;\n uploadedBytes: number;\n file?: File;\n error?: string;\n}\n\nexport interface UploadProgressState {\n isVisible: boolean;\n isMinimized: boolean;\n totalFiles: number;\n files: FileProgress[];\n errors: FileUploadError[];\n uploadId: number;\n}\n\nexport interface RootState {\n uploadProgress: UploadProgressState;\n}\n\nconst initialState: UploadProgressState = {\n isVisible: false,\n isMinimized: false,\n totalFiles: 0,\n files: [],\n errors: [],\n uploadId: 0,\n};\n\nconst uploadProgressSlice = createSlice({\n name: 'uploadProgress',\n initialState,\n reducers: {\n openUploadProgress(\n state,\n action: PayloadAction<{\n totalFiles: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n state.isVisible = true;\n state.isMinimized = false;\n\n // Create pending files for upload\n const pendingFiles: FileProgress[] = action.payload.fileNames.map((name, index) => ({\n name,\n index,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[index] ?? 0,\n uploadedBytes: 0,\n }));\n\n state.files = pendingFiles;\n state.totalFiles = action.payload.totalFiles;\n state.errors = [];\n state.uploadId += 1;\n },\n setFileUploading(state, action: PayloadAction<{ name: string; index: number; size: number }>) {\n const { index, size } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'uploading';\n state.files[index].size = size;\n }\n },\n setFileProgress(state, action: PayloadAction<{ index: number; bytes: number }>) {\n const { index, bytes } = action.payload;\n const file = state.files[index];\n if (file) {\n // Clamp to the known file size so the aggregate can never exceed 100%.\n file.uploadedBytes = Math.min(bytes, file.size);\n }\n },\n setFileComplete(state, action: PayloadAction<{ index: number; file: File }>) {\n const { index, file } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'complete';\n state.files[index].file = file;\n // Reflect completion in the aggregate even if the final progress event was throttled.\n state.files[index].uploadedBytes = state.files[index].size;\n }\n },\n setFileError(state, action: PayloadAction<{ index: number; name: string; message: string }>) {\n const { index, name, message } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'error';\n state.files[index].error = message;\n }\n state.errors = [...state.errors, { name, message }];\n },\n addUploadErrors(state, action: PayloadAction<FileUploadError[]>) {\n state.errors = [...state.errors, ...action.payload];\n },\n closeUploadProgress(state) {\n state.isVisible = false;\n state.isMinimized = false;\n state.totalFiles = 0;\n state.files = [];\n state.errors = [];\n },\n toggleMinimize(state) {\n state.isMinimized = !state.isMinimized;\n },\n cancelUpload(state) {\n // Mark all pending and uploading files as cancelled\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return { ...file, status: 'cancelled' as FileProgressStatus };\n }\n return file;\n });\n },\n setUploadFailed(state, action: PayloadAction<{ message: string }>) {\n // Mark all pending and uploading files as errored when a catastrophic failure occurs\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return {\n ...file,\n status: 'error' as FileProgressStatus,\n error: action.payload.message,\n };\n }\n return file;\n });\n state.errors = [...state.errors, { name: 'Upload Error', message: action.payload.message }];\n },\n retryCancelledFiles(state) {\n // Reset all cancelled files back to pending for retry\n state.files = state.files.map((file) => {\n if (file.status === 'cancelled') {\n return {\n ...file,\n status: 'pending' as FileProgressStatus,\n uploadedBytes: 0,\n };\n }\n return file;\n });\n },\n },\n});\n\n/**\n * Byte-weighted aggregate progress across the whole batch: `sum(uploadedBytes) / sum(size)`.\n *\n * Falls back to count-based progress (settled files / total files) when all sizes are\n * zero — e.g. URL-flow rows where the file size is unknown up front.\n */\nexport const selectAggregateProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number => {\n if (files.length === 0) return 0;\n\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\n\n if (totalSize === 0) {\n const settled = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n return Math.round((settled / files.length) * 100);\n }\n\n const uploadedBytes = files.reduce((sum, f) => sum + f.uploadedBytes, 0);\n return Math.round((uploadedBytes / totalSize) * 100);\n }\n);\n\nexport const {\n openUploadProgress,\n setFileUploading,\n setFileProgress,\n setFileComplete,\n setFileError,\n addUploadErrors,\n closeUploadProgress,\n toggleMinimize,\n cancelUpload,\n setUploadFailed,\n retryCancelledFiles,\n} = uploadProgressSlice.actions;\n\nexport const uploadProgressReducer = uploadProgressSlice.reducer;\n"],"names":["initialState","isVisible","isMinimized","totalFiles","files","errors","uploadId","uploadProgressSlice","createSlice","name","reducers","openUploadProgress","state","action","pendingFiles","payload","fileNames","map","index","status","size","fileSizes","uploadedBytes","setFileUploading","setFileProgress","bytes","file","Math","min","setFileComplete","setFileError","message","error","addUploadErrors","closeUploadProgress","toggleMinimize","cancelUpload","setUploadFailed","retryCancelledFiles","selectAggregateProgress","createSelector","uploadProgress","length","totalSize","reduce","sum","f","settled","filter","round","actions","uploadProgressReducer","reducer"],"mappings":";;AAkCA,MAAMA,YAAAA,GAAoC;IACxCC,SAAAA,EAAW,KAAA;IACXC,WAAAA,EAAa,KAAA;IACbC,UAAAA,EAAY,CAAA;AACZC,IAAAA,KAAAA,EAAO,EAAE;AACTC,IAAAA,MAAAA,EAAQ,EAAE;IACVC,QAAAA,EAAU;AACZ,CAAA;AAEA,MAAMC,sBAAsBC,WAAAA,CAAY;IACtCC,IAAAA,EAAM,gBAAA;AACNT,IAAAA,YAAAA;IACAU,QAAAA,EAAU;QACRC,kBAAAA,CAAAA,CACEC,KAAK,EACLC,MAIE,EAAA;AAEFD,YAAAA,KAAAA,CAAMX,SAAS,GAAG,IAAA;AAClBW,YAAAA,KAAAA,CAAMV,WAAW,GAAG,KAAA;;YAGpB,MAAMY,YAAAA,GAA+BD,MAAAA,CAAOE,OAAO,CAACC,SAAS,CAACC,GAAG,CAAC,CAACR,IAAAA,EAAMS,KAAAA,IAAW;AAClFT,oBAAAA,IAAAA;AACAS,oBAAAA,KAAAA;oBACAC,MAAAA,EAAQ,SAAA;AACRC,oBAAAA,IAAAA,EAAMP,OAAOE,OAAO,CAACM,SAAS,GAAGH,MAAM,IAAI,CAAA;oBAC3CI,aAAAA,EAAe;iBACjB,CAAA,CAAA;AAEAV,YAAAA,KAAAA,CAAMR,KAAK,GAAGU,YAAAA;AACdF,YAAAA,KAAAA,CAAMT,UAAU,GAAGU,MAAAA,CAAOE,OAAO,CAACZ,UAAU;YAC5CS,KAAAA,CAAMP,MAAM,GAAG,EAAE;AACjBO,YAAAA,KAAAA,CAAMN,QAAQ,IAAI,CAAA;AACpB,QAAA,CAAA;QACAiB,gBAAAA,CAAAA,CAAiBX,KAAK,EAAEC,MAAoE,EAAA;AAC1F,YAAA,MAAM,EAAEK,KAAK,EAAEE,IAAI,EAAE,GAAGP,OAAOE,OAAO;AACtC,YAAA,IAAIH,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,EAAE;AACtBN,gBAAAA,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACC,MAAM,GAAG,WAAA;AAC5BP,gBAAAA,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACE,IAAI,GAAGA,IAAAA;AAC5B,YAAA;AACF,QAAA,CAAA;QACAI,eAAAA,CAAAA,CAAgBZ,KAAK,EAAEC,MAAuD,EAAA;AAC5E,YAAA,MAAM,EAAEK,KAAK,EAAEO,KAAK,EAAE,GAAGZ,OAAOE,OAAO;AACvC,YAAA,MAAMW,IAAAA,GAAOd,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM;AAC/B,YAAA,IAAIQ,IAAAA,EAAM;;AAERA,gBAAAA,IAAAA,CAAKJ,aAAa,GAAGK,IAAAA,CAAKC,GAAG,CAACH,KAAAA,EAAOC,KAAKN,IAAI,CAAA;AAChD,YAAA;AACF,QAAA,CAAA;QACAS,eAAAA,CAAAA,CAAgBjB,KAAK,EAAEC,MAAoD,EAAA;AACzE,YAAA,MAAM,EAAEK,KAAK,EAAEQ,IAAI,EAAE,GAAGb,OAAOE,OAAO;AACtC,YAAA,IAAIH,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,EAAE;AACtBN,gBAAAA,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACC,MAAM,GAAG,UAAA;AAC5BP,gBAAAA,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACQ,IAAI,GAAGA,IAAAA;;gBAE1Bd,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACI,aAAa,GAAGV,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACE,IAAI;AAC5D,YAAA;AACF,QAAA,CAAA;QACAU,YAAAA,CAAAA,CAAalB,KAAK,EAAEC,MAAuE,EAAA;YACzF,MAAM,EAAEK,KAAK,EAAET,IAAI,EAAEsB,OAAO,EAAE,GAAGlB,MAAAA,CAAOE,OAAO;AAC/C,YAAA,IAAIH,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,EAAE;AACtBN,gBAAAA,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACC,MAAM,GAAG,OAAA;AAC5BP,gBAAAA,KAAAA,CAAMR,KAAK,CAACc,KAAAA,CAAM,CAACc,KAAK,GAAGD,OAAAA;AAC7B,YAAA;AACAnB,YAAAA,KAAAA,CAAMP,MAAM,GAAG;AAAIO,gBAAAA,GAAAA,KAAAA,CAAMP,MAAM;AAAE,gBAAA;AAAEI,oBAAAA,IAAAA;AAAMsB,oBAAAA;AAAQ;AAAE,aAAA;AACrD,QAAA,CAAA;QACAE,eAAAA,CAAAA,CAAgBrB,KAAK,EAAEC,MAAwC,EAAA;AAC7DD,YAAAA,KAAAA,CAAMP,MAAM,GAAG;AAAIO,gBAAAA,GAAAA,KAAAA,CAAMP,MAAM;AAAKQ,gBAAAA,GAAAA,MAAAA,CAAOE;AAAQ,aAAA;AACrD,QAAA,CAAA;AACAmB,QAAAA,mBAAAA,CAAAA,CAAoBtB,KAAK,EAAA;AACvBA,YAAAA,KAAAA,CAAMX,SAAS,GAAG,KAAA;AAClBW,YAAAA,KAAAA,CAAMV,WAAW,GAAG,KAAA;AACpBU,YAAAA,KAAAA,CAAMT,UAAU,GAAG,CAAA;YACnBS,KAAAA,CAAMR,KAAK,GAAG,EAAE;YAChBQ,KAAAA,CAAMP,MAAM,GAAG,EAAE;AACnB,QAAA,CAAA;AACA8B,QAAAA,cAAAA,CAAAA,CAAevB,KAAK,EAAA;AAClBA,YAAAA,KAAAA,CAAMV,WAAW,GAAG,CAACU,KAAAA,CAAMV,WAAW;AACxC,QAAA,CAAA;AACAkC,QAAAA,YAAAA,CAAAA,CAAaxB,KAAK,EAAA;;AAEhBA,YAAAA,KAAAA,CAAMR,KAAK,GAAGQ,KAAAA,CAAMR,KAAK,CAACa,GAAG,CAAC,CAACS,IAAAA,GAAAA;AAC7B,gBAAA,IAAIA,KAAKP,MAAM,KAAK,aAAaO,IAAAA,CAAKP,MAAM,KAAK,WAAA,EAAa;oBAC5D,OAAO;AAAE,wBAAA,GAAGO,IAAI;wBAAEP,MAAAA,EAAQ;AAAkC,qBAAA;AAC9D,gBAAA;gBACA,OAAOO,IAAAA;AACT,YAAA,CAAA,CAAA;AACF,QAAA,CAAA;QACAW,eAAAA,CAAAA,CAAgBzB,KAAK,EAAEC,MAA0C,EAAA;;AAE/DD,YAAAA,KAAAA,CAAMR,KAAK,GAAGQ,KAAAA,CAAMR,KAAK,CAACa,GAAG,CAAC,CAACS,IAAAA,GAAAA;AAC7B,gBAAA,IAAIA,KAAKP,MAAM,KAAK,aAAaO,IAAAA,CAAKP,MAAM,KAAK,WAAA,EAAa;oBAC5D,OAAO;AACL,wBAAA,GAAGO,IAAI;wBACPP,MAAAA,EAAQ,OAAA;wBACRa,KAAAA,EAAOnB,MAAAA,CAAOE,OAAO,CAACgB;AACxB,qBAAA;AACF,gBAAA;gBACA,OAAOL,IAAAA;AACT,YAAA,CAAA,CAAA;AACAd,YAAAA,KAAAA,CAAMP,MAAM,GAAG;AAAIO,gBAAAA,GAAAA,KAAAA,CAAMP,MAAM;AAAE,gBAAA;oBAAEI,IAAAA,EAAM,cAAA;oBAAgBsB,OAAAA,EAASlB,MAAAA,CAAOE,OAAO,CAACgB;AAAQ;AAAE,aAAA;AAC7F,QAAA,CAAA;AACAO,QAAAA,mBAAAA,CAAAA,CAAoB1B,KAAK,EAAA;;AAEvBA,YAAAA,KAAAA,CAAMR,KAAK,GAAGQ,KAAAA,CAAMR,KAAK,CAACa,GAAG,CAAC,CAACS,IAAAA,GAAAA;gBAC7B,IAAIA,IAAAA,CAAKP,MAAM,KAAK,WAAA,EAAa;oBAC/B,OAAO;AACL,wBAAA,GAAGO,IAAI;wBACPP,MAAAA,EAAQ,SAAA;wBACRG,aAAAA,EAAe;AACjB,qBAAA;AACF,gBAAA;gBACA,OAAOI,IAAAA;AACT,YAAA,CAAA,CAAA;AACF,QAAA;AACF;AACF,CAAA,CAAA;AAEA;;;;;IAMO,MAAMa,uBAAAA,GAA0BC,cAAAA,CACrC,CAAC5B,KAAAA,GAAqBA,KAAAA,CAAM6B,cAAc,CAACrC,KAAK,EAChD,CAACA,KAAAA,GAAAA;AACC,IAAA,IAAIA,KAAAA,CAAMsC,MAAM,KAAK,CAAA,EAAG,OAAO,CAAA;IAE/B,MAAMC,SAAAA,GAAYvC,KAAAA,CAAMwC,MAAM,CAAC,CAACC,KAAKC,CAAAA,GAAMD,GAAAA,GAAMC,CAAAA,CAAE1B,IAAI,EAAE,CAAA,CAAA;AAEzD,IAAA,IAAIuB,cAAc,CAAA,EAAG;AACnB,QAAA,MAAMI,UAAU3C,KAAAA,CAAM4C,MAAM,CAC1B,CAACF,CAAAA,GAAMA,EAAE3B,MAAM,KAAK,UAAA,IAAc2B,CAAAA,CAAE3B,MAAM,KAAK,OAAA,IAAW2B,EAAE3B,MAAM,KAAK,aACvEuB,MAAM;AACR,QAAA,OAAOf,KAAKsB,KAAK,CAAC,OAACF,GAAU3C,KAAAA,CAAMsC,MAAM,GAAI,GAAA,CAAA;AAC/C,IAAA;IAEA,MAAMpB,aAAAA,GAAgBlB,KAAAA,CAAMwC,MAAM,CAAC,CAACC,KAAKC,CAAAA,GAAMD,GAAAA,GAAMC,CAAAA,CAAExB,aAAa,EAAE,CAAA,CAAA;AACtE,IAAA,OAAOK,IAAAA,CAAKsB,KAAK,CAAE3B,gBAAgBqB,SAAAA,GAAa,GAAA,CAAA;AAClD,CAAA;AAGK,MAAM,EACXhC,kBAAkB,EAClBY,gBAAgB,EAChBC,eAAe,EACfK,eAAe,EACfC,YAAY,EACZG,eAAe,EACfC,mBAAmB,EACnBC,cAAc,EACdC,YAAY,EACZC,eAAe,EACfC,mBAAmB,EACpB,GAAG/B,mBAAAA,CAAoB2C;AAEjB,MAAMC,qBAAAA,GAAwB5C,mBAAAA,CAAoB6C;;;;"}
|
|
1
|
+
{"version":3,"file":"uploadProgress.mjs","sources":["../../../../admin/src/future/store/uploadProgress.ts"],"sourcesContent":["import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit';\n\nimport type { File } from '../../../../shared/contracts/files';\n\nexport interface FileUploadError {\n name: string;\n message: string;\n}\n\nexport type FileProgressStatus = 'pending' | 'uploading' | 'complete' | 'error' | 'cancelled';\n\n/**\n * AI metadata generation phase for a single row, tracked independently from the\n * upload status: a metadata failure never turns a successful upload into an error.\n * `undefined` means the row has no metadata phase at all, i.e. AI metadata is\n * disabled — non-images do get a phase and come back `skipped` from the server.\n */\nexport type FileMetadataStatus = 'generating' | 'generated' | 'skipped' | 'failed';\n\nexport type FileMetadataResultStatus = Exclude<FileMetadataStatus, 'generating'>;\n\nexport interface FileProgress {\n name: string;\n index: number;\n status: FileProgressStatus;\n size: number;\n uploadedBytes: number;\n file?: File;\n error?: string;\n metadataStatus?: FileMetadataStatus;\n}\n\nexport interface UploadProgressState {\n isVisible: boolean;\n isMinimized: boolean;\n totalFiles: number;\n files: FileProgress[];\n errors: FileUploadError[];\n uploadId: number;\n}\n\nexport interface RootState {\n uploadProgress: UploadProgressState;\n}\n\nconst initialState: UploadProgressState = {\n isVisible: false,\n isMinimized: false,\n totalFiles: 0,\n files: [],\n errors: [],\n uploadId: 0,\n};\n\nconst uploadProgressSlice = createSlice({\n name: 'uploadProgress',\n initialState,\n reducers: {\n openUploadProgress(\n state,\n action: PayloadAction<{\n totalFiles: number;\n fileNames: string[];\n fileSizes?: number[];\n }>\n ) {\n state.isVisible = true;\n state.isMinimized = false;\n\n // Create pending files for upload\n const pendingFiles: FileProgress[] = action.payload.fileNames.map((name, index) => ({\n name,\n index,\n status: 'pending' as FileProgressStatus,\n size: action.payload.fileSizes?.[index] ?? 0,\n uploadedBytes: 0,\n }));\n\n state.files = pendingFiles;\n state.totalFiles = action.payload.totalFiles;\n state.errors = [];\n state.uploadId += 1;\n },\n setFileUploading(state, action: PayloadAction<{ name: string; index: number; size: number }>) {\n const { index, size } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'uploading';\n state.files[index].size = size;\n }\n },\n setFileProgress(state, action: PayloadAction<{ index: number; bytes: number }>) {\n const { index, bytes } = action.payload;\n const file = state.files[index];\n if (file) {\n // Clamp to the known file size so the aggregate can never exceed 100%.\n file.uploadedBytes = Math.min(bytes, file.size);\n }\n },\n setFileComplete(state, action: PayloadAction<{ index: number; file: File }>) {\n const { index, file } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'complete';\n state.files[index].file = file;\n // Reflect completion in the aggregate even if the final progress event was throttled.\n state.files[index].uploadedBytes = state.files[index].size;\n }\n },\n setFileError(state, action: PayloadAction<{ index: number; name: string; message: string }>) {\n const { index, name, message } = action.payload;\n if (state.files[index]) {\n state.files[index].status = 'error';\n state.files[index].error = message;\n }\n state.errors = [...state.errors, { name, message }];\n },\n /**\n * Metadata generation started for a row.\n *\n * Metadata requests are fired-and-forgotten, so their callbacks can land after\n * a new batch has replaced `files` and reused the same row indices. Every\n * metadata payload therefore carries the `uploadId` it was fired for, and stale\n * ones are dropped instead of writing onto another batch's row.\n */\n setFileMetadataGenerating(state, action: PayloadAction<{ index: number; uploadId: number }>) {\n const { index, uploadId } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].metadataStatus = 'generating';\n }\n },\n /**\n * Terminal metadata outcome for a row. Never touches `status`/`error`/`errors`:\n * the upload itself already succeeded regardless of how generation went.\n */\n setFileMetadataResult(\n state,\n action: PayloadAction<{\n index: number;\n uploadId: number;\n status: FileMetadataResultStatus;\n }>\n ) {\n const { index, uploadId, status } = action.payload;\n if (uploadId !== state.uploadId) {\n return;\n }\n if (state.files[index]) {\n state.files[index].metadataStatus = status;\n }\n },\n addUploadErrors(state, action: PayloadAction<FileUploadError[]>) {\n state.errors = [...state.errors, ...action.payload];\n },\n closeUploadProgress(state) {\n state.isVisible = false;\n state.isMinimized = false;\n state.totalFiles = 0;\n state.files = [];\n state.errors = [];\n },\n toggleMinimize(state) {\n state.isMinimized = !state.isMinimized;\n },\n cancelUpload(state) {\n // Mark all pending and uploading files as cancelled\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return { ...file, status: 'cancelled' as FileProgressStatus };\n }\n return file;\n });\n },\n setUploadFailed(state, action: PayloadAction<{ message: string }>) {\n // Mark all pending and uploading files as errored when a catastrophic failure occurs\n state.files = state.files.map((file) => {\n if (file.status === 'pending' || file.status === 'uploading') {\n return {\n ...file,\n status: 'error' as FileProgressStatus,\n error: action.payload.message,\n };\n }\n return file;\n });\n state.errors = [...state.errors, { name: 'Upload Error', message: action.payload.message }];\n },\n retryCancelledFiles(state) {\n // Reset all cancelled files back to pending for retry\n state.files = state.files.map((file) => {\n if (file.status === 'cancelled') {\n return {\n ...file,\n status: 'pending' as FileProgressStatus,\n uploadedBytes: 0,\n // A retried row goes through the metadata phase again from scratch.\n metadataStatus: undefined,\n };\n }\n return file;\n });\n },\n },\n});\n\n/**\n * Byte-weighted aggregate progress across the whole batch: `sum(uploadedBytes) / sum(size)`.\n *\n * Falls back to count-based progress (settled files / total files) when all sizes are\n * zero — e.g. URL-flow rows where the file size is unknown up front.\n */\nexport const selectAggregateProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number => {\n if (files.length === 0) return 0;\n\n const totalSize = files.reduce((sum, f) => sum + f.size, 0);\n\n if (totalSize === 0) {\n const settled = files.filter(\n (f) => f.status === 'complete' || f.status === 'error' || f.status === 'cancelled'\n ).length;\n return Math.round((settled / files.length) * 100);\n }\n\n const uploadedBytes = files.reduce((sum, f) => sum + f.uploadedBytes, 0);\n return Math.round((uploadedBytes / totalSize) * 100);\n }\n);\n\n/**\n * Count-based metadata progress across the whole batch: `settled / expected`.\n *\n * Count-based rather than byte-weighted because the generation endpoint answers in one\n * go — there is no intermediate progress to weight.\n *\n * The denominator is every row *expected* to enter the phase, not just those that\n * already have. Generation is fired per file as each upload finishes, so counting only\n * started rows made the denominator grow mid-batch and the percentage jump backwards\n * (1/1 = 100%, then a second row starts and it drops to 1/2 = 50%), which flickered the\n * header subtitle on and off once per file.\n *\n * \"Expected\" is derived from upload outcome rather than from the AI-metadata flag:\n * - a row still uploading may yet enter the phase, so it counts toward the total;\n * - an errored or cancelled row never will, so it must not — otherwise a batch with\n * one failure could never reach 100%.\n *\n * Returns `null` when nothing has entered the phase and nothing is pending — i.e. AI\n * metadata is off, or the batch is done — so the header can hide the subtitle.\n */\nexport const selectMetadataProgress = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): number | null => {\n const started = files.filter((f) => f.metadataStatus !== undefined);\n\n // Nothing has entered the phase yet: with no started row there is no evidence AI\n // metadata is even enabled, so stay silent rather than showing a premature 0%.\n if (started.length === 0) return null;\n\n // Rows whose upload is still in flight are counted in the denominator so the\n // percentage only ever climbs as the batch progresses.\n const pending = files.filter(\n (f) => f.metadataStatus === undefined && (f.status === 'pending' || f.status === 'uploading')\n ).length;\n\n const expected = started.length + pending;\n const settled = started.filter((f) => f.metadataStatus !== 'generating').length;\n\n return Math.round((settled / expected) * 100);\n }\n);\n\n/**\n * Whether the metadata phase is still ongoing for the batch as a whole.\n *\n * The header uses this — not `selectMetadataProgress === 100` — to decide when to drop\n * the subtitle. Two distinct reasons the phase can be unfinished:\n * - a row is generating right now;\n * - no row is generating this instant, but rows are still uploading and will enter the\n * phase when they finish.\n *\n * The second clause is what keeps the subtitle stable. Generation for file N typically\n * finishes before file N+1 finishes uploading, so a naive \"is anything generating?\"\n * check goes false between every pair of files and blinks the subtitle out and back in.\n */\nexport const selectIsGeneratingMetadata = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): boolean => {\n if (files.some((f) => f.metadataStatus === 'generating')) return true;\n\n // Only bridge the gap if some row has already entered the phase — otherwise a batch\n // with AI metadata disabled would report the phase as ongoing for its whole upload.\n const hasStarted = files.some((f) => f.metadataStatus !== undefined);\n\n return hasStarted && files.some((f) => f.status === 'pending' || f.status === 'uploading');\n }\n);\n\n/**\n * How many rows reached each terminal metadata outcome, for the header's completion\n * message. `null` while the phase is unfinished — or was never entered at all — so the\n * caller has a single check for \"there is no settled outcome to report yet\".\n *\n * Counts are reported separately rather than collapsed into one total because they are\n * not interchangeable: only `generated` rows actually had metadata written, so only that\n * count can be claimed as a success. A batch of non-images settles entirely on `skipped`,\n * where \"generated on 0 files\" would be worse than saying nothing.\n */\nexport const selectMetadataOutcome = createSelector(\n (state: RootState) => state.uploadProgress.files,\n (files): { generated: number; skipped: number; failed: number } | null => {\n const started = files.filter((f) => f.metadataStatus !== undefined);\n\n if (started.length === 0) return null;\n\n // Mirrors `selectIsGeneratingMetadata`: the phase isn't over while a row is\n // generating, nor while a row is still uploading and has yet to enter it.\n const isOngoing =\n started.some((f) => f.metadataStatus === 'generating') ||\n files.some((f) => f.status === 'pending' || f.status === 'uploading');\n\n if (isOngoing) return null;\n\n return {\n generated: started.filter((f) => f.metadataStatus === 'generated').length,\n skipped: started.filter((f) => f.metadataStatus === 'skipped').length,\n failed: started.filter((f) => f.metadataStatus === 'failed').length,\n };\n }\n);\n\nexport const {\n openUploadProgress,\n setFileUploading,\n setFileProgress,\n setFileComplete,\n setFileError,\n setFileMetadataGenerating,\n setFileMetadataResult,\n addUploadErrors,\n closeUploadProgress,\n toggleMinimize,\n cancelUpload,\n setUploadFailed,\n retryCancelledFiles,\n} = uploadProgressSlice.actions;\n\nexport const uploadProgressReducer = uploadProgressSlice.reducer;\n"],"names":["initialState","isVisible","isMinimized","totalFiles","files","errors","uploadId","uploadProgressSlice","createSlice","name","reducers","openUploadProgress","state","action","pendingFiles","payload","fileNames","map","index","status","size","fileSizes","uploadedBytes","setFileUploading","setFileProgress","bytes","file","Math","min","setFileComplete","setFileError","message","error","setFileMetadataGenerating","metadataStatus","setFileMetadataResult","addUploadErrors","closeUploadProgress","toggleMinimize","cancelUpload","setUploadFailed","retryCancelledFiles","undefined","selectAggregateProgress","createSelector","uploadProgress","length","totalSize","reduce","sum","f","settled","filter","round","selectMetadataProgress","started","pending","expected","selectIsGeneratingMetadata","some","hasStarted","selectMetadataOutcome","isOngoing","generated","skipped","failed","actions","uploadProgressReducer","reducer"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reconstruct the drag set from a bulk-actions selection so the dialog can feed
|
|
5
|
+
* the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical
|
|
6
|
+
* `canDropItemOnFolder` predicate that pointer drag uses.
|
|
7
|
+
*
|
|
8
|
+
* Selection is page-scoped to the current folder, so every selected item shares
|
|
9
|
+
* `currentFolderId` as its location (files → `folderId`, folders → `parentId`).
|
|
10
|
+
* Names are irrelevant to drop validation, so they are left empty.
|
|
11
|
+
*/ const buildDragSetFromSelection = (selectedIds, selectedFolderIds, currentFolderId)=>{
|
|
12
|
+
const items = [];
|
|
13
|
+
selectedIds.forEach((id)=>{
|
|
14
|
+
items.push({
|
|
15
|
+
kind: 'file',
|
|
16
|
+
id,
|
|
17
|
+
name: '',
|
|
18
|
+
folderId: currentFolderId
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
selectedFolderIds.forEach((id)=>{
|
|
22
|
+
items.push({
|
|
23
|
+
kind: 'folder',
|
|
24
|
+
id,
|
|
25
|
+
name: '',
|
|
26
|
+
parentId: currentFolderId
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
return items;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
exports.buildDragSetFromSelection = buildDragSetFromSelection;
|
|
33
|
+
//# sourceMappingURL=buildDragSetFromSelection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildDragSetFromSelection.js","sources":["../../../../admin/src/future/utils/buildDragSetFromSelection.ts"],"sourcesContent":["import type { DragItemData } from '../types/dnd';\n\n/**\n * Reconstruct the drag set from a bulk-actions selection so the dialog can feed\n * the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical\n * `canDropItemOnFolder` predicate that pointer drag uses.\n *\n * Selection is page-scoped to the current folder, so every selected item shares\n * `currentFolderId` as its location (files → `folderId`, folders → `parentId`).\n * Names are irrelevant to drop validation, so they are left empty.\n */\nexport const buildDragSetFromSelection = (\n selectedIds: ReadonlySet<number>,\n selectedFolderIds: ReadonlySet<number>,\n currentFolderId: number | null\n): DragItemData[] => {\n const items: DragItemData[] = [];\n\n selectedIds.forEach((id) => {\n items.push({ kind: 'file', id, name: '', folderId: currentFolderId });\n });\n\n selectedFolderIds.forEach((id) => {\n items.push({ kind: 'folder', id, name: '', parentId: currentFolderId });\n });\n\n return items;\n};\n"],"names":["buildDragSetFromSelection","selectedIds","selectedFolderIds","currentFolderId","items","forEach","id","push","kind","name","folderId","parentId"],"mappings":";;AAEA;;;;;;;;AAQC,IACM,MAAMA,yBAAAA,GAA4B,CACvCC,aACAC,iBAAAA,EACAC,eAAAA,GAAAA;AAEA,IAAA,MAAMC,QAAwB,EAAE;IAEhCH,WAAAA,CAAYI,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACnBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YAAEC,IAAAA,EAAM,MAAA;AAAQF,YAAAA,EAAAA;YAAIG,IAAAA,EAAM,EAAA;YAAIC,QAAAA,EAAUP;AAAgB,SAAA,CAAA;AACrE,IAAA,CAAA,CAAA;IAEAD,iBAAAA,CAAkBG,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACzBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YAAEC,IAAAA,EAAM,QAAA;AAAUF,YAAAA,EAAAA;YAAIG,IAAAA,EAAM,EAAA;YAAIE,QAAAA,EAAUR;AAAgB,SAAA,CAAA;AACvE,IAAA,CAAA,CAAA;IAEA,OAAOC,KAAAA;AACT;;;;"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reconstruct the drag set from a bulk-actions selection so the dialog can feed
|
|
3
|
+
* the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical
|
|
4
|
+
* `canDropItemOnFolder` predicate that pointer drag uses.
|
|
5
|
+
*
|
|
6
|
+
* Selection is page-scoped to the current folder, so every selected item shares
|
|
7
|
+
* `currentFolderId` as its location (files → `folderId`, folders → `parentId`).
|
|
8
|
+
* Names are irrelevant to drop validation, so they are left empty.
|
|
9
|
+
*/ const buildDragSetFromSelection = (selectedIds, selectedFolderIds, currentFolderId)=>{
|
|
10
|
+
const items = [];
|
|
11
|
+
selectedIds.forEach((id)=>{
|
|
12
|
+
items.push({
|
|
13
|
+
kind: 'file',
|
|
14
|
+
id,
|
|
15
|
+
name: '',
|
|
16
|
+
folderId: currentFolderId
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
selectedFolderIds.forEach((id)=>{
|
|
20
|
+
items.push({
|
|
21
|
+
kind: 'folder',
|
|
22
|
+
id,
|
|
23
|
+
name: '',
|
|
24
|
+
parentId: currentFolderId
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
return items;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export { buildDragSetFromSelection };
|
|
31
|
+
//# sourceMappingURL=buildDragSetFromSelection.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildDragSetFromSelection.mjs","sources":["../../../../admin/src/future/utils/buildDragSetFromSelection.ts"],"sourcesContent":["import type { DragItemData } from '../types/dnd';\n\n/**\n * Reconstruct the drag set from a bulk-actions selection so the dialog can feed\n * the exact same `DragItemData[]` into `computeValidDropTargets` / the canonical\n * `canDropItemOnFolder` predicate that pointer drag uses.\n *\n * Selection is page-scoped to the current folder, so every selected item shares\n * `currentFolderId` as its location (files → `folderId`, folders → `parentId`).\n * Names are irrelevant to drop validation, so they are left empty.\n */\nexport const buildDragSetFromSelection = (\n selectedIds: ReadonlySet<number>,\n selectedFolderIds: ReadonlySet<number>,\n currentFolderId: number | null\n): DragItemData[] => {\n const items: DragItemData[] = [];\n\n selectedIds.forEach((id) => {\n items.push({ kind: 'file', id, name: '', folderId: currentFolderId });\n });\n\n selectedFolderIds.forEach((id) => {\n items.push({ kind: 'folder', id, name: '', parentId: currentFolderId });\n });\n\n return items;\n};\n"],"names":["buildDragSetFromSelection","selectedIds","selectedFolderIds","currentFolderId","items","forEach","id","push","kind","name","folderId","parentId"],"mappings":"AAEA;;;;;;;;AAQC,IACM,MAAMA,yBAAAA,GAA4B,CACvCC,aACAC,iBAAAA,EACAC,eAAAA,GAAAA;AAEA,IAAA,MAAMC,QAAwB,EAAE;IAEhCH,WAAAA,CAAYI,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACnBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YAAEC,IAAAA,EAAM,MAAA;AAAQF,YAAAA,EAAAA;YAAIG,IAAAA,EAAM,EAAA;YAAIC,QAAAA,EAAUP;AAAgB,SAAA,CAAA;AACrE,IAAA,CAAA,CAAA;IAEAD,iBAAAA,CAAkBG,OAAO,CAAC,CAACC,EAAAA,GAAAA;AACzBF,QAAAA,KAAAA,CAAMG,IAAI,CAAC;YAAEC,IAAAA,EAAM,QAAA;AAAUF,YAAAA,EAAAA;YAAIG,IAAAA,EAAM,EAAA;YAAIE,QAAAA,EAAUR;AAAgB,SAAA,CAAA;AACvE,IAAA,CAAA,CAAA;IAEA,OAAOC,KAAAA;AACT;;;;"}
|
|
@@ -35,10 +35,14 @@ const isFolderDescendantOf = (folderStructure, ancestorId, candidateId)=>{
|
|
|
35
35
|
}
|
|
36
36
|
return collectDescendantIds(ancestorNode).has(candidateId);
|
|
37
37
|
};
|
|
38
|
+
const isItemAtRoot = (item)=>item.kind === 'file' ? item.folderId == null : item.parentId == null;
|
|
38
39
|
const canDropItemOnFolder = ({ items, targetFolderId, folderStructure })=>{
|
|
39
40
|
if (items.length === 0) {
|
|
40
41
|
return false;
|
|
41
42
|
}
|
|
43
|
+
if (targetFolderId === null) {
|
|
44
|
+
return items.some((item)=>!isItemAtRoot(item));
|
|
45
|
+
}
|
|
42
46
|
const draggedFolderIds = new Set(items.filter((item)=>item.kind === 'folder').map((item)=>item.id));
|
|
43
47
|
if (draggedFolderIds.has(targetFolderId)) {
|
|
44
48
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canDropItemOnFolder.js","sources":["../../../../admin/src/future/utils/canDropItemOnFolder.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\nimport type { DragItemData } from '../types/dnd';\n\n// TODO: permission-aware droppables — server validates in v1.\n\nexport interface CanDropItemOnFolderParams {\n items: DragItemData[];\n targetFolderId: number;\n folderStructure: FolderNode[];\n}\n\nconst findNode = (nodes: FolderNode[], id: number): FolderNode | null => {\n for (const node of nodes) {\n if (node.id === id) {\n return node;\n }\n\n const found = findNode(node.children, id);\n if (found) {\n return found;\n }\n }\n\n return null;\n};\n\nconst collectDescendantIds = (node: FolderNode): Set<number> => {\n const ids = new Set<number>();\n\n const walk = (current: FolderNode) => {\n for (const child of current.children) {\n if (child.id != null) {\n ids.add(child.id);\n }\n walk(child);\n }\n };\n\n walk(node);\n return ids;\n};\n\nexport const isFolderDescendantOf = (\n folderStructure: FolderNode[],\n ancestorId: number,\n candidateId: number\n): boolean => {\n if (ancestorId === candidateId) {\n return true;\n }\n\n const ancestorNode = findNode(folderStructure, ancestorId);\n if (!ancestorNode) {\n return false;\n }\n\n return collectDescendantIds(ancestorNode).has(candidateId);\n};\n\nexport const canDropItemOnFolder = ({\n items,\n targetFolderId,\n folderStructure,\n}: CanDropItemOnFolderParams): boolean => {\n if (items.length === 0) {\n return false;\n }\n\n const draggedFolderIds = new Set(\n items\n .filter((item): item is Extract<DragItemData, { kind: 'folder' }> => item.kind === 'folder')\n .map((item) => item.id)\n );\n\n if (draggedFolderIds.has(targetFolderId)) {\n return false;\n }\n\n for (const folderId of draggedFolderIds) {\n if (isFolderDescendantOf(folderStructure, folderId, targetFolderId)) {\n return false;\n }\n }\n\n for (const item of items) {\n if (item.kind === 'file' && item.folderId === targetFolderId) {\n return false;\n }\n\n if (item.kind === 'folder' && item.parentId === targetFolderId) {\n return false;\n }\n }\n\n return true;\n};\n"],"names":["findNode","nodes","id","node","found","children","collectDescendantIds","ids","Set","walk","current","child","add","isFolderDescendantOf","folderStructure","ancestorId","candidateId","ancestorNode","has","
|
|
1
|
+
{"version":3,"file":"canDropItemOnFolder.js","sources":["../../../../admin/src/future/utils/canDropItemOnFolder.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\nimport type { DragItemData } from '../types/dnd';\n\n// TODO: permission-aware droppables — server validates in v1.\n\nexport interface CanDropItemOnFolderParams {\n items: DragItemData[];\n targetFolderId: number | null;\n folderStructure: FolderNode[];\n}\n\nconst findNode = (nodes: FolderNode[], id: number): FolderNode | null => {\n for (const node of nodes) {\n if (node.id === id) {\n return node;\n }\n\n const found = findNode(node.children, id);\n if (found) {\n return found;\n }\n }\n\n return null;\n};\n\nconst collectDescendantIds = (node: FolderNode): Set<number> => {\n const ids = new Set<number>();\n\n const walk = (current: FolderNode) => {\n for (const child of current.children) {\n if (child.id != null) {\n ids.add(child.id);\n }\n walk(child);\n }\n };\n\n walk(node);\n return ids;\n};\n\nexport const isFolderDescendantOf = (\n folderStructure: FolderNode[],\n ancestorId: number,\n candidateId: number\n): boolean => {\n if (ancestorId === candidateId) {\n return true;\n }\n\n const ancestorNode = findNode(folderStructure, ancestorId);\n if (!ancestorNode) {\n return false;\n }\n\n return collectDescendantIds(ancestorNode).has(candidateId);\n};\n\nconst isItemAtRoot = (item: DragItemData): boolean =>\n item.kind === 'file' ? item.folderId == null : item.parentId == null;\n\nexport const canDropItemOnFolder = ({\n items,\n targetFolderId,\n folderStructure,\n}: CanDropItemOnFolderParams): boolean => {\n if (items.length === 0) {\n return false;\n }\n\n if (targetFolderId === null) {\n return items.some((item) => !isItemAtRoot(item));\n }\n\n const draggedFolderIds = new Set(\n items\n .filter((item): item is Extract<DragItemData, { kind: 'folder' }> => item.kind === 'folder')\n .map((item) => item.id)\n );\n\n if (draggedFolderIds.has(targetFolderId)) {\n return false;\n }\n\n for (const folderId of draggedFolderIds) {\n if (isFolderDescendantOf(folderStructure, folderId, targetFolderId)) {\n return false;\n }\n }\n\n for (const item of items) {\n if (item.kind === 'file' && item.folderId === targetFolderId) {\n return false;\n }\n\n if (item.kind === 'folder' && item.parentId === targetFolderId) {\n return false;\n }\n }\n\n return true;\n};\n"],"names":["findNode","nodes","id","node","found","children","collectDescendantIds","ids","Set","walk","current","child","add","isFolderDescendantOf","folderStructure","ancestorId","candidateId","ancestorNode","has","isItemAtRoot","item","kind","folderId","parentId","canDropItemOnFolder","items","targetFolderId","length","some","draggedFolderIds","filter","map"],"mappings":"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"}
|
|
@@ -33,10 +33,14 @@ const isFolderDescendantOf = (folderStructure, ancestorId, candidateId)=>{
|
|
|
33
33
|
}
|
|
34
34
|
return collectDescendantIds(ancestorNode).has(candidateId);
|
|
35
35
|
};
|
|
36
|
+
const isItemAtRoot = (item)=>item.kind === 'file' ? item.folderId == null : item.parentId == null;
|
|
36
37
|
const canDropItemOnFolder = ({ items, targetFolderId, folderStructure })=>{
|
|
37
38
|
if (items.length === 0) {
|
|
38
39
|
return false;
|
|
39
40
|
}
|
|
41
|
+
if (targetFolderId === null) {
|
|
42
|
+
return items.some((item)=>!isItemAtRoot(item));
|
|
43
|
+
}
|
|
40
44
|
const draggedFolderIds = new Set(items.filter((item)=>item.kind === 'folder').map((item)=>item.id));
|
|
41
45
|
if (draggedFolderIds.has(targetFolderId)) {
|
|
42
46
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canDropItemOnFolder.mjs","sources":["../../../../admin/src/future/utils/canDropItemOnFolder.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\nimport type { DragItemData } from '../types/dnd';\n\n// TODO: permission-aware droppables — server validates in v1.\n\nexport interface CanDropItemOnFolderParams {\n items: DragItemData[];\n targetFolderId: number;\n folderStructure: FolderNode[];\n}\n\nconst findNode = (nodes: FolderNode[], id: number): FolderNode | null => {\n for (const node of nodes) {\n if (node.id === id) {\n return node;\n }\n\n const found = findNode(node.children, id);\n if (found) {\n return found;\n }\n }\n\n return null;\n};\n\nconst collectDescendantIds = (node: FolderNode): Set<number> => {\n const ids = new Set<number>();\n\n const walk = (current: FolderNode) => {\n for (const child of current.children) {\n if (child.id != null) {\n ids.add(child.id);\n }\n walk(child);\n }\n };\n\n walk(node);\n return ids;\n};\n\nexport const isFolderDescendantOf = (\n folderStructure: FolderNode[],\n ancestorId: number,\n candidateId: number\n): boolean => {\n if (ancestorId === candidateId) {\n return true;\n }\n\n const ancestorNode = findNode(folderStructure, ancestorId);\n if (!ancestorNode) {\n return false;\n }\n\n return collectDescendantIds(ancestorNode).has(candidateId);\n};\n\nexport const canDropItemOnFolder = ({\n items,\n targetFolderId,\n folderStructure,\n}: CanDropItemOnFolderParams): boolean => {\n if (items.length === 0) {\n return false;\n }\n\n const draggedFolderIds = new Set(\n items\n .filter((item): item is Extract<DragItemData, { kind: 'folder' }> => item.kind === 'folder')\n .map((item) => item.id)\n );\n\n if (draggedFolderIds.has(targetFolderId)) {\n return false;\n }\n\n for (const folderId of draggedFolderIds) {\n if (isFolderDescendantOf(folderStructure, folderId, targetFolderId)) {\n return false;\n }\n }\n\n for (const item of items) {\n if (item.kind === 'file' && item.folderId === targetFolderId) {\n return false;\n }\n\n if (item.kind === 'folder' && item.parentId === targetFolderId) {\n return false;\n }\n }\n\n return true;\n};\n"],"names":["findNode","nodes","id","node","found","children","collectDescendantIds","ids","Set","walk","current","child","add","isFolderDescendantOf","folderStructure","ancestorId","candidateId","ancestorNode","has","
|
|
1
|
+
{"version":3,"file":"canDropItemOnFolder.mjs","sources":["../../../../admin/src/future/utils/canDropItemOnFolder.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\nimport type { DragItemData } from '../types/dnd';\n\n// TODO: permission-aware droppables — server validates in v1.\n\nexport interface CanDropItemOnFolderParams {\n items: DragItemData[];\n targetFolderId: number | null;\n folderStructure: FolderNode[];\n}\n\nconst findNode = (nodes: FolderNode[], id: number): FolderNode | null => {\n for (const node of nodes) {\n if (node.id === id) {\n return node;\n }\n\n const found = findNode(node.children, id);\n if (found) {\n return found;\n }\n }\n\n return null;\n};\n\nconst collectDescendantIds = (node: FolderNode): Set<number> => {\n const ids = new Set<number>();\n\n const walk = (current: FolderNode) => {\n for (const child of current.children) {\n if (child.id != null) {\n ids.add(child.id);\n }\n walk(child);\n }\n };\n\n walk(node);\n return ids;\n};\n\nexport const isFolderDescendantOf = (\n folderStructure: FolderNode[],\n ancestorId: number,\n candidateId: number\n): boolean => {\n if (ancestorId === candidateId) {\n return true;\n }\n\n const ancestorNode = findNode(folderStructure, ancestorId);\n if (!ancestorNode) {\n return false;\n }\n\n return collectDescendantIds(ancestorNode).has(candidateId);\n};\n\nconst isItemAtRoot = (item: DragItemData): boolean =>\n item.kind === 'file' ? item.folderId == null : item.parentId == null;\n\nexport const canDropItemOnFolder = ({\n items,\n targetFolderId,\n folderStructure,\n}: CanDropItemOnFolderParams): boolean => {\n if (items.length === 0) {\n return false;\n }\n\n if (targetFolderId === null) {\n return items.some((item) => !isItemAtRoot(item));\n }\n\n const draggedFolderIds = new Set(\n items\n .filter((item): item is Extract<DragItemData, { kind: 'folder' }> => item.kind === 'folder')\n .map((item) => item.id)\n );\n\n if (draggedFolderIds.has(targetFolderId)) {\n return false;\n }\n\n for (const folderId of draggedFolderIds) {\n if (isFolderDescendantOf(folderStructure, folderId, targetFolderId)) {\n return false;\n }\n }\n\n for (const item of items) {\n if (item.kind === 'file' && item.folderId === targetFolderId) {\n return false;\n }\n\n if (item.kind === 'folder' && item.parentId === targetFolderId) {\n return false;\n }\n }\n\n return true;\n};\n"],"names":["findNode","nodes","id","node","found","children","collectDescendantIds","ids","Set","walk","current","child","add","isFolderDescendantOf","folderStructure","ancestorId","candidateId","ancestorNode","has","isItemAtRoot","item","kind","folderId","parentId","canDropItemOnFolder","items","targetFolderId","length","some","draggedFolderIds","filter","map"],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Flattens the nested folder structure (`/upload/folder-structure`) into a
|
|
5
|
+
* depth-first list of `{ id, label }` options where the label carries the full
|
|
6
|
+
* ancestry path. Used by destination selects so folders sharing a name stay
|
|
7
|
+
* distinguishable ("About / Images" vs "Tech / Images"), with children listed
|
|
8
|
+
* right under their parent.
|
|
9
|
+
*
|
|
10
|
+
* `excludeSubtreeIds` prunes a folder *and everything below it* in the same
|
|
11
|
+
* single pass — used to drop the selected folders (and their descendants) from
|
|
12
|
+
* bulk-move destinations without re-walking the tree per option.
|
|
13
|
+
*/ const flattenFolderStructure = (nodes, excludeSubtreeIds = new Set(), parentLabel = '')=>nodes.flatMap((node)=>{
|
|
14
|
+
if (node.id == null || excludeSubtreeIds.has(node.id)) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
const label = parentLabel ? `${parentLabel} / ${node.name ?? ''}` : node.name ?? '';
|
|
18
|
+
return [
|
|
19
|
+
{
|
|
20
|
+
id: node.id,
|
|
21
|
+
label
|
|
22
|
+
},
|
|
23
|
+
...flattenFolderStructure(node.children ?? [], excludeSubtreeIds, label)
|
|
24
|
+
];
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
exports.flattenFolderStructure = flattenFolderStructure;
|
|
28
|
+
//# sourceMappingURL=flattenFolderStructure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flattenFolderStructure.js","sources":["../../../../admin/src/future/utils/flattenFolderStructure.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\n\nexport interface FlatFolderOption {\n id: number;\n /** Full ancestry label, e.g. \"About / Images\". */\n label: string;\n}\n\n/**\n * Flattens the nested folder structure (`/upload/folder-structure`) into a\n * depth-first list of `{ id, label }` options where the label carries the full\n * ancestry path. Used by destination selects so folders sharing a name stay\n * distinguishable (\"About / Images\" vs \"Tech / Images\"), with children listed\n * right under their parent.\n *\n * `excludeSubtreeIds` prunes a folder *and everything below it* in the same\n * single pass — used to drop the selected folders (and their descendants) from\n * bulk-move destinations without re-walking the tree per option.\n */\nexport const flattenFolderStructure = (\n nodes: FolderNode[],\n excludeSubtreeIds: ReadonlySet<number> = new Set(),\n parentLabel = ''\n): FlatFolderOption[] =>\n nodes.flatMap((node) => {\n if (node.id == null || excludeSubtreeIds.has(node.id)) {\n return [];\n }\n\n const label = parentLabel ? `${parentLabel} / ${node.name ?? ''}` : (node.name ?? '');\n\n return [\n { id: node.id, label },\n ...flattenFolderStructure(node.children ?? [], excludeSubtreeIds, label),\n ];\n });\n"],"names":["flattenFolderStructure","nodes","excludeSubtreeIds","Set","parentLabel","flatMap","node","id","has","label","name","children"],"mappings":";;AAQA;;;;;;;;;;AAUC,IACM,MAAMA,sBAAAA,GAAyB,CACpCC,OACAC,iBAAAA,GAAyC,IAAIC,GAAAA,EAAK,EAClDC,cAAc,EAAE,GAEhBH,KAAAA,CAAMI,OAAO,CAAC,CAACC,IAAAA,GAAAA;QACb,IAAIA,IAAAA,CAAKC,EAAE,IAAI,IAAA,IAAQL,kBAAkBM,GAAG,CAACF,IAAAA,CAAKC,EAAE,CAAA,EAAG;AACrD,YAAA,OAAO,EAAE;AACX,QAAA;AAEA,QAAA,MAAME,KAAAA,GAAQL,WAAAA,GAAc,CAAA,EAAGA,WAAAA,CAAY,GAAG,EAAEE,IAAAA,CAAKI,IAAI,IAAI,EAAA,CAAA,CAAI,GAAIJ,IAAAA,CAAKI,IAAI,IAAI,EAAA;QAElF,OAAO;AACL,YAAA;AAAEH,gBAAAA,EAAAA,EAAID,KAAKC,EAAE;AAAEE,gBAAAA;AAAM,aAAA;AAClBT,YAAAA,GAAAA,sBAAAA,CAAuBM,IAAAA,CAAKK,QAAQ,IAAI,EAAE,EAAET,iBAAAA,EAAmBO,KAAAA;AACnE,SAAA;IACH,CAAA;;;;"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flattens the nested folder structure (`/upload/folder-structure`) into a
|
|
3
|
+
* depth-first list of `{ id, label }` options where the label carries the full
|
|
4
|
+
* ancestry path. Used by destination selects so folders sharing a name stay
|
|
5
|
+
* distinguishable ("About / Images" vs "Tech / Images"), with children listed
|
|
6
|
+
* right under their parent.
|
|
7
|
+
*
|
|
8
|
+
* `excludeSubtreeIds` prunes a folder *and everything below it* in the same
|
|
9
|
+
* single pass — used to drop the selected folders (and their descendants) from
|
|
10
|
+
* bulk-move destinations without re-walking the tree per option.
|
|
11
|
+
*/ const flattenFolderStructure = (nodes, excludeSubtreeIds = new Set(), parentLabel = '')=>nodes.flatMap((node)=>{
|
|
12
|
+
if (node.id == null || excludeSubtreeIds.has(node.id)) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const label = parentLabel ? `${parentLabel} / ${node.name ?? ''}` : node.name ?? '';
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
id: node.id,
|
|
19
|
+
label
|
|
20
|
+
},
|
|
21
|
+
...flattenFolderStructure(node.children ?? [], excludeSubtreeIds, label)
|
|
22
|
+
];
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export { flattenFolderStructure };
|
|
26
|
+
//# sourceMappingURL=flattenFolderStructure.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flattenFolderStructure.mjs","sources":["../../../../admin/src/future/utils/flattenFolderStructure.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\n\nexport interface FlatFolderOption {\n id: number;\n /** Full ancestry label, e.g. \"About / Images\". */\n label: string;\n}\n\n/**\n * Flattens the nested folder structure (`/upload/folder-structure`) into a\n * depth-first list of `{ id, label }` options where the label carries the full\n * ancestry path. Used by destination selects so folders sharing a name stay\n * distinguishable (\"About / Images\" vs \"Tech / Images\"), with children listed\n * right under their parent.\n *\n * `excludeSubtreeIds` prunes a folder *and everything below it* in the same\n * single pass — used to drop the selected folders (and their descendants) from\n * bulk-move destinations without re-walking the tree per option.\n */\nexport const flattenFolderStructure = (\n nodes: FolderNode[],\n excludeSubtreeIds: ReadonlySet<number> = new Set(),\n parentLabel = ''\n): FlatFolderOption[] =>\n nodes.flatMap((node) => {\n if (node.id == null || excludeSubtreeIds.has(node.id)) {\n return [];\n }\n\n const label = parentLabel ? `${parentLabel} / ${node.name ?? ''}` : (node.name ?? '');\n\n return [\n { id: node.id, label },\n ...flattenFolderStructure(node.children ?? [], excludeSubtreeIds, label),\n ];\n });\n"],"names":["flattenFolderStructure","nodes","excludeSubtreeIds","Set","parentLabel","flatMap","node","id","has","label","name","children"],"mappings":"AAQA;;;;;;;;;;AAUC,IACM,MAAMA,sBAAAA,GAAyB,CACpCC,OACAC,iBAAAA,GAAyC,IAAIC,GAAAA,EAAK,EAClDC,cAAc,EAAE,GAEhBH,KAAAA,CAAMI,OAAO,CAAC,CAACC,IAAAA,GAAAA;QACb,IAAIA,IAAAA,CAAKC,EAAE,IAAI,IAAA,IAAQL,kBAAkBM,GAAG,CAACF,IAAAA,CAAKC,EAAE,CAAA,EAAG;AACrD,YAAA,OAAO,EAAE;AACX,QAAA;AAEA,QAAA,MAAME,KAAAA,GAAQL,WAAAA,GAAc,CAAA,EAAGA,WAAAA,CAAY,GAAG,EAAEE,IAAAA,CAAKI,IAAI,IAAI,EAAA,CAAA,CAAI,GAAIJ,IAAAA,CAAKI,IAAI,IAAI,EAAA;QAElF,OAAO;AACL,YAAA;AAAEH,gBAAAA,EAAAA,EAAID,KAAKC,EAAE;AAAEE,gBAAAA;AAAM,aAAA;AAClBT,YAAAA,GAAAA,sBAAAA,CAAuBM,IAAAA,CAAKK,QAAQ,IAAI,EAAE,EAAET,iBAAAA,EAAmBO,KAAAA;AACnE,SAAA;IACH,CAAA;;;;"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var translations = require('./translations.js');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Single source of truth for the bulk-move success wording. Both the DnD drop
|
|
7
|
+
* and the BulkMoveDialog format the toast (and DnD live-region announcement)
|
|
8
|
+
* through here so the two paths stay identical for the same move.
|
|
9
|
+
*/ const formatMoveSuccessMessage = ({ formatMessage, count, source, destination })=>formatMessage({
|
|
10
|
+
id: translations.getTranslationKey('list.bulk-actions.move.success'),
|
|
11
|
+
defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}'
|
|
12
|
+
}, {
|
|
13
|
+
count,
|
|
14
|
+
source,
|
|
15
|
+
destination
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
exports.formatMoveSuccessMessage = formatMoveSuccessMessage;
|
|
19
|
+
//# sourceMappingURL=formatMoveSuccessMessage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatMoveSuccessMessage.js","sources":["../../../../admin/src/future/utils/formatMoveSuccessMessage.ts"],"sourcesContent":["import { getTranslationKey } from './translations';\n\nimport type { IntlShape } from 'react-intl';\n\ninterface FormatMoveSuccessArgs {\n formatMessage: IntlShape['formatMessage'];\n count: number;\n source: string;\n destination: string;\n}\n\n/**\n * Single source of truth for the bulk-move success wording. Both the DnD drop\n * and the BulkMoveDialog format the toast (and DnD live-region announcement)\n * through here so the two paths stay identical for the same move.\n */\nexport const formatMoveSuccessMessage = ({\n formatMessage,\n count,\n source,\n destination,\n}: FormatMoveSuccessArgs): string =>\n formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}',\n },\n { count, source, destination }\n );\n"],"names":["formatMoveSuccessMessage","formatMessage","count","source","destination","id","getTranslationKey","defaultMessage"],"mappings":";;;;AAWA;;;;AAIC,IACM,MAAMA,wBAAAA,GAA2B,CAAC,EACvCC,aAAa,EACbC,KAAK,EACLC,MAAM,EACNC,WAAW,EACW,GACtBH,aAAAA,CACE;AACEI,QAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,gCAAA,CAAA;QACtBC,cAAAA,EACE;KACJ,EACA;AAAEL,QAAAA,KAAAA;AAAOC,QAAAA,MAAAA;AAAQC,QAAAA;KAAY;;;;"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getTranslationKey } from './translations.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Single source of truth for the bulk-move success wording. Both the DnD drop
|
|
5
|
+
* and the BulkMoveDialog format the toast (and DnD live-region announcement)
|
|
6
|
+
* through here so the two paths stay identical for the same move.
|
|
7
|
+
*/ const formatMoveSuccessMessage = ({ formatMessage, count, source, destination })=>formatMessage({
|
|
8
|
+
id: getTranslationKey('list.bulk-actions.move.success'),
|
|
9
|
+
defaultMessage: '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}'
|
|
10
|
+
}, {
|
|
11
|
+
count,
|
|
12
|
+
source,
|
|
13
|
+
destination
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export { formatMoveSuccessMessage };
|
|
17
|
+
//# sourceMappingURL=formatMoveSuccessMessage.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatMoveSuccessMessage.mjs","sources":["../../../../admin/src/future/utils/formatMoveSuccessMessage.ts"],"sourcesContent":["import { getTranslationKey } from './translations';\n\nimport type { IntlShape } from 'react-intl';\n\ninterface FormatMoveSuccessArgs {\n formatMessage: IntlShape['formatMessage'];\n count: number;\n source: string;\n destination: string;\n}\n\n/**\n * Single source of truth for the bulk-move success wording. Both the DnD drop\n * and the BulkMoveDialog format the toast (and DnD live-region announcement)\n * through here so the two paths stay identical for the same move.\n */\nexport const formatMoveSuccessMessage = ({\n formatMessage,\n count,\n source,\n destination,\n}: FormatMoveSuccessArgs): string =>\n formatMessage(\n {\n id: getTranslationKey('list.bulk-actions.move.success'),\n defaultMessage:\n '{count, plural, =1 {# element has} other {# elements have}} been moved from {source} to {destination}',\n },\n { count, source, destination }\n );\n"],"names":["formatMoveSuccessMessage","formatMessage","count","source","destination","id","getTranslationKey","defaultMessage"],"mappings":";;AAWA;;;;AAIC,IACM,MAAMA,wBAAAA,GAA2B,CAAC,EACvCC,aAAa,EACbC,KAAK,EACLC,MAAM,EACNC,WAAW,EACW,GACtBH,aAAAA,CACE;AACEI,QAAAA,EAAAA,EAAIC,iBAAAA,CAAkB,gCAAA,CAAA;QACtBC,cAAAA,EACE;KACJ,EACA;AAAEL,QAAAA,KAAAA;AAAOC,QAAAA,MAAAA;AAAQC,QAAAA;KAAY;;;;"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Leaf display name for a folder id. `null` (root) → the Media Library label.
|
|
5
|
+
* Used by both the DnD success toast and the dialog success toast so the two
|
|
6
|
+
* paths name folders identically. Ancestry paths belong only in the dialog's
|
|
7
|
+
* select options (see `flattenFolderStructure`).
|
|
8
|
+
*
|
|
9
|
+
* `id === null` returns `rootLabel`; an unknown id falls back to `rootLabel`
|
|
10
|
+
* too (defensive — mirrors the dialog's previous `?? rootLabel`).
|
|
11
|
+
*/ const getFolderLabel = (folderStructure, id, rootLabel)=>{
|
|
12
|
+
if (id === null) {
|
|
13
|
+
return rootLabel;
|
|
14
|
+
}
|
|
15
|
+
const findNode = (nodes)=>{
|
|
16
|
+
for (const node of nodes){
|
|
17
|
+
if (node.id === id) {
|
|
18
|
+
return node;
|
|
19
|
+
}
|
|
20
|
+
const found = findNode(node.children ?? []);
|
|
21
|
+
if (found) {
|
|
22
|
+
return found;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
};
|
|
27
|
+
return findNode(folderStructure)?.name ?? rootLabel;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
exports.getFolderLabel = getFolderLabel;
|
|
31
|
+
//# sourceMappingURL=getFolderLabel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getFolderLabel.js","sources":["../../../../admin/src/future/utils/getFolderLabel.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\n\n/**\n * Leaf display name for a folder id. `null` (root) → the Media Library label.\n * Used by both the DnD success toast and the dialog success toast so the two\n * paths name folders identically. Ancestry paths belong only in the dialog's\n * select options (see `flattenFolderStructure`).\n *\n * `id === null` returns `rootLabel`; an unknown id falls back to `rootLabel`\n * too (defensive — mirrors the dialog's previous `?? rootLabel`).\n */\nexport const getFolderLabel = (\n folderStructure: FolderNode[],\n id: number | null,\n rootLabel: string\n): string => {\n if (id === null) {\n return rootLabel;\n }\n\n const findNode = (nodes: FolderNode[]): FolderNode | null => {\n for (const node of nodes) {\n if (node.id === id) {\n return node;\n }\n\n const found = findNode(node.children ?? []);\n if (found) {\n return found;\n }\n }\n\n return null;\n };\n\n return findNode(folderStructure)?.name ?? rootLabel;\n};\n"],"names":["getFolderLabel","folderStructure","id","rootLabel","findNode","nodes","node","found","children","name"],"mappings":";;AAEA;;;;;;;;AAQC,IACM,MAAMA,cAAAA,GAAiB,CAC5BC,iBACAC,EAAAA,EACAC,SAAAA,GAAAA;AAEA,IAAA,IAAID,OAAO,IAAA,EAAM;QACf,OAAOC,SAAAA;AACT,IAAA;AAEA,IAAA,MAAMC,WAAW,CAACC,KAAAA,GAAAA;QAChB,KAAK,MAAMC,QAAQD,KAAAA,CAAO;YACxB,IAAIC,IAAAA,CAAKJ,EAAE,KAAKA,EAAAA,EAAI;gBAClB,OAAOI,IAAAA;AACT,YAAA;AAEA,YAAA,MAAMC,KAAAA,GAAQH,QAAAA,CAASE,IAAAA,CAAKE,QAAQ,IAAI,EAAE,CAAA;AAC1C,YAAA,IAAID,KAAAA,EAAO;gBACT,OAAOA,KAAAA;AACT,YAAA;AACF,QAAA;QAEA,OAAO,IAAA;AACT,IAAA,CAAA;IAEA,OAAOH,QAAAA,CAASH,kBAAkBQ,IAAAA,IAAQN,SAAAA;AAC5C;;;;"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Leaf display name for a folder id. `null` (root) → the Media Library label.
|
|
3
|
+
* Used by both the DnD success toast and the dialog success toast so the two
|
|
4
|
+
* paths name folders identically. Ancestry paths belong only in the dialog's
|
|
5
|
+
* select options (see `flattenFolderStructure`).
|
|
6
|
+
*
|
|
7
|
+
* `id === null` returns `rootLabel`; an unknown id falls back to `rootLabel`
|
|
8
|
+
* too (defensive — mirrors the dialog's previous `?? rootLabel`).
|
|
9
|
+
*/ const getFolderLabel = (folderStructure, id, rootLabel)=>{
|
|
10
|
+
if (id === null) {
|
|
11
|
+
return rootLabel;
|
|
12
|
+
}
|
|
13
|
+
const findNode = (nodes)=>{
|
|
14
|
+
for (const node of nodes){
|
|
15
|
+
if (node.id === id) {
|
|
16
|
+
return node;
|
|
17
|
+
}
|
|
18
|
+
const found = findNode(node.children ?? []);
|
|
19
|
+
if (found) {
|
|
20
|
+
return found;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
};
|
|
25
|
+
return findNode(folderStructure)?.name ?? rootLabel;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export { getFolderLabel };
|
|
29
|
+
//# sourceMappingURL=getFolderLabel.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getFolderLabel.mjs","sources":["../../../../admin/src/future/utils/getFolderLabel.ts"],"sourcesContent":["import type { FolderNode } from '../../../../shared/contracts/folders';\n\n/**\n * Leaf display name for a folder id. `null` (root) → the Media Library label.\n * Used by both the DnD success toast and the dialog success toast so the two\n * paths name folders identically. Ancestry paths belong only in the dialog's\n * select options (see `flattenFolderStructure`).\n *\n * `id === null` returns `rootLabel`; an unknown id falls back to `rootLabel`\n * too (defensive — mirrors the dialog's previous `?? rootLabel`).\n */\nexport const getFolderLabel = (\n folderStructure: FolderNode[],\n id: number | null,\n rootLabel: string\n): string => {\n if (id === null) {\n return rootLabel;\n }\n\n const findNode = (nodes: FolderNode[]): FolderNode | null => {\n for (const node of nodes) {\n if (node.id === id) {\n return node;\n }\n\n const found = findNode(node.children ?? []);\n if (found) {\n return found;\n }\n }\n\n return null;\n };\n\n return findNode(folderStructure)?.name ?? rootLabel;\n};\n"],"names":["getFolderLabel","folderStructure","id","rootLabel","findNode","nodes","node","found","children","name"],"mappings":"AAEA;;;;;;;;AAQC,IACM,MAAMA,cAAAA,GAAiB,CAC5BC,iBACAC,EAAAA,EACAC,SAAAA,GAAAA;AAEA,IAAA,IAAID,OAAO,IAAA,EAAM;QACf,OAAOC,SAAAA;AACT,IAAA;AAEA,IAAA,MAAMC,WAAW,CAACC,KAAAA,GAAAA;QAChB,KAAK,MAAMC,QAAQD,KAAAA,CAAO;YACxB,IAAIC,IAAAA,CAAKJ,EAAE,KAAKA,EAAAA,EAAI;gBAClB,OAAOI,IAAAA;AACT,YAAA;AAEA,YAAA,MAAMC,KAAAA,GAAQH,QAAAA,CAASE,IAAAA,CAAKE,QAAQ,IAAI,EAAE,CAAA;AAC1C,YAAA,IAAID,KAAAA,EAAO;gBACT,OAAOA,KAAAA;AACT,YAAA;AACF,QAAA;QAEA,OAAO,IAAA;AACT,IAAA,CAAA;IAEA,OAAOH,QAAAA,CAASH,kBAAkBQ,IAAAA,IAAQN,SAAAA;AAC5C;;;;"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `_q` must be encoded on both hops: `useQueryParams.setQuery` and the fetch
|
|
5
|
+
* client both stringify with `{ encode: false }`, so an unencoded `&` would
|
|
6
|
+
* truncate the query. `qs.parse` decodes `+` as a space, so `+` needs escaping
|
|
7
|
+
* beyond what `encodeURIComponent` does.
|
|
8
|
+
*/ const encodeSearchQuery = (value)=>encodeURIComponent(value).replace(/\+/g, '%2B');
|
|
9
|
+
|
|
10
|
+
exports.encodeSearchQuery = encodeSearchQuery;
|
|
11
|
+
//# sourceMappingURL=searchQueryParam.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchQueryParam.js","sources":["../../../../admin/src/future/utils/searchQueryParam.ts"],"sourcesContent":["/**\n * `_q` must be encoded on both hops: `useQueryParams.setQuery` and the fetch\n * client both stringify with `{ encode: false }`, so an unencoded `&` would\n * truncate the query. `qs.parse` decodes `+` as a space, so `+` needs escaping\n * beyond what `encodeURIComponent` does.\n */\nexport const encodeSearchQuery = (value: string): string =>\n encodeURIComponent(value).replace(/\\+/g, '%2B');\n"],"names":["encodeSearchQuery","value","encodeURIComponent","replace"],"mappings":";;AAAA;;;;;IAMO,MAAMA,iBAAAA,GAAoB,CAACC,KAAAA,GAChCC,mBAAmBD,KAAAA,CAAAA,CAAOE,OAAO,CAAC,KAAA,EAAO,KAAA;;;;"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `_q` must be encoded on both hops: `useQueryParams.setQuery` and the fetch
|
|
3
|
+
* client both stringify with `{ encode: false }`, so an unencoded `&` would
|
|
4
|
+
* truncate the query. `qs.parse` decodes `+` as a space, so `+` needs escaping
|
|
5
|
+
* beyond what `encodeURIComponent` does.
|
|
6
|
+
*/ const encodeSearchQuery = (value)=>encodeURIComponent(value).replace(/\+/g, '%2B');
|
|
7
|
+
|
|
8
|
+
export { encodeSearchQuery };
|
|
9
|
+
//# sourceMappingURL=searchQueryParam.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searchQueryParam.mjs","sources":["../../../../admin/src/future/utils/searchQueryParam.ts"],"sourcesContent":["/**\n * `_q` must be encoded on both hops: `useQueryParams.setQuery` and the fetch\n * client both stringify with `{ encode: false }`, so an unencoded `&` would\n * truncate the query. `qs.parse` decodes `+` as a space, so `+` needs escaping\n * beyond what `encodeURIComponent` does.\n */\nexport const encodeSearchQuery = (value: string): string =>\n encodeURIComponent(value).replace(/\\+/g, '%2B');\n"],"names":["encodeSearchQuery","value","encodeURIComponent","replace"],"mappings":"AAAA;;;;;IAMO,MAAMA,iBAAAA,GAAoB,CAACC,KAAAA,GAChCC,mBAAmBD,KAAAA,CAAAA,CAAOE,OAAO,CAAC,KAAA,EAAO,KAAA;;;;"}
|
|
@@ -64,10 +64,8 @@ const useSelectionState = (keys, initialValue)=>{
|
|
|
64
64
|
]);
|
|
65
65
|
};
|
|
66
66
|
const deselectMultiple = (nextSelections)=>{
|
|
67
|
-
setSelections((currSelections)
|
|
68
|
-
|
|
69
|
-
...currSelections.filter((currentSelection)=>nextSelections.findIndex((nextSelection)=>keys.every((key)=>currentSelection[key] === nextSelection[key])) === -1)
|
|
70
|
-
]);
|
|
67
|
+
setSelections((currSelections)=>// filter out items in currSelections that are in nextSelections
|
|
68
|
+
currSelections.filter((currentSelection)=>nextSelections.findIndex((nextSelection)=>keys.every((key)=>currentSelection[key] === nextSelection[key])) === -1));
|
|
71
69
|
};
|
|
72
70
|
return [
|
|
73
71
|
selections,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSelectionState.js","sources":["../../../admin/src/hooks/useSelectionState.ts"],"sourcesContent":["import * as React from 'react';\n\nexport const useSelectionState = <TValues extends object>(\n keys: Array<keyof TValues>,\n initialValue: TValues[]\n) => {\n const [selections, setSelections] = React.useState(initialValue);\n\n const selectOne = (selection: TValues) => {\n const index = selections.findIndex((currentSelection) =>\n keys.every((key) => currentSelection[key] === selection[key])\n );\n\n if (index > -1) {\n setSelections((prevSelected) => [\n ...prevSelected.slice(0, index),\n ...prevSelected.slice(index + 1),\n ]);\n } else {\n setSelections((prevSelected) => [...prevSelected, selection]);\n }\n };\n\n const selectAll = (nextSelections?: TValues[]) => {\n if (selections.length > 0) {\n setSelections([]);\n } else {\n setSelections(nextSelections!);\n }\n };\n\n const selectOnly = (nextSelection: TValues) => {\n const index = selections.findIndex((currentSelection) =>\n keys.every((key) => currentSelection[key] === nextSelection[key])\n );\n\n if (index > -1) {\n setSelections([]);\n } else {\n setSelections([nextSelection]);\n }\n };\n\n const selectMultiple = (nextSelections: TValues[]) => {\n setSelections((currSelections) => [\n // already selected items\n ...currSelections,\n // filter out already selected items from nextSelections\n ...nextSelections.filter(\n (nextSelection) =>\n currSelections.findIndex((currentSelection) =>\n keys.every((key) => currentSelection[key] === nextSelection[key])\n ) === -1\n ),\n ]);\n };\n\n const deselectMultiple = (nextSelections: TValues[]) => {\n setSelections((currSelections)
|
|
1
|
+
{"version":3,"file":"useSelectionState.js","sources":["../../../admin/src/hooks/useSelectionState.ts"],"sourcesContent":["import * as React from 'react';\n\nexport const useSelectionState = <TValues extends object>(\n keys: Array<keyof TValues>,\n initialValue: TValues[]\n) => {\n const [selections, setSelections] = React.useState(initialValue);\n\n const selectOne = (selection: TValues) => {\n const index = selections.findIndex((currentSelection) =>\n keys.every((key) => currentSelection[key] === selection[key])\n );\n\n if (index > -1) {\n setSelections((prevSelected) => [\n ...prevSelected.slice(0, index),\n ...prevSelected.slice(index + 1),\n ]);\n } else {\n setSelections((prevSelected) => [...prevSelected, selection]);\n }\n };\n\n const selectAll = (nextSelections?: TValues[]) => {\n if (selections.length > 0) {\n setSelections([]);\n } else {\n setSelections(nextSelections!);\n }\n };\n\n const selectOnly = (nextSelection: TValues) => {\n const index = selections.findIndex((currentSelection) =>\n keys.every((key) => currentSelection[key] === nextSelection[key])\n );\n\n if (index > -1) {\n setSelections([]);\n } else {\n setSelections([nextSelection]);\n }\n };\n\n const selectMultiple = (nextSelections: TValues[]) => {\n setSelections((currSelections) => [\n // already selected items\n ...currSelections,\n // filter out already selected items from nextSelections\n ...nextSelections.filter(\n (nextSelection) =>\n currSelections.findIndex((currentSelection) =>\n keys.every((key) => currentSelection[key] === nextSelection[key])\n ) === -1\n ),\n ]);\n };\n\n const deselectMultiple = (nextSelections: TValues[]) => {\n setSelections((currSelections) =>\n // filter out items in currSelections that are in nextSelections\n currSelections.filter(\n (currentSelection) =>\n nextSelections.findIndex((nextSelection) =>\n keys.every((key) => currentSelection[key] === nextSelection[key])\n ) === -1\n )\n );\n };\n\n return [\n selections,\n { selectOne, selectAll, selectOnly, selectMultiple, deselectMultiple, setSelections },\n ] as const;\n};\n"],"names":["useSelectionState","keys","initialValue","selections","setSelections","React","useState","selectOne","selection","index","findIndex","currentSelection","every","key","prevSelected","slice","selectAll","nextSelections","length","selectOnly","nextSelection","selectMultiple","currSelections","filter","deselectMultiple"],"mappings":"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"}
|
|
@@ -42,10 +42,8 @@ const useSelectionState = (keys, initialValue)=>{
|
|
|
42
42
|
]);
|
|
43
43
|
};
|
|
44
44
|
const deselectMultiple = (nextSelections)=>{
|
|
45
|
-
setSelections((currSelections)
|
|
46
|
-
|
|
47
|
-
...currSelections.filter((currentSelection)=>nextSelections.findIndex((nextSelection)=>keys.every((key)=>currentSelection[key] === nextSelection[key])) === -1)
|
|
48
|
-
]);
|
|
45
|
+
setSelections((currSelections)=>// filter out items in currSelections that are in nextSelections
|
|
46
|
+
currSelections.filter((currentSelection)=>nextSelections.findIndex((nextSelection)=>keys.every((key)=>currentSelection[key] === nextSelection[key])) === -1));
|
|
49
47
|
};
|
|
50
48
|
return [
|
|
51
49
|
selections,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSelectionState.mjs","sources":["../../../admin/src/hooks/useSelectionState.ts"],"sourcesContent":["import * as React from 'react';\n\nexport const useSelectionState = <TValues extends object>(\n keys: Array<keyof TValues>,\n initialValue: TValues[]\n) => {\n const [selections, setSelections] = React.useState(initialValue);\n\n const selectOne = (selection: TValues) => {\n const index = selections.findIndex((currentSelection) =>\n keys.every((key) => currentSelection[key] === selection[key])\n );\n\n if (index > -1) {\n setSelections((prevSelected) => [\n ...prevSelected.slice(0, index),\n ...prevSelected.slice(index + 1),\n ]);\n } else {\n setSelections((prevSelected) => [...prevSelected, selection]);\n }\n };\n\n const selectAll = (nextSelections?: TValues[]) => {\n if (selections.length > 0) {\n setSelections([]);\n } else {\n setSelections(nextSelections!);\n }\n };\n\n const selectOnly = (nextSelection: TValues) => {\n const index = selections.findIndex((currentSelection) =>\n keys.every((key) => currentSelection[key] === nextSelection[key])\n );\n\n if (index > -1) {\n setSelections([]);\n } else {\n setSelections([nextSelection]);\n }\n };\n\n const selectMultiple = (nextSelections: TValues[]) => {\n setSelections((currSelections) => [\n // already selected items\n ...currSelections,\n // filter out already selected items from nextSelections\n ...nextSelections.filter(\n (nextSelection) =>\n currSelections.findIndex((currentSelection) =>\n keys.every((key) => currentSelection[key] === nextSelection[key])\n ) === -1\n ),\n ]);\n };\n\n const deselectMultiple = (nextSelections: TValues[]) => {\n setSelections((currSelections)
|
|
1
|
+
{"version":3,"file":"useSelectionState.mjs","sources":["../../../admin/src/hooks/useSelectionState.ts"],"sourcesContent":["import * as React from 'react';\n\nexport const useSelectionState = <TValues extends object>(\n keys: Array<keyof TValues>,\n initialValue: TValues[]\n) => {\n const [selections, setSelections] = React.useState(initialValue);\n\n const selectOne = (selection: TValues) => {\n const index = selections.findIndex((currentSelection) =>\n keys.every((key) => currentSelection[key] === selection[key])\n );\n\n if (index > -1) {\n setSelections((prevSelected) => [\n ...prevSelected.slice(0, index),\n ...prevSelected.slice(index + 1),\n ]);\n } else {\n setSelections((prevSelected) => [...prevSelected, selection]);\n }\n };\n\n const selectAll = (nextSelections?: TValues[]) => {\n if (selections.length > 0) {\n setSelections([]);\n } else {\n setSelections(nextSelections!);\n }\n };\n\n const selectOnly = (nextSelection: TValues) => {\n const index = selections.findIndex((currentSelection) =>\n keys.every((key) => currentSelection[key] === nextSelection[key])\n );\n\n if (index > -1) {\n setSelections([]);\n } else {\n setSelections([nextSelection]);\n }\n };\n\n const selectMultiple = (nextSelections: TValues[]) => {\n setSelections((currSelections) => [\n // already selected items\n ...currSelections,\n // filter out already selected items from nextSelections\n ...nextSelections.filter(\n (nextSelection) =>\n currSelections.findIndex((currentSelection) =>\n keys.every((key) => currentSelection[key] === nextSelection[key])\n ) === -1\n ),\n ]);\n };\n\n const deselectMultiple = (nextSelections: TValues[]) => {\n setSelections((currSelections) =>\n // filter out items in currSelections that are in nextSelections\n currSelections.filter(\n (currentSelection) =>\n nextSelections.findIndex((nextSelection) =>\n keys.every((key) => currentSelection[key] === nextSelection[key])\n ) === -1\n )\n );\n };\n\n return [\n selections,\n { selectOne, selectAll, selectOnly, selectMultiple, deselectMultiple, setSelections },\n ] as const;\n};\n"],"names":["useSelectionState","keys","initialValue","selections","setSelections","React","useState","selectOne","selection","index","findIndex","currentSelection","every","key","prevSelected","slice","selectAll","nextSelections","length","selectOnly","nextSelection","selectMultiple","currSelections","filter","deselectMultiple"],"mappings":";;AAEO,MAAMA,iBAAAA,GAAoB,CAC/BC,IAAAA,EACAC,YAAAA,GAAAA;AAEA,IAAA,MAAM,CAACC,UAAAA,EAAYC,aAAAA,CAAc,GAAGC,KAAAA,CAAMC,QAAQ,CAACJ,YAAAA,CAAAA;AAEnD,IAAA,MAAMK,YAAY,CAACC,SAAAA,GAAAA;AACjB,QAAA,MAAMC,QAAQN,UAAAA,CAAWO,SAAS,CAAC,CAACC,mBAClCV,IAAAA,CAAKW,KAAK,CAAC,CAACC,MAAQF,gBAAgB,CAACE,IAAI,KAAKL,SAAS,CAACK,GAAAA,CAAI,CAAA,CAAA;QAG9D,IAAIJ,KAAAA,GAAQ,EAAC,EAAG;AACdL,YAAAA,aAAAA,CAAc,CAACU,YAAAA,GAAiB;uBAC3BA,YAAAA,CAAaC,KAAK,CAAC,CAAA,EAAGN,KAAAA,CAAAA;uBACtBK,YAAAA,CAAaC,KAAK,CAACN,KAAAA,GAAQ,CAAA;AAC/B,iBAAA,CAAA;QACH,CAAA,MAAO;AACLL,YAAAA,aAAAA,CAAc,CAACU,YAAAA,GAAiB;AAAIA,oBAAAA,GAAAA,YAAAA;AAAcN,oBAAAA;AAAU,iBAAA,CAAA;AAC9D,QAAA;AACF,IAAA,CAAA;AAEA,IAAA,MAAMQ,YAAY,CAACC,cAAAA,GAAAA;QACjB,IAAId,UAAAA,CAAWe,MAAM,GAAG,CAAA,EAAG;AACzBd,YAAAA,aAAAA,CAAc,EAAE,CAAA;QAClB,CAAA,MAAO;YACLA,aAAAA,CAAca,cAAAA,CAAAA;AAChB,QAAA;AACF,IAAA,CAAA;AAEA,IAAA,MAAME,aAAa,CAACC,aAAAA,GAAAA;AAClB,QAAA,MAAMX,QAAQN,UAAAA,CAAWO,SAAS,CAAC,CAACC,mBAClCV,IAAAA,CAAKW,KAAK,CAAC,CAACC,MAAQF,gBAAgB,CAACE,IAAI,KAAKO,aAAa,CAACP,GAAAA,CAAI,CAAA,CAAA;QAGlE,IAAIJ,KAAAA,GAAQ,EAAC,EAAG;AACdL,YAAAA,aAAAA,CAAc,EAAE,CAAA;QAClB,CAAA,MAAO;YACLA,aAAAA,CAAc;AAACgB,gBAAAA;AAAc,aAAA,CAAA;AAC/B,QAAA;AACF,IAAA,CAAA;AAEA,IAAA,MAAMC,iBAAiB,CAACJ,cAAAA,GAAAA;AACtBb,QAAAA,aAAAA,CAAc,CAACkB,cAAAA,GAAmB;;AAE7BA,gBAAAA,GAAAA,cAAAA;;mBAEAL,cAAAA,CAAeM,MAAM,CACtB,CAACH,aAAAA,GACCE,eAAeZ,SAAS,CAAC,CAACC,gBAAAA,GACxBV,IAAAA,CAAKW,KAAK,CAAC,CAACC,GAAAA,GAAQF,gBAAgB,CAACE,GAAAA,CAAI,KAAKO,aAAa,CAACP,GAAAA,CAAI,CAAA,CAAA,KAC5D,EAAC;AAEZ,aAAA,CAAA;AACH,IAAA,CAAA;AAEA,IAAA,MAAMW,mBAAmB,CAACP,cAAAA,GAAAA;QACxBb,aAAAA,CAAc,CAACkB;YAEbA,cAAAA,CAAeC,MAAM,CACnB,CAACZ,gBAAAA,GACCM,eAAeP,SAAS,CAAC,CAACU,aAAAA,GACxBnB,IAAAA,CAAKW,KAAK,CAAC,CAACC,GAAAA,GAAQF,gBAAgB,CAACE,GAAAA,CAAI,KAAKO,aAAa,CAACP,GAAAA,CAAI,CAAA,CAAA,KAC5D,EAAC,CAAA,CAAA;AAGf,IAAA,CAAA;IAEA,OAAO;AACLV,QAAAA,UAAAA;AACA,QAAA;AAAEI,YAAAA,SAAAA;AAAWS,YAAAA,SAAAA;AAAWG,YAAAA,UAAAA;AAAYE,YAAAA,cAAAA;AAAgBG,YAAAA,gBAAAA;AAAkBpB,YAAAA;AAAc;AACrF,KAAA;AACH;;;;"}
|