@orthacms/media-admin 0.0.0-reserve.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +1 -0
  6. package/dist/lib/components/AssetActionsMenu/index.d.ts +37 -0
  7. package/dist/lib/components/AssetActionsMenu/index.d.ts.map +1 -0
  8. package/dist/lib/components/AssetActionsMenu/index.js +57 -0
  9. package/dist/lib/components/AssetDetailDrawer/MetaRow/index.d.ts +11 -0
  10. package/dist/lib/components/AssetDetailDrawer/MetaRow/index.d.ts.map +1 -0
  11. package/dist/lib/components/AssetDetailDrawer/MetaRow/index.js +9 -0
  12. package/dist/lib/components/AssetDetailDrawer/index.d.ts +28 -0
  13. package/dist/lib/components/AssetDetailDrawer/index.d.ts.map +1 -0
  14. package/dist/lib/components/AssetDetailDrawer/index.js +93 -0
  15. package/dist/lib/components/EntryMediaTab/MediaFieldSection/index.d.ts +28 -0
  16. package/dist/lib/components/EntryMediaTab/MediaFieldSection/index.d.ts.map +1 -0
  17. package/dist/lib/components/EntryMediaTab/MediaFieldSection/index.js +52 -0
  18. package/dist/lib/components/EntryMediaTab/index.d.ts +11 -0
  19. package/dist/lib/components/EntryMediaTab/index.d.ts.map +1 -0
  20. package/dist/lib/components/EntryMediaTab/index.js +31 -0
  21. package/dist/lib/components/FolderActionsMenu/index.d.ts +21 -0
  22. package/dist/lib/components/FolderActionsMenu/index.d.ts.map +1 -0
  23. package/dist/lib/components/FolderActionsMenu/index.js +25 -0
  24. package/dist/lib/components/MediaAltTextWidget/index.d.ts +9 -0
  25. package/dist/lib/components/MediaAltTextWidget/index.d.ts.map +1 -0
  26. package/dist/lib/components/MediaAltTextWidget/index.js +66 -0
  27. package/dist/lib/components/MediaBreadcrumbs/index.d.ts +11 -0
  28. package/dist/lib/components/MediaBreadcrumbs/index.d.ts.map +1 -0
  29. package/dist/lib/components/MediaEmptyState/index.d.ts +13 -0
  30. package/dist/lib/components/MediaEmptyState/index.d.ts.map +1 -0
  31. package/dist/lib/components/MediaEmptyState/index.js +38 -0
  32. package/dist/lib/components/MediaFieldControl/MediaFieldItem/index.d.ts +34 -0
  33. package/dist/lib/components/MediaFieldControl/MediaFieldItem/index.d.ts.map +1 -0
  34. package/dist/lib/components/MediaFieldControl/MediaFieldItem/index.js +117 -0
  35. package/dist/lib/components/MediaFieldControl/index.d.ts +61 -0
  36. package/dist/lib/components/MediaFieldControl/index.d.ts.map +1 -0
  37. package/dist/lib/components/MediaFieldControl/index.js +422 -0
  38. package/dist/lib/components/MediaFoldersNav/MediaFolderTreeItem/index.d.ts +17 -0
  39. package/dist/lib/components/MediaFoldersNav/MediaFolderTreeItem/index.d.ts.map +1 -0
  40. package/dist/lib/components/MediaFoldersNav/MediaFolderTreeItem/index.js +37 -0
  41. package/dist/lib/components/MediaFoldersNav/index.d.ts +22 -0
  42. package/dist/lib/components/MediaFoldersNav/index.d.ts.map +1 -0
  43. package/dist/lib/components/MediaFoldersNav/index.js +40 -0
  44. package/dist/lib/components/MediaGrid/MediaAssetCard/index.d.ts +18 -0
  45. package/dist/lib/components/MediaGrid/MediaAssetCard/index.d.ts.map +1 -0
  46. package/dist/lib/components/MediaGrid/MediaAssetCard/index.js +29 -0
  47. package/dist/lib/components/MediaGrid/MediaFolderCard/index.d.ts +16 -0
  48. package/dist/lib/components/MediaGrid/MediaFolderCard/index.d.ts.map +1 -0
  49. package/dist/lib/components/MediaGrid/MediaFolderCard/index.js +25 -0
  50. package/dist/lib/components/MediaGrid/index.d.ts +24 -0
  51. package/dist/lib/components/MediaGrid/index.d.ts.map +1 -0
  52. package/dist/lib/components/MediaGrid/index.js +20 -0
  53. package/dist/lib/components/MediaKindBadge/index.d.ts +10 -0
  54. package/dist/lib/components/MediaKindBadge/index.d.ts.map +1 -0
  55. package/dist/lib/components/MediaKindBadge/index.js +30 -0
  56. package/dist/lib/components/MediaKindIcon/index.d.ts +11 -0
  57. package/dist/lib/components/MediaKindIcon/index.d.ts.map +1 -0
  58. package/dist/lib/components/MediaKindIcon/index.js +20 -0
  59. package/dist/lib/components/MediaLibrarySkeleton/index.d.ts +18 -0
  60. package/dist/lib/components/MediaLibrarySkeleton/index.d.ts.map +1 -0
  61. package/dist/lib/components/MediaLibrarySkeleton/index.js +43 -0
  62. package/dist/lib/components/MediaPagination/index.d.ts +26 -0
  63. package/dist/lib/components/MediaPagination/index.d.ts.map +1 -0
  64. package/dist/lib/components/MediaPagination/index.js +52 -0
  65. package/dist/lib/components/MediaPickerDialog/MediaPickerTile/index.d.ts +17 -0
  66. package/dist/lib/components/MediaPickerDialog/MediaPickerTile/index.d.ts.map +1 -0
  67. package/dist/lib/components/MediaPickerDialog/MediaPickerTile/index.js +43 -0
  68. package/dist/lib/components/MediaPickerDialog/index.d.ts +21 -0
  69. package/dist/lib/components/MediaPickerDialog/index.d.ts.map +1 -0
  70. package/dist/lib/components/MediaPickerDialog/index.js +208 -0
  71. package/dist/lib/components/MediaSelectionBar/index.d.ts +18 -0
  72. package/dist/lib/components/MediaSelectionBar/index.d.ts.map +1 -0
  73. package/dist/lib/components/MediaSelectionBar/index.js +26 -0
  74. package/dist/lib/components/MediaStorageStat/index.d.ts +9 -0
  75. package/dist/lib/components/MediaStorageStat/index.d.ts.map +1 -0
  76. package/dist/lib/components/MediaStorageStat/index.js +31 -0
  77. package/dist/lib/components/MediaStorageWidget/index.d.ts +10 -0
  78. package/dist/lib/components/MediaStorageWidget/index.d.ts.map +1 -0
  79. package/dist/lib/components/MediaStorageWidget/index.js +93 -0
  80. package/dist/lib/components/MediaTable/MediaAssetRow/index.d.ts +17 -0
  81. package/dist/lib/components/MediaTable/MediaAssetRow/index.d.ts.map +1 -0
  82. package/dist/lib/components/MediaTable/MediaFolderRow/index.d.ts +16 -0
  83. package/dist/lib/components/MediaTable/MediaFolderRow/index.d.ts.map +1 -0
  84. package/dist/lib/components/MediaTable/index.d.ts +26 -0
  85. package/dist/lib/components/MediaTable/index.d.ts.map +1 -0
  86. package/dist/lib/components/MediaThumbnail/index.d.ts +19 -0
  87. package/dist/lib/components/MediaThumbnail/index.d.ts.map +1 -0
  88. package/dist/lib/components/MediaThumbnail/index.js +26 -0
  89. package/dist/lib/components/MediaToolbar/index.d.ts +19 -0
  90. package/dist/lib/components/MediaToolbar/index.d.ts.map +1 -0
  91. package/dist/lib/components/MediaToolbar/index.js +62 -0
  92. package/dist/lib/components/MediaTopBar/index.d.ts +20 -0
  93. package/dist/lib/components/MediaTopBar/index.d.ts.map +1 -0
  94. package/dist/lib/components/MediaTopBar/index.js +35 -0
  95. package/dist/lib/components/MediaUploadBanner/MediaUploadRow/index.d.ts +14 -0
  96. package/dist/lib/components/MediaUploadBanner/MediaUploadRow/index.d.ts.map +1 -0
  97. package/dist/lib/components/MediaUploadBanner/MediaUploadRow/index.js +51 -0
  98. package/dist/lib/components/MediaUploadBanner/index.d.ts +24 -0
  99. package/dist/lib/components/MediaUploadBanner/index.d.ts.map +1 -0
  100. package/dist/lib/components/MediaUploadBanner/index.js +99 -0
  101. package/dist/lib/components/MediaUploadsWidget/index.d.ts +7 -0
  102. package/dist/lib/components/MediaUploadsWidget/index.d.ts.map +1 -0
  103. package/dist/lib/components/MediaUploadsWidget/index.js +70 -0
  104. package/dist/lib/components/MoveAssetsDialog/DestinationOption/index.d.ts +19 -0
  105. package/dist/lib/components/MoveAssetsDialog/DestinationOption/index.d.ts.map +1 -0
  106. package/dist/lib/components/MoveAssetsDialog/DestinationOption/index.js +10 -0
  107. package/dist/lib/components/MoveAssetsDialog/index.d.ts +18 -0
  108. package/dist/lib/components/MoveAssetsDialog/index.d.ts.map +1 -0
  109. package/dist/lib/components/MoveAssetsDialog/index.js +41 -0
  110. package/dist/lib/components/NewFolderDialog/index.d.ts +13 -0
  111. package/dist/lib/components/NewFolderDialog/index.d.ts.map +1 -0
  112. package/dist/lib/components/NewFolderDialog/index.js +45 -0
  113. package/dist/lib/components/RenameDialog/index.d.ts +13 -0
  114. package/dist/lib/components/RenameDialog/index.d.ts.map +1 -0
  115. package/dist/lib/components/RenameDialog/index.js +37 -0
  116. package/dist/lib/components/UploadDialog/StagedFileRow/index.d.ts +25 -0
  117. package/dist/lib/components/UploadDialog/StagedFileRow/index.d.ts.map +1 -0
  118. package/dist/lib/components/UploadDialog/StagedFileRow/index.js +82 -0
  119. package/dist/lib/components/UploadDialog/index.d.ts +50 -0
  120. package/dist/lib/components/UploadDialog/index.d.ts.map +1 -0
  121. package/dist/lib/components/UploadDialog/index.js +120 -0
  122. package/dist/lib/components/WysiwygLibrarySource/index.d.ts +14 -0
  123. package/dist/lib/components/WysiwygLibrarySource/index.d.ts.map +1 -0
  124. package/dist/lib/components/WysiwygLibrarySource/index.js +28 -0
  125. package/dist/lib/components/WysiwygUploadSource/index.d.ts +25 -0
  126. package/dist/lib/components/WysiwygUploadSource/index.d.ts.map +1 -0
  127. package/dist/lib/components/WysiwygUploadSource/index.js +102 -0
  128. package/dist/lib/constants/index.d.ts +82 -0
  129. package/dist/lib/constants/index.d.ts.map +1 -0
  130. package/dist/lib/constants/index.js +75 -0
  131. package/dist/lib/hooks/useMediaInsights/index.d.ts +32 -0
  132. package/dist/lib/hooks/useMediaInsights/index.d.ts.map +1 -0
  133. package/dist/lib/hooks/useMediaInsights/index.js +85 -0
  134. package/dist/lib/hooks/useMediaLibrary/index.d.ts +83 -0
  135. package/dist/lib/hooks/useMediaLibrary/index.d.ts.map +1 -0
  136. package/dist/lib/hooks/useMediaLibrary/index.js +341 -0
  137. package/dist/lib/hooks/usePendingMediaUploads/index.d.ts +26 -0
  138. package/dist/lib/hooks/usePendingMediaUploads/index.d.ts.map +1 -0
  139. package/dist/lib/hooks/usePendingMediaUploads/index.js +217 -0
  140. package/dist/lib/hooks/useUploadQueue/index.d.ts +33 -0
  141. package/dist/lib/hooks/useUploadQueue/index.d.ts.map +1 -0
  142. package/dist/lib/hooks/useUploadQueue/index.js +218 -0
  143. package/dist/lib/hooks/useWysiwygAssetPicker/index.d.ts +23 -0
  144. package/dist/lib/hooks/useWysiwygAssetPicker/index.d.ts.map +1 -0
  145. package/dist/lib/infrastructure/apiMessage/index.d.ts +17 -0
  146. package/dist/lib/infrastructure/apiMessage/index.d.ts.map +1 -0
  147. package/dist/lib/infrastructure/apiMessage/index.js +30 -0
  148. package/dist/lib/infrastructure/httpMediaGateway/index.d.ts +9 -0
  149. package/dist/lib/infrastructure/httpMediaGateway/index.d.ts.map +1 -0
  150. package/dist/lib/infrastructure/httpMediaGateway/index.js +157 -0
  151. package/dist/lib/infrastructure/httpMediaInsightsGateway/index.d.ts +8 -0
  152. package/dist/lib/infrastructure/httpMediaInsightsGateway/index.d.ts.map +1 -0
  153. package/dist/lib/infrastructure/httpMediaInsightsGateway/index.js +35 -0
  154. package/dist/lib/infrastructure/mediaGateway/index.d.ts +120 -0
  155. package/dist/lib/infrastructure/mediaGateway/index.d.ts.map +1 -0
  156. package/dist/lib/infrastructure/mediaGateway/index.js +1 -0
  157. package/dist/lib/infrastructure/mediaInsightsGateway/index.d.ts +46 -0
  158. package/dist/lib/infrastructure/mediaInsightsGateway/index.d.ts.map +1 -0
  159. package/dist/lib/infrastructure/mediaInsightsGateway/index.js +6 -0
  160. package/dist/lib/infrastructure/mediaKeys/index.d.ts +23 -0
  161. package/dist/lib/infrastructure/mediaKeys/index.d.ts.map +1 -0
  162. package/dist/lib/infrastructure/mediaKeys/index.js +11 -0
  163. package/dist/lib/infrastructure/mediaMapper/index.d.ts +50 -0
  164. package/dist/lib/infrastructure/mediaMapper/index.d.ts.map +1 -0
  165. package/dist/lib/infrastructure/mediaMapper/index.js +39 -0
  166. package/dist/lib/pages/MediaLibraryPage/index.d.ts +23 -0
  167. package/dist/lib/pages/MediaLibraryPage/index.d.ts.map +1 -0
  168. package/dist/lib/pages/MediaLibraryPage/index.js +425 -0
  169. package/dist/lib/types/mediaAsset/index.d.ts +44 -0
  170. package/dist/lib/types/mediaAsset/index.d.ts.map +1 -0
  171. package/dist/lib/types/mediaAsset/index.js +1 -0
  172. package/dist/lib/types/mediaFieldDisplay/index.d.ts +48 -0
  173. package/dist/lib/types/mediaFieldDisplay/index.d.ts.map +1 -0
  174. package/dist/lib/types/mediaFieldDisplay/index.js +1 -0
  175. package/dist/lib/types/mediaFolder/index.d.ts +16 -0
  176. package/dist/lib/types/mediaFolder/index.d.ts.map +1 -0
  177. package/dist/lib/types/mediaFolder/index.js +1 -0
  178. package/dist/lib/types/pendingUpload/index.d.ts +38 -0
  179. package/dist/lib/types/pendingUpload/index.d.ts.map +1 -0
  180. package/dist/lib/types/pendingUpload/index.js +1 -0
  181. package/dist/lib/types/upload/index.d.ts +37 -0
  182. package/dist/lib/types/upload/index.d.ts.map +1 -0
  183. package/dist/lib/types/upload/index.js +1 -0
  184. package/dist/lib/utils/assetGradient/index.d.ts +8 -0
  185. package/dist/lib/utils/assetGradient/index.d.ts.map +1 -0
  186. package/dist/lib/utils/assetGradient/index.js +16 -0
  187. package/dist/lib/utils/assetHandlers/index.d.ts +11 -0
  188. package/dist/lib/utils/assetHandlers/index.d.ts.map +1 -0
  189. package/dist/lib/utils/assetHandlers/index.js +17 -0
  190. package/dist/lib/utils/folderContents/index.d.ts +17 -0
  191. package/dist/lib/utils/folderContents/index.d.ts.map +1 -0
  192. package/dist/lib/utils/folderContents/index.js +35 -0
  193. package/dist/lib/utils/folderTree/index.d.ts +13 -0
  194. package/dist/lib/utils/folderTree/index.d.ts.map +1 -0
  195. package/dist/lib/utils/folderTree/index.js +15 -0
  196. package/dist/lib/utils/formatBytes/index.d.ts +6 -0
  197. package/dist/lib/utils/formatBytes/index.d.ts.map +1 -0
  198. package/dist/lib/utils/formatBytes/index.js +17 -0
  199. package/dist/lib/utils/formatDuration/index.d.ts +6 -0
  200. package/dist/lib/utils/formatDuration/index.d.ts.map +1 -0
  201. package/dist/lib/utils/formatDuration/index.js +13 -0
  202. package/dist/lib/utils/kindFromMime/index.d.ts +10 -0
  203. package/dist/lib/utils/kindFromMime/index.d.ts.map +1 -0
  204. package/dist/lib/utils/kindFromMime/index.js +27 -0
  205. package/dist/lib/utils/mediaAccept/index.d.ts +30 -0
  206. package/dist/lib/utils/mediaAccept/index.d.ts.map +1 -0
  207. package/dist/lib/utils/mediaAccept/index.js +63 -0
  208. package/dist/lib/utils/mediaPlugin/index.d.ts +15 -0
  209. package/dist/lib/utils/mediaPlugin/index.d.ts.map +1 -0
  210. package/dist/lib/utils/mediaPlugin/index.js +177 -0
  211. package/dist/lib/utils/toWysiwygEmbed/index.d.ts +23 -0
  212. package/dist/lib/utils/toWysiwygEmbed/index.d.ts.map +1 -0
  213. package/dist/lib/utils/toWysiwygEmbed/index.js +44 -0
  214. package/package.json +50 -0
@@ -0,0 +1,425 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useState } from 'react';
3
+ import { defineMessages, useIntl } from 'react-intl';
4
+ import { Button, ConfirmDialog, Drawer, DrawerContent, DrawerTitle, cn, toast } from '@orthacms/design-system';
5
+ import { PanelLeft } from 'lucide-react';
6
+ import { useHasPermission } from '@orthacms/identity-admin';
7
+ import { MEDIA_CREATE, MEDIA_DELETE, MEDIA_READ, MEDIA_UPDATE, ROOT_FOLDER_ID } from '../../constants';
8
+ import { useMediaLibrary } from '../../hooks/useMediaLibrary';
9
+ import { MediaFoldersNav } from '../../components/MediaFoldersNav';
10
+ import { MediaTopBar } from '../../components/MediaTopBar';
11
+ import { MediaToolbar } from '../../components/MediaToolbar';
12
+ import { MediaSelectionBar } from '../../components/MediaSelectionBar';
13
+ import { MediaUploadBanner } from '../../components/MediaUploadBanner';
14
+ import { MediaGrid } from '../../components/MediaGrid';
15
+ import { MediaPagination } from '../../components/MediaPagination';
16
+ import { MediaEmptyState } from '../../components/MediaEmptyState';
17
+ import { AssetDetailDrawer } from '../../components/AssetDetailDrawer';
18
+ import { NewFolderDialog } from '../../components/NewFolderDialog';
19
+ import { RenameDialog } from '../../components/RenameDialog';
20
+ import { MoveAssetsDialog } from '../../components/MoveAssetsDialog';
21
+ import { UploadDialog } from '../../components/UploadDialog';
22
+ import { folderContents } from '../../utils/folderContents';
23
+ import { useDocumentTitle } from '@orthacms/utils-admin';
24
+ /** Intl descriptors for the Media Library page + its toasts, co-located. */
25
+ const messages = defineMessages({
26
+ title: { id: 'media.page.title', defaultMessage: 'Media Library' },
27
+ subtitle: {
28
+ id: 'media.page.subtitle',
29
+ defaultMessage: '{count, plural, one {# item} other {# items}} in {location}'
30
+ },
31
+ allMedia: { id: 'media.page.allMedia', defaultMessage: 'All media' },
32
+ noAccess: {
33
+ id: 'media.page.noAccess',
34
+ defaultMessage: 'You don’t have permission to view the media library.'
35
+ },
36
+ loadError: {
37
+ id: 'media.page.loadError',
38
+ defaultMessage: 'We couldn’t load the media library.'
39
+ },
40
+ retry: { id: 'media.page.retry', defaultMessage: 'Try again' },
41
+ openNav: { id: 'media.page.openNav', defaultMessage: 'Folders' },
42
+ navTitle: {
43
+ id: 'media.page.navTitle',
44
+ defaultMessage: 'Folder navigation'
45
+ },
46
+ selection: {
47
+ id: 'media.page.selection',
48
+ defaultMessage: '{count, plural, =0 {No items selected} one {# item selected} other {# items selected}}'
49
+ },
50
+ deleteAssetsTitle: {
51
+ id: 'media.page.deleteAssetsTitle',
52
+ defaultMessage: 'Delete {count, plural, one {# item} other {# items}}?'
53
+ },
54
+ deleteAssetsBody: {
55
+ id: 'media.page.deleteAssetsBody',
56
+ defaultMessage: 'This can’t be undone. The selected assets will be permanently removed.'
57
+ },
58
+ deleteFolderTitle: {
59
+ id: 'media.page.deleteFolderTitle',
60
+ defaultMessage: 'Delete “{name}”?'
61
+ },
62
+ deleteFolderTitleFull: {
63
+ id: 'media.page.deleteFolderTitleFull',
64
+ defaultMessage: 'Delete “{name}” and everything inside?'
65
+ },
66
+ deleteFolderBody: {
67
+ id: 'media.page.deleteFolderBody',
68
+ defaultMessage: 'This folder is empty. It will be permanently removed — this can’t be undone.'
69
+ },
70
+ /**
71
+ * Names exactly what goes with the folder. Both counts are always spelled
72
+ * out (`=0` included) so the sentence reads the same shape however the
73
+ * folder is filled, and nobody has to infer that "3 assets" also means the
74
+ * subfolders they sit in.
75
+ */
76
+ deleteFolderBodyFull: {
77
+ id: 'media.page.deleteFolderBodyFull',
78
+ defaultMessage: 'Deleting it also deletes {assets, plural, =0 {no assets} one {# asset} other {# assets}} and {folders, plural, =0 {no subfolders} one {# subfolder} other {# subfolders}} inside it. This can’t be undone.'
79
+ },
80
+ confirmDelete: { id: 'media.page.confirmDelete', defaultMessage: 'Delete' },
81
+ // toasts
82
+ tCreated: {
83
+ id: 'media.page.toast.created',
84
+ defaultMessage: 'Folder “{name}” created'
85
+ },
86
+ tRenamed: {
87
+ id: 'media.page.toast.renamed',
88
+ defaultMessage: 'Renamed to “{name}”'
89
+ },
90
+ tDuplicated: {
91
+ id: 'media.page.toast.duplicated',
92
+ defaultMessage: '{count, plural, one {# asset duplicated} other {# assets duplicated}}'
93
+ },
94
+ tMoved: {
95
+ id: 'media.page.toast.moved',
96
+ defaultMessage: '{count, plural, one {# asset moved} other {# assets moved}}'
97
+ },
98
+ tDeletedAssets: {
99
+ id: 'media.page.toast.deletedAssets',
100
+ defaultMessage: '{count, plural, one {# asset deleted} other {# assets deleted}}'
101
+ },
102
+ tDeletedFolder: {
103
+ id: 'media.page.toast.deletedFolder',
104
+ defaultMessage: 'Folder “{name}” deleted'
105
+ },
106
+ tDownload: {
107
+ id: 'media.page.toast.download',
108
+ defaultMessage: 'Downloading “{name}”'
109
+ },
110
+ tCopied: {
111
+ id: 'media.page.toast.copied',
112
+ defaultMessage: 'Link copied to clipboard'
113
+ },
114
+ tCopyFailed: {
115
+ id: 'media.page.toast.copyFailed',
116
+ defaultMessage: 'Couldn’t copy the link'
117
+ },
118
+ tAltSaved: {
119
+ id: 'media.page.toast.altSaved',
120
+ defaultMessage: 'Alt text saved'
121
+ },
122
+ assetsRegion: { id: 'media.page.assetsRegion', defaultMessage: 'Assets' }
123
+ });
124
+ /**
125
+ * How long a confirmed delete keeps trying to hold focus on the grid.
126
+ *
127
+ * Two overlays unwind after a delete and each restores focus to its own
128
+ * trigger, both of which sat on the tile that just went away — so whichever
129
+ * settles last can drop focus on `<body>` well after the dialog closed. The
130
+ * window is generous because that timing moves with machine load; nothing is
131
+ * taken from a control the user actually focused, so a long window costs
132
+ * nothing.
133
+ */
134
+ const FOCUS_RECLAIM_MS = 1_000;
135
+ /**
136
+ * The Media Library, mounted inside the workspace shell at
137
+ * `/workspaces/:id/media`. `useMediaLibrary` is the store — folders and the open
138
+ * folder's assets come from `@orthacms/media-server`, and every action (browse
139
+ * folders, upload, create folder, rename, duplicate, move, delete, filter, sort,
140
+ * open the detail drawer) runs against the API, firing a toast per action —
141
+ * except uploads, whose status lives in the `MediaUploadBanner`. Controls gate
142
+ * on the real `media:*` matrix via `useHasPermission`.
143
+ *
144
+ * A sticky `MediaTopBar` heads the page (the shared icon-tile + breadcrumb
145
+ * header every admin page carries), spanning both panes. Below it sits a
146
+ * two-pane layout — a folders sidebar beside the asset browser — plus the detail
147
+ * drawer and the create/rename/move/upload dialogs. Both panes sit
148
+ * **flush on the canvas** (no muted board, no bordered island), mirroring the
149
+ * Content Library's work area: the sidebar is divided by a hairline `border-r`
150
+ * and each pane scrolls independently. Sized with `flex-1` against the workspace
151
+ * shell's `min-h-svh` column — NOT its own `svh` calc — so there is exactly one
152
+ * viewport measurement in the chain; a second, independently rounded one can end
153
+ * up a pixel taller (visible at browser zoom ≠ 100%) and give the document a
154
+ * phantom scrollbar beside the pane's own.
155
+ */
156
+ export function MediaLibraryPage() {
157
+ const intl = useIntl();
158
+ // Names this route in the tab strip, the window list, the history
159
+ // and a screen reader's window announcement. Every private route but
160
+ // Workspaces was still titled a bare "Admin" (WCAG 2.4.2, `ORT-140`).
161
+ useDocumentTitle(intl.formatMessage(messages.title));
162
+ // Mirror the server's RBAC: reads gate the queries, writes gate controls.
163
+ const canRead = useHasPermission(MEDIA_READ);
164
+ const canCreate = useHasPermission(MEDIA_CREATE);
165
+ const canUpdate = useHasPermission(MEDIA_UPDATE);
166
+ const canDelete = useHasPermission(MEDIA_DELETE);
167
+ const store = useMediaLibrary(canRead);
168
+ /** Focus target after a delete removes the tile whose menu opened it. */
169
+ const gridRef = useRef(null);
170
+ /** Set by a confirmed delete, read once by the dialog's close-focus hook. */
171
+ const focusGridOnCloseRef = useRef(false);
172
+ const [navOpen, setNavOpen] = useState(false);
173
+ const [newFolderOpen, setNewFolderOpen] = useState(false);
174
+ const [uploadOpen, setUploadOpen] = useState(false);
175
+ const [renameTarget, setRenameTarget] = useState(null);
176
+ const [moveIds, setMoveIds] = useState(null);
177
+ const [deleteTarget, setDeleteTarget] = useState(null);
178
+ /**
179
+ * Puts focus on the grid and **keeps** it there for a few frames.
180
+ *
181
+ * A delete travels through two overlays — the tile's ⋯ menu and the confirm
182
+ * dialog — and each restores focus to its own trigger when it closes, on its
183
+ * own schedule. Both triggers live on the tile the delete just removed, so
184
+ * whichever lands last drops focus on `<body>`. Claiming the dialog's
185
+ * `onCloseAutoFocus` handles one of them; this reclaims from the other,
186
+ * bounded so it can never fight a focus the user chose themselves (it only
187
+ * acts while nothing at all is focused).
188
+ */
189
+ const focusGrid = () => {
190
+ const deadline = Date.now() + FOCUS_RECLAIM_MS;
191
+ const reclaim = () => {
192
+ // Re-read the ref every tick: a refetch can re-render the region.
193
+ const grid = gridRef.current;
194
+ if (!grid || document.activeElement === grid)
195
+ return;
196
+ const active = document.activeElement;
197
+ // Only step in when nothing holds focus: `<body>` after a layer
198
+ // gave up, or a node that has since left the document. A control
199
+ // the user actually moved to is left alone.
200
+ if (!active || active === document.body || !active.isConnected) {
201
+ grid.focus();
202
+ }
203
+ if (Date.now() < deadline)
204
+ window.setTimeout(reclaim, 32);
205
+ };
206
+ reclaim();
207
+ };
208
+ const locationLabel = store.currentFolder
209
+ ? store.currentFolder.name
210
+ : intl.formatMessage(messages.allMedia);
211
+ const hasFilters = store.search.trim() !== '' || store.kindFilter !== 'all';
212
+ // Sub-folders belong to the folder, not to a page of its assets, so they are
213
+ // drawn once on the first page rather than repeating above every page of
214
+ // files. On any later page the grid is assets alone.
215
+ const showFolders = store.page === 1;
216
+ const visibleFolders = showFolders ? store.childFolders : [];
217
+ // Emptiness is now a fact about the whole folder, not about what happened to
218
+ // load: `total` counts every asset matching the search and filter across it.
219
+ const isEmpty = visibleFolders.length === 0 && store.total === 0;
220
+ // Triggers a browser download for one asset (its bytes stream from the
221
+ // `media:read`-gated raw route). Shared by the row action and bulk download.
222
+ const downloadAsset = (asset) => {
223
+ const link = document.createElement('a');
224
+ link.href = asset.url;
225
+ link.download = asset.name;
226
+ document.body.appendChild(link);
227
+ link.click();
228
+ link.remove();
229
+ };
230
+ // A single dispatcher every card / row / drawer forwards asset actions to.
231
+ const handleAssetAction = (kind, asset) => {
232
+ switch (kind) {
233
+ case 'open':
234
+ store.openDetail(asset.id);
235
+ break;
236
+ case 'download': {
237
+ downloadAsset(asset);
238
+ toast.success(intl.formatMessage(messages.tDownload, { name: asset.name }));
239
+ break;
240
+ }
241
+ case 'copyLink':
242
+ // Confirm only once the clipboard actually took it — a denied
243
+ // permission used to still say "Link copied".
244
+ void navigator.clipboard
245
+ ?.writeText(new URL(asset.url, window.location.origin).href)
246
+ .then(() => toast.success(intl.formatMessage(messages.tCopied)), () => toast.error(intl.formatMessage(messages.tCopyFailed)));
247
+ break;
248
+ case 'duplicate': {
249
+ void store.duplicateAssets([asset.id]).then((ok) => {
250
+ if (ok) {
251
+ toast.success(intl.formatMessage(messages.tDuplicated, {
252
+ count: 1
253
+ }));
254
+ }
255
+ });
256
+ break;
257
+ }
258
+ case 'rename':
259
+ setRenameTarget({
260
+ kind: 'asset',
261
+ id: asset.id,
262
+ name: asset.name
263
+ });
264
+ break;
265
+ case 'move':
266
+ setMoveIds([asset.id]);
267
+ break;
268
+ case 'delete':
269
+ setDeleteTarget({ kind: 'assets', ids: [asset.id] });
270
+ break;
271
+ }
272
+ };
273
+ const handleRenameFolder = (folder) => setRenameTarget({ kind: 'folder', id: folder.id, name: folder.name });
274
+ const handleDeleteFolder = (folder) => setDeleteTarget({ kind: 'folder', folder });
275
+ const submitRename = (name) => {
276
+ if (!renameTarget)
277
+ return;
278
+ const done = renameTarget.kind === 'asset'
279
+ ? store.renameAsset(renameTarget.id, name)
280
+ : store.renameFolder(renameTarget.id, name);
281
+ void done.then((ok) => {
282
+ if (ok) {
283
+ toast.success(intl.formatMessage(messages.tRenamed, { name }));
284
+ }
285
+ });
286
+ };
287
+ const submitMove = (folderId) => {
288
+ if (!moveIds)
289
+ return;
290
+ const count = moveIds.length;
291
+ void store.moveAssets(moveIds, folderId).then((ok) => {
292
+ if (ok) {
293
+ toast.success(intl.formatMessage(messages.tMoved, { count }));
294
+ }
295
+ });
296
+ store.clearSelection();
297
+ };
298
+ const confirmDelete = () => {
299
+ if (!deleteTarget)
300
+ return;
301
+ if (deleteTarget.kind === 'assets') {
302
+ const count = deleteTarget.ids.length;
303
+ void store.deleteAssets(deleteTarget.ids).then((ok) => {
304
+ if (ok) {
305
+ toast.success(intl.formatMessage(messages.tDeletedAssets, { count }));
306
+ }
307
+ });
308
+ }
309
+ else {
310
+ const name = deleteTarget.folder.name;
311
+ void store.deleteFolder(deleteTarget.folder.id).then((ok) => {
312
+ if (ok) {
313
+ toast.success(intl.formatMessage(messages.tDeletedFolder, { name }));
314
+ }
315
+ });
316
+ }
317
+ setDeleteTarget(null);
318
+ // The dialog restores focus to whatever opened it — a tile's ⋯ trigger
319
+ // the delete is about to remove from the DOM, leaving focus on `<body>`.
320
+ // Claim the close so it lands on the grid instead. Cancelling leaves the
321
+ // flag false, so the trigger (which still exists) keeps focus.
322
+ focusGridOnCloseRef.current = true;
323
+ };
324
+ // What the pending folder delete would take with it, so the confirmation
325
+ // can say so — a folder delete cascades now, and an unqualified "Delete?"
326
+ // over a populated tree is the kind of prompt people regret answering.
327
+ const doomed = deleteTarget?.kind === 'folder'
328
+ ? folderContents(store.folders, store.folderCounts, deleteTarget.folder.id)
329
+ : null;
330
+ const isEmptyFolder = !!doomed && !doomed.assets && !doomed.folders;
331
+ const selectedIds = store.selectedAssets.map((asset) => asset.id);
332
+ if (!canRead) {
333
+ return (_jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center p-6 text-center", children: _jsx("p", { className: "max-w-sm text-sm text-muted-foreground", children: intl.formatMessage(messages.noAccess) }) }));
334
+ }
335
+ // A failed load gets its own state with a retry — never the empty state,
336
+ // which would misread a server error as "this folder is empty".
337
+ if (store.isError) {
338
+ return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col items-center justify-center gap-3 p-6 text-center", children: [_jsx("p", { className: "max-w-sm text-sm text-muted-foreground", role: "alert", children: intl.formatMessage(messages.loadError) }), _jsx(Button, { variant: "outline", onClick: store.reload, children: intl.formatMessage(messages.retry) })] }));
339
+ }
340
+ return (_jsxs("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col", children: [_jsx(MediaTopBar, { breadcrumbs: store.breadcrumbs, onNavigate: store.navigateTo }), _jsxs("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col lg:flex-row", children: [_jsx("div", { className: "border-b p-3 lg:hidden", children: _jsxs(Button, { variant: "outline", size: "sm", className: "shadow-none", onClick: () => setNavOpen(true), children: [_jsx(PanelLeft, { "aria-hidden": true }), intl.formatMessage(messages.openNav)] }) }), _jsx(MediaFoldersNav, { className: "hidden lg:flex lg:border-r", folders: store.folders, currentFolderId: store.currentFolderId, counts: store.folderCounts, rootCount: store.folderCounts.get(ROOT_FOLDER_ID) ?? 0, onNavigate: store.navigateTo, onNewFolder: () => setNewFolderOpen(true), canCreate: canCreate }), _jsx("div", { className: "min-w-0 flex-1 overflow-auto", children: _jsxs("div", { className: "p-4 sm:p-6", children: [_jsxs("div", { className: "mb-4", children: [_jsx("h1", { className: "text-2xl font-semibold tracking-[-0.01em]", children: store.currentFolder
341
+ ? store.currentFolder.name
342
+ : intl.formatMessage(messages.title) }), _jsx("p", { className: "mt-0.5 text-sm text-muted-foreground", children: intl.formatMessage(messages.subtitle, {
343
+ count: store.total,
344
+ location: locationLabel
345
+ }) })] }), _jsx(MediaToolbar, { search: store.search, onSearchChange: store.setSearch, kindFilter: store.kindFilter, onKindFilterChange: store.setKindFilter, sort: store.sort, onSortChange: store.setSort, onNewFolder: () => setNewFolderOpen(true), onUpload: () => setUploadOpen(true), canCreate: canCreate }), _jsx("p", { className: "sr-only", "aria-live": "polite", children: intl.formatMessage(messages.selection, {
346
+ count: selectedIds.length
347
+ }) }), _jsx(MediaUploadBanner, { items: store.uploadItems, summary: store.uploadSummary, onRetry: store.retryUpload, onCancel: store.cancelUpload, onDismiss: store.dismissUploads }), selectedIds.length > 0 ? (_jsx(MediaSelectionBar, { count: selectedIds.length, onDownload: () => {
348
+ store.selectedAssets.forEach(downloadAsset);
349
+ toast.success(intl.formatMessage(messages.tDownload, {
350
+ name: `${selectedIds.length} files`
351
+ }));
352
+ }, onDuplicate: () => {
353
+ const count = selectedIds.length;
354
+ void store
355
+ .duplicateAssets(selectedIds)
356
+ .then((ok) => {
357
+ if (ok) {
358
+ toast.success(intl.formatMessage(messages.tDuplicated, { count }));
359
+ }
360
+ });
361
+ }, onMove: () => setMoveIds(selectedIds), onDelete: () => setDeleteTarget({
362
+ kind: 'assets',
363
+ ids: selectedIds
364
+ }), onClear: store.clearSelection, canCreate: canCreate, canUpdate: canUpdate, canDelete: canDelete })) : null, _jsx("section", { ref: gridRef, tabIndex: -1, "aria-label": intl.formatMessage(messages.assetsRegion), "aria-busy": store.isRefreshing, className: cn('transition-opacity focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', store.isRefreshing && 'opacity-60'), children: isEmpty ? (_jsx(MediaEmptyState, { hasFilters: hasFilters, canCreate: canCreate, onUpload: () => setUploadOpen(true), onClearFilters: () => {
365
+ store.setSearch('');
366
+ store.setKindFilter('all');
367
+ } })) : (_jsx(MediaGrid, { folders: visibleFolders, assets: store.visibleAssets, folderCounts: store.folderCounts, selectedIds: store.selectedIds, onOpenFolder: store.navigateTo, onRenameFolder: handleRenameFolder, onDeleteFolder: handleDeleteFolder, onToggleSelect: store.toggleSelect, onAssetAction: handleAssetAction, canCreate: canCreate, canUpdate: canUpdate, canDelete: canDelete })) }), store.total > 0 ? (_jsx(MediaPagination, { page: store.page, pageCount: store.pageCount, pageSize: store.pageSize, total: store.total, onPageChange: store.setPage, onPageSizeChange: store.setPageSize })) : null] }) })] }), _jsx(Drawer, { direction: "left", open: navOpen, onOpenChange: setNavOpen, shouldScaleBackground: false, children: _jsxs(DrawerContent, { className: "data-[vaul-drawer-direction=left]:w-72", children: [_jsx(DrawerTitle, { className: "sr-only", children: intl.formatMessage(messages.navTitle) }), _jsx(MediaFoldersNav, { className: "w-full", folders: store.folders, currentFolderId: store.currentFolderId, counts: store.folderCounts, rootCount: store.folderCounts.get(ROOT_FOLDER_ID) ?? 0, onNavigate: (folderId) => {
368
+ store.navigateTo(folderId);
369
+ setNavOpen(false);
370
+ }, onNewFolder: () => {
371
+ setNavOpen(false);
372
+ setNewFolderOpen(true);
373
+ }, canCreate: canCreate })] }) }), _jsx(AssetDetailDrawer, { asset: store.detailAsset, locationLabel: locationLabel, onOpenChange: (open) => {
374
+ if (!open)
375
+ store.closeDetail();
376
+ }, onAction: handleAssetAction, onSaveAlt: (id, alt) => store.setAssetAlt(id, alt).then((ok) => {
377
+ if (ok) {
378
+ toast.success(intl.formatMessage(messages.tAltSaved));
379
+ }
380
+ return ok;
381
+ }), canCreate: canCreate, canUpdate: canUpdate, canDelete: canDelete }), _jsx(NewFolderDialog, { open: newFolderOpen, onOpenChange: setNewFolderOpen, locationLabel: locationLabel, onCreate: (name) => {
382
+ void store.createFolder(name).then((ok) => {
383
+ if (ok) {
384
+ toast.success(intl.formatMessage(messages.tCreated, { name }));
385
+ }
386
+ });
387
+ } }), _jsx(UploadDialog, { open: uploadOpen, onOpenChange: setUploadOpen, locationLabel: locationLabel,
388
+ // No toast here: the upload has only been *queued*. Progress and
389
+ // the per-file outcome are the banner's job — a success toast
390
+ // fired at submit time would claim a result nobody has yet.
391
+ onUpload: store.uploadFiles,
392
+ // The library's queue forwards alt to the API, so this is the
393
+ // one caller that may ask the author to describe the image.
394
+ collectAlt: true }), _jsx(RenameDialog, { open: renameTarget !== null, onOpenChange: (open) => {
395
+ if (!open)
396
+ setRenameTarget(null);
397
+ }, currentName: renameTarget?.name ?? '', onRename: submitRename }), _jsx(MoveAssetsDialog, { open: moveIds !== null, onOpenChange: (open) => {
398
+ if (!open)
399
+ setMoveIds(null);
400
+ }, folders: store.folders, count: moveIds?.length ?? 0, currentFolderId: store.currentFolderId, onMove: submitMove }), _jsx(ConfirmDialog, { open: deleteTarget !== null, onOpenChange: (open) => {
401
+ if (!open)
402
+ setDeleteTarget(null);
403
+ }, title: deleteTarget?.kind === 'folder'
404
+ ? intl.formatMessage(isEmptyFolder
405
+ ? messages.deleteFolderTitle
406
+ : messages.deleteFolderTitleFull, { name: deleteTarget.folder.name })
407
+ : intl.formatMessage(messages.deleteAssetsTitle, {
408
+ count: deleteTarget?.kind === 'assets'
409
+ ? deleteTarget.ids.length
410
+ : 0
411
+ }), description: deleteTarget?.kind === 'folder'
412
+ ? isEmptyFolder
413
+ ? intl.formatMessage(messages.deleteFolderBody)
414
+ : intl.formatMessage(messages.deleteFolderBodyFull, {
415
+ assets: doomed?.assets ?? 0,
416
+ folders: doomed?.folders ?? 0
417
+ })
418
+ : intl.formatMessage(messages.deleteAssetsBody), confirmLabel: intl.formatMessage(messages.confirmDelete), confirmVariant: "destructive", onCloseAutoFocus: (event) => {
419
+ if (!focusGridOnCloseRef.current)
420
+ return;
421
+ focusGridOnCloseRef.current = false;
422
+ event.preventDefault();
423
+ focusGrid();
424
+ }, onConfirm: confirmDelete })] }));
425
+ }
@@ -0,0 +1,44 @@
1
+ import type { MediaKind } from '../../constants';
2
+ /**
3
+ * A single media asset in the library — an uploaded file (image, video, audio,
4
+ * document, or archive) with its display metadata. This is the admin's model;
5
+ * once a media server exists it will be mapped from that API's wire shape.
6
+ */
7
+ export type MediaAsset = {
8
+ /** Stable unique id. */
9
+ id: string;
10
+ /** File name shown to the user (includes extension). */
11
+ name: string;
12
+ /** Coarse category driving the icon, badge, and kind filter. */
13
+ kind: MediaKind;
14
+ /** Route the browser fetches to stream the original bytes (download). */
15
+ url: string;
16
+ /** Small (~320px) WebP derivative for grid tiles; `undefined` if none. */
17
+ thumbUrl?: string;
18
+ /** Larger (~1280px) WebP derivative for the detail drawer; `undefined` if none. */
19
+ previewUrl?: string;
20
+ /** MIME type, e.g. `image/png`. */
21
+ mimeType: string;
22
+ /** File size in bytes. */
23
+ size: number;
24
+ /** Id of the folder the asset lives in (`ROOT_FOLDER_ID` at the top level). */
25
+ folderId: string;
26
+ /** Pixel dimensions for visual assets (`undefined` for audio/documents). */
27
+ dimensions?: {
28
+ width: number;
29
+ height: number;
30
+ };
31
+ /** Playback duration in seconds for audio/video. */
32
+ duration?: number;
33
+ /** Free-form labels for filtering and organisation. */
34
+ tags: string[];
35
+ /** Alternative text (accessibility) for images. */
36
+ alt?: string;
37
+ /** Display name of the member who uploaded it. */
38
+ uploadedBy: string;
39
+ /** ISO timestamp the asset was created/uploaded. */
40
+ createdAt: string;
41
+ /** ISO timestamp the asset was last modified. */
42
+ updatedAt: string;
43
+ };
44
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/types/mediaAsset/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG;IACrB,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,IAAI,EAAE,SAAS,CAAC;IAChB,yEAAyE;IACzE,GAAG,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;CACrB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,48 @@
1
+ /**
2
+ * One attached asset as a media **field** renders it — the merge of what the
3
+ * server resolved (`MediaRef`) and what the editor learned locally by picking or
4
+ * uploading (`MediaAsset`). A ref carries no size/dimensions, so those are
5
+ * optional: the tile shows whichever facts it has.
6
+ */
7
+ export type MediaFieldDisplay = {
8
+ /** The asset id stored in the field value. */
9
+ id: string;
10
+ /** File name, or the raw id while nothing else is known yet. */
11
+ name: string;
12
+ /**
13
+ * What the tile renders — the small derivative when the asset has one, else
14
+ * the original. A tile is ~180px wide, so pulling a full-size original for
15
+ * it is pure waste; a staged (not yet uploaded) file uses its local object
16
+ * URL here, which is the same idea from the other direction.
17
+ */
18
+ url: string;
19
+ /** The original bytes — the "open in a new tab" target, never the tile's `src`. */
20
+ originalUrl?: string;
21
+ /** Coarse kind; empty string when the asset hasn't been resolved yet. */
22
+ kind: string;
23
+ mimeType: string;
24
+ /** File size in bytes — known only for a picked/uploaded asset. */
25
+ size?: number;
26
+ /** Pixel dimensions — known only for a picked/uploaded visual asset. */
27
+ dimensions?: {
28
+ width: number;
29
+ height: number;
30
+ };
31
+ /** The id resolved to nothing: deleted, or in another workspace. */
32
+ missing?: boolean;
33
+ /**
34
+ * A file staged on the field but **not uploaded yet** — it goes up with the
35
+ * record on Save. `url` is a local object URL (images only) and `id` is the
36
+ * placeholder standing in for the asset.
37
+ */
38
+ pending?: boolean;
39
+ /**
40
+ * The id is real but nothing is known about it yet — the entry's media read
41
+ * is still in flight. The tile renders a placeholder rather than guessing
42
+ * the asset's raw route: guessing meant every edit-mode open fetched
43
+ * **full-size originals** for the moment before the refs landed (and
44
+ * labelled each tile with a uuid).
45
+ */
46
+ resolving?: boolean;
47
+ };
48
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/types/mediaFieldDisplay/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC5B,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ /**
2
+ * A folder grouping assets in the library. Folders form a tree via `parentId`;
3
+ * the top level is the synthetic root (`ROOT_FOLDER_ID`), which has no folder
4
+ * record of its own. This is the admin's model, mirrored from the future API.
5
+ */
6
+ export type MediaFolder = {
7
+ /** Stable unique id. */
8
+ id: string;
9
+ /** Folder name shown to the user. */
10
+ name: string;
11
+ /** Parent folder id (`ROOT_FOLDER_ID` for a top-level folder). */
12
+ parentId: string;
13
+ /** ISO timestamp the folder was created. */
14
+ createdAt: string;
15
+ };
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/types/mediaFolder/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACtB,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;CACrB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,38 @@
1
+ /**
2
+ * One file staged on a media field and **not yet uploaded** — the record is
3
+ * saved first, and only then do the bytes move (see `usePendingMediaUploads`).
4
+ */
5
+ export type PendingUpload = {
6
+ /**
7
+ * The placeholder id standing in for the asset in the form value. A real
8
+ * uuid on purpose: the shared kernel validates a media value as a uuid (or
9
+ * uuid[]), so a staged file satisfies client validation and the publish gate
10
+ * exactly like an attached asset would.
11
+ */
12
+ id: string;
13
+ /** The bytes to send when the record is saved. */
14
+ file: File;
15
+ /** Object URL for an image preview; revoked when the entry is dropped. */
16
+ previewUrl?: string;
17
+ /**
18
+ * The real asset id, once this file has been uploaded. Set when a save's
19
+ * upload step succeeded but the write behind it did not — so a retry attaches
20
+ * the asset already in the library instead of uploading it a second time.
21
+ */
22
+ uploadedId?: string;
23
+ };
24
+ /**
25
+ * What a media field needs in order to stage files — the handle
26
+ * `usePendingMediaUploads` publishes through the entry editor's presave slot, so
27
+ * the staging outlives the Media tab's body (tabs are routes; the panel unmounts
28
+ * on every tab switch).
29
+ */
30
+ export type MediaPendingUploads = {
31
+ /** Every staged file, keyed by its placeholder id. */
32
+ pending: ReadonlyMap<string, PendingUpload>;
33
+ /** Stage files and return their placeholder ids, in order. */
34
+ stage: (files: File[]) => string[];
35
+ /** Forget one staged file (removing it from a field also drops it here). */
36
+ drop: (id: string) => void;
37
+ };
38
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/types/pendingUpload/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IACxB;;;;;OAKG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,kDAAkD;IAClD,IAAI,EAAE,IAAI,CAAC;IACX,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAC9B,sDAAsD;IACtD,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC5C,8DAA8D;IAC9D,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;IACnC,4EAA4E;IAC5E,IAAI,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ import type { UploadStatus } from '../../constants';
2
+ /**
3
+ * One file in the upload queue — the view model the progress banner renders.
4
+ * The `File` itself is **not** here: it lives in the queue hook's ref map, so a
5
+ * progress tick re-renders a small serializable row instead of dragging a blob
6
+ * through React state.
7
+ */
8
+ export type UploadItem = {
9
+ /** Queue-local id (files have no stable identity of their own). */
10
+ id: string;
11
+ name: string;
12
+ /** Bytes, used both for display and to weight the batch percentage. */
13
+ size: number;
14
+ /** Whole-number percent of bytes sent, 0–100. */
15
+ progress: number;
16
+ status: UploadStatus;
17
+ /** Localized failure reason; set only when `status` is `failed`. */
18
+ error?: string;
19
+ };
20
+ /** The batch roll-up the banner headline reads from. */
21
+ export type UploadSummary = {
22
+ /** Every item still pending or uploading. */
23
+ inFlight: number;
24
+ done: number;
25
+ failed: number;
26
+ /** Items in the batch, excluding cancelled ones. */
27
+ total: number;
28
+ /**
29
+ * Whole-number percent across the batch, **weighted by file size** — ten
30
+ * thumbnails finishing shouldn't read as 90% while a 200 MB video is still
31
+ * going.
32
+ */
33
+ percent: number;
34
+ /** True while anything is pending or uploading. */
35
+ active: boolean;
36
+ };
37
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/types/upload/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IACrB,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;IACrB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wDAAwD;AACxD,MAAM,MAAM,aAAa,GAAG;IACxB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,MAAM,EAAE,OAAO,CAAC;CACnB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};