@djangocfg/ui-tools 2.1.404 → 2.1.408

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 (336) hide show
  1. package/README.md +9 -11
  2. package/dist/file-icon/index.cjs +449 -61
  3. package/dist/file-icon/index.cjs.map +1 -1
  4. package/dist/file-icon/index.d.cts +56 -18
  5. package/dist/file-icon/index.d.ts +56 -18
  6. package/dist/file-icon/index.mjs +448 -62
  7. package/dist/file-icon/index.mjs.map +1 -1
  8. package/dist/tree/index.cjs +49 -22
  9. package/dist/tree/index.cjs.map +1 -1
  10. package/dist/tree/index.d.cts +9 -3
  11. package/dist/tree/index.d.ts +9 -3
  12. package/dist/tree/index.mjs +49 -22
  13. package/dist/tree/index.mjs.map +1 -1
  14. package/dist/{types-B_zhyAqR.d.cts → types-eEu8SeiQ.d.cts} +4 -0
  15. package/dist/{types-B_zhyAqR.d.ts → types-eEu8SeiQ.d.ts} +4 -0
  16. package/package.json +13 -16
  17. package/src/components/FloatingToolbar/index.tsx +37 -3
  18. package/src/lib/page-snapshot/__tests__/capture-integration.test.ts +85 -0
  19. package/src/lib/page-snapshot/__tests__/engine.test.ts +36 -0
  20. package/src/lib/page-snapshot/__tests__/redaction-integration.test.ts +99 -0
  21. package/src/lib/page-snapshot/__tests__/tokens.test.ts +17 -0
  22. package/src/lib/page-snapshot/capture/__tests__/budget.test.ts +49 -0
  23. package/src/lib/page-snapshot/capture/__tests__/chrome-filter.test.ts +47 -0
  24. package/src/lib/page-snapshot/capture/__tests__/fold.test.ts +66 -0
  25. package/src/lib/page-snapshot/capture/__tests__/scope.test.ts +74 -0
  26. package/src/lib/page-snapshot/capture/__tests__/walk.test.ts +129 -0
  27. package/src/lib/page-snapshot/capture/accessible-name.ts +73 -0
  28. package/src/lib/page-snapshot/capture/budget.ts +95 -0
  29. package/src/lib/page-snapshot/capture/chrome-filter.ts +81 -0
  30. package/src/lib/page-snapshot/capture/classify.ts +111 -0
  31. package/src/lib/page-snapshot/capture/dom-utils.ts +111 -0
  32. package/src/lib/page-snapshot/capture/fold.ts +96 -0
  33. package/src/lib/page-snapshot/capture/scope.ts +169 -0
  34. package/src/lib/page-snapshot/capture/walk.ts +250 -0
  35. package/src/lib/page-snapshot/cst/__tests__/serialize.test.ts +50 -0
  36. package/src/lib/page-snapshot/cst/directives.ts +47 -0
  37. package/src/lib/page-snapshot/cst/payload.ts +50 -0
  38. package/src/lib/page-snapshot/cst/serialize.ts +84 -0
  39. package/src/lib/page-snapshot/cst/types.ts +115 -0
  40. package/src/lib/page-snapshot/engine.ts +176 -0
  41. package/src/lib/page-snapshot/index.ts +93 -0
  42. package/src/lib/page-snapshot/react/PageSnapshotChip.tsx +72 -0
  43. package/src/lib/page-snapshot/react/PageSnapshotPreview.tsx +78 -0
  44. package/src/lib/page-snapshot/react/__tests__/PageSnapshotChip.test.tsx +54 -0
  45. package/src/lib/page-snapshot/react/__tests__/provider.test.tsx +103 -0
  46. package/src/lib/page-snapshot/react/__tests__/use-page-snapshot-toggle.test.tsx +62 -0
  47. package/src/lib/page-snapshot/react/provider.tsx +162 -0
  48. package/src/lib/page-snapshot/react/use-page-snapshot-toggle.ts +47 -0
  49. package/src/lib/page-snapshot/react/use-page-snapshot.ts +67 -0
  50. package/src/lib/page-snapshot/redaction/__tests__/audit.test.ts +25 -0
  51. package/src/lib/page-snapshot/redaction/__tests__/heuristics.test.ts +73 -0
  52. package/src/lib/page-snapshot/redaction/__tests__/luhn.test.ts +26 -0
  53. package/src/lib/page-snapshot/redaction/__tests__/patterns.test.ts +60 -0
  54. package/src/lib/page-snapshot/redaction/audit.ts +58 -0
  55. package/src/lib/page-snapshot/redaction/heuristics.ts +75 -0
  56. package/src/lib/page-snapshot/redaction/index.ts +75 -0
  57. package/src/lib/page-snapshot/redaction/luhn.ts +25 -0
  58. package/src/lib/page-snapshot/redaction/patterns.ts +111 -0
  59. package/src/lib/page-snapshot/refs/__tests__/registry.test.ts +24 -0
  60. package/src/lib/page-snapshot/refs/registry.ts +46 -0
  61. package/src/lib/page-snapshot/staleness/__tests__/hash.test.ts +34 -0
  62. package/src/lib/page-snapshot/staleness/hash.ts +20 -0
  63. package/src/lib/page-snapshot/tokens.ts +15 -0
  64. package/src/tools/AudioPlayer/context/PlayerProvider.tsx +13 -14
  65. package/src/tools/AudioPlayer/hooks/useAudioElementEvents.ts +55 -6
  66. package/src/tools/AudioPlayer/lazy.tsx +13 -27
  67. package/src/tools/AudioPlayer/parts/Meta/TimeDisplay.tsx +2 -5
  68. package/src/tools/Chat/README.md +267 -39
  69. package/src/tools/Chat/composer/Composer.tsx +471 -0
  70. package/src/tools/Chat/composer/ComposerActionBar.tsx +65 -0
  71. package/src/tools/Chat/composer/ComposerBanner.tsx +128 -0
  72. package/src/tools/Chat/composer/ComposerButton.tsx +64 -0
  73. package/src/tools/Chat/composer/ComposerFooter.tsx +90 -0
  74. package/src/tools/Chat/composer/ComposerMenuButton.tsx +62 -0
  75. package/src/tools/Chat/composer/ComposerModelPicker.tsx +104 -0
  76. package/src/tools/Chat/composer/ComposerRichTextarea.tsx +88 -0
  77. package/src/tools/Chat/composer/ComposerToolPill.tsx +95 -0
  78. package/src/tools/Chat/composer/index.ts +45 -0
  79. package/src/tools/Chat/composer/size-context.tsx +26 -0
  80. package/src/tools/Chat/composer/types.ts +143 -0
  81. package/src/tools/Chat/composer/useComposerActions.tsx +164 -0
  82. package/src/tools/Chat/context/ChatProvider.tsx +54 -3
  83. package/src/tools/Chat/core/__tests__/metadata.test.ts +69 -0
  84. package/src/tools/Chat/core/index.ts +23 -1
  85. package/src/tools/Chat/core/markdown.ts +1 -1
  86. package/src/tools/Chat/core/metadata.ts +47 -0
  87. package/src/tools/Chat/core/payload-dispatch.ts +1 -1
  88. package/src/tools/Chat/core/transport/http.ts +71 -32
  89. package/src/tools/Chat/core/transport/sse.ts +18 -10
  90. package/src/tools/Chat/highlight/HighlightOverlay.tsx +101 -0
  91. package/src/tools/Chat/highlight/README.md +103 -0
  92. package/src/tools/Chat/highlight/SpotlightCanvas.tsx +153 -0
  93. package/src/tools/Chat/highlight/__tests__/HighlightOverlay.test.tsx +112 -0
  94. package/src/tools/Chat/highlight/__tests__/resolveRef.test.ts +55 -0
  95. package/src/tools/Chat/highlight/index.ts +21 -0
  96. package/src/tools/Chat/highlight/resolveRef.ts +42 -0
  97. package/src/tools/Chat/highlight/types.ts +49 -0
  98. package/src/tools/Chat/highlight/useHighlightTargets.ts +128 -0
  99. package/src/tools/Chat/hooks/index.ts +0 -5
  100. package/src/tools/Chat/hooks/useAutoFocusOnStreamEnd.ts +28 -47
  101. package/src/tools/Chat/hooks/useChat.ts +47 -14
  102. package/src/tools/Chat/hooks/useChatComposer.ts +2 -2
  103. package/src/tools/Chat/hooks/useChatLayout.ts +1 -1
  104. package/src/tools/Chat/hooks/useStreamEndFocus.ts +54 -0
  105. package/src/tools/Chat/index.ts +25 -219
  106. package/src/tools/Chat/launcher/ChatDock.tsx +1 -1
  107. package/src/tools/Chat/launcher/ChatLauncher.tsx +1 -1
  108. package/src/tools/Chat/launcher/{ChatHeader.tsx → header/ChatHeader.tsx} +24 -11
  109. package/src/tools/Chat/launcher/{ChatHeaderActionButton.tsx → header/ChatHeaderActionButton.tsx} +34 -3
  110. package/src/tools/Chat/launcher/{ChatHeaderLanguageButton.tsx → header/ChatHeaderLanguageButton.tsx} +2 -2
  111. package/src/tools/Chat/launcher/{ChatHeaderModeToggle.tsx → header/ChatHeaderModeToggle.tsx} +1 -1
  112. package/src/tools/Chat/launcher/{ChatHeaderResetButton.tsx → header/ChatHeaderResetButton.tsx} +2 -1
  113. package/src/tools/Chat/launcher/{HeaderSlots.tsx → header/HeaderSlots.tsx} +3 -3
  114. package/src/tools/Chat/launcher/header/index.ts +26 -0
  115. package/src/tools/Chat/launcher/index.ts +3 -10
  116. package/src/tools/Chat/lazy.tsx +38 -284
  117. package/src/tools/Chat/{components → messages}/MessageBubble.tsx +58 -5
  118. package/src/tools/Chat/{components → messages}/MessageList.tsx +8 -25
  119. package/src/tools/Chat/messages/blocks/MessageBlocks.tsx +131 -0
  120. package/src/tools/Chat/messages/blocks/builtin.tsx +91 -0
  121. package/src/tools/Chat/messages/blocks/index.ts +12 -0
  122. package/src/tools/Chat/messages/blocks/registry.tsx +42 -0
  123. package/src/tools/Chat/messages/blocks/renderers/AudioBlock.tsx +20 -0
  124. package/src/tools/Chat/messages/blocks/renderers/CodeBlock.tsx +19 -0
  125. package/src/tools/Chat/messages/blocks/renderers/GalleryBlock.tsx +26 -0
  126. package/src/tools/Chat/messages/blocks/renderers/ImageBlock.tsx +27 -0
  127. package/src/tools/Chat/messages/blocks/renderers/JsonBlock.tsx +12 -0
  128. package/src/tools/Chat/messages/blocks/renderers/LottieBlock.tsx +11 -0
  129. package/src/tools/Chat/messages/blocks/renderers/MapBlock.tsx +36 -0
  130. package/src/tools/Chat/messages/blocks/renderers/MermaidBlock.tsx +11 -0
  131. package/src/tools/Chat/messages/blocks/renderers/VideoBlock.tsx +24 -0
  132. package/src/tools/Chat/messages/blocks/renderers/types.ts +8 -0
  133. package/src/tools/Chat/{components → messages}/index.ts +11 -5
  134. package/src/tools/Chat/public.ts +212 -0
  135. package/src/tools/Chat/shell/ChatRoot.tsx +326 -0
  136. package/src/tools/Chat/{components → shell}/EmptyState.tsx +4 -2
  137. package/src/tools/Chat/shell/index.ts +15 -0
  138. package/src/tools/Chat/types/block.ts +120 -0
  139. package/src/tools/Chat/types/config.ts +0 -5
  140. package/src/tools/Chat/types/index.ts +17 -0
  141. package/src/tools/Chat/types/message.ts +3 -0
  142. package/src/tools/Chat/utils/index.ts +4 -0
  143. package/src/tools/CodeEditor/README.md +4 -6
  144. package/src/tools/CodeEditor/components/DiffEditor.tsx +48 -13
  145. package/src/tools/CodeEditor/components/Editor.tsx +96 -44
  146. package/src/tools/CodeEditor/context/EditorProvider.tsx +34 -17
  147. package/src/tools/CodeEditor/hooks/useEditorTheme.ts +92 -99
  148. package/src/tools/CodeEditor/hooks/useMonaco.ts +37 -22
  149. package/src/tools/CodeEditor/lazy.tsx +6 -0
  150. package/src/tools/CodeEditor/lib/index.ts +1 -1
  151. package/src/tools/CodeEditor/lib/themes.ts +3 -39
  152. package/src/tools/CronScheduler/CronScheduler.client.tsx +230 -61
  153. package/src/tools/CronScheduler/components/CustomInput.tsx +21 -4
  154. package/src/tools/CronScheduler/components/DayChips.tsx +13 -11
  155. package/src/tools/CronScheduler/components/MonthDayGrid.tsx +4 -4
  156. package/src/tools/CronScheduler/components/SchedulePreview.tsx +7 -3
  157. package/src/tools/CronScheduler/components/TimeSelector.tsx +1 -1
  158. package/src/tools/CronScheduler/index.tsx +1 -1
  159. package/src/tools/CronScheduler/types/index.ts +8 -3
  160. package/src/tools/CronScheduler/utils/cron-humanize.ts +61 -16
  161. package/src/tools/CronScheduler/utils/cron-parser.ts +13 -4
  162. package/src/tools/FileIcon/FileIcon.tsx +24 -39
  163. package/src/tools/FileIcon/get-file-icon.ts +73 -0
  164. package/src/tools/FileIcon/icons/icon-data.ts +399 -0
  165. package/src/tools/FileIcon/index.ts +4 -0
  166. package/src/tools/FileIcon/loader.ts +17 -35
  167. package/src/tools/FileIcon/specialFolders.ts +18 -0
  168. package/src/tools/Gallery/components/lightbox/GalleryLightbox.tsx +112 -35
  169. package/src/tools/Gallery/components/media/GalleryVideo.tsx +21 -2
  170. package/src/tools/Gallery/components/preview/GalleryCarousel.tsx +11 -1
  171. package/src/tools/Gallery/hooks/usePreloadImages.ts +54 -7
  172. package/src/tools/ImageViewer/components/ImageInfo.tsx +12 -1
  173. package/src/tools/ImageViewer/components/ImageToolbar.tsx +51 -43
  174. package/src/tools/ImageViewer/components/ImageViewer.tsx +106 -26
  175. package/src/tools/ImageViewer/hooks/useImageLoading.ts +13 -0
  176. package/src/tools/ImageViewer/utils/constants.ts +3 -0
  177. package/src/tools/ImageViewer/utils/index.ts +1 -0
  178. package/src/tools/JsonForm/JsonSchemaForm.tsx +4 -1
  179. package/src/tools/JsonForm/templates/ArrayFieldTemplate.tsx +5 -3
  180. package/src/tools/JsonForm/templates/BaseInputTemplate.tsx +7 -4
  181. package/src/tools/JsonForm/templates/ErrorListTemplate.tsx +3 -1
  182. package/src/tools/JsonForm/templates/ObjectFieldTemplate.tsx +23 -3
  183. package/src/tools/JsonForm/widgets/ColorWidget.tsx +20 -12
  184. package/src/tools/JsonForm/widgets/NumberWidget.tsx +14 -9
  185. package/src/tools/JsonForm/widgets/RadioWidget.tsx +78 -0
  186. package/src/tools/JsonForm/widgets/SelectWidget.tsx +1 -0
  187. package/src/tools/JsonForm/widgets/SliderWidget.tsx +7 -4
  188. package/src/tools/JsonForm/widgets/TextWidget.tsx +41 -17
  189. package/src/tools/JsonForm/widgets/index.ts +1 -0
  190. package/src/tools/JsonTree/components/JsonContent.tsx +115 -40
  191. package/src/tools/LottiePlayer/LottiePlayer.client.tsx +177 -72
  192. package/src/tools/LottiePlayer/index.tsx +14 -4
  193. package/src/tools/LottiePlayer/lazy.tsx +11 -3
  194. package/src/tools/LottiePlayer/types.ts +31 -1
  195. package/src/tools/LottiePlayer/useLottie.ts +32 -9
  196. package/src/tools/LottiePlayer/usePrefersReducedMotion.ts +46 -0
  197. package/src/tools/Map/components/LayerSwitcher.tsx +54 -21
  198. package/src/tools/Map/components/MapCluster.tsx +28 -21
  199. package/src/tools/Map/components/MapContainer.tsx +11 -4
  200. package/src/tools/Map/components/MapLegend.tsx +46 -15
  201. package/src/tools/Map/components/MapMarker.tsx +31 -2
  202. package/src/tools/Map/hooks/useMapEvents.ts +64 -105
  203. package/src/tools/MarkdownEditor/MarkdownEditor.tsx +61 -6
  204. package/src/tools/MarkdownEditor/MentionList.tsx +37 -4
  205. package/src/tools/MarkdownEditor/createMentionSuggestion.ts +11 -0
  206. package/src/tools/MarkdownEditor/lazy.tsx +32 -7
  207. package/src/tools/MarkdownEditor/styles.css +13 -0
  208. package/src/tools/MarkdownMessage/CodeBlock.tsx +40 -17
  209. package/src/tools/MarkdownMessage/MarkdownMessage.tsx +26 -6
  210. package/src/tools/MarkdownMessage/components.tsx +22 -9
  211. package/src/tools/MarkdownMessage/types.ts +24 -1
  212. package/src/tools/Mermaid/Mermaid.client.tsx +27 -5
  213. package/src/tools/Mermaid/components/MermaidErrorPanel.tsx +31 -0
  214. package/src/tools/Mermaid/components/MermaidFullscreenModal.tsx +14 -17
  215. package/src/tools/Mermaid/hooks/useMermaidRenderer.ts +264 -168
  216. package/src/tools/Mermaid/hooks/useMermaidValidation.ts +76 -10
  217. package/src/tools/Mermaid/index.tsx +6 -0
  218. package/src/tools/Mermaid/utils/mermaid-helpers.ts +141 -18
  219. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/FieldRow.tsx +11 -1
  220. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/buildTree.ts +49 -20
  221. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/index.tsx +7 -0
  222. package/src/tools/OpenapiViewer/components/DocsLayout/grouping.ts +7 -4
  223. package/src/tools/OpenapiViewer/constants.ts +3 -0
  224. package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +73 -11
  225. package/src/tools/OpenapiViewer/utils/schemaExport.ts +26 -6
  226. package/src/tools/PrettyCode/PrettyCode.client.tsx +23 -16
  227. package/src/tools/PrettyCode/lazy.tsx +1 -1
  228. package/src/tools/SpeechRecognition/README.md +1 -1
  229. package/src/tools/SpeechRecognition/__tests__/language.test.ts +9 -3
  230. package/src/tools/SpeechRecognition/components/RecordingPulse.tsx +59 -0
  231. package/src/tools/SpeechRecognition/components/index.ts +2 -0
  232. package/src/tools/SpeechRecognition/core/engine/external.ts +24 -7
  233. package/src/tools/SpeechRecognition/core/language.ts +23 -6
  234. package/src/tools/SpeechRecognition/hooks/usePushToTalk.ts +36 -5
  235. package/src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts +18 -11
  236. package/src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx +94 -26
  237. package/src/tools/SpeechRecognition/widgets/index.ts +1 -1
  238. package/src/tools/Tree/README.md +4 -8
  239. package/src/tools/Tree/TreeRoot.tsx +22 -10
  240. package/src/tools/Tree/components/TreeContent.tsx +24 -4
  241. package/src/tools/Tree/components/TreeLabel.tsx +8 -2
  242. package/src/tools/Tree/components/TreeRow.tsx +16 -6
  243. package/src/tools/Tree/data/flatten.ts +10 -4
  244. package/src/tools/Tree/types.ts +4 -0
  245. package/src/tools/Uploader/components/UploadAddButton.tsx +29 -6
  246. package/src/tools/Uploader/components/UploadDropzone.tsx +63 -7
  247. package/src/tools/Uploader/components/UploadPageDropOverlay.tsx +19 -5
  248. package/src/tools/Uploader/components/UploadPreviewItem.tsx +47 -17
  249. package/src/tools/Uploader/components/UploadPreviewList.tsx +24 -12
  250. package/src/tools/Uploader/utils/formatters.ts +8 -3
  251. package/src/tools/VideoPlayer/README.md +87 -230
  252. package/src/tools/VideoPlayer/VideoPlayer.tsx +82 -0
  253. package/src/tools/VideoPlayer/canvas/canvas-dispatcher.tsx +34 -0
  254. package/src/tools/VideoPlayer/canvas/hls-canvas.tsx +39 -0
  255. package/src/tools/VideoPlayer/canvas/iframe-canvas.tsx +33 -0
  256. package/src/tools/VideoPlayer/canvas/index.ts +12 -0
  257. package/src/tools/VideoPlayer/canvas/jsx-augmentation.ts +47 -0
  258. package/src/tools/VideoPlayer/canvas/native-canvas.tsx +38 -0
  259. package/src/tools/VideoPlayer/canvas/vimeo-canvas.tsx +40 -0
  260. package/src/tools/VideoPlayer/canvas/youtube-canvas.tsx +78 -0
  261. package/src/tools/VideoPlayer/index.ts +51 -65
  262. package/src/tools/VideoPlayer/lazy.tsx +11 -54
  263. package/src/tools/VideoPlayer/parts/controls-bar.tsx +35 -0
  264. package/src/tools/VideoPlayer/parts/fullscreen.tsx +19 -0
  265. package/src/tools/VideoPlayer/parts/index.ts +15 -0
  266. package/src/tools/VideoPlayer/parts/pip.tsx +19 -0
  267. package/src/tools/VideoPlayer/parts/play-button.tsx +19 -0
  268. package/src/tools/VideoPlayer/parts/playback-rate.tsx +31 -0
  269. package/src/tools/VideoPlayer/parts/poster.tsx +3 -0
  270. package/src/tools/VideoPlayer/parts/seek-bar.tsx +26 -0
  271. package/src/tools/VideoPlayer/parts/volume.tsx +32 -0
  272. package/src/tools/VideoPlayer/styles/video-player.css +141 -0
  273. package/src/tools/VideoPlayer/types.ts +82 -0
  274. package/src/tools/VideoPlayer/utils/parse-embed-url.ts +70 -0
  275. package/src/tools/VideoPlayer/utils/vimeo-id.ts +24 -0
  276. package/src/tools/VideoPlayer/utils/youtube-id.ts +64 -0
  277. package/src/tools/index.ts +37 -29
  278. package/src/tools/Chat/components/AudioToggle.tsx +0 -78
  279. package/src/tools/Chat/components/ChatRoot.tsx +0 -305
  280. package/src/tools/Chat/components/Composer.tsx +0 -216
  281. package/src/tools/Chat/hooks/useChatScroll.ts +0 -145
  282. package/src/tools/Chat/types.ts +0 -9
  283. package/src/tools/JsonTree/components/JsonToolbar.tsx +0 -95
  284. package/src/tools/JsonTree/hooks/useElementCorner.ts +0 -84
  285. package/src/tools/JsonTree/hooks/useNavbarHeight.ts +0 -83
  286. package/src/tools/OpenapiViewer/components/DocsLayout/schemaFields.ts +0 -121
  287. package/src/tools/Tour/README.md +0 -373
  288. package/src/tools/Tour/components/Tour.tsx +0 -12
  289. package/src/tools/Tour/components/TourContent.tsx +0 -171
  290. package/src/tools/Tour/components/TourNavigation.tsx +0 -77
  291. package/src/tools/Tour/components/TourProgress.tsx +0 -88
  292. package/src/tools/Tour/components/TourSpotlight.tsx +0 -199
  293. package/src/tools/Tour/components/index.ts +0 -5
  294. package/src/tools/Tour/context/TourContext.ts +0 -19
  295. package/src/tools/Tour/context/TourProvider.tsx +0 -292
  296. package/src/tools/Tour/context/index.ts +0 -2
  297. package/src/tools/Tour/hooks/index.ts +0 -3
  298. package/src/tools/Tour/hooks/useKeyboardNavigation.ts +0 -59
  299. package/src/tools/Tour/hooks/useStepTarget.ts +0 -121
  300. package/src/tools/Tour/hooks/useTour.ts +0 -42
  301. package/src/tools/Tour/index.ts +0 -38
  302. package/src/tools/Tour/types/index.ts +0 -224
  303. package/src/tools/Tour/utils/dom.ts +0 -98
  304. package/src/tools/Tour/utils/index.ts +0 -3
  305. package/src/tools/Tour/utils/logger.ts +0 -3
  306. package/src/tools/Tour/utils/scrollIntoView.ts +0 -24
  307. package/src/tools/VideoPlayer/components/VideoControls.tsx +0 -138
  308. package/src/tools/VideoPlayer/components/VideoErrorFallback.tsx +0 -172
  309. package/src/tools/VideoPlayer/components/VideoPlayer.tsx +0 -201
  310. package/src/tools/VideoPlayer/components/index.ts +0 -14
  311. package/src/tools/VideoPlayer/context/VideoPlayerContext.tsx +0 -52
  312. package/src/tools/VideoPlayer/context/index.ts +0 -8
  313. package/src/tools/VideoPlayer/hooks/index.ts +0 -12
  314. package/src/tools/VideoPlayer/hooks/useVideoPlayerSettings.ts +0 -71
  315. package/src/tools/VideoPlayer/hooks/useVideoPositionCache.ts +0 -117
  316. package/src/tools/VideoPlayer/providers/NativeProvider.tsx +0 -284
  317. package/src/tools/VideoPlayer/providers/StreamProvider.tsx +0 -505
  318. package/src/tools/VideoPlayer/providers/VidstackProvider.tsx +0 -397
  319. package/src/tools/VideoPlayer/providers/index.ts +0 -8
  320. package/src/tools/VideoPlayer/types/index.ts +0 -38
  321. package/src/tools/VideoPlayer/types/player.ts +0 -116
  322. package/src/tools/VideoPlayer/types/provider.ts +0 -93
  323. package/src/tools/VideoPlayer/types/sources.ts +0 -97
  324. package/src/tools/VideoPlayer/utils/debug.ts +0 -14
  325. package/src/tools/VideoPlayer/utils/fileSource.ts +0 -78
  326. package/src/tools/VideoPlayer/utils/index.ts +0 -12
  327. package/src/tools/VideoPlayer/utils/resolvers.ts +0 -75
  328. /package/src/tools/Chat/{config.ts → constants.ts} +0 -0
  329. /package/src/tools/Chat/launcher/{ChatHeaderAudioToggle.tsx → header/ChatHeaderAudioToggle.tsx} +0 -0
  330. /package/src/tools/Chat/{components → messages}/Attachments.tsx +0 -0
  331. /package/src/tools/Chat/{components → messages}/JumpToLatest.tsx +0 -0
  332. /package/src/tools/Chat/{components → messages}/MessageActions.tsx +0 -0
  333. /package/src/tools/Chat/{components → messages}/Sources.tsx +0 -0
  334. /package/src/tools/Chat/{components → messages}/StreamingIndicator.tsx +0 -0
  335. /package/src/tools/Chat/{components → messages}/ToolCalls.tsx +0 -0
  336. /package/src/tools/Chat/{components → shell}/ErrorBanner.tsx +0 -0
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useState, useCallback, useEffect } from 'react';
3
+ import { useState, useCallback, useEffect, useRef } from 'react';
4
4
  import {
5
5
  useBatchAddListener,
6
6
  useItemProgressListener,
@@ -28,6 +28,11 @@ export function UploadPreviewList({
28
28
  const abortItem = useAbortItem();
29
29
  const { upload } = useUploady();
30
30
 
31
+ // Live ref to items so unmount cleanup sees the latest object URLs
32
+ // (a [] effect would only ever capture the initial empty Map).
33
+ const itemsRef = useRef(items);
34
+ itemsRef.current = items;
35
+
31
36
  // Batch added - create initial items
32
37
  useBatchAddListener((batch) => {
33
38
  setItems(prev => {
@@ -36,8 +41,8 @@ export function UploadPreviewList({
36
41
  const file = item.file as File;
37
42
  let previewUrl: string | undefined;
38
43
 
39
- // Create object URL for image preview
40
- if (file.type.startsWith('image/')) {
44
+ // Create object URL for image/video preview
45
+ if (file.type.startsWith('image/') || file.type.startsWith('video/')) {
41
46
  previewUrl = URL.createObjectURL(file);
42
47
  }
43
48
 
@@ -80,11 +85,18 @@ export function UploadPreviewList({
80
85
  ? buildAssetFromResponse(response, existing.file)
81
86
  : undefined;
82
87
 
88
+ // The server now hosts the file — revoke the local object URL so it
89
+ // can be garbage-collected, and keep only a server-hosted preview URL.
90
+ if (existing.previewUrl) {
91
+ URL.revokeObjectURL(existing.previewUrl);
92
+ }
93
+
83
94
  next.set(item.id, {
84
95
  ...existing,
85
96
  status: 'complete',
86
97
  progress: 100,
87
98
  asset,
99
+ previewUrl: asset?.thumbnailUrl || asset?.url || undefined,
88
100
  });
89
101
  }
90
102
  return next;
@@ -153,15 +165,15 @@ export function UploadPreviewList({
153
165
  const handleRetry = useCallback((id: string) => {
154
166
  const item = items.get(id);
155
167
  if (item && (item.status === 'error' || item.status === 'aborted')) {
156
- // Reset status to pending
168
+ // rpldy.upload() creates a NEW batch item with a fresh id, so the old
169
+ // entry must be dropped (and its object URL revoked) to avoid a stale
170
+ // ghost item and a leaked URL. useBatchAddListener adds the new one.
171
+ if (item.previewUrl) {
172
+ URL.revokeObjectURL(item.previewUrl);
173
+ }
157
174
  setItems(prev => {
158
175
  const next = new Map(prev);
159
- next.set(id, {
160
- ...item,
161
- status: 'pending',
162
- progress: 0,
163
- error: undefined,
164
- });
176
+ next.delete(id);
165
177
  return next;
166
178
  });
167
179
 
@@ -171,10 +183,10 @@ export function UploadPreviewList({
171
183
  }
172
184
  }, [items, upload, onRetry]);
173
185
 
174
- // Cleanup object URLs on unmount
186
+ // Cleanup object URLs on unmount (reads live ref, not a stale closure)
175
187
  useEffect(() => {
176
188
  return () => {
177
- items.forEach(item => {
189
+ itemsRef.current.forEach(item => {
178
190
  if (item.previewUrl) {
179
191
  URL.revokeObjectURL(item.previewUrl);
180
192
  }
@@ -27,17 +27,22 @@ export function truncateFilename(filename: string, maxLength = 32): string {
27
27
  }
28
28
 
29
29
  export function formatFileSize(bytes: number): string {
30
- if (bytes === 0) return '0 B';
30
+ if (!Number.isFinite(bytes) || bytes <= 0) return '0 B';
31
31
 
32
- const units = ['B', 'KB', 'MB', 'GB', 'TB'];
32
+ const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
33
33
  const k = 1024;
34
- const i = Math.floor(Math.log(bytes) / Math.log(k));
34
+ // Clamp the unit index so absurdly large values never index out of bounds.
35
+ const i = Math.min(
36
+ Math.floor(Math.log(bytes) / Math.log(k)),
37
+ units.length - 1,
38
+ );
35
39
  const size = bytes / Math.pow(k, i);
36
40
 
37
41
  return `${size.toFixed(i > 0 ? 1 : 0)} ${units[i]}`;
38
42
  }
39
43
 
40
44
  export function formatDuration(seconds: number): string {
45
+ if (!Number.isFinite(seconds) || seconds < 0) return '0:00';
41
46
  const mins = Math.floor(seconds / 60);
42
47
  const secs = Math.floor(seconds % 60);
43
48
  return `${mins}:${secs.toString().padStart(2, '0')}`;
@@ -1,264 +1,121 @@
1
1
  # VideoPlayer
2
2
 
3
- Unified video player component supporting multiple modes and source types.
4
-
5
- ## Modes
6
-
7
- | Mode | Source Types | Use Case |
8
- |------|-------------|----------|
9
- | `vidstack` | YouTube, Vimeo, HLS, DASH | Full-featured player with themes and controls |
10
- | `native` | URL, data-url | Lightweight HTML5 player |
11
- | `streaming` | stream, blob | HTTP Range streaming with auth, binary data |
12
-
13
- Mode is auto-detected from source type, or can be forced via `mode` prop.
14
-
15
- ## Installation
3
+ Composable video player built on [media-chrome](https://media-chrome.org)
4
+ (Mux, MIT, ~12 KB). One `<MediaController>` shell drives **YouTube**,
5
+ **Vimeo**, **HLS**, native **MP4/WebM**, and arbitrary **iframe**
6
+ embeds — the source element swaps, the UI stays the same.
16
7
 
17
8
  ```tsx
18
- import {
19
- VideoPlayer,
20
- VideoPlayerProvider,
21
- VideoErrorFallback,
22
- resolveFileSource
23
- } from '@djangocfg/ui-nextjs';
24
- ```
9
+ import { VideoPlayer } from '@djangocfg/ui-tools/video-player';
25
10
 
26
- ## Basic Usage
11
+ // Structured source
12
+ <VideoPlayer source={{ type: 'youtube', videoId: 'sGbxmsDFVnE' }} />
27
13
 
28
- ### YouTube / Vimeo
29
-
30
- ```tsx
31
- <VideoPlayer source={{ type: 'youtube', id: 'dQw4w9WgXcQ' }} />
32
- <VideoPlayer source={{ type: 'vimeo', id: '123456789' }} />
14
+ // Raw URL — auto-classified (YouTube / Vimeo / HLS / MP4 / iframe)
15
+ <VideoPlayer source="https://youtu.be/sGbxmsDFVnE?t=90" />
33
16
  ```
34
17
 
35
- ### HLS / DASH Streaming
18
+ ## Why media-chrome
36
19
 
37
- ```tsx
38
- <VideoPlayer source={{ type: 'hls', url: 'https://example.com/video.m3u8' }} />
39
- <VideoPlayer source={{ type: 'dash', url: 'https://example.com/video.mpd' }} />
40
- ```
20
+ Native HTML5 `<video>` can't play YouTube. Hand-rolling the YouTube
21
+ IFrame API and keeping it in sync with a custom control bar is fragile.
22
+ media-chrome solves both: its `<media-controller>` speaks one protocol,
23
+ and provider elements (`<youtube-video>`, `<vimeo-video>`,
24
+ `<hls-video>`) plug into the same slot. Our control parts are thin
25
+ React wrappers themed through CSS custom properties.
41
26
 
42
- ### Direct URL
27
+ ## Sources
43
28
 
44
- ```tsx
45
- <VideoPlayer source={{ type: 'url', url: 'https://example.com/video.mp4' }} />
46
- ```
29
+ `VideoSource` is a discriminated union — pass an object, or a raw URL
30
+ string that `parseEmbedUrl` classifies for you.
47
31
 
48
- ### HTTP Range Streaming (with auth)
32
+ | `type` | Shape | Engine |
33
+ |---|---|---|
34
+ | `url` | `{ type:'url', url, mimeType?, poster?, title? }` | native `<video>` |
35
+ | `youtube` | `{ type:'youtube', videoId, startTime?, playlistId?, poster?, title? }` | `youtube-video-element` |
36
+ | `vimeo` | `{ type:'vimeo', videoId, startTime?, poster?, title? }` | `vimeo-video-element` |
37
+ | `hls` | `{ type:'hls', url, poster?, title? }` | `hls-video-element` (native in Safari, `hls.js` elsewhere) |
38
+ | `iframe` | `{ type:'iframe', url, allow?, poster?, title? }` | plain `<iframe>` — control bar auto-hidden |
49
39
 
50
40
  ```tsx
51
- // Full source (standalone)
52
- <VideoPlayer
53
- source={{
54
- type: 'stream',
55
- sessionId: 'abc123',
56
- path: '/videos/movie.mp4',
57
- getStreamUrl: (id, path) => `/api/stream/${id}?path=${path}&token=${token}`
58
- }}
59
- />
41
+ import { parseEmbedUrl } from '@djangocfg/ui-tools/video-player';
60
42
 
61
- // Simplified source (with context)
62
- <VideoPlayerProvider sessionId={sessionId} getStreamUrl={getStreamUrl}>
63
- <VideoPlayer source={{ type: 'stream', path: '/videos/movie.mp4' }} />
64
- </VideoPlayerProvider>
65
- ```
43
+ parseEmbedUrl('https://www.youtube.com/watch?v=ID&t=42s');
44
+ // { type: 'youtube', videoId: 'ID', startTime: 42 }
66
45
 
67
- ### Blob / ArrayBuffer
46
+ parseEmbedUrl('https://vimeo.com/76979871');
47
+ // → { type: 'vimeo', videoId: '76979871' }
68
48
 
69
- ```tsx
70
- <VideoPlayer
71
- source={{
72
- type: 'blob',
73
- data: arrayBuffer,
74
- mimeType: 'video/mp4'
75
- }}
76
- />
49
+ parseEmbedUrl('https://stream.mux.com/abc.m3u8');
50
+ // → { type: 'hls', url: '…' }
77
51
  ```
78
52
 
79
53
  ## Props
80
54
 
81
- | Prop | Type | Default | Description |
82
- |------|------|---------|-------------|
83
- | `source` | `VideoSourceUnion` | required | Video source configuration |
84
- | `mode` | `'auto' \| 'vidstack' \| 'native' \| 'streaming'` | `'auto'` | Force specific player mode |
85
- | `aspectRatio` | `number \| 'auto' \| 'fill'` | `16/9` | Aspect ratio or fill parent |
86
- | `autoPlay` | `boolean` | `false` | Auto-play on load |
87
- | `muted` | `boolean` | `false` | Muted by default |
88
- | `loop` | `boolean` | `false` | Loop playback |
89
- | `controls` | `boolean` | `true` | Show player controls |
90
- | `playsInline` | `boolean` | `true` | Inline playback on mobile |
91
- | `preload` | `'none' \| 'metadata' \| 'auto'` | `'metadata'` | Preload strategy |
92
- | `theme` | `'default' \| 'minimal' \| 'modern'` | `'default'` | Vidstack theme |
93
- | `errorFallback` | `ReactNode \| (props) => ReactNode` | - | Custom error UI |
94
- | `className` | `string` | - | Container className |
95
- | `videoClassName` | `string` | - | Video element className |
96
-
97
- ## Events
98
-
99
- | Event | Type | Description |
100
- |-------|------|-------------|
101
- | `onPlay` | `() => void` | Playback started |
102
- | `onPause` | `() => void` | Playback paused |
103
- | `onEnded` | `() => void` | Playback ended |
104
- | `onError` | `(error: string) => void` | Error occurred |
105
- | `onLoadStart` | `() => void` | Loading started |
106
- | `onCanPlay` | `() => void` | Ready to play |
107
- | `onTimeUpdate` | `(time: number, duration: number) => void` | Time updated |
108
-
109
- ## Ref API
110
-
111
- ```tsx
112
- const playerRef = useRef<VideoPlayerRef>(null);
113
-
114
- <VideoPlayer ref={playerRef} source={source} />
115
-
116
- // Control methods
117
- playerRef.current?.play();
118
- playerRef.current?.pause();
119
- playerRef.current?.seek(30); // Seek to 30 seconds
120
- playerRef.current?.setVolume(0.5); // 0-1
121
- playerRef.current?.setMuted(true);
122
- playerRef.current?.reload();
123
- ```
124
-
125
- ## Error Handling
126
-
127
- ### Custom Error Fallback
55
+ | Prop | Type | Default | Notes |
56
+ |---|---|---|---|
57
+ | `source` | `VideoSource \| string` | | Object or raw URL (auto-parsed). |
58
+ | `controls` | `boolean` | `true` | `false` no built-in control bar. |
59
+ | `aspectRatio` | `number \| 'auto' \| 'fill'` | `16/9` | `'fill'` stretches to parent height; `'auto'` keeps intrinsic. |
60
+ | `autoPlay` | `boolean` | `false` | Browsers require `muted` for autoplay to start. |
61
+ | `muted` / `loop` / `playsInline` | `boolean` | | Forwarded to the engine. |
62
+ | `preload` | `'none' \| 'metadata' \| 'auto'` | | Native sources only. |
63
+ | `crossOrigin` | `'' \| 'anonymous' \| 'use-credentials'` | `'anonymous'` | Native sources only. |
64
+ | `className` | `string` | | On the `<MediaController>` wrapper. |
65
+ | `children` | `ReactNode` | | Replaces the default control bar entirely. |
128
66
 
129
- ```tsx
130
- <VideoPlayer
131
- source={source}
132
- errorFallback={(props) => (
133
- <div>
134
- <p>Error: {props.error}</p>
135
- <button onClick={props.retry}>Retry</button>
136
- </div>
137
- )}
138
- />
139
- ```
140
-
141
- ### Pre-built Error Fallback with Download
142
-
143
- ```tsx
144
- import { VideoErrorFallback } from '@djangocfg/ui-nextjs';
145
-
146
- <VideoPlayer
147
- source={source}
148
- errorFallback={(props) => (
149
- <VideoErrorFallback
150
- {...props}
151
- downloadUrl={getDownloadUrl()}
152
- downloadFilename="video.mp4"
153
- fileSize="125 MB"
154
- />
155
- )}
156
- />
157
- ```
158
-
159
- ## Fill Mode
160
-
161
- Use `aspectRatio="fill"` to fill the parent container:
162
-
163
- ```tsx
164
- <div className="absolute inset-0">
165
- <VideoPlayer source={source} aspectRatio="fill" />
166
- </div>
167
- ```
67
+ ## Composable layout
168
68
 
169
- ## Context Provider
170
-
171
- For apps with multiple streaming videos, use the context to avoid repetition:
172
-
173
- ```tsx
174
- // In layout or parent component
175
- <VideoPlayerProvider
176
- sessionId={sessionId}
177
- getStreamUrl={(id, path) => `/api/stream/${id}?path=${path}`}
178
- >
179
- {/* All nested VideoPlayers use this config */}
180
- <VideoPlayer source={{ type: 'stream', path: '/video1.mp4' }} />
181
- <VideoPlayer source={{ type: 'stream', path: '/video2.mp4' }} />
182
- </VideoPlayerProvider>
183
- ```
184
-
185
- ## File Source Helper
186
-
187
- For file browser integration:
69
+ Bring your own control bar by passing `children`. Compose any
70
+ media-chrome element or our restyled parts:
188
71
 
189
72
  ```tsx
190
- import { resolveFileSource } from '@djangocfg/ui-nextjs';
191
-
192
- const source = resolveFileSource({
193
- file: { name: 'movie.mp4', path: '/videos/movie.mp4' },
194
- sessionId: 'abc123',
195
- getStreamUrl: (id, path) => `/api/stream/${id}?path=${path}`,
196
- });
197
-
198
- if (source) {
199
- return <VideoPlayer source={source} />;
200
- }
73
+ import {
74
+ VideoPlayer, ControlsBar, PlayButton, SeekBar,
75
+ Volume, PlaybackRate, Pip, Fullscreen,
76
+ } from '@djangocfg/ui-tools/video-player';
77
+
78
+ <VideoPlayer source={{ type: 'url', url: '/clip.mp4' }}>
79
+ <ControlsBar>
80
+ <PlayButton />
81
+ <SeekBar />
82
+ <Volume />
83
+ <PlaybackRate />
84
+ <Pip />
85
+ <Fullscreen />
86
+ </ControlsBar>
87
+ </VideoPlayer>
201
88
  ```
202
89
 
203
- ## Source Types Reference
90
+ ## Parts
204
91
 
205
- ```typescript
206
- type VideoSourceUnion =
207
- | { type: 'url'; url: string; title?: string; poster?: string }
208
- | { type: 'youtube'; id: string; title?: string; poster?: string }
209
- | { type: 'vimeo'; id: string; title?: string; poster?: string }
210
- | { type: 'hls'; url: string; title?: string; poster?: string }
211
- | { type: 'dash'; url: string; title?: string; poster?: string }
212
- | { type: 'stream'; sessionId: string; path: string; getStreamUrl: fn; mimeType?: string }
213
- | { type: 'blob'; data: ArrayBuffer | Blob; mimeType?: string }
214
- | { type: 'data-url'; data: string; mimeType?: string }
215
- ```
92
+ `PlayButton` · `SeekBar` · `Volume` · `PlaybackRate` · `Pip` ·
93
+ `Fullscreen` · `ControlsBar` · `Poster` — thin wrappers over
94
+ media-chrome components, restyled through semantic tokens. Each accepts
95
+ the props of the media-chrome element it wraps.
216
96
 
217
- ## Architecture
97
+ ## Theming
218
98
 
219
- ```
220
- VideoPlayer/
221
- ├── index.ts # Public API exports
222
- ├── types/
223
- │ ├── index.ts # Type re-exports
224
- │ ├── sources.ts # Source types (Url, YouTube, HLS, etc.)
225
- │ ├── player.ts # Player props, ref, events
226
- │ └── provider.ts # Provider & context types
227
- ├── hooks/
228
- │ ├── index.ts
229
- │ └── useVideoPositionCache.ts # Playback position caching
230
- ├── utils/
231
- │ ├── index.ts
232
- │ ├── resolvers.ts # resolvePlayerMode, isSimpleStreamSource
233
- │ └── fileSource.ts # resolveFileSource helper
234
- ├── components/
235
- │ ├── index.ts
236
- │ ├── VideoPlayer.tsx # Main orchestrator
237
- │ ├── VideoControls.tsx # Standalone Vidstack controls
238
- │ └── VideoErrorFallback.tsx # Pre-built error UI
239
- ├── context/
240
- │ ├── index.ts
241
- │ └── VideoPlayerContext.tsx # Streaming config provider
242
- └── providers/
243
- ├── index.ts
244
- ├── VidstackProvider.tsx # YouTube, Vimeo, HLS, DASH
245
- ├── NativeProvider.tsx # HTML5 <video>
246
- └── StreamProvider.tsx # HTTP Range, Blob
247
- ```
99
+ media-chrome reads CSS custom properties; `styles/video-player.css`
100
+ binds them to ui-core semantic tokens (`--primary`, `--popover`, …) via
101
+ `color-mix`, so the player follows light/dark and any active preset.
102
+ The video surface itself stays dark (`bg-black`) by convention.
248
103
 
249
- ## Accessibility
104
+ ## Notes
250
105
 
251
- Full accessibility support:
252
- - Keyboard navigation (Space, Arrow keys, F for fullscreen)
253
- - Screen reader announcements
254
- - Focus indicators
255
- - ARIA labels and roles
106
+ - **YouTube/Vimeo branding** — embed providers enforce a minimal logo;
107
+ `modestbranding` / `rel=0` reduce it but it cannot be fully removed.
108
+ - **PiP over YouTube** — depends on the embed; the button hides itself
109
+ when the engine reports no support.
110
+ - **HLS** native in Safari; elsewhere `hls-video-element` lazy-loads
111
+ `hls.js` (~110 KB) on first HLS mount only.
112
+ - **`LazyVideoPlayer`** — kept as a synchronous alias of `VideoPlayer`
113
+ for back-compat. No lazy boundary is needed; engines tree-shake per
114
+ source type.
256
115
 
257
- ## Browser Support
116
+ ## Storybook
258
117
 
259
- - Chrome 63+
260
- - Firefox 67+
261
- - Safari 12+
262
- - Edge 79+
263
- - iOS Safari 12+
264
- - Chrome Android 63+
118
+ `UI Tools / Video Player / Player` — `NativeMp4`, `YouTubeStarWars`,
119
+ `YouTubeStarTrek`, `YouTubeAutoParseUrl`, `YouTubeWithTimestamp`,
120
+ `Vimeo`, `Sintel`, `HlsStream`, `ComposableLayout`, `Fill`,
121
+ `NoControls`, `LazyAlias`.
@@ -0,0 +1,82 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * VideoPlayer — composable shell over `media-chrome` web components.
5
+ *
6
+ * The shell is just a `<MediaController>` wrapper + a `<CanvasDispatcher>`
7
+ * that mounts the right media element (`<video>`, `<youtube-video>`,
8
+ * `<vimeo-video>`, `<hls-video>`, `<iframe>`) into the `media` slot.
9
+ *
10
+ * Custom layouts: pass `children` to replace the default control bar.
11
+ * `controls={false}` opts out of any control surface (useful for
12
+ * iframe sources where the embed renders its own UI).
13
+ */
14
+
15
+ import { useMemo, type CSSProperties } from 'react';
16
+ import { MediaController } from 'media-chrome/react';
17
+ import { cn } from '@djangocfg/ui-core/lib';
18
+ import './styles/video-player.css';
19
+
20
+ import type { VideoPlayerProps, AspectRatioValue } from './types';
21
+ import { parseEmbedUrl } from './utils/parse-embed-url';
22
+ import { CanvasDispatcher } from './canvas/canvas-dispatcher';
23
+ import {
24
+ ControlsBar,
25
+ PlayButton,
26
+ SeekBar,
27
+ Volume,
28
+ PlaybackRate,
29
+ Pip,
30
+ Fullscreen,
31
+ } from './parts';
32
+
33
+ function aspectRatioStyle(ar: AspectRatioValue): CSSProperties | undefined {
34
+ if (ar === 'fill') return { height: '100%' };
35
+ if (ar === 'auto') return undefined;
36
+ return { aspectRatio: String(ar) };
37
+ }
38
+
39
+ export function VideoPlayer({
40
+ source,
41
+ controls = true,
42
+ aspectRatio = 16 / 9,
43
+ className,
44
+ children,
45
+ ...settings
46
+ }: VideoPlayerProps) {
47
+ const normalized = useMemo(
48
+ () => (typeof source === 'string' ? parseEmbedUrl(source) : source),
49
+ [source],
50
+ );
51
+
52
+ const isIframe = normalized.type === 'iframe';
53
+ // For iframe embeds media-chrome cannot drive the inner player — hide the
54
+ // control bar to avoid a non-functional UI.
55
+ const showControls = controls && !isIframe;
56
+
57
+ return (
58
+ <MediaController
59
+ // Fade controls + scrim after 2.5s of inactivity while playing;
60
+ // they reappear on mousemove / pause / focus (media-chrome built-in).
61
+ autohide="2.5"
62
+ className={cn(
63
+ 'video-player relative block w-full overflow-hidden rounded-lg bg-black',
64
+ className,
65
+ )}
66
+ style={aspectRatioStyle(aspectRatio)}
67
+ >
68
+ <CanvasDispatcher source={normalized} {...settings} />
69
+ {children ??
70
+ (showControls && (
71
+ <ControlsBar>
72
+ <PlayButton />
73
+ <SeekBar />
74
+ <Volume iconOnly />
75
+ <PlaybackRate />
76
+ <Pip />
77
+ <Fullscreen />
78
+ </ControlsBar>
79
+ ))}
80
+ </MediaController>
81
+ );
82
+ }
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Dispatches `source.type` to the right canvas. Single-switch indirection
5
+ * keeps `<VideoPlayer>` simple and isolates engine-specific imports inside
6
+ * the canvas files so tree-shakers can drop unused providers.
7
+ */
8
+
9
+ import type { VideoSource, VideoPlayerSettings } from '../types';
10
+ import { NativeCanvas } from './native-canvas';
11
+ import { YouTubeCanvas } from './youtube-canvas';
12
+ import { VimeoCanvas } from './vimeo-canvas';
13
+ import { HlsCanvas } from './hls-canvas';
14
+ import { IframeCanvas } from './iframe-canvas';
15
+
16
+ export interface CanvasDispatcherProps extends VideoPlayerSettings {
17
+ readonly source: VideoSource;
18
+ }
19
+
20
+ export function CanvasDispatcher({ source, ...settings }: CanvasDispatcherProps) {
21
+ switch (source.type) {
22
+ case 'youtube':
23
+ return <YouTubeCanvas source={source} {...settings} />;
24
+ case 'vimeo':
25
+ return <VimeoCanvas source={source} {...settings} />;
26
+ case 'hls':
27
+ return <HlsCanvas source={source} {...settings} />;
28
+ case 'iframe':
29
+ return <IframeCanvas source={source} />;
30
+ case 'url':
31
+ default:
32
+ return <NativeCanvas source={source} {...settings} />;
33
+ }
34
+ }
@@ -0,0 +1,39 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * `<hls-video slot="media">` — HLS (`.m3u8`) playback via
5
+ * `hls-video-element`, which lazy-loads `hls.js` only on browsers that
6
+ * don't ship native HLS (everything except Safari).
7
+ */
8
+
9
+ import 'hls-video-element';
10
+ import './jsx-augmentation';
11
+ import { MediaPosterImage } from 'media-chrome/react';
12
+ import type { HlsSource, VideoPlayerSettings } from '../types';
13
+
14
+ export interface HlsCanvasProps extends VideoPlayerSettings {
15
+ readonly source: HlsSource;
16
+ }
17
+
18
+ export function HlsCanvas({
19
+ source,
20
+ autoPlay,
21
+ muted,
22
+ loop,
23
+ crossOrigin = 'anonymous',
24
+ }: HlsCanvasProps) {
25
+ return (
26
+ <>
27
+ <hls-video
28
+ slot="media"
29
+ src={source.url}
30
+ crossOrigin={crossOrigin}
31
+ {...(muted && { muted: true })}
32
+ {...(autoPlay && { autoplay: true })}
33
+ {...(loop && { loop: true })}
34
+ playsInline
35
+ />
36
+ {source.poster && <MediaPosterImage slot="poster" src={source.poster} />}
37
+ </>
38
+ );
39
+ }
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Fallback `<iframe>` for arbitrary embed URLs that don't fit a known
5
+ * provider. Rendered *outside* the media-chrome controls (no
6
+ * `slot="media"`) because there is no JS bridge between the iframe
7
+ * contents and `<MediaController>`. Consumers should pass
8
+ * `controls={false}` for iframe sources, or live with a non-functional
9
+ * control bar — we hide it via CSS in `VideoPlayer.tsx`.
10
+ */
11
+
12
+ import type { IframeSource } from '../types';
13
+
14
+ export interface IframeCanvasProps {
15
+ readonly source: IframeSource;
16
+ }
17
+
18
+ const DEFAULT_ALLOW =
19
+ 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share';
20
+
21
+ export function IframeCanvas({ source }: IframeCanvasProps) {
22
+ return (
23
+ <iframe
24
+ slot="media"
25
+ src={source.url}
26
+ title={source.title ?? 'Embedded video'}
27
+ allow={source.allow ?? DEFAULT_ALLOW}
28
+ allowFullScreen
29
+ referrerPolicy="strict-origin-when-cross-origin"
30
+ className="absolute inset-0 h-full w-full border-0"
31
+ />
32
+ );
33
+ }
@@ -0,0 +1,12 @@
1
+ export { CanvasDispatcher } from './canvas-dispatcher';
2
+ export type { CanvasDispatcherProps } from './canvas-dispatcher';
3
+ export { NativeCanvas } from './native-canvas';
4
+ export type { NativeCanvasProps } from './native-canvas';
5
+ export { YouTubeCanvas } from './youtube-canvas';
6
+ export type { YouTubeCanvasProps } from './youtube-canvas';
7
+ export { VimeoCanvas } from './vimeo-canvas';
8
+ export type { VimeoCanvasProps } from './vimeo-canvas';
9
+ export { HlsCanvas } from './hls-canvas';
10
+ export type { HlsCanvasProps } from './hls-canvas';
11
+ export { IframeCanvas } from './iframe-canvas';
12
+ export type { IframeCanvasProps } from './iframe-canvas';