@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,8 @@
1
+ /**
2
+ * Derives a stable, pleasant two-stop CSS gradient from an asset id. Used as a
3
+ * self-contained stand-in thumbnail so the mockup needs no real image files or
4
+ * network — the same id always yields the same gradient, so tiles stay stable
5
+ * across renders. Presentation-only.
6
+ */
7
+ export declare function assetGradient(id: string): string;
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/utils/assetGradient/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAShD"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Derives a stable, pleasant two-stop CSS gradient from an asset id. Used as a
3
+ * self-contained stand-in thumbnail so the mockup needs no real image files or
4
+ * network — the same id always yields the same gradient, so tiles stay stable
5
+ * across renders. Presentation-only.
6
+ */
7
+ export function assetGradient(id) {
8
+ // A small FNV-ish hash so the hue is deterministic per id.
9
+ let hash = 0;
10
+ for (let i = 0; i < id.length; i += 1) {
11
+ hash = (hash * 31 + id.charCodeAt(i)) >>> 0;
12
+ }
13
+ const hue = hash % 360;
14
+ const hue2 = (hue + 40) % 360;
15
+ return `linear-gradient(135deg, hsl(${hue} 70% 62%), hsl(${hue2} 68% 48%))`;
16
+ }
@@ -0,0 +1,11 @@
1
+ import type { AssetActionHandlers, AssetActionKind } from '../../components/AssetActionsMenu';
2
+ import type { MediaAsset } from '../../types/mediaAsset';
3
+ /**
4
+ * Adapts a single `(kind, asset)` dispatcher into the discrete
5
+ * {@link AssetActionHandlers} the menu expects. Lets the grid tile and the
6
+ * detail drawer forward every asset action to one page-level handler instead of
7
+ * drilling six callbacks each. `'open'` is not among them — it isn't a menu
8
+ * item; the tile's thumbnail and filename dispatch it directly.
9
+ */
10
+ export declare function buildAssetHandlers(asset: MediaAsset, onAction: (kind: AssetActionKind, asset: MediaAsset) => void): AssetActionHandlers;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/utils/assetHandlers/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,mBAAmB,EACnB,eAAe,EAClB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAC9B,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,GAC7D,mBAAmB,CASrB"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Adapts a single `(kind, asset)` dispatcher into the discrete
3
+ * {@link AssetActionHandlers} the menu expects. Lets the grid tile and the
4
+ * detail drawer forward every asset action to one page-level handler instead of
5
+ * drilling six callbacks each. `'open'` is not among them — it isn't a menu
6
+ * item; the tile's thumbnail and filename dispatch it directly.
7
+ */
8
+ export function buildAssetHandlers(asset, onAction) {
9
+ return {
10
+ onDownload: () => onAction('download', asset),
11
+ onCopyLink: () => onAction('copyLink', asset),
12
+ onDuplicate: () => onAction('duplicate', asset),
13
+ onRename: () => onAction('rename', asset),
14
+ onMove: () => onAction('move', asset),
15
+ onDelete: () => onAction('delete', asset)
16
+ };
17
+ }
@@ -0,0 +1,17 @@
1
+ import type { MediaFolder } from '../../types/mediaFolder';
2
+ /** What a folder holds, counted through its whole subtree. */
3
+ export type FolderContents = {
4
+ /** Descendant folders (the folder itself excluded). */
5
+ folders: number;
6
+ /** Assets anywhere below, including directly inside. */
7
+ assets: number;
8
+ };
9
+ /**
10
+ * Counts everything a folder holds, recursively — what a delete would take with
11
+ * it. Derived from the store's already-loaded tree (`folders` + the per-folder
12
+ * `folderCounts` the list endpoint returns), so naming the damage in a
13
+ * confirmation costs no extra request; it is as fresh as the last folders read,
14
+ * which every mutation invalidates.
15
+ */
16
+ export declare function folderContents(folders: readonly MediaFolder[], assetCounts: ReadonlyMap<string, number>, folderId: string): FolderContents;
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/utils/folderContents/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,8DAA8D;AAC9D,MAAM,MAAM,cAAc,GAAG;IACzB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC1B,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACxC,QAAQ,EAAE,MAAM,GACjB,cAAc,CA0BhB"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Counts everything a folder holds, recursively — what a delete would take with
3
+ * it. Derived from the store's already-loaded tree (`folders` + the per-folder
4
+ * `folderCounts` the list endpoint returns), so naming the damage in a
5
+ * confirmation costs no extra request; it is as fresh as the last folders read,
6
+ * which every mutation invalidates.
7
+ */
8
+ export function folderContents(folders, assetCounts, folderId) {
9
+ const childrenOf = new Map();
10
+ for (const folder of folders) {
11
+ const siblings = childrenOf.get(folder.parentId) ?? [];
12
+ siblings.push(folder);
13
+ childrenOf.set(folder.parentId, siblings);
14
+ }
15
+ let descendants = 0;
16
+ let assets = assetCounts.get(folderId) ?? 0;
17
+ // Iterative, not recursive: a cycle in a malformed tree would otherwise
18
+ // blow the stack, and the visited set makes that impossible.
19
+ const seen = new Set([folderId]);
20
+ const queue = [folderId];
21
+ while (queue.length) {
22
+ const current = queue.pop();
23
+ if (!current)
24
+ break;
25
+ for (const child of childrenOf.get(current) ?? []) {
26
+ if (seen.has(child.id))
27
+ continue;
28
+ seen.add(child.id);
29
+ descendants += 1;
30
+ assets += assetCounts.get(child.id) ?? 0;
31
+ queue.push(child.id);
32
+ }
33
+ }
34
+ return { folders: descendants, assets };
35
+ }
@@ -0,0 +1,13 @@
1
+ import type { MediaFolder } from '../../types/mediaFolder';
2
+ /** A folder paired with its depth below the root, for indented rendering. */
3
+ export type FolderTreeNode = {
4
+ folder: MediaFolder;
5
+ depth: number;
6
+ };
7
+ /**
8
+ * Flattens the folder forest into a depth-first, name-sorted list annotated with
9
+ * each folder's depth below the root. Shared by the folders nav tree and the
10
+ * move-to-folder picker so both render the same ordering and indentation.
11
+ */
12
+ export declare function flattenFolderTree(folders: MediaFolder[], parentId?: string, depth?: number): FolderTreeNode[];
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/utils/folderTree/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,6EAA6E;AAC7E,MAAM,MAAM,cAAc,GAAG;IACzB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC7B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,GAAE,MAAuB,EACjC,KAAK,SAAI,GACV,cAAc,EAAE,CAQlB"}
@@ -0,0 +1,15 @@
1
+ import { ROOT_FOLDER_ID } from '../../constants';
2
+ /**
3
+ * Flattens the folder forest into a depth-first, name-sorted list annotated with
4
+ * each folder's depth below the root. Shared by the folders nav tree and the
5
+ * move-to-folder picker so both render the same ordering and indentation.
6
+ */
7
+ export function flattenFolderTree(folders, parentId = ROOT_FOLDER_ID, depth = 0) {
8
+ return folders
9
+ .filter((folder) => folder.parentId === parentId)
10
+ .sort((a, b) => a.name.localeCompare(b.name))
11
+ .flatMap((folder) => [
12
+ { folder, depth },
13
+ ...flattenFolderTree(folders, folder.id, depth + 1)
14
+ ]);
15
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Formats a byte count as a short human-readable size (e.g. `1.4 MB`). Uses
3
+ * binary units and trims a trailing `.0`. Presentation-only — safe for labels.
4
+ */
5
+ export declare function formatBytes(bytes: number): string;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/utils/formatBytes/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAYjD"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Formats a byte count as a short human-readable size (e.g. `1.4 MB`). Uses
3
+ * binary units and trims a trailing `.0`. Presentation-only — safe for labels.
4
+ */
5
+ export function formatBytes(bytes) {
6
+ if (bytes < 1024)
7
+ return `${bytes} B`;
8
+ const units = ['KB', 'MB', 'GB', 'TB'];
9
+ let value = bytes / 1024;
10
+ let unit = 0;
11
+ while (value >= 1024 && unit < units.length - 1) {
12
+ value /= 1024;
13
+ unit += 1;
14
+ }
15
+ const rounded = value >= 10 ? Math.round(value) : Math.round(value * 10) / 10;
16
+ return `${rounded} ${units[unit]}`;
17
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Formats a duration in seconds as `m:ss` (or `h:mm:ss` past an hour), for the
3
+ * playback length shown on audio/video assets. Presentation-only.
4
+ */
5
+ export declare function formatDuration(seconds: number): string;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/utils/formatDuration/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQtD"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Formats a duration in seconds as `m:ss` (or `h:mm:ss` past an hour), for the
3
+ * playback length shown on audio/video assets. Presentation-only.
4
+ */
5
+ export function formatDuration(seconds) {
6
+ const hrs = Math.floor(seconds / 3600);
7
+ const mins = Math.floor((seconds % 3600) / 60);
8
+ const secs = Math.floor(seconds % 60);
9
+ const pad = (n) => n.toString().padStart(2, '0');
10
+ return hrs > 0
11
+ ? `${hrs}:${pad(mins)}:${pad(secs)}`
12
+ : `${mins}:${pad(secs)}`;
13
+ }
@@ -0,0 +1,10 @@
1
+ import { type MediaKind } from '../../constants';
2
+ /**
3
+ * The coarse {@link MediaKind} for a MIME type. A **local, deliberately rough**
4
+ * classification for a file that hasn't been uploaded yet (a staged upload's
5
+ * preview tile) — the server owns the real one, and it's the server's `kind`
6
+ * that ends up on the asset. Unknown types read as a document, which is what the
7
+ * glyph fallback should be anyway.
8
+ */
9
+ export declare function kindFromMime(mimeType: string): MediaKind;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/utils/kindFromMime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAW7D;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAMxD"}
@@ -0,0 +1,27 @@
1
+ import { MEDIA_KIND } from '../../constants';
2
+ /** MIME types the library counts as an archive. */
3
+ const ARCHIVE_TYPES = [
4
+ 'application/zip',
5
+ 'application/x-tar',
6
+ 'application/gzip',
7
+ 'application/x-7z-compressed',
8
+ 'application/vnd.rar'
9
+ ];
10
+ /**
11
+ * The coarse {@link MediaKind} for a MIME type. A **local, deliberately rough**
12
+ * classification for a file that hasn't been uploaded yet (a staged upload's
13
+ * preview tile) — the server owns the real one, and it's the server's `kind`
14
+ * that ends up on the asset. Unknown types read as a document, which is what the
15
+ * glyph fallback should be anyway.
16
+ */
17
+ export function kindFromMime(mimeType) {
18
+ if (mimeType.startsWith('image/'))
19
+ return MEDIA_KIND.Image;
20
+ if (mimeType.startsWith('video/'))
21
+ return MEDIA_KIND.Video;
22
+ if (mimeType.startsWith('audio/'))
23
+ return MEDIA_KIND.Audio;
24
+ if (ARCHIVE_TYPES.includes(mimeType))
25
+ return MEDIA_KIND.Archive;
26
+ return MEDIA_KIND.Document;
27
+ }
@@ -0,0 +1,30 @@
1
+ import type { MediaAsset } from '../../types/mediaAsset';
2
+ /** A media field's accepted-asset restriction (mirrors the server's `accept`). */
3
+ export type MediaAccept = {
4
+ kinds?: readonly string[];
5
+ mimeTypes?: readonly string[];
6
+ };
7
+ /**
8
+ * Whether an asset satisfies a media field's `accept` — the admin mirror of the
9
+ * server's `acceptsAsset`, used to pre-filter the picker's candidates so a user
10
+ * can't select a disallowed asset (the server still enforces it on save). An
11
+ * absent restriction, or one with neither list, accepts anything.
12
+ */
13
+ export declare function acceptsAsset(accept: MediaAccept | undefined, asset: Pick<MediaAsset, 'kind' | 'mimeType'>): boolean;
14
+ /**
15
+ * Whether a **local file** satisfies a media field's `accept`, judged from its
16
+ * MIME type before it is uploaded — so a file staged on a field is checked at
17
+ * the moment it's chosen rather than after the save uploads it. The kind is the
18
+ * rough local classification ({@link kindFromMime}); the server re-checks the
19
+ * real asset on save, which is the enforcing pass.
20
+ */
21
+ export declare function acceptsFile(accept: MediaAccept | undefined, file: File): boolean;
22
+ /**
23
+ * The single coarse `kind` an `accept` allows, when it names exactly one and no
24
+ * MIME list — so the picker can seed the server's `?kind=` filter. Otherwise
25
+ * `undefined` (no server-side narrowing; filtered client-side).
26
+ */
27
+ export declare function singleAcceptKind(accept: MediaAccept | undefined): string | undefined;
28
+ /** A short human description of what a field accepts, for the empty-state hint. */
29
+ export declare function describeAccept(accept: MediaAccept | undefined): string | null;
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/utils/mediaAccept/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD,kFAAkF;AAClF,MAAM,MAAM,WAAW,GAAG;IACtB,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC,CAAC;AAUF;;;;;GAKG;AACH,wBAAgB,YAAY,CACxB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,CAAC,GAC7C,OAAO,CAQT;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACvB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,IAAI,EAAE,IAAI,GACX,OAAO,CAKT;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC5B,MAAM,EAAE,WAAW,GAAG,SAAS,GAChC,MAAM,GAAG,SAAS,CAKpB;AAED,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAM7E"}
@@ -0,0 +1,63 @@
1
+ import { kindFromMime } from '../kindFromMime';
2
+ /** A MIME pattern matches a concrete MIME exactly or as a `type/*` glob. */
3
+ function mimeMatches(pattern, mimeType) {
4
+ if (pattern === mimeType)
5
+ return true;
6
+ if (pattern.endsWith('/*'))
7
+ return mimeType.startsWith(pattern.slice(0, -1));
8
+ return false;
9
+ }
10
+ /**
11
+ * Whether an asset satisfies a media field's `accept` — the admin mirror of the
12
+ * server's `acceptsAsset`, used to pre-filter the picker's candidates so a user
13
+ * can't select a disallowed asset (the server still enforces it on save). An
14
+ * absent restriction, or one with neither list, accepts anything.
15
+ */
16
+ export function acceptsAsset(accept, asset) {
17
+ if (!accept)
18
+ return true;
19
+ const kinds = accept.kinds ?? [];
20
+ const mimeTypes = accept.mimeTypes ?? [];
21
+ if (!kinds.length && !mimeTypes.length)
22
+ return true;
23
+ if (kinds.includes(asset.kind))
24
+ return true;
25
+ if (mimeTypes.some((p) => mimeMatches(p, asset.mimeType)))
26
+ return true;
27
+ return false;
28
+ }
29
+ /**
30
+ * Whether a **local file** satisfies a media field's `accept`, judged from its
31
+ * MIME type before it is uploaded — so a file staged on a field is checked at
32
+ * the moment it's chosen rather than after the save uploads it. The kind is the
33
+ * rough local classification ({@link kindFromMime}); the server re-checks the
34
+ * real asset on save, which is the enforcing pass.
35
+ */
36
+ export function acceptsFile(accept, file) {
37
+ return acceptsAsset(accept, {
38
+ kind: kindFromMime(file.type),
39
+ mimeType: file.type
40
+ });
41
+ }
42
+ /**
43
+ * The single coarse `kind` an `accept` allows, when it names exactly one and no
44
+ * MIME list — so the picker can seed the server's `?kind=` filter. Otherwise
45
+ * `undefined` (no server-side narrowing; filtered client-side).
46
+ */
47
+ export function singleAcceptKind(accept) {
48
+ if (accept && !accept.mimeTypes?.length && accept.kinds?.length === 1) {
49
+ return accept.kinds[0];
50
+ }
51
+ return undefined;
52
+ }
53
+ /** A short human description of what a field accepts, for the empty-state hint. */
54
+ export function describeAccept(accept) {
55
+ if (!accept)
56
+ return null;
57
+ const parts = [];
58
+ if (accept.kinds?.length)
59
+ parts.push(accept.kinds.join(' / '));
60
+ if (accept.mimeTypes?.length)
61
+ parts.push(accept.mimeTypes.join(' / '));
62
+ return parts.length ? parts.join(', ') : null;
63
+ }
@@ -0,0 +1,15 @@
1
+ import type { AdminPlugin } from '@orthacms/bootstrap-admin';
2
+ /**
3
+ * Admin-side media plugin shape. A thin alias of {@link AdminPlugin}, kept named
4
+ * so future config has a home.
5
+ */
6
+ export type MediaAdminPlugin = AdminPlugin;
7
+ /**
8
+ * Creates the admin-side Media Library plugin. It lives **strictly inside a
9
+ * workspace**: it contributes no top-level route and no top-toolbar nav entry,
10
+ * only a rail button (`order: 20`) + a route to the workspace shell's slots
11
+ * (owned by `@orthacms/workspaces-admin`). Register it after
12
+ * `WorkspacesPlugin()` so those slots exist.
13
+ */
14
+ export declare function MediaPlugin(): MediaAdminPlugin;
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/utils/mediaPlugin/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AA6C7D;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,WAAW,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,WAAW,IAAI,gBAAgB,CAqJ9C"}
@@ -0,0 +1,177 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Suspense, lazy } from 'react';
3
+ import { WORKSPACE_ROUTE_SLOT, WORKSPACE_NAV_SLOT } from '@orthacms/workspaces-admin';
4
+ import { CONTENT_FIELD_TYPE, ENTRY_PRESAVE_SLOT, ENTRY_TAB, ENTRY_TAB_SLOT } from '@orthacms/content-admin';
5
+ import { WYSIWYG_MEDIA_SLOT } from '@orthacms/wysiwyg-admin';
6
+ import { INSIGHTS_SECTION_IDS, INSIGHTS_WIDGET_SLOT } from '@orthacms/insights-admin';
7
+ import { Image, Upload } from 'lucide-react';
8
+ import { MediaStorageStat } from '../../components/MediaStorageStat';
9
+ import { MediaStorageWidget } from '../../components/MediaStorageWidget';
10
+ import { MediaUploadsWidget } from '../../components/MediaUploadsWidget';
11
+ import { MediaAltTextWidget } from '../../components/MediaAltTextWidget';
12
+ import { EntryMediaTab } from '../../components/EntryMediaTab';
13
+ import { MediaLibraryPageSkeleton } from '../../components/MediaLibrarySkeleton';
14
+ import { WysiwygLibrarySource } from '../../components/WysiwygLibrarySource';
15
+ import { WysiwygUploadSource } from '../../components/WysiwygUploadSource';
16
+ import { MEDIA_PRESAVE_ID, usePendingMediaUploads } from '../../hooks/usePendingMediaUploads';
17
+ import { MEDIA_READ } from '../../constants';
18
+ const MediaLibraryPage = lazy(() => import('../../pages/MediaLibraryPage').then((module) => ({
19
+ default: module.MediaLibraryPage
20
+ })));
21
+ /** The Media tab applies to any type that declares a media field. */
22
+ function hasMediaField(schema) {
23
+ return schema.fields.some((field) => field.type === CONTENT_FIELD_TYPE.Media);
24
+ }
25
+ /**
26
+ * Creates the admin-side Media Library plugin. It lives **strictly inside a
27
+ * workspace**: it contributes no top-level route and no top-toolbar nav entry,
28
+ * only a rail button (`order: 20`) + a route to the workspace shell's slots
29
+ * (owned by `@orthacms/workspaces-admin`). Register it after
30
+ * `WorkspacesPlugin()` so those slots exist.
31
+ */
32
+ export function MediaPlugin() {
33
+ return {
34
+ name: 'media',
35
+ slots: [
36
+ {
37
+ // Insights widgets. Media owns `media_asset`, so it owns the
38
+ // cards reading it — the Insights plugin knows nothing about
39
+ // assets, kinds or bytes.
40
+ slot: INSIGHTS_WIDGET_SLOT,
41
+ items: [
42
+ {
43
+ id: 'insights.media.storage',
44
+ section: INSIGHTS_SECTION_IDS.Overview,
45
+ order: 40,
46
+ size: 'xs',
47
+ permission: MEDIA_READ,
48
+ titleId: 'media.insights.storage.label',
49
+ defaultTitle: 'Media storage',
50
+ Component: MediaStorageStat
51
+ },
52
+ {
53
+ id: 'insights.media.storageBreakdown',
54
+ section: INSIGHTS_SECTION_IDS.Reach,
55
+ order: 10,
56
+ size: 'sm',
57
+ permission: MEDIA_READ,
58
+ titleId: 'media.insights.storageBreakdown.title',
59
+ defaultTitle: "What's using the storage",
60
+ Component: MediaStorageWidget
61
+ },
62
+ {
63
+ id: 'insights.media.uploads',
64
+ section: INSIGHTS_SECTION_IDS.Reach,
65
+ order: 20,
66
+ size: 'sm',
67
+ permission: MEDIA_READ,
68
+ titleId: 'media.insights.uploads.title',
69
+ defaultTitle: 'Uploads',
70
+ Component: MediaUploadsWidget
71
+ },
72
+ {
73
+ id: 'insights.media.alt',
74
+ section: INSIGHTS_SECTION_IDS.Reach,
75
+ order: 30,
76
+ size: 'sm',
77
+ permission: MEDIA_READ,
78
+ titleId: 'media.insights.alt.title',
79
+ defaultTitle: 'Images missing alt text',
80
+ Component: MediaAltTextWidget
81
+ }
82
+ ]
83
+ },
84
+ {
85
+ slot: WORKSPACE_NAV_SLOT,
86
+ items: [
87
+ {
88
+ labelId: 'media.nav.label',
89
+ defaultLabel: 'Media Library',
90
+ to: 'media',
91
+ order: 20,
92
+ icon: Image,
93
+ iconColor: 'text-nav-teal'
94
+ }
95
+ ]
96
+ },
97
+ {
98
+ slot: WORKSPACE_ROUTE_SLOT,
99
+ items: [
100
+ {
101
+ path: 'media/*',
102
+ element: (_jsx(Suspense, { fallback: _jsx(MediaLibraryPageSkeleton, {}), children: _jsx(MediaLibraryPage, {}) }))
103
+ }
104
+ ]
105
+ },
106
+ // The entry editor's Media tab — rendered by content-admin's
107
+ // ENTRY_TAB_SLOT only when the open type has a media field, so the
108
+ // tab appears exactly where media applies. Its `slug` is content's
109
+ // own `media` tab route, already accepted by the tab router.
110
+ {
111
+ slot: ENTRY_TAB_SLOT,
112
+ items: [
113
+ {
114
+ id: 'media.entry.tab',
115
+ slug: ENTRY_TAB.Media,
116
+ label: {
117
+ id: 'media.tab.label',
118
+ defaultMessage: 'Media'
119
+ },
120
+ order: 10,
121
+ appliesTo: hasMediaField,
122
+ Component: EntryMediaTab
123
+ }
124
+ ]
125
+ },
126
+ // Rich text gets the library too. The editor
127
+ // (`@orthacms/wysiwyg-admin`) declares `WYSIWYG_MEDIA_SLOT` and
128
+ // knows only how to *hold* an image or a video; browsing folders
129
+ // and uploading are this plugin's job, so it fills the seam rather
130
+ // than the editor importing a library it would then be pinned to.
131
+ //
132
+ // Two entries, because they are different acts: pick something that
133
+ // exists, or add something that doesn't. Both end with an asset in
134
+ // the Media Library — an image in a body is a first-class asset,
135
+ // not an orphan attachment.
136
+ {
137
+ slot: WYSIWYG_MEDIA_SLOT,
138
+ items: [
139
+ {
140
+ id: 'media.wysiwyg.library',
141
+ label: {
142
+ id: 'media.wysiwyg.library',
143
+ defaultMessage: 'Media Library…'
144
+ },
145
+ icon: Image,
146
+ order: 10,
147
+ Source: WysiwygLibrarySource
148
+ },
149
+ {
150
+ id: 'media.wysiwyg.upload',
151
+ label: {
152
+ id: 'media.wysiwyg.upload',
153
+ defaultMessage: 'Upload files…'
154
+ },
155
+ icon: Upload,
156
+ order: 20,
157
+ Source: WysiwygUploadSource
158
+ }
159
+ ]
160
+ },
161
+ // Files chosen on a media field are staged, not uploaded: this step
162
+ // runs inside the record's save and puts them in the library then,
163
+ // so an abandoned edit leaves no orphan assets behind. It also owns
164
+ // the staging state, mounted above the tab body (a route) so it
165
+ // survives switching tabs.
166
+ {
167
+ slot: ENTRY_PRESAVE_SLOT,
168
+ items: [
169
+ {
170
+ id: MEDIA_PRESAVE_ID,
171
+ usePresave: usePendingMediaUploads
172
+ }
173
+ ]
174
+ }
175
+ ]
176
+ };
177
+ }
@@ -0,0 +1,23 @@
1
+ import type { WysiwygMediaEmbed } from '@orthacms/wysiwyg-admin';
2
+ import type { MediaAsset } from '../../types/mediaAsset';
3
+ /**
4
+ * A library asset as the rich-text editor's embed — the one place this plugin
5
+ * translates its own model into the editor's.
6
+ *
7
+ * Returns `null` for an asset the editor has no node for (audio, a PDF, a zip).
8
+ * The picker already filters those out; this is the second gate, so a caller
9
+ * that skips the filter can't put a `<img src="report.pdf">` in a body.
10
+ *
11
+ * ### Which URL
12
+ *
13
+ * An image embeds its **`previewUrl`** — the ~1280px derivative — not the
14
+ * original. A body rendered on a page has no use for a 12-megapixel JPEG, and
15
+ * the derivative is what the library generated it for. Falling back to `url`
16
+ * covers an SVG or a small image that got no derivative. A video always embeds
17
+ * `url`: there are no video derivatives, and the tag streams it.
18
+ *
19
+ * The asset's own `alt` rides along, so an author who wrote it once in the
20
+ * library doesn't write it again per body.
21
+ */
22
+ export declare function toWysiwygEmbed(asset: MediaAsset): WysiwygMediaEmbed | null;
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/utils/toWysiwygEmbed/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAGjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,iBAAiB,GAAG,IAAI,CAsB1E"}
@@ -0,0 +1,44 @@
1
+ import { WYSIWYG_MEDIA_KIND } from '@orthacms/wysiwyg-admin';
2
+ import { MEDIA_KIND } from '../../constants';
3
+ /**
4
+ * A library asset as the rich-text editor's embed — the one place this plugin
5
+ * translates its own model into the editor's.
6
+ *
7
+ * Returns `null` for an asset the editor has no node for (audio, a PDF, a zip).
8
+ * The picker already filters those out; this is the second gate, so a caller
9
+ * that skips the filter can't put a `<img src="report.pdf">` in a body.
10
+ *
11
+ * ### Which URL
12
+ *
13
+ * An image embeds its **`previewUrl`** — the ~1280px derivative — not the
14
+ * original. A body rendered on a page has no use for a 12-megapixel JPEG, and
15
+ * the derivative is what the library generated it for. Falling back to `url`
16
+ * covers an SVG or a small image that got no derivative. A video always embeds
17
+ * `url`: there are no video derivatives, and the tag streams it.
18
+ *
19
+ * The asset's own `alt` rides along, so an author who wrote it once in the
20
+ * library doesn't write it again per body.
21
+ */
22
+ export function toWysiwygEmbed(asset) {
23
+ if (asset.kind === MEDIA_KIND.Image) {
24
+ return {
25
+ kind: WYSIWYG_MEDIA_KIND.Image,
26
+ src: asset.previewUrl || asset.url,
27
+ ...(asset.alt ? { alt: asset.alt } : {}),
28
+ // Seeds the node (and the resize handle) with the asset's own
29
+ // width, so it lands at its natural size rather than stretched to
30
+ // the column. `previewUrl` is a *scaled* derivative, so the stored
31
+ // dimensions overstate it — the browser scales to fit either way,
32
+ // and the number is only ever a starting point the author drags.
33
+ ...(asset.dimensions ? { width: asset.dimensions.width } : {})
34
+ };
35
+ }
36
+ if (asset.kind === MEDIA_KIND.Video) {
37
+ return {
38
+ kind: WYSIWYG_MEDIA_KIND.Video,
39
+ src: asset.url,
40
+ ...(asset.dimensions ? { width: asset.dimensions.width } : {})
41
+ };
42
+ }
43
+ return null;
44
+ }