@djangocfg/ui-tools 2.1.417 → 2.1.419

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 (335) hide show
  1. package/dist/audio-player/index.cjs +1 -2
  2. package/dist/audio-player/index.cjs.map +1 -1
  3. package/dist/audio-player/index.d.cts +3 -11
  4. package/dist/audio-player/index.d.ts +3 -11
  5. package/dist/audio-player/index.mjs +1 -2
  6. package/dist/audio-player/index.mjs.map +1 -1
  7. package/dist/file-icon/index.cjs +3 -3
  8. package/dist/file-icon/index.cjs.map +1 -1
  9. package/dist/file-icon/index.mjs +3 -3
  10. package/dist/file-icon/index.mjs.map +1 -1
  11. package/dist/tree/index.cjs +0 -3
  12. package/dist/tree/index.cjs.map +1 -1
  13. package/dist/tree/index.mjs +0 -3
  14. package/dist/tree/index.mjs.map +1 -1
  15. package/package.json +117 -36
  16. package/src/common/FloatingToolbar/actions/CopyAction.tsx +31 -0
  17. package/src/{components → common}/FloatingToolbar/actions/DownloadAction.tsx +15 -10
  18. package/src/common/FloatingToolbar/actions/ExpandAction.tsx +33 -0
  19. package/src/common/FloatingToolbar/actions/FullscreenAction.tsx +38 -0
  20. package/src/{components → common}/FloatingToolbar/index.tsx +39 -0
  21. package/src/lib/http.ts +64 -0
  22. package/src/tools/chat/index.ts +1 -1
  23. package/src/tools/chat/launcher/ChatFAB.tsx +66 -74
  24. package/src/tools/chat/launcher/header/ChatHeaderActionButton.tsx +2 -3
  25. package/src/tools/chat/lazy.tsx +1 -1
  26. package/src/tools/chat/messages/MessageBubble.tsx +1 -1
  27. package/src/tools/chat/messages/blocks/builtin.tsx +1 -1
  28. package/src/tools/chat/messages/blocks/renderers/CodeBlock.tsx +2 -2
  29. package/src/tools/chat/messages/blocks/renderers/JsonBlock.tsx +12 -1
  30. package/src/tools/data/DataGrid/lazy.tsx +1 -1
  31. package/src/tools/data/DataTable/lazy.tsx +1 -1
  32. package/src/tools/data/JsonTree/JsonViewer.tsx +720 -0
  33. package/src/tools/data/JsonTree/README.md +126 -73
  34. package/src/tools/data/JsonTree/index.tsx +3 -95
  35. package/src/tools/data/JsonTree/lazy.tsx +10 -50
  36. package/src/tools/data/JsonTree/types.ts +82 -63
  37. package/src/tools/data/Kanban/lazy.tsx +1 -1
  38. package/src/tools/data/Listbox/lazy.tsx +1 -1
  39. package/src/tools/data/Masonry/lazy.tsx +1 -1
  40. package/src/tools/data/Timeline/lazy.tsx +1 -1
  41. package/src/tools/data/Tree/components/TreeRow.tsx +0 -11
  42. package/src/tools/data/Tree/lazy.tsx +1 -1
  43. package/src/tools/dev/Map/lazy.tsx +1 -1
  44. package/src/tools/dev/Mermaid/Mermaid.client.tsx +2 -2
  45. package/src/tools/dev/Mermaid/lazy.tsx +1 -1
  46. package/src/tools/dev/api/ApiRefTable/ApiRefTable.tsx +65 -0
  47. package/src/tools/dev/api/ApiRefTable/README.md +31 -0
  48. package/src/tools/dev/api/ApiRefTable/components/Row.tsx +96 -0
  49. package/src/tools/dev/api/ApiRefTable/components/TypeDisplay.tsx +44 -0
  50. package/src/tools/dev/api/ApiRefTable/index.ts +6 -0
  51. package/src/tools/dev/api/ApiRefTable/lazy.tsx +21 -0
  52. package/src/tools/dev/api/ApiRefTable/types.ts +82 -0
  53. package/src/tools/dev/api/ApiRefTable/utils.ts +42 -0
  54. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/ApiIntroSection.tsx +1 -1
  55. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/index.tsx +1 -1
  56. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/index.tsx +1 -1
  57. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/ResponseBody.tsx +7 -21
  58. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/RequestPanel.tsx +1 -1
  59. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/PrettyView.tsx +13 -19
  60. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/types.ts +1 -1
  61. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/lazy.tsx +1 -1
  62. package/src/tools/dev/api/RequestViewer/README.md +33 -0
  63. package/src/tools/dev/api/RequestViewer/RequestViewer.tsx +121 -0
  64. package/src/tools/dev/api/RequestViewer/components/BodyTab.tsx +44 -0
  65. package/src/tools/dev/api/RequestViewer/components/EmptyState.tsx +13 -0
  66. package/src/tools/dev/api/RequestViewer/components/HeadersTab.tsx +78 -0
  67. package/src/tools/dev/api/RequestViewer/components/TimingTab.tsx +113 -0
  68. package/src/tools/dev/api/RequestViewer/components/utils.ts +31 -0
  69. package/src/tools/dev/api/RequestViewer/index.ts +16 -0
  70. package/src/tools/dev/api/RequestViewer/lazy.tsx +30 -0
  71. package/src/tools/dev/api/RequestViewer/types.ts +81 -0
  72. package/src/tools/dev/code/DiffViewer/DiffViewer.tsx +144 -0
  73. package/src/tools/dev/code/DiffViewer/README.md +33 -0
  74. package/src/tools/dev/code/DiffViewer/components/CopyButton.tsx +49 -0
  75. package/src/tools/dev/code/DiffViewer/components/DiffLineContent.tsx +48 -0
  76. package/src/tools/dev/code/DiffViewer/components/SplitView.tsx +220 -0
  77. package/src/tools/dev/code/DiffViewer/components/UnifiedView.tsx +154 -0
  78. package/src/tools/dev/code/DiffViewer/hooks/useDiff.ts +47 -0
  79. package/src/tools/dev/code/DiffViewer/hooks/useHighlighter.ts +54 -0
  80. package/src/tools/dev/code/DiffViewer/index.ts +22 -0
  81. package/src/tools/dev/code/DiffViewer/lazy.tsx +22 -0
  82. package/src/tools/dev/code/DiffViewer/types.ts +109 -0
  83. package/src/tools/dev/code/DiffViewer/utils/computeDiff.ts +159 -0
  84. package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/CollapseToggle.tsx +1 -1
  85. package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/MarkdownMessage.tsx +1 -1
  86. package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/components.tsx +2 -2
  87. package/src/tools/dev/{PrettyCode → code/PrettyCode}/PrettyCode.client.tsx +2 -2
  88. package/src/tools/dev/{PrettyCode → code/PrettyCode}/lazy.tsx +1 -1
  89. package/src/tools/dev/ops/EnvTable/EnvTable.tsx +228 -0
  90. package/src/tools/dev/ops/EnvTable/README.md +29 -0
  91. package/src/tools/dev/ops/EnvTable/hooks/useEnvMask.ts +121 -0
  92. package/src/tools/dev/ops/EnvTable/index.ts +12 -0
  93. package/src/tools/dev/ops/EnvTable/lazy.tsx +21 -0
  94. package/src/tools/dev/ops/EnvTable/types.ts +76 -0
  95. package/src/tools/dev/ops/LogViewer/LogViewer.tsx +194 -0
  96. package/src/tools/dev/ops/LogViewer/README.md +30 -0
  97. package/src/tools/dev/ops/LogViewer/components/LogRow.tsx +151 -0
  98. package/src/tools/dev/ops/LogViewer/components/Toolbar.tsx +199 -0
  99. package/src/tools/dev/ops/LogViewer/hooks/useAutoScroll.ts +68 -0
  100. package/src/tools/dev/ops/LogViewer/hooks/useLogFilter.ts +58 -0
  101. package/src/tools/dev/ops/LogViewer/index.ts +18 -0
  102. package/src/tools/dev/ops/LogViewer/lazy.tsx +25 -0
  103. package/src/tools/dev/ops/LogViewer/types.ts +142 -0
  104. package/src/tools/dev/ops/LogViewer/utils/ansi.ts +231 -0
  105. package/src/tools/forms/CodeEditor/components/Editor.tsx +19 -0
  106. package/src/tools/forms/CodeEditor/hooks/useEditorTheme.ts +13 -73
  107. package/src/tools/forms/CodeEditor/lazy.tsx +1 -1
  108. package/src/tools/forms/CodeEditor/types/index.ts +7 -0
  109. package/src/tools/forms/FileUpload/lazy.tsx +1 -1
  110. package/src/tools/forms/JsonEditor/JsonEditor.tsx +115 -0
  111. package/src/tools/forms/JsonEditor/index.ts +1 -0
  112. package/src/tools/forms/JsonEditor/lazy.tsx +24 -0
  113. package/src/tools/forms/JsonForm/index.ts +1 -1
  114. package/src/tools/forms/JsonForm/lazy.tsx +1 -1
  115. package/src/tools/forms/MarkdownEditor/MarkdownEditor.tsx +40 -0
  116. package/src/tools/forms/MarkdownEditor/lazy.tsx +1 -1
  117. package/src/tools/forms/MarkdownEditor/styles.css +174 -21
  118. package/src/tools/forms/NotionEditor/CustomKeymap.ts +48 -0
  119. package/src/tools/forms/NotionEditor/LinkDialog.tsx +133 -0
  120. package/src/tools/forms/NotionEditor/NotionEditor.tsx +304 -0
  121. package/src/tools/forms/NotionEditor/README.md +237 -0
  122. package/src/tools/forms/NotionEditor/SlashExtension.ts +32 -0
  123. package/src/tools/forms/NotionEditor/SlashList.tsx +136 -0
  124. package/src/tools/forms/NotionEditor/TaskItemView.tsx +41 -0
  125. package/src/tools/forms/NotionEditor/createSlashSuggestion.ts +121 -0
  126. package/src/tools/forms/NotionEditor/extensions.ts +105 -0
  127. package/src/tools/forms/NotionEditor/index.ts +1 -0
  128. package/src/tools/forms/NotionEditor/lazy.tsx +44 -0
  129. package/src/tools/forms/NotionEditor/slashItems.ts +159 -0
  130. package/src/tools/forms/NotionEditor/styles.css +478 -0
  131. package/src/tools/forms/NotionEditor/types.ts +28 -0
  132. package/src/tools/index.ts +153 -13
  133. package/src/tools/input/Combobox/lazy.tsx +1 -1
  134. package/src/tools/input/CronScheduler/components/CronPreview.README.md +28 -0
  135. package/src/tools/input/CronScheduler/components/CronPreview.tsx +136 -0
  136. package/src/tools/input/CronScheduler/components/index.ts +3 -0
  137. package/src/tools/input/CronScheduler/index.tsx +5 -1
  138. package/src/tools/input/CronScheduler/lazy.tsx +5 -1
  139. package/src/tools/input/CronScheduler/utils/cron-next-runs.ts +122 -0
  140. package/src/tools/input/CronScheduler/utils/index.ts +1 -0
  141. package/src/tools/input/Scroller/lazy.tsx +1 -1
  142. package/src/tools/input/Sortable/lazy.tsx +1 -1
  143. package/src/tools/input/SpeechRecognition/lazy.tsx +1 -1
  144. package/src/tools/input/SpeechRecognition/widgets/VoiceComposerSlot.tsx +41 -36
  145. package/src/tools/media/AudioPlayer/PlayerShell.tsx +3 -11
  146. package/src/tools/media/AudioPlayer/types.ts +4 -11
  147. package/src/tools/media/ImageViewer/components/ImageToolbar.tsx +58 -47
  148. package/src/tools/media/ImageViewer/components/ImageViewer.tsx +35 -19
  149. package/src/tools/media/ImageViewer/lazy.tsx +1 -1
  150. package/src/tools/media/ImageViewer/types.ts +4 -0
  151. package/src/tools/media/LottiePlayer/lazy.tsx +1 -1
  152. package/src/tools/media/VideoPlayer/VideoPlayer.tsx +47 -1
  153. package/src/tools/media/VideoPlayer/parts/fullscreen.tsx +21 -4
  154. package/src/tools/media/VideoPlayer/parts/pip.tsx +21 -4
  155. package/src/tools/media/VideoPlayer/parts/play-button.tsx +21 -4
  156. package/src/tools/media/VideoPlayer/parts/playback-rate.tsx +19 -3
  157. package/src/tools/media/VideoPlayer/parts/volume.tsx +237 -18
  158. package/src/tools/media/VideoPlayer/styles/video-player.css +87 -7
  159. package/src/tools/media/VideoPlayer/types.ts +4 -0
  160. package/src/tools/overlay/ResponsiveDialog/lazy.tsx +1 -1
  161. package/src/tools/overlay/ScrollSpy/lazy.tsx +1 -1
  162. package/src/tools/overlay/SelectionToolbar/lazy.tsx +1 -1
  163. package/src/tools/overlay/Tour/lazy.tsx +1 -1
  164. package/src/tools/visual/Marquee/lazy.tsx +1 -1
  165. package/src/tools/visual/QRCode/lazy.tsx +1 -1
  166. package/src/tools/visual/charts/ActivityGraph/ActivityGraph.tsx +195 -0
  167. package/src/tools/visual/charts/ActivityGraph/README.md +28 -0
  168. package/src/tools/visual/charts/ActivityGraph/index.ts +8 -0
  169. package/src/tools/visual/charts/ActivityGraph/lazy.tsx +21 -0
  170. package/src/tools/visual/charts/ActivityGraph/types.ts +59 -0
  171. package/src/tools/visual/charts/ActivityGraph/utils.ts +88 -0
  172. package/src/tools/visual/charts/CommitGraph/CommitGraph.tsx +80 -0
  173. package/src/tools/visual/charts/CommitGraph/README.md +28 -0
  174. package/src/tools/visual/charts/CommitGraph/components/CommitDetail.tsx +107 -0
  175. package/src/tools/visual/charts/CommitGraph/components/CommitRow.tsx +122 -0
  176. package/src/tools/visual/charts/CommitGraph/components/Rails.tsx +171 -0
  177. package/src/tools/visual/charts/CommitGraph/hooks/useGraphLayout.ts +169 -0
  178. package/src/tools/visual/charts/CommitGraph/hooks/useLaneColors.ts +45 -0
  179. package/src/tools/visual/charts/CommitGraph/index.ts +14 -0
  180. package/src/tools/visual/charts/CommitGraph/lazy.tsx +25 -0
  181. package/src/tools/visual/charts/CommitGraph/types.ts +85 -0
  182. package/src/tools/visual/charts/CommitGraph/utils.ts +53 -0
  183. package/src/tools/visual/{Gauge → charts/Gauge}/lazy.tsx +1 -1
  184. package/src/tools/visual/charts/Sparkline/README.md +29 -0
  185. package/src/tools/visual/charts/Sparkline/Sparkline.tsx +217 -0
  186. package/src/tools/visual/charts/Sparkline/index.ts +9 -0
  187. package/src/tools/visual/charts/Sparkline/lazy.tsx +26 -0
  188. package/src/tools/visual/charts/Sparkline/types.ts +58 -0
  189. package/src/tools/visual/design/ColorPalette/ColorPalette.tsx +129 -0
  190. package/src/tools/visual/design/ColorPalette/README.md +34 -0
  191. package/src/tools/visual/design/ColorPalette/components/Swatch.tsx +102 -0
  192. package/src/tools/visual/design/ColorPalette/hooks/useCopyToClipboard.ts +41 -0
  193. package/src/tools/visual/design/ColorPalette/index.ts +12 -0
  194. package/src/tools/visual/design/ColorPalette/lazy.tsx +21 -0
  195. package/src/tools/visual/design/ColorPalette/types.ts +63 -0
  196. package/src/tools/visual/design/ColorPalette/utils.ts +83 -0
  197. package/src/tools/visual/{ColorPicker → design/ColorPicker}/lazy.tsx +1 -1
  198. package/src/tools/visual/{FileIcon → design/FileIcon}/treeAdapter.tsx +1 -1
  199. package/src/tools/visual/{Fps → indicators/Fps}/lazy.tsx +1 -1
  200. package/src/tools/visual/{Rating → indicators/Rating}/lazy.tsx +1 -1
  201. package/src/tools/visual/indicators/StatusIndicator/README.md +28 -0
  202. package/src/tools/visual/indicators/StatusIndicator/StatusIndicator.tsx +83 -0
  203. package/src/tools/visual/indicators/StatusIndicator/index.ts +14 -0
  204. package/src/tools/visual/indicators/StatusIndicator/lazy.tsx +21 -0
  205. package/src/tools/visual/indicators/StatusIndicator/types.ts +133 -0
  206. package/src/components/FloatingToolbar/actions/CopyAction.tsx +0 -22
  207. package/src/components/FloatingToolbar/actions/ExpandAction.tsx +0 -25
  208. package/src/components/FloatingToolbar/actions/FullscreenAction.tsx +0 -30
  209. package/src/tools/data/JsonTree/components/JsonContent.tsx +0 -197
  210. package/src/tools/data/JsonTree/hooks/useJsonExpand.ts +0 -50
  211. /package/src/{components → common}/FloatingToolbar/FloatingToolbar.css +0 -0
  212. /package/src/{components → common}/FloatingToolbar/actions/index.ts +0 -0
  213. /package/src/{components → common}/FloatingToolbar/hooks/useElementCorner.ts +0 -0
  214. /package/src/{components → common}/FloatingToolbar/hooks/useScrollIsolation.ts +0 -0
  215. /package/src/{components → common}/index.ts +0 -0
  216. /package/src/{components → common}/lazy-wrapper.tsx +0 -0
  217. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/README.md +0 -0
  218. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/DocsView.tsx +0 -0
  219. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/LanguageTabs.tsx +0 -0
  220. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/useCodeSnippet.ts +0 -0
  221. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +0 -0
  222. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/MethodBadge.tsx +0 -0
  223. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/PathDisplay.tsx +0 -0
  224. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/ParamGroup.tsx +0 -0
  225. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/ParamRow.tsx +0 -0
  226. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/index.tsx +0 -0
  227. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/RequestBody/index.tsx +0 -0
  228. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/ResponseRow.tsx +0 -0
  229. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/StatusTag.tsx +0 -0
  230. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/index.tsx +0 -0
  231. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/FieldRow.tsx +0 -0
  232. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/buildTree.ts +0 -0
  233. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/index.tsx +0 -0
  234. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/types.ts +0 -0
  235. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/SectionHeader.tsx +0 -0
  236. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/defaults.ts +0 -0
  237. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/index.tsx +0 -0
  238. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/context.tsx +0 -0
  239. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/hooks/useSectionHash.ts +0 -0
  240. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/index.tsx +0 -0
  241. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/store/index.ts +0 -0
  242. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/store/selectors.ts +0 -0
  243. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/types.ts +0 -0
  244. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/SchemaCopyMenu.tsx +0 -0
  245. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/BrandHeader.tsx +0 -0
  246. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/CategoryBlock.tsx +0 -0
  247. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/EndpointRow.tsx +0 -0
  248. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SchemaSection.tsx +0 -0
  249. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SearchInput.tsx +0 -0
  250. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SidebarBody.tsx +0 -0
  251. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/Toolbar.tsx +0 -0
  252. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/buildVM.ts +0 -0
  253. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/index.tsx +0 -0
  254. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/types.ts +0 -0
  255. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/useDebouncedValue.ts +0 -0
  256. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/SlideInPlayground.tsx +0 -0
  257. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/TryItSheet.tsx +0 -0
  258. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/anchor.ts +0 -0
  259. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/grouping.ts +0 -0
  260. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/index.tsx +0 -0
  261. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/sidebarLabel.ts +0 -0
  262. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/index.ts +0 -0
  263. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/BodyFormEditor.tsx +0 -0
  264. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/EndpointDraftSync.tsx +0 -0
  265. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/EndpointResetButton.tsx +0 -0
  266. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/PreviewView.tsx +0 -0
  267. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/RawView.tsx +0 -0
  268. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/StatusBar.tsx +0 -0
  269. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/ViewTabs.tsx +0 -0
  270. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/detectContent.ts +0 -0
  271. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/index.tsx +0 -0
  272. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/useResponseView.ts +0 -0
  273. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/SendButton.tsx +0 -0
  274. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ui.tsx +0 -0
  275. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/constants.ts +0 -0
  276. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/context/PlaygroundContext.tsx +0 -0
  277. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/index.ts +0 -0
  278. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useDocsUrlSync.ts +0 -0
  279. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useEndpointDraft.ts +0 -0
  280. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useMobile.ts +0 -0
  281. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useOpenApiSchema.ts +0 -0
  282. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/index.tsx +0 -0
  283. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/types.ts +0 -0
  284. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/apiKeyManager.ts +0 -0
  285. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/codeSamples.ts +0 -0
  286. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/formatters.ts +0 -0
  287. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/index.ts +0 -0
  288. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/operationToHar.ts +0 -0
  289. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/sampler.ts +0 -0
  290. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/schemaExport.ts +0 -0
  291. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/scrollParent.ts +0 -0
  292. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/url.ts +0 -0
  293. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/versionManager.ts +0 -0
  294. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/ActionRow.tsx +0 -0
  295. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/ChatMessageRow.tsx +0 -0
  296. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/CodeBlock.tsx +0 -0
  297. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/README.md +0 -0
  298. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/index.ts +0 -0
  299. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/linkRules.ts +0 -0
  300. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/plainText.ts +0 -0
  301. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/sanitize.ts +0 -0
  302. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/types.ts +0 -0
  303. /package/src/tools/dev/{PrettyCode → code/PrettyCode}/README.md +0 -0
  304. /package/src/tools/dev/{PrettyCode → code/PrettyCode}/index.tsx +0 -0
  305. /package/src/tools/dev/{PrettyCode → code/PrettyCode}/registerPrismLanguages.ts +0 -0
  306. /package/src/tools/visual/{Gauge → charts/Gauge}/Gauge.tsx +0 -0
  307. /package/src/tools/visual/{Gauge → charts/Gauge}/index.ts +0 -0
  308. /package/src/tools/visual/{Gauge → charts/Gauge}/types.ts +0 -0
  309. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/ColorPicker.tsx +0 -0
  310. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/ColorPickerContext.tsx +0 -0
  311. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/ColorPickerStore.tsx +0 -0
  312. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/index.ts +0 -0
  313. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/index.ts +0 -0
  314. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/lib/color-utils.ts +0 -0
  315. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerAlphaSlider.tsx +0 -0
  316. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerArea.tsx +0 -0
  317. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerEyeDropper.tsx +0 -0
  318. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerFormatSelect.tsx +0 -0
  319. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerHueSlider.tsx +0 -0
  320. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerInput.tsx +0 -0
  321. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerSwatch.tsx +0 -0
  322. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/index.ts +0 -0
  323. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/types.ts +0 -0
  324. /package/src/tools/visual/{FileIcon → design/FileIcon}/FileIcon.tsx +0 -0
  325. /package/src/tools/visual/{FileIcon → design/FileIcon}/get-file-icon.ts +0 -0
  326. /package/src/tools/visual/{FileIcon → design/FileIcon}/icons/icon-data.ts +0 -0
  327. /package/src/tools/visual/{FileIcon → design/FileIcon}/index.ts +0 -0
  328. /package/src/tools/visual/{FileIcon → design/FileIcon}/loader.ts +0 -0
  329. /package/src/tools/visual/{FileIcon → design/FileIcon}/specialFolders.ts +0 -0
  330. /package/src/tools/visual/{Fps → indicators/Fps}/Fps.tsx +0 -0
  331. /package/src/tools/visual/{Fps → indicators/Fps}/index.ts +0 -0
  332. /package/src/tools/visual/{Fps → indicators/Fps}/types.ts +0 -0
  333. /package/src/tools/visual/{Rating → indicators/Rating}/Rating.tsx +0 -0
  334. /package/src/tools/visual/{Rating → indicators/Rating}/index.ts +0 -0
  335. /package/src/tools/visual/{Rating → indicators/Rating}/types.ts +0 -0
@@ -1,32 +1,251 @@
1
1
  'use client';
2
2
 
3
- import { MediaMuteButton, MediaVolumeRange } from 'media-chrome/react';
3
+ import {
4
+ forwardRef,
5
+ useCallback,
6
+ useEffect,
7
+ useRef,
8
+ useState,
9
+ type ButtonHTMLAttributes,
10
+ type ReactNode,
11
+ } from 'react';
12
+ import { Volume2, VolumeX, Volume1 } from 'lucide-react';
4
13
  import { cn } from '@djangocfg/ui-core/lib';
5
- import type { ComponentProps } from 'react';
14
+ import {
15
+ Popover,
16
+ PopoverContent,
17
+ PopoverTrigger,
18
+ Slider,
19
+ Tooltip,
20
+ TooltipContent,
21
+ TooltipTrigger,
22
+ } from '@djangocfg/ui-core/components';
6
23
 
7
24
  export interface VolumeProps {
8
25
  readonly className?: string;
26
+ /**
27
+ * Reserved for backwards compatibility. The control is always a single
28
+ * icon button that opens a popover with a vertical slider on hover/focus,
29
+ * so this flag is now a no-op. Kept so existing consumers
30
+ * (`<Volume iconOnly />`) don't break.
31
+ */
9
32
  readonly iconOnly?: boolean;
10
- readonly muteProps?: ComponentProps<typeof MediaMuteButton>;
11
- readonly rangeProps?: ComponentProps<typeof MediaVolumeRange>;
33
+ /** Mute-button tooltip copy. Defaults to `"Volume"`. Pass `false` to opt out. */
34
+ readonly muteLabel?: ReactNode | false;
12
35
  }
13
36
 
14
- export function Volume({ className, iconOnly, muteProps, rangeProps }: VolumeProps) {
37
+ // Hide the popover slider on iOS Safari `video.volume` is read-only
38
+ // there (controlled by hardware buttons), so a JS slider does nothing.
39
+ // The trigger still toggles mute, which iOS *does* honour.
40
+ function isIosSafari(): boolean {
41
+ if (typeof navigator === 'undefined') return false;
42
+ const ua = navigator.userAgent;
43
+ const iOS =
44
+ /iPad|iPhone|iPod/.test(ua) ||
45
+ (navigator.platform === 'MacIntel' &&
46
+ (navigator as { maxTouchPoints?: number }).maxTouchPoints! > 1);
47
+ return iOS;
48
+ }
49
+
50
+ // media-chrome request event names. Dispatched on any descendant of
51
+ // `<media-controller>`; the controller catches them and updates its
52
+ // internal store, which then propagates state back via attributes.
53
+ const MEDIA_VOLUME_REQUEST = 'mediavolumerequest';
54
+ const MEDIA_MUTE_REQUEST = 'mediamuterequest';
55
+ const MEDIA_UNMUTE_REQUEST = 'mediaunmuterequest';
56
+
57
+ /**
58
+ * Click-style trigger that visually matches the rest of the VideoPlayer
59
+ * control bar. Mirrors `.video-player__control` styling from
60
+ * `styles/video-player.css` so it slots in next to `<MediaPlayButton>` &
61
+ * friends without standing out.
62
+ */
63
+ const TriggerButton = forwardRef<
64
+ HTMLButtonElement,
65
+ ButtonHTMLAttributes<HTMLButtonElement>
66
+ >(function TriggerButton({ className, children, ...rest }, ref) {
15
67
  return (
16
- <div className={cn('flex items-center', className)}>
17
- <MediaMuteButton
18
- {...muteProps}
19
- className={cn(
20
- 'media-control-square video-player__control h-8 w-8',
21
- muteProps?.className,
22
- )}
23
- />
24
- {!iconOnly && (
25
- <MediaVolumeRange
26
- {...rangeProps}
27
- className={cn('h-8 w-20', rangeProps?.className)}
28
- />
68
+ <button
69
+ ref={ref}
70
+ type="button"
71
+ className={cn(
72
+ 'video-player__control',
73
+ 'grid h-8 w-8 place-items-center rounded-md',
74
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60',
75
+ className,
29
76
  )}
77
+ {...rest}
78
+ >
79
+ {children}
80
+ </button>
81
+ );
82
+ });
83
+
84
+ /**
85
+ * VideoPlayer volume control.
86
+ *
87
+ * - **Click** the trigger → mute / unmute toggle (dispatches a
88
+ * `mediamuterequest` / `mediaunmuterequest` so media-chrome's store
89
+ * stays the single source of truth — no parallel state machine).
90
+ * - **Hover / keyboard focus** → opens a popover containing a vertical
91
+ * `<Slider>` (0..100). Drag = volume change via `mediavolumerequest`.
92
+ * - Icon reflects level (mute / low / high) and mirrors `media.muted`.
93
+ *
94
+ * State is read directly off the underlying `HTMLMediaElement` (located
95
+ * via the closest `<media-controller>`'s `.media` accessor); we never
96
+ * shadow media-chrome's store.
97
+ */
98
+ export function Volume({ className, muteLabel }: VolumeProps) {
99
+ const triggerRef = useRef<HTMLButtonElement | null>(null);
100
+ const [volume, setVolume] = useState(1);
101
+ const [muted, setMuted] = useState(false);
102
+ const [open, setOpen] = useState(false);
103
+ const [iosSafari] = useState(isIosSafari);
104
+
105
+ // Resolve & track the underlying HTMLMediaElement so we can read live
106
+ // volume / muted. media-chrome fires `mediaelementchange` on the
107
+ // controller whenever the slotted media element swaps (e.g. source
108
+ // change), so we re-bind listeners on each swap.
109
+ useEffect(() => {
110
+ const trigger = triggerRef.current;
111
+ if (!trigger) return;
112
+ const controller = trigger.closest('media-controller') as
113
+ | (HTMLElement & { media?: HTMLMediaElement | null })
114
+ | null;
115
+ if (!controller) return;
116
+
117
+ let media: HTMLMediaElement | null = controller.media ?? null;
118
+ const sync = () => {
119
+ if (!media) return;
120
+ setVolume(media.volume);
121
+ setMuted(media.muted);
122
+ };
123
+ const bind = (el: HTMLMediaElement | null) => {
124
+ if (!el) return;
125
+ el.addEventListener('volumechange', sync);
126
+ sync();
127
+ };
128
+ const unbind = (el: HTMLMediaElement | null) => {
129
+ if (!el) return;
130
+ el.removeEventListener('volumechange', sync);
131
+ };
132
+
133
+ bind(media);
134
+
135
+ const onMediaChange = (e: Event) => {
136
+ unbind(media);
137
+ const detail = (e as CustomEvent).detail as HTMLMediaElement | null;
138
+ media = detail ?? controller.media ?? null;
139
+ bind(media);
140
+ };
141
+ controller.addEventListener('mediaelementchange', onMediaChange);
142
+
143
+ return () => {
144
+ unbind(media);
145
+ controller.removeEventListener('mediaelementchange', onMediaChange);
146
+ };
147
+ }, []);
148
+
149
+ const dispatch = useCallback((name: string, detail?: number) => {
150
+ const trigger = triggerRef.current;
151
+ if (!trigger) return;
152
+ trigger.dispatchEvent(
153
+ new CustomEvent(name, { detail, bubbles: true, composed: true }),
154
+ );
155
+ }, []);
156
+
157
+ const toggleMute = useCallback(() => {
158
+ dispatch(muted ? MEDIA_UNMUTE_REQUEST : MEDIA_MUTE_REQUEST);
159
+ }, [dispatch, muted]);
160
+
161
+ const onSlider = useCallback(
162
+ (value: number) => {
163
+ dispatch(MEDIA_VOLUME_REQUEST, value);
164
+ // If the user nudges the slider above zero, treat that as an
165
+ // implicit unmute — matches what the native HTML5 volume slider does.
166
+ if (value > 0 && muted) dispatch(MEDIA_UNMUTE_REQUEST);
167
+ },
168
+ [dispatch, muted],
169
+ );
170
+
171
+ const effectiveVolume = muted ? 0 : volume;
172
+ const Icon =
173
+ muted || volume === 0 ? VolumeX : volume < 0.5 ? Volume1 : Volume2;
174
+
175
+ const trigger = (
176
+ <TriggerButton
177
+ ref={triggerRef}
178
+ aria-label={muted ? 'Unmute' : 'Mute'}
179
+ aria-pressed={muted}
180
+ onClick={toggleMute}
181
+ >
182
+ <Icon className="h-4 w-4" strokeWidth={1.75} />
183
+ </TriggerButton>
184
+ );
185
+
186
+ // On iOS Safari volume is hardware-only — skip the popover entirely
187
+ // and let the trigger work as a plain mute/unmute button.
188
+ if (iosSafari) {
189
+ if (muteLabel === false) return <div className={cn('flex', className)}>{trigger}</div>;
190
+ return (
191
+ <div className={cn('flex items-center', className)}>
192
+ <Tooltip>
193
+ <TooltipTrigger asChild>{trigger}</TooltipTrigger>
194
+ <TooltipContent side="top">{muteLabel ?? 'Mute / unmute'}</TooltipContent>
195
+ </Tooltip>
196
+ </div>
197
+ );
198
+ }
199
+
200
+ return (
201
+ <div
202
+ className={cn('flex items-center', className)}
203
+ // Hover-driven open: opening on pointerenter of the wrapper (not
204
+ // just the button) gives the user a forgiving hit-area when their
205
+ // cursor drifts between trigger and popover. The popover itself
206
+ // also catches pointerenter via Radix portal — so once it's open,
207
+ // moving into it keeps it open.
208
+ onPointerEnter={() => setOpen(true)}
209
+ onPointerLeave={() => setOpen(false)}
210
+ onFocus={() => setOpen(true)}
211
+ onBlur={(e) => {
212
+ // Don't close when focus moves between the trigger and the
213
+ // popover content (both live in this wrapper / its portal).
214
+ if (!e.currentTarget.contains(e.relatedTarget as Node | null)) {
215
+ setOpen(false);
216
+ }
217
+ }}
218
+ >
219
+ <Popover open={open} onOpenChange={setOpen}>
220
+ <PopoverTrigger asChild>{trigger}</PopoverTrigger>
221
+ <PopoverContent
222
+ side="top"
223
+ align="center"
224
+ sideOffset={8}
225
+ // Keep focus on the trigger so click-to-mute still works while
226
+ // the popover is visible; the slider remains keyboard-reachable
227
+ // via Tab once a user explicitly tabs into it.
228
+ onOpenAutoFocus={(e) => e.preventDefault()}
229
+ onCloseAutoFocus={(e) => e.preventDefault()}
230
+ onPointerEnter={() => setOpen(true)}
231
+ onPointerLeave={() => setOpen(false)}
232
+ className="flex w-12 flex-col items-center gap-2 p-3"
233
+ >
234
+ <span className="w-full text-center text-[10px] tabular-nums text-muted-foreground">
235
+ {Math.round(effectiveVolume * 100)}
236
+ </span>
237
+ <Slider
238
+ orientation="vertical"
239
+ min={0}
240
+ max={1}
241
+ step={0.01}
242
+ value={[effectiveVolume]}
243
+ onValueChange={([v]) => onSlider(v)}
244
+ aria-label="Volume"
245
+ className="h-24"
246
+ />
247
+ </PopoverContent>
248
+ </Popover>
30
249
  </div>
31
250
  );
32
251
  }
@@ -42,11 +42,6 @@ media-controller {
42
42
  --media-range-thumb-opacity: 0;
43
43
  --media-range-thumb-transition: opacity 0.15s ease;
44
44
 
45
- /* Tooltip */
46
- --media-tooltip-background: var(--popover);
47
- --media-tooltip-color: var(--popover-foreground);
48
- --media-tooltip-border-radius: 6px;
49
-
50
45
  /* Font */
51
46
  --media-font-family: inherit;
52
47
  --media-font-size: 12px;
@@ -87,6 +82,29 @@ media-control-bar {
87
82
  background: transparent;
88
83
  }
89
84
 
85
+ /*
86
+ * Suppress media-chrome's built-in tooltips. They are sticky elements
87
+ * slotted under each control button's shadow root (exposed via
88
+ * `part="tooltip"`) and paint a permanent "Play" / "Mute" / "Enter
89
+ * fullscreen" label below the bar. We wrap each control with our own
90
+ * Radix-backed <Tooltip> from @djangocfg/ui-core, which fires only on
91
+ * hover/focus and matches the rest of the platform. Reach into the
92
+ * shadow DOM via ::part(tooltip) — a global `media-tooltip` selector
93
+ * cannot pierce the boundary.
94
+ */
95
+ media-play-button::part(tooltip),
96
+ media-mute-button::part(tooltip),
97
+ media-fullscreen-button::part(tooltip),
98
+ media-pip-button::part(tooltip),
99
+ media-playback-rate-button::part(tooltip),
100
+ media-captions-button::part(tooltip),
101
+ media-airplay-button::part(tooltip),
102
+ media-cast-button::part(tooltip),
103
+ media-seek-forward-button::part(tooltip),
104
+ media-seek-backward-button::part(tooltip) {
105
+ display: none !important;
106
+ }
107
+
90
108
  /*
91
109
  * Auto-hide. media-chrome flags inactivity by setting `userinactive` on
92
110
  * the <media-controller>; it only does so while playback is running, and
@@ -120,17 +138,79 @@ media-time-range {
120
138
  --media-preview-background: var(--popover);
121
139
  --media-preview-border-radius: 6px;
122
140
  --media-text-color: var(--popover-foreground);
141
+
142
+ /* Kill media-chrome's default blue focus rectangle on the range track.
143
+ media-chrome paints a `box-shadow: inset 0 0 0 2px rgb(27 127 204 / .9)`
144
+ via `--media-focus-box-shadow` whenever the inner `<input type=range>`
145
+ matches `:focus-visible`, which wraps the entire seek-bar in a big
146
+ blue rectangle on click/Tab. We opt out of the track-wide ring and
147
+ surface focus on the thumb instead (see :focus-visible below). */
148
+ --media-focus-box-shadow: none;
123
149
  }
124
150
 
125
- /* Reveal the accent thumb only while hovering / scrubbing the rail. */
126
- media-time-range:hover {
151
+ /* Reveal the accent thumb only while hovering / scrubbing the rail, OR
152
+ when the rail is keyboard-focused so a11y keyboard users still see
153
+ where focus lives. */
154
+ media-time-range:hover,
155
+ media-time-range:focus-within {
127
156
  --media-range-thumb-opacity: 1;
128
157
  }
129
158
 
159
+ /* Keyboard focus indicator: a ring around the thumb (not the track).
160
+ Uses the platform `--ring` semantic token so it matches every other
161
+ focusable surface. */
162
+ media-time-range:focus-within {
163
+ --media-range-thumb-box-shadow: 0 0 0 2px var(--ring);
164
+ }
165
+
130
166
  media-time-range::part(preview-box) {
131
167
  display: none;
132
168
  }
133
169
 
170
+ /*
171
+ * YouTube iframe bleed-through suppression.
172
+ *
173
+ * YouTube's IFrame embed ToS requires showing some chrome that cannot be
174
+ * hidden via player params: the title bar at the top, the large pause
175
+ * button on the pause screen, and the bottom branding/share tray on
176
+ * hover/pause. We can't remove them, but we CAN make them non-interactive
177
+ * — so the user never triggers YouTube's hover-state, never gets a
178
+ * "More videos" overlay, and clicks never reach the YT iframe.
179
+ *
180
+ * The <youtube-video> custom element renders an inner <iframe>; disable
181
+ * pointer events on it (and on the wrapper itself, just in case the
182
+ * element later swaps layout). The companion `.vp-yt-click-shield` div
183
+ * inside <media-controller> absorbs clicks and converts them into
184
+ * media-chrome play/pause requests — so click-to-toggle still works.
185
+ *
186
+ * Our control bar lives above the shield and re-enables pointer events
187
+ * for the slot, so the controls stay clickable.
188
+ */
189
+ youtube-video,
190
+ youtube-video iframe {
191
+ pointer-events: none;
192
+ }
193
+
194
+ .vp-yt-click-shield {
195
+ position: absolute;
196
+ inset: 0;
197
+ cursor: pointer;
198
+ background: transparent;
199
+ pointer-events: auto;
200
+ /* Sit above the iframe but below the slotted control bar. */
201
+ z-index: 1;
202
+ }
203
+
204
+ /* media-chrome slots its control surfaces directly under
205
+ <media-controller>; raise them above the click-shield and restore
206
+ pointer events so buttons remain interactive. */
207
+ media-controller > media-control-bar,
208
+ media-controller > [slot='centered-chrome'],
209
+ media-controller > [slot='top-chrome'] {
210
+ z-index: 2;
211
+ pointer-events: auto;
212
+ }
213
+
134
214
  media-time-range::part(current-box) {
135
215
  padding: 2px 6px;
136
216
  border-radius: 6px;
@@ -79,4 +79,8 @@ export interface VideoPlayerProps extends VideoPlayerSettings {
79
79
  readonly className?: string;
80
80
  /** Custom children replace the default control bar entirely. */
81
81
  readonly children?: ReactNode;
82
+ /** Focus the player container on mount so media-chrome keyboard shortcuts
83
+ * (space=play/pause, f=fullscreen, arrows=seek/volume) are active
84
+ * immediately. Pair with `key={src}` upstream for per-source focus reset. */
85
+ readonly autoFocus?: boolean;
82
86
  }
@@ -10,7 +10,7 @@
10
10
  * import { LazyResponsiveDialog, LazyResponsiveDialogContent } from '@djangocfg/ui-tools/responsive-dialog';
11
11
  */
12
12
 
13
- import { createLazyComponent, LoadingFallback } from '../../../components/lazy-wrapper';
13
+ import { createLazyComponent, LoadingFallback } from '../../../common/lazy-wrapper';
14
14
  import type {
15
15
  ResponsiveDialogProps,
16
16
  ResponsiveDialogContentProps,
@@ -4,7 +4,7 @@
4
4
  * Lazy-loaded ScrollSpy Component
5
5
  */
6
6
 
7
- import { createLazyComponent, LoadingFallback } from '../../../components/lazy-wrapper';
7
+ import { createLazyComponent, LoadingFallback } from '../../../common/lazy-wrapper';
8
8
  import type {
9
9
  ScrollSpyProps,
10
10
  ScrollSpyNavProps,
@@ -4,7 +4,7 @@
4
4
  * Lazy-loaded SelectionToolbar Component
5
5
  */
6
6
 
7
- import { createLazyComponent, LoadingFallback } from '../../../components/lazy-wrapper';
7
+ import { createLazyComponent, LoadingFallback } from '../../../common/lazy-wrapper';
8
8
  import type {
9
9
  SelectionToolbarProps,
10
10
  SelectionToolbarContentProps,
@@ -4,7 +4,7 @@
4
4
  * Lazy-loaded Tour Component
5
5
  */
6
6
 
7
- import { createLazyComponent, LoadingFallback } from '../../../components/lazy-wrapper';
7
+ import { createLazyComponent, LoadingFallback } from '../../../common/lazy-wrapper';
8
8
  import type {
9
9
  TourProps,
10
10
  TourPopoverProps,
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { createLazyComponent } from '../../../components/lazy-wrapper';
3
+ import { createLazyComponent } from '../../../common/lazy-wrapper';
4
4
  import type { MarqueeProps } from './types';
5
5
 
6
6
  export const LazyMarquee = createLazyComponent<MarqueeProps>(
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { createLazyComponent } from '../../../components/lazy-wrapper';
3
+ import { createLazyComponent } from '../../../common/lazy-wrapper';
4
4
  import type { QRCodeProps } from './types';
5
5
 
6
6
  export const LazyQRCode = createLazyComponent<QRCodeProps>(
@@ -0,0 +1,195 @@
1
+ // Adapted from jalcoui (MIT) — github.com/jal-co/ui
2
+ //
3
+ // GitHub-style activity heatmap that visualizes daily counts as a
4
+ // color-intensity grid. Auto-sizes blocks to fit the container by default.
5
+ //
6
+ // Port notes:
7
+ // - Cell colors resolve through `useThemeColor('primary')` + `alpha(...)`,
8
+ // not raw `bg-emerald-*` (CONTRACT §2, AUDIT §1). The five emerald
9
+ // stops in the original collapse to five opacity stops on the active
10
+ // theme's `primary` token, which means the heatmap follows the theme
11
+ // preset automatically.
12
+ // - Intensity bucketing routed through `getIntensity` from
13
+ // `@djangocfg/ui-core/lib` (CONTRACT shared util added in Phase 0).
14
+ // - Container sizing routed through `useResizeObserver` from
15
+ // `@djangocfg/ui-core/hooks` (shared RO from Phase 0), replacing the
16
+ // local `ResizeObserver` instance in the source.
17
+ // - Tooltips: the original wrapped every cell in its own Radix
18
+ // `Tooltip.Provider`. Mounting a provider per cell violates CONTRACT
19
+ // §5 ("no nested providers") and explodes to 365 portals on a default
20
+ // 52-week graph. The port uses the native `title` attribute on each
21
+ // cell instead — accessible, no provider dependency, no portal.
22
+
23
+ 'use client';
24
+
25
+ import * as React from 'react';
26
+ import { cn } from '@djangocfg/ui-core/lib';
27
+ import { getIntensity } from '@djangocfg/ui-core/lib';
28
+ import { useResizeObserver } from '@djangocfg/ui-core/hooks';
29
+ import { useThemeColor, alpha } from '@djangocfg/ui-core/styles/palette';
30
+
31
+ import {
32
+ DAY_LABEL_INDICES,
33
+ DAY_LABEL_WIDTH,
34
+ DAY_LABELS,
35
+ DEFAULT_INTENSITY_OPACITY,
36
+ DEFAULT_INTENSITY_THRESHOLDS,
37
+ GAP,
38
+ MONTH_LABEL_HEIGHT,
39
+ type ActivityGraphProps,
40
+ } from './types';
41
+ import { buildWeeks, formatDate, getMonthLabels } from './utils';
42
+
43
+ export function ActivityGraph({
44
+ data,
45
+ intensityOpacity = DEFAULT_INTENSITY_OPACITY,
46
+ blockSize: fixedBlockSize,
47
+ blockRadius = 2,
48
+ weeks: weekCount = 52,
49
+ className,
50
+ ...props
51
+ }: ActivityGraphProps) {
52
+ const containerRef = React.useRef<HTMLDivElement>(null);
53
+ const { width: containerWidth } = useResizeObserver(containerRef);
54
+
55
+ const isAutoFit = fixedBlockSize == null;
56
+
57
+ // Auto-fit cell size based on container width. Falls back to a small
58
+ // default until the first measurement lands (consistent with original).
59
+ const autoSize = React.useMemo(() => {
60
+ if (!isAutoFit || containerWidth <= 0) return null;
61
+ const available = containerWidth - DAY_LABEL_WIDTH;
62
+ const size = (available - GAP * (weekCount - 1)) / weekCount;
63
+ return Math.max(4, Math.floor(size));
64
+ }, [isAutoFit, containerWidth, weekCount]);
65
+
66
+ const blockSize = fixedBlockSize ?? autoSize ?? 10;
67
+ const showGraph = !isAutoFit || autoSize != null;
68
+
69
+ // Resolve the theme color once; map each intensity bucket to an
70
+ // opacity stop applied to that color (see CONTRACT §3 — never raw
71
+ // Tailwind class for parametric colors).
72
+ const primary = useThemeColor('primary');
73
+ const intensityColors = React.useMemo(
74
+ () => intensityOpacity.map((op) => alpha(primary, op)),
75
+ [primary, intensityOpacity],
76
+ );
77
+
78
+ const weeks = React.useMemo(() => buildWeeks(data, weekCount), [data, weekCount]);
79
+ const maxCount = React.useMemo(
80
+ () => data.reduce((m, d) => (d.count > m ? d.count : m), 0),
81
+ [data],
82
+ );
83
+ const monthLabels = React.useMemo(
84
+ () => getMonthLabels(weeks, blockSize),
85
+ [weeks, blockSize],
86
+ );
87
+
88
+ const gridWidth = weeks.length * (blockSize + GAP) - GAP;
89
+ const gridHeight = 7 * (blockSize + GAP) - GAP;
90
+ const totalWidth = DAY_LABEL_WIDTH + gridWidth;
91
+
92
+ return (
93
+ <div
94
+ ref={containerRef}
95
+ className={cn(isAutoFit ? 'w-full' : 'overflow-x-auto', className)}
96
+ role="img"
97
+ aria-label="Activity graph"
98
+ data-slot="activity-graph"
99
+ {...props}
100
+ >
101
+ {showGraph && (
102
+ <div
103
+ className="flex flex-col gap-2"
104
+ style={{ minWidth: isAutoFit ? undefined : totalWidth }}
105
+ >
106
+ <div
107
+ className="relative"
108
+ style={{
109
+ width: isAutoFit ? '100%' : totalWidth,
110
+ height: MONTH_LABEL_HEIGHT + gridHeight,
111
+ }}
112
+ >
113
+ {monthLabels.map((m, i) => (
114
+ <span
115
+ key={`${m.label}-${i}`}
116
+ className="absolute text-[10px] leading-none text-muted-foreground"
117
+ style={{ left: DAY_LABEL_WIDTH + m.offset, top: 0 }}
118
+ >
119
+ {m.label}
120
+ </span>
121
+ ))}
122
+
123
+ {DAY_LABELS.map((label, i) => (
124
+ <span
125
+ key={label}
126
+ className="absolute text-[10px] leading-none text-muted-foreground"
127
+ style={{
128
+ left: 0,
129
+ top:
130
+ MONTH_LABEL_HEIGHT +
131
+ DAY_LABEL_INDICES[i] * (blockSize + GAP) +
132
+ blockSize / 2 -
133
+ 4,
134
+ }}
135
+ >
136
+ {label}
137
+ </span>
138
+ ))}
139
+
140
+ <div
141
+ className="absolute"
142
+ style={{
143
+ left: DAY_LABEL_WIDTH,
144
+ top: MONTH_LABEL_HEIGHT,
145
+ display: 'flex',
146
+ gap: GAP,
147
+ }}
148
+ >
149
+ {weeks.map((week, wi) => (
150
+ <div key={wi} style={{ display: 'flex', flexDirection: 'column', gap: GAP }}>
151
+ {week.map((day, di) => {
152
+ const ratio = maxCount > 0 ? day.count / maxCount : 0;
153
+ const intensity = getIntensity(ratio, [...DEFAULT_INTENSITY_THRESHOLDS]);
154
+ const title = `${day.count} contribution${day.count === 1 ? '' : 's'} — ${formatDate(day.date)}`;
155
+ return (
156
+ <div
157
+ key={di}
158
+ data-slot="activity-graph-cell"
159
+ data-intensity={intensity}
160
+ className="transition-colors"
161
+ title={title}
162
+ style={{
163
+ width: blockSize,
164
+ height: blockSize,
165
+ borderRadius: blockRadius,
166
+ backgroundColor: intensityColors[intensity],
167
+ }}
168
+ />
169
+ );
170
+ })}
171
+ </div>
172
+ ))}
173
+ </div>
174
+ </div>
175
+
176
+ <div className="flex items-center gap-1.5 self-end text-[10px] text-muted-foreground">
177
+ <span>Less</span>
178
+ {intensityColors.map((color, i) => (
179
+ <div
180
+ key={i}
181
+ style={{
182
+ width: blockSize,
183
+ height: blockSize,
184
+ borderRadius: blockRadius,
185
+ backgroundColor: color,
186
+ }}
187
+ />
188
+ ))}
189
+ <span>More</span>
190
+ </div>
191
+ </div>
192
+ )}
193
+ </div>
194
+ );
195
+ }
@@ -0,0 +1,28 @@
1
+ # ActivityGraph
2
+
3
+ GitHub-style contribution heatmap. Maps daily counts to opacity stops of the active theme `primary` color via `useThemeColor` + `useResizeObserver`.
4
+
5
+ ```tsx
6
+ import { ActivityGraph } from '@djangocfg/ui-tools/activity-graph';
7
+
8
+ <ActivityGraph
9
+ data={[{ date: '2025-01-12', count: 4 }, { date: '2025-01-13', count: 0 }]}
10
+ weeks={52}
11
+ />
12
+ ```
13
+
14
+ ## Props
15
+
16
+ | Prop | Type | Default | Description |
17
+ |---|---|---|---|
18
+ | `data` | `ActivityEntry[]` | — | Daily entries `{ date, count }`. Duplicate dates are summed. |
19
+ | `weeks` | `number` | `52` | Trailing week count. |
20
+ | `blockSize` | `number` | auto | Cell size in px. When omitted, auto-fits the container. |
21
+ | `blockRadius` | `number` | `2` | Cell border-radius in px. |
22
+ | `intensityOpacity` | `[number,number,number,number,number]` | `[0.08, 0.18, 0.35, 0.6, 0.95]` | Opacity stops applied to the theme `primary` color (intensity 0–4). |
23
+
24
+ Storybook: `apps/storybook/stories/ui-tools/visual/ActivityGraph.stories.tsx`
25
+
26
+ ---
27
+
28
+ Adapted from jalcoui (MIT).