@openrails/contentkit-ui 0.0.0-stage → 0.68.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 (367) hide show
  1. package/README.md +425 -2
  2. package/dist/appearance.d.ts +38 -0
  3. package/dist/appearance.js +15 -0
  4. package/dist/appearance.js.map +1 -0
  5. package/dist/assets/hash.worker-BYfo-7MQ.js.map +1 -0
  6. package/dist/client/aspect.d.ts +6 -0
  7. package/dist/client/aspect.js +17 -0
  8. package/dist/client/aspect.js.map +1 -0
  9. package/dist/client/client.d.ts +51 -0
  10. package/dist/client/client.js +35 -0
  11. package/dist/client/client.js.map +1 -0
  12. package/dist/client/codes.d.ts +12 -0
  13. package/dist/client/codes.js +19 -0
  14. package/dist/client/codes.js.map +1 -0
  15. package/dist/client/content/bans.d.ts +15 -0
  16. package/dist/client/content/bans.js +40 -0
  17. package/dist/client/content/bans.js.map +1 -0
  18. package/dist/client/content/comments.d.ts +36 -0
  19. package/dist/client/content/comments.js +93 -0
  20. package/dist/client/content/comments.js.map +1 -0
  21. package/dist/client/content/favorites.d.ts +14 -0
  22. package/dist/client/content/favorites.js +42 -0
  23. package/dist/client/content/favorites.js.map +1 -0
  24. package/dist/client/content/moderation.d.ts +18 -0
  25. package/dist/client/content/moderation.js +37 -0
  26. package/dist/client/content/moderation.js.map +1 -0
  27. package/dist/client/content/polls.d.ts +47 -0
  28. package/dist/client/content/polls.js +148 -0
  29. package/dist/client/content/polls.js.map +1 -0
  30. package/dist/client/content/posts.d.ts +44 -0
  31. package/dist/client/content/posts.js +91 -0
  32. package/dist/client/content/posts.js.map +1 -0
  33. package/dist/client/content/reactions.d.ts +12 -0
  34. package/dist/client/content/reactions.js +36 -0
  35. package/dist/client/content/reactions.js.map +1 -0
  36. package/dist/client/content/types.d.ts +91 -0
  37. package/dist/client/content/types.js +8 -0
  38. package/dist/client/content/types.js.map +1 -0
  39. package/dist/client/crop.d.ts +32 -0
  40. package/dist/client/crop.js +103 -0
  41. package/dist/client/crop.js.map +1 -0
  42. package/dist/client/errors.d.ts +54 -0
  43. package/dist/client/errors.js +80 -0
  44. package/dist/client/errors.js.map +1 -0
  45. package/dist/client/gallery.d.ts +43 -0
  46. package/dist/client/gallery.js +86 -0
  47. package/dist/client/gallery.js.map +1 -0
  48. package/dist/client/generated/error-codes.d.ts +5 -0
  49. package/dist/client/generated/error-codes.js +131 -0
  50. package/dist/client/generated/error-codes.js.map +1 -0
  51. package/dist/client/generated/routes.d.ts +1086 -0
  52. package/dist/client/generated/routes.js +1400 -0
  53. package/dist/client/generated/routes.js.map +1 -0
  54. package/dist/client/generated/wire.d.ts +630 -0
  55. package/dist/client/generated/wire.js +6 -0
  56. package/dist/client/generated/wire.js.map +1 -0
  57. package/dist/client/http.d.ts +79 -0
  58. package/dist/client/http.js +74 -0
  59. package/dist/client/http.js.map +1 -0
  60. package/dist/client/image.d.ts +24 -0
  61. package/dist/client/image.js +65 -0
  62. package/dist/client/image.js.map +1 -0
  63. package/dist/client/index.d.ts +33 -0
  64. package/dist/client/media/api.d.ts +36 -0
  65. package/dist/client/media/api.js +61 -0
  66. package/dist/client/media/api.js.map +1 -0
  67. package/dist/client/media/client.d.ts +247 -0
  68. package/dist/client/media/client.js +407 -0
  69. package/dist/client/media/client.js.map +1 -0
  70. package/dist/client/media/encode.d.ts +3 -0
  71. package/dist/client/media/encode.js +8 -0
  72. package/dist/client/media/encode.js.map +1 -0
  73. package/dist/client/media/hash-stream.d.ts +6 -0
  74. package/dist/client/media/hash-stream.js +16 -0
  75. package/dist/client/media/hash-stream.js.map +1 -0
  76. package/dist/client/media/hash.d.ts +12 -0
  77. package/dist/client/media/hash.js +48 -0
  78. package/dist/client/media/hash.js.map +1 -0
  79. package/dist/client/media/hash.worker-inline.js +18 -0
  80. package/dist/client/media/hash.worker-inline.js.map +1 -0
  81. package/dist/client/media/pacer.d.ts +21 -0
  82. package/dist/client/media/pacer.js +25 -0
  83. package/dist/client/media/pacer.js.map +1 -0
  84. package/dist/client/media/queue.d.ts +112 -0
  85. package/dist/client/media/queue.js +274 -0
  86. package/dist/client/media/queue.js.map +1 -0
  87. package/dist/client/media/ref.d.ts +6 -0
  88. package/dist/client/media/ref.js +10 -0
  89. package/dist/client/media/ref.js.map +1 -0
  90. package/dist/client/media/rules.d.ts +52 -0
  91. package/dist/client/media/rules.js +139 -0
  92. package/dist/client/media/rules.js.map +1 -0
  93. package/dist/client/media/transport.d.ts +13 -0
  94. package/dist/client/media/transport.js +33 -0
  95. package/dist/client/media/transport.js.map +1 -0
  96. package/dist/client/media/windows.d.ts +15 -0
  97. package/dist/client/media/windows.js +39 -0
  98. package/dist/client/media/windows.js.map +1 -0
  99. package/dist/client/playback.d.ts +200 -0
  100. package/dist/client/playback.js +142 -0
  101. package/dist/client/playback.js.map +1 -0
  102. package/dist/client/player.d.ts +102 -0
  103. package/dist/client/player.js +155 -0
  104. package/dist/client/player.js.map +1 -0
  105. package/dist/client/public.d.ts +25 -0
  106. package/dist/client/public.js +18 -0
  107. package/dist/client/public.js.map +1 -0
  108. package/dist/client/rendition.d.ts +19 -0
  109. package/dist/client/rendition.js +16 -0
  110. package/dist/client/rendition.js.map +1 -0
  111. package/dist/client/route.d.ts +39 -0
  112. package/dist/client/route.js +31 -0
  113. package/dist/client/route.js.map +1 -0
  114. package/dist/client/taxonomy.d.ts +30 -0
  115. package/dist/client/taxonomy.js +77 -0
  116. package/dist/client/taxonomy.js.map +1 -0
  117. package/dist/client.js +33 -0
  118. package/dist/components/encode-progress.d.ts +15 -0
  119. package/dist/components/encode-progress.js +49 -0
  120. package/dist/components/encode-progress.js.map +1 -0
  121. package/dist/components/hover-preview.d.ts +29 -0
  122. package/dist/components/hover-preview.js +49 -0
  123. package/dist/components/hover-preview.js.map +1 -0
  124. package/dist/components/image-crop-dialog.d.ts +48 -0
  125. package/dist/components/image-crop-dialog.js +359 -0
  126. package/dist/components/image-crop-dialog.js.map +1 -0
  127. package/dist/components/lazy-mount.d.ts +10 -0
  128. package/dist/components/lazy-mount.js +22 -0
  129. package/dist/components/lazy-mount.js.map +1 -0
  130. package/dist/components/media-folder-editor.d.ts +45 -0
  131. package/dist/components/media-folder-editor.js +564 -0
  132. package/dist/components/media-folder-editor.js.map +1 -0
  133. package/dist/components/media-gallery.d.ts +54 -0
  134. package/dist/components/media-gallery.js +597 -0
  135. package/dist/components/media-gallery.js.map +1 -0
  136. package/dist/components/media-readiness-notice.d.ts +24 -0
  137. package/dist/components/media-readiness-notice.js +61 -0
  138. package/dist/components/media-readiness-notice.js.map +1 -0
  139. package/dist/components/player-controls.d.ts +71 -0
  140. package/dist/components/player-controls.js +264 -0
  141. package/dist/components/player-controls.js.map +1 -0
  142. package/dist/components/progress.d.ts +5 -0
  143. package/dist/components/progress.js +14 -0
  144. package/dist/components/progress.js.map +1 -0
  145. package/dist/components/rendition-img.d.ts +34 -0
  146. package/dist/components/rendition-img.js +78 -0
  147. package/dist/components/rendition-img.js.map +1 -0
  148. package/dist/components/slot-editor.d.ts +95 -0
  149. package/dist/components/slot-editor.js +182 -0
  150. package/dist/components/slot-editor.js.map +1 -0
  151. package/dist/components/slot-image.d.ts +23 -0
  152. package/dist/components/slot-image.js +48 -0
  153. package/dist/components/slot-image.js.map +1 -0
  154. package/dist/components/slot-upload.d.ts +41 -0
  155. package/dist/components/slot-upload.js +213 -0
  156. package/dist/components/slot-upload.js.map +1 -0
  157. package/dist/components/social/bans.d.ts +42 -0
  158. package/dist/components/social/bans.js +300 -0
  159. package/dist/components/social/bans.js.map +1 -0
  160. package/dist/components/social/comments.d.ts +42 -0
  161. package/dist/components/social/comments.js +517 -0
  162. package/dist/components/social/comments.js.map +1 -0
  163. package/dist/components/social/engagement.d.ts +45 -0
  164. package/dist/components/social/engagement.js +129 -0
  165. package/dist/components/social/engagement.js.map +1 -0
  166. package/dist/components/social/index.d.ts +6 -0
  167. package/dist/components/social/moderation.d.ts +32 -0
  168. package/dist/components/social/moderation.js +383 -0
  169. package/dist/components/social/moderation.js.map +1 -0
  170. package/dist/components/social/parts.d.ts +50 -0
  171. package/dist/components/social/parts.js +106 -0
  172. package/dist/components/social/parts.js.map +1 -0
  173. package/dist/components/social/poll-editor.d.ts +34 -0
  174. package/dist/components/social/poll-editor.js +567 -0
  175. package/dist/components/social/poll-editor.js.map +1 -0
  176. package/dist/components/social/poll.d.ts +30 -0
  177. package/dist/components/social/poll.js +252 -0
  178. package/dist/components/social/poll.js.map +1 -0
  179. package/dist/components/sortable-list.d.ts +29 -0
  180. package/dist/components/sortable-list.js +121 -0
  181. package/dist/components/sortable-list.js.map +1 -0
  182. package/dist/components/video-mini-player.d.ts +35 -0
  183. package/dist/components/video-mini-player.js +236 -0
  184. package/dist/components/video-mini-player.js.map +1 -0
  185. package/dist/components/video-player.d.ts +146 -0
  186. package/dist/components/video-player.js +630 -0
  187. package/dist/components/video-player.js.map +1 -0
  188. package/dist/components/video-poster-picker.d.ts +59 -0
  189. package/dist/components/video-poster-picker.js +335 -0
  190. package/dist/components/video-poster-picker.js.map +1 -0
  191. package/dist/components/video-poster.d.ts +24 -0
  192. package/dist/components/video-poster.js +46 -0
  193. package/dist/components/video-poster.js.map +1 -0
  194. package/dist/i18n/context.d.ts +4 -0
  195. package/dist/i18n/context.js +11 -0
  196. package/dist/i18n/context.js.map +1 -0
  197. package/dist/i18n/messages.d.ts +40 -0
  198. package/dist/i18n/messages.js +94 -0
  199. package/dist/i18n/messages.js.map +1 -0
  200. package/dist/index.d.ts +23 -0
  201. package/dist/index.js +41 -0
  202. package/dist/locales/de.d.ts +2 -0
  203. package/dist/locales/de.js +271 -0
  204. package/dist/locales/de.js.map +1 -0
  205. package/dist/locales/en.d.ts +457 -0
  206. package/dist/locales/en.js +271 -0
  207. package/dist/locales/en.js.map +1 -0
  208. package/dist/locales/es.d.ts +2 -0
  209. package/dist/locales/es.js +271 -0
  210. package/dist/locales/es.js.map +1 -0
  211. package/dist/locales/ja.d.ts +2 -0
  212. package/dist/locales/ja.js +271 -0
  213. package/dist/locales/ja.js.map +1 -0
  214. package/dist/locales/ko.d.ts +2 -0
  215. package/dist/locales/ko.js +271 -0
  216. package/dist/locales/ko.js.map +1 -0
  217. package/dist/locales/social/de.d.ts +2 -0
  218. package/dist/locales/social/de.js +198 -0
  219. package/dist/locales/social/de.js.map +1 -0
  220. package/dist/locales/social/en.d.ts +195 -0
  221. package/dist/locales/social/en.js +198 -0
  222. package/dist/locales/social/en.js.map +1 -0
  223. package/dist/locales/social/es.d.ts +2 -0
  224. package/dist/locales/social/es.js +198 -0
  225. package/dist/locales/social/es.js.map +1 -0
  226. package/dist/locales/social/ja.d.ts +2 -0
  227. package/dist/locales/social/ja.js +198 -0
  228. package/dist/locales/social/ja.js.map +1 -0
  229. package/dist/locales/social/ko.d.ts +2 -0
  230. package/dist/locales/social/ko.js +198 -0
  231. package/dist/locales/social/ko.js.map +1 -0
  232. package/dist/locales/social/zh.d.ts +2 -0
  233. package/dist/locales/social/zh.js +198 -0
  234. package/dist/locales/social/zh.js.map +1 -0
  235. package/dist/locales/zh.d.ts +2 -0
  236. package/dist/locales/zh.js +271 -0
  237. package/dist/locales/zh.js.map +1 -0
  238. package/dist/provider.d.ts +22 -0
  239. package/dist/provider.js +59 -0
  240. package/dist/provider.js.map +1 -0
  241. package/dist/react/canonical.d.ts +33 -0
  242. package/dist/react/canonical.js +88 -0
  243. package/dist/react/canonical.js.map +1 -0
  244. package/dist/react/comments.d.ts +82 -0
  245. package/dist/react/comments.js +147 -0
  246. package/dist/react/comments.js.map +1 -0
  247. package/dist/react/config.d.ts +13 -0
  248. package/dist/react/config.js +14 -0
  249. package/dist/react/config.js.map +1 -0
  250. package/dist/react/context.d.ts +48 -0
  251. package/dist/react/context.js +37 -0
  252. package/dist/react/context.js.map +1 -0
  253. package/dist/react/crop.d.ts +49 -0
  254. package/dist/react/crop.js +107 -0
  255. package/dist/react/crop.js.map +1 -0
  256. package/dist/react/engagement.d.ts +38 -0
  257. package/dist/react/engagement.js +88 -0
  258. package/dist/react/engagement.js.map +1 -0
  259. package/dist/react/folder.d.ts +83 -0
  260. package/dist/react/folder.js +183 -0
  261. package/dist/react/folder.js.map +1 -0
  262. package/dist/react/gallery.d.ts +140 -0
  263. package/dist/react/gallery.js +477 -0
  264. package/dist/react/gallery.js.map +1 -0
  265. package/dist/react/index.d.ts +21 -0
  266. package/dist/react/inline-preview.d.ts +24 -0
  267. package/dist/react/inline-preview.js +76 -0
  268. package/dist/react/inline-preview.js.map +1 -0
  269. package/dist/react/player.d.ts +70 -0
  270. package/dist/react/player.js +214 -0
  271. package/dist/react/player.js.map +1 -0
  272. package/dist/react/polls.d.ts +74 -0
  273. package/dist/react/polls.js +176 -0
  274. package/dist/react/polls.js.map +1 -0
  275. package/dist/react/posts.d.ts +59 -0
  276. package/dist/react/posts.js +149 -0
  277. package/dist/react/posts.js.map +1 -0
  278. package/dist/react/provider.d.ts +23 -0
  279. package/dist/react/provider.js +35 -0
  280. package/dist/react/provider.js.map +1 -0
  281. package/dist/react/public.d.ts +39 -0
  282. package/dist/react/public.js +70 -0
  283. package/dist/react/public.js.map +1 -0
  284. package/dist/react/read.d.ts +52 -0
  285. package/dist/react/read.js +143 -0
  286. package/dist/react/read.js.map +1 -0
  287. package/dist/react/resources.d.ts +70 -0
  288. package/dist/react/resources.js +247 -0
  289. package/dist/react/resources.js.map +1 -0
  290. package/dist/react/slot.d.ts +102 -0
  291. package/dist/react/slot.js +181 -0
  292. package/dist/react/slot.js.map +1 -0
  293. package/dist/react/store.d.ts +51 -0
  294. package/dist/react/store.js +164 -0
  295. package/dist/react/store.js.map +1 -0
  296. package/dist/react/upload.d.ts +85 -0
  297. package/dist/react/upload.js +150 -0
  298. package/dist/react/upload.js.map +1 -0
  299. package/dist/react/use-resource.d.ts +43 -0
  300. package/dist/react/use-resource.js +66 -0
  301. package/dist/react/use-resource.js.map +1 -0
  302. package/dist/react/video.d.ts +118 -0
  303. package/dist/react/video.js +184 -0
  304. package/dist/react/video.js.map +1 -0
  305. package/dist/react/viewport.d.ts +19 -0
  306. package/dist/react/viewport.js +30 -0
  307. package/dist/react/viewport.js.map +1 -0
  308. package/dist/react.js +21 -0
  309. package/dist/scope.d.ts +14 -0
  310. package/dist/scope.js +30 -0
  311. package/dist/scope.js.map +1 -0
  312. package/dist/styles.css +3 -0
  313. package/dist/ui/alert-dialog.d.ts +18 -0
  314. package/dist/ui/alert-dialog.js +86 -0
  315. package/dist/ui/alert-dialog.js.map +1 -0
  316. package/dist/ui/alert.d.ts +10 -0
  317. package/dist/ui/alert.js +38 -0
  318. package/dist/ui/alert.js.map +1 -0
  319. package/dist/ui/avatar.d.ts +11 -0
  320. package/dist/ui/avatar.js +31 -0
  321. package/dist/ui/avatar.js.map +1 -0
  322. package/dist/ui/badge.d.ts +7 -0
  323. package/dist/ui/badge.js +31 -0
  324. package/dist/ui/badge.js.map +1 -0
  325. package/dist/ui/button.d.ts +8 -0
  326. package/dist/ui/button.js +46 -0
  327. package/dist/ui/button.js.map +1 -0
  328. package/dist/ui/dialog.d.ts +17 -0
  329. package/dist/ui/dialog.js +91 -0
  330. package/dist/ui/dialog.js.map +1 -0
  331. package/dist/ui/dropdown-menu.d.ts +15 -0
  332. package/dist/ui/dropdown-menu.js +105 -0
  333. package/dist/ui/dropdown-menu.js.map +1 -0
  334. package/dist/ui/input.d.ts +3 -0
  335. package/dist/ui/input.js +17 -0
  336. package/dist/ui/input.js.map +1 -0
  337. package/dist/ui/label.d.ts +3 -0
  338. package/dist/ui/label.js +15 -0
  339. package/dist/ui/label.js.map +1 -0
  340. package/dist/ui/progress.d.ts +7 -0
  341. package/dist/ui/progress.js +31 -0
  342. package/dist/ui/progress.js.map +1 -0
  343. package/dist/ui/select.d.ts +15 -0
  344. package/dist/ui/select.js +98 -0
  345. package/dist/ui/select.js.map +1 -0
  346. package/dist/ui/skeleton.d.ts +2 -0
  347. package/dist/ui/skeleton.js +14 -0
  348. package/dist/ui/skeleton.js.map +1 -0
  349. package/dist/ui/slider.d.ts +3 -0
  350. package/dist/ui/slider.js +35 -0
  351. package/dist/ui/slider.js.map +1 -0
  352. package/dist/ui/switch.d.ts +5 -0
  353. package/dist/ui/switch.js +20 -0
  354. package/dist/ui/switch.js.map +1 -0
  355. package/dist/ui/tabs.d.ts +10 -0
  356. package/dist/ui/tabs.js +47 -0
  357. package/dist/ui/tabs.js.map +1 -0
  358. package/dist/ui/textarea.d.ts +3 -0
  359. package/dist/ui/textarea.js +15 -0
  360. package/dist/ui/textarea.js.map +1 -0
  361. package/dist/ui/toggle-group.d.ts +5 -0
  362. package/dist/ui/toggle-group.js +23 -0
  363. package/dist/ui/toggle-group.js.map +1 -0
  364. package/dist/urls/index.d.ts +100 -0
  365. package/dist/urls.js +118 -0
  366. package/dist/urls.js.map +1 -0
  367. package/package.json +118 -3
@@ -0,0 +1,102 @@
1
+ import { AspectRatio } from '../client/aspect.js';
2
+ import { ContentKitClient } from '../client/client.js';
3
+ import { Size } from '../client/crop.js';
4
+ import { ContentKitError } from '../client/errors.js';
5
+ import { Edit, FileInfo, ReadResult, RefBody } from '../client/generated/wire.js';
6
+ import { CropSource } from '../client/image.js';
7
+ import { Progress } from '../client/media/client.js';
8
+ import { PublicPreset } from '../client/public.js';
9
+ import { Rendition } from '../client/rendition.js';
10
+ export interface SlotImageOptions {
11
+ ref: RefBody;
12
+ /** The upload path, e.g. "cover" or "avatar". */
13
+ path: string;
14
+ /** The public preset showing the upload. */
15
+ image?: PublicPreset | null;
16
+ /** An editor read of the item the host already has; skips the fetch. */
17
+ read?: ReadResult | null;
18
+ /** Overrides the provider's client; without one only a supplied read shows. */
19
+ client?: ContentKitClient | null;
20
+ }
21
+ export interface UseSlotImage {
22
+ /** The upload at path as an editor reads it; null when there is none. */
23
+ file: FileInfo | null;
24
+ /** The current published files; empty until the worker publishes them. */
25
+ renditions: Rendition[];
26
+ /** "W:H": the preset's, else "1:1". */
27
+ aspect: AspectRatio;
28
+ loading: boolean;
29
+ error?: ContentKitError;
30
+ reload: () => void;
31
+ /** Replaces the upload after a save without refetching. */
32
+ set: (file: FileInfo | null) => void;
33
+ }
34
+ /** An upload path's state (an editor read) and its public image. */
35
+ export declare function useSlotImage(o: SlotImageOptions): UseSlotImage;
36
+ export type SlotCropMode = "new" | "recrop";
37
+ /** edit: null is the centred crop at the aspect, unrotated. */
38
+ export type SlotCropState = {
39
+ status: "idle";
40
+ } | {
41
+ status: "decoding";
42
+ } | {
43
+ status: "cropping";
44
+ source: CropSource;
45
+ edit: Edit | null;
46
+ mode: SlotCropMode;
47
+ } | {
48
+ status: "saving";
49
+ source: CropSource;
50
+ edit: Edit | null;
51
+ mode: SlotCropMode;
52
+ progress?: Progress;
53
+ rendering?: boolean;
54
+ } | {
55
+ status: "done";
56
+ file: FileInfo;
57
+ } | {
58
+ status: "error";
59
+ error: ContentKitError;
60
+ source?: CropSource;
61
+ edit?: Edit | null;
62
+ mode?: SlotCropMode;
63
+ };
64
+ export interface SlotCropOptions {
65
+ ref: RefBody;
66
+ /** The upload path, e.g. "avatar". */
67
+ path: string;
68
+ /** The current upload (useSlotImage's file): its path, size and edit for recrop(). */
69
+ file?: FileInfo | null;
70
+ /** The output's "W:H"; default "1:1". */
71
+ aspect?: AspectRatio;
72
+ /** "reject": refuse animated images before uploading (the preset's Image.Animation). */
73
+ animation?: "reject";
74
+ onSaved?: (file: FileInfo) => void;
75
+ /** Replaces decodeImage (tests, custom decoders). */
76
+ decode?: (file: File) => Promise<CropSource>;
77
+ /** How long save() waits for the worker to render. Default 120 s. */
78
+ renderTimeout?: number;
79
+ /** Every failed decode or save (aborts excepted); the state shows it too. */
80
+ onError?: (e: ContentKitError, operation: "slot.decode" | "slot.save") => void;
81
+ /** Overrides the provider's client. */
82
+ client?: ContentKitClient;
83
+ }
84
+ export type UseSlotCrop = SlotCropState & {
85
+ aspect: AspectRatio;
86
+ /** The output's size in source pixels (crop, then rotation) while a source is open. */
87
+ cropped?: Size;
88
+ /** Opens a picked file for cropping. */
89
+ pick: (file: File) => Promise<void>;
90
+ /** Re-crops the committed upload; false when there is none. */
91
+ canRecrop: boolean;
92
+ recrop: () => Promise<void>;
93
+ setEdit: (e: Edit | null) => void;
94
+ /** Uploads the file and puts it with the edit (new), or edits the upload (recrop); then waits for the render. */
95
+ save: (edit?: Edit | null) => Promise<FileInfo | undefined>;
96
+ /** Aborts a save and closes the source. */
97
+ cancel: () => void;
98
+ };
99
+ /** The edit's output size: its crop (or the centred crop at aspect), turned. */
100
+ export declare function editOutput(source: Size, edit: Edit | null | undefined, shape: AspectRatio): Size;
101
+ /** pick → crop → save → done, and recrop() → crop → save for an existing upload. */
102
+ export declare function useSlotCrop(o: SlotCropOptions): UseSlotCrop;
@@ -0,0 +1,181 @@
1
+ import { ContentKitError as e, toContentKitError as t } from "../client/errors.js";
2
+ import { publicRenditions as n } from "../client/public.js";
3
+ import { ratio as r } from "../client/aspect.js";
4
+ import { samePath as i, stem as a } from "../client/media/client.js";
5
+ import { centeredCrop as o, constrainCrop as s, editedSize as c, rotation as l, sameEdit as u } from "../client/crop.js";
6
+ import { useContentKitClient as d } from "./context.js";
7
+ import { decodeImage as f, isAnimatedImage as p } from "../client/image.js";
8
+ import { withUpload as m } from "./store.js";
9
+ import { useMediaRead as h } from "./read.js";
10
+ import { useCallback as g, useEffect as _, useMemo as v, useRef as y, useState as b } from "react";
11
+ //#region src/react/slot.ts
12
+ function x(e) {
13
+ let t = h(e.ref, {
14
+ editor: !0,
15
+ prefix: a(e.path),
16
+ read: e.read,
17
+ client: e.client
18
+ }), r = t.read, o = r?.files.find((t) => t.upload && i(t.path, e.path)) ?? null, { path: s } = e, c = t.set, l = g((e) => c(m(r, s, e)), [
19
+ r,
20
+ s,
21
+ c
22
+ ]);
23
+ return {
24
+ file: o,
25
+ renditions: v(() => {
26
+ if (!r) return n(e.image);
27
+ let t = r.public?.find((t) => i(t.from, s) && (!e.image || t.preset === e.image.preset));
28
+ return n(t);
29
+ }, [
30
+ r,
31
+ e.image,
32
+ s
33
+ ]),
34
+ aspect: e.image?.aspect || "1:1",
35
+ loading: t.loading,
36
+ error: t.error,
37
+ reload: t.reload,
38
+ set: l
39
+ };
40
+ }
41
+ function S(e, t, n) {
42
+ let i = r(n), a = l(t?.rotate ?? 0), u = t?.crop ? s(t.crop, e, i, a) : o(e, i, a);
43
+ return c({
44
+ width: u.w,
45
+ height: u.h
46
+ }, a);
47
+ }
48
+ function C(n) {
49
+ let { media: r } = d(n.client), [i, a] = b({ status: "idle" }), o = y(i);
50
+ o.current = i;
51
+ let s = y(null), c = y(0), l = y(n);
52
+ l.current = n;
53
+ let m = n.aspect ?? "1:1", h = g((e) => {
54
+ let t = o.current, n = "source" in t ? t.source : void 0, r = "source" in e ? e.source : void 0;
55
+ n && n !== r && n.revoke?.(), o.current = e, a(e);
56
+ }, []);
57
+ _(() => () => {
58
+ s.current?.abort();
59
+ let e = o.current;
60
+ "source" in e && e.source?.revoke?.();
61
+ }, []);
62
+ let x = g(async (t, n, r) => {
63
+ s.current?.abort(), s.current = null;
64
+ let i = ++c.current;
65
+ h({ status: "decoding" });
66
+ try {
67
+ let e = await t();
68
+ if (i !== c.current) return e.revoke?.();
69
+ h({
70
+ status: "cropping",
71
+ source: e,
72
+ edit: r,
73
+ mode: n
74
+ });
75
+ } catch (t) {
76
+ if (i !== c.current) return;
77
+ let n = t instanceof e ? t : new e("decode", t instanceof Error ? t.message : String(t), { cause: t });
78
+ h({
79
+ status: "error",
80
+ error: n
81
+ }), l.current.onError?.(n, "slot.decode");
82
+ }
83
+ }, [h]), C = g((t) => x(async () => {
84
+ if (l.current.animation === "reject" && await p(t)) throw new e("animation_not_allowed", "animated images are not allowed here", { status: 422 });
85
+ return (l.current.decode ?? f)(t);
86
+ }, "new", null), [x]), w = !!n.file && !n.file.staged && (n.file.size ?? 0) > 0 && n.file.type.startsWith("image/"), T = g(async () => {
87
+ let e = l.current.file;
88
+ e && await x(() => r.editorView(l.current.ref, e.path), "recrop", e.edit ?? null);
89
+ }, [r, x]), E = g((e) => {
90
+ let t = o.current;
91
+ t.status === "cropping" ? u(t.edit, e) || h({
92
+ ...t,
93
+ edit: e
94
+ }) : t.status === "error" && t.source && t.mode && h({
95
+ status: "cropping",
96
+ source: t.source,
97
+ edit: e,
98
+ mode: t.mode
99
+ });
100
+ }, [h]), D = g(async (e) => {
101
+ e !== void 0 && E(e);
102
+ let n = o.current;
103
+ if (!(n.status === "cropping" || n.status === "error" && n.source && n.mode)) return;
104
+ let { source: i, mode: a } = n, c = n.edit ?? null, { ref: u, path: d, renderTimeout: f } = l.current, p = s.current = new AbortController(), m = (e) => p === s.current && h({
105
+ status: "saving",
106
+ source: i,
107
+ edit: c,
108
+ mode: a,
109
+ progress: e,
110
+ rendering: e?.phase === "processing"
111
+ });
112
+ m();
113
+ try {
114
+ let e;
115
+ if (a === "new") e = await r.put(i.file, {
116
+ ref: u,
117
+ path: d,
118
+ edit: c,
119
+ signal: p.signal,
120
+ timeout: f,
121
+ onProgress: m
122
+ });
123
+ else {
124
+ let t = l.current.file?.path ?? d;
125
+ await r.commit(u, [{
126
+ op: "edit",
127
+ path: t,
128
+ ...c ? { edit: c } : {}
129
+ }], { signal: p.signal }), m({
130
+ phase: "processing",
131
+ loaded: 0,
132
+ total: 0
133
+ }), e = await r.waitFor(u, t, {
134
+ signal: p.signal,
135
+ timeout: f
136
+ });
137
+ }
138
+ return p === s.current ? (h({
139
+ status: "done",
140
+ file: e
141
+ }), l.current.onSaved?.(e), e) : void 0;
142
+ } catch (e) {
143
+ if (p !== s.current) return;
144
+ let n = t(e);
145
+ h(n.code === "aborted" ? {
146
+ status: "cropping",
147
+ source: i,
148
+ edit: c,
149
+ mode: a
150
+ } : {
151
+ status: "error",
152
+ error: n,
153
+ source: i,
154
+ edit: c,
155
+ mode: a
156
+ }), n.code !== "aborted" && l.current.onError?.(n, "slot.save");
157
+ return;
158
+ }
159
+ }, [
160
+ r,
161
+ h,
162
+ E
163
+ ]), O = g(() => {
164
+ c.current++, s.current?.abort(), s.current = null, h({ status: "idle" });
165
+ }, [h]), k = v(() => "source" in i && i.source ? S(i.source, i.edit, m) : void 0, [i, m]);
166
+ return {
167
+ ...i,
168
+ aspect: m,
169
+ cropped: k,
170
+ pick: C,
171
+ canRecrop: w,
172
+ recrop: T,
173
+ setEdit: E,
174
+ save: D,
175
+ cancel: O
176
+ };
177
+ }
178
+ //#endregion
179
+ export { S as editOutput, C as useSlotCrop, x as useSlotImage };
180
+
181
+ //# sourceMappingURL=slot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slot.js","names":[],"sources":["../../src/react/slot.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { ratio, type AspectRatio } from \"../client/aspect.js\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport { centeredCrop, constrainCrop, editedSize, rotation, sameEdit, type Size } from \"../client/crop.js\";\nimport { ContentKitError, toContentKitError } from \"../client/errors.js\";\nimport type { Edit, FileInfo, ReadResult, RefBody } from \"../client/generated/wire.js\";\nimport { decodeImage, isAnimatedImage, type CropSource } from \"../client/image.js\";\nimport { samePath, stem, type Progress } from \"../client/media/client.js\";\nimport { publicRenditions, type PublicPreset } from \"../client/public.js\";\nimport type { Rendition } from \"../client/rendition.js\";\nimport { useContentKitClient } from \"./context.js\";\nimport { useMediaRead } from \"./read.js\";\nimport { withUpload } from \"./store.js\";\n\nexport interface SlotImageOptions {\n ref: RefBody;\n /** The upload path, e.g. \"cover\" or \"avatar\". */\n path: string;\n /** The public preset showing the upload. */\n image?: PublicPreset | null;\n /** An editor read of the item the host already has; skips the fetch. */\n read?: ReadResult | null;\n /** Overrides the provider's client; without one only a supplied read shows. */\n client?: ContentKitClient | null;\n}\n\nexport interface UseSlotImage {\n /** The upload at path as an editor reads it; null when there is none. */\n file: FileInfo | null;\n /** The current published files; empty until the worker publishes them. */\n renditions: Rendition[];\n /** \"W:H\": the preset's, else \"1:1\". */\n aspect: AspectRatio;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n /** Replaces the upload after a save without refetching. */\n set: (file: FileInfo | null) => void;\n}\n\n/** An upload path's state (an editor read) and its public image. */\nexport function useSlotImage(o: SlotImageOptions): UseSlotImage {\n const r = useMediaRead(o.ref, { editor: true, prefix: stem(o.path), read: o.read, client: o.client });\n const read = r.read;\n const file = read?.files.find((f) => f.upload && samePath(f.path, o.path)) ?? null;\n const { path } = o;\n const update = r.set;\n const set = useCallback((f: FileInfo | null) => update(withUpload(read, path, f)), [read, path, update]);\n const renditions = useMemo(() => {\n if (!read) return publicRenditions(o.image);\n const published = read.public?.find((p) => samePath(p.from, path) && (!o.image || p.preset === o.image.preset));\n return publicRenditions(published);\n }, [read, o.image, path]);\n return { file, renditions, aspect: o.image?.aspect || \"1:1\", loading: r.loading, error: r.error, reload: r.reload, set };\n}\n\nexport type SlotCropMode = \"new\" | \"recrop\";\n\n/** edit: null is the centred crop at the aspect, unrotated. */\nexport type SlotCropState =\n | { status: \"idle\" }\n | { status: \"decoding\" }\n | { status: \"cropping\"; source: CropSource; edit: Edit | null; mode: SlotCropMode }\n | { status: \"saving\"; source: CropSource; edit: Edit | null; mode: SlotCropMode; progress?: Progress; rendering?: boolean }\n | { status: \"done\"; file: FileInfo }\n | { status: \"error\"; error: ContentKitError; source?: CropSource; edit?: Edit | null; mode?: SlotCropMode };\n\nexport interface SlotCropOptions {\n ref: RefBody;\n /** The upload path, e.g. \"avatar\". */\n path: string;\n /** The current upload (useSlotImage's file): its path, size and edit for recrop(). */\n file?: FileInfo | null;\n /** The output's \"W:H\"; default \"1:1\". */\n aspect?: AspectRatio;\n /** \"reject\": refuse animated images before uploading (the preset's Image.Animation). */\n animation?: \"reject\";\n onSaved?: (file: FileInfo) => void;\n /** Replaces decodeImage (tests, custom decoders). */\n decode?: (file: File) => Promise<CropSource>;\n /** How long save() waits for the worker to render. Default 120 s. */\n renderTimeout?: number;\n /** Every failed decode or save (aborts excepted); the state shows it too. */\n onError?: (e: ContentKitError, operation: \"slot.decode\" | \"slot.save\") => void;\n /** Overrides the provider's client. */\n client?: ContentKitClient;\n}\n\nexport type UseSlotCrop = SlotCropState & {\n aspect: AspectRatio;\n /** The output's size in source pixels (crop, then rotation) while a source is open. */\n cropped?: Size;\n /** Opens a picked file for cropping. */\n pick: (file: File) => Promise<void>;\n /** Re-crops the committed upload; false when there is none. */\n canRecrop: boolean;\n recrop: () => Promise<void>;\n setEdit: (e: Edit | null) => void;\n /** Uploads the file and puts it with the edit (new), or edits the upload (recrop); then waits for the render. */\n save: (edit?: Edit | null) => Promise<FileInfo | undefined>;\n /** Aborts a save and closes the source. */\n cancel: () => void;\n};\n\n/** The edit's output size: its crop (or the centred crop at aspect), turned. */\nexport function editOutput(source: Size, edit: Edit | null | undefined, shape: AspectRatio): Size {\n const aspect = ratio(shape);\n const rot = rotation(edit?.rotate ?? 0);\n const c = edit?.crop ? constrainCrop(edit.crop, source, aspect, rot) : centeredCrop(source, aspect, rot);\n return editedSize({ width: c.w, height: c.h }, rot);\n}\n\n/** pick → crop → save → done, and recrop() → crop → save for an existing upload. */\nexport function useSlotCrop(o: SlotCropOptions): UseSlotCrop {\n const { media } = useContentKitClient(o.client);\n const [s, setS] = useState<SlotCropState>({ status: \"idle\" });\n const cur = useRef(s);\n cur.current = s;\n const ctl = useRef<AbortController | null>(null);\n const picks = useRef(0);\n const opts = useRef(o);\n opts.current = o;\n const aspect = o.aspect ?? \"1:1\";\n\n const set = useCallback((next: SlotCropState) => {\n const prev = cur.current;\n const src = \"source\" in prev ? prev.source : undefined;\n const keep = \"source\" in next ? next.source : undefined;\n if (src && src !== keep) src.revoke?.();\n cur.current = next;\n setS(next);\n }, []);\n\n useEffect(\n () => () => {\n ctl.current?.abort();\n const c = cur.current;\n if (\"source\" in c) c.source?.revoke?.();\n },\n [],\n );\n\n const open = useCallback(\n async (load: () => Promise<CropSource>, mode: SlotCropMode, edit: Edit | null) => {\n ctl.current?.abort();\n ctl.current = null;\n const n = ++picks.current;\n set({ status: \"decoding\" });\n try {\n const source = await load();\n if (n !== picks.current) return source.revoke?.();\n set({ status: \"cropping\", source, edit, mode });\n } catch (e) {\n if (n !== picks.current) return;\n const error = e instanceof ContentKitError ? e : new ContentKitError(\"decode\", e instanceof Error ? e.message : String(e), { cause: e });\n set({ status: \"error\", error });\n opts.current.onError?.(error, \"slot.decode\");\n }\n },\n [set],\n );\n\n const pick = useCallback(\n (file: File) =>\n open(async () => {\n if (opts.current.animation === \"reject\" && (await isAnimatedImage(file)))\n throw new ContentKitError(\"animation_not_allowed\", \"animated images are not allowed here\", { status: 422 });\n return (opts.current.decode ?? decodeImage)(file);\n }, \"new\", null),\n [open],\n );\n\n const canRecrop = !!o.file && !o.file.staged && (o.file.size ?? 0) > 0 && o.file.type.startsWith(\"image/\");\n const recrop = useCallback(async () => {\n const f = opts.current.file;\n if (!f) return;\n await open(() => media.editorView(opts.current.ref, f.path), \"recrop\", f.edit ?? null);\n }, [media, open]);\n\n const setEdit = useCallback(\n (edit: Edit | null) => {\n const c = cur.current;\n if (c.status === \"cropping\") {\n if (!sameEdit(c.edit, edit)) set({ ...c, edit });\n } else if (c.status === \"error\" && c.source && c.mode) set({ status: \"cropping\", source: c.source, edit, mode: c.mode });\n },\n [set],\n );\n\n const save = useCallback(\n async (next?: Edit | null) => {\n if (next !== undefined) setEdit(next);\n const c = cur.current;\n if (!(c.status === \"cropping\" || (c.status === \"error\" && c.source && c.mode))) return undefined;\n const { source, mode } = c as { source: CropSource; mode: SlotCropMode };\n const edit = c.edit ?? null;\n const { ref, path, renderTimeout: timeout } = opts.current;\n const a = (ctl.current = new AbortController());\n const saving = (progress?: Progress) =>\n a === ctl.current && set({ status: \"saving\", source, edit, mode, progress, rendering: progress?.phase === \"processing\" });\n saving();\n try {\n let file: FileInfo;\n if (mode === \"new\") file = await media.put(source.file!, { ref, path, edit, signal: a.signal, timeout, onProgress: saving });\n else {\n const at = opts.current.file?.path ?? path;\n await media.commit(ref, [{ op: \"edit\", path: at, ...(edit ? { edit } : {}) }], { signal: a.signal });\n saving({ phase: \"processing\", loaded: 0, total: 0 });\n file = await media.waitFor(ref, at, { signal: a.signal, timeout });\n }\n if (a !== ctl.current) return undefined;\n set({ status: \"done\", file });\n opts.current.onSaved?.(file);\n return file;\n } catch (e) {\n if (a !== ctl.current) return undefined;\n const error = toContentKitError(e);\n set(error.code === \"aborted\" ? { status: \"cropping\", source, edit, mode } : { status: \"error\", error, source, edit, mode });\n if (error.code !== \"aborted\") opts.current.onError?.(error, \"slot.save\");\n return undefined;\n }\n },\n [media, set, setEdit],\n );\n\n const cancel = useCallback(() => {\n picks.current++;\n ctl.current?.abort();\n ctl.current = null;\n set({ status: \"idle\" });\n }, [set]);\n\n const cropped = useMemo(() => (\"source\" in s && s.source ? editOutput(s.source, s.edit, aspect) : undefined), [s, aspect]);\n return { ...s, aspect, cropped, pick, canRecrop, recrop, setEdit, save, cancel };\n}\n"],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import { ContentKitClient } from '../client/client.js';
2
+ import { ContentKitError } from '../client/errors.js';
3
+ import { FileInfo, ReadResult, RefBody } from '../client/generated/wire.js';
4
+ import { ReadOptions } from '../client/media/api.js';
5
+ export interface ReadEntry {
6
+ read: ReadResult | null;
7
+ loading: boolean;
8
+ /** A read or set() has completed. */
9
+ loaded: boolean;
10
+ error?: ContentKitError;
11
+ }
12
+ /** What one reader asks of a shared read beyond the read itself. */
13
+ export interface ReadPolicy {
14
+ /** Poll at this interval (ms) while uploads process (editor reads). */
15
+ poll?: number | false;
16
+ /** Read again shortly before the URLs expire. */
17
+ refresh?: boolean;
18
+ }
19
+ /**
20
+ * How load() treats a read in flight: "initial" reads only when nothing is
21
+ * loaded or loading; "refresh" joins one in flight (timers, a player's grant
22
+ * refresh); "reload" restarts it (after a change the in-flight read predates).
23
+ */
24
+ export type LoadMode = "initial" | "refresh" | "reload";
25
+ export declare function readKey(ref: RefBody, o?: ReadOptions): string;
26
+ /** The read with the upload at path replaced by file in place (appended when new, removed when null). */
27
+ export declare function withUpload(read: ReadResult | null, path: string, file: FileInfo | null): ReadResult;
28
+ /**
29
+ * The reads hooks share, per client: one request per item and options while
30
+ * any hook shows it. set() updates every reader in place; a commit reloads
31
+ * the item's reads and a processed upload is patched into its editor reads.
32
+ * Readers may ask for polling while uploads process and a read before the
33
+ * URLs expire; the slot runs one timer for all of them.
34
+ */
35
+ export declare class ReadStore {
36
+ private readonly client;
37
+ private readonly slots;
38
+ constructor(client: ContentKitClient);
39
+ subscribe(key: string, ref: RefBody, options: ReadOptions, listener: () => void, policy?: ReadPolicy): () => void;
40
+ snapshot(key: string): ReadEntry;
41
+ load(key: string, mode?: LoadMode): void;
42
+ /** Replaces the read for every hook showing it, without refetching. */
43
+ set(key: string, read: ReadResult): void;
44
+ private fetch;
45
+ private schedule;
46
+ private reloadViewers;
47
+ private apply;
48
+ private update;
49
+ }
50
+ /** The client's shared read store. */
51
+ export declare function storeFor(client: ContentKitClient): ReadStore;
@@ -0,0 +1,164 @@
1
+ import { toContentKitError as e } from "../client/errors.js";
2
+ import { samePath as t, sleep as n } from "../client/media/client.js";
3
+ import { isProcessing as r, processing as i } from "../client/media/windows.js";
4
+ import { expiryDelay as a } from "../client/playback.js";
5
+ //#region src/react/store.ts
6
+ var o = {
7
+ read: null,
8
+ loading: !1,
9
+ loaded: !1
10
+ }, s = 3;
11
+ function c(e, t = {}) {
12
+ return JSON.stringify([
13
+ e.kind,
14
+ e.id,
15
+ t.prefix ?? "",
16
+ t.offset ?? 0,
17
+ t.limit ?? 0,
18
+ !!t.download,
19
+ !!t.editor
20
+ ]);
21
+ }
22
+ function l(e, n, r) {
23
+ let i = e?.files ?? [], a = i.findIndex((e) => e.upload && t(e.path, n)), o = i.filter((e, t) => t !== a);
24
+ return r && o.splice(a < 0 ? o.length : a, 0, r), {
25
+ access: "full",
26
+ expires: 0,
27
+ total: 0,
28
+ offset: 0,
29
+ limit: 0,
30
+ ...e,
31
+ files: o
32
+ };
33
+ }
34
+ var u = class {
35
+ client;
36
+ slots = /* @__PURE__ */ new Map();
37
+ constructor(e) {
38
+ this.client = e, e.subscribe((e) => this.apply(e));
39
+ }
40
+ subscribe(e, t, n, r, i = {}) {
41
+ let a = this.slots.get(e);
42
+ a || this.slots.set(e, a = {
43
+ key: e,
44
+ ref: t,
45
+ options: n,
46
+ listeners: /* @__PURE__ */ new Map(),
47
+ entry: o
48
+ });
49
+ let s = a;
50
+ return s.listeners.set(r, i), this.schedule(s), () => {
51
+ s.listeners.delete(r), queueMicrotask(() => {
52
+ if (this.slots.get(e) === s) {
53
+ if (s.listeners.size) return this.schedule(s);
54
+ s.ctl?.abort(), clearTimeout(s.timer), this.slots.delete(e);
55
+ }
56
+ });
57
+ };
58
+ }
59
+ snapshot(e) {
60
+ return this.slots.get(e)?.entry ?? o;
61
+ }
62
+ load(t, n = "initial") {
63
+ let r = this.slots.get(t);
64
+ if (!r || n === "initial" && (r.entry.loaded || r.ctl) || n === "refresh" && r.ctl) return;
65
+ r.ctl?.abort(), clearTimeout(r.timer);
66
+ let i = r.ctl = new AbortController();
67
+ this.update(r, {
68
+ loading: !0,
69
+ error: void 0
70
+ }), this.fetch(r, i.signal).then((e) => {
71
+ if (r.ctl !== i) return;
72
+ r.ctl = void 0;
73
+ let t = r.entry.read;
74
+ this.update(r, {
75
+ read: e,
76
+ loading: !1,
77
+ loaded: !0,
78
+ error: void 0
79
+ }), this.schedule(r), r.options.editor && d(t, e) && this.reloadViewers(r.ref);
80
+ }, (t) => {
81
+ r.ctl === i && (r.ctl = void 0, this.update(r, {
82
+ read: null,
83
+ loading: !1,
84
+ loaded: !0,
85
+ error: e(t)
86
+ }));
87
+ });
88
+ }
89
+ set(e, t) {
90
+ let n = this.slots.get(e);
91
+ n && (n.ctl?.abort(), n.ctl = void 0, this.update(n, {
92
+ read: t,
93
+ loading: !1,
94
+ loaded: !0,
95
+ error: void 0
96
+ }), this.schedule(n));
97
+ }
98
+ async fetch(t, r) {
99
+ for (let i = 0;; i++) try {
100
+ return await this.client.media.read(t.ref, {
101
+ ...t.options,
102
+ signal: r
103
+ });
104
+ } catch (t) {
105
+ let a = e(t);
106
+ if (a.code !== "rate_limited" || i >= s || r.aborted) throw a;
107
+ await n(Math.min(30, a.retryAfter ?? 1) * 1e3, r);
108
+ }
109
+ }
110
+ schedule(e) {
111
+ clearTimeout(e.timer), e.timer = void 0;
112
+ let t = e.entry.read;
113
+ if (!t || e.ctl || !e.listeners.size) return;
114
+ let n = null, r = [...e.listeners.values()].map((e) => e.poll).filter((e) => typeof e == "number" && e > 0);
115
+ if (r.length && i(t) && (n = Math.min(...r)), [...e.listeners.values()].some((e) => e.refresh)) {
116
+ let e = a(t.expires);
117
+ e !== null && (n = n === null ? e : Math.min(n, e));
118
+ }
119
+ n !== null && (e.timer = setTimeout(() => this.load(e.key, "refresh"), n));
120
+ }
121
+ reloadViewers(e) {
122
+ for (let [t, n] of this.slots) !n.options.editor && n.ref.kind === e.kind && n.ref.id === e.id && (n.entry.loaded || n.ctl) && this.load(t, "reload");
123
+ }
124
+ apply(e) {
125
+ if (e.type !== "media.committed" && e.type !== "media.processed") return;
126
+ let { ref: t } = e;
127
+ for (let [n, r] of this.slots) {
128
+ if (r.ref.kind !== t.kind || r.ref.id !== t.id) continue;
129
+ if (e.type === "media.committed") {
130
+ (r.entry.loaded || r.ctl) && this.load(n, "reload");
131
+ continue;
132
+ }
133
+ let i = r.entry.read, { file: a } = e;
134
+ if (i) {
135
+ if (!r.options.editor) {
136
+ this.load(n, "reload");
137
+ continue;
138
+ }
139
+ (!r.options.prefix || a.path.startsWith(r.options.prefix)) && (this.update(r, { read: l(i, a.path, a) }), r.ctl && this.load(n, "reload"));
140
+ }
141
+ }
142
+ }
143
+ update(e, t) {
144
+ e.entry = {
145
+ ...e.entry,
146
+ ...t
147
+ };
148
+ for (let t of e.listeners.keys()) t();
149
+ }
150
+ };
151
+ function d(e, t) {
152
+ if (!e) return !1;
153
+ let n = new Map(t.files.filter((e) => e.upload).map((e) => [e.path, e]));
154
+ return e.files.some((e) => r(e) && n.has(e.path) && !r(n.get(e.path)));
155
+ }
156
+ var f = /* @__PURE__ */ new WeakMap();
157
+ function p(e) {
158
+ let t = f.get(e);
159
+ return t || f.set(e, t = new u(e)), t;
160
+ }
161
+ //#endregion
162
+ export { u as ReadStore, c as readKey, p as storeFor, l as withUpload };
163
+
164
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","names":[],"sources":["../../src/react/store.ts"],"sourcesContent":["import type { ContentKitClient } from \"../client/client.js\";\nimport { toContentKitError, type ContentKitError } from \"../client/errors.js\";\nimport type { FileInfo, ReadResult, RefBody } from \"../client/generated/wire.js\";\nimport type { ContentKitChange } from \"../client/http.js\";\nimport type { ReadOptions } from \"../client/media/api.js\";\nimport { samePath, sleep } from \"../client/media/client.js\";\nimport { isProcessing, processing } from \"../client/media/windows.js\";\nimport { expiryDelay } from \"../client/playback.js\";\n\nexport interface ReadEntry {\n read: ReadResult | null;\n loading: boolean;\n /** A read or set() has completed. */\n loaded: boolean;\n error?: ContentKitError;\n}\n\n/** What one reader asks of a shared read beyond the read itself. */\nexport interface ReadPolicy {\n /** Poll at this interval (ms) while uploads process (editor reads). */\n poll?: number | false;\n /** Read again shortly before the URLs expire. */\n refresh?: boolean;\n}\n\n/**\n * How load() treats a read in flight: \"initial\" reads only when nothing is\n * loaded or loading; \"refresh\" joins one in flight (timers, a player's grant\n * refresh); \"reload\" restarts it (after a change the in-flight read predates).\n */\nexport type LoadMode = \"initial\" | \"refresh\" | \"reload\";\n\ninterface Slot {\n key: string;\n ref: RefBody;\n options: ReadOptions;\n listeners: Map<() => void, ReadPolicy>;\n entry: ReadEntry;\n ctl?: AbortController;\n timer?: ReturnType<typeof setTimeout>;\n}\n\nconst IDLE: ReadEntry = { read: null, loading: false, loaded: false };\n\n/** rate_limited reads are retried this often before the error shows. */\nconst RATE_LIMIT_RETRIES = 3;\n\nexport function readKey(ref: RefBody, o: ReadOptions = {}): string {\n return JSON.stringify([ref.kind, ref.id, o.prefix ?? \"\", o.offset ?? 0, o.limit ?? 0, !!o.download, !!o.editor]);\n}\n\n/** The read with the upload at path replaced by file in place (appended when new, removed when null). */\nexport function withUpload(read: ReadResult | null, path: string, file: FileInfo | null): ReadResult {\n const all = read?.files ?? [];\n const at = all.findIndex((x) => x.upload && samePath(x.path, path));\n const files = all.filter((_, i) => i !== at);\n if (file) files.splice(at < 0 ? files.length : at, 0, file);\n return { access: \"full\", expires: 0, total: 0, offset: 0, limit: 0, ...read, files };\n}\n\n/**\n * The reads hooks share, per client: one request per item and options while\n * any hook shows it. set() updates every reader in place; a commit reloads\n * the item's reads and a processed upload is patched into its editor reads.\n * Readers may ask for polling while uploads process and a read before the\n * URLs expire; the slot runs one timer for all of them.\n */\nexport class ReadStore {\n private readonly slots = new Map<string, Slot>();\n\n constructor(private readonly client: ContentKitClient) {\n client.subscribe((c) => this.apply(c));\n }\n\n subscribe(key: string, ref: RefBody, options: ReadOptions, listener: () => void, policy: ReadPolicy = {}): () => void {\n let s = this.slots.get(key);\n if (!s) this.slots.set(key, (s = { key, ref, options, listeners: new Map(), entry: IDLE }));\n const slot = s;\n slot.listeners.set(listener, policy);\n this.schedule(slot);\n return () => {\n slot.listeners.delete(listener);\n // Kept across a StrictMode remount; dropped once no hook shows it.\n queueMicrotask(() => {\n if (this.slots.get(key) !== slot) return;\n if (slot.listeners.size) return this.schedule(slot);\n slot.ctl?.abort();\n clearTimeout(slot.timer);\n this.slots.delete(key);\n });\n };\n }\n\n snapshot(key: string): ReadEntry {\n return this.slots.get(key)?.entry ?? IDLE;\n }\n\n load(key: string, mode: LoadMode = \"initial\"): void {\n const s = this.slots.get(key);\n if (!s) return;\n if (mode === \"initial\" && (s.entry.loaded || s.ctl)) return;\n if (mode === \"refresh\" && s.ctl) return;\n s.ctl?.abort();\n clearTimeout(s.timer);\n const ctl = (s.ctl = new AbortController());\n this.update(s, { loading: true, error: undefined });\n this.fetch(s, ctl.signal).then(\n (read) => {\n if (s.ctl !== ctl) return;\n s.ctl = undefined;\n const before = s.entry.read;\n this.update(s, { read, loading: false, loaded: true, error: undefined });\n this.schedule(s);\n if (s.options.editor && finished(before, read)) this.reloadViewers(s.ref);\n },\n (e) => {\n if (s.ctl !== ctl) return;\n s.ctl = undefined;\n this.update(s, { read: null, loading: false, loaded: true, error: toContentKitError(e) });\n },\n );\n }\n\n /** Replaces the read for every hook showing it, without refetching. */\n set(key: string, read: ReadResult): void {\n const s = this.slots.get(key);\n if (!s) return;\n s.ctl?.abort();\n s.ctl = undefined;\n this.update(s, { read, loading: false, loaded: true, error: undefined });\n this.schedule(s);\n }\n\n // The client retries transport faults; a read rate limit waits it out here.\n private async fetch(s: Slot, signal: AbortSignal): Promise<ReadResult> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await this.client.media.read(s.ref, { ...s.options, signal });\n } catch (e) {\n const err = toContentKitError(e);\n if (err.code !== \"rate_limited\" || attempt >= RATE_LIMIT_RETRIES || signal.aborted) throw err;\n await sleep(Math.min(30, err.retryAfter ?? 1) * 1000, signal);\n }\n }\n }\n\n // One timer per slot: the soonest of a poll (while uploads process) and the refresh before expiry.\n private schedule(s: Slot): void {\n clearTimeout(s.timer);\n s.timer = undefined;\n const read = s.entry.read;\n if (!read || s.ctl || !s.listeners.size) return;\n let delay: number | null = null;\n const polls = [...s.listeners.values()].map((p) => p.poll).filter((p): p is number => typeof p === \"number\" && p > 0);\n if (polls.length && processing(read)) delay = Math.min(...polls);\n if ([...s.listeners.values()].some((p) => p.refresh)) {\n const d = expiryDelay(read.expires);\n if (d !== null) delay = delay === null ? d : Math.min(delay, d);\n }\n if (delay !== null) s.timer = setTimeout(() => this.load(s.key, \"refresh\"), delay);\n }\n\n // Uploads finished processing: the item's viewer reads list new files and URLs.\n private reloadViewers(ref: RefBody): void {\n for (const [key, s] of this.slots) {\n if (!s.options.editor && s.ref.kind === ref.kind && s.ref.id === ref.id && (s.entry.loaded || s.ctl)) this.load(key, \"reload\");\n }\n }\n\n private apply(change: ContentKitChange): void {\n if (change.type !== \"media.committed\" && change.type !== \"media.processed\") return;\n const { ref } = change;\n for (const [key, s] of this.slots) {\n if (s.ref.kind !== ref.kind || s.ref.id !== ref.id) continue;\n if (change.type === \"media.committed\") {\n if (s.entry.loaded || s.ctl) this.load(key, \"reload\");\n continue;\n }\n const read = s.entry.read;\n const { file } = change;\n if (!read) continue;\n // A viewer read cannot be patched: its URLs and public files change.\n if (!s.options.editor) {\n this.load(key, \"reload\");\n continue;\n }\n if (s.options.prefix && !file.path.startsWith(s.options.prefix)) continue;\n this.update(s, { read: withUpload(read, file.path, file) });\n // A read in flight may predate the processing.\n if (s.ctl) this.load(key, \"reload\");\n }\n }\n\n // A new entry per change: useSyncExternalStore compares snapshots by identity.\n private update(s: Slot, patch: Partial<ReadEntry>): void {\n s.entry = { ...s.entry, ...patch };\n for (const l of s.listeners.keys()) l();\n }\n}\n\n/** Some upload processing in before is done (or failed) in after. */\nfunction finished(before: ReadResult | null, after: ReadResult): boolean {\n if (!before) return false;\n const now = new Map(after.files.filter((f) => f.upload).map((f) => [f.path, f]));\n return before.files.some((f) => isProcessing(f) && now.has(f.path) && !isProcessing(now.get(f.path)!));\n}\n\nconst stores = new WeakMap<ContentKitClient, ReadStore>();\n\n/** The client's shared read store. */\nexport function storeFor(client: ContentKitClient): ReadStore {\n let s = stores.get(client);\n if (!s) stores.set(client, (s = new ReadStore(client)));\n return s;\n}\n"],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ import { AspectRatio } from '../client/aspect.js';
2
+ import { ContentKitClient } from '../client/client.js';
3
+ import { Rotation, Size } from '../client/crop.js';
4
+ import { ContentKitError } from '../client/errors.js';
5
+ import { Crop, Edit, EncodeProgress, FileInfo } from '../client/generated/wire.js';
6
+ import { Progress, PutOptions, UploadedFile, UploadOptions } from '../client/media/client.js';
7
+ import { UploadQueue, QueueOptions, QueueSnapshot } from '../client/media/queue.js';
8
+ export interface UseUploadQueue extends QueueSnapshot {
9
+ queue: UploadQueue;
10
+ add: UploadQueue["add"];
11
+ move: UploadQueue["move"];
12
+ update: UploadQueue["update"];
13
+ remove: UploadQueue["remove"];
14
+ retry: UploadQueue["retry"];
15
+ start: UploadQueue["start"];
16
+ pause: UploadQueue["pause"];
17
+ commit: UploadQueue["commit"];
18
+ }
19
+ export interface UploadQueueOptions extends QueueOptions {
20
+ /** Overrides the provider's client. */
21
+ client?: ContentKitClient;
22
+ }
23
+ /**
24
+ * A file queue for one item (options are read once; key the component by
25
+ * ref to switch items). Unmounting pauses running uploads.
26
+ */
27
+ export declare function useUploadQueue({ client: own, ...options }: UploadQueueOptions): UseUploadQueue;
28
+ export interface UploadStatus {
29
+ status: "idle" | "uploading" | "done" | "error";
30
+ progress?: Progress;
31
+ error?: ContentKitError;
32
+ result?: UploadedFile & {
33
+ file?: FileInfo;
34
+ };
35
+ }
36
+ export interface UseUpload extends UploadStatus {
37
+ /**
38
+ * Uploads one file; with put it also commits it to its path (put's edit,
39
+ * meta and index) and waits until it is processed (result.file).
40
+ */
41
+ upload: (file: File, opts: Omit<UploadOptions, "signal" | "onProgress"> & {
42
+ put?: PutOptions;
43
+ }) => Promise<UploadedFile & {
44
+ file?: FileInfo;
45
+ }>;
46
+ cancel: () => void;
47
+ }
48
+ /** One upload at a time (a cover, an inline image); a new upload cancels the previous. */
49
+ export declare function useUpload({ client: own }?: {
50
+ client?: ContentKitClient;
51
+ }): UseUpload;
52
+ export interface UseCropOptions {
53
+ /** The upload's size (an editor read's w and h). */
54
+ source: Size;
55
+ /** The edited image's "W:H", e.g. the public preset's aspect; "" or omitted is free. */
56
+ aspect?: AspectRatio;
57
+ /** The current edit to start from. */
58
+ initial?: Edit | null;
59
+ }
60
+ export interface UseCrop {
61
+ /** In original pixels, inside the source and at the aspect. */
62
+ crop: Crop;
63
+ rotate: Rotation;
64
+ /** Ready for an edit or put op; null when nothing changes. Its identity changes only with its value. */
65
+ edit: Edit | null;
66
+ /** A rect in original pixels. */
67
+ setCrop: (rect: Crop) => void;
68
+ /** A rect on the image as displayed (after the rotation) at display size: a cropper's output. */
69
+ setFromDisplay: (rect: Crop, display: Size) => void;
70
+ /** Turns clockwise by deg (a multiple of 90); the crop keeps the aspect. */
71
+ rotateBy: (deg: number) => void;
72
+ reset: () => void;
73
+ }
74
+ /** Headless crop state for any cropper UI: a rect in original pixels, a rotation and the resulting edit. */
75
+ export declare function useCrop({ source: given, aspect: shape, initial }: UseCropOptions): UseCrop;
76
+ export interface UseEncodeProgress {
77
+ progress?: EncodeProgress;
78
+ /** Seconds left, counting down each second between reports; undefined while unknown. */
79
+ remaining?: number;
80
+ }
81
+ /**
82
+ * A pending video file's encode progress from the read API (`files[i].progress`),
83
+ * with the ETA counted down locally between polls.
84
+ */
85
+ export declare function useEncodeProgress(progress?: EncodeProgress | null): UseEncodeProgress;