@djangocfg/ui-tools 2.1.418 → 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 (304) hide show
  1. package/dist/file-icon/index.cjs +3 -3
  2. package/dist/file-icon/index.cjs.map +1 -1
  3. package/dist/file-icon/index.mjs +3 -3
  4. package/dist/file-icon/index.mjs.map +1 -1
  5. package/package.json +93 -28
  6. package/src/common/FloatingToolbar/actions/CopyAction.tsx +31 -0
  7. package/src/{components → common}/FloatingToolbar/actions/DownloadAction.tsx +15 -10
  8. package/src/common/FloatingToolbar/actions/ExpandAction.tsx +33 -0
  9. package/src/common/FloatingToolbar/actions/FullscreenAction.tsx +38 -0
  10. package/src/{components → common}/FloatingToolbar/index.tsx +39 -0
  11. package/src/lib/http.ts +64 -0
  12. package/src/tools/chat/index.ts +1 -1
  13. package/src/tools/chat/launcher/ChatFAB.tsx +66 -74
  14. package/src/tools/chat/launcher/header/ChatHeaderActionButton.tsx +2 -3
  15. package/src/tools/chat/lazy.tsx +1 -1
  16. package/src/tools/chat/messages/MessageBubble.tsx +1 -1
  17. package/src/tools/chat/messages/blocks/builtin.tsx +1 -1
  18. package/src/tools/chat/messages/blocks/renderers/CodeBlock.tsx +2 -2
  19. package/src/tools/chat/messages/blocks/renderers/JsonBlock.tsx +12 -1
  20. package/src/tools/data/DataGrid/lazy.tsx +1 -1
  21. package/src/tools/data/DataTable/lazy.tsx +1 -1
  22. package/src/tools/data/JsonTree/JsonViewer.tsx +720 -0
  23. package/src/tools/data/JsonTree/README.md +126 -73
  24. package/src/tools/data/JsonTree/index.tsx +3 -95
  25. package/src/tools/data/JsonTree/lazy.tsx +10 -50
  26. package/src/tools/data/JsonTree/types.ts +82 -63
  27. package/src/tools/data/Kanban/lazy.tsx +1 -1
  28. package/src/tools/data/Listbox/lazy.tsx +1 -1
  29. package/src/tools/data/Masonry/lazy.tsx +1 -1
  30. package/src/tools/data/Timeline/lazy.tsx +1 -1
  31. package/src/tools/data/Tree/lazy.tsx +1 -1
  32. package/src/tools/dev/Map/lazy.tsx +1 -1
  33. package/src/tools/dev/Mermaid/Mermaid.client.tsx +2 -2
  34. package/src/tools/dev/Mermaid/lazy.tsx +1 -1
  35. package/src/tools/dev/api/ApiRefTable/ApiRefTable.tsx +65 -0
  36. package/src/tools/dev/api/ApiRefTable/README.md +31 -0
  37. package/src/tools/dev/api/ApiRefTable/components/Row.tsx +96 -0
  38. package/src/tools/dev/api/ApiRefTable/components/TypeDisplay.tsx +44 -0
  39. package/src/tools/dev/api/ApiRefTable/index.ts +6 -0
  40. package/src/tools/dev/api/ApiRefTable/lazy.tsx +21 -0
  41. package/src/tools/dev/api/ApiRefTable/types.ts +82 -0
  42. package/src/tools/dev/api/ApiRefTable/utils.ts +42 -0
  43. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/ApiIntroSection.tsx +1 -1
  44. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/index.tsx +1 -1
  45. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/index.tsx +1 -1
  46. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/ResponseBody.tsx +7 -21
  47. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/RequestPanel.tsx +1 -1
  48. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/PrettyView.tsx +13 -19
  49. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/types.ts +1 -1
  50. package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/lazy.tsx +1 -1
  51. package/src/tools/dev/api/RequestViewer/README.md +33 -0
  52. package/src/tools/dev/api/RequestViewer/RequestViewer.tsx +121 -0
  53. package/src/tools/dev/api/RequestViewer/components/BodyTab.tsx +44 -0
  54. package/src/tools/dev/api/RequestViewer/components/EmptyState.tsx +13 -0
  55. package/src/tools/dev/api/RequestViewer/components/HeadersTab.tsx +78 -0
  56. package/src/tools/dev/api/RequestViewer/components/TimingTab.tsx +113 -0
  57. package/src/tools/dev/api/RequestViewer/components/utils.ts +31 -0
  58. package/src/tools/dev/api/RequestViewer/index.ts +16 -0
  59. package/src/tools/dev/api/RequestViewer/lazy.tsx +30 -0
  60. package/src/tools/dev/api/RequestViewer/types.ts +81 -0
  61. package/src/tools/dev/code/DiffViewer/DiffViewer.tsx +144 -0
  62. package/src/tools/dev/code/DiffViewer/README.md +33 -0
  63. package/src/tools/dev/code/DiffViewer/components/CopyButton.tsx +49 -0
  64. package/src/tools/dev/code/DiffViewer/components/DiffLineContent.tsx +48 -0
  65. package/src/tools/dev/code/DiffViewer/components/SplitView.tsx +220 -0
  66. package/src/tools/dev/code/DiffViewer/components/UnifiedView.tsx +154 -0
  67. package/src/tools/dev/code/DiffViewer/hooks/useDiff.ts +47 -0
  68. package/src/tools/dev/code/DiffViewer/hooks/useHighlighter.ts +54 -0
  69. package/src/tools/dev/code/DiffViewer/index.ts +22 -0
  70. package/src/tools/dev/code/DiffViewer/lazy.tsx +22 -0
  71. package/src/tools/dev/code/DiffViewer/types.ts +109 -0
  72. package/src/tools/dev/code/DiffViewer/utils/computeDiff.ts +159 -0
  73. package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/CollapseToggle.tsx +1 -1
  74. package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/MarkdownMessage.tsx +1 -1
  75. package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/components.tsx +2 -2
  76. package/src/tools/dev/{PrettyCode → code/PrettyCode}/PrettyCode.client.tsx +2 -2
  77. package/src/tools/dev/{PrettyCode → code/PrettyCode}/lazy.tsx +1 -1
  78. package/src/tools/dev/ops/EnvTable/EnvTable.tsx +228 -0
  79. package/src/tools/dev/ops/EnvTable/README.md +29 -0
  80. package/src/tools/dev/ops/EnvTable/hooks/useEnvMask.ts +121 -0
  81. package/src/tools/dev/ops/EnvTable/index.ts +12 -0
  82. package/src/tools/dev/ops/EnvTable/lazy.tsx +21 -0
  83. package/src/tools/dev/ops/EnvTable/types.ts +76 -0
  84. package/src/tools/dev/ops/LogViewer/LogViewer.tsx +194 -0
  85. package/src/tools/dev/ops/LogViewer/README.md +30 -0
  86. package/src/tools/dev/ops/LogViewer/components/LogRow.tsx +151 -0
  87. package/src/tools/dev/ops/LogViewer/components/Toolbar.tsx +199 -0
  88. package/src/tools/dev/ops/LogViewer/hooks/useAutoScroll.ts +68 -0
  89. package/src/tools/dev/ops/LogViewer/hooks/useLogFilter.ts +58 -0
  90. package/src/tools/dev/ops/LogViewer/index.ts +18 -0
  91. package/src/tools/dev/ops/LogViewer/lazy.tsx +25 -0
  92. package/src/tools/dev/ops/LogViewer/types.ts +142 -0
  93. package/src/tools/dev/ops/LogViewer/utils/ansi.ts +231 -0
  94. package/src/tools/forms/CodeEditor/hooks/useEditorTheme.ts +13 -73
  95. package/src/tools/forms/CodeEditor/lazy.tsx +1 -1
  96. package/src/tools/forms/FileUpload/lazy.tsx +1 -1
  97. package/src/tools/forms/JsonEditor/JsonEditor.tsx +115 -0
  98. package/src/tools/forms/JsonEditor/index.ts +1 -0
  99. package/src/tools/forms/JsonEditor/lazy.tsx +24 -0
  100. package/src/tools/forms/JsonForm/index.ts +1 -1
  101. package/src/tools/forms/JsonForm/lazy.tsx +1 -1
  102. package/src/tools/forms/MarkdownEditor/lazy.tsx +1 -1
  103. package/src/tools/forms/NotionEditor/README.md +237 -0
  104. package/src/tools/forms/NotionEditor/lazy.tsx +1 -1
  105. package/src/tools/index.ts +153 -13
  106. package/src/tools/input/Combobox/lazy.tsx +1 -1
  107. package/src/tools/input/CronScheduler/components/CronPreview.README.md +28 -0
  108. package/src/tools/input/CronScheduler/components/CronPreview.tsx +136 -0
  109. package/src/tools/input/CronScheduler/components/index.ts +3 -0
  110. package/src/tools/input/CronScheduler/index.tsx +5 -1
  111. package/src/tools/input/CronScheduler/lazy.tsx +5 -1
  112. package/src/tools/input/CronScheduler/utils/cron-next-runs.ts +122 -0
  113. package/src/tools/input/CronScheduler/utils/index.ts +1 -0
  114. package/src/tools/input/Scroller/lazy.tsx +1 -1
  115. package/src/tools/input/Sortable/lazy.tsx +1 -1
  116. package/src/tools/input/SpeechRecognition/lazy.tsx +1 -1
  117. package/src/tools/input/SpeechRecognition/widgets/VoiceComposerSlot.tsx +41 -36
  118. package/src/tools/media/ImageViewer/components/ImageToolbar.tsx +58 -47
  119. package/src/tools/media/ImageViewer/components/ImageViewer.tsx +27 -19
  120. package/src/tools/media/ImageViewer/lazy.tsx +1 -1
  121. package/src/tools/media/LottiePlayer/lazy.tsx +1 -1
  122. package/src/tools/media/VideoPlayer/VideoPlayer.tsx +28 -1
  123. package/src/tools/media/VideoPlayer/parts/fullscreen.tsx +21 -4
  124. package/src/tools/media/VideoPlayer/parts/pip.tsx +21 -4
  125. package/src/tools/media/VideoPlayer/parts/play-button.tsx +21 -4
  126. package/src/tools/media/VideoPlayer/parts/playback-rate.tsx +19 -3
  127. package/src/tools/media/VideoPlayer/parts/volume.tsx +237 -18
  128. package/src/tools/media/VideoPlayer/styles/video-player.css +87 -7
  129. package/src/tools/overlay/ResponsiveDialog/lazy.tsx +1 -1
  130. package/src/tools/overlay/ScrollSpy/lazy.tsx +1 -1
  131. package/src/tools/overlay/SelectionToolbar/lazy.tsx +1 -1
  132. package/src/tools/overlay/Tour/lazy.tsx +1 -1
  133. package/src/tools/visual/Marquee/lazy.tsx +1 -1
  134. package/src/tools/visual/QRCode/lazy.tsx +1 -1
  135. package/src/tools/visual/charts/ActivityGraph/ActivityGraph.tsx +195 -0
  136. package/src/tools/visual/charts/ActivityGraph/README.md +28 -0
  137. package/src/tools/visual/charts/ActivityGraph/index.ts +8 -0
  138. package/src/tools/visual/charts/ActivityGraph/lazy.tsx +21 -0
  139. package/src/tools/visual/charts/ActivityGraph/types.ts +59 -0
  140. package/src/tools/visual/charts/ActivityGraph/utils.ts +88 -0
  141. package/src/tools/visual/charts/CommitGraph/CommitGraph.tsx +80 -0
  142. package/src/tools/visual/charts/CommitGraph/README.md +28 -0
  143. package/src/tools/visual/charts/CommitGraph/components/CommitDetail.tsx +107 -0
  144. package/src/tools/visual/charts/CommitGraph/components/CommitRow.tsx +122 -0
  145. package/src/tools/visual/charts/CommitGraph/components/Rails.tsx +171 -0
  146. package/src/tools/visual/charts/CommitGraph/hooks/useGraphLayout.ts +169 -0
  147. package/src/tools/visual/charts/CommitGraph/hooks/useLaneColors.ts +45 -0
  148. package/src/tools/visual/charts/CommitGraph/index.ts +14 -0
  149. package/src/tools/visual/charts/CommitGraph/lazy.tsx +25 -0
  150. package/src/tools/visual/charts/CommitGraph/types.ts +85 -0
  151. package/src/tools/visual/charts/CommitGraph/utils.ts +53 -0
  152. package/src/tools/visual/{Gauge → charts/Gauge}/lazy.tsx +1 -1
  153. package/src/tools/visual/charts/Sparkline/README.md +29 -0
  154. package/src/tools/visual/charts/Sparkline/Sparkline.tsx +217 -0
  155. package/src/tools/visual/charts/Sparkline/index.ts +9 -0
  156. package/src/tools/visual/charts/Sparkline/lazy.tsx +26 -0
  157. package/src/tools/visual/charts/Sparkline/types.ts +58 -0
  158. package/src/tools/visual/design/ColorPalette/ColorPalette.tsx +129 -0
  159. package/src/tools/visual/design/ColorPalette/README.md +34 -0
  160. package/src/tools/visual/design/ColorPalette/components/Swatch.tsx +102 -0
  161. package/src/tools/visual/design/ColorPalette/hooks/useCopyToClipboard.ts +41 -0
  162. package/src/tools/visual/design/ColorPalette/index.ts +12 -0
  163. package/src/tools/visual/design/ColorPalette/lazy.tsx +21 -0
  164. package/src/tools/visual/design/ColorPalette/types.ts +63 -0
  165. package/src/tools/visual/design/ColorPalette/utils.ts +83 -0
  166. package/src/tools/visual/{ColorPicker → design/ColorPicker}/lazy.tsx +1 -1
  167. package/src/tools/visual/{FileIcon → design/FileIcon}/treeAdapter.tsx +1 -1
  168. package/src/tools/visual/{Fps → indicators/Fps}/lazy.tsx +1 -1
  169. package/src/tools/visual/{Rating → indicators/Rating}/lazy.tsx +1 -1
  170. package/src/tools/visual/indicators/StatusIndicator/README.md +28 -0
  171. package/src/tools/visual/indicators/StatusIndicator/StatusIndicator.tsx +83 -0
  172. package/src/tools/visual/indicators/StatusIndicator/index.ts +14 -0
  173. package/src/tools/visual/indicators/StatusIndicator/lazy.tsx +21 -0
  174. package/src/tools/visual/indicators/StatusIndicator/types.ts +133 -0
  175. package/src/components/FloatingToolbar/actions/CopyAction.tsx +0 -22
  176. package/src/components/FloatingToolbar/actions/ExpandAction.tsx +0 -25
  177. package/src/components/FloatingToolbar/actions/FullscreenAction.tsx +0 -30
  178. package/src/tools/data/JsonTree/components/JsonContent.tsx +0 -197
  179. package/src/tools/data/JsonTree/hooks/useJsonExpand.ts +0 -50
  180. /package/src/{components → common}/FloatingToolbar/FloatingToolbar.css +0 -0
  181. /package/src/{components → common}/FloatingToolbar/actions/index.ts +0 -0
  182. /package/src/{components → common}/FloatingToolbar/hooks/useElementCorner.ts +0 -0
  183. /package/src/{components → common}/FloatingToolbar/hooks/useScrollIsolation.ts +0 -0
  184. /package/src/{components → common}/index.ts +0 -0
  185. /package/src/{components → common}/lazy-wrapper.tsx +0 -0
  186. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/README.md +0 -0
  187. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/DocsView.tsx +0 -0
  188. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/LanguageTabs.tsx +0 -0
  189. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/CodeSamples/useCodeSnippet.ts +0 -0
  190. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +0 -0
  191. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/MethodBadge.tsx +0 -0
  192. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Header/PathDisplay.tsx +0 -0
  193. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/ParamGroup.tsx +0 -0
  194. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/ParamRow.tsx +0 -0
  195. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Parameters/index.tsx +0 -0
  196. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/RequestBody/index.tsx +0 -0
  197. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/ResponseRow.tsx +0 -0
  198. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/StatusTag.tsx +0 -0
  199. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Responses/index.tsx +0 -0
  200. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/FieldRow.tsx +0 -0
  201. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/buildTree.ts +0 -0
  202. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/index.tsx +0 -0
  203. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/SchemaFields/types.ts +0 -0
  204. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/SectionHeader.tsx +0 -0
  205. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/defaults.ts +0 -0
  206. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/Section/index.tsx +0 -0
  207. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/context.tsx +0 -0
  208. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/hooks/useSectionHash.ts +0 -0
  209. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/index.tsx +0 -0
  210. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/store/index.ts +0 -0
  211. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/store/selectors.ts +0 -0
  212. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/EndpointDoc/types.ts +0 -0
  213. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/SchemaCopyMenu.tsx +0 -0
  214. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/BrandHeader.tsx +0 -0
  215. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/CategoryBlock.tsx +0 -0
  216. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/EndpointRow.tsx +0 -0
  217. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SchemaSection.tsx +0 -0
  218. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SearchInput.tsx +0 -0
  219. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/SidebarBody.tsx +0 -0
  220. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/Toolbar.tsx +0 -0
  221. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/buildVM.ts +0 -0
  222. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/index.tsx +0 -0
  223. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/types.ts +0 -0
  224. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/Sidebar/useDebouncedValue.ts +0 -0
  225. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/SlideInPlayground.tsx +0 -0
  226. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/TryItSheet.tsx +0 -0
  227. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/anchor.ts +0 -0
  228. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/grouping.ts +0 -0
  229. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/index.tsx +0 -0
  230. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/DocsLayout/sidebarLabel.ts +0 -0
  231. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/index.ts +0 -0
  232. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/BodyFormEditor.tsx +0 -0
  233. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/EndpointDraftSync.tsx +0 -0
  234. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/EndpointResetButton.tsx +0 -0
  235. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/PreviewView.tsx +0 -0
  236. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/RawView.tsx +0 -0
  237. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/StatusBar.tsx +0 -0
  238. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/ViewTabs.tsx +0 -0
  239. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/detectContent.ts +0 -0
  240. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/index.tsx +0 -0
  241. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ResponsePanel/useResponseView.ts +0 -0
  242. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/SendButton.tsx +0 -0
  243. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/components/shared/ui.tsx +0 -0
  244. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/constants.ts +0 -0
  245. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/context/PlaygroundContext.tsx +0 -0
  246. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/index.ts +0 -0
  247. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useDocsUrlSync.ts +0 -0
  248. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useEndpointDraft.ts +0 -0
  249. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useMobile.ts +0 -0
  250. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/hooks/useOpenApiSchema.ts +0 -0
  251. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/index.tsx +0 -0
  252. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/types.ts +0 -0
  253. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/apiKeyManager.ts +0 -0
  254. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/codeSamples.ts +0 -0
  255. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/formatters.ts +0 -0
  256. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/index.ts +0 -0
  257. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/operationToHar.ts +0 -0
  258. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/sampler.ts +0 -0
  259. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/schemaExport.ts +0 -0
  260. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/scrollParent.ts +0 -0
  261. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/url.ts +0 -0
  262. /package/src/tools/dev/{OpenapiViewer → api/OpenapiViewer}/utils/versionManager.ts +0 -0
  263. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/ActionRow.tsx +0 -0
  264. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/ChatMessageRow.tsx +0 -0
  265. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/CodeBlock.tsx +0 -0
  266. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/README.md +0 -0
  267. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/index.ts +0 -0
  268. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/linkRules.ts +0 -0
  269. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/plainText.ts +0 -0
  270. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/sanitize.ts +0 -0
  271. /package/src/tools/dev/{MarkdownMessage → code/MarkdownMessage}/types.ts +0 -0
  272. /package/src/tools/dev/{PrettyCode → code/PrettyCode}/README.md +0 -0
  273. /package/src/tools/dev/{PrettyCode → code/PrettyCode}/index.tsx +0 -0
  274. /package/src/tools/dev/{PrettyCode → code/PrettyCode}/registerPrismLanguages.ts +0 -0
  275. /package/src/tools/visual/{Gauge → charts/Gauge}/Gauge.tsx +0 -0
  276. /package/src/tools/visual/{Gauge → charts/Gauge}/index.ts +0 -0
  277. /package/src/tools/visual/{Gauge → charts/Gauge}/types.ts +0 -0
  278. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/ColorPicker.tsx +0 -0
  279. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/ColorPickerContext.tsx +0 -0
  280. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/ColorPickerStore.tsx +0 -0
  281. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/context/index.ts +0 -0
  282. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/index.ts +0 -0
  283. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/lib/color-utils.ts +0 -0
  284. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerAlphaSlider.tsx +0 -0
  285. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerArea.tsx +0 -0
  286. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerEyeDropper.tsx +0 -0
  287. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerFormatSelect.tsx +0 -0
  288. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerHueSlider.tsx +0 -0
  289. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerInput.tsx +0 -0
  290. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/ColorPickerSwatch.tsx +0 -0
  291. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/parts/index.ts +0 -0
  292. /package/src/tools/visual/{ColorPicker → design/ColorPicker}/types.ts +0 -0
  293. /package/src/tools/visual/{FileIcon → design/FileIcon}/FileIcon.tsx +0 -0
  294. /package/src/tools/visual/{FileIcon → design/FileIcon}/get-file-icon.ts +0 -0
  295. /package/src/tools/visual/{FileIcon → design/FileIcon}/icons/icon-data.ts +0 -0
  296. /package/src/tools/visual/{FileIcon → design/FileIcon}/index.ts +0 -0
  297. /package/src/tools/visual/{FileIcon → design/FileIcon}/loader.ts +0 -0
  298. /package/src/tools/visual/{FileIcon → design/FileIcon}/specialFolders.ts +0 -0
  299. /package/src/tools/visual/{Fps → indicators/Fps}/Fps.tsx +0 -0
  300. /package/src/tools/visual/{Fps → indicators/Fps}/index.ts +0 -0
  301. /package/src/tools/visual/{Fps → indicators/Fps}/types.ts +0 -0
  302. /package/src/tools/visual/{Rating → indicators/Rating}/Rating.tsx +0 -0
  303. /package/src/tools/visual/{Rating → indicators/Rating}/index.ts +0 -0
  304. /package/src/tools/visual/{Rating → indicators/Rating}/types.ts +0 -0
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  import React, { lazy, Suspense } from 'react';
20
- import { LoadingFallback } from '../../../components';
20
+ import { LoadingFallback } from '../../../common';
21
21
  import type { CronSchedulerProps } from './types';
22
22
 
23
23
  // Lazy load the client component
@@ -78,6 +78,8 @@ export {
78
78
  parseCron,
79
79
  isValidCron,
80
80
  humanizeCron,
81
+ getNextRuns,
82
+ formatNextRun,
81
83
  } from './utils';
82
84
 
83
85
  // Re-export components for custom compositions
@@ -88,6 +90,8 @@ export {
88
90
  MonthDayGrid,
89
91
  CustomInput,
90
92
  SchedulePreview,
93
+ CronPreview,
91
94
  } from './components';
95
+ export type { CronPreviewProps } from './components';
92
96
 
93
97
  export default CronScheduler;
@@ -15,7 +15,7 @@
15
15
  * />
16
16
  */
17
17
 
18
- import { createLazyComponent, LoadingFallback } from '../../../components';
18
+ import { createLazyComponent, LoadingFallback } from '../../../common';
19
19
  import type { CronSchedulerProps } from './types';
20
20
 
21
21
  // Re-export types
@@ -28,6 +28,10 @@ export type {
28
28
  CronSchedulerState,
29
29
  } from './types';
30
30
 
31
+ // Re-export the read-only preview (small, no lazy needed — pure render).
32
+ export { CronPreview } from './components/CronPreview';
33
+ export type { CronPreviewProps } from './components/CronPreview';
34
+
31
35
  /**
32
36
  * LazyCronScheduler - Lazy-loaded cron expression builder
33
37
  *
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Cron Next Runs
3
+ *
4
+ * Computes upcoming run times for a standard 5-field cron expression
5
+ * (minute, hour, day-of-month, month, day-of-week). Supports `*`, lists
6
+ * (`1,2,3`), ranges (`1-5`), and step values (`*\/15`, `1-5/2`).
7
+ *
8
+ * Implementation note: this is a minute-resolution scanner — adequate for
9
+ * preview UIs (showing the next few runs). It does not attempt to support
10
+ * non-standard fields (seconds, year, `L`, `W`, `#`).
11
+ */
12
+
13
+ function parseField(field: string, min: number, max: number): number[] {
14
+ const values = new Set<number>();
15
+
16
+ for (const part of field.split(',')) {
17
+ const stepMatch = part.match(/^(.+)\/(\d+)$/);
18
+ const step = stepMatch ? parseInt(stepMatch[2], 10) : 1;
19
+ const range = stepMatch ? stepMatch[1] : part;
20
+
21
+ if (!Number.isFinite(step) || step < 1) continue;
22
+
23
+ if (range === '*') {
24
+ for (let i = min; i <= max; i += step) values.add(i);
25
+ } else if (range.includes('-')) {
26
+ const [start, end] = range.split('-').map(Number);
27
+ if (Number.isFinite(start) && Number.isFinite(end)) {
28
+ for (let i = start; i <= end; i += step) values.add(i);
29
+ }
30
+ } else {
31
+ const n = parseInt(range, 10);
32
+ if (Number.isFinite(n)) values.add(n);
33
+ }
34
+ }
35
+
36
+ return Array.from(values).sort((a, b) => a - b);
37
+ }
38
+
39
+ /**
40
+ * Compute the next `count` run times of a cron expression starting after
41
+ * `from` (exclusive). Returns an empty array for invalid expressions.
42
+ */
43
+ export function getNextRuns(cron: string, count: number, from: Date = new Date()): Date[] {
44
+ if (!cron || typeof cron !== 'string' || count <= 0) return [];
45
+
46
+ const fields = cron.trim().split(/\s+/);
47
+ if (fields.length !== 5) return [];
48
+
49
+ const minuteValues = parseField(fields[0], 0, 59);
50
+ const hourValues = parseField(fields[1], 0, 23);
51
+ const domValues = parseField(fields[2], 1, 31);
52
+ const monthValues = parseField(fields[3], 1, 12);
53
+ const dowValues = parseField(fields[4], 0, 6);
54
+
55
+ if (
56
+ minuteValues.length === 0 ||
57
+ hourValues.length === 0 ||
58
+ (fields[2] !== '*' && domValues.length === 0) ||
59
+ (fields[3] !== '*' && monthValues.length === 0) ||
60
+ (fields[4] !== '*' && dowValues.length === 0)
61
+ ) {
62
+ return [];
63
+ }
64
+
65
+ const minuteSet = new Set(minuteValues);
66
+ const hourSet = new Set(hourValues);
67
+ const domSet = fields[2] === '*' ? null : new Set(domValues);
68
+ const monthSet = fields[3] === '*' ? null : new Set(monthValues);
69
+ const dowSet = fields[4] === '*' ? null : new Set(dowValues);
70
+
71
+ const runs: Date[] = [];
72
+ const cursor = new Date(from);
73
+ cursor.setSeconds(0, 0);
74
+ cursor.setMinutes(cursor.getMinutes() + 1);
75
+
76
+ // cap at ~1 year of minutes — far enough for monthly schedules,
77
+ // tight enough that broken expressions return quickly.
78
+ const maxIterations = 366 * 24 * 60;
79
+
80
+ for (let i = 0; i < maxIterations && runs.length < count; i++) {
81
+ const m = cursor.getMinutes();
82
+ const h = cursor.getHours();
83
+ const d = cursor.getDate();
84
+ const mo = cursor.getMonth() + 1;
85
+ const w = cursor.getDay();
86
+
87
+ if (
88
+ minuteSet.has(m) &&
89
+ hourSet.has(h) &&
90
+ (domSet === null || domSet.has(d)) &&
91
+ (monthSet === null || monthSet.has(mo)) &&
92
+ (dowSet === null || dowSet.has(w))
93
+ ) {
94
+ runs.push(new Date(cursor));
95
+ }
96
+
97
+ cursor.setMinutes(cursor.getMinutes() + 1);
98
+ }
99
+
100
+ return runs;
101
+ }
102
+
103
+ /**
104
+ * Default human-friendly formatter for a next-run Date.
105
+ * Example: "Mon, Jan 5 at 9:00 AM"
106
+ */
107
+ export function formatNextRun(date: Date): string {
108
+ const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
109
+ const months = [
110
+ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
111
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
112
+ ];
113
+ const day = days[date.getDay()];
114
+ const month = months[date.getMonth()];
115
+ const d = date.getDate();
116
+ const h = date.getHours();
117
+ const m = date.getMinutes().toString().padStart(2, '0');
118
+ const period = h >= 12 ? 'PM' : 'AM';
119
+ const hour = h === 0 ? 12 : h > 12 ? h - 12 : h;
120
+
121
+ return `${day}, ${month} ${d} at ${hour}:${m} ${period}`;
122
+ }
@@ -10,3 +10,4 @@ export {
10
10
  getWeekdayName,
11
11
  getWeekdayShort,
12
12
  } from './cron-humanize';
13
+ export { getNextRuns, formatNextRun } from './cron-next-runs';
@@ -4,7 +4,7 @@
4
4
  * Lazy-loaded Scroller 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
  ScrollerProps,
10
10
  ScrollerItemProps,
@@ -4,7 +4,7 @@
4
4
  * Lazy-loaded Sortable 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
  SortableProps,
10
10
  SortableItemProps,
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { createLazyComponent } from '../../../components';
3
+ import { createLazyComponent } from '../../../common';
4
4
  import type { DictationFieldProps } from './widgets/DictationField';
5
5
 
6
6
  export const LazyDictationField = createLazyComponent<DictationFieldProps>(
@@ -6,6 +6,7 @@ import { AlertCircle, Loader2, Mic } from 'lucide-react';
6
6
 
7
7
  import { useCountdownFromSeconds, useNotificationSounds } from '@djangocfg/ui-core/hooks';
8
8
  import { cn } from '@djangocfg/ui-core/lib';
9
+ import { Tooltip, TooltipContent, TooltipTrigger } from '@djangocfg/ui-core/components';
9
10
  import { useActiveComposer } from '@djangocfg/ui-tools/composer-registry';
10
11
  import { useSpeechRecognition } from '../hooks/useSpeechRecognition';
11
12
  import { useVoiceSupport } from '../hooks/useVoiceSupport';
@@ -364,44 +365,48 @@ export function VoiceComposerSlot({
364
365
  Failed
365
366
  </span>
366
367
  ) : null}
367
- <button
368
- type="button"
369
- onClick={toggle}
370
- aria-pressed={slotState === 'listening'}
371
- aria-label={ariaLabel}
372
- title={tooltip}
373
- data-state={slotState}
374
- className={cn(
375
- 'relative inline-flex items-center justify-center rounded-full transition-all duration-200',
376
- 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
377
- SIZE_CLS[size],
378
- slotState === 'listening' &&
379
- 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
380
- slotState === 'processing' &&
381
- 'bg-primary/10 text-primary hover:bg-primary/15',
382
- slotState === 'error' &&
383
- 'bg-destructive/10 text-destructive hover:bg-destructive/15',
384
- slotState === 'idle' &&
385
- 'text-muted-foreground hover:bg-muted hover:text-foreground',
386
- className,
387
- )}
388
- >
389
- {/* Recording feedback — pulsing circle overlay driven by the
390
- live mic level. Hidden in every non-listening state. */}
391
- <RecordingPulse active={slotState === 'listening'} level={rec.level} />
392
- {slotState === 'processing' ? (
393
- <Loader2 className="animate-spin" />
394
- ) : slotState === 'error' ? (
395
- <AlertCircle />
396
- ) : (
397
- <Mic
368
+ <Tooltip>
369
+ <TooltipTrigger asChild>
370
+ <button
371
+ type="button"
372
+ onClick={toggle}
373
+ aria-pressed={slotState === 'listening'}
374
+ aria-label={ariaLabel}
375
+ data-state={slotState}
398
376
  className={cn(
399
- 'transition-transform duration-200',
400
- slotState === 'listening' && 'scale-110',
377
+ 'relative inline-flex items-center justify-center rounded-full transition-all duration-200',
378
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
379
+ SIZE_CLS[size],
380
+ slotState === 'listening' &&
381
+ 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
382
+ slotState === 'processing' &&
383
+ 'bg-primary/10 text-primary hover:bg-primary/15',
384
+ slotState === 'error' &&
385
+ 'bg-destructive/10 text-destructive hover:bg-destructive/15',
386
+ slotState === 'idle' &&
387
+ 'text-muted-foreground hover:bg-muted hover:text-foreground',
388
+ className,
401
389
  )}
402
- />
403
- )}
404
- </button>
390
+ >
391
+ {/* Recording feedback — pulsing circle overlay driven by the
392
+ live mic level. Hidden in every non-listening state. */}
393
+ <RecordingPulse active={slotState === 'listening'} level={rec.level} />
394
+ {slotState === 'processing' ? (
395
+ <Loader2 className="animate-spin" />
396
+ ) : slotState === 'error' ? (
397
+ <AlertCircle />
398
+ ) : (
399
+ <Mic
400
+ className={cn(
401
+ 'transition-transform duration-200',
402
+ slotState === 'listening' && 'scale-110',
403
+ )}
404
+ />
405
+ )}
406
+ </button>
407
+ </TooltipTrigger>
408
+ <TooltipContent side="top">{tooltip}</TooltipContent>
409
+ </Tooltip>
405
410
  </span>
406
411
  );
407
412
  }
@@ -4,10 +4,18 @@
4
4
  * ImageToolbar - Floating toolbar for image controls
5
5
  */
6
6
 
7
- import { useMemo } from 'react';
7
+ import { useMemo, type ReactNode } from 'react';
8
8
  import { ZoomIn, ZoomOut, RotateCw, FlipHorizontal, FlipVertical, Maximize2, Expand } from 'lucide-react';
9
9
  import { useControls } from 'react-zoom-pan-pinch';
10
- import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@djangocfg/ui-core/components';
10
+ import {
11
+ DropdownMenu,
12
+ DropdownMenuContent,
13
+ DropdownMenuItem,
14
+ DropdownMenuTrigger,
15
+ Tooltip,
16
+ TooltipContent,
17
+ TooltipTrigger,
18
+ } from '@djangocfg/ui-core/components';
11
19
  import { useAppT } from '@djangocfg/i18n';
12
20
  import { cn } from '@djangocfg/ui-core/lib';
13
21
  import { ZOOM_PRESETS } from '../utils';
@@ -29,6 +37,36 @@ const TB_BUTTON =
29
37
  'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70 ' +
30
38
  'disabled:opacity-40 disabled:pointer-events-none';
31
39
 
40
+ // Small helper so each icon button shares the same Tooltip wrapper +
41
+ // accessibility shape (visible tooltip on hover/focus, aria-label for AT).
42
+ function TbIconButton({
43
+ label,
44
+ onClick,
45
+ className,
46
+ children,
47
+ }: {
48
+ label: string;
49
+ onClick: () => void;
50
+ className?: string;
51
+ children: ReactNode;
52
+ }) {
53
+ return (
54
+ <Tooltip>
55
+ <TooltipTrigger asChild>
56
+ <button
57
+ type="button"
58
+ aria-label={label}
59
+ onClick={onClick}
60
+ className={cn(TB_BUTTON, 'h-7 w-7', className)}
61
+ >
62
+ {children}
63
+ </button>
64
+ </TooltipTrigger>
65
+ <TooltipContent side="top">{label}</TooltipContent>
66
+ </Tooltip>
67
+ );
68
+ }
69
+
32
70
  export function ImageToolbar({
33
71
  scale,
34
72
  transform,
@@ -59,14 +97,9 @@ export function ImageToolbar({
59
97
  return (
60
98
  <div className="absolute bottom-4 left-1/2 -translate-x-1/2 z-10 flex items-center gap-0.5 bg-black/60 backdrop-blur-sm border border-white/10 rounded-lg p-1 shadow-lg">
61
99
  {/* Zoom controls */}
62
- <button
63
- type="button"
64
- className={cn(TB_BUTTON, 'h-7 w-7')}
65
- onClick={() => zoomOut()}
66
- title={labels.zoomOut}
67
- >
100
+ <TbIconButton label={labels.zoomOut} onClick={() => zoomOut()}>
68
101
  <ZoomOut className="h-3.5 w-3.5" />
69
- </button>
102
+ </TbIconButton>
70
103
 
71
104
  <DropdownMenu>
72
105
  <DropdownMenuTrigger asChild>
@@ -90,56 +123,39 @@ export function ImageToolbar({
90
123
  </DropdownMenuContent>
91
124
  </DropdownMenu>
92
125
 
93
- <button
94
- type="button"
95
- className={cn(TB_BUTTON, 'h-7 w-7')}
96
- onClick={() => zoomIn()}
97
- title={labels.zoomIn}
98
- >
126
+ <TbIconButton label={labels.zoomIn} onClick={() => zoomIn()}>
99
127
  <ZoomIn className="h-3.5 w-3.5" />
100
- </button>
128
+ </TbIconButton>
101
129
 
102
130
  <div className="w-px h-4 bg-white/20 mx-1" />
103
131
 
104
132
  {/* Fit to view */}
105
- <button
106
- type="button"
107
- className={cn(TB_BUTTON, 'h-7 w-7')}
108
- onClick={() => resetTransform()}
109
- title={labels.fitToView}
110
- >
133
+ <TbIconButton label={labels.fitToView} onClick={() => resetTransform()}>
111
134
  <Maximize2 className="h-3.5 w-3.5" />
112
- </button>
135
+ </TbIconButton>
113
136
 
114
137
  <div className="w-px h-4 bg-white/20 mx-1" />
115
138
 
116
139
  {/* Transform controls */}
117
- <button
118
- type="button"
119
- className={cn(TB_BUTTON, 'h-7 w-7', transform.flipH && 'bg-white/20 text-white')}
140
+ <TbIconButton
141
+ label={labels.flipHorizontal}
120
142
  onClick={onFlipH}
121
- title={labels.flipHorizontal}
143
+ className={transform.flipH ? 'bg-white/20 text-white' : undefined}
122
144
  >
123
145
  <FlipHorizontal className="h-3.5 w-3.5" />
124
- </button>
146
+ </TbIconButton>
125
147
 
126
- <button
127
- type="button"
128
- className={cn(TB_BUTTON, 'h-7 w-7', transform.flipV && 'bg-white/20 text-white')}
148
+ <TbIconButton
149
+ label={labels.flipVertical}
129
150
  onClick={onFlipV}
130
- title={labels.flipVertical}
151
+ className={transform.flipV ? 'bg-white/20 text-white' : undefined}
131
152
  >
132
153
  <FlipVertical className="h-3.5 w-3.5" />
133
- </button>
154
+ </TbIconButton>
134
155
 
135
- <button
136
- type="button"
137
- className={cn(TB_BUTTON, 'h-7 w-7')}
138
- onClick={onRotate}
139
- title={labels.rotate}
140
- >
156
+ <TbIconButton label={labels.rotate} onClick={onRotate}>
141
157
  <RotateCw className="h-3.5 w-3.5" />
142
- </button>
158
+ </TbIconButton>
143
159
 
144
160
  {transform.rotation !== 0 && (
145
161
  <span className="text-[10px] text-white/60 font-mono pl-1">
@@ -150,14 +166,9 @@ export function ImageToolbar({
150
166
  {onExpand && (
151
167
  <>
152
168
  <div className="w-px h-4 bg-white/20 mx-1" />
153
- <button
154
- type="button"
155
- className={cn(TB_BUTTON, 'h-7 w-7')}
156
- onClick={onExpand}
157
- title={labels.fullscreen}
158
- >
169
+ <TbIconButton label={labels.fullscreen} onClick={onExpand}>
159
170
  <Expand className="h-3.5 w-3.5" />
160
- </button>
171
+ </TbIconButton>
161
172
  </>
162
173
  )}
163
174
  </div>
@@ -20,7 +20,7 @@ import {
20
20
  TransformComponent,
21
21
  type ReactZoomPanPinchRef,
22
22
  } from 'react-zoom-pan-pinch';
23
- import { cn, Dialog, DialogContent, DialogTitle, Alert, AlertDescription } from '@djangocfg/ui-core';
23
+ import { cn, Dialog, DialogContent, DialogTitle, Alert, AlertDescription, Tooltip, TooltipContent, TooltipTrigger } from '@djangocfg/ui-core';
24
24
  import { useAppT } from '@djangocfg/i18n';
25
25
  import { useHotkey } from '@djangocfg/ui-core/hooks';
26
26
 
@@ -313,24 +313,32 @@ export function ImageViewer({
313
313
  {/* Gallery navigation */}
314
314
  {hasMultiple && (
315
315
  <>
316
- <button
317
- type="button"
318
- onClick={prev}
319
- aria-label={labels.prev}
320
- title={labels.prev}
321
- className="absolute left-2 top-1/2 -translate-y-1/2 z-10 bg-black/50 hover:bg-black/70 text-white rounded-full p-1.5 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white"
322
- >
323
- <ChevronLeft className="h-5 w-5" />
324
- </button>
325
- <button
326
- type="button"
327
- onClick={next}
328
- aria-label={labels.next}
329
- title={labels.next}
330
- className="absolute right-2 top-1/2 -translate-y-1/2 z-10 bg-black/50 hover:bg-black/70 text-white rounded-full p-1.5 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white"
331
- >
332
- <ChevronRight className="h-5 w-5" />
333
- </button>
316
+ <Tooltip>
317
+ <TooltipTrigger asChild>
318
+ <button
319
+ type="button"
320
+ onClick={prev}
321
+ aria-label={labels.prev}
322
+ className="absolute left-2 top-1/2 -translate-y-1/2 z-10 bg-black/50 hover:bg-black/70 text-white rounded-full p-1.5 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white"
323
+ >
324
+ <ChevronLeft className="h-5 w-5" />
325
+ </button>
326
+ </TooltipTrigger>
327
+ <TooltipContent side="right">{labels.prev}</TooltipContent>
328
+ </Tooltip>
329
+ <Tooltip>
330
+ <TooltipTrigger asChild>
331
+ <button
332
+ type="button"
333
+ onClick={next}
334
+ aria-label={labels.next}
335
+ className="absolute right-2 top-1/2 -translate-y-1/2 z-10 bg-black/50 hover:bg-black/70 text-white rounded-full p-1.5 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white"
336
+ >
337
+ <ChevronRight className="h-5 w-5" />
338
+ </button>
339
+ </TooltipTrigger>
340
+ <TooltipContent side="left">{labels.next}</TooltipContent>
341
+ </Tooltip>
334
342
  <div className="absolute top-3 left-1/2 -translate-x-1/2 z-10 bg-black/60 backdrop-blur-sm border border-white/10 text-white/80 text-xs font-mono px-2 py-0.5 rounded-full pointer-events-none">
335
343
  {currentIndex + 1} / {images.length}
336
344
  </div>
@@ -10,7 +10,7 @@
10
10
  * import { ImageViewer } from '@djangocfg/ui-tools/image-viewer'
11
11
  */
12
12
 
13
- import { createLazyComponent, LoadingFallback } from '../../../components';
13
+ import { createLazyComponent, LoadingFallback } from '../../../common';
14
14
  import type { ImageViewerProps } from './types';
15
15
 
16
16
  // ============================================================================
@@ -10,7 +10,7 @@
10
10
  * import { LottiePlayer } from '@djangocfg/ui-tools/lottie'
11
11
  */
12
12
 
13
- import { createLazyComponent } from '../../../components';
13
+ import { createLazyComponent } from '../../../common';
14
14
  import type { LottiePlayerProps } from './types';
15
15
 
16
16
  // ============================================================================
@@ -12,7 +12,7 @@
12
12
  * iframe sources where the embed renders its own UI).
13
13
  */
14
14
 
15
- import { useEffect, useMemo, useRef, type CSSProperties } from 'react';
15
+ import { useCallback, useEffect, useMemo, useRef, type CSSProperties } from 'react';
16
16
  import { MediaController } from 'media-chrome/react';
17
17
  import { cn } from '@djangocfg/ui-core/lib';
18
18
  import './styles/video-player.css';
@@ -51,6 +51,7 @@ export function VideoPlayer({
51
51
  );
52
52
 
53
53
  const isIframe = normalized.type === 'iframe';
54
+ const isYouTube = normalized.type === 'youtube';
54
55
  // For iframe embeds media-chrome cannot drive the inner player — hide the
55
56
  // control bar to avoid a non-functional UI.
56
57
  const showControls = controls && !isIframe;
@@ -61,6 +62,20 @@ export function VideoPlayer({
61
62
  // all we use) — media-chrome's full MediaController type pulls private
62
63
  // fields we don't need to see.
63
64
  const controllerRef = useRef<HTMLElement | null>(null);
65
+
66
+ // Click-to-play overlay for YouTube — the underlying YT iframe is made
67
+ // `pointer-events: none` via CSS to suppress YouTube's own hover-state /
68
+ // UI (title bar, pause-screen, branding tray; ToS forbids hiding them,
69
+ // but we can make them inert). This overlay restores click-to-toggle
70
+ // by dispatching media-chrome request events the controller listens for.
71
+ const onYouTubeShieldClick = useCallback(() => {
72
+ const el = controllerRef.current;
73
+ if (!el) return;
74
+ const paused = el.hasAttribute('mediapaused');
75
+ const eventName = paused ? 'mediaplayrequest' : 'mediapauserequest';
76
+ el.dispatchEvent(new CustomEvent(eventName, { bubbles: true, composed: true }));
77
+ }, []);
78
+
64
79
  useEffect(() => {
65
80
  if (!autoFocus) return;
66
81
  const el = controllerRef.current;
@@ -85,6 +100,18 @@ export function VideoPlayer({
85
100
  style={aspectRatioStyle(aspectRatio)}
86
101
  >
87
102
  <CanvasDispatcher source={normalized} {...settings} />
103
+ {isYouTube && (
104
+ // Transparent click-shield over the YT iframe. The iframe itself
105
+ // is pointer-events:none (see video-player.css); this div absorbs
106
+ // pointer events and forwards a play/pause request to media-chrome,
107
+ // so users still get click-to-toggle without ever interacting with
108
+ // YouTube's own UI.
109
+ <div
110
+ className="vp-yt-click-shield"
111
+ onClick={onYouTubeShieldClick}
112
+ aria-hidden="true"
113
+ />
114
+ )}
88
115
  {children ??
89
116
  (showControls && (
90
117
  <ControlsBar>
@@ -2,12 +2,20 @@
2
2
 
3
3
  import { MediaFullscreenButton } from 'media-chrome/react';
4
4
  import { cn } from '@djangocfg/ui-core/lib';
5
- import type { ComponentProps } from 'react';
5
+ import {
6
+ Tooltip,
7
+ TooltipContent,
8
+ TooltipTrigger,
9
+ } from '@djangocfg/ui-core/components';
10
+ import type { ComponentProps, ReactNode } from 'react';
6
11
 
7
- export type FullscreenProps = ComponentProps<typeof MediaFullscreenButton>;
12
+ export type FullscreenProps = ComponentProps<typeof MediaFullscreenButton> & {
13
+ /** Tooltip copy. Defaults to `"Fullscreen"`. Pass `false` to opt out. */
14
+ readonly label?: ReactNode | false;
15
+ };
8
16
 
9
- export function Fullscreen({ className, ...props }: FullscreenProps) {
10
- return (
17
+ export function Fullscreen({ className, label, ...props }: FullscreenProps) {
18
+ const button = (
11
19
  <MediaFullscreenButton
12
20
  {...props}
13
21
  className={cn(
@@ -16,4 +24,13 @@ export function Fullscreen({ className, ...props }: FullscreenProps) {
16
24
  )}
17
25
  />
18
26
  );
27
+
28
+ if (label === false) return button;
29
+
30
+ return (
31
+ <Tooltip>
32
+ <TooltipTrigger asChild>{button}</TooltipTrigger>
33
+ <TooltipContent side="top">{label ?? 'Fullscreen'}</TooltipContent>
34
+ </Tooltip>
35
+ );
19
36
  }