@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
package/README.md CHANGED
@@ -1,3 +1,426 @@
1
- # Temporary Holding Version
1
+ # @openrails/contentkit-ui
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ ContentKit in the browser: a framework-free client, content URLs, React hooks
4
+ and styled components (uploads with cropping, video posters, galleries, HLS
5
+ playback, comments, reactions, favorites, polls and their staff screens).
6
+ `@openrails/contentkit-ui@X.Y.Z` speaks ContentKit `vX.Y.Z`.
7
+
8
+ ```sh
9
+ pnpm add @openrails/contentkit-ui@X.Y.Z
10
+ ```
11
+
12
+ | Import | Contents | Needs |
13
+ | --- | --- | --- |
14
+ | `@openrails/contentkit-ui` | styled components, `ContentKitUiProvider`, i18n | `react`, `react-dom` |
15
+ | `@openrails/contentkit-ui/react` | `ContentKitProvider` and every hook | `react` |
16
+ | `@openrails/contentkit-ui/client` | `createContentKitClient`, `ContentKitError`, generated wire types, crop/aspect/rendition/ABR/gallery math | nothing |
17
+ | `@openrails/contentkit-ui/urls` | content codes, `[/{lang}]/{route}/{CODE}[/{slug}]` paths, `canonical` | nothing |
18
+ | `@openrails/contentkit-ui/locales/{de,es,ja,ko,zh}` | message bundles (English is built in) | |
19
+ | `@openrails/contentkit-ui/styles.css` | the stylesheet, scoped under `.ckui` (the root entry also installs it) | |
20
+
21
+ `react` and `react-dom` are optional peers, so `/client` and `/urls` work
22
+ without React. hls.js loads on first play.
23
+
24
+ ## Client and providers
25
+
26
+ ```tsx
27
+ import { createContentKitClient } from "@openrails/contentkit-ui/client";
28
+ import { ContentKitProvider } from "@openrails/contentkit-ui/react";
29
+ import { ContentKitUiProvider } from "@openrails/contentkit-ui";
30
+ import { createContentURLs } from "@openrails/contentkit-ui/urls";
31
+
32
+ const contentkit = createContentKitClient({
33
+ baseUrl: "/api/v1/contentkit", // where the host mounts ContentKit
34
+ mounts: { upload: "/api/v1/user-uploads" }, // optional: modules mounted elsewhere (a string, or (ref) => string)
35
+ fetch: auth.authFetch, // an authenticating transport
36
+ token: () => auth.getAccessToken(), // HLS playlists on the API origin (hls.js uses XHR)
37
+ language: () => lang, // Accept-Language
38
+ });
39
+ const urls = createContentURLs({ routes: { video: "watch", gallery: "g" }, languages: ["en", "es"], origin: "https://example.com" });
40
+
41
+ <ContentKitProvider
42
+ client={contentkit}
43
+ viewer={user?.id ?? null} // the signed-in user as ContentKit sees it
44
+ onSignIn={() => openSignIn()} // asks signed-out visitors to sign in where the server refuses them
45
+ urls={urls}
46
+ navigate={(to, o) => navigate(to, o)}
47
+ onChange={(change) => queryClient.invalidateQueries({ queryKey: ["media", change.ref.id] })}
48
+ onError={(error, { operation }) => report(operation, error)}
49
+ >
50
+ <ContentKitUiProvider appearance={{ theme: "inherit" }} language={lang} density={[2, 3]}>
51
+ <App />
52
+ </ContentKitUiProvider>
53
+ </ContentKitProvider>;
54
+ ```
55
+
56
+ - **Mounts.** Without `mounts`, modules live under `baseUrl`: content at the
57
+ root, `/media` (reads, HLS), `/media/upload`, `/codes`, `/taxonomy`.
58
+ - **`client.media`**: `upload`, `put` (upload, commit, wait), `commit`, `read`,
59
+ `waitFor`, `editorView`, `getFrame`, `hlsBase(ref, dir)`, `discard`, and
60
+ `xhrSetup` for players (`<MediaGallery xhrSetup={contentkit.media.xhrSetup} />`).
61
+ Uploads hash in a Web Worker, send one checksum-bound PUT up to 64 MiB and
62
+ resumable multipart above (`onState`/`resume`). The bucket must allow CORS
63
+ `PUT` from the app's origin with `Content-Type` and `x-amz-checksum-sha256`.
64
+ - **Content modules**, one per resource, each method one route of the
65
+ generated route table with its generated types: `posts`, `comments`,
66
+ `reactions`, `favorites`, `polls`, `bans` (`"owner"` or `"global"`),
67
+ `moderation`, `taxonomy`, `codes`. Lists take `limit`/`offset` (the review
68
+ queue a `cursor`); staff lists are `posts.adminList` (`q` searches,
69
+ `deleted: true` lists deleted posts for `posts.restore`), `polls.adminList`,
70
+ `comments.adminList`.
71
+ - **Images of posts and polls** go to the item's server-named upload path:
72
+ `media.uploadNamed(ref, file)` resolves the name content routes take;
73
+ `media.uploadInline(postId, file)` resolves a body image's public URL;
74
+ `posts.uploadCover`, `polls.uploadImage` and `polls.uploadOptionImage` do
75
+ both steps. `folders` renames the post and poll media kinds. A post's
76
+ public image files exist only while it is published; `usePost` shows an
77
+ unpublished post's images to its editors (below).
78
+ - **`client.config()`** says what the content module allows: which
79
+ interactions signed-out visitors may make (`content.Options.Anonymous`,
80
+ none by default) and the longest comment (`CommentMaxLength`, 400 by
81
+ default; longer is `comment_too_long` with `details.max`).
82
+ - **`client.subscribe(listener)`** receives every successful mutation
83
+ (`media.committed`, `media.processed`, `comment.created`, `reaction.changed`,
84
+ `poll.updated`, `ban.saved`, …); `ContentKitProvider onChange` is the same
85
+ stream, for the host's cache.
86
+ - **`ContentKitProvider`** holds the client, URL config, `navigate`,
87
+ `onChange`, `onError` and the read store hooks share: one request per item
88
+ and options while any hook shows it; `set()` and processed uploads update
89
+ every reader in place.
90
+ - **`ContentKitUiProvider`**: `appearance` (`theme`
91
+ `light`/`dark`/`auto`/`inherit`, `variables` as `--ckui-*`), `language`
92
+ (loads its built-in bundle), `messages` (bundles over it), `t` (host
93
+ translate hook), `density` (default `[2, 3]`), `inlinePreview` (default true).
94
+
95
+ ## Hooks
96
+
97
+ Hooks read the client from `ContentKitProvider`; a `client` option overrides it.
98
+
99
+ ```tsx
100
+ const q = useUploadQueue({ ref, path: "originals/{name}" });
101
+ q.add(input.files!); q.move(id, 0); await q.commit();
102
+
103
+ const one = useUpload();
104
+ await one.upload(file, { ref, path: "inline/x.png", put: {} });
105
+
106
+ const img = useSlotImage({ ref, path: "avatar", image });
107
+ const crop = useSlotCrop({ ref, path: "avatar", file: img.file, aspect: "1:1", onSaved: img.set });
108
+ ```
109
+
110
+ Content hooks share one store per client: one request per item while any
111
+ hook shows it, kept per `viewer`, with every change made through the client
112
+ applied in place. Reactions, favorites and votes apply at once and roll back
113
+ if the server refuses them.
114
+
115
+ ```tsx
116
+ const thread = useComments(ref, { sort: "best" });
117
+ await thread.post("Nice!", { replyTo: parentId });
118
+ await thread.react(comment, 1);
119
+ const { counts, toggle } = useReaction(ref);
120
+ const { favorited, count, toggle: fav } = useFavorite(ref);
121
+ const { poll, vote } = usePoll(null, { language }); // the newest live poll
122
+ const editor = usePollEditor(pollId); // staff
123
+ ```
124
+
125
+ A post editor puts `editorBody` in its rich-text field and inserts what
126
+ `uploadImage(file)` resolves: URLs that show the images now (the file for a
127
+ fresh upload, signed editor views of an unpublished post's images, read and
128
+ refreshed as needed). `update({ body })` stores the images' public URLs
129
+ (`storedBody(html)` does the same for a host's own save); `imageSrc(url)` shows
130
+ the cover.
131
+
132
+ ```tsx
133
+ const editor = usePost(postId);
134
+ <RichText value={editor.editorBody} onImage={editor.uploadImage} onSave={(html) => editor.update({ body: html })} />
135
+ <img src={editor.imageSrc(editor.post?.cover_url)} alt="" />
136
+ ```
137
+
138
+ Also `useContentConfig` (`client.config()`, read once), `useCommentReplies`,
139
+ `useCanComment` (may the caller comment, may signed-out visitors, the ban
140
+ that stops it, and what it may do to others' comments), `useLatestComments`,
141
+ `usePolls`, `usePosts` (staff: `admin`, `deleted`, `q`), `usePost` (with `restore`), and for staff `useAdminComments`,
142
+ `useModerationQueue`, `useCommentBans`.
143
+
144
+ Also: `useCrop`, `useVideoImages`, `useFrameStrip`, `useVideoFrame`,
145
+ `useVideoPoster`, `useEncodeProgress`, `useHlsPlayer`, `useCarousel`,
146
+ `useGalleryView`, `useInlinePreview`, `useRefreshBeforeExpiry`, `usePresets`,
147
+ `useContentKitClient`, `useContentURLs`, `useErrorReporter`.
148
+
149
+ ### useMediaRead
150
+
151
+ ```tsx
152
+ const { read, loading, error, processing, reload, refresh, set } =
153
+ useMediaRead(ref, { prefix: "low-res/", editor: true, window: { start: 0, end: 120 } });
154
+ ```
155
+
156
+ One read per item and options, shared by every hook showing it. It reads
157
+ again shortly before the URLs expire, waits out `rate_limited`, polls while
158
+ uploads process (editor reads, every 2.5 s; `poll`), and reloads after any
159
+ commit through the same client. `window` splits a long item into reads of
160
+ `chunk` files (default 50) merged into one result; windows once read stay
161
+ loaded. `refresh()` joins a read in flight (a player's grant refresh);
162
+ `reload()` restarts it. `read` shows a host's read until `reload()`.
163
+
164
+ ### useMediaFolder
165
+
166
+ ```tsx
167
+ const folder = useMediaFolder(ref, { paths: ["images/{name}", "videos/{name}"], commit: "manual" });
168
+ folder.add(files); // screened, queued; returns the refusals
169
+ await folder.commit(); // the uploaded files, in queue order
170
+ await folder.move(path, 0); await folder.rename(path, "cover");
171
+ await folder.edit(path, edit); await folder.replace(path, file); await folder.remove([path]);
172
+ folder.discard(); // stop uploads and empty the queue
173
+ ```
174
+
175
+ An item's folder for its editors: the editor read (all windows), an upload
176
+ queue, and the kind's upload rules from the editor read, which screen type,
177
+ size and file caps before anything uploads and name files around names
178
+ taken. `groups` lists each path's uploads; `commit: "auto"` commits each
179
+ upload once those before it have (a draft). Failures go to the provider's
180
+ `onError` with an operation (`upload`, `folder.commit`, `folder.update`,
181
+ `folder.process` for uploads the worker fails after the editor opened) and
182
+ the file's name.
183
+
184
+ ### useEditorCrop
185
+
186
+ ```tsx
187
+ const crop = useEditorCrop(ref, cropping); // a path, or null
188
+ // crop.status: idle | loading | cropping (source, edit) | saving | done | error
189
+ await crop.save(edit);
190
+ ```
191
+
192
+ Re-crops a kept upload: while the path is set, its editor view (the
193
+ original, oriented, unedited) and current edit load; `save` commits an edit
194
+ op. Nothing is uploaded.
195
+
196
+ ### usePublicImage
197
+
198
+ ```tsx
199
+ const { image, rule, isDefault } = usePublicImage("user", userId, "avatar");
200
+ <SlotImage image={image} fallback={<Initials name={name} />} />
201
+ ```
202
+
203
+ An item's image for a public preset: the exact renditions from a read's
204
+ `public`, or from a host listing passed as `image` (no read), else the
205
+ kind's default image. Published files carry a generation, so a URL built
206
+ from a preset template only ever names the default; `rule` (from
207
+ `GET /media/presets`) gives the shape, widths and narrowest edit.
208
+
209
+ ### useCanonicalContent
210
+
211
+ ```tsx
212
+ useCanonicalContent(video.link, { title: video.title, image: posterURL, location: pathname + search, defaultLanguage: "en" });
213
+ ```
214
+
215
+ Once a content page's link arrives, replaces the address with its canonical
216
+ path (code spelling, merged code, current slug) through the provider's
217
+ `navigate`, else `history.replaceState`, and keeps `<link rel=canonical>`,
218
+ `og:url`, `og:title`, `og:image` and hreflang alternates (each language's
219
+ own slug, plus `x-default`) in the head while mounted. Needs the provider's
220
+ `urls`; `/urls` also exports `hreflang()` and `canonicalURL()` (drops
221
+ tracking parameters) for server rendering.
222
+
223
+ ### useNearViewport
224
+
225
+ ```tsx
226
+ const [ref, near] = useNearViewport({ margin: "800px 0px", leaveMargin: "2400px 0px" });
227
+ ```
228
+
229
+ Whether an element is near the viewport, to mount media as it approaches;
230
+ with `leaveMargin`, false again far away.
231
+
232
+ ## Components
233
+
234
+ `AvatarUpload`, `CoverUpload` (complete form fields); `SlotEditor` with
235
+ `SlotEditMenu`, `SlotEditError` and `useSlotEditor()` (the same pick, crop,
236
+ save and re-crop flow inside a host layout); `SlotImage`; `ImageCropDialog`;
237
+ `VideoPosterPicker`, `VideoPoster`; `EncodeProgress`; `RenditionImg`;
238
+ `MediaGallery`, `VideoPlayer` and `VideoMiniPlayer` (HLS with ABR, inline
239
+ muted previews, a grant refresh before `expires`, and a reason, Retry and
240
+ support code for every failure; see below). A `PublicPreset` (`{ preset, aspect, renditions }`)
241
+ is what a slot or poster shows; a read's `public` lists `PublicImage`s.
242
+
243
+ ### MediaFolderEditor
244
+
245
+ ```tsx
246
+ <MediaFolderEditor item={post} paths={["images/{name}", "videos/{name}"]} commit="auto" ref={handle}
247
+ toolbar={<ZipImport />} rowActions={(f) => <SetCover file={f} />} footer={note} />
248
+ handle.current.discard();
249
+ ```
250
+
251
+ `useMediaFolder` with its UI: a drop zone stating the rules, a queue with
252
+ progress, retry and remove, and each path's uploads as a sortable list with
253
+ thumbnail, name, edited badge, encode progress and failure; crop (with
254
+ `aspects` when the path has no preset shape), rename, replace and bulk
255
+ remove. `commit="manual"` adds the queue with "Add N"; `"auto"` commits as
256
+ files finish. `confirmRemove` replaces the browser's confirm.
257
+
258
+ ### SortableList
259
+
260
+ ```tsx
261
+ <SortableList items={rows} id={(r) => r.id} name={(r) => r.title} onMove={(from, to) => move(from, to)} layout="grid">
262
+ {(row, handle) => <>{handle}<Row row={row} /></>}
263
+ </SortableList>
264
+ ```
265
+
266
+ Reorders by a drag handle with a mouse, a finger or the keyboard (Space or
267
+ Enter lifts, arrows move, Space or Enter drops, Escape cancels), with
268
+ localized screen reader announcements. `onMove` fires once, on drop.
269
+
270
+ ### ImageCropDialog shapes
271
+
272
+ `aspects={["", "1:1", "4:5", "16:9"]}` adds a shape chooser (`""` is the
273
+ original's, turning with the image); a preview of the result and its size
274
+ sit under the controls (`preview={false}` hides them), and Reset returns to
275
+ the starting shape and edit.
276
+
277
+ ### MediaGallery item
278
+
279
+ `<MediaGallery item={post} prefix="low-res/" renderLocked={unlock} />` reads
280
+ through the client: the read (refreshed, reloaded after commits), HLS bases,
281
+ playlist auth and the grant refresh. Passing `read`, `hlsBase`, `xhrSetup`
282
+ and `refresh` yourself still works. A locked item's public teaser carries
283
+ the locked count and `renderLocked`, so the unlock is on the first slide;
284
+ the stage is never taller than `maxHeight` (default `80svh`).
285
+
286
+ ### VideoPosterPicker frames from another video
287
+
288
+ `frames={{ item: otherVideo, path: "source" }}` browses another item's video;
289
+ the chosen frame (cropped or whole) is uploaded to the poster path as an
290
+ image. Which video to offer is the host's choice.
291
+
292
+ ### HoverPreview
293
+
294
+ ```tsx
295
+ <div className="card relative"><Thumbnail /><HoverPreview item={video} duration={video.duration} /></div>
296
+ ```
297
+
298
+ A card's muted inline preview over the client's HLS base, fading in on hover
299
+ (or while it is the most visible card on touch screens), one page-wide, off
300
+ with reduced motion or Save-Data.
301
+
302
+ ### Images: fallback, skeleton, fit and LazyMount
303
+
304
+ `RenditionImg`, `SlotImage` and `VideoPoster` take `fallback` (shown without
305
+ renditions or after a failed load, e.g. a 404), and `RenditionImg` and
306
+ `SlotImage` take `skeleton` (a pulsing fill until loaded) and `fit`
307
+ (`cover` or `contain`). `<LazyMount placeholder={box}>` mounts its children
308
+ as it nears the viewport (`useNearViewport` options).
309
+
310
+ ### MediaReadinessNotice
311
+
312
+ `<MediaReadinessNotice item={post} description="Only editors see it until then." />`
313
+ renders nothing once every upload is processed; while some process, a
314
+ notice with the first video's encode progress; when uploads failed, which
315
+ ones.
316
+
317
+ ## Video player
318
+
319
+ `VideoPlayer` plays a ladder's HLS folder (`client.media.hlsBase(ref, dir)`)
320
+ with its own controls: seek bar with sprite preview, volume, subtitles,
321
+ audio, quality (Auto or a fixed rung), speed, picture in picture, theater,
322
+ fullscreen and a mini player.
323
+
324
+ ```tsx
325
+ <VideoPlayer
326
+ base={contentkit.media.hlsBase(ref, "hls/")}
327
+ xhrSetup={contentkit.media.xhrSetup}
328
+ refresh={reload} expires={read.expires} // re-grant before the token expires
329
+ width={video.w} height={video.h} duration={video.dur} poster={posterPreset}
330
+ autoPlay startAt={resumePoint} // resume; never a muted autoplay
331
+ keyboard="global" // a watch page; default "focus"
332
+ audioLanguage={lang} subtitleLanguage={null} // until the viewer chooses
333
+ onProgress={(p) => saveResume(p.time, p.played)} // interval, pause, seek, end, page hide, unload
334
+ onEvent={(e) => analytics(e)} // play, pause, seek, quality, audio, subtitles, fullscreen, error…
335
+ theater={theater} onTheaterChange={setTheater}
336
+ onMiniPlayer={setHandoff}
337
+ renderDownloads={({ className, container }) => <Downloads className={className} container={container} />}
338
+ menuItems={[{ label: "Version", value, options, onChange }, { label: "Report a problem", onSelect }]}
339
+ />
340
+ {handoff && <VideoMiniPlayer handoff={handoff} xhrSetup={contentkit.media.xhrSetup} onExpand={openWatchPage} onClose={() => setHandoff(null)} />}
341
+ ```
342
+
343
+ - **Shortcuts:** Space/K play, ←/→ 5 s, J/L 10 s, ↑/↓ volume, M mute, C
344
+ subtitles, F fullscreen, T theater, I mini player, 0–9 a tenth of the way,
345
+ Home/End, `<`/`>` speed. Never while typing or in a menu.
346
+ - **Touch:** a tap shows or hides the controls; a double tap on either side
347
+ seeks 10 s, and further taps keep seeking. A double click is fullscreen.
348
+ - **Remembered per browser:** volume and mute (`ckui.player.volume`), the
349
+ audio and subtitle languages (`ckui.player.tracks`) and a fixed quality
350
+ (`ckui.player.quality`); each `*Key` option renames or disables one.
351
+ - **Mini player:** `onMiniPlayer` gets a `PlayerHandoff` (position, playing,
352
+ languages, size, poster). `VideoMiniPlayer` docks it in a corner and calls
353
+ `onExpand({ time, playing })` or `onClose({ time })`; where it lives and
354
+ what expand opens are the host's.
355
+ - **Theming:** `appearance.variables.playerAccent` colors the played range
356
+ and pressed toggles (default white). Menus are always dark.
357
+
358
+ ## Comments, reactions and polls
359
+
360
+ `Comments` (threads with one-level replies, tombstones, held and rejected
361
+ states shown to their author, reactions, edit, delete, ban and rate-limit
362
+ notices, the server's longest comment), `ReactionButtons`, `FavoriteButton` (with the server's count)
363
+ and `Poll` (a final vote with results scaled to the leading option, or a
364
+ free-text answer and its groups; `results="always"` shows results before
365
+ voting). Signed-out visitors comment under a name, react and vote where the
366
+ server allows it (`content.Options.Anonymous`, read from the comment standing
367
+ and `client.config()`); elsewhere they are asked to sign in (`onSignIn`, the
368
+ provider's or their own) or, without one, told to.
369
+
370
+ Staff: `CommentModeration` (every comment, the review queue, the site's
371
+ bans), `CommentBans` with `CommentBanDialog`, and `PollEditor`. They show what
372
+ the caller's permissions allow and say so when a route refuses; the host
373
+ decides who sees them.
374
+
375
+ ```tsx
376
+ <Comments item={{ kind: "video", id }} count={video.comment_count} userHref={(u) => `/u/${u.username}`} />
377
+ <Poll language={lang} />
378
+ <CommentModeration contentKinds={["video", "post"]} itemHref={(i) => `/${i.kind}/${i.id}`} />
379
+ ```
380
+
381
+ ## Errors
382
+
383
+ Every call rejects with `ContentKitError`: `code` (the server's, or `network`,
384
+ `storage`, `aborted`, `resume_mismatch`, `decode`, `render_timeout`),
385
+ `status`, `retryAfter`, `details` (image and video limits), `blobs`
386
+ (`not_uploaded`), `ban` (`comment_banned`) and `action` (`rate_limited`),
387
+ plus `refusal`, `isLimit`, `isCeiling`, `blocksQueue` and `transient`. Every
388
+ code has a message in every bundle (`useMessages().error(e)`).
389
+
390
+ ## Migrating from the two old packages
391
+
392
+ | Before | Now |
393
+ | --- | --- |
394
+ | `@openrails/contentkit-upload` | `@openrails/contentkit-ui/client` |
395
+ | `@openrails/contentkit-upload/react` | `@openrails/contentkit-ui/react` |
396
+ | `@openrails/contentkit-upload/ui` | `@openrails/contentkit-ui` |
397
+ | `@openrails/contentkit-upload/locales/*`, `/styles.css` | `@openrails/contentkit-ui/locales/*`, `/styles.css` |
398
+ | `@openrails/contentkit-urls` | `@openrails/contentkit-ui/urls` |
399
+ | `createUploadClient({ endpoint, readEndpoint })` | `createContentKitClient({ baseUrl, mounts: { upload, media } }).media` |
400
+ | `UploadClient`, `UploadApi` | `MediaClient`, `MediaApi` |
401
+ | `UploadUiProvider client onError …` | `ContentKitProvider client onError` + `ContentKitUiProvider appearance messages t density inlinePreview` |
402
+ | `UploadError`, `UploadErrorCode` | `ContentKitError`, `ContentKitErrorCode` |
403
+ | `useUploadQueue(client, o)`, `useUpload(client)` | `useUploadQueue(o)`, `useUpload()` |
404
+ | `useRead(client, ref, o)` | `useMediaRead(ref, o)` |
405
+ | `useSlotImage(client, o)`, `useSlotCrop(client, o)` | `useSlotImage(o)`, `useSlotCrop(o)` |
406
+ | `useVideoImages`, `useFrameStrip`, `useVideoFrame`, `useVideoPoster` `(client, o)` | the same `(o)` |
407
+ | `PublicImage` (preset at an item) | `PublicPreset` |
408
+ | `SlotImage`, `VideoPoster` `placeholder` | `fallback` |
409
+ | `UploadUi*` types, `UploadUiRoot` | `ContentKitUi*`, `ContentKitUiRoot` |
410
+
411
+ ## Development
412
+
413
+ ```sh
414
+ pnpm check # typecheck, lint, unit + jsdom tests, build
415
+ CONTENTKIT_TEST_URL=… CONTENTKIT_TEST_S3_ENDPOINT=… CONTENTKIT_TEST_S3_ACCESS_KEY=… CONTENTKIT_TEST_S3_SECRET_KEY=… \
416
+ pnpm test:integration # the real handlers (contentkit.Runtime.Handler) over PostgreSQL and MinIO
417
+ pnpm build && pnpm screenshots # demo/ in Chromium, light/dark × desktop/mobile
418
+ # (social.html needs the integration variables)
419
+ ```
420
+
421
+ The wire types, route table and error codes in `src/client/generated/` are
422
+ generated from ContentKit's route catalog (`pnpm contract`; CI runs
423
+ `pnpm contract:check`). UI primitives come from
424
+ `pnpm dlx shadcn@4.21.0 add …` (`components.json`); local edits are marked
425
+ `// Local:`. The `release` workflow publishes the package to npm for every
426
+ published `v*` release.
@@ -0,0 +1,38 @@
1
+ import { CSSProperties } from 'react';
2
+ /**
3
+ * `light`/`dark` force a palette, `auto` follows the OS, `inherit` reads the
4
+ * host's shadcn tokens (`--primary`, …) and its `.dark` class.
5
+ */
6
+ export type ContentKitUiTheme = "light" | "dark" | "auto" | "inherit";
7
+ /** Raw CSS values applied as `--ckui-*` custom properties on every root. */
8
+ export interface ContentKitUiVariables {
9
+ background?: string;
10
+ foreground?: string;
11
+ card?: string;
12
+ cardForeground?: string;
13
+ popover?: string;
14
+ popoverForeground?: string;
15
+ primary?: string;
16
+ primaryForeground?: string;
17
+ secondary?: string;
18
+ secondaryForeground?: string;
19
+ muted?: string;
20
+ mutedForeground?: string;
21
+ accent?: string;
22
+ accentForeground?: string;
23
+ destructive?: string;
24
+ warning?: string;
25
+ border?: string;
26
+ input?: string;
27
+ ring?: string;
28
+ radius?: string;
29
+ fontFamily?: string;
30
+ /** The video player's played range, seek thumb and pressed toggles. Default white. */
31
+ playerAccent?: string;
32
+ }
33
+ export interface ContentKitUiAppearance {
34
+ theme?: ContentKitUiTheme;
35
+ variables?: ContentKitUiVariables;
36
+ }
37
+ export declare function appearanceStyle(a: ContentKitUiAppearance | undefined): CSSProperties | undefined;
38
+ export declare function appearanceTheme(a: ContentKitUiAppearance | undefined): ContentKitUiTheme;
@@ -0,0 +1,15 @@
1
+ //#region src/appearance.ts
2
+ var e = (e) => e.replace(/[A-Z]/g, (e) => "-" + e.toLowerCase());
3
+ function t(t) {
4
+ if (!t?.variables) return;
5
+ let n = {};
6
+ for (let [r, i] of Object.entries(t.variables)) i && (n[r === "fontFamily" ? "--ckui-font" : `--ckui-${e(r)}`] = i);
7
+ return n;
8
+ }
9
+ function n(e) {
10
+ return e?.theme ?? "auto";
11
+ }
12
+ //#endregion
13
+ export { t as appearanceStyle, n as appearanceTheme };
14
+
15
+ //# sourceMappingURL=appearance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appearance.js","names":[],"sources":["../src/appearance.ts"],"sourcesContent":["import type { CSSProperties } from \"react\";\n\n/**\n * `light`/`dark` force a palette, `auto` follows the OS, `inherit` reads the\n * host's shadcn tokens (`--primary`, …) and its `.dark` class.\n */\nexport type ContentKitUiTheme = \"light\" | \"dark\" | \"auto\" | \"inherit\";\n\n/** Raw CSS values applied as `--ckui-*` custom properties on every root. */\nexport interface ContentKitUiVariables {\n background?: string;\n foreground?: string;\n card?: string;\n cardForeground?: string;\n popover?: string;\n popoverForeground?: string;\n primary?: string;\n primaryForeground?: string;\n secondary?: string;\n secondaryForeground?: string;\n muted?: string;\n mutedForeground?: string;\n accent?: string;\n accentForeground?: string;\n destructive?: string;\n warning?: string;\n border?: string;\n input?: string;\n ring?: string;\n radius?: string;\n fontFamily?: string;\n /** The video player's played range, seek thumb and pressed toggles. Default white. */\n playerAccent?: string;\n}\n\nexport interface ContentKitUiAppearance {\n theme?: ContentKitUiTheme;\n variables?: ContentKitUiVariables;\n}\n\nconst kebab = (k: string) => k.replace(/[A-Z]/g, (c) => \"-\" + c.toLowerCase());\n\nexport function appearanceStyle(a: ContentKitUiAppearance | undefined): CSSProperties | undefined {\n if (!a?.variables) return undefined;\n const style: Record<string, string> = {};\n for (const [key, value] of Object.entries(a.variables)) {\n if (value) style[key === \"fontFamily\" ? \"--ckui-font\" : `--ckui-${kebab(key)}`] = value;\n }\n return style as CSSProperties;\n}\n\nexport function appearanceTheme(a: ContentKitUiAppearance | undefined): ContentKitUiTheme {\n return a?.theme ?? \"auto\";\n}\n"],"mappings":";AAwCA,IAAM,KAAS,MAAc,EAAE,QAAQ,WAAW,MAAM,MAAM,EAAE,YAAY,CAAC;AAE7E,SAAgB,EAAgB,GAAkE;CAChG,IAAI,CAAC,GAAG,WAAW;CACnB,IAAM,IAAgC,CAAC;CACvC,KAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,EAAE,SAAS,GACnD,AAAI,MAAO,EAAM,MAAQ,eAAe,gBAAgB,UAAU,EAAM,CAAG,OAAO;CAEpF,OAAO;AACT;AAEA,SAAgB,EAAgB,GAA0D;CACxF,OAAO,GAAG,SAAS;AACrB"}